mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:47:45 +00:00
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.
This commit is contained in:
parent
d117c4cccc
commit
70ca8b24dc
2 changed files with 90 additions and 48 deletions
|
@ -43,6 +43,7 @@ UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier pci_device_identifier)
|
||||||
, CharacterDevice(42, 42)
|
, CharacterDevice(42, 42)
|
||||||
, m_io_mixer_base(PCI::get_BAR0(pci_address()) & ~1)
|
, m_io_mixer_base(PCI::get_BAR0(pci_address()) & ~1)
|
||||||
, m_io_bus_base(PCI::get_BAR1(pci_address()) & ~1)
|
, m_io_bus_base(PCI::get_BAR1(pci_address()) & ~1)
|
||||||
|
, m_pcm_out_channel(channel("PCMOut"sv, NativeAudioBusChannel::PCMOutChannel))
|
||||||
{
|
{
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
@ -53,7 +54,7 @@ UNMAP_AFTER_INIT AC97::~AC97()
|
||||||
|
|
||||||
bool AC97::handle_irq(RegisterState const&)
|
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<u16>();
|
auto pcm_out_status = pcm_out_status_register.in<u16>();
|
||||||
|
|
||||||
bool is_dma_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0;
|
bool is_dma_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0;
|
||||||
|
@ -138,16 +139,8 @@ ErrorOr<size_t> AC97::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_
|
||||||
|
|
||||||
void AC97::reset_pcm_out()
|
void AC97::reset_pcm_out()
|
||||||
{
|
{
|
||||||
auto pcm_out_control_register = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutControl);
|
m_pcm_out_channel.reset();
|
||||||
pcm_out_control_register.out(AudioControlRegisterFlag::ResetRegisters);
|
|
||||||
|
|
||||||
while ((pcm_out_control_register.in<u8>() & AudioControlRegisterFlag::ResetRegisters) > 0)
|
|
||||||
IO::delay(50);
|
|
||||||
|
|
||||||
m_output_buffer_dma_running = false;
|
|
||||||
m_buffer_descriptor_list_index = 0;
|
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)
|
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);
|
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<u32>(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<u8>(last_valid_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AC97::set_pcm_output_sample_rate(u16 sample_rate)
|
void AC97::set_pcm_output_sample_rate(u16 sample_rate)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
@ -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);
|
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<u8>();
|
|
||||||
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<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& data, size_t length)
|
ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& data, size_t length)
|
||||||
{
|
{
|
||||||
VERIFY(length <= PAGE_SIZE);
|
VERIFY(length <= PAGE_SIZE);
|
||||||
|
@ -211,10 +183,10 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const&
|
||||||
|
|
||||||
// Block until we can write into an unused buffer
|
// Block until we can write into an unused buffer
|
||||||
do {
|
do {
|
||||||
auto pcm_out_status = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutStatus).in<u16>();
|
auto pcm_out_status = m_pcm_out_channel.reg(AC97Channel::Register::Status).in<u16>();
|
||||||
auto is_dma_controller_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0;
|
auto is_dma_controller_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0;
|
||||||
auto current_index = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutCurrentIndex).in<u8>();
|
auto current_index = m_pcm_out_channel.reg(AC97Channel::Register::CurrentIndexValue).in<u8>();
|
||||||
auto last_valid_index = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutLastValidIndex).in<u8>();
|
auto last_valid_index = m_pcm_out_channel.reg(AC97Channel::Register::LastValidIndex).in<u8>();
|
||||||
|
|
||||||
auto head_distance = static_cast<int>(last_valid_index) - current_index;
|
auto head_distance = static_cast<int>(last_valid_index) - current_index;
|
||||||
if (head_distance < 0)
|
if (head_distance < 0)
|
||||||
|
@ -226,14 +198,14 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const&
|
||||||
break;
|
break;
|
||||||
|
|
||||||
m_irq_queue.wait_forever("AC97"sv);
|
m_irq_queue.wait_forever("AC97"sv);
|
||||||
} while (m_output_buffer_dma_running);
|
} while (m_pcm_out_channel.dma_running());
|
||||||
|
|
||||||
sti();
|
sti();
|
||||||
|
|
||||||
// Copy data from userspace into one of our buffers
|
// 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));
|
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();
|
reset_pcm_out();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,10 +215,12 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const&
|
||||||
auto list_entry = &list_entries[m_buffer_descriptor_list_index];
|
auto list_entry = &list_entries[m_buffer_descriptor_list_index];
|
||||||
list_entry->buffer_pointer = static_cast<u32>(m_output_buffer->physical_page(m_output_buffer_page_index)->paddr().get());
|
list_entry->buffer_pointer = static_cast<u32>(m_output_buffer->physical_page(m_output_buffer_page_index)->paddr().get());
|
||||||
list_entry->control_and_length = number_of_samples | BufferDescriptorListEntryFlags::InterruptOnCompletion;
|
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) {
|
auto buffer_address = static_cast<u32>(m_buffer_descriptor_list->physical_page(0)->paddr().get());
|
||||||
start_output_buffer_dma();
|
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;
|
m_output_buffer_page_index = (m_output_buffer_page_index + 1) % m_output_buffer_page_count;
|
||||||
|
@ -255,4 +229,37 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const&
|
||||||
return length;
|
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<u8>() & 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<u8>();
|
||||||
|
control |= AudioControlRegisterFlag::RunPauseBusMaster;
|
||||||
|
control |= AudioControlRegisterFlag::FIFOErrorInterruptEnable;
|
||||||
|
control |= AudioControlRegisterFlag::InterruptOnCompletionEnable;
|
||||||
|
control_register.out(control);
|
||||||
|
|
||||||
|
m_dma_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,13 +57,16 @@ private:
|
||||||
Revision23 = 0b10,
|
Revision23 = 0b10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum NativeAudioBusChannel : u8 {
|
||||||
|
PCMInChannel = 0x00,
|
||||||
|
PCMOutChannel = 0x10,
|
||||||
|
MicrophoneInChannel = 0x20,
|
||||||
|
Microphone2Channel = 0x40,
|
||||||
|
PCMIn2Channel = 0x50,
|
||||||
|
SPDIFChannel = 0x60,
|
||||||
|
};
|
||||||
|
|
||||||
enum NativeAudioBusRegister : u8 {
|
enum NativeAudioBusRegister : u8 {
|
||||||
PCMOutBufferDescriptorListBaseAddress = 0x10,
|
|
||||||
PCMOutCurrentIndex = 0x14,
|
|
||||||
PCMOutLastValidIndex = 0x15,
|
|
||||||
PCMOutStatus = 0x16,
|
|
||||||
PCMOutPrefetchedIndex = 0x1a,
|
|
||||||
PCMOutControl = 0x1b,
|
|
||||||
GlobalControl = 0x2c,
|
GlobalControl = 0x2c,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,6 +105,39 @@ private:
|
||||||
InterruptOnCompletion = 1u << 31,
|
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);
|
AC97(PCI::DeviceIdentifier);
|
||||||
|
|
||||||
// ^IRQHandler
|
// ^IRQHandler
|
||||||
|
@ -110,13 +146,12 @@ private:
|
||||||
// ^CharacterDevice
|
// ^CharacterDevice
|
||||||
virtual StringView class_name() const override { return "AC97"sv; }
|
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 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);
|
void set_pcm_output_sample_rate(u16);
|
||||||
void set_pcm_output_volume(u8, u8, Muted);
|
void set_pcm_output_volume(u8, u8, Muted);
|
||||||
void set_output_buffer_dma_lvi(u8);
|
|
||||||
void start_output_buffer_dma();
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -124,9 +159,9 @@ private:
|
||||||
IOAddress m_io_bus_base;
|
IOAddress m_io_bus_base;
|
||||||
WaitQueue m_irq_queue;
|
WaitQueue m_irq_queue;
|
||||||
OwnPtr<Memory::Region> m_output_buffer;
|
OwnPtr<Memory::Region> m_output_buffer;
|
||||||
bool m_output_buffer_dma_running = false;
|
|
||||||
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;
|
||||||
u16 m_sample_rate = 44100;
|
u16 m_sample_rate = 44100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue