mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 03:22:43 +00:00 
			
		
		
		
	Kernel: Move all Graphics-related code into Devices/GPU directory
Like the HID, Audio and Storage subsystem, the Graphics subsystem (which
handles GPUs technically) exposes unix device files (typically in /dev).
To ensure consistency across the repository, move all related files to a
new directory under Kernel/Devices called "GPU".
Also remove the redundant "GPU" word from the VirtIO driver directory,
and the word "Graphics" from GraphicsManagement.{h,cpp} filenames.
			
			
This commit is contained in:
		
							parent
							
								
									31a7dabf02
								
							
						
					
					
						commit
						9ee098b119
					
				
					 69 changed files with 167 additions and 167 deletions
				
			
		|  | @ -0,0 +1,89 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <Kernel/Arch/Delay.h> | ||||
| #include <Kernel/Devices/GPU/Intel/Transcoder/AnalogDisplayTranscoder.h> | ||||
| #include <Kernel/Memory/PhysicalAddress.h> | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ErrorOr<NonnullOwnPtr<IntelAnalogDisplayTranscoder>> IntelAnalogDisplayTranscoder::create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address, | ||||
|     PhysicalAddress pipe_registers_start_address, PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_multiplier_register_start_address) | ||||
| { | ||||
|     auto transcoder_registers_mapping = TRY(Memory::map_typed<TranscoderRegisters volatile>(transcoder_registers_start_address, sizeof(IntelDisplayTranscoder::TranscoderRegisters), Memory::Region::Access::ReadWrite)); | ||||
|     auto pipe_registers_mapping = TRY(Memory::map_typed<PipeRegisters volatile>(pipe_registers_start_address, sizeof(IntelDisplayTranscoder::PipeRegisters), Memory::Region::Access::ReadWrite)); | ||||
|     auto dpll_registers_mapping = TRY(Memory::map_typed<DPLLRegisters volatile>(dpll_registers_start_address, sizeof(DPLLRegisters), Memory::Region::Access::ReadWrite)); | ||||
|     auto dpll_control_mapping = TRY(Memory::map_typed<DPLLControlRegisters volatile>(dpll_multiplier_register_start_address, sizeof(DPLLControlRegisters), Memory::Region::Access::ReadWrite)); | ||||
|     return adopt_nonnull_own_or_enomem(new (nothrow) IntelAnalogDisplayTranscoder(move(transcoder_registers_mapping), move(pipe_registers_mapping), move(dpll_registers_mapping), move(dpll_control_mapping))); | ||||
| } | ||||
| 
 | ||||
