diff --git a/kernel/include/kernel/Audio/AC97/Controller.h b/kernel/include/kernel/Audio/AC97/Controller.h index 404faa7f..5e4d6026 100644 --- a/kernel/include/kernel/Audio/AC97/Controller.h +++ b/kernel/include/kernel/Audio/AC97/Controller.h @@ -6,7 +6,7 @@ namespace Kernel { - class AC97AudioController : public AudioController, public Interruptable + class AC97AudioController final : public AudioController, public Interruptable { public: static BAN::ErrorOr create(PCI::Device& pci_device); @@ -23,11 +23,15 @@ namespace Kernel uint32_t get_current_pin() const override { return 0; } BAN::ErrorOr set_current_pin(uint32_t pin) override { if (pin != 0) return BAN::Error::from_errno(EINVAL); return {}; } + BAN::ErrorOr set_volume_mdB(int32_t) override; + private: AC97AudioController(PCI::Device& pci_device) : m_pci_device(pci_device) { } + uint32_t get_volume_data() const; + BAN::ErrorOr initialize(); BAN::ErrorOr initialize_bld(); BAN::ErrorOr initialize_interrupts(); diff --git a/kernel/include/kernel/Audio/Controller.h b/kernel/include/kernel/Audio/Controller.h index 7fa8bbbd..698ebe0b 100644 --- a/kernel/include/kernel/Audio/Controller.h +++ b/kernel/include/kernel/Audio/Controller.h @@ -4,6 +4,8 @@ #include #include +#include + namespace Kernel { @@ -28,6 +30,8 @@ namespace Kernel virtual uint32_t get_current_pin() const = 0; virtual BAN::ErrorOr set_current_pin(uint32_t) = 0; + virtual BAN::ErrorOr set_volume_mdB(int32_t) = 0; + bool can_read_impl() const override { return false; } bool can_write_impl() const override { SpinLockGuard _(m_spinlock); return !m_sample_data->full(); } bool has_error_impl() const override { return false; } @@ -44,6 +48,8 @@ namespace Kernel static constexpr size_t m_sample_data_capacity = 1 << 20; BAN::UniqPtr m_sample_data; + snd_volume_info m_volume_info {}; + private: const dev_t m_rdev; char m_name[10] {}; diff --git a/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h b/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h index 1f56b05c..2640745c 100644 --- a/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h +++ b/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h @@ -8,7 +8,7 @@ namespace Kernel class HDAudioController; - class HDAudioFunctionGroup : public AudioController + class HDAudioFunctionGroup final : public AudioController { public: static BAN::ErrorOr> create(BAN::RefPtr, uint8_t cid, HDAudio::AFGNode&&); @@ -24,6 +24,8 @@ namespace Kernel uint32_t get_current_pin() const override; BAN::ErrorOr set_current_pin(uint32_t) override; + BAN::ErrorOr set_volume_mdB(int32_t) override; + void handle_new_data() override; private: @@ -46,7 +48,6 @@ namespace Kernel BAN::ErrorOr recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector& path); uint16_t get_format_data() const; - uint16_t get_volume_data() const; size_t bdl_offset() const; diff --git a/kernel/include/kernel/Audio/HDAudio/Definitions.h b/kernel/include/kernel/Audio/HDAudio/Definitions.h index 8bd4ab97..cdacd656 100644 --- a/kernel/include/kernel/Audio/HDAudio/Definitions.h +++ b/kernel/include/kernel/Audio/HDAudio/Definitions.h @@ -55,6 +55,16 @@ namespace Kernel::HDAudio } pin_complex; }; + struct Amplifier + { + uint8_t offset; + uint8_t num_steps; + uint8_t step_size; + bool mute; + }; + + BAN::Optional output_amplifier; + BAN::Vector connections; }; diff --git a/kernel/kernel/Audio/AC97/Controller.cpp b/kernel/kernel/Audio/AC97/Controller.cpp index 3eabfb46..65ed3406 100644 --- a/kernel/kernel/Audio/AC97/Controller.cpp +++ b/kernel/kernel/Audio/AC97/Controller.cpp @@ -137,8 +137,27 @@ namespace Kernel // Reset mixer to default values m_mixer->write16(AudioMixerRegister::Reset, 0); - // Master volume 100%, no mute - m_mixer->write16(AudioMixerRegister::MasterVolume, 0x0000); + // Master volumes + m_mixer->write16(AudioMixerRegister::MasterVolume, 0x2020); + if (m_mixer->read16(AudioMixerRegister::MasterVolume) == 0x2020) + { + m_volume_info = { + .min_mdB = -94500, + .max_mdB = 0, + .step_mdB = 1500, + .mdB = 0, + }; + } + else + { + m_volume_info = { + .min_mdB = -46500, + .max_mdB = 0, + .step_mdB = 1500, + .mdB = 0, + }; + } + m_mixer->write16(AudioMixerRegister::MasterVolume, get_volume_data()); // PCM output volume left/right +0 db, no mute m_mixer->write16(AudioMixerRegister::PCMOutVolume, 0x0808); @@ -187,6 +206,19 @@ namespace Kernel return {}; } + uint32_t AC97AudioController::get_volume_data() const + { + const uint32_t steps = (-m_volume_info.mdB + m_volume_info.step_mdB / 2) / m_volume_info.step_mdB; + return (steps << 8) | steps; + } + + BAN::ErrorOr AC97AudioController::set_volume_mdB(int32_t mdB) + { + m_volume_info.mdB = BAN::Math::clamp(mdB, m_volume_info.min_mdB, m_volume_info.max_mdB); + m_mixer->write16(AudioMixerRegister::MasterVolume, get_volume_data()); + return {}; + } + void AC97AudioController::handle_new_data() { ASSERT(m_spinlock.current_processor_has_lock()); diff --git a/kernel/kernel/Audio/Controller.cpp b/kernel/kernel/Audio/Controller.cpp index 017dc161..6fa6781f 100644 --- a/kernel/kernel/Audio/Controller.cpp +++ b/kernel/kernel/Audio/Controller.cpp @@ -105,6 +105,12 @@ namespace Kernel case SND_SET_PIN: TRY(set_current_pin(*static_cast(arg))); return 0; + case SND_GET_VOLUME_INFO: + *static_cast(arg) = m_volume_info; + return 0; + case SND_SET_VOLUME_MDB: + TRY(set_volume_mdB(*static_cast(arg))); + return 0; } return CharacterDevice::ioctl_impl(cmd, arg); diff --git a/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp b/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp index 4d54f682..c30e50de 100644 --- a/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp +++ b/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp @@ -136,6 +136,37 @@ namespace Kernel return BAN::Error::from_errno(ENOTSUP); } + BAN::ErrorOr HDAudioFunctionGroup::set_volume_mdB(int32_t mdB) + { + mdB = BAN::Math::clamp(mdB, m_volume_info.min_mdB, m_volume_info.max_mdB); + + const auto& path = m_output_paths[m_output_path_index]; + for (size_t i = 0; i < path.size(); i++) + { + if (!path[i]->output_amplifier.has_value()) + continue; + + const int32_t step_round = (mdB >= 0) + ? +m_volume_info.step_mdB / 2 + : -m_volume_info.step_mdB / 2; + const uint32_t step = (mdB + step_round) / m_volume_info.step_mdB + path[i]->output_amplifier->offset; + const uint32_t volume = 0b1'0'1'1'0000'0'0000000 | step; + + TRY(m_controller->send_command({ + .data = static_cast(volume & 0xFF), + .command = static_cast(0x300 | (volume >> 8)), + .node_index = path[i]->id, + .codec_address = m_cid, + })); + + break; + } + + m_volume_info.mdB = mdB; + + return {}; + } + size_t HDAudioFunctionGroup::bdl_offset() const { const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t); @@ -291,13 +322,6 @@ namespace Kernel return 0b0'0'000'000'0'001'0001; } - uint16_t HDAudioFunctionGroup::get_volume_data() const - { - // TODO: don't hardcode this - // left and right output, no mute, max gain - return 0b1'0'1'1'0000'0'1111111; - } - BAN::ErrorOr HDAudioFunctionGroup::enable_output_path(uint8_t index) { ASSERT(index < m_output_paths.size()); @@ -318,7 +342,6 @@ namespace Kernel } const auto format = get_format_data(); - const auto volume = get_volume_data(); for (size_t i = 0; i < path.size(); i++) { @@ -347,13 +370,17 @@ namespace Kernel })); } - // set volume - TRY(m_controller->send_command({ - .data = static_cast(volume & 0xFF), - .command = static_cast(0x300 | (volume >> 8)), - .node_index = path[i]->id, - .codec_address = m_cid, - })); + // set volume to 0 dB, no mute + if (path[i]->output_amplifier.has_value()) + { + const uint32_t volume = 0b1'0'1'1'0000'0'0000000 | path[i]->output_amplifier->offset; + TRY(m_controller->send_command({ + .data = static_cast(volume & 0xFF), + .command = static_cast(0x300 | (volume >> 8)), + .node_index = path[i]->id, + .codec_address = m_cid, + })); + } switch (path[i]->type) { @@ -398,6 +425,41 @@ namespace Kernel } } + // update volume info to this path + m_volume_info.min_mdB = 0; + m_volume_info.max_mdB = 0; + m_volume_info.step_mdB = 0; + for (size_t i = 0; i < path.size(); i++) + { + if (!path[i]->output_amplifier.has_value()) + continue; + const auto& amp = path[i]->output_amplifier.value(); + + const int32_t step_mdB = amp.step_size * 250; + m_volume_info.step_mdB = step_mdB; + m_volume_info.min_mdB = -amp.offset * step_mdB; + m_volume_info.max_mdB = (amp.num_steps - amp.offset) * step_mdB; + m_volume_info.mdB = BAN::Math::clamp(m_volume_info.mdB, m_volume_info.min_mdB, m_volume_info.max_mdB); + + const int32_t step_round = (m_volume_info.mdB >= 0) + ? +step_mdB / 2 + : -step_mdB / 2; + const uint32_t step = (m_volume_info.mdB + step_round) / step_mdB + amp.offset; + const uint32_t volume = 0b1'0'1'1'0000'0'0000000 | step; + + TRY(m_controller->send_command({ + .data = static_cast(volume & 0xFF), + .command = static_cast(0x300 | (volume >> 8)), + .node_index = path[i]->id, + .codec_address = m_cid, + })); + + break; + } + + if (m_volume_info.min_mdB == 0 && m_volume_info.max_mdB == 0) + m_volume_info.mdB = 0; + return {}; } diff --git a/kernel/kernel/Audio/HDAudio/Controller.cpp b/kernel/kernel/Audio/HDAudio/Controller.cpp index d4ec4557..07214529 100644 --- a/kernel/kernel/Audio/HDAudio/Controller.cpp +++ b/kernel/kernel/Audio/HDAudio/Controller.cpp @@ -319,6 +319,20 @@ namespace Kernel }; } + if (const uint32_t out_amp_cap = send_command_or_zero(0xF00, 0x12)) + { + const uint8_t offset = (out_amp_cap >> 0) & 0x7F; + const uint8_t num_steps = (out_amp_cap >> 8) & 0x7F; + const uint8_t step_size = (out_amp_cap >> 16) & 0x7F; + const bool mute = (out_amp_cap >> 31); + result.output_amplifier = HDAudio::AFGWidget::Amplifier { + .offset = offset, + .num_steps = num_steps, + .step_size = step_size, + .mute = mute, + }; + } + const uint8_t connection_info = send_command_or_zero(0xF00, 0x0E); const uint8_t conn_width = (connection_info & 0x80) ? 2 : 1; const uint8_t conn_count = connection_info & 0x3F; diff --git a/userspace/libraries/LibC/include/sys/ioctl.h b/userspace/libraries/LibC/include/sys/ioctl.h index aa7d4b77..d1842427 100644 --- a/userspace/libraries/LibC/include/sys/ioctl.h +++ b/userspace/libraries/LibC/include/sys/ioctl.h @@ -5,6 +5,8 @@ __BEGIN_DECLS +#include + #define I_ATMARK 1 #define I_CANPUT 2 #define I_CKBAND 3 @@ -50,6 +52,13 @@ struct winsize #define TIOCGWINSZ 50 #define TIOCSWINSZ 51 +struct snd_volume_info +{ + int32_t min_mdB; + int32_t max_mdB; + int32_t step_mdB; + int32_t mdB; +}; #define SND_GET_CHANNELS 60 /* stores number of channels to uint32_t argument */ #define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */ #define SND_RESET_BUFFER 62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */ @@ -57,6 +66,8 @@ struct winsize #define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device as uint32_t */ #define SND_GET_PIN 65 /* gets the currently active pin as uint32_t */ #define SND_SET_PIN 66 /* sets the currently active pin to uint32_t */ +#define SND_GET_VOLUME_INFO 67 /* gets the current volume as snd_volume_info */ +#define SND_SET_VOLUME_MDB 68 /* sets the current volume to int32_t dB */ #define JOYSTICK_GET_LEDS 80 /* get controller led bitmap to uint8_t argument */ #define JOYSTICK_SET_LEDS 81 /* set controller leds to uint8_t bitmap */