• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /// This class is used to create the Extended Display Identification Data (EDID), which will be
25 /// exposed to the guest system.
26 ///
27 /// We ignore most of the spec, the point here being for us to provide enough for graphics to work
28 /// and to allow us to configure the resolution and refresh rate (via the preferred timing mode
29 /// pixel clock).
30 ///
31 /// The EDID spec defines a number of methods to provide mode information, but in priority order the
32 /// "detailed" timing information is first, so we provide a single block of detailed timing
33 /// information and no other form of timing information.
34 #[repr(C)]
35 pub struct EdidBytes {
36     bytes: [u8; EDID_DATA_LENGTH],
37 }
38 
39 impl EdidBytes {
40     /// Creates a virtual EDID block.
new(info: &DisplayInfo) -> VirtioGpuResult41     pub fn new(info: &DisplayInfo) -> VirtioGpuResult {
42         let mut edid: [u8; EDID_DATA_LENGTH] = [0; EDID_DATA_LENGTH];
43 
44         populate_header(&mut edid);
45         populate_edid_version(&mut edid);
46         populate_size(&mut edid, info);
47         populate_standard_timings(&mut edid)?;
48 
49         // 4 available descriptor blocks
50         let block0 = &mut edid[54..72];
51         populate_detailed_timing(block0, info);
52 
53         let block1 = &mut edid[72..90];
54         populate_display_name(block1);
55 
56         calculate_checksum(&mut edid);
57 
58         Ok(OkEdid(Box::new(Self { bytes: edid })))
59     }
60 
len(&self) -> usize61     pub fn len(&self) -> usize {
62         self.bytes.len()
63     }
64 
as_bytes(&self) -> &[u8]65     pub fn as_bytes(&self) -> &[u8] {
66         &self.bytes
67     }
68 }
69 
70 impl Debug for EdidBytes {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result71     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72         self.bytes[..].fmt(f)
73     }
74 }
75 
76 impl PartialEq for EdidBytes {
eq(&self, other: &EdidBytes) -> bool77     fn eq(&self, other: &EdidBytes) -> bool {
78         self.bytes[..] == other.bytes[..]
79     }
80 }
81 
82 #[derive(Copy, Clone)]
83 pub struct Resolution {
84     width: u32,
85     height: u32,
86 }
87 
88 impl Resolution {
new(width: u32, height: u32) -> Resolution89     fn new(width: u32, height: u32) -> Resolution {
90         Resolution { width, height }
91     }
92 
get_aspect_ratio(&self) -> (u32, u32)93     fn get_aspect_ratio(&self) -> (u32, u32) {
94         let divisor = gcd(self.width, self.height);
95         (self.width / divisor, self.height / divisor)
96     }
97 }
98 
gcd(x: u32, y: u32) -> u3299 fn gcd(x: u32, y: u32) -> u32 {
100     match y {
101         0 => x,
102         _ => gcd(y, x % y),
103     }
104 }
105 
106 #[derive(Copy, Clone)]
107 pub struct DisplayInfo {
108     resolution: Resolution,
109     refresh_rate: u32,
110     horizontal_blanking: u16,
111     vertical_blanking: u16,
112     horizontal_front: u16,
113     vertical_front: u16,
114     horizontal_sync: u16,
115     vertical_sync: u16,
116     width_millimeters: u16,
117     height_millimeters: u16,
118 }
119 
120 impl DisplayInfo {
121     /// Only width, height and refresh rate are required for the graphics stack to work, so instead
122     /// of pulling actual numbers from the system, we just use some typical values to populate other
123     /// fields for now.
new(params: &GpuDisplayParameters) -> Self124     pub fn new(params: &GpuDisplayParameters) -> Self {
125         let (width, height) = params.get_virtual_display_size();
126 
127         let width_millimeters = if params.horizontal_dpi() != 0 {
128             ((width as f32 / params.horizontal_dpi() as f32) * MILLIMETERS_PER_INCH) as u16
129         } else {
130             0
131         };
132         let height_millimeters = if params.vertical_dpi() != 0 {
133             ((height as f32 / params.vertical_dpi() as f32) * MILLIMETERS_PER_INCH) as u16
134         } else {
135             0
136         };
137 
138         Self {
139             resolution: Resolution::new(width, height),
140             refresh_rate: params.refresh_rate,
141             horizontal_blanking: DEFAULT_HORIZONTAL_BLANKING,
142             vertical_blanking: DEFAULT_VERTICAL_BLANKING,
143             horizontal_front: DEFAULT_HORIZONTAL_FRONT_PORCH,
144             vertical_front: DEFAULT_VERTICAL_FRONT_PORCH,
145             horizontal_sync: DEFAULT_HORIZONTAL_SYNC_PULSE,
146             vertical_sync: DEFAULT_VERTICAL_SYNC_PULSE,
147             width_millimeters,
148             height_millimeters,
149         }
150     }
151 
width(&self) -> u32152     pub fn width(&self) -> u32 {
153         self.resolution.width
154     }
155 
height(&self) -> u32156     pub fn height(&self) -> u32 {
157         self.resolution.height
158     }
159 
width_centimeters(&self) -> u8160     pub fn width_centimeters(&self) -> u8 {
161         (self.width_millimeters / 10) as u8
162     }
163 
height_centimeters(&self) -> u8164     pub fn height_centimeters(&self) -> u8 {
165         (self.height_millimeters / 10) as u8
166     }
167 }
168 
populate_display_name(edid_block: &mut [u8])169 fn populate_display_name(edid_block: &mut [u8]) {
170     // Display Product Name String Descriptor Tag
171     edid_block[0..5].clone_from_slice(&[0x00, 0x00, 0x00, 0xFC, 0x00]);
172     edid_block[5..].clone_from_slice("CrosvmDisplay".as_bytes());
173 }
174 
populate_detailed_timing(edid_block: &mut [u8], info: &DisplayInfo)175 fn populate_detailed_timing(edid_block: &mut [u8], info: &DisplayInfo) {
176     assert_eq!(edid_block.len(), 18);
177 
178     // Detailed timings
179     //
180     // 18 Byte Descriptors - 72 Bytes
181     // The 72 bytes in this section are divided into four data fields. Each of the four data fields
182     // are 18 bytes in length. These 18 byte data fields shall contain either detailed timing data
183     // as described in Section 3.10.2 or other types of data as described in Section 3.10.3. The
184     // addresses and the contents of the four 18 byte descriptors are shown in Table 3.20.
185     //
186     // We leave the bottom 6 bytes of this block purposefully empty.
187     let horizontal_blanking_lsb: u8 = (info.horizontal_blanking & 0xFF) as u8;
188     let horizontal_blanking_msb: u8 = ((info.horizontal_blanking >> 8) & 0x0F) as u8;
189 
190     let vertical_blanking_lsb: u8 = (info.vertical_blanking & 0xFF) as u8;
191     let vertical_blanking_msb: u8 = ((info.vertical_blanking >> 8) & 0x0F) as u8;
192 
193     // The pixel clock is what controls the refresh timing information.
194     //
195     // The formula for getting refresh rate out of this value is:
196     //   refresh_rate = clk * 10000 / (htotal * vtotal)
197     // Solving for clk:
198     //   clk = (refresh_rate * htotal * votal) / 10000
199     //
200     // where:
201     //   clk - The setting here
202     //   vtotal - Total lines
203     //   htotal - Total pixels per line
204     //
205     // Value here is pixel clock + 10,000, in 10khz steps.
206     //
207     // Pseudocode of kernel logic for vrefresh:
208     //    vtotal := mode->vtotal;
209     //    calc_val := (clock * 1000) / htotal
210     //    refresh := (calc_val + vtotal / 2) / vtotal
211     //    if flags & INTERLACE: refresh *= 2
212     //    if flags & DBLSCAN: refresh /= 2
213     //    if vscan > 1: refresh /= vscan
214     //
215     let htotal = info.width() + (info.horizontal_blanking as u32);
216     let vtotal = info.height() + (info.vertical_blanking as u32);
217     let mut clock: u16 = ((info.refresh_rate * htotal * vtotal) / 10000) as u16;
218     // Round to nearest 10khz.
219     clock = ((clock + 5) / 10) * 10;
220     edid_block[0..2].copy_from_slice(&clock.to_le_bytes());
221 
222     let width_lsb: u8 = (info.width() & 0xFF) as u8;
223     let width_msb: u8 = ((info.width() >> 8) & 0x0F) as u8;
224 
225     // Horizointal Addressable Video in pixels.
226     edid_block[2] = width_lsb;
227     // Horizontal blanking in pixels.
228     edid_block[3] = horizontal_blanking_lsb;
229     // Upper bits of the two above vals.
230     edid_block[4] = horizontal_blanking_msb | (width_msb << 4) as u8;
231 
232     let vertical_active: u32 = info.height();
233     let vertical_active_lsb: u8 = (vertical_active & 0xFF) as u8;
234     let vertical_active_msb: u8 = ((vertical_active >> 8) & 0x0F) as u8;
235 
236     // Vertical addressable video in *lines*
237     edid_block[5] = vertical_active_lsb;
238     // Vertical blanking in lines
239     edid_block[6] = vertical_blanking_lsb;
240     // Sigbits of the above.
241     edid_block[7] = vertical_blanking_msb | (vertical_active_msb << 4);
242 
243     let horizontal_front_lsb: u8 = (info.horizontal_front & 0xFF) as u8; // least sig 8 bits
244     let horizontal_front_msb: u8 = ((info.horizontal_front >> 8) & 0x03) as u8; // most sig 2 bits
245     let horizontal_sync_lsb: u8 = (info.horizontal_sync & 0xFF) as u8; // least sig 8 bits
246     let horizontal_sync_msb: u8 = ((info.horizontal_sync >> 8) & 0x03) as u8; // most sig 2 bits
247 
248     let vertical_front_lsb: u8 = (info.vertical_front & 0x0F) as u8; // least sig 4 bits
249     let vertical_front_msb: u8 = ((info.vertical_front >> 8) & 0x0F) as u8; // most sig 2 bits
250     let vertical_sync_lsb: u8 = (info.vertical_sync & 0xFF) as u8; // least sig 4 bits
251     let vertical_sync_msb: u8 = ((info.vertical_sync >> 8) & 0x0F) as u8; // most sig 2 bits
252 
253     // Horizontal front porch in pixels.
254     edid_block[8] = horizontal_front_lsb;
255     // Horizontal sync pulse width in pixels.
256     edid_block[9] = horizontal_sync_lsb;
257     // LSB of vertical front porch and sync pulse
258     edid_block[10] = vertical_sync_lsb | (vertical_front_lsb << 4);
259     // Upper 2 bits of these values.
260     edid_block[11] = vertical_sync_msb
261         | (vertical_front_msb << 2)
262         | (horizontal_sync_msb << 4)
263         | (horizontal_front_msb << 6);
264 
265     let width_millimeters_lsb: u8 = (info.width_millimeters & 0xFF) as u8; // least sig 8 bits
266     let width_millimeters_msb: u8 = ((info.width_millimeters >> 8) & 0xF) as u8; // most sig 4 bits
267 
268     let height_millimeters_lsb: u8 = (info.height_millimeters & 0xFF) as u8; // least sig 8 bits
269     let height_millimeters_msb: u8 = ((info.height_millimeters >> 8) & 0xF) as u8; // most sig 4 bits
270 
271     edid_block[12] = width_millimeters_lsb;
272     edid_block[13] = height_millimeters_lsb;
273     edid_block[14] = height_millimeters_msb | (width_millimeters_msb << 4);
274 }
275 
276 // The EDID header. This is defined by the EDID spec.
populate_header(edid: &mut [u8])277 fn populate_header(edid: &mut [u8]) {
278     edid[0] = 0x00;
279     edid[1] = 0xFF;
280     edid[2] = 0xFF;
281     edid[3] = 0xFF;
282     edid[4] = 0xFF;
283     edid[5] = 0xFF;
284     edid[6] = 0xFF;
285     edid[7] = 0x00;
286 
287     let manufacturer_name: [char; 3] = ['G', 'G', 'L'];
288     // 00001 -> A, 00010 -> B, etc
289     let manufacturer_id: u16 = manufacturer_name
290         .iter()
291         .map(|c| (*c as u8 - b'A' + 1) & 0x1F)
292         .fold(0u16, |res, lsb| (res << 5) | (lsb as u16));
293     edid[8..10].copy_from_slice(&manufacturer_id.to_be_bytes());
294 
295     let manufacture_product_id: u16 = 1;
296     edid[10..12].copy_from_slice(&manufacture_product_id.to_le_bytes());
297 
298     let serial_id: u32 = 1;
299     edid[12..16].copy_from_slice(&serial_id.to_le_bytes());
300 
301     let manufacture_week: u8 = 8;
302     edid[16] = manufacture_week;
303 
304     let manufacture_year: u32 = 2022;
305     edid[17] = (manufacture_year - 1990u32) as u8;
306 }
307 
308 // The standard timings are 8 timing modes with a lower priority (and different data format)
309 // than the 4 detailed timing modes.
populate_standard_timings(edid: &mut [u8]) -> VirtioGpuResult310 fn populate_standard_timings(edid: &mut [u8]) -> VirtioGpuResult {
311     let resolutions = [
312         Resolution::new(1440, 900),
313         Resolution::new(1600, 900),
314         Resolution::new(800, 600),
315         Resolution::new(1680, 1050),
316         Resolution::new(1856, 1392),
317         Resolution::new(1280, 1024),
318         Resolution::new(1400, 1050),
319         Resolution::new(1920, 1200),
320     ];
321 
322     // Index 0 is horizontal pixels / 8 - 31
323     // Index 1 is a combination of the refresh_rate - 60 (so we are setting to 0, for now) and two
324     // bits for the aspect ratio.
325     for (index, r) in resolutions.iter().enumerate() {
326         edid[0x26 + (index * 2)] = (r.width / 8 - 31) as u8;
327         let ar_bits = match r.get_aspect_ratio() {
328             (8, 5) => 0x0,
329             (4, 3) => 0x1,
330             (5, 4) => 0x2,
331             (16, 9) => 0x3,
332             (x, y) => return Err(ErrEdid(format!("Unsupported aspect ratio: {} {}", x, y))),
333         };
334         edid[0x27 + (index * 2)] = ar_bits;
335     }
336     Ok(OkNoData)
337 }
338 
339 // Per the EDID spec, needs to be 1 and 4.
populate_edid_version(edid: &mut [u8])340 fn populate_edid_version(edid: &mut [u8]) {
341     edid[18] = 1;
342     edid[19] = 4;
343 }
344 
populate_size(edid: &mut [u8], info: &DisplayInfo)345 fn populate_size(edid: &mut [u8], info: &DisplayInfo) {
346     edid[21] = info.width_centimeters();
347     edid[22] = info.height_centimeters();
348 }
349 
calculate_checksum(edid: &mut [u8])350 fn calculate_checksum(edid: &mut [u8]) {
351     let mut checksum: u8 = 0;
352     for byte in edid.iter().take(EDID_DATA_LENGTH - 1) {
353         checksum = checksum.wrapping_add(*byte);
354     }
355 
356     if checksum != 0 {
357         checksum = 255 - checksum + 1;
358     }
359 
360     edid[127] = checksum;
361 }
362