* [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests
@ 2023-01-03 17:15 Mark Yacoub
2023-01-03 18:45 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5) Patchwork
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Mark Yacoub @ 2023-01-03 17:15 UTC (permalink / raw)
To: igt-dev
Cc: robdclark, petri.latvala, ihf, amstan, seanpaul, matthewtlam,
markyacoub, khaled.almahallawy
[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 <markyacoub@chromium.org>
---
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 | 875 +++++
...olor_chamelium.c => kms_chamelium_color.c} | 0
tests/chamelium/kms_chamelium_edid.c | 560 +++
tests/chamelium/kms_chamelium_frames.c | 1085 ++++++
tests/chamelium/kms_chamelium_helper.c | 347 ++
tests/chamelium/kms_chamelium_helper.h | 91 +
tests/chamelium/kms_chamelium_hpd.c | 529 +++
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, 3525 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 <stdint.h>
+#include <stddef.h>
#include <xf86drmMode.h>
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 <stdbool.h>
+#include <stddef.h>
#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 <lyude@redhat.com>
- */
-
-#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 <fcntl.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdatomic.h>
-// #include <stdio.h>
-
-// 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..6c6177fc
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_audio.c
@@ -0,0 +1,875 @@
+/*
+ * 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 <lyude@redhat.com>
+ */
+
+#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..f7609932
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_edid.c
@@ -0,0 +1,560 @@
+/*
+ * 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 <lyude@redhat.com>
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdatomic.h>
+#include <xf86drmMode.h>
+
+#include "config.h"
+#include "igt.h"
+#include "igt_chamelium.h"
+#include "igt_edid.h"
+#include "igt_eld.h"
+#include "igt_vc4.h"
+#include "igt_infoframe.h"
+#include "kms_chamelium_helper.h"
+#include "monitor_edids/dp_edids.h"
+#include "monitor_edids/hdmi_edids.h"
+#include "monitor_edids/monitor_edids_helper.h"
+
+#define MODE_CLOCK_ACCURACY 0.05 /* 5% */
+
+static void get_connectors_link_status_failed(chamelium_data_t *data,
+ bool *link_status_failed)
+{
+ drmModeConnector *connector;
+ uint64_t link_status;
+ drmModePropertyPtr prop;
+ int p;
+
+ for (p = 0; p < data->port_count; p++) {
+ connector = chamelium_port_get_connector(data->chamelium,
+ data->ports[p], false);
+
+ igt_assert(kmstest_get_property(
+ data->drm_fd, connector->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR, "link-status", NULL,
+ &link_status, &prop));
+
+ link_status_failed[p] = link_status == DRM_MODE_LINK_STATUS_BAD;
+
+ drmModeFreeProperty(prop);
+ drmModeFreeConnector(connector);
+ }
+}
+
+static void check_mode(struct chamelium *chamelium, struct chamelium_port *port,
+ drmModeModeInfo *mode)
+{
+ struct chamelium_video_params video_params = { 0 };
+ double mode_clock;
+ int mode_hsync_offset, mode_vsync_offset;
+ int mode_hsync_width, mode_vsync_width;
+ int mode_hsync_polarity, mode_vsync_polarity;
+
+ chamelium_port_get_video_params(chamelium, port, &video_params);
+
+ mode_clock = (double)mode->clock / 1000;
+
+ if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_DisplayPort) {
+ /* this is what chamelium understands as offsets for DP */
+ mode_hsync_offset = mode->htotal - mode->hsync_start;
+ mode_vsync_offset = mode->vtotal - mode->vsync_start;
+ } else {
+ /* and this is what they are for other connectors */
+ mode_hsync_offset = mode->hsync_start - mode->hdisplay;
+ mode_vsync_offset = mode->vsync_start - mode->vdisplay;
+ }
+
+ mode_hsync_width = mode->hsync_end - mode->hsync_start;
+ mode_vsync_width = mode->vsync_end - mode->vsync_start;
+
+ mode_hsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC);
+ mode_vsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC);
+
+ igt_debug("Checking video mode:\n");
+ igt_debug("clock: got %f, expected %f ± %f%%\n", video_params.clock,
+ mode_clock, MODE_CLOCK_ACCURACY * 100);
+ igt_debug("hactive: got %d, expected %d\n", video_params.hactive,
+ mode->hdisplay);
+ igt_debug("vactive: got %d, expected %d\n", video_params.vactive,
+ mode->vdisplay);
+ igt_debug("hsync_offset: got %d, expected %d\n",
+ video_params.hsync_offset, mode_hsync_offset);
+ igt_debug("vsync_offset: got %d, expected %d\n",
+ video_params.vsync_offset, mode_vsync_offset);
+ igt_debug("htotal: got %d, expected %d\n", video_params.htotal,
+ mode->htotal);
+ igt_debug("vtotal: got %d, expected %d\n", video_params.vtotal,
+ mode->vtotal);
+ igt_debug("hsync_width: got %d, expected %d\n",
+ video_params.hsync_width, mode_hsync_width);
+ igt_debug("vsync_width: got %d, expected %d\n",
+ video_params.vsync_width, mode_vsync_width);
+ igt_debug("hsync_polarity: got %d, expected %d\n",
+ video_params.hsync_polarity, mode_hsync_polarity);
+ igt_debug("vsync_polarity: got %d, expected %d\n",
+ video_params.vsync_polarity, mode_vsync_polarity);
+
+ if (!isnan(video_params.clock)) {
+ igt_assert(video_params.clock >
+ mode_clock * (1 - MODE_CLOCK_ACCURACY));
+ igt_assert(video_params.clock <
+ mode_clock * (1 + MODE_CLOCK_ACCURACY));
+ }
+ igt_assert(video_params.hactive == mode->hdisplay);
+ igt_assert(video_params.vactive == mode->vdisplay);
+ igt_assert(video_params.hsync_offset == mode_hsync_offset);
+ igt_assert(video_params.vsync_offset == mode_vsync_offset);
+ igt_assert(video_params.htotal == mode->htotal);
+ igt_assert(video_params.vtotal == mode->vtotal);
+ igt_assert(video_params.hsync_width == mode_hsync_width);
+ igt_assert(video_params.vsync_width == mode_vsync_width);
+ igt_assert(video_params.hsync_polarity == mode_hsync_polarity);
+ igt_assert(video_params.vsync_polarity == mode_vsync_polarity);
+}
+
+static const char igt_custom_edid_type_read_desc[] =
+ "Make sure the EDID exposed by KMS is the same as the screen's";
+static void igt_custom_edid_type_read(chamelium_data_t *data,
+ struct chamelium_port *port,
+ enum igt_custom_edid_type edid)
+{
+ drmModePropertyBlobPtr edid_blob = NULL;
+ drmModeConnector *connector;
+ size_t raw_edid_size;
+ const struct edid *raw_edid;
+ uint64_t edid_blob_id;
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ chamelium_set_edid(data, port, edid);
+ chamelium_plug(data->chamelium, port);
+ chamelium_wait_for_conn_status_change(&data->display, data->chamelium,
+ port, DRM_MODE_CONNECTED);
+
+ igt_skip_on(chamelium_check_analog_bridge(data, port));
+
+ connector = chamelium_port_get_connector(data->chamelium, port, true);
+ igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL,
+ &edid_blob_id, NULL));
+ igt_assert(edid_blob_id != 0);
+ edid_blob = drmModeGetPropertyBlob(data->drm_fd, edid_blob_id);
+ igt_assert(edid_blob);
+
+ raw_edid = chamelium_edid_get_raw(data->edids[edid], port);
+ raw_edid_size = edid_get_size(raw_edid);
+ igt_assert(memcmp(raw_edid, edid_blob->data, raw_edid_size) == 0);
+
+ drmModeFreePropertyBlob(edid_blob);
+ drmModeFreeConnector(connector);
+}
+
+static const char igt_edid_stress_resolution_desc[] =
+ "Stress test the DUT by testing multiple EDIDs, one right after the other, "
+ "and ensure their validity by check the real screen resolution vs the "
+ "advertised mode resultion.";
+static void edid_stress_resolution(chamelium_data_t *data,
+ struct chamelium_port *port,
+ monitor_edid edids_list[],
+ size_t edids_list_len)
+{
+ int i;
+ struct chamelium *chamelium = data->chamelium;
+ struct udev_monitor *mon = igt_watch_uevents();
+
+ for (i = 0; i < edids_list_len; ++i) {
+ struct chamelium_edid *chamelium_edid;
+ drmModeModeInfo mode;
+ struct igt_fb fb = { 0 };
+ igt_output_t *output;
+ enum pipe pipe;
+ bool is_video_stable;
+ int screen_res_w, screen_res_h;
+
+ monitor_edid *edid = &edids_list[i];
+ igt_info("Testing out the EDID for %s\n",
+ monitor_edid_get_name(edid));
+
+ /* Getting and Setting the EDID on Chamelium. */
+ chamelium_edid =
+ get_chameleon_edid_from_monitor_edid(chamelium, edid);
+ chamelium_port_set_edid(data->chamelium, port, chamelium_edid);
+ free_chamelium_edid_from_monitor_edid(chamelium_edid);
+
+ igt_flush_uevents(mon);
+ chamelium_plug(chamelium, port);
+ chamelium_wait_for_connector_after_hotplug(data, mon, port,
+ DRM_MODE_CONNECTED);
+ igt_flush_uevents(mon);
+
+ /* Setting an output on the screen to turn it on. */
+ mode = chamelium_get_mode_for_port(chamelium, port);
+ chamelium_create_fb_for_mode(data, &fb, &mode);
+ output = chamelium_get_output_for_port(data, port);
+ pipe = chamelium_get_pipe_for_output(&data->display, output);
+ igt_output_set_pipe(output, pipe);
+ chamelium_enable_output(data, port, output, &mode, &fb);
+
+ /* Capture the screen resolution and verify. */
+ is_video_stable = chamelium_port_wait_video_input_stable(
+ chamelium, port, 5);
+ igt_assert(is_video_stable);
+
+ chamelium_port_get_resolution(chamelium, port, &screen_res_w,
+ &screen_res_h);
+ igt_assert(screen_res_w == fb.width);
+ igt_assert(screen_res_h == fb.height);
+
+ // Clean up
+ igt_remove_fb(data->drm_fd, &fb);
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_unplug(chamelium, port);
+ }
+
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+}
+
+static const char igt_edid_resolution_list_desc[] =
+ "Get an EDID with many modes of different configurations, set them on the screen and check the"
+ " screen resolution matches the mode resolution.";
+
+static void edid_resolution_list(chamelium_data_t *data,
+ struct chamelium_port *port)
+{
+ struct chamelium *chamelium = data->chamelium;
+ struct udev_monitor *mon = igt_watch_uevents();
+ drmModeConnector *connector;
+ drmModeModeInfoPtr modes;
+ int count_modes;
+ int i;
+ igt_output_t *output;
+ enum pipe pipe;
+
+ chamelium_unplug(chamelium, port);
+ chamelium_set_edid(data, port, IGT_CUSTOM_EDID_FULL);
+
+ igt_flush_uevents(mon);
+ chamelium_plug(chamelium, port);
+ chamelium_wait_for_connector_after_hotplug(data, mon, port,
+ DRM_MODE_CONNECTED);
+ igt_flush_uevents(mon);
+
+ connector = chamelium_port_get_connector(chamelium, port, true);
+ modes = connector->modes;
+ count_modes = connector->count_modes;
+
+ output = chamelium_get_output_for_port(data, port);
+ pipe = chamelium_get_pipe_for_output(&data->display, output);
+ igt_output_set_pipe(output, pipe);
+
+ for (i = 0; i < count_modes; ++i)
+ igt_debug("#%d %s %uHz\n", i, modes[i].name, modes[i].vrefresh);
+
+ for (i = 0; i < count_modes; ++i) {
+ struct igt_fb fb = { 0 };
+ bool is_video_stable;
+ int screen_res_w, screen_res_h;
+
+ igt_info("Testing #%d %s %uHz\n", i, modes[i].name,
+ modes[i].vrefresh);
+
+ /* Set the screen mode with the one we chose. */
+ chamelium_create_fb_for_mode(data, &fb, &modes[i]);
+ chamelium_enable_output(data, port, output, &modes[i], &fb);
+ is_video_stable = chamelium_port_wait_video_input_stable(
+ chamelium, port, 10);
+ igt_assert(is_video_stable);
+
+ chamelium_port_get_resolution(chamelium, port, &screen_res_w,
+ &screen_res_h);
+ igt_assert_eq(screen_res_w, modes[i].hdisplay);
+ igt_assert_eq(screen_res_h, modes[i].vdisplay);
+
+ igt_remove_fb(data->drm_fd, &fb);
+ }
+
+ igt_modeset_disable_all_outputs(&data->display);
+ drmModeFreeConnector(connector);
+}
+
+static const char test_suspend_resume_edid_change_desc[] =
+ "Simulate a screen being unplugged and another screen being plugged "
+ "during suspend, check that a uevent is sent and connector status is "
+ "updated";
+static void test_suspend_resume_edid_change(chamelium_data_t *data,
+ struct chamelium_port *port,
+ enum igt_suspend_state state,
+ enum igt_suspend_test test,
+ enum igt_custom_edid_type edid,
+ enum igt_custom_edid_type alt_edid)
+{
+ struct udev_monitor *mon = igt_watch_uevents();
+ bool link_status_failed[2][data->port_count];
+ int p;
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ /* Catch the event and flush all remaining ones. */
+ igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
+ igt_flush_uevents(mon);
+
+ /* First plug in the port */
+ chamelium_set_edid(data, port, edid);
+ chamelium_plug(data->chamelium, port);
+ igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
+
+ chamelium_wait_for_conn_status_change(&data->display, data->chamelium,
+ port, DRM_MODE_CONNECTED);
+
+ /*
+ * Change the edid before we suspend. On resume, the machine should
+ * notice the EDID change and fire a hotplug event.
+ */
+ chamelium_set_edid(data, port, alt_edid);
+
+ get_connectors_link_status_failed(data, link_status_failed[0]);
+
+ igt_flush_uevents(mon);
+
+ igt_system_suspend_autoresume(state, test);
+ igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
+ chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
+
+ get_connectors_link_status_failed(data, link_status_failed[1]);
+
+ for (p = 0; p < data->port_count; p++)
+ igt_skip_on(!link_status_failed[0][p] &&
+ link_status_failed[1][p]);
+}
+
+static const char test_mode_timings_desc[] =
+ "For each mode of the IGT base EDID, perform a modeset and check the "
+ "mode detected by the Chamelium receiver matches the mode we set";
+static void test_mode_timings(chamelium_data_t *data,
+ struct chamelium_port *port)
+{
+ int i, count_modes;
+
+ i = 0;
+ igt_require(chamelium_supports_get_video_params(data->chamelium));
+ do {
+ igt_output_t *output;
+ igt_plane_t *primary;
+ drmModeConnector *connector;
+ drmModeModeInfo *mode;
+ int fb_id;
+ struct igt_fb fb;
+
+ /*
+ * let's reset state each mode so we will get the
+ * HPD pulses realibably
+ */
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ /*
+ * modes may change due to mode pruining and link issues, so we
+ * need to refresh the connector
+ */
+ output = chamelium_prepare_output(data, port,
+ IGT_CUSTOM_EDID_BASE);
+ connector = chamelium_port_get_connector(data->chamelium, port,
+ false);
+ primary = igt_output_get_plane_type(output,
+ DRM_PLANE_TYPE_PRIMARY);
+ igt_assert(primary);
+
+ /* we may skip some modes due to above but that's ok */
+ count_modes = connector->count_modes;
+ if (i >= count_modes)
+ break;
+
+ mode = &connector->modes[i];
+
+ fb_id = igt_create_color_pattern_fb(
+ data->drm_fd, mode->hdisplay, mode->vdisplay,
+ DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, 0, 0, 0,
+ &fb);
+ igt_assert(fb_id > 0);
+
+ chamelium_enable_output(data, port, output, mode, &fb);
+
+ /* Trigger the FSM */
+ chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 0);
+
+ check_mode(data->chamelium, port, mode);
+
+ igt_remove_fb(data->drm_fd, &fb);
+ drmModeFreeConnector(connector);
+ } while (++i < count_modes);
+}
+
+IGT_TEST_DESCRIPTION("Testing EDID with a Chamelium board");
+igt_main
+{
+ chamelium_data_t data;
+ struct chamelium_port *port;
+ int p;
+
+ igt_fixture {
+ chamelium_init_test(&data);
+ }
+
+ igt_describe("DisplayPort tests");
+ igt_subtest_group {
+ igt_fixture {
+ chamelium_require_connector_present(
+ data.ports, DRM_MODE_CONNECTOR_DisplayPort,
+ data.port_count, 1);
+ }
+
+ igt_describe(igt_custom_edid_type_read_desc);
+ connector_subtest("dp-edid-read", DisplayPort)
+ {
+ igt_custom_edid_type_read(&data, port,
+ IGT_CUSTOM_EDID_BASE);
+ igt_custom_edid_type_read(&data, port,
+ IGT_CUSTOM_EDID_ALT);
+ }
+
+ igt_describe(igt_edid_stress_resolution_desc);
+ connector_subtest("dp-edid-stress-resolution-4k", DisplayPort)
+ edid_stress_resolution(&data, port, DP_EDIDS_4K,
+ ARRAY_SIZE(DP_EDIDS_4K));
+
+ igt_describe(igt_edid_stress_resolution_desc);
+ connector_subtest("dp-edid-stress-resolution-non-4k",
+ DisplayPort)
+ edid_stress_resolution(&data, port, DP_EDIDS_NON_4K,
+ ARRAY_SIZE(DP_EDIDS_NON_4K));
+
+ igt_describe(igt_edid_resolution_list_desc);
+ connector_subtest("dp-edid-resolution-list", DisplayPort)
+ edid_resolution_list(&data, port);
+
+ igt_describe(test_suspend_resume_edid_change_desc);
+ connector_subtest("dp-edid-change-during-suspend", DisplayPort)
+ test_suspend_resume_edid_change(&data, port,
+ SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE,
+ IGT_CUSTOM_EDID_BASE,
+ IGT_CUSTOM_EDID_ALT);
+
+ igt_describe(test_suspend_resume_edid_change_desc);
+ connector_subtest("dp-edid-change-during-hibernate",
+ DisplayPort)
+ test_suspend_resume_edid_change(&data, port,
+ SUSPEND_STATE_DISK,
+ SUSPEND_TEST_DEVICES,
+ IGT_CUSTOM_EDID_BASE,
+ IGT_CUSTOM_EDID_ALT);
+
+ igt_describe(test_mode_timings_desc);
+ connector_subtest("dp-mode-timings", DisplayPort)
+ test_mode_timings(&data, port);
+ }
+
+ igt_describe("HDMI tests");
+ igt_subtest_group {
+ igt_fixture {
+ chamelium_require_connector_present(
+ data.ports, DRM_MODE_CONNECTOR_HDMIA,
+ data.port_count, 1);
+ }
+
+ igt_describe(igt_custom_edid_type_read_desc);
+ connector_subtest("hdmi-edid-read", HDMIA)
+ {
+ igt_custom_edid_type_read(&data, port,
+ IGT_CUSTOM_EDID_BASE);
+ igt_custom_edid_type_read(&data, port,
+ IGT_CUSTOM_EDID_ALT);
+ }
+
+ igt_describe(igt_edid_stress_resolution_desc);
+ connector_subtest("hdmi-edid-stress-resolution-4k", HDMIA)
+ edid_stress_resolution(&data, port, HDMI_EDIDS_4K,
+ ARRAY_SIZE(HDMI_EDIDS_4K));
+
+ igt_describe(igt_edid_stress_resolution_desc);
+ connector_subtest("hdmi-edid-stress-resolution-non-4k", HDMIA)
+ edid_stress_resolution(&data, port, HDMI_EDIDS_NON_4K,
+ ARRAY_SIZE(HDMI_EDIDS_NON_4K));
+
+ igt_describe(test_suspend_resume_edid_change_desc);
+ connector_subtest("hdmi-edid-change-during-suspend", HDMIA)
+ test_suspend_resume_edid_change(&data, port,
+ SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE,
+ IGT_CUSTOM_EDID_BASE,
+ IGT_CUSTOM_EDID_ALT);
+
+ igt_describe(test_suspend_resume_edid_change_desc);
+ connector_subtest("hdmi-edid-change-during-hibernate", HDMIA)
+ test_suspend_resume_edid_change(&data, port,
+ SUSPEND_STATE_DISK,
+ SUSPEND_TEST_DEVICES,
+ IGT_CUSTOM_EDID_BASE,
+ IGT_CUSTOM_EDID_ALT);
+
+ igt_describe(test_mode_timings_desc);
+ connector_subtest("hdmi-mode-timings", HDMIA)
+ test_mode_timings(&data, port);
+ }
+
+ igt_describe("VGA tests");
+ igt_subtest_group {
+ igt_fixture {
+ chamelium_require_connector_present(
+ data.ports, DRM_MODE_CONNECTOR_VGA,
+ data.port_count, 1);
+ }
+
+ igt_describe(igt_custom_edid_type_read_desc);
+ connector_subtest("vga-edid-read", VGA)
+ {
+ igt_custom_edid_type_read(&data, port,
+ IGT_CUSTOM_EDID_BASE);
+ igt_custom_edid_type_read(&data, port,
+ IGT_CUSTOM_EDID_ALT);
+ }
+ }
+
+ igt_fixture {
+ igt_display_fini(&data.display);
+ close(data.drm_fd);
+ }
+}
diff --git a/tests/chamelium/kms_chamelium_frames.c b/tests/chamelium/kms_chamelium_frames.c
new file mode 100644
index 00000000..008bc34b
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_frames.c
@@ -0,0 +1,1085 @@
+/*
+ * Copyright © 2016 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Lyude Paul <lyude@redhat.com>
+ */
+
+#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..197d29be
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_helper.c
@@ -0,0 +1,347 @@
+/*
+ * 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 <lyude@redhat.com>
+ */
+
+#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..6dbde8b7
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_helper.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <lyude@redhat.com>
+ */
+
+#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..d9718d76
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_hpd.c
@@ -0,0 +1,529 @@
+/*
+ * 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 <lyude@redhat.com>
+ */
+
+#include "kms_chamelium_helper.h"
+
+#define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */
+#define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */
+
+#define HPD_TOGGLE_COUNT_VGA 5
+#define HPD_TOGGLE_COUNT_DP_HDMI 15
+#define HPD_TOGGLE_COUNT_FAST 3
+
+enum test_modeset_mode {
+ TEST_MODESET_ON,
+ TEST_MODESET_ON_OFF,
+ TEST_MODESET_OFF,
+};
+
+static void try_suspend_resume_hpd(chamelium_data_t *data,
+ struct chamelium_port *port,
+ enum igt_suspend_state state,
+ enum igt_suspend_test test,
+ struct udev_monitor *mon, bool connected)
+{
+ drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED :
+ DRM_MODE_CONNECTED;
+ int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
+ int delay;
+ int p;
+
+ igt_flush_uevents(mon);
+
+ delay = igt_get_autoresume_delay(state) * 1000 / 2;
+
+ if (port) {
+ chamelium_schedule_hpd_toggle(data->chamelium, port, delay,
+ !connected);
+ } else {
+ for (p = 0; p < data->port_count; p++) {
+ port = data->ports[p];
+ chamelium_schedule_hpd_toggle(data->chamelium, port,
+ delay, !connected);
+ }
+
+ port = NULL;
+ }
+
+ igt_system_suspend_autoresume(state, test);
+ igt_assert(chamelium_wait_for_hotplug(mon, &timeout));
+ chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
+
+ if (port) {
+ igt_assert_eq(chamelium_reprobe_connector(
+ &data->display, data->chamelium, port),
+ target_state);
+ } else {
+ for (p = 0; p < data->port_count; p++) {
+ drmModeConnection current_state;
+
+ port = data->ports[p];
+ /*
+ * There could be as many hotplug events sent by
+ * driver as connectors we scheduled an HPD toggle on
+ * above, depending on timing. So if we're not seeing
+ * the expected connector state try to wait for an HPD
+ * event for each connector/port.
+ */
+ current_state = chamelium_reprobe_connector(
+ &data->display, data->chamelium, port);
+ if (p > 0 && current_state != target_state) {
+ igt_assert(chamelium_wait_for_hotplug(
+ mon, &timeout));
+ current_state = chamelium_reprobe_connector(
+ &data->display, data->chamelium, port);
+ }
+
+ igt_assert_eq(current_state, target_state);
+ }
+
+ port = NULL;
+ }
+}
+
+static const char test_basic_hotplug_desc[] =
+ "Check that we get uevents and updated connector status on "
+ "hotplug and unplug";
+static void test_hotplug(chamelium_data_t *data, struct chamelium_port *port,
+ int toggle_count, enum test_modeset_mode modeset_mode)
+{
+ int i;
+ enum pipe pipe;
+ struct igt_fb fb = { 0 };
+ drmModeModeInfo mode;
+ struct udev_monitor *mon = igt_watch_uevents();
+ igt_output_t *output = chamelium_get_output_for_port(data, port);
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, NULL,
+ data->ports, data->port_count);
+
+ igt_hpd_storm_set_threshold(data->drm_fd, 0);
+
+ for (i = 0; i < toggle_count; i++) {
+ igt_flush_uevents(mon);
+
+ /* Check if we get a sysfs hotplug event */
+ chamelium_plug(data->chamelium, port);
+
+ chamelium_wait_for_connector_after_hotplug(data, mon, port,
+ DRM_MODE_CONNECTED);
+ igt_flush_uevents(mon);
+
+ if (modeset_mode == TEST_MODESET_ON_OFF ||
+ (modeset_mode == TEST_MODESET_ON && i == 0)) {
+ if (i == 0) {
+ /* We can only get mode and pipe once we are
+ * connected */
+ output = chamelium_get_output_for_port(data,
+ port);
+ pipe = chamelium_get_pipe_for_output(
+ &data->display, output);
+ mode = chamelium_get_mode_for_port(
+ data->chamelium, port);
+ chamelium_create_fb_for_mode(data, &fb, &mode);
+ }
+
+ igt_output_set_pipe(output, pipe);
+ chamelium_enable_output(data, port, output, &mode, &fb);
+ }
+
+ /* Now check if we get a hotplug from disconnection */
+ chamelium_unplug(data->chamelium, port);
+
+ chamelium_wait_for_connector_after_hotplug(
+ data, mon, port, DRM_MODE_DISCONNECTED);
+
+ igt_flush_uevents(mon);
+
+ if (modeset_mode == TEST_MODESET_ON_OFF) {
+ igt_output_set_pipe(output, PIPE_NONE);
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+ }
+ }
+
+ igt_cleanup_uevents(mon);
+ igt_hpd_storm_reset(data->drm_fd);
+ igt_remove_fb(data->drm_fd, &fb);
+}
+
+static const char test_hotplug_for_each_pipe_desc[] =
+ "Check that we get uevents and updated connector status on "
+ "hotplug and unplug for each pipe with valid output";
+static void test_hotplug_for_each_pipe(chamelium_data_t *data,
+ struct chamelium_port *port)
+{
+ igt_output_t *output;
+ enum pipe pipe;
+ struct udev_monitor *mon = igt_watch_uevents();
+
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ igt_hpd_storm_set_threshold(data->drm_fd, 0);
+ /* Disconnect if any port got connected */
+ chamelium_unplug(data->chamelium, port);
+ chamelium_wait_for_connector_after_hotplug(data, mon, port,
+ DRM_MODE_DISCONNECTED);
+
+ for_each_pipe(&data->display, pipe) {
+ igt_flush_uevents(mon);
+ /* Check if we get a sysfs hotplug event */
+ chamelium_plug(data->chamelium, port);
+ chamelium_wait_for_connector_after_hotplug(data, mon, port,
+ DRM_MODE_CONNECTED);
+ igt_flush_uevents(mon);
+ output = chamelium_get_output_for_port(data, port);
+
+ /* If pipe is valid for output then set it */
+ if (igt_pipe_connector_valid(pipe, output)) {
+ igt_output_set_pipe(output, pipe);
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+ }
+
+ chamelium_unplug(data->chamelium, port);
+ chamelium_wait_for_connector_after_hotplug(
+ data, mon, port, DRM_MODE_DISCONNECTED);
+ igt_flush_uevents(mon);
+ }
+
+ igt_cleanup_uevents(mon);
+ igt_hpd_storm_reset(data->drm_fd);
+}
+
+static const char test_suspend_resume_hpd_desc[] =
+ "Toggle HPD during suspend, check that uevents are sent and connector "
+ "status is updated";
+static void test_suspend_resume_hpd(chamelium_data_t *data,
+ struct chamelium_port *port,
+ enum igt_suspend_state state,
+ enum igt_suspend_test test)
+{
+ struct udev_monitor *mon = igt_watch_uevents();
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ /* Make sure we notice new connectors after resuming */
+ try_suspend_resume_hpd(data, port, state, test, mon, false);
+
+ /* Now make sure we notice disconnected connectors after resuming */
+ try_suspend_resume_hpd(data, port, state, test, mon, true);
+
+ igt_cleanup_uevents(mon);
+}
+
+static const char test_suspend_resume_hpd_common_desc[] =
+ "Toggle HPD during suspend on all connectors, check that uevents are "
+ "sent and connector status is updated";
+static void test_suspend_resume_hpd_common(chamelium_data_t *data,
+ enum igt_suspend_state state,
+ enum igt_suspend_test test)
+{
+ struct udev_monitor *mon = igt_watch_uevents();
+ struct chamelium_port *port;
+ int p;
+
+ for (p = 0; p < data->port_count; p++) {
+ port = data->ports[p];
+ igt_debug("Testing port %s\n", chamelium_port_get_name(port));
+ }
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, NULL,
+ data->ports, data->port_count);
+
+ /* Make sure we notice new connectors after resuming */
+ try_suspend_resume_hpd(data, NULL, state, test, mon, false);
+
+ /* Now make sure we notice disconnected connectors after resuming */
+ try_suspend_resume_hpd(data, NULL, state, test, mon, true);
+
+ igt_cleanup_uevents(mon);
+}
+
+static const char test_hpd_without_ddc_desc[] =
+ "Disable DDC on a VGA connector, check we still get a uevent on hotplug";
+static void test_hpd_without_ddc(chamelium_data_t *data,
+ struct chamelium_port *port)
+{
+ struct udev_monitor *mon = igt_watch_uevents();
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+ igt_flush_uevents(mon);
+
+ /* Disable the DDC on the connector and make sure we still get a
+ * hotplug
+ */
+ chamelium_port_set_ddc_state(data->chamelium, port, false);
+ chamelium_plug(data->chamelium, port);
+
+ igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
+ igt_assert_eq(chamelium_reprobe_connector(&data->display,
+ data->chamelium, port),
+ DRM_MODE_CONNECTED);
+
+ igt_cleanup_uevents(mon);
+}
+
+static const char test_hpd_storm_detect_desc[] =
+ "Trigger a series of hotplugs in a very small timeframe to simulate a"
+ "bad cable, check the kernel falls back to polling to avoid a hotplug "
+ "storm";
+static void test_hpd_storm_detect(chamelium_data_t *data,
+ struct chamelium_port *port, int width)
+{
+ struct udev_monitor *mon;
+ int count = 0;
+
+ igt_require_hpd_storm_ctl(data->drm_fd);
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ igt_hpd_storm_set_threshold(data->drm_fd, 1);
+ chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
+ igt_assert(igt_hpd_storm_detected(data->drm_fd));
+
+ mon = igt_watch_uevents();
+ chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
+
+ /*
+ * Polling should have been enabled by the HPD storm at this point,
+ * so we should only get at most 1 hotplug event
+ */
+ igt_until_timeout(5)
+ count += igt_hotplug_detected(mon, 1);
+ igt_assert_lt(count, 2);
+
+ igt_cleanup_uevents(mon);
+ igt_hpd_storm_reset(data->drm_fd);
+}
+
+static const char test_hpd_storm_disable_desc[] =
+ "Disable HPD storm detection, trigger a storm and check the kernel "
+ "doesn't detect one";
+static void test_hpd_storm_disable(chamelium_data_t *data,
+ struct chamelium_port *port, int width)
+{
+ igt_require_hpd_storm_ctl(data->drm_fd);
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ igt_hpd_storm_set_threshold(data->drm_fd, 0);
+ chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
+ igt_assert(!igt_hpd_storm_detected(data->drm_fd));
+
+ igt_hpd_storm_reset(data->drm_fd);
+}
+
+IGT_TEST_DESCRIPTION("Testing HPD with a Chamelium board");
+igt_main
+{
+ chamelium_data_t data;
+ struct chamelium_port *port;
+ int p;
+
+ igt_fixture {
+ chamelium_init_test(&data);
+ }
+
+ igt_describe("DisplayPort tests");
+ igt_subtest_group {
+ igt_fixture {
+ chamelium_require_connector_present(
+ data.ports, DRM_MODE_CONNECTOR_DisplayPort,
+ data.port_count, 1);
+ }
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("dp-hpd", DisplayPort)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI,
+ TEST_MODESET_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("dp-hpd-fast", DisplayPort) test_hotplug(
+ &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("dp-hpd-enable-disable-mode", DisplayPort)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+ TEST_MODESET_ON_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("dp-hpd-with-enabled-mode", DisplayPort)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+ TEST_MODESET_ON);
+
+ igt_describe(test_hotplug_for_each_pipe_desc);
+ connector_subtest("dp-hpd-for-each-pipe", DisplayPort)
+ test_hotplug_for_each_pipe(&data, port);
+
+ igt_describe(test_suspend_resume_hpd_desc);
+ connector_subtest("dp-hpd-after-suspend", DisplayPort)
+ test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE);
+
+ igt_describe(test_suspend_resume_hpd_desc);
+ connector_subtest("dp-hpd-after-hibernate", DisplayPort)
+ test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
+ SUSPEND_TEST_DEVICES);
+
+ igt_describe(test_hpd_storm_detect_desc);
+ connector_subtest("dp-hpd-storm", DisplayPort)
+ test_hpd_storm_detect(&data, port,
+ HPD_STORM_PULSE_INTERVAL_DP);
+
+ igt_describe(test_hpd_storm_disable_desc);
+ connector_subtest("dp-hpd-storm-disable", DisplayPort)
+ test_hpd_storm_disable(&data, port,
+ HPD_STORM_PULSE_INTERVAL_DP);
+ }
+
+ igt_describe("HDMI tests");
+ igt_subtest_group {
+ igt_fixture {
+ chamelium_require_connector_present(
+ data.ports, DRM_MODE_CONNECTOR_HDMIA,
+ data.port_count, 1);
+ }
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("hdmi-hpd", HDMIA)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI,
+ TEST_MODESET_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("hdmi-hpd-fast", HDMIA) test_hotplug(
+ &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("hdmi-hpd-enable-disable-mode", HDMIA)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+ TEST_MODESET_ON_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("hdmi-hpd-with-enabled-mode", HDMIA)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+ TEST_MODESET_ON);
+
+ igt_describe(test_hotplug_for_each_pipe_desc);
+ connector_subtest("hdmi-hpd-for-each-pipe", HDMIA)
+ test_hotplug_for_each_pipe(&data, port);
+
+ igt_describe(test_suspend_resume_hpd_desc);
+ connector_subtest("hdmi-hpd-after-suspend", HDMIA)
+ test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE);
+
+ igt_describe(test_suspend_resume_hpd_desc);
+ connector_subtest("hdmi-hpd-after-hibernate", HDMIA)
+ test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
+ SUSPEND_TEST_DEVICES);
+
+ igt_describe(test_hpd_storm_detect_desc);
+ connector_subtest("hdmi-hpd-storm", HDMIA)
+ test_hpd_storm_detect(&data, port,
+ HPD_STORM_PULSE_INTERVAL_HDMI);
+
+ igt_describe(test_hpd_storm_disable_desc);
+ connector_subtest("hdmi-hpd-storm-disable", HDMIA)
+ test_hpd_storm_disable(&data, port,
+ HPD_STORM_PULSE_INTERVAL_HDMI);
+ }
+
+ igt_describe("VGA tests");
+ igt_subtest_group {
+ igt_fixture {
+ chamelium_require_connector_present(
+ data.ports, DRM_MODE_CONNECTOR_VGA,
+ data.port_count, 1);
+ }
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("vga-hpd", VGA) test_hotplug(
+ &data, port, HPD_TOGGLE_COUNT_VGA, TEST_MODESET_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("vga-hpd-fast", VGA) test_hotplug(
+ &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("vga-hpd-enable-disable-mode", VGA)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+ TEST_MODESET_ON_OFF);
+
+ igt_describe(test_basic_hotplug_desc);
+ connector_subtest("vga-hpd-with-enabled-mode", VGA)
+ test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+ TEST_MODESET_ON);
+
+ igt_describe(test_suspend_resume_hpd_desc);
+ connector_subtest("vga-hpd-after-suspend", VGA)
+ test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE);
+
+ igt_describe(test_suspend_resume_hpd_desc);
+ connector_subtest("vga-hpd-after-hibernate", VGA)
+ test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
+ SUSPEND_TEST_DEVICES);
+
+ igt_describe(test_hpd_without_ddc_desc);
+ connector_subtest("vga-hpd-without-ddc", VGA)
+ test_hpd_without_ddc(&data, port);
+ }
+
+ igt_describe("Tests that operate on all connectors");
+ igt_subtest_group {
+ igt_fixture {
+ igt_require(data.port_count);
+ }
+
+ igt_describe(test_suspend_resume_hpd_common_desc);
+ igt_subtest("common-hpd-after-suspend")
+ test_suspend_resume_hpd_common(&data, SUSPEND_STATE_MEM,
+ SUSPEND_TEST_NONE);
+
+ igt_describe(test_suspend_resume_hpd_common_desc);
+ igt_subtest("common-hpd-after-hibernate")
+ test_suspend_resume_hpd_common(&data,
+ SUSPEND_STATE_DISK,
+ SUSPEND_TEST_DEVICES);
+ }
+
+ igt_describe(test_hotplug_for_each_pipe_desc);
+ connector_subtest("vga-hpd-for-each-pipe", VGA)
+ test_hotplug_for_each_pipe(&data, port);
+
+ igt_fixture {
+ igt_display_fini(&data.display);
+ close(data.drm_fd);
+ }
+}
diff --git a/tests/intel-ci/blacklist.txt b/tests/intel-ci/blacklist.txt
index 0d307730..6e5cc436 100644
--- a/tests/intel-ci/blacklist.txt
+++ b/tests/intel-ci/blacklist.txt
@@ -77,7 +77,7 @@ igt@kms_frontbuffer_tracking@.*drrs.*
# is too costly in comparison to the value
# provided.
###############################################
-igt@kms_chamelium@hdmi-.*-planes-random
+igt@kms_chamelium_frames@hdmi-.*-planes-random
###############################################
# Broadcom
###############################################
diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist
index f57f8ff3..fb4c0f73 100644
--- a/tests/intel-ci/fast-feedback.testlist
+++ b/tests/intel-ci/fast-feedback.testlist
@@ -92,14 +92,14 @@ igt@kms_addfb_basic@unused-modifier
igt@kms_addfb_basic@unused-offsets
igt@kms_addfb_basic@unused-pitches
igt@kms_busy@basic
-igt@kms_chamelium@dp-hpd-fast
-igt@kms_chamelium@dp-edid-read
-igt@kms_chamelium@dp-crc-fast
-igt@kms_chamelium@hdmi-hpd-fast
-igt@kms_chamelium@hdmi-edid-read
-igt@kms_chamelium@hdmi-crc-fast
-igt@kms_chamelium@vga-hpd-fast
-igt@kms_chamelium@vga-edid-read
+igt@kms_chamelium_hpd@dp-hpd-fast
+igt@kms_chamelium_edid@dp-edid-read
+igt@kms_chamelium_frames@dp-crc-fast
+igt@kms_chamelium_hpd@hdmi-hpd-fast
+igt@kms_chamelium_edid@hdmi-edid-read
+igt@kms_chamelium_frames@hdmi-crc-fast
+igt@kms_chamelium_hpd@vga-hpd-fast
+igt@kms_chamelium_edid@vga-edid-read
igt@kms_prop_blob@basic
igt@kms_cursor_legacy@basic-busy-flip-before-cursor
igt@kms_cursor_legacy@basic-flip-after-cursor
@@ -174,5 +174,5 @@ igt@i915_suspend@basic-s2idle-without-i915
igt@i915_suspend@basic-s3-without-i915
igt@gem_exec_suspend@basic-s0
igt@gem_exec_suspend@basic-s3
-igt@kms_chamelium@common-hpd-after-suspend
+igt@kms_chamelium_hpd@common-hpd-after-suspend
igt@kms_pipe_crc_basic@suspend-read-crc
diff --git a/tests/kms_color_helper.h b/tests/kms_color_helper.h
index f0ae30e3..f9242232 100644
--- a/tests/kms_color_helper.h
+++ b/tests/kms_color_helper.h
@@ -27,7 +27,7 @@
/*
* This header is for code that is shared between kms_color.c and
- * kms_color_chamelium.c. Reusability elsewhere can be questionable.
+ * kms_chamelium_color.c. Reusability elsewhere can be questionable.
*/
#include <math.h>
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.39.0.314.g84b9a713c41-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5)
2023-01-03 17:15 [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Mark Yacoub
@ 2023-01-03 18:45 ` Patchwork
2023-01-03 19:06 ` Mark Yacoub
2023-01-04 12:06 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6) Patchwork
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Patchwork @ 2023-01-03 18:45 UTC (permalink / raw)
To: Mark Yacoub; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 17957 bytes --]
== Series Details ==
Series: Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5)
URL : https://patchwork.freedesktop.org/series/111501/
State : failure
== Summary ==
CI Bug Log - changes from CI_DRM_12541 -> IGTPW_8293
====================================================
Summary
-------
**FAILURE**
Serious unknown changes coming with IGTPW_8293 absolutely need to be
verified manually.
If you think the reported changes have nothing to do with the changes
introduced in IGTPW_8293, please notify your bug team to allow them
to document this new failure mode, which will reduce false positives in CI.
External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/index.html
Participating hosts (42 -> 42)
------------------------------
Additional (1): fi-rkl-11600
Missing (1): fi-pnv-d510
Possible new issues
-------------------
Here are the unknown changes that may have been introduced in IGTPW_8293:
### IGT changes ###
#### Possible regressions ####
* igt@debugfs_test@read_all_entries:
- fi-icl-u2: [PASS][1] -> [ABORT][2]
[1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-icl-u2/igt@debugfs_test@read_all_entries.html
[2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-icl-u2/igt@debugfs_test@read_all_entries.html
* {igt@kms_chamelium_edid@dp-edid-read} (NEW):
- {fi-jsl-1}: NOTRUN -> [SKIP][3] +8 similar issues
[3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-jsl-1/igt@kms_chamelium_edid@dp-edid-read.html
- {bat-rpls-1}: NOTRUN -> [SKIP][4] +7 similar issues
[4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-rpls-1/igt@kms_chamelium_edid@dp-edid-read.html
* {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
- fi-adl-ddr5: NOTRUN -> [SKIP][5] +8 similar issues
[5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-adl-ddr5/igt@kms_chamelium_edid@hdmi-edid-read.html
- {fi-ehl-2}: NOTRUN -> [SKIP][6] +8 similar issues
[6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-ehl-2/igt@kms_chamelium_edid@hdmi-edid-read.html
- {bat-rpls-2}: NOTRUN -> [SKIP][7] +8 similar issues
[7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-rpls-2/igt@kms_chamelium_edid@hdmi-edid-read.html
- bat-dg1-6: NOTRUN -> [SKIP][8] +8 similar issues
[8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-6/igt@kms_chamelium_edid@hdmi-edid-read.html
* {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
- {bat-adln-1}: NOTRUN -> [SKIP][9] +8 similar issues
[9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adln-1/igt@kms_chamelium_frames@hdmi-crc-fast.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- {bat-adlp-9}: NOTRUN -> [SKIP][10] +8 similar issues
[10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-9/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
- {bat-dg2-oem1}: NOTRUN -> [SKIP][11] +8 similar issues
[11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-oem1/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
- fi-rkl-11600: NOTRUN -> [SKIP][12] +7 similar issues
[12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_chamelium_hpd@dp-hpd-fast.html
- bat-dg1-5: NOTRUN -> [SKIP][13] +8 similar issues
[13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-5/igt@kms_chamelium_hpd@dp-hpd-fast.html
- {bat-dg1-7}: NOTRUN -> [SKIP][14] +8 similar issues
[14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-7/igt@kms_chamelium_hpd@dp-hpd-fast.html
- {bat-dg2-9}: NOTRUN -> [SKIP][15] +8 similar issues
[15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-9/igt@kms_chamelium_hpd@dp-hpd-fast.html
* {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
- {bat-adlp-6}: NOTRUN -> [SKIP][16] +8 similar issues
[16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-6/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
* {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
- {bat-jsl-3}: NOTRUN -> [SKIP][17] +8 similar issues
[17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-jsl-3/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-rkl-guc: NOTRUN -> [SKIP][18] +8 similar issues
[18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-dg2-11}: NOTRUN -> [SKIP][19] +8 similar issues
[19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-11/igt@kms_chamelium_hpd@vga-hpd-fast.html
- bat-adlp-4: NOTRUN -> [SKIP][20] +8 similar issues
[20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-4/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-dg2-8}: NOTRUN -> [SKIP][21] +8 similar issues
[21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-8/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-adlm-1}: NOTRUN -> [SKIP][22] +8 similar issues
[22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html
New tests
---------
New tests have been introduced between CI_DRM_12541 and IGTPW_8293:
### New IGT tests (9) ###
* igt@kms_chamelium_edid@dp-edid-read:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-read:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@vga-edid-read:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-crc-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-fast:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@common-hpd-after-suspend:
- Statuses : 1 pass(s) 36 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-fast:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
Known issues
------------
Here are the changes found in IGTPW_8293 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@debugfs_test@basic-hwmon:
- fi-rkl-11600: NOTRUN -> [SKIP][23] ([i915#7456])
[23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@debugfs_test@basic-hwmon.html
* igt@gem_huc_copy@huc-copy:
- fi-rkl-11600: NOTRUN -> [SKIP][24] ([i915#2190])
[24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@gem_huc_copy@huc-copy.html
* igt@gem_lmem_swapping@basic:
- fi-rkl-11600: NOTRUN -> [SKIP][25] ([i915#4613]) +3 similar issues
[25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@gem_lmem_swapping@basic.html
* igt@gem_tiled_pread_basic:
- fi-rkl-11600: NOTRUN -> [SKIP][26] ([i915#3282])
[26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@gem_tiled_pread_basic.html
* igt@i915_pm_backlight@basic-brightness:
- fi-rkl-11600: NOTRUN -> [SKIP][27] ([i915#7561])
[27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@i915_pm_backlight@basic-brightness.html
* igt@i915_suspend@basic-s3-without-i915:
- fi-rkl-11600: NOTRUN -> [INCOMPLETE][28] ([i915#4817])
[28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@i915_suspend@basic-s3-without-i915.html
* {igt@kms_chamelium_edid@dp-edid-read} (NEW):
- fi-bsw-n3050: NOTRUN -> [SKIP][29] ([fdo#109271]) +8 similar issues
[29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-n3050/igt@kms_chamelium_edid@dp-edid-read.html
- fi-bwr-2160: NOTRUN -> [SKIP][30] ([fdo#109271]) +8 similar issues
[30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bwr-2160/igt@kms_chamelium_edid@dp-edid-read.html
* {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
- fi-cfl-guc: NOTRUN -> [SKIP][31] ([fdo#109271]) +8 similar issues
[31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-cfl-guc/igt@kms_chamelium_edid@hdmi-edid-read.html
- fi-skl-6600u: NOTRUN -> [SKIP][32] ([fdo#109271]) +8 similar issues
[32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-skl-6600u/igt@kms_chamelium_edid@hdmi-edid-read.html
* {igt@kms_chamelium_frames@dp-crc-fast} (NEW):
- fi-ilk-650: NOTRUN -> [SKIP][33] ([fdo#109271]) +8 similar issues
[33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-ilk-650/igt@kms_chamelium_frames@dp-crc-fast.html
* {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
- fi-cfl-8109u: NOTRUN -> [SKIP][34] ([fdo#109271]) +8 similar issues
[34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-cfl-8109u/igt@kms_chamelium_frames@hdmi-crc-fast.html
- fi-kbl-7567u: NOTRUN -> [SKIP][35] ([fdo#109271]) +8 similar issues
[35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-kbl-7567u/igt@kms_chamelium_frames@hdmi-crc-fast.html
- fi-kbl-8809g: NOTRUN -> [SKIP][36] ([fdo#109271]) +7 similar issues
[36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-kbl-8809g/igt@kms_chamelium_frames@hdmi-crc-fast.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- fi-glk-j4005: NOTRUN -> [SKIP][37] ([fdo#109271]) +8 similar issues
[37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-glk-j4005/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
- fi-snb-2600: NOTRUN -> [SKIP][38] ([fdo#109271]) +8 similar issues
[38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-snb-2600/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
- fi-skl-6700k2: NOTRUN -> [SKIP][39] ([fdo#109271]) +4 similar issues
[39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-skl-6700k2/igt@kms_chamelium_hpd@dp-hpd-fast.html
* {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
- fi-cfl-8700k: NOTRUN -> [SKIP][40] ([fdo#109271]) +8 similar issues
[40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-cfl-8700k/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
- fi-blb-e6850: NOTRUN -> [SKIP][41] ([fdo#109271]) +8 similar issues
[41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-blb-e6850/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
- {bat-kbl-2}: NOTRUN -> [SKIP][42] ([fdo#109271]) +8 similar issues
[42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-kbl-2/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
* {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
- fi-apl-guc: NOTRUN -> [SKIP][43] ([fdo#109271]) +8 similar issues
[43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-apl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-bsw-kefka: NOTRUN -> [SKIP][44] ([fdo#109271]) +8 similar issues
[44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-kefka/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-atsm-1}: NOTRUN -> [SKIP][45] ([i915#6078]) +7 similar issues
[45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-atsm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-skl-guc: NOTRUN -> [SKIP][46] ([fdo#109271]) +8 similar issues
[46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-skl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-ivb-3770: NOTRUN -> [SKIP][47] ([fdo#109271]) +8 similar issues
[47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-ivb-3770/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-elk-e7500: NOTRUN -> [SKIP][48] ([fdo#109271]) +8 similar issues
[48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-elk-e7500/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-bsw-nick: NOTRUN -> [SKIP][49] ([fdo#109271]) +8 similar issues
[49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-nick/igt@kms_chamelium_hpd@vga-hpd-fast.html
* igt@kms_cursor_legacy@basic-busy-flip-before-cursor:
- fi-rkl-11600: NOTRUN -> [SKIP][50] ([i915#4103])
[50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_cursor_legacy@basic-busy-flip-before-cursor.html
* igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size:
- fi-bsw-kefka: [PASS][51] -> [FAIL][52] ([i915#6298])
[51]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-bsw-kefka/igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size.html
[52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-kefka/igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size.html
* igt@kms_force_connector_basic@force-load-detect:
- fi-rkl-11600: NOTRUN -> [SKIP][53] ([fdo#109285] / [i915#4098])
[53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_force_connector_basic@force-load-detect.html
* igt@kms_psr@primary_page_flip:
- fi-rkl-11600: NOTRUN -> [SKIP][54] ([i915#1072]) +3 similar issues
[54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_psr@primary_page_flip.html
* igt@kms_setmode@basic-clone-single-crtc:
- fi-rkl-11600: NOTRUN -> [SKIP][55] ([i915#3555] / [i915#4098])
[55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_setmode@basic-clone-single-crtc.html
* igt@prime_vgem@basic-read:
- fi-rkl-11600: NOTRUN -> [SKIP][56] ([fdo#109295] / [i915#3291] / [i915#3708]) +2 similar issues
[56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@prime_vgem@basic-read.html
* igt@prime_vgem@basic-userptr:
- fi-rkl-11600: NOTRUN -> [SKIP][57] ([fdo#109295] / [i915#3301] / [i915#3708])
[57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@prime_vgem@basic-userptr.html
* igt@runner@aborted:
- fi-icl-u2: NOTRUN -> [FAIL][58] ([i915#4312])
[58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-icl-u2/igt@runner@aborted.html
#### Possible fixes ####
* igt@i915_selftest@live@hangcheck:
- bat-dg1-6: [INCOMPLETE][59] -> [PASS][60]
[59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
[60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
* igt@i915_selftest@live@slpc:
- bat-adlp-4: [DMESG-FAIL][61] ([i915#6367]) -> [PASS][62]
[61]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-4/igt@i915_selftest@live@slpc.html
[62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-4/igt@i915_selftest@live@slpc.html
- {bat-rpls-1}: [DMESG-FAIL][63] ([i915#6367]) -> [PASS][64]
[63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-rpls-1/igt@i915_selftest@live@slpc.html
[64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-rpls-1/igt@i915_selftest@live@slpc.html
* igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1:
- {bat-adlp-9}: [DMESG-WARN][65] ([i915#2867]) -> [PASS][66]
[65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
[66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
[fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
[i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
[i915#2190]: https://gitlab.freedesktop.org/drm/intel/issues/2190
[i915#2867]: https://gitlab.freedesktop.org/drm/intel/issues/2867
[i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
[i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
[i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
[i915#3546]: https://gitlab.freedesktop.org/drm/intel/issues/3546
[i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
[i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
[i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
[i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
[i915#4137]: https://gitlab.freedesktop.org/drm/intel/issues/4137
[i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
[i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
[i915#4817]: https://gitlab.freedesktop.org/drm/intel/issues/4817
[i915#6078]: https://gitlab.freedesktop.org/drm/intel/issues/6078
[i915#6298]: https://gitlab.freedesktop.org/drm/intel/issues/6298
[i915#6367]: https://gitlab.freedesktop.org/drm/intel/issues/6367
[i915#7456]: https://gitlab.freedesktop.org/drm/intel/issues/7456
[i915#7561]: https://gitlab.freedesktop.org/drm/intel/issues/7561
Build changes
-------------
* CI: CI-20190529 -> None
* IGT: IGT_7106 -> IGTPW_8293
CI-20190529: 20190529
CI_DRM_12541: b832866fa6063614b3637598aca19aee3bc3039f @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_8293: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/index.html
IGT_7106: 8cce332bdc50d2b20d553d7a0221737f4399d031 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
Testlist changes
----------------
+++ 71 lines
--- 71 lines
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/index.html
[-- Attachment #2: Type: text/html, Size: 21572 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5)
2023-01-03 18:45 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5) Patchwork
@ 2023-01-03 19:06 ` Mark Yacoub
0 siblings, 0 replies; 11+ messages in thread
From: Mark Yacoub @ 2023-01-03 19:06 UTC (permalink / raw)
To: Vudum, Lakshminarayana; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 19626 bytes --]
Hello, can I get a rerun? Thank you!
On Tue, Jan 3, 2023 at 1:45 PM Patchwork <patchwork@emeril.freedesktop.org>
wrote:
> *Patch Details*
> *Series:* Chamelium: Split kms_chamelium into multiple kms_chamelium
> tests (rev5)
> *URL:* https://patchwork.freedesktop.org/series/111501/
> *State:* failure
> *Details:* https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/index.html CI
> Bug Log - changes from CI_DRM_12541 -> IGTPW_8293 Summary
>
> *FAILURE*
>
> Serious unknown changes coming with IGTPW_8293 absolutely need to be
> verified manually.
>
> If you think the reported changes have nothing to do with the changes
> introduced in IGTPW_8293, please notify your bug team to allow them
> to document this new failure mode, which will reduce false positives in CI.
>
> External URL:
> https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/index.html
> Participating hosts (42 -> 42)
>
> Additional (1): fi-rkl-11600
> Missing (1): fi-pnv-d510
> Possible new issues
>
> Here are the unknown changes that may have been introduced in IGTPW_8293:
> IGT changes Possible regressions
>
> -
>
> igt@debugfs_test@read_all_entries:
> - fi-icl-u2: PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-icl-u2/igt@debugfs_test@read_all_entries.html>
> -> ABORT
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-icl-u2/igt@debugfs_test@read_all_entries.html>
> -
>
> {igt@kms_chamelium_edid@dp-edid-read} (NEW):
> -
>
> {fi-jsl-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-jsl-1/igt@kms_chamelium_edid@dp-edid-read.html>
> +8 similar issues
> -
>
> {bat-rpls-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-rpls-1/igt@kms_chamelium_edid@dp-edid-read.html>
> +7 similar issues
> -
>
> {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
> -
>
> fi-adl-ddr5: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-adl-ddr5/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> {fi-ehl-2}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-ehl-2/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> {bat-rpls-2}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-rpls-2/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> bat-dg1-6: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-6/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
> - {bat-adln-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adln-1/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
> -
>
> {bat-adlp-9}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-9/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> +8 similar issues
> -
>
> {bat-dg2-oem1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-oem1/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
> -
>
> fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +7 similar issues
> -
>
> bat-dg1-5: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-5/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg1-7}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-7/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg2-9}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-9/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
> - {bat-adlp-6}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-6/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
> -
>
> {bat-jsl-3}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-jsl-3/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> fi-rkl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg2-11}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-11/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> bat-adlp-4: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-4/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg2-8}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg2-8/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-adlm-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
>
> New tests
>
> New tests have been introduced between CI_DRM_12541 and IGTPW_8293:
> New IGT tests (9)
>
> -
>
> igt@kms_chamelium_edid@dp-edid-read:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_edid@hdmi-edid-read:
> - Statuses : 1 pass(s) 40 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_edid@vga-edid-read:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_frames@dp-crc-fast:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_frames@hdmi-crc-fast:
> - Statuses : 1 pass(s) 40 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@common-hpd-after-suspend:
> - Statuses : 1 pass(s) 36 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@dp-hpd-fast:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@hdmi-hpd-fast:
> - Statuses : 1 pass(s) 40 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@vga-hpd-fast:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
>
> Known issues
>
> Here are the changes found in IGTPW_8293 that come from known issues:
> IGT changes Issues hit
>
> -
>
> igt@debugfs_test@basic-hwmon:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@debugfs_test@basic-hwmon.html>
> (i915#7456 <https://gitlab.freedesktop.org/drm/intel/issues/7456>)
> -
>
> igt@gem_huc_copy@huc-copy:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@gem_huc_copy@huc-copy.html>
> (i915#2190 <https://gitlab.freedesktop.org/drm/intel/issues/2190>)
> -
>
> igt@gem_lmem_swapping@basic:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@gem_lmem_swapping@basic.html>
> (i915#4613 <https://gitlab.freedesktop.org/drm/intel/issues/4613>)
> +3 similar issues
> -
>
> igt@gem_tiled_pread_basic:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@gem_tiled_pread_basic.html>
> (i915#3282 <https://gitlab.freedesktop.org/drm/intel/issues/3282>)
> -
>
> igt@i915_pm_backlight@basic-brightness:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@i915_pm_backlight@basic-brightness.html>
> (i915#7561 <https://gitlab.freedesktop.org/drm/intel/issues/7561>)
> -
>
> igt@i915_suspend@basic-s3-without-i915:
> - fi-rkl-11600: NOTRUN -> INCOMPLETE
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@i915_suspend@basic-s3-without-i915.html>
> (i915#4817 <https://gitlab.freedesktop.org/drm/intel/issues/4817>)
> -
>
> {igt@kms_chamelium_edid@dp-edid-read} (NEW):
> -
>
> fi-bsw-n3050: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-n3050/igt@kms_chamelium_edid@dp-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-bwr-2160: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bwr-2160/igt@kms_chamelium_edid@dp-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
> -
>
> fi-cfl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-cfl-guc/igt@kms_chamelium_edid@hdmi-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-skl-6600u: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-skl-6600u/igt@kms_chamelium_edid@hdmi-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_frames@dp-crc-fast} (NEW):
> - fi-ilk-650: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-ilk-650/igt@kms_chamelium_frames@dp-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
> -
>
> fi-cfl-8109u: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-cfl-8109u/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-kbl-7567u: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-kbl-7567u/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-kbl-8809g: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-kbl-8809g/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +7 similar issues
> -
>
> {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
> -
>
> fi-glk-j4005: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-glk-j4005/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-snb-2600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-snb-2600/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
> - fi-skl-6700k2: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-skl-6700k2/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +4 similar issues
> -
>
> {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
> -
>
> fi-cfl-8700k: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-cfl-8700k/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-blb-e6850: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-blb-e6850/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {bat-kbl-2}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-kbl-2/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
> -
>
> fi-apl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-apl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-bsw-kefka: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-kefka/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {bat-atsm-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-atsm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (i915#6078 <https://gitlab.freedesktop.org/drm/intel/issues/6078>)
> +7 similar issues
> -
>
> fi-skl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-skl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-ivb-3770: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-ivb-3770/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-elk-e7500: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-elk-e7500/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-bsw-nick: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-nick/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> igt@kms_cursor_legacy@basic-busy-flip-before-cursor:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_cursor_legacy@basic-busy-flip-before-cursor.html>
> (i915#4103 <https://gitlab.freedesktop.org/drm/intel/issues/4103>)
> -
>
> igt@kms_cursor_legacy
> @basic-busy-flip-before-cursor@atomic-transitions-varying-size:
> - fi-bsw-kefka: PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-bsw-kefka/igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size.html>
> -> FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-bsw-kefka/igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size.html>
> (i915#6298 <https://gitlab.freedesktop.org/drm/intel/issues/6298>)
> -
>
> igt@kms_force_connector_basic@force-load-detect:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_force_connector_basic@force-load-detect.html>
> (fdo#109285 <https://bugs.freedesktop.org/show_bug.cgi?id=109285> /
> i915#4098 <https://gitlab.freedesktop.org/drm/intel/issues/4098>)
> -
>
> igt@kms_psr@primary_page_flip:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_psr@primary_page_flip.html>
> (i915#1072 <https://gitlab.freedesktop.org/drm/intel/issues/1072>)
> +3 similar issues
> -
>
> igt@kms_setmode@basic-clone-single-crtc:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@kms_setmode@basic-clone-single-crtc.html>
> (i915#3555 <https://gitlab.freedesktop.org/drm/intel/issues/3555> /
> i915#4098 <https://gitlab.freedesktop.org/drm/intel/issues/4098>)
> -
>
> igt@prime_vgem@basic-read:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@prime_vgem@basic-read.html>
> (fdo#109295 <https://bugs.freedesktop.org/show_bug.cgi?id=109295> /
> i915#3291 <https://gitlab.freedesktop.org/drm/intel/issues/3291> /
> i915#3708 <https://gitlab.freedesktop.org/drm/intel/issues/3708>)
> +2 similar issues
> -
>
> igt@prime_vgem@basic-userptr:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-rkl-11600/igt@prime_vgem@basic-userptr.html>
> (fdo#109295 <https://bugs.freedesktop.org/show_bug.cgi?id=109295> /
> i915#3301 <https://gitlab.freedesktop.org/drm/intel/issues/3301> /
> i915#3708 <https://gitlab.freedesktop.org/drm/intel/issues/3708>)
> -
>
> igt@runner@aborted:
> - fi-icl-u2: NOTRUN -> FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/fi-icl-u2/igt@runner@aborted.html>
> (i915#4312 <https://gitlab.freedesktop.org/drm/intel/issues/4312>)
>
> Possible fixes
>
> -
>
> igt@i915_selftest@live@hangcheck:
> - bat-dg1-6: INCOMPLETE
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-dg1-6/igt@i915_selftest@live@hangcheck.html>
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-dg1-6/igt@i915_selftest@live@hangcheck.html>
> -
>
> igt@i915_selftest@live@slpc:
> -
>
> bat-adlp-4: DMESG-FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-4/igt@i915_selftest@live@slpc.html>
> (i915#6367 <https://gitlab.freedesktop.org/drm/intel/issues/6367>)
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-4/igt@i915_selftest@live@slpc.html>
> -
>
> {bat-rpls-1}: DMESG-FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-rpls-1/igt@i915_selftest@live@slpc.html>
> (i915#6367 <https://gitlab.freedesktop.org/drm/intel/issues/6367>)
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-rpls-1/igt@i915_selftest@live@slpc.html>
> -
>
> igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1:
> - {bat-adlp-9}: DMESG-WARN
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html>
> (i915#2867 <https://gitlab.freedesktop.org/drm/intel/issues/2867>)
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html>
>
> {name}: This element is suppressed. This means it is ignored when computing
> the status of the difference (SUCCESS, WARNING, or FAILURE).
> Build changes
>
> - CI: CI-20190529 -> None
> - IGT: IGT_7106 -> IGTPW_8293
>
> CI-20190529: 20190529
> CI_DRM_12541: b832866fa6063614b3637598aca19aee3bc3039f @ git://
> anongit.freedesktop.org/gfx-ci/linux
> IGTPW_8293: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8293/index.html
> IGT_7106: 8cce332bdc50d2b20d553d7a0221737f4399d031 @
> https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
> Testlist changes
>
> +++ 71 lines
> --- 71 lines
>
[-- Attachment #2: Type: text/html, Size: 24337 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
2023-01-03 17:15 [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Mark Yacoub
2023-01-03 18:45 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5) Patchwork
@ 2023-01-04 12:06 ` Patchwork
2023-01-06 17:16 ` Mark Yacoub
2023-01-09 4:02 ` [igt-dev] ✓ Fi.CI.BAT: success " Patchwork
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Patchwork @ 2023-01-04 12:06 UTC (permalink / raw)
To: Mark Yacoub; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 17998 bytes --]
== Series Details ==
Series: Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
URL : https://patchwork.freedesktop.org/series/111501/
State : failure
== Summary ==
CI Bug Log - changes from CI_DRM_12541 -> IGTPW_8297
====================================================
Summary
-------
**FAILURE**
Serious unknown changes coming with IGTPW_8297 absolutely need to be
verified manually.
If you think the reported changes have nothing to do with the changes
introduced in IGTPW_8297, please notify your bug team to allow them
to document this new failure mode, which will reduce false positives in CI.
External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
Participating hosts (42 -> 42)
------------------------------
Additional (1): fi-rkl-11600
Missing (1): bat-dg2-oem1
Possible new issues
-------------------
Here are the unknown changes that may have been introduced in IGTPW_8297:
### IGT changes ###
#### Possible regressions ####
* igt@debugfs_test@read_all_entries:
- fi-icl-u2: [PASS][1] -> [ABORT][2]
[1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-icl-u2/igt@debugfs_test@read_all_entries.html
[2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-icl-u2/igt@debugfs_test@read_all_entries.html
* {igt@kms_chamelium_edid@dp-edid-read} (NEW):
- {fi-jsl-1}: NOTRUN -> [SKIP][3] +8 similar issues
[3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-jsl-1/igt@kms_chamelium_edid@dp-edid-read.html
- {bat-rpls-1}: NOTRUN -> [SKIP][4] +7 similar issues
[4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-rpls-1/igt@kms_chamelium_edid@dp-edid-read.html
* {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
- fi-adl-ddr5: NOTRUN -> [SKIP][5] +8 similar issues
[5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-adl-ddr5/igt@kms_chamelium_edid@hdmi-edid-read.html
- {fi-ehl-2}: NOTRUN -> [SKIP][6] +8 similar issues
[6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ehl-2/igt@kms_chamelium_edid@hdmi-edid-read.html
- {bat-rpls-2}: NOTRUN -> [SKIP][7] +8 similar issues
[7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-rpls-2/igt@kms_chamelium_edid@hdmi-edid-read.html
- bat-dg1-6: NOTRUN -> [SKIP][8] +8 similar issues
[8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-6/igt@kms_chamelium_edid@hdmi-edid-read.html
* {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
- {bat-adln-1}: NOTRUN -> [SKIP][9] +8 similar issues
[9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adln-1/igt@kms_chamelium_frames@hdmi-crc-fast.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- {bat-adlp-9}: NOTRUN -> [SKIP][10] +8 similar issues
[10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-9/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
- fi-rkl-11600: NOTRUN -> [SKIP][11] +7 similar issues
[11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_chamelium_hpd@dp-hpd-fast.html
- bat-dg1-5: NOTRUN -> [SKIP][12] +8 similar issues
[12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-5/igt@kms_chamelium_hpd@dp-hpd-fast.html
- {bat-dg1-7}: NOTRUN -> [SKIP][13] +8 similar issues
[13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-7/igt@kms_chamelium_hpd@dp-hpd-fast.html
- {bat-dg2-9}: NOTRUN -> [SKIP][14] +8 similar issues
[14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-9/igt@kms_chamelium_hpd@dp-hpd-fast.html
* {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
- {bat-adlp-6}: NOTRUN -> [SKIP][15] +8 similar issues
[15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-6/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
* {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
- {bat-jsl-3}: NOTRUN -> [SKIP][16] +8 similar issues
[16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-jsl-3/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-rkl-guc: NOTRUN -> [SKIP][17] +8 similar issues
[17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-dg2-11}: NOTRUN -> [SKIP][18] +8 similar issues
[18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-11/igt@kms_chamelium_hpd@vga-hpd-fast.html
- bat-adlp-4: NOTRUN -> [SKIP][19] +8 similar issues
[19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-4/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-dg2-8}: NOTRUN -> [SKIP][20] +8 similar issues
[20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-8/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-adlm-1}: NOTRUN -> [SKIP][21] +8 similar issues
[21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html
New tests
---------
New tests have been introduced between CI_DRM_12541 and IGTPW_8297:
### New IGT tests (9) ###
* igt@kms_chamelium_edid@dp-edid-read:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-read:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@vga-edid-read:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-crc-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-fast:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@common-hpd-after-suspend:
- Statuses : 1 pass(s) 36 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-fast:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
Known issues
------------
Here are the changes found in IGTPW_8297 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@debugfs_test@basic-hwmon:
- fi-rkl-11600: NOTRUN -> [SKIP][22] ([i915#7456])
[22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@debugfs_test@basic-hwmon.html
* igt@gem_huc_copy@huc-copy:
- fi-rkl-11600: NOTRUN -> [SKIP][23] ([i915#2190])
[23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_huc_copy@huc-copy.html
* igt@gem_lmem_swapping@basic:
- fi-rkl-11600: NOTRUN -> [SKIP][24] ([i915#4613]) +3 similar issues
[24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_lmem_swapping@basic.html
* igt@gem_tiled_pread_basic:
- fi-rkl-11600: NOTRUN -> [SKIP][25] ([i915#3282])
[25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_tiled_pread_basic.html
* igt@i915_pm_backlight@basic-brightness:
- fi-rkl-11600: NOTRUN -> [SKIP][26] ([i915#7561])
[26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@i915_pm_backlight@basic-brightness.html
* igt@i915_selftest@live@gt_heartbeat:
- fi-apl-guc: [PASS][27] -> [DMESG-FAIL][28] ([i915#5334])
[27]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-apl-guc/igt@i915_selftest@live@gt_heartbeat.html
[28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-apl-guc/igt@i915_selftest@live@gt_heartbeat.html
* igt@i915_suspend@basic-s3-without-i915:
- fi-rkl-11600: NOTRUN -> [INCOMPLETE][29] ([i915#4817])
[29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@i915_suspend@basic-s3-without-i915.html
* {igt@kms_chamelium_edid@dp-edid-read} (NEW):
- fi-bsw-n3050: NOTRUN -> [SKIP][30] ([fdo#109271]) +8 similar issues
[30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-n3050/igt@kms_chamelium_edid@dp-edid-read.html
- fi-bwr-2160: NOTRUN -> [SKIP][31] ([fdo#109271]) +8 similar issues
[31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bwr-2160/igt@kms_chamelium_edid@dp-edid-read.html
- fi-pnv-d510: NOTRUN -> [SKIP][32] ([fdo#109271]) +8 similar issues
[32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-pnv-d510/igt@kms_chamelium_edid@dp-edid-read.html
* {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
- fi-cfl-guc: NOTRUN -> [SKIP][33] ([fdo#109271]) +8 similar issues
[33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-guc/igt@kms_chamelium_edid@hdmi-edid-read.html
- fi-skl-6600u: NOTRUN -> [SKIP][34] ([fdo#109271]) +8 similar issues
[34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-6600u/igt@kms_chamelium_edid@hdmi-edid-read.html
* {igt@kms_chamelium_frames@dp-crc-fast} (NEW):
- fi-ilk-650: NOTRUN -> [SKIP][35] ([fdo#109271]) +8 similar issues
[35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ilk-650/igt@kms_chamelium_frames@dp-crc-fast.html
* {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
- fi-cfl-8109u: NOTRUN -> [SKIP][36] ([fdo#109271]) +8 similar issues
[36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-8109u/igt@kms_chamelium_frames@hdmi-crc-fast.html
- fi-kbl-7567u: NOTRUN -> [SKIP][37] ([fdo#109271]) +8 similar issues
[37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-kbl-7567u/igt@kms_chamelium_frames@hdmi-crc-fast.html
- fi-kbl-8809g: NOTRUN -> [SKIP][38] ([fdo#109271]) +7 similar issues
[38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-kbl-8809g/igt@kms_chamelium_frames@hdmi-crc-fast.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- fi-glk-j4005: NOTRUN -> [SKIP][39] ([fdo#109271]) +8 similar issues
[39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-glk-j4005/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
- fi-snb-2600: NOTRUN -> [SKIP][40] ([fdo#109271]) +8 similar issues
[40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-snb-2600/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
- fi-skl-6700k2: NOTRUN -> [SKIP][41] ([fdo#109271]) +4 similar issues
[41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-6700k2/igt@kms_chamelium_hpd@dp-hpd-fast.html
* {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
- fi-cfl-8700k: NOTRUN -> [SKIP][42] ([fdo#109271]) +8 similar issues
[42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-8700k/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
- fi-blb-e6850: NOTRUN -> [SKIP][43] ([fdo#109271]) +8 similar issues
[43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-blb-e6850/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
- {bat-kbl-2}: NOTRUN -> [SKIP][44] ([fdo#109271]) +8 similar issues
[44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-kbl-2/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
* {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
- fi-apl-guc: NOTRUN -> [SKIP][45] ([fdo#109271]) +8 similar issues
[45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-apl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-bsw-kefka: NOTRUN -> [SKIP][46] ([fdo#109271]) +8 similar issues
[46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-kefka/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-atsm-1}: NOTRUN -> [SKIP][47] ([i915#6078]) +7 similar issues
[47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-atsm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-skl-guc: NOTRUN -> [SKIP][48] ([fdo#109271]) +8 similar issues
[48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-ivb-3770: NOTRUN -> [SKIP][49] ([fdo#109271]) +8 similar issues
[49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ivb-3770/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-elk-e7500: NOTRUN -> [SKIP][50] ([fdo#109271]) +8 similar issues
[50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-elk-e7500/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-bsw-nick: NOTRUN -> [SKIP][51] ([fdo#109271]) +8 similar issues
[51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-nick/igt@kms_chamelium_hpd@vga-hpd-fast.html
* igt@kms_cursor_legacy@basic-busy-flip-before-cursor:
- fi-rkl-11600: NOTRUN -> [SKIP][52] ([i915#4103])
[52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_cursor_legacy@basic-busy-flip-before-cursor.html
* igt@kms_force_connector_basic@force-load-detect:
- fi-rkl-11600: NOTRUN -> [SKIP][53] ([fdo#109285] / [i915#4098])
[53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_force_connector_basic@force-load-detect.html
* igt@kms_psr@primary_page_flip:
- fi-rkl-11600: NOTRUN -> [SKIP][54] ([i915#1072]) +3 similar issues
[54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_psr@primary_page_flip.html
* igt@kms_setmode@basic-clone-single-crtc:
- fi-rkl-11600: NOTRUN -> [SKIP][55] ([i915#3555] / [i915#4098])
[55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_setmode@basic-clone-single-crtc.html
* igt@prime_vgem@basic-read:
- fi-rkl-11600: NOTRUN -> [SKIP][56] ([fdo#109295] / [i915#3291] / [i915#3708]) +2 similar issues
[56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@prime_vgem@basic-read.html
* igt@prime_vgem@basic-userptr:
- fi-rkl-11600: NOTRUN -> [SKIP][57] ([fdo#109295] / [i915#3301] / [i915#3708])
[57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@prime_vgem@basic-userptr.html
* igt@runner@aborted:
- fi-icl-u2: NOTRUN -> [FAIL][58] ([i915#4312])
[58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-icl-u2/igt@runner@aborted.html
#### Possible fixes ####
* igt@i915_selftest@live@hangcheck:
- bat-dg1-6: [INCOMPLETE][59] -> [PASS][60]
[59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
[60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
* igt@i915_selftest@live@migrate:
- {bat-atsm-1}: [DMESG-FAIL][61] ([i915#7699]) -> [PASS][62]
[61]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-atsm-1/igt@i915_selftest@live@migrate.html
[62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-atsm-1/igt@i915_selftest@live@migrate.html
* igt@i915_selftest@live@slpc:
- bat-adlp-4: [DMESG-FAIL][63] ([i915#6367]) -> [PASS][64]
[63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-4/igt@i915_selftest@live@slpc.html
[64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-4/igt@i915_selftest@live@slpc.html
* igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1:
- {bat-adlp-9}: [DMESG-WARN][65] ([i915#2867]) -> [PASS][66]
[65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
[66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
[fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
[i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
[i915#2190]: https://gitlab.freedesktop.org/drm/intel/issues/2190
[i915#2867]: https://gitlab.freedesktop.org/drm/intel/issues/2867
[i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
[i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
[i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
[i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
[i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
[i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
[i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
[i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
[i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
[i915#4817]: https://gitlab.freedesktop.org/drm/intel/issues/4817
[i915#4983]: https://gitlab.freedesktop.org/drm/intel/issues/4983
[i915#5334]: https://gitlab.freedesktop.org/drm/intel/issues/5334
[i915#6078]: https://gitlab.freedesktop.org/drm/intel/issues/6078
[i915#6257]: https://gitlab.freedesktop.org/drm/intel/issues/6257
[i915#6367]: https://gitlab.freedesktop.org/drm/intel/issues/6367
[i915#7336]: https://gitlab.freedesktop.org/drm/intel/issues/7336
[i915#7456]: https://gitlab.freedesktop.org/drm/intel/issues/7456
[i915#7561]: https://gitlab.freedesktop.org/drm/intel/issues/7561
[i915#7699]: https://gitlab.freedesktop.org/drm/intel/issues/7699
Build changes
-------------
* CI: CI-20190529 -> None
* IGT: IGT_7106 -> IGTPW_8297
CI-20190529: 20190529
CI_DRM_12541: b832866fa6063614b3637598aca19aee3bc3039f @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_8297: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
IGT_7106: 8cce332bdc50d2b20d553d7a0221737f4399d031 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
Testlist changes
----------------
+++ 71 lines
--- 73 lines
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
[-- Attachment #2: Type: text/html, Size: 21538 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
2023-01-04 12:06 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6) Patchwork
@ 2023-01-06 17:16 ` Mark Yacoub
[not found] ` <DM4PR11MB62134C50D1933888C97021E8ECF89@DM4PR11MB6213.namprd11.prod.outlook.com>
0 siblings, 1 reply; 11+ messages in thread
From: Mark Yacoub @ 2023-01-06 17:16 UTC (permalink / raw)
To: Vudum, Lakshminarayana; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 19571 bytes --]
Hi Lakshminarayana, can I get a rerun on this. Thank you!
On Wed, Jan 4, 2023 at 7:06 AM Patchwork <patchwork@emeril.freedesktop.org>
wrote:
> *Patch Details*
> *Series:* Chamelium: Split kms_chamelium into multiple kms_chamelium
> tests (rev6)
> *URL:* https://patchwork.freedesktop.org/series/111501/
> *State:* failure
> *Details:* https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html CI
> Bug Log - changes from CI_DRM_12541 -> IGTPW_8297 Summary
>
> *FAILURE*
>
> Serious unknown changes coming with IGTPW_8297 absolutely need to be
> verified manually.
>
> If you think the reported changes have nothing to do with the changes
> introduced in IGTPW_8297, please notify your bug team to allow them
> to document this new failure mode, which will reduce false positives in CI.
>
> External URL:
> https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
> Participating hosts (42 -> 42)
>
> Additional (1): fi-rkl-11600
> Missing (1): bat-dg2-oem1
> Possible new issues
>
> Here are the unknown changes that may have been introduced in IGTPW_8297:
> IGT changes Possible regressions
>
> -
>
> igt@debugfs_test@read_all_entries:
> - fi-icl-u2: PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-icl-u2/igt@debugfs_test@read_all_entries.html>
> -> ABORT
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-icl-u2/igt@debugfs_test@read_all_entries.html>
> -
>
> {igt@kms_chamelium_edid@dp-edid-read} (NEW):
> -
>
> {fi-jsl-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-jsl-1/igt@kms_chamelium_edid@dp-edid-read.html>
> +8 similar issues
> -
>
> {bat-rpls-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-rpls-1/igt@kms_chamelium_edid@dp-edid-read.html>
> +7 similar issues
> -
>
> {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
> -
>
> fi-adl-ddr5: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-adl-ddr5/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> {fi-ehl-2}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ehl-2/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> {bat-rpls-2}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-rpls-2/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> bat-dg1-6: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-6/igt@kms_chamelium_edid@hdmi-edid-read.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
> - {bat-adln-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adln-1/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
> - {bat-adlp-9}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-9/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
> -
>
> fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +7 similar issues
> -
>
> bat-dg1-5: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-5/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg1-7}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-7/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg2-9}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-9/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
> - {bat-adlp-6}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-6/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
> -
>
> {bat-jsl-3}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-jsl-3/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> fi-rkl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg2-11}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-11/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> bat-adlp-4: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-4/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-dg2-8}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-8/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
> -
>
> {bat-adlm-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> +8 similar issues
>
> New tests
>
> New tests have been introduced between CI_DRM_12541 and IGTPW_8297:
> New IGT tests (9)
>
> -
>
> igt@kms_chamelium_edid@dp-edid-read:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_edid@hdmi-edid-read:
> - Statuses : 1 pass(s) 40 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_edid@vga-edid-read:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_frames@dp-crc-fast:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_frames@hdmi-crc-fast:
> - Statuses : 1 pass(s) 40 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@common-hpd-after-suspend:
> - Statuses : 1 pass(s) 36 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@dp-hpd-fast:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@hdmi-hpd-fast:
> - Statuses : 1 pass(s) 40 skip(s)
> - Exec time: [0.0] s
> -
>
> igt@kms_chamelium_hpd@vga-hpd-fast:
> - Statuses : 41 skip(s)
> - Exec time: [0.0] s
>
> Known issues
>
> Here are the changes found in IGTPW_8297 that come from known issues:
> IGT changes Issues hit
>
> -
>
> igt@debugfs_test@basic-hwmon:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@debugfs_test@basic-hwmon.html>
> (i915#7456 <https://gitlab.freedesktop.org/drm/intel/issues/7456>)
> -
>
> igt@gem_huc_copy@huc-copy:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_huc_copy@huc-copy.html>
> (i915#2190 <https://gitlab.freedesktop.org/drm/intel/issues/2190>)
> -
>
> igt@gem_lmem_swapping@basic:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_lmem_swapping@basic.html>
> (i915#4613 <https://gitlab.freedesktop.org/drm/intel/issues/4613>)
> +3 similar issues
> -
>
> igt@gem_tiled_pread_basic:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_tiled_pread_basic.html>
> (i915#3282 <https://gitlab.freedesktop.org/drm/intel/issues/3282>)
> -
>
> igt@i915_pm_backlight@basic-brightness:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@i915_pm_backlight@basic-brightness.html>
> (i915#7561 <https://gitlab.freedesktop.org/drm/intel/issues/7561>)
> -
>
> igt@i915_selftest@live@gt_heartbeat:
> - fi-apl-guc: PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-apl-guc/igt@i915_selftest@live@gt_heartbeat.html>
> -> DMESG-FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-apl-guc/igt@i915_selftest@live@gt_heartbeat.html>
> (i915#5334 <https://gitlab.freedesktop.org/drm/intel/issues/5334>)
> -
>
> igt@i915_suspend@basic-s3-without-i915:
> - fi-rkl-11600: NOTRUN -> INCOMPLETE
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@i915_suspend@basic-s3-without-i915.html>
> (i915#4817 <https://gitlab.freedesktop.org/drm/intel/issues/4817>)
> -
>
> {igt@kms_chamelium_edid@dp-edid-read} (NEW):
> -
>
> fi-bsw-n3050: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-n3050/igt@kms_chamelium_edid@dp-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-bwr-2160: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bwr-2160/igt@kms_chamelium_edid@dp-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-pnv-d510: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-pnv-d510/igt@kms_chamelium_edid@dp-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
> -
>
> fi-cfl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-guc/igt@kms_chamelium_edid@hdmi-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-skl-6600u: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-6600u/igt@kms_chamelium_edid@hdmi-edid-read.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_frames@dp-crc-fast} (NEW):
> - fi-ilk-650: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ilk-650/igt@kms_chamelium_frames@dp-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
> -
>
> fi-cfl-8109u: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-8109u/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-kbl-7567u: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-kbl-7567u/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-kbl-8809g: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-kbl-8809g/igt@kms_chamelium_frames@hdmi-crc-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +7 similar issues
> -
>
> {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
> -
>
> fi-glk-j4005: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-glk-j4005/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-snb-2600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-snb-2600/igt@kms_chamelium_hpd@common-hpd-after-suspend.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
> - fi-skl-6700k2: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-6700k2/igt@kms_chamelium_hpd@dp-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +4 similar issues
> -
>
> {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
> -
>
> fi-cfl-8700k: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-8700k/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-blb-e6850: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-blb-e6850/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {bat-kbl-2}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-kbl-2/igt@kms_chamelium_hpd@hdmi-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
> -
>
> fi-apl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-apl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-bsw-kefka: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-kefka/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> {bat-atsm-1}: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-atsm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (i915#6078 <https://gitlab.freedesktop.org/drm/intel/issues/6078>)
> +7 similar issues
> -
>
> fi-skl-guc: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-ivb-3770: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ivb-3770/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-elk-e7500: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-elk-e7500/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> fi-bsw-nick: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-nick/igt@kms_chamelium_hpd@vga-hpd-fast.html>
> (fdo#109271 <https://bugs.freedesktop.org/show_bug.cgi?id=109271>)
> +8 similar issues
> -
>
> igt@kms_cursor_legacy@basic-busy-flip-before-cursor:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_cursor_legacy@basic-busy-flip-before-cursor.html>
> (i915#4103 <https://gitlab.freedesktop.org/drm/intel/issues/4103>)
> -
>
> igt@kms_force_connector_basic@force-load-detect:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_force_connector_basic@force-load-detect.html>
> (fdo#109285 <https://bugs.freedesktop.org/show_bug.cgi?id=109285> /
> i915#4098 <https://gitlab.freedesktop.org/drm/intel/issues/4098>)
> -
>
> igt@kms_psr@primary_page_flip:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_psr@primary_page_flip.html>
> (i915#1072 <https://gitlab.freedesktop.org/drm/intel/issues/1072>)
> +3 similar issues
> -
>
> igt@kms_setmode@basic-clone-single-crtc:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_setmode@basic-clone-single-crtc.html>
> (i915#3555 <https://gitlab.freedesktop.org/drm/intel/issues/3555> /
> i915#4098 <https://gitlab.freedesktop.org/drm/intel/issues/4098>)
> -
>
> igt@prime_vgem@basic-read:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@prime_vgem@basic-read.html>
> (fdo#109295 <https://bugs.freedesktop.org/show_bug.cgi?id=109295> /
> i915#3291 <https://gitlab.freedesktop.org/drm/intel/issues/3291> /
> i915#3708 <https://gitlab.freedesktop.org/drm/intel/issues/3708>)
> +2 similar issues
> -
>
> igt@prime_vgem@basic-userptr:
> - fi-rkl-11600: NOTRUN -> SKIP
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@prime_vgem@basic-userptr.html>
> (fdo#109295 <https://bugs.freedesktop.org/show_bug.cgi?id=109295> /
> i915#3301 <https://gitlab.freedesktop.org/drm/intel/issues/3301> /
> i915#3708 <https://gitlab.freedesktop.org/drm/intel/issues/3708>)
> -
>
> igt@runner@aborted:
> - fi-icl-u2: NOTRUN -> FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-icl-u2/igt@runner@aborted.html>
> (i915#4312 <https://gitlab.freedesktop.org/drm/intel/issues/4312>)
>
> Possible fixes
>
> -
>
> igt@i915_selftest@live@hangcheck:
> - bat-dg1-6: INCOMPLETE
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-dg1-6/igt@i915_selftest@live@hangcheck.html>
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-6/igt@i915_selftest@live@hangcheck.html>
> -
>
> igt@i915_selftest@live@migrate:
> - {bat-atsm-1}: DMESG-FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-atsm-1/igt@i915_selftest@live@migrate.html>
> (i915#7699 <https://gitlab.freedesktop.org/drm/intel/issues/7699>)
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-atsm-1/igt@i915_selftest@live@migrate.html>
> -
>
> igt@i915_selftest@live@slpc:
> - bat-adlp-4: DMESG-FAIL
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-4/igt@i915_selftest@live@slpc.html>
> (i915#6367 <https://gitlab.freedesktop.org/drm/intel/issues/6367>)
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-4/igt@i915_selftest@live@slpc.html>
> -
>
> igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1:
> - {bat-adlp-9}: DMESG-WARN
> <https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html>
> (i915#2867 <https://gitlab.freedesktop.org/drm/intel/issues/2867>)
> -> PASS
> <https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html>
>
> {name}: This element is suppressed. This means it is ignored when computing
> the status of the difference (SUCCESS, WARNING, or FAILURE).
> Build changes
>
> - CI: CI-20190529 -> None
> - IGT: IGT_7106 -> IGTPW_8297
>
> CI-20190529: 20190529
> CI_DRM_12541: b832866fa6063614b3637598aca19aee3bc3039f @ git://
> anongit.freedesktop.org/gfx-ci/linux
> IGTPW_8297: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
> IGT_7106: 8cce332bdc50d2b20d553d7a0221737f4399d031 @
> https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
> Testlist changes
>
> +++ 71 lines
> --- 73 lines
>
[-- Attachment #2: Type: text/html, Size: 24341 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* [igt-dev] ✓ Fi.CI.BAT: success for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
2023-01-03 17:15 [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Mark Yacoub
2023-01-03 18:45 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5) Patchwork
2023-01-04 12:06 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6) Patchwork
@ 2023-01-09 4:02 ` Patchwork
2023-01-09 5:27 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2023-01-09 15:51 ` [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Petri Latvala
4 siblings, 0 replies; 11+ messages in thread
From: Patchwork @ 2023-01-09 4:02 UTC (permalink / raw)
To: Mark Yacoub; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 17803 bytes --]
== Series Details ==
Series: Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
URL : https://patchwork.freedesktop.org/series/111501/
State : success
== Summary ==
CI Bug Log - changes from CI_DRM_12541 -> IGTPW_8297
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
Participating hosts (42 -> 42)
------------------------------
Additional (1): fi-rkl-11600
Missing (1): bat-dg2-oem1
Possible new issues
-------------------
Here are the unknown changes that may have been introduced in IGTPW_8297:
### IGT changes ###
#### Possible regressions ####
* {igt@kms_chamelium_edid@dp-edid-read} (NEW):
- {fi-jsl-1}: NOTRUN -> [SKIP][1] +8 similar issues
[1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-jsl-1/igt@kms_chamelium_edid@dp-edid-read.html
- {bat-rpls-1}: NOTRUN -> [SKIP][2] +7 similar issues
[2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-rpls-1/igt@kms_chamelium_edid@dp-edid-read.html
* {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
- fi-adl-ddr5: NOTRUN -> [SKIP][3] +8 similar issues
[3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-adl-ddr5/igt@kms_chamelium_edid@hdmi-edid-read.html
- {fi-ehl-2}: NOTRUN -> [SKIP][4] +8 similar issues
[4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ehl-2/igt@kms_chamelium_edid@hdmi-edid-read.html
- {bat-rpls-2}: NOTRUN -> [SKIP][5] +8 similar issues
[5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-rpls-2/igt@kms_chamelium_edid@hdmi-edid-read.html
- bat-dg1-6: NOTRUN -> [SKIP][6] +8 similar issues
[6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-6/igt@kms_chamelium_edid@hdmi-edid-read.html
* {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
- {bat-adln-1}: NOTRUN -> [SKIP][7] +8 similar issues
[7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adln-1/igt@kms_chamelium_frames@hdmi-crc-fast.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- {bat-adlp-9}: NOTRUN -> [SKIP][8] +8 similar issues
[8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-9/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
- fi-rkl-11600: NOTRUN -> [SKIP][9] +7 similar issues
[9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_chamelium_hpd@dp-hpd-fast.html
- bat-dg1-5: NOTRUN -> [SKIP][10] +8 similar issues
[10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-5/igt@kms_chamelium_hpd@dp-hpd-fast.html
- {bat-dg1-7}: NOTRUN -> [SKIP][11] +8 similar issues
[11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-7/igt@kms_chamelium_hpd@dp-hpd-fast.html
- {bat-dg2-9}: NOTRUN -> [SKIP][12] +8 similar issues
[12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-9/igt@kms_chamelium_hpd@dp-hpd-fast.html
* {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
- {bat-adlp-6}: NOTRUN -> [SKIP][13] +8 similar issues
[13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-6/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
* {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
- {bat-jsl-3}: NOTRUN -> [SKIP][14] +8 similar issues
[14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-jsl-3/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-rkl-guc: NOTRUN -> [SKIP][15] +8 similar issues
[15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-dg2-11}: NOTRUN -> [SKIP][16] +8 similar issues
[16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-11/igt@kms_chamelium_hpd@vga-hpd-fast.html
- bat-adlp-4: NOTRUN -> [SKIP][17] +8 similar issues
[17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-4/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-dg2-8}: NOTRUN -> [SKIP][18] +8 similar issues
[18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg2-8/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-adlm-1}: NOTRUN -> [SKIP][19] +8 similar issues
[19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html
New tests
---------
New tests have been introduced between CI_DRM_12541 and IGTPW_8297:
### New IGT tests (9) ###
* igt@kms_chamelium_edid@dp-edid-read:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-read:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@vga-edid-read:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-crc-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-fast:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@common-hpd-after-suspend:
- Statuses : 1 pass(s) 36 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-fast:
- Statuses : 1 pass(s) 40 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-fast:
- Statuses : 41 skip(s)
- Exec time: [0.0] s
Known issues
------------
Here are the changes found in IGTPW_8297 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@debugfs_test@basic-hwmon:
- fi-rkl-11600: NOTRUN -> [SKIP][20] ([i915#7456])
[20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@debugfs_test@basic-hwmon.html
* igt@debugfs_test@read_all_entries:
- fi-icl-u2: [PASS][21] -> [ABORT][22] ([i915#7763])
[21]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-icl-u2/igt@debugfs_test@read_all_entries.html
[22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-icl-u2/igt@debugfs_test@read_all_entries.html
* igt@gem_huc_copy@huc-copy:
- fi-rkl-11600: NOTRUN -> [SKIP][23] ([i915#2190])
[23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_huc_copy@huc-copy.html
* igt@gem_lmem_swapping@basic:
- fi-rkl-11600: NOTRUN -> [SKIP][24] ([i915#4613]) +3 similar issues
[24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_lmem_swapping@basic.html
* igt@gem_tiled_pread_basic:
- fi-rkl-11600: NOTRUN -> [SKIP][25] ([i915#3282])
[25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@gem_tiled_pread_basic.html
* igt@i915_pm_backlight@basic-brightness:
- fi-rkl-11600: NOTRUN -> [SKIP][26] ([i915#7561])
[26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@i915_pm_backlight@basic-brightness.html
* igt@i915_selftest@live@gt_heartbeat:
- fi-apl-guc: [PASS][27] -> [DMESG-FAIL][28] ([i915#5334])
[27]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/fi-apl-guc/igt@i915_selftest@live@gt_heartbeat.html
[28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-apl-guc/igt@i915_selftest@live@gt_heartbeat.html
* igt@i915_suspend@basic-s3-without-i915:
- fi-rkl-11600: NOTRUN -> [INCOMPLETE][29] ([i915#4817])
[29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@i915_suspend@basic-s3-without-i915.html
* {igt@kms_chamelium_edid@dp-edid-read} (NEW):
- fi-bsw-n3050: NOTRUN -> [SKIP][30] ([fdo#109271]) +8 similar issues
[30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-n3050/igt@kms_chamelium_edid@dp-edid-read.html
- fi-bwr-2160: NOTRUN -> [SKIP][31] ([fdo#109271]) +8 similar issues
[31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bwr-2160/igt@kms_chamelium_edid@dp-edid-read.html
- fi-pnv-d510: NOTRUN -> [SKIP][32] ([fdo#109271]) +8 similar issues
[32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-pnv-d510/igt@kms_chamelium_edid@dp-edid-read.html
* {igt@kms_chamelium_edid@hdmi-edid-read} (NEW):
- fi-cfl-guc: NOTRUN -> [SKIP][33] ([fdo#109271]) +8 similar issues
[33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-guc/igt@kms_chamelium_edid@hdmi-edid-read.html
- fi-skl-6600u: NOTRUN -> [SKIP][34] ([fdo#109271]) +8 similar issues
[34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-6600u/igt@kms_chamelium_edid@hdmi-edid-read.html
* {igt@kms_chamelium_frames@dp-crc-fast} (NEW):
- fi-ilk-650: NOTRUN -> [SKIP][35] ([fdo#109271]) +8 similar issues
[35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ilk-650/igt@kms_chamelium_frames@dp-crc-fast.html
* {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW):
- fi-cfl-8109u: NOTRUN -> [SKIP][36] ([fdo#109271]) +8 similar issues
[36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-8109u/igt@kms_chamelium_frames@hdmi-crc-fast.html
- fi-kbl-7567u: NOTRUN -> [SKIP][37] ([fdo#109271]) +8 similar issues
[37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-kbl-7567u/igt@kms_chamelium_frames@hdmi-crc-fast.html
- fi-kbl-8809g: NOTRUN -> [SKIP][38] ([fdo#109271]) +7 similar issues
[38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-kbl-8809g/igt@kms_chamelium_frames@hdmi-crc-fast.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- fi-glk-j4005: NOTRUN -> [SKIP][39] ([fdo#109271]) +8 similar issues
[39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-glk-j4005/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
- fi-snb-2600: NOTRUN -> [SKIP][40] ([fdo#109271]) +8 similar issues
[40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-snb-2600/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW):
- fi-skl-6700k2: NOTRUN -> [SKIP][41] ([fdo#109271]) +4 similar issues
[41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-6700k2/igt@kms_chamelium_hpd@dp-hpd-fast.html
* {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW):
- fi-cfl-8700k: NOTRUN -> [SKIP][42] ([fdo#109271]) +8 similar issues
[42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-cfl-8700k/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
- fi-blb-e6850: NOTRUN -> [SKIP][43] ([fdo#109271]) +8 similar issues
[43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-blb-e6850/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
- {bat-kbl-2}: NOTRUN -> [SKIP][44] ([fdo#109271]) +8 similar issues
[44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-kbl-2/igt@kms_chamelium_hpd@hdmi-hpd-fast.html
* {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW):
- fi-apl-guc: NOTRUN -> [SKIP][45] ([fdo#109271]) +8 similar issues
[45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-apl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-bsw-kefka: NOTRUN -> [SKIP][46] ([fdo#109271]) +8 similar issues
[46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-kefka/igt@kms_chamelium_hpd@vga-hpd-fast.html
- {bat-atsm-1}: NOTRUN -> [SKIP][47] ([i915#6078]) +7 similar issues
[47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-atsm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-skl-guc: NOTRUN -> [SKIP][48] ([fdo#109271]) +8 similar issues
[48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-skl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-ivb-3770: NOTRUN -> [SKIP][49] ([fdo#109271]) +8 similar issues
[49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-ivb-3770/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-elk-e7500: NOTRUN -> [SKIP][50] ([fdo#109271]) +8 similar issues
[50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-elk-e7500/igt@kms_chamelium_hpd@vga-hpd-fast.html
- fi-bsw-nick: NOTRUN -> [SKIP][51] ([fdo#109271]) +8 similar issues
[51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-bsw-nick/igt@kms_chamelium_hpd@vga-hpd-fast.html
* igt@kms_cursor_legacy@basic-busy-flip-before-cursor:
- fi-rkl-11600: NOTRUN -> [SKIP][52] ([i915#4103])
[52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_cursor_legacy@basic-busy-flip-before-cursor.html
* igt@kms_force_connector_basic@force-load-detect:
- fi-rkl-11600: NOTRUN -> [SKIP][53] ([fdo#109285] / [i915#4098])
[53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_force_connector_basic@force-load-detect.html
* igt@kms_psr@primary_page_flip:
- fi-rkl-11600: NOTRUN -> [SKIP][54] ([i915#1072]) +3 similar issues
[54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_psr@primary_page_flip.html
* igt@kms_setmode@basic-clone-single-crtc:
- fi-rkl-11600: NOTRUN -> [SKIP][55] ([i915#3555] / [i915#4098])
[55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@kms_setmode@basic-clone-single-crtc.html
* igt@prime_vgem@basic-read:
- fi-rkl-11600: NOTRUN -> [SKIP][56] ([fdo#109295] / [i915#3291] / [i915#3708]) +2 similar issues
[56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@prime_vgem@basic-read.html
* igt@prime_vgem@basic-userptr:
- fi-rkl-11600: NOTRUN -> [SKIP][57] ([fdo#109295] / [i915#3301] / [i915#3708])
[57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-rkl-11600/igt@prime_vgem@basic-userptr.html
* igt@runner@aborted:
- fi-icl-u2: NOTRUN -> [FAIL][58] ([i915#4312])
[58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/fi-icl-u2/igt@runner@aborted.html
#### Possible fixes ####
* igt@i915_selftest@live@hangcheck:
- bat-dg1-6: [INCOMPLETE][59] ([i915#4983]) -> [PASS][60]
[59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
[60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
* igt@i915_selftest@live@migrate:
- {bat-atsm-1}: [DMESG-FAIL][61] ([i915#7699]) -> [PASS][62]
[61]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-atsm-1/igt@i915_selftest@live@migrate.html
[62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-atsm-1/igt@i915_selftest@live@migrate.html
* igt@i915_selftest@live@slpc:
- bat-adlp-4: [DMESG-FAIL][63] ([i915#6367]) -> [PASS][64]
[63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-4/igt@i915_selftest@live@slpc.html
[64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-4/igt@i915_selftest@live@slpc.html
* igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1:
- {bat-adlp-9}: [DMESG-WARN][65] ([i915#2867]) -> [PASS][66]
[65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
[66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/bat-adlp-9/igt@kms_pipe_crc_basic@suspend-read-crc@pipe-c-dp-1.html
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
[fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
[i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
[i915#2190]: https://gitlab.freedesktop.org/drm/intel/issues/2190
[i915#2867]: https://gitlab.freedesktop.org/drm/intel/issues/2867
[i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
[i915#3291]: https://gitlab.freedesktop.org/drm/intel/issues/3291
[i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
[i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
[i915#3708]: https://gitlab.freedesktop.org/drm/intel/issues/3708
[i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
[i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
[i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
[i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
[i915#4817]: https://gitlab.freedesktop.org/drm/intel/issues/4817
[i915#4983]: https://gitlab.freedesktop.org/drm/intel/issues/4983
[i915#5334]: https://gitlab.freedesktop.org/drm/intel/issues/5334
[i915#6078]: https://gitlab.freedesktop.org/drm/intel/issues/6078
[i915#6257]: https://gitlab.freedesktop.org/drm/intel/issues/6257
[i915#6367]: https://gitlab.freedesktop.org/drm/intel/issues/6367
[i915#7336]: https://gitlab.freedesktop.org/drm/intel/issues/7336
[i915#7456]: https://gitlab.freedesktop.org/drm/intel/issues/7456
[i915#7561]: https://gitlab.freedesktop.org/drm/intel/issues/7561
[i915#7699]: https://gitlab.freedesktop.org/drm/intel/issues/7699
[i915#7763]: https://gitlab.freedesktop.org/drm/intel/issues/7763
Build changes
-------------
* CI: CI-20190529 -> None
* IGT: IGT_7106 -> IGTPW_8297
CI-20190529: 20190529
CI_DRM_12541: b832866fa6063614b3637598aca19aee3bc3039f @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_8297: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
IGT_7106: 8cce332bdc50d2b20d553d7a0221737f4399d031 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
Testlist changes
----------------
+++ 71 lines
--- 73 lines
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
[-- Attachment #2: Type: text/html, Size: 21385 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* [igt-dev] ✓ Fi.CI.IGT: success for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
2023-01-03 17:15 [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Mark Yacoub
` (2 preceding siblings ...)
2023-01-09 4:02 ` [igt-dev] ✓ Fi.CI.BAT: success " Patchwork
@ 2023-01-09 5:27 ` Patchwork
2023-01-09 15:51 ` [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Petri Latvala
4 siblings, 0 replies; 11+ messages in thread
From: Patchwork @ 2023-01-09 5:27 UTC (permalink / raw)
To: Mark Yacoub; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 37376 bytes --]
== Series Details ==
Series: Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6)
URL : https://patchwork.freedesktop.org/series/111501/
State : success
== Summary ==
CI Bug Log - changes from CI_DRM_12541_full -> IGTPW_8297_full
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
Participating hosts (13 -> 10)
------------------------------
Missing (3): pig-skl-6260u pig-kbl-iris pig-glk-j5005
Possible new issues
-------------------
Here are the unknown changes that may have been introduced in IGTPW_8297_full:
### IGT changes ###
#### Possible regressions ####
* {igt@kms_chamelium_edid@hdmi-edid-change-during-suspend} (NEW):
- {shard-rkl}: NOTRUN -> [SKIP][1] +59 similar issues
[1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-3/igt@kms_chamelium_edid@hdmi-edid-change-during-suspend.html
* {igt@kms_chamelium_frames@dp-crc-single} (NEW):
- {shard-tglu-9}: NOTRUN -> [SKIP][2] +4 similar issues
[2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-9/igt@kms_chamelium_frames@dp-crc-single.html
* {igt@kms_chamelium_hpd@hdmi-hpd} (NEW):
- {shard-tglu-10}: NOTRUN -> [SKIP][3] +10 similar issues
[3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-10/igt@kms_chamelium_hpd@hdmi-hpd.html
* {igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe} (NEW):
- {shard-tglu}: NOTRUN -> [SKIP][4] +20 similar issues
[4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-1/igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe.html
* {igt@kms_chamelium_hpd@hdmi-hpd-storm-disable} (NEW):
- {shard-dg1}: NOTRUN -> [SKIP][5] +59 similar issues
[5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-dg1-15/igt@kms_chamelium_hpd@hdmi-hpd-storm-disable.html
New tests
---------
New tests have been introduced between CI_DRM_12541_full and IGTPW_8297_full:
### New IGT tests (63) ###
* igt@kms_chamelium_audio@dp-audio:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_audio@dp-audio-edid:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_audio@hdmi-audio:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_audio@hdmi-audio-edid:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-0-25:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-0-50:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-0-75:
- Statuses : 5 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-blue-to-red:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-green-to-red:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-limited-range:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-max:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-negative:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@ctm-red-to-blue:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@degamma:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_color@gamma:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@dp-edid-change-during-suspend:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@dp-edid-read:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@dp-edid-resolution-list:
- Statuses : 5 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@dp-edid-stress-resolution-4k:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@dp-edid-stress-resolution-non-4k:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@dp-mode-timings:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-change-during-suspend:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-read:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-stress-resolution-4k:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-edid-stress-resolution-non-4k:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@hdmi-mode-timings:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_edid@vga-edid-read:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-crc-fast:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-crc-multiple:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-crc-single:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@dp-frame-dump:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-aspect-ratio:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-cmp-planar-formats:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-fast:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-multiple:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-nonplanar-formats:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-crc-single:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@hdmi-frame-dump:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_frames@vga-frame-dump:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@common-hpd-after-suspend:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-after-suspend:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-enable-disable-mode:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-fast:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-for-each-pipe:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-storm:
- Statuses : 5 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-storm-disable:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@dp-hpd-with-enabled-mode:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-after-suspend:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-enable-disable-mode:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-fast:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-storm:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-storm-disable:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@hdmi-hpd-with-enabled-mode:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-after-suspend:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-enable-disable-mode:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-fast:
- Statuses : 8 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-for-each-pipe:
- Statuses : 6 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-with-enabled-mode:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
* igt@kms_chamelium_hpd@vga-hpd-without-ddc:
- Statuses : 7 skip(s)
- Exec time: [0.0] s
Known issues
------------
Here are the changes found in IGTPW_8297_full that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@gem_caching@read-writes:
- shard-apl: [PASS][6] -> ([INCOMPLETE][7], [PASS][8]) ([i915#7708]) +1 similar issue
[6]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-apl1/igt@gem_caching@read-writes.html
[7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl3/igt@gem_caching@read-writes.html
[8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@gem_caching@read-writes.html
* igt@gem_exec_create@forked@smem:
- shard-glk: [PASS][9] -> [DMESG-WARN][10] ([i915#118])
[9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk7/igt@gem_exec_create@forked@smem.html
[10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk7/igt@gem_exec_create@forked@smem.html
* igt@gem_exec_fair@basic-none-share@rcs0:
- shard-glk: [PASS][11] -> [FAIL][12] ([i915#2842]) +3 similar issues
[11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk5/igt@gem_exec_fair@basic-none-share@rcs0.html
[12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk6/igt@gem_exec_fair@basic-none-share@rcs0.html
* igt@gem_exec_fair@basic-none-solo@rcs0:
- shard-apl: [PASS][13] -> [FAIL][14] ([i915#2842])
[13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-apl3/igt@gem_exec_fair@basic-none-solo@rcs0.html
[14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl2/igt@gem_exec_fair@basic-none-solo@rcs0.html
* igt@gem_lmem_swapping@massive-random:
- shard-glk: NOTRUN -> [SKIP][15] ([fdo#109271] / [i915#4613])
[15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk3/igt@gem_lmem_swapping@massive-random.html
* igt@gen9_exec_parse@allowed-single:
- shard-glk: [PASS][16] -> [DMESG-WARN][17] ([i915#5566] / [i915#716])
[16]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk9/igt@gen9_exec_parse@allowed-single.html
[17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk1/igt@gen9_exec_parse@allowed-single.html
* igt@i915_pm_rps@engine-order:
- shard-apl: [PASS][18] -> ([PASS][19], [FAIL][20]) ([i915#6537])
[18]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-apl1/igt@i915_pm_rps@engine-order.html
[19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl1/igt@i915_pm_rps@engine-order.html
[20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl7/igt@i915_pm_rps@engine-order.html
* igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc:
- shard-apl: NOTRUN -> ([SKIP][21], [SKIP][22]) ([fdo#109271] / [i915#3886]) +2 similar issues
[21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl1/igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc.html
[22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@kms_ccs@pipe-b-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc.html
* igt@kms_ccs@pipe-c-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc:
- shard-glk: NOTRUN -> [SKIP][23] ([fdo#109271] / [i915#3886]) +2 similar issues
[23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk6/igt@kms_ccs@pipe-c-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc.html
* {igt@kms_chamelium_color@ctm-0-75} (NEW):
- shard-apl: NOTRUN -> ([SKIP][24], [SKIP][25]) ([fdo#109271]) +83 similar issues
[24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@kms_chamelium_color@ctm-0-75.html
[25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl3/igt@kms_chamelium_color@ctm-0-75.html
- shard-snb: NOTRUN -> [SKIP][26] ([fdo#109271]) +5 similar issues
[26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-snb7/igt@kms_chamelium_color@ctm-0-75.html
* {igt@kms_chamelium_color@ctm-blue-to-red} (NEW):
- shard-glk: NOTRUN -> [SKIP][27] ([fdo#109271]) +112 similar issues
[27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk2/igt@kms_chamelium_color@ctm-blue-to-red.html
* {igt@kms_chamelium_hpd@common-hpd-after-suspend} (NEW):
- shard-apl: NOTRUN -> [SKIP][28] ([fdo#109271]) +20 similar issues
[28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl7/igt@kms_chamelium_hpd@common-hpd-after-suspend.html
* {igt@kms_chamelium_hpd@hdmi-hpd-storm} (NEW):
- shard-snb: NOTRUN -> ([SKIP][29], [SKIP][30]) ([fdo#109271]) +56 similar issues
[29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-snb5/igt@kms_chamelium_hpd@hdmi-hpd-storm.html
[30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-snb2/igt@kms_chamelium_hpd@hdmi-hpd-storm.html
* igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size:
- shard-apl: NOTRUN -> ([PASS][31], [FAIL][32]) ([i915#2346])
[31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl1/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html
[32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl7/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html
* igt@kms_plane_alpha_blend@alpha-opaque-fb@pipe-a-dp-1:
- shard-apl: NOTRUN -> ([FAIL][33], [FAIL][34]) ([i915#4573]) +2 similar issues
[33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@kms_plane_alpha_blend@alpha-opaque-fb@pipe-a-dp-1.html
[34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl1/igt@kms_plane_alpha_blend@alpha-opaque-fb@pipe-a-dp-1.html
* igt@kms_psr2_sf@cursor-plane-move-continuous-exceed-sf:
- shard-glk: NOTRUN -> [SKIP][35] ([fdo#109271] / [i915#658])
[35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk5/igt@kms_psr2_sf@cursor-plane-move-continuous-exceed-sf.html
* igt@kms_psr2_sf@overlay-primary-update-sf-dmg-area:
- shard-apl: NOTRUN -> [SKIP][36] ([fdo#109271] / [i915#658])
[36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@kms_psr2_sf@overlay-primary-update-sf-dmg-area.html
* igt@runner@aborted:
- shard-glk: NOTRUN -> [FAIL][37] ([i915#4312])
[37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk1/igt@runner@aborted.html
#### Possible fixes ####
* igt@fbdev@unaligned-write:
- {shard-rkl}: [SKIP][38] ([i915#2582]) -> [PASS][39]
[38]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-1/igt@fbdev@unaligned-write.html
[39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@fbdev@unaligned-write.html
* igt@gem_create@hog-create@smem0:
- {shard-rkl}: [FAIL][40] ([i915#7679]) -> [PASS][41]
[40]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-6/igt@gem_create@hog-create@smem0.html
[41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@gem_create@hog-create@smem0.html
* igt@gem_eio@in-flight-suspend:
- {shard-rkl}: [FAIL][42] ([fdo#103375]) -> [PASS][43]
[42]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-4/igt@gem_eio@in-flight-suspend.html
[43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-1/igt@gem_eio@in-flight-suspend.html
* igt@gem_exec_fair@basic-pace-share@rcs0:
- shard-glk: [FAIL][44] ([i915#2842]) -> [PASS][45]
[44]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk2/igt@gem_exec_fair@basic-pace-share@rcs0.html
[45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk1/igt@gem_exec_fair@basic-pace-share@rcs0.html
* igt@gem_exec_flush@basic-batch-kernel-default-cmd:
- {shard-rkl}: [SKIP][46] ([fdo#109313]) -> [PASS][47]
[46]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-2/igt@gem_exec_flush@basic-batch-kernel-default-cmd.html
[47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@gem_exec_flush@basic-batch-kernel-default-cmd.html
* igt@gem_exec_reloc@basic-cpu-gtt-noreloc:
- {shard-rkl}: [SKIP][48] ([i915#3281]) -> [PASS][49] +6 similar issues
[48]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-1/igt@gem_exec_reloc@basic-cpu-gtt-noreloc.html
[49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@gem_exec_reloc@basic-cpu-gtt-noreloc.html
* igt@gem_exec_schedule@semaphore-power:
- {shard-rkl}: [SKIP][50] ([i915#7276]) -> [PASS][51]
[50]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-3/igt@gem_exec_schedule@semaphore-power.html
[51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@gem_exec_schedule@semaphore-power.html
* igt@gem_exec_schedule@wide@rcs0:
- {shard-tglu-10}: [INCOMPLETE][52] ([i915#6772]) -> [PASS][53]
[52]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-tglu-10/igt@gem_exec_schedule@wide@rcs0.html
[53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-10/igt@gem_exec_schedule@wide@rcs0.html
* igt@gem_partial_pwrite_pread@writes-after-reads-uncached:
- shard-apl: [INCOMPLETE][54] ([i915#7708]) -> ([PASS][55], [PASS][56])
[54]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-apl3/igt@gem_partial_pwrite_pread@writes-after-reads-uncached.html
[55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@gem_partial_pwrite_pread@writes-after-reads-uncached.html
[56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl7/igt@gem_partial_pwrite_pread@writes-after-reads-uncached.html
* igt@gem_tiled_partial_pwrite_pread@writes:
- {shard-rkl}: [SKIP][57] ([i915#3282]) -> [PASS][58] +5 similar issues
[57]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-6/igt@gem_tiled_partial_pwrite_pread@writes.html
[58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@gem_tiled_partial_pwrite_pread@writes.html
* igt@gen9_exec_parse@allowed-all:
- {shard-rkl}: [SKIP][59] ([i915#2527]) -> [PASS][60] +1 similar issue
[59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-6/igt@gen9_exec_parse@allowed-all.html
[60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@gen9_exec_parse@allowed-all.html
* igt@i915_pm_dc@dc5-psr:
- {shard-rkl}: [SKIP][61] ([i915#658]) -> [PASS][62]
[61]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-4/igt@i915_pm_dc@dc5-psr.html
[62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@i915_pm_dc@dc5-psr.html
* igt@i915_pm_dc@dc6-dpms:
- {shard-rkl}: [SKIP][63] ([i915#3361]) -> [PASS][64]
[63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-5/igt@i915_pm_dc@dc6-dpms.html
[64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-4/igt@i915_pm_dc@dc6-dpms.html
* igt@i915_pm_rc6_residency@rc6-idle@rcs0:
- {shard-dg1}: [FAIL][65] ([i915#3591]) -> [PASS][66]
[65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-dg1-15/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html
[66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-dg1-19/igt@i915_pm_rc6_residency@rc6-idle@rcs0.html
* igt@i915_pm_rpm@dpms-lpsp:
- {shard-rkl}: [SKIP][67] ([i915#1397]) -> [PASS][68] +1 similar issue
[67]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-2/igt@i915_pm_rpm@dpms-lpsp.html
[68]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@i915_pm_rpm@dpms-lpsp.html
* igt@i915_pm_rpm@modeset-lpsp-stress:
- {shard-dg1}: [SKIP][69] ([i915#1397]) -> [PASS][70]
[69]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-dg1-15/igt@i915_pm_rpm@modeset-lpsp-stress.html
[70]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-dg1-14/igt@i915_pm_rpm@modeset-lpsp-stress.html
* igt@kms_big_fb@linear-64bpp-rotate-180:
- {shard-rkl}: [SKIP][71] ([i915#1845] / [i915#4098]) -> [PASS][72] +7 similar issues
[71]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-3/igt@kms_big_fb@linear-64bpp-rotate-180.html
[72]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@kms_big_fb@linear-64bpp-rotate-180.html
* igt@kms_big_fb@x-tiled-addfb-size-offset-overflow:
- {shard-tglu}: [SKIP][73] ([i915#1845] / [i915#7651]) -> [PASS][74] +2 similar issues
[73]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-tglu-6/igt@kms_big_fb@x-tiled-addfb-size-offset-overflow.html
[74]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-1/igt@kms_big_fb@x-tiled-addfb-size-offset-overflow.html
* igt@kms_cursor_legacy@2x-long-cursor-vs-flip-atomic:
- shard-glk: [FAIL][75] ([i915#6510]) -> [PASS][76]
[75]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk6/igt@kms_cursor_legacy@2x-long-cursor-vs-flip-atomic.html
[76]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk3/igt@kms_cursor_legacy@2x-long-cursor-vs-flip-atomic.html
* igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size:
- shard-glk: [FAIL][77] ([i915#2346]) -> [PASS][78]
[77]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk8/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html
[78]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk1/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html
* igt@kms_fbcon_fbt@psr-suspend:
- {shard-rkl}: [SKIP][79] ([fdo#110189] / [i915#3955]) -> [PASS][80]
[79]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-2/igt@kms_fbcon_fbt@psr-suspend.html
[80]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@kms_fbcon_fbt@psr-suspend.html
* igt@kms_flip@flip-vs-expired-vblank-interruptible@a-dp1:
- shard-apl: [FAIL][81] ([i915#79]) -> ([PASS][82], [PASS][83])
[81]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-apl6/igt@kms_flip@flip-vs-expired-vblank-interruptible@a-dp1.html
[82]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl7/igt@kms_flip@flip-vs-expired-vblank-interruptible@a-dp1.html
[83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl3/igt@kms_flip@flip-vs-expired-vblank-interruptible@a-dp1.html
* igt@kms_flip@plain-flip-ts-check@c-hdmi-a1:
- shard-glk: [FAIL][84] ([i915#2122]) -> [PASS][85]
[84]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-glk4/igt@kms_flip@plain-flip-ts-check@c-hdmi-a1.html
[85]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-glk3/igt@kms_flip@plain-flip-ts-check@c-hdmi-a1.html
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-msflip-blt:
- {shard-tglu}: [SKIP][86] ([i915#1849]) -> [PASS][87] +2 similar issues
[86]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-tglu-6/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-msflip-blt.html
[87]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-1/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-msflip-blt.html
* igt@kms_frontbuffer_tracking@fbc-tiling-linear:
- {shard-dg1}: [DMESG-WARN][88] ([i915#4391]) -> [PASS][89]
[88]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-dg1-16/igt@kms_frontbuffer_tracking@fbc-tiling-linear.html
[89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-dg1-15/igt@kms_frontbuffer_tracking@fbc-tiling-linear.html
* igt@kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-mmap-cpu:
- {shard-rkl}: [SKIP][90] ([i915#1849] / [i915#4098]) -> [PASS][91] +2 similar issues
[90]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-2/igt@kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-mmap-cpu.html
[91]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-mmap-cpu.html
* igt@kms_psr@sprite_render:
- {shard-rkl}: [SKIP][92] ([i915#1072]) -> [PASS][93]
[92]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-1/igt@kms_psr@sprite_render.html
[93]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-6/igt@kms_psr@sprite_render.html
* igt@kms_universal_plane@universal-plane-pipe-b-sanity:
- {shard-tglu}: [SKIP][94] ([fdo#109274]) -> [PASS][95]
[94]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-tglu-6/igt@kms_universal_plane@universal-plane-pipe-b-sanity.html
[95]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-1/igt@kms_universal_plane@universal-plane-pipe-b-sanity.html
* igt@kms_vblank@pipe-d-ts-continuation-dpms-rpm:
- {shard-tglu}: [SKIP][96] ([i915#7651]) -> [PASS][97] +6 similar issues
[96]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-tglu-6/igt@kms_vblank@pipe-d-ts-continuation-dpms-rpm.html
[97]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-tglu-3/igt@kms_vblank@pipe-d-ts-continuation-dpms-rpm.html
* igt@perf@gen8-unprivileged-single-ctx-counters:
- {shard-rkl}: [SKIP][98] ([i915#2436]) -> [PASS][99]
[98]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-rkl-1/igt@perf@gen8-unprivileged-single-ctx-counters.html
[99]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-rkl-5/igt@perf@gen8-unprivileged-single-ctx-counters.html
#### Warnings ####
* igt@gem_partial_pwrite_pread@writes-after-reads:
- shard-apl: [INCOMPLETE][100] ([i915#7708]) -> ([PASS][101], [INCOMPLETE][102]) ([i915#7708])
[100]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12541/shard-apl2/igt@gem_partial_pwrite_pread@writes-after-reads.html
[101]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl6/igt@gem_partial_pwrite_pread@writes-after-reads.html
[102]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/shard-apl2/igt@gem_partial_pwrite_pread@writes-after-reads.html
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#103375]: https://bugs.freedesktop.org/show_bug.cgi?id=103375
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#109274]: https://bugs.freedesktop.org/show_bug.cgi?id=109274
[fdo#109279]: https://bugs.freedesktop.org/show_bug.cgi?id=109279
[fdo#109280]: https://bugs.freedesktop.org/show_bug.cgi?id=109280
[fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
[fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289
[fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295
[fdo#109300]: https://bugs.freedesktop.org/show_bug.cgi?id=109300
[fdo#109307]: https://bugs.freedesktop.org/show_bug.cgi?id=109307
[fdo#109309]: https://bugs.freedesktop.org/show_bug.cgi?id=109309
[fdo#109312]: https://bugs.freedesktop.org/show_bug.cgi?id=109312
[fdo#109313]: https://bugs.freedesktop.org/show_bug.cgi?id=109313
[fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315
[fdo#109642]: https://bugs.freedesktop.org/show_bug.cgi?id=109642
[fdo#110189]: https://bugs.freedesktop.org/show_bug.cgi?id=110189
[fdo#110723]: https://bugs.freedesktop.org/show_bug.cgi?id=110723
[fdo#111068]: https://bugs.freedesktop.org/show_bug.cgi?id=111068
[fdo#111614]: https://bugs.freedesktop.org/show_bug.cgi?id=111614
[fdo#111615]: https://bugs.freedesktop.org/show_bug.cgi?id=111615
[fdo#111644]: https://bugs.freedesktop.org/show_bug.cgi?id=111644
[fdo#111825]: https://bugs.freedesktop.org/show_bug.cgi?id=111825
[fdo#112054]: https://bugs.freedesktop.org/show_bug.cgi?id=112054
[fdo#112283]: https://bugs.freedesktop.org/show_bug.cgi?id=112283
[i915#1072]: https://gitlab.freedesktop.org/drm/intel/issues/1072
[i915#118]: https://gitlab.freedesktop.org/drm/intel/issues/118
[i915#132]: https://gitlab.freedesktop.org/drm/intel/issues/132
[i915#1397]: https://gitlab.freedesktop.org/drm/intel/issues/1397
[i915#1722]: https://gitlab.freedesktop.org/drm/intel/issues/1722
[i915#1755]: https://gitlab.freedesktop.org/drm/intel/issues/1755
[i915#1825]: https://gitlab.freedesktop.org/drm/intel/issues/1825
[i915#1839]: https://gitlab.freedesktop.org/drm/intel/issues/1839
[i915#1845]: https://gitlab.freedesktop.org/drm/intel/issues/1845
[i915#1849]: https://gitlab.freedesktop.org/drm/intel/issues/1849
[i915#2122]: https://gitlab.freedesktop.org/drm/intel/issues/2122
[i915#2232]: https://gitlab.freedesktop.org/drm/intel/issues/2232
[i915#2346]: https://gitlab.freedesktop.org/drm/intel/issues/2346
[i915#2436]: https://gitlab.freedesktop.org/drm/intel/issues/2436
[i915#2437]: https://gitlab.freedesktop.org/drm/intel/issues/2437
[i915#2527]: https://gitlab.freedesktop.org/drm/intel/issues/2527
[i915#2575]: https://gitlab.freedesktop.org/drm/intel/issues/2575
[i915#2582]: https://gitlab.freedesktop.org/drm/intel/issues/2582
[i915#2587]: https://gitlab.freedesktop.org/drm/intel/issues/2587
[i915#2658]: https://gitlab.freedesktop.org/drm/intel/issues/2658
[i915#2672]: https://gitlab.freedesktop.org/drm/intel/issues/2672
[i915#2681]: https://gitlab.freedesktop.org/drm/intel/issues/2681
[i915#2705]: https://gitlab.freedesktop.org/drm/intel/issues/2705
[i915#280]: https://gitlab.freedesktop.org/drm/intel/issues/280
[i915#284]: https://gitlab.freedesktop.org/drm/intel/issues/284
[i915#2842]: https://gitlab.freedesktop.org/drm/intel/issues/2842
[i915#2846]: https://gitlab.freedesktop.org/drm/intel/issues/2846
[i915#2856]: https://gitlab.freedesktop.org/drm/intel/issues/2856
[i915#2920]: https://gitlab.freedesktop.org/drm/intel/issues/2920
[i915#2994]: https://gitlab.freedesktop.org/drm/intel/issues/2994
[i915#3116]: https://gitlab.freedesktop.org/drm/intel/issues/3116
[i915#315]: https://gitlab.freedesktop.org/drm/intel/issues/315
[i915#3281]: https://gitlab.freedesktop.org/drm/intel/issues/3281
[i915#3282]: https://gitlab.freedesktop.org/drm/intel/issues/3282
[i915#3297]: https://gitlab.freedesktop.org/drm/intel/issues/3297
[i915#3299]: https://gitlab.freedesktop.org/drm/intel/issues/3299
[i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
[i915#3318]: https://gitlab.freedesktop.org/drm/intel/issues/3318
[i915#3323]: https://gitlab.freedesktop.org/drm/intel/issues/3323
[i915#3359]: https://gitlab.freedesktop.org/drm/intel/issues/3359
[i915#3361]: https://gitlab.freedesktop.org/drm/intel/issues/3361
[i915#3458]: https://gitlab.freedesktop.org/drm/intel/issues/3458
[i915#3469]: https://gitlab.freedesktop.org/drm/intel/issues/3469
[i915#3546]: https://gitlab.freedesktop.org/drm/intel/issues/3546
[i915#3547]: https://gitlab.freedesktop.org/drm/intel/issues/3547
[i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
[i915#3591]: https://gitlab.freedesktop.org/drm/intel/issues/3591
[i915#3637]: https://gitlab.freedesktop.org/drm/intel/issues/3637
[i915#3638]: https://gitlab.freedesktop.org/drm/intel/issues/3638
[i915#3639]: https://gitlab.freedesktop.org/drm/intel/issues/3639
[i915#3689]: https://gitlab.freedesktop.org/drm/intel/issues/3689
[i915#3734]: https://gitlab.freedesktop.org/drm/intel/issues/3734
[i915#3742]: https://gitlab.freedesktop.org/drm/intel/issues/3742
[i915#3804]: https://gitlab.freedesktop.org/drm/intel/issues/3804
[i915#3826]: https://gitlab.freedesktop.org/drm/intel/issues/3826
[i915#3840]: https://gitlab.freedesktop.org/drm/intel/issues/3840
[i915#3886]: https://gitlab.freedesktop.org/drm/intel/issues/3886
[i915#3955]: https://gitlab.freedesktop.org/drm/intel/issues/3955
[i915#4070]: https://gitlab.freedesktop.org/drm/intel/issues/4070
[i915#4078]: https://gitlab.freedesktop.org/drm/intel/issues/4078
[i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098
[i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103
[i915#426]: https://gitlab.freedesktop.org/drm/intel/issues/426
[i915#4270]: https://gitlab.freedesktop.org/drm/intel/issues/4270
[i915#4281]: https://gitlab.freedesktop.org/drm/intel/issues/4281
[i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
[i915#4349]: https://gitlab.freedesktop.org/drm/intel/issues/4349
[i915#4391]: https://gitlab.freedesktop.org/drm/intel/issues/4391
[i915#4573]: https://gitlab.freedesktop.org/drm/intel/issues/4573
[i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
[i915#4767]: https://gitlab.freedesktop.org/drm/intel/issues/4767
[i915#4833]: https://gitlab.freedesktop.org/drm/intel/issues/4833
[i915#4880]: https://gitlab.freedesktop.org/drm/intel/issues/4880
[i915#4983]: https://gitlab.freedesktop.org/drm/intel/issues/4983
[i915#5176]: https://gitlab.freedesktop.org/drm/intel/issues/5176
[i915#5235]: https://gitlab.freedesktop.org/drm/intel/issues/5235
[i915#5286]: https://gitlab.freedesktop.org/drm/intel/issues/5286
[i915#5288]: https://gitlab.freedesktop.org/drm/intel/issues/5288
[i915#5289]: https://gitlab.freedesktop.org/drm/intel/issues/5289
[i915#5325]: https://gitlab.freedesktop.org/drm/intel/issues/5325
[i915#5327]: https://gitlab.freedesktop.org/drm/intel/issues/5327
[i915#533]: https://gitlab.freedesktop.org/drm/intel/issues/533
[i915#5439]: https://gitlab.freedesktop.org/drm/intel/issues/5439
[i915#5566]: https://gitlab.freedesktop.org/drm/intel/issues/5566
[i915#5723]: https://gitlab.freedesktop.org/drm/intel/issues/5723
[i915#5784]: https://gitlab.freedesktop.org/drm/intel/issues/5784
[i915#6095]: https://gitlab.freedesktop.org/drm/intel/issues/6095
[i915#6230]: https://gitlab.freedesktop.org/drm/intel/issues/6230
[i915#6247]: https://gitlab.freedesktop.org/drm/intel/issues/6247
[i915#6248]: https://gitlab.freedesktop.org/drm/intel/issues/6248
[i915#6252]: https://gitlab.freedesktop.org/drm/intel/issues/6252
[i915#6258]: https://gitlab.freedesktop.org/drm/intel/issues/6258
[i915#6301]: https://gitlab.freedesktop.org/drm/intel/issues/6301
[i915#6334]: https://gitlab.freedesktop.org/drm/intel/issues/6334
[i915#6344]: https://gitlab.freedesktop.org/drm/intel/issues/6344
[i915#6433]: https://gitlab.freedesktop.org/drm/intel/issues/6433
[i915#6463]: https://gitlab.freedesktop.org/drm/intel/issues/6463
[i915#6497]: https://gitlab.freedesktop.org/drm/intel/issues/6497
[i915#6510]: https://gitlab.freedesktop.org/drm/intel/issues/6510
[i915#6524]: https://gitlab.freedesktop.org/drm/intel/issues/6524
[i915#6537]: https://gitlab.freedesktop.org/drm/intel/issues/6537
[i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658
[i915#6590]: https://gitlab.freedesktop.org/drm/intel/issues/6590
[i915#6768]: https://gitlab.freedesktop.org/drm/intel/issues/6768
[i915#6772]: https://gitlab.freedesktop.org/drm/intel/issues/6772
[i915#6944]: https://gitlab.freedesktop.org/drm/intel/issues/6944
[i915#6946]: https://gitlab.freedesktop.org/drm/intel/issues/6946
[i915#6953]: https://gitlab.freedesktop.org/drm/intel/issues/6953
[i915#7052]: https://gitlab.freedesktop.org/drm/intel/issues/7052
[i915#7116]: https://gitlab.freedesktop.org/drm/intel/issues/7116
[i915#7118]: https://gitlab.freedesktop.org/drm/intel/issues/7118
[i915#7128]: https://gitlab.freedesktop.org/drm/intel/issues/7128
[i915#716]: https://gitlab.freedesktop.org/drm/intel/issues/716
[i915#7276]: https://gitlab.freedesktop.org/drm/intel/issues/7276
[i915#7294]: https://gitlab.freedesktop.org/drm/intel/issues/7294
[i915#7456]: https://gitlab.freedesktop.org/drm/intel/issues/7456
[i915#7561]: https://gitlab.freedesktop.org/drm/intel/issues/7561
[i915#7582]: https://gitlab.freedesktop.org/drm/intel/issues/7582
[i915#7651]: https://gitlab.freedesktop.org/drm/intel/issues/7651
[i915#7679]: https://gitlab.freedesktop.org/drm/intel/issues/7679
[i915#7697]: https://gitlab.freedesktop.org/drm/intel/issues/7697
[i915#7708]: https://gitlab.freedesktop.org/drm/intel/issues/7708
[i915#7742]: https://gitlab.freedesktop.org/drm/intel/issues/7742
[i915#79]: https://gitlab.freedesktop.org/drm/intel/issues/79
Build changes
-------------
* CI: CI-20190529 -> None
* IGT: IGT_7106 -> IGTPW_8297
* Piglit: piglit_4509 -> None
CI-20190529: 20190529
CI_DRM_12541: b832866fa6063614b3637598aca19aee3bc3039f @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_8297: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
IGT_7106: 8cce332bdc50d2b20d553d7a0221737f4399d031 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8297/index.html
[-- Attachment #2: Type: text/html, Size: 35322 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests
2023-01-03 17:15 [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Mark Yacoub
` (3 preceding siblings ...)
2023-01-09 5:27 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
@ 2023-01-09 15:51 ` Petri Latvala
4 siblings, 0 replies; 11+ messages in thread
From: Petri Latvala @ 2023-01-09 15:51 UTC (permalink / raw)
To: Mark Yacoub
Cc: robdclark, amstan, igt-dev, seanpaul, ihf, matthewtlam,
khaled.almahallawy, markyacoub
On Tue, Jan 03, 2023 at 12:15:15PM -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 <markyacoub@chromium.org>
Builds fine now, testlists and such verified.
Acked-by: Petri Latvala <petri.latvala@intel.com>
> ---
> 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 | 875 +++++
> ...olor_chamelium.c => kms_chamelium_color.c} | 0
> tests/chamelium/kms_chamelium_edid.c | 560 +++
> tests/chamelium/kms_chamelium_frames.c | 1085 ++++++
> tests/chamelium/kms_chamelium_helper.c | 347 ++
> tests/chamelium/kms_chamelium_helper.h | 91 +
> tests/chamelium/kms_chamelium_hpd.c | 529 +++
> 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, 3525 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 <stdint.h>
> +#include <stddef.h>
>
> #include <xf86drmMode.h>
>
> 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 <stdbool.h>
> +#include <stddef.h>
>
> #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 <lyude@redhat.com>
> - */
> -
> -#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 <fcntl.h>
> -#include <pthread.h>
> -#include <string.h>
> -#include <stdatomic.h>
> -// #include <stdio.h>
> -
> -// 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..6c6177fc
> --- /dev/null
> +++ b/tests/chamelium/kms_chamelium_audio.c
> @@ -0,0 +1,875 @@
> +/*
> + * 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 <lyude@redhat.com>
> + */
> +
> +#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..f7609932
> --- /dev/null
> +++ b/tests/chamelium/kms_chamelium_edid.c
> @@ -0,0 +1,560 @@
> +/*
> + * 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 <lyude@redhat.com>
> + */
> +
> +#include <fcntl.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <stdatomic.h>
> +#include <xf86drmMode.h>
> +
> +#include "config.h"
> +#include "igt.h"
> +#include "igt_chamelium.h"
> +#include "igt_edid.h"
> +#include "igt_eld.h"
> +#include "igt_vc4.h"
> +#include "igt_infoframe.h"
> +#include "kms_chamelium_helper.h"
> +#include "monitor_edids/dp_edids.h"
> +#include "monitor_edids/hdmi_edids.h"
> +#include "monitor_edids/monitor_edids_helper.h"
> +
> +#define MODE_CLOCK_ACCURACY 0.05 /* 5% */
> +
> +static void get_connectors_link_status_failed(chamelium_data_t *data,
> + bool *link_status_failed)
> +{
> + drmModeConnector *connector;
> + uint64_t link_status;
> + drmModePropertyPtr prop;
> + int p;
> +
> + for (p = 0; p < data->port_count; p++) {
> + connector = chamelium_port_get_connector(data->chamelium,
> + data->ports[p], false);
> +
> + igt_assert(kmstest_get_property(
> + data->drm_fd, connector->connector_id,
> + DRM_MODE_OBJECT_CONNECTOR, "link-status", NULL,
> + &link_status, &prop));
> +
> + link_status_failed[p] = link_status == DRM_MODE_LINK_STATUS_BAD;
> +
> + drmModeFreeProperty(prop);
> + drmModeFreeConnector(connector);
> + }
> +}
> +
> +static void check_mode(struct chamelium *chamelium, struct chamelium_port *port,
> + drmModeModeInfo *mode)
> +{
> + struct chamelium_video_params video_params = { 0 };
> + double mode_clock;
> + int mode_hsync_offset, mode_vsync_offset;
> + int mode_hsync_width, mode_vsync_width;
> + int mode_hsync_polarity, mode_vsync_polarity;
> +
> + chamelium_port_get_video_params(chamelium, port, &video_params);
> +
> + mode_clock = (double)mode->clock / 1000;
> +
> + if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_DisplayPort) {
> + /* this is what chamelium understands as offsets for DP */
> + mode_hsync_offset = mode->htotal - mode->hsync_start;
> + mode_vsync_offset = mode->vtotal - mode->vsync_start;
> + } else {
> + /* and this is what they are for other connectors */
> + mode_hsync_offset = mode->hsync_start - mode->hdisplay;
> + mode_vsync_offset = mode->vsync_start - mode->vdisplay;
> + }
> +
> + mode_hsync_width = mode->hsync_end - mode->hsync_start;
> + mode_vsync_width = mode->vsync_end - mode->vsync_start;
> +
> + mode_hsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PHSYNC);
> + mode_vsync_polarity = !!(mode->flags & DRM_MODE_FLAG_PVSYNC);
> +
> + igt_debug("Checking video mode:\n");
> + igt_debug("clock: got %f, expected %f ± %f%%\n", video_params.clock,
> + mode_clock, MODE_CLOCK_ACCURACY * 100);
> + igt_debug("hactive: got %d, expected %d\n", video_params.hactive,
> + mode->hdisplay);
> + igt_debug("vactive: got %d, expected %d\n", video_params.vactive,
> + mode->vdisplay);
> + igt_debug("hsync_offset: got %d, expected %d\n",
> + video_params.hsync_offset, mode_hsync_offset);
> + igt_debug("vsync_offset: got %d, expected %d\n",
> + video_params.vsync_offset, mode_vsync_offset);
> + igt_debug("htotal: got %d, expected %d\n", video_params.htotal,
> + mode->htotal);
> + igt_debug("vtotal: got %d, expected %d\n", video_params.vtotal,
> + mode->vtotal);
> + igt_debug("hsync_width: got %d, expected %d\n",
> + video_params.hsync_width, mode_hsync_width);
> + igt_debug("vsync_width: got %d, expected %d\n",
> + video_params.vsync_width, mode_vsync_width);
> + igt_debug("hsync_polarity: got %d, expected %d\n",
> + video_params.hsync_polarity, mode_hsync_polarity);
> + igt_debug("vsync_polarity: got %d, expected %d\n",
> + video_params.vsync_polarity, mode_vsync_polarity);
> +
> + if (!isnan(video_params.clock)) {
> + igt_assert(video_params.clock >
> + mode_clock * (1 - MODE_CLOCK_ACCURACY));
> + igt_assert(video_params.clock <
> + mode_clock * (1 + MODE_CLOCK_ACCURACY));
> + }
> + igt_assert(video_params.hactive == mode->hdisplay);
> + igt_assert(video_params.vactive == mode->vdisplay);
> + igt_assert(video_params.hsync_offset == mode_hsync_offset);
> + igt_assert(video_params.vsync_offset == mode_vsync_offset);
> + igt_assert(video_params.htotal == mode->htotal);
> + igt_assert(video_params.vtotal == mode->vtotal);
> + igt_assert(video_params.hsync_width == mode_hsync_width);
> + igt_assert(video_params.vsync_width == mode_vsync_width);
> + igt_assert(video_params.hsync_polarity == mode_hsync_polarity);
> + igt_assert(video_params.vsync_polarity == mode_vsync_polarity);
> +}
> +
> +static const char igt_custom_edid_type_read_desc[] =
> + "Make sure the EDID exposed by KMS is the same as the screen's";
> +static void igt_custom_edid_type_read(chamelium_data_t *data,
> + struct chamelium_port *port,
> + enum igt_custom_edid_type edid)
> +{
> + drmModePropertyBlobPtr edid_blob = NULL;
> + drmModeConnector *connector;
> + size_t raw_edid_size;
> + const struct edid *raw_edid;
> + uint64_t edid_blob_id;
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + chamelium_set_edid(data, port, edid);
> + chamelium_plug(data->chamelium, port);
> + chamelium_wait_for_conn_status_change(&data->display, data->chamelium,
> + port, DRM_MODE_CONNECTED);
> +
> + igt_skip_on(chamelium_check_analog_bridge(data, port));
> +
> + connector = chamelium_port_get_connector(data->chamelium, port, true);
> + igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id,
> + DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL,
> + &edid_blob_id, NULL));
> + igt_assert(edid_blob_id != 0);
> + edid_blob = drmModeGetPropertyBlob(data->drm_fd, edid_blob_id);
> + igt_assert(edid_blob);
> +
> + raw_edid = chamelium_edid_get_raw(data->edids[edid], port);
> + raw_edid_size = edid_get_size(raw_edid);
> + igt_assert(memcmp(raw_edid, edid_blob->data, raw_edid_size) == 0);
> +
> + drmModeFreePropertyBlob(edid_blob);
> + drmModeFreeConnector(connector);
> +}
> +
> +static const char igt_edid_stress_resolution_desc[] =
> + "Stress test the DUT by testing multiple EDIDs, one right after the other, "
> + "and ensure their validity by check the real screen resolution vs the "
> + "advertised mode resultion.";
> +static void edid_stress_resolution(chamelium_data_t *data,
> + struct chamelium_port *port,
> + monitor_edid edids_list[],
> + size_t edids_list_len)
> +{
> + int i;
> + struct chamelium *chamelium = data->chamelium;
> + struct udev_monitor *mon = igt_watch_uevents();
> +
> + for (i = 0; i < edids_list_len; ++i) {
> + struct chamelium_edid *chamelium_edid;
> + drmModeModeInfo mode;
> + struct igt_fb fb = { 0 };
> + igt_output_t *output;
> + enum pipe pipe;
> + bool is_video_stable;
> + int screen_res_w, screen_res_h;
> +
> + monitor_edid *edid = &edids_list[i];
> + igt_info("Testing out the EDID for %s\n",
> + monitor_edid_get_name(edid));
> +
> + /* Getting and Setting the EDID on Chamelium. */
> + chamelium_edid =
> + get_chameleon_edid_from_monitor_edid(chamelium, edid);
> + chamelium_port_set_edid(data->chamelium, port, chamelium_edid);
> + free_chamelium_edid_from_monitor_edid(chamelium_edid);
> +
> + igt_flush_uevents(mon);
> + chamelium_plug(chamelium, port);
> + chamelium_wait_for_connector_after_hotplug(data, mon, port,
> + DRM_MODE_CONNECTED);
> + igt_flush_uevents(mon);
> +
> + /* Setting an output on the screen to turn it on. */
> + mode = chamelium_get_mode_for_port(chamelium, port);
> + chamelium_create_fb_for_mode(data, &fb, &mode);
> + output = chamelium_get_output_for_port(data, port);
> + pipe = chamelium_get_pipe_for_output(&data->display, output);
> + igt_output_set_pipe(output, pipe);
> + chamelium_enable_output(data, port, output, &mode, &fb);
> +
> + /* Capture the screen resolution and verify. */
> + is_video_stable = chamelium_port_wait_video_input_stable(
> + chamelium, port, 5);
> + igt_assert(is_video_stable);
> +
> + chamelium_port_get_resolution(chamelium, port, &screen_res_w,
> + &screen_res_h);
> + igt_assert(screen_res_w == fb.width);
> + igt_assert(screen_res_h == fb.height);
> +
> + // Clean up
> + igt_remove_fb(data->drm_fd, &fb);
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_unplug(chamelium, port);
> + }
> +
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +}
> +
> +static const char igt_edid_resolution_list_desc[] =
> + "Get an EDID with many modes of different configurations, set them on the screen and check the"
> + " screen resolution matches the mode resolution.";
> +
> +static void edid_resolution_list(chamelium_data_t *data,
> + struct chamelium_port *port)
> +{
> + struct chamelium *chamelium = data->chamelium;
> + struct udev_monitor *mon = igt_watch_uevents();
> + drmModeConnector *connector;
> + drmModeModeInfoPtr modes;
> + int count_modes;
> + int i;
> + igt_output_t *output;
> + enum pipe pipe;
> +
> + chamelium_unplug(chamelium, port);
> + chamelium_set_edid(data, port, IGT_CUSTOM_EDID_FULL);
> +
> + igt_flush_uevents(mon);
> + chamelium_plug(chamelium, port);
> + chamelium_wait_for_connector_after_hotplug(data, mon, port,
> + DRM_MODE_CONNECTED);
> + igt_flush_uevents(mon);
> +
> + connector = chamelium_port_get_connector(chamelium, port, true);
> + modes = connector->modes;
> + count_modes = connector->count_modes;
> +
> + output = chamelium_get_output_for_port(data, port);
> + pipe = chamelium_get_pipe_for_output(&data->display, output);
> + igt_output_set_pipe(output, pipe);
> +
> + for (i = 0; i < count_modes; ++i)
> + igt_debug("#%d %s %uHz\n", i, modes[i].name, modes[i].vrefresh);
> +
> + for (i = 0; i < count_modes; ++i) {
> + struct igt_fb fb = { 0 };
> + bool is_video_stable;
> + int screen_res_w, screen_res_h;
> +
> + igt_info("Testing #%d %s %uHz\n", i, modes[i].name,
> + modes[i].vrefresh);
> +
> + /* Set the screen mode with the one we chose. */
> + chamelium_create_fb_for_mode(data, &fb, &modes[i]);
> + chamelium_enable_output(data, port, output, &modes[i], &fb);
> + is_video_stable = chamelium_port_wait_video_input_stable(
> + chamelium, port, 10);
> + igt_assert(is_video_stable);
> +
> + chamelium_port_get_resolution(chamelium, port, &screen_res_w,
> + &screen_res_h);
> + igt_assert_eq(screen_res_w, modes[i].hdisplay);
> + igt_assert_eq(screen_res_h, modes[i].vdisplay);
> +
> + igt_remove_fb(data->drm_fd, &fb);
> + }
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + drmModeFreeConnector(connector);
> +}
> +
> +static const char test_suspend_resume_edid_change_desc[] =
> + "Simulate a screen being unplugged and another screen being plugged "
> + "during suspend, check that a uevent is sent and connector status is "
> + "updated";
> +static void test_suspend_resume_edid_change(chamelium_data_t *data,
> + struct chamelium_port *port,
> + enum igt_suspend_state state,
> + enum igt_suspend_test test,
> + enum igt_custom_edid_type edid,
> + enum igt_custom_edid_type alt_edid)
> +{
> + struct udev_monitor *mon = igt_watch_uevents();
> + bool link_status_failed[2][data->port_count];
> + int p;
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + /* Catch the event and flush all remaining ones. */
> + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
> + igt_flush_uevents(mon);
> +
> + /* First plug in the port */
> + chamelium_set_edid(data, port, edid);
> + chamelium_plug(data->chamelium, port);
> + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
> +
> + chamelium_wait_for_conn_status_change(&data->display, data->chamelium,
> + port, DRM_MODE_CONNECTED);
> +
> + /*
> + * Change the edid before we suspend. On resume, the machine should
> + * notice the EDID change and fire a hotplug event.
> + */
> + chamelium_set_edid(data, port, alt_edid);
> +
> + get_connectors_link_status_failed(data, link_status_failed[0]);
> +
> + igt_flush_uevents(mon);
> +
> + igt_system_suspend_autoresume(state, test);
> + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
> + chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
> +
> + get_connectors_link_status_failed(data, link_status_failed[1]);
> +
> + for (p = 0; p < data->port_count; p++)
> + igt_skip_on(!link_status_failed[0][p] &&
> + link_status_failed[1][p]);
> +}
> +
> +static const char test_mode_timings_desc[] =
> + "For each mode of the IGT base EDID, perform a modeset and check the "
> + "mode detected by the Chamelium receiver matches the mode we set";
> +static void test_mode_timings(chamelium_data_t *data,
> + struct chamelium_port *port)
> +{
> + int i, count_modes;
> +
> + i = 0;
> + igt_require(chamelium_supports_get_video_params(data->chamelium));
> + do {
> + igt_output_t *output;
> + igt_plane_t *primary;
> + drmModeConnector *connector;
> + drmModeModeInfo *mode;
> + int fb_id;
> + struct igt_fb fb;
> +
> + /*
> + * let's reset state each mode so we will get the
> + * HPD pulses realibably
> + */
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + /*
> + * modes may change due to mode pruining and link issues, so we
> + * need to refresh the connector
> + */
> + output = chamelium_prepare_output(data, port,
> + IGT_CUSTOM_EDID_BASE);
> + connector = chamelium_port_get_connector(data->chamelium, port,
> + false);
> + primary = igt_output_get_plane_type(output,
> + DRM_PLANE_TYPE_PRIMARY);
> + igt_assert(primary);
> +
> + /* we may skip some modes due to above but that's ok */
> + count_modes = connector->count_modes;
> + if (i >= count_modes)
> + break;
> +
> + mode = &connector->modes[i];
> +
> + fb_id = igt_create_color_pattern_fb(
> + data->drm_fd, mode->hdisplay, mode->vdisplay,
> + DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, 0, 0, 0,
> + &fb);
> + igt_assert(fb_id > 0);
> +
> + chamelium_enable_output(data, port, output, mode, &fb);
> +
> + /* Trigger the FSM */
> + chamelium_capture(data->chamelium, port, 0, 0, 0, 0, 0);
> +
> + check_mode(data->chamelium, port, mode);
> +
> + igt_remove_fb(data->drm_fd, &fb);
> + drmModeFreeConnector(connector);
> + } while (++i < count_modes);
> +}
> +
> +IGT_TEST_DESCRIPTION("Testing EDID with a Chamelium board");
> +igt_main
> +{
> + chamelium_data_t data;
> + struct chamelium_port *port;
> + int p;
> +
> + igt_fixture {
> + chamelium_init_test(&data);
> + }
> +
> + igt_describe("DisplayPort tests");
> + igt_subtest_group {
> + igt_fixture {
> + chamelium_require_connector_present(
> + data.ports, DRM_MODE_CONNECTOR_DisplayPort,
> + data.port_count, 1);
> + }
> +
> + igt_describe(igt_custom_edid_type_read_desc);
> + connector_subtest("dp-edid-read", DisplayPort)
> + {
> + igt_custom_edid_type_read(&data, port,
> + IGT_CUSTOM_EDID_BASE);
> + igt_custom_edid_type_read(&data, port,
> + IGT_CUSTOM_EDID_ALT);
> + }
> +
> + igt_describe(igt_edid_stress_resolution_desc);
> + connector_subtest("dp-edid-stress-resolution-4k", DisplayPort)
> + edid_stress_resolution(&data, port, DP_EDIDS_4K,
> + ARRAY_SIZE(DP_EDIDS_4K));
> +
> + igt_describe(igt_edid_stress_resolution_desc);
> + connector_subtest("dp-edid-stress-resolution-non-4k",
> + DisplayPort)
> + edid_stress_resolution(&data, port, DP_EDIDS_NON_4K,
> + ARRAY_SIZE(DP_EDIDS_NON_4K));
> +
> + igt_describe(igt_edid_resolution_list_desc);
> + connector_subtest("dp-edid-resolution-list", DisplayPort)
> + edid_resolution_list(&data, port);
> +
> + igt_describe(test_suspend_resume_edid_change_desc);
> + connector_subtest("dp-edid-change-during-suspend", DisplayPort)
> + test_suspend_resume_edid_change(&data, port,
> + SUSPEND_STATE_MEM,
> + SUSPEND_TEST_NONE,
> + IGT_CUSTOM_EDID_BASE,
> + IGT_CUSTOM_EDID_ALT);
> +
> + igt_describe(test_suspend_resume_edid_change_desc);
> + connector_subtest("dp-edid-change-during-hibernate",
> + DisplayPort)
> + test_suspend_resume_edid_change(&data, port,
> + SUSPEND_STATE_DISK,
> + SUSPEND_TEST_DEVICES,
> + IGT_CUSTOM_EDID_BASE,
> + IGT_CUSTOM_EDID_ALT);
> +
> + igt_describe(test_mode_timings_desc);
> + connector_subtest("dp-mode-timings", DisplayPort)
> + test_mode_timings(&data, port);
> + }
> +
> + igt_describe("HDMI tests");
> + igt_subtest_group {
> + igt_fixture {
> + chamelium_require_connector_present(
> + data.ports, DRM_MODE_CONNECTOR_HDMIA,
> + data.port_count, 1);
> + }
> +
> + igt_describe(igt_custom_edid_type_read_desc);
> + connector_subtest("hdmi-edid-read", HDMIA)
> + {
> + igt_custom_edid_type_read(&data, port,
> + IGT_CUSTOM_EDID_BASE);
> + igt_custom_edid_type_read(&data, port,
> + IGT_CUSTOM_EDID_ALT);
> + }
> +
> + igt_describe(igt_edid_stress_resolution_desc);
> + connector_subtest("hdmi-edid-stress-resolution-4k", HDMIA)
> + edid_stress_resolution(&data, port, HDMI_EDIDS_4K,
> + ARRAY_SIZE(HDMI_EDIDS_4K));
> +
> + igt_describe(igt_edid_stress_resolution_desc);
> + connector_subtest("hdmi-edid-stress-resolution-non-4k", HDMIA)
> + edid_stress_resolution(&data, port, HDMI_EDIDS_NON_4K,
> + ARRAY_SIZE(HDMI_EDIDS_NON_4K));
> +
> + igt_describe(test_suspend_resume_edid_change_desc);
> + connector_subtest("hdmi-edid-change-during-suspend", HDMIA)
> + test_suspend_resume_edid_change(&data, port,
> + SUSPEND_STATE_MEM,
> + SUSPEND_TEST_NONE,
> + IGT_CUSTOM_EDID_BASE,
> + IGT_CUSTOM_EDID_ALT);
> +
> + igt_describe(test_suspend_resume_edid_change_desc);
> + connector_subtest("hdmi-edid-change-during-hibernate", HDMIA)
> + test_suspend_resume_edid_change(&data, port,
> + SUSPEND_STATE_DISK,
> + SUSPEND_TEST_DEVICES,
> + IGT_CUSTOM_EDID_BASE,
> + IGT_CUSTOM_EDID_ALT);
> +
> + igt_describe(test_mode_timings_desc);
> + connector_subtest("hdmi-mode-timings", HDMIA)
> + test_mode_timings(&data, port);
> + }
> +
> + igt_describe("VGA tests");
> + igt_subtest_group {
> + igt_fixture {
> + chamelium_require_connector_present(
> + data.ports, DRM_MODE_CONNECTOR_VGA,
> + data.port_count, 1);
> + }
> +
> + igt_describe(igt_custom_edid_type_read_desc);
> + connector_subtest("vga-edid-read", VGA)
> + {
> + igt_custom_edid_type_read(&data, port,
> + IGT_CUSTOM_EDID_BASE);
> + igt_custom_edid_type_read(&data, port,
> + IGT_CUSTOM_EDID_ALT);
> + }
> + }
> +
> + igt_fixture {
> + igt_display_fini(&data.display);
> + close(data.drm_fd);
> + }
> +}
> diff --git a/tests/chamelium/kms_chamelium_frames.c b/tests/chamelium/kms_chamelium_frames.c
> new file mode 100644
> index 00000000..008bc34b
> --- /dev/null
> +++ b/tests/chamelium/kms_chamelium_frames.c
> @@ -0,0 +1,1085 @@
> +/*
> + * Copyright © 2016 Red Hat Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Authors:
> + * Lyude Paul <lyude@redhat.com>
> + */
> +
> +#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..197d29be
> --- /dev/null
> +++ b/tests/chamelium/kms_chamelium_helper.c
> @@ -0,0 +1,347 @@
> +/*
> + * 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 <lyude@redhat.com>
> + */
> +
> +#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..6dbde8b7
> --- /dev/null
> +++ b/tests/chamelium/kms_chamelium_helper.h
> @@ -0,0 +1,91 @@
> +/*
> + * 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 <lyude@redhat.com>
> + */
> +
> +#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..d9718d76
> --- /dev/null
> +++ b/tests/chamelium/kms_chamelium_hpd.c
> @@ -0,0 +1,529 @@
> +/*
> + * 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 <lyude@redhat.com>
> + */
> +
> +#include "kms_chamelium_helper.h"
> +
> +#define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */
> +#define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */
> +
> +#define HPD_TOGGLE_COUNT_VGA 5
> +#define HPD_TOGGLE_COUNT_DP_HDMI 15
> +#define HPD_TOGGLE_COUNT_FAST 3
> +
> +enum test_modeset_mode {
> + TEST_MODESET_ON,
> + TEST_MODESET_ON_OFF,
> + TEST_MODESET_OFF,
> +};
> +
> +static void try_suspend_resume_hpd(chamelium_data_t *data,
> + struct chamelium_port *port,
> + enum igt_suspend_state state,
> + enum igt_suspend_test test,
> + struct udev_monitor *mon, bool connected)
> +{
> + drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED :
> + DRM_MODE_CONNECTED;
> + int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
> + int delay;
> + int p;
> +
> + igt_flush_uevents(mon);
> +
> + delay = igt_get_autoresume_delay(state) * 1000 / 2;
> +
> + if (port) {
> + chamelium_schedule_hpd_toggle(data->chamelium, port, delay,
> + !connected);
> + } else {
> + for (p = 0; p < data->port_count; p++) {
> + port = data->ports[p];
> + chamelium_schedule_hpd_toggle(data->chamelium, port,
> + delay, !connected);
> + }
> +
> + port = NULL;
> + }
> +
> + igt_system_suspend_autoresume(state, test);
> + igt_assert(chamelium_wait_for_hotplug(mon, &timeout));
> + chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
> +
> + if (port) {
> + igt_assert_eq(chamelium_reprobe_connector(
> + &data->display, data->chamelium, port),
> + target_state);
> + } else {
> + for (p = 0; p < data->port_count; p++) {
> + drmModeConnection current_state;
> +
> + port = data->ports[p];
> + /*
> + * There could be as many hotplug events sent by
> + * driver as connectors we scheduled an HPD toggle on
> + * above, depending on timing. So if we're not seeing
> + * the expected connector state try to wait for an HPD
> + * event for each connector/port.
> + */
> + current_state = chamelium_reprobe_connector(
> + &data->display, data->chamelium, port);
> + if (p > 0 && current_state != target_state) {
> + igt_assert(chamelium_wait_for_hotplug(
> + mon, &timeout));
> + current_state = chamelium_reprobe_connector(
> + &data->display, data->chamelium, port);
> + }
> +
> + igt_assert_eq(current_state, target_state);
> + }
> +
> + port = NULL;
> + }
> +}
> +
> +static const char test_basic_hotplug_desc[] =
> + "Check that we get uevents and updated connector status on "
> + "hotplug and unplug";
> +static void test_hotplug(chamelium_data_t *data, struct chamelium_port *port,
> + int toggle_count, enum test_modeset_mode modeset_mode)
> +{
> + int i;
> + enum pipe pipe;
> + struct igt_fb fb = { 0 };
> + drmModeModeInfo mode;
> + struct udev_monitor *mon = igt_watch_uevents();
> + igt_output_t *output = chamelium_get_output_for_port(data, port);
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, NULL,
> + data->ports, data->port_count);
> +
> + igt_hpd_storm_set_threshold(data->drm_fd, 0);
> +
> + for (i = 0; i < toggle_count; i++) {
> + igt_flush_uevents(mon);
> +
> + /* Check if we get a sysfs hotplug event */
> + chamelium_plug(data->chamelium, port);
> +
> + chamelium_wait_for_connector_after_hotplug(data, mon, port,
> + DRM_MODE_CONNECTED);
> + igt_flush_uevents(mon);
> +
> + if (modeset_mode == TEST_MODESET_ON_OFF ||
> + (modeset_mode == TEST_MODESET_ON && i == 0)) {
> + if (i == 0) {
> + /* We can only get mode and pipe once we are
> + * connected */
> + output = chamelium_get_output_for_port(data,
> + port);
> + pipe = chamelium_get_pipe_for_output(
> + &data->display, output);
> + mode = chamelium_get_mode_for_port(
> + data->chamelium, port);
> + chamelium_create_fb_for_mode(data, &fb, &mode);
> + }
> +
> + igt_output_set_pipe(output, pipe);
> + chamelium_enable_output(data, port, output, &mode, &fb);
> + }
> +
> + /* Now check if we get a hotplug from disconnection */
> + chamelium_unplug(data->chamelium, port);
> +
> + chamelium_wait_for_connector_after_hotplug(
> + data, mon, port, DRM_MODE_DISCONNECTED);
> +
> + igt_flush_uevents(mon);
> +
> + if (modeset_mode == TEST_MODESET_ON_OFF) {
> + igt_output_set_pipe(output, PIPE_NONE);
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> + }
> + }
> +
> + igt_cleanup_uevents(mon);
> + igt_hpd_storm_reset(data->drm_fd);
> + igt_remove_fb(data->drm_fd, &fb);
> +}
> +
> +static const char test_hotplug_for_each_pipe_desc[] =
> + "Check that we get uevents and updated connector status on "
> + "hotplug and unplug for each pipe with valid output";
> +static void test_hotplug_for_each_pipe(chamelium_data_t *data,
> + struct chamelium_port *port)
> +{
> + igt_output_t *output;
> + enum pipe pipe;
> + struct udev_monitor *mon = igt_watch_uevents();
> +
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + igt_hpd_storm_set_threshold(data->drm_fd, 0);
> + /* Disconnect if any port got connected */
> + chamelium_unplug(data->chamelium, port);
> + chamelium_wait_for_connector_after_hotplug(data, mon, port,
> + DRM_MODE_DISCONNECTED);
> +
> + for_each_pipe(&data->display, pipe) {
> + igt_flush_uevents(mon);
> + /* Check if we get a sysfs hotplug event */
> + chamelium_plug(data->chamelium, port);
> + chamelium_wait_for_connector_after_hotplug(data, mon, port,
> + DRM_MODE_CONNECTED);
> + igt_flush_uevents(mon);
> + output = chamelium_get_output_for_port(data, port);
> +
> + /* If pipe is valid for output then set it */
> + if (igt_pipe_connector_valid(pipe, output)) {
> + igt_output_set_pipe(output, pipe);
> + igt_display_commit2(&data->display, COMMIT_ATOMIC);
> + }
> +
> + chamelium_unplug(data->chamelium, port);
> + chamelium_wait_for_connector_after_hotplug(
> + data, mon, port, DRM_MODE_DISCONNECTED);
> + igt_flush_uevents(mon);
> + }
> +
> + igt_cleanup_uevents(mon);
> + igt_hpd_storm_reset(data->drm_fd);
> +}
> +
> +static const char test_suspend_resume_hpd_desc[] =
> + "Toggle HPD during suspend, check that uevents are sent and connector "
> + "status is updated";
> +static void test_suspend_resume_hpd(chamelium_data_t *data,
> + struct chamelium_port *port,
> + enum igt_suspend_state state,
> + enum igt_suspend_test test)
> +{
> + struct udev_monitor *mon = igt_watch_uevents();
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + /* Make sure we notice new connectors after resuming */
> + try_suspend_resume_hpd(data, port, state, test, mon, false);
> +
> + /* Now make sure we notice disconnected connectors after resuming */
> + try_suspend_resume_hpd(data, port, state, test, mon, true);
> +
> + igt_cleanup_uevents(mon);
> +}
> +
> +static const char test_suspend_resume_hpd_common_desc[] =
> + "Toggle HPD during suspend on all connectors, check that uevents are "
> + "sent and connector status is updated";
> +static void test_suspend_resume_hpd_common(chamelium_data_t *data,
> + enum igt_suspend_state state,
> + enum igt_suspend_test test)
> +{
> + struct udev_monitor *mon = igt_watch_uevents();
> + struct chamelium_port *port;
> + int p;
> +
> + for (p = 0; p < data->port_count; p++) {
> + port = data->ports[p];
> + igt_debug("Testing port %s\n", chamelium_port_get_name(port));
> + }
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, NULL,
> + data->ports, data->port_count);
> +
> + /* Make sure we notice new connectors after resuming */
> + try_suspend_resume_hpd(data, NULL, state, test, mon, false);
> +
> + /* Now make sure we notice disconnected connectors after resuming */
> + try_suspend_resume_hpd(data, NULL, state, test, mon, true);
> +
> + igt_cleanup_uevents(mon);
> +}
> +
> +static const char test_hpd_without_ddc_desc[] =
> + "Disable DDC on a VGA connector, check we still get a uevent on hotplug";
> +static void test_hpd_without_ddc(chamelium_data_t *data,
> + struct chamelium_port *port)
> +{
> + struct udev_monitor *mon = igt_watch_uevents();
> +
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> + igt_flush_uevents(mon);
> +
> + /* Disable the DDC on the connector and make sure we still get a
> + * hotplug
> + */
> + chamelium_port_set_ddc_state(data->chamelium, port, false);
> + chamelium_plug(data->chamelium, port);
> +
> + igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
> + igt_assert_eq(chamelium_reprobe_connector(&data->display,
> + data->chamelium, port),
> + DRM_MODE_CONNECTED);
> +
> + igt_cleanup_uevents(mon);
> +}
> +
> +static const char test_hpd_storm_detect_desc[] =
> + "Trigger a series of hotplugs in a very small timeframe to simulate a"
> + "bad cable, check the kernel falls back to polling to avoid a hotplug "
> + "storm";
> +static void test_hpd_storm_detect(chamelium_data_t *data,
> + struct chamelium_port *port, int width)
> +{
> + struct udev_monitor *mon;
> + int count = 0;
> +
> + igt_require_hpd_storm_ctl(data->drm_fd);
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + igt_hpd_storm_set_threshold(data->drm_fd, 1);
> + chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
> + igt_assert(igt_hpd_storm_detected(data->drm_fd));
> +
> + mon = igt_watch_uevents();
> + chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
> +
> + /*
> + * Polling should have been enabled by the HPD storm at this point,
> + * so we should only get at most 1 hotplug event
> + */
> + igt_until_timeout(5)
> + count += igt_hotplug_detected(mon, 1);
> + igt_assert_lt(count, 2);
> +
> + igt_cleanup_uevents(mon);
> + igt_hpd_storm_reset(data->drm_fd);
> +}
> +
> +static const char test_hpd_storm_disable_desc[] =
> + "Disable HPD storm detection, trigger a storm and check the kernel "
> + "doesn't detect one";
> +static void test_hpd_storm_disable(chamelium_data_t *data,
> + struct chamelium_port *port, int width)
> +{
> + igt_require_hpd_storm_ctl(data->drm_fd);
> + igt_modeset_disable_all_outputs(&data->display);
> + chamelium_reset_state(&data->display, data->chamelium, port,
> + data->ports, data->port_count);
> +
> + igt_hpd_storm_set_threshold(data->drm_fd, 0);
> + chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
> + igt_assert(!igt_hpd_storm_detected(data->drm_fd));
> +
> + igt_hpd_storm_reset(data->drm_fd);
> +}
> +
> +IGT_TEST_DESCRIPTION("Testing HPD with a Chamelium board");
> +igt_main
> +{
> + chamelium_data_t data;
> + struct chamelium_port *port;
> + int p;
> +
> + igt_fixture {
> + chamelium_init_test(&data);
> + }
> +
> + igt_describe("DisplayPort tests");
> + igt_subtest_group {
> + igt_fixture {
> + chamelium_require_connector_present(
> + data.ports, DRM_MODE_CONNECTOR_DisplayPort,
> + data.port_count, 1);
> + }
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("dp-hpd", DisplayPort)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI,
> + TEST_MODESET_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("dp-hpd-fast", DisplayPort) test_hotplug(
> + &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("dp-hpd-enable-disable-mode", DisplayPort)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
> + TEST_MODESET_ON_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("dp-hpd-with-enabled-mode", DisplayPort)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
> + TEST_MODESET_ON);
> +
> + igt_describe(test_hotplug_for_each_pipe_desc);
> + connector_subtest("dp-hpd-for-each-pipe", DisplayPort)
> + test_hotplug_for_each_pipe(&data, port);
> +
> + igt_describe(test_suspend_resume_hpd_desc);
> + connector_subtest("dp-hpd-after-suspend", DisplayPort)
> + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
> + SUSPEND_TEST_NONE);
> +
> + igt_describe(test_suspend_resume_hpd_desc);
> + connector_subtest("dp-hpd-after-hibernate", DisplayPort)
> + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
> + SUSPEND_TEST_DEVICES);
> +
> + igt_describe(test_hpd_storm_detect_desc);
> + connector_subtest("dp-hpd-storm", DisplayPort)
> + test_hpd_storm_detect(&data, port,
> + HPD_STORM_PULSE_INTERVAL_DP);
> +
> + igt_describe(test_hpd_storm_disable_desc);
> + connector_subtest("dp-hpd-storm-disable", DisplayPort)
> + test_hpd_storm_disable(&data, port,
> + HPD_STORM_PULSE_INTERVAL_DP);
> + }
> +
> + igt_describe("HDMI tests");
> + igt_subtest_group {
> + igt_fixture {
> + chamelium_require_connector_present(
> + data.ports, DRM_MODE_CONNECTOR_HDMIA,
> + data.port_count, 1);
> + }
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("hdmi-hpd", HDMIA)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI,
> + TEST_MODESET_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("hdmi-hpd-fast", HDMIA) test_hotplug(
> + &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("hdmi-hpd-enable-disable-mode", HDMIA)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
> + TEST_MODESET_ON_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("hdmi-hpd-with-enabled-mode", HDMIA)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
> + TEST_MODESET_ON);
> +
> + igt_describe(test_hotplug_for_each_pipe_desc);
> + connector_subtest("hdmi-hpd-for-each-pipe", HDMIA)
> + test_hotplug_for_each_pipe(&data, port);
> +
> + igt_describe(test_suspend_resume_hpd_desc);
> + connector_subtest("hdmi-hpd-after-suspend", HDMIA)
> + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
> + SUSPEND_TEST_NONE);
> +
> + igt_describe(test_suspend_resume_hpd_desc);
> + connector_subtest("hdmi-hpd-after-hibernate", HDMIA)
> + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
> + SUSPEND_TEST_DEVICES);
> +
> + igt_describe(test_hpd_storm_detect_desc);
> + connector_subtest("hdmi-hpd-storm", HDMIA)
> + test_hpd_storm_detect(&data, port,
> + HPD_STORM_PULSE_INTERVAL_HDMI);
> +
> + igt_describe(test_hpd_storm_disable_desc);
> + connector_subtest("hdmi-hpd-storm-disable", HDMIA)
> + test_hpd_storm_disable(&data, port,
> + HPD_STORM_PULSE_INTERVAL_HDMI);
> + }
> +
> + igt_describe("VGA tests");
> + igt_subtest_group {
> + igt_fixture {
> + chamelium_require_connector_present(
> + data.ports, DRM_MODE_CONNECTOR_VGA,
> + data.port_count, 1);
> + }
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("vga-hpd", VGA) test_hotplug(
> + &data, port, HPD_TOGGLE_COUNT_VGA, TEST_MODESET_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("vga-hpd-fast", VGA) test_hotplug(
> + &data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("vga-hpd-enable-disable-mode", VGA)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
> + TEST_MODESET_ON_OFF);
> +
> + igt_describe(test_basic_hotplug_desc);
> + connector_subtest("vga-hpd-with-enabled-mode", VGA)
> + test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
> + TEST_MODESET_ON);
> +
> + igt_describe(test_suspend_resume_hpd_desc);
> + connector_subtest("vga-hpd-after-suspend", VGA)
> + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
> + SUSPEND_TEST_NONE);
> +
> + igt_describe(test_suspend_resume_hpd_desc);
> + connector_subtest("vga-hpd-after-hibernate", VGA)
> + test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
> + SUSPEND_TEST_DEVICES);
> +
> + igt_describe(test_hpd_without_ddc_desc);
> + connector_subtest("vga-hpd-without-ddc", VGA)
> + test_hpd_without_ddc(&data, port);
> + }
> +
> + igt_describe("Tests that operate on all connectors");
> + igt_subtest_group {
> + igt_fixture {
> + igt_require(data.port_count);
> + }
> +
> + igt_describe(test_suspend_resume_hpd_common_desc);
> + igt_subtest("common-hpd-after-suspend")
> + test_suspend_resume_hpd_common(&data, SUSPEND_STATE_MEM,
> + SUSPEND_TEST_NONE);
> +
> + igt_describe(test_suspend_resume_hpd_common_desc);
> + igt_subtest("common-hpd-after-hibernate")
> + test_suspend_resume_hpd_common(&data,
> + SUSPEND_STATE_DISK,
> + SUSPEND_TEST_DEVICES);
> + }
> +
> + igt_describe(test_hotplug_for_each_pipe_desc);
> + connector_subtest("vga-hpd-for-each-pipe", VGA)
> + test_hotplug_for_each_pipe(&data, port);
> +
> + igt_fixture {
> + igt_display_fini(&data.display);
> + close(data.drm_fd);
> + }
> +}
> diff --git a/tests/intel-ci/blacklist.txt b/tests/intel-ci/blacklist.txt
> index 0d307730..6e5cc436 100644
> --- a/tests/intel-ci/blacklist.txt
> +++ b/tests/intel-ci/blacklist.txt
> @@ -77,7 +77,7 @@ igt@kms_frontbuffer_tracking@.*drrs.*
> # is too costly in comparison to the value
> # provided.
> ###############################################
> -igt@kms_chamelium@hdmi-.*-planes-random
> +igt@kms_chamelium_frames@hdmi-.*-planes-random
> ###############################################
> # Broadcom
> ###############################################
> diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist
> index f57f8ff3..fb4c0f73 100644
> --- a/tests/intel-ci/fast-feedback.testlist
> +++ b/tests/intel-ci/fast-feedback.testlist
> @@ -92,14 +92,14 @@ igt@kms_addfb_basic@unused-modifier
> igt@kms_addfb_basic@unused-offsets
> igt@kms_addfb_basic@unused-pitches
> igt@kms_busy@basic
> -igt@kms_chamelium@dp-hpd-fast
> -igt@kms_chamelium@dp-edid-read
> -igt@kms_chamelium@dp-crc-fast
> -igt@kms_chamelium@hdmi-hpd-fast
> -igt@kms_chamelium@hdmi-edid-read
> -igt@kms_chamelium@hdmi-crc-fast
> -igt@kms_chamelium@vga-hpd-fast
> -igt@kms_chamelium@vga-edid-read
> +igt@kms_chamelium_hpd@dp-hpd-fast
> +igt@kms_chamelium_edid@dp-edid-read
> +igt@kms_chamelium_frames@dp-crc-fast
> +igt@kms_chamelium_hpd@hdmi-hpd-fast
> +igt@kms_chamelium_edid@hdmi-edid-read
> +igt@kms_chamelium_frames@hdmi-crc-fast
> +igt@kms_chamelium_hpd@vga-hpd-fast
> +igt@kms_chamelium_edid@vga-edid-read
> igt@kms_prop_blob@basic
> igt@kms_cursor_legacy@basic-busy-flip-before-cursor
> igt@kms_cursor_legacy@basic-flip-after-cursor
> @@ -174,5 +174,5 @@ igt@i915_suspend@basic-s2idle-without-i915
> igt@i915_suspend@basic-s3-without-i915
> igt@gem_exec_suspend@basic-s0
> igt@gem_exec_suspend@basic-s3
> -igt@kms_chamelium@common-hpd-after-suspend
> +igt@kms_chamelium_hpd@common-hpd-after-suspend
> igt@kms_pipe_crc_basic@suspend-read-crc
> diff --git a/tests/kms_color_helper.h b/tests/kms_color_helper.h
> index f0ae30e3..f9242232 100644
> --- a/tests/kms_color_helper.h
> +++ b/tests/kms_color_helper.h
> @@ -27,7 +27,7 @@
>
> /*
> * This header is for code that is shared between kms_color.c and
> - * kms_color_chamelium.c. Reusability elsewhere can be questionable.
> + * kms_chamelium_color.c. Reusability elsewhere can be questionable.
> */
>
> #include <math.h>
> 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.39.0.314.g84b9a713c41-goog
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests
@ 2022-11-30 20:45 Mark Yacoub
2022-12-01 14:49 ` Petri Latvala
0 siblings, 1 reply; 11+ messages in thread
From: Mark Yacoub @ 2022-11-30 20:45 UTC (permalink / raw)
To: igt-dev
Cc: robdclark, vsuley, petri.latvala, ihf, amstan, kalin, seanpaul,
matthewtlam, markyacoub, khaled.almahallawy
[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 <markyacoub@chromium.org>
---
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 <stdint.h>
+#include <stddef.h>
#include <xf86drmMode.h>
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 <stdbool.h>
+#include <stddef.h>
#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 <lyude@redhat.com>
- */
-
-#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 <fcntl.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdatomic.h>
-// #include <stdio.h>
-
-// 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 <markyacoub@chromium.org>
+ */
+
+#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 <markyacoub@chromium.org>
+ */
+
+#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 <lyude@redhat.com>
+ */
+
+#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 <markyacoub@chromium.org>
+ */
+
+#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 <markyacoub@chromium.org>
+ */
+
+#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 <markyacoub@chromium.org>
+ */
+
+#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
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 <math.h>
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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests
2022-11-30 20:45 Mark Yacoub
@ 2022-12-01 14:49 ` Petri Latvala
0 siblings, 0 replies; 11+ messages in thread
From: Petri Latvala @ 2022-12-01 14:49 UTC (permalink / raw)
To: Mark Yacoub
Cc: robdclark, khaled.almahallawy, vsuley, igt-dev, kalin, seanpaul,
ihf, matthewtlam, markyacoub, amstan
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 <markyacoub@chromium.org>
> ---
> 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 <stdint.h>
> +#include <stddef.h>
>
> #include <xf86drmMode.h>
>
> 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 <stdbool.h>
> +#include <stddef.h>
>
> #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 <lyude@redhat.com>
> - */
> -
> -#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 <fcntl.h>
> -#include <pthread.h>
> -#include <string.h>
> -#include <stdatomic.h>
> -// #include <stdio.h>
> -
> -// 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 <markyacoub@chromium.org>
> + */
> +
> +#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 <markyacoub@chromium.org>
> + */
> +
> +#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 <lyude@redhat.com>
> + */
> +
> +#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 <markyacoub@chromium.org>
> + */
> +
> +#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 <markyacoub@chromium.org>
> + */
> +
> +#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 <markyacoub@chromium.org>
> + */
> +
> +#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 <math.h>
> 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
>
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2023-01-09 15:54 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-03 17:15 [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Mark Yacoub
2023-01-03 18:45 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev5) Patchwork
2023-01-03 19:06 ` Mark Yacoub
2023-01-04 12:06 ` [igt-dev] ✗ Fi.CI.BAT: failure for Chamelium: Split kms_chamelium into multiple kms_chamelium tests (rev6) Patchwork
2023-01-06 17:16 ` Mark Yacoub
[not found] ` <DM4PR11MB62134C50D1933888C97021E8ECF89@DM4PR11MB6213.namprd11.prod.outlook.com>
2023-01-09 4:05 ` Yedireswarapu, SaiX Nandan
2023-01-09 4:02 ` [igt-dev] ✓ Fi.CI.BAT: success " Patchwork
2023-01-09 5:27 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2023-01-09 15:51 ` [igt-dev] [PATCH] Chamelium: Split kms_chamelium into multiple kms_chamelium tests Petri Latvala
-- strict thread matches above, loose matches on Subject: below --
2022-11-30 20:45 Mark Yacoub
2022-12-01 14:49 ` Petri Latvala
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.