Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/gpu/edid.rs
5394 views
1
// Copyright 2022 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
//! Implementation of the EDID specification provided by software.
6
//! EDID spec: <https://glenwing.github.io/docs/VESA-EEDID-A2.pdf>
7
8
use std::fmt;
9
use std::fmt::Debug;
10
11
use super::protocol::GpuResponse::*;
12
use super::protocol::VirtioGpuResult;
13
use crate::virtio::gpu::GpuDisplayParameters;
14
15
const EDID_DATA_LENGTH: usize = 128;
16
const DEFAULT_HORIZONTAL_BLANKING: u16 = 560;
17
const DEFAULT_VERTICAL_BLANKING: u16 = 50;
18
const DEFAULT_HORIZONTAL_FRONT_PORCH: u16 = 64;
19
const DEFAULT_VERTICAL_FRONT_PORCH: u16 = 1;
20
const DEFAULT_HORIZONTAL_SYNC_PULSE: u16 = 192;
21
const DEFAULT_VERTICAL_SYNC_PULSE: u16 = 3;
22
const MILLIMETERS_PER_INCH: f32 = 25.4;
23
24
const DATA_BLOCK_TYPE_1_DETAILED_TIMING: u8 = 0x3;
25
const DATA_BLOCK_TYPE_1_DETAILED_TIMING_SIZE: u8 = 20;
26
const DATA_BLOCK_TYPE_1_DETAILED_TIMING_VERSION: u8 = 0x13;
27
const DISPLAYID_EXT: u8 = 0x70;
28
29
/// This class is used to create the Extended Display Identification Data (EDID), which will be
30
/// exposed to the guest system.
31
///
32
/// We ignore most of the spec, the point here being for us to provide enough for graphics to work
33
/// and to allow us to configure the resolution and refresh rate (via the preferred timing mode
34
/// pixel clock).
35
///
36
/// The EDID spec defines a number of methods to provide mode information, but in priority order the
37
/// "detailed" timing information is first, so we provide a single block of detailed timing
38
/// information and no other form of timing information.
39
#[repr(C)]
40
pub struct EdidBytes {
41
bytes: Vec<u8>,
42
}
43
44
impl EdidBytes {
45
/// Creates a virtual EDID block.
46
pub fn new(info: &DisplayInfo) -> VirtioGpuResult {
47
let mut edid = vec![0u8; EDID_DATA_LENGTH * 2];
48
49
populate_header(&mut edid);
50
populate_edid_version(&mut edid);
51
populate_size(&mut edid, info);
52
populate_standard_timings(&mut edid)?;
53
54
// Bytes 54 through 125 are divided into four descriptors. Each of the four descriptors
55
// are 18 bytes in length. These 18 byte descriptors shall contain either detailed timing
56
// data as described in Section 3.10.2 or other types of data as described in Section
57
// 3.10.3.
58
let descriptor1 = &mut edid[54..72];
59
populate_detailed_timing_descriptor(descriptor1, info);
60
let descriptor2 = &mut edid[72..90];
61
populate_display_name(descriptor2);
62
63
// We add one extension edid.
64
edid[126] = 1;
65
calculate_checksum(&mut edid, 127);
66
67
let display_id_extension = &mut edid[EDID_DATA_LENGTH..EDID_DATA_LENGTH * 2];
68
display_id_extension[0] = DISPLAYID_EXT; // This is a display id extensions block.
69
70
// Just populate a single display right now, starting at index 1.
71
populate_displayid_detailed_timings(display_id_extension, 1, info);
72
73
calculate_checksum(display_id_extension, 127);
74
75
Ok(OkEdid(Box::new(Self { bytes: edid })))
76
}
77
78
pub fn len(&self) -> usize {
79
self.bytes.len()
80
}
81
82
pub fn as_bytes(&self) -> &[u8] {
83
&self.bytes
84
}
85
}
86
87
impl Debug for EdidBytes {
88
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89
self.bytes[..].fmt(f)
90
}
91
}
92
93
impl PartialEq for EdidBytes {
94
fn eq(&self, other: &EdidBytes) -> bool {
95
self.bytes[..] == other.bytes[..]
96
}
97
}
98
99
#[derive(Copy, Clone)]
100
pub struct Resolution {
101
width: u32,
102
height: u32,
103
}
104
105
impl Resolution {
106
fn new(width: u32, height: u32) -> Resolution {
107
Resolution { width, height }
108
}
109
110
fn get_aspect_ratio(&self) -> (u32, u32) {
111
let divisor = gcd(self.width, self.height);
112
(self.width / divisor, self.height / divisor)
113
}
114
}
115
116
fn gcd(x: u32, y: u32) -> u32 {
117
match y {
118
0 => x,
119
_ => gcd(y, x % y),
120
}
121
}
122
123
#[derive(Copy, Clone)]
124
pub struct DisplayInfo {
125
resolution: Resolution,
126
refresh_rate: u32,
127
horizontal_blanking: u16,
128
vertical_blanking: u16,
129
horizontal_front: u16,
130
vertical_front: u16,
131
horizontal_sync: u16,
132
vertical_sync: u16,
133
width_millimeters: u16,
134
height_millimeters: u16,
135
}
136
137
impl DisplayInfo {
138
/// Only width, height and refresh rate are required for the graphics stack to work, so instead
139
/// of pulling actual numbers from the system, we just use some typical values to populate other
140
/// fields for now.
141
pub fn new(params: &GpuDisplayParameters) -> Self {
142
let (width, height) = params.get_virtual_display_size();
143
144
let width_millimeters = if params.horizontal_dpi() != 0 {
145
((width as f32 / params.horizontal_dpi() as f32) * MILLIMETERS_PER_INCH) as u16
146
} else {
147
0
148
};
149
let height_millimeters = if params.vertical_dpi() != 0 {
150
((height as f32 / params.vertical_dpi() as f32) * MILLIMETERS_PER_INCH) as u16
151
} else {
152
0
153
};
154
155
Self {
156
resolution: Resolution::new(width, height),
157
refresh_rate: params.refresh_rate,
158
horizontal_blanking: DEFAULT_HORIZONTAL_BLANKING,
159
vertical_blanking: DEFAULT_VERTICAL_BLANKING,
160
horizontal_front: DEFAULT_HORIZONTAL_FRONT_PORCH,
161
vertical_front: DEFAULT_VERTICAL_FRONT_PORCH,
162
horizontal_sync: DEFAULT_HORIZONTAL_SYNC_PULSE,
163
vertical_sync: DEFAULT_VERTICAL_SYNC_PULSE,
164
width_millimeters,
165
height_millimeters,
166
}
167
}
168
169
pub fn width(&self) -> u32 {
170
self.resolution.width
171
}
172
173
pub fn height(&self) -> u32 {
174
self.resolution.height
175
}
176
177
pub fn width_centimeters(&self) -> u8 {
178
(self.width_millimeters / 10) as u8
179
}
180
181
pub fn height_centimeters(&self) -> u8 {
182
(self.height_millimeters / 10) as u8
183
}
184
}
185
186
fn populate_display_name(edid_block: &mut [u8]) {
187
// Display Product Name String Descriptor Tag
188
edid_block[0..5].clone_from_slice(&[0x00, 0x00, 0x00, 0xFC, 0x00]);
189
edid_block[5..].clone_from_slice("CrosvmDisplay".as_bytes());
190
}
191
192
fn populate_detailed_timing_descriptor(edid_block: &mut [u8], info: &DisplayInfo) {
193
assert_eq!(edid_block.len(), 18);
194
195
// https://en.wikipedia.org/wiki/Extended_Display_Identification_Data#Detailed_Timing_Descriptor
196
197
// The pixel clock is what controls the refresh timing information.
198
//
199
// The formula for getting refresh rate out of this value is:
200
// refresh_rate = clk * 10000 / (htotal * vtotal)
201
// Solving for clk:
202
// clk = (refresh_rate * htotal * votal) / 10000
203
//
204
// where:
205
// clk - The setting here
206
// vtotal - Total lines
207
// htotal - Total pixels per line
208
//
209
// Value here is pixel clock + 10,000, in 10khz steps.
210
//
211
// Pseudocode of kernel logic for vrefresh:
212
// vtotal := mode->vtotal;
213
// calc_val := (clock * 1000) / htotal
214
// refresh := (calc_val + vtotal / 2) / vtotal
215
// if flags & INTERLACE: refresh *= 2
216
// if flags & DBLSCAN: refresh /= 2
217
// if vscan > 1: refresh /= vscan
218
//
219
let htotal = info.width() + (info.horizontal_blanking as u32);
220
let vtotal = info.height() + (info.vertical_blanking as u32);
221
let mut clock: u16 = ((info.refresh_rate * htotal * vtotal) / 10000) as u16;
222
// Round to nearest 10khz.
223
clock = ((clock + 5) / 10) * 10;
224
edid_block[0..2].copy_from_slice(&clock.to_le_bytes());
225
226
let width_lsb: u8 = (info.width() & 0b11111111) as u8; // least sig 8 bits
227
let width_msb: u8 = ((info.width() >> 8) & 0b00001111) as u8; // most sig 4 bits
228
229
let horizontal_blanking_lsb: u8 = (info.horizontal_blanking & 0b11111111) as u8; // least sig 8 bits
230
let horizontal_blanking_msb: u8 = ((info.horizontal_blanking >> 8) & 0b00001111) as u8; // most sig 4 bits
231
232
let vertical_blanking_lsb: u8 = (info.vertical_blanking & 0b11111111) as u8; // least sig 8 bits
233
let vertical_blanking_msb: u8 = ((info.vertical_blanking >> 8) & 0b00001111) as u8; // most sig 4 bits
234
235
// Horizointal Addressable Video in pixels.
236
edid_block[2] = width_lsb;
237
// Horizontal blanking in pixels.
238
edid_block[3] = horizontal_blanking_lsb;
239
// Upper bits of the two above vals.
240
edid_block[4] = horizontal_blanking_msb | (width_msb << 4);
241
242
let vertical_active: u32 = info.height();
243
let vertical_active_lsb: u8 = (vertical_active & 0xFF) as u8;
244
let vertical_active_msb: u8 = ((vertical_active >> 8) & 0x0F) as u8;
245
246
// Vertical addressable video in *lines*
247
edid_block[5] = vertical_active_lsb;
248
// Vertical blanking in lines
249
edid_block[6] = vertical_blanking_lsb;
250
// Sigbits of the above.
251
edid_block[7] = vertical_blanking_msb | (vertical_active_msb << 4);
252
253
let horizontal_front_lsb: u8 = (info.horizontal_front & 0b11111111) as u8; // least sig 8 bits
254
let horizontal_front_msb: u8 = ((info.horizontal_front >> 8) & 0b00000011) as u8; // most sig 2 bits
255
let horizontal_sync_lsb: u8 = (info.horizontal_sync & 0b11111111) as u8; // least sig 8 bits
256
let horizontal_sync_msb: u8 = ((info.horizontal_sync >> 8) & 0b00000011) as u8; // most sig 2 bits
257
258
let vertical_front_lsb: u8 = (info.vertical_front & 0b00001111) as u8; // least sig 4 bits
259
let vertical_front_msb: u8 = ((info.vertical_front >> 4) & 0b00000011) as u8; // most sig 2 bits
260
let vertical_sync_lsb: u8 = (info.vertical_sync & 0b00001111) as u8; // least sig 4 bits
261
let vertical_sync_msb: u8 = ((info.vertical_sync >> 4) & 0b00000011) as u8; // most sig 2 bits
262
263
// Horizontal front porch in pixels.
264
edid_block[8] = horizontal_front_lsb;
265
// Horizontal sync pulse width in pixels.
266
edid_block[9] = horizontal_sync_lsb;
267
// LSB of vertical front porch and sync pulse
268
edid_block[10] = vertical_sync_lsb | (vertical_front_lsb << 4);
269
// Upper 2 bits of these values.
270
edid_block[11] = vertical_sync_msb
271
| (vertical_front_msb << 2)
272
| (horizontal_sync_msb << 4)
273
| (horizontal_front_msb << 6);
274
275
let width_millimeters_lsb: u8 = (info.width_millimeters & 0b11111111) as u8; // least sig 8 bits
276
let width_millimeters_msb: u8 = ((info.width_millimeters >> 8) & 0b00001111) as u8; // most sig 4 bits
277
278
let height_millimeters_lsb: u8 = (info.height_millimeters & 0b11111111) as u8; // least sig 8 bits
279
let height_millimeters_msb: u8 = ((info.height_millimeters >> 8) & 0b00001111) as u8; // most sig 4 bits
280
281
edid_block[12] = width_millimeters_lsb;
282
edid_block[13] = height_millimeters_lsb;
283
edid_block[14] = height_millimeters_msb | (width_millimeters_msb << 4);
284
}
285
286
fn populate_displayid_detailed_timings(block: &mut [u8], start_index: usize, info: &DisplayInfo) {
287
// A single display id detailed timing block is 28 bytes:
288
// 4 bytes for the display id hdr
289
// 3 bytes for the display id block
290
// 20 bytes for the actual detailed timing data
291
// 1 byte for the checksum
292
let block = &mut block[start_index..start_index + 28];
293
block[0] = DATA_BLOCK_TYPE_1_DETAILED_TIMING_VERSION; // This doesn't seem to be used by the
294
// kernel.
295
block[1] = DATA_BLOCK_TYPE_1_DETAILED_TIMING_SIZE + 3; // Size of this data without this header.
296
block[2] = DATA_BLOCK_TYPE_1_DETAILED_TIMING; // Prod id. This doesn't seem to matter.
297
block[3] = 0; // Extension count
298
299
block[4] = DATA_BLOCK_TYPE_1_DETAILED_TIMING; // Structure of detailed timing info.
300
block[5] = 0x00; // Revision
301
block[6] = DATA_BLOCK_TYPE_1_DETAILED_TIMING_SIZE; // Length of the actual timing information,
302
// must be 20 for
303
// DATA_BLOCK_TYPE_1_DETAILED_TIMING.
304
305
// The pixel clock is what controls the refresh timing information.
306
//
307
// The formula for getting refresh rate out of this value is:
308
// refresh_rate = clk * 10000 / (htotal * vtotal)
309
// Solving for clk:
310
// clk = (refresh_rate * htotal * votal) / 10000
311
//
312
// where:
313
// clk - The setting here
314
// vtotal - Total lines
315
// htotal - Total pixels per line
316
//
317
// Value here is pixel clock + 10,000, in 10khz steps.
318
//
319
// Pseudocode of kernel logic for vrefresh:
320
// vtotal := mode->vtotal;
321
// calc_val := (clock * 1000) / htotal
322
// refresh := (calc_val + vtotal / 2) / vtotal
323
// if flags & INTERLACE: refresh *= 2
324
// if flags & DBLSCAN: refresh /= 2
325
// if vscan > 1: refresh /= vscan
326
327
let htotal = info.width() + (info.horizontal_blanking as u32);
328
let vtotal = info.height() + (info.vertical_blanking as u32);
329
let clock = info
330
.refresh_rate
331
.checked_mul(htotal)
332
.and_then(|x| x.checked_mul(vtotal))
333
.map(|x| x / 10000)
334
.unwrap_or_else(|| {
335
panic!(
336
concat!(
337
"attempt to multiply with overflow: info.refresh_rate = {}, info.width = {}, ",
338
"info.horizontal_blanking = {}, info.height() = {}, info.vertical_blanking = {}"
339
),
340
info.refresh_rate,
341
info.width(),
342
info.horizontal_blanking,
343
info.height(),
344
info.vertical_blanking
345
)
346
});
347
348
// 3 bytes for clock.
349
block[7] = (clock & 0xff) as u8;
350
block[8] = ((clock & 0xff00) >> 8) as u8;
351
block[9] = ((clock & 0xff0000) >> 16) as u8;
352
353
// Next byte is flags.
354
block[10] = 0x88;
355
356
// Note: We subtract 1 from all of these values because the kernel will then add 1 to all of
357
// them when they are read.
358
let hblanking = info.horizontal_blanking.saturating_sub(1);
359
let horizontal_blanking_lsb: u8 = (hblanking & 0xFF) as u8;
360
let horizontal_blanking_msb: u8 = ((hblanking >> 8) & 0x0F) as u8;
361
362
let vblanking = info.vertical_blanking.saturating_sub(1);
363
let vertical_blanking_lsb: u8 = (vblanking & 0xFF) as u8;
364
let vertical_blanking_msb: u8 = ((vblanking >> 8) & 0x0F) as u8;
365
366
let horizontal_active = info.width().saturating_sub(1);
367
let horizontal_active_lsb: u8 = (horizontal_active & 0xFF) as u8;
368
let horizontal_active_msb: u8 = ((horizontal_active >> 8) & 0xFF) as u8;
369
370
let vertical_active: u32 = info.height().saturating_sub(1);
371
let vertical_active_lsb: u8 = (vertical_active & 0xFF) as u8;
372
let vertical_active_msb: u8 = ((vertical_active >> 8) & 0xFF) as u8;
373
374
let hfront = info.horizontal_front.saturating_sub(1);
375
let horizontal_front_lsb: u8 = (hfront & 0xFF) as u8; // least sig 8 bits
376
let horizontal_front_msb: u8 = ((hfront >> 8) & 0x03) as u8; // most sig 2 bits
377
378
let hsync = info.horizontal_sync.saturating_sub(1);
379
let horizontal_sync_lsb: u8 = (hsync & 0xFF) as u8; // least sig 8 bits
380
let horizontal_sync_msb: u8 = ((hsync >> 8) & 0x03) as u8; // most sig 2 bits
381
382
let vfront = info.vertical_front.saturating_sub(1);
383
let vertical_front_lsb: u8 = (vfront & 0x0F) as u8; // least sig 4 bits
384
let vertical_front_msb: u8 = ((vfront >> 8) & 0x0F) as u8; // most sig 2 bits
385
386
let vsync = info.vertical_sync.saturating_sub(1);
387
let vertical_sync_lsb: u8 = (vsync & 0xFF) as u8; // least sig 4 bits
388
let vertical_sync_msb: u8 = ((vsync >> 8) & 0x0F) as u8; // most sig 2 bits
389
390
block[11] = horizontal_active_lsb;
391
block[12] = horizontal_active_msb;
392
block[13] = horizontal_blanking_lsb;
393
block[14] = horizontal_blanking_msb;
394
block[15] = horizontal_front_lsb;
395
block[16] = horizontal_front_msb;
396
block[17] = horizontal_sync_lsb;
397
block[18] = horizontal_sync_msb;
398
block[19] = vertical_active_lsb;
399
block[20] = vertical_active_msb;
400
block[21] = vertical_blanking_lsb;
401
block[22] = vertical_blanking_msb;
402
block[23] = vertical_front_lsb;
403
block[24] = vertical_front_msb;
404
block[25] = vertical_sync_lsb;
405
block[26] = vertical_sync_msb;
406
407
calculate_checksum(block, 27);
408
}
409
410
// The EDID header. This is defined by the EDID spec.
411
fn populate_header(edid: &mut [u8]) {
412
edid[0] = 0x00;
413
edid[1] = 0xFF;
414
edid[2] = 0xFF;
415
edid[3] = 0xFF;
416
edid[4] = 0xFF;
417
edid[5] = 0xFF;
418
edid[6] = 0xFF;
419
edid[7] = 0x00;
420
421
let manufacturer_name: [char; 3] = ['G', 'G', 'L'];
422
// 00001 -> A, 00010 -> B, etc
423
let manufacturer_id: u16 = manufacturer_name
424
.iter()
425
.map(|c| (*c as u8 - b'A' + 1) & 0x1F)
426
.fold(0u16, |res, lsb| (res << 5) | (lsb as u16));
427
edid[8..10].copy_from_slice(&manufacturer_id.to_be_bytes());
428
429
let manufacture_product_id: u16 = 1;
430
edid[10..12].copy_from_slice(&manufacture_product_id.to_le_bytes());
431
432
let serial_id: u32 = 1;
433
edid[12..16].copy_from_slice(&serial_id.to_le_bytes());
434
435
let manufacture_week: u8 = 8;
436
edid[16] = manufacture_week;
437
438
let manufacture_year: u32 = 2022;
439
edid[17] = (manufacture_year - 1990u32) as u8;
440
}
441
442
// The standard timings are 8 timing modes with a lower priority (and different data format)
443
// than the 4 detailed timing modes.
444
fn populate_standard_timings(edid: &mut [u8]) -> VirtioGpuResult {
445
let resolutions = [
446
Resolution::new(1440, 900),
447
Resolution::new(1600, 900),
448
Resolution::new(800, 600),
449
Resolution::new(1680, 1050),
450
Resolution::new(1856, 1392),
451
Resolution::new(1280, 1024),
452
Resolution::new(1400, 1050),
453
Resolution::new(1920, 1200),
454
];
455
456
// Index 0 is horizontal pixels / 8 - 31
457
// Index 1 is a combination of the refresh_rate - 60 (so we are setting to 0, for now) and two
458
// bits for the aspect ratio.
459
for (index, r) in resolutions.iter().enumerate() {
460
edid[0x26 + (index * 2)] = (r.width / 8 - 31) as u8;
461
let ar_bits = match r.get_aspect_ratio() {
462
(8, 5) => 0x0,
463
(4, 3) => 0x1,
464
(5, 4) => 0x2,
465
(16, 9) => 0x3,
466
(x, y) => return Err(ErrEdid(format!("Unsupported aspect ratio: {x} {y}"))),
467
};
468
edid[0x27 + (index * 2)] = ar_bits;
469
}
470
Ok(OkNoData)
471
}
472
473
// Per the EDID spec, needs to be 1 and 4.
474
fn populate_edid_version(edid: &mut [u8]) {
475
edid[18] = 1;
476
edid[19] = 4;
477
}
478
479
fn populate_size(edid: &mut [u8], info: &DisplayInfo) {
480
edid[21] = info.width_centimeters();
481
edid[22] = info.height_centimeters();
482
}
483
484
fn calculate_checksum(block: &mut [u8], length: usize) {
485
let mut checksum: u8 = 0;
486
for byte in block.iter().take(length) {
487
checksum = checksum.wrapping_add(*byte);
488
}
489
490
if checksum != 0 {
491
checksum = 255 - checksum + 1;
492
}
493
494
block[length] = checksum;
495
}
496
497