Improve HDMI/DisplayPort audio support in snd_hda(4):

- Enable and handle unsolicited responses from digital display pins,
reporting connection and EDID-Like Data (ELD) validity status changes.
 - Fetch ELD data, describing connected digital display device audio
capabilities. These data not really used at the moment (user is not
denied to use audio formats not supported by the device), only printed to
verbose logs. But they are useful for debugging. The fact that ELD was
received tells that HDMI link was established and video driver enabled
HDMI audio passthrough. Some old chips may not return ELD, so lack of it
is not necessary a problem.
 - Add some more points to CODEC configuration sequence:
   - For converter widgets, supporting more then two channels (HDMI/DP
     converter widgets support 8), set number of channels to handle.
   - For digital display pins (HDMI/DP) fill audio infoframe, reporting
     connected device about number of channels and speakers allocation.
   - For digital display pins (HDMI/DP) set mapping between channels seen
     by software and channels transferred via HDMI/DisplayPort.
 - Allow more audio formats, not used for analog connections because of
stereo pairs orientation, but easily applicable to HDMI/DisplayPort: 2.1,
3.0, 3.1, 4.1, 5.0, 6.0, 6.1, 7.0. That list may be filtered later using
info from ELD.
 - Disable MSI interrupts for NVIDIA HDA controllers before GT520.

At this point I can successfully play audio over HDMI from NVIDIA GT210
and GT520 cards with nvidia-driver-290.10 driver to Marantz SR4001
receiver in 2.0, 2.1, 3.0, 4.0, 4.1, 5.0 and 5.1 PCM formats at 44, 48,
88 and 96KHz at 16 and 24 bits, same as do AC3/DTS passthrough.
6.0, 6.1, 7.0 and 7.1 PCM formats are not working for me, but I think
it is because of receiver age.

MFC after:	2 months
Sponsored by:	iXsystems, Inc.
This commit is contained in:
Alexander Motin 2012-01-18 19:12:33 +00:00
parent 925c02f9dd
commit 88addcbe26
5 changed files with 522 additions and 96 deletions

View File

@ -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

View File

