Kernel: Implement variable rate audio support for AC97 devices

Previously we `VERIFY()`ed that the device supports variable-rate audio
(VRA). Now, we query the VRA bit and if VRA is not supported, we do not
enable double-rate audio and disallow setting any sample rate except
the fixed 48kHz rate as defined by the AC'97 specification. This should
allow the driver to function on a wider array of hardware.

Note that in the AC'97 specification, DRA without VRA is allowed when
supported: this effectively doubles the sample rate to 96kHZ. For now,
we ignore that possibility and let it default to 48kHZ.
This commit is contained in:
Jelle Raaijmakers 2021-11-25 19:37:11 +01:00 committed by Andreas Kling
parent 377a984905
commit 58bcb93777
Notes: sideshowbarker 2024-07-18 00:38:56 +09:00
2 changed files with 23 additions and 11 deletions

View File

@ -13,6 +13,9 @@ namespace Kernel {
static constexpr int buffer_descriptor_list_max_entries = 32;
static constexpr u16 pcm_default_sample_rate = 44100;
static constexpr u16 pcm_fixed_sample_rate = 48000;
// Valid output range - with double-rate enabled, sample rate can go up to 96kHZ
static constexpr u16 pcm_sample_rate_minimum = 8000;
static constexpr u16 pcm_sample_rate_maximum = 48000;
@ -101,21 +104,25 @@ UNMAP_AFTER_INIT void AC97::initialize()
// Reset mixer
m_io_mixer_base.offset(NativeAudioMixerRegister::Reset).out<u16>(1);
// Verify extended capabilities
auto extended_audio_id = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioID).in<u16>();
VERIFY((extended_audio_id & ExtendedAudioMask::VariableRatePCMAudio) > 0);
VERIFY((extended_audio_id & ExtendedAudioMask::Revision) >> 10 == AC97Revision::Revision23);
// Enable double rate PCM audio if supported
if ((extended_audio_id & ExtendedAudioMask::DoubleRatePCMAudio) > 0) {
auto extended_audio_status_control_register = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioStatusControl);
auto extended_audio_status = extended_audio_status_control_register.in<u16>();
// Enable variable and double rate PCM audio if supported
auto extended_audio_status_control_register = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioStatusControl);
auto extended_audio_status = extended_audio_status_control_register.in<u16>();
if ((extended_audio_id & ExtendedAudioMask::VariableRatePCMAudio) > 0) {
extended_audio_status |= ExtendedAudioStatusControlFlag::VariableRateAudio;
m_variable_rate_pcm_supported = true;
}
if (!m_variable_rate_pcm_supported) {
extended_audio_status &= ~ExtendedAudioStatusControlFlag::DoubleRateAudio;
} else if ((extended_audio_id & ExtendedAudioMask::DoubleRatePCMAudio) > 0) {
extended_audio_status |= ExtendedAudioStatusControlFlag::DoubleRateAudio;
extended_audio_status_control_register.out(extended_audio_status);
m_double_rate_pcm_enabled = true;
}
extended_audio_status_control_register.out(extended_audio_status);
MUST(set_pcm_output_sample_rate(m_sample_rate));
MUST(set_pcm_output_sample_rate(m_variable_rate_pcm_supported ? pcm_default_sample_rate : pcm_fixed_sample_rate));
// Left and right volume of 0 means attenuation of 0 dB
set_master_output_volume(0, 0, Muted::No);
@ -134,8 +141,6 @@ ErrorOr<void> AC97::ioctl(OpenFileDescription&, unsigned request, Userspace<void
}
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
auto sample_rate = static_cast<u32>(arg.ptr());
if (sample_rate == m_sample_rate)
return {};
TRY(set_pcm_output_sample_rate(sample_rate));
return {};
}
@ -165,8 +170,13 @@ void AC97::set_master_output_volume(u8 left_channel, u8 right_channel, Muted mut
ErrorOr<void> AC97::set_pcm_output_sample_rate(u32 sample_rate)
{
if (m_sample_rate == sample_rate)
return {};
auto const double_rate_shift = m_double_rate_pcm_enabled ? 1 : 0;
auto shifted_sample_rate = sample_rate >> double_rate_shift;
if (!m_variable_rate_pcm_supported && shifted_sample_rate != pcm_fixed_sample_rate)
return ENOTSUP;
if (shifted_sample_rate < pcm_sample_rate_minimum || shifted_sample_rate > pcm_sample_rate_maximum)
return ENOTSUP;

View File

@ -54,6 +54,7 @@ private:
};
enum ExtendedAudioStatusControlFlag : u16 {
VariableRateAudio = 1 << 0,
DoubleRateAudio = 1 << 1,
};
@ -170,7 +171,8 @@ private:
u8 m_output_buffer_page_count = 4;
u8 m_output_buffer_page_index = 0;
AC97Channel m_pcm_out_channel;
u32 m_sample_rate = 44100;
u32 m_sample_rate = 0;
bool m_variable_rate_pcm_supported = false;
};
}