summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorSimon Ser <simon.ser@intel.com>2019-04-23 16:04:52 +0300
committerArkadiusz Hiler <arkadiusz.hiler@intel.com>2019-04-25 13:07:15 +0300
commit311baff151f90c1db6f57ee9515216b4f9da5db7 (patch)
treecff58efbdacda05b06e7d88a2ab3d68816687eb4 /tests
parent11e10bc575516c56978640fcc697c27f277c660a (diff)
tests/kms_chamelium: add dp-audio test
This new test ensures DisplayPort audio works by using the Chamelium. It enables the DisplayPort output and sends an audio signal containing a set of frequencies we choose to all HDMI/DisplayPort audio devices. It starts recording audio on the Chamelium device and uses the stream server to retrieve captured audio pages. It then checks that the capture audio signal contains the frequencies we sent, and only those, by computing a FFT. A new library has been added to libigt to communicate with the stream server. It implements a simple custom TCP protocol. In case the test fails, a WAV file with the captured data is saved on disk. Right now the test has a few limitations: - Only the first channel is checked - IGT only generates audio with a single sampling rate (48 KHz) - Audio data is not captured in real-time These limitations will be lifted in future patches. PulseAudio must not run during the tests since ALSA is used directly. To ensure this, edit /etc/pulse/client.conf and add `autospawn=no`. Then run `pulseaudio --kill`. This commit deletes the existing audio tests. They weren't run and required an exotic configuration (HDMI audio splitter, dummy HDMI sink and a line-in port on the DUT). This patch also changes lib/igt_audio to use uint16_t instead of short. The rationale is: - The standard says a short is at least 16 bit wide, but a short can be larger (in practice it won't happen, but better use types correctly) - It makes it clearer that the audio format is S16_LE, since "16" is in the type name. This patch depends on the following Chameleon bugs: - https://crbug.com/948060 - https://crbug.com/950857 Signed-off-by: Simon Ser <simon.ser@intel.com> Reviewed-by: Martin Peres <martin.peres@linux.intel.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/audio.c193
-rw-r--r--tests/kms_chamelium.c276
-rw-r--r--tests/meson.build9
3 files changed, 271 insertions, 207 deletions
diff --git a/tests/audio.c b/tests/audio.c
deleted file mode 100644
index 560876a3..00000000
--- a/tests/audio.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright © 2017 Intel Corporation
- *
- * 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:
- * Paul Kocialkowski <paul.kocialkowski@linux.intel.com>
- */
-
-#include "config.h"
-#include "igt.h"
-
-#define PLAYBACK_CHANNELS 2
-#define PLAYBACK_FRAMES 1024
-
-#define CAPTURE_SAMPLE_RATE 48000
-#define CAPTURE_CHANNELS 2
-#define CAPTURE_DEVICE_NAME "default"
-#define CAPTURE_FRAMES 2048
-
-#define RUN_TIMEOUT 2000
-
-struct test_data {
- struct alsa *alsa;
- struct audio_signal *signal;
-
- int streak;
-};
-
-static int sampling_rates[] = {
- 32000,
- 44100,
- 48000,
- 88200,
- 96000,
- 176400,
- 192000,
-};
-
-static int sampling_rates_count = sizeof(sampling_rates) / sizeof(int);
-
-static int test_frequencies[] = {
- 300,
- 600,
- 1200,
- 80000,
- 10000,
-};
-
-static int test_frequencies_count = sizeof(test_frequencies) / sizeof(int);
-
-static int output_callback(void *data, short *buffer, int frames)
-{
- struct test_data *test_data = (struct test_data *) data;
-
- audio_signal_fill(test_data->signal, buffer, frames);
-
- return 0;
-}
-
-static int input_callback(void *data, short *buffer, int frames)
-{
- struct test_data *test_data = (struct test_data *) data;
- bool detect;
-
- detect = audio_signal_detect(test_data->signal, CAPTURE_CHANNELS,
- CAPTURE_SAMPLE_RATE, buffer, frames);
- if (detect)
- test_data->streak++;
- else
- test_data->streak = 0;
-
- /* A streak of 3 gives confidence that the signal is good. */
- if (test_data->streak == 3)
- return 1;
-
- return 0;
-}
-
-static void test_integrity(const char *device_name)
-{
- struct test_data data;
- int sampling_rate;
- bool run = false;
- bool test;
- int i, j;
- int ret;
-
- data.alsa = alsa_init();
- igt_assert(data.alsa);
-
- ret = alsa_open_input(data.alsa, CAPTURE_DEVICE_NAME);
- igt_assert(ret >= 0);
-
- alsa_configure_input(data.alsa, CAPTURE_CHANNELS,
- CAPTURE_SAMPLE_RATE);
-
- alsa_register_input_callback(data.alsa, input_callback, &data,
- CAPTURE_FRAMES);
-
- for (i = 0; i < sampling_rates_count; i++) {
- ret = alsa_open_output(data.alsa, device_name);
- igt_assert(ret >= 0);
-
- sampling_rate = sampling_rates[i];
-
- test = alsa_test_output_configuration(data.alsa,
- PLAYBACK_CHANNELS,
- sampling_rate);
- if (!test) {
- alsa_close_output(data.alsa);
- continue;
- }
-
- igt_debug("Testing with sampling rate %d\n", sampling_rate);
-
- alsa_configure_output(data.alsa, PLAYBACK_CHANNELS,
- sampling_rate);
-
- data.signal = audio_signal_init(PLAYBACK_CHANNELS,
- sampling_rate);
- igt_assert(data.signal);
-
- for (j = 0; j < test_frequencies_count; j++)
- audio_signal_add_frequency(data.signal,
- test_frequencies[j]);
-
- audio_signal_synthesize(data.signal);
-
- alsa_register_output_callback(data.alsa, output_callback,
- &data, PLAYBACK_FRAMES);
-
- data.streak = 0;
-
- ret = alsa_run(data.alsa, RUN_TIMEOUT);
- igt_assert(ret > 0);
-
- audio_signal_clean(data.signal);
- free(data.signal);
-
- alsa_close_output(data.alsa);
-
- run = true;
- }
-
- /* Make sure we tested at least one frequency */
- igt_assert(run);
-
- alsa_close_input(data.alsa);
- free(data.alsa);
-}
-
-static void test_suspend_resume_integrity(const char *device_name,
- enum igt_suspend_state state,
- enum igt_suspend_test test)
-{
- test_integrity(device_name);
-
- igt_system_suspend_autoresume(state, test);
-
- test_integrity(device_name);
-}
-
-igt_main
-{
- igt_subtest("hdmi-integrity")
- test_integrity("HDMI");
-
- igt_subtest("hdmi-integrity-after-suspend")
- test_suspend_resume_integrity("HDMI", SUSPEND_STATE_MEM,
- SUSPEND_TEST_NONE);
-
- igt_subtest("hdmi-integrity-after-hibernate")
- test_suspend_resume_integrity("HDMI", SUSPEND_STATE_DISK,
- SUSPEND_TEST_DEVICES);
-}
diff --git a/tests/kms_chamelium.c b/tests/kms_chamelium.c
index 2dc1049d..a712250a 100644
--- a/tests/kms_chamelium.c
+++ b/tests/kms_chamelium.c
@@ -413,7 +413,7 @@ test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port,
static igt_output_t *
prepare_output(data_t *data,
- struct chamelium_port *port)
+ struct chamelium_port *port, bool set_edid)
{
igt_display_t *display = &data->display;
igt_output_t *output;
@@ -428,7 +428,8 @@ prepare_output(data_t *data,
/* The chamelium's default EDID has a lot of resolutions, way more then
* we need to test
*/
- chamelium_port_set_edid(data->chamelium, port, data->edid_id);
+ if (set_edid)
+ chamelium_port_set_edid(data->chamelium, port, data->edid_id);
chamelium_plug(data->chamelium, port);
wait_for_connector(data, port, DRM_MODE_CONNECTED);
@@ -613,7 +614,7 @@ static void test_display_one_mode(data_t *data, struct chamelium_port *port,
reset_state(data, port);
- output = prepare_output(data, port);
+ output = prepare_output(data, port, true);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -644,7 +645,7 @@ static void test_display_all_modes(data_t *data, struct chamelium_port *port,
reset_state(data, port);
- output = prepare_output(data, port);
+ output = prepare_output(data, port, true);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -679,7 +680,7 @@ test_display_frame_dump(data_t *data, struct chamelium_port *port)
reset_state(data, port);
- output = prepare_output(data, port);
+ output = prepare_output(data, port, true);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -710,6 +711,266 @@ test_display_frame_dump(data_t *data, struct chamelium_port *port)
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_DURATION 2000 /* ms */
+/* A streak of 3 gives confidence that the signal is good. */
+#define MIN_STREAK 3
+
+/* TODO: Chamelium only supports 48KHz for now */
+static int sampling_rates[] = {
+/* 32000, */
+/* 44100, */
+ 48000,
+/* 88200, */
+/* 96000, */
+/* 176400, */
+/* 192000, */
+};
+
+static int sampling_rates_count = sizeof(sampling_rates) / sizeof(int);
+
+static int test_frequencies[] = {
+ 300,
+ 600,
+ 1200,
+ 80000,
+ 10000,
+};
+
+static int test_frequencies_count = sizeof(test_frequencies) / sizeof(int);
+
+static int
+output_callback(void *data, short *buffer, int frames)
+{
+ struct audio_signal *signal = (struct audio_signal *) data;
+
+ audio_signal_fill(signal, buffer, frames);
+
+ return 0;
+}
+
+static bool
+do_test_display_audio(data_t *data, struct chamelium_port *port,
+ struct alsa *alsa, int playback_channels,
+ int playback_rate)
+{
+ int ret, capture_rate, capture_channels, msec;
+ struct chamelium_audio_file *audio_file;
+ struct chamelium_stream *stream;
+ enum chamelium_stream_realtime_mode stream_mode;
+ struct audio_signal *signal;
+ int32_t *recv, *buf;
+ double *channel;
+ size_t i, streak, page_count;
+ size_t recv_len, buf_len, buf_cap, buf_size, channel_len;
+ bool ok;
+ char dump_suffix[64];
+ char *dump_path = NULL;
+ int dump_fd = -1;
+
+ if (!alsa_test_output_configuration(alsa, playback_channels,
+ playback_rate))
+ return false;
+
+ igt_debug("Testing with playback sampling rate %d\n", playback_rate);
+ alsa_configure_output(alsa, playback_channels, playback_rate);
+
+ chamelium_start_capturing_audio(data->chamelium, port, false);
+
+ stream = chamelium_stream_init();
+ igt_assert(stream);
+
+ stream_mode = CHAMELIUM_STREAM_REALTIME_STOP_WHEN_OVERFLOW;
+ ok = chamelium_stream_dump_realtime_audio(stream, stream_mode);
+ igt_assert(ok);
+
+ chamelium_stream_audio_format(stream, &capture_rate, &capture_channels);
+
+ if (igt_frame_dump_is_enabled()) {
+ snprintf(dump_suffix, sizeof(dump_suffix), "capture-%dch-%d",
+ playback_channels, playback_rate);
+
+ dump_fd = audio_create_wav_file_s32_le(dump_suffix,
+ capture_rate,
+ capture_channels,
+ &dump_path);
+ igt_assert(dump_fd >= 0);
+ }
+
+ signal = audio_signal_init(playback_channels, playback_rate);
+ igt_assert(signal);
+
+ for (i = 0; i < test_frequencies_count; i++)
+ audio_signal_add_frequency(signal, test_frequencies[i]);
+ audio_signal_synthesize(signal);
+
+ alsa_register_output_callback(alsa, output_callback, signal,
+ PLAYBACK_SAMPLES);
+
+ /* TODO: detect signal in real-time */
+ ret = alsa_run(alsa, AUDIO_DURATION);
+ igt_assert(ret == 0);
+
+ alsa_close_output(alsa);
+
+ /* 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. */
+ channel_len = CAPTURE_SAMPLES;
+ channel = malloc(sizeof(double) * channel_len);
+
+ buf_cap = capture_channels * channel_len;
+ buf = malloc(sizeof(int32_t) * buf_cap);
+ buf_len = 0;
+
+ recv = NULL;
+ recv_len = 0;
+
+ streak = 0;
+ msec = 0;
+ i = 0;
+ while (streak < MIN_STREAK && msec < AUDIO_DURATION) {
+ ok = chamelium_stream_receive_realtime_audio(stream,
+ &page_count,
+ &recv, &recv_len);
+ igt_assert(ok);
+
+ 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);
+
+ if (dump_fd >= 0) {
+ buf_size = buf_len * sizeof(int32_t);
+ igt_assert(write(dump_fd, buf, buf_size) == buf_size);
+ }
+
+ /* TODO: check other channels too, not just the first one */
+ audio_extract_channel_s32_le(channel, channel_len, buf, buf_len,
+ capture_channels, 0);
+
+ msec = i * channel_len / (double) capture_rate * 1000;
+ igt_debug("Detecting audio signal, t=%d msec\n", msec);
+
+ if (audio_signal_detect(signal, capture_rate, channel,
+ channel_len))
+ streak++;
+ else
+ streak = 0;
+
+ buf_len = 0;
+ i++;
+ }
+
+ if (dump_fd >= 0) {
+ close(dump_fd);
+ if (streak == MIN_STREAK) {
+ /* Test succeeded, no need to keep the captured data */
+ unlink(dump_path);
+ } else
+ igt_debug("Saved captured audio data to %s\n", dump_path);
+ free(dump_path);
+ }
+
+ free(recv);
+ free(buf);
+ free(channel);
+
+ ok = chamelium_stream_stop_realtime_audio(stream);
+ igt_assert(ok);
+
+ audio_file = chamelium_stop_capturing_audio(data->chamelium,
+ port);
+ if (audio_file) {
+ igt_debug("Audio file saved on the Chamelium in %s\n",
+ audio_file->path);
+ chamelium_destroy_audio_file(audio_file);
+ }
+
+ audio_signal_clean(signal);
+ free(signal);
+
+ chamelium_stream_deinit(stream);
+
+ igt_assert(streak == MIN_STREAK);
+ return true;
+}
+
+static void
+test_display_audio(data_t *data, struct chamelium_port *port,
+ const char *audio_device)
+{
+ bool run = false;
+ 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;
+
+ igt_require(alsa_has_exclusive_access());
+
+ alsa = alsa_init();
+ igt_assert(alsa);
+
+ reset_state(data, port);
+
+ /* Use the default Chamelium EDID for this test, as the base IGT EDID
+ * doesn't advertise audio support (see drm_detect_monitor_audio in
+ * the kernel tree). */
+ output = prepare_output(data, port, false);
+ 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,
+ LOCAL_DRM_FORMAT_MOD_NONE,
+ 0, 0, 0, &fb);
+ igt_assert(fb_id > 0);
+
+ enable_output(data, port, output, mode, &fb);
+
+ for (i = 0; i < sampling_rates_count; i++) {
+ ret = alsa_open_output(alsa, audio_device);
+ igt_assert(ret >= 0);
+
+ /* TODO: playback on all 8 available channels */
+ run |= do_test_display_audio(data, port, alsa,
+ PLAYBACK_CHANNELS,
+ sampling_rates[i]);
+
+ alsa_close_output(alsa);
+ }
+
+ /* Make sure we tested at least one frequency. */
+ igt_assert(run);
+
+ igt_remove_fb(data->drm_fd, &fb);
+
+ drmModeFreeConnector(connector);
+
+ free(alsa);
+}
+
+
static void select_tiled_modifier(igt_plane_t *plane, uint32_t width,
uint32_t height, uint32_t format,
uint64_t *modifier)
@@ -1037,7 +1298,7 @@ static void test_display_planes_random(data_t *data,
reset_state(data, port);
/* Find the connector and pipe. */
- output = prepare_output(data, port);
+ output = prepare_output(data, port, true);
mode = igt_output_get_mode(output);
@@ -1308,6 +1569,9 @@ igt_main
connector_subtest("dp-frame-dump", DisplayPort)
test_display_frame_dump(&data, port);
+
+ connector_subtest("dp-audio", DisplayPort)
+ test_display_audio(&data, port, "HDMI");
}
igt_subtest_group {
diff --git a/tests/meson.build b/tests/meson.build
index e3c8b07f..711979b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -239,20 +239,13 @@ if libdrm_nouveau.found()
test_deps += libdrm_nouveau
endif
-if _build_chamelium and chamelium.found()
+if chamelium_found
test_progs += [
'kms_chamelium',
]
test_deps += chamelium
endif
-if _build_audio and alsa.found() and gsl.found()
- test_progs += [
- 'audio',
- ]
- test_deps += alsa
-endif
-
test_executables = []
test_list = []