From 91b2708144df797921a873c06e802cf41cfce5ec Mon Sep 17 00:00:00 2001 From: mav Date: Fri, 8 Jun 2012 13:10:18 +0000 Subject: [PATCH] MFC r230181, r230312, r230326, r230331, r230451, r230465, r230488, r230507, r230511, r230513, r230532, r230537, r230551, r230554, r230571, r230574, r230585, r230641, r230768, r230807, r231024: Sync snd_hda(4) driver with HEAD. This includes major code refactoring, HDMI support, new volume control, automatic recording source selection, runtime reconfigureation, support for more then 4 PCM devices on controller, multichannel recording, additional playback/record streams, higher bandwidths support, more informative device names and many other things. Sponsored by: iXsystems, Inc. git-svn-id: svn://svn.freebsd.org/base/stable/8@236753 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- share/man/man4/snd_hda.4 | 24 +- sys/dev/sound/pci/hda/hda_reg.h | 48 + sys/dev/sound/pci/hda/hdaa.c | 1685 ++++++++++++++++++++------ sys/dev/sound/pci/hda/hdaa.h | 56 +- sys/dev/sound/pci/hda/hdaa_patches.c | 6 +- sys/dev/sound/pci/hda/hdac.c | 119 +- sys/dev/sound/pci/hda/hdac.h | 96 +- sys/dev/sound/pci/hda/hdac_if.m | 1 + sys/dev/sound/pci/hda/hdac_private.h | 5 + sys/dev/sound/pci/hda/hdacc.c | 130 +- sys/dev/sound/pcm/channel.c | 3 +- 11 files changed, 1688 insertions(+), 485 deletions(-) diff --git a/share/man/man4/snd_hda.4 b/share/man/man4/snd_hda.4 index cf2d94590..f768f2f55 100644 --- a/share/man/man4/snd_hda.4 +++ b/share/man/man4/snd_hda.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 11, 2012 +.Dd January 25, 2012 .Dt SND_HDA 4 .Os .Sh NAME @@ -182,6 +182,18 @@ May be specified as a 32-bit hexadecimal value with a leading or as a set of space-separated .Dq Ar option Ns = Ns Ar value pairs. +.It Va hint.pcm.%d.rec.autosrc +Controls automatic recording source feature: +.Bl -tag -compact +.It 0 +disabled, +.It 1 +once on attach, +.It 2 +enabled. +.El +When enabled, driver will automatically set recording source of the mixer to +connected input using jack presence detection statuses. .El .Pp Pin configuration is the UAA driver's main source of information about codec @@ -357,6 +369,16 @@ Original pin configuration written by BIOS. Setting this to a non-zero value makes driver to destroy existing pcm devices and process new pins configuration set via .Va dev.hdaa.%d.nid%d_config. +.It Va dev.pcm.%d.play.32bit , dev.pcm.%d.rec.32bit +HDA controller uses 32bit representation for all samples of more then 16 bits. +These variables allow to specify how many bits of these 32 should be +used by CODEC. +Depending on codec capabilities, possible values are 20, 24 and 32 bit. +The default value is 24. +.It Va dev.pcm.%d.rec.autosrc +Run-time equivalent of the +.Va hint.pcm.%d.rec.autosrc +tunable. .El .Sh EXAMPLES Taking HP Compaq DX2300 with Realtek ALC888 HDA codec for example. diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h index 847a62696..c60a74da7 100644 --- a/sys/dev/sound/pci/hda/hda_reg.h +++ b/sys/dev/sound/pci/hda/hda_reg.h @@ -419,6 +419,7 @@ HDA_CMD_VERB_SET_PIN_SENSE, (payload))) #define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT 0x80000000 +#define HDA_CMD_GET_PIN_SENSE_ELD_VALID 0x40000000 #define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK 0x7fffffff #define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT 0 @@ -675,17 +676,47 @@ HDA_CMD_VERB_SET_CONV_CHAN_COUNT, (payload))) #define HDA_CMD_VERB_GET_HDMI_DIP_SIZE 0xf2e + +#define HDA_CMD_GET_HDMI_DIP_SIZE(cad, nid, arg) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_SIZE, (arg))) + #define HDA_CMD_VERB_GET_HDMI_ELDD 0xf2f +#define HDA_CMD_GET_HDMI_ELDD(cad, nid, off) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_ELDD, (off))) + #define HDA_CMD_VERB_GET_HDMI_DIP_INDEX 0xf30 #define HDA_CMD_VERB_SET_HDMI_DIP_INDEX 0x730 +#define HDA_CMD_GET_HDMI_DIP_INDEX(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_INDEX, 0x0)) +#define HDA_CMD_SET_HDMI_DIP_INDEX(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_DIP_INDEX, (payload))) + #define HDA_CMD_VERB_GET_HDMI_DIP_DATA 0xf31 #define HDA_CMD_VERB_SET_HDMI_DIP_DATA 0x731 +#define HDA_CMD_GET_HDMI_DIP_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_DATA, 0x0)) +#define HDA_CMD_SET_HDMI_DIP_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_DIP_DATA, (payload))) + #define HDA_CMD_VERB_GET_HDMI_DIP_XMIT 0xf32 #define HDA_CMD_VERB_SET_HDMI_DIP_XMIT 0x732 +#define HDA_CMD_GET_HDMI_DIP_XMIT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_XMIT, 0x0)) +#define HDA_CMD_SET_HDMI_DIP_XMIT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_DIP_XMIT, (payload))) + #define HDA_CMD_VERB_GET_HDMI_CP_CTRL 0xf33 #define HDA_CMD_VERB_SET_HDMI_CP_CTRL 0x733 @@ -699,6 +730,23 @@ (HDA_CMD_12BIT((cad), (nid), \ HDA_CMD_VERB_SET_HDMI_CHAN_SLOT, (payload))) +#define HDA_HDMI_CODING_TYPE_REF_STREAM_HEADER 0 +#define HDA_HDMI_CODING_TYPE_LPCM 1 +#define HDA_HDMI_CODING_TYPE_AC3 2 +#define HDA_HDMI_CODING_TYPE_MPEG1 3 +#define HDA_HDMI_CODING_TYPE_MP3 4 +#define HDA_HDMI_CODING_TYPE_MPEG2 5 +#define HDA_HDMI_CODING_TYPE_AACLC 6 +#define HDA_HDMI_CODING_TYPE_DTS 7 +#define HDA_HDMI_CODING_TYPE_ATRAC 8 +#define HDA_HDMI_CODING_TYPE_SACD 9 +#define HDA_HDMI_CODING_TYPE_EAC3 10 +#define HDA_HDMI_CODING_TYPE_DTS_HD 11 +#define HDA_HDMI_CODING_TYPE_MLP 12 +#define HDA_HDMI_CODING_TYPE_DST 13 +#define HDA_HDMI_CODING_TYPE_WMAPRO 14 +#define HDA_HDMI_CODING_TYPE_REF_CTX 15 + /* Function Reset */ #define HDA_CMD_VERB_FUNCTION_RESET 0x7ff diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c index cee159ee2..82a667614 100644 --- a/sys/dev/sound/pci/hda/hdaa.c +++ b/sys/dev/sound/pci/hda/hdaa.c @@ -74,17 +74,6 @@ static const struct { #define HDAA_QUIRKS_TAB_LEN \ (sizeof(hdaa_quirks_tab) / sizeof(hdaa_quirks_tab[0])) -#define HDA_BDL_MIN 2 -#define HDA_BDL_MAX 256 -#define HDA_BDL_DEFAULT HDA_BDL_MIN - -#define HDA_BLK_MIN HDA_DMA_ALIGNMENT -#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) - -#define HDA_BUFSZ_MIN 4096 -#define HDA_BUFSZ_MAX 65536 -#define HDA_BUFSZ_DEFAULT 16384 - #define HDA_PARSE_MAXDEPTH 10 MALLOC_DEFINE(M_HDAA, "hdaa", "HDA Audio"); @@ -116,6 +105,12 @@ const char *HDA_LOCS[64] = { const char *HDA_GPIO_ACTIONS[8] = { "keep", "set", "clear", "disable", "input", "0x05", "0x06", "0x07"}; +const char *HDA_HDMI_CODING_TYPES[18] = { + "undefined", "LPCM", "AC-3", "MPEG1", "MP3", "MPEG2", "AAC-LC", "DTS", + "ATRAC", "DSD", "E-AC-3", "DTS-HD", "MLP", "DST", "WMAPro", "HE-AAC", + "HE-AACv2", "MPEG-Surround" +}; + /* Default */ static uint32_t hdaa_fmt[] = { SND_FORMAT(AFMT_S16_LE, 2, 0), @@ -169,6 +164,8 @@ static const struct { }; #define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) +const static char *ossnames[] = SOUND_DEVICE_NAMES; + /**************************************************************************** * Function prototypes ****************************************************************************/ @@ -187,7 +184,6 @@ static void hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf); static char * hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) { - static char *ossname[] = SOUND_DEVICE_NAMES; int i, first = 1; bzero(buf, len); @@ -195,7 +191,7 @@ hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) if (mask & (1 << i)) { if (first == 0) strlcat(buf, ", ", len); - strlcat(buf, ossname[i], len); + strlcat(buf, ossnames[i], len); first = 0; } } @@ -243,49 +239,30 @@ hdaa_audio_ctl_amp_get(struct hdaa_devinfo *devinfo, nid_t nid, int dir, } /* - * Jack detection (Speaker/HP redirection) event handler. + * Headphones redirection change handler. */ static void -hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid) +hdaa_hpredir_handler(struct hdaa_widget *w) { - struct hdaa_audio_as *as; - struct hdaa_widget *w; + struct hdaa_devinfo *devinfo = w->devinfo; + struct hdaa_audio_as *as = &devinfo->as[w->bindas]; + struct hdaa_widget *w1; struct hdaa_audio_ctl *ctl; - uint32_t val, res; - int j; - - as = &devinfo->as[asid]; - if (as->hpredir < 0) - return; - - w = hdaa_widget_get(devinfo, as->pins[15]); - if (w == NULL || w->enable == 0 || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - return; - - res = hda_command(devinfo->dev, - HDA_CMD_GET_PIN_SENSE(0, as->pins[15])); + uint32_t val; + int j, connected = w->wclass.pin.connected; HDA_BOOTVERBOSE( - device_printf(devinfo->dev, - "Pin sense: nid=%d sence=0x%08x", - as->pins[15], res); + device_printf((as->pdevinfo && as->pdevinfo->dev) ? + as->pdevinfo->dev : devinfo->dev, + "Redirect output to: %s\n", + connected ? "headphones": "main"); ); - - res = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0; - if (devinfo->quirks & HDAA_QUIRK_SENSEINV) - res ^= 1; - - HDA_BOOTVERBOSE( - printf(" %sconnected\n", res == 0 ? "dis" : ""); - ); - /* (Un)Mute headphone pin. */ ctl = hdaa_audio_ctl_amp_get(devinfo, - as->pins[15], HDAA_CTL_IN, -1, 1); + w->nid, HDAA_CTL_IN, -1, 1); if (ctl != NULL && ctl->mute) { /* If pin has muter - use it. */ - val = (res != 0) ? 0 : 1; + val = connected ? 0 : 1; if (val != ctl->forcemute) { ctl->forcemute = val; hdaa_audio_ctl_amp_set(ctl, @@ -294,21 +271,17 @@ hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid) } } else { /* If there is no muter - disable pin output. */ - w = hdaa_widget_get(devinfo, as->pins[15]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - 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; - hda_command(devinfo->dev, - HDA_CMD_SET_PIN_WIDGET_CTRL(0, - w->nid, w->wclass.pin.ctrl)); - } + if (connected) + 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; + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, + w->nid, w->wclass.pin.ctrl)); } } /* (Un)Mute other pins. */ @@ -319,7 +292,7 @@ hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid) as->pins[j], HDAA_CTL_IN, -1, 1); if (ctl != NULL && ctl->mute) { /* If pin has muter - use it. */ - val = (res != 0) ? 1 : 0; + val = connected ? 1 : 0; if (val == ctl->forcemute) continue; ctl->forcemute = val; @@ -329,32 +302,142 @@ hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid) continue; } /* If there is no muter - disable pin output. */ - w = hdaa_widget_get(devinfo, as->pins[j]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (res != 0) - val = w->wclass.pin.ctrl & + w1 = hdaa_widget_get(devinfo, as->pins[j]); + if (w1 != NULL) { + if (connected) + val = w1->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else - val = w->wclass.pin.ctrl | + val = w1->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val != w->wclass.pin.ctrl) { - w->wclass.pin.ctrl = val; + if (val != w1->wclass.pin.ctrl) { + w1->wclass.pin.ctrl = val; hda_command(devinfo->dev, HDA_CMD_SET_PIN_WIDGET_CTRL(0, - w->nid, w->wclass.pin.ctrl)); + w1->nid, w1->wclass.pin.ctrl)); } } } } /* - * Callback for poll based jack detection. + * Recording source change handler. + */ +static void +hdaa_autorecsrc_handler(struct hdaa_audio_as *as, struct hdaa_widget *w) +{ + struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo; + struct hdaa_devinfo *devinfo; + struct hdaa_widget *w1; + int i, mask, fullmask, prio, bestprio; + char buf[128]; + + if (!as->mixed || pdevinfo == NULL || pdevinfo->mixer == NULL) + return; + /* Don't touch anything if we asked not to. */ + if (pdevinfo->autorecsrc == 0 || + (pdevinfo->autorecsrc == 1 && w != NULL)) + return; + /* Don't touch anything if "mix" or "speaker" selected. */ + if (pdevinfo->recsrc & (SOUND_MASK_IMIX | SOUND_MASK_SPEAKER)) + return; + /* Don't touch anything if several selected. */ + if (ffs(pdevinfo->recsrc) != fls(pdevinfo->recsrc)) + return; + devinfo = pdevinfo->devinfo; + mask = fullmask = 0; + bestprio = 0; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + w1 = hdaa_widget_get(devinfo, as->pins[i]); + if (w1 == NULL || w1->enable == 0) + continue; + if (w1->wclass.pin.connected == 0) + continue; + prio = (w1->wclass.pin.connected == 1) ? 2 : 1; + if (prio < bestprio) + continue; + if (prio > bestprio) { + mask = 0; + bestprio = prio; + } + mask |= (1 << w1->ossdev); + fullmask |= (1 << w1->ossdev); + } + if (mask == 0) + return; + /* Prefer newly connected input. */ + if (w != NULL && (mask & (1 << w->ossdev))) + mask = (1 << w->ossdev); + /* Prefer previously selected input */ + if (mask & pdevinfo->recsrc) + mask &= pdevinfo->recsrc; + /* Prefer mic. */ + if (mask & SOUND_MASK_MIC) + mask = SOUND_MASK_MIC; + /* Prefer monitor (2nd mic). */ + if (mask & SOUND_MASK_MONITOR) + mask = SOUND_MASK_MONITOR; + /* Just take first one. */ + mask = (1 << (ffs(mask) - 1)); + HDA_BOOTVERBOSE( + hdaa_audio_ctl_ossmixer_mask2allname(mask, buf, sizeof(buf)); + device_printf(pdevinfo->dev, + "Automatically set rec source to: %s\n", buf); + ); + hdaa_unlock(devinfo); + mix_setrecsrc(pdevinfo->mixer, mask); + hdaa_lock(devinfo); +} + +/* + * Jack presence detection event handler. + */ +static void +hdaa_presence_handler(struct hdaa_widget *w) +{ + struct hdaa_devinfo *devinfo = w->devinfo; + struct hdaa_audio_as *as; + uint32_t res; + int connected; + + if (w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + return; + + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || + (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) + return; + + res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid)); + connected = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0; + if (devinfo->quirks & HDAA_QUIRK_SENSEINV) + connected = !connected; + if (connected == w->wclass.pin.connected) + return; + w->wclass.pin.connected = connected; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Pin sense: nid=%d sence=0x%08x (%sconnected)\n", + w->nid, res, !w->wclass.pin.connected ? "dis" : ""); + ); + + as = &devinfo->as[w->bindas]; + if (as->hpredir >= 0 && as->pins[15] == w->nid) + hdaa_hpredir_handler(w); + if (as->dir == HDAA_CTL_IN) + hdaa_autorecsrc_handler(as, w); +} + +/* + * Callback for poll based presence detection. */ static void hdaa_jack_poll_callback(void *arg) { struct hdaa_devinfo *devinfo = arg; + struct hdaa_widget *w; int i; hdaa_lock(devinfo); @@ -365,56 +448,203 @@ hdaa_jack_poll_callback(void *arg) for (i = 0; i < devinfo->ascnt; i++) { if (devinfo->as[i].hpredir < 0) continue; - hdaa_hp_switch_handler(devinfo, i); + w = hdaa_widget_get(devinfo, devinfo->as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdaa_presence_handler(w); } callout_reset(&devinfo->poll_jack, devinfo->poll_ival, hdaa_jack_poll_callback, devinfo); hdaa_unlock(devinfo); } +static void +hdaa_eld_dump(struct hdaa_widget *w) +{ + struct hdaa_devinfo *devinfo = w->devinfo; + device_t dev = devinfo->dev; + uint8_t *sad; + int len, mnl, i, sadc, fmt; + + if (w->eld == NULL || w->eld_len < 4) + return; + device_printf(dev, + "ELD nid=%d: ELD_Ver=%u Baseline_ELD_Len=%u\n", + w->nid, w->eld[0] >> 3, w->eld[2]); + if ((w->eld[0] >> 3) != 0x02) + return; + len = min(w->eld_len, (u_int)w->eld[2] * 4); + mnl = w->eld[4] & 0x1f; + device_printf(dev, + "ELD nid=%d: CEA_EDID_Ver=%u MNL=%u\n", + w->nid, w->eld[4] >> 5, mnl); + sadc = w->eld[5] >> 4; + device_printf(dev, + "ELD nid=%d: SAD_Count=%u Conn_Type=%u S_AI=%u HDCP=%u\n", + w->nid, sadc, (w->eld[5] >> 2) & 0x3, + (w->eld[5] >> 1) & 0x1, w->eld[5] & 0x1); + device_printf(dev, + "ELD nid=%d: Aud_Synch_Delay=%ums\n", + w->nid, w->eld[6] * 2); + device_printf(dev, + "ELD nid=%d: Channels=0x%b\n", + w->nid, w->eld[7], + "\020\07RLRC\06FLRC\05RC\04RLR\03FC\02LFE\01FLR"); + device_printf(dev, + "ELD nid=%d: Port_ID=0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + w->nid, w->eld[8], w->eld[9], w->eld[10], w->eld[11], + w->eld[12], w->eld[13], w->eld[14], w->eld[15]); + device_printf(dev, + "ELD nid=%d: Manufacturer_Name=0x%02x%02x\n", + w->nid, w->eld[16], w->eld[17]); + device_printf(dev, + "ELD nid=%d: Product_Code=0x%02x%02x\n", + w->nid, w->eld[18], w->eld[19]); + device_printf(dev, + "ELD nid=%d: Monitor_Name_String='%.*s'\n", + w->nid, mnl, &w->eld[20]); + for (i = 0; i < sadc; i++) { + sad = &w->eld[20 + mnl + i * 3]; + fmt = (sad[0] >> 3) & 0x0f; + if (fmt == HDA_HDMI_CODING_TYPE_REF_CTX) { + fmt = (sad[2] >> 3) & 0x1f; + if (fmt < 1 || fmt > 3) + fmt = 0; + else + fmt += 14; + } + device_printf(dev, + "ELD nid=%d: %s %dch freqs=0x%b", + w->nid, HDA_HDMI_CODING_TYPES[fmt], (sad[0] & 0x07) + 1, + sad[1], "\020\007192\006176\00596\00488\00348\00244\00132"); + switch (fmt) { + case HDA_HDMI_CODING_TYPE_LPCM: + printf(" sizes=0x%b", + sad[2] & 0x07, "\020\00324\00220\00116"); + break; + case HDA_HDMI_CODING_TYPE_AC3: + case HDA_HDMI_CODING_TYPE_MPEG1: + case HDA_HDMI_CODING_TYPE_MP3: + case HDA_HDMI_CODING_TYPE_MPEG2: + case HDA_HDMI_CODING_TYPE_AACLC: + case HDA_HDMI_CODING_TYPE_DTS: + case HDA_HDMI_CODING_TYPE_ATRAC: + printf(" max_bitrate=%d", sad[2] * 8000); + break; + case HDA_HDMI_CODING_TYPE_WMAPRO: + printf(" profile=%d", sad[2] & 0x07); + break; + } + printf("\n"); + } +} + +static void +hdaa_eld_handler(struct hdaa_widget *w) +{ + struct hdaa_devinfo *devinfo = w->devinfo; + uint32_t res; + int i; + + if (w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + return; + + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || + (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) + return; + + res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid)); + if ((w->eld != 0) == ((res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) != 0)) + return; + if (w->eld != NULL) { + w->eld_len = 0; + free(w->eld, M_HDAA); + w->eld = NULL; + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Pin sense: nid=%d sence=0x%08x " + "(%sconnected, ELD %svalid)\n", + w->nid, res, + (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ? "" : "dis", + (res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) ? "" : "in"); + ); + if ((res & HDA_CMD_GET_PIN_SENSE_ELD_VALID) == 0) + return; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_HDMI_DIP_SIZE(0, w->nid, 0x08)); + if (res == HDA_INVALID) + return; + w->eld_len = res & 0xff; + if (w->eld_len != 0) + w->eld = malloc(w->eld_len, M_HDAA, M_ZERO | M_NOWAIT); + if (w->eld == NULL) { + w->eld_len = 0; + return; + } + + for (i = 0; i < w->eld_len; i++) { + res = hda_command(devinfo->dev, + HDA_CMD_GET_HDMI_ELDD(0, w->nid, i)); + if (res & 0x80000000) + w->eld[i] = res & 0xff; + } + HDA_BOOTVERBOSE( + hdaa_eld_dump(w); + ); +} + /* - * Jack detection initializer. + * Pin sense initializer. */ static void -hdaa_hp_switch_init(struct hdaa_devinfo *devinfo) +hdaa_sense_init(struct hdaa_devinfo *devinfo) { - struct hdaa_audio_as *as = devinfo->as; - struct hdaa_widget *w; - int i, poll = 0; - - for (i = 0; i < devinfo->ascnt; i++) { - if (as[i].hpredir < 0) - continue; + struct hdaa_audio_as *as; + struct hdaa_widget *w; + int i, poll = 0; - w = hdaa_widget_get(devinfo, as[i].pins[15]); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); 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(devinfo->dev, - "No jack detection support at pin %d\n", - as[i].pins[15]); - continue; - } - if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { - as[i].unsol = HDAC_UNSOL_ALLOC( - device_get_parent(devinfo->dev), devinfo->dev, - w->nid); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap) && + w->unsol < 0) { + w->unsol = HDAC_UNSOL_ALLOC( + device_get_parent(devinfo->dev), devinfo->dev, w->nid); hda_command(devinfo->dev, HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, - HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | - as[i].unsol)); - } else - poll = 1; - HDA_BOOTVERBOSE( - device_printf(devinfo->dev, - "Headphones redirection " - "for as=%d nid=%d using %s.\n", - i, w->nid, - (poll != 0) ? "polling" : "unsolicited responses"); - ); - hdaa_hp_switch_handler(devinfo, i); + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | w->unsol)); + } + as = &devinfo->as[w->bindas]; + if (as->hpredir >= 0 && as->pins[15] == w->nid) { + 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(devinfo->dev, + "No presence detection support at nid %d\n", + as[i].pins[15]); + } else { + if (w->unsol < 0) + poll = 1; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Headphones redirection for " + "association %d nid=%d using %s.\n", + w->bindas, w->nid, + (poll != 0) ? "polling" : + "unsolicited responses"); + ); + }; + } + hdaa_presence_handler(w); + if (!HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) && + !HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) + continue; + hdaa_eld_handler(w); } if (poll) { callout_reset(&devinfo->poll_jack, 1, @@ -423,25 +653,25 @@ hdaa_hp_switch_init(struct hdaa_devinfo *devinfo) } static void -hdaa_hp_switch_deinit(struct hdaa_devinfo *devinfo) +hdaa_sense_deinit(struct hdaa_devinfo *devinfo) { - struct hdaa_audio_as *as = devinfo->as; - struct hdaa_widget *w; - int i; + struct hdaa_widget *w; + int i; - for (i = 0; i < devinfo->ascnt; i++) { - if (as[i].unsol < 0) - continue; - w = hdaa_widget_get(devinfo, as[i].pins[15]); + callout_stop(&devinfo->poll_jack); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); if (w == NULL || w->enable == 0 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; + if (w->unsol < 0) + continue; hda_command(devinfo->dev, HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, 0)); HDAC_UNSOL_FREE( device_get_parent(devinfo->dev), devinfo->dev, - as[i].unsol); - as[i].unsol = -1; + w->unsol); + w->unsol = -1; } } @@ -902,6 +1132,11 @@ hdaa_widget_parse(struct hdaa_widget *w) w->param.supp_pcm_size_rate = w->devinfo->supp_pcm_size_rate; } + if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) { + w->wclass.conv.stripecap = hda_command(dev, + HDA_CMD_GET_STRIPE_CONTROL(0, w->nid)) >> 20; + } else + w->wclass.conv.stripecap = 1; } else { w->param.supp_stream_formats = 0; w->param.supp_pcm_size_rate = 0; @@ -938,6 +1173,7 @@ hdaa_widget_parse(struct hdaa_widget *w) hdaa_sysctl_config, "A", "Original pin configuration"); hdaa_lock(w->devinfo); } + w->unsol = -1; } static void @@ -1001,6 +1237,10 @@ hdaa_widget_postprocess(struct hdaa_widget *w) } strlcat(w->name, HDA_CONNS[conn], sizeof(w->name)); strlcat(w->name, ")", sizeof(w->name)); + + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || + (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) + w->wclass.pin.connected = 2; } } @@ -1193,31 +1433,58 @@ hdaa_stream_format(struct hdaa_chan *ch) return (fmt); } +static int +hdaa_allowed_stripes(uint16_t fmt) +{ + static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; + int size; + + size = bits[(fmt >> 4) & 0x03]; + size *= (fmt & 0x0f) + 1; + size *= ((fmt >> 11) & 0x07) + 1; + return (0xffffffffU >> (32 - fls(size / 8))); +} + static void hdaa_audio_setup(struct hdaa_chan *ch) { struct hdaa_audio_as *as = &ch->devinfo->as[ch->as]; - struct hdaa_widget *w; - int i, chn, totalchn, c; + struct hdaa_widget *w, *wp; + int i, j, k, chn, cchn, totalchn, totalextchn, c; uint16_t fmt, dfmt; - uint16_t chmap[2][5] = {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ - { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ - int map = -1; + /* Mapping channel pairs to codec pins/converters. */ + const static uint16_t convmap[2][5] = + {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ + { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ + /* Mapping formats to HDMI channel allocations. */ + const static uint8_t hdmica[2][8] = + {{ 0x02, 0x00, 0x04, 0x08, 0x0a, 0x0e, 0x12, 0x12 }, /* x.0 */ + { 0x01, 0x03, 0x01, 0x03, 0x09, 0x0b, 0x0f, 0x13 }}; /* x.1 */ + /* Mapping formats to HDMI channels order. */ + const static uint32_t hdmich[2][8] = + {{ 0xFFFF0F00, 0xFFFFFF10, 0xFFF2FF10, 0xFF32FF10, + 0xFF324F10, 0xF5324F10, 0x54326F10, 0x54326F10 }, /* x.0 */ + { 0xFFFFF000, 0xFFFF0100, 0xFFFFF210, 0xFFFF2310, + 0xFF32F410, 0xFF324510, 0xF6324510, 0x76325410 }}; /* x.1 */ + int convmapid = -1; + nid_t nid; + uint8_t csum; totalchn = AFMT_CHANNEL(ch->fmt); + totalextchn = AFMT_EXTCHANNEL(ch->fmt); HDA_BOOTHVERBOSE( device_printf(ch->pdevinfo->dev, - "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", + "PCMDIR_%s: Stream setup fmt=%08x (%d.%d) speed=%d\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->fmt, ch->spd); + ch->fmt, totalchn - totalextchn, totalextchn, ch->spd); ); fmt = hdaa_stream_format(ch); - /* Set channel mapping for known speaker setups. */ + /* Set channels to I/O converters mapping for known speaker setups. */ if ((as->pinset == 0x0007 || as->pinset == 0x0013)) /* Standard 5.1 */ - map = 0; + convmapid = 0; else if (as->pinset == 0x0017) /* Standard 7.1 */ - map = 1; + convmapid = 1; dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; if (ch->fmt & AFMT_AC3) @@ -1234,22 +1501,16 @@ hdaa_audio_setup(struct hdaa_chan *ch) if (as->fakeredir && i == (as->pincnt - 1)) { c = (ch->sid << 4); } else { - if (map >= 0) /* Map known speaker setups. */ - chn = (((chmap[map][totalchn / 2] >> i * 4) & - 0xf) - 1) * 2; + /* Map channels to I/O converters, if set. */ + if (convmapid >= 0) + chn = (((convmap[convmapid][totalchn / 2] + >> i * 4) & 0xf) - 1) * 2; if (chn < 0 || chn >= totalchn) { c = 0; } else { c = (ch->sid << 4) | chn; } } - HDA_BOOTHVERBOSE( - device_printf(ch->pdevinfo->dev, - "PCMDIR_%s: Stream setup nid=%d: " - "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x\n", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->io[i], fmt, dfmt, c); - ); hda_command(ch->devinfo->dev, HDA_CMD_SET_CONV_FMT(0, ch->io[i], fmt)); if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { @@ -1258,15 +1519,112 @@ hdaa_audio_setup(struct hdaa_chan *ch) } hda_command(ch->devinfo->dev, HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], c)); -#if 0 - hda_command(ch->devinfo->dev, - HDA_CMD_SET_CONV_CHAN_COUNT(0, ch->io[i], 1)); - hda_command(ch->devinfo->dev, - HDA_CMD_SET_HDMI_CHAN_SLOT(0, ch->io[i], 0x00)); - hda_command(ch->devinfo->dev, - HDA_CMD_SET_HDMI_CHAN_SLOT(0, ch->io[i], 0x11)); -#endif - chn += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; + if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) { + hda_command(ch->devinfo->dev, + HDA_CMD_SET_STRIPE_CONTROL(0, w->nid, ch->stripectl)); + } + cchn = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); + if (cchn > 1 && chn < totalchn) { + cchn = min(cchn, totalchn - chn - 1); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_CHAN_COUNT(0, ch->io[i], cchn)); + } + HDA_BOOTHVERBOSE( + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup nid=%d: " + "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x, " + "chan_count=0x%02x, stripe=%d\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->io[i], fmt, dfmt, c, cchn, ch->stripectl); + ); + for (j = 0; j < 16; j++) { + if (as->dacs[ch->asindex][j] != ch->io[i]) + continue; + nid = as->pins[j]; + wp = hdaa_widget_get(ch->devinfo, nid); + if (wp == NULL) + continue; + if (!HDA_PARAM_PIN_CAP_DP(wp->wclass.pin.cap) && + !HDA_PARAM_PIN_CAP_HDMI(wp->wclass.pin.cap)) + continue; + + /* Set channel mapping. */ + for (k = 0; k < 8; k++) { + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_CHAN_SLOT(0, nid, + (((hdmich[totalextchn == 0 ? 0 : 1][totalchn - 1] + >> (k * 4)) & 0xf) << 4) | k)); + } + + /* + * Enable High Bit Rate (HBR) Encoded Packet Type + * (EPT), if supported and needed (8ch data). + */ + if (HDA_PARAM_PIN_CAP_HDMI(wp->wclass.pin.cap) && + HDA_PARAM_PIN_CAP_HBR(wp->wclass.pin.cap)) { + wp->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK; + if ((ch->fmt & AFMT_AC3) && (cchn == 7)) + wp->wclass.pin.ctrl |= 0x03; + hda_command(ch->devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, nid, + wp->wclass.pin.ctrl)); + } + + /* Stop audio infoframe transmission. */ + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_XMIT(0, nid, 0x00)); + + /* Clear audio infoframe buffer. */ + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00)); + for (k = 0; k < 32; k++) + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00)); + + /* Write HDMI/DisplayPort audio infoframe. */ + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00)); + if (w->eld != NULL && w->eld_len >= 6 && + ((w->eld[5] >> 2) & 0x3) == 1) { /* DisplayPort */ + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x84)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x1b)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x44)); + } else { /* HDMI */ + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x84)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x01)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x0a)); + csum = 0; + csum -= 0x84 + 0x01 + 0x0a + (totalchn - 1) + + hdmica[totalextchn == 0 ? 0 : 1][totalchn - 1]; + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, csum)); + } + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, totalchn - 1)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, 0x00)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_DATA(0, nid, + hdmica[totalextchn == 0 ? 0 : 1][totalchn - 1])); + + /* Start audio infoframe transmission. */ + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_INDEX(0, nid, 0x00)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_DIP_XMIT(0, nid, 0xc0)); + } + chn += cchn + 1; } } @@ -1351,6 +1709,8 @@ hdaa_channel_stop(struct hdaa_chan *ch) struct hdaa_widget *w; int i; + if ((ch->flags & HDAA_CHN_RUNNING) == 0) + return; ch->flags &= ~HDAA_CHN_RUNNING; HDAC_STREAM_STOP(device_get_parent(devinfo->dev), devinfo->dev, ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); @@ -1374,11 +1734,12 @@ static int hdaa_channel_start(struct hdaa_chan *ch) { struct hdaa_devinfo *devinfo = ch->devinfo; + uint32_t fmt; - ch->ptr = 0; - ch->prevptr = 0; + fmt = hdaa_stream_format(ch); + ch->stripectl = fls(ch->stripecap & hdaa_allowed_stripes(fmt)) - 1; ch->sid = HDAC_STREAM_ALLOC(device_get_parent(devinfo->dev), devinfo->dev, - ch->dir == PCMDIR_PLAY ? 1 : 0, hdaa_stream_format(ch), &ch->dmapos); + ch->dir == PCMDIR_PLAY ? 1 : 0, fmt, ch->stripectl, &ch->dmapos); if (ch->sid <= 0) return (EBUSY); hdaa_audio_setup(ch); @@ -1468,11 +1829,11 @@ hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m) struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdaa_devinfo *devinfo = pdevinfo->devinfo; struct hdaa_widget *w, *cw; - struct hdaa_audio_ctl *ctl; uint32_t mask, recmask; - int i, j, softpcmvol; + int i, j; hdaa_lock(devinfo); + pdevinfo->mixer = m; /* Make sure that in case of soft volume it won't stay muted. */ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { @@ -1480,11 +1841,10 @@ hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m) pdevinfo->right[i] = 100; } - mask = 0; - recmask = 0; - - /* Declate EAPD as ogain control. */ + /* Declare volume controls assigned to this association. */ + mask = pdevinfo->ossmask; if (pdevinfo->playas >= 0) { + /* Declate EAPD as ogain control. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdaa_widget_get(devinfo, i); if (w == NULL || w->enable == 0) @@ -1496,105 +1856,325 @@ hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m) mask |= SOUND_MASK_OGAIN; break; } + + /* Declare soft PCM volume if needed. */ + if ((mask & SOUND_MASK_PCM) == 0 || + (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL) || + pdevinfo->minamp[SOUND_MIXER_PCM] == + pdevinfo->maxamp[SOUND_MIXER_PCM]) { + mask |= SOUND_MASK_PCM; + pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "Forcing Soft PCM volume\n"); + ); + } + + /* Declare master volume if needed. */ + 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_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "Forcing master volume with PCM\n"); + ); + } } - /* Declare volume controls assigned to this association. */ - i = 0; - ctl = NULL; - while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) + /* Declare record sources available to this association. */ + recmask = 0; + if (pdevinfo->recas >= 0) { + for (i = 0; i < 16; i++) { + if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0) + continue; + w = hdaa_widget_get(devinfo, + devinfo->as[pdevinfo->recas].dacs[0][i]); + if (w == NULL || w->enable == 0) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->bindas != pdevinfo->recas && + cw->bindas != -2) + continue; + recmask |= cw->ossmask; + } + } + } + + recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + pdevinfo->ossmask = mask; + + mix_setrecdevs(m, recmask); + mix_setdevs(m, mask); + + hdaa_unlock(devinfo); + + return (0); +} + +/* + * Update amplification per pdevinfo per ossdev, calculate summary coefficient + * and write it to codec, update *left and *right to reflect remaining error. + */ +static void +hdaa_audio_ctl_dev_set(struct hdaa_audio_ctl *ctl, int ossdev, + int mute, int *left, int *right) +{ + int i, zleft, zright, sleft, sright, smute, lval, rval; + + ctl->devleft[ossdev] = *left; + ctl->devright[ossdev] = *right; + ctl->devmute[ossdev] = mute; + smute = sleft = sright = zleft = zright = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + sleft += ctl->devleft[i]; + sright += ctl->devright[i]; + smute |= ctl->devmute[i]; + if (i == ossdev) + continue; + zleft += ctl->devleft[i]; + zright += ctl->devright[i]; + } + lval = QDB2VAL(ctl, sleft); + rval = QDB2VAL(ctl, sright); + hdaa_audio_ctl_amp_set(ctl, smute, lval, rval); + *left -= VAL2QDB(ctl, lval) - VAL2QDB(ctl, QDB2VAL(ctl, zleft)); + *right -= VAL2QDB(ctl, rval) - VAL2QDB(ctl, QDB2VAL(ctl, zright)); +} + +/* + * Trace signal from source, setting volumes on the way. + */ +static void +hdaa_audio_ctl_source_volume(struct hdaa_pcm_devinfo *pdevinfo, + int ossdev, nid_t nid, int index, int mute, int left, int right, int depth) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *wc; + struct hdaa_audio_ctl *ctl; + int i, j, conns = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; + + /* Count number of active inputs. */ + if (depth > 0) { + for (j = 0; j < w->nconns; j++) { + if (!w->connsenable[j]) + continue; + conns++; + } + } + + /* If this is not a first step - use input mixer. + Pins have common input ctl so care must be taken. */ + if (depth > 0 && (conns == 1 || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN, + index, 1); + if (ctl) + hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right); + } + + /* If widget has own ossdev - not traverse it. + It will be traversed on it's own. */ + if (w->ossdev >= 0 && depth > 0) + return; + + /* 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; + + /* + * If signals mixed, we can't assign controls farther. + * Ignore this on depth zero. Caller must knows why. + */ + if (conns > 1 && + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + w->selconn != index)) + return; + + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1); + if (ctl) + hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdaa_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) { + hdaa_audio_ctl_source_volume(pdevinfo, ossdev, + wc->nid, j, mute, left, right, depth + 1); + } + } + } + return; +} + +/* + * Trace signal from destination, setting volumes on the way. + */ +static void +hdaa_audio_ctl_dest_volume(struct hdaa_pcm_devinfo *pdevinfo, + int ossdev, nid_t nid, int index, int mute, int left, int right, int depth) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w, *wc; + struct hdaa_audio_ctl *ctl; + int i, j, consumers, cleft, cright; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; + + 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 = hdaa_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 = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_OUT, -1, 1); + if (ctl) + hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &left, &right); + } + + /* 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++) { + if (w->connsenable[i] == 0) + continue; + if (index >= 0 && i != index) + continue; + cleft = left; + cright = right; + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_IN, i, 1); + if (ctl) + hdaa_audio_ctl_dev_set(ctl, ossdev, mute, &cleft, &cright); + hdaa_audio_ctl_dest_volume(pdevinfo, ossdev, w->conns[i], -1, + mute, cleft, cright, depth + 1); + } +} + +/* + * Set volumes for the specified pdevinfo and ossdev. + */ +static void +hdaa_audio_ctl_dev_volume(struct hdaa_pcm_devinfo *pdevinfo, unsigned dev) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + uint32_t mute; + int lvol, rvol; + int i, j; + + mute = 0; + if (pdevinfo->left[dev] == 0) { + mute |= HDAA_AMP_MUTE_LEFT; + lvol = -4000; + } else + lvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) * + pdevinfo->left[dev] + 50) / 100 + pdevinfo->minamp[dev]; + if (pdevinfo->right[dev] == 0) { + mute |= HDAA_AMP_MUTE_RIGHT; + rvol = -4000; + } else + rvol = ((pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) * + pdevinfo->right[dev] + 50) / 100 + pdevinfo->minamp[dev]; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas < 0 && pdevinfo->index != 0) continue; - if ((pdevinfo->playas >= 0 && - ctl->widget->bindas == pdevinfo->playas) || - (pdevinfo->recas >= 0 && - ctl->widget->bindas == pdevinfo->recas) || - (ctl->widget->bindas == -2 && pdevinfo->index == 0)) - mask |= ctl->ossmask; - } - - /* Declare record sources available to this association. */ - if (pdevinfo->recas >= 0) { - for (i = 0; i < 16; i++) { - if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0) - continue; - w = hdaa_widget_get(devinfo, - devinfo->as[pdevinfo->recas].dacs[0][i]); - if (w == NULL || w->enable == 0) - continue; + if (w->bindas != pdevinfo->playas && + w->bindas != pdevinfo->recas) + continue; + if (dev == SOUND_MIXER_RECLEV && + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdaa_audio_ctl_dest_volume(pdevinfo, dev, + w->nid, -1, mute, lvol, rvol, 0); + continue; + } + if (dev == SOUND_MIXER_VOLUME && + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + devinfo->as[w->bindas].dir == HDAA_CTL_OUT) { + hdaa_audio_ctl_dest_volume(pdevinfo, dev, + w->nid, -1, mute, lvol, rvol, 0); + continue; + } + if (dev == SOUND_MIXER_IGAIN && + w->pflags & HDAA_ADC_MONITOR) { for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; + if (!w->connsenable[j]) + continue; cw = hdaa_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) + continue; + if (cw->bindas == -1) + continue; + if (cw->bindas >= 0 && + devinfo->as[cw->bindas].dir != HDAA_CTL_IN) continue; - if (cw->bindas != pdevinfo->recas && - cw->bindas != -2) - continue; - recmask |= cw->ossmask; - } - } - } - - /* Declare soft PCM volume if needed. */ - if (pdevinfo->playas >= 0) { - ctl = NULL; - if ((mask & SOUND_MASK_PCM) == 0 || - (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL)) { - softpcmvol = 1; - mask |= SOUND_MASK_PCM; - } else { - softpcmvol = 0; - i = 0; - while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->bindas != pdevinfo->playas && - (ctl->widget->bindas != -2 || pdevinfo->index != 0)) - continue; - if (!(ctl->ossmask & SOUND_MASK_PCM)) - continue; - if (ctl->step > 0) - break; + hdaa_audio_ctl_dest_volume(pdevinfo, dev, + w->nid, j, mute, lvol, rvol, 0); } + continue; } - - 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"); - ); - } - } - - /* Declare master volume if needed. */ - if (pdevinfo->playas >= 0) { - if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == - SOUND_MASK_PCM) { - 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"); - ); - } + if (w->ossdev != dev) + continue; + hdaa_audio_ctl_source_volume(pdevinfo, dev, + w->nid, -1, mute, lvol, rvol, 0); + if (dev == SOUND_MIXER_IMIX && (w->pflags & HDAA_IMIX_AS_DST)) + hdaa_audio_ctl_dest_volume(pdevinfo, dev, + w->nid, -1, mute, lvol, rvol, 0); } - - recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; - mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; - - mix_setrecdevs(m, recmask); - mix_setdevs(m, mask); - - hdaa_unlock(devinfo); - - return (0); } +/* + * OSS Mixer set method. + */ static int hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) @@ -1602,12 +2182,10 @@ hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdaa_devinfo *devinfo = pdevinfo->devinfo; struct hdaa_widget *w; - struct hdaa_audio_ctl *ctl; - uint32_t mute; - int lvol, rvol; - int i, j; + int i; hdaa_lock(devinfo); + /* Save new values. */ pdevinfo->left[dev] = left; pdevinfo->right[dev] = right; @@ -1648,39 +2226,57 @@ hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, } /* Recalculate all controls related to this OSS device. */ - i = 0; - while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || - !(ctl->ossmask & (1 << dev))) + hdaa_audio_ctl_dev_volume(pdevinfo, dev); + + hdaa_unlock(devinfo); + return (left | (right << 8)); +} + +/* + * Set mixer settings to our own default values: + * +20dB for mics, -10dB for analog vol, mute for igain, 0dB for others. + */ +static void +hdaa_audio_ctl_set_defaults(struct hdaa_pcm_devinfo *pdevinfo) +{ + int amp, vol, dev; + + for (dev = 0; dev < SOUND_MIXER_NRDEVICES; dev++) { + if ((pdevinfo->ossmask & (1 << dev)) == 0) continue; - if (!((pdevinfo->playas >= 0 && - ctl->widget->bindas == pdevinfo->playas) || - (pdevinfo->recas >= 0 && - ctl->widget->bindas == pdevinfo->recas) || - ctl->widget->bindas == -2)) + + /* If the value was overriden, leave it as is. */ + if (resource_int_value(device_get_name(pdevinfo->dev), + device_get_unit(pdevinfo->dev), ossnames[dev], &vol) == 0) 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; - } - } - mute = (lvol == 0) ? HDAA_AMP_MUTE_LEFT : 0; - mute |= (rvol == 0) ? HDAA_AMP_MUTE_RIGHT : 0; - lvol = (lvol * ctl->step + 50) / 100; - rvol = (rvol * ctl->step + 50) / 100; - hdaa_audio_ctl_amp_set(ctl, mute, lvol, rvol); + vol = -1; + if (dev == SOUND_MIXER_OGAIN) + vol = 100; + else if (dev == SOUND_MIXER_IGAIN) + vol = 0; + else if (dev == SOUND_MIXER_MIC || + dev == SOUND_MIXER_MONITOR) + amp = 20 * 4; /* +20dB */ + else if (dev == SOUND_MIXER_VOLUME && !pdevinfo->digital) + amp = -10 * 4; /* -10dB */ + else + amp = 0; + if (vol < 0 && + (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) <= 0) { + vol = 100; + } else if (vol < 0) { + vol = ((amp - pdevinfo->minamp[dev]) * 100 + + (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]) / 2) / + (pdevinfo->maxamp[dev] - pdevinfo->minamp[dev]); + vol = imin(imax(vol, 1), 100); + } + mix_set(pdevinfo->mixer, dev, vol, vol); } - hdaa_unlock(devinfo); - - return (left | (right << 8)); } /* - * Commutate specified record source. + * Recursively commutate specified record source. */ static uint32_t hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) @@ -1763,6 +2359,7 @@ hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) struct hdaa_devinfo *devinfo = pdevinfo->devinfo; struct hdaa_widget *w; struct hdaa_audio_as *as; + struct hdaa_audio_ctl *ctl; struct hdaa_chan *ch; int i, j; uint32_t ret = 0xffffffff; @@ -1791,9 +2388,47 @@ hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) ch->io[i], 0); } } + if (ret == 0xffffffff) + ret = 0; + + /* + * Some controls could be shared. Reset volumes for controls + * related to previously chosen devices, as they may no longer + * affect the signal. + */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || + !(ctl->ossmask & pdevinfo->recsrc)) + continue; + if (!((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + (pdevinfo->index == 0 && + ctl->widget->bindas == -2))) + continue; + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if (pdevinfo->recsrc & (1 << j)) { + ctl->devleft[j] = 0; + ctl->devright[j] = 0; + ctl->devmute[j] = 0; + } + } + } + + /* + * Some controls could be shared. Set volumes for controls + * related to devices selected both previously and now. + */ + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if ((ret | pdevinfo->recsrc) & (1 << j)) + hdaa_audio_ctl_dev_volume(pdevinfo, j); + } + pdevinfo->recsrc = ret; hdaa_unlock(devinfo); - return ((ret == 0xffffffff)? 0 : ret); + return (ret); } static kobj_method_t hdaa_audio_ctl_ossmixer_methods[] = { @@ -2167,7 +2802,6 @@ hdaa_audio_as_parse(struct hdaa_devinfo *devinfo) as[i].hpredir = -1; as[i].digital = 0; as[i].num_chans = 1; - as[i].unsol = -1; as[i].location = -1; } @@ -2224,12 +2858,11 @@ hdaa_audio_as_parse(struct hdaa_devinfo *devinfo) as[cnt].enable = 0; } if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + as[cnt].digital |= 0x1; + if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) + as[cnt].digital |= 0x2; if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap)) - as[cnt].digital = 3; - else if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) - as[cnt].digital = 2; - else - as[cnt].digital = 1; + as[cnt].digital |= 0x4; } if (as[cnt].location == -1) { as[cnt].location = @@ -3477,31 +4110,31 @@ hdaa_audio_disable_crossas(struct hdaa_devinfo *devinfo) } -#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) - /* - * Find controls to control amplification for source. + * Find controls to control amplification for source and calculate possible + * amplification range. */ static int hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, - int ossdev, int ctlable, int depth, int need) + int ossdev, int ctlable, int depth, int *minamp, int *maxamp) { struct hdaa_widget *w, *wc; struct hdaa_audio_ctl *ctl; - int i, j, conns = 0, rneed; + int i, j, conns = 0, tminamp, tmaxamp, cminamp, cmaxamp, found = 0; if (depth > HDA_PARSE_MAXDEPTH) - return (need); + return (found); w = hdaa_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) - return (need); + return (found); /* Count number of active inputs. */ if (depth > 0) { for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) - conns++; + if (!w->connsenable[j]) + continue; + conns++; } } @@ -3512,81 +4145,96 @@ hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_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); + ctl->ossmask |= (1 << ossdev); + found++; + if (*minamp == *maxamp) { + *minamp += MINQDB(ctl); + *maxamp += MAXQDB(ctl); + } } } /* If widget has own ossdev - not traverse it. It will be traversed on it's own. */ if (w->ossdev >= 0 && depth > 0) - return (need); + return (found); /* 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); + return (found); /* record that this widget exports such signal, */ w->ossmask |= (1 << ossdev); - /* If signals mixed, we can't assign controls farther. + /* + * 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) + if (conns > 1 && + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) ctlable = 0; if (ctlable) { ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_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); + ctl->ossmask |= (1 << ossdev); + found++; + if (*minamp == *maxamp) { + *minamp += MINQDB(ctl); + *maxamp += MAXQDB(ctl); + } } } - rneed = 0; + cminamp = cmaxamp = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { wc = hdaa_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 |= hdaa_audio_ctl_source_amp(devinfo, - wc->nid, j, ossdev, ctlable, depth + 1, need); + tminamp = tmaxamp = 0; + found += hdaa_audio_ctl_source_amp(devinfo, + wc->nid, j, ossdev, ctlable, depth + 1, + &tminamp, &tmaxamp); + if (cminamp == 0 && cmaxamp == 0) { + cminamp = tminamp; + cmaxamp = tmaxamp; + } else if (tminamp != tmaxamp) { + cminamp = imax(cminamp, tminamp); + cmaxamp = imin(cmaxamp, tmaxamp); + } } } } - rneed &= need; - - return (rneed); + if (*minamp == *maxamp && cminamp < cmaxamp) { + *minamp += cminamp; + *maxamp += cmaxamp; + } + return (found); } /* - * Find controls to control amplification for destination. + * Find controls to control amplification for destination and calculate + * possible amplification range. */ -static void +static int hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, - int ossdev, int depth, int need) + int ossdev, int depth, int *minamp, int *maxamp) { struct hdaa_audio_as *as = devinfo->as; struct hdaa_widget *w, *wc; struct hdaa_audio_ctl *ctl; - int i, j, consumers; + int i, j, consumers, tminamp, tmaxamp, cminamp, cmaxamp, found = 0; if (depth > HDA_PARSE_MAXDEPTH) - return; + return (found); w = hdaa_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) - return; + return (found); if (depth > 0) { /* If this node produce output for several consumers, @@ -3609,43 +4257,58 @@ hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || (w->bindseqmask & (1 << 15)) == 0)) || consumers > 2) - return; + return (found); /* Else use it's output mixer. */ ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_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); + ctl->ossmask |= (1 << ossdev); + found++; + if (*minamp == *maxamp) { + *minamp += MINQDB(ctl); + *maxamp += MAXQDB(ctl); + } } } /* We must not traverse pin */ if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && depth > 0) - return; + return (found); + cminamp = cmaxamp = 0; for (i = 0; i < w->nconns; i++) { - int tneed = need; if (w->connsenable[i] == 0) continue; if (index >= 0 && i != index) continue; + tminamp = tmaxamp = 0; ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_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); + ctl->ossmask |= (1 << ossdev); + found++; + if (*minamp == *maxamp) { + tminamp += MINQDB(ctl); + tmaxamp += MAXQDB(ctl); + } } - hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, - depth + 1, tneed); + found += hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, + depth + 1, &tminamp, &tmaxamp); + if (cminamp == 0 && cmaxamp == 0) { + cminamp = tminamp; + cmaxamp = tmaxamp; + } else if (tminamp != tmaxamp) { + cminamp = imax(cminamp, tminamp); + cmaxamp = imin(cmaxamp, tmaxamp); + } + } + if (*minamp == *maxamp && cminamp < cmaxamp) { + *minamp += cminamp; + *maxamp += cmaxamp; } + return (found); } /* @@ -3853,43 +4516,82 @@ retry: hdaa_audio_trace_as_extra(devinfo); } +/* + * Store in pdevinfo new data about whether and how we can control signal + * for OSS device to/from specified widget. + */ +static void +hdaa_adjust_amp(struct hdaa_widget *w, int ossdev, + int found, int minamp, int maxamp) +{ + struct hdaa_devinfo *devinfo = w->devinfo; + struct hdaa_pcm_devinfo *pdevinfo; + + if (w->bindas >= 0) + pdevinfo = devinfo->as[w->bindas].pdevinfo; + else + pdevinfo = &devinfo->devs[0]; + if (found) + pdevinfo->ossmask |= (1 << ossdev); + if (minamp == 0 && maxamp == 0) + return; + if (pdevinfo->minamp[ossdev] == 0 && pdevinfo->maxamp[ossdev] == 0) { + pdevinfo->minamp[ossdev] = minamp; + pdevinfo->maxamp[ossdev] = maxamp; + } else { + pdevinfo->minamp[ossdev] = imax(pdevinfo->minamp[ossdev], minamp); + pdevinfo->maxamp[ossdev] = imin(pdevinfo->maxamp[ossdev], maxamp); + } +} + +/* + * Trace signals from/to all possible sources/destionstions to find possible + * recording sources, OSS device control ranges and to assign controls. + */ static void hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo) { struct hdaa_audio_as *as = devinfo->as; - struct hdaa_audio_ctl *ctl; struct hdaa_widget *w, *cw; - int i, j; + int i, j, minamp, maxamp, found; /* Assign mixers to the tree. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdaa_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; + minamp = maxamp = 0; 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 == HDAA_CTL_IN)) { if (w->ossdev < 0) continue; - hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, - w->ossdev, 1, 0, 1); + found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, &minamp, &maxamp); + hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { - hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, - SOUND_MIXER_RECLEV, 0, 1); + found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + SOUND_MIXER_RECLEV, 0, &minamp, &maxamp); + hdaa_adjust_amp(w, SOUND_MIXER_RECLEV, found, minamp, maxamp); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && as[w->bindas].dir == HDAA_CTL_OUT) { - hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, - SOUND_MIXER_VOLUME, 0, 1); + found = hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + SOUND_MIXER_VOLUME, 0, &minamp, &maxamp); + hdaa_adjust_amp(w, SOUND_MIXER_VOLUME, found, minamp, maxamp); } if (w->ossdev == SOUND_MIXER_IMIX) { - if (hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, - w->ossdev, 1, 0, 1)) { + minamp = maxamp = 0; + found = hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, &minamp, &maxamp); + if (minamp == maxamp) { /* If we are unable to control input monitor as source - try to control it as destination. */ - hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, - w->ossdev, 0, 1); + found += hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + w->ossdev, 0, &minamp, &maxamp); + w->pflags |= HDAA_IMIX_AS_DST; } + hdaa_adjust_amp(w, w->ossdev, found, minamp, maxamp); } if (w->pflags & HDAA_ADC_MONITOR) { for (j = 0; j < w->nconns; j++) { @@ -3903,17 +4605,15 @@ hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo) if (cw->bindas >= 0 && as[cw->bindas].dir != HDAA_CTL_IN) continue; - hdaa_audio_ctl_dest_amp(devinfo, - w->nid, j, SOUND_MIXER_IGAIN, 0, 1); + minamp = maxamp = 0; + found = hdaa_audio_ctl_dest_amp(devinfo, + w->nid, j, SOUND_MIXER_IGAIN, 0, + &minamp, &maxamp); + hdaa_adjust_amp(w, SOUND_MIXER_IGAIN, + found, minamp, maxamp); } } } - /* Treat unrequired as possible. */ - i = 0; - while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->ossmask == 0) - ctl->ossmask = ctl->possmask; - } } static void @@ -4181,6 +4881,7 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) ch->bit32 = 0; ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; + ch->stripecap = 0xff; ret = 0; channels = 0; @@ -4223,6 +4924,7 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) pcmcap &= w->param.supp_pcm_size_rate; } ch->io[ret++] = as[ch->as].dacs[ch->asindex][i]; + ch->stripecap &= w->wclass.conv.stripecap; /* Do not count redirection pin/dac channels. */ if (i == 15 && as[ch->as].hpredir >= 0) continue; @@ -4259,12 +4961,12 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) 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)) + 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; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) + ch->bit32 = 4; if (!(devinfo->quirks & HDAA_QUIRK_FORCESTEREO)) { ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0); if (ch->bit32) @@ -4275,21 +4977,51 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0); } - if (channels == 4 || /* Any 4-channel */ - pinset == 0x0007 || /* 5.1 */ - pinset == 0x0013 || /* 5.1 */ - pinset == 0x0017) { /* 7.1 */ + if (channels >= 3 && !onlystereo) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 3, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 3, 0); + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 3, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 3, 1); + } + if (channels >= 4) { ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0); + if (!onlystereo) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 1); + } + } + if (channels >= 5 && !onlystereo) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 5, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 5, 0); + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 5, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 5, 1); } - if (channels == 6 || /* Any 6-channel */ - pinset == 0x0017) { /* 7.1 */ + if (channels >= 6) { ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1); + if (!onlystereo) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 0); + } + } + if (channels >= 7 && !onlystereo) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 7, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 7, 0); + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 7, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 7, 1); } - if (channels == 8) { /* Any 8-channel */ + if (channels >= 8) { ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1); @@ -4297,6 +5029,10 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) } if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); + if (channels >= 8) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 8, 0); + ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 8, 1); + } } ch->fmtlist[i] = 0; i = 0; @@ -4334,7 +5070,7 @@ hdaa_pcmchannel_setup(struct hdaa_chan *ch) } static void -hdaa_create_pcms(struct hdaa_devinfo *devinfo) +hdaa_prepare_pcms(struct hdaa_devinfo *devinfo) { struct hdaa_audio_as *as = devinfo->as; int i, j, k, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; @@ -4389,6 +5125,7 @@ hdaa_create_pcms(struct hdaa_devinfo *devinfo) continue; devinfo->devs[j].playas = i; } + as[i].pdevinfo = &devinfo->devs[j]; for (k = 0; k < as[i].num_chans; k++) { devinfo->chans[as[i].chans[k]].pdevinfo = &devinfo->devs[j]; @@ -4397,6 +5134,13 @@ hdaa_create_pcms(struct hdaa_devinfo *devinfo) break; } } +} + +static void +hdaa_create_pcms(struct hdaa_devinfo *devinfo) +{ + int i; + for (i = 0; i < devinfo->num_devs; i++) { struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i]; @@ -4445,9 +5189,15 @@ hdaa_dump_ctls(struct hdaa_pcm_devinfo *pdevinfo, const char *banner, uint32_t f } else { device_printf(pdevinfo->dev, "Unknown Ctl"); } - printf(" (OSS: %s)\n", + printf(" (OSS: %s)", hdaa_audio_ctl_ossmixer_mask2allname(1 << j, buf, sizeof(buf))); + if (pdevinfo->ossmask & (1 << j)) { + printf(": %+d/%+ddB\n", + pdevinfo->minamp[j] / 4, + pdevinfo->maxamp[j] / 4); + } else + printf("\n"); device_printf(pdevinfo->dev, " |\n"); printed = 1; } @@ -4460,8 +5210,8 @@ hdaa_dump_ctls(struct hdaa_pcm_devinfo *pdevinfo, const char *banner, uint32_t f 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, + MINQDB(ctl) / 4, + MAXQDB(ctl) / 4, ctl->step + 1, ctl->mute?" + mute":""); } else @@ -4582,8 +5332,18 @@ hdaa_dump_pin(struct hdaa_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"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + if ((w->wclass.pin.ctrl & + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) == 0x03) + printf(" HBR"); + else if ((w->wclass.pin.ctrl & + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0) + printf(" EPTs"); + } else { + if ((w->wclass.pin.ctrl & + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) != 0) + printf(" VREFs"); + } printf("\n"); } @@ -4638,7 +5398,6 @@ hdaa_dump_amp(device_t dev, uint32_t cap, char *banner) static void hdaa_dump_nodes(struct hdaa_devinfo *devinfo) { - static char *ossname[] = SOUND_DEVICE_NAMES; struct hdaa_widget *w, *cw; char buf[64]; int i, j; @@ -4678,7 +5437,8 @@ hdaa_dump_nodes(struct hdaa_devinfo *devinfo) if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) printf(" PROC"); if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) - printf(" STRIPE"); + printf(" STRIPE(x%d)", + 1 << (fls(w->wclass.conv.stripecap) - 1)); j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); if (j == 1) printf(" STEREO"); @@ -4694,7 +5454,7 @@ hdaa_dump_nodes(struct hdaa_devinfo *devinfo) device_printf(devinfo->dev, " OSS: %s", hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); if (w->ossdev >= 0) - printf(" (%s)", ossname[w->ossdev]); + printf(" (%s)", ossnames[w->ossdev]); printf("\n"); } if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || @@ -4852,15 +5612,14 @@ hdaa_dump_mix(struct hdaa_pcm_devinfo *pdevinfo) int i; int printed = 0; - if (pdevinfo->index != 0) - return; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdaa_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->ossdev != SOUND_MIXER_IMIX) continue; + if (w->bindas != pdevinfo->recas) + continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); @@ -4954,9 +5713,12 @@ hdaa_pindump(device_t dev) res = hda_command(dev, HDA_CMD_GET_PIN_SENSE(0, w->nid)); } - printf(" Sense: 0x%08x (%sconnected)", res, + printf(" Sense: 0x%08x (%sconnected%s)", res, (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ? - "" : "dis"); + "" : "dis", + (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap) && + (res & HDA_CMD_GET_PIN_SENSE_ELD_VALID)) ? + ", ELD valid" : ""); if (delay > 0) printf(" delay %dus", delay * 10); } @@ -5045,6 +5807,10 @@ hdaa_configure(device_t dev) device_printf(dev, "Assigning names to signal sources...\n"); ); hdaa_audio_assign_names(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Preparing PCM devices...\n"); + ); + hdaa_prepare_pcms(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "Assigning mixers to the tree...\n"); ); @@ -5062,9 +5828,9 @@ hdaa_configure(device_t dev) ); hdaa_patch_direct(devinfo); HDA_BOOTHVERBOSE( - device_printf(dev, "HP switch init...\n"); + device_printf(dev, "Pin sense init...\n"); ); - hdaa_hp_switch_init(devinfo); + hdaa_sense_init(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "Creating PCM devices...\n"); ); @@ -5129,9 +5895,9 @@ hdaa_unconfigure(device_t dev) int i, j; HDA_BOOTHVERBOSE( - device_printf(dev, "HP switch deinit...\n"); + device_printf(dev, "Pin sense deinit...\n"); ); - hdaa_hp_switch_deinit(devinfo); + hdaa_sense_deinit(devinfo); free(devinfo->ctl, M_HDAA); devinfo->ctl = NULL; devinfo->ctlcnt = 0; @@ -5157,7 +5923,13 @@ hdaa_unconfigure(device_t dev) w->ossmask = 0; for (j = 0; j < w->nconns; j++) w->connsenable[j] = 1; - w->wclass.pin.config = w->wclass.pin.newconf; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + w->wclass.pin.config = w->wclass.pin.newconf; + if (w->eld != NULL) { + w->eld_len = 0; + free(w->eld, M_HDAA); + w->eld = NULL; + } } } @@ -5354,10 +6126,6 @@ hdaa_suspend(device_t dev) hdaa_channel_stop(&devinfo->chans[i]); } } - HDA_BOOTHVERBOSE( - device_printf(dev, "HP switch deinit...\n"); - ); - hdaa_hp_switch_deinit(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "Power down FG" " nid=%d to the D3 state...\n", @@ -5399,9 +6167,9 @@ hdaa_resume(device_t dev) ); hdaa_patch_direct(devinfo); HDA_BOOTHVERBOSE( - device_printf(dev, "HP switch init...\n"); + device_printf(dev, "Pin sense init...\n"); ); - hdaa_hp_switch_init(devinfo); + hdaa_sense_init(devinfo); hdaa_unlock(devinfo); for (i = 0; i < devinfo->num_devs; i++) { @@ -5654,12 +6422,29 @@ static void hdaa_unsol_intr(device_t dev, uint32_t resp) { struct hdaa_devinfo *devinfo = device_get_softc(dev); - int i, tag; + struct hdaa_widget *w; + int i, tag, flags; + HDA_BOOTHVERBOSE( + device_printf(dev, "Unsolicited response %08x\n", resp); + ); tag = resp >> 26; - for (i = 0; i < devinfo->ascnt; i++) { - if (devinfo->as[i].unsol == tag) - hdaa_hp_switch_handler(devinfo, i); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->unsol != tag) + continue; + if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) || + HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) + flags = resp & 0x03; + else + flags = 0x01; + if (flags & 0x01) + hdaa_presence_handler(w); + if (flags & 0x02) + hdaa_eld_handler(w); } } @@ -5700,9 +6485,12 @@ hdaa_chan_formula(struct hdaa_devinfo *devinfo, int asid, c = devinfo->chans[as->chans[0]].channels; if (c == 1) snprintf(buf, buflen, "mono"); - else if (c == 2) - buf[0] = 0; - else if (as->pinset == 0x0003) + else if (c == 2) { + if (as->hpredir < 0) + buf[0] = 0; + else + snprintf(buf, buflen, "2.0"); + } else if (as->pinset == 0x0003) snprintf(buf, buflen, "3.1"); else if (as->pinset == 0x0005 || as->pinset == 0x0011) snprintf(buf, buflen, "4.0"); @@ -5712,6 +6500,62 @@ hdaa_chan_formula(struct hdaa_devinfo *devinfo, int asid, snprintf(buf, buflen, "7.1"); else snprintf(buf, buflen, "%dch", c); + if (as->hpredir >= 0) + strlcat(buf, "+HP", buflen); +} + +static int +hdaa_chan_type(struct hdaa_devinfo *devinfo, int asid) +{ + struct hdaa_audio_as *as; + struct hdaa_widget *w; + int i, t = -1, t1; + + as = &devinfo->as[asid]; + for (i = 0; i < 16; i++) { + w = hdaa_widget_get(devinfo, as->pins[i]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + t1 = HDA_CONFIG_DEFAULTCONF_DEVICE(w->wclass.pin.config); + if (t == -1) + t = t1; + else if (t != t1) { + t = -2; + break; + } + } + return (t); +} + +static int +hdaa_sysctl_32bit(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_audio_as *as = (struct hdaa_audio_as *)oidp->oid_arg1; + struct hdaa_pcm_devinfo *pdevinfo = as->pdevinfo; + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_chan *ch; + int error, val, i; + uint32_t pcmcap; + + ch = &devinfo->chans[as->chans[0]]; + val = (ch->bit32 == 4) ? 32 : ((ch->bit32 == 3) ? 24 : + ((ch->bit32 == 2) ? 20 : 0)); + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + pcmcap = ch->supp_pcm_size_rate; + if (val == 32 && HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) + ch->bit32 = 4; + else if (val == 24 && HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) + ch->bit32 = 3; + else if (val == 20 && HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) + ch->bit32 = 2; + else + return (EINVAL); + for (i = 1; i < as->num_chans; i++) + devinfo->chans[as->chans[i]].bit32 = ch->bit32; + return (0); } static int @@ -5722,7 +6566,7 @@ hdaa_pcm_probe(device_t dev) struct hdaa_devinfo *devinfo = pdevinfo->devinfo; char chans1[8], chans2[8]; char buf[128]; - int loc1, loc2; + int loc1, loc2, t1, t2; if (pdevinfo->playas >= 0) loc1 = devinfo->as[pdevinfo->playas].location; @@ -5738,12 +6582,17 @@ hdaa_pcm_probe(device_t dev) loc1 = -2; chans1[0] = 0; chans2[0] = 0; - if (pdevinfo->playas >= 0) + t1 = t2 = -1; + if (pdevinfo->playas >= 0) { hdaa_chan_formula(devinfo, pdevinfo->playas, chans1, sizeof(chans1)); - if (pdevinfo->recas >= 0) + t1 = hdaa_chan_type(devinfo, pdevinfo->playas); + } + if (pdevinfo->recas >= 0) { hdaa_chan_formula(devinfo, pdevinfo->recas, chans2, sizeof(chans2)); + t2 = hdaa_chan_type(devinfo, pdevinfo->recas); + } if (chans1[0] != 0 || chans2[0] != 0) { if (chans1[0] == 0 && pdevinfo->playas >= 0) snprintf(chans1, sizeof(chans1), "2.0"); @@ -5752,14 +6601,24 @@ hdaa_pcm_probe(device_t dev) if (strcmp(chans1, chans2) == 0) chans2[0] = 0; } - snprintf(buf, sizeof(buf), "%s PCM (%s%s%s%s%s%s%s)", + if (t1 == -1) + t1 = t2; + else if (t2 == -1) + t2 = t1; + if (t1 != t2) + t1 = -2; + if (pdevinfo->digital) + t1 = -2; + snprintf(buf, sizeof(buf), "%s PCM (%s%s%s%s%s%s%s%s%s)", device_get_desc(device_get_parent(device_get_parent(dev))), loc1 >= 0 ? HDA_LOCS[loc1] : "", loc1 >= 0 ? " " : "", - (pdevinfo->digital == 3)?"DisplayPort": - ((pdevinfo->digital == 2)?"HDMI": - ((pdevinfo->digital)?"Digital":"Analog")), + (pdevinfo->digital == 0x7)?"HDMI/DP": + ((pdevinfo->digital == 0x5)?"DisplayPort": + ((pdevinfo->digital == 0x3)?"HDMI": + ((pdevinfo->digital)?"Digital":"Analog"))), chans1[0] ? " " : "", chans1, - chans2[0] ? "/" : "", chans2); + chans2[0] ? "/" : "", chans2, + t1 >= 0 ? " " : "", t1 >= 0 ? HDA_DEVS[t1] : ""); device_set_desc_copy(dev, buf); return (BUS_PROBE_SPECIFIC); } @@ -5771,6 +6630,7 @@ hdaa_pcm_attach(device_t dev) (struct hdaa_pcm_devinfo *)device_get_ivars(dev); struct hdaa_devinfo *devinfo = pdevinfo->devinfo; struct hdaa_audio_as *as; + struct snddev_info *d; char status[SND_STATUSLEN]; int i; @@ -5845,17 +6705,46 @@ hdaa_pcm_attach(device_t dev) pdevinfo->registered++; + d = device_get_softc(dev); if (pdevinfo->playas >= 0) { as = &devinfo->as[pdevinfo->playas]; for (i = 0; i < as->num_chans; i++) pcm_addchan(dev, PCMDIR_PLAY, &hdaa_channel_class, &devinfo->chans[as->chans[i]]); + SYSCTL_ADD_PROC(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, + "32bit", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + as, sizeof(as), hdaa_sysctl_32bit, "I", + "Resolution of 32bit samples (20/24/32bit)"); } if (pdevinfo->recas >= 0) { as = &devinfo->as[pdevinfo->recas]; for (i = 0; i < as->num_chans; i++) pcm_addchan(dev, PCMDIR_REC, &hdaa_channel_class, &devinfo->chans[as->chans[i]]); + SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, + "32bit", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + as, sizeof(as), hdaa_sysctl_32bit, "I", + "Resolution of 32bit samples (20/24/32bit)"); + pdevinfo->autorecsrc = 2; + resource_int_value(device_get_name(dev), device_get_unit(dev), + "rec.autosrc", &pdevinfo->autorecsrc); + SYSCTL_ADD_INT(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, + "autosrc", CTLTYPE_INT | CTLFLAG_RW, + &pdevinfo->autorecsrc, 0, + "Automatic recording source selection"); + } + + if (pdevinfo->mixer != NULL) { + hdaa_audio_ctl_set_defaults(pdevinfo); + if (pdevinfo->recas >= 0) { + as = &devinfo->as[pdevinfo->recas]; + hdaa_lock(devinfo); + hdaa_autorecsrc_handler(as, NULL); + hdaa_unlock(devinfo); + } } snprintf(status, SND_STATUSLEN, "on %s %s", diff --git a/sys/dev/sound/pci/hda/hdaa.h b/sys/dev/sound/pci/hda/hdaa.h index b56b6d754..06ee87ede 100644 --- a/sys/dev/sound/pci/hda/hdaa.h +++ b/sys/dev/sound/pci/hda/hdaa.h @@ -74,7 +74,10 @@ #define HDAA_AMP_LEFT_MUTED(v) ((v) & (HDAA_AMP_MUTE_LEFT)) #define HDAA_AMP_RIGHT_MUTED(v) (((v) & HDAA_AMP_MUTE_RIGHT) >> 1) +/* Widget in playback receiving signal from recording. */ #define HDAA_ADC_MONITOR (1 << 0) +/* Input mixer widget needs volume control as destination. */ +#define HDAA_IMIX_AS_DST (2 << 0) #define HDAA_CTL_OUT 1 #define HDAA_CTL_IN 2 @@ -82,6 +85,13 @@ #define HDA_MAX_CONNS 32 #define HDA_MAX_NAMELEN 32 +struct hdaa_audio_as; +struct hdaa_audio_ctl; +struct hdaa_chan; +struct hdaa_devinfo; +struct hdaa_pcm_devinfo; +struct hdaa_widget; + struct hdaa_widget { nid_t nid; int type; @@ -93,9 +103,12 @@ struct hdaa_widget { int bindseqmask; int ossdev; uint32_t ossmask; + int unsol; nid_t conns[HDA_MAX_CONNS]; u_char connsenable[HDA_MAX_CONNS]; char name[HDA_MAX_NAMELEN]; + uint8_t *eld; + int eld_len; struct hdaa_devinfo *devinfo; struct { uint32_t widget_cap; @@ -112,7 +125,11 @@ struct hdaa_widget { uint32_t newconf; uint32_t cap; uint32_t ctrl; + int connected; } pin; + struct { + uint8_t stripecap; + } conv; } wclass; }; @@ -123,7 +140,10 @@ struct hdaa_audio_ctl { int mute, step, size, offset; int left, right, forcemute; uint32_t muted; - uint32_t ossmask, possmask; + uint32_t ossmask; /* OSS devices that may affect control. */ + int devleft[SOUND_MIXER_NRDEVICES]; /* Left ampl in 1/4dB. */ + int devright[SOUND_MIXER_NRDEVICES]; /* Right ampl in 1/4dB. */ + int devmute[SOUND_MIXER_NRDEVICES]; /* Mutes per OSS device. */ }; /* Association is a group of pins bound for some special function. */ @@ -140,22 +160,28 @@ struct hdaa_audio_as { nid_t dacs[2][16]; int num_chans; int chans[2]; - int unsol; int location; /* Pins location, if all have the same */ int mixed; /* Mixed/multiplexed recording, not multichannel. */ + struct hdaa_pcm_devinfo *pdevinfo; }; struct hdaa_pcm_devinfo { device_t dev; struct hdaa_devinfo *devinfo; + struct snd_mixer *mixer; int index; int registered; int playas, recas; u_char left[SOUND_MIXER_NRDEVICES]; u_char right[SOUND_MIXER_NRDEVICES]; + int minamp[SOUND_MIXER_NRDEVICES]; /* Minimal amps in 1/4dB. */ + int maxamp[SOUND_MIXER_NRDEVICES]; /* Maximal amps in 1/4dB. */ int chan_size; int chan_blkcnt; u_char digital; + uint32_t ossmask; /* Mask of supported OSS devices. */ + uint32_t recsrc; /* Mask of supported OSS sources. */ + int autorecsrc; }; struct hdaa_devinfo { @@ -197,9 +223,9 @@ struct hdaa_chan { struct pcmchan_caps caps; struct hdaa_devinfo *devinfo; struct hdaa_pcm_devinfo *pdevinfo; - uint32_t spd, fmt, fmtlist[16], pcmrates[16]; + uint32_t spd, fmt, fmtlist[32], pcmrates[16]; uint32_t supp_stream_formats, supp_pcm_size_rate; - uint32_t ptr, prevptr, blkcnt, blksz; + uint32_t blkcnt, blksz; uint32_t *dmapos; uint32_t flags; int dir; @@ -210,15 +236,33 @@ struct hdaa_chan { int as; /* Number of association. */ int asindex; /* Index within association. */ nid_t io[16]; + uint8_t stripecap; /* AND of stripecap of all ios. */ + uint8_t stripectl; /* stripe to use to all ios. */ }; +#define MINQDB(ctl) \ + ((0 - (ctl)->offset) * ((ctl)->size + 1)) + +#define MAXQDB(ctl) \ + (((ctl)->step - (ctl)->offset) * ((ctl)->size + 1)) + +#define RANGEQDB(ctl) \ + ((ctl)->step * ((ctl)->size + 1)) + +#define VAL2QDB(ctl, val) \ + (((ctl)->size + 1) * ((int)(val) - (ctl)->offset)) + +#define QDB2VAL(ctl, qdb) \ + imax(imin((((qdb) + (ctl)->size / 2 * ((qdb) > 0 ? 1 : -1)) / \ + ((ctl)->size + 1) + (ctl)->offset), (ctl)->step), 0) + #define hdaa_codec_id(devinfo) \ (((uint32_t)hda_get_vendor_id(devinfo->dev) << 16) + \ hda_get_device_id(devinfo->dev)) #define hdaa_subvendor_id(devinfo) \ - (((uint32_t)hda_get_subvendor_id(devinfo->dev) << 16) + \ - hda_get_subdevice_id(devinfo->dev)) + (((uint32_t)hda_get_subdevice_id(devinfo->dev) << 16) + \ + hda_get_subvendor_id(devinfo->dev)) struct hdaa_widget *hdaa_widget_get(struct hdaa_devinfo *, nid_t); uint32_t hdaa_widget_pin_patch(uint32_t config, const char *str); diff --git a/sys/dev/sound/pci/hda/hdaa_patches.c b/sys/dev/sound/pci/hda/hdaa_patches.c index b20c3390b..cd03913ca 100644 --- a/sys/dev/sound/pci/hda/hdaa_patches.c +++ b/sys/dev/sound/pci/hda/hdaa_patches.c @@ -620,9 +620,9 @@ hdaa_patch_direct(struct hdaa_devinfo *devinfo) hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, 0x7e7, 0)); if (id == HDA_CODEC_ALC269) { - if (subid == 0x104316e3 || subid == 0x1043831a || - subid == 0x1043834a || subid == 0x10438398 || - subid == 0x104383ce) { + if (subid == 0x16e31043 || subid == 0x831a1043 || + subid == 0x834a1043 || subid == 0x83981043 || + subid == 0x83ce1043) { /* * The ditital mics on some Asus laptops produce * differential signals instead of expected stereo. diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index 0f11b29f7..90f31813c 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -46,7 +46,7 @@ #include #include -#define HDA_DRV_TEST_REV "20120111_0001" +#define HDA_DRV_TEST_REV "20120126_0002" SND_DECLARE_FILE("$FreeBSD$"); @@ -70,17 +70,6 @@ static const struct { #define HDAC_QUIRKS_TAB_LEN \ (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) -#define HDA_BDL_MIN 2 -#define HDA_BDL_MAX 256 -#define HDA_BDL_DEFAULT HDA_BDL_MIN - -#define HDA_BLK_MIN HDA_DMA_ALIGNMENT -#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) - -#define HDA_BUFSZ_MIN 4096 -#define HDA_BUFSZ_MAX 65536 -#define HDA_BUFSZ_DEFAULT 16384 - MALLOC_DEFINE(M_HDAC, "hdac", "HDA Controller"); static const struct { @@ -124,6 +113,17 @@ static const struct { { HDA_NVIDIA_MCP89_2, "NVIDIA MCP89", 0, 0 }, { HDA_NVIDIA_MCP89_3, "NVIDIA MCP89", 0, 0 }, { HDA_NVIDIA_MCP89_4, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_0BE2, "NVIDIA (0x0be2)", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_0BE3, "NVIDIA (0x0be3)", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_0BE4, "NVIDIA (0x0be4)", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GT100, "NVIDIA GT100", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GT104, "NVIDIA GT104", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GT106, "NVIDIA GT106", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GT108, "NVIDIA GT108", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GT116, "NVIDIA GT116", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GF119, "NVIDIA GF119", 0, 0 }, + { HDA_NVIDIA_GF110_1, "NVIDIA GF110", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_GF110_2, "NVIDIA GF110", 0, HDAC_QUIRK_MSI }, { HDA_ATI_SB450, "ATI SB450", 0, 0 }, { HDA_ATI_SB600, "ATI SB600", 0, 0 }, { HDA_ATI_RS600, "ATI RS600", 0, 0 }, @@ -143,12 +143,12 @@ static const struct { { HDA_SIS_966, "SiS 966", 0, 0 }, { HDA_ULI_M5461, "ULI M5461", 0, 0 }, /* Unknown */ - { HDA_INTEL_ALL, "Intel (Unknown)", 0, 0 }, - { HDA_NVIDIA_ALL, "NVIDIA (Unknown)", 0, 0 }, - { HDA_ATI_ALL, "ATI (Unknown)", 0, 0 }, - { HDA_VIA_ALL, "VIA (Unknown)", 0, 0 }, - { HDA_SIS_ALL, "SiS (Unknown)", 0, 0 }, - { HDA_ULI_ALL, "ULI (Unknown)", 0, 0 }, + { HDA_INTEL_ALL, "Intel", 0, 0 }, + { HDA_NVIDIA_ALL, "NVIDIA", 0, 0 }, + { HDA_ATI_ALL, "ATI", 0, 0 }, + { HDA_VIA_ALL, "VIA", 0, 0 }, + { HDA_SIS_ALL, "SiS", 0, 0 }, + { HDA_ULI_ALL, "ULI", 0, 0 }, }; #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) @@ -1006,26 +1006,27 @@ hdac_probe(device_t dev) result = ENXIO; for (i = 0; i < HDAC_DEVICES_LEN; i++) { if (hdac_devices[i].model == model) { - strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); - result = BUS_PROBE_DEFAULT; + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = BUS_PROBE_DEFAULT; break; } if (HDA_DEV_MATCH(hdac_devices[i].model, model) && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { - strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); - result = BUS_PROBE_GENERIC; + snprintf(desc, sizeof(desc), + "%s (0x%04x)", + hdac_devices[i].desc, pci_get_device(dev)); + result = BUS_PROBE_GENERIC; break; } } if (result == ENXIO && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { - strlcpy(desc, "Generic", sizeof(desc)); - result = BUS_PROBE_GENERIC; + snprintf(desc, sizeof(desc), "Generic (0x%08x)", model); + result = BUS_PROBE_GENERIC; } if (result != ENXIO) { - strlcat(desc, " HDA Controller", - sizeof(desc)); + strlcat(desc, " HDA Controller", sizeof(desc)); device_set_desc_copy(dev, desc); } @@ -1328,10 +1329,10 @@ sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) } static int -hdac_data_rate(uint16_t fmt) +hdac_mdata_rate(uint16_t fmt) { - static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; - int rate; + static const int mbits[8] = { 8, 16, 32, 32, 32, 32, 32, 32 }; + int rate, bits; if (fmt & (1 << 14)) rate = 44100; @@ -1339,8 +1340,24 @@ hdac_data_rate(uint16_t fmt) rate = 48000; rate *= ((fmt >> 11) & 0x07) + 1; rate /= ((fmt >> 8) & 0x07) + 1; - rate *= ((bits[(fmt >> 4) & 0x03]) * ((fmt & 0x0f) + 1) + 7) / 8; - return (rate); + bits = mbits[(fmt >> 4) & 0x03]; + bits *= (fmt & 0x0f) + 1; + return (rate * bits); +} + +static int +hdac_bdata_rate(uint16_t fmt, int output) +{ + static const int bbits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; + int rate, bits; + + rate = 48000; + rate *= ((fmt >> 11) & 0x07) + 1; + bits = bbits[(fmt >> 4) & 0x03]; + bits *= (fmt & 0x0f) + 1; + if (!output) + bits = ((bits + 7) & ~0x07) + 10; + return (rate * bits); } static void @@ -1358,7 +1375,7 @@ hdac_poll_reinit(struct hdac_softc *sc) if (s->running == 0) continue; pollticks = ((uint64_t)hz * s->blksz) / - hdac_data_rate(s->format); + (hdac_mdata_rate(s->format) / 8); pollticks >>= 1; if (pollticks > hz) pollticks = hz; @@ -1779,11 +1796,12 @@ hdac_find_stream(struct hdac_softc *sc, int dir, int stream) } static int -hdac_stream_alloc(device_t dev, device_t child, int dir, int format, +hdac_stream_alloc(device_t dev, device_t child, int dir, int format, int stripe, uint32_t **dmapos) { struct hdac_softc *sc = device_get_softc(dev); - int stream, ss; + nid_t cad = (uintptr_t)device_get_ivars(child); + int stream, ss, bw, maxbw, prevbw; /* Look for empty stream. */ ss = hdac_find_stream(sc, dir, 0); @@ -1792,6 +1810,28 @@ hdac_stream_alloc(device_t dev, device_t child, int dir, int format, if (ss < 0) return (0); + /* Check bus bandwidth. */ + bw = hdac_bdata_rate(format, dir); + if (dir == 1) { + bw *= 1 << (sc->num_sdo - stripe); + prevbw = sc->sdo_bw_used; + maxbw = 48000 * 960 * (1 << sc->num_sdo); + } else { + prevbw = sc->codecs[cad].sdi_bw_used; + maxbw = 48000 * 464; + } + HDA_BOOTHVERBOSE( + device_printf(dev, "%dKbps of %dKbps bandwidth used%s\n", + (bw + prevbw) / 1000, maxbw / 1000, + bw + prevbw > maxbw ? " -- OVERFLOW!" : ""); + ); + if (bw + prevbw > maxbw) + return (0); + if (dir == 1) + sc->sdo_bw_used += bw; + else + sc->codecs[cad].sdi_bw_used += bw; + /* Allocate stream number */ if (ss >= sc->num_iss + sc->num_oss) stream = 15 - (ss - sc->num_iss + sc->num_oss); @@ -1803,7 +1843,9 @@ hdac_stream_alloc(device_t dev, device_t child, int dir, int format, sc->streams[ss].dev = child; sc->streams[ss].dir = dir; sc->streams[ss].stream = stream; + sc->streams[ss].bw = bw; sc->streams[ss].format = format; + sc->streams[ss].stripe = stripe; if (dmapos != NULL) { if (sc->pos_dma.dma_vaddr != NULL) *dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + ss * 8); @@ -1817,11 +1859,16 @@ static void hdac_stream_free(device_t dev, device_t child, int dir, int stream) { struct hdac_softc *sc = device_get_softc(dev); + nid_t cad = (uintptr_t)device_get_ivars(child); int ss; ss = hdac_find_stream(sc, dir, stream); KASSERT(ss >= 0, ("Free for not allocated stream (%d/%d)\n", dir, stream)); + if (dir == 1) + sc->sdo_bw_used -= sc->streams[ss].bw; + else + sc->codecs[cad].sdi_bw_used -= sc->streams[ss].bw; sc->streams[ss].stream = 0; sc->streams[ss].dev = NULL; } @@ -1864,6 +1911,8 @@ hdac_stream_start(device_t dev, device_t child, ctl &= ~HDAC_SDCTL2_DIR; ctl &= ~HDAC_SDCTL2_STRM_MASK; ctl |= stream << HDAC_SDCTL2_STRM_SHIFT; + ctl &= ~HDAC_SDCTL2_STRIPE_MASK; + ctl |= sc->streams[ss].stripe << HDAC_SDCTL2_STRIPE_SHIFT; HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL2, ctl); HDAC_WRITE_2(&sc->mem, off + HDAC_SDFMT, sc->streams[ss].format); @@ -1872,6 +1921,8 @@ hdac_stream_start(device_t dev, device_t child, ctl |= 1 << ss; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + HDAC_WRITE_1(&sc->mem, off + HDAC_SDSTS, + HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS); ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | HDAC_SDCTL_RUN; diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h index fd2390f4b..71e6a6799 100644 --- a/sys/dev/sound/pci/hda/hdac.h +++ b/sys/dev/sound/pci/hda/hdac.h @@ -76,10 +76,21 @@ #define HDA_NVIDIA_MCP79_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac1) #define HDA_NVIDIA_MCP79_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac2) #define HDA_NVIDIA_MCP79_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac3) +#define HDA_NVIDIA_0BE2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0be2) +#define HDA_NVIDIA_0BE3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0be3) +#define HDA_NVIDIA_0BE4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0be4) +#define HDA_NVIDIA_GT100 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0be5) +#define HDA_NVIDIA_GT106 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0be9) +#define HDA_NVIDIA_GT108 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0bea) +#define HDA_NVIDIA_GT104 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0beb) +#define HDA_NVIDIA_GT116 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0bee) #define HDA_NVIDIA_MCP89_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d94) #define HDA_NVIDIA_MCP89_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d95) #define HDA_NVIDIA_MCP89_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d96) #define HDA_NVIDIA_MCP89_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d97) +#define HDA_NVIDIA_GF119 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0e08) +#define HDA_NVIDIA_GF110_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0e09) +#define HDA_NVIDIA_GF110_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0e0c) #define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) /* ATI */ @@ -294,6 +305,8 @@ #define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) #define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) #define HDA_CODEC_ALC665 HDA_CODEC_CONSTRUCT(REALTEK, 0x0665) +#define HDA_CODEC_ALC670 HDA_CODEC_CONSTRUCT(REALTEK, 0x0670) +#define HDA_CODEC_ALC680 HDA_CODEC_CONSTRUCT(REALTEK, 0x0680) #define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) #define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) #define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) @@ -307,6 +320,18 @@ #define HDA_CODEC_ALC899 HDA_CODEC_CONSTRUCT(REALTEK, 0x0899) #define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) +/* Motorola */ +#define MOTO_VENDORID 0x1057 +#define HDA_CODEC_MOTOXXXX HDA_CODEC_CONSTRUCT(MOTO, 0xffff) + +/* Creative */ +#define CREATIVE_VENDORID 0x1102 +#define HDA_CODEC_CA0110 HDA_CODEC_CONSTRUCT(CREATIVE, 0x000a) +#define HDA_CODEC_CA0110_2 HDA_CODEC_CONSTRUCT(CREATIVE, 0x000b) +#define HDA_CODEC_SB0880 HDA_CODEC_CONSTRUCT(CREATIVE, 0x000d) +#define HDA_CODEC_CA0132 HDA_CODEC_CONSTRUCT(CREATIVE, 0x0011) +#define HDA_CODEC_CAXXXX HDA_CODEC_CONSTRUCT(CREATIVE, 0xffff) + /* Analog Devices */ #define ANALOGDEVICES_VENDORID 0x11d4 #define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) @@ -328,10 +353,14 @@ #define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) /* CMedia */ -#define CMEDIA_VENDORID 0x434d -#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) +#define CMEDIA_VENDORID 0x13f6 +#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x9880) #define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) +#define CMEDIA2_VENDORID 0x434d +#define HDA_CODEC_CMI98802 HDA_CODEC_CONSTRUCT(CMEDIA2, 0x4980) +#define HDA_CODEC_CMIXXXX2 HDA_CODEC_CONSTRUCT(CMEDIA2, 0xffff) + /* Sigmatel */ #define SIGMATEL_VENDORID 0x8384 #define HDA_CODEC_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) @@ -373,6 +402,10 @@ #define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) #define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) #define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) +#define HDA_CODEC_STAC9255 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a4) +#define HDA_CODEC_STAC9255D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a5) +#define HDA_CODEC_STAC9254 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a6) +#define HDA_CODEC_STAC9254D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a7) #define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) #define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) #define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) @@ -387,10 +420,49 @@ #define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) #define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) #define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) +#define HDA_CODEC_IDT92HD71B8_2 HDA_CODEC_CONSTRUCT(IDT, 0x76b1) #define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) +#define HDA_CODEC_IDT92HD71B7_2 HDA_CODEC_CONSTRUCT(IDT, 0x76b3) +#define HDA_CODEC_IDT92HD71B6 HDA_CODEC_CONSTRUCT(IDT, 0x76b4) +#define HDA_CODEC_IDT92HD71B6_2 HDA_CODEC_CONSTRUCT(IDT, 0x76b5) #define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) +#define HDA_CODEC_IDT92HD71B5_2 HDA_CODEC_CONSTRUCT(IDT, 0x76b7) +#define HDA_CODEC_IDT92HD89C3 HDA_CODEC_CONSTRUCT(IDT, 0x76c0) +#define HDA_CODEC_IDT92HD89C2 HDA_CODEC_CONSTRUCT(IDT, 0x76c1) +#define HDA_CODEC_IDT92HD89C1 HDA_CODEC_CONSTRUCT(IDT, 0x76c2) +#define HDA_CODEC_IDT92HD89B3 HDA_CODEC_CONSTRUCT(IDT, 0x76c3) +#define HDA_CODEC_IDT92HD89B2 HDA_CODEC_CONSTRUCT(IDT, 0x76c4) +#define HDA_CODEC_IDT92HD89B1 HDA_CODEC_CONSTRUCT(IDT, 0x76c5) +#define HDA_CODEC_IDT92HD89E3 HDA_CODEC_CONSTRUCT(IDT, 0x76c6) +#define HDA_CODEC_IDT92HD89E2 HDA_CODEC_CONSTRUCT(IDT, 0x76c7) +#define HDA_CODEC_IDT92HD89E1 HDA_CODEC_CONSTRUCT(IDT, 0x76c8) +#define HDA_CODEC_IDT92HD89D3 HDA_CODEC_CONSTRUCT(IDT, 0x76c9) +#define HDA_CODEC_IDT92HD89D2 HDA_CODEC_CONSTRUCT(IDT, 0x76ca) +#define HDA_CODEC_IDT92HD89D1 HDA_CODEC_CONSTRUCT(IDT, 0x76cb) +#define HDA_CODEC_IDT92HD89F3 HDA_CODEC_CONSTRUCT(IDT, 0x76cc) +#define HDA_CODEC_IDT92HD89F2 HDA_CODEC_CONSTRUCT(IDT, 0x76cd) +#define HDA_CODEC_IDT92HD89F1 HDA_CODEC_CONSTRUCT(IDT, 0x76ce) +#define HDA_CODEC_IDT92HD87B1_3 HDA_CODEC_CONSTRUCT(IDT, 0x76d1) #define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) #define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) +#define HDA_CODEC_IDT92HD87B2_4 HDA_CODEC_CONSTRUCT(IDT, 0x76d9) +#define HDA_CODEC_IDT92HD93BXX HDA_CODEC_CONSTRUCT(IDT, 0x76df) +#define HDA_CODEC_IDT92HD91BXX HDA_CODEC_CONSTRUCT(IDT, 0x76e0) +#define HDA_CODEC_IDT92HD98BXX HDA_CODEC_CONSTRUCT(IDT, 0x76e3) +#define HDA_CODEC_IDT92HD99BXX HDA_CODEC_CONSTRUCT(IDT, 0x76e5) +#define HDA_CODEC_IDT92HD90BXX HDA_CODEC_CONSTRUCT(IDT, 0x76e7) +#define HDA_CODEC_IDT92HD66B1X5 HDA_CODEC_CONSTRUCT(IDT, 0x76e8) +#define HDA_CODEC_IDT92HD66B2X5 HDA_CODEC_CONSTRUCT(IDT, 0x76e9) +#define HDA_CODEC_IDT92HD66B3X5 HDA_CODEC_CONSTRUCT(IDT, 0x76ea) +#define HDA_CODEC_IDT92HD66C1X5 HDA_CODEC_CONSTRUCT(IDT, 0x76eb) +#define HDA_CODEC_IDT92HD66C2X5 HDA_CODEC_CONSTRUCT(IDT, 0x76ec) +#define HDA_CODEC_IDT92HD66C3X5 HDA_CODEC_CONSTRUCT(IDT, 0x76ed) +#define HDA_CODEC_IDT92HD66B1X3 HDA_CODEC_CONSTRUCT(IDT, 0x76ee) +#define HDA_CODEC_IDT92HD66B2X3 HDA_CODEC_CONSTRUCT(IDT, 0x76ef) +#define HDA_CODEC_IDT92HD66B3X3 HDA_CODEC_CONSTRUCT(IDT, 0x76f0) +#define HDA_CODEC_IDT92HD66C1X3 HDA_CODEC_CONSTRUCT(IDT, 0x76f1) +#define HDA_CODEC_IDT92HD66C2X3 HDA_CODEC_CONSTRUCT(IDT, 0x76f2) +#define HDA_CODEC_IDT92HD66C3_65 HDA_CODEC_CONSTRUCT(IDT, 0x76f3) #define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) /* Silicon Image */ @@ -484,7 +556,9 @@ /* NVIDIA */ #define HDA_CODEC_NVIDIAMCP78 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0002) -#define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) +#define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0003) +#define HDA_CODEC_NVIDIAMCP78_3 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0005) +#define HDA_CODEC_NVIDIAMCP78_4 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) #define HDA_CODEC_NVIDIAMCP7A HDA_CODEC_CONSTRUCT(NVIDIA, 0x0007) #define HDA_CODEC_NVIDIAGT220 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000a) #define HDA_CODEC_NVIDIAGT21X HDA_CODEC_CONSTRUCT(NVIDIA, 0x000b) @@ -494,6 +568,10 @@ #define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001) #define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) +/* Chrontel */ +#define CHRONTEL_VENDORID 0x17e8 +#define HDA_CODEC_CHXXXX HDA_CODEC_CONSTRUCT(CHRONTEL, 0xffff) + /* INTEL */ #define HDA_CODEC_INTELIP HDA_CODEC_CONSTRUCT(INTEL, 0x0054) #define HDA_CODEC_INTELBL HDA_CODEC_CONSTRUCT(INTEL, 0x2801) @@ -510,6 +588,18 @@ ****************************************************************************/ #define HDA_DMA_ALIGNMENT 128 + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BLK_MIN HDA_DMA_ALIGNMENT +#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) + +#define HDA_BUFSZ_MIN (HDA_BDL_MIN * HDA_BLK_MIN) +#define HDA_BUFSZ_MAX 262144 +#define HDA_BUFSZ_DEFAULT 65536 + #define HDA_GPIO_MAX 8 #define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ diff --git a/sys/dev/sound/pci/hda/hdac_if.m b/sys/dev/sound/pci/hda/hdac_if.m index 4fb77bd5b..f87e2edff 100644 --- a/sys/dev/sound/pci/hda/hdac_if.m +++ b/sys/dev/sound/pci/hda/hdac_if.m @@ -44,6 +44,7 @@ METHOD int stream_alloc { device_t child; int dir; int format; + int stripe; uint32_t **dmapos; }; diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h index 8532f83d9..b8798d7e3 100644 --- a/sys/dev/sound/pci/hda/hdac_private.h +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -155,6 +155,8 @@ struct hdac_stream { int stream; int blksz; int running; + int bw; + int stripe; uint16_t format; }; @@ -206,6 +208,8 @@ struct hdac_softc { int unsolq_st; uint32_t unsolq[HDAC_UNSOLQ_MAX]; + int sdo_bw_used; + struct hdac_stream *streams; struct { @@ -216,6 +220,7 @@ struct hdac_softc { uint8_t stepping_id; int pending; uint32_t response; + int sdi_bw_used; } codecs[HDAC_CODEC_MAX]; }; diff --git a/sys/dev/sound/pci/hda/hdacc.c b/sys/dev/sound/pci/hda/hdacc.c index 47feb1b58..740b2c548 100644 --- a/sys/dev/sound/pci/hda/hdacc.c +++ b/sys/dev/sound/pci/hda/hdacc.c @@ -92,6 +92,8 @@ static const struct { { HDA_CODEC_ALC662, 0, "Realtek ALC662" }, { HDA_CODEC_ALC663, 0, "Realtek ALC663" }, { HDA_CODEC_ALC665, 0, "Realtek ALC665" }, + { HDA_CODEC_ALC670, 0, "Realtek ALC670" }, + { HDA_CODEC_ALC680, 0, "Realtek ALC680" }, { HDA_CODEC_ALC861, 0x0340, "Realtek ALC660" }, { HDA_CODEC_ALC861, 0, "Realtek ALC861" }, { HDA_CODEC_ALC861VD, 0, "Realtek ALC861-VD" }, @@ -123,7 +125,12 @@ static const struct { { HDA_CODEC_AD1988B, 0, "Analog Devices AD1988B" }, { HDA_CODEC_AD1989A, 0, "Analog Devices AD1989A" }, { HDA_CODEC_AD1989B, 0, "Analog Devices AD1989B" }, + { HDA_CODEC_CA0110, 0, "Creative CA0110-IBG" }, + { HDA_CODEC_CA0110_2, 0, "Creative CA0110-IBG" }, + { HDA_CODEC_CA0132, 0, "Creative CA0132" }, + { HDA_CODEC_SB0880, 0, "Creative SB0880 X-Fi" }, { HDA_CODEC_CMI9880, 0, "CMedia CMI9880" }, + { HDA_CODEC_CMI98802, 0, "CMedia CMI9880" }, { HDA_CODEC_CXD9872RDK, 0, "Sigmatel CXD9872RD/K" }, { HDA_CODEC_CXD9872AKD, 0, "Sigmatel CXD9872AKD" }, { HDA_CODEC_STAC9200D, 0, "Sigmatel STAC9200D" }, @@ -148,6 +155,10 @@ static const struct { { HDA_CODEC_STAC9230D, 0, "Sigmatel STAC9230D" }, { HDA_CODEC_STAC9250, 0, "Sigmatel STAC9250" }, { HDA_CODEC_STAC9251, 0, "Sigmatel STAC9251" }, + { HDA_CODEC_STAC9255, 0, "Sigmatel STAC9255" }, + { HDA_CODEC_STAC9255D, 0, "Sigmatel STAC9255D" }, + { HDA_CODEC_STAC9254, 0, "Sigmatel STAC9254" }, + { HDA_CODEC_STAC9254D, 0, "Sigmatel STAC9254D" }, { HDA_CODEC_STAC9271X, 0, "Sigmatel STAC9271X" }, { HDA_CODEC_STAC9271D, 0, "Sigmatel STAC9271D" }, { HDA_CODEC_STAC9272X, 0, "Sigmatel STAC9272X" }, @@ -163,11 +174,28 @@ static const struct { { HDA_CODEC_IDT92HD005D, 0, "IDT 92HD005D" }, { HDA_CODEC_IDT92HD206X, 0, "IDT 92HD206X" }, { HDA_CODEC_IDT92HD206D, 0, "IDT 92HD206D" }, + { HDA_CODEC_IDT92HD66B1X5, 0, "IDT 92HD66B1X5" }, + { HDA_CODEC_IDT92HD66B2X5, 0, "IDT 92HD66B2X5" }, + { HDA_CODEC_IDT92HD66B3X5, 0, "IDT 92HD66B3X5" }, + { HDA_CODEC_IDT92HD66C1X5, 0, "IDT 92HD66C1X5" }, + { HDA_CODEC_IDT92HD66C2X5, 0, "IDT 92HD66C2X5" }, + { HDA_CODEC_IDT92HD66C3X5, 0, "IDT 92HD66C3X5" }, + { HDA_CODEC_IDT92HD66B1X3, 0, "IDT 92HD66B1X3" }, + { HDA_CODEC_IDT92HD66B2X3, 0, "IDT 92HD66B2X3" }, + { HDA_CODEC_IDT92HD66B3X3, 0, "IDT 92HD66B3X3" }, + { HDA_CODEC_IDT92HD66C1X3, 0, "IDT 92HD66C1X3" }, + { HDA_CODEC_IDT92HD66C2X3, 0, "IDT 92HD66C2X3" }, + { HDA_CODEC_IDT92HD66C3_65, 0, "IDT 92HD66C3_65" }, { HDA_CODEC_IDT92HD700X, 0, "IDT 92HD700X" }, { HDA_CODEC_IDT92HD700D, 0, "IDT 92HD700D" }, { HDA_CODEC_IDT92HD71B5, 0, "IDT 92HD71B5" }, + { HDA_CODEC_IDT92HD71B5_2, 0, "IDT 92HD71B5" }, + { HDA_CODEC_IDT92HD71B6, 0, "IDT 92HD71B6" }, + { HDA_CODEC_IDT92HD71B6_2, 0, "IDT 92HD71B6" }, { HDA_CODEC_IDT92HD71B7, 0, "IDT 92HD71B7" }, + { HDA_CODEC_IDT92HD71B7_2, 0, "IDT 92HD71B7" }, { HDA_CODEC_IDT92HD71B8, 0, "IDT 92HD71B8" }, + { HDA_CODEC_IDT92HD71B8_2, 0, "IDT 92HD71B8" }, { HDA_CODEC_IDT92HD73C1, 0, "IDT 92HD73C1" }, { HDA_CODEC_IDT92HD73D1, 0, "IDT 92HD73D1" }, { HDA_CODEC_IDT92HD73E1, 0, "IDT 92HD73E1" }, @@ -177,6 +205,28 @@ static const struct { { HDA_CODEC_IDT92HD81B1X, 0, "IDT 92HD81B1X" }, { HDA_CODEC_IDT92HD83C1C, 0, "IDT 92HD83C1C" }, { HDA_CODEC_IDT92HD83C1X, 0, "IDT 92HD83C1X" }, + { HDA_CODEC_IDT92HD87B1_3, 0, "IDT 92HD87B1/3" }, + { HDA_CODEC_IDT92HD87B2_4, 0, "IDT 92HD87B2/4" }, + { HDA_CODEC_IDT92HD89C3, 0, "IDT 92HD89C3" }, + { HDA_CODEC_IDT92HD89C2, 0, "IDT 92HD89C2" }, + { HDA_CODEC_IDT92HD89C1, 0, "IDT 92HD89C1" }, + { HDA_CODEC_IDT92HD89B3, 0, "IDT 92HD89B3" }, + { HDA_CODEC_IDT92HD89B2, 0, "IDT 92HD89B2" }, + { HDA_CODEC_IDT92HD89B1, 0, "IDT 92HD89B1" }, + { HDA_CODEC_IDT92HD89E3, 0, "IDT 92HD89E3" }, + { HDA_CODEC_IDT92HD89E2, 0, "IDT 92HD89E2" }, + { HDA_CODEC_IDT92HD89E1, 0, "IDT 92HD89E1" }, + { HDA_CODEC_IDT92HD89D3, 0, "IDT 92HD89D3" }, + { HDA_CODEC_IDT92HD89D2, 0, "IDT 92HD89D2" }, + { HDA_CODEC_IDT92HD89D1, 0, "IDT 92HD89D1" }, + { HDA_CODEC_IDT92HD89F3, 0, "IDT 92HD89F3" }, + { HDA_CODEC_IDT92HD89F2, 0, "IDT 92HD89F2" }, + { HDA_CODEC_IDT92HD89F1, 0, "IDT 92HD89F1" }, + { HDA_CODEC_IDT92HD90BXX, 0, "IDT 92HD90BXX" }, + { HDA_CODEC_IDT92HD91BXX, 0, "IDT 92HD91BXX" }, + { HDA_CODEC_IDT92HD93BXX, 0, "IDT 92HD93BXX" }, + { HDA_CODEC_IDT92HD98BXX, 0, "IDT 92HD98BXX" }, + { HDA_CODEC_IDT92HD99BXX, 0, "IDT 92HD99BXX" }, { HDA_CODEC_CX20549, 0, "Conexant CX20549 (Venice)" }, { HDA_CODEC_CX20551, 0, "Conexant CX20551 (Waikiki)" }, { HDA_CODEC_CX20561, 0, "Conexant CX20561 (Hermosa)" }, @@ -250,6 +300,8 @@ static const struct { { HDA_CODEC_NVIDIAMCP73, 0, "NVIDIA MCP73" }, { HDA_CODEC_NVIDIAMCP78, 0, "NVIDIA MCP78" }, { HDA_CODEC_NVIDIAMCP78_2, 0, "NVIDIA MCP78" }, + { HDA_CODEC_NVIDIAMCP78_3, 0, "NVIDIA MCP78" }, + { HDA_CODEC_NVIDIAMCP78_4, 0, "NVIDIA MCP78" }, { HDA_CODEC_NVIDIAMCP7A, 0, "NVIDIA MCP7A" }, { HDA_CODEC_NVIDIAGT220, 0, "NVIDIA GT220" }, { HDA_CODEC_NVIDIAGT21X, 0, "NVIDIA GT21x" }, @@ -266,44 +318,26 @@ static const struct { { HDA_CODEC_SII1390, 0, "Silicon Image SiI1390" }, { HDA_CODEC_SII1392, 0, "Silicon Image SiI1392" }, /* Unknown CODECs */ - { HDA_CODEC_ALCXXXX, 0, "Realtek (Unknown)" }, - { HDA_CODEC_ADXXXX, 0, "Analog Devices (Unknown)" }, - { HDA_CODEC_CSXXXX, 0, "Cirrus Logic (Unknown)" }, - { HDA_CODEC_CMIXXXX, 0, "CMedia (Unknown)" }, - { HDA_CODEC_STACXXXX, 0, "Sigmatel (Unknown)" }, - { HDA_CODEC_SIIXXXX, 0, "Silicon Image (Unknown)" }, - { HDA_CODEC_AGEREXXXX, 0, "Lucent/Agere Systems (Unknown)" }, - { HDA_CODEC_CXXXXX, 0, "Conexant (Unknown)" }, - { HDA_CODEC_VTXXXX, 0, "VIA (Unknown)" }, - { HDA_CODEC_ATIXXXX, 0, "ATI (Unknown)" }, - { HDA_CODEC_NVIDIAXXXX, 0, "NVIDIA (Unknown)" }, - { HDA_CODEC_INTELXXXX, 0, "Intel (Unknown)" }, - { HDA_CODEC_IDTXXXX, 0, "IDT (Unknown)" }, + { HDA_CODEC_ADXXXX, 0, "Analog Devices" }, + { HDA_CODEC_AGEREXXXX, 0, "Lucent/Agere Systems" }, + { HDA_CODEC_ALCXXXX, 0, "Realtek" }, + { HDA_CODEC_ATIXXXX, 0, "ATI" }, + { HDA_CODEC_CAXXXX, 0, "Creative" }, + { HDA_CODEC_CMIXXXX, 0, "CMedia" }, + { HDA_CODEC_CMIXXXX2, 0, "CMedia" }, + { HDA_CODEC_CSXXXX, 0, "Cirrus Logic" }, + { HDA_CODEC_CXXXXX, 0, "Conexant" }, + { HDA_CODEC_CHXXXX, 0, "Chrontel" }, + { HDA_CODEC_IDTXXXX, 0, "IDT" }, + { HDA_CODEC_INTELXXXX, 0, "Intel" }, + { HDA_CODEC_MOTOXXXX, 0, "Motorola" }, + { HDA_CODEC_NVIDIAXXXX, 0, "NVIDIA" }, + { HDA_CODEC_SIIXXXX, 0, "Silicon Image" }, + { HDA_CODEC_STACXXXX, 0, "Sigmatel" }, + { HDA_CODEC_VTXXXX, 0, "VIA" }, }; #define HDACC_CODECS_LEN (sizeof(hdacc_codecs) / sizeof(hdacc_codecs[0])) - -/**************************************************************************** - * Function prototypes - ****************************************************************************/ - -static char * -hdacc_codec_name(uint32_t id, uint16_t revid) -{ - int i; - - for (i = 0; i < HDACC_CODECS_LEN; i++) { - if (!HDA_DEV_MATCH(hdacc_codecs[i].id, id)) - continue; - if (hdacc_codecs[i].revid != 0 && - hdacc_codecs[i].revid != revid) - continue; - return (hdacc_codecs[i].name); - } - - return ((id == 0x00000000) ? "NULL CODEC" : "Unknown CODEC"); -} - static int hdacc_suspend(device_t dev) { @@ -337,10 +371,28 @@ hdacc_probe(device_t dev) { uint32_t id, revid; char buf[128]; + int i; id = ((uint32_t)hda_get_vendor_id(dev) << 16) + hda_get_device_id(dev); revid = ((uint32_t)hda_get_revision_id(dev) << 8) + hda_get_stepping_id(dev); - snprintf(buf, sizeof(buf), "%s HDA CODEC", hdacc_codec_name(id, revid)); + + for (i = 0; i < HDACC_CODECS_LEN; i++) { + if (!HDA_DEV_MATCH(hdacc_codecs[i].id, id)) + continue; + if (hdacc_codecs[i].revid != 0 && + hdacc_codecs[i].revid != revid) + continue; + break; + } + if (i < HDACC_CODECS_LEN) { + if ((hdacc_codecs[i].id & 0xffff) != 0xffff) + strlcpy(buf, hdacc_codecs[i].name, sizeof(buf)); + else + snprintf(buf, sizeof(buf), "%s (0x%04x)", + hdacc_codecs[i].name, hda_get_device_id(dev)); + } else + snprintf(buf, sizeof(buf), "Generic (0x%04x)", id); + strlcat(buf, " HDA CODEC", sizeof(buf)); device_set_desc_copy(dev, buf); return (BUS_PROBE_DEFAULT); } @@ -503,13 +555,13 @@ hdacc_codec_command(device_t dev, device_t child, uint32_t verb) static int hdacc_stream_alloc(device_t dev, device_t child, int dir, int format, - uint32_t **dmapos) + int stripe, uint32_t **dmapos) { struct hdacc_softc *codec = device_get_softc(dev); int stream; stream = HDAC_STREAM_ALLOC(device_get_parent(dev), dev, - dir, format, dmapos); + dir, format, stripe, dmapos); if (stream > 0) codec->streams[dir][stream] = child; return (stream); @@ -560,7 +612,7 @@ hdacc_stream_intr(device_t dev, int dir, int stream) struct hdacc_softc *codec = device_get_softc(dev); device_t child; - if ((child = codec->streams[dir][stream]) != NULL); + if ((child = codec->streams[dir][stream]) != NULL) HDAC_STREAM_INTR(child, dir, stream); } diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index 2b3b3904c..d45dec1e1 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -2000,9 +2000,10 @@ chn_setformat(struct pcm_channel *c, uint32_t format) int ret; /* XXX force stereo */ - if (format & AFMT_PASSTHROUGH) + if ((format & AFMT_PASSTHROUGH) && AFMT_CHANNEL(format) < 2) { format = SND_FORMAT(format, AFMT_PASSTHROUGH_CHANNEL, AFMT_PASSTHROUGH_EXTCHANNEL); + } oldformat = c->format; oldspeed = c->speed; -- 2.45.0