summaryrefslogtreecommitdiff
path: root/Kernel/Devices/Audio/IntelHDA/Format.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Kernel/Devices/Audio/IntelHDA/Format.cpp')
-rw-r--r--Kernel/Devices/Audio/IntelHDA/Format.cpp140
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,
+ };
+}
+
+}