| /* |
| * 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 render compression (RC), in which the main surface " |
| "is complemented by a color control surface (CCS) that " |
| "the display uses to interpret the compressed data."); |
| |
| enum test_flags { |
| TEST_CRC = 1 << 1, |
| TEST_ROTATE_180 = 1 << 2, |
| TEST_BAD_PIXEL_FORMAT = 1 << 3, |
| TEST_BAD_ROTATION_90 = 1 << 4, |
| TEST_NO_AUX_BUFFER = 1 << 5, |
| TEST_BAD_CCS_HANDLE = 1 << 6, |
| TEST_BAD_AUX_STRIDE = 1 << 7, |
| }; |
| |
| #define TEST_FAIL_ON_ADDFB2 \ |
| (TEST_BAD_PIXEL_FORMAT | TEST_NO_AUX_BUFFER | TEST_BAD_CCS_HANDLE | \ |
| TEST_BAD_AUX_STRIDE) |
| |
| enum test_fb_flags { |
| FB_COMPRESSED = 1 << 0, |
| FB_HAS_PLANE = 1 << 1, |
| FB_MISALIGN_AUX_STRIDE = 1 << 2, |
| FB_SMALL_AUX_STRIDE = 1 << 3, |
| FB_ZERO_AUX_STRIDE = 1 << 4, |
| }; |
| |
| typedef struct { |
| int drm_fd; |
| igt_display_t display; |
| igt_output_t *output; |
| enum pipe pipe; |
| enum test_flags flags; |
| igt_plane_t *plane; |
| igt_pipe_crc_t *pipe_crc; |
| uint64_t ccs_modifier; |
| } data_t; |
| |
| static const struct { |
| double r; |
| double g; |
| double b; |
| } colors[2] = { |
| {1.0, 0.0, 0.0}, |
| {0.0, 1.0, 0.0} |
| }; |
| |
| static const uint64_t ccs_modifiers[] = { |
| LOCAL_I915_FORMAT_MOD_Y_TILED_CCS, |
| LOCAL_I915_FORMAT_MOD_Yf_TILED_CCS, |
| }; |
| |
| /* |
| * Limit maximum used sprite plane width so this test will not mistakenly |
| * fail on hardware limitations which are not interesting to this test. |
| * On this test too wide sprite plane may fail during creation with dmesg |
| * comment saying: |
| * "Requested display configuration exceeds system watermark limitations" |
| */ |
| #define MAX_SPRITE_PLANE_WIDTH 2000 |
| |
| static void addfb_init(struct igt_fb *fb, struct drm_mode_fb_cmd2 *f) |
| { |
| int i; |
| |
| f->width = fb->width; |
| f->height = fb->height; |
| f->pixel_format = fb->drm_format; |
| f->flags = LOCAL_DRM_MODE_FB_MODIFIERS; |
| |
| for (i = 0; i < fb->num_planes; i++) { |
| f->handles[i] = fb->gem_handle; |
| f->modifier[i] = fb->modifier; |
| f->pitches[i] = fb->strides[i]; |
| f->offsets[i] = fb->offsets[i]; |
| } |
| } |
| |
| static void generate_fb(data_t *data, struct igt_fb *fb, |
| int width, int height, |
| enum test_fb_flags fb_flags) |
| { |
| struct drm_mode_fb_cmd2 f = {0}; |
| uint32_t format; |
| uint64_t modifier; |
| cairo_t *cr; |
| int ret; |
| |
| /* Use either compressed or Y-tiled to test. However, given the lack of |
| * available bandwidth, we use linear for the primary plane when |
| * testing sprites, since we cannot fit two CCS planes into the |
| * available FIFO configurations. |
| */ |
| if (fb_flags & FB_COMPRESSED) |
| modifier = data->ccs_modifier; |
| else if (!(fb_flags & FB_HAS_PLANE)) |
| modifier = LOCAL_I915_FORMAT_MOD_Y_TILED; |
| else |
| modifier = 0; |
| |
| if (data->flags & TEST_BAD_PIXEL_FORMAT) |
| format = DRM_FORMAT_RGB565; |
| else |
| format = DRM_FORMAT_XRGB8888; |
| |
| igt_create_bo_for_fb(data->drm_fd, width, height, format, modifier, fb); |
| igt_assert(fb->gem_handle > 0); |
| |
| addfb_init(fb, &f); |
| |
| if (fb_flags & FB_COMPRESSED) { |
| if (fb_flags & FB_MISALIGN_AUX_STRIDE) { |
| igt_skip_on_f(width <= 1024, |
| "FB already has the smallest possible stride\n"); |
| f.pitches[1] -= 64; |
| } |
| |
| if (fb_flags & FB_SMALL_AUX_STRIDE) { |
| igt_skip_on_f(width <= 1024, |
| "FB already has the smallest possible stride\n"); |
| f.pitches[1] = ALIGN(f.pitches[1]/2, 128); |
| } |
| |
| if (fb_flags & FB_ZERO_AUX_STRIDE) |
| f.pitches[1] = 0; |
| |
| /* Put the CCS buffer on a different BO. */ |
| if (data->flags & TEST_BAD_CCS_HANDLE) |
| f.handles[1] = gem_create(data->drm_fd, fb->size); |
| |
| if (data->flags & TEST_NO_AUX_BUFFER) { |
| f.handles[1] = 0; |
| f.modifier[1] = 0; |
| f.pitches[1] = 0; |
| f.offsets[1] = 0; |
| } |
| } |
| |
| if (!(data->flags & TEST_BAD_PIXEL_FORMAT)) { |
| int c = !!data->plane; |
| |
| cr = igt_get_cairo_ctx(data->drm_fd, fb); |
| igt_paint_color(cr, 0, 0, width, height, |
| colors[c].r, colors[c].g, colors[c].b); |
| igt_put_cairo_ctx(data->drm_fd, fb, cr); |
| } |
| |
| ret = drmIoctl(data->drm_fd, LOCAL_DRM_IOCTL_MODE_ADDFB2, &f); |
| if (data->flags & TEST_FAIL_ON_ADDFB2) { |
| igt_assert_eq(ret, -1); |
| igt_assert_eq(errno, EINVAL); |
| return; |
| } else |
| igt_assert_eq(ret, 0); |
| |
| fb->fb_id = f.fb_id; |
| } |
| |
| static bool try_config(data_t *data, enum test_fb_flags fb_flags, |
| igt_crc_t *crc) |
| { |
| igt_display_t *display = &data->display; |
| igt_plane_t *primary; |
| drmModeModeInfo *drm_mode = igt_output_get_mode(data->output); |
| enum igt_commit_style commit; |
| struct igt_fb fb, fb_sprite; |
| int ret; |
| |
| if (data->display.is_atomic) |
| commit = COMMIT_ATOMIC; |
| else |
| commit = COMMIT_UNIVERSAL; |
| |
| primary = igt_output_get_plane_type(data->output, |
| DRM_PLANE_TYPE_PRIMARY); |
| if (!igt_plane_has_format_mod(primary, DRM_FORMAT_XRGB8888, |
| data->ccs_modifier)) |
| return false; |
| |
| if (data->plane && fb_flags & FB_COMPRESSED) { |
| if (!igt_plane_has_format_mod(data->plane, DRM_FORMAT_XRGB8888, |
| data->ccs_modifier)) |
| return false; |
| |
| generate_fb(data, &fb, min(MAX_SPRITE_PLANE_WIDTH, drm_mode->hdisplay), |
| drm_mode->vdisplay, |
| (fb_flags & ~FB_COMPRESSED) | FB_HAS_PLANE); |
| generate_fb(data, &fb_sprite, 256, 256, fb_flags); |
| } else { |
| generate_fb(data, &fb, min(MAX_SPRITE_PLANE_WIDTH, drm_mode->hdisplay), |
| drm_mode->vdisplay, fb_flags); |
| } |
| |
| if (data->flags & TEST_FAIL_ON_ADDFB2) |
| return true; |
| |
| igt_plane_set_position(primary, 0, 0); |
| igt_plane_set_size(primary, drm_mode->hdisplay, drm_mode->vdisplay); |
| igt_plane_set_fb(primary, &fb); |
| |
| if (data->plane && fb_flags & FB_COMPRESSED) { |
| igt_plane_set_position(data->plane, 0, 0); |
| igt_plane_set_size(data->plane, 256, 256); |
| igt_plane_set_fb(data->plane, &fb_sprite); |
| } |
| |
| if (data->flags & TEST_ROTATE_180) |
| igt_plane_set_rotation(primary, IGT_ROTATION_180); |
| if (data->flags & TEST_BAD_ROTATION_90) |
| igt_plane_set_rotation(primary, IGT_ROTATION_90); |
| |
| ret = igt_display_try_commit2(display, commit); |
| if (data->flags & TEST_BAD_ROTATION_90) { |
| igt_assert_eq(ret, -EINVAL); |
| } else { |
| igt_assert_eq(ret, 0); |
| |
| if (crc) |
| igt_pipe_crc_collect_crc(data->pipe_crc, crc); |
| } |
| |
| igt_debug_wait_for_keypress("ccs"); |
| |
| if (data->plane && fb_flags & FB_COMPRESSED) { |
| igt_plane_set_position(data->plane, 0, 0); |
| igt_plane_set_size(data->plane, 0, 0); |
| igt_plane_set_fb(data->plane, NULL); |
| igt_remove_fb(display->drm_fd, &fb_sprite); |
| } |
| |
| igt_plane_set_fb(primary, NULL); |
| igt_plane_set_rotation(primary, IGT_ROTATION_0); |
| igt_display_commit2(display, commit); |
| |
| if (data->flags & TEST_CRC) |
| igt_remove_fb(data->drm_fd, &fb); |
| |
| return true; |
| } |
| |
| static int test_ccs(data_t *data) |
| { int valid_tests = 0; |
| igt_crc_t crc, ref_crc; |
| enum test_fb_flags fb_flags = 0; |
| |
| if (data->flags & TEST_CRC) { |
| data->pipe_crc = igt_pipe_crc_new(data->drm_fd, data->pipe, INTEL_PIPE_CRC_SOURCE_AUTO); |
| |
| if (try_config(data, fb_flags | FB_COMPRESSED, &ref_crc) && |
| try_config(data, fb_flags, &crc)) { |
| igt_assert_crc_equal(&crc, &ref_crc); |
| valid_tests++; |
| } |
| |
| igt_pipe_crc_free(data->pipe_crc); |
| data->pipe_crc = NULL; |
| } |
| |
| if (data->flags & TEST_BAD_PIXEL_FORMAT || |
| data->flags & TEST_BAD_ROTATION_90 || |
| data->flags & TEST_NO_AUX_BUFFER || |
| data->flags & TEST_BAD_CCS_HANDLE) { |
| valid_tests += try_config(data, fb_flags | FB_COMPRESSED, NULL); |
| } |
| |
| if (data->flags & TEST_BAD_AUX_STRIDE) { |
| valid_tests += try_config(data, fb_flags | FB_COMPRESSED | FB_MISALIGN_AUX_STRIDE , NULL); |
| valid_tests += try_config(data, fb_flags | FB_COMPRESSED | FB_SMALL_AUX_STRIDE , NULL); |
| valid_tests += try_config(data, fb_flags | FB_COMPRESSED | FB_ZERO_AUX_STRIDE , NULL); |
| } |
| |
| return valid_tests; |
| } |
| |
| static int test_output(data_t *data) |
| { |
| igt_display_t *display = &data->display; |
| int i, valid_tests = 0; |
| |
| igt_display_require_output_on_pipe(display, data->pipe); |
| |
| /* Sets data->output with a valid output. */ |
| for_each_valid_output_on_pipe(display, data->pipe, data->output) { |
| break; |
| } |
| |
| igt_output_set_pipe(data->output, data->pipe); |
| |
| for (i = 0; i < ARRAY_SIZE(ccs_modifiers); i++) { |
| data->ccs_modifier = ccs_modifiers[i]; |
| valid_tests += test_ccs(data); |
| } |
| |
| igt_output_set_pipe(data->output, PIPE_NONE); |
| igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY); |
| |
| return valid_tests; |
| } |
| |
| static data_t data; |
| |
| igt_main |
| { |
| enum pipe pipe; |
| |
| igt_fixture { |
| data.drm_fd = drm_open_driver_master(DRIVER_INTEL); |
| |
| igt_require(intel_gen(intel_get_drm_devid(data.drm_fd)) >= 9); |
| kmstest_set_vt_graphics_mode(); |
| igt_require_pipe_crc(data.drm_fd); |
| |
| igt_display_require(&data.display, data.drm_fd); |
| } |
| |
| for_each_pipe_static(pipe) { |
| const char *pipe_name = kmstest_pipe_name(pipe); |
| |
| data.pipe = pipe; |
| |
| data.flags = TEST_BAD_PIXEL_FORMAT; |
| igt_subtest_f("pipe-%s-bad-pixel-format", pipe_name) |
| igt_require(test_output(&data)); |
| |
| data.flags = TEST_BAD_ROTATION_90; |
| igt_subtest_f("pipe-%s-bad-rotation-90", pipe_name) |
| igt_require(test_output(&data)); |
| |
| data.flags = TEST_CRC; |
| igt_subtest_f("pipe-%s-crc-primary-basic", pipe_name) |
| igt_require(test_output(&data)); |
| |
| data.flags = TEST_CRC | TEST_ROTATE_180; |
| igt_subtest_f("pipe-%s-crc-primary-rotation-180", pipe_name) |
| igt_require(test_output(&data)); |
| |
| data.flags = TEST_CRC; |
| igt_subtest_f("pipe-%s-crc-sprite-planes-basic", pipe_name) { |
| int valid_tests = 0; |
| |
| igt_display_require_output_on_pipe(&data.display, data.pipe); |
| |
| for_each_plane_on_pipe(&data.display, data.pipe, data.plane) { |
| if (data.plane->type == DRM_PLANE_TYPE_PRIMARY) |
| continue; |
| valid_tests += test_output(&data); |
| } |
| |
| igt_require(valid_tests); |
| } |
| |
| data.plane = NULL; |
| |
| data.flags = TEST_NO_AUX_BUFFER; |
| igt_subtest_f("pipe-%s-missing-ccs-buffer", pipe_name) |
| igt_require(test_output(&data)); |
| |
| data.flags = TEST_BAD_CCS_HANDLE; |
| igt_subtest_f("pipe-%s-ccs-on-another-bo", pipe_name) |
| igt_require(test_output(&data)); |
| |
| data.flags = TEST_BAD_AUX_STRIDE; |
| igt_subtest_f("pipe-%s-bad-aux-stride", pipe_name) |
| igt_require(test_output(&data)); |
| } |
| |
| igt_fixture |
| igt_display_fini(&data.display); |
| } |