| IntelAnalogDisplayTranscoder::IntelAnalogDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile> transcoder_registers_mapping, | ||||
|     Memory::TypedMapping<PipeRegisters volatile> pipe_registers_mapping, Memory::TypedMapping<DPLLRegisters volatile> dpll_registers_mapping, Memory::TypedMapping<DPLLControlRegisters volatile> dpll_control_registers) | ||||
|     : IntelDisplayTranscoder(move(transcoder_registers_mapping), move(pipe_registers_mapping)) | ||||
|     , m_dpll_registers(move(dpll_registers_mapping)) | ||||
|     , m_dpll_control_registers(move(dpll_control_registers)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> IntelAnalogDisplayTranscoder::set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     u32 value = (settings.m2 - 2) | ((settings.m1 - 2) << 8) | ((settings.n - 2) << 16); | ||||
|     m_dpll_registers->divisor_a0 = value; | ||||
|     m_dpll_registers->divisor_a1 = value; | ||||
|     m_shadow_registers.dpll_divisor_a0 = value; | ||||
|     m_shadow_registers.dpll_divisor_a1 = value; | ||||
| 
 | ||||
|     // Note: We don't set the DAC multiplier now but reserve it for later usage (e.g. when enabling the DPLL)
 | ||||
|     m_shadow_registers.dpll_reserved_dac_multiplier = dac_multiplier; | ||||
|     // Note: We don't set the DPLL P1 now but reserve it for later usage (e.g. when enabling the DPLL)
 | ||||
|     m_shadow_registers.dpll_p1 = settings.p1; | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> IntelAnalogDisplayTranscoder::enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     // Explanation for Gen4 DPLL control bits:
 | ||||
|     // 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default)
 | ||||
|     // 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz)
 | ||||
|     // 3. bit 26 - set to 0b1 to ensure mode select to DAC mode
 | ||||
|     // 4. bit 28 - set to 0b1 to disable VGA mode
 | ||||
|     // 5. bit 31 - enable DPLL VCO (DPLL enabled and operational)
 | ||||
|     u32 control_value = (6 << 9) | (m_shadow_registers.dpll_p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31); | ||||
|     m_dpll_control_registers->control = control_value; | ||||
|     m_shadow_registers.dpll_control = control_value; | ||||
| 
 | ||||
|     // Explanation for Gen4 DPLL multiplier bits:
 | ||||
|     // 1. 0b0110 in bits 9 to 12 - use clock phase 6 (Default)
 | ||||
|     // 2. bits 24,25 - set to 0b00 to ensure FPA0/FPA1 (DPLL A Divisor 0, 1) divide by 10 (used for DAC modes under 270 MHz)
 | ||||
|     // 3. bit 26 - set to 0b1 to ensure mode select to DAC mode
 | ||||
|     // 4. bit 28 - set to 0b1 to disable VGA mode
 | ||||
|     // 5. bit 31 - enable DPLL VCO (DPLL enabled and operational)
 | ||||
|     u32 dac_multiplier_value = (m_shadow_registers.dpll_reserved_dac_multiplier - 1) | ((m_shadow_registers.dpll_reserved_dac_multiplier - 1) << 8); | ||||
|     m_dpll_control_registers->multiplier = dac_multiplier_value; | ||||
|     m_shadow_registers.dpll_raw_dac_multiplier = dac_multiplier_value; | ||||
| 
 | ||||
|     // The specification says we should wait (at least) about 150 microseconds
 | ||||
|     // after enabling the DPLL to allow the clock to stabilize
 | ||||
|     microseconds_delay(200); | ||||
|     for (size_t milliseconds_elapsed = 0; milliseconds_elapsed < 5; milliseconds_elapsed++) { | ||||
|         u32 control_value = m_dpll_control_registers->control; | ||||
|         if (control_value & (1 << 31)) | ||||
|             return {}; | ||||
|     } | ||||
|     return Error::from_errno(EBUSY); | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> IntelAnalogDisplayTranscoder::disable_dpll(Badge<IntelDisplayConnectorGroup>) | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     m_dpll_control_registers->control = 0; | ||||
|     m_shadow_registers.dpll_control = 0; | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,42 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/RefPtr.h> | ||||
| #include <AK/Try.h> | ||||
| #include <AK/Types.h> | ||||
| #include <Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h> | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class IntelDisplayConnectorGroup; | ||||
| class IntelAnalogDisplayTranscoder final : public IntelDisplayTranscoder { | ||||
| public: | ||||
|     static ErrorOr<NonnullOwnPtr<IntelAnalogDisplayTranscoder>> create_with_physical_addresses(PhysicalAddress transcoder_registers_start_address, | ||||
|         PhysicalAddress pipe_registers_start_address, PhysicalAddress dpll_registers_start_address, PhysicalAddress dpll_control_registers_start_address); | ||||
| 
 | ||||
|     virtual ErrorOr<void> set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) override; | ||||
|     virtual ErrorOr<void> enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) override; | ||||
|     virtual ErrorOr<void> disable_dpll(Badge<IntelDisplayConnectorGroup>) override; | ||||
| 
 | ||||
| private: | ||||
|     struct [[gnu::packed]] DPLLRegisters { | ||||
|         u32 divisor_a0; | ||||
|         u32 divisor_a1; | ||||
|     }; | ||||
| 
 | ||||
|     struct [[gnu::packed]] DPLLControlRegisters { | ||||
|         u32 control; | ||||
|         u32 padding; // On Gen4, this is the control register of DPLL B, don't touch this
 | ||||
|         u32 multiplier; | ||||
|     }; | ||||
| 
 | ||||
|     IntelAnalogDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile>, Memory::TypedMapping<PipeRegisters volatile>, Memory::TypedMapping<DPLLRegisters volatile>, Memory::TypedMapping<DPLLControlRegisters volatile>); | ||||
|     Memory::TypedMapping<DPLLRegisters volatile> m_dpll_registers; | ||||
|     Memory::TypedMapping<DPLLControlRegisters volatile> m_dpll_control_registers; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										110
									
								
								Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <Kernel/Arch/Delay.h> | ||||
