mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:12:45 +00:00 
			
		
		
		
	 7c0540a229
			
		
	
	
		7c0540a229
		
	
	
	
	
		
			
			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.
		
			
				
	
	
		
			149 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| };
 | |
| 
 | |
| }
 |