#include "linux-dmabuf-unstable-v1-server-protocol.h"
#include "linux-explicit-synchronization.h"
-static struct drm_property_enum_info plane_type_enums[] = {
- [WDRM_PLANE_TYPE_PRIMARY] = {
- .name = "Primary",
- },
- [WDRM_PLANE_TYPE_OVERLAY] = {
- .name = "Overlay",
- },
- [WDRM_PLANE_TYPE_CURSOR] = {
- .name = "Cursor",
- },
-};
-
-static const struct drm_property_info plane_props[] = {
- [WDRM_PLANE_TYPE] = {
- .name = "type",
- .enum_values = plane_type_enums,
- .num_enum_values = WDRM_PLANE_TYPE__COUNT,
- },
- [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
- [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
- [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
- [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
- [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
- [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
- [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
- [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
- [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
- [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
- [WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" },
- [WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" },
- [WDRM_PLANE_FB_DAMAGE_CLIPS] = { .name = "FB_DAMAGE_CLIPS" },
-};
-
-static struct drm_property_enum_info dpms_state_enums[] = {
- [WDRM_DPMS_STATE_OFF] = {
- .name = "Off",
- },
- [WDRM_DPMS_STATE_ON] = {
- .name = "On",
- },
- [WDRM_DPMS_STATE_STANDBY] = {
- .name = "Standby",
- },
- [WDRM_DPMS_STATE_SUSPEND] = {
- .name = "Suspend",
- },
-};
-
-static const struct drm_property_info connector_props[] = {
- [WDRM_CONNECTOR_EDID] = { .name = "EDID" },
- [WDRM_CONNECTOR_DPMS] = {
- .name = "DPMS",
- .enum_values = dpms_state_enums,
- .num_enum_values = WDRM_DPMS_STATE__COUNT,
- },
- [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
- [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", },
-};
-
-static const struct drm_property_info crtc_props[] = {
- [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", },
- [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", },
-};
-
/**
* Mode for drm_output_state_duplicate.
*/
DRM_OUTPUT_STATE_PRESERVE_PLANES, /**< preserve plane state */
};
-/**
- * Mode for drm_pending_state_apply and co.
- */
-enum drm_state_apply_mode {
- DRM_STATE_APPLY_SYNC, /**< state fully processed */
- DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */
- DRM_STATE_TEST_ONLY, /**< test if the state can be applied */
-};
-
enum drm_output_propose_state_mode {
DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY, /**< only assign to renderer & cursor */
return 0;
}
-/**
- * Get the current value of a KMS property
- *
- * Given a drmModeObjectGetProperties return, as well as the drm_property_info
- * for the target property, return the current value of that property,
- * with an optional default. If the property is a KMS enum type, the return
- * value will be translated into the appropriate internal enum.
- *
- * If the property is not present, the default value will be returned.
- *
- * @param info Internal structure for property to look up
- * @param props Raw KMS properties for the target object
- * @param def Value to return if property is not found
- */
-uint64_t
-drm_property_get_value(struct drm_property_info *info,
- const drmModeObjectProperties *props,
- uint64_t def)
-{
- unsigned int i;
-
- if (info->prop_id == 0)
- return def;
-
- for (i = 0; i < props->count_props; i++) {
- unsigned int j;
-
- if (props->props[i] != info->prop_id)
- continue;
-
- /* Simple (non-enum) types can return the value directly */
- if (info->num_enum_values == 0)
- return props->prop_values[i];
-
- /* Map from raw value to enum value */
- for (j = 0; j < info->num_enum_values; j++) {
- if (!info->enum_values[j].valid)
- continue;
- if (info->enum_values[j].value != props->prop_values[i])
- continue;
-
- return j;
- }
-
- /* We don't have a mapping for this enum; return default. */
- break;
- }
-
- return def;
-}
-
-/**
- * Cache DRM property values
- *
- * Update a per-object array of drm_property_info structures, given the
- * DRM properties of the object.
- *
- * Call this every time an object newly appears (note that only connectors
- * can be hotplugged), the first time it is seen, or when its status changes
- * in a way which invalidates the potential property values (currently, the
- * only case for this is connector hotplug).
- *
- * This updates the property IDs and enum values within the drm_property_info
- * array.
- *
- * DRM property enum values are dynamic at runtime; the user must query the
- * property to find out the desired runtime value for a requested string
- * name. Using the 'type' field on planes as an example, there is no single
- * hardcoded constant for primary plane types; instead, the property must be
- * queried at runtime to find the value associated with the string "Primary".
- *
- * This helper queries and caches the enum values, to allow us to use a set
- * of compile-time-constant enums portably across various implementations.
- * The values given in enum_names are searched for, and stored in the
- * same-indexed field of the map array.
- *
- * @param b DRM backend object
- * @param src DRM property info array to source from
- * @param info DRM property info array to copy into
- * @param num_infos Number of entries in the source array
- * @param props DRM object properties for the object
- */
-static void
-drm_property_info_populate(struct drm_backend *b,
- const struct drm_property_info *src,
- struct drm_property_info *info,
- unsigned int num_infos,
- drmModeObjectProperties *props)
-{
- drmModePropertyRes *prop;
- unsigned i, j;
-
- for (i = 0; i < num_infos; i++) {
- unsigned int j;
-
- info[i].name = src[i].name;
- info[i].prop_id = 0;
- info[i].num_enum_values = src[i].num_enum_values;
-
- if (src[i].num_enum_values == 0)
- continue;
-
- info[i].enum_values =
- malloc(src[i].num_enum_values *
- sizeof(*info[i].enum_values));
- assert(info[i].enum_values);
- for (j = 0; j < info[i].num_enum_values; j++) {
- info[i].enum_values[j].name = src[i].enum_values[j].name;
- info[i].enum_values[j].valid = false;
- }
- }
-
- for (i = 0; i < props->count_props; i++) {
- unsigned int k;
-
- prop = drmModeGetProperty(b->drm.fd, props->props[i]);
- if (!prop)
- continue;
-
- for (j = 0; j < num_infos; j++) {
- if (!strcmp(prop->name, info[j].name))
- break;
- }
-
- /* We don't know/care about this property. */
- if (j == num_infos) {
-#ifdef DEBUG
- weston_log("DRM debug: unrecognized property %u '%s'\n",
- prop->prop_id, prop->name);
-#endif
- drmModeFreeProperty(prop);
- continue;
- }
-
- if (info[j].num_enum_values == 0 &&
- (prop->flags & DRM_MODE_PROP_ENUM)) {
- weston_log("DRM: expected property %s to not be an"
- " enum, but it is; ignoring\n", prop->name);
- drmModeFreeProperty(prop);
- continue;
- }
-
- info[j].prop_id = props->props[i];
-
- if (info[j].num_enum_values == 0) {
- drmModeFreeProperty(prop);
- continue;
- }
-
- if (!(prop->flags & DRM_MODE_PROP_ENUM)) {
- weston_log("DRM: expected property %s to be an enum,"
- " but it is not; ignoring\n", prop->name);
- drmModeFreeProperty(prop);
- info[j].prop_id = 0;
- continue;
- }
-
- for (k = 0; k < info[j].num_enum_values; k++) {
- int l;
-
- for (l = 0; l < prop->count_enums; l++) {
- if (!strcmp(prop->enums[l].name,
- info[j].enum_values[k].name))
- break;
- }
-
- if (l == prop->count_enums)
- continue;
-
- info[j].enum_values[k].valid = true;
- info[j].enum_values[k].value = prop->enums[l].value;
- }
-
- drmModeFreeProperty(prop);
- }
-
-#ifdef DEBUG
- for (i = 0; i < num_infos; i++) {
- if (info[i].prop_id == 0)
- weston_log("DRM warning: property '%s' missing\n",
- info[i].name);
- }
-#endif
-}
-
-/**
- * Free DRM property information
- *
- * Frees all memory associated with a DRM property info array and zeroes
- * it out, leaving it usable for a further drm_property_info_update() or
- * drm_property_info_free().
- *
- * @param info DRM property info array
- * @param num_props Number of entries in array to free
- */
-static void
-drm_property_info_free(struct drm_property_info *info, int num_props)
-{
- int i;
-
- for (i = 0; i < num_props; i++)
- free(info[i].enum_values);
-
- memset(info, 0, sizeof(*info) * num_props);
-}
-
-static void
-drm_output_set_cursor(struct drm_output_state *output_state);
-
-static void
-drm_output_update_msc(struct drm_output *output, unsigned int seq);
-
static void
drm_output_destroy(struct weston_output *output_base);
return !!(plane->possible_crtcs & (1 << output->pipe));
}
-static struct drm_output *
+struct drm_output *
drm_output_find_by_crtc(struct drm_backend *b, uint32_t crtc_id)
{
struct drm_output *output;
return NULL;
}
-static struct drm_head *
+struct drm_head *
drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id)
{
struct weston_head *base;
* Free an existing plane state. As a special case, the state will not
* normally be freed if it is the current state; see drm_plane_set_state.
*/
-static void
+void
drm_plane_state_free(struct drm_plane_state *state, bool force)
{
if (!state)
/**
* Return a plane state from a drm_output_state.
*/
-static struct drm_plane_state *
+struct drm_plane_state *
drm_output_state_get_existing_plane(struct drm_output_state *state_output,
struct drm_plane *plane)
{
/**
* Free an unused drm_output_state.
*/
-static void
+void
drm_output_state_free(struct drm_output_state *state)
{
struct drm_plane_state *ps, *next;
*
* @param pending_state Pending state structure to free
*/
-static void
+void
drm_pending_state_free(struct drm_pending_state *pending_state)
{
struct drm_output_state *output_state, *tmp;
return NULL;
}
-static int drm_pending_state_apply_sync(struct drm_pending_state *state);
-static int drm_pending_state_test(struct drm_pending_state *state);
-
/**
* 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.
*/
-static void
+void
drm_output_update_complete(struct drm_output *output, uint32_t flags,
unsigned int sec, unsigned int usec)
{
weston_output_schedule_repaint(&output->base);
}
-/**
- * Mark an output state as current on the output, i.e. it has been
- * submitted to the kernel. The mode argument determines whether this
- * update will be applied synchronously (e.g. when calling drmModeSetCrtc),
- * or asynchronously (in which case we wait for events to complete).
- */
-static void
-drm_output_assign_state(struct drm_output_state *state,
- enum drm_state_apply_mode mode)
-{
- struct drm_output *output = state->output;
- struct drm_backend *b = to_drm_backend(output->base.compositor);
- struct drm_plane_state *plane_state;
-
- assert(!output->state_last);
-
- if (mode == DRM_STATE_APPLY_ASYNC)
- output->state_last = output->state_cur;
- else
- drm_output_state_free(output->state_cur);
-
- wl_list_remove(&state->link);
- wl_list_init(&state->link);
- state->pending_state = NULL;
-
- output->state_cur = state;
-
- if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
- drm_debug(b, "\t[CRTC:%u] setting pending flip\n", output->crtc_id);
- output->atomic_complete_pending = 1;
- }
-
- /* Replace state_cur on each affected plane with the new state, being
- * careful to dispose of orphaned (but only orphaned) previous state.
- * If the previous state is not orphaned (still has an output_state
- * attached), it will be disposed of by freeing the output_state. */
- wl_list_for_each(plane_state, &state->plane_list, link) {
- struct drm_plane *plane = plane_state->plane;
-
- if (plane->state_cur && !plane->state_cur->output_state)
- drm_plane_state_free(plane->state_cur, true);
- plane->state_cur = plane_state;
-
- if (mode != DRM_STATE_APPLY_ASYNC) {
- plane_state->complete = true;
- continue;
- }
-
- if (b->atomic_modeset)
- continue;
-
- assert(plane->type != WDRM_PLANE_TYPE_OVERLAY);
- if (plane->type == WDRM_PLANE_TYPE_PRIMARY)
- output->page_flip_pending = 1;
- }
-}
-
static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
struct weston_view *ev,
&c->primary_plane.damage, damage);
}
-static void
-drm_output_set_gamma(struct weston_output *output_base,
- uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
+
+static int
+drm_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage,
+ void *repaint_data)
{
- int rc;
+ struct drm_pending_state *pending_state = repaint_data;
struct drm_output *output = to_drm_output(output_base);
- struct drm_backend *backend =
- to_drm_backend(output->base.compositor);
+ struct drm_output_state *state = NULL;
+ struct drm_plane_state *scanout_state;
- /* check */
- if (output_base->gamma_size != size)
- return;
+ assert(!output->virtual);
+
+ 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;
+
+ drm_output_render(state, damage);
+ scanout_state = drm_output_state_get_plane(state,
+ output->scanout_plane);
+ if (!scanout_state || !scanout_state->fb)
+ goto err;
+
+ return 0;
- rc = drmModeCrtcSetGamma(backend->drm.fd,
- output->crtc_id,
- size, r, g, b);
- if (rc)
- weston_log("set gamma failed: %s\n", strerror(errno));
+err:
+ drm_output_state_free(state);
+ return -1;
}
/* Determine the type of vblank synchronization to use for the output.
return 0;
}
-static int
-drm_output_apply_state_legacy(struct drm_output_state *state)
+static void
+drm_output_start_repaint_loop(struct weston_output *output_base)
{
- struct drm_output *output = state->output;
- struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ 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_property_info *dpms_prop;
- struct drm_plane_state *scanout_state;
- struct drm_mode *mode;
- struct drm_head *head;
- const struct pixel_format_info *pinfo = NULL;
- uint32_t connectors[MAX_CLONED_CONNECTORS];
- int n_conn = 0;
- struct timespec now;
- int ret = 0;
+ struct drm_backend *backend =
+ to_drm_backend(output_base->compositor);
+ struct timespec ts, tnow;
+ struct timespec vbl2now;
+ int64_t refresh_nsec;
+ int ret;
+ drmVBlank vbl = {
+ .request.type = DRM_VBLANK_RELATIVE,
+ .request.sequence = 0,
+ .request.signal = 0,
+ };
- wl_list_for_each(head, &output->base.head_list, base.output_link) {
- assert(n_conn < MAX_CLONED_CONNECTORS);
- connectors[n_conn++] = head->connector_id;
- }
+ if (output->disable_pending || output->destroy_pending)
+ return;
- /* If disable_planes is set then assign_planes() wasn't
- * called for this render, so we could still have a stale
- * cursor plane set up.
- */
- if (output->base.disable_planes) {
- output->cursor_view = NULL;
- if (output->cursor_plane) {
- output->cursor_plane->base.x = INT32_MIN;
- output->cursor_plane->base.y = INT32_MIN;
- }
+ if (!output->scanout_plane->state_cur->fb) {
+ /* We can't page flip if there's no mode set */
+ goto finish_frame;
}
- if (state->dpms != WESTON_DPMS_ON) {
- if (output->cursor_plane) {
- ret = drmModeSetCursor(backend->drm.fd, output->crtc_id,
- 0, 0, 0);
- if (ret)
- weston_log("drmModeSetCursor failed disable: %s\n",
- strerror(errno));
- }
+ /* Need to smash all state in from scratch; current timings might not
+ * be what we want, page flip might not work, etc.
+ */
+ if (backend->state_invalid)
+ goto finish_frame;
- ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
- NULL, 0, NULL);
- if (ret)
- weston_log("drmModeSetCrtc failed disabling: %s\n",
- strerror(errno));
+ assert(scanout_plane->state_cur->output == output);
- drm_output_assign_state(state, DRM_STATE_APPLY_SYNC);
- weston_compositor_read_presentation_clock(output->base.compositor, &now);
- drm_output_update_complete(output,
- WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION,
- now.tv_sec, now.tv_nsec / 1000);
+ /* Try to get current msc and timestamp via instant query */
+ vbl.request.type |= drm_waitvblank_pipe(output);
+ ret = drmWaitVBlank(backend->drm.fd, &vbl);
- return 0;
- }
+ /* 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;
- scanout_state =
- drm_output_state_get_existing_plane(state, scanout_plane);
-
- /* The legacy SetCrtc API doesn't allow us to do scaling, and the
- * legacy PageFlip API doesn't allow us to do clipping either. */
- assert(scanout_state->src_x == 0);
- assert(scanout_state->src_y == 0);
- assert(scanout_state->src_w ==
- (unsigned) (output->base.current_mode->width << 16));
- assert(scanout_state->src_h ==
- (unsigned) (output->base.current_mode->height << 16));
- assert(scanout_state->dest_x == 0);
- assert(scanout_state->dest_y == 0);
- assert(scanout_state->dest_w == scanout_state->src_w >> 16);
- assert(scanout_state->dest_h == scanout_state->src_h >> 16);
- /* The legacy SetCrtc API doesn't support fences */
- assert(scanout_state->in_fence_fd == -1);
-
- mode = to_drm_mode(output->base.current_mode);
- if (backend->state_invalid ||
- !scanout_plane->state_cur->fb ||
- scanout_plane->state_cur->fb->strides[0] !=
- scanout_state->fb->strides[0]) {
-
- ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- scanout_state->fb->fb_id,
- 0, 0,
- connectors, n_conn,
- &mode->mode_info);
- if (ret) {
- weston_log("set mode failed: %s\n", strerror(errno));
- goto err;
+ /* 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(backend->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,
+ WP_PRESENTATION_FEEDBACK_INVALID);
+ return;
}
}
- pinfo = scanout_state->fb->format;
- drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n",
- output->crtc_id, scanout_state->plane->plane_id,
- pinfo ? pinfo->drm_format_name : "UNKNOWN");
-
- if (drmModePageFlip(backend->drm.fd, output->crtc_id,
- scanout_state->fb->fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
- weston_log("queueing pageflip failed: %s\n", strerror(errno));
- goto err;
- }
+ /* Immediate query didn't provide valid timestamp.
+ * Use pageflip fallback.
+ */
assert(!output->page_flip_pending);
+ assert(!output->state_last);
- if (output->pageflip_timer)
- wl_event_source_timer_update(output->pageflip_timer,
- backend->pageflip_timeout);
-
- drm_output_set_cursor(state);
-
- if (state->dpms != output->state_cur->dpms) {
- wl_list_for_each(head, &output->base.head_list, base.output_link) {
- dpms_prop = &head->props_conn[WDRM_CONNECTOR_DPMS];
- if (dpms_prop->prop_id == 0)
- continue;
-
- ret = drmModeConnectorSetProperty(backend->drm.fd,
- head->connector_id,
- dpms_prop->prop_id,
- state->dpms);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- head->base.name);
- }
- }
- }
-
- drm_output_assign_state(state, DRM_STATE_APPLY_ASYNC);
-
- return 0;
-
-err:
- output->cursor_view = NULL;
- drm_output_state_free(state);
- return -1;
-}
-
-#ifdef HAVE_DRM_ATOMIC
-static int
-crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output,
- enum wdrm_crtc_property prop, uint64_t val)
-{
- struct drm_property_info *info = &output->props_crtc[prop];
- int ret;
-
- if (info->prop_id == 0)
- return -1;
-
- ret = drmModeAtomicAddProperty(req, output->crtc_id, info->prop_id,
- val);
- drm_debug(output->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
- (unsigned long) output->crtc_id,
- (unsigned long) info->prop_id, info->name,
- (unsigned long long) val, (unsigned long long) val);
- return (ret <= 0) ? -1 : 0;
-}
-
-static int
-connector_add_prop(drmModeAtomicReq *req, struct drm_head *head,
- enum wdrm_connector_property prop, uint64_t val)
-{
- struct drm_property_info *info = &head->props_conn[prop];
- int ret;
-
- if (info->prop_id == 0)
- return -1;
-
- ret = drmModeAtomicAddProperty(req, head->connector_id,
- info->prop_id, val);
- drm_debug(head->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
- (unsigned long) head->connector_id,
- (unsigned long) info->prop_id, info->name,
- (unsigned long long) val, (unsigned long long) val);
- return (ret <= 0) ? -1 : 0;
-}
-
-static int
-plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
- enum wdrm_plane_property prop, uint64_t val)
-{
- struct drm_property_info *info = &plane->props[prop];
- int ret;
-
- if (info->prop_id == 0)
- return -1;
-
- ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
- val);
- drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
- (unsigned long) plane->plane_id,
- (unsigned long) info->prop_id, info->name,
- (unsigned long long) val, (unsigned long long) val);
- return (ret <= 0) ? -1 : 0;
-}
-
-static int
-plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend,
- struct drm_plane_state *plane_state)
-{
- struct drm_plane *plane = plane_state->plane;
- struct drm_property_info *info =
- &plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS];
- pixman_box32_t *rects;
- uint32_t blob_id;
- int n_rects;
- int ret;
-
- if (!pixman_region32_not_empty(&plane_state->damage))
- return 0;
-
- /*
- * If a plane doesn't support fb damage blob property, kernel will
- * perform full plane update.
- */
- if (info->prop_id == 0)
- return 0;
-
- rects = pixman_region32_rectangles(&plane_state->damage, &n_rects);
-
- ret = drmModeCreatePropertyBlob(backend->drm.fd, rects,
- sizeof(*rects) * n_rects, &blob_id);
- if (ret != 0)
- return ret;
-
- ret = plane_add_prop(req, plane, WDRM_PLANE_FB_DAMAGE_CLIPS, blob_id);
- if (ret != 0)
- return ret;
-
- return 0;
-}
-
-static int
-drm_output_apply_state_atomic(struct drm_output_state *state,
- drmModeAtomicReq *req,
- uint32_t *flags)
-{
- struct drm_output *output = state->output;
- struct drm_backend *b = to_drm_backend(output->base.compositor);
- struct drm_plane_state *plane_state;
- struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
- struct drm_head *head;
- int ret = 0;
-
- drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
- (*flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "testing" : "applying",
- (unsigned long) output->base.id, output->base.name);
-
- if (state->dpms != output->state_cur->dpms) {
- drm_debug(b, "\t\t\t[atomic] DPMS state differs, modeset OK\n");
- *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
- }
-
- if (state->dpms == WESTON_DPMS_ON) {
- ret = drm_mode_ensure_blob(b, current_mode);
- if (ret != 0)
- return ret;
-
- ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
- current_mode->blob_id);
- ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
-
- /* No need for the DPMS property, since it is implicit in
- * routing and CRTC activity. */
- wl_list_for_each(head, &output->base.head_list, base.output_link) {
- ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID,
- output->crtc_id);
- }
- } else {
- ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
- ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
-
- /* No need for the DPMS property, since it is implicit in
- * routing and CRTC activity. */
- wl_list_for_each(head, &output->base.head_list, base.output_link)
- ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
- }
-
- if (ret != 0) {
- weston_log("couldn't set atomic CRTC/connector state\n");
- return ret;
- }
-
- wl_list_for_each(plane_state, &state->plane_list, link) {
- struct drm_plane *plane = plane_state->plane;
- const struct pixel_format_info *pinfo = NULL;
-
- ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_ID,
- plane_state->fb ? plane_state->fb->fb_id : 0);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID,
- plane_state->fb ? output->crtc_id : 0);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_X,
- plane_state->src_x);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_Y,
- plane_state->src_y);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_W,
- plane_state->src_w);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_H,
- plane_state->src_h);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_X,
- plane_state->dest_x);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_Y,
- plane_state->dest_y);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_W,
- plane_state->dest_w);
- ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_H,
- plane_state->dest_h);
- ret |= plane_add_damage(req, b, plane_state);
-
- if (plane_state->fb && plane_state->fb->format)
- pinfo = plane_state->fb->format;
-
- drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
- (unsigned long) plane->plane_id,
- pinfo ? pinfo->drm_format_name : "UNKNOWN");
-
- if (plane_state->in_fence_fd >= 0) {
- ret |= plane_add_prop(req, plane,
- WDRM_PLANE_IN_FENCE_FD,
- plane_state->in_fence_fd);
- }
-
- if (ret != 0) {
- weston_log("couldn't set plane state\n");
- return ret;
- }
- }
-
- return 0;
-}
-
-/**
- * Helper function used only by drm_pending_state_apply, with the same
- * guarantees and constraints as that function.
- */
-static int
-drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
- enum drm_state_apply_mode mode)
-{
- struct drm_backend *b = pending_state->backend;
- struct drm_output_state *output_state, *tmp;
- struct drm_plane *plane;
- drmModeAtomicReq *req = drmModeAtomicAlloc();
- uint32_t flags;
- int ret = 0;
-
- if (!req)
- return -1;
-
- switch (mode) {
- case DRM_STATE_APPLY_SYNC:
- flags = 0;
- break;
- case DRM_STATE_APPLY_ASYNC:
- flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
- break;
- case DRM_STATE_TEST_ONLY:
- flags = DRM_MODE_ATOMIC_TEST_ONLY;
- break;
- }
-
- if (b->state_invalid) {
- struct weston_head *head_base;
- struct drm_head *head;
- uint32_t *unused;
- int err;
-
- drm_debug(b, "\t\t[atomic] previous state invalid; "
- "starting with fresh state\n");
-
- /* If we need to reset all our state (e.g. because we've
- * just started, or just been VT-switched in), explicitly
- * disable all the CRTCs and connectors we aren't using. */
- wl_list_for_each(head_base,
- &b->compositor->head_list, compositor_link) {
- struct drm_property_info *info;
-
- if (weston_head_is_enabled(head_base))
- continue;
-
- head = to_drm_head(head_base);
-
- drm_debug(b, "\t\t[atomic] disabling inactive head %s\n",
- head_base->name);
-
- info = &head->props_conn[WDRM_CONNECTOR_CRTC_ID];
- err = drmModeAtomicAddProperty(req, head->connector_id,
- info->prop_id, 0);
- drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> 0\n",
- (unsigned long) head->connector_id,
- (unsigned long) info->prop_id,
- info->name);
- if (err <= 0)
- ret = -1;
- }
-
- wl_array_for_each(unused, &b->unused_crtcs) {
- struct drm_property_info infos[WDRM_CRTC__COUNT];
- struct drm_property_info *info;
- drmModeObjectProperties *props;
- uint64_t active;
-
- memset(infos, 0, sizeof(infos));
-
- /* We can't emit a disable on a CRTC that's already
- * off, as the kernel will refuse to generate an event
- * for an off->off state and fail the commit.
- */
- props = drmModeObjectGetProperties(b->drm.fd,
- *unused,
- DRM_MODE_OBJECT_CRTC);
- if (!props) {
- ret = -1;
- continue;
- }
-
- drm_property_info_populate(b, crtc_props, infos,
- WDRM_CRTC__COUNT,
- props);
-
- info = &infos[WDRM_CRTC_ACTIVE];
- active = drm_property_get_value(info, props, 0);
- drmModeFreeObjectProperties(props);
- if (active == 0) {
- drm_property_info_free(infos, WDRM_CRTC__COUNT);
- continue;
- }
-
- drm_debug(b, "\t\t[atomic] disabling unused CRTC %lu\n",
- (unsigned long) *unused);
-
- drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> 0\n",
- (unsigned long) *unused,
- (unsigned long) info->prop_id, info->name);
- err = drmModeAtomicAddProperty(req, *unused,
- info->prop_id, 0);
- if (err <= 0)
- ret = -1;
-
- info = &infos[WDRM_CRTC_MODE_ID];
- drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> 0\n",
- (unsigned long) *unused,
- (unsigned long) info->prop_id, info->name);
- err = drmModeAtomicAddProperty(req, *unused,
- info->prop_id, 0);
- if (err <= 0)
- ret = -1;
-
- drm_property_info_free(infos, WDRM_CRTC__COUNT);
- }
-
- /* Disable all the planes; planes which are being used will
- * override this state in the output-state application. */
- wl_list_for_each(plane, &b->plane_list, link) {
- drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n",
- (unsigned long) plane->plane_id);
- plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0);
- plane_add_prop(req, plane, WDRM_PLANE_FB_ID, 0);
- }
-
- flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
- }
-
- wl_list_for_each(output_state, &pending_state->output_list, link) {
- if (output_state->output->virtual)
- continue;
- if (mode == DRM_STATE_APPLY_SYNC)
- assert(output_state->dpms == WESTON_DPMS_OFF);
- ret |= drm_output_apply_state_atomic(output_state, req, &flags);
- }
-
- if (ret != 0) {
- weston_log("atomic: couldn't compile atomic state\n");
- goto out;
- }
-
- ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
- drm_debug(b, "[atomic] drmModeAtomicCommit\n");
-
- /* Test commits do not take ownership of the state; return
- * without freeing here. */
- if (mode == DRM_STATE_TEST_ONLY) {
- drmModeAtomicFree(req);
- return ret;
- }
-
- if (ret != 0) {
- weston_log("atomic: couldn't commit new state: %s\n",
- strerror(errno));
- goto out;
- }
-
- wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
- link)
- drm_output_assign_state(output_state, mode);
-
- b->state_invalid = false;
-
- assert(wl_list_empty(&pending_state->output_list));
-
-out:
- drmModeAtomicFree(req);
- drm_pending_state_free(pending_state);
- return ret;
-}
-#endif
-
-/**
- * Tests a pending state, to see if the kernel will accept the update as
- * constructed.
- *
- * Using atomic modesetting, the kernel performs the same checks as it would
- * on a real commit, returning success or failure without actually modifying
- * the running state. It does not return -EBUSY if there are pending updates
- * in flight, so states may be tested at any point, however this means a
- * state which passed testing may fail on a real commit if the timing is not
- * respected (e.g. committing before the previous commit has completed).
- *
- * Without atomic modesetting, we have no way to check, so we optimistically
- * claim it will work.
- *
- * Unlike drm_pending_state_apply() and drm_pending_state_apply_sync(), this
- * function does _not_ take ownership of pending_state, nor does it clear
- * state_invalid.
- */
-static int
-drm_pending_state_test(struct drm_pending_state *pending_state)
-{
-#ifdef HAVE_DRM_ATOMIC
- struct drm_backend *b = pending_state->backend;
-
- if (b->atomic_modeset)
- return drm_pending_state_apply_atomic(pending_state,
- DRM_STATE_TEST_ONLY);
-#endif
-
- /* We have no way to test state before application on the legacy
- * modesetting API, so just claim it succeeded. */
- return 0;
-}
-
-/**
- * Applies all of a pending_state asynchronously: the primary entry point for
- * applying KMS state to a device. Updates the state for all outputs in the
- * pending_state, as well as disabling any unclaimed outputs.
- *
- * Unconditionally takes ownership of pending_state, and clears state_invalid.
- */
-static int
-drm_pending_state_apply(struct drm_pending_state *pending_state)
-{
- struct drm_backend *b = pending_state->backend;
- struct drm_output_state *output_state, *tmp;
- uint32_t *unused;
-
-#ifdef HAVE_DRM_ATOMIC
- if (b->atomic_modeset)
- return drm_pending_state_apply_atomic(pending_state,
- DRM_STATE_APPLY_ASYNC);
-#endif
-
- if (b->state_invalid) {
- /* If we need to reset all our state (e.g. because we've
- * just started, or just been VT-switched in), explicitly
- * disable all the CRTCs we aren't using. This also disables
- * all connectors on these CRTCs, so we don't need to do that
- * separately with the pre-atomic API. */
- wl_array_for_each(unused, &b->unused_crtcs)
- drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
- NULL);
- }
-
- wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
- link) {
- struct drm_output *output = output_state->output;
- int ret;
-
- if (output->virtual) {
- drm_output_assign_state(output_state,
- DRM_STATE_APPLY_ASYNC);
- continue;
- }
-
- ret = drm_output_apply_state_legacy(output_state);
- if (ret != 0) {
- weston_log("Couldn't apply state for output %s\n",
- output->base.name);
- }
- }
-
- b->state_invalid = false;
-
- assert(wl_list_empty(&pending_state->output_list));
-
- drm_pending_state_free(pending_state);
-
- return 0;
-}
-
-/**
- * The synchronous version of drm_pending_state_apply. May only be used to
- * disable outputs. Does so synchronously: the request is guaranteed to have
- * completed on return, and the output will not be touched afterwards.
- *
- * Unconditionally takes ownership of pending_state, and clears state_invalid.
- */
-static int
-drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
-{
- struct drm_backend *b = pending_state->backend;
- struct drm_output_state *output_state, *tmp;
- uint32_t *unused;
-
-#ifdef HAVE_DRM_ATOMIC
- if (b->atomic_modeset)
- return drm_pending_state_apply_atomic(pending_state,
- DRM_STATE_APPLY_SYNC);
-#endif
-
- if (b->state_invalid) {
- /* If we need to reset all our state (e.g. because we've
- * just started, or just been VT-switched in), explicitly
- * disable all the CRTCs we aren't using. This also disables
- * all connectors on these CRTCs, so we don't need to do that
- * separately with the pre-atomic API. */
- wl_array_for_each(unused, &b->unused_crtcs)
- drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
- NULL);
- }
-
- wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
- link) {
- int ret;
-
- assert(output_state->dpms == WESTON_DPMS_OFF);
- ret = drm_output_apply_state_legacy(output_state);
- if (ret != 0) {
- weston_log("Couldn't apply state for output %s\n",
- output_state->output->base.name);
- }
- }
-
- b->state_invalid = false;
-
- assert(wl_list_empty(&pending_state->output_list));
-
- drm_pending_state_free(pending_state);
-
- return 0;
-}
-
-static int
-drm_output_repaint(struct weston_output *output_base,
- pixman_region32_t *damage,
- void *repaint_data)
-{
- struct drm_pending_state *pending_state = repaint_data;
- struct drm_output *output = to_drm_output(output_base);
- struct drm_output_state *state = NULL;
- struct drm_plane_state *scanout_state;
-
- assert(!output->virtual);
-
- 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;
-
- drm_output_render(state, damage);
- 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;
-}
-
-static void
-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_backend *backend =
- to_drm_backend(output_base->compositor);
- struct timespec ts, tnow;
- struct timespec vbl2now;
- int64_t refresh_nsec;
- int ret;
- drmVBlank vbl = {
- .request.type = DRM_VBLANK_RELATIVE,
- .request.sequence = 0,
- .request.signal = 0,
- };
-
- if (output->disable_pending || output->destroy_pending)
- return;
-
- if (!output->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 (backend->state_invalid)
- goto finish_frame;
-
- assert(scanout_plane->state_cur->output == output);
-
- /* Try to get current msc and timestamp via instant query */
- vbl.request.type |= drm_waitvblank_pipe(output);
- ret = drmWaitVBlank(backend->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(backend->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,
- WP_PRESENTATION_FEEDBACK_INVALID);
- return;
- }
- }
-
- /* 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(backend);
- drm_output_state_duplicate(output->state_cur, pending_state,
- DRM_OUTPUT_STATE_PRESERVE_PLANES);
+ pending_state = drm_pending_state_alloc(backend);
+ drm_output_state_duplicate(output->state_cur, pending_state,
+ DRM_OUTPUT_STATE_PRESERVE_PLANES);
ret = drm_pending_state_apply(pending_state);
if (ret != 0) {
WP_PRESENTATION_FEEDBACK_INVALID);
}
-static void
-drm_output_update_msc(struct drm_output *output, unsigned int seq)
-{
- uint64_t msc_hi = output->base.msc >> 32;
-
- if (seq < (output->base.msc & 0xffffffff))
- msc_hi++;
-
- output->base.msc = (msc_hi << 32) + seq;
-}
-
-
-static void
-page_flip_handler(int fd, unsigned int frame,
- unsigned int sec, unsigned int usec, void *data)
-{
- struct drm_output *output = data;
- struct drm_backend *b = to_drm_backend(output->base.compositor);
- uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
- WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
- WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
-
- drm_output_update_msc(output, frame);
-
- assert(!b->atomic_modeset);
- assert(output->page_flip_pending);
- output->page_flip_pending = 0;
-
- drm_output_update_complete(output, flags, sec, usec);
-}
-
/**
* Begin a new repaint cycle
*
b->repaint_data = NULL;
}
-#ifdef HAVE_DRM_ATOMIC
-static void
-atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
- unsigned int usec, unsigned int crtc_id, void *data)
-{
- struct drm_backend *b = data;
- struct drm_output *output = drm_output_find_by_crtc(b, crtc_id);
- uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
- WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
- WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
-
- /* During the initial modeset, we can disable CRTCs which we don't
- * actually handle during normal operation; this will give us events
- * for unknown outputs. Ignore them. */
- if (!output || !output->base.enabled)
- return;
-
- drm_output_update_msc(output, frame);
-
- drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
- assert(b->atomic_modeset);
- assert(output->atomic_complete_pending);
- output->atomic_complete_pending = 0;
-
- drm_output_update_complete(output, flags, sec, usec);
- drm_debug(b, "[atomic][CRTC:%u] flip processing completed\n", crtc_id);
-}
-#endif
-
static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_view *ev,
return NULL;
}
-static void
-drm_output_set_cursor(struct drm_output_state *output_state)
-{
- struct drm_output *output = output_state->output;
- struct drm_backend *b = to_drm_backend(output->base.compositor);
- struct drm_plane *plane = output->cursor_plane;
- struct drm_plane_state *state;
- EGLint handle;
- struct gbm_bo *bo;
-
- if (!plane)
- return;
-
- state = drm_output_state_get_existing_plane(output_state, plane);
- if (!state)
- return;
-
- if (!state->fb) {
- pixman_region32_fini(&plane->base.damage);
- pixman_region32_init(&plane->base.damage);
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
- return;
- }
-
- assert(state->fb == output->gbm_cursor_fb[output->current_cursor]);
- assert(!plane->state_cur->output || plane->state_cur->output == output);
-
- if (plane->state_cur->fb != state->fb) {
- bo = state->fb->bo;
- handle = gbm_bo_get_handle(bo).s32;
- if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle,
- b->cursor_width, b->cursor_height)) {
- weston_log("failed to set cursor: %s\n",
- strerror(errno));
- goto err;
- }
- }
-
- pixman_region32_fini(&plane->base.damage);
- pixman_region32_init(&plane->base.damage);
-
- if (drmModeMoveCursor(b->drm.fd, output->crtc_id,
- state->dest_x, state->dest_y)) {
- weston_log("failed to move cursor: %s\n", strerror(errno));
- goto err;
- }
-
- return;
-
-err:
- b->cursors_are_broken = 1;
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
-}
static struct drm_output_state *
drm_output_propose_state(struct weston_output *output_base,
return 0;
}
-static int
-on_drm_input(int fd, uint32_t mask, void *data)
-{
-#ifdef HAVE_DRM_ATOMIC
- struct drm_backend *b = data;
-#endif
- drmEventContext evctx;
-
- memset(&evctx, 0, sizeof evctx);
-#ifndef HAVE_DRM_ATOMIC
- evctx.version = 2;
-#else
- evctx.version = 3;
- if (b->atomic_modeset)
- evctx.page_flip_handler2 = atomic_flip_handler;
- else
-#endif
- evctx.page_flip_handler = page_flip_handler;
- drmHandleEvent(fd, &evctx);
-
- return 1;
-}
-
-static int
-init_kms_caps(struct drm_backend *b)
-{
- uint64_t cap;
- int ret;
- clockid_t clk_id;
-
- weston_log("using %s\n", b->drm.filename);
-
- ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
- if (ret == 0 && cap == 1)
- clk_id = CLOCK_MONOTONIC;
- else
- clk_id = CLOCK_REALTIME;
-
- if (weston_compositor_set_presentation_clock(b->compositor, clk_id) < 0) {
- weston_log("Error: failed to set presentation clock %d.\n",
- clk_id);
- return -1;
- }
-
- ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
- if (ret == 0)
- b->cursor_width = cap;
- else
- b->cursor_width = 64;
-
- ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
- if (ret == 0)
- b->cursor_height = cap;
- else
- b->cursor_height = 64;
-
- if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
- b->universal_planes = (ret == 0);
- }
- weston_log("DRM: %s universal planes\n",
- b->universal_planes ? "supports" : "does not support");
-
-#ifdef HAVE_DRM_ATOMIC
- if (b->universal_planes && !getenv("WESTON_DISABLE_ATOMIC")) {
- ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
- if (ret != 0)
- cap = 0;
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
- b->atomic_modeset = ((ret == 0) && (cap == 1));
- }
-#endif
- weston_log("DRM: %s atomic modesetting\n",
- b->atomic_modeset ? "supports" : "does not support");
-
-#ifdef HAVE_DRM_ADDFB2_MODIFIERS
- ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
- if (ret == 0)
- b->fb_modifiers = cap;
- else
-#endif
- b->fb_modifiers = 0;
-
- /*
- * KMS support for hardware planes cannot properly synchronize
- * without nuclear page flip. Without nuclear/atomic, hw plane
- * and cursor plane updates would either tear or cause extra
- * waits for vblanks which means dropping the compositor framerate
- * to a fraction. For cursors, it's not so bad, so they are
- * enabled.
- */
- if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
- b->sprites_are_broken = 1;
-
- ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
- b->aspect_ratio_supported = (ret == 0);
- weston_log("DRM: %s picture aspect ratio\n",
- b->aspect_ratio_supported ? "supports" : "does not support");
-
- return 0;
-}
-
static struct gbm_device *
create_gbm_device(int fd)
{
return pixman_renderer_init(b->compositor);
}
-#ifdef HAVE_DRM_FORMATS_BLOB
-static inline uint32_t *
-formats_ptr(struct drm_format_modifier_blob *blob)
-{
- return (uint32_t *)(((char *)blob) + blob->formats_offset);
-}
-
-static inline struct drm_format_modifier *
-modifiers_ptr(struct drm_format_modifier_blob *blob)
-{
- return (struct drm_format_modifier *)
- (((char *)blob) + blob->modifiers_offset);
-}
-#endif
-
-/**
- * Populates the plane's formats array, using either the IN_FORMATS blob
- * property (if available), or the plane's format list if not.
- */
-static int
-drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
- const drmModeObjectProperties *props)
-{
- unsigned i;
-#ifdef HAVE_DRM_FORMATS_BLOB
- drmModePropertyBlobRes *blob;
- struct drm_format_modifier_blob *fmt_mod_blob;
- struct drm_format_modifier *blob_modifiers;
- uint32_t *blob_formats;
- uint32_t blob_id;
-
- blob_id = drm_property_get_value(&plane->props[WDRM_PLANE_IN_FORMATS],
- props,
- 0);
- if (blob_id == 0)
- goto fallback;
-
- blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
- if (!blob)
- goto fallback;
-
- fmt_mod_blob = blob->data;
- blob_formats = formats_ptr(fmt_mod_blob);
- blob_modifiers = modifiers_ptr(fmt_mod_blob);
-
- if (plane->count_formats != fmt_mod_blob->count_formats) {
- weston_log("DRM backend: format count differs between "
- "plane (%d) and IN_FORMATS (%d)\n",
- plane->count_formats,
- fmt_mod_blob->count_formats);
- weston_log("This represents a kernel bug; Weston is "
- "unable to continue.\n");
- abort();
- }
-
- for (i = 0; i < fmt_mod_blob->count_formats; i++) {
- uint32_t count_modifiers = 0;
- uint64_t *modifiers = NULL;
- unsigned j;
-
- for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
- struct drm_format_modifier *mod = &blob_modifiers[j];
-
- if ((i < mod->offset) || (i > mod->offset + 63))
- continue;
- if (!(mod->formats & (1 << (i - mod->offset))))
- continue;
-
- modifiers = realloc(modifiers,
- (count_modifiers + 1) *
- sizeof(modifiers[0]));
- assert(modifiers);
- modifiers[count_modifiers++] = mod->modifier;
- }
-
- plane->formats[i].format = blob_formats[i];
- plane->formats[i].modifiers = modifiers;
- plane->formats[i].count_modifiers = count_modifiers;
- }
-
- drmModeFreePropertyBlob(blob);
-
- return 0;
-
-fallback:
-#endif
- /* No IN_FORMATS blob available, so just use the old. */
- assert(plane->count_formats == kplane->count_formats);
- for (i = 0; i < kplane->count_formats; i++)
- plane->formats[i].format = kplane->formats[i];
-
- return 0;
-}
/**
* Create a drm_plane for a hardware plane
--- /dev/null
+/*
+ * 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 <stdint.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include <gbm.h>
+
+#include <libweston/libweston.h>
+#include <libweston/backend-drm.h>
+#include "shared/helpers.h"
+#include "drm-internal.h"
+#include "pixel-formats.h"
+#include "presentation-time-server-protocol.h"
+
+struct drm_property_enum_info plane_type_enums[] = {
+ [WDRM_PLANE_TYPE_PRIMARY] = {
+ .name = "Primary",
+ },
+ [WDRM_PLANE_TYPE_OVERLAY] = {
+ .name = "Overlay",
+ },
+ [WDRM_PLANE_TYPE_CURSOR] = {
+ .name = "Cursor",
+ },
+};
+
+const struct drm_property_info plane_props[] = {
+ [WDRM_PLANE_TYPE] = {
+ .name = "type",
+ .enum_values = plane_type_enums,
+ .num_enum_values = WDRM_PLANE_TYPE__COUNT,
+ },
+ [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
+ [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
+ [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
+ [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
+ [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
+ [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
+ [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
+ [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
+ [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
+ [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
+ [WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" },
+ [WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" },
+ [WDRM_PLANE_FB_DAMAGE_CLIPS] = { .name = "FB_DAMAGE_CLIPS" },
+};
+
+struct drm_property_enum_info dpms_state_enums[] = {
+ [WDRM_DPMS_STATE_OFF] = {
+ .name = "Off",
+ },
+ [WDRM_DPMS_STATE_ON] = {
+ .name = "On",
+ },
+ [WDRM_DPMS_STATE_STANDBY] = {
+ .name = "Standby",
+ },
+ [WDRM_DPMS_STATE_SUSPEND] = {
+ .name = "Suspend",
+ },
+};
+
+const struct drm_property_info connector_props[] = {
+ [WDRM_CONNECTOR_EDID] = { .name = "EDID" },
+ [WDRM_CONNECTOR_DPMS] = {
+ .name = "DPMS",
+ .enum_values = dpms_state_enums,
+ .num_enum_values = WDRM_DPMS_STATE__COUNT,
+ },
+ [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
+ [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", },
+};
+
+const struct drm_property_info crtc_props[] = {
+ [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", },
+ [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", },
+};
+
+
+/**
+ * Mode for drm_pending_state_apply and co.
+ */
+enum drm_state_apply_mode {
+ DRM_STATE_APPLY_SYNC, /**< state fully processed */
+ DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */
+ DRM_STATE_TEST_ONLY, /**< test if the state can be applied */
+};
+
+/**
+ * Get the current value of a KMS property
+ *
+ * Given a drmModeObjectGetProperties return, as well as the drm_property_info
+ * for the target property, return the current value of that property,
+ * with an optional default. If the property is a KMS enum type, the return
+ * value will be translated into the appropriate internal enum.
+ *
+ * If the property is not present, the default value will be returned.
+ *
+ * @param info Internal structure for property to look up
+ * @param props Raw KMS properties for the target object
+ * @param def Value to return if property is not found
+ */
+uint64_t
+drm_property_get_value(struct drm_property_info *info,
+ const drmModeObjectProperties *props,
+ uint64_t def)
+{
+ unsigned int i;
+
+ if (info->prop_id == 0)
+ return def;
+
+ for (i = 0; i < props->count_props; i++) {
+ unsigned int j;
+
+ if (props->props[i] != info->prop_id)
+ continue;
+
+ /* Simple (non-enum) types can return the value directly */
+ if (info->num_enum_values == 0)
+ return props->prop_values[i];
+
+ /* Map from raw value to enum value */
+ for (j = 0; j < info->num_enum_values; j++) {
+ if (!info->enum_values[j].valid)
+ continue;
+ if (info->enum_values[j].value != props->prop_values[i])
+ continue;
+
+ return j;
+ }
+
+ /* We don't have a mapping for this enum; return default. */
+ break;
+ }
+
+ return def;
+}
+
+/**
+ * Cache DRM property values
+ *
+ * Update a per-object array of drm_property_info structures, given the
+ * DRM properties of the object.
+ *
+ * Call this every time an object newly appears (note that only connectors
+ * can be hotplugged), the first time it is seen, or when its status changes
+ * in a way which invalidates the potential property values (currently, the
+ * only case for this is connector hotplug).
+ *
+ * This updates the property IDs and enum values within the drm_property_info
+ * array.
+ *
+ * DRM property enum values are dynamic at runtime; the user must query the
+ * property to find out the desired runtime value for a requested string
+ * name. Using the 'type' field on planes as an example, there is no single
+ * hardcoded constant for primary plane types; instead, the property must be
+ * queried at runtime to find the value associated with the string "Primary".
+ *
+ * This helper queries and caches the enum values, to allow us to use a set
+ * of compile-time-constant enums portably across various implementations.
+ * The values given in enum_names are searched for, and stored in the
+ * same-indexed field of the map array.
+ *
+ * @param b DRM backend object
+ * @param src DRM property info array to source from
+ * @param info DRM property info array to copy into
+ * @param num_infos Number of entries in the source array
+ * @param props DRM object properties for the object
+ */
+void
+drm_property_info_populate(struct drm_backend *b,
+ const struct drm_property_info *src,
+ struct drm_property_info *info,
+ unsigned int num_infos,
+ drmModeObjectProperties *props)
+{
+ drmModePropertyRes *prop;
+ unsigned i, j;
+
+ for (i = 0; i < num_infos; i++) {
+ unsigned int j;
+
+ info[i].name = src[i].name;
+ info[i].prop_id = 0;
+ info[i].num_enum_values = src[i].num_enum_values;
+
+ if (src[i].num_enum_values == 0)
+ continue;
+
+ info[i].enum_values =
+ malloc(src[i].num_enum_values *
+ sizeof(*info[i].enum_values));
+ assert(info[i].enum_values);
+ for (j = 0; j < info[i].num_enum_values; j++) {
+ info[i].enum_values[j].name = src[i].enum_values[j].name;
+ info[i].enum_values[j].valid = false;
+ }
+ }
+
+ for (i = 0; i < props->count_props; i++) {
+ unsigned int k;
+
+ prop = drmModeGetProperty(b->drm.fd, props->props[i]);
+ if (!prop)
+ continue;
+
+ for (j = 0; j < num_infos; j++) {
+ if (!strcmp(prop->name, info[j].name))
+ break;
+ }
+
+ /* We don't know/care about this property. */
+ if (j == num_infos) {
+#ifdef DEBUG
+ weston_log("DRM debug: unrecognized property %u '%s'\n",
+ prop->prop_id, prop->name);
+#endif
+ drmModeFreeProperty(prop);
+ continue;
+ }
+
+ if (info[j].num_enum_values == 0 &&
+ (prop->flags & DRM_MODE_PROP_ENUM)) {
+ weston_log("DRM: expected property %s to not be an"
+ " enum, but it is; ignoring\n", prop->name);
+ drmModeFreeProperty(prop);
+ continue;
+ }
+
+ info[j].prop_id = props->props[i];
+
+ if (info[j].num_enum_values == 0) {
+ drmModeFreeProperty(prop);
+ continue;
+ }
+
+ if (!(prop->flags & DRM_MODE_PROP_ENUM)) {
+ weston_log("DRM: expected property %s to be an enum,"
+ " but it is not; ignoring\n", prop->name);
+ drmModeFreeProperty(prop);
+ info[j].prop_id = 0;
+ continue;
+ }
+
+ for (k = 0; k < info[j].num_enum_values; k++) {
+ int l;
+
+ for (l = 0; l < prop->count_enums; l++) {
+ if (!strcmp(prop->enums[l].name,
+ info[j].enum_values[k].name))
+ break;
+ }
+
+ if (l == prop->count_enums)
+ continue;
+
+ info[j].enum_values[k].valid = true;
+ info[j].enum_values[k].value = prop->enums[l].value;
+ }
+
+ drmModeFreeProperty(prop);
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < num_infos; i++) {
+ if (info[i].prop_id == 0)
+ weston_log("DRM warning: property '%s' missing\n",
+ info[i].name);
+ }
+#endif
+}
+
+/**
+ * Free DRM property information
+ *
+ * Frees all memory associated with a DRM property info array and zeroes
+ * it out, leaving it usable for a further drm_property_info_update() or
+ * drm_property_info_free().
+ *
+ * @param info DRM property info array
+ * @param num_props Number of entries in array to free
+ */
+void
+drm_property_info_free(struct drm_property_info *info, int num_props)
+{
+ int i;
+
+ for (i = 0; i < num_props; i++)
+ free(info[i].enum_values);
+
+ memset(info, 0, sizeof(*info) * num_props);
+}
+
+#ifdef HAVE_DRM_FORMATS_BLOB
+static inline uint32_t *
+formats_ptr(struct drm_format_modifier_blob *blob)
+{
+ return (uint32_t *)(((char *)blob) + blob->formats_offset);
+}
+
+static inline struct drm_format_modifier *
+modifiers_ptr(struct drm_format_modifier_blob *blob)
+{
+ return (struct drm_format_modifier *)
+ (((char *)blob) + blob->modifiers_offset);
+}
+#endif
+
+/**
+ * Populates the plane's formats array, using either the IN_FORMATS blob
+ * property (if available), or the plane's format list if not.
+ */
+int
+drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
+ const drmModeObjectProperties *props)
+{
+ unsigned i;
+#ifdef HAVE_DRM_FORMATS_BLOB
+ drmModePropertyBlobRes *blob;
+ struct drm_format_modifier_blob *fmt_mod_blob;
+ struct drm_format_modifier *blob_modifiers;
+ uint32_t *blob_formats;
+ uint32_t blob_id;
+
+ blob_id = drm_property_get_value(&plane->props[WDRM_PLANE_IN_FORMATS],
+ props,
+ 0);
+ if (blob_id == 0)
+ goto fallback;
+
+ blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
+ if (!blob)
+ goto fallback;
+
+ fmt_mod_blob = blob->data;
+ blob_formats = formats_ptr(fmt_mod_blob);
+ blob_modifiers = modifiers_ptr(fmt_mod_blob);
+
+ if (plane->count_formats != fmt_mod_blob->count_formats) {
+ weston_log("DRM backend: format count differs between "
+ "plane (%d) and IN_FORMATS (%d)\n",
+ plane->count_formats,
+ fmt_mod_blob->count_formats);
+ weston_log("This represents a kernel bug; Weston is "
+ "unable to continue.\n");
+ abort();
+ }
+
+ for (i = 0; i < fmt_mod_blob->count_formats; i++) {
+ uint32_t count_modifiers = 0;
+ uint64_t *modifiers = NULL;
+ unsigned j;
+
+ for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
+ struct drm_format_modifier *mod = &blob_modifiers[j];
+
+ if ((i < mod->offset) || (i > mod->offset + 63))
+ continue;
+ if (!(mod->formats & (1 << (i - mod->offset))))
+ continue;
+
+ modifiers = realloc(modifiers,
+ (count_modifiers + 1) *
+ sizeof(modifiers[0]));
+ assert(modifiers);
+ modifiers[count_modifiers++] = mod->modifier;
+ }
+
+ plane->formats[i].format = blob_formats[i];
+ plane->formats[i].modifiers = modifiers;
+ plane->formats[i].count_modifiers = count_modifiers;
+ }
+
+ drmModeFreePropertyBlob(blob);
+
+ return 0;
+
+fallback:
+#endif
+ /* No IN_FORMATS blob available, so just use the old. */
+ assert(plane->count_formats == kplane->count_formats);
+ for (i = 0; i < kplane->count_formats; i++)
+ plane->formats[i].format = kplane->formats[i];
+
+ return 0;
+}
+
+void
+drm_output_set_gamma(struct weston_output *output_base,
+ uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
+{
+ int rc;
+ struct drm_output *output = to_drm_output(output_base);
+ struct drm_backend *backend =
+ to_drm_backend(output->base.compositor);
+
+ /* check */
+ if (output_base->gamma_size != size)
+ return;
+
+ rc = drmModeCrtcSetGamma(backend->drm.fd,
+ output->crtc_id,
+ size, r, g, b);
+ if (rc)
+ weston_log("set gamma failed: %s\n", strerror(errno));
+}
+
+/**
+ * Mark an output state as current on the output, i.e. it has been
+ * submitted to the kernel. The mode argument determines whether this
+ * update will be applied synchronously (e.g. when calling drmModeSetCrtc),
+ * or asynchronously (in which case we wait for events to complete).
+ */
+static void
+drm_output_assign_state(struct drm_output_state *state,
+ enum drm_state_apply_mode mode)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct drm_plane_state *plane_state;
+
+ assert(!output->state_last);
+
+ if (mode == DRM_STATE_APPLY_ASYNC)
+ output->state_last = output->state_cur;
+ else
+ drm_output_state_free(output->state_cur);
+
+ wl_list_remove(&state->link);
+ wl_list_init(&state->link);
+ state->pending_state = NULL;
+
+ output->state_cur = state;
+
+ if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
+ drm_debug(b, "\t[CRTC:%u] setting pending flip\n", output->crtc_id);
+ output->atomic_complete_pending = 1;
+ }
+
+ /* Replace state_cur on each affected plane with the new state, being
+ * careful to dispose of orphaned (but only orphaned) previous state.
+ * If the previous state is not orphaned (still has an output_state
+ * attached), it will be disposed of by freeing the output_state. */
+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ struct drm_plane *plane = plane_state->plane;
+
+ if (plane->state_cur && !plane->state_cur->output_state)
+ drm_plane_state_free(plane->state_cur, true);
+ plane->state_cur = plane_state;
+
+ if (mode != DRM_STATE_APPLY_ASYNC) {
+ plane_state->complete = true;
+ continue;
+ }
+
+ if (b->atomic_modeset)
+ continue;
+
+ assert(plane->type != WDRM_PLANE_TYPE_OVERLAY);
+ if (plane->type == WDRM_PLANE_TYPE_PRIMARY)
+ output->page_flip_pending = 1;
+ }
+}
+
+static void
+drm_output_set_cursor(struct drm_output_state *output_state)
+{
+ struct drm_output *output = output_state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct drm_plane *plane = output->cursor_plane;
+ struct drm_plane_state *state;
+ int handle;
+ struct gbm_bo *bo;
+
+ if (!plane)
+ return;
+
+ state = drm_output_state_get_existing_plane(output_state, plane);
+ if (!state)
+ return;
+
+ if (!state->fb) {
+ pixman_region32_fini(&plane->base.damage);
+ pixman_region32_init(&plane->base.damage);
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ return;
+ }
+
+ assert(state->fb == output->gbm_cursor_fb[output->current_cursor]);
+ assert(!plane->state_cur->output || plane->state_cur->output == output);
+
+ if (plane->state_cur->fb != state->fb) {
+ bo = state->fb->bo;
+ handle = gbm_bo_get_handle(bo).s32;
+ if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle,
+ b->cursor_width, b->cursor_height)) {
+ weston_log("failed to set cursor: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ }
+
+ pixman_region32_fini(&plane->base.damage);
+ pixman_region32_init(&plane->base.damage);
+
+ if (drmModeMoveCursor(b->drm.fd, output->crtc_id,
+ state->dest_x, state->dest_y)) {
+ weston_log("failed to move cursor: %s\n", strerror(errno));
+ goto err;
+ }
+
+ return;
+
+err:
+ b->cursors_are_broken = 1;
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+}
+
+static int
+drm_output_apply_state_legacy(struct drm_output_state *state)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ struct drm_plane *scanout_plane = output->scanout_plane;
+ struct drm_property_info *dpms_prop;
+ struct drm_plane_state *scanout_state;
+ struct drm_mode *mode;
+ struct drm_head *head;
+ const struct pixel_format_info *pinfo = NULL;
+ uint32_t connectors[MAX_CLONED_CONNECTORS];
+ int n_conn = 0;
+ struct timespec now;
+ int ret = 0;
+
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ assert(n_conn < MAX_CLONED_CONNECTORS);
+ connectors[n_conn++] = head->connector_id;
+ }
+
+ /* If disable_planes is set then assign_planes() wasn't
+ * called for this render, so we could still have a stale
+ * cursor plane set up.
+ */
+ if (output->base.disable_planes) {
+ output->cursor_view = NULL;
+ if (output->cursor_plane) {
+ output->cursor_plane->base.x = INT32_MIN;
+ output->cursor_plane->base.y = INT32_MIN;
+ }
+ }
+
+ if (state->dpms != WESTON_DPMS_ON) {
+ if (output->cursor_plane) {
+ ret = drmModeSetCursor(backend->drm.fd, output->crtc_id,
+ 0, 0, 0);
+ if (ret)
+ weston_log("drmModeSetCursor failed disable: %s\n",
+ strerror(errno));
+ }
+
+ ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
+ NULL, 0, NULL);
+ if (ret)
+ weston_log("drmModeSetCrtc failed disabling: %s\n",
+ strerror(errno));
+
+ drm_output_assign_state(state, DRM_STATE_APPLY_SYNC);
+ weston_compositor_read_presentation_clock(output->base.compositor, &now);
+ drm_output_update_complete(output,
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION,
+ now.tv_sec, now.tv_nsec / 1000);
+
+ return 0;
+ }
+
+ scanout_state =
+ drm_output_state_get_existing_plane(state, scanout_plane);
+
+ /* The legacy SetCrtc API doesn't allow us to do scaling, and the
+ * legacy PageFlip API doesn't allow us to do clipping either. */
+ assert(scanout_state->src_x == 0);
+ assert(scanout_state->src_y == 0);
+ assert(scanout_state->src_w ==
+ (unsigned) (output->base.current_mode->width << 16));
+ assert(scanout_state->src_h ==
+ (unsigned) (output->base.current_mode->height << 16));
+ assert(scanout_state->dest_x == 0);
+ assert(scanout_state->dest_y == 0);
+ assert(scanout_state->dest_w == scanout_state->src_w >> 16);
+ assert(scanout_state->dest_h == scanout_state->src_h >> 16);
+ /* The legacy SetCrtc API doesn't support fences */
+ assert(scanout_state->in_fence_fd == -1);
+
+ mode = to_drm_mode(output->base.current_mode);
+ if (backend->state_invalid ||
+ !scanout_plane->state_cur->fb ||
+ scanout_plane->state_cur->fb->strides[0] !=
+ scanout_state->fb->strides[0]) {
+
+ ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
+ scanout_state->fb->fb_id,
+ 0, 0,
+ connectors, n_conn,
+ &mode->mode_info);
+ if (ret) {
+ weston_log("set mode failed: %s\n", strerror(errno));
+ goto err;
+ }
+ }
+
+ pinfo = scanout_state->fb->format;
+ drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n",
+ output->crtc_id, scanout_state->plane->plane_id,
+ pinfo ? pinfo->drm_format_name : "UNKNOWN");
+
+ if (drmModePageFlip(backend->drm.fd, output->crtc_id,
+ scanout_state->fb->fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
+ weston_log("queueing pageflip failed: %s\n", strerror(errno));
+ goto err;
+ }
+
+ assert(!output->page_flip_pending);
+
+ if (output->pageflip_timer)
+ wl_event_source_timer_update(output->pageflip_timer,
+ backend->pageflip_timeout);
+
+ drm_output_set_cursor(state);
+
+ if (state->dpms != output->state_cur->dpms) {
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ dpms_prop = &head->props_conn[WDRM_CONNECTOR_DPMS];
+ if (dpms_prop->prop_id == 0)
+ continue;
+
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+ head->connector_id,
+ dpms_prop->prop_id,
+ state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for %s\n",
+ head->base.name);
+ }
+ }
+ }
+
+ drm_output_assign_state(state, DRM_STATE_APPLY_ASYNC);
+
+ return 0;
+
+err:
+ output->cursor_view = NULL;
+ drm_output_state_free(state);
+ return -1;
+}
+
+#ifdef HAVE_DRM_ATOMIC
+static int
+crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_crtc_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &output->props_crtc[prop];
+ int ret;
+
+ if (info->prop_id == 0)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->crtc_id, info->prop_id,
+ val);
+ drm_debug(output->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
+ (unsigned long) output->crtc_id,
+ (unsigned long) info->prop_id, info->name,
+ (unsigned long long) val, (unsigned long long) val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+connector_add_prop(drmModeAtomicReq *req, struct drm_head *head,
+ enum wdrm_connector_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &head->props_conn[prop];
+ int ret;
+
+ if (info->prop_id == 0)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, head->connector_id,
+ info->prop_id, val);
+ drm_debug(head->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
+ (unsigned long) head->connector_id,
+ (unsigned long) info->prop_id, info->name,
+ (unsigned long long) val, (unsigned long long) val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
+ enum wdrm_plane_property prop, uint64_t val)
+{
+ struct drm_property_info *info = &plane->props[prop];
+ int ret;
+
+ if (info->prop_id == 0)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
+ val);
+ drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
+ (unsigned long) plane->plane_id,
+ (unsigned long) info->prop_id, info->name,
+ (unsigned long long) val, (unsigned long long) val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+
+static int
+plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend,
+ struct drm_plane_state *plane_state)
+{
+ struct drm_plane *plane = plane_state->plane;
+ struct drm_property_info *info =
+ &plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS];
+ pixman_box32_t *rects;
+ uint32_t blob_id;
+ int n_rects;
+ int ret;
+
+ if (!pixman_region32_not_empty(&plane_state->damage))
+ return 0;
+
+ /*
+ * If a plane doesn't support fb damage blob property, kernel will
+ * perform full plane update.
+ */
+ if (info->prop_id == 0)
+ return 0;
+
+ rects = pixman_region32_rectangles(&plane_state->damage, &n_rects);
+
+ ret = drmModeCreatePropertyBlob(backend->drm.fd, rects,
+ sizeof(*rects) * n_rects, &blob_id);
+ if (ret != 0)
+ return ret;
+
+ ret = plane_add_prop(req, plane, WDRM_PLANE_FB_DAMAGE_CLIPS, blob_id);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+drm_output_apply_state_atomic(struct drm_output_state *state,
+ drmModeAtomicReq *req,
+ uint32_t *flags)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ struct drm_plane_state *plane_state;
+ struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
+ struct drm_head *head;
+ int ret = 0;
+
+ drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
+ (*flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "testing" : "applying",
+ (unsigned long) output->base.id, output->base.name);
+
+ if (state->dpms != output->state_cur->dpms) {
+ drm_debug(b, "\t\t\t[atomic] DPMS state differs, modeset OK\n");
+ *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ }
+
+ if (state->dpms == WESTON_DPMS_ON) {
+ ret = drm_mode_ensure_blob(b, current_mode);
+ if (ret != 0)
+ return ret;
+
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
+ current_mode->blob_id);
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
+
+ /* No need for the DPMS property, since it is implicit in
+ * routing and CRTC activity. */
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID,
+ output->crtc_id);
+ }
+ } else {
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
+
+ /* No need for the DPMS property, since it is implicit in
+ * routing and CRTC activity. */
+ wl_list_for_each(head, &output->base.head_list, base.output_link)
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
+ }
+
+ if (ret != 0) {
+ weston_log("couldn't set atomic CRTC/connector state\n");
+ return ret;
+ }
+
+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ struct drm_plane *plane = plane_state->plane;
+ const struct pixel_format_info *pinfo = NULL;
+
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_ID,
+ plane_state->fb ? plane_state->fb->fb_id : 0);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID,
+ plane_state->fb ? output->crtc_id : 0);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_X,
+ plane_state->src_x);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_Y,
+ plane_state->src_y);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_W,
+ plane_state->src_w);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_H,
+ plane_state->src_h);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_X,
+ plane_state->dest_x);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_Y,
+ plane_state->dest_y);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_W,
+ plane_state->dest_w);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_H,
+ plane_state->dest_h);
+ ret |= plane_add_damage(req, b, plane_state);
+
+ if (plane_state->fb && plane_state->fb->format)
+ pinfo = plane_state->fb->format;
+
+ drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
+ (unsigned long) plane->plane_id,
+ pinfo ? pinfo->drm_format_name : "UNKNOWN");
+
+ if (plane_state->in_fence_fd >= 0) {
+ ret |= plane_add_prop(req, plane,
+ WDRM_PLANE_IN_FENCE_FD,
+ plane_state->in_fence_fd);
+ }
+
+ if (ret != 0) {
+ weston_log("couldn't set plane state\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Helper function used only by drm_pending_state_apply, with the same
+ * guarantees and constraints as that function.
+ */
+static int
+drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
+ enum drm_state_apply_mode mode)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ struct drm_plane *plane;
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+ uint32_t flags;
+ int ret = 0;
+
+ if (!req)
+ return -1;
+
+ switch (mode) {
+ case DRM_STATE_APPLY_SYNC:
+ flags = 0;
+ break;
+ case DRM_STATE_APPLY_ASYNC:
+ flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ break;
+ case DRM_STATE_TEST_ONLY:
+ flags = DRM_MODE_ATOMIC_TEST_ONLY;
+ break;
+ }
+
+ if (b->state_invalid) {
+ struct weston_head *head_base;
+ struct drm_head *head;
+ uint32_t *unused;
+ int err;
+
+ drm_debug(b, "\t\t[atomic] previous state invalid; "
+ "starting with fresh state\n");
+
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs and connectors we aren't using. */
+ wl_list_for_each(head_base,
+ &b->compositor->head_list, compositor_link) {
+ struct drm_property_info *info;
+
+ if (weston_head_is_enabled(head_base))
+ continue;
+
+ head = to_drm_head(head_base);
+
+ drm_debug(b, "\t\t[atomic] disabling inactive head %s\n",
+ head_base->name);
+
+ info = &head->props_conn[WDRM_CONNECTOR_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, head->connector_id,
+ info->prop_id, 0);
+ drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> 0\n",
+ (unsigned long) head->connector_id,
+ (unsigned long) info->prop_id,
+ info->name);
+ if (err <= 0)
+ ret = -1;
+ }
+
+ wl_array_for_each(unused, &b->unused_crtcs) {
+ struct drm_property_info infos[WDRM_CRTC__COUNT];
+ struct drm_property_info *info;
+ drmModeObjectProperties *props;
+ uint64_t active;
+
+ memset(infos, 0, sizeof(infos));
+
+ /* We can't emit a disable on a CRTC that's already
+ * off, as the kernel will refuse to generate an event
+ * for an off->off state and fail the commit.
+ */
+ props = drmModeObjectGetProperties(b->drm.fd,
+ *unused,
+ DRM_MODE_OBJECT_CRTC);
+ if (!props) {
+ ret = -1;
+ continue;
+ }
+
+ drm_property_info_populate(b, crtc_props, infos,
+ WDRM_CRTC__COUNT,
+ props);
+
+ info = &infos[WDRM_CRTC_ACTIVE];
+ active = drm_property_get_value(info, props, 0);
+ drmModeFreeObjectProperties(props);
+ if (active == 0) {
+ drm_property_info_free(infos, WDRM_CRTC__COUNT);
+ continue;
+ }
+
+ drm_debug(b, "\t\t[atomic] disabling unused CRTC %lu\n",
+ (unsigned long) *unused);
+
+ drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> 0\n",
+ (unsigned long) *unused,
+ (unsigned long) info->prop_id, info->name);
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ info = &infos[WDRM_CRTC_MODE_ID];
+ drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> 0\n",
+ (unsigned long) *unused,
+ (unsigned long) info->prop_id, info->name);
+ err = drmModeAtomicAddProperty(req, *unused,
+ info->prop_id, 0);
+ if (err <= 0)
+ ret = -1;
+
+ drm_property_info_free(infos, WDRM_CRTC__COUNT);
+ }
+
+ /* Disable all the planes; planes which are being used will
+ * override this state in the output-state application. */
+ wl_list_for_each(plane, &b->plane_list, link) {
+ drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n",
+ (unsigned long) plane->plane_id);
+ plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0);
+ plane_add_prop(req, plane, WDRM_PLANE_FB_ID, 0);
+ }
+
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ }
+
+ wl_list_for_each(output_state, &pending_state->output_list, link) {
+ if (output_state->output->virtual)
+ continue;
+ if (mode == DRM_STATE_APPLY_SYNC)
+ assert(output_state->dpms == WESTON_DPMS_OFF);
+ ret |= drm_output_apply_state_atomic(output_state, req, &flags);
+ }
+
+ if (ret != 0) {
+ weston_log("atomic: couldn't compile atomic state\n");
+ goto out;
+ }
+
+ ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
+ drm_debug(b, "[atomic] drmModeAtomicCommit\n");
+
+ /* Test commits do not take ownership of the state; return
+ * without freeing here. */
+ if (mode == DRM_STATE_TEST_ONLY) {
+ drmModeAtomicFree(req);
+ return ret;
+ }
+
+ if (ret != 0) {
+ weston_log("atomic: couldn't commit new state: %s\n",
+ strerror(errno));
+ goto out;
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link)
+ drm_output_assign_state(output_state, mode);
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+
+out:
+ drmModeAtomicFree(req);
+ drm_pending_state_free(pending_state);
+ return ret;
+}
+#endif
+
+/**
+ * Tests a pending state, to see if the kernel will accept the update as
+ * constructed.
+ *
+ * Using atomic modesetting, the kernel performs the same checks as it would
+ * on a real commit, returning success or failure without actually modifying
+ * the running state. It does not return -EBUSY if there are pending updates
+ * in flight, so states may be tested at any point, however this means a
+ * state which passed testing may fail on a real commit if the timing is not
+ * respected (e.g. committing before the previous commit has completed).
+ *
+ * Without atomic modesetting, we have no way to check, so we optimistically
+ * claim it will work.
+ *
+ * Unlike drm_pending_state_apply() and drm_pending_state_apply_sync(), this
+ * function does _not_ take ownership of pending_state, nor does it clear
+ * state_invalid.
+ */
+int
+drm_pending_state_test(struct drm_pending_state *pending_state)
+{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = pending_state->backend;
+
+ if (b->atomic_modeset)
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_STATE_TEST_ONLY);
+#endif
+
+ /* We have no way to test state before application on the legacy
+ * modesetting API, so just claim it succeeded. */
+ return 0;
+}
+
+/**
+ * Applies all of a pending_state asynchronously: the primary entry point for
+ * applying KMS state to a device. Updates the state for all outputs in the
+ * pending_state, as well as disabling any unclaimed outputs.
+ *
+ * Unconditionally takes ownership of pending_state, and clears state_invalid.
+ */
+int
+drm_pending_state_apply(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ uint32_t *unused;
+
+#ifdef HAVE_DRM_ATOMIC
+ if (b->atomic_modeset)
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_STATE_APPLY_ASYNC);
+#endif
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs we aren't using. This also disables
+ * all connectors on these CRTCs, so we don't need to do that
+ * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
+ NULL);
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link) {
+ struct drm_output *output = output_state->output;
+ int ret;
+
+ if (output->virtual) {
+ drm_output_assign_state(output_state,
+ DRM_STATE_APPLY_ASYNC);
+ continue;
+ }
+
+ ret = drm_output_apply_state_legacy(output_state);
+ if (ret != 0) {
+ weston_log("Couldn't apply state for output %s\n",
+ output->base.name);
+ }
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+
+ drm_pending_state_free(pending_state);
+
+ return 0;
+}
+
+/**
+ * The synchronous version of drm_pending_state_apply. May only be used to
+ * disable outputs. Does so synchronously: the request is guaranteed to have
+ * completed on return, and the output will not be touched afterwards.
+ *
+ * Unconditionally takes ownership of pending_state, and clears state_invalid.
+ */
+int
+drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
+{
+ struct drm_backend *b = pending_state->backend;
+ struct drm_output_state *output_state, *tmp;
+ uint32_t *unused;
+
+#ifdef HAVE_DRM_ATOMIC
+ if (b->atomic_modeset)
+ return drm_pending_state_apply_atomic(pending_state,
+ DRM_STATE_APPLY_SYNC);
+#endif
+
+ if (b->state_invalid) {
+ /* If we need to reset all our state (e.g. because we've
+ * just started, or just been VT-switched in), explicitly
+ * disable all the CRTCs we aren't using. This also disables
+ * all connectors on these CRTCs, so we don't need to do that
+ * separately with the pre-atomic API. */
+ wl_array_for_each(unused, &b->unused_crtcs)
+ drmModeSetCrtc(b->drm.fd, *unused, 0, 0, 0, NULL, 0,
+ NULL);
+ }
+
+ wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
+ link) {
+ int ret;
+
+ assert(output_state->dpms == WESTON_DPMS_OFF);
+ ret = drm_output_apply_state_legacy(output_state);
+ if (ret != 0) {
+ weston_log("Couldn't apply state for output %s\n",
+ output_state->output->base.name);
+ }
+ }
+
+ b->state_invalid = false;
+
+ assert(wl_list_empty(&pending_state->output_list));
+
+ drm_pending_state_free(pending_state);
+
+ return 0;
+}
+
+void
+drm_output_update_msc(struct drm_output *output, unsigned int seq)
+{
+ uint64_t msc_hi = output->base.msc >> 32;
+
+ if (seq < (output->base.msc & 0xffffffff))
+ msc_hi++;
+
+ output->base.msc = (msc_hi << 32) + seq;
+}
+
+static void
+page_flip_handler(int fd, unsigned int frame,
+ unsigned int sec, unsigned int usec, void *data)
+{
+ struct drm_output *output = data;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+ drm_output_update_msc(output, frame);
+
+ assert(!b->atomic_modeset);
+ assert(output->page_flip_pending);
+ output->page_flip_pending = 0;
+
+ drm_output_update_complete(output, flags, sec, usec);
+}
+
+#ifdef HAVE_DRM_ATOMIC
+static void
+atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, unsigned int crtc_id, void *data)
+{
+ struct drm_backend *b = data;
+ struct drm_output *output = drm_output_find_by_crtc(b, crtc_id);
+ uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+ /* During the initial modeset, we can disable CRTCs which we don't
+ * actually handle during normal operation; this will give us events
+ * for unknown outputs. Ignore them. */
+ if (!output || !output->base.enabled)
+ return;
+
+ drm_output_update_msc(output, frame);
+
+ drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
+ assert(b->atomic_modeset);
+ assert(output->atomic_complete_pending);
+ output->atomic_complete_pending = 0;
+
+ drm_output_update_complete(output, flags, sec, usec);
+ drm_debug(b, "[atomic][CRTC:%u] flip processing completed\n", crtc_id);
+}
+#endif
+
+int
+on_drm_input(int fd, uint32_t mask, void *data)
+{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = data;
+#endif
+ drmEventContext evctx;
+
+ memset(&evctx, 0, sizeof evctx);
+#ifndef HAVE_DRM_ATOMIC
+ evctx.version = 2;
+#else
+ evctx.version = 3;
+ if (b->atomic_modeset)
+ evctx.page_flip_handler2 = atomic_flip_handler;
+ else
+#endif
+ evctx.page_flip_handler = page_flip_handler;
+ drmHandleEvent(fd, &evctx);
+
+ return 1;
+}
+
+int
+init_kms_caps(struct drm_backend *b)
+{
+ uint64_t cap;
+ int ret;
+ clockid_t clk_id;
+
+ weston_log("using %s\n", b->drm.filename);
+
+ ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
+ if (ret == 0 && cap == 1)
+ clk_id = CLOCK_MONOTONIC;
+ else
+ clk_id = CLOCK_REALTIME;
+
+ if (weston_compositor_set_presentation_clock(b->compositor, clk_id) < 0) {
+ weston_log("Error: failed to set presentation clock %d.\n",
+ clk_id);
+ return -1;
+ }
+
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
+ if (ret == 0)
+ b->cursor_width = cap;
+ else
+ b->cursor_width = 64;
+
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
+ if (ret == 0)
+ b->cursor_height = cap;
+ else
+ b->cursor_height = 64;
+
+ if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ b->universal_planes = (ret == 0);
+ }
+ weston_log("DRM: %s universal planes\n",
+ b->universal_planes ? "supports" : "does not support");
+
+#ifdef HAVE_DRM_ATOMIC
+ if (b->universal_planes && !getenv("WESTON_DISABLE_ATOMIC")) {
+ ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
+ if (ret != 0)
+ cap = 0;
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ b->atomic_modeset = ((ret == 0) && (cap == 1));
+ }
+#endif
+ weston_log("DRM: %s atomic modesetting\n",
+ b->atomic_modeset ? "supports" : "does not support");
+
+#ifdef HAVE_DRM_ADDFB2_MODIFIERS
+ ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
+ if (ret == 0)
+ b->fb_modifiers = cap;
+ else
+#endif
+ b->fb_modifiers = 0;
+
+ /*
+ * KMS support for hardware planes cannot properly synchronize
+ * without nuclear page flip. Without nuclear/atomic, hw plane
+ * and cursor plane updates would either tear or cause extra
+ * waits for vblanks which means dropping the compositor framerate
+ * to a fraction. For cursors, it's not so bad, so they are
+ * enabled.
+ */
+ if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
+ b->sprites_are_broken = 1;
+
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
+ b->aspect_ratio_supported = (ret == 0);
+ weston_log("DRM: %s picture aspect ratio\n",
+ b->aspect_ratio_supported ? "supports" : "does not support");
+
+ return 0;
+}