diff --git a/src/add-ons/kernel/drivers/audio/hda/driver.h b/src/add-ons/kernel/drivers/audio/hda/driver.h index 3924b75698..e197e4c42f 100644 --- a/src/add-ons/kernel/drivers/audio/hda/driver.h +++ b/src/add-ons/kernel/drivers/audio/hda/driver.h @@ -38,6 +38,7 @@ #define HDA_MAX_CODECS 15 #define HDA_MAX_STREAMS 16 #define MAX_CODEC_RESPONSES 16 +#define MAX_CODEC_UNSOL_RESPONSES 16 #define MAX_INPUTS 32 #define MAX_IO_WIDGETS 8 #define MAX_ASSOCIATIONS 16 @@ -46,6 +47,7 @@ #define STREAM_MAX_BUFFERS 10 #define STREAM_MIN_BUFFERS 2 + enum { STREAM_PLAYBACK, STREAM_RECORD @@ -272,11 +274,16 @@ struct hda_codec { uint8 revision; uint8 stepping; uint8 addr; + + uint32 quirks; sem_id response_sem; uint32 responses[MAX_CODEC_RESPONSES]; uint32 response_count; - + + uint32 unsol_responses[MAX_CODEC_UNSOL_RESPONSES]; + uint32 unsol_response_count; + hda_audio_group* audio_groups[HDA_MAX_AUDIO_GROUPS]; uint32 num_audio_groups; diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp index d185ff9542..e27b32843d 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp @@ -12,6 +12,42 @@ #include "driver.h" #include "hda_codec_defs.h" +#undef TRACE +#define TRACE_CODEC +#ifdef TRACE_CODEC +#define TRACE(a...) dprintf(a) +#else +#define TRACE(a...) +#endif +#define ERROR(a...) dprintf(a) + +#define HDA_ALL 0xffffffff +#define HDA_QUIRK_GPIO0 (1 << 0) +#define HDA_QUIRK_GPIO1 (1 << 1) +#define HDA_QUIRK_GPIO2 (1 << 2) +#define HDA_QUIRK_GPIO3 (1 << 3) +#define HDA_QUIRK_GPIO4 (1 << 4) +#define HDA_QUIRK_GPIO5 (1 << 5) +#define HDA_QUIRK_GPIO6 (1 << 6) +#define HDA_QUIRK_GPIO7 (1 << 7) +#define HDA_QUIRK_IVREF50 (1 << 8) +#define HDA_QUIRK_IVREF80 (1 << 9) +#define HDA_QUIRK_IVREF100 (1 << 10) +#define HDA_QUIRK_OVREF50 (1 << 11) +#define HDA_QUIRK_OVREF80 (1 << 12) +#define HDA_QUIRK_OVREF100 (1 << 13) +#define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 \ + | HDA_QUIRK_IVREF100) +#define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 \ + | HDA_QUIRK_OVREF100) + + +#define ANALOGDEVICES_VENDORID 0x11d4 +#define CIRRUSLOGIC_VENDORID 0x1013 +#define CONEXANT_VENDORID 0x14f1 +#define REALTEK_VENDORID 0x10ec +#define SIGMATEL_VENDORID 0x8384 + static const char* kPortConnector[] = { "Jack", "None", "Fixed", "Dual" @@ -93,7 +129,7 @@ dump_widget_audio_capabilities(uint32 capabilities) } if (offset != 0) - dprintf("\t%s\n", buffer); + TRACE("\t%s\n", buffer); } @@ -115,7 +151,7 @@ dump_widget_inputs(hda_widget& widget) } if (offset != 0) - dprintf("\tInputs: %s\n", buffer); + TRACE("\tInputs: %s\n", buffer); } @@ -131,7 +167,7 @@ dump_widget_amplifier_capabilities(hda_widget& widget, bool input) if (capabilities == 0) return; - dprintf("\t%s Amp: %sstep size: %f dB, # steps: %ld, offset to 0 dB: " + TRACE("\t%s Amp: %sstep size: %f dB, # steps: %ld, offset to 0 dB: " "%ld\n", input ? "In" : "Out", (capabilities & AMP_CAP_MUTE) != 0 ? "supports mute, " : "", AMP_CAP_STEP_SIZE(capabilities), @@ -143,7 +179,7 @@ dump_widget_amplifier_capabilities(hda_widget& widget, bool input) static void dump_widget_pm_support(hda_widget& widget) { - dprintf("\tSupported power states: %s%s%s%s\n", + TRACE("\tSupported power states: %s%s%s%s\n", widget.pm & POWER_STATE_D0 ? "D0 " : "", widget.pm & POWER_STATE_D1 ? "D1 " : "", widget.pm & POWER_STATE_D2 ? "D2 " : "", @@ -154,7 +190,7 @@ dump_widget_pm_support(hda_widget& widget) static void dump_widget_stream_support(hda_widget& widget) { - dprintf("\tSupported formats: %s%s%s%s%s%s%s%s%s\n", + TRACE("\tSupported formats: %s%s%s%s%s%s%s%s%s\n", widget.d.io.formats & B_FMT_8BIT_S ? "8bits " : "", widget.d.io.formats & B_FMT_16BIT ? "16bits " : "", widget.d.io.formats & B_FMT_20BIT ? "20bits " : "", @@ -164,7 +200,7 @@ dump_widget_stream_support(hda_widget& widget) widget.d.io.formats & B_FMT_DOUBLE ? "double " : "", widget.d.io.formats & B_FMT_EXTENDED ? "extended " : "", widget.d.io.formats & B_FMT_BITSTREAM ? "bitstream " : ""); - dprintf("\tSupported rates: %s%s%s%s%s%s%s%s%s%s%s%s\n", + TRACE("\tSupported rates: %s%s%s%s%s%s%s%s%s%s%s%s\n", widget.d.io.rates & B_SR_8000 ? "8khz " : "", widget.d.io.rates & B_SR_11025 ? "11khz " : "", widget.d.io.rates & B_SR_16000 ? "16khz " : "", @@ -181,16 +217,36 @@ dump_widget_stream_support(hda_widget& widget) } +static void +dump_pin_complex_capabilities(hda_widget& widget) +{ + TRACE("\t%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + widget.d.pin.capabilities & PIN_CAP_IMP_SENSE ? "[Imp Sense] " : "", + widget.d.pin.capabilities & PIN_CAP_TRIGGER_REQ ? "[Trigger Req]" : "", + widget.d.pin.capabilities & PIN_CAP_PRES_DETECT ? "[Pres Detect]" : "", + widget.d.pin.capabilities & PIN_CAP_HP_DRIVE ? "[HP Drive]" : "", + widget.d.pin.capabilities & PIN_CAP_OUTPUT ? "[Output]" : "", + widget.d.pin.capabilities & PIN_CAP_INPUT ? "[Input]" : "", + widget.d.pin.capabilities & PIN_CAP_BALANCE ? "[Balance]" : "", + widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_HIZ ? "[VRef HIZ]" : "", + widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_50 ? "[VRef 50]" : "", + widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_GROUND ? "[VRef Gr]" : "", + widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_80 ? "[VRef 80]" : "", + widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_100 ? "[VRef 100]" : "", + widget.d.pin.capabilities & PIN_CAP_EAPD_CAP ? "[EAPD]" : ""); +} + + static void dump_audiogroup_widgets(hda_audio_group* audioGroup) { - dprintf("\tAudiogroup:\n"); + TRACE("\tAudiogroup:\n"); /* Iterate over all Widgets and collect info */ for (uint32 i = 0; i < audioGroup->widget_count; i++) { hda_widget& widget = audioGroup->widgets[i]; uint32 nodeID = audioGroup->widget_start + i; - dprintf("%ld: %s\n", nodeID, get_widget_type_name(widget.type)); + TRACE("%ld: %s\n", nodeID, get_widget_type_name(widget.type)); switch (widget.type) { case WT_AUDIO_OUTPUT: @@ -198,10 +254,7 @@ dump_audiogroup_widgets(hda_audio_group* audioGroup) break; case WT_PIN_COMPLEX: - dprintf("\t%s%s%s\n", PIN_CAP_IS_INPUT(widget.d.pin.capabilities) ? "[Input] " : "", - PIN_CAP_IS_OUTPUT(widget.d.pin.capabilities) ? "[Output]" : "", - PIN_CAP_IS_EAPD_CAP(widget.d.pin.capabilities) ? "[EAPD Cap]" : ""); - + dump_pin_complex_capabilities(widget); break; default: @@ -393,7 +446,7 @@ hda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID) static status_t hda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget) { - uint32 verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id, + corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id, VID_GET_PARAMETER, PID_CONNECTION_LIST_LENGTH); uint32 response; @@ -430,7 +483,7 @@ hda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget) VID_GET_CONNECTION_LIST_ENTRY, i); if (hda_send_verbs(audioGroup->codec, &verb, &response, 1) != B_OK) { - dprintf("hda: Error parsing inputs for widget %ld!\n", + ERROR("hda: Error parsing inputs for widget %ld!\n", widget->node_id); break; } @@ -444,7 +497,7 @@ hda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget) input &= ~rangeMask; if (input < previousInput || previousInput == -1) { - dprintf("hda: invalid range from %ld to %ld\n", previousInput, + ERROR("hda: invalid range from %ld to %ld\n", previousInput, input); continue; } @@ -505,6 +558,39 @@ hda_widget_get_associations(hda_audio_group* audioGroup) } +static uint32 +hda_widget_prepare_pin_ctrl(hda_audio_group* audioGroup, hda_widget *widget, + bool isOutput) +{ + uint32 ctrl = 0; + if (isOutput) + ctrl = PIN_ENABLE_HEAD_PHONE | PIN_ENABLE_OUTPUT; + else + ctrl = PIN_ENABLE_INPUT; + + if (PIN_CAP_IS_VREF_CTRL_50_CAP(widget->d.pin.capabilities) + && (audioGroup->codec->quirks & (isOutput ? HDA_QUIRK_OVREF50 + : HDA_QUIRK_IVREF50))) { + ctrl |= PIN_ENABLE_VREF_50; + TRACE("%s vref 50 enabled\n", isOutput ? "output" : "input"); + } + if (PIN_CAP_IS_VREF_CTRL_80_CAP(widget->d.pin.capabilities) + && (audioGroup->codec->quirks & (isOutput ? HDA_QUIRK_OVREF80 + : HDA_QUIRK_IVREF80))) { + ctrl |= PIN_ENABLE_VREF_80; + TRACE("%s vref 80 enabled\n", isOutput ? "output" : "input"); + } + if (PIN_CAP_IS_VREF_CTRL_100_CAP(widget->d.pin.capabilities) + && (audioGroup->codec->quirks & (isOutput ? HDA_QUIRK_OVREF100 + : HDA_QUIRK_IVREF100))) { + ctrl |= PIN_ENABLE_VREF_100; + TRACE("%s vref 100 enabled\n", isOutput ? "output" : "input"); + } + + return ctrl; +} + + // #pragma mark - audio group functions @@ -528,12 +614,12 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) if (hda_send_verbs(audioGroup->codec, verbs, resp, 3) != B_OK) return B_ERROR; - dprintf("hda: Audio Group: Output delay: %ld samples, Input delay: %ld " + TRACE("hda: Audio Group: Output delay: %ld samples, Input delay: %ld " "samples, Beep Generator: %s\n", AUDIO_GROUP_CAP_OUTPUT_DELAY(resp[0]), AUDIO_GROUP_CAP_INPUT_DELAY(resp[0]), AUDIO_GROUP_CAP_BEEPGEN(resp[0]) ? "yes" : "no"); - dprintf("hda: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n", + TRACE("hda: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n", GPIO_COUNT_NUM_GPIO(resp[1]), GPIO_COUNT_NUM_GPO(resp[1]), GPIO_COUNT_NUM_GPI(resp[1]), GPIO_COUNT_GPIUNSOL(resp[1]) ? "yes" : "no", GPIO_COUNT_GPIWAKE(resp[1]) ? "yes" : "no"); @@ -542,13 +628,13 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) audioGroup->widget_start = SUB_NODE_COUNT_START(resp[2]); audioGroup->widget_count = SUB_NODE_COUNT_TOTAL(resp[2]); - dprintf("hda: widget start %lu, count %lu\n", audioGroup->widget_start, + TRACE("hda: widget start %lu, count %lu\n", audioGroup->widget_start, audioGroup->widget_count); audioGroup->widgets = (hda_widget*)calloc(audioGroup->widget_count, sizeof(*audioGroup->widgets)); if (audioGroup->widgets == NULL) { - dprintf("ERROR: Not enough memory!\n"); + ERROR("ERROR: Not enough memory!\n"); return B_NO_MEMORY; } @@ -613,7 +699,7 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) hda_widget_get_amplifier_capabilities(audioGroup, &widget); } - dprintf("%ld: %s\n", nodeID, get_widget_type_name(widget.type)); + TRACE("%ld: %s\n", nodeID, get_widget_type_name(widget.type)); switch (widget.type) { case WT_AUDIO_OUTPUT: @@ -628,17 +714,17 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) { widget.d.pin.capabilities = resp[0]; - dprintf("\t%s%s\n", PIN_CAP_IS_INPUT(resp[0]) ? "[Input] " : "", + TRACE("\t%s%s\n", PIN_CAP_IS_INPUT(resp[0]) ? "[Input] " : "", PIN_CAP_IS_OUTPUT(resp[0]) ? "[Output]" : ""); } else { - dprintf("%s: Error getting Pin Complex IO\n", __func__); + ERROR("%s: Error getting Pin Complex IO\n", __func__); } verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID, VID_GET_CONFIGURATION_DEFAULT, 0); if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) { widget.d.pin.config = resp[0]; - dprintf("\t%s, %s, %s, %s, Association:%ld\n", + TRACE("\t%s, %s, %s, %s, Association:%ld\n", kPortConnector[CONF_DEFAULT_CONNECTIVITY(resp[0])], kDefaultDevice[CONF_DEFAULT_DEVICE(resp[0])], kConnectionType[CONF_DEFAULT_CONNTYPE(resp[0])], @@ -667,7 +753,7 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) } hda_widget_get_associations(audioGroup); - + return B_OK; } @@ -683,7 +769,7 @@ hda_widget_find_output_path(hda_audio_group* audioGroup, hda_widget* widget, switch (widget->type) { case WT_AUDIO_OUTPUT: widget->flags |= WIDGET_FLAG_OUTPUT_PATH; -dprintf(" %*soutput: added output widget %ld\n", (int)depth * 2, "", widget->node_id); +TRACE(" %*soutput: added output widget %ld\n", (int)depth * 2, "", widget->node_id); return true; case WT_AUDIO_MIXER: @@ -705,11 +791,11 @@ dprintf(" %*soutput: added output widget %ld\n", (int)depth * 2, "", widget widget->active_input = i; widget->flags |= WIDGET_FLAG_OUTPUT_PATH; -dprintf(" %*soutput: added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); +TRACE(" %*soutput: added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); found = true; } } -if (!found) dprintf(" %*soutput: not added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); +if (!found) TRACE(" %*soutput: not added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); return found; } @@ -739,7 +825,7 @@ hda_widget_find_input_path(hda_audio_group* audioGroup, hda_widget* widget, case PIN_DEV_LINE_IN: case PIN_DEV_MIC_IN: widget->flags |= WIDGET_FLAG_INPUT_PATH; -dprintf(" %*sinput: added input widget %ld\n", (int)depth * 2, "", widget->node_id); +TRACE(" %*sinput: added input widget %ld\n", (int)depth * 2, "", widget->node_id); return true; break; } @@ -765,11 +851,11 @@ dprintf(" %*sinput: added input widget %ld\n", (int)depth * 2, "", widget-> widget->active_input = i; widget->flags |= WIDGET_FLAG_INPUT_PATH; -dprintf(" %*sinput: added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); +TRACE(" %*sinput: added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); found = true; } } -if (!found) dprintf(" %*sinput: not added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); +if (!found) TRACE(" %*sinput: not added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); return found; } @@ -783,7 +869,7 @@ hda_audio_group_build_output_tree(hda_audio_group* audioGroup, bool useMixer) { bool found = false; -dprintf("build output tree: %suse mixer\n", useMixer ? "" : "don't "); +TRACE("build output tree: %suse mixer\n", useMixer ? "" : "don't "); for (uint32 i = 0; i < audioGroup->widget_count; i++) { hda_widget& widget = audioGroup->widgets[i]; @@ -796,21 +882,21 @@ dprintf("build output tree: %suse mixer\n", useMixer ? "" : "don't "); && device != PIN_DEV_LINE_OUT) continue; -dprintf(" look at pin widget %ld (%ld inputs)\n", widget.node_id, widget.num_inputs); +TRACE(" look at pin widget %ld (%ld inputs)\n", widget.node_id, widget.num_inputs); for (uint32 j = 0; j < widget.num_inputs; j++) { hda_widget* inputWidget = hda_audio_group_get_widget(audioGroup, widget.inputs[j]); -dprintf(" try widget %ld: %p\n", widget.inputs[j], inputWidget); +TRACE(" try widget %ld: %p\n", widget.inputs[j], inputWidget); if (inputWidget == NULL) continue; if (useMixer && inputWidget->type != WT_AUDIO_MIXER && inputWidget->type != WT_AUDIO_SELECTOR) continue; -dprintf(" widget %ld is candidate\n", inputWidget->node_id); +TRACE(" widget %ld is candidate\n", inputWidget->node_id); if (hda_widget_find_output_path(audioGroup, inputWidget, 0)) { -dprintf(" add pin widget %ld\n", widget.node_id); +TRACE(" add pin widget %ld\n", widget.node_id); if (widget.active_input == -1) widget.active_input = j; widget.flags |= WIDGET_FLAG_OUTPUT_PATH; @@ -829,25 +915,25 @@ hda_audio_group_build_input_tree(hda_audio_group* audioGroup) { bool found = false; -dprintf("build input tree\n"); +TRACE("build input tree\n"); for (uint32 i = 0; i < audioGroup->widget_count; i++) { hda_widget& widget = audioGroup->widgets[i]; if (widget.type != WT_AUDIO_INPUT) continue; -dprintf(" look at input widget %ld (%ld inputs)\n", widget.node_id, widget.num_inputs); +TRACE(" look at input widget %ld (%ld inputs)\n", widget.node_id, widget.num_inputs); for (uint32 j = 0; j < widget.num_inputs; j++) { hda_widget* inputWidget = hda_audio_group_get_widget(audioGroup, widget.inputs[j]); -dprintf(" try widget %ld: %p\n", widget.inputs[j], inputWidget); +TRACE(" try widget %ld: %p\n", widget.inputs[j], inputWidget); if (inputWidget == NULL) continue; -dprintf(" widget %ld is candidate\n", inputWidget->node_id); +TRACE(" widget %ld is candidate\n", inputWidget->node_id); if (hda_widget_find_input_path(audioGroup, inputWidget, 0)) { -dprintf(" add pin widget %ld\n", widget.node_id); +TRACE(" add pin widget %ld\n", widget.node_id); if (widget.active_input == -1) widget.active_input = j; widget.flags |= WIDGET_FLAG_INPUT_PATH; @@ -866,16 +952,16 @@ hda_audio_group_build_tree(hda_audio_group* audioGroup) { if (!hda_audio_group_build_output_tree(audioGroup, true)) { // didn't find a mixer path, try again without -dprintf("try without mixer!\n"); +TRACE("try without mixer!\n"); if (!hda_audio_group_build_output_tree(audioGroup, false)) return ENODEV; } if (!hda_audio_group_build_input_tree(audioGroup)) { - dprintf("hda: build input tree failed\n"); + ERROR("hda: build input tree failed\n"); } -dprintf("build tree!\n"); +TRACE("build tree!\n"); // select active connections @@ -895,9 +981,31 @@ dprintf("build tree!\n"); corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget.node_id, VID_SET_CONNECTION_SELECT, widget.active_input); if (hda_send_verbs(audioGroup->codec, &verb, NULL, 1) != B_OK) - dprintf("hda: Setting output selector %ld failed on widget %ld!\n", + ERROR("hda: Setting output selector %ld failed on widget %ld!\n", widget.active_input, widget.node_id); } + + // GPIO + uint32 gpio = 0; + for (int32 i = 0; i < 8; i++) { + if (audioGroup->codec->quirks & (1 << i)) { + gpio |= (1 << i); + } + } + + if (gpio != 0) { + corb_t verb[] = { + MAKE_VERB(audioGroup->codec->addr, + audioGroup->widget.node_id, VID_SET_GPIODATA, gpio), + MAKE_VERB(audioGroup->codec->addr, + audioGroup->widget.node_id, VID_SET_GPIO_EN, gpio), + MAKE_VERB(audioGroup->codec->addr, + audioGroup->widget.node_id, VID_SET_GPIO_DIR, gpio) + }; + TRACE("hda: Setting gpio 0x%lx\n", gpio); + if (hda_send_verbs(audioGroup->codec, verb, NULL, 3) != B_OK) + ERROR("hda: Setting gpio failed!\n"); + } dump_audiogroup_widgets(audioGroup); @@ -905,6 +1013,27 @@ dprintf("build tree!\n"); } +static void +hda_codec_switch_init(hda_audio_group* audioGroup) +{ + for (uint32 i = 0; i < audioGroup->widget_count; i++) { + hda_widget& widget = audioGroup->widgets[i]; + if (widget.type != WT_PIN_COMPLEX) + continue; + + if (widget.capabilities.audio & AUDIO_CAP_UNSOLICITED_RESPONSES + && widget.d.pin.capabilities & PIN_CAP_PRES_DETECT + && (CONF_DEFAULT_MISC(widget.d.pin.config) & 1 == 0)) { + corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget.node_id, + VID_SET_UNSOLRESP, UNSOLRESP_ENABLE); + hda_send_verbs(audioGroup->codec, &verb, NULL, 1); + TRACE("hda: Enabled unsolicited responses on widget %ld\n", + widget.node_id); + } + } +} + + static void hda_codec_delete_audio_group(hda_audio_group* audioGroup) { @@ -949,10 +1078,11 @@ hda_codec_new_audio_group(hda_codec* codec, uint32 audioGroupNodeID) if (hda_audio_group_build_tree(audioGroup) != B_OK) goto err; - + hda_codec_switch_init(audioGroup); + audioGroup->playback_stream = hda_stream_new(audioGroup, STREAM_PLAYBACK); audioGroup->record_stream = hda_stream_new(audioGroup, STREAM_RECORD); - dprintf("hda: streams playback %p, record %p\n", audioGroup->playback_stream, + TRACE("hda: streams playback %p, record %p\n", audioGroup->playback_stream, audioGroup->record_stream); if (audioGroup->playback_stream != NULL @@ -995,15 +1125,15 @@ hda_audio_group_get_widgets(hda_audio_group* audioGroup, hda_stream* stream) if ((widget.flags & flags) != 0) { if (widget.type == WT_PIN_COMPLEX) { stream->pin_widget = widget.node_id; + + uint32 ctrl = hda_widget_prepare_pin_ctrl(audioGroup, &widget, + flags == WIDGET_FLAG_OUTPUT_PATH); -dprintf("ENABLE pin widget %ld\n", widget.node_id); +TRACE("ENABLE pin widget %ld\n", widget.node_id); /* FIXME: Force Pin Widget to unmute; enable hp/output */ - uint32 verb = MAKE_VERB(audioGroup->codec->addr, + corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget.node_id, - VID_SET_PIN_WIDGET_CONTROL, - flags == WIDGET_FLAG_OUTPUT_PATH ? - PIN_ENABLE_HEAD_PHONE | PIN_ENABLE_OUTPUT - : PIN_ENABLE_INPUT); + VID_SET_PIN_WIDGET_CONTROL, ctrl); hda_send_verbs(audioGroup->codec, &verb, NULL, 1); if (PIN_CAP_IS_EAPD_CAP(widget.d.pin.capabilities)) { @@ -1018,15 +1148,15 @@ dprintf("ENABLE pin widget %ld\n", widget.node_id); result | EAPDBTL_ENABLE_EAPD); hda_send_verbs(audioGroup->codec, &verb, NULL, 1); -dprintf("ENABLE EAPD pin widget %ld\n", widget.node_id); +TRACE("ENABLE EAPD pin widget %ld\n", widget.node_id); } } } if (widget.capabilities.output_amplifier != 0) { -dprintf("UNMUTE/SET OUTPUT GAIN widget %ld (offset %ld)\n", widget.node_id, +TRACE("UNMUTE/SET OUTPUT GAIN widget %ld (offset %ld)\n", widget.node_id, AMP_CAP_OFFSET(widget.capabilities.output_amplifier)); - uint32 verb = MAKE_VERB(audioGroup->codec->addr, + corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget.node_id, VID_SET_AMPLIFIER_GAIN_MUTE, AMP_SET_OUTPUT | AMP_SET_LEFT_CHANNEL @@ -1035,10 +1165,10 @@ dprintf("UNMUTE/SET OUTPUT GAIN widget %ld (offset %ld)\n", widget.node_id, hda_send_verbs(audioGroup->codec, &verb, NULL, 1); } if (widget.capabilities.input_amplifier != 0) { -dprintf("UNMUTE/SET INPUT GAIN widget %ld (offset %ld)\n", widget.node_id, +TRACE("UNMUTE/SET INPUT GAIN widget %ld (offset %ld)\n", widget.node_id, AMP_CAP_OFFSET(widget.capabilities.input_amplifier)); for (uint32 i = 0; i < widget.num_inputs; i++) { - uint32 verb = MAKE_VERB(audioGroup->codec->addr, + corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget.node_id, VID_SET_AMPLIFIER_GAIN_MUTE, AMP_SET_INPUT | AMP_SET_LEFT_CHANNEL @@ -1076,6 +1206,47 @@ dprintf("UNMUTE/SET INPUT GAIN widget %ld (offset %ld)\n", widget.node_id, } +static const struct { + uint32 subsystem_vendor_id, subsystem_id; + uint32 codec_vendor_id, codec_id; + uint32 quirks, nonquirks; +} hda_codec_quirks[] = { + { HDA_ALL, HDA_ALL, HDA_ALL, HDA_ALL, HDA_QUIRK_IVREF, 0 }, + { HDA_ALL, HDA_ALL, HDA_ALL, HDA_ALL, HDA_QUIRK_IVREF, 0 }, + { 0x10de, 0xcb79, CIRRUSLOGIC_VENDORID, 0x4206, HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 }, // MacBook Pro 5.5 + { 0x8384, 0x7680, SIGMATEL_VENDORID, 0x7680, HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0}, +}; + + +void +hda_codec_get_quirks(hda_codec* codec) +{ + codec->quirks = 0; + uint32 subsystem_id = codec->controller->pci_info.u.h0.subsystem_id; + uint32 subsystem_vendor_id = codec->controller->pci_info.u.h0.subsystem_vendor_id; + uint32 codec_vendor_id = codec->vendor_id; + uint32 codec_id = codec->product_id; + for (uint32 i = 0; i < (sizeof(hda_codec_quirks) + / sizeof(hda_codec_quirks[0])); i++) { + if (hda_codec_quirks[i].subsystem_id != 0xffffffff + && hda_codec_quirks[i].subsystem_id != subsystem_id) + continue; + if (hda_codec_quirks[i].subsystem_vendor_id != 0xffffffff + && hda_codec_quirks[i].subsystem_vendor_id != subsystem_vendor_id) + continue; + if (hda_codec_quirks[i].codec_vendor_id != 0xffffffff + && hda_codec_quirks[i].codec_vendor_id != codec_vendor_id) + continue; + if (hda_codec_quirks[i].codec_id != 0xffffffff + && hda_codec_quirks[i].codec_id != codec_id) + continue; + + codec->quirks |= hda_codec_quirks[i].quirks; + codec->quirks &= ~(hda_codec_quirks[i].nonquirks); + } +} + + void hda_codec_delete(hda_codec* codec) { @@ -1101,13 +1272,14 @@ hda_codec_new(hda_controller* controller, uint32 codecAddress) hda_codec* codec = (hda_codec*)calloc(1, sizeof(hda_codec)); if (codec == NULL) { - dprintf("hda: Failed to alloc a codec\n"); + ERROR("hda: Failed to alloc a codec\n"); return NULL; } codec->controller = controller; codec->addr = codecAddress; codec->response_sem = create_sem(0, "hda_codec_response_sem"); + codec->unsol_response_count = 0; controller->codecs[codecAddress] = codec; struct { @@ -1131,7 +1303,7 @@ hda_codec_new(hda_controller* controller, uint32 codecAddress) PID_SUB_NODE_COUNT); if (hda_send_verbs(codec, verbs, (uint32*)&response, 3) != B_OK) { - dprintf("hda: Failed to get vendor and revision parameters\n"); + ERROR("hda: Failed to get vendor and revision parameters\n"); goto err; } @@ -1141,8 +1313,9 @@ hda_codec_new(hda_controller* controller, uint32 codecAddress) codec->revision = response.revision; codec->minor = response.minor; codec->major = response.major; + hda_codec_get_quirks(codec); - dprintf("Codec %ld Vendor: %04lx Product: %04lx, Revision: " + TRACE("Codec %ld Vendor: %04lx Product: %04lx, Revision: " "%lu.%lu.%lu.%lu\n", codecAddress, response.vendor, response.device, response.major, response.minor, response.revision, response.stepping); @@ -1153,7 +1326,7 @@ hda_codec_new(hda_controller* controller, uint32 codecAddress) PID_FUNCTION_GROUP_TYPE); if (hda_send_verbs(codec, verbs, &groupType, 1) != B_OK) { - dprintf("hda: Failed to get function group type\n"); + ERROR("hda: Failed to get function group type\n"); goto err; } @@ -1161,7 +1334,7 @@ hda_codec_new(hda_controller* controller, uint32 codecAddress) /* Found an Audio Function Group! */ status_t status = hda_codec_new_audio_group(codec, nodeID); if (status != B_OK) { - dprintf("hda: Failed to setup new audio function group (%s)!\n", + ERROR("hda: Failed to setup new audio function group (%s)!\n", strerror(status)); goto err; } diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h b/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h index 139f4b7deb..89c90685f6 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h @@ -226,8 +226,12 @@ enum pin_dev_type { #define PIN_CAP_VREF_CTRL_100 (1L << 13) #define PIN_CAP_EAPD_CAP (1L << 16) +#define PIN_CAP_IS_PRES_DETECT_CAP(c) ((c & PIN_CAP_PRES_DETECT) != 0) #define PIN_CAP_IS_OUTPUT(c) ((c & PIN_CAP_OUTPUT) != 0) #define PIN_CAP_IS_INPUT(c) ((c & PIN_CAP_INPUT) != 0) +#define PIN_CAP_IS_VREF_CTRL_50_CAP(c) ((c & PIN_CAP_VREF_CTRL_50) != 0) +#define PIN_CAP_IS_VREF_CTRL_80_CAP(c) ((c & PIN_CAP_VREF_CTRL_80) != 0) +#define PIN_CAP_IS_VREF_CTRL_100_CAP(c) ((c & PIN_CAP_VREF_CTRL_100) != 0) #define PIN_CAP_IS_EAPD_CAP(c) ((c & PIN_CAP_EAPD_CAP) != 0) /* PCM support */ @@ -268,7 +272,14 @@ enum pin_dev_type { #define PIN_ENABLE_HEAD_PHONE (1L << 7) #define PIN_ENABLE_OUTPUT (1L << 6) #define PIN_ENABLE_INPUT (1L << 5) -#define PIN_ENABLE_VOLTAGE_REF_MASK 0x3 +#define PIN_ENABLE_VREF_HIZ 0 +#define PIN_ENABLE_VREF_50 1 +#define PIN_ENABLE_VREF_GROUND 2 +#define PIN_ENABLE_VREF_80 4 +#define PIN_ENABLE_VREF_100 5 + +/* Unsolicited Response */ +#define UNSOLRESP_ENABLE (1L << 7) /* Supported power states */ #define POWER_STATE_D0 (1L << 0) diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp index 0edd303991..cc2ff4d169 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp @@ -162,16 +162,25 @@ hda_interrupt_handler(hda_controller* controller) uint32 cad = responseFlags & RESPONSE_FLAGS_CODEC_MASK; hda_codec* codec = controller->codecs[cad]; - if ((responseFlags & RESPONSE_FLAGS_UNSOLICITED) != 0) { - dprintf("hda: Unsolicited response: %08lx/%08lx\n", - response, responseFlags); - continue; - } if (codec == NULL) { dprintf("hda: Response for unknown codec %ld: " "%08lx/%08lx\n", cad, response, responseFlags); continue; } + + if ((responseFlags & RESPONSE_FLAGS_UNSOLICITED) != 0) { + dprintf("hda: Unsolicited response: %08lx/%08lx\n", + response, responseFlags); + if (codec->unsol_response_count >= MAX_CODEC_UNSOL_RESPONSES) { + dprintf("hda: too many unsol responses received" + " for codec %ld: %08lx/%08lx!\n", + cad, response, responseFlags); + continue; + } + codec->unsol_responses[codec->unsol_response_count++] = + response; + continue; + } if (codec->response_count >= MAX_CODEC_RESPONSES) { dprintf("hda: too many responses received for codec %ld" ": %08lx/%08lx!\n", cad, response, responseFlags); @@ -287,6 +296,11 @@ reset_controller(hda_controller* controller) // Wait for codecs to finish their own reset (apparently needs more // time than documented in the specs) snooze(1000); + + // Enable unsolicited responses + control = controller->Read32(HDAC_GLOBAL_CONTROL); + controller->Write32(HDAC_GLOBAL_CONTROL, control | GLOBAL_CONTROL_UNSOLICITED); + return B_OK; }