• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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