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