devices/virtio/gpu/
edid.rs

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
8use std::fmt;
9use std::fmt::Debug;
10
11use super::protocol::GpuResponse::*;
12use super::protocol::VirtioGpuResult;
13use crate::virtio::gpu::GpuDisplayParameters;
14
15const EDID_DATA_LENGTH: usize = 128;
16const DEFAULT_HORIZONTAL_BLANKING: u16 = 560;
17const DEFAULT_VERTICAL_BLANKING: u16 = 50;
18const DEFAULT_HORIZONTAL_FRONT_PORCH: u16 = 64;
19const DEFAULT_VERTICAL_FRONT_PORCH: u16 = 1;
20const DEFAULT_HORIZONTAL_SYNC_PULSE: u16 = 192;
21const DEFAULT_VERTICAL_SYNC_PULSE: u16 = 3;
22const MILLIMETERS_PER_INCH: f32 = 25.4;
23
24const DATA_BLOCK_TYPE_1_DETAILED_TIMING: u8 = 0x3;
25const DATA_BLOCK_TYPE_1_DETAILED_TIMING_SIZE: u8 = 20;
26const DATA_BLOCK_TYPE_1_DETAILED_TIMING_VERSION: u8 = 0x13;
27const 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)]
40pub struct EdidBytes {
41    bytes: Vec<u8>,
42}
43
44impl 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
87impl Debug for EdidBytes {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        self.bytes[..].fmt(f)
90    }
91}
92
93impl PartialEq for EdidBytes {
94    fn eq(&self, other: &EdidBytes) -> bool {
95        self.bytes[..] == other.bytes[..]
96    }
97}
98
99#[derive(Copy, Clone)]
100pub struct Resolution {
101    width: u32,
102    height: u32,
103}
104
105impl 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
116fn gcd(x: u32, y: u32) -> u32 {
117    match y {
118        0 => x,
119        _ => gcd(y, x % y),
120    }
121}
122
123#[derive(Copy, Clone)]
124pub 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
137impl 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
186fn 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
192fn 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
230                                                                                     // bits
231    let horizontal_blanking_msb: u8 = ((info.horizontal_blanking >> 8) & 0b00001111) as u8; // most sig 4 bits
232
233    let vertical_blanking_lsb: u8 = (info.vertical_blanking & 0b11111111) as u8; // least sig 8 bits
234    let vertical_blanking_msb: u8 = ((info.vertical_blanking >> 8) & 0b00001111) as u8; // most sig 4 bits
235
236    // Horizointal Addressable Video in pixels.
237    edid_block[2] = width_lsb;
238    // Horizontal blanking in pixels.
239    edid_block[3] = horizontal_blanking_lsb;
240    // Upper bits of the two above vals.
241    edid_block[4] = horizontal_blanking_msb | (width_msb << 4);
242
243    let vertical_active: u32 = info.height();
244    let vertical_active_lsb: u8 = (vertical_active & 0xFF) as u8;
245    let vertical_active_msb: u8 = ((vertical_active >> 8) & 0x0F) as u8;
246
247    // Vertical addressable video in *lines*
248    edid_block[5] = vertical_active_lsb;
249    // Vertical blanking in lines
250    edid_block[6] = vertical_blanking_lsb;
251    // Sigbits of the above.
252    edid_block[7] = vertical_blanking_msb | (vertical_active_msb << 4);
253
254    let horizontal_front_lsb: u8 = (info.horizontal_front & 0b11111111) as u8; // least sig 8 bits
255    let horizontal_front_msb: u8 = ((info.horizontal_front >> 8) & 0b00000011) as u8; // most sig 2
256                                                                                      // bits
257    let horizontal_sync_lsb: u8 = (info.horizontal_sync & 0b11111111) as u8; // least sig 8 bits
258    let horizontal_sync_msb: u8 = ((info.horizontal_sync >> 8) & 0b00000011) as u8; // most sig 2
259                                                                                    // bits
260
261    let vertical_front_lsb: u8 = (info.vertical_front & 0b00001111) as u8; // least sig 4 bits
262    let vertical_front_msb: u8 = ((info.vertical_front >> 4) & 0b00000011) as u8; // most sig 2 bits
263    let vertical_sync_lsb: u8 = (info.vertical_sync & 0b00001111) as u8; // least sig 4 bits
264    let vertical_sync_msb: u8 = ((info.vertical_sync >> 4) & 0b00000011) as u8; // most sig 2 bits
265
266    // Horizontal front porch in pixels.
267    edid_block[8] = horizontal_front_lsb;
268    // Horizontal sync pulse width in pixels.
269    edid_block[9] = horizontal_sync_lsb;
270    // LSB of vertical front porch and sync pulse
271    edid_block[10] = vertical_sync_lsb | (vertical_front_lsb << 4);
272    // Upper 2 bits of these values.
273    edid_block[11] = vertical_sync_msb
274        | (vertical_front_msb << 2)
275        | (horizontal_sync_msb << 4)
276        | (horizontal_front_msb << 6);
277
278    let width_millimeters_lsb: u8 = (info.width_millimeters & 0b11111111) as u8; // least sig 8 bits
279    let width_millimeters_msb: u8 = ((info.width_millimeters >> 8) & 0b00001111) as u8; // most sig 4 bits
280
281    let height_millimeters_lsb: u8 = (info.height_millimeters & 0b11111111) as u8; // least sig 8
282                                                                                   // bits
283    let height_millimeters_msb: u8 = ((info.height_millimeters >> 8) & 0b00001111) as u8; // most sig 4 bits
284
285    edid_block[12] = width_millimeters_lsb;
286    edid_block[13] = height_millimeters_lsb;
287    edid_block[14] = height_millimeters_msb | (width_millimeters_msb << 4);
288}
289
290fn populate_displayid_detailed_timings(block: &mut [u8], start_index: usize, info: &DisplayInfo) {
291    // A single display id detailed timing block is 28 bytes:
292    //  4 bytes for the display id hdr
293    //  3 bytes for the display id block
294    //  20 bytes for the actual detailed timing data
295    //  1 byte for the checksum
296    let block = &mut block[start_index..start_index + 28];
297    block[0] = DATA_BLOCK_TYPE_1_DETAILED_TIMING_VERSION; // This doesn't seem to be used by the
298                                                          // kernel.
299    block[1] = DATA_BLOCK_TYPE_1_DETAILED_TIMING_SIZE + 3; // Size of this data without this header.
300    block[2] = DATA_BLOCK_TYPE_1_DETAILED_TIMING; // Prod id. This doesn't seem to matter.
301    block[3] = 0; // Extension count
302
303    block[4] = DATA_BLOCK_TYPE_1_DETAILED_TIMING; // Structure of detailed timing info.
304    block[5] = 0x00; // Revision
305    block[6] = DATA_BLOCK_TYPE_1_DETAILED_TIMING_SIZE; // Length of the actual timing information,
306                                                       // must be 20 for
307                                                       // DATA_BLOCK_TYPE_1_DETAILED_TIMING.
308
309    // The pixel clock is what controls the refresh timing information.
310    //
311    // The formula for getting refresh rate out of this value is:
312    //   refresh_rate = clk * 10000 / (htotal * vtotal)
313    // Solving for clk:
314    //   clk = (refresh_rate * htotal * votal) / 10000
315    //
316    // where:
317    //   clk - The setting here
318    //   vtotal - Total lines
319    //   htotal - Total pixels per line
320    //
321    // Value here is pixel clock + 10,000, in 10khz steps.
322    //
323    // Pseudocode of kernel logic for vrefresh:
324    //    vtotal := mode->vtotal;
325    //    calc_val := (clock * 1000) / htotal
326    //    refresh := (calc_val + vtotal / 2) / vtotal
327    //    if flags & INTERLACE: refresh *= 2
328    //    if flags & DBLSCAN: refresh /= 2
329    //    if vscan > 1: refresh /= vscan
330
331    let htotal = info.width() + (info.horizontal_blanking as u32);
332    let vtotal = info.height() + (info.vertical_blanking as u32);
333    let clock = info
334        .refresh_rate
335        .checked_mul(htotal)
336        .and_then(|x| x.checked_mul(vtotal))
337        .map(|x| x / 10000)
338        .unwrap_or_else(|| {
339            panic!(
340                concat!(
341                    "attempt to multiply with overflow: info.refresh_rate = {}, info.width = {}, ",
342                    "info.horizontal_blanking = {}, info.height() = {}, info.vertical_blanking = {}"
343                ),
344                info.refresh_rate,
345                info.width(),
346                info.horizontal_blanking,
347                info.height(),
348                info.vertical_blanking
349            )
350        });
351
352    // 3 bytes for clock.
353    block[7] = (clock & 0xff) as u8;
354    block[8] = ((clock & 0xff00) >> 8) as u8;
355    block[9] = ((clock & 0xff0000) >> 16) as u8;
356
357    // Next byte is flags.
358    block[10] = 0x88;
359
360    // Note: We subtract 1 from all of these values because the kernel will then add 1 to all of
361    // them when they are read.
362    let hblanking = info.horizontal_blanking.saturating_sub(1);
363    let horizontal_blanking_lsb: u8 = (hblanking & 0xFF) as u8;
364    let horizontal_blanking_msb: u8 = ((hblanking >> 8) & 0x0F) as u8;
365
366    let vblanking = info.vertical_blanking.saturating_sub(1);
367    let vertical_blanking_lsb: u8 = (vblanking & 0xFF) as u8;
368    let vertical_blanking_msb: u8 = ((vblanking >> 8) & 0x0F) as u8;
369
370    let horizontal_active = info.width().saturating_sub(1);
371    let horizontal_active_lsb: u8 = (horizontal_active & 0xFF) as u8;
372    let horizontal_active_msb: u8 = ((horizontal_active >> 8) & 0xFF) as u8;
373
374    let vertical_active: u32 = info.height().saturating_sub(1);
375    let vertical_active_lsb: u8 = (vertical_active & 0xFF) as u8;
376    let vertical_active_msb: u8 = ((vertical_active >> 8) & 0xFF) as u8;
377
378    let hfront = info.horizontal_front.saturating_sub(1);
379    let horizontal_front_lsb: u8 = (hfront & 0xFF) as u8; // least sig 8 bits
380    let horizontal_front_msb: u8 = ((hfront >> 8) & 0x03) as u8; // most sig 2 bits
381
382    let hsync = info.horizontal_sync.saturating_sub(1);
383    let horizontal_sync_lsb: u8 = (hsync & 0xFF) as u8; // least sig 8 bits
384    let horizontal_sync_msb: u8 = ((hsync >> 8) & 0x03) as u8; // most sig 2 bits
385
386    let vfront = info.vertical_front.saturating_sub(1);
387    let vertical_front_lsb: u8 = (vfront & 0x0F) as u8; // least sig 4 bits
388    let vertical_front_msb: u8 = ((vfront >> 8) & 0x0F) as u8; // most sig 2 bits
389
390    let vsync = info.vertical_sync.saturating_sub(1);
391    let vertical_sync_lsb: u8 = (vsync & 0xFF) as u8; // least sig 4 bits
392    let vertical_sync_msb: u8 = ((vsync >> 8) & 0x0F) as u8; // most sig 2 bits
393
394    block[11] = horizontal_active_lsb;
395    block[12] = horizontal_active_msb;
396    block[13] = horizontal_blanking_lsb;
397    block[14] = horizontal_blanking_msb;
398    block[15] = horizontal_front_lsb;
399    block[16] = horizontal_front_msb;
400    block[17] = horizontal_sync_lsb;
401    block[18] = horizontal_sync_msb;
402    block[19] = vertical_active_lsb;
403    block[20] = vertical_active_msb;
404    block[21] = vertical_blanking_lsb;
405    block[22] = vertical_blanking_msb;
406    block[23] = vertical_front_lsb;
407    block[24] = vertical_front_msb;
408    block[25] = vertical_sync_lsb;
409    block[26] = vertical_sync_msb;
410
411    calculate_checksum(block, 27);
412}
413
414// The EDID header. This is defined by the EDID spec.
415fn populate_header(edid: &mut [u8]) {
416    edid[0] = 0x00;
417    edid[1] = 0xFF;
418    edid[2] = 0xFF;
419    edid[3] = 0xFF;
420    edid[4] = 0xFF;
421    edid[5] = 0xFF;
422    edid[6] = 0xFF;
423    edid[7] = 0x00;
424
425    let manufacturer_name: [char; 3] = ['G', 'G', 'L'];
426    // 00001 -> A, 00010 -> B, etc
427    let manufacturer_id: u16 = manufacturer_name
428        .iter()
429        .map(|c| (*c as u8 - b'A' + 1) & 0x1F)
430        .fold(0u16, |res, lsb| (res << 5) | (lsb as u16));
431    edid[8..10].copy_from_slice(&manufacturer_id.to_be_bytes());
432
433    let manufacture_product_id: u16 = 1;
434    edid[10..12].copy_from_slice(&manufacture_product_id.to_le_bytes());
435
436    let serial_id: u32 = 1;
437    edid[12..16].copy_from_slice(&serial_id.to_le_bytes());
438
439    let manufacture_week: u8 = 8;
440    edid[16] = manufacture_week;
441
442    let manufacture_year: u32 = 2022;
443    edid[17] = (manufacture_year - 1990u32) as u8;
444}
445
446// The standard timings are 8 timing modes with a lower priority (and different data format)
447// than the 4 detailed timing modes.
448fn populate_standard_timings(edid: &mut [u8]) -> VirtioGpuResult {
449    let resolutions = [
450        Resolution::new(1440, 900),
451        Resolution::new(1600, 900),
452        Resolution::new(800, 600),
453        Resolution::new(1680, 1050),
454        Resolution::new(1856, 1392),
455        Resolution::new(1280, 1024),
456        Resolution::new(1400, 1050),
457        Resolution::new(1920, 1200),
458    ];
459
460    // Index 0 is horizontal pixels / 8 - 31
461    // Index 1 is a combination of the refresh_rate - 60 (so we are setting to 0, for now) and two
462    // bits for the aspect ratio.
463    for (index, r) in resolutions.iter().enumerate() {
464        edid[0x26 + (index * 2)] = (r.width / 8 - 31) as u8;
465        let ar_bits = match r.get_aspect_ratio() {
466            (8, 5) => 0x0,
467            (4, 3) => 0x1,
468            (5, 4) => 0x2,
469            (16, 9) => 0x3,
470            (x, y) => return Err(ErrEdid(format!("Unsupported aspect ratio: {x} {y}"))),
471        };
472        edid[0x27 + (index * 2)] = ar_bits;
473    }
474    Ok(OkNoData)
475}
476
477// Per the EDID spec, needs to be 1 and 4.
478fn populate_edid_version(edid: &mut [u8]) {
479    edid[18] = 1;
480    edid[19] = 4;
481}
482
483fn populate_size(edid: &mut [u8], info: &DisplayInfo) {
484    edid[21] = info.width_centimeters();
485    edid[22] = info.height_centimeters();
486}
487
488fn calculate_checksum(block: &mut [u8], length: usize) {
489    let mut checksum: u8 = 0;
490    for byte in block.iter().take(length) {
491        checksum = checksum.wrapping_add(*byte);
492    }
493
494    if checksum != 0 {
495        checksum = 255 - checksum + 1;
496    }
497
498    block[length] = checksum;
499}