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 <assert.h>
29 #include <string.h>
30 #include <stdint.h>
31 #include <time.h>
32 #include <xf86drmMode.h>
33
34 #include "igt_core.h"
35 #include "igt_edid.h"
36
37 /**
38 * SECTION:igt_edid
39 * @short_description: EDID generation library
40 * @title: EDID
41 * @include: igt_edid.h
42 *
43 * This library contains helpers to generate custom EDIDs.
44
45 * The E-EDID specification is available at:
46 * https://glenwing.github.io/docs/VESA-EEDID-A2.pdf
47 *
48 * The EDID CEA extension is defined in CEA-861-D section 7. The HDMI VSDB is
49 * defined in the HDMI spec.
50 */
51
52 static const char edid_header[] = {
53 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
54 };
55
56 static const char monitor_range_padding[] = {
57 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
58 };
59
60 const uint8_t hdmi_ieee_oui[3] = {0x03, 0x0C, 0x00};
61
62 /* vfreq is in Hz */
std_timing_set(struct std_timing * st,int hsize,int vfreq,enum std_timing_aspect aspect)63 static void std_timing_set(struct std_timing *st, int hsize, int vfreq,
64 enum std_timing_aspect aspect)
65 {
66 assert(hsize >= 256 && hsize <= 2288);
67 st->hsize = hsize / 8 - 31;
68 st->vfreq_aspect = aspect << 6 | (vfreq - 60);
69 }
70
std_timing_unset(struct std_timing * st)71 static void std_timing_unset(struct std_timing *st)
72 {
73 memset(st, 0x01, sizeof(struct std_timing));
74 }
75
76 /**
77 * detailed_timing_set_mode: fill a detailed timing based on a mode
78 */
detailed_timing_set_mode(struct detailed_timing * dt,drmModeModeInfo * mode,int width_mm,int height_mm)79 void detailed_timing_set_mode(struct detailed_timing *dt, drmModeModeInfo *mode,
80 int width_mm, int height_mm)
81 {
82 int hactive, hblank, vactive, vblank, hsync_offset, hsync_pulse_width,
83 vsync_offset, vsync_pulse_width;
84 struct detailed_pixel_timing *pt = &dt->data.pixel_data;
85
86 hactive = mode->hdisplay;
87 hsync_offset = mode->hsync_start - mode->hdisplay;
88 hsync_pulse_width = mode->hsync_end - mode->hsync_start;
89 hblank = mode->htotal - mode->hdisplay;
90
91 vactive = mode->vdisplay;
92 vsync_offset = mode->vsync_start - mode->vdisplay;
93 vsync_pulse_width = mode->vsync_end - mode->vsync_start;
94 vblank = mode->vtotal - mode->vdisplay;
95
96 dt->pixel_clock[0] = (mode->clock / 10) & 0x00FF;
97 dt->pixel_clock[1] = ((mode->clock / 10) & 0xFF00) >> 8;
98
99 assert(hactive <= 0xFFF);
100 assert(hblank <= 0xFFF);
101 pt->hactive_lo = hactive & 0x0FF;
102 pt->hblank_lo = hblank & 0x0FF;
103 pt->hactive_hblank_hi = (hactive & 0xF00) >> 4 | (hblank & 0xF00) >> 8;
104
105 assert(vactive <= 0xFFF);
106 assert(vblank <= 0xFFF);
107 pt->vactive_lo = vactive & 0x0FF;
108 pt->vblank_lo = vblank & 0x0FF;
109 pt->vactive_vblank_hi = (vactive & 0xF00) >> 4 | (vblank & 0xF00) >> 8;
110
111 assert(hsync_offset <= 0x3FF);
112 assert(hsync_pulse_width <= 0x3FF);
113 assert(vsync_offset <= 0x3F);
114 assert(vsync_pulse_width <= 0x3F);
115 pt->hsync_offset_lo = hsync_offset & 0x0FF;
116 pt->hsync_pulse_width_lo = hsync_pulse_width & 0x0FF;
117 pt->vsync_offset_pulse_width_lo = (vsync_offset & 0xF) << 4
118 | (vsync_pulse_width & 0xF);
119 pt->hsync_vsync_offset_pulse_width_hi =
120 ((hsync_offset & 0x300) >> 2) | ((hsync_pulse_width & 0x300) >> 4)
121 | ((vsync_offset & 0x30) >> 2) | ((vsync_pulse_width & 0x30) >> 4);
122
123 assert(width_mm <= 0xFFF);
124 assert(height_mm <= 0xFFF);
125 pt->width_mm_lo = width_mm & 0x0FF;
126 pt->height_mm_lo = height_mm & 0x0FF;
127 pt->width_height_mm_hi = (width_mm & 0xF00) >> 4
128 | (height_mm & 0xF00) >> 8;
129
130 pt->misc = EDID_PT_SYNC_DIGITAL_SEPARATE;
131 if (mode->flags & DRM_MODE_FLAG_PHSYNC)
132 pt->misc |= EDID_PT_HSYNC_POSITIVE;
133 if (mode->flags & DRM_MODE_FLAG_PVSYNC)
134 pt->misc |= EDID_PT_VSYNC_POSITIVE;
135 }
136
137 /**
138 * detailed_timing_set_monitor_range_mode: set a detailed timing to be a
139 * monitor range based on a mode
140 */
detailed_timing_set_monitor_range_mode(struct detailed_timing * dt,drmModeModeInfo * mode)141 void detailed_timing_set_monitor_range_mode(struct detailed_timing *dt,
142 drmModeModeInfo *mode)
143 {
144 struct detailed_non_pixel *np = &dt->data.other_data;
145 struct detailed_data_monitor_range *mr = &np->data.range;
146
147 dt->pixel_clock[0] = dt->pixel_clock[1] = 0;
148
149 np->type = EDID_DETAIL_MONITOR_RANGE;
150
151 mr->min_vfreq = mode->vrefresh - 1;
152 mr->max_vfreq = mode->vrefresh + 1;
153 mr->min_hfreq_khz = (mode->clock / mode->htotal) - 1;
154 mr->max_hfreq_khz = (mode->clock / mode->htotal) + 1;
155 mr->pixel_clock_mhz = (mode->clock / 10000) + 1;
156 mr->flags = 0;
157
158 memcpy(mr->formula.pad, monitor_range_padding,
159 sizeof(monitor_range_padding));
160 }
161
162 /**
163 * detailed_timing_set_string: set a detailed timing to be a string
164 */
detailed_timing_set_string(struct detailed_timing * dt,enum detailed_non_pixel_type type,const char * str)165 void detailed_timing_set_string(struct detailed_timing *dt,
166 enum detailed_non_pixel_type type,
167 const char *str)
168 {
169 struct detailed_non_pixel *np = &dt->data.other_data;
170 struct detailed_data_string *ds = &np->data.string;
171 size_t len;
172
173 switch (type) {
174 case EDID_DETAIL_MONITOR_NAME:
175 case EDID_DETAIL_MONITOR_STRING:
176 case EDID_DETAIL_MONITOR_SERIAL:
177 break;
178 default:
179 assert(0); /* not a string type */
180 }
181
182 dt->pixel_clock[0] = dt->pixel_clock[1] = 0;
183
184 np->type = type;
185
186 strncpy(ds->str, str, sizeof(ds->str));
187 len = strlen(str);
188 if (len < sizeof(ds->str))
189 ds->str[len] = '\n';
190 }
191
192 /**
193 * edid_get_mfg: reads the 3-letter manufacturer identifier
194 *
195 * The string is *not* NULL-terminated.
196 */
edid_get_mfg(const struct edid * edid,char out[static3])197 void edid_get_mfg(const struct edid *edid, char out[static 3])
198 {
199 out[0] = ((edid->mfg_id[0] & 0x7C) >> 2) + '@';
200 out[1] = (((edid->mfg_id[0] & 0x03) << 3) |
201 ((edid->mfg_id[1] & 0xE0) >> 5)) + '@';
202 out[2] = (edid->mfg_id[1] & 0x1F) + '@';
203 }
204
edid_set_mfg(struct edid * edid,const char mfg[static3])205 static void edid_set_mfg(struct edid *edid, const char mfg[static 3])
206 {
207 edid->mfg_id[0] = (mfg[0] - '@') << 2 | (mfg[1] - '@') >> 3;
208 edid->mfg_id[1] = (mfg[1] - '@') << 5 | (mfg[2] - '@');
209 }
210
edid_set_gamma(struct edid * edid,float gamma)211 static void edid_set_gamma(struct edid *edid, float gamma)
212 {
213 edid->gamma = (gamma * 100) - 100;
214 }
215
216 /**
217 * edid_init: initialize an EDID
218 *
219 * The EDID will be pre-filled with established and standard timings:
220 *
221 * - 1920x1080 60Hz
222 * - 1280x720 60Hz
223 * - 1024x768 60Hz
224 * - 800x600 60Hz
225 * - 640x480 60Hz
226 */
edid_init(struct edid * edid)227 void edid_init(struct edid *edid)
228 {
229 size_t i;
230 time_t t;
231 struct tm *tm;
232
233 memset(edid, 0, sizeof(struct edid));
234
235 memcpy(edid->header, edid_header, sizeof(edid_header));
236 edid_set_mfg(edid, "IGT");
237 edid->version = 1;
238 edid->revision = 3;
239 edid->input = 0x80;
240 edid->width_cm = 52;
241 edid->height_cm = 30;
242 edid_set_gamma(edid, 2.20);
243 edid->features = 0x02;
244
245 /* Year of manufacture */
246 t = time(NULL);
247 tm = localtime(&t);
248 edid->mfg_year = tm->tm_year - 90;
249
250 /* Established timings: 640x480 60Hz, 800x600 60Hz, 1024x768 60Hz */
251 edid->established_timings.t1 = 0x21;
252 edid->established_timings.t2 = 0x08;
253
254 /* Standard timings */
255 /* 1920x1080 60Hz */
256 std_timing_set(&edid->standard_timings[0], 1920, 60, STD_TIMING_16_9);
257 /* 1280x720 60Hz */
258 std_timing_set(&edid->standard_timings[1], 1280, 60, STD_TIMING_16_9);
259 /* 1024x768 60Hz */
260 std_timing_set(&edid->standard_timings[2], 1024, 60, STD_TIMING_4_3);
261 /* 800x600 60Hz */
262 std_timing_set(&edid->standard_timings[3], 800, 60, STD_TIMING_4_3);
263 /* 640x480 60Hz */
264 std_timing_set(&edid->standard_timings[4], 640, 60, STD_TIMING_4_3);
265 for (i = 5; i < STD_TIMINGS_LEN; i++)
266 std_timing_unset(&edid->standard_timings[i]);
267 }
268
269 /**
270 * edid_init_with_mode: initialize an EDID and sets its preferred mode
271 */
edid_init_with_mode(struct edid * edid,drmModeModeInfo * mode)272 void edid_init_with_mode(struct edid *edid, drmModeModeInfo *mode)
273 {
274 edid_init(edid);
275
276 /* Preferred timing */
277 detailed_timing_set_mode(&edid->detailed_timings[0], mode,
278 edid->width_cm * 10, edid->height_cm * 10);
279 detailed_timing_set_monitor_range_mode(&edid->detailed_timings[1],
280 mode);
281 detailed_timing_set_string(&edid->detailed_timings[2],
282 EDID_DETAIL_MONITOR_NAME, "IGT");
283 }
284
compute_checksum(const uint8_t * buf,size_t size)285 static uint8_t compute_checksum(const uint8_t *buf, size_t size)
286 {
287 size_t i;
288 uint8_t sum = 0;
289
290 assert(size > 0);
291 for (i = 0; i < size - 1; i++) {
292 sum += buf[i];
293 }
294
295 return 256 - sum;
296 }
297
298 /**
299 * edid_update_checksum: compute and update the checksums of the main EDID
300 * block and all extension blocks
301 */
edid_update_checksum(struct edid * edid)302 void edid_update_checksum(struct edid *edid)
303 {
304 size_t i;
305 struct edid_ext *ext;
306
307 edid->checksum = compute_checksum((uint8_t *) edid,
308 sizeof(struct edid));
309
310 for (i = 0; i < edid->extensions_len; i++) {
311 ext = &edid->extensions[i];
312 if (ext->tag == EDID_EXT_CEA)
313 ext->data.cea.checksum =
314 compute_checksum((uint8_t *) ext,
315 sizeof(struct edid_ext));
316 }
317 }
318
319 /**
320 * edid_get_size: return the size of the EDID block in bytes including EDID
321 * extensions, if any.
322 */
edid_get_size(const struct edid * edid)323 size_t edid_get_size(const struct edid *edid)
324 {
325 return sizeof(struct edid) +
326 edid->extensions_len * sizeof(struct edid_ext);
327 }
328
329 /**
330 * cea_sad_init_pcm:
331 * @channels: the number of supported channels (max. 8)
332 * @sampling_rates: bitfield of enum cea_sad_sampling_rate
333 * @sample_sizes: bitfield of enum cea_sad_pcm_sample_size
334 *
335 * Initialize a Short Audio Descriptor to advertise PCM support.
336 */
cea_sad_init_pcm(struct cea_sad * sad,int channels,uint8_t sampling_rates,uint8_t sample_sizes)337 void cea_sad_init_pcm(struct cea_sad *sad, int channels,
338 uint8_t sampling_rates, uint8_t sample_sizes)
339 {
340 assert(channels <= 8);
341 sad->format_channels = CEA_SAD_FORMAT_PCM << 3 | (channels - 1);
342 sad->sampling_rates = sampling_rates;
343 sad->bitrate = sample_sizes;
344 }
345
346 /**
347 * cea_vsdb_get_hdmi_default:
348 *
349 * Returns the default Vendor Specific Data block for HDMI.
350 */
cea_vsdb_get_hdmi_default(size_t * size)351 const struct cea_vsdb *cea_vsdb_get_hdmi_default(size_t *size)
352 {
353 /* We'll generate a VSDB with 2 extension fields. */
354 static char raw[CEA_VSDB_HDMI_MIN_SIZE + 2] = {0};
355 struct cea_vsdb *vsdb;
356 struct hdmi_vsdb *hdmi;
357
358 *size = sizeof(raw);
359
360 /* Magic incantation. Works better if you orient your screen in the
361 * direction of the VESA headquarters. */
362 vsdb = (struct cea_vsdb *) raw;
363 memcpy(vsdb->ieee_oui, hdmi_ieee_oui, sizeof(hdmi_ieee_oui));
364 hdmi = &vsdb->data.hdmi;
365 hdmi->src_phy_addr[0] = 0x10;
366 hdmi->src_phy_addr[1] = 0x00;
367 /* 2 VSDB extension fields */
368 hdmi->flags1 = 0x38;
369 hdmi->max_tdms_clock = 0x2D;
370
371 return vsdb;
372 }
373
edid_cea_data_block_init(struct edid_cea_data_block * block,enum edid_cea_data_type type,size_t size)374 static void edid_cea_data_block_init(struct edid_cea_data_block *block,
375 enum edid_cea_data_type type, size_t size)
376 {
377 assert(size <= 0xFF);
378 block->type_len = type << 5 | size;
379 }
380
381 /**
382 * edid_cea_data_block_set_sad: initialize a CEA data block to contain Short
383 * Audio Descriptors
384 */
edid_cea_data_block_set_sad(struct edid_cea_data_block * block,const struct cea_sad * sads,size_t sads_len)385 size_t edid_cea_data_block_set_sad(struct edid_cea_data_block *block,
386 const struct cea_sad *sads, size_t sads_len)
387 {
388 size_t sads_size;
389
390 sads_size = sizeof(struct cea_sad) * sads_len;
391 edid_cea_data_block_init(block, EDID_CEA_DATA_AUDIO, sads_size);
392
393 memcpy(block->data.sads, sads, sads_size);
394
395 return sizeof(struct edid_cea_data_block) + sads_size;
396 }
397
398 /**
399 * edid_cea_data_block_set_svd: initialize a CEA data block to contain Short
400 * Video Descriptors
401 */
edid_cea_data_block_set_svd(struct edid_cea_data_block * block,const uint8_t * svds,size_t svds_len)402 size_t edid_cea_data_block_set_svd(struct edid_cea_data_block *block,
403 const uint8_t *svds, size_t svds_len)
404 {
405 edid_cea_data_block_init(block, EDID_CEA_DATA_VIDEO, svds_len);
406 memcpy(block->data.svds, svds, svds_len);
407 return sizeof(struct edid_cea_data_block) + svds_len;
408 }
409
410 /**
411 * edid_cea_data_block_set_vsdb: initialize a CEA data block to contain a
412 * Vendor Specific Data Block
413 */
edid_cea_data_block_set_vsdb(struct edid_cea_data_block * block,const struct cea_vsdb * vsdb,size_t vsdb_size)414 size_t edid_cea_data_block_set_vsdb(struct edid_cea_data_block *block,
415 const struct cea_vsdb *vsdb, size_t vsdb_size)
416 {
417 edid_cea_data_block_init(block, EDID_CEA_DATA_VENDOR_SPECIFIC,
418 vsdb_size);
419
420 memcpy(block->data.vsdbs, vsdb, vsdb_size);
421
422 return sizeof(struct edid_cea_data_block) + vsdb_size;
423 }
424
425 /**
426 * edid_cea_data_block_set_hdmi_vsdb: initialize a CEA data block to contain an
427 * HDMI VSDB
428 */
edid_cea_data_block_set_hdmi_vsdb(struct edid_cea_data_block * block,const struct hdmi_vsdb * hdmi,size_t hdmi_size)429 size_t edid_cea_data_block_set_hdmi_vsdb(struct edid_cea_data_block *block,
430 const struct hdmi_vsdb *hdmi,
431 size_t hdmi_size)
432 {
433 char raw_vsdb[CEA_VSDB_HDMI_MAX_SIZE] = {0};
434 struct cea_vsdb *vsdb = (struct cea_vsdb *) raw_vsdb;
435
436 assert(hdmi_size >= HDMI_VSDB_MIN_SIZE &&
437 hdmi_size <= HDMI_VSDB_MAX_SIZE);
438 memcpy(vsdb->ieee_oui, hdmi_ieee_oui, sizeof(hdmi_ieee_oui));
439 memcpy(&vsdb->data.hdmi, hdmi, hdmi_size);
440
441 return edid_cea_data_block_set_vsdb(block, vsdb,
442 CEA_VSDB_HEADER_SIZE + hdmi_size);
443 }
444
445 /**
446 * edid_cea_data_block_set_speaker_alloc: initialize a CEA data block to
447 * contain a Speaker Allocation Data block
448 */
edid_cea_data_block_set_speaker_alloc(struct edid_cea_data_block * block,const struct cea_speaker_alloc * speakers)449 size_t edid_cea_data_block_set_speaker_alloc(struct edid_cea_data_block *block,
450 const struct cea_speaker_alloc *speakers)
451 {
452 size_t size;
453
454 size = sizeof(struct cea_speaker_alloc);
455 edid_cea_data_block_init(block, EDID_CEA_DATA_SPEAKER_ALLOC, size);
456 memcpy(block->data.speakers, speakers, size);
457
458 return sizeof(struct edid_cea_data_block) + size;
459 }
460
461 /**
462 * edid_ext_set_cea: initialize an EDID extension block to contain a CEA
463 * extension. CEA extensions contain a Data Block Collection (with multiple
464 * CEA data blocks) followed by multiple Detailed Timing Descriptors.
465 */
edid_ext_set_cea(struct edid_ext * ext,size_t data_blocks_size,uint8_t num_native_dtds,uint8_t flags)466 void edid_ext_set_cea(struct edid_ext *ext, size_t data_blocks_size,
467 uint8_t num_native_dtds, uint8_t flags)
468 {
469 struct edid_cea *cea = &ext->data.cea;
470
471 ext->tag = EDID_EXT_CEA;
472
473 assert(num_native_dtds <= 0x0F);
474 assert((flags & 0x0F) == 0);
475 assert(data_blocks_size <= sizeof(cea->data));
476 cea->revision = 3;
477 cea->dtd_start = 4 + data_blocks_size;
478 cea->misc = flags | num_native_dtds;
479 }
480