All of lore.kernel.org
 help / color / mirror / Atom feed
* [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test
@ 2020-08-07 23:21 Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 1/6] kmstest: Extend Rect class Laurent Pinchart
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

Hello,

This patch series improves the test of the CRC calculation performed by
the DISCOM.

For DU channels backed by a VSP, the VSP can calculate the CRC of frames
using the DISCOM hardware block. The frame on which the CRC is computed
can be an input to the VSP (a DRM plane) or the output of the VSP (the
DU channel output). This is useful to verify that the screen, or a
portion of it, contains the expected data.

The test suite already contains a CRC test case, but it only verifies
that consecutive CRC values without any page flip do not vary. This
series extends the test to verify the actual CRC value.

Patches 1/6 and 2/6 improve the geometry helper classes in kmstest.
Patches 3/6 and then ensures that the CRC test leaves all CRTC disabled
even when it fails, to avoid cross-interactions between tests.

Patch 4/6 introduces a Composer class in the CRC test, to group all code
related to composition of the display. This is a small refactoring that
prepares for patch 5/6 that computes reference CRC values in the
Composer class, and compares them with the values computed by the
hardware.

The last patch, 6/6, adds a C utility to compute the CRC value of a
frame. This is useful when using the DISCOM to calculate the expected
CRC values, either during development or at runtime. The CRC calculation
code is generated with the Python crcmod module, using it only requires
a single line of code to compute a CRC on a data buffer:

	crc = calculate_crc(image, size, 0);

Note that the CRC test requires extensions to kmsxx that haven't been
merged upstream yet (one of them has actually not even been posted). The
kmsxx patches can be found at

	git://git.ideasonboard.com/renesas/kmsxx.git

I will work on upstreaming them.

Laurent Pinchart (6):
  kmstest: Extend Rect class
  kmstest: Add additional geometry classes
  tests: crc: Disable CRTC regarless of test status
  tests: crc: Introduce Composer class
  tests: crc: Compute reference CRC values and compare them
  crc: Add a utility to compute the CRC of a frame

 Makefile              |   2 +-
 README                |   1 +
 crc/Makefile          |  32 +++++
 crc/gen-crc.py        |  14 +++
 crc/main.c            | 271 ++++++++++++++++++++++++++++++++++++++++++
 tests/kms-test-crc.py | 145 +++++++++++++++++++---
 tests/kmstest.py      |  38 ++++++
 7 files changed, 488 insertions(+), 15 deletions(-)
 create mode 100644 crc/Makefile
 create mode 100755 crc/gen-crc.py
 create mode 100644 crc/main.c

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [kms-tests] [PATCH 1/6] kmstest: Extend Rect class
  2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
@ 2020-08-07 23:21 ` Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 2/6] kmstest: Add additional geometry classes Laurent Pinchart
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

Add a helper isEmpty() method to the Rect class, as well as a __repr__()
method to pretty-print the rectangle.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 tests/kmstest.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/tests/kmstest.py b/tests/kmstest.py
index f13e3fb7aa32..0281c6727271 100755
--- a/tests/kmstest.py
+++ b/tests/kmstest.py
@@ -212,6 +212,13 @@ class Rect(object):
         self.width = width
         self.height = height
 
+    def __repr__(self):
+        return "(%d,%d)/%ux%u" % (self.left, self.top, self.width, self.height)
+
+    def isEmpty(self):
+        """Check if the rectangle has a zero width or height"""
+        return self.width == 0 or self.height == 0
+
 
 class AtomicRequest(pykms.AtomicReq):
     """pymkms.AtomicReq wrapper to track state changes"""
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kms-tests] [PATCH 2/6] kmstest: Add additional geometry classes
  2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 1/6] kmstest: Extend Rect class Laurent Pinchart
@ 2020-08-07 23:21 ` Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 3/6] tests: crc: Disable CRTC regarless of test status Laurent Pinchart
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

Add Dist, Point and Size classes in addition to the Rect class.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 tests/kmstest.py | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/tests/kmstest.py b/tests/kmstest.py
index 0281c6727271..949bb20b8b1a 100755
--- a/tests/kmstest.py
+++ b/tests/kmstest.py
@@ -205,6 +205,37 @@ class CRCReader(object):
         return crcs
 
 
+class Dist(object):
+    def __init__(self, x, y):
+        self.x = x
+        self.y = y
+
+    def __repr__(self):
+        return "(%d,%d)" % (self.x, self.y)
+
+
+class Point(object):
+    def __init__(self, x, y):
+        self.x = x
+        self.y = y
+
+    def __repr__(self):
+        return "(%d,%d)" % (self.x, self.y)
+
+    def move(self, distance):
+        self.x += distance.x
+        self.y += distance.y
+
+
+class Size(object):
+    def __init__(self, width, height):
+        self.width = width
+        self.height = height
+
+    def __repr__(self):
+        return "%ux%u" % (self.width, self.height)
+
+
 class Rect(object):
     def __init__(self, left, top, width, height):
         self.left = left
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kms-tests] [PATCH 3/6] tests: crc: Disable CRTC regarless of test status
  2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 1/6] kmstest: Extend Rect class Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 2/6] kmstest: Add additional geometry classes Laurent Pinchart
@ 2020-08-07 23:21 ` Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 4/6] tests: crc: Introduce Composer class Laurent Pinchart
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

The CRTC should be disabled when the test completes, regardless of
whether it succeeds or fails.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 tests/kms-test-crc.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/kms-test-crc.py b/tests/kms-test-crc.py
index dcdfcd4abb71..e3e31b34e2d4 100755
--- a/tests/kms-test-crc.py
+++ b/tests/kms-test-crc.py
@@ -105,7 +105,8 @@ class CRCTest(kmstest.KMSTest):
 
                 self.logger.log("CRC value 0x%08x" % crcs[0])
             else:
-                self.atomic_crtc_disable(crtc)
                 self.success()
 
+            self.atomic_crtc_disable(crtc)
+
 CRCTest().execute()
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kms-tests] [PATCH 4/6] tests: crc: Introduce Composer class
  2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
                   ` (2 preceding siblings ...)
  2020-08-07 23:21 ` [kms-tests] [PATCH 3/6] tests: crc: Disable CRTC regarless of test status Laurent Pinchart
@ 2020-08-07 23:21 ` Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 5/6] tests: crc: Compute reference CRC values and compare them Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 6/6] crc: Add a utility to compute the CRC of a frame Laurent Pinchart
  5 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

The Composer gathers the computation of planes position. It is currently
only used to configure the planes.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 tests/kms-test-crc.py | 45 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/tests/kms-test-crc.py b/tests/kms-test-crc.py
index e3e31b34e2d4..8876c88506c1 100755
--- a/tests/kms-test-crc.py
+++ b/tests/kms-test-crc.py
@@ -2,9 +2,43 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 # SPDX-FileCopyrightText: 2017-2019 Renesas Electronics Corporation
 
+import copy
 import kmstest
 import pykms
 
+
+class Composer(object):
+    # Manage the composition of planes on the screen by computing the source
+    # and destination rectangles of each plane.
+    #
+    # Stack the plane, starting at START_POINT, with a fixed INCREMENT.
+    #
+
+    START_POINT = kmstest.Point(0, 0)
+    INCREMENT = kmstest.Dist(50, 50)
+
+    def __init__(self, crtc, planes, fb):
+        self.__fb_size = kmstest.Size(fb.width, fb.height)
+        self.__planes_positions = {}
+
+        position = copy.copy(Composer.START_POINT)
+        for plane in planes:
+            self.__planes_positions[plane] = copy.copy(position)
+            position.move(Composer.INCREMENT)
+
+    def source(self, plane):
+        pos = self.__planes_positions[plane]
+        return kmstest.Rect(0, 0,
+                            max(0, self.__fb_size.width - pos.x),
+                            max(0, self.__fb_size.height - pos.y))
+
+    def destination(self, plane):
+        pos = self.__planes_positions[plane]
+        return kmstest.Rect(pos.x, pos.y,
+                            max(0, self.__fb_size.width - pos.x),
+                            max(0, self.__fb_size.height - pos.y))
+
+
 class CRCTest(kmstest.KMSTest):
     """Test CRC calculation on pipeline output."""
 
@@ -48,10 +82,13 @@ class CRCTest(kmstest.KMSTest):
             self.logger.log("Testing connector %s, CRTC %u, mode %s with %u planes" % \
                   (connector.fullname, crtc.id, mode.name, len(planes)))
 
-            # Create a frame buffer
+            # Create a frame buffer and draw a test pattern.
             fb = pykms.DumbFramebuffer(self.card, mode.hdisplay, mode.vdisplay, "XR24")
             pykms.draw_test_pattern(fb)
 
+            # Create a composer.
+            composer = Composer(crtc, planes, fb)
+
             # Set the mode and add all planes
             ret = self.atomic_crtc_mode_set(crtc, connector, mode, sync=True)
             if ret < 0:
@@ -60,10 +97,9 @@ class CRCTest(kmstest.KMSTest):
 
             req = kmstest.AtomicRequest(self)
 
-            offset = 100
             for plane in planes:
-                source = kmstest.Rect(0, 0, fb.width, fb.height)
-                destination = kmstest.Rect(offset, offset, fb.width, fb.height)
+                source = composer.source(plane)
+                destination = composer.destination(plane)
                 req.add(plane, {
                         'FB_ID': fb.id,
                         'CRTC_ID': crtc.id,
@@ -76,7 +112,6 @@ class CRCTest(kmstest.KMSTest):
                         'CRTC_W': destination.width,
                         'CRTC_H': destination.height,
                 })
-                offset += 50
 
             ret = req.commit(0)
             if ret < 0:
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kms-tests] [PATCH 5/6] tests: crc: Compute reference CRC values and compare them
  2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
                   ` (3 preceding siblings ...)
  2020-08-07 23:21 ` [kms-tests] [PATCH 4/6] tests: crc: Introduce Composer class Laurent Pinchart
@ 2020-08-07 23:21 ` Laurent Pinchart
  2020-08-07 23:21 ` [kms-tests] [PATCH 6/6] crc: Add a utility to compute the CRC of a frame Laurent Pinchart
  5 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

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@ideasonboard.com>
---
 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kms-tests] [PATCH 6/6] crc: Add a utility to compute the CRC of a frame
  2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
                   ` (4 preceding siblings ...)
  2020-08-07 23:21 ` [kms-tests] [PATCH 5/6] tests: crc: Compute reference CRC values and compare them Laurent Pinchart
