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 */
49void intel_update_watermarks(struct intel_display *display)
50{
51 if (display->funcs.wm->update_wm)
52 display->funcs.wm->update_wm(display);
53}
54
55int 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
66bool 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
79void 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
88void 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
97int 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
107void 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
113void intel_wm_sanitize(struct intel_display *display)
114{
115 if (display->funcs.wm->sanitize)
116 return display->funcs.wm->sanitize(display);
117}
118
119bool 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
142void 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
172void 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
180static 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(dev: 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, fmt: "WM%d %u (%u.%u usec)\n",
203 level, wm[level], latency / 10, latency % 10);
204 }
205
206 drm_modeset_unlock_all(dev: display->drm);
207}
208
209static 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, wm: latencies);
220
221 return 0;
222}
223
224static 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, wm: latencies);
235
236 return 0;
237}
238
239static 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, wm: latencies);
250
251 return 0;
252}
253
254static 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
264static 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
274static 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
284static 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(to: tmp, from: ubuf, n: 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(dev: display->drm);
309
310 for (level = 0; level < display->wm.num_levels; level++)
311 wm[level] = new[level];
312
313 drm_modeset_unlock_all(dev: display->drm);
314
315 return len;
316}
317
318static 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, wm: latencies);
331}
332
333static 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, wm: latencies);
346}
347
348static 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, wm: latencies);
361}
362
363static 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
372static 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
381static 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
390void 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