From 5c20d5e2915549c118743e94b03d17f7bad6e08f Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 24 Mar 2026 00:53:19 +0200 Subject: [PATCH] Kernel: HDAudio hide unusable pins and cleanup path finding --- .../kernel/Audio/HDAudio/AudioFunctionGroup.h | 3 +- .../kernel/Audio/HDAudio/Definitions.h | 2 + .../Audio/HDAudio/AudioFunctionGroup.cpp | 103 ++++++++++-------- kernel/kernel/Audio/HDAudio/Controller.cpp | 8 +- 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h b/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h index efe15d69..1f56b05c 100644 --- a/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h +++ b/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h @@ -53,8 +53,6 @@ namespace Kernel void queue_bdl_data(); private: - static constexpr size_t m_max_path_length = 16; - // use 6 512 sample BDL entries // each entry is ~10.7 ms at 48 kHz // -> total buffered audio is 64 ms @@ -66,6 +64,7 @@ namespace Kernel const uint8_t m_cid; BAN::Vector> m_output_paths; + BAN::Vector m_output_pins; size_t m_output_path_index { SIZE_MAX }; uint8_t m_stream_id { 0xFF }; diff --git a/kernel/include/kernel/Audio/HDAudio/Definitions.h b/kernel/include/kernel/Audio/HDAudio/Definitions.h index f094c523..8bd4ab97 100644 --- a/kernel/include/kernel/Audio/HDAudio/Definitions.h +++ b/kernel/include/kernel/Audio/HDAudio/Definitions.h @@ -50,6 +50,8 @@ namespace Kernel::HDAudio { bool input; bool output; + bool display; // HDMI or DP + uint32_t config; } pin_complex; }; diff --git a/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp b/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp index 18f15e76..4d54f682 100644 --- a/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp +++ b/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp @@ -2,6 +2,8 @@ #include #include +#include + namespace Kernel { @@ -52,19 +54,13 @@ namespace Kernel { if (widget.type == HDAudio::AFGWidget::Type::PinComplex) { - const uint32_t config = TRY(m_controller->send_command({ - .data = 0x00, - .command = 0xF1C, - .node_index = widget.id, - .codec_address = m_cid, - })); - - dprintln(" widget {}: {} ({}, {}), {32b}", + dprintln(" widget {}: {} ({}, {}, {}), {32b}", widget.id, widget_to_string(widget.type), (int)widget.pin_complex.output, (int)widget.pin_complex.input, - config + (int)widget.pin_complex.display, + widget.pin_complex.config ); } else @@ -81,59 +77,49 @@ namespace Kernel } TRY(initialize_stream()); - TRY(initialize_output()); + + if (auto ret = initialize_output(); ret.is_error()) + { + // No usable pins, not really an error + if (ret.error().get_error_code() == ENODEV) + return {}; + return ret.release_error(); + } + DevFileSystem::get().add_device(this); + return {}; } uint32_t HDAudioFunctionGroup::get_total_pins() const { - uint32_t count = 0; - for (const auto& widget : m_afg_node.widgets) - if (widget.type == HDAudio::AFGWidget::Type::PinComplex && widget.pin_complex.output) - count++; - return count; + return m_output_pins.size(); } uint32_t HDAudioFunctionGroup::get_current_pin() const { const auto current_id = m_output_paths[m_output_path_index].front()->id; - - uint32_t pin = 0; - for (const auto& widget : m_afg_node.widgets) - { - if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output) - continue; - if (widget.id == current_id) - return pin; - pin++; - } - + for (size_t i = 0; i < m_output_pins.size(); i++) + if (m_output_pins[i]->id == current_id) + return i; ASSERT_NOT_REACHED(); } BAN::ErrorOr HDAudioFunctionGroup::set_current_pin(uint32_t pin) { - uint32_t pin_id = 0; - for (const auto& widget : m_afg_node.widgets) - { - if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output) - continue; - if (pin-- > 0) - continue; - pin_id = widget.id; - break; - } + if (pin >= m_output_pins.size()) + return BAN::Error::from_errno(EINVAL); if (auto ret = disable_output_path(m_output_path_index); ret.is_error()) dwarnln("failed to disable old output path {}", ret.error()); + const uint32_t pin_id = m_output_pins[pin]->id; for (size_t i = 0; i < m_output_paths.size(); i++) { if (m_output_paths[i].front()->id != pin_id) continue; - if (auto ret = enable_output_path(i); !ret.is_error()) + if (auto ret = enable_output_path(i); ret.is_error()) { if (ret.error().get_error_code() == ENOTSUP) continue; @@ -141,7 +127,6 @@ namespace Kernel return ret.release_error(); } - dprintln("set output widget to {}", pin_id); m_output_path_index = i; return {}; } @@ -232,18 +217,39 @@ namespace Kernel BAN::ErrorOr HDAudioFunctionGroup::initialize_output() { - BAN::Vector path; - TRY(path.reserve(m_max_path_length)); - for (const auto& widget : m_afg_node.widgets) { if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output) continue; + + // no physical connection + if ((widget.pin_complex.config >> 30) == 0b01) + continue; + + // needs a GPU + if (widget.pin_complex.display) + continue; + + BAN::Vector path; TRY(path.push_back(&widget)); TRY(recurse_output_paths(widget, path)); - path.pop_back(); + + if (!m_output_paths.empty() && m_output_paths.back().front()->id == widget.id) + TRY(m_output_pins.push_back(&widget)); } + if (m_output_pins.empty()) + return BAN::Error::from_errno(ENODEV); + + // prefer short paths + BAN::sort::sort(m_output_paths.begin(), m_output_paths.end(), + [](const auto& a, const auto& b) { + if (a.front()->id != b.front()->id) + return a.front()->id < b.front()->id; + return a.size() < b.size(); + } + ); + dprintln_if(DEBUG_HDAUDIO, "found {} paths from output to DAC", m_output_paths.size()); // select first supported path @@ -444,10 +450,6 @@ namespace Kernel BAN::ErrorOr HDAudioFunctionGroup::recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector& path) { - // cycle "detection" - if (path.size() >= m_max_path_length) - return {}; - // we've reached a DAC if (widget.type == HDAudio::AFGWidget::Type::OutputConverter) { @@ -464,9 +466,18 @@ namespace Kernel { if (!widget.connections.contains(connection.id)) continue; + + // cycle detection + for (const auto* w : path) + if (w == &connection) + goto already_visited; + TRY(path.push_back(&connection)); TRY(recurse_output_paths(connection, path)); path.pop_back(); + + already_visited: + continue; } return {}; diff --git a/kernel/kernel/Audio/HDAudio/Controller.cpp b/kernel/kernel/Audio/HDAudio/Controller.cpp index a19714b8..d4ec4557 100644 --- a/kernel/kernel/Audio/HDAudio/Controller.cpp +++ b/kernel/kernel/Audio/HDAudio/Controller.cpp @@ -311,8 +311,12 @@ namespace Kernel if (result.type == AFGWidget::Type::PinComplex) { const uint32_t cap = send_command_or_zero(0xF00, 0x0C); - result.pin_complex.output = !!(cap & (1 << 4)); - result.pin_complex.input = !!(cap & (1 << 5)); + result.pin_complex = { + .input = !!(cap & (1 << 5)), + .output = !!(cap & (1 << 4)), + .display = !!(cap & ((1 << 7) | (1 << 24))), + .config = send_command_or_zero(0xF1C, 0x00), + }; } const uint8_t connection_info = send_command_or_zero(0xF00, 0x0E);