| /* |
| * Copyright © 2013 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 "igt_sysfs.h" |
| #include "igt_psr.h" |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include "intel_bufmgr.h" |
| |
| enum operations { |
| PAGE_FLIP, |
| MMAP_GTT, |
| MMAP_CPU, |
| BLT, |
| RENDER, |
| PLANE_MOVE, |
| PLANE_ONOFF, |
| }; |
| |
| static const char *op_str(enum operations op) |
| { |
| static const char * const name[] = { |
| [PAGE_FLIP] = "page_flip", |
| [MMAP_GTT] = "mmap_gtt", |
| [MMAP_CPU] = "mmap_cpu", |
| [BLT] = "blt", |
| [RENDER] = "render", |
| [PLANE_MOVE] = "plane_move", |
| [PLANE_ONOFF] = "plane_onoff", |
| }; |
| |
| return name[op]; |
| } |
| |
| typedef struct { |
| int drm_fd; |
| int debugfs_fd; |
| enum operations op; |
| uint32_t devid; |
| uint32_t crtc_id; |
| igt_display_t display; |
| drm_intel_bufmgr *bufmgr; |
| struct igt_fb fb_green, fb_white; |
| igt_plane_t *test_plane; |
| int mod_size; |
| int mod_stride; |
| drmModeModeInfo *mode; |
| igt_output_t *output; |
| bool with_psr_disabled; |
| } data_t; |
| |
| static void create_cursor_fb(data_t *data) |
| { |
| cairo_t *cr; |
| uint32_t fb_id; |
| |
| fb_id = igt_create_fb(data->drm_fd, 64, 64, |
| DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE, |
| &data->fb_white); |
| igt_assert(fb_id); |
| |
| cr = igt_get_cairo_ctx(data->drm_fd, &data->fb_white); |
| igt_paint_color_alpha(cr, 0, 0, 64, 64, 1.0, 1.0, 1.0, 1.0); |
| igt_put_cairo_ctx(data->drm_fd, &data->fb_white, cr); |
| } |
| |
| static void setup_output(data_t *data) |
| { |
| igt_display_t *display = &data->display; |
| igt_output_t *output; |
| enum pipe pipe; |
| |
| for_each_pipe_with_valid_output(display, pipe, output) { |
| drmModeConnectorPtr c = output->config.connector; |
| |
| if (c->connector_type != DRM_MODE_CONNECTOR_eDP) |
| continue; |
| |
| igt_output_set_pipe(output, pipe); |
| data->crtc_id = output->config.crtc->crtc_id; |
| data->output = output; |
| data->mode = igt_output_get_mode(output); |
| |
| return; |
| } |
| } |
| |
| static void display_init(data_t *data) |
| { |
| igt_display_init(&data->display, data->drm_fd); |
| setup_output(data); |
| } |
| |
| static void display_fini(data_t *data) |
| { |
| igt_display_fini(&data->display); |
| } |
| |
| static void fill_blt(data_t *data, uint32_t handle, unsigned char color) |
| { |
| drm_intel_bo *dst = gem_handle_to_libdrm_bo(data->bufmgr, |
| data->drm_fd, |
| "", handle); |
| struct intel_batchbuffer *batch; |
| |
| batch = intel_batchbuffer_alloc(data->bufmgr, data->devid); |
| igt_assert(batch); |
| |
| COLOR_BLIT_COPY_BATCH_START(0); |
| OUT_BATCH((1 << 24) | (0xf0 << 16) | 0); |
| OUT_BATCH(0); |
| OUT_BATCH(0xfff << 16 | 0xfff); |
| OUT_RELOC(dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0); |
| OUT_BATCH(color); |
| ADVANCE_BATCH(); |
| |
| intel_batchbuffer_flush(batch); |
| intel_batchbuffer_free(batch); |
| |
| gem_bo_busy(data->drm_fd, handle); |
| } |
| |
| static void scratch_buf_init(struct igt_buf *buf, drm_intel_bo *bo, |
| int size, int stride) |
| { |
| memset(buf, 0, sizeof(*buf)); |
| |
| buf->bo = bo; |
| buf->stride = stride; |
| buf->tiling = I915_TILING_X; |
| buf->size = size; |
| } |
| |
| static void fill_render(data_t *data, uint32_t handle, unsigned char color) |
| { |
| drm_intel_bo *src, *dst; |
| struct intel_batchbuffer *batch; |
| struct igt_buf src_buf, dst_buf; |
| const uint8_t buf[4] = { color, color, color, color }; |
| igt_render_copyfunc_t rendercopy = igt_get_render_copyfunc(data->devid); |
| |
| igt_skip_on(!rendercopy); |
| |
| dst = gem_handle_to_libdrm_bo(data->bufmgr, data->drm_fd, "", handle); |
| igt_assert(dst); |
| |
| src = drm_intel_bo_alloc(data->bufmgr, "", data->mod_size, 4096); |
| igt_assert(src); |
| |
| gem_write(data->drm_fd, src->handle, 0, buf, 4); |
| |
| scratch_buf_init(&src_buf, src, data->mod_size, data->mod_stride); |
| scratch_buf_init(&dst_buf, dst, data->mod_size, data->mod_stride); |
| |
| batch = intel_batchbuffer_alloc(data->bufmgr, data->devid); |
| igt_assert(batch); |
| |
| rendercopy(batch, NULL, |
| &src_buf, 0, 0, 0xff, 0xff, |
| &dst_buf, 0, 0); |
| |
| intel_batchbuffer_free(batch); |
| |
| gem_bo_busy(data->drm_fd, handle); |
| } |
| |
| static bool sink_support(data_t *data) |
| { |
| char buf[512]; |
| |
| igt_debugfs_simple_read(data->debugfs_fd, "i915_edp_psr_status", |
| buf, sizeof(buf)); |
| |
| return data->with_psr_disabled || |
| strstr(buf, "Sink_Support: yes\n"); |
| } |
| |
| static bool psr_wait_entry_if_enabled(data_t *data) |
| { |
| if (data->with_psr_disabled) |
| return true; |
| |
| return psr_wait_entry(data->debugfs_fd); |
| } |
| |
| static inline void manual(const char *expected) |
| { |
| igt_debug_manual_check("all", expected); |
| } |
| |
| static bool drrs_disabled(data_t *data) |
| { |
| char buf[512]; |
| |
| igt_debugfs_simple_read(data->debugfs_fd, "i915_drrs_status", |
| buf, sizeof(buf)); |
| |
| return !strstr(buf, "DRRS Supported: Yes\n"); |
| } |
| |
| static void run_test(data_t *data) |
| { |
| uint32_t handle = data->fb_white.gem_handle; |
| igt_plane_t *test_plane = data->test_plane; |
| void *ptr; |
| const char *expected = ""; |
| |
| /* Confirm that screen became Green */ |
| manual("screen GREEN"); |
| |
| /* Confirm screen stays Green after PSR got active */ |
| igt_assert(psr_wait_entry_if_enabled(data)); |
| manual("screen GREEN"); |
| |
| /* Setting a secondary fb/plane */ |
| igt_plane_set_fb(test_plane, &data->fb_white); |
| igt_display_commit(&data->display); |
| |
| /* Confirm it is not Green anymore */ |
| if (test_plane->type == DRM_PLANE_TYPE_PRIMARY) |
| manual("screen WHITE"); |
| else |
| manual("GREEN background with WHITE box"); |
| |
| igt_assert(psr_wait_entry_if_enabled(data)); |
| switch (data->op) { |
| case PAGE_FLIP: |
| /* Only in use when testing primary plane */ |
| igt_assert(drmModePageFlip(data->drm_fd, data->crtc_id, |
| data->fb_green.fb_id, 0, NULL) == 0); |
| expected = "GREEN"; |
| break; |
| case MMAP_GTT: |
| ptr = gem_mmap__gtt(data->drm_fd, handle, data->mod_size, |
| PROT_WRITE); |
| gem_set_domain(data->drm_fd, handle, |
| I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); |
| memset(ptr, 0xcc, data->mod_size); |
| munmap(ptr, data->mod_size); |
| expected = "BLACK or TRANSPARENT mark on top of plane in test"; |
| break; |
| case MMAP_CPU: |
| ptr = gem_mmap__cpu(data->drm_fd, handle, 0, data->mod_size, |
| PROT_WRITE); |
| gem_set_domain(data->drm_fd, handle, |
| I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); |
| memset(ptr, 0, data->mod_size); |
| munmap(ptr, data->mod_size); |
| gem_sw_finish(data->drm_fd, handle); |
| expected = "BLACK or TRANSPARENT mark on top of plane in test"; |
| break; |
| case BLT: |
| fill_blt(data, handle, 0); |
| expected = "BLACK or TRANSPARENT mark on top of plane in test"; |
| break; |
| case RENDER: |
| fill_render(data, handle, 0); |
| expected = "BLACK or TRANSPARENT mark on top of plane in test"; |
| break; |
| case PLANE_MOVE: |
| /* Only in use when testing Sprite and Cursor */ |
| igt_plane_set_position(test_plane, 500, 500); |
| igt_display_commit(&data->display); |
| expected = "White box moved to 500x500"; |
| break; |
| case PLANE_ONOFF: |
| /* Only in use when testing Sprite and Cursor */ |
| igt_plane_set_fb(test_plane, NULL); |
| igt_display_commit(&data->display); |
| expected = "screen GREEN"; |
| break; |
| } |
| igt_assert(psr_active(data->debugfs_fd, false)); |
| manual(expected); |
| } |
| |
| static void test_cleanup(data_t *data) { |
| igt_plane_t *primary; |
| |
| primary = igt_output_get_plane_type(data->output, |
| DRM_PLANE_TYPE_PRIMARY); |
| igt_plane_set_fb(primary, NULL); |
| igt_plane_set_fb(data->test_plane, NULL); |
| igt_display_commit(&data->display); |
| |
| igt_remove_fb(data->drm_fd, &data->fb_green); |
| igt_remove_fb(data->drm_fd, &data->fb_white); |
| } |
| |
| static void setup_test_plane(data_t *data, int test_plane) |
| { |
| uint32_t white_h, white_v; |
| igt_plane_t *primary, *sprite, *cursor; |
| |
| igt_create_color_fb(data->drm_fd, |
| data->mode->hdisplay, data->mode->vdisplay, |
| DRM_FORMAT_XRGB8888, |
| LOCAL_I915_FORMAT_MOD_X_TILED, |
| 0.0, 1.0, 0.0, |
| &data->fb_green); |
| |
| primary = igt_output_get_plane_type(data->output, |
| DRM_PLANE_TYPE_PRIMARY); |
| igt_plane_set_fb(primary, NULL); |
| data->test_plane = primary; |
| |
| white_h = data->mode->hdisplay; |
| white_v = data->mode->vdisplay; |
| |
| /* Ignoring pitch and bpp to avoid changing full screen */ |
| data->mod_size = white_h * white_v; |
| data->mod_stride = white_h * 4; |
| |
| switch (test_plane) { |
| case DRM_PLANE_TYPE_OVERLAY: |
| sprite = igt_output_get_plane_type(data->output, |
| DRM_PLANE_TYPE_OVERLAY); |
| igt_plane_set_fb(sprite, NULL); |
| white_h = white_h/2; |
| white_v = white_v/2; |
| data->test_plane = sprite; |
| case DRM_PLANE_TYPE_PRIMARY: |
| igt_create_color_fb(data->drm_fd, |
| white_h, white_v, |
| DRM_FORMAT_XRGB8888, |
| LOCAL_I915_FORMAT_MOD_X_TILED, |
| 1.0, 1.0, 1.0, |
| &data->fb_white); |
| break; |
| case DRM_PLANE_TYPE_CURSOR: |
| cursor = igt_output_get_plane_type(data->output, |
| DRM_PLANE_TYPE_CURSOR); |
| igt_plane_set_fb(cursor, NULL); |
| create_cursor_fb(data); |
| igt_plane_set_position(cursor, 0, 0); |
| |
| /* Cursor is 64 x 64, ignoring pitch and bbp again */ |
| data->mod_size = 64 * 64; |
| data->test_plane = cursor; |
| break; |
| } |
| |
| igt_display_commit(&data->display); |
| |
| igt_plane_set_fb(primary, &data->fb_green); |
| igt_display_commit(&data->display); |
| } |
| |
| static void dpms_off_on(data_t *data) |
| { |
| kmstest_set_connector_dpms(data->drm_fd, data->output->config.connector, |
| DRM_MODE_DPMS_OFF); |
| kmstest_set_connector_dpms(data->drm_fd, data->output->config.connector, |
| DRM_MODE_DPMS_ON); |
| } |
| |
| static int opt_handler(int opt, int opt_index, void *_data) |
| { |
| data_t *data = _data; |
| |
| switch (opt) { |
| case 'n': |
| data->with_psr_disabled = true; |
| break; |
| default: |
| igt_assert(0); |
| } |
| |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| const char *help_str = |
| " --no-psr\tRun test without PSR."; |
| static struct option long_options[] = { |
| {"no-psr", 0, 0, 'n'}, |
| { 0, 0, 0, 0 } |
| }; |
| data_t data = {}; |
| enum operations op; |
| |
| igt_subtest_init_parse_opts(&argc, argv, "", long_options, |
| help_str, opt_handler, &data); |
| igt_skip_on_simulation(); |
| |
| igt_fixture { |
| data.drm_fd = drm_open_driver_master(DRIVER_INTEL); |
| data.debugfs_fd = igt_debugfs_dir(data.drm_fd); |
| kmstest_set_vt_graphics_mode(); |
| data.devid = intel_get_drm_devid(data.drm_fd); |
| |
| if (!data.with_psr_disabled) |
| psr_enable(data.debugfs_fd); |
| |
| igt_require_f(sink_support(&data), |
| "Sink does not support PSR\n"); |
| |
| data.bufmgr = drm_intel_bufmgr_gem_init(data.drm_fd, 4096); |
| igt_assert(data.bufmgr); |
| drm_intel_bufmgr_gem_enable_reuse(data.bufmgr); |
| |
| display_init(&data); |
| } |
| |
| igt_subtest("basic") { |
| setup_test_plane(&data, DRM_PLANE_TYPE_PRIMARY); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| test_cleanup(&data); |
| } |
| |
| igt_subtest("no_drrs") { |
| setup_test_plane(&data, DRM_PLANE_TYPE_PRIMARY); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| igt_assert(drrs_disabled(&data)); |
| test_cleanup(&data); |
| } |
| |
| for (op = PAGE_FLIP; op <= RENDER; op++) { |
| igt_subtest_f("primary_%s", op_str(op)) { |
| data.op = op; |
| setup_test_plane(&data, DRM_PLANE_TYPE_PRIMARY); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| run_test(&data); |
| test_cleanup(&data); |
| } |
| } |
| |
| for (op = MMAP_GTT; op <= PLANE_ONOFF; op++) { |
| igt_subtest_f("sprite_%s", op_str(op)) { |
| data.op = op; |
| setup_test_plane(&data, DRM_PLANE_TYPE_OVERLAY); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| run_test(&data); |
| test_cleanup(&data); |
| } |
| } |
| |
| for (op = MMAP_GTT; op <= PLANE_ONOFF; op++) { |
| igt_subtest_f("cursor_%s", op_str(op)) { |
| data.op = op; |
| setup_test_plane(&data, DRM_PLANE_TYPE_CURSOR); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| run_test(&data); |
| test_cleanup(&data); |
| } |
| } |
| |
| igt_subtest_f("dpms") { |
| data.op = RENDER; |
| setup_test_plane(&data, DRM_PLANE_TYPE_PRIMARY); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| dpms_off_on(&data); |
| run_test(&data); |
| test_cleanup(&data); |
| } |
| |
| igt_subtest_f("suspend") { |
| data.op = PLANE_ONOFF; |
| setup_test_plane(&data, DRM_PLANE_TYPE_CURSOR); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| igt_system_suspend_autoresume(SUSPEND_STATE_MEM, |
| SUSPEND_TEST_NONE); |
| igt_assert(psr_wait_entry_if_enabled(&data)); |
| run_test(&data); |
| test_cleanup(&data); |
| } |
| |
| igt_fixture { |
| if (!data.with_psr_disabled) |
| psr_disable(data.debugfs_fd); |
| |
| close(data.debugfs_fd); |
| drm_intel_bufmgr_destroy(data.bufmgr); |
| display_fini(&data); |
| } |
| |
| igt_exit(); |
| } |