| #include <Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h> | ||||
| #include <Kernel/Memory/PhysicalAddress.h> | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| IntelDisplayTranscoder::IntelDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile> registers_mapping, Memory::TypedMapping<PipeRegisters volatile> pipe_registers_mapping) | ||||
|     : m_transcoder_registers(move(registers_mapping)) | ||||
|     , m_pipe_registers(move(pipe_registers_mapping)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| IntelDisplayTranscoder::ShadowRegisters IntelDisplayTranscoder::current_registers_state() const | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     return m_shadow_registers; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> IntelDisplayTranscoder::set_mode_setting_timings(Badge<IntelDisplayConnectorGroup>, DisplayConnector::ModeSetting const& mode_setting) | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "htotal - {}, {}", (mode_setting.horizontal_active - 1), (mode_setting.horizontal_total() - 1)); | ||||
|     m_shadow_registers.horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16); | ||||
|     m_transcoder_registers->horizontal_total = ((mode_setting.horizontal_active - 1) | (mode_setting.horizontal_total() - 1) << 16); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "hblank - {}, {}", (mode_setting.horizontal_blanking_start() - 1), (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1)); | ||||
|     m_shadow_registers.horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16); | ||||
|     m_transcoder_registers->horizontal_blank = ((mode_setting.horizontal_blanking_start() - 1) | (mode_setting.horizontal_blanking_start() + mode_setting.horizontal_blank_pixels - 1) << 16); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "hsync - {}, {}", (mode_setting.horizontal_sync_start() - 1), (mode_setting.horizontal_sync_end() - 1)); | ||||
|     m_shadow_registers.horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16); | ||||
|     m_transcoder_registers->horizontal_sync = ((mode_setting.horizontal_sync_start() - 1) | (mode_setting.horizontal_sync_end() - 1) << 16); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "vtotal - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1)); | ||||
|     m_shadow_registers.vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); | ||||
|     m_transcoder_registers->vertical_total = ((mode_setting.vertical_active - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "vblank - {}, {}", (mode_setting.vertical_blanking_start() - 1), (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1)); | ||||
|     m_shadow_registers.vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); | ||||
|     m_transcoder_registers->vertical_blank = ((mode_setting.vertical_blanking_start() - 1) | (mode_setting.vertical_blanking_start() + mode_setting.vertical_blank_lines - 1) << 16); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "vsync - {}, {}", (mode_setting.vertical_sync_start() - 1), (mode_setting.vertical_sync_end() - 1)); | ||||
|     m_shadow_registers.vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16); | ||||
|     m_transcoder_registers->vertical_sync = ((mode_setting.vertical_sync_start() - 1) | (mode_setting.vertical_sync_end() - 1) << 16); | ||||
| 
 | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (mode_setting.vertical_active - 1), (mode_setting.horizontal_active - 1)); | ||||
|     m_shadow_registers.pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16); | ||||
|     m_transcoder_registers->pipe_source = ((mode_setting.vertical_active - 1) | (mode_setting.horizontal_active - 1) << 16); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> IntelDisplayTranscoder::disable_pipe(Badge<IntelDisplayConnectorGroup>) | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     m_pipe_registers->pipe_configuration = 0; | ||||
|     m_shadow_registers.pipe_conf = 0; | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "Disabling Pipe"); | ||||
|     size_t milliseconds_elapsed = 0; | ||||
|     while (milliseconds_elapsed < 100) { | ||||
|         u32 value = m_pipe_registers->pipe_configuration; | ||||
|         if (!(value & (1 << 30))) | ||||
|             return {}; | ||||
|         microseconds_delay(1000); | ||||
|         milliseconds_elapsed++; | ||||
|     } | ||||
|     return Error::from_errno(EBUSY); | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> IntelDisplayTranscoder::enable_pipe(Badge<IntelDisplayConnectorGroup>) | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     u32 value = m_pipe_registers->pipe_configuration; | ||||
|     // Note: Just verify these are not already enabled...
 | ||||
|     if ((value & (1 << 30)) && (value & (1 << 31))) | ||||
|         return {}; | ||||
| 
 | ||||
|     // Note: Set the pipe configuration register with these bits:
 | ||||
|     // 1. Bit 31 - to enable the Pipe
 | ||||
|     // 2. Bit 24 - to enable Gamma Unit Mode to 10 bit Gamma mode.
 | ||||
|     // 3. Bits 21-23 are set to zero to indicate Progressive mode (non Interlaced mode)
 | ||||
|     // 4. Bits 18 and 19 are set to zero to indicate Normal operations of assigned
 | ||||
