From 70ca8b24dc9eaaf7c6637daf15f445b31e890e25 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Tue, 23 Nov 2021 12:36:32 +0100 Subject: [PATCH] Kernel: Add generic channel support to AC97 This factors out some hardcoded PCMOut registers into a new private class called AC97Channel, which wraps around a channel's registers and provides some shared functionality. No functional changes. --- Kernel/Devices/Audio/AC97.cpp | 85 +++++++++++++++++++---------------- Kernel/Devices/Audio/AC97.h | 53 ++++++++++++++++++---- 2 files changed, 90 insertions(+), 48 deletions(-) diff --git a/Kernel/Devices/Audio/AC97.cpp b/Kernel/Devices/Audio/AC97.cpp index f6be6329dd..856250d953 100644 --- a/Kernel/Devices/Audio/AC97.cpp +++ b/Kernel/Devices/Audio/AC97.cpp @@ -43,6 +43,7 @@ UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier pci_device_identifier) , CharacterDevice(42, 42) , m_io_mixer_base(PCI::get_BAR0(pci_address()) & ~1) , m_io_bus_base(PCI::get_BAR1(pci_address()) & ~1) + , m_pcm_out_channel(channel("PCMOut"sv, NativeAudioBusChannel::PCMOutChannel)) { initialize(); } @@ -53,7 +54,7 @@ UNMAP_AFTER_INIT AC97::~AC97() bool AC97::handle_irq(RegisterState const&) { - auto pcm_out_status_register = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutStatus); + auto pcm_out_status_register = m_pcm_out_channel.reg(AC97Channel::Register::Status); auto pcm_out_status = pcm_out_status_register.in(); bool is_dma_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0; @@ -138,16 +139,8 @@ ErrorOr AC97::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_ void AC97::reset_pcm_out() { - auto pcm_out_control_register = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutControl); - pcm_out_control_register.out(AudioControlRegisterFlag::ResetRegisters); - - while ((pcm_out_control_register.in() & AudioControlRegisterFlag::ResetRegisters) > 0) - IO::delay(50); - - m_output_buffer_dma_running = false; + m_pcm_out_channel.reset(); m_buffer_descriptor_list_index = 0; - - dbgln("AC97 @ {}: PCM out is reset", pci_address()); } void AC97::set_master_output_volume(u8 left_channel, u8 right_channel, Muted mute) @@ -158,13 +151,6 @@ 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_output_buffer_dma_lvi(u8 last_valid_index) -{ - auto buffer_address = static_cast(m_buffer_descriptor_list->physical_page(0)->paddr().get()); - m_io_bus_base.offset(NativeAudioBusRegister::PCMOutBufferDescriptorListBaseAddress).out(buffer_address); - m_io_bus_base.offset(NativeAudioBusRegister::PCMOutLastValidIndex).out(last_valid_index); -} - void AC97::set_pcm_output_sample_rate(u16 sample_rate) { auto pcm_front_dac_rate_register = m_io_mixer_base.offset(NativeAudioMixerRegister::PCMFrontDACRate); @@ -181,20 +167,6 @@ void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute) m_io_mixer_base.offset(NativeAudioMixerRegister::SetPCMOutputVolume).out(volume_value); } -void AC97::start_output_buffer_dma() -{ - dbgln("AC97 @ {}: starting DMA engine", pci_address()); - - auto pcm_out_control_register = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutControl); - auto pcm_out_control = pcm_out_control_register.in(); - pcm_out_control |= AudioControlRegisterFlag::RunPauseBusMaster; - pcm_out_control |= AudioControlRegisterFlag::FIFOErrorInterruptEnable; - pcm_out_control |= AudioControlRegisterFlag::InterruptOnCompletionEnable; - pcm_out_control_register.out(pcm_out_control); - - m_output_buffer_dma_running = true; -} - ErrorOr AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& data, size_t length) { VERIFY(length <= PAGE_SIZE); @@ -211,10 +183,10 @@ ErrorOr AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& // Block until we can write into an unused buffer do { - auto pcm_out_status = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutStatus).in(); + auto pcm_out_status = m_pcm_out_channel.reg(AC97Channel::Register::Status).in(); auto is_dma_controller_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0; - auto current_index = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutCurrentIndex).in(); - auto last_valid_index = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutLastValidIndex).in(); + auto current_index = m_pcm_out_channel.reg(AC97Channel::Register::CurrentIndexValue).in(); + auto last_valid_index = m_pcm_out_channel.reg(AC97Channel::Register::LastValidIndex).in(); auto head_distance = static_cast(last_valid_index) - current_index; if (head_distance < 0) @@ -226,14 +198,14 @@ ErrorOr AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& break; m_irq_queue.wait_forever("AC97"sv); - } while (m_output_buffer_dma_running); + } while (m_pcm_out_channel.dma_running()); sti(); // Copy data from userspace into one of our buffers TRY(data.read(m_output_buffer->vaddr_from_page_index(m_output_buffer_page_index).as_ptr(), length)); - if (!m_output_buffer_dma_running) { + if (!m_pcm_out_channel.dma_running()) { reset_pcm_out(); } @@ -243,10 +215,12 @@ ErrorOr AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& auto list_entry = &list_entries[m_buffer_descriptor_list_index]; list_entry->buffer_pointer = static_cast(m_output_buffer->physical_page(m_output_buffer_page_index)->paddr().get()); list_entry->control_and_length = number_of_samples | BufferDescriptorListEntryFlags::InterruptOnCompletion; - set_output_buffer_dma_lvi(m_buffer_descriptor_list_index); - if (!m_output_buffer_dma_running) { - start_output_buffer_dma(); + auto buffer_address = static_cast(m_buffer_descriptor_list->physical_page(0)->paddr().get()); + m_pcm_out_channel.set_last_valid_index(buffer_address, m_buffer_descriptor_list_index); + + if (!m_pcm_out_channel.dma_running()) { + m_pcm_out_channel.start_dma(); } m_output_buffer_page_index = (m_output_buffer_page_index + 1) % m_output_buffer_page_count; @@ -255,4 +229,37 @@ ErrorOr AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& return length; } +void AC97::AC97Channel::reset() +{ + dbgln("AC97 @ {}: channel {}: resetting", m_device.pci_address(), name()); + + auto control_register = reg(Register::Control); + control_register.out(AudioControlRegisterFlag::ResetRegisters); + + while ((control_register.in() & AudioControlRegisterFlag::ResetRegisters) > 0) + IO::delay(50); + + m_dma_running = false; +} + +void AC97::AC97Channel::set_last_valid_index(u32 buffer_address, u8 last_valid_index) +{ + reg(Register::BufferDescriptorListBaseAddress).out(buffer_address); + reg(Register::LastValidIndex).out(last_valid_index); +} + +void AC97::AC97Channel::start_dma() +{ + dbgln("AC97 @ {}: channel {}: starting DMA engine", m_device.pci_address(), name()); + + auto control_register = reg(Register::Control); + auto control = control_register.in(); + control |= AudioControlRegisterFlag::RunPauseBusMaster; + control |= AudioControlRegisterFlag::FIFOErrorInterruptEnable; + control |= AudioControlRegisterFlag::InterruptOnCompletionEnable; + control_register.out(control); + + m_dma_running = true; +} + } diff --git a/Kernel/Devices/Audio/AC97.h b/Kernel/Devices/Audio/AC97.h index 3df59d08f6..e6b60bf153 100644 --- a/Kernel/Devices/Audio/AC97.h +++ b/Kernel/Devices/Audio/AC97.h @@ -57,13 +57,16 @@ private: Revision23 = 0b10, }; + enum NativeAudioBusChannel : u8 { + PCMInChannel = 0x00, + PCMOutChannel = 0x10, + MicrophoneInChannel = 0x20, + Microphone2Channel = 0x40, + PCMIn2Channel = 0x50, + SPDIFChannel = 0x60, + }; + enum NativeAudioBusRegister : u8 { - PCMOutBufferDescriptorListBaseAddress = 0x10, - PCMOutCurrentIndex = 0x14, - PCMOutLastValidIndex = 0x15, - PCMOutStatus = 0x16, - PCMOutPrefetchedIndex = 0x1a, - PCMOutControl = 0x1b, GlobalControl = 0x2c, }; @@ -102,6 +105,39 @@ private: InterruptOnCompletion = 1u << 31, }; + class AC97Channel { + public: + enum Register : u8 { + BufferDescriptorListBaseAddress = 0x00, + CurrentIndexValue = 0x04, + LastValidIndex = 0x05, + Status = 0x06, + PositionInCurrentBuffer = 0x08, + PrefetchedIndexValue = 0x0a, + Control = 0x0b, + }; + + AC97Channel(AC97& device, StringView name, IOAddress channel_base) + : m_channel_base(channel_base) + , m_device(device) + , m_name(name) + { + } + + bool dma_running() const { return m_dma_running; } + StringView name() const { return m_name; } + IOAddress reg(Register reg) const { return m_channel_base.offset(reg); } + void reset(); + void set_last_valid_index(u32 buffer_address, u8 last_valid_index); + void start_dma(); + + private: + IOAddress m_channel_base; + AC97& m_device; + bool m_dma_running = false; + StringView m_name; + }; + AC97(PCI::DeviceIdentifier); // ^IRQHandler @@ -110,13 +146,12 @@ private: // ^CharacterDevice virtual StringView class_name() const override { return "AC97"sv; } + AC97Channel channel(StringView name, NativeAudioBusChannel channel) { return AC97Channel(*this, name, m_io_bus_base.offset(channel)); } void initialize(); void reset_pcm_out(); void set_master_output_volume(u8, u8, Muted); void set_pcm_output_sample_rate(u16); void set_pcm_output_volume(u8, u8, Muted); - void set_output_buffer_dma_lvi(u8); - void start_output_buffer_dma(); OwnPtr m_buffer_descriptor_list; u8 m_buffer_descriptor_list_index = 0; @@ -124,9 +159,9 @@ private: IOAddress m_io_bus_base; WaitQueue m_irq_queue; OwnPtr m_output_buffer; - bool m_output_buffer_dma_running = false; u8 m_output_buffer_page_count = 4; u8 m_output_buffer_page_index = 0; + AC97Channel m_pcm_out_channel; u16 m_sample_rate = 44100; };