From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) by gabe.freedesktop.org (Postfix) with ESMTPS id 54B8910E5FD for ; Thu, 1 Dec 2022 14:49:13 +0000 (UTC) Date: Thu, 1 Dec 2022 16:49:25 +0200 From: Petri Latvala To: Mark Yacoub Message-ID: References: <20221130204526.3182975-1-markyacoub@chromium.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20221130204526.3182975-1-markyacoub@chromium.org> Subject: Re: [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: robdclark@chromium.org, khaled.almahallawy@intel.com, vsuley@google.com, igt-dev@lists.freedesktop.org, kalin@google.com, seanpaul@chromium.org, ihf@google.com, matthewtlam@google.com, markyacoub@google.com, amstan@chromium.org Errors-To: igt-dev-bounces@lists.freedesktop.org Sender: "igt-dev" List-ID: On Wed, Nov 30, 2022 at 03:45:26PM -0500, Mark Yacoub wrote: > [Why] > kms_chamelium tests file has grown so much and became a bit big to > manage. > Splitting specific tests like we do for kms_ tests into separate files > puts logically related functionalities into the same place so tests are > more clear. > > [How] > Split kms_chamelium into 4 different tests, each testing something > specific. The tests are: > 1. kms_chamelium_audio > 2. kms_chamelium_edid > 3. kms_chamelium_frames > 4. kms_chamelium_hpd > 5. kms_chamelium_color which used to be kms_color_chamelium but renamed > for consistency. > > All common code lives in kms_chamelium_helper and the function names > have a chamelium_ prefix. > > Signed-off-by: Mark Yacoub > --- > 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 | 532 +++ > 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, 3429 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..6c069112 > --- /dev/null > +++ b/tests/chamelium/kms_chamelium_edid.c > @@ -0,0 +1,532 @@ > +// SPDX-License-Identifier: MIT > +/* > + * A Chamelium test for testing the EDID functionality. > + * > + * Copyright 2022 Google LLC. > + * > + * Authors: Mark Yacoub > + */ > + > +#include "igt_chamelium.h" > +#include "igt_edid.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); > + 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 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..74e2524d 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_hdp@dp-hpd-fast > +igt@kms_chamelium_edid@dp-edid-read > +igt@kms_chamelium_frames@dp-crc-fast > +igt@kms_chamelium_hdp@hdmi-hpd-fast > +igt@kms_chamelium_edid@hdmi-edid-read > +igt@kms_chamelium_frames@hdmi-crc-fast > +igt@kms_chamelium_hdp@vga-hpd-fast > +igt@kms_chamelium_edid@vga-edid-read A couple of typos here, kms_chamelium_hdp written, kms_chamelium_hpd intended. -- Petri Latvala > 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 > -- > 2.38.1.584.g0f3c55d4c2-goog >