From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by gabe.freedesktop.org (Postfix) with ESMTPS id D21B988A27 for ; Mon, 2 Jan 2023 16:50:51 +0000 (UTC) Date: Mon, 2 Jan 2023 17:50:45 +0100 From: Kamil Konieczny To: igt-dev@lists.freedesktop.org Message-ID: References: <20221229192714.40806-1-markyacoub@chromium.org> <87zgb5yrzy.fsf@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <87zgb5yrzy.fsf@intel.com> Subject: Re: [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Yacoub Errors-To: igt-dev-bounces@lists.freedesktop.org Sender: "igt-dev" List-ID: Hi, On 2022-12-30 at 14:51:13 +0200, Jani Nikula wrote: > On Thu, 29 Dec 2022, Mark Yacoub wrote: > > From: Mark Yacoub > > A commit message would be appreciated to let us know what you're doing, > regardless of the "DO NOT REVIEW" part. > > Thanks, > Jani. > There is even better solution, just use igt-trybot intel-gfx-trybot@lists.freedesktop.org Example: https://patchwork.freedesktop.org/series/112048/ Regards, Kamil > > > > --- > > docs/chamelium.txt | 2 +- > > lib/igt_edid.h | 1 + > > lib/igt_eld.h | 1 + > > lib/monitor_edids/monitor_edids_helper.c | 2 +- > > tests/chamelium/kms_chamelium.c | 3132 ----------------- > > tests/chamelium/kms_chamelium_audio.c | 858 +++++ > > ...olor_chamelium.c => kms_chamelium_color.c} | 0 > > tests/chamelium/kms_chamelium_edid.c | 543 +++ > > tests/chamelium/kms_chamelium_frames.c | 1085 ++++++ > > tests/chamelium/kms_chamelium_helper.c | 330 ++ > > tests/chamelium/kms_chamelium_helper.h | 74 + > > tests/chamelium/kms_chamelium_hpd.c | 512 +++ > > tests/intel-ci/blacklist.txt | 2 +- > > tests/intel-ci/fast-feedback.testlist | 18 +- > > tests/kms_color_helper.h | 2 +- > > tests/meson.build | 14 +- > > tests/vc4_ci/vc4-chamelium-fast.testlist | 28 +- > > 17 files changed, 3440 insertions(+), 3164 deletions(-) > > delete mode 100644 tests/chamelium/kms_chamelium.c > > create mode 100644 tests/chamelium/kms_chamelium_audio.c > > rename tests/chamelium/{kms_color_chamelium.c => kms_chamelium_color.c} (100%) > > create mode 100644 tests/chamelium/kms_chamelium_edid.c > > create mode 100644 tests/chamelium/kms_chamelium_frames.c > > create mode 100644 tests/chamelium/kms_chamelium_helper.c > > create mode 100644 tests/chamelium/kms_chamelium_helper.h > > create mode 100644 tests/chamelium/kms_chamelium_hpd.c > > > > diff --git a/docs/chamelium.txt b/docs/chamelium.txt > > index c4c22468..f82c8b0c 100644 > > --- a/docs/chamelium.txt > > +++ b/docs/chamelium.txt > > @@ -241,7 +241,7 @@ Current Support in IGT > > > > Support for the Chamelium platform in IGT is found in the following places: > > * lib/igt_chamelium.c: library with Chamelium-related helpers > > -* tests/kms_chamelium.c: sub-tests using the Chamelium > > +* tests/kms_chamelium_*.c: sub-tests using the Chamelium > > > > As of early April 2019, the following features are tested by IGT: > > * Pixel-by-pixel frame integrity tests for DP and HDMI > > diff --git a/lib/igt_edid.h b/lib/igt_edid.h > > index 477f16c2..85a9ef5e 100644 > > --- a/lib/igt_edid.h > > +++ b/lib/igt_edid.h > > @@ -29,6 +29,7 @@ > > #include "config.h" > > > > #include > > +#include > > > > #include > > > > diff --git a/lib/igt_eld.h b/lib/igt_eld.h > > index 30d7012d..1a46b6d2 100644 > > --- a/lib/igt_eld.h > > +++ b/lib/igt_eld.h > > @@ -29,6 +29,7 @@ > > #include "config.h" > > > > #include > > +#include > > > > #include "igt_edid.h" > > > > diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c > > index 41f199bd..1cbf1c22 100644 > > --- a/lib/monitor_edids/monitor_edids_helper.c > > +++ b/lib/monitor_edids/monitor_edids_helper.c > > @@ -1,4 +1,4 @@ > > -// SPDX-License-Identifier: GPL-2.0 > > +// SPDX-License-Identifier: MIT > > /* > > * A helper library for parsing and making use of real EDID data from monitors > > * and make them compatible with IGT and Chamelium. > > diff --git a/tests/chamelium/kms_chamelium.c b/tests/chamelium/kms_chamelium.c > > deleted file mode 100644 > > index 3c4b4d75..00000000 > > --- a/tests/chamelium/kms_chamelium.c > > +++ /dev/null > > @@ -1,3132 +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 "igt_vc4.h" > > -#include "igt_edid.h" > > -#include "igt_eld.h" > > -#include "igt_infoframe.h" > > -#include "monitor_edids/dp_edids.h" > > -#include "monitor_edids/hdmi_edids.h" > > -#include "monitor_edids/monitor_edids_helper.h" > > - > > -#include > > -#include > > -#include > > -#include > > -// #include > > - > > -// struct chamelium_edid; > > - > > -enum test_modeset_mode { > > - TEST_MODESET_ON, > > - TEST_MODESET_ON_OFF, > > - TEST_MODESET_OFF, > > -}; > > - > > -typedef struct { > > - struct chamelium *chamelium; > > - struct chamelium_port **ports; > > - igt_display_t display; > > - int port_count; > > - > > - int drm_fd; > > - > > - struct chamelium_edid *edids[IGT_CUSTOM_EDID_COUNT]; > > -} data_t; > > - > > -#define ONLINE_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); > > - } > > -} > > - > > -/* Wait for hotplug and return the remaining time left from timeout */ > > -static bool wait_for_hotplug(struct udev_monitor *mon, int *timeout) > > -{ > > - struct timespec start, end; > > - int elapsed; > > - bool detected; > > - > > - igt_assert_eq(igt_gettime(&start), 0); > > - detected = igt_hotplug_detected(mon, *timeout); > > - igt_assert_eq(igt_gettime(&end), 0); > > - > > - elapsed = igt_time_elapsed(&start, &end); > > - igt_assert_lte(0, elapsed); > > - *timeout = max(0, *timeout - elapsed); > > - > > - return detected; > > -} > > - > > -static void > > -wait_for_connector_after_hotplug(data_t *data, struct udev_monitor *mon, > > - struct chamelium_port *port, > > - drmModeConnection status) > > -{ > > - int timeout = CHAMELIUM_HOTPLUG_TIMEOUT; > > - int hotplug_count = 0; > > - > > - igt_debug("Waiting for %s to get %s after a hotplug event...\n", > > - chamelium_port_get_name(port), > > - kmstest_connector_status_str(status)); > > - > > - while (timeout > 0) { > > - if (!wait_for_hotplug(mon, &timeout)) > > - break; > > - > > - hotplug_count++; > > - > > - if (chamelium_reprobe_connector(&data->display, data->chamelium, > > - port) == status) > > - return; > > - } > > - > > - igt_assert_f(false, "Timed out waiting for %s to get %s after a hotplug. Current state %s hotplug_count %d\n", > > - chamelium_port_get_name(port), > > - kmstest_connector_status_str(status), > > - kmstest_connector_status_str(chamelium_reprobe_connector(&data->display, data->chamelium, port)), hotplug_count); > > -} > > - > > - > > -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; > > - const struct edid *edid; > > - char edid_vendor[3]; > > - > > - if (chamelium_port_get_type(port) != DRM_MODE_CONNECTOR_VGA) { > > - drmModeFreeConnector(connector); > > - 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 = (const struct edid *) edid_blob->data; > > - edid_get_mfg(edid, edid_vendor); > > - > > - drmModeFreePropertyBlob(edid_blob); > > - drmModeFreeConnector(connector); > > - > > - /* Analog bridges provide their own EDID */ > > - if (edid_vendor[0] != 'I' || edid_vendor[1] != 'G' || > > - edid_vendor[2] != 'T') > > - return true; > > - > > - return false; > > -} > > - > > -static void chamelium_paint_xr24_pattern(uint32_t *data, > > - size_t width, size_t height, > > - size_t stride, size_t block_size) > > -{ > > - uint32_t colors[] = { 0xff000000, > > - 0xffff0000, > > - 0xff00ff00, > > - 0xff0000ff, > > - 0xffffffff }; > > - unsigned i, j; > > - > > - for (i = 0; i < height; i++) > > - for (j = 0; j < width; j++) > > - *(data + i * stride / 4 + j) = colors[((j / block_size) + (i / block_size)) % 5]; > > -} > > - > > -static int chamelium_get_pattern_fb(data_t *data, size_t width, size_t height, > > - uint32_t fourcc, size_t block_size, > > - struct igt_fb *fb) > > -{ > > - int fb_id; > > - void *ptr; > > - > > - igt_assert(fourcc == DRM_FORMAT_XRGB8888); > > - > > - fb_id = igt_create_fb(data->drm_fd, width, height, fourcc, > > - DRM_FORMAT_MOD_LINEAR, fb); > > - igt_assert(fb_id > 0); > > - > > - ptr = igt_fb_map_buffer(fb->fd, fb); > > - igt_assert(ptr); > > - > > - chamelium_paint_xr24_pattern(ptr, width, height, fb->strides[0], > > - block_size); > > - igt_fb_unmap_buffer(fb, ptr); > > - > > - return fb_id; > > -} > > - > > -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 */ > > - if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_DEGAMMA_LUT)) > > - igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_DEGAMMA_LUT, NULL, 0); > > - if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_GAMMA_LUT)) > > - igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_GAMMA_LUT, NULL, 0); > > - if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_CTM)) > > - 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 enum pipe get_pipe_for_output(igt_display_t *display, igt_output_t *output) > > -{ > > - enum pipe pipe; > > - > > - for_each_pipe(display, pipe) { > > - if (igt_pipe_connector_valid(pipe, output)) { > > - return pipe; > > - } > > - } > > - > > - igt_assert_f(false, "No pipe found for output %s\n", > > - igt_output_name(output)); > > -} > > - > > -static void create_fb_for_mode(data_t *data, struct igt_fb *fb, drmModeModeInfo *mode) > > -{ > > - int fb_id; > > - > > - fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, 64, fb); > > - > > - igt_assert(fb_id > 0); > > -} > > - > > -static drmModeModeInfo get_mode_for_port(struct chamelium *chamelium, > > - struct chamelium_port *port) > > -{ > > - drmModeConnector *connector = chamelium_port_get_connector(chamelium, > > - port, false); > > - drmModeModeInfo mode; > > - igt_assert(&connector->modes[0] != NULL); > > - memcpy(&mode, &connector->modes[0], sizeof(mode)); > > - drmModeFreeConnector(connector); > > - return mode; > > -} > > - > > -static igt_output_t *get_output_for_port(data_t *data, > > - struct chamelium_port *port) > > -{ > > - drmModeConnector *connector = > > - chamelium_port_get_connector(data->chamelium, port, true); > > - igt_output_t *output = igt_output_from_connector(&data->display, > > - connector); > > - drmModeFreeConnector(connector); > > - igt_assert(output != NULL); > > - return output; > > -} > > - > > -static const char test_hotplug_for_each_pipe_desc[] = > > - "Check that we get uevents and updated connector status on " > > - "hotplug and unplug for each pipe with valid output"; > > -static void > > -test_hotplug_for_each_pipe(data_t *data, struct chamelium_port *port) > > -{ > > - igt_output_t *output; > > - enum pipe pipe; > > - struct udev_monitor *mon = igt_watch_uevents(); > > - > > - chamelium_reset_state(&data->display, > > - data->chamelium, > > - port, > > - data->ports, > > - data->port_count); > > - > > - igt_hpd_storm_set_threshold(data->drm_fd, 0); > > - /* Disconnect if any port got connected */ > > - chamelium_unplug(data->chamelium, port); > > - wait_for_connector_after_hotplug(data, mon, port, > > - DRM_MODE_DISCONNECTED); > > - > > - for_each_pipe(&data->display, pipe) { > > - igt_flush_uevents(mon); > > - /* Check if we get a sysfs hotplug event */ > > - chamelium_plug(data->chamelium, port); > > - wait_for_connector_after_hotplug(data, mon, port, > > - DRM_MODE_CONNECTED); > > - igt_flush_uevents(mon); > > - output = get_output_for_port(data, port); > > - > > - /* If pipe is valid for output then set it */ > > - if (igt_pipe_connector_valid(pipe, output)) { > > - igt_output_set_pipe(output, pipe); > > - igt_display_commit2(&data->display, COMMIT_ATOMIC); > > - } > > - > > - chamelium_unplug(data->chamelium, port); > > - wait_for_connector_after_hotplug(data, mon, port, > > - DRM_MODE_DISCONNECTED); > > - igt_flush_uevents(mon); > > - } > > - > > - igt_cleanup_uevents(mon); > > - igt_hpd_storm_reset(data->drm_fd); > > -} > > - > > -static const char test_basic_hotplug_desc[] = > > - "Check that we get uevents and updated connector status on " > > - "hotplug and unplug"; > > -static void > > -test_hotplug(data_t *data, struct chamelium_port *port, int toggle_count, > > - enum test_modeset_mode modeset_mode) > > -{ > > - int i; > > - enum pipe pipe; > > - struct igt_fb fb = {0}; > > - drmModeModeInfo mode; > > - struct udev_monitor *mon = igt_watch_uevents(); > > - igt_output_t *output = get_output_for_port(data, port); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, NULL, > > - data->ports, data->port_count); > > - > > - > > - igt_hpd_storm_set_threshold(data->drm_fd, 0); > > - > > - for (i = 0; i < toggle_count; i++) { > > - igt_flush_uevents(mon); > > - > > - /* Check if we get a sysfs hotplug event */ > > - chamelium_plug(data->chamelium, port); > > - > > - wait_for_connector_after_hotplug(data, mon, port, > > - DRM_MODE_CONNECTED); > > - igt_flush_uevents(mon); > > - > > - if (modeset_mode == TEST_MODESET_ON_OFF || > > - (modeset_mode == TEST_MODESET_ON && i == 0 )) { > > - if (i == 0) { > > - /* We can only get mode and pipe once we are connected */ > > - output = get_output_for_port(data, port); > > - pipe = get_pipe_for_output(&data->display, output); > > - mode = get_mode_for_port(data->chamelium, port); > > - create_fb_for_mode(data, &fb, &mode); > > - } > > - > > - igt_output_set_pipe(output, pipe); > > - enable_output(data, port, output, &mode, &fb); > > - } > > - > > - /* Now check if we get a hotplug from disconnection */ > > - chamelium_unplug(data->chamelium, port); > > - > > - wait_for_connector_after_hotplug(data, mon, port, > > - DRM_MODE_DISCONNECTED); > > - > > - igt_flush_uevents(mon); > > - > > - if (modeset_mode == TEST_MODESET_ON_OFF) { > > - igt_output_set_pipe(output, PIPE_NONE); > > - igt_display_commit2(&data->display, COMMIT_ATOMIC); > > - } > > - } > > - > > - igt_cleanup_uevents(mon); > > - igt_hpd_storm_reset(data->drm_fd); > > - igt_remove_fb(data->drm_fd, &fb); > > -} > > - > > -static void set_edid(data_t *data, struct chamelium_port *port, > > - enum igt_custom_edid_type edid) > > -{ > > - chamelium_port_set_edid(data->chamelium, port, data->edids[edid]); > > -} > > - > > -static const char igt_custom_edid_type_read_desc[] = > > - "Make sure the EDID exposed by KMS is the same as the screen's"; > > -static void > > -igt_custom_edid_type_read(data_t *data, struct chamelium_port *port, enum igt_custom_edid_type edid) > > -{ > > - drmModePropertyBlobPtr edid_blob = NULL; > > - drmModeConnector *connector; > > - size_t raw_edid_size; > > - const struct edid *raw_edid; > > - uint64_t edid_blob_id; > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - set_edid(data, port, edid); > > - chamelium_plug(data->chamelium, port); > > - chamelium_wait_for_conn_status_change(&data->display, data->chamelium, > > - port, DRM_MODE_CONNECTED); > > - > > - igt_skip_on(check_analog_bridge(data, port)); > > - > > - connector = chamelium_port_get_connector(data->chamelium, port, true); > > - 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_id != 0); > > - igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd, > > - edid_blob_id)); > > - > > - raw_edid = chamelium_edid_get_raw(data->edids[edid], port); > > - raw_edid_size = edid_get_size(raw_edid); > > - igt_assert(memcmp(raw_edid, edid_blob->data, raw_edid_size) == 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) > > -{ > > - drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED : > > - DRM_MODE_CONNECTED; > > - int timeout = CHAMELIUM_HOTPLUG_TIMEOUT; > > - int delay; > > - int p; > > - > > - igt_flush_uevents(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(wait_for_hotplug(mon, &timeout)); > > - chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT); > > - > > - if (port) { > > - igt_assert_eq(chamelium_reprobe_connector(&data->display, > > - data->chamelium, > > - port), > > - target_state); > > - } else { > > - for (p = 0; p < data->port_count; p++) { > > - drmModeConnection current_state; > > - > > - port = data->ports[p]; > > - /* > > - * There could be as many hotplug events sent by > > - * driver as connectors we scheduled an HPD toggle on > > - * above, depending on timing. So if we're not seeing > > - * the expected connector state try to wait for an HPD > > - * event for each connector/port. > > - */ > > - current_state = chamelium_reprobe_connector(&data->display, data->chamelium, port); > > - if (p > 0 && current_state != target_state) { > > - igt_assert(wait_for_hotplug(mon, &timeout)); > > - current_state = chamelium_reprobe_connector(&data->display, data->chamelium, port); > > - } > > - > > - igt_assert_eq(current_state, target_state); > > - } > > - > > - port = NULL; > > - } > > -} > > - > > -static const char test_suspend_resume_hpd_desc[] = > > - "Toggle HPD during suspend, check that uevents are sent and connector " > > - "status is updated"; > > -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_uevents(); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - /* 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_uevents(mon); > > -} > > - > > -static const char test_suspend_resume_hpd_common_desc[] = > > - "Toggle HPD during suspend on all connectors, check that uevents are " > > - "sent and connector status is updated"; > > -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_uevents(); > > - 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)); > > - } > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, NULL, > > - data->ports, data->port_count); > > - > > - /* 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_uevents(mon); > > -} > > - > > -static const char test_suspend_resume_edid_change_desc[] = > > - "Simulate a screen being unplugged and another screen being plugged " > > - "during suspend, check that a uevent is sent and connector status is " > > - "updated"; > > -static void > > -test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port, > > - enum igt_suspend_state state, > > - enum igt_suspend_test test, > > - enum igt_custom_edid_type edid, > > - enum igt_custom_edid_type alt_edid) > > -{ > > - struct udev_monitor *mon = igt_watch_uevents(); > > - bool link_status_failed[2][data->port_count]; > > - int p; > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - /* Catch the event and flush all remaining ones. */ > > - igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)); > > - igt_flush_uevents(mon); > > - > > - /* First plug in the port */ > > - set_edid(data, port, edid); > > - chamelium_plug(data->chamelium, port); > > - igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)); > > - > > - chamelium_wait_for_conn_status_change(&data->display, data->chamelium, > > - port, DRM_MODE_CONNECTED); > > - > > - /* > > - * Change the edid before we suspend. On resume, the machine should > > - * notice the EDID change and fire a hotplug event. > > - */ > > - set_edid(data, port, alt_edid); > > - > > - get_connectors_link_status_failed(data, link_status_failed[0]); > > - > > - igt_flush_uevents(mon); > > - > > - igt_system_suspend_autoresume(state, test); > > - igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)); > > - chamelium_assert_reachable(data->chamelium, ONLINE_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, enum igt_custom_edid_type edid) > > -{ > > - igt_display_t *display = &data->display; > > - igt_output_t *output; > > - enum pipe pipe; > > - > > - /* The chamelium's default EDID has a lot of resolutions, way more then > > - * we need to test. Additionally the default EDID doesn't support HDMI > > - * audio. > > - */ > > - set_edid(data, port, edid); > > - > > - chamelium_plug(data->chamelium, port); > > - chamelium_wait_for_conn_status_change(&data->display, data->chamelium, > > - port, DRM_MODE_CONNECTED); > > - > > - igt_display_reset(display); > > - > > - output = get_output_for_port(data, port); > > - > > - /* Refresh pipe to update connected status */ > > - igt_output_set_pipe(output, PIPE_NONE); > > - > > - pipe = get_pipe_for_output(display, output); > > - igt_output_set_pipe(output, pipe); > > - > > - return output; > > -} > > - > > -static void do_test_display(data_t *data, struct chamelium_port *port, > > - igt_output_t *output, drmModeModeInfo *mode, > > - uint32_t fourcc, enum chamelium_check check, > > - int count) > > -{ > > - struct chamelium_fb_crc_async_data *fb_crc; > > - struct igt_fb frame_fb, fb; > > - int i, fb_id, captured_frame_count; > > - int frame_id; > > - > > - fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, 64, &fb); > > - igt_assert(fb_id > 0); > > - > > - frame_id = igt_fb_convert(&frame_fb, &fb, fourcc, > > - DRM_FORMAT_MOD_LINEAR); > > - igt_assert(frame_id > 0); > > - > > - if (check == CHAMELIUM_CHECK_CRC) > > - fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd, > > - &fb); > > - > > - enable_output(data, port, output, mode, &frame_fb); > > - > > - if (check == CHAMELIUM_CHECK_CRC) { > > - igt_crc_t *expected_crc; > > - igt_crc_t *crc; > > - > > - /* 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 (i = 0; i < captured_frame_count; i++) > > - chamelium_assert_crc_eq_or_dump(data->chamelium, > > - expected_crc, &crc[i], > > - &fb, i); > > - > > - free(expected_crc); > > - free(crc); > > - } else if (check == CHAMELIUM_CHECK_ANALOG || > > - check == CHAMELIUM_CHECK_CHECKERBOARD) { > > - struct chamelium_frame_dump *dump; > > - > > - igt_assert(count == 1); > > - > > - dump = chamelium_port_dump_pixels(data->chamelium, port, 0, 0, > > - 0, 0); > > - > > - if (check == CHAMELIUM_CHECK_ANALOG) > > - chamelium_crop_analog_frame(dump, mode->hdisplay, > > - mode->vdisplay); > > - > > - chamelium_assert_frame_match_or_dump(data->chamelium, port, > > - dump, &fb, check); > > - chamelium_destroy_frame_dump(dump); > > - } > > - > > - igt_remove_fb(data->drm_fd, &frame_fb); > > - igt_remove_fb(data->drm_fd, &fb); > > -} > > - > > -static const char test_display_one_mode_desc[] = > > - "Pick the first mode of the IGT base EDID, display and capture a few " > > - "frames, then check captured frames are correct"; > > -static void test_display_one_mode(data_t *data, struct chamelium_port *port, > > - uint32_t fourcc, enum chamelium_check check, > > - int count) > > -{ > > - drmModeConnector *connector; > > - drmModeModeInfo *mode; > > - igt_output_t *output; > > - igt_plane_t *primary; > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - output = prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > - connector = chamelium_port_get_connector(data->chamelium, port, false); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - igt_require(igt_plane_has_format_mod(primary, fourcc, DRM_FORMAT_MOD_LINEAR)); > > - > > - mode = &connector->modes[0]; > > - if (check == CHAMELIUM_CHECK_ANALOG) { > > - bool bridge = check_analog_bridge(data, port); > > - > > - igt_assert(!(bridge && prune_vga_mode(data, mode))); > > - } > > - > > - do_test_display(data, port, output, mode, fourcc, check, count); > > - > > - drmModeFreeConnector(connector); > > -} > > - > > -static const char test_display_all_modes_desc[] = > > - "For each mode of the IGT base EDID, display and capture a few " > > - "frames, then check captured frames are correct"; > > -static void test_display_all_modes(data_t *data, struct chamelium_port *port, > > - uint32_t fourcc, enum chamelium_check check, > > - int count) > > -{ > > - bool bridge; > > - int i, count_modes; > > - > > - if (check == CHAMELIUM_CHECK_ANALOG) > > - bridge = check_analog_bridge(data, port); > > - > > - i = 0; > > - do { > > - igt_output_t *output; > > - igt_plane_t *primary; > > - drmModeConnector *connector; > > - drmModeModeInfo *mode; > > - > > - /* > > - * let's reset state each mode so we will get the > > - * HPD pulses realibably > > - */ > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - /* > > - * modes may change due to mode pruining and link issues, so we > > - * need to refresh the connector > > - */ > > - output = prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > - connector = chamelium_port_get_connector(data->chamelium, port, > > - false); > > - primary = igt_output_get_plane_type(output, > > - DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - igt_require(igt_plane_has_format_mod(primary, fourcc, > > - DRM_FORMAT_MOD_LINEAR)); > > - > > - /* we may skip some modes due to above but that's ok */ > > - count_modes = connector->count_modes; > > - if (i >= count_modes) > > - break; > > - > > - mode = &connector->modes[i]; > > - > > - if (check == CHAMELIUM_CHECK_ANALOG && bridge && > > - prune_vga_mode(data, mode)) > > - continue; > > - > > - do_test_display(data, port, output, mode, fourcc, check, > > - count); > > - drmModeFreeConnector(connector); > > - } while (++i < count_modes); > > -} > > - > > -static const char test_display_frame_dump_desc[] = > > - "For each mode of the IGT base EDID, display and capture a few " > > - "frames, then download the captured frames and compare them " > > - "bit-by-bit to the sent ones"; > > -static void > > -test_display_frame_dump(data_t *data, struct chamelium_port *port) > > -{ > > - > > - int i, count_modes; > > - > > - i = 0; > > - do { > > - igt_output_t *output; > > - igt_plane_t *primary; > > - struct igt_fb fb; > > - struct chamelium_frame_dump *frame; > > - drmModeModeInfo *mode; > > - drmModeConnector *connector; > > - int fb_id, j; > > - > > - /* > > - * let's reset state each mode so we will get the > > - * HPD pulses realibably > > - */ > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - /* > > - * modes may change due to mode pruining and link issues, so we > > - * need to refresh the connector > > - */ > > - output = prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > - connector = chamelium_port_get_connector(data->chamelium, port, > > - false); > > - primary = igt_output_get_plane_type(output, > > - DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - /* we may skip some modes due to above but that's ok */ > > - count_modes = connector->count_modes; > > - if (i >= count_modes) > > - break; > > - > > - mode = &connector->modes[i]; > > - > > - fb_id = igt_create_color_pattern_fb(data->drm_fd, > > - mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, > > - DRM_FORMAT_MOD_LINEAR, > > - 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); > > - } while (++i < count_modes); > > -} > > - > > -#define MODE_CLOCK_ACCURACY 0.05 /* 5% */ > > - > > -static void check_mode(struct chamelium *chamelium, struct chamelium_port *port, > > - drmModeModeInfo *mode) > > -{ > > - struct chamelium_video_params video_params = {0}; > > - double mode_clock; > > - int mode_hsync_offset, mode_vsync_offset; > > - int mode_hsync_width, mode_vsync_width; > > - int mode_hsync_polarity, mode_vsync_polarity; > > - > > - chamelium_port_get_video_params(chamelium, port, &video_params); > > - > > - mode_clock = (double) mode->clock / 1000; > > - > > - if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_DisplayPort) { > > - /* this is what chamelium understands as offsets for DP */ > > - mode_hsync_offset = mode->htotal - mode->hsync_start; > > - mode_vsync_offset = mode->vtotal - mode->vsync_start; > > - } else { > > - /* and this is what they are for other connectors */ > > - mode_hsync_offset = mode->hsync_start - mode->hdisplay; > > - mode_vsync_offset = mode->vsync_start - mode->vdisplay; > > - } > > - > > - mode_hsync_width = mode->hsync_end - mode->hsync_start; > > - mode_vsync_width = mode->vsync_end - mode->vsync_start; > > - > > - mode_hsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC); > > - mode_vsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC); > > - > > - igt_debug("Checking video mode:\n"); > > - igt_debug("clock: got %f, expected %f ± %f%%\n", > > - video_params.clock, mode_clock, MODE_CLOCK_ACCURACY * 100); > > - igt_debug("hactive: got %d, expected %d\n", > > - video_params.hactive, mode->hdisplay); > > - igt_debug("vactive: got %d, expected %d\n", > > - video_params.vactive, mode->vdisplay); > > - igt_debug("hsync_offset: got %d, expected %d\n", > > - video_params.hsync_offset, mode_hsync_offset); > > - igt_debug("vsync_offset: got %d, expected %d\n", > > - video_params.vsync_offset, mode_vsync_offset); > > - igt_debug("htotal: got %d, expected %d\n", > > - video_params.htotal, mode->htotal); > > - igt_debug("vtotal: got %d, expected %d\n", > > - video_params.vtotal, mode->vtotal); > > - igt_debug("hsync_width: got %d, expected %d\n", > > - video_params.hsync_width, mode_hsync_width); > > - igt_debug("vsync_width: got %d, expected %d\n", > > - video_params.vsync_width, mode_vsync_width); > > - igt_debug("hsync_polarity: got %d, expected %d\n", > > - video_params.hsync_polarity, mode_hsync_polarity); > > - igt_debug("vsync_polarity: got %d, expected %d\n", > > - video_params.vsync_polarity, mode_vsync_polarity); > > - > > - if (!isnan(video_params.clock)) { > > - igt_assert(video_params.clock > > > - mode_clock * (1 - MODE_CLOCK_ACCURACY)); > > - igt_assert(video_params.clock < > > - mode_clock * (1 + MODE_CLOCK_ACCURACY)); > > - } > > - igt_assert(video_params.hactive == mode->hdisplay); > > - igt_assert(video_params.vactive == mode->vdisplay); > > - igt_assert(video_params.hsync_offset == mode_hsync_offset); > > - igt_assert(video_params.vsync_offset == mode_vsync_offset); > > - igt_assert(video_params.htotal == mode->htotal); > > - igt_assert(video_params.vtotal == mode->vtotal); > > - igt_assert(video_params.hsync_width == mode_hsync_width); > > - igt_assert(video_params.vsync_width == mode_vsync_width); > > - igt_assert(video_params.hsync_polarity == mode_hsync_polarity); > > - igt_assert(video_params.vsync_polarity == mode_vsync_polarity); > > -} > > - > > -static const char test_mode_timings_desc[] = > > - "For each mode of the IGT base EDID, perform a modeset and check the " > > - "mode detected by the Chamelium receiver matches the mode we set"; > > -static void test_mode_timings(data_t *data, struct chamelium_port *port) > > -{ > > - int i, count_modes; > > - > > - i = 0; > > - igt_require(chamelium_supports_get_video_params(data->chamelium)); > > - do { > > - igt_output_t *output; > > - igt_plane_t *primary; > > - drmModeConnector *connector; > > - drmModeModeInfo *mode; > > - int fb_id; > > - struct igt_fb fb; > > - > > - /* > > - * let's reset state each mode so we will get the > > - * HPD pulses realibably > > - */ > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - /* > > - * modes may change due to mode pruining and link issues, so we > > - * need to refresh the connector > > - */ > > - output = prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > - connector = chamelium_port_get_connector(data->chamelium, port, false); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - /* we may skip some modes due to above but that's ok */ > > - count_modes = connector->count_modes; > > - if (i >= count_modes) > > - break; > > - > > - mode = &connector->modes[i]; > > - > > - fb_id = igt_create_color_pattern_fb(data->drm_fd, > > - mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, > > - DRM_FORMAT_MOD_LINEAR, > > - 0, 0, 0, &fb); > > - igt_assert(fb_id > 0); > > - > > - enable_output(data, port, output, mode, &fb); > > - > > - /* Trigger the FSM */ > > - chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 0); > > - > > - check_mode(data->chamelium, port, mode); > > - > > - igt_remove_fb(data->drm_fd, &fb); > > - drmModeFreeConnector(connector); > > - } while (++i < count_modes); > > -} > > - > > -struct vic_mode { > > - int hactive, vactive; > > - int vrefresh; /* Hz */ > > - uint32_t picture_ar; > > -}; > > - > > -/* Maps Video Identification Codes to a mode */ > > -static const struct vic_mode vic_modes[] = { > > - [16] = { > > - .hactive = 1920, > > - .vactive = 1080, > > - .vrefresh = 60, > > - .picture_ar = DRM_MODE_PICTURE_ASPECT_16_9, > > - }, > > -}; > > - > > -/* Maps aspect ratios to their mode flag */ > > -static const uint32_t mode_ar_flags[] = { > > - [DRM_MODE_PICTURE_ASPECT_16_9] = DRM_MODE_FLAG_PIC_AR_16_9, > > -}; > > - > > -static enum infoframe_avi_picture_aspect_ratio > > -get_infoframe_avi_picture_ar(uint32_t aspect_ratio) > > -{ > > - /* The AVI picture aspect ratio field only supports 4:3 and 16:9 */ > > - switch (aspect_ratio) { > > - case DRM_MODE_PICTURE_ASPECT_4_3: > > - return INFOFRAME_AVI_PIC_AR_4_3; > > - case DRM_MODE_PICTURE_ASPECT_16_9: > > - return INFOFRAME_AVI_PIC_AR_16_9; > > - default: > > - return INFOFRAME_AVI_PIC_AR_UNSPECIFIED; > > - } > > -} > > - > > -static bool vic_mode_matches_drm(const struct vic_mode *vic_mode, > > - drmModeModeInfo *drm_mode) > > -{ > > - uint32_t ar_flag = mode_ar_flags[vic_mode->picture_ar]; > > - > > - return vic_mode->hactive == drm_mode->hdisplay && > > - vic_mode->vactive == drm_mode->vdisplay && > > - vic_mode->vrefresh == drm_mode->vrefresh && > > - ar_flag == (drm_mode->flags & DRM_MODE_FLAG_PIC_AR_MASK); > > -} > > - > > -static const char test_display_aspect_ratio_desc[] = > > - "Pick a mode with a picture aspect-ratio, capture AVI InfoFrames and " > > - "check they include the relevant fields"; > > -static void test_display_aspect_ratio(data_t *data, struct chamelium_port *port) > > -{ > > - igt_output_t *output; > > - igt_plane_t *primary; > > - drmModeConnector *connector; > > - drmModeModeInfo *mode; > > - int fb_id, i; > > - struct igt_fb fb; > > - bool found, ok; > > - struct chamelium_infoframe *infoframe; > > - struct infoframe_avi infoframe_avi; > > - uint8_t vic = 16; /* TODO: test more VICs */ > > - const struct vic_mode *vic_mode; > > - uint32_t aspect_ratio; > > - enum infoframe_avi_picture_aspect_ratio frame_ar; > > - > > - igt_require(chamelium_supports_get_last_infoframe(data->chamelium)); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - output = prepare_output(data, port, IGT_CUSTOM_EDID_ASPECT_RATIO); > > - connector = chamelium_port_get_connector(data->chamelium, port, false); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - vic_mode = &vic_modes[vic]; > > - aspect_ratio = vic_mode->picture_ar; > > - > > - found = false; > > - igt_assert(connector->count_modes > 0); > > - for (i = 0; i < connector->count_modes; i++) { > > - mode = &connector->modes[i]; > > - > > - if (vic_mode_matches_drm(vic_mode, mode)) { > > - found = true; > > - break; > > - } > > - } > > - igt_assert_f(found, > > - "Failed to find mode with the correct aspect ratio\n"); > > - > > - fb_id = igt_create_color_pattern_fb(data->drm_fd, > > - mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, > > - DRM_FORMAT_MOD_LINEAR, > > - 0, 0, 0, &fb); > > - igt_assert(fb_id > 0); > > - > > - enable_output(data, port, output, mode, &fb); > > - > > - infoframe = chamelium_get_last_infoframe(data->chamelium, port, > > - CHAMELIUM_INFOFRAME_AVI); > > - igt_assert_f(infoframe, "AVI InfoFrame not received\n"); > > - > > - ok = infoframe_avi_parse(&infoframe_avi, infoframe->version, > > - infoframe->payload, infoframe->payload_size); > > - igt_assert_f(ok, "Failed to parse AVI InfoFrame\n"); > > - > > - frame_ar = get_infoframe_avi_picture_ar(aspect_ratio); > > - > > - igt_debug("Checking AVI InfoFrame\n"); > > - igt_debug("Picture aspect ratio: got %d, expected %d\n", > > - infoframe_avi.picture_aspect_ratio, frame_ar); > > - igt_debug("Video Identification Code (VIC): got %d, expected %d\n", > > - infoframe_avi.vic, vic); > > - > > - igt_assert(infoframe_avi.picture_aspect_ratio == frame_ar); > > - igt_assert(infoframe_avi.vic == vic); > > - > > - chamelium_infoframe_destroy(infoframe); > > - igt_remove_fb(data->drm_fd, &fb); > > - drmModeFreeConnector(connector); > > -} > > - > > - > > -/* Playback parameters control the audio signal we synthesize and send */ > > -#define PLAYBACK_CHANNELS 2 > > -#define PLAYBACK_SAMPLES 1024 > > - > > -/* Capture paremeters control the audio signal we receive */ > > -#define CAPTURE_SAMPLES 2048 > > - > > -#define AUDIO_TIMEOUT 2000 /* ms */ > > -/* A streak of 3 gives confidence that the signal is good. */ > > -#define MIN_STREAK 3 > > - > > -#define FLATLINE_AMPLITUDE 0.1 /* normalized, ie. in [0, 1] */ > > -#define FLATLINE_AMPLITUDE_ACCURACY 0.001 /* ± 0.1 % of the full amplitude */ > > -#define FLATLINE_ALIGN_ACCURACY 0 /* number of samples */ > > - > > -/* TODO: enable >48KHz rates, these are not reliable */ > > -static int test_sampling_rates[] = { > > - 32000, > > - 44100, > > - 48000, > > - /* 88200, */ > > - /* 96000, */ > > - /* 176400, */ > > - /* 192000, */ > > -}; > > - > > -static int test_sampling_rates_count = sizeof(test_sampling_rates) / sizeof(int); > > - > > -/* Test frequencies (Hz): a sine signal will be generated for each. > > - * > > - * Depending on the sampling rate chosen, it might not be possible to properly > > - * detect the generated sine (see Nyquist–Shannon sampling theorem). > > - * Frequencies that can't be reliably detected will be automatically pruned in > > - * #audio_signal_add_frequency. For instance, the 80KHz frequency can only be > > - * tested with a 192KHz sampling rate. > > - */ > > -static int test_frequencies[] = { > > - 300, > > - 600, > > - 1200, > > - 10000, > > - 80000, > > -}; > > - > > -static int test_frequencies_count = sizeof(test_frequencies) / sizeof(int); > > - > > -static const snd_pcm_format_t test_formats[] = { > > - SND_PCM_FORMAT_S16_LE, > > - SND_PCM_FORMAT_S24_LE, > > - SND_PCM_FORMAT_S32_LE, > > -}; > > - > > -static const size_t test_formats_count = sizeof(test_formats) / sizeof(test_formats[0]); > > - > > -struct audio_state { > > - struct alsa *alsa; > > - struct chamelium *chamelium; > > - struct chamelium_port *port; > > - struct chamelium_stream *stream; > > - > > - /* The capture format is only available after capture has started. */ > > - struct { > > - snd_pcm_format_t format; > > - int channels; > > - int rate; > > - } playback, capture; > > - > > - char *name; > > - struct audio_signal *signal; /* for frequencies test only */ > > - int channel_mapping[CHAMELIUM_MAX_AUDIO_CHANNELS]; > > - > > - size_t recv_pages; > > - int msec; > > - > > - int dump_fd; > > - char *dump_path; > > - > > - pthread_t thread; > > - atomic_bool run; > > - atomic_bool positive; /* for pulse test only */ > > -}; > > - > > -static void audio_state_init(struct audio_state *state, data_t *data, > > - struct alsa *alsa, struct chamelium_port *port, > > - snd_pcm_format_t format, int channels, int rate) > > -{ > > - memset(state, 0, sizeof(*state)); > > - state->dump_fd = -1; > > - > > - state->alsa = alsa; > > - state->chamelium = data->chamelium; > > - state->port = port; > > - > > - state->playback.format = format; > > - state->playback.channels = channels; > > - state->playback.rate = rate; > > - > > - alsa_configure_output(alsa, format, channels, rate); > > - > > - state->stream = chamelium_stream_init(); > > - igt_assert_f(state->stream, > > - "Failed to initialize Chamelium stream client\n"); > > -} > > - > > -static void audio_state_fini(struct audio_state *state) > > -{ > > - chamelium_stream_deinit(state->stream); > > - free(state->name); > > -} > > - > > -static void *run_audio_thread(void *data) > > -{ > > - struct alsa *alsa = data; > > - > > - alsa_run(alsa, -1); > > - return NULL; > > -} > > - > > -static void audio_state_start(struct audio_state *state, const char *name) > > -{ > > - int ret; > > - bool ok; > > - size_t i, j; > > - enum chamelium_stream_realtime_mode stream_mode; > > - char dump_suffix[64]; > > - > > - free(state->name); > > - state->name = strdup(name); > > - state->recv_pages = 0; > > - state->msec = 0; > > - > > - igt_debug("Starting %s test with playback format %s, " > > - "sampling rate %d Hz and %d channels\n", > > - name, snd_pcm_format_name(state->playback.format), > > - state->playback.rate, state->playback.channels); > > - > > - chamelium_start_capturing_audio(state->chamelium, state->port, false); > > - > > - stream_mode = CHAMELIUM_STREAM_REALTIME_STOP_WHEN_OVERFLOW; > > - ok = chamelium_stream_dump_realtime_audio(state->stream, stream_mode); > > - igt_assert_f(ok, "Failed to start streaming audio capture\n"); > > - > > - /* Start playing audio */ > > - state->run = true; > > - ret = pthread_create(&state->thread, NULL, > > - run_audio_thread, state->alsa); > > - igt_assert_f(ret == 0, "Failed to start audio playback thread\n"); > > - > > - /* The Chamelium device only supports this PCM format. */ > > - state->capture.format = SND_PCM_FORMAT_S32_LE; > > - > > - /* Only after we've started playing audio, we can retrieve the capture > > - * format used by the Chamelium device. */ > > - chamelium_get_audio_format(state->chamelium, state->port, > > - &state->capture.rate, > > - &state->capture.channels); > > - if (state->capture.rate == 0) { > > - igt_debug("Audio receiver doesn't indicate the capture " > > - "sampling rate, assuming it's %d Hz\n", > > - state->playback.rate); > > - state->capture.rate = state->playback.rate; > > - } > > - > > - chamelium_get_audio_channel_mapping(state->chamelium, state->port, > > - state->channel_mapping); > > - /* Make sure we can capture all channels we send. */ > > - for (i = 0; i < state->playback.channels; i++) { > > - ok = false; > > - for (j = 0; j < state->capture.channels; j++) { > > - if (state->channel_mapping[j] == i) { > > - ok = true; > > - break; > > - } > > - } > > - igt_assert_f(ok, "Cannot capture all channels\n"); > > - } > > - > > - if (igt_frame_dump_is_enabled()) { > > - snprintf(dump_suffix, sizeof(dump_suffix), > > - "capture-%s-%s-%dch-%dHz", > > - name, snd_pcm_format_name(state->playback.format), > > - state->playback.channels, state->playback.rate); > > - > > - state->dump_fd = audio_create_wav_file_s32_le(dump_suffix, > > - state->capture.rate, > > - state->capture.channels, > > - &state->dump_path); > > - igt_assert_f(state->dump_fd >= 0, > > - "Failed to create audio dump file\n"); > > - } > > -} > > - > > -static void audio_state_receive(struct audio_state *state, > > - int32_t **recv, size_t *recv_len) > > -{ > > - bool ok; > > - size_t page_count; > > - size_t recv_size; > > - > > - ok = chamelium_stream_receive_realtime_audio(state->stream, > > - &page_count, > > - recv, recv_len); > > - igt_assert_f(ok, "Failed to receive audio from stream server\n"); > > - > > - state->msec = state->recv_pages * *recv_len > > - / (double) state->capture.channels > > - / (double) state->capture.rate * 1000; > > - state->recv_pages++; > > - > > - if (state->dump_fd >= 0) { > > - recv_size = *recv_len * sizeof(int32_t); > > - igt_assert_f(write(state->dump_fd, *recv, recv_size) == recv_size, > > - "Failed to write to audio dump file\n"); > > - } > > -} > > - > > -static void audio_state_stop(struct audio_state *state, bool success) > > -{ > > - bool ok; > > - int ret; > > - struct chamelium_audio_file *audio_file; > > - enum igt_log_level log_level; > > - > > - igt_debug("Stopping audio playback\n"); > > - state->run = false; > > - ret = pthread_join(state->thread, NULL); > > - igt_assert_f(ret == 0, "Failed to join audio playback thread\n"); > > - > > - ok = chamelium_stream_stop_realtime_audio(state->stream); > > - igt_assert_f(ok, "Failed to stop streaming audio capture\n"); > > - > > - audio_file = chamelium_stop_capturing_audio(state->chamelium, > > - state->port); > > - if (audio_file) { > > - igt_debug("Audio file saved on the Chamelium in %s\n", > > - audio_file->path); > > - chamelium_destroy_audio_file(audio_file); > > - } > > - > > - if (state->dump_fd >= 0) { > > - close(state->dump_fd); > > - state->dump_fd = -1; > > - > > - if (success) { > > - /* Test succeeded, no need to keep the captured data */ > > - unlink(state->dump_path); > > - } else > > - igt_debug("Saved captured audio data to %s\n", > > - state->dump_path); > > - free(state->dump_path); > > - state->dump_path = NULL; > > - } > > - > > - if (success) > > - log_level = IGT_LOG_DEBUG; > > - else > > - log_level = IGT_LOG_CRITICAL; > > - > > - igt_log(IGT_LOG_DOMAIN, log_level, "Audio %s test result for format %s, " > > - "sampling rate %d Hz and %d channels: %s\n", > > - state->name, snd_pcm_format_name(state->playback.format), > > - state->playback.rate, state->playback.channels, > > - success ? "ALL GREEN" : "FAILED"); > > - > > -} > > - > > -static void check_audio_infoframe(struct audio_state *state) > > -{ > > - struct chamelium_infoframe *infoframe; > > - struct infoframe_audio infoframe_audio; > > - struct infoframe_audio expected = {0}; > > - bool ok; > > - > > - if (!chamelium_supports_get_last_infoframe(state->chamelium)) { > > - igt_debug("Skipping audio InfoFrame check: " > > - "Chamelium board doesn't support GetLastInfoFrame\n"); > > - return; > > - } > > - > > - expected.coding_type = INFOFRAME_AUDIO_CT_PCM; > > - expected.channel_count = state->playback.channels; > > - expected.sampling_freq = state->playback.rate; > > - expected.sample_size = snd_pcm_format_width(state->playback.format); > > - > > - infoframe = chamelium_get_last_infoframe(state->chamelium, state->port, > > - CHAMELIUM_INFOFRAME_AUDIO); > > - if (infoframe == NULL && state->playback.channels <= 2) { > > - /* Audio InfoFrames are optional for mono and stereo audio */ > > - igt_debug("Skipping audio InfoFrame check: " > > - "no InfoFrame received\n"); > > - return; > > - } > > - igt_assert_f(infoframe != NULL, "no audio InfoFrame received\n"); > > - > > - ok = infoframe_audio_parse(&infoframe_audio, infoframe->version, > > - infoframe->payload, infoframe->payload_size); > > - chamelium_infoframe_destroy(infoframe); > > - igt_assert_f(ok, "failed to parse audio InfoFrame\n"); > > - > > - igt_debug("Checking audio InfoFrame:\n"); > > - igt_debug("coding_type: got %d, expected %d\n", > > - infoframe_audio.coding_type, expected.coding_type); > > - igt_debug("channel_count: got %d, expected %d\n", > > - infoframe_audio.channel_count, expected.channel_count); > > - igt_debug("sampling_freq: got %d, expected %d\n", > > - infoframe_audio.sampling_freq, expected.sampling_freq); > > - igt_debug("sample_size: got %d, expected %d\n", > > - infoframe_audio.sample_size, expected.sample_size); > > - > > - if (infoframe_audio.coding_type != INFOFRAME_AUDIO_CT_UNSPECIFIED) > > - igt_assert(infoframe_audio.coding_type == expected.coding_type); > > - if (infoframe_audio.channel_count >= 0) > > - igt_assert(infoframe_audio.channel_count == expected.channel_count); > > - if (infoframe_audio.sampling_freq >= 0) > > - igt_assert(infoframe_audio.sampling_freq == expected.sampling_freq); > > - if (infoframe_audio.sample_size >= 0) > > - igt_assert(infoframe_audio.sample_size == expected.sample_size); > > -} > > - > > -static int > > -audio_output_frequencies_callback(void *data, void *buffer, int samples) > > -{ > > - struct audio_state *state = data; > > - double *tmp; > > - size_t len; > > - > > - len = samples * state->playback.channels; > > - tmp = malloc(len * sizeof(double)); > > - audio_signal_fill(state->signal, tmp, samples); > > - audio_convert_to(buffer, tmp, len, state->playback.format); > > - free(tmp); > > - > > - return state->run ? 0 : -1; > > -} > > - > > -static bool test_audio_frequencies(struct audio_state *state) > > -{ > > - int freq, step; > > - int32_t *recv, *buf; > > - double *channel; > > - size_t i, j, streak; > > - size_t recv_len, buf_len, buf_cap, channel_len; > > - bool success; > > - int capture_chan; > > - > > - state->signal = audio_signal_init(state->playback.channels, > > - state->playback.rate); > > - igt_assert_f(state->signal, "Failed to initialize audio signal\n"); > > - > > - /* We'll choose different frequencies per channel to make sure they are > > - * independent from each other. To do so, we'll add a different offset > > - * to the base frequencies for each channel. We need to choose a big > > - * enough offset so that we're sure to detect mixed up channels. We > > - * choose an offset of two 2 bins in the final FFT to enforce a clear > > - * difference. > > - * > > - * Note that we assume capture_rate == playback_rate. We'll assert this > > - * later on. We cannot retrieve the capture rate before starting > > - * playing audio, so we don't really have the choice. > > - */ > > - step = 2 * state->playback.rate / CAPTURE_SAMPLES; > > - for (i = 0; i < test_frequencies_count; i++) { > > - for (j = 0; j < state->playback.channels; j++) { > > - freq = test_frequencies[i] + j * step; > > - audio_signal_add_frequency(state->signal, freq, j); > > - } > > - } > > - audio_signal_synthesize(state->signal); > > - > > - alsa_register_output_callback(state->alsa, > > - audio_output_frequencies_callback, state, > > - PLAYBACK_SAMPLES); > > - > > - audio_state_start(state, "frequencies"); > > - > > - igt_assert_f(state->capture.rate == state->playback.rate, > > - "Capture rate (%dHz) doesn't match playback rate (%dHz)\n", > > - state->capture.rate, state->playback.rate); > > - > > - /* Needs to be a multiple of 128, because that's the number of samples > > - * we get per channel each time we receive an audio page from the > > - * Chamelium device. > > - * > > - * Additionally, this value needs to be high enough to guarantee we > > - * capture a full period of each sine we generate. If we capture 2048 > > - * samples at a 192KHz sampling rate, we get a full period for a >94Hz > > - * sines. For lower sampling rates, the capture duration will be > > - * longer. > > - */ > > - channel_len = CAPTURE_SAMPLES; > > - channel = malloc(sizeof(double) * channel_len); > > - > > - buf_cap = state->capture.channels * channel_len; > > - buf = malloc(sizeof(int32_t) * buf_cap); > > - buf_len = 0; > > - > > - recv = NULL; > > - recv_len = 0; > > - > > - success = false; > > - streak = 0; > > - while (!success && state->msec < AUDIO_TIMEOUT) { > > - audio_state_receive(state, &recv, &recv_len); > > - > > - memcpy(&buf[buf_len], recv, recv_len * sizeof(int32_t)); > > - buf_len += recv_len; > > - > > - if (buf_len < buf_cap) > > - continue; > > - igt_assert(buf_len == buf_cap); > > - > > - igt_debug("Detecting audio signal, t=%d msec\n", state->msec); > > - > > - for (j = 0; j < state->playback.channels; j++) { > > - capture_chan = state->channel_mapping[j]; > > - igt_assert(capture_chan >= 0); > > - igt_debug("Processing channel %zu (captured as " > > - "channel %d)\n", j, capture_chan); > > - > > - audio_extract_channel_s32_le(channel, channel_len, > > - buf, buf_len, > > - state->capture.channels, > > - capture_chan); > > - > > - if (audio_signal_detect(state->signal, > > - state->capture.rate, j, > > - channel, channel_len)) > > - streak++; > > - else > > - streak = 0; > > - } > > - > > - buf_len = 0; > > - > > - success = streak == MIN_STREAK * state->playback.channels; > > - } > > - > > - audio_state_stop(state, success); > > - > > - free(recv); > > - free(buf); > > - free(channel); > > - audio_signal_fini(state->signal); > > - > > - check_audio_infoframe(state); > > - > > - return success; > > -} > > - > > -static int audio_output_flatline_callback(void *data, void *buffer, > > - int samples) > > -{ > > - struct audio_state *state = data; > > - double *tmp; > > - size_t len, i; > > - > > - len = samples * state->playback.channels; > > - tmp = malloc(len * sizeof(double)); > > - for (i = 0; i < len; i++) > > - tmp[i] = (state->positive ? 1 : -1) * FLATLINE_AMPLITUDE; > > - audio_convert_to(buffer, tmp, len, state->playback.format); > > - free(tmp); > > - > > - return state->run ? 0 : -1; > > -} > > - > > -static bool detect_flatline_amplitude(double *buf, size_t buf_len, bool pos) > > -{ > > - double expected, min, max; > > - size_t i; > > - bool ok; > > - > > - min = max = NAN; > > - for (i = 0; i < buf_len; i++) { > > - if (isnan(min) || buf[i] < min) > > - min = buf[i]; > > - if (isnan(max) || buf[i] > max) > > - max = buf[i]; > > - } > > - > > - expected = (pos ? 1 : -1) * FLATLINE_AMPLITUDE; > > - ok = (min >= expected - FLATLINE_AMPLITUDE_ACCURACY && > > - max <= expected + FLATLINE_AMPLITUDE_ACCURACY); > > - if (ok) > > - igt_debug("Flatline wave amplitude detected\n"); > > - else > > - igt_debug("Flatline amplitude not detected (min=%f, max=%f)\n", > > - min, max); > > - return ok; > > -} > > - > > -static ssize_t detect_falling_edge(double *buf, size_t buf_len) > > -{ > > - size_t i; > > - > > - for (i = 0; i < buf_len; i++) { > > - if (buf[i] < 0) > > - return i; > > - } > > - > > - return -1; > > -} > > - > > -/** test_audio_flatline: > > - * > > - * Send a constant value (one positive, then a negative one) and check that: > > - * > > - * - The amplitude of the flatline is correct > > - * - All channels switch from a positive signal to a negative one at the same > > - * time (ie. all channels are aligned) > > - */ > > -static bool test_audio_flatline(struct audio_state *state) > > -{ > > - bool success, amp_success, align_success; > > - int32_t *recv; > > - size_t recv_len, i, channel_len; > > - ssize_t j; > > - int streak, capture_chan; > > - double *channel; > > - int falling_edges[CHAMELIUM_MAX_AUDIO_CHANNELS]; > > - > > - alsa_register_output_callback(state->alsa, > > - audio_output_flatline_callback, state, > > - PLAYBACK_SAMPLES); > > - > > - /* Start by sending a positive signal */ > > - state->positive = true; > > - > > - audio_state_start(state, "flatline"); > > - > > - for (i = 0; i < state->playback.channels; i++) > > - falling_edges[i] = -1; > > - > > - recv = NULL; > > - recv_len = 0; > > - amp_success = false; > > - streak = 0; > > - while (!amp_success && state->msec < AUDIO_TIMEOUT) { > > - audio_state_receive(state, &recv, &recv_len); > > - > > - igt_debug("Detecting audio signal, t=%d msec\n", state->msec); > > - > > - for (i = 0; i < state->playback.channels; i++) { > > - capture_chan = state->channel_mapping[i]; > > - igt_assert(capture_chan >= 0); > > - igt_debug("Processing channel %zu (captured as " > > - "channel %d)\n", i, capture_chan); > > - > > - channel_len = audio_extract_channel_s32_le(NULL, 0, > > - recv, recv_len, > > - state->capture.channels, > > - capture_chan); > > - channel = malloc(channel_len * sizeof(double)); > > - audio_extract_channel_s32_le(channel, channel_len, > > - recv, recv_len, > > - state->capture.channels, > > - capture_chan); > > - > > - /* Check whether the amplitude is fine */ > > - if (detect_flatline_amplitude(channel, channel_len, > > - state->positive)) > > - streak++; > > - else > > - streak = 0; > > - > > - /* If we're now sending a negative signal, detect the > > - * falling edge */ > > - j = detect_falling_edge(channel, channel_len); > > - if (!state->positive && j >= 0) { > > - falling_edges[i] = recv_len * state->recv_pages > > - + j; > > - } > > - > > - free(channel); > > - } > > - > > - amp_success = streak == MIN_STREAK * state->playback.channels; > > - > > - if (amp_success && state->positive) { > > - /* Switch to a negative signal after we've detected the > > - * positive one. */ > > - state->positive = false; > > - amp_success = false; > > - streak = 0; > > - igt_debug("Switching to negative square wave\n"); > > - } > > - } > > - > > - /* Check alignment between all channels by comparing the index of the > > - * falling edge. */ > > - align_success = true; > > - for (i = 0; i < state->playback.channels; i++) { > > - if (falling_edges[i] < 0) { > > - igt_critical("Falling edge not detected for channel %zu\n", > > - i); > > - align_success = false; > > - continue; > > - } > > - > > - if (abs(falling_edges[0] - falling_edges[i]) > > > - FLATLINE_ALIGN_ACCURACY) { > > - igt_critical("Channel alignment mismatch: " > > - "channel 0 has a falling edge at index %d " > > - "while channel %zu has index %d\n", > > - falling_edges[0], i, falling_edges[i]); > > - align_success = false; > > - } > > - } > > - > > - success = amp_success && align_success; > > - audio_state_stop(state, success); > > - > > - free(recv); > > - > > - return success; > > -} > > - > > -static bool check_audio_configuration(struct alsa *alsa, snd_pcm_format_t format, > > - int channels, int sampling_rate) > > -{ > > - if (!alsa_test_output_configuration(alsa, format, channels, > > - sampling_rate)) { > > - igt_debug("Skipping test with format %s, sampling rate %d Hz " > > - "and %d channels because at least one of the " > > - "selected output devices doesn't support this " > > - "configuration\n", > > - snd_pcm_format_name(format), > > - sampling_rate, channels); > > - return false; > > - } > > - /* TODO: the Chamelium device sends a malformed signal for some audio > > - * configurations. See crbug.com/950917 */ > > - if ((format != SND_PCM_FORMAT_S16_LE && sampling_rate >= 44100) || > > - channels > 2) { > > - igt_debug("Skipping test with format %s, sampling rate %d Hz " > > - "and %d channels because the Chamelium device " > > - "doesn't support this configuration\n", > > - snd_pcm_format_name(format), > > - sampling_rate, channels); > > - return false; > > - } > > - return true; > > -} > > - > > -static const char test_display_audio_desc[] = > > - "Playback various audio signals with various audio formats/rates, " > > - "capture them and check they are correct"; > > -static void > > -test_display_audio(data_t *data, struct chamelium_port *port, > > - const char *audio_device, enum igt_custom_edid_type edid) > > -{ > > - bool run, success; > > - struct alsa *alsa; > > - int ret; > > - igt_output_t *output; > > - igt_plane_t *primary; > > - struct igt_fb fb; > > - drmModeModeInfo *mode; > > - drmModeConnector *connector; > > - int fb_id, i, j; > > - int channels, sampling_rate; > > - snd_pcm_format_t format; > > - struct audio_state state; > > - > > - igt_require(alsa_has_exclusive_access()); > > - > > - /* Old Chamelium devices need an update for DisplayPort audio and > > - * chamelium_get_audio_format support. */ > > - igt_require(chamelium_has_audio_support(data->chamelium, port)); > > - > > - alsa = alsa_init(); > > - igt_assert(alsa); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - output = prepare_output(data, port, edid); > > - connector = chamelium_port_get_connector(data->chamelium, port, false); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - /* Enable the output because the receiver won't try to receive audio if > > - * it doesn't receive video. */ > > - igt_assert(connector->count_modes > 0); > > - mode = &connector->modes[0]; > > - > > - fb_id = igt_create_color_pattern_fb(data->drm_fd, > > - mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, > > - DRM_FORMAT_MOD_LINEAR, > > - 0, 0, 0, &fb); > > - igt_assert(fb_id > 0); > > - > > - enable_output(data, port, output, mode, &fb); > > - > > - run = false; > > - success = true; > > - for (i = 0; i < test_sampling_rates_count; i++) { > > - for (j = 0; j < test_formats_count; j++) { > > - ret = alsa_open_output(alsa, audio_device); > > - igt_assert_f(ret >= 0, "Failed to open ALSA output\n"); > > - > > - /* TODO: playback on all 8 available channels (this > > - * isn't supported by Chamelium devices yet, see > > - * https://crbug.com/950917) */ > > - format = test_formats[j]; > > - channels = PLAYBACK_CHANNELS; > > - sampling_rate = test_sampling_rates[i]; > > - > > - if (!check_audio_configuration(alsa, format, channels, > > - sampling_rate)) > > - continue; > > - > > - run = true; > > - > > - audio_state_init(&state, data, alsa, port, > > - format, channels, sampling_rate); > > - success &= test_audio_frequencies(&state); > > - success &= test_audio_flatline(&state); > > - audio_state_fini(&state); > > - > > - alsa_close_output(alsa); > > - } > > - } > > - > > - /* Make sure we tested at least one frequency and format. */ > > - igt_assert(run); > > - /* Make sure all runs were successful. */ > > - igt_assert(success); > > - > > - igt_remove_fb(data->drm_fd, &fb); > > - > > - drmModeFreeConnector(connector); > > - > > - free(alsa); > > -} > > - > > -static const char test_display_audio_edid_desc[] = > > - "Plug a connector with an EDID suitable for audio, check ALSA's " > > - "EDID-Like Data reports the correct audio parameters"; > > -static void > > -test_display_audio_edid(data_t *data, struct chamelium_port *port, > > - enum igt_custom_edid_type edid) > > -{ > > - igt_output_t *output; > > - igt_plane_t *primary; > > - struct igt_fb fb; > > - drmModeModeInfo *mode; > > - drmModeConnector *connector; > > - int fb_id; > > - struct eld_entry eld; > > - struct eld_sad *sad; > > - > > - igt_require(eld_is_supported()); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - output = prepare_output(data, port, edid); > > - connector = chamelium_port_get_connector(data->chamelium, port, false); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - /* Enable the output because audio cannot be played on inactive > > - * connectors. */ > > - igt_assert(connector->count_modes > 0); > > - mode = &connector->modes[0]; > > - > > - fb_id = igt_create_color_pattern_fb(data->drm_fd, > > - mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, > > - DRM_FORMAT_MOD_LINEAR, > > - 0, 0, 0, &fb); > > - igt_assert(fb_id > 0); > > - > > - enable_output(data, port, output, mode, &fb); > > - > > - igt_assert(eld_get_igt(&eld)); > > - igt_assert(eld.sads_len == 1); > > - > > - sad = &eld.sads[0]; > > - igt_assert(sad->coding_type == CEA_SAD_FORMAT_PCM); > > - igt_assert(sad->channels == 2); > > - igt_assert(sad->rates == (CEA_SAD_SAMPLING_RATE_32KHZ | > > - CEA_SAD_SAMPLING_RATE_44KHZ | CEA_SAD_SAMPLING_RATE_48KHZ)); > > - igt_assert(sad->bits == (CEA_SAD_SAMPLE_SIZE_16 | > > - CEA_SAD_SAMPLE_SIZE_20 | CEA_SAD_SAMPLE_SIZE_24)); > > - > > - igt_remove_fb(data->drm_fd, &fb); > > - > > - drmModeFreeConnector(connector); > > -} > > - > > -static void randomize_plane_stride(data_t *data, > > - uint32_t width, uint32_t height, > > - uint32_t format, uint64_t modifier, > > - size_t *stride) > > -{ > > - size_t stride_min; > > - uint32_t max_tile_w = 4, tile_w, tile_h; > > - int i; > > - struct igt_fb dummy; > > - > > - stride_min = width * igt_format_plane_bpp(format, 0) / 8; > > - > > - /* Randomize the stride to less than twice the minimum. */ > > - *stride = (rand() % stride_min) + stride_min; > > - > > - /* > > - * Create a dummy FB to determine bpp for each plane, and calculate > > - * the maximum tile width from that. > > - */ > > - igt_create_fb(data->drm_fd, 64, 64, format, modifier, &dummy); > > - for (i = 0; i < dummy.num_planes; i++) { > > - igt_get_fb_tile_size(data->drm_fd, modifier, dummy.plane_bpp[i], &tile_w, &tile_h); > > - > > - if (tile_w > max_tile_w) > > - max_tile_w = tile_w; > > - } > > - igt_remove_fb(data->drm_fd, &dummy); > > - > > - /* > > - * Pixman requires the stride to be aligned to 32-bits, which is > > - * reflected in the initial value of max_tile_w and the hw > > - * may require a multiple of tile width, choose biggest of the 2. > > - */ > > - *stride = ALIGN(*stride, max_tile_w); > > -} > > - > > -static void update_tiled_modifier(igt_plane_t *plane, uint32_t width, > > - uint32_t height, uint32_t format, > > - uint64_t *modifier) > > -{ > > - if (*modifier == DRM_FORMAT_MOD_BROADCOM_SAND256) { > > - /* Randomize the column height to less than twice the minimum. */ > > - size_t column_height = (rand() % height) + height; > > - > > - igt_debug("Selecting VC4 SAND256 tiling with column height %ld\n", > > - column_height); > > - > > - *modifier = DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(column_height); > > - } > > -} > > - > > -static void randomize_plane_setup(data_t *data, igt_plane_t *plane, > > - drmModeModeInfo *mode, > > - uint32_t *width, uint32_t *height, > > - uint32_t *format, uint64_t *modifier, > > - bool allow_yuv) > > -{ > > - int min_dim; > > - uint32_t idx[plane->format_mod_count]; > > - unsigned int count = 0; > > - unsigned int i; > > - > > - /* First pass to count the supported formats. */ > > - for (i = 0; i < plane->format_mod_count; i++) > > - if (igt_fb_supported_format(plane->formats[i]) && > > - (allow_yuv || !igt_format_is_yuv(plane->formats[i]))) > > - idx[count++] = i; > > - > > - igt_assert(count > 0); > > - > > - i = idx[rand() % count]; > > - *format = plane->formats[i]; > > - *modifier = plane->modifiers[i]; > > - > > - update_tiled_modifier(plane, *width, *height, *format, modifier); > > - > > - /* > > - * Randomize width and height in the mode dimensions range. > > - * > > - * Restrict to a min of 2 * min_dim, this way src_w/h are always at > > - * least min_dim, because src_w = width - (rand % w / 2). > > - * > > - * Use a minimum dimension of 16 for YUV, because planar YUV > > - * subsamples the UV plane. > > - */ > > - min_dim = igt_format_is_yuv(*format) ? 16 : 8; > > - > > - *width = max((rand() % mode->hdisplay) + 1, 2 * min_dim); > > - *height = max((rand() % mode->vdisplay) + 1, 2 * min_dim); > > -} > > - > > -static void configure_plane(igt_plane_t *plane, uint32_t src_w, uint32_t src_h, > > - uint32_t src_x, uint32_t src_y, uint32_t crtc_w, > > - uint32_t crtc_h, int32_t crtc_x, int32_t crtc_y, > > - struct igt_fb *fb) > > -{ > > - igt_plane_set_fb(plane, fb); > > - > > - igt_plane_set_position(plane, crtc_x, crtc_y); > > - igt_plane_set_size(plane, crtc_w, crtc_h); > > - > > - igt_fb_set_position(fb, plane, src_x, src_y); > > - igt_fb_set_size(fb, plane, src_w, src_h); > > -} > > - > > -static void randomize_plane_coordinates(data_t *data, igt_plane_t *plane, > > - drmModeModeInfo *mode, > > - struct igt_fb *fb, > > - uint32_t *src_w, uint32_t *src_h, > > - uint32_t *src_x, uint32_t *src_y, > > - uint32_t *crtc_w, uint32_t *crtc_h, > > - int32_t *crtc_x, int32_t *crtc_y, > > - bool allow_scaling) > > -{ > > - bool is_yuv = igt_format_is_yuv(fb->drm_format); > > - uint32_t width = fb->width, height = fb->height; > > - double ratio; > > - int ret; > > - > > - /* Randomize source offset in the first half of the original size. */ > > - *src_x = rand() % (width / 2); > > - *src_y = rand() % (height / 2); > > - > > - /* The source size only includes the active source area. */ > > - *src_w = width - *src_x; > > - *src_h = height - *src_y; > > - > > - if (allow_scaling) { > > - *crtc_w = (rand() % mode->hdisplay) + 1; > > - *crtc_h = (rand() % mode->vdisplay) + 1; > > - > > - /* > > - * Don't bother with scaling if dimensions are quite close in > > - * order to get non-scaling cases more frequently. Also limit > > - * scaling to 3x to avoid agressive filtering that makes > > - * comparison less reliable, and don't go above 2x downsampling > > - * to avoid possible hw limitations. > > - */ > > - > > - ratio = ((double) *crtc_w / *src_w); > > - if (ratio < 0.5) > > - *src_w = *crtc_w * 2; > > - else if (ratio > 0.8 && ratio < 1.2) > > - *crtc_w = *src_w; > > - else if (ratio > 3.0) > > - *crtc_w = *src_w * 3; > > - > > - ratio = ((double) *crtc_h / *src_h); > > - if (ratio < 0.5) > > - *src_h = *crtc_h * 2; > > - else if (ratio > 0.8 && ratio < 1.2) > > - *crtc_h = *src_h; > > - else if (ratio > 3.0) > > - *crtc_h = *src_h * 3; > > - } else { > > - *crtc_w = *src_w; > > - *crtc_h = *src_h; > > - } > > - > > - if (*crtc_w != *src_w || *crtc_h != *src_h) { > > - /* > > - * When scaling is involved, make sure to not go off-bounds or > > - * scaled clipping may result in decimal dimensions, that most > > - * drivers don't support. > > - */ > > - if (*crtc_w < mode->hdisplay) > > - *crtc_x = rand() % (mode->hdisplay - *crtc_w); > > - else > > - *crtc_x = 0; > > - > > - if (*crtc_h < mode->vdisplay) > > - *crtc_y = rand() % (mode->vdisplay - *crtc_h); > > - else > > - *crtc_y = 0; > > - } else { > > - /* > > - * Randomize the on-crtc position and allow the plane to go > > - * off-display by less than half of its on-crtc dimensions. > > - */ > > - *crtc_x = (rand() % mode->hdisplay) - *crtc_w / 2; > > - *crtc_y = (rand() % mode->vdisplay) - *crtc_h / 2; > > - } > > - > > - configure_plane(plane, *src_w, *src_h, *src_x, *src_y, > > - *crtc_w, *crtc_h, *crtc_x, *crtc_y, fb); > > - ret = igt_display_try_commit_atomic(&data->display, > > - DRM_MODE_ATOMIC_TEST_ONLY | > > - DRM_MODE_ATOMIC_ALLOW_MODESET, > > - NULL); > > - if (!ret) > > - return; > > - > > - /* Coordinates are logged in the dumped debug log, so only report w/h on failure here. */ > > - igt_assert_f(ret != -ENOSPC,"Failure in testcase, invalid coordinates on a %ux%u fb\n", width, height); > > - > > - /* Make YUV coordinates a multiple of 2 and retry the math. */ > > - if (is_yuv) { > > - *src_x &= ~1; > > - *src_y &= ~1; > > - *src_w &= ~1; > > - *src_h &= ~1; > > - /* To handle 1:1 scaling, clear crtc_w/h too. */ > > - *crtc_w &= ~1; > > - *crtc_h &= ~1; > > - > > - if (*crtc_x < 0 && (*crtc_x & 1)) > > - (*crtc_x)++; > > - else > > - *crtc_x &= ~1; > > - > > - /* If negative, round up to 0 instead of down */ > > - if (*crtc_y < 0 && (*crtc_y & 1)) > > - (*crtc_y)++; > > - else > > - *crtc_y &= ~1; > > - > > - configure_plane(plane, *src_w, *src_h, *src_x, *src_y, *crtc_w, > > - *crtc_h, *crtc_x, *crtc_y, fb); > > - ret = igt_display_try_commit_atomic(&data->display, > > - DRM_MODE_ATOMIC_TEST_ONLY | > > - DRM_MODE_ATOMIC_ALLOW_MODESET, > > - NULL); > > - if (!ret) > > - return; > > - } > > - > > - igt_assert(!ret || allow_scaling); > > - igt_info("Scaling ratio %g / %g failed, trying without scaling.\n", > > - ((double) *crtc_w / *src_w), ((double) *crtc_h / *src_h)); > > - > > - *crtc_w = *src_w; > > - *crtc_h = *src_h; > > - > > - configure_plane(plane, *src_w, *src_h, *src_x, *src_y, *crtc_w, > > - *crtc_h, *crtc_x, *crtc_y, fb); > > - igt_display_commit_atomic(&data->display, > > - DRM_MODE_ATOMIC_TEST_ONLY | > > - DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); > > -} > > - > > -static void blit_plane_cairo(data_t *data, cairo_surface_t *result, > > - uint32_t src_w, uint32_t src_h, > > - uint32_t src_x, uint32_t src_y, > > - uint32_t crtc_w, uint32_t crtc_h, > > - int32_t crtc_x, int32_t crtc_y, > > - struct igt_fb *fb) > > -{ > > - cairo_surface_t *surface; > > - cairo_surface_t *clipped_surface; > > - cairo_t *cr; > > - > > - surface = igt_get_cairo_surface(data->drm_fd, fb); > > - > > - if (src_x || src_y) { > > - clipped_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, > > - src_w, src_h); > > - > > - cr = cairo_create(clipped_surface); > > - > > - cairo_translate(cr, -1. * src_x, -1. * src_y); > > - > > - cairo_set_source_surface(cr, surface, 0, 0); > > - > > - cairo_paint(cr); > > - cairo_surface_flush(clipped_surface); > > - > > - cairo_destroy(cr); > > - } else { > > - clipped_surface = surface; > > - } > > - > > - cr = cairo_create(result); > > - > > - cairo_translate(cr, crtc_x, crtc_y); > > - > > - if (src_w != crtc_w || src_h != crtc_h) { > > - cairo_scale(cr, (double) crtc_w / src_w, > > - (double) crtc_h / src_h); > > - } > > - > > - cairo_set_source_surface(cr, clipped_surface, 0, 0); > > - cairo_surface_destroy(clipped_surface); > > - > > - if (src_w != crtc_w || src_h != crtc_h) { > > - cairo_pattern_set_filter(cairo_get_source(cr), > > - CAIRO_FILTER_BILINEAR); > > - cairo_pattern_set_extend(cairo_get_source(cr), > > - CAIRO_EXTEND_NONE); > > - } > > - > > - cairo_paint(cr); > > - cairo_surface_flush(result); > > - > > - cairo_destroy(cr); > > -} > > - > > -static void prepare_randomized_plane(data_t *data, > > - drmModeModeInfo *mode, > > - igt_plane_t *plane, > > - struct igt_fb *overlay_fb, > > - unsigned int index, > > - cairo_surface_t *result_surface, > > - bool allow_scaling, bool allow_yuv) > > -{ > > - struct igt_fb pattern_fb; > > - uint32_t overlay_fb_w, overlay_fb_h; > > - uint32_t overlay_src_w, overlay_src_h; > > - uint32_t overlay_src_x, overlay_src_y; > > - int32_t overlay_crtc_x, overlay_crtc_y; > > - uint32_t overlay_crtc_w, overlay_crtc_h; > > - uint32_t format; > > - uint64_t modifier; > > - size_t stride; > > - bool tiled; > > - int fb_id; > > - > > - randomize_plane_setup(data, plane, mode, &overlay_fb_w, &overlay_fb_h, > > - &format, &modifier, allow_yuv); > > - > > - tiled = (modifier != DRM_FORMAT_MOD_LINEAR); > > - igt_debug("Plane %d: framebuffer size %dx%d %s format (%s)\n", > > - index, overlay_fb_w, overlay_fb_h, > > - igt_format_str(format), tiled ? "tiled" : "linear"); > > - > > - /* Get a pattern framebuffer for the overlay plane. */ > > - fb_id = chamelium_get_pattern_fb(data, overlay_fb_w, overlay_fb_h, > > - DRM_FORMAT_XRGB8888, 32, &pattern_fb); > > - igt_assert(fb_id > 0); > > - > > - randomize_plane_stride(data, overlay_fb_w, overlay_fb_h, > > - format, modifier, &stride); > > - > > - igt_debug("Plane %d: stride %ld\n", index, stride); > > - > > - fb_id = igt_fb_convert_with_stride(overlay_fb, &pattern_fb, format, > > - modifier, stride); > > - igt_assert(fb_id > 0); > > - > > - randomize_plane_coordinates(data, plane, mode, overlay_fb, > > - &overlay_src_w, &overlay_src_h, > > - &overlay_src_x, &overlay_src_y, > > - &overlay_crtc_w, &overlay_crtc_h, > > - &overlay_crtc_x, &overlay_crtc_y, > > - allow_scaling); > > - > > - igt_debug("Plane %d: in-framebuffer size %dx%d\n", index, > > - overlay_src_w, overlay_src_h); > > - igt_debug("Plane %d: in-framebuffer position %dx%d\n", index, > > - overlay_src_x, overlay_src_y); > > - igt_debug("Plane %d: on-crtc size %dx%d\n", index, > > - overlay_crtc_w, overlay_crtc_h); > > - igt_debug("Plane %d: on-crtc position %dx%d\n", index, > > - overlay_crtc_x, overlay_crtc_y); > > - > > - blit_plane_cairo(data, result_surface, overlay_src_w, overlay_src_h, > > - overlay_src_x, overlay_src_y, > > - overlay_crtc_w, overlay_crtc_h, > > - overlay_crtc_x, overlay_crtc_y, &pattern_fb); > > - > > - /* Remove the original pattern framebuffer. */ > > - igt_remove_fb(data->drm_fd, &pattern_fb); > > -} > > - > > -static const char test_display_planes_random_desc[] = > > - "Setup a few overlay planes with random parameters, capture the frame " > > - "and check it matches the expected output"; > > -static void test_display_planes_random(data_t *data, > > - struct chamelium_port *port, > > - enum chamelium_check check) > > -{ > > - igt_output_t *output; > > - drmModeModeInfo *mode; > > - igt_plane_t *primary_plane; > > - struct igt_fb primary_fb; > > - struct igt_fb result_fb; > > - struct igt_fb *overlay_fbs; > > - igt_crc_t *crc; > > - igt_crc_t *expected_crc; > > - struct chamelium_fb_crc_async_data *fb_crc; > > - unsigned int overlay_planes_max = 0; > > - unsigned int overlay_planes_count; > > - cairo_surface_t *result_surface; > > - int captured_frame_count; > > - bool allow_scaling; > > - bool allow_yuv; > > - unsigned int i; > > - unsigned int fb_id; > > - > > - switch (check) { > > - case CHAMELIUM_CHECK_CRC: > > - allow_scaling = false; > > - allow_yuv = false; > > - break; > > - case CHAMELIUM_CHECK_CHECKERBOARD: > > - allow_scaling = true; > > - allow_yuv = true; > > - break; > > - default: > > - igt_assert(false); > > - } > > - > > - srand(time(NULL)); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - /* Find the connector and pipe. */ > > - output = prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > - > > - mode = igt_output_get_mode(output); > > - > > - /* Get a framebuffer for the primary plane. */ > > - primary_plane = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary_plane); > > - > > - fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay, > > - DRM_FORMAT_XRGB8888, 64, &primary_fb); > > - igt_assert(fb_id > 0); > > - > > - /* Get a framebuffer for the cairo composition result. */ > > - fb_id = igt_create_fb(data->drm_fd, mode->hdisplay, > > - mode->vdisplay, DRM_FORMAT_XRGB8888, > > - DRM_FORMAT_MOD_LINEAR, &result_fb); > > - igt_assert(fb_id > 0); > > - > > - result_surface = igt_get_cairo_surface(data->drm_fd, &result_fb); > > - > > - /* Paint the primary framebuffer on the result surface. */ > > - blit_plane_cairo(data, result_surface, 0, 0, 0, 0, 0, 0, 0, 0, > > - &primary_fb); > > - > > - /* Configure the primary plane. */ > > - igt_plane_set_fb(primary_plane, &primary_fb); > > - > > - overlay_planes_max = > > - igt_output_count_plane_type(output, DRM_PLANE_TYPE_OVERLAY); > > - > > - /* Limit the number of planes to a reasonable scene. */ > > - overlay_planes_max = min(overlay_planes_max, 4u); > > - > > - overlay_planes_count = (rand() % overlay_planes_max) + 1; > > - igt_debug("Using %d overlay planes\n", overlay_planes_count); > > - > > - overlay_fbs = calloc(sizeof(struct igt_fb), overlay_planes_count); > > - > > - for (i = 0; i < overlay_planes_count; i++) { > > - struct igt_fb *overlay_fb = &overlay_fbs[i]; > > - igt_plane_t *plane = > > - igt_output_get_plane_type_index(output, > > - DRM_PLANE_TYPE_OVERLAY, > > - i); > > - igt_assert(plane); > > - > > - prepare_randomized_plane(data, mode, plane, overlay_fb, i, > > - result_surface, allow_scaling, > > - allow_yuv); > > - } > > - > > - cairo_surface_destroy(result_surface); > > - > > - if (check == CHAMELIUM_CHECK_CRC) > > - fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd, > > - &result_fb); > > - > > - igt_display_commit2(&data->display, COMMIT_ATOMIC); > > - > > - if (check == CHAMELIUM_CHECK_CRC) { > > - chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1); > > - crc = chamelium_read_captured_crcs(data->chamelium, > > - &captured_frame_count); > > - > > - igt_assert(captured_frame_count == 1); > > - > > - expected_crc = chamelium_calculate_fb_crc_async_finish(fb_crc); > > - > > - chamelium_assert_crc_eq_or_dump(data->chamelium, > > - expected_crc, crc, > > - &result_fb, 0); > > - > > - free(expected_crc); > > - free(crc); > > - } else if (check == CHAMELIUM_CHECK_CHECKERBOARD) { > > - struct chamelium_frame_dump *dump; > > - > > - dump = chamelium_port_dump_pixels(data->chamelium, port, 0, 0, > > - 0, 0); > > - chamelium_assert_frame_match_or_dump(data->chamelium, port, > > - dump, &result_fb, check); > > - chamelium_destroy_frame_dump(dump); > > - } > > - > > - for (i = 0; i < overlay_planes_count; i++) > > - igt_remove_fb(data->drm_fd, &overlay_fbs[i]); > > - > > - free(overlay_fbs); > > - > > - igt_remove_fb(data->drm_fd, &primary_fb); > > - igt_remove_fb(data->drm_fd, &result_fb); > > -} > > - > > -static const char test_hpd_without_ddc_desc[] = > > - "Disable DDC on a VGA connector, check we still get a uevent on hotplug"; > > -static void > > -test_hpd_without_ddc(data_t *data, struct chamelium_port *port) > > -{ > > - struct udev_monitor *mon = igt_watch_uevents(); > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - igt_flush_uevents(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, CHAMELIUM_HOTPLUG_TIMEOUT)); > > - igt_assert_eq(chamelium_reprobe_connector(&data->display, > > - data->chamelium, port), > > - DRM_MODE_CONNECTED); > > - > > - igt_cleanup_uevents(mon); > > -} > > - > > -static const char test_hpd_storm_detect_desc[] = > > - "Trigger a series of hotplugs in a very small timeframe to simulate a" > > - "bad cable, check the kernel falls back to polling to avoid a hotplug " > > - "storm"; > > -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); > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - 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_uevents(); > > - 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_uevents(mon); > > - igt_hpd_storm_reset(data->drm_fd); > > -} > > - > > -static const char test_hpd_storm_disable_desc[] = > > - "Disable HPD storm detection, trigger a storm and check the kernel " > > - "doesn't detect one"; > > -static void > > -test_hpd_storm_disable(data_t *data, struct chamelium_port *port, int width) > > -{ > > - igt_require_hpd_storm_ctl(data->drm_fd); > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_reset_state(&data->display, data->chamelium, > > - port, data->ports, data->port_count); > > - > > - 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); > > -} > > - > > -static const char igt_edid_stress_resolution_desc[] = > > - "Stress test the DUT by testing multiple EDIDs, one right after the other," > > - "and ensure their validity by check the real screen resolution vs the" > > - "advertised mode resultion."; > > -static void edid_stress_resolution(data_t *data, struct chamelium_port *port, > > - monitor_edid edids_list[], > > - size_t edids_list_len) > > -{ > > - int i; > > - struct chamelium *chamelium = data->chamelium; > > - struct udev_monitor *mon = igt_watch_uevents(); > > - > > - for (i = 0; i < edids_list_len; ++i) { > > - struct chamelium_edid *chamelium_edid; > > - drmModeModeInfo mode; > > - struct igt_fb fb = { 0 }; > > - igt_output_t *output; > > - enum pipe pipe; > > - bool is_video_stable; > > - int screen_res_w, screen_res_h; > > - > > - monitor_edid *edid = &edids_list[i]; > > - igt_info("Testing out the EDID for %s\n", > > - monitor_edid_get_name(edid)); > > - > > - /* Getting and Setting the EDID on Chamelium. */ > > - chamelium_edid = > > - get_chameleon_edid_from_monitor_edid(chamelium, edid); > > - chamelium_port_set_edid(data->chamelium, port, chamelium_edid); > > - free_chamelium_edid_from_monitor_edid(chamelium_edid); > > - > > - igt_flush_uevents(mon); > > - chamelium_plug(chamelium, port); > > - wait_for_connector_after_hotplug(data, mon, port, > > - DRM_MODE_CONNECTED); > > - igt_flush_uevents(mon); > > - > > - /* Setting an output on the screen to turn it on. */ > > - mode = get_mode_for_port(chamelium, port); > > - create_fb_for_mode(data, &fb, &mode); > > - output = get_output_for_port(data, port); > > - pipe = get_pipe_for_output(&data->display, output); > > - igt_output_set_pipe(output, pipe); > > - enable_output(data, port, output, &mode, &fb); > > - > > - /* Capture the screen resolution and verify. */ > > - is_video_stable = chamelium_port_wait_video_input_stable( > > - chamelium, port, 5); > > - igt_assert(is_video_stable); > > - > > - chamelium_port_get_resolution(chamelium, port, &screen_res_w, > > - &screen_res_h); > > - igt_assert(screen_res_w == fb.width); > > - igt_assert(screen_res_h == fb.height); > > - > > - // Clean up > > - igt_remove_fb(data->drm_fd, &fb); > > - igt_modeset_disable_all_outputs(&data->display); > > - chamelium_unplug(chamelium, port); > > - } > > - > > - chamelium_reset_state(&data->display, data->chamelium, port, > > - data->ports, data->port_count); > > -} > > - > > -static const char igt_edid_resolution_list_desc[] = > > - "Get an EDID with many modes of different configurations, set them on the screen and check the" > > - " screen resolution matches the mode resolution."; > > - > > -static void edid_resolution_list(data_t *data, struct chamelium_port *port) > > -{ > > - struct chamelium *chamelium = data->chamelium; > > - struct udev_monitor *mon = igt_watch_uevents(); > > - drmModeConnector *connector; > > - drmModeModeInfoPtr modes; > > - int count_modes; > > - int i; > > - igt_output_t *output; > > - enum pipe pipe; > > - > > - chamelium_unplug(chamelium, port); > > - set_edid(data, port, IGT_CUSTOM_EDID_FULL); > > - > > - igt_flush_uevents(mon); > > - chamelium_plug(chamelium, port); > > - wait_for_connector_after_hotplug(data, mon, port, DRM_MODE_CONNECTED); > > - igt_flush_uevents(mon); > > - > > - connector = chamelium_port_get_connector(chamelium, port, true); > > - modes = connector->modes; > > - count_modes = connector->count_modes; > > - > > - output = get_output_for_port(data, port); > > - pipe = get_pipe_for_output(&data->display, output); > > - igt_output_set_pipe(output, pipe); > > - > > - for (i = 0; i < count_modes; ++i) > > - igt_debug("#%d %s %uHz\n", i, modes[i].name, modes[i].vrefresh); > > - > > - for (i = 0; i < count_modes; ++i) { > > - struct igt_fb fb = { 0 }; > > - bool is_video_stable; > > - int screen_res_w, screen_res_h; > > - > > - igt_info("Testing #%d %s %uHz\n", i, modes[i].name, > > - modes[i].vrefresh); > > - > > - /* Set the screen mode with the one we chose. */ > > - create_fb_for_mode(data, &fb, &modes[i]); > > - enable_output(data, port, output, &modes[i], &fb); > > - is_video_stable = chamelium_port_wait_video_input_stable( > > - chamelium, port, 10); > > - igt_assert(is_video_stable); > > - > > - chamelium_port_get_resolution(chamelium, port, &screen_res_w, > > - &screen_res_h); > > - igt_assert_eq(screen_res_w, modes[i].hdisplay); > > - igt_assert_eq(screen_res_h, modes[i].vdisplay); > > - > > - igt_remove_fb(data->drm_fd, &fb); > > - } > > - > > - igt_modeset_disable_all_outputs(&data->display); > > - drmModeFreeConnector(connector); > > -} > > - > > -#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__) > > - > > -#define connector_dynamic_subtest(name__, type__) \ > > - igt_subtest_with_dynamic(name__) \ > > - for_each_port(p, port) \ > > - if (chamelium_port_get_type(port) == \ > > - DRM_MODE_CONNECTOR_ ## type__) > > - > > - > > -static data_t data; > > - > > -IGT_TEST_DESCRIPTION("Tests requiring a Chamelium board"); > > -igt_main > > -{ > > - struct chamelium_port *port; > > - int p; > > - size_t i; > > - > > - igt_fixture { > > - /* So fbcon doesn't try to reprobe things itself */ > > - kmstest_set_vt_graphics_mode(); > > - > > - data.drm_fd = drm_open_driver_master(DRIVER_ANY); > > - igt_display_require(&data.display, data.drm_fd); > > - igt_require(data.display.is_atomic); > > - > > - /* > > - * XXX: disabling modeset, can be removed when > > - * igt_display_require will start doing this for us > > - */ > > - igt_display_commit2(&data.display, COMMIT_ATOMIC); > > - > > - /* we need to initalize chamelium after igt_display_require */ > > - data.chamelium = chamelium_init(data.drm_fd, &data.display); > > - igt_require(data.chamelium); > > - > > - data.ports = chamelium_get_ports(data.chamelium, > > - &data.port_count); > > - > > - for (i = 0; i < IGT_CUSTOM_EDID_COUNT; ++i) { > > - data.edids[i] = chamelium_new_edid(data.chamelium, > > - igt_kms_get_custom_edid(i)); > > - } > > - } > > - > > - igt_describe("DisplayPort tests"); > > - igt_subtest_group { > > - igt_fixture { > > - chamelium_require_connector_present(data.ports, DRM_MODE_CONNECTOR_DisplayPort, > > - data.port_count, 1); > > - } > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("dp-hpd", DisplayPort) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_DP_HDMI, > > - TEST_MODESET_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("dp-hpd-fast", DisplayPort) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("dp-hpd-enable-disable-mode", DisplayPort) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_ON_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("dp-hpd-with-enabled-mode", DisplayPort) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_ON); > > - > > - igt_describe(igt_custom_edid_type_read_desc); > > - connector_subtest("dp-edid-read", DisplayPort) { > > - igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_BASE); > > - igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_ALT); > > - } > > - > > - igt_describe(igt_edid_stress_resolution_desc); > > - connector_subtest("dp-edid-stress-resolution-4k", DisplayPort) > > - edid_stress_resolution(&data, port, DP_EDIDS_4K, > > - ARRAY_SIZE(DP_EDIDS_4K)); > > - > > - igt_describe(igt_edid_stress_resolution_desc); > > - connector_subtest("dp-edid-stress-resolution-non-4k", > > - DisplayPort) > > - edid_stress_resolution(&data, port, DP_EDIDS_NON_4K, > > - ARRAY_SIZE(DP_EDIDS_NON_4K)); > > - > > - igt_describe(igt_edid_resolution_list_desc); > > - connector_subtest("dp-edid-resolution-list", DisplayPort) > > - edid_resolution_list(&data, port); > > - > > - igt_describe(test_suspend_resume_hpd_desc); > > - connector_subtest("dp-hpd-after-suspend", DisplayPort) > > - test_suspend_resume_hpd(&data, port, > > - SUSPEND_STATE_MEM, > > - SUSPEND_TEST_NONE); > > - > > - igt_describe(test_suspend_resume_hpd_desc); > > - connector_subtest("dp-hpd-after-hibernate", DisplayPort) > > - test_suspend_resume_hpd(&data, port, > > - SUSPEND_STATE_DISK, > > - SUSPEND_TEST_DEVICES); > > - > > - igt_describe(test_hpd_storm_detect_desc); > > - connector_subtest("dp-hpd-storm", DisplayPort) > > - test_hpd_storm_detect(&data, port, > > - HPD_STORM_PULSE_INTERVAL_DP); > > - > > - igt_describe(test_hpd_storm_disable_desc); > > - connector_subtest("dp-hpd-storm-disable", DisplayPort) > > - test_hpd_storm_disable(&data, port, > > - HPD_STORM_PULSE_INTERVAL_DP); > > - > > - igt_describe(test_suspend_resume_edid_change_desc); > > - connector_subtest("dp-edid-change-during-suspend", DisplayPort) > > - test_suspend_resume_edid_change(&data, port, > > - SUSPEND_STATE_MEM, > > - SUSPEND_TEST_NONE, > > - IGT_CUSTOM_EDID_BASE, > > - IGT_CUSTOM_EDID_ALT); > > - > > - igt_describe(test_suspend_resume_edid_change_desc); > > - connector_subtest("dp-edid-change-during-hibernate", DisplayPort) > > - test_suspend_resume_edid_change(&data, port, > > - SUSPEND_STATE_DISK, > > - SUSPEND_TEST_DEVICES, > > - IGT_CUSTOM_EDID_BASE, > > - IGT_CUSTOM_EDID_ALT); > > - > > - igt_describe(test_display_all_modes_desc); > > - connector_subtest("dp-crc-single", DisplayPort) > > - test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_CRC, 1); > > - > > - igt_describe(test_display_one_mode_desc); > > - connector_subtest("dp-crc-fast", DisplayPort) > > - test_display_one_mode(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_CRC, 1); > > - > > - igt_describe(test_display_all_modes_desc); > > - connector_subtest("dp-crc-multiple", DisplayPort) > > - test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_CRC, 3); > > - > > - igt_describe(test_display_frame_dump_desc); > > - connector_subtest("dp-frame-dump", DisplayPort) > > - test_display_frame_dump(&data, port); > > - > > - igt_describe(test_mode_timings_desc); > > - connector_subtest("dp-mode-timings", DisplayPort) > > - test_mode_timings(&data, port); > > - > > - igt_describe(test_display_audio_desc); > > - connector_subtest("dp-audio", DisplayPort) > > - test_display_audio(&data, port, "HDMI", > > - IGT_CUSTOM_EDID_DP_AUDIO); > > - > > - igt_describe(test_display_audio_edid_desc); > > - connector_subtest("dp-audio-edid", DisplayPort) > > - test_display_audio_edid(&data, port, > > - IGT_CUSTOM_EDID_DP_AUDIO); > > - > > - igt_describe(test_hotplug_for_each_pipe_desc); > > - connector_subtest("dp-hpd-for-each-pipe", DisplayPort) > > - test_hotplug_for_each_pipe(&data, port); > > - } > > - > > - igt_describe("HDMI tests"); > > - igt_subtest_group { > > - igt_fixture { > > - chamelium_require_connector_present(data.ports, DRM_MODE_CONNECTOR_HDMIA, > > - data.port_count, 1); > > - } > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("hdmi-hpd", HDMIA) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_DP_HDMI, > > - TEST_MODESET_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("hdmi-hpd-fast", HDMIA) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("hdmi-hpd-enable-disable-mode", HDMIA) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_ON_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("hdmi-hpd-with-enabled-mode", HDMIA) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_ON); > > - > > - igt_describe(igt_custom_edid_type_read_desc); > > - connector_subtest("hdmi-edid-read", HDMIA) { > > - igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_BASE); > > - igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_ALT); > > - } > > - > > - igt_describe(igt_edid_stress_resolution_desc); > > - connector_subtest("hdmi-edid-stress-resolution-4k", HDMIA) > > - edid_stress_resolution(&data, port, HDMI_EDIDS_4K, > > - ARRAY_SIZE(HDMI_EDIDS_4K)); > > - > > - igt_describe(igt_edid_stress_resolution_desc); > > - connector_subtest("hdmi-edid-stress-resolution-non-4k", HDMIA) > > - edid_stress_resolution(&data, port, HDMI_EDIDS_NON_4K, > > - ARRAY_SIZE(HDMI_EDIDS_NON_4K)); > > - > > - igt_describe(test_suspend_resume_hpd_desc); > > - connector_subtest("hdmi-hpd-after-suspend", HDMIA) > > - test_suspend_resume_hpd(&data, port, > > - SUSPEND_STATE_MEM, > > - SUSPEND_TEST_NONE); > > - > > - igt_describe(test_suspend_resume_hpd_desc); > > - connector_subtest("hdmi-hpd-after-hibernate", HDMIA) > > - test_suspend_resume_hpd(&data, port, > > - SUSPEND_STATE_DISK, > > - SUSPEND_TEST_DEVICES); > > - > > - igt_describe(test_hpd_storm_detect_desc); > > - connector_subtest("hdmi-hpd-storm", HDMIA) > > - test_hpd_storm_detect(&data, port, > > - HPD_STORM_PULSE_INTERVAL_HDMI); > > - > > - igt_describe(test_hpd_storm_disable_desc); > > - connector_subtest("hdmi-hpd-storm-disable", HDMIA) > > - test_hpd_storm_disable(&data, port, > > - HPD_STORM_PULSE_INTERVAL_HDMI); > > - > > - igt_describe(test_suspend_resume_edid_change_desc); > > - connector_subtest("hdmi-edid-change-during-suspend", HDMIA) > > - test_suspend_resume_edid_change(&data, port, > > - SUSPEND_STATE_MEM, > > - SUSPEND_TEST_NONE, > > - IGT_CUSTOM_EDID_BASE, > > - IGT_CUSTOM_EDID_ALT); > > - > > - igt_describe(test_suspend_resume_edid_change_desc); > > - connector_subtest("hdmi-edid-change-during-hibernate", HDMIA) > > - test_suspend_resume_edid_change(&data, port, > > - SUSPEND_STATE_DISK, > > - SUSPEND_TEST_DEVICES, > > - IGT_CUSTOM_EDID_BASE, > > - IGT_CUSTOM_EDID_ALT); > > - > > - igt_describe(test_display_all_modes_desc); > > - connector_subtest("hdmi-crc-single", HDMIA) > > - test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_CRC, 1); > > - > > - igt_describe(test_display_one_mode_desc); > > - connector_subtest("hdmi-crc-fast", HDMIA) > > - test_display_one_mode(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_CRC, 1); > > - > > - igt_describe(test_display_all_modes_desc); > > - connector_subtest("hdmi-crc-multiple", HDMIA) > > - test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_CRC, 3); > > - > > - igt_describe(test_display_one_mode_desc); > > - connector_dynamic_subtest("hdmi-crc-nonplanar-formats", HDMIA) { > > - int k; > > - igt_output_t *output; > > - igt_plane_t *primary; > > - > > - output = prepare_output(&data, port, IGT_CUSTOM_EDID_BASE); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - for (k = 0; k < primary->format_mod_count; k++) { > > - if (!igt_fb_supported_format(primary->formats[k])) > > - continue; > > - > > - if (igt_format_is_yuv(primary->formats[k])) > > - continue; > > - > > - if (primary->modifiers[k] != DRM_FORMAT_MOD_LINEAR) > > - continue; > > - > > - igt_dynamic_f("%s", igt_format_str(primary->formats[k])) > > - test_display_one_mode(&data, port, primary->formats[k], > > - CHAMELIUM_CHECK_CRC, 1); > > - } > > - } > > - > > - igt_describe(test_display_planes_random_desc); > > - connector_subtest("hdmi-crc-planes-random", HDMIA) > > - test_display_planes_random(&data, port, > > - CHAMELIUM_CHECK_CRC); > > - > > - igt_describe(test_display_one_mode_desc); > > - connector_dynamic_subtest("hdmi-cmp-planar-formats", HDMIA) { > > - int k; > > - igt_output_t *output; > > - igt_plane_t *primary; > > - > > - output = prepare_output(&data, port, IGT_CUSTOM_EDID_BASE); > > - primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > - igt_assert(primary); > > - > > - for (k = 0; k < primary->format_mod_count; k++) { > > - if (!igt_fb_supported_format(primary->formats[k])) > > - continue; > > - > > - if (!igt_format_is_yuv(primary->formats[k])) > > - continue; > > - > > - if (primary->modifiers[k] != DRM_FORMAT_MOD_LINEAR) > > - continue; > > - > > - igt_dynamic_f("%s", igt_format_str(primary->formats[k])) > > - test_display_one_mode(&data, port, primary->formats[k], > > - CHAMELIUM_CHECK_CHECKERBOARD, 1); > > - } > > - } > > - > > - igt_describe(test_display_planes_random_desc); > > - connector_subtest("hdmi-cmp-planes-random", HDMIA) > > - test_display_planes_random(&data, port, > > - CHAMELIUM_CHECK_CHECKERBOARD); > > - > > - igt_describe(test_display_frame_dump_desc); > > - connector_subtest("hdmi-frame-dump", HDMIA) > > - test_display_frame_dump(&data, port); > > - > > - igt_describe(test_mode_timings_desc); > > - connector_subtest("hdmi-mode-timings", HDMIA) > > - test_mode_timings(&data, port); > > - > > - igt_describe(test_display_audio_desc); > > - connector_subtest("hdmi-audio", HDMIA) > > - test_display_audio(&data, port, "HDMI", > > - IGT_CUSTOM_EDID_HDMI_AUDIO); > > - > > - igt_describe(test_display_audio_edid_desc); > > - connector_subtest("hdmi-audio-edid", HDMIA) > > - test_display_audio_edid(&data, port, > > - IGT_CUSTOM_EDID_HDMI_AUDIO); > > - > > - igt_describe(test_display_aspect_ratio_desc); > > - connector_subtest("hdmi-aspect-ratio", HDMIA) > > - test_display_aspect_ratio(&data, port); > > - > > - igt_describe(test_hotplug_for_each_pipe_desc); > > - connector_subtest("hdmi-hpd-for-each-pipe", HDMIA) > > - test_hotplug_for_each_pipe(&data, port); > > - } > > - > > - igt_describe("VGA tests"); > > - igt_subtest_group { > > - igt_fixture { > > - chamelium_require_connector_present(data.ports, DRM_MODE_CONNECTOR_VGA, > > - data.port_count, 1); > > - } > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("vga-hpd", VGA) > > - test_hotplug(&data, port, HPD_TOGGLE_COUNT_VGA, > > - TEST_MODESET_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("vga-hpd-fast", VGA) > > - test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("vga-hpd-enable-disable-mode", VGA) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_ON_OFF); > > - > > - igt_describe(test_basic_hotplug_desc); > > - connector_subtest("vga-hpd-with-enabled-mode", VGA) > > - test_hotplug(&data, port, > > - HPD_TOGGLE_COUNT_FAST, > > - TEST_MODESET_ON); > > - > > - igt_describe(igt_custom_edid_type_read_desc); > > - connector_subtest("vga-edid-read", VGA) { > > - igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_BASE); > > - igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_ALT); > > - } > > - > > - igt_describe(test_suspend_resume_hpd_desc); > > - connector_subtest("vga-hpd-after-suspend", VGA) > > - test_suspend_resume_hpd(&data, port, > > - SUSPEND_STATE_MEM, > > - SUSPEND_TEST_NONE); > > - > > - igt_describe(test_suspend_resume_hpd_desc); > > - connector_subtest("vga-hpd-after-hibernate", VGA) > > - test_suspend_resume_hpd(&data, port, > > - SUSPEND_STATE_DISK, > > - SUSPEND_TEST_DEVICES); > > - > > - igt_describe(test_hpd_without_ddc_desc); > > - connector_subtest("vga-hpd-without-ddc", VGA) > > - test_hpd_without_ddc(&data, port); > > - > > - igt_describe(test_display_all_modes_desc); > > - connector_subtest("vga-frame-dump", VGA) > > - test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > - CHAMELIUM_CHECK_ANALOG, 1); > > - } > > - > > - igt_describe("Tests that operate on all connectors"); > > - igt_subtest_group { > > - > > - igt_fixture { > > - igt_require(data.port_count); > > - } > > - > > - igt_describe(test_suspend_resume_hpd_common_desc); > > - igt_subtest("common-hpd-after-suspend") > > - test_suspend_resume_hpd_common(&data, > > - SUSPEND_STATE_MEM, > > - SUSPEND_TEST_NONE); > > - > > - igt_describe(test_suspend_resume_hpd_common_desc); > > - igt_subtest("common-hpd-after-hibernate") > > - test_suspend_resume_hpd_common(&data, > > - SUSPEND_STATE_DISK, > > - SUSPEND_TEST_DEVICES); > > - } > > - > > - igt_describe(test_hotplug_for_each_pipe_desc); > > - connector_subtest("vga-hpd-for-each-pipe", VGA) > > - test_hotplug_for_each_pipe(&data, port); > > - > > - igt_fixture { > > - igt_display_fini(&data.display); > > - close(data.drm_fd); > > - } > > -} > > diff --git a/tests/chamelium/kms_chamelium_audio.c b/tests/chamelium/kms_chamelium_audio.c > > new file mode 100644 > > index 00000000..4d13744c > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_audio.c > > @@ -0,0 +1,858 @@ > > +// SPDX-License-Identifier: MIT > > +/* > > + * A Chamelium test for testing the Audio functionality. > > + * > > + * Copyright 2022 Google LLC. > > + * > > + * Authors: Mark Yacoub > > + */ > > + > > +#include "igt_eld.h" > > +#include "igt_infoframe.h" > > +#include "kms_chamelium_helper.h" > > + > > +/* Playback parameters control the audio signal we synthesize and send */ > > +#define PLAYBACK_CHANNELS 2 > > +#define PLAYBACK_SAMPLES 1024 > > + > > +/* Capture paremeters control the audio signal we receive */ > > +#define CAPTURE_SAMPLES 2048 > > + > > +#define AUDIO_TIMEOUT 2000 /* ms */ > > +/* A streak of 3 gives confidence that the signal is good. */ > > +#define MIN_STREAK 3 > > + > > +#define FLATLINE_AMPLITUDE 0.1 /* normalized, ie. in [0, 1] */ > > +#define FLATLINE_AMPLITUDE_ACCURACY 0.001 /* ± 0.1 % of the full amplitude */ > > +#define FLATLINE_ALIGN_ACCURACY 0 /* number of samples */ > > + > > +struct audio_state { > > + struct alsa *alsa; > > + struct chamelium *chamelium; > > + struct chamelium_port *port; > > + struct chamelium_stream *stream; > > + > > + /* The capture format is only available after capture has started. */ > > + struct { > > + snd_pcm_format_t format; > > + int channels; > > + int rate; > > + } playback, capture; > > + > > + char *name; > > + struct audio_signal *signal; /* for frequencies test only */ > > + int channel_mapping[CHAMELIUM_MAX_AUDIO_CHANNELS]; > > + > > + size_t recv_pages; > > + int msec; > > + > > + int dump_fd; > > + char *dump_path; > > + > > + pthread_t thread; > > + atomic_bool run; > > + atomic_bool positive; /* for pulse test only */ > > +}; > > + > > +/* TODO: enable >48KHz rates, these are not reliable */ > > +static int test_sampling_rates[] = { > > + 32000, 44100, 48000, > > + /* 88200, */ > > + /* 96000, */ > > + /* 176400, */ > > + /* 192000, */ > > +}; > > + > > +static int test_sampling_rates_count = > > + sizeof(test_sampling_rates) / sizeof(int); > > + > > +/* Test frequencies (Hz): a sine signal will be generated for each. > > + * > > + * Depending on the sampling rate chosen, it might not be possible to properly > > + * detect the generated sine (see Nyquist–Shannon sampling theorem). > > + * Frequencies that can't be reliably detected will be automatically pruned in > > + * #audio_signal_add_frequency. For instance, the 80KHz frequency can only be > > + * tested with a 192KHz sampling rate. > > + */ > > +static int test_frequencies[] = { > > + 300, 600, 1200, 10000, 80000, > > +}; > > + > > +static int test_frequencies_count = sizeof(test_frequencies) / sizeof(int); > > + > > +static const snd_pcm_format_t test_formats[] = { > > + SND_PCM_FORMAT_S16_LE, > > + SND_PCM_FORMAT_S24_LE, > > + SND_PCM_FORMAT_S32_LE, > > +}; > > + > > +static const size_t test_formats_count = > > + sizeof(test_formats) / sizeof(test_formats[0]); > > + > > +static void audio_state_init(struct audio_state *state, chamelium_data_t *data, > > + struct alsa *alsa, struct chamelium_port *port, > > + snd_pcm_format_t format, int channels, int rate) > > +{ > > + memset(state, 0, sizeof(*state)); > > + state->dump_fd = -1; > > + > > + state->alsa = alsa; > > + state->chamelium = data->chamelium; > > + state->port = port; > > + > > + state->playback.format = format; > > + state->playback.channels = channels; > > + state->playback.rate = rate; > > + > > + alsa_configure_output(alsa, format, channels, rate); > > + > > + state->stream = chamelium_stream_init(); > > + igt_assert_f(state->stream, > > + "Failed to initialize Chamelium stream client\n"); > > +} > > + > > +static void audio_state_fini(struct audio_state *state) > > +{ > > + chamelium_stream_deinit(state->stream); > > + free(state->name); > > +} > > + > > +static void *run_audio_thread(void *data) > > +{ > > + struct alsa *alsa = data; > > + > > + alsa_run(alsa, -1); > > + return NULL; > > +} > > + > > +static void audio_state_start(struct audio_state *state, const char *name) > > +{ > > + int ret; > > + bool ok; > > + size_t i, j; > > + enum chamelium_stream_realtime_mode stream_mode; > > + char dump_suffix[64]; > > + > > + free(state->name); > > + state->name = strdup(name); > > + state->recv_pages = 0; > > + state->msec = 0; > > + > > + igt_debug("Starting %s test with playback format %s, " > > + "sampling rate %d Hz and %d channels\n", > > + name, snd_pcm_format_name(state->playback.format), > > + state->playback.rate, state->playback.channels); > > + > > + chamelium_start_capturing_audio(state->chamelium, state->port, false); > > + > > + stream_mode = CHAMELIUM_STREAM_REALTIME_STOP_WHEN_OVERFLOW; > > + ok = chamelium_stream_dump_realtime_audio(state->stream, stream_mode); > > + igt_assert_f(ok, "Failed to start streaming audio capture\n"); > > + > > + /* Start playing audio */ > > + state->run = true; > > + ret = pthread_create(&state->thread, NULL, run_audio_thread, > > + state->alsa); > > + igt_assert_f(ret == 0, "Failed to start audio playback thread\n"); > > + > > + /* The Chamelium device only supports this PCM format. */ > > + state->capture.format = SND_PCM_FORMAT_S32_LE; > > + > > + /* Only after we've started playing audio, we can retrieve the capture > > + * format used by the Chamelium device. */ > > + chamelium_get_audio_format(state->chamelium, state->port, > > + &state->capture.rate, > > + &state->capture.channels); > > + if (state->capture.rate == 0) { > > + igt_debug("Audio receiver doesn't indicate the capture " > > + "sampling rate, assuming it's %d Hz\n", > > + state->playback.rate); > > + state->capture.rate = state->playback.rate; > > + } > > + > > + chamelium_get_audio_channel_mapping(state->chamelium, state->port, > > + state->channel_mapping); > > + /* Make sure we can capture all channels we send. */ > > + for (i = 0; i < state->playback.channels; i++) { > > + ok = false; > > + for (j = 0; j < state->capture.channels; j++) { > > + if (state->channel_mapping[j] == i) { > > + ok = true; > > + break; > > + } > > + } > > + igt_assert_f(ok, "Cannot capture all channels\n"); > > + } > > + > > + if (igt_frame_dump_is_enabled()) { > > + snprintf(dump_suffix, sizeof(dump_suffix), > > + "capture-%s-%s-%dch-%dHz", name, > > + snd_pcm_format_name(state->playback.format), > > + state->playback.channels, state->playback.rate); > > + > > + state->dump_fd = audio_create_wav_file_s32_le( > > + dump_suffix, state->capture.rate, > > + state->capture.channels, &state->dump_path); > > + igt_assert_f(state->dump_fd >= 0, > > + "Failed to create audio dump file\n"); > > + } > > +} > > + > > +static void audio_state_receive(struct audio_state *state, int32_t **recv, > > + size_t *recv_len) > > +{ > > + bool ok; > > + size_t page_count; > > + size_t recv_size; > > + > > + ok = chamelium_stream_receive_realtime_audio(state->stream, &page_count, > > + recv, recv_len); > > + igt_assert_f(ok, "Failed to receive audio from stream server\n"); > > + > > + state->msec = state->recv_pages * *recv_len / > > + (double)state->capture.channels / > > + (double)state->capture.rate * 1000; > > + state->recv_pages++; > > + > > + if (state->dump_fd >= 0) { > > + recv_size = *recv_len * sizeof(int32_t); > > + igt_assert_f(write(state->dump_fd, *recv, recv_size) == > > + recv_size, > > + "Failed to write to audio dump file\n"); > > + } > > +} > > + > > +static void audio_state_stop(struct audio_state *state, bool success) > > +{ > > + bool ok; > > + int ret; > > + struct chamelium_audio_file *audio_file; > > + enum igt_log_level log_level; > > + > > + igt_debug("Stopping audio playback\n"); > > + state->run = false; > > + ret = pthread_join(state->thread, NULL); > > + igt_assert_f(ret == 0, "Failed to join audio playback thread\n"); > > + > > + ok = chamelium_stream_stop_realtime_audio(state->stream); > > + igt_assert_f(ok, "Failed to stop streaming audio capture\n"); > > + > > + audio_file = > > + chamelium_stop_capturing_audio(state->chamelium, state->port); > > + if (audio_file) { > > + igt_debug("Audio file saved on the Chamelium in %s\n", > > + audio_file->path); > > + chamelium_destroy_audio_file(audio_file); > > + } > > + > > + if (state->dump_fd >= 0) { > > + close(state->dump_fd); > > + state->dump_fd = -1; > > + > > + if (success) { > > + /* Test succeeded, no need to keep the captured data */ > > + unlink(state->dump_path); > > + } else > > + igt_debug("Saved captured audio data to %s\n", > > + state->dump_path); > > + free(state->dump_path); > > + state->dump_path = NULL; > > + } > > + > > + if (success) > > + log_level = IGT_LOG_DEBUG; > > + else > > + log_level = IGT_LOG_CRITICAL; > > + > > + igt_log(IGT_LOG_DOMAIN, log_level, > > + "Audio %s test result for format %s, " > > + "sampling rate %d Hz and %d channels: %s\n", > > + state->name, snd_pcm_format_name(state->playback.format), > > + state->playback.rate, state->playback.channels, > > + success ? "ALL GREEN" : "FAILED"); > > +} > > + > > +static void check_audio_infoframe(struct audio_state *state) > > +{ > > + struct chamelium_infoframe *infoframe; > > + struct infoframe_audio infoframe_audio; > > + struct infoframe_audio expected = { 0 }; > > + bool ok; > > + > > + if (!chamelium_supports_get_last_infoframe(state->chamelium)) { > > + igt_debug("Skipping audio InfoFrame check: " > > + "Chamelium board doesn't support GetLastInfoFrame\n"); > > + return; > > + } > > + > > + expected.coding_type = INFOFRAME_AUDIO_CT_PCM; > > + expected.channel_count = state->playback.channels; > > + expected.sampling_freq = state->playback.rate; > > + expected.sample_size = snd_pcm_format_width(state->playback.format); > > + > > + infoframe = chamelium_get_last_infoframe(state->chamelium, state->port, > > + CHAMELIUM_INFOFRAME_AUDIO); > > + if (infoframe == NULL && state->playback.channels <= 2) { > > + /* Audio InfoFrames are optional for mono and stereo audio */ > > + igt_debug("Skipping audio InfoFrame check: " > > + "no InfoFrame received\n"); > > + return; > > + } > > + igt_assert_f(infoframe != NULL, "no audio InfoFrame received\n"); > > + > > + ok = infoframe_audio_parse(&infoframe_audio, infoframe->version, > > + infoframe->payload, infoframe->payload_size); > > + chamelium_infoframe_destroy(infoframe); > > + igt_assert_f(ok, "failed to parse audio InfoFrame\n"); > > + > > + igt_debug("Checking audio InfoFrame:\n"); > > + igt_debug("coding_type: got %d, expected %d\n", > > + infoframe_audio.coding_type, expected.coding_type); > > + igt_debug("channel_count: got %d, expected %d\n", > > + infoframe_audio.channel_count, expected.channel_count); > > + igt_debug("sampling_freq: got %d, expected %d\n", > > + infoframe_audio.sampling_freq, expected.sampling_freq); > > + igt_debug("sample_size: got %d, expected %d\n", > > + infoframe_audio.sample_size, expected.sample_size); > > + > > + if (infoframe_audio.coding_type != INFOFRAME_AUDIO_CT_UNSPECIFIED) > > + igt_assert(infoframe_audio.coding_type == expected.coding_type); > > + if (infoframe_audio.channel_count >= 0) > > + igt_assert(infoframe_audio.channel_count == > > + expected.channel_count); > > + if (infoframe_audio.sampling_freq >= 0) > > + igt_assert(infoframe_audio.sampling_freq == > > + expected.sampling_freq); > > + if (infoframe_audio.sample_size >= 0) > > + igt_assert(infoframe_audio.sample_size == expected.sample_size); > > +} > > + > > +static int audio_output_frequencies_callback(void *data, void *buffer, > > + int samples) > > +{ > > + struct audio_state *state = data; > > + double *tmp; > > + size_t len; > > + > > + len = samples * state->playback.channels; > > + tmp = malloc(len * sizeof(double)); > > + audio_signal_fill(state->signal, tmp, samples); > > + audio_convert_to(buffer, tmp, len, state->playback.format); > > + free(tmp); > > + > > + return state->run ? 0 : -1; > > +} > > + > > +static bool test_audio_frequencies(struct audio_state *state) > > +{ > > + int freq, step; > > + int32_t *recv, *buf; > > + double *channel; > > + size_t i, j, streak; > > + size_t recv_len, buf_len, buf_cap, channel_len; > > + bool success; > > + int capture_chan; > > + > > + state->signal = audio_signal_init(state->playback.channels, > > + state->playback.rate); > > + igt_assert_f(state->signal, "Failed to initialize audio signal\n"); > > + > > + /* We'll choose different frequencies per channel to make sure they are > > + * independent from each other. To do so, we'll add a different offset > > + * to the base frequencies for each channel. We need to choose a big > > + * enough offset so that we're sure to detect mixed up channels. We > > + * choose an offset of two 2 bins in the final FFT to enforce a clear > > + * difference. > > + * > > + * Note that we assume capture_rate == playback_rate. We'll assert this > > + * later on. We cannot retrieve the capture rate before starting > > + * playing audio, so we don't really have the choice. > > + */ > > + step = 2 * state->playback.rate / CAPTURE_SAMPLES; > > + for (i = 0; i < test_frequencies_count; i++) { > > + for (j = 0; j < state->playback.channels; j++) { > > + freq = test_frequencies[i] + j * step; > > + audio_signal_add_frequency(state->signal, freq, j); > > + } > > + } > > + audio_signal_synthesize(state->signal); > > + > > + alsa_register_output_callback(state->alsa, > > + audio_output_frequencies_callback, state, > > + PLAYBACK_SAMPLES); > > + > > + audio_state_start(state, "frequencies"); > > + > > + igt_assert_f(state->capture.rate == state->playback.rate, > > + "Capture rate (%dHz) doesn't match playback rate (%dHz)\n", > > + state->capture.rate, state->playback.rate); > > + > > + /* Needs to be a multiple of 128, because that's the number of samples > > + * we get per channel each time we receive an audio page from the > > + * Chamelium device. > > + * > > + * Additionally, this value needs to be high enough to guarantee we > > + * capture a full period of each sine we generate. If we capture 2048 > > + * samples at a 192KHz sampling rate, we get a full period for a >94Hz > > + * sines. For lower sampling rates, the capture duration will be > > + * longer. > > + */ > > + channel_len = CAPTURE_SAMPLES; > > + channel = malloc(sizeof(double) * channel_len); > > + > > + buf_cap = state->capture.channels * channel_len; > > + buf = malloc(sizeof(int32_t) * buf_cap); > > + buf_len = 0; > > + > > + recv = NULL; > > + recv_len = 0; > > + > > + success = false; > > + streak = 0; > > + while (!success && state->msec < AUDIO_TIMEOUT) { > > + audio_state_receive(state, &recv, &recv_len); > > + > > + memcpy(&buf[buf_len], recv, recv_len * sizeof(int32_t)); > > + buf_len += recv_len; > > + > > + if (buf_len < buf_cap) > > + continue; > > + igt_assert(buf_len == buf_cap); > > + > > + igt_debug("Detecting audio signal, t=%d msec\n", state->msec); > > + > > + for (j = 0; j < state->playback.channels; j++) { > > + capture_chan = state->channel_mapping[j]; > > + igt_assert(capture_chan >= 0); > > + igt_debug("Processing channel %zu (captured as " > > + "channel %d)\n", > > + j, capture_chan); > > + > > + audio_extract_channel_s32_le(channel, channel_len, buf, > > + buf_len, > > + state->capture.channels, > > + capture_chan); > > + > > + if (audio_signal_detect(state->signal, > > + state->capture.rate, j, channel, > > + channel_len)) > > + streak++; > > + else > > + streak = 0; > > + } > > + > > + buf_len = 0; > > + > > + success = streak == MIN_STREAK * state->playback.channels; > > + } > > + > > + audio_state_stop(state, success); > > + > > + free(recv); > > + free(buf); > > + free(channel); > > + audio_signal_fini(state->signal); > > + > > + check_audio_infoframe(state); > > + > > + return success; > > +} > > + > > +static int audio_output_flatline_callback(void *data, void *buffer, int samples) > > +{ > > + struct audio_state *state = data; > > + double *tmp; > > + size_t len, i; > > + > > + len = samples * state->playback.channels; > > + tmp = malloc(len * sizeof(double)); > > + for (i = 0; i < len; i++) > > + tmp[i] = (state->positive ? 1 : -1) * FLATLINE_AMPLITUDE; > > + audio_convert_to(buffer, tmp, len, state->playback.format); > > + free(tmp); > > + > > + return state->run ? 0 : -1; > > +} > > + > > +static bool detect_flatline_amplitude(double *buf, size_t buf_len, bool pos) > > +{ > > + double expected, min, max; > > + size_t i; > > + bool ok; > > + > > + min = max = NAN; > > + for (i = 0; i < buf_len; i++) { > > + if (isnan(min) || buf[i] < min) > > + min = buf[i]; > > + if (isnan(max) || buf[i] > max) > > + max = buf[i]; > > + } > > + > > + expected = (pos ? 1 : -1) * FLATLINE_AMPLITUDE; > > + ok = (min >= expected - FLATLINE_AMPLITUDE_ACCURACY && > > + max <= expected + FLATLINE_AMPLITUDE_ACCURACY); > > + if (ok) > > + igt_debug("Flatline wave amplitude detected\n"); > > + else > > + igt_debug("Flatline amplitude not detected (min=%f, max=%f)\n", > > + min, max); > > + return ok; > > +} > > + > > +static ssize_t detect_falling_edge(double *buf, size_t buf_len) > > +{ > > + size_t i; > > + > > + for (i = 0; i < buf_len; i++) { > > + if (buf[i] < 0) > > + return i; > > + } > > + > > + return -1; > > +} > > + > > +/** test_audio_flatline: > > + * > > + * Send a constant value (one positive, then a negative one) and check that: > > + * > > + * - The amplitude of the flatline is correct > > + * - All channels switch from a positive signal to a negative one at the same > > + * time (ie. all channels are aligned) > > + */ > > +static bool test_audio_flatline(struct audio_state *state) > > +{ > > + bool success, amp_success, align_success; > > + int32_t *recv; > > + size_t recv_len, i, channel_len; > > + ssize_t j; > > + int streak, capture_chan; > > + double *channel; > > + int falling_edges[CHAMELIUM_MAX_AUDIO_CHANNELS]; > > + > > + alsa_register_output_callback(state->alsa, > > + audio_output_flatline_callback, state, > > + PLAYBACK_SAMPLES); > > + > > + /* Start by sending a positive signal */ > > + state->positive = true; > > + > > + audio_state_start(state, "flatline"); > > + > > + for (i = 0; i < state->playback.channels; i++) > > + falling_edges[i] = -1; > > + > > + recv = NULL; > > + recv_len = 0; > > + amp_success = false; > > + streak = 0; > > + while (!amp_success && state->msec < AUDIO_TIMEOUT) { > > + audio_state_receive(state, &recv, &recv_len); > > + > > + igt_debug("Detecting audio signal, t=%d msec\n", state->msec); > > + > > + for (i = 0; i < state->playback.channels; i++) { > > + capture_chan = state->channel_mapping[i]; > > + igt_assert(capture_chan >= 0); > > + igt_debug("Processing channel %zu (captured as " > > + "channel %d)\n", > > + i, capture_chan); > > + > > + channel_len = audio_extract_channel_s32_le( > > + NULL, 0, recv, recv_len, > > + state->capture.channels, capture_chan); > > + channel = malloc(channel_len * sizeof(double)); > > + audio_extract_channel_s32_le(channel, channel_len, recv, > > + recv_len, > > + state->capture.channels, > > + capture_chan); > > + > > + /* Check whether the amplitude is fine */ > > + if (detect_flatline_amplitude(channel, channel_len, > > + state->positive)) > > + streak++; > > + else > > + streak = 0; > > + > > + /* If we're now sending a negative signal, detect the > > + * falling edge */ > > + j = detect_falling_edge(channel, channel_len); > > + if (!state->positive && j >= 0) { > > + falling_edges[i] = > > + recv_len * state->recv_pages + j; > > + } > > + > > + free(channel); > > + } > > + > > + amp_success = streak == MIN_STREAK * state->playback.channels; > > + > > + if (amp_success && state->positive) { > > + /* Switch to a negative signal after we've detected the > > + * positive one. */ > > + state->positive = false; > > + amp_success = false; > > + streak = 0; > > + igt_debug("Switching to negative square wave\n"); > > + } > > + } > > + > > + /* Check alignment between all channels by comparing the index of the > > + * falling edge. */ > > + align_success = true; > > + for (i = 0; i < state->playback.channels; i++) { > > + if (falling_edges[i] < 0) { > > + igt_critical( > > + "Falling edge not detected for channel %zu\n", > > + i); > > + align_success = false; > > + continue; > > + } > > + > > + if (abs(falling_edges[0] - falling_edges[i]) > > > + FLATLINE_ALIGN_ACCURACY) { > > + igt_critical("Channel alignment mismatch: " > > + "channel 0 has a falling edge at index %d " > > + "while channel %zu has index %d\n", > > + falling_edges[0], i, falling_edges[i]); > > + align_success = false; > > + } > > + } > > + > > + success = amp_success && align_success; > > + audio_state_stop(state, success); > > + > > + free(recv); > > + > > + return success; > > +} > > + > > +static bool check_audio_configuration(struct alsa *alsa, > > + snd_pcm_format_t format, int channels, > > + int sampling_rate) > > +{ > > + if (!alsa_test_output_configuration(alsa, format, channels, > > + sampling_rate)) { > > + igt_debug("Skipping test with format %s, sampling rate %d Hz " > > + "and %d channels because at least one of the " > > + "selected output devices doesn't support this " > > + "configuration\n", > > + snd_pcm_format_name(format), sampling_rate, channels); > > + return false; > > + } > > + /* TODO: the Chamelium device sends a malformed signal for some audio > > + * configurations. See crbug.com/950917 */ > > + if ((format != SND_PCM_FORMAT_S16_LE && sampling_rate >= 44100) || > > + channels > 2) { > > + igt_debug("Skipping test with format %s, sampling rate %d Hz " > > + "and %d channels because the Chamelium device " > > + "doesn't support this configuration\n", > > + snd_pcm_format_name(format), sampling_rate, channels); > > + return false; > > + } > > + return true; > > +} > > + > > +static const char test_display_audio_desc[] = > > + "Playback various audio signals with various audio formats/rates, " > > + "capture them and check they are correct"; > > +static void test_display_audio(chamelium_data_t *data, > > + struct chamelium_port *port, > > + const char *audio_device, > > + enum igt_custom_edid_type edid) > > +{ > > + bool run, success; > > + struct alsa *alsa; > > + int ret; > > + igt_output_t *output; > > + igt_plane_t *primary; > > + struct igt_fb fb; > > + drmModeModeInfo *mode; > > + drmModeConnector *connector; > > + int fb_id, i, j; > > + int channels, sampling_rate; > > + snd_pcm_format_t format; > > + struct audio_state state; > > + > > + igt_require(alsa_has_exclusive_access()); > > + > > + /* Old Chamelium devices need an update for DisplayPort audio and > > + * chamelium_get_audio_format support. */ > > + igt_require(chamelium_has_audio_support(data->chamelium, port)); > > + > > + alsa = alsa_init(); > > + igt_assert(alsa); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + output = chamelium_prepare_output(data, port, edid); > > + connector = chamelium_port_get_connector(data->chamelium, port, false); > > + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + /* Enable the output because the receiver won't try to receive audio if > > + * it doesn't receive video. */ > > + igt_assert(connector->count_modes > 0); > > + mode = &connector->modes[0]; > > + > > + fb_id = igt_create_color_pattern_fb(data->drm_fd, mode->hdisplay, > > + mode->vdisplay, DRM_FORMAT_XRGB8888, > > + DRM_FORMAT_MOD_LINEAR, 0, 0, 0, > > + &fb); > > + igt_assert(fb_id > 0); > > + > > + chamelium_enable_output(data, port, output, mode, &fb); > > + > > + run = false; > > + success = true; > > + for (i = 0; i < test_sampling_rates_count; i++) { > > + for (j = 0; j < test_formats_count; j++) { > > + ret = alsa_open_output(alsa, audio_device); > > + igt_assert_f(ret >= 0, "Failed to open ALSA output\n"); > > + > > + /* TODO: playback on all 8 available channels (this > > + * isn't supported by Chamelium devices yet, see > > + * https://crbug.com/950917) */ > > + format = test_formats[j]; > > + channels = PLAYBACK_CHANNELS; > > + sampling_rate = test_sampling_rates[i]; > > + > > + if (!check_audio_configuration(alsa, format, channels, > > + sampling_rate)) > > + continue; > > + > > + run = true; > > + > > + audio_state_init(&state, data, alsa, port, format, > > + channels, sampling_rate); > > + success &= test_audio_frequencies(&state); > > + success &= test_audio_flatline(&state); > > + audio_state_fini(&state); > > + > > + alsa_close_output(alsa); > > + } > > + } > > + > > + /* Make sure we tested at least one frequency and format. */ > > + igt_assert(run); > > + /* Make sure all runs were successful. */ > > + igt_assert(success); > > + > > + igt_remove_fb(data->drm_fd, &fb); > > + > > + drmModeFreeConnector(connector); > > + > > + free(alsa); > > +} > > + > > +static const char test_display_audio_edid_desc[] = > > + "Plug a connector with an EDID suitable for audio, check ALSA's " > > + "EDID-Like Data reports the correct audio parameters"; > > +static void test_display_audio_edid(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_custom_edid_type edid) > > +{ > > + igt_output_t *output; > > + igt_plane_t *primary; > > + struct igt_fb fb; > > + drmModeModeInfo *mode; > > + drmModeConnector *connector; > > + int fb_id; > > + struct eld_entry eld; > > + struct eld_sad *sad; > > + > > + igt_require(eld_is_supported()); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + output = chamelium_prepare_output(data, port, edid); > > + connector = chamelium_port_get_connector(data->chamelium, port, false); > > + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + /* Enable the output because audio cannot be played on inactive > > + * connectors. */ > > + igt_assert(connector->count_modes > 0); > > + mode = &connector->modes[0]; > > + > > + fb_id = igt_create_color_pattern_fb(data->drm_fd, mode->hdisplay, > > + mode->vdisplay, DRM_FORMAT_XRGB8888, > > + DRM_FORMAT_MOD_LINEAR, 0, 0, 0, > > + &fb); > > + igt_assert(fb_id > 0); > > + > > + chamelium_enable_output(data, port, output, mode, &fb); > > + > > + igt_assert(eld_get_igt(&eld)); > > + igt_assert(eld.sads_len == 1); > > + > > + sad = &eld.sads[0]; > > + igt_assert(sad->coding_type == CEA_SAD_FORMAT_PCM); > > + igt_assert(sad->channels == 2); > > + igt_assert(sad->rates == > > + (CEA_SAD_SAMPLING_RATE_32KHZ | CEA_SAD_SAMPLING_RATE_44KHZ | > > + CEA_SAD_SAMPLING_RATE_48KHZ)); > > + igt_assert(sad->bits == > > + (CEA_SAD_SAMPLE_SIZE_16 | CEA_SAD_SAMPLE_SIZE_20 | > > + CEA_SAD_SAMPLE_SIZE_24)); > > + > > + igt_remove_fb(data->drm_fd, &fb); > > + > > + drmModeFreeConnector(connector); > > +} > > + > > +IGT_TEST_DESCRIPTION("Testing Audio with a Chamelium board"); > > +igt_main > > +{ > > + chamelium_data_t data; > > + struct chamelium_port *port; > > + int p; > > + > > + igt_fixture { > > + chamelium_init_test(&data); > > + } > > + > > + igt_describe("DisplayPort tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_DisplayPort, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_display_audio_desc); > > + connector_subtest("dp-audio", DisplayPort) test_display_audio( > > + &data, port, "HDMI", IGT_CUSTOM_EDID_DP_AUDIO); > > + > > + igt_describe(test_display_audio_edid_desc); > > + connector_subtest("dp-audio-edid", DisplayPort) > > + test_display_audio_edid(&data, port, > > + IGT_CUSTOM_EDID_DP_AUDIO); > > + } > > + > > + igt_describe("HDMI tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_HDMIA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_display_audio_desc); > > + connector_subtest("hdmi-audio", HDMIA) test_display_audio( > > + &data, port, "HDMI", IGT_CUSTOM_EDID_HDMI_AUDIO); > > + > > + igt_describe(test_display_audio_edid_desc); > > + connector_subtest("hdmi-audio-edid", HDMIA) > > + test_display_audio_edid(&data, port, > > + IGT_CUSTOM_EDID_HDMI_AUDIO); > > + } > > + > > + igt_fixture { > > + igt_display_fini(&data.display); > > + close(data.drm_fd); > > + } > > +} > > diff --git a/tests/chamelium/kms_color_chamelium.c b/tests/chamelium/kms_chamelium_color.c > > similarity index 100% > > rename from tests/chamelium/kms_color_chamelium.c > > rename to tests/chamelium/kms_chamelium_color.c > > diff --git a/tests/chamelium/kms_chamelium_edid.c b/tests/chamelium/kms_chamelium_edid.c > > new file mode 100644 > > index 00000000..c9e15f41 > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_edid.c > > @@ -0,0 +1,543 @@ > > +// SPDX-License-Identifier: MIT > > +/* > > + * A Chamelium test for testing the EDID functionality. > > + * > > + * Copyright 2022 Google LLC. > > + * > > + * Authors: Mark Yacoub > > + */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "config.h" > > +#include "igt.h" > > +#include "igt_chamelium.h" > > +#include "igt_edid.h" > > +#include "igt_eld.h" > > +#include "igt_vc4.h" > > +#include "igt_infoframe.h" > > +#include "kms_chamelium_helper.h" > > +#include "monitor_edids/dp_edids.h" > > +#include "monitor_edids/hdmi_edids.h" > > +#include "monitor_edids/monitor_edids_helper.h" > > + > > +#define MODE_CLOCK_ACCURACY 0.05 /* 5% */ > > + > > +static void get_connectors_link_status_failed(chamelium_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 check_mode(struct chamelium *chamelium, struct chamelium_port *port, > > + drmModeModeInfo *mode) > > +{ > > + struct chamelium_video_params video_params = { 0 }; > > + double mode_clock; > > + int mode_hsync_offset, mode_vsync_offset; > > + int mode_hsync_width, mode_vsync_width; > > + int mode_hsync_polarity, mode_vsync_polarity; > > + > > + chamelium_port_get_video_params(chamelium, port, &video_params); > > + > > + mode_clock = (double)mode->clock / 1000; > > + > > + if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_DisplayPort) { > > + /* this is what chamelium understands as offsets for DP */ > > + mode_hsync_offset = mode->htotal - mode->hsync_start; > > + mode_vsync_offset = mode->vtotal - mode->vsync_start; > > + } else { > > + /* and this is what they are for other connectors */ > > + mode_hsync_offset = mode->hsync_start - mode->hdisplay; > > + mode_vsync_offset = mode->vsync_start - mode->vdisplay; > > + } > > + > > + mode_hsync_width = mode->hsync_end - mode->hsync_start; > > + mode_vsync_width = mode->vsync_end - mode->vsync_start; > > + > > + mode_hsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC); > > + mode_vsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC); > > + > > + igt_debug("Checking video mode:\n"); > > + igt_debug("clock: got %f, expected %f ± %f%%\n", video_params.clock, > > + mode_clock, MODE_CLOCK_ACCURACY * 100); > > + igt_debug("hactive: got %d, expected %d\n", video_params.hactive, > > + mode->hdisplay); > > + igt_debug("vactive: got %d, expected %d\n", video_params.vactive, > > + mode->vdisplay); > > + igt_debug("hsync_offset: got %d, expected %d\n", > > + video_params.hsync_offset, mode_hsync_offset); > > + igt_debug("vsync_offset: got %d, expected %d\n", > > + video_params.vsync_offset, mode_vsync_offset); > > + igt_debug("htotal: got %d, expected %d\n", video_params.htotal, > > + mode->htotal); > > + igt_debug("vtotal: got %d, expected %d\n", video_params.vtotal, > > + mode->vtotal); > > + igt_debug("hsync_width: got %d, expected %d\n", > > + video_params.hsync_width, mode_hsync_width); > > + igt_debug("vsync_width: got %d, expected %d\n", > > + video_params.vsync_width, mode_vsync_width); > > + igt_debug("hsync_polarity: got %d, expected %d\n", > > + video_params.hsync_polarity, mode_hsync_polarity); > > + igt_debug("vsync_polarity: got %d, expected %d\n", > > + video_params.vsync_polarity, mode_vsync_polarity); > > + > > + if (!isnan(video_params.clock)) { > > + igt_assert(video_params.clock > > > + mode_clock * (1 - MODE_CLOCK_ACCURACY)); > > + igt_assert(video_params.clock < > > + mode_clock * (1 + MODE_CLOCK_ACCURACY)); > > + } > > + igt_assert(video_params.hactive == mode->hdisplay); > > + igt_assert(video_params.vactive == mode->vdisplay); > > + igt_assert(video_params.hsync_offset == mode_hsync_offset); > > + igt_assert(video_params.vsync_offset == mode_vsync_offset); > > + igt_assert(video_params.htotal == mode->htotal); > > + igt_assert(video_params.vtotal == mode->vtotal); > > + igt_assert(video_params.hsync_width == mode_hsync_width); > > + igt_assert(video_params.vsync_width == mode_vsync_width); > > + igt_assert(video_params.hsync_polarity == mode_hsync_polarity); > > + igt_assert(video_params.vsync_polarity == mode_vsync_polarity); > > +} > > + > > +static const char igt_custom_edid_type_read_desc[] = > > + "Make sure the EDID exposed by KMS is the same as the screen's"; > > +static void igt_custom_edid_type_read(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_custom_edid_type edid) > > +{ > > + drmModePropertyBlobPtr edid_blob = NULL; > > + drmModeConnector *connector; > > + size_t raw_edid_size; > > + const struct edid *raw_edid; > > + uint64_t edid_blob_id; > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + chamelium_set_edid(data, port, edid); > > + chamelium_plug(data->chamelium, port); > > + chamelium_wait_for_conn_status_change(&data->display, data->chamelium, > > + port, DRM_MODE_CONNECTED); > > + > > + igt_skip_on(chamelium_check_analog_bridge(data, port)); > > + > > + connector = chamelium_port_get_connector(data->chamelium, port, true); > > + 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_id != 0); > > + edid_blob = drmModeGetPropertyBlob(data->drm_fd, edid_blob_id); > > + igt_assert(edid_blob); > > + > > + raw_edid = chamelium_edid_get_raw(data->edids[edid], port); > > + raw_edid_size = edid_get_size(raw_edid); > > + igt_assert(memcmp(raw_edid, edid_blob->data, raw_edid_size) == 0); > > + > > + drmModeFreePropertyBlob(edid_blob); > > + drmModeFreeConnector(connector); > > +} > > + > > +static const char igt_edid_stress_resolution_desc[] = > > + "Stress test the DUT by testing multiple EDIDs, one right after the other," > > + "and ensure their validity by check the real screen resolution vs the" > > + "advertised mode resultion."; > > +static void edid_stress_resolution(chamelium_data_t *data, > > + struct chamelium_port *port, > > + monitor_edid edids_list[], > > + size_t edids_list_len) > > +{ > > + int i; > > + struct chamelium *chamelium = data->chamelium; > > + struct udev_monitor *mon = igt_watch_uevents(); > > + > > + for (i = 0; i < edids_list_len; ++i) { > > + struct chamelium_edid *chamelium_edid; > > + drmModeModeInfo mode; > > + struct igt_fb fb = { 0 }; > > + igt_output_t *output; > > + enum pipe pipe; > > + bool is_video_stable; > > + int screen_res_w, screen_res_h; > > + > > + monitor_edid *edid = &edids_list[i]; > > + igt_info("Testing out the EDID for %s\n", > > + monitor_edid_get_name(edid)); > > + > > + /* Getting and Setting the EDID on Chamelium. */ > > + chamelium_edid = > > + get_chameleon_edid_from_monitor_edid(chamelium, edid); > > + chamelium_port_set_edid(data->chamelium, port, chamelium_edid); > > + free_chamelium_edid_from_monitor_edid(chamelium_edid); > > + > > + igt_flush_uevents(mon); > > + chamelium_plug(chamelium, port); > > + chamelium_wait_for_connector_after_hotplug(data, mon, port, > > + DRM_MODE_CONNECTED); > > + igt_flush_uevents(mon); > > + > > + /* Setting an output on the screen to turn it on. */ > > + mode = chamelium_get_mode_for_port(chamelium, port); > > + chamelium_create_fb_for_mode(data, &fb, &mode); > > + output = chamelium_get_output_for_port(data, port); > > + pipe = chamelium_get_pipe_for_output(&data->display, output); > > + igt_output_set_pipe(output, pipe); > > + chamelium_enable_output(data, port, output, &mode, &fb); > > + > > + /* Capture the screen resolution and verify. */ > > + is_video_stable = chamelium_port_wait_video_input_stable( > > + chamelium, port, 5); > > + igt_assert(is_video_stable); > > + > > + chamelium_port_get_resolution(chamelium, port, &screen_res_w, > > + &screen_res_h); > > + igt_assert(screen_res_w == fb.width); > > + igt_assert(screen_res_h == fb.height); > > + > > + // Clean up > > + igt_remove_fb(data->drm_fd, &fb); > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_unplug(chamelium, port); > > + } > > + > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > +} > > + > > +static const char igt_edid_resolution_list_desc[] = > > + "Get an EDID with many modes of different configurations, set them on the screen and check the" > > + " screen resolution matches the mode resolution."; > > + > > +static void edid_resolution_list(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + struct chamelium *chamelium = data->chamelium; > > + struct udev_monitor *mon = igt_watch_uevents(); > > + drmModeConnector *connector; > > + drmModeModeInfoPtr modes; > > + int count_modes; > > + int i; > > + igt_output_t *output; > > + enum pipe pipe; > > + > > + chamelium_unplug(chamelium, port); > > + chamelium_set_edid(data, port, IGT_CUSTOM_EDID_FULL); > > + > > + igt_flush_uevents(mon); > > + chamelium_plug(chamelium, port); > > + chamelium_wait_for_connector_after_hotplug(data, mon, port, > > + DRM_MODE_CONNECTED); > > + igt_flush_uevents(mon); > > + > > + connector = chamelium_port_get_connector(chamelium, port, true); > > + modes = connector->modes; > > + count_modes = connector->count_modes; > > + > > + output = chamelium_get_output_for_port(data, port); > > + pipe = chamelium_get_pipe_for_output(&data->display, output); > > + igt_output_set_pipe(output, pipe); > > + > > + for (i = 0; i < count_modes; ++i) > > + igt_debug("#%d %s %uHz\n", i, modes[i].name, modes[i].vrefresh); > > + > > + for (i = 0; i < count_modes; ++i) { > > + struct igt_fb fb = { 0 }; > > + bool is_video_stable; > > + int screen_res_w, screen_res_h; > > + > > + igt_info("Testing #%d %s %uHz\n", i, modes[i].name, > > + modes[i].vrefresh); > > + > > + /* Set the screen mode with the one we chose. */ > > + chamelium_create_fb_for_mode(data, &fb, &modes[i]); > > + chamelium_enable_output(data, port, output, &modes[i], &fb); > > + is_video_stable = chamelium_port_wait_video_input_stable( > > + chamelium, port, 10); > > + igt_assert(is_video_stable); > > + > > + chamelium_port_get_resolution(chamelium, port, &screen_res_w, > > + &screen_res_h); > > + igt_assert_eq(screen_res_w, modes[i].hdisplay); > > + igt_assert_eq(screen_res_h, modes[i].vdisplay); > > + > > + igt_remove_fb(data->drm_fd, &fb); > > + } > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + drmModeFreeConnector(connector); > > +} > > + > > +static const char test_suspend_resume_edid_change_desc[] = > > + "Simulate a screen being unplugged and another screen being plugged " > > + "during suspend, check that a uevent is sent and connector status is " > > + "updated"; > > +static void test_suspend_resume_edid_change(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_suspend_state state, > > + enum igt_suspend_test test, > > + enum igt_custom_edid_type edid, > > + enum igt_custom_edid_type alt_edid) > > +{ > > + struct udev_monitor *mon = igt_watch_uevents(); > > + bool link_status_failed[2][data->port_count]; > > + int p; > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + /* Catch the event and flush all remaining ones. */ > > + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)); > > + igt_flush_uevents(mon); > > + > > + /* First plug in the port */ > > + chamelium_set_edid(data, port, edid); > > + chamelium_plug(data->chamelium, port); > > + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)); > > + > > + chamelium_wait_for_conn_status_change(&data->display, data->chamelium, > > + 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_set_edid(data, port, alt_edid); > > + > > + get_connectors_link_status_failed(data, link_status_failed[0]); > > + > > + igt_flush_uevents(mon); > > + > > + igt_system_suspend_autoresume(state, test); > > + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)); > > + chamelium_assert_reachable(data->chamelium, ONLINE_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 const char test_mode_timings_desc[] = > > + "For each mode of the IGT base EDID, perform a modeset and check the " > > + "mode detected by the Chamelium receiver matches the mode we set"; > > +static void test_mode_timings(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + int i, count_modes; > > + > > + i = 0; > > + igt_require(chamelium_supports_get_video_params(data->chamelium)); > > + do { > > + igt_output_t *output; > > + igt_plane_t *primary; > > + drmModeConnector *connector; > > + drmModeModeInfo *mode; > > + int fb_id; > > + struct igt_fb fb; > > + > > + /* > > + * let's reset state each mode so we will get the > > + * HPD pulses realibably > > + */ > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + /* > > + * modes may change due to mode pruining and link issues, so we > > + * need to refresh the connector > > + */ > > + output = chamelium_prepare_output(data, port, > > + IGT_CUSTOM_EDID_BASE); > > + connector = chamelium_port_get_connector(data->chamelium, port, > > + false); > > + primary = igt_output_get_plane_type(output, > > + DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + /* we may skip some modes due to above but that's ok */ > > + count_modes = connector->count_modes; > > + if (i >= count_modes) > > + break; > > + > > + mode = &connector->modes[i]; > > + > > + fb_id = igt_create_color_pattern_fb( > > + data->drm_fd, mode->hdisplay, mode->vdisplay, > > + DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, 0, 0, 0, > > + &fb); > > + igt_assert(fb_id > 0); > > + > > + chamelium_enable_output(data, port, output, mode, &fb); > > + > > + /* Trigger the FSM */ > > + chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 0); > > + > > + check_mode(data->chamelium, port, mode); > > + > > + igt_remove_fb(data->drm_fd, &fb); > > + drmModeFreeConnector(connector); > > + } while (++i < count_modes); > > +} > > + > > +IGT_TEST_DESCRIPTION("Testing EDID with a Chamelium board"); > > +igt_main > > +{ > > + chamelium_data_t data; > > + struct chamelium_port *port; > > + int p; > > + > > + igt_fixture { > > + chamelium_init_test(&data); > > + } > > + > > + igt_describe("DisplayPort tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_DisplayPort, > > + data.port_count, 1); > > + } > > + > > + igt_describe(igt_custom_edid_type_read_desc); > > + connector_subtest("dp-edid-read", DisplayPort) > > + { > > + igt_custom_edid_type_read(&data, port, > > + IGT_CUSTOM_EDID_BASE); > > + igt_custom_edid_type_read(&data, port, > > + IGT_CUSTOM_EDID_ALT); > > + } > > + > > + igt_describe(igt_edid_stress_resolution_desc); > > + connector_subtest("dp-edid-stress-resolution-4k", DisplayPort) > > + edid_stress_resolution(&data, port, DP_EDIDS_4K, > > + ARRAY_SIZE(DP_EDIDS_4K)); > > + > > + igt_describe(igt_edid_stress_resolution_desc); > > + connector_subtest("dp-edid-stress-resolution-non-4k", > > + DisplayPort) > > + edid_stress_resolution(&data, port, DP_EDIDS_NON_4K, > > + ARRAY_SIZE(DP_EDIDS_NON_4K)); > > + > > + igt_describe(igt_edid_resolution_list_desc); > > + connector_subtest("dp-edid-resolution-list", DisplayPort) > > + edid_resolution_list(&data, port); > > + > > + igt_describe(test_suspend_resume_edid_change_desc); > > + connector_subtest("dp-edid-change-during-suspend", DisplayPort) > > + test_suspend_resume_edid_change(&data, port, > > + SUSPEND_STATE_MEM, > > + SUSPEND_TEST_NONE, > > + IGT_CUSTOM_EDID_BASE, > > + IGT_CUSTOM_EDID_ALT); > > + > > + igt_describe(test_suspend_resume_edid_change_desc); > > + connector_subtest("dp-edid-change-during-hibernate", > > + DisplayPort) > > + test_suspend_resume_edid_change(&data, port, > > + SUSPEND_STATE_DISK, > > + SUSPEND_TEST_DEVICES, > > + IGT_CUSTOM_EDID_BASE, > > + IGT_CUSTOM_EDID_ALT); > > + > > + igt_describe(test_mode_timings_desc); > > + connector_subtest("dp-mode-timings", DisplayPort) > > + test_mode_timings(&data, port); > > + } > > + > > + igt_describe("HDMI tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_HDMIA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(igt_custom_edid_type_read_desc); > > + connector_subtest("hdmi-edid-read", HDMIA) > > + { > > + igt_custom_edid_type_read(&data, port, > > + IGT_CUSTOM_EDID_BASE); > > + igt_custom_edid_type_read(&data, port, > > + IGT_CUSTOM_EDID_ALT); > > + } > > + > > + igt_describe(igt_edid_stress_resolution_desc); > > + connector_subtest("hdmi-edid-stress-resolution-4k", HDMIA) > > + edid_stress_resolution(&data, port, HDMI_EDIDS_4K, > > + ARRAY_SIZE(HDMI_EDIDS_4K)); > > + > > + igt_describe(igt_edid_stress_resolution_desc); > > + connector_subtest("hdmi-edid-stress-resolution-non-4k", HDMIA) > > + edid_stress_resolution(&data, port, HDMI_EDIDS_NON_4K, > > + ARRAY_SIZE(HDMI_EDIDS_NON_4K)); > > + > > + igt_describe(test_suspend_resume_edid_change_desc); > > + connector_subtest("hdmi-edid-change-during-suspend", HDMIA) > > + test_suspend_resume_edid_change(&data, port, > > + SUSPEND_STATE_MEM, > > + SUSPEND_TEST_NONE, > > + IGT_CUSTOM_EDID_BASE, > > + IGT_CUSTOM_EDID_ALT); > > + > > + igt_describe(test_suspend_resume_edid_change_desc); > > + connector_subtest("hdmi-edid-change-during-hibernate", HDMIA) > > + test_suspend_resume_edid_change(&data, port, > > + SUSPEND_STATE_DISK, > > + SUSPEND_TEST_DEVICES, > > + IGT_CUSTOM_EDID_BASE, > > + IGT_CUSTOM_EDID_ALT); > > + > > + igt_describe(test_mode_timings_desc); > > + connector_subtest("hdmi-mode-timings", HDMIA) > > + test_mode_timings(&data, port); > > + } > > + > > + igt_describe("VGA tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_VGA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(igt_custom_edid_type_read_desc); > > + connector_subtest("vga-edid-read", VGA) > > + { > > + igt_custom_edid_type_read(&data, port, > > + IGT_CUSTOM_EDID_BASE); > > + igt_custom_edid_type_read(&data, port, > > + IGT_CUSTOM_EDID_ALT); > > + } > > + } > > + > > + igt_fixture { > > + igt_display_fini(&data.display); > > + close(data.drm_fd); > > + } > > +} > > diff --git a/tests/chamelium/kms_chamelium_frames.c b/tests/chamelium/kms_chamelium_frames.c > > new file mode 100644 > > index 00000000..008bc34b > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_frames.c > > @@ -0,0 +1,1085 @@ > > +/* > > + * 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 "igt_eld.h" > > +#include "igt_infoframe.h" > > +#include "kms_chamelium_helper.h" > > + > > +#define connector_dynamic_subtest(name__, type__) \ > > + igt_subtest_with_dynamic(name__) \ > > + for_each_port(p, port) if (chamelium_port_get_type(port) == \ > > + DRM_MODE_CONNECTOR_##type__) > > + > > +struct vic_mode { > > + int hactive, vactive; > > + int vrefresh; /* Hz */ > > + uint32_t picture_ar; > > +}; > > + > > +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 }, > > +}; > > + > > +/* Maps Video Identification Codes to a mode */ > > +static const struct vic_mode vic_modes[] = { > > + [16] = { > > + .hactive = 1920, > > + .vactive = 1080, > > + .vrefresh = 60, > > + .picture_ar = DRM_MODE_PICTURE_ASPECT_16_9, > > + }, > > +}; > > + > > +/* Maps aspect ratios to their mode flag */ > > +static const uint32_t mode_ar_flags[] = { > > + [DRM_MODE_PICTURE_ASPECT_16_9] = DRM_MODE_FLAG_PIC_AR_16_9, > > +}; > > + > > +static bool prune_vga_mode(chamelium_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 void do_test_display(chamelium_data_t *data, struct chamelium_port *port, > > + igt_output_t *output, drmModeModeInfo *mode, > > + uint32_t fourcc, enum chamelium_check check, > > + int count) > > +{ > > + struct chamelium_fb_crc_async_data *fb_crc; > > + struct igt_fb frame_fb, fb; > > + int i, fb_id, captured_frame_count; > > + int frame_id; > > + > > + fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay, > > + DRM_FORMAT_XRGB8888, 64, &fb); > > + igt_assert(fb_id > 0); > > + > > + frame_id = > > + igt_fb_convert(&frame_fb, &fb, fourcc, DRM_FORMAT_MOD_LINEAR); > > + igt_assert(frame_id > 0); > > + > > + if (check == CHAMELIUM_CHECK_CRC) > > + fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd, > > + &fb); > > + > > + chamelium_enable_output(data, port, output, mode, &frame_fb); > > + > > + if (check == CHAMELIUM_CHECK_CRC) { > > + igt_crc_t *expected_crc; > > + igt_crc_t *crc; > > + > > + /* 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 (i = 0; i < captured_frame_count; i++) > > + chamelium_assert_crc_eq_or_dump( > > + data->chamelium, expected_crc, &crc[i], &fb, i); > > + > > + free(expected_crc); > > + free(crc); > > + } else if (check == CHAMELIUM_CHECK_ANALOG || > > + check == CHAMELIUM_CHECK_CHECKERBOARD) { > > + struct chamelium_frame_dump *dump; > > + > > + igt_assert(count == 1); > > + > > + dump = chamelium_port_dump_pixels(data->chamelium, port, 0, 0, > > + 0, 0); > > + > > + if (check == CHAMELIUM_CHECK_ANALOG) > > + chamelium_crop_analog_frame(dump, mode->hdisplay, > > + mode->vdisplay); > > + > > + chamelium_assert_frame_match_or_dump(data->chamelium, port, > > + dump, &fb, check); > > + chamelium_destroy_frame_dump(dump); > > + } > > + > > + igt_remove_fb(data->drm_fd, &frame_fb); > > + igt_remove_fb(data->drm_fd, &fb); > > +} > > + > > +static enum infoframe_avi_picture_aspect_ratio > > +get_infoframe_avi_picture_ar(uint32_t aspect_ratio) > > +{ > > + /* The AVI picture aspect ratio field only supports 4:3 and 16:9 */ > > + switch (aspect_ratio) { > > + case DRM_MODE_PICTURE_ASPECT_4_3: > > + return INFOFRAME_AVI_PIC_AR_4_3; > > + case DRM_MODE_PICTURE_ASPECT_16_9: > > + return INFOFRAME_AVI_PIC_AR_16_9; > > + default: > > + return INFOFRAME_AVI_PIC_AR_UNSPECIFIED; > > + } > > +} > > + > > +static bool vic_mode_matches_drm(const struct vic_mode *vic_mode, > > + drmModeModeInfo *drm_mode) > > +{ > > + uint32_t ar_flag = mode_ar_flags[vic_mode->picture_ar]; > > + > > + return vic_mode->hactive == drm_mode->hdisplay && > > + vic_mode->vactive == drm_mode->vdisplay && > > + vic_mode->vrefresh == drm_mode->vrefresh && > > + ar_flag == (drm_mode->flags & DRM_MODE_FLAG_PIC_AR_MASK); > > +} > > + > > +static void randomize_plane_stride(chamelium_data_t *data, uint32_t width, > > + uint32_t height, uint32_t format, > > + uint64_t modifier, size_t *stride) > > +{ > > + size_t stride_min; > > + uint32_t max_tile_w = 4, tile_w, tile_h; > > + int i; > > + struct igt_fb dummy; > > + > > + stride_min = width * igt_format_plane_bpp(format, 0) / 8; > > + > > + /* Randomize the stride to less than twice the minimum. */ > > + *stride = (rand() % stride_min) + stride_min; > > + > > + /* > > + * Create a dummy FB to determine bpp for each plane, and calculate > > + * the maximum tile width from that. > > + */ > > + igt_create_fb(data->drm_fd, 64, 64, format, modifier, &dummy); > > + for (i = 0; i < dummy.num_planes; i++) { > > + igt_get_fb_tile_size(data->drm_fd, modifier, dummy.plane_bpp[i], > > + &tile_w, &tile_h); > > + > > + if (tile_w > max_tile_w) > > + max_tile_w = tile_w; > > + } > > + igt_remove_fb(data->drm_fd, &dummy); > > + > > + /* > > + * Pixman requires the stride to be aligned to 32-bits, which is > > + * reflected in the initial value of max_tile_w and the hw > > + * may require a multiple of tile width, choose biggest of the 2. > > + */ > > + *stride = ALIGN(*stride, max_tile_w); > > +} > > + > > +static void update_tiled_modifier(igt_plane_t *plane, uint32_t width, > > + uint32_t height, uint32_t format, > > + uint64_t *modifier) > > +{ > > + if (*modifier == DRM_FORMAT_MOD_BROADCOM_SAND256) { > > + /* Randomize the column height to less than twice the minimum. > > + */ > > + size_t column_height = (rand() % height) + height; > > + > > + igt_debug( > > + "Selecting VC4 SAND256 tiling with column height %ld\n", > > + column_height); > > + > > + *modifier = DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT( > > + column_height); > > + } > > +} > > + > > +static void randomize_plane_setup(chamelium_data_t *data, igt_plane_t *plane, > > + drmModeModeInfo *mode, uint32_t *width, > > + uint32_t *height, uint32_t *format, > > + uint64_t *modifier, bool allow_yuv) > > +{ > > + int min_dim; > > + uint32_t idx[plane->format_mod_count]; > > + unsigned int count = 0; > > + unsigned int i; > > + > > + /* First pass to count the supported formats. */ > > + for (i = 0; i < plane->format_mod_count; i++) > > + if (igt_fb_supported_format(plane->formats[i]) && > > + (allow_yuv || !igt_format_is_yuv(plane->formats[i]))) > > + idx[count++] = i; > > + > > + igt_assert(count > 0); > > + > > + i = idx[rand() % count]; > > + *format = plane->formats[i]; > > + *modifier = plane->modifiers[i]; > > + > > + update_tiled_modifier(plane, *width, *height, *format, modifier); > > + > > + /* > > + * Randomize width and height in the mode dimensions range. > > + * > > + * Restrict to a min of 2 * min_dim, this way src_w/h are always at > > + * least min_dim, because src_w = width - (rand % w / 2). > > + * > > + * Use a minimum dimension of 16 for YUV, because planar YUV > > + * subsamples the UV plane. > > + */ > > + min_dim = igt_format_is_yuv(*format) ? 16 : 8; > > + > > + *width = max((rand() % mode->hdisplay) + 1, 2 * min_dim); > > + *height = max((rand() % mode->vdisplay) + 1, 2 * min_dim); > > +} > > + > > +static void configure_plane(igt_plane_t *plane, uint32_t src_w, uint32_t src_h, > > + uint32_t src_x, uint32_t src_y, uint32_t crtc_w, > > + uint32_t crtc_h, int32_t crtc_x, int32_t crtc_y, > > + struct igt_fb *fb) > > +{ > > + igt_plane_set_fb(plane, fb); > > + > > + igt_plane_set_position(plane, crtc_x, crtc_y); > > + igt_plane_set_size(plane, crtc_w, crtc_h); > > + > > + igt_fb_set_position(fb, plane, src_x, src_y); > > + igt_fb_set_size(fb, plane, src_w, src_h); > > +} > > + > > +static void randomize_plane_coordinates( > > + chamelium_data_t *data, igt_plane_t *plane, drmModeModeInfo *mode, > > + struct igt_fb *fb, uint32_t *src_w, uint32_t *src_h, uint32_t *src_x, > > + uint32_t *src_y, uint32_t *crtc_w, uint32_t *crtc_h, int32_t *crtc_x, > > + int32_t *crtc_y, bool allow_scaling) > > +{ > > + bool is_yuv = igt_format_is_yuv(fb->drm_format); > > + uint32_t width = fb->width, height = fb->height; > > + double ratio; > > + int ret; > > + > > + /* Randomize source offset in the first half of the original size. */ > > + *src_x = rand() % (width / 2); > > + *src_y = rand() % (height / 2); > > + > > + /* The source size only includes the active source area. */ > > + *src_w = width - *src_x; > > + *src_h = height - *src_y; > > + > > + if (allow_scaling) { > > + *crtc_w = (rand() % mode->hdisplay) + 1; > > + *crtc_h = (rand() % mode->vdisplay) + 1; > > + > > + /* > > + * Don't bother with scaling if dimensions are quite close in > > + * order to get non-scaling cases more frequently. Also limit > > + * scaling to 3x to avoid aggressive filtering that makes > > + * comparison less reliable, and don't go above 2x downsampling > > + * to avoid possible hw limitations. > > + */ > > + > > + ratio = ((double)*crtc_w / *src_w); > > + if (ratio < 0.5) > > + *src_w = *crtc_w * 2; > > + else if (ratio > 0.8 && ratio < 1.2) > > + *crtc_w = *src_w; > > + else if (ratio > 3.0) > > + *crtc_w = *src_w * 3; > > + > > + ratio = ((double)*crtc_h / *src_h); > > + if (ratio < 0.5) > > + *src_h = *crtc_h * 2; > > + else if (ratio > 0.8 && ratio < 1.2) > > + *crtc_h = *src_h; > > + else if (ratio > 3.0) > > + *crtc_h = *src_h * 3; > > + } else { > > + *crtc_w = *src_w; > > + *crtc_h = *src_h; > > + } > > + > > + if (*crtc_w != *src_w || *crtc_h != *src_h) { > > + /* > > + * When scaling is involved, make sure to not go off-bounds or > > + * scaled clipping may result in decimal dimensions, that most > > + * drivers don't support. > > + */ > > + if (*crtc_w < mode->hdisplay) > > + *crtc_x = rand() % (mode->hdisplay - *crtc_w); > > + else > > + *crtc_x = 0; > > + > > + if (*crtc_h < mode->vdisplay) > > + *crtc_y = rand() % (mode->vdisplay - *crtc_h); > > + else > > + *crtc_y = 0; > > + } else { > > + /* > > + * Randomize the on-crtc position and allow the plane to go > > + * off-display by less than half of its on-crtc dimensions. > > + */ > > + *crtc_x = (rand() % mode->hdisplay) - *crtc_w / 2; > > + *crtc_y = (rand() % mode->vdisplay) - *crtc_h / 2; > > + } > > + > > + configure_plane(plane, *src_w, *src_h, *src_x, *src_y, *crtc_w, *crtc_h, > > + *crtc_x, *crtc_y, fb); > > + ret = igt_display_try_commit_atomic( > > + &data->display, > > + DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, > > + NULL); > > + if (!ret) > > + return; > > + > > + /* Coordinates are logged in the dumped debug log, so only report w/h on > > + * failure here. */ > > + igt_assert_f(ret != -ENOSPC, > > + "Failure in testcase, invalid coordinates on a %ux%u fb\n", > > + width, height); > > + > > + /* Make YUV coordinates a multiple of 2 and retry the math. */ > > + if (is_yuv) { > > + *src_x &= ~1; > > + *src_y &= ~1; > > + *src_w &= ~1; > > + *src_h &= ~1; > > + /* To handle 1:1 scaling, clear crtc_w/h too. */ > > + *crtc_w &= ~1; > > + *crtc_h &= ~1; > > + > > + if (*crtc_x < 0 && (*crtc_x & 1)) > > + (*crtc_x)++; > > + else > > + *crtc_x &= ~1; > > + > > + /* If negative, round up to 0 instead of down */ > > + if (*crtc_y < 0 && (*crtc_y & 1)) > > + (*crtc_y)++; > > + else > > + *crtc_y &= ~1; > > + > > + configure_plane(plane, *src_w, *src_h, *src_x, *src_y, *crtc_w, > > + *crtc_h, *crtc_x, *crtc_y, fb); > > + ret = igt_display_try_commit_atomic( > > + &data->display, > > + DRM_MODE_ATOMIC_TEST_ONLY | > > + DRM_MODE_ATOMIC_ALLOW_MODESET, > > + NULL); > > + if (!ret) > > + return; > > + } > > + > > + igt_assert(!ret || allow_scaling); > > + igt_info("Scaling ratio %g / %g failed, trying without scaling.\n", > > + ((double)*crtc_w / *src_w), ((double)*crtc_h / *src_h)); > > + > > + *crtc_w = *src_w; > > + *crtc_h = *src_h; > > + > > + configure_plane(plane, *src_w, *src_h, *src_x, *src_y, *crtc_w, *crtc_h, > > + *crtc_x, *crtc_y, fb); > > + igt_display_commit_atomic(&data->display, > > + DRM_MODE_ATOMIC_TEST_ONLY | > > + DRM_MODE_ATOMIC_ALLOW_MODESET, > > + NULL); > > +} > > + > > +static void blit_plane_cairo(chamelium_data_t *data, cairo_surface_t *result, > > + uint32_t src_w, uint32_t src_h, uint32_t src_x, > > + uint32_t src_y, uint32_t crtc_w, uint32_t crtc_h, > > + int32_t crtc_x, int32_t crtc_y, struct igt_fb *fb) > > +{ > > + cairo_surface_t *surface; > > + cairo_surface_t *clipped_surface; > > + cairo_t *cr; > > + > > + surface = igt_get_cairo_surface(data->drm_fd, fb); > > + > > + if (src_x || src_y) { > > + clipped_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, > > + src_w, src_h); > > + > > + cr = cairo_create(clipped_surface); > > + > > + cairo_translate(cr, -1. * src_x, -1. * src_y); > > + > > + cairo_set_source_surface(cr, surface, 0, 0); > > + > > + cairo_paint(cr); > > + cairo_surface_flush(clipped_surface); > > + > > + cairo_destroy(cr); > > + } else { > > + clipped_surface = surface; > > + } > > + > > + cr = cairo_create(result); > > + > > + cairo_translate(cr, crtc_x, crtc_y); > > + > > + if (src_w != crtc_w || src_h != crtc_h) { > > + cairo_scale(cr, (double)crtc_w / src_w, (double)crtc_h / src_h); > > + } > > + > > + cairo_set_source_surface(cr, clipped_surface, 0, 0); > > + cairo_surface_destroy(clipped_surface); > > + > > + if (src_w != crtc_w || src_h != crtc_h) { > > + cairo_pattern_set_filter(cairo_get_source(cr), > > + CAIRO_FILTER_BILINEAR); > > + cairo_pattern_set_extend(cairo_get_source(cr), > > + CAIRO_EXTEND_NONE); > > + } > > + > > + cairo_paint(cr); > > + cairo_surface_flush(result); > > + > > + cairo_destroy(cr); > > +} > > + > > +static void prepare_randomized_plane(chamelium_data_t *data, > > + drmModeModeInfo *mode, igt_plane_t *plane, > > + struct igt_fb *overlay_fb, > > + unsigned int index, > > + cairo_surface_t *result_surface, > > + bool allow_scaling, bool allow_yuv) > > +{ > > + struct igt_fb pattern_fb; > > + uint32_t overlay_fb_w, overlay_fb_h; > > + uint32_t overlay_src_w, overlay_src_h; > > + uint32_t overlay_src_x, overlay_src_y; > > + int32_t overlay_crtc_x, overlay_crtc_y; > > + uint32_t overlay_crtc_w, overlay_crtc_h; > > + uint32_t format; > > + uint64_t modifier; > > + size_t stride; > > + bool tiled; > > + int fb_id; > > + > > + randomize_plane_setup(data, plane, mode, &overlay_fb_w, &overlay_fb_h, > > + &format, &modifier, allow_yuv); > > + > > + tiled = (modifier != DRM_FORMAT_MOD_LINEAR); > > + igt_debug("Plane %d: framebuffer size %dx%d %s format (%s)\n", index, > > + overlay_fb_w, overlay_fb_h, igt_format_str(format), > > + tiled ? "tiled" : "linear"); > > + > > + /* Get a pattern framebuffer for the overlay plane. */ > > + fb_id = chamelium_get_pattern_fb(data, overlay_fb_w, overlay_fb_h, > > + DRM_FORMAT_XRGB8888, 32, &pattern_fb); > > + igt_assert(fb_id > 0); > > + > > + randomize_plane_stride(data, overlay_fb_w, overlay_fb_h, format, > > + modifier, &stride); > > + > > + igt_debug("Plane %d: stride %ld\n", index, stride); > > + > > + fb_id = igt_fb_convert_with_stride(overlay_fb, &pattern_fb, format, > > + modifier, stride); > > + igt_assert(fb_id > 0); > > + > > + randomize_plane_coordinates(data, plane, mode, overlay_fb, > > + &overlay_src_w, &overlay_src_h, > > + &overlay_src_x, &overlay_src_y, > > + &overlay_crtc_w, &overlay_crtc_h, > > + &overlay_crtc_x, &overlay_crtc_y, > > + allow_scaling); > > + > > + igt_debug("Plane %d: in-framebuffer size %dx%d\n", index, overlay_src_w, > > + overlay_src_h); > > + igt_debug("Plane %d: in-framebuffer position %dx%d\n", index, > > + overlay_src_x, overlay_src_y); > > + igt_debug("Plane %d: on-crtc size %dx%d\n", index, overlay_crtc_w, > > + overlay_crtc_h); > > + igt_debug("Plane %d: on-crtc position %dx%d\n", index, overlay_crtc_x, > > + overlay_crtc_y); > > + > > + blit_plane_cairo(data, result_surface, overlay_src_w, overlay_src_h, > > + overlay_src_x, overlay_src_y, overlay_crtc_w, > > + overlay_crtc_h, overlay_crtc_x, overlay_crtc_y, > > + &pattern_fb); > > + > > + /* Remove the original pattern framebuffer. */ > > + igt_remove_fb(data->drm_fd, &pattern_fb); > > +} > > + > > +static const char test_display_one_mode_desc[] = > > + "Pick the first mode of the IGT base EDID, display and capture a few " > > + "frames, then check captured frames are correct"; > > +static void test_display_one_mode(chamelium_data_t *data, > > + struct chamelium_port *port, uint32_t fourcc, > > + enum chamelium_check check, int count) > > +{ > > + drmModeConnector *connector; > > + drmModeModeInfo *mode; > > + igt_output_t *output; > > + igt_plane_t *primary; > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + output = chamelium_prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > + connector = chamelium_port_get_connector(data->chamelium, port, false); > > + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + igt_require(igt_plane_has_format_mod(primary, fourcc, > > + DRM_FORMAT_MOD_LINEAR)); > > + > > + mode = &connector->modes[0]; > > + if (check == CHAMELIUM_CHECK_ANALOG) { > > + bool bridge = chamelium_check_analog_bridge(data, port); > > + > > + igt_assert(!(bridge && prune_vga_mode(data, mode))); > > + } > > + > > + do_test_display(data, port, output, mode, fourcc, check, count); > > + > > + drmModeFreeConnector(connector); > > +} > > + > > +static const char test_display_all_modes_desc[] = > > + "For each mode of the IGT base EDID, display and capture a few " > > + "frames, then check captured frames are correct"; > > +static void test_display_all_modes(chamelium_data_t *data, > > + struct chamelium_port *port, uint32_t fourcc, > > + enum chamelium_check check, int count) > > +{ > > + bool bridge; > > + int i, count_modes; > > + > > + if (check == CHAMELIUM_CHECK_ANALOG) > > + bridge = chamelium_check_analog_bridge(data, port); > > + > > + i = 0; > > + do { > > + igt_output_t *output; > > + igt_plane_t *primary; > > + drmModeConnector *connector; > > + drmModeModeInfo *mode; > > + > > + /* > > + * let's reset state each mode so we will get the > > + * HPD pulses realibably > > + */ > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + /* > > + * modes may change due to mode pruining and link issues, so we > > + * need to refresh the connector > > + */ > > + output = chamelium_prepare_output(data, port, > > + IGT_CUSTOM_EDID_BASE); > > + connector = chamelium_port_get_connector(data->chamelium, port, > > + false); > > + primary = igt_output_get_plane_type(output, > > + DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + igt_require(igt_plane_has_format_mod(primary, fourcc, > > + DRM_FORMAT_MOD_LINEAR)); > > + > > + /* we may skip some modes due to above but that's ok */ > > + count_modes = connector->count_modes; > > + if (i >= count_modes) > > + break; > > + > > + mode = &connector->modes[i]; > > + > > + if (check == CHAMELIUM_CHECK_ANALOG && bridge && > > + prune_vga_mode(data, mode)) > > + continue; > > + > > + do_test_display(data, port, output, mode, fourcc, check, count); > > + drmModeFreeConnector(connector); > > + } while (++i < count_modes); > > +} > > + > > +static const char test_display_frame_dump_desc[] = > > + "For each mode of the IGT base EDID, display and capture a few " > > + "frames, then download the captured frames and compare them " > > + "bit-by-bit to the sent ones"; > > +static void test_display_frame_dump(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + int i, count_modes; > > + > > + i = 0; > > + do { > > + igt_output_t *output; > > + igt_plane_t *primary; > > + struct igt_fb fb; > > + struct chamelium_frame_dump *frame; > > + drmModeModeInfo *mode; > > + drmModeConnector *connector; > > + int fb_id, j; > > + > > + /* > > + * let's reset state each mode so we will get the > > + * HPD pulses realibably > > + */ > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + /* > > + * modes may change due to mode pruining and link issues, so we > > + * need to refresh the connector > > + */ > > + output = chamelium_prepare_output(data, port, > > + IGT_CUSTOM_EDID_BASE); > > + connector = chamelium_port_get_connector(data->chamelium, port, > > + false); > > + primary = igt_output_get_plane_type(output, > > + DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + /* we may skip some modes due to above but that's ok */ > > + count_modes = connector->count_modes; > > + if (i >= count_modes) > > + break; > > + > > + mode = &connector->modes[i]; > > + > > + fb_id = igt_create_color_pattern_fb( > > + data->drm_fd, mode->hdisplay, mode->vdisplay, > > + DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, 0, 0, 0, > > + &fb); > > + igt_assert(fb_id > 0); > > + > > + chamelium_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); > > + } while (++i < count_modes); > > +} > > + > > +static const char test_display_aspect_ratio_desc[] = > > + "Pick a mode with a picture aspect-ratio, capture AVI InfoFrames and " > > + "check they include the relevant fields"; > > +static void test_display_aspect_ratio(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + igt_output_t *output; > > + igt_plane_t *primary; > > + drmModeConnector *connector; > > + drmModeModeInfo *mode; > > + int fb_id, i; > > + struct igt_fb fb; > > + bool found, ok; > > + struct chamelium_infoframe *infoframe; > > + struct infoframe_avi infoframe_avi; > > + uint8_t vic = 16; /* TODO: test more VICs */ > > + const struct vic_mode *vic_mode; > > + uint32_t aspect_ratio; > > + enum infoframe_avi_picture_aspect_ratio frame_ar; > > + > > + igt_require(chamelium_supports_get_last_infoframe(data->chamelium)); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + output = chamelium_prepare_output(data, port, > > + IGT_CUSTOM_EDID_ASPECT_RATIO); > > + connector = chamelium_port_get_connector(data->chamelium, port, false); > > + primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + vic_mode = &vic_modes[vic]; > > + aspect_ratio = vic_mode->picture_ar; > > + > > + found = false; > > + igt_assert(connector->count_modes > 0); > > + for (i = 0; i < connector->count_modes; i++) { > > + mode = &connector->modes[i]; > > + > > + if (vic_mode_matches_drm(vic_mode, mode)) { > > + found = true; > > + break; > > + } > > + } > > + igt_assert_f(found, > > + "Failed to find mode with the correct aspect ratio\n"); > > + > > + fb_id = igt_create_color_pattern_fb(data->drm_fd, mode->hdisplay, > > + mode->vdisplay, DRM_FORMAT_XRGB8888, > > + DRM_FORMAT_MOD_LINEAR, 0, 0, 0, > > + &fb); > > + igt_assert(fb_id > 0); > > + > > + chamelium_enable_output(data, port, output, mode, &fb); > > + > > + infoframe = chamelium_get_last_infoframe(data->chamelium, port, > > + CHAMELIUM_INFOFRAME_AVI); > > + igt_assert_f(infoframe, "AVI InfoFrame not received\n"); > > + > > + ok = infoframe_avi_parse(&infoframe_avi, infoframe->version, > > + infoframe->payload, infoframe->payload_size); > > + igt_assert_f(ok, "Failed to parse AVI InfoFrame\n"); > > + > > + frame_ar = get_infoframe_avi_picture_ar(aspect_ratio); > > + > > + igt_debug("Checking AVI InfoFrame\n"); > > + igt_debug("Picture aspect ratio: got %d, expected %d\n", > > + infoframe_avi.picture_aspect_ratio, frame_ar); > > + igt_debug("Video Identification Code (VIC): got %d, expected %d\n", > > + infoframe_avi.vic, vic); > > + > > + igt_assert(infoframe_avi.picture_aspect_ratio == frame_ar); > > + igt_assert(infoframe_avi.vic == vic); > > + > > + chamelium_infoframe_destroy(infoframe); > > + igt_remove_fb(data->drm_fd, &fb); > > + drmModeFreeConnector(connector); > > +} > > + > > +static const char test_display_planes_random_desc[] = > > + "Setup a few overlay planes with random parameters, capture the frame " > > + "and check it matches the expected output"; > > +static void test_display_planes_random(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum chamelium_check check) > > +{ > > + igt_output_t *output; > > + drmModeModeInfo *mode; > > + igt_plane_t *primary_plane; > > + struct igt_fb primary_fb; > > + struct igt_fb result_fb; > > + struct igt_fb *overlay_fbs; > > + igt_crc_t *crc; > > + igt_crc_t *expected_crc; > > + struct chamelium_fb_crc_async_data *fb_crc; > > + unsigned int overlay_planes_max = 0; > > + unsigned int overlay_planes_count; > > + cairo_surface_t *result_surface; > > + int captured_frame_count; > > + bool allow_scaling; > > + bool allow_yuv; > > + unsigned int i; > > + unsigned int fb_id; > > + > > + switch (check) { > > + case CHAMELIUM_CHECK_CRC: > > + allow_scaling = false; > > + allow_yuv = false; > > + break; > > + case CHAMELIUM_CHECK_CHECKERBOARD: > > + allow_scaling = true; > > + allow_yuv = true; > > + break; > > + default: > > + igt_assert(false); > > + } > > + > > + srand(time(NULL)); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + /* Find the connector and pipe. */ > > + output = chamelium_prepare_output(data, port, IGT_CUSTOM_EDID_BASE); > > + > > + mode = igt_output_get_mode(output); > > + > > + /* Get a framebuffer for the primary plane. */ > > + primary_plane = > > + igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary_plane); > > + > > + fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay, > > + DRM_FORMAT_XRGB8888, 64, &primary_fb); > > + igt_assert(fb_id > 0); > > + > > + /* Get a framebuffer for the cairo composition result. */ > > + fb_id = igt_create_fb(data->drm_fd, mode->hdisplay, mode->vdisplay, > > + DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, > > + &result_fb); > > + igt_assert(fb_id > 0); > > + > > + result_surface = igt_get_cairo_surface(data->drm_fd, &result_fb); > > + > > + /* Paint the primary framebuffer on the result surface. */ > > + blit_plane_cairo(data, result_surface, 0, 0, 0, 0, 0, 0, 0, 0, > > + &primary_fb); > > + > > + /* Configure the primary plane. */ > > + igt_plane_set_fb(primary_plane, &primary_fb); > > + > > + overlay_planes_max = > > + igt_output_count_plane_type(output, DRM_PLANE_TYPE_OVERLAY); > > + > > + /* Limit the number of planes to a reasonable scene. */ > > + overlay_planes_max = min(overlay_planes_max, 4u); > > + > > + overlay_planes_count = (rand() % overlay_planes_max) + 1; > > + igt_debug("Using %d overlay planes\n", overlay_planes_count); > > + > > + overlay_fbs = calloc(sizeof(struct igt_fb), overlay_planes_count); > > + > > + for (i = 0; i < overlay_planes_count; i++) { > > + struct igt_fb *overlay_fb = &overlay_fbs[i]; > > + igt_plane_t *plane = igt_output_get_plane_type_index( > > + output, DRM_PLANE_TYPE_OVERLAY, i); > > + igt_assert(plane); > > + > > + prepare_randomized_plane(data, mode, plane, overlay_fb, i, > > + result_surface, allow_scaling, > > + allow_yuv); > > + } > > + > > + cairo_surface_destroy(result_surface); > > + > > + if (check == CHAMELIUM_CHECK_CRC) > > + fb_crc = chamelium_calculate_fb_crc_async_start(data->drm_fd, > > + &result_fb); > > + > > + igt_display_commit2(&data->display, COMMIT_ATOMIC); > > + > > + if (check == CHAMELIUM_CHECK_CRC) { > > + chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 1); > > + crc = chamelium_read_captured_crcs(data->chamelium, > > + &captured_frame_count); > > + > > + igt_assert(captured_frame_count == 1); > > + > > + expected_crc = chamelium_calculate_fb_crc_async_finish(fb_crc); > > + > > + chamelium_assert_crc_eq_or_dump(data->chamelium, expected_crc, > > + crc, &result_fb, 0); > > + > > + free(expected_crc); > > + free(crc); > > + } else if (check == CHAMELIUM_CHECK_CHECKERBOARD) { > > + struct chamelium_frame_dump *dump; > > + > > + dump = chamelium_port_dump_pixels(data->chamelium, port, 0, 0, > > + 0, 0); > > + chamelium_assert_frame_match_or_dump(data->chamelium, port, > > + dump, &result_fb, check); > > + chamelium_destroy_frame_dump(dump); > > + } > > + > > + for (i = 0; i < overlay_planes_count; i++) > > + igt_remove_fb(data->drm_fd, &overlay_fbs[i]); > > + > > + free(overlay_fbs); > > + > > + igt_remove_fb(data->drm_fd, &primary_fb); > > + igt_remove_fb(data->drm_fd, &result_fb); > > +} > > + > > +IGT_TEST_DESCRIPTION("Tests requiring a Chamelium board"); > > +igt_main > > +{ > > + chamelium_data_t data; > > + struct chamelium_port *port; > > + int p; > > + > > + igt_fixture { > > + chamelium_init_test(&data); > > + } > > + > > + igt_describe("DisplayPort tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_DisplayPort, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_display_all_modes_desc); > > + connector_subtest("dp-crc-single", DisplayPort) > > + test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_CRC, 1); > > + > > + igt_describe(test_display_one_mode_desc); > > + connector_subtest("dp-crc-fast", DisplayPort) > > + test_display_one_mode(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_CRC, 1); > > + > > + igt_describe(test_display_all_modes_desc); > > + connector_subtest("dp-crc-multiple", DisplayPort) > > + test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_CRC, 3); > > + > > + igt_describe(test_display_frame_dump_desc); > > + connector_subtest("dp-frame-dump", DisplayPort) > > + test_display_frame_dump(&data, port); > > + } > > + > > + igt_describe("HDMI tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_HDMIA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_display_all_modes_desc); > > + connector_subtest("hdmi-crc-single", HDMIA) > > + test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_CRC, 1); > > + > > + igt_describe(test_display_one_mode_desc); > > + connector_subtest("hdmi-crc-fast", HDMIA) > > + test_display_one_mode(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_CRC, 1); > > + > > + igt_describe(test_display_all_modes_desc); > > + connector_subtest("hdmi-crc-multiple", HDMIA) > > + test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_CRC, 3); > > + > > + igt_describe(test_display_one_mode_desc); > > + connector_dynamic_subtest("hdmi-crc-nonplanar-formats", HDMIA) > > + { > > + int k; > > + igt_output_t *output; > > + igt_plane_t *primary; > > + > > + output = chamelium_prepare_output(&data, port, > > + IGT_CUSTOM_EDID_BASE); > > + primary = igt_output_get_plane_type( > > + output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + for (k = 0; k < primary->format_mod_count; k++) { > > + if (!igt_fb_supported_format( > > + primary->formats[k])) > > + continue; > > + > > + if (igt_format_is_yuv(primary->formats[k])) > > + continue; > > + > > + if (primary->modifiers[k] != > > + DRM_FORMAT_MOD_LINEAR) > > + continue; > > + > > + igt_dynamic_f( > > + "%s", > > + igt_format_str(primary->formats[k])) > > + test_display_one_mode( > > + &data, port, > > + primary->formats[k], > > + CHAMELIUM_CHECK_CRC, 1); > > + } > > + } > > + > > + igt_describe(test_display_planes_random_desc); > > + connector_subtest("hdmi-crc-planes-random", HDMIA) > > + test_display_planes_random(&data, port, > > + CHAMELIUM_CHECK_CRC); > > + > > + igt_describe(test_display_one_mode_desc); > > + connector_dynamic_subtest("hdmi-cmp-planar-formats", HDMIA) > > + { > > + int k; > > + igt_output_t *output; > > + igt_plane_t *primary; > > + > > + output = chamelium_prepare_output(&data, port, > > + IGT_CUSTOM_EDID_BASE); > > + primary = igt_output_get_plane_type( > > + output, DRM_PLANE_TYPE_PRIMARY); > > + igt_assert(primary); > > + > > + for (k = 0; k < primary->format_mod_count; k++) { > > + if (!igt_fb_supported_format( > > + primary->formats[k])) > > + continue; > > + > > + if (!igt_format_is_yuv(primary->formats[k])) > > + continue; > > + > > + if (primary->modifiers[k] != > > + DRM_FORMAT_MOD_LINEAR) > > + continue; > > + > > + igt_dynamic_f( > > + "%s", > > + igt_format_str(primary->formats[k])) > > + test_display_one_mode( > > + &data, port, > > + primary->formats[k], > > + CHAMELIUM_CHECK_CHECKERBOARD, > > + 1); > > + } > > + } > > + > > + igt_describe(test_display_planes_random_desc); > > + connector_subtest("hdmi-cmp-planes-random", HDMIA) > > + test_display_planes_random( > > + &data, port, CHAMELIUM_CHECK_CHECKERBOARD); > > + > > + igt_describe(test_display_frame_dump_desc); > > + connector_subtest("hdmi-frame-dump", HDMIA) > > + test_display_frame_dump(&data, port); > > + > > + igt_describe(test_display_aspect_ratio_desc); > > + connector_subtest("hdmi-aspect-ratio", HDMIA) > > + test_display_aspect_ratio(&data, port); > > + } > > + > > + igt_describe("VGA tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_VGA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_display_all_modes_desc); > > + connector_subtest("vga-frame-dump", VGA) > > + test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888, > > + CHAMELIUM_CHECK_ANALOG, 1); > > + } > > + > > + igt_fixture { > > + igt_display_fini(&data.display); > > + close(data.drm_fd); > > + } > > +} > > diff --git a/tests/chamelium/kms_chamelium_helper.c b/tests/chamelium/kms_chamelium_helper.c > > new file mode 100644 > > index 00000000..b9544288 > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_helper.c > > @@ -0,0 +1,330 @@ > > +// SPDX-License-Identifier: MIT > > +/* > > + * A helper library for all Chamelium tests. > > + * > > + * Copyright 2022 Google LLC. > > + * > > + * Authors: Mark Yacoub > > + */ > > + > > +#include "igt_edid.h" > > +#include "kms_chamelium_helper.h" > > + > > +void chamelium_init_test(chamelium_data_t *data) > > +{ > > + int i; > > + > > + /* So fbcon doesn't try to reprobe things itself */ > > + kmstest_set_vt_graphics_mode(); > > + > > + data->drm_fd = drm_open_driver_master(DRIVER_ANY); > > + igt_display_require(&data->display, data->drm_fd); > > + igt_require(data->display.is_atomic); > > + > > + /* > > + * XXX: disabling modeset, can be removed when > > + * igt_display_require will start doing this for us > > + */ > > + igt_display_commit2(&data->display, COMMIT_ATOMIC); > > + > > + /* we need to initalize chamelium after igt_display_require */ > > + data->chamelium = chamelium_init(data->drm_fd, &data->display); > > + igt_require(data->chamelium); > > + > > + data->ports = chamelium_get_ports(data->chamelium, &data->port_count); > > + > > + for (i = 0; i < IGT_CUSTOM_EDID_COUNT; ++i) { > > + data->edids[i] = chamelium_new_edid(data->chamelium, > > + igt_kms_get_custom_edid(i)); > > + } > > +} > > + > > +/* Wait for hotplug and return the remaining time left from timeout */ > > +bool chamelium_wait_for_hotplug(struct udev_monitor *mon, int *timeout) > > +{ > > + struct timespec start, end; > > + int elapsed; > > + bool detected; > > + > > + igt_assert_eq(igt_gettime(&start), 0); > > + detected = igt_hotplug_detected(mon, *timeout); > > + igt_assert_eq(igt_gettime(&end), 0); > > + > > + elapsed = igt_time_elapsed(&start, &end); > > + igt_assert_lte(0, elapsed); > > + *timeout = max(0, *timeout - elapsed); > > + > > + return detected; > > +} > > + > > +/** > > + * chamelium_wait_for_connector_after_hotplug: > > + * > > + * Waits for the connector attached to @port to have a status of @status after > > + * it's plugged/unplugged. > > + * > > + */ > > +void chamelium_wait_for_connector_after_hotplug(chamelium_data_t *data, > > + struct udev_monitor *mon, > > + struct chamelium_port *port, > > + drmModeConnection status) > > +{ > > + int timeout = CHAMELIUM_HOTPLUG_TIMEOUT; > > + int hotplug_count = 0; > > + > > + igt_debug("Waiting for %s to get %s after a hotplug event...\n", > > + chamelium_port_get_name(port), > > + kmstest_connector_status_str(status)); > > + > > + while (timeout > 0) { > > + if (!chamelium_wait_for_hotplug(mon, &timeout)) > > + break; > > + > > + hotplug_count++; > > + > > + if (chamelium_reprobe_connector(&data->display, data->chamelium, > > + port) == status) > > + return; > > + } > > + > > + igt_assert_f( > > + false, > > + "Timed out waiting for %s to get %s after a hotplug. Current state %s hotplug_count %d\n", > > + chamelium_port_get_name(port), > > + kmstest_connector_status_str(status), > > + kmstest_connector_status_str(chamelium_reprobe_connector( > > + &data->display, data->chamelium, port)), > > + hotplug_count); > > +} > > + > > +/** > > + * chamelium_port_get_connector: > > + * @data: The Chamelium data instance to use > > + * @port: The chamelium port to prepare its connector > > + * @edid: The chamelium's default EDID has a lot of resolutions, way more then > > + * we need to test. Additionally the default EDID doesn't support > > + * HDMI audio. > > + * > > + * Makes sure the output display of the connector attached to @port is connected > > + * and ready for use. > > + * > > + * Returns: a pointer to the enabled igt_output_t > > + */ > > +igt_output_t *chamelium_prepare_output(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_custom_edid_type edid) > > +{ > > + igt_display_t *display = &data->display; > > + igt_output_t *output; > > + enum pipe pipe; > > + > > + /* The chamelium's default EDID has a lot of resolutions, way more then > > + * we need to test. Additionally the default EDID doesn't support HDMI > > + * audio. > > + */ > > + chamelium_set_edid(data, port, edid); > > + > > + chamelium_plug(data->chamelium, port); > > + chamelium_wait_for_conn_status_change(&data->display, data->chamelium, > > + port, DRM_MODE_CONNECTED); > > + > > + igt_display_reset(display); > > + > > + output = chamelium_get_output_for_port(data, port); > > + > > + /* Refresh pipe to update connected status */ > > + igt_output_set_pipe(output, PIPE_NONE); > > + > > + pipe = chamelium_get_pipe_for_output(display, output); > > + igt_output_set_pipe(output, pipe); > > + > > + return output; > > +} > > + > > +/** > > + * chamelium_enable_output: > > + * > > + * Modesets the connector attached to @port for the assigned @mode and draws the > > + * @fb. > > + * > > + */ > > +void chamelium_enable_output(chamelium_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 */ > > + if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_DEGAMMA_LUT)) > > + igt_pipe_obj_replace_prop_blob(primary->pipe, > > + IGT_CRTC_DEGAMMA_LUT, NULL, 0); > > + if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_GAMMA_LUT)) > > + igt_pipe_obj_replace_prop_blob(primary->pipe, > > + IGT_CRTC_GAMMA_LUT, NULL, 0); > > + if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_CTM)) > > + 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); > > +} > > + > > +/* Return pipe attached to @outpu.t */ > > +enum pipe chamelium_get_pipe_for_output(igt_display_t *display, > > + igt_output_t *output) > > +{ > > + enum pipe pipe; > > + > > + for_each_pipe(display, pipe) { > > + if (igt_pipe_connector_valid(pipe, output)) { > > + return pipe; > > + } > > + } > > + > > + igt_assert_f(false, "No pipe found for output %s\n", > > + igt_output_name(output)); > > +} > > + > > +static void chamelium_paint_xr24_pattern(uint32_t *data, size_t width, > > + size_t height, size_t stride, > > + size_t block_size) > > +{ > > + uint32_t colors[] = { 0xff000000, 0xffff0000, 0xff00ff00, 0xff0000ff, > > + 0xffffffff }; > > + unsigned i, j; > > + > > + for (i = 0; i < height; i++) > > + for (j = 0; j < width; j++) > > + *(data + i * stride / 4 + > > + j) = colors[((j / block_size) + (i / block_size)) % 5]; > > +} > > + > > +/** > > + * chamelium_get_pattern_fb: > > + * > > + * Creates an @fb with an xr24 pattern and returns the fb_id. > > + * > > + */ > > +int chamelium_get_pattern_fb(chamelium_data_t *data, size_t width, > > + size_t height, uint32_t fourcc, size_t block_size, > > + struct igt_fb *fb) > > +{ > > + int fb_id; > > + void *ptr; > > + > > + igt_assert(fourcc == DRM_FORMAT_XRGB8888); > > + > > + fb_id = igt_create_fb(data->drm_fd, width, height, fourcc, > > + DRM_FORMAT_MOD_LINEAR, fb); > > + igt_assert(fb_id > 0); > > + > > + ptr = igt_fb_map_buffer(fb->fd, fb); > > + igt_assert(ptr); > > + > > + chamelium_paint_xr24_pattern(ptr, width, height, fb->strides[0], > > + block_size); > > + igt_fb_unmap_buffer(fb, ptr); > > + > > + return fb_id; > > +} > > + > > +/* Generate a simple @fb for the size of @mode. */ > > +void chamelium_create_fb_for_mode(chamelium_data_t *data, struct igt_fb *fb, > > + drmModeModeInfo *mode) > > +{ > > + int fb_id; > > + > > + fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay, > > + DRM_FORMAT_XRGB8888, 64, fb); > > + > > + igt_assert(fb_id > 0); > > +} > > + > > +/* Returns the first preferred mode for the connector attached to @port. */ > > +drmModeModeInfo chamelium_get_mode_for_port(struct chamelium *chamelium, > > + struct chamelium_port *port) > > +{ > > + drmModeConnector *connector = > > + chamelium_port_get_connector(chamelium, port, false); > > + drmModeModeInfo mode; > > + igt_assert(&connector->modes[0] != NULL); > > + memcpy(&mode, &connector->modes[0], sizeof(mode)); > > + drmModeFreeConnector(connector); > > + return mode; > > +} > > + > > +/* Returns the igt display output for the connector attached to @port. */ > > +igt_output_t *chamelium_get_output_for_port(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + drmModeConnector *connector = > > + chamelium_port_get_connector(data->chamelium, port, true); > > + igt_output_t *output = > > + igt_output_from_connector(&data->display, connector); > > + drmModeFreeConnector(connector); > > + igt_assert(output != NULL); > > + return output; > > +} > > + > > +/* Set the EDID of index @edid to Chamelium's @port. */ > > +void chamelium_set_edid(chamelium_data_t *data, struct chamelium_port *port, > > + enum igt_custom_edid_type edid) > > +{ > > + chamelium_port_set_edid(data->chamelium, port, data->edids[edid]); > > +} > > + > > +/** > > + * chamelium_check_analog_bridge: > > + * > > + * Check if the connector associalted to @port is an analog bridge by checking > > + * if it has its own EDID. > > + * > > + */ > > +bool chamelium_check_analog_bridge(chamelium_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; > > + const struct edid *edid; > > + char edid_vendor[3]; > > + > > + if (chamelium_port_get_type(port) != DRM_MODE_CONNECTOR_VGA) { > > + drmModeFreeConnector(connector); > > + 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 = (const struct edid *)edid_blob->data; > > + edid_get_mfg(edid, edid_vendor); > > + > > + drmModeFreePropertyBlob(edid_blob); > > + drmModeFreeConnector(connector); > > + > > + /* Analog bridges provide their own EDID */ > > + if (edid_vendor[0] != 'I' || edid_vendor[1] != 'G' || > > + edid_vendor[2] != 'T') > > + return true; > > + > > + return false; > > +} > > \ No newline at end of file > > diff --git a/tests/chamelium/kms_chamelium_helper.h b/tests/chamelium/kms_chamelium_helper.h > > new file mode 100644 > > index 00000000..09fa4829 > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_helper.h > > @@ -0,0 +1,74 @@ > > +/* SPDX-License-Identifier: MIT */ > > +/* > > + * A helper library for all Chamelium tests. > > + * > > + * Copyright 2022 Google LLC. > > + * > > + * Authors: Mark Yacoub > > + */ > > + > > +#ifndef TESTS_CHAMELIUM_CHAMELIUM_HELPER_H > > +#define TESTS_CHAMELIUM_CHAMELIUM_HELPER_H > > + > > +#include "igt.h" > > + > > +#define ONLINE_TIMEOUT 20 /* seconds */ > > + > > +#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__) > > + > > +/* > > + * The chamelium data structure is used to store all the information known about > > + * chamelium to run the tests. > > + */ > > +typedef struct { > > + struct chamelium *chamelium; > > + struct chamelium_port **ports; > > + igt_display_t display; > > + int port_count; > > + > > + int drm_fd; > > + > > + struct chamelium_edid *edids[IGT_CUSTOM_EDID_COUNT]; > > +} chamelium_data_t; > > + > > +void chamelium_init_test(chamelium_data_t *data); > > + > > +bool chamelium_wait_for_hotplug(struct udev_monitor *mon, int *timeout); > > +void chamelium_wait_for_connector_after_hotplug(chamelium_data_t *data, > > + struct udev_monitor *mon, > > + struct chamelium_port *port, > > + drmModeConnection status); > > + > > +igt_output_t *chamelium_prepare_output(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_custom_edid_type edid); > > +void chamelium_enable_output(chamelium_data_t *data, > > + struct chamelium_port *port, igt_output_t *output, > > + drmModeModeInfo *mode, struct igt_fb *fb); > > +enum pipe chamelium_get_pipe_for_output(igt_display_t *display, > > + igt_output_t *output); > > + > > +int chamelium_get_pattern_fb(chamelium_data_t *data, size_t width, > > + size_t height, uint32_t fourcc, size_t block_size, > > + struct igt_fb *fb); > > +void chamelium_create_fb_for_mode(chamelium_data_t *data, struct igt_fb *fb, > > + drmModeModeInfo *mode); > > +drmModeModeInfo chamelium_get_mode_for_port(struct chamelium *chamelium, > > + struct chamelium_port *port); > > +igt_output_t *chamelium_get_output_for_port(chamelium_data_t *data, > > + struct chamelium_port *port); > > + > > +void chamelium_set_edid(chamelium_data_t *data, struct chamelium_port *port, > > + enum igt_custom_edid_type edid); > > + > > +bool chamelium_check_analog_bridge(chamelium_data_t *data, > > + struct chamelium_port *port); > > + > > +#endif /* TESTS_CHAMELIUM_CHAMELIUM_HELPER_H */ > > diff --git a/tests/chamelium/kms_chamelium_hpd.c b/tests/chamelium/kms_chamelium_hpd.c > > new file mode 100644 > > index 00000000..8a4e1aba > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_hpd.c > > @@ -0,0 +1,512 @@ > > +// SPDX-License-Identifier: MIT > > +/* > > + * A Chamelium test for testing the HPD functionality. > > + * > > + * Copyright 2022 Google LLC. > > + * > > + * Authors: Mark Yacoub > > + */ > > + > > +#include "kms_chamelium_helper.h" > > + > > +#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 > > + > > +enum test_modeset_mode { > > + TEST_MODESET_ON, > > + TEST_MODESET_ON_OFF, > > + TEST_MODESET_OFF, > > +}; > > + > > +static void try_suspend_resume_hpd(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_suspend_state state, > > + enum igt_suspend_test test, > > + struct udev_monitor *mon, bool connected) > > +{ > > + drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED : > > + DRM_MODE_CONNECTED; > > + int timeout = CHAMELIUM_HOTPLUG_TIMEOUT; > > + int delay; > > + int p; > > + > > + igt_flush_uevents(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(chamelium_wait_for_hotplug(mon, &timeout)); > > + chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT); > > + > > + if (port) { > > + igt_assert_eq(chamelium_reprobe_connector( > > + &data->display, data->chamelium, port), > > + target_state); > > + } else { > > + for (p = 0; p < data->port_count; p++) { > > + drmModeConnection current_state; > > + > > + port = data->ports[p]; > > + /* > > + * There could be as many hotplug events sent by > > + * driver as connectors we scheduled an HPD toggle on > > + * above, depending on timing. So if we're not seeing > > + * the expected connector state try to wait for an HPD > > + * event for each connector/port. > > + */ > > + current_state = chamelium_reprobe_connector( > > + &data->display, data->chamelium, port); > > + if (p > 0 && current_state != target_state) { > > + igt_assert(chamelium_wait_for_hotplug( > > + mon, &timeout)); > > + current_state = chamelium_reprobe_connector( > > + &data->display, data->chamelium, port); > > + } > > + > > + igt_assert_eq(current_state, target_state); > > + } > > + > > + port = NULL; > > + } > > +} > > + > > +static const char test_basic_hotplug_desc[] = > > + "Check that we get uevents and updated connector status on " > > + "hotplug and unplug"; > > +static void test_hotplug(chamelium_data_t *data, struct chamelium_port *port, > > + int toggle_count, enum test_modeset_mode modeset_mode) > > +{ > > + int i; > > + enum pipe pipe; > > + struct igt_fb fb = { 0 }; > > + drmModeModeInfo mode; > > + struct udev_monitor *mon = igt_watch_uevents(); > > + igt_output_t *output = chamelium_get_output_for_port(data, port); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, NULL, > > + data->ports, data->port_count); > > + > > + igt_hpd_storm_set_threshold(data->drm_fd, 0); > > + > > + for (i = 0; i < toggle_count; i++) { > > + igt_flush_uevents(mon); > > + > > + /* Check if we get a sysfs hotplug event */ > > + chamelium_plug(data->chamelium, port); > > + > > + chamelium_wait_for_connector_after_hotplug(data, mon, port, > > + DRM_MODE_CONNECTED); > > + igt_flush_uevents(mon); > > + > > + if (modeset_mode == TEST_MODESET_ON_OFF || > > + (modeset_mode == TEST_MODESET_ON && i == 0)) { > > + if (i == 0) { > > + /* We can only get mode and pipe once we are > > + * connected */ > > + output = chamelium_get_output_for_port(data, > > + port); > > + pipe = chamelium_get_pipe_for_output( > > + &data->display, output); > > + mode = chamelium_get_mode_for_port( > > + data->chamelium, port); > > + chamelium_create_fb_for_mode(data, &fb, &mode); > > + } > > + > > + igt_output_set_pipe(output, pipe); > > + chamelium_enable_output(data, port, output, &mode, &fb); > > + } > > + > > + /* Now check if we get a hotplug from disconnection */ > > + chamelium_unplug(data->chamelium, port); > > + > > + chamelium_wait_for_connector_after_hotplug( > > + data, mon, port, DRM_MODE_DISCONNECTED); > > + > > + igt_flush_uevents(mon); > > + > > + if (modeset_mode == TEST_MODESET_ON_OFF) { > > + igt_output_set_pipe(output, PIPE_NONE); > > + igt_display_commit2(&data->display, COMMIT_ATOMIC); > > + } > > + } > > + > > + igt_cleanup_uevents(mon); > > + igt_hpd_storm_reset(data->drm_fd); > > + igt_remove_fb(data->drm_fd, &fb); > > +} > > + > > +static const char test_hotplug_for_each_pipe_desc[] = > > + "Check that we get uevents and updated connector status on " > > + "hotplug and unplug for each pipe with valid output"; > > +static void test_hotplug_for_each_pipe(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + igt_output_t *output; > > + enum pipe pipe; > > + struct udev_monitor *mon = igt_watch_uevents(); > > + > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + igt_hpd_storm_set_threshold(data->drm_fd, 0); > > + /* Disconnect if any port got connected */ > > + chamelium_unplug(data->chamelium, port); > > + chamelium_wait_for_connector_after_hotplug(data, mon, port, > > + DRM_MODE_DISCONNECTED); > > + > > + for_each_pipe(&data->display, pipe) { > > + igt_flush_uevents(mon); > > + /* Check if we get a sysfs hotplug event */ > > + chamelium_plug(data->chamelium, port); > > + chamelium_wait_for_connector_after_hotplug(data, mon, port, > > + DRM_MODE_CONNECTED); > > + igt_flush_uevents(mon); > > + output = chamelium_get_output_for_port(data, port); > > + > > + /* If pipe is valid for output then set it */ > > + if (igt_pipe_connector_valid(pipe, output)) { > > + igt_output_set_pipe(output, pipe); > > + igt_display_commit2(&data->display, COMMIT_ATOMIC); > > + } > > + > > + chamelium_unplug(data->chamelium, port); > > + chamelium_wait_for_connector_after_hotplug( > > + data, mon, port, DRM_MODE_DISCONNECTED); > > + igt_flush_uevents(mon); > > + } > > + > > + igt_cleanup_uevents(mon); > > + igt_hpd_storm_reset(data->drm_fd); > > +} > > + > > +static const char test_suspend_resume_hpd_desc[] = > > + "Toggle HPD during suspend, check that uevents are sent and connector " > > + "status is updated"; > > +static void test_suspend_resume_hpd(chamelium_data_t *data, > > + struct chamelium_port *port, > > + enum igt_suspend_state state, > > + enum igt_suspend_test test) > > +{ > > + struct udev_monitor *mon = igt_watch_uevents(); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + /* 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_uevents(mon); > > +} > > + > > +static const char test_suspend_resume_hpd_common_desc[] = > > + "Toggle HPD during suspend on all connectors, check that uevents are " > > + "sent and connector status is updated"; > > +static void test_suspend_resume_hpd_common(chamelium_data_t *data, > > + enum igt_suspend_state state, > > + enum igt_suspend_test test) > > +{ > > + struct udev_monitor *mon = igt_watch_uevents(); > > + 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)); > > + } > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, NULL, > > + data->ports, data->port_count); > > + > > + /* 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_uevents(mon); > > +} > > + > > +static const char test_hpd_without_ddc_desc[] = > > + "Disable DDC on a VGA connector, check we still get a uevent on hotplug"; > > +static void test_hpd_without_ddc(chamelium_data_t *data, > > + struct chamelium_port *port) > > +{ > > + struct udev_monitor *mon = igt_watch_uevents(); > > + > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + igt_flush_uevents(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, CHAMELIUM_HOTPLUG_TIMEOUT)); > > + igt_assert_eq(chamelium_reprobe_connector(&data->display, > > + data->chamelium, port), > > + DRM_MODE_CONNECTED); > > + > > + igt_cleanup_uevents(mon); > > +} > > + > > +static const char test_hpd_storm_detect_desc[] = > > + "Trigger a series of hotplugs in a very small timeframe to simulate a" > > + "bad cable, check the kernel falls back to polling to avoid a hotplug " > > + "storm"; > > +static void test_hpd_storm_detect(chamelium_data_t *data, > > + struct chamelium_port *port, int width) > > +{ > > + struct udev_monitor *mon; > > + int count = 0; > > + > > + igt_require_hpd_storm_ctl(data->drm_fd); > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + 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_uevents(); > > + 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_uevents(mon); > > + igt_hpd_storm_reset(data->drm_fd); > > +} > > + > > +static const char test_hpd_storm_disable_desc[] = > > + "Disable HPD storm detection, trigger a storm and check the kernel " > > + "doesn't detect one"; > > +static void test_hpd_storm_disable(chamelium_data_t *data, > > + struct chamelium_port *port, int width) > > +{ > > + igt_require_hpd_storm_ctl(data->drm_fd); > > + igt_modeset_disable_all_outputs(&data->display); > > + chamelium_reset_state(&data->display, data->chamelium, port, > > + data->ports, data->port_count); > > + > > + 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); > > +} > > + > > +IGT_TEST_DESCRIPTION("Testing HPD with a Chamelium board"); > > +igt_main > > +{ > > + chamelium_data_t data; > > + struct chamelium_port *port; > > + int p; > > + > > + igt_fixture { > > + chamelium_init_test(&data); > > + } > > + > > + igt_describe("DisplayPort tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_DisplayPort, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("dp-hpd", DisplayPort) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI, > > + TEST_MODESET_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("dp-hpd-fast", DisplayPort) test_hotplug( > > + &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("dp-hpd-enable-disable-mode", DisplayPort) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > + TEST_MODESET_ON_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("dp-hpd-with-enabled-mode", DisplayPort) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > + TEST_MODESET_ON); > > + > > + igt_describe(test_hotplug_for_each_pipe_desc); > > + connector_subtest("dp-hpd-for-each-pipe", DisplayPort) > > + test_hotplug_for_each_pipe(&data, port); > > + > > + igt_describe(test_suspend_resume_hpd_desc); > > + connector_subtest("dp-hpd-after-suspend", DisplayPort) > > + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM, > > + SUSPEND_TEST_NONE); > > + > > + igt_describe(test_suspend_resume_hpd_desc); > > + connector_subtest("dp-hpd-after-hibernate", DisplayPort) > > + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK, > > + SUSPEND_TEST_DEVICES); > > + > > + igt_describe(test_hpd_storm_detect_desc); > > + connector_subtest("dp-hpd-storm", DisplayPort) > > + test_hpd_storm_detect(&data, port, > > + HPD_STORM_PULSE_INTERVAL_DP); > > + > > + igt_describe(test_hpd_storm_disable_desc); > > + connector_subtest("dp-hpd-storm-disable", DisplayPort) > > + test_hpd_storm_disable(&data, port, > > + HPD_STORM_PULSE_INTERVAL_DP); > > + } > > + > > + igt_describe("HDMI tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_HDMIA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("hdmi-hpd", HDMIA) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI, > > + TEST_MODESET_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("hdmi-hpd-fast", HDMIA) test_hotplug( > > + &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("hdmi-hpd-enable-disable-mode", HDMIA) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > + TEST_MODESET_ON_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("hdmi-hpd-with-enabled-mode", HDMIA) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > + TEST_MODESET_ON); > > + > > + igt_describe(test_hotplug_for_each_pipe_desc); > > + connector_subtest("hdmi-hpd-for-each-pipe", HDMIA) > > + test_hotplug_for_each_pipe(&data, port); > > + > > + igt_describe(test_suspend_resume_hpd_desc); > > + connector_subtest("hdmi-hpd-after-suspend", HDMIA) > > + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM, > > + SUSPEND_TEST_NONE); > > + > > + igt_describe(test_suspend_resume_hpd_desc); > > + connector_subtest("hdmi-hpd-after-hibernate", HDMIA) > > + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK, > > + SUSPEND_TEST_DEVICES); > > + > > + igt_describe(test_hpd_storm_detect_desc); > > + connector_subtest("hdmi-hpd-storm", HDMIA) > > + test_hpd_storm_detect(&data, port, > > + HPD_STORM_PULSE_INTERVAL_HDMI); > > + > > + igt_describe(test_hpd_storm_disable_desc); > > + connector_subtest("hdmi-hpd-storm-disable", HDMIA) > > + test_hpd_storm_disable(&data, port, > > + HPD_STORM_PULSE_INTERVAL_HDMI); > > + } > > + > > + igt_describe("VGA tests"); > > + igt_subtest_group { > > + igt_fixture { > > + chamelium_require_connector_present( > > + data.ports, DRM_MODE_CONNECTOR_VGA, > > + data.port_count, 1); > > + } > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("vga-hpd", VGA) test_hotplug( > > + &data, port, HPD_TOGGLE_COUNT_VGA, TEST_MODESET_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("vga-hpd-fast", VGA) test_hotplug( > > + &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("vga-hpd-enable-disable-mode", VGA) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > + TEST_MODESET_ON_OFF); > > + > > + igt_describe(test_basic_hotplug_desc); > > + connector_subtest("vga-hpd-with-enabled-mode", VGA) > > + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST, > > + TEST_MODESET_ON); > > + > > + igt_describe(test_suspend_resume_hpd_desc); > > + connector_subtest("vga-hpd-after-suspend", VGA) > > + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM, > > + SUSPEND_TEST_NONE); > > + > > + igt_describe(test_suspend_resume_hpd_desc); > > + connector_subtest("vga-hpd-after-hibernate", VGA) > > + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK, > > + SUSPEND_TEST_DEVICES); > > + > > + igt_describe(test_hpd_without_ddc_desc); > > + connector_subtest("vga-hpd-without-ddc", VGA) > > + test_hpd_without_ddc(&data, port); > > + } > > + > > + igt_describe("Tests that operate on all connectors"); > > + igt_subtest_group { > > + igt_fixture { > > + igt_require(data.port_count); > > + } > > + > > + igt_describe(test_suspend_resume_hpd_common_desc); > > + igt_subtest("common-hpd-after-suspend") > > + test_suspend_resume_hpd_common(&data, SUSPEND_STATE_MEM, > > + SUSPEND_TEST_NONE); > > + > > + igt_describe(test_suspend_resume_hpd_common_desc); > > + igt_subtest("common-hpd-after-hibernate") > > + test_suspend_resume_hpd_common(&data, > > + SUSPEND_STATE_DISK, > > + SUSPEND_TEST_DEVICES); > > + } > > + > > + igt_describe(test_hotplug_for_each_pipe_desc); > > + connector_subtest("vga-hpd-for-each-pipe", VGA) > > + test_hotplug_for_each_pipe(&data, port); > > + > > + igt_fixture { > > + igt_display_fini(&data.display); > > + close(data.drm_fd); > > + } > > +} > > diff --git a/tests/intel-ci/blacklist.txt b/tests/intel-ci/blacklist.txt > > index 0d307730..6e5cc436 100644 > > --- a/tests/intel-ci/blacklist.txt > > +++ b/tests/intel-ci/blacklist.txt > > @@ -77,7 +77,7 @@ igt@kms_frontbuffer_tracking@.*drrs.* > > # is too costly in comparison to the value > > # provided. > > ############################################### > > -igt@kms_chamelium@hdmi-.*-planes-random > > +igt@kms_chamelium_frames@hdmi-.*-planes-random > > ############################################### > > # Broadcom > > ############################################### > > diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist > > index f57f8ff3..fb4c0f73 100644 > > --- a/tests/intel-ci/fast-feedback.testlist > > +++ b/tests/intel-ci/fast-feedback.testlist > > @@ -92,14 +92,14 @@ igt@kms_addfb_basic@unused-modifier > > igt@kms_addfb_basic@unused-offsets > > igt@kms_addfb_basic@unused-pitches > > igt@kms_busy@basic > > -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_hpd@dp-hpd-fast > > +igt@kms_chamelium_edid@dp-edid-read > > +igt@kms_chamelium_frames@dp-crc-fast > > +igt@kms_chamelium_hpd@hdmi-hpd-fast > > +igt@kms_chamelium_edid@hdmi-edid-read > > +igt@kms_chamelium_frames@hdmi-crc-fast > > +igt@kms_chamelium_hpd@vga-hpd-fast > > +igt@kms_chamelium_edid@vga-edid-read > > igt@kms_prop_blob@basic > > igt@kms_cursor_legacy@basic-busy-flip-before-cursor > > igt@kms_cursor_legacy@basic-flip-after-cursor > > @@ -174,5 +174,5 @@ igt@i915_suspend@basic-s2idle-without-i915 > > igt@i915_suspend@basic-s3-without-i915 > > igt@gem_exec_suspend@basic-s0 > > igt@gem_exec_suspend@basic-s3 > > -igt@kms_chamelium@common-hpd-after-suspend > > +igt@kms_chamelium_hpd@common-hpd-after-suspend > > igt@kms_pipe_crc_basic@suspend-read-crc > > diff --git a/tests/kms_color_helper.h b/tests/kms_color_helper.h > > index f0ae30e3..f9242232 100644 > > --- a/tests/kms_color_helper.h > > +++ b/tests/kms_color_helper.h > > @@ -27,7 +27,7 @@ > > > > /* > > * This header is for code that is shared between kms_color.c and > > - * kms_color_chamelium.c. Reusability elsewhere can be questionable. > > + * kms_chamelium_color.c. Reusability elsewhere can be questionable. > > */ > > > > #include > > diff --git a/tests/meson.build b/tests/meson.build > > index 5c052e73..b52399d5 100644 > > --- a/tests/meson.build > > +++ b/tests/meson.build > > @@ -260,7 +260,10 @@ msm_progs = [ > > ] > > > > chamelium_progs = [ > > - 'kms_chamelium', > > + 'kms_chamelium_audio', > > + 'kms_chamelium_edid', > > + 'kms_chamelium_frames', > > + 'kms_chamelium_hpd', > > ] > > > > test_deps = [ igt_deps ] > > @@ -309,7 +312,8 @@ endforeach > > if chamelium.found() > > foreach prog : chamelium_progs > > test_executables += executable(prog, > > - join_paths('chamelium', prog + '.c'), > > + [join_paths('chamelium', prog + '.c'), > > + join_paths('chamelium', 'kms_chamelium_helper.c')], > > dependencies : test_deps, > > install_dir : libexecdir, > > install_rpath : libexecdir_rpathdir, > > @@ -436,13 +440,13 @@ test_executables += executable('kms_color', > > test_list += 'kms_color' > > > > if chamelium.found() > > - test_executables += executable('kms_color_chamelium', > > - [ 'chamelium/kms_color_chamelium.c', 'kms_color_helper.c' ], > > + test_executables += executable('kms_chamelium_color', > > + [ 'chamelium/kms_chamelium_color.c', 'kms_color_helper.c' ], > > dependencies : test_deps + [ chamelium ], > > install_dir : libexecdir, > > install_rpath : libexecdir_rpathdir, > > install : true) > > - test_list += 'kms_color_chamelium' > > + test_list += 'kms_chamelium_color' > > endif > > > > test_executables += executable('sw_sync', 'sw_sync.c', > > diff --git a/tests/vc4_ci/vc4-chamelium-fast.testlist b/tests/vc4_ci/vc4-chamelium-fast.testlist > > index dd45d12a..a5521021 100644 > > --- a/tests/vc4_ci/vc4-chamelium-fast.testlist > > +++ b/tests/vc4_ci/vc4-chamelium-fast.testlist > > @@ -1,14 +1,14 @@ > > -igt@kms_chamelium@hdmi-crc-abgr8888 > > -igt@kms_chamelium@hdmi-crc-argb1555 > > -igt@kms_chamelium@hdmi-crc-argb8888 > > -igt@kms_chamelium@hdmi-crc-bgr565 > > -igt@kms_chamelium@hdmi-crc-bgr888 > > -igt@kms_chamelium@hdmi-crc-fast > > -igt@kms_chamelium@hdmi-crc-rgb565 > > -igt@kms_chamelium@hdmi-crc-rgb888 > > -igt@kms_chamelium@hdmi-crc-xbgr8888 > > -igt@kms_chamelium@hdmi-crc-xrgb1555 > > -igt@kms_chamelium@hdmi-crc-xrgb8888 > > -igt@kms_chamelium@hdmi-edid-read > > -igt@kms_chamelium@hdmi-hpd > > -igt@kms_chamelium@hdmi-hpd-fast > > +igt@kms_chamelium_frames@hdmi-crc-abgr8888 > > +igt@kms_chamelium_frames@hdmi-crc-argb1555 > > +igt@kms_chamelium_frames@hdmi-crc-argb8888 > > +igt@kms_chamelium_frames@hdmi-crc-bgr565 > > +igt@kms_chamelium_frames@hdmi-crc-bgr888 > > +igt@kms_chamelium_frames@hdmi-crc-fast > > +igt@kms_chamelium_frames@hdmi-crc-rgb565 > > +igt@kms_chamelium_frames@hdmi-crc-rgb888 > > +igt@kms_chamelium_frames@hdmi-crc-xbgr8888 > > +igt@kms_chamelium_frames@hdmi-crc-xrgb1555 > > +igt@kms_chamelium_frames@hdmi-crc-xrgb8888 > > +igt@kms_chamelium_edid@hdmi-edid-read > > +igt@kms_chamelium_hpd@hdmi-hpd > > +igt@kms_chamelium_hpd@hdmi-hpd-fast > > -- > Jani Nikula, Intel Open Source Graphics Center