Linux Audio

Check our new training course

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