Serenity Operating System
at master 278 lines 13 kB view raw
1/* 2 * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <Kernel/Arch/Delay.h> 8#include <Kernel/Bus/PCI/API.h> 9#include <Kernel/Debug.h> 10#include <Kernel/Devices/DeviceManagement.h> 11#include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h> 12#include <Kernel/Graphics/GraphicsManagement.h> 13#include <Kernel/Graphics/Intel/DisplayConnectorGroup.h> 14#include <Kernel/Graphics/Intel/Plane/G33DisplayPlane.h> 15#include <Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h> 16#include <Kernel/Graphics/Intel/Transcoder/PLL.h> 17#include <Kernel/Memory/Region.h> 18#include <Kernel/Memory/TypedMapping.h> 19 20namespace Kernel { 21 22ErrorOr<NonnullLockRefPtr<IntelDisplayConnectorGroup>> IntelDisplayConnectorGroup::try_create(Badge<IntelNativeGraphicsAdapter>, IntelGraphics::Generation generation, MMIORegion const& first_region, MMIORegion const& second_region) 23{ 24 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)); 25 // NOTE: 0x5100 is the offset of the start of the GMBus registers 26 auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(first_region.pci_bar_paddr.offset(0x5100))); 27 auto connector_group = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelDisplayConnectorGroup(generation, move(gmbus_connector), move(registers_region), first_region, second_region))); 28 TRY(connector_group->initialize_connectors()); 29 return connector_group; 30} 31 32IntelDisplayConnectorGroup::IntelDisplayConnectorGroup(IntelGraphics::Generation generation, NonnullOwnPtr<GMBusConnector> gmbus_connector, NonnullOwnPtr<Memory::Region> registers_region, MMIORegion const& first_region, MMIORegion const& second_region) 33 : m_mmio_first_region(first_region) 34 , m_mmio_second_region(second_region) 35 , m_assigned_mmio_registers_region(m_mmio_first_region) 36 , m_generation(generation) 37 , m_registers_region(move(registers_region)) 38 , m_gmbus_connector(move(gmbus_connector)) 39{ 40} 41 42ErrorOr<void> IntelDisplayConnectorGroup::initialize_gen4_connectors() 43{ 44 // NOTE: Just assume we will need one Gen4 "transcoder" 45 // NOTE: Main block of registers starting at HorizontalTotalA register (0x60000) 46 auto transcoder_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x60000); 47 // NOTE: Main block of Pipe registers starting at PipeA_DSL register (0x70000) 48 auto pipe_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x70000); 49 // NOTE: DPLL registers starting at DPLLDivisorA0 register (0x6040) 50 auto dpll_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6040); 51 // NOTE: DPLL A control registers starting at 0x6014 (DPLL A Control register), 52 // DPLL A Multiplier is at 0x601C, between them (at 0x6018) there is the DPLL B Control register. 53 auto dpll_control_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6014); 54 m_transcoders[0] = TRY(IntelAnalogDisplayTranscoder::create_with_physical_addresses(transcoder_registers_paddr, pipe_registers_paddr, dpll_registers_paddr, dpll_control_registers_paddr)); 55 m_planes[0] = TRY(IntelG33DisplayPlane::create_with_physical_address(m_mmio_first_region.pci_bar_paddr.offset(0x70180))); 56 Array<u8, 128> crt_edid_bytes {}; 57 { 58 SpinlockLocker control_lock(m_control_lock); 59 TRY(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0)); 60 TRY(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size())); 61 } 62 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)); 63 m_connectors[0]->set_edid_bytes({}, crt_edid_bytes); 64 return {}; 65} 66 67ErrorOr<void> IntelDisplayConnectorGroup::initialize_connectors() 68{ 69 70 // NOTE: Intel Graphics Generation 4 is pretty ancient beast, and we should not 71 // assume we can find a VBT for it. Just initialize the (assumed) CRT connector and be done with it. 72 if (m_generation == IntelGraphics::Generation::Gen4) { 73 TRY(initialize_gen4_connectors()); 74 } else { 75 VERIFY_NOT_REACHED(); 76 } 77 78 for (size_t connector_index = 0; connector_index < m_connectors.size(); connector_index++) { 79 if (!m_connectors[connector_index]) 80 continue; 81 if (!m_connectors[connector_index]->m_edid_valid) 82 continue; 83 TRY(m_connectors[connector_index]->set_safe_mode_setting()); 84 TRY(m_connectors[connector_index]->create_attached_framebuffer_console({})); 85 } 86 return {}; 87} 88 89ErrorOr<void> IntelDisplayConnectorGroup::set_safe_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector) 90{ 91 VERIFY(connector.m_modeset_lock.is_locked()); 92 if (!connector.m_edid_parser.has_value()) 93 return Error::from_errno(ENOTSUP); 94 if (!connector.m_edid_parser.value().detailed_timing(0).has_value()) 95 return Error::from_errno(ENOTSUP); 96 auto details = connector.m_edid_parser.value().detailed_timing(0).release_value(); 97 98 DisplayConnector::ModeSetting modesetting { 99 // Note: We assume that we always use 32 bit framebuffers. 100 .horizontal_stride = details.horizontal_addressable_pixels() * sizeof(u32), 101 .pixel_clock_in_khz = details.pixel_clock_khz(), 102 .horizontal_active = details.horizontal_addressable_pixels(), 103 .horizontal_front_porch_pixels = details.horizontal_front_porch_pixels(), 104 .horizontal_sync_time_pixels = details.horizontal_sync_pulse_width_pixels(), 105 .horizontal_blank_pixels = details.horizontal_blanking_pixels(), 106 .vertical_active = details.vertical_addressable_lines(), 107 .vertical_front_porch_lines = details.vertical_front_porch_lines(), 108 .vertical_sync_time_lines = details.vertical_sync_pulse_width_lines(), 109 .vertical_blank_lines = details.vertical_blanking_lines(), 110 .horizontal_offset = 0, 111 .vertical_offset = 0, 112 }; 113 114 return set_mode_setting(connector, modesetting); 115} 116 117ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) 118{ 119 return set_mode_setting(connector, mode_setting); 120} 121 122ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) 123{ 124 VERIFY(connector.m_modeset_lock.is_locked()); 125 126 VERIFY(to_underlying(connector.connector_index()) < m_connectors.size()); 127 VERIFY(&connector == m_connectors[to_underlying(connector.connector_index())].ptr()); 128 129 DisplayConnector::ModeSetting actual_mode_setting = mode_setting; 130 actual_mode_setting.horizontal_stride = actual_mode_setting.horizontal_active * sizeof(u32); 131 VERIFY(actual_mode_setting.horizontal_stride != 0); 132 if (m_generation == IntelGraphics::Generation::Gen4) { 133 TRY(set_gen4_mode_setting(connector, actual_mode_setting)); 134 } else { 135 VERIFY_NOT_REACHED(); 136 } 137 138 connector.m_current_mode_setting = actual_mode_setting; 139 if (!connector.m_framebuffer_console.is_null()) 140 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); 141 return {}; 142} 143 144ErrorOr<void> IntelDisplayConnectorGroup::set_gen4_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting) 145{ 146 VERIFY(connector.m_modeset_lock.is_locked()); 147 SpinlockLocker control_lock(m_control_lock); 148 SpinlockLocker modeset_lock(m_modeset_lock); 149 if (!set_crt_resolution(mode_setting)) 150 return Error::from_errno(ENOTSUP); 151 return {}; 152} 153 154void IntelDisplayConnectorGroup::enable_vga_plane() 155{ 156 VERIFY(m_control_lock.is_locked()); 157 VERIFY(m_modeset_lock.is_locked()); 158} 159 160StringView IntelDisplayConnectorGroup::convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const 161{ 162 switch (index) { 163 case AnalogOutputRegisterOffset::AnalogDisplayPort: 164 return "AnalogDisplayPort"sv; 165 case AnalogOutputRegisterOffset::VGADisplayPlaneControl: 166 return "VGADisplayPlaneControl"sv; 167 default: 168 VERIFY_NOT_REACHED(); 169 } 170} 171 172void IntelDisplayConnectorGroup::write_to_general_register(RegisterOffset offset, u32 value) 173{ 174 VERIFY(m_control_lock.is_locked()); 175 SpinlockLocker lock(m_registers_lock); 176 auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr(); 177 *reg = value; 178} 179u32 IntelDisplayConnectorGroup::read_from_general_register(RegisterOffset offset) const 180{ 181 VERIFY(m_control_lock.is_locked()); 182 SpinlockLocker lock(m_registers_lock); 183 auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr(); 184 u32 value = *reg; 185 return value; 186} 187 188void IntelDisplayConnectorGroup::write_to_analog_output_register(AnalogOutputRegisterOffset index, u32 value) 189{ 190 dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_analog_output_register_to_string(index), value); 191 write_to_general_register(to_underlying(index), value); 192} 193 194u32 IntelDisplayConnectorGroup::read_from_analog_output_register(AnalogOutputRegisterOffset index) const 195{ 196 u32 value = read_from_general_register(to_underlying(index)); 197 dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_analog_output_register_to_string(index), value); 198 return value; 199} 200 201static size_t compute_dac_multiplier(size_t pixel_clock_in_khz) 202{ 203 dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz); 204 VERIFY(pixel_clock_in_khz >= 25000); 205 if (pixel_clock_in_khz >= 100000) { 206 return 1; 207 } else if (pixel_clock_in_khz >= 50000) { 208 return 2; 209 } else { 210 return 4; 211 } 212} 213 214bool IntelDisplayConnectorGroup::set_crt_resolution(DisplayConnector::ModeSetting const& mode_setting) 215{ 216 VERIFY(m_control_lock.is_locked()); 217 VERIFY(m_modeset_lock.is_locked()); 218 219 // Note: Just in case we still allow access to VGA IO ports, disable it now. 220 GraphicsManagement::the().disable_vga_emulation_access_permanently(); 221 222 auto dac_multiplier = compute_dac_multiplier(mode_setting.pixel_clock_in_khz); 223 auto pll_settings = create_pll_settings(m_generation, (1000 * mode_setting.pixel_clock_in_khz * dac_multiplier), 96'000'000); 224 if (!pll_settings.has_value()) 225 return false; 226 auto settings = pll_settings.value(); 227 228 disable_dac_output(); 229 MUST(m_planes[0]->disable({})); 230 MUST(m_transcoders[0]->disable_pipe({})); 231 MUST(m_transcoders[0]->disable_dpll({})); 232 disable_vga_emulation(); 233 234 dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2); 235 MUST(m_transcoders[0]->set_dpll_settings({}, settings, dac_multiplier)); 236 MUST(m_transcoders[0]->disable_dpll({})); 237 MUST(m_transcoders[0]->enable_dpll_without_vga({})); 238 MUST(m_transcoders[0]->set_mode_setting_timings({}, mode_setting)); 239 240 VERIFY(!m_transcoders[0]->pipe_enabled({})); 241 MUST(m_transcoders[0]->enable_pipe({})); 242 243 MUST(m_planes[0]->set_aperture_base({}, m_mmio_second_region.pci_bar_paddr)); 244 MUST(m_planes[0]->set_pipe({}, IntelDisplayPlane::PipeSelect::PipeA)); 245 MUST(m_planes[0]->set_horizontal_stride({}, mode_setting.horizontal_active * 4)); 246 MUST(m_planes[0]->set_horizontal_active_pixels_count({}, mode_setting.horizontal_active)); 247 // Note: This doesn't affect anything on the plane settings for Gen4, but we still 248 // do it for the sake of "completeness". 249 MUST(m_planes[0]->set_vertical_active_pixels_count({}, mode_setting.vertical_active)); 250 MUST(m_planes[0]->enable({})); 251 enable_dac_output(); 252 253 return true; 254} 255 256void IntelDisplayConnectorGroup::disable_dac_output() 257{ 258 VERIFY(m_control_lock.is_locked()); 259 VERIFY(m_modeset_lock.is_locked()); 260 write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, 0b11 << 10); 261} 262 263void IntelDisplayConnectorGroup::enable_dac_output() 264{ 265 VERIFY(m_control_lock.is_locked()); 266 VERIFY(m_modeset_lock.is_locked()); 267 write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, (1 << 31)); 268} 269 270void IntelDisplayConnectorGroup::disable_vga_emulation() 271{ 272 VERIFY(m_control_lock.is_locked()); 273 VERIFY(m_modeset_lock.is_locked()); 274 write_to_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl, (1 << 31)); 275 read_from_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl); 276} 277 278}