|     //  Cursor and Display planes.
 | ||||
|     m_pipe_registers->pipe_configuration = (1 << 31) | (1 << 24); | ||||
|     m_shadow_registers.pipe_conf = (1 << 31) | (1 << 24); | ||||
|     dbgln_if(INTEL_GRAPHICS_DEBUG, "Enabling Pipe"); | ||||
|     size_t milliseconds_elapsed = 0; | ||||
|     while (milliseconds_elapsed < 100) { | ||||
|         u32 value = m_pipe_registers->pipe_configuration; | ||||
|         if ((value & (1 << 30))) | ||||
|             return {}; | ||||
|         microseconds_delay(1000); | ||||
|         milliseconds_elapsed++; | ||||
|     } | ||||
|     // FIXME: Seems like my video card is buggy and doesn't set the enabled bit (bit 30)!!
 | ||||
|     return {}; | ||||
| } | ||||
| bool IntelDisplayTranscoder::pipe_enabled(Badge<IntelDisplayConnectorGroup>) const | ||||
| { | ||||
|     SpinlockLocker locker(m_access_lock); | ||||
|     u32 value = m_pipe_registers->pipe_configuration; | ||||
|     return (value & (1 << 30)); | ||||
| } | ||||
| } | ||||
							
								
								
									
										118
									
								
								Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								Kernel/Devices/GPU/Intel/Transcoder/DisplayTranscoder.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,118 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/RefPtr.h> | ||||
