mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 10:27:34 +00:00
Kernel: Allow higher audio sample rates than 65kHZ (u16
)
Executing `asctl set r 96000` no longer results in weird sample rates being set on the audio devices. SB16 checks for a sample rate between 1 and 44100 Hz, while AC97 implements double-rate support which allows sample rates between 8kHz and 96kHZ.
This commit is contained in:
parent
c349634967
commit
f97c9a5968
9 changed files with 56 additions and 28 deletions
|
@ -13,6 +13,10 @@ namespace Kernel {
|
||||||
|
|
||||||
static constexpr int buffer_descriptor_list_max_entries = 32;
|
static constexpr int buffer_descriptor_list_max_entries = 32;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
static ErrorOr<OwnPtr<Memory::Region>> allocate_physical_buffer(size_t size, StringView name)
|
static ErrorOr<OwnPtr<Memory::Region>> allocate_physical_buffer(size_t size, StringView name)
|
||||||
{
|
{
|
||||||
auto vmobject = TRY(Memory::AnonymousVMObject::try_create_physically_contiguous_with_size(Memory::page_round_up(size)));
|
auto vmobject = TRY(Memory::AnonymousVMObject::try_create_physically_contiguous_with_size(Memory::page_round_up(size)));
|
||||||
|
@ -92,22 +96,31 @@ UNMAP_AFTER_INIT void AC97::initialize()
|
||||||
auto control = m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).in<u32>();
|
auto control = m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).in<u32>();
|
||||||
control |= GlobalControlFlag::GPIInterruptEnable;
|
control |= GlobalControlFlag::GPIInterruptEnable;
|
||||||
control |= GlobalControlFlag::AC97ColdReset;
|
control |= GlobalControlFlag::AC97ColdReset;
|
||||||
m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).out<u32>(control);
|
m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).out(control);
|
||||||
|
|
||||||
// Reset mixer
|
// Reset mixer
|
||||||
m_io_mixer_base.offset(NativeAudioMixerRegister::Reset).out<u16>(1);
|
m_io_mixer_base.offset(NativeAudioMixerRegister::Reset).out<u16>(1);
|
||||||
|
|
||||||
// Verify extended capabilities
|
// Verify extended capabilities
|
||||||
auto extended_audio_id = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioID).in<u16>();
|
auto extended_audio_id = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioID).in<u16>();
|
||||||
VERIFY((extended_audio_id & ExtendedAudioMask::VariableRatePCMAudio) == 1);
|
VERIFY((extended_audio_id & ExtendedAudioMask::VariableRatePCMAudio) > 0);
|
||||||
VERIFY((extended_audio_id & ExtendedAudioMask::Revision) >> 10 == AC97Revision::Revision23);
|
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>();
|
||||||
|
extended_audio_status |= ExtendedAudioStatusControlFlag::DoubleRateAudio;
|
||||||
|
extended_audio_status_control_register.out(extended_audio_status);
|
||||||
|
m_double_rate_pcm_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MUST(set_pcm_output_sample_rate(m_sample_rate));
|
||||||
|
|
||||||
// Left and right volume of 0 means attenuation of 0 dB
|
// Left and right volume of 0 means attenuation of 0 dB
|
||||||
set_master_output_volume(0, 0, Muted::No);
|
set_master_output_volume(0, 0, Muted::No);
|
||||||
set_pcm_output_volume(0, 0, Muted::No);
|
set_pcm_output_volume(0, 0, Muted::No);
|
||||||
|
|
||||||
set_pcm_output_sample_rate(m_sample_rate);
|
|
||||||
|
|
||||||
reset_pcm_out();
|
reset_pcm_out();
|
||||||
enable_irq();
|
enable_irq();
|
||||||
}
|
}
|
||||||
|
@ -116,15 +129,14 @@ ErrorOr<void> AC97::ioctl(OpenFileDescription&, unsigned request, Userspace<void
|
||||||
{
|
{
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case SOUNDCARD_IOCTL_GET_SAMPLE_RATE: {
|
case SOUNDCARD_IOCTL_GET_SAMPLE_RATE: {
|
||||||
auto output = static_ptr_cast<u16*>(arg);
|
auto output = static_ptr_cast<u32*>(arg);
|
||||||
return copy_to_user(output, &m_sample_rate);
|
return copy_to_user(output, &m_sample_rate);
|
||||||
}
|
}
|
||||||
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
|
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
|
||||||
auto sample_rate_value = static_cast<u16>(arg.ptr());
|
auto sample_rate = static_cast<u32>(arg.ptr());
|
||||||
if (sample_rate_value == 0)
|
if (sample_rate == m_sample_rate)
|
||||||
return EINVAL;
|
return {};
|
||||||
if (m_sample_rate != sample_rate_value)
|
TRY(set_pcm_output_sample_rate(sample_rate));
|
||||||
set_pcm_output_sample_rate(sample_rate_value);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -151,12 +163,20 @@ void AC97::set_master_output_volume(u8 left_channel, u8 right_channel, Muted mut
|
||||||
m_io_mixer_base.offset(NativeAudioMixerRegister::SetMasterOutputVolume).out(volume_value);
|
m_io_mixer_base.offset(NativeAudioMixerRegister::SetMasterOutputVolume).out(volume_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AC97::set_pcm_output_sample_rate(u16 sample_rate)
|
ErrorOr<void> AC97::set_pcm_output_sample_rate(u32 sample_rate)
|
||||||
{
|
{
|
||||||
|
auto const double_rate_shift = m_double_rate_pcm_enabled ? 1 : 0;
|
||||||
|
auto shifted_sample_rate = sample_rate >> double_rate_shift;
|
||||||
|
if (shifted_sample_rate < pcm_sample_rate_minimum || shifted_sample_rate > pcm_sample_rate_maximum)
|
||||||
|
return ENOTSUP;
|
||||||
|
|
||||||
auto pcm_front_dac_rate_register = m_io_mixer_base.offset(NativeAudioMixerRegister::PCMFrontDACRate);
|
auto pcm_front_dac_rate_register = m_io_mixer_base.offset(NativeAudioMixerRegister::PCMFrontDACRate);
|
||||||
pcm_front_dac_rate_register.out(sample_rate);
|
pcm_front_dac_rate_register.out<u16>(shifted_sample_rate);
|
||||||
m_sample_rate = pcm_front_dac_rate_register.in<u16>();
|
m_sample_rate = static_cast<u32>(pcm_front_dac_rate_register.in<u16>()) << double_rate_shift;
|
||||||
|
|
||||||
dbgln("AC97 @ {}: PCM front DAC rate set to {} Hz", pci_address(), m_sample_rate);
|
dbgln("AC97 @ {}: PCM front DAC rate set to {} Hz", pci_address(), m_sample_rate);
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute)
|
void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute)
|
||||||
|
|
|
@ -43,14 +43,20 @@ private:
|
||||||
SetMasterOutputVolume = 0x02,
|
SetMasterOutputVolume = 0x02,
|
||||||
SetPCMOutputVolume = 0x18,
|
SetPCMOutputVolume = 0x18,
|
||||||
ExtendedAudioID = 0x28,
|
ExtendedAudioID = 0x28,
|
||||||
|
ExtendedAudioStatusControl = 0x2a,
|
||||||
PCMFrontDACRate = 0x2c,
|
PCMFrontDACRate = 0x2c,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ExtendedAudioMask : u16 {
|
enum ExtendedAudioMask : u16 {
|
||||||
VariableRatePCMAudio = 1 << 0,
|
VariableRatePCMAudio = 1 << 0,
|
||||||
|
DoubleRatePCMAudio = 1 << 1,
|
||||||
Revision = 3 << 10,
|
Revision = 3 << 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ExtendedAudioStatusControlFlag : u16 {
|
||||||
|
DoubleRateAudio = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum AC97Revision : u8 {
|
enum AC97Revision : u8 {
|
||||||
Revision21OrEarlier = 0b00,
|
Revision21OrEarlier = 0b00,
|
||||||
Revision22 = 0b01,
|
Revision22 = 0b01,
|
||||||
|
@ -150,12 +156,13 @@ private:
|
||||||
void initialize();
|
void initialize();
|
||||||
void reset_pcm_out();
|
void reset_pcm_out();
|
||||||
void set_master_output_volume(u8, u8, Muted);
|
void set_master_output_volume(u8, u8, Muted);
|
||||||
void set_pcm_output_sample_rate(u16);
|
ErrorOr<void> set_pcm_output_sample_rate(u32);
|
||||||
void set_pcm_output_volume(u8, u8, Muted);
|
void set_pcm_output_volume(u8, u8, Muted);
|
||||||
ErrorOr<void> write_single_buffer(UserOrKernelBuffer const&, size_t, size_t);
|
ErrorOr<void> write_single_buffer(UserOrKernelBuffer const&, size_t, size_t);
|
||||||
|
|
||||||
OwnPtr<Memory::Region> m_buffer_descriptor_list;
|
OwnPtr<Memory::Region> m_buffer_descriptor_list;
|
||||||
u8 m_buffer_descriptor_list_index = 0;
|
u8 m_buffer_descriptor_list_index = 0;
|
||||||
|
bool m_double_rate_pcm_enabled = false;
|
||||||
IOAddress m_io_mixer_base;
|
IOAddress m_io_mixer_base;
|
||||||
IOAddress m_io_bus_base;
|
IOAddress m_io_bus_base;
|
||||||
WaitQueue m_irq_queue;
|
WaitQueue m_irq_queue;
|
||||||
|
@ -163,7 +170,7 @@ private:
|
||||||
u8 m_output_buffer_page_count = 4;
|
u8 m_output_buffer_page_count = 4;
|
||||||
u8 m_output_buffer_page_index = 0;
|
u8 m_output_buffer_page_index = 0;
|
||||||
AC97Channel m_pcm_out_channel;
|
AC97Channel m_pcm_out_channel;
|
||||||
u16 m_sample_rate = 44100;
|
u32 m_sample_rate = 44100;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,9 +123,10 @@ ErrorOr<void> SB16::ioctl(OpenFileDescription&, unsigned request, Userspace<void
|
||||||
return copy_to_user(output, &m_sample_rate);
|
return copy_to_user(output, &m_sample_rate);
|
||||||
}
|
}
|
||||||
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
|
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
|
||||||
auto sample_rate_value = static_cast<u16>(arg.ptr());
|
auto sample_rate_input = static_cast<u32>(arg.ptr());
|
||||||
if (sample_rate_value == 0)
|
if (sample_rate_input == 0 || sample_rate_input > 44100)
|
||||||
return EINVAL;
|
return ENOTSUP;
|
||||||
|
auto sample_rate_value = static_cast<u16>(sample_rate_input);
|
||||||
if (m_sample_rate != sample_rate_value)
|
if (m_sample_rate != sample_rate_value)
|
||||||
set_sample_rate(sample_rate_value);
|
set_sample_rate(sample_rate_value);
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -11,8 +11,8 @@ endpoint AudioServer
|
||||||
set_self_volume(double volume) => ()
|
set_self_volume(double volume) => ()
|
||||||
|
|
||||||
// Audio device
|
// Audio device
|
||||||
set_sample_rate(u16 sample_rate) => ()
|
set_sample_rate(u32 sample_rate) => ()
|
||||||
get_sample_rate() => (u16 sample_rate)
|
get_sample_rate() => (u32 sample_rate)
|
||||||
|
|
||||||
// Buffer playback
|
// Buffer playback
|
||||||
enqueue_buffer(Core::AnonymousBuffer buffer, i32 buffer_id, int sample_count) => (bool success)
|
enqueue_buffer(Core::AnonymousBuffer buffer, i32 buffer_id, int sample_count) => (bool success)
|
||||||
|
|
|
@ -73,7 +73,7 @@ Messages::AudioServer::GetSampleRateResponse ClientConnection::get_sample_rate()
|
||||||
return { m_mixer.audiodevice_get_sample_rate() };
|
return { m_mixer.audiodevice_get_sample_rate() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientConnection::set_sample_rate(u16 sample_rate)
|
void ClientConnection::set_sample_rate(u32 sample_rate)
|
||||||
{
|
{
|
||||||
m_mixer.audiodevice_set_sample_rate(sample_rate);
|
m_mixer.audiodevice_set_sample_rate(sample_rate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ private:
|
||||||
virtual Messages::AudioServer::GetPlayingBufferResponse get_playing_buffer() override;
|
virtual Messages::AudioServer::GetPlayingBufferResponse get_playing_buffer() override;
|
||||||
virtual Messages::AudioServer::GetMutedResponse get_muted() override;
|
virtual Messages::AudioServer::GetMutedResponse get_muted() override;
|
||||||
virtual void set_muted(bool) override;
|
virtual void set_muted(bool) override;
|
||||||
virtual void set_sample_rate(u16 sample_rate) override;
|
virtual void set_sample_rate(u32 sample_rate) override;
|
||||||
virtual Messages::AudioServer::GetSampleRateResponse get_sample_rate() override;
|
virtual Messages::AudioServer::GetSampleRateResponse get_sample_rate() override;
|
||||||
|
|
||||||
Mixer& m_mixer;
|
Mixer& m_mixer;
|
||||||
|
|
|
@ -165,7 +165,7 @@ void Mixer::set_muted(bool muted)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int Mixer::audiodevice_set_sample_rate(u16 sample_rate)
|
int Mixer::audiodevice_set_sample_rate(u32 sample_rate)
|
||||||
{
|
{
|
||||||
int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_SET_SAMPLE_RATE, sample_rate);
|
int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_SET_SAMPLE_RATE, sample_rate);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
|
@ -173,9 +173,9 @@ int Mixer::audiodevice_set_sample_rate(u16 sample_rate)
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 Mixer::audiodevice_get_sample_rate() const
|
u32 Mixer::audiodevice_get_sample_rate() const
|
||||||
{
|
{
|
||||||
u16 sample_rate = 0;
|
u32 sample_rate = 0;
|
||||||
int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_GET_SAMPLE_RATE, &sample_rate);
|
int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_GET_SAMPLE_RATE, &sample_rate);
|
||||||
if (code != 0)
|
if (code != 0)
|
||||||
dbgln("Error while getting sample rate: ioctl error: {}", strerror(errno));
|
dbgln("Error while getting sample rate: ioctl error: {}", strerror(errno));
|
||||||
|
|
|
@ -119,8 +119,8 @@ public:
|
||||||
bool is_muted() const { return m_muted; }
|
bool is_muted() const { return m_muted; }
|
||||||
void set_muted(bool);
|
void set_muted(bool);
|
||||||
|
|
||||||
int audiodevice_set_sample_rate(u16 sample_rate);
|
int audiodevice_set_sample_rate(u32 sample_rate);
|
||||||
u16 audiodevice_get_sample_rate() const;
|
u32 audiodevice_get_sample_rate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mixer(NonnullRefPtr<Core::ConfigFile> config);
|
Mixer(NonnullRefPtr<Core::ConfigFile> config);
|
||||||
|
|
|
@ -81,7 +81,7 @@ int main(int argc, char** argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AudioVariable::SampleRate: {
|
case AudioVariable::SampleRate: {
|
||||||
u16 sample_rate = audio_client->get_sample_rate();
|
u32 sample_rate = audio_client->get_sample_rate();
|
||||||
if (human_mode)
|
if (human_mode)
|
||||||
outln("Sample rate: {:5d} Hz", sample_rate);
|
outln("Sample rate: {:5d} Hz", sample_rate);
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue