From: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> Add a test to try out all the different plane enable/disable order permutations. Signed-off-by: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> --- tests/Makefile.sources | 1 + tests/kms_plane_blinker.c | 529 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 530 insertions(+) create mode 100644 tests/kms_plane_blinker.c diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 04dd2d58612f..9c60bb90335d 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -110,6 +110,7 @@ TESTS_progs_M = \ kms_pipe_color \ kms_pipe_crc_basic \ kms_plane \ + kms_plane_blinker \ kms_plane_multiple \ kms_properties \ kms_psr_sink_crc \ diff --git a/tests/kms_plane_blinker.c b/tests/kms_plane_blinker.c new file mode 100644 index 000000000000..c6dace1121b5 --- /dev/null +++ b/tests/kms_plane_blinker.c @@ -0,0 +1,529 @@ +/* + * 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" + +IGT_TEST_DESCRIPTION("Test all plane enable/disable sequence permutations."); + +#define __FAC_0 1 +#define __FAC_1 1 +#define __FAC_2 (2*__FAC_1) +#define __FAC_3 (3*__FAC_2) +#define __FAC_4 (4*__FAC_3) +#define _FAC(n) __FAC_ ## n +#define FAC(n) _FAC(n) + +#define __SUB_4_0 4 +#define __SUB_4_1 3 +#define __SUB_4_2 2 +#define __SUB_4_3 1 +#define __SUB_4_4 0 +#define _SUB(a,b) __SUB_ ## a ## _ ## b +#define SUB(a,b) _SUB(a,b) + +#define C(k,n) (FAC(k) / (FAC(SUB(k, n)) * FAC(n))) + +/* all combinations */ +#define NUM_REF_CRCS (C(4,0) + C(4,1) + C(4,2) + C(4,3) + C(4,4)) + +typedef struct { + int drm_fd; + igt_display_t display; + igt_output_t *output; + enum pipe pipe; + igt_pipe_crc_t *pipe_crc; + igt_crc_t ref_crc[NUM_REF_CRCS]; + uint8_t ref_crc_active_planes[NUM_REF_CRCS]; + int num_ref_crcs; + uint32_t devid; + unsigned int delay_us; + unsigned int timeout_ms; + struct igt_fb ref_fb; + struct igt_fb fb[4]; + igt_plane_t *plane[4]; + uint8_t num_planes; + uint8_t active_plane[4]; + uint8_t num_active_planes; +} data_t; + +static unsigned int gettime_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +static const struct { + float r, g, b; +} colors[] = { + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 1.0f, 1.0f }, + { 1.0f, 0.0f, 1.0f }, + { 1.0f, 1.0f, 0.0f }, +}; + +static uint32_t plane_format(data_t *data, int plane) +{ + if (plane == data->num_planes - 1) + return DRM_FORMAT_ARGB8888; + else + return DRM_FORMAT_XRGB8888; +} + +static int plane_x(int plane) +{ + return plane * 100; +} + +static int plane_y(int plane) +{ + return plane * 100; +} + +static int plane_width(data_t *data, int plane) +{ + drmModeModeInfo *mode = igt_output_get_mode(data->output); + + if (plane == 0) + return mode->hdisplay; + else if (plane == data->num_planes - 1) + return 64; + else + return mode->hdisplay / 2; +} + +static int plane_height(data_t *data, int plane) +{ + drmModeModeInfo *mode = igt_output_get_mode(data->output); + + if (plane == 0) + return mode->vdisplay; + else if (plane == data->num_planes - 1) + return 64; + else + return mode->vdisplay / 2; +} + +static igt_crc_t *find_ref_crc(data_t *data, uint8_t active_planes) +{ + int i; + + for (i = 0; i < data->num_ref_crcs; i++) { + if (data->ref_crc_active_planes[i] == active_planes) + return &data->ref_crc[i]; + } + + return NULL; +} + +static void generate_ref_crc(data_t *data, int num_active_planes) +{ + drmModeModeInfo *mode; + cairo_t *cr; + int i; + + mode = igt_output_get_mode(data->output); + + data->ref_crc_active_planes[data->num_ref_crcs] = 0; + + cr = igt_get_cairo_ctx(data->drm_fd, &data->ref_fb); + + igt_paint_color(cr, 0, 0, mode->hdisplay, mode->vdisplay, + 0.0, 0.0, 0.0); + + for (i = 0; i < num_active_planes; i++) { + int plane = data->active_plane[i]; + int w, h; + int x, y; + + data->ref_crc_active_planes[data->num_ref_crcs] |= 1 << plane; + + x = plane_x(plane); + y = plane_y(plane); + w = plane_width(data, plane); + h = plane_height(data, plane); + + cairo_save(cr); + cairo_rectangle(cr, x, y, w, h); + cairo_clip(cr); + + igt_paint_color(cr, x, y, w, h, + colors[plane].r, colors[plane].g, colors[plane].b); + cairo_translate(cr, x, y); + igt_paint_test_pattern(cr, w, h); + cairo_restore(cr); + } + + igt_assert(cairo_status(cr) == 0); + cairo_destroy(cr); + + igt_wait_for_vblank(data->drm_fd, data->pipe); + + igt_assert(data->num_ref_crcs < NUM_REF_CRCS); + igt_pipe_crc_collect_crc(data->pipe_crc, &data->ref_crc[data->num_ref_crcs]); + data->num_ref_crcs++; +} + +static void do_combination(data_t *data, int plane, int idx, + int num_active_planes) +{ + if (idx == num_active_planes) { + generate_ref_crc(data, num_active_planes); + return; + } + + for (; plane < data->num_planes; plane++) { + data->active_plane[idx] = plane; + do_combination(data, plane + 1, idx + 1, num_active_planes); + } +} + +static bool igt_crc_equal(const igt_crc_t *a, const igt_crc_t *b) +{ + return a->n_words == b->n_words && + memcmp(a->crc, b->crc, sizeof(a->crc[0]) * a->n_words) == 0; +} + +static void generate_ref_crcs(data_t *data) +{ + int num_active_planes, i, j; + + data->num_ref_crcs = 0; + + /* ref crc for 0 active planes */ + for (num_active_planes = 0; + num_active_planes <= data->num_active_planes; + num_active_planes++) + do_combination(data, 0, 0, num_active_planes); + + for (i = 0; i < data->num_ref_crcs; i++) { + for (j = i + 1; j < data->num_ref_crcs; j++) { + if (igt_crc_equal(&data->ref_crc[i], &data->ref_crc[j])) + break; + } + if (j < data->num_ref_crcs) { + igt_warn("reference CRCs not unique\n"); + break; + } + } +} + +static bool prepare_crtc(data_t *data) +{ + igt_display_t *display = &data->display; + drmModeModeInfo *mode; + int i; + + /* select the pipe we want to use */ + igt_output_set_pipe(data->output, data->pipe); + igt_display_commit(display); + + if (!data->output->valid) { + igt_output_set_pipe(data->output, PIPE_ANY); + igt_display_commit(display); + return false; + } + + mode = igt_output_get_mode(data->output); + + igt_create_color_fb(data->drm_fd, + mode->hdisplay, mode->vdisplay, + DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, + 1.0, 1.0, 1.0, + &data->ref_fb); + + for (i = 0; i < data->num_planes; i++) { + igt_create_color_pattern_fb(data->drm_fd, + plane_width(data, i), plane_height(data, i), + plane_format(data, i), LOCAL_DRM_FORMAT_MOD_NONE, + colors[i].r, colors[i].g, colors[i].b, + &data->fb[i]); + + data->plane[i] = igt_output_get_plane(data->output, i); + } + + igt_plane_set_fb(data->plane[0], &data->ref_fb); + igt_display_commit(display); + + /* create the pipe_crc object for this pipe */ + if (data->pipe_crc) + igt_pipe_crc_free(data->pipe_crc); + data->pipe_crc = igt_pipe_crc_new_nonblock(data->pipe, + INTEL_PIPE_CRC_SOURCE_AUTO); + + generate_ref_crcs(data); + + igt_plane_set_fb(data->plane[0], NULL); + igt_display_commit2(display, COMMIT_UNIVERSAL); + + igt_remove_fb(data->drm_fd, &data->ref_fb); + + return true; +} + +#define NCRC 10 + +static void check_crcs(data_t *data, uint8_t active_planes, + igt_crc_t *last_ref_crc, + igt_crc_t *ref_crc) +{ + igt_crc_t *crc; + int i, n; + + n = igt_pipe_crc_get_crcs(data->pipe_crc, NCRC, &crc); + + for (i = 0; i < n; i++) { + if (!igt_crc_equal(&crc[i], last_ref_crc)) + break; + } + for (; i < n; i++) + igt_assert_crc_equal(&crc[i], ref_crc); + + free(crc); +} + +static void do_test(data_t *data) +{ + igt_display_t *display = &data->display; + igt_crc_t *last_ref_crc; + unsigned int start; + int num; + + igt_info(" Testing planes: none -> "); + for (num = 1; num < data->num_active_planes; num++) { + int idx; + + igt_info("%d", data->active_plane[0]); + for (idx = 1; idx < num; idx++) + igt_info(",%d", data->active_plane[idx]); + igt_info(" -> "); + } + for (; num > 0; num--) { + int idx; + + igt_info("%d", data->active_plane[0]); + for (idx = 1; idx < num; idx++) + igt_info(",%d", data->active_plane[idx]); + igt_info(" -> "); + } + igt_info("none\n"); + + start = gettime_ms(); + + last_ref_crc = find_ref_crc(data, 0); + + igt_pipe_crc_start(data->pipe_crc); + + do { + int idx; + uint8_t active_planes = 0; + + for (idx = 0; idx < data->num_active_planes; idx++) { + int plane = data->active_plane[idx]; + igt_crc_t *ref_crc; + + active_planes |= 1 << plane; + + ref_crc = find_ref_crc(data, active_planes); + igt_assert(ref_crc); + + igt_plane_set_fb(data->plane[plane], &data->fb[plane]); + igt_plane_set_position(data->plane[plane], + plane_x(plane), plane_y(plane)); + igt_display_commit2(display, COMMIT_UNIVERSAL); + usleep(data->delay_us); + + check_crcs(data, active_planes, last_ref_crc, ref_crc); + last_ref_crc = ref_crc; + } + + igt_assert(active_planes != 0); + + for (idx = data->num_active_planes - 1; idx >= 0; idx--) { + int plane = data->active_plane[idx]; + igt_crc_t *ref_crc; + + active_planes &= ~(1 << plane); + + ref_crc = find_ref_crc(data, active_planes); + igt_assert(ref_crc); + + igt_plane_set_fb(data->plane[plane], NULL); + igt_display_commit2(display, COMMIT_UNIVERSAL); + usleep(data->delay_us); + + check_crcs(data, active_planes, last_ref_crc, ref_crc); + last_ref_crc = ref_crc; + } + + igt_assert(active_planes == 0); + } while (data->timeout_ms > 0 && + gettime_ms() - start < data->timeout_ms); + + igt_pipe_crc_stop(data->pipe_crc); +} + +static void do_permutation(data_t *data, + unsigned int available_planes, + int idx) +{ + int plane; + + if (idx == data->num_active_planes) { + do_test(data); + return; + } + + for (plane = 0; plane < data->num_planes; plane++) { + if (!(available_planes & (1 << plane))) + continue; + + data->active_plane[idx] = plane; + do_permutation(data, + available_planes & ~(1 << plane), + idx + 1); + } +} + +static void test_permutations(data_t *data) +{ + do_permutation(data, (1 << data->num_planes) - 1, 0); +} + +static void cleanup_crtc(data_t *data) +{ + igt_display_t *display = &data->display; + int i; + + igt_pipe_crc_free(data->pipe_crc); + data->pipe_crc = NULL; + + for (i = 0; i < data->num_planes; i++) { + igt_remove_fb(data->drm_fd, &data->fb[i]); + igt_plane_set_fb(data->plane[i], NULL); + } + + igt_output_set_pipe(data->output, PIPE_ANY); + igt_display_commit(display); +} + +static void test(data_t *data) +{ + igt_display_t *display = &data->display; + int valid_tests = 0; + + igt_require(data->pipe <= display->n_pipes); + igt_require(data->num_active_planes <= display->pipes[data->pipe].n_planes); + + data->num_planes = data->display.pipes[data->pipe].n_planes; + + for_each_connected_output(display, data->output) { + if (!prepare_crtc(data)) + continue; + + valid_tests++; + + igt_info("Beginning %s on pipe %s, connector %s\n", + igt_subtest_name(), + kmstest_pipe_name(data->pipe), + igt_output_name(data->output)); + + test_permutations(data); + + igt_info("\n%s on pipe %s, connector %s: PASSED\n\n", + igt_subtest_name(), + kmstest_pipe_name(data->pipe), + igt_output_name(data->output)); + + cleanup_crtc(data); + } + + igt_require_f(valid_tests, "no valid crtc/connector combinations found\n"); +} + +static int opt_handler(int opt, int opt_index, void *_data) +{ + data_t *data = _data; + + switch (opt) { + case 'd': + data->delay_us = strtoull(optarg, NULL, 0) * 1000; + break; + case 't': + data->timeout_ms = strtoull(optarg, NULL, 0); + break; + default: + break; + } + + return 0; +} + +static data_t data; + +int main(int argc, char *argv[]) +{ + static const struct option long_opts[] = { + { .name = "delay", .val = 'd', .has_arg = required_argument }, + { .name = "timeout", .val = 't', .has_arg = required_argument }, + {} + }; + static const char *help_str = + " --delay\t\tDelay (in ms) between each plane enable/disable\n" + " --timeout\t\tTimeout (in ms) per connector for each subtest\n"; + + data.timeout_ms = 100; + data.delay_us = 20000; + + igt_subtest_init_parse_opts(&argc, argv, "", long_opts, help_str, + opt_handler, &data); + + data.drm_fd = drm_open_driver_master(DRIVER_INTEL); + data.devid = intel_get_drm_devid(data.drm_fd); + + igt_require_pipe_crc(); + + kmstest_set_vt_graphics_mode(); + + igt_display_init(&data.display, data.drm_fd); + igt_require(data.display.has_universal_planes); + + for (data.pipe = PIPE_A; data.pipe <= PIPE_C; data.pipe++) { + for (data.num_active_planes = 1; + data.num_active_planes <= 4; data.num_active_planes++) { + igt_subtest_f("pipe-%s-%d-planes", + kmstest_pipe_name(data.pipe), data.num_active_planes) { + test(&data); + } + } + } + + igt_display_fini(&data.display); + + igt_exit(); +} -- 2.7.4 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx