File Index
Files and directories
Sign in
FILE

intel_wm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/i915/display/intel_wm.c C
  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(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
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, 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, 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, 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(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
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, 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, 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, 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}
~/ Read-only file preview.