xref: /aosp_15_r20/external/autotest/client/cros/chameleon/edid.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from __future__ import absolute_import
7from __future__ import division
8from __future__ import print_function
9from functools import reduce
10import codecs
11import logging
12import operator
13import os
14import six
15from six.moves import map
16from six.moves import range
17import sys
18
19
20# TODO: This is a quick workaround; some of our arm devices so far only
21# support the HDMI EDIDs and the DP one at 1680x1050. A more proper
22# solution is to build a database of supported resolutions and pixel
23# clocks for each model and check if the EDID is in the supported list.
24def is_edid_supported(host, width, height):
25    """Check whether the EDID is supported by DUT
26
27    @param host: A CrosHost object.
28    @param width: The screen width
29    @param height: The screen height
30
31    @return: True if the check passes; False otherwise.
32    """
33    # TODO: Support client test that the host is not a CrosHost.
34    platform = host.get_platform()
35    if platform in ('snow', 'spring', 'skate', 'peach_pi', 'veyron_jerry'):
36        if (width, height) in [(1280, 800), (1440, 900), (1600, 900),
37                               (3840, 2160)]:
38            return False
39    if platform in ('kahlee', 'grunt'):
40        if (width, height) in [(3840, 2160)]:
41            return False
42    return True
43
44
45class Edid(object):
46    """Edid is an abstraction of EDID (Extended Display Identification Data).
47
48    It provides methods to get the properties, manipulate the structure,
49    import from a file, export to a file, etc.
50
51    """
52
53    BLOCK_SIZE = 128
54
55
56    def __init__(self, data, skip_verify=False):
57        """Construct an Edid.
58
59        @param data: A byte-array of EDID data.
60        @param skip_verify: True to skip the correctness check.
61        """
62        if not Edid.verify(data) and not skip_verify:
63            raise ValueError('Not a valid EDID.')
64        self.data = data
65
66
67    @staticmethod
68    def verify(data):
69        """Verify the correctness of EDID.
70
71        @param data: A byte-array of EDID data.
72
73        @return True if the EDID is correct; False otherwise.
74        """
75        data_len = len(data)
76        if data_len % Edid.BLOCK_SIZE != 0:
77            logging.debug('EDID has an invalid length: %d', data_len)
78            return False
79
80        for start in range(0, data_len, Edid.BLOCK_SIZE):
81            # Each block (128-byte) has a checksum at the last byte.
82            if sys.version_info.major < 3:
83                checksum = reduce(operator.add,
84                              list(map(ord, data[start:start+Edid.BLOCK_SIZE])))
85            else:
86                checksum = reduce(operator.add,
87                                  list(data[start:start+Edid.BLOCK_SIZE]))
88            if checksum % 256 != 0:
89                logging.debug('Wrong checksum in the block %d of EDID',
90                              start // Edid.BLOCK_SIZE)
91                return False
92
93        return True
94
95
96    @classmethod
97    def from_file(cls, filename, skip_verify=False):
98        """Construct an Edid from a file.
99
100        @param filename: A string of filename.
101        @param skip_verify: True to skip the correctness check.
102        """
103        if not os.path.exists(filename):
104            raise ValueError('EDID file %r does not exist' % filename)
105
106        if filename.upper().endswith('.TXT'):
107            # Convert the EDID text format, returning from xrandr.
108            data = reduce(operator.add,
109                          [codecs.decode(s.strip(), 'hex') for s in open(filename).readlines()])
110        else:
111            data = open(filename, 'rb').read()
112        return cls(data, skip_verify)
113
114
115    def to_file(self, filename):
116        """Export the EDID to a file.
117
118        @param filename: A string of filename.
119        """
120        with open(filename, 'w+') as f:
121            f.write(self.data)
122
123
124# A constant object to represent no EDID.
125NO_EDID = Edid('', skip_verify=True)
126