mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 07:28:11 +00:00
Kernel: Move all Graphics-related code into Devices/GPU directory
Like the HID, Audio and Storage subsystem, the Graphics subsystem (which handles GPUs technically) exposes unix device files (typically in /dev). To ensure consistency across the repository, move all related files to a new directory under Kernel/Devices called "GPU". Also remove the redundant "GPU" word from the VirtIO driver directory, and the word "Graphics" from GraphicsManagement.{h,cpp} filenames.
This commit is contained in:
parent
31a7dabf02
commit
9ee098b119
69 changed files with 167 additions and 167 deletions
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/Delay.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<NonnullOwnPtr<IntelAnalogDisplayTranscoder>> IntelAnalogDisplayTranscoder::create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address,
|
||||
PhysicalAddress pipe_registers_start_address, PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_multiplier_register_start_address)
|
||||
{
|
||||
auto transcoder_registers_mapping = TRY(Memory::map_typed<TranscoderRegisters volatile>(transcoder_registers_start_address, sizeof(IntelDisplayTranscoder::TranscoderRegisters), Memory::Region::Access::ReadWrite));
|
||||
auto pipe_registers_mapping = TRY(Memory::map_typed<PipeRegisters volatile>(pipe_registers_start_address, sizeof(IntelDisplayTranscoder::PipeRegisters), Memory::Region::Access::ReadWrite));
|
||||
auto dpll_registers_mapping = TRY(Memory::map_typed<DPLLRegisters volatile>(dpll_registers_start_address, sizeof(DPLLRegisters), Memory::Region::Access::ReadWrite));
|
||||
auto dpll_control_mapping = TRY(Memory::map_typed<DPLLControlRegisters volatile>(dpll_multiplier_register_start_address, sizeof(DPLLControlRegisters), Memory::Region::Access::ReadWrite));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) IntelAnalogDisplayTranscoder(move(transcoder_registers_mapping), move(pipe_registers_mapping), move(dpll_registers_mapping), move(dpll_control_mapping)));
|
||||
}
|
||||
|
||||
IntelAnalogDisplayTranscoder::IntelAnalogDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile> transcoder_registers_mapping,
|
||||
Memory::TypedMapping<PipeRegisters volatile> pipe_registers_mapping, Memory::TypedMapping<DPLLRegisters volatile> dpll_registers_mapping, Memory::TypedMapping<DPLLControlRegisters volatile> dpll_control_registers)
|
||||
: IntelDisplayTranscoder(move(transcoder_registers_mapping), move(pipe_registers_mapping))
|
||||
, m_dpll_registers(move(dpll_registers_mapping))
|
||||
, m_dpll_control_registers(move(dpll_control_registers))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelAnalogDisplayTranscoder::set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
u32 value = (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16);
|
||||
m_dpll_registers->divisor_a0 = value;
|
||||
m_dpll_registers->divisor_a1 = value;
|
||||
m_shadow_registers.dpll_divisor_a0 = value;
|
||||
m_shadow_registers.dpll_divisor_a1 = value;
|
||||
|
||||
// Note: We don't set the DAC multiplier now but reserve it for later usage (e.g. when enabling the DPLL)
|
||||
m_shadow_registers.dpll_reserved_dac_multiplier = dac_multiplier;
|
||||
// Note: We don't set the DPLL P1 now but reserve it for later usage (e.g. when enabling the DPLL)
|
||||
m_shadow_registers.dpll_p1 = settings.p1;
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelAnalogDisplayTranscoder::enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
// Explanation for Gen4 DPLL control bits:
|
||||
// 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default)
|
||||
// 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz)
|
||||
// 3. bit 26 - set to 0b1 to ensure mode select to DAC mode
|
||||
// 4. bit 28 - set to 0b1 to disable VGA mode
|
||||
// 5. bit 31 - enable DPLL VCO (DPLL enabled and operational)
|
||||
u32 control_value = (6 << 9) | (m_shadow_registers.dpll_p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31);
|
||||
m_dpll_control_registers->control = control_value;
|
||||
m_shadow_registers.dpll_control = control_value;
|
||||
|
||||
// Explanation for Gen4 DPLL multiplier bits:
|
||||
// 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default)
|
||||
// 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz)
|
||||
// 3. bit 26 - set to 0b1 to ensure mode select to DAC mode
|
||||
// 4. bit 28 - set to 0b1 to disable VGA mode
|
||||
// 5. bit 31 - enable DPLL VCO (DPLL enabled and operational)
|
||||
u32 dac_multiplier_value = (m_shadow_registers.dpll_reserved_dac_multiplier - 1) | ((m_shadow_registers.dpll_reserved_dac_multiplier - 1) << 8);
|
||||
m_dpll_control_registers->multiplier = dac_multiplier_value;
|
||||
m_shadow_registers.dpll_raw_dac_multiplier = dac_multiplier_value;
|
||||
|
||||
// The specification says we should wait (at least) about 150 microseconds
|
||||
// after enabling the DPLL to allow the clock to stabilize
|
||||
microseconds_delay(200);
|
||||
for (size_t milliseconds_elapsed = 0; milliseconds_elapsed < 5; milliseconds_elapsed++) {
|
||||
u32 control_value = m_dpll_control_registers->control;
|
||||
if (control_value & (1 << 31))
|
||||
return {};
|
||||
}
|
||||
return Error::from_errno(EBUSY);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelAnalogDisplayTranscoder::disable_dpll(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_dpll_control_registers->control = 0;
|
||||
m_shadow_registers.dpll_control = 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class IntelDisplayConnectorGroup;
|
||||
class IntelAnalogDisplayTranscoder final : public IntelDisplayTranscoder {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<IntelAnalogDisplayTranscoder>> create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address,
|
||||
PhysicalAddress pipe_registers_start_address, PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_control_registers_start_address);
|
||||
|
||||
virtual ErrorOr<void> set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) override;
|
||||
virtual ErrorOr<void> enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) override;
|
||||
virtual ErrorOr<void> disable_dpll(Badge<IntelDisplayConnectorGroup>) override;
|
||||
|
||||
private:
|
||||
struct [[gnu::packed]] DPLLRegisters {
|
||||
u32 divisor_a0;
|
||||
u32 divisor_a1;
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] DPLLControlRegisters {
|
||||
u32 control;
|
||||
u32 padding; // On Gen4, this is the control register of DPLL B, don't touch this
|
||||
u32 multiplier;
|
||||
};
|
||||
|
||||
IntelAnalogDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile>, Memory::TypedMapping<PipeRegisters volatile>, Memory::TypedMapping<DPLLRegisters volatile>, Memory::TypedMapping<DPLLControlRegisters volatile>);
|
||||
Memory::TypedMapping<DPLLRegisters volatile> m_dpll_registers;
|
||||
Memory::TypedMapping<DPLLControlRegisters volatile> m_dpll_control_registers;
|
||||
};
|
||||
}
|
110
Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp
Normal file
110
Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/Delay.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
IntelDisplayTranscoder::IntelDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile> registers_mapping, Memory::TypedMapping<PipeRegisters volatile> pipe_registers_mapping)
|
||||
: m_transcoder_registers(move(registers_mapping))
|
||||
, m_pipe_registers(move(pipe_registers_mapping))
|
||||
{
|
||||
}
|
||||
|
||||
IntelDisplayTranscoder::ShadowRegisters IntelDisplayTranscoder::current_registers_state() const
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
return m_shadow_registers;
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayTranscoder::set_mode_setting_timings(Badge<IntelDisplayConnectorGroup>, DisplayConnector::ModeSetting const& mode_setting)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (mode_setting.horizontal_active - 1), (mode_setting.horizontal_total() - 1));
|
||||
m_shadow_registers.horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16);
|
||||
m_transcoder_registers->horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (mode_setting.horizontal_blanking_start() - 1), (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1));
|
||||
m_shadow_registers.horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16);
|
||||
m_transcoder_registers->horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (mode_setting.horizontal_sync_start() - 1), (mode_setting.horizontal_sync_end() - 1));
|
||||
m_shadow_registers.horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16);
|
||||
m_transcoder_registers->horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1));
|
||||
m_shadow_registers.vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
|
||||
m_transcoder_registers->vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (mode_setting.vertical_blanking_start() - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1));
|
||||
m_shadow_registers.vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
|
||||
m_transcoder_registers->vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (mode_setting.vertical_sync_start() - 1), (mode_setting.vertical_sync_end() - 1));
|
||||
m_shadow_registers.vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16);
|
||||
m_transcoder_registers->vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.horizontal_active - 1));
|
||||
m_shadow_registers.pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16);
|
||||
m_transcoder_registers->pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayTranscoder::disable_pipe(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_pipe_registers->pipe_configuration = 0;
|
||||
m_shadow_registers.pipe_conf = 0;
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe");
|
||||
size_t milliseconds_elapsed = 0;
|
||||
while (milliseconds_elapsed < 100) {
|
||||
u32 value = m_pipe_registers->pipe_configuration;
|
||||
if (!(value & (1 << 30)))
|
||||
return {};
|
||||
microseconds_delay(1000);
|
||||
milliseconds_elapsed++;
|
||||
}
|
||||
return Error::from_errno(EBUSY);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayTranscoder::enable_pipe(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
u32 value = m_pipe_registers->pipe_configuration;
|
||||
// Note: Just verify these are not already enabled...
|
||||
if ((value & (1 << 30)) && (value & (1 << 31)))
|
||||
return {};
|
||||
|
||||
// Note: Set the pipe configuration register with these bits:
|
||||
// 1. Bit 31 - to enable the Pipe
|
||||
// 2. Bit 24 - to enable Gamma Unit Mode to 10 bit Gamma mode.
|
||||
// 3. Bits 21-23 are set to zero to indicate Progressive mode (non Interlaced mode)
|
||||
// 4. Bits 18 and 19 are set to zero to indicate Normal operations of assigned
|
||||
// Cursor and Display planes.
|
||||
m_pipe_registers->pipe_configuration = (1 << 31) | (1 << 24);
|
||||
m_shadow_registers.pipe_conf = (1 << 31) | (1 << 24);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Enabling Pipe");
|
||||
size_t milliseconds_elapsed = 0;
|
||||
while (milliseconds_elapsed < 100) {
|
||||
u32 value = m_pipe_registers->pipe_configuration;
|
||||
if ((value & (1 << 30)))
|
||||
return {};
|
||||
microseconds_delay(1000);
|
||||
milliseconds_elapsed++;
|
||||
}
|
||||
// FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!!
|
||||
return {};
|
||||
}
|
||||
bool IntelDisplayTranscoder::pipe_enabled(Badge<IntelDisplayConnectorGroup>) const
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
u32 value = m_pipe_registers->pipe_configuration;
|
||||
return (value & (1 << 30));
|
||||
}
|
||||
}
|
118
Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h
Normal file
118
Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Devices/GPU/DisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Definitions.h>
|
||||
#include <Kernel/Locking/Spinlock.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class IntelDisplayConnectorGroup;
|
||||
class IntelDisplayTranscoder {
|
||||
public:
|
||||
// Note: This is used to "cache" all the registers we wrote to, because
|
||||
// we might not be able to read them directly from hardware later.
|
||||
struct ShadowRegisters {
|
||||
u32 horizontal_total;
|
||||
u32 horizontal_blank;
|
||||
u32 horizontal_sync;
|
||||
u32 vertical_total;
|
||||
u32 vertical_blank;
|
||||
u32 vertical_sync;
|
||||
u32 exit_line;
|
||||
u32 pipe_source;
|
||||
u32 pipe_border_color_pattern;
|
||||
u32 reserved;
|
||||
u32 vsync_shift;
|
||||
u32 pipe_mult;
|
||||
u32 dpll_reserved_dac_multiplier;
|
||||
u32 dpll_raw_dac_multiplier;
|
||||
u32 dpll_divisor_a0;
|
||||
u32 dpll_divisor_a1;
|
||||
u32 dpll_p1;
|
||||
u32 dpll_control;
|
||||
u32 m1_value;
|
||||
u32 n1_value;
|
||||
u32 m2_value;
|
||||
u32 n2_value;
|
||||
u32 m1_link;
|
||||
u32 n1_link;
|
||||
u32 m2_link;
|
||||
u32 n2_link;
|
||||
u32 pipe_conf;
|
||||
};
|
||||
|
||||
ErrorOr<void> set_mode_setting_timings(Badge<IntelDisplayConnectorGroup>, DisplayConnector::ModeSetting const&);
|
||||
virtual ErrorOr<void> set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) = 0;
|
||||
virtual ErrorOr<void> enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) = 0;
|
||||
virtual ErrorOr<void> disable_dpll(Badge<IntelDisplayConnectorGroup>) = 0;
|
||||
|
||||
ErrorOr<void> disable_pipe(Badge<IntelDisplayConnectorGroup>);
|
||||
ErrorOr<void> enable_pipe(Badge<IntelDisplayConnectorGroup>);
|
||||
bool pipe_enabled(Badge<IntelDisplayConnectorGroup>) const;
|
||||
|
||||
ShadowRegisters current_registers_state() const;
|
||||
|
||||
virtual ~IntelDisplayTranscoder() = default;
|
||||
|
||||
protected:
|
||||
struct [[gnu::packed]] TranscoderRegisters {
|
||||
u32 horizontal_total;
|
||||
u32 horizontal_blank;
|
||||
u32 horizontal_sync;
|
||||
u32 vertical_total;
|
||||
u32 vertical_blank;
|
||||
u32 vertical_sync;
|
||||
u32 exit_line;
|
||||
u32 pipe_source;
|
||||
u32 pipe_border_color_pattern;
|
||||
u32 reserved;
|
||||
u32 vsync_shift;
|
||||
u32 pipe_mult;
|
||||
u32 m1_value;
|
||||
u32 n1_value;
|
||||
u32 m2_value;
|
||||
u32 n2_value;
|
||||
u32 m1_link;
|
||||
u32 n1_link;
|
||||
u32 m2_link;
|
||||
u32 n2_link;
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] PipeRegisters {
|
||||
u32 pipe_display_scan_line;
|
||||
u32 pipe_display_scan_line_count_range_compare;
|
||||
u32 pipe_configuration;
|
||||
u32 reserved;
|
||||
u32 pipe_gamma_correction_max_red;
|
||||
u32 pipe_gamma_correction_max_green;
|
||||
u32 pipe_gamma_correction_max_blue;
|
||||
u32 reserved2[2];
|
||||
u32 pipe_display_status;
|
||||
u32 reserved3[2];
|
||||
u32 display_arbitration_control;
|
||||
u32 display_fifo_watermark_control1;
|
||||
u32 display_fifo_watermark_control2;
|
||||
u32 display_fifo_watermark_control3;
|
||||
u32 pipe_frame_count_high;
|
||||
// Note: The specification calls this "Pipe Frame Count Low and Pixel Count"
|
||||
u32 pipe_frame_count_low;
|
||||
};
|
||||
|
||||
IntelDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile>, Memory::TypedMapping<PipeRegisters volatile>);
|
||||
mutable Spinlock<LockRank::None> m_access_lock;
|
||||
|
||||
ShadowRegisters m_shadow_registers {};
|
||||
Memory::TypedMapping<TranscoderRegisters volatile> m_transcoder_registers;
|
||||
Memory::TypedMapping<PipeRegisters volatile> m_pipe_registers;
|
||||
};
|
||||
}
|
125
Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp
Normal file
125
Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/PLL.h>
|
||||
|
||||
namespace Kernel::IntelGraphics {
|
||||
|
||||
static constexpr PLLMaxSettings g35limits {
|
||||
{ 20'000'000, 400'000'000 }, // values in Hz, dot_clock
|
||||
{ 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO
|
||||
{ 3, 8 }, // n
|
||||
{ 70, 120 }, // m
|
||||
{ 10, 20 }, // m1
|
||||
{ 5, 9 }, // m2
|
||||
{ 5, 80 }, // p
|
||||
{ 1, 8 }, // p1
|
||||
{ 5, 10 } // p2
|
||||
};
|
||||
|
||||
PLLMaxSettings const& pll_max_settings_for_generation(Generation generation)
|
||||
{
|
||||
switch (generation) {
|
||||
case Generation::Gen4:
|
||||
return g35limits;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency)
|
||||
{
|
||||
if (target_frequency >= checked_frequency)
|
||||
return target_frequency - checked_frequency;
|
||||
return checked_frequency - target_frequency;
|
||||
}
|
||||
|
||||
Optional<PLLSettings> create_pll_settings(Generation generation, u64 target_frequency, u64 reference_clock)
|
||||
{
|
||||
PLLSettings settings {};
|
||||
PLLSettings best_settings {};
|
||||
auto& limits = pll_max_settings_for_generation(generation);
|
||||
// FIXME: Is this correct for all Intel Native graphics cards?
|
||||
settings.p2 = 10;
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency);
|
||||
u64 best_difference = 0xffffffff;
|
||||
for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) {
|
||||
for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) {
|
||||
for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) {
|
||||
for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
|
||||
if (!check_pll_settings(settings, reference_clock, limits))
|
||||
continue;
|
||||
auto current_dot_clock = settings.compute_dot_clock(reference_clock);
|
||||
if (current_dot_clock == target_frequency)
|
||||
return settings;
|
||||
auto difference = find_absolute_difference(target_frequency, current_dot_clock);
|
||||
if (difference < best_difference && (current_dot_clock > target_frequency)) {
|
||||
best_settings = settings;
|
||||
best_difference = difference;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best_settings.is_valid())
|
||||
return best_settings;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits)
|
||||
{
|
||||
if (settings.n < limits.n.min || settings.n > limits.n.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n);
|
||||
return false;
|
||||
}
|
||||
if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1);
|
||||
return false;
|
||||
}
|
||||
if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2);
|
||||
return false;
|
||||
}
|
||||
if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.m1 <= settings.m2) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto m = settings.compute_m();
|
||||
auto p = settings.compute_p();
|
||||
|
||||
if (m < limits.m.min || m > limits.m.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m);
|
||||
return false;
|
||||
}
|
||||
if (p < limits.p.min || p > limits.p.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dot = settings.compute_dot_clock(reference_clock);
|
||||
auto vco = settings.compute_vco(reference_clock);
|
||||
|
||||
if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot);
|
||||
return false;
|
||||
}
|
||||
if (vco < limits.vco.min || vco > limits.vco.max) {
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
18
Kernel/Devices/GPU/Intel/Transcoder/PLL.h
Normal file
18
Kernel/Devices/GPU/Intel/Transcoder/PLL.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Optional.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Definitions.h>
|
||||
|
||||
namespace Kernel::IntelGraphics {
|
||||
|
||||
PLLMaxSettings const& pll_max_settings_for_generation(Generation);
|
||||
Optional<PLLSettings> create_pll_settings(Generation, u64 target_frequency, u64 reference_clock);
|
||||
bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue