blob: 042a7c263aab7b79d5031144a5be3f154b734c18 [file] [log] [blame]
/*
* Copyright © 2015 Intel Corporation
* Copyright © 2014-2015 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Micah Fedke <micah.fedke@collabora.co.uk>
* Daniel Stone <daniels@collabora.com>
* Pekka Paalanen <pekka.paalanen@collabora.co.uk>
*/
/*
* Testcase: testing atomic modesetting API
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <xf86drmMode.h>
#include <cairo.h>
#include "drm.h"
#include "ioctl_wrappers.h"
#include "drmtest.h"
#include "igt.h"
#include "igt_aux.h"
#include "sw_sync.h"
#ifndef DRM_CLIENT_CAP_ATOMIC
#define DRM_CLIENT_CAP_ATOMIC 3
#endif
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
#ifndef DRM_CAP_CURSOR_HEIGHT
#define DRM_CAP_CURSOR_HEIGHT 0x9
#endif
#ifndef DRM_MODE_ATOMIC_TEST_ONLY
#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
#define DRM_MODE_ATOMIC_NONBLOCK 0x0200
#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
struct drm_mode_atomic {
__u32 flags;
__u32 count_objs;
__u64 objs_ptr;
__u64 count_props_ptr;
__u64 props_ptr;
__u64 prop_values_ptr;
__u64 reserved;
__u64 user_data;
};
#endif
IGT_TEST_DESCRIPTION("Test atomic modesetting API");
enum kms_atomic_check_relax {
ATOMIC_RELAX_NONE = 0,
CRTC_RELAX_MODE = (1 << 0),
PLANE_RELAX_FB = (1 << 1)
};
/**
* KMS plane type enum
*
* KMS plane types are represented by enums, which do not have stable numeric
* values, but must be looked up by their string value each time.
*
* To make the code more simple, we define a plane_type enum which maps to
* each KMS enum value. These values must be looked up through the map, and
* cannot be passed directly to KMS functions.
*/
enum plane_type {
PLANE_TYPE_PRIMARY = 0,
PLANE_TYPE_OVERLAY,
PLANE_TYPE_CURSOR,
NUM_PLANE_TYPE_PROPS
};
static const char *plane_type_prop_names[NUM_PLANE_TYPE_PROPS] = {
"Primary",
"Overlay",
"Cursor"
};
struct kms_atomic_blob {
uint32_t id; /* 0 if not already allocated */
size_t len;
void *data;
};
struct kms_atomic_connector_state {
struct kms_atomic_state *state;
uint32_t obj;
uint32_t crtc_id;
};
struct kms_atomic_plane_state {
struct kms_atomic_state *state;
uint32_t obj;
enum plane_type type;
uint32_t crtc_mask;
uint32_t crtc_id; /* 0 to disable */
uint32_t fb_id; /* 0 to disable */
uint32_t src_x, src_y, src_w, src_h; /* 16.16 fixed-point */
uint32_t crtc_x, crtc_y, crtc_w, crtc_h; /* normal integers */
int32_t fence_fd;
};
struct kms_atomic_crtc_state {
struct kms_atomic_state *state;
uint32_t obj;
int idx;
bool active;
int32_t *out_fence_ptr;
struct kms_atomic_blob mode;
};
struct kms_atomic_state {
struct kms_atomic_connector_state *connectors;
int num_connectors;
struct kms_atomic_crtc_state *crtcs;
int num_crtcs;
struct kms_atomic_plane_state *planes;
int num_planes;
struct kms_atomic_desc *desc;
};
struct kms_atomic_desc {
int fd;
uint32_t props_connector[IGT_NUM_CONNECTOR_PROPS];
uint32_t props_crtc[IGT_NUM_CRTC_PROPS];
uint32_t props_plane[IGT_NUM_PLANE_PROPS];
uint64_t props_plane_type[NUM_PLANE_TYPE_PROPS];
};
static uint32_t blob_duplicate(int fd, uint32_t id_orig)
{
drmModePropertyBlobPtr orig = drmModeGetPropertyBlob(fd, id_orig);
uint32_t id_new;
igt_assert(orig);
do_or_die(drmModeCreatePropertyBlob(fd, orig->data, orig->length,
&id_new));
drmModeFreePropertyBlob(orig);
return id_new;
}
#define crtc_set_prop(req, crtc, prop, value) \
igt_assert_lt(0, drmModeAtomicAddProperty(req, crtc->obj, \
crtc->state->desc->props_crtc[prop], \
value));
#define plane_set_prop(req, plane, prop, value) \
igt_assert_lt(0, drmModeAtomicAddProperty(req, plane->obj, \
plane->state->desc->props_plane[prop], \
value));
#define do_atomic_commit(fd, req, flags) \
do_or_die(drmModeAtomicCommit(fd, req, flags, NULL));
#define do_atomic_commit_err(fd, req, flags, err) { \
igt_assert_neq(drmModeAtomicCommit(fd, req, flags, NULL), 0); \
igt_assert_eq(errno, err); \
}
#define crtc_commit_atomic(crtc, plane, req, relax, flags) { \
drmModeAtomicSetCursor(req, 0); \
crtc_populate_req(crtc, req); \
plane_populate_req(plane, req); \
do_atomic_commit((crtc)->state->desc->fd, req, flags); \
if (!(flags & DRM_MODE_ATOMIC_TEST_ONLY)) { \
crtc_check_current_state(crtc, plane, relax); \
plane_check_current_state(plane, relax); \
} \
}
#define crtc_commit_atomic_err(crtc, plane, crtc_old, plane_old, req, flags, relax, e) { \
drmModeAtomicSetCursor(req, 0); \
crtc_populate_req(crtc, req); \
plane_populate_req(plane, req); \
do_atomic_commit_err((crtc)->state->desc->fd, req, flags, e); \
crtc_check_current_state(crtc_old, plane_old, relax); \
plane_check_current_state(plane_old, relax); \
}
#define plane_commit_atomic(plane, req, relax) { \
drmModeAtomicSetCursor(req, 0); \
plane_populate_req(plane, req); \
do_atomic_commit((plane)->state->desc->fd, req, 0); \
plane_check_current_state(plane, relax); \
}
#define plane_commit_atomic_err(plane, plane_old, req, relax, e) { \
drmModeAtomicSetCursor(req, 0); \
plane_populate_req(plane, req); \
do_atomic_commit_err((plane)->state->desc->fd, req, 0, e); \
plane_check_current_state(plane_old, relax); \
}
static void
connector_get_current_state(struct kms_atomic_connector_state *connector)
{
drmModeObjectPropertiesPtr props;
int i;
props = drmModeObjectGetProperties(connector->state->desc->fd,
connector->obj,
DRM_MODE_OBJECT_CONNECTOR);
igt_assert(props);
for (i = 0; i < props->count_props; i++) {
uint32_t *prop_ids = connector->state->desc->props_connector;
if (props->props[i] == prop_ids[IGT_CONNECTOR_CRTC_ID])
connector->crtc_id = props->prop_values[i];
}
drmModeFreeObjectProperties(props);
}
#if 0
/* XXX: Checking this repeatedly actually hangs the GPU. I have literally no
* idea why. */
static void
connector_check_current_state(struct kms_atomic_connector_state *connector)
{
struct kms_atomic_connector_state connector_kernel;
drmModeConnectorPtr legacy;
uint32_t crtc_id;
legacy = drmModeGetConnectorCurrent(connector->state->desc->fd,
connector->obj);
igt_assert(legacy);
if (legacy->encoder_id) {
drmModeEncoderPtr legacy_enc;
legacy_enc = drmModeGetEncoder(connector->state->desc->fd,
legacy->encoder_id);
igt_assert(legacy_enc);
crtc_id = legacy_enc->crtc_id;
drmModeFreeEncoder(legacy_enc);
} else {
crtc_id = 0;
}
igt_assert_eq_u32(crtc_id, connector->crtc_id);
memcpy(&connector_kernel, connector, sizeof(connector_kernel));
connector_get_current_state(&connector_kernel);
do_or_die(memcmp(&connector_kernel, connector,
sizeof(connector_kernel)));
drmModeFreeConnector(legacy);
}
#endif
static struct kms_atomic_connector_state *
find_connector(struct kms_atomic_state *state,
struct kms_atomic_crtc_state *crtc)
{
int i;
for (i = 0; i < state->num_connectors; i++) {
struct kms_atomic_connector_state *connector =
&state->connectors[i];
if (!connector->obj)
continue;
if (crtc && connector->crtc_id != crtc->obj)
continue;
return connector;
}
return NULL;
}
static void plane_populate_req(struct kms_atomic_plane_state *plane,
drmModeAtomicReq *req)
{
if (plane->fence_fd)
plane_set_prop(req, plane, IGT_PLANE_IN_FENCE_FD, plane->fence_fd);
plane_set_prop(req, plane, IGT_PLANE_CRTC_ID, plane->crtc_id);
plane_set_prop(req, plane, IGT_PLANE_FB_ID, plane->fb_id);
plane_set_prop(req, plane, IGT_PLANE_SRC_X, plane->src_x);
plane_set_prop(req, plane, IGT_PLANE_SRC_Y, plane->src_y);
plane_set_prop(req, plane, IGT_PLANE_SRC_W, plane->src_w);
plane_set_prop(req, plane, IGT_PLANE_SRC_H, plane->src_h);
plane_set_prop(req, plane, IGT_PLANE_CRTC_X, plane->crtc_x);
plane_set_prop(req, plane, IGT_PLANE_CRTC_Y, plane->crtc_y);
plane_set_prop(req, plane, IGT_PLANE_CRTC_W, plane->crtc_w);
plane_set_prop(req, plane, IGT_PLANE_CRTC_H, plane->crtc_h);
}
static void plane_get_current_state(struct kms_atomic_plane_state *plane)
{
struct kms_atomic_desc *desc = plane->state->desc;
drmModeObjectPropertiesPtr props;
int i;
props = drmModeObjectGetProperties(desc->fd, plane->obj,
DRM_MODE_OBJECT_PLANE);
igt_assert(props);
for (i = 0; i < props->count_props; i++) {
uint32_t *prop_ids = desc->props_plane;
if (props->props[i] == prop_ids[IGT_PLANE_CRTC_ID])
plane->crtc_id = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_FB_ID])
plane->fb_id = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_CRTC_X])
plane->crtc_x = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_CRTC_Y])
plane->crtc_y = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_CRTC_W])
plane->crtc_w = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_CRTC_H])
plane->crtc_h = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_SRC_X])
plane->src_x = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_SRC_Y])
plane->src_y = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_SRC_W])
plane->src_w = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_SRC_H])
plane->src_h = props->prop_values[i];
else if (props->props[i] == prop_ids[IGT_PLANE_TYPE]) {
int j;
for (j = 0; j < ARRAY_SIZE(desc->props_plane_type); j++) {
if (props->prop_values[i] == desc->props_plane_type[j]) {
plane->type = j;
break;
}
}
}
}
drmModeFreeObjectProperties(props);
}
static void plane_check_current_state(struct kms_atomic_plane_state *plane,
enum kms_atomic_check_relax relax)
{
drmModePlanePtr legacy;
struct kms_atomic_plane_state plane_kernel;
legacy = drmModeGetPlane(plane->state->desc->fd, plane->obj);
igt_assert(legacy);
igt_assert_eq_u32(legacy->crtc_id, plane->crtc_id);
if (!(relax & PLANE_RELAX_FB))
igt_assert_eq_u32(legacy->fb_id, plane->fb_id);
memcpy(&plane_kernel, plane, sizeof(plane_kernel));
plane_get_current_state(&plane_kernel);
/* Legacy cursor ioctls create their own, unknowable, internal
* framebuffer which we can't reason about. */
if (relax & PLANE_RELAX_FB)
plane_kernel.fb_id = plane->fb_id;
do_or_die(memcmp(&plane_kernel, plane, sizeof(plane_kernel)));
drmModeFreePlane(legacy);
}
static void plane_commit_legacy(struct kms_atomic_plane_state *plane,
enum kms_atomic_check_relax relax)
{
do_or_die(drmModeSetPlane(plane->state->desc->fd, plane->obj,
plane->crtc_id,
plane->fb_id, 0,
plane->crtc_x, plane->crtc_y,
plane->crtc_w, plane->crtc_h,
plane->src_x, plane->src_y,
plane->src_w, plane->src_h));
plane_check_current_state(plane, relax);
}
static struct kms_atomic_plane_state *
find_plane(struct kms_atomic_state *state, enum plane_type type,
struct kms_atomic_crtc_state *crtc)
{
struct kms_atomic_plane_state *ret = NULL;
int i;
for (i = 0; i < state->num_planes; i++) {
struct kms_atomic_plane_state *plane = &state->planes[i];
if (!plane->obj)
continue;
if (type != NUM_PLANE_TYPE_PROPS && plane->type != type)
continue;
if (crtc && !(plane->crtc_mask & (1 << crtc->idx)))
continue;
plane_get_current_state(plane);
/* Try to find a plane that's already on this CRTC. In
* particular, this ensures that for special (primary/cursor)
* planes that can be on multiple CRTCs, we find the same
* one that the legacy ioctls will. */
if (!crtc || plane->crtc_id == crtc->obj)
return plane;
ret = plane;
}
return ret;
}
static void crtc_populate_req(struct kms_atomic_crtc_state *crtc,
drmModeAtomicReq *req)
{
if (crtc->out_fence_ptr)
crtc_set_prop(req, crtc, IGT_CRTC_OUT_FENCE_PTR,
to_user_pointer(crtc->out_fence_ptr));
crtc_set_prop(req, crtc, IGT_CRTC_MODE_ID, crtc->mode.id);
crtc_set_prop(req, crtc, IGT_CRTC_ACTIVE, crtc->active);
}
static void crtc_get_current_state(struct kms_atomic_crtc_state *crtc)
{
drmModeObjectPropertiesPtr props;
int i;
props = drmModeObjectGetProperties(crtc->state->desc->fd, crtc->obj,
DRM_MODE_OBJECT_CRTC);
igt_assert(props);
for (i = 0; i < props->count_props; i++) {
uint32_t *prop_ids = crtc->state->desc->props_crtc;
if (props->props[i] == prop_ids[IGT_CRTC_MODE_ID]) {
drmModePropertyBlobPtr blob;
crtc->mode.id = props->prop_values[i];
if (!crtc->mode.id) {
crtc->mode.len = 0;
continue;
}
blob = drmModeGetPropertyBlob(crtc->state->desc->fd,
crtc->mode.id);
igt_assert(blob);
igt_assert_eq_u32(blob->length,
sizeof(struct drm_mode_modeinfo));
if (!crtc->mode.data ||
memcmp(crtc->mode.data, blob->data, blob->length) != 0)
crtc->mode.data = blob->data;
crtc->mode.len = blob->length;
}
else if (props->props[i] == prop_ids[IGT_CRTC_ACTIVE]) {
crtc->active = props->prop_values[i];
}
}
drmModeFreeObjectProperties(props);
}
static void crtc_check_current_state(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *primary,
enum kms_atomic_check_relax relax)
{
struct kms_atomic_crtc_state crtc_kernel;
drmModeCrtcPtr legacy;
legacy = drmModeGetCrtc(crtc->state->desc->fd, crtc->obj);
igt_assert(legacy);
igt_assert_eq_u32(legacy->crtc_id, crtc->obj);
igt_assert_eq_u32(legacy->x, primary->src_x >> 16);
igt_assert_eq_u32(legacy->y, primary->src_y >> 16);
if (crtc->active)
igt_assert_eq_u32(legacy->buffer_id, primary->fb_id);
else
igt_assert_eq_u32(legacy->buffer_id, 0);
if (legacy->mode_valid) {
igt_assert_neq(legacy->mode_valid, 0);
igt_assert_eq(crtc->mode.len,
sizeof(struct drm_mode_modeinfo));
do_or_die(memcmp(&legacy->mode, crtc->mode.data,
crtc->mode.len));
igt_assert_eq(legacy->width, legacy->mode.hdisplay);
igt_assert_eq(legacy->height, legacy->mode.vdisplay);
} else {
igt_assert_eq(legacy->mode_valid, 0);
}
memcpy(&crtc_kernel, crtc, sizeof(crtc_kernel));
crtc_get_current_state(&crtc_kernel);
if (crtc_kernel.mode.id != 0)
igt_assert_eq(crtc_kernel.mode.len,
sizeof(struct drm_mode_modeinfo));
/* Optionally relax the check for MODE_ID: using the legacy SetCrtc
* API can potentially change MODE_ID even if the mode itself remains
* unchanged. */
if (((relax & CRTC_RELAX_MODE) &&
(crtc_kernel.mode.id != crtc->mode.id &&
crtc_kernel.mode.id != 0 && crtc->mode.id != 0)) &&
memcmp(crtc_kernel.mode.data, crtc->mode.data,
sizeof(struct drm_mode_modeinfo)) == 0) {
crtc_kernel.mode.id = crtc->mode.id;
crtc_kernel.mode.data = crtc->mode.data;
}
do_or_die(memcmp(&crtc_kernel, crtc, sizeof(crtc_kernel)));
drmModeFreeCrtc(legacy);
}
static void crtc_commit_legacy(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane,
enum kms_atomic_check_relax relax)
{
drmModeObjectPropertiesPtr props;
uint32_t *connectors;
int num_connectors = 0;
int i;
if (!crtc->active) {
do_or_die(drmModeSetCrtc(crtc->state->desc->fd,
crtc->obj, 0, 0, 0, NULL, 0, NULL));
return;
}
connectors = calloc(crtc->state->num_connectors,
sizeof(*connectors));
igt_assert(connectors);
igt_assert_neq_u32(crtc->mode.id, 0);
for (i = 0; i < crtc->state->num_connectors; i++) {
struct kms_atomic_connector_state *connector =
&crtc->state->connectors[i];
if (connector->crtc_id != crtc->obj)
continue;
connectors[num_connectors++] = connector->obj;
}
do_or_die(drmModeSetCrtc(crtc->state->desc->fd, crtc->obj,
plane->fb_id,
plane->src_x >> 16, plane->src_y >> 16,
(num_connectors) ? connectors : NULL,
num_connectors,
crtc->mode.data));
/* When doing a legacy commit, the core may update MODE_ID to be a new
* blob implicitly created by the legacy request. Hence we backfill
* the value in the state object to ensure they match. */
props = drmModeObjectGetProperties(crtc->state->desc->fd, crtc->obj,
DRM_MODE_OBJECT_CRTC);
igt_assert(props);
for (i = 0; i < props->count_props; i++) {
if (props->props[i] !=
crtc->state->desc->props_crtc[IGT_CRTC_MODE_ID])
continue;
crtc->mode.id = props->prop_values[i];
break;
}
drmModeFreeObjectProperties(props);
crtc_check_current_state(crtc, plane, relax);
plane_check_current_state(plane, relax);
}
static struct kms_atomic_crtc_state *find_crtc(struct kms_atomic_state *state,
bool must_be_enabled)
{
int i;
for (i = 0; i < state->num_crtcs; i++) {
struct kms_atomic_crtc_state *crtc = &state->crtcs[i];
if (!crtc->obj)
continue;
if (must_be_enabled && !crtc->active)
continue;
crtc_get_current_state(crtc);
return crtc;
}
return NULL;
}
static void fill_obj_props(int fd, uint32_t id, int type, int num_props,
const char **prop_names, uint32_t *prop_ids)
{
drmModeObjectPropertiesPtr props;
int i, j;
props = drmModeObjectGetProperties(fd, id, type);
igt_assert(props);
for (i = 0; i < props->count_props; i++) {
drmModePropertyPtr prop =
drmModeGetProperty(fd, props->props[i]);
for (j = 0; j < num_props; j++) {
if (strcmp(prop->name, prop_names[j]) != 0)
continue;
prop_ids[j] = props->props[i];
break;
}
drmModeFreeProperty(prop);
}
drmModeFreeObjectProperties(props);
}
static void fill_obj_prop_map(int fd, uint32_t id, int type, const char *name,
int num_enums, const char **enum_names,
uint64_t *enum_ids)
{
drmModeObjectPropertiesPtr props;
int i, j, k;
props = drmModeObjectGetProperties(fd, id, type);
igt_assert(props);
for (i = 0; i < props->count_props; i++) {
drmModePropertyPtr prop =
drmModeGetProperty(fd, props->props[i]);
igt_assert(prop);
if (strcmp(prop->name, name) != 0) {
drmModeFreeProperty(prop);
continue;
}
for (j = 0; j < prop->count_enums; j++) {
struct drm_mode_property_enum *e = &prop->enums[j];
for (k = 0; k < num_enums; k++) {
if (strcmp(e->name, enum_names[k]) != 0)
continue;
enum_ids[k] = e->value;
break;
}
}
drmModeFreeProperty(prop);
}
}
static void atomic_setup(struct kms_atomic_state *state)
{
struct kms_atomic_desc *desc = state->desc;
drmModeResPtr res;
drmModePlaneResPtr res_plane;
int i;
desc->fd = drm_open_driver_master(DRIVER_ANY);
igt_assert_fd(desc->fd);
igt_skip_on(drmSetClientCap(desc->fd, DRM_CLIENT_CAP_ATOMIC, 1));
res = drmModeGetResources(desc->fd);
res_plane = drmModeGetPlaneResources(desc->fd);
igt_assert(res);
igt_assert(res_plane);
igt_assert_lt(0, res->count_crtcs);
state->num_crtcs = res->count_crtcs;
state->crtcs = calloc(state->num_crtcs, sizeof(*state->crtcs));
igt_assert(state->crtcs);
igt_assert_lt(0, res_plane->count_planes);
state->num_planes = res_plane->count_planes;
state->planes = calloc(state->num_planes, sizeof(*state->planes));
igt_assert(state->planes);
igt_assert_lt(0, res->count_connectors);
state->num_connectors = res->count_connectors;
state->connectors = calloc(state->num_connectors,
sizeof(*state->connectors));
igt_assert(state->connectors);
fill_obj_props(desc->fd, res->crtcs[0],
DRM_MODE_OBJECT_CRTC, IGT_NUM_CRTC_PROPS,
igt_crtc_prop_names, desc->props_crtc);
fill_obj_props(desc->fd, res_plane->planes[0],
DRM_MODE_OBJECT_PLANE, IGT_NUM_PLANE_PROPS,
igt_plane_prop_names, desc->props_plane);
fill_obj_prop_map(desc->fd, res_plane->planes[0],
DRM_MODE_OBJECT_PLANE, "type",
NUM_PLANE_TYPE_PROPS, plane_type_prop_names,
desc->props_plane_type);
fill_obj_props(desc->fd, res->connectors[0],
DRM_MODE_OBJECT_CONNECTOR, IGT_NUM_CONNECTOR_PROPS,
igt_connector_prop_names, desc->props_connector);
for (i = 0; i < state->num_crtcs; i++) {
struct kms_atomic_crtc_state *crtc = &state->crtcs[i];
crtc->state = state;
crtc->obj = res->crtcs[i];
crtc->idx = i;
crtc_get_current_state(crtc);
/* The blob pointed to by MODE_ID could well be transient,
* and lose its last reference as we switch away from it.
* Duplicate the blob here so we have a reference we know we
* own. */
if (crtc->mode.id != 0)
crtc->mode.id = blob_duplicate(desc->fd, crtc->mode.id);
}
for (i = 0; i < state->num_planes; i++) {
drmModePlanePtr plane =
drmModeGetPlane(desc->fd, res_plane->planes[i]);
igt_assert(plane);
state->planes[i].state = state;
state->planes[i].obj = res_plane->planes[i];
state->planes[i].crtc_mask = plane->possible_crtcs;
plane_get_current_state(&state->planes[i]);
}
for (i = 0; i < state->num_connectors; i++) {
state->connectors[i].state = state;
state->connectors[i].obj = res->connectors[i];
connector_get_current_state(&state->connectors[i]);
}
drmModeFreePlaneResources(res_plane);
drmModeFreeResources(res);
}
static struct kms_atomic_state *
atomic_state_dup(const struct kms_atomic_state *state)
{
struct kms_atomic_state *ret = calloc(1, sizeof(*ret));
igt_assert(ret);
*ret = *state;
ret->crtcs = calloc(ret->num_crtcs, sizeof(*ret->crtcs));
igt_assert(ret->crtcs);
memcpy(ret->crtcs, state->crtcs, ret->num_crtcs * sizeof(*ret->crtcs));
ret->planes = calloc(ret->num_planes, sizeof(*ret->planes));
igt_assert(ret->planes);
memcpy(ret->planes, state->planes,
ret->num_planes * sizeof(*ret->planes));
ret->connectors = calloc(ret->num_connectors, sizeof(*ret->connectors));
igt_assert(ret->connectors);
memcpy(ret->connectors, state->connectors,
ret->num_connectors * sizeof(*ret->connectors));
return ret;
}
static void atomic_state_free(struct kms_atomic_state *state)
{
free(state->crtcs);
free(state->planes);
free(state->connectors);
free(state);
}
static uint32_t plane_get_igt_format(struct kms_atomic_plane_state *plane)
{
drmModePlanePtr plane_kms;
const uint32_t *igt_formats;
uint32_t ret = 0;
int num_igt_formats;
int i;
plane_kms = drmModeGetPlane(plane->state->desc->fd, plane->obj);
igt_assert(plane_kms);
igt_get_all_cairo_formats(&igt_formats, &num_igt_formats);
for (i = 0; i < num_igt_formats; i++) {
int j;
for (j = 0; j < plane_kms->count_formats; j++) {
if (plane_kms->formats[j] == igt_formats[i]) {
ret = plane_kms->formats[j];
break;
}
}
}
drmModeFreePlane(plane_kms);
return ret;
}
static void
set_dpms(int fd, int mode)
{
int i;
drmModeConnector *connector;
uint32_t id;
drmModeRes *resources = drmModeGetResources(fd);
for (i = 0; i < resources->count_connectors; i++) {
id = resources->connectors[i];
connector = drmModeGetConnectorCurrent(fd, id);
kmstest_set_connector_dpms(fd, connector, mode);
drmModeFreeConnector(connector);
}
}
static void plane_overlay(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane_old)
{
struct drm_mode_modeinfo *mode = crtc->mode.data;
struct kms_atomic_plane_state plane = *plane_old;
uint32_t format = plane_get_igt_format(&plane);
drmModeAtomicReq *req = drmModeAtomicAlloc();
struct igt_fb fb;
igt_require(req);
igt_require(format != 0);
plane.src_x = 0;
plane.src_y = 0;
plane.src_w = (mode->hdisplay / 2) << 16;
plane.src_h = (mode->vdisplay / 2) << 16;
plane.crtc_x = mode->hdisplay / 4;
plane.crtc_y = mode->vdisplay / 4;
plane.crtc_w = mode->hdisplay / 2;
plane.crtc_h = mode->vdisplay / 2;
plane.crtc_id = crtc->obj;
plane.fb_id = igt_create_pattern_fb(plane.state->desc->fd,
plane.crtc_w, plane.crtc_h,
format, I915_TILING_NONE, &fb);
/* Enable the overlay plane using the atomic API, and double-check
* state is what we think it should be. */
plane_commit_atomic(&plane, req, ATOMIC_RELAX_NONE);
/* Disable the plane and check the state matches the old. */
plane_commit_atomic(plane_old, req, ATOMIC_RELAX_NONE);
/* Re-enable the plane through the legacy plane API, and verify through
* atomic. */
plane_commit_legacy(&plane, ATOMIC_RELAX_NONE);
/* Restore the plane to its original settings through the legacy plane
* API, and verify through atomic. */
plane_commit_legacy(plane_old, ATOMIC_RELAX_NONE);
drmModeAtomicFree(req);
}
static void plane_primary(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane_old)
{
struct drm_mode_modeinfo *mode = crtc->mode.data;
struct kms_atomic_plane_state plane = *plane_old;
uint32_t format = plane_get_igt_format(&plane);
drmModeAtomicReq *req = drmModeAtomicAlloc();
struct igt_fb fb;
uint32_t flags = 0;
int ret;
igt_require(format != 0);
plane.src_x = 0;
plane.src_y = 0;
plane.src_w = mode->hdisplay << 16;
plane.src_h = mode->vdisplay << 16;
plane.crtc_x = 0;
plane.crtc_y = 0;
plane.crtc_w = mode->hdisplay;
plane.crtc_h = mode->vdisplay;
plane.crtc_id = crtc->obj;
plane.fb_id = igt_create_pattern_fb(plane.state->desc->fd,
plane.crtc_w, plane.crtc_h,
format, I915_TILING_NONE, &fb);
drmModeAtomicSetCursor(req, 0);
crtc_populate_req(crtc, req);
plane_populate_req(&plane, req);
ret = drmModeAtomicCommit(crtc->state->desc->fd, req,
DRM_MODE_ATOMIC_TEST_ONLY, NULL);
/* Try harder in case the failure is caused by disallowing modeset. */
if (ret == -EINVAL)
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
/* Flip the primary plane using the atomic API, and double-check
* state is what we think it should be. */
crtc_commit_atomic(crtc, &plane, req, ATOMIC_RELAX_NONE, flags);
/* Restore the primary plane and check the state matches the old. */
crtc_commit_atomic(crtc, plane_old, req, ATOMIC_RELAX_NONE, flags);
/* Re-enable the plane through the legacy CRTC/primary-plane API, and
* verify through atomic. */
crtc_commit_legacy(crtc, &plane, CRTC_RELAX_MODE);
/* Restore the plane to its original settings through the legacy CRTC
* API, and verify through atomic. */
crtc_commit_legacy(crtc, plane_old, CRTC_RELAX_MODE);
/* Finally, restore to the original state. */
crtc_commit_atomic(crtc, plane_old, req, ATOMIC_RELAX_NONE, flags);
drmModeAtomicFree(req);
}
/* test to ensure that DRM_MODE_ATOMIC_TEST_ONLY really only touches the
* free-standing state objects and nothing else.
*/
static void test_only(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane_old)
{
struct drm_mode_modeinfo *mode = crtc->mode.data;
struct kms_atomic_plane_state plane = *plane_old;
uint32_t format = plane_get_igt_format(&plane);
drmModeAtomicReq *req = drmModeAtomicAlloc();
struct igt_fb fb;
int ret;
igt_require(format != 0);
plane.src_x = 0;
plane.src_y = 0;
plane.src_w = mode->hdisplay << 16;
plane.src_h = mode->vdisplay << 16;
plane.crtc_x = 0;
plane.crtc_y = 0;
plane.crtc_w = mode->hdisplay;
plane.crtc_h = mode->vdisplay;
plane.crtc_id = crtc->obj;
plane.fb_id = igt_create_pattern_fb(plane.state->desc->fd,
plane.crtc_w, plane.crtc_h,
format, I915_TILING_NONE, &fb);
drmModeAtomicSetCursor(req, 0);
crtc_populate_req(crtc, req);
plane_populate_req(&plane, req);
ret = drmModeAtomicCommit(crtc->state->desc->fd, req,
DRM_MODE_ATOMIC_TEST_ONLY, NULL);
igt_assert_eq(ret, 0);
/* go through dpms off/on cycle */
set_dpms(crtc->state->desc->fd, DRM_MODE_DPMS_OFF);
set_dpms(crtc->state->desc->fd, DRM_MODE_DPMS_ON);
/* check the state */
crtc_check_current_state(crtc, plane_old, ATOMIC_RELAX_NONE);
plane_check_current_state(plane_old, ATOMIC_RELAX_NONE);
/* Re-enable the plane through the legacy CRTC/primary-plane API, and
* verify through atomic. */
crtc_commit_legacy(crtc, plane_old, CRTC_RELAX_MODE);
drmModeAtomicFree(req);
}
static void plane_cursor(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane_old)
{
struct drm_mode_modeinfo *mode = crtc->mode.data;
struct kms_atomic_plane_state plane = *plane_old;
drmModeAtomicReq *req = drmModeAtomicAlloc();
struct igt_fb fb;
uint64_t width, height;
igt_assert(req);
/* Any kernel new enough for atomic, also has the cursor size caps. */
do_or_die(drmGetCap(plane.state->desc->fd,
DRM_CAP_CURSOR_WIDTH, &width));
do_or_die(drmGetCap(plane.state->desc->fd,
DRM_CAP_CURSOR_HEIGHT, &height));
plane.src_x = 0;
plane.src_y = 0;
plane.src_w = width << 16;
plane.src_h = height << 16;
plane.crtc_x = mode->hdisplay / 2;
plane.crtc_y = mode->vdisplay / 2;
plane.crtc_w = width;
plane.crtc_h = height;
plane.crtc_id = crtc->obj;
plane.fb_id = igt_create_color_fb(plane.state->desc->fd,
width, height,
DRM_FORMAT_ARGB8888,
LOCAL_DRM_FORMAT_MOD_NONE,
0.0, 0.0, 0.0,
&fb);
igt_assert_neq_u32(plane.fb_id, 0);
/* Flip the cursor plane using the atomic API, and double-check
* state is what we think it should be. */
plane_commit_atomic(&plane, req, ATOMIC_RELAX_NONE);
/* Restore the cursor plane and check the state matches the old. */
plane_commit_atomic(plane_old, req, ATOMIC_RELAX_NONE);
/* Re-enable the plane through the legacy cursor API, and verify
* through atomic. */
do_or_die(drmModeMoveCursor(plane.state->desc->fd, plane.crtc_id,
plane.crtc_x, plane.crtc_y));
do_or_die(drmModeSetCursor(plane.state->desc->fd, plane.crtc_id,
fb.gem_handle, width, height));
plane_check_current_state(&plane, PLANE_RELAX_FB);
/* Wiggle. */
plane.crtc_x -= 16;
plane.crtc_y -= 16;
do_or_die(drmModeMoveCursor(plane.state->desc->fd, plane.crtc_id,
plane.crtc_x, plane.crtc_y));
plane_check_current_state(&plane, PLANE_RELAX_FB);
/* Restore the plane to its original settings through the legacy cursor
* API, and verify through atomic. */
do_or_die(drmModeSetCursor2(plane.state->desc->fd, plane.crtc_id,
0, 0, 0, 0, 0));
plane_check_current_state(plane_old, ATOMIC_RELAX_NONE);
/* Finally, restore to the original state. */
plane_commit_atomic(plane_old, req, ATOMIC_RELAX_NONE);
drmModeAtomicFree(req);
}
static void plane_invalid_params(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane_old,
struct kms_atomic_connector_state *conn)
{
struct drm_mode_modeinfo *mode = crtc->mode.data;
struct kms_atomic_plane_state plane = *plane_old;
uint32_t format = plane_get_igt_format(&plane);
drmModeAtomicReq *req = drmModeAtomicAlloc();
struct igt_fb fb;
/* Pass a series of invalid object IDs for the FB ID. */
plane.fb_id = plane.obj;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.fb_id = crtc->obj;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.fb_id = conn->obj;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.fb_id = crtc->mode.id;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.fb_id = plane_old->fb_id;
plane_commit_atomic(&plane, req, ATOMIC_RELAX_NONE);
/* Pass a series of invalid object IDs for the CRTC ID. */
plane.crtc_id = plane.obj;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.crtc_id = plane.fb_id;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.crtc_id = conn->obj;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.crtc_id = crtc->mode.id;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.crtc_id = plane_old->crtc_id;
plane_commit_atomic(&plane, req, ATOMIC_RELAX_NONE);
/* Create a framebuffer too small for the plane configuration. */
igt_require(format != 0);
plane.src_x = 0;
plane.src_y = 0;
plane.src_w = mode->hdisplay << 16;
plane.src_h = mode->vdisplay << 16;
plane.crtc_x = 0;
plane.crtc_y = 0;
plane.crtc_w = mode->hdisplay;
plane.crtc_h = mode->vdisplay;
plane.crtc_id = crtc->obj;
plane.fb_id = igt_create_pattern_fb(plane.state->desc->fd,
plane.crtc_w - 1, plane.crtc_h - 1,
format, I915_TILING_NONE, &fb);
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, ENOSPC);
/* Restore the primary plane and check the state matches the old. */
plane_commit_atomic(plane_old, req, ATOMIC_RELAX_NONE);
drmModeAtomicFree(req);
}
static void plane_invalid_params_fence(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane_old,
struct kms_atomic_connector_state *conn)
{
struct kms_atomic_plane_state plane = *plane_old;
drmModeAtomicReq *req = drmModeAtomicAlloc();
int timeline, fence_fd;
igt_require_sw_sync();
/* invalid fence fd */
plane.fence_fd = plane.state->desc->fd;
plane.crtc_id = plane_old->crtc_id;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
/* Valid fence_fd but invalid CRTC */
timeline = sw_sync_timeline_create();
fence_fd = sw_sync_timeline_create_fence(timeline, 1);
plane.fence_fd = fence_fd;
plane.crtc_id = ~0U;
plane_commit_atomic_err(&plane, plane_old, req,
ATOMIC_RELAX_NONE, EINVAL);
plane.fence_fd = -1;
close(fence_fd);
close(timeline);
drmModeAtomicFree(req);
}
static void crtc_invalid_params(struct kms_atomic_crtc_state *crtc_old,
struct kms_atomic_plane_state *plane,
struct kms_atomic_connector_state *conn)
{
struct kms_atomic_crtc_state crtc = *crtc_old;
drmModeAtomicReq *req = drmModeAtomicAlloc();
igt_assert(req);
/* Pass a series of invalid object IDs for the mode ID. */
crtc.mode.id = plane->obj;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
crtc.mode.id = crtc.obj;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
crtc.mode.id = conn->obj;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
crtc.mode.id = plane->fb_id;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
/* successful TEST_ONLY with fences set */
crtc.mode.id = crtc_old->mode.id;
crtc_commit_atomic(&crtc, plane, req, ATOMIC_RELAX_NONE,
DRM_MODE_ATOMIC_TEST_ONLY);
/*
* TEST_ONLY cannot be combined with DRM_MODE_PAGE_FLIP_EVENT,
* but DRM_MODE_PAGE_FLIP_EVENT will always generate EINVAL
* without valid crtc, so test it here.
*/
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req,
DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_PAGE_FLIP_EVENT,
ATOMIC_RELAX_NONE, EINVAL);
/* Create a blob which is the wrong size to be a valid mode. */
do_or_die(drmModeCreatePropertyBlob(crtc.state->desc->fd,
crtc.mode.data,
sizeof(struct drm_mode_modeinfo) - 1,
&crtc.mode.id));
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
do_or_die(drmModeCreatePropertyBlob(crtc.state->desc->fd,
crtc.mode.data,
sizeof(struct drm_mode_modeinfo) + 1,
&crtc.mode.id));
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
/* Restore the CRTC and check the state matches the old. */
crtc_commit_atomic(crtc_old, plane, req, ATOMIC_RELAX_NONE, 0);
drmModeAtomicFree(req);
}
static void crtc_invalid_params_fence(struct kms_atomic_crtc_state *crtc_old,
struct kms_atomic_plane_state *plane,
struct kms_atomic_connector_state *conn)
{
struct kms_atomic_crtc_state crtc = *crtc_old;
drmModeAtomicReq *req = drmModeAtomicAlloc();
int timeline, fence_fd, *out_fence;
igt_require_sw_sync();
/* invalid out_fence_ptr */
crtc.mode.id = crtc_old->mode.id;
crtc.out_fence_ptr = (int32_t *) crtc_invalid_params;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EFAULT);
/* invalid out_fence_ptr */
crtc.mode.id = crtc_old->mode.id;
crtc.out_fence_ptr = (int32_t *) 0x8;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EFAULT);
crtc.out_fence_ptr = (int32_t *) 0;
/* valid in fence but not allowed prop on crtc */
timeline = sw_sync_timeline_create();
fence_fd = sw_sync_timeline_create_fence(timeline, 1);
plane->fence_fd = fence_fd;
crtc.active = false;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
out_fence = malloc(sizeof(uint64_t));
igt_assert(out_fence);
/* valid out fence ptr and flip event but not allowed prop on crtc */
crtc.out_fence_ptr = (int32_t *) out_fence;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
/* valid out fence ptr and flip event but not allowed prop on crtc */
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req,
DRM_MODE_PAGE_FLIP_EVENT,
ATOMIC_RELAX_NONE, EINVAL);
/* valid page flip event but not allowed prop on crtc */
crtc.out_fence_ptr = (int32_t *) 0;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req,
DRM_MODE_PAGE_FLIP_EVENT,
ATOMIC_RELAX_NONE, EINVAL);
crtc.active = true;
/* valid out fence ptr and flip event but invalid prop on crtc */
crtc.out_fence_ptr = (int32_t *) out_fence;
crtc.mode.id = plane->fb_id;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req, 0,
ATOMIC_RELAX_NONE, EINVAL);
/* valid out fence ptr and flip event but invalid prop on crtc */
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req,
DRM_MODE_PAGE_FLIP_EVENT,
ATOMIC_RELAX_NONE, EINVAL);
/* valid page flip event but invalid prop on crtc */
crtc.out_fence_ptr = (int32_t *) 0;
crtc_commit_atomic_err(&crtc, plane, crtc_old, plane, req,
DRM_MODE_PAGE_FLIP_EVENT,
ATOMIC_RELAX_NONE, EINVAL);
/* successful TEST_ONLY with fences set */
plane->fence_fd = fence_fd;
crtc.mode.id = crtc_old->mode.id;
crtc_commit_atomic(&crtc, plane, req, ATOMIC_RELAX_NONE,
DRM_MODE_ATOMIC_TEST_ONLY);
igt_assert(*out_fence == -1);
close(fence_fd);
close(timeline);
/* reset fences */
plane->fence_fd = -1;
crtc.out_fence_ptr = (int32_t *) 0;
crtc_commit_atomic(&crtc, plane, req, ATOMIC_RELAX_NONE, 0);
/* out fence ptr but not page flip event */
crtc.out_fence_ptr = (int32_t *) out_fence;
crtc_commit_atomic(crtc_old, plane, req, ATOMIC_RELAX_NONE, 0);
close(*out_fence);
free(out_fence);
drmModeAtomicFree(req);
}
/* Abuse the atomic ioctl directly in order to test various invalid conditions,
* which the libdrm wrapper won't allow us to create. */
static void atomic_invalid_params(struct kms_atomic_crtc_state *crtc,
struct kms_atomic_plane_state *plane,
struct kms_atomic_connector_state *connector)
{
struct kms_atomic_desc *desc = crtc->state->desc;
struct drm_mode_atomic ioc;
uint32_t obj_raw[16]; /* array of objects (sized by count_objs) */
uint32_t num_props_raw[16]; /* array of num props per obj (ditto) */
uint32_t props_raw[256]; /* array of props (sum of count_props) */
uint64_t values_raw[256]; /* array of values for properties (ditto) */
int i;
memset(&ioc, 0, sizeof(ioc));
/* An empty request should do nothing. */
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
for (i = 0; i < ARRAY_SIZE(obj_raw); i++)
obj_raw[i] = 0;
for (i = 0; i < ARRAY_SIZE(num_props_raw); i++)
num_props_raw[i] = 0;
for (i = 0; i < ARRAY_SIZE(props_raw); i++)
props_raw[i] = 0;
for (i = 0; i < ARRAY_SIZE(values_raw); i++)
values_raw[i] = 0;
ioc.objs_ptr = (uintptr_t) obj_raw;
ioc.count_props_ptr = (uintptr_t) num_props_raw;
ioc.props_ptr = (uintptr_t) props_raw;
ioc.prop_values_ptr = (uintptr_t) values_raw;
/* Valid pointers, but still should copy nothing. */
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Valid noop, but with event set should fail. */
ioc.flags = DRM_MODE_PAGE_FLIP_EVENT;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EINVAL);
/* Nonsense flags. */
ioc.flags = 0xdeadbeef;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EINVAL);
ioc.flags = 0;
/* Safety check that flags is reset properly. */
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Reserved/MBZ. */
ioc.reserved = 1;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EINVAL);
ioc.reserved = 0;
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Zero is not a valid object ID. */
ioc.count_objs = ARRAY_SIZE(obj_raw);
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
/* Invalid object type (not a thing we can set properties on). */
ioc.count_objs = 1;
obj_raw[0] = crtc->mode.id;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
obj_raw[0] = plane->fb_id;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
/* Filled object but with no properties; no-op. */
for (i = 0; i < ARRAY_SIZE(obj_raw); i++)
obj_raw[i] = crtc->obj;
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Pass in all sorts of things other than the property ID. */
num_props_raw[0] = 1;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
props_raw[0] = crtc->obj;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
props_raw[0] = plane->obj;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
props_raw[0] = connector->obj;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
props_raw[0] = crtc->mode.id;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
/* Valid property, valid value. */
for (i = 0; i < ARRAY_SIZE(props_raw); i++) {
props_raw[i] = desc->props_crtc[IGT_CRTC_MODE_ID];
values_raw[i] = crtc->mode.id;
}
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Setting the same thing multiple times is OK. */
for (i = 0; i < ARRAY_SIZE(obj_raw); i++)
num_props_raw[i] = ARRAY_SIZE(props_raw) / ARRAY_SIZE(obj_raw);
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
ioc.count_objs = ARRAY_SIZE(obj_raw);
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Pass a series of outlandish addresses. */
ioc.objs_ptr = 0;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
ioc.objs_ptr = (uintptr_t) obj_raw;
ioc.count_props_ptr = 0;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
ioc.count_props_ptr = (uintptr_t) num_props_raw;
ioc.props_ptr = 0;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
ioc.props_ptr = (uintptr_t) props_raw;
ioc.prop_values_ptr = 0;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
ioc.prop_values_ptr = (uintptr_t) values_raw;
do_ioctl(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc);
/* Attempt to overflow and/or trip various boundary conditions. */
ioc.count_objs = UINT32_MAX / sizeof(uint32_t);
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, ENOENT);
ioc.count_objs = ARRAY_SIZE(obj_raw);
ioc.objs_ptr = UINT64_MAX - sizeof(uint32_t);
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
ioc.count_objs = 1;
ioc.objs_ptr = UINT64_MAX - sizeof(uint32_t);
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
num_props_raw[0] = UINT32_MAX / sizeof(uint32_t);
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
num_props_raw[0] = UINT32_MAX - 1;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
for (i = 0; i < ARRAY_SIZE(obj_raw); i++)
num_props_raw[i] = (UINT32_MAX / ARRAY_SIZE(obj_raw)) + 1;
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
for (i = 0; i < ARRAY_SIZE(obj_raw); i++)
num_props_raw[i] = ARRAY_SIZE(props_raw) / ARRAY_SIZE(obj_raw);
do_ioctl_err(desc->fd, DRM_IOCTL_MODE_ATOMIC, &ioc, EFAULT);
}
igt_main
{
struct kms_atomic_desc desc;
struct kms_atomic_state *current;
memset(&desc, 0, sizeof(desc));
current = calloc(1, sizeof(*current));
igt_assert(current);
current->desc = &desc;
igt_fixture
atomic_setup(current);
igt_subtest("plane_overlay_legacy") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, PLANE_TYPE_OVERLAY, crtc);
igt_require(crtc);
igt_require(plane);
plane_overlay(crtc, plane);
atomic_state_free(scratch);
}
igt_subtest("plane_primary_legacy") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, PLANE_TYPE_PRIMARY, crtc);
igt_require(crtc);
igt_require(plane);
plane_primary(crtc, plane);
atomic_state_free(scratch);
}
igt_subtest("test_only") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, PLANE_TYPE_PRIMARY, crtc);
igt_require(crtc);
igt_require(plane);
test_only(crtc, plane);
atomic_state_free(scratch);
}
igt_subtest("plane_cursor_legacy") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, PLANE_TYPE_CURSOR, crtc);
igt_require(crtc);
igt_require(plane);
plane_cursor(crtc, plane);
atomic_state_free(scratch);
}
igt_subtest("plane_invalid_params") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(current, PLANE_TYPE_PRIMARY, crtc);
struct kms_atomic_connector_state *conn =
find_connector(scratch, crtc);
igt_require(crtc);
igt_require(plane);
plane_invalid_params(crtc, plane, conn);
atomic_state_free(scratch);
}
igt_subtest("plane_invalid_params_fence") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(current, PLANE_TYPE_PRIMARY, crtc);
struct kms_atomic_connector_state *conn =
find_connector(scratch, crtc);
igt_require(crtc);
igt_require(plane);
plane_invalid_params_fence(crtc, plane, conn);
atomic_state_free(scratch);
}
igt_subtest("crtc_invalid_params") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, NUM_PLANE_TYPE_PROPS, crtc);
struct kms_atomic_connector_state *conn =
find_connector(scratch, crtc);
igt_require(crtc);
igt_require(plane);
igt_require(conn);
crtc_invalid_params(crtc, plane, conn);
atomic_state_free(scratch);
}
igt_subtest("crtc_invalid_params_fence") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, NUM_PLANE_TYPE_PROPS, crtc);
struct kms_atomic_connector_state *conn =
find_connector(scratch, crtc);
igt_require(crtc);
igt_require(plane);
igt_require(conn);
crtc_invalid_params_fence(crtc, plane, conn);
atomic_state_free(scratch);
}
igt_subtest("atomic_invalid_params") {
struct kms_atomic_state *scratch = atomic_state_dup(current);
struct kms_atomic_crtc_state *crtc = find_crtc(scratch, true);
struct kms_atomic_plane_state *plane =
find_plane(scratch, NUM_PLANE_TYPE_PROPS, crtc);
struct kms_atomic_connector_state *conn =
find_connector(scratch, crtc);
igt_require(crtc);
igt_require(plane);
igt_require(conn);
atomic_invalid_params(crtc, plane, conn);
atomic_state_free(scratch);
}
atomic_state_free(current);
igt_fixture
close(desc.fd);
}