From 745a87347e7abc281204f4af9d5d7d891bf81c77 Mon Sep 17 00:00:00 2001 From: Nelson Liu Date: Wed, 7 Sep 2016 19:27:39 +0800 Subject: [PATCH 07/19] atomic: weston support atomic mode weston support atomic and overlay Test: test ok Change-Id: I1cc9e75d4040ac32c8f70f3562760eddfbe9727f Signed-off-by: Nelson Liu CR-Id: AUTO00000252 --- src/compositor-drm.c | 1937 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 1674 insertions(+), 263 deletions(-) diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 2016232..84bffc5 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -59,6 +59,8 @@ #include "presentation-time-server-protocol.h" #include "linux-dmabuf.h" +#define ATOMIC_DEBUG getenv("ATOMIC_DEBUG") + #ifndef DRM_CAP_TIMESTAMP_MONOTONIC #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 #endif @@ -75,6 +77,88 @@ #define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64 #endif +/** + * List of properties attached to DRM planes + */ +enum wdrm_plane_property { + WDRM_PLANE_TYPE = 0, + WDRM_PLANE_SRC_X, + WDRM_PLANE_SRC_Y, + WDRM_PLANE_SRC_W, + WDRM_PLANE_SRC_H, + WDRM_PLANE_CRTC_X, + WDRM_PLANE_CRTC_Y, + WDRM_PLANE_CRTC_W, + WDRM_PLANE_CRTC_H, + WDRM_PLANE_FB_ID, + WDRM_PLANE_CRTC_ID, + WDRM_PLANE__COUNT +}; + +/** + * Possible values for the WDRM_PLANE_TYPE property. + */ +enum wdrm_plane_type { + WDRM_PLANE_TYPE_PRIMARY = 0, + WDRM_PLANE_TYPE_OVERLAY, + WDRM_PLANE_TYPE_CURSOR, + WDRM_PLANE_TYPE__COUNT +}; + +/** + * List of properties attached to DRM CRTCs + */ +enum wdrm_crtc_property { + WDRM_CRTC_MODE_ID = 0, + WDRM_CRTC_ACTIVE, + WDRM_CRTC__COUNT +}; + +/** + * List of properties attached to DRM connectors + */ +enum wdrm_connector_property { + WDRM_CONNECTOR_CRTC_ID = 0, + WDRM_CONNECTOR__COUNT +}; + +/** + * One property attached to a DRM mode object (plane, CRTC, connector). + */ +struct property_item { + drmModePropertyRes *drm_prop; + uint32_t id; + uint64_t value; +}; + +/** + * Holding structure for plane properties. + */ +struct plane_properties { + struct property_item item[WDRM_PLANE__COUNT]; + uint64_t typemap[WDRM_PLANE_TYPE__COUNT]; /**< map for type enum */ + uint32_t value_valid_mask; + uint32_t value_pend_mask; +}; + +/** + * Holding structure for CRTC properties. + */ +struct crtc_properties { + struct property_item item[WDRM_CRTC__COUNT]; + uint32_t value_valid_mask; + uint32_t value_pend_mask; +}; + +/** + * Holding structure for CRTC properties. + */ +struct connector_properties { + struct property_item item[WDRM_CONNECTOR__COUNT]; + uint32_t value_valid_mask; + uint32_t value_pend_mask; +}; + struct drm_backend { struct weston_backend base; struct weston_compositor *compositor; @@ -106,12 +190,15 @@ struct drm_backend { uint32_t min_height, max_height; int no_addfb2; - struct wl_list sprite_list; + struct wl_list plane_list; int sprites_are_broken; int sprites_hidden; int cursors_are_broken; + bool universal_planes; + bool atomic_modeset; + int use_pixman; uint32_t prev_state; @@ -137,6 +224,7 @@ struct drm_backend { struct drm_mode { struct weston_mode base; drmModeModeInfo mode_info; + uint32_t blob_id; }; struct drm_fb { @@ -159,6 +247,59 @@ struct drm_edid { char serial_number[13]; }; +enum plane_property { + PLANE_PROP_ROTATE = 0, + PLANE_PROP_ALPHA, + PLANE_PROP_COLORKEY, + PLANE_PROP_ZPOS, + PLANE_PROP_MAX, +}; + +/** + * A plane represents one buffer, positioned within a CRTC, and stacked + * relative to other planes on the same CRTC. + * + * Each CRTC has a 'primary plane', which use used to display the classic + * framebuffer contents, as accessed through the legacy drmModeSetCrtc + * call (which combines setting the CRTC's actual physical mode, and the + * properties of the primary plane). + * + * The cursor plane also has its own alternate legacy API. + * + * Other planes are used opportunistically to display content we do not + * wish to blit into the primary plane. These non-primary/cursor planes + * are referred to as 'sprites'. + */ +struct drm_plane { + struct wl_list link; + + struct weston_plane plane; + + enum wdrm_plane_type type; + + struct weston_view *view; + struct wl_listener view_destroy; + + struct drm_fb *current, *next; + struct drm_output *output; + struct drm_backend *backend; + + struct plane_properties props; + + uint32_t possible_crtcs; + uint32_t plane_id; + uint32_t count_formats; + + int32_t src_x, src_y; + uint32_t src_w, src_h; + uint32_t dest_x, dest_y; + uint32_t dest_w, dest_h; + + struct wl_list flip_link; /* drm_output::plane_flip_list */ + + uint32_t formats[]; +}; + struct drm_output { struct weston_output base; @@ -172,17 +313,20 @@ struct drm_output { enum dpms_enum dpms; + struct crtc_properties props_crtc; + struct connector_properties props_conn; + int vblank_pending; int page_flip_pending; int destroy_pending; struct gbm_surface *gbm_surface; struct gbm_bo *gbm_cursor_bo[2]; - struct weston_plane cursor_plane; + struct drm_plane *cursor_plane; struct weston_plane fb_plane; + struct drm_plane *primary_plane; struct weston_view *cursor_view; int current_cursor; - struct drm_fb *current, *next; struct backlight *backlight; struct drm_fb *dumb[2]; @@ -192,36 +336,605 @@ struct drm_output { struct vaapi_recorder *recorder; struct wl_listener recorder_frame_listener; + + struct wl_list plane_flip_list; /* drm_plane::flip_link */ }; -/* - * An output has a primary display plane plus zero or more sprites for - * blending display contents. +static struct gl_renderer_interface *gl_renderer; + +static const char default_seat[] = "seat0"; +/** + * Return a string describing the type of a DRM object */ -struct drm_sprite { - struct wl_list link; +static const char * +drm_object_type_str(uint32_t obj_type) +{ + switch (obj_type) { + case DRM_MODE_OBJECT_CRTC: return "crtc"; + case DRM_MODE_OBJECT_CONNECTOR: return "connector"; + case DRM_MODE_OBJECT_ENCODER: return "encoder"; + case DRM_MODE_OBJECT_MODE: return "mode"; + case DRM_MODE_OBJECT_PROPERTY: return "property"; + case DRM_MODE_OBJECT_FB: return "fb"; + case DRM_MODE_OBJECT_BLOB: return "blob"; + case DRM_MODE_OBJECT_PLANE: return "plane"; + default: return "???"; + } +} - struct weston_plane plane; +/** + * Cache a mapping from static values to a DRM property enum + * + * 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. + * + * For example, if the DRM driver exposes 'Foo' as value 7 and 'Bar' as value + * 19, passing in enum_names containing 'Foo' and 'Bar' in that order, will + * populate map with 7 and 19, in that order. + * + * This should always be used with static C enums to represent each value, e.g. + * WDRM_PLANE_MISC_FOO, WDRM_PLANE_MISC_BAR. + * + * Property lookup is not mandatory and may fail; users should carefully + * check the return value, which is a bitmask populated with the indices + * of properties which were successfully looked up. Values not successfully + * looked up may return 0, which may represent a false positive. + * + * @param prop DRM enum property to cache map for + * @param map Array for enum values; each entry is written for the corresponding + * entry in enum_names + * @param enum_names List of string values to look up enum values for + * @param nenums Length of enum_names and map arrays + * + * @returns Bitmask of populated entries in map + */ +static uint32_t +drm_property_get_enum_map(const struct property_item *item, uint64_t *map, + const char * const *enum_names, int nenums) +{ + drmModePropertyRes *prop = item->drm_prop; + uint32_t valid_mask = 0; + int seen = 0; + int i, j; - struct drm_fb *current, *next; - struct drm_output *output; - struct drm_backend *backend; + assert(nenums <= 32 && "update return type"); + memset(map, 0, nenums * sizeof(*map)); - uint32_t possible_crtcs; - uint32_t plane_id; - uint32_t count_formats; + if (!prop) { + weston_log("DRM warning: attempting to init property enum, " + "that was not found. (e.g. '%s')\n", + enum_names[0]); + return 0; + } - int32_t src_x, src_y; - uint32_t src_w, src_h; - uint32_t dest_x, dest_y; - uint32_t dest_w, dest_h; + if (!(prop->flags & DRM_MODE_PROP_ENUM) || prop->count_enums < 1) { + weston_log("DRM error: property %d '%s' is not an enum.\n", + prop->prop_id, prop->name); + return 0; + } - uint32_t formats[]; -}; + for (i = 0; i < prop->count_enums; i++) { + struct drm_mode_property_enum *en = &prop->enums[i]; -static struct gl_renderer_interface *gl_renderer; + for (j = 0; j < nenums; j++) { + if (!strcmp(en->name, enum_names[j])) + break; + } -static const char default_seat[] = "seat0"; + if (j == nenums) { + weston_log("DRM debug: property %d '%s' " + "has unrecognized enum %#" PRIx64 " '%s'\n", + prop->prop_id, prop->name, + (uint64_t)en->value, en->name); + break; + } + + map[j] = en->value; + valid_mask |= (1U << j); + seen++; + } + + if (seen != nenums) + weston_log("DRM debug: property %d '%s' has %u of %u " + "expected enum values.\n", + prop->prop_id, prop->name, seen, nenums); + + return valid_mask; +} + +/** + * Cache DRM property values + * + * Given a particular DRM object, find specified properties by name, and + * cache their internal property_item representation. Taking a list of + * names in prop_names, each corresponding entry in prop_items (matching + * index) will be populated with the corresponding DRM property. + * + * All users of DRM object properties in this file should use this + * mechanism. + * + * Property lookup is not mandatory and may fail; users should carefully + * check the return value, which is a bitmask populated with the indices + * of properties which were successfully looked up. + * + * @param ec Internal DRM compositor structure + * @param prop_items Array of internal property representations + * @param prop_names Array of property names to look up + * @param nprops Length of prop_items and prop_names arrays + * @param obj_id DRM object ID + * @param obj_type DRM object type (DRM_MODE_OBJECT_*) + * + * @returns Bitmask of populated entries in prop_items + */ +static uint32_t +drm_properties_get_from_obj(struct drm_backend *b, + struct property_item *prop_items, + const char * const *prop_names, + unsigned nprops, + uint32_t obj_id, uint32_t obj_type) +{ + drmModeObjectProperties *props; + drmModePropertyRes *prop; + uint32_t valid_mask = 0; + unsigned i, j; + + assert(nprops <= 32 && "update return type"); + + memset(prop_items, 0, nprops * sizeof *prop_items); + + props = drmModeObjectGetProperties(b->drm.fd, obj_id, obj_type); + if (!props) { + weston_log("DRM error : get properties for object %u " + "of type %#x '%s' failed.\n", obj_id, obj_type, + drm_object_type_str(obj_type)); + return valid_mask; + } + + for (i = 0; i < props->count_props; i++) { + prop = drmModeGetProperty(b->drm.fd, props->props[i]); + if (!prop) + continue; + + for (j = 0; j < nprops; j++) { + if (strcmp(prop->name, prop_names[j]) == 0) + break; + } + + if (j == nprops) { + weston_log("DRM debug: unrecognized property %u '%s' on" + " object %u of type %#x '%s'\n", + prop->prop_id, prop->name, + obj_id, obj_type, + drm_object_type_str(obj_type)); + drmModeFreeProperty(prop); + continue; + } + + assert(prop_items[j].drm_prop == NULL); + prop_items[j].drm_prop = prop; + prop_items[j].id = prop->prop_id; + prop_items[j].value = props->prop_values[i]; + valid_mask |= 1U << j; + } + + for (i = 0; i < nprops; i++) { + if (prop_items[i].drm_prop == NULL) + weston_log("DRM warning: property '%s' missing from obj" + " %u of type %#x '%s'\n", prop_names[i], + obj_id, obj_type, + drm_object_type_str(obj_type)); + } + + drmModeFreeObjectProperties(props); + + return valid_mask; +} + +/** + * Frees an array of property_items + * + * @param items Array to free + * @param len Number of items in array + */ +static void +property_item_array_release(struct property_item *items, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (items[i].drm_prop) + drmModeFreeProperty(items[i].drm_prop); + } +} + +/** + * Get one property from a DRM plane + * + * Retrieve the cached value of a property on a DRM plane. The property passed + * must be valid for the plane, i.e. must have been in the return value of + * drm_properties_get_from_obj. + * + * @param plane Plane to retrieve property for + * @param prop Property to retrieve + */ +static uint64_t +drm_plane_property_get(struct drm_plane *plane, enum wdrm_plane_property prop) +{ + assert(plane->props.value_valid_mask & (1U << prop)); + return plane->props.item[prop].value; +} + +static bool +plane_properties_init(struct drm_plane *plane) +{ + static const char * const plane_property_names[] = { + [WDRM_PLANE_TYPE] = "type", + [WDRM_PLANE_SRC_X] = "SRC_X", + [WDRM_PLANE_SRC_Y] = "SRC_Y", + [WDRM_PLANE_SRC_W] = "SRC_W", + [WDRM_PLANE_SRC_H] = "SRC_H", + [WDRM_PLANE_CRTC_X] = "CRTC_X", + [WDRM_PLANE_CRTC_Y] = "CRTC_Y", + [WDRM_PLANE_CRTC_W] = "CRTC_W", + [WDRM_PLANE_CRTC_H] = "CRTC_H", + [WDRM_PLANE_FB_ID] = "FB_ID", + [WDRM_PLANE_CRTC_ID] = "CRTC_ID", + }; + static const char * const plane_type_names[] = { + [WDRM_PLANE_TYPE_PRIMARY] = "Primary", + [WDRM_PLANE_TYPE_OVERLAY] = "Overlay", + [WDRM_PLANE_TYPE_CURSOR] = "Cursor", + }; + uint32_t type_mask; + uint32_t required_mask; + + static_assert(ARRAY_LENGTH(plane_property_names) == WDRM_PLANE__COUNT, + "plane_property_names mismatch with the enum"); + static_assert(ARRAY_LENGTH(plane_type_names) == WDRM_PLANE_TYPE__COUNT, + "plane_type_names mismatch with the enum"); + static_assert(WDRM_PLANE__COUNT <= 32, + "need more bits for plane item_valid_mask"); + + plane->props.value_valid_mask = + drm_properties_get_from_obj(plane->backend, + plane->props.item, + plane_property_names, + WDRM_PLANE__COUNT, + plane->plane_id, + DRM_MODE_OBJECT_PLANE); + + required_mask = 0; + if (plane->backend->universal_planes) { + required_mask |= 1 << WDRM_PLANE_TYPE; + if (plane->backend->atomic_modeset) { + required_mask |= + (1 << WDRM_PLANE_SRC_X) | (1 << WDRM_PLANE_SRC_Y) | \ + (1 << WDRM_PLANE_SRC_W) | (1 << WDRM_PLANE_SRC_H) | \ + (1 << WDRM_PLANE_CRTC_X) | (1 << WDRM_PLANE_CRTC_Y) | \ + (1 << WDRM_PLANE_CRTC_W) | (1 << WDRM_PLANE_CRTC_H) | \ + (1 << WDRM_PLANE_FB_ID) | (1 << WDRM_PLANE_CRTC_ID); + + if (plane->props.value_valid_mask != required_mask) { + weston_log("DRM error: failed to look up all plane properties " + "(wanted 0x%x got 0x%x) on ID %d\n", + required_mask, plane->props.value_valid_mask, + plane->plane_id); + return false; + } + } else { + if (!(plane->props.value_valid_mask & required_mask)) { + weston_log("DRM error: universal_planes failed to look up plane type " + "(wanted 0x%x got 0x%x) on ID %d\n", + required_mask, plane->props.value_valid_mask, + plane->plane_id); + return false; + } + } + } + /* Only the universal-plane enum below here. */ + if (!plane->backend->universal_planes) + return true; + + type_mask = + drm_property_get_enum_map(&plane->props.item[WDRM_PLANE_TYPE], + plane->props.typemap, + plane_type_names, + WDRM_PLANE_TYPE__COUNT); + + required_mask = 1 << WDRM_PLANE_TYPE_PRIMARY; + required_mask |= 1 << WDRM_PLANE_TYPE_OVERLAY; + required_mask |= 1 << WDRM_PLANE_TYPE_CURSOR; + if (type_mask != required_mask) { + weston_log("DRM error: failed to look up all plane type " + "enum members (wanted 0x%x got 0x%x) on ID %d\n", + required_mask, type_mask, plane->plane_id); + return false; + } + + return true; +} + +/** + * Initialise DRM properties for CRTC and connector + * + * Set up the holding structures to track DRM object properties set on the CRTC + * and connector associated with an output. + * Free the memory allocated here with output_properties_release. + * + * @param output Output to configure + */ +static bool output_properties_init(struct drm_output *output) +{ + struct drm_backend *b = + (struct drm_backend *) output->base.compositor->backend; + static const char * const crtc_property_names[] = { + [WDRM_CRTC_MODE_ID] = "MODE_ID", + [WDRM_CRTC_ACTIVE] = "ACTIVE", + }; + static const char * const connector_property_names[] = { + [WDRM_CONNECTOR_CRTC_ID] = "CRTC_ID", + }; + uint32_t required_mask; + + static_assert(ARRAY_LENGTH(crtc_property_names) == WDRM_CRTC__COUNT, + "crtc_property_names mismatch with the enum"); + static_assert(WDRM_CRTC__COUNT <= 32, + "need more bits for crtc item_valid_mask"); + + static_assert(ARRAY_LENGTH(connector_property_names) == WDRM_CONNECTOR__COUNT, + "connector_property_names mismatch with the enum"); + static_assert(WDRM_CONNECTOR__COUNT <= 32, + "need more bits for connector item_valid_mask"); + + output->props_crtc.value_valid_mask = + drm_properties_get_from_obj(b, + output->props_crtc.item, + crtc_property_names, + WDRM_CRTC__COUNT, + output->crtc_id, + DRM_MODE_OBJECT_CRTC); + + required_mask = 0; + if (b->atomic_modeset) { + required_mask |= (1 << WDRM_CRTC_MODE_ID) | \ + (1 << WDRM_CRTC_ACTIVE); + if (output->props_crtc.value_valid_mask != required_mask) { + weston_log("DRM error: failed to look up all CRTC properties " + "(wanted 0x%x got 0x%x) on ID %d\n", + required_mask, output->props_crtc.value_valid_mask, + output->crtc_id); + return false; + } + } + + output->props_conn.value_valid_mask = + drm_properties_get_from_obj(b, + output->props_conn.item, + connector_property_names, + WDRM_CONNECTOR__COUNT, + output->connector_id, + DRM_MODE_OBJECT_CONNECTOR); + required_mask = 0; + if (b->atomic_modeset) { + required_mask |= (1 << WDRM_CONNECTOR_CRTC_ID); + if (output->props_conn.value_valid_mask != required_mask) { + weston_log("DRM error: failed to look up all connector properties " + "(wanted 0x%x got 0x%x) on ID %d\n", + required_mask, output->props_conn.value_valid_mask, + output->connector_id); + return false; + } + } + + return true; +} + +/** + * Free DRM CRTC and connector properties + * + * The counterpart to output_properties_init. + * + * @param output Output to release properties for + */ +static void output_properties_release(struct drm_output *output) +{ + property_item_array_release(output->props_crtc.item, WDRM_CRTC__COUNT); + output->props_crtc.value_valid_mask = 0; + property_item_array_release(output->props_conn.item, + WDRM_CONNECTOR__COUNT); + output->props_conn.value_valid_mask = 0; +} + +static void +property_debug(const char *klass, uint32_t obj_id, + struct property_item *item, unsigned len, + unsigned en, uint64_t value) +{ + if (ATOMIC_DEBUG) { + uint32_t prop_id = len; + const char *prop_name = ""; + + if (en < len) { + prop_id = item[en].id; + if (item[en].drm_prop) + prop_name = item[en].drm_prop->name; + else + prop_name = ""; + } + + weston_log("DRM req %s id %u: prop[%u] %u '%s' = %" PRIu64 "\n", + klass, obj_id, en, prop_id, prop_name, value); + } +} + +static void +print_drm_mode_info(const drmModeModeInfo *m) +{ +#ifdef ATOMIC_DEBUG + weston_log_continue("mode '%s', px %u MHz, refresh %u\n", m->name, + m->clock, m->vrefresh); + weston_log_continue("horz: %u %u %u %u %u\n", m->hdisplay, + m->hsync_start, m->hsync_end, m->htotal, m->hskew); + weston_log_continue("vert: %u %u %u %u %u\n", m->vdisplay, + m->vsync_start, m->vsync_end, m->vtotal, m->vscan); + weston_log_continue("flags %#08x, type %#08x\n", m->flags, m->type); +#endif /* ATOMIC_DEBUG */ +} + +static void +plane_property_debug(struct drm_plane *plane, + enum wdrm_plane_property prop, uint64_t value) +{ + property_debug("plane", plane->plane_id, plane->props.item, + WDRM_PLANE__COUNT, prop, value); +} + +static void +connector_property_debug(struct drm_output *output, + enum wdrm_connector_property prop, uint64_t value) +{ + property_debug("connector", output->connector_id, + output->props_conn.item, WDRM_CONNECTOR__COUNT, + prop, value); +} + +static void +crtc_property_debug(struct drm_output *output, + enum wdrm_crtc_property prop, uint64_t value) +{ + property_debug("CRTC", output->crtc_id, output->props_crtc.item, + WDRM_CRTC__COUNT, prop, value); +} + +static void +drm_plane_update_begin(struct drm_plane *sprite) +{ + sprite->props.value_pend_mask = 0; +} + +static void +drm_plane_update_success(struct drm_plane *sprite) +{ + sprite->props.value_valid_mask |= sprite->props.value_pend_mask; +} + +static void +drm_crtc_update_begin(struct drm_output *output) +{ + output->props_crtc.value_pend_mask = 0; +} + +static void +drm_crtc_update_success(struct drm_output *output) +{ + output->props_crtc.value_valid_mask |= output->props_crtc.value_pend_mask; +} + +static void +drm_connector_update_begin(struct drm_output *output) +{ + output->props_conn.value_pend_mask = 0; +} + +static void +drm_connector_update_success(struct drm_output *output) +{ + output->props_conn.value_valid_mask |= output->props_conn.value_pend_mask; +} + +static int +atomic_plane_add(drmModeAtomicReq *req, struct drm_plane *plane, + enum wdrm_plane_property prop, uint64_t value) +{ + struct property_item *item = &plane->props.item[prop]; + uint32_t mask = 1U << prop; + + if (!item->id) + return -1; + + if ((plane->props.value_valid_mask | + plane->props.value_pend_mask) & mask && + item->value == value) + return 0; + + plane_property_debug(plane, prop, value); + + if (drmModeAtomicAddProperty(req, plane->plane_id, item->id, value) < 0) + return -1; + + plane->props.value_valid_mask &= ~mask; + item->value = value; + plane->props.value_pend_mask |= mask; + + return 0; +} + +static int +atomic_connector_add(drmModeAtomicReq *req, struct drm_output *output, + enum wdrm_connector_property prop, uint64_t value) +{ + struct property_item *item = &output->props_conn.item[prop]; + uint32_t mask = 1U << prop; + + if (!item->id) + return -1; + + if ((output->props_conn.value_valid_mask | + output->props_conn.value_pend_mask) & mask && + item->value == value) + return 0; + + connector_property_debug(output, prop, value); + + if (drmModeAtomicAddProperty(req, output->connector_id, item->id, + value) < 0) + return -1; + + output->props_conn.value_valid_mask &= ~mask; + item->value = value; + output->props_conn.value_pend_mask |= mask; + + return 0; +} + +static int +atomic_crtc_add(drmModeAtomicReq *req, struct drm_output *output, + enum wdrm_crtc_property prop, uint64_t value) +{ + struct property_item *item = &output->props_crtc.item[prop]; + uint32_t mask = 1U << prop; + + if (!item->id) + return -1; + + if ((output->props_crtc.value_valid_mask | + output->props_crtc.value_pend_mask) & mask && + item->value == value) + return 0; + + crtc_property_debug(output, prop, value); + + if (drmModeAtomicAddProperty(req, output->crtc_id, item->id, value) < 0) + return -1; + + output->props_crtc.value_valid_mask &= ~mask; + item->value = value; + output->props_crtc.value_pend_mask |= mask; + + return 0; +} + +static void +cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo, + struct weston_view *ev); static void drm_output_set_cursor(struct drm_output *output); @@ -230,7 +943,7 @@ static void drm_output_update_msc(struct drm_output *output, unsigned int seq); static int -drm_sprite_crtc_supported(struct drm_output *output, uint32_t supported) +drm_plane_crtc_supported(struct drm_output *output, uint32_t supported) { struct weston_compositor *ec = output->base.compositor; struct drm_backend *b =(struct drm_backend *)ec->backend; @@ -488,6 +1201,12 @@ drm_output_prepare_scanout_view(struct drm_output *output, if (ev->geometry.scissor_enabled) return NULL; + if (output->primary_plane->current && + output->primary_plane->current->buffer_ref.buffer == buffer) { + output->primary_plane->next = output->primary_plane->current; + goto out; + } + bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, buffer->resource, GBM_BO_USE_SCANOUT); @@ -501,15 +1220,16 @@ drm_output_prepare_scanout_view(struct drm_output *output, return NULL; } - output->next = drm_fb_get_from_bo(bo, b, format); - if (!output->next) { + output->primary_plane->next = drm_fb_get_from_bo(bo, b, format); + if (!output->primary_plane->next) { gbm_bo_destroy(bo); return NULL; } - drm_fb_set_buffer(output->next, buffer); + drm_fb_set_buffer(output->primary_plane->next, buffer); - return &output->fb_plane; +out: + return &output->primary_plane->plane; } static void @@ -519,6 +1239,14 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage) (struct drm_backend *)output->base.compositor->backend; struct gbm_bo *bo; + if (!pixman_region32_not_empty(damage) && + output->primary_plane->current) { + if (!output->primary_plane->next) + output->primary_plane->next = + output->primary_plane->current; + return; + } + output->base.compositor->renderer->repaint_output(&output->base, damage); @@ -528,8 +1256,8 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage) return; } - output->next = drm_fb_get_from_bo(bo, b, output->gbm_format); - if (!output->next) { + output->primary_plane->next = drm_fb_get_from_bo(bo, b, output->gbm_format); + if (!output->primary_plane->next) { weston_log("failed to get drm_fb for bo\n"); gbm_surface_release_buffer(output->gbm_surface, bo); return; @@ -552,7 +1280,7 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage) output->current_image ^= 1; - output->next = output->dumb[output->current_image]; + output->primary_plane->next = output->dumb[output->current_image]; pixman_renderer_output_set_buffer(&output->base, output->image[output->current_image]); @@ -562,66 +1290,324 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage) pixman_region32_fini(&previous_damage); } -static void -drm_output_render(struct drm_output *output, pixman_region32_t *damage) -{ - struct weston_compositor *c = output->base.compositor; - struct drm_backend *b = (struct drm_backend *)c->backend; +static void +drm_output_render(struct drm_output *output, pixman_region32_t *damage) +{ + struct weston_compositor *c = output->base.compositor; + struct drm_backend *b = (struct drm_backend *)c->backend; + + if (b->use_pixman) + drm_output_render_pixman(output, damage); + else + drm_output_render_gl(output, damage); + + pixman_region32_subtract(&c->primary_plane.damage, + &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) +{ + int rc; + struct drm_output *output = (struct drm_output *) output_base; + struct drm_backend *backend = + (struct drm_backend *) output->base.compositor->backend; + + /* check */ + if (output_base->gamma_size != size) + return; + if (!output->original_crtc) + return; + + rc = drmModeCrtcSetGamma(backend->drm.fd, + output->crtc_id, + size, r, g, b); + if (rc) + weston_log("set gamma failed: %m\n"); +} + +/** + * Populates an existing atomic request with the properties required for a + * full modeset. + * + * drm_output_populate_atomic_pageflip() must be called for at least the + * primary plane after this, and before committing, if the output is enabled. + * + * XXX: bool properties are lame. + * + * @param output Output to calculate modeset parameters for + * @param req Pre-allocated atomic request to fill with values + * @param enable If true, enable the output; if true, switch it off + * @ret -1 on error, 1 if any properties were set, or 0 if no change + */ +static int +drm_output_populate_atomic_modeset(struct drm_output *output, + drmModeAtomicReq *req, + bool enable, + bool force) +{ + struct drm_mode *mode = NULL; + int ret = 0; + + if (force) { + output->props_crtc.value_valid_mask = 0; + output->props_conn.value_valid_mask = 0; + } + + drm_crtc_update_begin(output); + drm_connector_update_begin(output); + + if (enable) { + mode = container_of(output->base.current_mode, + struct drm_mode, base); + assert(mode->blob_id); + print_drm_mode_info(&mode->mode_info); + output->primary_plane->src_w = mode->base.width << 16; + output->primary_plane->src_h = mode->base.height << 16; + output->primary_plane->dest_w = mode->base.width; + output->primary_plane->dest_h = mode->base.height; + } else { + output->primary_plane->src_w = 0; + output->primary_plane->src_h = 0; + output->primary_plane->dest_w = 0; + output->primary_plane->dest_h = 0; + } + + ret |= atomic_connector_add(req, output, WDRM_CONNECTOR_CRTC_ID, + enable ? output->crtc_id : 0); + ret |= atomic_crtc_add(req, output, WDRM_CRTC_MODE_ID, + enable ? mode->blob_id : 0); + ret |= atomic_crtc_add(req, output, WDRM_CRTC_ACTIVE, enable); + + if (ret) + return -1; + + if (!output->props_crtc.value_pend_mask && + !output->props_conn.value_pend_mask) + return 0; + + 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. + */ +static unsigned int +drm_waitvblank_pipe(struct drm_output *output) +{ + if (output->pipe > 1) + return (output->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) & + DRM_VBLANK_HIGH_CRTC_MASK; + else if (output->pipe > 0) + return DRM_VBLANK_SECONDARY; + else + return 0; +} + +/** + * Populates an existing atomic request with the properties required to change + * the visible framebuffer. + * + * XXX: bool properties are lame + * + * @param output Output for this plane to be displayed on + * @param p Plane to configure + * @param req Atomic request to populate with changes + * @param force If true, will populate request regardless of current state + * @ret -1 on error, 1 if any properties were set, or 0 if no change + */ +static int +drm_output_populate_atomic_plane(struct drm_output *output, struct drm_plane *p, + drmModeAtomicReq *req, bool force) +{ + struct drm_backend *backend = + (struct drm_backend *) output->base.compositor->backend; + uint32_t fb_id = 0; + int ret = 0; + + if (force) + p->props.value_valid_mask = 0; + + assert(p->output == output); + + if (p->next && !backend->sprites_hidden) + fb_id = p->next->fb_id; + + if (fb_id == 0) { + output = NULL; + p->src_x = 0; + p->src_y = 0; + p->src_w = 0; + p->src_h = 0; + p->dest_x = 0; + p->dest_y = 0; + p->dest_w = 0; + p->dest_h = 0; + } + + drm_plane_update_begin(p); + ret |= atomic_plane_add(req, p, WDRM_PLANE_CRTC_ID, + output ? output->crtc_id : 0); + ret |= atomic_plane_add(req, p, WDRM_PLANE_FB_ID, fb_id); + ret |= atomic_plane_add(req, p, WDRM_PLANE_SRC_X, p->src_x); + ret |= atomic_plane_add(req, p, WDRM_PLANE_SRC_Y, p->src_y); + ret |= atomic_plane_add(req, p, WDRM_PLANE_SRC_W, p->src_w); + ret |= atomic_plane_add(req, p, WDRM_PLANE_SRC_H, p->src_h); + ret |= atomic_plane_add(req, p, WDRM_PLANE_CRTC_X, p->dest_x); + ret |= atomic_plane_add(req, p, WDRM_PLANE_CRTC_Y, p->dest_y); + ret |= atomic_plane_add(req, p, WDRM_PLANE_CRTC_W, p->dest_w); + ret |= atomic_plane_add(req, p, WDRM_PLANE_CRTC_H, p->dest_h); + + if (ret) + return -1; + + if (!p->props.value_pend_mask) + return 0; + + return 1; +} + +static int +drm_output_repaint_atomic(struct weston_output *output_base, + pixman_region32_t *damage) +{ + struct drm_output *output = (struct drm_output *) output_base; + struct drm_backend *backend = + (struct drm_backend *)output->base.compositor->backend; + struct drm_plane *plane, *plane_tmp; + drmModeAtomicReq *req; + uint32_t flags; + bool any_submitted = false; + int errno_save; + int ret = 0; + + if (output->destroy_pending) + return -1; + + assert(wl_list_empty(&output->plane_flip_list)); + /* The only legitimate scenario for repaint being called whilst off, is + * that we're being called as part of a modeset, e.g. during init. */ + assert(output->dpms == WESTON_DPMS_ON || + !output->primary_plane->current); + + if (!output->primary_plane->next) + drm_output_render(output, damage); + + if (!output->primary_plane->next) { + weston_log("DRM: output render failed\n"); + return -1; + } + + /* If there is no current framebuffer, then we need to do a modeset. */ + if (!output->primary_plane->current) { + req = drmModeAtomicAlloc(); + if (!req) { + weston_log("DRM: couldn't allocate atomic request\n"); + goto err_render; + } + ret = drm_output_populate_atomic_modeset(output, req, true, + false); + if (ret < 0) + goto err_populate; + else if (ret > 0) + any_submitted = true; + + flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_ATOMIC_ALLOW_MODESET; + ret = drmModeAtomicCommit(backend->drm.fd, req, flags, output); + if (ret) { + weston_log("DRM error: atomic commit failed for modeset "); + goto err_populate; + } + drmModeAtomicFree(req); + } + + req = drmModeAtomicAlloc(); + if (!req) { + weston_log("DRM: couldn't allocate atomic request\n"); + goto err_render; + } + + wl_list_for_each(plane, &backend->plane_list, link) { + if (plane->output != output) + continue; + + /* XXX: We shouldn't be forcing this really, but unfortunately + * when we populate the values in the test modeset, there + * is no notion of it being a temporary request; thus + * without force here, we end up doing nothing, since + * it thinks all the values are up-to-date. */ + ret = drm_output_populate_atomic_plane(output, plane, req, true); + if (ret < 0) { + goto err_populate; + } else if (ret > 0) { + any_submitted = true; + wl_list_insert(&output->plane_flip_list, + &plane->flip_link); + } + } + + if (!any_submitted) { + weston_log("warning: repaint with no damage; forcing flip anyway\n"); + plane = output->primary_plane; + ret = drm_output_populate_atomic_plane(output, plane, req, true); + if (ret < 0) + goto err_populate; + wl_list_insert(&output->plane_flip_list, &plane->flip_link); + } + + flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; + errno_save = 0; + ret = drmModeAtomicCommit(backend->drm.fd, req, flags, output); + errno_save = errno; + if (ret) { + weston_log("DRM error: atomic commit failed for output " + "%s (%dx%d): %s", + output->base.name, + output->base.current_mode->width, + output->base.current_mode->height, + strerror(errno_save)); + goto err_pageflip; + } + drmModeAtomicFree(req); + + if (!output->primary_plane->current) { + drm_crtc_update_success(output); + drm_connector_update_success(output); + } + + wl_list_for_each(plane, &output->plane_flip_list, flip_link) + drm_plane_update_success(plane); + + output->dpms = WESTON_DPMS_ON; - if (b->use_pixman) - drm_output_render_pixman(output, damage); - else - drm_output_render_gl(output, damage); + return 0; - pixman_region32_subtract(&c->primary_plane.damage, - &c->primary_plane.damage, damage); -} +err_pageflip: + wl_list_for_each_safe(plane, plane_tmp, &output->plane_flip_list, + flip_link) + wl_list_remove(&plane->flip_link); /* XXX: release next? */ -static 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 = (struct drm_output *) output_base; - struct drm_backend *backend = - (struct drm_backend *) output->base.compositor->backend; + output->cursor_view = NULL; - /* check */ - if (output_base->gamma_size != size) - return; - if (!output->original_crtc) - return; +err_populate: + drmModeAtomicFree(req); - rc = drmModeCrtcSetGamma(backend->drm.fd, - output->crtc_id, - size, r, g, b); - if (rc) - weston_log("set gamma failed: %m\n"); -} +err_render: + drm_output_release_fb(output, output->primary_plane->next); + output->primary_plane->next = NULL; -/* 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. - */ -static unsigned int -drm_waitvblank_pipe(struct drm_output *output) -{ - if (output->pipe > 1) - return (output->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) & - DRM_VBLANK_HIGH_CRTC_MASK; - else if (output->pipe > 0) - return DRM_VBLANK_SECONDARY; - else - return 0; + return -1; } static int @@ -631,23 +1617,23 @@ drm_output_repaint(struct weston_output *output_base, struct drm_output *output = (struct drm_output *) output_base; struct drm_backend *backend = (struct drm_backend *)output->base.compositor->backend; - struct drm_sprite *s; + struct drm_plane *s; struct drm_mode *mode; int ret = 0; if (output->destroy_pending) return -1; - if (!output->next) + if (!output->primary_plane->next) drm_output_render(output, damage); - if (!output->next) + if (!output->primary_plane->next) return -1; mode = container_of(output->base.current_mode, struct drm_mode, base); - if (!output->current || - output->current->stride != output->next->stride) { + if (!output->primary_plane->current || + output->primary_plane->current->stride != output->primary_plane->next->stride) { ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, - output->next->fb_id, 0, 0, + output->primary_plane->next->fb_id, 0, 0, &output->connector_id, 1, &mode->mode_info); if (ret) { @@ -658,7 +1644,7 @@ drm_output_repaint(struct weston_output *output_base, } if (drmModePageFlip(backend->drm.fd, output->crtc_id, - output->next->fb_id, + output->primary_plane->next->fb_id, DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { weston_log("queueing pageflip failed: %m\n"); goto err_pageflip; @@ -671,7 +1657,7 @@ drm_output_repaint(struct weston_output *output_base, /* * Now, update all the sprite surfaces */ - wl_list_for_each(s, &backend->sprite_list, link) { + wl_list_for_each(s, &backend->plane_list, link) { uint32_t flags = 0, fb_id = 0; drmVBlank vbl = { .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT, @@ -679,7 +1665,7 @@ drm_output_repaint(struct weston_output *output_base, }; if ((!s->current && !s->next) || - !drm_sprite_crtc_supported(output, s->possible_crtcs)) + !drm_plane_crtc_supported(output, s->possible_crtcs)) continue; if (s->next && !backend->sprites_hidden) @@ -716,9 +1702,9 @@ drm_output_repaint(struct weston_output *output_base, err_pageflip: output->cursor_view = NULL; - if (output->next) { - drm_output_release_fb(output, output->next); - output->next = NULL; + if (output->primary_plane->next) { + drm_output_release_fb(output, output->primary_plane->next); + output->primary_plane->next = NULL; } return -1; @@ -728,60 +1714,61 @@ static void drm_output_start_repaint_loop(struct weston_output *output_base) { struct drm_output *output = (struct drm_output *) output_base; - struct drm_backend *backend = (struct drm_backend *) + struct drm_backend *b = (struct drm_backend *) output_base->compositor->backend; uint32_t fb_id; - struct timespec ts, tnow; - struct timespec vbl2now; - int64_t refresh_nsec; + struct timespec ts; int ret; - drmVBlank vbl = { - .request.type = DRM_VBLANK_RELATIVE, - .request.sequence = 0, - .request.signal = 0, - }; if (output->destroy_pending) return; - if (!output->current) { + if (!output->primary_plane->current) { /* We can't page flip if there's no mode set */ + weston_log("start_repaint_loop: no modeset pending\n"); goto finish_frame; } - /* Try to get current msc and timestamp via instant query */ - vbl.request.type |= drm_waitvblank_pipe(output); - ret = drmWaitVBlank(backend->drm.fd, &vbl); + fb_id = output->primary_plane->current->fb_id; - /* 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; + if (b->atomic_modeset) { + drmModeAtomicReq *req = drmModeAtomicAlloc(); - /* 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; + output->primary_plane->next = output->primary_plane->current; + + if (output->dpms != WESTON_DPMS_ON) { + ret = drm_output_populate_atomic_modeset(output, req, + true, true); + if (ret <= 0) { + weston_log("DRM: re-enabling output failed\n"); + goto finish_frame; + } } - } - /* Immediate query didn't provide valid timestamp. - * Use pageflip fallback. - */ - fb_id = output->current->fb_id; + ret = drm_output_populate_atomic_plane(output, output->primary_plane, + req, true); + if (ret <= 0) { + weston_log("DRM: start repaint atomic failed\n"); + goto finish_frame; + } - if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id, - DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { + ret = drmModeAtomicCommit(b->drm.fd, req, + (DRM_MODE_PAGE_FLIP_EVENT | \ + DRM_MODE_ATOMIC_NONBLOCK), + output); + drmModeAtomicFree(req); + if (ret) { + weston_log("DRM error: start repaint atomic commit\n"); + goto finish_frame; + } + + output->dpms = WESTON_DPMS_ON; + wl_list_insert(&output->plane_flip_list, + &output->primary_plane->flip_link); + + drm_plane_update_success(output->primary_plane); + } else if (drmModePageFlip(b->drm.fd, output->crtc_id, fb_id, + DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { weston_log("queueing pageflip failed: %m\n"); goto finish_frame; } @@ -790,7 +1777,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base) finish_frame: /* if we cannot page-flip, immediately finish frame */ - weston_compositor_read_presentation_clock(output_base->compositor, &ts); + weston_compositor_read_presentation_clock(b->compositor, &ts); weston_output_finish_frame(output_base, &ts, WP_PRESENTATION_FEEDBACK_INVALID); } @@ -810,7 +1797,7 @@ static void vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { - struct drm_sprite *s = (struct drm_sprite *)data; + struct drm_plane *s = (struct drm_plane *)data; struct drm_output *output = s->output; struct timespec ts; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | @@ -838,6 +1825,9 @@ page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { struct drm_output *output = (struct drm_output *) data; + struct drm_backend *b = + (struct drm_backend *) output->base.compositor->backend; + struct drm_plane *plane, *plane_tmp; struct timespec ts; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | @@ -845,13 +1835,43 @@ page_flip_handler(int fd, unsigned int frame, drm_output_update_msc(output, frame); + wl_list_for_each_safe(plane, plane_tmp, + &output->plane_flip_list, flip_link) { + /* Cursor plane has two cursor_bo only, each time switch it.So, + * no need to release fb and do any operation for cursor. + */ + if (plane != output->cursor_plane) { + if (plane->current != plane->next) + drm_output_release_fb(output, plane->current); + } + + plane->current = plane->next; + plane->next = NULL; + + /*if the frame is not last, then remove plane destroy function and recycle plane*/ + if ((plane != output->primary_plane) && + (plane != output->cursor_plane) && (plane->current)) { + wl_list_remove(&plane->view_destroy.link); + plane->view = NULL; + } + /* if the plane commit nothing, then stop it. Avoid extral + * cost for drmModeAtomicCommit. + */ + if ((!plane->current) && (!plane->view)) + plane->output = NULL; + + wl_list_remove(&plane->flip_link); + } + /* We don't set page_flip_pending on start_repaint_loop, in that case * we just want to page flip to the current buffer to get an accurate * timestamp */ - if (output->page_flip_pending) { - drm_output_release_fb(output, output->current); - output->current = output->next; - output->next = NULL; + if (!b->atomic_modeset && output->page_flip_pending) { + if (output->primary_plane->current != output->primary_plane->next) + drm_output_release_fb(output, + output->primary_plane->current); + output->primary_plane->current = output->primary_plane->next; + output->primary_plane->next = NULL; } output->page_flip_pending = 0; @@ -871,7 +1891,7 @@ page_flip_handler(int fd, unsigned int frame, } static uint32_t -drm_output_check_sprite_format(struct drm_sprite *s, +drm_output_check_sprite_format(struct drm_plane *s, struct weston_view *ev, struct gbm_bo *bo) { uint32_t i, format; @@ -914,7 +1934,7 @@ drm_output_prepare_overlay_view(struct drm_output *output, struct drm_backend *b = (struct drm_backend *)ec->backend; struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport; struct wl_resource *buffer_resource; - struct drm_sprite *s; + struct drm_plane *s; struct linux_dmabuf_buffer *dmabuf; int found = 0; struct gbm_bo *bo; @@ -926,12 +1946,6 @@ drm_output_prepare_overlay_view(struct drm_output *output, if (b->gbm == NULL) return NULL; - if (viewport->buffer.transform != output->base.transform) - return NULL; - - if (viewport->buffer.scale != output->base.current_scale) - return NULL; - if (b->sprites_are_broken) return NULL; @@ -942,20 +1956,26 @@ drm_output_prepare_overlay_view(struct drm_output *output, return NULL; buffer_resource = ev->surface->buffer_ref.buffer->resource; - if (ev->alpha != 1.0f) - return NULL; - if (wl_shm_buffer_get(buffer_resource)) return NULL; - if (!drm_view_transform_supported(ev)) - return NULL; + wl_list_for_each(s, &b->plane_list, link) { + if (!drm_plane_crtc_supported(output, s->possible_crtcs)) + continue; - wl_list_for_each(s, &b->sprite_list, link) { - if (!drm_sprite_crtc_supported(output, s->possible_crtcs)) + if (s->type != WDRM_PLANE_TYPE_OVERLAY) continue; - if (!s->next) { + /* XXX: At this point, we need two runs through assign_planes; + * one to prepare any necessary views, and see if there + * are any currently-unused planes. This process may + * actually free up some planes for other views to use; + * if any planes have been freed up, we should do another + * pass to see if any planeless views can use any planes + * which have just been freed. But we want to cache what + * we can, so we're not, e.g., calling gbm_bo_import + * twice. */ + if (!s->view) { found = 1; break; } @@ -1009,8 +2029,6 @@ drm_output_prepare_overlay_view(struct drm_output *output, return NULL; } - drm_fb_set_buffer(s->next, ev->surface->buffer_ref.buffer); - box = pixman_region32_extents(&ev->transform.boundingbox); s->plane.x = box->x1; s->plane.y = box->y1; @@ -1025,56 +2043,74 @@ drm_output_prepare_overlay_view(struct drm_output *output, &output->base.region); pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y); box = pixman_region32_extents(&dest_rect); + tbox = weston_transformed_rect(output->base.width, - output->base.height, - output->base.transform, - output->base.current_scale, - *box); + output->base.height, + output->base.transform, + output->base.current_scale, + *box); + s->dest_x = tbox.x1; s->dest_y = tbox.y1; s->dest_w = tbox.x2 - tbox.x1; s->dest_h = tbox.y2 - tbox.y1; pixman_region32_fini(&dest_rect); - pixman_region32_init(&src_rect); - pixman_region32_intersect(&src_rect, &ev->transform.boundingbox, - &output->base.region); - box = pixman_region32_extents(&src_rect); - - weston_view_from_global_fixed(ev, - wl_fixed_from_int(box->x1), - wl_fixed_from_int(box->y1), - &sx1, &sy1); - weston_view_from_global_fixed(ev, - wl_fixed_from_int(box->x2), - wl_fixed_from_int(box->y2), - &sx2, &sy2); - - if (sx1 < 0) - sx1 = 0; - if (sy1 < 0) - sy1 = 0; - if (sx2 > wl_fixed_from_int(ev->surface->width)) - sx2 = wl_fixed_from_int(ev->surface->width); - if (sy2 > wl_fixed_from_int(ev->surface->height)) - sy2 = wl_fixed_from_int(ev->surface->height); - - tbox.x1 = sx1; - tbox.y1 = sy1; - tbox.x2 = sx2; - tbox.y2 = sy2; + if (viewport->buffer.src_width == wl_fixed_from_int(-1)) { + pixman_region32_init(&src_rect); + pixman_region32_intersect(&src_rect, &ev->transform.boundingbox, + &output->base.region); + box = pixman_region32_extents(&src_rect); + + weston_view_from_global_fixed(ev, + wl_fixed_from_int(box->x1), + wl_fixed_from_int(box->y1), + &sx1, &sy1); + weston_view_from_global_fixed(ev, + wl_fixed_from_int(box->x2), + wl_fixed_from_int(box->y2), + &sx2, &sy2); + + if (sx1 < 0) + sx1 = 0; + if (sy1 < 0) + sy1 = 0; + if (sx2 > wl_fixed_from_int(ev->surface->width)) + sx2 = wl_fixed_from_int(ev->surface->width); + if (sy2 > wl_fixed_from_int(ev->surface->height)) + sy2 = wl_fixed_from_int(ev->surface->height); + + tbox.x1 = sx1; + tbox.y1 = sy1; + tbox.x2 = sx2; + tbox.y2 = sy2; + + pixman_region32_fini(&src_rect); + } else { + tbox.x1 = viewport->buffer.src_x; + tbox.y1 = viewport->buffer.src_y; + tbox.x2 = tbox.x1 + viewport->buffer.src_width; + tbox.y2 = tbox.y1 + viewport->buffer.src_height; + } tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width), - wl_fixed_from_int(ev->surface->height), - viewport->buffer.transform, - viewport->buffer.scale, - tbox); + wl_fixed_from_int(ev->surface->height), + viewport->buffer.transform, + viewport->buffer.scale, + tbox); s->src_x = tbox.x1 << 8; s->src_y = tbox.y1 << 8; s->src_w = (tbox.x2 - tbox.x1) << 8; s->src_h = (tbox.y2 - tbox.y1) << 8; - pixman_region32_fini(&src_rect); + + if (s->next != s->current) + drm_fb_set_buffer(s->next, ev->surface->buffer_ref.buffer); + + s->output = output; + s->view = ev; + + wl_signal_add(&ev->destroy_signal, &s->view_destroy); return &s->plane; } @@ -1085,19 +2121,15 @@ drm_output_prepare_cursor_view(struct drm_output *output, { struct drm_backend *b = (struct drm_backend *)output->base.compositor->backend; - struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport; struct wl_shm_buffer *shmbuf; + struct gbm_bo *bo; + uint32_t format; + pixman_region32_t dest_rect; + pixman_box32_t *box, tbox; - if (ev->transform.enabled && - (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE)) - return NULL; if (b->gbm == NULL) return NULL; - if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL) - return NULL; - if (viewport->buffer.scale != output->base.current_scale) - return NULL; - if (output->cursor_view) + if (output->cursor_plane == NULL) return NULL; if (ev->output_mask != (1u << output->base.id)) return NULL; @@ -1116,9 +2148,52 @@ drm_output_prepare_cursor_view(struct drm_output *output, ev->surface->height > b->cursor_height) return NULL; + if (b->atomic_modeset) + { + output->current_cursor ^= 1; + bo = output->gbm_cursor_bo[output->current_cursor]; + /*I guess if bo is used by drm, weston should not reuse it directly.*/ + cursor_bo_update(b, bo, ev); + format = drm_output_check_sprite_format(output->cursor_plane, ev, bo); + if (format == 0) { + weston_log("failed to support format for cursor bo\n"); + return NULL; + } + + output->cursor_plane->next = drm_fb_get_from_bo(bo, b, format); + + if (!output->cursor_plane->next) { + weston_log("failed to get fb from cursor bo\n"); + return NULL; + } + + output->cursor_plane->src_x = 0; + output->cursor_plane->src_y = 0; + output->cursor_plane->src_w = b->cursor_width << 16; + output->cursor_plane->src_h = b->cursor_height << 16; + + pixman_region32_init(&dest_rect); + pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox, + &output->base.region); + pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y); + box = pixman_region32_extents(&dest_rect); + + tbox = weston_transformed_rect(output->base.width, + output->base.height, + output->base.transform, + output->base.current_scale, + *box); + + output->cursor_plane->dest_x = tbox.x1; + output->cursor_plane->dest_y = tbox.y1; + output->cursor_plane->dest_w = b->cursor_width; + output->cursor_plane->dest_h = b->cursor_height; + pixman_region32_fini(&dest_rect); + } output->cursor_view = ev; + output->cursor_plane->output = output; - return &output->cursor_plane; + return &output->cursor_plane->plane; } /** @@ -1172,17 +2247,17 @@ drm_output_set_cursor(struct drm_output *output) output->cursor_view = NULL; if (ev == NULL) { drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); - output->cursor_plane.x = INT32_MIN; - output->cursor_plane.y = INT32_MIN; + output->cursor_plane->plane.x = INT32_MIN; + output->cursor_plane->plane.y = INT32_MIN; return; } buffer = ev->surface->buffer_ref.buffer; if (buffer && - pixman_region32_not_empty(&output->cursor_plane.damage)) { - pixman_region32_fini(&output->cursor_plane.damage); - pixman_region32_init(&output->cursor_plane.damage); + pixman_region32_not_empty(&output->cursor_plane->plane.damage)) { + pixman_region32_fini(&output->cursor_plane->plane.damage); + pixman_region32_init(&output->cursor_plane->plane.damage); output->current_cursor ^= 1; bo = output->gbm_cursor_bo[output->current_cursor]; @@ -1203,14 +2278,14 @@ drm_output_set_cursor(struct drm_output *output) x = (x - output->base.x) * output->base.current_scale; y = (y - output->base.y) * output->base.current_scale; - if (output->cursor_plane.x != x || output->cursor_plane.y != y) { + if (output->cursor_plane->plane.x != x || output->cursor_plane->plane.y != y) { if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) { weston_log("failed to move cursor: %m\n"); b->cursors_are_broken = 1; } - output->cursor_plane.x = x; - output->cursor_plane.y = y; + output->cursor_plane->plane.x = x; + output->cursor_plane->plane.y = y; } } @@ -1223,6 +2298,8 @@ drm_assign_planes(struct weston_output *output_base) struct weston_view *ev, *next; pixman_region32_t overlap, surface_overlap; struct weston_plane *primary, *next_plane; + drmModeAtomicReqPtr req = NULL; + int ret; /* * Find a surface for each sprite in the output using some heuristics: @@ -1240,6 +2317,12 @@ drm_assign_planes(struct weston_output *output_base) pixman_region32_init(&overlap); primary = &output_base->compositor->primary_plane; + if (b->atomic_modeset) { + req = drmModeAtomicAlloc(); + if (!req) + return; + } + wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) { struct weston_surface *es = ev->surface; @@ -1273,6 +2356,42 @@ drm_assign_planes(struct weston_output *output_base) next_plane = drm_output_prepare_scanout_view(output, ev); if (next_plane == NULL) next_plane = drm_output_prepare_overlay_view(output, ev); + + /* Check whether or not the kernel likes this import. */ + if (b->atomic_modeset && next_plane && next_plane != primary) { + struct drm_plane *plane = + container_of(next_plane, struct drm_plane, plane); + + int saved_cursor = drmModeAtomicGetCursor(req); + + /* This is not matched with an update_success, as we + * never actually commit it, just check that it could + * potentially be committed at some stage. */ + drm_plane_update_begin(plane); + ret = drm_output_populate_atomic_plane(output, plane, + req, false); + if (ret > 0) + ret = drmModeAtomicCommit(b->drm.fd, req, + DRM_MODE_ATOMIC_TEST_ONLY, + output); + if (ret != 0) { + drmModeAtomicSetCursor(req, saved_cursor); + weston_log(" ... no luck [kernel].\n"); + if (plane->next) { + if (plane != output->cursor_plane) + drm_output_release_fb(output, + plane->next); + plane->next = NULL; + } + plane->output = NULL; + plane->view = NULL; + wl_list_remove(&plane->view_destroy.link); + next_plane = NULL; + } else { + weston_log(" \\o/\n"); + } + } + if (next_plane == NULL) next_plane = primary; @@ -1283,7 +2402,8 @@ drm_assign_planes(struct weston_output *output_base) &ev->transform.boundingbox); if (next_plane == primary || - next_plane == &output->cursor_plane) { + (output->cursor_plane && + next_plane == &output->cursor_plane->plane)) { /* cursor plane involves a copy */ ev->psf_flags = 0; } else { @@ -1296,6 +2416,9 @@ drm_assign_planes(struct weston_output *output_base) pixman_region32_fini(&surface_overlap); } pixman_region32_fini(&overlap); + + if (b->atomic_modeset) + drmModeAtomicFree(req); } static void @@ -1308,6 +2431,7 @@ drm_output_destroy(struct weston_output *output_base) struct drm_backend *b = (struct drm_backend *)output->base.compositor->backend; drmModeCrtcPtr origcrtc = output->original_crtc; + int i; if (output->page_flip_pending) { output->destroy_pending = 1; @@ -1319,6 +2443,7 @@ drm_output_destroy(struct weston_output *output_base) backlight_destroy(output->backlight); drmModeFreeProperty(output->dpms_prop); + output_properties_release(output); /* Turn off hardware cursor */ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); @@ -1337,10 +2462,15 @@ drm_output_destroy(struct weston_output *output_base) } else { gl_renderer->output_destroy(output_base); gbm_surface_destroy(output->gbm_surface); + /*free cursor_bo*/ + for (i = 0; i < 2; i++) { + if (output->gbm_cursor_bo[i]) + gbm_bo_destroy(output->gbm_cursor_bo[i]); + } } weston_plane_release(&output->fb_plane); - weston_plane_release(&output->cursor_plane); + weston_plane_release(&output->cursor_plane->plane); weston_output_destroy(&output->base); @@ -1424,9 +2554,9 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; /* reset rendering stuff. */ - drm_output_release_fb(output, output->current); - drm_output_release_fb(output, output->next); - output->current = output->next = NULL; + drm_output_release_fb(output, output->primary_plane->current); + drm_output_release_fb(output, output->primary_plane->next); + output->primary_plane->current = output->primary_plane->next = NULL; if (b->use_pixman) { drm_output_fini_pixman(output); @@ -1517,6 +2647,24 @@ init_drm(struct drm_backend *b, struct udev_device *device) else b->cursor_height = 64; + ret = drmSetClientCap(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"); + + ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1); + b->atomic_modeset = (ret == 0); + weston_log("DRM: %susing atomic modesetting\n", + b->atomic_modeset ? "" : "not "); + /* + * KMS support for hardware planes cannot properly synchronize + * without nuclear page flip. Without nuclear/atomic, hw plane + * plane updates would either tear or cause extra waits for vblanks + * which means dropping the compositor framerate to a fraction. + */ + if (!b->atomic_modeset) + b->sprites_are_broken = 1; + return 0; } @@ -1625,6 +2773,8 @@ init_pixman(struct drm_backend *b) static struct drm_mode * drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) { + struct drm_backend *b = + (struct drm_backend *) output->base.compositor->backend; struct drm_mode *mode; uint64_t refresh; @@ -1653,6 +2803,15 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) if (info->type & DRM_MODE_TYPE_PREFERRED) mode->base.flags |= WL_OUTPUT_MODE_PREFERRED; + if (b->atomic_modeset && + drmModeCreatePropertyBlob(b->drm.fd, info, sizeof(*info), + &mode->blob_id) != 0) { + weston_log("Failed to create blob for mode %s\n", + info->name); + free(mode); + return NULL; + } + wl_list_insert(output->base.mode_list.prev, &mode->base.link); return mode; @@ -1744,18 +2903,146 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) if (!output->dpms_prop) return; - - ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id, - output->dpms_prop->prop_id, level); - if (ret) { - weston_log("DRM: DPMS: failed property set for %s\n", - output->base.name); - return; + if (b->atomic_modeset) { + drmModeAtomicReq *req = drmModeAtomicAlloc(); + bool enable = (level == WESTON_DPMS_ON); + ret = drm_output_populate_atomic_modeset(output, req, + enable, false); + if (ret < 0) { + weston_log("DRM: DPMS: failed to add ACTIVE prop\n"); + return; + } + if (ret > 0) + ret = drmModeAtomicCommit(b->drm.fd, req, + DRM_MODE_ATOMIC_ALLOW_MODESET, + output); + drmModeAtomicFree(req); + if (ret) { + weston_log("DRM: DPMS: failed atomic commit for %s\n", + output->base.name); + return; + } + drm_crtc_update_success(output); + drm_connector_update_success(output); + } else { + ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id, + output->dpms_prop->prop_id, level); + if (ret) { + weston_log("DRM: DPMS: failed property set for %s\n", + output->base.name); + return; + } } output->dpms = level; } +static void +drm_output_init_primary_plane(struct drm_output *output) +{ + struct drm_backend *b = + (struct drm_backend *) output->base.compositor->backend; + struct drm_plane *plane; + + if (b->universal_planes) { + wl_list_for_each(plane, &b->plane_list, link) { + if (plane->type != WDRM_PLANE_TYPE_PRIMARY) + continue; + if (plane->output) + continue; + if (output->primary_plane) + continue; + if (!drm_plane_crtc_supported(output, + plane->possible_crtcs)) + continue; + + plane->output = output; + output->primary_plane = plane; + break; + } + } else { + /* XXX: Gross open-coding ... ? */ + plane = zalloc(sizeof(*plane) + sizeof(uint32_t)); + if (!plane) { + weston_log("%s: out of memory\n", __func__); + return; + } + + weston_plane_init(&plane->plane, b->compositor, 0, 0); + wl_list_insert(&b->plane_list, &plane->link); + + plane->plane_id = 0; + plane->possible_crtcs = 0; + plane->output = output; + plane->current = NULL; + plane->next = NULL; + plane->backend = b; + plane->count_formats = 1; + plane->formats[0] = output->gbm_format; + plane->type = WDRM_PLANE_TYPE_PRIMARY; + + output->primary_plane = plane; + } + + /* Unlike the cursor plane, we don't stack the primary plane into + * the base list, because it's implicitly placed at the bottom. The + * base compositor's primary_plane covers everything we construct + * with our renderer, with each output taking a chunk of the + * base primary_plane. So here we just internally treat this plane + * as a partial shadow of weston_compositor::primary_plane. */ +} + +static void +drm_output_init_cursor(struct drm_output *output) +{ + struct drm_backend *b = + (struct drm_backend *) output->base.compositor->backend; + struct drm_plane *plane; + + if (b->universal_planes) { + wl_list_for_each(plane, &b->plane_list, link) { + if (plane->type != WDRM_PLANE_TYPE_CURSOR) + continue; + if (plane->output) + continue; + if (output->cursor_plane) + continue; + if (!drm_plane_crtc_supported(output, + plane->possible_crtcs)) + continue; + plane->output = output; + output->cursor_plane = plane; + break; + } + } else { + /* XXX: Gross open-coding ... ? */ + plane = zalloc(sizeof(*plane) + sizeof(uint32_t)); + if (!plane) { + weston_log("%s: out of memory\n", __func__); + return; + } + + weston_plane_init(&plane->plane, b->compositor, 0, 0); + wl_list_insert(&b->plane_list, &plane->link); + + plane->plane_id = 0; + plane->possible_crtcs = 0; + plane->output = output; + plane->current = NULL; + plane->next = NULL; + plane->backend = b; + plane->count_formats = 1; + plane->formats[0] = GBM_FORMAT_ARGB8888; + plane->type = WDRM_PLANE_TYPE_CURSOR; + + output->cursor_plane = plane; + } + if (!output->cursor_plane) + return; + weston_compositor_stack_plane(b->compositor, &output->cursor_plane->plane, + NULL); +} + static const char * const connector_type_names[] = { [DRM_MODE_CONNECTOR_Unknown] = "Unknown", [DRM_MODE_CONNECTOR_VGA] = "VGA", @@ -2320,7 +3607,9 @@ create_output_for_connector(struct drm_backend *b, output->base.make = "unknown"; output->base.model = "unknown"; output->base.serial_number = "unknown"; + output->base.compositor = b->compositor; wl_list_init(&output->base.mode_list); + wl_list_init(&output->plane_flip_list); mode = b->configure_output(b->compositor, b->use_current_mode, output->base.name, &config); @@ -2352,7 +3641,7 @@ create_output_for_connector(struct drm_backend *b, if (mode == WESTON_DRM_BACKEND_OUTPUT_OFF) { weston_log("Disabling output %s\n", output->base.name); drmModeSetCrtc(b->drm.fd, output->crtc_id, - 0, 0, 0, 0, 0, NULL); + 0, 0, 0, 0, 0, NULL); goto err_free; } @@ -2367,6 +3656,16 @@ create_output_for_connector(struct drm_backend *b, connector->mmWidth, connector->mmHeight, config.base.transform, config.base.scale); + drm_output_init_primary_plane(output); + if (!output->primary_plane) { + weston_log("Failed to find primary plane for output %s\n", + output->base.name); + goto err_output; + } + drm_output_init_cursor(output); + if (!output_properties_init(output)) + goto err_output; + if (b->use_pixman) { if (drm_output_init_pixman(output, b) < 0) { weston_log("Failed to init output pixman state\n"); @@ -2395,7 +3694,11 @@ create_output_for_connector(struct drm_backend *b, output->base.connection_internal = 1; output->base.start_repaint_loop = drm_output_start_repaint_loop; - output->base.repaint = drm_output_repaint; + if (b->atomic_modeset) + output->base.repaint = drm_output_repaint_atomic; + else + output->base.repaint = drm_output_repaint; + output->base.destroy = drm_output_destroy; output->base.assign_planes = drm_assign_planes; output->base.set_dpms = drm_set_dpms; @@ -2404,11 +3707,8 @@ create_output_for_connector(struct drm_backend *b, output->base.gamma_size = output->original_crtc->gamma_size; output->base.set_gamma = drm_output_set_gamma; - weston_plane_init(&output->cursor_plane, b->compositor, - INT32_MIN, INT32_MIN); weston_plane_init(&output->fb_plane, b->compositor, 0, 0); - weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL); weston_compositor_stack_plane(b->compositor, &output->fb_plane, &b->compositor->primary_plane); @@ -2435,6 +3735,10 @@ err_output: err_free: wl_list_for_each_safe(drm_mode, next, &output->base.mode_list, base.link) { + /* XXX: Needs proper destroy function. */ + if (b->atomic_modeset) + drmModeDestroyPropertyBlob(b->drm.fd, + drm_mode->blob_id); wl_list_remove(&drm_mode->base.link); free(drm_mode); } @@ -2448,10 +3752,109 @@ err_free: return -1; } +/** +* Get type for a DRM plane +* +* Given a drm_plane object, find its type as an internal enum. +* +* @param plane Plane to find type for +* @returns Internal enum for plane type, or OVERLAY if unknown +*/ +static enum wdrm_plane_type +drm_plane_get_type(struct drm_plane *plane) +{ + uint64_t drm_type = drm_plane_property_get(plane, WDRM_PLANE_TYPE); + int i; + + for (i = 0; i < WDRM_PLANE_TYPE__COUNT; i++) { + if (plane->props.typemap[i] == drm_type) + return i; + } + + return WDRM_PLANE_TYPE_OVERLAY; +} + +/** + * Handle destruction of a view active on a KMS plane + * + * Called when the weston_view currently being displayed on a KMS + * plane has been destroyed. The very act of destroying a view on + * an active plane will cause a repaint to be scheduled, but we + * still need to disable the plane itself. + * + * @param listener view_destroy listener struct from the plane + * @param data The view being destroyed + */ +static void +drm_plane_view_destroyed(struct wl_listener *listener, void *data) +{ + struct drm_plane *plane = + container_of(listener, struct drm_plane, view_destroy); + + assert(plane->view == data); + plane->view = NULL; + weston_log("view %p destroyed for plane %d\n", data, plane->plane_id); +} + +/** + * Create a drm_plane for a hardware plane + * + * Creates one drm_plane structure for a hardware plane, and initialises its + * properties and formats. + * + * This function does not add the plane to the list of usable planes in Weston + * itself; the caller is responsible for this. + * + * Call drm_plane_destroy to clean up the plane. + * + * @param ec Compositor to create plane for + * @param kplane DRM plane to create + */ +static struct drm_plane * +drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) +{ + struct drm_plane *plane; + + plane = zalloc(sizeof(*plane) + ((sizeof(uint32_t)) * + kplane->count_formats)); + if (!plane) { + weston_log("%s: out of memory\n", __func__); + return NULL; + } + + weston_plane_init(&plane->plane, b->compositor, 0, 0); + plane->possible_crtcs = kplane->possible_crtcs; + plane->plane_id = kplane->plane_id; + plane->current = NULL; + plane->next = NULL; + plane->output = NULL; + plane->backend = b; + plane->count_formats = kplane->count_formats; + plane->view_destroy.notify = drm_plane_view_destroyed; + memcpy(plane->formats, kplane->formats, + kplane->count_formats * sizeof(kplane->formats[0])); + + if (!plane_properties_init(plane)) { + weston_log("couldn't init plane properties for %d\n", + plane->plane_id); + free(plane); + return NULL; + } + + if (b->universal_planes) + plane->type = drm_plane_get_type(plane); + else + plane->type = WDRM_PLANE_TYPE_OVERLAY; + + wl_list_insert(&b->plane_list, &plane->link); + + return plane; +} + static void create_sprites(struct drm_backend *b) { - struct drm_sprite *sprite; + struct drm_plane *drm_plane; drmModePlaneRes *plane_res; drmModePlane *plane; uint32_t i; @@ -2468,29 +3871,18 @@ create_sprites(struct drm_backend *b) if (!plane) continue; - sprite = zalloc(sizeof(*sprite) + ((sizeof(uint32_t)) * - plane->count_formats)); - if (!sprite) { - weston_log("%s: out of memory\n", - __func__); - drmModeFreePlane(plane); + drm_plane = drm_plane_create(b, plane); + drmModeFreePlane(plane); + if (!drm_plane) { + weston_log("couldn't create plane %d!\n", plane_res->planes[i]); + continue; } - sprite->possible_crtcs = plane->possible_crtcs; - sprite->plane_id = plane->plane_id; - sprite->current = NULL; - sprite->next = NULL; - sprite->backend = b; - sprite->count_formats = plane->count_formats; - memcpy(sprite->formats, plane->formats, - plane->count_formats * sizeof(plane->formats[0])); - drmModeFreePlane(plane); - weston_plane_init(&sprite->plane, b->compositor, 0, 0); - weston_compositor_stack_plane(b->compositor, &sprite->plane, - &b->compositor->primary_plane); - - wl_list_insert(&b->sprite_list, &sprite->link); + if (drm_plane->type == WDRM_PLANE_TYPE_OVERLAY) + weston_compositor_stack_plane(b->compositor, + &drm_plane->plane, + &b->compositor->primary_plane); } drmModeFreePlaneResources(plane_res); @@ -2499,13 +3891,13 @@ create_sprites(struct drm_backend *b) static void destroy_sprites(struct drm_backend *backend) { - struct drm_sprite *sprite, *next; + struct drm_plane *sprite, *next; struct drm_output *output; output = container_of(backend->compositor->output_list.next, struct drm_output, base.link); - wl_list_for_each_safe(sprite, next, &backend->sprite_list, link) { + wl_list_for_each_safe(sprite, next, &backend->plane_list, link) { drmModeSetPlane(backend->drm.fd, sprite->plane_id, output->crtc_id, 0, 0, @@ -2718,10 +4110,19 @@ drm_backend_set_modes(struct drm_backend *backend) { struct drm_output *output; struct drm_mode *drm_mode; + drmModeAtomicReq *req = NULL; int ret; + if (backend->atomic_modeset) { + req = drmModeAtomicAlloc(); + if (!req) { + weston_log("DRM: failed to allocate atomic req\n"); + return; + } + } + wl_list_for_each(output, &backend->compositor->output_list, base.link) { - if (!output->current) { + if (!output->primary_plane->current) { /* If something that would cause the output to * switch mode happened while in another vt, we * might not have a current drm_fb. In that case, @@ -2730,10 +4131,14 @@ drm_backend_set_modes(struct drm_backend *backend) weston_output_schedule_repaint(&output->base); continue; } + if (backend->atomic_modeset) { + drm_output_populate_atomic_modeset(output, req, true, true); + continue; + } drm_mode = (struct drm_mode *) output->base.current_mode; ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, - output->current->fb_id, 0, 0, + output->primary_plane->current->fb_id, 0, 0, &output->connector_id, 1, &drm_mode->mode_info); if (ret < 0) { @@ -2743,6 +4148,13 @@ drm_backend_set_modes(struct drm_backend *backend) output->base.x, output->base.y); } } + if (backend->atomic_modeset) { + ret = drmModeAtomicCommit(backend->drm.fd, req, + DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + drmModeAtomicFree(req); + if (ret) + weston_log("DRM: atomic modeset failed\n"); + } } static void @@ -2750,7 +4162,7 @@ session_notify(struct wl_listener *listener, void *data) { struct weston_compositor *compositor = data; struct drm_backend *b = (struct drm_backend *)compositor->backend; - struct drm_sprite *sprite; + struct drm_plane *sprite; struct drm_output *output; if (compositor->session_active) { @@ -2782,7 +4194,7 @@ session_notify(struct wl_listener *listener, void *data) output = container_of(compositor->output_list.next, struct drm_output, base.link); - wl_list_for_each(sprite, &b->sprite_list, link) + wl_list_for_each(sprite, &b->plane_list, link) drmModeSetPlane(b->drm.fd, sprite->plane_id, output->crtc_id, 0, 0, @@ -2894,7 +4306,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data) if (!output->recorder) return; - ret = drmPrimeHandleToFD(b->drm.fd, output->current->handle, + ret = drmPrimeHandleToFD(b->drm.fd, output->primary_plane->current->handle, DRM_CLOEXEC, &fd); if (ret) { weston_log("[libva recorder] " @@ -2903,7 +4315,7 @@ recorder_frame_notify(struct wl_listener *listener, void *data) } ret = vaapi_recorder_frame(output->recorder, fd, - output->current->stride); + output->primary_plane->current->stride); if (ret < 0) { weston_log("[libva recorder] aborted: %m\n"); recorder_destroy(output); @@ -3057,11 +4469,12 @@ drm_backend_create(struct weston_compositor *compositor, * * These can be enabled again when nuclear/atomic support lands. */ - b->sprites_are_broken = 1; + b->sprites_are_broken = 0; b->compositor = compositor; b->use_pixman = config->use_pixman; b->configure_output = config->configure_output; b->use_current_mode = config->use_current_mode; + compositor->backend = &b->base; if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0) goto err_compositor; @@ -3122,7 +4535,7 @@ drm_backend_create(struct weston_compositor *compositor, weston_setup_vt_switch_bindings(compositor); - wl_list_init(&b->sprite_list); + wl_list_init(&b->plane_list); create_sprites(b); if (udev_input_init(&b->input, @@ -3184,8 +4597,6 @@ drm_backend_create(struct weston_compositor *compositor, "support failed.\n"); } - compositor->backend = &b->base; - return b; err_udev_monitor: -- 1.9.1