Re: [igt-dev] [PATCH i-g-t 2/2] tests: Add tests for DP MST

Ville Syrjälä
2020-03-24T18:16:03Z

On Fri, Jan 17, 2020 at 10:14:18AM -0800, José Roberto de Souza wrote:
> Test DP MST corner cases.
>
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: José Roberto de Souza <jose.souza@intel.com>
> ---
> tests/Makefile.sources | 1 +
> tests/kms_mst.c | 347 +++++++++++++++++++++++++++++++++++++++++
> tests/meson.build | 1 +
> 3 files changed, 349 insertions(+)
> create mode 100644 tests/kms_mst.c
>
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 806eb02d..68794ed3 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -59,6 +59,7 @@ TESTS_progs = \
> kms_lease \
> kms_legacy_colorkey \
> kms_mmap_write_crc \
> + kms_mst \
> kms_panel_fitting \
> kms_pipe_b_c_ivb \
> kms_pipe_crc_basic \
> diff --git a/tests/kms_mst.c b/tests/kms_mst.c
> new file mode 100644
> index 00000000..7cc01417
> --- /dev/null
> +++ b/tests/kms_mst.c
> @@ -0,0 +1,347 @@
> +#include <ctype.h>
> +#include <string.h>
> +
> +#include "igt.h"
> +
> +IGT_TEST_DESCRIPTION("Test DP MST corner cases.");
> +/*
> + * It is testing only the first MST port found, to test multiple ports or force
> + * test in a specific port changes will be needed
> + */
> +
> +#define SQUARE_SIZE 100
> +#define MAX_MST_OUTPUTS 3
> +#define RESOLUTION_H 1920
> +#define RESOLUTION_V 1080
> +#define ANOTHER_RESOLUTION_H 1024
> +#define ANOTHER_RESOLUTION_V 768

There a quite a lot of assumptions in this test. In genral I get
the feeling this should be more of a general "test all the modeset
combinations" type of thing. It would then get run for all kinds of
other cases with similar behaviour (eg. port sync).

In fact I wonder if something like kms_atomic_transitions doesn't
already hit these cases? I think that's pretty much what it's supposed
to do.

