Instead of only checking that the CRC value is constant, compute the expected CRC value, and compare it to the hardware-generated CRC. Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx> --- README | 1 + tests/kms-test-crc.py | 103 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/README b/README index 7f770d4170a3..dcd34611d820 100644 --- a/README +++ b/README @@ -33,6 +33,7 @@ The tests scripts require the following dependencies to be installed on the target. * Python 3 +* The Python module 'crcmod' * kmsxx Python bindings (https://github.com/tomba/kmsxx.git) kmsxx hasn't released any stable version yet, it is recommended to use the diff --git a/tests/kms-test-crc.py b/tests/kms-test-crc.py index 8876c88506c1..60187eff1c48 100755 --- a/tests/kms-test-crc.py +++ b/tests/kms-test-crc.py @@ -3,8 +3,10 @@ # SPDX-FileCopyrightText: 2017-2019 Renesas Electronics Corporation import copy +import crcmod import kmstest import pykms +import tempfile class Composer(object): @@ -17,15 +19,31 @@ class Composer(object): START_POINT = kmstest.Point(0, 0) INCREMENT = kmstest.Dist(50, 50) - def __init__(self, crtc, planes, fb): + # The CRC initial value is XORed with the final XOR value, so we need to + # pass 0 to get the desired 0xffffffff initial value. + CRC = crcmod.Crc(0x104c11db7, 0x00000000, True, 0xffffffff) + + def __init__(self, mode, crtc, planes, fb): + self.__screen_size = kmstest.Size(mode.hdisplay, mode.vdisplay) self.__fb_size = kmstest.Size(fb.width, fb.height) self.__planes_positions = {} + self.__crcs = {} + + # Dump the contents of the frame buffer to compute the CRCs. + file = tempfile.TemporaryFile() + pykms.dump_framebuffer(fb, file.fileno()) + file.seek(0) + image = file.read() + file.close() position = copy.copy(Composer.START_POINT) for plane in planes: self.__planes_positions[plane] = copy.copy(position) + self.__crcs[plane] = self.__plane_crc(plane, image) position.move(Composer.INCREMENT) + self.__crcs[crtc] = self.__crtc_crc(planes, image) + def source(self, plane): pos = self.__planes_positions[plane] return kmstest.Rect(0, 0, @@ -39,6 +57,50 @@ class Composer(object): max(0, self.__fb_size.height - pos.y)) + def crc(self, obj): + return self.__crcs[obj] + + def __plane_crc(self, plane, image): + # Compute the reference CRC for a plane. Only the visible part of the + # plane is used by the hardware. + + crcg = Composer.CRC.new() + + src = self.source(plane) + if src.isEmpty(): + return 0 + + for y in range(src.height): + src_offset = ((src.top + y) * self.__fb_size.width + src.left) * 4 + crcg.update(image[src_offset:src_offset+src.width*4]) + + return crcg.crcValue + + def __crtc_crc(self, planes, image): + # Compute the reference CRC for the composed display output. Start with + # a black background and compose the planes. + + crcg = Composer.CRC.new() + + # The background is set to black, with and alpha value of 255 (opaque) + # to match the BRx configuration. + output = [0, 0, 0, 255] * (self.__screen_size.width * self.__screen_size.height) + + # Compose planes on top. + for plane in planes: + src = self.source(plane) + dst = self.destination(plane) + + for y in range(src.height): + src_offset = ((src.top + y) * self.__fb_size.width + src.left) * 4 + dst_offset = ((dst.top + y) * self.__screen_size.width + dst.left) * 4 + output[dst_offset:dst_offset+src.width*4] = image[src_offset:src_offset+src.width*4] + + crcg.update(bytes(output)) + + return crcg.crcValue + + class CRCTest(kmstest.KMSTest): """Test CRC calculation on pipeline output.""" @@ -86,8 +148,8 @@ class CRCTest(kmstest.KMSTest): fb = pykms.DumbFramebuffer(self.card, mode.hdisplay, mode.vdisplay, "XR24") pykms.draw_test_pattern(fb) - # Create a composer. - composer = Composer(crtc, planes, fb) + # Create a composer. This will compute the reference CRCs. + composer = Composer(mode, crtc, planes, fb) # Set the mode and add all planes ret = self.atomic_crtc_mode_set(crtc, connector, mode, sync=True) @@ -124,21 +186,40 @@ class CRCTest(kmstest.KMSTest): self.fail("No page flip registered") continue - sources = ["auto"] + ["plane%u" % plane.id for plane in planes] - for src in sources: - self.logger.log("Computing CRC in source %s" % src) + sources = [crtc] + planes + for source in sources: + if source == crtc: + crc_source = "auto" + else: + crc_source = "plane%u" % source.id + self.logger.log("Computing CRC from source %s" % crc_source) + + # Set the CRC source and acquire 10 CRC values. Discard the + # first value, as the device is running and the new source + # needs one frame to take effect. crc_reader = kmstest.CRCReader(crtc) - crc_reader.start(src) + crc_reader.start(crc_source) crcs = crc_reader.read(10) crc_reader.stop() - crcs = [c.crcs[0] for c in crcs] - if len(set(crcs)) != 1: - self.fail("CRC values not constant on source %s" % src) + crcs = [c.crcs[0] for c in crcs[1:]] + self.logger.log("CRC value[0] 0x%08x" % crcs[0]) + + failures = 0 + ref_crc = composer.crc(source) + + for i in range(len(crcs)): + crc = crcs[i] + if crc != ref_crc: + self.logger.log("CRC value[%u] 0x%08x does not match reference 0x%08x" + % (i, crc, ref_crc)) + failures += 1 + + if failures: + self.fail("Incorrect CRC values on source %s" % crc_source) break - self.logger.log("CRC value 0x%08x" % crcs[0]) else: self.success() -- Regards, Laurent Pinchart