mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-30 06:52:52 +00:00
Kernel/Graphics: Introduce the IntelDisplayConnectorGroup class
In the real world, graphics hardware tend to have multiple display connectors. However, usually the connectors share one register space but still keeping different PLL timings and display lanes. This new class should represent a group of multiple display connectors working together in the same Intel graphics adapter. This opens an opportunity to abstract the interface so we could support future Intel iGPU generations. This is also a preparation before the driver can support newer devices and utilize their capabilities. The mentioned preparation is applied in a these aspects: 1. The code is splitted into more classes to adjust to future expansion. 2 classes are introduced: IntelDisplayPlane and IntelDisplayTranscoder, so the IntelDisplayPlane controls the plane registers and second class controls the pipeline (transcoder, encoder) registers. On gen4 it's not really useful because there are probably one plane and one encoder to care about, but in future generations, there are likely to be multiple transcoders and planes to accommodate multi head support. 2. The set_edid_bytes method in the DisplayConnector class can now be told to not assume the provided EDID bytes are always invalid. Therefore it can refrain from printing error messages if this flag parameter is true. This is useful for supporting real hardware situation when on boot not all ports are connected to a monitor, which can result in floating bus condition (essentially all the bytes we read are 0xFF). 3. An IntelNativeDisplayConnector could now be set to flag other types of connections such as eDP (embedded DisplayPort), Analog output, etc. This is important because on the Intel gen4 graphics we could assume to have one analog output connector, but on future generations this is very likely to not be the case, as there might be no VGA outputs, but rather only an eDP connector which is converted to VGA by a design choice of the motherboard manufacturer. 4. Add ConnectorIndex to IntelNativeDisplayConnector class - Currently this is used to verify we always handle the correct connector when doing modesetting. Later, it will be used to locate special settings needed when handling connector requests. 5. Prepare to support more types of display planes. For example, the Intel Skylake register set for display planes is a bit different, so let's ensure we can properly support it in the near future.
This commit is contained in:
parent
e2dde64628
commit
2def16a3d2
Notes:
sideshowbarker
2024-07-17 00:00:11 +09:00
Author: https://github.com/supercomputer7
Commit: 2def16a3d2
Pull-request: https://github.com/SerenityOS/serenity/pull/17283
Reviewed-by: https://github.com/ADKaster ✅
19 changed files with 1211 additions and 689 deletions
|
@ -10,194 +10,47 @@
|
|||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Graphics/Intel/DisplayConnectorGroup.h>
|
||||
#include <Kernel/Graphics/Intel/NativeDisplayConnector.h>
|
||||
#include <Kernel/Memory/Region.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace IntelGraphics {
|
||||
|
||||
#define DDC2_I2C_ADDRESS 0x50
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
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));
|
||||
}
|
||||
|
||||
static Graphics::Modesetting calculate_modesetting_from_edid(EDID::Parser& edid, size_t index)
|
||||
ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console(Badge<IntelDisplayConnectorGroup>)
|
||||
{
|
||||
auto details = edid.detailed_timing(index).release_value();
|
||||
|
||||
Graphics::Modesetting mode;
|
||||
VERIFY(details.pixel_clock_khz());
|
||||
mode.pixel_clock_in_khz = details.pixel_clock_khz();
|
||||
|
||||
size_t horizontal_active = details.horizontal_addressable_pixels();
|
||||
size_t horizontal_sync_offset = details.horizontal_front_porch_pixels();
|
||||
|
||||
mode.horizontal.active = horizontal_active;
|
||||
mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset;
|
||||
mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + details.horizontal_sync_pulse_width_pixels();
|
||||
mode.horizontal.total = horizontal_active + details.horizontal_blanking_pixels();
|
||||
|
||||
size_t vertical_active = details.vertical_addressable_lines();
|
||||
size_t vertical_sync_offset = details.vertical_front_porch_lines();
|
||||
|
||||
mode.vertical.active = vertical_active;
|
||||
mode.vertical.sync_start = vertical_active + vertical_sync_offset;
|
||||
mode.vertical.sync_end = vertical_active + vertical_sync_offset + details.vertical_sync_pulse_width_lines();
|
||||
mode.vertical.total = vertical_active + details.vertical_blanking_lines();
|
||||
return mode;
|
||||
}
|
||||
|
||||
static bool check_pll_settings(IntelGraphics::PLLSettings const& settings, size_t reference_clock, IntelGraphics::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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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<IntelGraphics::PLLSettings> IntelNativeDisplayConnector::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits)
|
||||
{
|
||||
IntelGraphics::PLLSettings settings;
|
||||
IntelGraphics::PLLSettings best_settings;
|
||||
// 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;
|
||||
m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), width, height, pitch);
|
||||
GraphicsManagement::the().set_console(*m_framebuffer_console);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<IntelNativeDisplayConnector>> IntelNativeDisplayConnector::try_create(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, PhysicalAddress registers_region_address, size_t registers_region_length)
|
||||
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)
|
||||
{
|
||||
auto registers_region = TRY(MM.allocate_kernel_region(PhysicalAddress(registers_region_address), registers_region_length, "Intel Native Graphics Registers"sv, Memory::Region::Access::ReadWrite));
|
||||
|
||||
// FIXME: Try to put the address as parameter to this function to allow creating this DisplayConnector for many generations...
|
||||
auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(registers_region_address.offset(to_underlying(IntelGraphics::RegisterIndex::GMBusClock))));
|
||||
|
||||
auto connector = TRY(DeviceManagement::try_create_device<IntelNativeDisplayConnector>(framebuffer_address, framebuffer_resource_size, move(gmbus_connector), move(registers_region)));
|
||||
TRY(connector->initialize_gmbus_settings_and_read_edid());
|
||||
// Note: This is very important to set the resolution to something safe so we
|
||||
// can create a framebuffer console with valid resolution.
|
||||
{
|
||||
SpinlockLocker control_lock(connector->m_control_lock);
|
||||
TRY(connector->set_safe_mode_setting());
|
||||
}
|
||||
TRY(connector->create_attached_framebuffer_console());
|
||||
return connector;
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::initialize_gmbus_settings_and_read_edid()
|
||||
void IntelNativeDisplayConnector::set_edid_bytes(Badge<IntelDisplayConnectorGroup>, Array<u8, 128> const& raw_bytes)
|
||||
{
|
||||
gmbus_read_edid();
|
||||
return {};
|
||||
// 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)
|
||||
|
@ -210,6 +63,12 @@ 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());
|
||||
|
@ -229,398 +88,9 @@ ErrorOr<void> IntelNativeDisplayConnector::flush_first_surface()
|
|||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::create_attached_framebuffer_console()
|
||||
{
|
||||
m_framebuffer_console = Graphics::ContiguousFramebufferConsole::initialize(m_framebuffer_address.value(), m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride);
|
||||
GraphicsManagement::the().set_console(*m_framebuffer_console);
|
||||
return {};
|
||||
}
|
||||
|
||||
IntelNativeDisplayConnector::IntelNativeDisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, NonnullOwnPtr<GMBusConnector> gmbus_connector, NonnullOwnPtr<Memory::Region> registers_region)
|
||||
: DisplayConnector(framebuffer_address, framebuffer_resource_size, true)
|
||||
, m_registers_region(move(registers_region))
|
||||
, m_gmbus_connector(move(gmbus_connector))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::set_mode_setting(DisplayConnector::ModeSetting const&)
|
||||
{
|
||||
return Error::from_errno(ENOTIMPL);
|
||||
}
|
||||
|
||||
ErrorOr<void> IntelNativeDisplayConnector::set_safe_mode_setting()
|
||||
{
|
||||
set_safe_crt_resolution();
|
||||
return {};
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_vga_plane()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
}
|
||||
|
||||
[[maybe_unused]] static StringView convert_register_index_to_string(IntelGraphics::RegisterIndex index)
|
||||
{
|
||||
switch (index) {
|
||||
case IntelGraphics::RegisterIndex::PipeAConf:
|
||||
return "PipeAConf"sv;
|
||||
case IntelGraphics::RegisterIndex::PipeBConf:
|
||||
return "PipeBConf"sv;
|
||||
case IntelGraphics::RegisterIndex::GMBusData:
|
||||
return "GMBusData"sv;
|
||||
case IntelGraphics::RegisterIndex::GMBusStatus:
|
||||
return "GMBusStatus"sv;
|
||||
case IntelGraphics::RegisterIndex::GMBusCommand:
|
||||
return "GMBusCommand"sv;
|
||||
case IntelGraphics::RegisterIndex::GMBusClock:
|
||||
return "GMBusClock"sv;
|
||||
case IntelGraphics::RegisterIndex::DisplayPlaneAControl:
|
||||
return "DisplayPlaneAControl"sv;
|
||||
case IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset:
|
||||
return "DisplayPlaneALinearOffset"sv;
|
||||
case IntelGraphics::RegisterIndex::DisplayPlaneAStride:
|
||||
return "DisplayPlaneAStride"sv;
|
||||
case IntelGraphics::RegisterIndex::DisplayPlaneASurface:
|
||||
return "DisplayPlaneASurface"sv;
|
||||
case IntelGraphics::RegisterIndex::DPLLDivisorA0:
|
||||
return "DPLLDivisorA0"sv;
|
||||
case IntelGraphics::RegisterIndex::DPLLDivisorA1:
|
||||
return "DPLLDivisorA1"sv;
|
||||
case IntelGraphics::RegisterIndex::DPLLControlA:
|
||||
return "DPLLControlA"sv;
|
||||
case IntelGraphics::RegisterIndex::DPLLControlB:
|
||||
return "DPLLControlB"sv;
|
||||
case IntelGraphics::RegisterIndex::DPLLMultiplierA:
|
||||
return "DPLLMultiplierA"sv;
|
||||
case IntelGraphics::RegisterIndex::HTotalA:
|
||||
return "HTotalA"sv;
|
||||
case IntelGraphics::RegisterIndex::HBlankA:
|
||||
return "HBlankA"sv;
|
||||
case IntelGraphics::RegisterIndex::HSyncA:
|
||||
return "HSyncA"sv;
|
||||
case IntelGraphics::RegisterIndex::VTotalA:
|
||||
return "VTotalA"sv;
|
||||
case IntelGraphics::RegisterIndex::VBlankA:
|
||||
return "VBlankA"sv;
|
||||
case IntelGraphics::RegisterIndex::VSyncA:
|
||||
return "VSyncA"sv;
|
||||
case IntelGraphics::RegisterIndex::PipeASource:
|
||||
return "PipeASource"sv;
|
||||
case IntelGraphics::RegisterIndex::AnalogDisplayPort:
|
||||
return "AnalogDisplayPort"sv;
|
||||
case IntelGraphics::RegisterIndex::VGADisplayPlaneControl:
|
||||
return "VGADisplayPlaneControl"sv;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::write_to_register(IntelGraphics::RegisterIndex index, u32 value) const
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
SpinlockLocker lock(m_registers_lock);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_register_index_to_string(index), value);
|
||||
auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(to_underlying(index)).as_ptr();
|
||||
*reg = value;
|
||||
}
|
||||
u32 IntelNativeDisplayConnector::read_from_register(IntelGraphics::RegisterIndex index) const
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
SpinlockLocker lock(m_registers_lock);
|
||||
auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(to_underlying(index)).as_ptr();
|
||||
u32 value = *reg;
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_register_index_to_string(index), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IntelNativeDisplayConnector::pipe_a_enabled() const
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
|
||||
}
|
||||
|
||||
bool IntelNativeDisplayConnector::pipe_b_enabled() const
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
return read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & (1 << 30);
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::gmbus_read_edid()
|
||||
{
|
||||
Array<u8, 128> crt_edid_bytes {};
|
||||
{
|
||||
SpinlockLocker control_lock(m_control_lock);
|
||||
MUST(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0));
|
||||
MUST(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size()));
|
||||
}
|
||||
set_edid_bytes(crt_edid_bytes);
|
||||
}
|
||||
|
||||
bool IntelNativeDisplayConnector::is_resolution_valid(size_t, size_t)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
// FIXME: Check that we are able to modeset to the requested resolution!
|
||||
return true;
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_output()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
disable_dac_output();
|
||||
disable_all_planes();
|
||||
disable_pipe_a();
|
||||
disable_pipe_b();
|
||||
disable_dpll();
|
||||
disable_vga_emulation();
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_output(PhysicalAddress fb_address, size_t width)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(!pipe_a_enabled());
|
||||
enable_pipe_a();
|
||||
enable_primary_plane(fb_address, width);
|
||||
enable_dac_output();
|
||||
}
|
||||
|
||||
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 IntelNativeDisplayConnector::set_safe_crt_resolution()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
SpinlockLocker modeset_lock(m_modeset_lock);
|
||||
|
||||
// Note: Just in case we still allow access to VGA IO ports, disable it now.
|
||||
GraphicsManagement::the().disable_vga_emulation_access_permanently();
|
||||
|
||||
// FIXME: Get the requested resolution from the EDID!!
|
||||
auto modesetting = calculate_modesetting_from_edid(m_edid_parser.value(), 0);
|
||||
|
||||
disable_output();
|
||||
|
||||
auto dac_multiplier = compute_dac_multiplier(modesetting.pixel_clock_in_khz);
|
||||
auto pll_settings = create_pll_settings((1000 * modesetting.pixel_clock_in_khz * dac_multiplier), 96'000'000, IntelGraphics::G35Limits);
|
||||
if (!pll_settings.has_value())
|
||||
VERIFY_NOT_REACHED();
|
||||
auto settings = pll_settings.value();
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
|
||||
enable_dpll_without_vga(pll_settings.value(), dac_multiplier);
|
||||
set_display_timings(modesetting);
|
||||
enable_output(m_framebuffer_address.value(), modesetting.horizontal.blanking_start());
|
||||
|
||||
DisplayConnector::ModeSetting mode_set {
|
||||
.horizontal_stride = modesetting.horizontal.blanking_start() * sizeof(u32),
|
||||
.pixel_clock_in_khz = 0,
|
||||
.horizontal_active = modesetting.horizontal.blanking_start(),
|
||||
.horizontal_front_porch_pixels = 0,
|
||||
.horizontal_sync_time_pixels = 0,
|
||||
.horizontal_blank_pixels = 0,
|
||||
.vertical_active = modesetting.vertical.blanking_start(),
|
||||
.vertical_front_porch_lines = 0,
|
||||
.vertical_sync_time_lines = 0,
|
||||
.vertical_blank_lines = 0,
|
||||
.horizontal_offset = 0,
|
||||
.vertical_offset = 0,
|
||||
};
|
||||
|
||||
m_current_mode_setting = mode_set;
|
||||
|
||||
if (m_framebuffer_console)
|
||||
m_framebuffer_console->set_resolution(m_current_mode_setting.horizontal_active, m_current_mode_setting.vertical_active, m_current_mode_setting.horizontal_stride);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::set_display_timings(Graphics::Modesetting const& modesetting)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31)));
|
||||
VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30)));
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (modesetting.horizontal.active - 1), (modesetting.horizontal.total - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::HTotalA, (modesetting.horizontal.active - 1) | (modesetting.horizontal.total - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (modesetting.horizontal.blanking_start() - 1), (modesetting.horizontal.blanking_end() - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::HBlankA, (modesetting.horizontal.blanking_start() - 1) | (modesetting.horizontal.blanking_end() - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (modesetting.horizontal.sync_start - 1), (modesetting.horizontal.sync_end - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::HSyncA, (modesetting.horizontal.sync_start - 1) | (modesetting.horizontal.sync_end - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (modesetting.vertical.active - 1), (modesetting.vertical.total - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::VTotalA, (modesetting.vertical.active - 1) | (modesetting.vertical.total - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (modesetting.vertical.blanking_start() - 1), (modesetting.vertical.blanking_end() - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::VBlankA, (modesetting.vertical.blanking_start() - 1) | (modesetting.vertical.blanking_end() - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (modesetting.vertical.sync_start - 1), (modesetting.vertical.sync_end - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::VSyncA, (modesetting.vertical.sync_start - 1) | (modesetting.vertical.sync_end - 1) << 16);
|
||||
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (modesetting.vertical.active - 1), (modesetting.horizontal.active - 1));
|
||||
write_to_register(IntelGraphics::RegisterIndex::PipeASource, (modesetting.vertical.active - 1) | (modesetting.horizontal.active - 1) << 16);
|
||||
|
||||
microseconds_delay(200);
|
||||
}
|
||||
|
||||
bool IntelNativeDisplayConnector::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const
|
||||
{
|
||||
size_t current_time = 0;
|
||||
while (current_time < milliseconds_timeout) {
|
||||
if (pipe_a_enabled())
|
||||
return true;
|
||||
microseconds_delay(1000);
|
||||
current_time++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool IntelNativeDisplayConnector::wait_for_disabled_pipe_a(size_t milliseconds_timeout) const
|
||||
{
|
||||
size_t current_time = 0;
|
||||
while (current_time < milliseconds_timeout) {
|
||||
if (!pipe_a_enabled())
|
||||
return true;
|
||||
microseconds_delay(1000);
|
||||
current_time++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IntelNativeDisplayConnector::wait_for_disabled_pipe_b(size_t milliseconds_timeout) const
|
||||
{
|
||||
size_t current_time = 0;
|
||||
while (current_time < milliseconds_timeout) {
|
||||
if (!pipe_b_enabled())
|
||||
return true;
|
||||
microseconds_delay(1000);
|
||||
current_time++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_dpll()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, 0);
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLControlB, 0);
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_pipe_a()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::PipeAConf, 0);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A");
|
||||
wait_for_disabled_pipe_a(100);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done.");
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_pipe_b()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::PipeAConf, 0);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B");
|
||||
wait_for_disabled_pipe_b(100);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done.");
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_pipe_a()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 31)));
|
||||
VERIFY(!(read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30)));
|
||||
write_to_register(IntelGraphics::RegisterIndex::PipeAConf, (1 << 31) | (1 << 24));
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A");
|
||||
// FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!!
|
||||
wait_for_enabled_pipe_a(100);
|
||||
dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A - done.");
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_primary_plane(PhysicalAddress fb_address, size_t width)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
VERIFY(((width * 4) % 64 == 0));
|
||||
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAStride, width * 4);
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset, 0);
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneASurface, fb_address.get());
|
||||
|
||||
// FIXME: Serenity uses BGR 32 bit pixel format, but maybe we should try to determine it somehow!
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, (0b0110 << 26) | (1 << 31));
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::set_dpll_registers(IntelGraphics::PLLSettings const& settings)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA0, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLDivisorA1, (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16));
|
||||
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, 0);
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_dpll_without_vga(IntelGraphics::PLLSettings const& settings, size_t dac_multiplier)
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
|
||||
set_dpll_registers(settings);
|
||||
|
||||
microseconds_delay(200);
|
||||
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, (6 << 9) | (settings.p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31));
|
||||
write_to_register(IntelGraphics::RegisterIndex::DPLLMultiplierA, (dac_multiplier - 1) | ((dac_multiplier - 1) << 8));
|
||||
|
||||
// The specification says we should wait (at least) about 150 microseconds
|
||||
// after enabling the DPLL to allow the clock to stabilize
|
||||
microseconds_delay(200);
|
||||
VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31));
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_dac_output()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, 0b11 << 10);
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::enable_dac_output()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, (1 << 31));
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_vga_emulation()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl, (1 << 31));
|
||||
read_from_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl);
|
||||
}
|
||||
|
||||
void IntelNativeDisplayConnector::disable_all_planes()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_modeset_lock.is_locked());
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, 0);
|
||||
write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneBControl, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue