mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 12:44:59 +00:00

This is an implementation that tries to follow the spec as closely as possible, and works with Qemu's Intel HDA and some bare metal HDA controllers out there. Compiling with `INTEL_HDA_DEBUG=on` will provide a lot of detailed information that could help us getting this to work on more bare metal controllers as well :^) Output format is limited to `i16` samples for now.
140 lines
4.1 KiB
C++
140 lines
4.1 KiB
C++
/*
|
|
* 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,
|
|
};
|
|
}
|
|
|
|
}
|