[igt-dev] [PATCH i-g-t 3/7] tests/kms_chamelium: generate an EDID with audio support

Simon Ser
2019-04-26T16:21:17Z

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 is suitable for HDMI audio. For DisplayPort audio, we keep using
the Chamelium's default EDID.

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

Signed-off-by: Simon Ser <simon.ser@intel.com>
---
lib/igt_chamelium.c | 9 +++-
tests/kms_chamelium.c | 123 ++++++++++++++++++++++++++++++------------
2 files changed, 95 insertions(+), 37 deletions(-)

diff --git a/lib/igt_chamelium.c b/lib/igt_chamelium.c
index ffc68f35..44445444 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"
@@ -499,14 +500,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 714e5e06..126af226 100644
--- a/tests/kms_chamelium.c
+++ b/tests/kms_chamelium.c
@@ -27,6 +27,7 @@
#include "config.h"
#include "igt.h"
#include "igt_vc4.h"
+#include "igt_edid.h"

#include <fcntl.h>
#include <pthread.h>
@@ -414,8 +415,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, int edid_id)
{
igt_display_t *display = &data->display;
igt_output_t *output;
@@ -428,10 +428,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);
+ chamelium_port_set_edid(data->chamelium, port, edid_id);

chamelium_plug(data->chamelium, port);
wait_for_connector(data, port, DRM_MODE_CONNECTED);
@@ -616,7 +616,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, data->edid_id);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -647,7 +647,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, data->edid_id);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -682,7 +682,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, data->edid_id);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -725,7 +725,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,
@@ -735,7 +735,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,
@@ -986,7 +986,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, int edid_id)
{
bool run = false;
struct alsa *alsa;
@@ -1005,10 +1005,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_id);
connector = chamelium_port_get_connector(data->chamelium, port, false);
primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
igt_assert(primary);
@@ -1027,14 +1024,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);
}
@@ -1377,7 +1374,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, data->edid_id);

mode = igt_output_get_mode(output);

@@ -1557,7 +1554,17 @@ static data_t data;
igt_main
{
struct chamelium_port *port;
- int edid_id, alt_edid_id, p;
+ int p, channels;
+ uint8_t sampling_rates, sample_sizes;
+ char raw_edid[sizeof(struct edid) + sizeof(struct edid_ext)] = {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;

igt_fixture {
igt_skip_on_simulation();
@@ -1569,12 +1576,52 @@ 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,
+ /* 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);
+
+ data.edid_id = chamelium_new_edid(data.chamelium,
+ (unsigned char *) raw_edid);
+
+ data.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;

/* So fbcon doesn't try to reprobe things itself */
kmstest_set_vt_graphics_mode();
@@ -1598,9 +1645,9 @@ igt_main
HPD_TOGGLE_COUNT_FAST);

connector_subtest("dp-edid-read", DisplayPort) {
- test_edid_read(&data, port, edid_id,
+ test_edid_read(&data, port, data.edid_id,
igt_kms_get_base_edid());
- test_edid_read(&data, port, alt_edid_id,
+ test_edid_read(&data, port, data.alt_edid_id,
igt_kms_get_alt_edid());
}

@@ -1626,13 +1673,15 @@ igt_main
test_suspend_resume_edid_change(&data, port,
SUSPEND_STATE_MEM,
SUSPEND_TEST_NONE,
- edid_id, alt_edid_id);
+ data.edid_id,
+ data.alt_edid_id);

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);
+ data.edid_id,
+ data.alt_edid_id);

connector_subtest("dp-crc-single", DisplayPort)
test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888,
@@ -1649,8 +1698,10 @@ 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", 0);
}

igt_subtest_group {
@@ -1668,9 +1719,9 @@ igt_main
HPD_TOGGLE_COUNT_FAST);

connector_subtest("hdmi-edid-read", HDMIA) {
- test_edid_read(&data, port, edid_id,
+ test_edid_read(&data, port, data.edid_id,
igt_kms_get_base_edid());
- test_edid_read(&data, port, alt_edid_id,
+ test_edid_read(&data, port, data.alt_edid_id,
igt_kms_get_alt_edid());
}

@@ -1696,13 +1747,15 @@ igt_main
test_suspend_resume_edid_change(&data, port,
SUSPEND_STATE_MEM,
SUSPEND_TEST_NONE,
- edid_id, alt_edid_id);
+ data.edid_id,
+ data.alt_edid_id);

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);
+ data.edid_id,
+ data.alt_edid_id);

connector_subtest("hdmi-crc-single", HDMIA)
test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888,
@@ -1813,9 +1866,9 @@ igt_main
test_basic_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST);

connector_subtest("vga-edid-read", VGA) {
- test_edid_read(&data, port, edid_id,
+ test_edid_read(&data, port, data.edid_id,
igt_kms_get_base_edid());
- test_edid_read(&data, port, alt_edid_id,
+ test_edid_read(&data, port, data.alt_edid_id,
igt_kms_get_alt_edid());