xref: /freebsd/sys/dev/sound/pci/hda/hdaa.c (revision 3ddaf8200bc90b1410755ebac7b5c979ea90a2f6)
17c6b05d2SAlexander Motin /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
47c6b05d2SAlexander Motin  * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
57c6b05d2SAlexander Motin  * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
67c6b05d2SAlexander Motin  * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org>
77c6b05d2SAlexander Motin  * All rights reserved.
87c6b05d2SAlexander Motin  *
97c6b05d2SAlexander Motin  * Redistribution and use in source and binary forms, with or without
107c6b05d2SAlexander Motin  * modification, are permitted provided that the following conditions
117c6b05d2SAlexander Motin  * are met:
127c6b05d2SAlexander Motin  * 1. Redistributions of source code must retain the above copyright
137c6b05d2SAlexander Motin  *    notice, this list of conditions and the following disclaimer.
147c6b05d2SAlexander Motin  * 2. Redistributions in binary form must reproduce the above copyright
157c6b05d2SAlexander Motin  *    notice, this list of conditions and the following disclaimer in the
167c6b05d2SAlexander Motin  *    documentation and/or other materials provided with the distribution.
177c6b05d2SAlexander Motin  *
187c6b05d2SAlexander Motin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
197c6b05d2SAlexander Motin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
207c6b05d2SAlexander Motin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
217c6b05d2SAlexander Motin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
227c6b05d2SAlexander Motin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
237c6b05d2SAlexander Motin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
247c6b05d2SAlexander Motin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
257c6b05d2SAlexander Motin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
267c6b05d2SAlexander Motin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
277c6b05d2SAlexander Motin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
287c6b05d2SAlexander Motin  * SUCH DAMAGE.
297c6b05d2SAlexander Motin  */
307c6b05d2SAlexander Motin 
317c6b05d2SAlexander Motin /*
327c6b05d2SAlexander Motin  * Intel High Definition Audio (Audio function) driver for FreeBSD.
337c6b05d2SAlexander Motin  */
347c6b05d2SAlexander Motin 
357c6b05d2SAlexander Motin #ifdef HAVE_KERNEL_OPTION_HEADERS
367c6b05d2SAlexander Motin #include "opt_snd.h"
377c6b05d2SAlexander Motin #endif
387c6b05d2SAlexander Motin 
397c6b05d2SAlexander Motin #include <dev/sound/pcm/sound.h>
407c6b05d2SAlexander Motin 
417c6b05d2SAlexander Motin #include <sys/ctype.h>
427c6b05d2SAlexander Motin #include <sys/taskqueue.h>
437c6b05d2SAlexander Motin 
447c6b05d2SAlexander Motin #include <dev/sound/pci/hda/hdac.h>
457c6b05d2SAlexander Motin #include <dev/sound/pci/hda/hdaa.h>
467c6b05d2SAlexander Motin #include <dev/sound/pci/hda/hda_reg.h>
477c6b05d2SAlexander Motin 
487c6b05d2SAlexander Motin #include "mixer_if.h"
497c6b05d2SAlexander Motin 
507c6b05d2SAlexander Motin #define hdaa_lock(devinfo)	snd_mtxlock((devinfo)->lock)
517c6b05d2SAlexander Motin #define hdaa_unlock(devinfo)	snd_mtxunlock((devinfo)->lock)
527c6b05d2SAlexander Motin #define hdaa_lockassert(devinfo) snd_mtxassert((devinfo)->lock)
537c6b05d2SAlexander Motin 
547c6b05d2SAlexander Motin static const struct {
55abe917adSMarius Strobl 	const char *key;
567c6b05d2SAlexander Motin 	uint32_t value;
577c6b05d2SAlexander Motin } hdaa_quirks_tab[] = {
587c6b05d2SAlexander Motin 	{ "softpcmvol", HDAA_QUIRK_SOFTPCMVOL },
597c6b05d2SAlexander Motin 	{ "fixedrate", HDAA_QUIRK_FIXEDRATE },
607c6b05d2SAlexander Motin 	{ "forcestereo", HDAA_QUIRK_FORCESTEREO },
617c6b05d2SAlexander Motin 	{ "eapdinv", HDAA_QUIRK_EAPDINV },
627c6b05d2SAlexander Motin 	{ "senseinv", HDAA_QUIRK_SENSEINV },
637c6b05d2SAlexander Motin 	{ "ivref50", HDAA_QUIRK_IVREF50 },
647c6b05d2SAlexander Motin 	{ "ivref80", HDAA_QUIRK_IVREF80 },
657c6b05d2SAlexander Motin 	{ "ivref100", HDAA_QUIRK_IVREF100 },
667c6b05d2SAlexander Motin 	{ "ovref50", HDAA_QUIRK_OVREF50 },
677c6b05d2SAlexander Motin 	{ "ovref80", HDAA_QUIRK_OVREF80 },
687c6b05d2SAlexander Motin 	{ "ovref100", HDAA_QUIRK_OVREF100 },
697c6b05d2SAlexander Motin 	{ "ivref", HDAA_QUIRK_IVREF },
707c6b05d2SAlexander Motin 	{ "ovref", HDAA_QUIRK_OVREF },
717c6b05d2SAlexander Motin 	{ "vref", HDAA_QUIRK_VREF },
727c6b05d2SAlexander Motin };
737c6b05d2SAlexander Motin 
747c6b05d2SAlexander Motin #define HDA_PARSE_MAXDEPTH	10
757c6b05d2SAlexander Motin 
767c6b05d2SAlexander Motin MALLOC_DEFINE(M_HDAA, "hdaa", "HDA Audio");
777c6b05d2SAlexander Motin 
78abe917adSMarius Strobl static const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue",
79abe917adSMarius Strobl     "Green", "Red", "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B",
80abe917adSMarius Strobl     "Res.C", "Res.D", "White", "Other"};
817c6b05d2SAlexander Motin 
82abe917adSMarius Strobl static const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD",
837c6b05d2SAlexander Motin     "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in",
847c6b05d2SAlexander Motin     "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"};
857c6b05d2SAlexander Motin 
86abe917adSMarius Strobl static const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"};
877c6b05d2SAlexander Motin 
88abe917adSMarius Strobl static const char *HDA_CONNECTORS[16] = {
897c6b05d2SAlexander Motin     "Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical", "Digital", "Analog",
907c6b05d2SAlexander Motin     "DIN", "XLR", "RJ-11", "Combo", "0xc", "0xd", "0xe", "Other" };
917c6b05d2SAlexander Motin 
92abe917adSMarius Strobl static const char *HDA_LOCS[64] = {
937c6b05d2SAlexander Motin     "0x00", "Rear", "Front", "Left", "Right", "Top", "Bottom", "Rear-panel",
947c6b05d2SAlexander Motin 	"Drive-bay", "0x09", "0x0a", "0x0b", "0x0c", "0x0d", "0x0e", "0x0f",
957c6b05d2SAlexander Motin     "Internal", "0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "Riser",
967c6b05d2SAlexander Motin 	"0x18", "Onboard", "0x1a", "0x1b", "0x1c", "0x1d", "0x1e", "0x1f",
977c6b05d2SAlexander Motin     "External", "Ext-Rear", "Ext-Front", "Ext-Left", "Ext-Right", "Ext-Top", "Ext-Bottom", "0x07",
987c6b05d2SAlexander Motin 	"0x28", "0x29", "0x2a", "0x2b", "0x2c", "0x2d", "0x2e", "0x2f",
997c6b05d2SAlexander Motin     "Other", "0x31", "0x32", "0x33", "0x34", "0x35", "Other-Bott", "Lid-In",
1007c6b05d2SAlexander Motin 	"Lid-Out", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "0x3f" };
1017c6b05d2SAlexander Motin 
102abe917adSMarius Strobl static const char *HDA_GPIO_ACTIONS[8] = {
1037c6b05d2SAlexander Motin     "keep", "set", "clear", "disable", "input", "0x05", "0x06", "0x07"};
1047c6b05d2SAlexander Motin 
105abe917adSMarius Strobl static const char *HDA_HDMI_CODING_TYPES[18] = {
10688addcbeSAlexander Motin     "undefined", "LPCM", "AC-3", "MPEG1", "MP3", "MPEG2", "AAC-LC", "DTS",
10788addcbeSAlexander Motin     "ATRAC", "DSD", "E-AC-3", "DTS-HD", "MLP", "DST", "WMAPro", "HE-AAC",
10888addcbeSAlexander Motin     "HE-AACv2", "MPEG-Surround"
10988addcbeSAlexander Motin };
11088addcbeSAlexander Motin 
1117c6b05d2SAlexander Motin /* Default */
1127c6b05d2SAlexander Motin static uint32_t hdaa_fmt[] = {
1137c6b05d2SAlexander Motin 	SND_FORMAT(AFMT_S16_LE, 2, 0),
1147c6b05d2SAlexander Motin 	0
1157c6b05d2SAlexander Motin };
1167c6b05d2SAlexander Motin 
1177c6b05d2SAlexander Motin static struct pcmchan_caps hdaa_caps = {48000, 48000, hdaa_fmt, 0};
1187c6b05d2SAlexander Motin 
1197c6b05d2SAlexander Motin static const struct {
1207c6b05d2SAlexander Motin 	uint32_t	rate;
1217c6b05d2SAlexander Motin 	int		valid;
1227c6b05d2SAlexander Motin 	uint16_t	base;
1237c6b05d2SAlexander Motin 	uint16_t	mul;
1247c6b05d2SAlexander Motin 	uint16_t	div;
1257c6b05d2SAlexander Motin } hda_rate_tab[] = {
1267c6b05d2SAlexander Motin 	{   8000, 1, 0x0000, 0x0000, 0x0500 },	/* (48000 * 1) / 6 */
1277c6b05d2SAlexander Motin 	{   9600, 0, 0x0000, 0x0000, 0x0400 },	/* (48000 * 1) / 5 */
1287c6b05d2SAlexander Motin 	{  12000, 0, 0x0000, 0x0000, 0x0300 },	/* (48000 * 1) / 4 */
1297c6b05d2SAlexander Motin 	{  16000, 1, 0x0000, 0x0000, 0x0200 },	/* (48000 * 1) / 3 */
1307c6b05d2SAlexander Motin 	{  18000, 0, 0x0000, 0x1000, 0x0700 },	/* (48000 * 3) / 8 */
1317c6b05d2SAlexander Motin 	{  19200, 0, 0x0000, 0x0800, 0x0400 },	/* (48000 * 2) / 5 */
1327c6b05d2SAlexander Motin 	{  24000, 0, 0x0000, 0x0000, 0x0100 },	/* (48000 * 1) / 2 */
1337c6b05d2SAlexander Motin 	{  28800, 0, 0x0000, 0x1000, 0x0400 },	/* (48000 * 3) / 5 */
1347c6b05d2SAlexander Motin 	{  32000, 1, 0x0000, 0x0800, 0x0200 },	/* (48000 * 2) / 3 */
1357c6b05d2SAlexander Motin 	{  36000, 0, 0x0000, 0x1000, 0x0300 },	/* (48000 * 3) / 4 */
1367c6b05d2SAlexander Motin 	{  38400, 0, 0x0000, 0x1800, 0x0400 },	/* (48000 * 4) / 5 */
1377c6b05d2SAlexander Motin 	{  48000, 1, 0x0000, 0x0000, 0x0000 },	/* (48000 * 1) / 1 */
1387c6b05d2SAlexander Motin 	{  64000, 0, 0x0000, 0x1800, 0x0200 },	/* (48000 * 4) / 3 */
1397c6b05d2SAlexander Motin 	{  72000, 0, 0x0000, 0x1000, 0x0100 },	/* (48000 * 3) / 2 */
1407c6b05d2SAlexander Motin 	{  96000, 1, 0x0000, 0x0800, 0x0000 },	/* (48000 * 2) / 1 */
1417c6b05d2SAlexander Motin 	{ 144000, 0, 0x0000, 0x1000, 0x0000 },	/* (48000 * 3) / 1 */
1427c6b05d2SAlexander Motin 	{ 192000, 1, 0x0000, 0x1800, 0x0000 },	/* (48000 * 4) / 1 */
1437c6b05d2SAlexander Motin 	{   8820, 0, 0x4000, 0x0000, 0x0400 },	/* (44100 * 1) / 5 */
1447c6b05d2SAlexander Motin 	{  11025, 1, 0x4000, 0x0000, 0x0300 },	/* (44100 * 1) / 4 */
1457c6b05d2SAlexander Motin 	{  12600, 0, 0x4000, 0x0800, 0x0600 },	/* (44100 * 2) / 7 */
1467c6b05d2SAlexander Motin 	{  14700, 0, 0x4000, 0x0000, 0x0200 },	/* (44100 * 1) / 3 */
1477c6b05d2SAlexander Motin 	{  17640, 0, 0x4000, 0x0800, 0x0400 },	/* (44100 * 2) / 5 */
1487c6b05d2SAlexander Motin 	{  18900, 0, 0x4000, 0x1000, 0x0600 },	/* (44100 * 3) / 7 */
1497c6b05d2SAlexander Motin 	{  22050, 1, 0x4000, 0x0000, 0x0100 },	/* (44100 * 1) / 2 */
1507c6b05d2SAlexander Motin 	{  25200, 0, 0x4000, 0x1800, 0x0600 },	/* (44100 * 4) / 7 */
1517c6b05d2SAlexander Motin 	{  26460, 0, 0x4000, 0x1000, 0x0400 },	/* (44100 * 3) / 5 */
1527c6b05d2SAlexander Motin 	{  29400, 0, 0x4000, 0x0800, 0x0200 },	/* (44100 * 2) / 3 */
1537c6b05d2SAlexander Motin 	{  33075, 0, 0x4000, 0x1000, 0x0300 },	/* (44100 * 3) / 4 */
1547c6b05d2SAlexander Motin 	{  35280, 0, 0x4000, 0x1800, 0x0400 },	/* (44100 * 4) / 5 */
1557c6b05d2SAlexander Motin 	{  44100, 1, 0x4000, 0x0000, 0x0000 },	/* (44100 * 1) / 1 */
1567c6b05d2SAlexander Motin 	{  58800, 0, 0x4000, 0x1800, 0x0200 },	/* (44100 * 4) / 3 */
1577c6b05d2SAlexander Motin 	{  66150, 0, 0x4000, 0x1000, 0x0100 },	/* (44100 * 3) / 2 */
1587c6b05d2SAlexander Motin 	{  88200, 1, 0x4000, 0x0800, 0x0000 },	/* (44100 * 2) / 1 */
1597c6b05d2SAlexander Motin 	{ 132300, 0, 0x4000, 0x1000, 0x0000 },	/* (44100 * 3) / 1 */
1607c6b05d2SAlexander Motin 	{ 176400, 1, 0x4000, 0x1800, 0x0000 },	/* (44100 * 4) / 1 */
1617c6b05d2SAlexander Motin };
1627c6b05d2SAlexander Motin #define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0]))
1637c6b05d2SAlexander Motin 
1643d741b14SAlexander Motin const static char *ossnames[] = SOUND_DEVICE_NAMES;
1653d741b14SAlexander Motin 
1667c6b05d2SAlexander Motin /****************************************************************************
1677c6b05d2SAlexander Motin  * Function prototypes
1687c6b05d2SAlexander Motin  ****************************************************************************/
1697c6b05d2SAlexander Motin static int	hdaa_pcmchannel_setup(struct hdaa_chan *);
1707c6b05d2SAlexander Motin 
1717c6b05d2SAlexander Motin static void	hdaa_widget_connection_select(struct hdaa_widget *, uint8_t);
1727c6b05d2SAlexander Motin static void	hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *,
1737c6b05d2SAlexander Motin 						uint32_t, int, int);
1747c6b05d2SAlexander Motin static struct	hdaa_audio_ctl *hdaa_audio_ctl_amp_get(struct hdaa_devinfo *,
1757c6b05d2SAlexander Motin 							nid_t, int, int, int);
1767c6b05d2SAlexander Motin static void	hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *,
1777c6b05d2SAlexander Motin 				nid_t, int, int, int, int, int, int);
1787c6b05d2SAlexander Motin 
1797c6b05d2SAlexander Motin static void	hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf);
1807c6b05d2SAlexander Motin 
1817c6b05d2SAlexander Motin static char *
hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask,char * buf,size_t len)1827c6b05d2SAlexander Motin hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len)
1837c6b05d2SAlexander Motin {
1847c6b05d2SAlexander Motin 	int i, first = 1;
1857c6b05d2SAlexander Motin 
1867c6b05d2SAlexander Motin 	bzero(buf, len);
1877c6b05d2SAlexander Motin 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
1887c6b05d2SAlexander Motin 		if (mask & (1 << i)) {
1897c6b05d2SAlexander Motin 			if (first == 0)
1907c6b05d2SAlexander Motin 				strlcat(buf, ", ", len);
1913d741b14SAlexander Motin 			strlcat(buf, ossnames[i], len);
1927c6b05d2SAlexander Motin 			first = 0;
1937c6b05d2SAlexander Motin 		}
1947c6b05d2SAlexander Motin 	}
1957c6b05d2SAlexander Motin 	return (buf);
1967c6b05d2SAlexander Motin }
1977c6b05d2SAlexander Motin 
1987c6b05d2SAlexander Motin static struct hdaa_audio_ctl *
hdaa_audio_ctl_each(struct hdaa_devinfo * devinfo,int * index)1997c6b05d2SAlexander Motin hdaa_audio_ctl_each(struct hdaa_devinfo *devinfo, int *index)
2007c6b05d2SAlexander Motin {
2017c6b05d2SAlexander Motin 	if (devinfo == NULL ||
2027c6b05d2SAlexander Motin 	    index == NULL || devinfo->ctl == NULL ||
2037c6b05d2SAlexander Motin 	    devinfo->ctlcnt < 1 ||
2047c6b05d2SAlexander Motin 	    *index < 0 || *index >= devinfo->ctlcnt)
2057c6b05d2SAlexander Motin 		return (NULL);
2067c6b05d2SAlexander Motin 	return (&devinfo->ctl[(*index)++]);
2077c6b05d2SAlexander Motin }
2087c6b05d2SAlexander Motin 
2097c6b05d2SAlexander Motin static struct hdaa_audio_ctl *
hdaa_audio_ctl_amp_get(struct hdaa_devinfo * devinfo,nid_t nid,int dir,int index,int cnt)2107c6b05d2SAlexander Motin hdaa_audio_ctl_amp_get(struct hdaa_devinfo *devinfo, nid_t nid, int dir,
2117c6b05d2SAlexander Motin 						int index, int cnt)
2127c6b05d2SAlexander Motin {
2137c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
2147c6b05d2SAlexander Motin 	int i, found = 0;
2157c6b05d2SAlexander Motin 
2167c6b05d2SAlexander Motin 	if (devinfo == NULL || devinfo->ctl == NULL)
2177c6b05d2SAlexander Motin 		return (NULL);
2187c6b05d2SAlexander Motin 
2197c6b05d2SAlexander Motin 	i = 0;
2207c6b05d2SAlexander Motin 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
2217c6b05d2SAlexander Motin 		if (ctl->enable == 0)
2227c6b05d2SAlexander Motin 			continue;
2237c6b05d2SAlexander Motin 		if (ctl->widget->nid != nid)
2247c6b05d2SAlexander Motin 			continue;
2257c6b05d2SAlexander Motin 		if (dir && ctl->ndir != dir)
2267c6b05d2SAlexander Motin 			continue;
2277c6b05d2SAlexander Motin 		if (index >= 0 && ctl->ndir == HDAA_CTL_IN &&
2287c6b05d2SAlexander Motin 		    ctl->dir == ctl->ndir && ctl->index != index)
2297c6b05d2SAlexander Motin 			continue;
2307c6b05d2SAlexander Motin 		found++;
2317c6b05d2SAlexander Motin 		if (found == cnt || cnt <= 0)
2327c6b05d2SAlexander Motin 			return (ctl);
2337c6b05d2SAlexander Motin 	}
2347c6b05d2SAlexander Motin 
2357c6b05d2SAlexander Motin 	return (NULL);
2367c6b05d2SAlexander Motin }
2377c6b05d2SAlexander Motin 
2386d36c821SAlexander Motin static const struct matrix {
2396d36c821SAlexander Motin 	struct pcmchan_matrix	m;
2406d36c821SAlexander Motin 	int			analog;
2416d36c821SAlexander Motin } matrixes[]  = {
2426d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_1_0,	1 },
2436d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_2_0,	1 },
2446d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_2_1,	0 },
2456d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_3_0,	0 },
2466d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_3_1,	0 },
2476d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_4_0,	1 },
2486d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_4_1,	0 },
2496d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_5_0,	0 },
2506d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_5_1,	1 },
2516d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_6_0,	0 },
2526d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_6_1,	0 },
2536d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_7_0,	0 },
2546d36c821SAlexander Motin     { SND_CHN_MATRIX_MAP_7_1,	1 },
2556d36c821SAlexander Motin };
2566d36c821SAlexander Motin 
2576d36c821SAlexander Motin static const char *channel_names[] = SND_CHN_T_NAMES;
2586d36c821SAlexander Motin 
2596d36c821SAlexander Motin /*
2606d36c821SAlexander Motin  * Connected channels change handler.
2616d36c821SAlexander Motin  */
2626d36c821SAlexander Motin static void
hdaa_channels_handler(struct hdaa_audio_as * as)2636d36c821SAlexander Motin hdaa_channels_handler(struct hdaa_audio_as *as)
2646d36c821SAlexander Motin {
2656d36c821SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo;
2666d36c821SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
2676d36c821SAlexander Motin 	struct hdaa_chan *ch = &devinfo->chans[as->chans[0]];
2686d36c821SAlexander Motin 	struct hdaa_widget *w;
2696d36c821SAlexander Motin 	uint8_t *eld;
270c597c557SChristos Margiolis 	int total, sub, assume, channels;
271c597c557SChristos Margiolis 	size_t i;
2726d36c821SAlexander Motin 	uint16_t cpins, upins, tpins;
2736d36c821SAlexander Motin 
2746d36c821SAlexander Motin 	cpins = upins = 0;
2756d36c821SAlexander Motin 	eld = NULL;
2766d36c821SAlexander Motin 	for (i = 0; i < 16; i++) {
2776d36c821SAlexander Motin 		if (as->pins[i] <= 0)
2786d36c821SAlexander Motin 			continue;
2796d36c821SAlexander Motin 		w = hdaa_widget_get(devinfo, as->pins[i]);
2806d36c821SAlexander Motin 		if (w == NULL)
2816d36c821SAlexander Motin 			continue;
2826d36c821SAlexander Motin 		if (w->wclass.pin.connected == 1)
2836d36c821SAlexander Motin 			cpins |= (1 << i);
2846d36c821SAlexander Motin 		else if (w->wclass.pin.connected != 0)
2856d36c821SAlexander Motin 			upins |= (1 << i);
2866d36c821SAlexander Motin 		if (w->eld != NULL && w->eld_len >= 8)
2876d36c821SAlexander Motin 			eld = w->eld;
2886d36c821SAlexander Motin 	}
2896d36c821SAlexander Motin 	tpins = cpins | upins;
2906d36c821SAlexander Motin 	if (as->hpredir >= 0)
2916d36c821SAlexander Motin 		tpins &= 0x7fff;
2926d36c821SAlexander Motin 	if (tpins == 0)
2936d36c821SAlexander Motin 		tpins = as->pinset;
2946d36c821SAlexander Motin 
2956d36c821SAlexander Motin 	total = sub = assume = channels = 0;
2966d36c821SAlexander Motin 	if (eld) {
2976d36c821SAlexander Motin 		/* Map CEA speakers to sound(4) channels. */
2986d36c821SAlexander Motin 		if (eld[7] & 0x01) /* Front Left/Right */
2996d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3006d36c821SAlexander Motin 		if (eld[7] & 0x02) /* Low Frequency Effect */
3016d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_LF;
3026d36c821SAlexander Motin 		if (eld[7] & 0x04) /* Front Center */
3036d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FC;
3046d36c821SAlexander Motin 		if (eld[7] & 0x08) { /* Rear Left/Right */
3056d36c821SAlexander Motin 			/* If we have both RLR and RLRC, report RLR as side. */
3066d36c821SAlexander Motin 			if (eld[7] & 0x40) /* Rear Left/Right Center */
3076d36c821SAlexander Motin 			    channels |= SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR;
3086d36c821SAlexander Motin 			else
3096d36c821SAlexander Motin 			    channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3106d36c821SAlexander Motin 		}
3116d36c821SAlexander Motin 		if (eld[7] & 0x10) /* Rear center */
3126d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_BC;
3136d36c821SAlexander Motin 		if (eld[7] & 0x20) /* Front Left/Right Center */
3146d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FLC | SND_CHN_T_MASK_FRC;
3156d36c821SAlexander Motin 		if (eld[7] & 0x40) /* Rear Left/Right Center */
3166d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3176d36c821SAlexander Motin 	} else if (as->pinset != 0 && (tpins & 0xffe0) == 0) {
3186d36c821SAlexander Motin 		/* Map UAA speakers to sound(4) channels. */
3196d36c821SAlexander Motin 		if (tpins & 0x0001)
3206d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3216d36c821SAlexander Motin 		if (tpins & 0x0002)
3226d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF;
3236d36c821SAlexander Motin 		if (tpins & 0x0004)
3246d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3256d36c821SAlexander Motin 		if (tpins & 0x0008)
3266d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FLC | SND_CHN_T_MASK_FRC;
3276d36c821SAlexander Motin 		if (tpins & 0x0010) {
3286d36c821SAlexander Motin 			/* If there is no back pin, report side as back. */
3296d36c821SAlexander Motin 			if ((as->pinset & 0x0004) == 0)
3306d36c821SAlexander Motin 			    channels |= SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR;
3316d36c821SAlexander Motin 			else
3326d36c821SAlexander Motin 			    channels |= SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR;
3336d36c821SAlexander Motin 		}
3346d36c821SAlexander Motin 	} else if (as->mixed) {
3356d36c821SAlexander Motin 		/* Mixed assoc can be only stereo or theoretically mono. */
3366d36c821SAlexander Motin 		if (ch->channels == 1)
3376d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FC;
3386d36c821SAlexander Motin 		else
3396d36c821SAlexander Motin 			channels |= SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3406d36c821SAlexander Motin 	}
3416d36c821SAlexander Motin 	if (channels) {	/* We have some usable channels info. */
3426d36c821SAlexander Motin 		HDA_BOOTVERBOSE(
3436d36c821SAlexander Motin 			device_printf(pdevinfo->dev, "%s channel set is: ",
3446d36c821SAlexander Motin 			    as->dir == HDAA_CTL_OUT ? "Playback" : "Recording");
3456d36c821SAlexander Motin 			for (i = 0; i < SND_CHN_T_MAX; i++)
3466d36c821SAlexander Motin 				if (channels & (1 << i))
3476d36c821SAlexander Motin 					printf("%s, ", channel_names[i]);
3486d36c821SAlexander Motin 			printf("\n");
3496d36c821SAlexander Motin 		);
3506d36c821SAlexander Motin 		/* Look for maximal fitting matrix. */
351c597c557SChristos Margiolis 		for (i = 0; i < nitems(matrixes); i++) {
3526d36c821SAlexander Motin 			if (as->pinset != 0 && matrixes[i].analog == 0)
3536d36c821SAlexander Motin 				continue;
3546d36c821SAlexander Motin 			if ((matrixes[i].m.mask & ~channels) == 0) {
3556d36c821SAlexander Motin 				total = matrixes[i].m.channels;
3566d36c821SAlexander Motin 				sub = matrixes[i].m.ext;
3576d36c821SAlexander Motin 			}
3586d36c821SAlexander Motin 		}
3596d36c821SAlexander Motin 	}
3606d36c821SAlexander Motin 	if (total == 0) {
3616d36c821SAlexander Motin 		assume = 1;
3626d36c821SAlexander Motin 		total = ch->channels;
3636d36c821SAlexander Motin 		sub = (total == 6 || total == 8) ? 1 : 0;
3646d36c821SAlexander Motin 	}
3656d36c821SAlexander Motin 	HDA_BOOTVERBOSE(
3666d36c821SAlexander Motin 		device_printf(pdevinfo->dev,
3676d36c821SAlexander Motin 		    "%s channel matrix is: %s%d.%d (%s)\n",
3686d36c821SAlexander Motin 		    as->dir == HDAA_CTL_OUT ? "Playback" : "Recording",
3696d36c821SAlexander Motin 		    assume ? "unknown, assuming " : "", total - sub, sub,
3706d36c821SAlexander Motin 		    cpins != 0 ? "connected" :
3716d36c821SAlexander Motin 		    (upins != 0 ? "unknown" : "disconnected"));
3726d36c821SAlexander Motin 	);
3736d36c821SAlexander Motin }
3746d36c821SAlexander Motin 
3757c6b05d2SAlexander Motin /*
376d9360bbfSAlexander Motin  * Headphones redirection change handler.
3777c6b05d2SAlexander Motin  */
3787c6b05d2SAlexander Motin static void
hdaa_hpredir_handler(struct hdaa_widget * w)379d9360bbfSAlexander Motin hdaa_hpredir_handler(struct hdaa_widget *w)
3807c6b05d2SAlexander Motin {
38188addcbeSAlexander Motin 	struct hdaa_devinfo *devinfo = w->devinfo;
382d9360bbfSAlexander Motin 	struct hdaa_audio_as *as = &devinfo->as[w->bindas];
38388addcbeSAlexander Motin 	struct hdaa_widget *w1;
3847c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
385d9360bbfSAlexander Motin 	uint32_t val;
386d9360bbfSAlexander Motin 	int j, connected = w->wclass.pin.connected;
3877c6b05d2SAlexander Motin 
3887c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
389d9360bbfSAlexander Motin 		device_printf((as->pdevinfo && as->pdevinfo->dev) ?
390d9360bbfSAlexander Motin 		    as->pdevinfo->dev : devinfo->dev,
391d9360bbfSAlexander Motin 		    "Redirect output to: %s\n",
392d9360bbfSAlexander Motin 		    connected ? "headphones": "main");
3937c6b05d2SAlexander Motin 	);
3947c6b05d2SAlexander Motin 	/* (Un)Mute headphone pin. */
3957c6b05d2SAlexander Motin 	ctl = hdaa_audio_ctl_amp_get(devinfo,
39688addcbeSAlexander Motin 	    w->nid, HDAA_CTL_IN, -1, 1);
3977c6b05d2SAlexander Motin 	if (ctl != NULL && ctl->mute) {
3987c6b05d2SAlexander Motin 		/* If pin has muter - use it. */
399d9360bbfSAlexander Motin 		val = connected ? 0 : 1;
4007c6b05d2SAlexander Motin 		if (val != ctl->forcemute) {
4017c6b05d2SAlexander Motin 			ctl->forcemute = val;
4027c6b05d2SAlexander Motin 			hdaa_audio_ctl_amp_set(ctl,
4037c6b05d2SAlexander Motin 			    HDAA_AMP_MUTE_DEFAULT,
4047c6b05d2SAlexander Motin 			    HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT);
4057c6b05d2SAlexander Motin 		}
4067c6b05d2SAlexander Motin 	} else {
4077c6b05d2SAlexander Motin 		/* If there is no muter - disable pin output. */
408d9360bbfSAlexander Motin 		if (connected)
4097c6b05d2SAlexander Motin 			val = w->wclass.pin.ctrl |
4107c6b05d2SAlexander Motin 			    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4117c6b05d2SAlexander Motin 		else
4127c6b05d2SAlexander Motin 			val = w->wclass.pin.ctrl &
4137c6b05d2SAlexander Motin 			    ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4147c6b05d2SAlexander Motin 		if (val != w->wclass.pin.ctrl) {
4157c6b05d2SAlexander Motin 			w->wclass.pin.ctrl = val;
4167c6b05d2SAlexander Motin 			hda_command(devinfo->dev,
4177c6b05d2SAlexander Motin 			    HDA_CMD_SET_PIN_WIDGET_CTRL(0,
4187c6b05d2SAlexander Motin 			    w->nid, w->wclass.pin.ctrl));
4197c6b05d2SAlexander Motin 		}
4207c6b05d2SAlexander Motin 	}
4217c6b05d2SAlexander Motin 	/* (Un)Mute other pins. */
4227c6b05d2SAlexander Motin 	for (j = 0; j < 15; j++) {
4237c6b05d2SAlexander Motin 		if (as->pins[j] <= 0)
4247c6b05d2SAlexander Motin 			continue;
4257c6b05d2SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo,
4267c6b05d2SAlexander Motin 		    as->pins[j], HDAA_CTL_IN, -1, 1);
4277c6b05d2SAlexander Motin 		if (ctl != NULL && ctl->mute) {
4287c6b05d2SAlexander Motin 			/* If pin has muter - use it. */
429d9360bbfSAlexander Motin 			val = connected ? 1 : 0;
4307c6b05d2SAlexander Motin 			if (val == ctl->forcemute)
4317c6b05d2SAlexander Motin 				continue;
4327c6b05d2SAlexander Motin 			ctl->forcemute = val;
4337c6b05d2SAlexander Motin 			hdaa_audio_ctl_amp_set(ctl,
4347c6b05d2SAlexander Motin 			    HDAA_AMP_MUTE_DEFAULT,
4357c6b05d2SAlexander Motin 			    HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT);
4367c6b05d2SAlexander Motin 			continue;
4377c6b05d2SAlexander Motin 		}
4387c6b05d2SAlexander Motin 		/* If there is no muter - disable pin output. */
43988addcbeSAlexander Motin 		w1 = hdaa_widget_get(devinfo, as->pins[j]);
440d9360bbfSAlexander Motin 		if (w1 != NULL) {
441d9360bbfSAlexander Motin 			if (connected)
44288addcbeSAlexander Motin 				val = w1->wclass.pin.ctrl &
4437c6b05d2SAlexander Motin 				    ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
4447c6b05d2SAlexander Motin 			else
44588addcbeSAlexander Motin 				val = w1->wclass.pin.ctrl |
4467c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
44788addcbeSAlexander Motin 			if (val != w1->wclass.pin.ctrl) {
44888addcbeSAlexander Motin 				w1->wclass.pin.ctrl = val;
4497c6b05d2SAlexander Motin 				hda_command(devinfo->dev,
4507c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL(0,
45188addcbeSAlexander Motin 				    w1->nid, w1->wclass.pin.ctrl));
4527c6b05d2SAlexander Motin 			}
4537c6b05d2SAlexander Motin 		}
4547c6b05d2SAlexander Motin 	}
4557c6b05d2SAlexander Motin }
4567c6b05d2SAlexander Motin 
4577c6b05d2SAlexander Motin /*
458d9360bbfSAlexander Motin  * Recording source change handler.
459d9360bbfSAlexander Motin  */
460d9360bbfSAlexander Motin static void
hdaa_autorecsrc_handler(struct hdaa_audio_as * as,struct hdaa_widget * w)461d9360bbfSAlexander Motin hdaa_autorecsrc_handler(struct hdaa_audio_as *as, struct hdaa_widget *w)
462d9360bbfSAlexander Motin {
463d9360bbfSAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo;
464d9360bbfSAlexander Motin 	struct hdaa_devinfo *devinfo;
465d9360bbfSAlexander Motin 	struct hdaa_widget *w1;
466d9360bbfSAlexander Motin 	int i, mask, fullmask, prio, bestprio;
467d9360bbfSAlexander Motin 	char buf[128];
468d9360bbfSAlexander Motin 
469d9360bbfSAlexander Motin 	if (!as->mixed || pdevinfo == NULL || pdevinfo->mixer == NULL)
470d9360bbfSAlexander Motin 		return;
471d9360bbfSAlexander Motin 	/* Don't touch anything if we asked not to. */
472d9360bbfSAlexander Motin 	if (pdevinfo->autorecsrc == 0 ||
473d9360bbfSAlexander Motin 	    (pdevinfo->autorecsrc == 1 && w != NULL))
474d9360bbfSAlexander Motin 		return;
475d9360bbfSAlexander Motin 	/* Don't touch anything if "mix" or "speaker" selected. */
476d9360bbfSAlexander Motin 	if (pdevinfo->recsrc & (SOUND_MASK_IMIX | SOUND_MASK_SPEAKER))
477d9360bbfSAlexander Motin 		return;
478d9360bbfSAlexander Motin 	/* Don't touch anything if several selected. */
479d9360bbfSAlexander Motin 	if (ffs(pdevinfo->recsrc) != fls(pdevinfo->recsrc))
480d9360bbfSAlexander Motin 		return;
481d9360bbfSAlexander Motin 	devinfo = pdevinfo->devinfo;
482d9360bbfSAlexander Motin 	mask = fullmask = 0;
483d9360bbfSAlexander Motin 	bestprio = 0;
484d9360bbfSAlexander Motin 	for (i = 0; i < 16; i++) {
485d9360bbfSAlexander Motin 		if (as->pins[i] <= 0)
486d9360bbfSAlexander Motin 			continue;
487d9360bbfSAlexander Motin 		w1 = hdaa_widget_get(devinfo, as->pins[i]);
488d9360bbfSAlexander Motin 		if (w1 == NULL || w1->enable == 0)
489d9360bbfSAlexander Motin 			continue;
490d9360bbfSAlexander Motin 		if (w1->wclass.pin.connected == 0)
491d9360bbfSAlexander Motin 			continue;
492d9360bbfSAlexander Motin 		prio = (w1->wclass.pin.connected == 1) ? 2 : 1;
493d9360bbfSAlexander Motin 		if (prio < bestprio)
494d9360bbfSAlexander Motin 			continue;
495d9360bbfSAlexander Motin 		if (prio > bestprio) {
496d9360bbfSAlexander Motin 			mask = 0;
497d9360bbfSAlexander Motin 			bestprio = prio;
498d9360bbfSAlexander Motin 		}
499d9360bbfSAlexander Motin 		mask |= (1 << w1->ossdev);
500d9360bbfSAlexander Motin 		fullmask |= (1 << w1->ossdev);
501d9360bbfSAlexander Motin 	}
502d9360bbfSAlexander Motin 	if (mask == 0)
503d9360bbfSAlexander Motin 		return;
504d9360bbfSAlexander Motin 	/* Prefer newly connected input. */
505d9360bbfSAlexander Motin 	if (w != NULL && (mask & (1 << w->ossdev)))
506d9360bbfSAlexander Motin 		mask = (1 << w->ossdev);
507d9360bbfSAlexander Motin 	/* Prefer previously selected input */
508d9360bbfSAlexander Motin 	if (mask & pdevinfo->recsrc)
509d9360bbfSAlexander Motin 		mask &= pdevinfo->recsrc;
510d9360bbfSAlexander Motin 	/* Prefer mic. */
511d9360bbfSAlexander Motin 	if (mask & SOUND_MASK_MIC)
512d9360bbfSAlexander Motin 		mask = SOUND_MASK_MIC;
513d9360bbfSAlexander Motin 	/* Prefer monitor (2nd mic). */
514d9360bbfSAlexander Motin 	if (mask & SOUND_MASK_MONITOR)
515d9360bbfSAlexander Motin 		mask = SOUND_MASK_MONITOR;
516d9360bbfSAlexander Motin 	/* Just take first one. */
517d9360bbfSAlexander Motin 	mask = (1 << (ffs(mask) - 1));
518d9360bbfSAlexander Motin 	HDA_BOOTVERBOSE(
519d9360bbfSAlexander Motin 		hdaa_audio_ctl_ossmixer_mask2allname(mask, buf, sizeof(buf));
520d9360bbfSAlexander Motin 		device_printf(pdevinfo->dev,
521d9360bbfSAlexander Motin 		    "Automatically set rec source to: %s\n", buf);
522d9360bbfSAlexander Motin 	);
523d9360bbfSAlexander Motin 	hdaa_unlock(devinfo);
524d9360bbfSAlexander Motin 	mix_setrecsrc(pdevinfo->mixer, mask);
525d9360bbfSAlexander Motin 	hdaa_lock(devinfo);
526d9360bbfSAlexander Motin }
527d9360bbfSAlexander Motin 
528d9360bbfSAlexander Motin /*
529d9360bbfSAlexander Motin  * Jack presence detection event handler.
530d9360bbfSAlexander Motin  */
531d9360bbfSAlexander Motin static void
hdaa_presence_handler(struct hdaa_widget * w)532d9360bbfSAlexander Motin hdaa_presence_handler(struct hdaa_widget *w)
533d9360bbfSAlexander Motin {
534d9360bbfSAlexander Motin 	struct hdaa_devinfo *devinfo = w->devinfo;
535d9360bbfSAlexander Motin 	struct hdaa_audio_as *as;
536d9360bbfSAlexander Motin 	uint32_t res;
537401f7c11SAlexander Motin 	int connected, old;
538d9360bbfSAlexander Motin 
539d9360bbfSAlexander Motin 	if (w->enable == 0 || w->type !=
540d9360bbfSAlexander Motin 	    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
541d9360bbfSAlexander Motin 		return;
542d9360bbfSAlexander Motin 
543d9360bbfSAlexander Motin 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 ||
544d9360bbfSAlexander Motin 	    (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0)
545d9360bbfSAlexander Motin 		return;
546d9360bbfSAlexander Motin 
547d9360bbfSAlexander Motin 	res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid));
548d9360bbfSAlexander Motin 	connected = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0;
549d9360bbfSAlexander Motin 	if (devinfo->quirks & HDAA_QUIRK_SENSEINV)
550d9360bbfSAlexander Motin 		connected = !connected;
551401f7c11SAlexander Motin 	old = w->wclass.pin.connected;
552401f7c11SAlexander Motin 	if (connected == old)
553d9360bbfSAlexander Motin 		return;
554d9360bbfSAlexander Motin 	w->wclass.pin.connected = connected;
555d9360bbfSAlexander Motin 	HDA_BOOTVERBOSE(
556401f7c11SAlexander Motin 		if (connected || old != 2) {
557d9360bbfSAlexander Motin 			device_printf(devinfo->dev,
5586dcf64dfSUlrich Spörlein 			    "Pin sense: nid=%d sense=0x%08x (%sconnected)\n",
559401f7c11SAlexander Motin 			    w->nid, res, !connected ? "dis" : "");
560401f7c11SAlexander Motin 		}
561d9360bbfSAlexander Motin 	);
562d9360bbfSAlexander Motin 
563d9360bbfSAlexander Motin 	as = &devinfo->as[w->bindas];
564d9360bbfSAlexander Motin 	if (as->hpredir >= 0 && as->pins[15] == w->nid)
565d9360bbfSAlexander Motin 		hdaa_hpredir_handler(w);
566401f7c11SAlexander Motin 	if (as->dir == HDAA_CTL_IN && old != 2)
567d9360bbfSAlexander Motin 		hdaa_autorecsrc_handler(as, w);
5686d36c821SAlexander Motin 	if (old != 2)
5696d36c821SAlexander Motin 		hdaa_channels_handler(as);
570d9360bbfSAlexander Motin }
571d9360bbfSAlexander Motin 
572d9360bbfSAlexander Motin /*
573d9360bbfSAlexander Motin  * Callback for poll based presence detection.
5747c6b05d2SAlexander Motin  */
5757c6b05d2SAlexander Motin static void
hdaa_jack_poll_callback(void * arg)5767c6b05d2SAlexander Motin hdaa_jack_poll_callback(void *arg)
5777c6b05d2SAlexander Motin {
5787c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = arg;
57988addcbeSAlexander Motin 	struct hdaa_widget *w;
5807c6b05d2SAlexander Motin 	int i;
5817c6b05d2SAlexander Motin 
5827c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
5837c6b05d2SAlexander Motin 	if (devinfo->poll_ival == 0) {
5847c6b05d2SAlexander Motin 		hdaa_unlock(devinfo);
5857c6b05d2SAlexander Motin 		return;
5867c6b05d2SAlexander Motin 	}
5877c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->ascnt; i++) {
5887c6b05d2SAlexander Motin 		if (devinfo->as[i].hpredir < 0)
5897c6b05d2SAlexander Motin 			continue;
59088addcbeSAlexander Motin 		w = hdaa_widget_get(devinfo, devinfo->as[i].pins[15]);
59188addcbeSAlexander Motin 		if (w == NULL || w->enable == 0 || w->type !=
59288addcbeSAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
59388addcbeSAlexander Motin 			continue;
594d9360bbfSAlexander Motin 		hdaa_presence_handler(w);
5957c6b05d2SAlexander Motin 	}
5967c6b05d2SAlexander Motin 	callout_reset(&devinfo->poll_jack, devinfo->poll_ival,
5977c6b05d2SAlexander Motin 	    hdaa_jack_poll_callback, devinfo);
5987c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
5997c6b05d2SAlexander Motin }
6007c6b05d2SAlexander Motin 
60188addcbeSAlexander Motin static void
hdaa_eld_dump(struct hdaa_widget * w)60288addcbeSAlexander Motin hdaa_eld_dump(struct hdaa_widget *w)
60388addcbeSAlexander Motin {
60488addcbeSAlexander Motin 	struct hdaa_devinfo *devinfo = w->devinfo;
60588addcbeSAlexander Motin 	device_t dev = devinfo->dev;
60688addcbeSAlexander Motin 	uint8_t *sad;
607557627ddSWarner Losh 	int mnl, i, sadc, fmt;
60888addcbeSAlexander Motin 
60988addcbeSAlexander Motin 	if (w->eld == NULL || w->eld_len < 4)
61088addcbeSAlexander Motin 		return;
61188addcbeSAlexander Motin 	device_printf(dev,
61288addcbeSAlexander Motin 	    "ELD nid=%d: ELD_Ver=%u Baseline_ELD_Len=%u\n",
61388addcbeSAlexander Motin 	    w->nid, w->eld[0] >> 3, w->eld[2]);
61488addcbeSAlexander Motin 	if ((w->eld[0] >> 3) != 0x02)
61588addcbeSAlexander Motin 		return;
61688addcbeSAlexander Motin 	mnl = w->eld[4] & 0x1f;
61788addcbeSAlexander Motin 	device_printf(dev,
61888addcbeSAlexander Motin 	    "ELD nid=%d: CEA_EDID_Ver=%u MNL=%u\n",
61988addcbeSAlexander Motin 	    w->nid, w->eld[4] >> 5, mnl);
62088addcbeSAlexander Motin 	sadc = w->eld[5] >> 4;
62188addcbeSAlexander Motin 	device_printf(dev,
62288addcbeSAlexander Motin 	    "ELD nid=%d: SAD_Count=%u Conn_Type=%u S_AI=%u HDCP=%u\n",
62388addcbeSAlexander Motin 	    w->nid, sadc, (w->eld[5] >> 2) & 0x3,
62488addcbeSAlexander Motin 	    (w->eld[5] >> 1) & 0x1, w->eld[5] & 0x1);
62588addcbeSAlexander Motin 	device_printf(dev,
62688addcbeSAlexander Motin 	    "ELD nid=%d: Aud_Synch_Delay=%ums\n",
62788addcbeSAlexander Motin 	    w->nid, w->eld[6] * 2);
62888addcbeSAlexander Motin 	device_printf(dev,
62988addcbeSAlexander Motin 	    "ELD nid=%d: Channels=0x%b\n",
63088addcbeSAlexander Motin 	    w->nid, w->eld[7],
63188addcbeSAlexander Motin 	    "\020\07RLRC\06FLRC\05RC\04RLR\03FC\02LFE\01FLR");
63288addcbeSAlexander Motin 	device_printf(dev,
63388addcbeSAlexander Motin 	    "ELD nid=%d: Port_ID=0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
63488addcbeSAlexander Motin 	    w->nid, w->eld[8], w->eld[9], w->eld[10], w->eld[11],
63588addcbeSAlexander Motin 	    w->eld[12], w->eld[13], w->eld[14], w->eld[15]);
63688addcbeSAlexander Motin 	device_printf(dev,
63788addcbeSAlexander Motin 	    "ELD nid=%d: Manufacturer_Name=0x%02x%02x\n",
63888addcbeSAlexander Motin 	    w->nid, w->eld[16], w->eld[17]);
63988addcbeSAlexander Motin 	device_printf(dev,
64088addcbeSAlexander Motin 	    "ELD nid=%d: Product_Code=0x%02x%02x\n",
64188addcbeSAlexander Motin 	    w->nid, w->eld[18], w->eld[19]);
64288addcbeSAlexander Motin 	device_printf(dev,
64388addcbeSAlexander Motin 	    "ELD nid=%d: Monitor_Name_String='%.*s'\n",
64488addcbeSAlexander Motin 	    w->nid, mnl, &w->eld[20]);
64588addcbeSAlexander Motin 	for (i = 0; i < sadc; i++) {
64688addcbeSAlexander Motin 		sad = &w->eld[20 + mnl + i * 3];
64788addcbeSAlexander Motin 		fmt = (sad[0] >> 3) & 0x0f;
64888addcbeSAlexander Motin 		if (fmt == HDA_HDMI_CODING_TYPE_REF_CTX) {
64988addcbeSAlexander Motin 			fmt = (sad[2] >> 3) & 0x1f;
65088addcbeSAlexander Motin 			if (fmt < 1 || fmt > 3)
65188addcbeSAlexander Motin 				fmt = 0;
65288addcbeSAlexander Motin 			else
65388addcbeSAlexander Motin 				fmt += 14;
65488addcbeSAlexander Motin 		}
65588addcbeSAlexander Motin 		device_printf(dev,
65688addcbeSAlexander Motin 		    "ELD nid=%d: %s %dch freqs=0x%b",
65788addcbeSAlexander Motin 		    w->nid, HDA_HDMI_CODING_TYPES[fmt], (sad[0] & 0x07) + 1,
65888addcbeSAlexander Motin 		    sad[1], "\020\007192\006176\00596\00488\00348\00244\00132");
65988addcbeSAlexander Motin 		switch (fmt) {
66088addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_LPCM:
66188addcbeSAlexander Motin 			printf(" sizes=0x%b",
66288addcbeSAlexander Motin 			    sad[2] & 0x07, "\020\00324\00220\00116");
66388addcbeSAlexander Motin 			break;
66488addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_AC3:
66588addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_MPEG1:
66688addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_MP3:
66788addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_MPEG2:
66888addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_AACLC:
66988addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_DTS:
67088addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_ATRAC:
67188addcbeSAlexander Motin 			printf(" max_bitrate=%d", sad[2] * 8000);
67288addcbeSAlexander Motin 			break;
67388addcbeSAlexander Motin 		case HDA_HDMI_CODING_TYPE_WMAPRO:
67488addcbeSAlexander Motin 			printf(" profile=%d", sad[2] & 0x07);
67588addcbeSAlexander Motin 			break;
67688addcbeSAlexander Motin 		}
67788addcbeSAlexander Motin 		printf("\n");
67888addcbeSAlexander Motin 	}
67988addcbeSAlexander Motin }
68088addcbeSAlexander Motin 
68188addcbeSAlexander Motin static void
hdaa_eld_handler(struct hdaa_widget * w)68288addcbeSAlexander Motin hdaa_eld_handler(struct hdaa_widget *w)
68388addcbeSAlexander Motin {
68488addcbeSAlexander Motin 	struct hdaa_devinfo *devinfo = w->devinfo;
68588addcbeSAlexander Motin 	uint32_t res;
68688addcbeSAlexander Motin 	int i;
68788addcbeSAlexander Motin 
68888addcbeSAlexander Motin 	if (w->enable == 0 || w->type !=
68988addcbeSAlexander Motin 	    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
69088addcbeSAlexander Motin 		return;
69188addcbeSAlexander Motin 
692d9360bbfSAlexander Motin 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 ||
693d9360bbfSAlexander Motin 	    (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0)
694d9360bbfSAlexander Motin 		return;
695d9360bbfSAlexander Motin 
696d9360bbfSAlexander Motin 	res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid));
697d9360bbfSAlexander Motin 	if ((w->eld != 0) == ((res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) != 0))
698d9360bbfSAlexander Motin 		return;
69988addcbeSAlexander Motin 	if (w->eld != NULL) {
70088addcbeSAlexander Motin 		w->eld_len = 0;
70188addcbeSAlexander Motin 		free(w->eld, M_HDAA);
70288addcbeSAlexander Motin 		w->eld = NULL;
70388addcbeSAlexander Motin 	}
70488addcbeSAlexander Motin 	HDA_BOOTVERBOSE(
70588addcbeSAlexander Motin 		device_printf(devinfo->dev,
7066dcf64dfSUlrich Spörlein 		    "Pin sense: nid=%d sense=0x%08x "
70788addcbeSAlexander Motin 		    "(%sconnected, ELD %svalid)\n",
70888addcbeSAlexander Motin 		    w->nid, res,
70988addcbeSAlexander Motin 		    (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ? "" : "dis",
71088addcbeSAlexander Motin 		    (res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) ? "" : "in");
71188addcbeSAlexander Motin 	);
71288addcbeSAlexander Motin 	if ((res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) == 0)
71388addcbeSAlexander Motin 		return;
71488addcbeSAlexander Motin 
71588addcbeSAlexander Motin 	res = hda_command(devinfo->dev,
71688addcbeSAlexander Motin 	    HDA_CMD_GET_HDMI_DIP_SIZE(0, w->nid, 0x08));
71788addcbeSAlexander Motin 	if (res == HDA_INVALID)
71888addcbeSAlexander Motin 		return;
71988addcbeSAlexander Motin 	w->eld_len = res & 0xff;
72088addcbeSAlexander Motin 	if (w->eld_len != 0)
72188addcbeSAlexander Motin 		w->eld = malloc(w->eld_len, M_HDAA, M_ZERO | M_NOWAIT);
72288addcbeSAlexander Motin 	if (w->eld == NULL) {
72388addcbeSAlexander Motin 		w->eld_len = 0;
72488addcbeSAlexander Motin 		return;
72588addcbeSAlexander Motin 	}
72688addcbeSAlexander Motin 
72788addcbeSAlexander Motin 	for (i = 0; i < w->eld_len; i++) {
72888addcbeSAlexander Motin 		res = hda_command(devinfo->dev,
72988addcbeSAlexander Motin 		    HDA_CMD_GET_HDMI_ELDD(0, w->nid, i));
73088addcbeSAlexander Motin 		if (res & 0x80000000)
73188addcbeSAlexander Motin 			w->eld[i] = res & 0xff;
73288addcbeSAlexander Motin 	}
73388addcbeSAlexander Motin 	HDA_BOOTVERBOSE(
73488addcbeSAlexander Motin 		hdaa_eld_dump(w);
73588addcbeSAlexander Motin 	);
7366d36c821SAlexander Motin 	hdaa_channels_handler(&devinfo->as[w->bindas]);
73788addcbeSAlexander Motin }
73888addcbeSAlexander Motin 
739d9360bbfSAlexander Motin /*
740d9360bbfSAlexander Motin  * Pin sense initializer.
741d9360bbfSAlexander Motin  */
74288addcbeSAlexander Motin static void
hdaa_sense_init(struct hdaa_devinfo * devinfo)743d9360bbfSAlexander Motin hdaa_sense_init(struct hdaa_devinfo *devinfo)
74488addcbeSAlexander Motin {
745d9360bbfSAlexander Motin 	struct hdaa_audio_as *as;
74688addcbeSAlexander Motin 	struct hdaa_widget *w;
747d9360bbfSAlexander Motin 	int i, poll = 0;
74888addcbeSAlexander Motin 
74988addcbeSAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
75088addcbeSAlexander Motin 		w = hdaa_widget_get(devinfo, i);
751d9360bbfSAlexander Motin 		if (w == NULL || w->enable == 0 || w->type !=
75288addcbeSAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
75388addcbeSAlexander Motin 			continue;
754742963ffSAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) {
755742963ffSAlexander Motin 			if (w->unsol < 0)
75688addcbeSAlexander Motin 				w->unsol = HDAC_UNSOL_ALLOC(
757742963ffSAlexander Motin 				    device_get_parent(devinfo->dev),
758742963ffSAlexander Motin 				    devinfo->dev, w->nid);
75988addcbeSAlexander Motin 			hda_command(devinfo->dev,
76088addcbeSAlexander Motin 			    HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid,
761d9360bbfSAlexander Motin 			    HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | w->unsol));
76288addcbeSAlexander Motin 		}
763d9360bbfSAlexander Motin 		as = &devinfo->as[w->bindas];
764d9360bbfSAlexander Motin 		if (as->hpredir >= 0 && as->pins[15] == w->nid) {
765d9360bbfSAlexander Motin 			if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 ||
766d9360bbfSAlexander Motin 			    (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) {
767d9360bbfSAlexander Motin 				device_printf(devinfo->dev,
768d9360bbfSAlexander Motin 				    "No presence detection support at nid %d\n",
769076b76e8SAlexander Motin 				    w->nid);
770d9360bbfSAlexander Motin 			} else {
771d9360bbfSAlexander Motin 				if (w->unsol < 0)
772d9360bbfSAlexander Motin 					poll = 1;
773d9360bbfSAlexander Motin 				HDA_BOOTVERBOSE(
774d9360bbfSAlexander Motin 					device_printf(devinfo->dev,
775d9360bbfSAlexander Motin 					    "Headphones redirection for "
776d9360bbfSAlexander Motin 					    "association %d nid=%d using %s.\n",
777d9360bbfSAlexander Motin 					    w->bindas, w->nid,
778076b76e8SAlexander Motin 					    (w->unsol < 0) ? "polling" :
779d9360bbfSAlexander Motin 					    "unsolicited responses");
780d9360bbfSAlexander Motin 				);
78174b8d63dSPedro F. Giffuni 			}
782d9360bbfSAlexander Motin 		}
783d9360bbfSAlexander Motin 		hdaa_presence_handler(w);
784d9360bbfSAlexander Motin 		if (!HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) &&
785d9360bbfSAlexander Motin 		    !HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
786d9360bbfSAlexander Motin 			continue;
78788addcbeSAlexander Motin 		hdaa_eld_handler(w);
78888addcbeSAlexander Motin 	}
789d9360bbfSAlexander Motin 	if (poll) {
790d9360bbfSAlexander Motin 		callout_reset(&devinfo->poll_jack, 1,
791d9360bbfSAlexander Motin 		    hdaa_jack_poll_callback, devinfo);
792d9360bbfSAlexander Motin 	}
79388addcbeSAlexander Motin }
79488addcbeSAlexander Motin 
79588addcbeSAlexander Motin static void
hdaa_sense_deinit(struct hdaa_devinfo * devinfo)796d9360bbfSAlexander Motin hdaa_sense_deinit(struct hdaa_devinfo *devinfo)
79788addcbeSAlexander Motin {
79888addcbeSAlexander Motin 	struct hdaa_widget *w;
79988addcbeSAlexander Motin 	int i;
80088addcbeSAlexander Motin 
801d9360bbfSAlexander Motin 	callout_stop(&devinfo->poll_jack);
80288addcbeSAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
80388addcbeSAlexander Motin 		w = hdaa_widget_get(devinfo, i);
804d9360bbfSAlexander Motin 		if (w == NULL || w->enable == 0 || w->type !=
80588addcbeSAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
80688addcbeSAlexander Motin 			continue;
80788addcbeSAlexander Motin 		if (w->unsol < 0)
80888addcbeSAlexander Motin 			continue;
80988addcbeSAlexander Motin 		hda_command(devinfo->dev,
81088addcbeSAlexander Motin 		    HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, 0));
81188addcbeSAlexander Motin 		HDAC_UNSOL_FREE(
81288addcbeSAlexander Motin 		    device_get_parent(devinfo->dev), devinfo->dev,
81388addcbeSAlexander Motin 		    w->unsol);
81488addcbeSAlexander Motin 		w->unsol = -1;
8157c6b05d2SAlexander Motin 	}
8167c6b05d2SAlexander Motin }
8177c6b05d2SAlexander Motin 
8187c6b05d2SAlexander Motin uint32_t
hdaa_widget_pin_patch(uint32_t config,const char * str)8197c6b05d2SAlexander Motin hdaa_widget_pin_patch(uint32_t config, const char *str)
8207c6b05d2SAlexander Motin {
8217c6b05d2SAlexander Motin 	char buf[256];
8227c6b05d2SAlexander Motin 	char *key, *value, *rest, *bad;
8237c6b05d2SAlexander Motin 	int ival, i;
8247c6b05d2SAlexander Motin 
8257c6b05d2SAlexander Motin 	strlcpy(buf, str, sizeof(buf));
8267c6b05d2SAlexander Motin 	rest = buf;
8277c6b05d2SAlexander Motin 	while ((key = strsep(&rest, "=")) != NULL) {
8287c6b05d2SAlexander Motin 		value = strsep(&rest, " \t");
8297c6b05d2SAlexander Motin 		if (value == NULL)
8307c6b05d2SAlexander Motin 			break;
8317c6b05d2SAlexander Motin 		ival = strtol(value, &bad, 10);
8327c6b05d2SAlexander Motin 		if (strcmp(key, "seq") == 0) {
8337c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK;
8347c6b05d2SAlexander Motin 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) &
8357c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK);
8367c6b05d2SAlexander Motin 		} else if (strcmp(key, "as") == 0) {
8377c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK;
8387c6b05d2SAlexander Motin 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) &
8397c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK);
8407c6b05d2SAlexander Motin 		} else if (strcmp(key, "misc") == 0) {
8417c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK;
8427c6b05d2SAlexander Motin 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) &
8437c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_MISC_MASK);
8447c6b05d2SAlexander Motin 		} else if (strcmp(key, "color") == 0) {
8457c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK;
8467c6b05d2SAlexander Motin 			if (bad[0] == 0) {
8477c6b05d2SAlexander Motin 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) &
8487c6b05d2SAlexander Motin 				    HDA_CONFIG_DEFAULTCONF_COLOR_MASK);
84974b8d63dSPedro F. Giffuni 			}
8507c6b05d2SAlexander Motin 			for (i = 0; i < 16; i++) {
8517c6b05d2SAlexander Motin 				if (strcasecmp(HDA_COLORS[i], value) == 0) {
8527c6b05d2SAlexander Motin 					config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT);
8537c6b05d2SAlexander Motin 					break;
8547c6b05d2SAlexander Motin 				}
8557c6b05d2SAlexander Motin 			}
8567c6b05d2SAlexander Motin 		} else if (strcmp(key, "ctype") == 0) {
8577c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK;
8587c6b05d2SAlexander Motin 			if (bad[0] == 0) {
8597c6b05d2SAlexander Motin 			config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) &
8607c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK);
8617c6b05d2SAlexander Motin 			}
8627c6b05d2SAlexander Motin 			for (i = 0; i < 16; i++) {
8637c6b05d2SAlexander Motin 				if (strcasecmp(HDA_CONNECTORS[i], value) == 0) {
8647c6b05d2SAlexander Motin 					config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT);
8657c6b05d2SAlexander Motin 					break;
8667c6b05d2SAlexander Motin 				}
8677c6b05d2SAlexander Motin 			}
8687c6b05d2SAlexander Motin 		} else if (strcmp(key, "device") == 0) {
8697c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
8707c6b05d2SAlexander Motin 			if (bad[0] == 0) {
8717c6b05d2SAlexander Motin 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) &
8727c6b05d2SAlexander Motin 				    HDA_CONFIG_DEFAULTCONF_DEVICE_MASK);
8737c6b05d2SAlexander Motin 				continue;
87474b8d63dSPedro F. Giffuni 			}
8757c6b05d2SAlexander Motin 			for (i = 0; i < 16; i++) {
8767c6b05d2SAlexander Motin 				if (strcasecmp(HDA_DEVS[i], value) == 0) {
8777c6b05d2SAlexander Motin 					config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT);
8787c6b05d2SAlexander Motin 					break;
8797c6b05d2SAlexander Motin 				}
8807c6b05d2SAlexander Motin 			}
8817c6b05d2SAlexander Motin 		} else if (strcmp(key, "loc") == 0) {
8827c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK;
8837c6b05d2SAlexander Motin 			if (bad[0] == 0) {
8847c6b05d2SAlexander Motin 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) &
8857c6b05d2SAlexander Motin 				    HDA_CONFIG_DEFAULTCONF_LOCATION_MASK);
8867c6b05d2SAlexander Motin 				continue;
8877c6b05d2SAlexander Motin 			}
8887c6b05d2SAlexander Motin 			for (i = 0; i < 64; i++) {
8897c6b05d2SAlexander Motin 				if (strcasecmp(HDA_LOCS[i], value) == 0) {
8907c6b05d2SAlexander Motin 					config |= (i << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT);
8917c6b05d2SAlexander Motin 					break;
8927c6b05d2SAlexander Motin 				}
8937c6b05d2SAlexander Motin 			}
8947c6b05d2SAlexander Motin 		} else if (strcmp(key, "conn") == 0) {
8957c6b05d2SAlexander Motin 			config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK;
8967c6b05d2SAlexander Motin 			if (bad[0] == 0) {
8977c6b05d2SAlexander Motin 				config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) &
8987c6b05d2SAlexander Motin 				    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK);
8997c6b05d2SAlexander Motin 				continue;
90074b8d63dSPedro F. Giffuni 			}
9017c6b05d2SAlexander Motin 			for (i = 0; i < 4; i++) {
9027c6b05d2SAlexander Motin 				if (strcasecmp(HDA_CONNS[i], value) == 0) {
9037c6b05d2SAlexander Motin 					config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT);
9047c6b05d2SAlexander Motin 					break;
9057c6b05d2SAlexander Motin 				}
9067c6b05d2SAlexander Motin 			}
9077c6b05d2SAlexander Motin 		}
9087c6b05d2SAlexander Motin 	}
9097c6b05d2SAlexander Motin 	return (config);
9107c6b05d2SAlexander Motin }
9117c6b05d2SAlexander Motin 
9127c6b05d2SAlexander Motin uint32_t
hdaa_gpio_patch(uint32_t gpio,const char * str)9137c6b05d2SAlexander Motin hdaa_gpio_patch(uint32_t gpio, const char *str)
9147c6b05d2SAlexander Motin {
9157c6b05d2SAlexander Motin 	char buf[256];
9167c6b05d2SAlexander Motin 	char *key, *value, *rest;
9177c6b05d2SAlexander Motin 	int ikey, i;
9187c6b05d2SAlexander Motin 
9197c6b05d2SAlexander Motin 	strlcpy(buf, str, sizeof(buf));
9207c6b05d2SAlexander Motin 	rest = buf;
9217c6b05d2SAlexander Motin 	while ((key = strsep(&rest, "=")) != NULL) {
9227c6b05d2SAlexander Motin 		value = strsep(&rest, " \t");
9237c6b05d2SAlexander Motin 		if (value == NULL)
9247c6b05d2SAlexander Motin 			break;
9257c6b05d2SAlexander Motin 		ikey = strtol(key, NULL, 10);
9267c6b05d2SAlexander Motin 		if (ikey < 0 || ikey > 7)
9277c6b05d2SAlexander Motin 			continue;
9287c6b05d2SAlexander Motin 		for (i = 0; i < 7; i++) {
9297c6b05d2SAlexander Motin 			if (strcasecmp(HDA_GPIO_ACTIONS[i], value) == 0) {
9307c6b05d2SAlexander Motin 				gpio &= ~HDAA_GPIO_MASK(ikey);
9317c6b05d2SAlexander Motin 				gpio |= i << HDAA_GPIO_SHIFT(ikey);
9327c6b05d2SAlexander Motin 				break;
9337c6b05d2SAlexander Motin 			}
9347c6b05d2SAlexander Motin 		}
9357c6b05d2SAlexander Motin 	}
9367c6b05d2SAlexander Motin 	return (gpio);
9377c6b05d2SAlexander Motin }
9387c6b05d2SAlexander Motin 
9397c6b05d2SAlexander Motin static void
hdaa_local_patch_pin(struct hdaa_widget * w)9407c6b05d2SAlexander Motin hdaa_local_patch_pin(struct hdaa_widget *w)
9417c6b05d2SAlexander Motin {
9427c6b05d2SAlexander Motin 	device_t dev = w->devinfo->dev;
9437c6b05d2SAlexander Motin 	const char *res = NULL;
9447c6b05d2SAlexander Motin 	uint32_t config, orig;
9457c6b05d2SAlexander Motin 	char buf[32];
9467c6b05d2SAlexander Motin 
9477c6b05d2SAlexander Motin 	config = orig = w->wclass.pin.config;
9487c6b05d2SAlexander Motin 	snprintf(buf, sizeof(buf), "cad%u.nid%u.config",
9497c6b05d2SAlexander Motin 	    hda_get_codec_id(dev), w->nid);
9507c6b05d2SAlexander Motin 	if (resource_string_value(device_get_name(
9517c6b05d2SAlexander Motin 	    device_get_parent(device_get_parent(dev))),
9527c6b05d2SAlexander Motin 	    device_get_unit(device_get_parent(device_get_parent(dev))),
9537c6b05d2SAlexander Motin 	    buf, &res) == 0) {
9547c6b05d2SAlexander Motin 		if (strncmp(res, "0x", 2) == 0) {
9557c6b05d2SAlexander Motin 			config = strtol(res + 2, NULL, 16);
9567c6b05d2SAlexander Motin 		} else {
9577c6b05d2SAlexander Motin 			config = hdaa_widget_pin_patch(config, res);
9587c6b05d2SAlexander Motin 		}
9597c6b05d2SAlexander Motin 	}
9607c6b05d2SAlexander Motin 	snprintf(buf, sizeof(buf), "nid%u.config", w->nid);
9617c6b05d2SAlexander Motin 	if (resource_string_value(device_get_name(dev), device_get_unit(dev),
9627c6b05d2SAlexander Motin 	    buf, &res) == 0) {
9637c6b05d2SAlexander Motin 		if (strncmp(res, "0x", 2) == 0) {
9647c6b05d2SAlexander Motin 			config = strtol(res + 2, NULL, 16);
9657c6b05d2SAlexander Motin 		} else {
9667c6b05d2SAlexander Motin 			config = hdaa_widget_pin_patch(config, res);
9677c6b05d2SAlexander Motin 		}
9687c6b05d2SAlexander Motin 	}
9697c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
9707c6b05d2SAlexander Motin 		if (config != orig)
9717c6b05d2SAlexander Motin 			device_printf(w->devinfo->dev,
9727c6b05d2SAlexander Motin 			    "Patching pin config nid=%u 0x%08x -> 0x%08x\n",
9737c6b05d2SAlexander Motin 			    w->nid, orig, config);
9747c6b05d2SAlexander Motin 	);
9757c6b05d2SAlexander Motin 	w->wclass.pin.newconf = w->wclass.pin.config = config;
9767c6b05d2SAlexander Motin }
9777c6b05d2SAlexander Motin 
9787ec13509SAlexander Motin static void
hdaa_dump_audio_formats_sb(struct sbuf * sb,uint32_t fcap,uint32_t pcmcap)9797ec13509SAlexander Motin hdaa_dump_audio_formats_sb(struct sbuf *sb, uint32_t fcap, uint32_t pcmcap)
9807ec13509SAlexander Motin {
9817ec13509SAlexander Motin 	uint32_t cap;
9827ec13509SAlexander Motin 
9837ec13509SAlexander Motin 	cap = fcap;
9847ec13509SAlexander Motin 	if (cap != 0) {
9857ec13509SAlexander Motin 		sbuf_printf(sb, "     Stream cap: 0x%08x", cap);
9867ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap))
9877ec13509SAlexander Motin 			sbuf_printf(sb, " AC3");
9887ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap))
9897ec13509SAlexander Motin 			sbuf_printf(sb, " FLOAT32");
9907ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
9917ec13509SAlexander Motin 			sbuf_printf(sb, " PCM");
9927ec13509SAlexander Motin 		sbuf_printf(sb, "\n");
9937ec13509SAlexander Motin 	}
9947ec13509SAlexander Motin 	cap = pcmcap;
9957ec13509SAlexander Motin 	if (cap != 0) {
9967ec13509SAlexander Motin 		sbuf_printf(sb, "        PCM cap: 0x%08x", cap);
9977ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap))
9987ec13509SAlexander Motin 			sbuf_printf(sb, " 8");
9997ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap))
10007ec13509SAlexander Motin 			sbuf_printf(sb, " 16");
10017ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap))
10027ec13509SAlexander Motin 			sbuf_printf(sb, " 20");
10037ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap))
10047ec13509SAlexander Motin 			sbuf_printf(sb, " 24");
10057ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap))
10067ec13509SAlexander Motin 			sbuf_printf(sb, " 32");
10077ec13509SAlexander Motin 		sbuf_printf(sb, " bits,");
10087ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap))
10097ec13509SAlexander Motin 			sbuf_printf(sb, " 8");
10107ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap))
10117ec13509SAlexander Motin 			sbuf_printf(sb, " 11");
10127ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap))
10137ec13509SAlexander Motin 			sbuf_printf(sb, " 16");
10147ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap))
10157ec13509SAlexander Motin 			sbuf_printf(sb, " 22");
10167ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap))
10177ec13509SAlexander Motin 			sbuf_printf(sb, " 32");
10187ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap))
10197ec13509SAlexander Motin 			sbuf_printf(sb, " 44");
10207ec13509SAlexander Motin 		sbuf_printf(sb, " 48");
10217ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap))
10227ec13509SAlexander Motin 			sbuf_printf(sb, " 88");
10237ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap))
10247ec13509SAlexander Motin 			sbuf_printf(sb, " 96");
10257ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap))
10267ec13509SAlexander Motin 			sbuf_printf(sb, " 176");
10277ec13509SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap))
10287ec13509SAlexander Motin 			sbuf_printf(sb, " 192");
10297ec13509SAlexander Motin 		sbuf_printf(sb, " KHz\n");
10307ec13509SAlexander Motin 	}
10317ec13509SAlexander Motin }
10327ec13509SAlexander Motin 
10337ec13509SAlexander Motin static void
hdaa_dump_pin_sb(struct sbuf * sb,struct hdaa_widget * w)10347ec13509SAlexander Motin hdaa_dump_pin_sb(struct sbuf *sb, struct hdaa_widget *w)
10357ec13509SAlexander Motin {
10367ec13509SAlexander Motin 	uint32_t pincap, conf;
10377ec13509SAlexander Motin 
10387ec13509SAlexander Motin 	pincap = w->wclass.pin.cap;
10397ec13509SAlexander Motin 
10407ec13509SAlexander Motin 	sbuf_printf(sb, "        Pin cap: 0x%08x", pincap);
10417ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap))
10427ec13509SAlexander Motin 		sbuf_printf(sb, " ISC");
10437ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap))
10447ec13509SAlexander Motin 		sbuf_printf(sb, " TRQD");
10457ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap))
10467ec13509SAlexander Motin 		sbuf_printf(sb, " PDC");
10477ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
10487ec13509SAlexander Motin 		sbuf_printf(sb, " HP");
10497ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap))
10507ec13509SAlexander Motin 		sbuf_printf(sb, " OUT");
10517ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap))
10527ec13509SAlexander Motin 		sbuf_printf(sb, " IN");
10537ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap))
10547ec13509SAlexander Motin 		sbuf_printf(sb, " BAL");
10557ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_HDMI(pincap))
10567ec13509SAlexander Motin 		sbuf_printf(sb, " HDMI");
10577ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) {
10587ec13509SAlexander Motin 		sbuf_printf(sb, " VREF[");
10597ec13509SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
10607ec13509SAlexander Motin 			sbuf_printf(sb, " 50");
10617ec13509SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
10627ec13509SAlexander Motin 			sbuf_printf(sb, " 80");
10637ec13509SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
10647ec13509SAlexander Motin 			sbuf_printf(sb, " 100");
10657ec13509SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap))
10667ec13509SAlexander Motin 			sbuf_printf(sb, " GROUND");
10677ec13509SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap))
10687ec13509SAlexander Motin 			sbuf_printf(sb, " HIZ");
10697ec13509SAlexander Motin 		sbuf_printf(sb, " ]");
10707ec13509SAlexander Motin 	}
10717ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap))
10727ec13509SAlexander Motin 		sbuf_printf(sb, " EAPD");
10737ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_DP(pincap))
10747ec13509SAlexander Motin 		sbuf_printf(sb, " DP");
10757ec13509SAlexander Motin 	if (HDA_PARAM_PIN_CAP_HBR(pincap))
10767ec13509SAlexander Motin 		sbuf_printf(sb, " HBR");
10777ec13509SAlexander Motin 	sbuf_printf(sb, "\n");
10787ec13509SAlexander Motin 	conf = w->wclass.pin.config;
10797ec13509SAlexander Motin 	sbuf_printf(sb, "     Pin config: 0x%08x", conf);
10807ec13509SAlexander Motin 	sbuf_printf(sb, " as=%d seq=%d "
10817ec13509SAlexander Motin 	    "device=%s conn=%s ctype=%s loc=%s color=%s misc=%d\n",
10827ec13509SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf),
10837ec13509SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf),
10847ec13509SAlexander Motin 	    HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)],
10857ec13509SAlexander Motin 	    HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)],
10867ec13509SAlexander Motin 	    HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)],
10877ec13509SAlexander Motin 	    HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)],
10887ec13509SAlexander Motin 	    HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)],
10897ec13509SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_MISC(conf));
10907ec13509SAlexander Motin 	sbuf_printf(sb, "    Pin control: 0x%08x", w->wclass.pin.ctrl);
10917ec13509SAlexander Motin 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE)
10927ec13509SAlexander Motin 		sbuf_printf(sb, " HP");
10937ec13509SAlexander Motin 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE)
10947ec13509SAlexander Motin 		sbuf_printf(sb, " IN");
10957ec13509SAlexander Motin 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE)
10967ec13509SAlexander Motin 		sbuf_printf(sb, " OUT");
10977ec13509SAlexander Motin 	if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
10987ec13509SAlexander Motin 		if ((w->wclass.pin.ctrl &
10997ec13509SAlexander Motin 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) == 0x03)
11007ec13509SAlexander Motin 			sbuf_printf(sb, " HBR");
11017ec13509SAlexander Motin 		else if ((w->wclass.pin.ctrl &
11027ec13509SAlexander Motin 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
11037ec13509SAlexander Motin 			sbuf_printf(sb, " EPTs");
11047ec13509SAlexander Motin 	} else {
11057ec13509SAlexander Motin 		if ((w->wclass.pin.ctrl &
11067ec13509SAlexander Motin 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
11077ec13509SAlexander Motin 			sbuf_printf(sb, " VREFs");
11087ec13509SAlexander Motin 	}
11097ec13509SAlexander Motin 	sbuf_printf(sb, "\n");
11107ec13509SAlexander Motin }
11117ec13509SAlexander Motin 
11127ec13509SAlexander Motin static void
hdaa_dump_amp_sb(struct sbuf * sb,uint32_t cap,const char * banner)1113abe917adSMarius Strobl hdaa_dump_amp_sb(struct sbuf *sb, uint32_t cap, const char *banner)
11147ec13509SAlexander Motin {
11157ec13509SAlexander Motin 	int offset, size, step;
11167ec13509SAlexander Motin 
11177ec13509SAlexander Motin 	offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap);
11187ec13509SAlexander Motin 	size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap);
11197ec13509SAlexander Motin 	step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap);
11207ec13509SAlexander Motin 	sbuf_printf(sb, "     %s amp: 0x%08x "
11217ec13509SAlexander Motin 	    "mute=%d step=%d size=%d offset=%d (%+d/%+ddB)\n",
11227ec13509SAlexander Motin 	    banner, cap,
11237ec13509SAlexander Motin 	    HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap),
11247ec13509SAlexander Motin 	    step, size, offset,
11257ec13509SAlexander Motin 	    ((0 - offset) * (size + 1)) / 4,
11267ec13509SAlexander Motin 	    ((step - offset) * (size + 1)) / 4);
11277ec13509SAlexander Motin }
11287ec13509SAlexander Motin 
11297ec13509SAlexander Motin static int
hdaa_sysctl_caps(SYSCTL_HANDLER_ARGS)11307ec13509SAlexander Motin hdaa_sysctl_caps(SYSCTL_HANDLER_ARGS)
11317ec13509SAlexander Motin {
11327ec13509SAlexander Motin 	struct hdaa_devinfo *devinfo;
11337ec13509SAlexander Motin 	struct hdaa_widget *w, *cw;
11347ec13509SAlexander Motin 	struct sbuf sb;
11357ec13509SAlexander Motin 	char buf[64];
11367ec13509SAlexander Motin 	int error, j;
11377ec13509SAlexander Motin 
11387ec13509SAlexander Motin 	w = (struct hdaa_widget *)oidp->oid_arg1;
11397ec13509SAlexander Motin 	devinfo = w->devinfo;
11407ec13509SAlexander Motin 	sbuf_new_for_sysctl(&sb, NULL, 256, req);
11417ec13509SAlexander Motin 
11427ec13509SAlexander Motin 	sbuf_printf(&sb, "%s%s\n", w->name,
11437ec13509SAlexander Motin 	    (w->enable == 0) ? " [DISABLED]" : "");
11447ec13509SAlexander Motin 	sbuf_printf(&sb, "     Widget cap: 0x%08x",
11457ec13509SAlexander Motin 	    w->param.widget_cap);
11467ec13509SAlexander Motin 	if (w->param.widget_cap & 0x0ee1) {
11477ec13509SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap))
11487ec13509SAlexander Motin 		    sbuf_printf(&sb, " LRSWAP");
11497ec13509SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap))
11507ec13509SAlexander Motin 		    sbuf_printf(&sb, " PWR");
11517ec13509SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap))
11527ec13509SAlexander Motin 		    sbuf_printf(&sb, " DIGITAL");
11537ec13509SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap))
11547ec13509SAlexander Motin 		    sbuf_printf(&sb, " UNSOL");
11557ec13509SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap))
11567ec13509SAlexander Motin 		    sbuf_printf(&sb, " PROC");
11577ec13509SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap))
11587ec13509SAlexander Motin 		    sbuf_printf(&sb, " STRIPE(x%d)",
11597ec13509SAlexander Motin 			1 << (fls(w->wclass.conv.stripecap) - 1));
11607ec13509SAlexander Motin 		j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap);
11617ec13509SAlexander Motin 		if (j == 1)
11627ec13509SAlexander Motin 		    sbuf_printf(&sb, " STEREO");
11637ec13509SAlexander Motin 		else if (j > 1)
11647ec13509SAlexander Motin 		    sbuf_printf(&sb, " %dCH", j + 1);
11657ec13509SAlexander Motin 	}
11667ec13509SAlexander Motin 	sbuf_printf(&sb, "\n");
11677ec13509SAlexander Motin 	if (w->bindas != -1) {
11687ec13509SAlexander Motin 		sbuf_printf(&sb, "    Association: %d (0x%04x)\n",
11697ec13509SAlexander Motin 		    w->bindas, w->bindseqmask);
11707ec13509SAlexander Motin 	}
11717ec13509SAlexander Motin 	if (w->ossmask != 0 || w->ossdev >= 0) {
11727ec13509SAlexander Motin 		sbuf_printf(&sb, "            OSS: %s",
11737ec13509SAlexander Motin 		    hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf)));
11747ec13509SAlexander Motin 		if (w->ossdev >= 0)
11757ec13509SAlexander Motin 		    sbuf_printf(&sb, " (%s)", ossnames[w->ossdev]);
11767ec13509SAlexander Motin 		sbuf_printf(&sb, "\n");
11777ec13509SAlexander Motin 	}
11787ec13509SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
11797ec13509SAlexander Motin 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
11807ec13509SAlexander Motin 		hdaa_dump_audio_formats_sb(&sb,
11817ec13509SAlexander Motin 		    w->param.supp_stream_formats,
11827ec13509SAlexander Motin 		    w->param.supp_pcm_size_rate);
11837ec13509SAlexander Motin 	} else if (w->type ==
11847ec13509SAlexander Motin 	    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin)
11857ec13509SAlexander Motin 		hdaa_dump_pin_sb(&sb, w);
11867ec13509SAlexander Motin 	if (w->param.eapdbtl != HDA_INVALID) {
11877ec13509SAlexander Motin 		sbuf_printf(&sb, "           EAPD: 0x%08x%s%s%s\n",
11887ec13509SAlexander Motin 		    w->param.eapdbtl,
11897ec13509SAlexander Motin 		    (w->param.eapdbtl & HDA_CMD_SET_EAPD_BTL_ENABLE_LR_SWAP) ?
11907ec13509SAlexander Motin 		     " LRSWAP" : "",
11917ec13509SAlexander Motin 		    (w->param.eapdbtl & HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ?
11927ec13509SAlexander Motin 		     " EAPD" : "",
11937ec13509SAlexander Motin 		    (w->param.eapdbtl & HDA_CMD_SET_EAPD_BTL_ENABLE_BTL) ?
11947ec13509SAlexander Motin 		     " BTL" : "");
11957ec13509SAlexander Motin 	}
11967ec13509SAlexander Motin 	if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) &&
11977ec13509SAlexander Motin 	    w->param.outamp_cap != 0)
11987ec13509SAlexander Motin 		hdaa_dump_amp_sb(&sb, w->param.outamp_cap, "Output");
11997ec13509SAlexander Motin 	if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) &&
12007ec13509SAlexander Motin 	    w->param.inamp_cap != 0)
12017ec13509SAlexander Motin 		hdaa_dump_amp_sb(&sb, w->param.inamp_cap, " Input");
12027ec13509SAlexander Motin 	if (w->nconns > 0)
12037ec13509SAlexander Motin 		sbuf_printf(&sb, "    Connections: %d\n", w->nconns);
12047ec13509SAlexander Motin 	for (j = 0; j < w->nconns; j++) {
12057ec13509SAlexander Motin 		cw = hdaa_widget_get(devinfo, w->conns[j]);
12067ec13509SAlexander Motin 		sbuf_printf(&sb, "          + %s<- nid=%d [%s]",
12077ec13509SAlexander Motin 		    (w->connsenable[j] == 0)?"[DISABLED] ":"",
12087ec13509SAlexander Motin 		    w->conns[j], (cw == NULL) ? "GHOST!" : cw->name);
12097ec13509SAlexander Motin 		if (cw == NULL)
12107ec13509SAlexander Motin 			sbuf_printf(&sb, " [UNKNOWN]");
12117ec13509SAlexander Motin 		else if (cw->enable == 0)
12127ec13509SAlexander Motin 			sbuf_printf(&sb, " [DISABLED]");
12137ec13509SAlexander Motin 		if (w->nconns > 1 && w->selconn == j && w->type !=
12147ec13509SAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
12157ec13509SAlexander Motin 			sbuf_printf(&sb, " (selected)");
12167ec13509SAlexander Motin 		sbuf_printf(&sb, "\n");
12177ec13509SAlexander Motin 	}
12187ec13509SAlexander Motin 	error = sbuf_finish(&sb);
12197ec13509SAlexander Motin 	sbuf_delete(&sb);
12207ec13509SAlexander Motin 	return (error);
12217ec13509SAlexander Motin }
12227ec13509SAlexander Motin 
12237c6b05d2SAlexander Motin static int
hdaa_sysctl_config(SYSCTL_HANDLER_ARGS)12247c6b05d2SAlexander Motin hdaa_sysctl_config(SYSCTL_HANDLER_ARGS)
12257c6b05d2SAlexander Motin {
12267c6b05d2SAlexander Motin 	char buf[256];
12277c6b05d2SAlexander Motin 	int error;
12287c6b05d2SAlexander Motin 	uint32_t conf;
12297c6b05d2SAlexander Motin 
12307c6b05d2SAlexander Motin 	conf = *(uint32_t *)oidp->oid_arg1;
12317c6b05d2SAlexander Motin 	snprintf(buf, sizeof(buf), "0x%08x as=%d seq=%d "
12327c6b05d2SAlexander Motin 	    "device=%s conn=%s ctype=%s loc=%s color=%s misc=%d",
12337c6b05d2SAlexander Motin 	    conf,
12347c6b05d2SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf),
12357c6b05d2SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf),
12367c6b05d2SAlexander Motin 	    HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)],
12377c6b05d2SAlexander Motin 	    HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)],
12387c6b05d2SAlexander Motin 	    HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)],
12397c6b05d2SAlexander Motin 	    HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)],
12407c6b05d2SAlexander Motin 	    HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)],
12417c6b05d2SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_MISC(conf));
12427c6b05d2SAlexander Motin 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
12437c6b05d2SAlexander Motin 	if (error != 0 || req->newptr == NULL)
12447c6b05d2SAlexander Motin 		return (error);
12457c6b05d2SAlexander Motin 	if (strncmp(buf, "0x", 2) == 0)
12467c6b05d2SAlexander Motin 		conf = strtol(buf + 2, NULL, 16);
12477c6b05d2SAlexander Motin 	else
12487c6b05d2SAlexander Motin 		conf = hdaa_widget_pin_patch(conf, buf);
12497c6b05d2SAlexander Motin 	*(uint32_t *)oidp->oid_arg1 = conf;
12507c6b05d2SAlexander Motin 	return (0);
12517c6b05d2SAlexander Motin }
12527c6b05d2SAlexander Motin 
12537c6b05d2SAlexander Motin static void
hdaa_config_fetch(const char * str,uint32_t * on,uint32_t * off)12547c6b05d2SAlexander Motin hdaa_config_fetch(const char *str, uint32_t *on, uint32_t *off)
12557c6b05d2SAlexander Motin {
1256c597c557SChristos Margiolis 	size_t k;
1257c597c557SChristos Margiolis 	int i = 0, j, len, inv;
12587c6b05d2SAlexander Motin 
12597c6b05d2SAlexander Motin 	for (;;) {
12607c6b05d2SAlexander Motin 		while (str[i] != '\0' &&
12617c6b05d2SAlexander Motin 		    (str[i] == ',' || isspace(str[i]) != 0))
12627c6b05d2SAlexander Motin 			i++;
12637c6b05d2SAlexander Motin 		if (str[i] == '\0')
12647c6b05d2SAlexander Motin 			return;
12657c6b05d2SAlexander Motin 		j = i;
12667c6b05d2SAlexander Motin 		while (str[j] != '\0' &&
12677c6b05d2SAlexander Motin 		    !(str[j] == ',' || isspace(str[j]) != 0))
12687c6b05d2SAlexander Motin 			j++;
12697c6b05d2SAlexander Motin 		len = j - i;
12707c6b05d2SAlexander Motin 		if (len > 2 && strncmp(str + i, "no", 2) == 0)
12717c6b05d2SAlexander Motin 			inv = 2;
12727c6b05d2SAlexander Motin 		else
12737c6b05d2SAlexander Motin 			inv = 0;
1274abe917adSMarius Strobl 		for (k = 0; len > inv && k < nitems(hdaa_quirks_tab); k++) {
12757c6b05d2SAlexander Motin 			if (strncmp(str + i + inv,
12767c6b05d2SAlexander Motin 			    hdaa_quirks_tab[k].key, len - inv) != 0)
12777c6b05d2SAlexander Motin 				continue;
12787c6b05d2SAlexander Motin 			if (len - inv != strlen(hdaa_quirks_tab[k].key))
12797c6b05d2SAlexander Motin 				continue;
12807c6b05d2SAlexander Motin 			if (inv == 0) {
12817c6b05d2SAlexander Motin 				*on |= hdaa_quirks_tab[k].value;
12827c6b05d2SAlexander Motin 				*off &= ~hdaa_quirks_tab[k].value;
12837c6b05d2SAlexander Motin 			} else {
12847c6b05d2SAlexander Motin 				*off |= hdaa_quirks_tab[k].value;
12857c6b05d2SAlexander Motin 				*on &= ~hdaa_quirks_tab[k].value;
12867c6b05d2SAlexander Motin 			}
12877c6b05d2SAlexander Motin 			break;
12887c6b05d2SAlexander Motin 		}
12897c6b05d2SAlexander Motin 		i = j;
12907c6b05d2SAlexander Motin 	}
12917c6b05d2SAlexander Motin }
12927c6b05d2SAlexander Motin 
12937c6b05d2SAlexander Motin static int
hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS)12947c6b05d2SAlexander Motin hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS)
12957c6b05d2SAlexander Motin {
12967c6b05d2SAlexander Motin 	char buf[256];
1297c597c557SChristos Margiolis 	int error, n = 0;
1298c597c557SChristos Margiolis 	size_t i;
12997c6b05d2SAlexander Motin 	uint32_t quirks, quirks_off;
13007c6b05d2SAlexander Motin 
13017c6b05d2SAlexander Motin 	quirks = *(uint32_t *)oidp->oid_arg1;
13027c6b05d2SAlexander Motin 	buf[0] = 0;
1303abe917adSMarius Strobl 	for (i = 0; i < nitems(hdaa_quirks_tab); i++) {
13047c6b05d2SAlexander Motin 		if ((quirks & hdaa_quirks_tab[i].value) != 0)
13057c6b05d2SAlexander Motin 			n += snprintf(buf + n, sizeof(buf) - n, "%s%s",
13067c6b05d2SAlexander Motin 			    n != 0 ? "," : "", hdaa_quirks_tab[i].key);
13077c6b05d2SAlexander Motin 	}
13087c6b05d2SAlexander Motin 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
13097c6b05d2SAlexander Motin 	if (error != 0 || req->newptr == NULL)
13107c6b05d2SAlexander Motin 		return (error);
13117c6b05d2SAlexander Motin 	if (strncmp(buf, "0x", 2) == 0)
13127c6b05d2SAlexander Motin 		quirks = strtol(buf + 2, NULL, 16);
13137c6b05d2SAlexander Motin 	else {
13147c6b05d2SAlexander Motin 		quirks = 0;
13157c6b05d2SAlexander Motin 		hdaa_config_fetch(buf, &quirks, &quirks_off);
13167c6b05d2SAlexander Motin 	}
13177c6b05d2SAlexander Motin 	*(uint32_t *)oidp->oid_arg1 = quirks;
13187c6b05d2SAlexander Motin 	return (0);
13197c6b05d2SAlexander Motin }
13207c6b05d2SAlexander Motin 
13217c6b05d2SAlexander Motin static void
hdaa_local_patch(struct hdaa_devinfo * devinfo)13227c6b05d2SAlexander Motin hdaa_local_patch(struct hdaa_devinfo *devinfo)
13237c6b05d2SAlexander Motin {
13247c6b05d2SAlexander Motin 	struct hdaa_widget *w;
13257c6b05d2SAlexander Motin 	const char *res = NULL;
13267c6b05d2SAlexander Motin 	uint32_t quirks_on = 0, quirks_off = 0, x;
13277c6b05d2SAlexander Motin 	int i;
13287c6b05d2SAlexander Motin 
13297c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
13307c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
13317c6b05d2SAlexander Motin 		if (w == NULL)
13327c6b05d2SAlexander Motin 			continue;
13337c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
13347c6b05d2SAlexander Motin 			hdaa_local_patch_pin(w);
13357c6b05d2SAlexander Motin 	}
13367c6b05d2SAlexander Motin 
13377c6b05d2SAlexander Motin 	if (resource_string_value(device_get_name(devinfo->dev),
13387c6b05d2SAlexander Motin 	    device_get_unit(devinfo->dev), "config", &res) == 0) {
13397c6b05d2SAlexander Motin 		if (res != NULL && strlen(res) > 0)
13407c6b05d2SAlexander Motin 			hdaa_config_fetch(res, &quirks_on, &quirks_off);
13417c6b05d2SAlexander Motin 		devinfo->quirks |= quirks_on;
13427c6b05d2SAlexander Motin 		devinfo->quirks &= ~quirks_off;
13437c6b05d2SAlexander Motin 	}
13447c6b05d2SAlexander Motin 	if (devinfo->newquirks == -1)
13457c6b05d2SAlexander Motin 		devinfo->newquirks = devinfo->quirks;
13467c6b05d2SAlexander Motin 	else
13477c6b05d2SAlexander Motin 		devinfo->quirks = devinfo->newquirks;
13487c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
13497c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
13507c6b05d2SAlexander Motin 		    "Config options: 0x%08x\n", devinfo->quirks);
13517c6b05d2SAlexander Motin 	);
13527c6b05d2SAlexander Motin 
13537c6b05d2SAlexander Motin 	if (resource_string_value(device_get_name(devinfo->dev),
13547c6b05d2SAlexander Motin 	    device_get_unit(devinfo->dev), "gpio_config", &res) == 0) {
13557c6b05d2SAlexander Motin 		if (strncmp(res, "0x", 2) == 0) {
13567c6b05d2SAlexander Motin 			devinfo->gpio = strtol(res + 2, NULL, 16);
13577c6b05d2SAlexander Motin 		} else {
13587c6b05d2SAlexander Motin 			devinfo->gpio = hdaa_gpio_patch(devinfo->gpio, res);
13597c6b05d2SAlexander Motin 		}
13607c6b05d2SAlexander Motin 	}
13617c6b05d2SAlexander Motin 	if (devinfo->newgpio == -1)
13627c6b05d2SAlexander Motin 		devinfo->newgpio = devinfo->gpio;
13637c6b05d2SAlexander Motin 	else
13647c6b05d2SAlexander Motin 		devinfo->gpio = devinfo->newgpio;
13657c6b05d2SAlexander Motin 	if (devinfo->newgpo == -1)
13667c6b05d2SAlexander Motin 		devinfo->newgpo = devinfo->gpo;
13677c6b05d2SAlexander Motin 	else
13687c6b05d2SAlexander Motin 		devinfo->gpo = devinfo->newgpo;
13697c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
13707c6b05d2SAlexander Motin 		device_printf(devinfo->dev, "GPIO config options:");
13717c6b05d2SAlexander Motin 		for (i = 0; i < 7; i++) {
13727c6b05d2SAlexander Motin 			x = (devinfo->gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i);
13737c6b05d2SAlexander Motin 			if (x != 0)
13747c6b05d2SAlexander Motin 				printf(" %d=%s", i, HDA_GPIO_ACTIONS[x]);
13757c6b05d2SAlexander Motin 		}
13767c6b05d2SAlexander Motin 		printf("\n");
13777c6b05d2SAlexander Motin 	);
13787c6b05d2SAlexander Motin }
13797c6b05d2SAlexander Motin 
13807c6b05d2SAlexander Motin static void
hdaa_widget_connection_parse(struct hdaa_widget * w)13817c6b05d2SAlexander Motin hdaa_widget_connection_parse(struct hdaa_widget *w)
13827c6b05d2SAlexander Motin {
13837c6b05d2SAlexander Motin 	uint32_t res;
13847c6b05d2SAlexander Motin 	int i, j, max, ents, entnum;
13857c6b05d2SAlexander Motin 	nid_t nid = w->nid;
13867c6b05d2SAlexander Motin 	nid_t cnid, addcnid, prevcnid;
13877c6b05d2SAlexander Motin 
13887c6b05d2SAlexander Motin 	w->nconns = 0;
13897c6b05d2SAlexander Motin 
13907c6b05d2SAlexander Motin 	res = hda_command(w->devinfo->dev,
13917c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_CONN_LIST_LENGTH));
13927c6b05d2SAlexander Motin 
13937c6b05d2SAlexander Motin 	ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res);
13947c6b05d2SAlexander Motin 
13957c6b05d2SAlexander Motin 	if (ents < 1)
13967c6b05d2SAlexander Motin 		return;
13977c6b05d2SAlexander Motin 
13987c6b05d2SAlexander Motin 	entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4;
13997c6b05d2SAlexander Motin 	max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1;
14007c6b05d2SAlexander Motin 	prevcnid = 0;
14017c6b05d2SAlexander Motin 
14027c6b05d2SAlexander Motin #define CONN_RMASK(e)		(1 << ((32 / (e)) - 1))
14037c6b05d2SAlexander Motin #define CONN_NMASK(e)		(CONN_RMASK(e) - 1)
14047c6b05d2SAlexander Motin #define CONN_RESVAL(r, e, n)	((r) >> ((32 / (e)) * (n)))
14057c6b05d2SAlexander Motin #define CONN_RANGE(r, e, n)	(CONN_RESVAL(r, e, n) & CONN_RMASK(e))
14067c6b05d2SAlexander Motin #define CONN_CNID(r, e, n)	(CONN_RESVAL(r, e, n) & CONN_NMASK(e))
14077c6b05d2SAlexander Motin 
14087c6b05d2SAlexander Motin 	for (i = 0; i < ents; i += entnum) {
14097c6b05d2SAlexander Motin 		res = hda_command(w->devinfo->dev,
14107c6b05d2SAlexander Motin 		    HDA_CMD_GET_CONN_LIST_ENTRY(0, nid, i));
14117c6b05d2SAlexander Motin 		for (j = 0; j < entnum; j++) {
14127c6b05d2SAlexander Motin 			cnid = CONN_CNID(res, entnum, j);
14137c6b05d2SAlexander Motin 			if (cnid == 0) {
14147c6b05d2SAlexander Motin 				if (w->nconns < ents)
14157c6b05d2SAlexander Motin 					device_printf(w->devinfo->dev,
14167c6b05d2SAlexander Motin 					    "WARNING: nid=%d has zero cnid "
14177c6b05d2SAlexander Motin 					    "entnum=%d j=%d index=%d "
14187c6b05d2SAlexander Motin 					    "entries=%d found=%d res=0x%08x\n",
14197c6b05d2SAlexander Motin 					    nid, entnum, j, i,
14207c6b05d2SAlexander Motin 					    ents, w->nconns, res);
14217c6b05d2SAlexander Motin 				else
14227c6b05d2SAlexander Motin 					goto getconns_out;
14237c6b05d2SAlexander Motin 			}
14247c6b05d2SAlexander Motin 			if (cnid < w->devinfo->startnode ||
14257c6b05d2SAlexander Motin 			    cnid >= w->devinfo->endnode) {
14267c6b05d2SAlexander Motin 				HDA_BOOTVERBOSE(
14277c6b05d2SAlexander Motin 					device_printf(w->devinfo->dev,
14287c6b05d2SAlexander Motin 					    "WARNING: nid=%d has cnid outside "
14297c6b05d2SAlexander Motin 					    "of the AFG range j=%d "
14307c6b05d2SAlexander Motin 					    "entnum=%d index=%d res=0x%08x\n",
14317c6b05d2SAlexander Motin 					    nid, j, entnum, i, res);
14327c6b05d2SAlexander Motin 				);
14337c6b05d2SAlexander Motin 			}
14347c6b05d2SAlexander Motin 			if (CONN_RANGE(res, entnum, j) == 0)
14357c6b05d2SAlexander Motin 				addcnid = cnid;
14367c6b05d2SAlexander Motin 			else if (prevcnid == 0 || prevcnid >= cnid) {
14377c6b05d2SAlexander Motin 				device_printf(w->devinfo->dev,
14387c6b05d2SAlexander Motin 				    "WARNING: Invalid child range "
14397c6b05d2SAlexander Motin 				    "nid=%d index=%d j=%d entnum=%d "
14407c6b05d2SAlexander Motin 				    "prevcnid=%d cnid=%d res=0x%08x\n",
14417c6b05d2SAlexander Motin 				    nid, i, j, entnum, prevcnid,
14427c6b05d2SAlexander Motin 				    cnid, res);
14437c6b05d2SAlexander Motin 				addcnid = cnid;
14447c6b05d2SAlexander Motin 			} else
14457c6b05d2SAlexander Motin 				addcnid = prevcnid + 1;
14467c6b05d2SAlexander Motin 			while (addcnid <= cnid) {
14477c6b05d2SAlexander Motin 				if (w->nconns > max) {
14487c6b05d2SAlexander Motin 					device_printf(w->devinfo->dev,
14497c6b05d2SAlexander Motin 					    "Adding %d (nid=%d): "
14507c6b05d2SAlexander Motin 					    "Max connection reached! max=%d\n",
14517c6b05d2SAlexander Motin 					    addcnid, nid, max + 1);
14527c6b05d2SAlexander Motin 					goto getconns_out;
14537c6b05d2SAlexander Motin 				}
14547c6b05d2SAlexander Motin 				w->connsenable[w->nconns] = 1;
14557c6b05d2SAlexander Motin 				w->conns[w->nconns++] = addcnid++;
14567c6b05d2SAlexander Motin 			}
14577c6b05d2SAlexander Motin 			prevcnid = cnid;
14587c6b05d2SAlexander Motin 		}
14597c6b05d2SAlexander Motin 	}
14607c6b05d2SAlexander Motin 
14617c6b05d2SAlexander Motin getconns_out:
14627c6b05d2SAlexander Motin 	return;
14637c6b05d2SAlexander Motin }
14647c6b05d2SAlexander Motin 
14657c6b05d2SAlexander Motin static void
hdaa_widget_parse(struct hdaa_widget * w)14667c6b05d2SAlexander Motin hdaa_widget_parse(struct hdaa_widget *w)
14677c6b05d2SAlexander Motin {
14687c6b05d2SAlexander Motin 	device_t dev = w->devinfo->dev;
14697c6b05d2SAlexander Motin 	uint32_t wcap, cap;
14707c6b05d2SAlexander Motin 	nid_t nid = w->nid;
14717c6b05d2SAlexander Motin 	char buf[64];
14727c6b05d2SAlexander Motin 
14737c6b05d2SAlexander Motin 	w->param.widget_cap = wcap = hda_command(dev,
14747c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_AUDIO_WIDGET_CAP));
14757c6b05d2SAlexander Motin 	w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap);
14767c6b05d2SAlexander Motin 
14777c6b05d2SAlexander Motin 	hdaa_widget_connection_parse(w);
14787c6b05d2SAlexander Motin 
14797c6b05d2SAlexander Motin 	if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) {
14807c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap))
14817c6b05d2SAlexander Motin 			w->param.outamp_cap =
14827c6b05d2SAlexander Motin 			    hda_command(dev,
14837c6b05d2SAlexander Motin 			    HDA_CMD_GET_PARAMETER(0, nid,
14847c6b05d2SAlexander Motin 			    HDA_PARAM_OUTPUT_AMP_CAP));
14857c6b05d2SAlexander Motin 		else
14867c6b05d2SAlexander Motin 			w->param.outamp_cap =
14877c6b05d2SAlexander Motin 			    w->devinfo->outamp_cap;
14887c6b05d2SAlexander Motin 	} else
14897c6b05d2SAlexander Motin 		w->param.outamp_cap = 0;
14907c6b05d2SAlexander Motin 
14917c6b05d2SAlexander Motin 	if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) {
14927c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap))
14937c6b05d2SAlexander Motin 			w->param.inamp_cap =
14947c6b05d2SAlexander Motin 			    hda_command(dev,
14957c6b05d2SAlexander Motin 			    HDA_CMD_GET_PARAMETER(0, nid,
14967c6b05d2SAlexander Motin 			    HDA_PARAM_INPUT_AMP_CAP));
14977c6b05d2SAlexander Motin 		else
14987c6b05d2SAlexander Motin 			w->param.inamp_cap =
14997c6b05d2SAlexander Motin 			    w->devinfo->inamp_cap;
15007c6b05d2SAlexander Motin 	} else
15017c6b05d2SAlexander Motin 		w->param.inamp_cap = 0;
15027c6b05d2SAlexander Motin 
15037c6b05d2SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
15047c6b05d2SAlexander Motin 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
15057c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) {
15067c6b05d2SAlexander Motin 			cap = hda_command(dev,
15077c6b05d2SAlexander Motin 			    HDA_CMD_GET_PARAMETER(0, nid,
15087c6b05d2SAlexander Motin 			    HDA_PARAM_SUPP_STREAM_FORMATS));
15097c6b05d2SAlexander Motin 			w->param.supp_stream_formats = (cap != 0) ? cap :
15107c6b05d2SAlexander Motin 			    w->devinfo->supp_stream_formats;
15117c6b05d2SAlexander Motin 			cap = hda_command(dev,
15127c6b05d2SAlexander Motin 			    HDA_CMD_GET_PARAMETER(0, nid,
15137c6b05d2SAlexander Motin 			    HDA_PARAM_SUPP_PCM_SIZE_RATE));
15147c6b05d2SAlexander Motin 			w->param.supp_pcm_size_rate = (cap != 0) ? cap :
15157c6b05d2SAlexander Motin 			    w->devinfo->supp_pcm_size_rate;
15167c6b05d2SAlexander Motin 		} else {
15177c6b05d2SAlexander Motin 			w->param.supp_stream_formats =
15187c6b05d2SAlexander Motin 			    w->devinfo->supp_stream_formats;
15197c6b05d2SAlexander Motin 			w->param.supp_pcm_size_rate =
15207c6b05d2SAlexander Motin 			    w->devinfo->supp_pcm_size_rate;
15217c6b05d2SAlexander Motin 		}
15226fa8e691SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) {
15236fa8e691SAlexander Motin 			w->wclass.conv.stripecap = hda_command(dev,
15246fa8e691SAlexander Motin 			    HDA_CMD_GET_STRIPE_CONTROL(0, w->nid)) >> 20;
15256fa8e691SAlexander Motin 		} else
15266fa8e691SAlexander Motin 			w->wclass.conv.stripecap = 1;
15277c6b05d2SAlexander Motin 	} else {
15287c6b05d2SAlexander Motin 		w->param.supp_stream_formats = 0;
15297c6b05d2SAlexander Motin 		w->param.supp_pcm_size_rate = 0;
15307c6b05d2SAlexander Motin 	}
15317c6b05d2SAlexander Motin 
15327c6b05d2SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
15337c6b05d2SAlexander Motin 		w->wclass.pin.original = w->wclass.pin.newconf =
15347c6b05d2SAlexander Motin 		    w->wclass.pin.config = hda_command(dev,
15357c6b05d2SAlexander Motin 			HDA_CMD_GET_CONFIGURATION_DEFAULT(0, w->nid));
15367c6b05d2SAlexander Motin 		w->wclass.pin.cap = hda_command(dev,
1537db702c59SEitan Adler 		    HDA_CMD_GET_PARAMETER(0, w->nid, HDA_PARAM_PIN_CAP));
15387c6b05d2SAlexander Motin 		w->wclass.pin.ctrl = hda_command(dev,
15397c6b05d2SAlexander Motin 		    HDA_CMD_GET_PIN_WIDGET_CTRL(0, nid));
1540401f7c11SAlexander Motin 		w->wclass.pin.connected = 2;
15417c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_EAPD_CAP(w->wclass.pin.cap)) {
15427c6b05d2SAlexander Motin 			w->param.eapdbtl = hda_command(dev,
15437c6b05d2SAlexander Motin 			    HDA_CMD_GET_EAPD_BTL_ENABLE(0, nid));
15447c6b05d2SAlexander Motin 			w->param.eapdbtl &= 0x7;
15457c6b05d2SAlexander Motin 			w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
15467c6b05d2SAlexander Motin 		} else
15477c6b05d2SAlexander Motin 			w->param.eapdbtl = HDA_INVALID;
15487ec13509SAlexander Motin 	}
15497ec13509SAlexander Motin 	w->unsol = -1;
15507c6b05d2SAlexander Motin 
15517c6b05d2SAlexander Motin 	hdaa_unlock(w->devinfo);
15527ec13509SAlexander Motin 	snprintf(buf, sizeof(buf), "nid%d", w->nid);
15537ec13509SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
15547ec13509SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
15557ec13509SAlexander Motin 	    buf, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
15565b81187fSConrad Meyer 	    w, 0, hdaa_sysctl_caps, "A", "Node capabilities");
15577ec13509SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
15587c6b05d2SAlexander Motin 		snprintf(buf, sizeof(buf), "nid%d_config", w->nid);
15597c6b05d2SAlexander Motin 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
15607c6b05d2SAlexander Motin 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
15617c6b05d2SAlexander Motin 		    buf, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
15625b81187fSConrad Meyer 		    &w->wclass.pin.newconf, 0, hdaa_sysctl_config, "A",
15635b81187fSConrad Meyer 		    "Current pin configuration");
15647c6b05d2SAlexander Motin 		snprintf(buf, sizeof(buf), "nid%d_original", w->nid);
15657c6b05d2SAlexander Motin 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
15667c6b05d2SAlexander Motin 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
15677c6b05d2SAlexander Motin 		    buf, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
15685b81187fSConrad Meyer 		    &w->wclass.pin.original, 0, hdaa_sysctl_config, "A",
15695b81187fSConrad Meyer 		    "Original pin configuration");
15707c6b05d2SAlexander Motin 	}
15717ec13509SAlexander Motin 	hdaa_lock(w->devinfo);
15727c6b05d2SAlexander Motin }
15737c6b05d2SAlexander Motin 
15747c6b05d2SAlexander Motin static void
hdaa_widget_postprocess(struct hdaa_widget * w)15757c6b05d2SAlexander Motin hdaa_widget_postprocess(struct hdaa_widget *w)
15767c6b05d2SAlexander Motin {
1577abe917adSMarius Strobl 	const char *typestr;
15787c6b05d2SAlexander Motin 
15797c6b05d2SAlexander Motin 	w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(w->param.widget_cap);
15807c6b05d2SAlexander Motin 	switch (w->type) {
15817c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT:
15827c6b05d2SAlexander Motin 		typestr = "audio output";
15837c6b05d2SAlexander Motin 		break;
15847c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
15857c6b05d2SAlexander Motin 		typestr = "audio input";
15867c6b05d2SAlexander Motin 		break;
15877c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER:
15887c6b05d2SAlexander Motin 		typestr = "audio mixer";
15897c6b05d2SAlexander Motin 		break;
15907c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR:
15917c6b05d2SAlexander Motin 		typestr = "audio selector";
15927c6b05d2SAlexander Motin 		break;
15937c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
15947c6b05d2SAlexander Motin 		typestr = "pin";
15957c6b05d2SAlexander Motin 		break;
15967c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET:
15977c6b05d2SAlexander Motin 		typestr = "power widget";
15987c6b05d2SAlexander Motin 		break;
15997c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET:
16007c6b05d2SAlexander Motin 		typestr = "volume widget";
16017c6b05d2SAlexander Motin 		break;
16027c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET:
16037c6b05d2SAlexander Motin 		typestr = "beep widget";
16047c6b05d2SAlexander Motin 		break;
16057c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET:
16067c6b05d2SAlexander Motin 		typestr = "vendor widget";
16077c6b05d2SAlexander Motin 		break;
16087c6b05d2SAlexander Motin 	default:
16097c6b05d2SAlexander Motin 		typestr = "unknown type";
16107c6b05d2SAlexander Motin 		break;
16117c6b05d2SAlexander Motin 	}
16127c6b05d2SAlexander Motin 	strlcpy(w->name, typestr, sizeof(w->name));
16137c6b05d2SAlexander Motin 
16147c6b05d2SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
16157c6b05d2SAlexander Motin 		uint32_t config;
16167c6b05d2SAlexander Motin 		const char *devstr;
16177c6b05d2SAlexander Motin 		int conn, color;
16187c6b05d2SAlexander Motin 
16197c6b05d2SAlexander Motin 		config = w->wclass.pin.config;
16207c6b05d2SAlexander Motin 		devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >>
16217c6b05d2SAlexander Motin 		    HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT];
16227c6b05d2SAlexander Motin 		conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >>
16237c6b05d2SAlexander Motin 		    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT;
16247c6b05d2SAlexander Motin 		color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >>
16257c6b05d2SAlexander Motin 		    HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT;
16267c6b05d2SAlexander Motin 		strlcat(w->name, ": ", sizeof(w->name));
16277c6b05d2SAlexander Motin 		strlcat(w->name, devstr, sizeof(w->name));
16287c6b05d2SAlexander Motin 		strlcat(w->name, " (", sizeof(w->name));
16297c6b05d2SAlexander Motin 		if (conn == 0 && color != 0 && color != 15) {
16307c6b05d2SAlexander Motin 			strlcat(w->name, HDA_COLORS[color], sizeof(w->name));
16317c6b05d2SAlexander Motin 			strlcat(w->name, " ", sizeof(w->name));
16327c6b05d2SAlexander Motin 		}
16337c6b05d2SAlexander Motin 		strlcat(w->name, HDA_CONNS[conn], sizeof(w->name));
16347c6b05d2SAlexander Motin 		strlcat(w->name, ")", sizeof(w->name));
16357c6b05d2SAlexander Motin 	}
16367c6b05d2SAlexander Motin }
16377c6b05d2SAlexander Motin 
16387c6b05d2SAlexander Motin struct hdaa_widget *
hdaa_widget_get(struct hdaa_devinfo * devinfo,nid_t nid)16397c6b05d2SAlexander Motin hdaa_widget_get(struct hdaa_devinfo *devinfo, nid_t nid)
16407c6b05d2SAlexander Motin {
16417c6b05d2SAlexander Motin 	if (devinfo == NULL || devinfo->widget == NULL ||
16427c6b05d2SAlexander Motin 		    nid < devinfo->startnode || nid >= devinfo->endnode)
16437c6b05d2SAlexander Motin 		return (NULL);
16447c6b05d2SAlexander Motin 	return (&devinfo->widget[nid - devinfo->startnode]);
16457c6b05d2SAlexander Motin }
16467c6b05d2SAlexander Motin 
16477c6b05d2SAlexander Motin 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)16487c6b05d2SAlexander Motin hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *devinfo, nid_t nid,
16497c6b05d2SAlexander Motin 					int index, int lmute, int rmute,
16507c6b05d2SAlexander Motin 					int left, int right, int dir)
16517c6b05d2SAlexander Motin {
16527c6b05d2SAlexander Motin 	uint16_t v = 0;
16537c6b05d2SAlexander Motin 
16547c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
16557c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
16567c6b05d2SAlexander Motin 		    "Setting amplifier nid=%d index=%d %s mute=%d/%d vol=%d/%d\n",
16577c6b05d2SAlexander Motin 		    nid,index,dir ? "in" : "out",lmute,rmute,left,right);
16587c6b05d2SAlexander Motin 	);
16597c6b05d2SAlexander Motin 	if (left != right || lmute != rmute) {
16607c6b05d2SAlexander Motin 		v = (1 << (15 - dir)) | (1 << 13) | (index << 8) |
16617c6b05d2SAlexander Motin 		    (lmute << 7) | left;
16627c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
16637c6b05d2SAlexander Motin 		    HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v));
16647c6b05d2SAlexander Motin 		v = (1 << (15 - dir)) | (1 << 12) | (index << 8) |
16657c6b05d2SAlexander Motin 		    (rmute << 7) | right;
16667c6b05d2SAlexander Motin 	} else
16677c6b05d2SAlexander Motin 		v = (1 << (15 - dir)) | (3 << 12) | (index << 8) |
16687c6b05d2SAlexander Motin 		    (lmute << 7) | left;
16697c6b05d2SAlexander Motin 
16707c6b05d2SAlexander Motin 	hda_command(devinfo->dev,
16717c6b05d2SAlexander Motin 	    HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v));
16727c6b05d2SAlexander Motin }
16737c6b05d2SAlexander Motin 
16747c6b05d2SAlexander Motin static void
hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl * ctl,uint32_t mute,int left,int right)16757c6b05d2SAlexander Motin hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *ctl, uint32_t mute,
16767c6b05d2SAlexander Motin 						int left, int right)
16777c6b05d2SAlexander Motin {
16787c6b05d2SAlexander Motin 	nid_t nid;
16797c6b05d2SAlexander Motin 	int lmute, rmute;
16807c6b05d2SAlexander Motin 
16817c6b05d2SAlexander Motin 	nid = ctl->widget->nid;
16827c6b05d2SAlexander Motin 
16837c6b05d2SAlexander Motin 	/* Save new values if valid. */
16847c6b05d2SAlexander Motin 	if (mute != HDAA_AMP_MUTE_DEFAULT)
16857c6b05d2SAlexander Motin 		ctl->muted = mute;
16867c6b05d2SAlexander Motin 	if (left != HDAA_AMP_VOL_DEFAULT)
16877c6b05d2SAlexander Motin 		ctl->left = left;
16887c6b05d2SAlexander Motin 	if (right != HDAA_AMP_VOL_DEFAULT)
16897c6b05d2SAlexander Motin 		ctl->right = right;
16907c6b05d2SAlexander Motin 	/* Prepare effective values */
16917c6b05d2SAlexander Motin 	if (ctl->forcemute) {
16927c6b05d2SAlexander Motin 		lmute = 1;
16937c6b05d2SAlexander Motin 		rmute = 1;
16947c6b05d2SAlexander Motin 		left = 0;
16957c6b05d2SAlexander Motin 		right = 0;
16967c6b05d2SAlexander Motin 	} else {
16977c6b05d2SAlexander Motin 		lmute = HDAA_AMP_LEFT_MUTED(ctl->muted);
16987c6b05d2SAlexander Motin 		rmute = HDAA_AMP_RIGHT_MUTED(ctl->muted);
16997c6b05d2SAlexander Motin 		left = ctl->left;
17007c6b05d2SAlexander Motin 		right = ctl->right;
17017c6b05d2SAlexander Motin 	}
17027c6b05d2SAlexander Motin 	/* Apply effective values */
17037c6b05d2SAlexander Motin 	if (ctl->dir & HDAA_CTL_OUT)
17047c6b05d2SAlexander Motin 		hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index,
17057c6b05d2SAlexander Motin 		    lmute, rmute, left, right, 0);
17067c6b05d2SAlexander Motin 	if (ctl->dir & HDAA_CTL_IN)
17077c6b05d2SAlexander Motin 		hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index,
17087c6b05d2SAlexander Motin 		    lmute, rmute, left, right, 1);
17097c6b05d2SAlexander Motin }
17107c6b05d2SAlexander Motin 
17117c6b05d2SAlexander Motin static void
hdaa_widget_connection_select(struct hdaa_widget * w,uint8_t index)17127c6b05d2SAlexander Motin hdaa_widget_connection_select(struct hdaa_widget *w, uint8_t index)
17137c6b05d2SAlexander Motin {
17147c6b05d2SAlexander Motin 	if (w == NULL || w->nconns < 1 || index > (w->nconns - 1))
17157c6b05d2SAlexander Motin 		return;
17167c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
17177c6b05d2SAlexander Motin 		device_printf(w->devinfo->dev,
17187c6b05d2SAlexander Motin 		    "Setting selector nid=%d index=%d\n", w->nid, index);
17197c6b05d2SAlexander Motin 	);
17207c6b05d2SAlexander Motin 	hda_command(w->devinfo->dev,
17217c6b05d2SAlexander Motin 	    HDA_CMD_SET_CONNECTION_SELECT_CONTROL(0, w->nid, index));
17227c6b05d2SAlexander Motin 	w->selconn = index;
17237c6b05d2SAlexander Motin }
17247c6b05d2SAlexander Motin 
17257c6b05d2SAlexander Motin /****************************************************************************
17267c6b05d2SAlexander Motin  * Device Methods
17277c6b05d2SAlexander Motin  ****************************************************************************/
17287c6b05d2SAlexander Motin 
17297c6b05d2SAlexander Motin static void *
hdaa_channel_init(kobj_t obj,void * data,struct snd_dbuf * b,struct pcm_channel * c,int dir)17307c6b05d2SAlexander Motin hdaa_channel_init(kobj_t obj, void *data, struct snd_dbuf *b,
17317c6b05d2SAlexander Motin 					struct pcm_channel *c, int dir)
17327c6b05d2SAlexander Motin {
17337c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
17347c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = ch->pdevinfo;
17357c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
17367c6b05d2SAlexander Motin 
17377c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
17387c6b05d2SAlexander Motin 	if (devinfo->quirks & HDAA_QUIRK_FIXEDRATE) {
17397c6b05d2SAlexander Motin 		ch->caps.minspeed = ch->caps.maxspeed = 48000;
17407c6b05d2SAlexander Motin 		ch->pcmrates[0] = 48000;
17417c6b05d2SAlexander Motin 		ch->pcmrates[1] = 0;
17427c6b05d2SAlexander Motin 	}
17437c6b05d2SAlexander Motin 	ch->dir = dir;
17447c6b05d2SAlexander Motin 	ch->b = b;
17457c6b05d2SAlexander Motin 	ch->c = c;
17467c6b05d2SAlexander Motin 	ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt;
17477c6b05d2SAlexander Motin 	ch->blkcnt = pdevinfo->chan_blkcnt;
17487c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
17497c6b05d2SAlexander Motin 
17507c6b05d2SAlexander Motin 	if (sndbuf_alloc(ch->b, bus_get_dma_tag(devinfo->dev),
175180a79189SJustin Hibbits 	    hda_get_dma_nocache(devinfo->dev) ? BUS_DMA_NOCACHE :
175280a79189SJustin Hibbits 	    BUS_DMA_COHERENT,
17537c6b05d2SAlexander Motin 	    pdevinfo->chan_size) != 0)
17547c6b05d2SAlexander Motin 		return (NULL);
17557c6b05d2SAlexander Motin 
17567c6b05d2SAlexander Motin 	return (ch);
17577c6b05d2SAlexander Motin }
17587c6b05d2SAlexander Motin 
17597c6b05d2SAlexander Motin static int
hdaa_channel_setformat(kobj_t obj,void * data,uint32_t format)17607c6b05d2SAlexander Motin hdaa_channel_setformat(kobj_t obj, void *data, uint32_t format)
17617c6b05d2SAlexander Motin {
17627c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
17637c6b05d2SAlexander Motin 	int i;
17647c6b05d2SAlexander Motin 
17657c6b05d2SAlexander Motin 	for (i = 0; ch->caps.fmtlist[i] != 0; i++) {
17667c6b05d2SAlexander Motin 		if (format == ch->caps.fmtlist[i]) {
17677c6b05d2SAlexander Motin 			ch->fmt = format;
17687c6b05d2SAlexander Motin 			return (0);
17697c6b05d2SAlexander Motin 		}
17707c6b05d2SAlexander Motin 	}
17717c6b05d2SAlexander Motin 
17727c6b05d2SAlexander Motin 	return (EINVAL);
17737c6b05d2SAlexander Motin }
17747c6b05d2SAlexander Motin 
17757c6b05d2SAlexander Motin static uint32_t
hdaa_channel_setspeed(kobj_t obj,void * data,uint32_t speed)17767c6b05d2SAlexander Motin hdaa_channel_setspeed(kobj_t obj, void *data, uint32_t speed)
17777c6b05d2SAlexander Motin {
17787c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
17797c6b05d2SAlexander Motin 	uint32_t spd = 0, threshold;
17807c6b05d2SAlexander Motin 	int i;
17817c6b05d2SAlexander Motin 
17827c6b05d2SAlexander Motin 	/* First look for equal or multiple frequency. */
17837c6b05d2SAlexander Motin 	for (i = 0; ch->pcmrates[i] != 0; i++) {
17847c6b05d2SAlexander Motin 		spd = ch->pcmrates[i];
17857c6b05d2SAlexander Motin 		if (speed != 0 && spd / speed * speed == spd) {
17867c6b05d2SAlexander Motin 			ch->spd = spd;
17877c6b05d2SAlexander Motin 			return (spd);
17887c6b05d2SAlexander Motin 		}
17897c6b05d2SAlexander Motin 	}
17907c6b05d2SAlexander Motin 	/* If no match, just find nearest. */
17917c6b05d2SAlexander Motin 	for (i = 0; ch->pcmrates[i] != 0; i++) {
17927c6b05d2SAlexander Motin 		spd = ch->pcmrates[i];
17937c6b05d2SAlexander Motin 		threshold = spd + ((ch->pcmrates[i + 1] != 0) ?
17947c6b05d2SAlexander Motin 		    ((ch->pcmrates[i + 1] - spd) >> 1) : 0);
17957c6b05d2SAlexander Motin 		if (speed < threshold)
17967c6b05d2SAlexander Motin 			break;
17977c6b05d2SAlexander Motin 	}
17987c6b05d2SAlexander Motin 	ch->spd = spd;
17997c6b05d2SAlexander Motin 	return (spd);
18007c6b05d2SAlexander Motin }
18017c6b05d2SAlexander Motin 
18027c6b05d2SAlexander Motin static uint16_t
hdaa_stream_format(struct hdaa_chan * ch)18037c6b05d2SAlexander Motin hdaa_stream_format(struct hdaa_chan *ch)
18047c6b05d2SAlexander Motin {
18057c6b05d2SAlexander Motin 	int i;
18067c6b05d2SAlexander Motin 	uint16_t fmt;
18077c6b05d2SAlexander Motin 
18087c6b05d2SAlexander Motin 	fmt = 0;
18097c6b05d2SAlexander Motin 	if (ch->fmt & AFMT_S16_LE)
18107c6b05d2SAlexander Motin 		fmt |= ch->bit16 << 4;
18117c6b05d2SAlexander Motin 	else if (ch->fmt & AFMT_S32_LE)
18127c6b05d2SAlexander Motin 		fmt |= ch->bit32 << 4;
18137c6b05d2SAlexander Motin 	else
18147c6b05d2SAlexander Motin 		fmt |= 1 << 4;
18157c6b05d2SAlexander Motin 	for (i = 0; i < HDA_RATE_TAB_LEN; i++) {
18167c6b05d2SAlexander Motin 		if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) {
18177c6b05d2SAlexander Motin 			fmt |= hda_rate_tab[i].base;
18187c6b05d2SAlexander Motin 			fmt |= hda_rate_tab[i].mul;
18197c6b05d2SAlexander Motin 			fmt |= hda_rate_tab[i].div;
18207c6b05d2SAlexander Motin 			break;
18217c6b05d2SAlexander Motin 		}
18227c6b05d2SAlexander Motin 	}
18237c6b05d2SAlexander Motin 	fmt |= (AFMT_CHANNEL(ch->fmt) - 1);
18247c6b05d2SAlexander Motin 
18257c6b05d2SAlexander Motin 	return (fmt);
18267c6b05d2SAlexander Motin }
18277c6b05d2SAlexander Motin 
18286fa8e691SAlexander Motin static int
hdaa_allowed_stripes(uint16_t fmt)18296fa8e691SAlexander Motin hdaa_allowed_stripes(uint16_t fmt)
18306fa8e691SAlexander Motin {
18316fa8e691SAlexander Motin 	static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 };
18326fa8e691SAlexander Motin 	int size;
18336fa8e691SAlexander Motin 
18346fa8e691SAlexander Motin 	size = bits[(fmt >> 4) & 0x03];
18356fa8e691SAlexander Motin 	size *= (fmt & 0x0f) + 1;
18366fa8e691SAlexander Motin 	size *= ((fmt >> 11) & 0x07) + 1;
18376fa8e691SAlexander Motin 	return (0xffffffffU >> (32 - fls(size / 8)));
18386fa8e691SAlexander Motin }
18396fa8e691SAlexander Motin 
18407c6b05d2SAlexander Motin static void
hdaa_audio_setup(struct hdaa_chan * ch)18417c6b05d2SAlexander Motin hdaa_audio_setup(struct hdaa_chan *ch)
18427c6b05d2SAlexander Motin {
18437c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = &ch->devinfo->as[ch->as];
184488addcbeSAlexander Motin 	struct hdaa_widget *w, *wp;
184588addcbeSAlexander Motin 	int i, j, k, chn, cchn, totalchn, totalextchn, c;
18467c6b05d2SAlexander Motin 	uint16_t fmt, dfmt;
184788addcbeSAlexander Motin 	/* Mapping channel pairs to codec pins/converters. */
184888addcbeSAlexander Motin 	const static uint16_t convmap[2][5] =
18496d36c821SAlexander Motin 	    /*  1.0     2.0     4.0     5.1     7.1  */
18506d36c821SAlexander Motin 	    {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x4231 },	/* no dup. */
18516d36c821SAlexander Motin 	     { 0x0010, 0x0001, 0x2201, 0x2231, 0x4231 }};	/* side dup. */
185288addcbeSAlexander Motin 	/* Mapping formats to HDMI channel allocations. */
185388addcbeSAlexander Motin 	const static uint8_t hdmica[2][8] =
18546d36c821SAlexander Motin 	    /*  1     2     3     4     5     6     7     8  */
185588addcbeSAlexander Motin 	    {{ 0x02, 0x00, 0x04, 0x08, 0x0a, 0x0e, 0x12, 0x12 }, /* x.0 */
185688addcbeSAlexander Motin 	     { 0x01, 0x03, 0x01, 0x03, 0x09, 0x0b, 0x0f, 0x13 }}; /* x.1 */
185788addcbeSAlexander Motin 	/* Mapping formats to HDMI channels order. */
185888addcbeSAlexander Motin 	const static uint32_t hdmich[2][8] =
18596d36c821SAlexander Motin 	    /*  1  /  5     2  /  6     3  /  7     4  /  8  */
186088addcbeSAlexander Motin 	    {{ 0xFFFF0F00, 0xFFFFFF10, 0xFFF2FF10, 0xFF32FF10,
186188addcbeSAlexander Motin 	       0xFF324F10, 0xF5324F10, 0x54326F10, 0x54326F10 }, /* x.0 */
186288addcbeSAlexander Motin 	     { 0xFFFFF000, 0xFFFF0100, 0xFFFFF210, 0xFFFF2310,
186388addcbeSAlexander Motin 	       0xFF32F410, 0xFF324510, 0xF6324510, 0x76325410 }}; /* x.1 */
186488addcbeSAlexander Motin 	int convmapid = -1;
186588addcbeSAlexander Motin 	nid_t nid;
186688addcbeSAlexander Motin 	uint8_t csum;
18677c6b05d2SAlexander Motin 
18687c6b05d2SAlexander Motin 	totalchn = AFMT_CHANNEL(ch->fmt);
186988addcbeSAlexander Motin 	totalextchn = AFMT_EXTCHANNEL(ch->fmt);
18707c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
18717c6b05d2SAlexander Motin 		device_printf(ch->pdevinfo->dev,
187288addcbeSAlexander Motin 		    "PCMDIR_%s: Stream setup fmt=%08x (%d.%d) speed=%d\n",
18737c6b05d2SAlexander Motin 		    (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC",
187488addcbeSAlexander Motin 		    ch->fmt, totalchn - totalextchn, totalextchn, ch->spd);
18757c6b05d2SAlexander Motin 	);
18767c6b05d2SAlexander Motin 	fmt = hdaa_stream_format(ch);
18777c6b05d2SAlexander Motin 
187888addcbeSAlexander Motin 	/* Set channels to I/O converters mapping for known speaker setups. */
18796d36c821SAlexander Motin 	if ((as->pinset == 0x0007 || as->pinset == 0x0013) || /* Standard 5.1 */
18806d36c821SAlexander Motin 	    (as->pinset == 0x0017)) /* Standard 7.1 */
18816d36c821SAlexander Motin 		convmapid = (ch->dir == PCMDIR_PLAY);
18827c6b05d2SAlexander Motin 
18837c6b05d2SAlexander Motin 	dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN;
18847c6b05d2SAlexander Motin 	if (ch->fmt & AFMT_AC3)
18857c6b05d2SAlexander Motin 		dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO;
18867c6b05d2SAlexander Motin 
18877c6b05d2SAlexander Motin 	chn = 0;
18887c6b05d2SAlexander Motin 	for (i = 0; ch->io[i] != -1; i++) {
18897c6b05d2SAlexander Motin 		w = hdaa_widget_get(ch->devinfo, ch->io[i]);
18907c6b05d2SAlexander Motin 		if (w == NULL)
18917c6b05d2SAlexander Motin 			continue;
18927c6b05d2SAlexander Motin 
18937c6b05d2SAlexander Motin 		/* If HP redirection is enabled, but failed to use same
18947c6b05d2SAlexander Motin 		   DAC, make last DAC to duplicate first one. */
18957c6b05d2SAlexander Motin 		if (as->fakeredir && i == (as->pincnt - 1)) {
18967c6b05d2SAlexander Motin 			c = (ch->sid << 4);
18977c6b05d2SAlexander Motin 		} else {
189888addcbeSAlexander Motin 			/* Map channels to I/O converters, if set. */
189988addcbeSAlexander Motin 			if (convmapid >= 0)
190088addcbeSAlexander Motin 				chn = (((convmap[convmapid][totalchn / 2]
190188addcbeSAlexander Motin 				    >> i * 4) & 0xf) - 1) * 2;
19027c6b05d2SAlexander Motin 			if (chn < 0 || chn >= totalchn) {
19037c6b05d2SAlexander Motin 				c = 0;
19047c6b05d2SAlexander Motin 			} else {
19057c6b05d2SAlexander Motin 				c = (ch->sid << 4) | chn;
19067c6b05d2SAlexander Motin 			}
19077c6b05d2SAlexander Motin 		}
19087c6b05d2SAlexander Motin 		hda_command(ch->devinfo->dev,
19097c6b05d2SAlexander Motin 		    HDA_CMD_SET_CONV_FMT(0, ch->io[i], fmt));
19107c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
19117c6b05d2SAlexander Motin 			hda_command(ch->devinfo->dev,
19127c6b05d2SAlexander Motin 			    HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], dfmt));
19137c6b05d2SAlexander Motin 		}
19147c6b05d2SAlexander Motin 		hda_command(ch->devinfo->dev,
19157c6b05d2SAlexander Motin 		    HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], c));
19166fa8e691SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) {
19176fa8e691SAlexander Motin 			hda_command(ch->devinfo->dev,
19186fa8e691SAlexander Motin 			    HDA_CMD_SET_STRIPE_CONTROL(0, w->nid, ch->stripectl));
19196fa8e691SAlexander Motin 		}
192088addcbeSAlexander Motin 		cchn = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap);
192188addcbeSAlexander Motin 		if (cchn > 1 && chn < totalchn) {
192288addcbeSAlexander Motin 			cchn = min(cchn, totalchn - chn - 1);
19237c6b05d2SAlexander Motin 			hda_command(ch->devinfo->dev,
192488addcbeSAlexander Motin 			    HDA_CMD_SET_CONV_CHAN_COUNT(0, ch->io[i], cchn));
192588addcbeSAlexander Motin 		}
192688addcbeSAlexander Motin 		HDA_BOOTHVERBOSE(
192788addcbeSAlexander Motin 			device_printf(ch->pdevinfo->dev,
192888addcbeSAlexander Motin 			    "PCMDIR_%s: Stream setup nid=%d: "
192988addcbeSAlexander Motin 			    "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x, "
19306fa8e691SAlexander Motin 			    "chan_count=0x%02x, stripe=%d\n",
193188addcbeSAlexander Motin 			    (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC",
19326fa8e691SAlexander Motin 			    ch->io[i], fmt, dfmt, c, cchn, ch->stripectl);
193388addcbeSAlexander Motin 		);
193488addcbeSAlexander Motin 		for (j = 0; j < 16; j++) {
193588addcbeSAlexander Motin 			if (as->dacs[ch->asindex][j] != ch->io[i])
193688addcbeSAlexander Motin 				continue;
193788addcbeSAlexander Motin 			nid = as->pins[j];
193888addcbeSAlexander Motin 			wp = hdaa_widget_get(ch->devinfo, nid);
193988addcbeSAlexander Motin 			if (wp == NULL)
194088addcbeSAlexander Motin 				continue;
194188addcbeSAlexander Motin 			if (!HDA_PARAM_PIN_CAP_DP(wp->wclass.pin.cap) &&
194288addcbeSAlexander Motin 			    !HDA_PARAM_PIN_CAP_HDMI(wp->wclass.pin.cap))
194388addcbeSAlexander Motin 				continue;
194488addcbeSAlexander Motin 
194588addcbeSAlexander Motin 			/* Set channel mapping. */
194688addcbeSAlexander Motin 			for (k = 0; k < 8; k++) {
19477c6b05d2SAlexander Motin 				hda_command(ch->devinfo->dev,
194888addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_CHAN_SLOT(0, nid,
194988addcbeSAlexander Motin 				    (((hdmich[totalextchn == 0 ? 0 : 1][totalchn - 1]
195088addcbeSAlexander Motin 				     >> (k * 4)) & 0xf) << 4) | k));
195188addcbeSAlexander Motin 			}
195288addcbeSAlexander Motin 
195353b95d17SAlexander Motin 			/*
195453b95d17SAlexander Motin 			 * Enable High Bit Rate (HBR) Encoded Packet Type
195553b95d17SAlexander Motin 			 * (EPT), if supported and needed (8ch data).
195653b95d17SAlexander Motin 			 */
195753b95d17SAlexander Motin 			if (HDA_PARAM_PIN_CAP_HDMI(wp->wclass.pin.cap) &&
195853b95d17SAlexander Motin 			    HDA_PARAM_PIN_CAP_HBR(wp->wclass.pin.cap)) {
195953b95d17SAlexander Motin 				wp->wclass.pin.ctrl &=
196053b95d17SAlexander Motin 				    ~HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK;
1961f4cb8238SAlexander Motin 				if ((ch->fmt & AFMT_AC3) && (cchn == 7))
196253b95d17SAlexander Motin 					wp->wclass.pin.ctrl |= 0x03;
196353b95d17SAlexander Motin 				hda_command(ch->devinfo->dev,
196453b95d17SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL(0, nid,
196553b95d17SAlexander Motin 				    wp->wclass.pin.ctrl));
196653b95d17SAlexander Motin 			}
196753b95d17SAlexander Motin 
196888addcbeSAlexander Motin 			/* Stop audio infoframe transmission. */
19697c6b05d2SAlexander Motin 			hda_command(ch->devinfo->dev,
197088addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
197188addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
197288addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_XMIT(0, nid, 0x00));
197388addcbeSAlexander Motin 
197488addcbeSAlexander Motin 			/* Clear audio infoframe buffer. */
197588addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
197688addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
197788addcbeSAlexander Motin 			for (k = 0; k < 32; k++)
197888addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
197988addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00));
198088addcbeSAlexander Motin 
198188addcbeSAlexander Motin 			/* Write HDMI/DisplayPort audio infoframe. */
198288addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
198388addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
198488addcbeSAlexander Motin 			if (w->eld != NULL && w->eld_len >= 6 &&
198588addcbeSAlexander Motin 			    ((w->eld[5] >> 2) & 0x3) == 1) { /* DisplayPort */
198688addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
198788addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x84));
198888addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
198988addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x1b));
199088addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
199188addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x44));
199288addcbeSAlexander Motin 			} else {	/* HDMI */
199388addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
199488addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x84));
199588addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
199688addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x01));
199788addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
199888addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x0a));
199988addcbeSAlexander Motin 				csum = 0;
200088addcbeSAlexander Motin 				csum -= 0x84 + 0x01 + 0x0a + (totalchn - 1) +
200188addcbeSAlexander Motin 				    hdmica[totalextchn == 0 ? 0 : 1][totalchn - 1];
200288addcbeSAlexander Motin 				hda_command(ch->devinfo->dev,
200388addcbeSAlexander Motin 				    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, csum));
200488addcbeSAlexander Motin 			}
200588addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
200688addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, totalchn - 1));
200788addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
200888addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00));
200988addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
201088addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00));
201188addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
201288addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_DATA(0, nid,
201388addcbeSAlexander Motin 			    hdmica[totalextchn == 0 ? 0 : 1][totalchn - 1]));
201488addcbeSAlexander Motin 
201588addcbeSAlexander Motin 			/* Start audio infoframe transmission. */
201688addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
201788addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00));
201888addcbeSAlexander Motin 			hda_command(ch->devinfo->dev,
201988addcbeSAlexander Motin 			    HDA_CMD_SET_HDMI_DIP_XMIT(0, nid, 0xc0));
202088addcbeSAlexander Motin 		}
202188addcbeSAlexander Motin 		chn += cchn + 1;
20227c6b05d2SAlexander Motin 	}
20237c6b05d2SAlexander Motin }
20247c6b05d2SAlexander Motin 
20257c6b05d2SAlexander Motin /*
20267c6b05d2SAlexander Motin  * Greatest Common Divisor.
20277c6b05d2SAlexander Motin  */
20287c6b05d2SAlexander Motin static unsigned
gcd(unsigned a,unsigned b)20297c6b05d2SAlexander Motin gcd(unsigned a, unsigned b)
20307c6b05d2SAlexander Motin {
20317c6b05d2SAlexander Motin 	u_int c;
20327c6b05d2SAlexander Motin 
20337c6b05d2SAlexander Motin 	while (b != 0) {
20347c6b05d2SAlexander Motin 		c = a;
20357c6b05d2SAlexander Motin 		a = b;
20367c6b05d2SAlexander Motin 		b = (c % b);
20377c6b05d2SAlexander Motin 	}
20387c6b05d2SAlexander Motin 	return (a);
20397c6b05d2SAlexander Motin }
20407c6b05d2SAlexander Motin 
20417c6b05d2SAlexander Motin /*
20427c6b05d2SAlexander Motin  * Least Common Multiple.
20437c6b05d2SAlexander Motin  */
20447c6b05d2SAlexander Motin static unsigned
lcm(unsigned a,unsigned b)20457c6b05d2SAlexander Motin lcm(unsigned a, unsigned b)
20467c6b05d2SAlexander Motin {
20477c6b05d2SAlexander Motin 
20487c6b05d2SAlexander Motin 	return ((a * b) / gcd(a, b));
20497c6b05d2SAlexander Motin }
20507c6b05d2SAlexander Motin 
20517c6b05d2SAlexander Motin static int
hdaa_channel_setfragments(kobj_t obj,void * data,uint32_t blksz,uint32_t blkcnt)20527c6b05d2SAlexander Motin hdaa_channel_setfragments(kobj_t obj, void *data,
20537c6b05d2SAlexander Motin 					uint32_t blksz, uint32_t blkcnt)
20547c6b05d2SAlexander Motin {
20557c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
20567c6b05d2SAlexander Motin 
20577c6b05d2SAlexander Motin 	blksz -= blksz % lcm(HDA_DMA_ALIGNMENT, sndbuf_getalign(ch->b));
20587c6b05d2SAlexander Motin 
20597c6b05d2SAlexander Motin 	if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN))
20607c6b05d2SAlexander Motin 		blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN;
20617c6b05d2SAlexander Motin 	if (blksz < HDA_BLK_MIN)
20627c6b05d2SAlexander Motin 		blksz = HDA_BLK_MIN;
20637c6b05d2SAlexander Motin 	if (blkcnt > HDA_BDL_MAX)
20647c6b05d2SAlexander Motin 		blkcnt = HDA_BDL_MAX;
20657c6b05d2SAlexander Motin 	if (blkcnt < HDA_BDL_MIN)
20667c6b05d2SAlexander Motin 		blkcnt = HDA_BDL_MIN;
20677c6b05d2SAlexander Motin 
20687c6b05d2SAlexander Motin 	while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) {
20697c6b05d2SAlexander Motin 		if ((blkcnt >> 1) >= HDA_BDL_MIN)
20707c6b05d2SAlexander Motin 			blkcnt >>= 1;
20717c6b05d2SAlexander Motin 		else if ((blksz >> 1) >= HDA_BLK_MIN)
20727c6b05d2SAlexander Motin 			blksz >>= 1;
20737c6b05d2SAlexander Motin 		else
20747c6b05d2SAlexander Motin 			break;
20757c6b05d2SAlexander Motin 	}
20767c6b05d2SAlexander Motin 
20777c6b05d2SAlexander Motin 	if ((sndbuf_getblksz(ch->b) != blksz ||
20787c6b05d2SAlexander Motin 	    sndbuf_getblkcnt(ch->b) != blkcnt) &&
20797c6b05d2SAlexander Motin 	    sndbuf_resize(ch->b, blkcnt, blksz) != 0)
20807c6b05d2SAlexander Motin 		device_printf(ch->devinfo->dev, "%s: failed blksz=%u blkcnt=%u\n",
20817c6b05d2SAlexander Motin 		    __func__, blksz, blkcnt);
20827c6b05d2SAlexander Motin 
20837c6b05d2SAlexander Motin 	ch->blksz = sndbuf_getblksz(ch->b);
20847c6b05d2SAlexander Motin 	ch->blkcnt = sndbuf_getblkcnt(ch->b);
20857c6b05d2SAlexander Motin 
20867c6b05d2SAlexander Motin 	return (0);
20877c6b05d2SAlexander Motin }
20887c6b05d2SAlexander Motin 
20897c6b05d2SAlexander Motin static uint32_t
hdaa_channel_setblocksize(kobj_t obj,void * data,uint32_t blksz)20907c6b05d2SAlexander Motin hdaa_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
20917c6b05d2SAlexander Motin {
20927c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
20937c6b05d2SAlexander Motin 
20947c6b05d2SAlexander Motin 	hdaa_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt);
20957c6b05d2SAlexander Motin 
20967c6b05d2SAlexander Motin 	return (ch->blksz);
20977c6b05d2SAlexander Motin }
20987c6b05d2SAlexander Motin 
20997c6b05d2SAlexander Motin static void
hdaa_channel_stop(struct hdaa_chan * ch)21007c6b05d2SAlexander Motin hdaa_channel_stop(struct hdaa_chan *ch)
21017c6b05d2SAlexander Motin {
21027c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = ch->devinfo;
21037c6b05d2SAlexander Motin 	struct hdaa_widget *w;
21047c6b05d2SAlexander Motin 	int i;
21057c6b05d2SAlexander Motin 
210620332cc2SAlexander Motin 	if ((ch->flags & HDAA_CHN_RUNNING) == 0)
210720332cc2SAlexander Motin 		return;
21087c6b05d2SAlexander Motin 	ch->flags &= ~HDAA_CHN_RUNNING;
21097c6b05d2SAlexander Motin 	HDAC_STREAM_STOP(device_get_parent(devinfo->dev), devinfo->dev,
21107c6b05d2SAlexander Motin 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21117c6b05d2SAlexander Motin 	for (i = 0; ch->io[i] != -1; i++) {
21127c6b05d2SAlexander Motin 		w = hdaa_widget_get(ch->devinfo, ch->io[i]);
21137c6b05d2SAlexander Motin 		if (w == NULL)
21147c6b05d2SAlexander Motin 			continue;
21157c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
21167c6b05d2SAlexander Motin 			hda_command(devinfo->dev,
21177c6b05d2SAlexander Motin 			    HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], 0));
21187c6b05d2SAlexander Motin 		}
21197c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
21207c6b05d2SAlexander Motin 		    HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i],
21217c6b05d2SAlexander Motin 		    0));
21227c6b05d2SAlexander Motin 	}
21237c6b05d2SAlexander Motin 	HDAC_STREAM_FREE(device_get_parent(devinfo->dev), devinfo->dev,
21247c6b05d2SAlexander Motin 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21257c6b05d2SAlexander Motin }
21267c6b05d2SAlexander Motin 
21277c6b05d2SAlexander Motin static int
hdaa_channel_start(struct hdaa_chan * ch)21287c6b05d2SAlexander Motin hdaa_channel_start(struct hdaa_chan *ch)
21297c6b05d2SAlexander Motin {
21307c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = ch->devinfo;
21316fa8e691SAlexander Motin 	uint32_t fmt;
21327c6b05d2SAlexander Motin 
21336fa8e691SAlexander Motin 	fmt = hdaa_stream_format(ch);
21344642c8c5SMichal Meloun 	ch->stripectl = fls(ch->stripecap & hdaa_allowed_stripes(fmt) &
21354642c8c5SMichal Meloun 	    hda_get_stripes_mask(devinfo->dev)) - 1;
21367c6b05d2SAlexander Motin 	ch->sid = HDAC_STREAM_ALLOC(device_get_parent(devinfo->dev), devinfo->dev,
21376fa8e691SAlexander Motin 	    ch->dir == PCMDIR_PLAY ? 1 : 0, fmt, ch->stripectl, &ch->dmapos);
21387c6b05d2SAlexander Motin 	if (ch->sid <= 0)
21397c6b05d2SAlexander Motin 		return (EBUSY);
21407c6b05d2SAlexander Motin 	hdaa_audio_setup(ch);
21417c6b05d2SAlexander Motin 	HDAC_STREAM_RESET(device_get_parent(devinfo->dev), devinfo->dev,
21427c6b05d2SAlexander Motin 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21437c6b05d2SAlexander Motin 	HDAC_STREAM_START(device_get_parent(devinfo->dev), devinfo->dev,
21447c6b05d2SAlexander Motin 	    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid,
21457c6b05d2SAlexander Motin 	    sndbuf_getbufaddr(ch->b), ch->blksz, ch->blkcnt);
21467c6b05d2SAlexander Motin 	ch->flags |= HDAA_CHN_RUNNING;
21477c6b05d2SAlexander Motin 	return (0);
21487c6b05d2SAlexander Motin }
21497c6b05d2SAlexander Motin 
21507c6b05d2SAlexander Motin static int
hdaa_channel_trigger(kobj_t obj,void * data,int go)21517c6b05d2SAlexander Motin hdaa_channel_trigger(kobj_t obj, void *data, int go)
21527c6b05d2SAlexander Motin {
21537c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
21547c6b05d2SAlexander Motin 	int error = 0;
21557c6b05d2SAlexander Motin 
21567c6b05d2SAlexander Motin 	if (!PCMTRIG_COMMON(go))
21577c6b05d2SAlexander Motin 		return (0);
21587c6b05d2SAlexander Motin 
21597c6b05d2SAlexander Motin 	hdaa_lock(ch->devinfo);
21607c6b05d2SAlexander Motin 	switch (go) {
21617c6b05d2SAlexander Motin 	case PCMTRIG_START:
21627c6b05d2SAlexander Motin 		error = hdaa_channel_start(ch);
21637c6b05d2SAlexander Motin 		break;
21647c6b05d2SAlexander Motin 	case PCMTRIG_STOP:
21657c6b05d2SAlexander Motin 	case PCMTRIG_ABORT:
21667c6b05d2SAlexander Motin 		hdaa_channel_stop(ch);
21677c6b05d2SAlexander Motin 		break;
21687c6b05d2SAlexander Motin 	default:
21697c6b05d2SAlexander Motin 		break;
21707c6b05d2SAlexander Motin 	}
21717c6b05d2SAlexander Motin 	hdaa_unlock(ch->devinfo);
21727c6b05d2SAlexander Motin 
21737c6b05d2SAlexander Motin 	return (error);
21747c6b05d2SAlexander Motin }
21757c6b05d2SAlexander Motin 
21767c6b05d2SAlexander Motin static uint32_t
hdaa_channel_getptr(kobj_t obj,void * data)21777c6b05d2SAlexander Motin hdaa_channel_getptr(kobj_t obj, void *data)
21787c6b05d2SAlexander Motin {
21797c6b05d2SAlexander Motin 	struct hdaa_chan *ch = data;
21807c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = ch->devinfo;
21817c6b05d2SAlexander Motin 	uint32_t ptr;
21827c6b05d2SAlexander Motin 
21837c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
21847c6b05d2SAlexander Motin 	if (ch->dmapos != NULL) {
21857c6b05d2SAlexander Motin 		ptr = *(ch->dmapos);
21867c6b05d2SAlexander Motin 	} else {
21877c6b05d2SAlexander Motin 		ptr = HDAC_STREAM_GETPTR(
21887c6b05d2SAlexander Motin 		    device_get_parent(devinfo->dev), devinfo->dev,
21897c6b05d2SAlexander Motin 		    ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid);
21907c6b05d2SAlexander Motin 	}
21917c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
21927c6b05d2SAlexander Motin 
21937c6b05d2SAlexander Motin 	/*
2194a1581cd7SGordon Bergling 	 * Round to available space and force 128 bytes alignment.
21957c6b05d2SAlexander Motin 	 */
21967c6b05d2SAlexander Motin 	ptr %= ch->blksz * ch->blkcnt;
21977c6b05d2SAlexander Motin 	ptr &= HDA_BLK_ALIGN;
21987c6b05d2SAlexander Motin 
21997c6b05d2SAlexander Motin 	return (ptr);
22007c6b05d2SAlexander Motin }
22017c6b05d2SAlexander Motin 
22027c6b05d2SAlexander Motin static struct pcmchan_caps *
hdaa_channel_getcaps(kobj_t obj,void * data)22037c6b05d2SAlexander Motin hdaa_channel_getcaps(kobj_t obj, void *data)
22047c6b05d2SAlexander Motin {
22057c6b05d2SAlexander Motin 	return (&((struct hdaa_chan *)data)->caps);
22067c6b05d2SAlexander Motin }
22077c6b05d2SAlexander Motin 
22087c6b05d2SAlexander Motin static kobj_method_t hdaa_channel_methods[] = {
22097c6b05d2SAlexander Motin 	KOBJMETHOD(channel_init,		hdaa_channel_init),
22107c6b05d2SAlexander Motin 	KOBJMETHOD(channel_setformat,		hdaa_channel_setformat),
22117c6b05d2SAlexander Motin 	KOBJMETHOD(channel_setspeed,		hdaa_channel_setspeed),
22127c6b05d2SAlexander Motin 	KOBJMETHOD(channel_setblocksize,	hdaa_channel_setblocksize),
22137c6b05d2SAlexander Motin 	KOBJMETHOD(channel_setfragments,	hdaa_channel_setfragments),
22147c6b05d2SAlexander Motin 	KOBJMETHOD(channel_trigger,		hdaa_channel_trigger),
22157c6b05d2SAlexander Motin 	KOBJMETHOD(channel_getptr,		hdaa_channel_getptr),
22167c6b05d2SAlexander Motin 	KOBJMETHOD(channel_getcaps,		hdaa_channel_getcaps),
22177c6b05d2SAlexander Motin 	KOBJMETHOD_END
22187c6b05d2SAlexander Motin };
22197c6b05d2SAlexander Motin CHANNEL_DECLARE(hdaa_channel);
22207c6b05d2SAlexander Motin 
22217c6b05d2SAlexander Motin static int
hdaa_audio_ctl_ossmixer_init(struct snd_mixer * m)22227c6b05d2SAlexander Motin hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m)
22237c6b05d2SAlexander Motin {
22247c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
22257c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
22267c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
22277c6b05d2SAlexander Motin 	uint32_t mask, recmask;
22283d741b14SAlexander Motin 	int i, j;
22297c6b05d2SAlexander Motin 
22307c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
22313d741b14SAlexander Motin 	pdevinfo->mixer = m;
22327c6b05d2SAlexander Motin 
22337c6b05d2SAlexander Motin 	/* Make sure that in case of soft volume it won't stay muted. */
22347c6b05d2SAlexander Motin 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
22357c6b05d2SAlexander Motin 		pdevinfo->left[i] = 100;
22367c6b05d2SAlexander Motin 		pdevinfo->right[i] = 100;
22377c6b05d2SAlexander Motin 	}
22387c6b05d2SAlexander Motin 
22393d741b14SAlexander Motin 	/* Declare volume controls assigned to this association. */
22403d741b14SAlexander Motin 	mask = pdevinfo->ossmask;
22417c6b05d2SAlexander Motin 	if (pdevinfo->playas >= 0) {
22423d741b14SAlexander Motin 		/* Declate EAPD as ogain control. */
22437c6b05d2SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
22447c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo, i);
22457c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
22467c6b05d2SAlexander Motin 				continue;
22477c6b05d2SAlexander Motin 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
22487c6b05d2SAlexander Motin 			    w->param.eapdbtl == HDA_INVALID ||
22497c6b05d2SAlexander Motin 			    w->bindas != pdevinfo->playas)
22507c6b05d2SAlexander Motin 				continue;
22517c6b05d2SAlexander Motin 			mask |= SOUND_MASK_OGAIN;
22527c6b05d2SAlexander Motin 			break;
22537c6b05d2SAlexander Motin 		}
22543d741b14SAlexander Motin 
22553d741b14SAlexander Motin 		/* Declare soft PCM volume if needed. */
22563d741b14SAlexander Motin 		if ((mask & SOUND_MASK_PCM) == 0 ||
22573d741b14SAlexander Motin 		    (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL) ||
22583d741b14SAlexander Motin 		    pdevinfo->minamp[SOUND_MIXER_PCM] ==
22593d741b14SAlexander Motin 		     pdevinfo->maxamp[SOUND_MIXER_PCM]) {
22603d741b14SAlexander Motin 			mask |= SOUND_MASK_PCM;
22613d741b14SAlexander Motin 			pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL);
22623d741b14SAlexander Motin 			HDA_BOOTHVERBOSE(
22633d741b14SAlexander Motin 				device_printf(pdevinfo->dev,
22643d741b14SAlexander Motin 				    "Forcing Soft PCM volume\n");
22653d741b14SAlexander Motin 			);
22667c6b05d2SAlexander Motin 		}
22677c6b05d2SAlexander Motin 
22683d741b14SAlexander Motin 		/* Declare master volume if needed. */
22693d741b14SAlexander Motin 		if ((mask & SOUND_MASK_VOLUME) == 0) {
22703d741b14SAlexander Motin 			mask |= SOUND_MASK_VOLUME;
22713d741b14SAlexander Motin 			mix_setparentchild(m, SOUND_MIXER_VOLUME,
22723d741b14SAlexander Motin 			    SOUND_MASK_PCM);
22733d741b14SAlexander Motin 			mix_setrealdev(m, SOUND_MIXER_VOLUME,
22743d741b14SAlexander Motin 			    SOUND_MIXER_NONE);
22753d741b14SAlexander Motin 			HDA_BOOTHVERBOSE(
22763d741b14SAlexander Motin 				device_printf(pdevinfo->dev,
22773d741b14SAlexander Motin 				    "Forcing master volume with PCM\n");
22783d741b14SAlexander Motin 			);
22793d741b14SAlexander Motin 		}
22807c6b05d2SAlexander Motin 	}
22817c6b05d2SAlexander Motin 
22827c6b05d2SAlexander Motin 	/* Declare record sources available to this association. */
22833d741b14SAlexander Motin 	recmask = 0;
22847c6b05d2SAlexander Motin 	if (pdevinfo->recas >= 0) {
22857c6b05d2SAlexander Motin 		for (i = 0; i < 16; i++) {
22867c6b05d2SAlexander Motin 			if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0)
22877c6b05d2SAlexander Motin 				continue;
22887c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo,
22897c6b05d2SAlexander Motin 			    devinfo->as[pdevinfo->recas].dacs[0][i]);
22907c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
22917c6b05d2SAlexander Motin 				continue;
22927c6b05d2SAlexander Motin 			for (j = 0; j < w->nconns; j++) {
22937c6b05d2SAlexander Motin 				if (w->connsenable[j] == 0)
22947c6b05d2SAlexander Motin 					continue;
22957c6b05d2SAlexander Motin 				cw = hdaa_widget_get(devinfo, w->conns[j]);
22967c6b05d2SAlexander Motin 				if (cw == NULL || cw->enable == 0)
22977c6b05d2SAlexander Motin 					continue;
22987c6b05d2SAlexander Motin 				if (cw->bindas != pdevinfo->recas &&
22997c6b05d2SAlexander Motin 				    cw->bindas != -2)
23007c6b05d2SAlexander Motin 					continue;
23017c6b05d2SAlexander Motin 				recmask |= cw->ossmask;
23027c6b05d2SAlexander Motin 			}
23037c6b05d2SAlexander Motin 		}
23047c6b05d2SAlexander Motin 	}
23057c6b05d2SAlexander Motin 
23067c6b05d2SAlexander Motin 	recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
23077c6b05d2SAlexander Motin 	mask &= (1 << SOUND_MIXER_NRDEVICES) - 1;
23083d741b14SAlexander Motin 	pdevinfo->ossmask = mask;
23097c6b05d2SAlexander Motin 
23107c6b05d2SAlexander Motin 	mix_setrecdevs(m, recmask);
23117c6b05d2SAlexander Motin 	mix_setdevs(m, mask);
23127c6b05d2SAlexander Motin 
23137c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
23147c6b05d2SAlexander Motin 
23157c6b05d2SAlexander Motin 	return (0);
23167c6b05d2SAlexander Motin }
23177c6b05d2SAlexander Motin 
23183d741b14SAlexander Motin /*
23193d741b14SAlexander Motin  * Update amplification per pdevinfo per ossdev, calculate summary coefficient
23203d741b14SAlexander Motin  * and write it to codec, update *left and *right to reflect remaining error.
23213d741b14SAlexander Motin  */
23223d741b14SAlexander Motin static void
hdaa_audio_ctl_dev_set(struct hdaa_audio_ctl * ctl,int ossdev,int mute,int * left,int * right)23233d741b14SAlexander Motin hdaa_audio_ctl_dev_set(struct hdaa_audio_ctl *ctl, int ossdev,
23243d741b14SAlexander Motin     int mute, int *left, int *right)
23253d741b14SAlexander Motin {
23263d741b14SAlexander Motin 	int i, zleft, zright, sleft, sright, smute, lval, rval;
23273d741b14SAlexander Motin 
23283d741b14SAlexander Motin 	ctl->devleft[ossdev] = *left;
23293d741b14SAlexander Motin 	ctl->devright[ossdev] = *right;
23303d741b14SAlexander Motin 	ctl->devmute[ossdev] = mute;
23313d741b14SAlexander Motin 	smute = sleft = sright = zleft = zright = 0;
23323d741b14SAlexander Motin 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
23333d741b14SAlexander Motin 		sleft += ctl->devleft[i];
23343d741b14SAlexander Motin 		sright += ctl->devright[i];
23353d741b14SAlexander Motin 		smute |= ctl->devmute[i];
23363d741b14SAlexander Motin 		if (i == ossdev)
23373d741b14SAlexander Motin 			continue;
23383d741b14SAlexander Motin 		zleft += ctl->devleft[i];
23393d741b14SAlexander Motin 		zright += ctl->devright[i];
23403d741b14SAlexander Motin 	}
23413d741b14SAlexander Motin 	lval = QDB2VAL(ctl, sleft);
23423d741b14SAlexander Motin 	rval = QDB2VAL(ctl, sright);
23433d741b14SAlexander Motin 	hdaa_audio_ctl_amp_set(ctl, smute, lval, rval);
23443d741b14SAlexander Motin 	*left -= VAL2QDB(ctl, lval) - VAL2QDB(ctl, QDB2VAL(ctl, zleft));
23453d741b14SAlexander Motin 	*right -= VAL2QDB(ctl, rval) - VAL2QDB(ctl, QDB2VAL(ctl, zright));
23463d741b14SAlexander Motin }
23473d741b14SAlexander Motin 
23483d741b14SAlexander Motin /*
23493d741b14SAlexander Motin  * Trace signal from source, setting volumes on the way.
23503d741b14SAlexander Motin  */
23513d741b14SAlexander Motin 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)23523d741b14SAlexander Motin hdaa_audio_ctl_source_volume(struct hdaa_pcm_devinfo *pdevinfo,
23533d741b14SAlexander Motin     int ossdev, nid_t nid, int index, int mute, int left, int right, int depth)
23543d741b14SAlexander Motin {
23553d741b14SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
23563d741b14SAlexander Motin 	struct hdaa_widget *w, *wc;
23573d741b14SAlexander Motin 	struct hdaa_audio_ctl *ctl;
23583d741b14SAlexander Motin 	int i, j, conns = 0;
23593d741b14SAlexander Motin 
23603d741b14SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
23613d741b14SAlexander Motin 		return;
23623d741b14SAlexander Motin 
23633d741b14SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
23643d741b14SAlexander Motin 	if (w == NULL || w->enable == 0)
23653d741b14SAlexander Motin 		return;
23663d741b14SAlexander Motin 
23673d741b14SAlexander Motin 	/* Count number of active inputs. */
23683d741b14SAlexander Motin 	if (depth > 0) {
23693d741b14SAlexander Motin 		for (j = 0; j < w->nconns; j++) {
23703d741b14SAlexander Motin 			if (!w->connsenable[j])
23713d741b14SAlexander Motin 				continue;
23723d741b14SAlexander Motin 			conns++;
23733d741b14SAlexander Motin 		}
23743d741b14SAlexander Motin 	}
23753d741b14SAlexander Motin 
23763d741b14SAlexander Motin 	/* If this is not a first step - use input mixer.
23773d741b14SAlexander Motin 	   Pins have common input ctl so care must be taken. */
23783d741b14SAlexander Motin 	if (depth > 0 && (conns == 1 ||
23793d741b14SAlexander Motin 	    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) {
23803d741b14SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN,
23813d741b14SAlexander Motin 		    index, 1);
23823d741b14SAlexander Motin 		if (ctl)
23833d741b14SAlexander Motin 			hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
23843d741b14SAlexander Motin 	}
23853d741b14SAlexander Motin 
23863d741b14SAlexander Motin 	/* If widget has own ossdev - not traverse it.
238728323addSBryan Drewery 	   It will be traversed on its own. */
23883d741b14SAlexander Motin 	if (w->ossdev >= 0 && depth > 0)
23893d741b14SAlexander Motin 		return;
23903d741b14SAlexander Motin 
23913d741b14SAlexander Motin 	/* We must not traverse pin */
23923d741b14SAlexander Motin 	if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT ||
23933d741b14SAlexander Motin 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
23943d741b14SAlexander Motin 	    depth > 0)
23953d741b14SAlexander Motin 		return;
23963d741b14SAlexander Motin 
23973d741b14SAlexander Motin 	/*
23983d741b14SAlexander Motin 	 * If signals mixed, we can't assign controls farther.
23993d741b14SAlexander Motin 	 * Ignore this on depth zero. Caller must knows why.
24003d741b14SAlexander Motin 	 */
24013d741b14SAlexander Motin 	if (conns > 1 &&
24023d741b14SAlexander Motin 	    (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER ||
24033d741b14SAlexander Motin 	     w->selconn != index))
24043d741b14SAlexander Motin 		return;
24053d741b14SAlexander Motin 
24063d741b14SAlexander Motin 	ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1);
24073d741b14SAlexander Motin 	if (ctl)
24083d741b14SAlexander Motin 		hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
24093d741b14SAlexander Motin 
24103d741b14SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
24113d741b14SAlexander Motin 		wc = hdaa_widget_get(devinfo, i);
24123d741b14SAlexander Motin 		if (wc == NULL || wc->enable == 0)
24133d741b14SAlexander Motin 			continue;
24143d741b14SAlexander Motin 		for (j = 0; j < wc->nconns; j++) {
24153d741b14SAlexander Motin 			if (wc->connsenable[j] && wc->conns[j] == nid) {
24163d741b14SAlexander Motin 				hdaa_audio_ctl_source_volume(pdevinfo, ossdev,
24173d741b14SAlexander Motin 				    wc->nid, j, mute, left, right, depth + 1);
24183d741b14SAlexander Motin 			}
24193d741b14SAlexander Motin 		}
24203d741b14SAlexander Motin 	}
24213d741b14SAlexander Motin 	return;
24223d741b14SAlexander Motin }
24233d741b14SAlexander Motin 
24243d741b14SAlexander Motin /*
24253d741b14SAlexander Motin  * Trace signal from destination, setting volumes on the way.
24263d741b14SAlexander Motin  */
24273d741b14SAlexander Motin 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)24283d741b14SAlexander Motin hdaa_audio_ctl_dest_volume(struct hdaa_pcm_devinfo *pdevinfo,
24293d741b14SAlexander Motin     int ossdev, nid_t nid, int index, int mute, int left, int right, int depth)
24303d741b14SAlexander Motin {
24313d741b14SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
24323d741b14SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
24333d741b14SAlexander Motin 	struct hdaa_widget *w, *wc;
24343d741b14SAlexander Motin 	struct hdaa_audio_ctl *ctl;
24353d741b14SAlexander Motin 	int i, j, consumers, cleft, cright;
24363d741b14SAlexander Motin 
24373d741b14SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
24383d741b14SAlexander Motin 		return;
24393d741b14SAlexander Motin 
24403d741b14SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
24413d741b14SAlexander Motin 	if (w == NULL || w->enable == 0)
24423d741b14SAlexander Motin 		return;
24433d741b14SAlexander Motin 
24443d741b14SAlexander Motin 	if (depth > 0) {
24453d741b14SAlexander Motin 		/* If this node produce output for several consumers,
24463d741b14SAlexander Motin 		   we can't touch it. */
24473d741b14SAlexander Motin 		consumers = 0;
24483d741b14SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
24493d741b14SAlexander Motin 			wc = hdaa_widget_get(devinfo, i);
24503d741b14SAlexander Motin 			if (wc == NULL || wc->enable == 0)
24513d741b14SAlexander Motin 				continue;
24523d741b14SAlexander Motin 			for (j = 0; j < wc->nconns; j++) {
24533d741b14SAlexander Motin 				if (wc->connsenable[j] && wc->conns[j] == nid)
24543d741b14SAlexander Motin 					consumers++;
24553d741b14SAlexander Motin 			}
24563d741b14SAlexander Motin 		}
24573d741b14SAlexander Motin 		/* The only exception is if real HP redirection is configured
24583d741b14SAlexander Motin 		   and this is a duplication point.
24593d741b14SAlexander Motin 		   XXX: Actually exception is not completely correct.
24603d741b14SAlexander Motin 		   XXX: Duplication point check is not perfect. */
24613d741b14SAlexander Motin 		if ((consumers == 2 && (w->bindas < 0 ||
24623d741b14SAlexander Motin 		    as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir ||
24633d741b14SAlexander Motin 		    (w->bindseqmask & (1 << 15)) == 0)) ||
24643d741b14SAlexander Motin 		    consumers > 2)
24653d741b14SAlexander Motin 			return;
24663d741b14SAlexander Motin 
24673d741b14SAlexander Motin 		/* Else use it's output mixer. */
24683d741b14SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
24693d741b14SAlexander Motin 		    HDAA_CTL_OUT, -1, 1);
24703d741b14SAlexander Motin 		if (ctl)
24713d741b14SAlexander Motin 			hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right);
24723d741b14SAlexander Motin 	}
24733d741b14SAlexander Motin 
24743d741b14SAlexander Motin 	/* We must not traverse pin */
24753d741b14SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
24763d741b14SAlexander Motin 	    depth > 0)
24773d741b14SAlexander Motin 		return;
24783d741b14SAlexander Motin 
24793d741b14SAlexander Motin 	for (i = 0; i < w->nconns; i++) {
24803d741b14SAlexander Motin 		if (w->connsenable[i] == 0)
24813d741b14SAlexander Motin 			continue;
24823d741b14SAlexander Motin 		if (index >= 0 && i != index)
24833d741b14SAlexander Motin 			continue;
24843d741b14SAlexander Motin 		cleft = left;
24853d741b14SAlexander Motin 		cright = right;
24863d741b14SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
24873d741b14SAlexander Motin 		    HDAA_CTL_IN, i, 1);
24883d741b14SAlexander Motin 		if (ctl)
24893d741b14SAlexander Motin 			hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &cleft, &cright);
24903d741b14SAlexander Motin 		hdaa_audio_ctl_dest_volume(pdevinfo, ossdev, w->conns[i], -1,
24913d741b14SAlexander Motin 		    mute, cleft, cright, depth + 1);
24923d741b14SAlexander Motin 	}
24933d741b14SAlexander Motin }
24943d741b14SAlexander Motin 
24953d741b14SAlexander Motin /*
24963d741b14SAlexander Motin  * Set volumes for the specified pdevinfo and ossdev.
24973d741b14SAlexander Motin  */
24983d741b14SAlexander Motin static void
hdaa_audio_ctl_dev_volume(struct hdaa_pcm_devinfo * pdevinfo,unsigned dev)24993d741b14SAlexander Motin hdaa_audio_ctl_dev_volume(struct hdaa_pcm_devinfo *pdevinfo, unsigned dev)
25003d741b14SAlexander Motin {
25013d741b14SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
25023d741b14SAlexander Motin 	struct hdaa_widget *w, *cw;
25033d741b14SAlexander Motin 	uint32_t mute;
25043d741b14SAlexander Motin 	int lvol, rvol;
25053d741b14SAlexander Motin 	int i, j;
25063d741b14SAlexander Motin 
25073d741b14SAlexander Motin 	mute = 0;
25083d741b14SAlexander Motin 	if (pdevinfo->left[dev] == 0) {
25093d741b14SAlexander Motin 		mute |= HDAA_AMP_MUTE_LEFT;
25103d741b14SAlexander Motin 		lvol = -4000;
25113d741b14SAlexander Motin 	} else
25123d741b14SAlexander Motin 		lvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) *
25133d741b14SAlexander Motin 		    pdevinfo->left[dev] + 50) / 100 + pdevinfo->minamp[dev];
25143d741b14SAlexander Motin 	if (pdevinfo->right[dev] == 0) {
25153d741b14SAlexander Motin 		mute |= HDAA_AMP_MUTE_RIGHT;
25163d741b14SAlexander Motin 		rvol = -4000;
25173d741b14SAlexander Motin 	} else
25183d741b14SAlexander Motin 		rvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) *
25193d741b14SAlexander Motin 		    pdevinfo->right[dev] + 50) / 100 + pdevinfo->minamp[dev];
25203d741b14SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
25213d741b14SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
25223d741b14SAlexander Motin 		if (w == NULL || w->enable == 0)
25233d741b14SAlexander Motin 			continue;
252452e9cf7dSAlexander Motin 		if (w->bindas < 0) {
252552e9cf7dSAlexander Motin 			if (pdevinfo->index != 0)
25263d741b14SAlexander Motin 				continue;
252752e9cf7dSAlexander Motin 		} else {
25283d741b14SAlexander Motin 			if (w->bindas != pdevinfo->playas &&
25293d741b14SAlexander Motin 			    w->bindas != pdevinfo->recas)
25303d741b14SAlexander Motin 				continue;
253152e9cf7dSAlexander Motin 		}
25323d741b14SAlexander Motin 		if (dev == SOUND_MIXER_RECLEV &&
25333d741b14SAlexander Motin 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
25343d741b14SAlexander Motin 			hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25353d741b14SAlexander Motin 			    w->nid, -1, mute, lvol, rvol, 0);
25363d741b14SAlexander Motin 			continue;
25373d741b14SAlexander Motin 		}
25383d741b14SAlexander Motin 		if (dev == SOUND_MIXER_VOLUME &&
25393d741b14SAlexander Motin 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
25403d741b14SAlexander Motin 		    devinfo->as[w->bindas].dir == HDAA_CTL_OUT) {
25413d741b14SAlexander Motin 			hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25423d741b14SAlexander Motin 			    w->nid, -1, mute, lvol, rvol, 0);
25433d741b14SAlexander Motin 			continue;
25443d741b14SAlexander Motin 		}
25453d741b14SAlexander Motin 		if (dev == SOUND_MIXER_IGAIN &&
25463d741b14SAlexander Motin 		    w->pflags & HDAA_ADC_MONITOR) {
25473d741b14SAlexander Motin 			for (j = 0; j < w->nconns; j++) {
25483d741b14SAlexander Motin 				if (!w->connsenable[j])
25493d741b14SAlexander Motin 				    continue;
25503d741b14SAlexander Motin 				cw = hdaa_widget_get(devinfo, w->conns[j]);
25513d741b14SAlexander Motin 				if (cw == NULL || cw->enable == 0)
25523d741b14SAlexander Motin 				    continue;
25533d741b14SAlexander Motin 				if (cw->bindas == -1)
25543d741b14SAlexander Motin 				    continue;
25553d741b14SAlexander Motin 				if (cw->bindas >= 0 &&
25563d741b14SAlexander Motin 				    devinfo->as[cw->bindas].dir != HDAA_CTL_IN)
25573d741b14SAlexander Motin 					continue;
25583d741b14SAlexander Motin 				hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25593d741b14SAlexander Motin 				    w->nid, j, mute, lvol, rvol, 0);
25603d741b14SAlexander Motin 			}
25613d741b14SAlexander Motin 			continue;
25623d741b14SAlexander Motin 		}
25633d741b14SAlexander Motin 		if (w->ossdev != dev)
25643d741b14SAlexander Motin 			continue;
25653d741b14SAlexander Motin 		hdaa_audio_ctl_source_volume(pdevinfo, dev,
25663d741b14SAlexander Motin 		    w->nid, -1, mute, lvol, rvol, 0);
25673d741b14SAlexander Motin 		if (dev == SOUND_MIXER_IMIX && (w->pflags & HDAA_IMIX_AS_DST))
25683d741b14SAlexander Motin 			hdaa_audio_ctl_dest_volume(pdevinfo, dev,
25693d741b14SAlexander Motin 			    w->nid, -1, mute, lvol, rvol, 0);
25703d741b14SAlexander Motin 	}
25713d741b14SAlexander Motin }
25723d741b14SAlexander Motin 
25733d741b14SAlexander Motin /*
25743d741b14SAlexander Motin  * OSS Mixer set method.
25753d741b14SAlexander Motin  */
25767c6b05d2SAlexander Motin static int
hdaa_audio_ctl_ossmixer_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)25777c6b05d2SAlexander Motin hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev,
25787c6b05d2SAlexander Motin 					unsigned left, unsigned right)
25797c6b05d2SAlexander Motin {
25807c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
25817c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
25827c6b05d2SAlexander Motin 	struct hdaa_widget *w;
25833d741b14SAlexander Motin 	int i;
25847c6b05d2SAlexander Motin 
25857c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
25863d741b14SAlexander Motin 
25877c6b05d2SAlexander Motin 	/* Save new values. */
25887c6b05d2SAlexander Motin 	pdevinfo->left[dev] = left;
25897c6b05d2SAlexander Motin 	pdevinfo->right[dev] = right;
25907c6b05d2SAlexander Motin 
25917c6b05d2SAlexander Motin 	/* 'ogain' is the special case implemented with EAPD. */
25927c6b05d2SAlexander Motin 	if (dev == SOUND_MIXER_OGAIN) {
25937c6b05d2SAlexander Motin 		uint32_t orig;
25947c6b05d2SAlexander Motin 		w = NULL;
25957c6b05d2SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
25967c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo, i);
25977c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
25987c6b05d2SAlexander Motin 				continue;
25997c6b05d2SAlexander Motin 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
26007c6b05d2SAlexander Motin 			    w->param.eapdbtl == HDA_INVALID)
26017c6b05d2SAlexander Motin 				continue;
26027c6b05d2SAlexander Motin 			break;
26037c6b05d2SAlexander Motin 		}
26047c6b05d2SAlexander Motin 		if (i >= devinfo->endnode) {
26057c6b05d2SAlexander Motin 			hdaa_unlock(devinfo);
26067c6b05d2SAlexander Motin 			return (-1);
26077c6b05d2SAlexander Motin 		}
26087c6b05d2SAlexander Motin 		orig = w->param.eapdbtl;
26097c6b05d2SAlexander Motin 		if (left == 0)
26107c6b05d2SAlexander Motin 			w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
26117c6b05d2SAlexander Motin 		else
26127c6b05d2SAlexander Motin 			w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
26137c6b05d2SAlexander Motin 		if (orig != w->param.eapdbtl) {
26147c6b05d2SAlexander Motin 			uint32_t val;
26157c6b05d2SAlexander Motin 
26167c6b05d2SAlexander Motin 			val = w->param.eapdbtl;
26177c6b05d2SAlexander Motin 			if (devinfo->quirks & HDAA_QUIRK_EAPDINV)
26187c6b05d2SAlexander Motin 				val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
26197c6b05d2SAlexander Motin 			hda_command(devinfo->dev,
26207c6b05d2SAlexander Motin 			    HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid, val));
26217c6b05d2SAlexander Motin 		}
26227c6b05d2SAlexander Motin 		hdaa_unlock(devinfo);
26237c6b05d2SAlexander Motin 		return (left | (left << 8));
26247c6b05d2SAlexander Motin 	}
26257c6b05d2SAlexander Motin 
26267c6b05d2SAlexander Motin 	/* Recalculate all controls related to this OSS device. */
26273d741b14SAlexander Motin 	hdaa_audio_ctl_dev_volume(pdevinfo, dev);
26287c6b05d2SAlexander Motin 
26297c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
26307c6b05d2SAlexander Motin 	return (left | (right << 8));
26317c6b05d2SAlexander Motin }
26327c6b05d2SAlexander Motin 
26337c6b05d2SAlexander Motin /*
26343d741b14SAlexander Motin  * Set mixer settings to our own default values:
26353d741b14SAlexander Motin  * +20dB for mics, -10dB for analog vol, mute for igain, 0dB for others.
26363d741b14SAlexander Motin  */
26373d741b14SAlexander Motin static void
hdaa_audio_ctl_set_defaults(struct hdaa_pcm_devinfo * pdevinfo)26383d741b14SAlexander Motin hdaa_audio_ctl_set_defaults(struct hdaa_pcm_devinfo *pdevinfo)
26393d741b14SAlexander Motin {
26403d741b14SAlexander Motin 	int amp, vol, dev;
26413d741b14SAlexander Motin 
26423d741b14SAlexander Motin 	for (dev = 0; dev < SOUND_MIXER_NRDEVICES; dev++) {
26433d741b14SAlexander Motin 		if ((pdevinfo->ossmask & (1 << dev)) == 0)
26443d741b14SAlexander Motin 			continue;
26453d741b14SAlexander Motin 
2646ac34f366SGordon Bergling 		/* If the value was overridden, leave it as is. */
26473d741b14SAlexander Motin 		if (resource_int_value(device_get_name(pdevinfo->dev),
26483d741b14SAlexander Motin 		    device_get_unit(pdevinfo->dev), ossnames[dev], &vol) == 0)
26493d741b14SAlexander Motin 			continue;
26503d741b14SAlexander Motin 
26513d741b14SAlexander Motin 		vol = -1;
26523d741b14SAlexander Motin 		if (dev == SOUND_MIXER_OGAIN)
26533d741b14SAlexander Motin 			vol = 100;
26543d741b14SAlexander Motin 		else if (dev == SOUND_MIXER_IGAIN)
26553d741b14SAlexander Motin 			vol = 0;
26563d741b14SAlexander Motin 		else if (dev == SOUND_MIXER_MIC ||
26573d741b14SAlexander Motin 		    dev == SOUND_MIXER_MONITOR)
26583d741b14SAlexander Motin 			amp = 20 * 4;	/* +20dB */
26593d741b14SAlexander Motin 		else if (dev == SOUND_MIXER_VOLUME && !pdevinfo->digital)
26603d741b14SAlexander Motin 			amp = -10 * 4;	/* -10dB */
26613d741b14SAlexander Motin 		else
26623d741b14SAlexander Motin 			amp = 0;
26633d741b14SAlexander Motin 		if (vol < 0 &&
26643d741b14SAlexander Motin 		    (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) <= 0) {
26653d741b14SAlexander Motin 			vol = 100;
26663d741b14SAlexander Motin 		} else if (vol < 0) {
26673d741b14SAlexander Motin 			vol = ((amp - pdevinfo->minamp[dev]) * 100 +
26683d741b14SAlexander Motin 			    (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) / 2) /
26693d741b14SAlexander Motin 			    (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]);
26703d741b14SAlexander Motin 			vol = imin(imax(vol, 1), 100);
26713d741b14SAlexander Motin 		}
26723d741b14SAlexander Motin 		mix_set(pdevinfo->mixer, dev, vol, vol);
26733d741b14SAlexander Motin 	}
26743d741b14SAlexander Motin }
26753d741b14SAlexander Motin 
26763d741b14SAlexander Motin /*
26773d741b14SAlexander Motin  * Recursively commutate specified record source.
26787c6b05d2SAlexander Motin  */
26797c6b05d2SAlexander Motin static uint32_t
hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo * pdevinfo,uint32_t src,nid_t nid,int depth)26807c6b05d2SAlexander Motin hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth)
26817c6b05d2SAlexander Motin {
26827c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
26837c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
26847c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
26857c6b05d2SAlexander Motin 	char buf[64];
26867c6b05d2SAlexander Motin 	int i, muted;
26877c6b05d2SAlexander Motin 	uint32_t res = 0;
26887c6b05d2SAlexander Motin 
26897c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
26907c6b05d2SAlexander Motin 		return (0);
26917c6b05d2SAlexander Motin 
26927c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
26937c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
26947c6b05d2SAlexander Motin 		return (0);
26957c6b05d2SAlexander Motin 
26967c6b05d2SAlexander Motin 	for (i = 0; i < w->nconns; i++) {
26977c6b05d2SAlexander Motin 		if (w->connsenable[i] == 0)
26987c6b05d2SAlexander Motin 			continue;
26997c6b05d2SAlexander Motin 		cw = hdaa_widget_get(devinfo, w->conns[i]);
27007c6b05d2SAlexander Motin 		if (cw == NULL || cw->enable == 0 || cw->bindas == -1)
27017c6b05d2SAlexander Motin 			continue;
27027c6b05d2SAlexander Motin 		/* Call recursively to trace signal to it's source if needed. */
27037c6b05d2SAlexander Motin 		if ((src & cw->ossmask) != 0) {
27047c6b05d2SAlexander Motin 			if (cw->ossdev < 0) {
27057c6b05d2SAlexander Motin 				res |= hdaa_audio_ctl_recsel_comm(pdevinfo, src,
27067c6b05d2SAlexander Motin 				    w->conns[i], depth + 1);
27077c6b05d2SAlexander Motin 			} else {
27087c6b05d2SAlexander Motin 				res |= cw->ossmask;
27097c6b05d2SAlexander Motin 			}
27107c6b05d2SAlexander Motin 		}
27117c6b05d2SAlexander Motin 		/* We have two special cases: mixers and others (selectors). */
27127c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) {
27137c6b05d2SAlexander Motin 			ctl = hdaa_audio_ctl_amp_get(devinfo,
27147c6b05d2SAlexander Motin 			    w->nid, HDAA_CTL_IN, i, 1);
27157c6b05d2SAlexander Motin 			if (ctl == NULL)
27167c6b05d2SAlexander Motin 				continue;
27177c6b05d2SAlexander Motin 			/* If we have input control on this node mute them
27187c6b05d2SAlexander Motin 			 * according to requested sources. */
27197c6b05d2SAlexander Motin 			muted = (src & cw->ossmask) ? 0 : 1;
27207c6b05d2SAlexander Motin 			if (muted != ctl->forcemute) {
27217c6b05d2SAlexander Motin 				ctl->forcemute = muted;
27227c6b05d2SAlexander Motin 				hdaa_audio_ctl_amp_set(ctl,
27237c6b05d2SAlexander Motin 				    HDAA_AMP_MUTE_DEFAULT,
27247c6b05d2SAlexander Motin 				    HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT);
27257c6b05d2SAlexander Motin 			}
27267c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
27277c6b05d2SAlexander Motin 				device_printf(pdevinfo->dev,
27287c6b05d2SAlexander Motin 				    "Recsel (%s): nid %d source %d %s\n",
27297c6b05d2SAlexander Motin 				    hdaa_audio_ctl_ossmixer_mask2allname(
27307c6b05d2SAlexander Motin 				    src, buf, sizeof(buf)),
27317c6b05d2SAlexander Motin 				    nid, i, muted?"mute":"unmute");
27327c6b05d2SAlexander Motin 			);
27337c6b05d2SAlexander Motin 		} else {
27347c6b05d2SAlexander Motin 			if (w->nconns == 1)
27357c6b05d2SAlexander Motin 				break;
27367c6b05d2SAlexander Motin 			if ((src & cw->ossmask) == 0)
27377c6b05d2SAlexander Motin 				continue;
27387c6b05d2SAlexander Motin 			/* If we found requested source - select it and exit. */
27397c6b05d2SAlexander Motin 			hdaa_widget_connection_select(w, i);
27407c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
27417c6b05d2SAlexander Motin 				device_printf(pdevinfo->dev,
27427c6b05d2SAlexander Motin 				    "Recsel (%s): nid %d source %d select\n",
27437c6b05d2SAlexander Motin 				    hdaa_audio_ctl_ossmixer_mask2allname(
27447c6b05d2SAlexander Motin 				    src, buf, sizeof(buf)),
27457c6b05d2SAlexander Motin 				    nid, i);
27467c6b05d2SAlexander Motin 			);
27477c6b05d2SAlexander Motin 			break;
27487c6b05d2SAlexander Motin 		}
27497c6b05d2SAlexander Motin 	}
27507c6b05d2SAlexander Motin 	return (res);
27517c6b05d2SAlexander Motin }
27527c6b05d2SAlexander Motin 
27537c6b05d2SAlexander Motin static uint32_t
hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer * m,uint32_t src)27547c6b05d2SAlexander Motin hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src)
27557c6b05d2SAlexander Motin {
27567c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m);
27577c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
27587c6b05d2SAlexander Motin 	struct hdaa_widget *w;
27597c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
27603d741b14SAlexander Motin 	struct hdaa_audio_ctl *ctl;
27617c6b05d2SAlexander Motin 	struct hdaa_chan *ch;
27627c6b05d2SAlexander Motin 	int i, j;
27637c6b05d2SAlexander Motin 	uint32_t ret = 0xffffffff;
27647c6b05d2SAlexander Motin 
27657c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
27667c6b05d2SAlexander Motin 	if (pdevinfo->recas < 0) {
27677c6b05d2SAlexander Motin 		hdaa_unlock(devinfo);
27687c6b05d2SAlexander Motin 		return (0);
27697c6b05d2SAlexander Motin 	}
27707c6b05d2SAlexander Motin 	as = &devinfo->as[pdevinfo->recas];
27717c6b05d2SAlexander Motin 
27727c6b05d2SAlexander Motin 	/* For non-mixed associations we always recording everything. */
27737c6b05d2SAlexander Motin 	if (!as->mixed) {
27747c6b05d2SAlexander Motin 		hdaa_unlock(devinfo);
27757c6b05d2SAlexander Motin 		return (mix_getrecdevs(m));
27767c6b05d2SAlexander Motin 	}
27777c6b05d2SAlexander Motin 
27787c6b05d2SAlexander Motin 	/* Commutate requested recsrc for each ADC. */
27797c6b05d2SAlexander Motin 	for (j = 0; j < as->num_chans; j++) {
27807c6b05d2SAlexander Motin 		ch = &devinfo->chans[as->chans[j]];
27817c6b05d2SAlexander Motin 		for (i = 0; ch->io[i] >= 0; i++) {
27827c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo, ch->io[i]);
27837c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
27847c6b05d2SAlexander Motin 				continue;
27857c6b05d2SAlexander Motin 			ret &= hdaa_audio_ctl_recsel_comm(pdevinfo, src,
27867c6b05d2SAlexander Motin 			    ch->io[i], 0);
27877c6b05d2SAlexander Motin 		}
27887c6b05d2SAlexander Motin 	}
27893d741b14SAlexander Motin 	if (ret == 0xffffffff)
27903d741b14SAlexander Motin 		ret = 0;
27917c6b05d2SAlexander Motin 
27923d741b14SAlexander Motin 	/*
27933d741b14SAlexander Motin 	 * Some controls could be shared. Reset volumes for controls
27943d741b14SAlexander Motin 	 * related to previously chosen devices, as they may no longer
27953d741b14SAlexander Motin 	 * affect the signal.
27963d741b14SAlexander Motin 	 */
27973d741b14SAlexander Motin 	i = 0;
27983d741b14SAlexander Motin 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
27993d741b14SAlexander Motin 		if (ctl->enable == 0 ||
28003d741b14SAlexander Motin 		    !(ctl->ossmask & pdevinfo->recsrc))
28013d741b14SAlexander Motin 			continue;
28023d741b14SAlexander Motin 		if (!((pdevinfo->playas >= 0 &&
28033d741b14SAlexander Motin 		    ctl->widget->bindas == pdevinfo->playas) ||
28043d741b14SAlexander Motin 		    (pdevinfo->recas >= 0 &&
28053d741b14SAlexander Motin 		    ctl->widget->bindas == pdevinfo->recas) ||
28063d741b14SAlexander Motin 		    (pdevinfo->index == 0 &&
28073d741b14SAlexander Motin 		    ctl->widget->bindas == -2)))
28083d741b14SAlexander Motin 			continue;
28093d741b14SAlexander Motin 		for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
28103d741b14SAlexander Motin 			if (pdevinfo->recsrc & (1 << j)) {
28113d741b14SAlexander Motin 				ctl->devleft[j] = 0;
28123d741b14SAlexander Motin 				ctl->devright[j] = 0;
28133d741b14SAlexander Motin 				ctl->devmute[j] = 0;
28143d741b14SAlexander Motin 			}
28153d741b14SAlexander Motin 		}
28163d741b14SAlexander Motin 	}
28173d741b14SAlexander Motin 
28183d741b14SAlexander Motin 	/*
28193d741b14SAlexander Motin 	 * Some controls could be shared. Set volumes for controls
28203d741b14SAlexander Motin 	 * related to devices selected both previously and now.
28213d741b14SAlexander Motin 	 */
28223d741b14SAlexander Motin 	for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
28233d741b14SAlexander Motin 		if ((ret | pdevinfo->recsrc) & (1 << j))
28243d741b14SAlexander Motin 			hdaa_audio_ctl_dev_volume(pdevinfo, j);
28253d741b14SAlexander Motin 	}
28263d741b14SAlexander Motin 
28273d741b14SAlexander Motin 	pdevinfo->recsrc = ret;
28287c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
28293d741b14SAlexander Motin 	return (ret);
28307c6b05d2SAlexander Motin }
28317c6b05d2SAlexander Motin 
28327c6b05d2SAlexander Motin static kobj_method_t hdaa_audio_ctl_ossmixer_methods[] = {
28337c6b05d2SAlexander Motin 	KOBJMETHOD(mixer_init,		hdaa_audio_ctl_ossmixer_init),
28347c6b05d2SAlexander Motin 	KOBJMETHOD(mixer_set,		hdaa_audio_ctl_ossmixer_set),
28357c6b05d2SAlexander Motin 	KOBJMETHOD(mixer_setrecsrc,	hdaa_audio_ctl_ossmixer_setrecsrc),
28367c6b05d2SAlexander Motin 	KOBJMETHOD_END
28377c6b05d2SAlexander Motin };
28387c6b05d2SAlexander Motin MIXER_DECLARE(hdaa_audio_ctl_ossmixer);
28397c6b05d2SAlexander Motin 
28407c6b05d2SAlexander Motin static void
hdaa_dump_gpi(struct hdaa_devinfo * devinfo)28417c6b05d2SAlexander Motin hdaa_dump_gpi(struct hdaa_devinfo *devinfo)
28427c6b05d2SAlexander Motin {
28437c6b05d2SAlexander Motin 	device_t dev = devinfo->dev;
28447c6b05d2SAlexander Motin 	int i;
28457c6b05d2SAlexander Motin 	uint32_t data, wake, unsol, sticky;
28467c6b05d2SAlexander Motin 
28477c6b05d2SAlexander Motin 	if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap) > 0) {
28487c6b05d2SAlexander Motin 		data = hda_command(dev,
28497c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPI_DATA(0, devinfo->nid));
28507c6b05d2SAlexander Motin 		wake = hda_command(dev,
28517c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(0, devinfo->nid));
28527c6b05d2SAlexander Motin 		unsol = hda_command(dev,
28537c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(0, devinfo->nid));
28547c6b05d2SAlexander Motin 		sticky = hda_command(dev,
28557c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPI_STICKY_MASK(0, devinfo->nid));
28567c6b05d2SAlexander Motin 		for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap); i++) {
28577c6b05d2SAlexander Motin 			device_printf(dev, " GPI%d:%s%s%s state=%d", i,
28587c6b05d2SAlexander Motin 				    (sticky & (1 << i)) ? " sticky" : "",
28597c6b05d2SAlexander Motin 				    (unsol & (1 << i)) ? " unsol" : "",
28607c6b05d2SAlexander Motin 				    (wake & (1 << i)) ? " wake" : "",
28617c6b05d2SAlexander Motin 				    (data >> i) & 1);
28627c6b05d2SAlexander Motin 		}
28637c6b05d2SAlexander Motin 	}
28647c6b05d2SAlexander Motin }
28657c6b05d2SAlexander Motin 
28667c6b05d2SAlexander Motin static void
hdaa_dump_gpio(struct hdaa_devinfo * devinfo)28677c6b05d2SAlexander Motin hdaa_dump_gpio(struct hdaa_devinfo *devinfo)
28687c6b05d2SAlexander Motin {
28697c6b05d2SAlexander Motin 	device_t dev = devinfo->dev;
28707c6b05d2SAlexander Motin 	int i;
28717c6b05d2SAlexander Motin 	uint32_t data, dir, enable, wake, unsol, sticky;
28727c6b05d2SAlexander Motin 
28737c6b05d2SAlexander Motin 	if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap) > 0) {
28747c6b05d2SAlexander Motin 		data = hda_command(dev,
28757c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_DATA(0, devinfo->nid));
28767c6b05d2SAlexander Motin 		enable = hda_command(dev,
28777c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid));
28787c6b05d2SAlexander Motin 		dir = hda_command(dev,
28797c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid));
28807c6b05d2SAlexander Motin 		wake = hda_command(dev,
28817c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(0, devinfo->nid));
28827c6b05d2SAlexander Motin 		unsol = hda_command(dev,
28837c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(0, devinfo->nid));
28847c6b05d2SAlexander Motin 		sticky = hda_command(dev,
28857c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_STICKY_MASK(0, devinfo->nid));
28867c6b05d2SAlexander Motin 		for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); i++) {
28877c6b05d2SAlexander Motin 			device_printf(dev, " GPIO%d: ", i);
28887c6b05d2SAlexander Motin 			if ((enable & (1 << i)) == 0) {
28897c6b05d2SAlexander Motin 				printf("disabled\n");
28907c6b05d2SAlexander Motin 				continue;
28917c6b05d2SAlexander Motin 			}
28927c6b05d2SAlexander Motin 			if ((dir & (1 << i)) == 0) {
28937c6b05d2SAlexander Motin 				printf("input%s%s%s",
28947c6b05d2SAlexander Motin 				    (sticky & (1 << i)) ? " sticky" : "",
28957c6b05d2SAlexander Motin 				    (unsol & (1 << i)) ? " unsol" : "",
28967c6b05d2SAlexander Motin 				    (wake & (1 << i)) ? " wake" : "");
28977c6b05d2SAlexander Motin 			} else
28987c6b05d2SAlexander Motin 				printf("output");
28997c6b05d2SAlexander Motin 			printf(" state=%d\n", (data >> i) & 1);
29007c6b05d2SAlexander Motin 		}
29017c6b05d2SAlexander Motin 	}
29027c6b05d2SAlexander Motin }
29037c6b05d2SAlexander Motin 
29047c6b05d2SAlexander Motin static void
hdaa_dump_gpo(struct hdaa_devinfo * devinfo)29057c6b05d2SAlexander Motin hdaa_dump_gpo(struct hdaa_devinfo *devinfo)
29067c6b05d2SAlexander Motin {
29077c6b05d2SAlexander Motin 	device_t dev = devinfo->dev;
29087c6b05d2SAlexander Motin 	int i;
29097c6b05d2SAlexander Motin 	uint32_t data;
29107c6b05d2SAlexander Motin 
29117c6b05d2SAlexander Motin 	if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap) > 0) {
29127c6b05d2SAlexander Motin 		data = hda_command(dev,
29137c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
29147c6b05d2SAlexander Motin 		for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); i++) {
29157c6b05d2SAlexander Motin 			device_printf(dev, " GPO%d: state=%d", i,
29167c6b05d2SAlexander Motin 				    (data >> i) & 1);
29177c6b05d2SAlexander Motin 		}
29187c6b05d2SAlexander Motin 	}
29197c6b05d2SAlexander Motin }
29207c6b05d2SAlexander Motin 
29217c6b05d2SAlexander Motin static void
hdaa_audio_parse(struct hdaa_devinfo * devinfo)29227c6b05d2SAlexander Motin hdaa_audio_parse(struct hdaa_devinfo *devinfo)
29237c6b05d2SAlexander Motin {
29247c6b05d2SAlexander Motin 	struct hdaa_widget *w;
29257c6b05d2SAlexander Motin 	uint32_t res;
29267c6b05d2SAlexander Motin 	int i;
29277c6b05d2SAlexander Motin 	nid_t nid;
29287c6b05d2SAlexander Motin 
29297c6b05d2SAlexander Motin 	nid = devinfo->nid;
29307c6b05d2SAlexander Motin 
29317c6b05d2SAlexander Motin 	res = hda_command(devinfo->dev,
29327c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_GPIO_COUNT));
29337c6b05d2SAlexander Motin 	devinfo->gpio_cap = res;
29347c6b05d2SAlexander Motin 
29357c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
29367c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
29377c6b05d2SAlexander Motin 		    "NumGPIO=%d NumGPO=%d "
29387c6b05d2SAlexander Motin 		    "NumGPI=%d GPIWake=%d GPIUnsol=%d\n",
29397c6b05d2SAlexander Motin 		    HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap),
29407c6b05d2SAlexander Motin 		    HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap),
29417c6b05d2SAlexander Motin 		    HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap),
29427c6b05d2SAlexander Motin 		    HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap),
29437c6b05d2SAlexander Motin 		    HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap));
29447c6b05d2SAlexander Motin 		hdaa_dump_gpi(devinfo);
29457c6b05d2SAlexander Motin 		hdaa_dump_gpio(devinfo);
29467c6b05d2SAlexander Motin 		hdaa_dump_gpo(devinfo);
29477c6b05d2SAlexander Motin 	);
29487c6b05d2SAlexander Motin 
29497c6b05d2SAlexander Motin 	res = hda_command(devinfo->dev,
29507c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_STREAM_FORMATS));
29517c6b05d2SAlexander Motin 	devinfo->supp_stream_formats = res;
29527c6b05d2SAlexander Motin 
29537c6b05d2SAlexander Motin 	res = hda_command(devinfo->dev,
29547c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE));
29557c6b05d2SAlexander Motin 	devinfo->supp_pcm_size_rate = res;
29567c6b05d2SAlexander Motin 
29577c6b05d2SAlexander Motin 	res = hda_command(devinfo->dev,
29587c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_OUTPUT_AMP_CAP));
29597c6b05d2SAlexander Motin 	devinfo->outamp_cap = res;
29607c6b05d2SAlexander Motin 
29617c6b05d2SAlexander Motin 	res = hda_command(devinfo->dev,
29627c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_INPUT_AMP_CAP));
29637c6b05d2SAlexander Motin 	devinfo->inamp_cap = res;
29647c6b05d2SAlexander Motin 
29657c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
29667c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
29677c6b05d2SAlexander Motin 		if (w == NULL)
29687c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "Ghost widget! nid=%d!\n", i);
29697c6b05d2SAlexander Motin 		else {
29707c6b05d2SAlexander Motin 			w->devinfo = devinfo;
29717c6b05d2SAlexander Motin 			w->nid = i;
29727c6b05d2SAlexander Motin 			w->enable = 1;
29737c6b05d2SAlexander Motin 			w->selconn = -1;
29747c6b05d2SAlexander Motin 			w->pflags = 0;
29757c6b05d2SAlexander Motin 			w->ossdev = -1;
29767c6b05d2SAlexander Motin 			w->bindas = -1;
29777c6b05d2SAlexander Motin 			w->param.eapdbtl = HDA_INVALID;
29787c6b05d2SAlexander Motin 			hdaa_widget_parse(w);
29797c6b05d2SAlexander Motin 		}
29807c6b05d2SAlexander Motin 	}
29817c6b05d2SAlexander Motin }
29827c6b05d2SAlexander Motin 
29837c6b05d2SAlexander Motin static void
hdaa_audio_postprocess(struct hdaa_devinfo * devinfo)29847c6b05d2SAlexander Motin hdaa_audio_postprocess(struct hdaa_devinfo *devinfo)
29857c6b05d2SAlexander Motin {
29867c6b05d2SAlexander Motin 	struct hdaa_widget *w;
29877c6b05d2SAlexander Motin 	int i;
29887c6b05d2SAlexander Motin 
29897c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
29907c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
29917c6b05d2SAlexander Motin 		if (w == NULL)
29927c6b05d2SAlexander Motin 			continue;
29937c6b05d2SAlexander Motin 		hdaa_widget_postprocess(w);
29947c6b05d2SAlexander Motin 	}
29957c6b05d2SAlexander Motin }
29967c6b05d2SAlexander Motin 
29977c6b05d2SAlexander Motin static void
hdaa_audio_ctl_parse(struct hdaa_devinfo * devinfo)29987c6b05d2SAlexander Motin hdaa_audio_ctl_parse(struct hdaa_devinfo *devinfo)
29997c6b05d2SAlexander Motin {
30007c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctls;
30017c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
30027c6b05d2SAlexander Motin 	int i, j, cnt, max, ocap, icap;
30037c6b05d2SAlexander Motin 	int mute, offset, step, size;
30047c6b05d2SAlexander Motin 
30057c6b05d2SAlexander Motin 	/* XXX This is redundant */
30067c6b05d2SAlexander Motin 	max = 0;
30077c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
30087c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
30097c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
30107c6b05d2SAlexander Motin 			continue;
30117c6b05d2SAlexander Motin 		if (w->param.outamp_cap != 0)
30127c6b05d2SAlexander Motin 			max++;
30137c6b05d2SAlexander Motin 		if (w->param.inamp_cap != 0) {
30147c6b05d2SAlexander Motin 			switch (w->type) {
30157c6b05d2SAlexander Motin 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR:
30167c6b05d2SAlexander Motin 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER:
30177c6b05d2SAlexander Motin 				for (j = 0; j < w->nconns; j++) {
30187c6b05d2SAlexander Motin 					cw = hdaa_widget_get(devinfo,
30197c6b05d2SAlexander Motin 					    w->conns[j]);
30207c6b05d2SAlexander Motin 					if (cw == NULL || cw->enable == 0)
30217c6b05d2SAlexander Motin 						continue;
30227c6b05d2SAlexander Motin 					max++;
30237c6b05d2SAlexander Motin 				}
30247c6b05d2SAlexander Motin 				break;
30257c6b05d2SAlexander Motin 			default:
30267c6b05d2SAlexander Motin 				max++;
30277c6b05d2SAlexander Motin 				break;
30287c6b05d2SAlexander Motin 			}
30297c6b05d2SAlexander Motin 		}
30307c6b05d2SAlexander Motin 	}
30317c6b05d2SAlexander Motin 	devinfo->ctlcnt = max;
30327c6b05d2SAlexander Motin 
30337c6b05d2SAlexander Motin 	if (max < 1)
30347c6b05d2SAlexander Motin 		return;
30357c6b05d2SAlexander Motin 
30363cc01caaSChristos Margiolis 	ctls = malloc(sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT);
30377c6b05d2SAlexander Motin 
30387c6b05d2SAlexander Motin 	if (ctls == NULL) {
30397c6b05d2SAlexander Motin 		/* Blekh! */
30407c6b05d2SAlexander Motin 		device_printf(devinfo->dev, "unable to allocate ctls!\n");
30417c6b05d2SAlexander Motin 		devinfo->ctlcnt = 0;
30427c6b05d2SAlexander Motin 		return;
30437c6b05d2SAlexander Motin 	}
30447c6b05d2SAlexander Motin 
30457c6b05d2SAlexander Motin 	cnt = 0;
30467c6b05d2SAlexander Motin 	for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) {
30477c6b05d2SAlexander Motin 		if (cnt >= max) {
30487c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "%s: Ctl overflow!\n",
30497c6b05d2SAlexander Motin 			    __func__);
30507c6b05d2SAlexander Motin 			break;
30517c6b05d2SAlexander Motin 		}
30527c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
30537c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
30547c6b05d2SAlexander Motin 			continue;
30557c6b05d2SAlexander Motin 		ocap = w->param.outamp_cap;
30567c6b05d2SAlexander Motin 		icap = w->param.inamp_cap;
30577c6b05d2SAlexander Motin 		if (ocap != 0) {
30587c6b05d2SAlexander Motin 			mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap);
30597c6b05d2SAlexander Motin 			step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap);
30607c6b05d2SAlexander Motin 			size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap);
30617c6b05d2SAlexander Motin 			offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap);
30627c6b05d2SAlexander Motin 			/*if (offset > step) {
30637c6b05d2SAlexander Motin 				HDA_BOOTVERBOSE(
30647c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
30657c6b05d2SAlexander Motin 					    "BUGGY outamp: nid=%d "
30667c6b05d2SAlexander Motin 					    "[offset=%d > step=%d]\n",
30677c6b05d2SAlexander Motin 					    w->nid, offset, step);
30687c6b05d2SAlexander Motin 				);
30697c6b05d2SAlexander Motin 				offset = step;
30707c6b05d2SAlexander Motin 			}*/
30717c6b05d2SAlexander Motin 			ctls[cnt].enable = 1;
30727c6b05d2SAlexander Motin 			ctls[cnt].widget = w;
30737c6b05d2SAlexander Motin 			ctls[cnt].mute = mute;
30747c6b05d2SAlexander Motin 			ctls[cnt].step = step;
30757c6b05d2SAlexander Motin 			ctls[cnt].size = size;
30767c6b05d2SAlexander Motin 			ctls[cnt].offset = offset;
30777c6b05d2SAlexander Motin 			ctls[cnt].left = offset;
30787c6b05d2SAlexander Motin 			ctls[cnt].right = offset;
30797c6b05d2SAlexander Motin 			if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
30807c6b05d2SAlexander Motin 			    w->waspin)
30817c6b05d2SAlexander Motin 				ctls[cnt].ndir = HDAA_CTL_IN;
30827c6b05d2SAlexander Motin 			else
30837c6b05d2SAlexander Motin 				ctls[cnt].ndir = HDAA_CTL_OUT;
30847c6b05d2SAlexander Motin 			ctls[cnt++].dir = HDAA_CTL_OUT;
30857c6b05d2SAlexander Motin 		}
30867c6b05d2SAlexander Motin 
30877c6b05d2SAlexander Motin 		if (icap != 0) {
30887c6b05d2SAlexander Motin 			mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap);
30897c6b05d2SAlexander Motin 			step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap);
30907c6b05d2SAlexander Motin 			size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap);
30917c6b05d2SAlexander Motin 			offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap);
30927c6b05d2SAlexander Motin 			/*if (offset > step) {
30937c6b05d2SAlexander Motin 				HDA_BOOTVERBOSE(
30947c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
30957c6b05d2SAlexander Motin 					    "BUGGY inamp: nid=%d "
30967c6b05d2SAlexander Motin 					    "[offset=%d > step=%d]\n",
30977c6b05d2SAlexander Motin 					    w->nid, offset, step);
30987c6b05d2SAlexander Motin 				);
30997c6b05d2SAlexander Motin 				offset = step;
31007c6b05d2SAlexander Motin 			}*/
31017c6b05d2SAlexander Motin 			switch (w->type) {
31027c6b05d2SAlexander Motin 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR:
31037c6b05d2SAlexander Motin 			case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER:
31047c6b05d2SAlexander Motin 				for (j = 0; j < w->nconns; j++) {
31057c6b05d2SAlexander Motin 					if (cnt >= max) {
31067c6b05d2SAlexander Motin 						device_printf(devinfo->dev,
31077c6b05d2SAlexander Motin 						    "%s: Ctl overflow!\n",
31087c6b05d2SAlexander Motin 						    __func__);
31097c6b05d2SAlexander Motin 						break;
31107c6b05d2SAlexander Motin 					}
31117c6b05d2SAlexander Motin 					cw = hdaa_widget_get(devinfo,
31127c6b05d2SAlexander Motin 					    w->conns[j]);
31137c6b05d2SAlexander Motin 					if (cw == NULL || cw->enable == 0)
31147c6b05d2SAlexander Motin 						continue;
31157c6b05d2SAlexander Motin 					ctls[cnt].enable = 1;
31167c6b05d2SAlexander Motin 					ctls[cnt].widget = w;
31177c6b05d2SAlexander Motin 					ctls[cnt].childwidget = cw;
31187c6b05d2SAlexander Motin 					ctls[cnt].index = j;
31197c6b05d2SAlexander Motin 					ctls[cnt].mute = mute;
31207c6b05d2SAlexander Motin 					ctls[cnt].step = step;
31217c6b05d2SAlexander Motin 					ctls[cnt].size = size;
31227c6b05d2SAlexander Motin 					ctls[cnt].offset = offset;
31237c6b05d2SAlexander Motin 					ctls[cnt].left = offset;
31247c6b05d2SAlexander Motin 					ctls[cnt].right = offset;
31257c6b05d2SAlexander Motin 				ctls[cnt].ndir = HDAA_CTL_IN;
31267c6b05d2SAlexander Motin 					ctls[cnt++].dir = HDAA_CTL_IN;
31277c6b05d2SAlexander Motin 				}
31287c6b05d2SAlexander Motin 				break;
31297c6b05d2SAlexander Motin 			default:
31307c6b05d2SAlexander Motin 				if (cnt >= max) {
31317c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
31327c6b05d2SAlexander Motin 					    "%s: Ctl overflow!\n",
31337c6b05d2SAlexander Motin 					    __func__);
31347c6b05d2SAlexander Motin 					break;
31357c6b05d2SAlexander Motin 				}
31367c6b05d2SAlexander Motin 				ctls[cnt].enable = 1;
31377c6b05d2SAlexander Motin 				ctls[cnt].widget = w;
31387c6b05d2SAlexander Motin 				ctls[cnt].mute = mute;
31397c6b05d2SAlexander Motin 				ctls[cnt].step = step;
31407c6b05d2SAlexander Motin 				ctls[cnt].size = size;
31417c6b05d2SAlexander Motin 				ctls[cnt].offset = offset;
31427c6b05d2SAlexander Motin 				ctls[cnt].left = offset;
31437c6b05d2SAlexander Motin 				ctls[cnt].right = offset;
31447c6b05d2SAlexander Motin 				if (w->type ==
31457c6b05d2SAlexander Motin 				    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
31467c6b05d2SAlexander Motin 					ctls[cnt].ndir = HDAA_CTL_OUT;
31477c6b05d2SAlexander Motin 				else
31487c6b05d2SAlexander Motin 					ctls[cnt].ndir = HDAA_CTL_IN;
31497c6b05d2SAlexander Motin 				ctls[cnt++].dir = HDAA_CTL_IN;
31507c6b05d2SAlexander Motin 				break;
31517c6b05d2SAlexander Motin 			}
31527c6b05d2SAlexander Motin 		}
31537c6b05d2SAlexander Motin 	}
31547c6b05d2SAlexander Motin 
31557c6b05d2SAlexander Motin 	devinfo->ctl = ctls;
31567c6b05d2SAlexander Motin }
31577c6b05d2SAlexander Motin 
31587c6b05d2SAlexander Motin static void
hdaa_audio_as_parse(struct hdaa_devinfo * devinfo)31597c6b05d2SAlexander Motin hdaa_audio_as_parse(struct hdaa_devinfo *devinfo)
31607c6b05d2SAlexander Motin {
31617c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
31627c6b05d2SAlexander Motin 	struct hdaa_widget *w;
31637c6b05d2SAlexander Motin 	int i, j, cnt, max, type, dir, assoc, seq, first, hpredir;
31647c6b05d2SAlexander Motin 
31657c6b05d2SAlexander Motin 	/* Count present associations */
31667c6b05d2SAlexander Motin 	max = 0;
31677c6b05d2SAlexander Motin 	for (j = 1; j < 16; j++) {
31687c6b05d2SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
31697c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo, i);
31707c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
31717c6b05d2SAlexander Motin 				continue;
31727c6b05d2SAlexander Motin 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
31737c6b05d2SAlexander Motin 				continue;
31747c6b05d2SAlexander Motin 			if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config)
31757c6b05d2SAlexander Motin 			    != j)
31767c6b05d2SAlexander Motin 				continue;
31777c6b05d2SAlexander Motin 			max++;
31787c6b05d2SAlexander Motin 			if (j != 15)  /* There could be many 1-pin assocs #15 */
31797c6b05d2SAlexander Motin 				break;
31807c6b05d2SAlexander Motin 		}
31817c6b05d2SAlexander Motin 	}
31827c6b05d2SAlexander Motin 
31837c6b05d2SAlexander Motin 	devinfo->ascnt = max;
31847c6b05d2SAlexander Motin 
31857c6b05d2SAlexander Motin 	if (max < 1)
31867c6b05d2SAlexander Motin 		return;
31877c6b05d2SAlexander Motin 
31883cc01caaSChristos Margiolis 	as = malloc(sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT);
31897c6b05d2SAlexander Motin 
31907c6b05d2SAlexander Motin 	if (as == NULL) {
31917c6b05d2SAlexander Motin 		/* Blekh! */
31927c6b05d2SAlexander Motin 		device_printf(devinfo->dev, "unable to allocate assocs!\n");
31937c6b05d2SAlexander Motin 		devinfo->ascnt = 0;
31947c6b05d2SAlexander Motin 		return;
31957c6b05d2SAlexander Motin 	}
31967c6b05d2SAlexander Motin 
31977c6b05d2SAlexander Motin 	for (i = 0; i < max; i++) {
31987c6b05d2SAlexander Motin 		as[i].hpredir = -1;
31997c6b05d2SAlexander Motin 		as[i].digital = 0;
32007c6b05d2SAlexander Motin 		as[i].num_chans = 1;
32017c6b05d2SAlexander Motin 		as[i].location = -1;
32027c6b05d2SAlexander Motin 	}
32037c6b05d2SAlexander Motin 
32047c6b05d2SAlexander Motin 	/* Scan associations skipping as=0. */
32057c6b05d2SAlexander Motin 	cnt = 0;
32068dca9c27SHans Petter Selasky 	for (j = 1; j < 16 && cnt < max; j++) {
32077c6b05d2SAlexander Motin 		first = 16;
32087c6b05d2SAlexander Motin 		hpredir = 0;
32097c6b05d2SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
32107c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo, i);
32117c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
32127c6b05d2SAlexander Motin 				continue;
32137c6b05d2SAlexander Motin 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
32147c6b05d2SAlexander Motin 				continue;
32157c6b05d2SAlexander Motin 			assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config);
32167c6b05d2SAlexander Motin 			seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config);
32177c6b05d2SAlexander Motin 			if (assoc != j) {
32187c6b05d2SAlexander Motin 				continue;
32197c6b05d2SAlexander Motin 			}
32207c6b05d2SAlexander Motin 			KASSERT(cnt < max,
32217c6b05d2SAlexander Motin 			    ("%s: Associations owerflow (%d of %d)",
32227c6b05d2SAlexander Motin 			    __func__, cnt, max));
32237c6b05d2SAlexander Motin 			type = w->wclass.pin.config &
32247c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
32257c6b05d2SAlexander Motin 			/* Get pin direction. */
32267c6b05d2SAlexander Motin 			if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT ||
32277c6b05d2SAlexander Motin 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER ||
32287c6b05d2SAlexander Motin 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT ||
32297c6b05d2SAlexander Motin 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT ||
32307c6b05d2SAlexander Motin 			    type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT)
32317c6b05d2SAlexander Motin 				dir = HDAA_CTL_OUT;
32327c6b05d2SAlexander Motin 			else
32337c6b05d2SAlexander Motin 				dir = HDAA_CTL_IN;
32347c6b05d2SAlexander Motin 			/* If this is a first pin - create new association. */
32357c6b05d2SAlexander Motin 			if (as[cnt].pincnt == 0) {
32367c6b05d2SAlexander Motin 				as[cnt].enable = 1;
32377c6b05d2SAlexander Motin 				as[cnt].index = j;
32387c6b05d2SAlexander Motin 				as[cnt].dir = dir;
32397c6b05d2SAlexander Motin 			}
32407c6b05d2SAlexander Motin 			if (seq < first)
32417c6b05d2SAlexander Motin 				first = seq;
32427c6b05d2SAlexander Motin 			/* Check association correctness. */
32437c6b05d2SAlexander Motin 			if (as[cnt].pins[seq] != 0) {
32447c6b05d2SAlexander Motin 				device_printf(devinfo->dev, "%s: Duplicate pin %d (%d) "
32457c6b05d2SAlexander Motin 				    "in association %d! Disabling association.\n",
32467c6b05d2SAlexander Motin 				    __func__, seq, w->nid, j);
32477c6b05d2SAlexander Motin 				as[cnt].enable = 0;
32487c6b05d2SAlexander Motin 			}
32497c6b05d2SAlexander Motin 			if (dir != as[cnt].dir) {
32507c6b05d2SAlexander Motin 				device_printf(devinfo->dev, "%s: Pin %d has wrong "
32517c6b05d2SAlexander Motin 				    "direction for association %d! Disabling "
32527c6b05d2SAlexander Motin 				    "association.\n",
32537c6b05d2SAlexander Motin 				    __func__, w->nid, j);
32547c6b05d2SAlexander Motin 				as[cnt].enable = 0;
32557c6b05d2SAlexander Motin 			}
32567c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
3257e0f1c0d7SAlexander Motin 				as[cnt].digital |= 0x1;
3258e0f1c0d7SAlexander Motin 				if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
3259e0f1c0d7SAlexander Motin 					as[cnt].digital |= 0x2;
32607c6b05d2SAlexander Motin 				if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap))
3261e0f1c0d7SAlexander Motin 					as[cnt].digital |= 0x4;
32627c6b05d2SAlexander Motin 			}
32637c6b05d2SAlexander Motin 			if (as[cnt].location == -1) {
32647c6b05d2SAlexander Motin 				as[cnt].location =
32657c6b05d2SAlexander Motin 				    HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config);
32667c6b05d2SAlexander Motin 			} else if (as[cnt].location !=
32677c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config)) {
32687c6b05d2SAlexander Motin 				as[cnt].location = -2;
32697c6b05d2SAlexander Motin 			}
32707c6b05d2SAlexander Motin 			/* Headphones with seq=15 may mean redirection. */
32717c6b05d2SAlexander Motin 			if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT &&
32727c6b05d2SAlexander Motin 			    seq == 15)
32737c6b05d2SAlexander Motin 				hpredir = 1;
32747c6b05d2SAlexander Motin 			as[cnt].pins[seq] = w->nid;
32757c6b05d2SAlexander Motin 			as[cnt].pincnt++;
32767c6b05d2SAlexander Motin 			/* Association 15 is a multiple unassociated pins. */
32777c6b05d2SAlexander Motin 			if (j == 15)
32787c6b05d2SAlexander Motin 				cnt++;
32797c6b05d2SAlexander Motin 		}
32807c6b05d2SAlexander Motin 		if (j != 15 && as[cnt].pincnt > 0) {
32817c6b05d2SAlexander Motin 			if (hpredir && as[cnt].pincnt > 1)
32827c6b05d2SAlexander Motin 				as[cnt].hpredir = first;
32837c6b05d2SAlexander Motin 			cnt++;
32847c6b05d2SAlexander Motin 		}
32857c6b05d2SAlexander Motin 	}
32867c6b05d2SAlexander Motin 	for (i = 0; i < max; i++) {
32877c6b05d2SAlexander Motin 		if (as[i].dir == HDAA_CTL_IN && (as[i].pincnt == 1 ||
32887c6b05d2SAlexander Motin 		    as[i].pins[14] > 0 || as[i].pins[15] > 0))
32897c6b05d2SAlexander Motin 			as[i].mixed = 1;
32907c6b05d2SAlexander Motin 	}
32917c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
32927c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
32937c6b05d2SAlexander Motin 		    "%d associations found:\n", max);
32947c6b05d2SAlexander Motin 		for (i = 0; i < max; i++) {
32957c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
32967c6b05d2SAlexander Motin 			    "Association %d (%d) %s%s:\n",
32977c6b05d2SAlexander Motin 			    i, as[i].index, (as[i].dir == HDAA_CTL_IN)?"in":"out",
32987c6b05d2SAlexander Motin 			    as[i].enable?"":" (disabled)");
32997c6b05d2SAlexander Motin 			for (j = 0; j < 16; j++) {
33007c6b05d2SAlexander Motin 				if (as[i].pins[j] == 0)
33017c6b05d2SAlexander Motin 					continue;
33027c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
33037c6b05d2SAlexander Motin 				    " Pin nid=%d seq=%d\n",
33047c6b05d2SAlexander Motin 				    as[i].pins[j], j);
33057c6b05d2SAlexander Motin 			}
33067c6b05d2SAlexander Motin 		}
33077c6b05d2SAlexander Motin 	);
33087c6b05d2SAlexander Motin 
33097c6b05d2SAlexander Motin 	devinfo->as = as;
33107c6b05d2SAlexander Motin }
33117c6b05d2SAlexander Motin 
33127c6b05d2SAlexander Motin /*
33137c6b05d2SAlexander Motin  * Trace path from DAC to pin.
33147c6b05d2SAlexander Motin  */
33157c6b05d2SAlexander Motin 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)33167c6b05d2SAlexander Motin hdaa_audio_trace_dac(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid,
33177c6b05d2SAlexander Motin     int dupseq, int min, int only, int depth)
33187c6b05d2SAlexander Motin {
33197c6b05d2SAlexander Motin 	struct hdaa_widget *w;
33207c6b05d2SAlexander Motin 	int i, im = -1;
33217c6b05d2SAlexander Motin 	nid_t m = 0, ret;
33227c6b05d2SAlexander Motin 
33237c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
33247c6b05d2SAlexander Motin 		return (0);
33257c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
33267c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
33277c6b05d2SAlexander Motin 		return (0);
33287c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
33297c6b05d2SAlexander Motin 		if (!only) {
33307c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
33317c6b05d2SAlexander Motin 			    " %*stracing via nid %d\n",
33327c6b05d2SAlexander Motin 				depth + 1, "", w->nid);
33337c6b05d2SAlexander Motin 		}
33347c6b05d2SAlexander Motin 	);
33357c6b05d2SAlexander Motin 	/* Use only unused widgets */
33367c6b05d2SAlexander Motin 	if (w->bindas >= 0 && w->bindas != as) {
33377c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
33387c6b05d2SAlexander Motin 			if (!only) {
33397c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
33407c6b05d2SAlexander Motin 				    " %*snid %d busy by association %d\n",
33417c6b05d2SAlexander Motin 					depth + 1, "", w->nid, w->bindas);
33427c6b05d2SAlexander Motin 			}
33437c6b05d2SAlexander Motin 		);
33447c6b05d2SAlexander Motin 		return (0);
33457c6b05d2SAlexander Motin 	}
33467c6b05d2SAlexander Motin 	if (dupseq < 0) {
33477c6b05d2SAlexander Motin 		if (w->bindseqmask != 0) {
33487c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
33497c6b05d2SAlexander Motin 				if (!only) {
33507c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
33517c6b05d2SAlexander Motin 					    " %*snid %d busy by seqmask %x\n",
33527c6b05d2SAlexander Motin 						depth + 1, "", w->nid, w->bindseqmask);
33537c6b05d2SAlexander Motin 				}
33547c6b05d2SAlexander Motin 			);
33557c6b05d2SAlexander Motin 			return (0);
33567c6b05d2SAlexander Motin 		}
33577c6b05d2SAlexander Motin 	} else {
33587c6b05d2SAlexander Motin 		/* If this is headphones - allow duplicate first pin. */
33597c6b05d2SAlexander Motin 		if (w->bindseqmask != 0 &&
33607c6b05d2SAlexander Motin 		    (w->bindseqmask & (1 << dupseq)) == 0) {
33617c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
33627c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
33637c6b05d2SAlexander Motin 				    " %*snid %d busy by seqmask %x\n",
33647c6b05d2SAlexander Motin 					depth + 1, "", w->nid, w->bindseqmask);
33657c6b05d2SAlexander Motin 			);
33667c6b05d2SAlexander Motin 			return (0);
33677c6b05d2SAlexander Motin 		}
33687c6b05d2SAlexander Motin 	}
33697c6b05d2SAlexander Motin 
33707c6b05d2SAlexander Motin 	switch (w->type) {
33717c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
33727c6b05d2SAlexander Motin 		/* Do not traverse input. AD1988 has digital monitor
33737c6b05d2SAlexander Motin 		for which we are not ready. */
33747c6b05d2SAlexander Motin 		break;
33757c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT:
33767c6b05d2SAlexander Motin 		/* If we are tracing HP take only dac of first pin. */
33777c6b05d2SAlexander Motin 		if ((only == 0 || only == w->nid) &&
33787c6b05d2SAlexander Motin 		    (w->nid >= min) && (dupseq < 0 || w->nid ==
33797c6b05d2SAlexander Motin 		    devinfo->as[as].dacs[0][dupseq]))
33807c6b05d2SAlexander Motin 			m = w->nid;
33817c6b05d2SAlexander Motin 		break;
33827c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
33837c6b05d2SAlexander Motin 		if (depth > 0)
33847c6b05d2SAlexander Motin 			break;
33857c6b05d2SAlexander Motin 		/* Fall */
33867c6b05d2SAlexander Motin 	default:
33877c6b05d2SAlexander Motin 		/* Find reachable DACs with smallest nid respecting constraints. */
33887c6b05d2SAlexander Motin 		for (i = 0; i < w->nconns; i++) {
33897c6b05d2SAlexander Motin 			if (w->connsenable[i] == 0)
33907c6b05d2SAlexander Motin 				continue;
33917c6b05d2SAlexander Motin 			if (w->selconn != -1 && w->selconn != i)
33927c6b05d2SAlexander Motin 				continue;
33937c6b05d2SAlexander Motin 			if ((ret = hdaa_audio_trace_dac(devinfo, as, seq,
33947c6b05d2SAlexander Motin 			    w->conns[i], dupseq, min, only, depth + 1)) != 0) {
33957c6b05d2SAlexander Motin 				if (m == 0 || ret < m) {
33967c6b05d2SAlexander Motin 					m = ret;
33977c6b05d2SAlexander Motin 					im = i;
33987c6b05d2SAlexander Motin 				}
33997c6b05d2SAlexander Motin 				if (only || dupseq >= 0)
34007c6b05d2SAlexander Motin 					break;
34017c6b05d2SAlexander Motin 			}
34027c6b05d2SAlexander Motin 		}
34037c6b05d2SAlexander Motin 		if (im >= 0 && only && ((w->nconns > 1 &&
34047c6b05d2SAlexander Motin 		    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) ||
34057c6b05d2SAlexander Motin 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR))
34067c6b05d2SAlexander Motin 			w->selconn = im;
34077c6b05d2SAlexander Motin 		break;
34087c6b05d2SAlexander Motin 	}
34097c6b05d2SAlexander Motin 	if (m && only) {
34107c6b05d2SAlexander Motin 		w->bindas = as;
34117c6b05d2SAlexander Motin 		w->bindseqmask |= (1 << seq);
34127c6b05d2SAlexander Motin 	}
34137c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
34147c6b05d2SAlexander Motin 		if (!only) {
34157c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
34167c6b05d2SAlexander Motin 			    " %*snid %d returned %d\n",
34177c6b05d2SAlexander Motin 				depth + 1, "", w->nid, m);
34187c6b05d2SAlexander Motin 		}
34197c6b05d2SAlexander Motin 	);
34207c6b05d2SAlexander Motin 	return (m);
34217c6b05d2SAlexander Motin }
34227c6b05d2SAlexander Motin 
34237c6b05d2SAlexander Motin /*
34247c6b05d2SAlexander Motin  * Trace path from widget to ADC.
34257c6b05d2SAlexander Motin  */
34267c6b05d2SAlexander Motin 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)34277c6b05d2SAlexander Motin hdaa_audio_trace_adc(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid,
34287c6b05d2SAlexander Motin     int mixed, int min, int only, int depth, int *length, int onlylength)
34297c6b05d2SAlexander Motin {
34307c6b05d2SAlexander Motin 	struct hdaa_widget *w, *wc;
34317c6b05d2SAlexander Motin 	int i, j, im, lm = HDA_PARSE_MAXDEPTH;
34327c6b05d2SAlexander Motin 	nid_t m = 0, ret;
34337c6b05d2SAlexander Motin 
34347c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
34357c6b05d2SAlexander Motin 		return (0);
34367c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
34377c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
34387c6b05d2SAlexander Motin 		return (0);
34397c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
34407c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
34417c6b05d2SAlexander Motin 		    " %*stracing via nid %d\n",
34427c6b05d2SAlexander Motin 			depth + 1, "", w->nid);
34437c6b05d2SAlexander Motin 	);
34447c6b05d2SAlexander Motin 	/* Use only unused widgets */
34457c6b05d2SAlexander Motin 	if (w->bindas >= 0 && w->bindas != as) {
34467c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
34477c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
34487c6b05d2SAlexander Motin 			    " %*snid %d busy by association %d\n",
34497c6b05d2SAlexander Motin 				depth + 1, "", w->nid, w->bindas);
34507c6b05d2SAlexander Motin 		);
34517c6b05d2SAlexander Motin 		return (0);
34527c6b05d2SAlexander Motin 	}
34537c6b05d2SAlexander Motin 	if (!mixed && w->bindseqmask != 0) {
34547c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
34557c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
34567c6b05d2SAlexander Motin 			    " %*snid %d busy by seqmask %x\n",
34577c6b05d2SAlexander Motin 				depth + 1, "", w->nid, w->bindseqmask);
34587c6b05d2SAlexander Motin 		);
34597c6b05d2SAlexander Motin 		return (0);
34607c6b05d2SAlexander Motin 	}
34617c6b05d2SAlexander Motin 	switch (w->type) {
34627c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
34637c6b05d2SAlexander Motin 		if ((only == 0 || only == w->nid) && (w->nid >= min) &&
34647c6b05d2SAlexander Motin 		    (onlylength == 0 || onlylength == depth)) {
34657c6b05d2SAlexander Motin 			m = w->nid;
34667c6b05d2SAlexander Motin 			*length = depth;
34677c6b05d2SAlexander Motin 		}
34687c6b05d2SAlexander Motin 		break;
34697c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
34707c6b05d2SAlexander Motin 		if (depth > 0)
34717c6b05d2SAlexander Motin 			break;
34727c6b05d2SAlexander Motin 		/* Fall */
34737c6b05d2SAlexander Motin 	default:
34747c6b05d2SAlexander Motin 		/* Try to find reachable ADCs with specified nid. */
34757c6b05d2SAlexander Motin 		for (j = devinfo->startnode; j < devinfo->endnode; j++) {
34767c6b05d2SAlexander Motin 			wc = hdaa_widget_get(devinfo, j);
34777c6b05d2SAlexander Motin 			if (wc == NULL || wc->enable == 0)
34787c6b05d2SAlexander Motin 				continue;
34797c6b05d2SAlexander Motin 			im = -1;
34807c6b05d2SAlexander Motin 			for (i = 0; i < wc->nconns; i++) {
34817c6b05d2SAlexander Motin 				if (wc->connsenable[i] == 0)
34827c6b05d2SAlexander Motin 					continue;
34837c6b05d2SAlexander Motin 				if (wc->conns[i] != nid)
34847c6b05d2SAlexander Motin 					continue;
34857c6b05d2SAlexander Motin 				if ((ret = hdaa_audio_trace_adc(devinfo, as, seq,
34867c6b05d2SAlexander Motin 				    j, mixed, min, only, depth + 1,
34877c6b05d2SAlexander Motin 				    length, onlylength)) != 0) {
34887c6b05d2SAlexander Motin 					if (m == 0 || ret < m ||
34898684fef0SAlexander Motin 					    (ret == m && *length < lm)) {
34907c6b05d2SAlexander Motin 						m = ret;
34917c6b05d2SAlexander Motin 						im = i;
34927c6b05d2SAlexander Motin 						lm = *length;
34938684fef0SAlexander Motin 					} else
34948684fef0SAlexander Motin 						*length = lm;
34957c6b05d2SAlexander Motin 					if (only)
34967c6b05d2SAlexander Motin 						break;
34977c6b05d2SAlexander Motin 				}
34987c6b05d2SAlexander Motin 			}
34997c6b05d2SAlexander Motin 			if (im >= 0 && only && ((wc->nconns > 1 &&
35007c6b05d2SAlexander Motin 			    wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) ||
35017c6b05d2SAlexander Motin 			    wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR))
35027c6b05d2SAlexander Motin 				wc->selconn = im;
35037c6b05d2SAlexander Motin 		}
35047c6b05d2SAlexander Motin 		break;
35057c6b05d2SAlexander Motin 	}
35067c6b05d2SAlexander Motin 	if (m && only) {
35077c6b05d2SAlexander Motin 		w->bindas = as;
35087c6b05d2SAlexander Motin 		w->bindseqmask |= (1 << seq);
35097c6b05d2SAlexander Motin 	}
35107c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
35117c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
35127c6b05d2SAlexander Motin 		    " %*snid %d returned %d\n",
35137c6b05d2SAlexander Motin 			depth + 1, "", w->nid, m);
35147c6b05d2SAlexander Motin 	);
35157c6b05d2SAlexander Motin 	return (m);
35167c6b05d2SAlexander Motin }
35177c6b05d2SAlexander Motin 
35187c6b05d2SAlexander Motin /*
35197c6b05d2SAlexander Motin  * Erase trace path of the specified association.
35207c6b05d2SAlexander Motin  */
35217c6b05d2SAlexander Motin static void
hdaa_audio_undo_trace(struct hdaa_devinfo * devinfo,int as,int seq)35227c6b05d2SAlexander Motin hdaa_audio_undo_trace(struct hdaa_devinfo *devinfo, int as, int seq)
35237c6b05d2SAlexander Motin {
35247c6b05d2SAlexander Motin 	struct hdaa_widget *w;
35257c6b05d2SAlexander Motin 	int i;
35267c6b05d2SAlexander Motin 
35277c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
35287c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
35297c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
35307c6b05d2SAlexander Motin 			continue;
35317c6b05d2SAlexander Motin 		if (w->bindas == as) {
35327c6b05d2SAlexander Motin 			if (seq >= 0) {
35337c6b05d2SAlexander Motin 				w->bindseqmask &= ~(1 << seq);
35347c6b05d2SAlexander Motin 				if (w->bindseqmask == 0) {
35357c6b05d2SAlexander Motin 					w->bindas = -1;
35367c6b05d2SAlexander Motin 					w->selconn = -1;
35377c6b05d2SAlexander Motin 				}
35387c6b05d2SAlexander Motin 			} else {
35397c6b05d2SAlexander Motin 				w->bindas = -1;
35407c6b05d2SAlexander Motin 				w->bindseqmask = 0;
35417c6b05d2SAlexander Motin 				w->selconn = -1;
35427c6b05d2SAlexander Motin 			}
35437c6b05d2SAlexander Motin 		}
35447c6b05d2SAlexander Motin 	}
35457c6b05d2SAlexander Motin }
35467c6b05d2SAlexander Motin 
35477c6b05d2SAlexander Motin /*
35487c6b05d2SAlexander Motin  * Trace association path from DAC to output
35497c6b05d2SAlexander Motin  */
35507c6b05d2SAlexander Motin static int
hdaa_audio_trace_as_out(struct hdaa_devinfo * devinfo,int as,int seq)35517c6b05d2SAlexander Motin hdaa_audio_trace_as_out(struct hdaa_devinfo *devinfo, int as, int seq)
35527c6b05d2SAlexander Motin {
35537c6b05d2SAlexander Motin 	struct hdaa_audio_as *ases = devinfo->as;
35547c6b05d2SAlexander Motin 	int i, hpredir;
35557c6b05d2SAlexander Motin 	nid_t min, res;
35567c6b05d2SAlexander Motin 
35577c6b05d2SAlexander Motin 	/* Find next pin */
35587c6b05d2SAlexander Motin 	for (i = seq; i < 16 && ases[as].pins[i] == 0; i++)
35597c6b05d2SAlexander Motin 		;
35607c6b05d2SAlexander Motin 	/* Check if there is no any left. If so - we succeeded. */
35617c6b05d2SAlexander Motin 	if (i == 16)
35627c6b05d2SAlexander Motin 		return (1);
35637c6b05d2SAlexander Motin 
35647c6b05d2SAlexander Motin 	hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1;
35657c6b05d2SAlexander Motin 	min = 0;
35667c6b05d2SAlexander Motin 	do {
35677c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
35687c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
35697c6b05d2SAlexander Motin 			    " Tracing pin %d with min nid %d",
35707c6b05d2SAlexander Motin 			    ases[as].pins[i], min);
35717c6b05d2SAlexander Motin 			if (hpredir >= 0)
35727c6b05d2SAlexander Motin 				printf(" and hpredir %d", hpredir);
35737c6b05d2SAlexander Motin 			printf("\n");
35747c6b05d2SAlexander Motin 		);
35757c6b05d2SAlexander Motin 		/* Trace this pin taking min nid into account. */
35767c6b05d2SAlexander Motin 		res = hdaa_audio_trace_dac(devinfo, as, i,
35777c6b05d2SAlexander Motin 		    ases[as].pins[i], hpredir, min, 0, 0);
35787c6b05d2SAlexander Motin 		if (res == 0) {
35797c6b05d2SAlexander Motin 			/* If we failed - return to previous and redo it. */
35807c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
35817c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
35827c6b05d2SAlexander Motin 				    " Unable to trace pin %d seq %d with min "
35837c6b05d2SAlexander Motin 				    "nid %d",
35847c6b05d2SAlexander Motin 				    ases[as].pins[i], i, min);
35857c6b05d2SAlexander Motin 				if (hpredir >= 0)
35867c6b05d2SAlexander Motin 					printf(" and hpredir %d", hpredir);
35877c6b05d2SAlexander Motin 				printf("\n");
35887c6b05d2SAlexander Motin 			);
35897c6b05d2SAlexander Motin 			return (0);
35907c6b05d2SAlexander Motin 		}
35917c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
35927c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
35937c6b05d2SAlexander Motin 			    " Pin %d traced to DAC %d",
35947c6b05d2SAlexander Motin 			    ases[as].pins[i], res);
35957c6b05d2SAlexander Motin 			if (hpredir >= 0)
35967c6b05d2SAlexander Motin 				printf(" and hpredir %d", hpredir);
35977c6b05d2SAlexander Motin 			if (ases[as].fakeredir)
35987c6b05d2SAlexander Motin 				printf(" with fake redirection");
35997c6b05d2SAlexander Motin 			printf("\n");
36007c6b05d2SAlexander Motin 		);
36017c6b05d2SAlexander Motin 		/* Trace again to mark the path */
36027c6b05d2SAlexander Motin 		hdaa_audio_trace_dac(devinfo, as, i,
36037c6b05d2SAlexander Motin 		    ases[as].pins[i], hpredir, min, res, 0);
36047c6b05d2SAlexander Motin 		ases[as].dacs[0][i] = res;
36057c6b05d2SAlexander Motin 		/* We succeeded, so call next. */
36067c6b05d2SAlexander Motin 		if (hdaa_audio_trace_as_out(devinfo, as, i + 1))
36077c6b05d2SAlexander Motin 			return (1);
36087c6b05d2SAlexander Motin 		/* If next failed, we should retry with next min */
36097c6b05d2SAlexander Motin 		hdaa_audio_undo_trace(devinfo, as, i);
36107c6b05d2SAlexander Motin 		ases[as].dacs[0][i] = 0;
36117c6b05d2SAlexander Motin 		min = res + 1;
36127c6b05d2SAlexander Motin 	} while (1);
36137c6b05d2SAlexander Motin }
36147c6b05d2SAlexander Motin 
36157c6b05d2SAlexander Motin /*
36167c6b05d2SAlexander Motin  * Check equivalency of two DACs.
36177c6b05d2SAlexander Motin  */
36187c6b05d2SAlexander Motin static int
hdaa_audio_dacs_equal(struct hdaa_widget * w1,struct hdaa_widget * w2)36197c6b05d2SAlexander Motin hdaa_audio_dacs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2)
36207c6b05d2SAlexander Motin {
36217c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = w1->devinfo;
36227c6b05d2SAlexander Motin 	struct hdaa_widget *w3;
36237c6b05d2SAlexander Motin 	int i, j, c1, c2;
36247c6b05d2SAlexander Motin 
36257c6b05d2SAlexander Motin 	if (memcmp(&w1->param, &w2->param, sizeof(w1->param)))
36267c6b05d2SAlexander Motin 		return (0);
36277c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
36287c6b05d2SAlexander Motin 		w3 = hdaa_widget_get(devinfo, i);
36297c6b05d2SAlexander Motin 		if (w3 == NULL || w3->enable == 0)
36307c6b05d2SAlexander Motin 			continue;
36317c6b05d2SAlexander Motin 		if (w3->bindas != w1->bindas)
36327c6b05d2SAlexander Motin 			continue;
36337c6b05d2SAlexander Motin 		if (w3->nconns == 0)
36347c6b05d2SAlexander Motin 			continue;
36357c6b05d2SAlexander Motin 		c1 = c2 = -1;
36367c6b05d2SAlexander Motin 		for (j = 0; j < w3->nconns; j++) {
36377c6b05d2SAlexander Motin 			if (w3->connsenable[j] == 0)
36387c6b05d2SAlexander Motin 				continue;
36397c6b05d2SAlexander Motin 			if (w3->conns[j] == w1->nid)
36407c6b05d2SAlexander Motin 				c1 = j;
36417c6b05d2SAlexander Motin 			if (w3->conns[j] == w2->nid)
36427c6b05d2SAlexander Motin 				c2 = j;
36437c6b05d2SAlexander Motin 		}
36447c6b05d2SAlexander Motin 		if (c1 < 0)
36457c6b05d2SAlexander Motin 			continue;
36467c6b05d2SAlexander Motin 		if (c2 < 0)
36477c6b05d2SAlexander Motin 			return (0);
36487c6b05d2SAlexander Motin 		if (w3->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
36497c6b05d2SAlexander Motin 			return (0);
36507c6b05d2SAlexander Motin 	}
36517c6b05d2SAlexander Motin 	return (1);
36527c6b05d2SAlexander Motin }
36537c6b05d2SAlexander Motin 
36547c6b05d2SAlexander Motin /*
36557c6b05d2SAlexander Motin  * Check equivalency of two ADCs.
36567c6b05d2SAlexander Motin  */
36577c6b05d2SAlexander Motin static int
hdaa_audio_adcs_equal(struct hdaa_widget * w1,struct hdaa_widget * w2)36587c6b05d2SAlexander Motin hdaa_audio_adcs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2)
36597c6b05d2SAlexander Motin {
36607c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = w1->devinfo;
36617c6b05d2SAlexander Motin 	struct hdaa_widget *w3, *w4;
36627c6b05d2SAlexander Motin 	int i;
36637c6b05d2SAlexander Motin 
36647c6b05d2SAlexander Motin 	if (memcmp(&w1->param, &w2->param, sizeof(w1->param)))
36657c6b05d2SAlexander Motin 		return (0);
36667c6b05d2SAlexander Motin 	if (w1->nconns != 1 || w2->nconns != 1)
36677c6b05d2SAlexander Motin 		return (0);
36687c6b05d2SAlexander Motin 	if (w1->conns[0] == w2->conns[0])
36697c6b05d2SAlexander Motin 		return (1);
36707c6b05d2SAlexander Motin 	w3 = hdaa_widget_get(devinfo, w1->conns[0]);
36717c6b05d2SAlexander Motin 	if (w3 == NULL || w3->enable == 0)
36727c6b05d2SAlexander Motin 		return (0);
36737c6b05d2SAlexander Motin 	w4 = hdaa_widget_get(devinfo, w2->conns[0]);
36747c6b05d2SAlexander Motin 	if (w4 == NULL || w4->enable == 0)
36757c6b05d2SAlexander Motin 		return (0);
36767c6b05d2SAlexander Motin 	if (w3->bindas == w4->bindas && w3->bindseqmask == w4->bindseqmask)
36777c6b05d2SAlexander Motin 		return (1);
36787c6b05d2SAlexander Motin 	if (w4->bindas >= 0)
36797c6b05d2SAlexander Motin 		return (0);
36807c6b05d2SAlexander Motin 	if (w3->type != w4->type)
36817c6b05d2SAlexander Motin 		return (0);
36827c6b05d2SAlexander Motin 	if (memcmp(&w3->param, &w4->param, sizeof(w3->param)))
36837c6b05d2SAlexander Motin 		return (0);
36847c6b05d2SAlexander Motin 	if (w3->nconns != w4->nconns)
36857c6b05d2SAlexander Motin 		return (0);
36867c6b05d2SAlexander Motin 	for (i = 0; i < w3->nconns; i++) {
36877c6b05d2SAlexander Motin 		if (w3->conns[i] != w4->conns[i])
36887c6b05d2SAlexander Motin 			return (0);
36897c6b05d2SAlexander Motin 	}
36907c6b05d2SAlexander Motin 	return (1);
36917c6b05d2SAlexander Motin }
36927c6b05d2SAlexander Motin 
36937c6b05d2SAlexander Motin /*
36947c6b05d2SAlexander Motin  * Look for equivalent DAC/ADC to implement second channel.
36957c6b05d2SAlexander Motin  */
36967c6b05d2SAlexander Motin static void
hdaa_audio_adddac(struct hdaa_devinfo * devinfo,int asid)36977c6b05d2SAlexander Motin hdaa_audio_adddac(struct hdaa_devinfo *devinfo, int asid)
36987c6b05d2SAlexander Motin {
36997c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = &devinfo->as[asid];
37007c6b05d2SAlexander Motin 	struct hdaa_widget *w1, *w2;
37017c6b05d2SAlexander Motin 	int i, pos;
37027c6b05d2SAlexander Motin 	nid_t nid1, nid2;
37037c6b05d2SAlexander Motin 
37047c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
37057c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
37067c6b05d2SAlexander Motin 		    "Looking for additional %sC "
37077c6b05d2SAlexander Motin 		    "for association %d (%d)\n",
37087c6b05d2SAlexander Motin 		    (as->dir == HDAA_CTL_OUT) ? "DA" : "AD",
37097c6b05d2SAlexander Motin 		    asid, as->index);
37107c6b05d2SAlexander Motin 	);
37117c6b05d2SAlexander Motin 
37120ce96176SGordon Bergling 	/* Find the existing DAC position and return if found more the one. */
37137c6b05d2SAlexander Motin 	pos = -1;
37147c6b05d2SAlexander Motin 	for (i = 0; i < 16; i++) {
37157c6b05d2SAlexander Motin 		if (as->dacs[0][i] <= 0)
37167c6b05d2SAlexander Motin 			continue;
37177c6b05d2SAlexander Motin 		if (pos >= 0 && as->dacs[0][i] != as->dacs[0][pos])
37187c6b05d2SAlexander Motin 			return;
37197c6b05d2SAlexander Motin 		pos = i;
37207c6b05d2SAlexander Motin 	}
37217c6b05d2SAlexander Motin 
37227c6b05d2SAlexander Motin 	nid1 = as->dacs[0][pos];
37237c6b05d2SAlexander Motin 	w1 = hdaa_widget_get(devinfo, nid1);
37247c6b05d2SAlexander Motin 	w2 = NULL;
37257c6b05d2SAlexander Motin 	for (nid2 = devinfo->startnode; nid2 < devinfo->endnode; nid2++) {
37267c6b05d2SAlexander Motin 		w2 = hdaa_widget_get(devinfo, nid2);
37277c6b05d2SAlexander Motin 		if (w2 == NULL || w2->enable == 0)
37287c6b05d2SAlexander Motin 			continue;
37297c6b05d2SAlexander Motin 		if (w2->bindas >= 0)
37307c6b05d2SAlexander Motin 			continue;
37317c6b05d2SAlexander Motin 		if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) {
37327c6b05d2SAlexander Motin 			if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT)
37337c6b05d2SAlexander Motin 				continue;
37347c6b05d2SAlexander Motin 			if (hdaa_audio_dacs_equal(w1, w2))
37357c6b05d2SAlexander Motin 				break;
37367c6b05d2SAlexander Motin 		} else {
37377c6b05d2SAlexander Motin 			if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT)
37387c6b05d2SAlexander Motin 				continue;
37397c6b05d2SAlexander Motin 			if (hdaa_audio_adcs_equal(w1, w2))
37407c6b05d2SAlexander Motin 				break;
37417c6b05d2SAlexander Motin 		}
37427c6b05d2SAlexander Motin 	}
37437c6b05d2SAlexander Motin 	if (nid2 >= devinfo->endnode)
37447c6b05d2SAlexander Motin 		return;
37457c6b05d2SAlexander Motin 	w2->bindas = w1->bindas;
37467c6b05d2SAlexander Motin 	w2->bindseqmask = w1->bindseqmask;
37477c6b05d2SAlexander Motin 	if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
37487c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
37497c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
37507c6b05d2SAlexander Motin 			    " ADC %d considered equal to ADC %d\n", nid2, nid1);
37517c6b05d2SAlexander Motin 		);
37527c6b05d2SAlexander Motin 		w1 = hdaa_widget_get(devinfo, w1->conns[0]);
37537c6b05d2SAlexander Motin 		w2 = hdaa_widget_get(devinfo, w2->conns[0]);
37547c6b05d2SAlexander Motin 		w2->bindas = w1->bindas;
37557c6b05d2SAlexander Motin 		w2->bindseqmask = w1->bindseqmask;
37567c6b05d2SAlexander Motin 	} else {
37577c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
37587c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
37597c6b05d2SAlexander Motin 			    " DAC %d considered equal to DAC %d\n", nid2, nid1);
37607c6b05d2SAlexander Motin 		);
37617c6b05d2SAlexander Motin 	}
37627c6b05d2SAlexander Motin 	for (i = 0; i < 16; i++) {
37637c6b05d2SAlexander Motin 		if (as->dacs[0][i] <= 0)
37647c6b05d2SAlexander Motin 			continue;
37657c6b05d2SAlexander Motin 		as->dacs[as->num_chans][i] = nid2;
37667c6b05d2SAlexander Motin 	}
37677c6b05d2SAlexander Motin 	as->num_chans++;
37687c6b05d2SAlexander Motin }
37697c6b05d2SAlexander Motin 
37707c6b05d2SAlexander Motin /*
37717c6b05d2SAlexander Motin  * Trace association path from input to ADC
37727c6b05d2SAlexander Motin  */
37737c6b05d2SAlexander Motin static int
hdaa_audio_trace_as_in(struct hdaa_devinfo * devinfo,int as)37747c6b05d2SAlexander Motin hdaa_audio_trace_as_in(struct hdaa_devinfo *devinfo, int as)
37757c6b05d2SAlexander Motin {
37767c6b05d2SAlexander Motin 	struct hdaa_audio_as *ases = devinfo->as;
37777c6b05d2SAlexander Motin 	struct hdaa_widget *w;
37787c6b05d2SAlexander Motin 	int i, j, k, length;
37797c6b05d2SAlexander Motin 
37807c6b05d2SAlexander Motin 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
37817c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, j);
37827c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
37837c6b05d2SAlexander Motin 			continue;
37847c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT)
37857c6b05d2SAlexander Motin 			continue;
37867c6b05d2SAlexander Motin 		if (w->bindas >= 0 && w->bindas != as)
37877c6b05d2SAlexander Motin 			continue;
37887c6b05d2SAlexander Motin 
37897c6b05d2SAlexander Motin 		/* Find next pin */
37907c6b05d2SAlexander Motin 		for (i = 0; i < 16; i++) {
37917c6b05d2SAlexander Motin 			if (ases[as].pins[i] == 0)
37927c6b05d2SAlexander Motin 				continue;
37937c6b05d2SAlexander Motin 
37947c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
37957c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
37967c6b05d2SAlexander Motin 				    " Tracing pin %d to ADC %d\n",
37977c6b05d2SAlexander Motin 				    ases[as].pins[i], j);
37987c6b05d2SAlexander Motin 			);
37997c6b05d2SAlexander Motin 			/* Trace this pin taking goal into account. */
38007c6b05d2SAlexander Motin 			if (hdaa_audio_trace_adc(devinfo, as, i,
38017c6b05d2SAlexander Motin 			    ases[as].pins[i], 1, 0, j, 0, &length, 0) == 0) {
38027c6b05d2SAlexander Motin 				/* If we failed - return to previous and redo it. */
38037c6b05d2SAlexander Motin 				HDA_BOOTVERBOSE(
38047c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
38057c6b05d2SAlexander Motin 					    " Unable to trace pin %d to ADC %d, undo traces\n",
38067c6b05d2SAlexander Motin 					    ases[as].pins[i], j);
38077c6b05d2SAlexander Motin 				);
38087c6b05d2SAlexander Motin 				hdaa_audio_undo_trace(devinfo, as, -1);
38097c6b05d2SAlexander Motin 				for (k = 0; k < 16; k++)
38107c6b05d2SAlexander Motin 					ases[as].dacs[0][k] = 0;
38117c6b05d2SAlexander Motin 				break;
38127c6b05d2SAlexander Motin 			}
38137c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
38147c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
38157c6b05d2SAlexander Motin 				    " Pin %d traced to ADC %d\n",
38167c6b05d2SAlexander Motin 				    ases[as].pins[i], j);
38177c6b05d2SAlexander Motin 			);
38187c6b05d2SAlexander Motin 			ases[as].dacs[0][i] = j;
38197c6b05d2SAlexander Motin 		}
38207c6b05d2SAlexander Motin 		if (i == 16)
38217c6b05d2SAlexander Motin 			return (1);
38227c6b05d2SAlexander Motin 	}
38237c6b05d2SAlexander Motin 	return (0);
38247c6b05d2SAlexander Motin }
38257c6b05d2SAlexander Motin 
38267c6b05d2SAlexander Motin /*
38277c6b05d2SAlexander Motin  * Trace association path from input to multiple ADCs
38287c6b05d2SAlexander Motin  */
38297c6b05d2SAlexander Motin static int
hdaa_audio_trace_as_in_mch(struct hdaa_devinfo * devinfo,int as,int seq)38307c6b05d2SAlexander Motin hdaa_audio_trace_as_in_mch(struct hdaa_devinfo *devinfo, int as, int seq)
38317c6b05d2SAlexander Motin {
38327c6b05d2SAlexander Motin 	struct hdaa_audio_as *ases = devinfo->as;
38337c6b05d2SAlexander Motin 	int i, length;
38347c6b05d2SAlexander Motin 	nid_t min, res;
38357c6b05d2SAlexander Motin 
38367c6b05d2SAlexander Motin 	/* Find next pin */
38377c6b05d2SAlexander Motin 	for (i = seq; i < 16 && ases[as].pins[i] == 0; i++)
38387c6b05d2SAlexander Motin 		;
38397c6b05d2SAlexander Motin 	/* Check if there is no any left. If so - we succeeded. */
38407c6b05d2SAlexander Motin 	if (i == 16)
38417c6b05d2SAlexander Motin 		return (1);
38427c6b05d2SAlexander Motin 
38437c6b05d2SAlexander Motin 	min = 0;
38447c6b05d2SAlexander Motin 	do {
38457c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
38467c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
38477c6b05d2SAlexander Motin 			    " Tracing pin %d with min nid %d",
38487c6b05d2SAlexander Motin 			    ases[as].pins[i], min);
38497c6b05d2SAlexander Motin 			printf("\n");
38507c6b05d2SAlexander Motin 		);
38517c6b05d2SAlexander Motin 		/* Trace this pin taking min nid into account. */
38527c6b05d2SAlexander Motin 		res = hdaa_audio_trace_adc(devinfo, as, i,
38537c6b05d2SAlexander Motin 		    ases[as].pins[i], 0, min, 0, 0, &length, 0);
38547c6b05d2SAlexander Motin 		if (res == 0) {
38557c6b05d2SAlexander Motin 			/* If we failed - return to previous and redo it. */
38567c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
38577c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
38587c6b05d2SAlexander Motin 				    " Unable to trace pin %d seq %d with min "
38597c6b05d2SAlexander Motin 				    "nid %d",
38607c6b05d2SAlexander Motin 				    ases[as].pins[i], i, min);
38617c6b05d2SAlexander Motin 				printf("\n");
38627c6b05d2SAlexander Motin 			);
38637c6b05d2SAlexander Motin 			return (0);
38647c6b05d2SAlexander Motin 		}
38657c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
38667c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
38677c6b05d2SAlexander Motin 			    " Pin %d traced to ADC %d\n",
38687c6b05d2SAlexander Motin 			    ases[as].pins[i], res);
38697c6b05d2SAlexander Motin 		);
38707c6b05d2SAlexander Motin 		/* Trace again to mark the path */
38717c6b05d2SAlexander Motin 		hdaa_audio_trace_adc(devinfo, as, i,
38727c6b05d2SAlexander Motin 		    ases[as].pins[i], 0, min, res, 0, &length, length);
38737c6b05d2SAlexander Motin 		ases[as].dacs[0][i] = res;
38747c6b05d2SAlexander Motin 		/* We succeeded, so call next. */
38757c6b05d2SAlexander Motin 		if (hdaa_audio_trace_as_in_mch(devinfo, as, i + 1))
38767c6b05d2SAlexander Motin 			return (1);
38777c6b05d2SAlexander Motin 		/* If next failed, we should retry with next min */
38787c6b05d2SAlexander Motin 		hdaa_audio_undo_trace(devinfo, as, i);
38797c6b05d2SAlexander Motin 		ases[as].dacs[0][i] = 0;
38807c6b05d2SAlexander Motin 		min = res + 1;
38817c6b05d2SAlexander Motin 	} while (1);
38827c6b05d2SAlexander Motin }
38837c6b05d2SAlexander Motin 
38847c6b05d2SAlexander Motin /*
38857c6b05d2SAlexander Motin  * Trace input monitor path from mixer to output association.
38867c6b05d2SAlexander Motin  */
38877c6b05d2SAlexander Motin static int
hdaa_audio_trace_to_out(struct hdaa_devinfo * devinfo,nid_t nid,int depth)38887c6b05d2SAlexander Motin hdaa_audio_trace_to_out(struct hdaa_devinfo *devinfo, nid_t nid, int depth)
38897c6b05d2SAlexander Motin {
38907c6b05d2SAlexander Motin 	struct hdaa_audio_as *ases = devinfo->as;
38917c6b05d2SAlexander Motin 	struct hdaa_widget *w, *wc;
38927c6b05d2SAlexander Motin 	int i, j;
38937c6b05d2SAlexander Motin 	nid_t res = 0;
38947c6b05d2SAlexander Motin 
38957c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
38967c6b05d2SAlexander Motin 		return (0);
38977c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
38987c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
38997c6b05d2SAlexander Motin 		return (0);
39007c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
39017c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
39027c6b05d2SAlexander Motin 		    " %*stracing via nid %d\n",
39037c6b05d2SAlexander Motin 			depth + 1, "", w->nid);
39047c6b05d2SAlexander Motin 	);
39057c6b05d2SAlexander Motin 	/* Use only unused widgets */
39067c6b05d2SAlexander Motin 	if (depth > 0 && w->bindas != -1) {
39077c6b05d2SAlexander Motin 		if (w->bindas < 0 || ases[w->bindas].dir == HDAA_CTL_OUT) {
39087c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
39097c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
39107c6b05d2SAlexander Motin 				    " %*snid %d found output association %d\n",
39117c6b05d2SAlexander Motin 					depth + 1, "", w->nid, w->bindas);
39127c6b05d2SAlexander Motin 			);
39137c6b05d2SAlexander Motin 			if (w->bindas >= 0)
39147c6b05d2SAlexander Motin 				w->pflags |= HDAA_ADC_MONITOR;
39157c6b05d2SAlexander Motin 			return (1);
39167c6b05d2SAlexander Motin 		} else {
39177c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
39187c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
39197c6b05d2SAlexander Motin 				    " %*snid %d busy by input association %d\n",
39207c6b05d2SAlexander Motin 					depth + 1, "", w->nid, w->bindas);
39217c6b05d2SAlexander Motin 			);
39227c6b05d2SAlexander Motin 			return (0);
39237c6b05d2SAlexander Motin 		}
39247c6b05d2SAlexander Motin 	}
39257c6b05d2SAlexander Motin 
39267c6b05d2SAlexander Motin 	switch (w->type) {
39277c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT:
39287c6b05d2SAlexander Motin 		/* Do not traverse input. AD1988 has digital monitor
39297c6b05d2SAlexander Motin 		for which we are not ready. */
39307c6b05d2SAlexander Motin 		break;
39317c6b05d2SAlexander Motin 	case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
39327c6b05d2SAlexander Motin 		if (depth > 0)
39337c6b05d2SAlexander Motin 			break;
39347c6b05d2SAlexander Motin 		/* Fall */
39357c6b05d2SAlexander Motin 	default:
39367c6b05d2SAlexander Motin 		/* Try to find reachable ADCs with specified nid. */
39377c6b05d2SAlexander Motin 		for (j = devinfo->startnode; j < devinfo->endnode; j++) {
39387c6b05d2SAlexander Motin 			wc = hdaa_widget_get(devinfo, j);
39397c6b05d2SAlexander Motin 			if (wc == NULL || wc->enable == 0)
39407c6b05d2SAlexander Motin 				continue;
39417c6b05d2SAlexander Motin 			for (i = 0; i < wc->nconns; i++) {
39427c6b05d2SAlexander Motin 				if (wc->connsenable[i] == 0)
39437c6b05d2SAlexander Motin 					continue;
39447c6b05d2SAlexander Motin 				if (wc->conns[i] != nid)
39457c6b05d2SAlexander Motin 					continue;
39467c6b05d2SAlexander Motin 				if (hdaa_audio_trace_to_out(devinfo,
39477c6b05d2SAlexander Motin 				    j, depth + 1) != 0) {
39487c6b05d2SAlexander Motin 					res = 1;
39497c6b05d2SAlexander Motin 					if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR &&
39507c6b05d2SAlexander Motin 					    wc->selconn == -1)
39517c6b05d2SAlexander Motin 						wc->selconn = i;
39527c6b05d2SAlexander Motin 				}
39537c6b05d2SAlexander Motin 			}
39547c6b05d2SAlexander Motin 		}
39557c6b05d2SAlexander Motin 		break;
39567c6b05d2SAlexander Motin 	}
39577c6b05d2SAlexander Motin 	if (res && w->bindas == -1)
39587c6b05d2SAlexander Motin 		w->bindas = -2;
39597c6b05d2SAlexander Motin 
39607c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
39617c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
39627c6b05d2SAlexander Motin 		    " %*snid %d returned %d\n",
39637c6b05d2SAlexander Motin 			depth + 1, "", w->nid, res);
39647c6b05d2SAlexander Motin 	);
39657c6b05d2SAlexander Motin 	return (res);
39667c6b05d2SAlexander Motin }
39677c6b05d2SAlexander Motin 
39687c6b05d2SAlexander Motin /*
39697c6b05d2SAlexander Motin  * Trace extra associations (beeper, monitor)
39707c6b05d2SAlexander Motin  */
39717c6b05d2SAlexander Motin static void
hdaa_audio_trace_as_extra(struct hdaa_devinfo * devinfo)39727c6b05d2SAlexander Motin hdaa_audio_trace_as_extra(struct hdaa_devinfo *devinfo)
39737c6b05d2SAlexander Motin {
39747c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
39757c6b05d2SAlexander Motin 	struct hdaa_widget *w;
39767c6b05d2SAlexander Motin 	int j;
39777c6b05d2SAlexander Motin 
39787c6b05d2SAlexander Motin 	/* Input monitor */
39797c6b05d2SAlexander Motin 	/* Find mixer associated with input, but supplying signal
39807c6b05d2SAlexander Motin 	   for output associations. Hope it will be input monitor. */
39817c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
39827c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
39837c6b05d2SAlexander Motin 		    "Tracing input monitor\n");
39847c6b05d2SAlexander Motin 	);
39857c6b05d2SAlexander Motin 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
39867c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, j);
39877c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
39887c6b05d2SAlexander Motin 			continue;
39897c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
39907c6b05d2SAlexander Motin 			continue;
39917c6b05d2SAlexander Motin 		if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN)
39927c6b05d2SAlexander Motin 			continue;
39937c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
39947c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
39957c6b05d2SAlexander Motin 			    " Tracing nid %d to out\n",
39967c6b05d2SAlexander Motin 			    j);
39977c6b05d2SAlexander Motin 		);
39987c6b05d2SAlexander Motin 		if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) {
39997c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
40007c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
40017c6b05d2SAlexander Motin 				    " nid %d is input monitor\n",
40027c6b05d2SAlexander Motin 					w->nid);
40037c6b05d2SAlexander Motin 			);
40047c6b05d2SAlexander Motin 			w->ossdev = SOUND_MIXER_IMIX;
40057c6b05d2SAlexander Motin 		}
40067c6b05d2SAlexander Motin 	}
40077c6b05d2SAlexander Motin 
40087c6b05d2SAlexander Motin 	/* Other inputs monitor */
40097c6b05d2SAlexander Motin 	/* Find input pins supplying signal for output associations.
40107c6b05d2SAlexander Motin 	   Hope it will be input monitoring. */
40117c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
40127c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
40137c6b05d2SAlexander Motin 		    "Tracing other input monitors\n");
40147c6b05d2SAlexander Motin 	);
40157c6b05d2SAlexander Motin 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
40167c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, j);
40177c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
40187c6b05d2SAlexander Motin 			continue;
40197c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
40207c6b05d2SAlexander Motin 			continue;
40217c6b05d2SAlexander Motin 		if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN)
40227c6b05d2SAlexander Motin 			continue;
40237c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
40247c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
40257c6b05d2SAlexander Motin 			    " Tracing nid %d to out\n",
40267c6b05d2SAlexander Motin 			    j);
40277c6b05d2SAlexander Motin 		);
40287c6b05d2SAlexander Motin 		if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) {
40297c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
40307c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
40317c6b05d2SAlexander Motin 				    " nid %d is input monitor\n",
40327c6b05d2SAlexander Motin 					w->nid);
40337c6b05d2SAlexander Motin 			);
40347c6b05d2SAlexander Motin 		}
40357c6b05d2SAlexander Motin 	}
40367c6b05d2SAlexander Motin 
40377c6b05d2SAlexander Motin 	/* Beeper */
40387c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
40397c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
40407c6b05d2SAlexander Motin 		    "Tracing beeper\n");
40417c6b05d2SAlexander Motin 	);
40427c6b05d2SAlexander Motin 	for (j = devinfo->startnode; j < devinfo->endnode; j++) {
40437c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, j);
40447c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
40457c6b05d2SAlexander Motin 			continue;
40467c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET)
40477c6b05d2SAlexander Motin 			continue;
40487c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
40497c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
40507c6b05d2SAlexander Motin 			    " Tracing nid %d to out\n",
40517c6b05d2SAlexander Motin 			    j);
40527c6b05d2SAlexander Motin 		);
40537c6b05d2SAlexander Motin 		if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) {
40547c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
40557c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
40567c6b05d2SAlexander Motin 				    " nid %d traced to out\n",
40577c6b05d2SAlexander Motin 				    j);
40587c6b05d2SAlexander Motin 			);
40597c6b05d2SAlexander Motin 		}
40607c6b05d2SAlexander Motin 		w->bindas = -2;
40617c6b05d2SAlexander Motin 	}
40627c6b05d2SAlexander Motin }
40637c6b05d2SAlexander Motin 
40647c6b05d2SAlexander Motin /*
40657c6b05d2SAlexander Motin  * Bind assotiations to PCM channels
40667c6b05d2SAlexander Motin  */
40677c6b05d2SAlexander Motin static void
hdaa_audio_bind_as(struct hdaa_devinfo * devinfo)40687c6b05d2SAlexander Motin hdaa_audio_bind_as(struct hdaa_devinfo *devinfo)
40697c6b05d2SAlexander Motin {
40707c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
40717c6b05d2SAlexander Motin 	int i, j, cnt = 0, free;
40727c6b05d2SAlexander Motin 
40737c6b05d2SAlexander Motin 	for (j = 0; j < devinfo->ascnt; j++) {
40747c6b05d2SAlexander Motin 		if (as[j].enable)
40757c6b05d2SAlexander Motin 			cnt += as[j].num_chans;
40767c6b05d2SAlexander Motin 	}
40777c6b05d2SAlexander Motin 	if (devinfo->num_chans == 0) {
40783cc01caaSChristos Margiolis 		devinfo->chans = malloc(sizeof(struct hdaa_chan) * cnt,
40797c6b05d2SAlexander Motin 		    M_HDAA, M_ZERO | M_NOWAIT);
40807c6b05d2SAlexander Motin 		if (devinfo->chans == NULL) {
40817c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
40827c6b05d2SAlexander Motin 			    "Channels memory allocation failed!\n");
40837c6b05d2SAlexander Motin 			return;
40847c6b05d2SAlexander Motin 		}
40857c6b05d2SAlexander Motin 	} else {
40867c6b05d2SAlexander Motin 		devinfo->chans = (struct hdaa_chan *)realloc(devinfo->chans,
40877c6b05d2SAlexander Motin 		    sizeof(struct hdaa_chan) * (devinfo->num_chans + cnt),
40887c6b05d2SAlexander Motin 		    M_HDAA, M_ZERO | M_NOWAIT);
40897c6b05d2SAlexander Motin 		if (devinfo->chans == NULL) {
40907c6b05d2SAlexander Motin 			devinfo->num_chans = 0;
40917c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
40927c6b05d2SAlexander Motin 			    "Channels memory allocation failed!\n");
40937c6b05d2SAlexander Motin 			return;
40947c6b05d2SAlexander Motin 		}
40957c6b05d2SAlexander Motin 		/* Fixup relative pointers after realloc */
40967c6b05d2SAlexander Motin 		for (j = 0; j < devinfo->num_chans; j++)
40977c6b05d2SAlexander Motin 			devinfo->chans[j].caps.fmtlist = devinfo->chans[j].fmtlist;
40987c6b05d2SAlexander Motin 	}
40997c6b05d2SAlexander Motin 	free = devinfo->num_chans;
41007c6b05d2SAlexander Motin 	devinfo->num_chans += cnt;
41017c6b05d2SAlexander Motin 
41027c6b05d2SAlexander Motin 	for (j = free; j < free + cnt; j++) {
41037c6b05d2SAlexander Motin 		devinfo->chans[j].devinfo = devinfo;
41047c6b05d2SAlexander Motin 		devinfo->chans[j].as = -1;
41057c6b05d2SAlexander Motin 	}
41067c6b05d2SAlexander Motin 
41077c6b05d2SAlexander Motin 	/* Assign associations in order of their numbers, */
41087c6b05d2SAlexander Motin 	for (j = 0; j < devinfo->ascnt; j++) {
41097c6b05d2SAlexander Motin 		if (as[j].enable == 0)
41107c6b05d2SAlexander Motin 			continue;
41117c6b05d2SAlexander Motin 		for (i = 0; i < as[j].num_chans; i++) {
41127c6b05d2SAlexander Motin 			devinfo->chans[free].as = j;
41137c6b05d2SAlexander Motin 			devinfo->chans[free].asindex = i;
41147c6b05d2SAlexander Motin 			devinfo->chans[free].dir =
41157c6b05d2SAlexander Motin 			    (as[j].dir == HDAA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY;
41167c6b05d2SAlexander Motin 			hdaa_pcmchannel_setup(&devinfo->chans[free]);
41177c6b05d2SAlexander Motin 			as[j].chans[i] = free;
41187c6b05d2SAlexander Motin 			free++;
41197c6b05d2SAlexander Motin 		}
41207c6b05d2SAlexander Motin 	}
41217c6b05d2SAlexander Motin }
41227c6b05d2SAlexander Motin 
41237c6b05d2SAlexander Motin static void
hdaa_audio_disable_nonaudio(struct hdaa_devinfo * devinfo)41247c6b05d2SAlexander Motin hdaa_audio_disable_nonaudio(struct hdaa_devinfo *devinfo)
41257c6b05d2SAlexander Motin {
41267c6b05d2SAlexander Motin 	struct hdaa_widget *w;
41277c6b05d2SAlexander Motin 	int i;
41287c6b05d2SAlexander Motin 
41297c6b05d2SAlexander Motin 	/* Disable power and volume widgets. */
41307c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
41317c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
41327c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
41337c6b05d2SAlexander Motin 			continue;
41347c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET ||
41357c6b05d2SAlexander Motin 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) {
41367c6b05d2SAlexander Motin 			w->enable = 0;
41377c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
41387c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
41397c6b05d2SAlexander Motin 				    " Disabling nid %d due to it's"
41407c6b05d2SAlexander Motin 				    " non-audio type.\n",
41417c6b05d2SAlexander Motin 				    w->nid);
41427c6b05d2SAlexander Motin 			);
41437c6b05d2SAlexander Motin 		}
41447c6b05d2SAlexander Motin 	}
41457c6b05d2SAlexander Motin }
41467c6b05d2SAlexander Motin 
41477c6b05d2SAlexander Motin static void
hdaa_audio_disable_useless(struct hdaa_devinfo * devinfo)41487c6b05d2SAlexander Motin hdaa_audio_disable_useless(struct hdaa_devinfo *devinfo)
41497c6b05d2SAlexander Motin {
41507c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
41517c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
41527c6b05d2SAlexander Motin 	int done, found, i, j, k;
41537c6b05d2SAlexander Motin 
41547c6b05d2SAlexander Motin 	/* Disable useless pins. */
41557c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
41567c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
41577c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
41587c6b05d2SAlexander Motin 			continue;
41597c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
41607c6b05d2SAlexander Motin 			if ((w->wclass.pin.config &
41617c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) ==
41627c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) {
41637c6b05d2SAlexander Motin 				w->enable = 0;
41647c6b05d2SAlexander Motin 				HDA_BOOTHVERBOSE(
41657c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
41667c6b05d2SAlexander Motin 					    " Disabling pin nid %d due"
41677c6b05d2SAlexander Motin 					    " to None connectivity.\n",
41687c6b05d2SAlexander Motin 					    w->nid);
41697c6b05d2SAlexander Motin 				);
41707c6b05d2SAlexander Motin 			} else if ((w->wclass.pin.config &
41717c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) {
41727c6b05d2SAlexander Motin 				w->enable = 0;
41737c6b05d2SAlexander Motin 				HDA_BOOTHVERBOSE(
41747c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
41757c6b05d2SAlexander Motin 					    " Disabling unassociated"
41767c6b05d2SAlexander Motin 					    " pin nid %d.\n",
41777c6b05d2SAlexander Motin 					    w->nid);
41787c6b05d2SAlexander Motin 				);
41797c6b05d2SAlexander Motin 			}
41807c6b05d2SAlexander Motin 		}
41817c6b05d2SAlexander Motin 	}
41827c6b05d2SAlexander Motin 	do {
41837c6b05d2SAlexander Motin 		done = 1;
41847c6b05d2SAlexander Motin 		/* Disable and mute controls for disabled widgets. */
41857c6b05d2SAlexander Motin 		i = 0;
41867c6b05d2SAlexander Motin 		while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
41877c6b05d2SAlexander Motin 			if (ctl->enable == 0)
41887c6b05d2SAlexander Motin 				continue;
41897c6b05d2SAlexander Motin 			if (ctl->widget->enable == 0 ||
41907c6b05d2SAlexander Motin 			    (ctl->childwidget != NULL &&
41917c6b05d2SAlexander Motin 			    ctl->childwidget->enable == 0)) {
41927c6b05d2SAlexander Motin 				ctl->forcemute = 1;
41937c6b05d2SAlexander Motin 				ctl->muted = HDAA_AMP_MUTE_ALL;
41947c6b05d2SAlexander Motin 				ctl->left = 0;
41957c6b05d2SAlexander Motin 				ctl->right = 0;
41967c6b05d2SAlexander Motin 				ctl->enable = 0;
41977c6b05d2SAlexander Motin 				if (ctl->ndir == HDAA_CTL_IN)
41987c6b05d2SAlexander Motin 					ctl->widget->connsenable[ctl->index] = 0;
41997c6b05d2SAlexander Motin 				done = 0;
42007c6b05d2SAlexander Motin 				HDA_BOOTHVERBOSE(
42017c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
42027c6b05d2SAlexander Motin 					    " Disabling ctl %d nid %d cnid %d due"
42037c6b05d2SAlexander Motin 					    " to disabled widget.\n", i,
42047c6b05d2SAlexander Motin 					    ctl->widget->nid,
42057c6b05d2SAlexander Motin 					    (ctl->childwidget != NULL)?
42067c6b05d2SAlexander Motin 					    ctl->childwidget->nid:-1);
42077c6b05d2SAlexander Motin 				);
42087c6b05d2SAlexander Motin 			}
42097c6b05d2SAlexander Motin 		}
42107c6b05d2SAlexander Motin 		/* Disable useless widgets. */
42117c6b05d2SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
42127c6b05d2SAlexander Motin 			w = hdaa_widget_get(devinfo, i);
42137c6b05d2SAlexander Motin 			if (w == NULL || w->enable == 0)
42147c6b05d2SAlexander Motin 				continue;
42157c6b05d2SAlexander Motin 			/* Disable inputs with disabled child widgets. */
42167c6b05d2SAlexander Motin 			for (j = 0; j < w->nconns; j++) {
42177c6b05d2SAlexander Motin 				if (w->connsenable[j]) {
42187c6b05d2SAlexander Motin 					cw = hdaa_widget_get(devinfo, w->conns[j]);
42197c6b05d2SAlexander Motin 					if (cw == NULL || cw->enable == 0) {
42207c6b05d2SAlexander Motin 						w->connsenable[j] = 0;
42217c6b05d2SAlexander Motin 						HDA_BOOTHVERBOSE(
42227c6b05d2SAlexander Motin 							device_printf(devinfo->dev,
42237c6b05d2SAlexander Motin 							    " Disabling nid %d connection %d due"
42247c6b05d2SAlexander Motin 							    " to disabled child widget.\n",
42257c6b05d2SAlexander Motin 							    i, j);
42267c6b05d2SAlexander Motin 						);
42277c6b05d2SAlexander Motin 					}
42287c6b05d2SAlexander Motin 				}
42297c6b05d2SAlexander Motin 			}
42307c6b05d2SAlexander Motin 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR &&
42317c6b05d2SAlexander Motin 			    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
42327c6b05d2SAlexander Motin 				continue;
42337c6b05d2SAlexander Motin 			/* Disable mixers and selectors without inputs. */
42347c6b05d2SAlexander Motin 			found = 0;
42357c6b05d2SAlexander Motin 			for (j = 0; j < w->nconns; j++) {
42367c6b05d2SAlexander Motin 				if (w->connsenable[j]) {
42377c6b05d2SAlexander Motin 					found = 1;
42387c6b05d2SAlexander Motin 					break;
42397c6b05d2SAlexander Motin 				}
42407c6b05d2SAlexander Motin 			}
42417c6b05d2SAlexander Motin 			if (found == 0) {
42427c6b05d2SAlexander Motin 				w->enable = 0;
42437c6b05d2SAlexander Motin 				done = 0;
42447c6b05d2SAlexander Motin 				HDA_BOOTHVERBOSE(
42457c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
42467c6b05d2SAlexander Motin 					    " Disabling nid %d due to all it's"
42477c6b05d2SAlexander Motin 					    " inputs disabled.\n", w->nid);
42487c6b05d2SAlexander Motin 				);
42497c6b05d2SAlexander Motin 			}
42507c6b05d2SAlexander Motin 			/* Disable nodes without consumers. */
42517c6b05d2SAlexander Motin 			if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR &&
42527c6b05d2SAlexander Motin 			    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
42537c6b05d2SAlexander Motin 				continue;
42547c6b05d2SAlexander Motin 			found = 0;
42557c6b05d2SAlexander Motin 			for (k = devinfo->startnode; k < devinfo->endnode; k++) {
42567c6b05d2SAlexander Motin 				cw = hdaa_widget_get(devinfo, k);
42577c6b05d2SAlexander Motin 				if (cw == NULL || cw->enable == 0)
42587c6b05d2SAlexander Motin 					continue;
42597c6b05d2SAlexander Motin 				for (j = 0; j < cw->nconns; j++) {
42607c6b05d2SAlexander Motin 					if (cw->connsenable[j] && cw->conns[j] == i) {
42617c6b05d2SAlexander Motin 						found = 1;
42627c6b05d2SAlexander Motin 						break;
42637c6b05d2SAlexander Motin 					}
42647c6b05d2SAlexander Motin 				}
42657c6b05d2SAlexander Motin 			}
42667c6b05d2SAlexander Motin 			if (found == 0) {
42677c6b05d2SAlexander Motin 				w->enable = 0;
42687c6b05d2SAlexander Motin 				done = 0;
42697c6b05d2SAlexander Motin 				HDA_BOOTHVERBOSE(
42707c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
42717c6b05d2SAlexander Motin 					    " Disabling nid %d due to all it's"
42727c6b05d2SAlexander Motin 					    " consumers disabled.\n", w->nid);
42737c6b05d2SAlexander Motin 				);
42747c6b05d2SAlexander Motin 			}
42757c6b05d2SAlexander Motin 		}
42767c6b05d2SAlexander Motin 	} while (done == 0);
42777c6b05d2SAlexander Motin 
42787c6b05d2SAlexander Motin }
42797c6b05d2SAlexander Motin 
42807c6b05d2SAlexander Motin static void
hdaa_audio_disable_unas(struct hdaa_devinfo * devinfo)42817c6b05d2SAlexander Motin hdaa_audio_disable_unas(struct hdaa_devinfo *devinfo)
42827c6b05d2SAlexander Motin {
42837c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
42847c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
42857c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
42867c6b05d2SAlexander Motin 	int i, j, k;
42877c6b05d2SAlexander Motin 
42887c6b05d2SAlexander Motin 	/* Disable unassosiated widgets. */
42897c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
42907c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
42917c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
42927c6b05d2SAlexander Motin 			continue;
42937c6b05d2SAlexander Motin 		if (w->bindas == -1) {
42947c6b05d2SAlexander Motin 			w->enable = 0;
42957c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
42967c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
42977c6b05d2SAlexander Motin 				    " Disabling unassociated nid %d.\n",
42987c6b05d2SAlexander Motin 				    w->nid);
42997c6b05d2SAlexander Motin 			);
43007c6b05d2SAlexander Motin 		}
43017c6b05d2SAlexander Motin 	}
43027c6b05d2SAlexander Motin 	/* Disable input connections on input pin and
43037c6b05d2SAlexander Motin 	 * output on output. */
43047c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
43057c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
43067c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
43077c6b05d2SAlexander Motin 			continue;
43087c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
43097c6b05d2SAlexander Motin 			continue;
43107c6b05d2SAlexander Motin 		if (w->bindas < 0)
43117c6b05d2SAlexander Motin 			continue;
43127c6b05d2SAlexander Motin 		if (as[w->bindas].dir == HDAA_CTL_IN) {
43137c6b05d2SAlexander Motin 			for (j = 0; j < w->nconns; j++) {
43147c6b05d2SAlexander Motin 				if (w->connsenable[j] == 0)
43157c6b05d2SAlexander Motin 					continue;
43167c6b05d2SAlexander Motin 				w->connsenable[j] = 0;
43177c6b05d2SAlexander Motin 				HDA_BOOTHVERBOSE(
43187c6b05d2SAlexander Motin 					device_printf(devinfo->dev,
43197c6b05d2SAlexander Motin 					    " Disabling connection to input pin "
43207c6b05d2SAlexander Motin 					    "nid %d conn %d.\n",
43217c6b05d2SAlexander Motin 					    i, j);
43227c6b05d2SAlexander Motin 				);
43237c6b05d2SAlexander Motin 			}
43247c6b05d2SAlexander Motin 			ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
43257c6b05d2SAlexander Motin 			    HDAA_CTL_IN, -1, 1);
43267c6b05d2SAlexander Motin 			if (ctl && ctl->enable) {
43277c6b05d2SAlexander Motin 				ctl->forcemute = 1;
43287c6b05d2SAlexander Motin 				ctl->muted = HDAA_AMP_MUTE_ALL;
43297c6b05d2SAlexander Motin 				ctl->left = 0;
43307c6b05d2SAlexander Motin 				ctl->right = 0;
43317c6b05d2SAlexander Motin 				ctl->enable = 0;
43327c6b05d2SAlexander Motin 			}
43337c6b05d2SAlexander Motin 		} else {
43347c6b05d2SAlexander Motin 			ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
43357c6b05d2SAlexander Motin 			    HDAA_CTL_OUT, -1, 1);
43367c6b05d2SAlexander Motin 			if (ctl && ctl->enable) {
43377c6b05d2SAlexander Motin 				ctl->forcemute = 1;
43387c6b05d2SAlexander Motin 				ctl->muted = HDAA_AMP_MUTE_ALL;
43397c6b05d2SAlexander Motin 				ctl->left = 0;
43407c6b05d2SAlexander Motin 				ctl->right = 0;
43417c6b05d2SAlexander Motin 				ctl->enable = 0;
43427c6b05d2SAlexander Motin 			}
43437c6b05d2SAlexander Motin 			for (k = devinfo->startnode; k < devinfo->endnode; k++) {
43447c6b05d2SAlexander Motin 				cw = hdaa_widget_get(devinfo, k);
43457c6b05d2SAlexander Motin 				if (cw == NULL || cw->enable == 0)
43467c6b05d2SAlexander Motin 					continue;
43477c6b05d2SAlexander Motin 				for (j = 0; j < cw->nconns; j++) {
43487c6b05d2SAlexander Motin 					if (cw->connsenable[j] && cw->conns[j] == i) {
43497c6b05d2SAlexander Motin 						cw->connsenable[j] = 0;
43507c6b05d2SAlexander Motin 						HDA_BOOTHVERBOSE(
43517c6b05d2SAlexander Motin 							device_printf(devinfo->dev,
43527c6b05d2SAlexander Motin 							    " Disabling connection from output pin "
43537c6b05d2SAlexander Motin 							    "nid %d conn %d cnid %d.\n",
43547c6b05d2SAlexander Motin 							    k, j, i);
43557c6b05d2SAlexander Motin 						);
43567c6b05d2SAlexander Motin 						if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
43577c6b05d2SAlexander Motin 						    cw->nconns > 1)
43587c6b05d2SAlexander Motin 							continue;
43597c6b05d2SAlexander Motin 						ctl = hdaa_audio_ctl_amp_get(devinfo, k,
43607c6b05d2SAlexander Motin 					    HDAA_CTL_IN, j, 1);
43617c6b05d2SAlexander Motin 						if (ctl && ctl->enable) {
43627c6b05d2SAlexander Motin 							ctl->forcemute = 1;
43637c6b05d2SAlexander Motin 							ctl->muted = HDAA_AMP_MUTE_ALL;
43647c6b05d2SAlexander Motin 							ctl->left = 0;
43657c6b05d2SAlexander Motin 							ctl->right = 0;
43667c6b05d2SAlexander Motin 							ctl->enable = 0;
43677c6b05d2SAlexander Motin 						}
43687c6b05d2SAlexander Motin 					}
43697c6b05d2SAlexander Motin 				}
43707c6b05d2SAlexander Motin 			}
43717c6b05d2SAlexander Motin 		}
43727c6b05d2SAlexander Motin 	}
43737c6b05d2SAlexander Motin }
43747c6b05d2SAlexander Motin 
43757c6b05d2SAlexander Motin static void
hdaa_audio_disable_notselected(struct hdaa_devinfo * devinfo)43767c6b05d2SAlexander Motin hdaa_audio_disable_notselected(struct hdaa_devinfo *devinfo)
43777c6b05d2SAlexander Motin {
43787c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
43797c6b05d2SAlexander Motin 	struct hdaa_widget *w;
43807c6b05d2SAlexander Motin 	int i, j;
43817c6b05d2SAlexander Motin 
43827c6b05d2SAlexander Motin 	/* On playback path we can safely disable all unseleted inputs. */
43837c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
43847c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
43857c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
43867c6b05d2SAlexander Motin 			continue;
43877c6b05d2SAlexander Motin 		if (w->nconns <= 1)
43887c6b05d2SAlexander Motin 			continue;
43897c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
43907c6b05d2SAlexander Motin 			continue;
43917c6b05d2SAlexander Motin 		if (w->bindas < 0 || as[w->bindas].dir == HDAA_CTL_IN)
43927c6b05d2SAlexander Motin 			continue;
43937c6b05d2SAlexander Motin 		for (j = 0; j < w->nconns; j++) {
43947c6b05d2SAlexander Motin 			if (w->connsenable[j] == 0)
43957c6b05d2SAlexander Motin 				continue;
43967c6b05d2SAlexander Motin 			if (w->selconn < 0 || w->selconn == j)
43977c6b05d2SAlexander Motin 				continue;
43987c6b05d2SAlexander Motin 			w->connsenable[j] = 0;
43997c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
44007c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
44017c6b05d2SAlexander Motin 				    " Disabling unselected connection "
44027c6b05d2SAlexander Motin 				    "nid %d conn %d.\n",
44037c6b05d2SAlexander Motin 				    i, j);
44047c6b05d2SAlexander Motin 			);
44057c6b05d2SAlexander Motin 		}
44067c6b05d2SAlexander Motin 	}
44077c6b05d2SAlexander Motin }
44087c6b05d2SAlexander Motin 
44097c6b05d2SAlexander Motin static void
hdaa_audio_disable_crossas(struct hdaa_devinfo * devinfo)44107c6b05d2SAlexander Motin hdaa_audio_disable_crossas(struct hdaa_devinfo *devinfo)
44117c6b05d2SAlexander Motin {
44127c6b05d2SAlexander Motin 	struct hdaa_audio_as *ases = devinfo->as;
44137c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
44147c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
44157c6b05d2SAlexander Motin 	int i, j;
44167c6b05d2SAlexander Motin 
44177c6b05d2SAlexander Motin 	/* Disable crossassociatement and unwanted crosschannel connections. */
44187c6b05d2SAlexander Motin 	/* ... using selectors */
44197c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
44207c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
44217c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
44227c6b05d2SAlexander Motin 			continue;
44237c6b05d2SAlexander Motin 		if (w->nconns <= 1)
44247c6b05d2SAlexander Motin 			continue;
44257c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
44267c6b05d2SAlexander Motin 			continue;
44277c6b05d2SAlexander Motin 		/* Allow any -> mix */
44287c6b05d2SAlexander Motin 		if (w->bindas == -2)
44297c6b05d2SAlexander Motin 			continue;
44307c6b05d2SAlexander Motin 		for (j = 0; j < w->nconns; j++) {
44317c6b05d2SAlexander Motin 			if (w->connsenable[j] == 0)
44327c6b05d2SAlexander Motin 				continue;
44337c6b05d2SAlexander Motin 			cw = hdaa_widget_get(devinfo, w->conns[j]);
44347c6b05d2SAlexander Motin 			if (cw == NULL || w->enable == 0)
44357c6b05d2SAlexander Motin 				continue;
44367c6b05d2SAlexander Motin 			/* Allow mix -> out. */
44377c6b05d2SAlexander Motin 			if (cw->bindas == -2 && w->bindas >= 0 &&
44387c6b05d2SAlexander Motin 			    ases[w->bindas].dir == HDAA_CTL_OUT)
44397c6b05d2SAlexander Motin 				continue;
44407c6b05d2SAlexander Motin 			/* Allow mix -> mixed-in. */
44417c6b05d2SAlexander Motin 			if (cw->bindas == -2 && w->bindas >= 0 &&
44427c6b05d2SAlexander Motin 			    ases[w->bindas].mixed)
44437c6b05d2SAlexander Motin 				continue;
44447c6b05d2SAlexander Motin 			/* Allow in -> mix. */
44457c6b05d2SAlexander Motin 			if ((w->pflags & HDAA_ADC_MONITOR) &&
44467c6b05d2SAlexander Motin 			     cw->bindas >= 0 &&
44477c6b05d2SAlexander Motin 			     ases[cw->bindas].dir == HDAA_CTL_IN)
44487c6b05d2SAlexander Motin 				continue;
44497c6b05d2SAlexander Motin 			/* Allow if have common as/seqs. */
44507c6b05d2SAlexander Motin 			if (w->bindas == cw->bindas &&
44517c6b05d2SAlexander Motin 			    (w->bindseqmask & cw->bindseqmask) != 0)
44527c6b05d2SAlexander Motin 				continue;
44537c6b05d2SAlexander Motin 			w->connsenable[j] = 0;
44547c6b05d2SAlexander Motin 			HDA_BOOTHVERBOSE(
44557c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
44567c6b05d2SAlexander Motin 				    " Disabling crossassociatement connection "
44577c6b05d2SAlexander Motin 				    "nid %d conn %d cnid %d.\n",
44587c6b05d2SAlexander Motin 				    i, j, cw->nid);
44597c6b05d2SAlexander Motin 			);
44607c6b05d2SAlexander Motin 		}
44617c6b05d2SAlexander Motin 	}
44627c6b05d2SAlexander Motin 	/* ... using controls */
44637c6b05d2SAlexander Motin 	i = 0;
44647c6b05d2SAlexander Motin 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
44657c6b05d2SAlexander Motin 		if (ctl->enable == 0 || ctl->childwidget == NULL)
44667c6b05d2SAlexander Motin 			continue;
44677c6b05d2SAlexander Motin 		/* Allow any -> mix */
44687c6b05d2SAlexander Motin 		if (ctl->widget->bindas == -2)
44697c6b05d2SAlexander Motin 			continue;
44707c6b05d2SAlexander Motin 		/* Allow mix -> out. */
44717c6b05d2SAlexander Motin 		if (ctl->childwidget->bindas == -2 &&
44727c6b05d2SAlexander Motin 		    ctl->widget->bindas >= 0 &&
44737c6b05d2SAlexander Motin 		    ases[ctl->widget->bindas].dir == HDAA_CTL_OUT)
44747c6b05d2SAlexander Motin 			continue;
44757c6b05d2SAlexander Motin 		/* Allow mix -> mixed-in. */
44767c6b05d2SAlexander Motin 		if (ctl->childwidget->bindas == -2 &&
44777c6b05d2SAlexander Motin 		    ctl->widget->bindas >= 0 &&
44787c6b05d2SAlexander Motin 		    ases[ctl->widget->bindas].mixed)
44797c6b05d2SAlexander Motin 			continue;
44807c6b05d2SAlexander Motin 		/* Allow in -> mix. */
44817c6b05d2SAlexander Motin 		if ((ctl->widget->pflags & HDAA_ADC_MONITOR) &&
44827c6b05d2SAlexander Motin 		    ctl->childwidget->bindas >= 0 &&
44837c6b05d2SAlexander Motin 		    ases[ctl->childwidget->bindas].dir == HDAA_CTL_IN)
44847c6b05d2SAlexander Motin 			continue;
44857c6b05d2SAlexander Motin 		/* Allow if have common as/seqs. */
44867c6b05d2SAlexander Motin 		if (ctl->widget->bindas == ctl->childwidget->bindas &&
44877c6b05d2SAlexander Motin 		    (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) != 0)
44887c6b05d2SAlexander Motin 			continue;
44897c6b05d2SAlexander Motin 		ctl->forcemute = 1;
44907c6b05d2SAlexander Motin 		ctl->muted = HDAA_AMP_MUTE_ALL;
44917c6b05d2SAlexander Motin 		ctl->left = 0;
44927c6b05d2SAlexander Motin 		ctl->right = 0;
44937c6b05d2SAlexander Motin 		ctl->enable = 0;
44947c6b05d2SAlexander Motin 		if (ctl->ndir == HDAA_CTL_IN)
44957c6b05d2SAlexander Motin 			ctl->widget->connsenable[ctl->index] = 0;
44967c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
44977c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
44987c6b05d2SAlexander Motin 			    " Disabling crossassociatement connection "
44997c6b05d2SAlexander Motin 			    "ctl %d nid %d cnid %d.\n", i,
45007c6b05d2SAlexander Motin 			    ctl->widget->nid,
45017c6b05d2SAlexander Motin 			    ctl->childwidget->nid);
45027c6b05d2SAlexander Motin 		);
45037c6b05d2SAlexander Motin 	}
45047c6b05d2SAlexander Motin 
45057c6b05d2SAlexander Motin }
45067c6b05d2SAlexander Motin 
45077c6b05d2SAlexander Motin /*
45083d741b14SAlexander Motin  * Find controls to control amplification for source and calculate possible
45093d741b14SAlexander Motin  * amplification range.
45107c6b05d2SAlexander Motin  */
45117c6b05d2SAlexander Motin 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)45127c6b05d2SAlexander Motin hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index,
45133d741b14SAlexander Motin     int ossdev, int ctlable, int depth, int *minamp, int *maxamp)
45147c6b05d2SAlexander Motin {
45157c6b05d2SAlexander Motin 	struct hdaa_widget *w, *wc;
45167c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
45173d741b14SAlexander Motin 	int i, j, conns = 0, tminamp, tmaxamp, cminamp, cmaxamp, found = 0;
45187c6b05d2SAlexander Motin 
45197c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
45203d741b14SAlexander Motin 		return (found);
45217c6b05d2SAlexander Motin 
45227c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
45237c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
45243d741b14SAlexander Motin 		return (found);
45257c6b05d2SAlexander Motin 
45267c6b05d2SAlexander Motin 	/* Count number of active inputs. */
45277c6b05d2SAlexander Motin 	if (depth > 0) {
45287c6b05d2SAlexander Motin 		for (j = 0; j < w->nconns; j++) {
45293d741b14SAlexander Motin 			if (!w->connsenable[j])
45303d741b14SAlexander Motin 				continue;
45317c6b05d2SAlexander Motin 			conns++;
45327c6b05d2SAlexander Motin 		}
45337c6b05d2SAlexander Motin 	}
45347c6b05d2SAlexander Motin 
45357c6b05d2SAlexander Motin 	/* If this is not a first step - use input mixer.
45367c6b05d2SAlexander Motin 	   Pins have common input ctl so care must be taken. */
45377c6b05d2SAlexander Motin 	if (depth > 0 && ctlable && (conns == 1 ||
45387c6b05d2SAlexander Motin 	    w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) {
45397c6b05d2SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN,
45407c6b05d2SAlexander Motin 		    index, 1);
45417c6b05d2SAlexander Motin 		if (ctl) {
45427c6b05d2SAlexander Motin 			ctl->ossmask |= (1 << ossdev);
45433d741b14SAlexander Motin 			found++;
45443d741b14SAlexander Motin 			if (*minamp == *maxamp) {
45453d741b14SAlexander Motin 				*minamp += MINQDB(ctl);
45463d741b14SAlexander Motin 				*maxamp += MAXQDB(ctl);
45473d741b14SAlexander Motin 			}
45487c6b05d2SAlexander Motin 		}
45497c6b05d2SAlexander Motin 	}
45507c6b05d2SAlexander Motin 
45517c6b05d2SAlexander Motin 	/* If widget has own ossdev - not traverse it.
455228323addSBryan Drewery 	   It will be traversed on its own. */
45537c6b05d2SAlexander Motin 	if (w->ossdev >= 0 && depth > 0)
45543d741b14SAlexander Motin 		return (found);
45557c6b05d2SAlexander Motin 
45567c6b05d2SAlexander Motin 	/* We must not traverse pin */
45577c6b05d2SAlexander Motin 	if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT ||
45587c6b05d2SAlexander Motin 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) &&
45597c6b05d2SAlexander Motin 	    depth > 0)
45603d741b14SAlexander Motin 		return (found);
45617c6b05d2SAlexander Motin 
45627c6b05d2SAlexander Motin 	/* record that this widget exports such signal, */
45637c6b05d2SAlexander Motin 	w->ossmask |= (1 << ossdev);
45647c6b05d2SAlexander Motin 
45653d741b14SAlexander Motin 	/*
45663d741b14SAlexander Motin 	 * If signals mixed, we can't assign controls farther.
45677c6b05d2SAlexander Motin 	 * Ignore this on depth zero. Caller must knows why.
45687c6b05d2SAlexander Motin 	 */
45693d741b14SAlexander Motin 	if (conns > 1 &&
45703d741b14SAlexander Motin 	    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
45717c6b05d2SAlexander Motin 		ctlable = 0;
45727c6b05d2SAlexander Motin 
45737c6b05d2SAlexander Motin 	if (ctlable) {
45747c6b05d2SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1);
45757c6b05d2SAlexander Motin 		if (ctl) {
45767c6b05d2SAlexander Motin 			ctl->ossmask |= (1 << ossdev);
45773d741b14SAlexander Motin 			found++;
45783d741b14SAlexander Motin 			if (*minamp == *maxamp) {
45793d741b14SAlexander Motin 				*minamp += MINQDB(ctl);
45803d741b14SAlexander Motin 				*maxamp += MAXQDB(ctl);
45813d741b14SAlexander Motin 			}
45827c6b05d2SAlexander Motin 		}
45837c6b05d2SAlexander Motin 	}
45847c6b05d2SAlexander Motin 
45853d741b14SAlexander Motin 	cminamp = cmaxamp = 0;
45867c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
45877c6b05d2SAlexander Motin 		wc = hdaa_widget_get(devinfo, i);
45887c6b05d2SAlexander Motin 		if (wc == NULL || wc->enable == 0)
45897c6b05d2SAlexander Motin 			continue;
45907c6b05d2SAlexander Motin 		for (j = 0; j < wc->nconns; j++) {
45917c6b05d2SAlexander Motin 			if (wc->connsenable[j] && wc->conns[j] == nid) {
45923d741b14SAlexander Motin 				tminamp = tmaxamp = 0;
45933d741b14SAlexander Motin 				found += hdaa_audio_ctl_source_amp(devinfo,
45943d741b14SAlexander Motin 				    wc->nid, j, ossdev, ctlable, depth + 1,
45953d741b14SAlexander Motin 				    &tminamp, &tmaxamp);
45963d741b14SAlexander Motin 				if (cminamp == 0 && cmaxamp == 0) {
45973d741b14SAlexander Motin 					cminamp = tminamp;
45983d741b14SAlexander Motin 					cmaxamp = tmaxamp;
45993d741b14SAlexander Motin 				} else if (tminamp != tmaxamp) {
46003d741b14SAlexander Motin 					cminamp = imax(cminamp, tminamp);
46013d741b14SAlexander Motin 					cmaxamp = imin(cmaxamp, tmaxamp);
46027c6b05d2SAlexander Motin 				}
46037c6b05d2SAlexander Motin 			}
46047c6b05d2SAlexander Motin 		}
46053d741b14SAlexander Motin 	}
46063d741b14SAlexander Motin 	if (*minamp == *maxamp && cminamp < cmaxamp) {
46073d741b14SAlexander Motin 		*minamp += cminamp;
46083d741b14SAlexander Motin 		*maxamp += cmaxamp;
46093d741b14SAlexander Motin 	}
46103d741b14SAlexander Motin 	return (found);
46117c6b05d2SAlexander Motin }
46127c6b05d2SAlexander Motin 
46137c6b05d2SAlexander Motin /*
46143d741b14SAlexander Motin  * Find controls to control amplification for destination and calculate
46153d741b14SAlexander Motin  * possible amplification range.
46167c6b05d2SAlexander Motin  */
46173d741b14SAlexander Motin static int
hdaa_audio_ctl_dest_amp(struct hdaa_devinfo * devinfo,nid_t nid,int index,int ossdev,int depth,int * minamp,int * maxamp)46187c6b05d2SAlexander Motin hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index,
46193d741b14SAlexander Motin     int ossdev, int depth, int *minamp, int *maxamp)
46207c6b05d2SAlexander Motin {
46217c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
46227c6b05d2SAlexander Motin 	struct hdaa_widget *w, *wc;
46237c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
46243d741b14SAlexander Motin 	int i, j, consumers, tminamp, tmaxamp, cminamp, cmaxamp, found = 0;
46257c6b05d2SAlexander Motin 
46267c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
46273d741b14SAlexander Motin 		return (found);
46287c6b05d2SAlexander Motin 
46297c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
46307c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
46313d741b14SAlexander Motin 		return (found);
46327c6b05d2SAlexander Motin 
46337c6b05d2SAlexander Motin 	if (depth > 0) {
46347c6b05d2SAlexander Motin 		/* If this node produce output for several consumers,
46357c6b05d2SAlexander Motin 		   we can't touch it. */
46367c6b05d2SAlexander Motin 		consumers = 0;
46377c6b05d2SAlexander Motin 		for (i = devinfo->startnode; i < devinfo->endnode; i++) {
46387c6b05d2SAlexander Motin 			wc = hdaa_widget_get(devinfo, i);
46397c6b05d2SAlexander Motin 			if (wc == NULL || wc->enable == 0)
46407c6b05d2SAlexander Motin 				continue;
46417c6b05d2SAlexander Motin 			for (j = 0; j < wc->nconns; j++) {
46427c6b05d2SAlexander Motin 				if (wc->connsenable[j] && wc->conns[j] == nid)
46437c6b05d2SAlexander Motin 					consumers++;
46447c6b05d2SAlexander Motin 			}
46457c6b05d2SAlexander Motin 		}
46467c6b05d2SAlexander Motin 		/* The only exception is if real HP redirection is configured
46477c6b05d2SAlexander Motin 		   and this is a duplication point.
46487c6b05d2SAlexander Motin 		   XXX: Actually exception is not completely correct.
46497c6b05d2SAlexander Motin 		   XXX: Duplication point check is not perfect. */
46507c6b05d2SAlexander Motin 		if ((consumers == 2 && (w->bindas < 0 ||
46517c6b05d2SAlexander Motin 		    as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir ||
46527c6b05d2SAlexander Motin 		    (w->bindseqmask & (1 << 15)) == 0)) ||
46537c6b05d2SAlexander Motin 		    consumers > 2)
46543d741b14SAlexander Motin 			return (found);
46557c6b05d2SAlexander Motin 
46567c6b05d2SAlexander Motin 		/* Else use it's output mixer. */
46577c6b05d2SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
46587c6b05d2SAlexander Motin 		    HDAA_CTL_OUT, -1, 1);
46597c6b05d2SAlexander Motin 		if (ctl) {
46607c6b05d2SAlexander Motin 			ctl->ossmask |= (1 << ossdev);
46613d741b14SAlexander Motin 			found++;
46623d741b14SAlexander Motin 			if (*minamp == *maxamp) {
46633d741b14SAlexander Motin 				*minamp += MINQDB(ctl);
46643d741b14SAlexander Motin 				*maxamp += MAXQDB(ctl);
46653d741b14SAlexander Motin 			}
46667c6b05d2SAlexander Motin 		}
46677c6b05d2SAlexander Motin 	}
46687c6b05d2SAlexander Motin 
46697c6b05d2SAlexander Motin 	/* We must not traverse pin */
46707c6b05d2SAlexander Motin 	if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
46717c6b05d2SAlexander Motin 	    depth > 0)
46723d741b14SAlexander Motin 		return (found);
46737c6b05d2SAlexander Motin 
46743d741b14SAlexander Motin 	cminamp = cmaxamp = 0;
46757c6b05d2SAlexander Motin 	for (i = 0; i < w->nconns; i++) {
46767c6b05d2SAlexander Motin 		if (w->connsenable[i] == 0)
46777c6b05d2SAlexander Motin 			continue;
46787c6b05d2SAlexander Motin 		if (index >= 0 && i != index)
46797c6b05d2SAlexander Motin 			continue;
46803d741b14SAlexander Motin 		tminamp = tmaxamp = 0;
46817c6b05d2SAlexander Motin 		ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid,
46827c6b05d2SAlexander Motin 		    HDAA_CTL_IN, i, 1);
46837c6b05d2SAlexander Motin 		if (ctl) {
46847c6b05d2SAlexander Motin 			ctl->ossmask |= (1 << ossdev);
46853d741b14SAlexander Motin 			found++;
46863d741b14SAlexander Motin 			if (*minamp == *maxamp) {
46873d741b14SAlexander Motin 				tminamp += MINQDB(ctl);
46883d741b14SAlexander Motin 				tmaxamp += MAXQDB(ctl);
46897c6b05d2SAlexander Motin 			}
46907c6b05d2SAlexander Motin 		}
46913d741b14SAlexander Motin 		found += hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev,
46923d741b14SAlexander Motin 		    depth + 1, &tminamp, &tmaxamp);
46933d741b14SAlexander Motin 		if (cminamp == 0 && cmaxamp == 0) {
46943d741b14SAlexander Motin 			cminamp = tminamp;
46953d741b14SAlexander Motin 			cmaxamp = tmaxamp;
46963d741b14SAlexander Motin 		} else if (tminamp != tmaxamp) {
46973d741b14SAlexander Motin 			cminamp = imax(cminamp, tminamp);
46983d741b14SAlexander Motin 			cmaxamp = imin(cmaxamp, tmaxamp);
46993d741b14SAlexander Motin 		}
47003d741b14SAlexander Motin 	}
47013d741b14SAlexander Motin 	if (*minamp == *maxamp && cminamp < cmaxamp) {
47023d741b14SAlexander Motin 		*minamp += cminamp;
47033d741b14SAlexander Motin 		*maxamp += cmaxamp;
47043d741b14SAlexander Motin 	}
47053d741b14SAlexander Motin 	return (found);
47067c6b05d2SAlexander Motin }
47077c6b05d2SAlexander Motin 
47087c6b05d2SAlexander Motin /*
47097c6b05d2SAlexander Motin  * Assign OSS names to sound sources
47107c6b05d2SAlexander Motin  */
47117c6b05d2SAlexander Motin static void
hdaa_audio_assign_names(struct hdaa_devinfo * devinfo)47127c6b05d2SAlexander Motin hdaa_audio_assign_names(struct hdaa_devinfo *devinfo)
47137c6b05d2SAlexander Motin {
47147c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
47157c6b05d2SAlexander Motin 	struct hdaa_widget *w;
47167c6b05d2SAlexander Motin 	int i, j;
47177c6b05d2SAlexander Motin 	int type = -1, use, used = 0;
47187c6b05d2SAlexander Motin 	static const int types[7][13] = {
47197c6b05d2SAlexander Motin 	    { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2,
47207c6b05d2SAlexander Motin 	      SOUND_MIXER_LINE3, -1 },	/* line */
47217c6b05d2SAlexander Motin 	    { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */
47227c6b05d2SAlexander Motin 	    { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */
47237c6b05d2SAlexander Motin 	    { SOUND_MIXER_CD, -1 },	/* cd */
47247c6b05d2SAlexander Motin 	    { SOUND_MIXER_SPEAKER, -1 },	/* speaker */
47257c6b05d2SAlexander Motin 	    { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3,
47267c6b05d2SAlexander Motin 	      -1 },	/* digital */
47277c6b05d2SAlexander Motin 	    { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2,
47287c6b05d2SAlexander Motin 	      SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT,
47297c6b05d2SAlexander Motin 	      SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1,
47307c6b05d2SAlexander Motin 	      SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR,
47317c6b05d2SAlexander Motin 	      -1 }	/* others */
47327c6b05d2SAlexander Motin 	};
47337c6b05d2SAlexander Motin 
47347c6b05d2SAlexander Motin 	/* Surely known names */
47357c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
47367c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
47377c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
47387c6b05d2SAlexander Motin 			continue;
47397c6b05d2SAlexander Motin 		if (w->bindas == -1)
47407c6b05d2SAlexander Motin 			continue;
47417c6b05d2SAlexander Motin 		use = -1;
47427c6b05d2SAlexander Motin 		switch (w->type) {
47437c6b05d2SAlexander Motin 		case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX:
47447c6b05d2SAlexander Motin 			if (as[w->bindas].dir == HDAA_CTL_OUT)
47457c6b05d2SAlexander Motin 				break;
47467c6b05d2SAlexander Motin 			type = -1;
47477c6b05d2SAlexander Motin 			switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) {
47487c6b05d2SAlexander Motin 			case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN:
47497c6b05d2SAlexander Motin 				type = 0;
47507c6b05d2SAlexander Motin 				break;
47517c6b05d2SAlexander Motin 			case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN:
47527c6b05d2SAlexander Motin 				if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK)
47537c6b05d2SAlexander Motin 				    == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK)
47547c6b05d2SAlexander Motin 					break;
47557c6b05d2SAlexander Motin 				type = 1;
47567c6b05d2SAlexander Motin 				break;
47577c6b05d2SAlexander Motin 			case HDA_CONFIG_DEFAULTCONF_DEVICE_CD:
47587c6b05d2SAlexander Motin 				type = 3;
47597c6b05d2SAlexander Motin 				break;
47607c6b05d2SAlexander Motin 			case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER:
47617c6b05d2SAlexander Motin 				type = 4;
47627c6b05d2SAlexander Motin 				break;
47637c6b05d2SAlexander Motin 			case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN:
47647c6b05d2SAlexander Motin 			case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN:
47657c6b05d2SAlexander Motin 				type = 5;
47667c6b05d2SAlexander Motin 				break;
47677c6b05d2SAlexander Motin 			}
47687c6b05d2SAlexander Motin 			if (type == -1)
47697c6b05d2SAlexander Motin 				break;
47707c6b05d2SAlexander Motin 			j = 0;
47717c6b05d2SAlexander Motin 			while (types[type][j] >= 0 &&
47727c6b05d2SAlexander Motin 			    (used & (1 << types[type][j])) != 0) {
47737c6b05d2SAlexander Motin 				j++;
47747c6b05d2SAlexander Motin 			}
47757c6b05d2SAlexander Motin 			if (types[type][j] >= 0)
47767c6b05d2SAlexander Motin 				use = types[type][j];
47777c6b05d2SAlexander Motin 			break;
47787c6b05d2SAlexander Motin 		case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT:
47797c6b05d2SAlexander Motin 			use = SOUND_MIXER_PCM;
47807c6b05d2SAlexander Motin 			break;
47817c6b05d2SAlexander Motin 		case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET:
47827c6b05d2SAlexander Motin 			use = SOUND_MIXER_SPEAKER;
47837c6b05d2SAlexander Motin 			break;
47847c6b05d2SAlexander Motin 		default:
47857c6b05d2SAlexander Motin 			break;
47867c6b05d2SAlexander Motin 		}
47877c6b05d2SAlexander Motin 		if (use >= 0) {
47887c6b05d2SAlexander Motin 			w->ossdev = use;
47897c6b05d2SAlexander Motin 			used |= (1 << use);
47907c6b05d2SAlexander Motin 		}
47917c6b05d2SAlexander Motin 	}
47927c6b05d2SAlexander Motin 	/* Semi-known names */
47937c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
47947c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
47957c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
47967c6b05d2SAlexander Motin 			continue;
47977c6b05d2SAlexander Motin 		if (w->ossdev >= 0)
47987c6b05d2SAlexander Motin 			continue;
47997c6b05d2SAlexander Motin 		if (w->bindas == -1)
48007c6b05d2SAlexander Motin 			continue;
48017c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
48027c6b05d2SAlexander Motin 			continue;
48037c6b05d2SAlexander Motin 		if (as[w->bindas].dir == HDAA_CTL_OUT)
48047c6b05d2SAlexander Motin 			continue;
48057c6b05d2SAlexander Motin 		type = -1;
48067c6b05d2SAlexander Motin 		switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) {
48077c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT:
48087c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER:
48097c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT:
48107c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX:
48117c6b05d2SAlexander Motin 			type = 0;
48127c6b05d2SAlexander Motin 			break;
48137c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN:
48147c6b05d2SAlexander Motin 			type = 2;
48157c6b05d2SAlexander Motin 			break;
48167c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT:
48177c6b05d2SAlexander Motin 		case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT:
48187c6b05d2SAlexander Motin 			type = 5;
48197c6b05d2SAlexander Motin 			break;
48207c6b05d2SAlexander Motin 		}
48217c6b05d2SAlexander Motin 		if (type == -1)
48227c6b05d2SAlexander Motin 			break;
48237c6b05d2SAlexander Motin 		j = 0;
48247c6b05d2SAlexander Motin 		while (types[type][j] >= 0 &&
48257c6b05d2SAlexander Motin 		    (used & (1 << types[type][j])) != 0) {
48267c6b05d2SAlexander Motin 			j++;
48277c6b05d2SAlexander Motin 		}
48287c6b05d2SAlexander Motin 		if (types[type][j] >= 0) {
48297c6b05d2SAlexander Motin 			w->ossdev = types[type][j];
48307c6b05d2SAlexander Motin 			used |= (1 << types[type][j]);
48317c6b05d2SAlexander Motin 		}
48327c6b05d2SAlexander Motin 	}
48337c6b05d2SAlexander Motin 	/* Others */
48347c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
48357c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
48367c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
48377c6b05d2SAlexander Motin 			continue;
48387c6b05d2SAlexander Motin 		if (w->ossdev >= 0)
48397c6b05d2SAlexander Motin 			continue;
48407c6b05d2SAlexander Motin 		if (w->bindas == -1)
48417c6b05d2SAlexander Motin 			continue;
48427c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
48437c6b05d2SAlexander Motin 			continue;
48447c6b05d2SAlexander Motin 		if (as[w->bindas].dir == HDAA_CTL_OUT)
48457c6b05d2SAlexander Motin 			continue;
48467c6b05d2SAlexander Motin 		j = 0;
48477c6b05d2SAlexander Motin 		while (types[6][j] >= 0 &&
48487c6b05d2SAlexander Motin 		    (used & (1 << types[6][j])) != 0) {
48497c6b05d2SAlexander Motin 			j++;
48507c6b05d2SAlexander Motin 		}
48517c6b05d2SAlexander Motin 		if (types[6][j] >= 0) {
48527c6b05d2SAlexander Motin 			w->ossdev = types[6][j];
48537c6b05d2SAlexander Motin 			used |= (1 << types[6][j]);
48547c6b05d2SAlexander Motin 		}
48557c6b05d2SAlexander Motin 	}
48567c6b05d2SAlexander Motin }
48577c6b05d2SAlexander Motin 
48587c6b05d2SAlexander Motin static void
hdaa_audio_build_tree(struct hdaa_devinfo * devinfo)48597c6b05d2SAlexander Motin hdaa_audio_build_tree(struct hdaa_devinfo *devinfo)
48607c6b05d2SAlexander Motin {
48617c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
48627c6b05d2SAlexander Motin 	int j, res;
48637c6b05d2SAlexander Motin 
48647c6b05d2SAlexander Motin 	/* Trace all associations in order of their numbers. */
48657c6b05d2SAlexander Motin 	for (j = 0; j < devinfo->ascnt; j++) {
48667c6b05d2SAlexander Motin 		if (as[j].enable == 0)
48677c6b05d2SAlexander Motin 			continue;
48687c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
48697c6b05d2SAlexander Motin 			device_printf(devinfo->dev,
48707c6b05d2SAlexander Motin 			    "Tracing association %d (%d)\n", j, as[j].index);
48717c6b05d2SAlexander Motin 		);
48727c6b05d2SAlexander Motin 		if (as[j].dir == HDAA_CTL_OUT) {
48737c6b05d2SAlexander Motin retry:
48747c6b05d2SAlexander Motin 			res = hdaa_audio_trace_as_out(devinfo, j, 0);
48757c6b05d2SAlexander Motin 			if (res == 0 && as[j].hpredir >= 0 &&
48767c6b05d2SAlexander Motin 			    as[j].fakeredir == 0) {
48777c6b05d2SAlexander Motin 				/* If CODEC can't do analog HP redirection
48787c6b05d2SAlexander Motin 				   try to make it using one more DAC. */
48797c6b05d2SAlexander Motin 				as[j].fakeredir = 1;
48807c6b05d2SAlexander Motin 				goto retry;
48817c6b05d2SAlexander Motin 			}
48827c6b05d2SAlexander Motin 		} else if (as[j].mixed)
48837c6b05d2SAlexander Motin 			res = hdaa_audio_trace_as_in(devinfo, j);
48847c6b05d2SAlexander Motin 		else
48857c6b05d2SAlexander Motin 			res = hdaa_audio_trace_as_in_mch(devinfo, j, 0);
48867c6b05d2SAlexander Motin 		if (res) {
48877c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
48887c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
48897c6b05d2SAlexander Motin 				    "Association %d (%d) trace succeeded\n",
48907c6b05d2SAlexander Motin 				    j, as[j].index);
48917c6b05d2SAlexander Motin 			);
48927c6b05d2SAlexander Motin 		} else {
48937c6b05d2SAlexander Motin 			HDA_BOOTVERBOSE(
48947c6b05d2SAlexander Motin 				device_printf(devinfo->dev,
48957c6b05d2SAlexander Motin 				    "Association %d (%d) trace failed\n",
48967c6b05d2SAlexander Motin 				    j, as[j].index);
48977c6b05d2SAlexander Motin 			);
48987c6b05d2SAlexander Motin 			as[j].enable = 0;
48997c6b05d2SAlexander Motin 		}
49007c6b05d2SAlexander Motin 	}
49017c6b05d2SAlexander Motin 
49027c6b05d2SAlexander Motin 	/* Look for additional DACs/ADCs. */
49037c6b05d2SAlexander Motin 	for (j = 0; j < devinfo->ascnt; j++) {
49047c6b05d2SAlexander Motin 		if (as[j].enable == 0)
49057c6b05d2SAlexander Motin 			continue;
49067c6b05d2SAlexander Motin 		hdaa_audio_adddac(devinfo, j);
49077c6b05d2SAlexander Motin 	}
49087c6b05d2SAlexander Motin 
49097c6b05d2SAlexander Motin 	/* Trace mixer and beeper pseudo associations. */
49107c6b05d2SAlexander Motin 	hdaa_audio_trace_as_extra(devinfo);
49117c6b05d2SAlexander Motin }
49127c6b05d2SAlexander Motin 
49133d741b14SAlexander Motin /*
49143d741b14SAlexander Motin  * Store in pdevinfo new data about whether and how we can control signal
49153d741b14SAlexander Motin  * for OSS device to/from specified widget.
49163d741b14SAlexander Motin  */
49173d741b14SAlexander Motin static void
hdaa_adjust_amp(struct hdaa_widget * w,int ossdev,int found,int minamp,int maxamp)49183d741b14SAlexander Motin hdaa_adjust_amp(struct hdaa_widget *w, int ossdev,
49193d741b14SAlexander Motin     int found, int minamp, int maxamp)
49203d741b14SAlexander Motin {
49213d741b14SAlexander Motin 	struct hdaa_devinfo *devinfo = w->devinfo;
49223d741b14SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo;
49233d741b14SAlexander Motin 
49243d741b14SAlexander Motin 	if (w->bindas >= 0)
49253d741b14SAlexander Motin 		pdevinfo = devinfo->as[w->bindas].pdevinfo;
49263d741b14SAlexander Motin 	else
49273d741b14SAlexander Motin 		pdevinfo = &devinfo->devs[0];
49283d741b14SAlexander Motin 	if (found)
49293d741b14SAlexander Motin 		pdevinfo->ossmask |= (1 << ossdev);
49303d741b14SAlexander Motin 	if (minamp == 0 && maxamp == 0)
49313d741b14SAlexander Motin 		return;
49323d741b14SAlexander Motin 	if (pdevinfo->minamp[ossdev] == 0 && pdevinfo->maxamp[ossdev] == 0) {
49333d741b14SAlexander Motin 		pdevinfo->minamp[ossdev] = minamp;
49343d741b14SAlexander Motin 		pdevinfo->maxamp[ossdev] = maxamp;
49353d741b14SAlexander Motin 	} else {
49363d741b14SAlexander Motin 		pdevinfo->minamp[ossdev] = imax(pdevinfo->minamp[ossdev], minamp);
49373d741b14SAlexander Motin 		pdevinfo->maxamp[ossdev] = imin(pdevinfo->maxamp[ossdev], maxamp);
49383d741b14SAlexander Motin 	}
49393d741b14SAlexander Motin }
49403d741b14SAlexander Motin 
49413d741b14SAlexander Motin /*
49423d741b14SAlexander Motin  * Trace signals from/to all possible sources/destionstions to find possible
49433d741b14SAlexander Motin  * recording sources, OSS device control ranges and to assign controls.
49443d741b14SAlexander Motin  */
49457c6b05d2SAlexander Motin static void
hdaa_audio_assign_mixers(struct hdaa_devinfo * devinfo)49467c6b05d2SAlexander Motin hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo)
49477c6b05d2SAlexander Motin {
49487c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
49497c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
49503d741b14SAlexander Motin 	int i, j, minamp, maxamp, found;
49517c6b05d2SAlexander Motin 
49527c6b05d2SAlexander Motin 	/* Assign mixers to the tree. */
49537c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
49547c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
49557c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
49567c6b05d2SAlexander Motin 			continue;
49573d741b14SAlexander Motin 		minamp = maxamp = 0;
49587c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
49597c6b05d2SAlexander Motin 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET ||
49607c6b05d2SAlexander Motin 		    (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
49617c6b05d2SAlexander Motin 		    as[w->bindas].dir == HDAA_CTL_IN)) {
49627c6b05d2SAlexander Motin 			if (w->ossdev < 0)
49637c6b05d2SAlexander Motin 				continue;
49643d741b14SAlexander Motin 			found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
49653d741b14SAlexander Motin 			    w->ossdev, 1, 0, &minamp, &maxamp);
49663d741b14SAlexander Motin 			hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp);
49677c6b05d2SAlexander Motin 		} else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
49683d741b14SAlexander Motin 			found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
49693d741b14SAlexander Motin 			    SOUND_MIXER_RECLEV, 0, &minamp, &maxamp);
49703d741b14SAlexander Motin 			hdaa_adjust_amp(w, SOUND_MIXER_RECLEV, found, minamp, maxamp);
49717c6b05d2SAlexander Motin 		} else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
49727c6b05d2SAlexander Motin 		    as[w->bindas].dir == HDAA_CTL_OUT) {
49733d741b14SAlexander Motin 			found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
49743d741b14SAlexander Motin 			    SOUND_MIXER_VOLUME, 0, &minamp, &maxamp);
49753d741b14SAlexander Motin 			hdaa_adjust_amp(w, SOUND_MIXER_VOLUME, found, minamp, maxamp);
49767c6b05d2SAlexander Motin 		}
49777c6b05d2SAlexander Motin 		if (w->ossdev == SOUND_MIXER_IMIX) {
49783d741b14SAlexander Motin 			minamp = maxamp = 0;
49793d741b14SAlexander Motin 			found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1,
49803d741b14SAlexander Motin 			    w->ossdev, 1, 0, &minamp, &maxamp);
49813d741b14SAlexander Motin 			if (minamp == maxamp) {
49827c6b05d2SAlexander Motin 				/* If we are unable to control input monitor
49837c6b05d2SAlexander Motin 				   as source - try to control it as destination. */
49843d741b14SAlexander Motin 				found += hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1,
49853d741b14SAlexander Motin 				    w->ossdev, 0, &minamp, &maxamp);
49863d741b14SAlexander Motin 				w->pflags |= HDAA_IMIX_AS_DST;
49877c6b05d2SAlexander Motin 			}
49883d741b14SAlexander Motin 			hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp);
49897c6b05d2SAlexander Motin 		}
49907c6b05d2SAlexander Motin 		if (w->pflags & HDAA_ADC_MONITOR) {
49917c6b05d2SAlexander Motin 			for (j = 0; j < w->nconns; j++) {
49927c6b05d2SAlexander Motin 				if (!w->connsenable[j])
49937c6b05d2SAlexander Motin 				    continue;
49947c6b05d2SAlexander Motin 				cw = hdaa_widget_get(devinfo, w->conns[j]);
49957c6b05d2SAlexander Motin 				if (cw == NULL || cw->enable == 0)
49967c6b05d2SAlexander Motin 				    continue;
49977c6b05d2SAlexander Motin 				if (cw->bindas == -1)
49987c6b05d2SAlexander Motin 				    continue;
49997c6b05d2SAlexander Motin 				if (cw->bindas >= 0 &&
50007c6b05d2SAlexander Motin 				    as[cw->bindas].dir != HDAA_CTL_IN)
50017c6b05d2SAlexander Motin 					continue;
50023d741b14SAlexander Motin 				minamp = maxamp = 0;
50033d741b14SAlexander Motin 				found = hdaa_audio_ctl_dest_amp(devinfo,
50043d741b14SAlexander Motin 				    w->nid, j, SOUND_MIXER_IGAIN, 0,
50053d741b14SAlexander Motin 				    &minamp, &maxamp);
50063d741b14SAlexander Motin 				hdaa_adjust_amp(w, SOUND_MIXER_IGAIN,
50073d741b14SAlexander Motin 				    found, minamp, maxamp);
50087c6b05d2SAlexander Motin 			}
50097c6b05d2SAlexander Motin 		}
50107c6b05d2SAlexander Motin 	}
50117c6b05d2SAlexander Motin }
50127c6b05d2SAlexander Motin 
50137c6b05d2SAlexander Motin static void
hdaa_audio_prepare_pin_ctrl(struct hdaa_devinfo * devinfo)50147c6b05d2SAlexander Motin hdaa_audio_prepare_pin_ctrl(struct hdaa_devinfo *devinfo)
50157c6b05d2SAlexander Motin {
50167c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
50177c6b05d2SAlexander Motin 	struct hdaa_widget *w;
50187c6b05d2SAlexander Motin 	uint32_t pincap;
50197c6b05d2SAlexander Motin 	int i;
50207c6b05d2SAlexander Motin 
50217c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->nodecnt; i++) {
50227c6b05d2SAlexander Motin 		w = &devinfo->widget[i];
50237c6b05d2SAlexander Motin 		if (w == NULL)
50247c6b05d2SAlexander Motin 			continue;
50257c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX &&
50267c6b05d2SAlexander Motin 		    w->waspin == 0)
50277c6b05d2SAlexander Motin 			continue;
50287c6b05d2SAlexander Motin 
50297c6b05d2SAlexander Motin 		pincap = w->wclass.pin.cap;
50307c6b05d2SAlexander Motin 
50317c6b05d2SAlexander Motin 		/* Disable everything. */
5032fceeeec7SSean Bruno 		if (devinfo->init_clear) {
50337c6b05d2SAlexander Motin 			w->wclass.pin.ctrl &= ~(
50347c6b05d2SAlexander Motin 			HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE |
50357c6b05d2SAlexander Motin 			HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE |
50367c6b05d2SAlexander Motin 			HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE |
50377c6b05d2SAlexander Motin 			HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK);
5038fceeeec7SSean Bruno 		}
50397c6b05d2SAlexander Motin 
50407c6b05d2SAlexander Motin 		if (w->enable == 0) {
50417c6b05d2SAlexander Motin 			/* Pin is unused so left it disabled. */
50427c6b05d2SAlexander Motin 			continue;
50437c6b05d2SAlexander Motin 		} else if (w->waspin) {
50447c6b05d2SAlexander Motin 			/* Enable input for beeper input. */
50457c6b05d2SAlexander Motin 			w->wclass.pin.ctrl |=
50467c6b05d2SAlexander Motin 			    HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE;
50477c6b05d2SAlexander Motin 		} else if (w->bindas < 0 || as[w->bindas].enable == 0) {
50487c6b05d2SAlexander Motin 			/* Pin is unused so left it disabled. */
50497c6b05d2SAlexander Motin 			continue;
50507c6b05d2SAlexander Motin 		} else if (as[w->bindas].dir == HDAA_CTL_IN) {
50517c6b05d2SAlexander Motin 			/* Input pin, configure for input. */
50527c6b05d2SAlexander Motin 			if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap))
50537c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50547c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE;
50557c6b05d2SAlexander Motin 
50567c6b05d2SAlexander Motin 			if ((devinfo->quirks & HDAA_QUIRK_IVREF100) &&
50577c6b05d2SAlexander Motin 			    HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
50587c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50597c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50607c6b05d2SAlexander Motin 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100);
50617c6b05d2SAlexander Motin 			else if ((devinfo->quirks & HDAA_QUIRK_IVREF80) &&
50627c6b05d2SAlexander Motin 			    HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
50637c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50647c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50657c6b05d2SAlexander Motin 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80);
50667c6b05d2SAlexander Motin 			else if ((devinfo->quirks & HDAA_QUIRK_IVREF50) &&
50677c6b05d2SAlexander Motin 			    HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
50687c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50697c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50707c6b05d2SAlexander Motin 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50);
50717c6b05d2SAlexander Motin 		} else {
50727c6b05d2SAlexander Motin 			/* Output pin, configure for output. */
50737c6b05d2SAlexander Motin 			if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap))
50747c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50757c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE;
50767c6b05d2SAlexander Motin 
50777c6b05d2SAlexander Motin 			if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) &&
50787c6b05d2SAlexander Motin 			    (w->wclass.pin.config &
50797c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) ==
50807c6b05d2SAlexander Motin 			    HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT)
50817c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50827c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE;
50837c6b05d2SAlexander Motin 
50847c6b05d2SAlexander Motin 			if ((devinfo->quirks & HDAA_QUIRK_OVREF100) &&
50857c6b05d2SAlexander Motin 			    HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
50867c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50877c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50887c6b05d2SAlexander Motin 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100);
50897c6b05d2SAlexander Motin 			else if ((devinfo->quirks & HDAA_QUIRK_OVREF80) &&
50907c6b05d2SAlexander Motin 			    HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
50917c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50927c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50937c6b05d2SAlexander Motin 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80);
50947c6b05d2SAlexander Motin 			else if ((devinfo->quirks & HDAA_QUIRK_OVREF50) &&
50957c6b05d2SAlexander Motin 			    HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
50967c6b05d2SAlexander Motin 				w->wclass.pin.ctrl |=
50977c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(
50987c6b05d2SAlexander Motin 				    HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50);
50997c6b05d2SAlexander Motin 		}
51007c6b05d2SAlexander Motin 	}
51017c6b05d2SAlexander Motin }
51027c6b05d2SAlexander Motin 
51037c6b05d2SAlexander Motin static void
hdaa_audio_ctl_commit(struct hdaa_devinfo * devinfo)51047c6b05d2SAlexander Motin hdaa_audio_ctl_commit(struct hdaa_devinfo *devinfo)
51057c6b05d2SAlexander Motin {
51067c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
51077c6b05d2SAlexander Motin 	int i, z;
51087c6b05d2SAlexander Motin 
51097c6b05d2SAlexander Motin 	i = 0;
51107c6b05d2SAlexander Motin 	while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
51117c6b05d2SAlexander Motin 		if (ctl->enable == 0 || ctl->ossmask != 0) {
51127c6b05d2SAlexander Motin 			/* Mute disabled and mixer controllable controls.
51137c6b05d2SAlexander Motin 			 * Last will be initialized by mixer_init().
51147c6b05d2SAlexander Motin 			 * This expected to reduce click on startup. */
51157c6b05d2SAlexander Motin 			hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_ALL, 0, 0);
51167c6b05d2SAlexander Motin 			continue;
51177c6b05d2SAlexander Motin 		}
51187c6b05d2SAlexander Motin 		/* Init fixed controls to 0dB amplification. */
51197c6b05d2SAlexander Motin 		z = ctl->offset;
51207c6b05d2SAlexander Motin 		if (z > ctl->step)
51217c6b05d2SAlexander Motin 			z = ctl->step;
51227c6b05d2SAlexander Motin 		hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_NONE, z, z);
51237c6b05d2SAlexander Motin 	}
51247c6b05d2SAlexander Motin }
51257c6b05d2SAlexander Motin 
51267c6b05d2SAlexander Motin static void
hdaa_gpio_commit(struct hdaa_devinfo * devinfo)51277c6b05d2SAlexander Motin hdaa_gpio_commit(struct hdaa_devinfo *devinfo)
51287c6b05d2SAlexander Motin {
51297c6b05d2SAlexander Motin 	uint32_t gdata, gmask, gdir;
51307c6b05d2SAlexander Motin 	int i, numgpio;
51317c6b05d2SAlexander Motin 
51327c6b05d2SAlexander Motin 	numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap);
51337c6b05d2SAlexander Motin 	if (devinfo->gpio != 0 && numgpio != 0) {
51347c6b05d2SAlexander Motin 		gdata = hda_command(devinfo->dev,
51357c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_DATA(0, devinfo->nid));
51367c6b05d2SAlexander Motin 		gmask = hda_command(devinfo->dev,
51377c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid));
51387c6b05d2SAlexander Motin 		gdir = hda_command(devinfo->dev,
51397c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid));
51407c6b05d2SAlexander Motin 		for (i = 0; i < numgpio; i++) {
51417c6b05d2SAlexander Motin 			if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51427c6b05d2SAlexander Motin 			    HDAA_GPIO_SET(i)) {
51437c6b05d2SAlexander Motin 				gdata |= (1 << i);
51447c6b05d2SAlexander Motin 				gmask |= (1 << i);
51457c6b05d2SAlexander Motin 				gdir |= (1 << i);
51467c6b05d2SAlexander Motin 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51477c6b05d2SAlexander Motin 			    HDAA_GPIO_CLEAR(i)) {
51487c6b05d2SAlexander Motin 				gdata &= ~(1 << i);
51497c6b05d2SAlexander Motin 				gmask |= (1 << i);
51507c6b05d2SAlexander Motin 				gdir |= (1 << i);
51517c6b05d2SAlexander Motin 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51527c6b05d2SAlexander Motin 			    HDAA_GPIO_DISABLE(i)) {
51537c6b05d2SAlexander Motin 				gmask &= ~(1 << i);
51547c6b05d2SAlexander Motin 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51557c6b05d2SAlexander Motin 			    HDAA_GPIO_INPUT(i)) {
51567c6b05d2SAlexander Motin 				gmask |= (1 << i);
51577c6b05d2SAlexander Motin 				gdir &= ~(1 << i);
51587c6b05d2SAlexander Motin 			}
51597c6b05d2SAlexander Motin 		}
51607c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
51617c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "GPIO commit\n");
51627c6b05d2SAlexander Motin 		);
51637c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
51647c6b05d2SAlexander Motin 		    HDA_CMD_SET_GPIO_ENABLE_MASK(0, devinfo->nid, gmask));
51657c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
51667c6b05d2SAlexander Motin 		    HDA_CMD_SET_GPIO_DIRECTION(0, devinfo->nid, gdir));
51677c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
51687c6b05d2SAlexander Motin 		    HDA_CMD_SET_GPIO_DATA(0, devinfo->nid, gdata));
51697c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
51707c6b05d2SAlexander Motin 			hdaa_dump_gpio(devinfo);
51717c6b05d2SAlexander Motin 		);
51727c6b05d2SAlexander Motin 	}
51737c6b05d2SAlexander Motin }
51747c6b05d2SAlexander Motin 
51757c6b05d2SAlexander Motin static void
hdaa_gpo_commit(struct hdaa_devinfo * devinfo)51767c6b05d2SAlexander Motin hdaa_gpo_commit(struct hdaa_devinfo *devinfo)
51777c6b05d2SAlexander Motin {
51787c6b05d2SAlexander Motin 	uint32_t gdata;
51797c6b05d2SAlexander Motin 	int i, numgpo;
51807c6b05d2SAlexander Motin 
51817c6b05d2SAlexander Motin 	numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap);
51827c6b05d2SAlexander Motin 	if (devinfo->gpo != 0 && numgpo != 0) {
51837c6b05d2SAlexander Motin 		gdata = hda_command(devinfo->dev,
51847c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
51857c6b05d2SAlexander Motin 		for (i = 0; i < numgpo; i++) {
51867c6b05d2SAlexander Motin 			if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51877c6b05d2SAlexander Motin 			    HDAA_GPIO_SET(i)) {
51887c6b05d2SAlexander Motin 				gdata |= (1 << i);
51897c6b05d2SAlexander Motin 			} else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) ==
51907c6b05d2SAlexander Motin 			    HDAA_GPIO_CLEAR(i)) {
51917c6b05d2SAlexander Motin 				gdata &= ~(1 << i);
51927c6b05d2SAlexander Motin 			}
51937c6b05d2SAlexander Motin 		}
51947c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
51957c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "GPO commit\n");
51967c6b05d2SAlexander Motin 		);
51977c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
51987c6b05d2SAlexander Motin 		    HDA_CMD_SET_GPO_DATA(0, devinfo->nid, gdata));
51997c6b05d2SAlexander Motin 		HDA_BOOTVERBOSE(
52007c6b05d2SAlexander Motin 			hdaa_dump_gpo(devinfo);
52017c6b05d2SAlexander Motin 		);
52027c6b05d2SAlexander Motin 	}
52037c6b05d2SAlexander Motin }
52047c6b05d2SAlexander Motin 
52057c6b05d2SAlexander Motin static void
hdaa_audio_commit(struct hdaa_devinfo * devinfo)52067c6b05d2SAlexander Motin hdaa_audio_commit(struct hdaa_devinfo *devinfo)
52077c6b05d2SAlexander Motin {
52087c6b05d2SAlexander Motin 	struct hdaa_widget *w;
52097c6b05d2SAlexander Motin 	int i;
52107c6b05d2SAlexander Motin 
52117c6b05d2SAlexander Motin 	/* Commit controls. */
52127c6b05d2SAlexander Motin 	hdaa_audio_ctl_commit(devinfo);
52137c6b05d2SAlexander Motin 
52147c6b05d2SAlexander Motin 	/* Commit selectors, pins and EAPD. */
52157c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->nodecnt; i++) {
52167c6b05d2SAlexander Motin 		w = &devinfo->widget[i];
52177c6b05d2SAlexander Motin 		if (w == NULL)
52187c6b05d2SAlexander Motin 			continue;
52197c6b05d2SAlexander Motin 		if (w->selconn == -1)
52207c6b05d2SAlexander Motin 			w->selconn = 0;
52217c6b05d2SAlexander Motin 		if (w->nconns > 0)
52227c6b05d2SAlexander Motin 			hdaa_widget_connection_select(w, w->selconn);
52237c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX ||
52247c6b05d2SAlexander Motin 		    w->waspin) {
52257c6b05d2SAlexander Motin 			hda_command(devinfo->dev,
52267c6b05d2SAlexander Motin 			    HDA_CMD_SET_PIN_WIDGET_CTRL(0, w->nid,
52277c6b05d2SAlexander Motin 			    w->wclass.pin.ctrl));
52287c6b05d2SAlexander Motin 		}
52297c6b05d2SAlexander Motin 		if (w->param.eapdbtl != HDA_INVALID) {
52307c6b05d2SAlexander Motin 			uint32_t val;
52317c6b05d2SAlexander Motin 
52327c6b05d2SAlexander Motin 			val = w->param.eapdbtl;
52337c6b05d2SAlexander Motin 			if (devinfo->quirks &
52347c6b05d2SAlexander Motin 			    HDAA_QUIRK_EAPDINV)
52357c6b05d2SAlexander Motin 				val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD;
52367c6b05d2SAlexander Motin 			hda_command(devinfo->dev,
52377c6b05d2SAlexander Motin 			    HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid,
52387c6b05d2SAlexander Motin 			    val));
52397c6b05d2SAlexander Motin 		}
52407c6b05d2SAlexander Motin 	}
52417c6b05d2SAlexander Motin 
52427c6b05d2SAlexander Motin 	hdaa_gpio_commit(devinfo);
52437c6b05d2SAlexander Motin 	hdaa_gpo_commit(devinfo);
52447c6b05d2SAlexander Motin }
52457c6b05d2SAlexander Motin 
52467c6b05d2SAlexander Motin static void
hdaa_powerup(struct hdaa_devinfo * devinfo)52477c6b05d2SAlexander Motin hdaa_powerup(struct hdaa_devinfo *devinfo)
52487c6b05d2SAlexander Motin {
52497c6b05d2SAlexander Motin 	int i;
52507c6b05d2SAlexander Motin 
52517c6b05d2SAlexander Motin 	hda_command(devinfo->dev,
52527c6b05d2SAlexander Motin 	    HDA_CMD_SET_POWER_STATE(0,
52537c6b05d2SAlexander Motin 	    devinfo->nid, HDA_CMD_POWER_STATE_D0));
52547c6b05d2SAlexander Motin 	DELAY(100);
52557c6b05d2SAlexander Motin 
52567c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
52577c6b05d2SAlexander Motin 		hda_command(devinfo->dev,
52587c6b05d2SAlexander Motin 		    HDA_CMD_SET_POWER_STATE(0,
52597c6b05d2SAlexander Motin 		    i, HDA_CMD_POWER_STATE_D0));
52607c6b05d2SAlexander Motin 	}
52617c6b05d2SAlexander Motin 	DELAY(1000);
52627c6b05d2SAlexander Motin }
52637c6b05d2SAlexander Motin 
52647c6b05d2SAlexander Motin static int
hdaa_pcmchannel_setup(struct hdaa_chan * ch)52657c6b05d2SAlexander Motin hdaa_pcmchannel_setup(struct hdaa_chan *ch)
52667c6b05d2SAlexander Motin {
52677c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = ch->devinfo;
52687c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
52697c6b05d2SAlexander Motin 	struct hdaa_widget *w;
52707c6b05d2SAlexander Motin 	uint32_t cap, fmtcap, pcmcap;
52717c6b05d2SAlexander Motin 	int i, j, ret, channels, onlystereo;
52727c6b05d2SAlexander Motin 	uint16_t pinset;
52737c6b05d2SAlexander Motin 
52747c6b05d2SAlexander Motin 	ch->caps = hdaa_caps;
52757c6b05d2SAlexander Motin 	ch->caps.fmtlist = ch->fmtlist;
52767c6b05d2SAlexander Motin 	ch->bit16 = 1;
52777c6b05d2SAlexander Motin 	ch->bit32 = 0;
52787c6b05d2SAlexander Motin 	ch->pcmrates[0] = 48000;
52797c6b05d2SAlexander Motin 	ch->pcmrates[1] = 0;
52806fa8e691SAlexander Motin 	ch->stripecap = 0xff;
52817c6b05d2SAlexander Motin 
52827c6b05d2SAlexander Motin 	ret = 0;
52837c6b05d2SAlexander Motin 	channels = 0;
52847c6b05d2SAlexander Motin 	onlystereo = 1;
52857c6b05d2SAlexander Motin 	pinset = 0;
52867c6b05d2SAlexander Motin 	fmtcap = devinfo->supp_stream_formats;
52877c6b05d2SAlexander Motin 	pcmcap = devinfo->supp_pcm_size_rate;
52887c6b05d2SAlexander Motin 
52897c6b05d2SAlexander Motin 	for (i = 0; i < 16; i++) {
52907c6b05d2SAlexander Motin 		/* Check as is correct */
52917c6b05d2SAlexander Motin 		if (ch->as < 0)
52927c6b05d2SAlexander Motin 			break;
52937c6b05d2SAlexander Motin 		/* Cound only present DACs */
52947c6b05d2SAlexander Motin 		if (as[ch->as].dacs[ch->asindex][i] <= 0)
52957c6b05d2SAlexander Motin 			continue;
52967c6b05d2SAlexander Motin 		/* Ignore duplicates */
52977c6b05d2SAlexander Motin 		for (j = 0; j < ret; j++) {
52987c6b05d2SAlexander Motin 			if (ch->io[j] == as[ch->as].dacs[ch->asindex][i])
52997c6b05d2SAlexander Motin 				break;
53007c6b05d2SAlexander Motin 		}
53017c6b05d2SAlexander Motin 		if (j < ret)
53027c6b05d2SAlexander Motin 			continue;
53037c6b05d2SAlexander Motin 
53047c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, as[ch->as].dacs[ch->asindex][i]);
53057c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
53067c6b05d2SAlexander Motin 			continue;
53077c6b05d2SAlexander Motin 		cap = w->param.supp_stream_formats;
53087c6b05d2SAlexander Motin 		if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) &&
53097c6b05d2SAlexander Motin 		    !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap))
53107c6b05d2SAlexander Motin 			continue;
53117c6b05d2SAlexander Motin 		/* Many CODECs does not declare AC3 support on SPDIF.
53127c6b05d2SAlexander Motin 		   I don't beleave that they doesn't support it! */
53137c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap))
53147c6b05d2SAlexander Motin 			cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK;
53157c6b05d2SAlexander Motin 		if (ret == 0) {
53167c6b05d2SAlexander Motin 			fmtcap = cap;
53177c6b05d2SAlexander Motin 			pcmcap = w->param.supp_pcm_size_rate;
53187c6b05d2SAlexander Motin 		} else {
53197c6b05d2SAlexander Motin 			fmtcap &= cap;
53207c6b05d2SAlexander Motin 			pcmcap &= w->param.supp_pcm_size_rate;
53217c6b05d2SAlexander Motin 		}
53227c6b05d2SAlexander Motin 		ch->io[ret++] = as[ch->as].dacs[ch->asindex][i];
53236fa8e691SAlexander Motin 		ch->stripecap &= w->wclass.conv.stripecap;
53247c6b05d2SAlexander Motin 		/* Do not count redirection pin/dac channels. */
53257c6b05d2SAlexander Motin 		if (i == 15 && as[ch->as].hpredir >= 0)
53267c6b05d2SAlexander Motin 			continue;
53277c6b05d2SAlexander Motin 		channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1;
53287c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1)
53297c6b05d2SAlexander Motin 			onlystereo = 0;
53307c6b05d2SAlexander Motin 		pinset |= (1 << i);
53317c6b05d2SAlexander Motin 	}
53327c6b05d2SAlexander Motin 	ch->io[ret] = -1;
53337c6b05d2SAlexander Motin 	ch->channels = channels;
53347c6b05d2SAlexander Motin 
53357c6b05d2SAlexander Motin 	if (as[ch->as].fakeredir)
53367c6b05d2SAlexander Motin 		ret--;
53377c6b05d2SAlexander Motin 	/* Standard speaks only about stereo pins and playback, ... */
53387c6b05d2SAlexander Motin 	if ((!onlystereo) || as[ch->as].mixed)
53397c6b05d2SAlexander Motin 		pinset = 0;
53407c6b05d2SAlexander Motin 	/* ..., but there it gives us info about speakers layout. */
53417c6b05d2SAlexander Motin 	as[ch->as].pinset = pinset;
53427c6b05d2SAlexander Motin 
53437c6b05d2SAlexander Motin 	ch->supp_stream_formats = fmtcap;
53447c6b05d2SAlexander Motin 	ch->supp_pcm_size_rate = pcmcap;
53457c6b05d2SAlexander Motin 
53467c6b05d2SAlexander Motin 	/*
53477c6b05d2SAlexander Motin 	 *  8bit = 0
53487c6b05d2SAlexander Motin 	 * 16bit = 1
53497c6b05d2SAlexander Motin 	 * 20bit = 2
53507c6b05d2SAlexander Motin 	 * 24bit = 3
53517c6b05d2SAlexander Motin 	 * 32bit = 4
53527c6b05d2SAlexander Motin 	 */
53537c6b05d2SAlexander Motin 	if (ret > 0) {
53547c6b05d2SAlexander Motin 		i = 0;
53557c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) {
53567c6b05d2SAlexander Motin 			if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap))
53577c6b05d2SAlexander Motin 				ch->bit16 = 1;
53587c6b05d2SAlexander Motin 			else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap))
53597c6b05d2SAlexander Motin 				ch->bit16 = 0;
53604f240903SAlexander Motin 			if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap))
53617c6b05d2SAlexander Motin 				ch->bit32 = 3;
53627c6b05d2SAlexander Motin 			else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap))
53637c6b05d2SAlexander Motin 				ch->bit32 = 2;
53644f240903SAlexander Motin 			else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap))
53654f240903SAlexander Motin 				ch->bit32 = 4;
53667c6b05d2SAlexander Motin 			if (!(devinfo->quirks & HDAA_QUIRK_FORCESTEREO)) {
53677c6b05d2SAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0);
53687c6b05d2SAlexander Motin 				if (ch->bit32)
53697c6b05d2SAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0);
53707c6b05d2SAlexander Motin 			}
53717c6b05d2SAlexander Motin 			if (channels >= 2) {
53727c6b05d2SAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0);
53737c6b05d2SAlexander Motin 				if (ch->bit32)
53747c6b05d2SAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0);
53757c6b05d2SAlexander Motin 			}
537688addcbeSAlexander Motin 			if (channels >= 3 && !onlystereo) {
537788addcbeSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 3, 0);
537888addcbeSAlexander Motin 				if (ch->bit32)
537988addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 3, 0);
538088addcbeSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 3, 1);
538188addcbeSAlexander Motin 				if (ch->bit32)
538288addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 3, 1);
538388addcbeSAlexander Motin 			}
538488addcbeSAlexander Motin 			if (channels >= 4) {
53857c6b05d2SAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0);
53867c6b05d2SAlexander Motin 				if (ch->bit32)
53877c6b05d2SAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0);
538888addcbeSAlexander Motin 				if (!onlystereo) {
538988addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 1);
539088addcbeSAlexander Motin 					if (ch->bit32)
539188addcbeSAlexander Motin 						ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 1);
53927c6b05d2SAlexander Motin 				}
539388addcbeSAlexander Motin 			}
539488addcbeSAlexander Motin 			if (channels >= 5 && !onlystereo) {
539588addcbeSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 5, 0);
539688addcbeSAlexander Motin 				if (ch->bit32)
539788addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 5, 0);
539888addcbeSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 5, 1);
539988addcbeSAlexander Motin 				if (ch->bit32)
540088addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 5, 1);
540188addcbeSAlexander Motin 			}
540288addcbeSAlexander Motin 			if (channels >= 6) {
54037c6b05d2SAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1);
54047c6b05d2SAlexander Motin 				if (ch->bit32)
54057c6b05d2SAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1);
540688addcbeSAlexander Motin 				if (!onlystereo) {
540788addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 0);
540888addcbeSAlexander Motin 					if (ch->bit32)
540988addcbeSAlexander Motin 						ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 0);
54107c6b05d2SAlexander Motin 				}
541188addcbeSAlexander Motin 			}
541288addcbeSAlexander Motin 			if (channels >= 7 && !onlystereo) {
541388addcbeSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 7, 0);
541488addcbeSAlexander Motin 				if (ch->bit32)
541588addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 7, 0);
541688addcbeSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 7, 1);
541788addcbeSAlexander Motin 				if (ch->bit32)
541888addcbeSAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 7, 1);
541988addcbeSAlexander Motin 			}
542088addcbeSAlexander Motin 			if (channels >= 8) {
54217c6b05d2SAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1);
54227c6b05d2SAlexander Motin 				if (ch->bit32)
54237c6b05d2SAlexander Motin 					ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1);
54247c6b05d2SAlexander Motin 			}
54257c6b05d2SAlexander Motin 		}
54267c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) {
54277c6b05d2SAlexander Motin 			ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0);
5428bf318c6aSAlexander Motin 			if (channels >= 8) {
5429bf318c6aSAlexander Motin 				ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 8, 1);
5430bf318c6aSAlexander Motin 			}
54317c6b05d2SAlexander Motin 		}
54327c6b05d2SAlexander Motin 		ch->fmtlist[i] = 0;
54337c6b05d2SAlexander Motin 		i = 0;
54347c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap))
54357c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 8000;
54367c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap))
54377c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 11025;
54387c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap))
54397c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 16000;
54407c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap))
54417c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 22050;
54427c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap))
54437c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 32000;
54447c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap))
54457c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 44100;
54467c6b05d2SAlexander Motin 		/* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */
54477c6b05d2SAlexander Motin 		ch->pcmrates[i++] = 48000;
54487c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap))
54497c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 88200;
54507c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap))
54517c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 96000;
54527c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap))
54537c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 176400;
54547c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap))
54557c6b05d2SAlexander Motin 			ch->pcmrates[i++] = 192000;
54567c6b05d2SAlexander Motin 		/* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */
54577c6b05d2SAlexander Motin 		ch->pcmrates[i] = 0;
54587c6b05d2SAlexander Motin 		if (i > 0) {
54597c6b05d2SAlexander Motin 			ch->caps.minspeed = ch->pcmrates[0];
54607c6b05d2SAlexander Motin 			ch->caps.maxspeed = ch->pcmrates[i - 1];
54617c6b05d2SAlexander Motin 		}
54627c6b05d2SAlexander Motin 	}
54637c6b05d2SAlexander Motin 
54647c6b05d2SAlexander Motin 	return (ret);
54657c6b05d2SAlexander Motin }
54667c6b05d2SAlexander Motin 
54677c6b05d2SAlexander Motin static void
hdaa_prepare_pcms(struct hdaa_devinfo * devinfo)54683d741b14SAlexander Motin hdaa_prepare_pcms(struct hdaa_devinfo *devinfo)
54697c6b05d2SAlexander Motin {
54707c6b05d2SAlexander Motin 	struct hdaa_audio_as *as = devinfo->as;
54717c6b05d2SAlexander Motin 	int i, j, k, apdev = 0, ardev = 0, dpdev = 0, drdev = 0;
54727c6b05d2SAlexander Motin 
54737c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->ascnt; i++) {
54747c6b05d2SAlexander Motin 		if (as[i].enable == 0)
54757c6b05d2SAlexander Motin 			continue;
54767c6b05d2SAlexander Motin 		if (as[i].dir == HDAA_CTL_IN) {
54777c6b05d2SAlexander Motin 			if (as[i].digital)
54787c6b05d2SAlexander Motin 				drdev++;
54797c6b05d2SAlexander Motin 			else
54807c6b05d2SAlexander Motin 				ardev++;
54817c6b05d2SAlexander Motin 		} else {
54827c6b05d2SAlexander Motin 			if (as[i].digital)
54837c6b05d2SAlexander Motin 				dpdev++;
54847c6b05d2SAlexander Motin 			else
54857c6b05d2SAlexander Motin 				apdev++;
54867c6b05d2SAlexander Motin 		}
54877c6b05d2SAlexander Motin 	}
54887c6b05d2SAlexander Motin 	devinfo->num_devs =
54897c6b05d2SAlexander Motin 	    max(ardev, apdev) + max(drdev, dpdev);
54903cc01caaSChristos Margiolis 	devinfo->devs = malloc(devinfo->num_devs *
54913cc01caaSChristos Margiolis 	    sizeof(struct hdaa_pcm_devinfo), M_HDAA, M_ZERO | M_NOWAIT);
54927c6b05d2SAlexander Motin 	if (devinfo->devs == NULL) {
54937c6b05d2SAlexander Motin 		device_printf(devinfo->dev,
54947c6b05d2SAlexander Motin 		    "Unable to allocate memory for devices\n");
54957c6b05d2SAlexander Motin 		return;
54967c6b05d2SAlexander Motin 	}
54977c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->num_devs; i++) {
54987c6b05d2SAlexander Motin 		devinfo->devs[i].index = i;
54997c6b05d2SAlexander Motin 		devinfo->devs[i].devinfo = devinfo;
55007c6b05d2SAlexander Motin 		devinfo->devs[i].playas = -1;
55017c6b05d2SAlexander Motin 		devinfo->devs[i].recas = -1;
55027c6b05d2SAlexander Motin 		devinfo->devs[i].digital = 255;
55037c6b05d2SAlexander Motin 	}
55047c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->ascnt; i++) {
55057c6b05d2SAlexander Motin 		if (as[i].enable == 0)
55067c6b05d2SAlexander Motin 			continue;
55077c6b05d2SAlexander Motin 		for (j = 0; j < devinfo->num_devs; j++) {
55087c6b05d2SAlexander Motin 			if (devinfo->devs[j].digital != 255 &&
55097c6b05d2SAlexander Motin 			    (!devinfo->devs[j].digital) !=
55107c6b05d2SAlexander Motin 			    (!as[i].digital))
55117c6b05d2SAlexander Motin 				continue;
55127c6b05d2SAlexander Motin 			if (as[i].dir == HDAA_CTL_IN) {
55137c6b05d2SAlexander Motin 				if (devinfo->devs[j].recas >= 0)
55147c6b05d2SAlexander Motin 					continue;
55157c6b05d2SAlexander Motin 				devinfo->devs[j].recas = i;
55167c6b05d2SAlexander Motin 			} else {
55177c6b05d2SAlexander Motin 				if (devinfo->devs[j].playas >= 0)
55187c6b05d2SAlexander Motin 					continue;
55197c6b05d2SAlexander Motin 				devinfo->devs[j].playas = i;
55207c6b05d2SAlexander Motin 			}
55213d741b14SAlexander Motin 			as[i].pdevinfo = &devinfo->devs[j];
55227c6b05d2SAlexander Motin 			for (k = 0; k < as[i].num_chans; k++) {
55237c6b05d2SAlexander Motin 				devinfo->chans[as[i].chans[k]].pdevinfo =
55247c6b05d2SAlexander Motin 				    &devinfo->devs[j];
55257c6b05d2SAlexander Motin 			}
55267c6b05d2SAlexander Motin 			devinfo->devs[j].digital = as[i].digital;
55277c6b05d2SAlexander Motin 			break;
55287c6b05d2SAlexander Motin 		}
55297c6b05d2SAlexander Motin 	}
55303d741b14SAlexander Motin }
55313d741b14SAlexander Motin 
55323d741b14SAlexander Motin static void
hdaa_create_pcms(struct hdaa_devinfo * devinfo)55333d741b14SAlexander Motin hdaa_create_pcms(struct hdaa_devinfo *devinfo)
55343d741b14SAlexander Motin {
55353d741b14SAlexander Motin 	int i;
55363d741b14SAlexander Motin 
55377c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->num_devs; i++) {
55387c6b05d2SAlexander Motin 		struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
55397c6b05d2SAlexander Motin 
55405b56413dSWarner Losh 		pdevinfo->dev = device_add_child(devinfo->dev, "pcm", DEVICE_UNIT_ANY);
55417c6b05d2SAlexander Motin 		device_set_ivars(pdevinfo->dev, (void *)pdevinfo);
55427c6b05d2SAlexander Motin 	}
55437c6b05d2SAlexander Motin }
55447c6b05d2SAlexander Motin 
55457c6b05d2SAlexander Motin static void
hdaa_dump_ctls(struct hdaa_pcm_devinfo * pdevinfo,const char * banner,uint32_t flag)55467c6b05d2SAlexander Motin hdaa_dump_ctls(struct hdaa_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag)
55477c6b05d2SAlexander Motin {
55487c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
55497c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
55507c6b05d2SAlexander Motin 	char buf[64];
55511ba70629SAlexander Motin 	int i, j, printed = 0;
55527c6b05d2SAlexander Motin 
55537c6b05d2SAlexander Motin 	if (flag == 0) {
55547c6b05d2SAlexander Motin 		flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM |
55557c6b05d2SAlexander Motin 		    SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV |
55567c6b05d2SAlexander Motin 		    SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN |
55577c6b05d2SAlexander Motin 		    SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR);
55587c6b05d2SAlexander Motin 	}
55597c6b05d2SAlexander Motin 
55607c6b05d2SAlexander Motin 	for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
55617c6b05d2SAlexander Motin 		if ((flag & (1 << j)) == 0)
55627c6b05d2SAlexander Motin 			continue;
55637c6b05d2SAlexander Motin 		i = 0;
55647c6b05d2SAlexander Motin 		printed = 0;
55657c6b05d2SAlexander Motin 		while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
55667c6b05d2SAlexander Motin 			if (ctl->enable == 0 ||
55677c6b05d2SAlexander Motin 			    ctl->widget->enable == 0)
55687c6b05d2SAlexander Motin 				continue;
55697c6b05d2SAlexander Motin 			if (!((pdevinfo->playas >= 0 &&
55707c6b05d2SAlexander Motin 			    ctl->widget->bindas == pdevinfo->playas) ||
55717c6b05d2SAlexander Motin 			    (pdevinfo->recas >= 0 &&
55727c6b05d2SAlexander Motin 			    ctl->widget->bindas == pdevinfo->recas) ||
55737c6b05d2SAlexander Motin 			    (ctl->widget->bindas == -2 && pdevinfo->index == 0)))
55747c6b05d2SAlexander Motin 				continue;
55757c6b05d2SAlexander Motin 			if ((ctl->ossmask & (1 << j)) == 0)
55767c6b05d2SAlexander Motin 				continue;
55777c6b05d2SAlexander Motin 
55787c6b05d2SAlexander Motin 			if (printed == 0) {
55797c6b05d2SAlexander Motin 				if (banner != NULL) {
55807c6b05d2SAlexander Motin 					device_printf(pdevinfo->dev, "%s", banner);
55817c6b05d2SAlexander Motin 				} else {
55827c6b05d2SAlexander Motin 					device_printf(pdevinfo->dev, "Unknown Ctl");
55837c6b05d2SAlexander Motin 				}
55843d741b14SAlexander Motin 				printf(" (OSS: %s)",
55857c6b05d2SAlexander Motin 				    hdaa_audio_ctl_ossmixer_mask2allname(1 << j,
55867c6b05d2SAlexander Motin 				    buf, sizeof(buf)));
55873d741b14SAlexander Motin 				if (pdevinfo->ossmask & (1 << j)) {
55883d741b14SAlexander Motin 					printf(": %+d/%+ddB\n",
55893d741b14SAlexander Motin 					    pdevinfo->minamp[j] / 4,
55903d741b14SAlexander Motin 					    pdevinfo->maxamp[j] / 4);
55913d741b14SAlexander Motin 				} else
55923d741b14SAlexander Motin 					printf("\n");
55937c6b05d2SAlexander Motin 				printed = 1;
55947c6b05d2SAlexander Motin 			}
55957c6b05d2SAlexander Motin 			device_printf(pdevinfo->dev, "   +- ctl %2d (nid %3d %s", i,
55967c6b05d2SAlexander Motin 				ctl->widget->nid,
55977c6b05d2SAlexander Motin 				(ctl->ndir == HDAA_CTL_IN)?"in ":"out");
55987c6b05d2SAlexander Motin 			if (ctl->ndir == HDAA_CTL_IN && ctl->ndir == ctl->dir)
55997c6b05d2SAlexander Motin 				printf(" %2d): ", ctl->index);
56007c6b05d2SAlexander Motin 			else
56017c6b05d2SAlexander Motin 				printf("):    ");
56027c6b05d2SAlexander Motin 			if (ctl->step > 0) {
56037c6b05d2SAlexander Motin 				printf("%+d/%+ddB (%d steps)%s\n",
56043d741b14SAlexander Motin 				    MINQDB(ctl) / 4,
56053d741b14SAlexander Motin 				    MAXQDB(ctl) / 4,
56067c6b05d2SAlexander Motin 				    ctl->step + 1,
56077c6b05d2SAlexander Motin 				    ctl->mute?" + mute":"");
56087c6b05d2SAlexander Motin 			} else
56097c6b05d2SAlexander Motin 				printf("%s\n", ctl->mute?"mute":"");
56107c6b05d2SAlexander Motin 		}
56117c6b05d2SAlexander Motin 	}
56127ec13509SAlexander Motin 	if (printed)
56137ec13509SAlexander Motin 		device_printf(pdevinfo->dev, "\n");
56147c6b05d2SAlexander Motin }
56157c6b05d2SAlexander Motin 
56167c6b05d2SAlexander Motin static void
hdaa_dump_audio_formats(device_t dev,uint32_t fcap,uint32_t pcmcap)56177c6b05d2SAlexander Motin hdaa_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap)
56187c6b05d2SAlexander Motin {
56197c6b05d2SAlexander Motin 	uint32_t cap;
56207c6b05d2SAlexander Motin 
56217c6b05d2SAlexander Motin 	cap = fcap;
56227c6b05d2SAlexander Motin 	if (cap != 0) {
56237ec13509SAlexander Motin 		device_printf(dev, "     Stream cap: 0x%08x", cap);
56247c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap))
56257c6b05d2SAlexander Motin 			printf(" AC3");
56267c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap))
56277c6b05d2SAlexander Motin 			printf(" FLOAT32");
56287c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap))
56297c6b05d2SAlexander Motin 			printf(" PCM");
56307c6b05d2SAlexander Motin 		printf("\n");
56317c6b05d2SAlexander Motin 	}
56327c6b05d2SAlexander Motin 	cap = pcmcap;
56337c6b05d2SAlexander Motin 	if (cap != 0) {
56347ec13509SAlexander Motin 		device_printf(dev, "        PCM cap: 0x%08x", cap);
56357c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap))
56367c6b05d2SAlexander Motin 			printf(" 8");
56377c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap))
56387c6b05d2SAlexander Motin 			printf(" 16");
56397c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap))
56407c6b05d2SAlexander Motin 			printf(" 20");
56417c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap))
56427c6b05d2SAlexander Motin 			printf(" 24");
56437c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap))
56447c6b05d2SAlexander Motin 			printf(" 32");
56457c6b05d2SAlexander Motin 		printf(" bits,");
56467c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap))
56477c6b05d2SAlexander Motin 			printf(" 8");
56487c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap))
56497c6b05d2SAlexander Motin 			printf(" 11");
56507c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap))
56517c6b05d2SAlexander Motin 			printf(" 16");
56527c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap))
56537c6b05d2SAlexander Motin 			printf(" 22");
56547c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap))
56557c6b05d2SAlexander Motin 			printf(" 32");
56567c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap))
56577c6b05d2SAlexander Motin 			printf(" 44");
56587c6b05d2SAlexander Motin 		printf(" 48");
56597c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap))
56607c6b05d2SAlexander Motin 			printf(" 88");
56617c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap))
56627c6b05d2SAlexander Motin 			printf(" 96");
56637c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap))
56647c6b05d2SAlexander Motin 			printf(" 176");
56657c6b05d2SAlexander Motin 		if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap))
56667c6b05d2SAlexander Motin 			printf(" 192");
56677c6b05d2SAlexander Motin 		printf(" KHz\n");
56687c6b05d2SAlexander Motin 	}
56697c6b05d2SAlexander Motin }
56707c6b05d2SAlexander Motin 
56717c6b05d2SAlexander Motin static void
hdaa_dump_pin(struct hdaa_widget * w)56727c6b05d2SAlexander Motin hdaa_dump_pin(struct hdaa_widget *w)
56737c6b05d2SAlexander Motin {
56747c6b05d2SAlexander Motin 	uint32_t pincap;
56757c6b05d2SAlexander Motin 
56767c6b05d2SAlexander Motin 	pincap = w->wclass.pin.cap;
56777c6b05d2SAlexander Motin 
56787ec13509SAlexander Motin 	device_printf(w->devinfo->dev, "        Pin cap: 0x%08x", pincap);
56797c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap))
56807c6b05d2SAlexander Motin 		printf(" ISC");
56817c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap))
56827c6b05d2SAlexander Motin 		printf(" TRQD");
56837c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap))
56847c6b05d2SAlexander Motin 		printf(" PDC");
56857c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap))
56867c6b05d2SAlexander Motin 		printf(" HP");
56877c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap))
56887c6b05d2SAlexander Motin 		printf(" OUT");
56897c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap))
56907c6b05d2SAlexander Motin 		printf(" IN");
56917c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap))
56927c6b05d2SAlexander Motin 		printf(" BAL");
56937c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_HDMI(pincap))
56947c6b05d2SAlexander Motin 		printf(" HDMI");
56957c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) {
56967c6b05d2SAlexander Motin 		printf(" VREF[");
56977c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap))
56987c6b05d2SAlexander Motin 			printf(" 50");
56997c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap))
57007c6b05d2SAlexander Motin 			printf(" 80");
57017c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap))
57027c6b05d2SAlexander Motin 			printf(" 100");
57037c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap))
57047c6b05d2SAlexander Motin 			printf(" GROUND");
57057c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap))
57067c6b05d2SAlexander Motin 			printf(" HIZ");
57077c6b05d2SAlexander Motin 		printf(" ]");
57087c6b05d2SAlexander Motin 	}
57097c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap))
57107c6b05d2SAlexander Motin 		printf(" EAPD");
57117c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_DP(pincap))
57127c6b05d2SAlexander Motin 		printf(" DP");
57137c6b05d2SAlexander Motin 	if (HDA_PARAM_PIN_CAP_HBR(pincap))
57147c6b05d2SAlexander Motin 		printf(" HBR");
57157c6b05d2SAlexander Motin 	printf("\n");
57167c6b05d2SAlexander Motin 	device_printf(w->devinfo->dev, "     Pin config: 0x%08x\n",
57177c6b05d2SAlexander Motin 	    w->wclass.pin.config);
57187c6b05d2SAlexander Motin 	device_printf(w->devinfo->dev, "    Pin control: 0x%08x", w->wclass.pin.ctrl);
57197c6b05d2SAlexander Motin 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE)
57207c6b05d2SAlexander Motin 		printf(" HP");
57217c6b05d2SAlexander Motin 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE)
57227c6b05d2SAlexander Motin 		printf(" IN");
57237c6b05d2SAlexander Motin 	if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE)
57247c6b05d2SAlexander Motin 		printf(" OUT");
572588addcbeSAlexander Motin 	if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) {
572688addcbeSAlexander Motin 		if ((w->wclass.pin.ctrl &
572788addcbeSAlexander Motin 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) == 0x03)
572888addcbeSAlexander Motin 			printf(" HBR");
572988addcbeSAlexander Motin 		else if ((w->wclass.pin.ctrl &
573088addcbeSAlexander Motin 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
573188addcbeSAlexander Motin 			printf(" EPTs");
573288addcbeSAlexander Motin 	} else {
573388addcbeSAlexander Motin 		if ((w->wclass.pin.ctrl &
573488addcbeSAlexander Motin 		    HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0)
57357c6b05d2SAlexander Motin 			printf(" VREFs");
573688addcbeSAlexander Motin 	}
57377c6b05d2SAlexander Motin 	printf("\n");
57387c6b05d2SAlexander Motin }
57397c6b05d2SAlexander Motin 
57407c6b05d2SAlexander Motin static void
hdaa_dump_pin_config(struct hdaa_widget * w,uint32_t conf)57417c6b05d2SAlexander Motin hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf)
57427c6b05d2SAlexander Motin {
57437c6b05d2SAlexander Motin 
57447c6b05d2SAlexander Motin 	device_printf(w->devinfo->dev, "%2d %08x %-2d %-2d "
57457c6b05d2SAlexander Motin 	    "%-13s %-5s %-7s %-10s %-7s %d%s\n",
57467c6b05d2SAlexander Motin 	    w->nid, conf,
57477c6b05d2SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf),
57487c6b05d2SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf),
57497c6b05d2SAlexander Motin 	    HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)],
57507c6b05d2SAlexander Motin 	    HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)],
57517c6b05d2SAlexander Motin 	    HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)],
57527c6b05d2SAlexander Motin 	    HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)],
57537c6b05d2SAlexander Motin 	    HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)],
57547c6b05d2SAlexander Motin 	    HDA_CONFIG_DEFAULTCONF_MISC(conf),
57557c6b05d2SAlexander Motin 	    (w->enable == 0)?" DISA":"");
57567c6b05d2SAlexander Motin }
57577c6b05d2SAlexander Motin 
57587c6b05d2SAlexander Motin static void
hdaa_dump_pin_configs(struct hdaa_devinfo * devinfo)57597c6b05d2SAlexander Motin hdaa_dump_pin_configs(struct hdaa_devinfo *devinfo)
57607c6b05d2SAlexander Motin {
57617c6b05d2SAlexander Motin 	struct hdaa_widget *w;
57627c6b05d2SAlexander Motin 	int i;
57637c6b05d2SAlexander Motin 
57647c6b05d2SAlexander Motin 	device_printf(devinfo->dev, "nid   0x    as seq "
57657c6b05d2SAlexander Motin 	    "device       conn  jack    loc        color   misc\n");
57667c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
57677c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
57687c6b05d2SAlexander Motin 		if (w == NULL)
57697c6b05d2SAlexander Motin 			continue;
57707c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
57717c6b05d2SAlexander Motin 			continue;
57727c6b05d2SAlexander Motin 		hdaa_dump_pin_config(w, w->wclass.pin.config);
57737c6b05d2SAlexander Motin 	}
57747c6b05d2SAlexander Motin }
57757c6b05d2SAlexander Motin 
57767c6b05d2SAlexander Motin static void
hdaa_dump_amp(device_t dev,uint32_t cap,const char * banner)5777abe917adSMarius Strobl hdaa_dump_amp(device_t dev, uint32_t cap, const char *banner)
57787c6b05d2SAlexander Motin {
57797ec13509SAlexander Motin 	int offset, size, step;
57807ec13509SAlexander Motin 
57817ec13509SAlexander Motin 	offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap);
57827ec13509SAlexander Motin 	size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap);
57837ec13509SAlexander Motin 	step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap);
57847ec13509SAlexander Motin 	device_printf(dev, "     %s amp: 0x%08x "
57857ec13509SAlexander Motin 	    "mute=%d step=%d size=%d offset=%d (%+d/%+ddB)\n",
57867ec13509SAlexander Motin 	    banner, cap,
57877c6b05d2SAlexander Motin 	    HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap),
57887ec13509SAlexander Motin 	    step, size, offset,
57897ec13509SAlexander Motin 	    ((0 - offset) * (size + 1)) / 4,
57907ec13509SAlexander Motin 	    ((step - offset) * (size + 1)) / 4);
57917c6b05d2SAlexander Motin }
57927c6b05d2SAlexander Motin 
57937c6b05d2SAlexander Motin static void
hdaa_dump_nodes(struct hdaa_devinfo * devinfo)57947c6b05d2SAlexander Motin hdaa_dump_nodes(struct hdaa_devinfo *devinfo)
57957c6b05d2SAlexander Motin {
57967c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
57977c6b05d2SAlexander Motin 	char buf[64];
57987c6b05d2SAlexander Motin 	int i, j;
57997c6b05d2SAlexander Motin 
58007c6b05d2SAlexander Motin 	device_printf(devinfo->dev, "\n");
58017ec13509SAlexander Motin 	device_printf(devinfo->dev, "Default parameters:\n");
58027c6b05d2SAlexander Motin 	hdaa_dump_audio_formats(devinfo->dev,
58037c6b05d2SAlexander Motin 	    devinfo->supp_stream_formats,
58047c6b05d2SAlexander Motin 	    devinfo->supp_pcm_size_rate);
58057ec13509SAlexander Motin 	hdaa_dump_amp(devinfo->dev, devinfo->inamp_cap, " Input");
58067ec13509SAlexander Motin 	hdaa_dump_amp(devinfo->dev, devinfo->outamp_cap, "Output");
58077c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
58087c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
58097c6b05d2SAlexander Motin 		if (w == NULL) {
58107c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "Ghost widget nid=%d\n", i);
58117c6b05d2SAlexander Motin 			continue;
58127c6b05d2SAlexander Motin 		}
58137c6b05d2SAlexander Motin 		device_printf(devinfo->dev, "\n");
58147c6b05d2SAlexander Motin 		device_printf(devinfo->dev, "            nid: %d%s\n", w->nid,
58157c6b05d2SAlexander Motin 		    (w->enable == 0) ? " [DISABLED]" : "");
58167c6b05d2SAlexander Motin 		device_printf(devinfo->dev, "           Name: %s\n", w->name);
58177ec13509SAlexander Motin 		device_printf(devinfo->dev, "     Widget cap: 0x%08x",
58187c6b05d2SAlexander Motin 		    w->param.widget_cap);
58197c6b05d2SAlexander Motin 		if (w->param.widget_cap & 0x0ee1) {
58207c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap))
58217c6b05d2SAlexander Motin 			    printf(" LRSWAP");
58227c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap))
58237c6b05d2SAlexander Motin 			    printf(" PWR");
58247c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap))
58257c6b05d2SAlexander Motin 			    printf(" DIGITAL");
58267c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap))
58277c6b05d2SAlexander Motin 			    printf(" UNSOL");
58287c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap))
58297c6b05d2SAlexander Motin 			    printf(" PROC");
58307c6b05d2SAlexander Motin 			if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap))
58316fa8e691SAlexander Motin 			    printf(" STRIPE(x%d)",
58326fa8e691SAlexander Motin 				1 << (fls(w->wclass.conv.stripecap) - 1));
58337c6b05d2SAlexander Motin 			j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap);
58347c6b05d2SAlexander Motin 			if (j == 1)
58357c6b05d2SAlexander Motin 			    printf(" STEREO");
58367c6b05d2SAlexander Motin 			else if (j > 1)
58377c6b05d2SAlexander Motin 			    printf(" %dCH", j + 1);
58387c6b05d2SAlexander Motin 		}
58397ec13509SAlexander Motin 		printf("\n");
58407c6b05d2SAlexander Motin 		if (w->bindas != -1) {
58417ec13509SAlexander Motin 			device_printf(devinfo->dev, "    Association: %d (0x%04x)\n",
58427c6b05d2SAlexander Motin 			    w->bindas, w->bindseqmask);
58437c6b05d2SAlexander Motin 		}
58447c6b05d2SAlexander Motin 		if (w->ossmask != 0 || w->ossdev >= 0) {
58457c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "            OSS: %s",
58467c6b05d2SAlexander Motin 			    hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf)));
58477c6b05d2SAlexander Motin 			if (w->ossdev >= 0)
58483d741b14SAlexander Motin 			    printf(" (%s)", ossnames[w->ossdev]);
58497c6b05d2SAlexander Motin 			printf("\n");
58507c6b05d2SAlexander Motin 		}
58517c6b05d2SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT ||
58527c6b05d2SAlexander Motin 		    w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) {
58537c6b05d2SAlexander Motin 			hdaa_dump_audio_formats(devinfo->dev,
58547c6b05d2SAlexander Motin 			    w->param.supp_stream_formats,
58557c6b05d2SAlexander Motin 			    w->param.supp_pcm_size_rate);
58567c6b05d2SAlexander Motin 		} else if (w->type ==
58577c6b05d2SAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin)
58587c6b05d2SAlexander Motin 			hdaa_dump_pin(w);
58597c6b05d2SAlexander Motin 		if (w->param.eapdbtl != HDA_INVALID)
58607c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "           EAPD: 0x%08x\n",
58617c6b05d2SAlexander Motin 			    w->param.eapdbtl);
58627c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) &&
58637c6b05d2SAlexander Motin 		    w->param.outamp_cap != 0)
58647c6b05d2SAlexander Motin 			hdaa_dump_amp(devinfo->dev, w->param.outamp_cap, "Output");
58657c6b05d2SAlexander Motin 		if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) &&
58667c6b05d2SAlexander Motin 		    w->param.inamp_cap != 0)
58677c6b05d2SAlexander Motin 			hdaa_dump_amp(devinfo->dev, w->param.inamp_cap, " Input");
58687ec13509SAlexander Motin 		if (w->nconns > 0)
58697ec13509SAlexander Motin 			device_printf(devinfo->dev, "    Connections: %d\n", w->nconns);
58707c6b05d2SAlexander Motin 		for (j = 0; j < w->nconns; j++) {
58717c6b05d2SAlexander Motin 			cw = hdaa_widget_get(devinfo, w->conns[j]);
58727c6b05d2SAlexander Motin 			device_printf(devinfo->dev, "          + %s<- nid=%d [%s]",
58737c6b05d2SAlexander Motin 			    (w->connsenable[j] == 0)?"[DISABLED] ":"",
58747c6b05d2SAlexander Motin 			    w->conns[j], (cw == NULL) ? "GHOST!" : cw->name);
58757c6b05d2SAlexander Motin 			if (cw == NULL)
58767c6b05d2SAlexander Motin 				printf(" [UNKNOWN]");
58777c6b05d2SAlexander Motin 			else if (cw->enable == 0)
58787c6b05d2SAlexander Motin 				printf(" [DISABLED]");
58797c6b05d2SAlexander Motin 			if (w->nconns > 1 && w->selconn == j && w->type !=
58807c6b05d2SAlexander Motin 			    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)
58817c6b05d2SAlexander Motin 				printf(" (selected)");
58827c6b05d2SAlexander Motin 			printf("\n");
58837c6b05d2SAlexander Motin 		}
58847c6b05d2SAlexander Motin 	}
58857c6b05d2SAlexander Motin 
58867c6b05d2SAlexander Motin }
58877c6b05d2SAlexander Motin 
58887c6b05d2SAlexander Motin static void
hdaa_dump_dst_nid(struct hdaa_pcm_devinfo * pdevinfo,nid_t nid,int depth)58897c6b05d2SAlexander Motin hdaa_dump_dst_nid(struct hdaa_pcm_devinfo *pdevinfo, nid_t nid, int depth)
58907c6b05d2SAlexander Motin {
58917c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
58927c6b05d2SAlexander Motin 	struct hdaa_widget *w, *cw;
58937c6b05d2SAlexander Motin 	char buf[64];
58947ec13509SAlexander Motin 	int i;
58957c6b05d2SAlexander Motin 
58967c6b05d2SAlexander Motin 	if (depth > HDA_PARSE_MAXDEPTH)
58977c6b05d2SAlexander Motin 		return;
58987c6b05d2SAlexander Motin 
58997c6b05d2SAlexander Motin 	w = hdaa_widget_get(devinfo, nid);
59007c6b05d2SAlexander Motin 	if (w == NULL || w->enable == 0)
59017c6b05d2SAlexander Motin 		return;
59027c6b05d2SAlexander Motin 
59037c6b05d2SAlexander Motin 	if (depth == 0)
59047c6b05d2SAlexander Motin 		device_printf(pdevinfo->dev, "%*s", 4, "");
59057c6b05d2SAlexander Motin 	else
59067c6b05d2SAlexander Motin 		device_printf(pdevinfo->dev, "%*s  + <- ", 4 + (depth - 1) * 7, "");
59077c6b05d2SAlexander Motin 	printf("nid=%d [%s]", w->nid, w->name);
59087c6b05d2SAlexander Motin 
59097c6b05d2SAlexander Motin 	if (depth > 0) {
59107c6b05d2SAlexander Motin 		if (w->ossmask == 0) {
59117c6b05d2SAlexander Motin 			printf("\n");
59127c6b05d2SAlexander Motin 			return;
59137c6b05d2SAlexander Motin 		}
59147c6b05d2SAlexander Motin 		printf(" [src: %s]",
59157c6b05d2SAlexander Motin 		    hdaa_audio_ctl_ossmixer_mask2allname(
59167c6b05d2SAlexander Motin 			w->ossmask, buf, sizeof(buf)));
59177c6b05d2SAlexander Motin 		if (w->ossdev >= 0) {
59187c6b05d2SAlexander Motin 			printf("\n");
59197c6b05d2SAlexander Motin 			return;
59207c6b05d2SAlexander Motin 		}
59217c6b05d2SAlexander Motin 	}
59227c6b05d2SAlexander Motin 	printf("\n");
59237c6b05d2SAlexander Motin 
59247c6b05d2SAlexander Motin 	for (i = 0; i < w->nconns; i++) {
59257c6b05d2SAlexander Motin 		if (w->connsenable[i] == 0)
59267c6b05d2SAlexander Motin 			continue;
59277c6b05d2SAlexander Motin 		cw = hdaa_widget_get(devinfo, w->conns[i]);
59287c6b05d2SAlexander Motin 		if (cw == NULL || cw->enable == 0 || cw->bindas == -1)
59297c6b05d2SAlexander Motin 			continue;
59307c6b05d2SAlexander Motin 		hdaa_dump_dst_nid(pdevinfo, w->conns[i], depth + 1);
59317c6b05d2SAlexander Motin 	}
59327c6b05d2SAlexander Motin 
59337c6b05d2SAlexander Motin }
59347c6b05d2SAlexander Motin 
59357c6b05d2SAlexander Motin static void
hdaa_dump_dac(struct hdaa_pcm_devinfo * pdevinfo)59367c6b05d2SAlexander Motin hdaa_dump_dac(struct hdaa_pcm_devinfo *pdevinfo)
59377c6b05d2SAlexander Motin {
59387c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
59397c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
59407c6b05d2SAlexander Motin 	struct hdaa_widget *w;
59417ec13509SAlexander Motin 	nid_t *nids;
59427ec13509SAlexander Motin 	int chid, i;
59437c6b05d2SAlexander Motin 
59447c6b05d2SAlexander Motin 	if (pdevinfo->playas < 0)
59457c6b05d2SAlexander Motin 		return;
59467c6b05d2SAlexander Motin 
59477ec13509SAlexander Motin 	device_printf(pdevinfo->dev, "Playback:\n");
59487ec13509SAlexander Motin 
59497ec13509SAlexander Motin 	chid = devinfo->as[pdevinfo->playas].chans[0];
59507ec13509SAlexander Motin 	hdaa_dump_audio_formats(pdevinfo->dev,
59517ec13509SAlexander Motin 	    devinfo->chans[chid].supp_stream_formats,
59527ec13509SAlexander Motin 	    devinfo->chans[chid].supp_pcm_size_rate);
59537ec13509SAlexander Motin 	for (i = 0; i < devinfo->as[pdevinfo->playas].num_chans; i++) {
59547ec13509SAlexander Motin 		chid = devinfo->as[pdevinfo->playas].chans[i];
59557ec13509SAlexander Motin 		device_printf(pdevinfo->dev, "            DAC:");
59567ec13509SAlexander Motin 		for (nids = devinfo->chans[chid].io; *nids != -1; nids++)
59577ec13509SAlexander Motin 			printf(" %d", *nids);
59587ec13509SAlexander Motin 		printf("\n");
59597ec13509SAlexander Motin 	}
59607ec13509SAlexander Motin 
59617c6b05d2SAlexander Motin 	as = &devinfo->as[pdevinfo->playas];
59627c6b05d2SAlexander Motin 	for (i = 0; i < 16; i++) {
59637c6b05d2SAlexander Motin 		if (as->pins[i] <= 0)
59647c6b05d2SAlexander Motin 			continue;
59657c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, as->pins[i]);
59667c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
59677c6b05d2SAlexander Motin 			continue;
59687c6b05d2SAlexander Motin 		device_printf(pdevinfo->dev, "\n");
59697c6b05d2SAlexander Motin 		hdaa_dump_dst_nid(pdevinfo, as->pins[i], 0);
59707c6b05d2SAlexander Motin 	}
59717ec13509SAlexander Motin 	device_printf(pdevinfo->dev, "\n");
59727c6b05d2SAlexander Motin }
59737c6b05d2SAlexander Motin 
59747c6b05d2SAlexander Motin static void
hdaa_dump_adc(struct hdaa_pcm_devinfo * pdevinfo)59757c6b05d2SAlexander Motin hdaa_dump_adc(struct hdaa_pcm_devinfo *pdevinfo)
59767c6b05d2SAlexander Motin {
59777c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
59787c6b05d2SAlexander Motin 	struct hdaa_widget *w;
59797ec13509SAlexander Motin 	nid_t *nids;
59807ec13509SAlexander Motin 	int chid, i;
59817c6b05d2SAlexander Motin 
59827c6b05d2SAlexander Motin 	if (pdevinfo->recas < 0)
59837c6b05d2SAlexander Motin 		return;
59847c6b05d2SAlexander Motin 
59857ec13509SAlexander Motin 	device_printf(pdevinfo->dev, "Record:\n");
59867ec13509SAlexander Motin 
59877ec13509SAlexander Motin 	chid = devinfo->as[pdevinfo->recas].chans[0];
59887ec13509SAlexander Motin 	hdaa_dump_audio_formats(pdevinfo->dev,
59897ec13509SAlexander Motin 	    devinfo->chans[chid].supp_stream_formats,
59907ec13509SAlexander Motin 	    devinfo->chans[chid].supp_pcm_size_rate);
59917ec13509SAlexander Motin 	for (i = 0; i < devinfo->as[pdevinfo->recas].num_chans; i++) {
59927ec13509SAlexander Motin 		chid = devinfo->as[pdevinfo->recas].chans[i];
59937ec13509SAlexander Motin 		device_printf(pdevinfo->dev, "            ADC:");
59947ec13509SAlexander Motin 		for (nids = devinfo->chans[chid].io; *nids != -1; nids++)
59957ec13509SAlexander Motin 			printf(" %d", *nids);
59967ec13509SAlexander Motin 		printf("\n");
59977ec13509SAlexander Motin 	}
59987ec13509SAlexander Motin 
59997c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
60007c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
60017c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
60027c6b05d2SAlexander Motin 			continue;
60037c6b05d2SAlexander Motin 		if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT)
60047c6b05d2SAlexander Motin 			continue;
60057c6b05d2SAlexander Motin 		if (w->bindas != pdevinfo->recas)
60067c6b05d2SAlexander Motin 			continue;
60077c6b05d2SAlexander Motin 		device_printf(pdevinfo->dev, "\n");
60087c6b05d2SAlexander Motin 		hdaa_dump_dst_nid(pdevinfo, i, 0);
60097c6b05d2SAlexander Motin 	}
60107ec13509SAlexander Motin 	device_printf(pdevinfo->dev, "\n");
60117c6b05d2SAlexander Motin }
60127c6b05d2SAlexander Motin 
60137c6b05d2SAlexander Motin static void
hdaa_dump_mix(struct hdaa_pcm_devinfo * pdevinfo)60147c6b05d2SAlexander Motin hdaa_dump_mix(struct hdaa_pcm_devinfo *pdevinfo)
60157c6b05d2SAlexander Motin {
60167c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
60177c6b05d2SAlexander Motin 	struct hdaa_widget *w;
60187c6b05d2SAlexander Motin 	int i;
60197c6b05d2SAlexander Motin 	int printed = 0;
60207c6b05d2SAlexander Motin 
60217c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
60227c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
60237c6b05d2SAlexander Motin 		if (w == NULL || w->enable == 0)
60247c6b05d2SAlexander Motin 			continue;
60257c6b05d2SAlexander Motin 		if (w->ossdev != SOUND_MIXER_IMIX)
60267c6b05d2SAlexander Motin 			continue;
60273d741b14SAlexander Motin 		if (w->bindas != pdevinfo->recas)
60283d741b14SAlexander Motin 			continue;
60297c6b05d2SAlexander Motin 		if (printed == 0) {
60307c6b05d2SAlexander Motin 			printed = 1;
60317c6b05d2SAlexander Motin 			device_printf(pdevinfo->dev, "Input Mix:\n");
60327c6b05d2SAlexander Motin 		}
60337c6b05d2SAlexander Motin 		device_printf(pdevinfo->dev, "\n");
60347c6b05d2SAlexander Motin 		hdaa_dump_dst_nid(pdevinfo, i, 0);
60357c6b05d2SAlexander Motin 	}
60367ec13509SAlexander Motin 	if (printed)
60377c6b05d2SAlexander Motin 		device_printf(pdevinfo->dev, "\n");
60387c6b05d2SAlexander Motin }
60397c6b05d2SAlexander Motin 
60407c6b05d2SAlexander Motin static void
hdaa_pindump(device_t dev)60417c6b05d2SAlexander Motin hdaa_pindump(device_t dev)
60427c6b05d2SAlexander Motin {
60437c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
60447c6b05d2SAlexander Motin 	struct hdaa_widget *w;
60457c6b05d2SAlexander Motin 	uint32_t res, pincap, delay;
60467c6b05d2SAlexander Motin 	int i;
60477c6b05d2SAlexander Motin 
60487c6b05d2SAlexander Motin 	device_printf(dev, "Dumping AFG pins:\n");
60497c6b05d2SAlexander Motin 	device_printf(dev, "nid   0x    as seq "
60507c6b05d2SAlexander Motin 	    "device       conn  jack    loc        color   misc\n");
60517c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
60527c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
60537c6b05d2SAlexander Motin 		if (w == NULL || w->type !=
60547c6b05d2SAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
60557c6b05d2SAlexander Motin 			continue;
60567c6b05d2SAlexander Motin 		hdaa_dump_pin_config(w, w->wclass.pin.config);
60577c6b05d2SAlexander Motin 		pincap = w->wclass.pin.cap;
60587c6b05d2SAlexander Motin 		device_printf(dev, "    Caps: %2s %3s %2s %4s %4s",
60597c6b05d2SAlexander Motin 		    HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"",
60607c6b05d2SAlexander Motin 		    HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"",
60617c6b05d2SAlexander Motin 		    HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"",
60627c6b05d2SAlexander Motin 		    HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"",
60637c6b05d2SAlexander Motin 		    HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":"");
60647c6b05d2SAlexander Motin 		if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) ||
60657c6b05d2SAlexander Motin 		    HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) {
60667c6b05d2SAlexander Motin 			if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) {
60677c6b05d2SAlexander Motin 				delay = 0;
60687c6b05d2SAlexander Motin 				hda_command(dev,
60697c6b05d2SAlexander Motin 				    HDA_CMD_SET_PIN_SENSE(0, w->nid, 0));
60707c6b05d2SAlexander Motin 				do {
60717c6b05d2SAlexander Motin 					res = hda_command(dev,
60727c6b05d2SAlexander Motin 					    HDA_CMD_GET_PIN_SENSE(0, w->nid));
60737c6b05d2SAlexander Motin 					if (res != 0x7fffffff && res != 0xffffffff)
60747c6b05d2SAlexander Motin 						break;
60757c6b05d2SAlexander Motin 					DELAY(10);
60767c6b05d2SAlexander Motin 				} while (++delay < 10000);
60777c6b05d2SAlexander Motin 			} else {
60787c6b05d2SAlexander Motin 				delay = 0;
60797c6b05d2SAlexander Motin 				res = hda_command(dev, HDA_CMD_GET_PIN_SENSE(0,
60807c6b05d2SAlexander Motin 				    w->nid));
60817c6b05d2SAlexander Motin 			}
608288addcbeSAlexander Motin 			printf(" Sense: 0x%08x (%sconnected%s)", res,
60837c6b05d2SAlexander Motin 			    (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ?
608488addcbeSAlexander Motin 			     "" : "dis",
608588addcbeSAlexander Motin 			    (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap) &&
608688addcbeSAlexander Motin 			     (res & HDA_CMD_GET_PIN_SENSE_ELD_VALID)) ?
608788addcbeSAlexander Motin 			      ", ELD valid" : "");
60887c6b05d2SAlexander Motin 			if (delay > 0)
60897c6b05d2SAlexander Motin 				printf(" delay %dus", delay * 10);
60907c6b05d2SAlexander Motin 		}
60917c6b05d2SAlexander Motin 		printf("\n");
60927c6b05d2SAlexander Motin 	}
60937c6b05d2SAlexander Motin 	device_printf(dev,
60947c6b05d2SAlexander Motin 	    "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n",
60957c6b05d2SAlexander Motin 	    HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap),
60967c6b05d2SAlexander Motin 	    HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap),
60977c6b05d2SAlexander Motin 	    HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap),
60987c6b05d2SAlexander Motin 	    HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap),
60997c6b05d2SAlexander Motin 	    HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap));
61007c6b05d2SAlexander Motin 	hdaa_dump_gpi(devinfo);
61017c6b05d2SAlexander Motin 	hdaa_dump_gpio(devinfo);
61027c6b05d2SAlexander Motin 	hdaa_dump_gpo(devinfo);
61037c6b05d2SAlexander Motin }
61047c6b05d2SAlexander Motin 
61057c6b05d2SAlexander Motin static void
hdaa_configure(device_t dev)61067c6b05d2SAlexander Motin hdaa_configure(device_t dev)
61077c6b05d2SAlexander Motin {
61087c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
61097c6b05d2SAlexander Motin 	struct hdaa_audio_ctl *ctl;
61107c6b05d2SAlexander Motin 	int i;
61117c6b05d2SAlexander Motin 
61127c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61137c6b05d2SAlexander Motin 		device_printf(dev, "Applying built-in patches...\n");
61147c6b05d2SAlexander Motin 	);
61157c6b05d2SAlexander Motin 	hdaa_patch(devinfo);
61167c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61177c6b05d2SAlexander Motin 		device_printf(dev, "Applying local patches...\n");
61187c6b05d2SAlexander Motin 	);
61197c6b05d2SAlexander Motin 	hdaa_local_patch(devinfo);
61207c6b05d2SAlexander Motin 	hdaa_audio_postprocess(devinfo);
61217c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61227c6b05d2SAlexander Motin 		device_printf(dev, "Parsing Ctls...\n");
61237c6b05d2SAlexander Motin 	);
61247c6b05d2SAlexander Motin 	hdaa_audio_ctl_parse(devinfo);
61257c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61267c6b05d2SAlexander Motin 		device_printf(dev, "Disabling nonaudio...\n");
61277c6b05d2SAlexander Motin 	);
61287c6b05d2SAlexander Motin 	hdaa_audio_disable_nonaudio(devinfo);
61297c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61307c6b05d2SAlexander Motin 		device_printf(dev, "Disabling useless...\n");
61317c6b05d2SAlexander Motin 	);
61327c6b05d2SAlexander Motin 	hdaa_audio_disable_useless(devinfo);
61337c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
61347c6b05d2SAlexander Motin 		device_printf(dev, "Patched pins configuration:\n");
61357c6b05d2SAlexander Motin 		hdaa_dump_pin_configs(devinfo);
61367c6b05d2SAlexander Motin 	);
61377c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61387c6b05d2SAlexander Motin 		device_printf(dev, "Parsing pin associations...\n");
61397c6b05d2SAlexander Motin 	);
61407c6b05d2SAlexander Motin 	hdaa_audio_as_parse(devinfo);
61417c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61427c6b05d2SAlexander Motin 		device_printf(dev, "Building AFG tree...\n");
61437c6b05d2SAlexander Motin 	);
61447c6b05d2SAlexander Motin 	hdaa_audio_build_tree(devinfo);
61457c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61467c6b05d2SAlexander Motin 		device_printf(dev, "Disabling unassociated "
61477c6b05d2SAlexander Motin 		    "widgets...\n");
61487c6b05d2SAlexander Motin 	);
61497c6b05d2SAlexander Motin 	hdaa_audio_disable_unas(devinfo);
61507c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61517c6b05d2SAlexander Motin 		device_printf(dev, "Disabling nonselected "
61527c6b05d2SAlexander Motin 		    "inputs...\n");
61537c6b05d2SAlexander Motin 	);
61547c6b05d2SAlexander Motin 	hdaa_audio_disable_notselected(devinfo);
61557c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61567c6b05d2SAlexander Motin 		device_printf(dev, "Disabling useless...\n");
61577c6b05d2SAlexander Motin 	);
61587c6b05d2SAlexander Motin 	hdaa_audio_disable_useless(devinfo);
61597c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61607c6b05d2SAlexander Motin 		device_printf(dev, "Disabling "
61617c6b05d2SAlexander Motin 		    "crossassociatement connections...\n");
61627c6b05d2SAlexander Motin 	);
61637c6b05d2SAlexander Motin 	hdaa_audio_disable_crossas(devinfo);
61647c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61657c6b05d2SAlexander Motin 		device_printf(dev, "Disabling useless...\n");
61667c6b05d2SAlexander Motin 	);
61677c6b05d2SAlexander Motin 	hdaa_audio_disable_useless(devinfo);
61687c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61697c6b05d2SAlexander Motin 		device_printf(dev, "Binding associations to channels...\n");
61707c6b05d2SAlexander Motin 	);
61717c6b05d2SAlexander Motin 	hdaa_audio_bind_as(devinfo);
61727c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61737c6b05d2SAlexander Motin 		device_printf(dev, "Assigning names to signal sources...\n");
61747c6b05d2SAlexander Motin 	);
61757c6b05d2SAlexander Motin 	hdaa_audio_assign_names(devinfo);
61767c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61773d741b14SAlexander Motin 		device_printf(dev, "Preparing PCM devices...\n");
61783d741b14SAlexander Motin 	);
61793d741b14SAlexander Motin 	hdaa_prepare_pcms(devinfo);
61803d741b14SAlexander Motin 	HDA_BOOTHVERBOSE(
61817c6b05d2SAlexander Motin 		device_printf(dev, "Assigning mixers to the tree...\n");
61827c6b05d2SAlexander Motin 	);
61837c6b05d2SAlexander Motin 	hdaa_audio_assign_mixers(devinfo);
61847c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61857c6b05d2SAlexander Motin 		device_printf(dev, "Preparing pin controls...\n");
61867c6b05d2SAlexander Motin 	);
61877c6b05d2SAlexander Motin 	hdaa_audio_prepare_pin_ctrl(devinfo);
61887c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61897c6b05d2SAlexander Motin 		device_printf(dev, "AFG commit...\n");
61907c6b05d2SAlexander Motin 	);
61917c6b05d2SAlexander Motin 	hdaa_audio_commit(devinfo);
61927c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
61937c6b05d2SAlexander Motin 		device_printf(dev, "Applying direct built-in patches...\n");
61947c6b05d2SAlexander Motin 	);
61957c6b05d2SAlexander Motin 	hdaa_patch_direct(devinfo);
61967c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
6197d9360bbfSAlexander Motin 		device_printf(dev, "Pin sense init...\n");
619888addcbeSAlexander Motin 	);
6199d9360bbfSAlexander Motin 	hdaa_sense_init(devinfo);
62007c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
62017c6b05d2SAlexander Motin 		device_printf(dev, "Creating PCM devices...\n");
62027c6b05d2SAlexander Motin 	);
62037c6b05d2SAlexander Motin 	hdaa_create_pcms(devinfo);
62047c6b05d2SAlexander Motin 
62057c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
62067c6b05d2SAlexander Motin 		if (devinfo->quirks != 0) {
62077c6b05d2SAlexander Motin 			device_printf(dev, "FG config/quirks:");
6208abe917adSMarius Strobl 			for (i = 0; i < nitems(hdaa_quirks_tab); i++) {
62097c6b05d2SAlexander Motin 				if ((devinfo->quirks &
62107c6b05d2SAlexander Motin 				    hdaa_quirks_tab[i].value) ==
62117c6b05d2SAlexander Motin 				    hdaa_quirks_tab[i].value)
62127c6b05d2SAlexander Motin 					printf(" %s", hdaa_quirks_tab[i].key);
62137c6b05d2SAlexander Motin 			}
62147c6b05d2SAlexander Motin 			printf("\n");
62157c6b05d2SAlexander Motin 		}
62167c6b05d2SAlexander Motin 	);
62177c6b05d2SAlexander Motin 
62187c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
62197c6b05d2SAlexander Motin 		device_printf(dev, "\n");
62207ec13509SAlexander Motin 		device_printf(dev, "+-----------+\n");
62217ec13509SAlexander Motin 		device_printf(dev, "| HDA NODES |\n");
62227ec13509SAlexander Motin 		device_printf(dev, "+-----------+\n");
62237ec13509SAlexander Motin 		hdaa_dump_nodes(devinfo);
62247ec13509SAlexander Motin 
62257ec13509SAlexander Motin 		device_printf(dev, "\n");
62267ec13509SAlexander Motin 		device_printf(dev, "+----------------+\n");
62277ec13509SAlexander Motin 		device_printf(dev, "| HDA AMPLIFIERS |\n");
62287ec13509SAlexander Motin 		device_printf(dev, "+----------------+\n");
62297c6b05d2SAlexander Motin 		device_printf(dev, "\n");
62307c6b05d2SAlexander Motin 		i = 0;
62317c6b05d2SAlexander Motin 		while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) {
62327c6b05d2SAlexander Motin 			device_printf(dev, "%3d: nid %3d %s (%s) index %d", i,
62337c6b05d2SAlexander Motin 			    (ctl->widget != NULL) ? ctl->widget->nid : -1,
62347c6b05d2SAlexander Motin 			    (ctl->ndir == HDAA_CTL_IN)?"in ":"out",
62357c6b05d2SAlexander Motin 			    (ctl->dir == HDAA_CTL_IN)?"in ":"out",
62367c6b05d2SAlexander Motin 			    ctl->index);
62377c6b05d2SAlexander Motin 			if (ctl->childwidget != NULL)
62387c6b05d2SAlexander Motin 				printf(" cnid %3d", ctl->childwidget->nid);
62397c6b05d2SAlexander Motin 			else
62407c6b05d2SAlexander Motin 				printf("         ");
62417c6b05d2SAlexander Motin 			printf(" ossmask=0x%08x\n",
62427c6b05d2SAlexander Motin 			    ctl->ossmask);
62437c6b05d2SAlexander Motin 			device_printf(dev,
62447c6b05d2SAlexander Motin 			    "       mute: %d step: %3d size: %3d off: %3d%s\n",
62457c6b05d2SAlexander Motin 			    ctl->mute, ctl->step, ctl->size, ctl->offset,
62467c6b05d2SAlexander Motin 			    (ctl->enable == 0) ? " [DISABLED]" :
62477c6b05d2SAlexander Motin 			    ((ctl->ossmask == 0) ? " [UNUSED]" : ""));
62487c6b05d2SAlexander Motin 		}
62497c6b05d2SAlexander Motin 		device_printf(dev, "\n");
62507c6b05d2SAlexander Motin 	);
62517c6b05d2SAlexander Motin }
62527c6b05d2SAlexander Motin 
62537c6b05d2SAlexander Motin static void
hdaa_unconfigure(device_t dev)62547c6b05d2SAlexander Motin hdaa_unconfigure(device_t dev)
62557c6b05d2SAlexander Motin {
62567c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
62577c6b05d2SAlexander Motin 	struct hdaa_widget *w;
62587c6b05d2SAlexander Motin 	int i, j;
62597c6b05d2SAlexander Motin 
62607c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
6261d9360bbfSAlexander Motin 		device_printf(dev, "Pin sense deinit...\n");
62627c6b05d2SAlexander Motin 	);
6263d9360bbfSAlexander Motin 	hdaa_sense_deinit(devinfo);
62647c6b05d2SAlexander Motin 	free(devinfo->ctl, M_HDAA);
62657c6b05d2SAlexander Motin 	devinfo->ctl = NULL;
62667c6b05d2SAlexander Motin 	devinfo->ctlcnt = 0;
62677c6b05d2SAlexander Motin 	free(devinfo->as, M_HDAA);
62687c6b05d2SAlexander Motin 	devinfo->as = NULL;
62697c6b05d2SAlexander Motin 	devinfo->ascnt = 0;
62707c6b05d2SAlexander Motin 	free(devinfo->devs, M_HDAA);
62717c6b05d2SAlexander Motin 	devinfo->devs = NULL;
62727c6b05d2SAlexander Motin 	devinfo->num_devs = 0;
62737c6b05d2SAlexander Motin 	free(devinfo->chans, M_HDAA);
62747c6b05d2SAlexander Motin 	devinfo->chans = NULL;
62757c6b05d2SAlexander Motin 	devinfo->num_chans = 0;
62767c6b05d2SAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
62777c6b05d2SAlexander Motin 		w = hdaa_widget_get(devinfo, i);
62787c6b05d2SAlexander Motin 		if (w == NULL)
62797c6b05d2SAlexander Motin 			continue;
62807c6b05d2SAlexander Motin 		w->enable = 1;
62817c6b05d2SAlexander Motin 		w->selconn = -1;
62827c6b05d2SAlexander Motin 		w->pflags = 0;
62837c6b05d2SAlexander Motin 		w->bindas = -1;
62847c6b05d2SAlexander Motin 		w->bindseqmask = 0;
62857c6b05d2SAlexander Motin 		w->ossdev = -1;
62867c6b05d2SAlexander Motin 		w->ossmask = 0;
62877c6b05d2SAlexander Motin 		for (j = 0; j < w->nconns; j++)
62887c6b05d2SAlexander Motin 			w->connsenable[j] = 1;
62893d741b14SAlexander Motin 		if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
62907c6b05d2SAlexander Motin 			w->wclass.pin.config = w->wclass.pin.newconf;
629188addcbeSAlexander Motin 		if (w->eld != NULL) {
629288addcbeSAlexander Motin 			w->eld_len = 0;
629388addcbeSAlexander Motin 			free(w->eld, M_HDAA);
629488addcbeSAlexander Motin 			w->eld = NULL;
629588addcbeSAlexander Motin 		}
62967c6b05d2SAlexander Motin 	}
62977c6b05d2SAlexander Motin }
62987c6b05d2SAlexander Motin 
62997c6b05d2SAlexander Motin static int
hdaa_sysctl_gpi_state(SYSCTL_HANDLER_ARGS)63007c6b05d2SAlexander Motin hdaa_sysctl_gpi_state(SYSCTL_HANDLER_ARGS)
63017c6b05d2SAlexander Motin {
63027c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63037c6b05d2SAlexander Motin 	device_t dev = devinfo->dev;
63047c6b05d2SAlexander Motin 	char buf[256];
63057c6b05d2SAlexander Motin 	int n = 0, i, numgpi;
63067c6b05d2SAlexander Motin 	uint32_t data = 0;
63077c6b05d2SAlexander Motin 
63087c6b05d2SAlexander Motin 	buf[0] = 0;
63097c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
63107c6b05d2SAlexander Motin 	numgpi = HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap);
63117c6b05d2SAlexander Motin 	if (numgpi > 0) {
63127c6b05d2SAlexander Motin 		data = hda_command(dev,
63137c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPI_DATA(0, devinfo->nid));
63147c6b05d2SAlexander Motin 	}
63157c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
63167c6b05d2SAlexander Motin 	for (i = 0; i < numgpi; i++) {
63177c6b05d2SAlexander Motin 		n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%d",
63187c6b05d2SAlexander Motin 		    n != 0 ? " " : "", i, ((data >> i) & 1));
63197c6b05d2SAlexander Motin 	}
63207c6b05d2SAlexander Motin 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
63217c6b05d2SAlexander Motin }
63227c6b05d2SAlexander Motin 
63237c6b05d2SAlexander Motin static int
hdaa_sysctl_gpio_state(SYSCTL_HANDLER_ARGS)63247c6b05d2SAlexander Motin hdaa_sysctl_gpio_state(SYSCTL_HANDLER_ARGS)
63257c6b05d2SAlexander Motin {
63267c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63277c6b05d2SAlexander Motin 	device_t dev = devinfo->dev;
63287c6b05d2SAlexander Motin 	char buf[256];
63297c6b05d2SAlexander Motin 	int n = 0, i, numgpio;
63307c6b05d2SAlexander Motin 	uint32_t data = 0, enable = 0, dir = 0;
63317c6b05d2SAlexander Motin 
63327c6b05d2SAlexander Motin 	buf[0] = 0;
63337c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
63347c6b05d2SAlexander Motin 	numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap);
63357c6b05d2SAlexander Motin 	if (numgpio > 0) {
63367c6b05d2SAlexander Motin 		data = hda_command(dev,
63377c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_DATA(0, devinfo->nid));
63387c6b05d2SAlexander Motin 		enable = hda_command(dev,
63397c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid));
63407c6b05d2SAlexander Motin 		dir = hda_command(dev,
63417c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid));
63427c6b05d2SAlexander Motin 	}
63437c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
63447c6b05d2SAlexander Motin 	for (i = 0; i < numgpio; i++) {
63457c6b05d2SAlexander Motin 		n += snprintf(buf + n, sizeof(buf) - n, "%s%d=",
63467c6b05d2SAlexander Motin 		    n != 0 ? " " : "", i);
63477c6b05d2SAlexander Motin 		if ((enable & (1 << i)) == 0) {
63487c6b05d2SAlexander Motin 			n += snprintf(buf + n, sizeof(buf) - n, "disabled");
63497c6b05d2SAlexander Motin 			continue;
63507c6b05d2SAlexander Motin 		}
63517c6b05d2SAlexander Motin 		n += snprintf(buf + n, sizeof(buf) - n, "%sput(%d)",
63527c6b05d2SAlexander Motin 		    ((dir >> i) & 1) ? "out" : "in", ((data >> i) & 1));
63537c6b05d2SAlexander Motin 	}
63547c6b05d2SAlexander Motin 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
63557c6b05d2SAlexander Motin }
63567c6b05d2SAlexander Motin 
63577c6b05d2SAlexander Motin static int
hdaa_sysctl_gpio_config(SYSCTL_HANDLER_ARGS)63587c6b05d2SAlexander Motin hdaa_sysctl_gpio_config(SYSCTL_HANDLER_ARGS)
63597c6b05d2SAlexander Motin {
63607c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63617c6b05d2SAlexander Motin 	char buf[256];
63627c6b05d2SAlexander Motin 	int error, n = 0, i, numgpio;
63637c6b05d2SAlexander Motin 	uint32_t gpio, x;
63647c6b05d2SAlexander Motin 
63657c6b05d2SAlexander Motin 	gpio = devinfo->newgpio;
63667c6b05d2SAlexander Motin 	numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap);
63677c6b05d2SAlexander Motin 	buf[0] = 0;
63687c6b05d2SAlexander Motin 	for (i = 0; i < numgpio; i++) {
63697c6b05d2SAlexander Motin 		x = (gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i);
63707c6b05d2SAlexander Motin 		n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%s",
63717c6b05d2SAlexander Motin 		    n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]);
63727c6b05d2SAlexander Motin 	}
63737c6b05d2SAlexander Motin 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
63747c6b05d2SAlexander Motin 	if (error != 0 || req->newptr == NULL)
63757c6b05d2SAlexander Motin 		return (error);
63767c6b05d2SAlexander Motin 	if (strncmp(buf, "0x", 2) == 0)
63777c6b05d2SAlexander Motin 		gpio = strtol(buf + 2, NULL, 16);
63787c6b05d2SAlexander Motin 	else
63797c6b05d2SAlexander Motin 		gpio = hdaa_gpio_patch(gpio, buf);
63807c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
63817c6b05d2SAlexander Motin 	devinfo->newgpio = devinfo->gpio = gpio;
63827c6b05d2SAlexander Motin 	hdaa_gpio_commit(devinfo);
63837c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
63847c6b05d2SAlexander Motin 	return (0);
63857c6b05d2SAlexander Motin }
63867c6b05d2SAlexander Motin 
63877c6b05d2SAlexander Motin static int
hdaa_sysctl_gpo_state(SYSCTL_HANDLER_ARGS)63887c6b05d2SAlexander Motin hdaa_sysctl_gpo_state(SYSCTL_HANDLER_ARGS)
63897c6b05d2SAlexander Motin {
63907c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
63917c6b05d2SAlexander Motin 	device_t dev = devinfo->dev;
63927c6b05d2SAlexander Motin 	char buf[256];
63937c6b05d2SAlexander Motin 	int n = 0, i, numgpo;
63947c6b05d2SAlexander Motin 	uint32_t data = 0;
63957c6b05d2SAlexander Motin 
63967c6b05d2SAlexander Motin 	buf[0] = 0;
63977c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
63987c6b05d2SAlexander Motin 	numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap);
63997c6b05d2SAlexander Motin 	if (numgpo > 0) {
64007c6b05d2SAlexander Motin 		data = hda_command(dev,
64017c6b05d2SAlexander Motin 		    HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
64027c6b05d2SAlexander Motin 	}
64037c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
64047c6b05d2SAlexander Motin 	for (i = 0; i < numgpo; i++) {
64057c6b05d2SAlexander Motin 		n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%d",
64067c6b05d2SAlexander Motin 		    n != 0 ? " " : "", i, ((data >> i) & 1));
64077c6b05d2SAlexander Motin 	}
64087c6b05d2SAlexander Motin 	return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
64097c6b05d2SAlexander Motin }
64107c6b05d2SAlexander Motin 
64117c6b05d2SAlexander Motin static int
hdaa_sysctl_gpo_config(SYSCTL_HANDLER_ARGS)64127c6b05d2SAlexander Motin hdaa_sysctl_gpo_config(SYSCTL_HANDLER_ARGS)
64137c6b05d2SAlexander Motin {
64147c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = oidp->oid_arg1;
64157c6b05d2SAlexander Motin 	char buf[256];
64167c6b05d2SAlexander Motin 	int error, n = 0, i, numgpo;
64177c6b05d2SAlexander Motin 	uint32_t gpo, x;
64187c6b05d2SAlexander Motin 
64197c6b05d2SAlexander Motin 	gpo = devinfo->newgpo;
64207c6b05d2SAlexander Motin 	numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap);
64217c6b05d2SAlexander Motin 	buf[0] = 0;
64227c6b05d2SAlexander Motin 	for (i = 0; i < numgpo; i++) {
64237c6b05d2SAlexander Motin 		x = (gpo & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i);
64247c6b05d2SAlexander Motin 		n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%s",
64257c6b05d2SAlexander Motin 		    n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]);
64267c6b05d2SAlexander Motin 	}
64277c6b05d2SAlexander Motin 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
64287c6b05d2SAlexander Motin 	if (error != 0 || req->newptr == NULL)
64297c6b05d2SAlexander Motin 		return (error);
64307c6b05d2SAlexander Motin 	if (strncmp(buf, "0x", 2) == 0)
64317c6b05d2SAlexander Motin 		gpo = strtol(buf + 2, NULL, 16);
64327c6b05d2SAlexander Motin 	else
64337c6b05d2SAlexander Motin 		gpo = hdaa_gpio_patch(gpo, buf);
64347c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
64357c6b05d2SAlexander Motin 	devinfo->newgpo = devinfo->gpo = gpo;
64367c6b05d2SAlexander Motin 	hdaa_gpo_commit(devinfo);
64377c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
64387c6b05d2SAlexander Motin 	return (0);
64397c6b05d2SAlexander Motin }
64407c6b05d2SAlexander Motin 
64417c6b05d2SAlexander Motin static int
hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS)64427c6b05d2SAlexander Motin hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS)
64437c6b05d2SAlexander Motin {
64447c6b05d2SAlexander Motin 	device_t dev;
64457c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo;
64467c6b05d2SAlexander Motin 	int error, val;
64477c6b05d2SAlexander Motin 
64487c6b05d2SAlexander Motin 	dev = oidp->oid_arg1;
64497c6b05d2SAlexander Motin 	devinfo = device_get_softc(dev);
64507c6b05d2SAlexander Motin 	if (devinfo == NULL)
64517c6b05d2SAlexander Motin 		return (EINVAL);
64527c6b05d2SAlexander Motin 	val = 0;
64537c6b05d2SAlexander Motin 	error = sysctl_handle_int(oidp, &val, 0, req);
64547c6b05d2SAlexander Motin 	if (error != 0 || req->newptr == NULL || val == 0)
64557c6b05d2SAlexander Motin 		return (error);
64567c6b05d2SAlexander Motin 
64577c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
64587c6b05d2SAlexander Motin 		device_printf(dev, "Reconfiguration...\n");
64597c6b05d2SAlexander Motin 	);
646096e500a3SWarner Losh 
646196e500a3SWarner Losh 	bus_topo_lock();
646296e500a3SWarner Losh 
646396e500a3SWarner Losh 	if ((error = device_delete_children(dev)) != 0) {
646496e500a3SWarner Losh 		bus_topo_unlock();
64657c6b05d2SAlexander Motin 		return (error);
646696e500a3SWarner Losh 	}
64677c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
64687c6b05d2SAlexander Motin 	hdaa_unconfigure(dev);
64697c6b05d2SAlexander Motin 	hdaa_configure(dev);
64707c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
647118250ec6SJohn Baldwin 	bus_attach_children(dev);
64727c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
64737c6b05d2SAlexander Motin 		device_printf(dev, "Reconfiguration done\n");
64747c6b05d2SAlexander Motin 	);
647596e500a3SWarner Losh 
647696e500a3SWarner Losh 	bus_topo_unlock();
647796e500a3SWarner Losh 
64787c6b05d2SAlexander Motin 	return (0);
64797c6b05d2SAlexander Motin }
64807c6b05d2SAlexander Motin 
64817c6b05d2SAlexander Motin static int
hdaa_suspend(device_t dev)64827c6b05d2SAlexander Motin hdaa_suspend(device_t dev)
64837c6b05d2SAlexander Motin {
64847c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
64857c6b05d2SAlexander Motin 	int i;
64867c6b05d2SAlexander Motin 
64877c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
64887c6b05d2SAlexander Motin 		device_printf(dev, "Suspend...\n");
64897c6b05d2SAlexander Motin 	);
64907c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
64917c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
64927c6b05d2SAlexander Motin 		device_printf(dev, "Stop streams...\n");
64937c6b05d2SAlexander Motin 	);
64947c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->num_chans; i++) {
64957c6b05d2SAlexander Motin 		if (devinfo->chans[i].flags & HDAA_CHN_RUNNING) {
64967c6b05d2SAlexander Motin 			devinfo->chans[i].flags |= HDAA_CHN_SUSPEND;
64977c6b05d2SAlexander Motin 			hdaa_channel_stop(&devinfo->chans[i]);
64987c6b05d2SAlexander Motin 		}
64997c6b05d2SAlexander Motin 	}
65007c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65017c6b05d2SAlexander Motin 		device_printf(dev, "Power down FG"
65027c6b05d2SAlexander Motin 		    " nid=%d to the D3 state...\n",
65037c6b05d2SAlexander Motin 		    devinfo->nid);
65047c6b05d2SAlexander Motin 	);
65057c6b05d2SAlexander Motin 	hda_command(devinfo->dev,
65067c6b05d2SAlexander Motin 	    HDA_CMD_SET_POWER_STATE(0,
65077c6b05d2SAlexander Motin 	    devinfo->nid, HDA_CMD_POWER_STATE_D3));
65087c6b05d2SAlexander Motin 	callout_stop(&devinfo->poll_jack);
65097c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
65107c6b05d2SAlexander Motin 	callout_drain(&devinfo->poll_jack);
65117c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65127c6b05d2SAlexander Motin 		device_printf(dev, "Suspend done\n");
65137c6b05d2SAlexander Motin 	);
65147c6b05d2SAlexander Motin 	return (0);
65157c6b05d2SAlexander Motin }
65167c6b05d2SAlexander Motin 
65177c6b05d2SAlexander Motin static int
hdaa_resume(device_t dev)65187c6b05d2SAlexander Motin hdaa_resume(device_t dev)
65197c6b05d2SAlexander Motin {
65207c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
65217c6b05d2SAlexander Motin 	int i;
65227c6b05d2SAlexander Motin 
65237c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65247c6b05d2SAlexander Motin 		device_printf(dev, "Resume...\n");
65257c6b05d2SAlexander Motin 	);
65267c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
65277c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65287c6b05d2SAlexander Motin 		device_printf(dev, "Power up audio FG nid=%d...\n",
65297c6b05d2SAlexander Motin 		    devinfo->nid);
65307c6b05d2SAlexander Motin 	);
65317c6b05d2SAlexander Motin 	hdaa_powerup(devinfo);
65327c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65337c6b05d2SAlexander Motin 		device_printf(dev, "AFG commit...\n");
65347c6b05d2SAlexander Motin 	);
65357c6b05d2SAlexander Motin 	hdaa_audio_commit(devinfo);
65367c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65377c6b05d2SAlexander Motin 		device_printf(dev, "Applying direct built-in patches...\n");
65387c6b05d2SAlexander Motin 	);
65397c6b05d2SAlexander Motin 	hdaa_patch_direct(devinfo);
65407c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
6541d9360bbfSAlexander Motin 		device_printf(dev, "Pin sense init...\n");
65427c6b05d2SAlexander Motin 	);
6543d9360bbfSAlexander Motin 	hdaa_sense_init(devinfo);
65447c6b05d2SAlexander Motin 
65457c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
65467c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->num_devs; i++) {
65477c6b05d2SAlexander Motin 		struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
65487c6b05d2SAlexander Motin 		HDA_BOOTHVERBOSE(
65497c6b05d2SAlexander Motin 			device_printf(pdevinfo->dev,
65507c6b05d2SAlexander Motin 			    "OSS mixer reinitialization...\n");
65517c6b05d2SAlexander Motin 		);
65527c6b05d2SAlexander Motin 		if (mixer_reinit(pdevinfo->dev) == -1)
65537c6b05d2SAlexander Motin 			device_printf(pdevinfo->dev,
65547c6b05d2SAlexander Motin 			    "unable to reinitialize the mixer\n");
65557c6b05d2SAlexander Motin 	}
65567c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
65577c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65587c6b05d2SAlexander Motin 		device_printf(dev, "Start streams...\n");
65597c6b05d2SAlexander Motin 	);
65607c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->num_chans; i++) {
65617c6b05d2SAlexander Motin 		if (devinfo->chans[i].flags & HDAA_CHN_SUSPEND) {
65627c6b05d2SAlexander Motin 			devinfo->chans[i].flags &= ~HDAA_CHN_SUSPEND;
65637c6b05d2SAlexander Motin 			hdaa_channel_start(&devinfo->chans[i]);
65647c6b05d2SAlexander Motin 		}
65657c6b05d2SAlexander Motin 	}
65667c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
65677c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
65687c6b05d2SAlexander Motin 		device_printf(dev, "Resume done\n");
65697c6b05d2SAlexander Motin 	);
65707c6b05d2SAlexander Motin 	return (0);
65717c6b05d2SAlexander Motin }
65727c6b05d2SAlexander Motin 
65737c6b05d2SAlexander Motin static int
hdaa_probe(device_t dev)65747c6b05d2SAlexander Motin hdaa_probe(device_t dev)
65757c6b05d2SAlexander Motin {
657652ce0de4SAlexander Motin 	const char *pdesc;
65777c6b05d2SAlexander Motin 
65787c6b05d2SAlexander Motin 	if (hda_get_node_type(dev) != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO)
65797c6b05d2SAlexander Motin 		return (ENXIO);
658052ce0de4SAlexander Motin 	pdesc = device_get_desc(device_get_parent(dev));
6581f7d3d0a4SChristos Margiolis 	device_set_descf(dev, "%.*s Audio Function Group",
658252ce0de4SAlexander Motin 	    (int)(strlen(pdesc) - 10), pdesc);
65837c6b05d2SAlexander Motin 	return (BUS_PROBE_DEFAULT);
65847c6b05d2SAlexander Motin }
65857c6b05d2SAlexander Motin 
65867c6b05d2SAlexander Motin static int
hdaa_attach(device_t dev)65877c6b05d2SAlexander Motin hdaa_attach(device_t dev)
65887c6b05d2SAlexander Motin {
65897c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
65907c6b05d2SAlexander Motin 	uint32_t res;
65917c6b05d2SAlexander Motin 	nid_t nid = hda_get_node_id(dev);
65927c6b05d2SAlexander Motin 
65937c6b05d2SAlexander Motin 	devinfo->dev = dev;
65947c6b05d2SAlexander Motin 	devinfo->lock = HDAC_GET_MTX(device_get_parent(dev), dev);
65957c6b05d2SAlexander Motin 	devinfo->nid = nid;
65967c6b05d2SAlexander Motin 	devinfo->newquirks = -1;
65977c6b05d2SAlexander Motin 	devinfo->newgpio = -1;
65987c6b05d2SAlexander Motin 	devinfo->newgpo = -1;
6599fd90e2edSJung-uk Kim 	callout_init(&devinfo->poll_jack, 1);
66007c6b05d2SAlexander Motin 	devinfo->poll_ival = hz;
66017c6b05d2SAlexander Motin 
66027c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
66037c6b05d2SAlexander Motin 	res = hda_command(dev,
66047c6b05d2SAlexander Motin 	    HDA_CMD_GET_PARAMETER(0 , nid, HDA_PARAM_SUB_NODE_COUNT));
66057c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
66067c6b05d2SAlexander Motin 
66077c6b05d2SAlexander Motin 	devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res);
66087c6b05d2SAlexander Motin 	devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res);
66097c6b05d2SAlexander Motin 	devinfo->endnode = devinfo->startnode + devinfo->nodecnt;
66107c6b05d2SAlexander Motin 
66117c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
6612ed228e40SAlexander Motin 		device_printf(dev, "Subsystem ID: 0x%08x\n",
6613ed228e40SAlexander Motin 		    hda_get_subsystem_id(dev));
6614ed228e40SAlexander Motin 	);
6615ed228e40SAlexander Motin 	HDA_BOOTHVERBOSE(
66167c6b05d2SAlexander Motin 		device_printf(dev,
66177c6b05d2SAlexander Motin 		    "Audio Function Group at nid=%d: %d subnodes %d-%d\n",
66187c6b05d2SAlexander Motin 		    nid, devinfo->nodecnt,
66197c6b05d2SAlexander Motin 		    devinfo->startnode, devinfo->endnode - 1);
66207c6b05d2SAlexander Motin 	);
66217c6b05d2SAlexander Motin 
66227c6b05d2SAlexander Motin 	if (devinfo->nodecnt > 0)
66233cc01caaSChristos Margiolis 		devinfo->widget = malloc(sizeof(*(devinfo->widget)) *
66243cc01caaSChristos Margiolis 		    devinfo->nodecnt, M_HDAA, M_WAITOK | M_ZERO);
66257c6b05d2SAlexander Motin 	else
66267c6b05d2SAlexander Motin 		devinfo->widget = NULL;
66277c6b05d2SAlexander Motin 
66287c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
66297c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
66307c6b05d2SAlexander Motin 		device_printf(dev, "Powering up...\n");
66317c6b05d2SAlexander Motin 	);
66327c6b05d2SAlexander Motin 	hdaa_powerup(devinfo);
66337c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
66347c6b05d2SAlexander Motin 		device_printf(dev, "Parsing audio FG...\n");
66357c6b05d2SAlexander Motin 	);
66367c6b05d2SAlexander Motin 	hdaa_audio_parse(devinfo);
66377c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
66387c6b05d2SAlexander Motin 		device_printf(dev, "Original pins configuration:\n");
66397c6b05d2SAlexander Motin 		hdaa_dump_pin_configs(devinfo);
66407c6b05d2SAlexander Motin 	);
66417c6b05d2SAlexander Motin 	hdaa_configure(dev);
66427c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
66437c6b05d2SAlexander Motin 
66447c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66457c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
66467c6b05d2SAlexander Motin 	    "config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
6647e6f79ac5SConrad Meyer 	    &devinfo->newquirks, 0, hdaa_sysctl_quirks, "A",
6648e6f79ac5SConrad Meyer 	    "Configuration options");
66497c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66507c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
66517c6b05d2SAlexander Motin 	    "gpi_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
6652e6f79ac5SConrad Meyer 	    devinfo, 0, hdaa_sysctl_gpi_state, "A", "GPI state");
66537c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66547c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
66557c6b05d2SAlexander Motin 	    "gpio_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
6656e6f79ac5SConrad Meyer 	    devinfo, 0, hdaa_sysctl_gpio_state, "A", "GPIO state");
66577c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66587c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
66597c6b05d2SAlexander Motin 	    "gpio_config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
6660e6f79ac5SConrad Meyer 	    devinfo, 0, hdaa_sysctl_gpio_config, "A", "GPIO configuration");
66617c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66627c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
66637c6b05d2SAlexander Motin 	    "gpo_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
6664e6f79ac5SConrad Meyer 	    devinfo, 0, hdaa_sysctl_gpo_state, "A", "GPO state");
66657c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66667c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
66677c6b05d2SAlexander Motin 	    "gpo_config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
6668e6f79ac5SConrad Meyer 	    devinfo, 0, hdaa_sysctl_gpo_config, "A", "GPO configuration");
66697c6b05d2SAlexander Motin 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
66707c6b05d2SAlexander Motin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
667196e500a3SWarner Losh 	    "reconfig", CTLTYPE_INT | CTLFLAG_RW,
6672e6f79ac5SConrad Meyer 	    dev, 0, hdaa_sysctl_reconfig, "I", "Reprocess configuration");
6673fceeeec7SSean Bruno 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
6674fceeeec7SSean Bruno 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
6675fceeeec7SSean Bruno 	    "init_clear", CTLFLAG_RW,
6676fceeeec7SSean Bruno 	    &devinfo->init_clear, 1,"Clear initial pin widget configuration");
667718250ec6SJohn Baldwin 	bus_attach_children(dev);
66787c6b05d2SAlexander Motin 	return (0);
66797c6b05d2SAlexander Motin }
66807c6b05d2SAlexander Motin 
66817c6b05d2SAlexander Motin static int
hdaa_detach(device_t dev)66827c6b05d2SAlexander Motin hdaa_detach(device_t dev)
66837c6b05d2SAlexander Motin {
66847c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
66857c6b05d2SAlexander Motin 	int error;
66867c6b05d2SAlexander Motin 
6687*3ddaf820SJohn Baldwin 	if ((error = bus_generic_detach(dev)) != 0)
66887c6b05d2SAlexander Motin 		return (error);
66897c6b05d2SAlexander Motin 
66907c6b05d2SAlexander Motin 	hdaa_lock(devinfo);
66917c6b05d2SAlexander Motin 	hdaa_unconfigure(dev);
66927c6b05d2SAlexander Motin 	devinfo->poll_ival = 0;
66937c6b05d2SAlexander Motin 	callout_stop(&devinfo->poll_jack);
66947c6b05d2SAlexander Motin 	hdaa_unlock(devinfo);
66957c6b05d2SAlexander Motin 	callout_drain(&devinfo->poll_jack);
66967c6b05d2SAlexander Motin 
66977c6b05d2SAlexander Motin 	free(devinfo->widget, M_HDAA);
66987c6b05d2SAlexander Motin 	return (0);
66997c6b05d2SAlexander Motin }
67007c6b05d2SAlexander Motin 
67017c6b05d2SAlexander Motin static int
hdaa_print_child(device_t dev,device_t child)67027c6b05d2SAlexander Motin hdaa_print_child(device_t dev, device_t child)
67037c6b05d2SAlexander Motin {
67047c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
67057c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo =
67067c6b05d2SAlexander Motin 	    (struct hdaa_pcm_devinfo *)device_get_ivars(child);
67077c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
67087c6b05d2SAlexander Motin 	int retval, first = 1, i;
67097c6b05d2SAlexander Motin 
67107c6b05d2SAlexander Motin 	retval = bus_print_child_header(dev, child);
67117c6b05d2SAlexander Motin 	retval += printf(" at nid ");
67127c6b05d2SAlexander Motin 	if (pdevinfo->playas >= 0) {
67137c6b05d2SAlexander Motin 		as = &devinfo->as[pdevinfo->playas];
67147c6b05d2SAlexander Motin 		for (i = 0; i < 16; i++) {
67157c6b05d2SAlexander Motin 			if (as->pins[i] <= 0)
67167c6b05d2SAlexander Motin 				continue;
67177c6b05d2SAlexander Motin 			retval += printf("%s%d", first ? "" : ",", as->pins[i]);
67187c6b05d2SAlexander Motin 			first = 0;
67197c6b05d2SAlexander Motin 		}
67207c6b05d2SAlexander Motin 	}
67217c6b05d2SAlexander Motin 	if (pdevinfo->recas >= 0) {
67227c6b05d2SAlexander Motin 		if (pdevinfo->playas >= 0) {
67237c6b05d2SAlexander Motin 			retval += printf(" and ");
67247c6b05d2SAlexander Motin 			first = 1;
67257c6b05d2SAlexander Motin 		}
67267c6b05d2SAlexander Motin 		as = &devinfo->as[pdevinfo->recas];
67277c6b05d2SAlexander Motin 		for (i = 0; i < 16; i++) {
67287c6b05d2SAlexander Motin 			if (as->pins[i] <= 0)
67297c6b05d2SAlexander Motin 				continue;
67307c6b05d2SAlexander Motin 			retval += printf("%s%d", first ? "" : ",", as->pins[i]);
67317c6b05d2SAlexander Motin 			first = 0;
67327c6b05d2SAlexander Motin 		}
67337c6b05d2SAlexander Motin 	}
67347c6b05d2SAlexander Motin 	retval += bus_print_child_footer(dev, child);
67357c6b05d2SAlexander Motin 
67367c6b05d2SAlexander Motin 	return (retval);
67377c6b05d2SAlexander Motin }
67387c6b05d2SAlexander Motin 
67397c6b05d2SAlexander Motin static int
hdaa_child_location(device_t dev,device_t child,struct sbuf * sb)6740ddfc9c4cSWarner Losh hdaa_child_location(device_t dev, device_t child, struct sbuf *sb)
67417c6b05d2SAlexander Motin {
67427c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
67437c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo =
67447c6b05d2SAlexander Motin 	    (struct hdaa_pcm_devinfo *)device_get_ivars(child);
67457c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
6746ddfc9c4cSWarner Losh 	int first = 1, i;
67477c6b05d2SAlexander Motin 
6748ddfc9c4cSWarner Losh 	sbuf_printf(sb, "nid=");
67497c6b05d2SAlexander Motin 	if (pdevinfo->playas >= 0) {
67507c6b05d2SAlexander Motin 		as = &devinfo->as[pdevinfo->playas];
67517c6b05d2SAlexander Motin 		for (i = 0; i < 16; i++) {
67527c6b05d2SAlexander Motin 			if (as->pins[i] <= 0)
67537c6b05d2SAlexander Motin 				continue;
6754ddfc9c4cSWarner Losh 			sbuf_printf(sb, "%s%d", first ? "" : ",", as->pins[i]);
67557c6b05d2SAlexander Motin 			first = 0;
67567c6b05d2SAlexander Motin 		}
67577c6b05d2SAlexander Motin 	}
67587c6b05d2SAlexander Motin 	if (pdevinfo->recas >= 0) {
67597c6b05d2SAlexander Motin 		as = &devinfo->as[pdevinfo->recas];
67607c6b05d2SAlexander Motin 		for (i = 0; i < 16; i++) {
67617c6b05d2SAlexander Motin 			if (as->pins[i] <= 0)
67627c6b05d2SAlexander Motin 				continue;
6763ddfc9c4cSWarner Losh 			sbuf_printf(sb, "%s%d", first ? "" : ",", as->pins[i]);
67647c6b05d2SAlexander Motin 			first = 0;
67657c6b05d2SAlexander Motin 		}
67667c6b05d2SAlexander Motin 	}
67677c6b05d2SAlexander Motin 	return (0);
67687c6b05d2SAlexander Motin }
67697c6b05d2SAlexander Motin 
67707c6b05d2SAlexander Motin static void
hdaa_stream_intr(device_t dev,int dir,int stream)67717c6b05d2SAlexander Motin hdaa_stream_intr(device_t dev, int dir, int stream)
67727c6b05d2SAlexander Motin {
67737c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
67747c6b05d2SAlexander Motin 	struct hdaa_chan *ch;
67757c6b05d2SAlexander Motin 	int i;
67767c6b05d2SAlexander Motin 
67777c6b05d2SAlexander Motin 	for (i = 0; i < devinfo->num_chans; i++) {
67787c6b05d2SAlexander Motin 		ch = &devinfo->chans[i];
67797c6b05d2SAlexander Motin 		if (!(ch->flags & HDAA_CHN_RUNNING))
67807c6b05d2SAlexander Motin 			continue;
67817c6b05d2SAlexander Motin 		if (ch->dir == ((dir == 1) ? PCMDIR_PLAY : PCMDIR_REC) &&
67827c6b05d2SAlexander Motin 		    ch->sid == stream) {
67837c6b05d2SAlexander Motin 			hdaa_unlock(devinfo);
67847c6b05d2SAlexander Motin 			chn_intr(ch->c);
67857c6b05d2SAlexander Motin 			hdaa_lock(devinfo);
67867c6b05d2SAlexander Motin 		}
67877c6b05d2SAlexander Motin 	}
67887c6b05d2SAlexander Motin }
67897c6b05d2SAlexander Motin 
67907c6b05d2SAlexander Motin static void
hdaa_unsol_intr(device_t dev,uint32_t resp)67917c6b05d2SAlexander Motin hdaa_unsol_intr(device_t dev, uint32_t resp)
67927c6b05d2SAlexander Motin {
67937c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = device_get_softc(dev);
679488addcbeSAlexander Motin 	struct hdaa_widget *w;
679588addcbeSAlexander Motin 	int i, tag, flags;
67967c6b05d2SAlexander Motin 
679788addcbeSAlexander Motin 	HDA_BOOTHVERBOSE(
679888addcbeSAlexander Motin 		device_printf(dev, "Unsolicited response %08x\n", resp);
679988addcbeSAlexander Motin 	);
68007c6b05d2SAlexander Motin 	tag = resp >> 26;
680188addcbeSAlexander Motin 	for (i = devinfo->startnode; i < devinfo->endnode; i++) {
680288addcbeSAlexander Motin 		w = hdaa_widget_get(devinfo, i);
680388addcbeSAlexander Motin 		if (w == NULL || w->enable == 0 || w->type !=
680488addcbeSAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
680588addcbeSAlexander Motin 			continue;
680688addcbeSAlexander Motin 		if (w->unsol != tag)
680788addcbeSAlexander Motin 			continue;
680888addcbeSAlexander Motin 		if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) ||
680988addcbeSAlexander Motin 		    HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
681088addcbeSAlexander Motin 			flags = resp & 0x03;
681188addcbeSAlexander Motin 		else
681288addcbeSAlexander Motin 			flags = 0x01;
681388addcbeSAlexander Motin 		if (flags & 0x01)
6814d9360bbfSAlexander Motin 			hdaa_presence_handler(w);
681588addcbeSAlexander Motin 		if (flags & 0x02)
681688addcbeSAlexander Motin 			hdaa_eld_handler(w);
68177c6b05d2SAlexander Motin 	}
68187c6b05d2SAlexander Motin }
68197c6b05d2SAlexander Motin 
68207c6b05d2SAlexander Motin static device_method_t hdaa_methods[] = {
68217c6b05d2SAlexander Motin 	/* device interface */
68227c6b05d2SAlexander Motin 	DEVMETHOD(device_probe,		hdaa_probe),
68237c6b05d2SAlexander Motin 	DEVMETHOD(device_attach,	hdaa_attach),
68247c6b05d2SAlexander Motin 	DEVMETHOD(device_detach,	hdaa_detach),
68257c6b05d2SAlexander Motin 	DEVMETHOD(device_suspend,	hdaa_suspend),
68267c6b05d2SAlexander Motin 	DEVMETHOD(device_resume,	hdaa_resume),
68277c6b05d2SAlexander Motin 	/* Bus interface */
68287c6b05d2SAlexander Motin 	DEVMETHOD(bus_print_child,	hdaa_print_child),
6829ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_location,	hdaa_child_location),
68307c6b05d2SAlexander Motin 	DEVMETHOD(hdac_stream_intr,	hdaa_stream_intr),
68317c6b05d2SAlexander Motin 	DEVMETHOD(hdac_unsol_intr,	hdaa_unsol_intr),
68327c6b05d2SAlexander Motin 	DEVMETHOD(hdac_pindump,		hdaa_pindump),
6833abe917adSMarius Strobl 	DEVMETHOD_END
68347c6b05d2SAlexander Motin };
68357c6b05d2SAlexander Motin 
68367c6b05d2SAlexander Motin static driver_t hdaa_driver = {
68377c6b05d2SAlexander Motin 	"hdaa",
68387c6b05d2SAlexander Motin 	hdaa_methods,
68397c6b05d2SAlexander Motin 	sizeof(struct hdaa_devinfo),
68407c6b05d2SAlexander Motin };
68417c6b05d2SAlexander Motin 
68423390adfeSJohn Baldwin DRIVER_MODULE(snd_hda, hdacc, hdaa_driver, NULL, NULL);
68437c6b05d2SAlexander Motin 
68447c6b05d2SAlexander Motin static void
hdaa_chan_formula(struct hdaa_devinfo * devinfo,int asid,char * buf,int buflen)68457c6b05d2SAlexander Motin hdaa_chan_formula(struct hdaa_devinfo *devinfo, int asid,
68467c6b05d2SAlexander Motin     char *buf, int buflen)
68477c6b05d2SAlexander Motin {
68487c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
68497c6b05d2SAlexander Motin 	int c;
68507c6b05d2SAlexander Motin 
68517c6b05d2SAlexander Motin 	as = &devinfo->as[asid];
68527c6b05d2SAlexander Motin 	c = devinfo->chans[as->chans[0]].channels;
68537c6b05d2SAlexander Motin 	if (c == 1)
68547c6b05d2SAlexander Motin 		snprintf(buf, buflen, "mono");
6855c0e199f6SAlexander Motin 	else if (c == 2) {
6856c0e199f6SAlexander Motin 		if (as->hpredir < 0)
68577c6b05d2SAlexander Motin 			buf[0] = 0;
6858c0e199f6SAlexander Motin 		else
6859c0e199f6SAlexander Motin 			snprintf(buf, buflen, "2.0");
6860c0e199f6SAlexander Motin 	} else if (as->pinset == 0x0003)
68617c6b05d2SAlexander Motin 		snprintf(buf, buflen, "3.1");
68627c6b05d2SAlexander Motin 	else if (as->pinset == 0x0005 || as->pinset == 0x0011)
68637c6b05d2SAlexander Motin 		snprintf(buf, buflen, "4.0");
68647c6b05d2SAlexander Motin 	else if (as->pinset == 0x0007 || as->pinset == 0x0013)
68657c6b05d2SAlexander Motin 		snprintf(buf, buflen, "5.1");
68667c6b05d2SAlexander Motin 	else if (as->pinset == 0x0017)
68677c6b05d2SAlexander Motin 		snprintf(buf, buflen, "7.1");
68687c6b05d2SAlexander Motin 	else
68697c6b05d2SAlexander Motin 		snprintf(buf, buflen, "%dch", c);
6870c0e199f6SAlexander Motin 	if (as->hpredir >= 0)
6871c0e199f6SAlexander Motin 		strlcat(buf, "+HP", buflen);
6872c0e199f6SAlexander Motin }
6873c0e199f6SAlexander Motin 
6874c0e199f6SAlexander Motin static int
hdaa_chan_type(struct hdaa_devinfo * devinfo,int asid)6875c0e199f6SAlexander Motin hdaa_chan_type(struct hdaa_devinfo *devinfo, int asid)
6876c0e199f6SAlexander Motin {
6877c0e199f6SAlexander Motin 	struct hdaa_audio_as *as;
6878c0e199f6SAlexander Motin 	struct hdaa_widget *w;
6879c0e199f6SAlexander Motin 	int i, t = -1, t1;
6880c0e199f6SAlexander Motin 
6881c0e199f6SAlexander Motin 	as = &devinfo->as[asid];
6882c0e199f6SAlexander Motin 	for (i = 0; i < 16; i++) {
6883c0e199f6SAlexander Motin 		w = hdaa_widget_get(devinfo, as->pins[i]);
6884c0e199f6SAlexander Motin 		if (w == NULL || w->enable == 0 || w->type !=
6885c0e199f6SAlexander Motin 		    HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
6886c0e199f6SAlexander Motin 			continue;
6887c0e199f6SAlexander Motin 		t1 = HDA_CONFIG_DEFAULTCONF_DEVICE(w->wclass.pin.config);
6888c0e199f6SAlexander Motin 		if (t == -1)
6889c0e199f6SAlexander Motin 			t = t1;
6890c0e199f6SAlexander Motin 		else if (t != t1) {
6891c0e199f6SAlexander Motin 			t = -2;
6892c0e199f6SAlexander Motin 			break;
6893c0e199f6SAlexander Motin 		}
6894c0e199f6SAlexander Motin 	}
6895c0e199f6SAlexander Motin 	return (t);
68967c6b05d2SAlexander Motin }
68977c6b05d2SAlexander Motin 
68987c6b05d2SAlexander Motin static int
hdaa_sysctl_32bit(SYSCTL_HANDLER_ARGS)68994f240903SAlexander Motin hdaa_sysctl_32bit(SYSCTL_HANDLER_ARGS)
69004f240903SAlexander Motin {
69014f240903SAlexander Motin 	struct hdaa_audio_as *as = (struct hdaa_audio_as *)oidp->oid_arg1;
69024f240903SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo;
69034f240903SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
69044f240903SAlexander Motin 	struct hdaa_chan *ch;
69054f240903SAlexander Motin 	int error, val, i;
69064f240903SAlexander Motin 	uint32_t pcmcap;
69074f240903SAlexander Motin 
69084f240903SAlexander Motin 	ch = &devinfo->chans[as->chans[0]];
69094f240903SAlexander Motin 	val = (ch->bit32 == 4) ? 32 : ((ch->bit32 == 3) ? 24 :
69104f240903SAlexander Motin 	    ((ch->bit32 == 2) ? 20 : 0));
69114f240903SAlexander Motin 	error = sysctl_handle_int(oidp, &val, 0, req);
69124f240903SAlexander Motin 	if (error != 0 || req->newptr == NULL)
69134f240903SAlexander Motin 		return (error);
69144f240903SAlexander Motin 	pcmcap = ch->supp_pcm_size_rate;
69154f240903SAlexander Motin 	if (val == 32 && HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap))
69164f240903SAlexander Motin 		ch->bit32 = 4;
69174f240903SAlexander Motin 	else if (val == 24 && HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap))
69184f240903SAlexander Motin 		ch->bit32 = 3;
69194f240903SAlexander Motin 	else if (val == 20 && HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap))
69204f240903SAlexander Motin 		ch->bit32 = 2;
69214f240903SAlexander Motin 	else
69224f240903SAlexander Motin 		return (EINVAL);
69234f240903SAlexander Motin 	for (i = 1; i < as->num_chans; i++)
69244f240903SAlexander Motin 		devinfo->chans[as->chans[i]].bit32 = ch->bit32;
69254f240903SAlexander Motin 	return (0);
69264f240903SAlexander Motin }
69274f240903SAlexander Motin 
69284f240903SAlexander Motin static int
hdaa_pcm_probe(device_t dev)69297c6b05d2SAlexander Motin hdaa_pcm_probe(device_t dev)
69307c6b05d2SAlexander Motin {
69317c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo =
69327c6b05d2SAlexander Motin 	    (struct hdaa_pcm_devinfo *)device_get_ivars(dev);
69337c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
693452ce0de4SAlexander Motin 	const char *pdesc;
69357c6b05d2SAlexander Motin 	char chans1[8], chans2[8];
6936c0e199f6SAlexander Motin 	int loc1, loc2, t1, t2;
69377c6b05d2SAlexander Motin 
69387c6b05d2SAlexander Motin 	if (pdevinfo->playas >= 0)
69397c6b05d2SAlexander Motin 		loc1 = devinfo->as[pdevinfo->playas].location;
69407c6b05d2SAlexander Motin 	else
69417c6b05d2SAlexander Motin 		loc1 = devinfo->as[pdevinfo->recas].location;
69427c6b05d2SAlexander Motin 	if (pdevinfo->recas >= 0)
69437c6b05d2SAlexander Motin 		loc2 = devinfo->as[pdevinfo->recas].location;
69447c6b05d2SAlexander Motin 	else
69457c6b05d2SAlexander Motin 		loc2 = loc1;
69467c6b05d2SAlexander Motin 	if (loc1 != loc2)
69477c6b05d2SAlexander Motin 		loc1 = -2;
69487c6b05d2SAlexander Motin 	if (loc1 >= 0 && HDA_LOCS[loc1][0] == '0')
69497c6b05d2SAlexander Motin 		loc1 = -2;
69507c6b05d2SAlexander Motin 	chans1[0] = 0;
69517c6b05d2SAlexander Motin 	chans2[0] = 0;
6952c0e199f6SAlexander Motin 	t1 = t2 = -1;
6953c0e199f6SAlexander Motin 	if (pdevinfo->playas >= 0) {
69547c6b05d2SAlexander Motin 		hdaa_chan_formula(devinfo, pdevinfo->playas,
69557c6b05d2SAlexander Motin 		    chans1, sizeof(chans1));
6956c0e199f6SAlexander Motin 		t1 = hdaa_chan_type(devinfo, pdevinfo->playas);
6957c0e199f6SAlexander Motin 	}
6958c0e199f6SAlexander Motin 	if (pdevinfo->recas >= 0) {
69597c6b05d2SAlexander Motin 		hdaa_chan_formula(devinfo, pdevinfo->recas,
69607c6b05d2SAlexander Motin 		    chans2, sizeof(chans2));
6961c0e199f6SAlexander Motin 		t2 = hdaa_chan_type(devinfo, pdevinfo->recas);
6962c0e199f6SAlexander Motin 	}
69637c6b05d2SAlexander Motin 	if (chans1[0] != 0 || chans2[0] != 0) {
69647c6b05d2SAlexander Motin 		if (chans1[0] == 0 && pdevinfo->playas >= 0)
69657c6b05d2SAlexander Motin 			snprintf(chans1, sizeof(chans1), "2.0");
69667c6b05d2SAlexander Motin 		else if (chans2[0] == 0 && pdevinfo->recas >= 0)
69677c6b05d2SAlexander Motin 			snprintf(chans2, sizeof(chans2), "2.0");
69687c6b05d2SAlexander Motin 		if (strcmp(chans1, chans2) == 0)
69697c6b05d2SAlexander Motin 			chans2[0] = 0;
69707c6b05d2SAlexander Motin 	}
6971c0e199f6SAlexander Motin 	if (t1 == -1)
6972c0e199f6SAlexander Motin 		t1 = t2;
6973c0e199f6SAlexander Motin 	else if (t2 == -1)
6974c0e199f6SAlexander Motin 		t2 = t1;
6975c0e199f6SAlexander Motin 	if (t1 != t2)
6976c0e199f6SAlexander Motin 		t1 = -2;
6977c0e199f6SAlexander Motin 	if (pdevinfo->digital)
6978c0e199f6SAlexander Motin 		t1 = -2;
697952ce0de4SAlexander Motin 	pdesc = device_get_desc(device_get_parent(dev));
6980f7d3d0a4SChristos Margiolis 	device_set_descf(dev, "%.*s (%s%s%s%s%s%s%s%s%s)",
698152ce0de4SAlexander Motin 	    (int)(strlen(pdesc) - 21), pdesc,
69827c6b05d2SAlexander Motin 	    loc1 >= 0 ? HDA_LOCS[loc1] : "", loc1 >= 0 ? " " : "",
6983e0f1c0d7SAlexander Motin 	    (pdevinfo->digital == 0x7)?"HDMI/DP":
6984e0f1c0d7SAlexander Motin 	    ((pdevinfo->digital == 0x5)?"DisplayPort":
6985e0f1c0d7SAlexander Motin 	    ((pdevinfo->digital == 0x3)?"HDMI":
6986e0f1c0d7SAlexander Motin 	    ((pdevinfo->digital)?"Digital":"Analog"))),
69877c6b05d2SAlexander Motin 	    chans1[0] ? " " : "", chans1,
6988c0e199f6SAlexander Motin 	    chans2[0] ? "/" : "", chans2,
6989c0e199f6SAlexander Motin 	    t1 >= 0 ? " " : "", t1 >= 0 ? HDA_DEVS[t1] : "");
69907c6b05d2SAlexander Motin 	return (BUS_PROBE_SPECIFIC);
69917c6b05d2SAlexander Motin }
69927c6b05d2SAlexander Motin 
69937c6b05d2SAlexander Motin static int
hdaa_pcm_attach(device_t dev)69947c6b05d2SAlexander Motin hdaa_pcm_attach(device_t dev)
69957c6b05d2SAlexander Motin {
69967c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo =
69977c6b05d2SAlexander Motin 	    (struct hdaa_pcm_devinfo *)device_get_ivars(dev);
69987c6b05d2SAlexander Motin 	struct hdaa_devinfo *devinfo = pdevinfo->devinfo;
69997c6b05d2SAlexander Motin 	struct hdaa_audio_as *as;
70004f240903SAlexander Motin 	struct snddev_info *d;
70017c6b05d2SAlexander Motin 	char status[SND_STATUSLEN];
70027c6b05d2SAlexander Motin 	int i;
70037c6b05d2SAlexander Motin 
70047c6b05d2SAlexander Motin 	pdevinfo->chan_size = pcm_getbuffersize(dev,
70057c6b05d2SAlexander Motin 	    HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX);
70067c6b05d2SAlexander Motin 
70077c6b05d2SAlexander Motin 	HDA_BOOTVERBOSE(
70087c6b05d2SAlexander Motin 		hdaa_dump_dac(pdevinfo);
70097c6b05d2SAlexander Motin 		hdaa_dump_adc(pdevinfo);
70107c6b05d2SAlexander Motin 		hdaa_dump_mix(pdevinfo);
70117c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME);
70127c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM);
70137c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD);
70147c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC);
70157c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR);
70167c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE);
70177c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER);
70187c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV);
70197c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX);
70207c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN);
70217c6b05d2SAlexander Motin 		hdaa_dump_ctls(pdevinfo, NULL, 0);
70227c6b05d2SAlexander Motin 	);
70237c6b05d2SAlexander Motin 
70247c6b05d2SAlexander Motin 	if (resource_int_value(device_get_name(dev),
70257c6b05d2SAlexander Motin 	    device_get_unit(dev), "blocksize", &i) == 0 && i > 0) {
70267c6b05d2SAlexander Motin 		i &= HDA_BLK_ALIGN;
70277c6b05d2SAlexander Motin 		if (i < HDA_BLK_MIN)
70287c6b05d2SAlexander Motin 			i = HDA_BLK_MIN;
70297c6b05d2SAlexander Motin 		pdevinfo->chan_blkcnt = pdevinfo->chan_size / i;
70307c6b05d2SAlexander Motin 		i = 0;
70317c6b05d2SAlexander Motin 		while (pdevinfo->chan_blkcnt >> i)
70327c6b05d2SAlexander Motin 			i++;
70337c6b05d2SAlexander Motin 		pdevinfo->chan_blkcnt = 1 << (i - 1);
70347c6b05d2SAlexander Motin 		if (pdevinfo->chan_blkcnt < HDA_BDL_MIN)
70357c6b05d2SAlexander Motin 			pdevinfo->chan_blkcnt = HDA_BDL_MIN;
70367c6b05d2SAlexander Motin 		else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX)
70377c6b05d2SAlexander Motin 			pdevinfo->chan_blkcnt = HDA_BDL_MAX;
70387c6b05d2SAlexander Motin 	} else
70397c6b05d2SAlexander Motin 		pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT;
70407c6b05d2SAlexander Motin 
70417c6b05d2SAlexander Motin 	/*
70427c6b05d2SAlexander Motin 	 * We don't register interrupt handler with snd_setup_intr
70437c6b05d2SAlexander Motin 	 * in pcm device. Mark pcm device as MPSAFE manually.
70447c6b05d2SAlexander Motin 	 */
70457c6b05d2SAlexander Motin 	pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
70467c6b05d2SAlexander Motin 
70477c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
70487c6b05d2SAlexander Motin 		device_printf(dev, "OSS mixer initialization...\n");
70497c6b05d2SAlexander Motin 	);
70507c6b05d2SAlexander Motin 	if (mixer_init(dev, &hdaa_audio_ctl_ossmixer_class, pdevinfo) != 0)
70517c6b05d2SAlexander Motin 		device_printf(dev, "Can't register mixer\n");
70527c6b05d2SAlexander Motin 
70537c6b05d2SAlexander Motin 	HDA_BOOTHVERBOSE(
70547c6b05d2SAlexander Motin 		device_printf(dev, "Registering PCM channels...\n");
70557c6b05d2SAlexander Motin 	);
7056516a9c02SChristos Margiolis 	pcm_init(dev, pdevinfo);
70577c6b05d2SAlexander Motin 
70587c6b05d2SAlexander Motin 	pdevinfo->registered++;
70597c6b05d2SAlexander Motin 
70604f240903SAlexander Motin 	d = device_get_softc(dev);
70617c6b05d2SAlexander Motin 	if (pdevinfo->playas >= 0) {
70627c6b05d2SAlexander Motin 		as = &devinfo->as[pdevinfo->playas];
70637c6b05d2SAlexander Motin 		for (i = 0; i < as->num_chans; i++)
70647c6b05d2SAlexander Motin 			pcm_addchan(dev, PCMDIR_PLAY, &hdaa_channel_class,
70657c6b05d2SAlexander Motin 			    &devinfo->chans[as->chans[i]]);
70664f240903SAlexander Motin 		SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
70674f240903SAlexander Motin 		    SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO,
70684f240903SAlexander Motin 		    "32bit", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
70694f240903SAlexander Motin 		    as, sizeof(as), hdaa_sysctl_32bit, "I",
70704f240903SAlexander Motin 		    "Resolution of 32bit samples (20/24/32bit)");
70717c6b05d2SAlexander Motin 	}
70727c6b05d2SAlexander Motin 	if (pdevinfo->recas >= 0) {
70737c6b05d2SAlexander Motin 		as = &devinfo->as[pdevinfo->recas];
70747c6b05d2SAlexander Motin 		for (i = 0; i < as->num_chans; i++)
70757c6b05d2SAlexander Motin 			pcm_addchan(dev, PCMDIR_REC, &hdaa_channel_class,
70767c6b05d2SAlexander Motin 			    &devinfo->chans[as->chans[i]]);
70774f240903SAlexander Motin 		SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
70784f240903SAlexander Motin 		    SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO,
70794f240903SAlexander Motin 		    "32bit", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
70804f240903SAlexander Motin 		    as, sizeof(as), hdaa_sysctl_32bit, "I",
70814f240903SAlexander Motin 		    "Resolution of 32bit samples (20/24/32bit)");
7082d9360bbfSAlexander Motin 		pdevinfo->autorecsrc = 2;
70837599ae77SAlexander Motin 		resource_int_value(device_get_name(dev), device_get_unit(dev),
70847599ae77SAlexander Motin 		    "rec.autosrc", &pdevinfo->autorecsrc);
7085d9360bbfSAlexander Motin 		SYSCTL_ADD_INT(&d->rec_sysctl_ctx,
7086d9360bbfSAlexander Motin 		    SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO,
7087f0188618SHans Petter Selasky 		    "autosrc", CTLFLAG_RW,
7088d9360bbfSAlexander Motin 		    &pdevinfo->autorecsrc, 0,
7089d9360bbfSAlexander Motin 		    "Automatic recording source selection");
7090d9360bbfSAlexander Motin 	}
7091d9360bbfSAlexander Motin 
7092d9360bbfSAlexander Motin 	if (pdevinfo->mixer != NULL) {
7093d9360bbfSAlexander Motin 		hdaa_audio_ctl_set_defaults(pdevinfo);
70946d36c821SAlexander Motin 		hdaa_lock(devinfo);
70956d36c821SAlexander Motin 		if (pdevinfo->playas >= 0) {
70966d36c821SAlexander Motin 			as = &devinfo->as[pdevinfo->playas];
70976d36c821SAlexander Motin 			hdaa_channels_handler(as);
70986d36c821SAlexander Motin 		}
7099d9360bbfSAlexander Motin 		if (pdevinfo->recas >= 0) {
7100d9360bbfSAlexander Motin 			as = &devinfo->as[pdevinfo->recas];
7101d9360bbfSAlexander Motin 			hdaa_autorecsrc_handler(as, NULL);
71026d36c821SAlexander Motin 			hdaa_channels_handler(as);
7103d9360bbfSAlexander Motin 		}
71046d36c821SAlexander Motin 		hdaa_unlock(devinfo);
71057c6b05d2SAlexander Motin 	}
71067c6b05d2SAlexander Motin 
7107837cd192SChristos Margiolis 	snprintf(status, SND_STATUSLEN, "on %s",
7108837cd192SChristos Margiolis 	    device_get_nameunit(device_get_parent(dev)));
71097c6b05d2SAlexander Motin 
7110516a9c02SChristos Margiolis 	return (pcm_register(dev, status));
71117c6b05d2SAlexander Motin }
71127c6b05d2SAlexander Motin 
71137c6b05d2SAlexander Motin static int
hdaa_pcm_detach(device_t dev)71147c6b05d2SAlexander Motin hdaa_pcm_detach(device_t dev)
71157c6b05d2SAlexander Motin {
71167c6b05d2SAlexander Motin 	struct hdaa_pcm_devinfo *pdevinfo =
71177c6b05d2SAlexander Motin 	    (struct hdaa_pcm_devinfo *)device_get_ivars(dev);
71187c6b05d2SAlexander Motin 	int err;
71197c6b05d2SAlexander Motin 
71207c6b05d2SAlexander Motin 	if (pdevinfo->registered > 0) {
71217c6b05d2SAlexander Motin 		err = pcm_unregister(dev);
71227c6b05d2SAlexander Motin 		if (err != 0)
71237c6b05d2SAlexander Motin 			return (err);
71247c6b05d2SAlexander Motin 	}
71257c6b05d2SAlexander Motin 
71267c6b05d2SAlexander Motin 	return (0);
71277c6b05d2SAlexander Motin }
71287c6b05d2SAlexander Motin 
71297c6b05d2SAlexander Motin static device_method_t hdaa_pcm_methods[] = {
71307c6b05d2SAlexander Motin 	/* device interface */
71317c6b05d2SAlexander Motin 	DEVMETHOD(device_probe,		hdaa_pcm_probe),
71327c6b05d2SAlexander Motin 	DEVMETHOD(device_attach,	hdaa_pcm_attach),
71337c6b05d2SAlexander Motin 	DEVMETHOD(device_detach,	hdaa_pcm_detach),
7134abe917adSMarius Strobl 	DEVMETHOD_END
71357c6b05d2SAlexander Motin };
71367c6b05d2SAlexander Motin 
71377c6b05d2SAlexander Motin static driver_t hdaa_pcm_driver = {
71387c6b05d2SAlexander Motin 	"pcm",
71397c6b05d2SAlexander Motin 	hdaa_pcm_methods,
71407c6b05d2SAlexander Motin 	PCM_SOFTC_SIZE,
71417c6b05d2SAlexander Motin };
71427c6b05d2SAlexander Motin 
71432287364eSJohn Baldwin DRIVER_MODULE(snd_hda_pcm, hdaa, hdaa_pcm_driver, NULL, NULL);
71447c6b05d2SAlexander Motin MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
71457c6b05d2SAlexander Motin MODULE_VERSION(snd_hda, 1);
7146