DisplayConnectorGroup.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*
  2. * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/Arch/Delay.h>
  7. #include <Kernel/Bus/PCI/API.h>
  8. #include <Kernel/Debug.h>
  9. #include <Kernel/Devices/DeviceManagement.h>
  10. #include <Kernel/Graphics/Console/ContiguousFramebufferConsole.h>
  11. #include <Kernel/Graphics/GraphicsManagement.h>
  12. #include <Kernel/Graphics/Intel/DisplayConnectorGroup.h>
  13. #include <Kernel/Graphics/Intel/Plane/G33DisplayPlane.h>
  14. #include <Kernel/Graphics/Intel/Transcoder/AnalogDisplayTranscoder.h>
  15. #include <Kernel/Memory/Region.h>
  16. #include <Kernel/Memory/TypedMapping.h>
  17. namespace Kernel {
  18. namespace IntelGraphics {
  19. static constexpr PLLMaxSettings G35Limits {
  20. { 20'000'000, 400'000'000 }, // values in Hz, dot_clock
  21. { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO
  22. { 3, 8 }, // n
  23. { 70, 120 }, // m
  24. { 10, 20 }, // m1
  25. { 5, 9 }, // m2
  26. { 5, 80 }, // p
  27. { 1, 8 }, // p1
  28. { 5, 10 } // p2
  29. };
  30. }
  31. static bool check_pll_settings(IntelGraphics::PLLSettings const& settings, size_t reference_clock, IntelGraphics::PLLMaxSettings const& limits)
  32. {
  33. if (settings.n < limits.n.min || settings.n > limits.n.max) {
  34. dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n);
  35. return false;
  36. }
  37. if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) {
  38. dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1);
  39. return false;
  40. }
  41. if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) {
  42. dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2);
  43. return false;
  44. }
  45. if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) {
  46. dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1);
  47. return false;
  48. }
  49. if (settings.m1 <= settings.m2) {
  50. dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1);
  51. return false;
  52. }
  53. auto m = settings.compute_m();
  54. auto p = settings.compute_p();
  55. if (m < limits.m.min || m > limits.m.max) {
  56. dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m);
  57. return false;
  58. }
  59. if (p < limits.p.min || p > limits.p.max) {
  60. dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p);
  61. return false;
  62. }
  63. auto dot = settings.compute_dot_clock(reference_clock);
  64. auto vco = settings.compute_vco(reference_clock);
  65. if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) {
  66. dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot);
  67. return false;
  68. }
  69. if (vco < limits.vco.min || vco > limits.vco.max) {
  70. dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco);
  71. return false;
  72. }
  73. return true;
  74. }
  75. static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency)
  76. {
  77. if (target_frequency >= checked_frequency)
  78. return target_frequency - checked_frequency;
  79. return checked_frequency - target_frequency;
  80. }
  81. Optional<IntelGraphics::PLLSettings> IntelDisplayConnectorGroup::create_pll_settings(u64 target_frequency, u64 reference_clock, IntelGraphics::PLLMaxSettings const& limits)
  82. {
  83. IntelGraphics::PLLSettings settings;
  84. IntelGraphics::PLLSettings best_settings;
  85. // FIXME: Is this correct for all Intel Native graphics cards?
  86. settings.p2 = 10;
  87. dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency);
  88. u64 best_difference = 0xffffffff;
  89. for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) {
  90. for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) {
  91. for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) {
  92. for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) {
  93. dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
  94. if (!check_pll_settings(settings, reference_clock, limits))
  95. continue;
  96. auto current_dot_clock = settings.compute_dot_clock(reference_clock);
  97. if (current_dot_clock == target_frequency)
  98. return settings;
  99. auto difference = find_absolute_difference(target_frequency, current_dot_clock);
  100. if (difference < best_difference && (current_dot_clock > target_frequency)) {
  101. best_settings = settings;
  102. best_difference = difference;
  103. }
  104. }
  105. }
  106. }
  107. }
  108. if (best_settings.is_valid())
  109. return best_settings;
  110. return {};
  111. }
  112. ErrorOr<NonnullLockRefPtr<IntelDisplayConnectorGroup>> IntelDisplayConnectorGroup::try_create(Badge<IntelNativeGraphicsAdapter>, Generation generation, MMIORegion const& first_region, MMIORegion const& second_region)
  113. {
  114. 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));
  115. // NOTE: 0x5100 is the offset of the start of the GMBus registers
  116. auto gmbus_connector = TRY(GMBusConnector::create_with_physical_address(first_region.pci_bar_paddr.offset(0x5100)));
  117. auto connector_group = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) IntelDisplayConnectorGroup(generation, move(gmbus_connector), move(registers_region), first_region, second_region)));
  118. TRY(connector_group->initialize_connectors());
  119. return connector_group;
  120. }
  121. IntelDisplayConnectorGroup::IntelDisplayConnectorGroup(Generation generation, NonnullOwnPtr<GMBusConnector> gmbus_connector, NonnullOwnPtr<Memory::Region> registers_region, MMIORegion const& first_region, MMIORegion const& second_region)
  122. : m_mmio_first_region(first_region)
  123. , m_mmio_second_region(second_region)
  124. , m_assigned_mmio_registers_region(m_mmio_first_region)
  125. , m_generation(generation)
  126. , m_registers_region(move(registers_region))
  127. , m_gmbus_connector(move(gmbus_connector))
  128. {
  129. }
  130. ErrorOr<void> IntelDisplayConnectorGroup::initialize_gen4_connectors()
  131. {
  132. // NOTE: Just assume we will need one Gen4 "transcoder"
  133. // NOTE: Main block of registers starting at HorizontalTotalA register (0x60000)
  134. auto transcoder_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x60000);
  135. // NOTE: DPLL registers starting at DPLLDivisorA0 register (0x6040)
  136. auto dpll_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6040);
  137. // NOTE: DPLL A control registers starting at 0x6014 (DPLL A Control register),
  138. // DPLL A Multiplier is at 0x601C, between them (at 0x6018) there is the DPLL B Control register.
  139. auto dpll_control_registers_paddr = m_mmio_first_region.pci_bar_paddr.offset(0x6014);
  140. m_transcoders[0] = TRY(IntelAnalogDisplayTranscoder::create_with_physical_addresses(transcoder_registers_paddr, dpll_registers_paddr, dpll_control_registers_paddr));
  141. m_planes[0] = TRY(IntelG33DisplayPlane::create_with_physical_address(m_mmio_first_region.pci_bar_paddr.offset(0x70180)));
  142. Array<u8, 128> crt_edid_bytes {};
  143. {
  144. SpinlockLocker control_lock(m_control_lock);
  145. TRY(m_gmbus_connector->write(Graphics::ddc2_i2c_address, 0));
  146. TRY(m_gmbus_connector->read(Graphics::ddc2_i2c_address, crt_edid_bytes.data(), crt_edid_bytes.size()));
  147. }
  148. 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));
  149. m_connectors[0]->set_edid_bytes({}, crt_edid_bytes);
  150. return {};
  151. }
  152. ErrorOr<void> IntelDisplayConnectorGroup::initialize_connectors()
  153. {
  154. // NOTE: Intel Graphics Generation 4 is pretty ancient beast, and we should not
  155. // assume we can find a VBT for it. Just initialize the (assumed) CRT connector and be done with it.
  156. if (m_generation == Generation::Gen4) {
  157. TRY(initialize_gen4_connectors());
  158. } else {
  159. VERIFY_NOT_REACHED();
  160. }
  161. for (size_t connector_index = 0; connector_index < m_connectors.size(); connector_index++) {
  162. if (!m_connectors[connector_index])
  163. continue;
  164. if (!m_connectors[connector_index]->m_edid_valid)
  165. continue;
  166. TRY(m_connectors[connector_index]->set_safe_mode_setting());
  167. TRY(m_connectors[connector_index]->create_attached_framebuffer_console({}));
  168. }
  169. return {};
  170. }
  171. ErrorOr<void> IntelDisplayConnectorGroup::set_safe_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector)
  172. {
  173. VERIFY(connector.m_modeset_lock.is_locked());
  174. if (!connector.m_edid_parser.has_value())
  175. return Error::from_errno(ENOTSUP);
  176. if (!connector.m_edid_parser.value().detailed_timing(0).has_value())
  177. return Error::from_errno(ENOTSUP);
  178. auto details = connector.m_edid_parser.value().detailed_timing(0).release_value();
  179. DisplayConnector::ModeSetting modesetting {
  180. // Note: We assume that we always use 32 bit framebuffers.
  181. .horizontal_stride = details.horizontal_addressable_pixels() * sizeof(u32),
  182. .pixel_clock_in_khz = details.pixel_clock_khz(),
  183. .horizontal_active = details.horizontal_addressable_pixels(),
  184. .horizontal_front_porch_pixels = details.horizontal_front_porch_pixels(),
  185. .horizontal_sync_time_pixels = details.horizontal_sync_pulse_width_pixels(),
  186. .horizontal_blank_pixels = details.horizontal_blanking_pixels(),
  187. .vertical_active = details.vertical_addressable_lines(),
  188. .vertical_front_porch_lines = details.vertical_front_porch_lines(),
  189. .vertical_sync_time_lines = details.vertical_sync_pulse_width_lines(),
  190. .vertical_blank_lines = details.vertical_blanking_lines(),
  191. .horizontal_offset = 0,
  192. .vertical_offset = 0,
  193. };
  194. return set_mode_setting(connector, modesetting);
  195. }
  196. ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(Badge<IntelNativeDisplayConnector>, IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
  197. {
  198. return set_mode_setting(connector, mode_setting);
  199. }
  200. ErrorOr<void> IntelDisplayConnectorGroup::set_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
  201. {
  202. VERIFY(connector.m_modeset_lock.is_locked());
  203. VERIFY(to_underlying(connector.connector_index()) < m_connectors.size());
  204. VERIFY(&connector == m_connectors[to_underlying(connector.connector_index())].ptr());
  205. DisplayConnector::ModeSetting actual_mode_setting = mode_setting;
  206. actual_mode_setting.horizontal_stride = actual_mode_setting.horizontal_active * sizeof(u32);
  207. VERIFY(actual_mode_setting.horizontal_stride != 0);
  208. if (m_generation == Generation::Gen4) {
  209. TRY(set_gen4_mode_setting(connector, actual_mode_setting));
  210. } else {
  211. VERIFY_NOT_REACHED();
  212. }
  213. connector.m_current_mode_setting = actual_mode_setting;
  214. if (!connector.m_framebuffer_console.is_null())
  215. 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);
  216. return {};
  217. }
  218. ErrorOr<void> IntelDisplayConnectorGroup::set_gen4_mode_setting(IntelNativeDisplayConnector& connector, DisplayConnector::ModeSetting const& mode_setting)
  219. {
  220. VERIFY(connector.m_modeset_lock.is_locked());
  221. SpinlockLocker control_lock(m_control_lock);
  222. SpinlockLocker modeset_lock(m_modeset_lock);
  223. if (!set_crt_resolution(mode_setting))
  224. return Error::from_errno(ENOTSUP);
  225. return {};
  226. }
  227. void IntelDisplayConnectorGroup::enable_vga_plane()
  228. {
  229. VERIFY(m_control_lock.is_locked());
  230. VERIFY(m_modeset_lock.is_locked());
  231. }
  232. StringView IntelDisplayConnectorGroup::convert_analog_output_register_to_string(AnalogOutputRegisterOffset index) const
  233. {
  234. switch (index) {
  235. case AnalogOutputRegisterOffset::AnalogDisplayPort:
  236. return "AnalogDisplayPort"sv;
  237. case AnalogOutputRegisterOffset::VGADisplayPlaneControl:
  238. return "VGADisplayPlaneControl"sv;
  239. default:
  240. VERIFY_NOT_REACHED();
  241. }
  242. }
  243. void IntelDisplayConnectorGroup::write_to_general_register(RegisterOffset offset, u32 value)
  244. {
  245. VERIFY(m_control_lock.is_locked());
  246. SpinlockLocker lock(m_registers_lock);
  247. auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr();
  248. *reg = value;
  249. }
  250. u32 IntelDisplayConnectorGroup::read_from_general_register(RegisterOffset offset) const
  251. {
  252. VERIFY(m_control_lock.is_locked());
  253. SpinlockLocker lock(m_registers_lock);
  254. auto* reg = (u32 volatile*)m_registers_region->vaddr().offset(offset.value()).as_ptr();
  255. u32 value = *reg;
  256. return value;
  257. }
  258. void IntelDisplayConnectorGroup::write_to_analog_output_register(AnalogOutputRegisterOffset index, u32 value)
  259. {
  260. dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector:: Write to {} value of {:x}", convert_analog_output_register_to_string(index), value);
  261. write_to_general_register(to_underlying(index), value);
  262. }
  263. u32 IntelDisplayConnectorGroup::read_from_analog_output_register(AnalogOutputRegisterOffset index) const
  264. {
  265. u32 value = read_from_general_register(to_underlying(index));
  266. dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel Graphics Display Connector: Read from {} value of {:x}", convert_analog_output_register_to_string(index), value);
  267. return value;
  268. }
  269. void IntelDisplayConnectorGroup::write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister index, u32 value)
  270. {
  271. write_to_general_register(to_underlying(index), value);
  272. }
  273. u32 IntelDisplayConnectorGroup::read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister index) const
  274. {
  275. u32 value = read_from_general_register(to_underlying(index));
  276. return value;
  277. }
  278. bool IntelDisplayConnectorGroup::pipe_a_enabled() const
  279. {
  280. VERIFY(m_control_lock.is_locked());
  281. return read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf) & (1 << 30);
  282. }
  283. bool IntelDisplayConnectorGroup::pipe_b_enabled() const
  284. {
  285. VERIFY(m_control_lock.is_locked());
  286. return read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeBConf) & (1 << 30);
  287. }
  288. static size_t compute_dac_multiplier(size_t pixel_clock_in_khz)
  289. {
  290. dbgln_if(INTEL_GRAPHICS_DEBUG, "Intel native graphics: Pixel clock is {} KHz", pixel_clock_in_khz);
  291. VERIFY(pixel_clock_in_khz >= 25000);
  292. if (pixel_clock_in_khz >= 100000) {
  293. return 1;
  294. } else if (pixel_clock_in_khz >= 50000) {
  295. return 2;
  296. } else {
  297. return 4;
  298. }
  299. }
  300. bool IntelDisplayConnectorGroup::set_crt_resolution(DisplayConnector::ModeSetting const& mode_setting)
  301. {
  302. VERIFY(m_control_lock.is_locked());
  303. VERIFY(m_modeset_lock.is_locked());
  304. // Note: Just in case we still allow access to VGA IO ports, disable it now.
  305. GraphicsManagement::the().disable_vga_emulation_access_permanently();
  306. auto dac_multiplier = compute_dac_multiplier(mode_setting.pixel_clock_in_khz);
  307. auto pll_settings = create_pll_settings((1000 * mode_setting.pixel_clock_in_khz * dac_multiplier), 96'000'000, IntelGraphics::G35Limits);
  308. if (!pll_settings.has_value())
  309. return false;
  310. auto settings = pll_settings.value();
  311. disable_dac_output();
  312. MUST(m_planes[0]->disable({}));
  313. disable_pipe_a();
  314. disable_pipe_b();
  315. MUST(m_transcoders[0]->disable_dpll({}));
  316. disable_vga_emulation();
  317. dbgln_if(INTEL_GRAPHICS_DEBUG, "PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
  318. MUST(m_transcoders[0]->set_dpll_settings({}, settings, dac_multiplier));
  319. MUST(m_transcoders[0]->disable_dpll({}));
  320. MUST(m_transcoders[0]->enable_dpll_without_vga({}));
  321. MUST(m_transcoders[0]->set_mode_setting_timings({}, mode_setting));
  322. VERIFY(!pipe_a_enabled());
  323. enable_pipe_a();
  324. MUST(m_planes[0]->set_plane_settings({}, m_mmio_second_region.pci_bar_paddr, IntelDisplayPlane::PipeSelect::PipeA, mode_setting.horizontal_active));
  325. MUST(m_planes[0]->enable({}));
  326. enable_dac_output();
  327. return true;
  328. }
  329. bool IntelDisplayConnectorGroup::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const
  330. {
  331. size_t current_time = 0;
  332. while (current_time < milliseconds_timeout) {
  333. if (pipe_a_enabled())
  334. return true;
  335. microseconds_delay(1000);
  336. current_time++;
  337. }
  338. return false;
  339. }
  340. bool IntelDisplayConnectorGroup::wait_for_disabled_pipe_a(size_t milliseconds_timeout) const
  341. {
  342. size_t current_time = 0;
  343. while (current_time < milliseconds_timeout) {
  344. if (!pipe_a_enabled())
  345. return true;
  346. microseconds_delay(1000);
  347. current_time++;
  348. }
  349. return false;
  350. }
  351. bool IntelDisplayConnectorGroup::wait_for_disabled_pipe_b(size_t milliseconds_timeout) const
  352. {
  353. size_t current_time = 0;
  354. while (current_time < milliseconds_timeout) {
  355. if (!pipe_b_enabled())
  356. return true;
  357. microseconds_delay(1000);
  358. current_time++;
  359. }
  360. return false;
  361. }
  362. void IntelDisplayConnectorGroup::disable_pipe_a()
  363. {
  364. VERIFY(m_control_lock.is_locked());
  365. VERIFY(m_modeset_lock.is_locked());
  366. write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf, 0);
  367. dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A");
  368. wait_for_disabled_pipe_a(100);
  369. dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe A - done.");
  370. }
  371. void IntelDisplayConnectorGroup::disable_pipe_b()
  372. {
  373. VERIFY(m_control_lock.is_locked());
  374. VERIFY(m_modeset_lock.is_locked());
  375. write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf, 0);
  376. dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B");
  377. wait_for_disabled_pipe_b(100);
  378. dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe B - done.");
  379. }
  380. void IntelDisplayConnectorGroup::enable_pipe_a()
  381. {
  382. VERIFY(m_control_lock.is_locked());
  383. VERIFY(m_modeset_lock.is_locked());
  384. VERIFY(!(read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf) & (1 << 31)));
  385. VERIFY(!(read_from_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf) & (1 << 30)));
  386. write_to_global_generation_register(IntelGraphics::GlobalGenerationRegister::PipeAConf, (1 << 31) | (1 << 24));
  387. dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A");
  388. // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!!
  389. wait_for_enabled_pipe_a(100);
  390. dbgln_if(INTEL_GRAPHICS_DEBUG, "enabling Pipe A - done.");
  391. }
  392. void IntelDisplayConnectorGroup::disable_dac_output()
  393. {
  394. VERIFY(m_control_lock.is_locked());
  395. VERIFY(m_modeset_lock.is_locked());
  396. write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, 0b11 << 10);
  397. }
  398. void IntelDisplayConnectorGroup::enable_dac_output()
  399. {
  400. VERIFY(m_control_lock.is_locked());
  401. VERIFY(m_modeset_lock.is_locked());
  402. write_to_analog_output_register(AnalogOutputRegisterOffset::AnalogDisplayPort, (1 << 31));
  403. }
  404. void IntelDisplayConnectorGroup::disable_vga_emulation()
  405. {
  406. VERIFY(m_control_lock.is_locked());
  407. VERIFY(m_modeset_lock.is_locked());
  408. write_to_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl, (1 << 31));
  409. read_from_analog_output_register(AnalogOutputRegisterOffset::VGADisplayPlaneControl);
  410. }
  411. }