mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05: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)
|
||||
, 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<u16>();
|
||||
|
||||
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()
|
||||
{
|
||||
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<u8>() & 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<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)
|
||||
{
|
||||
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<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)
|
||||
{
|
||||
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
|
||||
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 current_index = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutCurrentIndex).in<u8>();
|
||||
auto last_valid_index = m_io_bus_base.offset(NativeAudioBusRegister::PCMOutLastValidIndex).in<u8>();
|
||||
auto current_index = m_pcm_out_channel.reg(AC97Channel::Register::CurrentIndexValue).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;
|
||||
if (head_distance < 0)
|
||||
|
@ -226,14 +198,14 @@ ErrorOr<size_t> 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<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const&
|
|||
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->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<u32>(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<size_t> 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<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,
|
||||
};
|
||||
|
||||
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<Memory::Region> 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<Memory::Region> 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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue