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