Print this page
10619 audiohd ignores digital output pins such as HDMI/DP
*** 24,33 ****
--- 24,35 ----
#include <sys/audio/audio_driver.h>
#include <sys/note.h>
#include <sys/beep.h>
#include <sys/pci.h>
+ #include <sys/stddef.h>
+ #include <sys/sysmacros.h>
#include "audiohd.h"
#define DRVNAME "audiohd"
/*
*** 82,91 ****
--- 84,96 ----
static void audiohd_finish_beep_path(hda_codec_t *);
static void audiohd_do_set_beep_volume(audiohd_state_t *,
audiohd_path_t *, uint64_t);
static void audiohd_set_beep_volume(audiohd_state_t *);
static int audiohd_set_beep(void *, uint64_t);
+ static void audiohd_setup_hdmi_dp(audiohd_state_t *, uint8_t, uint8_t,
+ boolean_t);
+ static audiohd_eld_t *audiohd_get_eld(audiohd_state_t *, uint8_t, uint8_t);
static void audiohd_pin_sense(audiohd_state_t *, uint32_t, uint32_t);
static int audiohd_beep;
static int audiohd_beep_divider;
static int audiohd_beep_vol = 1;
*** 331,341 ****
if (audiohd_init_state(statep, dip) != DDI_SUCCESS) {
audio_dev_warn(NULL, "audiohd_init_state failed");
goto error;
}
! /* Set PCI command register to enable bus master and memeory I/O */
if (audiohd_init_pci(statep, &hda_dev_accattr) != DDI_SUCCESS) {
audio_dev_warn(statep->adev,
"couldn't init pci regs");
goto error;
}
--- 336,346 ----
if (audiohd_init_state(statep, dip) != DDI_SUCCESS) {
audio_dev_warn(NULL, "audiohd_init_state failed");
goto error;
}
! /* Set PCI command register to enable bus master and memory I/O */
if (audiohd_init_pci(statep, &hda_dev_accattr) != DDI_SUCCESS) {
audio_dev_warn(statep->adev,
"couldn't init pci regs");
goto error;
}
*** 580,590 ****
/* enable SPDIF output */
for (i = 0; i < path->pin_nums; i++) {
wid = path->pin_wid[i];
widget = codec->widget[wid];
pin = (audiohd_pin_t *)widget->priv;
! if (pin->device == DTYPE_SPDIF_OUT) {
ctrl = audioha_codec_verb_get(
statep,
codec->index,
path->adda_wid,
AUDIOHDC_VERB_GET_SPDIF_CTL,
--- 585,596 ----
/* enable SPDIF output */
for (i = 0; i < path->pin_nums; i++) {
wid = path->pin_wid[i];
widget = codec->widget[wid];
pin = (audiohd_pin_t *)widget->priv;
! if (pin->device == DTYPE_SPDIF_OUT ||
! pin->device == DTYPE_DIGIT_OUT) {
ctrl = audioha_codec_verb_get(
statep,
codec->index,
path->adda_wid,
AUDIOHDC_VERB_GET_SPDIF_CTL,
*** 664,674 ****
--- 670,690 ----
path->adda_wid,
AUDIOHDC_VERB_SET_CONV_FMT,
statep->port[PORT_DAC]->format << 4 |
statep->pchan - 1);
}
+
+ /* Setup HDMI/DP output pin */
+ if (pin->device == DTYPE_DIGIT_OUT) {
+ pin->eld = audiohd_get_eld(statep, codec->index, pin->wid);
+ if (pin->eld != NULL &&
+ (pin->eld->eld_conn_type & 0x3) != 0)
+ audiohd_setup_hdmi_dp(statep, codec->index, pin->wid,
+ pin->eld->eld_conn_type == AUDIOHD_ELD_CONN_HDMI);
+ }
}
+
static void
audiohd_init_record_path(audiohd_path_t *path)
{
audiohd_state_t *statep = path->statep;
hda_codec_t *codec = path->codec;
*** 2140,2150 ****
vid = pci_config_get16(statep->hda_pci_handle, PCI_CONF_VENID);
switch (vid) {
case AUDIOHD_VID_INTEL:
/*
* Currently, Intel (G)MCH and ICHx chipsets support PCI
! * Express QoS. It implemenets two VCs(virtual channels)
* and allows OS software to map 8 traffic classes to the
* two VCs. Some BIOSes initialize HD audio hardware to
* use TC7 (traffic class 7) and to map TC7 to VC1 as Intel
* recommended. However, solaris doesn't support PCI express
* QoS yet. As a result, this driver can not work for those
--- 2156,2166 ----
vid = pci_config_get16(statep->hda_pci_handle, PCI_CONF_VENID);
switch (vid) {
case AUDIOHD_VID_INTEL:
/*
* Currently, Intel (G)MCH and ICHx chipsets support PCI
! * Express QoS. It implements two VCs (virtual channels)
* and allows OS software to map 8 traffic classes to the
* two VCs. Some BIOSes initialize HD audio hardware to
* use TC7 (traffic class 7) and to map TC7 to VC1 as Intel
* recommended. However, solaris doesn't support PCI express
* QoS yet. As a result, this driver can not work for those
*** 2722,2731 ****
--- 2738,2748 ----
/* enable the unsolicited response of the pin */
if ((widget->widget_cap & AUDIOHD_URCAP_MASK) &&
(pin->cap & AUDIOHD_DTCCAP_MASK) &&
((pin->device == DTYPE_LINEOUT) ||
+ (pin->device == DTYPE_DIGIT_OUT) ||
(pin->device == DTYPE_SPDIF_OUT) ||
(pin->device == DTYPE_HP_OUT) ||
(pin->device == DTYPE_MIC_IN))) {
urctrl = (uint8_t)(1 << (AUDIOHD_UR_ENABLE_OFF - 1));
urctrl |= (wid & AUDIOHD_UR_TAG_MASK);
*** 3238,3247 ****
--- 3255,3265 ----
if ((pin->config & AUDIOHD_PIN_CONF_MASK) ==
AUDIOHD_PIN_NO_CONN)
continue;
if ((pin->device != DTYPE_LINEOUT) &&
(pin->device != DTYPE_SPEAKER) &&
+ (pin->device != DTYPE_DIGIT_OUT) &&
(pin->device != DTYPE_SPDIF_OUT) &&
(pin->device != DTYPE_HP_OUT))
continue;
if (pin->finish)
continue;
*** 3752,3762 ****
if (path == NULL)
path = kmem_zalloc(sizeof (audiohd_path_t),
KM_SLEEP);
else
! bzero(path, sizeof (audiohd_port_t));
path->adda_wid = wid;
/*
* Is there any ADC widget which has more than one input ??
--- 3770,3780 ----
if (path == NULL)
path = kmem_zalloc(sizeof (audiohd_path_t),
KM_SLEEP);
else
! bzero(path, sizeof (audiohd_path_t));
path->adda_wid = wid;
/*
* Is there any ADC widget which has more than one input ??
*** 4551,4560 ****
--- 4569,4579 ----
if ((pin->config & AUDIOHD_PIN_CONF_MASK) ==
AUDIOHD_PIN_NO_CONN)
continue;
if ((pin->device != DTYPE_LINEOUT) &&
(pin->device != DTYPE_SPEAKER) &&
+ (pin->device != DTYPE_DIGIT_OUT) &&
(pin->device != DTYPE_SPDIF_OUT) &&
(pin->device != DTYPE_HP_OUT))
continue;
widget = codec->widget[pin->wid];
*** 4823,4832 ****
--- 4842,4852 ----
path->pin_wid[path->pin_nums++] = wid;
break;
case DTYPE_LINEOUT:
case DTYPE_HP_OUT:
case DTYPE_SPDIF_OUT:
+ case DTYPE_DIGIT_OUT:
widget->path_flags |= AUDIOHD_PATH_LOOPBACK;
widget->in_weight++;
pin->adc_wid = path->adda_wid;
path->pin_wid[path->pin_nums++] = wid;
retval = (DDI_SUCCESS);
*** 4891,4901 ****
continue;
if (path == NULL)
path = kmem_zalloc(sizeof (audiohd_path_t), KM_SLEEP);
else
! bzero(path, sizeof (audiohd_port_t));
path->adda_wid = wid;
for (i = 0; i < widget->nconns; i++) {
retval = audiohd_find_output_pins(codec,
widget->avail_conn[i], 0, path);
--- 4911,4921 ----
continue;
if (path == NULL)
path = kmem_zalloc(sizeof (audiohd_path_t), KM_SLEEP);
else
! bzero(path, sizeof (audiohd_path_t));
path->adda_wid = wid;
for (i = 0; i < widget->nconns; i++) {
retval = audiohd_find_output_pins(codec,
widget->avail_conn[i], 0, path);
*** 5285,5294 ****
--- 5305,5315 ----
widget = codec->widget[pin->wid];
if ((widget->widget_cap &
(AUDIOHD_URCAP_MASK) &&
(pin->cap & AUDIOHD_DTCCAP_MASK)) &&
((pin->device == DTYPE_LINEOUT) ||
+ (pin->device == DTYPE_DIGIT_OUT) ||
(pin->device == DTYPE_SPDIF_OUT) ||
(pin->device == DTYPE_HP_OUT) ||
(pin->device == DTYPE_MIC_IN))) {
urctrl = (uint8_t)(1 <<
(AUDIOHD_UR_ENABLE_OFF - 1));
*** 5617,5643 ****
}
}
}
}
}
/*
* audiohd_pin_sense()
*
* Description
*
* When the earphone is plugged into the jack associtated with the pin
* complex, we disable the built in speaker. When the earphone is plugged
* out of the jack, we enable the built in speaker.
*/
static void
audiohd_pin_sense(audiohd_state_t *statep, uint32_t resp, uint32_t respex)
{
uint8_t index;
uint8_t id;
uint32_t rs;
audiohd_widget_t *widget;
- audiohd_pin_t *pin;
hda_codec_t *codec;
index = respex & AUDIOHD_RIRB_CODEC_MASK;
id = resp >> (AUDIOHD_RIRB_WID_OFF - 1);
--- 5638,5791 ----
}
}
}
}
}
+
/*
+ * audiohd_setup_hdmi_dp()
+ *
+ * Description
+ *
+ * Set up a basic channel mapping and a audio infoframe for a
+ * HDMI/DisplayPort pin widget. Due to lack of testing hardware
+ * this is limited to 2 channels for now.
+ */
+ static void
+ audiohd_setup_hdmi_dp(audiohd_state_t *statep, uint8_t index, uint8_t id,
+ boolean_t hdmi)
+ {
+ uint8_t chanmap[] = { 0, 1, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf };
+ uint8_t dp_infoframe[] = { 0x84, 0x1b, 0x44, 0x01, 0x00, 0x00, 0x02 };
+ uint8_t hdmi_infoframe[] = { 0x84, 0x01, 0x0a,
+ (-(0x84 + 0x01 + 0x0a + 0x01 + 0x02)) & 0xff,
+ 0x01, 0x00, 0x00, 0x02 };
+ uint8_t *infoframe;
+ size_t infolen;
+ int i;
+
+ if (hdmi) {
+ infoframe = hdmi_infoframe;
+ infolen = sizeof (hdmi_infoframe);
+ } else {
+ infoframe = dp_infoframe;
+ infolen = sizeof (dp_infoframe);
+ }
+
+ /*
+ * Setup channel mapping. Only 2 channels supported for now.
+ */
+ for (i = 0; i != sizeof (chanmap); i++)
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_ASP_CHMAP, (chanmap[i] << 4) | i);
+
+ /*
+ * Send audio infoframe.
+ */
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_XMIT, AUDIOHD_DIP_XMIT_STOP);
+
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+ for (i = 0; i != AUDIOHD_DIP_DATA_LEN; i++)
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_DATA, 0);
+
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+ for (i = 0; i != infolen; i++)
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_DATA, infoframe[i]);
+
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_INDEX, 0);
+ (void) audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_SET_DIP_XMIT, AUDIOHD_DIP_XMIT_BEST_EFFORT);
+
+ }
+
+ /*
+ * audiohd_get_eld()
+ *
+ * Description
+ *
+ * Fetch the EDID-like data (ELD) from a pin that is a HDMI/DP
+ * display sink device. The data is coming tightly packed from
+ * hardware, unpack it into a audiohd_eld_t structure for easy
+ * access. We get the complete ELD, but we completely ignore the
+ * vendor specific part.
+ */
+ static audiohd_eld_t *
+ audiohd_get_eld(audiohd_state_t *statep, uint8_t index, uint8_t id)
+ {
+ uint8_t *buf;
+ audiohd_eld_t *eld;
+ off_t sad_off;
+ uint32_t rs;
+ size_t eldsz;
+ size_t i;
+
+ rs = audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_GET_DIP_SIZE, AUDIOHD_DIP_SIZE_ELD);
+
+ eldsz = (rs & AUDIOHD_DIP_SIZE_ELD_MASK) + 1;
+ if (eldsz < AUDIOHD_ELD_HDR_LEN)
+ return (NULL);
+
+ eldsz = MAX(eldsz, sizeof (audiohd_eld_t));
+
+ buf = kmem_zalloc(eldsz, KM_SLEEP);
+ eld = (audiohd_eld_t *)buf;
+
+ /*
+ * The device will return 0 for any offset that is beyond the length of
+ * ELD, so we don't have to check whether the size returned by DIP SIZE
+ * is less than sizeof (audiohd_eld_t).
+ */
+ for (i = 0; i != eldsz; i++)
+ buf[i] = audioha_codec_verb_get(statep, index, id,
+ AUDIOHDC_VERB_GET_ELD, i);
+
+ if (eld->eld_ver != AUDIOHD_ELD_VER_2 &&
+ eld->eld_ver != AUDIOHD_ELD_VER_NODRV) {
+ kmem_free(buf, eldsz);
+ return (NULL);
+ }
+
+ eld->eld_size = eldsz;
+
+ /* move the CEA SADs to eld_cea_sad */
+ sad_off = offsetof(audiohd_eld_t, eld_monitor_name) + eld->eld_mnl;
+ bcopy(buf + sad_off, (void *)eld->eld_cea_sad,
+ sizeof (audiohd_sad_t) * eld->eld_sad_count);
+ bzero(buf + sad_off, offsetof(audiohd_eld_t, eld_cea_sad) - sad_off);
+
+ return (eld);
+ }
+
+ /*
* audiohd_pin_sense()
*
* Description
*
* When the earphone is plugged into the jack associtated with the pin
* complex, we disable the built in speaker. When the earphone is plugged
* out of the jack, we enable the built in speaker.
+ *
+ * When a digital pin complex reports valid EDID-like data (ELD), update
+ * it.
*/
static void
audiohd_pin_sense(audiohd_state_t *statep, uint32_t resp, uint32_t respex)
{
+ audiohd_pin_t *pin = NULL;
uint8_t index;
uint8_t id;
uint32_t rs;
audiohd_widget_t *widget;
hda_codec_t *codec;
index = respex & AUDIOHD_RIRB_CODEC_MASK;
id = resp >> (AUDIOHD_RIRB_WID_OFF - 1);
*** 5646,5662 ****
return;
widget = codec->widget[id];
if (widget == NULL)
return;
rs = audioha_codec_verb_get(statep, index, id,
AUDIOHDC_VERB_GET_PIN_SENSE, 0);
! if (rs & AUDIOHD_PIN_PRES_MASK) {
/* A MIC is plugged in, we select the MIC as input */
! if ((widget->type == WTYPE_PIN) &&
! (pin = (audiohd_pin_t *)widget->priv) &&
! (pin->device == DTYPE_MIC_IN)) {
audiohd_select_mic(statep, index, id, 1);
return;
}
/* output pin is plugged */
audiohd_change_speaker_state(statep, AUDIOHD_SP_OFF);
--- 5794,5820 ----
return;
widget = codec->widget[id];
if (widget == NULL)
return;
+ if (widget->type == WTYPE_PIN)
+ pin = (audiohd_pin_t *)widget->priv;
+
rs = audioha_codec_verb_get(statep, index, id,
AUDIOHDC_VERB_GET_PIN_SENSE, 0);
!
! if ((widget->widget_cap & AUDIOHD_WIDCAP_DIGIT) != 0 &&
! (rs & AUDIOHD_PIN_ELDV_MASK) != 0) {
! /* updated EDID-like data */
! if (pin != NULL) {
! if (pin->eld != NULL)
! kmem_free(pin->eld, pin->eld->eld_size);
! pin->eld = audiohd_get_eld(statep, index, id);
! }
! } else if (rs & AUDIOHD_PIN_PRES_MASK) {
/* A MIC is plugged in, we select the MIC as input */
! if (pin != NULL && pin->device == DTYPE_MIC_IN) {
audiohd_select_mic(statep, index, id, 1);
return;
}
/* output pin is plugged */
audiohd_change_speaker_state(statep, AUDIOHD_SP_OFF);