From 313492b63f752d4639877b668b29b57569429634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 11 Mar 2008 15:51:48 +0000 Subject: [PATCH] * Moved connection retrieval code into its own function. * Extended it to support long format as well. * Extended it to support ranges of widgets (I didn't find those in the standard, but the FreeBSD and Linux drivers implement them, and my laptop even makes use of them, too). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24357 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/drivers/audio/hda/hda_codec.cpp | 133 ++++++++++++------ 1 file changed, 92 insertions(+), 41 deletions(-) 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 c5f2cf0284..4497dc07f3 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp @@ -143,6 +143,81 @@ hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nodeID) } +static status_t +hda_widget_get_connections(hda_codec* codec, uint32 nodeID, hda_widget* widget) +{ + uint32 verb = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAMETER, + PID_CONNECTION_LIST_LENGTH); + uint32 response; + + if (hda_send_verbs(codec, &verb, &response, 1) != B_OK) + return B_ERROR; + + uint32 listEntries = response & 0x7f; + bool longForm = (response & 0xf0) != 0; + + if (listEntries == 0) + return B_OK; + +#if 1 + if (widget->num_inputs > 1) { + // Get currently active connection + verb = MAKE_VERB(codec->addr, nodeID, VID_GET_CONNECTION_SELECT, 0); + if (hda_send_verbs(codec, &verb, &response, 1) == B_OK) + widget->active_input = response & 0xff; + } +#endif + + uint32 valuesPerEntry = longForm ? 2 : 4; + uint32 shift = 32 / valuesPerEntry; + uint32 rangeMask = (1 << (shift - 1)); + int32 previousInput = -1; + uint32 numInputs = 0; + + for (uint32 i = 0; i < listEntries; i++) { + if ((i % valuesPerEntry) == 0) { + // We get 2 or 4 answers per call depending on if we're + // in short or long list mode + verb = MAKE_VERB(codec->addr, nodeID, + VID_GET_CONNECTION_LIST_ENTRY, i); + if (hda_send_verbs(codec, &verb, &response, 1) != B_OK) { + dprintf("hda: Error parsing inputs for widget %ld!\n", + nodeID); + break; + } + } + + int32 input = (response >> (shift * (i % valuesPerEntry))) + & ((1 << shift) - 1); + + if (input & rangeMask) { + // found range + input &= ~rangeMask; + + if (input < previousInput || previousInput == -1) { + dprintf("hda: invalid range from %ld to %ld\n", previousInput, + input); + continue; + } + + for (int32 rangeInput = previousInput + 1; rangeInput <= input + && numInputs < MAX_INPUTS; rangeInput++) { + widget->inputs[numInputs++] = rangeInput; + } + + previousInput = -1; + } else if (numInputs < MAX_INPUTS) { + // standard value + widget->inputs[numInputs++] = input; + previousInput = input; + } + } + + widget->num_inputs = numInputs; + return B_OK; +} + + static const char* get_widget_type_name(hda_widget_type type) { @@ -220,13 +295,11 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) verbs[0] = MAKE_VERB(audioGroup->codec->addr, widget, VID_GET_PARAMETER, PID_AUDIO_WIDGET_CAP); - verbs[1] = MAKE_VERB(audioGroup->codec->addr, widget, VID_GET_PARAMETER, - PID_CONNECTION_LIST_LENGTH); - if (hda_send_verbs(audioGroup->codec, verbs, resp, 2) != B_OK) + if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) != B_OK) return B_ERROR; audioGroup->widgets[i].type = (hda_widget_type)(resp[0] >> 20); - audioGroup->widgets[i].num_inputs = resp[1] & 0x7f; + audioGroup->widgets[i].active_input = -1; dprintf("%ld: %s\n", widget, get_widget_type_name(audioGroup->widgets[i].type)); @@ -326,48 +399,26 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) hda_widget_get_pm_support(audioGroup->codec, widget, &audioGroup->widgets[i].pm); + hda_widget_get_connections(audioGroup->codec, widget, + &audioGroup->widgets[i]); - if (audioGroup->widgets[i].num_inputs) { - if (audioGroup->widgets[i].num_inputs > 1) { - verbs[0] = MAKE_VERB(audioGroup->codec->addr, widget, - VID_GET_CONNECTION_SELECT, 0); - if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) - audioGroup->widgets[i].active_input = resp[0] & 0xff; - else - audioGroup->widgets[i].active_input = -1; - } else - audioGroup->widgets[i].active_input = -1; + // dump connections - buf[0] = '\0'; - off = 0; + buf[0] = '\0'; + off = 0; - for (uint32 inputIndex = 0; inputIndex - < audioGroup->widgets[i].num_inputs; inputIndex++) { - if ((inputIndex % 4) == 0) { - // we get 4 answers per call in short list mode - verbs[0] = MAKE_VERB(audioGroup->codec->addr, widget, - VID_GET_CONNECTION_LIST_ENTRY, inputIndex); - if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) - != B_OK) { - dprintf("hda: Error parsing inputs for widget %ld!\n", - widget); - break; - } - } + for (uint32 inputIndex = 0; inputIndex + < audioGroup->widgets[i].num_inputs; inputIndex++) { + int32 input = audioGroup->widgets[i].inputs[inputIndex]; - uint32 input = (resp[0] >> (8 * (inputIndex % 4))) & 0xff; - - if ((int32)inputIndex != audioGroup->widgets[i].active_input) - off += sprintf(buf + off, "%ld ", input); - else - off += sprintf(buf + off, "(%ld) ", input); - - audioGroup->widgets[i].inputs[inputIndex] = input; - } - - if (off != 0) - dprintf("\tConnections: %s\n", buf); + if ((int32)inputIndex != audioGroup->widgets[i].active_input) + off += sprintf(buf + off, "%ld ", input); + else + off += sprintf(buf + off, "(%ld) ", input); } + + if (off != 0) + dprintf("\tConnections: %s\n", buf); } return B_OK;