> +
> +typedef struct {
> + int drm_fd;
> + igt_display_t display;
> + igt_output_t *mst_output[MAX_MST_OUTPUTS];
> + struct igt_fb fb[MAX_MST_OUTPUTS];
> + uint8_t mst_connectors, prepared;
> +} data_t;
> +
> +static drmModeModeInfo *find_mode(igt_output_t *output, uint16_t h, uint16_t v,
> + uint32_t *mode_clock)
> +{
> + drmModeModeInfo *ret = NULL;
> + int i;
> +
> + for (i = 0; i < output->config.connector->count_modes; i++) {
> + drmModeModeInfo *mode;
> +
> + mode = &output->config.connector->modes[i];
> + if (mode->hdisplay != h ||
> + mode->vdisplay != v)
> + continue;
> +
> + if (mode_clock && *mode_clock && mode->clock != *mode_clock)
> + continue;
> +
> + if (mode_clock && !*mode_clock)
> + *mode_clock = mode->clock;
> +
> + ret = mode;
> +
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int16_t get_parent_conn_id(const char *mst_path)
> +{
> + int i;
> + char buffer[6];
> +
> + if (mst_path[0] != 'm' || mst_path[1] != 's' || mst_path[2] != 't' ||
> + mst_path[3] != ':')
> + return -1;
> +
> + /* Unlikely to need more than 5 digits to represent a connector id */
> + for (i = 4; i < 9; i++) {
> + if (!isdigit(mst_path[i]))
> + break;
> + }
> +
> + if (i == 4)
> + return -1;
> +
> + i = i - 4;
> + memcpy(buffer, &mst_path[4], i);
> + buffer[i] = 0;
> + return atoi(buffer);
> +}
> +
> +static void search_mst_outputs(data_t *data)
> +{
> + igt_display_t *display = &data->display;
> + igt_output_t *output;
> + uint32_t clock = 0;
> + int16_t parent_conn = -1;
> +
> + for_each_connected_output(display, output) {
> + drmModePropertyBlobPtr blob;
> + char mst_path_buffer[32];
> + drmModeModeInfo *mode;
> + uint64_t val;
> +
> + igt_output_set_pipe(output, PIPE_NONE);
> +
> + if (data->mst_connectors == MAX_MST_OUTPUTS)
> + continue;
> +
> + if (!igt_output_has_prop(output, IGT_CONNECTOR_PATH)) {
> + igt_debug("\tno patch prop\n");
> + continue;
> + }
> +
> + val = igt_output_get_prop(output, IGT_CONNECTOR_PATH);
> + blob = drmModeGetPropertyBlob(data->drm_fd, val);
> + strncpy(mst_path_buffer, (const char *)blob->data,
> + sizeof(mst_path_buffer) - 1);
> + drmModeFreePropertyBlob(blob);
> +
> + if (parent_conn != -1) {
> + if (get_parent_conn_id(mst_path_buffer) != parent_conn)
> + continue;
> + } else {
> + parent_conn = get_parent_conn_id(mst_path_buffer);
> + igt_info("MST parent connector %i\n", parent_conn);
> + }
> +
> + mode = find_mode(output, RESOLUTION_H, RESOLUTION_V, &clock);
> + if (!mode) {
> + igt_info("MST connector %s[%s] found but no compatible mode found\n",
> + output->name, mst_path_buffer);
> + continue;
> + }
> +
> + igt_info("Added MST connector %s[%s]\n", output->name, mst_path_buffer);
> +
> + igt_output_override_mode(output, mode);
> + data->mst_output[data->mst_connectors] = output;
> + data->mst_connectors++;
> + }
> +
> + igt_require_f(data->mst_connectors, "No MST connector found\n");
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +}
> +
> +static void prepare(data_t *data, uint8_t n_outputs)
> +{
> + igt_output_t *output;
> + uint8_t i;
> +
> + for (i = 0; i < data->mst_connectors && i < n_outputs; i++) {
> + const drmModeModeInfo *mode;
> + igt_plane_t *primary;
> + double r, g, b;
> + cairo_t *cr;
> +
> + output = data->mst_output[i];
> + mode = igt_output_get_mode(output);
> +
> + igt_output_set_pipe(output, PIPE_A + i);
> +
> + primary = igt_output_get_plane_type(output,
> + DRM_PLANE_TYPE_PRIMARY);
> + 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->fb[i]);
> +
> + cr = igt_get_cairo_ctx(data->drm_fd, &data->fb[i]);
> +
> + /* paint square in different color */
> + switch (i % 4) {
> + case 0:
> + r = 1.0;
> + g = b = 0.0;
> + break;
> + case 1:
> + g = 1.0;
> + r = b = 0.0;
> + break;
> + case 2:
> + b = 1.0;
> + r = g = 0.0;
> + break;
> + default:
> + case 4:
> + r = g = b = 0.0;
> + break;
> + }
> + igt_paint_color_alpha(cr, 0, 0, SQUARE_SIZE, SQUARE_SIZE,
> + r, g, b, 1.0);
> +
> + igt_plane_set_fb(primary, &data->fb[i]);
> + igt_put_cairo_ctx(data->drm_fd, &data->fb[i], cr);
> + }
> +
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> + data->prepared = n_outputs;
> +}
> +
> +static void change_resolution(data_t *data, bool all)
> +{
> + igt_output_t *output;
> + uint32_t clock = 0;
> + uint8_t i;
> +
> + for (i = 0; i < data->mst_connectors; i++) {
> + drmModeModeInfo *mode;
> +
> + output = data->mst_output[i];
> + if (output->pending_pipe == PIPE_NONE)
> + continue;
> +
> + mode = find_mode(output, ANOTHER_RESOLUTION_H,
> + ANOTHER_RESOLUTION_V, &clock);
> + if (!mode) {
> + igt_info("New resolution not found on %s skipping it\n",
> + output->name);
> + igt_output_set_pipe(output, PIPE_NONE);
> + continue;
> + }
> +
> + igt_output_override_mode(output, mode);
> +
> + if (!all)
> + break;
> + }
> +
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +}
> +
> +static void restore_resolution(data_t *data)
> +{
> + igt_output_t *output;
> + uint32_t clock = 0;
> + uint8_t i;
> +
> + for (i = 0; i < data->mst_connectors; i++) {
> + drmModeModeInfo *mode;
> +
> + output = data->mst_output[i];
> + mode = find_mode(output, RESOLUTION_H, RESOLUTION_V, &clock);
> + /*
> + * No need to check because this mode was already found in
> + * search_mst_outputs()
> + */
> + igt_output_override_mode(output, mode);
> + /* Reenable any sink left off in change_resolution() */
> + igt_output_set_pipe(output, PIPE_A + i);
> + }
> +
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +}
> +
> +static void cleanup(data_t *data)
> +{
> + uint8_t i;
> +
> + for (i = 0; i < data->prepared; i++) {
> + igt_output_t *output = data->mst_output[i];
> + igt_plane_t *primary;
> +
> + primary = igt_output_get_plane_type(output,
> + DRM_PLANE_TYPE_PRIMARY);
> + igt_plane_set_fb(primary, NULL);
> + igt_output_set_pipe(output, PIPE_NONE);
> + }
> +
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +
> + for (i = 0; i < data->prepared; i++)
> + igt_remove_fb(data->drm_fd, &data->fb[i]);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + data_t data = {};
> +
> + igt_subtest_init_parse_opts(&argc, argv, "", NULL,
> + NULL, NULL, NULL);
> + igt_skip_on_simulation();
> +
> + igt_fixture {
> + data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
> + kmstest_set_vt_graphics_mode();
> +
> + igt_display_require(&data.display, data.drm_fd);
> + search_mst_outputs(&data);
> + }
> +
> + igt_describe("Enable all MST streams in the same port");
> + igt_subtest_f("all_enabled") {
> + prepare(&data, data.mst_connectors);
> + igt_debug_manual_check("all", "all streams enabled");
> + cleanup(&data);
> + }
> +
> + igt_describe("Test change the master CRTC of the MST stream");
> + igt_subtest_f("change_master") {
> + enum pipe pipe;
> +
> + igt_require(data.mst_connectors >= 2);
> +
> + prepare(&data, 2);
> + igt_debug_manual_check("all", "dual output");
> +
> + pipe = data.mst_output[0]->pending_pipe;
> + igt_output_set_pipe(data.mst_output[0], PIPE_NONE);
> + igt_display_commit2(&data.display, COMMIT_ATOMIC);
> + igt_debug_manual_check("all", "first MST stream disabled");
> +
> + igt_output_set_pipe(data.mst_output[0], pipe);
> + igt_display_commit2(&data.display, COMMIT_ATOMIC);
> + igt_debug_manual_check("all", "dual output");
> +
> + pipe = data.mst_output[1]->pending_pipe;
> + igt_output_set_pipe(data.mst_output[1], PIPE_NONE);
> + igt_display_commit2(&data.display, COMMIT_ATOMIC);
> + igt_debug_manual_check("all", "second MST stream disabled");
> +
> + igt_output_set_pipe(data.mst_output[1], pipe);
> + igt_display_commit2(&data.display, COMMIT_ATOMIC);
> + igt_debug_manual_check("all", "dual output");
> +
> + cleanup(&data);
> + }
> +
> + igt_describe("Test change the resolution off all MST streams");
> + igt_subtest_f("change_resolution") {
> + prepare(&data, data.mst_connectors);
> + igt_debug_manual_check("all", "regular resolution in all streams");
> +
> + change_resolution(&data, true);
> + igt_debug_manual_check("all", "new resolution set in compatible streams");
> +
> + restore_resolution(&data);
> + igt_debug_manual_check("all", "regular resolution in all streams again");
> + cleanup(&data);
> + }
> +
> + igt_describe("Test do a fullmodeset in the master CRTC of the MST stream");
> + igt_subtest_f("fullmodeset_master") {
> + prepare(&data, data.mst_connectors);
> + igt_debug_manual_check("all", "regular resolution in all streams");
> +
> + change_resolution(&data, false);
> + igt_debug_manual_check("all", "new resolution in the first stream");
> +
> + restore_resolution(&data);
> + igt_debug_manual_check("all", "regular resolution in all streams again");
> + cleanup(&data);
> + }
> +
> + igt_fixture {
> + igt_display_reset(&data.display);
> + igt_display_fini(&data.display);
> + }
> +
> + igt_exit();
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index a79d22ba..a552301b 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -43,6 +43,7 @@ test_progs = [
> 'kms_lease',
> 'kms_legacy_colorkey',
> 'kms_mmap_write_crc',
> + 'kms_mst',
> 'kms_panel_fitting',
> 'kms_pipe_b_c_ivb',
> 'kms_pipe_crc_basic',
> --
> 2.25.0

--
Ville Syrjälä
Intel