OutputPath.h 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/NonnullOwnPtr.h>
  8. #include <AK/NonnullRefPtr.h>
  9. #include <AK/StringBuilder.h>
  10. #include <AK/Vector.h>
  11. #include <Kernel/Devices/Audio/Channel.h>
  12. #include <Kernel/Devices/Audio/IntelHDA/Stream.h>
  13. #include <Kernel/KString.h>
  14. namespace Kernel::Audio::IntelHDA {
  15. class WidgetNode;
  16. class OutputPath {
  17. public:
  18. static constexpr u8 fixed_pcm_bits = 16;
  19. static constexpr u8 fixed_channel_count = 2;
  20. static ErrorOr<NonnullOwnPtr<OutputPath>> create(Vector<NonnullRefPtr<WidgetNode>> widget_path, NonnullOwnPtr<OutputStream> output_stream)
  21. {
  22. return adopt_nonnull_own_or_enomem(new (nothrow) OutputPath(move(widget_path), move(output_stream)));
  23. }
  24. OutputStream& output_stream() { return *m_output_stream; }
  25. ErrorOr<void> activate()
  26. {
  27. // Power on the function group and all widgets that support it
  28. auto output_widget = get<WidgetNode::WidgetType::AudioOutput>();
  29. auto group = output_widget->parent_node();
  30. TRY(group->set_power_state(Node::PowerState::D0));
  31. for (auto& widget : m_widget_path) {
  32. if (widget->power_control_supported())
  33. TRY(widget->set_power_state(Node::PowerState::D0));
  34. }
  35. // Link the audio output widget to the output stream number and first channel
  36. TRY(output_widget->set_converter_stream_and_channel(m_output_stream->stream_number(), OutputStream::fixed_channel));
  37. // Set full volume for all output amplifiers in the path
  38. for (auto& widget : m_widget_path) {
  39. if (!widget->output_amp_present())
  40. continue;
  41. // NOTE: setting gain to the offset means 0dB attenuation / 100% volume
  42. TRY(widget->set_amplifier_gain_mute({
  43. .mute = false,
  44. .gain = widget->output_amp_capabilities().offset,
  45. }));
  46. }
  47. // Walk through pairs of widgets and connect them to each other
  48. for (size_t i = 0; i < m_widget_path.size() - 1; ++i) {
  49. auto left_widget = m_widget_path[i];
  50. auto right_widget = m_widget_path[i + 1];
  51. VERIFY(left_widget->connection_list_present());
  52. if (left_widget->connection_list().size() == 1) {
  53. // If there is only one possible connection, it is fixed and we cannot change it.
  54. VERIFY(left_widget->connection_selected_node_id() == right_widget->node_id());
  55. } else {
  56. // Find the index of the right widget node id in the connection list
  57. size_t connection_index = 0;
  58. for (auto connection_node_id : left_widget->connection_list()) {
  59. if (connection_node_id == right_widget->node_id())
  60. break;
  61. ++connection_index;
  62. }
  63. VERIFY(connection_index < left_widget->connection_list().size());
  64. // Select this index
  65. TRY(left_widget->set_connection_select(connection_index));
  66. }
  67. }
  68. // Enable pin complex output
  69. auto pin_widget = get<WidgetNode::WidgetType::PinComplex>();
  70. TRY(pin_widget->set_pin_control({ .output_enabled = true }));
  71. // Finally, retrieve the active converter format for the output widget and set the same for our output stream
  72. auto converter_format = TRY(output_widget->get_converter_format());
  73. TRY(set_format(converter_format));
  74. return {};
  75. }
  76. ErrorOr<void> set_format(FormatParameters format)
  77. {
  78. // FIXME: support other PCM bit sizes and channel counts
  79. format.pcm_bits = fixed_pcm_bits;
  80. format.number_of_channels = fixed_channel_count;
  81. // 7.3.3.8: Converter Format
  82. // "The Converter Format control determines the format the converter will use. This must match the
  83. // format programmed into the Stream Descriptor on the controller so that the data format being
  84. // transmitted on the link matches what is expected by the consumer of the data."
  85. auto output_widget = get<WidgetNode::WidgetType::AudioOutput>();
  86. if (!output_widget->supported_pcm_rates().contains_slow(format.sample_rate)
  87. || !output_widget->supported_pcm_sizes().contains_slow(format.pcm_bits)
  88. || format.number_of_channels > output_widget->channel_count())
  89. return ENOTSUP;
  90. TRY(m_output_stream->set_format(format));
  91. TRY(output_widget->set_converter_format(format));
  92. return {};
  93. }
  94. ErrorOr<NonnullOwnPtr<KString>> to_string()
  95. {
  96. StringBuilder builder;
  97. TRY(builder.try_append("OutputPath: ["sv));
  98. for (size_t i = 0; i < m_widget_path.size(); ++i) {
  99. auto widget = m_widget_path[i];
  100. TRY(builder.try_append(TRY(widget->to_string())->view()));
  101. if (i < m_widget_path.size() - 1)
  102. TRY(builder.try_append(" → "sv));
  103. }
  104. TRY(builder.try_append(']'));
  105. return KString::try_create(builder.string_view());
  106. }
  107. private:
  108. OutputPath(Vector<NonnullRefPtr<WidgetNode>> widget_path, NonnullOwnPtr<OutputStream> output_stream)
  109. : m_widget_path(move(widget_path))
  110. , m_output_stream(move(output_stream))
  111. {
  112. }
  113. template<WidgetNode::WidgetType T>
  114. NonnullRefPtr<WidgetNode> get()
  115. {
  116. for (auto& widget : m_widget_path) {
  117. if (widget->widget_type() == T)
  118. return widget;
  119. }
  120. VERIFY_NOT_REACHED();
  121. }
  122. Vector<NonnullRefPtr<WidgetNode>> m_widget_path;
  123. NonnullOwnPtr<OutputStream> m_output_stream;
  124. };
  125. }