Skip to content
Snippets Groups Projects
drm.c 114 KiB
Newer Older
 * Copyright © 2008-2011 Kristian Høgsberg
 * Copyright © 2011 Intel Corporation
 * Copyright © 2017, 2018 Collabora, Ltd.
 * Copyright © 2017, 2018 General Electric Company
 * Copyright (c) 2018 DisplayLink (UK) Ltd.
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
#include "config.h"
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

#include <libudev.h>
#include <libweston/libweston.h>
#include <libweston/backend-drm.h>
#include "drm-internal.h"
#include "shared/helpers.h"
#include "shared/timespec-util.h"
#include "shared/string-helpers.h"
Pekka Paalanen's avatar
Pekka Paalanen committed
#include "shared/weston-drm-fourcc.h"
#include "output-capture.h"
#include "pixel-formats.h"
#include "libbacklight.h"
#include "libinput-seat.h"
#include "launcher-util.h"
#include "presentation-time-server-protocol.h"
#include "linux-dmabuf.h"
#include "linux-dmabuf-unstable-v1-server-protocol.h"
#include "linux-explicit-synchronization.h"
static const char default_seat[] = "seat0";
drm_backend_create_faked_zpos(struct drm_device *device)
	struct drm_backend *b = device->backend;
	struct drm_plane *plane, *tmp;
	struct wl_list tmp_list;
	uint64_t zpos = 0ULL;
	uint64_t zpos_min_primary;
	uint64_t zpos_min_overlay;
	uint64_t zpos_min_cursor;

	/* if the property is there, bail out sooner */
	wl_list_for_each(plane, &device->plane_list, link) {
		if (plane->props[WDRM_PLANE_ZPOS].prop_id != 0)
			return;
	drm_debug(b, "[drm-backend] zpos property not found. "
		     "Using invented immutable zpos values:\n");

	wl_list_init(&tmp_list);
	wl_list_insert_list(&tmp_list, &device->plane_list);
	wl_list_init(&device->plane_list);

	zpos_min_primary = zpos;
	wl_list_for_each_safe(plane, tmp, &tmp_list, link) {
		if (plane->type != WDRM_PLANE_TYPE_PRIMARY)
			continue;

		plane->zpos_min = zpos_min_primary;
		plane->zpos_max = zpos_min_primary;
		wl_list_remove(&plane->link);
		wl_list_insert(&device->plane_list, &plane->link);

		drm_debug(b, "\t[plane] %s plane %d, zpos_min %"PRIu64", "
			      "zpos_max %"PRIu64"\n",
			      drm_output_get_plane_type_name(plane),
			      plane->plane_id, plane->zpos_min, plane->zpos_max);
	wl_list_for_each_safe(plane, tmp, &tmp_list, link) {
		if (plane->type != WDRM_PLANE_TYPE_OVERLAY)
			continue;

		plane->zpos_min = zpos_min_overlay;
		plane->zpos_max = zpos_min_overlay;
		wl_list_remove(&plane->link);
		wl_list_insert(&device->plane_list, &plane->link);

		drm_debug(b, "\t[plane] %s plane %d, zpos_min %"PRIu64", "
			      "zpos_max %"PRIu64"\n",
			      drm_output_get_plane_type_name(plane),
			      plane->plane_id, plane->zpos_min, plane->zpos_max);
	wl_list_for_each_safe(plane, tmp, &tmp_list, link) {
		if (plane->type != WDRM_PLANE_TYPE_CURSOR)
			continue;

		plane->zpos_min = zpos_min_cursor;
		plane->zpos_max = zpos_min_cursor;
		wl_list_remove(&plane->link);
		wl_list_insert(&device->plane_list, &plane->link);
		zpos++;

		drm_debug(b, "\t[plane] %s plane %d, zpos_min %"PRIu64", "
			      "zpos_max %"PRIu64"\n",
			      drm_output_get_plane_type_name(plane),
			      plane->plane_id, plane->zpos_min, plane->zpos_max);
	}

	assert(wl_list_empty(&tmp_list));
static int
pageflip_timeout(void *data) {
	/*
	 * Our timer just went off, that means we're not receiving drm
	 * page flip events anymore for that output. Let's gracefully exit
	 * weston with a return value so devs can debug what's going on.
	 */
	struct drm_output *output = data;
	struct weston_compositor *compositor = output->base.compositor;

	weston_log("Pageflip timeout reached on output %s, your "
	           "driver is probably buggy!  Exiting.\n",
		   output->base.name);
	weston_compositor_exit_with_code(compositor, EXIT_FAILURE);

	return 0;
}

/* Creates the pageflip timer. Note that it isn't armed by default */
static int
drm_output_pageflip_timer_create(struct drm_output *output)
{
	struct wl_event_loop *loop = NULL;
	struct weston_compositor *ec = output->base.compositor;

	loop = wl_display_get_event_loop(ec->wl_display);
	assert(loop);
	output->pageflip_timer = wl_event_loop_add_timer(loop,
	                                                 pageflip_timeout,
	                                                 output);

	if (output->pageflip_timer == NULL) {
		weston_log("creating drm pageflip timer failed: %s\n",
			   strerror(errno));
/**
 * Returns true if the plane can be used on the given output for its current
 * repaint cycle.
 */
drm_plane_is_available(struct drm_plane *plane, struct drm_output *output)
	assert(plane->state_cur);

	if (output->is_virtual)
	/* The plane still has a request not yet completed by the kernel. */
	if (!plane->state_cur->complete)
		return false;

	/* The plane is still active on another output. */
	if (plane->state_cur->output && plane->state_cur->output != output)
		return false;

	/* Check whether the plane can be used with this CRTC; possible_crtcs
	 * is a bitmask of CRTC indices (pipe), rather than CRTC object ID. */
	return !!(plane->possible_crtcs & (1 << output->crtc->pipe));
drm_crtc_find(struct drm_device *device, uint32_t crtc_id)
	wl_list_for_each(crtc, &device->crtc_list, link) {
		if (crtc->crtc_id == crtc_id)
			return crtc;
drm_head_find_by_connector(struct drm_backend *backend, struct drm_device *device, uint32_t connector_id)
{
	struct weston_head *base;
	struct drm_head *head;

	wl_list_for_each(base,
			 &backend->compositor->head_list, compositor_link) {
		head = to_drm_head(base);

		if (head->connector.device != device)
			continue;

		if (head->connector.connector_id != connector_id)
			continue;

		return head;
static struct drm_writeback *
drm_writeback_find_by_connector(struct drm_device *device, uint32_t connector_id)
{
	struct drm_writeback *writeback;

	wl_list_for_each(writeback, &device->writeback_connector_list, link) {
		if (writeback->connector.connector_id == connector_id)
			return writeback;
	}

	return NULL;
}

/**
 * Get output state to disable output
 *
 * Returns a pointer to an output_state object which can be used to disable
 * an output (e.g. DPMS off).
 *
 * @param pending_state The pending state object owning this update
 * @param output The output to disable
 * @returns A drm_output_state to disable the output
 */
static struct drm_output_state *
drm_output_get_disable_state(struct drm_pending_state *pending_state,
			     struct drm_output *output)
{
	struct drm_output_state *output_state;

	output_state = drm_output_state_duplicate(output->state_cur,
						  pending_state,
						  DRM_OUTPUT_STATE_CLEAR_PLANES);
	output_state->dpms = WESTON_DPMS_OFF;

	output_state->protection = WESTON_HDCP_DISABLE;

static int
drm_output_apply_mode(struct drm_output *output);

/**
 * Mark a drm_output_state (the output's last state) as complete. This handles
 * any post-completion actions such as updating the repaint timer, disabling the
 * output, and finally freeing the state.
 */
drm_output_update_complete(struct drm_output *output, uint32_t flags,
			   unsigned int sec, unsigned int usec)
{
	struct drm_device *device = output->device;
	struct drm_plane_state *ps;
	struct timespec ts;

	/* Stop the pageflip timer instead of rearming it here */
	if (output->pageflip_timer)
		wl_event_source_timer_update(output->pageflip_timer, 0);

	wl_list_for_each(ps, &output->state_cur->plane_list, link)
		ps->complete = true;

	drm_output_state_free(output->state_last);
	output->state_last = NULL;

	if (output->destroy_pending) {
		output->destroy_pending = false;
		output->disable_pending = false;
		output->dpms_off_pending = false;
		output->mode_switch_pending = false;
		drm_output_destroy(&output->base);
		return;
	} else if (output->disable_pending) {
		output->disable_pending = false;
		output->dpms_off_pending = false;
		output->mode_switch_pending = false;
		weston_output_disable(&output->base);
		return;
	} else if (output->dpms_off_pending) {
		struct drm_pending_state *pending = drm_pending_state_alloc(device);
		output->dpms_off_pending = false;
		output->mode_switch_pending = false;
		drm_output_get_disable_state(pending, output);
		drm_pending_state_apply_sync(pending);
	} else if (output->mode_switch_pending) {
		output->mode_switch_pending = false;
		drm_output_apply_mode(output);
	}
	if (output->state_cur->dpms == WESTON_DPMS_OFF &&
	    output->base.repaint_status != REPAINT_AWAITING_COMPLETION) {
		/* DPMS can happen to us either in the middle of a repaint
		 * cycle (when we have painted fresh content, only to throw it
		 * away for DPMS off), or at any other random point. If the
		 * latter is true, then we cannot go through finish_frame,
		 * because the repaint machinery does not expect this. */
	if (output->state_cur->tear)
		flags |= WESTON_FINISH_FRAME_TEARING;

	ts.tv_sec = sec;
	ts.tv_nsec = usec * 1000;

	if (output->state_cur->dpms != WESTON_DPMS_OFF)
		weston_output_finish_frame(&output->base, &ts, flags);
	else
		weston_output_finish_frame(&output->base, NULL,
					   WP_PRESENTATION_FEEDBACK_INVALID);

	/* We can't call this from frame_notify, because the output's
	 * repaint needed flag is cleared just after that */
	if (output->recorder)
		weston_output_schedule_repaint(&output->base);
}

static struct drm_fb *
drm_output_render_pixman(struct drm_output_state *state,
			 pixman_region32_t *damage)
	struct drm_output *output = state->output;
	struct weston_compositor *ec = output->base.compositor;

	output->current_image ^= 1;

	ec->renderer->repaint_output(&output->base, damage,
				     output->renderbuffer[output->current_image]);
	return drm_fb_ref(output->dumb[output->current_image]);
drm_output_render(struct drm_output_state *state)
	struct drm_output *output = state->output;
	struct drm_device *device = output->device;
	struct weston_compositor *c = output->base.compositor;
	struct drm_plane_state *scanout_state;
	struct drm_plane *scanout_plane = output->scanout_plane;
	struct drm_property_info *damage_info =
		&scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS];
	struct drm_fb *fb;
	pixman_region32_t damage, scanout_damage;
	pixman_box32_t *rects;
	int n_rects;
	/* If we already have a client buffer promoted to scanout, then we don't
	 * want to render. */
	scanout_state = drm_output_state_get_plane(state, scanout_plane);
	if (scanout_state->fb)
	pixman_region32_init(&damage);

	weston_output_flush_damage_for_primary_plane(&output->base, &damage);

	/*
	 * If we don't have any damage on the primary plane, and we already
	 * have a renderer buffer active, we can reuse it; else we pass
	 * the damaged region into the renderer to re-render the affected
	 * area. But, we still have to call the renderer anyway if any screen
	 * capture is pending, otherwise the capture will not complete.
	if (!pixman_region32_not_empty(&damage) &&
	    wl_list_empty(&output->base.frame_signal.listener_list) &&
	    !weston_output_has_renderer_capture_tasks(&output->base) &&
	    scanout_plane->state_cur->fb &&
	    (scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE ||
	     scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB)) {
		fb = drm_fb_ref(scanout_plane->state_cur->fb);
	} else if (c->renderer->type == WESTON_RENDERER_PIXMAN) {
		fb = drm_output_render_pixman(state, &damage);
		fb = drm_output_render_gl(state, &damage);
	if (!fb) {
		drm_plane_state_put_back(scanout_state);
	}

	scanout_state->fb = fb;
	scanout_state->output = output;

	scanout_state->src_x = 0;
	scanout_state->src_y = 0;
	scanout_state->src_w = fb->width << 16;
	scanout_state->src_h = fb->height << 16;

	scanout_state->dest_x = 0;
	scanout_state->dest_y = 0;
	scanout_state->dest_w = output->base.current_mode->width;
	scanout_state->dest_h = output->base.current_mode->height;
	scanout_state->zpos = scanout_plane->zpos_min;

	/* Don't bother calculating plane damage if the plane doesn't support it */
	if (damage_info->prop_id == 0)

	pixman_region32_init(&scanout_damage);
	weston_region_global_to_output(&scanout_damage,
				       &output->base,
	assert(scanout_state->damage_blob_id == 0);

	rects = pixman_region32_rectangles(&scanout_damage, &n_rects);

	/*
	 * If this function fails, the blob id should still be 0.
	 * This tells the kernel there is no damage information, which means
	 * that it will consider the whole plane damaged. While this may
	 * affect efficiency, it should still produce correct results.
	 */
	drmModeCreatePropertyBlob(device->drm.fd, rects,
				  sizeof(*rects) * n_rects,
				  &scanout_state->damage_blob_id);

	pixman_region32_fini(&scanout_damage);
out:
	pixman_region32_fini(&damage);
drm_connector_get_possible_crtcs_mask(struct drm_connector *connector)
{
	struct drm_device *device = connector->device;
	uint32_t possible_crtcs = 0;
	drmModeConnector *conn = connector->conn;
	drmModeEncoder *encoder;
	int i;

	for (i = 0; i < conn->count_encoders; i++) {
		encoder = drmModeGetEncoder(device->drm.fd,
					    conn->encoders[i]);
		if (!encoder)
			continue;

		possible_crtcs |= encoder->possible_crtcs;
		drmModeFreeEncoder(encoder);
	}

	return possible_crtcs;
}

static struct drm_writeback *
drm_output_find_compatible_writeback(struct drm_output *output)
{
	struct drm_crtc *crtc;
	struct drm_writeback *wb;
	bool in_use;
	uint32_t possible_crtcs;

	wl_list_for_each(wb, &output->device->writeback_connector_list, link) {
		/* Another output may be using the writeback connector. */
		in_use = false;
		wl_list_for_each(crtc, &output->device->crtc_list, link) {
			if (crtc->output && crtc->output->wb_state &&
			    crtc->output->wb_state->wb == wb) {
				in_use = true;
				break;
			}
		}
		if (in_use)
			continue;

		/* Is the writeback connector compatible with the CRTC? */
		possible_crtcs =
			drm_connector_get_possible_crtcs_mask(&wb->connector);
		if (!(possible_crtcs & (1 << output->crtc->pipe)))
			continue;

		/* Does the writeback connector support the output gbm format? */
		if (!weston_drm_format_array_find_format(&wb->formats,
							 output->format->format))
			continue;

		return wb;
	}

	return NULL;
}

static struct drm_writeback_state *
drm_writeback_state_alloc(void)
{
	struct drm_writeback_state *state;

	state = zalloc(sizeof *state);
	if (!state)
		return NULL;

	state->state = DRM_OUTPUT_WB_SCREENSHOT_OFF;
	state->out_fence_fd = -1;
	wl_array_init(&state->referenced_fbs);

	return state;
}

static void
drm_writeback_state_free(struct drm_writeback_state *state)
{
	struct drm_fb **fb;

	if (state->out_fence_fd >= 0)
		close(state->out_fence_fd);

	/* Unref framebuffer that was given to save the content of the writeback */
	if (state->fb)
		drm_fb_unref(state->fb);

	/* Unref framebuffers that were in use in the same commit of the one with
	 * the writeback setup */
	wl_array_for_each(fb, &state->referenced_fbs)
		drm_fb_unref(*fb);
	wl_array_release(&state->referenced_fbs);

	free(state);
}

static void
drm_output_pick_writeback_capture_task(struct drm_output *output)
{
	struct weston_capture_task *ct;
	struct weston_buffer *buffer;
	struct drm_writeback *wb;
	const char *msg;
	int32_t width = output->base.current_mode->width;
	int32_t height = output->base.current_mode->height;
	uint32_t format = output->format->format;

	assert(output->device->atomic_modeset);

	ct = weston_output_pull_capture_task(&output->base,
					     WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK,
					     width, height, pixel_format_get_info(format));
	if (!ct)
		return;

	if (output->base.disable_planes > 0) {
		msg = "drm: KMS planes usage is disabled for now, so " \
		      "writeback capture tasks are rejected";
		goto err;
	}

	wb = drm_output_find_compatible_writeback(output);
	if (!wb) {
		msg = "drm: could not find writeback connector for output";
		goto err;
	}

	buffer = weston_capture_task_get_buffer(ct);
	assert(buffer->width == width);
	assert(buffer->height == height);
	assert(buffer->pixel_format->format == output->format->format);

	output->wb_state = drm_writeback_state_alloc();
	if (!output->wb_state) {
		msg = "drm: failed to allocate memory for writeback state";
		goto err;
	}

	output->wb_state->fb = drm_fb_create_dumb(output->device, width, height, format);
	if (!output->wb_state->fb) {
		msg = "drm: failed to create dumb buffer for writeback state";
		goto err_fb;
	}

	output->wb_state->output = output;
	output->wb_state->wb = wb;
	output->wb_state->state = DRM_OUTPUT_WB_SCREENSHOT_PREPARE_COMMIT;
	output->wb_state->ct = ct;

	return;

err_fb:
	drm_writeback_state_free(output->wb_state);
	output->wb_state = NULL;
err:
	weston_capture_task_retire_failed(ct, msg);
}

#ifdef BUILD_DRM_GBM
/**
 * Update the image for the current cursor surface
 *
 * @param plane_state DRM cursor plane state
 * @param ev Source view for cursor
 */
static void
cursor_bo_update(struct drm_output *output, struct weston_view *ev)
{
	struct drm_device *device = output->device;
	struct gbm_bo *bo = output->gbm_cursor_fb[output->current_cursor]->bo;
	struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
	uint32_t buf[device->cursor_width * device->cursor_height];
	uint8_t *s;
	int i;

	assert(buffer && buffer->shm_buffer);
	assert(buffer->width <= device->cursor_width);
	assert(buffer->height <= device->cursor_height);

	memset(buf, 0, sizeof buf);

	s = wl_shm_buffer_get_data(buffer->shm_buffer);

	wl_shm_buffer_begin_access(buffer->shm_buffer);
	for (i = 0; i < buffer->height; i++)
		memcpy(buf + i * device->cursor_width,
		       s + i * buffer->stride,
		       buffer->width * 4);
	wl_shm_buffer_end_access(buffer->shm_buffer);

	if (bo) {
		if (gbm_bo_write(bo, buf, sizeof buf) < 0)
			weston_log("failed update cursor: %s\n", strerror(errno));
	} else {
		memcpy(output->gbm_cursor_fb[output->current_cursor]->map,
		       buf, sizeof buf);
	}
}
#else
static void
cursor_bo_update(struct drm_output *output, struct weston_view *ev)
{
}
#endif

static void
drm_output_prepare_repaint(struct weston_output *output_base)
{
	struct drm_output *output = to_drm_output(output_base);

	output->device->will_repaint = true;
}

drm_output_repaint(struct weston_output *output_base)
	struct drm_output *output = to_drm_output(output_base);
	struct drm_output_state *state = NULL;
	struct drm_plane_state *scanout_state;
	struct drm_plane_state *cursor_state;
	struct drm_pending_state *pending_state;
	struct drm_device *device;
	assert(!output->is_virtual);
	pending_state = device->repaint_data;
	if (output->disable_pending || output->destroy_pending)
		goto err;

	assert(!output->state_last);

	/* If planes have been disabled in the core, we might not have
	 * hit assign_planes at all, so might not have valid output state
	 * here. */
	state = drm_pending_state_get_output(pending_state, output);
	if (!state)
		state = drm_output_state_duplicate(output->state_cur,
						   pending_state,
						   DRM_OUTPUT_STATE_CLEAR_PLANES);
	state->dpms = WESTON_DPMS_ON;

	cursor_state = drm_output_state_get_existing_plane(state,
							   output->cursor_plane);
	if (cursor_state && cursor_state->fb) {
		pixman_region32_t damage;

		assert(cursor_state->plane == output->cursor_plane);
		assert(cursor_state->fb == output->gbm_cursor_fb[0]);

		pixman_region32_init(&damage);
		weston_output_flush_damage_for_plane(&output->base,
						     &output->cursor_plane->base,
						     &damage);
		if (pixman_region32_not_empty(&damage)) {
			output->current_cursor++;
			output->current_cursor =
				output->current_cursor %
					ARRAY_LENGTH(output->gbm_cursor_fb);
			cursor_bo_update(output, output->cursor_view);
		}
		pixman_region32_fini(&damage);

		cursor_state->fb = drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
		drm_fb_unref(output->gbm_cursor_fb[0]);
	}


	if (output_base->allow_protection)
		state->protection = output_base->desired_protection;
	else
		state->protection = WESTON_HDCP_DISABLE;

	if (drm_output_ensure_hdr_output_metadata_blob(output) < 0)
		goto err;

	if (device->atomic_modeset)
		drm_output_pick_writeback_capture_task(output);
	drm_output_render(state);
	scanout_state = drm_output_state_get_plane(state,
						   output->scanout_plane);
	if (!scanout_state || !scanout_state->fb)
		goto err;

	return 0;
err:
	drm_output_state_free(state);
	return -1;
/* Determine the type of vblank synchronization to use for the output.
 * The pipe parameter indicates which CRTC is in use.  Knowing this, we
 * can determine which vblank sequence type to use for it.  Traditional
 * cards had only two CRTCs, with CRTC 0 using no special flags, and
 * CRTC 1 using DRM_VBLANK_SECONDARY.  The first bit of the pipe
 * parameter indicates this.
 * Bits 1-5 of the pipe parameter are 5 bit wide pipe number between
 * 0-31.  If this is non-zero it indicates we're dealing with a
 * multi-gpu situation and we need to calculate the vblank sync
 * using DRM_BLANK_HIGH_CRTC_MASK.
 */
drm_waitvblank_pipe(struct drm_crtc *crtc)
	if (crtc->pipe > 1)
		return (crtc->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) &
				DRM_VBLANK_HIGH_CRTC_MASK;
		return DRM_VBLANK_SECONDARY;
	else
		return 0;
}

drm_output_start_repaint_loop(struct weston_output *output_base)
	struct drm_output *output = to_drm_output(output_base);
	struct drm_pending_state *pending_state;
	struct drm_plane *scanout_plane = output->scanout_plane;
	struct drm_device *device = output->device;
	struct drm_backend *backend = device->backend;
	struct weston_compositor *compositor = backend->compositor;
	struct timespec ts, tnow;
	struct timespec vbl2now;
	int64_t refresh_nsec;
	uint32_t flags = WP_PRESENTATION_FEEDBACK_INVALID;
	int ret;
	drmVBlank vbl = {
		.request.type = DRM_VBLANK_RELATIVE,
		.request.sequence = 0,
		.request.signal = 0,
	};
	if (output->disable_pending || output->destroy_pending)
	if (!scanout_plane->state_cur->fb) {
		/* We can't page flip if there's no mode set */
		goto finish_frame;
	/* Need to smash all state in from scratch; current timings might not
	 * be what we want, page flip might not work, etc.
	 */
	if (device->state_invalid)
	assert(scanout_plane->state_cur->output == output);
	/* If we're tearing, we've been generating timestamps from the
	 * presentation clock that don't line up with the msc timestamps,
	 * and could be more recent than the latest msc, which would cause
	 * an assert() later.
	 */
	if (output->state_cur->tear) {
		flags |= WESTON_FINISH_FRAME_TEARING;
		goto finish_frame;
	}

	/* Try to get current msc and timestamp via instant query */
	vbl.request.type |= drm_waitvblank_pipe(output->crtc);
	ret = drmWaitVBlank(device->drm.fd, &vbl);
	/* Error ret or zero timestamp means failure to get valid timestamp */
	if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) {
		ts.tv_sec = vbl.reply.tval_sec;
		ts.tv_nsec = vbl.reply.tval_usec * 1000;
		/* Valid timestamp for most recent vblank - not stale?
		 * Stale ts could happen on Linux 3.17+, so make sure it
		 * is not older than 1 refresh duration since now.
		 */
		weston_compositor_read_presentation_clock(compositor,
							  &tnow);
		timespec_sub(&vbl2now, &tnow, &ts);
		refresh_nsec =
			millihz_to_nsec(output->base.current_mode->refresh);
		if (timespec_to_nsec(&vbl2now) < refresh_nsec) {
			drm_output_update_msc(output, vbl.reply.sequence);
			weston_output_finish_frame(output_base, &ts, flags);
	/* Immediate query didn't provide valid timestamp.
	 * Use pageflip fallback.
	 */
	assert(!output->page_flip_pending);
	assert(!output->state_last);
	pending_state = drm_pending_state_alloc(device);
	drm_output_state_duplicate(output->state_cur, pending_state,
				   DRM_OUTPUT_STATE_PRESERVE_PLANES);
	ret = drm_pending_state_apply(pending_state);
	if (ret != 0) {
		weston_log("applying repaint-start state failed: %s\n",
			   strerror(errno));
		if (ret == -EACCES || ret == -EBUSY)
			return ret;

finish_frame:
	/* if we cannot page-flip, immediately finish frame */
	weston_output_finish_frame(output_base, NULL, flags);
static void
drm_repaint_begin_device(struct drm_device *device)
{
	struct drm_backend *b = device->backend;
	struct drm_pending_state *pending_state;

	device->will_repaint = false;
	pending_state = drm_pending_state_alloc(device);
	device->repaint_data = pending_state;

	if (weston_log_scope_is_enabled(b->debug))
		drm_debug(b, "[repaint] Beginning repaint (%s); pending_state %p\n",
			  device->drm.filename, device->repaint_data);
}

/**
 * Begin a new repaint cycle
 *
 * Called by the core compositor at the beginning of a repaint cycle. Creates
 * a new pending_state structure to own any output state created by individual
 * output repaint functions until the repaint is flushed or cancelled.
drm_repaint_begin(struct weston_backend *backend)
	struct drm_backend *b = container_of(backend, struct drm_backend, base);
	if (b->drm->will_repaint)
		drm_repaint_begin_device(b->drm);

	wl_list_for_each(device, &b->kms_list, link) {
		if (device->will_repaint)
			drm_repaint_begin_device(device);
	}
	if (weston_log_scope_is_enabled(b->debug)) {
		char *dbg = weston_compositor_print_scene_graph(b->compositor);
		drm_debug(b, "%s", dbg);
		free(dbg);
	}
drm_repaint_flush_device(struct drm_device *device)
{
	struct drm_backend *b = device->backend;
	struct drm_pending_state *pending_state;
	int ret;

	pending_state = device->repaint_data;
	if (!pending_state)

	ret = drm_pending_state_apply(pending_state);
	if (ret != 0)
		weston_log("repaint-flush failed: %s\n", strerror(errno));

	drm_debug(b, "[repaint] flushed (%s) pending_state %p\n",
		  device->drm.filename, pending_state);
	device->repaint_data = NULL;

	wl_list_for_each(base, &b->compositor->output_list, link) {
		struct drm_output *tmp = to_drm_output(base);
		if (!base->will_repaint || !tmp || tmp->device != device)
			continue;
			weston_output_schedule_repaint_restart(base);
			weston_output_schedule_repaint_reset(base);
}

/**
 * Flush a repaint set
 *
 * Called by the core compositor when a repaint cycle has been completed
 * and should be flushed. Frees the pending state, transitioning ownership
 * of the output state from the pending state, to the update itself. When
 * the update completes (see drm_output_update_complete), the output
 * state will be freed.
drm_repaint_flush(struct weston_backend *backend)
	struct drm_backend *b = container_of(backend, struct drm_backend, base);
	drm_repaint_flush_device(b->drm);
	wl_list_for_each(device, &b->kms_list, link)
		drm_repaint_flush_device(device);
}

static void
drm_repaint_cancel_device(struct drm_device *device)
{