Serenity Operating System
1/*
2 * Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Format.h>
8#include <Kernel/Debug.h>
9#include <Kernel/Graphics/Intel/Transcoder/PLL.h>
10
11namespace Kernel::IntelGraphics {
12
13static constexpr PLLMaxSettings g35limits {
14 { 20'000'000, 400'000'000 }, // values in Hz, dot_clock
15 { 1'400'000'000, 2'800'000'000 }, // values in Hz, VCO
16 { 3, 8 }, // n
17 { 70, 120 }, // m
18 { 10, 20 }, // m1
19 { 5, 9 }, // m2
20 { 5, 80 }, // p
21 { 1, 8 }, // p1
22 { 5, 10 } // p2
23};
24
25PLLMaxSettings const& pll_max_settings_for_generation(Generation generation)
26{
27 switch (generation) {
28 case Generation::Gen4:
29 return g35limits;
30 default:
31 VERIFY_NOT_REACHED();
32 }
33}
34
35static size_t find_absolute_difference(u64 target_frequency, u64 checked_frequency)
36{
37 if (target_frequency >= checked_frequency)
38 return target_frequency - checked_frequency;
39 return checked_frequency - target_frequency;
40}
41
42Optional<PLLSettings> create_pll_settings(Generation generation, u64 target_frequency, u64 reference_clock)
43{
44 PLLSettings settings {};
45 PLLSettings best_settings {};
46 auto& limits = pll_max_settings_for_generation(generation);
47 // FIXME: Is this correct for all Intel Native graphics cards?
48 settings.p2 = 10;
49 dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for ref clock of {} Hz, for target of {} Hz", reference_clock, target_frequency);
50 u64 best_difference = 0xffffffff;
51 for (settings.n = limits.n.min; settings.n <= limits.n.max; ++settings.n) {
52 for (settings.m1 = limits.m1.max; settings.m1 >= limits.m1.min; --settings.m1) {
53 for (settings.m2 = limits.m2.max; settings.m2 >= limits.m2.min; --settings.m2) {
54 for (settings.p1 = limits.p1.max; settings.p1 >= limits.p1.min; --settings.p1) {
55 dbgln_if(INTEL_GRAPHICS_DEBUG, "Check PLL settings for {} {} {} {} {}", settings.n, settings.m1, settings.m2, settings.p1, settings.p2);
56 if (!check_pll_settings(settings, reference_clock, limits))
57 continue;
58 auto current_dot_clock = settings.compute_dot_clock(reference_clock);
59 if (current_dot_clock == target_frequency)
60 return settings;
61 auto difference = find_absolute_difference(target_frequency, current_dot_clock);
62 if (difference < best_difference && (current_dot_clock > target_frequency)) {
63 best_settings = settings;
64 best_difference = difference;
65 }
66 }
67 }
68 }
69 }
70 if (best_settings.is_valid())
71 return best_settings;
72 return {};
73}
74
75bool check_pll_settings(PLLSettings const& settings, size_t reference_clock, PLLMaxSettings const& limits)
76{
77 if (settings.n < limits.n.min || settings.n > limits.n.max) {
78 dbgln_if(INTEL_GRAPHICS_DEBUG, "N is invalid {}", settings.n);
79 return false;
80 }
81 if (settings.m1 < limits.m1.min || settings.m1 > limits.m1.max) {
82 dbgln_if(INTEL_GRAPHICS_DEBUG, "m1 is invalid {}", settings.m1);
83 return false;
84 }
85 if (settings.m2 < limits.m2.min || settings.m2 > limits.m2.max) {
86 dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {}", settings.m2);
87 return false;
88 }
89 if (settings.p1 < limits.p1.min || settings.p1 > limits.p1.max) {
90 dbgln_if(INTEL_GRAPHICS_DEBUG, "p1 is invalid {}", settings.p1);
91 return false;
92 }
93
94 if (settings.m1 <= settings.m2) {
95 dbgln_if(INTEL_GRAPHICS_DEBUG, "m2 is invalid {} as it is bigger than m1 {}", settings.m2, settings.m1);
96 return false;
97 }
98
99 auto m = settings.compute_m();
100 auto p = settings.compute_p();
101
102 if (m < limits.m.min || m > limits.m.max) {
103 dbgln_if(INTEL_GRAPHICS_DEBUG, "m invalid {}", m);
104 return false;
105 }
106 if (p < limits.p.min || p > limits.p.max) {
107 dbgln_if(INTEL_GRAPHICS_DEBUG, "p invalid {}", p);
108 return false;
109 }
110
111 auto dot = settings.compute_dot_clock(reference_clock);
112 auto vco = settings.compute_vco(reference_clock);
113
114 if (dot < limits.dot_clock.min || dot > limits.dot_clock.max) {
115 dbgln_if(INTEL_GRAPHICS_DEBUG, "Dot clock invalid {}", dot);
116 return false;
117 }
118 if (vco < limits.vco.min || vco > limits.vco.max) {
119 dbgln_if(INTEL_GRAPHICS_DEBUG, "VCO clock invalid {}", vco);
120 return false;
121 }
122 return true;
123}
124
125}