diff options
-rw-r--r-- | Kernel/Devices/Audio/AC97.cpp | 46 | ||||
-rw-r--r-- | Kernel/Devices/Audio/AC97.h | 11 | ||||
-rw-r--r-- | Kernel/Devices/Audio/SB16.cpp | 7 | ||||
-rw-r--r-- | Userland/Services/AudioServer/AudioServer.ipc | 4 | ||||
-rw-r--r-- | Userland/Services/AudioServer/ClientConnection.cpp | 2 | ||||
-rw-r--r-- | Userland/Services/AudioServer/ClientConnection.h | 2 | ||||
-rw-r--r-- | Userland/Services/AudioServer/Mixer.cpp | 6 | ||||
-rw-r--r-- | Userland/Services/AudioServer/Mixer.h | 4 | ||||
-rw-r--r-- | Userland/Utilities/asctl.cpp | 2 |
9 files changed, 56 insertions, 28 deletions
diff --git a/Kernel/Devices/Audio/AC97.cpp b/Kernel/Devices/Audio/AC97.cpp index f2e0302cf9..c8161ddff0 100644 --- a/Kernel/Devices/Audio/AC97.cpp +++ b/Kernel/Devices/Audio/AC97.cpp @@ -13,6 +13,10 @@ namespace Kernel { 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) { 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>(); control |= GlobalControlFlag::GPIInterruptEnable; control |= GlobalControlFlag::AC97ColdReset; - m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).out<u32>(control); + m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).out(control); // 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) == 1); + 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>(); + 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 set_master_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(); enable_irq(); } @@ -116,15 +129,14 @@ ErrorOr<void> AC97::ioctl(OpenFileDescription&, unsigned request, Userspace<void { switch (request) { 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); } case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: { - auto sample_rate_value = static_cast<u16>(arg.ptr()); - if (sample_rate_value == 0) - return EINVAL; - if (m_sample_rate != sample_rate_value) - set_pcm_output_sample_rate(sample_rate_value); + auto sample_rate = static_cast<u32>(arg.ptr()); + if (sample_rate == m_sample_rate) + return {}; + TRY(set_pcm_output_sample_rate(sample_rate)); return {}; } 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); } -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); - pcm_front_dac_rate_register.out(sample_rate); - m_sample_rate = pcm_front_dac_rate_register.in<u16>(); + pcm_front_dac_rate_register.out<u16>(shifted_sample_rate); + 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); + + return {}; } void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute) diff --git a/Kernel/Devices/Audio/AC97.h b/Kernel/Devices/Audio/AC97.h index db85ad5452..0fa5a51d41 100644 --- a/Kernel/Devices/Audio/AC97.h +++ b/Kernel/Devices/Audio/AC97.h @@ -43,14 +43,20 @@ private: SetMasterOutputVolume = 0x02, SetPCMOutputVolume = 0x18, ExtendedAudioID = 0x28, + ExtendedAudioStatusControl = 0x2a, PCMFrontDACRate = 0x2c, }; enum ExtendedAudioMask : u16 { VariableRatePCMAudio = 1 << 0, + DoubleRatePCMAudio = 1 << 1, Revision = 3 << 10, }; + enum ExtendedAudioStatusControlFlag : u16 { + DoubleRateAudio = 1 << 1, + }; + enum AC97Revision : u8 { Revision21OrEarlier = 0b00, Revision22 = 0b01, @@ -150,12 +156,13 @@ private: void initialize(); void reset_pcm_out(); 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); ErrorOr<void> write_single_buffer(UserOrKernelBuffer const&, size_t, size_t); OwnPtr<Memory::Region> m_buffer_descriptor_list; u8 m_buffer_descriptor_list_index = 0; + bool m_double_rate_pcm_enabled = false; IOAddress m_io_mixer_base; IOAddress m_io_bus_base; WaitQueue m_irq_queue; @@ -163,7 +170,7 @@ private: u8 m_output_buffer_page_count = 4; u8 m_output_buffer_page_index = 0; AC97Channel m_pcm_out_channel; - u16 m_sample_rate = 44100; + u32 m_sample_rate = 44100; }; } diff --git a/Kernel/Devices/Audio/SB16.cpp b/Kernel/Devices/Audio/SB16.cpp index e5bfef119d..ce2cdc8d63 100644 --- a/Kernel/Devices/Audio/SB16.cpp +++ b/Kernel/Devices/Audio/SB16.cpp @@ -123,9 +123,10 @@ ErrorOr<void> SB16::ioctl(OpenFileDescription&, unsigned request, Userspace<void return copy_to_user(output, &m_sample_rate); } case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: { - auto sample_rate_value = static_cast<u16>(arg.ptr()); - if (sample_rate_value == 0) - return EINVAL; + auto sample_rate_input = static_cast<u32>(arg.ptr()); + if (sample_rate_input == 0 || sample_rate_input > 44100) + return ENOTSUP; + auto sample_rate_value = static_cast<u16>(sample_rate_input); if (m_sample_rate != sample_rate_value) set_sample_rate(sample_rate_value); return {}; diff --git a/Userland/Services/AudioServer/AudioServer.ipc b/Userland/Services/AudioServer/AudioServer.ipc index f34a84532b..d0396bad7e 100644 --- a/Userland/Services/AudioServer/AudioServer.ipc +++ b/Userland/Services/AudioServer/AudioServer.ipc @@ -11,8 +11,8 @@ endpoint AudioServer set_self_volume(double volume) => () // Audio device - set_sample_rate(u16 sample_rate) => () - get_sample_rate() => (u16 sample_rate) + set_sample_rate(u32 sample_rate) => () + get_sample_rate() => (u32 sample_rate) // Buffer playback enqueue_buffer(Core::AnonymousBuffer buffer, i32 buffer_id, int sample_count) => (bool success) diff --git a/Userland/Services/AudioServer/ClientConnection.cpp b/Userland/Services/AudioServer/ClientConnection.cpp index be04b960b5..ee84a59536 100644 --- a/Userland/Services/AudioServer/ClientConnection.cpp +++ b/Userland/Services/AudioServer/ClientConnection.cpp @@ -73,7 +73,7 @@ Messages::AudioServer::GetSampleRateResponse ClientConnection::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); } diff --git a/Userland/Services/AudioServer/ClientConnection.h b/Userland/Services/AudioServer/ClientConnection.h index a4d2d6a177..be5458d4e1 100644 --- a/Userland/Services/AudioServer/ClientConnection.h +++ b/Userland/Services/AudioServer/ClientConnection.h @@ -49,7 +49,7 @@ private: virtual Messages::AudioServer::GetPlayingBufferResponse get_playing_buffer() override; virtual Messages::AudioServer::GetMutedResponse get_muted() 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; Mixer& m_mixer; diff --git a/Userland/Services/AudioServer/Mixer.cpp b/Userland/Services/AudioServer/Mixer.cpp index 04b95c138e..27b6daf1bf 100644 --- a/Userland/Services/AudioServer/Mixer.cpp +++ b/Userland/Services/AudioServer/Mixer.cpp @@ -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); if (code != 0) @@ -173,9 +173,9 @@ int Mixer::audiodevice_set_sample_rate(u16 sample_rate) 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); if (code != 0) dbgln("Error while getting sample rate: ioctl error: {}", strerror(errno)); diff --git a/Userland/Services/AudioServer/Mixer.h b/Userland/Services/AudioServer/Mixer.h index 0cd48c8500..9c64ab0eec 100644 --- a/Userland/Services/AudioServer/Mixer.h +++ b/Userland/Services/AudioServer/Mixer.h @@ -119,8 +119,8 @@ public: bool is_muted() const { return m_muted; } void set_muted(bool); - int audiodevice_set_sample_rate(u16 sample_rate); - u16 audiodevice_get_sample_rate() const; + int audiodevice_set_sample_rate(u32 sample_rate); + u32 audiodevice_get_sample_rate() const; private: Mixer(NonnullRefPtr<Core::ConfigFile> config); diff --git a/Userland/Utilities/asctl.cpp b/Userland/Utilities/asctl.cpp index c2ffe42db8..8d064880d7 100644 --- a/Userland/Utilities/asctl.cpp +++ b/Userland/Utilities/asctl.cpp @@ -81,7 +81,7 @@ int main(int argc, char** argv) break; } case AudioVariable::SampleRate: { - u16 sample_rate = audio_client->get_sample_rate(); + u32 sample_rate = audio_client->get_sample_rate(); if (human_mode) outln("Sample rate: {:5d} Hz", sample_rate); else |