libliftoff/plane.c
Simon Ser a3dc9c48e4 Don't use planes currently in-use on other CRTCs on first commit
When doing the first commit, libliftoff will try to disable all
candidate planes. This is necessary to make sure we start atomic
test commits from a clean state, and are able to incrementally come
up with a combination which works.

However, disabling a plane currently in-use on another CRTC (e.g.
a primary plane) will result in failed commits. We can't steal that
plane without removing it from the other CRTC first.

Add a new liftoff_plane.crtc_id field populated with the current
CRTC ID for the plane.
2022-08-19 09:38:50 +02:00

273 lines
6.8 KiB
C

#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "private.h"
static int
guess_plane_zpos_from_type(struct liftoff_device *device, uint32_t plane_id,
uint32_t type)
{
struct liftoff_plane *primary;
/* From far to close to the eye: primary, overlay, cursor. Unless
* the overlay ID < primary ID. */
switch (type) {
case DRM_PLANE_TYPE_PRIMARY:
return 0;
case DRM_PLANE_TYPE_CURSOR:
return 2;
case DRM_PLANE_TYPE_OVERLAY:
if (liftoff_list_empty(&device->planes)) {
return 0; /* No primary plane, shouldn't happen */
}
primary = liftoff_container_of(device->planes.next,
primary, link);
if (plane_id < primary->id) {
return -1;
} else {
return 1;
}
}
return 0;
}
struct liftoff_plane *
liftoff_plane_create(struct liftoff_device *device, uint32_t id)
{
struct liftoff_plane *plane, *cur;
drmModePlane *drm_plane;
drmModeObjectProperties *drm_props;
uint32_t i;
drmModePropertyRes *drm_prop;
struct liftoff_plane_property *prop;
uint64_t value;
bool has_type = false, has_zpos = false;
liftoff_list_for_each(plane, &device->planes, link) {
if (plane->id == id) {
liftoff_log(LIFTOFF_ERROR, "tried to register plane "
"%"PRIu32" twice\n", id);
errno = EEXIST;
return NULL;
}
}
plane = calloc(1, sizeof(*plane));
if (plane == NULL) {
liftoff_log_errno(LIFTOFF_ERROR, "calloc");
return NULL;
}
drm_plane = drmModeGetPlane(device->drm_fd, id);
if (drm_plane == NULL) {
liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetPlane");
return NULL;
}
plane->id = drm_plane->plane_id;
plane->possible_crtcs = drm_plane->possible_crtcs;
plane->crtc_id = drm_plane->crtc_id;
drmModeFreePlane(drm_plane);
drm_props = drmModeObjectGetProperties(device->drm_fd, id,
DRM_MODE_OBJECT_PLANE);
if (drm_props == NULL) {
liftoff_log_errno(LIFTOFF_ERROR, "drmModeObjectGetProperties");
return NULL;
}
plane->props = calloc(drm_props->count_props,
sizeof(struct liftoff_plane_property));
if (plane->props == NULL) {
liftoff_log_errno(LIFTOFF_ERROR, "calloc");
drmModeFreeObjectProperties(drm_props);
return NULL;
}
for (i = 0; i < drm_props->count_props; i++) {
drm_prop = drmModeGetProperty(device->drm_fd,
drm_props->props[i]);
if (drm_prop == NULL) {
liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetProperty");
drmModeFreeObjectProperties(drm_props);
return NULL;
}
prop = &plane->props[i];
memcpy(prop->name, drm_prop->name, sizeof(prop->name));
prop->id = drm_prop->prop_id;
drmModeFreeProperty(drm_prop);
plane->props_len++;
value = drm_props->prop_values[i];
if (strcmp(prop->name, "type") == 0) {
plane->type = value;
has_type = true;
} else if (strcmp(prop->name, "zpos") == 0) {
plane->zpos = value;
has_zpos = true;
}
}
drmModeFreeObjectProperties(drm_props);
if (!has_type) {
liftoff_log(LIFTOFF_ERROR,
"plane %"PRIu32" is missing the 'type' property",
plane->id);
free(plane);
errno = EINVAL;
return NULL;
} else if (!has_zpos) {
plane->zpos = guess_plane_zpos_from_type(device, plane->id,
plane->type);
}
/* During plane allocation, we will use the plane list order to fill
* planes with FBs. Primary planes need to be filled first, then planes
* far from the primary planes, then planes closer and closer to the
* primary plane. */
if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
liftoff_list_insert(&device->planes, &plane->link);
} else {
liftoff_list_for_each(cur, &device->planes, link) {
if (cur->type != DRM_PLANE_TYPE_PRIMARY &&
plane->zpos >= cur->zpos) {
liftoff_list_insert(cur->link.prev, &plane->link);
break;
}
}
if (plane->link.next == NULL) { /* not inserted */
liftoff_list_insert(device->planes.prev, &plane->link);
}
}
return plane;
}
void
liftoff_plane_destroy(struct liftoff_plane *plane)
{
if (plane->layer != NULL) {
plane->layer->plane = NULL;
}
liftoff_list_remove(&plane->link);
free(plane->props);
free(plane);
}
uint32_t
liftoff_plane_get_id(struct liftoff_plane *plane)
{
return plane->id;
}
static struct liftoff_plane_property *
plane_get_property(struct liftoff_plane *plane, const char *name)
{
size_t i;
for (i = 0; i < plane->props_len; i++) {
if (strcmp(plane->props[i].name, name) == 0) {
return &plane->props[i];
}
}
return NULL;
}
static int
plane_set_prop(struct liftoff_plane *plane, drmModeAtomicReq *req,
struct liftoff_plane_property *prop, uint64_t value)
{
int ret;
ret = drmModeAtomicAddProperty(req, plane->id, prop->id, value);
if (ret < 0) {
liftoff_log(LIFTOFF_ERROR, "drmModeAtomicAddProperty: %s",
strerror(-ret));
return ret;
}
return 0;
}
static int
set_plane_prop_str(struct liftoff_plane *plane, drmModeAtomicReq *req,
const char *name, uint64_t value)
{
struct liftoff_plane_property *prop;
prop = plane_get_property(plane, name);
if (prop == NULL) {
liftoff_log(LIFTOFF_DEBUG,
"plane %"PRIu32" is missing the %s property",
plane->id, name);
return -EINVAL;
}
return plane_set_prop(plane, req, prop, value);
}
int
plane_apply(struct liftoff_plane *plane, struct liftoff_layer *layer,
drmModeAtomicReq *req)
{
int cursor, ret;
size_t i;
struct liftoff_layer_property *layer_prop;
struct liftoff_plane_property *plane_prop;
cursor = drmModeAtomicGetCursor(req);
if (layer == NULL) {
ret = set_plane_prop_str(plane, req, "FB_ID", 0);
if (ret != 0) {
return ret;
}
return set_plane_prop_str(plane, req, "CRTC_ID", 0);
}
ret = set_plane_prop_str(plane, req, "CRTC_ID", layer->output->crtc_id);
if (ret != 0) {
return ret;
}
for (i = 0; i < layer->props_len; i++) {
layer_prop = &layer->props[i];
if (strcmp(layer_prop->name, "zpos") == 0) {
/* We don't yet support setting the zpos property. We
* only use it (read-only) during plane allocation. */
continue;
}
plane_prop = plane_get_property(plane, layer_prop->name);
if (plane_prop == NULL) {
if (strcmp(layer_prop->name, "alpha") == 0 &&
layer_prop->value == 0xFFFF) {
continue; /* Layer is completely opaque */
}
if (strcmp(layer_prop->name, "rotation") == 0 &&
layer_prop->value == DRM_MODE_ROTATE_0) {
continue; /* Layer isn't rotated */
}
if (strcmp(layer_prop->name, "SCALING_FILTER") == 0 &&
layer_prop->value == 0) {
continue; /* Layer uses default scaling filter */
}
if (strcmp(layer_prop->name, "pixel blend mode") == 0 &&
layer_prop->value == 0) {
continue; /* Layer uses pre-multiplied alpha */
}
if (strcmp(layer_prop->name, "FB_DAMAGE_CLIPS") == 0) {
continue; /* Damage can be omitted */
}
drmModeAtomicSetCursor(req, cursor);
return -EINVAL;
}
ret = plane_set_prop(plane, req, plane_prop, layer_prop->value);
if (ret != 0) {
drmModeAtomicSetCursor(req, cursor);
return ret;
}
}
return 0;
}