mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 17:57:35 +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
123
Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.cpp
Normal file
123
Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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/Auxiliary/GMBusConnector.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
enum class GMBusStatus {
|
||||
TransactionCompletion,
|
||||
HardwareReady
|
||||
};
|
||||
|
||||
enum GMBusCycle {
|
||||
Wait = 1,
|
||||
Stop = 4,
|
||||
};
|
||||
|
||||
ErrorOr<NonnullOwnPtr<GMBusConnector>> GMBusConnector::create_with_physical_address(PhysicalAddress gmbus_start_address)
|
||||
{
|
||||
auto registers_mapping = TRY(map_typed<GMBusRegisters volatile>(gmbus_start_address, sizeof(GMBusRegisters), Memory::Region::Access::ReadWrite));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) GMBusConnector(move(registers_mapping)));
|
||||
}
|
||||
|
||||
GMBusConnector::GMBusConnector(Memory::TypedMapping<GMBusRegisters volatile> registers_mapping)
|
||||
: m_gmbus_registers(move(registers_mapping))
|
||||
{
|
||||
set_default_rate();
|
||||
set_pin_pair(PinPair::DedicatedAnalog);
|
||||
}
|
||||
|
||||
bool GMBusConnector::wait_for(GMBusStatus desired_status, size_t milliseconds_timeout)
|
||||
{
|
||||
VERIFY(m_access_lock.is_locked());
|
||||
size_t milliseconds_passed = 0;
|
||||
while (1) {
|
||||
if (milliseconds_timeout < milliseconds_passed)
|
||||
return false;
|
||||
full_memory_barrier();
|
||||
u32 status = m_gmbus_registers->status;
|
||||
full_memory_barrier();
|
||||
VERIFY(!(status & (1 << 10))); // error happened
|
||||
switch (desired_status) {
|
||||
case GMBusStatus::HardwareReady:
|
||||
if (status & (1 << 11))
|
||||
return true;
|
||||
break;
|
||||
case GMBusStatus::TransactionCompletion:
|
||||
if (status & (1 << 14))
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
microseconds_delay(1000);
|
||||
milliseconds_passed++;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> GMBusConnector::write(unsigned address, u32 data)
|
||||
{
|
||||
VERIFY(address < 256);
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
full_memory_barrier();
|
||||
m_gmbus_registers->data = data;
|
||||
full_memory_barrier();
|
||||
m_gmbus_registers->command = ((address << 1) | (1 << 16) | (GMBusCycle::Wait << 25) | (1 << 30));
|
||||
full_memory_barrier();
|
||||
if (!wait_for(GMBusStatus::TransactionCompletion, 250))
|
||||
return Error::from_errno(EBUSY);
|
||||
return {};
|
||||
}
|
||||
|
||||
void GMBusConnector::set_default_rate()
|
||||
{
|
||||
// FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
// Set the rate to 100KHz
|
||||
m_gmbus_registers->clock = m_gmbus_registers->clock & ~(0b111 << 8);
|
||||
}
|
||||
|
||||
void GMBusConnector::set_pin_pair(PinPair pin_pair)
|
||||
{
|
||||
// FIXME: Verify GMBUS is idle
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_gmbus_registers->clock = (m_gmbus_registers->clock & (~0b111)) | (pin_pair & 0b111);
|
||||
}
|
||||
|
||||
ErrorOr<void> GMBusConnector::read(unsigned address, u8* buf, size_t length)
|
||||
{
|
||||
VERIFY(address < 256);
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
size_t nread = 0;
|
||||
auto read_set = [&] {
|
||||
full_memory_barrier();
|
||||
u32 data = m_gmbus_registers->data;
|
||||
full_memory_barrier();
|
||||
for (size_t index = 0; index < 4; index++) {
|
||||
if (nread == length)
|
||||
break;
|
||||
buf[nread] = (data >> (8 * index)) & 0xFF;
|
||||
nread++;
|
||||
}
|
||||
};
|
||||
|
||||
full_memory_barrier();
|
||||
m_gmbus_registers->command = (1 | (address << 1) | (length << 16) | (GMBusCycle::Wait << 25) | (1 << 30));
|
||||
full_memory_barrier();
|
||||
while (nread < length) {
|
||||
if (!wait_for(GMBusStatus::HardwareReady, 250))
|
||||
return Error::from_errno(EBUSY);
|
||||
read_set();
|
||||
}
|
||||
if (!wait_for(GMBusStatus::TransactionCompletion, 250))
|
||||
return Error::from_errno(EBUSY);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
53
Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h
Normal file
53
Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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/Locking/Spinlock.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
struct [[gnu::packed]] GMBusRegisters {
|
||||
u32 clock;
|
||||
u32 command;
|
||||
u32 status;
|
||||
u32 data;
|
||||
};
|
||||
|
||||
enum class GMBusStatus;
|
||||
|
||||
class GMBusConnector {
|
||||
public:
|
||||
enum PinPair : u8 {
|
||||
None = 0,
|
||||
DedicatedControl = 1,
|
||||
DedicatedAnalog = 0b10,
|
||||
IntegratedDigital = 0b11,
|
||||
sDVO = 0b101,
|
||||
Dconnector = 0b111,
|
||||
};
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<GMBusConnector>> create_with_physical_address(PhysicalAddress gmbus_start_address);
|
||||
|
||||
ErrorOr<void> write(unsigned address, u32 data);
|
||||
ErrorOr<void> read(unsigned address, u8* buf, size_t length);
|
||||
void set_default_rate();
|
||||
|
||||
private:
|
||||
void set_pin_pair(PinPair pin_pair);
|
||||
|
||||
bool wait_for(GMBusStatus desired_status, size_t milliseconds_timeout);
|
||||
|
||||
explicit GMBusConnector(Memory::TypedMapping<GMBusRegisters volatile>);
|
||||
Spinlock<LockRank::None> m_access_lock;
|
||||
Memory::TypedMapping<GMBusRegisters volatile> m_gmbus_registers;
|
||||
};
|
||||
}
|
55
Kernel/Devices/GPU/Intel/Definitions.h
Normal file
55
Kernel/Devices/GPU/Intel/Definitions.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace Kernel::IntelGraphics {
|
||||
|
||||
enum class Generation {
|
||||
Gen4,
|
||||
Gen9,
|
||||
};
|
||||
|
||||
struct PLLSettings;
|
||||
|
||||
struct PLLParameterLimit {
|
||||
size_t min, max;
|
||||
};
|
||||
|
||||
struct PLLMaxSettings {
|
||||
PLLParameterLimit dot_clock, vco, n, m, m1, m2, p, p1, p2;
|
||||
};
|
||||
|
||||
struct PLLSettings {
|
||||
bool is_valid() const { return (n != 0 && m1 != 0 && m2 != 0 && p1 != 0 && p2 != 0); }
|
||||
u64 compute_dot_clock(u64 refclock) const
|
||||
{
|
||||
return (refclock * (5 * m1 + m2) / n) / (p1 * p2);
|
||||
}
|
||||
|
||||
u64 compute_vco(u64 refclock) const
|
||||
{
|
||||
return refclock * (5 * m1 + m2) / n;
|
||||
}
|
||||
|
||||
u64 compute_m() const
|
||||
{
|
||||
return 5 * m1 + m2;
|
||||
}
|
||||
|
||||
u64 compute_p() const
|
||||
{
|
||||
return p1 * p2;
|
||||
}
|
||||
u64 n { 0 };
|
||||
u64 m1 { 0 };
|
||||
u64 m2 { 0 };
|
||||
u64 p1 { 0 };
|
||||
u64 p2 { 0 };
|
||||
};
|
||||
}
|
278
Kernel/Devices/GPU/Intel/DisplayConnectorGroup.cpp
Normal file
278
Kernel/Devices/GPU/Intel/DisplayConnectorGroup.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/Delay.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/PLL.h>
|
||||
#include <Kernel/Devices/GPU/Management.h>
|
||||
#include <Kernel/Memory/Region.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<IntelDisplayConnectorGroup>> IntelDisplayConnectorGroup::try_create(Badge<IntelNativeGraphicsAdapter>, IntelGraphics::Generation generation, MMIORegion const& first_region, MMIORegion const& second_region)
|
||||
{
|
||||
auto registers_region = TRY(MM.allocate_kernel_region(first_region.pci_bar_paddr, first_region.pci_bar_space_length, "Intel Native Graphics Registers"sv, Memory::Region::Access::ReadWrite));
|
||||
// NOTE: 0x5100 is the offset of the start of the GMBus registers
|
||||
auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(first_region.pci_bar_paddr.offset(0x5100)));
|
||||
auto connector_group = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelDisplayConnectorGroup(generation, move(gmbus_connector), move(registers_region), first_region, second_region)));
|
||||
TRY(connector_group->initialize_connectors());
|
||||
return connector_group;
|
||||
}
|
||||
|
||||
IntelDisplayConnectorGroup::IntelDisplayConnectorGroup(IntelGraphics::Generation generation, NonnullOwnPtr<GMBusConnector> gmbus_connector, NonnullOwnPtr<Memory::Region> registers_region, MMIORegion const& first_region, MMIORegion const& second_region)
|
||||
: m_mmio_first_region(first_region)
|
||||
, m_mmio_second_region(second_region)
|
||||
, m_assigned_mmio_registers_region(m_mmio_first_region)
|
||||
, m_generation(generation)
|
||||
, m_registers_region(move(registers_region))
|
||||
, m_gmbus_connector(move(gmbus_connector))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayConnectorGroup::initialize_gen4_connectors()
|
||||
{
|
||||
// NOTE: Just assume we will need one Gen4 "transcoder"
|
||||
// NOTE: Main block of registers starting at HorizontalTotalA register (0x60000)
|
||||
auto transcoder_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x60000);
|
||||
// NOTE: Main block of Pipe registers starting at PipeA_DSL register (0x70000)
|
||||
auto pipe_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x70000);
|
||||
// NOTE: DPLL registers starting at DPLLDivisorA0 register (0x6040)
|
||||
auto dpll_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6040);
|
||||
// NOTE: DPLL A control registers starting at 0x6014 (DPLL A Control register),
|
||||
// DPLL A Multiplier is at 0x601C, between them (at 0x6018) there is the DPLL B Control register.
|
||||
auto dpll_control_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6014);
|
||||
m_transcoders[0] = TRY(IntelAnalogDisplayTranscoder::create_with_physical_addresses(transcoder_registers_paddr, pipe_registers_paddr, dpll_registers_paddr, dpll_control_registers_paddr));
|
||||
m_planes[0] = TRY(IntelG33DisplayPlane::create_with_physical_address(m_mmio_first_region.pci_bar_paddr.offset(0x70180)));
|
||||
Array<u8, 128> crt_edid_bytes {};
|
||||
{
|
||||
SpinlockLocker control_lock(m_control_lock);
|
||||
TRY(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0));
|
||||
TRY(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size()));
|
||||
}
|
||||
m_connectors[0] = TRY(IntelNativeDisplayConnector::try_create_with_display_connector_group(*this, IntelNativeDisplayConnector::ConnectorIndex::PortA, IntelNativeDisplayConnector::Type::Analog, m_mmio_second_region.pci_bar_paddr, m_mmio_second_region.pci_bar_space_length));
|
||||
m_connectors[0]->set_edid_bytes({}, crt_edid_bytes);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayConnectorGroup::initialize_connectors()
|
||||
{
|
||||
|
||||
// NOTE: Intel Graphics Generation 4 is pretty ancient beast, and we should not
|
||||
// assume we can find a VBT for it. Just initialize the (assumed) CRT connector and be done with it.
|
||||
if (m_generation == IntelGraphics::Generation::Gen4) {
|
||||
TRY(initialize_gen4_connectors());
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (size_t connector_index = 0; connector_index < m_connectors.size(); connector_index++) {
|
||||
if (!m_connectors[connector_index])
|
||||
continue;
|
||||
if (!m_connectors[connector_index]->m_edid_valid)
|
||||
continue;
|
||||
TRY(m_connectors[connector_index]->set_safe_mode_setting());
|
||||
TRY(m_connectors[connector_index]->create_attached_framebuffer_console({}));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayConnectorGroup::set_safe_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector)
|
||||
{
|
||||
VERIFY(connector.m_modeset_lock.is_locked());
|
||||
if (!connector.m_edid_parser.has_value())
|
||||
return Error::from_errno(ENOTSUP);
|
||||
if (!connector.m_edid_parser.value().detailed_timing(0).has_value())
|
||||
return Error::from_errno(ENOTSUP);
|
||||
auto details = connector.m_edid_parser.value().detailed_timing(0).release_value();
|
||||
|
||||
DisplayConnector::ModeSetting modesetting {
|
||||
// Note: We assume that we always use 32 bit framebuffers.
|
||||
.horizontal_stride = details.horizontal_addressable_pixels() * sizeof(u32),
|
||||
.pixel_clock_in_khz = details.pixel_clock_khz(),
|
||||
.horizontal_active = details.horizontal_addressable_pixels(),
|
||||
.horizontal_front_porch_pixels = details.horizontal_front_porch_pixels(),
|
||||
.horizontal_sync_time_pixels = details.horizontal_sync_pulse_width_pixels(),
|
||||
.horizontal_blank_pixels = details.horizontal_blanking_pixels(),
|
||||
.vertical_active = details.vertical_addressable_lines(),
|
||||
.vertical_front_porch_lines = details.vertical_front_porch_lines(),
|
||||
.vertical_sync_time_lines = details.vertical_sync_pulse_width_lines(),
|
||||
.vertical_blank_lines = details.vertical_blanking_lines(),
|
||||
.horizontal_offset = 0,
|
||||
.vertical_offset = 0,
|
||||
};
|
||||
|
||||
return set_mode_setting(connector, modesetting);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
|
||||
{
|
||||
return set_mode_setting(connector, mode_setting);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
|
||||
{
|
||||
VERIFY(connector.m_modeset_lock.is_locked());
|
||||
|
||||
VERIFY(to_underlying(connector.connector_index()) < m_connectors.size());
|
||||
VERIFY(&connector == m_connectors[to_underlying(connector.connector_index())].ptr());
|
||||
|
||||
DisplayConnector::ModeSetting actual_mode_setting = mode_setting;
|
||||
actual_mode_setting.horizontal_stride = actual_mode_setting.horizontal_active * sizeof(u32);
|
||||
VERIFY(actual_mode_setting.horizontal_stride != 0);
|
||||
if (m_generation == IntelGraphics::Generation::Gen4) {
|
||||
TRY(set_gen4_mode_setting(connector, actual_mode_setting));
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
connector.m_current_mode_setting = actual_mode_setting;
|
||||
if (!connector.m_framebuffer_console.is_null())
|
||||
static_cast<Graphics::GenericFramebufferConsoleImpl*>(connector.m_framebuffer_console.ptr())->set_resolution(actual_mode_setting.horizontal_active, actual_mode_setting.vertical_active, actual_mode_setting.horizontal_stride);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayConnectorGroup::set_gen4_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
|
||||
{
|
||||
VERIFY(connector.m_modeset_lock.is_locked());
|
||||
SpinlockLocker control_lock(m_control_lock);
|
||||
SpinlockLocker modeset_lock(m_modeset_lock);
|
||||
if (!set_crt_resolution(mode_setting))
|
||||
return Error::from_errno(ENOTSUP);
|
||||
return {};
|
||||
}
|
||||
|
||||
void IntelDisplayConnectorGroup::enable_vga_plane()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
}
|
||||
|
||||
StringView IntelDisplayConnectorGroup::convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const
|
||||
{
|
||||
switch (index) {
|
||||
case AnalogOutputRegisterOffset::AnalogDisplayPort:
|
||||
return "AnalogDisplayPort"sv;
|
||||
case AnalogOutputRegisterOffset::VGADisplayPlaneControl:
|
||||
return "VGADisplayPlaneControl"sv;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void IntelDisplayConnectorGroup::write_to_general_register(RegisterOffset offset, u32 value)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
SpinlockLocker lock(m_registers_lock);
|
||||
auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr();
|
||||
*reg = value;
|
||||
}
|
||||
u32 IntelDisplayConnectorGroup::read_from_general_register(RegisterOffset offset) const
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
SpinlockLocker lock(m_registers_lock);
|
||||
auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr();
|
||||
u32 value = *reg;
|
||||
return value;
|
||||
}
|
||||
|
||||
void IntelDisplayConnectorGroup::write_to_analog_output_register(AnalogOutputRegisterOffset index, u32 value)
|
||||
{
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_analog_output_register_to_string(index), value);
|
||||
write_to_general_register(to_underlying(index), value);
|
||||
}
|
||||
|
||||
u32 IntelDisplayConnectorGroup::read_from_analog_output_register(AnalogOutputRegisterOffset index) const
|
||||
{
|
||||
u32 value = read_from_general_register(to_underlying(index));
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_analog_output_register_to_string(index), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static size_t compute_dac_multiplier(size_t pixel_clock_in_khz)
|
||||
{
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz);
|
||||
VERIFY(pixel_clock_in_khz >= 25000);
|
||||
if (pixel_clock_in_khz >= 100000) {
|
||||
return 1;
|
||||
} else if (pixel_clock_in_khz >= 50000) {
|
||||
return 2;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
bool IntelDisplayConnectorGroup::set_crt_resolution(DisplayConnector::ModeSetting const& mode_setting)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
|
||||
// Note: Just in case we still allow access to VGA IO ports, disable it now.
|
||||
GraphicsManagement::the().disable_vga_emulation_access_permanently();
|
||||
|
||||
auto dac_multiplier = compute_dac_multiplier(mode_setting.pixel_clock_in_khz);
|
||||
auto pll_settings = create_pll_settings(m_generation, (1000 * mode_setting.pixel_clock_in_khz * dac_multiplier), 96'000'000);
|
||||
if (!pll_settings.has_value())
|
||||
return false;
|
||||
auto settings = pll_settings.value();
|
||||
|
||||
disable_dac_output();
|
||||
MUST(m_planes[0]->disable({}));
|
||||
MUST(m_transcoders[0]->disable_pipe({}));
|
||||
MUST(m_transcoders[0]->disable_dpll({}));
|
||||
disable_vga_emulation();
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
|
||||
MUST(m_transcoders[0]->set_dpll_settings({}, settings, dac_multiplier));
|
||||
MUST(m_transcoders[0]->disable_dpll({}));
|
||||
MUST(m_transcoders[0]->enable_dpll_without_vga({}));
|
||||
MUST(m_transcoders[0]->set_mode_setting_timings({}, mode_setting));
|
||||
|
||||
VERIFY(!m_transcoders[0]->pipe_enabled({}));
|
||||
MUST(m_transcoders[0]->enable_pipe({}));
|
||||
|
||||
MUST(m_planes[0]->set_aperture_base({}, m_mmio_second_region.pci_bar_paddr));
|
||||
MUST(m_planes[0]->set_pipe({}, IntelDisplayPlane::PipeSelect::PipeA));
|
||||
MUST(m_planes[0]->set_horizontal_stride({}, mode_setting.horizontal_active * 4));
|
||||
MUST(m_planes[0]->set_horizontal_active_pixels_count({}, mode_setting.horizontal_active));
|
||||
// Note: This doesn't affect anything on the plane settings for Gen4, but we still
|
||||
// do it for the sake of "completeness".
|
||||
MUST(m_planes[0]->set_vertical_active_pixels_count({}, mode_setting.vertical_active));
|
||||
MUST(m_planes[0]->enable({}));
|
||||
enable_dac_output();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IntelDisplayConnectorGroup::disable_dac_output()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, 0b11 << 10);
|
||||
}
|
||||
|
||||
void IntelDisplayConnectorGroup::enable_dac_output()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, (1 << 31));
|
||||
}
|
||||
|
||||
void IntelDisplayConnectorGroup::disable_vga_emulation()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl, (1 << 31));
|
||||
read_from_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl);
|
||||
}
|
||||
|
||||
}
|
97
Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h
Normal file
97
Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 <Kernel/Devices/GPU/Console/GenericFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Definitions.h>
|
||||
#include <Kernel/Devices/GPU/Intel/NativeDisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h>
|
||||
#include <Kernel/Library/LockRefPtr.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
#include <LibEDID/EDID.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class IntelNativeGraphicsAdapter;
|
||||
class IntelDisplayConnectorGroup : public RefCounted<IntelDisplayConnectorGroup> {
|
||||
friend class IntelNativeGraphicsAdapter;
|
||||
|
||||
public:
|
||||
struct MMIORegion {
|
||||
enum class BARAssigned {
|
||||
BAR0,
|
||||
BAR2,
|
||||
};
|
||||
BARAssigned pci_bar_assigned;
|
||||
PhysicalAddress pci_bar_paddr;
|
||||
size_t pci_bar_space_length;
|
||||
};
|
||||
|
||||
private:
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, RegisterOffset);
|
||||
|
||||
enum class AnalogOutputRegisterOffset {
|
||||
AnalogDisplayPort = 0x61100,
|
||||
VGADisplayPlaneControl = 0x71400,
|
||||
};
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullLockRefPtr<IntelDisplayConnectorGroup>> try_create(Badge<IntelNativeGraphicsAdapter>, IntelGraphics::Generation, MMIORegion const&, MMIORegion const&);
|
||||
|
||||
ErrorOr<void> set_safe_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector&);
|
||||
ErrorOr<void> set_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&);
|
||||
|
||||
private:
|
||||
IntelDisplayConnectorGroup(IntelGraphics::Generation generation, NonnullOwnPtr<GMBusConnector>, NonnullOwnPtr<Memory::Region> registers_region, MMIORegion const&, MMIORegion const&);
|
||||
|
||||
ErrorOr<void> set_mode_setting(IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&);
|
||||
|
||||
StringView convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const;
|
||||
void write_to_analog_output_register(AnalogOutputRegisterOffset, u32 value);
|
||||
u32 read_from_analog_output_register(AnalogOutputRegisterOffset) const;
|
||||
void write_to_general_register(RegisterOffset offset, u32 value);
|
||||
u32 read_from_general_register(RegisterOffset offset) const;
|
||||
|
||||
// DisplayConnector initialization related methods
|
||||
ErrorOr<void> initialize_connectors();
|
||||
ErrorOr<void> initialize_gen4_connectors();
|
||||
|
||||
// General Modesetting methods
|
||||
ErrorOr<void> set_gen4_mode_setting(IntelNativeDisplayConnector&, DisplayConnector::ModeSetting const&);
|
||||
|
||||
bool set_crt_resolution(DisplayConnector::ModeSetting const&);
|
||||
|
||||
void disable_vga_emulation();
|
||||
void enable_vga_plane();
|
||||
|
||||
void disable_dac_output();
|
||||
void enable_dac_output();
|
||||
|
||||
Spinlock<LockRank::None> m_control_lock;
|
||||
Spinlock<LockRank::None> m_modeset_lock;
|
||||
mutable Spinlock<LockRank::None> m_registers_lock;
|
||||
|
||||
// Note: The linux driver specifies an enum of possible ports and there is only
|
||||
// 9 ports (PORT_{A-I}). PORT_TC{1-6} are mapped to PORT_{D-I}.
|
||||
Array<LockRefPtr<IntelNativeDisplayConnector>, 9> m_connectors;
|
||||
|
||||
Array<OwnPtr<IntelDisplayTranscoder>, 5> m_transcoders;
|
||||
Array<OwnPtr<IntelDisplayPlane>, 3> m_planes;
|
||||
|
||||
const MMIORegion m_mmio_first_region;
|
||||
const MMIORegion m_mmio_second_region;
|
||||
MMIORegion const& m_assigned_mmio_registers_region;
|
||||
|
||||
const IntelGraphics::Generation m_generation;
|
||||
NonnullOwnPtr<Memory::Region> m_registers_region;
|
||||
NonnullOwnPtr<GMBusConnector> m_gmbus_connector;
|
||||
};
|
||||
}
|
96
Kernel/Devices/GPU/Intel/NativeDisplayConnector.cpp
Normal file
96
Kernel/Devices/GPU/Intel/NativeDisplayConnector.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/Delay.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h>
|
||||
#include <Kernel/Devices/GPU/Intel/NativeDisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/Management.h>
|
||||
#include <Kernel/Memory/Region.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> IntelNativeDisplayConnector::try_create_with_display_connector_group(IntelDisplayConnectorGroup const& parent_connector_group, ConnectorIndex connector_index, Type type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size)
|
||||
{
|
||||
return TRY(DeviceManagement::try_create_device<IntelNativeDisplayConnector>(parent_connector_group, connector_index, type, framebuffer_address, framebuffer_resource_size));
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
size_t pitch = 0;
|
||||
{
|
||||
SpinlockLocker control_locker(m_control_lock);
|
||||
SpinlockLocker mode_set_locker(m_modeset_lock);
|
||||
width = m_current_mode_setting.horizontal_active;
|
||||
height = m_current_mode_setting.vertical_active;
|
||||
pitch = m_current_mode_setting.horizontal_stride;
|
||||
}
|
||||
m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), width, height, pitch);
|
||||
GraphicsManagement::the().set_console(*m_framebuffer_console);
|
||||
return {};
|
||||
}
|
||||
|
||||
IntelNativeDisplayConnector::IntelNativeDisplayConnector(IntelDisplayConnectorGroup const& parent_connector_group, ConnectorIndex connector_index, Type type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size)
|
||||
: DisplayConnector(framebuffer_address, framebuffer_resource_size, true)
|
||||
, m_type(type)
|
||||
, m_connector_index(connector_index)
|
||||
, m_parent_connector_group(parent_connector_group)
|
||||
{
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::set_edid_bytes(Badge<IntelDisplayConnectorGroup>, Array<u8, 128> const& raw_bytes)
|
||||
{
|
||||
// Note: The provided EDID might be invalid (because there's no attached monitor)
|
||||
// Therefore, set might_be_invalid to true to indicate that.
|
||||
DisplayConnector::set_edid_bytes(raw_bytes, true);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::set_y_offset(size_t)
|
||||
{
|
||||
return Error::from_errno(ENOTIMPL);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::unblank()
|
||||
{
|
||||
return Error::from_errno(ENOTIMPL);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::set_safe_mode_setting()
|
||||
{
|
||||
SpinlockLocker locker(m_modeset_lock);
|
||||
return m_parent_connector_group->set_safe_mode_setting({}, *this);
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_console()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
if (m_framebuffer_console)
|
||||
m_framebuffer_console->enable();
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_console()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
if (m_framebuffer_console)
|
||||
m_framebuffer_console->disable();
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::flush_first_surface()
|
||||
{
|
||||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::set_mode_setting(DisplayConnector::ModeSetting const&)
|
||||
{
|
||||
return Error::from_errno(ENOTIMPL);
|
||||
}
|
||||
|
||||
}
|
80
Kernel/Devices/GPU/Intel/NativeDisplayConnector.h
Normal file
80
Kernel/Devices/GPU/Intel/NativeDisplayConnector.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Try.h>
|
||||
#include <Kernel/Devices/GPU/Console/GenericFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/Definitions.h>
|
||||
#include <Kernel/Devices/GPU/DisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/Intel/Auxiliary/GMBusConnector.h>
|
||||
#include <Kernel/Library/LockRefPtr.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class IntelDisplayConnectorGroup;
|
||||
class IntelNativeDisplayConnector final
|
||||
: public DisplayConnector {
|
||||
friend class IntelDisplayConnectorGroup;
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
Invalid,
|
||||
Analog,
|
||||
DVO,
|
||||
LVDS,
|
||||
TVOut,
|
||||
HDMI,
|
||||
DisplayPort,
|
||||
EmbeddedDisplayPort,
|
||||
};
|
||||
|
||||
enum class ConnectorIndex : size_t {
|
||||
PortA = 0,
|
||||
PortB = 1,
|
||||
PortC = 2,
|
||||
PortD = 3,
|
||||
PortE = 4,
|
||||
PortF = 5,
|
||||
PortH = 6,
|
||||
PortG = 7,
|
||||
PortI = 8,
|
||||
};
|
||||
|
||||
static ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> try_create_with_display_connector_group(IntelDisplayConnectorGroup const&, ConnectorIndex, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size);
|
||||
|
||||
void set_edid_bytes(Badge<IntelDisplayConnectorGroup>, Array<u8, 128> const& edid_bytes);
|
||||
ErrorOr<void> create_attached_framebuffer_console(Badge<IntelDisplayConnectorGroup>);
|
||||
|
||||
ConnectorIndex connector_index() const { return m_connector_index; }
|
||||
|
||||
private:
|
||||
// ^DisplayConnector
|
||||
// FIXME: Implement modesetting capabilities in runtime from userland...
|
||||
virtual bool mutable_mode_setting_capable() const override { return false; }
|
||||
// FIXME: Implement double buffering capabilities in runtime from userland...
|
||||
virtual bool double_framebuffering_capable() const override { return false; }
|
||||
virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override;
|
||||
virtual ErrorOr<void> set_safe_mode_setting() override;
|
||||
virtual ErrorOr<void> set_y_offset(size_t y) override;
|
||||
virtual ErrorOr<void> unblank() override;
|
||||
virtual ErrorOr<void> flush_first_surface() override final;
|
||||
virtual void enable_console() override;
|
||||
virtual void disable_console() override;
|
||||
virtual bool partial_flush_support() const override { return false; }
|
||||
virtual bool flush_support() const override { return false; }
|
||||
// Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting.
|
||||
virtual bool refresh_rate_support() const override { return true; }
|
||||
|
||||
IntelNativeDisplayConnector(IntelDisplayConnectorGroup const&, ConnectorIndex connector_index, Type, PhysicalAddress framebuffer_address, size_t framebuffer_resource_size);
|
||||
Type const m_type { Type::Analog };
|
||||
ConnectorIndex const m_connector_index { 0 };
|
||||
NonnullLockRefPtr<IntelDisplayConnectorGroup> m_parent_connector_group;
|
||||
LockRefPtr<Graphics::GenericFramebufferConsole> m_framebuffer_console;
|
||||
};
|
||||
}
|
72
Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.cpp
Normal file
72
Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Devices/GPU/Console/ContiguousFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/Definitions.h>
|
||||
#include <Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.h>
|
||||
#include <Kernel/Devices/GPU/Management.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static constexpr u16 supported_models[] {
|
||||
0x29c2, // Intel G35 Adapter
|
||||
};
|
||||
|
||||
static bool is_supported_model(u16 device_id)
|
||||
{
|
||||
for (auto& id : supported_models) {
|
||||
if (id == device_id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ErrorOr<bool> IntelNativeGraphicsAdapter::probe(PCI::DeviceIdentifier const& pci_device_identifier)
|
||||
{
|
||||
return is_supported_model(pci_device_identifier.hardware_id().device_id);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<GenericGraphicsAdapter>> IntelNativeGraphicsAdapter::create(PCI::DeviceIdentifier const& pci_device_identifier)
|
||||
{
|
||||
auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelNativeGraphicsAdapter(pci_device_identifier)));
|
||||
TRY(adapter->initialize_adapter());
|
||||
return adapter;
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeGraphicsAdapter::initialize_adapter()
|
||||
{
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", device_identifier().address());
|
||||
auto bar0_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR0);
|
||||
auto bar2_space_size = PCI::get_BAR_space_size(device_identifier(), PCI::HeaderType0BaseRegister::BAR2);
|
||||
dmesgln_pci(*this, "MMIO @ {}, space size is {:x} bytes", PhysicalAddress(PCI::get_BAR0(device_identifier())), bar0_space_size);
|
||||
dmesgln_pci(*this, "framebuffer @ {}", PhysicalAddress(PCI::get_BAR2(device_identifier())));
|
||||
|
||||
using MMIORegion = IntelDisplayConnectorGroup::MMIORegion;
|
||||
MMIORegion first_region { MMIORegion::BARAssigned::BAR0, PhysicalAddress(PCI::get_BAR0(device_identifier()) & PCI::bar_address_mask), bar0_space_size };
|
||||
MMIORegion second_region { MMIORegion::BARAssigned::BAR2, PhysicalAddress(PCI::get_BAR2(device_identifier()) & PCI::bar_address_mask), bar2_space_size };
|
||||
|
||||
PCI::enable_bus_mastering(device_identifier());
|
||||
PCI::enable_io_space(device_identifier());
|
||||
PCI::enable_memory_space(device_identifier());
|
||||
|
||||
switch (device_identifier().hardware_id().device_id) {
|
||||
case 0x29c2:
|
||||
m_connector_group = TRY(IntelDisplayConnectorGroup::try_create({}, IntelGraphics::Generation::Gen4, first_region, second_region));
|
||||
return {};
|
||||
default:
|
||||
return Error::from_errno(ENODEV);
|
||||
}
|
||||
}
|
||||
|
||||
IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier)
|
||||
: GenericGraphicsAdapter()
|
||||
, PCI::Device(const_cast<PCI::DeviceIdentifier&>(pci_device_identifier))
|
||||
{
|
||||
}
|
||||
|
||||
}
|
38
Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.h
Normal file
38
Kernel/Devices/GPU/Intel/NativeGraphicsAdapter.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Devices/GPU/Definitions.h>
|
||||
#include <Kernel/Devices/GPU/Intel/DisplayConnectorGroup.h>
|
||||
#include <Kernel/Devices/GPU/Intel/NativeDisplayConnector.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
#include <LibEDID/EDID.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class IntelNativeGraphicsAdapter final
|
||||
: public GenericGraphicsAdapter
|
||||
, public PCI::Device {
|
||||
|
||||
public:
|
||||
static ErrorOr<bool> probe(PCI::DeviceIdentifier const&);
|
||||
static ErrorOr<NonnullLockRefPtr<GenericGraphicsAdapter>> create(PCI::DeviceIdentifier const&);
|
||||
|
||||
virtual ~IntelNativeGraphicsAdapter() = default;
|
||||
|
||||
virtual StringView device_name() const override { return "IntelNativeGraphicsAdapter"sv; }
|
||||
|
||||
private:
|
||||
ErrorOr<void> initialize_adapter();
|
||||
|
||||
explicit IntelNativeGraphicsAdapter(PCI::DeviceIdentifier const&);
|
||||
|
||||
LockRefPtr<IntelDisplayConnectorGroup> m_connector_group;
|
||||
};
|
||||
}
|
70
Kernel/Devices/GPU/Intel/Plane/DisplayPlane.cpp
Normal file
70
Kernel/Devices/GPU/Intel/Plane/DisplayPlane.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
IntelDisplayPlane::IntelDisplayPlane(Memory::TypedMapping<PlaneRegisters volatile> plane_registers_mapping)
|
||||
: m_plane_registers(move(plane_registers_mapping))
|
||||
{
|
||||
}
|
||||
|
||||
IntelDisplayPlane::ShadowRegisters IntelDisplayPlane::shadow_registers() const
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
return m_shadow_registers;
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayPlane::set_horizontal_active_pixels_count(Badge<IntelDisplayConnectorGroup>, size_t horizontal_active_pixels_count)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_horizontal_active_pixels_count = horizontal_active_pixels_count;
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> IntelDisplayPlane::set_vertical_active_pixels_count(Badge<IntelDisplayConnectorGroup>, size_t vertical_active_pixels_count)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_vertical_active_pixels_count = vertical_active_pixels_count;
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> IntelDisplayPlane::set_horizontal_stride(Badge<IntelDisplayConnectorGroup>, size_t horizontal_stride)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_horizontal_stride = horizontal_stride;
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> IntelDisplayPlane::set_aperture_base(Badge<IntelDisplayConnectorGroup>, PhysicalAddress aperture_start)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_aperture_start.set(aperture_start.get());
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> IntelDisplayPlane::set_pipe(Badge<IntelDisplayConnectorGroup>, PipeSelect pipe_select)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
m_pipe_select = pipe_select;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IntelDisplayPlane::is_enabled(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
return m_shadow_registers.control & (1 << 31);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelDisplayPlane::disable(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
// Note: We use the shadow register so we don't have the already set
|
||||
// settings being lost.
|
||||
m_shadow_registers.control &= ~(1 << 31);
|
||||
m_plane_registers->control = m_shadow_registers.control;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
77
Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h
Normal file
77
Kernel/Devices/GPU/Intel/Plane/DisplayPlane.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 IntelDisplayPlane {
|
||||
public:
|
||||
enum class PipeSelect {
|
||||
PipeA,
|
||||
PipeB,
|
||||
PipeC,
|
||||
PipeD,
|
||||
};
|
||||
|
||||
// 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 control;
|
||||
u32 linear_offset;
|
||||
u32 stride;
|
||||
u32 surface_base;
|
||||
};
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<IntelDisplayPlane>> create_with_physical_address(PhysicalAddress plane_registers_start_address);
|
||||
|
||||
ErrorOr<void> set_horizontal_active_pixels_count(Badge<IntelDisplayConnectorGroup>, size_t horizontal_active_pixels_count);
|
||||
ErrorOr<void> set_vertical_active_pixels_count(Badge<IntelDisplayConnectorGroup>, size_t vertical_active_pixels_count);
|
||||
ErrorOr<void> set_horizontal_stride(Badge<IntelDisplayConnectorGroup>, size_t horizontal_stride);
|
||||
ErrorOr<void> set_aperture_base(Badge<IntelDisplayConnectorGroup>, PhysicalAddress aperture_start);
|
||||
ErrorOr<void> set_pipe(Badge<IntelDisplayConnectorGroup>, PipeSelect);
|
||||
|
||||
virtual ErrorOr<void> enable(Badge<IntelDisplayConnectorGroup>) = 0;
|
||||
bool is_enabled(Badge<IntelDisplayConnectorGroup>);
|
||||
ErrorOr<void> disable(Badge<IntelDisplayConnectorGroup>);
|
||||
|
||||
ShadowRegisters shadow_registers() const;
|
||||
|
||||
virtual ~IntelDisplayPlane() = default;
|
||||
|
||||
protected:
|
||||
struct [[gnu::packed]] PlaneRegisters {
|
||||
u32 control;
|
||||
u32 linear_offset;
|
||||
u32 stride;
|
||||
u8 padding[24]; // Note: This might contain other registers, don't touch them.
|
||||
u32 surface_base;
|
||||
};
|
||||
|
||||
explicit IntelDisplayPlane(Memory::TypedMapping<PlaneRegisters volatile> registers_mapping);
|
||||
mutable Spinlock<LockRank::None> m_access_lock;
|
||||
ShadowRegisters m_shadow_registers {};
|
||||
Memory::TypedMapping<PlaneRegisters volatile> m_plane_registers;
|
||||
|
||||
// Note: The PipeSelect value is used only in planes until Skylake graphics.
|
||||
PipeSelect m_pipe_select { PipeSelect::PipeA };
|
||||
|
||||
PhysicalAddress m_aperture_start;
|
||||
size_t m_horizontal_stride { 0 };
|
||||
size_t m_horizontal_active_pixels_count { 0 };
|
||||
size_t m_vertical_active_pixels_count { 0 };
|
||||
};
|
||||
}
|
60
Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.cpp
Normal file
60
Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h>
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<NonnullOwnPtr<IntelG33DisplayPlane>> IntelG33DisplayPlane::create_with_physical_address(PhysicalAddress plane_registers_start_address)
|
||||
{
|
||||
auto registers_mapping = TRY(Memory::map_typed<PlaneRegisters volatile>(plane_registers_start_address, sizeof(PlaneRegisters), Memory::Region::Access::ReadWrite));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) IntelG33DisplayPlane(move(registers_mapping)));
|
||||
}
|
||||
|
||||
IntelG33DisplayPlane::IntelG33DisplayPlane(Memory::TypedMapping<PlaneRegisters volatile> registers_mapping)
|
||||
: IntelDisplayPlane(move(registers_mapping))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelG33DisplayPlane::enable(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
SpinlockLocker locker(m_access_lock);
|
||||
VERIFY(((m_horizontal_active_pixels_count * 4) % 64 == 0));
|
||||
VERIFY(((m_horizontal_stride) % 64 == 0));
|
||||
|
||||
u32 control_value = 0;
|
||||
|
||||
switch (m_pipe_select) {
|
||||
case PipeSelect::PipeA:
|
||||
control_value |= (0b00 << 24);
|
||||
break;
|
||||
case PipeSelect::PipeB:
|
||||
control_value |= (0b01 << 24);
|
||||
break;
|
||||
case PipeSelect::PipeC:
|
||||
control_value |= (0b10 << 24);
|
||||
break;
|
||||
case PipeSelect::PipeD:
|
||||
control_value |= (0b11 << 24);
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: Set the plane to work with 32 bit BGRX (Ignore Alpha channel).
|
||||
// Note: Bit 31 is set to turn on the plane.
|
||||
control_value |= (0b0110 << 26) | (1 << 31);
|
||||
|
||||
m_plane_registers->stride = m_horizontal_stride;
|
||||
m_shadow_registers.stride = m_horizontal_stride;
|
||||
m_plane_registers->linear_offset = 0;
|
||||
m_shadow_registers.linear_offset = 0;
|
||||
m_plane_registers->surface_base = m_aperture_start.get();
|
||||
m_shadow_registers.surface_base = m_aperture_start.get();
|
||||
m_plane_registers->control = control_value;
|
||||
m_shadow_registers.control = control_value;
|
||||
return {};
|
||||
}
|
||||
}
|
26
Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h
Normal file
26
Kernel/Devices/GPU/Intel/Plane/G33DisplayPlane.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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/Plane/DisplayPlane.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class IntelDisplayConnectorGroup;
|
||||
class IntelG33DisplayPlane final : public IntelDisplayPlane {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<IntelG33DisplayPlane>> create_with_physical_address(PhysicalAddress plane_registers_start_address);
|
||||
|
||||
virtual ErrorOr<void> enable(Badge<IntelDisplayConnectorGroup>) override;
|
||||
|
||||
private:
|
||||
explicit IntelG33DisplayPlane(Memory::TypedMapping<volatile IntelDisplayPlane::PlaneRegisters> plane_registers_mapping);
|
||||
};
|
||||
}
|
|
@ -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