2021-07-27 12:28:00 +02:00
|
|
|
#include <errno.h>
|
2019-10-11 16:09:35 +02:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2023-02-09 16:41:31 +01:00
|
|
|
#include <sys/types.h>
|
2019-10-11 16:09:35 +02:00
|
|
|
#include "private.h"
|
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
static int
|
|
|
|
guess_plane_zpos_from_type(struct liftoff_device *device, uint32_t plane_id,
|
|
|
|
uint32_t type)
|
2019-10-11 16:09:35 +02:00
|
|
|
{
|
|
|
|
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:
|
2019-11-24 12:59:10 +01:00
|
|
|
if (liftoff_list_empty(&device->planes)) {
|
2019-10-11 16:09:35 +02:00
|
|
|
return 0; /* No primary plane, shouldn't happen */
|
|
|
|
}
|
2019-11-24 12:59:10 +01:00
|
|
|
primary = liftoff_container_of(device->planes.next,
|
2019-10-11 16:09:35 +02:00
|
|
|
primary, link);
|
|
|
|
if (plane_id < primary->id) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
struct liftoff_plane *
|
|
|
|
liftoff_plane_create(struct liftoff_device *device, uint32_t id)
|
2019-10-11 16:09:35 +02:00
|
|
|
{
|
|
|
|
struct liftoff_plane *plane, *cur;
|
|
|
|
drmModePlane *drm_plane;
|
|
|
|
drmModeObjectProperties *drm_props;
|
|
|
|
uint32_t i;
|
2023-02-15 18:47:04 +01:00
|
|
|
drmModePropertyRes *prop;
|
2019-10-11 16:09:35 +02:00
|
|
|
uint64_t value;
|
|
|
|
bool has_type = false, has_zpos = false;
|
|
|
|
|
2021-02-23 18:01:38 +01:00
|
|
|
liftoff_list_for_each(plane, &device->planes, link) {
|
|
|
|
if (plane->id == id) {
|
|
|
|
liftoff_log(LIFTOFF_ERROR, "tried to register plane "
|
|
|
|
"%"PRIu32" twice\n", id);
|
2021-07-30 16:19:29 +02:00
|
|
|
errno = EEXIST;
|
2021-02-23 18:01:38 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-11 16:09:35 +02:00
|
|
|
plane = calloc(1, sizeof(*plane));
|
|
|
|
if (plane == NULL) {
|
2019-10-19 12:35:14 +02:00
|
|
|
liftoff_log_errno(LIFTOFF_ERROR, "calloc");
|
2019-10-11 16:09:35 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-24 12:59:10 +01:00
|
|
|
drm_plane = drmModeGetPlane(device->drm_fd, id);
|
2019-10-11 16:09:35 +02:00
|
|
|
if (drm_plane == NULL) {
|
2019-10-19 12:35:14 +02:00
|
|
|
liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetPlane");
|
2019-10-11 16:09:35 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
plane->id = drm_plane->plane_id;
|
|
|
|
plane->possible_crtcs = drm_plane->possible_crtcs;
|
|
|
|
drmModeFreePlane(drm_plane);
|
|
|
|
|
2019-11-24 12:59:10 +01:00
|
|
|
drm_props = drmModeObjectGetProperties(device->drm_fd, id,
|
2019-10-11 16:09:35 +02:00
|
|
|
DRM_MODE_OBJECT_PLANE);
|
|
|
|
if (drm_props == NULL) {
|
2019-10-19 12:35:14 +02:00
|
|
|
liftoff_log_errno(LIFTOFF_ERROR, "drmModeObjectGetProperties");
|
2019-10-11 16:09:35 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2023-02-15 18:47:04 +01:00
|
|
|
plane->props = calloc(drm_props->count_props, sizeof(plane->props[0]));
|
2019-10-11 16:09:35 +02:00
|
|
|
if (plane->props == NULL) {
|
2019-10-19 12:35:14 +02:00
|
|
|
liftoff_log_errno(LIFTOFF_ERROR, "calloc");
|
2019-10-11 16:09:35 +02:00
|
|
|
drmModeFreeObjectProperties(drm_props);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (i = 0; i < drm_props->count_props; i++) {
|
2023-02-15 18:47:04 +01:00
|
|
|
prop = drmModeGetProperty(device->drm_fd, drm_props->props[i]);
|
|
|
|
if (prop == NULL) {
|
2019-10-19 12:35:14 +02:00
|
|
|
liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetProperty");
|
2019-10-11 16:09:35 +02:00
|
|
|
drmModeFreeObjectProperties(drm_props);
|
|
|
|
return NULL;
|
|
|
|
}
|
2023-02-15 18:47:04 +01:00
|
|
|
plane->props[i] = prop;
|
2019-10-11 16:09:35 +02:00
|
|
|
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;
|
2023-02-09 16:41:31 +01:00
|
|
|
} else if (strcmp(prop->name, "IN_FORMATS") == 0) {
|
|
|
|
plane->in_formats_blob = drmModeGetPropertyBlob(device->drm_fd,
|
|
|
|
value);
|
|
|
|
if (plane->in_formats_blob == NULL) {
|
|
|
|
liftoff_log_errno(LIFTOFF_ERROR, "drmModeGetPropertyBlob");
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-10-11 16:09:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
drmModeFreeObjectProperties(drm_props);
|
|
|
|
|
|
|
|
if (!has_type) {
|
|
|
|
liftoff_log(LIFTOFF_ERROR,
|
|
|
|
"plane %"PRIu32" is missing the 'type' property",
|
|
|
|
plane->id);
|
|
|
|
free(plane);
|
2021-07-30 16:19:29 +02:00
|
|
|
errno = EINVAL;
|
2019-10-11 16:09:35 +02:00
|
|
|
return NULL;
|
|
|
|
} else if (!has_zpos) {
|
2019-11-24 12:59:10 +01:00
|
|
|
plane->zpos = guess_plane_zpos_from_type(device, plane->id,
|
2019-10-11 16:09:35 +02:00
|
|
|
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) {
|
2019-11-24 12:59:10 +01:00
|
|
|
liftoff_list_insert(&device->planes, &plane->link);
|
2019-10-11 16:09:35 +02:00
|
|
|
} else {
|
2019-11-24 12:59:10 +01:00
|
|
|
liftoff_list_for_each(cur, &device->planes, link) {
|
2019-10-11 16:09:35 +02:00
|
|
|
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 */
|
2019-11-24 12:59:10 +01:00
|
|
|
liftoff_list_insert(device->planes.prev, &plane->link);
|
2019-10-11 16:09:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return plane;
|
|
|
|
}
|
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
void
|
|
|
|
liftoff_plane_destroy(struct liftoff_plane *plane)
|
2019-10-11 16:09:35 +02:00
|
|
|
{
|
2023-02-15 18:47:04 +01:00
|
|
|
size_t i;
|
|
|
|
|
2019-12-06 15:06:14 +01:00
|
|
|
if (plane->layer != NULL) {
|
|
|
|
plane->layer->plane = NULL;
|
|
|
|
}
|
2023-02-15 18:47:04 +01:00
|
|
|
|
|
|
|
for (i = 0; i < plane->props_len; i++) {
|
|
|
|
drmModeFreeProperty(plane->props[i]);
|
|
|
|
}
|
|
|
|
|
2019-10-11 16:09:35 +02:00
|
|
|
liftoff_list_remove(&plane->link);
|
|
|
|
free(plane->props);
|
2023-02-09 16:41:31 +01:00
|
|
|
drmModeFreePropertyBlob(plane->in_formats_blob);
|
2019-10-11 16:09:35 +02:00
|
|
|
free(plane);
|
|
|
|
}
|
2019-10-19 12:43:09 +02:00
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
uint32_t
|
|
|
|
liftoff_plane_get_id(struct liftoff_plane *plane)
|
2021-07-01 11:39:45 +02:00
|
|
|
{
|
|
|
|
return plane->id;
|
|
|
|
}
|
|
|
|
|
2023-02-15 18:47:04 +01:00
|
|
|
static const drmModePropertyRes *
|
2021-08-13 22:02:33 +02:00
|
|
|
plane_get_property(struct liftoff_plane *plane, const char *name)
|
2019-10-19 12:43:09 +02:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < plane->props_len; i++) {
|
2023-02-15 18:47:04 +01:00
|
|
|
if (strcmp(plane->props[i]->name, name) == 0) {
|
|
|
|
return plane->props[i];
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
static int
|
|
|
|
plane_set_prop(struct liftoff_plane *plane, drmModeAtomicReq *req,
|
2023-02-15 18:47:04 +01:00
|
|
|
const drmModePropertyRes *prop, uint64_t value)
|
2019-10-19 12:43:09 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2023-02-15 18:47:04 +01:00
|
|
|
ret = drmModeAtomicAddProperty(req, plane->id, prop->prop_id, value);
|
2019-10-19 12:43:09 +02:00
|
|
|
if (ret < 0) {
|
2021-07-27 12:28:00 +02:00
|
|
|
liftoff_log(LIFTOFF_ERROR, "drmModeAtomicAddProperty: %s",
|
|
|
|
strerror(-ret));
|
|
|
|
return ret;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
|
2021-07-27 12:28:00 +02:00
|
|
|
return 0;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
static int
|
|
|
|
set_plane_prop_str(struct liftoff_plane *plane, drmModeAtomicReq *req,
|
|
|
|
const char *name, uint64_t value)
|
2019-10-19 12:43:09 +02:00
|
|
|
{
|
2023-02-15 18:47:04 +01:00
|
|
|
const drmModePropertyRes *prop;
|
2019-10-19 12:43:09 +02:00
|
|
|
|
|
|
|
prop = plane_get_property(plane, name);
|
|
|
|
if (prop == NULL) {
|
|
|
|
liftoff_log(LIFTOFF_DEBUG,
|
|
|
|
"plane %"PRIu32" is missing the %s property",
|
|
|
|
plane->id, name);
|
2021-07-27 12:28:00 +02:00
|
|
|
return -EINVAL;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return plane_set_prop(plane, req, prop, value);
|
|
|
|
}
|
|
|
|
|
2023-02-09 16:41:31 +01:00
|
|
|
bool
|
|
|
|
plane_check_layer_fb(struct liftoff_plane *plane, struct liftoff_layer *layer)
|
|
|
|
{
|
|
|
|
const struct drm_format_modifier_blob *set;
|
|
|
|
const uint32_t *formats;
|
|
|
|
const struct drm_format_modifier *modifiers;
|
|
|
|
size_t i;
|
|
|
|
ssize_t format_index, modifier_index;
|
|
|
|
int format_shift;
|
|
|
|
|
|
|
|
/* TODO: add support for legacy format list with implicit modifier */
|
|
|
|
if (layer->fb_info.fb_id == 0 ||
|
|
|
|
!(layer->fb_info.flags & DRM_MODE_FB_MODIFIERS) ||
|
|
|
|
plane->in_formats_blob == NULL) {
|
|
|
|
return true; /* not enough information to reject */
|
|
|
|
}
|
|
|
|
|
|
|
|
set = plane->in_formats_blob->data;
|
|
|
|
|
|
|
|
formats = (void *)((char *)set + set->formats_offset);
|
|
|
|
format_index = -1;
|
|
|
|
for (i = 0; i < set->count_formats; ++i) {
|
|
|
|
if (formats[i] == layer->fb_info.pixel_format) {
|
|
|
|
format_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (format_index < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
modifiers = (void *)((char *)set + set->modifiers_offset);
|
|
|
|
modifier_index = -1;
|
|
|
|
for (i = 0; i < set->count_modifiers; i++) {
|
|
|
|
if (modifiers[i].modifier == layer->fb_info.modifier) {
|
|
|
|
modifier_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (modifier_index < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format_index < modifiers[modifier_index].offset ||
|
|
|
|
format_index >= modifiers[modifier_index].offset + 64) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
format_shift = (int)(format_index - modifiers[modifier_index].offset);
|
|
|
|
return (modifiers[modifier_index].formats & ((uint64_t)1 << format_shift)) != 0;
|
|
|
|
}
|
|
|
|
|
2021-08-13 22:02:33 +02:00
|
|
|
int
|
|
|
|
plane_apply(struct liftoff_plane *plane, struct liftoff_layer *layer,
|
|
|
|
drmModeAtomicReq *req)
|
2019-10-19 12:43:09 +02:00
|
|
|
{
|
2021-07-27 12:28:00 +02:00
|
|
|
int cursor, ret;
|
2019-10-19 12:43:09 +02:00
|
|
|
size_t i;
|
|
|
|
struct liftoff_layer_property *layer_prop;
|
2023-02-15 18:47:04 +01:00
|
|
|
const drmModePropertyRes *plane_prop;
|
2019-10-19 12:43:09 +02:00
|
|
|
|
|
|
|
cursor = drmModeAtomicGetCursor(req);
|
|
|
|
|
|
|
|
if (layer == NULL) {
|
2021-07-27 12:28:00 +02:00
|
|
|
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);
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
|
2021-07-27 12:28:00 +02:00
|
|
|
ret = set_plane_prop_str(plane, req, "CRTC_ID", layer->output->crtc_id);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-05-22 17:40:33 +02:00
|
|
|
if (strcmp(layer_prop->name, "alpha") == 0 &&
|
|
|
|
layer_prop->value == 0xFFFF) {
|
|
|
|
continue; /* Layer is completely opaque */
|
|
|
|
}
|
2020-05-26 11:34:17 +02:00
|
|
|
if (strcmp(layer_prop->name, "rotation") == 0 &&
|
|
|
|
layer_prop->value == DRM_MODE_ROTATE_0) {
|
|
|
|
continue; /* Layer isn't rotated */
|
|
|
|
}
|
2022-08-03 22:51:05 +02:00
|
|
|
if (strcmp(layer_prop->name, "SCALING_FILTER") == 0 &&
|
|
|
|
layer_prop->value == 0) {
|
|
|
|
continue; /* Layer uses default scaling filter */
|
|
|
|
}
|
2022-08-03 23:00:13 +02:00
|
|
|
if (strcmp(layer_prop->name, "pixel blend mode") == 0 &&
|
|
|
|
layer_prop->value == 0) {
|
|
|
|
continue; /* Layer uses pre-multiplied alpha */
|
|
|
|
}
|
2022-08-03 22:54:30 +02:00
|
|
|
if (strcmp(layer_prop->name, "FB_DAMAGE_CLIPS") == 0) {
|
|
|
|
continue; /* Damage can be omitted */
|
|
|
|
}
|
2019-10-19 12:43:09 +02:00
|
|
|
drmModeAtomicSetCursor(req, cursor);
|
2021-07-27 12:28:00 +02:00
|
|
|
return -EINVAL;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
|
2021-07-27 12:28:00 +02:00
|
|
|
ret = plane_set_prop(plane, req, plane_prop, layer_prop->value);
|
|
|
|
if (ret != 0) {
|
2019-10-19 12:43:09 +02:00
|
|
|
drmModeAtomicSetCursor(req, cursor);
|
2021-07-27 12:28:00 +02:00
|
|
|
return ret;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-27 12:28:00 +02:00
|
|
|
return 0;
|
2019-10-19 12:43:09 +02:00
|
|
|
}
|