Kernel: HDAudio hide unusable pins and cleanup path finding
This commit is contained in:
parent
f89d690716
commit
5c20d5e291
|
|
@ -53,8 +53,6 @@ namespace Kernel
|
||||||
void queue_bdl_data();
|
void queue_bdl_data();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t m_max_path_length = 16;
|
|
||||||
|
|
||||||
// use 6 512 sample BDL entries
|
// use 6 512 sample BDL entries
|
||||||
// each entry is ~10.7 ms at 48 kHz
|
// each entry is ~10.7 ms at 48 kHz
|
||||||
// -> total buffered audio is 64 ms
|
// -> total buffered audio is 64 ms
|
||||||
|
|
@ -66,6 +64,7 @@ namespace Kernel
|
||||||
const uint8_t m_cid;
|
const uint8_t m_cid;
|
||||||
|
|
||||||
BAN::Vector<BAN::Vector<const HDAudio::AFGWidget*>> m_output_paths;
|
BAN::Vector<BAN::Vector<const HDAudio::AFGWidget*>> m_output_paths;
|
||||||
|
BAN::Vector<const HDAudio::AFGWidget*> m_output_pins;
|
||||||
size_t m_output_path_index { SIZE_MAX };
|
size_t m_output_path_index { SIZE_MAX };
|
||||||
|
|
||||||
uint8_t m_stream_id { 0xFF };
|
uint8_t m_stream_id { 0xFF };
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ namespace Kernel::HDAudio
|
||||||
{
|
{
|
||||||
bool input;
|
bool input;
|
||||||
bool output;
|
bool output;
|
||||||
|
bool display; // HDMI or DP
|
||||||
|
uint32_t config;
|
||||||
} pin_complex;
|
} pin_complex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
#include <kernel/Audio/HDAudio/Registers.h>
|
#include <kernel/Audio/HDAudio/Registers.h>
|
||||||
#include <kernel/FS/DevFS/FileSystem.h>
|
#include <kernel/FS/DevFS/FileSystem.h>
|
||||||
|
|
||||||
|
#include <BAN/Sort.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -52,19 +54,13 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
if (widget.type == HDAudio::AFGWidget::Type::PinComplex)
|
if (widget.type == HDAudio::AFGWidget::Type::PinComplex)
|
||||||
{
|
{
|
||||||
const uint32_t config = TRY(m_controller->send_command({
|
dprintln(" widget {}: {} ({}, {}, {}), {32b}",
|
||||||
.data = 0x00,
|
|
||||||
.command = 0xF1C,
|
|
||||||
.node_index = widget.id,
|
|
||||||
.codec_address = m_cid,
|
|
||||||
}));
|
|
||||||
|
|
||||||
dprintln(" widget {}: {} ({}, {}), {32b}",
|
|
||||||
widget.id,
|
widget.id,
|
||||||
widget_to_string(widget.type),
|
widget_to_string(widget.type),
|
||||||
(int)widget.pin_complex.output,
|
(int)widget.pin_complex.output,
|
||||||
(int)widget.pin_complex.input,
|
(int)widget.pin_complex.input,
|
||||||
config
|
(int)widget.pin_complex.display,
|
||||||
|
widget.pin_complex.config
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -81,59 +77,49 @@ namespace Kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(initialize_stream());
|
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);
|
DevFileSystem::get().add_device(this);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t HDAudioFunctionGroup::get_total_pins() const
|
uint32_t HDAudioFunctionGroup::get_total_pins() const
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
return m_output_pins.size();
|
||||||
for (const auto& widget : m_afg_node.widgets)
|
|
||||||
if (widget.type == HDAudio::AFGWidget::Type::PinComplex && widget.pin_complex.output)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t HDAudioFunctionGroup::get_current_pin() const
|
uint32_t HDAudioFunctionGroup::get_current_pin() const
|
||||||
{
|
{
|
||||||
const auto current_id = m_output_paths[m_output_path_index].front()->id;
|
const auto current_id = m_output_paths[m_output_path_index].front()->id;
|
||||||
|
for (size_t i = 0; i < m_output_pins.size(); i++)
|
||||||
uint32_t pin = 0;
|
if (m_output_pins[i]->id == current_id)
|
||||||
for (const auto& widget : m_afg_node.widgets)
|
return i;
|
||||||
{
|
|
||||||
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
|
|
||||||
continue;
|
|
||||||
if (widget.id == current_id)
|
|
||||||
return pin;
|
|
||||||
pin++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> HDAudioFunctionGroup::set_current_pin(uint32_t pin)
|
BAN::ErrorOr<void> HDAudioFunctionGroup::set_current_pin(uint32_t pin)
|
||||||
{
|
{
|
||||||
uint32_t pin_id = 0;
|
if (pin >= m_output_pins.size())
|
||||||
for (const auto& widget : m_afg_node.widgets)
|
return BAN::Error::from_errno(EINVAL);
|
||||||
{
|
|
||||||
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
|
|
||||||
continue;
|
|
||||||
if (pin-- > 0)
|
|
||||||
continue;
|
|
||||||
pin_id = widget.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto ret = disable_output_path(m_output_path_index); ret.is_error())
|
if (auto ret = disable_output_path(m_output_path_index); ret.is_error())
|
||||||
dwarnln("failed to disable old output path {}", ret.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++)
|
for (size_t i = 0; i < m_output_paths.size(); i++)
|
||||||
{
|
{
|
||||||
if (m_output_paths[i].front()->id != pin_id)
|
if (m_output_paths[i].front()->id != pin_id)
|
||||||
continue;
|
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)
|
if (ret.error().get_error_code() == ENOTSUP)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -141,7 +127,6 @@ namespace Kernel
|
||||||
return ret.release_error();
|
return ret.release_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintln("set output widget to {}", pin_id);
|
|
||||||
m_output_path_index = i;
|
m_output_path_index = i;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -232,18 +217,39 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<void> HDAudioFunctionGroup::initialize_output()
|
BAN::ErrorOr<void> HDAudioFunctionGroup::initialize_output()
|
||||||
{
|
{
|
||||||
BAN::Vector<const HDAudio::AFGWidget*> path;
|
|
||||||
TRY(path.reserve(m_max_path_length));
|
|
||||||
|
|
||||||
for (const auto& widget : m_afg_node.widgets)
|
for (const auto& widget : m_afg_node.widgets)
|
||||||
{
|
{
|
||||||
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
|
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// no physical connection
|
||||||
|
if ((widget.pin_complex.config >> 30) == 0b01)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// needs a GPU
|
||||||
|
if (widget.pin_complex.display)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BAN::Vector<const HDAudio::AFGWidget*> path;
|
||||||
TRY(path.push_back(&widget));
|
TRY(path.push_back(&widget));
|
||||||
TRY(recurse_output_paths(widget, path));
|
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());
|
dprintln_if(DEBUG_HDAUDIO, "found {} paths from output to DAC", m_output_paths.size());
|
||||||
|
|
||||||
// select first supported path
|
// select first supported path
|
||||||
|
|
@ -444,10 +450,6 @@ namespace Kernel
|
||||||
|
|
||||||
BAN::ErrorOr<void> HDAudioFunctionGroup::recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector<const HDAudio::AFGWidget*>& path)
|
BAN::ErrorOr<void> HDAudioFunctionGroup::recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector<const HDAudio::AFGWidget*>& path)
|
||||||
{
|
{
|
||||||
// cycle "detection"
|
|
||||||
if (path.size() >= m_max_path_length)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// we've reached a DAC
|
// we've reached a DAC
|
||||||
if (widget.type == HDAudio::AFGWidget::Type::OutputConverter)
|
if (widget.type == HDAudio::AFGWidget::Type::OutputConverter)
|
||||||
{
|
{
|
||||||
|
|
@ -464,9 +466,18 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
if (!widget.connections.contains(connection.id))
|
if (!widget.connections.contains(connection.id))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// cycle detection
|
||||||
|
for (const auto* w : path)
|
||||||
|
if (w == &connection)
|
||||||
|
goto already_visited;
|
||||||
|
|
||||||
TRY(path.push_back(&connection));
|
TRY(path.push_back(&connection));
|
||||||
TRY(recurse_output_paths(connection, path));
|
TRY(recurse_output_paths(connection, path));
|
||||||
path.pop_back();
|
path.pop_back();
|
||||||
|
|
||||||
|
already_visited:
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
||||||
|
|
@ -311,8 +311,12 @@ namespace Kernel
|
||||||
if (result.type == AFGWidget::Type::PinComplex)
|
if (result.type == AFGWidget::Type::PinComplex)
|
||||||
{
|
{
|
||||||
const uint32_t cap = send_command_or_zero(0xF00, 0x0C);
|
const uint32_t cap = send_command_or_zero(0xF00, 0x0C);
|
||||||
result.pin_complex.output = !!(cap & (1 << 4));
|
result.pin_complex = {
|
||||||
result.pin_complex.input = !!(cap & (1 << 5));
|
.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);
|
const uint8_t connection_info = send_command_or_zero(0xF00, 0x0E);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue