public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
From: Simon Ser <simon.ser@intel.com>
To: igt-dev@lists.freedesktop.org
Subject: [igt-dev] [PATCH i-g-t v4 5/9] tests/kms_chamelium: generate an EDID with audio support
Date: Wed, 15 May 2019 10:29:18 +0300	[thread overview]
Message-ID: <20190515072922.30768-6-simon.ser@intel.com> (raw)
In-Reply-To: <20190515072922.30768-1-simon.ser@intel.com>

The default Chamelium EDID works great with DisplayPort but has issues with
HDMI. The EDID advertises itself as a DisplayPort-only device, which causes
issues with HDMI audio. Additionally, the EDID doesn't contain a magic
incantation within a vendor-specific data block that is needed for audio to
work.

This patch makes it so an EDID is generated and set by IGT. The EDID is the
base IGT EDID patched to append a Short Audio Descriptor and Vendor Specific
Data block extension.

This generated EDID is suitable for HDMI audio. For DisplayPort audio, we keep
using the Chamelium's default EDID.

A new enum test_edid has been introduced to prevent mismatches between the
Chamelium EDID ID and the raw EDID.

The global variable holding the test frequencies has been renamed to prevent
shadowing by local variables.

Signed-off-by: Simon Ser <simon.ser@intel.com>
Reviewed-by: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
---
 lib/igt_chamelium.c   |   9 +-
 tests/kms_chamelium.c | 192 ++++++++++++++++++++++++++++++------------
 2 files changed, 147 insertions(+), 54 deletions(-)

diff --git a/lib/igt_chamelium.c b/lib/igt_chamelium.c
index 1d4b6aa9ffd1..eeab7b84e03a 100644
--- a/lib/igt_chamelium.c
+++ b/lib/igt_chamelium.c
@@ -38,6 +38,7 @@
 #include "igt_chamelium.h"
 #include "igt_core.h"
 #include "igt_aux.h"
+#include "igt_edid.h"
 #include "igt_frame.h"
 #include "igt_list.h"
 #include "igt_kms.h"
@@ -530,14 +531,18 @@ void chamelium_schedule_hpd_toggle(struct chamelium *chamelium,
  *
  * Returns: The ID of the EDID uploaded to the chamelium.
  */
-int chamelium_new_edid(struct chamelium *chamelium, const unsigned char *edid)
+int chamelium_new_edid(struct chamelium *chamelium,
+		       const unsigned char *raw_edid)
 {
 	xmlrpc_value *res;
 	struct chamelium_edid *allocated_edid;
 	int edid_id;
+	struct edid *edid = (struct edid *) raw_edid;
+	size_t edid_size = sizeof(struct edid) +
+			   edid->extensions_len * sizeof(struct edid_ext);
 
 	res = chamelium_rpc(chamelium, NULL, "CreateEdid", "(6)",
-			    edid, EDID_LENGTH);
+			    raw_edid, edid_size);
 
 	xmlrpc_read_int(&chamelium->env, res, &edid_id);
 	xmlrpc_DECREF(res);
diff --git a/tests/kms_chamelium.c b/tests/kms_chamelium.c
index c2d5793a41c5..6d9d4111ea9b 100644
--- a/tests/kms_chamelium.c
+++ b/tests/kms_chamelium.c
@@ -27,12 +27,20 @@
 #include "config.h"
 #include "igt.h"
 #include "igt_vc4.h"
+#include "igt_edid.h"
 
 #include <fcntl.h>
 #include <pthread.h>
 #include <string.h>
 #include <stdatomic.h>
 
+enum test_edid {
+	TEST_EDID_CHAMELIUM_DEFAULT,
+	TEST_EDID_BASE,
+	TEST_EDID_ALT,
+	TEST_EDID_HDMI_AUDIO,
+};
+
 typedef struct {
 	struct chamelium *chamelium;
 	struct chamelium_port **ports;
@@ -41,8 +49,7 @@ typedef struct {
 
 	int drm_fd;
 
-	int edid_id;
-	int alt_edid_id;
+	int edids[4];
 } data_t;
 
 #define HOTPLUG_TIMEOUT 20 /* seconds */
@@ -255,18 +262,26 @@ test_basic_hotplug(data_t *data, struct chamelium_port *port, int toggle_count)
 	igt_hpd_storm_reset(data->drm_fd);
 }
 
+static const unsigned char *get_edid(enum test_edid edid);
+
+static void set_edid(data_t *data, struct chamelium_port *port,
+		     enum test_edid edid)
+{
+	chamelium_port_set_edid(data->chamelium, port, data->edids[edid]);
+}
+
 static void
-test_edid_read(data_t *data, struct chamelium_port *port,
-	       int edid_id, const unsigned char *edid)
+test_edid_read(data_t *data, struct chamelium_port *port, enum test_edid edid)
 {
 	drmModePropertyBlobPtr edid_blob = NULL;
+	const unsigned char *raw_edid = get_edid(edid);
 	drmModeConnector *connector = chamelium_port_get_connector(
 	    data->chamelium, port, false);
 	uint64_t edid_blob_id;
 
 	reset_state(data, port);
 
-	chamelium_port_set_edid(data->chamelium, port, edid_id);
+	set_edid(data, port, edid);
 	chamelium_plug(data->chamelium, port);
 	wait_for_connector(data, port, DRM_MODE_CONNECTED);
 
@@ -278,7 +293,7 @@ test_edid_read(data_t *data, struct chamelium_port *port,
 	igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd,
 						      edid_blob_id));
 
-	igt_assert(memcmp(edid, edid_blob->data, EDID_LENGTH) == 0);
+	igt_assert(memcmp(raw_edid, edid_blob->data, EDID_LENGTH) == 0);
 
 	drmModeFreePropertyBlob(edid_blob);
 	drmModeFreeConnector(connector);
@@ -406,8 +421,8 @@ static void
 test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port,
 				enum igt_suspend_state state,
 				enum igt_suspend_test test,
-				int edid_id,
-				int alt_edid_id)
+				enum test_edid edid,
+				enum test_edid alt_edid)
 {
 	struct udev_monitor *mon = igt_watch_hotplug();
 	bool link_status_failed[2][data->port_count];
@@ -420,7 +435,7 @@ test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port,
 	igt_flush_hotplugs(mon);
 
 	/* First plug in the port */
-	chamelium_port_set_edid(data->chamelium, port, edid_id);
+	set_edid(data, port, edid);
 	chamelium_plug(data->chamelium, port);
 	igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT));
 
@@ -430,7 +445,7 @@ test_suspend_resume_edid_change(data_t *data, struct chamelium_port *port,
 	 * Change the edid before we suspend. On resume, the machine should
 	 * notice the EDID change and fire a hotplug event.
 	 */
-	chamelium_port_set_edid(data->chamelium, port, alt_edid_id);
+	set_edid(data, port, alt_edid);
 
 	get_connectors_link_status_failed(data, link_status_failed[0]);
 
@@ -447,8 +462,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, bool set_edid)
+prepare_output(data_t *data, struct chamelium_port *port, enum test_edid edid)
 {
 	igt_display_t *display = &data->display;
 	igt_output_t *output;
@@ -461,10 +475,10 @@ prepare_output(data_t *data,
 	igt_require(res = drmModeGetResources(data->drm_fd));
 
 	/* The chamelium's default EDID has a lot of resolutions, way more then
-	 * we need to test
+	 * we need to test. Additionally the default EDID doesn't support HDMI
+	 * audio.
 	 */
-	if (set_edid)
-		chamelium_port_set_edid(data->chamelium, port, data->edid_id);
+	set_edid(data, port, edid);
 
 	chamelium_plug(data->chamelium, port);
 	wait_for_connector(data, port, DRM_MODE_CONNECTED);
@@ -649,7 +663,7 @@ static void test_display_one_mode(data_t *data, struct chamelium_port *port,
 
 	reset_state(data, port);
 
-	output = prepare_output(data, port, true);
+	output = prepare_output(data, port, TEST_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);
@@ -680,7 +694,7 @@ static void test_display_all_modes(data_t *data, struct chamelium_port *port,
 
 	reset_state(data, port);
 
-	output = prepare_output(data, port, true);
+	output = prepare_output(data, port, TEST_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);
@@ -715,7 +729,7 @@ test_display_frame_dump(data_t *data, struct chamelium_port *port)
 
 	reset_state(data, port);
 
-	output = prepare_output(data, port, true);
+	output = prepare_output(data, port, TEST_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);
@@ -758,7 +772,7 @@ test_display_frame_dump(data_t *data, struct chamelium_port *port)
 /* A streak of 3 gives confidence that the signal is good. */
 #define MIN_STREAK 3
 
-static int sampling_rates[] = {
+static int test_sampling_rates[] = {
 	32000,
 	44100,
 	48000,
@@ -768,7 +782,7 @@ static int sampling_rates[] = {
 	192000,
 };
 
-static int sampling_rates_count = sizeof(sampling_rates) / sizeof(int);
+static int test_sampling_rates_count = sizeof(test_sampling_rates) / sizeof(int);
 
 static int test_frequencies[] = {
 	300,
@@ -1019,7 +1033,7 @@ do_test_display_audio(data_t *data, struct chamelium_port *port,
 
 static void
 test_display_audio(data_t *data, struct chamelium_port *port,
-		   const char *audio_device)
+		   const char *audio_device, enum test_edid edid)
 {
 	bool run = false;
 	struct alsa *alsa;
@@ -1043,10 +1057,7 @@ test_display_audio(data_t *data, struct chamelium_port *port,
 
 	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);
+	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);
@@ -1065,14 +1076,14 @@ test_display_audio(data_t *data, struct chamelium_port *port,
 
 	enable_output(data, port, output, mode, &fb);
 
-	for (i = 0; i < sampling_rates_count; i++) {
+	for (i = 0; i < test_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]);
+					     test_sampling_rates[i]);
 
 		alsa_close_output(alsa);
 	}
@@ -1415,7 +1426,7 @@ static void test_display_planes_random(data_t *data,
 	reset_state(data, port);
 
 	/* Find the connector and pipe. */
-	output = prepare_output(data, port, true);
+	output = prepare_output(data, port, TEST_EDID_BASE);
 
 	mode = igt_output_get_mode(output);
 
@@ -1579,6 +1590,82 @@ test_hpd_storm_disable(data_t *data, struct chamelium_port *port, int width)
 	igt_hpd_storm_reset(data->drm_fd);
 }
 
+#define HDMI_AUDIO_EDID_SIZE (sizeof(struct edid) + sizeof(struct edid_ext))
+
+static unsigned const char *get_hdmi_audio_edid(void)
+{
+	int channels;
+	uint8_t sampling_rates, sample_sizes;
+	static unsigned char raw_edid[HDMI_AUDIO_EDID_SIZE] = {0};
+	struct edid *edid;
+	struct edid_ext *edid_ext;
+	struct edid_cea *edid_cea;
+	char *cea_data;
+	struct edid_cea_data_block *block;
+	struct cea_sad sad = {0};
+	const struct cea_vsd *vsd;
+	size_t cea_data_size, vsd_size;
+
+	/* Initialize the Short Audio Descriptor for PCM */
+	channels = 2; /* TODO: speaker alloc blocks for > 2 channels */
+	sampling_rates = CEA_SAD_SAMPLING_RATE_32KHZ |
+			 CEA_SAD_SAMPLING_RATE_44KHZ |
+			 CEA_SAD_SAMPLING_RATE_48KHZ |
+			 CEA_SAD_SAMPLING_RATE_88KHZ |
+			 CEA_SAD_SAMPLING_RATE_96KHZ |
+			 CEA_SAD_SAMPLING_RATE_176KHZ |
+			 CEA_SAD_SAMPLING_RATE_192KHZ;
+	sample_sizes = CEA_SAD_SAMPLE_SIZE_16 |
+		       CEA_SAD_SAMPLE_SIZE_20 |
+		       CEA_SAD_SAMPLE_SIZE_24;
+	cea_sad_init_pcm(&sad, channels, sampling_rates, sample_sizes);
+
+	/* Create a new EDID from the base IGT EDID, and add an
+	 * extension that advertises audio support. */
+	edid = (struct edid *) raw_edid;
+	memcpy(edid, igt_kms_get_base_edid(), sizeof(struct edid));
+	edid->extensions_len = 1;
+	edid_ext = &edid->extensions[0];
+	edid_cea = &edid_ext->data.cea;
+	cea_data = edid_cea->data;
+	cea_data_size = 0;
+
+	block = (struct edid_cea_data_block *) &cea_data[cea_data_size];
+	cea_data_size += edid_cea_data_block_set_sad(block, &sad, 1);
+
+	/* A Vendor Specific Data block is needed for HDMI audio */
+	block = (struct edid_cea_data_block *) &cea_data[cea_data_size];
+	vsd = cea_vsd_get_hdmi_default(&vsd_size);
+	cea_data_size += edid_cea_data_block_set_vsd(block, vsd,
+						     vsd_size);
+
+	igt_assert(cea_data_size <= sizeof(edid_cea->data));
+
+	edid_ext_set_cea(edid_ext, cea_data_size,
+			 EDID_CEA_BASIC_AUDIO);
+
+	edid_update_checksum(edid);
+	edid_ext_update_cea_checksum(edid_ext);
+
+	return raw_edid;
+}
+
+static const unsigned char *get_edid(enum test_edid edid)
+{
+	switch (edid) {
+	case TEST_EDID_CHAMELIUM_DEFAULT:
+		igt_assert(0); /* we don't have the raw data for this one */
+		return NULL;
+	case TEST_EDID_BASE:
+		return igt_kms_get_base_edid();
+	case TEST_EDID_ALT:
+		return igt_kms_get_alt_edid();
+	case TEST_EDID_HDMI_AUDIO:
+		return get_hdmi_audio_edid();
+	}
+	assert(0); /* unreachable */
+}
+
 #define for_each_port(p, port)            \
 	for (p = 0, port = data.ports[p]; \
 	     p < data.port_count;         \
@@ -1595,7 +1682,8 @@ static data_t data;
 igt_main
 {
 	struct chamelium_port *port;
-	int edid_id, alt_edid_id, p;
+	int p;
+	size_t i;
 
 	igt_fixture {
 		igt_skip_on_simulation();
@@ -1607,12 +1695,11 @@ igt_main
 		data.ports = chamelium_get_ports(data.chamelium,
 						 &data.port_count);
 
-		edid_id = chamelium_new_edid(data.chamelium,
-					     igt_kms_get_base_edid());
-		alt_edid_id = chamelium_new_edid(data.chamelium,
-						 igt_kms_get_alt_edid());
-		data.edid_id = edid_id;
-		data.alt_edid_id = alt_edid_id;
+		data.edids[TEST_EDID_CHAMELIUM_DEFAULT] = CHAMELIUM_DEFAULT_EDID;
+		for (i = 1; i < sizeof(data.edids) / sizeof(data.edids[0]); ++i) {
+			data.edids[i] = chamelium_new_edid(data.chamelium,
+							   get_edid(i));
+		}
 
 		/* So fbcon doesn't try to reprobe things itself */
 		kmstest_set_vt_graphics_mode();
@@ -1636,10 +1723,8 @@ igt_main
 					   HPD_TOGGLE_COUNT_FAST);
 
 		connector_subtest("dp-edid-read", DisplayPort) {
-			test_edid_read(&data, port, edid_id,
-				       igt_kms_get_base_edid());
-			test_edid_read(&data, port, alt_edid_id,
-				       igt_kms_get_alt_edid());
+			test_edid_read(&data, port, TEST_EDID_BASE);
+			test_edid_read(&data, port, TEST_EDID_ALT);
 		}
 
 		connector_subtest("dp-hpd-after-suspend", DisplayPort)
@@ -1664,13 +1749,15 @@ igt_main
 			test_suspend_resume_edid_change(&data, port,
 							SUSPEND_STATE_MEM,
 							SUSPEND_TEST_NONE,
-							edid_id, alt_edid_id);
+							TEST_EDID_BASE,
+							TEST_EDID_ALT);
 
 		connector_subtest("dp-edid-change-during-hibernate", DisplayPort)
 			test_suspend_resume_edid_change(&data, port,
 							SUSPEND_STATE_DISK,
 							SUSPEND_TEST_DEVICES,
-							edid_id, alt_edid_id);
+							TEST_EDID_BASE,
+							TEST_EDID_ALT);
 
 		connector_subtest("dp-crc-single", DisplayPort)
 			test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888,
@@ -1687,8 +1774,11 @@ igt_main
 		connector_subtest("dp-frame-dump", DisplayPort)
 			test_display_frame_dump(&data, port);
 
+		/* The EDID we generate advertises HDMI audio, not DP audio.
+		 * Use the Chamelium's default EDID for DP audio. */
 		connector_subtest("dp-audio", DisplayPort)
-			test_display_audio(&data, port, "HDMI");
+			test_display_audio(&data, port, "HDMI",
+					   TEST_EDID_CHAMELIUM_DEFAULT);
 	}
 
 	igt_subtest_group {
@@ -1706,10 +1796,8 @@ igt_main
 					   HPD_TOGGLE_COUNT_FAST);
 
 		connector_subtest("hdmi-edid-read", HDMIA) {
-			test_edid_read(&data, port, edid_id,
-				       igt_kms_get_base_edid());
-			test_edid_read(&data, port, alt_edid_id,
-				       igt_kms_get_alt_edid());
+			test_edid_read(&data, port, TEST_EDID_BASE);
+			test_edid_read(&data, port, TEST_EDID_ALT);
 		}
 
 		connector_subtest("hdmi-hpd-after-suspend", HDMIA)
@@ -1734,13 +1822,15 @@ igt_main
 			test_suspend_resume_edid_change(&data, port,
 							SUSPEND_STATE_MEM,
 							SUSPEND_TEST_NONE,
-							edid_id, alt_edid_id);
+							TEST_EDID_BASE,
+							TEST_EDID_ALT);
 
 		connector_subtest("hdmi-edid-change-during-hibernate", HDMIA)
 			test_suspend_resume_edid_change(&data, port,
 							SUSPEND_STATE_DISK,
 							SUSPEND_TEST_DEVICES,
-							edid_id, alt_edid_id);
+							TEST_EDID_BASE,
+							TEST_EDID_ALT);
 
 		connector_subtest("hdmi-crc-single", HDMIA)
 			test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888,
@@ -1851,10 +1941,8 @@ igt_main
 			test_basic_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST);
 
 		connector_subtest("vga-edid-read", VGA) {
-			test_edid_read(&data, port, edid_id,
-				       igt_kms_get_base_edid());
-			test_edid_read(&data, port, alt_edid_id,
-				       igt_kms_get_alt_edid());
+			test_edid_read(&data, port, TEST_EDID_BASE);
+			test_edid_read(&data, port, TEST_EDID_ALT);
 		}
 
 		connector_subtest("vga-hpd-after-suspend", VGA)
-- 
2.21.0

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

  parent reply	other threads:[~2019-05-15  7:29 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-15  7:29 [igt-dev] [PATCH i-g-t v4 0/9] tests/kms_chamelium: add HDMI audio test Simon Ser
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 1/9] tests/kms_chamelium: skip if GetAudioFormat is not supported Simon Ser
2019-05-16  7:27   ` [igt-dev] [PATCH i-g-t v5] " Simon Ser
2019-05-16  8:30     ` Arkadiusz Hiler
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 2/9] lib/igt_edid: add support for Short Audio Descriptors Simon Ser
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 3/9] lib/igt_edid: add support for Vendor Specific Data blocks Simon Ser
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 4/9] lib/igt_chamelium: add CHAMELIUM_DEFAULT_EDID Simon Ser
2019-05-15  7:29 ` Simon Ser [this message]
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 6/9] tests/kms_chamelium: disable >48KHz audio tests Simon Ser
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 7/9] tests/kms_chamelium: enable audio test on HDMI ports Simon Ser
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 8/9] tests/kms_chamelium: don't abort audio test on first fail Simon Ser
2019-05-15  7:29 ` [igt-dev] [PATCH i-g-t v4 9/9] HAX: add {dp, hdmi}-audio test to fast-feedback Simon Ser
2019-05-15  8:25 ` [igt-dev] ✗ Fi.CI.BAT: failure for tests/kms_chamelium: add HDMI audio test (rev2) Patchwork
2019-05-15  8:36   ` Ser, Simon
2019-05-15 13:30     ` Arkadiusz Hiler
2019-05-16  9:44     ` Petri Latvala
2019-05-15 16:37 ` [igt-dev] ✓ Fi.CI.IGT: success " Patchwork
2019-05-16  7:58 ` [igt-dev] ✓ Fi.CI.BAT: success for tests/kms_chamelium: add HDMI audio test (rev3) Patchwork
2019-05-16  9:52 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork
2019-05-16 10:19   ` Arkadiusz Hiler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190515072922.30768-6-simon.ser@intel.com \
    --to=simon.ser@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox