xref: /aosp_15_r20/external/igt-gpu-tools/lib/igt_eld.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1*d83cc019SAndroid Build Coastguard Worker /*
2*d83cc019SAndroid Build Coastguard Worker  * Copyright © 2019 Intel Corporation
3*d83cc019SAndroid Build Coastguard Worker  *
4*d83cc019SAndroid Build Coastguard Worker  * Permission is hereby granted, free of charge, to any person obtaining a
5*d83cc019SAndroid Build Coastguard Worker  * copy of this software and associated documentation files (the "Software"),
6*d83cc019SAndroid Build Coastguard Worker  * to deal in the Software without restriction, including without limitation
7*d83cc019SAndroid Build Coastguard Worker  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*d83cc019SAndroid Build Coastguard Worker  * and/or sell copies of the Software, and to permit persons to whom the
9*d83cc019SAndroid Build Coastguard Worker  * Software is furnished to do so, subject to the following conditions:
10*d83cc019SAndroid Build Coastguard Worker  *
11*d83cc019SAndroid Build Coastguard Worker  * The above copyright notice and this permission notice (including the next
12*d83cc019SAndroid Build Coastguard Worker  * paragraph) shall be included in all copies or substantial portions of the
13*d83cc019SAndroid Build Coastguard Worker  * Software.
14*d83cc019SAndroid Build Coastguard Worker  *
15*d83cc019SAndroid Build Coastguard Worker  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*d83cc019SAndroid Build Coastguard Worker  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*d83cc019SAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18*d83cc019SAndroid Build Coastguard Worker  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*d83cc019SAndroid Build Coastguard Worker  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*d83cc019SAndroid Build Coastguard Worker  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21*d83cc019SAndroid Build Coastguard Worker  * IN THE SOFTWARE.
22*d83cc019SAndroid Build Coastguard Worker  *
23*d83cc019SAndroid Build Coastguard Worker  * Authors: Simon Ser <[email protected]>
24*d83cc019SAndroid Build Coastguard Worker  */
25*d83cc019SAndroid Build Coastguard Worker 
26*d83cc019SAndroid Build Coastguard Worker #include "config.h"
27*d83cc019SAndroid Build Coastguard Worker 
28*d83cc019SAndroid Build Coastguard Worker #include <dirent.h>
29*d83cc019SAndroid Build Coastguard Worker #include <stdint.h>
30*d83cc019SAndroid Build Coastguard Worker #include <stdio.h>
31*d83cc019SAndroid Build Coastguard Worker #include <string.h>
32*d83cc019SAndroid Build Coastguard Worker 
33*d83cc019SAndroid Build Coastguard Worker #include "igt_core.h"
34*d83cc019SAndroid Build Coastguard Worker #include "igt_eld.h"
35*d83cc019SAndroid Build Coastguard Worker 
36*d83cc019SAndroid Build Coastguard Worker #define ELD_PREFIX "eld#"
37*d83cc019SAndroid Build Coastguard Worker #define ELD_DELIM " \t"
38*d83cc019SAndroid Build Coastguard Worker 
39*d83cc019SAndroid Build Coastguard Worker /**
40*d83cc019SAndroid Build Coastguard Worker  * EDID-Like Data (ELD) is metadata parsed and exposed by ALSA for HDMI and
41*d83cc019SAndroid Build Coastguard Worker  * DisplayPort connectors supporting audio. This includes the monitor name and
42*d83cc019SAndroid Build Coastguard Worker  * the supported audio parameters (formats, sampling rates, sample sizes and so
43*d83cc019SAndroid Build Coastguard Worker  * on).
44*d83cc019SAndroid Build Coastguard Worker  *
45*d83cc019SAndroid Build Coastguard Worker  * Audio parameters come from Short Audio Descriptors (SAD) blocks in the
46*d83cc019SAndroid Build Coastguard Worker  * EDID. Enumerations from igt_edid are used since they are the same.
47*d83cc019SAndroid Build Coastguard Worker  */
48*d83cc019SAndroid Build Coastguard Worker 
parse_sad_coding_type(const char * value)49*d83cc019SAndroid Build Coastguard Worker static enum cea_sad_format parse_sad_coding_type(const char *value)
50*d83cc019SAndroid Build Coastguard Worker {
51*d83cc019SAndroid Build Coastguard Worker 	if (strcmp(value, "LPCM") == 0)
52*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_FORMAT_PCM;
53*d83cc019SAndroid Build Coastguard Worker 	else
54*d83cc019SAndroid Build Coastguard Worker 		return 0;
55*d83cc019SAndroid Build Coastguard Worker }
56*d83cc019SAndroid Build Coastguard Worker 
parse_sad_rate(const char * value)57*d83cc019SAndroid Build Coastguard Worker static enum cea_sad_sampling_rate parse_sad_rate(const char *value)
58*d83cc019SAndroid Build Coastguard Worker {
59*d83cc019SAndroid Build Coastguard Worker 	switch (atoi(value)) {
60*d83cc019SAndroid Build Coastguard Worker 	case 32000:
61*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_32KHZ;
62*d83cc019SAndroid Build Coastguard Worker 	case 44100:
63*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_44KHZ;
64*d83cc019SAndroid Build Coastguard Worker 	case 48000:
65*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_48KHZ;
66*d83cc019SAndroid Build Coastguard Worker 	case 88000:
67*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_88KHZ;
68*d83cc019SAndroid Build Coastguard Worker 	case 96000:
69*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_96KHZ;
70*d83cc019SAndroid Build Coastguard Worker 	case 176000:
71*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_176KHZ;
72*d83cc019SAndroid Build Coastguard Worker 	case 192000:
73*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLING_RATE_192KHZ;
74*d83cc019SAndroid Build Coastguard Worker 	default:
75*d83cc019SAndroid Build Coastguard Worker 		return 0;
76*d83cc019SAndroid Build Coastguard Worker 	}
77*d83cc019SAndroid Build Coastguard Worker }
78*d83cc019SAndroid Build Coastguard Worker 
parse_sad_bit(const char * value)79*d83cc019SAndroid Build Coastguard Worker static enum cea_sad_pcm_sample_size parse_sad_bit(const char *value)
80*d83cc019SAndroid Build Coastguard Worker {
81*d83cc019SAndroid Build Coastguard Worker 	switch (atoi(value)) {
82*d83cc019SAndroid Build Coastguard Worker 	case 16:
83*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLE_SIZE_16;
84*d83cc019SAndroid Build Coastguard Worker 	case 20:
85*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLE_SIZE_20;
86*d83cc019SAndroid Build Coastguard Worker 	case 24:
87*d83cc019SAndroid Build Coastguard Worker 		return CEA_SAD_SAMPLE_SIZE_24;
88*d83cc019SAndroid Build Coastguard Worker 	default:
89*d83cc019SAndroid Build Coastguard Worker 		return 0;
90*d83cc019SAndroid Build Coastguard Worker 	}
91*d83cc019SAndroid Build Coastguard Worker }
92*d83cc019SAndroid Build Coastguard Worker 
parse_sad_field(struct eld_sad * sad,const char * key,char * value)93*d83cc019SAndroid Build Coastguard Worker static void parse_sad_field(struct eld_sad *sad, const char *key, char *value)
94*d83cc019SAndroid Build Coastguard Worker {
95*d83cc019SAndroid Build Coastguard Worker 	char *tok;
96*d83cc019SAndroid Build Coastguard Worker 
97*d83cc019SAndroid Build Coastguard Worker 	/* Some fields are prefixed with the raw hex value, strip it */
98*d83cc019SAndroid Build Coastguard Worker 	if (value[0] == '[') {
99*d83cc019SAndroid Build Coastguard Worker 		value = strchr(value, ' ');
100*d83cc019SAndroid Build Coastguard Worker 		igt_assert(value != NULL);
101*d83cc019SAndroid Build Coastguard Worker 		value++; /* skip the space */
102*d83cc019SAndroid Build Coastguard Worker 	}
103*d83cc019SAndroid Build Coastguard Worker 
104*d83cc019SAndroid Build Coastguard Worker 	/* Single-value fields */
105*d83cc019SAndroid Build Coastguard Worker 	if (strcmp(key, "coding_type") == 0)
106*d83cc019SAndroid Build Coastguard Worker 		sad->coding_type = parse_sad_coding_type(value);
107*d83cc019SAndroid Build Coastguard Worker 	else if (strcmp(key, "channels") == 0)
108*d83cc019SAndroid Build Coastguard Worker 		sad->channels = atoi(value);
109*d83cc019SAndroid Build Coastguard Worker 
110*d83cc019SAndroid Build Coastguard Worker 	/* Multiple-value fields */
111*d83cc019SAndroid Build Coastguard Worker 	tok = strtok(value, " ");
112*d83cc019SAndroid Build Coastguard Worker 	while (tok) {
113*d83cc019SAndroid Build Coastguard Worker 		if (strcmp(key, "rates") == 0)
114*d83cc019SAndroid Build Coastguard Worker 			sad->rates |= parse_sad_rate(tok);
115*d83cc019SAndroid Build Coastguard Worker 		else if (strcmp(key, "bits") == 0)
116*d83cc019SAndroid Build Coastguard Worker 			sad->bits |= parse_sad_bit(tok);
117*d83cc019SAndroid Build Coastguard Worker 
118*d83cc019SAndroid Build Coastguard Worker 		tok = strtok(NULL, " ");
119*d83cc019SAndroid Build Coastguard Worker 	}
120*d83cc019SAndroid Build Coastguard Worker }
121*d83cc019SAndroid Build Coastguard Worker 
122*d83cc019SAndroid Build Coastguard Worker /** eld_parse_entry: parse an ELD entry
123*d83cc019SAndroid Build Coastguard Worker  *
124*d83cc019SAndroid Build Coastguard Worker  * Here is an example of an ELD entry:
125*d83cc019SAndroid Build Coastguard Worker  *
126*d83cc019SAndroid Build Coastguard Worker  *     $ cat /proc/asound/card0/eld#0.2
127*d83cc019SAndroid Build Coastguard Worker  *     monitor_present         1
128*d83cc019SAndroid Build Coastguard Worker  *     eld_valid               1
129*d83cc019SAndroid Build Coastguard Worker  *     monitor_name            U2879G6
130*d83cc019SAndroid Build Coastguard Worker  *     connection_type         DisplayPort
131*d83cc019SAndroid Build Coastguard Worker  *     eld_version             [0x2] CEA-861D or below
132*d83cc019SAndroid Build Coastguard Worker  *     edid_version            [0x3] CEA-861-B, C or D
133*d83cc019SAndroid Build Coastguard Worker  *     manufacture_id          0xe305
134*d83cc019SAndroid Build Coastguard Worker  *     product_id              0x2879
135*d83cc019SAndroid Build Coastguard Worker  *     port_id                 0x800
136*d83cc019SAndroid Build Coastguard Worker  *     support_hdcp            0
137*d83cc019SAndroid Build Coastguard Worker  *     support_ai              0
138*d83cc019SAndroid Build Coastguard Worker  *     audio_sync_delay        0
139*d83cc019SAndroid Build Coastguard Worker  *     speakers                [0x1] FL/FR
140*d83cc019SAndroid Build Coastguard Worker  *     sad_count               1
141*d83cc019SAndroid Build Coastguard Worker  *     sad0_coding_type        [0x1] LPCM
142*d83cc019SAndroid Build Coastguard Worker  *     sad0_channels           2
143*d83cc019SAndroid Build Coastguard Worker  *     sad0_rates              [0xe0] 32000 44100 48000
144*d83cc019SAndroid Build Coastguard Worker  *     sad0_bits               [0xe0000] 16 20 24
145*d83cc019SAndroid Build Coastguard Worker  *
146*d83cc019SAndroid Build Coastguard Worker  * Each entry contains one or more SAD blocks. Their contents is exposed in
147*d83cc019SAndroid Build Coastguard Worker  * sadN_* fields.
148*d83cc019SAndroid Build Coastguard Worker  */
eld_parse_entry(const char * path,struct eld_entry * eld)149*d83cc019SAndroid Build Coastguard Worker static bool eld_parse_entry(const char *path, struct eld_entry *eld)
150*d83cc019SAndroid Build Coastguard Worker {
151*d83cc019SAndroid Build Coastguard Worker 	FILE *f;
152*d83cc019SAndroid Build Coastguard Worker 	char buf[1024];
153*d83cc019SAndroid Build Coastguard Worker 	char *key, *value, *sad_key;
154*d83cc019SAndroid Build Coastguard Worker 	size_t len;
155*d83cc019SAndroid Build Coastguard Worker 	bool monitor_present = false;
156*d83cc019SAndroid Build Coastguard Worker 	int sad_index;
157*d83cc019SAndroid Build Coastguard Worker 
158*d83cc019SAndroid Build Coastguard Worker 	memset(eld, 0, sizeof(*eld));
159*d83cc019SAndroid Build Coastguard Worker 
160*d83cc019SAndroid Build Coastguard Worker 	f = fopen(path, "r");
161*d83cc019SAndroid Build Coastguard Worker 	if (!f) {
162*d83cc019SAndroid Build Coastguard Worker 		igt_debug("Failed to open ELD file: %s\n", path);
163*d83cc019SAndroid Build Coastguard Worker 		return false;
164*d83cc019SAndroid Build Coastguard Worker 	}
165*d83cc019SAndroid Build Coastguard Worker 
166*d83cc019SAndroid Build Coastguard Worker 	while ((fgets(buf, sizeof(buf), f)) != NULL) {
167*d83cc019SAndroid Build Coastguard Worker 		len = strlen(buf);
168*d83cc019SAndroid Build Coastguard Worker 		if (buf[len - 1] == '\n')
169*d83cc019SAndroid Build Coastguard Worker 			buf[len - 1] = '\0';
170*d83cc019SAndroid Build Coastguard Worker 
171*d83cc019SAndroid Build Coastguard Worker 		key = strtok(buf, ELD_DELIM);
172*d83cc019SAndroid Build Coastguard Worker 		value = strtok(NULL, "");
173*d83cc019SAndroid Build Coastguard Worker 		/* Skip whitespace at the beginning */
174*d83cc019SAndroid Build Coastguard Worker 		value += strspn(value, ELD_DELIM);
175*d83cc019SAndroid Build Coastguard Worker 
176*d83cc019SAndroid Build Coastguard Worker 		if (strcmp(key, "monitor_present") == 0)
177*d83cc019SAndroid Build Coastguard Worker 			monitor_present = strcmp(value, "1") == 0;
178*d83cc019SAndroid Build Coastguard Worker 		else if (strcmp(key, "eld_valid") == 0)
179*d83cc019SAndroid Build Coastguard Worker 			eld->valid = strcmp(value, "1") == 0;
180*d83cc019SAndroid Build Coastguard Worker 		else if (strcmp(key, "monitor_name") == 0)
181*d83cc019SAndroid Build Coastguard Worker 			snprintf(eld->monitor_name, sizeof(eld->monitor_name),
182*d83cc019SAndroid Build Coastguard Worker 				 "%s", value);
183*d83cc019SAndroid Build Coastguard Worker 		else if (strcmp(key, "sad_count") == 0)
184*d83cc019SAndroid Build Coastguard Worker 			eld->sads_len = atoi(value);
185*d83cc019SAndroid Build Coastguard Worker 		else if (sscanf(key, "sad%d_%ms", &sad_index, &sad_key) == 2) {
186*d83cc019SAndroid Build Coastguard Worker 			igt_assert(sad_index < ELD_SADS_CAP);
187*d83cc019SAndroid Build Coastguard Worker 			igt_assert(sad_index < eld->sads_len);
188*d83cc019SAndroid Build Coastguard Worker 			parse_sad_field(&eld->sads[sad_index], sad_key, value);
189*d83cc019SAndroid Build Coastguard Worker 			free(sad_key);
190*d83cc019SAndroid Build Coastguard Worker 		}
191*d83cc019SAndroid Build Coastguard Worker 	}
192*d83cc019SAndroid Build Coastguard Worker 
193*d83cc019SAndroid Build Coastguard Worker 	if (ferror(f) != 0) {
194*d83cc019SAndroid Build Coastguard Worker 		igt_debug("Failed to read ELD file %s: %d\n", path, ferror(f));
195*d83cc019SAndroid Build Coastguard Worker 		return false;
196*d83cc019SAndroid Build Coastguard Worker 	}
197*d83cc019SAndroid Build Coastguard Worker 
198*d83cc019SAndroid Build Coastguard Worker 	fclose(f);
199*d83cc019SAndroid Build Coastguard Worker 
200*d83cc019SAndroid Build Coastguard Worker 	if (!monitor_present)
201*d83cc019SAndroid Build Coastguard Worker 		igt_debug("Monitor not present in ELD: %s\n", path);
202*d83cc019SAndroid Build Coastguard Worker 	return monitor_present;
203*d83cc019SAndroid Build Coastguard Worker }
204*d83cc019SAndroid Build Coastguard Worker 
205*d83cc019SAndroid Build Coastguard Worker /** eld_get_igt: retrieve the ALSA ELD entry matching the IGT EDID */
eld_get_igt(struct eld_entry * eld)206*d83cc019SAndroid Build Coastguard Worker bool eld_get_igt(struct eld_entry *eld)
207*d83cc019SAndroid Build Coastguard Worker {
208*d83cc019SAndroid Build Coastguard Worker 	DIR *dir;
209*d83cc019SAndroid Build Coastguard Worker 	struct dirent *dirent;
210*d83cc019SAndroid Build Coastguard Worker 	int i, n_elds;
211*d83cc019SAndroid Build Coastguard Worker 	char card[64];
212*d83cc019SAndroid Build Coastguard Worker 	char path[PATH_MAX];
213*d83cc019SAndroid Build Coastguard Worker 
214*d83cc019SAndroid Build Coastguard Worker 	n_elds = 0;
215*d83cc019SAndroid Build Coastguard Worker 	for (i = 0; i < 8; i++) {
216*d83cc019SAndroid Build Coastguard Worker 		snprintf(card, sizeof(card), "/proc/asound/card%d", i);
217*d83cc019SAndroid Build Coastguard Worker 		dir = opendir(card);
218*d83cc019SAndroid Build Coastguard Worker 		if (!dir)
219*d83cc019SAndroid Build Coastguard Worker 			continue;
220*d83cc019SAndroid Build Coastguard Worker 
221*d83cc019SAndroid Build Coastguard Worker 		while ((dirent = readdir(dir))) {
222*d83cc019SAndroid Build Coastguard Worker 			if (strncmp(dirent->d_name, ELD_PREFIX,
223*d83cc019SAndroid Build Coastguard Worker 				    strlen(ELD_PREFIX)) != 0)
224*d83cc019SAndroid Build Coastguard Worker 				continue;
225*d83cc019SAndroid Build Coastguard Worker 
226*d83cc019SAndroid Build Coastguard Worker 			n_elds++;
227*d83cc019SAndroid Build Coastguard Worker 
228*d83cc019SAndroid Build Coastguard Worker 			snprintf(path, sizeof(path), "%s/%s", card,
229*d83cc019SAndroid Build Coastguard Worker 				 dirent->d_name);
230*d83cc019SAndroid Build Coastguard Worker 			if (!eld_parse_entry(path, eld)) {
231*d83cc019SAndroid Build Coastguard Worker 				continue;
232*d83cc019SAndroid Build Coastguard Worker 			}
233*d83cc019SAndroid Build Coastguard Worker 
234*d83cc019SAndroid Build Coastguard Worker 			if (!eld->valid) {
235*d83cc019SAndroid Build Coastguard Worker 				igt_debug("Skipping invalid ELD: %s\n", path);
236*d83cc019SAndroid Build Coastguard Worker 				continue;
237*d83cc019SAndroid Build Coastguard Worker 			}
238*d83cc019SAndroid Build Coastguard Worker 
239*d83cc019SAndroid Build Coastguard Worker 			if (strcmp(eld->monitor_name, "IGT") != 0) {
240*d83cc019SAndroid Build Coastguard Worker 				igt_debug("Skipping non-IGT ELD: %s "
241*d83cc019SAndroid Build Coastguard Worker 					  "(monitor name: %s)\n",
242*d83cc019SAndroid Build Coastguard Worker 					  path, eld->monitor_name);
243*d83cc019SAndroid Build Coastguard Worker 				continue;
244*d83cc019SAndroid Build Coastguard Worker 			}
245*d83cc019SAndroid Build Coastguard Worker 
246*d83cc019SAndroid Build Coastguard Worker 			closedir(dir);
247*d83cc019SAndroid Build Coastguard Worker 			return true;
248*d83cc019SAndroid Build Coastguard Worker 		}
249*d83cc019SAndroid Build Coastguard Worker 		closedir(dir);
250*d83cc019SAndroid Build Coastguard Worker 	}
251*d83cc019SAndroid Build Coastguard Worker 
252*d83cc019SAndroid Build Coastguard Worker 	if (n_elds == 0)
253*d83cc019SAndroid Build Coastguard Worker 		igt_debug("Found zero ELDs\n");
254*d83cc019SAndroid Build Coastguard Worker 
255*d83cc019SAndroid Build Coastguard Worker 	return false;
256*d83cc019SAndroid Build Coastguard Worker }
257*d83cc019SAndroid Build Coastguard Worker 
258*d83cc019SAndroid Build Coastguard Worker /** eld_has_igt: check whether ALSA has detected the audio-capable IGT EDID by
259*d83cc019SAndroid Build Coastguard Worker  * parsing ELD entries */
eld_has_igt(void)260*d83cc019SAndroid Build Coastguard Worker bool eld_has_igt(void)
261*d83cc019SAndroid Build Coastguard Worker {
262*d83cc019SAndroid Build Coastguard Worker 	struct eld_entry eld;
263*d83cc019SAndroid Build Coastguard Worker 	return eld_get_igt(&eld);
264*d83cc019SAndroid Build Coastguard Worker }
265