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