1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 07:37:44 +00:00
serenity/Kernel/Devices/Audio/AC97.h
Liav A 6efa27537a Kernel/Audio: Introduce a new design architecture for the subsystem
We have 3 new components:
1. The AudioManagement singleton. This class like in other subsystems,
is responsible to find hardware audio controllers and keep a reference
to them.
2. AudioController class - this class is the parent class for hardware
controllers like the Sound Blaster 16 or Intel 82801AA (AC97). For now,
this class has simple interface for getting and controlling sample rate
of audio channels, as well a write interface for specific audio channel
but not reading from it. One AudioController object might have multiple
AudioChannel "child" objects to hold with reference counting.
3. AudioChannel class - this is based on the CharacterDevice class, and
represents hardware PCM audio channel. It facilitates an ioctl interface
which should be consistent across all supported hardware currently.
It has a weak reference to a parent AudioController, and when trying to
write to a channel, it redirects the data to the parent AudioController.
Each audio channel device should be added into a new directory under the
/dev filesystem called "audio".
2022-02-14 11:39:19 +01:00

177 lines
5.1 KiB
C++

/*
* Copyright (c) 2021, Jelle Raaijmakers <jelle@gmta.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Devices/Audio/Controller.h>
#include <Kernel/Devices/CharacterDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
namespace Kernel {
// See: https://www-inst.eecs.berkeley.edu/~cs150/Documents/ac97_r23.pdf
// And: https://www.intel.com/content/dam/doc/manual/io-controller-hub-7-hd-audio-ac97-manual.pdf
class AC97 final
: public AudioController
, public PCI::Device
, public IRQHandler {
public:
static ErrorOr<NonnullRefPtr<AC97>> try_create(PCI::DeviceIdentifier const&);
virtual ~AC97() override;
// ^IRQHandler
virtual StringView purpose() const override { return "AC97"sv; }
private:
enum NativeAudioMixerRegister : u8 {
Reset = 0x00,
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 {
VariableRateAudio = 1 << 0,
DoubleRateAudio = 1 << 1,
};
enum AC97Revision : u8 {
Revision21OrEarlier = 0b00,
Revision22 = 0b01,
Revision23 = 0b10,
};
enum NativeAudioBusChannel : u8 {
PCMInChannel = 0x00,
PCMOutChannel = 0x10,
MicrophoneInChannel = 0x20,
Microphone2Channel = 0x40,
PCMIn2Channel = 0x50,
SPDIFChannel = 0x60,
};
enum NativeAudioBusRegister : u8 {
GlobalControl = 0x2c,
};
enum AudioStatusRegisterFlag : u16 {
DMAControllerHalted = 1 << 0,
CurrentEqualsLastValid = 1 << 1,
LastValidBufferCompletionInterrupt = 1 << 2,
BufferCompletionInterruptStatus = 1 << 3,
FIFOError = 1 << 4,
};
enum AudioControlRegisterFlag : u8 {
RunPauseBusMaster = 1 << 0,
ResetRegisters = 1 << 1,
FIFOErrorInterruptEnable = 1 << 3,
InterruptOnCompletionEnable = 1 << 4,
};
enum GlobalControlFlag : u32 {
GPIInterruptEnable = 1 << 0,
AC97ColdReset = 1 << 1,
};
enum Muted {
Yes,
No,
};
struct BufferDescriptorListEntry {
u32 buffer_pointer;
u32 control_and_length;
};
enum BufferDescriptorListEntryFlags : u32 {
BufferUnderrunPolicy = 1 << 30,
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;
};
explicit AC97(PCI::DeviceIdentifier const&);
// ^IRQHandler
virtual bool handle_irq(const RegisterState&) override;
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);
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);
// ^AudioController
virtual RefPtr<AudioChannel> audio_channel(u32 index) const override;
virtual ErrorOr<size_t> write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) override;
virtual void detect_hardware_audio_channels(Badge<AudioManagement>) override;
virtual ErrorOr<void> set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) override;
virtual ErrorOr<u32> get_pcm_output_sample_rate(size_t channel_index) override;
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;
OwnPtr<Memory::Region> m_output_buffer;
u8 m_output_buffer_page_count = 4;
u8 m_output_buffer_page_index = 0;
AC97Channel m_pcm_out_channel;
u32 m_sample_rate = 0;
bool m_variable_rate_pcm_supported = false;
RefPtr<AudioChannel> m_audio_channel;
};
}