* [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI
@ 2022-12-29 19:27 Mark Yacoub
2022-12-29 21:16 ` [igt-dev] ✓ Fi.CI.BAT: success for testing headers in CI (rev2) Patchwork
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Mark Yacoub @ 2022-12-29 19:27 UTC (permalink / raw)
To: igt-dev; +Cc: Mark Yacoub
From: Mark Yacoub <markyacoub@google.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 | 858 +++++
...olor_chamelium.c => kms_chamelium_color.c} | 0
tests/chamelium/kms_chamelium_edid.c | 543 +++
tests/chamelium/kms_chamelium_frames.c | 1085 ++++++
tests/chamelium/kms_chamelium_helper.c | 330 ++
tests/chamelium/kms_chamelium_helper.h | 74 +
tests/chamelium/kms_chamelium_hpd.c | 512 +++
tests/intel-ci/blacklist.txt | 2 +-
tests/intel-ci/fast-feedback.testlist | 18 +-
tests/kms_color_helper.h | 2 +-
tests/meson.build | 14 +-
tests/vc4_ci/vc4-chamelium-fast.testlist | 28 +-
17 files changed, 3440 insertions(+), 3164 deletions(-)
delete mode 100644 tests/chamelium/kms_chamelium.c
create mode 100644 tests/chamelium/kms_chamelium_audio.c
rename tests/chamelium/{kms_color_chamelium.c => kms_chamelium_color.c} (100%)
create mode 100644 tests/chamelium/kms_chamelium_edid.c
create mode 100644 tests/chamelium/kms_chamelium_frames.c
create mode 100644 tests/chamelium/kms_chamelium_helper.c
create mode 100644 tests/chamelium/kms_chamelium_helper.h
create mode 100644 tests/chamelium/kms_chamelium_hpd.c
diff --git a/docs/chamelium.txt b/docs/chamelium.txt
index c4c22468..f82c8b0c 100644
--- a/docs/chamelium.txt
+++ b/docs/chamelium.txt
@@ -241,7 +241,7 @@ Current Support in IGT
Support for the Chamelium platform in IGT is found in the following places:
* lib/igt_chamelium.c: library with Chamelium-related helpers
-* tests/kms_chamelium.c: sub-tests using the Chamelium
+* tests/kms_chamelium_*.c: sub-tests using the Chamelium
As of early April 2019, the following features are tested by IGT:
* Pixel-by-pixel frame integrity tests for DP and HDMI
diff --git a/lib/igt_edid.h b/lib/igt_edid.h
index 477f16c2..85a9ef5e 100644
--- a/lib/igt_edid.h
+++ b/lib/igt_edid.h
@@ -29,6 +29,7 @@
#include "config.h"
#include <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..c9e15f41
--- /dev/null
+++ b/tests/chamelium/kms_chamelium_edid.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: MIT
+/*
+ * A Chamelium test for testing the EDID functionality.
+ *
+ * Copyright 2022 Google LLC.
+ *
+ * Authors: Mark Yacoub <markyacoub@chromium.org>
+ */
+
+#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..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..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] 5+ messages in thread* [igt-dev] ✓ Fi.CI.BAT: success for testing headers in CI (rev2) 2022-12-29 19:27 [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Mark Yacoub @ 2022-12-29 21:16 ` Patchwork 2022-12-29 23:27 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork 2022-12-30 12:51 ` [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Jani Nikula 2 siblings, 0 replies; 5+ messages in thread From: Patchwork @ 2022-12-29 21:16 UTC (permalink / raw) To: Mark Yacoub; +Cc: igt-dev [-- Attachment #1: Type: text/plain, Size: 13663 bytes --] == Series Details == Series: testing headers in CI (rev2) URL : https://patchwork.freedesktop.org/series/112294/ State : success == Summary == CI Bug Log - changes from CI_DRM_12529 -> IGTPW_8283 ==================================================== Summary ------- **SUCCESS** No regressions found. External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/index.html Participating hosts (44 -> 42) ------------------------------ Missing (2): fi-rkl-11600 fi-snb-2520m Possible new issues ------------------- Here are the unknown changes that may have been introduced in IGTPW_8283: ### 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_8283/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_8283/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_8283/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_8283/fi-ehl-2/igt@kms_chamelium_edid@hdmi-edid-read.html - {bat-rpls-2}: NOTRUN -> [SKIP][5] +7 similar issues [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/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_8283/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_8283/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_8283/bat-adlp-9/igt@kms_chamelium_hpd@common-hpd-after-suspend.html * {igt@kms_chamelium_hpd@dp-hpd-fast} (NEW): - bat-dg1-5: NOTRUN -> [SKIP][9] +8 similar issues [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-dg1-5/igt@kms_chamelium_hpd@dp-hpd-fast.html - {bat-dg1-7}: NOTRUN -> [SKIP][10] +8 similar issues [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-dg1-7/igt@kms_chamelium_hpd@dp-hpd-fast.html - {bat-dg2-9}: NOTRUN -> [SKIP][11] +8 similar issues [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-dg2-9/igt@kms_chamelium_hpd@dp-hpd-fast.html * {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW): - {bat-adlp-6}: NOTRUN -> [SKIP][12] +8 similar issues [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-adlp-6/igt@kms_chamelium_hpd@hdmi-hpd-fast.html * {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW): - fi-icl-u2: NOTRUN -> [SKIP][13] +8 similar issues [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-icl-u2/igt@kms_chamelium_hpd@vga-hpd-fast.html - {bat-atsm-1}: NOTRUN -> [SKIP][14] +7 similar issues [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-atsm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html - {bat-jsl-3}: NOTRUN -> [SKIP][15] +8 similar issues [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-jsl-3/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-rkl-guc: NOTRUN -> [SKIP][16] +8 similar issues [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-rkl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html - {bat-dg2-11}: NOTRUN -> [SKIP][17] +8 similar issues [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-dg2-11/igt@kms_chamelium_hpd@vga-hpd-fast.html - bat-adlp-4: NOTRUN -> [SKIP][18] +8 similar issues [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-adlp-4/igt@kms_chamelium_hpd@vga-hpd-fast.html - {bat-dg2-8}: NOTRUN -> [SKIP][19] +8 similar issues [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-dg2-8/igt@kms_chamelium_hpd@vga-hpd-fast.html - {bat-adlm-1}: NOTRUN -> [SKIP][20] +8 similar issues [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-adlm-1/igt@kms_chamelium_hpd@vga-hpd-fast.html New tests --------- New tests have been introduced between CI_DRM_12529 and IGTPW_8283: ### New IGT tests (9) ### * igt@kms_chamelium_edid@dp-edid-read: - Statuses : 42 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@hdmi-edid-read: - Statuses : 1 pass(s) 41 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@vga-edid-read: - Statuses : 42 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@dp-crc-fast: - Statuses : 42 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-crc-fast: - Statuses : 1 pass(s) 41 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@common-hpd-after-suspend: - Statuses : 1 pass(s) 37 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-fast: - Statuses : 42 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@hdmi-hpd-fast: - Statuses : 1 pass(s) 41 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-fast: - Statuses : 42 skip(s) - Exec time: [0.0] s Known issues ------------ Here are the changes found in IGTPW_8283 that come from known issues: ### IGT changes ### #### Issues hit #### * {igt@kms_chamelium_edid@dp-edid-read} (NEW): - fi-bsw-n3050: NOTRUN -> [SKIP][21] ([fdo#109271]) +8 similar issues [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-bsw-n3050/igt@kms_chamelium_edid@dp-edid-read.html - fi-bwr-2160: NOTRUN -> [SKIP][22] ([fdo#109271]) +8 similar issues [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-bwr-2160/igt@kms_chamelium_edid@dp-edid-read.html - fi-pnv-d510: NOTRUN -> [SKIP][23] ([fdo#109271]) +8 similar issues [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-pnv-d510/igt@kms_chamelium_edid@dp-edid-read.html * {igt@kms_chamelium_edid@hdmi-edid-read} (NEW): - fi-cfl-guc: NOTRUN -> [SKIP][24] ([fdo#109271]) +8 similar issues [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-cfl-guc/igt@kms_chamelium_edid@hdmi-edid-read.html - fi-skl-6600u: NOTRUN -> [SKIP][25] ([fdo#109271]) +8 similar issues [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-skl-6600u/igt@kms_chamelium_edid@hdmi-edid-read.html * {igt@kms_chamelium_frames@dp-crc-fast} (NEW): - fi-ilk-650: NOTRUN -> [SKIP][26] ([fdo#109271]) +8 similar issues [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-ilk-650/igt@kms_chamelium_frames@dp-crc-fast.html * {igt@kms_chamelium_frames@hdmi-crc-fast} (NEW): - fi-cfl-8109u: NOTRUN -> [SKIP][27] ([fdo#109271]) +8 similar issues [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-cfl-8109u/igt@kms_chamelium_frames@hdmi-crc-fast.html - fi-kbl-7567u: NOTRUN -> [SKIP][28] ([fdo#109271]) +8 similar issues [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-kbl-7567u/igt@kms_chamelium_frames@hdmi-crc-fast.html - fi-kbl-8809g: NOTRUN -> [SKIP][29] ([fdo#109271]) +7 similar issues [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/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][30] ([fdo#109271]) +8 similar issues [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-glk-j4005/igt@kms_chamelium_hpd@common-hpd-after-suspend.html - fi-snb-2600: NOTRUN -> [SKIP][31] ([fdo#109271]) +8 similar issues [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/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][32] ([fdo#109271]) +4 similar issues [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-skl-6700k2/igt@kms_chamelium_hpd@dp-hpd-fast.html * {igt@kms_chamelium_hpd@hdmi-hpd-fast} (NEW): - fi-cfl-8700k: NOTRUN -> [SKIP][33] ([fdo#109271]) +8 similar issues [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-cfl-8700k/igt@kms_chamelium_hpd@hdmi-hpd-fast.html - fi-blb-e6850: NOTRUN -> [SKIP][34] ([fdo#109271]) +8 similar issues [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-blb-e6850/igt@kms_chamelium_hpd@hdmi-hpd-fast.html - {bat-kbl-2}: NOTRUN -> [SKIP][35] ([fdo#109271]) +8 similar issues [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-kbl-2/igt@kms_chamelium_hpd@hdmi-hpd-fast.html * {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW): - fi-apl-guc: NOTRUN -> [SKIP][36] ([fdo#109271]) +8 similar issues [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-apl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-bsw-kefka: NOTRUN -> [SKIP][37] ([fdo#109271]) +8 similar issues [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-bsw-kefka/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-skl-guc: NOTRUN -> [SKIP][38] ([fdo#109271]) +8 similar issues [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-skl-guc/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-hsw-4770: NOTRUN -> [SKIP][39] ([fdo#109271]) +8 similar issues [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-hsw-4770/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-ivb-3770: NOTRUN -> [SKIP][40] ([fdo#109271]) +8 similar issues [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-ivb-3770/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-elk-e7500: NOTRUN -> [SKIP][41] ([fdo#109271]) +8 similar issues [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-elk-e7500/igt@kms_chamelium_hpd@vga-hpd-fast.html - fi-bsw-nick: NOTRUN -> [SKIP][42] ([fdo#109271]) +8 similar issues [42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-bsw-nick/igt@kms_chamelium_hpd@vga-hpd-fast.html * igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size: - fi-bsw-kefka: [PASS][43] -> [FAIL][44] ([i915#6298]) [43]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/fi-bsw-kefka/igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size.html [44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/fi-bsw-kefka/igt@kms_cursor_legacy@basic-busy-flip-before-cursor@atomic-transitions-varying-size.html #### Possible fixes #### * igt@i915_pm_rpm@basic-rte: - {bat-adln-1}: [DMESG-WARN][45] -> [PASS][46] [45]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/bat-adln-1/igt@i915_pm_rpm@basic-rte.html [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-adln-1/igt@i915_pm_rpm@basic-rte.html * igt@i915_selftest@live@migrate: - {bat-adlp-9}: [DMESG-FAIL][47] ([i915#7699]) -> [PASS][48] [47]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/bat-adlp-9/igt@i915_selftest@live@migrate.html [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-adlp-9/igt@i915_selftest@live@migrate.html * igt@i915_selftest@live@slpc: - {bat-rpls-1}: [DMESG-FAIL][49] ([i915#6367]) -> [PASS][50] [49]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/bat-rpls-1/igt@i915_selftest@live@slpc.html [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/bat-rpls-1/igt@i915_selftest@live@slpc.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#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295 [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#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312 [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613 [i915#4983]: https://gitlab.freedesktop.org/drm/intel/issues/4983 [i915#6257]: https://gitlab.freedesktop.org/drm/intel/issues/6257 [i915#6298]: https://gitlab.freedesktop.org/drm/intel/issues/6298 [i915#6367]: https://gitlab.freedesktop.org/drm/intel/issues/6367 [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621 [i915#6687]: https://gitlab.freedesktop.org/drm/intel/issues/6687 [i915#6997]: https://gitlab.freedesktop.org/drm/intel/issues/6997 [i915#7336]: https://gitlab.freedesktop.org/drm/intel/issues/7336 [i915#7699]: https://gitlab.freedesktop.org/drm/intel/issues/7699 Build changes ------------- * CI: CI-20190529 -> None * IGT: IGT_7104 -> IGTPW_8283 CI-20190529: 20190529 CI_DRM_12529: 0ca1a1772701d5c3350d8736080e528e2f2f413e @ git://anongit.freedesktop.org/gfx-ci/linux IGTPW_8283: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/index.html IGT_7104: fe5def13049225967770eaaf19ec01ef80e2adc5 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git == Logs == For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/index.html [-- Attachment #2: Type: text/html, Size: 16070 bytes --] ^ permalink raw reply [flat|nested] 5+ messages in thread
* [igt-dev] ✓ Fi.CI.IGT: success for testing headers in CI (rev2) 2022-12-29 19:27 [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Mark Yacoub 2022-12-29 21:16 ` [igt-dev] ✓ Fi.CI.BAT: success for testing headers in CI (rev2) Patchwork @ 2022-12-29 23:27 ` Patchwork 2022-12-30 12:51 ` [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Jani Nikula 2 siblings, 0 replies; 5+ messages in thread From: Patchwork @ 2022-12-29 23:27 UTC (permalink / raw) To: Mark Yacoub; +Cc: igt-dev [-- Attachment #1: Type: text/plain, Size: 35265 bytes --] == Series Details == Series: testing headers in CI (rev2) URL : https://patchwork.freedesktop.org/series/112294/ State : success == Summary == CI Bug Log - changes from CI_DRM_12529_full -> IGTPW_8283_full ==================================================== Summary ------- **SUCCESS** No regressions found. External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/index.html Participating hosts (14 -> 10) ------------------------------ Missing (4): shard-rkl0 pig-kbl-iris pig-glk-j5005 pig-skl-6260u Possible new issues ------------------- Here are the unknown changes that may have been introduced in IGTPW_8283_full: ### IGT changes ### #### Possible regressions #### * {igt@kms_chamelium_edid@hdmi-mode-timings} (NEW): - {shard-tglu}: NOTRUN -> [SKIP][1] +21 similar issues [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-tglu-1/igt@kms_chamelium_edid@hdmi-mode-timings.html * {igt@kms_chamelium_frames@hdmi-frame-dump} (NEW): - {shard-tglu-9}: NOTRUN -> [SKIP][2] +4 similar issues [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-tglu-9/igt@kms_chamelium_frames@hdmi-frame-dump.html * {igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe} (NEW): - {shard-tglu-10}: NOTRUN -> [SKIP][3] +10 similar issues [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-tglu-10/igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe.html * {igt@kms_chamelium_hpd@hdmi-hpd-storm-disable} (NEW): - {shard-dg1}: NOTRUN -> [SKIP][4] +59 similar issues [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-16/igt@kms_chamelium_hpd@hdmi-hpd-storm-disable.html * {igt@kms_chamelium_hpd@vga-hpd-fast} (NEW): - {shard-rkl}: NOTRUN -> [SKIP][5] +58 similar issues [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-2/igt@kms_chamelium_hpd@vga-hpd-fast.html #### Suppressed #### The following results come from untrusted machines, tests, or statuses. They do not affect the overall result. * igt@drm_fdinfo@idle@rcs0: - {shard-rkl}: NOTRUN -> [FAIL][6] [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-2/igt@drm_fdinfo@idle@rcs0.html New tests --------- New tests have been introduced between CI_DRM_12529_full and IGTPW_8283_full: ### New IGT tests (63) ### * igt@kms_chamelium_audio@dp-audio: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_audio@dp-audio-edid: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_audio@hdmi-audio: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_audio@hdmi-audio-edid: - Statuses : - Exec time: [None] s * igt@kms_chamelium_color@ctm-0-25: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_color@ctm-0-50: - Statuses : 5 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 : 6 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 : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_color@ctm-max: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_color@ctm-negative: - Statuses : 4 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_color@ctm-red-to-blue: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_color@degamma: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_color@gamma: - Statuses : 4 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@dp-edid-change-during-suspend: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@dp-edid-read: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@dp-edid-resolution-list: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@dp-edid-stress-resolution-4k: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@dp-edid-stress-resolution-non-4k: - Statuses : 4 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 : - Exec time: [None] s * igt@kms_chamelium_edid@hdmi-edid-read: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_edid@hdmi-edid-stress-resolution-4k: - Statuses : 6 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 : 5 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 : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@dp-crc-single: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@dp-frame-dump: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-aspect-ratio: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-cmp-planar-formats: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-crc-fast: - Statuses : 4 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-crc-multiple: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-crc-nonplanar-formats: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-crc-single: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@hdmi-frame-dump: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_frames@vga-frame-dump: - Statuses : 6 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 : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-after-suspend: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-enable-disable-mode: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-fast: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-for-each-pipe: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-storm: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-storm-disable: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@dp-hpd-with-enabled-mode: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@hdmi-hpd: - Statuses : - Exec time: [None] s * igt@kms_chamelium_hpd@hdmi-hpd-after-suspend: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@hdmi-hpd-enable-disable-mode: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@hdmi-hpd-fast: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@hdmi-hpd-for-each-pipe: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@hdmi-hpd-storm: - Statuses : 6 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 : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-after-suspend: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-enable-disable-mode: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-fast: - Statuses : 6 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-for-each-pipe: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-with-enabled-mode: - Statuses : 5 skip(s) - Exec time: [0.0] s * igt@kms_chamelium_hpd@vga-hpd-without-ddc: - Statuses : 6 skip(s) - Exec time: [0.0] s Known issues ------------ Here are the changes found in IGTPW_8283_full that come from known issues: ### IGT changes ### #### Issues hit #### * igt@gem_exec_fair@basic-none-rrul@rcs0: - shard-glk: [PASS][7] -> [FAIL][8] ([i915#2842]) +2 similar issues [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-glk5/igt@gem_exec_fair@basic-none-rrul@rcs0.html [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk1/igt@gem_exec_fair@basic-none-rrul@rcs0.html * igt@gem_lmem_swapping@parallel-random-verify-ccs: - shard-apl: NOTRUN -> [SKIP][9] ([fdo#109271] / [i915#4613]) [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl2/igt@gem_lmem_swapping@parallel-random-verify-ccs.html * igt@gem_partial_pwrite_pread@writes-after-reads-snoop: - shard-apl: [PASS][10] -> [INCOMPLETE][11] ([i915#7708]) [10]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-apl1/igt@gem_partial_pwrite_pread@writes-after-reads-snoop.html [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl2/igt@gem_partial_pwrite_pread@writes-after-reads-snoop.html * igt@gen9_exec_parse@allowed-all: - shard-apl: [PASS][12] -> [DMESG-WARN][13] ([i915#5566] / [i915#716]) +1 similar issue [12]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-apl3/igt@gen9_exec_parse@allowed-all.html [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl7/igt@gen9_exec_parse@allowed-all.html * igt@kms_ccs@pipe-a-bad-aux-stride-y_tiled_gen12_rc_ccs_cc: - shard-glk: NOTRUN -> [SKIP][14] ([fdo#109271] / [i915#3886]) +4 similar issues [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk4/igt@kms_ccs@pipe-a-bad-aux-stride-y_tiled_gen12_rc_ccs_cc.html * igt@kms_ccs@pipe-a-crc-primary-rotation-180-y_tiled_gen12_mc_ccs: - shard-apl: NOTRUN -> [SKIP][15] ([fdo#109271] / [i915#3886]) +1 similar issue [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl7/igt@kms_ccs@pipe-a-crc-primary-rotation-180-y_tiled_gen12_mc_ccs.html * {igt@kms_chamelium_color@ctm-0-75} (NEW): - shard-apl: NOTRUN -> [SKIP][16] ([fdo#109271]) +143 similar issues [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl6/igt@kms_chamelium_color@ctm-0-75.html * igt@kms_cursor_crc@cursor-random-max-size: - shard-glk: NOTRUN -> [SKIP][17] ([fdo#109271]) +161 similar issues [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk9/igt@kms_cursor_crc@cursor-random-max-size.html * igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size: - shard-apl: [PASS][18] -> [FAIL][19] ([i915#2346]) +1 similar issue [18]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-apl1/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl1/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html * igt@kms_plane_alpha_blend@alpha-opaque-fb@pipe-b-hdmi-a-2: - shard-glk: NOTRUN -> [FAIL][20] ([i915#4573]) +2 similar issues [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk8/igt@kms_plane_alpha_blend@alpha-opaque-fb@pipe-b-hdmi-a-2.html * igt@kms_psr2_sf@cursor-plane-update-sf: - shard-apl: NOTRUN -> [SKIP][21] ([fdo#109271] / [i915#658]) +1 similar issue [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl7/igt@kms_psr2_sf@cursor-plane-update-sf.html * igt@kms_psr2_su@frontbuffer-xrgb8888: - shard-glk: NOTRUN -> [SKIP][22] ([fdo#109271] / [i915#658]) +2 similar issues [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk9/igt@kms_psr2_su@frontbuffer-xrgb8888.html * igt@kms_rotation_crc@sprite-rotation-270: - shard-snb: NOTRUN -> [SKIP][23] ([fdo#109271]) +149 similar issues [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-snb5/igt@kms_rotation_crc@sprite-rotation-270.html * igt@sysfs_clients@recycle: - shard-glk: NOTRUN -> [SKIP][24] ([fdo#109271] / [i915#2994]) [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk9/igt@sysfs_clients@recycle.html * igt@sysfs_clients@recycle-many: - shard-apl: NOTRUN -> [SKIP][25] ([fdo#109271] / [i915#2994]) +1 similar issue [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl3/igt@sysfs_clients@recycle-many.html #### Possible fixes #### * igt@fbdev@info: - {shard-rkl}: [SKIP][26] ([i915#2582]) -> [PASS][27] [26]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-2/igt@fbdev@info.html [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-5/igt@fbdev@info.html * igt@fbdev@read: - {shard-dg1}: [FAIL][28] -> [PASS][29] [28]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-dg1-12/igt@fbdev@read.html [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-13/igt@fbdev@read.html - shard-apl: [FAIL][30] -> [PASS][31] [30]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-apl3/igt@fbdev@read.html [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl1/igt@fbdev@read.html - shard-snb: [FAIL][32] -> [PASS][33] [32]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-snb7/igt@fbdev@read.html [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-snb7/igt@fbdev@read.html - shard-glk: [FAIL][34] -> [PASS][35] [34]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-glk9/igt@fbdev@read.html [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk9/igt@fbdev@read.html * igt@feature_discovery@psr1: - {shard-rkl}: [SKIP][36] ([i915#658]) -> [PASS][37] +1 similar issue [36]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-2/igt@feature_discovery@psr1.html [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@feature_discovery@psr1.html * igt@gem_ctx_persistence@hang: - {shard-rkl}: [SKIP][38] ([i915#6252]) -> [PASS][39] [38]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-5/igt@gem_ctx_persistence@hang.html [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-2/igt@gem_ctx_persistence@hang.html * igt@gem_eio@unwedge-stress: - {shard-dg1}: [FAIL][40] ([i915#5784]) -> [PASS][41] [40]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-dg1-19/igt@gem_eio@unwedge-stress.html [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-19/igt@gem_eio@unwedge-stress.html * igt@gem_exec_fair@basic-deadline: - shard-glk: [FAIL][42] ([i915#2846]) -> [PASS][43] [42]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-glk5/igt@gem_exec_fair@basic-deadline.html [43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk1/igt@gem_exec_fair@basic-deadline.html * igt@gem_exec_fair@basic-pace-solo@rcs0: - {shard-rkl}: [FAIL][44] ([i915#2842]) -> [PASS][45] +1 similar issue [44]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-6/igt@gem_exec_fair@basic-pace-solo@rcs0.html [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-5/igt@gem_exec_fair@basic-pace-solo@rcs0.html * igt@gem_exec_reloc@basic-softpin: - {shard-rkl}: [SKIP][46] ([i915#3281]) -> [PASS][47] [46]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-6/igt@gem_exec_reloc@basic-softpin.html [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-5/igt@gem_exec_reloc@basic-softpin.html * igt@gem_mmap_wc@set-cache-level: - {shard-rkl}: [SKIP][48] ([i915#1850]) -> [PASS][49] [48]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-3/igt@gem_mmap_wc@set-cache-level.html [49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@gem_mmap_wc@set-cache-level.html * igt@gem_pread@uncached: - {shard-rkl}: [SKIP][50] ([i915#3282]) -> [PASS][51] [50]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-3/igt@gem_pread@uncached.html [51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-5/igt@gem_pread@uncached.html * igt@gen9_exec_parse@shadow-peek: - {shard-rkl}: [SKIP][52] ([i915#2527]) -> [PASS][53] +1 similar issue [52]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-1/igt@gen9_exec_parse@shadow-peek.html [53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-5/igt@gen9_exec_parse@shadow-peek.html * igt@i915_hangman@engine-engine-error@bcs0: - {shard-rkl}: [SKIP][54] ([i915#6258]) -> [PASS][55] [54]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-5/igt@i915_hangman@engine-engine-error@bcs0.html [55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@i915_hangman@engine-engine-error@bcs0.html * igt@i915_pm_lpsp@kms-lpsp@kms-lpsp-hdmi-a: - {shard-dg1}: [SKIP][56] ([i915#1937]) -> [PASS][57] [56]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-dg1-18/igt@i915_pm_lpsp@kms-lpsp@kms-lpsp-hdmi-a.html [57]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-14/igt@i915_pm_lpsp@kms-lpsp@kms-lpsp-hdmi-a.html * igt@i915_pm_rc6_residency@rc6-idle@bcs0: - {shard-dg1}: [FAIL][58] ([i915#3591]) -> [PASS][59] [58]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-dg1-12/igt@i915_pm_rc6_residency@rc6-idle@bcs0.html [59]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-18/igt@i915_pm_rc6_residency@rc6-idle@bcs0.html * igt@i915_pm_rpm@system-suspend-modeset: - {shard-rkl}: [SKIP][60] ([fdo#109308]) -> [PASS][61] [60]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-5/igt@i915_pm_rpm@system-suspend-modeset.html [61]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@i915_pm_rpm@system-suspend-modeset.html * igt@kms_big_fb@x-tiled-32bpp-rotate-0: - {shard-rkl}: [SKIP][62] ([i915#1845] / [i915#4098]) -> [PASS][63] +14 similar issues [62]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-5/igt@kms_big_fb@x-tiled-32bpp-rotate-0.html [63]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@kms_big_fb@x-tiled-32bpp-rotate-0.html * igt@kms_ccs@pipe-a-crc-primary-basic-y_tiled_gen12_rc_ccs_cc: - {shard-tglu}: [SKIP][64] ([i915#7651]) -> [PASS][65] +2 similar issues [64]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-tglu-6/igt@kms_ccs@pipe-a-crc-primary-basic-y_tiled_gen12_rc_ccs_cc.html [65]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-tglu-8/igt@kms_ccs@pipe-a-crc-primary-basic-y_tiled_gen12_rc_ccs_cc.html * igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size: - shard-glk: [FAIL][66] ([i915#2346]) -> [PASS][67] +1 similar issue [66]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-glk5/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html [67]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk8/igt@kms_cursor_legacy@flip-vs-cursor@atomic-transitions-varying-size.html * igt@kms_fbcon_fbt@psr-suspend: - {shard-rkl}: [SKIP][68] ([fdo#110189] / [i915#3955]) -> [PASS][69] [68]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-1/igt@kms_fbcon_fbt@psr-suspend.html [69]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@kms_fbcon_fbt@psr-suspend.html * igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@ac-hdmi-a1-hdmi-a2: - shard-glk: [FAIL][70] ([i915#79]) -> [PASS][71] [70]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-glk7/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@ac-hdmi-a1-hdmi-a2.html [71]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-glk2/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@ac-hdmi-a1-hdmi-a2.html * igt@kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-render: - {shard-rkl}: [SKIP][72] ([i915#1849] / [i915#4098]) -> [PASS][73] +9 similar issues [72]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-2/igt@kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-render.html [73]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-render.html * igt@kms_plane@plane-panning-top-left@pipe-a-planes: - {shard-rkl}: [SKIP][74] ([i915#1849] / [i915#3558]) -> [PASS][75] +1 similar issue [74]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-2/igt@kms_plane@plane-panning-top-left@pipe-a-planes.html [75]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@kms_plane@plane-panning-top-left@pipe-a-planes.html * igt@kms_properties@crtc-properties-legacy: - {shard-rkl}: [SKIP][76] ([i915#1849]) -> [PASS][77] +1 similar issue [76]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-2/igt@kms_properties@crtc-properties-legacy.html [77]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@kms_properties@crtc-properties-legacy.html * igt@kms_psr@sprite_plane_onoff: - {shard-rkl}: [SKIP][78] ([i915#1072]) -> [PASS][79] +1 similar issue [78]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-1/igt@kms_psr@sprite_plane_onoff.html [79]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@kms_psr@sprite_plane_onoff.html * igt@perf_pmu@busy-double-start@vcs0: - {shard-dg1}: [FAIL][80] ([i915#4349]) -> [PASS][81] +1 similar issue [80]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-dg1-14/igt@perf_pmu@busy-double-start@vcs0.html [81]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-19/igt@perf_pmu@busy-double-start@vcs0.html * igt@sysfs_heartbeat_interval@precise@vcs0: - {shard-dg1}: [FAIL][82] ([i915#1755]) -> [PASS][83] +1 similar issue [82]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-dg1-17/igt@sysfs_heartbeat_interval@precise@vcs0.html [83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-dg1-14/igt@sysfs_heartbeat_interval@precise@vcs0.html * igt@testdisplay: - {shard-rkl}: [SKIP][84] ([i915#4098]) -> [PASS][85] +1 similar issue [84]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-rkl-4/igt@testdisplay.html [85]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-rkl-6/igt@testdisplay.html #### Warnings #### * igt@runner@aborted: - shard-apl: ([FAIL][86], [FAIL][87]) ([i915#3002] / [i915#4312]) -> ([FAIL][88], [FAIL][89], [FAIL][90]) ([fdo#109271] / [i915#3002] / [i915#4312]) [86]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-apl3/igt@runner@aborted.html [87]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_12529/shard-apl6/igt@runner@aborted.html [88]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl6/igt@runner@aborted.html [89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl6/igt@runner@aborted.html [90]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/shard-apl7/igt@runner@aborted.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#109283]: https://bugs.freedesktop.org/show_bug.cgi?id=109283 [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285 [fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289 [fdo#109291]: https://bugs.freedesktop.org/show_bug.cgi?id=109291 [fdo#109295]: https://bugs.freedesktop.org/show_bug.cgi?id=109295 [fdo#109300]: https://bugs.freedesktop.org/show_bug.cgi?id=109300 [fdo#109303]: https://bugs.freedesktop.org/show_bug.cgi?id=109303 [fdo#109308]: https://bugs.freedesktop.org/show_bug.cgi?id=109308 [fdo#109309]: https://bugs.freedesktop.org/show_bug.cgi?id=109309 [fdo#109312]: https://bugs.freedesktop.org/show_bug.cgi?id=109312 [fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315 [fdo#109506]: https://bugs.freedesktop.org/show_bug.cgi?id=109506 [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#111656]: https://bugs.freedesktop.org/show_bug.cgi?id=111656 [fdo#111825]: https://bugs.freedesktop.org/show_bug.cgi?id=111825 [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827 [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#132]: https://gitlab.freedesktop.org/drm/intel/issues/132 [i915#1397]: https://gitlab.freedesktop.org/drm/intel/issues/1397 [i915#1755]: https://gitlab.freedesktop.org/drm/intel/issues/1755 [i915#1769]: https://gitlab.freedesktop.org/drm/intel/issues/1769 [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#1850]: https://gitlab.freedesktop.org/drm/intel/issues/1850 [i915#1902]: https://gitlab.freedesktop.org/drm/intel/issues/1902 [i915#1937]: https://gitlab.freedesktop.org/drm/intel/issues/1937 [i915#2346]: https://gitlab.freedesktop.org/drm/intel/issues/2346 [i915#2433]: https://gitlab.freedesktop.org/drm/intel/issues/2433 [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#3002]: https://gitlab.freedesktop.org/drm/intel/issues/3002 [i915#3116]: https://gitlab.freedesktop.org/drm/intel/issues/3116 [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#3359]: https://gitlab.freedesktop.org/drm/intel/issues/3359 [i915#3458]: https://gitlab.freedesktop.org/drm/intel/issues/3458 [i915#3469]: https://gitlab.freedesktop.org/drm/intel/issues/3469 [i915#3528]: https://gitlab.freedesktop.org/drm/intel/issues/3528 [i915#3539]: https://gitlab.freedesktop.org/drm/intel/issues/3539 [i915#3546]: https://gitlab.freedesktop.org/drm/intel/issues/3546 [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555 [i915#3558]: https://gitlab.freedesktop.org/drm/intel/issues/3558 [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#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#3825]: https://gitlab.freedesktop.org/drm/intel/issues/3825 [i915#3826]: https://gitlab.freedesktop.org/drm/intel/issues/3826 [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#4077]: https://gitlab.freedesktop.org/drm/intel/issues/4077 [i915#4079]: https://gitlab.freedesktop.org/drm/intel/issues/4079 [i915#4083]: https://gitlab.freedesktop.org/drm/intel/issues/4083 [i915#4098]: https://gitlab.freedesktop.org/drm/intel/issues/4098 [i915#4103]: https://gitlab.freedesktop.org/drm/intel/issues/4103 [i915#4270]: https://gitlab.freedesktop.org/drm/intel/issues/4270 [i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312 [i915#4349]: https://gitlab.freedesktop.org/drm/intel/issues/4349 [i915#4387]: https://gitlab.freedesktop.org/drm/intel/issues/4387 [i915#4525]: https://gitlab.freedesktop.org/drm/intel/issues/4525 [i915#4538]: https://gitlab.freedesktop.org/drm/intel/issues/4538 [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#4771]: https://gitlab.freedesktop.org/drm/intel/issues/4771 [i915#4812]: https://gitlab.freedesktop.org/drm/intel/issues/4812 [i915#4833]: https://gitlab.freedesktop.org/drm/intel/issues/4833 [i915#4852]: https://gitlab.freedesktop.org/drm/intel/issues/4852 [i915#4877]: https://gitlab.freedesktop.org/drm/intel/issues/4877 [i915#4880]: https://gitlab.freedesktop.org/drm/intel/issues/4880 [i915#5176]: https://gitlab.freedesktop.org/drm/intel/issues/5176 [i915#5234]: https://gitlab.freedesktop.org/drm/intel/issues/5234 [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#5563]: https://gitlab.freedesktop.org/drm/intel/issues/5563 [i915#5566]: https://gitlab.freedesktop.org/drm/intel/issues/5566 [i915#5784]: https://gitlab.freedesktop.org/drm/intel/issues/5784 [i915#6095]: https://gitlab.freedesktop.org/drm/intel/issues/6095 [i915#6227]: https://gitlab.freedesktop.org/drm/intel/issues/6227 [i915#6230]: https://gitlab.freedesktop.org/drm/intel/issues/6230 [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#6334]: https://gitlab.freedesktop.org/drm/intel/issues/6334 [i915#6335]: https://gitlab.freedesktop.org/drm/intel/issues/6335 [i915#6344]: https://gitlab.freedesktop.org/drm/intel/issues/6344 [i915#6355]: https://gitlab.freedesktop.org/drm/intel/issues/6355 [i915#6412]: https://gitlab.freedesktop.org/drm/intel/issues/6412 [i915#6433]: https://gitlab.freedesktop.org/drm/intel/issues/6433 [i915#6497]: https://gitlab.freedesktop.org/drm/intel/issues/6497 [i915#6524]: https://gitlab.freedesktop.org/drm/intel/issues/6524 [i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658 [i915#6590]: https://gitlab.freedesktop.org/drm/intel/issues/6590 [i915#6621]: https://gitlab.freedesktop.org/drm/intel/issues/6621 [i915#6768]: https://gitlab.freedesktop.org/drm/intel/issues/6768 [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#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#7697]: https://gitlab.freedesktop.org/drm/intel/issues/7697 [i915#7701]: https://gitlab.freedesktop.org/drm/intel/issues/7701 [i915#7708]: https://gitlab.freedesktop.org/drm/intel/issues/7708 [i915#7711]: https://gitlab.freedesktop.org/drm/intel/issues/7711 [i915#79]: https://gitlab.freedesktop.org/drm/intel/issues/79 Build changes ------------- * CI: CI-20190529 -> None * IGT: IGT_7104 -> IGTPW_8283 * Piglit: piglit_4509 -> None CI-20190529: 20190529 CI_DRM_12529: 0ca1a1772701d5c3350d8736080e528e2f2f413e @ git://anongit.freedesktop.org/gfx-ci/linux IGTPW_8283: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_8283/index.html IGT_7104: fe5def13049225967770eaaf19ec01ef80e2adc5 @ 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_8283/index.html [-- Attachment #2: Type: text/html, Size: 32577 bytes --] ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI 2022-12-29 19:27 [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Mark Yacoub 2022-12-29 21:16 ` [igt-dev] ✓ Fi.CI.BAT: success for testing headers in CI (rev2) Patchwork 2022-12-29 23:27 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork @ 2022-12-30 12:51 ` Jani Nikula 2023-01-02 16:50 ` Kamil Konieczny 2 siblings, 1 reply; 5+ messages in thread From: Jani Nikula @ 2022-12-30 12:51 UTC (permalink / raw) To: Mark Yacoub, igt-dev; +Cc: Mark Yacoub On Thu, 29 Dec 2022, Mark Yacoub <markyacoub@chromium.org> wrote: > From: Mark Yacoub <markyacoub@google.com> A commit message would be appreciated to let us know what you're doing, regardless of the "DO NOT REVIEW" part. Thanks, Jani. > > --- > docs/chamelium.txt | 2 +- > lib/igt_edid.h | 1 + > lib/igt_eld.h | 1 + > lib/monitor_edids/monitor_edids_helper.c | 2 +- > tests/chamelium/kms_chamelium.c | 3132 ----------------- > tests/chamelium/kms_chamelium_audio.c | 858 +++++ > ...olor_chamelium.c => kms_chamelium_color.c} | 0 > tests/chamelium/kms_chamelium_edid.c | 543 +++ > tests/chamelium/kms_chamelium_frames.c | 1085 ++++++ > tests/chamelium/kms_chamelium_helper.c | 330 ++ > tests/chamelium/kms_chamelium_helper.h | 74 + > tests/chamelium/kms_chamelium_hpd.c | 512 +++ > tests/intel-ci/blacklist.txt | 2 +- > tests/intel-ci/fast-feedback.testlist | 18 +- > tests/kms_color_helper.h | 2 +- > tests/meson.build | 14 +- > tests/vc4_ci/vc4-chamelium-fast.testlist | 28 +- > 17 files changed, 3440 insertions(+), 3164 deletions(-) > delete mode 100644 tests/chamelium/kms_chamelium.c > create mode 100644 tests/chamelium/kms_chamelium_audio.c > rename tests/chamelium/{kms_color_chamelium.c => kms_chamelium_color.c} (100%) > create mode 100644 tests/chamelium/kms_chamelium_edid.c > create mode 100644 tests/chamelium/kms_chamelium_frames.c > create mode 100644 tests/chamelium/kms_chamelium_helper.c > create mode 100644 tests/chamelium/kms_chamelium_helper.h > create mode 100644 tests/chamelium/kms_chamelium_hpd.c > > diff --git a/docs/chamelium.txt b/docs/chamelium.txt > index c4c22468..f82c8b0c 100644 > --- a/docs/chamelium.txt > +++ b/docs/chamelium.txt > @@ -241,7 +241,7 @@ Current Support in IGT > > Support for the Chamelium platform in IGT is found in the following places: > * lib/igt_chamelium.c: library with Chamelium-related helpers > -* tests/kms_chamelium.c: sub-tests using the Chamelium > +* tests/kms_chamelium_*.c: sub-tests using the Chamelium > > As of early April 2019, the following features are tested by IGT: > * Pixel-by-pixel frame integrity tests for DP and HDMI > diff --git a/lib/igt_edid.h b/lib/igt_edid.h > index 477f16c2..85a9ef5e 100644 > --- a/lib/igt_edid.h > +++ b/lib/igt_edid.h > @@ -29,6 +29,7 @@ > #include "config.h" > > #include <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..c9e15f41 > --- /dev/null > +++ b/tests/chamelium/kms_chamelium_edid.c > @@ -0,0 +1,543 @@ > +// SPDX-License-Identifier: MIT > +/* > + * A Chamelium test for testing the EDID functionality. > + * > + * Copyright 2022 Google LLC. > + * > + * Authors: Mark Yacoub <markyacoub@chromium.org> > + */ > + > +#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..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..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 -- Jani Nikula, Intel Open Source Graphics Center ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI 2022-12-30 12:51 ` [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Jani Nikula @ 2023-01-02 16:50 ` Kamil Konieczny 0 siblings, 0 replies; 5+ messages in thread From: Kamil Konieczny @ 2023-01-02 16:50 UTC (permalink / raw) To: igt-dev; +Cc: Mark Yacoub Hi, On 2022-12-30 at 14:51:13 +0200, Jani Nikula wrote: > On Thu, 29 Dec 2022, Mark Yacoub <markyacoub@chromium.org> wrote: > > From: Mark Yacoub <markyacoub@google.com> > > A commit message would be appreciated to let us know what you're doing, > regardless of the "DO NOT REVIEW" part. > > Thanks, > Jani. > There is even better solution, just use igt-trybot intel-gfx-trybot@lists.freedesktop.org Example: https://patchwork.freedesktop.org/series/112048/ Regards, Kamil > > > > --- > > docs/chamelium.txt | 2 +- > > lib/igt_edid.h | 1 + > > lib/igt_eld.h | 1 + > > lib/monitor_edids/monitor_edids_helper.c | 2 +- > > tests/chamelium/kms_chamelium.c | 3132 ----------------- > > tests/chamelium/kms_chamelium_audio.c | 858 +++++ > > ...olor_chamelium.c => kms_chamelium_color.c} | 0 > > tests/chamelium/kms_chamelium_edid.c | 543 +++ > > tests/chamelium/kms_chamelium_frames.c | 1085 ++++++ > > tests/chamelium/kms_chamelium_helper.c | 330 ++ > > tests/chamelium/kms_chamelium_helper.h | 74 + > > tests/chamelium/kms_chamelium_hpd.c | 512 +++ > > tests/intel-ci/blacklist.txt | 2 +- > > tests/intel-ci/fast-feedback.testlist | 18 +- > > tests/kms_color_helper.h | 2 +- > > tests/meson.build | 14 +- > > tests/vc4_ci/vc4-chamelium-fast.testlist | 28 +- > > 17 files changed, 3440 insertions(+), 3164 deletions(-) > > delete mode 100644 tests/chamelium/kms_chamelium.c > > create mode 100644 tests/chamelium/kms_chamelium_audio.c > > rename tests/chamelium/{kms_color_chamelium.c => kms_chamelium_color.c} (100%) > > create mode 100644 tests/chamelium/kms_chamelium_edid.c > > create mode 100644 tests/chamelium/kms_chamelium_frames.c > > create mode 100644 tests/chamelium/kms_chamelium_helper.c > > create mode 100644 tests/chamelium/kms_chamelium_helper.h > > create mode 100644 tests/chamelium/kms_chamelium_hpd.c > > > > diff --git a/docs/chamelium.txt b/docs/chamelium.txt > > index c4c22468..f82c8b0c 100644 > > --- a/docs/chamelium.txt > > +++ b/docs/chamelium.txt > > @@ -241,7 +241,7 @@ Current Support in IGT > > > > Support for the Chamelium platform in IGT is found in the following places: > > * lib/igt_chamelium.c: library with Chamelium-related helpers > > -* tests/kms_chamelium.c: sub-tests using the Chamelium > > +* tests/kms_chamelium_*.c: sub-tests using the Chamelium > > > > As of early April 2019, the following features are tested by IGT: > > * Pixel-by-pixel frame integrity tests for DP and HDMI > > diff --git a/lib/igt_edid.h b/lib/igt_edid.h > > index 477f16c2..85a9ef5e 100644 > > --- a/lib/igt_edid.h > > +++ b/lib/igt_edid.h > > @@ -29,6 +29,7 @@ > > #include "config.h" > > > > #include <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..c9e15f41 > > --- /dev/null > > +++ b/tests/chamelium/kms_chamelium_edid.c > > @@ -0,0 +1,543 @@ > > +// SPDX-License-Identifier: MIT > > +/* > > + * A Chamelium test for testing the EDID functionality. > > + * > > + * Copyright 2022 Google LLC. > > + * > > + * Authors: Mark Yacoub <markyacoub@chromium.org> > > + */ > > + > > +#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..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..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 > > -- > Jani Nikula, Intel Open Source Graphics Center ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2023-01-02 16:50 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-12-29 19:27 [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Mark Yacoub 2022-12-29 21:16 ` [igt-dev] ✓ Fi.CI.BAT: success for testing headers in CI (rev2) Patchwork 2022-12-29 23:27 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork 2022-12-30 12:51 ` [igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI Jani Nikula 2023-01-02 16:50 ` Kamil Konieczny
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox