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