xref: /netbsd-src/sys/dev/hdaudio/hdafg.c (revision 5669537f550999e367bd91809ed0e010a0bf286c)
1*5669537fSriastradh /* $NetBSD: hdafg.c,v 1.32 2024/01/29 18:58:54 riastradh Exp $ */
28a9ff04bSjmcneill 
38a9ff04bSjmcneill /*
48a9ff04bSjmcneill  * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk>
58a9ff04bSjmcneill  * Copyright (c) 2009-2011 Jared D. McNeill <jmcneill@invisible.ca>
68a9ff04bSjmcneill  * All rights reserved.
78a9ff04bSjmcneill  *
88a9ff04bSjmcneill  * This code is derived from software contributed to The NetBSD Foundation
98a9ff04bSjmcneill  * by Precedence Technologies Ltd
108a9ff04bSjmcneill  *
118a9ff04bSjmcneill  * Redistribution and use in source and binary forms, with or without
128a9ff04bSjmcneill  * modification, are permitted provided that the following conditions
138a9ff04bSjmcneill  * are met:
148a9ff04bSjmcneill  * 1. Redistributions of source code must retain the above copyright
158a9ff04bSjmcneill  *    notice, this list of conditions and the following disclaimer.
168a9ff04bSjmcneill  * 2. The name of the author may not be used to endorse or promote products
178a9ff04bSjmcneill  *    derived from this software without specific prior written permission.
188a9ff04bSjmcneill  *
198a9ff04bSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
208a9ff04bSjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
218a9ff04bSjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
228a9ff04bSjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
238a9ff04bSjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
248a9ff04bSjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
258a9ff04bSjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
268a9ff04bSjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
278a9ff04bSjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
288a9ff04bSjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
298a9ff04bSjmcneill  * SUCH DAMAGE.
308a9ff04bSjmcneill  */
318a9ff04bSjmcneill 
328a9ff04bSjmcneill /*
338a9ff04bSjmcneill  * Widget parsing from FreeBSD hdac.c:
348a9ff04bSjmcneill  *
358a9ff04bSjmcneill  * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
368a9ff04bSjmcneill  * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
378a9ff04bSjmcneill  * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org>
388a9ff04bSjmcneill  * All rights reserved.
398a9ff04bSjmcneill  *
408a9ff04bSjmcneill  * Redistribution and use in source and binary forms, with or without
418a9ff04bSjmcneill  * modification, are permitted provided that the following conditions
428a9ff04bSjmcneill  * are met:
438a9ff04bSjmcneill  * 1. Redistributions of source code must retain the above copyright
448a9ff04bSjmcneill  *    notice, this list of conditions and the following disclaimer.
458a9ff04bSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
468a9ff04bSjmcneill  *    notice, this list of conditions and the following disclaimer in the
478a9ff04bSjmcneill  *    documentation and/or other materials provided with the distribution.
488a9ff04bSjmcneill  *
498a9ff04bSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
508a9ff04bSjmcneill  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
518a9ff04bSjmcneill  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
528a9ff04bSjmcneill  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
538a9ff04bSjmcneill  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
548a9ff04bSjmcneill  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
558a9ff04bSjmcneill  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
568a9ff04bSjmcneill  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
578a9ff04bSjmcneill  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
588a9ff04bSjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
598a9ff04bSjmcneill  * SUCH DAMAGE.
608a9ff04bSjmcneill  */
618a9ff04bSjmcneill 
628a9ff04bSjmcneill #include <sys/cdefs.h>
63*5669537fSriastradh __KERNEL_RCSID(0, "$NetBSD: hdafg.c,v 1.32 2024/01/29 18:58:54 riastradh Exp $");
648a9ff04bSjmcneill 
658a9ff04bSjmcneill #include <sys/types.h>
668a9ff04bSjmcneill #include <sys/param.h>
678a9ff04bSjmcneill #include <sys/systm.h>
688a9ff04bSjmcneill #include <sys/kernel.h>
698a9ff04bSjmcneill #include <sys/device.h>
708a9ff04bSjmcneill #include <sys/conf.h>
718a9ff04bSjmcneill #include <sys/bus.h>
728a9ff04bSjmcneill #include <sys/kmem.h>
738a9ff04bSjmcneill #include <sys/module.h>
74fdaf53a0Sriastradh #include <sys/condvar.h>
75fdaf53a0Sriastradh #include <sys/kthread.h>
76fdaf53a0Sriastradh #include <sys/mutex.h>
778a9ff04bSjmcneill 
788a9ff04bSjmcneill #include <sys/audioio.h>
79e622eac4Sisaki #include <dev/audio/audio_if.h>
808a9ff04bSjmcneill 
81343fcd6eSjmcneill #ifdef _KERNEL_OPT
82343fcd6eSjmcneill #include "opt_hdaudio.h"
83343fcd6eSjmcneill #endif
848a9ff04bSjmcneill 
858a9ff04bSjmcneill #include "hdaudiovar.h"
868a9ff04bSjmcneill #include "hdaudioreg.h"
878a9ff04bSjmcneill #include "hdaudio_mixer.h"
888a9ff04bSjmcneill #include "hdaudioio.h"
898a9ff04bSjmcneill #include "hdaudio_verbose.h"
908a9ff04bSjmcneill #include "hdaudiodevs.h"
918a9ff04bSjmcneill #include "hdafg_dd.h"
928a9ff04bSjmcneill #include "hdmireg.h"
938a9ff04bSjmcneill 
948a9ff04bSjmcneill #ifndef AUFMT_SURROUND_7_1
958a9ff04bSjmcneill #define AUFMT_SURROUND_7_1 (AUFMT_DOLBY_5_1|AUFMT_SIDE_LEFT|AUFMT_SIDE_RIGHT)
968a9ff04bSjmcneill #endif
978a9ff04bSjmcneill 
988a9ff04bSjmcneill #if defined(HDAFG_DEBUG)
998a9ff04bSjmcneill static int hdafg_debug = HDAFG_DEBUG;
1008a9ff04bSjmcneill #else
1018a9ff04bSjmcneill static int hdafg_debug = 0;
1028a9ff04bSjmcneill #endif
1038a9ff04bSjmcneill 
1048a9ff04bSjmcneill #define hda_debug(sc, ...)		\
1058a9ff04bSjmcneill 	if (hdafg_debug) hda_print(sc, __VA_ARGS__)
1068a9ff04bSjmcneill #define hda_debug1(sc, ...)		\
1078a9ff04bSjmcneill 	if (hdafg_debug) hda_print1(sc, __VA_ARGS__)
1088a9ff04bSjmcneill 
1098a9ff04bSjmcneill #define HDAUDIO_MIXER_CLASS_OUTPUTS	0
1108a9ff04bSjmcneill #define HDAUDIO_MIXER_CLASS_INPUTS	1
1118a9ff04bSjmcneill #define HDAUDIO_MIXER_CLASS_RECORD	2
1128a9ff04bSjmcneill #define HDAUDIO_MIXER_CLASS_LAST	HDAUDIO_MIXER_CLASS_RECORD
1138a9ff04bSjmcneill 
1148a9ff04bSjmcneill #define HDAUDIO_GPIO_MASK	0
1158a9ff04bSjmcneill #define HDAUDIO_GPIO_DIR	1
1168a9ff04bSjmcneill #define HDAUDIO_GPIO_DATA	2
1178a9ff04bSjmcneill 
1188a9ff04bSjmcneill #define HDAUDIO_UNSOLTAG_EVENT_HP	0x01
1198a9ff04bSjmcneill #define HDAUDIO_UNSOLTAG_EVENT_DD	0x02
1208a9ff04bSjmcneill 
1218a9ff04bSjmcneill #define HDAUDIO_HP_SENSE_PERIOD		hz
1228a9ff04bSjmcneill 
1238a9ff04bSjmcneill const u_int hdafg_possible_rates[] = {
1248a9ff04bSjmcneill 	8000, 11025, 16000, 22050, 32000, 44100,
1258a9ff04bSjmcneill 	48000, 88200, 96000, 176500, 192000, /* 384000, */
1268a9ff04bSjmcneill };
1278a9ff04bSjmcneill 
1288a9ff04bSjmcneill static const char *hdafg_mixer_names[] = HDAUDIO_DEVICE_NAMES;
1298a9ff04bSjmcneill 
1308a9ff04bSjmcneill static const char *hdafg_port_connectivity[] = {
1318a9ff04bSjmcneill 	"Jack",
1328a9ff04bSjmcneill 	"Unconnected",
1338a9ff04bSjmcneill 	"Built-In",
1348a9ff04bSjmcneill 	"Jack & Built-In"
1358a9ff04bSjmcneill };
1368a9ff04bSjmcneill static const char *hdafg_default_device[] = {
1378a9ff04bSjmcneill 	"Line Out",
1388a9ff04bSjmcneill 	"Speaker",
1398a9ff04bSjmcneill 	"HP Out",
1408a9ff04bSjmcneill 	"CD",
1418a9ff04bSjmcneill 	"SPDIF Out",
1428a9ff04bSjmcneill 	"Digital Out",
1438a9ff04bSjmcneill 	"Modem Line Side",
1448a9ff04bSjmcneill 	"Modem Handset Side",
1458a9ff04bSjmcneill 	"Line In",
1468a9ff04bSjmcneill 	"AUX",
1478a9ff04bSjmcneill 	"Mic In",
1488a9ff04bSjmcneill 	"Telephony",
1498a9ff04bSjmcneill 	"SPDIF In",
1508a9ff04bSjmcneill 	"Digital In",
1518a9ff04bSjmcneill 	"Reserved",
1528a9ff04bSjmcneill 	"Other"
1538a9ff04bSjmcneill };
1548a9ff04bSjmcneill static const char *hdafg_color[] = {
1558a9ff04bSjmcneill 	"Unknown",
1568a9ff04bSjmcneill 	"Black",
1578a9ff04bSjmcneill 	"Grey",
1588a9ff04bSjmcneill 	"Blue",
1598a9ff04bSjmcneill 	"Green",
1608a9ff04bSjmcneill 	"Red",
1618a9ff04bSjmcneill 	"Orange",
1628a9ff04bSjmcneill 	"Yellow",
1638a9ff04bSjmcneill 	"Purple",
1648a9ff04bSjmcneill 	"Pink",
1658a9ff04bSjmcneill 	"ReservedA",
1668a9ff04bSjmcneill 	"ReservedB",
1678a9ff04bSjmcneill 	"ReservedC",
1688a9ff04bSjmcneill 	"ReservedD",
1698a9ff04bSjmcneill 	"White",
1708a9ff04bSjmcneill 	"Other"
1718a9ff04bSjmcneill };
1728a9ff04bSjmcneill 
1738a9ff04bSjmcneill #define HDAUDIO_MAXFORMATS	24
1748a9ff04bSjmcneill #define HDAUDIO_MAXCONNECTIONS	32
1758a9ff04bSjmcneill #define HDAUDIO_MAXPINS		16
1768a9ff04bSjmcneill #define HDAUDIO_PARSE_MAXDEPTH	10
1778a9ff04bSjmcneill 
1788a9ff04bSjmcneill #define HDAUDIO_AMP_VOL_DEFAULT (-1)
1798a9ff04bSjmcneill #define HDAUDIO_AMP_MUTE_DEFAULT (0xffffffff)
1808a9ff04bSjmcneill #define HDAUDIO_AMP_MUTE_NONE	0
1818a9ff04bSjmcneill #define HDAUDIO_AMP_MUTE_LEFT	(1 << 0)
1828a9ff04bSjmcneill #define HDAUDIO_AMP_MUTE_RIGHT	(1 << 1)
1838a9ff04bSjmcneill #define HDAUDIO_AMP_MUTE_ALL	(HDAUDIO_AMP_MUTE_LEFT | HDAUDIO_AMP_MUTE_RIGHT)
1848a9ff04bSjmcneill #define HDAUDIO_AMP_LEFT_MUTED(x)	((x) & HDAUDIO_AMP_MUTE_LEFT)
1858a9ff04bSjmcneill #define HDAUDIO_AMP_RIGHT_MUTED(x)	(((x) & HDAUDIO_AMP_MUTE_RIGHT) >> 1)
1868a9ff04bSjmcneill 
1878a9ff04bSjmcneill #define HDAUDIO_ADC_MONITOR	1
1888a9ff04bSjmcneill 
1898a9ff04bSjmcneill enum hdaudio_pindir {
1908a9ff04bSjmcneill 	HDAUDIO_PINDIR_NONE = 0,
1918a9ff04bSjmcneill 	HDAUDIO_PINDIR_OUT = 1,
1928a9ff04bSjmcneill 	HDAUDIO_PINDIR_IN = 2,
1938a9ff04bSjmcneill 	HDAUDIO_PINDIR_INOUT = 3,
1948a9ff04bSjmcneill };
1958a9ff04bSjmcneill 
1968a9ff04bSjmcneill #define hda_get_param(sc, cop)					\
1978a9ff04bSjmcneill 	hdaudio_command((sc)->sc_codec, (sc)->sc_nid,		\
1988a9ff04bSjmcneill 	  CORB_GET_PARAMETER, COP_##cop)
1998a9ff04bSjmcneill #define hda_get_wparam(w, cop)					\
2008a9ff04bSjmcneill 	hdaudio_command((w)->w_afg->sc_codec, (w)->w_nid,	\
2018a9ff04bSjmcneill 	  CORB_GET_PARAMETER, COP_##cop)
2028a9ff04bSjmcneill 
2038a9ff04bSjmcneill struct hdaudio_assoc {
2048a9ff04bSjmcneill 	bool			as_enable;
2058a9ff04bSjmcneill 	bool			as_activated;
2068a9ff04bSjmcneill 	u_char			as_index;
2078a9ff04bSjmcneill 	enum hdaudio_pindir	as_dir;
2088a9ff04bSjmcneill 	u_char			as_pincnt;
2098a9ff04bSjmcneill 	u_char			as_fakeredir;
2108a9ff04bSjmcneill 	int			as_digital;
2118a9ff04bSjmcneill #define HDAFG_AS_ANALOG		0
2128a9ff04bSjmcneill #define HDAFG_AS_SPDIF		1
2138a9ff04bSjmcneill #define HDAFG_AS_HDMI		2
2148a9ff04bSjmcneill #define HDAFG_AS_DISPLAYPORT	3
2158a9ff04bSjmcneill 	bool			as_displaydev;
2168a9ff04bSjmcneill 	int			as_hpredir;
2178a9ff04bSjmcneill 	int			as_pins[HDAUDIO_MAXPINS];
2188a9ff04bSjmcneill 	int			as_dacs[HDAUDIO_MAXPINS];
2198a9ff04bSjmcneill };
2208a9ff04bSjmcneill 
2218a9ff04bSjmcneill struct hdaudio_widget {
2228a9ff04bSjmcneill 	struct hdafg_softc	*w_afg;
2238a9ff04bSjmcneill 	char				w_name[32];
2248a9ff04bSjmcneill 	int				w_nid;
2258a9ff04bSjmcneill 	bool				w_enable;
2268a9ff04bSjmcneill 	bool				w_waspin;
2278a9ff04bSjmcneill 	int				w_selconn;
2288a9ff04bSjmcneill 	int				w_bindas;
2298a9ff04bSjmcneill 	int				w_bindseqmask;
2308a9ff04bSjmcneill 	int				w_pflags;
2318a9ff04bSjmcneill 	int				w_audiodev;
2328a9ff04bSjmcneill 	uint32_t			w_audiomask;
2338a9ff04bSjmcneill 
2348a9ff04bSjmcneill 	int				w_nconns;
2358a9ff04bSjmcneill 	int				w_conns[HDAUDIO_MAXCONNECTIONS];
2368a9ff04bSjmcneill 	bool				w_connsenable[HDAUDIO_MAXCONNECTIONS];
2378a9ff04bSjmcneill 
2388a9ff04bSjmcneill 	int				w_type;
2398a9ff04bSjmcneill 	struct {
2408a9ff04bSjmcneill 		uint32_t		aw_cap;
2418a9ff04bSjmcneill 		uint32_t		pcm_size_rate;
2428a9ff04bSjmcneill 		uint32_t		stream_format;
2438a9ff04bSjmcneill 		uint32_t		outamp_cap;
2448a9ff04bSjmcneill 		uint32_t		inamp_cap;
2458a9ff04bSjmcneill 		uint32_t		eapdbtl;
2468a9ff04bSjmcneill 	} w_p;
2478a9ff04bSjmcneill 	struct {
2488a9ff04bSjmcneill 		uint32_t		config;
2498a9ff04bSjmcneill 		uint32_t		biosconfig;
2508a9ff04bSjmcneill 		uint32_t		cap;
2518a9ff04bSjmcneill 		uint32_t		ctrl;
2528a9ff04bSjmcneill 	} w_pin;
2538a9ff04bSjmcneill };
2548a9ff04bSjmcneill 
2558a9ff04bSjmcneill struct hdaudio_control {
2568a9ff04bSjmcneill 	struct hdaudio_widget	*ctl_widget, *ctl_childwidget;
2578a9ff04bSjmcneill 	bool			ctl_enable;
2588a9ff04bSjmcneill 	int			ctl_index;
2598a9ff04bSjmcneill 	enum hdaudio_pindir	ctl_dir, ctl_ndir;
2608a9ff04bSjmcneill 	int			ctl_mute, ctl_step, ctl_size, ctl_offset;
2618a9ff04bSjmcneill 	int			ctl_left, ctl_right, ctl_forcemute;
2628a9ff04bSjmcneill 	uint32_t		ctl_muted;
2638a9ff04bSjmcneill 	uint32_t		ctl_audiomask, ctl_paudiomask;
2648a9ff04bSjmcneill };
2658a9ff04bSjmcneill 
2668a9ff04bSjmcneill #define HDAUDIO_CONTROL_GIVE(ctl)	((ctl)->ctl_step ? 1 : 0)
2678a9ff04bSjmcneill 
2688a9ff04bSjmcneill struct hdaudio_mixer {
2698a9ff04bSjmcneill 	struct hdaudio_control		*mx_ctl;
2708a9ff04bSjmcneill 	mixer_devinfo_t			mx_di;
2718a9ff04bSjmcneill };
2728a9ff04bSjmcneill 
2738a9ff04bSjmcneill struct hdaudio_audiodev {
2748a9ff04bSjmcneill 	struct hdafg_softc	*ad_sc;
2758a9ff04bSjmcneill 	device_t			ad_audiodev;
2768a9ff04bSjmcneill 	int				ad_nformats;
2778a9ff04bSjmcneill 	struct audio_format		ad_formats[HDAUDIO_MAXFORMATS];
2788a9ff04bSjmcneill 
2798a9ff04bSjmcneill 	struct hdaudio_stream		*ad_playback;
2808a9ff04bSjmcneill 	void				(*ad_playbackintr)(void *);
2818a9ff04bSjmcneill 	void				*ad_playbackintrarg;
2828a9ff04bSjmcneill 	int				ad_playbacknid[HDAUDIO_MAXPINS];
2838a9ff04bSjmcneill 	struct hdaudio_assoc		*ad_playbackassoc;
2848a9ff04bSjmcneill 	struct hdaudio_stream		*ad_capture;
2858a9ff04bSjmcneill 	void				(*ad_captureintr)(void *);
2868a9ff04bSjmcneill 	void				*ad_captureintrarg;
2878a9ff04bSjmcneill 	int				ad_capturenid[HDAUDIO_MAXPINS];
2888a9ff04bSjmcneill 	struct hdaudio_assoc		*ad_captureassoc;
2898a9ff04bSjmcneill };
2908a9ff04bSjmcneill 
2918a9ff04bSjmcneill struct hdafg_softc {
2928a9ff04bSjmcneill 	device_t			sc_dev;
2938a9ff04bSjmcneill 	kmutex_t			sc_lock;
2948a9ff04bSjmcneill 	kmutex_t			sc_intr_lock;
2958a9ff04bSjmcneill 	struct hdaudio_softc		*sc_host;
2968a9ff04bSjmcneill 	struct hdaudio_codec		*sc_codec;
2978a9ff04bSjmcneill 	struct hdaudio_function_group	*sc_fg;
2988a9ff04bSjmcneill 	int				sc_nid;
2998a9ff04bSjmcneill 	uint16_t			sc_vendor, sc_product;
3008a9ff04bSjmcneill 
3018a9ff04bSjmcneill 	prop_array_t			sc_config;
3028a9ff04bSjmcneill 
3038a9ff04bSjmcneill 	int				sc_startnode, sc_endnode;
3048a9ff04bSjmcneill 	int				sc_nwidgets;
3058a9ff04bSjmcneill 	struct hdaudio_widget		*sc_widgets;
3068a9ff04bSjmcneill 	int				sc_nassocs;
3078a9ff04bSjmcneill 	struct hdaudio_assoc		*sc_assocs;
3088a9ff04bSjmcneill 	int				sc_nctls;
3098a9ff04bSjmcneill 	struct hdaudio_control		*sc_ctls;
3108a9ff04bSjmcneill 	int				sc_nmixers;
3118a9ff04bSjmcneill 	struct hdaudio_mixer		*sc_mixers;
3128a9ff04bSjmcneill 	bool				sc_has_beepgen;
3138a9ff04bSjmcneill 
3148a9ff04bSjmcneill 	int				sc_pchan, sc_rchan;
3158a9ff04bSjmcneill 	audio_params_t			sc_pparam, sc_rparam;
3168a9ff04bSjmcneill 
317fdaf53a0Sriastradh 	kmutex_t			sc_jack_lock;
318fdaf53a0Sriastradh 	kcondvar_t			sc_jack_cv;
319fdaf53a0Sriastradh 	struct lwp			*sc_jack_thread;
3208a9ff04bSjmcneill 	bool				sc_jack_polling;
321fdaf53a0Sriastradh 	bool				sc_jack_suspended;
322fdaf53a0Sriastradh 	bool				sc_jack_dying;
3238a9ff04bSjmcneill 
3248a9ff04bSjmcneill 	struct {
3258a9ff04bSjmcneill 		uint32_t		afg_cap;
3268a9ff04bSjmcneill 		uint32_t		pcm_size_rate;
3278a9ff04bSjmcneill 		uint32_t		stream_format;
3288a9ff04bSjmcneill 		uint32_t		outamp_cap;
3298a9ff04bSjmcneill 		uint32_t		inamp_cap;
3308a9ff04bSjmcneill 		uint32_t		power_states;
3318a9ff04bSjmcneill 		uint32_t		gpio_cnt;
3328a9ff04bSjmcneill 	} sc_p;
3338a9ff04bSjmcneill 
3348a9ff04bSjmcneill 	struct hdaudio_audiodev		sc_audiodev;
3358e864669Sjmcneill 
3368e864669Sjmcneill 	uint16_t			sc_fixed_rate;
337a85a8464Sjmcneill 	bool				sc_disable_dip;
3380519d6fcSmlelstv 
3390519d6fcSmlelstv 	char				sc_name[MAX_AUDIO_DEV_LEN];
3400519d6fcSmlelstv 	char				sc_version[MAX_AUDIO_DEV_LEN];
3418a9ff04bSjmcneill };
3428a9ff04bSjmcneill 
3438a9ff04bSjmcneill static int	hdafg_match(device_t, cfdata_t, void *);
3448a9ff04bSjmcneill static void	hdafg_attach(device_t, device_t, void *);
3458a9ff04bSjmcneill static int	hdafg_detach(device_t, int);
3468a9ff04bSjmcneill static void	hdafg_childdet(device_t, device_t);
3478a9ff04bSjmcneill static bool	hdafg_suspend(device_t, const pmf_qual_t *);
3488a9ff04bSjmcneill static bool	hdafg_resume(device_t, const pmf_qual_t *);
3498a9ff04bSjmcneill 
3508a9ff04bSjmcneill static int	hdafg_unsol(device_t, uint8_t);
3518a9ff04bSjmcneill static int	hdafg_widget_info(void *, prop_dictionary_t,
3528a9ff04bSjmcneill 					prop_dictionary_t);
3538a9ff04bSjmcneill static int	hdafg_codec_info(void *, prop_dictionary_t,
3548a9ff04bSjmcneill 				       prop_dictionary_t);
3558a9ff04bSjmcneill static void	hdafg_enable_analog_beep(struct hdafg_softc *);
3568a9ff04bSjmcneill 
3578a9ff04bSjmcneill CFATTACH_DECL2_NEW(
3588a9ff04bSjmcneill     hdafg,
3598a9ff04bSjmcneill     sizeof(struct hdafg_softc),
3608a9ff04bSjmcneill     hdafg_match,
3618a9ff04bSjmcneill     hdafg_attach,
3628a9ff04bSjmcneill     hdafg_detach,
3638a9ff04bSjmcneill     NULL,
3648a9ff04bSjmcneill     NULL,
3658a9ff04bSjmcneill     hdafg_childdet
3668a9ff04bSjmcneill );
3678a9ff04bSjmcneill 
368e622eac4Sisaki static int	hdafg_query_format(void *, audio_format_query_t *);
369e622eac4Sisaki static int	hdafg_set_format(void *, int,
370e622eac4Sisaki 				   const audio_params_t *,
371e622eac4Sisaki 				   const audio_params_t *,
372e622eac4Sisaki 				   audio_filter_reg_t *,
373e622eac4Sisaki 				   audio_filter_reg_t *);
3748a9ff04bSjmcneill static int	hdafg_round_blocksize(void *, int, int,
3758a9ff04bSjmcneill 					const audio_params_t *);
3768a9ff04bSjmcneill static int	hdafg_commit_settings(void *);
3778a9ff04bSjmcneill static int	hdafg_halt_output(void *);
3788a9ff04bSjmcneill static int	hdafg_halt_input(void *);
3798a9ff04bSjmcneill static int	hdafg_set_port(void *, mixer_ctrl_t *);
3808a9ff04bSjmcneill static int	hdafg_get_port(void *, mixer_ctrl_t *);
3818a9ff04bSjmcneill static int	hdafg_query_devinfo(void *, mixer_devinfo_t *);
3828a9ff04bSjmcneill static void *	hdafg_allocm(void *, int, size_t);
3838a9ff04bSjmcneill static void	hdafg_freem(void *, void *, size_t);
3848a9ff04bSjmcneill static int	hdafg_getdev(void *, struct audio_device *);
3858a9ff04bSjmcneill static int	hdafg_get_props(void *);
3868a9ff04bSjmcneill static int	hdafg_trigger_output(void *, void *, void *, int,
3878a9ff04bSjmcneill 				       void (*)(void *), void *,
3888a9ff04bSjmcneill 				       const audio_params_t *);
3898a9ff04bSjmcneill static int	hdafg_trigger_input(void *, void *, void *, int,
3908a9ff04bSjmcneill 				      void (*)(void *), void *,
3918a9ff04bSjmcneill 				      const audio_params_t *);
3928a9ff04bSjmcneill static void	hdafg_get_locks(void *, kmutex_t **, kmutex_t **);
3938a9ff04bSjmcneill 
3948a9ff04bSjmcneill static const struct audio_hw_if hdafg_hw_if = {
395e622eac4Sisaki 	.query_format		= hdafg_query_format,
396e622eac4Sisaki 	.set_format		= hdafg_set_format,
3978a9ff04bSjmcneill 	.round_blocksize	= hdafg_round_blocksize,
3988a9ff04bSjmcneill 	.commit_settings	= hdafg_commit_settings,
3998a9ff04bSjmcneill 	.halt_output		= hdafg_halt_output,
4008a9ff04bSjmcneill 	.halt_input		= hdafg_halt_input,
4018a9ff04bSjmcneill 	.getdev			= hdafg_getdev,
4028a9ff04bSjmcneill 	.set_port		= hdafg_set_port,
4038a9ff04bSjmcneill 	.get_port		= hdafg_get_port,
4048a9ff04bSjmcneill 	.query_devinfo		= hdafg_query_devinfo,
4058a9ff04bSjmcneill 	.allocm			= hdafg_allocm,
4068a9ff04bSjmcneill 	.freem			= hdafg_freem,
4078a9ff04bSjmcneill 	.get_props		= hdafg_get_props,
4088a9ff04bSjmcneill 	.trigger_output		= hdafg_trigger_output,
4098a9ff04bSjmcneill 	.trigger_input		= hdafg_trigger_input,
4108a9ff04bSjmcneill 	.get_locks		= hdafg_get_locks,
4118a9ff04bSjmcneill };
4128a9ff04bSjmcneill 
4138a9ff04bSjmcneill static int
hdafg_append_formats(struct hdaudio_audiodev * ad,const struct audio_format * format)4148a9ff04bSjmcneill hdafg_append_formats(struct hdaudio_audiodev *ad,
4158a9ff04bSjmcneill     const struct audio_format *format)
4168a9ff04bSjmcneill {
4178a9ff04bSjmcneill 	if (ad->ad_nformats + 1 >= HDAUDIO_MAXFORMATS) {
4188a9ff04bSjmcneill 		hda_print1(ad->ad_sc, "[ENOMEM] ");
4198a9ff04bSjmcneill 		return ENOMEM;
4208a9ff04bSjmcneill 	}
4218a9ff04bSjmcneill 	ad->ad_formats[ad->ad_nformats++] = *format;
4228a9ff04bSjmcneill 
4238a9ff04bSjmcneill 	return 0;
4248a9ff04bSjmcneill }
4258a9ff04bSjmcneill 
4268a9ff04bSjmcneill static struct hdaudio_widget *
hdafg_widget_lookup(struct hdafg_softc * sc,int nid)4278a9ff04bSjmcneill hdafg_widget_lookup(struct hdafg_softc *sc, int nid)
4288a9ff04bSjmcneill {
4298a9ff04bSjmcneill 	if (sc->sc_widgets == NULL || sc->sc_nwidgets == 0) {
4308a9ff04bSjmcneill 		hda_error(sc, "lookup failed; widgets %p nwidgets %d\n",
4318a9ff04bSjmcneill 		    sc->sc_widgets, sc->sc_nwidgets);
4328a9ff04bSjmcneill 		return NULL;
4338a9ff04bSjmcneill 	}
4348a9ff04bSjmcneill 	if (nid < sc->sc_startnode || nid >= sc->sc_endnode) {
4358a9ff04bSjmcneill 		hda_debug(sc, "nid %02X out of range (%02X-%02X)\n",
4368a9ff04bSjmcneill 		    nid, sc->sc_startnode, sc->sc_endnode);
4378a9ff04bSjmcneill 		return NULL;
4388a9ff04bSjmcneill 	}
4398a9ff04bSjmcneill 	return &sc->sc_widgets[nid - sc->sc_startnode];
4408a9ff04bSjmcneill }
4418a9ff04bSjmcneill 
4428a9ff04bSjmcneill static struct hdaudio_control *
hdafg_control_lookup(struct hdafg_softc * sc,int nid,enum hdaudio_pindir dir,int index,int cnt)4438a9ff04bSjmcneill hdafg_control_lookup(struct hdafg_softc *sc, int nid,
4448a9ff04bSjmcneill     enum hdaudio_pindir dir, int index, int cnt)
4458a9ff04bSjmcneill {
4468a9ff04bSjmcneill 	struct hdaudio_control *ctl;
4478a9ff04bSjmcneill 	int i, found = 0;
4488a9ff04bSjmcneill 
4498a9ff04bSjmcneill 	if (sc->sc_ctls == NULL)
4508a9ff04bSjmcneill 		return NULL;
4518a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
4528a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
4538a9ff04bSjmcneill 		if (ctl->ctl_enable == false)
4548a9ff04bSjmcneill 			continue;
4558a9ff04bSjmcneill 		if (ctl->ctl_widget->w_nid != nid)
4568a9ff04bSjmcneill 			continue;
4578a9ff04bSjmcneill 		if (dir && ctl->ctl_ndir != dir)
4588a9ff04bSjmcneill 			continue;
4598a9ff04bSjmcneill 		if (index >= 0 && ctl->ctl_ndir == HDAUDIO_PINDIR_IN &&
4608a9ff04bSjmcneill 		    ctl->ctl_dir == ctl->ctl_ndir && ctl->ctl_index != index)
4618a9ff04bSjmcneill 			continue;
4628a9ff04bSjmcneill 		found++;
4638a9ff04bSjmcneill 		if (found == cnt || cnt <= 0)
4648a9ff04bSjmcneill 			return ctl;
4658a9ff04bSjmcneill 	}
4668a9ff04bSjmcneill 
4678a9ff04bSjmcneill 	return NULL;
4688a9ff04bSjmcneill }
4698a9ff04bSjmcneill 
4708a9ff04bSjmcneill static void
hdafg_widget_connection_parse(struct hdaudio_widget * w)4718a9ff04bSjmcneill hdafg_widget_connection_parse(struct hdaudio_widget *w)
4728a9ff04bSjmcneill {
4738a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
4748a9ff04bSjmcneill 	uint32_t res;
4758a9ff04bSjmcneill 	int i, j, maxconns, ents, entnum;
4768a9ff04bSjmcneill 	int cnid, addcnid, prevcnid;
4778a9ff04bSjmcneill 
4788a9ff04bSjmcneill 	w->w_nconns = 0;
4798a9ff04bSjmcneill 
4808a9ff04bSjmcneill 	res = hda_get_wparam(w, CONNECTION_LIST_LENGTH);
4818a9ff04bSjmcneill 	ents = COP_CONNECTION_LIST_LENGTH_LEN(res);
4828a9ff04bSjmcneill 	if (ents < 1)
4838a9ff04bSjmcneill 		return;
4848a9ff04bSjmcneill 	if (res & COP_CONNECTION_LIST_LENGTH_LONG_FORM)
4858a9ff04bSjmcneill 		entnum = 2;
4868a9ff04bSjmcneill 	else
4878a9ff04bSjmcneill 		entnum = 4;
4888a9ff04bSjmcneill 	maxconns = (sizeof(w->w_conns) / sizeof(w->w_conns[0])) - 1;
4898a9ff04bSjmcneill 	prevcnid = 0;
4908a9ff04bSjmcneill 
4918a9ff04bSjmcneill #define CONN_RMASK(e)		(1 << ((32 / (e)) - 1))
4928a9ff04bSjmcneill #define CONN_NMASK(e)		(CONN_RMASK(e) - 1)
4938a9ff04bSjmcneill #define CONN_RESVAL(r, e, n)	((r) >> ((32 / (e)) * (n)))
4948a9ff04bSjmcneill #define CONN_RANGE(r, e, n)	(CONN_RESVAL(r, e, n) & CONN_RMASK(e))
4958a9ff04bSjmcneill #define CONN_CNID(r, e, n)	(CONN_RESVAL(r, e, n) & CONN_NMASK(e))
4968a9ff04bSjmcneill 
4978a9ff04bSjmcneill 	for (i = 0; i < ents; i += entnum) {
4988a9ff04bSjmcneill 		res = hdaudio_command(sc->sc_codec, w->w_nid,
4998a9ff04bSjmcneill 		    CORB_GET_CONNECTION_LIST_ENTRY, i);
5008a9ff04bSjmcneill 		for (j = 0; j < entnum; j++) {
5018a9ff04bSjmcneill 			cnid = CONN_CNID(res, entnum, j);
5028a9ff04bSjmcneill 			if (cnid == 0) {
5038a9ff04bSjmcneill 				if (w->w_nconns < ents) {
5048a9ff04bSjmcneill 					hda_error(sc, "WARNING: zero cnid\n");
5058a9ff04bSjmcneill 				} else {
5068a9ff04bSjmcneill 					goto getconns_out;
5078a9ff04bSjmcneill 				}
5088a9ff04bSjmcneill 			}
5098a9ff04bSjmcneill 			if (cnid < sc->sc_startnode || cnid >= sc->sc_endnode)
5108a9ff04bSjmcneill 				hda_debug(sc, "ghost nid=%02X\n", cnid);
5118a9ff04bSjmcneill 			if (CONN_RANGE(res, entnum, j) == 0)
5128a9ff04bSjmcneill 				addcnid = cnid;
5138a9ff04bSjmcneill 			else if (prevcnid == 0 || prevcnid >= cnid) {
5148a9ff04bSjmcneill 				hda_error(sc, "invalid child range\n");
5158a9ff04bSjmcneill 				addcnid = cnid;
5168a9ff04bSjmcneill 			} else
5178a9ff04bSjmcneill 				addcnid = prevcnid + 1;
5188a9ff04bSjmcneill 			while (addcnid <= cnid) {
5198a9ff04bSjmcneill 				if (w->w_nconns > maxconns) {
5208a9ff04bSjmcneill 					hda_error(sc,
5218a9ff04bSjmcneill 					    "max connections reached\n");
5228a9ff04bSjmcneill 					goto getconns_out;
5238a9ff04bSjmcneill 				}
5248a9ff04bSjmcneill 				w->w_connsenable[w->w_nconns] = true;
5258a9ff04bSjmcneill 				w->w_conns[w->w_nconns++] = addcnid++;
5268a9ff04bSjmcneill 				hda_trace(sc, "add connection %02X->%02X\n",
5278a9ff04bSjmcneill 				    w->w_nid, addcnid - 1);
5288a9ff04bSjmcneill 			}
5298a9ff04bSjmcneill 			prevcnid = cnid;
5308a9ff04bSjmcneill 		}
5318a9ff04bSjmcneill 	}
5328a9ff04bSjmcneill #undef CONN_RMASK
5338a9ff04bSjmcneill #undef CONN_NMASK
5348a9ff04bSjmcneill #undef CONN_RESVAL
5358a9ff04bSjmcneill #undef CONN_RANGE
5368a9ff04bSjmcneill #undef CONN_CNID
5378a9ff04bSjmcneill 
5388a9ff04bSjmcneill getconns_out:
5398a9ff04bSjmcneill 	return;
5408a9ff04bSjmcneill }
5418a9ff04bSjmcneill 
5428a9ff04bSjmcneill static void
hdafg_widget_pin_dump(struct hdafg_softc * sc)5438a9ff04bSjmcneill hdafg_widget_pin_dump(struct hdafg_softc *sc)
5448a9ff04bSjmcneill {
5458a9ff04bSjmcneill 	struct hdaudio_widget *w;
5468a9ff04bSjmcneill 	int i, conn;
5478a9ff04bSjmcneill 
5488a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
5498a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
5508a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
5518a9ff04bSjmcneill 			continue;
5528a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
5538a9ff04bSjmcneill 			continue;
5548a9ff04bSjmcneill 		conn = COP_CFG_PORT_CONNECTIVITY(w->w_pin.config);
5558a9ff04bSjmcneill 		if (conn != 1) {
5568a9ff04bSjmcneill #ifdef HDAUDIO_DEBUG
5578a9ff04bSjmcneill 			int color = COP_CFG_COLOR(w->w_pin.config);
5588a9ff04bSjmcneill 			int defdev = COP_CFG_DEFAULT_DEVICE(w->w_pin.config);
5598a9ff04bSjmcneill 			hda_trace(sc, "io %02X: %s (%s, %s)\n",
5608a9ff04bSjmcneill 			    w->w_nid,
5618a9ff04bSjmcneill 			    hdafg_default_device[defdev],
5628a9ff04bSjmcneill 			    hdafg_color[color],
5638a9ff04bSjmcneill 			    hdafg_port_connectivity[conn]);
5648a9ff04bSjmcneill #endif
5658a9ff04bSjmcneill 		}
5668a9ff04bSjmcneill 	}
5678a9ff04bSjmcneill }
5688a9ff04bSjmcneill 
5698a9ff04bSjmcneill static void
hdafg_widget_setconfig(struct hdaudio_widget * w,uint32_t cfg)5708a9ff04bSjmcneill hdafg_widget_setconfig(struct hdaudio_widget *w, uint32_t cfg)
5718a9ff04bSjmcneill {
5728a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
5738a9ff04bSjmcneill 
5748a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
5758a9ff04bSjmcneill 	    CORB_SET_CONFIGURATION_DEFAULT_1, (cfg >>  0) & 0xff);
5768a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
5778a9ff04bSjmcneill 	    CORB_SET_CONFIGURATION_DEFAULT_2, (cfg >>  8) & 0xff);
5788a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
5798a9ff04bSjmcneill 	    CORB_SET_CONFIGURATION_DEFAULT_3, (cfg >> 16) & 0xff);
5808a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
5818a9ff04bSjmcneill 	    CORB_SET_CONFIGURATION_DEFAULT_4, (cfg >> 24) & 0xff);
5828a9ff04bSjmcneill }
5838a9ff04bSjmcneill 
5848a9ff04bSjmcneill static uint32_t
hdafg_widget_getconfig(struct hdaudio_widget * w)5858a9ff04bSjmcneill hdafg_widget_getconfig(struct hdaudio_widget *w)
5868a9ff04bSjmcneill {
5878a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
5888a9ff04bSjmcneill 	uint32_t config = 0;
5898a9ff04bSjmcneill 	prop_object_iterator_t iter;
5908a9ff04bSjmcneill 	prop_dictionary_t dict;
5918a9ff04bSjmcneill 	prop_object_t obj;
5928a9ff04bSjmcneill 	int16_t nid;
5938a9ff04bSjmcneill 
5948a9ff04bSjmcneill 	if (sc->sc_config == NULL)
5958a9ff04bSjmcneill 		goto biosconfig;
5968a9ff04bSjmcneill 
5978a9ff04bSjmcneill 	iter = prop_array_iterator(sc->sc_config);
5988a9ff04bSjmcneill 	if (iter == NULL)
5998a9ff04bSjmcneill 		goto biosconfig;
6008a9ff04bSjmcneill 	prop_object_iterator_reset(iter);
6018a9ff04bSjmcneill 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
6028a9ff04bSjmcneill 		if (prop_object_type(obj) != PROP_TYPE_DICTIONARY)
6038a9ff04bSjmcneill 			continue;
6048a9ff04bSjmcneill 		dict = (prop_dictionary_t)obj;
6058a9ff04bSjmcneill 		if (!prop_dictionary_get_int16(dict, "nid", &nid) ||
6068a9ff04bSjmcneill 		    !prop_dictionary_get_uint32(dict, "config", &config))
6078a9ff04bSjmcneill 			continue;
6088a9ff04bSjmcneill 		if (nid == w->w_nid)
6098a9ff04bSjmcneill 			return config;
6108a9ff04bSjmcneill 	}
6118a9ff04bSjmcneill 
6128a9ff04bSjmcneill biosconfig:
6138a9ff04bSjmcneill 	return hdaudio_command(sc->sc_codec, w->w_nid,
6148a9ff04bSjmcneill 	    CORB_GET_CONFIGURATION_DEFAULT, 0);
6158a9ff04bSjmcneill }
6168a9ff04bSjmcneill 
6178a9ff04bSjmcneill static void
hdafg_widget_pin_parse(struct hdaudio_widget * w)6188a9ff04bSjmcneill hdafg_widget_pin_parse(struct hdaudio_widget *w)
6198a9ff04bSjmcneill {
6208a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
6218a9ff04bSjmcneill 	int conn, color, defdev;
6228a9ff04bSjmcneill 
6238a9ff04bSjmcneill 	w->w_pin.cap = hda_get_wparam(w, PIN_CAPABILITIES);
6248a9ff04bSjmcneill 	w->w_pin.config = hdafg_widget_getconfig(w);
6258a9ff04bSjmcneill 	w->w_pin.biosconfig = hdaudio_command(sc->sc_codec, w->w_nid,
6268a9ff04bSjmcneill 	    CORB_GET_CONFIGURATION_DEFAULT, 0);
6278a9ff04bSjmcneill 	w->w_pin.ctrl = hdaudio_command(sc->sc_codec, w->w_nid,
6288a9ff04bSjmcneill 	    CORB_GET_PIN_WIDGET_CONTROL, 0);
6298a9ff04bSjmcneill 
6308a9ff04bSjmcneill 	/* treat line-out as speaker, unless connection type is RCA */
6318a9ff04bSjmcneill 	if (COP_CFG_DEFAULT_DEVICE(w->w_pin.config) == COP_DEVICE_LINE_OUT &&
6328a9ff04bSjmcneill 	    COP_CFG_CONNECTION_TYPE(w->w_pin.config) != COP_CONN_TYPE_RCA) {
6338a9ff04bSjmcneill 		w->w_pin.config &= ~COP_DEVICE_MASK;
6348a9ff04bSjmcneill 		w->w_pin.config |= (COP_DEVICE_SPEAKER << COP_DEVICE_SHIFT);
6358a9ff04bSjmcneill 	}
6368a9ff04bSjmcneill 
6378a9ff04bSjmcneill 	if (w->w_pin.cap & COP_PINCAP_EAPD_CAPABLE) {
6388a9ff04bSjmcneill 		w->w_p.eapdbtl = hdaudio_command(sc->sc_codec, w->w_nid,
6398a9ff04bSjmcneill 		    CORB_GET_EAPD_BTL_ENABLE, 0);
6408a9ff04bSjmcneill 		w->w_p.eapdbtl &= 0x7;
6418a9ff04bSjmcneill 		w->w_p.eapdbtl |= COP_EAPD_ENABLE_EAPD;
6428a9ff04bSjmcneill 	} else
6438a9ff04bSjmcneill 		w->w_p.eapdbtl = 0xffffffff;
6448a9ff04bSjmcneill 
6458a9ff04bSjmcneill #if 0
6468a9ff04bSjmcneill 	/* XXX VT1708 */
6478a9ff04bSjmcneill 	if (COP_CFG_DEFAULT_DEVICE(w->w_pin.config) == COP_DEVICE_SPEAKER &&
6488a9ff04bSjmcneill 	    COP_CFG_DEFAULT_ASSOCIATION(w->w_pin.config) == 15) {
6498a9ff04bSjmcneill 		hda_trace(sc, "forcing speaker nid %02X to assoc=14\n",
6508a9ff04bSjmcneill 		    w->w_nid);
6518a9ff04bSjmcneill 		/* set assoc=14 */
6528a9ff04bSjmcneill 		w->w_pin.config &= ~0xf0;
6538a9ff04bSjmcneill 		w->w_pin.config |= 0xe0;
6548a9ff04bSjmcneill 	}
6558a9ff04bSjmcneill 	if (COP_CFG_DEFAULT_DEVICE(w->w_pin.config) == COP_DEVICE_HP_OUT &&
6568a9ff04bSjmcneill 	    COP_CFG_PORT_CONNECTIVITY(w->w_pin.config) == COP_PORT_NONE) {
6578a9ff04bSjmcneill 		hda_trace(sc, "forcing hp out nid %02X to assoc=14\n",
6588a9ff04bSjmcneill 		    w->w_nid);
6598a9ff04bSjmcneill 		/* set connectivity to 'jack' */
6608a9ff04bSjmcneill 		w->w_pin.config &= ~(COP_PORT_BOTH << 30);
6618a9ff04bSjmcneill 		w->w_pin.config |= (COP_PORT_JACK << 30);
6628a9ff04bSjmcneill 		/* set seq=15 */
6638a9ff04bSjmcneill 		w->w_pin.config &= ~0xf;
6648a9ff04bSjmcneill 		w->w_pin.config |= 15;
6658a9ff04bSjmcneill 		/* set assoc=14 */
6668a9ff04bSjmcneill 		w->w_pin.config &= ~0xf0;
6678a9ff04bSjmcneill 		w->w_pin.config |= 0xe0;
6688a9ff04bSjmcneill 	}
6698a9ff04bSjmcneill #endif
6708a9ff04bSjmcneill 
6718a9ff04bSjmcneill 	conn = COP_CFG_PORT_CONNECTIVITY(w->w_pin.config);
6728a9ff04bSjmcneill 	color = COP_CFG_COLOR(w->w_pin.config);
6738a9ff04bSjmcneill 	defdev = COP_CFG_DEFAULT_DEVICE(w->w_pin.config);
6748a9ff04bSjmcneill 
6758a9ff04bSjmcneill 	strlcat(w->w_name, ": ", sizeof(w->w_name));
6768a9ff04bSjmcneill 	strlcat(w->w_name, hdafg_default_device[defdev], sizeof(w->w_name));
6778a9ff04bSjmcneill 	strlcat(w->w_name, " (", sizeof(w->w_name));
6788a9ff04bSjmcneill 	if (conn == 0 && color != 0 && color != 15) {
6798a9ff04bSjmcneill 		strlcat(w->w_name, hdafg_color[color], sizeof(w->w_name));
6808a9ff04bSjmcneill 		strlcat(w->w_name, " ", sizeof(w->w_name));
6818a9ff04bSjmcneill 	}
6828a9ff04bSjmcneill 	strlcat(w->w_name, hdafg_port_connectivity[conn], sizeof(w->w_name));
6838a9ff04bSjmcneill 	strlcat(w->w_name, ")", sizeof(w->w_name));
6848a9ff04bSjmcneill }
6858a9ff04bSjmcneill 
6868a9ff04bSjmcneill static uint32_t
hdafg_widget_getcaps(struct hdaudio_widget * w)6878a9ff04bSjmcneill hdafg_widget_getcaps(struct hdaudio_widget *w)
6888a9ff04bSjmcneill {
6898a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
6908a9ff04bSjmcneill 	uint32_t wcap, config;
6918a9ff04bSjmcneill 	bool pcbeep = false;
6928a9ff04bSjmcneill 
6938a9ff04bSjmcneill 	wcap = hda_get_wparam(w, AUDIO_WIDGET_CAPABILITIES);
6948a9ff04bSjmcneill 	config = hdafg_widget_getconfig(w);
6958a9ff04bSjmcneill 
6968a9ff04bSjmcneill 	w->w_waspin = false;
6978a9ff04bSjmcneill 
6988a9ff04bSjmcneill 	switch (sc->sc_vendor) {
6998a9ff04bSjmcneill 	case HDAUDIO_VENDOR_ANALOG:
7008a9ff04bSjmcneill 		/*
7018a9ff04bSjmcneill 		 * help the parser by marking the analog
7028a9ff04bSjmcneill 		 * beeper as a beep generator
7038a9ff04bSjmcneill 		 */
7048a9ff04bSjmcneill 		if (w->w_nid == 0x1a &&
7058a9ff04bSjmcneill 		    COP_CFG_SEQUENCE(config) == 0x0 &&
7068a9ff04bSjmcneill 		    COP_CFG_DEFAULT_ASSOCIATION(config) == 0xf &&
7078a9ff04bSjmcneill 		    COP_CFG_PORT_CONNECTIVITY(config) ==
7088a9ff04bSjmcneill 		      COP_PORT_FIXED_FUNCTION &&
7098a9ff04bSjmcneill 		    COP_CFG_DEFAULT_DEVICE(config) ==
7108a9ff04bSjmcneill 		      COP_DEVICE_OTHER) {
7118a9ff04bSjmcneill 			pcbeep = true;
7128a9ff04bSjmcneill 		}
7138a9ff04bSjmcneill 		break;
7148a9ff04bSjmcneill 	}
7158a9ff04bSjmcneill 
7168a9ff04bSjmcneill 	if (pcbeep ||
7178a9ff04bSjmcneill 	    (sc->sc_has_beepgen == false &&
7188a9ff04bSjmcneill 	    COP_CFG_DEFAULT_DEVICE(config) == COP_DEVICE_SPEAKER &&
7198a9ff04bSjmcneill 	    (wcap & (COP_AWCAP_INAMP_PRESENT|COP_AWCAP_OUTAMP_PRESENT)) == 0)) {
7208a9ff04bSjmcneill 		wcap &= ~COP_AWCAP_TYPE_MASK;
7218a9ff04bSjmcneill 		wcap |= (COP_AWCAP_TYPE_BEEP_GENERATOR << COP_AWCAP_TYPE_SHIFT);
7228a9ff04bSjmcneill 		w->w_waspin = true;
7238a9ff04bSjmcneill 	}
7248a9ff04bSjmcneill 
7258a9ff04bSjmcneill 	return wcap;
7268a9ff04bSjmcneill }
7278a9ff04bSjmcneill 
7288a9ff04bSjmcneill static void
hdafg_widget_parse(struct hdaudio_widget * w)7298a9ff04bSjmcneill hdafg_widget_parse(struct hdaudio_widget *w)
7308a9ff04bSjmcneill {
7318a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
7328a9ff04bSjmcneill 	const char *tstr;
7338a9ff04bSjmcneill 
7348a9ff04bSjmcneill 	w->w_p.aw_cap = hdafg_widget_getcaps(w);
7358a9ff04bSjmcneill 	w->w_type = COP_AWCAP_TYPE(w->w_p.aw_cap);
7368a9ff04bSjmcneill 
7378a9ff04bSjmcneill 	switch (w->w_type) {
7388a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_OUTPUT:	tstr = "audio output"; break;
7398a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_INPUT:	tstr = "audio input"; break;
7408a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_MIXER:	tstr = "audio mixer"; break;
7418a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_SELECTOR:	tstr = "audio selector"; break;
7428a9ff04bSjmcneill 	case COP_AWCAP_TYPE_PIN_COMPLEX:	tstr = "pin"; break;
7438a9ff04bSjmcneill 	case COP_AWCAP_TYPE_POWER_WIDGET:	tstr = "power widget"; break;
7448a9ff04bSjmcneill 	case COP_AWCAP_TYPE_VOLUME_KNOB:	tstr = "volume knob"; break;
7458a9ff04bSjmcneill 	case COP_AWCAP_TYPE_BEEP_GENERATOR:	tstr = "beep generator"; break;
7468a9ff04bSjmcneill 	case COP_AWCAP_TYPE_VENDOR_DEFINED:	tstr = "vendor defined"; break;
7478a9ff04bSjmcneill 	default:				tstr = "unknown"; break;
7488a9ff04bSjmcneill 	}
7498a9ff04bSjmcneill 
7508a9ff04bSjmcneill 	strlcpy(w->w_name, tstr, sizeof(w->w_name));
7518a9ff04bSjmcneill 
7528a9ff04bSjmcneill 	hdafg_widget_connection_parse(w);
7538a9ff04bSjmcneill 
7548a9ff04bSjmcneill 	if (w->w_p.aw_cap & COP_AWCAP_INAMP_PRESENT) {
7558a9ff04bSjmcneill 		if (w->w_p.aw_cap & COP_AWCAP_AMP_PARAM_OVERRIDE)
7568a9ff04bSjmcneill 			w->w_p.inamp_cap = hda_get_wparam(w,
7578a9ff04bSjmcneill 			    AMPLIFIER_CAPABILITIES_INAMP);
7588a9ff04bSjmcneill 		else
7598a9ff04bSjmcneill 			w->w_p.inamp_cap = sc->sc_p.inamp_cap;
7608a9ff04bSjmcneill 	}
7618a9ff04bSjmcneill 	if (w->w_p.aw_cap & COP_AWCAP_OUTAMP_PRESENT) {
7628a9ff04bSjmcneill 		if (w->w_p.aw_cap & COP_AWCAP_AMP_PARAM_OVERRIDE)
7638a9ff04bSjmcneill 			w->w_p.outamp_cap = hda_get_wparam(w,
7648a9ff04bSjmcneill 			    AMPLIFIER_CAPABILITIES_OUTAMP);
7658a9ff04bSjmcneill 		else
7668a9ff04bSjmcneill 			w->w_p.outamp_cap = sc->sc_p.outamp_cap;
7678a9ff04bSjmcneill 	}
7688a9ff04bSjmcneill 
7698a9ff04bSjmcneill 	w->w_p.stream_format = 0;
7708a9ff04bSjmcneill 	w->w_p.pcm_size_rate = 0;
7718a9ff04bSjmcneill 	switch (w->w_type) {
7728a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_OUTPUT:
7738a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_INPUT:
7748a9ff04bSjmcneill 		if (w->w_p.aw_cap & COP_AWCAP_FORMAT_OVERRIDE) {
7758a9ff04bSjmcneill 			w->w_p.stream_format = hda_get_wparam(w,
7768a9ff04bSjmcneill 			    SUPPORTED_STREAM_FORMATS);
7778a9ff04bSjmcneill 			w->w_p.pcm_size_rate = hda_get_wparam(w,
7788a9ff04bSjmcneill 			    SUPPORTED_PCM_SIZE_RATES);
7798a9ff04bSjmcneill 		} else {
7808a9ff04bSjmcneill 			w->w_p.stream_format = sc->sc_p.stream_format;
7818a9ff04bSjmcneill 			w->w_p.pcm_size_rate = sc->sc_p.pcm_size_rate;
7828a9ff04bSjmcneill 		}
7838a9ff04bSjmcneill 		break;
7848a9ff04bSjmcneill 	case COP_AWCAP_TYPE_PIN_COMPLEX:
7858a9ff04bSjmcneill 		hdafg_widget_pin_parse(w);
7868a9ff04bSjmcneill 		hdafg_widget_setconfig(w, w->w_pin.config);
7878a9ff04bSjmcneill 		break;
7888a9ff04bSjmcneill 	}
7898a9ff04bSjmcneill }
7908a9ff04bSjmcneill 
7918a9ff04bSjmcneill static int
hdafg_assoc_count_channels(struct hdafg_softc * sc,struct hdaudio_assoc * as,enum hdaudio_pindir dir)7928a9ff04bSjmcneill hdafg_assoc_count_channels(struct hdafg_softc *sc,
7938a9ff04bSjmcneill     struct hdaudio_assoc *as, enum hdaudio_pindir dir)
7948a9ff04bSjmcneill {
7958a9ff04bSjmcneill 	struct hdaudio_widget *w;
7968a9ff04bSjmcneill 	int *dacmap;
7978a9ff04bSjmcneill 	int i, dacmapsz = sizeof(*dacmap) * sc->sc_endnode;
7988a9ff04bSjmcneill 	int nchans = 0;
7998a9ff04bSjmcneill 
8008a9ff04bSjmcneill 	if (as->as_enable == false || as->as_dir != dir)
8018a9ff04bSjmcneill 		return 0;
8028a9ff04bSjmcneill 
8038a9ff04bSjmcneill 	dacmap = kmem_zalloc(dacmapsz, KM_SLEEP);
8048a9ff04bSjmcneill 
8058a9ff04bSjmcneill 	for (i = 0; i < HDAUDIO_MAXPINS; i++)
8068a9ff04bSjmcneill 		if (as->as_dacs[i])
8078a9ff04bSjmcneill 			dacmap[as->as_dacs[i]] = 1;
8088a9ff04bSjmcneill 
80972caf958Sisaki 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
8108a9ff04bSjmcneill 		if (!dacmap[i])
8118a9ff04bSjmcneill 			continue;
8128a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
8138a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
8148a9ff04bSjmcneill 			continue;
8158a9ff04bSjmcneill 		nchans += COP_AWCAP_CHANNEL_COUNT(w->w_p.aw_cap);
8168a9ff04bSjmcneill 	}
8178a9ff04bSjmcneill 
8188a9ff04bSjmcneill 	kmem_free(dacmap, dacmapsz);
8198a9ff04bSjmcneill 
8208a9ff04bSjmcneill 	return nchans;
8218a9ff04bSjmcneill }
8228a9ff04bSjmcneill 
8238a9ff04bSjmcneill static const char *
hdafg_assoc_type_string(struct hdaudio_assoc * as)8248a9ff04bSjmcneill hdafg_assoc_type_string(struct hdaudio_assoc *as)
8258a9ff04bSjmcneill {
8268a9ff04bSjmcneill 	switch (as->as_digital) {
8278a9ff04bSjmcneill 	case HDAFG_AS_ANALOG:
8288a9ff04bSjmcneill 		return as->as_dir == HDAUDIO_PINDIR_IN ?
8298a9ff04bSjmcneill 		    "ADC" : "DAC";
8308a9ff04bSjmcneill 	case HDAFG_AS_SPDIF:
8318a9ff04bSjmcneill 		return as->as_dir == HDAUDIO_PINDIR_IN ?
8328a9ff04bSjmcneill 		    "DIG-In" : "DIG";
8338a9ff04bSjmcneill 	case HDAFG_AS_HDMI:
8348a9ff04bSjmcneill 		return as->as_dir == HDAUDIO_PINDIR_IN ?
8358a9ff04bSjmcneill 		    "HDMI-In" : "HDMI";
8368a9ff04bSjmcneill 	case HDAFG_AS_DISPLAYPORT:
8378a9ff04bSjmcneill 		return as->as_dir == HDAUDIO_PINDIR_IN ?
8388a9ff04bSjmcneill 		    "DP-In" : "DP";
8398a9ff04bSjmcneill 	default:
8408a9ff04bSjmcneill 		return as->as_dir == HDAUDIO_PINDIR_IN ?
8418a9ff04bSjmcneill 		    "Unknown-In" : "Unknown-Out";
8428a9ff04bSjmcneill 	}
8438a9ff04bSjmcneill }
8448a9ff04bSjmcneill 
8458a9ff04bSjmcneill static void
hdafg_assoc_dump_dd(struct hdafg_softc * sc,struct hdaudio_assoc * as,int pin,int lock)8468a9ff04bSjmcneill hdafg_assoc_dump_dd(struct hdafg_softc *sc, struct hdaudio_assoc *as, int pin,
8478a9ff04bSjmcneill 	int lock)
8488a9ff04bSjmcneill {
8498a9ff04bSjmcneill 	struct hdafg_dd_info hdi;
8508a9ff04bSjmcneill 	struct hdaudio_widget *w;
8518a9ff04bSjmcneill 	uint8_t elddata[256];
8528a9ff04bSjmcneill 	unsigned int elddatalen = 0, i;
8538a9ff04bSjmcneill 	uint32_t res;
8548a9ff04bSjmcneill 	uint32_t (*cmd)(struct hdaudio_codec *, int, uint32_t, uint32_t) =
8558a9ff04bSjmcneill 	    lock ? hdaudio_command : hdaudio_command_unlocked;
8568a9ff04bSjmcneill 
8578a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, as->as_pins[pin]);
8588a9ff04bSjmcneill 
8598a9ff04bSjmcneill 	if (w->w_pin.cap & COP_PINCAP_TRIGGER_REQD) {
8608a9ff04bSjmcneill 		(*cmd)(sc->sc_codec, as->as_pins[pin],
8618a9ff04bSjmcneill 		    CORB_SET_PIN_SENSE, 0);
8628a9ff04bSjmcneill 	}
8638a9ff04bSjmcneill 	res = (*cmd)(sc->sc_codec, as->as_pins[pin],
8648a9ff04bSjmcneill 	    CORB_GET_PIN_SENSE, 0);
8658a9ff04bSjmcneill 
8668a9ff04bSjmcneill #ifdef HDAFG_HDMI_DEBUG
8678a9ff04bSjmcneill 	hda_print(sc, "Display Device, pin=%02X\n", as->as_pins[pin]);
8688a9ff04bSjmcneill 	hda_print(sc, "  COP_GET_PIN_SENSE_PRESENSE_DETECT=%d\n",
8698a9ff04bSjmcneill 	    !!(res & COP_GET_PIN_SENSE_PRESENSE_DETECT));
8708a9ff04bSjmcneill 	hda_print(sc, "  COP_GET_PIN_SENSE_ELD_VALID=%d\n",
8718a9ff04bSjmcneill 	    !!(res & COP_GET_PIN_SENSE_ELD_VALID));
8728a9ff04bSjmcneill #endif
8738a9ff04bSjmcneill 
8748a9ff04bSjmcneill 	if ((res &
8758a9ff04bSjmcneill 	    (COP_GET_PIN_SENSE_PRESENSE_DETECT|COP_GET_PIN_SENSE_ELD_VALID)) ==
8768a9ff04bSjmcneill 	    (COP_GET_PIN_SENSE_PRESENSE_DETECT|COP_GET_PIN_SENSE_ELD_VALID)) {
8778a9ff04bSjmcneill 		res = (*cmd)(sc->sc_codec, as->as_pins[pin],
8788a9ff04bSjmcneill 		    CORB_GET_HDMI_DIP_SIZE, COP_DIP_ELD_SIZE);
8798a9ff04bSjmcneill 		elddatalen = COP_DIP_BUFFER_SIZE(res);
8808a9ff04bSjmcneill 		if (elddatalen == 0)
8818a9ff04bSjmcneill 			elddatalen = sizeof(elddata); /* paranoid */
8828a9ff04bSjmcneill 		for (i = 0; i < elddatalen; i++) {
8838a9ff04bSjmcneill 			res = (*cmd)(sc->sc_codec, as->as_pins[pin],
8848a9ff04bSjmcneill 			    CORB_GET_HDMI_ELD_DATA, i);
8858a9ff04bSjmcneill 			if (!(res & COP_ELD_VALID)) {
886634cdb73Smrg #ifdef HDAFG_HDMI_DEBUG
8878a9ff04bSjmcneill 				hda_error(sc, "bad ELD size (%u/%u)\n",
8888a9ff04bSjmcneill 				    i, elddatalen);
889634cdb73Smrg #endif
8908a9ff04bSjmcneill 				break;
8918a9ff04bSjmcneill 			}
8928a9ff04bSjmcneill 			elddata[i] = COP_ELD_DATA(res);
8938a9ff04bSjmcneill 		}
8948a9ff04bSjmcneill 
8958a9ff04bSjmcneill 		if (hdafg_dd_parse_info(elddata, elddatalen, &hdi) != 0) {
896634cdb73Smrg #ifdef HDAFG_HDMI_DEBUG
8978a9ff04bSjmcneill 			hda_error(sc, "failed to parse ELD data\n");
898634cdb73Smrg #endif
8998a9ff04bSjmcneill 			return;
9008a9ff04bSjmcneill 		}
9018a9ff04bSjmcneill 
90298d658eeSjmcneill #ifdef HDAFG_HDMI_DEBUG
9038a9ff04bSjmcneill 		hda_print(sc, "  ELD version=0x%x", ELD_VER(&hdi.eld));
9048a9ff04bSjmcneill 		hda_print1(sc, ",len=%u", hdi.eld.header.baseline_eld_len * 4);
9058a9ff04bSjmcneill 		hda_print1(sc, ",edid=0x%x", ELD_CEA_EDID_VER(&hdi.eld));
9068a9ff04bSjmcneill 		hda_print1(sc, ",port=0x%" PRIx64, hdi.eld.port_id);
9078a9ff04bSjmcneill 		hda_print1(sc, ",vendor=0x%04x", hdi.eld.vendor);
9088a9ff04bSjmcneill 		hda_print1(sc, ",product=0x%04x", hdi.eld.product);
9098a9ff04bSjmcneill 		hda_print1(sc, "\n");
9108a9ff04bSjmcneill 		hda_print(sc, "  Monitor = '%s'\n", hdi.monitor);
9118a9ff04bSjmcneill 		for (i = 0; i < hdi.nsad; i++) {
9128a9ff04bSjmcneill 			hda_print(sc, "  SAD id=%u", i);
9138a9ff04bSjmcneill 			hda_print1(sc, ",format=%u",
9148a9ff04bSjmcneill 			    CEA_AUDIO_FORMAT(&hdi.sad[i]));
9158a9ff04bSjmcneill 			hda_print1(sc, ",channels=%u",
9168a9ff04bSjmcneill 			    CEA_MAX_CHANNELS(&hdi.sad[i]));
9178a9ff04bSjmcneill 			hda_print1(sc, ",rate=0x%02x",
9188a9ff04bSjmcneill 			    CEA_SAMPLE_RATE(&hdi.sad[i]));
9198a9ff04bSjmcneill 			if (CEA_AUDIO_FORMAT(&hdi.sad[i]) ==
9208a9ff04bSjmcneill 			    CEA_AUDIO_FORMAT_LPCM)
9218a9ff04bSjmcneill 				hda_print1(sc, ",precision=0x%x",
9228a9ff04bSjmcneill 				    CEA_PRECISION(&hdi.sad[i]));
9238a9ff04bSjmcneill 			else
9248a9ff04bSjmcneill 				hda_print1(sc, ",maxbitrate=%u",
9258a9ff04bSjmcneill 				    CEA_MAX_BITRATE(&hdi.sad[i]));
9268a9ff04bSjmcneill 			hda_print1(sc, "\n");
9278a9ff04bSjmcneill 		}
92898d658eeSjmcneill #endif
9298a9ff04bSjmcneill 	}
9308a9ff04bSjmcneill }
9318a9ff04bSjmcneill 
9328a9ff04bSjmcneill static char *
hdafg_mixer_mask2allname(uint32_t mask,char * buf,size_t len)9338a9ff04bSjmcneill hdafg_mixer_mask2allname(uint32_t mask, char *buf, size_t len)
9348a9ff04bSjmcneill {
9358a9ff04bSjmcneill 	static const char *audioname[] = HDAUDIO_DEVICE_NAMES;
9368a9ff04bSjmcneill 	int i, first = 1;
9378a9ff04bSjmcneill 
9388a9ff04bSjmcneill 	memset(buf, 0, len);
9398a9ff04bSjmcneill 	for (i = 0; i < HDAUDIO_MIXER_NRDEVICES; i++) {
9408a9ff04bSjmcneill 		if (mask & (1 << i)) {
9418a9ff04bSjmcneill 			if (first == 0)
9428a9ff04bSjmcneill 				strlcat(buf, ", ", len);
9438a9ff04bSjmcneill 			strlcat(buf, audioname[i], len);
9448a9ff04bSjmcneill 			first = 0;
9458a9ff04bSjmcneill 		}
9468a9ff04bSjmcneill 	}
9478a9ff04bSjmcneill 
9488a9ff04bSjmcneill 	return buf;
9498a9ff04bSjmcneill }
9508a9ff04bSjmcneill 
9518a9ff04bSjmcneill static void
hdafg_dump_dst_nid(struct hdafg_softc * sc,int nid,int depth)9528a9ff04bSjmcneill hdafg_dump_dst_nid(struct hdafg_softc *sc, int nid, int depth)
9538a9ff04bSjmcneill {
9548a9ff04bSjmcneill 	struct hdaudio_widget *w, *cw;
9558a9ff04bSjmcneill 	char buf[64];
9568a9ff04bSjmcneill 	int i;
9578a9ff04bSjmcneill 
9588a9ff04bSjmcneill 	if (depth > HDAUDIO_PARSE_MAXDEPTH)
9598a9ff04bSjmcneill 		return;
9608a9ff04bSjmcneill 
9618a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
9628a9ff04bSjmcneill 	if (w == NULL || w->w_enable == false)
9638a9ff04bSjmcneill 		return;
9648a9ff04bSjmcneill 
9658a9ff04bSjmcneill 	aprint_debug("%*s", 4 + depth * 7, "");
9668a9ff04bSjmcneill 	aprint_debug("nid=%02X [%s]", w->w_nid, w->w_name);
9678a9ff04bSjmcneill 
9688a9ff04bSjmcneill 	if (depth > 0) {
9698a9ff04bSjmcneill 		if (w->w_audiomask == 0) {
9708a9ff04bSjmcneill 			aprint_debug("\n");
9718a9ff04bSjmcneill 			return;
9728a9ff04bSjmcneill 		}
9738a9ff04bSjmcneill 		aprint_debug(" [source: %s]",
9748a9ff04bSjmcneill 		    hdafg_mixer_mask2allname(w->w_audiomask, buf, sizeof(buf)));
9758a9ff04bSjmcneill 		if (w->w_audiodev >= 0) {
9768a9ff04bSjmcneill 			aprint_debug("\n");
9778a9ff04bSjmcneill 			return;
9788a9ff04bSjmcneill 		}
9798a9ff04bSjmcneill 	}
9808a9ff04bSjmcneill 
9818a9ff04bSjmcneill 	aprint_debug("\n");
9828a9ff04bSjmcneill 
9838a9ff04bSjmcneill 	for (i = 0; i < w->w_nconns; i++) {
9848a9ff04bSjmcneill 		if (w->w_connsenable[i] == 0)
9858a9ff04bSjmcneill 			continue;
9868a9ff04bSjmcneill 		cw = hdafg_widget_lookup(sc, w->w_conns[i]);
9878a9ff04bSjmcneill 		if (cw == NULL || cw->w_enable == false || cw->w_bindas == -1)
9888a9ff04bSjmcneill 			continue;
9898a9ff04bSjmcneill 		hdafg_dump_dst_nid(sc, w->w_conns[i], depth + 1);
9908a9ff04bSjmcneill 	}
9918a9ff04bSjmcneill }
9928a9ff04bSjmcneill 
9938a9ff04bSjmcneill static void
hdafg_assoc_dump(struct hdafg_softc * sc)9948a9ff04bSjmcneill hdafg_assoc_dump(struct hdafg_softc *sc)
9958a9ff04bSjmcneill {
9968a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
9978a9ff04bSjmcneill 	struct hdaudio_widget *w;
9988a9ff04bSjmcneill 	uint32_t conn, defdev, curdev, curport;
9998a9ff04bSjmcneill 	int maxassocs = sc->sc_nassocs;
10008a9ff04bSjmcneill 	int i, j;
10018a9ff04bSjmcneill 
10028a9ff04bSjmcneill 	for (i = 0; i < maxassocs; i++) {
10038a9ff04bSjmcneill 		uint32_t devmask = 0, portmask = 0;
10048a9ff04bSjmcneill 		bool firstdev = true;
10058a9ff04bSjmcneill 		int nchan;
10068a9ff04bSjmcneill 
10078a9ff04bSjmcneill 		if (as[i].as_enable == false)
10088a9ff04bSjmcneill 			continue;
10098a9ff04bSjmcneill 
10108a9ff04bSjmcneill 		hda_print(sc, "%s%02X",
10118a9ff04bSjmcneill 		    hdafg_assoc_type_string(&as[i]), i);
10128a9ff04bSjmcneill 
10138a9ff04bSjmcneill 		nchan = hdafg_assoc_count_channels(sc, &as[i],
10148a9ff04bSjmcneill 		    as[i].as_dir);
10158a9ff04bSjmcneill 		hda_print1(sc, " %dch:", nchan);
10168a9ff04bSjmcneill 
10178a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
10188a9ff04bSjmcneill 			if (as[i].as_dacs[j] == 0)
10198a9ff04bSjmcneill 				continue;
10208a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_pins[j]);
10218a9ff04bSjmcneill 			if (w == NULL)
10228a9ff04bSjmcneill 				continue;
10238a9ff04bSjmcneill 			conn = COP_CFG_PORT_CONNECTIVITY(w->w_pin.config);
10248a9ff04bSjmcneill 			defdev = COP_CFG_DEFAULT_DEVICE(w->w_pin.config);
10258a9ff04bSjmcneill 			if (conn != COP_PORT_NONE) {
10268a9ff04bSjmcneill 				devmask |= (1 << defdev);
10278a9ff04bSjmcneill 				portmask |= (1 << conn);
10288a9ff04bSjmcneill 			}
10298a9ff04bSjmcneill 		}
10308a9ff04bSjmcneill 		for (curdev = 0; curdev < 16; curdev++) {
10318a9ff04bSjmcneill 			bool firstport = true;
10328a9ff04bSjmcneill 			if ((devmask & (1 << curdev)) == 0)
10338a9ff04bSjmcneill 				continue;
10348a9ff04bSjmcneill 
10358a9ff04bSjmcneill 			if (firstdev == false)
10368a9ff04bSjmcneill 				hda_print1(sc, ",");
10378a9ff04bSjmcneill 			firstdev = false;
10388a9ff04bSjmcneill 			hda_print1(sc, " %s",
10398a9ff04bSjmcneill 			    hdafg_default_device[curdev]);
10408a9ff04bSjmcneill 
10418a9ff04bSjmcneill 			for (curport = 0; curport < 4; curport++) {
10428a9ff04bSjmcneill 				bool devonport = false;
10438a9ff04bSjmcneill 				if ((portmask & (1 << curport)) == 0)
10448a9ff04bSjmcneill 					continue;
10458a9ff04bSjmcneill 
10468a9ff04bSjmcneill 				for (j = 0; j < HDAUDIO_MAXPINS; j++) {
10478a9ff04bSjmcneill 					if (as[i].as_dacs[j] == 0)
10488a9ff04bSjmcneill 						continue;
10498a9ff04bSjmcneill 
10508a9ff04bSjmcneill 					w = hdafg_widget_lookup(sc,
10518a9ff04bSjmcneill 					    as[i].as_pins[j]);
10528a9ff04bSjmcneill 					if (w == NULL)
10538a9ff04bSjmcneill 						continue;
10548a9ff04bSjmcneill 					conn = COP_CFG_PORT_CONNECTIVITY(w->w_pin.config);
10558a9ff04bSjmcneill 					defdev = COP_CFG_DEFAULT_DEVICE(w->w_pin.config);
10568a9ff04bSjmcneill 					if (conn != curport || defdev != curdev)
10578a9ff04bSjmcneill 						continue;
10588a9ff04bSjmcneill 
10598a9ff04bSjmcneill 					devonport = true;
10608a9ff04bSjmcneill 				}
10618a9ff04bSjmcneill 
10628a9ff04bSjmcneill 				if (devonport == false)
10638a9ff04bSjmcneill 					continue;
10648a9ff04bSjmcneill 
10658a9ff04bSjmcneill 				hda_print1(sc, " [%s",
10668a9ff04bSjmcneill 				    hdafg_port_connectivity[curport]);
10678a9ff04bSjmcneill 				for (j = 0; j < HDAUDIO_MAXPINS; j++) {
10688a9ff04bSjmcneill 					if (as[i].as_dacs[j] == 0)
10698a9ff04bSjmcneill 						continue;
10708a9ff04bSjmcneill 
10718a9ff04bSjmcneill 					w = hdafg_widget_lookup(sc,
10728a9ff04bSjmcneill 					    as[i].as_pins[j]);
10738a9ff04bSjmcneill 					if (w == NULL)
10748a9ff04bSjmcneill 						continue;
10758a9ff04bSjmcneill 					conn = COP_CFG_PORT_CONNECTIVITY(w->w_pin.config);
10768a9ff04bSjmcneill 					defdev = COP_CFG_DEFAULT_DEVICE(w->w_pin.config);
10778a9ff04bSjmcneill 					if (conn != curport || defdev != curdev)
10788a9ff04bSjmcneill 						continue;
10798a9ff04bSjmcneill 
10808a9ff04bSjmcneill 					if (firstport == false)
10818a9ff04bSjmcneill 						hda_trace1(sc, ",");
10828a9ff04bSjmcneill 					else
10838a9ff04bSjmcneill 						hda_trace1(sc, " ");
10848a9ff04bSjmcneill 					firstport = false;
10858a9ff04bSjmcneill #ifdef HDAUDIO_DEBUG
10868a9ff04bSjmcneill 					int color =
10878a9ff04bSjmcneill 					    COP_CFG_COLOR(w->w_pin.config);
10888a9ff04bSjmcneill 					hda_trace1(sc, "%s",
10898a9ff04bSjmcneill 					    hdafg_color[color]);
10908a9ff04bSjmcneill #endif
10918a9ff04bSjmcneill 					hda_trace1(sc, "(%02X)", w->w_nid);
10928a9ff04bSjmcneill 				}
10938a9ff04bSjmcneill 				hda_print1(sc, "]");
10948a9ff04bSjmcneill 			}
10958a9ff04bSjmcneill 		}
10968a9ff04bSjmcneill 		hda_print1(sc, "\n");
10978a9ff04bSjmcneill 
10988a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
10998a9ff04bSjmcneill 			if (as[i].as_pins[j] == 0)
11008a9ff04bSjmcneill 				continue;
11018a9ff04bSjmcneill 			hdafg_dump_dst_nid(sc, as[i].as_pins[j], 0);
11028a9ff04bSjmcneill 		}
11038a9ff04bSjmcneill 
11048a9ff04bSjmcneill 		if (as[i].as_displaydev == true) {
11058a9ff04bSjmcneill 			for (j = 0; j < HDAUDIO_MAXPINS; j++) {
11068a9ff04bSjmcneill 				if (as[i].as_pins[j] == 0)
11078a9ff04bSjmcneill 					continue;
11088a9ff04bSjmcneill 				hdafg_assoc_dump_dd(sc, &as[i], j, 1);
11098a9ff04bSjmcneill 			}
11108a9ff04bSjmcneill 		}
11118a9ff04bSjmcneill 	}
11128a9ff04bSjmcneill }
11138a9ff04bSjmcneill 
11148a9ff04bSjmcneill static void
hdafg_assoc_parse(struct hdafg_softc * sc)11158a9ff04bSjmcneill hdafg_assoc_parse(struct hdafg_softc *sc)
11168a9ff04bSjmcneill {
11178a9ff04bSjmcneill 	struct hdaudio_assoc *as;
11188a9ff04bSjmcneill 	struct hdaudio_widget *w;
11198a9ff04bSjmcneill 	int i, j, cnt, maxassocs, type, assoc, seq, first, hpredir;
11208a9ff04bSjmcneill 	enum hdaudio_pindir dir;
11218a9ff04bSjmcneill 
11228a9ff04bSjmcneill 	hda_debug(sc, "  count present associations\n");
11238a9ff04bSjmcneill 	/* Count present associations */
11248a9ff04bSjmcneill 	maxassocs = 0;
11258a9ff04bSjmcneill 	for (j = 1; j < HDAUDIO_MAXPINS; j++) {
11268a9ff04bSjmcneill 		for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
11278a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, i);
11288a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
11298a9ff04bSjmcneill 				continue;
11308a9ff04bSjmcneill 			if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
11318a9ff04bSjmcneill 				continue;
11328a9ff04bSjmcneill 			if (COP_CFG_DEFAULT_ASSOCIATION(w->w_pin.config) != j)
11338a9ff04bSjmcneill 				continue;
11348a9ff04bSjmcneill 			maxassocs++;
11358a9ff04bSjmcneill 			if (j != 15) /* There could be many 1-pin assocs #15 */
11368a9ff04bSjmcneill 				break;
11378a9ff04bSjmcneill 		}
11388a9ff04bSjmcneill 	}
11398a9ff04bSjmcneill 
11408a9ff04bSjmcneill 	hda_debug(sc, "  maxassocs %d\n", maxassocs);
11418a9ff04bSjmcneill 	sc->sc_nassocs = maxassocs;
11428a9ff04bSjmcneill 
11438a9ff04bSjmcneill 	if (maxassocs < 1)
11448a9ff04bSjmcneill 		return;
11458a9ff04bSjmcneill 
11468a9ff04bSjmcneill 	hda_debug(sc, "  allocating memory\n");
11478a9ff04bSjmcneill 	as = kmem_zalloc(maxassocs * sizeof(*as), KM_SLEEP);
11488a9ff04bSjmcneill 	for (i = 0; i < maxassocs; i++) {
11498a9ff04bSjmcneill 		as[i].as_hpredir = -1;
11508a9ff04bSjmcneill 		/* as[i].as_chan = NULL; */
11518a9ff04bSjmcneill 		as[i].as_digital = HDAFG_AS_SPDIF;
11528a9ff04bSjmcneill 	}
11538a9ff04bSjmcneill 
11548a9ff04bSjmcneill 	hda_debug(sc, "  scan associations, skipping as=0\n");
11558a9ff04bSjmcneill 	/* Scan associations skipping as=0 */
11568a9ff04bSjmcneill 	cnt = 0;
11578a9ff04bSjmcneill 	for (j = 1; j < HDAUDIO_MAXPINS && cnt < maxassocs; j++) {
11588a9ff04bSjmcneill 		first = 16;
11598a9ff04bSjmcneill 		hpredir = 0;
11608a9ff04bSjmcneill 		for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
11618a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, i);
11628a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
11638a9ff04bSjmcneill 				continue;
11648a9ff04bSjmcneill 			if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
11658a9ff04bSjmcneill 				continue;
11668a9ff04bSjmcneill 			assoc = COP_CFG_DEFAULT_ASSOCIATION(w->w_pin.config);
11678a9ff04bSjmcneill 			seq = COP_CFG_SEQUENCE(w->w_pin.config);
11688a9ff04bSjmcneill 			if (assoc != j)
11698a9ff04bSjmcneill 				continue;
11708a9ff04bSjmcneill 			KASSERT(cnt < maxassocs);
11718a9ff04bSjmcneill 			type = COP_CFG_DEFAULT_DEVICE(w->w_pin.config);
11728a9ff04bSjmcneill 			/* Get pin direction */
11738a9ff04bSjmcneill 			switch (type) {
11748a9ff04bSjmcneill 			case COP_DEVICE_LINE_OUT:
11758a9ff04bSjmcneill 			case COP_DEVICE_SPEAKER:
11768a9ff04bSjmcneill 			case COP_DEVICE_HP_OUT:
11778a9ff04bSjmcneill 			case COP_DEVICE_SPDIF_OUT:
11788a9ff04bSjmcneill 			case COP_DEVICE_DIGITAL_OTHER_OUT:
11798a9ff04bSjmcneill 				dir = HDAUDIO_PINDIR_OUT;
11808a9ff04bSjmcneill 				break;
11818a9ff04bSjmcneill 			default:
11828a9ff04bSjmcneill 				dir = HDAUDIO_PINDIR_IN;
11838a9ff04bSjmcneill 				break;
11848a9ff04bSjmcneill 			}
11858a9ff04bSjmcneill 			/* If this is a first pin, create new association */
11868a9ff04bSjmcneill 			if (as[cnt].as_pincnt == 0) {
11878a9ff04bSjmcneill 				as[cnt].as_enable = true;
11888a9ff04bSjmcneill 				as[cnt].as_activated = true;
11898a9ff04bSjmcneill 				as[cnt].as_index = j;
11908a9ff04bSjmcneill 				as[cnt].as_dir = dir;
11918a9ff04bSjmcneill 			}
11928a9ff04bSjmcneill 			if (seq < first)
11938a9ff04bSjmcneill 				first = seq;
11948a9ff04bSjmcneill 			/* Check association correctness */
11958a9ff04bSjmcneill 			if (as[cnt].as_pins[seq] != 0) {
11968a9ff04bSjmcneill 				hda_error(sc, "duplicate pin in association\n");
11978a9ff04bSjmcneill 				as[cnt].as_enable = false;
11988a9ff04bSjmcneill 			}
11998a9ff04bSjmcneill 			if (dir != as[cnt].as_dir) {
12008a9ff04bSjmcneill 				hda_error(sc,
12018a9ff04bSjmcneill 				    "pin %02X has wrong direction for %02X\n",
12028a9ff04bSjmcneill 				    w->w_nid, j);
12038a9ff04bSjmcneill 				as[cnt].as_enable = false;
12048a9ff04bSjmcneill 			}
12058a9ff04bSjmcneill 			if ((w->w_p.aw_cap & COP_AWCAP_DIGITAL) == 0)
12068a9ff04bSjmcneill 				as[cnt].as_digital = HDAFG_AS_ANALOG;
12078a9ff04bSjmcneill 			if (w->w_pin.cap & (COP_PINCAP_HDMI|COP_PINCAP_DP))
12088a9ff04bSjmcneill 				as[cnt].as_displaydev = true;
12098a9ff04bSjmcneill 			if (w->w_pin.cap & COP_PINCAP_HDMI)
12108a9ff04bSjmcneill 				as[cnt].as_digital = HDAFG_AS_HDMI;
12118a9ff04bSjmcneill 			if (w->w_pin.cap & COP_PINCAP_DP)
12128a9ff04bSjmcneill 				as[cnt].as_digital = HDAFG_AS_DISPLAYPORT;
12138a9ff04bSjmcneill 			/* Headphones with seq=15 may mean redirection */
12148a9ff04bSjmcneill 			if (type == COP_DEVICE_HP_OUT && seq == 15)
12158a9ff04bSjmcneill 				hpredir = 1;
12168a9ff04bSjmcneill 			as[cnt].as_pins[seq] = w->w_nid;
12178a9ff04bSjmcneill 			as[cnt].as_pincnt++;
12188a9ff04bSjmcneill 			if (j == 15)
12198a9ff04bSjmcneill 				cnt++;
12208a9ff04bSjmcneill 		}
12218a9ff04bSjmcneill 		if (j != 15 && cnt < maxassocs && as[cnt].as_pincnt > 0) {
12228a9ff04bSjmcneill 			if (hpredir && as[cnt].as_pincnt > 1)
12238a9ff04bSjmcneill 				as[cnt].as_hpredir = first;
12248a9ff04bSjmcneill 			cnt++;
12258a9ff04bSjmcneill 		}
12268a9ff04bSjmcneill 	}
12278a9ff04bSjmcneill 
12288a9ff04bSjmcneill 	hda_debug(sc, "  all done\n");
12298a9ff04bSjmcneill 	sc->sc_assocs = as;
12308a9ff04bSjmcneill }
12318a9ff04bSjmcneill 
12328a9ff04bSjmcneill static void
hdafg_control_parse(struct hdafg_softc * sc)12338a9ff04bSjmcneill hdafg_control_parse(struct hdafg_softc *sc)
12348a9ff04bSjmcneill {
12358a9ff04bSjmcneill 	struct hdaudio_control *ctl;
12368a9ff04bSjmcneill 	struct hdaudio_widget *w, *cw;
12378a9ff04bSjmcneill 	int i, j, cnt, maxctls, ocap, icap;
12388a9ff04bSjmcneill 	int mute, offset, step, size;
12398a9ff04bSjmcneill 
12408a9ff04bSjmcneill 	maxctls = 0;
12418a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
12428a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
12438a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
12448a9ff04bSjmcneill 			continue;
12458a9ff04bSjmcneill 		if (w->w_p.outamp_cap)
12468a9ff04bSjmcneill 			maxctls++;
12478a9ff04bSjmcneill 		if (w->w_p.inamp_cap) {
12488a9ff04bSjmcneill 			switch (w->w_type) {
12498a9ff04bSjmcneill 			case COP_AWCAP_TYPE_AUDIO_SELECTOR:
12508a9ff04bSjmcneill 			case COP_AWCAP_TYPE_AUDIO_MIXER:
12518a9ff04bSjmcneill 				for (j = 0; j < w->w_nconns; j++) {
12528a9ff04bSjmcneill 					cw = hdafg_widget_lookup(sc,
12538a9ff04bSjmcneill 					    w->w_conns[j]);
12548a9ff04bSjmcneill 					if (cw == NULL || cw->w_enable == false)
12558a9ff04bSjmcneill 						continue;
12568a9ff04bSjmcneill 					maxctls++;
12578a9ff04bSjmcneill 				}
12588a9ff04bSjmcneill 				break;
12598a9ff04bSjmcneill 			default:
12608a9ff04bSjmcneill 				maxctls++;
12618a9ff04bSjmcneill 				break;
12628a9ff04bSjmcneill 			}
12638a9ff04bSjmcneill 		}
12648a9ff04bSjmcneill 	}
12658a9ff04bSjmcneill 
12668a9ff04bSjmcneill 	sc->sc_nctls = maxctls;
12678a9ff04bSjmcneill 	if (maxctls < 1)
12688a9ff04bSjmcneill 		return;
12698a9ff04bSjmcneill 
12708a9ff04bSjmcneill 	ctl = kmem_zalloc(sc->sc_nctls * sizeof(*ctl), KM_SLEEP);
12718a9ff04bSjmcneill 
12728a9ff04bSjmcneill 	cnt = 0;
12738a9ff04bSjmcneill 	for (i = sc->sc_startnode; cnt < maxctls && i < sc->sc_endnode; i++) {
12748a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
12758a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
12768a9ff04bSjmcneill 			continue;
12778a9ff04bSjmcneill 		ocap = w->w_p.outamp_cap;
12788a9ff04bSjmcneill 		icap = w->w_p.inamp_cap;
12798a9ff04bSjmcneill 		if (ocap) {
12808a9ff04bSjmcneill 			hda_trace(sc, "add ctrl outamp %d:%02X:FF\n",
12818a9ff04bSjmcneill 			    cnt, w->w_nid);
12828a9ff04bSjmcneill 			mute = COP_AMPCAP_MUTE_CAPABLE(ocap);
12838a9ff04bSjmcneill 			step = COP_AMPCAP_NUM_STEPS(ocap);
12848a9ff04bSjmcneill 			size = COP_AMPCAP_STEP_SIZE(ocap);
12858a9ff04bSjmcneill 			offset = COP_AMPCAP_OFFSET(ocap);
12868a9ff04bSjmcneill 			ctl[cnt].ctl_enable = true;
12878a9ff04bSjmcneill 			ctl[cnt].ctl_widget = w;
12888a9ff04bSjmcneill 			ctl[cnt].ctl_mute = mute;
12898a9ff04bSjmcneill 			ctl[cnt].ctl_step = step;
12908a9ff04bSjmcneill 			ctl[cnt].ctl_size = size;
12918a9ff04bSjmcneill 			ctl[cnt].ctl_offset = offset;
12928a9ff04bSjmcneill 			ctl[cnt].ctl_left = offset;
12938a9ff04bSjmcneill 			ctl[cnt].ctl_right = offset;
12948a9ff04bSjmcneill 			if (w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX ||
12958a9ff04bSjmcneill 			    w->w_waspin == true)
12968a9ff04bSjmcneill 				ctl[cnt].ctl_ndir = HDAUDIO_PINDIR_IN;
12978a9ff04bSjmcneill 			else
12988a9ff04bSjmcneill 				ctl[cnt].ctl_ndir = HDAUDIO_PINDIR_OUT;
12998a9ff04bSjmcneill 			ctl[cnt++].ctl_dir = HDAUDIO_PINDIR_OUT;
13008a9ff04bSjmcneill 		}
13018a9ff04bSjmcneill 		if (icap) {
13028a9ff04bSjmcneill 			mute = COP_AMPCAP_MUTE_CAPABLE(icap);
13038a9ff04bSjmcneill 			step = COP_AMPCAP_NUM_STEPS(icap);
13048a9ff04bSjmcneill 			size = COP_AMPCAP_STEP_SIZE(icap);
13058a9ff04bSjmcneill 			offset = COP_AMPCAP_OFFSET(icap);
13068a9ff04bSjmcneill 			switch (w->w_type) {
13078a9ff04bSjmcneill 			case COP_AWCAP_TYPE_AUDIO_SELECTOR:
13088a9ff04bSjmcneill 			case COP_AWCAP_TYPE_AUDIO_MIXER:
13098a9ff04bSjmcneill 				for (j = 0; j < w->w_nconns; j++) {
13108a9ff04bSjmcneill 					if (cnt >= maxctls)
13118a9ff04bSjmcneill 						break;
13128a9ff04bSjmcneill 					cw = hdafg_widget_lookup(sc,
13138a9ff04bSjmcneill 					    w->w_conns[j]);
13148a9ff04bSjmcneill 					if (cw == NULL || cw->w_enable == false)
13158a9ff04bSjmcneill 						continue;
13168a9ff04bSjmcneill 					hda_trace(sc, "add ctrl inamp selmix "
13178a9ff04bSjmcneill 					    "%d:%02X:%02X\n", cnt, w->w_nid,
13188a9ff04bSjmcneill 					    cw->w_nid);
13198a9ff04bSjmcneill 					ctl[cnt].ctl_enable = true;
13208a9ff04bSjmcneill 					ctl[cnt].ctl_widget = w;
13218a9ff04bSjmcneill 					ctl[cnt].ctl_childwidget = cw;
13228a9ff04bSjmcneill 					ctl[cnt].ctl_index = j;
13238a9ff04bSjmcneill 					ctl[cnt].ctl_mute = mute;
13248a9ff04bSjmcneill 					ctl[cnt].ctl_step = step;
13258a9ff04bSjmcneill 					ctl[cnt].ctl_size = size;
13268a9ff04bSjmcneill 					ctl[cnt].ctl_offset = offset;
13278a9ff04bSjmcneill 					ctl[cnt].ctl_left = offset;
13288a9ff04bSjmcneill 					ctl[cnt].ctl_right = offset;
13298a9ff04bSjmcneill 					ctl[cnt].ctl_ndir = HDAUDIO_PINDIR_IN;
13308a9ff04bSjmcneill 					ctl[cnt++].ctl_dir = HDAUDIO_PINDIR_IN;
13318a9ff04bSjmcneill 				}
13328a9ff04bSjmcneill 				break;
13338a9ff04bSjmcneill 			default:
13348a9ff04bSjmcneill 				if (cnt >= maxctls)
13358a9ff04bSjmcneill 					break;
13368a9ff04bSjmcneill 				hda_trace(sc, "add ctrl inamp "
13378a9ff04bSjmcneill 				    "%d:%02X:FF\n", cnt, w->w_nid);
13388a9ff04bSjmcneill 				ctl[cnt].ctl_enable = true;
13398a9ff04bSjmcneill 				ctl[cnt].ctl_widget = w;
13408a9ff04bSjmcneill 				ctl[cnt].ctl_mute = mute;
13418a9ff04bSjmcneill 				ctl[cnt].ctl_step = step;
13428a9ff04bSjmcneill 				ctl[cnt].ctl_size = size;
13438a9ff04bSjmcneill 				ctl[cnt].ctl_offset = offset;
13448a9ff04bSjmcneill 				ctl[cnt].ctl_left = offset;
13458a9ff04bSjmcneill 				ctl[cnt].ctl_right = offset;
13468a9ff04bSjmcneill 				if (w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX)
13478a9ff04bSjmcneill 					ctl[cnt].ctl_ndir = HDAUDIO_PINDIR_OUT;
13488a9ff04bSjmcneill 				else
13498a9ff04bSjmcneill 					ctl[cnt].ctl_ndir = HDAUDIO_PINDIR_IN;
13508a9ff04bSjmcneill 				ctl[cnt++].ctl_dir = HDAUDIO_PINDIR_IN;
13518a9ff04bSjmcneill 				break;
13528a9ff04bSjmcneill 			}
13538a9ff04bSjmcneill 		}
13548a9ff04bSjmcneill 	}
13558a9ff04bSjmcneill 
13568a9ff04bSjmcneill 	sc->sc_ctls = ctl;
13578a9ff04bSjmcneill }
13588a9ff04bSjmcneill 
13598a9ff04bSjmcneill static void
hdafg_parse(struct hdafg_softc * sc)13608a9ff04bSjmcneill hdafg_parse(struct hdafg_softc *sc)
13618a9ff04bSjmcneill {
13628a9ff04bSjmcneill 	struct hdaudio_widget *w;
13638a9ff04bSjmcneill 	uint32_t nodecnt, wcap;
13648a9ff04bSjmcneill 	int nid;
13658a9ff04bSjmcneill 
13668a9ff04bSjmcneill 	nodecnt = hda_get_param(sc, SUBORDINATE_NODE_COUNT);
13678a9ff04bSjmcneill 	sc->sc_startnode = COP_NODECNT_STARTNODE(nodecnt);
13688a9ff04bSjmcneill 	sc->sc_nwidgets = COP_NODECNT_NUMNODES(nodecnt);
13698a9ff04bSjmcneill 	sc->sc_endnode = sc->sc_startnode + sc->sc_nwidgets;
13708a9ff04bSjmcneill 	hda_debug(sc, "afg start %02X end %02X nwidgets %d\n",
13718a9ff04bSjmcneill 	    sc->sc_startnode, sc->sc_endnode, sc->sc_nwidgets);
13728a9ff04bSjmcneill 
13738a9ff04bSjmcneill 	hda_debug(sc, "powering up widgets\n");
13748a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, sc->sc_nid,
13758a9ff04bSjmcneill 	    CORB_SET_POWER_STATE, COP_POWER_STATE_D0);
13768a9ff04bSjmcneill 	hda_delay(100);
13778a9ff04bSjmcneill 	for (nid = sc->sc_startnode; nid < sc->sc_endnode; nid++)
13788a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, nid,
13798a9ff04bSjmcneill 		    CORB_SET_POWER_STATE, COP_POWER_STATE_D0);
13808a9ff04bSjmcneill 	hda_delay(1000);
13818a9ff04bSjmcneill 
13828a9ff04bSjmcneill 	sc->sc_p.afg_cap = hda_get_param(sc, AUDIO_FUNCTION_GROUP_CAPABILITIES);
13838a9ff04bSjmcneill 	sc->sc_p.stream_format = hda_get_param(sc, SUPPORTED_STREAM_FORMATS);
13848a9ff04bSjmcneill 	sc->sc_p.pcm_size_rate = hda_get_param(sc, SUPPORTED_PCM_SIZE_RATES);
13858a9ff04bSjmcneill 	sc->sc_p.outamp_cap = hda_get_param(sc, AMPLIFIER_CAPABILITIES_OUTAMP);
13868a9ff04bSjmcneill 	sc->sc_p.inamp_cap = hda_get_param(sc, AMPLIFIER_CAPABILITIES_INAMP);
13878a9ff04bSjmcneill 	sc->sc_p.power_states = hda_get_param(sc, SUPPORTED_POWER_STATES);
13888a9ff04bSjmcneill 	sc->sc_p.gpio_cnt = hda_get_param(sc, GPIO_COUNT);
13898a9ff04bSjmcneill 
13908a9ff04bSjmcneill 	sc->sc_widgets = kmem_zalloc(sc->sc_nwidgets * sizeof(*w), KM_SLEEP);
13918a9ff04bSjmcneill 	hda_debug(sc, "afg widgets %p-%p\n",
13928a9ff04bSjmcneill 	    sc->sc_widgets, sc->sc_widgets + sc->sc_nwidgets);
13938a9ff04bSjmcneill 
13948a9ff04bSjmcneill 	for (nid = sc->sc_startnode; nid < sc->sc_endnode; nid++) {
13958a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, nid);
13968a9ff04bSjmcneill 		if (w == NULL)
13978a9ff04bSjmcneill 			continue;
13988a9ff04bSjmcneill 		wcap = hdaudio_command(sc->sc_codec, nid, CORB_GET_PARAMETER,
13998a9ff04bSjmcneill 		    COP_AUDIO_WIDGET_CAPABILITIES);
14008a9ff04bSjmcneill 		switch (COP_AWCAP_TYPE(wcap)) {
14018a9ff04bSjmcneill 		case COP_AWCAP_TYPE_BEEP_GENERATOR:
14028a9ff04bSjmcneill 			sc->sc_has_beepgen = true;
14038a9ff04bSjmcneill 			break;
14048a9ff04bSjmcneill 		}
14058a9ff04bSjmcneill 	}
14068a9ff04bSjmcneill 
14078a9ff04bSjmcneill 	for (nid = sc->sc_startnode; nid < sc->sc_endnode; nid++) {
14088a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, nid);
14098a9ff04bSjmcneill 		if (w == NULL)
14108a9ff04bSjmcneill 			continue;
14118a9ff04bSjmcneill 		w->w_afg = sc;
14128a9ff04bSjmcneill 		w->w_nid = nid;
14138a9ff04bSjmcneill 		w->w_enable = true;
14148a9ff04bSjmcneill 		w->w_pflags = 0;
14158a9ff04bSjmcneill 		w->w_audiodev = -1;
14168a9ff04bSjmcneill 		w->w_selconn = -1;
14178a9ff04bSjmcneill 		w->w_bindas = -1;
14188a9ff04bSjmcneill 		w->w_p.eapdbtl = 0xffffffff;
14198a9ff04bSjmcneill 		hdafg_widget_parse(w);
14208a9ff04bSjmcneill 	}
14218a9ff04bSjmcneill }
14228a9ff04bSjmcneill 
14238a9ff04bSjmcneill static void
hdafg_disable_nonaudio(struct hdafg_softc * sc)14248a9ff04bSjmcneill hdafg_disable_nonaudio(struct hdafg_softc *sc)
14258a9ff04bSjmcneill {
14268a9ff04bSjmcneill 	struct hdaudio_widget *w;
14278a9ff04bSjmcneill 	int i;
14288a9ff04bSjmcneill 
14298a9ff04bSjmcneill 	/* Disable power and volume widgets */
14308a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
14318a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
14328a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
14338a9ff04bSjmcneill 			continue;
14348a9ff04bSjmcneill 		if (w->w_type == COP_AWCAP_TYPE_POWER_WIDGET ||
14358a9ff04bSjmcneill 		    w->w_type == COP_AWCAP_TYPE_VOLUME_KNOB) {
14368a9ff04bSjmcneill 			hda_trace(w->w_afg, "disable %02X [nonaudio]\n",
14378a9ff04bSjmcneill 			    w->w_nid);
14388a9ff04bSjmcneill 			w->w_enable = false;
14398a9ff04bSjmcneill 		}
14408a9ff04bSjmcneill 	}
14418a9ff04bSjmcneill }
14428a9ff04bSjmcneill 
14438a9ff04bSjmcneill static void
hdafg_disable_useless(struct hdafg_softc * sc)14448a9ff04bSjmcneill hdafg_disable_useless(struct hdafg_softc *sc)
14458a9ff04bSjmcneill {
14468a9ff04bSjmcneill 	struct hdaudio_widget *w, *cw;
14478a9ff04bSjmcneill 	struct hdaudio_control *ctl;
14488a9ff04bSjmcneill 	int done, found, i, j, k;
14498a9ff04bSjmcneill 	int conn, assoc;
14508a9ff04bSjmcneill 
14518a9ff04bSjmcneill 	/* Disable useless pins */
14528a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
14538a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
14548a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
14558a9ff04bSjmcneill 			continue;
14568a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
14578a9ff04bSjmcneill 			continue;
14588a9ff04bSjmcneill 		conn = COP_CFG_PORT_CONNECTIVITY(w->w_pin.config);
14598a9ff04bSjmcneill 		assoc = COP_CFG_DEFAULT_ASSOCIATION(w->w_pin.config);
14608a9ff04bSjmcneill 		if (conn == COP_PORT_NONE) {
14618a9ff04bSjmcneill 			hda_trace(w->w_afg, "disable %02X [no connectivity]\n",
14628a9ff04bSjmcneill 			    w->w_nid);
14638a9ff04bSjmcneill 			w->w_enable = false;
14648a9ff04bSjmcneill 		}
14658a9ff04bSjmcneill 		if (assoc == 0) {
14668a9ff04bSjmcneill 			hda_trace(w->w_afg, "disable %02X [no association]\n",
14678a9ff04bSjmcneill 			    w->w_nid);
14688a9ff04bSjmcneill 			w->w_enable = false;
14698a9ff04bSjmcneill 		}
14708a9ff04bSjmcneill 	}
14718a9ff04bSjmcneill 
14728a9ff04bSjmcneill 	do {
14738a9ff04bSjmcneill 		done = 1;
14748a9ff04bSjmcneill 		/* Disable and mute controls for disabled widgets */
14758a9ff04bSjmcneill 		for (i = 0; i < sc->sc_nctls; i++) {
14768a9ff04bSjmcneill 			ctl = &sc->sc_ctls[i];
14778a9ff04bSjmcneill 			if (ctl->ctl_enable == false)
14788a9ff04bSjmcneill 				continue;
14798a9ff04bSjmcneill 			if (ctl->ctl_widget->w_enable == false ||
14808a9ff04bSjmcneill 			    (ctl->ctl_childwidget != NULL &&
14818a9ff04bSjmcneill 			     ctl->ctl_childwidget->w_enable == false)) {
14828a9ff04bSjmcneill 				ctl->ctl_forcemute = 1;
14838a9ff04bSjmcneill 				ctl->ctl_muted = HDAUDIO_AMP_MUTE_ALL;
14848a9ff04bSjmcneill 				ctl->ctl_left = ctl->ctl_right = 0;
14858a9ff04bSjmcneill 				ctl->ctl_enable = false;
14868a9ff04bSjmcneill 				if (ctl->ctl_ndir == HDAUDIO_PINDIR_IN)
14878a9ff04bSjmcneill 					ctl->ctl_widget->w_connsenable[
14888a9ff04bSjmcneill 					    ctl->ctl_index] = false;
14898a9ff04bSjmcneill 				done = 0;
14908a9ff04bSjmcneill 				hda_trace(ctl->ctl_widget->w_afg,
14918a9ff04bSjmcneill 				    "disable ctl %d:%02X:%02X [widget disabled]\n",
14928a9ff04bSjmcneill 				    i, ctl->ctl_widget->w_nid,
14938a9ff04bSjmcneill 				    ctl->ctl_childwidget ?
14948a9ff04bSjmcneill 				    ctl->ctl_childwidget->w_nid : 0xff);
14958a9ff04bSjmcneill 			}
14968a9ff04bSjmcneill 		}
14978a9ff04bSjmcneill 		/* Disable useless widgets */
14988a9ff04bSjmcneill 		for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
14998a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, i);
15008a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
15018a9ff04bSjmcneill 				continue;
15028a9ff04bSjmcneill 			/* Disable inputs with disabled child widgets */
15038a9ff04bSjmcneill 			for (j = 0; j < w->w_nconns; j++) {
15048a9ff04bSjmcneill 				if (!w->w_connsenable[j])
15058a9ff04bSjmcneill 					continue;
15068a9ff04bSjmcneill 				cw = hdafg_widget_lookup(sc,
15078a9ff04bSjmcneill 				    w->w_conns[j]);
15088a9ff04bSjmcneill 				if (cw == NULL || cw->w_enable == false) {
15098a9ff04bSjmcneill 					w->w_connsenable[j] = false;
15108a9ff04bSjmcneill 					hda_trace(w->w_afg,
15118a9ff04bSjmcneill 					    "disable conn %02X->%02X "
15128a9ff04bSjmcneill 					    "[disabled child]\n",
15138a9ff04bSjmcneill 					    w->w_nid, w->w_conns[j]);
15148a9ff04bSjmcneill 				}
15158a9ff04bSjmcneill 			}
15168a9ff04bSjmcneill 			if (w->w_type != COP_AWCAP_TYPE_AUDIO_SELECTOR &&
15178a9ff04bSjmcneill 			    w->w_type != COP_AWCAP_TYPE_AUDIO_MIXER)
15188a9ff04bSjmcneill 				continue;
15198a9ff04bSjmcneill 			/* Disable mixers and selectors without inputs */
15208a9ff04bSjmcneill 			found = 0;
15218a9ff04bSjmcneill 			for (j = 0; j < w->w_nconns; j++)
15228a9ff04bSjmcneill 				if (w->w_connsenable[j]) {
15238a9ff04bSjmcneill 					found = 1;
15248a9ff04bSjmcneill 					break;
15258a9ff04bSjmcneill 				}
15268a9ff04bSjmcneill 			if (found == 0) {
15278a9ff04bSjmcneill 				w->w_enable = false;
15288a9ff04bSjmcneill 				done = 0;
15298a9ff04bSjmcneill 				hda_trace(w->w_afg,
15308a9ff04bSjmcneill 				    "disable %02X [inputs disabled]\n",
15318a9ff04bSjmcneill 				    w->w_nid);
15328a9ff04bSjmcneill 			}
15338a9ff04bSjmcneill 			/* Disable nodes without consumers */
15348a9ff04bSjmcneill 			if (w->w_type != COP_AWCAP_TYPE_AUDIO_SELECTOR &&
15358a9ff04bSjmcneill 			    w->w_type != COP_AWCAP_TYPE_AUDIO_MIXER)
15368a9ff04bSjmcneill 				continue;
15378a9ff04bSjmcneill 			found = 0;
15388a9ff04bSjmcneill 			for (k = sc->sc_startnode; k < sc->sc_endnode; k++) {
15398a9ff04bSjmcneill 				cw = hdafg_widget_lookup(sc, k);
15408a9ff04bSjmcneill 				if (cw == NULL || cw->w_enable == false)
15418a9ff04bSjmcneill 					continue;
15428a9ff04bSjmcneill 				for (j = 0; j < cw->w_nconns; j++) {
15438a9ff04bSjmcneill 					if (cw->w_connsenable[j] &&
15448a9ff04bSjmcneill 					    cw->w_conns[j] == i) {
15458a9ff04bSjmcneill 						found = 1;
15468a9ff04bSjmcneill 						break;
15478a9ff04bSjmcneill 					}
15488a9ff04bSjmcneill 				}
15498a9ff04bSjmcneill 			}
15508a9ff04bSjmcneill 			if (found == 0) {
15518a9ff04bSjmcneill 				w->w_enable = false;
15528a9ff04bSjmcneill 				done = 0;
15538a9ff04bSjmcneill 				hda_trace(w->w_afg,
15548a9ff04bSjmcneill 				    "disable %02X [consumers disabled]\n",
15558a9ff04bSjmcneill 				    w->w_nid);
15568a9ff04bSjmcneill 			}
15578a9ff04bSjmcneill 		}
15588a9ff04bSjmcneill 	} while (done == 0);
15598a9ff04bSjmcneill }
15608a9ff04bSjmcneill 
15618a9ff04bSjmcneill static void
hdafg_assoc_trace_undo(struct hdafg_softc * sc,int as,int seq)15628a9ff04bSjmcneill hdafg_assoc_trace_undo(struct hdafg_softc *sc, int as, int seq)
15638a9ff04bSjmcneill {
15648a9ff04bSjmcneill 	struct hdaudio_widget *w;
15658a9ff04bSjmcneill 	int i;
15668a9ff04bSjmcneill 
15678a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
15688a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
15698a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
15708a9ff04bSjmcneill 			continue;
15718a9ff04bSjmcneill 		if (w->w_bindas != as)
15728a9ff04bSjmcneill 			continue;
15738a9ff04bSjmcneill 		if (seq >= 0) {
15748a9ff04bSjmcneill 			w->w_bindseqmask &= ~(1 << seq);
15758a9ff04bSjmcneill 			if (w->w_bindseqmask == 0) {
15768a9ff04bSjmcneill 				w->w_bindas = -1;
15778a9ff04bSjmcneill 				w->w_selconn = -1;
15788a9ff04bSjmcneill 			}
15798a9ff04bSjmcneill 		} else {
15808a9ff04bSjmcneill 			w->w_bindas = -1;
15818a9ff04bSjmcneill 			w->w_bindseqmask = 0;
15828a9ff04bSjmcneill 			w->w_selconn = -1;
15838a9ff04bSjmcneill 		}
15848a9ff04bSjmcneill 	}
15858a9ff04bSjmcneill }
15868a9ff04bSjmcneill 
15878a9ff04bSjmcneill static int
hdafg_assoc_trace_dac(struct hdafg_softc * sc,int as,int seq,int nid,int dupseq,int minassoc,int only,int depth)15888a9ff04bSjmcneill hdafg_assoc_trace_dac(struct hdafg_softc *sc, int as, int seq,
15898a9ff04bSjmcneill     int nid, int dupseq, int minassoc, int only, int depth)
15908a9ff04bSjmcneill {
15918a9ff04bSjmcneill 	struct hdaudio_widget *w;
15928a9ff04bSjmcneill 	int i, im = -1;
15938a9ff04bSjmcneill 	int m = 0, ret;
15948a9ff04bSjmcneill 
15958a9ff04bSjmcneill 	if (depth >= HDAUDIO_PARSE_MAXDEPTH)
15968a9ff04bSjmcneill 		return 0;
15978a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
15988a9ff04bSjmcneill 	if (w == NULL || w->w_enable == false)
15998a9ff04bSjmcneill 		return 0;
16008a9ff04bSjmcneill 	/* We use only unused widgets */
16018a9ff04bSjmcneill 	if (w->w_bindas >= 0 && w->w_bindas != as) {
16028a9ff04bSjmcneill 		if (!only)
16038a9ff04bSjmcneill 			hda_trace(sc, "depth %d nid %02X busy by assoc %d\n",
16048a9ff04bSjmcneill 			    depth + 1, nid, w->w_bindas);
16058a9ff04bSjmcneill 		return 0;
16068a9ff04bSjmcneill 	}
16078a9ff04bSjmcneill 	if (dupseq < 0) {
16088a9ff04bSjmcneill 		if (w->w_bindseqmask != 0) {
16098a9ff04bSjmcneill 			if (!only)
16108a9ff04bSjmcneill 				hda_trace(sc,
16118a9ff04bSjmcneill 				    "depth %d nid %02X busy by seqmask %x\n",
16128a9ff04bSjmcneill 				    depth + 1, nid, w->w_bindas);
16138a9ff04bSjmcneill 			return 0;
16148a9ff04bSjmcneill 		}
16158a9ff04bSjmcneill 	} else {
16168a9ff04bSjmcneill 		/* If this is headphones, allow duplicate first pin */
16178a9ff04bSjmcneill 		if (w->w_bindseqmask != 0 &&
16188a9ff04bSjmcneill 		    (w->w_bindseqmask & (1 << dupseq)) == 0)
16198a9ff04bSjmcneill 			return 0;
16208a9ff04bSjmcneill 	}
16218a9ff04bSjmcneill 
16228a9ff04bSjmcneill 	switch (w->w_type) {
16238a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_INPUT:
16248a9ff04bSjmcneill 		break;
16258a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_OUTPUT:
16268a9ff04bSjmcneill 		/* If we are tracing HP take only dac of first pin */
16278a9ff04bSjmcneill 		if ((only == 0 || only == w->w_nid) &&
16288a9ff04bSjmcneill 		    (w->w_nid >= minassoc) && (dupseq < 0 || w->w_nid ==
16298a9ff04bSjmcneill 		    sc->sc_assocs[as].as_dacs[dupseq]))
16308a9ff04bSjmcneill 			m = w->w_nid;
16318a9ff04bSjmcneill 		break;
16328a9ff04bSjmcneill 	case COP_AWCAP_TYPE_PIN_COMPLEX:
16338a9ff04bSjmcneill 		if (depth > 0)
16348a9ff04bSjmcneill 			break;
16358a9ff04bSjmcneill 		/* FALLTHROUGH */
16368a9ff04bSjmcneill 	default:
16378a9ff04bSjmcneill 		for (i = 0; i < w->w_nconns; i++) {
16388a9ff04bSjmcneill 			if (w->w_connsenable[i] == false)
16398a9ff04bSjmcneill 				continue;
16408a9ff04bSjmcneill 			if (w->w_selconn != -1 && w->w_selconn != i)
16418a9ff04bSjmcneill 				continue;
16428a9ff04bSjmcneill 			ret = hdafg_assoc_trace_dac(sc, as, seq,
16438a9ff04bSjmcneill 			    w->w_conns[i], dupseq, minassoc, only, depth + 1);
16448a9ff04bSjmcneill 			if (ret) {
16458a9ff04bSjmcneill 				if (m == 0 || ret < m) {
16468a9ff04bSjmcneill 					m = ret;
16478a9ff04bSjmcneill 					im = i;
16488a9ff04bSjmcneill 				}
16498a9ff04bSjmcneill 				if (only || dupseq >= 0)
16508a9ff04bSjmcneill 					break;
16518a9ff04bSjmcneill 			}
16528a9ff04bSjmcneill 		}
16538a9ff04bSjmcneill 		if (m && only && ((w->w_nconns > 1 &&
16548a9ff04bSjmcneill 		    w->w_type != COP_AWCAP_TYPE_AUDIO_MIXER) ||
16558a9ff04bSjmcneill 		    w->w_type == COP_AWCAP_TYPE_AUDIO_SELECTOR))
16568a9ff04bSjmcneill 			w->w_selconn = im;
16578a9ff04bSjmcneill 		break;
16588a9ff04bSjmcneill 	}
16598a9ff04bSjmcneill 	if (m && only) {
16608a9ff04bSjmcneill 		w->w_bindas = as;
16618a9ff04bSjmcneill 		w->w_bindseqmask |= (1 << seq);
16628a9ff04bSjmcneill 	}
16638a9ff04bSjmcneill 	if (!only)
16648a9ff04bSjmcneill 		hda_trace(sc, "depth %d nid %02X dupseq %d returned %02X\n",
16658a9ff04bSjmcneill 		    depth + 1, nid, dupseq, m);
16668a9ff04bSjmcneill 
16678a9ff04bSjmcneill 	return m;
16688a9ff04bSjmcneill }
16698a9ff04bSjmcneill 
16708a9ff04bSjmcneill static int
hdafg_assoc_trace_out(struct hdafg_softc * sc,int as,int seq)16718a9ff04bSjmcneill hdafg_assoc_trace_out(struct hdafg_softc *sc, int as, int seq)
16728a9ff04bSjmcneill {
16738a9ff04bSjmcneill 	struct hdaudio_assoc *assocs = sc->sc_assocs;
16748a9ff04bSjmcneill 	int i, hpredir;
16758a9ff04bSjmcneill 	int minassoc, res;
16768a9ff04bSjmcneill 
16778a9ff04bSjmcneill 	/* Find next pin */
16788a9ff04bSjmcneill 	for (i = seq; i < HDAUDIO_MAXPINS && assocs[as].as_pins[i] == 0; i++)
16798a9ff04bSjmcneill 		;
16808a9ff04bSjmcneill 	/* Check if there is any left, if not then we have succeeded */
16818a9ff04bSjmcneill 	if (i == HDAUDIO_MAXPINS)
16828a9ff04bSjmcneill 		return 1;
16838a9ff04bSjmcneill 
16848a9ff04bSjmcneill 	hpredir = (i == 15 && assocs[as].as_fakeredir == 0) ?
16858a9ff04bSjmcneill 	    assocs[as].as_hpredir : -1;
16868a9ff04bSjmcneill 	minassoc = res = 0;
16878a9ff04bSjmcneill 	do {
16888a9ff04bSjmcneill 		/* Trace this pin taking min nid into account */
16898a9ff04bSjmcneill 		res = hdafg_assoc_trace_dac(sc, as, i,
16908a9ff04bSjmcneill 		    assocs[as].as_pins[i], hpredir, minassoc, 0, 0);
16918a9ff04bSjmcneill 		if (res == 0) {
16928a9ff04bSjmcneill 			/* If we failed, return to previous and redo it */
16938a9ff04bSjmcneill 			hda_trace(sc, "  trace failed as=%d seq=%d pin=%02X "
16948a9ff04bSjmcneill 			    "hpredir=%d minassoc=%d\n",
16958a9ff04bSjmcneill 			    as, seq, assocs[as].as_pins[i], hpredir, minassoc);
16968a9ff04bSjmcneill 			return 0;
16978a9ff04bSjmcneill 		}
16988a9ff04bSjmcneill 		/* Trace again to mark the path */
16998a9ff04bSjmcneill 		hdafg_assoc_trace_dac(sc, as, i,
17008a9ff04bSjmcneill 		    assocs[as].as_pins[i], hpredir, minassoc, res, 0);
17018a9ff04bSjmcneill 		assocs[as].as_dacs[i] = res;
17028a9ff04bSjmcneill 		/* We succeeded, so call next */
17038a9ff04bSjmcneill 		if (hdafg_assoc_trace_out(sc, as, i + 1))
17048a9ff04bSjmcneill 			return 1;
17058a9ff04bSjmcneill 		/* If next failed, we should retry with next min */
17068a9ff04bSjmcneill 		hdafg_assoc_trace_undo(sc, as, i);
17078a9ff04bSjmcneill 		assocs[as].as_dacs[i] = 0;
17088a9ff04bSjmcneill 		minassoc = res + 1;
17098a9ff04bSjmcneill 	} while (1);
17108a9ff04bSjmcneill }
17118a9ff04bSjmcneill 
17128a9ff04bSjmcneill static int
hdafg_assoc_trace_adc(struct hdafg_softc * sc,int assoc,int seq,int nid,int only,int depth)17138a9ff04bSjmcneill hdafg_assoc_trace_adc(struct hdafg_softc *sc, int assoc, int seq,
17148a9ff04bSjmcneill     int nid, int only, int depth)
17158a9ff04bSjmcneill {
17168a9ff04bSjmcneill 	struct hdaudio_widget *w, *wc;
17178a9ff04bSjmcneill 	int i, j;
17188a9ff04bSjmcneill 	int res = 0;
17198a9ff04bSjmcneill 
17208a9ff04bSjmcneill 	if (depth > HDAUDIO_PARSE_MAXDEPTH)
17218a9ff04bSjmcneill 		return 0;
17228a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
17238a9ff04bSjmcneill 	if (w == NULL || w->w_enable == false)
17248a9ff04bSjmcneill 		return 0;
17258a9ff04bSjmcneill 	/* Use only unused widgets */
17268a9ff04bSjmcneill 	if (w->w_bindas >= 0 && w->w_bindas != assoc)
17278a9ff04bSjmcneill 		return 0;
17288a9ff04bSjmcneill 
17298a9ff04bSjmcneill 	switch (w->w_type) {
17308a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_INPUT:
17318a9ff04bSjmcneill 		if (only == w->w_nid)
17328a9ff04bSjmcneill 			res = 1;
17338a9ff04bSjmcneill 		break;
17348a9ff04bSjmcneill 	case COP_AWCAP_TYPE_PIN_COMPLEX:
17358a9ff04bSjmcneill 		if (depth > 0)
17368a9ff04bSjmcneill 			break;
17378a9ff04bSjmcneill 		/* FALLTHROUGH */
17388a9ff04bSjmcneill 	default:
17398a9ff04bSjmcneill 		/* Try to find reachable ADCs with specified nid */
17408a9ff04bSjmcneill 		for (j = sc->sc_startnode; j < sc->sc_endnode; j++) {
17418a9ff04bSjmcneill 			wc = hdafg_widget_lookup(sc, j);
17428a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
17438a9ff04bSjmcneill 				continue;
17448a9ff04bSjmcneill 			for (i = 0; i < wc->w_nconns; i++) {
17458a9ff04bSjmcneill 				if (wc->w_connsenable[i] == false)
17468a9ff04bSjmcneill 					continue;
17478a9ff04bSjmcneill 				if (wc->w_conns[i] != nid)
17488a9ff04bSjmcneill 					continue;
17498a9ff04bSjmcneill 				if (hdafg_assoc_trace_adc(sc, assoc, seq,
17508a9ff04bSjmcneill 				    j, only, depth + 1) != 0) {
17518a9ff04bSjmcneill 					res = 1;
17528a9ff04bSjmcneill 					if (((wc->w_nconns > 1 &&
17538a9ff04bSjmcneill 					    wc->w_type != COP_AWCAP_TYPE_AUDIO_MIXER) ||
17548a9ff04bSjmcneill 					    wc->w_type != COP_AWCAP_TYPE_AUDIO_SELECTOR)
17558a9ff04bSjmcneill 					    && wc->w_selconn == -1)
17568a9ff04bSjmcneill 						wc->w_selconn = i;
17578a9ff04bSjmcneill 				}
17588a9ff04bSjmcneill 			}
17598a9ff04bSjmcneill 		}
17608a9ff04bSjmcneill 		break;
17618a9ff04bSjmcneill 	}
17628a9ff04bSjmcneill 	if (res) {
17638a9ff04bSjmcneill 		w->w_bindas = assoc;
17648a9ff04bSjmcneill 		w->w_bindseqmask |= (1 << seq);
17658a9ff04bSjmcneill 	}
17668a9ff04bSjmcneill 	return res;
17678a9ff04bSjmcneill }
17688a9ff04bSjmcneill 
17698a9ff04bSjmcneill static int
hdafg_assoc_trace_in(struct hdafg_softc * sc,int assoc)17708a9ff04bSjmcneill hdafg_assoc_trace_in(struct hdafg_softc *sc, int assoc)
17718a9ff04bSjmcneill {
17728a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
17738a9ff04bSjmcneill 	struct hdaudio_widget *w;
17748a9ff04bSjmcneill 	int i, j, k;
17758a9ff04bSjmcneill 
17768a9ff04bSjmcneill 	for (j = sc->sc_startnode; j < sc->sc_endnode; j++) {
17778a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, j);
17788a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
17798a9ff04bSjmcneill 			continue;
17808a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_AUDIO_INPUT)
17818a9ff04bSjmcneill 			continue;
17828a9ff04bSjmcneill 		if (w->w_bindas >= 0 && w->w_bindas != assoc)
17838a9ff04bSjmcneill 			continue;
17848a9ff04bSjmcneill 
17858a9ff04bSjmcneill 		/* Find next pin */
17868a9ff04bSjmcneill 		for (i = 0; i < HDAUDIO_MAXPINS; i++) {
17878a9ff04bSjmcneill 			if (as[assoc].as_pins[i] == 0)
17888a9ff04bSjmcneill 				continue;
17898a9ff04bSjmcneill 			/* Trace this pin taking goal into account */
17908a9ff04bSjmcneill 			if (hdafg_assoc_trace_adc(sc, assoc, i,
17918a9ff04bSjmcneill 			    as[assoc].as_pins[i], j, 0) == 0) {
17928a9ff04bSjmcneill 				hdafg_assoc_trace_undo(sc, assoc, -1);
17938a9ff04bSjmcneill 				for (k = 0; k < HDAUDIO_MAXPINS; k++)
17948a9ff04bSjmcneill 					as[assoc].as_dacs[k] = 0;
17958a9ff04bSjmcneill 				break;
17968a9ff04bSjmcneill 			}
17978a9ff04bSjmcneill 			as[assoc].as_dacs[i] = j;
17988a9ff04bSjmcneill 		}
17998a9ff04bSjmcneill 		if (i == HDAUDIO_MAXPINS)
18008a9ff04bSjmcneill 			return 1;
18018a9ff04bSjmcneill 	}
18028a9ff04bSjmcneill 	return 0;
18038a9ff04bSjmcneill }
18048a9ff04bSjmcneill 
18058a9ff04bSjmcneill static int
hdafg_assoc_trace_to_out(struct hdafg_softc * sc,int nid,int depth)18068a9ff04bSjmcneill hdafg_assoc_trace_to_out(struct hdafg_softc *sc, int nid, int depth)
18078a9ff04bSjmcneill {
18088a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
18098a9ff04bSjmcneill 	struct hdaudio_widget *w, *wc;
18108a9ff04bSjmcneill 	int i, j;
18118a9ff04bSjmcneill 	int res = 0;
18128a9ff04bSjmcneill 
18138a9ff04bSjmcneill 	if (depth > HDAUDIO_PARSE_MAXDEPTH)
18148a9ff04bSjmcneill 		return 0;
18158a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
18168a9ff04bSjmcneill 	if (w == NULL || w->w_enable == false)
18178a9ff04bSjmcneill 		return 0;
18188a9ff04bSjmcneill 
18198a9ff04bSjmcneill 	/* Use only unused widgets */
18208a9ff04bSjmcneill 	if (depth > 0 && w->w_bindas != -1) {
18218a9ff04bSjmcneill 		if (w->w_bindas < 0 ||
18228a9ff04bSjmcneill 		    as[w->w_bindas].as_dir == HDAUDIO_PINDIR_OUT) {
18238a9ff04bSjmcneill 			return 1;
18248a9ff04bSjmcneill 		} else {
18258a9ff04bSjmcneill 			return 0;
18268a9ff04bSjmcneill 		}
18278a9ff04bSjmcneill 	}
18288a9ff04bSjmcneill 
18298a9ff04bSjmcneill 	switch (w->w_type) {
18308a9ff04bSjmcneill 	case COP_AWCAP_TYPE_AUDIO_INPUT:
18318a9ff04bSjmcneill 		/* Do not traverse input (not yet supported) */
18328a9ff04bSjmcneill 		break;
18338a9ff04bSjmcneill 	case COP_AWCAP_TYPE_PIN_COMPLEX:
18348a9ff04bSjmcneill 		if (depth > 0)
18358a9ff04bSjmcneill 			break;
18368a9ff04bSjmcneill 		/* FALLTHROUGH */
18378a9ff04bSjmcneill 	default:
18388a9ff04bSjmcneill 		/* Try to find reachable ADCs with specified nid */
18398a9ff04bSjmcneill 		for (j = sc->sc_startnode; j < sc->sc_endnode; j++) {
18408a9ff04bSjmcneill 			wc = hdafg_widget_lookup(sc, j);
18418a9ff04bSjmcneill 			if (wc == NULL || wc->w_enable == false)
18428a9ff04bSjmcneill 				continue;
18438a9ff04bSjmcneill 			for (i = 0; i < wc->w_nconns; i++) {
18448a9ff04bSjmcneill 				if (wc->w_connsenable[i] == false)
18458a9ff04bSjmcneill 					continue;
18468a9ff04bSjmcneill 				if (wc->w_conns[i] != nid)
18478a9ff04bSjmcneill 					continue;
18488a9ff04bSjmcneill 				if (hdafg_assoc_trace_to_out(sc,
18498a9ff04bSjmcneill 				    j, depth + 1) != 0) {
18508a9ff04bSjmcneill 					res = 1;
18518a9ff04bSjmcneill 					if (wc->w_type ==
18528a9ff04bSjmcneill 					    COP_AWCAP_TYPE_AUDIO_SELECTOR &&
18538a9ff04bSjmcneill 					    wc->w_selconn == -1)
18548a9ff04bSjmcneill 						wc->w_selconn = i;
18558a9ff04bSjmcneill 				}
18568a9ff04bSjmcneill 			}
18578a9ff04bSjmcneill 		}
18588a9ff04bSjmcneill 		break;
18598a9ff04bSjmcneill 	}
18608a9ff04bSjmcneill 	if (res)
18618a9ff04bSjmcneill 		w->w_bindas = -2;
18628a9ff04bSjmcneill 	return res;
18638a9ff04bSjmcneill }
18648a9ff04bSjmcneill 
18658a9ff04bSjmcneill static void
hdafg_assoc_trace_misc(struct hdafg_softc * sc)18668a9ff04bSjmcneill hdafg_assoc_trace_misc(struct hdafg_softc *sc)
18678a9ff04bSjmcneill {
18688a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
18698a9ff04bSjmcneill 	struct hdaudio_widget *w;
18708a9ff04bSjmcneill 	int j;
18718a9ff04bSjmcneill 
18728a9ff04bSjmcneill 	/* Input monitor */
18738a9ff04bSjmcneill 	/*
18748a9ff04bSjmcneill 	 * Find mixer associated with input, but supplying signal
18758a9ff04bSjmcneill 	 * for output associations. Hope it will be input monitor.
18768a9ff04bSjmcneill 	 */
18778a9ff04bSjmcneill 	for (j = sc->sc_startnode; j < sc->sc_endnode; j++) {
18788a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, j);
18798a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
18808a9ff04bSjmcneill 			continue;
18818a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_AUDIO_MIXER)
18828a9ff04bSjmcneill 			continue;
18838a9ff04bSjmcneill 		if (w->w_bindas < 0 ||
18848a9ff04bSjmcneill 		    as[w->w_bindas].as_dir != HDAUDIO_PINDIR_IN)
18858a9ff04bSjmcneill 			continue;
18868a9ff04bSjmcneill 		if (hdafg_assoc_trace_to_out(sc, w->w_nid, 0)) {
18878a9ff04bSjmcneill 			w->w_pflags |= HDAUDIO_ADC_MONITOR;
18888a9ff04bSjmcneill 			w->w_audiodev = HDAUDIO_MIXER_IMIX;
18898a9ff04bSjmcneill 		}
18908a9ff04bSjmcneill 	}
18918a9ff04bSjmcneill 
18928a9ff04bSjmcneill 	/* Beeper */
18938a9ff04bSjmcneill 	for (j = sc->sc_startnode; j < sc->sc_endnode; j++) {
18948a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, j);
18958a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
18968a9ff04bSjmcneill 			continue;
18978a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_BEEP_GENERATOR)
18988a9ff04bSjmcneill 			continue;
18998a9ff04bSjmcneill 		if (hdafg_assoc_trace_to_out(sc, w->w_nid, 0)) {
19008a9ff04bSjmcneill 			hda_debug(sc, "beeper %02X traced to out\n", w->w_nid);
19018a9ff04bSjmcneill 		}
19028a9ff04bSjmcneill 		w->w_bindas = -2;
19038a9ff04bSjmcneill 	}
19048a9ff04bSjmcneill }
19058a9ff04bSjmcneill 
19068a9ff04bSjmcneill static void
hdafg_build_tree(struct hdafg_softc * sc)19078a9ff04bSjmcneill hdafg_build_tree(struct hdafg_softc *sc)
19088a9ff04bSjmcneill {
19098a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
19108a9ff04bSjmcneill 	int i, j, res;
19118a9ff04bSjmcneill 
19128a9ff04bSjmcneill 	/* Trace all associations in order of their numbers */
19138a9ff04bSjmcneill 
19148a9ff04bSjmcneill 	/* Trace DACs first */
19158a9ff04bSjmcneill 	for (j = 0; j < sc->sc_nassocs; j++) {
19168a9ff04bSjmcneill 		if (as[j].as_enable == false)
19178a9ff04bSjmcneill 			continue;
19188a9ff04bSjmcneill 		if (as[j].as_dir != HDAUDIO_PINDIR_OUT)
19198a9ff04bSjmcneill 			continue;
19208a9ff04bSjmcneill retry:
19218a9ff04bSjmcneill 		res = hdafg_assoc_trace_out(sc, j, 0);
19228a9ff04bSjmcneill 		if (res == 0 && as[j].as_hpredir >= 0 &&
19238a9ff04bSjmcneill 		    as[j].as_fakeredir == 0) {
19248a9ff04bSjmcneill 			/*
19258a9ff04bSjmcneill 			 * If codec can't do analog HP redirection
19268a9ff04bSjmcneill 			 * try to make it using one more DAC
19278a9ff04bSjmcneill 			 */
19288a9ff04bSjmcneill 			as[j].as_fakeredir = 1;
19298a9ff04bSjmcneill 			goto retry;
19308a9ff04bSjmcneill 		}
19318a9ff04bSjmcneill 		if (!res) {
19328a9ff04bSjmcneill 			hda_debug(sc, "disable assoc %d (%d) [trace failed]\n",
19338a9ff04bSjmcneill 			    j, as[j].as_index);
19348a9ff04bSjmcneill 			for (i = 0; i < HDAUDIO_MAXPINS; i++) {
19358a9ff04bSjmcneill 				if (as[j].as_pins[i] == 0)
19368a9ff04bSjmcneill 					continue;
19378a9ff04bSjmcneill 				hda_debug(sc, "  assoc %d pin%d: %02X\n", j, i,
19388a9ff04bSjmcneill 				    as[j].as_pins[i]);
19398a9ff04bSjmcneill 			}
19408a9ff04bSjmcneill 			for (i = 0; i < HDAUDIO_MAXPINS; i++) {
19418a9ff04bSjmcneill 				if (as[j].as_dacs[i] == 0)
19428a9ff04bSjmcneill 					continue;
19438a9ff04bSjmcneill 				hda_debug(sc, "  assoc %d dac%d: %02X\n", j, i,
19448a9ff04bSjmcneill 				    as[j].as_dacs[i]);
19458a9ff04bSjmcneill 			}
19468a9ff04bSjmcneill 
19478a9ff04bSjmcneill 			as[j].as_enable = false;
19488a9ff04bSjmcneill 		}
19498a9ff04bSjmcneill 	}
19508a9ff04bSjmcneill 
19518a9ff04bSjmcneill 	/* Trace ADCs */
19528a9ff04bSjmcneill 	for (j = 0; j < sc->sc_nassocs; j++) {
19538a9ff04bSjmcneill 		if (as[j].as_enable == false)
19548a9ff04bSjmcneill 			continue;
19558a9ff04bSjmcneill 		if (as[j].as_dir != HDAUDIO_PINDIR_IN)
19568a9ff04bSjmcneill 			continue;
19578a9ff04bSjmcneill 		res = hdafg_assoc_trace_in(sc, j);
19588a9ff04bSjmcneill 		if (!res) {
19598a9ff04bSjmcneill 			hda_debug(sc, "disable assoc %d (%d) [trace failed]\n",
19608a9ff04bSjmcneill 			    j, as[j].as_index);
19618a9ff04bSjmcneill 			for (i = 0; i < HDAUDIO_MAXPINS; i++) {
19628a9ff04bSjmcneill 				if (as[j].as_pins[i] == 0)
19638a9ff04bSjmcneill 					continue;
19648a9ff04bSjmcneill 				hda_debug(sc, "  assoc %d pin%d: %02X\n", j, i,
19658a9ff04bSjmcneill 				    as[j].as_pins[i]);
19668a9ff04bSjmcneill 			}
19678a9ff04bSjmcneill 			for (i = 0; i < HDAUDIO_MAXPINS; i++) {
19688a9ff04bSjmcneill 				if (as[j].as_dacs[i] == 0)
19698a9ff04bSjmcneill 					continue;
19708a9ff04bSjmcneill 				hda_debug(sc, "  assoc %d adc%d: %02X\n", j, i,
19718a9ff04bSjmcneill 				    as[j].as_dacs[i]);
19728a9ff04bSjmcneill 			}
19738a9ff04bSjmcneill 
19748a9ff04bSjmcneill 			as[j].as_enable = false;
19758a9ff04bSjmcneill 		}
19768a9ff04bSjmcneill 	}
19778a9ff04bSjmcneill 
19788a9ff04bSjmcneill 	/* Trace mixer and beeper pseudo associations */
19798a9ff04bSjmcneill 	hdafg_assoc_trace_misc(sc);
19808a9ff04bSjmcneill }
19818a9ff04bSjmcneill 
19828a9ff04bSjmcneill static void
hdafg_prepare_pin_controls(struct hdafg_softc * sc)19838a9ff04bSjmcneill hdafg_prepare_pin_controls(struct hdafg_softc *sc)
19848a9ff04bSjmcneill {
19858a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
19868a9ff04bSjmcneill 	struct hdaudio_widget *w;
19878a9ff04bSjmcneill 	uint32_t pincap;
19888a9ff04bSjmcneill 	int i;
19898a9ff04bSjmcneill 
19908a9ff04bSjmcneill 	hda_debug(sc, "*** prepare pin controls, nwidgets = %d\n",
19918a9ff04bSjmcneill 	    sc->sc_nwidgets);
19928a9ff04bSjmcneill 
19938a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nwidgets; i++) {
19948a9ff04bSjmcneill 		w = &sc->sc_widgets[i];
19958a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX) {
19968a9ff04bSjmcneill 			hda_debug(sc, "  skipping pin %02X type 0x%x\n",
19978a9ff04bSjmcneill 			    w->w_nid, w->w_type);
19988a9ff04bSjmcneill 			continue;
19998a9ff04bSjmcneill 		}
20008a9ff04bSjmcneill 		pincap = w->w_pin.cap;
20018a9ff04bSjmcneill 
20028a9ff04bSjmcneill 		/* Disable everything */
20038a9ff04bSjmcneill 		w->w_pin.ctrl &= ~(
20048a9ff04bSjmcneill 		    COP_PWC_VREF_ENABLE_MASK |
20058a9ff04bSjmcneill 		    COP_PWC_IN_ENABLE |
20068a9ff04bSjmcneill 		    COP_PWC_OUT_ENABLE |
20078a9ff04bSjmcneill 		    COP_PWC_HPHN_ENABLE);
20088a9ff04bSjmcneill 
20098a9ff04bSjmcneill 		if (w->w_enable == false ||
20108a9ff04bSjmcneill 		    w->w_bindas < 0 || as[w->w_bindas].as_enable == false) {
20118a9ff04bSjmcneill 			/* Pin is unused so leave it disabled */
20128a9ff04bSjmcneill 			if ((pincap & (COP_PINCAP_OUTPUT_CAPABLE |
20138a9ff04bSjmcneill 			    COP_PINCAP_INPUT_CAPABLE)) ==
20148a9ff04bSjmcneill 			    (COP_PINCAP_OUTPUT_CAPABLE |
20158a9ff04bSjmcneill 			    COP_PINCAP_INPUT_CAPABLE)) {
20168a9ff04bSjmcneill 				hda_debug(sc, "pin %02X off, "
20178a9ff04bSjmcneill 				    "in/out capable (bindas=%d "
20188a9ff04bSjmcneill 				    "enable=%d as_enable=%d)\n",
20198a9ff04bSjmcneill 				    w->w_nid, w->w_bindas, w->w_enable,
20208a9ff04bSjmcneill 				    w->w_bindas >= 0 ?
20218a9ff04bSjmcneill 				    as[w->w_bindas].as_enable : -1);
20228a9ff04bSjmcneill 				w->w_pin.ctrl |= COP_PWC_OUT_ENABLE;
20238a9ff04bSjmcneill 			} else
20248a9ff04bSjmcneill 				hda_debug(sc, "pin %02X off\n", w->w_nid);
20258a9ff04bSjmcneill 			continue;
20268a9ff04bSjmcneill 		} else if (as[w->w_bindas].as_dir == HDAUDIO_PINDIR_IN) {
20278a9ff04bSjmcneill 			/* Input pin, configure for input */
20288a9ff04bSjmcneill 			if (pincap & COP_PINCAP_INPUT_CAPABLE)
20298a9ff04bSjmcneill 				w->w_pin.ctrl |= COP_PWC_IN_ENABLE;
20308a9ff04bSjmcneill 
20318a9ff04bSjmcneill 			hda_debug(sc, "pin %02X in ctrl 0x%x\n", w->w_nid,
20328a9ff04bSjmcneill 			    w->w_pin.ctrl);
20338a9ff04bSjmcneill 
20348a9ff04bSjmcneill 			if (COP_CFG_DEFAULT_DEVICE(w->w_pin.config) !=
20358a9ff04bSjmcneill 			    COP_DEVICE_MIC_IN)
20368a9ff04bSjmcneill 				continue;
20378a9ff04bSjmcneill 			if (COP_PINCAP_VREF_CONTROL(pincap) & COP_VREF_80)
20388a9ff04bSjmcneill 				w->w_pin.ctrl |= COP_PWC_VREF_80;
20398a9ff04bSjmcneill 			else if (COP_PINCAP_VREF_CONTROL(pincap) & COP_VREF_50)
20408a9ff04bSjmcneill 				w->w_pin.ctrl |= COP_PWC_VREF_50;
20418a9ff04bSjmcneill 		} else {
20428a9ff04bSjmcneill 			/* Output pin, configure for output */
20438a9ff04bSjmcneill 			if (pincap & COP_PINCAP_OUTPUT_CAPABLE)
20448a9ff04bSjmcneill 				w->w_pin.ctrl |= COP_PWC_OUT_ENABLE;
20458a9ff04bSjmcneill 			if ((pincap & COP_PINCAP_HEADPHONE_DRIVE_CAPABLE) &&
20468a9ff04bSjmcneill 			    (COP_CFG_DEFAULT_DEVICE(w->w_pin.config) ==
20478a9ff04bSjmcneill 			    COP_DEVICE_HP_OUT))
20488a9ff04bSjmcneill 				w->w_pin.ctrl |= COP_PWC_HPHN_ENABLE;
20498a9ff04bSjmcneill 			/* XXX VREF */
20508a9ff04bSjmcneill 			hda_debug(sc, "pin %02X out ctrl 0x%x\n", w->w_nid,
20518a9ff04bSjmcneill 			    w->w_pin.ctrl);
20528a9ff04bSjmcneill 		}
20538a9ff04bSjmcneill 	}
20548a9ff04bSjmcneill }
20558a9ff04bSjmcneill 
20568a9ff04bSjmcneill #if defined(HDAFG_DEBUG) && HDAFG_DEBUG > 1
2057a6e6c18eSchristos static void
hdafg_dump_ctl(const struct hdafg_softc * sc,const struct hdaudio_control * ctl)2058a6e6c18eSchristos hdafg_dump_ctl(const struct hdafg_softc *sc, const struct hdaudio_control *ctl)
2059a6e6c18eSchristos {
2060a6e6c18eSchristos 	int type = ctl->ctl_widget ? ctl->ctl_widget->w_type : -1;
2061a6e6c18eSchristos 	int i = (int)(ctl - sc->sc_ctls);
20628a9ff04bSjmcneill 
20638a9ff04bSjmcneill 	hda_print(sc, "%03X: nid %02X type %d %s (%s) index %d",
20648a9ff04bSjmcneill 	    i, (ctl->ctl_widget ? ctl->ctl_widget->w_nid : -1), type,
2065a6e6c18eSchristos 	    ctl->ctl_ndir == HDAUDIO_PINDIR_IN ? "in " : "out",
2066a6e6c18eSchristos 	    ctl->ctl_dir == HDAUDIO_PINDIR_IN ? "in " : "out",
20678a9ff04bSjmcneill 	    ctl->ctl_index);
2068a6e6c18eSchristos 
20698a9ff04bSjmcneill 	if (ctl->ctl_childwidget)
2070a6e6c18eSchristos 		hda_print1(sc, " cnid %02X", ctl->ctl_childwidget->w_nid);
20718a9ff04bSjmcneill 	else
20728a9ff04bSjmcneill 		hda_print1(sc, "          ");
20738a9ff04bSjmcneill 	hda_print1(sc, "\n");
20748a9ff04bSjmcneill 	hda_print(sc, "     mute: %d step: %3d size: %3d off: %3d%s\n",
20758a9ff04bSjmcneill 	    ctl->ctl_mute, ctl->ctl_step, ctl->ctl_size,
2076a6e6c18eSchristos 	    ctl->ctl_offset, ctl->ctl_enable == false ? " [DISABLED]" : "");
20778a9ff04bSjmcneill }
20788a9ff04bSjmcneill #endif
2079a6e6c18eSchristos 
2080a6e6c18eSchristos static void
hdafg_dump(const struct hdafg_softc * sc)2081a6e6c18eSchristos hdafg_dump(const struct hdafg_softc *sc)
2082a6e6c18eSchristos {
2083a6e6c18eSchristos #if defined(HDAFG_DEBUG) && HDAFG_DEBUG > 1
2084a6e6c18eSchristos 	for (int i = 0; i < sc->sc_nctls; i++)
2085a6e6c18eSchristos 		hdafg_dump_ctl(sc, &sc->sc_ctls[i]);
2086a6e6c18eSchristos #endif
20878a9ff04bSjmcneill }
20888a9ff04bSjmcneill 
20898a9ff04bSjmcneill static int
hdafg_match(device_t parent,cfdata_t match,void * opaque)20908a9ff04bSjmcneill hdafg_match(device_t parent, cfdata_t match, void *opaque)
20918a9ff04bSjmcneill {
20928a9ff04bSjmcneill 	prop_dictionary_t args = opaque;
20938a9ff04bSjmcneill 	uint8_t fgtype;
20948a9ff04bSjmcneill 	bool rv;
20958a9ff04bSjmcneill 
20968a9ff04bSjmcneill 	rv = prop_dictionary_get_uint8(args, "function-group-type", &fgtype);
20978a9ff04bSjmcneill 	if (rv == false || fgtype != HDAUDIO_GROUP_TYPE_AFG)
20988a9ff04bSjmcneill 		return 0;
20998a9ff04bSjmcneill 
21008a9ff04bSjmcneill 	return 1;
21018a9ff04bSjmcneill }
21028a9ff04bSjmcneill 
21038a9ff04bSjmcneill static void
hdafg_disable_unassoc(struct hdafg_softc * sc)21048a9ff04bSjmcneill hdafg_disable_unassoc(struct hdafg_softc *sc)
21058a9ff04bSjmcneill {
21068a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
21078a9ff04bSjmcneill 	struct hdaudio_widget *w, *cw;
21088a9ff04bSjmcneill 	struct hdaudio_control *ctl;
21098a9ff04bSjmcneill 	int i, j, k;
21108a9ff04bSjmcneill 
21118a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
21128a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
21138a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
21148a9ff04bSjmcneill 			continue;
2115fd157521Smanu 
2116fd157521Smanu 		/* Disable unassociated widgets */
2117fd157521Smanu 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX) {
21188a9ff04bSjmcneill 			if (w->w_bindas == -1) {
21198a9ff04bSjmcneill 				w->w_enable = 0;
21208a9ff04bSjmcneill 				hda_trace(sc, "disable %02X [unassociated]\n",
21218a9ff04bSjmcneill 				    w->w_nid);
21228a9ff04bSjmcneill 			}
2123fd157521Smanu 			continue;
21248a9ff04bSjmcneill 		}
21258a9ff04bSjmcneill 
2126fd157521Smanu 		/*
2127fd157521Smanu 		 * Disable input connections on input pin
2128fd157521Smanu 		 * and output on output pin
2129fd157521Smanu 		 */
21308a9ff04bSjmcneill 		if (w->w_bindas < 0)
21318a9ff04bSjmcneill 			continue;
21328a9ff04bSjmcneill 		if (as[w->w_bindas].as_dir == HDAUDIO_PINDIR_IN) {
21338a9ff04bSjmcneill 			hda_trace(sc, "disable %02X input connections\n",
21348a9ff04bSjmcneill 			    w->w_nid);
21358a9ff04bSjmcneill 			for (j = 0; j < w->w_nconns; j++)
21368a9ff04bSjmcneill 				w->w_connsenable[j] = false;
21378a9ff04bSjmcneill 			ctl = hdafg_control_lookup(sc, w->w_nid,
21388a9ff04bSjmcneill 			    HDAUDIO_PINDIR_IN, -1, 1);
21398a9ff04bSjmcneill 			if (ctl && ctl->ctl_enable == true) {
21408a9ff04bSjmcneill 				ctl->ctl_forcemute = 1;
21418a9ff04bSjmcneill 				ctl->ctl_muted = HDAUDIO_AMP_MUTE_ALL;
21428a9ff04bSjmcneill 				ctl->ctl_left = ctl->ctl_right = 0;
21438a9ff04bSjmcneill 				ctl->ctl_enable = false;
21448a9ff04bSjmcneill 			}
21458a9ff04bSjmcneill 		} else {
21468a9ff04bSjmcneill 			ctl = hdafg_control_lookup(sc, w->w_nid,
21478a9ff04bSjmcneill 			    HDAUDIO_PINDIR_OUT, -1, 1);
21488a9ff04bSjmcneill 			if (ctl && ctl->ctl_enable == true) {
21498a9ff04bSjmcneill 				ctl->ctl_forcemute = 1;
21508a9ff04bSjmcneill 				ctl->ctl_muted = HDAUDIO_AMP_MUTE_ALL;
21518a9ff04bSjmcneill 				ctl->ctl_left = ctl->ctl_right = 0;
21528a9ff04bSjmcneill 				ctl->ctl_enable = false;
21538a9ff04bSjmcneill 			}
21548a9ff04bSjmcneill 			for (k = sc->sc_startnode; k < sc->sc_endnode; k++) {
21558a9ff04bSjmcneill 				cw = hdafg_widget_lookup(sc, k);
21568a9ff04bSjmcneill 				if (cw == NULL || cw->w_enable == false)
21578a9ff04bSjmcneill 					continue;
21588a9ff04bSjmcneill 				for (j = 0; j < cw->w_nconns; j++) {
21598a9ff04bSjmcneill 					if (!cw->w_connsenable[j])
21608a9ff04bSjmcneill 						continue;
21618a9ff04bSjmcneill 					if (cw->w_conns[j] != i)
21628a9ff04bSjmcneill 						continue;
21638a9ff04bSjmcneill 					hda_trace(sc, "disable %02X -> %02X "
21648a9ff04bSjmcneill 					    "output connection\n",
21658a9ff04bSjmcneill 					    cw->w_nid, cw->w_conns[j]);
21668a9ff04bSjmcneill 					cw->w_connsenable[j] = false;
21678a9ff04bSjmcneill 					if (cw->w_type ==
21688a9ff04bSjmcneill 					    COP_AWCAP_TYPE_PIN_COMPLEX &&
21698a9ff04bSjmcneill 					    cw->w_nconns > 1)
21708a9ff04bSjmcneill 						continue;
21718a9ff04bSjmcneill 					ctl = hdafg_control_lookup(sc,
21728a9ff04bSjmcneill 					    k, HDAUDIO_PINDIR_IN, j, 1);
21738a9ff04bSjmcneill 					if (ctl && ctl->ctl_enable == true) {
21748a9ff04bSjmcneill 						ctl->ctl_forcemute = 1;
21758a9ff04bSjmcneill 						ctl->ctl_muted =
21768a9ff04bSjmcneill 						    HDAUDIO_AMP_MUTE_ALL;
21778a9ff04bSjmcneill 						ctl->ctl_left =
21788a9ff04bSjmcneill 						    ctl->ctl_right = 0;
21798a9ff04bSjmcneill 						ctl->ctl_enable = false;
21808a9ff04bSjmcneill 					}
21818a9ff04bSjmcneill 				}
21828a9ff04bSjmcneill 			}
21838a9ff04bSjmcneill 		}
21848a9ff04bSjmcneill 	}
21858a9ff04bSjmcneill }
21868a9ff04bSjmcneill 
21878a9ff04bSjmcneill static void
hdafg_disable_unsel(struct hdafg_softc * sc)21888a9ff04bSjmcneill hdafg_disable_unsel(struct hdafg_softc *sc)
21898a9ff04bSjmcneill {
21908a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
21918a9ff04bSjmcneill 	struct hdaudio_widget *w;
21928a9ff04bSjmcneill 	int i, j;
21938a9ff04bSjmcneill 
21948a9ff04bSjmcneill 	/* On playback path we can safely disable all unselected inputs */
21958a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
21968a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
21978a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
21988a9ff04bSjmcneill 			continue;
21998a9ff04bSjmcneill 		if (w->w_nconns <= 1)
22008a9ff04bSjmcneill 			continue;
22018a9ff04bSjmcneill 		if (w->w_type == COP_AWCAP_TYPE_AUDIO_MIXER)
22028a9ff04bSjmcneill 			continue;
22038a9ff04bSjmcneill 		if (w->w_bindas < 0 ||
22048a9ff04bSjmcneill 		    as[w->w_bindas].as_dir == HDAUDIO_PINDIR_IN)
22058a9ff04bSjmcneill 			continue;
22068a9ff04bSjmcneill 		for (j = 0; j < w->w_nconns; j++) {
22078a9ff04bSjmcneill 			if (w->w_connsenable[j] == false)
22088a9ff04bSjmcneill 				continue;
22098a9ff04bSjmcneill 			if (w->w_selconn < 0 || w->w_selconn == j)
22108a9ff04bSjmcneill 				continue;
22118a9ff04bSjmcneill 			hda_trace(sc, "disable %02X->%02X [unselected]\n",
22128a9ff04bSjmcneill 			    w->w_nid, w->w_conns[j]);
22138a9ff04bSjmcneill 			w->w_connsenable[j] = false;
22148a9ff04bSjmcneill 		}
22158a9ff04bSjmcneill 	}
22168a9ff04bSjmcneill }
22178a9ff04bSjmcneill 
22188a9ff04bSjmcneill static void
hdafg_disable_crossassoc(struct hdafg_softc * sc)22198a9ff04bSjmcneill hdafg_disable_crossassoc(struct hdafg_softc *sc)
22208a9ff04bSjmcneill {
22218a9ff04bSjmcneill 	struct hdaudio_widget *w, *cw;
22228a9ff04bSjmcneill 	struct hdaudio_control *ctl;
22238a9ff04bSjmcneill 	int i, j;
22248a9ff04bSjmcneill 
22258a9ff04bSjmcneill 	/* Disable cross associated and unwanted cross channel connections */
22268a9ff04bSjmcneill 
22278a9ff04bSjmcneill 	/* ... using selectors */
22288a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
22298a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
22308a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
22318a9ff04bSjmcneill 			continue;
22328a9ff04bSjmcneill 		if (w->w_nconns <= 1)
22338a9ff04bSjmcneill 			continue;
22348a9ff04bSjmcneill 		if (w->w_type == COP_AWCAP_TYPE_AUDIO_MIXER)
22358a9ff04bSjmcneill 			continue;
22368a9ff04bSjmcneill 		if (w->w_bindas == -2)
22378a9ff04bSjmcneill 			continue;
22388a9ff04bSjmcneill 		for (j = 0; j < w->w_nconns; j++) {
22398a9ff04bSjmcneill 			if (w->w_connsenable[j] == false)
22408a9ff04bSjmcneill 				continue;
22418a9ff04bSjmcneill 			cw = hdafg_widget_lookup(sc, w->w_conns[j]);
22428a9ff04bSjmcneill 			if (cw == NULL || cw->w_enable == false)
22438a9ff04bSjmcneill 				continue;
22448a9ff04bSjmcneill 			if (cw->w_bindas == -2)
22458a9ff04bSjmcneill 				continue;
22468a9ff04bSjmcneill 			if (w->w_bindas == cw->w_bindas &&
22478a9ff04bSjmcneill 			    (w->w_bindseqmask & cw->w_bindseqmask) != 0)
22488a9ff04bSjmcneill 				continue;
22498a9ff04bSjmcneill 			hda_trace(sc, "disable %02X->%02X [crossassoc]\n",
22508a9ff04bSjmcneill 			    w->w_nid, w->w_conns[j]);
22518a9ff04bSjmcneill 			w->w_connsenable[j] = false;
22528a9ff04bSjmcneill 		}
22538a9ff04bSjmcneill 	}
22548a9ff04bSjmcneill 	/* ... using controls */
22558a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
22568a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
22578a9ff04bSjmcneill 		if (ctl->ctl_enable == false || ctl->ctl_childwidget == NULL)
22588a9ff04bSjmcneill 			continue;
22598a9ff04bSjmcneill 		if (ctl->ctl_widget->w_bindas == -2 ||
22608a9ff04bSjmcneill 		    ctl->ctl_childwidget->w_bindas == -2)
22618a9ff04bSjmcneill 			continue;
22628a9ff04bSjmcneill 		if (ctl->ctl_widget->w_bindas !=
22638a9ff04bSjmcneill 		    ctl->ctl_childwidget->w_bindas ||
22648a9ff04bSjmcneill 		    (ctl->ctl_widget->w_bindseqmask &
22658a9ff04bSjmcneill 		    ctl->ctl_childwidget->w_bindseqmask) == 0) {
22668a9ff04bSjmcneill 			ctl->ctl_forcemute = 1;
22678a9ff04bSjmcneill 			ctl->ctl_muted = HDAUDIO_AMP_MUTE_ALL;
22688a9ff04bSjmcneill 			ctl->ctl_left = ctl->ctl_right = 0;
22698a9ff04bSjmcneill 			ctl->ctl_enable = false;
22708a9ff04bSjmcneill 			if (ctl->ctl_ndir == HDAUDIO_PINDIR_IN) {
22718a9ff04bSjmcneill 				hda_trace(sc, "disable ctl %d:%02X:%02X "
22728a9ff04bSjmcneill 				    "[crossassoc]\n",
22738a9ff04bSjmcneill 				    i, ctl->ctl_widget->w_nid,
22748a9ff04bSjmcneill 				    ctl->ctl_widget->w_conns[ctl->ctl_index]);
22758a9ff04bSjmcneill 				ctl->ctl_widget->w_connsenable[
22768a9ff04bSjmcneill 				    ctl->ctl_index] = false;
22778a9ff04bSjmcneill 			}
22788a9ff04bSjmcneill 		}
22798a9ff04bSjmcneill 	}
22808a9ff04bSjmcneill }
22818a9ff04bSjmcneill 
22828a9ff04bSjmcneill static struct hdaudio_control *
hdafg_control_amp_get(struct hdafg_softc * sc,int nid,enum hdaudio_pindir dir,int index,int cnt)22838a9ff04bSjmcneill hdafg_control_amp_get(struct hdafg_softc *sc, int nid,
22848a9ff04bSjmcneill     enum hdaudio_pindir dir, int index, int cnt)
22858a9ff04bSjmcneill {
22868a9ff04bSjmcneill 	struct hdaudio_control *ctl;
22878a9ff04bSjmcneill 	int i, found = 0;
22888a9ff04bSjmcneill 
22898a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
22908a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
22918a9ff04bSjmcneill 		if (ctl->ctl_enable == false)
22928a9ff04bSjmcneill 			continue;
22938a9ff04bSjmcneill 		if (ctl->ctl_widget->w_nid != nid)
22948a9ff04bSjmcneill 			continue;
22958a9ff04bSjmcneill 		if (dir && ctl->ctl_ndir != dir)
22968a9ff04bSjmcneill 			continue;
22978a9ff04bSjmcneill 		if (index >= 0 && ctl->ctl_ndir == HDAUDIO_PINDIR_IN &&
22988a9ff04bSjmcneill 		    ctl->ctl_dir == ctl->ctl_ndir &&
22998a9ff04bSjmcneill 		    ctl->ctl_index != index)
23008a9ff04bSjmcneill 			continue;
23018a9ff04bSjmcneill 		++found;
23028a9ff04bSjmcneill 		if (found == cnt || cnt <= 0)
23038a9ff04bSjmcneill 			return ctl;
23048a9ff04bSjmcneill 	}
23058a9ff04bSjmcneill 
23068a9ff04bSjmcneill 	return NULL;
23078a9ff04bSjmcneill }
23088a9ff04bSjmcneill 
23098a9ff04bSjmcneill static void
hdafg_control_amp_set1(struct hdaudio_control * ctl,int lmute,int rmute,int left,int right,int dir)23108a9ff04bSjmcneill hdafg_control_amp_set1(struct hdaudio_control *ctl, int lmute, int rmute,
23118a9ff04bSjmcneill     int left, int right, int dir)
23128a9ff04bSjmcneill {
23138a9ff04bSjmcneill 	struct hdafg_softc *sc = ctl->ctl_widget->w_afg;
23148a9ff04bSjmcneill 	int index = ctl->ctl_index;
23158a9ff04bSjmcneill 	uint16_t v = 0;
23168a9ff04bSjmcneill 
23178a9ff04bSjmcneill 	if (left != right || lmute != rmute) {
23188a9ff04bSjmcneill 		v = (1 << (15 - dir)) | (1 << 13) | (index << 8) |
23198a9ff04bSjmcneill 		    (lmute << 7) | left;
23208a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, ctl->ctl_widget->w_nid,
23218a9ff04bSjmcneill 		    CORB_SET_AMPLIFIER_GAIN_MUTE, v);
23228a9ff04bSjmcneill 		v = (1 << (15 - dir)) | (1 << 12) | (index << 8) |
23238a9ff04bSjmcneill 		    (rmute << 7) | right;
23248a9ff04bSjmcneill 	} else
23258a9ff04bSjmcneill 		v = (1 << (15 - dir)) | (3 << 12) | (index << 8) |
23268a9ff04bSjmcneill 		    (lmute << 7) | left;
23278a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, ctl->ctl_widget->w_nid,
23288a9ff04bSjmcneill 	    CORB_SET_AMPLIFIER_GAIN_MUTE, v);
23298a9ff04bSjmcneill }
23308a9ff04bSjmcneill 
23318a9ff04bSjmcneill static void
hdafg_control_amp_set(struct hdaudio_control * ctl,uint32_t mute,int left,int right)23328a9ff04bSjmcneill hdafg_control_amp_set(struct hdaudio_control *ctl, uint32_t mute,
23338a9ff04bSjmcneill     int left, int right)
23348a9ff04bSjmcneill {
23358a9ff04bSjmcneill 	int lmute, rmute;
23368a9ff04bSjmcneill 
23378a9ff04bSjmcneill 	/* Save new values if valid */
23388a9ff04bSjmcneill 	if (mute != HDAUDIO_AMP_MUTE_DEFAULT)
23398a9ff04bSjmcneill 		ctl->ctl_muted = mute;
23408a9ff04bSjmcneill 	if (left != HDAUDIO_AMP_VOL_DEFAULT)
23418a9ff04bSjmcneill 		ctl->ctl_left = left;
23428a9ff04bSjmcneill 	if (right != HDAUDIO_AMP_VOL_DEFAULT)
23438a9ff04bSjmcneill 		ctl->ctl_right = right;
23448a9ff04bSjmcneill 
23458a9ff04bSjmcneill 	/* Prepare effective values */
23468a9ff04bSjmcneill 	if (ctl->ctl_forcemute) {
23478a9ff04bSjmcneill 		lmute = rmute = 1;
23488a9ff04bSjmcneill 		left = right = 0;
23498a9ff04bSjmcneill 	} else {
23508a9ff04bSjmcneill 		lmute = HDAUDIO_AMP_LEFT_MUTED(ctl->ctl_muted);
23518a9ff04bSjmcneill 		rmute = HDAUDIO_AMP_RIGHT_MUTED(ctl->ctl_muted);
23528a9ff04bSjmcneill 		left = ctl->ctl_left;
23538a9ff04bSjmcneill 		right = ctl->ctl_right;
23548a9ff04bSjmcneill 	}
23558a9ff04bSjmcneill 
23568a9ff04bSjmcneill 	/* Apply effective values */
23578a9ff04bSjmcneill 	if (ctl->ctl_dir & HDAUDIO_PINDIR_OUT)
23588a9ff04bSjmcneill 		hdafg_control_amp_set1(ctl, lmute, rmute, left, right, 0);
23598a9ff04bSjmcneill 	if (ctl->ctl_dir & HDAUDIO_PINDIR_IN)
23608a9ff04bSjmcneill 		hdafg_control_amp_set1(ctl, lmute, rmute, left, right, 1);
23618a9ff04bSjmcneill }
23628a9ff04bSjmcneill 
2363a6e6c18eSchristos /*
2364a6e6c18eSchristos  * Muting the input pins directly does not work, we mute the mixers which
2365a6e6c18eSchristos  * are parents to them
2366a6e6c18eSchristos  */
236743e00902Schristos static bool
hdafg_mixer_child_is_input(const struct hdafg_softc * sc,const struct hdaudio_control * ctl)2368a6e6c18eSchristos hdafg_mixer_child_is_input(const struct hdafg_softc *sc,
2369a6e6c18eSchristos     const struct hdaudio_control *ctl)
237043e00902Schristos {
2371a6e6c18eSchristos 	const struct hdaudio_widget *w;
237243e00902Schristos 	const struct hdaudio_assoc *as = sc->sc_assocs;
237343e00902Schristos 
2374a6e6c18eSchristos 	switch (ctl->ctl_widget->w_type) {
237543e00902Schristos 	case COP_AWCAP_TYPE_AUDIO_INPUT:
237643e00902Schristos 		return true;
2377a6e6c18eSchristos 
2378a6e6c18eSchristos 	case COP_AWCAP_TYPE_AUDIO_MIXER:
2379a6e6c18eSchristos 		w = ctl->ctl_childwidget;
2380a6e6c18eSchristos 		if (w == NULL)
2381a6e6c18eSchristos 			return false;
2382a6e6c18eSchristos 
2383a6e6c18eSchristos 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
2384a6e6c18eSchristos 			return false;
2385a6e6c18eSchristos 
238643e00902Schristos 		if (as[w->w_bindas].as_dir == HDAUDIO_PINDIR_OUT)
238743e00902Schristos 			return false;
2388a6e6c18eSchristos 
238943e00902Schristos 		switch (COP_CFG_DEFAULT_DEVICE(w->w_pin.config)) {
239043e00902Schristos 		case COP_DEVICE_MIC_IN:
239143e00902Schristos 		case COP_DEVICE_LINE_IN:
239243e00902Schristos 		case COP_DEVICE_SPDIF_IN:
239343e00902Schristos 		case COP_DEVICE_DIGITAL_OTHER_IN:
239443e00902Schristos 			return true;
239543e00902Schristos 		default:
239643e00902Schristos 			return false;
239743e00902Schristos 		}
2398a6e6c18eSchristos 
239943e00902Schristos 	default:
240043e00902Schristos 		return false;
240143e00902Schristos 	}
240243e00902Schristos }
240343e00902Schristos 
24048a9ff04bSjmcneill static void
hdafg_control_commit(struct hdafg_softc * sc)24058a9ff04bSjmcneill hdafg_control_commit(struct hdafg_softc *sc)
24068a9ff04bSjmcneill {
24078a9ff04bSjmcneill 	struct hdaudio_control *ctl;
24088a9ff04bSjmcneill 	int i, z;
24098a9ff04bSjmcneill 
24108a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
24118a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
24128a9ff04bSjmcneill 		//if (ctl->ctl_enable == false || ctl->ctl_audiomask != 0)
24138a9ff04bSjmcneill 		if (ctl->ctl_enable == false)
24148a9ff04bSjmcneill 			continue;
24158a9ff04bSjmcneill 		/* Init fixed controls to 0dB amplification */
24168a9ff04bSjmcneill 		z = ctl->ctl_offset;
24178a9ff04bSjmcneill 		if (z > ctl->ctl_step)
24188a9ff04bSjmcneill 			z = ctl->ctl_step;
241943e00902Schristos 
2420a6e6c18eSchristos 		if (hdafg_mixer_child_is_input(sc, ctl))
2421c7d2d3eaSchristos 			hdafg_control_amp_set(ctl, HDAUDIO_AMP_MUTE_ALL, z, z);
2422c7d2d3eaSchristos 		else
24238a9ff04bSjmcneill 			hdafg_control_amp_set(ctl, HDAUDIO_AMP_MUTE_NONE, z, z);
24248a9ff04bSjmcneill 	}
24258a9ff04bSjmcneill }
24268a9ff04bSjmcneill 
24278a9ff04bSjmcneill static void
hdafg_widget_connection_select(struct hdaudio_widget * w,uint8_t index)24288a9ff04bSjmcneill hdafg_widget_connection_select(struct hdaudio_widget *w, uint8_t index)
24298a9ff04bSjmcneill {
24308a9ff04bSjmcneill 	struct hdafg_softc *sc = w->w_afg;
24318a9ff04bSjmcneill 
24328a9ff04bSjmcneill 	if (w->w_nconns < 1 || index > (w->w_nconns - 1))
24338a9ff04bSjmcneill 		return;
24348a9ff04bSjmcneill 
24358a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
24368a9ff04bSjmcneill 	    CORB_SET_CONNECTION_SELECT_CONTROL, index);
24378a9ff04bSjmcneill 	w->w_selconn = index;
24388a9ff04bSjmcneill }
24398a9ff04bSjmcneill 
24408a9ff04bSjmcneill static void
hdafg_assign_names(struct hdafg_softc * sc)24418a9ff04bSjmcneill hdafg_assign_names(struct hdafg_softc *sc)
24428a9ff04bSjmcneill {
24438a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
24448a9ff04bSjmcneill 	struct hdaudio_widget *w;
24458a9ff04bSjmcneill 	int i, j;
24468a9ff04bSjmcneill 	int type = -1, use, used =0;
24478a9ff04bSjmcneill 	static const int types[7][13] = {
24488a9ff04bSjmcneill 	    { HDAUDIO_MIXER_LINE, HDAUDIO_MIXER_LINE1, HDAUDIO_MIXER_LINE2,
24498a9ff04bSjmcneill 	      HDAUDIO_MIXER_LINE3, -1 },
24508a9ff04bSjmcneill 	    { HDAUDIO_MIXER_MONITOR, HDAUDIO_MIXER_MIC, -1 }, /* int mic */
24518a9ff04bSjmcneill 	    { HDAUDIO_MIXER_MIC, HDAUDIO_MIXER_MONITOR, -1 }, /* ext mic */
24528a9ff04bSjmcneill 	    { HDAUDIO_MIXER_CD, -1 },
24538a9ff04bSjmcneill 	    { HDAUDIO_MIXER_SPEAKER, -1 },
24548a9ff04bSjmcneill 	    { HDAUDIO_MIXER_DIGITAL1, HDAUDIO_MIXER_DIGITAL2,
24558a9ff04bSjmcneill 	      HDAUDIO_MIXER_DIGITAL3, -1 },
24568a9ff04bSjmcneill 	    { HDAUDIO_MIXER_LINE, HDAUDIO_MIXER_LINE1, HDAUDIO_MIXER_LINE2,
24578a9ff04bSjmcneill 	      HDAUDIO_MIXER_LINE3, HDAUDIO_MIXER_PHONEIN,
24588a9ff04bSjmcneill 	      HDAUDIO_MIXER_PHONEOUT, HDAUDIO_MIXER_VIDEO, HDAUDIO_MIXER_RADIO,
24598a9ff04bSjmcneill 	      HDAUDIO_MIXER_DIGITAL1, HDAUDIO_MIXER_DIGITAL2,
24608a9ff04bSjmcneill 	      HDAUDIO_MIXER_DIGITAL3, HDAUDIO_MIXER_MONITOR, -1 } /* others */
24618a9ff04bSjmcneill 	};
24628a9ff04bSjmcneill 
24638a9ff04bSjmcneill 	/* Surely known names */
24648a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
24658a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
24668a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
24678a9ff04bSjmcneill 			continue;
24688a9ff04bSjmcneill 		if (w->w_bindas == -1)
24698a9ff04bSjmcneill 			continue;
24708a9ff04bSjmcneill 		use = -1;
24718a9ff04bSjmcneill 		switch (w->w_type) {
24728a9ff04bSjmcneill 		case COP_AWCAP_TYPE_PIN_COMPLEX:
24738a9ff04bSjmcneill 			if (as[w->w_bindas].as_dir == HDAUDIO_PINDIR_OUT)
24748a9ff04bSjmcneill 				break;
24758a9ff04bSjmcneill 			type = -1;
24768a9ff04bSjmcneill 			switch (COP_CFG_DEFAULT_DEVICE(w->w_pin.config)) {
24778a9ff04bSjmcneill 			case COP_DEVICE_LINE_IN:
24788a9ff04bSjmcneill 				type = 0;
24798a9ff04bSjmcneill 				break;
24808a9ff04bSjmcneill 			case COP_DEVICE_MIC_IN:
24818a9ff04bSjmcneill 				if (COP_CFG_PORT_CONNECTIVITY(w->w_pin.config)
24828a9ff04bSjmcneill 				    == COP_PORT_JACK)
24838a9ff04bSjmcneill 					break;
24848a9ff04bSjmcneill 				type = 1;
24858a9ff04bSjmcneill 				break;
24868a9ff04bSjmcneill 			case COP_DEVICE_CD:
24878a9ff04bSjmcneill 				type = 3;
24888a9ff04bSjmcneill 				break;
24898a9ff04bSjmcneill 			case COP_DEVICE_SPEAKER:
24908a9ff04bSjmcneill 				type = 4;
24918a9ff04bSjmcneill 				break;
24928a9ff04bSjmcneill 			case COP_DEVICE_SPDIF_IN:
24938a9ff04bSjmcneill 			case COP_DEVICE_DIGITAL_OTHER_IN:
24948a9ff04bSjmcneill 				type = 5;
24958a9ff04bSjmcneill 				break;
24968a9ff04bSjmcneill 			}
24978a9ff04bSjmcneill 			if (type == -1)
24988a9ff04bSjmcneill 				break;
24998a9ff04bSjmcneill 			j = 0;
25008a9ff04bSjmcneill 			while (types[type][j] >= 0 &&
25018a9ff04bSjmcneill 			    (used & (1 << types[type][j])) != 0) {
25028a9ff04bSjmcneill 				j++;
25038a9ff04bSjmcneill 			}
25048a9ff04bSjmcneill 			if (types[type][j] >= 0)
25058a9ff04bSjmcneill 				use = types[type][j];
25068a9ff04bSjmcneill 			break;
25078a9ff04bSjmcneill 		case COP_AWCAP_TYPE_AUDIO_OUTPUT:
25088a9ff04bSjmcneill 			use = HDAUDIO_MIXER_PCM;
25098a9ff04bSjmcneill 			break;
25108a9ff04bSjmcneill 		case COP_AWCAP_TYPE_BEEP_GENERATOR:
25118a9ff04bSjmcneill 			use = HDAUDIO_MIXER_SPEAKER;
25128a9ff04bSjmcneill 			break;
25138a9ff04bSjmcneill 		default:
25148a9ff04bSjmcneill 			break;
25158a9ff04bSjmcneill 		}
25168a9ff04bSjmcneill 		if (use >= 0) {
25178a9ff04bSjmcneill 			w->w_audiodev = use;
25188a9ff04bSjmcneill 			used |= (1 << use);
25198a9ff04bSjmcneill 		}
25208a9ff04bSjmcneill 	}
25218a9ff04bSjmcneill 	/* Semi-known names */
25228a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
25238a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
25248a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
25258a9ff04bSjmcneill 			continue;
25268a9ff04bSjmcneill 		if (w->w_audiodev >= 0)
25278a9ff04bSjmcneill 			continue;
25288a9ff04bSjmcneill 		if (w->w_bindas == -1)
25298a9ff04bSjmcneill 			continue;
25308a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
25318a9ff04bSjmcneill 			continue;
25328a9ff04bSjmcneill 		if (as[w->w_bindas].as_dir == HDAUDIO_PINDIR_OUT)
25338a9ff04bSjmcneill 			continue;
25348a9ff04bSjmcneill 		type = -1;
25358a9ff04bSjmcneill 		switch (COP_CFG_DEFAULT_DEVICE(w->w_pin.config)) {
25368a9ff04bSjmcneill 		case COP_DEVICE_LINE_OUT:
25378a9ff04bSjmcneill 		case COP_DEVICE_SPEAKER:
25388a9ff04bSjmcneill 		case COP_DEVICE_HP_OUT:
25398a9ff04bSjmcneill 		case COP_DEVICE_AUX:
25408a9ff04bSjmcneill 			type = 0;
25418a9ff04bSjmcneill 			break;
25428a9ff04bSjmcneill 		case COP_DEVICE_MIC_IN:
25438a9ff04bSjmcneill 			type = 2;
25448a9ff04bSjmcneill 			break;
25458a9ff04bSjmcneill 		case COP_DEVICE_SPDIF_OUT:
25468a9ff04bSjmcneill 		case COP_DEVICE_DIGITAL_OTHER_OUT:
25478a9ff04bSjmcneill 			type = 5;
25488a9ff04bSjmcneill 			break;
25498a9ff04bSjmcneill 		}
25508a9ff04bSjmcneill 		if (type == -1)
25518a9ff04bSjmcneill 			break;
25528a9ff04bSjmcneill 		j = 0;
25538a9ff04bSjmcneill 		while (types[type][j] >= 0 &&
25548a9ff04bSjmcneill 		    (used & (1 << types[type][j])) != 0) {
25558a9ff04bSjmcneill 			j++;
25568a9ff04bSjmcneill 		}
25578a9ff04bSjmcneill 		if (types[type][j] >= 0) {
25588a9ff04bSjmcneill 			w->w_audiodev = types[type][j];
25598a9ff04bSjmcneill 			used |= (1 << types[type][j]);
25608a9ff04bSjmcneill 		}
25618a9ff04bSjmcneill 	}
25628a9ff04bSjmcneill 	/* Others */
25638a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
25648a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
25658a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
25668a9ff04bSjmcneill 			continue;
25678a9ff04bSjmcneill 		if (w->w_audiodev >= 0)
25688a9ff04bSjmcneill 			continue;
25698a9ff04bSjmcneill 		if (w->w_bindas == -1)
25708a9ff04bSjmcneill 			continue;
25718a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
25728a9ff04bSjmcneill 			continue;
25738a9ff04bSjmcneill 		if (as[w->w_bindas].as_dir == HDAUDIO_PINDIR_OUT)
25748a9ff04bSjmcneill 			continue;
25758a9ff04bSjmcneill 		j = 0;
25768a9ff04bSjmcneill 		while (types[6][j] >= 0 &&
25778a9ff04bSjmcneill 		    (used & (1 << types[6][j])) != 0) {
25788a9ff04bSjmcneill 			j++;
25798a9ff04bSjmcneill 		}
25808a9ff04bSjmcneill 		if (types[6][j] >= 0) {
25818a9ff04bSjmcneill 			w->w_audiodev = types[6][j];
25828a9ff04bSjmcneill 			used |= (1 << types[6][j]);
25838a9ff04bSjmcneill 		}
25848a9ff04bSjmcneill 	}
25858a9ff04bSjmcneill }
25868a9ff04bSjmcneill 
25878a9ff04bSjmcneill static int
hdafg_control_source_amp(struct hdafg_softc * sc,int nid,int index,int audiodev,int ctlable,int depth,int need)25888a9ff04bSjmcneill hdafg_control_source_amp(struct hdafg_softc *sc, int nid, int index,
25898a9ff04bSjmcneill     int audiodev, int ctlable, int depth, int need)
25908a9ff04bSjmcneill {
25918a9ff04bSjmcneill 	struct hdaudio_widget *w, *wc;
25928a9ff04bSjmcneill 	struct hdaudio_control *ctl;
25938a9ff04bSjmcneill 	int i, j, conns = 0, rneed;
25948a9ff04bSjmcneill 
25958a9ff04bSjmcneill 	if (depth >= HDAUDIO_PARSE_MAXDEPTH)
25968a9ff04bSjmcneill 		return need;
25978a9ff04bSjmcneill 
25988a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
25998a9ff04bSjmcneill 	if (w == NULL || w->w_enable == false)
26008a9ff04bSjmcneill 		return need;
26018a9ff04bSjmcneill 
26028a9ff04bSjmcneill 	/* Count number of active inputs */
26038a9ff04bSjmcneill 	if (depth > 0) {
26048a9ff04bSjmcneill 		for (j = 0; j < w->w_nconns; j++) {
26058a9ff04bSjmcneill 			if (w->w_connsenable[j])
26068a9ff04bSjmcneill 				++conns;
26078a9ff04bSjmcneill 		}
26088a9ff04bSjmcneill 	}
26098a9ff04bSjmcneill 
26108a9ff04bSjmcneill 	/*
26118a9ff04bSjmcneill 	 * If this is not a first step, use input mixer. Pins have common
26128a9ff04bSjmcneill 	 * input ctl so care must be taken
26138a9ff04bSjmcneill 	 */
26148a9ff04bSjmcneill 	if (depth > 0 && ctlable && (conns == 1 ||
26158a9ff04bSjmcneill 	    w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)) {
26168a9ff04bSjmcneill 		ctl = hdafg_control_amp_get(sc, w->w_nid,
26178a9ff04bSjmcneill 		    HDAUDIO_PINDIR_IN, index, 1);
26188a9ff04bSjmcneill 		if (ctl) {
26198a9ff04bSjmcneill 			if (HDAUDIO_CONTROL_GIVE(ctl) & need)
26208a9ff04bSjmcneill 				ctl->ctl_audiomask |= (1 << audiodev);
26218a9ff04bSjmcneill 			else
26228a9ff04bSjmcneill 				ctl->ctl_paudiomask |= (1 << audiodev);
26238a9ff04bSjmcneill 			need &= ~HDAUDIO_CONTROL_GIVE(ctl);
26248a9ff04bSjmcneill 		}
26258a9ff04bSjmcneill 	}
26268a9ff04bSjmcneill 
26278a9ff04bSjmcneill 	/* If widget has own audiodev, don't traverse it. */
26288a9ff04bSjmcneill 	if (w->w_audiodev >= 0 && depth > 0)
26298a9ff04bSjmcneill 		return need;
26308a9ff04bSjmcneill 
26318a9ff04bSjmcneill 	/* We must not traverse pins */
26328a9ff04bSjmcneill 	if ((w->w_type == COP_AWCAP_TYPE_AUDIO_INPUT ||
26338a9ff04bSjmcneill 	    w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX) && depth > 0)
26348a9ff04bSjmcneill 		return need;
26358a9ff04bSjmcneill 
26368a9ff04bSjmcneill 	/* Record that this widget exports such signal */
26378a9ff04bSjmcneill 	w->w_audiomask |= (1 << audiodev);
26388a9ff04bSjmcneill 
26398a9ff04bSjmcneill 	/*
26408a9ff04bSjmcneill 	 * If signals mixed, we can't assign controls further. Ignore this
26418a9ff04bSjmcneill 	 * on depth zero. Caller must know why. Ignore this for static
26428a9ff04bSjmcneill 	 * selectors if this input is selected.
26438a9ff04bSjmcneill 	 */
26448a9ff04bSjmcneill 	if (conns > 1)
26458a9ff04bSjmcneill 		ctlable = 0;
26468a9ff04bSjmcneill 
26478a9ff04bSjmcneill 	if (ctlable) {
26488a9ff04bSjmcneill 		ctl = hdafg_control_amp_get(sc, w->w_nid,
26498a9ff04bSjmcneill 		    HDAUDIO_PINDIR_OUT, -1, 1);
26508a9ff04bSjmcneill 		if (ctl) {
26518a9ff04bSjmcneill 			if (HDAUDIO_CONTROL_GIVE(ctl) & need)
26528a9ff04bSjmcneill 				ctl->ctl_audiomask |= (1 << audiodev);
26538a9ff04bSjmcneill 			else
26548a9ff04bSjmcneill 				ctl->ctl_paudiomask |= (1 << audiodev);
26558a9ff04bSjmcneill 			need &= ~HDAUDIO_CONTROL_GIVE(ctl);
26568a9ff04bSjmcneill 		}
26578a9ff04bSjmcneill 	}
26588a9ff04bSjmcneill 
26598a9ff04bSjmcneill 	rneed = 0;
26608a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
26618a9ff04bSjmcneill 		wc = hdafg_widget_lookup(sc, i);
26628a9ff04bSjmcneill 		if (wc == NULL || wc->w_enable == false)
26638a9ff04bSjmcneill 			continue;
26648a9ff04bSjmcneill 		for (j = 0; j < wc->w_nconns; j++) {
26658a9ff04bSjmcneill 			if (wc->w_connsenable[j] && wc->w_conns[j] == nid) {
26668a9ff04bSjmcneill 				rneed |= hdafg_control_source_amp(sc,
26678a9ff04bSjmcneill 				    wc->w_nid, j, audiodev, ctlable, depth + 1,
26688a9ff04bSjmcneill 				    need);
26698a9ff04bSjmcneill 			}
26708a9ff04bSjmcneill 		}
26718a9ff04bSjmcneill 	}
26728a9ff04bSjmcneill 	rneed &= need;
26738a9ff04bSjmcneill 
26748a9ff04bSjmcneill 	return rneed;
26758a9ff04bSjmcneill }
26768a9ff04bSjmcneill 
26778a9ff04bSjmcneill static void
hdafg_control_dest_amp(struct hdafg_softc * sc,int nid,int audiodev,int depth,int need)26788a9ff04bSjmcneill hdafg_control_dest_amp(struct hdafg_softc *sc, int nid,
26798a9ff04bSjmcneill     int audiodev, int depth, int need)
26808a9ff04bSjmcneill {
26818a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
26828a9ff04bSjmcneill 	struct hdaudio_widget *w, *wc;
26838a9ff04bSjmcneill 	struct hdaudio_control *ctl;
26848a9ff04bSjmcneill 	int i, j, consumers;
26858a9ff04bSjmcneill 
26868a9ff04bSjmcneill 	if (depth > HDAUDIO_PARSE_MAXDEPTH)
26878a9ff04bSjmcneill 		return;
26888a9ff04bSjmcneill 
26898a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
26908a9ff04bSjmcneill 	if (w == NULL || w->w_enable == false)
26918a9ff04bSjmcneill 		return;
26928a9ff04bSjmcneill 
26938a9ff04bSjmcneill 	if (depth > 0) {
26948a9ff04bSjmcneill 		/*
26958a9ff04bSjmcneill 		 * If this node produces output for several consumers,
26968a9ff04bSjmcneill 		 * we can't touch it
26978a9ff04bSjmcneill 		 */
26988a9ff04bSjmcneill 		consumers = 0;
26998a9ff04bSjmcneill 		for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
27008a9ff04bSjmcneill 			wc = hdafg_widget_lookup(sc, i);
27018a9ff04bSjmcneill 			if (wc == NULL || wc->w_enable == false)
27028a9ff04bSjmcneill 				continue;
27038a9ff04bSjmcneill 			for (j = 0; j < wc->w_nconns; j++) {
27048a9ff04bSjmcneill 				if (wc->w_connsenable[j] &&
27058a9ff04bSjmcneill 				    wc->w_conns[j] == nid)
27068a9ff04bSjmcneill 					++consumers;
27078a9ff04bSjmcneill 			}
27088a9ff04bSjmcneill 		}
27098a9ff04bSjmcneill 		/*
27108a9ff04bSjmcneill 		 * The only exception is if real HP redirection is configured
27118a9ff04bSjmcneill 		 * and this is a duplication point.
27128a9ff04bSjmcneill 		 * XXX: Not completely correct.
27138a9ff04bSjmcneill 		 */
27148a9ff04bSjmcneill 		if ((consumers == 2 && (w->w_bindas < 0 ||
27158a9ff04bSjmcneill 		    as[w->w_bindas].as_hpredir < 0 ||
27168a9ff04bSjmcneill 		    as[w->w_bindas].as_fakeredir ||
27178a9ff04bSjmcneill 		    (w->w_bindseqmask & (1 << 15)) == 0)) ||
27188a9ff04bSjmcneill 		    consumers > 2)
27198a9ff04bSjmcneill 			return;
27208a9ff04bSjmcneill 
27218a9ff04bSjmcneill 		/* Else use its output mixer */
27228a9ff04bSjmcneill 		ctl = hdafg_control_amp_get(sc, w->w_nid,
27238a9ff04bSjmcneill 		    HDAUDIO_PINDIR_OUT, -1, 1);
27248a9ff04bSjmcneill 		if (ctl) {
27258a9ff04bSjmcneill 			if (HDAUDIO_CONTROL_GIVE(ctl) & need)
27268a9ff04bSjmcneill 				ctl->ctl_audiomask |= (1 << audiodev);
27278a9ff04bSjmcneill 			else
27288a9ff04bSjmcneill 				ctl->ctl_paudiomask |= (1 << audiodev);
27298a9ff04bSjmcneill 			need &= ~HDAUDIO_CONTROL_GIVE(ctl);
27308a9ff04bSjmcneill 		}
27318a9ff04bSjmcneill 	}
27328a9ff04bSjmcneill 
27338a9ff04bSjmcneill 	/* We must not traverse pin */
27348a9ff04bSjmcneill 	if (w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX && depth > 0)
27358a9ff04bSjmcneill 		return;
27368a9ff04bSjmcneill 
27378a9ff04bSjmcneill 	for (i = 0; i < w->w_nconns; i++) {
27388a9ff04bSjmcneill 		int tneed = need;
27398a9ff04bSjmcneill 		if (w->w_connsenable[i] == false)
27408a9ff04bSjmcneill 			continue;
27418a9ff04bSjmcneill 		ctl = hdafg_control_amp_get(sc, w->w_nid,
27428a9ff04bSjmcneill 		    HDAUDIO_PINDIR_IN, i, 1);
27438a9ff04bSjmcneill 		if (ctl) {
27448a9ff04bSjmcneill 			if (HDAUDIO_CONTROL_GIVE(ctl) & tneed)
27458a9ff04bSjmcneill 				ctl->ctl_audiomask |= (1 << audiodev);
27468a9ff04bSjmcneill 			else
27478a9ff04bSjmcneill 				ctl->ctl_paudiomask |= (1 << audiodev);
27488a9ff04bSjmcneill 			tneed &= ~HDAUDIO_CONTROL_GIVE(ctl);
27498a9ff04bSjmcneill 		}
27508a9ff04bSjmcneill 		hdafg_control_dest_amp(sc, w->w_conns[i], audiodev,
27518a9ff04bSjmcneill 		    depth + 1, tneed);
27528a9ff04bSjmcneill 	}
27538a9ff04bSjmcneill }
27548a9ff04bSjmcneill 
27558a9ff04bSjmcneill static void
hdafg_assign_mixers(struct hdafg_softc * sc)27568a9ff04bSjmcneill hdafg_assign_mixers(struct hdafg_softc *sc)
27578a9ff04bSjmcneill {
27588a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
27598a9ff04bSjmcneill 	struct hdaudio_control *ctl;
27608a9ff04bSjmcneill 	struct hdaudio_widget *w;
27618a9ff04bSjmcneill 	int i;
27628a9ff04bSjmcneill 
27638a9ff04bSjmcneill 	/* Assign mixers to the tree */
27648a9ff04bSjmcneill 	for (i = sc->sc_startnode; i < sc->sc_endnode; i++) {
27658a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, i);
27668a9ff04bSjmcneill 		if (w == NULL || w->w_enable == FALSE)
27678a9ff04bSjmcneill 			continue;
27688a9ff04bSjmcneill 		if (w->w_type == COP_AWCAP_TYPE_AUDIO_OUTPUT ||
27698a9ff04bSjmcneill 		    w->w_type == COP_AWCAP_TYPE_BEEP_GENERATOR ||
27708a9ff04bSjmcneill 		    (w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX &&
27718a9ff04bSjmcneill 		    as[w->w_bindas].as_dir == HDAUDIO_PINDIR_IN)) {
27728a9ff04bSjmcneill 			if (w->w_audiodev < 0)
27738a9ff04bSjmcneill 				continue;
27748a9ff04bSjmcneill 			hdafg_control_source_amp(sc, w->w_nid, -1,
27758a9ff04bSjmcneill 			    w->w_audiodev, 1, 0, 1);
27768a9ff04bSjmcneill 		} else if (w->w_pflags & HDAUDIO_ADC_MONITOR) {
27778a9ff04bSjmcneill 			if (w->w_audiodev < 0)
27788a9ff04bSjmcneill 				continue;
27798a9ff04bSjmcneill 			if (hdafg_control_source_amp(sc, w->w_nid, -1,
27808a9ff04bSjmcneill 			    w->w_audiodev, 1, 0, 1)) {
27818a9ff04bSjmcneill 				/* If we are unable to control input monitor
27828a9ff04bSjmcneill 				   as source, try to control it as dest */
27838a9ff04bSjmcneill 				hdafg_control_dest_amp(sc, w->w_nid,
27848a9ff04bSjmcneill 				    w->w_audiodev, 0, 1);
27858a9ff04bSjmcneill 			}
27868a9ff04bSjmcneill 		} else if (w->w_type == COP_AWCAP_TYPE_AUDIO_INPUT) {
27878a9ff04bSjmcneill 			hdafg_control_dest_amp(sc, w->w_nid,
27888a9ff04bSjmcneill 			    HDAUDIO_MIXER_RECLEV, 0, 1);
27898a9ff04bSjmcneill 		} else if (w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX &&
27908a9ff04bSjmcneill 		    as[w->w_bindas].as_dir == HDAUDIO_PINDIR_OUT) {
27918a9ff04bSjmcneill 			hdafg_control_dest_amp(sc, w->w_nid,
27928a9ff04bSjmcneill 			    HDAUDIO_MIXER_VOLUME, 0, 1);
27938a9ff04bSjmcneill 		}
27948a9ff04bSjmcneill 	}
27958a9ff04bSjmcneill 	/* Treat unrequired as possible */
27968a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
27978a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
27988a9ff04bSjmcneill 		if (ctl->ctl_audiomask == 0)
27998a9ff04bSjmcneill 			ctl->ctl_audiomask = ctl->ctl_paudiomask;
28008a9ff04bSjmcneill 	}
28018a9ff04bSjmcneill }
28028a9ff04bSjmcneill 
28038a9ff04bSjmcneill static void
hdafg_build_mixers(struct hdafg_softc * sc)28048a9ff04bSjmcneill hdafg_build_mixers(struct hdafg_softc *sc)
28058a9ff04bSjmcneill {
28068a9ff04bSjmcneill 	struct hdaudio_mixer *mx;
28078a9ff04bSjmcneill 	struct hdaudio_control *ctl, *masterctl = NULL;
28088a9ff04bSjmcneill 	uint32_t audiomask = 0;
28098a9ff04bSjmcneill 	int nmixers = 0;
28108a9ff04bSjmcneill 	int i, j, index = 0;
28118a9ff04bSjmcneill 	int ndac, nadc;
28128a9ff04bSjmcneill 	int ctrlcnt[HDAUDIO_MIXER_NRDEVICES];
28138a9ff04bSjmcneill 
28148a9ff04bSjmcneill 	memset(ctrlcnt, 0, sizeof(ctrlcnt));
28158a9ff04bSjmcneill 
28168a9ff04bSjmcneill 	/* Count the number of required mixers */
28178a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
28188a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
28198a9ff04bSjmcneill 		if (ctl->ctl_enable == false ||
28208a9ff04bSjmcneill 		    ctl->ctl_audiomask == 0)
28218a9ff04bSjmcneill 			continue;
28228a9ff04bSjmcneill 		audiomask |= ctl->ctl_audiomask;
28238a9ff04bSjmcneill 		++nmixers;
28248a9ff04bSjmcneill 		if (ctl->ctl_mute)
28258a9ff04bSjmcneill 			++nmixers;
28268a9ff04bSjmcneill 	}
28278a9ff04bSjmcneill 
28288a9ff04bSjmcneill 	/* XXXJDM TODO: softvol */
28298a9ff04bSjmcneill 	/* Declare master volume if needed */
28308a9ff04bSjmcneill 	if ((audiomask & (HDAUDIO_MASK(VOLUME) | HDAUDIO_MASK(PCM))) ==
28318a9ff04bSjmcneill 	    HDAUDIO_MASK(PCM)) {
28328a9ff04bSjmcneill 		audiomask |= HDAUDIO_MASK(VOLUME);
28338a9ff04bSjmcneill 		for (i = 0; i < sc->sc_nctls; i++) {
28348a9ff04bSjmcneill 			if (sc->sc_ctls[i].ctl_audiomask == HDAUDIO_MASK(PCM)) {
28358a9ff04bSjmcneill 				masterctl = &sc->sc_ctls[i];
28368a9ff04bSjmcneill 				++nmixers;
28378a9ff04bSjmcneill 				if (masterctl->ctl_mute)
28388a9ff04bSjmcneill 					++nmixers;
28398a9ff04bSjmcneill 				break;
28408a9ff04bSjmcneill 			}
28418a9ff04bSjmcneill 		}
28428a9ff04bSjmcneill 	}
28438a9ff04bSjmcneill 
28448a9ff04bSjmcneill 	/* Make room for mixer classes */
28458a9ff04bSjmcneill 	nmixers += (HDAUDIO_MIXER_CLASS_LAST + 1);
28468a9ff04bSjmcneill 
28478a9ff04bSjmcneill 	/* count DACs and ADCs for selectors */
28488a9ff04bSjmcneill 	ndac = nadc = 0;
28498a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
28508a9ff04bSjmcneill 		if (sc->sc_assocs[i].as_enable == false)
28518a9ff04bSjmcneill 			continue;
28528a9ff04bSjmcneill 		if (sc->sc_assocs[i].as_dir == HDAUDIO_PINDIR_OUT)
28538a9ff04bSjmcneill 			++ndac;
28548a9ff04bSjmcneill 		else if (sc->sc_assocs[i].as_dir == HDAUDIO_PINDIR_IN)
28558a9ff04bSjmcneill 			++nadc;
28568a9ff04bSjmcneill 	}
28578a9ff04bSjmcneill 
28588a9ff04bSjmcneill 	/* Make room for selectors */
28598a9ff04bSjmcneill 	if (ndac > 0)
28608a9ff04bSjmcneill 		++nmixers;
28618a9ff04bSjmcneill 	if (nadc > 0)
28628a9ff04bSjmcneill 		++nmixers;
28638a9ff04bSjmcneill 
28648a9ff04bSjmcneill 	hda_trace(sc, "  need %d mixers (3 classes%s)\n",
28658a9ff04bSjmcneill 	    nmixers, masterctl ? " + fake master" : "");
28668a9ff04bSjmcneill 
28678a9ff04bSjmcneill 	/* Allocate memory for the mixers */
28688a9ff04bSjmcneill 	mx = kmem_zalloc(nmixers * sizeof(*mx), KM_SLEEP);
28698a9ff04bSjmcneill 	sc->sc_nmixers = nmixers;
28708a9ff04bSjmcneill 
28718a9ff04bSjmcneill 	/* Build class mixers */
28728a9ff04bSjmcneill 	for (i = 0; i <= HDAUDIO_MIXER_CLASS_LAST; i++) {
28738a9ff04bSjmcneill 		mx[index].mx_ctl = NULL;
28748a9ff04bSjmcneill 		mx[index].mx_di.index = index;
28758a9ff04bSjmcneill 		mx[index].mx_di.type = AUDIO_MIXER_CLASS;
28768a9ff04bSjmcneill 		mx[index].mx_di.mixer_class = i;
28778a9ff04bSjmcneill 		mx[index].mx_di.next = mx[index].mx_di.prev = AUDIO_MIXER_LAST;
28788a9ff04bSjmcneill 		switch (i) {
28798a9ff04bSjmcneill 		case HDAUDIO_MIXER_CLASS_OUTPUTS:
28808a9ff04bSjmcneill 			strcpy(mx[index].mx_di.label.name, AudioCoutputs);
28818a9ff04bSjmcneill 			break;
28828a9ff04bSjmcneill 		case HDAUDIO_MIXER_CLASS_INPUTS:
28838a9ff04bSjmcneill 			strcpy(mx[index].mx_di.label.name, AudioCinputs);
28848a9ff04bSjmcneill 			break;
28858a9ff04bSjmcneill 		case HDAUDIO_MIXER_CLASS_RECORD:
28868a9ff04bSjmcneill 			strcpy(mx[index].mx_di.label.name, AudioCrecord);
28878a9ff04bSjmcneill 			break;
28888a9ff04bSjmcneill 		}
28898a9ff04bSjmcneill 		++index;
28908a9ff04bSjmcneill 	}
28918a9ff04bSjmcneill 
28928a9ff04bSjmcneill 	/* Shadow master control */
28938a9ff04bSjmcneill 	if (masterctl != NULL) {
28948a9ff04bSjmcneill 		mx[index].mx_ctl = masterctl;
28958a9ff04bSjmcneill 		mx[index].mx_di.index = index;
28968a9ff04bSjmcneill 		mx[index].mx_di.type = AUDIO_MIXER_VALUE;
28978a9ff04bSjmcneill 		mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
28988a9ff04bSjmcneill 		mx[index].mx_di.un.v.num_channels = 2;	/* XXX */
28998a9ff04bSjmcneill 		mx[index].mx_di.mixer_class = HDAUDIO_MIXER_CLASS_OUTPUTS;
290012d2ac60Schristos 		mx[index].mx_di.un.v.delta = 256 /
290163bce96cSkre 		    (masterctl->ctl_step ? masterctl->ctl_step : 1);
29028a9ff04bSjmcneill 		strcpy(mx[index].mx_di.label.name, AudioNmaster);
29038a9ff04bSjmcneill 		strcpy(mx[index].mx_di.un.v.units.name, AudioNvolume);
29048a9ff04bSjmcneill 		hda_trace(sc, "  adding outputs.%s\n",
29058a9ff04bSjmcneill 		    mx[index].mx_di.label.name);
29068a9ff04bSjmcneill 		++index;
29078a9ff04bSjmcneill 		if (masterctl->ctl_mute) {
29088a9ff04bSjmcneill 			mx[index] = mx[index - 1];
29098a9ff04bSjmcneill 			mx[index].mx_di.index = index;
29108a9ff04bSjmcneill 			mx[index].mx_di.type = AUDIO_MIXER_ENUM;
29118a9ff04bSjmcneill 			mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
29128a9ff04bSjmcneill 			strcpy(mx[index].mx_di.label.name, AudioNmaster "." AudioNmute);
29138a9ff04bSjmcneill 			mx[index].mx_di.un.e.num_mem = 2;
29148a9ff04bSjmcneill 			strcpy(mx[index].mx_di.un.e.member[0].label.name, AudioNoff);
29158a9ff04bSjmcneill 			mx[index].mx_di.un.e.member[0].ord = 0;
29168a9ff04bSjmcneill 			strcpy(mx[index].mx_di.un.e.member[1].label.name, AudioNon);
29178a9ff04bSjmcneill 			mx[index].mx_di.un.e.member[1].ord = 1;
29188a9ff04bSjmcneill 			++index;
29198a9ff04bSjmcneill 		}
29208a9ff04bSjmcneill 	}
29218a9ff04bSjmcneill 
29228a9ff04bSjmcneill 	/* Build volume mixers */
29238a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nctls; i++) {
29248a9ff04bSjmcneill 		uint32_t audiodev;
29258a9ff04bSjmcneill 
29268a9ff04bSjmcneill 		ctl = &sc->sc_ctls[i];
29278a9ff04bSjmcneill 		if (ctl->ctl_enable == false ||
29288a9ff04bSjmcneill 		    ctl->ctl_audiomask == 0)
29298a9ff04bSjmcneill 			continue;
29308a9ff04bSjmcneill 		audiodev = ffs(ctl->ctl_audiomask) - 1;
29318a9ff04bSjmcneill 		mx[index].mx_ctl = ctl;
29328a9ff04bSjmcneill 		mx[index].mx_di.index = index;
29338a9ff04bSjmcneill 		mx[index].mx_di.type = AUDIO_MIXER_VALUE;
29348a9ff04bSjmcneill 		mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
29358a9ff04bSjmcneill 		mx[index].mx_di.un.v.num_channels = 2;	/* XXX */
293612d2ac60Schristos 		mx[index].mx_di.un.v.delta = 256 /
293712d2ac60Schristos 		    (ctl->ctl_step ? ctl->ctl_step : 1);
29388a9ff04bSjmcneill 		if (ctrlcnt[audiodev] > 0)
29398a9ff04bSjmcneill 			snprintf(mx[index].mx_di.label.name,
29408a9ff04bSjmcneill 			    sizeof(mx[index].mx_di.label.name),
29418a9ff04bSjmcneill 			    "%s%d",
29428a9ff04bSjmcneill 			    hdafg_mixer_names[audiodev],
29438a9ff04bSjmcneill 			    ctrlcnt[audiodev] + 1);
29448a9ff04bSjmcneill 		else
29458a9ff04bSjmcneill 			strcpy(mx[index].mx_di.label.name,
29468a9ff04bSjmcneill 			    hdafg_mixer_names[audiodev]);
29478a9ff04bSjmcneill 		ctrlcnt[audiodev]++;
29488a9ff04bSjmcneill 
29498a9ff04bSjmcneill 		switch (audiodev) {
29508a9ff04bSjmcneill 		case HDAUDIO_MIXER_VOLUME:
29518a9ff04bSjmcneill 		case HDAUDIO_MIXER_BASS:
29528a9ff04bSjmcneill 		case HDAUDIO_MIXER_TREBLE:
29538a9ff04bSjmcneill 		case HDAUDIO_MIXER_OGAIN:
29548a9ff04bSjmcneill 			mx[index].mx_di.mixer_class =
29558a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_OUTPUTS;
29568a9ff04bSjmcneill 			hda_trace(sc, "  adding outputs.%s\n",
29578a9ff04bSjmcneill 			    mx[index].mx_di.label.name);
29588a9ff04bSjmcneill 			break;
29598a9ff04bSjmcneill 		case HDAUDIO_MIXER_MIC:
29608a9ff04bSjmcneill 		case HDAUDIO_MIXER_MONITOR:
29618a9ff04bSjmcneill 			mx[index].mx_di.mixer_class =
29628a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_RECORD;
29638a9ff04bSjmcneill 			hda_trace(sc, "  adding record.%s\n",
29648a9ff04bSjmcneill 			    mx[index].mx_di.label.name);
29658a9ff04bSjmcneill 			break;
29668a9ff04bSjmcneill 		default:
29678a9ff04bSjmcneill 			mx[index].mx_di.mixer_class =
29688a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_INPUTS;
29698a9ff04bSjmcneill 			hda_trace(sc, "  adding inputs.%s\n",
29708a9ff04bSjmcneill 			    mx[index].mx_di.label.name);
29718a9ff04bSjmcneill 			break;
29728a9ff04bSjmcneill 		}
29738a9ff04bSjmcneill 		strcpy(mx[index].mx_di.un.v.units.name, AudioNvolume);
29748a9ff04bSjmcneill 
29758a9ff04bSjmcneill 		++index;
29768a9ff04bSjmcneill 
29778a9ff04bSjmcneill 		if (ctl->ctl_mute) {
29788a9ff04bSjmcneill 			mx[index] = mx[index - 1];
29798a9ff04bSjmcneill 			mx[index].mx_di.index = index;
29808a9ff04bSjmcneill 			mx[index].mx_di.type = AUDIO_MIXER_ENUM;
29818a9ff04bSjmcneill 			mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
29828a9ff04bSjmcneill 			snprintf(mx[index].mx_di.label.name,
29838a9ff04bSjmcneill 			    sizeof(mx[index].mx_di.label.name),
29848a9ff04bSjmcneill 			    "%s." AudioNmute,
29858a9ff04bSjmcneill 			    mx[index - 1].mx_di.label.name);
29868a9ff04bSjmcneill 			mx[index].mx_di.un.e.num_mem = 2;
29878a9ff04bSjmcneill 			strcpy(mx[index].mx_di.un.e.member[0].label.name, AudioNoff);
29888a9ff04bSjmcneill 			mx[index].mx_di.un.e.member[0].ord = 0;
29898a9ff04bSjmcneill 			strcpy(mx[index].mx_di.un.e.member[1].label.name, AudioNon);
29908a9ff04bSjmcneill 			mx[index].mx_di.un.e.member[1].ord = 1;
29918a9ff04bSjmcneill 			++index;
29928a9ff04bSjmcneill 		}
29938a9ff04bSjmcneill 	}
29948a9ff04bSjmcneill 
29958a9ff04bSjmcneill 	/* DAC selector */
29968a9ff04bSjmcneill 	if (ndac > 0) {
29978a9ff04bSjmcneill 		mx[index].mx_ctl = NULL;
29988a9ff04bSjmcneill 		mx[index].mx_di.index = index;
29998a9ff04bSjmcneill 		mx[index].mx_di.type = AUDIO_MIXER_SET;
30008a9ff04bSjmcneill 		mx[index].mx_di.mixer_class = HDAUDIO_MIXER_CLASS_OUTPUTS;
30018a9ff04bSjmcneill 		mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
30028a9ff04bSjmcneill 		strcpy(mx[index].mx_di.label.name, "dacsel"); /* AudioNselect */
30038a9ff04bSjmcneill 		mx[index].mx_di.un.s.num_mem = ndac;
30048a9ff04bSjmcneill 		for (i = 0, j = 0; i < sc->sc_nassocs; i++) {
30058a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_enable == false)
30068a9ff04bSjmcneill 				continue;
30078a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_dir != HDAUDIO_PINDIR_OUT)
30088a9ff04bSjmcneill 				continue;
30098a9ff04bSjmcneill 			mx[index].mx_di.un.s.member[j].mask = 1 << i;
30108a9ff04bSjmcneill 			snprintf(mx[index].mx_di.un.s.member[j].label.name,
30118a9ff04bSjmcneill 			    sizeof(mx[index].mx_di.un.s.member[j].label.name),
30128a9ff04bSjmcneill 			    "%s%02X",
30138a9ff04bSjmcneill 			    hdafg_assoc_type_string(&sc->sc_assocs[i]), i);
30148a9ff04bSjmcneill 			++j;
30158a9ff04bSjmcneill 		}
30168a9ff04bSjmcneill 		++index;
30178a9ff04bSjmcneill 	}
30188a9ff04bSjmcneill 
30198a9ff04bSjmcneill 	/* ADC selector */
30208a9ff04bSjmcneill 	if (nadc > 0) {
30218a9ff04bSjmcneill 		mx[index].mx_ctl = NULL;
30228a9ff04bSjmcneill 		mx[index].mx_di.index = index;
30238a9ff04bSjmcneill 		mx[index].mx_di.type = AUDIO_MIXER_SET;
30248a9ff04bSjmcneill 		mx[index].mx_di.mixer_class = HDAUDIO_MIXER_CLASS_RECORD;
30258a9ff04bSjmcneill 		mx[index].mx_di.prev = mx[index].mx_di.next = AUDIO_MIXER_LAST;
30268a9ff04bSjmcneill 		strcpy(mx[index].mx_di.label.name, AudioNsource);
30278a9ff04bSjmcneill 		mx[index].mx_di.un.s.num_mem = nadc;
30288a9ff04bSjmcneill 		for (i = 0, j = 0; i < sc->sc_nassocs; i++) {
30298a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_enable == false)
30308a9ff04bSjmcneill 				continue;
30318a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_dir != HDAUDIO_PINDIR_IN)
30328a9ff04bSjmcneill 				continue;
30338a9ff04bSjmcneill 			mx[index].mx_di.un.s.member[j].mask = 1 << i;
30348a9ff04bSjmcneill 			snprintf(mx[index].mx_di.un.s.member[j].label.name,
30358a9ff04bSjmcneill 			    sizeof(mx[index].mx_di.un.s.member[j].label.name),
30368a9ff04bSjmcneill 			    "%s%02X",
30378a9ff04bSjmcneill 			    hdafg_assoc_type_string(&sc->sc_assocs[i]), i);
30388a9ff04bSjmcneill 			++j;
30398a9ff04bSjmcneill 		}
30408a9ff04bSjmcneill 		++index;
30418a9ff04bSjmcneill 	}
30428a9ff04bSjmcneill 
30438a9ff04bSjmcneill 	sc->sc_mixers = mx;
30448a9ff04bSjmcneill }
30458a9ff04bSjmcneill 
30468a9ff04bSjmcneill static void
hdafg_commit(struct hdafg_softc * sc)30478a9ff04bSjmcneill hdafg_commit(struct hdafg_softc *sc)
30488a9ff04bSjmcneill {
30498a9ff04bSjmcneill 	struct hdaudio_widget *w;
30508a9ff04bSjmcneill 	uint32_t gdata, gmask, gdir;
30518a9ff04bSjmcneill 	int commitgpio;
30528a9ff04bSjmcneill 	int i;
30538a9ff04bSjmcneill 
30548a9ff04bSjmcneill 	/* Commit controls */
30558a9ff04bSjmcneill 	hdafg_control_commit(sc);
30568a9ff04bSjmcneill 
30578a9ff04bSjmcneill 	/* Commit selectors, pins, and EAPD */
30588a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nwidgets; i++) {
30598a9ff04bSjmcneill 		w = &sc->sc_widgets[i];
30608a9ff04bSjmcneill 		if (w->w_selconn == -1)
30618a9ff04bSjmcneill 			w->w_selconn = 0;
30628a9ff04bSjmcneill 		if (w->w_nconns > 0)
30638a9ff04bSjmcneill 			hdafg_widget_connection_select(w, w->w_selconn);
30648a9ff04bSjmcneill 		if (w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX)
30658a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
30668a9ff04bSjmcneill 			    CORB_SET_PIN_WIDGET_CONTROL, w->w_pin.ctrl);
30678a9ff04bSjmcneill 		if (w->w_p.eapdbtl != 0xffffffff)
30688a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
30698a9ff04bSjmcneill 			    CORB_SET_EAPD_BTL_ENABLE, w->w_p.eapdbtl);
30708a9ff04bSjmcneill 	}
30718a9ff04bSjmcneill 
30728a9ff04bSjmcneill 	gdata = gmask = gdir = commitgpio = 0;
30738a9ff04bSjmcneill #ifdef notyet
30748a9ff04bSjmcneill 	int numgpio = COP_GPIO_COUNT_NUM_GPIO(sc->sc_p.gpio_cnt);
30758a9ff04bSjmcneill 
30768a9ff04bSjmcneill 	hda_trace(sc, "found %d GPIOs\n", numgpio);
30778a9ff04bSjmcneill 	for (i = 0; i < numgpio && i < 8; i++) {
30788a9ff04bSjmcneill 		if (commitgpio == 0)
30798a9ff04bSjmcneill 			commitgpio = 1;
30808a9ff04bSjmcneill 		gdata |= 1 << i;
30818a9ff04bSjmcneill 		gmask |= 1 << i;
30828a9ff04bSjmcneill 		gdir |= 1 << i;
30838a9ff04bSjmcneill 	}
30848a9ff04bSjmcneill #endif
30858a9ff04bSjmcneill 
30868a9ff04bSjmcneill 	if (commitgpio) {
30878a9ff04bSjmcneill 		hda_trace(sc, "GPIO commit: data=%08X mask=%08X dir=%08X\n",
30888a9ff04bSjmcneill 		    gdata, gmask, gdir);
30898a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, sc->sc_nid,
30908a9ff04bSjmcneill 		    CORB_SET_GPIO_ENABLE_MASK, gmask);
30918a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, sc->sc_nid,
30928a9ff04bSjmcneill 		    CORB_SET_GPIO_DIRECTION, gdir);
30938a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, sc->sc_nid,
30948a9ff04bSjmcneill 		    CORB_SET_GPIO_DATA, gdata);
30958a9ff04bSjmcneill 	}
30968a9ff04bSjmcneill }
30978a9ff04bSjmcneill 
30988a9ff04bSjmcneill static void
hdafg_stream_connect_hdmi(struct hdafg_softc * sc,struct hdaudio_assoc * as,struct hdaudio_widget * w,const audio_params_t * params)30998a9ff04bSjmcneill hdafg_stream_connect_hdmi(struct hdafg_softc *sc, struct hdaudio_assoc *as,
31008e864669Sjmcneill     struct hdaudio_widget *w, const audio_params_t *params)
31018a9ff04bSjmcneill {
31028a9ff04bSjmcneill 	struct hdmi_audio_infoframe hdmi;
31038a9ff04bSjmcneill 	/* TODO struct displayport_audio_infoframe dp; */
31048a9ff04bSjmcneill 	uint8_t *dip = NULL;
31058a9ff04bSjmcneill 	size_t diplen = 0;
31068a9ff04bSjmcneill 	int i;
31078a9ff04bSjmcneill 
31088a9ff04bSjmcneill #ifdef HDAFG_HDMI_DEBUG
31098a9ff04bSjmcneill 	uint32_t res;
31108a9ff04bSjmcneill 	res = hdaudio_command(sc->sc_codec, w->w_nid,
31118a9ff04bSjmcneill 	    CORB_GET_HDMI_DIP_XMIT_CTRL, 0);
31128a9ff04bSjmcneill 	hda_print(sc, "connect HDMI nid %02X, xmitctrl = 0x%08X\n",
31138a9ff04bSjmcneill 	    w->w_nid, res);
31148a9ff04bSjmcneill #endif
31158a9ff04bSjmcneill 
31168a9ff04bSjmcneill 	/* disable infoframe transmission */
31178a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
31188a9ff04bSjmcneill 	    CORB_SET_HDMI_DIP_XMIT_CTRL, COP_DIP_XMIT_CTRL_DISABLE);
31198a9ff04bSjmcneill 
3120a85a8464Sjmcneill 	if (sc->sc_disable_dip)
3121a85a8464Sjmcneill 		return;
3122a85a8464Sjmcneill 
31238a9ff04bSjmcneill 	/* build new infoframe */
31248a9ff04bSjmcneill 	if (as->as_digital == HDAFG_AS_HDMI) {
31258a9ff04bSjmcneill 		dip = (uint8_t *)&hdmi;
31268a9ff04bSjmcneill 		diplen = sizeof(hdmi);
31278a9ff04bSjmcneill 		memset(&hdmi, 0, sizeof(hdmi));
31288a9ff04bSjmcneill 		hdmi.header.packet_type = HDMI_AI_PACKET_TYPE;
31298a9ff04bSjmcneill 		hdmi.header.version = HDMI_AI_VERSION;
31308a9ff04bSjmcneill 		hdmi.header.length = HDMI_AI_LENGTH;
31318e864669Sjmcneill 		hdmi.ct_cc = params->channels - 1;
31328e864669Sjmcneill 		if (params->channels > 2) {
31338e864669Sjmcneill 			hdmi.ca = 0x1f;
31348e864669Sjmcneill 		} else {
31358e864669Sjmcneill 			hdmi.ca = 0x00;
31368e864669Sjmcneill 		}
31378a9ff04bSjmcneill 		hdafg_dd_hdmi_ai_cksum(&hdmi);
31388a9ff04bSjmcneill 	}
31398a9ff04bSjmcneill 	/* update data island with new audio infoframe */
31408a9ff04bSjmcneill 	if (dip) {
31418a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, w->w_nid,
31428a9ff04bSjmcneill 		    CORB_SET_HDMI_DIP_INDEX, 0);
31438a9ff04bSjmcneill 		for (i = 0; i < diplen; i++) {
31448a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
31458a9ff04bSjmcneill 			    CORB_SET_HDMI_DIP_DATA, dip[i]);
31468a9ff04bSjmcneill 		}
31478a9ff04bSjmcneill 	}
31488a9ff04bSjmcneill 
31498a9ff04bSjmcneill 	/* enable infoframe transmission */
31508a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, w->w_nid,
31518a9ff04bSjmcneill 	    CORB_SET_HDMI_DIP_XMIT_CTRL, COP_DIP_XMIT_CTRL_BEST_EFFORT);
31528a9ff04bSjmcneill }
31538a9ff04bSjmcneill 
31548a9ff04bSjmcneill static void
hdafg_stream_connect(struct hdafg_softc * sc,int mode)31558a9ff04bSjmcneill hdafg_stream_connect(struct hdafg_softc *sc, int mode)
31568a9ff04bSjmcneill {
31578a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
31588a9ff04bSjmcneill 	struct hdaudio_widget *w;
31598e864669Sjmcneill 	const audio_params_t *params;
31608a9ff04bSjmcneill 	uint16_t fmt, dfmt;
31618a9ff04bSjmcneill 	int tag, chn, maxchan, c;
31628a9ff04bSjmcneill 	int i, j, k;
31638a9ff04bSjmcneill 
31648a9ff04bSjmcneill 	KASSERT(mode == AUMODE_PLAY || mode == AUMODE_RECORD);
31658a9ff04bSjmcneill 
31668e864669Sjmcneill 	if (mode == AUMODE_PLAY) {
31678a9ff04bSjmcneill 		fmt = hdaudio_stream_param(sc->sc_audiodev.ad_playback,
31688a9ff04bSjmcneill 		    &sc->sc_pparam);
31698e864669Sjmcneill 		params = &sc->sc_pparam;
31708e864669Sjmcneill 	} else {
31718a9ff04bSjmcneill 		fmt = hdaudio_stream_param(sc->sc_audiodev.ad_capture,
31728a9ff04bSjmcneill 		    &sc->sc_rparam);
31738e864669Sjmcneill 		params = &sc->sc_rparam;
31748e864669Sjmcneill 	}
31758a9ff04bSjmcneill 
31768a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
31778a9ff04bSjmcneill 		if (as[i].as_enable == false)
31788a9ff04bSjmcneill 			continue;
31798a9ff04bSjmcneill 
31808a9ff04bSjmcneill 		if (mode == AUMODE_PLAY && as[i].as_dir != HDAUDIO_PINDIR_OUT)
31818a9ff04bSjmcneill 			continue;
31828a9ff04bSjmcneill 		if (mode == AUMODE_RECORD && as[i].as_dir != HDAUDIO_PINDIR_IN)
31838a9ff04bSjmcneill 			continue;
31848a9ff04bSjmcneill 
31858a9ff04bSjmcneill 		fmt &= ~HDAUDIO_FMT_CHAN_MASK;
31868a9ff04bSjmcneill 		if (as[i].as_dir == HDAUDIO_PINDIR_OUT &&
31878a9ff04bSjmcneill 		    sc->sc_audiodev.ad_playback != NULL) {
31888a9ff04bSjmcneill 			tag = hdaudio_stream_tag(sc->sc_audiodev.ad_playback);
31898a9ff04bSjmcneill 			fmt |= HDAUDIO_FMT_CHAN(sc->sc_pparam.channels);
31908a9ff04bSjmcneill 			maxchan = sc->sc_pparam.channels;
31918a9ff04bSjmcneill 		} else if (as[i].as_dir == HDAUDIO_PINDIR_IN &&
31928a9ff04bSjmcneill 		    sc->sc_audiodev.ad_capture != NULL) {
31938a9ff04bSjmcneill 			tag = hdaudio_stream_tag(sc->sc_audiodev.ad_capture);
31948a9ff04bSjmcneill 			fmt |= HDAUDIO_FMT_CHAN(sc->sc_rparam.channels);
31958a9ff04bSjmcneill 			maxchan = sc->sc_rparam.channels;
31968a9ff04bSjmcneill 		} else {
31978a9ff04bSjmcneill 			tag = 0;
31988a9ff04bSjmcneill 			if (as[i].as_dir == HDAUDIO_PINDIR_OUT) {
31998a9ff04bSjmcneill 				fmt |= HDAUDIO_FMT_CHAN(sc->sc_pchan);
32008a9ff04bSjmcneill 				maxchan = sc->sc_pchan;
32018a9ff04bSjmcneill 			} else {
32028a9ff04bSjmcneill 				fmt |= HDAUDIO_FMT_CHAN(sc->sc_rchan);
32038a9ff04bSjmcneill 				maxchan = sc->sc_rchan;
32048a9ff04bSjmcneill 			}
32058a9ff04bSjmcneill 		}
32068a9ff04bSjmcneill 
32078a9ff04bSjmcneill 		chn = 0;
32088a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
32098a9ff04bSjmcneill 			if (as[i].as_dacs[j] == 0)
32108a9ff04bSjmcneill 				continue;
32118a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_dacs[j]);
32128a9ff04bSjmcneill 			if (w == NULL || w->w_enable == FALSE)
32138a9ff04bSjmcneill 				continue;
32148a9ff04bSjmcneill 			if (as[i].as_hpredir >= 0 && i == as[i].as_pincnt)
32158a9ff04bSjmcneill 				chn = 0;
32168a9ff04bSjmcneill 			if (chn >= maxchan)
32178a9ff04bSjmcneill 				chn = 0;	/* XXX */
32188a9ff04bSjmcneill 			c = (tag << 4) | chn;
32198a9ff04bSjmcneill 
32208a9ff04bSjmcneill 			if (as[i].as_activated == false)
32218a9ff04bSjmcneill 				c = 0;
32228a9ff04bSjmcneill 
32238a9ff04bSjmcneill 			/*
32248a9ff04bSjmcneill 			 * If a non-PCM stream is being connected, and the
32258a9ff04bSjmcneill 			 * analog converter doesn't support non-PCM streams,
32268a9ff04bSjmcneill 			 * then don't decode it
32278a9ff04bSjmcneill 			 */
32288a9ff04bSjmcneill 			if (!(w->w_p.aw_cap & COP_AWCAP_DIGITAL) &&
32298a9ff04bSjmcneill 			    !(w->w_p.stream_format & COP_STREAM_FORMAT_AC3) &&
32308a9ff04bSjmcneill 			    (fmt & HDAUDIO_FMT_TYPE_NONPCM)) {
32318a9ff04bSjmcneill 				hdaudio_command(sc->sc_codec, w->w_nid,
32328a9ff04bSjmcneill 				    CORB_SET_CONVERTER_STREAM_CHANNEL, 0);
32338a9ff04bSjmcneill 				continue;
32348a9ff04bSjmcneill 			}
32358a9ff04bSjmcneill 
32368a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
32378a9ff04bSjmcneill 			    CORB_SET_CONVERTER_FORMAT, fmt);
32388a9ff04bSjmcneill 			if (w->w_p.aw_cap & COP_AWCAP_DIGITAL) {
32398a9ff04bSjmcneill 				dfmt = hdaudio_command(sc->sc_codec, w->w_nid,
32408a9ff04bSjmcneill 				    CORB_GET_DIGITAL_CONVERTER_CONTROL, 0) &
32418a9ff04bSjmcneill 				    0xff;
32428a9ff04bSjmcneill 				dfmt |= COP_DIGITAL_CONVCTRL1_DIGEN;
32438a9ff04bSjmcneill 				if (fmt & HDAUDIO_FMT_TYPE_NONPCM)
32448a9ff04bSjmcneill 					dfmt |= COP_DIGITAL_CONVCTRL1_NAUDIO;
32458a9ff04bSjmcneill 				else
32468a9ff04bSjmcneill 					dfmt &= ~COP_DIGITAL_CONVCTRL1_NAUDIO;
32478e864669Sjmcneill 				if (sc->sc_vendor == HDAUDIO_VENDOR_NVIDIA)
32488e864669Sjmcneill 					dfmt |= COP_DIGITAL_CONVCTRL1_COPY;
32498a9ff04bSjmcneill 				hdaudio_command(sc->sc_codec, w->w_nid,
32508a9ff04bSjmcneill 				    CORB_SET_DIGITAL_CONVERTER_CONTROL_1, dfmt);
32518a9ff04bSjmcneill 			}
32528a9ff04bSjmcneill 			if (w->w_pin.cap & (COP_PINCAP_HDMI|COP_PINCAP_DP)) {
32538a9ff04bSjmcneill 				hdaudio_command(sc->sc_codec, w->w_nid,
32548a9ff04bSjmcneill 				    CORB_SET_CONVERTER_CHANNEL_COUNT,
32558a9ff04bSjmcneill 				    maxchan - 1);
32568a9ff04bSjmcneill 				for (k = 0; k < maxchan; k++) {
32578a9ff04bSjmcneill 					hdaudio_command(sc->sc_codec, w->w_nid,
32588a9ff04bSjmcneill 					    CORB_ASP_SET_CHANNEL_MAPPING,
32598a9ff04bSjmcneill 					    (k << 4) | k);
32608a9ff04bSjmcneill 				}
32618a9ff04bSjmcneill 			}
32628a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
32638a9ff04bSjmcneill 			    CORB_SET_CONVERTER_STREAM_CHANNEL, c);
32648a9ff04bSjmcneill 			chn += COP_AWCAP_CHANNEL_COUNT(w->w_p.aw_cap);
32658a9ff04bSjmcneill 		}
32668a9ff04bSjmcneill 
32678a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
32688a9ff04bSjmcneill 			if (as[i].as_pins[j] == 0)
32698a9ff04bSjmcneill 				continue;
32708a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_pins[j]);
32718a9ff04bSjmcneill 			if (w == NULL || w->w_enable == FALSE)
32728a9ff04bSjmcneill 				continue;
32738a9ff04bSjmcneill 			if (w->w_pin.cap & (COP_PINCAP_HDMI|COP_PINCAP_DP))
32748a9ff04bSjmcneill 				hdafg_stream_connect_hdmi(sc, &as[i],
32758e864669Sjmcneill 				    w, params);
32768a9ff04bSjmcneill 		}
32778a9ff04bSjmcneill 	}
32788a9ff04bSjmcneill }
32798a9ff04bSjmcneill 
32808a9ff04bSjmcneill static int
hdafg_stream_intr(struct hdaudio_stream * st)32818a9ff04bSjmcneill hdafg_stream_intr(struct hdaudio_stream *st)
32828a9ff04bSjmcneill {
32838a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = st->st_cookie;
32848a9ff04bSjmcneill 	int handled = 0;
32858a9ff04bSjmcneill 
32868a9ff04bSjmcneill 	(void)hda_read1(ad->ad_sc->sc_host, HDAUDIO_SD_STS(st->st_shift));
32878a9ff04bSjmcneill 	hda_write1(ad->ad_sc->sc_host, HDAUDIO_SD_STS(st->st_shift),
32888a9ff04bSjmcneill 	    HDAUDIO_STS_DESE | HDAUDIO_STS_FIFOE | HDAUDIO_STS_BCIS);
32898a9ff04bSjmcneill 
32908a9ff04bSjmcneill 	mutex_spin_enter(&ad->ad_sc->sc_intr_lock);
32918a9ff04bSjmcneill 	/* XXX test (sts & HDAUDIO_STS_BCIS)? */
32928a9ff04bSjmcneill 	if (st == ad->ad_playback && ad->ad_playbackintr) {
32938a9ff04bSjmcneill 		ad->ad_playbackintr(ad->ad_playbackintrarg);
32948a9ff04bSjmcneill 		handled = 1;
32958a9ff04bSjmcneill 	} else if (st == ad->ad_capture && ad->ad_captureintr) {
32968a9ff04bSjmcneill 		ad->ad_captureintr(ad->ad_captureintrarg);
32978a9ff04bSjmcneill 		handled = 1;
32988a9ff04bSjmcneill 	}
32998a9ff04bSjmcneill 	mutex_spin_exit(&ad->ad_sc->sc_intr_lock);
33008a9ff04bSjmcneill 
33018a9ff04bSjmcneill 	return handled;
33028a9ff04bSjmcneill }
33038a9ff04bSjmcneill 
33048a9ff04bSjmcneill static bool
hdafg_rate_supported(struct hdafg_softc * sc,u_int frequency)33058a9ff04bSjmcneill hdafg_rate_supported(struct hdafg_softc *sc, u_int frequency)
33068a9ff04bSjmcneill {
33078a9ff04bSjmcneill 	uint32_t caps = sc->sc_p.pcm_size_rate;
33088a9ff04bSjmcneill 
33098e864669Sjmcneill 	if (sc->sc_fixed_rate)
33108e864669Sjmcneill 		return frequency == sc->sc_fixed_rate;
33118e864669Sjmcneill 
33128a9ff04bSjmcneill #define ISFREQOK(shift) ((caps & (1 << (shift))) ? true : false)
33138a9ff04bSjmcneill 	switch (frequency) {
33148a9ff04bSjmcneill 	case 8000:
33158a9ff04bSjmcneill 		return ISFREQOK(0);
33168a9ff04bSjmcneill 	case 11025:
33178a9ff04bSjmcneill 		return ISFREQOK(1);
33188a9ff04bSjmcneill 	case 16000:
33198a9ff04bSjmcneill 		return ISFREQOK(2);
33208a9ff04bSjmcneill 	case 22050:
33218a9ff04bSjmcneill 		return ISFREQOK(3);
33228a9ff04bSjmcneill 	case 32000:
33238a9ff04bSjmcneill 		return ISFREQOK(4);
33248a9ff04bSjmcneill 	case 44100:
33258a9ff04bSjmcneill 		return ISFREQOK(5);
33268e864669Sjmcneill 		return true;
33278a9ff04bSjmcneill 	case 48000:
33288a9ff04bSjmcneill 		return true;	/* Must be supported by all codecs */
33298a9ff04bSjmcneill 	case 88200:
33308a9ff04bSjmcneill 		return ISFREQOK(7);
33318a9ff04bSjmcneill 	case 96000:
33328a9ff04bSjmcneill 		return ISFREQOK(8);
33338a9ff04bSjmcneill 	case 176400:
33348a9ff04bSjmcneill 		return ISFREQOK(9);
33358a9ff04bSjmcneill 	case 192000:
33368a9ff04bSjmcneill 		return ISFREQOK(10);
33378a9ff04bSjmcneill 	case 384000:
33388a9ff04bSjmcneill 		return ISFREQOK(11);
33398a9ff04bSjmcneill 	default:
33408a9ff04bSjmcneill 		return false;
33418a9ff04bSjmcneill 	}
33428a9ff04bSjmcneill #undef ISFREQOK
33438a9ff04bSjmcneill }
33448a9ff04bSjmcneill 
33458a9ff04bSjmcneill static bool
hdafg_bits_supported(struct hdafg_softc * sc,u_int bits)33468a9ff04bSjmcneill hdafg_bits_supported(struct hdafg_softc *sc, u_int bits)
33478a9ff04bSjmcneill {
33488a9ff04bSjmcneill 	uint32_t caps = sc->sc_p.pcm_size_rate;
33498a9ff04bSjmcneill #define ISBITSOK(shift) ((caps & (1 << (shift))) ? true : false)
33508a9ff04bSjmcneill 	switch (bits) {
33518a9ff04bSjmcneill 	case 8:
33528a9ff04bSjmcneill 		return ISBITSOK(16);
33538a9ff04bSjmcneill 	case 16:
33548a9ff04bSjmcneill 		return ISBITSOK(17);
33558a9ff04bSjmcneill 	case 20:
33568a9ff04bSjmcneill 		return ISBITSOK(18);
33578a9ff04bSjmcneill 	case 24:
33588a9ff04bSjmcneill 		return ISBITSOK(19);
33598a9ff04bSjmcneill 	case 32:
33608a9ff04bSjmcneill 		return ISBITSOK(20);
33618a9ff04bSjmcneill 	default:
33628a9ff04bSjmcneill 		return false;
33638a9ff04bSjmcneill 	}
33648a9ff04bSjmcneill #undef ISBITSOK
33658a9ff04bSjmcneill }
33668a9ff04bSjmcneill 
33678a9ff04bSjmcneill static bool
hdafg_probe_encoding(struct hdafg_softc * sc,u_int validbits,u_int precision,int encoding,bool force)33688a9ff04bSjmcneill hdafg_probe_encoding(struct hdafg_softc *sc,
33698a9ff04bSjmcneill     u_int validbits, u_int precision, int encoding, bool force)
33708a9ff04bSjmcneill {
33718a9ff04bSjmcneill 	struct audio_format f;
33728a9ff04bSjmcneill 	int i;
33738a9ff04bSjmcneill 
33748a9ff04bSjmcneill 	if (!force && hdafg_bits_supported(sc, validbits) == false)
33758a9ff04bSjmcneill 		return false;
33768a9ff04bSjmcneill 
33778a9ff04bSjmcneill 	memset(&f, 0, sizeof(f));
33788a9ff04bSjmcneill 	f.driver_data = NULL;
33798a9ff04bSjmcneill 	f.mode = 0;
33808a9ff04bSjmcneill 	f.encoding = encoding;
33818a9ff04bSjmcneill 	f.validbits = validbits;
33828a9ff04bSjmcneill 	f.precision = precision;
33838a9ff04bSjmcneill 	f.channels = 0;
33848a9ff04bSjmcneill 	f.channel_mask = 0;
33858a9ff04bSjmcneill 	f.frequency_type = 0;
33868a9ff04bSjmcneill 	for (i = 0; i < __arraycount(hdafg_possible_rates); i++) {
33878a9ff04bSjmcneill 		u_int rate = hdafg_possible_rates[i];
33888a9ff04bSjmcneill 		if (hdafg_rate_supported(sc, rate))
33898a9ff04bSjmcneill 			f.frequency[f.frequency_type++] = rate;
33908a9ff04bSjmcneill 	}
3391e622eac4Sisaki 	/* XXX ad hoc.. */
3392e622eac4Sisaki 	if (encoding == AUDIO_ENCODING_AC3)
3393e622eac4Sisaki 		f.priority = -1;
33948a9ff04bSjmcneill 
33958a9ff04bSjmcneill #define HDAUDIO_INITFMT(ch, chmask)			\
33968a9ff04bSjmcneill 	do {						\
33978a9ff04bSjmcneill 		f.channels = (ch);			\
33988a9ff04bSjmcneill 		f.channel_mask = (chmask);		\
33998a9ff04bSjmcneill 		f.mode = 0;				\
34008a9ff04bSjmcneill 		if (sc->sc_pchan >= (ch))		\
34018a9ff04bSjmcneill 			f.mode |= AUMODE_PLAY;		\
34028a9ff04bSjmcneill 		if (sc->sc_rchan >= (ch))		\
34038a9ff04bSjmcneill 			f.mode |= AUMODE_RECORD;	\
34048a9ff04bSjmcneill 		if (f.mode != 0)			\
34058a9ff04bSjmcneill 			hdafg_append_formats(&sc->sc_audiodev, &f); \
34068a9ff04bSjmcneill 	} while (0)
34078a9ff04bSjmcneill 
34088a9ff04bSjmcneill 	/* Commented out, otherwise monaural samples play through left
34098a9ff04bSjmcneill 	 * channel only
34108a9ff04bSjmcneill 	 */
34118a9ff04bSjmcneill 	/* HDAUDIO_INITFMT(1, AUFMT_MONAURAL); */
34128a9ff04bSjmcneill 	HDAUDIO_INITFMT(2, AUFMT_STEREO);
34138a9ff04bSjmcneill 	HDAUDIO_INITFMT(4, AUFMT_SURROUND4);
34148a9ff04bSjmcneill 	HDAUDIO_INITFMT(6, AUFMT_DOLBY_5_1);
34158a9ff04bSjmcneill 	HDAUDIO_INITFMT(8, AUFMT_SURROUND_7_1);
34168a9ff04bSjmcneill 
34178a9ff04bSjmcneill #undef HDAUDIO_INITFMT
34188a9ff04bSjmcneill 
34198a9ff04bSjmcneill 	return true;
34208a9ff04bSjmcneill }
34218a9ff04bSjmcneill 
34228a9ff04bSjmcneill 
34238a9ff04bSjmcneill static void
hdafg_configure_encodings(struct hdafg_softc * sc)34248a9ff04bSjmcneill hdafg_configure_encodings(struct hdafg_softc *sc)
34258a9ff04bSjmcneill {
34268a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
34278a9ff04bSjmcneill 	struct hdaudio_widget *w;
34288a9ff04bSjmcneill 	struct audio_format f;
34298a9ff04bSjmcneill 	uint32_t stream_format, caps;
34308a9ff04bSjmcneill 	int nchan, i, nid;
34318a9ff04bSjmcneill 
34328a9ff04bSjmcneill 	sc->sc_pchan = sc->sc_rchan = 0;
34338a9ff04bSjmcneill 
34348ac9ff4eSmaya 	for (i = 0; i < sc->sc_nassocs; i++) {
34358a9ff04bSjmcneill 		nchan = hdafg_assoc_count_channels(sc, &as[i],
34368a9ff04bSjmcneill 		    HDAUDIO_PINDIR_OUT);
34378a9ff04bSjmcneill 		if (nchan > sc->sc_pchan)
34388a9ff04bSjmcneill 			sc->sc_pchan = nchan;
34398a9ff04bSjmcneill 	}
34408ac9ff4eSmaya 	for (i = 0; i < sc->sc_nassocs; i++) {
34418a9ff04bSjmcneill 		nchan = hdafg_assoc_count_channels(sc, &as[i],
34428a9ff04bSjmcneill 		    HDAUDIO_PINDIR_IN);
34438a9ff04bSjmcneill 		if (nchan > sc->sc_rchan)
34448a9ff04bSjmcneill 			sc->sc_rchan = nchan;
34458a9ff04bSjmcneill 	}
34468a9ff04bSjmcneill 	hda_print(sc, "%dch/%dch", sc->sc_pchan, sc->sc_rchan);
34478a9ff04bSjmcneill 
34488a9ff04bSjmcneill 	for (i = 0; i < __arraycount(hdafg_possible_rates); i++)
34498a9ff04bSjmcneill 		if (hdafg_rate_supported(sc,
34508a9ff04bSjmcneill 		    hdafg_possible_rates[i]))
34518a9ff04bSjmcneill 			hda_print1(sc, " %uHz", hdafg_possible_rates[i]);
34528a9ff04bSjmcneill 
34538a9ff04bSjmcneill 	stream_format = sc->sc_p.stream_format;
34548a9ff04bSjmcneill 	caps = 0;
34558a9ff04bSjmcneill 	for (nid = sc->sc_startnode; nid < sc->sc_endnode; nid++) {
34568a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, nid);
34578a9ff04bSjmcneill 		if (w == NULL)
34588a9ff04bSjmcneill 			continue;
34598a9ff04bSjmcneill 		stream_format |= w->w_p.stream_format;
34608a9ff04bSjmcneill 		caps |= w->w_p.aw_cap;
34618a9ff04bSjmcneill 	}
34628a9ff04bSjmcneill 	if (stream_format == 0) {
34638a9ff04bSjmcneill 		hda_print(sc,
34648a9ff04bSjmcneill 		    "WARNING: unsupported stream format mask 0x%X, assuming PCM\n",
34658a9ff04bSjmcneill 		    stream_format);
34668a9ff04bSjmcneill 		stream_format |= COP_STREAM_FORMAT_PCM;
34678a9ff04bSjmcneill 	}
34688a9ff04bSjmcneill 
34698a9ff04bSjmcneill 	if (stream_format & COP_STREAM_FORMAT_PCM) {
34708a9ff04bSjmcneill 		int e = AUDIO_ENCODING_SLINEAR_LE;
34718a9ff04bSjmcneill 		if (hdafg_probe_encoding(sc, 8, 16, e, false))
34728a9ff04bSjmcneill 			hda_print1(sc, " PCM8");
34738a9ff04bSjmcneill 		if (hdafg_probe_encoding(sc, 16, 16, e, false))
34748a9ff04bSjmcneill 			hda_print1(sc, " PCM16");
34758a9ff04bSjmcneill 		if (hdafg_probe_encoding(sc, 20, 32, e, false))
34768a9ff04bSjmcneill 			hda_print1(sc, " PCM20");
34778a9ff04bSjmcneill 		if (hdafg_probe_encoding(sc, 24, 32, e, false))
34788a9ff04bSjmcneill 			hda_print1(sc, " PCM24");
34798a9ff04bSjmcneill 		if (hdafg_probe_encoding(sc, 32, 32, e, false))
34808a9ff04bSjmcneill 			hda_print1(sc, " PCM32");
34818a9ff04bSjmcneill 	}
34828a9ff04bSjmcneill 
34838a9ff04bSjmcneill 	if ((stream_format & COP_STREAM_FORMAT_AC3) ||
34848a9ff04bSjmcneill 	    (caps & COP_AWCAP_DIGITAL)) {
34858a9ff04bSjmcneill 		int e = AUDIO_ENCODING_AC3;
34868a9ff04bSjmcneill 		if (hdafg_probe_encoding(sc, 16, 16, e, false))
34878a9ff04bSjmcneill 			hda_print1(sc, " AC3");
34888a9ff04bSjmcneill 	}
34898a9ff04bSjmcneill 
34908a9ff04bSjmcneill 	if (sc->sc_audiodev.ad_nformats == 0) {
34918a9ff04bSjmcneill 		hdafg_probe_encoding(sc, 16, 16, AUDIO_ENCODING_SLINEAR_LE, true);
34928a9ff04bSjmcneill 		hda_print1(sc, " PCM16*");
34938a9ff04bSjmcneill 	}
34948a9ff04bSjmcneill 
34958a9ff04bSjmcneill 	/*
34968a9ff04bSjmcneill 	 * XXX JDM 20090614
34978a9ff04bSjmcneill 	 * MI audio assumes that at least one playback and one capture format
34988a9ff04bSjmcneill 	 * is reported by the hw driver; until this bug is resolved just
34998a9ff04bSjmcneill 	 * report 2ch capabilities if the function group does not support
35008a9ff04bSjmcneill 	 * the direction.
35018a9ff04bSjmcneill 	 */
35028a9ff04bSjmcneill 	if (sc->sc_rchan == 0 || sc->sc_pchan == 0) {
35038a9ff04bSjmcneill 		memset(&f, 0, sizeof(f));
35048a9ff04bSjmcneill 		f.driver_data = NULL;
35058a9ff04bSjmcneill 		f.mode = 0;
35068a9ff04bSjmcneill 		f.encoding = AUDIO_ENCODING_SLINEAR_LE;
35078a9ff04bSjmcneill 		f.validbits = 16;
35088a9ff04bSjmcneill 		f.precision = 16;
35098a9ff04bSjmcneill 		f.channels = 2;
35108a9ff04bSjmcneill 		f.channel_mask = AUFMT_STEREO;
35118a9ff04bSjmcneill 		f.frequency_type = 0;
35128e864669Sjmcneill 		f.frequency[0] = f.frequency[1] = sc->sc_fixed_rate ?
35138e864669Sjmcneill 		    sc->sc_fixed_rate : 48000;
35148a9ff04bSjmcneill 		f.mode = AUMODE_PLAY|AUMODE_RECORD;
35158a9ff04bSjmcneill 		hdafg_append_formats(&sc->sc_audiodev, &f);
35168a9ff04bSjmcneill 	}
35178a9ff04bSjmcneill 
35188a9ff04bSjmcneill 	hda_print1(sc, "\n");
35198a9ff04bSjmcneill }
35208a9ff04bSjmcneill 
35218a9ff04bSjmcneill static void
hdafg_hp_switch_handler(struct hdafg_softc * sc)3522fdaf53a0Sriastradh hdafg_hp_switch_handler(struct hdafg_softc *sc)
35238a9ff04bSjmcneill {
35248a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
35258a9ff04bSjmcneill 	struct hdaudio_widget *w;
35268a9ff04bSjmcneill 	uint32_t res = 0;
35278a9ff04bSjmcneill 	int i, j;
35288a9ff04bSjmcneill 
3529fdaf53a0Sriastradh 	KASSERT(sc->sc_jack_polling);
3530fdaf53a0Sriastradh 	KASSERT(mutex_owned(&sc->sc_jack_lock));
3531fdaf53a0Sriastradh 
35328a9ff04bSjmcneill 	if (!device_is_active(sc->sc_dev))
3533fdaf53a0Sriastradh 		return;
35348a9ff04bSjmcneill 
35358a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
35368a9ff04bSjmcneill 		if (as[i].as_digital != HDAFG_AS_ANALOG &&
35378a9ff04bSjmcneill 		    as[i].as_digital != HDAFG_AS_SPDIF)
35388a9ff04bSjmcneill 			continue;
35398a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
35408a9ff04bSjmcneill 			if (as[i].as_pins[j] == 0)
35418a9ff04bSjmcneill 				continue;
35428a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_pins[j]);
35438a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
35448a9ff04bSjmcneill 				continue;
35458a9ff04bSjmcneill 			if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
35468a9ff04bSjmcneill 				continue;
35478a9ff04bSjmcneill 			if (COP_CFG_DEFAULT_DEVICE(w->w_pin.config) !=
35488a9ff04bSjmcneill 			    COP_DEVICE_HP_OUT)
35498a9ff04bSjmcneill 				continue;
35508a9ff04bSjmcneill 			res |= hdaudio_command(sc->sc_codec, as[i].as_pins[j],
35518a9ff04bSjmcneill 			    CORB_GET_PIN_SENSE, 0) &
35528a9ff04bSjmcneill 			    COP_GET_PIN_SENSE_PRESENSE_DETECT;
35538a9ff04bSjmcneill 		}
35548a9ff04bSjmcneill 	}
35558a9ff04bSjmcneill 
35568a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
35578a9ff04bSjmcneill 		if (as[i].as_digital != HDAFG_AS_ANALOG &&
35588a9ff04bSjmcneill 		    as[i].as_digital != HDAFG_AS_SPDIF)
35598a9ff04bSjmcneill 			continue;
35608a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
35618a9ff04bSjmcneill 			if (as[i].as_pins[j] == 0)
35628a9ff04bSjmcneill 				continue;
35638a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_pins[j]);
35648a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
35658a9ff04bSjmcneill 				continue;
35668a9ff04bSjmcneill 			if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
35678a9ff04bSjmcneill 				continue;
35688a9ff04bSjmcneill 			switch (COP_CFG_DEFAULT_DEVICE(w->w_pin.config)) {
35698a9ff04bSjmcneill 			case COP_DEVICE_HP_OUT:
35708a9ff04bSjmcneill 				if (res & COP_GET_PIN_SENSE_PRESENSE_DETECT)
35718a9ff04bSjmcneill 					w->w_pin.ctrl |= COP_PWC_OUT_ENABLE;
35728a9ff04bSjmcneill 				else
35738a9ff04bSjmcneill 					w->w_pin.ctrl &= ~COP_PWC_OUT_ENABLE;
35748a9ff04bSjmcneill 				hdaudio_command(sc->sc_codec, w->w_nid,
35758a9ff04bSjmcneill 				    CORB_SET_PIN_WIDGET_CONTROL, w->w_pin.ctrl);
35768a9ff04bSjmcneill 				break;
35778a9ff04bSjmcneill 			case COP_DEVICE_LINE_OUT:
35788a9ff04bSjmcneill 			case COP_DEVICE_SPEAKER:
35798a9ff04bSjmcneill 			case COP_DEVICE_AUX:
35808a9ff04bSjmcneill 				if (res & COP_GET_PIN_SENSE_PRESENSE_DETECT)
35818a9ff04bSjmcneill 					w->w_pin.ctrl &= ~COP_PWC_OUT_ENABLE;
35828a9ff04bSjmcneill 				else
35838a9ff04bSjmcneill 					w->w_pin.ctrl |= COP_PWC_OUT_ENABLE;
35848a9ff04bSjmcneill 				hdaudio_command(sc->sc_codec, w->w_nid,
35858a9ff04bSjmcneill 				    CORB_SET_PIN_WIDGET_CONTROL, w->w_pin.ctrl);
35868a9ff04bSjmcneill 				break;
35878a9ff04bSjmcneill 			default:
35888a9ff04bSjmcneill 				break;
35898a9ff04bSjmcneill 			}
35908a9ff04bSjmcneill 		}
35918a9ff04bSjmcneill 	}
3592fdaf53a0Sriastradh }
35938a9ff04bSjmcneill 
3594fdaf53a0Sriastradh static void
hdafg_hp_switch_thread(void * opaque)3595fdaf53a0Sriastradh hdafg_hp_switch_thread(void *opaque)
3596fdaf53a0Sriastradh {
3597fdaf53a0Sriastradh 	struct hdafg_softc *sc = opaque;
3598fdaf53a0Sriastradh 
3599fdaf53a0Sriastradh 	KASSERT(sc->sc_jack_polling);
3600fdaf53a0Sriastradh 
3601fdaf53a0Sriastradh 	mutex_enter(&sc->sc_jack_lock);
3602fdaf53a0Sriastradh 	while (!sc->sc_jack_dying) {
3603fdaf53a0Sriastradh 		if (sc->sc_jack_suspended) {
3604fdaf53a0Sriastradh 			cv_wait(&sc->sc_jack_cv, &sc->sc_jack_lock);
3605fdaf53a0Sriastradh 			continue;
3606fdaf53a0Sriastradh 		}
3607fdaf53a0Sriastradh 		hdafg_hp_switch_handler(sc);
3608fdaf53a0Sriastradh 		(void)cv_timedwait(&sc->sc_jack_cv, &sc->sc_jack_lock,
3609fdaf53a0Sriastradh 		    HDAUDIO_HP_SENSE_PERIOD);
3610fdaf53a0Sriastradh 	}
3611fdaf53a0Sriastradh 	mutex_exit(&sc->sc_jack_lock);
3612fdaf53a0Sriastradh 
3613fdaf53a0Sriastradh 	kthread_exit(0);
36148a9ff04bSjmcneill }
36158a9ff04bSjmcneill 
36168a9ff04bSjmcneill static void
hdafg_hp_switch_init(struct hdafg_softc * sc)36178a9ff04bSjmcneill hdafg_hp_switch_init(struct hdafg_softc *sc)
36188a9ff04bSjmcneill {
36198a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
36208a9ff04bSjmcneill 	struct hdaudio_widget *w;
36218a9ff04bSjmcneill 	bool enable = false;
36228a9ff04bSjmcneill 	int i, j;
3623fdaf53a0Sriastradh 	int error;
36248a9ff04bSjmcneill 
36258a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
36268a9ff04bSjmcneill 		if (as[i].as_hpredir < 0 && as[i].as_displaydev == false)
36278a9ff04bSjmcneill 			continue;
36288a9ff04bSjmcneill 		if (as[i].as_displaydev == false)
36298a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_pins[15]);
36308a9ff04bSjmcneill 		else {
36318a9ff04bSjmcneill 			w = NULL;
36328a9ff04bSjmcneill 			for (j = 0; j < HDAUDIO_MAXPINS; j++) {
36338a9ff04bSjmcneill 				if (as[i].as_pins[j] == 0)
36348a9ff04bSjmcneill 					continue;
36358a9ff04bSjmcneill 				w = hdafg_widget_lookup(sc, as[i].as_pins[j]);
36368a9ff04bSjmcneill 				if (w && w->w_enable &&
36378a9ff04bSjmcneill 				    w->w_type == COP_AWCAP_TYPE_PIN_COMPLEX)
36388a9ff04bSjmcneill 					break;
36398a9ff04bSjmcneill 				w = NULL;
36408a9ff04bSjmcneill 			}
36418a9ff04bSjmcneill 		}
36428a9ff04bSjmcneill 		if (w == NULL || w->w_enable == false)
36438a9ff04bSjmcneill 			continue;
36448a9ff04bSjmcneill 		if (w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
36458a9ff04bSjmcneill 			continue;
36468a9ff04bSjmcneill 		if (!(w->w_pin.cap & COP_PINCAP_PRESENSE_DETECT_CAPABLE)) {
36478a9ff04bSjmcneill 			continue;
36488a9ff04bSjmcneill 		}
36498a9ff04bSjmcneill 		if (COP_CFG_MISC(w->w_pin.config) & 1) {
36508a9ff04bSjmcneill 			hda_trace(sc, "no presence detect on pin %02X\n",
36518a9ff04bSjmcneill 			    w->w_nid);
36528a9ff04bSjmcneill 			continue;
36538a9ff04bSjmcneill 		}
36548a9ff04bSjmcneill 		if ((w->w_pin.cap & (COP_PINCAP_HDMI|COP_PINCAP_DP)) == 0)
36558a9ff04bSjmcneill 			enable = true;
36568a9ff04bSjmcneill 
36578a9ff04bSjmcneill 		if (w->w_p.aw_cap & COP_AWCAP_UNSOL_CAPABLE) {
36588a9ff04bSjmcneill 			uint8_t val = COP_SET_UNSOLICITED_RESPONSE_ENABLE;
36598a9ff04bSjmcneill 			if (w->w_pin.cap & (COP_PINCAP_HDMI|COP_PINCAP_DP))
36608a9ff04bSjmcneill 				val |= HDAUDIO_UNSOLTAG_EVENT_DD;
36618a9ff04bSjmcneill 			else
36628a9ff04bSjmcneill 				val |= HDAUDIO_UNSOLTAG_EVENT_HP;
36638a9ff04bSjmcneill 
36648a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
36658a9ff04bSjmcneill 			    CORB_SET_UNSOLICITED_RESPONSE, val);
36668a9ff04bSjmcneill 
36678a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, w->w_nid,
36688a9ff04bSjmcneill 			    CORB_SET_AMPLIFIER_GAIN_MUTE, 0xb000);
36698a9ff04bSjmcneill 		}
36708a9ff04bSjmcneill 
36718a9ff04bSjmcneill 		hda_trace(sc, "presence detect [pin=%02X,%s",
36728a9ff04bSjmcneill 		    w->w_nid,
36738a9ff04bSjmcneill 		    (w->w_p.aw_cap & COP_AWCAP_UNSOL_CAPABLE) ?
36748a9ff04bSjmcneill 		     "unsol" : "poll"
36758a9ff04bSjmcneill 		    );
36768a9ff04bSjmcneill 		if (w->w_pin.cap & COP_PINCAP_HDMI)
36778a9ff04bSjmcneill 			hda_trace1(sc, ",hdmi");
36788a9ff04bSjmcneill 		if (w->w_pin.cap & COP_PINCAP_DP)
36798a9ff04bSjmcneill 			hda_trace1(sc, ",displayport");
36808a9ff04bSjmcneill 		hda_trace1(sc, "]\n");
36818a9ff04bSjmcneill 	}
3682fdaf53a0Sriastradh 	if (!enable) {
36838a9ff04bSjmcneill 		hda_trace(sc, "jack detect not enabled\n");
3684fdaf53a0Sriastradh 		return;
3685fdaf53a0Sriastradh 	}
3686fdaf53a0Sriastradh 
3687fdaf53a0Sriastradh 	mutex_init(&sc->sc_jack_lock, MUTEX_DEFAULT, IPL_NONE);
3688fdaf53a0Sriastradh 	cv_init(&sc->sc_jack_cv, "hdafghp");
3689fdaf53a0Sriastradh 	sc->sc_jack_polling = true;
3690fdaf53a0Sriastradh 	error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, /*ci*/NULL,
3691fdaf53a0Sriastradh 	    hdafg_hp_switch_thread, sc, &sc->sc_jack_thread,
3692fdaf53a0Sriastradh 	    "%s hotplug detect", device_xname(sc->sc_dev));
3693fdaf53a0Sriastradh 	if (error) {
3694fdaf53a0Sriastradh 		aprint_error_dev(sc->sc_dev, "failed to create hotplug thread:"
3695fdaf53a0Sriastradh 		    " %d", error);
3696fdaf53a0Sriastradh 		sc->sc_jack_polling = false;
3697fdaf53a0Sriastradh 		cv_destroy(&sc->sc_jack_cv);
3698fdaf53a0Sriastradh 		mutex_destroy(&sc->sc_jack_lock);
3699fdaf53a0Sriastradh 	}
37008a9ff04bSjmcneill }
37018a9ff04bSjmcneill 
37028a9ff04bSjmcneill static void
hdafg_attach(device_t parent,device_t self,void * opaque)37038a9ff04bSjmcneill hdafg_attach(device_t parent, device_t self, void *opaque)
37048a9ff04bSjmcneill {
37058a9ff04bSjmcneill 	struct hdafg_softc *sc = device_private(self);
37068a9ff04bSjmcneill 	audio_params_t defparams;
37078a9ff04bSjmcneill 	prop_dictionary_t args = opaque;
37088a9ff04bSjmcneill 	uint64_t fgptr = 0;
37098a9ff04bSjmcneill 	uint32_t astype = 0;
37108a9ff04bSjmcneill 	uint8_t nid = 0;
3711e622eac4Sisaki 	int i;
37128a9ff04bSjmcneill 	bool rv;
37138a9ff04bSjmcneill 
37148a9ff04bSjmcneill 	aprint_naive("\n");
37158a9ff04bSjmcneill 	sc->sc_dev = self;
37168a9ff04bSjmcneill 
37178a9ff04bSjmcneill 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
37188a9ff04bSjmcneill 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
37198a9ff04bSjmcneill 
37208a9ff04bSjmcneill 	if (!pmf_device_register(self, hdafg_suspend, hdafg_resume))
37218a9ff04bSjmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
37228a9ff04bSjmcneill 
37238a9ff04bSjmcneill 	sc->sc_config = prop_dictionary_get(args, "pin-config");
37248a9ff04bSjmcneill 	if (sc->sc_config && prop_object_type(sc->sc_config) != PROP_TYPE_ARRAY)
37258a9ff04bSjmcneill 		sc->sc_config = NULL;
37268a9ff04bSjmcneill 
37278a9ff04bSjmcneill 	prop_dictionary_get_uint16(args, "vendor-id", &sc->sc_vendor);
37288a9ff04bSjmcneill 	prop_dictionary_get_uint16(args, "product-id", &sc->sc_product);
37290519d6fcSmlelstv 	hdaudio_findvendor(sc->sc_name, sizeof(sc->sc_name), sc->sc_vendor);
37300519d6fcSmlelstv 	hdaudio_findproduct(sc->sc_version, sizeof(sc->sc_version), sc->sc_vendor,
37318a9ff04bSjmcneill 	    sc->sc_product);
37320519d6fcSmlelstv 	hda_print1(sc, ": %s %s%s\n", sc->sc_name, sc->sc_version,
37338a9ff04bSjmcneill 	    sc->sc_config ? " (custom configuration)" : "");
37348a9ff04bSjmcneill 
37358e864669Sjmcneill 	switch (sc->sc_vendor) {
37368e864669Sjmcneill 	case HDAUDIO_VENDOR_NVIDIA:
37378e864669Sjmcneill 		switch (sc->sc_product) {
37388e864669Sjmcneill 		case HDAUDIO_PRODUCT_NVIDIA_TEGRA124_HDMI:
37398e864669Sjmcneill 			sc->sc_fixed_rate = 44100;
3740a85a8464Sjmcneill 			sc->sc_disable_dip = true;
37418e864669Sjmcneill 			break;
37428e864669Sjmcneill 		}
37438e864669Sjmcneill 		break;
37448e864669Sjmcneill 	}
37458e864669Sjmcneill 
37468a9ff04bSjmcneill 	rv = prop_dictionary_get_uint64(args, "function-group", &fgptr);
37478a9ff04bSjmcneill 	if (rv == false || fgptr == 0) {
37488a9ff04bSjmcneill 		hda_error(sc, "missing function-group property\n");
37498a9ff04bSjmcneill 		return;
37508a9ff04bSjmcneill 	}
37518a9ff04bSjmcneill 	rv = prop_dictionary_get_uint8(args, "node-id", &nid);
37528a9ff04bSjmcneill 	if (rv == false || nid == 0) {
37538a9ff04bSjmcneill 		hda_error(sc, "missing node-id property\n");
37548a9ff04bSjmcneill 		return;
37558a9ff04bSjmcneill 	}
37568a9ff04bSjmcneill 
37578a9ff04bSjmcneill 	prop_dictionary_set_uint64(device_properties(self),
37588a9ff04bSjmcneill 	    "codecinfo-callback",
37598a9ff04bSjmcneill 	    (uint64_t)(uintptr_t)hdafg_codec_info);
37608a9ff04bSjmcneill 	prop_dictionary_set_uint64(device_properties(self),
37618a9ff04bSjmcneill 	    "widgetinfo-callback",
37628a9ff04bSjmcneill 	    (uint64_t)(uintptr_t)hdafg_widget_info);
37638a9ff04bSjmcneill 
37648a9ff04bSjmcneill 	sc->sc_nid = nid;
37658a9ff04bSjmcneill 	sc->sc_fg = (struct hdaudio_function_group *)(vaddr_t)fgptr;
37668a9ff04bSjmcneill 	sc->sc_fg->fg_unsol = hdafg_unsol;
37678a9ff04bSjmcneill 	sc->sc_codec = sc->sc_fg->fg_codec;
37688a9ff04bSjmcneill 	KASSERT(sc->sc_codec != NULL);
37698a9ff04bSjmcneill 	sc->sc_host = sc->sc_codec->co_host;
37708a9ff04bSjmcneill 	KASSERT(sc->sc_host != NULL);
37718a9ff04bSjmcneill 
37728a9ff04bSjmcneill 	hda_debug(sc, "parsing widgets\n");
37738a9ff04bSjmcneill 	hdafg_parse(sc);
37748a9ff04bSjmcneill 	hda_debug(sc, "parsing controls\n");
37758a9ff04bSjmcneill 	hdafg_control_parse(sc);
37768a9ff04bSjmcneill 	hda_debug(sc, "disabling non-audio devices\n");
37778a9ff04bSjmcneill 	hdafg_disable_nonaudio(sc);
37788a9ff04bSjmcneill 	hda_debug(sc, "disabling useless devices\n");
37798a9ff04bSjmcneill 	hdafg_disable_useless(sc);
37808a9ff04bSjmcneill 	hda_debug(sc, "parsing associations\n");
37818a9ff04bSjmcneill 	hdafg_assoc_parse(sc);
37828a9ff04bSjmcneill 	hda_debug(sc, "building tree\n");
37838a9ff04bSjmcneill 	hdafg_build_tree(sc);
37848a9ff04bSjmcneill 	hda_debug(sc, "disabling unassociated pins\n");
37858a9ff04bSjmcneill 	hdafg_disable_unassoc(sc);
37868a9ff04bSjmcneill 	hda_debug(sc, "disabling unselected pins\n");
37878a9ff04bSjmcneill 	hdafg_disable_unsel(sc);
37888a9ff04bSjmcneill 	hda_debug(sc, "disabling useless devices\n");
37898a9ff04bSjmcneill 	hdafg_disable_useless(sc);
37908a9ff04bSjmcneill 	hda_debug(sc, "disabling cross-associated pins\n");
37918a9ff04bSjmcneill 	hdafg_disable_crossassoc(sc);
37928a9ff04bSjmcneill 	hda_debug(sc, "disabling useless devices\n");
37938a9ff04bSjmcneill 	hdafg_disable_useless(sc);
37948a9ff04bSjmcneill 
37958a9ff04bSjmcneill 	hda_debug(sc, "assigning mixer names to sound sources\n");
37968a9ff04bSjmcneill 	hdafg_assign_names(sc);
37978a9ff04bSjmcneill 	hda_debug(sc, "assigning mixers to device tree\n");
37988a9ff04bSjmcneill 	hdafg_assign_mixers(sc);
37998a9ff04bSjmcneill 
38008a9ff04bSjmcneill 	hda_debug(sc, "preparing pin controls\n");
38018a9ff04bSjmcneill 	hdafg_prepare_pin_controls(sc);
38029f4a9600Sandvar 	hda_debug(sc, "committing settings\n");
38038a9ff04bSjmcneill 	hdafg_commit(sc);
38048a9ff04bSjmcneill 
38058a9ff04bSjmcneill 	hda_debug(sc, "setup jack sensing\n");
38068a9ff04bSjmcneill 	hdafg_hp_switch_init(sc);
38078a9ff04bSjmcneill 
38088a9ff04bSjmcneill 	hda_debug(sc, "building mixer controls\n");
38098a9ff04bSjmcneill 	hdafg_build_mixers(sc);
38108a9ff04bSjmcneill 
38118a9ff04bSjmcneill 	hdafg_dump(sc);
38128a9ff04bSjmcneill 	if (1) hdafg_widget_pin_dump(sc);
38138a9ff04bSjmcneill 	hdafg_assoc_dump(sc);
38148a9ff04bSjmcneill 
38158a9ff04bSjmcneill 	hda_debug(sc, "enabling analog beep\n");
38168a9ff04bSjmcneill 	hdafg_enable_analog_beep(sc);
38178a9ff04bSjmcneill 
38188a9ff04bSjmcneill 	hda_debug(sc, "configuring encodings\n");
38198a9ff04bSjmcneill 	sc->sc_audiodev.ad_sc = sc;
38208a9ff04bSjmcneill 	hdafg_configure_encodings(sc);
38218a9ff04bSjmcneill 
38228a9ff04bSjmcneill 	hda_debug(sc, "reserving streams\n");
38238a9ff04bSjmcneill 	sc->sc_audiodev.ad_capture = hdaudio_stream_establish(sc->sc_host,
38248a9ff04bSjmcneill 	    HDAUDIO_STREAM_ISS, hdafg_stream_intr, &sc->sc_audiodev);
38258a9ff04bSjmcneill 	sc->sc_audiodev.ad_playback = hdaudio_stream_establish(sc->sc_host,
38268a9ff04bSjmcneill 	    HDAUDIO_STREAM_OSS, hdafg_stream_intr, &sc->sc_audiodev);
38278a9ff04bSjmcneill 
382807452c5eSjmcneill 	if (sc->sc_audiodev.ad_capture == NULL &&
382907452c5eSjmcneill 	    sc->sc_audiodev.ad_playback == NULL) {
383007452c5eSjmcneill 		hda_error(sc, "couldn't find any input or output streams\n");
383107452c5eSjmcneill 		return;
383207452c5eSjmcneill 	}
383307452c5eSjmcneill 
38348a9ff04bSjmcneill 	hda_debug(sc, "connecting streams\n");
38358a9ff04bSjmcneill 	defparams.channels = 2;
38368e864669Sjmcneill 	defparams.sample_rate = sc->sc_fixed_rate ? sc->sc_fixed_rate : 48000;
38378a9ff04bSjmcneill 	defparams.precision = defparams.validbits = 16;
38388a9ff04bSjmcneill 	defparams.encoding = AUDIO_ENCODING_SLINEAR_LE;
38398a9ff04bSjmcneill 	sc->sc_pparam = sc->sc_rparam = defparams;
38408a9ff04bSjmcneill 	hdafg_stream_connect(sc, AUMODE_PLAY);
38418a9ff04bSjmcneill 	hdafg_stream_connect(sc, AUMODE_RECORD);
38428a9ff04bSjmcneill 
38438a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
38448a9ff04bSjmcneill 		astype |= (1 << sc->sc_assocs[i].as_digital);
38458a9ff04bSjmcneill 	}
38468a9ff04bSjmcneill 	hda_debug(sc, "assoc type mask: %x\n", astype);
38478a9ff04bSjmcneill 
38488a9ff04bSjmcneill 	if (astype == 0)
38498a9ff04bSjmcneill 		return;
38508a9ff04bSjmcneill 
38518a9ff04bSjmcneill 	hda_debug(sc, "attaching audio device\n");
38528a9ff04bSjmcneill 	sc->sc_audiodev.ad_audiodev = audio_attach_mi(&hdafg_hw_if,
38538a9ff04bSjmcneill 	    &sc->sc_audiodev, self);
38548a9ff04bSjmcneill }
38558a9ff04bSjmcneill 
38568a9ff04bSjmcneill static int
hdafg_detach(device_t self,int flags)38578a9ff04bSjmcneill hdafg_detach(device_t self, int flags)
38588a9ff04bSjmcneill {
38598a9ff04bSjmcneill 	struct hdafg_softc *sc = device_private(self);
38608a9ff04bSjmcneill 	struct hdaudio_widget *wl, *w = sc->sc_widgets;
38618a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
38628a9ff04bSjmcneill 	struct hdaudio_control *ctl = sc->sc_ctls;
38638a9ff04bSjmcneill 	struct hdaudio_mixer *mx = sc->sc_mixers;
38648a9ff04bSjmcneill 	int nid;
38658a9ff04bSjmcneill 
3866fdaf53a0Sriastradh 	if (sc->sc_jack_polling) {
3867fdaf53a0Sriastradh 		int error __diagused;
3868fdaf53a0Sriastradh 
3869fdaf53a0Sriastradh 		mutex_enter(&sc->sc_jack_lock);
3870fdaf53a0Sriastradh 		sc->sc_jack_dying = true;
3871fdaf53a0Sriastradh 		cv_broadcast(&sc->sc_jack_cv);
3872fdaf53a0Sriastradh 		mutex_exit(&sc->sc_jack_lock);
3873fdaf53a0Sriastradh 		error = kthread_join(sc->sc_jack_thread);
3874fdaf53a0Sriastradh 		KASSERTMSG(error == 0, "error=%d", error);
3875fdaf53a0Sriastradh 	}
38768a9ff04bSjmcneill 
38778a9ff04bSjmcneill 	if (sc->sc_config)
38788a9ff04bSjmcneill 		prop_object_release(sc->sc_config);
38798a9ff04bSjmcneill 	if (sc->sc_audiodev.ad_audiodev)
38808a9ff04bSjmcneill 		config_detach(sc->sc_audiodev.ad_audiodev, flags);
38818a9ff04bSjmcneill 	if (sc->sc_audiodev.ad_playback)
38828a9ff04bSjmcneill 		hdaudio_stream_disestablish(sc->sc_audiodev.ad_playback);
38838a9ff04bSjmcneill 	if (sc->sc_audiodev.ad_capture)
38848a9ff04bSjmcneill 		hdaudio_stream_disestablish(sc->sc_audiodev.ad_capture);
38858a9ff04bSjmcneill 
38868a9ff04bSjmcneill 	/* restore bios pin widget configuration */
38878a9ff04bSjmcneill 	for (nid = sc->sc_startnode; nid < sc->sc_endnode; nid++) {
38888a9ff04bSjmcneill 		wl = hdafg_widget_lookup(sc, nid);
38898a9ff04bSjmcneill 		if (wl == NULL || wl->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
38908a9ff04bSjmcneill 			continue;
38918a9ff04bSjmcneill 		hdafg_widget_setconfig(wl, wl->w_pin.biosconfig);
38928a9ff04bSjmcneill 	}
38938a9ff04bSjmcneill 
38948a9ff04bSjmcneill 	if (w)
38958a9ff04bSjmcneill 		kmem_free(w, sc->sc_nwidgets * sizeof(*w));
38968a9ff04bSjmcneill 	if (as)
38978a9ff04bSjmcneill 		kmem_free(as, sc->sc_nassocs * sizeof(*as));
38988a9ff04bSjmcneill 	if (ctl)
38998a9ff04bSjmcneill 		kmem_free(ctl, sc->sc_nctls * sizeof(*ctl));
39008a9ff04bSjmcneill 	if (mx)
39018a9ff04bSjmcneill 		kmem_free(mx, sc->sc_nmixers * sizeof(*mx));
39028a9ff04bSjmcneill 
39038a9ff04bSjmcneill 	mutex_destroy(&sc->sc_lock);
39048a9ff04bSjmcneill 	mutex_destroy(&sc->sc_intr_lock);
39058a9ff04bSjmcneill 
39068a9ff04bSjmcneill 	pmf_device_deregister(self);
39078a9ff04bSjmcneill 
39088a9ff04bSjmcneill 	return 0;
39098a9ff04bSjmcneill }
39108a9ff04bSjmcneill 
39118a9ff04bSjmcneill static void
hdafg_childdet(device_t self,device_t child)39128a9ff04bSjmcneill hdafg_childdet(device_t self, device_t child)
39138a9ff04bSjmcneill {
39148a9ff04bSjmcneill 	struct hdafg_softc *sc = device_private(self);
39158a9ff04bSjmcneill 
39168a9ff04bSjmcneill 	if (child == sc->sc_audiodev.ad_audiodev)
39178a9ff04bSjmcneill 		sc->sc_audiodev.ad_audiodev = NULL;
39188a9ff04bSjmcneill }
39198a9ff04bSjmcneill 
39208a9ff04bSjmcneill static bool
hdafg_suspend(device_t self,const pmf_qual_t * qual)39218a9ff04bSjmcneill hdafg_suspend(device_t self, const pmf_qual_t *qual)
39228a9ff04bSjmcneill {
39238a9ff04bSjmcneill 	struct hdafg_softc *sc = device_private(self);
39248a9ff04bSjmcneill 
3925fdaf53a0Sriastradh 	if (sc->sc_jack_polling) {
3926fdaf53a0Sriastradh 		mutex_enter(&sc->sc_jack_lock);
3927fdaf53a0Sriastradh 		KASSERT(!sc->sc_jack_suspended);
3928fdaf53a0Sriastradh 		sc->sc_jack_suspended = true;
3929fdaf53a0Sriastradh 		mutex_exit(&sc->sc_jack_lock);
3930fdaf53a0Sriastradh 	}
39318a9ff04bSjmcneill 
39328a9ff04bSjmcneill 	return true;
39338a9ff04bSjmcneill }
39348a9ff04bSjmcneill 
39358a9ff04bSjmcneill static bool
hdafg_resume(device_t self,const pmf_qual_t * qual)39368a9ff04bSjmcneill hdafg_resume(device_t self, const pmf_qual_t *qual)
39378a9ff04bSjmcneill {
39388a9ff04bSjmcneill 	struct hdafg_softc *sc = device_private(self);
39398a9ff04bSjmcneill 	struct hdaudio_widget *w;
39408a9ff04bSjmcneill 	int nid;
39418a9ff04bSjmcneill 
39428a9ff04bSjmcneill 	hdaudio_command(sc->sc_codec, sc->sc_nid,
39438a9ff04bSjmcneill 	    CORB_SET_POWER_STATE, COP_POWER_STATE_D0);
39448a9ff04bSjmcneill 	hda_delay(100);
39458a9ff04bSjmcneill 	for (nid = sc->sc_startnode; nid < sc->sc_endnode; nid++) {
39468a9ff04bSjmcneill 		hdaudio_command(sc->sc_codec, nid,
39478a9ff04bSjmcneill 		    CORB_SET_POWER_STATE, COP_POWER_STATE_D0);
39488a9ff04bSjmcneill 		w = hdafg_widget_lookup(sc, nid);
39498a9ff04bSjmcneill 
39508a9ff04bSjmcneill 		/* restore pin widget configuration */
39518a9ff04bSjmcneill 		if (w == NULL || w->w_type != COP_AWCAP_TYPE_PIN_COMPLEX)
39528a9ff04bSjmcneill 			continue;
39538a9ff04bSjmcneill 		hdafg_widget_setconfig(w, w->w_pin.config);
39548a9ff04bSjmcneill 	}
39558a9ff04bSjmcneill 	hda_delay(1000);
39568a9ff04bSjmcneill 
39578a9ff04bSjmcneill 	hdafg_commit(sc);
39588a9ff04bSjmcneill 	hdafg_stream_connect(sc, AUMODE_PLAY);
39598a9ff04bSjmcneill 	hdafg_stream_connect(sc, AUMODE_RECORD);
39608a9ff04bSjmcneill 
3961fdaf53a0Sriastradh 	if (sc->sc_jack_polling) {
3962fdaf53a0Sriastradh 		mutex_enter(&sc->sc_jack_lock);
3963fdaf53a0Sriastradh 		KASSERT(sc->sc_jack_suspended);
3964fdaf53a0Sriastradh 		sc->sc_jack_suspended = false;
3965fdaf53a0Sriastradh 		cv_broadcast(&sc->sc_jack_cv);
3966fdaf53a0Sriastradh 		mutex_exit(&sc->sc_jack_lock);
3967fdaf53a0Sriastradh 	}
39688a9ff04bSjmcneill 
39698a9ff04bSjmcneill 	return true;
39708a9ff04bSjmcneill }
39718a9ff04bSjmcneill 
39728a9ff04bSjmcneill static int
hdafg_query_format(void * opaque,audio_format_query_t * afp)3973e622eac4Sisaki hdafg_query_format(void *opaque, audio_format_query_t *afp)
39748a9ff04bSjmcneill {
39758a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
3976e622eac4Sisaki 
3977e622eac4Sisaki 	return audio_query_format(ad->ad_formats, ad->ad_nformats, afp);
39788a9ff04bSjmcneill }
39798a9ff04bSjmcneill 
39808a9ff04bSjmcneill static int
hdafg_set_format(void * opaque,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)3981e622eac4Sisaki hdafg_set_format(void *opaque, int setmode,
3982e622eac4Sisaki     const audio_params_t *play, const audio_params_t *rec,
3983e622eac4Sisaki     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
39848a9ff04bSjmcneill {
39858a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
39868a9ff04bSjmcneill 
39878a9ff04bSjmcneill 	if (play && (setmode & AUMODE_PLAY)) {
3988e622eac4Sisaki 		ad->ad_sc->sc_pparam = *play;
39898a9ff04bSjmcneill 		hdafg_stream_connect(ad->ad_sc, AUMODE_PLAY);
39908a9ff04bSjmcneill 	}
39918a9ff04bSjmcneill 	if (rec && (setmode & AUMODE_RECORD)) {
3992e622eac4Sisaki 		ad->ad_sc->sc_rparam = *rec;
39938a9ff04bSjmcneill 		hdafg_stream_connect(ad->ad_sc, AUMODE_RECORD);
39948a9ff04bSjmcneill 	}
39958a9ff04bSjmcneill 	return 0;
39968a9ff04bSjmcneill }
39978a9ff04bSjmcneill 
39985820741cSisaki /* LCM for round_blocksize */
39995820741cSisaki static u_int gcd(u_int, u_int);
40005820741cSisaki static u_int lcm(u_int, u_int);
40015820741cSisaki 
gcd(u_int a,u_int b)40025820741cSisaki static u_int gcd(u_int a, u_int b)
40035820741cSisaki {
40045820741cSisaki 
40055820741cSisaki 	return (b == 0) ? a : gcd(b, a % b);
40065820741cSisaki }
lcm(u_int a,u_int b)40075820741cSisaki static u_int lcm(u_int a, u_int b)
40085820741cSisaki {
40095820741cSisaki 
40105820741cSisaki 	return a * b / gcd(a, b);
40115820741cSisaki }
40125820741cSisaki 
40138a9ff04bSjmcneill static int
hdafg_round_blocksize(void * opaque,int blksize,int mode,const audio_params_t * param)40148a9ff04bSjmcneill hdafg_round_blocksize(void *opaque, int blksize, int mode,
40158a9ff04bSjmcneill     const audio_params_t *param)
40168a9ff04bSjmcneill {
40178a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
40188a9ff04bSjmcneill 	struct hdaudio_stream *st;
40195820741cSisaki 	u_int minblksize;
4020e622eac4Sisaki 	int bufsize;
40218a9ff04bSjmcneill 
40228a9ff04bSjmcneill 	st = (mode == AUMODE_PLAY) ? ad->ad_playback : ad->ad_capture;
40238a9ff04bSjmcneill 	if (st == NULL) {
40248a9ff04bSjmcneill 		hda_trace(ad->ad_sc,
40258a9ff04bSjmcneill 		    "round_blocksize called for invalid stream\n");
40268a9ff04bSjmcneill 		return 128;
40278a9ff04bSjmcneill 	}
40288a9ff04bSjmcneill 
40295820741cSisaki 	if (blksize > 8192)
40305820741cSisaki 		blksize = 8192;
40315820741cSisaki 
40325820741cSisaki 	/* Make sure there are enough BDL descriptors */
40335820741cSisaki 	bufsize = st->st_data.dma_size;
40345820741cSisaki 	if (bufsize > HDAUDIO_BDL_MAX * blksize) {
40355820741cSisaki 		blksize = bufsize / HDAUDIO_BDL_MAX;
40365820741cSisaki 	}
40375820741cSisaki 
4038e622eac4Sisaki 	/*
4039e622eac4Sisaki 	 * HD audio's buffer constraint looks like following:
4040e622eac4Sisaki 	 * - The buffer MUST start on a 128bytes boundary.
4041e622eac4Sisaki 	 * - The buffer size MUST be one sample or more.
4042e622eac4Sisaki 	 * - The buffer size is preferred multiple of 128bytes for efficiency.
4043e622eac4Sisaki 	 *
4044e622eac4Sisaki 	 * https://www.intel.co.jp/content/www/jp/ja/standards/high-definition-audio-specification.html , p70.
40455820741cSisaki 	 *
40465820741cSisaki 	 * Also, the audio layer requires that the blocksize must be a
40475820741cSisaki 	 * multiple of the number of channels.
4048e622eac4Sisaki 	 */
40495820741cSisaki 	minblksize = lcm(128, param->channels);
40505820741cSisaki 	blksize = rounddown(blksize, minblksize);
40515820741cSisaki 	if (blksize < minblksize)
40525820741cSisaki 		blksize = minblksize;
40538a9ff04bSjmcneill 
4054e622eac4Sisaki 	return blksize;
40558a9ff04bSjmcneill }
40568a9ff04bSjmcneill 
40578a9ff04bSjmcneill static int
hdafg_commit_settings(void * opaque)40588a9ff04bSjmcneill hdafg_commit_settings(void *opaque)
40598a9ff04bSjmcneill {
40608a9ff04bSjmcneill 	return 0;
40618a9ff04bSjmcneill }
40628a9ff04bSjmcneill 
40638a9ff04bSjmcneill static int
hdafg_halt_output(void * opaque)40648a9ff04bSjmcneill hdafg_halt_output(void *opaque)
40658a9ff04bSjmcneill {
40668a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
40678a9ff04bSjmcneill 	struct hdafg_softc *sc = ad->ad_sc;
40688a9ff04bSjmcneill 	struct hdaudio_assoc *as = ad->ad_sc->sc_assocs;
40698a9ff04bSjmcneill 	struct hdaudio_widget *w;
40708a9ff04bSjmcneill 	uint16_t dfmt;
40718a9ff04bSjmcneill 	int i, j;
40728a9ff04bSjmcneill 
40738a9ff04bSjmcneill 	/* Disable digital outputs */
40748a9ff04bSjmcneill 	for (i = 0; i < sc->sc_nassocs; i++) {
40758a9ff04bSjmcneill 		if (as[i].as_enable == false)
40768a9ff04bSjmcneill 			continue;
40778a9ff04bSjmcneill 		if (as[i].as_dir != HDAUDIO_PINDIR_OUT)
40788a9ff04bSjmcneill 			continue;
40798a9ff04bSjmcneill 		for (j = 0; j < HDAUDIO_MAXPINS; j++) {
40808a9ff04bSjmcneill 			if (as[i].as_dacs[j] == 0)
40818a9ff04bSjmcneill 				continue;
40828a9ff04bSjmcneill 			w = hdafg_widget_lookup(sc, as[i].as_dacs[j]);
40838a9ff04bSjmcneill 			if (w == NULL || w->w_enable == false)
40848a9ff04bSjmcneill 				continue;
40858a9ff04bSjmcneill 			if (w->w_p.aw_cap & COP_AWCAP_DIGITAL) {
40868a9ff04bSjmcneill 				dfmt = hdaudio_command(sc->sc_codec, w->w_nid,
40878a9ff04bSjmcneill 				    CORB_GET_DIGITAL_CONVERTER_CONTROL, 0) &
40888a9ff04bSjmcneill 				    0xff;
40898a9ff04bSjmcneill 				dfmt &= ~COP_DIGITAL_CONVCTRL1_DIGEN;
40908a9ff04bSjmcneill 				hdaudio_command(sc->sc_codec, w->w_nid,
40918a9ff04bSjmcneill 				    CORB_SET_DIGITAL_CONVERTER_CONTROL_1, dfmt);
40928a9ff04bSjmcneill 			}
40938a9ff04bSjmcneill 		}
40948a9ff04bSjmcneill 	}
40958a9ff04bSjmcneill 
40968a9ff04bSjmcneill 	hdaudio_stream_stop(ad->ad_playback);
40978a9ff04bSjmcneill 
40988a9ff04bSjmcneill 	return 0;
40998a9ff04bSjmcneill }
41008a9ff04bSjmcneill 
41018a9ff04bSjmcneill static int
hdafg_halt_input(void * opaque)41028a9ff04bSjmcneill hdafg_halt_input(void *opaque)
41038a9ff04bSjmcneill {
41048a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
41058a9ff04bSjmcneill 
41068a9ff04bSjmcneill 	hdaudio_stream_stop(ad->ad_capture);
41078a9ff04bSjmcneill 
41088a9ff04bSjmcneill 	return 0;
41098a9ff04bSjmcneill }
41108a9ff04bSjmcneill 
41118a9ff04bSjmcneill static int
hdafg_getdev(void * opaque,struct audio_device * audiodev)41128a9ff04bSjmcneill hdafg_getdev(void *opaque, struct audio_device *audiodev)
41138a9ff04bSjmcneill {
41148a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
41158a9ff04bSjmcneill 	struct hdafg_softc *sc = ad->ad_sc;
41168a9ff04bSjmcneill 
41170519d6fcSmlelstv 	memcpy(audiodev->name, sc->sc_name, sizeof(audiodev->name));
41180519d6fcSmlelstv 	memcpy(audiodev->version, sc->sc_version, sizeof(audiodev->version));
4119db8ce746Skhorben 	snprintf(audiodev->config, sizeof(audiodev->config),
41208a9ff04bSjmcneill 	    "%02Xh", sc->sc_nid);
41218a9ff04bSjmcneill 
41228a9ff04bSjmcneill 	return 0;
41238a9ff04bSjmcneill }
41248a9ff04bSjmcneill 
41258a9ff04bSjmcneill static int
hdafg_set_port(void * opaque,mixer_ctrl_t * mc)41268a9ff04bSjmcneill hdafg_set_port(void *opaque, mixer_ctrl_t *mc)
41278a9ff04bSjmcneill {
41288a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
41298a9ff04bSjmcneill 	struct hdafg_softc *sc = ad->ad_sc;
41308a9ff04bSjmcneill 	struct hdaudio_mixer *mx;
41318a9ff04bSjmcneill 	struct hdaudio_control *ctl;
41328a9ff04bSjmcneill 	int i, divisor;
41338a9ff04bSjmcneill 
41348a9ff04bSjmcneill 	if (mc->dev < 0 || mc->dev >= sc->sc_nmixers)
41358a9ff04bSjmcneill 		return EINVAL;
41368a9ff04bSjmcneill 	mx = &sc->sc_mixers[mc->dev];
41378a9ff04bSjmcneill 	ctl = mx->mx_ctl;
41388a9ff04bSjmcneill 	if (ctl == NULL) {
41398a9ff04bSjmcneill 		if (mx->mx_di.type != AUDIO_MIXER_SET)
41408a9ff04bSjmcneill 			return ENXIO;
41418a9ff04bSjmcneill 		if (mx->mx_di.mixer_class != HDAUDIO_MIXER_CLASS_OUTPUTS &&
41428a9ff04bSjmcneill 		    mx->mx_di.mixer_class != HDAUDIO_MIXER_CLASS_RECORD)
41438a9ff04bSjmcneill 			return ENXIO;
41448a9ff04bSjmcneill 		for (i = 0; i < sc->sc_nassocs; i++) {
41458a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_dir != HDAUDIO_PINDIR_OUT &&
41468a9ff04bSjmcneill 			    mx->mx_di.mixer_class ==
41478a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_OUTPUTS)
41488a9ff04bSjmcneill 				continue;
41498a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_dir != HDAUDIO_PINDIR_IN &&
41508a9ff04bSjmcneill 			    mx->mx_di.mixer_class ==
41518a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_RECORD)
41528a9ff04bSjmcneill 				continue;
41538a9ff04bSjmcneill 			sc->sc_assocs[i].as_activated =
41548a9ff04bSjmcneill 			    (mc->un.mask & (1 << i)) ? true : false;
41558a9ff04bSjmcneill 		}
41568a9ff04bSjmcneill 		hdafg_stream_connect(ad->ad_sc,
41578a9ff04bSjmcneill 		    mx->mx_di.mixer_class == HDAUDIO_MIXER_CLASS_OUTPUTS ?
41588a9ff04bSjmcneill 		    AUMODE_PLAY : AUMODE_RECORD);
41598a9ff04bSjmcneill 		return 0;
41608a9ff04bSjmcneill 	}
41618a9ff04bSjmcneill 
41628a9ff04bSjmcneill 	switch (mx->mx_di.type) {
41638a9ff04bSjmcneill 	case AUDIO_MIXER_VALUE:
41648a9ff04bSjmcneill 		if (ctl->ctl_step == 0)
41658a9ff04bSjmcneill 			divisor = 128; /* ??? - just avoid div by 0 */
41668a9ff04bSjmcneill 		else
41678a9ff04bSjmcneill 			divisor = 255 / ctl->ctl_step;
41688a9ff04bSjmcneill 
41698a9ff04bSjmcneill 		hdafg_control_amp_set(ctl, HDAUDIO_AMP_MUTE_NONE,
41708a9ff04bSjmcneill 		  mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] / divisor,
41718a9ff04bSjmcneill 		  mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] / divisor);
41728a9ff04bSjmcneill 		break;
41738a9ff04bSjmcneill 	case AUDIO_MIXER_ENUM:
41748a9ff04bSjmcneill 		hdafg_control_amp_set(ctl,
41758a9ff04bSjmcneill 		    mc->un.ord ? HDAUDIO_AMP_MUTE_ALL : HDAUDIO_AMP_MUTE_NONE,
41768a9ff04bSjmcneill 		    ctl->ctl_left, ctl->ctl_right);
41778a9ff04bSjmcneill 		break;
41788a9ff04bSjmcneill 	default:
41798a9ff04bSjmcneill 		return ENXIO;
41808a9ff04bSjmcneill 	}
41818a9ff04bSjmcneill 
41828a9ff04bSjmcneill 	return 0;
41838a9ff04bSjmcneill }
41848a9ff04bSjmcneill 
41858a9ff04bSjmcneill static int
hdafg_get_port(void * opaque,mixer_ctrl_t * mc)41868a9ff04bSjmcneill hdafg_get_port(void *opaque, mixer_ctrl_t *mc)
41878a9ff04bSjmcneill {
41888a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
41898a9ff04bSjmcneill 	struct hdafg_softc *sc = ad->ad_sc;
41908a9ff04bSjmcneill 	struct hdaudio_mixer *mx;
41918a9ff04bSjmcneill 	struct hdaudio_control *ctl;
41928a9ff04bSjmcneill 	u_int mask = 0;
41938a9ff04bSjmcneill 	int i, factor;
41948a9ff04bSjmcneill 
41958a9ff04bSjmcneill 	if (mc->dev < 0 || mc->dev >= sc->sc_nmixers)
41968a9ff04bSjmcneill 		return EINVAL;
41978a9ff04bSjmcneill 	mx = &sc->sc_mixers[mc->dev];
41988a9ff04bSjmcneill 	ctl = mx->mx_ctl;
41998a9ff04bSjmcneill 	if (ctl == NULL) {
42008a9ff04bSjmcneill 		if (mx->mx_di.type != AUDIO_MIXER_SET)
42018a9ff04bSjmcneill 			return ENXIO;
42028a9ff04bSjmcneill 		if (mx->mx_di.mixer_class != HDAUDIO_MIXER_CLASS_OUTPUTS &&
42038a9ff04bSjmcneill 		    mx->mx_di.mixer_class != HDAUDIO_MIXER_CLASS_RECORD)
42048a9ff04bSjmcneill 			return ENXIO;
42058a9ff04bSjmcneill 		for (i = 0; i < sc->sc_nassocs; i++) {
42068a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_enable == false)
42078a9ff04bSjmcneill 				continue;
42088a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_activated == false)
42098a9ff04bSjmcneill 				continue;
42108a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_dir == HDAUDIO_PINDIR_OUT &&
42118a9ff04bSjmcneill 			    mx->mx_di.mixer_class ==
42128a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_OUTPUTS)
42138a9ff04bSjmcneill 				mask |= (1 << i);
42148a9ff04bSjmcneill 			if (sc->sc_assocs[i].as_dir == HDAUDIO_PINDIR_IN &&
42158a9ff04bSjmcneill 			    mx->mx_di.mixer_class ==
42168a9ff04bSjmcneill 			    HDAUDIO_MIXER_CLASS_RECORD)
42178a9ff04bSjmcneill 				mask |= (1 << i);
42188a9ff04bSjmcneill 		}
42198a9ff04bSjmcneill 		mc->un.mask = mask;
42208a9ff04bSjmcneill 		return 0;
42218a9ff04bSjmcneill 	}
42228a9ff04bSjmcneill 
42238a9ff04bSjmcneill 	switch (mx->mx_di.type) {
42248a9ff04bSjmcneill 	case AUDIO_MIXER_VALUE:
42258a9ff04bSjmcneill 		if (ctl->ctl_step == 0)
42268a9ff04bSjmcneill 			factor = 128; /* ??? - just avoid div by 0 */
42278a9ff04bSjmcneill 		else
42288a9ff04bSjmcneill 			factor = 255 / ctl->ctl_step;
42298a9ff04bSjmcneill 
42308a9ff04bSjmcneill 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = ctl->ctl_left * factor;
42318a9ff04bSjmcneill 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = ctl->ctl_right * factor;
42328a9ff04bSjmcneill 		break;
42338a9ff04bSjmcneill 	case AUDIO_MIXER_ENUM:
42348a9ff04bSjmcneill 		mc->un.ord = (ctl->ctl_muted || ctl->ctl_forcemute) ? 1 : 0;
42358a9ff04bSjmcneill 		break;
42368a9ff04bSjmcneill 	default:
42378a9ff04bSjmcneill 		return ENXIO;
42388a9ff04bSjmcneill 	}
42398a9ff04bSjmcneill 	return 0;
42408a9ff04bSjmcneill }
42418a9ff04bSjmcneill 
42428a9ff04bSjmcneill static int
hdafg_query_devinfo(void * opaque,mixer_devinfo_t * di)42438a9ff04bSjmcneill hdafg_query_devinfo(void *opaque, mixer_devinfo_t *di)
42448a9ff04bSjmcneill {
42458a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
42468a9ff04bSjmcneill 	struct hdafg_softc *sc = ad->ad_sc;
42478a9ff04bSjmcneill 
42488a9ff04bSjmcneill 	if (di->index < 0 || di->index >= sc->sc_nmixers)
42498a9ff04bSjmcneill 		return ENXIO;
42508a9ff04bSjmcneill 
42518a9ff04bSjmcneill 	*di = sc->sc_mixers[di->index].mx_di;
42528a9ff04bSjmcneill 
42538a9ff04bSjmcneill 	return 0;
42548a9ff04bSjmcneill }
42558a9ff04bSjmcneill 
42568a9ff04bSjmcneill static void *
hdafg_allocm(void * opaque,int direction,size_t size)42578a9ff04bSjmcneill hdafg_allocm(void *opaque, int direction, size_t size)
42588a9ff04bSjmcneill {
42598a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
42608a9ff04bSjmcneill 	struct hdaudio_stream *st;
42618a9ff04bSjmcneill 	int err;
42628a9ff04bSjmcneill 
42638a9ff04bSjmcneill 	st = (direction == AUMODE_PLAY) ? ad->ad_playback : ad->ad_capture;
42648a9ff04bSjmcneill 	if (st == NULL)
42658a9ff04bSjmcneill 		return NULL;
42668a9ff04bSjmcneill 
42678a9ff04bSjmcneill 	if (st->st_data.dma_valid == true)
42688a9ff04bSjmcneill 		hda_error(ad->ad_sc, "WARNING: allocm leak\n");
42698a9ff04bSjmcneill 
42708a9ff04bSjmcneill 	st->st_data.dma_size = size;
42718a9ff04bSjmcneill 	err = hdaudio_dma_alloc(st->st_host, &st->st_data,
42728a9ff04bSjmcneill 	    BUS_DMA_COHERENT | BUS_DMA_NOCACHE);
42738a9ff04bSjmcneill 	if (err || st->st_data.dma_valid == false)
42748a9ff04bSjmcneill 		return NULL;
42758a9ff04bSjmcneill 
42768a9ff04bSjmcneill 	return DMA_KERNADDR(&st->st_data);
42778a9ff04bSjmcneill }
42788a9ff04bSjmcneill 
42798a9ff04bSjmcneill static void
hdafg_freem(void * opaque,void * addr,size_t size)42808a9ff04bSjmcneill hdafg_freem(void *opaque, void *addr, size_t size)
42818a9ff04bSjmcneill {
42828a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
42838a9ff04bSjmcneill 	struct hdaudio_stream *st;
42848a9ff04bSjmcneill 
4285*5669537fSriastradh 	if (ad->ad_playback != NULL &&
4286*5669537fSriastradh 	    addr == DMA_KERNADDR(&ad->ad_playback->st_data))
42878a9ff04bSjmcneill 		st = ad->ad_playback;
4288*5669537fSriastradh 	else if (ad->ad_capture != NULL &&
4289*5669537fSriastradh 	    addr == DMA_KERNADDR(&ad->ad_capture->st_data))
42908a9ff04bSjmcneill 		st = ad->ad_capture;
42918a9ff04bSjmcneill 	else
4292add1b63fSriastradh 		panic("bad hdafg hwbuf mem: %p (%zu bytes)", addr, size);
42938a9ff04bSjmcneill 
42948a9ff04bSjmcneill 	hdaudio_dma_free(st->st_host, &st->st_data);
42958a9ff04bSjmcneill }
42968a9ff04bSjmcneill 
42978a9ff04bSjmcneill static int
hdafg_get_props(void * opaque)42988a9ff04bSjmcneill hdafg_get_props(void *opaque)
42998a9ff04bSjmcneill {
43008a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
4301ede47d01Sisaki 	int props = 0;
43028a9ff04bSjmcneill 
43038a9ff04bSjmcneill 	if (ad->ad_playback)
43048a9ff04bSjmcneill 		props |= AUDIO_PROP_PLAYBACK;
43058a9ff04bSjmcneill 	if (ad->ad_capture)
43068a9ff04bSjmcneill 		props |= AUDIO_PROP_CAPTURE;
43078a9ff04bSjmcneill 	if (ad->ad_playback && ad->ad_capture) {
43088a9ff04bSjmcneill 		props |= AUDIO_PROP_FULLDUPLEX;
43098a9ff04bSjmcneill 		props |= AUDIO_PROP_INDEPENDENT;
43108a9ff04bSjmcneill 	}
43118a9ff04bSjmcneill 
43128a9ff04bSjmcneill 	return props;
43138a9ff04bSjmcneill }
43148a9ff04bSjmcneill 
43158a9ff04bSjmcneill static int
hdafg_trigger_output(void * opaque,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * param)43168a9ff04bSjmcneill hdafg_trigger_output(void *opaque, void *start, void *end, int blksize,
43178a9ff04bSjmcneill     void (*intr)(void *), void *intrarg, const audio_params_t *param)
43188a9ff04bSjmcneill {
43198a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
43208a9ff04bSjmcneill 	bus_size_t dmasize;
43218a9ff04bSjmcneill 
43228a9ff04bSjmcneill 	if (ad->ad_playback == NULL)
43238a9ff04bSjmcneill 		return ENXIO;
43248a9ff04bSjmcneill 	if (ad->ad_playback->st_data.dma_valid == false)
43258a9ff04bSjmcneill 		return ENOMEM;
43268a9ff04bSjmcneill 
43278a9ff04bSjmcneill 	ad->ad_playbackintr = intr;
43288a9ff04bSjmcneill 	ad->ad_playbackintrarg = intrarg;
43298a9ff04bSjmcneill 
43308a9ff04bSjmcneill 	dmasize = (char *)end - (char *)start;
43318a9ff04bSjmcneill 	hdafg_stream_connect(ad->ad_sc, AUMODE_PLAY);
43328e864669Sjmcneill 	hdaudio_stream_start(ad->ad_playback, blksize, dmasize,
43338e864669Sjmcneill 	    &ad->ad_sc->sc_pparam);
43348a9ff04bSjmcneill 
43358a9ff04bSjmcneill 	return 0;
43368a9ff04bSjmcneill }
43378a9ff04bSjmcneill 
43388a9ff04bSjmcneill static int
hdafg_trigger_input(void * opaque,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * param)43398a9ff04bSjmcneill hdafg_trigger_input(void *opaque, void *start, void *end, int blksize,
43408a9ff04bSjmcneill     void (*intr)(void *), void *intrarg, const audio_params_t *param)
43418a9ff04bSjmcneill {
43428a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
43438a9ff04bSjmcneill 	bus_size_t dmasize;
43448a9ff04bSjmcneill 
43458a9ff04bSjmcneill 	if (ad->ad_capture == NULL)
43468a9ff04bSjmcneill 		return ENXIO;
43478a9ff04bSjmcneill 	if (ad->ad_capture->st_data.dma_valid == false)
43488a9ff04bSjmcneill 		return ENOMEM;
43498a9ff04bSjmcneill 
43508a9ff04bSjmcneill 	ad->ad_captureintr = intr;
43518a9ff04bSjmcneill 	ad->ad_captureintrarg = intrarg;
43528a9ff04bSjmcneill 
43538a9ff04bSjmcneill 	dmasize = (char *)end - (char *)start;
43548a9ff04bSjmcneill 	hdafg_stream_connect(ad->ad_sc, AUMODE_RECORD);
43558e864669Sjmcneill 	hdaudio_stream_start(ad->ad_capture, blksize, dmasize,
43568e864669Sjmcneill 	    &ad->ad_sc->sc_rparam);
43578a9ff04bSjmcneill 
43588a9ff04bSjmcneill 	return 0;
43598a9ff04bSjmcneill }
43608a9ff04bSjmcneill 
43618a9ff04bSjmcneill static void
hdafg_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)43628a9ff04bSjmcneill hdafg_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
43638a9ff04bSjmcneill {
43648a9ff04bSjmcneill 	struct hdaudio_audiodev *ad = opaque;
43658a9ff04bSjmcneill 
43668a9ff04bSjmcneill 	*intr = &ad->ad_sc->sc_intr_lock;
43678a9ff04bSjmcneill 	*thread = &ad->ad_sc->sc_lock;
43688a9ff04bSjmcneill }
43698a9ff04bSjmcneill 
43708a9ff04bSjmcneill static int
hdafg_unsol(device_t self,uint8_t tag)43718a9ff04bSjmcneill hdafg_unsol(device_t self, uint8_t tag)
43728a9ff04bSjmcneill {
43738a9ff04bSjmcneill 	struct hdafg_softc *sc = device_private(self);
43748a9ff04bSjmcneill 	struct hdaudio_assoc *as = sc->sc_assocs;
43758a9ff04bSjmcneill 	int i, j;
43768a9ff04bSjmcneill 
43778a9ff04bSjmcneill 	switch (tag) {
43788a9ff04bSjmcneill 	case HDAUDIO_UNSOLTAG_EVENT_DD:
4379634cdb73Smrg #ifdef HDAFG_HDMI_DEBUG
43808a9ff04bSjmcneill 		hda_print(sc, "unsol: display device hotplug\n");
4381634cdb73Smrg #endif
43828a9ff04bSjmcneill 		for (i = 0; i < sc->sc_nassocs; i++) {
43838a9ff04bSjmcneill 			if (as[i].as_displaydev == false)
43848a9ff04bSjmcneill 				continue;
43858a9ff04bSjmcneill 			for (j = 0; j < HDAUDIO_MAXPINS; j++) {
43868a9ff04bSjmcneill 				if (as[i].as_pins[j] == 0)
43878a9ff04bSjmcneill 					continue;
43888a9ff04bSjmcneill 				hdafg_assoc_dump_dd(sc, &as[i], j, 0);
43898a9ff04bSjmcneill 			}
43908a9ff04bSjmcneill 		}
43918a9ff04bSjmcneill 		break;
43928a9ff04bSjmcneill 	default:
4393634cdb73Smrg #ifdef HDAFG_HDMI_DEBUG
43948a9ff04bSjmcneill 		hda_print(sc, "unsol: tag=%u\n", tag);
4395634cdb73Smrg #endif
43968a9ff04bSjmcneill 		break;
43978a9ff04bSjmcneill 	}
43988a9ff04bSjmcneill 
43998a9ff04bSjmcneill 	return 0;
44008a9ff04bSjmcneill }
44018a9ff04bSjmcneill 
44028a9ff04bSjmcneill static int
hdafg_widget_info(void * opaque,prop_dictionary_t request,prop_dictionary_t response)44038a9ff04bSjmcneill hdafg_widget_info(void *opaque, prop_dictionary_t request,
44048a9ff04bSjmcneill     prop_dictionary_t response)
44058a9ff04bSjmcneill {
44068a9ff04bSjmcneill 	struct hdafg_softc *sc = opaque;
44078a9ff04bSjmcneill 	struct hdaudio_widget *w;
44088a9ff04bSjmcneill 	prop_array_t connlist;
44098a9ff04bSjmcneill 	uint32_t config, wcap;
44108a9ff04bSjmcneill 	uint16_t index;
44118a9ff04bSjmcneill 	int nid;
44128a9ff04bSjmcneill 	int i;
44138a9ff04bSjmcneill 
44148a9ff04bSjmcneill 	if (prop_dictionary_get_uint16(request, "index", &index) == false)
44158a9ff04bSjmcneill 		return EINVAL;
44168a9ff04bSjmcneill 
44178a9ff04bSjmcneill 	nid = sc->sc_startnode + index;
44188a9ff04bSjmcneill 	if (nid >= sc->sc_endnode)
44198a9ff04bSjmcneill 		return EINVAL;
44208a9ff04bSjmcneill 
44218a9ff04bSjmcneill 	w = hdafg_widget_lookup(sc, nid);
44228a9ff04bSjmcneill 	if (w == NULL)
44238a9ff04bSjmcneill 		return ENXIO;
44248a9ff04bSjmcneill 	wcap = hda_get_wparam(w, PIN_CAPABILITIES);
44258a9ff04bSjmcneill 	config = hdaudio_command(sc->sc_codec, w->w_nid,
44268a9ff04bSjmcneill 	    CORB_GET_CONFIGURATION_DEFAULT, 0);
4427814a7798Sthorpej 	prop_dictionary_set_string_nocopy(response, "name", w->w_name);
44288a9ff04bSjmcneill 	prop_dictionary_set_bool(response, "enable", w->w_enable);
44298a9ff04bSjmcneill 	prop_dictionary_set_uint8(response, "nid", w->w_nid);
44308a9ff04bSjmcneill 	prop_dictionary_set_uint8(response, "type", w->w_type);
44318a9ff04bSjmcneill 	prop_dictionary_set_uint32(response, "config", config);
44328a9ff04bSjmcneill 	prop_dictionary_set_uint32(response, "cap", wcap);
44338a9ff04bSjmcneill 	if (w->w_nconns == 0)
44348a9ff04bSjmcneill 		return 0;
44358a9ff04bSjmcneill 	connlist = prop_array_create();
44368a9ff04bSjmcneill 	for (i = 0; i < w->w_nconns; i++) {
44378a9ff04bSjmcneill 		if (w->w_conns[i] == 0)
44388a9ff04bSjmcneill 			continue;
4439814a7798Sthorpej 		prop_array_add_and_rel(connlist,
4440814a7798Sthorpej 		    prop_number_create_unsigned(w->w_conns[i]));
44418a9ff04bSjmcneill 	}
44428a9ff04bSjmcneill 	prop_dictionary_set(response, "connlist", connlist);
44438a9ff04bSjmcneill 	prop_object_release(connlist);
44448a9ff04bSjmcneill 	return 0;
44458a9ff04bSjmcneill }
44468a9ff04bSjmcneill 
44478a9ff04bSjmcneill static int
hdafg_codec_info(void * opaque,prop_dictionary_t request,prop_dictionary_t response)44488a9ff04bSjmcneill hdafg_codec_info(void *opaque, prop_dictionary_t request,
44498a9ff04bSjmcneill     prop_dictionary_t response)
44508a9ff04bSjmcneill {
44518a9ff04bSjmcneill 	struct hdafg_softc *sc = opaque;
44528a9ff04bSjmcneill 	prop_dictionary_set_uint16(response, "vendor-id",
44538a9ff04bSjmcneill 	    sc->sc_vendor);
44548a9ff04bSjmcneill 	prop_dictionary_set_uint16(response, "product-id",
44558a9ff04bSjmcneill 	    sc->sc_product);
44568a9ff04bSjmcneill 	return 0;
44578a9ff04bSjmcneill }
44588a9ff04bSjmcneill 
44598a9ff04bSjmcneill MODULE(MODULE_CLASS_DRIVER, hdafg, "hdaudio");
44608a9ff04bSjmcneill 
44618a9ff04bSjmcneill #ifdef _MODULE
44628a9ff04bSjmcneill #include "ioconf.c"
44638a9ff04bSjmcneill #endif
44648a9ff04bSjmcneill 
44658a9ff04bSjmcneill static int
hdafg_modcmd(modcmd_t cmd,void * opaque)44668a9ff04bSjmcneill hdafg_modcmd(modcmd_t cmd, void *opaque)
44678a9ff04bSjmcneill {
44688a9ff04bSjmcneill 	int error = 0;
44698a9ff04bSjmcneill 
44708a9ff04bSjmcneill 	switch (cmd) {
44718a9ff04bSjmcneill 	case MODULE_CMD_INIT:
44728a9ff04bSjmcneill #ifdef _MODULE
44738a9ff04bSjmcneill 		error = config_init_component(cfdriver_ioconf_hdafg,
44748a9ff04bSjmcneill 		    cfattach_ioconf_hdafg, cfdata_ioconf_hdafg);
44758a9ff04bSjmcneill #endif
44768a9ff04bSjmcneill 		return error;
44778a9ff04bSjmcneill 	case MODULE_CMD_FINI:
44788a9ff04bSjmcneill #ifdef _MODULE
44798a9ff04bSjmcneill 		error = config_fini_component(cfdriver_ioconf_hdafg,
44808a9ff04bSjmcneill 		    cfattach_ioconf_hdafg, cfdata_ioconf_hdafg);
44818a9ff04bSjmcneill #endif
44828a9ff04bSjmcneill 		return error;
44838a9ff04bSjmcneill 	default:
44848a9ff04bSjmcneill 		return ENOTTY;
44858a9ff04bSjmcneill 	}
44868a9ff04bSjmcneill }
44878a9ff04bSjmcneill 
44888a9ff04bSjmcneill #define HDAFG_GET_ANACTRL		0xfe0
44898a9ff04bSjmcneill #define HDAFG_SET_ANACTRL		0x7e0
44908a9ff04bSjmcneill #define HDAFG_ANALOG_BEEP_EN		__BIT(5)
44918a9ff04bSjmcneill #define HDAFG_ALC231_MONO_OUT_MIXER	0xf
44928a9ff04bSjmcneill #define HDAFG_STAC9200_AFG		0x1
44938a9ff04bSjmcneill #define HDAFG_STAC9200_GET_ANACTRL_PAYLOAD	0x0
44948a9ff04bSjmcneill #define HDAFG_ALC231_INPUT_BOTH_CHANNELS_UNMUTE 0x7100
44958a9ff04bSjmcneill 
44968a9ff04bSjmcneill static void
hdafg_enable_analog_beep(struct hdafg_softc * sc)44978a9ff04bSjmcneill hdafg_enable_analog_beep(struct hdafg_softc *sc)
44988a9ff04bSjmcneill {
44998a9ff04bSjmcneill 	int nid;
45008a9ff04bSjmcneill 	uint32_t response;
45018a9ff04bSjmcneill 
45028a9ff04bSjmcneill 	switch (sc->sc_vendor) {
45038a9ff04bSjmcneill 	case HDAUDIO_VENDOR_SIGMATEL:
45048a9ff04bSjmcneill 		switch (sc->sc_product) {
45058a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9200:
45068a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9200D:
45078a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9202:
45088a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9202D:
45098a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9204:
45108a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9204D:
45118a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9205:
45128a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9205_1:
45138a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_SIGMATEL_STAC9205D:
45148a9ff04bSjmcneill 			nid = HDAFG_STAC9200_AFG;
45158a9ff04bSjmcneill 
45168a9ff04bSjmcneill 			response = hdaudio_command(sc->sc_codec, nid,
45178a9ff04bSjmcneill 			    HDAFG_GET_ANACTRL,
45188a9ff04bSjmcneill 			    HDAFG_STAC9200_GET_ANACTRL_PAYLOAD);
45198a9ff04bSjmcneill 			hda_delay(100);
45208a9ff04bSjmcneill 
45218a9ff04bSjmcneill 			response |= HDAFG_ANALOG_BEEP_EN;
45228a9ff04bSjmcneill 
45238a9ff04bSjmcneill 			hdaudio_command(sc->sc_codec, nid, HDAFG_SET_ANACTRL,
45248a9ff04bSjmcneill 			    response);
45258a9ff04bSjmcneill 			hda_delay(100);
45268a9ff04bSjmcneill 			break;
45278a9ff04bSjmcneill 		default:
45288a9ff04bSjmcneill 			break;
45298a9ff04bSjmcneill 		}
45308a9ff04bSjmcneill 		break;
45318a9ff04bSjmcneill 	case HDAUDIO_VENDOR_REALTEK:
45328a9ff04bSjmcneill 		switch (sc->sc_product) {
45338a9ff04bSjmcneill 		case HDAUDIO_PRODUCT_REALTEK_ALC269:
45348a9ff04bSjmcneill 			/* The Panasonic Toughbook CF19 - Mk 5 uses a Realtek
45358a9ff04bSjmcneill 			 * ALC231 that identifies as an ALC269.
45368a9ff04bSjmcneill 			 * This unmutes the PCBEEP on the speaker.
45378a9ff04bSjmcneill 			 */
45388a9ff04bSjmcneill 			nid = HDAFG_ALC231_MONO_OUT_MIXER;
45398a9ff04bSjmcneill 			response = hdaudio_command(sc->sc_codec, nid,
45408a9ff04bSjmcneill 			    CORB_SET_AMPLIFIER_GAIN_MUTE,
45418a9ff04bSjmcneill 			    HDAFG_ALC231_INPUT_BOTH_CHANNELS_UNMUTE);
45428a9ff04bSjmcneill 			hda_delay(100);
45438a9ff04bSjmcneill 			break;
45448a9ff04bSjmcneill 		default:
45458a9ff04bSjmcneill 			break;
45468a9ff04bSjmcneill 		}
45478a9ff04bSjmcneill 	default:
45488a9ff04bSjmcneill 		break;
45498a9ff04bSjmcneill 	}
45508a9ff04bSjmcneill }
4551