xref: /linux/drivers/gpu/drm/i915/display/intel_wm.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2023 Intel Corporation
4  */
5 
6 #include <linux/debugfs.h>
7 
8 #include <drm/drm_print.h>
9 
10 #include "i9xx_wm.h"
11 #include "intel_display_core.h"
12 #include "intel_display_types.h"
13 #include "intel_wm.h"
14 #include "skl_watermark.h"
15 
16 /**
17  * intel_update_watermarks - update FIFO watermark values based on current modes
18  * @display: display device
19  *
20  * Calculate watermark values for the various WM regs based on current mode
21  * and plane configuration.
22  *
23  * There are several cases to deal with here:
24  *   - normal (i.e. non-self-refresh)
25  *   - self-refresh (SR) mode
26  *   - lines are large relative to FIFO size (buffer can hold up to 2)
27  *   - lines are small relative to FIFO size (buffer can hold more than 2
28  *     lines), so need to account for TLB latency
29  *
30  *   The normal calculation is:
31  *     watermark = dotclock * bytes per pixel * latency
32  *   where latency is platform & configuration dependent (we assume pessimal
33  *   values here).
34  *
35  *   The SR calculation is:
36  *     watermark = (trunc(latency/line time)+1) * surface width *
37  *       bytes per pixel
38  *   where
39  *     line time = htotal / dotclock
40  *     surface width = hdisplay for normal plane and 64 for cursor
41  *   and latency is assumed to be high, as above.
42  *
43  * The final value programmed to the register should always be rounded up,
44  * and include an extra 2 entries to account for clock crossings.
45  *
46  * We don't use the sprite, so we can ignore that.  And on Crestline we have
47  * to set the non-SR watermarks to 8.
48  */
49 void intel_update_watermarks(struct intel_display *display)
50 {
51 	if (display->funcs.wm->update_wm)
52 		display->funcs.wm->update_wm(display);
53 }
54 
55 int intel_wm_compute(struct intel_atomic_state *state,
56 		     struct intel_crtc *crtc)
57 {
58 	struct intel_display *display = to_intel_display(state);
59 
60 	if (!display->funcs.wm->compute_watermarks)
61 		return 0;
62 
63 	return display->funcs.wm->compute_watermarks(state, crtc);
64 }
65 
66 bool intel_initial_watermarks(struct intel_atomic_state *state,
67 			      struct intel_crtc *crtc)
68 {
69 	struct intel_display *display = to_intel_display(state);
70 
71 	if (display->funcs.wm->initial_watermarks) {
72 		display->funcs.wm->initial_watermarks(state, crtc);
73 		return true;
74 	}
75 
76 	return false;
77 }
78 
79 void intel_atomic_update_watermarks(struct intel_atomic_state *state,
80 				    struct intel_crtc *crtc)
81 {
82 	struct intel_display *display = to_intel_display(state);
83 
84 	if (display->funcs.wm->atomic_update_watermarks)
85 		display->funcs.wm->atomic_update_watermarks(state, crtc);
86 }
87 
88 void intel_optimize_watermarks(struct intel_atomic_state *state,
89 			       struct intel_crtc *crtc)
90 {
91 	struct intel_display *display = to_intel_display(state);
92 
93 	if (display->funcs.wm->optimize_watermarks)
94 		display->funcs.wm->optimize_watermarks(state, crtc);
95 }
96 
97 int intel_compute_global_watermarks(struct intel_atomic_state *state)
98 {
99 	struct intel_display *display = to_intel_display(state);
100 
101 	if (display->funcs.wm->compute_global_watermarks)
102 		return display->funcs.wm->compute_global_watermarks(state);
103 
104 	return 0;
105 }
106 
107 void intel_wm_get_hw_state(struct intel_display *display)
108 {
109 	if (display->funcs.wm->get_hw_state)
110 		return display->funcs.wm->get_hw_state(display);
111 }
112 
113 void intel_wm_sanitize(struct intel_display *display)
114 {
115 	if (display->funcs.wm->sanitize)
116 		return display->funcs.wm->sanitize(display);
117 }
118 
119 bool intel_wm_plane_visible(const struct intel_crtc_state *crtc_state,
120 			    const struct intel_plane_state *plane_state)
121 {
122 	struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
123 
124 	/* FIXME check the 'enable' instead */
125 	if (!crtc_state->hw.active)
126 		return false;
127 
128 	/*
129 	 * Treat cursor with fb as always visible since cursor updates
130 	 * can happen faster than the vrefresh rate, and the current
131 	 * watermark code doesn't handle that correctly. Cursor updates
132 	 * which set/clear the fb or change the cursor size are going
133 	 * to get throttled by intel_legacy_cursor_update() to work
134 	 * around this problem with the watermark code.
135 	 */
136 	if (plane->id == PLANE_CURSOR)
137 		return plane_state->hw.fb != NULL;
138 	else
139 		return plane_state->uapi.visible;
140 }
141 
142 void intel_print_wm_latency(struct intel_display *display,
143 			    const char *name, const u16 wm[])
144 {
145 	int level;
146 
147 	for (level = 0; level < display->wm.num_levels; level++) {
148 		unsigned int latency = wm[level];
149 
150 		if (latency == 0) {
151 			drm_dbg_kms(display->drm,
152 				    "%s WM%d latency not provided\n",
153 				    name, level);
154 			continue;
155 		}
156 
157 		/*
158 		 * - latencies are in us on gen9.
159 		 * - before then, WM1+ latency values are in 0.5us units
160 		 */
161 		if (DISPLAY_VER(display) >= 9)
162 			latency *= 10;
163 		else if (level > 0)
164 			latency *= 5;
165 
166 		drm_dbg_kms(display->drm,
167 			    "%s WM%d latency %u (%u.%u usec)\n", name, level,
168 			    wm[level], latency / 10, latency % 10);
169 	}
170 }
171 
172 void intel_wm_init(struct intel_display *display)
173 {
174 	if (DISPLAY_VER(display) >= 9)
175 		skl_wm_init(display);
176 	else
177 		i9xx_wm_init(display);
178 }
179 
180 static void wm_latency_show(struct seq_file *m, const u16 wm[8])
181 {
182 	struct intel_display *display = m->private;
183 	int level;
184 
185 	drm_modeset_lock_all(display->drm);
186 
187 	for (level = 0; level < display->wm.num_levels; level++) {
188 		unsigned int latency = wm[level];
189 
190 		/*
191 		 * - WM1+ latency values in 0.5us units
192 		 * - latencies are in us on gen9/vlv/chv
193 		 */
194 		if (DISPLAY_VER(display) >= 9 ||
195 		    display->platform.valleyview ||
196 		    display->platform.cherryview ||
197 		    display->platform.g4x)
198 			latency *= 10;
199 		else if (level > 0)
200 			latency *= 5;
201 
202 		seq_printf(m, "WM%d %u (%u.%u usec)\n",
203 			   level, wm[level], latency / 10, latency % 10);
204 	}
205 
206 	drm_modeset_unlock_all(display->drm);
207 }
208 
209 static int pri_wm_latency_show(struct seq_file *m, void *data)
210 {
211 	struct intel_display *display = m->private;
212 	const u16 *latencies;
213 
214 	if (DISPLAY_VER(display) >= 9)
215 		latencies = display->wm.skl_latency;
216 	else
217 		latencies = display->wm.pri_latency;
218 
219 	wm_latency_show(m, latencies);
220 
221 	return 0;
222 }
223 
224 static int spr_wm_latency_show(struct seq_file *m, void *data)
225 {
226 	struct intel_display *display = m->private;
227 	const u16 *latencies;
228 
229 	if (DISPLAY_VER(display) >= 9)
230 		latencies = display->wm.skl_latency;
231 	else
232 		latencies = display->wm.spr_latency;
233 
234 	wm_latency_show(m, latencies);
235 
236 	return 0;
237 }
238 
239 static int cur_wm_latency_show(struct seq_file *m, void *data)
240 {
241 	struct intel_display *display = m->private;
242 	const u16 *latencies;
243 
244 	if (DISPLAY_VER(display) >= 9)
245 		latencies = display->wm.skl_latency;
246 	else
247 		latencies = display->wm.cur_latency;
248 
249 	wm_latency_show(m, latencies);
250 
251 	return 0;
252 }
253 
254 static int pri_wm_latency_open(struct inode *inode, struct file *file)
255 {
256 	struct intel_display *display = inode->i_private;
257 
258 	if (DISPLAY_VER(display) < 5 && !display->platform.g4x)
259 		return -ENODEV;
260 
261 	return single_open(file, pri_wm_latency_show, display);
262 }
263 
264 static int spr_wm_latency_open(struct inode *inode, struct file *file)
265 {
266 	struct intel_display *display = inode->i_private;
267 
268 	if (HAS_GMCH(display))
269 		return -ENODEV;
270 
271 	return single_open(file, spr_wm_latency_show, display);
272 }
273 
274 static int cur_wm_latency_open(struct inode *inode, struct file *file)
275 {
276 	struct intel_display *display = inode->i_private;
277 
278 	if (HAS_GMCH(display))
279 		return -ENODEV;
280 
281 	return single_open(file, cur_wm_latency_show, display);
282 }
283 
284 static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
285 				size_t len, loff_t *offp, u16 wm[8])
286 {
287 	struct seq_file *m = file->private_data;
288 	struct intel_display *display = m->private;
289 	u16 new[8] = {};
290 	int level;
291 	int ret;
292 	char tmp[32];
293 
294 	if (len >= sizeof(tmp))
295 		return -EINVAL;
296 
297 	if (copy_from_user(tmp, ubuf, len))
298 		return -EFAULT;
299 
300 	tmp[len] = '\0';
301 
302 	ret = sscanf(tmp, "%hu %hu %hu %hu %hu %hu %hu %hu",
303 		     &new[0], &new[1], &new[2], &new[3],
304 		     &new[4], &new[5], &new[6], &new[7]);
305 	if (ret != display->wm.num_levels)
306 		return -EINVAL;
307 
308 	drm_modeset_lock_all(display->drm);
309 
310 	for (level = 0; level < display->wm.num_levels; level++)
311 		wm[level] = new[level];
312 
313 	drm_modeset_unlock_all(display->drm);
314 
315 	return len;
316 }
317 
318 static ssize_t pri_wm_latency_write(struct file *file, const char __user *ubuf,
319 				    size_t len, loff_t *offp)
320 {
321 	struct seq_file *m = file->private_data;
322 	struct intel_display *display = m->private;
323 	u16 *latencies;
324 
325 	if (DISPLAY_VER(display) >= 9)
326 		latencies = display->wm.skl_latency;
327 	else
328 		latencies = display->wm.pri_latency;
329 
330 	return wm_latency_write(file, ubuf, len, offp, latencies);
331 }
332 
333 static ssize_t spr_wm_latency_write(struct file *file, const char __user *ubuf,
334 				    size_t len, loff_t *offp)
335 {
336 	struct seq_file *m = file->private_data;
337 	struct intel_display *display = m->private;
338 	u16 *latencies;
339 
340 	if (DISPLAY_VER(display) >= 9)
341 		latencies = display->wm.skl_latency;
342 	else
343 		latencies = display->wm.spr_latency;
344 
345 	return wm_latency_write(file, ubuf, len, offp, latencies);
346 }
347 
348 static ssize_t cur_wm_latency_write(struct file *file, const char __user *ubuf,
349 				    size_t len, loff_t *offp)
350 {
351 	struct seq_file *m = file->private_data;
352 	struct intel_display *display = m->private;
353 	u16 *latencies;
354 
355 	if (DISPLAY_VER(display) >= 9)
356 		latencies = display->wm.skl_latency;
357 	else
358 		latencies = display->wm.cur_latency;
359 
360 	return wm_latency_write(file, ubuf, len, offp, latencies);
361 }
362 
363 static const struct file_operations i915_pri_wm_latency_fops = {
364 	.owner = THIS_MODULE,
365 	.open = pri_wm_latency_open,
366 	.read = seq_read,
367 	.llseek = seq_lseek,
368 	.release = single_release,
369 	.write = pri_wm_latency_write
370 };
371 
372 static const struct file_operations i915_spr_wm_latency_fops = {
373 	.owner = THIS_MODULE,
374 	.open = spr_wm_latency_open,
375 	.read = seq_read,
376 	.llseek = seq_lseek,
377 	.release = single_release,
378 	.write = spr_wm_latency_write
379 };
380 
381 static const struct file_operations i915_cur_wm_latency_fops = {
382 	.owner = THIS_MODULE,
383 	.open = cur_wm_latency_open,
384 	.read = seq_read,
385 	.llseek = seq_lseek,
386 	.release = single_release,
387 	.write = cur_wm_latency_write
388 };
389 
390 void intel_wm_debugfs_register(struct intel_display *display)
391 {
392 	struct dentry *debugfs_root = display->drm->debugfs_root;
393 
394 	debugfs_create_file("i915_pri_wm_latency", 0644, debugfs_root,
395 			    display, &i915_pri_wm_latency_fops);
396 
397 	debugfs_create_file("i915_spr_wm_latency", 0644, debugfs_root,
398 			    display, &i915_spr_wm_latency_fops);
399 
400 	debugfs_create_file("i915_cur_wm_latency", 0644, debugfs_root,
401 			    display, &i915_cur_wm_latency_fops);
402 
403 	skl_watermark_debugfs_register(display);
404 }
405