@ -116,6 +116,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),
@ -246,43 +252,38 @@ hdaa_audio_ctl_amp_get(struct hdaa_devinfo *devinfo, nid_t nid, int dir,
* Jack detection (Speaker/HP redirection) event handler.
*/
static void
hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid)
hdaa_hp_switch_handler(struct hdaa_widget *w)
{
struct hdaa_devinfo *devinfo = w->devinfo;
struct hdaa_audio_as *as;
struct hdaa_widget *w;
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 !=
if (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]));
res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid));
HDA_BOOTVERBOSE(
device_printf(devinfo->dev,
"Pin sense: nid=%d sence=0x%08x",
as->pins[15], res);
"Pin sense: nid=%d sence=0x%08x", w->nid, res);
);
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" : "");
printf(" (%sconnected)\n", res == 0 ? "dis" : "");
);
as = &devinfo->as[w->bindas];
if (as->hpredir < 0 || as->pins[15] != w->nid)
return;
/* (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;
@ -294,9 +295,6 @@ 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;
@ -310,7 +308,6 @@ hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid)
w->nid, w->wclass.pin.ctrl));
}
}
}
/* (Un)Mute other pins. */
for (j = 0; j < 15; j++) {
if (as->pins[j] <= 0)
@ -329,20 +326,20 @@ 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 ==
w1 = hdaa_widget_get(devinfo, as->pins[j]);
if (w1 != NULL && w1->type ==
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) {
if (res != 0)
val = w->wclass.pin.ctrl &
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));
}
}
}
@ -355,6 +352,7 @@ static void
hdaa_jack_poll_callback(void *arg)
{
struct hdaa_devinfo *devinfo = arg;
struct hdaa_widget *w;
int i;
hdaa_lock(devinfo);
@ -365,7 +363,11 @@ 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_hp_switch_handler(w);
}
callout_reset(&devinfo->poll_jack, devinfo->poll_ival,
hdaa_jack_poll_callback, devinfo);
@ -397,15 +399,17 @@ hdaa_hp_switch_init(struct hdaa_devinfo *devinfo)
as[i].pins[15]);
continue;
}
if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) {
as[i].unsol = HDAC_UNSOL_ALLOC(
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
w->unsol));
}
if (w->unsol < 0)
poll = 1;
HDA_BOOTVERBOSE(
device_printf(devinfo->dev,
@ -414,7 +418,7 @@ hdaa_hp_switch_init(struct hdaa_devinfo *devinfo)
i, w->nid,
(poll != 0) ? "polling" : "unsolicited responses");
);
hdaa_hp_switch_handler(devinfo, i);
hdaa_hp_switch_handler(w);
}
if (poll) {
callout_reset(&devinfo->poll_jack, 1,
@ -430,18 +434,208 @@ hdaa_hp_switch_deinit(struct hdaa_devinfo *devinfo)
int i;
for (i = 0; i < devinfo->ascnt; i++) {
if (as[i].unsol < 0)
continue;
w = hdaa_widget_get(devinfo, as[i].pins[15]);
if (w == NULL || w->enable == 0 || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) ||
HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
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;
}
}
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 (w->eld != NULL) {
w->eld_len = 0;
free(w->eld, M_HDAA);
w->eld = NULL;
}
res = hda_command(devinfo->dev, HDA_CMD_GET_PIN_SENSE(0, w->nid));
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);
);
}
static void
hdaa_eld_init(struct hdaa_devinfo *devinfo)
{
struct hdaa_widget *w;
int i;
for (i = devinfo->startnode; i < devinfo->endnode; i++) {
w = hdaa_widget_get(devinfo, i);
if (w == NULL || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
if (!HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) &&
!HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
continue;
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 |
w->unsol));
}
hdaa_eld_handler(w);
}
}
static void
hdaa_eld_deinit(struct hdaa_devinfo *devinfo)
{
struct hdaa_widget *w;
int i;
for (i = devinfo->startnode; i < devinfo->endnode; i++) {
w = hdaa_widget_get(devinfo, i);
if (w == NULL || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
continue;
if (!HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap) &&
!HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap))
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,
w->unsol);
w->unsol = -1;
}
}
@ -938,6 +1132,7 @@ hdaa_widget_parse(struct hdaa_widget *w)
hdaa_sysctl_config, "A", "Original pin configuration");
hdaa_lock(w->devinfo);
}
w->unsol = -1;
}
static void
@ -1197,27 +1392,42 @@ 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 */
/* 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 */
int map = -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 +1444,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 +1462,93 @@ 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
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], 1));
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\n",
(ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC",
ch->io[i], fmt, dfmt, c, cchn);
);
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, ch->io[i], 0x00));
HDA_CMD_SET_HDMI_CHAN_SLOT(0, nid,
(((hdmich[totalextchn == 0 ? 0 : 1][totalchn - 1]
>> (k * 4)) & 0xf) << 4) | k));
}
/* Stop audio infoframe transmission. */
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;
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;
}
}
@ -2169,7 +2451,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;
}
@ -4277,21 +4558,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 == 6 || /* Any 6-channel */
pinset == 0x0017) { /* 7.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) {
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 == 8) { /* Any 8-channel */
}
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) {
ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1);
if (ch->bit32)
ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1);
@ -4584,8 +4895,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)
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");
}
@ -4956,9 +5277,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);
}
@ -5063,6 +5387,10 @@ hdaa_configure(device_t dev)
device_printf(dev, "Applying direct built-in patches...\n");
);
hdaa_patch_direct(devinfo);
HDA_BOOTHVERBOSE(
device_printf(dev, "ELD init...\n");
);
hdaa_eld_init(devinfo);
HDA_BOOTHVERBOSE(
device_printf(dev, "HP switch init...\n");
);
@ -5134,6 +5462,10 @@ hdaa_unconfigure(device_t dev)
device_printf(dev, "HP switch deinit...\n");
);
hdaa_hp_switch_deinit(devinfo);
HDA_BOOTHVERBOSE(
device_printf(dev, "ELD deinit...\n");
);
hdaa_eld_deinit(devinfo);
free(devinfo->ctl, M_HDAA);
devinfo->ctl = NULL;
devinfo->ctlcnt = 0;
@ -5160,6 +5492,11 @@ hdaa_unconfigure(device_t dev)
for (j = 0; j < w->nconns; j++)
w->connsenable[j] = 1;
w->wclass.pin.config = w->wclass.pin.newconf;
if (w->eld != NULL) {
w->eld_len = 0;
free(w->eld, M_HDAA);
w->eld = NULL;
}
}
}
@ -5656,12 +5993,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_hp_switch_handler(w);
if (flags & 0x02)
hdaa_eld_handler(w);
}
}

View File

@ -93,9 +93,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;
@ -140,7 +143,6 @@ 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. */
};
@ -197,7 +199,7 @@ 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 *dmapos;

View File

@ -124,6 +124,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 },

View File

@ -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 */