Date: Fri, 8 Jun 2012 13:10:18 +0000 (UTC) From: Alexander Motin <mav@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r236753 - in stable/8: share/man/man4 sys/dev/sound/pci/hda sys/dev/sound/pcm Message-ID: <201206081310.q58DAIfk060994@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mav Date: Fri Jun 8 13:10:18 2012 New Revision: 236753 URL: http://svn.freebsd.org/changeset/base/236753 Log: 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. Modified: stable/8/share/man/man4/snd_hda.4 stable/8/sys/dev/sound/pci/hda/hda_reg.h stable/8/sys/dev/sound/pci/hda/hdaa.c stable/8/sys/dev/sound/pci/hda/hdaa.h stable/8/sys/dev/sound/pci/hda/hdaa_patches.c stable/8/sys/dev/sound/pci/hda/hdac.c stable/8/sys/dev/sound/pci/hda/hdac.h stable/8/sys/dev/sound/pci/hda/hdac_if.m stable/8/sys/dev/sound/pci/hda/hdac_private.h stable/8/sys/dev/sound/pci/hda/hdacc.c stable/8/sys/dev/sound/pcm/channel.c Directory Properties: stable/8/share/man/man4/ (props changed) stable/8/sys/ (props changed) Modified: stable/8/share/man/man4/snd_hda.4 ============================================================================== --- stable/8/share/man/man4/snd_hda.4 Fri Jun 8 12:56:10 2012 (r236752) +++ stable/8/share/man/man4/snd_hda.4 Fri Jun 8 13:10:18 2012 (r236753) @@ -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 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 BI 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. Modified: stable/8/sys/dev/sound/pci/hda/hda_reg.h ============================================================================== --- stable/8/sys/dev/sound/pci/hda/hda_reg.h Fri Jun 8 12:56:10 2012 (r236752) +++ stable/8/sys/dev/sound/pci/hda/hda_reg.h Fri Jun 8 13:10:18 2012 (r236753) @@ -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 Modified: stable/8/sys/dev/sound/pci/hda/hdaa.c ============================================================================== --- stable/8/sys/dev/sound/pci/hda/hdaa.c Fri Jun 8 12:56:10 2012 (r236752) +++ stable/8/sys/dev/sound/pci/hda/hdaa.c Fri Jun 8 13:10:18 2012 (r236753) @@ -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 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(uin 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_devin } /* - * 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])); - - HDA_BOOTVERBOSE( - device_printf(devinfo->dev, - "Pin sense: nid=%d sence=0x%08x", - as->pins[15], res); - ); - - res = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0; - if (devinfo->quirks & HDAA_QUIRK_SENSEINV) - res ^= 1; + uint32_t val; + int j, connected = w->wclass.pin.connected; HDA_BOOTVERBOSE( - printf(" %sconnected\n", res == 0 ? "dis" : ""); + device_printf((as->pdevinfo && as->pdevinfo->dev) ? + as->pdevinfo->dev : devinfo->dev, + "Redirect output to: %s\n", + connected ? "headphones": "main"); ); - /* (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_devin } } 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_devin 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_devin 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 } 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_widg } 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_ 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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201206081310.q58DAIfk060994>