From 8809638e8e42488aac701066d7ced164854c6c9c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 16 Nov 2017 13:33:14 +0100 Subject: tests: Rename chamelium to kms_chamelium. Signed-off-by: Maarten Lankhorst Reviewed-by: Lyude Paul Acked-by: Martin Peres --- tests/Makefile.am | 6 +- tests/chamelium.c | 936 ---------------------------------- tests/intel-ci/fast-feedback.testlist | 18 +- tests/kms_chamelium.c | 936 ++++++++++++++++++++++++++++++++++ tests/meson.build | 2 +- 5 files changed, 949 insertions(+), 949 deletions(-) delete mode 100644 tests/chamelium.c create mode 100644 tests/kms_chamelium.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 89a97015..db360523 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,7 +16,7 @@ endif if HAVE_CHAMELIUM TESTS_progs += \ - chamelium \ + kms_chamelium \ $(NULL) endif @@ -153,8 +153,8 @@ vc4_wait_bo_LDADD = $(LDADD) $(DRM_VC4_LIBS) vc4_wait_seqno_CFLAGS = $(AM_CFLAGS) $(DRM_VC4_CFLAGS) vc4_wait_seqno_LDADD = $(LDADD) $(DRM_VC4_LIBS) -chamelium_CFLAGS = $(AM_CFLAGS) $(XMLRPC_CFLAGS) $(LIBUDEV_CFLAGS) -chamelium_LDADD = $(LDADD) $(XMLRPC_LIBS) $(LIBUDEV_LIBS) +kms_chamelium_CFLAGS = $(AM_CFLAGS) $(XMLRPC_CFLAGS) $(LIBUDEV_CFLAGS) +kms_chamelium_LDADD = $(LDADD) $(XMLRPC_LIBS) $(LIBUDEV_LIBS) audio_CFLAGS = $(AM_CFLAGS) $(ALSA_CFLAGS) audio_LDADD = $(LDADD) $(ALSA_LIBS) diff --git a/tests/chamelium.c b/tests/chamelium.c deleted file mode 100644 index 8855a830..00000000 --- a/tests/chamelium.c +++ /dev/null @@ -1,936 +0,0 @@ -/* - * Copyright © 2016 Red Hat 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 (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. - * - * Authors: - * Lyude Paul - */ - -#include "config.h" -#include "igt.h" - -#include -#include - -typedef struct { - struct chamelium *chamelium; - struct chamelium_port **ports; - igt_display_t display; - int port_count; - - int drm_fd; - - int edid_id; - int alt_edid_id; -} data_t; - -#define HOTPLUG_TIMEOUT 20 /* seconds */ - -#define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */ -#define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */ - -#define HPD_TOGGLE_COUNT_VGA 5 -#define HPD_TOGGLE_COUNT_DP_HDMI 15 -#define HPD_TOGGLE_COUNT_FAST 3 - -static void -get_connectors_link_status_failed(data_t *data, bool *link_status_failed) -{ - drmModeConnector *connector; - uint64_t link_status; - drmModePropertyPtr prop; - int p; - - for (p = 0; p < data->port_count; p++) { - connector = chamelium_port_get_connector(data->chamelium, - data->ports[p], false); - - igt_assert(kmstest_get_property(data->drm_fd, - connector->connector_id, - DRM_MODE_OBJECT_CONNECTOR, - "link-status", NULL, - &link_status, &prop)); - - link_status_failed[p] = link_status == DRM_MODE_LINK_STATUS_BAD; - - drmModeFreeProperty(prop); - drmModeFreeConnector(connector); - } -} - -static void -require_connector_present(data_t *data, unsigned int type) -{ - int i; - bool found = false; - - for (i = 0; i < data->port_count && !found; i++) { - if (chamelium_port_get_type(data->ports[i]) == type) - found = true; - } - - igt_require_f(found, "No port of type %s was found\n", - kmstest_connector_type_str(type)); -} - -static drmModeConnection -reprobe_connector(data_t *data, struct chamelium_port *port) -{ - drmModeConnector *connector; - drmModeConnection status; - - igt_debug("Reprobing %s...\n", chamelium_port_get_name(port)); - connector = chamelium_port_get_connector(data->chamelium, port, true); - igt_assert(connector); - status = connector->connection; - - drmModeFreeConnector(connector); - return status; -} - -static void -wait_for_connector(data_t *data, struct chamelium_port *port, - drmModeConnection status) -{ - bool finished = false; - - igt_debug("Waiting for %s to %sconnect...\n", - chamelium_port_get_name(port), - status == DRM_MODE_DISCONNECTED ? "dis" : ""); - - /* - * Rely on simple reprobing so we don't fail tests that don't require - * that hpd events work in the event that hpd doesn't work on the system - */ - igt_until_timeout(HOTPLUG_TIMEOUT) { - if (reprobe_connector(data, port) == status) { - finished = true; - return; - } - - usleep(50000); - } - - igt_assert(finished); -} - -static int chamelium_vga_modes[][2] = { - { 1600, 1200 }, - { 1920, 1200 }, - { 1920, 1080 }, - { 1680, 1050 }, - { 1280, 1024 }, - { 1280, 960 }, - { 1440, 900 }, - { 1280, 800 }, - { 1024, 768 }, - { 1360, 768 }, - { 1280, 720 }, - { 800, 600 }, - { 640, 480 }, - { -1, -1 }, -}; - -static bool -prune_vga_mode(data_t *data, drmModeModeInfo *mode) -{ - int i = 0; - - while (chamelium_vga_modes[i][0] != -1) { - if (mode->hdisplay == chamelium_vga_modes[i][0] && - mode->vdisplay == chamelium_vga_modes[i][1]) - return false; - - i++; - } - - return true; -} - -static bool -check_analog_bridge(data_t *data, struct chamelium_port *port) -{ - drmModePropertyBlobPtr edid_blob = NULL; - drmModeConnector *connector = chamelium_port_get_connector( - data->chamelium, port, false); - uint64_t edid_blob_id; - unsigned char *edid; - char edid_vendor[3]; - - if (chamelium_port_get_type(port) != DRM_MODE_CONNECTOR_VGA) - return false; - - igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id, - DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL, - &edid_blob_id, NULL)); - igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd, - edid_blob_id)); - - edid = (unsigned char *) edid_blob->data; - - edid_vendor[0] = ((edid[8] & 0x7c) >> 2) + '@'; - edid_vendor[1] = (((edid[8] & 0x03) << 3) | - ((edid[9] & 0xe0) >> 5)) + '@'; - edid_vendor[2] = (edid[9] & 0x1f) + '@'; - - /* Analog bridges provide their own EDID */ - if (edid_vendor[0] != 'I' || edid_vendor[1] != 'G' || - edid_vendor[0] != 'T') - return true; - - drmModeFreePropertyBlob(edid_blob); - drmModeFreeConnector(connector); - - return false; -} - -static void -reset_state(data_t *data, struct chamelium_port *port) -{ - int p; - - chamelium_reset(data->chamelium); - - if (port) { - wait_for_connector(data, port, DRM_MODE_DISCONNECTED); - } else { - for (p = 0; p < data->port_count; p++) { - port = data->ports[p]; - wait_for_connector(data, port, DRM_MODE_DISCONNECTED); - } - } -} - -static void -test_basic_hotplug(data_t *data, struct chamelium_port *port, int toggle_count) -{ - struct udev_monitor *mon = igt_watch_hotplug(); - int i; - - reset_state(data, NULL); - igt_hpd_storm_set_threshold(data->drm_fd, 0); - - for (i = 0; i < toggle_count; i++) { - igt_flush_hotplugs(mon); - - /* Check if we get a sysfs hotplug event */ - chamelium_plug(data->chamelium, port); - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - igt_assert_eq(reprobe_connector(data, port), - DRM_MODE_CONNECTED); - - igt_flush_hotplugs(mon); - - /* Now check if we get a hotplug from disconnection */ - chamelium_unplug(data->chamelium, port); - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - igt_assert_eq(reprobe_connector(data, port), - DRM_MODE_DISCONNECTED); - } - - igt_cleanup_hotplug(mon); - igt_hpd_storm_reset(data->drm_fd); -} - -static void -test_edid_read(data_t *data, struct chamelium_port *port, - int edid_id, const unsigned char *edid) -{ - drmModePropertyBlobPtr edid_blob = NULL; - drmModeConnector *connector = chamelium_port_get_connector( - data->chamelium, port, false); - uint64_t edid_blob_id; - - reset_state(data, port); - - chamelium_port_set_edid(data->chamelium, port, edid_id); - chamelium_plug(data->chamelium, port); - wait_for_connector(data, port, DRM_MODE_CONNECTED); - - igt_skip_on(check_analog_bridge(data, port)); - - igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id, - DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL, - &edid_blob_id, NULL)); - igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd, - edid_blob_id)); - - igt_assert(memcmp(edid, edid_blob->data, EDID_LENGTH) == 0); - - drmModeFreePropertyBlob(edid_blob); - drmModeFreeConnector(connector); -} - -static void -try_suspend_resume_hpd(data_t *data, struct chamelium_port *port, - enum igt_suspend_state state, enum igt_suspend_test test, - struct udev_monitor *mon, bool connected) -{ - int delay; - int p; - - igt_flush_hotplugs(mon); - - delay = igt_get_autoresume_delay(state) * 1000 / 2; - - if (port) { - chamelium_schedule_hpd_toggle(data->chamelium, port, delay, - !connected); - } else { - for (p = 0; p < data->port_count; p++) { - port = data->ports[p]; - chamelium_schedule_hpd_toggle(data->chamelium, port, - delay, !connected); - } - - port = NULL; - } - - igt_system_suspend_autoresume(state, test); - - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - if (port) { - igt_assert_eq(reprobe_connector(data, port), connected ? - DRM_MODE_DISCONNECTED : DRM_MODE_CONNECTED); - } else { - for (p = 0; p < data->port_count; p++) { - port = data->ports[p]; - igt_assert_eq(reprobe_connector(data, port), connected ? - DRM_MODE_DISCONNECTED : - DRM_MODE_CONNECTED); - } - - port = NULL; - } -} - -static void -test_suspend_resume_hpd(data_t *data, struct chamelium_port *port, - enum igt_suspend_state state, - enum igt_suspend_test test) -{ - struct udev_monitor *mon = igt_watch_hotplug(); - - reset_state(data, port); - - /* Make sure we notice new connectors after resuming */ - try_suspend_resume_hpd(data, port, state, test, mon, false); - - /* Now make sure we notice disconnected connectors after resuming */ - try_suspend_resume_hpd(data, port, state, test, mon, true); - - igt_cleanup_hotplug(mon); -} - -static void -test_suspend_resume_hpd_common(data_t *data, enum igt_suspend_state state, - enum igt_suspend_test test) -{ - struct udev_monitor *mon = igt_watch_hotplug(); - struct chamelium_port *port; - int p; - - for (p = 0; p < data->port_count; p++) { - port = data->ports[p]; - igt_debug("Testing port %s\n", chamelium_port_get_name(port)); - } - - reset_state(data, NULL); - - /* Make sure we notice new connectors after resuming */ - try_suspend_resume_hpd(data, NULL, state, test, mon, false); - - /* Now make sure we notice disconnected connectors after resuming */ - try_suspend_resume_hpd(data, NULL, state, test, mon, true); - - igt_cleanup_hotplug(mon); -} - -static void -test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port, - enum igt_suspend_state state, - enum igt_suspend_test test, - int edid_id, - int alt_edid_id) -{ - struct udev_monitor *mon = igt_watch_hotplug(); - bool link_status_failed[2][data->port_count]; - int p; - - reset_state(data, port); - - /* Catch the event and flush all remaining ones. */ - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - igt_flush_hotplugs(mon); - - /* First plug in the port */ - chamelium_port_set_edid(data->chamelium, port, edid_id); - chamelium_plug(data->chamelium, port); - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - - wait_for_connector(data, port, DRM_MODE_CONNECTED); - - /* - * Change the edid before we suspend. On resume, the machine should - * notice the EDID change and fire a hotplug event. - */ - chamelium_port_set_edid(data->chamelium, port, alt_edid_id); - - get_connectors_link_status_failed(data, link_status_failed[0]); - - igt_flush_hotplugs(mon); - - igt_system_suspend_autoresume(state, test); - - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - - get_connectors_link_status_failed(data, link_status_failed[1]); - - for (p = 0; p < data->port_count; p++) - igt_skip_on(!link_status_failed[0][p] && link_status_failed[1][p]); -} - -static igt_output_t * -prepare_output(data_t *data, - struct chamelium_port *port) -{ - igt_display_t *display = &data->display; - igt_output_t *output; - drmModeRes *res; - drmModeConnector *connector = - chamelium_port_get_connector(data->chamelium, port, false); - enum pipe pipe; - bool found = false; - - igt_assert(res = drmModeGetResources(data->drm_fd)); - - /* The chamelium's default EDID has a lot of resolutions, way more then - * we need to test - */ - chamelium_port_set_edid(data->chamelium, port, data->edid_id); - - chamelium_plug(data->chamelium, port); - wait_for_connector(data, port, DRM_MODE_CONNECTED); - - igt_display_reset(display); - - output = igt_output_from_connector(display, connector); - - for_each_pipe(display, pipe) { - if (!igt_pipe_connector_valid(pipe, output)) - continue; - - found = true; - break; - } - - igt_assert_f(found, "No pipe found for output %s\n", igt_output_name(output)); - - igt_output_set_pipe(output, pipe); - - drmModeFreeConnector(connector); - drmModeFreeResources(res); - - return output; -} - -static void -enable_output(data_t *data, - struct chamelium_port *port, - igt_output_t *output, - drmModeModeInfo *mode, - struct igt_fb *fb) -{ - igt_display_t *display = output->display; - igt_plane_t *primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); - drmModeConnector *connector = chamelium_port_get_connector( - data->chamelium, port, false); - - igt_assert(primary); - - igt_plane_set_size(primary, mode->hdisplay, mode->vdisplay); - igt_plane_set_fb(primary, fb); - igt_output_override_mode(output, mode); - - /* Clear any color correction values that might be enabled */ - igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_DEGAMMA_LUT, NULL, 0); - igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_GAMMA_LUT, NULL, 0); - igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_CTM, NULL, 0); - - igt_display_commit2(display, COMMIT_ATOMIC); - - if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_VGA) - usleep(250000); - - drmModeFreeConnector(connector); -} - -static void -test_display_crc(data_t *data, struct chamelium_port *port, int count, - bool fast) -{ - igt_output_t *output; - igt_plane_t *primary; - igt_crc_t *crc; - igt_crc_t *expected_crc; - struct chamelium_fb_crc_async_data *fb_crc; - struct igt_fb fb; - drmModeModeInfo *mode; - drmModeConnector *connector; - int fb_id, i, j, captured_frame_count; - int count_modes; - - reset_state(data, port); - - output = prepare_output(data, port); - connector = chamelium_port_get_connector(data->chamelium, port, false); - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); - igt_assert(primary); - - count_modes = fast ? 1 : connector->count_modes; - - for (i = 0; i < count_modes; i++) { - mode = &connector->modes[i]; - fb_id = igt_create_color_pattern_fb(data->drm_fd, - mode->hdisplay, - mode->vdisplay, - DRM_FORMAT_XRGB8888, - LOCAL_DRM_FORMAT_MOD_NONE, - 0, 0, 0, &fb); - igt_assert(fb_id > 0); - - fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd, - &fb); - - enable_output(data, port, output, mode, &fb); - - /* We want to keep the display running for a little bit, since - * there's always the potential the driver isn't able to keep - * the display running properly for very long - */ - chamelium_capture(data->chamelium, port, 0, 0, 0, 0, count); - crc = chamelium_read_captured_crcs(data->chamelium, - &captured_frame_count); - - igt_assert(captured_frame_count == count); - - igt_debug("Captured %d frames\n", captured_frame_count); - - expected_crc = chamelium_calculate_fb_crc_async_finish(fb_crc); - - for (j = 0; j < captured_frame_count; j++) - chamelium_assert_crc_eq_or_dump(data->chamelium, - expected_crc, &crc[j], - &fb, j); - - free(expected_crc); - free(crc); - - igt_remove_fb(data->drm_fd, &fb); - } - - drmModeFreeConnector(connector); -} - -static void -test_display_frame_dump(data_t *data, struct chamelium_port *port) -{ - igt_output_t *output; - igt_plane_t *primary; - struct igt_fb fb; - struct chamelium_frame_dump *frame; - drmModeModeInfo *mode; - drmModeConnector *connector; - int fb_id, i, j; - - reset_state(data, port); - - output = prepare_output(data, port); - connector = chamelium_port_get_connector(data->chamelium, port, false); - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); - igt_assert(primary); - - for (i = 0; i < connector->count_modes; i++) { - mode = &connector->modes[i]; - fb_id = igt_create_color_pattern_fb(data->drm_fd, - mode->hdisplay, mode->vdisplay, - DRM_FORMAT_XRGB8888, - LOCAL_DRM_FORMAT_MOD_NONE, - 0, 0, 0, &fb); - igt_assert(fb_id > 0); - - enable_output(data, port, output, mode, &fb); - - igt_debug("Reading frame dumps from Chamelium...\n"); - chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 5); - for (j = 0; j < 5; j++) { - frame = chamelium_read_captured_frame( - data->chamelium, j); - chamelium_assert_frame_eq(data->chamelium, frame, &fb); - chamelium_destroy_frame_dump(frame); - } - - igt_remove_fb(data->drm_fd, &fb); - } - - drmModeFreeConnector(connector); -} - -static void -test_analog_frame_dump(data_t *data, struct chamelium_port *port) -{ - igt_output_t *output; - igt_plane_t *primary; - struct igt_fb fb; - struct chamelium_frame_dump *frame; - drmModeModeInfo *mode; - drmModeConnector *connector; - int fb_id, i; - bool bridge; - - reset_state(data, port); - - output = prepare_output(data, port); - connector = chamelium_port_get_connector(data->chamelium, port, false); - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); - igt_assert(primary); - - bridge = check_analog_bridge(data, port); - - for (i = 0; i < connector->count_modes; i++) { - mode = &connector->modes[i]; - - if (bridge && prune_vga_mode(data, mode)) - continue; - - fb_id = igt_create_color_pattern_fb(data->drm_fd, - mode->hdisplay, mode->vdisplay, - DRM_FORMAT_XRGB8888, - LOCAL_DRM_FORMAT_MOD_NONE, - 0, 0, 0, &fb); - igt_assert(fb_id > 0); - - enable_output(data, port, output, mode, &fb); - - igt_debug("Reading frame dumps from Chamelium...\n"); - - frame = chamelium_port_dump_pixels(data->chamelium, port, 0, 0, - 0, 0); - - chamelium_crop_analog_frame(frame, mode->hdisplay, - mode->vdisplay); - - chamelium_assert_analog_frame_match_or_dump(data->chamelium, - port, frame, &fb); - - chamelium_destroy_frame_dump(frame); - - igt_remove_fb(data->drm_fd, &fb); - } - - drmModeFreeConnector(connector); -} - -static void -test_hpd_without_ddc(data_t *data, struct chamelium_port *port) -{ - struct udev_monitor *mon = igt_watch_hotplug(); - - reset_state(data, port); - igt_flush_hotplugs(mon); - - /* Disable the DDC on the connector and make sure we still get a - * hotplug - */ - chamelium_port_set_ddc_state(data->chamelium, port, false); - chamelium_plug(data->chamelium, port); - - igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); - igt_assert_eq(reprobe_connector(data, port), DRM_MODE_CONNECTED); - - igt_cleanup_hotplug(mon); -} - -static void -test_hpd_storm_detect(data_t *data, struct chamelium_port *port, int width) -{ - struct udev_monitor *mon; - int count = 0; - - igt_require_hpd_storm_ctl(data->drm_fd); - reset_state(data, port); - - igt_hpd_storm_set_threshold(data->drm_fd, 1); - chamelium_fire_hpd_pulses(data->chamelium, port, width, 10); - igt_assert(igt_hpd_storm_detected(data->drm_fd)); - - mon = igt_watch_hotplug(); - chamelium_fire_hpd_pulses(data->chamelium, port, width, 10); - - /* - * Polling should have been enabled by the HPD storm at this point, - * so we should only get at most 1 hotplug event - */ - igt_until_timeout(5) - count += igt_hotplug_detected(mon, 1); - igt_assert_lt(count, 2); - - igt_cleanup_hotplug(mon); - igt_hpd_storm_reset(data->drm_fd); -} - -static void -test_hpd_storm_disable(data_t *data, struct chamelium_port *port, int width) -{ - igt_require_hpd_storm_ctl(data->drm_fd); - reset_state(data, port); - - igt_hpd_storm_set_threshold(data->drm_fd, 0); - chamelium_fire_hpd_pulses(data->chamelium, port, - width, 10); - igt_assert(!igt_hpd_storm_detected(data->drm_fd)); - - igt_hpd_storm_reset(data->drm_fd); -} - -#define for_each_port(p, port) \ - for (p = 0, port = data.ports[p]; \ - p < data.port_count; \ - p++, port = data.ports[p]) - -#define connector_subtest(name__, type__) \ - igt_subtest(name__) \ - for_each_port(p, port) \ - if (chamelium_port_get_type(port) == \ - DRM_MODE_CONNECTOR_ ## type__) - -static data_t data; - -igt_main -{ - struct chamelium_port *port; - int edid_id, alt_edid_id, p; - - igt_fixture { - igt_skip_on_simulation(); - - data.drm_fd = drm_open_driver_master(DRIVER_ANY); - data.chamelium = chamelium_init(data.drm_fd); - igt_require(data.chamelium); - - data.ports = chamelium_get_ports(data.chamelium, - &data.port_count); - - edid_id = chamelium_new_edid(data.chamelium, - igt_kms_get_base_edid()); - alt_edid_id = chamelium_new_edid(data.chamelium, - igt_kms_get_alt_edid()); - data.edid_id = edid_id; - data.alt_edid_id = alt_edid_id; - - /* So fbcon doesn't try to reprobe things itself */ - kmstest_set_vt_graphics_mode(); - - igt_display_init(&data.display, data.drm_fd); - igt_require(data.display.is_atomic); - } - - igt_subtest_group { - igt_fixture { - require_connector_present( - &data, DRM_MODE_CONNECTOR_DisplayPort); - } - - connector_subtest("dp-hpd", DisplayPort) - test_basic_hotplug(&data, port, - HPD_TOGGLE_COUNT_DP_HDMI); - - connector_subtest("dp-hpd-fast", DisplayPort) - test_basic_hotplug(&data, port, - HPD_TOGGLE_COUNT_FAST); - - connector_subtest("dp-edid-read", DisplayPort) { - test_edid_read(&data, port, edid_id, - igt_kms_get_base_edid()); - test_edid_read(&data, port, alt_edid_id, - igt_kms_get_alt_edid()); - } - - connector_subtest("dp-hpd-after-suspend", DisplayPort) - test_suspend_resume_hpd(&data, port, - SUSPEND_STATE_MEM, - SUSPEND_TEST_NONE); - - connector_subtest("dp-hpd-after-hibernate", DisplayPort) - test_suspend_resume_hpd(&data, port, - SUSPEND_STATE_DISK, - SUSPEND_TEST_DEVICES); - - connector_subtest("dp-hpd-storm", DisplayPort) - test_hpd_storm_detect(&data, port, - HPD_STORM_PULSE_INTERVAL_DP); - - connector_subtest("dp-hpd-storm-disable", DisplayPort) - test_hpd_storm_disable(&data, port, - HPD_STORM_PULSE_INTERVAL_DP); - - connector_subtest("dp-edid-change-during-suspend", DisplayPort) - test_suspend_resume_edid_change(&data, port, - SUSPEND_STATE_MEM, - SUSPEND_TEST_NONE, - edid_id, alt_edid_id); - - connector_subtest("dp-edid-change-during-hibernate", DisplayPort) - test_suspend_resume_edid_change(&data, port, - SUSPEND_STATE_DISK, - SUSPEND_TEST_DEVICES, - edid_id, alt_edid_id); - - connector_subtest("dp-crc-single", DisplayPort) - test_display_crc(&data, port, 1, false); - - connector_subtest("dp-crc-fast", DisplayPort) - test_display_crc(&data, port, 1, true); - - connector_subtest("dp-crc-multiple", DisplayPort) - test_display_crc(&data, port, 3, false); - - connector_subtest("dp-frame-dump", DisplayPort) - test_display_frame_dump(&data, port); - } - - igt_subtest_group { - igt_fixture { - require_connector_present( - &data, DRM_MODE_CONNECTOR_HDMIA); - } - - connector_subtest("hdmi-hpd", HDMIA) - test_basic_hotplug(&data, port, - HPD_TOGGLE_COUNT_DP_HDMI); - - connector_subtest("hdmi-hpd-fast", HDMIA) - test_basic_hotplug(&data, port, - HPD_TOGGLE_COUNT_FAST); - - connector_subtest("hdmi-edid-read", HDMIA) { - test_edid_read(&data, port, edid_id, - igt_kms_get_base_edid()); - test_edid_read(&data, port, alt_edid_id, - igt_kms_get_alt_edid()); - } - - connector_subtest("hdmi-hpd-after-suspend", HDMIA) - test_suspend_resume_hpd(&data, port, - SUSPEND_STATE_MEM, - SUSPEND_TEST_NONE); - - connector_subtest("hdmi-hpd-after-hibernate", HDMIA) - test_suspend_resume_hpd(&data, port, - SUSPEND_STATE_DISK, - SUSPEND_TEST_DEVICES); - - connector_subtest("hdmi-hpd-storm", HDMIA) - test_hpd_storm_detect(&data, port, - HPD_STORM_PULSE_INTERVAL_HDMI); - - connector_subtest("hdmi-hpd-storm-disable", HDMIA) - test_hpd_storm_disable(&data, port, - HPD_STORM_PULSE_INTERVAL_HDMI); - - connector_subtest("hdmi-edid-change-during-suspend", HDMIA) - test_suspend_resume_edid_change(&data, port, - SUSPEND_STATE_MEM, - SUSPEND_TEST_NONE, - edid_id, alt_edid_id); - - connector_subtest("hdmi-edid-change-during-hibernate", HDMIA) - test_suspend_resume_edid_change(&data, port, - SUSPEND_STATE_DISK, - SUSPEND_TEST_DEVICES, - edid_id, alt_edid_id); - - connector_subtest("hdmi-crc-single", HDMIA) - test_display_crc(&data, port, 1, false); - - connector_subtest("hdmi-crc-fast", HDMIA) - test_display_crc(&data, port, 1, true); - - connector_subtest("hdmi-crc-multiple", HDMIA) - test_display_crc(&data, port, 3, false); - - connector_subtest("hdmi-frame-dump", HDMIA) - test_display_frame_dump(&data, port); - } - - igt_subtest_group { - igt_fixture { - require_connector_present( - &data, DRM_MODE_CONNECTOR_VGA); - } - - connector_subtest("vga-hpd", VGA) - test_basic_hotplug(&data, port, HPD_TOGGLE_COUNT_VGA); - - connector_subtest("vga-hpd-fast", VGA) - test_basic_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST); - - connector_subtest("vga-edid-read", VGA) { - test_edid_read(&data, port, edid_id, - igt_kms_get_base_edid()); - test_edid_read(&data, port, alt_edid_id, - igt_kms_get_alt_edid()); - } - - connector_subtest("vga-hpd-after-suspend", VGA) - test_suspend_resume_hpd(&data, port, - SUSPEND_STATE_MEM, - SUSPEND_TEST_NONE); - - connector_subtest("vga-hpd-after-hibernate", VGA) - test_suspend_resume_hpd(&data, port, - SUSPEND_STATE_DISK, - SUSPEND_TEST_DEVICES); - - connector_subtest("vga-hpd-without-ddc", VGA) - test_hpd_without_ddc(&data, port); - - connector_subtest("vga-frame-dump", VGA) - test_analog_frame_dump(&data, port); - } - - igt_subtest_group { - igt_subtest("common-hpd-after-suspend") - test_suspend_resume_hpd_common(&data, - SUSPEND_STATE_MEM, - SUSPEND_TEST_NONE); - - igt_subtest("common-hpd-after-hibernate") - test_suspend_resume_hpd_common(&data, - SUSPEND_STATE_DISK, - SUSPEND_TEST_DEVICES); - } - - igt_fixture { - igt_display_fini(&data.display); - close(data.drm_fd); - } -} diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist index bf8c1e66..f74da743 100644 --- a/tests/intel-ci/fast-feedback.testlist +++ b/tests/intel-ci/fast-feedback.testlist @@ -1,14 +1,5 @@ # Keep alphabetically sorted by default -igt@chamelium@dp-hpd-fast -igt@chamelium@dp-edid-read -igt@chamelium@dp-crc-fast -igt@chamelium@hdmi-hpd-fast -igt@chamelium@hdmi-edid-read -igt@chamelium@hdmi-crc-fast -igt@chamelium@vga-hpd-fast -igt@chamelium@vga-edid-read -igt@chamelium@common-hpd-after-suspend igt@core_auth@basic-auth igt@core_prop_blob@basic igt@debugfs_test@read_all_entries @@ -208,6 +199,15 @@ igt@kms_addfb_basic@unused-pitches igt@kms_busy@basic-flip-a igt@kms_busy@basic-flip-b igt@kms_busy@basic-flip-c +igt@kms_chamelium@dp-hpd-fast +igt@kms_chamelium@dp-edid-read +igt@kms_chamelium@dp-crc-fast +igt@kms_chamelium@hdmi-hpd-fast +igt@kms_chamelium@hdmi-edid-read +igt@kms_chamelium@hdmi-crc-fast +igt@kms_chamelium@vga-hpd-fast +igt@kms_chamelium@vga-edid-read +igt@kms_chamelium@common-hpd-after-suspend igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy igt@kms_cursor_legacy@basic-flip-after-cursor-atomic diff --git a/tests/kms_chamelium.c b/tests/kms_chamelium.c new file mode 100644 index 00000000..8855a830 --- /dev/null +++ b/tests/kms_chamelium.c @@ -0,0 +1,936 @@ +/* + * Copyright © 2016 Red Hat 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 (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. + * + * Authors: + * Lyude Paul + */ + +#include "config.h" +#include "igt.h" + +#include +#include + +typedef struct { + struct chamelium *chamelium; + struct chamelium_port **ports; + igt_display_t display; + int port_count; + + int drm_fd; + + int edid_id; + int alt_edid_id; +} data_t; + +#define HOTPLUG_TIMEOUT 20 /* seconds */ + +#define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */ +#define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */ + +#define HPD_TOGGLE_COUNT_VGA 5 +#define HPD_TOGGLE_COUNT_DP_HDMI 15 +#define HPD_TOGGLE_COUNT_FAST 3 + +static void +get_connectors_link_status_failed(data_t *data, bool *link_status_failed) +{ + drmModeConnector *connector; + uint64_t link_status; + drmModePropertyPtr prop; + int p; + + for (p = 0; p < data->port_count; p++) { + connector = chamelium_port_get_connector(data->chamelium, + data->ports[p], false); + + igt_assert(kmstest_get_property(data->drm_fd, + connector->connector_id, + DRM_MODE_OBJECT_CONNECTOR, + "link-status", NULL, + &link_status, &prop)); + + link_status_failed[p] = link_status == DRM_MODE_LINK_STATUS_BAD; + + drmModeFreeProperty(prop); + drmModeFreeConnector(connector); + } +} + +static void +require_connector_present(data_t *data, unsigned int type) +{ + int i; + bool found = false; + + for (i = 0; i < data->port_count && !found; i++) { + if (chamelium_port_get_type(data->ports[i]) == type) + found = true; + } + + igt_require_f(found, "No port of type %s was found\n", + kmstest_connector_type_str(type)); +} + +static drmModeConnection +reprobe_connector(data_t *data, struct chamelium_port *port) +{ + drmModeConnector *connector; + drmModeConnection status; + + igt_debug("Reprobing %s...\n", chamelium_port_get_name(port)); + connector = chamelium_port_get_connector(data->chamelium, port, true); + igt_assert(connector); + status = connector->connection; + + drmModeFreeConnector(connector); + return status; +} + +static void +wait_for_connector(data_t *data, struct chamelium_port *port, + drmModeConnection status) +{ + bool finished = false; + + igt_debug("Waiting for %s to %sconnect...\n", + chamelium_port_get_name(port), + status == DRM_MODE_DISCONNECTED ? "dis" : ""); + + /* + * Rely on simple reprobing so we don't fail tests that don't require + * that hpd events work in the event that hpd doesn't work on the system + */ + igt_until_timeout(HOTPLUG_TIMEOUT) { + if (reprobe_connector(data, port) == status) { + finished = true; + return; + } + + usleep(50000); + } + + igt_assert(finished); +} + +static int chamelium_vga_modes[][2] = { + { 1600, 1200 }, + { 1920, 1200 }, + { 1920, 1080 }, + { 1680, 1050 }, + { 1280, 1024 }, + { 1280, 960 }, + { 1440, 900 }, + { 1280, 800 }, + { 1024, 768 }, + { 1360, 768 }, + { 1280, 720 }, + { 800, 600 }, + { 640, 480 }, + { -1, -1 }, +}; + +static bool +prune_vga_mode(data_t *data, drmModeModeInfo *mode) +{ + int i = 0; + + while (chamelium_vga_modes[i][0] != -1) { + if (mode->hdisplay == chamelium_vga_modes[i][0] && + mode->vdisplay == chamelium_vga_modes[i][1]) + return false; + + i++; + } + + return true; +} + +static bool +check_analog_bridge(data_t *data, struct chamelium_port *port) +{ + drmModePropertyBlobPtr edid_blob = NULL; + drmModeConnector *connector = chamelium_port_get_connector( + data->chamelium, port, false); + uint64_t edid_blob_id; + unsigned char *edid; + char edid_vendor[3]; + + if (chamelium_port_get_type(port) != DRM_MODE_CONNECTOR_VGA) + return false; + + igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id, + DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL, + &edid_blob_id, NULL)); + igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd, + edid_blob_id)); + + edid = (unsigned char *) edid_blob->data; + + edid_vendor[0] = ((edid[8] & 0x7c) >> 2) + '@'; + edid_vendor[1] = (((edid[8] & 0x03) << 3) | + ((edid[9] & 0xe0) >> 5)) + '@'; + edid_vendor[2] = (edid[9] & 0x1f) + '@'; + + /* Analog bridges provide their own EDID */ + if (edid_vendor[0] != 'I' || edid_vendor[1] != 'G' || + edid_vendor[0] != 'T') + return true; + + drmModeFreePropertyBlob(edid_blob); + drmModeFreeConnector(connector); + + return false; +} + +static void +reset_state(data_t *data, struct chamelium_port *port) +{ + int p; + + chamelium_reset(data->chamelium); + + if (port) { + wait_for_connector(data, port, DRM_MODE_DISCONNECTED); + } else { + for (p = 0; p < data->port_count; p++) { + port = data->ports[p]; + wait_for_connector(data, port, DRM_MODE_DISCONNECTED); + } + } +} + +static void +test_basic_hotplug(data_t *data, struct chamelium_port *port, int toggle_count) +{ + struct udev_monitor *mon = igt_watch_hotplug(); + int i; + + reset_state(data, NULL); + igt_hpd_storm_set_threshold(data->drm_fd, 0); + + for (i = 0; i < toggle_count; i++) { + igt_flush_hotplugs(mon); + + /* Check if we get a sysfs hotplug event */ + chamelium_plug(data->chamelium, port); + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + igt_assert_eq(reprobe_connector(data, port), + DRM_MODE_CONNECTED); + + igt_flush_hotplugs(mon); + + /* Now check if we get a hotplug from disconnection */ + chamelium_unplug(data->chamelium, port); + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + igt_assert_eq(reprobe_connector(data, port), + DRM_MODE_DISCONNECTED); + } + + igt_cleanup_hotplug(mon); + igt_hpd_storm_reset(data->drm_fd); +} + +static void +test_edid_read(data_t *data, struct chamelium_port *port, + int edid_id, const unsigned char *edid) +{ + drmModePropertyBlobPtr edid_blob = NULL; + drmModeConnector *connector = chamelium_port_get_connector( + data->chamelium, port, false); + uint64_t edid_blob_id; + + reset_state(data, port); + + chamelium_port_set_edid(data->chamelium, port, edid_id); + chamelium_plug(data->chamelium, port); + wait_for_connector(data, port, DRM_MODE_CONNECTED); + + igt_skip_on(check_analog_bridge(data, port)); + + igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id, + DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL, + &edid_blob_id, NULL)); + igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd, + edid_blob_id)); + + igt_assert(memcmp(edid, edid_blob->data, EDID_LENGTH) == 0); + + drmModeFreePropertyBlob(edid_blob); + drmModeFreeConnector(connector); +} + +static void +try_suspend_resume_hpd(data_t *data, struct chamelium_port *port, + enum igt_suspend_state state, enum igt_suspend_test test, + struct udev_monitor *mon, bool connected) +{ + int delay; + int p; + + igt_flush_hotplugs(mon); + + delay = igt_get_autoresume_delay(state) * 1000 / 2; + + if (port) { + chamelium_schedule_hpd_toggle(data->chamelium, port, delay, + !connected); + } else { + for (p = 0; p < data->port_count; p++) { + port = data->ports[p]; + chamelium_schedule_hpd_toggle(data->chamelium, port, + delay, !connected); + } + + port = NULL; + } + + igt_system_suspend_autoresume(state, test); + + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + if (port) { + igt_assert_eq(reprobe_connector(data, port), connected ? + DRM_MODE_DISCONNECTED : DRM_MODE_CONNECTED); + } else { + for (p = 0; p < data->port_count; p++) { + port = data->ports[p]; + igt_assert_eq(reprobe_connector(data, port), connected ? + DRM_MODE_DISCONNECTED : + DRM_MODE_CONNECTED); + } + + port = NULL; + } +} + +static void +test_suspend_resume_hpd(data_t *data, struct chamelium_port *port, + enum igt_suspend_state state, + enum igt_suspend_test test) +{ + struct udev_monitor *mon = igt_watch_hotplug(); + + reset_state(data, port); + + /* Make sure we notice new connectors after resuming */ + try_suspend_resume_hpd(data, port, state, test, mon, false); + + /* Now make sure we notice disconnected connectors after resuming */ + try_suspend_resume_hpd(data, port, state, test, mon, true); + + igt_cleanup_hotplug(mon); +} + +static void +test_suspend_resume_hpd_common(data_t *data, enum igt_suspend_state state, + enum igt_suspend_test test) +{ + struct udev_monitor *mon = igt_watch_hotplug(); + struct chamelium_port *port; + int p; + + for (p = 0; p < data->port_count; p++) { + port = data->ports[p]; + igt_debug("Testing port %s\n", chamelium_port_get_name(port)); + } + + reset_state(data, NULL); + + /* Make sure we notice new connectors after resuming */ + try_suspend_resume_hpd(data, NULL, state, test, mon, false); + + /* Now make sure we notice disconnected connectors after resuming */ + try_suspend_resume_hpd(data, NULL, state, test, mon, true); + + igt_cleanup_hotplug(mon); +} + +static void +test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port, + enum igt_suspend_state state, + enum igt_suspend_test test, + int edid_id, + int alt_edid_id) +{ + struct udev_monitor *mon = igt_watch_hotplug(); + bool link_status_failed[2][data->port_count]; + int p; + + reset_state(data, port); + + /* Catch the event and flush all remaining ones. */ + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + igt_flush_hotplugs(mon); + + /* First plug in the port */ + chamelium_port_set_edid(data->chamelium, port, edid_id); + chamelium_plug(data->chamelium, port); + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + + wait_for_connector(data, port, DRM_MODE_CONNECTED); + + /* + * Change the edid before we suspend. On resume, the machine should + * notice the EDID change and fire a hotplug event. + */ + chamelium_port_set_edid(data->chamelium, port, alt_edid_id); + + get_connectors_link_status_failed(data, link_status_failed[0]); + + igt_flush_hotplugs(mon); + + igt_system_suspend_autoresume(state, test); + + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + + get_connectors_link_status_failed(data, link_status_failed[1]); + + for (p = 0; p < data->port_count; p++) + igt_skip_on(!link_status_failed[0][p] && link_status_failed[1][p]); +} + +static igt_output_t * +prepare_output(data_t *data, + struct chamelium_port *port) +{ + igt_display_t *display = &data->display; + igt_output_t *output; + drmModeRes *res; + drmModeConnector *connector = + chamelium_port_get_connector(data->chamelium, port, false); + enum pipe pipe; + bool found = false; + + igt_assert(res = drmModeGetResources(data->drm_fd)); + + /* The chamelium's default EDID has a lot of resolutions, way more then + * we need to test + */ + chamelium_port_set_edid(data->chamelium, port, data->edid_id); + + chamelium_plug(data->chamelium, port); + wait_for_connector(data, port, DRM_MODE_CONNECTED); + + igt_display_reset(display); + + output = igt_output_from_connector(display, connector); + + for_each_pipe(display, pipe) { + if (!igt_pipe_connector_valid(pipe, output)) + continue; + + found = true; + break; + } + + igt_assert_f(found, "No pipe found for output %s\n", igt_output_name(output)); + + igt_output_set_pipe(output, pipe); + + drmModeFreeConnector(connector); + drmModeFreeResources(res); + + return output; +} + +static void +enable_output(data_t *data, + struct chamelium_port *port, + igt_output_t *output, + drmModeModeInfo *mode, + struct igt_fb *fb) +{ + igt_display_t *display = output->display; + igt_plane_t *primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); + drmModeConnector *connector = chamelium_port_get_connector( + data->chamelium, port, false); + + igt_assert(primary); + + igt_plane_set_size(primary, mode->hdisplay, mode->vdisplay); + igt_plane_set_fb(primary, fb); + igt_output_override_mode(output, mode); + + /* Clear any color correction values that might be enabled */ + igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_DEGAMMA_LUT, NULL, 0); + igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_GAMMA_LUT, NULL, 0); + igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_CTM, NULL, 0); + + igt_display_commit2(display, COMMIT_ATOMIC); + + if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_VGA) + usleep(250000); + + drmModeFreeConnector(connector); +} + +static void +test_display_crc(data_t *data, struct chamelium_port *port, int count, + bool fast) +{ + igt_output_t *output; + igt_plane_t *primary; + igt_crc_t *crc; + igt_crc_t *expected_crc; + struct chamelium_fb_crc_async_data *fb_crc; + struct igt_fb fb; + drmModeModeInfo *mode; + drmModeConnector *connector; + int fb_id, i, j, captured_frame_count; + int count_modes; + + reset_state(data, port); + + output = prepare_output(data, port); + connector = chamelium_port_get_connector(data->chamelium, port, false); + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); + igt_assert(primary); + + count_modes = fast ? 1 : connector->count_modes; + + for (i = 0; i < count_modes; i++) { + mode = &connector->modes[i]; + fb_id = igt_create_color_pattern_fb(data->drm_fd, + mode->hdisplay, + mode->vdisplay, + DRM_FORMAT_XRGB8888, + LOCAL_DRM_FORMAT_MOD_NONE, + 0, 0, 0, &fb); + igt_assert(fb_id > 0); + + fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd, + &fb); + + enable_output(data, port, output, mode, &fb); + + /* We want to keep the display running for a little bit, since + * there's always the potential the driver isn't able to keep + * the display running properly for very long + */ + chamelium_capture(data->chamelium, port, 0, 0, 0, 0, count); + crc = chamelium_read_captured_crcs(data->chamelium, + &captured_frame_count); + + igt_assert(captured_frame_count == count); + + igt_debug("Captured %d frames\n", captured_frame_count); + + expected_crc = chamelium_calculate_fb_crc_async_finish(fb_crc); + + for (j = 0; j < captured_frame_count; j++) + chamelium_assert_crc_eq_or_dump(data->chamelium, + expected_crc, &crc[j], + &fb, j); + + free(expected_crc); + free(crc); + + igt_remove_fb(data->drm_fd, &fb); + } + + drmModeFreeConnector(connector); +} + +static void +test_display_frame_dump(data_t *data, struct chamelium_port *port) +{ + igt_output_t *output; + igt_plane_t *primary; + struct igt_fb fb; + struct chamelium_frame_dump *frame; + drmModeModeInfo *mode; + drmModeConnector *connector; + int fb_id, i, j; + + reset_state(data, port); + + output = prepare_output(data, port); + connector = chamelium_port_get_connector(data->chamelium, port, false); + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); + igt_assert(primary); + + for (i = 0; i < connector->count_modes; i++) { + mode = &connector->modes[i]; + fb_id = igt_create_color_pattern_fb(data->drm_fd, + mode->hdisplay, mode->vdisplay, + DRM_FORMAT_XRGB8888, + LOCAL_DRM_FORMAT_MOD_NONE, + 0, 0, 0, &fb); + igt_assert(fb_id > 0); + + enable_output(data, port, output, mode, &fb); + + igt_debug("Reading frame dumps from Chamelium...\n"); + chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 5); + for (j = 0; j < 5; j++) { + frame = chamelium_read_captured_frame( + data->chamelium, j); + chamelium_assert_frame_eq(data->chamelium, frame, &fb); + chamelium_destroy_frame_dump(frame); + } + + igt_remove_fb(data->drm_fd, &fb); + } + + drmModeFreeConnector(connector); +} + +static void +test_analog_frame_dump(data_t *data, struct chamelium_port *port) +{ + igt_output_t *output; + igt_plane_t *primary; + struct igt_fb fb; + struct chamelium_frame_dump *frame; + drmModeModeInfo *mode; + drmModeConnector *connector; + int fb_id, i; + bool bridge; + + reset_state(data, port); + + output = prepare_output(data, port); + connector = chamelium_port_get_connector(data->chamelium, port, false); + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); + igt_assert(primary); + + bridge = check_analog_bridge(data, port); + + for (i = 0; i < connector->count_modes; i++) { + mode = &connector->modes[i]; + + if (bridge && prune_vga_mode(data, mode)) + continue; + + fb_id = igt_create_color_pattern_fb(data->drm_fd, + mode->hdisplay, mode->vdisplay, + DRM_FORMAT_XRGB8888, + LOCAL_DRM_FORMAT_MOD_NONE, + 0, 0, 0, &fb); + igt_assert(fb_id > 0); + + enable_output(data, port, output, mode, &fb); + + igt_debug("Reading frame dumps from Chamelium...\n"); + + frame = chamelium_port_dump_pixels(data->chamelium, port, 0, 0, + 0, 0); + + chamelium_crop_analog_frame(frame, mode->hdisplay, + mode->vdisplay); + + chamelium_assert_analog_frame_match_or_dump(data->chamelium, + port, frame, &fb); + + chamelium_destroy_frame_dump(frame); + + igt_remove_fb(data->drm_fd, &fb); + } + + drmModeFreeConnector(connector); +} + +static void +test_hpd_without_ddc(data_t *data, struct chamelium_port *port) +{ + struct udev_monitor *mon = igt_watch_hotplug(); + + reset_state(data, port); + igt_flush_hotplugs(mon); + + /* Disable the DDC on the connector and make sure we still get a + * hotplug + */ + chamelium_port_set_ddc_state(data->chamelium, port, false); + chamelium_plug(data->chamelium, port); + + igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT)); + igt_assert_eq(reprobe_connector(data, port), DRM_MODE_CONNECTED); + + igt_cleanup_hotplug(mon); +} + +static void +test_hpd_storm_detect(data_t *data, struct chamelium_port *port, int width) +{ + struct udev_monitor *mon; + int count = 0; + + igt_require_hpd_storm_ctl(data->drm_fd); + reset_state(data, port); + + igt_hpd_storm_set_threshold(data->drm_fd, 1); + chamelium_fire_hpd_pulses(data->chamelium, port, width, 10); + igt_assert(igt_hpd_storm_detected(data->drm_fd)); + + mon = igt_watch_hotplug(); + chamelium_fire_hpd_pulses(data->chamelium, port, width, 10); + + /* + * Polling should have been enabled by the HPD storm at this point, + * so we should only get at most 1 hotplug event + */ + igt_until_timeout(5) + count += igt_hotplug_detected(mon, 1); + igt_assert_lt(count, 2); + + igt_cleanup_hotplug(mon); + igt_hpd_storm_reset(data->drm_fd); +} + +static void +test_hpd_storm_disable(data_t *data, struct chamelium_port *port, int width) +{ + igt_require_hpd_storm_ctl(data->drm_fd); + reset_state(data, port); + + igt_hpd_storm_set_threshold(data->drm_fd, 0); + chamelium_fire_hpd_pulses(data->chamelium, port, + width, 10); + igt_assert(!igt_hpd_storm_detected(data->drm_fd)); + + igt_hpd_storm_reset(data->drm_fd); +} + +#define for_each_port(p, port) \ + for (p = 0, port = data.ports[p]; \ + p < data.port_count; \ + p++, port = data.ports[p]) + +#define connector_subtest(name__, type__) \ + igt_subtest(name__) \ + for_each_port(p, port) \ + if (chamelium_port_get_type(port) == \ + DRM_MODE_CONNECTOR_ ## type__) + +static data_t data; + +igt_main +{ + struct chamelium_port *port; + int edid_id, alt_edid_id, p; + + igt_fixture { + igt_skip_on_simulation(); + + data.drm_fd = drm_open_driver_master(DRIVER_ANY); + data.chamelium = chamelium_init(data.drm_fd); + igt_require(data.chamelium); + + data.ports = chamelium_get_ports(data.chamelium, + &data.port_count); + + edid_id = chamelium_new_edid(data.chamelium, + igt_kms_get_base_edid()); + alt_edid_id = chamelium_new_edid(data.chamelium, + igt_kms_get_alt_edid()); + data.edid_id = edid_id; + data.alt_edid_id = alt_edid_id; + + /* So fbcon doesn't try to reprobe things itself */ + kmstest_set_vt_graphics_mode(); + + igt_display_init(&data.display, data.drm_fd); + igt_require(data.display.is_atomic); + } + + igt_subtest_group { + igt_fixture { + require_connector_present( + &data, DRM_MODE_CONNECTOR_DisplayPort); + } + + connector_subtest("dp-hpd", DisplayPort) + test_basic_hotplug(&data, port, + HPD_TOGGLE_COUNT_DP_HDMI); + + connector_subtest("dp-hpd-fast", DisplayPort) + test_basic_hotplug(&data, port, + HPD_TOGGLE_COUNT_FAST); + + connector_subtest("dp-edid-read", DisplayPort) { + test_edid_read(&data, port, edid_id, + igt_kms_get_base_edid()); + test_edid_read(&data, port, alt_edid_id, + igt_kms_get_alt_edid()); + } + + connector_subtest("dp-hpd-after-suspend", DisplayPort) + test_suspend_resume_hpd(&data, port, + SUSPEND_STATE_MEM, + SUSPEND_TEST_NONE); + + connector_subtest("dp-hpd-after-hibernate", DisplayPort) + test_suspend_resume_hpd(&data, port, + SUSPEND_STATE_DISK, + SUSPEND_TEST_DEVICES); + + connector_subtest("dp-hpd-storm", DisplayPort) + test_hpd_storm_detect(&data, port, + HPD_STORM_PULSE_INTERVAL_DP); + + connector_subtest("dp-hpd-storm-disable", DisplayPort) + test_hpd_storm_disable(&data, port, + HPD_STORM_PULSE_INTERVAL_DP); + + connector_subtest("dp-edid-change-during-suspend", DisplayPort) + test_suspend_resume_edid_change(&data, port, + SUSPEND_STATE_MEM, + SUSPEND_TEST_NONE, + edid_id, alt_edid_id); + + connector_subtest("dp-edid-change-during-hibernate", DisplayPort) + test_suspend_resume_edid_change(&data, port, + SUSPEND_STATE_DISK, + SUSPEND_TEST_DEVICES, + edid_id, alt_edid_id); + + connector_subtest("dp-crc-single", DisplayPort) + test_display_crc(&data, port, 1, false); + + connector_subtest("dp-crc-fast", DisplayPort) + test_display_crc(&data, port, 1, true); + + connector_subtest("dp-crc-multiple", DisplayPort) + test_display_crc(&data, port, 3, false); + + connector_subtest("dp-frame-dump", DisplayPort) + test_display_frame_dump(&data, port); + } + + igt_subtest_group { + igt_fixture { + require_connector_present( + &data, DRM_MODE_CONNECTOR_HDMIA); + } + + connector_subtest("hdmi-hpd", HDMIA) + test_basic_hotplug(&data, port, + HPD_TOGGLE_COUNT_DP_HDMI); + + connector_subtest("hdmi-hpd-fast", HDMIA) + test_basic_hotplug(&data, port, + HPD_TOGGLE_COUNT_FAST); + + connector_subtest("hdmi-edid-read", HDMIA) { + test_edid_read(&data, port, edid_id, + igt_kms_get_base_edid()); + test_edid_read(&data, port, alt_edid_id, + igt_kms_get_alt_edid()); + } + + connector_subtest("hdmi-hpd-after-suspend", HDMIA) + test_suspend_resume_hpd(&data, port, + SUSPEND_STATE_MEM, + SUSPEND_TEST_NONE); + + connector_subtest("hdmi-hpd-after-hibernate", HDMIA) + test_suspend_resume_hpd(&data, port, + SUSPEND_STATE_DISK, + SUSPEND_TEST_DEVICES); + + connector_subtest("hdmi-hpd-storm", HDMIA) + test_hpd_storm_detect(&data, port, + HPD_STORM_PULSE_INTERVAL_HDMI); + + connector_subtest("hdmi-hpd-storm-disable", HDMIA) + test_hpd_storm_disable(&data, port, + HPD_STORM_PULSE_INTERVAL_HDMI); + + connector_subtest("hdmi-edid-change-during-suspend", HDMIA) + test_suspend_resume_edid_change(&data, port, + SUSPEND_STATE_MEM, + SUSPEND_TEST_NONE, + edid_id, alt_edid_id); + + connector_subtest("hdmi-edid-change-during-hibernate", HDMIA) + test_suspend_resume_edid_change(&data, port, + SUSPEND_STATE_DISK, + SUSPEND_TEST_DEVICES, + edid_id, alt_edid_id); + + connector_subtest("hdmi-crc-single", HDMIA) + test_display_crc(&data, port, 1, false); + + connector_subtest("hdmi-crc-fast", HDMIA) + test_display_crc(&data, port, 1, true); + + connector_subtest("hdmi-crc-multiple", HDMIA) + test_display_crc(&data, port, 3, false); + + connector_subtest("hdmi-frame-dump", HDMIA) + test_display_frame_dump(&data, port); + } + + igt_subtest_group { + igt_fixture { + require_connector_present( + &data, DRM_MODE_CONNECTOR_VGA); + } + + connector_subtest("vga-hpd", VGA) + test_basic_hotplug(&data, port, HPD_TOGGLE_COUNT_VGA); + + connector_subtest("vga-hpd-fast", VGA) + test_basic_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST); + + connector_subtest("vga-edid-read", VGA) { + test_edid_read(&data, port, edid_id, + igt_kms_get_base_edid()); + test_edid_read(&data, port, alt_edid_id, + igt_kms_get_alt_edid()); + } + + connector_subtest("vga-hpd-after-suspend", VGA) + test_suspend_resume_hpd(&data, port, + SUSPEND_STATE_MEM, + SUSPEND_TEST_NONE); + + connector_subtest("vga-hpd-after-hibernate", VGA) + test_suspend_resume_hpd(&data, port, + SUSPEND_STATE_DISK, + SUSPEND_TEST_DEVICES); + + connector_subtest("vga-hpd-without-ddc", VGA) + test_hpd_without_ddc(&data, port); + + connector_subtest("vga-frame-dump", VGA) + test_analog_frame_dump(&data, port); + } + + igt_subtest_group { + igt_subtest("common-hpd-after-suspend") + test_suspend_resume_hpd_common(&data, + SUSPEND_STATE_MEM, + SUSPEND_TEST_NONE); + + igt_subtest("common-hpd-after-hibernate") + test_suspend_resume_hpd_common(&data, + SUSPEND_STATE_DISK, + SUSPEND_TEST_DEVICES); + } + + igt_fixture { + igt_display_fini(&data.display); + close(data.drm_fd); + } +} diff --git a/tests/meson.build b/tests/meson.build index c3d5372f..20ff79dc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -251,7 +251,7 @@ endif if chamelium.found() test_progs += [ - 'chamelium', + 'kms_chamelium', ] test_deps += chamelium endif -- cgit v1.2.3