1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 05:34:58 +00:00
serenity/Kernel/Devices/Audio/IntelHDA/OutputPath.h
Liav A 7c0540a229 Everywhere: Move global Kernel pattern code to Kernel/Library directory
This has KString, KBuffer, DoubleBuffer, KBufferBuilder, IOWindow,
UserOrKernelBuffer and ScopedCritical classes being moved to the
Kernel/Library subdirectory.

Also, move the panic and assertions handling code to that directory.
2023-06-04 21:32:34 +02:00

149 lines
5.6 KiB
C++

/*
* Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <Kernel/Devices/Audio/Channel.h>
#include <Kernel/Devices/Audio/IntelHDA/Stream.h>
#include <Kernel/Library/KString.h>
namespace Kernel::Audio::IntelHDA {
class WidgetNode;
class OutputPath {
public:
static constexpr u8 fixed_pcm_bits = 16;
static constexpr u8 fixed_channel_count = 2;
static ErrorOr<NonnullOwnPtr<OutputPath>> create(Vector<NonnullRefPtr<WidgetNode>> widget_path, NonnullOwnPtr<OutputStream> output_stream)
{
return adopt_nonnull_own_or_enomem(new (nothrow) OutputPath(move(widget_path), move(output_stream)));
}
OutputStream& output_stream() { return *m_output_stream; }
ErrorOr<void> activate()
{
// Power on the function group and all widgets that support it
auto output_widget = get<WidgetNode::WidgetType::AudioOutput>();
auto group = output_widget->parent_node();
TRY(group->set_power_state(Node::PowerState::D0));
for (auto& widget : m_widget_path) {
if (widget->power_control_supported())
TRY(widget->set_power_state(Node::PowerState::D0));
}
// Link the audio output widget to the output stream number and first channel
TRY(output_widget->set_converter_stream_and_channel(m_output_stream->stream_number(), OutputStream::fixed_channel));
// Set full volume for all output amplifiers in the path
for (auto& widget : m_widget_path) {
if (!widget->output_amp_present())
continue;
// NOTE: setting gain to the offset means 0dB attenuation / 100% volume
TRY(widget->set_amplifier_gain_mute({
.mute = false,
.gain = widget->output_amp_capabilities().offset,
}));
}
// Walk through pairs of widgets and connect them to each other
for (size_t i = 0; i < m_widget_path.size() - 1; ++i) {
auto left_widget = m_widget_path[i];
auto right_widget = m_widget_path[i + 1];
VERIFY(left_widget->connection_list_present());
if (left_widget->connection_list().size() == 1) {
// If there is only one possible connection, it is fixed and we cannot change it.
VERIFY(left_widget->connection_selected_node_id() == right_widget->node_id());
} else {
// Find the index of the right widget node id in the connection list
size_t connection_index = 0;
for (auto connection_node_id : left_widget->connection_list()) {
if (connection_node_id == right_widget->node_id())
break;
++connection_index;
}
VERIFY(connection_index < left_widget->connection_list().size());
// Select this index
TRY(left_widget->set_connection_select(connection_index));
}
}
// Enable pin complex output
auto pin_widget = get<WidgetNode::WidgetType::PinComplex>();
TRY(pin_widget->set_pin_control({ .output_enabled = true }));
// Finally, retrieve the active converter format for the output widget and set the same for our output stream
auto converter_format = TRY(output_widget->get_converter_format());
TRY(set_format(converter_format));
return {};
}
ErrorOr<void> set_format(FormatParameters format)
{
// FIXME: support other PCM bit sizes and channel counts
format.pcm_bits = fixed_pcm_bits;
format.number_of_channels = fixed_channel_count;
// 7.3.3.8: Converter Format
// "The Converter Format control determines the format the converter will use. This must match the
// format programmed into the Stream Descriptor on the controller so that the data format being
// transmitted on the link matches what is expected by the consumer of the data."
auto output_widget = get<WidgetNode::WidgetType::AudioOutput>();
if (!output_widget->supported_pcm_rates().contains_slow(format.sample_rate)
|| !output_widget->supported_pcm_sizes().contains_slow(format.pcm_bits)
|| format.number_of_channels > output_widget->channel_count())
return ENOTSUP;
TRY(m_output_stream->set_format(format));
TRY(output_widget->set_converter_format(format));
return {};
}
ErrorOr<NonnullOwnPtr<KString>> to_string()
{
StringBuilder builder;
TRY(builder.try_append("OutputPath: ["sv));
for (size_t i = 0; i < m_widget_path.size(); ++i) {
auto widget = m_widget_path[i];
TRY(builder.try_append(TRY(widget->to_string())->view()));
if (i < m_widget_path.size() - 1)
TRY(builder.try_append(""sv));
}
TRY(builder.try_append(']'));
return KString::try_create(builder.string_view());
}
private:
OutputPath(Vector<NonnullRefPtr<WidgetNode>> widget_path, NonnullOwnPtr<OutputStream> output_stream)
: m_widget_path(move(widget_path))
, m_output_stream(move(output_stream))
{
}
template<WidgetNode::WidgetType T>
NonnullRefPtr<WidgetNode> get()
{
for (auto& widget : m_widget_path) {
if (widget->widget_type() == T)
return widget;
}
VERIFY_NOT_REACHED();
}
Vector<NonnullRefPtr<WidgetNode>> m_widget_path;
NonnullOwnPtr<OutputStream> m_output_stream;
};
}