diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/sound/pci/hda/hda_reg.h | 56 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac.c | 5849 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_private.h | 95 |
3 files changed, 3550 insertions, 2450 deletions
diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h index 7965daf..3c5757a 100644 --- a/sys/dev/sound/pci/hda/hda_reg.h +++ b/sys/dev/sound/pci/hda/hda_reg.h @@ -180,13 +180,14 @@ HDA_CMD_VERB_SET_CONV_FMT, (payload))) /* Digital Converter Control */ -#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT 0xf0d +#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT1 0xf0d +#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT2 0xf0e #define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1 0x70d #define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2 0x70e #define HDA_CMD_GET_DIGITAL_CONV_FMT(cad, nid) \ (HDA_CMD_12BIT((cad), (nid), \ - HDA_CMD_VERB_GET_DIGITAL_CONV_FMTT, 0x0)) + HDA_CMD_VERB_GET_DIGITAL_CONV_FMT1, 0x0)) #define HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, nid, payload) \ (HDA_CMD_12BIT((cad), (nid), \ HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1, (payload))) @@ -1034,7 +1035,7 @@ HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT) #define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(param) \ (((param) & HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) >> \ - HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT) #define HDA_PARAM_PIN_CAP_TRIGGER_REQD(param) \ (((param) & HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK) >> \ HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT) @@ -1193,14 +1194,47 @@ HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT) -#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK 0x00000000f -#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK 0x0000000f0 -#define HDA_CONFIG_DEFAULTCONF_MISC_MASK 0x000000f00 -#define HDA_CONFIG_DEFAULTCONF_COLOR_MASK 0x00000f000 -#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK 0x000f00000 -#define HDA_CONFIG_DEFAULTCONF_DEVICE_MASK 0x000f00000 -#define HDA_CONFIG_DEFAULTCONF_LOCATION_MASK 0x03f000000 -#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK 0x0c0000000 +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK 0x0000000f +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT 0 +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK 0x000000f0 +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT 4 +#define HDA_CONFIG_DEFAULTCONF_MISC_MASK 0x00000f00 +#define HDA_CONFIG_DEFAULTCONF_MISC_SHIFT 8 +#define HDA_CONFIG_DEFAULTCONF_COLOR_MASK 0x0000f000 +#define HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT 12 +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK 0x000f0000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT 16 +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MASK 0x00f00000 +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT 20 +#define HDA_CONFIG_DEFAULTCONF_LOCATION_MASK 0x3f000000 +#define HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT 24 +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK 0xc0000000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT 30 + +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_MISC(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_MISC_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_COLOR(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_DEVICE(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_LOCATION(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_LOCATION_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) #define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK (0<<30) #define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE (1<<30) diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index 06fa544..2f2ca85 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -61,6 +62,7 @@ * * Daniel Eischen <deischen@FreeBSD.org> * * * Maxime Guillaud <bsd-ports@mguillaud.net> * * * Ariff Abdullah <ariff@FreeBSD.org> * + * * Alexander Motin <mav@FreeBSD.org> * * * * * * ....and various people from freebsd-multimedia@FreeBSD.org * * * * @@ -81,8 +83,8 @@ #include "mixer_if.h" -#define HDA_DRV_TEST_REV "20080420_0052" -#define HDA_WIDGET_PARSER_REV 1 +#define HDA_DRV_TEST_REV "20080913_0111" +#define HDA_WIDGET_PARSER_REV 2 SND_DECLARE_FILE("$FreeBSD$"); @@ -213,6 +215,7 @@ SND_DECLARE_FILE("$FreeBSD$"); #define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) #define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) #define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) +#define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) #define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) /* Asus */ @@ -260,6 +263,11 @@ SND_DECLARE_FILE("$FreeBSD$"); #define APPLE_VENDORID 0x106b #define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) +/* Sony */ +#define SONY_VENDORID 0x104d +#define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) +#define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) + /* * Apple Intel MacXXXX seems using Sigmatel codec/vendor id * instead of their own, which is beyond my comprehension @@ -308,6 +316,7 @@ SND_DECLARE_FILE("$FreeBSD$"); /* Misc constants.. */ +#define HDA_AMP_VOL_DEFAULT (-1) #define HDA_AMP_MUTE_DEFAULT (0xffffffff) #define HDA_AMP_MUTE_NONE (0) #define HDA_AMP_MUTE_LEFT (1 << 0) @@ -317,16 +326,10 @@ SND_DECLARE_FILE("$FreeBSD$"); #define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) #define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) -#define HDA_DAC_PATH (1 << 0) -#define HDA_ADC_PATH (1 << 1) -#define HDA_ADC_RECSEL (1 << 2) - -#define HDA_DAC_LOCKED (1 << 3) -#define HDA_ADC_LOCKED (1 << 4) +#define HDA_ADC_MONITOR (1 << 0) -#define HDA_CTL_OUT (1 << 0) -#define HDA_CTL_IN (1 << 1) -#define HDA_CTL_BOTH (HDA_CTL_IN | HDA_CTL_OUT) +#define HDA_CTL_OUT 1 +#define HDA_CTL_IN 2 #define HDA_GPIO_MAX 8 /* 0 - 7 = GPIO , 8 = Flush */ @@ -346,6 +349,7 @@ SND_DECLARE_FILE("$FreeBSD$"); #define HDA_QUIRK_FORCESTEREO (1 << 11) #define HDA_QUIRK_EAPDINV (1 << 12) #define HDA_QUIRK_DMAPOS (1 << 13) +#define HDA_QUIRK_SENSEINV (1 << 14) /* 26 - 31 = vrefs */ #define HDA_QUIRK_IVREF50 (1 << 26) @@ -361,9 +365,6 @@ SND_DECLARE_FILE("$FreeBSD$"); HDA_QUIRK_OVREF100) #define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) -#define SOUND_MASK_SKIP (1 << 30) -#define SOUND_MASK_DISABLE (1 << 31) - #if __FreeBSD_version < 600000 #define taskqueue_drain(...) #endif @@ -386,6 +387,7 @@ static const struct { { "forcestereo", HDA_QUIRK_FORCESTEREO }, { "eapdinv", HDA_QUIRK_EAPDINV }, { "dmapos", HDA_QUIRK_DMAPOS }, + { "senseinv", HDA_QUIRK_SENSEINV }, { "ivref50", HDA_QUIRK_IVREF50 }, { "ivref80", HDA_QUIRK_IVREF80 }, { "ivref100", HDA_QUIRK_IVREF100 }, @@ -413,14 +415,18 @@ static const struct { #define HDA_PARSE_MAXDEPTH 10 #define HDAC_UNSOLTAG_EVENT_HP 0x00 -#define HDAC_UNSOLTAG_EVENT_TEST 0x01 MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); -enum { - HDA_PARSE_MIXER, - HDA_PARSE_DIRECT -}; +const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", + "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", + "White", "Other"}; + +const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", + "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", + "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; + +const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; /* Default */ static uint32_t hdac_fmt[] = { @@ -535,6 +541,7 @@ static const struct { #define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) #define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) #define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) +#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) #define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) /* Analog Devices */ @@ -554,16 +561,31 @@ static const struct { /* Sigmatel */ #define SIGMATEL_VENDORID 0x8384 +#define HDA_CODEC_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) +#define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) +#define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) +#define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) +#define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) +#define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) +#define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) +#define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) +#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) +#define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) #define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) +#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) #define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) #define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) -#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) -#define HDA_CODEC_STAC9227 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) -#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) #define HDA_CODEC_STAC9205 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) -#define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) #define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) +/* Silicon Image */ +#define SII_VENDORID 0x1095 +#define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) + +/* Lucent/Agere */ +#define AGERE_VENDORID 0x11c1 +#define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) + /* * Conexant * @@ -594,6 +616,11 @@ static const struct { #define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) #define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) +/* ATI */ +#define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) + +/* NVIDIA */ +#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) /* Codecs */ static const struct { @@ -611,6 +638,7 @@ static const struct { { HDA_CODEC_ALC883, "Realtek ALC883" }, { HDA_CODEC_ALC885, "Realtek ALC885" }, { HDA_CODEC_ALC888, "Realtek ALC888" }, + { HDA_CODEC_ALC889, "Realtek ALC889" }, { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, { HDA_CODEC_AD1983, "Analog Devices AD1983" }, { HDA_CODEC_AD1984, "Analog Devices AD1984" }, @@ -622,7 +650,14 @@ static const struct { { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, - { HDA_CODEC_STAC9227, "Sigmatel STAC9227" }, + { HDA_CODEC_STAC9230X, "Sigmatel STAC9230X" }, + { HDA_CODEC_STAC9230D, "Sigmatel STAC9230D" }, + { HDA_CODEC_STAC9229X, "Sigmatel STAC9229X" }, + { HDA_CODEC_STAC9229D, "Sigmatel STAC9229D" }, + { HDA_CODEC_STAC9228X, "Sigmatel STAC9228X" }, + { HDA_CODEC_STAC9228D, "Sigmatel STAC9228D" }, + { HDA_CODEC_STAC9227X, "Sigmatel STAC9227X" }, + { HDA_CODEC_STAC9227D, "Sigmatel STAC9227D" }, { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, { HDA_CODEC_STAC9205, "Sigmatel STAC9205" }, { HDA_CODEC_STAC9872AK,"Sigmatel STAC9872AK" }, @@ -645,131 +680,21 @@ static const struct { { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, + { HDA_CODEC_SIIXXXX, "Silicon Image (Unknown)" }, + { HDA_CODEC_AGEREXXXX, "Lucent/Agere Systems (Unknown)" }, { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, + { HDA_CODEC_ATIXXXX, "ATI (Unknown)" }, + { HDA_CODEC_NVIDIAXXXX,"NVidia (Unknown)" }, }; #define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) -enum { - HDAC_HP_SWITCH_CTL, - HDAC_HP_SWITCH_CTRL, - HDAC_HP_SWITCH_DEBUG -}; - -static const struct { - uint32_t model; - uint32_t id; - int type; - int inverted; - int polling; - int execsense; - nid_t hpnid; - nid_t spkrnid[8]; - nid_t eapdnid; -} hdac_hp_switch[] = { - /* Specific OEM models */ - { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 17, { 16, -1 }, 16 }, - /* { HP_XW4300_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 21, { 16, 17, -1 }, -1 } */ - /* { HP_3010_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_DEBUG, - 0, 1, 0, 16, { 15, 18, 19, 20, 21, -1 }, -1 }, */ - { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, -1 }, 5 }, - { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, -1 }, 5 }, - { HP_NX6325_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, -1 }, 5 }, - /* { HP_DC7700_SUBVENDOR, HDA_CODEC_ALC262, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 21, { 22, 27, -1 }, -1 }, */ - { TOSHIBA_U200_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, -1 }, -1 }, - { TOSHIBA_A135_SUBVENDOR, HDA_CODEC_ALC861VD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 27, { 20, -1 }, -1 }, - { DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, - 0, 0, -1, 13, { 14, -1 }, -1 }, - { DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, - 0, 0, -1, 13, { 14, -1 }, -1 }, - { DELL_OPLX745_SUBVENDOR, HDA_CODEC_AD1983, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, 7, -1 }, -1 }, - { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205, HDAC_HP_SWITCH_CTRL, - 0, 0, -1, 10, { 13, -1 }, -1 }, - { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205, HDAC_HP_SWITCH_CTRL, - 0, 0, -1, 10, { 13, -1 }, -1 }, - { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 21, { 20, 22, -1 }, -1 }, - { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDAC_HP_SWITCH_CTRL, - 0, 0, -1, 10, { 13, -1 }, -1 }, - { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, - 1, 0, -1, 26, { 27, -1 }, -1 }, - { LENOVO_3KN200_SUBVENDOR, HDA_CODEC_ALC861VD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 27, { 20, -1 }, -1 }, - /* { LENOVO_TCA55_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 26, { 27, 28, 29, 30, -1 }, -1 }, */ - { LG_LW20_SUBVENDOR, HDA_CODEC_ALC880, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 27, { 20, -1 }, -1 }, - { ACER_A5050_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { ACER_3681WXM_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { ACER_A4520_SUBVENDOR, HDA_CODEC_ALC268, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { ACER_A4710_SUBVENDOR, HDA_CODEC_ALC268, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { ACER_A4715_SUBVENDOR, HDA_CODEC_ALC268, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { UNIWILL_9080_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { MSI_MS1034_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 27, -1 }, -1 }, - { MSI_MS034A_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 27, -1 }, -1 }, - { FS_SI1848_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - { FL_S7020D_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 16, -1 }, -1 }, - { FL_U1010_SUBVENDOR, HDA_CODEC_ALC262, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, - /* - * All models that at least come from the same vendor with - * simmilar codec. - */ - { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 17, { 16, -1 }, 16 }, - { HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, -1 }, 5 }, - { TOSHIBA_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 6, { 5, -1 }, -1 }, - { DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, - 0, 0, -1, 13, { 14, -1 }, -1 }, -#if 0 - { LENOVO_ALL_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, - 1, 0, -1, 26, { 27, -1 }, -1 }, - { ACER_ALL_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, - 0, 0, -1, 20, { 21, -1 }, -1 }, -#endif -}; -#define HDAC_HP_SWITCH_LEN \ - (sizeof(hdac_hp_switch) / sizeof(hdac_hp_switch[0])) - -static const struct { - uint32_t model; - uint32_t id; - nid_t eapdnid; - int hp_switch; -} hdac_eapd_switch[] = { - { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, 16, 1 }, - { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, - { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, -}; -#define HDAC_EAPD_SWITCH_LEN \ - (sizeof(hdac_eapd_switch) / sizeof(hdac_eapd_switch[0])) /**************************************************************************** * Function prototypes ****************************************************************************/ static void hdac_intr_handler(void *); -static int hdac_reset(struct hdac_softc *); +static int hdac_reset(struct hdac_softc *, int); static int hdac_get_capabilities(struct hdac_softc *); static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); static int hdac_dma_alloc(struct hdac_softc *, @@ -783,10 +708,10 @@ static void hdac_corb_init(struct hdac_softc *); static void hdac_rirb_init(struct hdac_softc *); static void hdac_corb_start(struct hdac_softc *); static void hdac_rirb_start(struct hdac_softc *); -static void hdac_scan_codecs(struct hdac_softc *, int); -static int hdac_probe_codec(struct hdac_codec *); -static struct hdac_devinfo *hdac_probe_function(struct hdac_codec *, nid_t); -static void hdac_add_child(struct hdac_softc *, struct hdac_devinfo *); +static void hdac_scan_codecs(struct hdac_softc *); +static void hdac_probe_codec(struct hdac_codec *); +static void hdac_probe_function(struct hdac_codec *, nid_t); +static int hdac_pcmchannel_setup(struct hdac_chan *); static void hdac_attach2(void *); @@ -798,34 +723,37 @@ static void hdac_command_send_internal(struct hdac_softc *, static int hdac_probe(device_t); static int hdac_attach(device_t); static int hdac_detach(device_t); +static int hdac_suspend(device_t); +static int hdac_resume(device_t); static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, uint32_t, int, int); static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, - nid_t, int, int); + nid_t, int, int, int); static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, nid_t, nid_t, int, int, int, int, int, int); -static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *); static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); static int hdac_rirb_flush(struct hdac_softc *sc); static int hdac_unsolq_flush(struct hdac_softc *sc); +static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf); + #define hdac_command(a1, a2, a3) \ hdac_command_sendone_internal(a1, a2, a3) -#define hdac_codec_id(d) \ - ((uint32_t)((d == NULL) ? 0x00000000 : \ - ((((uint32_t)(d)->vendor_id & 0x0000ffff) << 16) | \ - ((uint32_t)(d)->device_id & 0x0000ffff)))) +#define hdac_codec_id(c) \ + ((uint32_t)((c == NULL) ? 0x00000000 : \ + ((((uint32_t)(c)->vendor_id & 0x0000ffff) << 16) | \ + ((uint32_t)(c)->device_id & 0x0000ffff)))) static char * -hdac_codec_name(struct hdac_devinfo *devinfo) +hdac_codec_name(struct hdac_codec *codec) { uint32_t id; int i; - id = hdac_codec_id(devinfo); + id = hdac_codec_id(codec); for (i = 0; i < HDAC_CODECS_LEN; i++) { if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) @@ -836,20 +764,6 @@ hdac_codec_name(struct hdac_devinfo *devinfo) } static char * -hdac_audio_ctl_ossmixer_mask2name(uint32_t devmask) -{ - static char *ossname[] = SOUND_DEVICE_NAMES; - static char *unknown = "???"; - int i; - - for (i = SOUND_MIXER_NRDEVICES - 1; i >= 0; i--) { - if (devmask & (1 << i)) - return (ossname[i]); - } - return (unknown); -} - -static void hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) { static char *ossname[] = SOUND_DEVICE_NAMES; @@ -864,6 +778,7 @@ hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) first = 0; } } + return (buf); } static struct hdac_audio_ctl * @@ -879,48 +794,46 @@ hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) } static struct hdac_audio_ctl * -hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, +hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, int dir, int index, int cnt) { - struct hdac_audio_ctl *ctl, *retctl = NULL; - int i, at, atindex, found = 0; + struct hdac_audio_ctl *ctl; + int i, found = 0; if (devinfo == NULL || devinfo->function.audio.ctl == NULL) return (NULL); - at = cnt; - if (at == 0) - at = 1; - else if (at < 0) - at = -1; - atindex = index; - if (atindex < 0) - atindex = -1; - i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) + if (ctl->enable == 0) continue; - if (!(ctl->widget->nid == nid && (atindex == -1 || - ctl->index == atindex))) + if (ctl->widget->nid != nid) + continue; + if (dir && ctl->ndir != dir) + continue; + if (index >= 0 && ctl->ndir == HDA_CTL_IN && + ctl->dir == ctl->ndir && ctl->index != index) continue; found++; - if (found == cnt) + if (found == cnt || cnt <= 0) return (ctl); - retctl = ctl; } - return ((at == -1) ? retctl : NULL); + return (NULL); } +/* + * Jack detection (Speaker/HP redirection) event handler. + */ static void hdac_hp_switch_handler(struct hdac_devinfo *devinfo) { + struct hdac_audio_as *as; struct hdac_softc *sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; - uint32_t val, id, res; - int i = 0, j, timeout, forcemute; + uint32_t val, res; + int i, j; nid_t cad; if (devinfo == NULL || devinfo->codec == NULL || @@ -929,84 +842,47 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) sc = devinfo->codec->sc; cad = devinfo->codec->cad; - id = hdac_codec_id(devinfo); - for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { - if (HDA_DEV_MATCH(hdac_hp_switch[i].model, - sc->pci_subvendor) && - hdac_hp_switch[i].id == id) - break; - } - - if (i >= HDAC_HP_SWITCH_LEN) - return; - - forcemute = 0; - if (hdac_hp_switch[i].eapdnid != -1) { - w = hdac_widget_get(devinfo, hdac_hp_switch[i].eapdnid); - if (w != NULL && w->param.eapdbtl != HDAC_INVALID) - forcemute = (w->param.eapdbtl & - HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1; - } - - if (hdac_hp_switch[i].execsense != -1) - hdac_command(sc, - HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid, - hdac_hp_switch[i].execsense), cad); + as = devinfo->function.audio.as; + for (i = 0; i < devinfo->function.audio.ascnt; i++) { + if (as[i].hpredir < 0) + continue; + + w = hdac_widget_get(devinfo, as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; - timeout = 10000; - do { res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), - cad); - if (hdac_hp_switch[i].execsense == -1 || res != 0x7fffffff) - break; - DELAY(10); - } while (--timeout != 0); + HDA_CMD_GET_PIN_SENSE(cad, as[i].pins[15]), cad); - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: Pin sense: nid=%d timeout=%d res=0x%08x\n", - hdac_hp_switch[i].hpnid, timeout, res); - ); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Pin sense: nid=%d res=0x%08x\n", + as[i].pins[15], res); + ); - res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); - res ^= hdac_hp_switch[i].inverted; + res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); + if (devinfo->function.audio.quirks & HDA_QUIRK_SENSEINV) + res ^= 1; - switch (hdac_hp_switch[i].type) { - case HDAC_HP_SWITCH_CTL: + /* (Un)Mute headphone pin. */ ctl = hdac_audio_ctl_amp_get(devinfo, - hdac_hp_switch[i].hpnid, 0, 1); - if (ctl != NULL) { - val = (res != 0 && forcemute == 0) ? - HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL; - if (val != ctl->muted) { - ctl->muted = val; + as[i].pins[15], HDA_CTL_IN, -1, 1); + if (ctl != NULL && ctl->mute) { + /* If pin has muter - use it. */ + val = (res != 0) ? 0 : 1; + if (val != ctl->forcemute) { + ctl->forcemute = val; hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, ctl->left, - ctl->right); + HDA_AMP_MUTE_DEFAULT, + HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); } - } - for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { - ctl = hdac_audio_ctl_amp_get(devinfo, - hdac_hp_switch[i].spkrnid[j], 0, 1); - if (ctl == NULL) - continue; - val = (res != 0 || forcemute == 1) ? - HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE; - if (val == ctl->muted) - continue; - ctl->muted = val; - hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, - ctl->left, ctl->right); - } - break; - case HDAC_HP_SWITCH_CTRL: - if (res != 0) { - /* HP in */ - w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + } else { + /* If there is no muter - disable pin output. */ + w = hdac_widget_get(devinfo, as[i].pins[15]); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (forcemute == 0) + if (res != 0) val = w->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else @@ -1019,27 +895,34 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) w->nid, w->wclass.pin.ctrl), cad); } } - for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { - w = hdac_widget_get(devinfo, - hdac_hp_switch[i].spkrnid[j]); - if (w == NULL || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val == w->wclass.pin.ctrl) + } + /* (Un)Mute other pins. */ + for (j = 0; j < 15; j++) { + if (as[i].pins[j] <= 0) + continue; + ctl = hdac_audio_ctl_amp_get(devinfo, + as[i].pins[j], HDA_CTL_IN, -1, 1); + if (ctl != NULL && ctl->mute) { + /* If pin has muter - use it. */ + val = (res != 0) ? 1 : 0; + if (val == ctl->forcemute) continue; - w->wclass.pin.ctrl = val; - hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL( - cad, w->nid, w->wclass.pin.ctrl), cad); + ctl->forcemute = val; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, + HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); + continue; } - } else { - /* HP out */ - w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + /* If there is no muter - disable pin output. */ + w = hdac_widget_get(devinfo, as[i].pins[j]); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (res != 0) + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (val != w->wclass.pin.ctrl) { w->wclass.pin.ctrl = val; hdac_command(sc, @@ -1047,65 +930,98 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) w->nid, w->wclass.pin.ctrl), cad); } } - for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { - w = hdac_widget_get(devinfo, - hdac_hp_switch[i].spkrnid[j]); - if (w == NULL || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (forcemute == 0) - val = w->wclass.pin.ctrl | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - else - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val == w->wclass.pin.ctrl) - continue; - w->wclass.pin.ctrl = val; - hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL( - cad, w->nid, w->wclass.pin.ctrl), cad); - } } - break; - case HDAC_HP_SWITCH_DEBUG: - if (hdac_hp_switch[i].execsense != -1) + } +} + +/* + * Callback for poll based jack detection. + */ +static void +hdac_jack_poll_callback(void *arg) +{ + struct hdac_devinfo *devinfo = arg; + struct hdac_softc *sc; + + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return; + sc = devinfo->codec->sc; + hdac_lock(sc); + if (sc->poll_ival == 0) { + hdac_unlock(sc); + return; + } + hdac_hp_switch_handler(devinfo); + callout_reset(&sc->poll_jack, sc->poll_ival, + hdac_jack_poll_callback, devinfo); + hdac_unlock(sc); +} + +/* + * Jack detection initializer. + */ +static void +hdac_hp_switch_init(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w; + uint32_t id; + int i, enable = 0, poll = 0; + nid_t cad; + + id = hdac_codec_id(devinfo->codec); + cad = devinfo->codec->cad; + for (i = 0; i < devinfo->function.audio.ascnt; i++) { + if (as[i].hpredir < 0) + continue; + + w = hdac_widget_get(devinfo, as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || + (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { + device_printf(sc->dev, + "No jack detection support at pin %d\n", + as[i].pins[15]); + continue; + } + enable = 1; + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { hdac_command(sc, - HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid, - hdac_hp_switch[i].execsense), cad); - res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); - device_printf(sc->dev, - "[ 0] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", - hdac_hp_switch[i].hpnid, res); - for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { - w = hdac_widget_get(devinfo, - hdac_hp_switch[i].spkrnid[j]); - if (w == NULL || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (hdac_hp_switch[i].execsense != -1) - hdac_command(sc, - HDA_CMD_SET_PIN_SENSE(cad, w->nid, - hdac_hp_switch[i].execsense), cad); - res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); + HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | + HDAC_UNSOLTAG_EVENT_HP), cad); + } else + poll = 1; + HDA_BOOTVERBOSE( device_printf(sc->dev, - "[%2d] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", - j + 1, w->nid, res); + "Enabling headphone/speaker " + "audio routing switching:\n"); + device_printf(sc->dev, "\tas=%d sense nid=%d [%s]\n", + i, w->nid, (poll != 0) ? "POLL" : "UNSOL"); + ); + } + if (enable) { + hdac_hp_switch_handler(devinfo); + if (poll) { + callout_reset(&sc->poll_jack, 1, + hdac_jack_poll_callback, devinfo); } - break; - default: - break; } } +/* + * Unsolicited messages handler. + */ static void hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) { struct hdac_softc *sc; struct hdac_devinfo *devinfo = NULL; - device_t *devlist = NULL; - int devcount, i; + int i; if (codec == NULL || codec->sc == NULL) return; @@ -1113,22 +1029,16 @@ hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) sc = codec->sc; HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Unsol Tag: 0x%08x\n", tag); + device_printf(sc->dev, "Unsol Tag: 0x%08x\n", tag); ); - device_get_children(sc->dev, &devlist, &devcount); - for (i = 0; devlist != NULL && i < devcount; i++) { - devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); - if (devinfo != NULL && devinfo->node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && - devinfo->codec != NULL && - devinfo->codec->cad == codec->cad) { + for (i = 0; i < codec->num_fgs; i++) { + if (codec->fgs[i].node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + devinfo = &codec->fgs[i]; break; - } else - devinfo = NULL; + } } - if (devlist != NULL) - free(devlist, M_TEMP); if (devinfo == NULL) return; @@ -1137,10 +1047,8 @@ hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) case HDAC_UNSOLTAG_EVENT_HP: hdac_hp_switch_handler(devinfo); break; - case HDAC_UNSOLTAG_EVENT_TEST: - device_printf(sc->dev, "Unsol Test!\n"); - break; default: + device_printf(sc->dev, "Unknown unsol tag: 0x%08x!\n", tag); break; } } @@ -1165,7 +1073,7 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) #ifdef HDAC_INTR_EXTRA HDA_BOOTVERBOSE( if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) - device_printf(sc->dev, + device_printf(ch->pdevinfo->dev, "PCMDIR_%s intr triggered beyond stream boundary:" "%08x\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); @@ -1201,6 +1109,7 @@ hdac_intr_handler(void *context) uint8_t rirbsts; struct hdac_rirb *rirb_base; uint32_t trigger; + int i; sc = (struct hdac_softc *)context; @@ -1239,12 +1148,11 @@ hdac_intr_handler(void *context) } if (intsts & HDAC_INTSTS_SIS_MASK) { - if ((intsts & (1 << sc->num_iss)) && - hdac_stream_intr(sc, &sc->play) != 0) - trigger |= HDAC_TRIGGER_PLAY; - if ((intsts & (1 << 0)) && - hdac_stream_intr(sc, &sc->rec) != 0) - trigger |= HDAC_TRIGGER_REC; + for (i = 0; i < sc->num_chans; i++) { + if ((intsts & (1 << (sc->chans[i].off >> 5))) && + hdac_stream_intr(sc, &sc->chans[i]) != 0) + trigger |= (1 << i); + } /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & @@ -1254,21 +1162,21 @@ hdac_intr_handler(void *context) hdac_unlock(sc); - if (trigger & HDAC_TRIGGER_PLAY) - chn_intr(sc->play.c); - if (trigger & HDAC_TRIGGER_REC) - chn_intr(sc->rec.c); + for (i = 0; i < sc->num_chans; i++) { + if (trigger & (1 << i)) + chn_intr(sc->chans[i].c); + } if (trigger & HDAC_TRIGGER_UNSOL) taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); } /**************************************************************************** - * int hdac_reset(hdac_softc *) + * int hdac_reset(hdac_softc *, int) * * Reset the hdac to a quiescent and known state. ****************************************************************************/ static int -hdac_reset(struct hdac_softc *sc) +hdac_reset(struct hdac_softc *sc, int wakeup) { uint32_t gctl; int count, i; @@ -1312,6 +1220,11 @@ hdac_reset(struct hdac_softc *sc) device_printf(sc->dev, "Unable to put hdac in reset\n"); return (ENXIO); } + + /* If wakeup is not requested - leave the controller in reset state. */ + if (!wakeup) + return (0); + DELAY(100); gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); @@ -1394,6 +1307,13 @@ hdac_get_capabilities(struct hdac_softc *sc) return (ENXIO); } + HDA_BOOTVERBOSE( + device_printf(sc->dev, " CORB size: %d\n", sc->corb_size); + device_printf(sc->dev, " RIRB size: %d\n", sc->rirb_size); + device_printf(sc->dev, " Streams: ISS=%d OSS=%d BSS=%d\n", + sc->num_iss, sc->num_oss, sc->num_bss); + ); + return (0); } @@ -1602,8 +1522,8 @@ hdac_irq_alloc(struct hdac_softc *sc) __func__); goto hdac_irq_alloc_fail; } - result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE, - hdac_intr_handler, sc, &irq->irq_handle); + result = bus_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE | INTR_TYPE_AV, + NULL, hdac_intr_handler, sc, &irq->irq_handle); if (result != 0) { device_printf(sc->dev, "%s: Unable to setup interrupt handler (%x)\n", @@ -1732,18 +1652,16 @@ hdac_rirb_init(struct hdac_softc *sc) sc->rirb_rp = 0; HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); - if (sc->polling == 0) { - /* Setup the interrupt threshold */ - HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); + /* Setup the interrupt threshold */ + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); - /* Enable Overrun and response received reporting */ + /* Enable Overrun and response received reporting */ #if 0 - HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, - HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, + HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); #else - HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); #endif - } #if 0 /* @@ -1794,19 +1712,14 @@ hdac_rirb_start(struct hdac_softc *sc) * Scan the bus for available codecs, starting with num. ****************************************************************************/ static void -hdac_scan_codecs(struct hdac_softc *sc, int num) +hdac_scan_codecs(struct hdac_softc *sc) { struct hdac_codec *codec; int i; uint16_t statests; - if (num < 0) - num = 0; - if (num >= HDAC_CODEC_MAX) - num = HDAC_CODEC_MAX - 1; - statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); - for (i = num; i < HDAC_CODEC_MAX; i++) { + for (i = 0; i < HDAC_CODEC_MAX; i++) { if (HDAC_STATESTS_SDIWAKE(statests, i)) { /* We have found a codec. */ codec = (struct hdac_codec *)malloc(sizeof(*codec), @@ -1822,8 +1735,7 @@ hdac_scan_codecs(struct hdac_softc *sc, int num) codec->sc = sc; codec->cad = i; sc->codecs[i] = codec; - if (hdac_probe_codec(codec) != 0) - break; + hdac_probe_codec(codec); } } /* All codecs have been probed, now try to attach drivers to them */ @@ -1835,11 +1747,10 @@ hdac_scan_codecs(struct hdac_softc *sc, int num) * * Probe a the given codec_id for available function groups. ****************************************************************************/ -static int +static void hdac_probe_codec(struct hdac_codec *codec) { struct hdac_softc *sc = codec->sc; - struct hdac_devinfo *devinfo; uint32_t vendorid, revisionid, subnode; int startnode; int endnode; @@ -1847,7 +1758,7 @@ hdac_probe_codec(struct hdac_codec *codec) nid_t cad = codec->cad; HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Probing codec: %d\n", cad); + device_printf(sc->dev, "Probing codec %d...\n", cad); ); vendorid = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), @@ -1855,6 +1766,33 @@ hdac_probe_codec(struct hdac_codec *codec) revisionid = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), cad); + codec->vendor_id = HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); + codec->device_id = HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); + codec->revision_id = HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); + codec->stepping_id = HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); + + if (vendorid == HDAC_INVALID && revisionid == HDAC_INVALID) { + device_printf(sc->dev, "Codec #%d is not responding!" + " Probing aborted.\n", cad); + return; + } + + device_printf(sc->dev, "<HDA Codec #%d: %s>\n", + cad, hdac_codec_name(codec)); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "<HDA Codec ID: 0x%08x>\n", + hdac_codec_id(codec)); + device_printf(sc->dev, " Vendor: 0x%04x\n", + codec->vendor_id); + device_printf(sc->dev, " Device: 0x%04x\n", + codec->device_id); + device_printf(sc->dev, " Revision: 0x%02x\n", + codec->revision_id); + device_printf(sc->dev, " Stepping: 0x%02x\n", + codec->stepping_id); + device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", + sc->pci_subvendor); + ); subnode = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), cad); @@ -1862,75 +1800,73 @@ hdac_probe_codec(struct hdac_codec *codec) endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: \tstartnode=%d endnode=%d\n", + device_printf(sc->dev, "\tstartnode=%d endnode=%d\n", startnode, endnode); ); - for (i = startnode; i < endnode; i++) { - devinfo = hdac_probe_function(codec, i); - if (devinfo != NULL) { - /* XXX Ignore other FG. */ - devinfo->vendor_id = - HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); - devinfo->device_id = - HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); - devinfo->revision_id = - HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); - devinfo->stepping_id = - HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: \tFound AFG nid=%d " - "[startnode=%d endnode=%d]\n", - devinfo->nid, startnode, endnode); - ); - return (1); - } + + codec->fgs = (struct hdac_devinfo *)malloc(sizeof(struct hdac_devinfo) * + (endnode - startnode), M_HDAC, M_NOWAIT | M_ZERO); + if (codec->fgs == NULL) { + device_printf(sc->dev, "%s: Unable to allocate functiom groups\n", + __func__); + return; } - HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: \tAFG not found\n"); - ); - return (0); + for (i = startnode; i < endnode; i++) + hdac_probe_function(codec, i); + return; } -static struct hdac_devinfo * +/* + * Probe codec function and add it to the list. + */ +static void hdac_probe_function(struct hdac_codec *codec, nid_t nid) { struct hdac_softc *sc = codec->sc; - struct hdac_devinfo *devinfo; + struct hdac_devinfo *devinfo = &codec->fgs[codec->num_fgs]; uint32_t fctgrptype; + uint32_t res; nid_t cad = codec->cad; fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); - /* XXX For now, ignore other FG. */ - if (fctgrptype != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) - return (NULL); - - devinfo = (struct hdac_devinfo *)malloc(sizeof(*devinfo), M_HDAC, - M_NOWAIT | M_ZERO); - if (devinfo == NULL) { - device_printf(sc->dev, "%s: Unable to allocate ivar\n", - __func__); - return (NULL); - } - devinfo->nid = nid; devinfo->node_type = fctgrptype; devinfo->codec = codec; - hdac_add_child(sc, devinfo); + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); - return (devinfo); -} + devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); + devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); + devinfo->endnode = devinfo->startnode + devinfo->nodecnt; -static void -hdac_add_child(struct hdac_softc *sc, struct hdac_devinfo *devinfo) -{ - devinfo->dev = device_add_child(sc->dev, NULL, -1); - device_set_ivars(devinfo->dev, (void *)devinfo); - /* XXX - Print more information when booting verbose??? */ + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "\tFound %s FG nid=%d startnode=%d endnode=%d total=%d\n", + (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": + (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": + "unknown", nid, devinfo->startnode, devinfo->endnode, + devinfo->nodecnt); + ); + + if (devinfo->nodecnt > 0) + devinfo->widget = (struct hdac_widget *)malloc( + sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, + M_NOWAIT | M_ZERO); + else + devinfo->widget = NULL; + + if (devinfo->widget == NULL) { + device_printf(sc->dev, "unable to allocate widgets!\n"); + devinfo->endnode = devinfo->startnode; + devinfo->nodecnt = 0; + return; + } + + codec->num_fgs++; } static void @@ -1983,9 +1919,9 @@ hdac_widget_connection_parse(struct hdac_widget *w) cnid >= w->devinfo->endnode) { HDA_BOOTVERBOSE( device_printf(sc->dev, - "%s: GHOST: nid=%d j=%d " + "GHOST: nid=%d j=%d " "entnum=%d index=%d res=0x%08x\n", - __func__, nid, j, entnum, i, res); + nid, j, entnum, i, res); ); } if (CONN_RANGE(res, entnum, j) == 0) @@ -2003,11 +1939,12 @@ hdac_widget_connection_parse(struct hdac_widget *w) while (addcnid <= cnid) { if (w->nconns > max) { device_printf(sc->dev, - "%s: nid=%d: Adding %d: " + "Adding %d (nid=%d): " "Max connection reached! max=%d\n", - __func__, nid, addcnid, max + 1); + addcnid, nid, max + 1); goto getconns_out; } + w->connsenable[w->nconns] = 1; w->conns[w->nconns++] = addcnid++; } prevcnid = cnid; @@ -2015,33 +1952,112 @@ hdac_widget_connection_parse(struct hdac_widget *w) } getconns_out: - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: %s: nid=%d entries=%d found=%d\n", - __func__, nid, ents, w->nconns); - ); return; } static uint32_t +hdac_widget_pin_patch(uint32_t config, const char *str) +{ + char buf[256]; + char *key, *value, *rest, *bad; + int ival, i; + + strlcpy(buf, str, sizeof(buf)); + rest = buf; + while ((key = strsep(&rest, "=")) != NULL) { + value = strsep(&rest, " \t"); + if (value == NULL) + break; + ival = strtol(value, &bad, 10); + if (strcmp(key, "seq") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); + } else if (strcmp(key, "as") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); + } else if (strcmp(key, "misc") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & + HDA_CONFIG_DEFAULTCONF_MISC_MASK); + } else if (strcmp(key, "color") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & + HDA_CONFIG_DEFAULTCONF_COLOR_MASK); + }; + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_COLORS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); + break; + } + } + } else if (strcmp(key, "ctype") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); + } else if (strcmp(key, "device") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); + continue; + }; + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_DEVS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); + break; + } + } + } else if (strcmp(key, "loc") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & + HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); + } else if (strcmp(key, "conn") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + continue; + }; + for (i = 0; i < 4; i++) { + if (strcasecmp(HDA_CONNS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); + break; + } + } + } + } + return (config); +} + +static uint32_t hdac_widget_pin_getconfig(struct hdac_widget *w) { struct hdac_softc *sc; uint32_t config, orig, id; nid_t cad, nid; + char buf[32]; + const char *res = NULL, *patch = NULL; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; - id = hdac_codec_id(w->devinfo); + id = hdac_codec_id(w->devinfo->codec); config = hdac_command(sc, HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), cad); orig = config; - /* - * XXX REWRITE!!!! Don't argue! + HDA_BOOTVERBOSE( + hdac_dump_pin_config(w, orig); + ); + + /* XXX: Old patches require complete review. + * Now they may create more problem then solve due to + * incorrect associations. */ if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { switch (nid) { @@ -2063,14 +2079,6 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) * Super broken BIOS */ switch (nid) { - case 20: - break; - case 21: - break; - case 22: - break; - case 23: - break; case 24: /* MIC1 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; @@ -2091,12 +2099,6 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; break; - case 30: - break; - case 31: - break; - default: - break; } } else if (id == HDA_CODEC_ALC883 && (sc->pci_subvendor == MSI_MS034A_SUBVENDOR || @@ -2114,8 +2116,6 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; - default: - break; } } else if (id == HDA_CODEC_CXVENICE && sc->pci_subvendor == HP_V3000_SUBVENDOR) { @@ -2136,8 +2136,6 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; - default: - break; } } else if (id == HDA_CODEC_CXWAIKIKI && sc->pci_subvendor == HP_DV5000_SUBVENDOR) { @@ -2147,8 +2145,6 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; break; - default: - break; } } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == ASUS_W6F_SUBVENDOR) { @@ -2175,8 +2171,6 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); break; - default: - break; } } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == UNIWILL_9075_SUBVENDOR) { @@ -2187,42 +2181,66 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); break; - default: - break; } - } else if (id == HDA_CODEC_AD1986A && + } + + /* New patches */ + if (id == HDA_CODEC_AD1986A && (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR || sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR)) { switch (nid) { - case 28: /* LINE */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + case 28: /* 5.1 out => 2.0 out + 2 inputs */ + patch = "device=Line-in as=8 seq=1"; break; - case 29: /* MIC */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + case 29: + patch = "device=Mic as=8 seq=2"; break; - default: + case 31: /* Lot of inputs configured with as=15 and unusable */ + patch = "as=8 seq=3"; + break; + case 32: + patch = "as=8 seq=4"; + break; + case 34: + patch = "as=8 seq=5"; + break; + case 36: + patch = "as=8 seq=6"; + break; + } + } else if (id == HDA_CODEC_ALC260 && + HDA_DEV_MATCH(SONY_S5_SUBVENDOR, sc->pci_subvendor)) { + switch (nid) { + case 16: + patch = "seq=15 device=Headphones"; break; } } else if (id == HDA_CODEC_ALC268 && HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor)) { switch (nid) { case 28: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - default: + patch = "device=CD conn=fixed"; break; } } + if (patch != NULL) + config = hdac_widget_pin_patch(config, patch); + + snprintf(buf, sizeof(buf), "cad%u.nid%u.config", cad, nid); + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), buf, &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + config = strtol(res + 2, NULL, 16); + } else { + config = hdac_widget_pin_patch(config, res); + } + } + HDA_BOOTVERBOSE( if (config != orig) device_printf(sc->dev, - "HDA_DEBUG: Pin config nid=%u 0x%08x -> 0x%08x\n", + "Patching pin config nid=%u 0x%08x -> 0x%08x\n", nid, orig, config); ); @@ -2239,7 +2257,7 @@ hdac_widget_pin_getcaps(struct hdac_widget *w) sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; - id = hdac_codec_id(w->devinfo); + id = hdac_codec_id(w->devinfo->codec); caps = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); @@ -2248,7 +2266,7 @@ hdac_widget_pin_getcaps(struct hdac_widget *w) HDA_BOOTVERBOSE( if (caps != orig) device_printf(sc->dev, - "HDA_DEBUG: Pin caps nid=%u 0x%08x -> 0x%08x\n", + "Patching pin caps nid=%u 0x%08x -> 0x%08x\n", nid, orig, caps); ); @@ -2260,7 +2278,7 @@ hdac_widget_pin_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t config, pincap; - char *devstr, *connstr; + const char *devstr, *connstr; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; @@ -2271,18 +2289,8 @@ hdac_widget_pin_parse(struct hdac_widget *w) w->wclass.pin.cap = pincap; w->wclass.pin.ctrl = hdac_command(sc, - HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) & - ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad); - if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) - w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; - if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) - w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) - w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { w->param.eapdbtl = hdac_command(sc, HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); @@ -2291,74 +2299,11 @@ hdac_widget_pin_parse(struct hdac_widget *w) } else w->param.eapdbtl = HDAC_INVALID; - switch (config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: - devstr = "line out"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: - devstr = "speaker"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: - devstr = "headphones out"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: - devstr = "CD"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: - devstr = "SPDIF out"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: - devstr = "digital (other) out"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE: - devstr = "modem, line side"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET: - devstr = "modem, handset side"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: - devstr = "line in"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: - devstr = "AUX"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: - devstr = "Mic in"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY: - devstr = "telephony"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: - devstr = "SPDIF in"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: - devstr = "digital (other) in"; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER: - devstr = "other"; - break; - default: - devstr = "unknown"; - break; - } + devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> + HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; - switch (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) { - case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK: - connstr = "jack"; - break; - case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE: - connstr = "none"; - break; - case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED: - connstr = "fixed"; - break; - case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH: - connstr = "jack / fixed"; - break; - default: - connstr = "unknown"; - break; - } + connstr = HDA_CONNS[(config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT]; strlcat(w->name, ": ", sizeof(w->name)); strlcat(w->name, devstr, sizeof(w->name)); @@ -2367,6 +2312,64 @@ hdac_widget_pin_parse(struct hdac_widget *w) strlcat(w->name, ")", sizeof(w->name)); } +static uint32_t +hdac_widget_getcaps(struct hdac_widget *w, int *waspin) +{ + struct hdac_softc *sc; + uint32_t caps, orig, id; + nid_t cad, nid, beeper = -1; + + sc = w->devinfo->codec->sc; + cad = w->devinfo->codec->cad; + nid = w->nid; + id = hdac_codec_id(w->devinfo->codec); + + caps = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), + cad); + orig = caps; + + /* On some codecs beeper is an input pin, but it is not recordable + alone. Also most of BIOSes does not declare beeper pin. + Change beeper pin node type to beeper to help parser. */ + *waspin = 0; + switch (id) { + case HDA_CODEC_AD1988: + case HDA_CODEC_AD1988B: + beeper = 26; + break; + case HDA_CODEC_ALC260: + beeper = 23; + break; + case HDA_CODEC_ALC262: + case HDA_CODEC_ALC268: + case HDA_CODEC_ALC880: + case HDA_CODEC_ALC882: + case HDA_CODEC_ALC883: + case HDA_CODEC_ALC885: + case HDA_CODEC_ALC888: + case HDA_CODEC_ALC889: + beeper = 29; + break; + } + if (nid == beeper) { + caps &= ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; + caps |= HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; + *waspin = 1; + } + + HDA_BOOTVERBOSE( + if (caps != orig) { + device_printf(sc->dev, + "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", + nid, orig, caps); + } + ); + + return (caps); +} + static void hdac_widget_parse(struct hdac_widget *w) { @@ -2376,9 +2379,8 @@ hdac_widget_parse(struct hdac_widget *w) nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; - wcap = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), - cad); + wcap = hdac_widget_getcaps(w, &w->waspin); + w->param.widget_cap = wcap; w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); @@ -2417,13 +2419,6 @@ hdac_widget_parse(struct hdac_widget *w) strlcpy(w->name, typestr, sizeof(w->name)); - if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(wcap)) { - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), - cad); - DELAY(1000); - } - hdac_widget_connection_parse(w); if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { @@ -2515,38 +2510,42 @@ hda_poll_channel(struct hdac_chan *ch) return (1); } -#define hda_chan_active(sc) (((sc)->play.flags | (sc)->rec.flags) & \ - HDAC_CHN_RUNNING) - static void hda_poll_callback(void *arg) { struct hdac_softc *sc = arg; uint32_t trigger; + int i, active = 0; if (sc == NULL) return; hdac_lock(sc); - if (sc->polling == 0 || hda_chan_active(sc) == 0) { + if (sc->polling == 0) { hdac_unlock(sc); return; } trigger = 0; - trigger |= (hda_poll_channel(&sc->play) != 0) ? HDAC_TRIGGER_PLAY : 0; - trigger |= (hda_poll_channel(&sc->rec)) != 0 ? HDAC_TRIGGER_REC : 0; + for (i = 0; i < sc->num_chans; i++) { + if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) + continue; + active = 1; + if (hda_poll_channel(&sc->chans[i])) + trigger |= (1 << i); + } /* XXX */ - callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/, - hda_poll_callback, sc); + if (active) + callout_reset(&sc->poll_hda, sc->poll_ticks, + hda_poll_callback, sc); hdac_unlock(sc); - if (trigger & HDAC_TRIGGER_PLAY) - chn_intr(sc->play.c); - if (trigger & HDAC_TRIGGER_REC) - chn_intr(sc->rec.c); + for (i = 0; i < sc->num_chans; i++) { + if (trigger & (1 << i)) + chn_intr(sc->chans[i].c); + } } static int @@ -2635,6 +2634,45 @@ hdac_poll_callback(void *arg) } static void +hdac_poll_reinit(struct hdac_softc *sc) +{ + int i, pollticks, min = 1000000; + struct hdac_chan *ch; + + for (i = 0; i < sc->num_chans; i++) { + if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) + continue; + ch = &sc->chans[i]; + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->b) * + sndbuf_getspd(ch->b)); + pollticks >>= 1; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks=%d < 1 !\n", + __func__, pollticks); + ); + pollticks = 1; + } + if (min > pollticks) + min = pollticks; + } + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, min); + ); + sc->poll_ticks = min; + if (min == 1000000) + callout_stop(&sc->poll_hda); + else + callout_reset(&sc->poll_hda, 1, hda_poll_callback, sc); +} + +static void hdac_stream_stop(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; @@ -2647,48 +2685,12 @@ hdac_stream_stop(struct hdac_chan *ch) ch->flags &= ~HDAC_CHN_RUNNING; - if (sc->polling != 0) { - int pollticks; + if (sc->polling != 0) + hdac_poll_reinit(sc); - if (hda_chan_active(sc) == 0) { - callout_stop(&sc->poll_hda); - sc->poll_ticks = 1; - } else { - if (sc->play.flags & HDAC_CHN_RUNNING) - ch = &sc->play; - else - ch = &sc->rec; - pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->b) * - sndbuf_getspd(ch->b)); - pollticks >>= 2; - if (pollticks > hz) - pollticks = hz; - if (pollticks < 1) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks=%d < 1 !\n", - __func__, pollticks); - ); - pollticks = 1; - } - if (pollticks > sc->poll_ticks) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks %d -> %d\n", - __func__, sc->poll_ticks, - pollticks); - ); - sc->poll_ticks = pollticks; - callout_reset(&sc->poll_hda, 1, - hda_poll_callback, sc); - } - } - } else { - ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); - ctl &= ~(1 << (ch->off >> 5)); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); - } + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << (ch->off >> 5)); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); } static void @@ -2697,52 +2699,19 @@ hdac_stream_start(struct hdac_chan *ch) struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; - if (sc->polling != 0) { - int pollticks; + ch->flags |= HDAC_CHN_RUNNING; - pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b)); - pollticks >>= 2; - if (pollticks > hz) - pollticks = hz; - if (pollticks < 1) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks=%d < 1 !\n", - __func__, pollticks); - ); - pollticks = 1; - } - if (hda_chan_active(sc) == 0 || pollticks < sc->poll_ticks) { - HDA_BOOTVERBOSE( - if (hda_chan_active(sc) == 0) { - device_printf(sc->dev, - "%s: pollticks=%d\n", - __func__, pollticks); - } else { - device_printf(sc->dev, - "%s: pollticks %d -> %d\n", - __func__, sc->poll_ticks, - pollticks); - } - ); - sc->poll_ticks = pollticks; - callout_reset(&sc->poll_hda, 1, hda_poll_callback, - sc); - } - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - ctl |= HDAC_SDCTL_RUN; - } else { - ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); - ctl |= 1 << (ch->off >> 5); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | - HDAC_SDCTL_RUN; - } - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + if (sc->polling != 0) + hdac_poll_reinit(sc); - ch->flags |= HDAC_CHN_RUNNING; + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << (ch->off >> 5); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); } static void @@ -2802,19 +2771,14 @@ hdac_bdl_setup(struct hdac_chan *ch) addr = (uint64_t)sndbuf_getbufaddr(ch->b); bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; - if (sc->polling != 0) { - blksz = ch->blksz * ch->blkcnt; - blkcnt = 1; - } else { - blksz = ch->blksz; - blkcnt = ch->blkcnt; - } + blksz = ch->blksz; + blkcnt = ch->blkcnt; for (i = 0; i < blkcnt; i++, bdle++) { bdle->addrl = (uint32_t)addr; bdle->addrh = (uint32_t)(addr >> 32); bdle->len = blksz; - bdle->ioc = 1 ^ sc->polling; + bdle->ioc = 1; addr += blksz; } @@ -2881,32 +2845,36 @@ hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, nid_t nid, cad; int lmute, rmute; - if (ctl == NULL || ctl->widget == NULL || - ctl->widget->devinfo == NULL || - ctl->widget->devinfo->codec == NULL || - ctl->widget->devinfo->codec->sc == NULL) - return; - sc = ctl->widget->devinfo->codec->sc; cad = ctl->widget->devinfo->codec->cad; nid = ctl->widget->nid; - if (mute == HDA_AMP_MUTE_DEFAULT) { + /* Save new values if valid. */ + if (mute != HDA_AMP_MUTE_DEFAULT) + ctl->muted = mute; + if (left != HDA_AMP_VOL_DEFAULT) + ctl->left = left; + if (right != HDA_AMP_VOL_DEFAULT) + ctl->right = right; + /* Prepare effective values */ + if (ctl->forcemute) { + lmute = 1; + rmute = 1; + left = 0; + right = 0; + } else { lmute = HDA_AMP_LEFT_MUTED(ctl->muted); rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); - } else { - lmute = HDA_AMP_LEFT_MUTED(mute); - rmute = HDA_AMP_RIGHT_MUTED(mute); + left = ctl->left; + right = ctl->right; } - + /* Apply effective values */ if (ctl->dir & HDA_CTL_OUT) hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, lmute, rmute, left, right, 0); if (ctl->dir & HDA_CTL_IN) - hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, lmute, rmute, left, right, 1); - ctl->left = left; - ctl->right = right; } static void @@ -3074,20 +3042,26 @@ static void * hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, struct pcm_channel *c, int dir) { - struct hdac_devinfo *devinfo = data; + struct hdac_pcm_devinfo *pdevinfo = data; + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_chan *ch; + int i, ord = 0, chid; hdac_lock(sc); + + chid = (dir == PCMDIR_PLAY)?pdevinfo->play:pdevinfo->rec; + ch = &sc->chans[chid]; + for (i = 0; i < sc->num_chans && i < chid; i++) { + if (ch->dir == sc->chans[i].dir) + ord++; + } if (dir == PCMDIR_PLAY) { - ch = &sc->play; - ch->off = (sc->num_iss + devinfo->function.audio.playcnt) << 5; - devinfo->function.audio.playcnt++; + ch->off = (sc->num_iss + ord) << 5; } else { - ch = &sc->rec; - ch->off = devinfo->function.audio.reccnt << 5; - devinfo->function.audio.reccnt++; + ch->off = ord << 5; } + if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { ch->caps.minspeed = ch->caps.maxspeed = 48000; ch->pcmrates[0] = 48000; @@ -3102,9 +3076,8 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, ch->dir = dir; ch->b = b; ch->c = c; - ch->devinfo = devinfo; - ch->blksz = sc->chan_size / sc->chan_blkcnt; - ch->blkcnt = sc->chan_blkcnt; + ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; + ch->blkcnt = pdevinfo->chan_blkcnt; hdac_unlock(sc); if (hdac_bdl_alloc(ch) != 0) { @@ -3114,7 +3087,7 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, if (sndbuf_alloc(ch->b, sc->chan_dmat, (sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0, - sc->chan_size) != 0) + pdevinfo->chan_size) != 0) return (NULL); return (ch); @@ -3163,11 +3136,18 @@ static void hdac_stream_setup(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; + struct hdac_audio_as *as = &ch->devinfo->function.audio.as[ch->as]; struct hdac_widget *w; - int i, chn, totalchn; + int i, chn, totalchn, c; nid_t cad = ch->devinfo->codec->cad; - uint16_t fmt; + uint16_t fmt, dfmt; + HDA_BOOTVERBOSE( + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->fmt, ch->spd); + ); fmt = 0; if (ch->fmt & AFMT_S16_LE) fmt |= ch->bit16 << 4; @@ -3185,37 +3165,53 @@ hdac_stream_setup(struct hdac_chan *ch) } } - if (ch->fmt & AFMT_STEREO) { + if (ch->fmt & (AFMT_STEREO | AFMT_AC3)) { fmt |= 1; totalchn = 2; } else totalchn = 1; HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); + + dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; + if (ch->fmt & AFMT_AC3) + dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; chn = 0; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(ch->devinfo, ch->io[i]); if (w == NULL) continue; + + if (as->hpredir >= 0 && i == as->pincnt) + chn = 0; HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: PCMDIR_%s: Stream setup nid=%d " - "fmt=0x%08x\n", + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup nid=%d: " + "fmt=0x%04x, dfmt=0x%04x\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->io[i], fmt); + ch->io[i], fmt, dfmt); ); hdac_command(sc, HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); - if (ch->dir == PCMDIR_REC) + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { hdac_command(sc, - HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], - (chn < totalchn) ? ((ch->sid << 4) | chn) : 0), + HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], dfmt), cad); - else - hdac_command(sc, - HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], - ch->sid << 4), cad); + } + /* If HP redirection is enabled, but failed to use same + DAC make last DAC one to duplicate first one. */ + if (as->hpredir >= 0 && i == as->pincnt) { + c = (ch->sid << 4); + } else if (chn >= totalchn) { + /* This is until OSS will support multichannel. + Should be: c = 0; to disable unused DAC */ + c = (ch->sid << 4); + }else { + c = (ch->sid << 4) | chn; + } + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], c), cad); chn += HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(w->param.widget_cap) ? 2 : 1; @@ -3265,9 +3261,8 @@ static int hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct hdac_chan *ch = data; - struct hdac_softc *sc = ch->devinfo->codec->sc; - hdac_channel_setfragments(obj, data, blksz, sc->chan_blkcnt); + hdac_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); return (ch->blksz); } @@ -3276,12 +3271,21 @@ static void hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) { struct hdac_devinfo *devinfo = ch->devinfo; + struct hdac_widget *w; nid_t cad = devinfo->codec->cad; int i; hdac_stream_stop(ch); for (i = 0; ch->io[i] != -1; i++) { + w = hdac_widget_get(ch->devinfo, ch->io[i]); + if (w == NULL) + continue; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + hdac_command(sc, + HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], 0), + cad); + } hdac_command(sc, HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], 0), cad); @@ -3371,191 +3375,124 @@ static kobj_method_t hdac_channel_methods[] = { }; CHANNEL_DECLARE(hdac_channel); -static void -hdac_jack_poll_callback(void *arg) -{ - struct hdac_devinfo *devinfo = arg; - struct hdac_softc *sc; - - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) - return; - sc = devinfo->codec->sc; - hdac_lock(sc); - if (sc->poll_ival == 0) { - hdac_unlock(sc); - return; - } - hdac_hp_switch_handler(devinfo); - callout_reset(&sc->poll_jack, sc->poll_ival, - hdac_jack_poll_callback, devinfo); - hdac_unlock(sc); -} - static int hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) { - struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; uint32_t mask, recmask, id; int i, j, softpcmvol; - nid_t cad; hdac_lock(sc); + /* Make sure that in case of soft volume it won't stay muted. */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + pdevinfo->left[i] = 100; + pdevinfo->right[i] = 100; + } + mask = 0; recmask = 0; + id = hdac_codec_id(devinfo->codec); - id = hdac_codec_id(devinfo); - cad = devinfo->codec->cad; - for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { - if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model, - sc->pci_subvendor) && hdac_hp_switch[i].id == id)) - continue; - w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); - if (w == NULL || w->enable == 0 || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (hdac_hp_switch[i].polling != 0) - callout_reset(&sc->poll_jack, 1, - hdac_jack_poll_callback, devinfo); - else if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) - hdac_command(sc, - HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, - HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | - HDAC_UNSOLTAG_EVENT_HP), cad); - else - continue; - hdac_hp_switch_handler(devinfo); - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: Enabling headphone/speaker " - "audio routing switching:\n"); - device_printf(sc->dev, - "HDA_DEBUG: \tindex=%d nid=%d " - "pci_subvendor=0x%08x " - "codec=0x%08x [%s]\n", - i, w->nid, sc->pci_subvendor, id, - (hdac_hp_switch[i].polling != 0) ? "POLL" : - "UNSOL"); - ); - break; - } - for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { - if (!(HDA_DEV_MATCH(hdac_eapd_switch[i].model, - sc->pci_subvendor) && - hdac_eapd_switch[i].id == id)) - continue; - w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); - if (w == NULL || w->enable == 0) - break; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - w->param.eapdbtl == HDAC_INVALID) - break; - mask |= SOUND_MASK_OGAIN; - break; - } - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - mask |= w->ctlflags; - if (!(w->pflags & HDA_ADC_RECSEL)) - continue; - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) + /* Declate EAPD as ogain control. */ + if (pdevinfo->play >= 0) { + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID || + w->bindas != sc->chans[pdevinfo->play].as) continue; - recmask |= cw->ctlflags; + mask |= SOUND_MASK_OGAIN; + break; } } - if (!(mask & SOUND_MASK_PCM)) { - softpcmvol = 1; - mask |= SOUND_MASK_PCM; - } else - softpcmvol = (devinfo->function.audio.quirks & - HDA_QUIRK_SOFTPCMVOL) ? 1 : 0; - + /* Declare volume controls assigned to this association. */ i = 0; ctl = NULL; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->widget == NULL || ctl->enable == 0) - continue; - if (!(ctl->ossmask & SOUND_MASK_PCM)) + if (ctl->enable == 0) continue; - if (ctl->step > 0) - break; - } - - if (softpcmvol == 1 || ctl == NULL) { - pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL); - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: %s Soft PCM volume\n", - (softpcmvol == 1) ? - "Forcing" : "Enabling"); - ); - i = 0; - /* - * XXX Temporary quirk for STAC9220, until the parser - * become smarter. - */ - if (id == HDA_CODEC_STAC9220) { - mask |= SOUND_MASK_VOLUME; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != - NULL) { - if (ctl->widget == NULL || ctl->enable == 0) + if ((pdevinfo->play >= 0 && + ctl->widget->bindas == sc->chans[pdevinfo->play].as) || + (pdevinfo->rec >= 0 && + ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || + (ctl->widget->bindas == -2 && pdevinfo->index == 0)) + mask |= ctl->ossmask; + } + + /* Declare record sources available to this association. */ + if (pdevinfo->rec >= 0) { + struct hdac_chan *ch = &sc->chans[pdevinfo->rec]; + for (i = 0; ch->io[i] != -1; i++) { + w = hdac_widget_get(devinfo, ch->io[i]); + if (w == NULL || w->enable == 0) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) continue; - if (ctl->widget->nid == 11 && ctl->index == 0) { - ctl->ossmask = SOUND_MASK_VOLUME; - ctl->ossval = 100 | (100 << 8); - } else - ctl->ossmask &= ~SOUND_MASK_VOLUME; - } - } else if (id == HDA_CODEC_STAC9221) { - mask |= SOUND_MASK_VOLUME; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != - NULL) { - if (ctl->widget == NULL) + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) continue; - if (ctl->widget->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && - ctl->index == 0 && (ctl->widget->nid == 2 || - ctl->widget->enable != 0)) { - ctl->enable = 1; - ctl->ossmask = SOUND_MASK_VOLUME; - ctl->ossval = 100 | (100 << 8); - } else if (ctl->enable == 0) + if (cw->bindas != sc->chans[pdevinfo->rec].as && + cw->bindas != -2) continue; - else - ctl->ossmask &= ~SOUND_MASK_VOLUME; + recmask |= cw->ossmask; } + } + } + + /* Declare soft PCM and master volume if needed. */ + if (pdevinfo->play >= 0) { + ctl = NULL; + if ((mask & SOUND_MASK_PCM) == 0 || + (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { + softpcmvol = 1; + mask |= SOUND_MASK_PCM; } else { - mix_setparentchild(m, SOUND_MIXER_VOLUME, - SOUND_MASK_PCM); - if (!(mask & SOUND_MASK_VOLUME)) - mix_setrealdev(m, SOUND_MIXER_VOLUME, - SOUND_MIXER_NONE); - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != - NULL) { - if (ctl->widget == NULL || ctl->enable == 0) + softpcmvol = 0; + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->bindas != sc->chans[pdevinfo->play].as && + (ctl->widget->bindas != -2 || pdevinfo->index != 0)) continue; - if (!HDA_FLAG_MATCH(ctl->ossmask, - SOUND_MASK_VOLUME | SOUND_MASK_PCM)) + if (!(ctl->ossmask & SOUND_MASK_PCM)) continue; - if (!(ctl->mute == 1 && ctl->step == 0)) - ctl->enable = 0; + if (ctl->step > 0) + break; } } + + if (softpcmvol == 1 || ctl == NULL) { + pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "%s Soft PCM volume\n", + (softpcmvol == 1) ? "Forcing" : "Enabling"); + ); + } + + if ((mask & SOUND_MASK_VOLUME) == 0) { + mask |= SOUND_MASK_VOLUME; + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "Forcing master volume with PCM\n"); + ); + } } - recmask &= ~(SOUND_MASK_PCM | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER | - SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_IGAIN | - SOUND_MASK_OGAIN); recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; @@ -3571,36 +3508,34 @@ static int hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { - struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; - uint32_t id, mute; - int lvol, rvol, mlvol, mrvol; - int i = 0; + uint32_t mute; + int lvol, rvol; + int i, j; hdac_lock(sc); + /* Save new values. */ + pdevinfo->left[dev] = left; + pdevinfo->right[dev] = right; + + /* 'ogain' is the special case implemented with EAPD. */ if (dev == SOUND_MIXER_OGAIN) { uint32_t orig; - /*if (left != right || !(left == 0 || left == 1)) { - hdac_unlock(sc); - return (-1); - }*/ - id = hdac_codec_id(devinfo); - for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { - if (HDA_DEV_MATCH(hdac_eapd_switch[i].model, - sc->pci_subvendor) && - hdac_eapd_switch[i].id == id) - break; - } - if (i >= HDAC_EAPD_SWITCH_LEN) { - hdac_unlock(sc); - return (-1); + w = NULL; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) + continue; + break; } - w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); - if (w == NULL || - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - w->param.eapdbtl == HDAC_INVALID) { + if (i >= devinfo->endnode) { hdac_unlock(sc); return (-1); } @@ -3612,8 +3547,6 @@ hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, if (orig != w->param.eapdbtl) { uint32_t val; - if (hdac_eapd_switch[i].hp_switch != 0) - hdac_hp_switch_handler(devinfo); val = w->param.eapdbtl; if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; @@ -3624,51 +3557,32 @@ hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, hdac_unlock(sc); return (left | (left << 8)); } - if (dev == SOUND_MIXER_VOLUME) - devinfo->function.audio.mvol = left | (right << 8); - - mlvol = devinfo->function.audio.mvol & 0x7f; - mrvol = (devinfo->function.audio.mvol >> 8) & 0x7f; - lvol = 0; - rvol = 0; + /* Recalculate all controls related to this OSS device. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->widget == NULL || ctl->enable == 0 || + if (ctl->enable == 0 || !(ctl->ossmask & (1 << dev))) continue; - switch (dev) { - case SOUND_MIXER_VOLUME: - lvol = ((ctl->ossval & 0x7f) * left) / 100; - lvol = (lvol * ctl->step) / 100; - rvol = (((ctl->ossval >> 8) & 0x7f) * right) / 100; - rvol = (rvol * ctl->step) / 100; - break; - default: - if (ctl->ossmask & SOUND_MASK_VOLUME) { - lvol = (left * mlvol) / 100; - lvol = (lvol * ctl->step) / 100; - rvol = (right * mrvol) / 100; - rvol = (rvol * ctl->step) / 100; - } else { - lvol = (left * ctl->step) / 100; - rvol = (right * ctl->step) / 100; + if (!((pdevinfo->play >= 0 && + ctl->widget->bindas == sc->chans[pdevinfo->play].as) || + (pdevinfo->rec >= 0 && + ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || + ctl->widget->bindas == -2)) + continue; + + lvol = 100; + rvol = 100; + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if (ctl->ossmask & (1 << j)) { + lvol = lvol * pdevinfo->left[j] / 100; + rvol = rvol * pdevinfo->right[j] / 100; } - ctl->ossval = left | (right << 8); - break; - } - mute = 0; - if (ctl->step < 1) { - mute |= (left == 0) ? HDA_AMP_MUTE_LEFT : - (ctl->muted & HDA_AMP_MUTE_LEFT); - mute |= (right == 0) ? HDA_AMP_MUTE_RIGHT : - (ctl->muted & HDA_AMP_MUTE_RIGHT); - } else { - mute |= (lvol == 0) ? HDA_AMP_MUTE_LEFT : - (ctl->muted & HDA_AMP_MUTE_LEFT); - mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : - (ctl->muted & HDA_AMP_MUTE_RIGHT); } + mute = (left == 0) ? HDA_AMP_MUTE_LEFT : 0; + mute |= (right == 0) ? HDA_AMP_MUTE_RIGHT : 0; + lvol = (lvol * ctl->step + 50) / 100; + rvol = (rvol * ctl->step + 50) / 100; hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); } hdac_unlock(sc); @@ -3676,54 +3590,108 @@ hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, return (left | (right << 8)); } -static int -hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +/* + * Commutate specified record source. + */ +static uint32_t +hdac_audio_ctl_recsel_comm(struct hdac_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) { - struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w, *cw; - struct hdac_softc *sc = devinfo->codec->sc; - uint32_t ret = src, target; - int i, j; + struct hdac_audio_ctl *ctl; + char buf[64]; + int i, muted; + uint32_t res = 0; - target = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (src & (1 << i)) { - target = 1 << i; + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->bindas == -1) + continue; + /* Call recursively to trace signal to it's source if needed. */ + if ((src & cw->ossmask) != 0) { + if (cw->ossdev < 0) { + res |= hdac_audio_ctl_recsel_comm(pdevinfo, src, + w->conns[i], depth + 1); + } else { + res |= cw->ossmask; + } + } + /* We have two special cases: mixers and others (selectors). */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { + ctl = hdac_audio_ctl_amp_get(devinfo, + w->nid, HDA_CTL_IN, i, 1); + if (ctl == NULL) + continue; + /* If we have input control on this node mute them + * according to requested sources. */ + muted = (src & cw->ossmask) ? 0 : 1; + if (muted != ctl->forcemute) { + ctl->forcemute = muted; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, + HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); + } + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "Recsel (%s): nid %d source %d %s\n", + hdac_audio_ctl_ossmixer_mask2allname( + src, buf, sizeof(buf)), + nid, i, muted?"mute":"unmute"); + ); + } else { + if (w->nconns == 1) + break; + if ((src & cw->ossmask) == 0) + continue; + /* If we found requested source - select it and exit. */ + hdac_widget_connection_select(w, i); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "Recsel (%s): nid %d source %d select\n", + hdac_audio_ctl_ossmixer_mask2allname( + src, buf, sizeof(buf)), + nid, i); + ); break; } } + return (res); +} + +static uint32_t +hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdac_devinfo *devinfo = pdevinfo->devinfo; + struct hdac_widget *w; + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_chan *ch; + int i; + uint32_t ret = 0xffffffff; hdac_lock(sc); - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); + /* Commutate requested recsrc for each ADC. */ + ch = &sc->chans[pdevinfo->rec]; + for (i = 0; ch->io[i] != -1; i++) { + w = hdac_widget_get(devinfo, ch->io[i]); if (w == NULL || w->enable == 0) continue; - if (!(w->pflags & HDA_ADC_RECSEL)) - continue; - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - if ((target == SOUND_MASK_VOLUME && - cw->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || - (target != SOUND_MASK_VOLUME && - cw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)) - continue; - if (cw->ctlflags & target) { - if (!(w->pflags & HDA_ADC_LOCKED)) - hdac_widget_connection_select(w, j); - ret = target; - j += w->nconns; - } - } + ret &= hdac_audio_ctl_recsel_comm(pdevinfo, src, ch->io[i], 0); } hdac_unlock(sc); - return (ret); + return ((ret == 0xffffffff)? 0 : ret); } static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { @@ -3762,7 +3730,9 @@ hdac_attach(device_t dev) uint16_t vendor; uint8_t v; - sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + device_printf(dev, "<HDA Driver Revision: %s>\n", HDA_DRV_TEST_REV); + + sc = device_get_softc(dev); sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); sc->dev = dev; sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; @@ -3780,7 +3750,7 @@ hdac_attach(device_t dev) TASK_INIT(&sc->unsolq_task, 0, hdac_unsolq_task, sc); - sc->poll_ticks = 1; + sc->poll_ticks = 1000000; sc->poll_ival = HDAC_POLL_INTERVAL; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "polling", &i) == 0 && i != 0) @@ -3788,26 +3758,6 @@ hdac_attach(device_t dev) else sc->polling = 0; - sc->chan_size = pcm_getbuffersize(dev, - HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); - - if (resource_int_value(device_get_name(dev), - device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { - i &= HDA_BLK_ALIGN; - if (i < HDA_BLK_MIN) - i = HDA_BLK_MIN; - sc->chan_blkcnt = sc->chan_size / i; - i = 0; - while (sc->chan_blkcnt >> i) - i++; - sc->chan_blkcnt = 1 << (i - 1); - if (sc->chan_blkcnt < HDA_BDL_MIN) - sc->chan_blkcnt = HDA_BDL_MIN; - else if (sc->chan_blkcnt > HDA_BDL_MAX) - sc->chan_blkcnt = HDA_BDL_MAX; - } else - sc->chan_blkcnt = HDA_BDL_DEFAULT; - result = bus_dma_tag_create(NULL, /* parent */ HDAC_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ @@ -3815,9 +3765,9 @@ hdac_attach(device_t dev) BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ - sc->chan_size, /* maxsize */ + HDA_BUFSZ_MAX, /* maxsize */ 1, /* nsegments */ - sc->chan_size, /* maxsegsz */ + HDA_BUFSZ_MAX, /* maxsegsz */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ @@ -3935,7 +3885,10 @@ hdac_attach(device_t dev) goto hdac_attach_fail; /* Quiesce everything */ - hdac_reset(sc); + HDA_BOOTVERBOSE( + device_printf(dev, "Reset controller...\n"); + ); + hdac_reset(sc, 1); /* Initialize the CORB and RIRB */ hdac_corb_init(sc); @@ -3965,7 +3918,8 @@ hdac_attach_fail: static void hdac_audio_parse(struct hdac_devinfo *devinfo) { - struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_codec *codec = devinfo->codec; + struct hdac_softc *sc = codec->sc; struct hdac_widget *w; uint32_t res; int i; @@ -3974,40 +3928,11 @@ hdac_audio_parse(struct hdac_devinfo *devinfo) cad = devinfo->codec->cad; nid = devinfo->nid; - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), cad); - - DELAY(100); - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); - - devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); - devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); - devinfo->endnode = devinfo->startnode + devinfo->nodecnt; - res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); devinfo->function.audio.gpio = res; HDA_BOOTVERBOSE( - device_printf(sc->dev, " Vendor: 0x%08x\n", - devinfo->vendor_id); - device_printf(sc->dev, " Device: 0x%08x\n", - devinfo->device_id); - device_printf(sc->dev, " Revision: 0x%08x\n", - devinfo->revision_id); - device_printf(sc->dev, " Stepping: 0x%08x\n", - devinfo->stepping_id); - device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", - sc->pci_subvendor); - device_printf(sc->dev, " Nodes: start=%d " - "endnode=%d total=%d\n", - devinfo->startnode, devinfo->endnode, devinfo->nodecnt); - device_printf(sc->dev, " CORB size: %d\n", sc->corb_size); - device_printf(sc->dev, " RIRB size: %d\n", sc->rirb_size); - device_printf(sc->dev, " Streams: ISS=%d OSS=%d BSS=%d\n", - sc->num_iss, sc->num_oss, sc->num_bss); device_printf(sc->dev, " GPIO: 0x%08x\n", devinfo->function.audio.gpio); device_printf(sc->dev, " NumGPIO=%d NumGPO=%d " @@ -4039,20 +3964,6 @@ hdac_audio_parse(struct hdac_devinfo *devinfo) cad); devinfo->function.audio.inamp_cap = res; - if (devinfo->nodecnt > 0) - devinfo->widget = (struct hdac_widget *)malloc( - sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, - M_NOWAIT | M_ZERO); - else - devinfo->widget = NULL; - - if (devinfo->widget == NULL) { - device_printf(sc->dev, "unable to allocate widgets!\n"); - devinfo->endnode = devinfo->startnode; - devinfo->nodecnt = 0; - return; - } - for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) @@ -4063,7 +3974,8 @@ hdac_audio_parse(struct hdac_devinfo *devinfo) w->enable = 1; w->selconn = -1; w->pflags = 0; - w->ctlflags = 0; + w->ossdev = -1; + w->bindas = -1; w->param.eapdbtl = HDAC_INVALID; hdac_widget_parse(w); } @@ -4141,7 +4053,7 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) /*if (offset > step) { HDA_BOOTVERBOSE( device_printf(sc->dev, - "HDA_DEBUG: BUGGY outamp: nid=%d " + "BUGGY outamp: nid=%d " "[offset=%d > step=%d]\n", w->nid, offset, step); ); @@ -4155,6 +4067,11 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->waspin) + ctls[cnt].ndir = HDA_CTL_IN; + else + ctls[cnt].ndir = HDA_CTL_OUT; ctls[cnt++].dir = HDA_CTL_OUT; } @@ -4166,7 +4083,7 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) /*if (offset > step) { HDA_BOOTVERBOSE( device_printf(sc->dev, - "HDA_DEBUG: BUGGY inamp: nid=%d " + "BUGGY inamp: nid=%d " "[offset=%d > step=%d]\n", w->nid, offset, step); ); @@ -4196,6 +4113,7 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; + ctls[cnt].ndir = HDA_CTL_IN; ctls[cnt++].dir = HDA_CTL_IN; } break; @@ -4214,6 +4132,11 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + ctls[cnt].ndir = HDA_CTL_OUT; + else + ctls[cnt].ndir = HDA_CTL_IN; ctls[cnt++].dir = HDA_CTL_IN; break; } @@ -4223,6 +4146,141 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) devinfo->function.audio.ctl = ctls; } +static void +hdac_audio_as_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_as *as; + struct hdac_widget *w; + int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; + + /* XXX This is redundant */ + max = 0; + for (j = 0; j < 16; j++) { + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) + != j) + continue; + max++; + if (j != 15) /* There could be many 1-pin assocs #15 */ + break; + } + } + + devinfo->function.audio.ascnt = max; + + if (max < 1) + return; + + as = (struct hdac_audio_as *)malloc( + sizeof(*as) * max, M_HDAC, M_ZERO | M_NOWAIT); + + if (as == NULL) { + /* Blekh! */ + device_printf(sc->dev, "unable to allocate assocs!\n"); + devinfo->function.audio.ascnt = 0; + return; + } + + for (i = 0; i < max; i++) { + as[i].hpredir = -1; + as[i].chan = -1; + } + + /* Scan associations skipping as=0. */ + cnt = 0; + for (j = 1; j < 16; j++) { + first = 16; + hpredir = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); + seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); + if (assoc != j) { + continue; + } + KASSERT(cnt < max, + ("%s: Associations owerflow (%d of %d)", + __func__, cnt, max)); + type = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + /* Get pin direction. */ + if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) + dir = HDA_CTL_OUT; + else + dir = HDA_CTL_IN; + /* If this is a first pin - create new association. */ + if (as[cnt].pincnt == 0) { + as[cnt].enable = 1; + as[cnt].index = j; + as[cnt].dir = dir; + } + if (seq < first) + first = seq; + /* Check association correctness. */ + if (as[cnt].pins[seq] != 0) { + device_printf(sc->dev, "%s: Duplicate pin %d (%d) " + "in association %d! Disabling association.\n", + __func__, seq, w->nid, j); + as[cnt].enable = 0; + } + if (dir != as[cnt].dir) { + device_printf(sc->dev, "%s: Pin %d has wrong " + "direction for association %d! Disabling " + "association.\n", + __func__, w->nid, j); + as[cnt].enable = 0; + } + /* Headphones with seq=15 may mean redirection. */ + if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && + seq == 15) + hpredir = 1; + as[cnt].pins[seq] = w->nid; + as[cnt].pincnt++; + /* Association 15 is a multiple unassociated pins. */ + if (j == 15) + cnt++; + } + if (j != 15 && as[cnt].pincnt > 0) { + if (hpredir && as[cnt].pincnt > 1) + as[cnt].hpredir = first; + cnt++; + } + } + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%d associations found\n", max); + for (i = 0; i < max; i++) { + device_printf(sc->dev, + "Association %d (%d) %s%s:\n", + i, as[i].index, (as[i].dir == HDA_CTL_IN)?"in":"out", + as[i].enable?"":" (disabled)"); + for (j = 0; j < 16; j++) { + if (as[i].pins[j] == 0) + continue; + device_printf(sc->dev, + " Pin nid=%d seq=%d\n", + as[i].pins[j], j); + } + } + ); + + devinfo->function.audio.as = as; +} + static const struct { uint32_t model; uint32_t id; @@ -4260,7 +4318,7 @@ static const struct { { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO1, 0 }, { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, + HDA_QUIRK_EAPDINV | HDA_QUIRK_SENSEINV, 0 }, { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, @@ -4276,9 +4334,7 @@ static const struct { { HDA_MATCH_ALL, HDA_CODEC_AD1988B, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, { HDA_MATCH_ALL, HDA_CODEC_CXVENICE, - 0, HDA_QUIRK_FORCESTEREO }, - { HDA_MATCH_ALL, HDA_CODEC_STACXXXX, - HDA_QUIRK_SOFTPCMVOL, 0 } + 0, HDA_QUIRK_FORCESTEREO } }; #define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) @@ -4286,11 +4342,10 @@ static void hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) { struct hdac_widget *w; - struct hdac_audio_ctl *ctl; uint32_t id, subvendor; int i; - id = hdac_codec_id(devinfo); + id = hdac_codec_id(devinfo->codec); subvendor = devinfo->codec->sc->pci_subvendor; /* @@ -4309,110 +4364,6 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) } switch (id) { - case HDA_CODEC_ALC260: - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) - continue; - if (w->nid != 5) - w->enable = 0; - } - if (subvendor == HP_XW4300_SUBVENDOR) { - ctl = hdac_audio_ctl_amp_get(devinfo, 16, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 17, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - } else if (subvendor == HP_3010_SUBVENDOR) { - ctl = hdac_audio_ctl_amp_get(devinfo, 17, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 21, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - } - break; - case HDA_CODEC_ALC262: - if (subvendor == HP_DC7700S_SUBVENDOR) { - ctl = hdac_audio_ctl_amp_get(devinfo, 21, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_PHONEOUT; - ctl->widget->ctlflags |= SOUND_MASK_PHONEOUT; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 22, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - } else if (subvendor == HP_DC7700_SUBVENDOR) { - ctl = hdac_audio_ctl_amp_get(devinfo, 22, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 27, 0, 1); - if (ctl != NULL && ctl->widget != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; - } - } - break; - case HDA_CODEC_ALC268: - if (HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, subvendor)) { - w = hdac_widget_get(devinfo, 29); - if (w != NULL) { - w->enable = 1; - w->type = - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET; - w->param.widget_cap &= - ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; - w->param.widget_cap |= - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; - strlcpy(w->name, "beep widget", sizeof(w->name)); - } - } - break; - case HDA_CODEC_ALC861: - ctl = hdac_audio_ctl_amp_get(devinfo, 21, 2, 1); - if (ctl != NULL) - ctl->muted = HDA_AMP_MUTE_ALL; - break; - case HDA_CODEC_ALC880: - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && - w->nid != 9 && w->nid != 29) { - w->enable = 0; - } else if (w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET && - w->nid == 29) { - w->type = - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET; - w->param.widget_cap &= - ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; - w->param.widget_cap |= - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; - strlcpy(w->name, "beep widget", sizeof(w->name)); - } - } - break; case HDA_CODEC_ALC883: /* * nid: 24/25 = External (jack) or Internal (fixed) Mic. @@ -4442,37 +4393,8 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) * nid: 26 = Line-in, leave it alone. */ break; - case HDA_CODEC_AD1981HD: - w = hdac_widget_get(devinfo, 11); - if (w != NULL && w->enable != 0 && w->nconns > 3) - w->selconn = 3; - if (subvendor == IBM_M52_SUBVENDOR) { - ctl = hdac_audio_ctl_amp_get(devinfo, 7, 0, 1); - if (ctl != NULL) - ctl->ossmask = SOUND_MASK_SPEAKER; - } - break; case HDA_CODEC_AD1986A: - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) - continue; - if (w->nid != 3) - w->enable = 0; - } - if (subvendor == ASUS_M2NPVMX_SUBVENDOR || - subvendor == ASUS_A8NVMCSM_SUBVENDOR) { - /* nid 28 is mic, nid 29 is line-in */ - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->selconn = 2; - w = hdac_widget_get(devinfo, 16); - if (w != NULL) - w->selconn = 1; - } else if (subvendor == ASUS_A8X_SUBVENDOR) { + if (subvendor == ASUS_A8X_SUBVENDOR) { /* * This is just plain ridiculous.. There * are several A8 series that share the same @@ -4488,798 +4410,1482 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) ~HDA_QUIRK_EAPDINV; } break; - case HDA_CODEC_AD1988: - case HDA_CODEC_AD1988B: - /*w = hdac_widget_get(devinfo, 12); - if (w != NULL) { - w->selconn = 1; - w->pflags |= HDA_ADC_LOCKED; - } - w = hdac_widget_get(devinfo, 13); - if (w != NULL) { - w->selconn = 4; - w->pflags |= HDA_ADC_LOCKED; - } - w = hdac_widget_get(devinfo, 14); - if (w != NULL) { - w->selconn = 2; - w->pflags |= HDA_ADC_LOCKED; - }*/ - ctl = hdac_audio_ctl_amp_get(devinfo, 57, 0, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_IGAIN; - ctl->widget->ctlflags |= SOUND_MASK_IGAIN; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 58, 0, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_IGAIN; - ctl->widget->ctlflags |= SOUND_MASK_IGAIN; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 60, 0, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_IGAIN; - ctl->widget->ctlflags |= SOUND_MASK_IGAIN; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 32, 0, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; - ctl->widget->ctlflags |= SOUND_MASK_MIC; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 32, 4, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; - ctl->widget->ctlflags |= SOUND_MASK_MIC; + } +} + +/* + * Trace path from DAC to pin. + */ +static nid_t +hdac_audio_trace_dac(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, + int dupseq, int min, int only, int depth) +{ + struct hdac_widget *w; + int i, im = -1; + nid_t m = 0, ret; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTVERBOSE( + if (!only) { + device_printf(devinfo->codec->sc->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); } - ctl = hdac_audio_ctl_amp_get(devinfo, 32, 1, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_LINE | SOUND_MASK_VOLUME; - ctl->widget->ctlflags |= SOUND_MASK_LINE; + ); + /* Use only unused widgets */ + if (w->bindas >= 0 && w->bindas != as) { + HDA_BOOTVERBOSE( + if (!only) { + device_printf(devinfo->codec->sc->dev, + " %*snid %d busy by association %d\n", + depth + 1, "", w->nid, w->bindas); + } + ); + return (0); + } + if (dupseq < 0) { + if (w->bindseqmask != 0) { + HDA_BOOTVERBOSE( + if (!only) { + device_printf(devinfo->codec->sc->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + } + ); + return (0); } - ctl = hdac_audio_ctl_amp_get(devinfo, 32, 7, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_SPEAKER | SOUND_MASK_VOLUME; - ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } else { + /* If this is headphones - allow duplicate first pin. */ + if (w->bindseqmask != 0 && + (w->bindseqmask & (1 << dupseq)) == 0) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + ); + return (0); } + } + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + /* Do not traverse input. AD1988 has digital monitor + for which we are not ready. */ break; - case HDA_CODEC_STAC9205: - if ((subvendor == DELL_V1500_SUBVENDOR) || - (subvendor == DELL_D630_SUBVENDOR)) { - w = hdac_widget_get(devinfo, 29); - if (w != NULL) - w->selconn = 1; - w = hdac_widget_get(devinfo, 30); - if (w != NULL) - w->selconn = 1; - } + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + /* If we are tracing HP take only dac of first pin. */ + if ((only == 0 || only == w->nid) && + (w->nid >= min) && (dupseq < 0 || w->nid == + devinfo->function.audio.as[as].dacs[dupseq])) + m = w->nid; break; - case HDA_CODEC_STAC9221: - /* - * Dell XPS M1210 need all DACs for each output jacks - */ - if (subvendor == DELL_XPSM1210_SUBVENDOR) + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) break; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + /* Fall */ + default: + /* Find reachable DACs with smallest nid respecting constraints. */ + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) continue; - if (w->nid != 2) - w->enable = 0; - } - break; - case HDA_CODEC_STAC9221D: - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) + if (w->selconn != -1 && w->selconn != i) continue; - if (w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && - w->nid != 6) - w->enable = 0; - + if ((ret = hdac_audio_trace_dac(devinfo, as, seq, + w->conns[i], dupseq, min, only, depth + 1)) != 0) { + if (m == 0 || ret < m) { + m = ret; + im = i; + } + if (only || dupseq >= 0) + break; + } } + if (m && only && ((w->nconns > 1 && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + w->selconn = im; break; - case HDA_CODEC_STAC9227: - w = hdac_widget_get(devinfo, 8); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 9); - if (w != NULL) - w->enable = 0; - break; - case HDA_CODEC_CXWAIKIKI: - if (subvendor == HP_DV5000_SUBVENDOR) { - w = hdac_widget_get(devinfo, 27); - if (w != NULL) - w->enable = 0; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 16, 0, 1); - if (ctl != NULL) - ctl->ossmask = SOUND_MASK_SKIP; - ctl = hdac_audio_ctl_amp_get(devinfo, 25, 0, 1); - if (ctl != NULL && ctl->childwidget != NULL && - ctl->childwidget->enable != 0) { - ctl->ossmask = SOUND_MASK_PCM | SOUND_MASK_VOLUME; - ctl->childwidget->ctlflags |= SOUND_MASK_PCM; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 25, 1, 1); - if (ctl != NULL && ctl->childwidget != NULL && - ctl->childwidget->enable != 0) { - ctl->ossmask = SOUND_MASK_LINE | SOUND_MASK_VOLUME; - ctl->childwidget->ctlflags |= SOUND_MASK_LINE; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 25, 2, 1); - if (ctl != NULL && ctl->childwidget != NULL && - ctl->childwidget->enable != 0) { - ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; - ctl->childwidget->ctlflags |= SOUND_MASK_MIC; - } - ctl = hdac_audio_ctl_amp_get(devinfo, 26, 0, 1); - if (ctl != NULL) { - ctl->ossmask = SOUND_MASK_SKIP; - /* XXX mixer \=rec mic broken.. why?!? */ - /* ctl->widget->ctlflags |= SOUND_MASK_MIC; */ + } + if (m && only) { + w->bindas = as; + w->bindseqmask |= (1 << seq); + } + HDA_BOOTVERBOSE( + if (!only) { + device_printf(devinfo->codec->sc->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, m); } + ); + return (m); +} + +/* + * Trace path from widget to ADC. + */ +static nid_t +hdac_audio_trace_adc(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, + int only, int depth) +{ + struct hdac_widget *w, *wc; + int i, j; + nid_t res = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + ); + /* Use only unused widgets */ + if (w->bindas >= 0 && w->bindas != as) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*snid %d busy by association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (0); + } + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + /* If we are tracing HP take only dac of first pin. */ + if (only == w->nid) + res = 1; break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ default: + /* Try to find reachable ADCs with specified nid. */ + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + wc = hdac_widget_get(devinfo, j); + if (wc == NULL || wc->enable == 0) + continue; + for (i = 0; i < wc->nconns; i++) { + if (wc->connsenable[i] == 0) + continue; + if (wc->conns[i] != nid) + continue; + if (hdac_audio_trace_adc(devinfo, as, seq, + j, only, depth + 1) != 0) { + res = 1; + if (((wc->nconns > 1 && + wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) && + wc->selconn == -1) + wc->selconn = i; + } + } + } break; } + if (res) { + w->bindas = as; + w->bindseqmask |= (1 << seq); + } + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, res); + ); + return (res); } -static int -hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *devinfo) +/* + * Erase trace path of the specified association. + */ +static void +hdac_audio_undo_trace(struct hdac_devinfo *devinfo, int as, int seq) { - int *dev = &devinfo->function.audio.ossidx; - - while (*dev < SOUND_MIXER_NRDEVICES) { - switch (*dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_BASS: - case SOUND_MIXER_TREBLE: - case SOUND_MIXER_PCM: - case SOUND_MIXER_SPEAKER: - case SOUND_MIXER_LINE: - case SOUND_MIXER_MIC: - case SOUND_MIXER_CD: - case SOUND_MIXER_RECLEV: - case SOUND_MIXER_IGAIN: - case SOUND_MIXER_OGAIN: /* reserved for EAPD switch */ - (*dev)++; - break; - default: - return (*dev)++; - break; + struct hdac_widget *w; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == as) { + if (seq >= 0) { + w->bindseqmask &= ~(1 << seq); + if (w->bindseqmask == 0) { + w->bindas = -1; + w->selconn = -1; + } + } else { + w->bindas = -1; + w->bindseqmask = 0; + w->selconn = -1; + } } } +} - return (-1); +/* + * Trace association path from DAC to output + */ +static int +hdac_audio_trace_as_out(struct hdac_devinfo *devinfo, int as, int seq) +{ + struct hdac_audio_as *ases = devinfo->function.audio.as; + int i, hpredir; + nid_t min, res; + + /* Find next pin */ + for (i = seq; ases[as].pins[i] == 0 && i < 16; i++) + ; + /* Check if there is no any left. If so - we succeded. */ + if (i == 16) + return (1); + + hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; + min = 0; + res = 0; + do { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Tracing pin %d with min nid %d", + ases[as].pins[i], min); + if (hpredir >= 0) + printf(" and hpredir %d\n", hpredir); + else + printf("\n"); + ); + /* Trace this pin taking min nid into account. */ + res = hdac_audio_trace_dac(devinfo, as, i, + ases[as].pins[i], hpredir, min, 0, 0); + if (res == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Unable to trace pin %d seq %d with min " + "nid %d hpredir %d\n", + ases[as].pins[i], i, min, hpredir); + ); + return (0); + } + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Pin %d traced to DAC %d%s\n", + ases[as].pins[i], res, + ases[as].fakeredir?" with fake redirection":""); + ); + /* Trace again to mark the path */ + hdac_audio_trace_dac(devinfo, as, i, + ases[as].pins[i], hpredir, min, res, 0); + ases[as].dacs[i] = res; + /* We succeded, so call next. */ + if (hdac_audio_trace_as_out(devinfo, as, i + 1)) + return (1); + /* If next failed, we should retry with next min */ + hdac_audio_undo_trace(devinfo, as, i); + ases[as].dacs[i] = 0; + min = res + 1; + } while (1); } +/* + * Trace association path from input to ADC + */ static int -hdac_widget_find_dac_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +hdac_audio_trace_as_in(struct hdac_devinfo *devinfo, int as) { + struct hdac_audio_as *ases = devinfo->function.audio.as; struct hdac_widget *w; - int i, ret = 0; + int i, j, k; - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: - w->pflags |= HDA_DAC_PATH; - ret = 1; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: - for (i = 0; i < w->nconns; i++) { - if (hdac_widget_find_dac_path(devinfo, - w->conns[i], depth + 1) != 0) { - if (w->selconn == -1) - w->selconn = i; - ret = 1; - w->pflags |= HDA_DAC_PATH; + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdac_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->bindas >= 0 && w->bindas != as) + continue; + + /* Find next pin */ + for (i = 0; i < 16; i++) { + if (ases[as].pins[i] == 0) + continue; + + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Tracing pin %d to ADC %d\n", + ases[as].pins[i], j); + ); + /* Trace this pin taking goal into account. */ + if (hdac_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], j, 0) == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Unable to trace pin %d to ADC %d\n", + ases[as].pins[i], j); + ); + hdac_audio_undo_trace(devinfo, as, -1); + for (k = 0; k < 16; k++) + ases[as].dacs[k] = 0; + break; } + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Traced to ADC %d\n", + j); + ); + ases[as].dacs[i] = j; } - break; - default: - break; + if (i == 16) + return (1); } - return (ret); + return (0); } -static int -hdac_widget_find_adc_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +/* + * Trace input monitor path from mixer to output association. + */ +static nid_t +hdac_audio_trace_to_out(struct hdac_devinfo *devinfo, nid_t nid, int depth) { - struct hdac_widget *w; - int i, conndev, ret = 0; + struct hdac_audio_as *ases = devinfo->function.audio.as; + struct hdac_widget *w, *wc; + int i, j; + nid_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + ); + /* Use only unused widgets */ + if (depth > 0 && w->bindas != -1) { + if (w->bindas < 0 || ases[w->bindas].dir == HDA_CTL_OUT) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*snid %d found output association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (1); + } else { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*snid %d busy by input association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (0); + } + } + switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: - for (i = 0; i < w->nconns; i++) { - if (hdac_widget_find_adc_path(devinfo, w->conns[i], - depth + 1) != 0) { - if (w->selconn == -1) - w->selconn = i; - w->pflags |= HDA_ADC_PATH; - ret = 1; - } - } + /* Do not traverse input. AD1988 has digital monitor + for which we are not ready. */ break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - conndev = w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - if (HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && - (conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_CD || - conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN || - conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN)) { - w->pflags |= HDA_ADC_PATH; - ret = 1; + if (depth > 0) + break; + /* Fall */ + default: + /* Try to find reachable ADCs with specified nid. */ + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + wc = hdac_widget_get(devinfo, j); + if (wc == NULL || wc->enable == 0) + continue; + for (i = 0; i < wc->nconns; i++) { + if (wc->connsenable[i] == 0) + continue; + if (wc->conns[i] != nid) + continue; + if (hdac_audio_trace_to_out(devinfo, + j, depth + 1) != 0) { + res = 1; + if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + wc->selconn == -1) + wc->selconn = i; + } + } } break; - /*case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: - if (w->pflags & HDA_DAC_PATH) { - w->pflags |= HDA_ADC_PATH; - ret = 1; + } + if (res) + w->bindas = -2; + + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, res); + ); + return (res); +} + +/* + * Trace extra associations (beeper, monitor) + */ +static void +hdac_audio_trace_as_extra(struct hdac_devinfo *devinfo) +{ + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w; + int j; + + /* Input monitor */ + /* Find mixer associated with input, but supplying signal + for output associations. Hope it will be input monitor. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "Tracing input monitor\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdac_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " nid %d is input monitor\n", + w->nid); + ); + w->pflags |= HDA_ADC_MONITOR; + w->ossdev = SOUND_MIXER_IMIX; } - break;*/ - default: - break; } - return (ret); + + /* Beeper */ + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "Tracing beeper\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdac_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Tracing nid %d to out\n", + j); + ); + hdac_audio_trace_to_out(devinfo, w->nid, 0); + w->bindas = -2; + } } -static uint32_t -hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, - nid_t nid, nid_t pnid, int index, int depth) +/* + * Bind assotiations to PCM channels + */ +static void +hdac_audio_bind_as(struct hdac_devinfo *devinfo) { - struct hdac_widget *w, *pw; - struct hdac_audio_ctl *ctl; - uint32_t fl = 0; - int i, ossdev, conndev, strategy; + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_as *as = devinfo->function.audio.as; + int j, cnt = 0, free; + + for (j = 0; j < devinfo->function.audio.ascnt; j++) { + if (as[j].enable) + cnt++; + } + if (sc->num_chans == 0) { + sc->chans = (struct hdac_chan *)malloc( + sizeof(struct hdac_chan) * cnt, + M_HDAC, M_ZERO | M_NOWAIT); + if (sc->chans == NULL) { + device_printf(devinfo->codec->sc->dev, + "Channels memory allocation failed!\n"); + return; + } + } else { + sc->chans = (struct hdac_chan *)realloc(sc->chans, + sizeof(struct hdac_chan) * cnt, + M_HDAC, M_ZERO | M_NOWAIT); + if (sc->chans == NULL) { + sc->num_chans = 0; + device_printf(devinfo->codec->sc->dev, + "Channels memory allocation failed!\n"); + return; + } + } + free = sc->num_chans; + sc->num_chans += cnt; - if (depth > HDA_PARSE_MAXDEPTH) - return (0); + for (j = free; j < free + cnt; j++) { + devinfo->codec->sc->chans[j].devinfo = devinfo; + devinfo->codec->sc->chans[j].as = -1; + } - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); + /* Assign associations in order of their numbers, */ + free = 0; + for (j = 0; j < devinfo->function.audio.ascnt; j++) { + if (as[j].enable == 0) + continue; + + as[j].chan = free; + devinfo->codec->sc->chans[free].as = j; + if (as[j].dir == HDA_CTL_IN) { + devinfo->codec->sc->chans[free].dir = PCMDIR_REC; + devinfo->function.audio.reccnt++; + } else { + devinfo->codec->sc->chans[free].dir = PCMDIR_PLAY; + devinfo->function.audio.playcnt++; + } + hdac_pcmchannel_setup(&devinfo->codec->sc->chans[free]); + free++; + } +} - pw = hdac_widget_get(devinfo, pnid); - strategy = devinfo->function.audio.parsing_strategy; +static void +hdac_audio_disable_nonaudio(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + int i; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER - || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) { - for (i = 0; i < w->nconns; i++) { - fl |= hdac_audio_ctl_outamp_build(devinfo, w->conns[i], - w->nid, i, depth + 1); + /* Disable power and volume widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { + w->enable = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling nid %d due to it's" + " non-audio type.\n", + w->nid); + ); } - w->ctlflags |= fl; - return (fl); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && - (w->pflags & HDA_DAC_PATH)) { + } +} + +static void +hdac_audio_disable_useless(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + int done, found, i, j, k; + + /* Disable useless pins. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { + w->enable = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling pin nid %d due" + " to None connectivity.\n", + w->nid); + ); + } + } + do { + done = 1; + /* Disable and mute controls for disabled widgets. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) + if (ctl->enable == 0) + continue; + if (ctl->widget->enable == 0 || + (ctl->childwidget != NULL && + ctl->childwidget->enable == 0)) { + ctl->forcemute = 1; + ctl->muted = HDA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + if (ctl->ndir == HDA_CTL_IN) + ctl->widget->connsenable[ctl->index] = 0; + done = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling ctl %d nid %d cnid %d due" + " to disabled widget.\n", i, + ctl->widget->nid, + (ctl->childwidget != NULL)? + ctl->childwidget->nid:-1); + ); + } + } + /* Disable useless widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) continue; - /* XXX This should be compressed! */ - if (((ctl->widget->nid == w->nid) || - (ctl->widget->nid == pnid && ctl->index == index && - (ctl->dir & HDA_CTL_IN)) || - (ctl->widget->nid == pnid && pw != NULL && - pw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - (pw->nconns < 2 || pw->selconn == index || - pw->selconn == -1) && - (ctl->dir & HDA_CTL_OUT)) || - (strategy == HDA_PARSE_DIRECT && - ctl->widget->nid == w->nid)) && - !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { - /*if (pw != NULL && pw->selconn == -1) - pw->selconn = index; - fl |= SOUND_MASK_VOLUME; - fl |= SOUND_MASK_PCM; - ctl->ossmask |= SOUND_MASK_VOLUME; - ctl->ossmask |= SOUND_MASK_PCM; - ctl->ossdev = SOUND_MIXER_PCM;*/ - if (!(w->ctlflags & SOUND_MASK_PCM) || - (pw != NULL && - !(pw->ctlflags & SOUND_MASK_PCM))) { - fl |= SOUND_MASK_VOLUME; - fl |= SOUND_MASK_PCM; - ctl->ossmask |= SOUND_MASK_VOLUME; - ctl->ossmask |= SOUND_MASK_PCM; - ctl->ossdev = SOUND_MIXER_PCM; - w->ctlflags |= SOUND_MASK_VOLUME; - w->ctlflags |= SOUND_MASK_PCM; - if (pw != NULL) { - if (pw->selconn == -1) - pw->selconn = index; - pw->ctlflags |= - SOUND_MASK_VOLUME; - pw->ctlflags |= - SOUND_MASK_PCM; + /* Disable inputs with disabled child widgets. */ + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) { + w->connsenable[j] = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling nid %d connection %d due" + " to disabled child widget.\n", + i, j); + ); } } } - } - w->ctlflags |= fl; - return (fl); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && - (w->pflags & HDA_ADC_PATH)) { - conndev = w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; - /* XXX This should be compressed! */ - if (((ctl->widget->nid == pnid && ctl->index == index && - (ctl->dir & HDA_CTL_IN)) || - (ctl->widget->nid == pnid && pw != NULL && - pw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - (pw->nconns < 2 || pw->selconn == index || - pw->selconn == -1) && - (ctl->dir & HDA_CTL_OUT)) || - (strategy == HDA_PARSE_DIRECT && - ctl->widget->nid == w->nid)) && - !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { - if (pw != NULL && pw->selconn == -1) - pw->selconn = index; - ossdev = 0; - switch (conndev) { - case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: - ossdev = SOUND_MIXER_MIC; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: - ossdev = SOUND_MIXER_LINE; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: - ossdev = SOUND_MIXER_CD; - break; - default: - ossdev = - hdac_audio_ctl_ossmixer_getnextdev( - devinfo); - if (ossdev < 0) - ossdev = 0; + /* Disable mixers and selectors without inputs. */ + found = 0; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) { + found = 1; break; } - if (strategy == HDA_PARSE_MIXER) { - fl |= SOUND_MASK_VOLUME; - ctl->ossmask |= SOUND_MASK_VOLUME; + } + if (found == 0) { + w->enable = 0; + done = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling nid %d due to all it's" + " inputs disabled.\n", w->nid); + ); + } + /* Disable nodes without consumers. */ + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + found = 0; + for (k = devinfo->startnode; k < devinfo->endnode; k++) { + cw = hdac_widget_get(devinfo, k); + if (cw == NULL || cw->enable == 0) + continue; + for (j = 0; j < cw->nconns; j++) { + if (cw->connsenable[j] && cw->conns[j] == i) { + found = 1; + break; + } } - fl |= 1 << ossdev; - ctl->ossmask |= 1 << ossdev; - ctl->ossdev = ossdev; + } + if (found == 0) { + w->enable = 0; + done = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling nid %d due to all it's" + " consumers disabled.\n", w->nid); + ); } } - w->ctlflags |= fl; - return (fl); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) - continue; - /* XXX This should be compressed! */ - if (((ctl->widget->nid == pnid && ctl->index == index && - (ctl->dir & HDA_CTL_IN)) || - (ctl->widget->nid == pnid && pw != NULL && - pw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - (pw->nconns < 2 || pw->selconn == index || - pw->selconn == -1) && - (ctl->dir & HDA_CTL_OUT)) || - (strategy == HDA_PARSE_DIRECT && - ctl->widget->nid == w->nid)) && - !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { - if (pw != NULL && pw->selconn == -1) - pw->selconn = index; - fl |= SOUND_MASK_VOLUME; - fl |= SOUND_MASK_SPEAKER; - ctl->ossmask |= SOUND_MASK_VOLUME; - ctl->ossmask |= SOUND_MASK_SPEAKER; - ctl->ossdev = SOUND_MIXER_SPEAKER; + } while (done == 0); + +} + +static void +hdac_audio_disable_unas(struct hdac_devinfo *devinfo) +{ + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + int i, j, k; + + /* Disable unassosiated widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == -1) { + w->enable = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling unassociated nid %d.\n", + w->nid); + ); + } + } + /* Disable input connections on input pin and + * output on output. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->bindas < 0) + continue; + if (as[w->bindas].dir == HDA_CTL_IN) { + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + w->connsenable[j] = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling connection to input pin " + "nid %d conn %d.\n", + i, j); + ); + } + ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, + HDA_CTL_IN, -1, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + } else { + ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, + HDA_CTL_OUT, -1, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + for (k = devinfo->startnode; k < devinfo->endnode; k++) { + cw = hdac_widget_get(devinfo, k); + if (cw == NULL || cw->enable == 0) + continue; + for (j = 0; j < cw->nconns; j++) { + if (cw->connsenable[j] && cw->conns[j] == i) { + cw->connsenable[j] = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling connection from output pin " + "nid %d conn %d cnid %d.\n", + k, j, i); + ); + if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + cw->nconns > 1) + continue; + ctl = hdac_audio_ctl_amp_get(devinfo, k, + HDA_CTL_IN, j, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + } + } } } - w->ctlflags |= fl; - return (fl); } - return (0); } -static uint32_t -hdac_audio_ctl_inamp_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +static void +hdac_audio_disable_notselected(struct hdac_devinfo *devinfo) +{ + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w; + int i, j; + + /* On playback path we can safely disable all unseleted inputs. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->nconns <= 1) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + if (w->bindas < 0 || as[w->bindas].dir == HDA_CTL_IN) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + if (w->selconn < 0 || w->selconn == j) + continue; + w->connsenable[j] = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling unselected connection " + "nid %d conn %d.\n", + i, j); + ); + } + } +} + +static void +hdac_audio_disable_crossas(struct hdac_devinfo *devinfo) { struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; - uint32_t fl; - int i; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); + int i, j; - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - /*if (!(w->pflags & HDA_ADC_PATH)) - return (0); - if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) - return (0);*/ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) + /* Disable crossassociatement connections. */ + /* ... using selectors */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->nconns <= 1) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; - if (ctl->widget->nid == nid) { - ctl->ossmask |= SOUND_MASK_RECLEV; - w->ctlflags |= SOUND_MASK_RECLEV; - return (SOUND_MASK_RECLEV); + if (w->bindas == -2) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || w->enable == 0) + continue; + if (w->bindas == cw->bindas || cw->bindas == -2) + continue; + w->connsenable[j] = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling crossassociatement connection " + "nid %d conn %d cnid %d.\n", + i, j, cw->nid); + ); } } - for (i = 0; i < w->nconns; i++) { - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL || cw->enable == 0) + /* ... using controls */ + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->childwidget == NULL) continue; - if (cw->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + if (ctl->widget->bindas == -2 || + ctl->childwidget->bindas == -2) continue; - fl = hdac_audio_ctl_inamp_build(devinfo, cw->nid, depth + 1); - if (fl != 0) { - cw->ctlflags |= fl; - w->ctlflags |= fl; - return (fl); + if (ctl->widget->bindas != ctl->childwidget->bindas) { + ctl->forcemute = 1; + ctl->muted = HDA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + if (ctl->ndir == HDA_CTL_IN) + ctl->widget->connsenable[ctl->index] = 0; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + " Disabling crossassociatement connection " + "ctl %d nid %d cnid %d.\n", i, + ctl->widget->nid, + ctl->childwidget->nid); + ); } } - return (0); + } +#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) + +/* + * Find controls to control amplification for source. + */ static int -hdac_audio_ctl_recsel_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +hdac_audio_ctl_source_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, + int ossdev, int ctlable, int depth, int need) { - struct hdac_widget *w, *cw; - int i, child = 0; - + struct hdac_widget *w, *wc; + struct hdac_audio_ctl *ctl; + int i, j, conns = 0, rneed; + if (depth > HDA_PARSE_MAXDEPTH) - return (0); + return (need); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) - return (0); - /*if (!(w->pflags & HDA_ADC_PATH)) - return (0); - if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) - return (0);*/ - /* XXX weak! */ - for (i = 0; i < w->nconns; i++) { - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL) - continue; - if (++child > 1) { - w->pflags |= HDA_ADC_RECSEL; - return (1); + return (need); + + /* Count number of active inputs. */ + if (depth > 0) { + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) + conns++; } } - for (i = 0; i < w->nconns; i++) { - if (hdac_audio_ctl_recsel_build(devinfo, - w->conns[i], depth + 1) != 0) - return (1); + + /* If this is not a first step - use input mixer. + Pins have common input ctl so care must be taken. */ + if (depth > 0 && ctlable && (conns == 1 || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { + ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, + index, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } } - return (0); + + /* If widget has own ossdev - not traverse it. + It will be traversed on it's own. */ + if (w->ossdev >= 0 && depth > 0) + return (need); + + /* We must not traverse pin */ + if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && + depth > 0) + return (need); + + /* record that this widget exports such signal, */ + w->ossmask |= (1 << ossdev); + + /* If signals mixed, we can't assign controls farther. + * Ignore this on depth zero. Caller must knows why. + * Ignore this for static selectors if this input selected. + */ + if (conns > 1) + ctlable = 0; + + if (ctlable) { + ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + rneed = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdac_widget_get(devinfo, i); + if (wc == NULL || wc->enable == 0) + continue; + for (j = 0; j < wc->nconns; j++) { + if (wc->connsenable[j] && wc->conns[j] == nid) { + rneed |= hdac_audio_ctl_source_amp(devinfo, + wc->nid, j, ossdev, ctlable, depth + 1, need); + } + } + } + rneed &= need; + + return (rneed); } -static int -hdac_audio_build_tree_strategy(struct hdac_devinfo *devinfo) +/* + * Find controls to control amplification for destination. + */ +static void +hdac_audio_ctl_dest_amp(struct hdac_devinfo *devinfo, nid_t nid, + int ossdev, int depth, int need) { - struct hdac_widget *w, *cw; - int i, j, conndev, found_dac = 0; - int strategy; + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w, *wc; + struct hdac_audio_ctl *ctl; + int i, j, consumers; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; - strategy = devinfo->function.audio.parsing_strategy; + if (depth > 0) { + /* If this node produce output for several consumers, + we can't touch it. */ + consumers = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdac_widget_get(devinfo, i); + if (wc == NULL || wc->enable == 0) + continue; + for (j = 0; j < wc->nconns; j++) { + if (wc->connsenable[j] && wc->conns[j] == nid) + consumers++; + } + } + /* The only exception is if real HP redirection is configured + and this is a duplication point. + XXX: Actually exception is not completely correct. + XXX: Duplication point check is not perfect. */ + if ((consumers == 2 && (w->bindas < 0 || + as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || + (w->bindseqmask & (1 << 15)) == 0)) || + consumers > 2) + return; + /* Else use it's output mixer. */ + ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, + HDA_CTL_OUT, -1, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + /* We must not traverse pin */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + depth > 0) + return; + + for (i = 0; i < w->nconns; i++) { + int tneed = need; + if (w->connsenable[i] == 0) + continue; + ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, + HDA_CTL_IN, i, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & tneed) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + tneed &= ~HDA_CTL_GIVE(ctl); + } + hdac_audio_ctl_dest_amp(devinfo, w->conns[i], ossdev, + depth + 1, tneed); + } +} + +/* + * Assign OSS names to sound sources + */ +static void +hdac_audio_assign_names(struct hdac_devinfo *devinfo) +{ + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w; + int i, j; + int type = -1, use, used = 0; + static const int types[7][13] = { + { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, + SOUND_MIXER_LINE3, -1 }, /* line */ + { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ + { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ + { SOUND_MIXER_CD, -1 }, /* cd */ + { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ + { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, + -1 }, /* digital */ + { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, + SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, + SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, + SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, + -1 } /* others */ + }; + + /* Surely known names */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; + if (w->bindas == -1) + continue; + use = -1; + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (as[w->bindas].dir == HDA_CTL_OUT) + break; + type = -1; + switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + type = 0; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) + == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + break; + type = 1; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + type = 3; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + type = 4; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: + type = 5; + break; + } + if (type == -1) + break; + j = 0; + while (types[type][j] >= 0 && + (used & (1 << types[type][j])) != 0) { + j++; + } + if (types[type][j] >= 0) + use = types[type][j]; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + use = SOUND_MIXER_PCM; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + use = SOUND_MIXER_SPEAKER; + break; + default: + break; + } + if (use >= 0) { + w->ossdev = use; + used |= (1 << use); + } + } + /* Semi-known names */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev >= 0) + continue; + if (w->bindas == -1) + continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; - if (!HDA_PARAM_PIN_CAP_OUTPUT_CAP(w->wclass.pin.cap)) + if (as[w->bindas].dir == HDA_CTL_OUT) + continue; + type = -1; + switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: + type = 0; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + type = 2; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: + type = 5; + break; + } + if (type == -1) + break; + j = 0; + while (types[type][j] >= 0 && + (used & (1 << types[type][j])) != 0) { + j++; + } + if (types[type][j] >= 0) { + w->ossdev = types[type][j]; + used |= (1 << types[type][j]); + } + } + /* Others */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev >= 0) continue; - conndev = w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - if (!(conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || - conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || - conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT)) + if (w->bindas == -1) continue; - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - if (strategy == HDA_PARSE_MIXER && !(cw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || - cw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) - continue; - if (hdac_widget_find_dac_path(devinfo, cw->nid, 0) - != 0) { - if (w->selconn == -1) - w->selconn = j; - w->pflags |= HDA_DAC_PATH; - found_dac++; - } + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (as[w->bindas].dir == HDA_CTL_OUT) + continue; + j = 0; + while (types[6][j] >= 0 && + (used & (1 << types[6][j])) != 0) { + j++; + } + if (types[6][j] >= 0) { + w->ossdev = types[6][j]; + used |= (1 << types[6][j]); } } - - return (found_dac); } static void hdac_audio_build_tree(struct hdac_devinfo *devinfo) { - struct hdac_widget *w; - struct hdac_audio_ctl *ctl; - int i, j, dacs, strategy; + struct hdac_audio_as *as = devinfo->function.audio.as; + int j, res; - /* Construct DAC path */ - strategy = HDA_PARSE_MIXER; - devinfo->function.audio.parsing_strategy = strategy; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, - "HDA_DEBUG: HWiP: HDA Widget Parser - Revision %d\n", + "HWiP: HDA Widget Parser - Revision %d\n", HDA_WIDGET_PARSER_REV); ); - dacs = hdac_audio_build_tree_strategy(devinfo); - if (dacs == 0) { + + /* Trace all associations in order of their numbers, */ + for (j = 0; j < devinfo->function.audio.ascnt; j++) { + if (as[j].enable == 0) + continue; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, - "HDA_DEBUG: HWiP: 0 DAC path found! " - "Retrying parser " - "using HDA_PARSE_DIRECT strategy.\n"); + "Tracing association %d (%d)\n", j, as[j].index); ); - strategy = HDA_PARSE_DIRECT; - devinfo->function.audio.parsing_strategy = strategy; - dacs = hdac_audio_build_tree_strategy(devinfo); + if (as[j].dir == HDA_CTL_OUT) { +retry: + res = hdac_audio_trace_as_out(devinfo, j, 0); + if (res == 0 && as[j].hpredir >= 0 && + as[j].fakeredir == 0) { + /* If codec can't do analog HP redirection + try to make it using one more DAC. */ + as[j].fakeredir = 1; + goto retry; + } + } else { + res = hdac_audio_trace_as_in(devinfo, j); + } + if (res) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "Association %d (%d) trace succeded\n", + j, as[j].index); + ); + } else { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "Association %d (%d) trace failed\n", + j, as[j].index); + ); + as[j].enable = 0; + } } - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "HDA_DEBUG: HWiP: Found %d DAC path using HDA_PARSE_%s " - "strategy.\n", - dacs, (strategy == HDA_PARSE_MIXER) ? "MIXER" : "DIRECT"); - ); + /* Trace mixer and beeper pseudo associations. */ + hdac_audio_trace_as_extra(devinfo); +} - /* Construct ADC path */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) - continue; - (void)hdac_widget_find_adc_path(devinfo, w->nid, 0); - } +static void +hdac_audio_assign_mixers(struct hdac_devinfo *devinfo) +{ + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_audio_ctl *ctl; + struct hdac_widget *w; + int i; - /* Output mixers */ + /* Assign mixers to the tree. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; - if ((strategy == HDA_PARSE_MIXER && - (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) - && (w->pflags & HDA_DAC_PATH)) || - (strategy == HDA_PARSE_DIRECT && (w->pflags & - (HDA_DAC_PATH | HDA_ADC_PATH)))) { - w->ctlflags |= hdac_audio_ctl_outamp_build(devinfo, - w->nid, devinfo->startnode - 1, 0, 0); - } else if (w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { - j = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &j)) != - NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) - continue; - if (ctl->widget->nid != w->nid) - continue; - ctl->ossmask |= SOUND_MASK_VOLUME; - ctl->ossmask |= SOUND_MASK_SPEAKER; - ctl->ossdev = SOUND_MIXER_SPEAKER; - w->ctlflags |= SOUND_MASK_VOLUME; - w->ctlflags |= SOUND_MASK_SPEAKER; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + as[w->bindas].dir == HDA_CTL_IN)) { + if (w->ossdev < 0) + continue; + hdac_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, 1); + } else if ((w->pflags & HDA_ADC_MONITOR) != 0) { + if (w->ossdev < 0) + continue; + if (hdac_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, 1)) { + /* If we are unable to control input monitor + as source - try to control it as destination. */ + hdac_audio_ctl_dest_amp(devinfo, w->nid, + w->ossdev, 0, 1); } + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdac_audio_ctl_dest_amp(devinfo, w->nid, + SOUND_MIXER_RECLEV, 0, 1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + as[w->bindas].dir == HDA_CTL_OUT) { + hdac_audio_ctl_dest_amp(devinfo, w->nid, + SOUND_MIXER_VOLUME, 0, 1); } } + /* Treat unrequired as possible. */ + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->ossmask == 0) + ctl->ossmask = ctl->possmask; + } +} - /* Input mixers (rec) */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) +static void +hdac_audio_prepare_pin_ctrl(struct hdac_devinfo *devinfo) +{ + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w; + uint32_t pincap; + int i; + + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; - if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && - w->pflags & HDA_ADC_PATH)) + + pincap = w->wclass.pin.cap; + + /* Disable everything. */ + w->wclass.pin.ctrl &= ~( + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + + if (w->enable == 0 || + w->bindas < 0 || as[w->bindas].enable == 0) { + /* Pin is unused so left it disabled. */ continue; - hdac_audio_ctl_inamp_build(devinfo, w->nid, 0); - hdac_audio_ctl_recsel_build(devinfo, w->nid, 0); + } else if (as[w->bindas].dir == HDA_CTL_IN) { + /* Input pin, configure for input. */ + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + + if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } else { + /* Output pin, configure for output. */ + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == + HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + + if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } } } -#define HDA_COMMIT_CONN (1 << 0) -#define HDA_COMMIT_CTRL (1 << 1) -#define HDA_COMMIT_EAPD (1 << 2) -#define HDA_COMMIT_GPIO (1 << 3) -#define HDA_COMMIT_MISC (1 << 4) -#define HDA_COMMIT_ALL (HDA_COMMIT_CONN | HDA_COMMIT_CTRL | \ - HDA_COMMIT_EAPD | HDA_COMMIT_GPIO | HDA_COMMIT_MISC) - static void -hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) +hdac_audio_commit(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; nid_t cad; + uint32_t gdata, gmask, gdir; + int commitgpio, numgpio; int i; - if (!(cfl & HDA_COMMIT_ALL)) - return; - cad = devinfo->codec->cad; - if ((cfl & HDA_COMMIT_MISC)) { - if (sc->pci_subvendor == APPLE_INTEL_MAC) - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0x7e7, 0), cad); - } - - if (cfl & HDA_COMMIT_GPIO) { - uint32_t gdata, gmask, gdir; - int commitgpio, numgpio; + if (sc->pci_subvendor == APPLE_INTEL_MAC) + hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, + 0x7e7, 0), cad); - gdata = 0; - gmask = 0; - gdir = 0; - commitgpio = 0; + gdata = 0; + gmask = 0; + gdir = 0; + commitgpio = 0; - numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( - devinfo->function.audio.gpio); + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( + devinfo->function.audio.gpio); - if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) - commitgpio = (numgpio > 0) ? 1 : 0; - else { - for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { - if (!(devinfo->function.audio.quirks & - (1 << i))) - continue; - if (commitgpio == 0) { - commitgpio = 1; - HDA_BOOTVERBOSE( - gdata = hdac_command(sc, - HDA_CMD_GET_GPIO_DATA(cad, - devinfo->nid), cad); - gmask = hdac_command(sc, - HDA_CMD_GET_GPIO_ENABLE_MASK(cad, - devinfo->nid), cad); - gdir = hdac_command(sc, - HDA_CMD_GET_GPIO_DIRECTION(cad, - devinfo->nid), cad); - device_printf(sc->dev, - "GPIO init: data=0x%08x " - "mask=0x%08x dir=0x%08x\n", - gdata, gmask, gdir); - gdata = 0; - gmask = 0; - gdir = 0; - ); - } - gdata |= 1 << i; - gmask |= 1 << i; - gdir |= 1 << i; + if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) + commitgpio = (numgpio > 0) ? 1 : 0; + else { + for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { + if (!(devinfo->function.audio.quirks & + (1 << i))) + continue; + if (commitgpio == 0) { + commitgpio = 1; + HDA_BOOTVERBOSE( + gdata = hdac_command(sc, + HDA_CMD_GET_GPIO_DATA(cad, + devinfo->nid), cad); + gmask = hdac_command(sc, + HDA_CMD_GET_GPIO_ENABLE_MASK(cad, + devinfo->nid), cad); + gdir = hdac_command(sc, + HDA_CMD_GET_GPIO_DIRECTION(cad, + devinfo->nid), cad); + device_printf(sc->dev, + "GPIO init: data=0x%08x " + "mask=0x%08x dir=0x%08x\n", + gdata, gmask, gdir); + gdata = 0; + gmask = 0; + gdir = 0; + ); } + gdata |= 1 << i; + gmask |= 1 << i; + gdir |= 1 << i; } + } - if (commitgpio != 0) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "GPIO commit: data=0x%08x mask=0x%08x " - "dir=0x%08x\n", - gdata, gmask, gdir); - ); - hdac_command(sc, - HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, - gmask), cad); - hdac_command(sc, - HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, - gdir), cad); - hdac_command(sc, - HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, - gdata), cad); - } + if (commitgpio != 0) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "GPIO commit: data=0x%08x mask=0x%08x " + "dir=0x%08x\n", + gdata, gmask, gdir); + ); + hdac_command(sc, + HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, + gmask), cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, + gdir), cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, + gdata), cad); } for (i = 0; i < devinfo->nodecnt; i++) { w = &devinfo->widget[i]; - if (w == NULL || w->enable == 0) + if (w == NULL) continue; - if (cfl & HDA_COMMIT_CONN) { - if (w->selconn == -1) - w->selconn = 0; - if (w->nconns > 0) - hdac_widget_connection_select(w, w->selconn); - } - if ((cfl & HDA_COMMIT_CTRL) && - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - uint32_t pincap; - - pincap = w->wclass.pin.cap; - - if ((w->pflags & (HDA_DAC_PATH | HDA_ADC_PATH)) == - (HDA_DAC_PATH | HDA_ADC_PATH)) - device_printf(sc->dev, "WARNING: node %d " - "participate both for DAC/ADC!\n", w->nid); - if (w->pflags & HDA_DAC_PATH) { - w->wclass.pin.ctrl &= - ~HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) != - HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) - w->wclass.pin.ctrl &= - ~HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; - if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && - HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && - HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && - HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); - } else if (w->pflags & HDA_ADC_PATH) { - w->wclass.pin.ctrl &= - ~(HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE); - if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && - HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && - HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && - HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); - } else - w->wclass.pin.ctrl &= ~( - HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + if (w->selconn == -1) + w->selconn = 0; + if (w->nconns > 0) + hdac_widget_connection_select(w, w->selconn); + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } - if ((cfl & HDA_COMMIT_EAPD) && - w->param.eapdbtl != HDAC_INVALID) { + if (w->param.eapdbtl != HDAC_INVALID) { uint32_t val; val = w->param.eapdbtl; @@ -5298,64 +5904,54 @@ hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) static void hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) { - struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_ctl *ctl; - int i; + int i, z; - devinfo->function.audio.mvol = 100 | (100 << 8); i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, "[%2d] Ctl nid=%d", - i, (ctl->widget != NULL) ? - ctl->widget->nid : -1); - if (ctl->childwidget != NULL) - printf(" childnid=%d", - ctl->childwidget->nid); - if (ctl->widget == NULL) - printf(" NULL WIDGET!"); - printf(" DISABLED\n"); - ); + if (ctl->enable == 0) { + /* Mute disabled controls. */ + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_ALL, 0, 0); continue; } - HDA_BOOTVERBOSE( - if (ctl->ossmask == 0) { - device_printf(sc->dev, "[%2d] Ctl nid=%d", - i, ctl->widget->nid); - if (ctl->childwidget != NULL) - printf(" childnid=%d", - ctl->childwidget->nid); - printf(" Bind to NONE\n"); - } - ); - if (ctl->step > 0) { - ctl->ossval = (ctl->left * 100) / ctl->step; - ctl->ossval |= ((ctl->right * 100) / ctl->step) << 8; - } else - ctl->ossval = 0; - hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, - ctl->left, ctl->right); + /* Init controls to 0dB amplification. */ + z = ctl->offset; + if (z > ctl->step) + z = ctl->step; + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_NONE, z, z); } } -static int -hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) +static void +hdac_powerup(struct hdac_devinfo *devinfo) { - struct hdac_chan *ch; - struct hdac_widget *w; - uint32_t cap, fmtcap, pcmcap, path; - int i, type, ret, max; + struct hdac_softc *sc = devinfo->codec->sc; + nid_t cad = devinfo->codec->cad; + int i; - if (dir == PCMDIR_PLAY) { - type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT; - ch = &devinfo->codec->sc->play; - path = HDA_DAC_PATH; - } else { - type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT; - ch = &devinfo->codec->sc->rec; - path = HDA_ADC_PATH; + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, + devinfo->nid, HDA_CMD_POWER_STATE_D0), + cad); + DELAY(100); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, + i, HDA_CMD_POWER_STATE_D0), + cad); } + DELAY(1000); +} + +static int +hdac_pcmchannel_setup(struct hdac_chan *ch) +{ + struct hdac_devinfo *devinfo = ch->devinfo; + struct hdac_audio_as *as = devinfo->function.audio.as; + struct hdac_widget *w; + uint32_t cap, fmtcap, pcmcap; + int i, j, ret, max; ch->caps = hdac_caps; ch->caps.fmtlist = ch->fmtlist; @@ -5369,31 +5965,44 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) pcmcap = devinfo->function.audio.supp_pcm_size_rate; max = (sizeof(ch->io) / sizeof(ch->io[0])) - 1; - for (i = devinfo->startnode; i < devinfo->endnode && ret < max; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0 || w->type != type || - !(w->pflags & path)) + for (i = 0; i < 16 && ret < max; i++) { + /* Check as is correct */ + if (ch->as < 0) + break; + /* Cound only present DACs */ + if (as[ch->as].dacs[i] <= 0) + continue; + /* Ignore duplicates */ + for (j = 0; j < ret; j++) { + if (ch->io[j] == as[ch->as].dacs[i]) + break; + } + if (j < ret) continue; - cap = w->param.widget_cap; - /*if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(cap)) - continue;*/ - if (!HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(cap)) + + w = hdac_widget_get(devinfo, as[ch->as].dacs[i]); + if (w == NULL || w->enable == 0) + continue; + if (!HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(w->param.widget_cap)) continue; cap = w->param.supp_stream_formats; - /*if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) { - } - if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) { + /*if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) { }*/ - if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && + !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) continue; + /* Many codec does not declare AC3 support on SPDIF. + I don't beleave that they doesn't support it! */ + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; if (ret == 0) { - fmtcap = w->param.supp_stream_formats; + fmtcap = cap; pcmcap = w->param.supp_pcm_size_rate; } else { - fmtcap &= w->param.supp_stream_formats; + fmtcap &= cap; pcmcap &= w->param.supp_pcm_size_rate; } - ch->io[ret++] = i; + ch->io[ret++] = as[ch->as].dacs[i]; } ch->io[ret] = -1; @@ -5408,52 +6017,56 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) * 32bit = 4 */ if (ret > 0) { - cap = pcmcap; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) - ch->bit16 = 1; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) - ch->bit16 = 0; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) - ch->bit32 = 4; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) - ch->bit32 = 3; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) - ch->bit32 = 2; i = 0; - if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) - ch->fmtlist[i++] = AFMT_S16_LE; - ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; - if (ch->bit32 > 0) { - if (!(devinfo->function.audio.quirks & - HDA_QUIRK_FORCESTEREO)) - ch->fmtlist[i++] = AFMT_S32_LE; - ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) + ch->bit16 = 1; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) + ch->bit16 = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) + ch->bit32 = 4; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) + ch->bit32 = 3; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) + ch->bit32 = 2; + if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S16_LE; + ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; + if (ch->bit32 > 0) { + if (!(devinfo->function.audio.quirks & + HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S32_LE; + ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + } + } + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { + ch->fmtlist[i++] = AFMT_AC3; } ch->fmtlist[i] = 0; i = 0; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) ch->pcmrates[i++] = 8000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) ch->pcmrates[i++] = 11025; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) ch->pcmrates[i++] = 16000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) ch->pcmrates[i++] = 22050; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) ch->pcmrates[i++] = 32000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) ch->pcmrates[i++] = 44100; - /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(cap)) */ + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ ch->pcmrates[i++] = 48000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) ch->pcmrates[i++] = 88200; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) ch->pcmrates[i++] = 96000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) ch->pcmrates[i++] = 176400; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) ch->pcmrates[i++] = 192000; - /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(cap)) */ + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ ch->pcmrates[i] = 0; if (i > 0) { ch->caps.minspeed = ch->pcmrates[0]; @@ -5465,74 +6078,80 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) } static void -hdac_dump_ctls(struct hdac_devinfo *devinfo, const char *banner, uint32_t flag) +hdac_dump_ctls(struct hdac_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) { + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_audio_ctl *ctl; struct hdac_softc *sc = devinfo->codec->sc; - int i; - uint32_t fl = 0; - + char buf[64]; + int i, j, printed; if (flag == 0) { - fl = SOUND_MASK_VOLUME | SOUND_MASK_PCM | + flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | - SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_OGAIN; + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_OGAIN | + SOUND_MASK_IMIX | SOUND_MASK_MONITOR); } - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL || - ctl->widget->enable == 0 || (ctl->ossmask & - (SOUND_MASK_SKIP | SOUND_MASK_DISABLE))) + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if ((flag & (1 << j)) == 0) continue; - if ((flag == 0 && (ctl->ossmask & ~fl)) || - (flag != 0 && (ctl->ossmask & flag))) { - if (banner != NULL) { - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "%s\n", banner); - } - goto hdac_ctl_dump_it_all; - } - } - - return; + i = 0; + printed = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || + ctl->widget->enable == 0) + continue; + if (!((pdevinfo->play >= 0 && + ctl->widget->bindas == sc->chans[pdevinfo->play].as) || + (pdevinfo->rec >= 0 && + ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || + (ctl->widget->bindas == -2 && pdevinfo->index == 0))) + continue; + if ((ctl->ossmask & (1 << j)) == 0) + continue; -hdac_ctl_dump_it_all: - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->widget == NULL || - ctl->widget->enable == 0) - continue; - if (!((flag == 0 && (ctl->ossmask & ~fl)) || - (flag != 0 && (ctl->ossmask & flag)))) - continue; - if (flag == 0) { - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "Unknown Ctl (OSS: %s)\n", - hdac_audio_ctl_ossmixer_mask2name(ctl->ossmask)); + if (printed == 0) { + device_printf(pdevinfo->dev, "\n"); + if (banner != NULL) { + device_printf(pdevinfo->dev, "%s", banner); + } else { + device_printf(pdevinfo->dev, "Unknown Ctl"); + } + printf(" (OSS: %s)\n", + hdac_audio_ctl_ossmixer_mask2allname(1 << j, + buf, sizeof(buf))); + device_printf(pdevinfo->dev, " |\n"); + printed = 1; + } + device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, + ctl->widget->nid, + (ctl->ndir == HDA_CTL_IN)?"in ":"out"); + if (ctl->ndir == HDA_CTL_IN && ctl->ndir == ctl->dir) + printf(" %2d): ", ctl->index); + else + printf("): "); + if (ctl->step > 0) { + printf("%+d/%+ddB (%d steps)%s\n", + (0 - ctl->offset) * (ctl->size + 1) / 4, + (ctl->step - ctl->offset) * (ctl->size + 1) / 4, + ctl->step + 1, + ctl->mute?" + mute":""); + } else + printf("%s\n", ctl->mute?"mute":""); } - device_printf(sc->dev, " |\n"); - device_printf(sc->dev, " +- nid: %2d index: %2d ", - ctl->widget->nid, ctl->index); - if (ctl->childwidget != NULL) - printf("(nid: %2d) ", ctl->childwidget->nid); - else - printf(" "); - printf("mute: %d step: %3d size: %3d off: %3d dir=0x%x ossmask=0x%08x\n", - ctl->mute, ctl->step, ctl->size, ctl->offset, ctl->dir, - ctl->ossmask); } } static void -hdac_dump_audio_formats(struct hdac_softc *sc, uint32_t fcap, uint32_t pcmcap) +hdac_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) { uint32_t cap; cap = fcap; if (cap != 0) { - device_printf(sc->dev, " Stream cap: 0x%08x\n", cap); - device_printf(sc->dev, " Format:"); + device_printf(dev, " Stream cap: 0x%08x\n", cap); + device_printf(dev, " Format:"); if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) printf(" AC3"); if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) @@ -5543,8 +6162,8 @@ hdac_dump_audio_formats(struct hdac_softc *sc, uint32_t fcap, uint32_t pcmcap) } cap = pcmcap; if (cap != 0) { - device_printf(sc->dev, " PCM cap: 0x%08x\n", cap); - device_printf(sc->dev, " PCM size:"); + device_printf(dev, " PCM cap: 0x%08x\n", cap); + device_printf(dev, " PCM size:"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) printf(" 8"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) @@ -5556,7 +6175,7 @@ hdac_dump_audio_formats(struct hdac_softc *sc, uint32_t fcap, uint32_t pcmcap) if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) printf(" 32"); printf("\n"); - device_printf(sc->dev, " PCM rate:"); + device_printf(dev, " PCM rate:"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) printf(" 8"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) @@ -5634,10 +6253,47 @@ hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) printf(" IN"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) printf(" OUT"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) + printf(" VREFs"); printf("\n"); } static void +hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + + device_printf(sc->dev, "nid %d 0x%08x as %2d seq %2d %13s %5s " + "jack %2d loc %2d color %7s misc %d%s\n", + w->nid, conf, + HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), + HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), + HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], + HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], + HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf), + HDA_CONFIG_DEFAULTCONF_LOCATION(conf), + HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], + HDA_CONFIG_DEFAULTCONF_MISC(conf), + (w->enable == 0)?" [DISABLED]":""); +} + +static void +hdac_dump_pin_configs(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdac_dump_pin_config(w, w->wclass.pin.config); + } +} + +static void hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) { device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); @@ -5653,13 +6309,15 @@ static void hdac_dump_nodes(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; + static char *ossname[] = SOUND_DEVICE_NAMES; struct hdac_widget *w, *cw; + char buf[64]; int i, j; device_printf(sc->dev, "\n"); device_printf(sc->dev, "Default Parameter\n"); device_printf(sc->dev, "-----------------\n"); - hdac_dump_audio_formats(sc, + hdac_dump_audio_formats(sc->dev, devinfo->function.audio.supp_stream_formats, devinfo->function.audio.supp_pcm_size_rate); device_printf(sc->dev, " IN amp: 0x%08x\n", @@ -5680,13 +6338,18 @@ hdac_dump_nodes(struct hdac_devinfo *devinfo) device_printf(sc->dev, " name: %s\n", w->name); device_printf(sc->dev, " widget_cap: 0x%08x\n", w->param.widget_cap); - device_printf(sc->dev, " Parse flags: 0x%08x\n", + device_printf(sc->dev, " Parse flags: 0x%x\n", w->pflags); - device_printf(sc->dev, " Ctl flags: 0x%08x\n", - w->ctlflags); + device_printf(sc->dev, " Association: %d (0x%08x)\n", + w->bindas, w->bindseqmask); + device_printf(sc->dev, " OSS: %s", + hdac_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); + if (w->ossdev >= 0) + printf(" (%s)", ossname[w->ossdev]); + printf("\n"); if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { - hdac_dump_audio_formats(sc, + hdac_dump_audio_formats(sc->dev, w->param.supp_stream_formats, w->param.supp_pcm_size_rate); } else if (w->type == @@ -5702,10 +6365,12 @@ hdac_dump_nodes(struct hdac_devinfo *devinfo) w->param.inamp_cap != 0) hdac_dump_amp(sc, w->param.inamp_cap, " Input"); device_printf(sc->dev, " connections: %d\n", w->nconns); + if (w->nconns > 0) + device_printf(sc->dev, " |\n"); for (j = 0; j < w->nconns; j++) { cw = hdac_widget_get(devinfo, w->conns[j]); - device_printf(sc->dev, " |\n"); - device_printf(sc->dev, " + <- nid=%d [%s]", + device_printf(sc->dev, " + %s<- nid=%d [%s]", + (w->connsenable[j] == 0)?"[DISABLED] ":"", w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); if (cw == NULL) printf(" [UNKNOWN]"); @@ -5720,142 +6385,171 @@ hdac_dump_nodes(struct hdac_devinfo *devinfo) } -static int -hdac_dump_dac_internal(struct hdac_devinfo *devinfo, nid_t nid, int depth) +static void +hdac_dump_dst_nid(struct hdac_pcm_devinfo *pdevinfo, nid_t nid, int depth) { + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w, *cw; - struct hdac_softc *sc = devinfo->codec->sc; - int i; + char buf[64]; + int i, printed = 0; if (depth > HDA_PARSE_MAXDEPTH) - return (0); + return; w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0 || !(w->pflags & HDA_DAC_PATH)) - return (0); + if (w == NULL || w->enable == 0) + return; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - device_printf(sc->dev, "\n"); - device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); - device_printf(sc->dev, " ^\n"); - device_printf(sc->dev, " |\n"); - device_printf(sc->dev, " +-----<------+\n"); - } else { - device_printf(sc->dev, " ^\n"); - device_printf(sc->dev, " |\n"); - device_printf(sc->dev, " "); - printf(" nid=%d [%s]\n", w->nid, w->name); - } + if (depth == 0) + device_printf(pdevinfo->dev, "%*s", 4, ""); + else + device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); + printf("nid=%d [%s]", w->nid, w->name); - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) { - return (1); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { - for (i = 0; i < w->nconns; i++) { - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL || cw->enable == 0 || cw->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (hdac_dump_dac_internal(devinfo, cw->nid, - depth + 1) != 0) - return (1); + if (depth > 0) { + if (w->ossmask == 0) { + printf("\n"); + return; + } + printf(" [src: %s]", + hdac_audio_ctl_ossmixer_mask2allname( + w->ossmask, buf, sizeof(buf))); + if (w->ossdev >= 0) { + printf("\n"); + return; } - } else if ((w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && - w->selconn > -1 && w->selconn < w->nconns) { - if (hdac_dump_dac_internal(devinfo, w->conns[w->selconn], - depth + 1) != 0) - return (1); + } + printf("\n"); + + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->bindas == -1) + continue; + if (printed == 0) { + device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); + printed = 1; + } + hdac_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); } - return (0); } static void -hdac_dump_dac(struct hdac_devinfo *devinfo) +hdac_dump_dac(struct hdac_pcm_devinfo *pdevinfo) { - struct hdac_widget *w; + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; int i, printed = 0; + if (pdevinfo->play < 0) + return; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - !(w->pflags & HDA_DAC_PATH)) + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->bindas != sc->chans[pdevinfo->play].as) continue; if (printed == 0) { printed = 1; - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "Playback path:\n"); + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Playback:\n"); } - hdac_dump_dac_internal(devinfo, w->nid, 0); + device_printf(pdevinfo->dev, "\n"); + hdac_dump_dst_nid(pdevinfo, i, 0); } } static void -hdac_dump_adc(struct hdac_devinfo *devinfo) +hdac_dump_adc(struct hdac_pcm_devinfo *pdevinfo) { - struct hdac_widget *w, *cw; + struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; - int i, j; + struct hdac_widget *w; + int i; int printed = 0; - char ossdevs[256]; + + if (pdevinfo->rec < 0) + return; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; - if (!(w->pflags & HDA_ADC_RECSEL)) + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->bindas != sc->chans[pdevinfo->rec].as) continue; if (printed == 0) { printed = 1; - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "Recording sources:\n"); + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Record:\n"); } - device_printf(sc->dev, "\n"); - device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - hdac_audio_ctl_ossmixer_mask2allname(cw->ctlflags, - ossdevs, sizeof(ossdevs)); - device_printf(sc->dev, " |\n"); - device_printf(sc->dev, " + <- nid=%d [%s]", - cw->nid, cw->name); - if (strlen(ossdevs) > 0) { - printf(" [recsrc: %s]", ossdevs); - } - printf("\n"); + device_printf(pdevinfo->dev, "\n"); + hdac_dump_dst_nid(pdevinfo, i, 0); + } +} + +static void +hdac_dump_mix(struct hdac_pcm_devinfo *pdevinfo) +{ + struct hdac_devinfo *devinfo = pdevinfo->devinfo; + struct hdac_widget *w; + int i; + int printed = 0; + + if (pdevinfo->index != 0) + return; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if ((w->pflags & HDA_ADC_MONITOR) == 0) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Input Mix:\n"); } + device_printf(pdevinfo->dev, "\n"); + hdac_dump_dst_nid(pdevinfo, i, 0); } } static void -hdac_dump_pcmchannels(struct hdac_softc *sc, int pcnt, int rcnt) +hdac_dump_pcmchannels(struct hdac_pcm_devinfo *pdevinfo) { + struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; nid_t *nids; + int i; - if (pcnt > 0) { - device_printf(sc->dev, "\n"); - device_printf(sc->dev, " PCM Playback: %d\n", pcnt); - hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, - sc->play.supp_pcm_size_rate); - device_printf(sc->dev, " DAC:"); - for (nids = sc->play.io; *nids != -1; nids++) + if (pdevinfo->play >= 0) { + i = pdevinfo->play; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Playback:\n"); + device_printf(pdevinfo->dev, "\n"); + hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, + sc->chans[i].supp_pcm_size_rate); + device_printf(pdevinfo->dev, " DAC:"); + for (nids = sc->chans[i].io; *nids != -1; nids++) printf(" %d", *nids); printf("\n"); } - - if (rcnt > 0) { - device_printf(sc->dev, "\n"); - device_printf(sc->dev, " PCM Record: %d\n", rcnt); - hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, - sc->rec.supp_pcm_size_rate); - device_printf(sc->dev, " ADC:"); - for (nids = sc->rec.io; *nids != -1; nids++) + if (pdevinfo->rec >= 0) { + i = pdevinfo->rec; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Record:\n"); + device_printf(pdevinfo->dev, "\n"); + hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, + sc->chans[i].supp_pcm_size_rate); + device_printf(pdevinfo->dev, " ADC:"); + for (nids = sc->chans[i].io; *nids != -1; nids++) printf(" %d", *nids); printf("\n"); } @@ -5864,9 +6558,7 @@ hdac_dump_pcmchannels(struct hdac_softc *sc, int pcnt, int rcnt) static void hdac_release_resources(struct hdac_softc *sc) { - struct hdac_devinfo *devinfo = NULL; - device_t *devlist = NULL; - int i, devcount; + int i, j; if (sc == NULL) return; @@ -5877,7 +6569,7 @@ hdac_release_resources(struct hdac_softc *sc) callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); callout_stop(&sc->poll_jack); - hdac_reset(sc); + hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); callout_drain(&sc->poll_hda); @@ -5886,43 +6578,40 @@ hdac_release_resources(struct hdac_softc *sc) hdac_irq_free(sc); - device_get_children(sc->dev, &devlist, &devcount); - for (i = 0; devlist != NULL && i < devcount; i++) { - devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); - if (devinfo == NULL) - continue; - if (devinfo->widget != NULL) - free(devinfo->widget, M_HDAC); - if (devinfo->node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && - devinfo->function.audio.ctl != NULL) - free(devinfo->function.audio.ctl, M_HDAC); - free(devinfo, M_HDAC); - device_delete_child(sc->dev, devlist[i]); - } - if (devlist != NULL) - free(devlist, M_TEMP); - for (i = 0; i < HDAC_CODEC_MAX; i++) { - if (sc->codecs[i] != NULL) - free(sc->codecs[i], M_HDAC); + if (sc->codecs[i] == NULL) + continue; + for (j = 0; j < sc->codecs[i]->num_fgs; j++) { + free(sc->codecs[i]->fgs[j].widget, M_HDAC); + if (sc->codecs[i]->fgs[j].node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + free(sc->codecs[i]->fgs[j].function.audio.ctl, + M_HDAC); + free(sc->codecs[i]->fgs[j].function.audio.as, + M_HDAC); + free(sc->codecs[i]->fgs[j].function.audio.devs, + M_HDAC); + } + } + free(sc->codecs[i]->fgs, M_HDAC); + free(sc->codecs[i], M_HDAC); sc->codecs[i] = NULL; } hdac_dma_free(sc, &sc->pos_dma); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); - if (sc->play.blkcnt > 0) - hdac_dma_free(sc, &sc->play.bdl_dma); - if (sc->rec.blkcnt > 0) - hdac_dma_free(sc, &sc->rec.bdl_dma); + for (i = 0; i < sc->num_chans; i++) { + if (sc->chans[i].blkcnt > 0) + hdac_dma_free(sc, &sc->chans[i].bdl_dma); + } + free(sc->chans, M_HDAC); if (sc->chan_dmat != NULL) { bus_dma_tag_destroy(sc->chan_dmat); sc->chan_dmat = NULL; } hdac_mem_free(sc); snd_mtxfree(sc->lock); - free(sc, M_DEVBUF); } /* This function surely going to make its way into upper level someday. */ @@ -5944,7 +6633,7 @@ hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) if (!(res != NULL && strlen(res) > 0)) return; HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: HDA Config:"); + device_printf(sc->dev, "HDA Config:"); ); for (;;) { while (res[i] != '\0' && @@ -5990,17 +6679,14 @@ static int sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; - struct hdac_devinfo *devinfo; device_t dev; uint32_t ctl; int err, val; dev = oidp->oid_arg1; - devinfo = pcm_getdevinfo(dev); - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) + sc = device_get_softc(dev); + if (sc == NULL) return (EINVAL); - sc = devinfo->codec->sc; hdac_lock(sc); val = sc->polling; hdac_unlock(sc); @@ -6013,35 +6699,27 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) hdac_lock(sc); if (val != sc->polling) { - if (hda_chan_active(sc) != 0) - err = EBUSY; - else if (val == 0) { + if (val == 0) { + callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); hdac_unlock(sc); + callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); hdac_lock(sc); - HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, - sc->rirb_size / 2); - ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); - ctl |= HDAC_RIRBCTL_RINTCTL; - HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, - HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); sc->polling = 0; - DELAY(1000); + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= HDAC_INTCTL_GIE; + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); } else { - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, 0); - HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, 0); - ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); - ctl &= ~HDAC_RIRBCTL_RINTCTL; - HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl); + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~HDAC_INTCTL_GIE; + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); hdac_lock(sc); - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, - sc); sc->polling = 1; - DELAY(1000); + hdac_poll_reinit(sc); + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } } hdac_unlock(sc); @@ -6053,16 +6731,13 @@ static int sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; - struct hdac_devinfo *devinfo; device_t dev; int err, val; dev = oidp->oid_arg1; - devinfo = pcm_getdevinfo(dev); - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) + sc = device_get_softc(dev); + if (sc == NULL) return (EINVAL); - sc = devinfo->codec->sc; hdac_lock(sc); val = ((uint64_t)sc->poll_ival * 1000) / hz; hdac_unlock(sc); @@ -6088,153 +6763,167 @@ sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) return (err); } -#ifdef SND_DEBUG static int sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; + struct hdac_codec *codec; struct hdac_devinfo *devinfo; struct hdac_widget *w; device_t dev; - uint32_t res, pincap, timeout; + uint32_t res, pincap, delay; + int codec_index, fg_index; int i, err, val; nid_t cad; dev = oidp->oid_arg1; - devinfo = pcm_getdevinfo(dev); - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) + sc = device_get_softc(dev); + if (sc == NULL) return (EINVAL); val = 0; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL || val == 0) return (err); - sc = devinfo->codec->sc; - cad = devinfo->codec->cad; + + /* XXX: Temporary. For debugging. */ + if (val == 100) { + hdac_suspend(dev); + return (0); + } else if (val == 101) { + hdac_resume(dev); + return (0); + } + hdac_lock(sc); - device_printf(dev, "HDAC Dump AFG [nid=%d]:\n", devinfo->nid); - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { + codec = sc->codecs[codec_index]; + if (codec == NULL) continue; - pincap = w->wclass.pin.cap; - if ((HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || - HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) && - HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { - timeout = 10000; - hdac_command(sc, - HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), cad); - do { + cad = codec->cad; + for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { + devinfo = &codec->fgs[fg_index]; + if (devinfo->node_type != + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) + continue; + + device_printf(dev, "Dumping AFG cad=%d nid=%d pins:\n", + codec_index, devinfo->nid); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdac_dump_pin_config(w, w->wclass.pin.config); + pincap = w->wclass.pin.cap; + device_printf(dev, " Caps: %2s %3s %2s %4s %4s", + HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", + HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", + HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", + HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", + HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { + delay = 0; + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), cad); + do { + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); + if (res != 0x7fffffff && res != 0xffffffff) + break; + DELAY(10); + } while (++delay < 10000); + } else { + delay = 0; + res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, + w->nid), cad); + } + printf(" Sense: 0x%08x", res); + if (delay > 0) + printf(" delay %dus", delay * 10); + } + printf("\n"); + } + device_printf(dev, + "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); + if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { + device_printf(dev, " GPI:"); res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); - if (res != 0x7fffffff) - break; - DELAY(10); - } while (--timeout != 0); - } else { - timeout = -1; - res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, - w->nid), cad); + HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); + printf(" data=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), + cad); + printf(" wake=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), + cad); + printf(" unsol=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); + printf(" sticky=0x%08x\n", res); + } + if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { + device_printf(dev, " GPO:"); + res = hdac_command(sc, + HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); + printf(" data=0x%08x\n", res); + } + if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { + device_printf(dev, "GPIO:"); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); + printf(" data=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); + printf(" enable=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); + printf(" direction=0x%08x\n", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); + device_printf(dev, " wake=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), + cad); + printf(" unsol=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); + printf(" sticky=0x%08x\n", res); + } } - device_printf(dev, - "PIN_SENSE: nid=%-3d timeout=%d res=0x%08x [%s]\n", - w->nid, timeout, res, - (w->enable == 0) ? "DISABLED" : "ENABLED"); - } - device_printf(dev, - "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", - HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); - if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { - device_printf(dev, " GPI:"); - res = hdac_command(sc, - HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" wake=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" unsol=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); - printf(" sticky=0x%08x\n", res); - } - if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { - device_printf(dev, " GPO:"); - res = hdac_command(sc, - HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x\n", res); - } - if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { - device_printf(dev, "GPI0:"); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); - printf(" enable=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); - printf(" direction=0x%08x\n", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); - device_printf(dev, " wake=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" unsol=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); - printf(" sticky=0x%08x\n", res); } hdac_unlock(sc); return (0); } #endif -#endif static void hdac_attach2(void *arg) { + struct hdac_codec *codec; struct hdac_softc *sc; - struct hdac_widget *w; struct hdac_audio_ctl *ctl; uint32_t quirks_on, quirks_off; - int pcnt, rcnt, codec_index; - int i; - char status[SND_STATUSLEN]; - device_t *devlist = NULL; - int devcount; - struct hdac_devinfo *devinfo = NULL; + int codec_index, fg_index; + int i, pdev, rdev, dmaalloc = 0; + struct hdac_devinfo *devinfo; sc = (struct hdac_softc *)arg; hdac_config_fetch(sc, &quirks_on, &quirks_off); HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: HDA Config: on=0x%08x off=0x%08x\n", + device_printf(sc->dev, "HDA Config: on=0x%08x off=0x%08x\n", quirks_on, quirks_off); ); - if (resource_int_value(device_get_name(sc->dev), - device_get_unit(sc->dev), "codec_index", &codec_index) != 0) { - switch (sc->pci_subvendor) { - case GB_G33S2H_SUBVENDOR: - codec_index = 2; - break; - default: - codec_index = 0; - break; - } - } - hdac_lock(sc); /* Remove ourselves from the config hooks */ @@ -6245,266 +6934,476 @@ hdac_attach2(void *arg) /* Start the corb and rirb engines */ HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Starting CORB Engine...\n"); + device_printf(sc->dev, "Starting CORB Engine...\n"); ); hdac_corb_start(sc); HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Starting RIRB Engine...\n"); + device_printf(sc->dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); HDA_BOOTVERBOSE( device_printf(sc->dev, - "HDA_DEBUG: Enabling controller interrupt...\n"); + "Enabling controller interrupt...\n"); ); - if (sc->polling == 0) - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, - HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); - + if (sc->polling == 0) { + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, + HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + } else { + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); + } DELAY(1000); HDA_BOOTVERBOSE( device_printf(sc->dev, - "HDA_DEBUG: Scanning HDA codecs [start index=%d] ...\n", - codec_index); + "Scanning HDA codecs ...\n"); ); - hdac_scan_codecs(sc, codec_index); + hdac_scan_codecs(sc); + + for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { + codec = sc->codecs[codec_index]; + if (codec == NULL) + continue; + for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { + devinfo = &codec->fgs[fg_index]; + device_printf(sc->dev, "\n"); + if (devinfo->node_type != + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Power down unsupported non-audio FG" + " cad=%d nid=%d to the D3 state...\n", + codec->cad, devinfo->nid); + ); + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(codec->cad, + devinfo->nid, HDA_CMD_POWER_STATE_D3), + codec->cad); + continue; + } - device_get_children(sc->dev, &devlist, &devcount); - for (i = 0; devlist != NULL && i < devcount; i++) { - devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); - if (devinfo != NULL && devinfo->node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - break; - } else - devinfo = NULL; - } - if (devlist != NULL) - free(devlist, M_TEMP); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Power up audio FG cad=%d nid=%d...\n", + devinfo->codec->cad, devinfo->nid); + ); + hdac_powerup(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Parsing audio FG...\n"); + ); + hdac_audio_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Parsing Ctls...\n"); + ); + hdac_audio_ctl_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Parsing vendor patch...\n"); + ); + hdac_vendor_patch_parse(devinfo); + devinfo->function.audio.quirks |= quirks_on; + devinfo->function.audio.quirks &= ~quirks_off; - if (devinfo == NULL) { - hdac_unlock(sc); - device_printf(sc->dev, "Audio Function Group not found!\n"); - hdac_release_resources(sc); - return; + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling nonaudio...\n"); + ); + hdac_audio_disable_nonaudio(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling useless...\n"); + ); + hdac_audio_disable_useless(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Patched pins configuration:\n"); + hdac_dump_pin_configs(devinfo); + device_printf(sc->dev, "Parsing pin associations...\n"); + ); + hdac_audio_as_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Building AFG tree...\n"); + ); + hdac_audio_build_tree(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling unassociated " + "widgets...\n"); + ); + hdac_audio_disable_unas(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling nonselected " + "inputs...\n"); + ); + hdac_audio_disable_notselected(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling useless...\n"); + ); + hdac_audio_disable_useless(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling " + "crossassociatement connections...\n"); + ); + hdac_audio_disable_crossas(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Disabling useless...\n"); + ); + hdac_audio_disable_useless(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Binding associations to channels...\n"); + ); + hdac_audio_bind_as(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Assigning names to signal sources...\n"); + ); + hdac_audio_assign_names(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Assigning mixers to the tree...\n"); + ); + hdac_audio_assign_mixers(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Preparing pin controls...\n"); + ); + hdac_audio_prepare_pin_ctrl(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "AFG commit...\n"); + ); + hdac_audio_commit(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Ctls commit...\n"); + ); + hdac_audio_ctl_commit(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HP switch init...\n"); + ); + hdac_hp_switch_init(devinfo); + + if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && + dmaalloc == 0) { + if (hdac_dma_alloc(sc, &sc->pos_dma, + (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Failed to " + "allocate DMA pos buffer " + "(non-fatal)\n"); + ); + } else + dmaalloc = 1; + } + + i = devinfo->function.audio.playcnt; + if (devinfo->function.audio.reccnt > i) + i = devinfo->function.audio.reccnt; + devinfo->function.audio.devs = + (struct hdac_pcm_devinfo *)malloc( + sizeof(struct hdac_pcm_devinfo) * i, + M_HDAC, M_ZERO | M_NOWAIT); + if (devinfo->function.audio.devs == NULL) { + device_printf(sc->dev, + "Unable to allocate memory for devices\n"); + continue; + } + devinfo->function.audio.num_devs = i; + for (i = 0; i < devinfo->function.audio.num_devs; i++) { + devinfo->function.audio.devs[i].index = i; + devinfo->function.audio.devs[i].devinfo = devinfo; + devinfo->function.audio.devs[i].play = -1; + devinfo->function.audio.devs[i].rec = -1; + } + pdev = 0; + rdev = 0; + for (i = 0; i < devinfo->function.audio.ascnt; i++) { + if (devinfo->function.audio.as[i].enable == 0) + continue; + if (devinfo->function.audio.as[i].dir == + HDA_CTL_IN) { + devinfo->function.audio.devs[rdev].rec + = devinfo->function.audio.as[i].chan; + sc->chans[devinfo->function.audio.as[i].chan].pdevinfo = + &devinfo->function.audio.devs[rdev]; + rdev++; + } else { + devinfo->function.audio.devs[pdev].play + = devinfo->function.audio.as[i].chan; + sc->chans[devinfo->function.audio.as[i].chan].pdevinfo = + &devinfo->function.audio.devs[pdev]; + pdev++; + } + } + for (i = 0; i < devinfo->function.audio.num_devs; i++) { + struct hdac_pcm_devinfo *pdevinfo = + &devinfo->function.audio.devs[i]; + pdevinfo->dev = + device_add_child(sc->dev, "pcm", -1); + device_set_ivars(pdevinfo->dev, + (void *)pdevinfo); + } + + HDA_BOOTVERBOSE( + if (devinfo->function.audio.quirks != 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "HDA config/quirks:"); + for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { + if ((devinfo->function.audio.quirks & + hdac_quirks_tab[i].value) == + hdac_quirks_tab[i].value) + printf(" %s", hdac_quirks_tab[i].key); + } + printf("\n"); + } + + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA NODES |\n"); + device_printf(sc->dev, "+-------------------+\n"); + hdac_dump_nodes(devinfo); + + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "\n"); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + device_printf(sc->dev, "%3d: nid %3d %s (%s) index %d", i, + (ctl->widget != NULL) ? ctl->widget->nid : -1, + (ctl->ndir == HDA_CTL_IN)?"in ":"out", + (ctl->dir == HDA_CTL_IN)?"in ":"out", + ctl->index); + if (ctl->childwidget != NULL) + printf(" cnid %3d", ctl->childwidget->nid); + else + printf(" "); + printf(" ossmask=0x%08x\n", + ctl->ossmask); + device_printf(sc->dev, + " mute: %d step: %3d size: %3d off: %3d%s\n", + ctl->mute, ctl->step, ctl->size, ctl->offset, + (ctl->enable == 0) ? " [DISABLED]" : + ((ctl->ossmask == 0) ? " [UNUSED]" : "")); + } + ); + } } + hdac_unlock(sc); HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: Parsing AFG nid=%d cad=%d\n", - devinfo->nid, devinfo->codec->cad); + device_printf(sc->dev, "\n"); ); - hdac_audio_parse(devinfo); + + bus_generic_attach(sc->dev); + +#ifdef SND_DYNSYSCTL + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_polling, "I", "Enable polling mode"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, + sizeof(sc->dev), sysctl_hdac_polling_interval, "I", + "Controller/Jack Sense polling interval (1-1000 ms)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_pindump, "I", "Dump pin states/data"); +#endif +} + +/**************************************************************************** + * int hdac_suspend(device_t) + * + * Suspend and power down HDA bus and codecs. + ****************************************************************************/ +static int +hdac_suspend(device_t dev) +{ + struct hdac_softc *sc; + struct hdac_codec *codec; + struct hdac_devinfo *devinfo; + int codec_index, fg_index, i; + HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Parsing Ctls...\n"); + device_printf(dev, "Suspend...\n"); ); - hdac_audio_ctl_parse(devinfo); + + sc = device_get_softc(dev); + hdac_lock(sc); + HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Parsing vendor patch...\n"); + device_printf(dev, "Stop streams...\n"); ); - hdac_vendor_patch_parse(devinfo); - if (quirks_on != 0) - devinfo->function.audio.quirks |= quirks_on; - if (quirks_off != 0) - devinfo->function.audio.quirks &= ~quirks_off; - - /* XXX Disable all DIGITAL path. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL) - continue; - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - w->enable = 0; - continue; + for (i = 0; i < sc->num_chans; i++) { + if (sc->chans[i].flags & HDAC_CHN_RUNNING) { + sc->chans[i].flags |= HDAC_CHN_SUSPEND; + hdac_channel_stop(sc, &sc->chans[i]); } - /* XXX Disable useless pin ? */ - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - (w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) - w->enable = 0; } - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->widget == NULL) - continue; - if (ctl->ossmask & SOUND_MASK_DISABLE) - ctl->enable = 0; - w = ctl->widget; - if (w->enable == 0 || - HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) - ctl->enable = 0; - w = ctl->childwidget; - if (w == NULL) + + for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { + codec = sc->codecs[codec_index]; + if (codec == NULL) continue; - if (w->enable == 0 || - HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) - ctl->enable = 0; + for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { + devinfo = &codec->fgs[fg_index]; + HDA_BOOTVERBOSE( + device_printf(dev, + "Power down FG" + " cad=%d nid=%d to the D3 state...\n", + codec->cad, devinfo->nid); + ); + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(codec->cad, + devinfo->nid, HDA_CMD_POWER_STATE_D3), + codec->cad); + } } HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Building AFG tree...\n"); + device_printf(dev, "Reset controller...\n"); ); - hdac_audio_build_tree(devinfo); + callout_stop(&sc->poll_hda); + callout_stop(&sc->poll_hdac); + callout_stop(&sc->poll_jack); + hdac_reset(sc, 0); + hdac_unlock(sc); + taskqueue_drain(taskqueue_thread, &sc->unsolq_task); + callout_drain(&sc->poll_hda); + callout_drain(&sc->poll_hdac); + callout_drain(&sc->poll_jack); - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->ossmask & (SOUND_MASK_SKIP | SOUND_MASK_DISABLE)) - ctl->ossmask = 0; - } HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: AFG commit...\n"); + device_printf(dev, "Suspend done\n"); ); - hdac_audio_commit(devinfo, HDA_COMMIT_ALL); + + return (0); +} + +/**************************************************************************** + * int hdac_resume(device_t) + * + * Powerup and restore HDA bus and codecs state. + ****************************************************************************/ +static int +hdac_resume(device_t dev) +{ + struct hdac_softc *sc; + struct hdac_codec *codec; + struct hdac_devinfo *devinfo; + int codec_index, fg_index, i; + HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: Ctls commit...\n"); + device_printf(dev, "Resume...\n"); ); - hdac_audio_ctl_commit(devinfo); + sc = device_get_softc(dev); + hdac_lock(sc); + + /* Quiesce everything */ HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: PCMDIR_PLAY setup...\n"); + device_printf(dev, "Reset controller...\n"); ); - pcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_PLAY); + hdac_reset(sc, 1); + + /* Initialize the CORB and RIRB */ + hdac_corb_init(sc); + hdac_rirb_init(sc); + + /* Start the corb and rirb engines */ HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA_DEBUG: PCMDIR_REC setup...\n"); + device_printf(dev, "Starting CORB Engine...\n"); ); - rcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_REC); - - hdac_unlock(sc); + hdac_corb_start(sc); HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: OSS mixer initialization...\n"); + device_printf(dev, "Starting RIRB Engine...\n"); ); - - /* - * There is no point of return after this. If the driver failed, - * so be it. Let the detach procedure do all the cleanup. - */ - if (mixer_init(sc->dev, &hdac_audio_ctl_ossmixer_class, devinfo) != 0) - device_printf(sc->dev, "Can't register mixer\n"); - - if (pcnt > 0) - pcnt = 1; - if (rcnt > 0) - rcnt = 1; + hdac_rirb_start(sc); HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: Registering PCM channels...\n"); + device_printf(dev, + "Enabling controller interrupt...\n"); ); - if (pcm_register(sc->dev, devinfo, pcnt, rcnt) != 0) - device_printf(sc->dev, "Can't register PCM\n"); - - sc->registered++; - - if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && - hdac_dma_alloc(sc, &sc->pos_dma, - (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "Failed to allocate DMA pos buffer (non-fatal)\n"); - ); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | + HDAC_GCTL_UNSOL); + if (sc->polling == 0) { + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, + HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + } else { + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } + DELAY(1000); + + for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { + codec = sc->codecs[codec_index]; + if (codec == NULL) + continue; + for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { + devinfo = &codec->fgs[fg_index]; + if (devinfo->node_type != + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + HDA_BOOTVERBOSE( + device_printf(dev, + "Power down unsupported non-audio FG" + " cad=%d nid=%d to the D3 state...\n", + codec->cad, devinfo->nid); + ); + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(codec->cad, + devinfo->nid, HDA_CMD_POWER_STATE_D3), + codec->cad); + continue; + } - for (i = 0; i < pcnt; i++) - pcm_addchan(sc->dev, PCMDIR_PLAY, &hdac_channel_class, devinfo); - for (i = 0; i < rcnt; i++) - pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, + "Power up audio FG cad=%d nid=%d...\n", + devinfo->codec->cad, devinfo->nid); + ); + hdac_powerup(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "AFG commit...\n"); + ); + hdac_audio_commit(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "Ctls commit...\n"); + ); + hdac_audio_ctl_commit(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "HP switch init...\n"); + ); + hdac_hp_switch_init(devinfo); -#ifdef SND_DYNSYSCTL - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), - sysctl_hdac_polling, "I", "Enable polling mode"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, - sizeof(sc->dev), sysctl_hdac_polling_interval, "I", - "Controller/Jack Sense polling interval (1-1000 ms)"); -#ifdef SND_DEBUG - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), - sysctl_hdac_pindump, "I", "Dump pin states/data"); -#endif -#endif + hdac_unlock(sc); + for (i = 0; i < devinfo->function.audio.num_devs; i++) { + struct hdac_pcm_devinfo *pdevinfo = + &devinfo->function.audio.devs[i]; + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "OSS mixer reinitialization...\n"); + ); + if (mixer_reinit(pdevinfo->dev) == -1) + device_printf(pdevinfo->dev, + "unable to reinitialize the mixer\n"); + } + hdac_lock(sc); + } + } - snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]", - rman_get_start(sc->mem.mem_res), rman_get_start(sc->irq.irq_res), - PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV); - pcm_setstatus(sc->dev, status); - device_printf(sc->dev, "<HDA Codec: %s>\n", hdac_codec_name(devinfo)); HDA_BOOTVERBOSE( - device_printf(sc->dev, "<HDA Codec ID: 0x%08x>\n", - hdac_codec_id(devinfo)); + device_printf(dev, "Start streams...\n"); ); - device_printf(sc->dev, "<HDA Driver Revision: %s>\n", - HDA_DRV_TEST_REV); + for (i = 0; i < sc->num_chans; i++) { + if (sc->chans[i].flags & HDAC_CHN_SUSPEND) { + sc->chans[i].flags &= ~HDAC_CHN_SUSPEND; + hdac_channel_start(sc, &sc->chans[i]); + } + } + + hdac_unlock(sc); HDA_BOOTVERBOSE( - if (devinfo->function.audio.quirks != 0) { - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "HDA config/quirks:"); - for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { - if ((devinfo->function.audio.quirks & - hdac_quirks_tab[i].value) == - hdac_quirks_tab[i].value) - printf(" %s", hdac_quirks_tab[i].key); - } - printf("\n"); - } - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "+-------------------+\n"); - device_printf(sc->dev, "| DUMPING HDA NODES |\n"); - device_printf(sc->dev, "+-------------------+\n"); - hdac_dump_nodes(devinfo); - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "+------------------------+\n"); - device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); - device_printf(sc->dev, "+------------------------+\n"); - device_printf(sc->dev, "\n"); - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - device_printf(sc->dev, "%3d: nid=%d", i, - (ctl->widget != NULL) ? ctl->widget->nid : -1); - if (ctl->childwidget != NULL) - printf(" cnid=%d", ctl->childwidget->nid); - printf(" dir=0x%x index=%d " - "ossmask=0x%08x ossdev=%d%s\n", - ctl->dir, ctl->index, - ctl->ossmask, ctl->ossdev, - (ctl->enable == 0) ? " [DISABLED]" : ""); - } - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "+-----------------------------------+\n"); - device_printf(sc->dev, "| DUMPING HDA AUDIO/VOLUME CONTROLS |\n"); - device_printf(sc->dev, "+-----------------------------------+\n"); - hdac_dump_ctls(devinfo, "Master Volume (OSS: vol)", SOUND_MASK_VOLUME); - hdac_dump_ctls(devinfo, "PCM Volume (OSS: pcm)", SOUND_MASK_PCM); - hdac_dump_ctls(devinfo, "CD Volume (OSS: cd)", SOUND_MASK_CD); - hdac_dump_ctls(devinfo, "Microphone Volume (OSS: mic)", SOUND_MASK_MIC); - hdac_dump_ctls(devinfo, "Line-in Volume (OSS: line)", SOUND_MASK_LINE); - hdac_dump_ctls(devinfo, "Recording Level (OSS: rec)", SOUND_MASK_RECLEV); - hdac_dump_ctls(devinfo, "Speaker/Beep (OSS: speaker)", SOUND_MASK_SPEAKER); - hdac_dump_ctls(devinfo, NULL, 0); - hdac_dump_dac(devinfo); - hdac_dump_adc(devinfo); - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "+--------------------------------------+\n"); - device_printf(sc->dev, "| DUMPING PCM Playback/Record Channels |\n"); - device_printf(sc->dev, "+--------------------------------------+\n"); - hdac_dump_pcmchannels(sc, pcnt, rcnt); + device_printf(dev, "Resume done\n"); ); - if (sc->polling != 0) { - hdac_lock(sc); - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); - hdac_unlock(sc); - } + return (0); } - /**************************************************************************** * int hdac_detach(device_t) * @@ -6513,21 +7412,17 @@ hdac_attach2(void *arg) static int hdac_detach(device_t dev) { - struct hdac_softc *sc = NULL; - struct hdac_devinfo *devinfo = NULL; - int err; + struct hdac_softc *sc; + device_t *devlist = NULL; + int i, devcount; - devinfo = (struct hdac_devinfo *)pcm_getdevinfo(dev); - if (devinfo != NULL && devinfo->codec != NULL) - sc = devinfo->codec->sc; - if (sc == NULL) - return (0); + sc = device_get_softc(dev); - if (sc->registered > 0) { - err = pcm_unregister(dev); - if (err != 0) - return (err); - } + device_get_children(dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) + device_delete_child(dev, devlist[i]); + if (devlist != NULL) + free(devlist, M_TEMP); hdac_release_resources(sc); @@ -6539,15 +7434,159 @@ static device_method_t hdac_methods[] = { DEVMETHOD(device_probe, hdac_probe), DEVMETHOD(device_attach, hdac_attach), DEVMETHOD(device_detach, hdac_detach), + DEVMETHOD(device_suspend, hdac_suspend), + DEVMETHOD(device_resume, hdac_resume), { 0, 0 } }; static driver_t hdac_driver = { - "pcm", + "hdac", hdac_methods, - PCM_SOFTC_SIZE, + sizeof(struct hdac_softc), }; -DRIVER_MODULE(snd_hda, pci, hdac_driver, pcm_devclass, 0, 0); +static devclass_t hdac_devclass; + +DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_hda, 1); + +static int +hdac_pcm_probe(device_t dev) +{ + struct hdac_pcm_devinfo *pdevinfo = + (struct hdac_pcm_devinfo *)device_get_ivars(dev); + char buf[128]; + + snprintf(buf, sizeof(buf), "HDA codec #%d %s PCM #%d", + pdevinfo->devinfo->codec->cad, + hdac_codec_name(pdevinfo->devinfo->codec), + pdevinfo->index); + device_set_desc_copy(dev, buf); + return (0); +} + +static int +hdac_pcm_attach(device_t dev) +{ + struct hdac_pcm_devinfo *pdevinfo = + (struct hdac_pcm_devinfo *)device_get_ivars(dev); + struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; + char status[SND_STATUSLEN]; + int i; + + pdevinfo->chan_size = pcm_getbuffersize(dev, + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + + HDA_BOOTVERBOSE( + device_printf(dev, "+--------------------------------------+\n"); + device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); + device_printf(dev, "+--------------------------------------+\n"); + hdac_dump_pcmchannels(pdevinfo); + device_printf(dev, "\n"); + device_printf(dev, "+--------------------------------+\n"); + device_printf(dev, "| DUMPING Playback/Record Pathes |\n"); + device_printf(dev, "+--------------------------------+\n"); + hdac_dump_dac(pdevinfo); + hdac_dump_adc(pdevinfo); + hdac_dump_mix(pdevinfo); + device_printf(dev, "\n"); + device_printf(dev, "+-------------------------+\n"); + device_printf(dev, "| DUMPING Volume Controls |\n"); + device_printf(dev, "+-------------------------+\n"); + hdac_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); + hdac_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); + hdac_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); + hdac_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); + hdac_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); + hdac_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); + hdac_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); + hdac_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); + hdac_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); + hdac_dump_ctls(pdevinfo, NULL, 0); + device_printf(dev, "\n"); + ); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + i &= HDA_BLK_ALIGN; + if (i < HDA_BLK_MIN) + i = HDA_BLK_MIN; + pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; + i = 0; + while (pdevinfo->chan_blkcnt >> i) + i++; + pdevinfo->chan_blkcnt = 1 << (i - 1); + if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) + pdevinfo->chan_blkcnt = HDA_BDL_MIN; + else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) + pdevinfo->chan_blkcnt = HDA_BDL_MAX; + } else + pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; + + /* + * We don't register interrupt handler with snd_setup_intr + * in pcm device. Mark pcm device as MPSAFE manually. + */ + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + + HDA_BOOTVERBOSE( + device_printf(dev, "OSS mixer initialization...\n"); + ); + if (mixer_init(dev, &hdac_audio_ctl_ossmixer_class, pdevinfo) != 0) + device_printf(dev, "Can't register mixer\n"); + + HDA_BOOTVERBOSE( + device_printf(dev, "Registering PCM channels...\n"); + ); + if (pcm_register(dev, pdevinfo, (pdevinfo->play >= 0)?1:0, + (pdevinfo->rec >= 0)?1:0) != 0) + device_printf(dev, "Can't register PCM\n"); + + pdevinfo->registered++; + + if (pdevinfo->play >= 0) + pcm_addchan(dev, PCMDIR_PLAY, &hdac_channel_class, pdevinfo); + if (pdevinfo->rec >= 0) + pcm_addchan(dev, PCMDIR_REC, &hdac_channel_class, pdevinfo); + + snprintf(status, SND_STATUSLEN, "at %s cad %d %s [%s]", + device_get_nameunit(sc->dev), pdevinfo->devinfo->codec->cad, + PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV); + pcm_setstatus(dev, status); + + return (0); +} + +static int +hdac_pcm_detach(device_t dev) +{ + struct hdac_pcm_devinfo *pdevinfo = + (struct hdac_pcm_devinfo *)device_get_ivars(dev); + int err; + + if (pdevinfo->registered > 0) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + } + + return (0); +} + +static device_method_t hdac_pcm_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdac_pcm_probe), + DEVMETHOD(device_attach, hdac_pcm_attach), + DEVMETHOD(device_detach, hdac_pcm_detach), + { 0, 0 } +}; + +static driver_t hdac_pcm_driver = { + "pcm", + hdac_pcm_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_hda_pcm, hdac, hdac_pcm_driver, pcm_devclass, 0, 0); + diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h index dd8d3c4..66bbbd8 100644 --- a/sys/dev/sound/pci/hda/hdac_private.h +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -161,18 +161,6 @@ struct hdac_command_list { typedef int nid_t; struct hdac_softc; -/**************************************************************************** - * struct hdac_codec - * - ****************************************************************************/ -struct hdac_codec { - int verbs_sent; - int responses_received; - nid_t cad; - struct hdac_command_list *commands; - struct hdac_softc *sc; -}; - struct hdac_bdle { volatile uint32_t addrl; volatile uint32_t addrh; @@ -183,15 +171,19 @@ struct hdac_bdle { #define HDA_MAX_CONNS 32 #define HDA_MAX_NAMELEN 32 -struct hdac_devinfo; - struct hdac_widget { nid_t nid; int type; int enable; int nconns, selconn; - uint32_t pflags, ctlflags; + int waspin; + uint32_t pflags; + int bindas; + int bindseqmask; + int ossdev; + uint32_t ossmask; nid_t conns[HDA_MAX_CONNS]; + u_char connsenable[HDA_MAX_CONNS]; char name[HDA_MAX_NAMELEN]; struct hdac_devinfo *devinfo; struct { @@ -201,7 +193,6 @@ struct hdac_widget { uint32_t supp_stream_formats; uint32_t supp_pcm_size_rate; uint32_t eapdbtl; - int outpath; } param; union { struct { @@ -215,12 +206,36 @@ struct hdac_widget { struct hdac_audio_ctl { struct hdac_widget *widget, *childwidget; int enable; - int index; + int index, dir, ndir; int mute, step, size, offset; - int left, right; + int left, right, forcemute; uint32_t muted; - int ossdev; - uint32_t dir, ossmask, ossval; + uint32_t ossmask, possmask; +}; + +/* Association is a group of pins bound for some special function. */ +struct hdac_audio_as { + u_char enable; + u_char index; + u_char dir; + u_char pincnt; + u_char fakeredir; + nid_t hpredir; + nid_t pins[16]; + nid_t dacs[16]; + int chan; +}; + +struct hdac_pcm_devinfo { + device_t dev; + struct hdac_devinfo *devinfo; + int index; + int registered; + int play, rec; + u_char left[SOUND_MIXER_NRDEVICES]; + u_char right[SOUND_MIXER_NRDEVICES]; + int chan_size; + int chan_blkcnt; }; /**************************************************************************** @@ -230,11 +245,6 @@ struct hdac_audio_ctl { * in the ivar of each child of the hdac bus ****************************************************************************/ struct hdac_devinfo { - device_t dev; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision_id; - uint8_t stepping_id; uint8_t node_type; nid_t nid; nid_t startnode, endnode; @@ -247,14 +257,14 @@ struct hdac_devinfo { uint32_t inamp_cap; uint32_t supp_stream_formats; uint32_t supp_pcm_size_rate; - int ctlcnt, pcnt, rcnt; + int ctlcnt, ascnt; struct hdac_audio_ctl *ctl; - uint32_t mvol; + struct hdac_audio_as *as; uint32_t quirks; uint32_t gpio; - int ossidx; int playcnt, reccnt; - int parsing_strategy; + struct hdac_pcm_devinfo *devs; + int num_devs; } audio; /* XXX undefined: modem, hdmi. */ } function; @@ -268,6 +278,7 @@ struct hdac_chan { struct pcm_channel *c; struct pcmchan_caps caps; struct hdac_devinfo *devinfo; + struct hdac_pcm_devinfo *pdevinfo; struct hdac_dma bdl_dma; uint32_t spd, fmt, fmtlist[8], pcmrates[16]; uint32_t supp_stream_formats, supp_pcm_size_rate; @@ -278,10 +289,29 @@ struct hdac_chan { int off; int sid; int bit16, bit32; + int as; nid_t io[16]; }; /**************************************************************************** + * struct hdac_codec + * + ****************************************************************************/ +struct hdac_codec { + int verbs_sent; + int responses_received; + nid_t cad; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + struct hdac_command_list *commands; + struct hdac_softc *sc; + struct hdac_devinfo *fgs; + int num_fgs; +}; + +/**************************************************************************** * struct hdac_softc * * This structure holds the current state of the hdac driver. @@ -303,6 +333,8 @@ struct hdac_softc { uint32_t flags; + struct hdac_chan *chans; + int num_chans; int num_iss; int num_oss; int num_bss; @@ -319,10 +351,7 @@ struct hdac_softc { struct hdac_dma pos_dma; - struct hdac_chan play, rec; bus_dma_tag_t chan_dmat; - int chan_size; - int chan_blkcnt; /* * Polling @@ -345,8 +374,6 @@ struct hdac_softc { uint32_t unsolq[HDAC_UNSOLQ_MAX]; struct hdac_codec *codecs[HDAC_CODEC_MAX]; - - int registered; }; /**************************************************************************** |