@ 2020-08-07 23:21 ` Laurent Pinchart
  5 siblings, 0 replies; 7+ messages in thread
From: Laurent Pinchart @ 2020-08-07 23:21 UTC (permalink / raw)
  To: linux-renesas-soc; +Cc: Kieran Bingham, Kuninori Morimoto

The discom-crc utility computes the CRC of a frame using the same
algorithm as the DISCOM hardware block. This is useful to precompute CRC
values and then compare them with the hardware-generated values.

The utility computes the CRC on data stored in a file passed as a
command line argument. It supports two optional arguments, --crop and
--size, to specify the crop rectangle and the image size. The size only
needs to be specified if a crop rectangle is set, it is deduced from the
file size otherwise.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 Makefile       |   2 +-
 crc/Makefile   |  32 ++++++
 crc/gen-crc.py |  14 +++
 crc/main.c     | 271 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 318 insertions(+), 1 deletion(-)
 create mode 100644 crc/Makefile
 create mode 100755 crc/gen-crc.py
 create mode 100644 crc/main.c

diff --git a/Makefile b/Makefile
index 1f0da15546b8..e9c0edb785e7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: CC0-1.0
 
-SUBDIRS=tests
+SUBDIRS=crc tests
 
 recursive=all clean install
 
diff --git a/crc/Makefile b/crc/Makefile
new file mode 100644
index 000000000000..0922163da31b
--- /dev/null
+++ b/crc/Makefile
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: CC0-1.0
+
+CROSS_COMPILE ?=
+
+CC	:= $(CROSS_COMPILE)gcc
+CFLAGS	?= -O2 -W -Wall -Wno-unused-parameter -Iinclude
+LDFLAGS	?=
+LIBS	:=
+
+OUTPUT	:= discom-crc
+OBJECTS	:= main.o
+
+%.o : %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+all: $(OUTPUT)
+
+$(OUTPUT): $(OBJECTS)
+	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+crc.c : gen-crc.py
+	./$< $@
+
+main.o : crc.c
+
+clean:
+	-rm -f *.o
+	-rm -f crc.c
+	-rm -f $(OUTPUT)
+
+install:
+	cp $(OUTPUT) $(INSTALL_DIR)/
diff --git a/crc/gen-crc.py b/crc/gen-crc.py
new file mode 100755
index 000000000000..10c5776d071d
--- /dev/null
+++ b/crc/gen-crc.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2020 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+import crcmod
+import sys
+
+# The initial value is ignored, it must instead be passed to the generated C
+# function.
+crc = crcmod.Crc(0x104c11db7, 0xffffffff, True, 0xffffffff)
+
+f = open(sys.argv[1], 'w')
+crc.generateCode('calculate_crc', f, 'uint8_t', 'uint32_t')
+f.close()
diff --git a/crc/main.c b/crc/main.c
new file mode 100644
index 000000000000..b63421902f18
--- /dev/null
+++ b/crc/main.c
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2020 Laurent Pinchart <laurent.pinchart@ideasonboard.com> */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "crc.c"
+
+struct image_rect {
+	int left;
+	int top;
+	unsigned int width;
+	unsigned int height;
+};
+
+struct image_size {
+	unsigned int width;
+	unsigned int height;
+};
+
+struct options {
+	const char *filename;
+	struct image_rect crop;
+	struct image_size size;
+};
+
+/* -----------------------------------------------------------------------------
+ * Miscellaneous helpers
+ */
+
+static bool rect_is_empty(const struct image_rect *rect)
+{
+	return !rect->width || !rect->height;
+}
+
+static bool rect_is_null(const struct image_rect *rect)
+{
+	return !rect->left && !rect->top && !rect->width && !rect->height;
+}
+
+static bool size_is_null(const struct image_size *size)
+{
+	return !size->width && !size->height;
+}
+
+static int readall(int fd, void *buf, size_t count)
+{
+	int ret;
+
+	do {
+		ret = read(fd, buf, count);
+		if (ret == -1)
+			return -errno;
+		if (ret == 0)
+			return -ENODATA;
+
+		count -= ret;
+		buf += ret;
+	} while (count);
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Usage and argument parsing
+ */
+
+static void usage(const char *argv0)
+{
+	printf("Usage: %s [options] <infile>\n\n", argv0);
+	printf("Calculate the R-Car DISCOM CRC for the image stored in <infile>\n\n");
+	printf("Supported options:\n");
+	printf("-c, --crop (X,Y)/WxH		Crop the input image (needs --size)\n");
+	printf("-s, --size WxH			Input image size\n");
+}
+
+static struct option opts[] = {
+	{"crop", 1, 0, 'c'},
+	{"size", 1, 0, 's'},
+	{0, 0, 0, 0}
+};
+
+static int parse_args(struct options *options, int argc, char *argv[])
+{
+	int ret;
+	int c;
+
+	if (argc < 2) {
+		usage(argv[0]);
+		return 1;
+	}
+
+	memset(options, 0, sizeof(*options));
+
+	opterr = 0;
+	while ((c = getopt_long(argc, argv, "c:s:", opts, NULL)) != -1) {
+		int count;
+
+		switch (c) {
+		case 'c':
+			ret = sscanf(optarg, "(%d,%d)/%ux%u%n",
+				     &options->crop.left, &options->crop.top,
+				     &options->crop.width, &options->crop.height,
+				     &count);
+			if (ret != 4 || count != (int)strlen(optarg)) {
+				printf("Invalid crop value '%s'\n", optarg);
+				return 1;
+			}
+			break;
+
+		case 's':
+			ret = sscanf(optarg, "%ux%u%n",
+				     &options->size.width, &options->size.height,
+				     &count);
+			if (ret != 2 || count != (int)strlen(optarg)) {
+				printf("Invalid size value '%s'\n", optarg);
+				return 1;
+			}
+			break;
+
+		default:
+			printf("Invalid option -%c\n", c);
+			printf("Run %s -h for help.\n", argv[0]);
+			return 1;
+		}
+	}
+
+	if (optind != argc - 1) {
+		usage(argv[0]);
+		return 1;
+	}
+
+	options->filename = argv[optind];
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Main
+ */
+
+int main(int argc, char *argv[])
+{
+	struct options options;
+	void *image = NULL;
+	uint32_t crc;
+	off_t offset;
+	off_t size;
+	int fd = -1;
+	int ret;
+
+	/* Parse and validate options. */
+	ret = parse_args(&options, argc, argv);
+	if (ret)
+		return ret;
+
+	if (!rect_is_null(&options.crop)) {
+		if (size_is_null(&options.size)) {
+			printf("--crop requires --size\n");
+			goto error;
+		}
+
+		if (rect_is_empty(&options.crop)) {
+			printf("Crop rectangle is empty\n");
+			goto error;
+		}
+
+		if (options.crop.left < 0 || options.crop.top < 0 ||
+		    options.crop.left + options.crop.width > options.size.width ||
+		    options.crop.top + options.crop.height > options.size.height) {
+			printf("Crop rectangle out of image bounds\n");
+			goto error;
+		}
+	}
+
+	/* Open the file and determine its size. */
+	fd = open(options.filename, O_RDONLY);
+	if (fd == -1) {
+		printf("Failed to open '%s': %s\n", options.filename,
+		       strerror(errno));
+		goto error;
+	}
+
+	size = lseek(fd, 0, SEEK_END);
+	if (size == -1) {
+		printf("Failed to determine file size: %s\n", strerror(errno));
+		goto error;
+	}
+
+	if (!size_is_null(&options.size) &&
+	    options.size.width * options.size.height * 4 != size) {
+		printf("Image size %ux%u doesn't match file size %jd\n",
+		       options.size.width, options.size.height, (intmax_t)size);
+		goto error;
+	}
+
+	/* Read the image data. */
+	if (!rect_is_null(&options.crop))
+		size = options.crop.width * options.crop.height * 4;
+
+	image = malloc(size);
+	if (!image) {
+		printf("Unable to allocate memory for image data\n");
+		goto error;
+	}
+
+	offset = (options.crop.top * options.size.width + options.crop.left) * 4;
+	lseek(fd, offset, SEEK_SET);
+
+	if (!options.crop.width || options.crop.width == options.size.width) {
+		/*
+		 * When the crop rectangle width spans the whole image, read it
+		 * in one go.
+		 */
+		ret = readall(fd, image, size);
+		if (ret < 0) {
+			printf("Unable to read image: %s\n", strerror(errno));
+			goto error;
+		}
+	} else {
+		/* Otherwise, read line by line. */
+		void *line = image;
+		unsigned int y;
+
+		offset = (options.size.width - options.crop.width) * 4;
+
+		for (y = 0; y < options.crop.height; ++y) {
+			ret = readall(fd, line, options.crop.width * 4);
+			if (ret < 0) {
+				printf("Unable to read line %u: %s\n", y,
+				       strerror(errno));
+				goto error;
+			}
+
+			lseek(fd, offset, SEEK_CUR);
+			line += options.crop.width * 4;
+		}
+	}
+
+	close(fd);
+	fd = -1;
+
+	/*
+	 * Compute the CRC. The generate CRC code XORs the initial value with
+	 * the final XOR value, so we need to pass 0 to get the desired
+	 * 0xffffffff initial value.
+	 */
+	crc = calculate_crc(image, size, 0);
+
+	free(image);
+	image = NULL;
+
+	printf("0x%08x\n", crc);
+
+	return 0;
+
+error:
+	free(image);
+	if (fd != -1)
+		close(fd);
+	return 1;
+}
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-08-07 23:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-07 23:21 [kms-tests] [PATCH 0/7] Improve CRC (DISCOM) test Laurent Pinchart
2020-08-07 23:21 ` [kms-tests] [PATCH 1/6] kmstest: Extend Rect class Laurent Pinchart
2020-08-07 23:21 ` [kms-tests] [PATCH 2/6] kmstest: Add additional geometry classes Laurent Pinchart
2020-08-07 23:21 ` [kms-tests] [PATCH 3/6] tests: crc: Disable CRTC regarless of test status Laurent Pinchart
2020-08-07 23:21 ` [kms-tests] [PATCH 4/6] tests: crc: Introduce Composer class Laurent Pinchart
2020-08-07 23:21 ` [kms-tests] [PATCH 5/6] tests: crc: Compute reference CRC values and compare them Laurent Pinchart
2020-08-07 23:21 ` [kms-tests] [PATCH 6/6] crc: Add a utility to compute the CRC of a frame Laurent Pinchart

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.