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);