diff options
Diffstat (limited to 'Kernel/Devices/Audio/IntelHDA/Format.cpp')
-rw-r--r-- | Kernel/Devices/Audio/IntelHDA/Format.cpp | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/Kernel/Devices/Audio/IntelHDA/Format.cpp b/Kernel/Devices/Audio/IntelHDA/Format.cpp new file mode 100644 index 0000000000..64f26e0379 --- /dev/null +++ b/Kernel/Devices/Audio/IntelHDA/Format.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Format.h" +#include <AK/Array.h> + +namespace Kernel::Audio::IntelHDA { + +// 3.3.41: Input/Output/Bidirectional Stream Descriptor Format +// 3.7.1: Stream Format Structure +struct SampleRateParameters { + u32 sample_rate; + u8 base; + u8 multiple; + u8 divisor; +}; +static constexpr Array<SampleRateParameters, 15> sample_rate_parameters { { + // clang-format off + { 6'000, 0b0, 0b000, 0b111 }, + { 8'000, 0b0, 0b000, 0b101 }, + { 9'600, 0b0, 0b000, 0b100 }, + { 11'025, 0b1, 0b000, 0b011 }, + { 16'000, 0b0, 0b000, 0b010 }, + { 22'050, 0b1, 0b000, 0b001 }, + { 24'000, 0b0, 0b000, 0b001 }, + { 32'000, 0b0, 0b001, 0b010 }, + { 44'100, 0b1, 0b000, 0b000 }, + { 48'000, 0b0, 0b000, 0b000 }, + { 88'200, 0b1, 0b001, 0b000 }, + { 96'000, 0b0, 0b001, 0b000 }, + { 144'000, 0b0, 0b010, 0b000 }, + { 176'400, 0b1, 0b011, 0b000 }, + { 192'000, 0b0, 0b011, 0b000 }, + // clang-format on +} }; + +struct PcmBitsParameters { + u8 pcm_bits; + u8 encoding; +}; +static constexpr Array<PcmBitsParameters, 5> pcm_bits_parameters { { + // clang-format off + { 8, 0b000 }, + { 16, 0b001 }, + { 20, 0b010 }, + { 24, 0b011 }, + { 32, 0b100 }, + // clang-format on +} }; + +ErrorOr<u16> encode_format(FormatParameters format) +{ + // 3.3.41: Input/Output/Bidirectional Stream Descriptor Format + // 3.7.1: Stream Format Structure + + // Stream type + // NOTE: we only support PCM streams + auto is_pcm = true; + + // Sample rate parameters + Optional<SampleRateParameters> selected_sample_rate {}; + for (auto sample_rate_parameter : sample_rate_parameters) { + if (sample_rate_parameter.sample_rate == format.sample_rate) { + selected_sample_rate = sample_rate_parameter; + break; + } + } + if (!selected_sample_rate.has_value()) + return ENOTSUP; + + // Bit size + Optional<PcmBitsParameters> selected_bit_rate {}; + for (auto pcm_bits_parameter : pcm_bits_parameters) { + if (pcm_bits_parameter.pcm_bits == format.pcm_bits) { + selected_bit_rate = pcm_bits_parameter; + break; + } + } + if (!selected_bit_rate.has_value()) + return ENOTSUP; + + // Number of channels + if (format.number_of_channels < 1 || format.number_of_channels > 16) + return ENOTSUP; + + // Construct stream format + return ((is_pcm ? 0 : 1) << 15) + | ((selected_sample_rate->base & 0x1) << 14) + | ((selected_sample_rate->multiple & 0x7) << 11) + | ((selected_sample_rate->divisor & 0x7) << 8) + | ((selected_bit_rate->encoding & 0x7) << 4) + | ((format.number_of_channels - 1) & 0xf); +} + +ErrorOr<FormatParameters> decode_format(u16 format) +{ + // 3.3.41: Input/Output/Bidirectional Stream Descriptor Format + // 3.7.1: Stream Format Structure + + // Sample rate + u8 sample_rate_base = (format >> 14) & 0x1; + u8 sample_rate_multiple = (format >> 11) & 0x7; + u8 sample_rate_divisor = (format >> 8) & 0x7; + Optional<SampleRateParameters> found_sample_rate {}; + for (auto sample_rate_parameter : sample_rate_parameters) { + if (sample_rate_parameter.base == sample_rate_base + && sample_rate_parameter.multiple == sample_rate_multiple + && sample_rate_parameter.divisor == sample_rate_divisor) { + found_sample_rate = sample_rate_parameter; + break; + } + } + + // PCM bits + u8 pcm_bits = (format >> 4) & 0x7; + Optional<PcmBitsParameters> found_pcm_bits {}; + for (auto pcm_bits_parameter : pcm_bits_parameters) { + if (pcm_bits_parameter.encoding == pcm_bits) { + found_pcm_bits = pcm_bits_parameter; + break; + } + } + + // Number of channels + u8 number_of_channels = (format & 0xf) + 1; + + if (!found_sample_rate.has_value() || !found_pcm_bits.has_value()) + return EINVAL; + + return FormatParameters { + .sample_rate = found_sample_rate.release_value().sample_rate, + .pcm_bits = found_pcm_bits.release_value().pcm_bits, + .number_of_channels = number_of_channels, + }; +} + +} |