| #include <AK/Try.h> | ||||
| #include <AK/Types.h> | ||||
| #include <Kernel/Devices/GPU/DisplayConnector.h> | ||||
| #include <Kernel/Devices/GPU/Intel/Definitions.h> | ||||
| #include <Kernel/Locking/Spinlock.h> | ||||
| #include <Kernel/Memory/TypedMapping.h> | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class IntelDisplayConnectorGroup; | ||||
| class IntelDisplayTranscoder { | ||||
| public: | ||||
|     // Note: This is used to "cache" all the registers we wrote to, because
 | ||||
|     // we might not be able to read them directly from hardware later.
 | ||||
|     struct ShadowRegisters { | ||||
|         u32 horizontal_total; | ||||
|         u32 horizontal_blank; | ||||
|         u32 horizontal_sync; | ||||
|         u32 vertical_total; | ||||
|         u32 vertical_blank; | ||||
|         u32 vertical_sync; | ||||
|         u32 exit_line; | ||||
|         u32 pipe_source; | ||||
|         u32 pipe_border_color_pattern; | ||||
|         u32 reserved; | ||||
|         u32 vsync_shift; | ||||
|         u32 pipe_mult; | ||||
|         u32 dpll_reserved_dac_multiplier; | ||||
|         u32 dpll_raw_dac_multiplier; | ||||
|         u32 dpll_divisor_a0; | ||||
|         u32 dpll_divisor_a1; | ||||
|         u32 dpll_p1; | ||||
|         u32 dpll_control; | ||||
|         u32 m1_value; | ||||
|         u32 n1_value; | ||||
|         u32 m2_value; | ||||
|         u32 n2_value; | ||||
|         u32 m1_link; | ||||
|         u32 n1_link; | ||||
|         u32 m2_link; | ||||
|         u32 n2_link; | ||||
|         u32 pipe_conf; | ||||
|     }; | ||||
| 
 | ||||
|     ErrorOr<void> set_mode_setting_timings(Badge<IntelDisplayConnectorGroup>, DisplayConnector::ModeSetting const&); | ||||
|     virtual ErrorOr<void> set_dpll_settings(Badge<IntelDisplayConnectorGroup>, IntelGraphics::PLLSettings const& settings, size_t dac_multiplier) = 0; | ||||
|     virtual ErrorOr<void> enable_dpll_without_vga(Badge<IntelDisplayConnectorGroup>) = 0; | ||||
|     virtual ErrorOr<void> disable_dpll(Badge<IntelDisplayConnectorGroup>) = 0; | ||||
| 
 | ||||
|     ErrorOr<void> disable_pipe(Badge<IntelDisplayConnectorGroup>); | ||||
|     ErrorOr<void> enable_pipe(Badge<IntelDisplayConnectorGroup>); | ||||
|     bool pipe_enabled(Badge<IntelDisplayConnectorGroup>) const; | ||||
| 
 | ||||
|     ShadowRegisters current_registers_state() const; | ||||
| 
 | ||||
|     virtual ~IntelDisplayTranscoder() = default; | ||||
| 
 | ||||
| protected: | ||||
|     struct [[gnu::packed]] TranscoderRegisters { | ||||
|         u32 horizontal_total; | ||||
|         u32 horizontal_blank; | ||||
|         u32 horizontal_sync; | ||||
|         u32 vertical_total; | ||||
|         u32 vertical_blank; | ||||
|         u32 vertical_sync; | ||||
|         u32 exit_line; | ||||
|         u32 pipe_source; | ||||
|         u32 pipe_border_color_pattern; | ||||
|         u32 reserved; | ||||
|         u32 vsync_shift; | ||||
|         u32 pipe_mult; | ||||
|         u32 m1_value; | ||||
|         u32 n1_value; | ||||
|         u32 m2_value; | ||||
|         u32 n2_value; | ||||
|         u32 m1_link; | ||||
|         u32 n1_link; | ||||
|         u32 m2_link; | ||||
|         u32 n2_link; | ||||
|     }; | ||||
| 
 | ||||
|     struct [[gnu::packed]] PipeRegisters { | ||||
|         u32 pipe_display_scan_line; | ||||
|         u32 pipe_display_scan_line_count_range_compare; | ||||
|         u32 pipe_configuration; | ||||
|         u32 reserved; | ||||
|         u32 pipe_gamma_correction_max_red; | ||||
|         u32 pipe_gamma_correction_max_green; | ||||
|         u32 pipe_gamma_correction_max_blue; | ||||
|         u32 reserved2[2]; | ||||
|         u32 pipe_display_status; | ||||
|         u32 reserved3[2]; | ||||
|         u32 display_arbitration_control; | ||||
|         u32 display_fifo_watermark_control1; | ||||
|         u32 display_fifo_watermark_control2; | ||||
|         u32 display_fifo_watermark_control3; | ||||
|         u32 pipe_frame_count_high; | ||||
|         // Note: The specification calls this "Pipe Frame Count Low and Pixel Count"
 | ||||
|         u32 pipe_frame_count_low; | ||||
|     }; | ||||
| 
 | ||||
|     IntelDisplayTranscoder(Memory::TypedMapping<TranscoderRegisters volatile>, Memory::TypedMapping<PipeRegisters volatile>); | ||||
|     mutable Spinlock<LockRank::None> m_access_lock; | ||||
| 
 | ||||
|     ShadowRegisters m_shadow_registers {}; | ||||
|     Memory::TypedMapping<TranscoderRegisters volatile> m_transcoder_registers; | ||||
|     Memory::TypedMapping<PipeRegisters volatile> m_pipe_registers; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										125
									
								
								Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								Kernel/Devices/GPU/Intel/Transcoder/PLL.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,125 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <AK/Format.h> | ||||
| #include <Kernel/Debug.h> | ||||
| #include <Kernel/Devices/GPU/Intel/Transcoder/PLL.h> | ||||
| 
 | ||||
| namespace Kernel::IntelGraphics { | ||||
| 
 | ||||
| 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
 | ||||
| }; | ||||
| 
 | ||||
| PLLMaxSettings const& pll_max_settings_for_generation(Generation generation) | ||||
| { | ||||
|     switch (generation) { | ||||
|     case Generation::Gen4: | ||||
|         return g35limits; | ||||
|     default: | ||||
|         VERIFY_NOT_REACHED(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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<PLLSettings> create_pll_settings(Generation generation, u64 target_frequency, u64 reference_clock) | ||||
| { | ||||
|     PLLSettings settings {}; | ||||
|     PLLSettings best_settings {}; | ||||
|     auto& limits = pll_max_settings_for_generation(generation); | ||||
|     // 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 {}; | ||||
| } | ||||
| 
 | ||||
| bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, 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; | ||||
|     } | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										18
									
								
								Kernel/Devices/GPU/Intel/Transcoder/PLL.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Kernel/Devices/GPU/Intel/Transcoder/PLL.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/Optional.h> | ||||
| #include <Kernel/Devices/GPU/Intel/Definitions.h> | ||||
| 
 | ||||
| namespace Kernel::IntelGraphics { | ||||
| 
 | ||||
| PLLMaxSettings const& pll_max_settings_for_generation(Generation); | ||||
| Optional<PLLSettings> create_pll_settings(Generation, u64 target_frequency, u64 reference_clock); | ||||
| bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits); | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Liav A
						Liav A