/* * Copyright (c) 2023, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #include "Format.h" #include 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 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 pcm_bits_parameters { { // clang-format off { 8, 0b000 }, { 16, 0b001 }, { 20, 0b010 }, { 24, 0b011 }, { 32, 0b100 }, // clang-format on } }; ErrorOr 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 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 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 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 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 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, }; } }