|
@@ -0,0 +1,605 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
+ */
|
|
|
+
|
|
|
+#include <Kernel/Graphics/Definitions.h>
|
|
|
+#include <Kernel/Graphics/IntelNativeGraphicsAdapter.h>
|
|
|
+#include <Kernel/IO.h>
|
|
|
+#include <Kernel/PhysicalAddress.h>
|
|
|
+
|
|
|
+namespace Kernel {
|
|
|
+
|
|
|
+static constexpr IntelNativeGraphicsAdapter::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
|
|
|
+};
|
|
|
+
|
|
|
+#define DDC2_I2C_ADDRESS 0x50
|
|
|
+
|
|
|
+RefPtr<IntelNativeGraphicsAdapter> IntelNativeGraphicsAdapter::initialize(PCI::Address address)
|
|
|
+{
|
|
|
+ auto id = PCI::get_id(address);
|
|
|
+ VERIFY(id.vendor_id == 0x8086);
|
|
|
+ if (id.device_id != 0x29c2)
|
|
|
+ return {};
|
|
|
+ return adopt_ref(*new IntelNativeGraphicsAdapter(address));
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static Graphics::Modesetting calculate_modesetting_from_edid(const Graphics::VideoInfoBlock& edid, size_t index)
|
|
|
+{
|
|
|
+ Graphics::Modesetting mode;
|
|
|
+ VERIFY(edid.details[0].pixel_clock);
|
|
|
+ mode.pixel_clock_in_khz = edid.details[0].pixel_clock * 10;
|
|
|
+
|
|
|
+ size_t horizontal_active = edid.details[index].horizontal_active | ((edid.details[index].horizontal_active_blank_msb >> 4) << 8);
|
|
|
+ size_t horizontal_blank = edid.details[index].horizontal_blank | ((edid.details[index].horizontal_active_blank_msb & 0xF) << 8);
|
|
|
+ size_t horizontal_sync_offset = edid.details[index].horizontal_sync_offset | ((edid.details[index].sync_msb >> 6) << 8);
|
|
|
+ size_t horizontal_sync_pulse = edid.details[index].horizontal_sync_pulse | (((edid.details[index].sync_msb >> 4) & 0x3) << 8);
|
|
|
+
|
|
|
+ mode.horizontal.active = horizontal_active;
|
|
|
+ mode.horizontal.sync_start = horizontal_active + horizontal_sync_offset;
|
|
|
+ mode.horizontal.sync_end = horizontal_active + horizontal_sync_offset + horizontal_sync_pulse;
|
|
|
+ mode.horizontal.total = horizontal_active + horizontal_blank;
|
|
|
+
|
|
|
+ size_t vertical_active = edid.details[index].vertical_active | ((edid.details[index].vertical_active_blank_msb >> 4) << 8);
|
|
|
+ size_t vertical_blank = edid.details[index].vertical_blank | ((edid.details[index].vertical_active_blank_msb & 0xF) << 8);
|
|
|
+ size_t vertical_sync_offset = (edid.details[index].vertical_sync >> 4) | (((edid.details[index].sync_msb >> 2) & 0x3) << 4);
|
|
|
+ size_t vertical_sync_pulse = (edid.details[index].vertical_sync & 0xF) | ((edid.details[index].sync_msb & 0x3) << 4);
|
|
|
+
|
|
|
+ mode.vertical.active = vertical_active;
|
|
|
+ mode.vertical.sync_start = vertical_active + vertical_sync_offset;
|
|
|
+ mode.vertical.sync_end = vertical_active + vertical_sync_offset + vertical_sync_pulse;
|
|
|
+ mode.vertical.total = vertical_active + vertical_blank;
|
|
|
+ return mode;
|
|
|
+}
|
|
|
+
|
|
|
+static bool check_pll_settings(const IntelNativeGraphicsAdapter::PLLSettings& settings, size_t reference_clock, const IntelNativeGraphicsAdapter::PLLMaxSettings& 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;
|
|
|
+}
|
|
|
+
|
|
|
+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<IntelNativeGraphicsAdapter::PLLSettings> IntelNativeGraphicsAdapter::create_pll_settings(u64 target_frequency, u64 reference_clock, const PLLMaxSettings& limits)
|
|
|
+{
|
|
|
+ IntelNativeGraphicsAdapter::PLLSettings settings;
|
|
|
+ IntelNativeGraphicsAdapter::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;
|
|
|
+ return {};
|
|
|
+}
|
|
|
+
|
|
|
+IntelNativeGraphicsAdapter::IntelNativeGraphicsAdapter(PCI::Address address)
|
|
|
+ : PCI::DeviceController(address)
|
|
|
+ , m_registers(PCI::get_BAR0(address) & 0xfffffffc)
|
|
|
+ , m_framebuffer_addr(PCI::get_BAR2(address) & 0xfffffffc)
|
|
|
+{
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Native Graphics Adapter @ {}", address);
|
|
|
+ auto bar0_space_size = PCI::get_BAR_space_size(address, 0);
|
|
|
+ VERIFY(bar0_space_size == 0x80000);
|
|
|
+ dmesgln("Intel Native Graphics Adapter @ {}, MMIO @ {}, space size is {:x} bytes", address, PhysicalAddress(PCI::get_BAR0(address)), bar0_space_size);
|
|
|
+ dmesgln("Intel Native Graphics Adapter @ {}, framebuffer @ {}", address, PhysicalAddress(PCI::get_BAR2(address)));
|
|
|
+ m_registers_region = MM.allocate_kernel_region(PhysicalAddress(PCI::get_BAR0(address)).page_base(), bar0_space_size, "Intel Native Graphics Registers", Region::Access::Read | Region::Access::Write);
|
|
|
+ PCI::enable_bus_mastering(address);
|
|
|
+ {
|
|
|
+ ScopedSpinLock control_lock(m_control_lock);
|
|
|
+ set_gmbus_default_rate();
|
|
|
+ set_gmbus_pin_pair(GMBusPinPair::DedicatedAnalog);
|
|
|
+ }
|
|
|
+ gmbus_read_edid();
|
|
|
+ auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 0);
|
|
|
+ dmesgln("Intel Native Graphics Adapter @ {}, preferred resolution is {:d}x{:d}", address, modesetting.horizontal.active, modesetting.vertical.active);
|
|
|
+
|
|
|
+ set_crt_resolution(modesetting.horizontal.active, modesetting.vertical.active);
|
|
|
+}
|
|
|
+
|
|
|
+static inline const char* convert_register_index_to_string(IntelGraphics::RegisterIndex index)
|
|
|
+{
|
|
|
+ switch (index) {
|
|
|
+ case IntelGraphics::RegisterIndex::PipeAConf:
|
|
|
+ return "PipeAConf";
|
|
|
+ case IntelGraphics::RegisterIndex::PipeBConf:
|
|
|
+ return "PipeBConf";
|
|
|
+ case IntelGraphics::RegisterIndex::GMBusData:
|
|
|
+ return "GMBusData";
|
|
|
+ case IntelGraphics::RegisterIndex::GMBusStatus:
|
|
|
+ return "GMBusStatus";
|
|
|
+ case IntelGraphics::RegisterIndex::GMBusCommand:
|
|
|
+ return "GMBusCommand";
|
|
|
+ case IntelGraphics::RegisterIndex::GMBusClock:
|
|
|
+ return "GMBusClock";
|
|
|
+ case IntelGraphics::RegisterIndex::DisplayPlaneAControl:
|
|
|
+ return "DisplayPlaneAControl";
|
|
|
+ case IntelGraphics::RegisterIndex::DisplayPlaneALinearOffset:
|
|
|
+ return "DisplayPlaneALinearOffset";
|
|
|
+ case IntelGraphics::RegisterIndex::DisplayPlaneAStride:
|
|
|
+ return "DisplayPlaneAStride";
|
|
|
+ case IntelGraphics::RegisterIndex::DisplayPlaneASurface:
|
|
|
+ return "DisplayPlaneASurface";
|
|
|
+ case IntelGraphics::RegisterIndex::DPLLDivisorA0:
|
|
|
+ return "DPLLDivisorA0";
|
|
|
+ case IntelGraphics::RegisterIndex::DPLLDivisorA1:
|
|
|
+ return "DPLLDivisorA1";
|
|
|
+ case IntelGraphics::RegisterIndex::DPLLControlA:
|
|
|
+ return "DPLLControlA";
|
|
|
+ case IntelGraphics::RegisterIndex::DPLLControlB:
|
|
|
+ return "DPLLControlB";
|
|
|
+ case IntelGraphics::RegisterIndex::DPLLMultiplierA:
|
|
|
+ return "DPLLMultiplierA";
|
|
|
+ case IntelGraphics::RegisterIndex::HTotalA:
|
|
|
+ return "HTotalA";
|
|
|
+ case IntelGraphics::RegisterIndex::HBlankA:
|
|
|
+ return "HBlankA";
|
|
|
+ case IntelGraphics::RegisterIndex::HSyncA:
|
|
|
+ return "HSyncA";
|
|
|
+ case IntelGraphics::RegisterIndex::VTotalA:
|
|
|
+ return "VTotalA";
|
|
|
+ case IntelGraphics::RegisterIndex::VBlankA:
|
|
|
+ return "VBlankA";
|
|
|
+ case IntelGraphics::RegisterIndex::VSyncA:
|
|
|
+ return "VSyncA";
|
|
|
+ case IntelGraphics::RegisterIndex::PipeASource:
|
|
|
+ return "PipeASource";
|
|
|
+ case IntelGraphics::RegisterIndex::AnalogDisplayPort:
|
|
|
+ return "AnalogDisplayPort";
|
|
|
+ case IntelGraphics::RegisterIndex::VGADisplayPlaneControl:
|
|
|
+ return "VGADisplayPlaneControl";
|
|
|
+ default:
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::write_to_register(IntelGraphics::RegisterIndex index, u32 value) const
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_registers_region);
|
|
|
+ ScopedSpinLock lock(m_registers_lock);
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics {}: Write to {} value of {:x}", pci_address(), convert_register_index_to_string(index), value);
|
|
|
+ auto* reg = (volatile u32*)m_registers_region->vaddr().offset(index).as_ptr();
|
|
|
+ *reg = value;
|
|
|
+}
|
|
|
+u32 IntelNativeGraphicsAdapter::read_from_register(IntelGraphics::RegisterIndex index) const
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_registers_region);
|
|
|
+ ScopedSpinLock lock(m_registers_lock);
|
|
|
+ auto* reg = (volatile u32*)m_registers_region->vaddr().offset(index).as_ptr();
|
|
|
+ u32 value = *reg;
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics {}: Read from {} value of {:x}", pci_address(), convert_register_index_to_string(index), value);
|
|
|
+ return value;
|
|
|
+}
|
|
|
+
|
|
|
+bool IntelNativeGraphicsAdapter::pipe_a_enabled() const
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ return read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & (1 << 30);
|
|
|
+}
|
|
|
+bool IntelNativeGraphicsAdapter::pipe_b_enabled() const
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ return read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & (1 << 30);
|
|
|
+}
|
|
|
+
|
|
|
+bool IntelNativeGraphicsAdapter::gmbus_wait_for(GMBusStatus desired_status, Optional<size_t> milliseconds_timeout)
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ size_t milliseconds_passed = 0;
|
|
|
+ while (1) {
|
|
|
+ if (milliseconds_timeout.has_value() && milliseconds_timeout.value() < milliseconds_passed)
|
|
|
+ return false;
|
|
|
+ full_memory_barrier();
|
|
|
+ u32 status = read_from_register(IntelGraphics::RegisterIndex::GMBusStatus);
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ IO::delay(1000);
|
|
|
+ milliseconds_passed++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::gmbus_write(unsigned address, u32 byte)
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(address < 256);
|
|
|
+ full_memory_barrier();
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::GMBusData, byte);
|
|
|
+ full_memory_barrier();
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, ((address << 1) | (1 << 16) | (GMBusCycle::Wait << 25) | (1 << 30)));
|
|
|
+ full_memory_barrier();
|
|
|
+ gmbus_wait_for(GMBusStatus::TransactionCompletion, {});
|
|
|
+}
|
|
|
+void IntelNativeGraphicsAdapter::gmbus_read(unsigned address, u8* buf, size_t length)
|
|
|
+{
|
|
|
+ VERIFY(address < 256);
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ size_t nread = 0;
|
|
|
+ auto read_set = [&] {
|
|
|
+ full_memory_barrier();
|
|
|
+ u32 data = read_from_register(IntelGraphics::RegisterIndex::GMBusData);
|
|
|
+ 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();
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::GMBusCommand, (1 | (address << 1) | (length << 16) | (GMBusCycle::Wait << 25) | (1 << 30)));
|
|
|
+ full_memory_barrier();
|
|
|
+ while (nread < length) {
|
|
|
+ gmbus_wait_for(GMBusStatus::HardwareReady, {});
|
|
|
+ read_set();
|
|
|
+ }
|
|
|
+ gmbus_wait_for(GMBusStatus::TransactionCompletion, {});
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::gmbus_read_edid()
|
|
|
+{
|
|
|
+ ScopedSpinLock control_lock(m_control_lock);
|
|
|
+ gmbus_write(DDC2_I2C_ADDRESS, 0);
|
|
|
+ gmbus_read(DDC2_I2C_ADDRESS, (u8*)&m_crt_edid, sizeof(Graphics::VideoInfoBlock));
|
|
|
+}
|
|
|
+
|
|
|
+bool IntelNativeGraphicsAdapter::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 IntelNativeGraphicsAdapter::disable_output()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ disable_dac_output();
|
|
|
+ disable_all_planes();
|
|
|
+ disable_pipe_a();
|
|
|
+ disable_pipe_b();
|
|
|
+ disable_vga_emulation();
|
|
|
+ disable_dpll();
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::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();
|
|
|
+}
|
|
|
+
|
|
|
+bool IntelNativeGraphicsAdapter::set_crt_resolution(size_t width, size_t height)
|
|
|
+{
|
|
|
+ ScopedSpinLock control_lock(m_control_lock);
|
|
|
+ ScopedSpinLock modeset_lock(m_modeset_lock);
|
|
|
+ if (!is_resolution_valid(width, height)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // FIXME: Get the requested resolution from the EDID!!
|
|
|
+ auto modesetting = calculate_modesetting_from_edid(m_crt_edid, 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, 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);
|
|
|
+ set_dpll_registers(pll_settings.value(), dac_multiplier);
|
|
|
+ set_display_timings(modesetting);
|
|
|
+ auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
|
|
|
+ VERIFY(!address.is_null());
|
|
|
+ enable_output(address, width);
|
|
|
+
|
|
|
+ m_framebuffer_width = width;
|
|
|
+ m_framebuffer_height = height;
|
|
|
+ m_framebuffer_stride = width * 4;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::set_display_timings(const Graphics::Modesetting& 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);
|
|
|
+
|
|
|
+ IO::delay(200);
|
|
|
+}
|
|
|
+
|
|
|
+bool IntelNativeGraphicsAdapter::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;
|
|
|
+ IO::delay(1000);
|
|
|
+ current_time++;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+bool IntelNativeGraphicsAdapter::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;
|
|
|
+ IO::delay(1000);
|
|
|
+ current_time++;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool IntelNativeGraphicsAdapter::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;
|
|
|
+ IO::delay(1000);
|
|
|
+ current_time++;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::disable_dpll()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000);
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::DPLLControlB, read_from_register(IntelGraphics::RegisterIndex::DPLLControlB) & ~0x80000000);
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::disable_pipe_a()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeAConf) & ~0x80000000);
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A");
|
|
|
+ wait_for_disabled_pipe_a(100);
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done.");
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::disable_pipe_b()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::PipeAConf, read_from_register(IntelGraphics::RegisterIndex::PipeBConf) & ~0x80000000);
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B");
|
|
|
+ wait_for_disabled_pipe_b(100);
|
|
|
+ dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done.");
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::set_gmbus_default_rate()
|
|
|
+{
|
|
|
+ // FIXME: Verify GMBUS Rate Select is set only when GMBUS is idle
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ // Set the rate to 100KHz
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::GMBusClock, read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & ~(0b111 << 8));
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::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, read_from_register(IntelGraphics::RegisterIndex::PipeAConf) | 0x80000000);
|
|
|
+ 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 IntelNativeGraphicsAdapter::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, (read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & (~(0b1111 << 26))) | (0b0110 << 26) | (1 << 31));
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::set_dpll_registers(const PLLSettings& settings, size_t dac_multiplier)
|
|
|
+{
|
|
|
+ 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, read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & ~0x80000000);
|
|
|
+
|
|
|
+ IO::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
|
|
|
+ IO::delay(200);
|
|
|
+ VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31));
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::set_gmbus_pin_pair(GMBusPinPair pin_pair)
|
|
|
+{
|
|
|
+ // FIXME: Verify GMBUS is idle
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::GMBusClock, (read_from_register(IntelGraphics::RegisterIndex::GMBusClock) & (~0b111)) | (pin_pair & 0b111));
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::disable_dac_output()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, 0b11 << 10);
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::enable_dac_output()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::AnalogDisplayPort, (read_from_register(IntelGraphics::RegisterIndex::AnalogDisplayPort) & (~(0b11 << 10))) | 0x80000000);
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::disable_vga_emulation()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl, (read_from_register(IntelGraphics::RegisterIndex::VGADisplayPlaneControl) & (~(1 << 30))) | 0x80000000);
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::disable_all_planes()
|
|
|
+{
|
|
|
+ VERIFY(m_control_lock.is_locked());
|
|
|
+ VERIFY(m_modeset_lock.is_locked());
|
|
|
+ write_to_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl, read_from_register(IntelGraphics::RegisterIndex::DisplayPlaneAControl) & ~(1 << 31));
|
|
|
+}
|
|
|
+
|
|
|
+void IntelNativeGraphicsAdapter::initialize_framebuffer_devices()
|
|
|
+{
|
|
|
+ auto address = PhysicalAddress(PCI::get_BAR2(pci_address()) & 0xfffffff0);
|
|
|
+ VERIFY(!address.is_null());
|
|
|
+ VERIFY(m_framebuffer_stride != 0);
|
|
|
+ VERIFY(m_framebuffer_height != 0);
|
|
|
+ VERIFY(m_framebuffer_width != 0);
|
|
|
+ m_framebuffer = m_framebuffer = RawFramebufferDevice::create(*this, address, m_framebuffer_stride, m_framebuffer_width, m_framebuffer_height);
|
|
|
+}
|
|
|
+}
|