diff options
author | Eryk Brol <eryk.brol@amd.com> | 2021-12-16 09:55:01 -0500 |
---|---|---|
committer | Rodrigo Siqueira <Rodrigo.Siqueira@amd.com> | 2021-12-21 15:40:12 -0500 |
commit | 731e09c15b4fd559ce8aec30065fccde17a9b834 (patch) | |
tree | b5dfc3bbe3db33f423dd07734c032ca79b57b22e /tests/amdgpu | |
parent | 2f59b36a40331e98610dab12baefbe82b6e3cef0 (diff) |
tests/amdgpu: Introduces DP DSC test
This commit adds a DP DSC test that checks:
* Forces DSC on/off and ensures it is reset properly
* Check DSC slice height property
* Verify various DSC slice dimensions
* Tests various combinations of link_rate + lane_count and logs if DSC
enabled/disabled Tests different bpc settings and logs if DSC is
enabled/disabled
Change since V3:
- Drop useless test
Change since V2:
- Remove IGT_CRTC_DSC_SLICE_HEIGHT crtc property from this commit
Change since V1:
- Rebase
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Nicholas Choi <Nicholas.Choi@amd.com>
Cc: Mark Yacoub <markyacoub@chromium.org>
Cc: Hayden Goodfellow <hayden.goodfellow@amd.com>
Cc: Hersen Wu <hersenxs.wu@amd.com>
Cc: Roman Li <roman.li@amd.com>
Acked-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: Mikita Lipski <mikita.lipski@amd.com>
Signed-off-by: Eryk Brol <eryk.brol@amd.com>
Diffstat (limited to 'tests/amdgpu')
-rw-r--r-- | tests/amdgpu/amd_dp_dsc.c | 607 | ||||
-rw-r--r-- | tests/amdgpu/meson.build | 1 |
2 files changed, 608 insertions, 0 deletions
diff --git a/tests/amdgpu/amd_dp_dsc.c b/tests/amdgpu/amd_dp_dsc.c new file mode 100644 index 00000000..e3f3a39f --- /dev/null +++ b/tests/amdgpu/amd_dp_dsc.c @@ -0,0 +1,607 @@ +/* + * Copyright 2021 Advanced Micro Devices, Inc. + * + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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_amd.h" +#include "sw_sync.h" +#include <fcntl.h> +#include <signal.h> + +#define NUM_SLICE_SLOTS 4 + +/* Maximumm pipes on any AMD ASIC. */ +#define MAX_PIPES 6 + +/* Common test data. */ +typedef struct data { + igt_display_t display; + igt_plane_t *primary[MAX_PIPES]; + igt_output_t *output[MAX_PIPES]; + igt_pipe_t *pipe[MAX_PIPES]; + igt_pipe_crc_t *pipe_crc[MAX_PIPES]; + drmModeModeInfo mode[MAX_PIPES]; + enum pipe pipe_id[MAX_PIPES]; + int fd; +} data_t; + +/* BPC connector state. */ +typedef struct output_bpc { + unsigned int current; + unsigned int maximum; +} output_bpc_t; + +/* Common test cleanup. */ +static void test_fini(data_t *data) +{ + igt_display_t *display = &data->display; + int i; + + for (i = 0; i < display->n_pipes; ++i) { + igt_pipe_crc_free(data->pipe_crc[i]); + } + + igt_display_reset(display); + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0); +} + +/* Common test setup. */ +static void test_init(data_t *data) +{ + igt_display_t *display = &data->display; + int i, n; + + for (i = 0; i < display->n_pipes; ++i) { + data->pipe_id[i] = PIPE_A + i; + data->pipe[i] = &data->display.pipes[data->pipe_id[i]]; + data->primary[i] = igt_pipe_get_plane_type( + data->pipe[i], DRM_PLANE_TYPE_PRIMARY); + data->pipe_crc[i] = + igt_pipe_crc_new(data->fd, data->pipe_id[i], "auto"); + } + + for (i = 0, n = 0; i < display->n_outputs && n < display->n_pipes; ++i) { + igt_output_t *output = &display->outputs[i]; + data->output[n] = output; + + /* Only allow physically connected displays for the tests. */ + if (!igt_output_is_connected(output)) + continue; + + /* Ensure that outpus are DP, DSC & FEC capable*/ + if (!(is_dp_fec_supported(data->fd, output->name) && + is_dp_dsc_supported(data->fd, output->name))) + continue; + + if (output->config.connector->connector_type != + DRM_MODE_CONNECTOR_DisplayPort) + continue; + + igt_assert(kmstest_get_connector_default_mode( + data->fd, output->config.connector, &data->mode[n])); + + n += 1; + } + + igt_display_reset(display); +} + +static void test_dsc_enable(data_t *data) +{ + bool dsc_on, dsc_after, dsc_before; + igt_display_t *display = &data->display; + igt_output_t *output; + igt_fb_t ref_fb; + int i, test_conn_cnt = 0; + + test_init(data); + igt_enable_connectors(data->fd); + + for (i = 0; i < display->n_pipes; i++) { + /* Setup the output */ + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + igt_create_pattern_fb(data->fd, + data->mode[i].hdisplay, + data->mode[i].vdisplay, + DRM_FORMAT_XRGB8888, + 0, + &ref_fb); + igt_output_set_pipe(output, data->pipe_id[i]); + igt_plane_set_fb(data->primary[i], &ref_fb); + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0); + + test_conn_cnt++; + + /* Save pipe's initial DSC state */ + dsc_before = igt_amd_read_dsc_clock_status(data->fd, output->name); + + /* Force enable DSC */ + igt_amd_write_dsc_clock_en(data->fd, output->name, DSC_FORCE_ON); + + igt_plane_set_fb(data->primary[i], &ref_fb); + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + /* Check if DSC is enabled */ + dsc_on = igt_amd_read_dsc_clock_status(data->fd, output->name) == 1; + + /* Revert DSC to automatic state */ + igt_amd_write_dsc_clock_en(data->fd, output->name, DSC_FORCE_OFF); + + igt_plane_set_fb(data->primary[i], &ref_fb); + igt_display_commit_atomic(display,DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + dsc_after = igt_amd_read_dsc_clock_status(data->fd, output->name); + + /* Revert DSC back to automatic mechanism by disabling state overwrites*/ + igt_plane_set_fb(data->primary[i], &ref_fb); + + igt_amd_write_dsc_clock_en(data->fd, output->name, DSC_AUTOMATIC); + + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + igt_assert_f(dsc_on, "Enabling DSC on pipe failed.\n"); + igt_assert_f(dsc_after == dsc_before, "Reverting DSC to initial state failed.\n"); + + /* Cleanup fb */ + igt_remove_fb(data->fd, &ref_fb); + } + + test_fini(data); + igt_skip_on(test_conn_cnt == 0); +} + +static bool update_slice_height(data_t *data, int v_addressable, + int *num_slices, igt_output_t *output, int conn_idx, igt_fb_t ref_fb) +{ + int i; + bool pass = true; + + for(i = 0; i < NUM_SLICE_SLOTS; i++) { + int act_slice_height; + int slice_height = v_addressable / num_slices[i] + (v_addressable % num_slices[i]); + + /* Overwrite DSC slice height */ + igt_amd_write_dsc_param_slice_height(data->fd, output->name, slice_height); + igt_plane_set_fb(data->primary[conn_idx], &ref_fb); + igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + igt_info("Forcing slice height: slice height %d num slices vertical %d\n", slice_height, num_slices[i]); + + act_slice_height = igt_amd_read_dsc_param_slice_height(data->fd, output->name); + + igt_info("Reading slice height: actual slice height %d VS assigned slice height %d\n", act_slice_height, slice_height); + + pass = (slice_height == act_slice_height); + + if (!pass) + break; + } + + igt_amd_write_dsc_param_slice_height(data->fd, output->name, 0); + igt_plane_set_fb(data->primary[conn_idx], &ref_fb); + igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + return pass; +} + +static bool update_slice_width(data_t *data, int h_addressable, + int *num_slices, igt_output_t *output, int conn_idx, igt_fb_t ref_fb) +{ + int i; + bool pass = true; + + for(i = 0; i < NUM_SLICE_SLOTS; i++) { + int act_slice_width; + int slice_width = h_addressable / num_slices[i] + (h_addressable % num_slices[i]); + + /* Overwrite DSC slice width */ + igt_amd_write_dsc_param_slice_width(data->fd, output->name, slice_width); + igt_plane_set_fb(data->primary[conn_idx], &ref_fb); + igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + igt_info("Forcing slice width: slice width %d num slices horisontal %d\n", slice_width, num_slices[i]); + + act_slice_width = igt_amd_read_dsc_param_slice_width(data->fd, output->name); + + igt_info("Reading slice width: actual slice width %d VS assigned slice width %d\n", act_slice_width, slice_width); + + pass = (slice_width == act_slice_width); + + if (!pass) + break; + } + + igt_amd_write_dsc_param_slice_width(data->fd, output->name, 0); + igt_plane_set_fb(data->primary[conn_idx], &ref_fb); + igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + return pass; +} + +static void test_dsc_slice_dimensions_change(data_t *data) +{ + bool dsc_on, dsc_after, dsc_before; + igt_output_t *output; + igt_display_t *display = &data->display; + igt_fb_t ref_fb; + int num_slices [] = { 1, 2, 4, 8 }; + int h_addressable, v_addressable; + bool ret_slice_height= false, ret_slice_width = false; + int i, test_conn_cnt = 0; + + test_init(data); + igt_enable_connectors(data->fd); + + for (i = 0; i < display->n_pipes; i++) { + /* Setup the output */ + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + igt_create_pattern_fb(data->fd, + data->mode[i].hdisplay, + data->mode[i].vdisplay, + DRM_FORMAT_XRGB8888, + 0, + &ref_fb); + igt_output_set_pipe(output, data->pipe_id[i]); + igt_plane_set_fb(data->primary[i], &ref_fb); + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0); + + test_conn_cnt++; + + h_addressable = data->mode->hdisplay; + v_addressable = data->mode->vdisplay; + + igt_info("Mode info: v_ative %d h_active %d\n", v_addressable, h_addressable); + + /* Save pipe's initial DSC state */ + dsc_before = igt_amd_read_dsc_clock_status(data->fd, output->name); + + /* Force enable DSC */ + igt_amd_write_dsc_clock_en(data->fd, output->name, DSC_FORCE_ON); + + igt_plane_set_fb(data->primary[i], &ref_fb); + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + /* Check if DSC is enabled */ + dsc_on = igt_amd_read_dsc_clock_status(data->fd, output->name) == 1; + + if (dsc_on) { + ret_slice_height = update_slice_height(data, v_addressable, num_slices, output, i, ref_fb); + ret_slice_width = update_slice_width(data, h_addressable, num_slices, output, i, ref_fb); + } + + /* Force disable DSC */ + igt_amd_write_dsc_clock_en(data->fd, output->name, DSC_FORCE_OFF); + + igt_plane_set_fb(data->primary[i], &ref_fb); + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + dsc_after = igt_amd_read_dsc_clock_status(data->fd, output->name); + + /* Revert DSC back to automatic mechanism by disabling state overwrites*/ + igt_plane_set_fb(data->primary[i], &ref_fb); + + igt_amd_write_dsc_clock_en(data->fd, output->name, DSC_AUTOMATIC); + + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + igt_assert_f(dsc_on, "Enabling DSC on pipe failed.\n"); + igt_assert_f(ret_slice_height, "Changing slice height failed.\n"); + igt_assert_f(ret_slice_width, "Changing slice width failed.\n"); + igt_assert_f(dsc_after == dsc_before, "Reverting DSC to initial state failed.\n"); + + /* Cleanup fb */ + igt_remove_fb(data->fd, &ref_fb); + } + + test_fini(data); + igt_skip_on(test_conn_cnt == 0); +} + +static void test_dsc_link_settings(data_t *data) +{ + igt_output_t *output; + igt_fb_t ref_fb[MAX_PIPES]; + igt_crc_t ref_crc[MAX_PIPES], new_crc[MAX_PIPES]; + int lane_count[4], link_rate[4], link_spread[4]; + igt_display_t *display = &data->display; + int i, lc, lr; + bool dsc_on; + const enum dc_lane_count lane_count_vals[] = + { + LANE_COUNT_TWO, + LANE_COUNT_FOUR + }; + const enum dc_link_rate link_rate_vals[] = + { + LINK_RATE_LOW, + LINK_RATE_HIGH, + LINK_RATE_HIGH2, + LINK_RATE_HIGH3 + }; + + test_init(data); + + /* Setup all outputs */ + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + igt_create_pattern_fb(data->fd, + data->mode[i].hdisplay, + data->mode[i].vdisplay, + DRM_FORMAT_XRGB8888, + 0, + &ref_fb[i]); + igt_output_set_pipe(output, data->pipe_id[i]); + igt_plane_set_fb(data->primary[i], &ref_fb[i]); + } + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0); + + /* Collect reference CRCs */ + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + igt_pipe_crc_collect_crc(data->pipe_crc[i], &ref_crc[i]); + } + + for (lc = 0; lc < ARRAY_SIZE(lane_count_vals); lc++) { + for (lr = 0; lr < ARRAY_SIZE(link_rate_vals); lr++) { + /* Write new link_settings */ + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + /* Write lower link settings */ + igt_info("Applying lane count: %d, link rate 0x%02x, on default training\n", + lane_count_vals[lc], link_rate_vals[lr]); + igt_amd_write_link_settings(data->fd, output->name, + lane_count_vals[lc], + link_rate_vals[lr], + LINK_TRAINING_DEFAULT); + usleep(500 * MSEC_PER_SEC); + } + + /* Trigger commit after writing new link settings */ + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + /* Verify lower link settings */ + igt_amd_read_link_settings(data->fd, output->name, + lane_count, + link_rate, + link_spread); + + igt_assert_f(lane_count[0] == lane_count_vals[lc], "Lowering lane count settings failed\n"); + igt_assert_f(link_rate[0] == link_rate_vals[lr], "Lowering link rate settings failed\n"); + + /* Log current mode and DSC status */ + dsc_on = igt_amd_read_dsc_clock_status(data->fd, output->name) == 1; + igt_info("Current mode is: %dx%d @%dHz -- DSC is: %s\n", + data->mode[i].hdisplay, + data->mode[i].vdisplay, + data->mode[i].vrefresh, + dsc_on ? "ON" : "OFF"); + + igt_pipe_crc_collect_crc(data->pipe_crc[i], &new_crc[i]); + igt_assert_crc_equal(&ref_crc[i], &new_crc[i]); + } + } + } + + /* Cleanup all fbs */ + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + igt_remove_fb(data->fd, &ref_fb[i]); + } + + test_fini(data); +} + +/* Returns the current and maximum bpc from the connector debugfs. */ +static output_bpc_t get_output_bpc(int data_fd, char *connector_name) +{ + char buf[256]; + char *start_loc; + int fd, res; + output_bpc_t info; + + fd = igt_debugfs_connector_dir(data_fd, connector_name, O_RDONLY); + igt_assert(fd >= 0); + + res = igt_debugfs_simple_read(fd, "output_bpc", buf, sizeof(buf)); + + igt_require(res > 0); + + close(fd); + + igt_assert(start_loc = strstr(buf, "Current: ")); + igt_assert_eq(sscanf(start_loc, "Current: %u", &info.current), 1); + + igt_assert(start_loc = strstr(buf, "Maximum: ")); + igt_assert_eq(sscanf(start_loc, "Maximum: %u", &info.maximum), 1); + + return info; +} + +/* Verifies that connector has the correct output bpc */ +static void assert_output_bpc(int data_fd, char *connector_name, unsigned int bpc) +{ + output_bpc_t info = get_output_bpc(data_fd, connector_name); + + igt_require_f(info.maximum >= bpc, + "Monitor doesn't support %u bpc, max is %u\n", bpc, + info.maximum); + + igt_assert_eq(info.current, bpc); +} + +/* Returns the highest bpc this dispaly supports */ +static int get_max_supported_bpc(int data_fd, char *connector_name) +{ + output_bpc_t info = get_output_bpc(data_fd, connector_name); + return info.maximum; +} + +static void test_dsc_bpc(data_t *data) +{ + igt_output_t *output; + igt_fb_t ref_fb[MAX_PIPES]; + igt_crc_t test_crc; + igt_display_t *display = &data->display; + int i, bpc, max_supported_bpc[MAX_PIPES]; + bool dsc_on; + const int bpc_vals[] = {12, 10, 8}; + + test_init(data); + + /* Find max supported bpc */ + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + igt_info("Checking bpc support of conn %s\n", output->name); + max_supported_bpc[i] = get_max_supported_bpc(data->fd, output->name); + } + + /* Setup all outputs */ + for (bpc = 0; bpc < ARRAY_SIZE(bpc_vals); bpc++) { + igt_info("Testing bpc = %d\n", bpc_vals[bpc]); + + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + if (max_supported_bpc[i] < bpc_vals[bpc]) { + igt_info("Display doesn't support bpc of %d, max is %d. Skipping to next bpc value.\n", bpc_vals[bpc], max_supported_bpc[i]); + continue; + } + igt_info("Setting bpc = %d\n", bpc_vals[bpc]); + igt_output_set_prop_value(output, IGT_CONNECTOR_MAX_BPC, bpc_vals[bpc]); + igt_create_pattern_fb(data->fd, + data->mode[i].hdisplay, + data->mode[i].vdisplay, + DRM_FORMAT_XRGB8888, + 0, + &ref_fb[i]); + igt_output_set_pipe(output, data->pipe_id[i]); + igt_plane_set_fb(data->primary[i], &ref_fb[i]); + } + + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0); + + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + if (max_supported_bpc[i] < bpc_vals[bpc]) + continue; + + /* Check that crc is non-zero */ + igt_pipe_crc_collect_crc(data->pipe_crc[i], &test_crc); + igt_assert(test_crc.crc[0] && test_crc.crc[1] && test_crc.crc[2]); + + /* Check current bpc */ + igt_info("Verifying display %s has correct bpc\n", output->name); + assert_output_bpc(data->fd, output->name, bpc_vals[bpc]); + + /* Log current mode and DSC status */ + dsc_on = igt_amd_read_dsc_clock_status(data->fd, output->name) == 1; + igt_info("Current mode is: %dx%d @%dHz -- DSC is: %s\n", + data->mode[i].hdisplay, + data->mode[i].vdisplay, + data->mode[i].vrefresh, + dsc_on ? "ON" : "OFF"); + } + + /* Cleanup all fbs */ + for (i = 0; i < display->n_pipes; i++) { + output = data->output[i]; + if (!output || !igt_output_is_connected(output)) + continue; + + if (max_supported_bpc[i] < bpc_vals[bpc]) + continue; + + igt_remove_fb(data->fd, &ref_fb[i]); + } + } + + test_fini(data); +} + +igt_main +{ + data_t data = { 0 }; + + igt_skip_on_simulation(); + + igt_fixture + { + data.fd = drm_open_driver_master(DRIVER_ANY); + + igt_display_require(&data.display, data.fd); + igt_require(data.display.is_atomic); + igt_display_require_output(&data.display); + + igt_amd_require_dsc(&data.display, data.fd); + kmstest_set_vt_graphics_mode(); + } + + igt_describe("Forces DSC on/off & ensures it is reset properly"); + igt_subtest("dsc-enable-basic") + test_dsc_enable(&data); + + igt_describe("Tests various DSC slice dimensions"); + igt_subtest("dsc-slice-dimensions-change") + test_dsc_slice_dimensions_change(&data); + + igt_describe("Tests various combinations of link_rate + lane_count and logs if DSC enabled/disabled"); + igt_subtest("dsc-link-settings") + test_dsc_link_settings(&data); + + igt_describe("Tests different bpc settings and logs if DSC is enabled/disabled"); + igt_subtest("dsc-bpc") + test_dsc_bpc(&data); + + igt_fixture + { + igt_reset_connectors(); + igt_display_fini(&data.display); + } +} diff --git a/tests/amdgpu/meson.build b/tests/amdgpu/meson.build index d232e490..d8e8ddfb 100644 --- a/tests/amdgpu/meson.build +++ b/tests/amdgpu/meson.build @@ -17,6 +17,7 @@ if libdrm_amdgpu.found() 'amd_link_settings', 'amd_vrr_range', 'amd_mode_switch', + 'amd_dp_dsc', 'amd_psr', 'amd_plane', ] |