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