1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright (C) 2025 Intel Corporation
4 */
5
6 #include <linux/string_choices.h>
7 #include <linux/types.h>
8
9 #include <drm/drm_device.h>
10 #include <drm/drm_print.h>
11
12 #include "i915_drv.h"
13 #include "i915_reg.h"
14 #include "intel_crtc.h"
15 #include "intel_cmtg.h"
16 #include "intel_cmtg_regs.h"
17 #include "intel_de.h"
18 #include "intel_display_device.h"
19 #include "intel_display_power.h"
20
21 /**
22 * DOC: Common Primary Timing Generator (CMTG)
23 *
24 * The CMTG is a timing generator that runs in parallel to transcoders timing
25 * generators (TG) to provide a synchronization mechanism where CMTG acts as
26 * primary and transcoders TGs act as secondary to the CMTG. The CMTG outputs
27 * its TG start and frame sync signals to the transcoders that are configured
28 * as secondary, which use those signals to synchronize their own timing with
29 * the CMTG's.
30 *
31 * The CMTG can be used only with eDP or MIPI command mode and supports the
32 * following use cases:
33 *
34 * - Dual eDP: The CMTG can be used to keep two eDP TGs in sync when on a
35 * dual eDP configuration (with or without PSR/PSR2 enabled).
36 *
37 * - Single eDP as secondary: It is also possible to use a single eDP
38 * configuration with the transcoder TG as secondary to the CMTG. That would
39 * allow a flow that would not require a modeset on the existing eDP when a
40 * new eDP is added for a dual eDP configuration with CMTG.
41 *
42 * - DC6v: In DC6v, the transcoder might be off but the CMTG keeps running to
43 * maintain frame timings. When exiting DC6v, the transcoder TG then is
44 * synced back the CMTG.
45 *
46 * Currently, the driver does not use the CMTG, but we need to make sure that
47 * we disable it in case we inherit a display configuration with it enabled.
48 */
49
50 /*
51 * We describe here only the minimum data required to allow us to properly
52 * disable the CMTG if necessary.
53 */
54 struct intel_cmtg_config {
55 bool cmtg_a_enable;
56 /*
57 * Xe2_LPD adds a second CMTG that can be used for dual eDP async mode.
58 */
59 bool cmtg_b_enable;
60 bool trans_a_secondary;
61 bool trans_b_secondary;
62 };
63
intel_cmtg_has_cmtg_b(struct intel_display * display)64 static bool intel_cmtg_has_cmtg_b(struct intel_display *display)
65 {
66 return DISPLAY_VER(display) >= 20;
67 }
68
intel_cmtg_has_clock_sel(struct intel_display * display)69 static bool intel_cmtg_has_clock_sel(struct intel_display *display)
70 {
71 return DISPLAY_VER(display) >= 14;
72 }
73
intel_cmtg_dump_config(struct intel_display * display,struct intel_cmtg_config * cmtg_config)74 static void intel_cmtg_dump_config(struct intel_display *display,
75 struct intel_cmtg_config *cmtg_config)
76 {
77 drm_dbg_kms(display->drm,
78 "CMTG readout: CMTG A: %s, CMTG B: %s, Transcoder A secondary: %s, Transcoder B secondary: %s\n",
79 str_enabled_disabled(cmtg_config->cmtg_a_enable),
80 intel_cmtg_has_cmtg_b(display) ? str_enabled_disabled(cmtg_config->cmtg_b_enable) : "n/a",
81 str_yes_no(cmtg_config->trans_a_secondary),
82 str_yes_no(cmtg_config->trans_b_secondary));
83 }
84
intel_cmtg_transcoder_is_secondary(struct intel_display * display,enum transcoder trans)85 static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display,
86 enum transcoder trans)
87 {
88 enum intel_display_power_domain power_domain;
89 intel_wakeref_t wakeref;
90 u32 val = 0;
91
92 if (!HAS_TRANSCODER(display, trans))
93 return false;
94
95 power_domain = POWER_DOMAIN_TRANSCODER(trans);
96
97 with_intel_display_power_if_enabled(display, power_domain, wakeref)
98 val = intel_de_read(display, TRANS_DDI_FUNC_CTL2(display, trans));
99
100 return val & CMTG_SECONDARY_MODE;
101 }
102
intel_cmtg_get_config(struct intel_display * display,struct intel_cmtg_config * cmtg_config)103 static void intel_cmtg_get_config(struct intel_display *display,
104 struct intel_cmtg_config *cmtg_config)
105 {
106 u32 val;
107
108 val = intel_de_read(display, TRANS_CMTG_CTL_A);
109 cmtg_config->cmtg_a_enable = val & CMTG_ENABLE;
110
111 if (intel_cmtg_has_cmtg_b(display)) {
112 val = intel_de_read(display, TRANS_CMTG_CTL_B);
113 cmtg_config->cmtg_b_enable = val & CMTG_ENABLE;
114 }
115
116 cmtg_config->trans_a_secondary = intel_cmtg_transcoder_is_secondary(display, TRANSCODER_A);
117 cmtg_config->trans_b_secondary = intel_cmtg_transcoder_is_secondary(display, TRANSCODER_B);
118 }
119
intel_cmtg_disable_requires_modeset(struct intel_display * display,struct intel_cmtg_config * cmtg_config)120 static bool intel_cmtg_disable_requires_modeset(struct intel_display *display,
121 struct intel_cmtg_config *cmtg_config)
122 {
123 if (DISPLAY_VER(display) >= 20)
124 return false;
125
126 return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary;
127 }
128
intel_cmtg_disable(struct intel_display * display,struct intel_cmtg_config * cmtg_config)129 static void intel_cmtg_disable(struct intel_display *display,
130 struct intel_cmtg_config *cmtg_config)
131 {
132 u32 clk_sel_clr = 0;
133 u32 clk_sel_set = 0;
134
135 if (cmtg_config->trans_a_secondary)
136 intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, TRANSCODER_A),
137 CMTG_SECONDARY_MODE, 0);
138
139 if (cmtg_config->trans_b_secondary)
140 intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, TRANSCODER_B),
141 CMTG_SECONDARY_MODE, 0);
142
143 if (cmtg_config->cmtg_a_enable) {
144 drm_dbg_kms(display->drm, "Disabling CMTG A\n");
145 intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0);
146 clk_sel_clr |= CMTG_CLK_SEL_A_MASK;
147 clk_sel_set |= CMTG_CLK_SEL_A_DISABLED;
148 }
149
150 if (cmtg_config->cmtg_b_enable) {
151 drm_dbg_kms(display->drm, "Disabling CMTG B\n");
152 intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0);
153 clk_sel_clr |= CMTG_CLK_SEL_B_MASK;
154 clk_sel_set |= CMTG_CLK_SEL_B_DISABLED;
155 }
156
157 if (intel_cmtg_has_clock_sel(display) && clk_sel_clr)
158 intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set);
159 }
160
161 /*
162 * Read out CMTG configuration and, on platforms that allow disabling it without
163 * a modeset, do it.
164 *
165 * This function must be called before any port PLL is disabled in the general
166 * sanitization process, because we need whatever port PLL that is providing the
167 * clock for CMTG to be on before accessing CMTG registers.
168 */
intel_cmtg_sanitize(struct intel_display * display)169 void intel_cmtg_sanitize(struct intel_display *display)
170 {
171 struct intel_cmtg_config cmtg_config = {};
172
173 if (!HAS_CMTG(display))
174 return;
175
176 intel_cmtg_get_config(display, &cmtg_config);
177 intel_cmtg_dump_config(display, &cmtg_config);
178
179 /*
180 * FIXME: The driver is not prepared to handle cases where a modeset is
181 * required for disabling the CMTG: we need a proper way of tracking
182 * CMTG state and do the right syncronization with respect to triggering
183 * the modeset as part of the disable sequence.
184 */
185 if (intel_cmtg_disable_requires_modeset(display, &cmtg_config))
186 return;
187
188 intel_cmtg_disable(display, &cmtg_config);
189 }
190