/* * Copyright © 2016 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "igt.h" #include "drmtest.h" #include #include #include #include #include static void prepare_pipe(igt_display_t *display, enum pipe pipe, igt_output_t *output, struct igt_fb *fb) { drmModeModeInfo *mode = igt_output_get_mode(output); igt_create_pattern_fb(display->drm_fd, mode->hdisplay, mode->vdisplay, DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, fb); igt_output_set_pipe(output, pipe); igt_plane_set_fb(igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY), fb); igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY); } static void cleanup_pipe(igt_display_t *display, enum pipe pipe, igt_output_t *output, struct igt_fb *fb) { igt_plane_t *plane; for_each_plane_on_pipe(display, pipe, plane) igt_plane_set_fb(plane, NULL); igt_output_set_pipe(output, PIPE_NONE); igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY); igt_remove_fb(display->drm_fd, fb); } static bool ignore_property(uint32_t obj_type, uint32_t prop_flags, const char *name, bool atomic) { if (prop_flags & DRM_MODE_PROP_IMMUTABLE) return true; switch (obj_type) { case DRM_MODE_OBJECT_CONNECTOR: if (atomic && !strcmp(name, "DPMS")) return true; break; default: break; } return false; } static void test_properties(int fd, uint32_t type, uint32_t id, bool atomic) { drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(fd, id, type); int i, ret; drmModeAtomicReqPtr req = NULL; igt_assert(props); if (atomic) req = drmModeAtomicAlloc(); for (i = 0; i < props->count_props; i++) { uint32_t prop_id = props->props[i]; uint64_t prop_value = props->prop_values[i]; drmModePropertyPtr prop = drmModeGetProperty(fd, prop_id); igt_assert(prop); if (ignore_property(type, prop->flags, prop->name, atomic)) { igt_debug("Ignoring property \"%s\"\n", prop->name); continue; } igt_debug("Testing property \"%s\"\n", prop->name); if (!atomic) { ret = drmModeObjectSetProperty(fd, id, type, prop_id, prop_value); igt_assert_eq(ret, 0); } else { ret = drmModeAtomicAddProperty(req, id, prop_id, prop_value); igt_assert(ret >= 0); ret = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_TEST_ONLY, NULL); igt_assert_eq(ret, 0); } drmModeFreeProperty(prop); } drmModeFreeObjectProperties(props); if (atomic) { ret = drmModeAtomicCommit(fd, req, 0, NULL); igt_assert_eq(ret, 0); drmModeAtomicFree(req); } } static void run_plane_property_tests(igt_display_t *display, enum pipe pipe, igt_output_t *output, bool atomic) { struct igt_fb fb; igt_plane_t *plane; prepare_pipe(display, pipe, output, &fb); for_each_plane_on_pipe(display, pipe, plane) { igt_info("Testing plane properties on %s.#%d-%s (output: %s)\n", kmstest_pipe_name(pipe), plane->index, kmstest_plane_type_name(plane->type), output->name); test_properties(display->drm_fd, DRM_MODE_OBJECT_PLANE, plane->drm_plane->plane_id, atomic); } cleanup_pipe(display, pipe, output, &fb); } static void run_crtc_property_tests(igt_display_t *display, enum pipe pipe, igt_output_t *output, bool atomic) { struct igt_fb fb; prepare_pipe(display, pipe, output, &fb); igt_info("Testing crtc properties on %s (output: %s)\n", kmstest_pipe_name(pipe), output->name); test_properties(display->drm_fd, DRM_MODE_OBJECT_CRTC, display->pipes[pipe].crtc_id, atomic); cleanup_pipe(display, pipe, output, &fb); } static void run_connector_property_tests(igt_display_t *display, enum pipe pipe, igt_output_t *output, bool atomic) { struct igt_fb fb; if (pipe != PIPE_NONE) prepare_pipe(display, pipe, output, &fb); igt_info("Testing connector properties on output %s (pipe: %s)\n", output->name, kmstest_pipe_name(pipe)); test_properties(display->drm_fd, DRM_MODE_OBJECT_CONNECTOR, output->id, atomic); if (pipe != PIPE_NONE) cleanup_pipe(display, pipe, output, &fb); } static void plane_properties(igt_display_t *display, bool atomic) { bool found_any = false, found; igt_output_t *output; enum pipe pipe; if (atomic) igt_skip_on(!display->is_atomic); for_each_pipe(display, pipe) { found = false; for_each_valid_output_on_pipe(display, pipe, output) { found_any = found = true; run_plane_property_tests(display, pipe, output, atomic); break; } } igt_skip_on(!found_any); } static void crtc_properties(igt_display_t *display, bool atomic) { bool found_any_valid_pipe = false, found; enum pipe pipe; igt_output_t *output; if (atomic) igt_skip_on(!display->is_atomic); for_each_pipe(display, pipe) { found = false; for_each_valid_output_on_pipe(display, pipe, output) { found_any_valid_pipe = found = true; run_crtc_property_tests(display, pipe, output, atomic); break; } } igt_skip_on(!found_any_valid_pipe); } static void connector_properties(igt_display_t *display, bool atomic) { int i; enum pipe pipe; igt_output_t *output; if (atomic) igt_skip_on(!display->is_atomic); for_each_connected_output(display, output) { bool found = false; for_each_pipe(display, pipe) { if (!igt_pipe_connector_valid(pipe, output)) continue; found = true; run_connector_property_tests(display, pipe, output, atomic); break; } igt_assert_f(found, "Connected output should have at least 1 valid crtc\n"); } for (i = 0; i < display->n_outputs; i++) if (!igt_output_is_connected(&display->outputs[i])) run_connector_property_tests(display, PIPE_NONE, &display->outputs[i], atomic); } static void test_invalid_properties(int fd, uint32_t id1, uint32_t type1, uint32_t id2, uint32_t type2, bool atomic) { drmModeObjectPropertiesPtr props1 = drmModeObjectGetProperties(fd, id1, type1); drmModeObjectPropertiesPtr props2 = drmModeObjectGetProperties(fd, id2, type2); int i, j, ret; drmModeAtomicReqPtr req; igt_assert(props1 && props2); for (i = 0; i < props2->count_props; i++) { uint32_t prop_id = props2->props[i]; uint64_t prop_value = props2->prop_values[i]; drmModePropertyPtr prop = drmModeGetProperty(fd, prop_id); bool found = false; igt_assert(prop); for (j = 0; j < props1->count_props; j++) if (props1->props[j] == prop_id) { found = true; break; } if (found) continue; igt_debug("Testing property \"%s\" on [%x:%u]\n", prop->name, type1, id1); if (!atomic) { ret = drmModeObjectSetProperty(fd, id1, type1, prop_id, prop_value); igt_assert_eq(ret, -EINVAL); } else { req = drmModeAtomicAlloc(); igt_assert(req); ret = drmModeAtomicAddProperty(req, id1, prop_id, prop_value); igt_assert(ret >= 0); ret = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); igt_assert_eq(ret, -ENOENT); drmModeAtomicFree(req); } drmModeFreeProperty(prop); } drmModeFreeObjectProperties(props1); drmModeFreeObjectProperties(props2); } static void test_object_invalid_properties(igt_display_t *display, uint32_t id, uint32_t type, bool atomic) { igt_output_t *output; igt_plane_t *plane; enum pipe pipe; int i; for_each_pipe(display, pipe) test_invalid_properties(display->drm_fd, id, type, display->pipes[pipe].crtc_id, DRM_MODE_OBJECT_CRTC, atomic); for_each_pipe(display, pipe) for_each_plane_on_pipe(display, pipe, plane) test_invalid_properties(display->drm_fd, id, type, plane->drm_plane->plane_id, DRM_MODE_OBJECT_PLANE, atomic); for (i = 0, output = &display->outputs[0]; i < display->n_outputs; output = &display->outputs[++i]) test_invalid_properties(display->drm_fd, id, type, output->id, DRM_MODE_OBJECT_CONNECTOR, atomic); } static void get_prop_sanity(igt_display_t *display) { int i, fd; uint64_t *values; struct drm_mode_property_enum *enums; fd = display->drm_fd; /* * There's no way to enumerate all properties, we just have to * brute-force the first few kms ids. 1000 should be enough. */ for (i = 0; i < 1000; i++) { struct drm_mode_get_property prop; memset(&prop, 0, sizeof(prop)); prop.prop_id = i; if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) continue; if (prop.count_values) { values = calloc(prop.count_values, sizeof(uint64_t)); igt_assert(values); memset(values, 0x5c, sizeof(uint64_t)*prop.count_values); prop.values_ptr = to_user_pointer(values); } /* despite what libdrm makes you believe, we never supply * additional information for BLOB properties, only for enums * and bitmasks */ igt_assert_eq(!!prop.count_enum_blobs, !!(prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))); if (prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) igt_assert(prop.count_enum_blobs == prop.count_values); if (prop.count_enum_blobs) { enums = calloc(prop.count_enum_blobs, sizeof(*enums)); memset(enums, 0x5c, sizeof(*enums)*prop.count_enum_blobs); igt_assert(enums); prop.enum_blob_ptr = to_user_pointer(enums); } do_ioctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop); for (int j = 0; j < prop.count_values; j++) { igt_assert(values[j] != 0x5c5c5c5c5c5c5c5cULL); if (!(prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) continue; igt_assert(enums[j].value != 0x5c5c5c5c5c5c5c5cULL); igt_assert(enums[j].value == values[j]); igt_assert(enums[j].name[0] != '\0'); } } } static void invalid_properties(igt_display_t *display, bool atomic) { igt_output_t *output; igt_plane_t *plane; enum pipe pipe; int i; if (atomic) igt_skip_on(!display->is_atomic); for_each_pipe(display, pipe) test_object_invalid_properties(display, display->pipes[pipe].crtc_id, DRM_MODE_OBJECT_CRTC, atomic); for_each_pipe(display, pipe) for_each_plane_on_pipe(display, pipe, plane) test_object_invalid_properties(display, plane->drm_plane->plane_id, DRM_MODE_OBJECT_PLANE, atomic); for (i = 0, output = &display->outputs[0]; i < display->n_outputs; output = &display->outputs[++i]) test_object_invalid_properties(display, output->id, DRM_MODE_OBJECT_CONNECTOR, atomic); } igt_main { igt_display_t display; igt_skip_on_simulation(); igt_fixture { display.drm_fd = drm_open_driver_master(DRIVER_ANY); kmstest_set_vt_graphics_mode(); igt_display_init(&display, display.drm_fd); } igt_subtest("plane-properties-legacy") plane_properties(&display, false); igt_subtest("plane-properties-atomic") plane_properties(&display, true); igt_subtest("crtc-properties-legacy") crtc_properties(&display, false); igt_subtest("crtc-properties-atomic") crtc_properties(&display, true); igt_subtest("connector-properties-legacy") connector_properties(&display, false); igt_subtest("connector-properties-atomic") connector_properties(&display, true); igt_subtest("invalid-properties-legacy") invalid_properties(&display, false); igt_subtest("invalid-properties-atomic") invalid_properties(&display, true); igt_subtest("get_properties-sanity") get_prop_sanity(&display); igt_fixture { igt_display_fini(&display); } }