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:
Liav A 2022-05-05 13:25:51 +03:00 committed by Andrew Kaster
parent e2dde64628
commit 2def16a3d2
Notes: sideshowbarker 2024-07-17 00:00:11 +09:00
19 changed files with 1211 additions and 689 deletions

View file

@ -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);
}
}