[igt-dev] [PATCH v2] [DO NOT REVIEW] testing headers in CI
Kamil Konieczny
kamil.konieczny at linux.intel.com
Mon Jan 2 16:50:45 UTC 2023
Hi,
On 2022-12-30 at 14:51:13 +0200, Jani Nikula wrote:
> On Thu, 29 Dec 2022, Mark Yacoub <markyacoub at chromium.org> wrote:
> > From: Mark Yacoub <markyacoub at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at kms_frontbuffer_tracking@.*drrs.*
> > # is too costly in comparison to the value
> > # provided.
> > ###############################################
> > -igt at kms_chamelium@hdmi-.*-planes-random
> > +igt at 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 at kms_addfb_basic@unused-modifier
> > igt at kms_addfb_basic@unused-offsets
> > igt at kms_addfb_basic@unused-pitches
> > igt at kms_busy@basic
> > -igt at kms_chamelium@dp-hpd-fast
> > -igt at kms_chamelium@dp-edid-read
> > -igt at kms_chamelium@dp-crc-fast
> > -igt at kms_chamelium@hdmi-hpd-fast
> > -igt at kms_chamelium@hdmi-edid-read
> > -igt at kms_chamelium@hdmi-crc-fast
> > -igt at kms_chamelium@vga-hpd-fast
> > -igt at kms_chamelium@vga-edid-read
> > +igt at kms_chamelium_hpd@dp-hpd-fast
> > +igt at kms_chamelium_edid@dp-edid-read
> > +igt at kms_chamelium_frames@dp-crc-fast
> > +igt at kms_chamelium_hpd@hdmi-hpd-fast
> > +igt at kms_chamelium_edid@hdmi-edid-read
> > +igt at kms_chamelium_frames@hdmi-crc-fast
> > +igt at kms_chamelium_hpd@vga-hpd-fast
> > +igt at kms_chamelium_edid@vga-edid-read
> > igt at kms_prop_blob@basic
> > igt at kms_cursor_legacy@basic-busy-flip-before-cursor
> > igt at kms_cursor_legacy@basic-flip-after-cursor
> > @@ -174,5 +174,5 @@ igt at i915_suspend@basic-s2idle-without-i915
> > igt at i915_suspend@basic-s3-without-i915
> > igt at gem_exec_suspend@basic-s0
> > igt at gem_exec_suspend@basic-s3
> > -igt at kms_chamelium@common-hpd-after-suspend
> > +igt at kms_chamelium_hpd@common-hpd-after-suspend
> > igt at 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 at kms_chamelium@hdmi-crc-abgr8888
> > -igt at kms_chamelium@hdmi-crc-argb1555
> > -igt at kms_chamelium@hdmi-crc-argb8888
> > -igt at kms_chamelium@hdmi-crc-bgr565
> > -igt at kms_chamelium@hdmi-crc-bgr888
> > -igt at kms_chamelium@hdmi-crc-fast
> > -igt at kms_chamelium@hdmi-crc-rgb565
> > -igt at kms_chamelium@hdmi-crc-rgb888
> > -igt at kms_chamelium@hdmi-crc-xbgr8888
> > -igt at kms_chamelium@hdmi-crc-xrgb1555
> > -igt at kms_chamelium@hdmi-crc-xrgb8888
> > -igt at kms_chamelium@hdmi-edid-read
> > -igt at kms_chamelium@hdmi-hpd
> > -igt at kms_chamelium@hdmi-hpd-fast
> > +igt at kms_chamelium_frames@hdmi-crc-abgr8888
> > +igt at kms_chamelium_frames@hdmi-crc-argb1555
> > +igt at kms_chamelium_frames@hdmi-crc-argb8888
> > +igt at kms_chamelium_frames@hdmi-crc-bgr565
> > +igt at kms_chamelium_frames@hdmi-crc-bgr888
> > +igt at kms_chamelium_frames@hdmi-crc-fast
> > +igt at kms_chamelium_frames@hdmi-crc-rgb565
> > +igt at kms_chamelium_frames@hdmi-crc-rgb888
> > +igt at kms_chamelium_frames@hdmi-crc-xbgr8888
> > +igt at kms_chamelium_frames@hdmi-crc-xrgb1555
> > +igt at kms_chamelium_frames@hdmi-crc-xrgb8888
> > +igt at kms_chamelium_edid@hdmi-edid-read
> > +igt at kms_chamelium_hpd@hdmi-hpd
> > +igt at kms_chamelium_hpd@hdmi-hpd-fast
>
> --
> Jani Nikula, Intel Open Source Graphics Center
More information about the igt-dev
mailing list