• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "igt.h"
24 #include "sw_sync.h"
25 #include <fcntl.h>
26 #include <signal.h>
27 
28 #define NSECS_PER_SEC (1000000000ull)
29 
30 /*
31  * Each test measurement step runs for ~5 seconds.
32  * This gives a decent sample size + enough time for any adaptation to occur if necessary.
33  */
34 #define TEST_DURATION_NS (5000000000ull)
35 
36 enum {
37 	TEST_NONE = 0,
38 	TEST_DPMS = 1 << 0,
39 	TEST_SUSPEND = 1 << 1,
40 };
41 
42 typedef struct range {
43 	unsigned int min;
44 	unsigned int max;
45 } range_t;
46 
47 typedef struct data {
48 	igt_display_t display;
49 	int drm_fd;
50 	igt_fb_t fb0;
51 	igt_fb_t fb1;
52 } data_t;
53 
54 typedef void (*test_t)(data_t*, enum pipe, igt_output_t*, uint32_t);
55 
56 /* Converts a timespec structure to nanoseconds. */
timespec_to_ns(struct timespec * ts)57 static uint64_t timespec_to_ns(struct timespec *ts)
58 {
59 	return ts->tv_sec * NSECS_PER_SEC + ts->tv_nsec;
60 }
61 
62 /*
63  * Gets a vblank event from DRM and returns its timestamp in nanoseconds.
64  * This blocks until the event is received.
65  */
get_vblank_event_ns(data_t * data)66 static uint64_t get_vblank_event_ns(data_t *data)
67 {
68 	struct drm_event_vblank ev;
69 
70 	igt_set_timeout(1, "Waiting for vblank event\n");
71 	igt_assert_eq(read(data->drm_fd, &ev, sizeof(ev)), sizeof(ev));
72 	igt_reset_timeout();
73 
74 	return ev.tv_sec * NSECS_PER_SEC + ev.tv_usec * 1000ull;
75 }
76 
77 /*
78  * Returns the current CLOCK_MONOTONIC time in nanoseconds.
79  * The regular IGT helpers can't be used since they default to
80  * CLOCK_MONOTONIC_RAW - which isn't what the kernel uses for its timestamps.
81  */
get_time_ns(void)82 static uint64_t get_time_ns(void)
83 {
84 	struct timespec ts;
85 	memset(&ts, 0, sizeof(ts));
86 	errno = 0;
87 
88 	if (!clock_gettime(CLOCK_MONOTONIC, &ts))
89 		return timespec_to_ns(&ts);
90 
91 	igt_warn("Could not read monotonic time: %s\n", strerror(errno));
92 	igt_fail(-errno);
93 
94 	return 0;
95 }
96 
97 /* Returns the rate duration in nanoseconds for the given refresh rate. */
rate_from_refresh(uint64_t refresh)98 static uint64_t rate_from_refresh(uint64_t refresh)
99 {
100 	return NSECS_PER_SEC / refresh;
101 }
102 
103 /* Returns the min and max vrr range from the connector debugfs. */
get_vrr_range(data_t * data,igt_output_t * output)104 static range_t get_vrr_range(data_t *data, igt_output_t *output)
105 {
106 	char buf[256];
107 	char *start_loc;
108 	int fd, res;
109 	range_t range;
110 
111 	fd = igt_debugfs_connector_dir(data->drm_fd, output->name, O_RDONLY);
112 	igt_assert(fd >= 0);
113 
114 	res = igt_debugfs_simple_read(fd, "vrr_range", buf, sizeof(buf));
115 	igt_require(res > 0);
116 
117 	close(fd);
118 
119 	igt_assert(start_loc = strstr(buf, "Min: "));
120 	igt_assert_eq(sscanf(start_loc, "Min: %u", &range.min), 1);
121 
122 	igt_assert(start_loc = strstr(buf, "Max: "));
123 	igt_assert_eq(sscanf(start_loc, "Max: %u", &range.max), 1);
124 
125 	return range;
126 }
127 
128 /* Returns a suitable vrr test frequency. */
get_test_rate_ns(data_t * data,igt_output_t * output)129 static uint32_t get_test_rate_ns(data_t *data, igt_output_t *output)
130 {
131 	drmModeModeInfo *mode = igt_output_get_mode(output);
132 	range_t range;
133 	uint32_t vtest;
134 
135 	/*
136 	 * The frequency with the fastest convergence speed should be
137 	 * the midpoint between the current mode vfreq and the min
138 	 * supported vfreq.
139 	 */
140 	range = get_vrr_range(data, output);
141 	igt_require(mode->vrefresh > range.min);
142 
143 	vtest = (mode->vrefresh - range.min) / 2 + range.min;
144 	igt_require(vtest < mode->vrefresh);
145 
146 	return rate_from_refresh(vtest);
147 }
148 
149 /* Returns true if an output supports VRR. */
has_vrr(igt_output_t * output)150 static bool has_vrr(igt_output_t *output)
151 {
152 	return igt_output_has_prop(output, IGT_CONNECTOR_VRR_CAPABLE) &&
153 	       igt_output_get_prop(output, IGT_CONNECTOR_VRR_CAPABLE);
154 }
155 
156 /* Toggles variable refresh rate on the pipe. */
set_vrr_on_pipe(data_t * data,enum pipe pipe,bool enabled)157 static void set_vrr_on_pipe(data_t *data, enum pipe pipe, bool enabled)
158 {
159 	igt_pipe_set_prop_value(&data->display, pipe, IGT_CRTC_VRR_ENABLED,
160 				enabled);
161 	igt_display_commit_atomic(&data->display, 0, NULL);
162 }
163 
164 /* Prepare the display for testing on the given pipe. */
prepare_test(data_t * data,igt_output_t * output,enum pipe pipe)165 static void prepare_test(data_t *data, igt_output_t *output, enum pipe pipe)
166 {
167 	drmModeModeInfo mode = *igt_output_get_mode(output);
168 	igt_plane_t *primary;
169 	cairo_t *cr;
170 
171 	/* Reset output */
172 	igt_display_reset(&data->display);
173 	igt_output_set_pipe(output, pipe);
174 
175 	/* Prepare resources */
176 	igt_create_color_fb(data->drm_fd, mode.hdisplay, mode.vdisplay,
177 			    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
178 			    0.50, 0.50, 0.50, &data->fb0);
179 
180 	igt_create_color_fb(data->drm_fd, mode.hdisplay, mode.vdisplay,
181 			    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
182 			    0.50, 0.50, 0.50, &data->fb1);
183 
184 	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb0);
185 
186 	igt_paint_color(cr, 0, 0, mode.hdisplay / 10, mode.vdisplay / 10,
187 			1.00, 0.00, 0.00);
188 
189 	igt_put_cairo_ctx(data->drm_fd, &data->fb0, cr);
190 
191 	/* Take care of any required modesetting before the test begins. */
192 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
193 	igt_plane_set_fb(primary, &data->fb0);
194 
195 	igt_display_commit_atomic(&data->display,
196 				  DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
197 }
198 
199 /* Waits for the vblank interval. Returns the vblank timestamp in ns. */
200 static uint64_t
wait_for_vblank(data_t * data,enum pipe pipe)201 wait_for_vblank(data_t *data, enum pipe pipe)
202 {
203 	drmVBlank vbl = {};
204 
205 	vbl.request.type = kmstest_get_vbl_flag(pipe);
206 	vbl.request.type |= DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
207 	vbl.request.sequence = 1;
208 	drmWaitVBlank(data->drm_fd, &vbl);
209 
210 	return get_vblank_event_ns(data);
211 }
212 
213 /* Performs an asynchronous non-blocking page-flip on a pipe. */
214 static int
do_flip(data_t * data,enum pipe pipe_id,igt_fb_t * fb)215 do_flip(data_t *data, enum pipe pipe_id, igt_fb_t *fb)
216 {
217 	igt_pipe_t *pipe = &data->display.pipes[pipe_id];
218 	int ret;
219 
220 	igt_set_timeout(1, "Scheduling page flip\n");
221 
222 	/*
223 	 * Only the legacy flip ioctl supports async flips.
224 	 * It's also non-blocking, but returns -EBUSY if flipping too fast.
225 	 * 2x monitor tests will need async flips in the atomic API.
226 	 */
227 	do {
228 		ret = drmModePageFlip(data->drm_fd, pipe->crtc_id,
229 				      fb->fb_id,
230 				      DRM_MODE_PAGE_FLIP_EVENT |
231 				      DRM_MODE_PAGE_FLIP_ASYNC,
232 				      data);
233 	} while (ret == -EBUSY);
234 
235 	igt_assert_eq(ret, 0);
236 	igt_reset_timeout();
237 
238 	return 0;
239 }
240 
241 /*
242  * Flips at the given rate and measures against the expected value.
243  * Returns the pass rate as a percentage from 0 - 100.
244  *
245  * The VRR API is quite flexible in terms of definition - the driver
246  * can arbitrarily restrict the bounds further than the absolute
247  * min and max range. But VRR is really about extending the flip
248  * to prevent stuttering or to match a source content rate.
249  *
250  * The only way to "present" at a fixed rate like userspace in a vendor
251  * neutral manner is to do it with async flips. This avoids the need
252  * to wait for next vblank and it should eventually converge at the
253  * desired rate.
254  */
255 static uint32_t
flip_and_measure(data_t * data,igt_output_t * output,enum pipe pipe,uint64_t rate_ns,uint64_t duration_ns)256 flip_and_measure(data_t *data, igt_output_t *output, enum pipe pipe,
257 		 uint64_t rate_ns, uint64_t duration_ns)
258 {
259 	uint64_t start_ns, last_vblank_ns;
260 	uint32_t total_flip = 0, total_pass = 0;
261 	bool front = false;
262 
263 	/* Align with the vblank region to speed up convergence. */
264 	last_vblank_ns = wait_for_vblank(data, pipe);
265 	start_ns = get_time_ns();
266 
267 	for (;;) {
268 		uint64_t now_ns, vblank_ns, wait_ns, target_ns;
269 		int64_t diff_ns;
270 
271 		front = !front;
272 		do_flip(data, pipe, front ? &data->fb1 : &data->fb0);
273 
274 		vblank_ns = get_vblank_event_ns(data);
275 		diff_ns = rate_ns - (vblank_ns - last_vblank_ns);
276 		last_vblank_ns = vblank_ns;
277 
278 		total_flip += 1;
279 
280 		/*
281 		 * Check if the difference between the two flip timestamps
282 		 * was within the required threshold from the expected rate.
283 		 *
284 		 * A ~50us threshold is arbitrary, but it's roughly the
285 		 * difference between 144Hz and 143Hz which should give this
286 		 * enough accuracy for most use cases.
287 		 */
288 		if (llabs(diff_ns) < 50000ll)
289 			total_pass += 1;
290 
291 		now_ns = get_time_ns();
292 		if (now_ns - start_ns > duration_ns)
293 			break;
294 
295 		/*
296 		 * Burn CPU until next timestamp, sleeping isn't accurate enough.
297 		 * It's worth noting that the target timestamp is based on absolute
298 		 * timestamp rather than a delta to avoid accumulation errors.
299 		 */
300 		diff_ns = now_ns - start_ns;
301 		wait_ns = ((diff_ns + rate_ns - 1) / rate_ns) * rate_ns;
302 		target_ns = start_ns + wait_ns - 10;
303 
304 		while (get_time_ns() < target_ns);
305 	}
306 
307 	igt_info("Completed %u flips, %u were in threshold for %luns.\n",
308 		 total_flip, total_pass, rate_ns);
309 
310 	return total_flip ? ((total_pass * 100) / total_flip) : 0;
311 }
312 
313 /* Basic VRR flip functionality test - enable, measure, disable, measure */
314 static void
test_basic(data_t * data,enum pipe pipe,igt_output_t * output,uint32_t flags)315 test_basic(data_t *data, enum pipe pipe, igt_output_t *output, uint32_t flags)
316 {
317 	uint64_t rate;
318 	uint32_t result;
319 
320 	rate = get_test_rate_ns(data, output);
321 
322 	prepare_test(data, output, pipe);
323 
324 	set_vrr_on_pipe(data, pipe, 1);
325 
326 	/*
327 	 * Do a short run with VRR, but don't check the result.
328 	 * This is to make sure we were actually in the middle of
329 	 * active flipping before doing the DPMS/suspend steps.
330 	 */
331 	flip_and_measure(data, output, pipe, rate, 250000000ull);
332 
333 	if (flags & TEST_DPMS) {
334 		kmstest_set_connector_dpms(output->display->drm_fd,
335 					   output->config.connector,
336 					   DRM_MODE_DPMS_OFF);
337 		kmstest_set_connector_dpms(output->display->drm_fd,
338 					   output->config.connector,
339 					   DRM_MODE_DPMS_ON);
340 	}
341 
342 	if (flags & TEST_SUSPEND)
343 		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
344 					      SUSPEND_TEST_NONE);
345 
346 	result = flip_and_measure(data, output, pipe, rate, TEST_DURATION_NS);
347 
348 	set_vrr_on_pipe(data, pipe, 0);
349 
350 	/* This check is delayed until after VRR is disabled so it isn't
351 	 * left enabled if the test fails. */
352 	igt_assert_f(result > 75,
353 		     "Target VRR on threshold not reached, result was %u%%\n",
354 		     result);
355 
356 	result = flip_and_measure(data, output, pipe, rate, TEST_DURATION_NS);
357 
358 	igt_assert_f(result < 10,
359 		     "Target VRR off threshold exceeded, result was %u%%\n",
360 		     result);
361 
362 	igt_remove_fb(data->drm_fd, &data->fb1);
363 	igt_remove_fb(data->drm_fd, &data->fb0);
364 }
365 
366 /* Runs tests on outputs that are VRR capable. */
367 static void
run_vrr_test(data_t * data,test_t test,uint32_t flags)368 run_vrr_test(data_t *data, test_t test, uint32_t flags)
369 {
370 	igt_output_t *output;
371 	bool found = false;
372 
373 	for_each_connected_output(&data->display, output) {
374 		enum pipe pipe;
375 
376 		if (!has_vrr(output))
377 			continue;
378 
379 		for_each_pipe(&data->display, pipe)
380 			if (igt_pipe_connector_valid(pipe, output)) {
381 				test(data, pipe, output, flags);
382 				found = true;
383 				break;
384 			}
385 	}
386 
387 	if (!found)
388 		igt_skip("No vrr capable outputs found.\n");
389 }
390 
391 igt_main
392 {
393 	data_t data = { 0 };
394 
395 	igt_skip_on_simulation();
396 
397 	igt_fixture {
398 		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
399 
400 		kmstest_set_vt_graphics_mode();
401 
402 		igt_display_require(&data.display, data.drm_fd);
403 		igt_require(data.display.is_atomic);
404 		igt_display_require_output(&data.display);
405 	}
406 
407 	igt_subtest("flip-basic")
408 		run_vrr_test(&data, test_basic, 0);
409 
410 	igt_subtest("flip-dpms")
411 		run_vrr_test(&data, test_basic, TEST_DPMS);
412 
413 	igt_subtest("flip-suspend")
414 		run_vrr_test(&data, test_basic, TEST_SUSPEND);
415 
416 	igt_fixture {
417 		igt_display_fini(&data.display);
418 	}
419 }
420