• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging, os, re, struct, time
6
7from autotest_lib.client.bin import test
8from autotest_lib.client.bin import utils
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib.cros import chrome
11from autotest_lib.client.cros import cros_logging
12
13# Kernel 3.8 to 3.14 has cur_delay_info, 3.18+ has frequency_info.
14CLOCK_PATHS = ['/sys/kernel/debug/dri/0/i915_frequency_info',
15               '/sys/kernel/debug/dri/0/i915_cur_delayinfo']
16# Kernel 3.8 has i915_fbc, kernel > 3.8 i915_fbc_status.
17FBC_PATHS = ['/sys/kernel/debug/dri/0/i915_fbc',
18             '/sys/kernel/debug/dri/0/i915_fbc_status']
19GEM_PATHS = ['/sys/kernel/debug/dri/0/i915_gem_active']
20PSR_PATHS = ['/sys/kernel/debug/dri/0/i915_edp_psr_status']
21RC6_PATHS = ['/sys/kernel/debug/dri/0/i915_drpc_info']
22
23
24class graphics_Idle(test.test):
25    """Class for graphics_Idle.  See 'control' for details."""
26    version = 1
27    _gpu_type = None
28    _cpu_type = None
29    _board = None
30
31    def run_once(self):
32        # Try to protect against runaway previous tests.
33        if not utils.wait_for_idle_cpu(20.0, 0.1):
34            logging.warning('Could not get idle CPU before running tests.')
35        # We use kiosk mode to make sure Chrome is idle.
36        with chrome.Chrome(logged_in=False, extra_browser_args=['--kiosk']):
37            self._gpu_type = utils.get_gpu_family()
38            self._cpu_type = utils.get_cpu_soc_family()
39            self._board = utils.get_board()
40            errors = ''
41            errors += self.verify_graphics_dvfs()
42            errors += self.verify_graphics_fbc()
43            errors += self.verify_graphics_psr()
44            errors += self.verify_graphics_gem_idle()
45            errors += self.verify_graphics_i915_min_clock()
46            errors += self.verify_graphics_rc6()
47            errors += self.verify_lvds_downclock()
48            errors += self.verify_short_blanking()
49            if errors:
50                raise error.TestFail(errors)
51
52    def get_valid_path(self, paths):
53        for path in paths:
54            if os.path.exists(path):
55                return path
56        logging.error('Error: %s not found.', ' '.join(paths))
57        return None
58
59    def verify_lvds_downclock(self):
60        """On systems which support LVDS downclock, checks the kernel log for
61        a message that an LVDS downclock mode has been added."""
62        logging.info('Running verify_lvds_downclock')
63        board = utils.get_board()
64        if not (board == 'alex' or board == 'lumpy' or board == 'stout'):
65            return ''
66
67        # Get the downclock message from the logs.
68        reader = cros_logging.LogReader()
69        reader.set_start_by_reboot(-1)
70        if not reader.can_find('Adding LVDS downclock mode'):
71            logging.error('Error: LVDS downclock quirk not applied.')
72            return 'LVDS downclock quirk not applied. '
73
74        return ''
75
76    def verify_short_blanking(self):
77        """On baytrail systems with a known panel, checks the kernel log for a
78        message that a short blanking mode has been added."""
79        logging.info('Running verify_short_blanking')
80        if self._gpu_type != 'baytrail' or utils.has_no_monitor():
81            return ''
82
83        # Open the EDID to find the panel model.
84        param_path = '/sys/class/drm/card0-eDP-1/edid'
85        if not os.path.exists(param_path):
86            logging.error('Error: %s not found.', param_path)
87            return 'Short blanking not added (no EDID found). '
88
89        with open(param_path, 'r') as edp_edid_file:
90            edp_edid_file.seek(8)
91            data = edp_edid_file.read(2)
92            manufacturer = int(struct.unpack('<H', data)[0])
93            data = edp_edid_file.read(2)
94            product_code = int(struct.unpack('<H', data)[0])
95
96        # This is not the panel we are looking for (AUO B116XTN02.2)
97        if manufacturer != 0xaf06 or product_code != 0x225c:
98            return ''
99
100        # Get the downclock message from the logs.
101        reader = cros_logging.LogReader()
102        reader.set_start_by_reboot(-1)
103        if not reader.can_find('Modified preferred into a short blanking mode'):
104            logging.error('Error: short blanking not added.')
105            return 'Short blanking not added. '
106
107        return ''
108
109    def verify_graphics_rc6(self):
110        """ On systems which support RC6 (non atom), check that we are able to
111        get into rc6; idle before doing so, and retry every second for 20
112        seconds."""
113        logging.info('Running verify_graphics_rc6')
114        # TODO(ihf): Implement on baytrail/braswell using residency counters.
115        # But note the format changed since SNB, so this will be complex.
116        if (utils.get_cpu_soc_family() == 'x86_64' and
117                self._gpu_type != 'pinetrail' and
118                self._gpu_type != 'baytrail' and self._gpu_type != 'braswell'):
119            tries = 0
120            found = False
121            param_path = self.get_valid_path(RC6_PATHS)
122            if not param_path:
123                return 'RC6_PATHS not found.'
124            while found == False and tries < 20:
125                time.sleep(1)
126                with open(param_path, 'r') as drpc_info_file:
127                    for line in drpc_info_file:
128                        match = re.search(r'Current RC state: (.*)', line)
129                        if match and match.group(1) == 'RC6':
130                            found = True
131                            break
132
133                tries += 1
134
135            if not found:
136                utils.log_process_activity()
137                logging.error('Error: did not see the GPU in RC6.')
138                return 'Did not see the GPU in RC6. '
139
140        return ''
141
142    def verify_graphics_i915_min_clock(self):
143        """ On i915 systems, check that we get into the lowest clock frequency;
144        idle before doing so, and retry every second for 20 seconds."""
145        logging.info('Running verify_graphics_i915_min_clock')
146        if (utils.get_cpu_soc_family() == 'x86_64' and
147                self._gpu_type != 'pinetrail'):
148            tries = 0
149            found = False
150            param_path = self.get_valid_path(CLOCK_PATHS)
151            if not param_path:
152                return 'CLOCK_PATHS not found.'
153            while not found and tries < 80:
154                time.sleep(0.25)
155
156                with open(param_path, 'r') as delayinfo_file:
157                    for line in delayinfo_file:
158                        # This file has a different format depending on the
159                        # board, so we parse both. Also, it would be tedious
160                        # to add the minimum clock for each board, so instead
161                        # we use 650MHz which is the max of the minimum clocks.
162                        match = re.search(r'CAGF: (.*)MHz', line)
163                        if match and int(match.group(1)) <= 650:
164                            found = True
165                            break
166
167                        match = re.search(r'current GPU freq: (.*) MHz', line)
168                        if match and int(match.group(1)) <= 650:
169                            found = True
170                            break
171
172                tries += 1
173
174            if not found:
175                utils.log_process_activity()
176                logging.error('Error: did not see the min i915 clock')
177                return 'Did not see the min i915 clock. '
178
179        return ''
180
181    def verify_graphics_dvfs(self):
182        """ On systems which support DVFS, check that we get into the lowest
183        clock frequency; idle before doing so, and retry every second for 20
184        seconds."""
185        logging.info('Running verify_graphics_dvfs')
186        if self._gpu_type == 'mali':
187            if self._cpu_type == 'exynos5':
188                node = '/sys/devices/11800000.mali/'
189                enable_node = 'dvfs'
190                enable_value = 'on'
191            elif self._cpu_type == 'rockchip':
192                node = '/sys/devices/ffa30000.gpu/'
193                enable_node = 'dvfs_enable'
194                enable_value = '1'
195            else:
196                logging.error('Error: Unknown CPU type (%s) for mali GPU.',
197                              self._cpu_type)
198                return 'Unknown CPU type for mali GPU. '
199
200            clock_path = utils.locate_file('clock', node)
201            enable_path = utils.locate_file(enable_node, node)
202            freqs_path = utils.locate_file('available_frequencies', node)
203
204            enable = utils.read_one_line(enable_path)
205            logging.info('DVFS enable = %s', enable)
206            if not enable == enable_value:
207                logging.error('Error: DVFS is not enabled')
208                return 'DVFS is not enabled. '
209
210            # available_frequencies are always sorted in ascending order
211            lowest_freq = int(utils.read_one_line(freqs_path))
212
213            # daisy_* (exynos5250) boards set idle frequency to 266000000
214            # See: crbug.com/467401 and crosbug.com/p/19710
215            if self._board.startswith('daisy'):
216                lowest_freq = 266000000
217
218            logging.info('Expecting idle DVFS clock = %u', lowest_freq)
219
220            tries = 0
221            found = False
222            while not found and tries < 80:
223                time.sleep(0.25)
224                clock = int(utils.read_one_line(clock_path))
225                if clock <= lowest_freq:
226                    logging.info('Found idle DVFS clock = %u', clock)
227                    found = True
228                    break
229
230                tries += 1
231
232            if not found:
233                utils.log_process_activity()
234                logging.error('Error: DVFS clock (%u) > min (%u)', clock,
235                              lowest_freq)
236                return 'Did not see the min DVFS clock. '
237
238        return ''
239
240    def verify_graphics_fbc(self):
241        """ On systems which support FBC, check that we can get into FBC;
242        idle before doing so, and retry every second for 20 seconds."""
243        logging.info('Running verify_graphics_fbc')
244
245        # Link's FBC is disabled (crbug.com/338588).
246        # TODO(marcheu): remove this when/if we fix this bug.
247        board = utils.get_board()
248        if board == 'link':
249            return ''
250
251        # Machines which don't have a monitor can't get FBC.
252        if utils.has_no_monitor():
253            return ''
254
255        if (self._gpu_type == 'haswell' or self._gpu_type == 'ivybridge' or
256                self._gpu_type == 'sandybridge'):
257            tries = 0
258            found = False
259            param_path = self.get_valid_path(FBC_PATHS)
260            if not param_path:
261                return 'FBC_PATHS not found.'
262            while not found and tries < 20:
263                time.sleep(1)
264                with open(param_path, 'r') as fbc_info_file:
265                    for line in fbc_info_file:
266                        if re.search('FBC enabled', line):
267                            found = True
268                            break
269
270                tries += 1
271
272            if not found:
273                logging.error('Error: did not see FBC enabled.')
274                return 'Did not see FBC enabled. '
275
276        return ''
277
278    def verify_graphics_psr(self):
279        """ On systems which support PSR, check that we can get into PSR;
280        idle before doing so, and retry every second for 20 seconds."""
281        logging.info('Running verify_graphics_psr')
282
283        board = utils.get_board()
284        if board != 'samus':
285            return ''
286        tries = 0
287        found = False
288        param_path = self.get_valid_path(PSR_PATHS)
289        if not param_path:
290            return 'PSR_PATHS not found.'
291        while not found and tries < 20:
292            time.sleep(1)
293            with open(param_path, 'r') as fbc_info_file:
294                for line in fbc_info_file:
295                    match = re.search(r'Performance_Counter: (.*)', line)
296                    if match and int(match.group(1)) > 0:
297                        found = True
298                        break
299
300            tries += 1
301
302        if not found:
303            logging.error('Error: did not see PSR enabled.')
304            return 'Did not see PSR enabled. '
305
306        return ''
307
308    def verify_graphics_gem_idle(self):
309        """ On systems which have i915, check that we can get all gem objects
310        to become idle (i.e. the i915_gem_active list need to go to 0);
311        idle before doing so, and retry every second for 20 seconds."""
312        logging.info('Running verify_graphics_gem_idle')
313        if (utils.get_cpu_soc_family() == 'x86_64' and
314                self._gpu_type != 'pinetrail'):
315            tries = 0
316            found = False
317            gem_path = self.get_valid_path(GEM_PATHS)
318            if not gem_path:
319                return 'GEM_PATHS not found.'
320            while not found and tries < 240:
321                time.sleep(0.25)
322                with open(gem_path, 'r') as gem_file:
323                    for line in gem_file:
324                        if re.search('Total 0 objects', line):
325                            found = True
326                            break
327
328                tries += 1
329
330            if not found:
331                utils.log_process_activity()
332                logging.error('Error: did not reach 0 gem actives.')
333                return 'Did not reach 0 gem actives. '
334
335        return ''
336