[PATCH] [i-g-t] tests/amdgpu/amd_multidisplay_modeset: Run mode set and display enable for multidisplays
Kamil Konieczny
kamil.konieczny at linux.intel.com
Wed Feb 21 08:37:42 UTC 2024
Hi Hersen,
On 2024-02-20 at 11:43:23 -0500, Hersen Wu wrote:
> Run mode set, display enable and disable for multiple
> displays.
>
> Loop all display modes. Loop all display combinations.
> For DP display(eDP, SST DP, displays connected to
> outputs of MST hub), if it supports DP rx crc test, IGT
> will read DP rx crc and compare with pipe crc. If DSC
> is not enabled for display, DP rx crc should equal to
> pipe crc.
>
> Signed-off-by: Hersen Wu <hersenxs.wu at amd.com>
> ---
> tests/amdgpu/amd_multidisplay_modeset.c | 446 ++++++++++++++++++++++++
> tests/amdgpu/meson.build | 11 +-
> 2 files changed, 452 insertions(+), 5 deletions(-)
> create mode 100644 tests/amdgpu/amd_multidisplay_modeset.c
>
> diff --git a/tests/amdgpu/amd_multidisplay_modeset.c b/tests/amdgpu/amd_multidisplay_modeset.c
> new file mode 100644
> index 000000000..66981f212
> --- /dev/null
> +++ b/tests/amdgpu/amd_multidisplay_modeset.c
> @@ -0,0 +1,446 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright 2024 Advanced Micro Devices, Inc.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Delere this text from "THE SOFTWARE...", it is SPDX which raplaces it.
> + * 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 <dirent.h>
> +#include "igt.h"
> +#include "igt_amd.h"
> +
> +#define MAX_PIPES 6
> +
> +/* Common test data. */
> +struct data_t {
> + igt_display_t display;
> + igt_plane_t *primary[MAX_PIPES];
> + igt_output_t *output[MAX_PIPES];
> + int fd;
> + igt_pipe_crc_t *pipe_crc_dprx[MAX_PIPES];
> + igt_crc_t crc_fb[MAX_PIPES];
> + igt_crc_t crc_dprx[MAX_PIPES];
> + igt_pipe_crc_t *pipe_crc_otg[MAX_PIPES];
> + igt_crc_t crc_otg[MAX_PIPES];
> +};
> +
> +enum sub_test {
> + MODE_SET,
> + DISPLAY_ENABLE_DISABLE
> +};
> +
> +/* DP DPCD offset */
> +#define DPCD_TEST_SINK_MISC 0x246
> +
> +static bool is_mst_connector(int drm_fd, uint32_t connector_id)
> +{
> + return kmstest_get_property(drm_fd, connector_id,
> + DRM_MODE_OBJECT_CONNECTOR,
> + "PATH", NULL,
> + NULL, NULL);
> +}
> +
> +static bool sink_detect_error(int drm_fd, uint32_t connector_id, int error_code)
> +{
> + switch (error_code) {
> + case ETIMEDOUT:
> + case ENXIO:
> + return true;
> + case EIO:
> + return is_mst_connector(drm_fd, connector_id);
> + default:
> + return false;
> + };
> +}
> +
> +/* Read from /dev/drm_dp_aux
> + * addr: DPCD offset
> + * val: Read value of DPCD register
> + */
> +static bool dpcd_read_byte(int drm_fd,
> + drmModeConnector *connector, uint32_t addr, uint8_t *val)
> +{
> + DIR *dir;
> + int dir_fd;
> + uint8_t buf[16] = {0};
> + *val = 0;
> +
> + dir_fd = igt_connector_sysfs_open(drm_fd, connector);
> + igt_assert(dir_fd >= 0);
> +
> + if (connector->connection != DRM_MODE_CONNECTED &&
> + is_mst_connector(drm_fd, connector->connector_id)) {
> + close(dir_fd);
> + return false;
> + }
> +
> + dir = fdopendir(dir_fd);
> + igt_assert(dir);
> +
> + for (;;) {
> + struct dirent *ent;
> + char path[5 + sizeof(ent->d_name)];
> + int fd, ret, i, j, k;
> +
> + ent = readdir(dir);
> + if (!ent)
> + break;
> +
> + if (strncmp(ent->d_name, "drm_dp_aux", 10))
> + continue;
> +
> + snprintf(path, sizeof(path), "/dev/%s", ent->d_name);
> +
> + fd = open(path, O_RDONLY);
> + igt_assert(fd >= 0);
> +
> + k = (addr / 16) + 1;
> + j = addr % 16;
> +
> + /* read 16 bytes each loop */
> + for (i = 0; i < k; i++) {
> + ret = read(fd, buf, sizeof(buf));
> + if (ret < 0)
> + break;
> + if (ret != sizeof(buf))
> + break;
> + }
> +
> + igt_info("%s: %s\n", path,
> + ret > 0 ? "success" : strerror(errno));
> +
> + igt_assert(ret == sizeof(buf) ||
> + sink_detect_error(drm_fd, connector->connector_id, errno));
> +
> + close(fd);
> +
> + closedir(dir);
> + close(dir_fd);
> +
> + if (ret > 0)
> + *val = buf[j];
> +
> + return (ret > 0);
> + }
> +
> + closedir(dir);
> + close(dir_fd);
> + return false;
> +}
> +
> +static void set_all_output_pipe_to_none(struct data_t *data)
> +{
> + igt_output_t *output;
> +
> + for_each_connected_output(&data->display, output) {
> + igt_output_set_pipe(output, PIPE_NONE);
> + }
> +
> + igt_display_commit_atomic(&data->display,
> + DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +}
> +
> +static void test_init(struct data_t *data)
> +{
> + igt_display_t *display = &data->display;
> + int i;
> + bool ret = false;
> + uint8_t dpcd_246h = 0;
> +
> + for (i = 0; i < MAX_PIPES; i++)
> + data->pipe_crc_dprx[i] = NULL;
> +
> + for_each_pipe(display, i) {
> + igt_output_t *output;
> + igt_pipe_t *pipes;
> +
> + /* For each valid pipe, get one connected display.
> + * This will let displays connected to MST hub be
> + * tested
> + */
> + output = igt_get_single_output_for_pipe(display, i);
> + pipes = &display->pipes[i];
> + data->primary[i] = igt_pipe_get_plane_type(
> + &data->display.pipes[i], DRM_PLANE_TYPE_PRIMARY);
> + data->output[i] = output;
> +
> + /* dp rx crc only available for eDP, SST DP, MST DP */
> + if ((output->config.connector->connector_type ==
> + DRM_MODE_CONNECTOR_eDP ||
> + output->config.connector->connector_type ==
> + DRM_MODE_CONNECTOR_DisplayPort)) {
> + /* DPCD 0x246 bit5: 1 -- sink device support CRC test */
> + ret = dpcd_read_byte(data->fd, output->config.connector,
> + DPCD_TEST_SINK_MISC, &dpcd_246h);
> + if (ret && ((dpcd_246h & 0x20) != 0x0))
> + data->pipe_crc_dprx[i] = igt_pipe_crc_new(
> + data->fd, pipes->pipe,
> + AMDGPU_PIPE_CRC_SOURCE_DPRX);
> + }
> +
> + data->pipe_crc_otg[i] = igt_pipe_crc_new(data->fd, pipes->pipe,
> + IGT_PIPE_CRC_SOURCE_AUTO);
> + /* disable eDP PSR */
> + if (data->output[i]->config.connector->connector_type ==
> + DRM_MODE_CONNECTOR_eDP) {
> + kmstest_set_connector_dpms(display->drm_fd,
> + data->output[i]->config.connector,
> + DRM_MODE_DPMS_OFF);
> +
> + igt_amd_disallow_edp_enter_psr(data->fd,
> + data->output[i]->name, true);
> +
> + kmstest_set_connector_dpms(display->drm_fd,
> + data->output[i]->config.connector,
> + DRM_MODE_DPMS_ON);
> + }
> + }
> +
> + igt_require(data->output[0]);
> + igt_display_reset(display);
> +}
> +
> +static void test_fini(struct data_t *data)
> +{
> + igt_display_t *display = &data->display;
> + int i = 0;
> +
> + for_each_pipe(display, i) {
> + if (data->pipe_crc_dprx[i])
> + igt_pipe_crc_free(data->pipe_crc_dprx[i]);
> + igt_pipe_crc_free(data->pipe_crc_otg[i]);
> + }
> +
> + igt_display_reset(display);
> + igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
> +}
> +
> +static void multiple_display_test(struct data_t *data, enum sub_test test_mode)
> +{
> + igt_output_t *output;
> + struct igt_fb *buf;
> + drmModeConnectorPtr conn;
> + drmModeModeInfoPtr kmode;
> + igt_display_t *display = &data->display;
> + int i, j, ret, num_disps, count_modes_disp_config, bitmap_disps;
> + char *crc_str, *crc_str_1, *crc_str_2;
> + bool any_mst = false;
> +
> + test_init(data);
> +
> + num_disps = 0;
> + for_each_connected_output(display, output)
> + num_disps++;
> +
> + igt_info("Connected num_disps:%d\n", num_disps);
> +
> + igt_skip_on_f(num_disps > igt_display_get_n_pipes(&data->display) ||
> + num_disps > data->display.n_outputs,
> + "ASIC does not have %d outputs/pipes\n", num_disps);
> +
> + buf = calloc(sizeof(struct igt_fb), num_disps);
> + igt_assert_f(buf, "Failed to allocate memory\n");
> +
> + /* For mode test, it is max number of modes for
> + * all connected displays. For enable/disable test,
> + * it is number of connected display combinations
> + */
> + count_modes_disp_config = 0;
> + for_each_connected_output(display, output) {
> + conn = output->config.connector;
> + if (count_modes_disp_config < conn->count_modes)
> + count_modes_disp_config = conn->count_modes;
> + if (is_mst_connector(data->fd, conn->connector_id))
> + any_mst = true;
> + }
> +
> + if (test_mode == DISPLAY_ENABLE_DISABLE)
> + count_modes_disp_config = (1 << num_disps) - 1;
> +
> + /* display combination bitmap for mode set or enable display
> + * bit 0: display 0
> + * bit 1: display 1
> + * bit 2: display 2, etc.
> + *
> + * bitmap_disps:0x5 means display 0,2 light up
> + */
> + bitmap_disps = (1 << num_disps) - 1;
> + igt_info("count_modes_disp_config:%d bitmap_disps:%x\n\n\n",
> + count_modes_disp_config, bitmap_disps);
> +
> + for (i = 0; i < count_modes_disp_config; i++) {
> + if (test_mode == DISPLAY_ENABLE_DISABLE) {
> + bitmap_disps = i + 1;
> + igt_info("\n\ndispconfig loop:%d disp bitmap:%x -----\n",
> + i, bitmap_disps);
> +
> + /* disable all displays */
> + set_all_output_pipe_to_none(data);
> + } else
> + igt_info("\n\nnmode loop:%d -----\n", i);
> + j = 0;
> + for_each_connected_output(display, output) {
> + if (test_mode == DISPLAY_ENABLE_DISABLE) {
> + /* only enable display mapping to
> + * bitmap_disps with value 1
> + */
> + if (!(bitmap_disps & (1 << j))) {
> + j++;
> + continue;
> + }
> + kmode = igt_output_get_mode(output);
> + igt_assert(kmode);
> + igt_info("pipe:%d %s mode:%s\n",
> + j, output->name, kmode->name);
> + igt_info("clk:%d ht:%d vt:%d hz:%d\n",
> + kmode->clock, kmode->htotal,
> + kmode->vtotal, kmode->vrefresh);
> + } else {
> + conn = output->config.connector;
> +
> + if (i < conn->count_modes)
> + kmode = &conn->modes[i];
> + else
> + kmode = &conn->modes[0];
> +
> + igt_info("pipe:%d %s mode:%s\n", j, output->name, kmode->name);
> + igt_info("clk:%d ht:%d vt:%d hz:%d\n", kmode->clock,
> + kmode->htotal, kmode->vtotal, kmode->vrefresh);
> +
> + if (is_mst_connector(data->fd, conn->connector_id)) {
> + /* mst hub may not support mode with high clk
> + * more than 4k at 60hz or high refresh rate
> + */
> + if (kmode->clock > 596000 || kmode->vrefresh > 120) {
> + kmode = &conn->modes[0];
> + igt_info("Mode may not be supported by mst hub. Use default mode from monitor!\n");
> + igt_info("clk:%d ht:%d vt:%d hz:%d\n",
> + kmode->clock, kmode->htotal,
> + kmode->vtotal, kmode->vrefresh);
> + }
> + }
> +
> + /* memory for output->config.connector will be re-alloacted */
> + igt_output_override_mode(output, kmode);
> + }
> +
> + igt_create_pattern_fb(data->fd, kmode->hdisplay,
> + kmode->vdisplay, DRM_FORMAT_XRGB8888,
> + 0, (buf + j));
> +
> + igt_output_set_pipe(output, j);
> + igt_plane_set_fb(data->primary[j], (buf + j));
> + j++;
> + }
> +
> + igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +
> + /* MST hub may take longer time for mode change or display
> + * enable/disable. Wait for MST stable before CRC check.
> + * TODO: check if there is a better way to check MST stable
> + */
> + if (any_mst)
> + sleep(20);
> +
> + j = 0;
> + for_each_connected_output(display, output) {
> + bool dsc_on = false;
> +
> + /* For test_mode:MODE_SET, bitmap_disps =
> + * (1 << num_disps) - 1. All connected
> + * displays will be checked.
> + */
> + if (!(bitmap_disps & (1 << j))) {
> + j++;
> + continue;
> + }
> +
> + if (data->pipe_crc_dprx[j]) {
> + igt_pipe_crc_collect_crc(data->pipe_crc_dprx[j],
> + &data->crc_dprx[j]);
> + crc_str = igt_crc_to_string(&data->crc_dprx[j]);
> + igt_info("pipe:%d %s dprx crc:%s\n",
> + j, output->name, crc_str);
> + } else
> + igt_info("pipe:%d %s monitor dprx not available\n",
> + j, output->name);
> +
> + igt_pipe_crc_collect_crc(data->pipe_crc_otg[j],
> + &data->crc_otg[j]);
> + igt_fb_calc_crc((buf + j), &data->crc_fb[j]);
> +
> + crc_str_1 = igt_crc_to_string(&data->crc_otg[j]);
> + igt_info("pipe:%d %s otg crc:%s\n",
> + j, output->name, crc_str_1);
> +
> + crc_str_2 = igt_crc_to_string(&data->crc_fb[j]);
> + igt_info("pipe:%d %s fb crc:%s\n",
> + j, output->name, crc_str_2);
> +
> + if (data->pipe_crc_dprx[j]) {
> + ret = strcmp(crc_str, crc_str_1);
> + dsc_on = igt_amd_read_dsc_clock_status(
> + data->fd, output->name);
> + if (ret != 0 && dsc_on)
> + igt_info("pipe:%d %s otg crc != dprx crc due to dsc on\n",
> + j, output->name);
> +
> + igt_assert_f(((ret == 0) || (dsc_on)),
> + "ERROR! OTG CRC != DPRX and DSC off\n");
> + }
> + j++;
> + }
> +
> + j = 0;
> + for_each_connected_output(display, output) {
> + igt_remove_fb(data->fd, (buf + j));
> + j++;
> + }
> +
> + set_all_output_pipe_to_none(data);
> + }
> +
> + test_fini(data);
> + free(buf);
> +}
> +
> +IGT_TEST_DESCRIPTION("Test multi-display mode set, display enable and disable");
> +igt_main
> +{
> + struct data_t data;
> +
> + igt_skip_on_simulation();
> +
> + memset(&data, 0, sizeof(data));
> +
> + igt_fixture
> + {
> + data.fd = drm_open_driver_master(DRIVER_AMDGPU);
> +
> + kmstest_set_vt_graphics_mode();
> +
> + igt_display_require(&data.display, data.fd);
> + igt_require(&data.display.is_atomic);
> + igt_display_require_output(&data.display);
> + }
> +
> + igt_describe("Loop through all supported modes and check DP RX CRC and Pipe CRC");
> + igt_subtest("multiple-display-mode-switch")
> + multiple_display_test(&data, MODE_SET);
> + igt_describe("Enable and Disable displays and check DP RX CRC and Pipe CRC");
> + igt_subtest("multiple-display-enable-disable")
> + multiple_display_test(&data, DISPLAY_ENABLE_DISABLE);
> +
> +
> + igt_fixture
> + {
> + igt_display_fini(&data.display);
> + drm_close_driver(data.fd);
> + }
> +}
> diff --git a/tests/amdgpu/meson.build b/tests/amdgpu/meson.build
> index 5bd502496..af1d80b6b 100644
> --- a/tests/amdgpu/meson.build
> +++ b/tests/amdgpu/meson.build
> @@ -13,32 +13,33 @@ if libdrm_amdgpu.found()
> 'amd_deadlock',
> 'amd_dp_dsc',
> 'amd_freesync_video_mode',
> - 'amd_hotplug',
> 'amd_gang_cs' ,
> + 'amd_hotplug',
> 'amd_ilr',
> 'amd_info',
> 'amd_jpeg_dec',
> 'amd_link_settings',
> + 'amd_mall',
> 'amd_max_bpc',
> 'amd_mem_leak',
> 'amd_mode_switch',
> 'amd_module_load',
> + 'amd_multidisplay_modeset',
This is ok, rest of meson.build cleanup imho should go in
separate patch.
Regards,
Kamil
> + 'amd_odm',
> 'amd_pci_unplug',
> 'amd_plane',
> 'amd_prime',
> 'amd_psr',
> 'amd_ras',
> 'amd_security',
> + 'amd_subvp',
> 'amd_uvd_dec',
> 'amd_uvd_enc',
> 'amd_vce_dec',
> 'amd_vcn',
> 'amd_vm',
> - 'amd_vrr_range',
> - 'amd_mall',
> - 'amd_odm',
> - 'amd_subvp',
> 'amd_vpe',
> + 'amd_vrr_range',
> ]
> if libdrm_amdgpu.version().version_compare('> 2.4.97')
> amdgpu_progs +=[ 'amd_syncobj', ]
> --
> 2.25.1
>
More information about the igt-dev
mailing list