summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stone <daniels@collabora.com>2016-02-24 17:40:47 (GMT)
committerDaniel Stone <daniels@collabora.com>2016-12-16 15:39:40 (GMT)
commit0c04dcb7ce366028675014285b391b181c9f11eb (patch)
tree94e154fa1af0634ddb4e69ab5387f41f69cd9f04
parent21de9c28ef85238db072bb53cc54495f2bba4e38 (diff)
downloadtest-definitions-master.tar.gz
test-definitions-master.tar.xz
Add graphics testingHEADmaster
Both with and without Chamelium. Signed-off-by: Daniel Stone <daniels@collabora.com>
-rw-r--r--kernel/gfx/gfx-tests-basic.json51
-rw-r--r--kernel/gfx/gfx-tests-basic.yaml29
-rw-r--r--kernel/gfx/gfx-tests-chamelium-mux.yaml20
-rw-r--r--kernel/gfx/gfx-tests-chamelium.json94
-rw-r--r--kernel/gfx/gfx-tests-chamelium.yaml29
-rw-r--r--kernel/gfx/scripts/dell-u2415h-1080p.edidbin0 -> 256 bytes
-rwxr-xr-xkernel/gfx/scripts/libdrm.py334
-rw-r--r--kernel/gfx/scripts/libdrm.pycbin0 -> 9233 bytes
-rwxr-xr-xkernel/gfx/scripts/run-kms-test.py479
9 files changed, 1036 insertions, 0 deletions
diff --git a/kernel/gfx/gfx-tests-basic.json b/kernel/gfx/gfx-tests-basic.json
new file mode 100644
index 0000000..4d6d36c
--- /dev/null
+++ b/kernel/gfx/gfx-tests-basic.json
@@ -0,0 +1,51 @@
+{
+ "actions": [
+ {
+ "command": "deploy_kernel",
+ "parameters": {
+ "dtb": "{{kernbase}}/{{fdtfile}}.dtb",
+ "kernel": "{{kernbase}}/zImage",
+ "login_commands": [
+ "sudo su"
+ ],
+ "login_prompt": "login:",
+ "nfsrootfs": "http://images.collabora.co.uk/lava/ceti/ospack-{{arch}}-core.tar.gz",
+ "password": "collabora",
+ "password_prompt": "Password:",
+ "target_type": "ubuntu",
+ "username": "collabora"
+ }
+ },
+ {
+ "command": "boot_image",
+ "parameters": {
+ "test_image_prompt": "root@singularity"
+ }
+ },
+ {
+ "command": "lava_test_shell",
+ "parameters": {
+ "testdef_repos": [
+ {
+ "git-repo": "git://git.collabora.co.uk/git/user/daniels/test-definitions.git",
+ "testdef": "kernel/gfx/gfx-tests-basic.yaml",
+ "parameters": {
+ "lava_device_type": "{{lava_device_type}}"
+ }
+ }
+ ],
+ "timeout": 1800
+ }
+ },
+ {
+ "command": "submit_results",
+ "parameters": {
+ "server": "https://lava.collabora.co.uk/RPC2/",
+ "stream": "/public/personal/daniels/"
+ }
+ }
+ ],
+ "device_type": "{{lava_device_type}}",
+ "job_name": "gfx-tests-{{lava_device_type}}",
+ "timeout": 2400
+}
diff --git a/kernel/gfx/gfx-tests-basic.yaml b/kernel/gfx/gfx-tests-basic.yaml
new file mode 100644
index 0000000..50f2922
--- /dev/null
+++ b/kernel/gfx/gfx-tests-basic.yaml
@@ -0,0 +1,29 @@
+metadata:
+ name: gfx-tests-kms
+ format: "Lava-Test-Shell Test Definition 1.0"
+ description: "Test basic Kernel Mode Setting functionality"
+ maintainer:
+ - daniels@collabora.com
+ scope:
+ - functional
+ devices:
+ - snow
+ - peach-pit
+ - peach-pi
+ - tegra124-nyan-big
+ - tegra124-nyan-blaze
+ environment:
+ - lava-test-shell
+
+install:
+ deps:
+ - python2.7
+ - python-cffi
+ - python-pyudev
+
+run:
+ steps:
+ - ./kernel/gfx/scripts/run-kms-test.py "$lava_device_type"
+
+parse:
+ pattern: '(?P<test_case_id>\S*):\s+(?P<result>(pass|fail))'
diff --git a/kernel/gfx/gfx-tests-chamelium-mux.yaml b/kernel/gfx/gfx-tests-chamelium-mux.yaml
new file mode 100644
index 0000000..c3276a2
--- /dev/null
+++ b/kernel/gfx/gfx-tests-chamelium-mux.yaml
@@ -0,0 +1,20 @@
+metadata:
+ name: gfx-tests-chamelium-mux
+ format: "Lava-Test-Shell Test Definition 1.0"
+ description: "Test basic Kernel Mode Setting functionality"
+ maintainer:
+ - daniels@collabora.com
+ scope:
+ - functional
+ devices:
+ - chamelium
+ environment:
+ - lava-test-shell
+
+run:
+ steps:
+ - echo -n $(lava-group dut) $(lava-group chamelium) | nc $MUX_HOST $MUX_PORT
+ - lava-wait client-done
+
+parse:
+ pattern: '(?P<test_case_id>\S*):\s+(?P<result>(pass|fail))'
diff --git a/kernel/gfx/gfx-tests-chamelium.json b/kernel/gfx/gfx-tests-chamelium.json
new file mode 100644
index 0000000..22722fd
--- /dev/null
+++ b/kernel/gfx/gfx-tests-chamelium.json
@@ -0,0 +1,94 @@
+{
+ "device_group": [
+ {
+ "role": "chamelium",
+ "count": 1,
+ "device_type": "chamelium"
+ },
+ {
+ "role": "dut",
+ "count": 1,
+ "device_type": "{{lava_device_type}}",
+ "tags": [
+ "chamelium-hdmi"
+ ]
+ }
+ ],
+ "actions": [
+ {
+ "command": "dummy_deploy",
+ "parameters": {
+ "role": "chamelium",
+ "target_type": "oe"
+ }
+ },
+ {
+ "command": "lava_test_shell",
+ "parameters": {
+ "role": "chamelium",
+ "testdef_repos": [
+ {
+ "git-repo": "git://git.collabora.co.uk/git/user/daniels/test-definitions.git",
+ "testdef": "kernel/gfx/gfx-tests-chamelium-mux.yaml",
+ "parameters": {
+ "MUX_HOST": "hdmi-mux-cbg-0",
+ "MUX_PORT": "3199"
+ }
+ }
+ ],
+ "timeout": 180
+ }
+ },
+ {
+ "command": "deploy_kernel",
+ "parameters": {
+ "role": "dut",
+ "kernel": "{{kernbase}}/zImage",
+ "dtb": "{{kernbase}}/{{fdtfile}}.dtb",
+ "nfsrootfs": "http://images.collabora.co.uk/lava/ceti/ospack-{{arch}}-core.tar.gz",
+ "login_commands": [
+ "sudo su",
+ "systemctl --failed"
+ ],
+ "login_prompt": "login:",
+ "username": "collabora",
+ "password_prompt": "Password:",
+ "password": "collabora",
+ "target_type": "ubuntu"
+ }
+ },
+ {
+ "command": "boot_image",
+ "parameters": {
+ "role": "dut",
+ "test_image_prompt": "root@singularity"
+ }
+ },
+ {
+ "command": "lava_test_shell",
+ "parameters": {
+ "role": "dut",
+ "testdef_repos": [
+ {
+ "git-repo": "git://git.collabora.co.uk/git/user/daniels/test-definitions.git",
+ "testdef": "kernel/gfx/gfx-tests-chamelium.yaml",
+ "parameters": {
+ "device_type": "{{lava_device_type}}",
+ "CHAMELIUM_PORT": "HDMI-A-1:HDMI"
+ }
+ }
+ ],
+ "timeout": 6000
+ }
+ },
+ {
+ "command": "submit_results",
+ "parameters": {
+ "server": "https://lava.collabora.co.uk/RPC2/",
+ "stream": "{{bundle_stream}}"
+ }
+ }
+ ],
+ "job_name": "gfx-tests-chamelium-{{lava_device_type}}",
+ "timeout": 2400
+}
diff --git a/kernel/gfx/gfx-tests-chamelium.yaml b/kernel/gfx/gfx-tests-chamelium.yaml
new file mode 100644
index 0000000..fc53c8d
--- /dev/null
+++ b/kernel/gfx/gfx-tests-chamelium.yaml
@@ -0,0 +1,29 @@
+metadata:
+ name: gfx-tests-kms
+ format: "Lava-Test-Shell Test Definition 1.0"
+ description: "Test basic Kernel Mode Setting functionality"
+ maintainer:
+ - daniels@collabora.com
+ scope:
+ - functional
+ devices:
+ - peach-pi
+ - tegra124-nyan-big
+ - rk3288-rock2-square
+ - exynos5422-odroidxu3
+ environment:
+ - lava-test-shell
+
+install:
+ deps:
+ - python2.7
+ - python-cffi
+ - python-pyudev
+
+run:
+ steps:
+ - ./kernel/gfx/scripts/run-kms-test.py "$device_type" $(lava-group chamelium) "$CHAMELIUM_PORT"
+ - lava-send client-done
+
+parse:
+ pattern: '(?P<test_case_id>\S*):\s+(?P<result>(pass|fail))'
diff --git a/kernel/gfx/scripts/dell-u2415h-1080p.edid b/kernel/gfx/scripts/dell-u2415h-1080p.edid
new file mode 100644
index 0000000..11990ad
--- /dev/null
+++ b/kernel/gfx/scripts/dell-u2415h-1080p.edid
Binary files differ
diff --git a/kernel/gfx/scripts/libdrm.py b/kernel/gfx/scripts/libdrm.py
new file mode 100755
index 0000000..0cb86c3
--- /dev/null
+++ b/kernel/gfx/scripts/libdrm.py
@@ -0,0 +1,334 @@
+#!/usr/bin/python
+# -*- encoding: utf8 -*-
+#
+# libdrm.py: Interface to the C-based libdrm
+#
+# Copyright © 2006-2015 The ChromiumOS Authors
+# Copyright © 2015 Collabora, Ltd.
+#
+# Partly based on the ChromiumOS autotest client/cros/graphics/drm.py, which is
+# licensed under the GNU General Public License, version 2 or above.
+#
+# Some portability issues may occur on weird architectures: as Python ctypes
+# lacks explicitly-sized types, we rely on ushort representing a 16-bit
+# unsigned integer, uint representing a 32-bit integer, and ulong representing
+# a 64-bit integer.
+
+from ctypes import *
+import sys
+import os
+
+DRM_MODE_CONNECTED = 1
+DRM_MODE_DISCONNECTED = 2
+DRM_MODE_UNKNOWNCONNECTION = 3
+
+DRM_MODE_CONNECTOR_Unknown = 0
+DRM_MODE_CONNECTOR_VGA = 1
+DRM_MODE_CONNECTOR_DVII = 2
+DRM_MODE_CONNECTOR_DVID = 3
+DRM_MODE_CONNECTOR_DVIA = 4
+DRM_MODE_CONNECTOR_Composite = 5
+DRM_MODE_CONNECTOR_SVIDEO = 6
+DRM_MODE_CONNECTOR_LVDS = 7
+DRM_MODE_CONNECTOR_Component = 8
+DRM_MODE_CONNECTOR_9PinDIN = 9
+DRM_MODE_CONNECTOR_DisplayPort = 10
+DRM_MODE_CONNECTOR_HDMIA = 11
+DRM_MODE_CONNECTOR_HDMIB = 12
+DRM_MODE_CONNECTOR_TV = 13
+DRM_MODE_CONNECTOR_eDP = 14
+DRM_MODE_CONNECTOR_VIRTUAL = 15
+DRM_MODE_CONNECTOR_DSI = 16
+
+DRM_MODE_TYPE_BUILTIN = (1 << 0)
+DRM_MODE_TYPE_CLOCK_C = ((1 << 1) | DRM_MODE_TYPE_BUILTIN)
+DRM_MODE_TYPE_CRTC_C = ((1 << 2) | DRM_MODE_TYPE_BUILTIN)
+DRM_MODE_TYPE_PREFERRED = (1 << 3)
+DRM_MODE_TYPE_DEFAULT = (1 << 4)
+DRM_MODE_TYPE_USERDEF = (1 << 5)
+DRM_MODE_TYPE_DRIVER = (1 << 6)
+
+# Taken from <asm-generic/ioctl.h>, minus the huge admonishing comment telling
+# you to not do this.
+def make_ioc(base, num, direc, size):
+ return (direc << 30) | (size << 16) | (base << 8) | num
+
+DRM_IOC_BASE = ord('d')
+IOC_MODE_W = (1 << 0)
+IOC_MODE_R = (1 << 1)
+
+DRM_IOCTL_MODE_CREATE_DUMB = make_ioc(DRM_IOC_BASE, 0xB2,
+ IOC_MODE_R | IOC_MODE_W, 0x20)
+DRM_IOCTL_MODE_MAP_DUMB = make_ioc(DRM_IOC_BASE, 0xB3,
+ IOC_MODE_R | IOC_MODE_W, 0x10)
+
+class drmConnection:
+ def __init__(self, drm, fd):
+ self.fd = os.dup(fd)
+ self._l = drm
+
+ def get_version(self):
+ ret = self._l.drmModeGetVersion(self.fd).contents
+ ret._l = self._l
+ return ret
+
+ def get_resources(self):
+ ret = self._l.drmModeGetResources(self.fd).contents
+ ret._l = self._l
+ ret.fd = self.fd
+ return ret
+
+def connect(drm, fd):
+ return drmConnection(drm, fd)
+
+class drmVersionRec(Structure):
+ _fields_ = [
+ ("version_major", c_int),
+ ("version_minor", c_int),
+ ("version_patchlevel", c_int),
+ ("name_len", c_int),
+ ("name", POINTER(c_char)),
+ ("date_len", c_int),
+ ("date", POINTER(c_char)),
+ ("desc_len", c_int),
+ ("desc", POINTER(c_char))
+ ]
+
+ _l = None
+
+ def __del__(self):
+ self._l.drmModeFreeVersion(self)
+
+class drmModeResRec(Structure):
+ _fields_ = [
+ ("count_fbs", c_int),
+ ("fbs", POINTER(c_uint)),
+ ("count_crtcs", c_int),
+ ("crtcs", POINTER(c_uint)),
+ ("count_connectors", c_int),
+ ("connectors", POINTER(c_uint)),
+ ("count_encoders", c_int),
+ ("encoders", POINTER(c_uint)),
+ ("min_width", c_uint),
+ ("max_width", c_uint),
+ ("min_height", c_uint),
+ ("max_height", c_uint),
+ ]
+
+ _l = None
+ fd = -1
+
+ def __del__(self):
+ self._l.drmModeFreeResources(self)
+
+ def get_connector(self, connector_id):
+ ret = self._l.drmModeGetConnector(self.fd, connector_id).contents
+ ret._l = self._l
+ return ret
+
+ def get_encoder(self, encoder_id):
+ ret = self._l.drmModeGetEncoder(self.fd, encoder_id).contents
+ ret._l = self._l
+ return ret
+
+ def get_crtc(self, crtc_id):
+ ret = self._l.drmModeGetCrtc(self.fd, crtc_id).contents
+ ret._l = self._l
+ return ret
+
+class drmModeModeInfoRec(Structure):
+ _fields_ = [
+ ("clock", c_uint),
+ ("hdisplay", c_ushort),
+ ("hsync_start", c_ushort),
+ ("hsync_end", c_ushort),
+ ("htotal", c_ushort),
+ ("hskew", c_ushort),
+ ("vdisplay", c_ushort),
+ ("vsync_start", c_ushort),
+ ("vsync_end", c_ushort),
+ ("vtotal", c_ushort),
+ ("vscan", c_ushort),
+ ("vrefresh", c_uint),
+ ("flags", c_uint),
+ ("type", c_uint),
+ ("name", c_char * 32)
+ ]
+
+class drmModeConnectorRec(Structure):
+ _fields_ = [
+ ("connector_id", c_uint),
+ ("encoder_id", c_uint),
+ ("connector_type", c_uint),
+ ("connector_type_id", c_uint),
+ ("connection", c_int),
+ ("mmWidth", c_uint),
+ ("mmHeight", c_uint),
+ ("subpixel", c_int),
+ ("count_modes", c_int),
+ ("modes", POINTER(drmModeModeInfoRec)),
+ ("count_props", c_int),
+ ("props", POINTER(c_uint)),
+ ("prop_values", POINTER(c_ulong)),
+ ("count_encoders", c_int),
+ ("encoders", POINTER(c_uint))
+ ]
+
+ _l = None
+
+ def __del__(self):
+ self._l.drmModeFreeConnector(self)
+
+class drmModeCreateDumbIoc(Structure):
+ _fields_ = [
+ ("height", c_uint),
+ ("width", c_uint),
+ ("bpp", c_uint),
+ ("flags", c_uint),
+ ("handle", c_uint),
+ ("pitch", c_uint),
+ ("size", c_ulonglong)
+ ]
+
+class drmModeMapDumbIoc(Structure):
+ _fields_ = [
+ ("handle", c_uint),
+ ("pad", c_uint),
+ ("offset", c_ulonglong)
+ ]
+
+def encoder_type_to_name(val):
+ if val == DRM_MODE_ENCODER_NONE:
+ return "None"
+ if val == DRM_MODE_ENCODER_DAC:
+ return "DAC"
+ if val == DRM_MODE_ENCODER_TMDS:
+ return "TMDS"
+ if val == DRM_MODE_ENCODER_LVDS:
+ return "LVDS"
+ if val == DRM_MODE_ENCODER_TVDAC:
+ return "TVDAC"
+ if val == DRM_MODE_ENCODER_VIRTUAL:
+ return "Virtual"
+ if val == DRM_MODE_ENCODER_DSI:
+ return "DSI"
+
+class drmModeEncoderRec(Structure):
+ _fields_ = [
+ ("encoder_id", c_uint),
+ ("encoder_type", c_uint),
+ ("crtc_id", c_uint),
+ ("possible_crtcs", c_uint),
+ ("possible_clones", c_uint)
+ ]
+
+ _l = None
+
+ def __del__(self):
+ self._l.drmModeFreeEncoder(self)
+
+class drmModeCrtcRec(Structure):
+ _fields_ = [
+ ("crtc_id", c_uint),
+ ("buffer_id", c_uint),
+ ("x", c_uint),
+ ("y", c_uint),
+ ("width", c_uint),
+ ("height", c_uint),
+ ("mode_valid", c_int),
+ ("mode", drmModeModeInfoRec),
+ ("gamma_size", c_int)
+ ]
+
+ _l = None
+
+ def __del__(self):
+ self._l.drmModeFreeCrtc(self)
+
+def connector_type_to_name(val):
+ if val == DRM_MODE_CONNECTOR_Unknown:
+ return "Unknown"
+ if val == DRM_MODE_CONNECTOR_VGA:
+ return "VGA"
+ if val == DRM_MODE_CONNECTOR_DVII:
+ return "DVI-I"
+ if val == DRM_MODE_CONNECTOR_DVID:
+ return "DVI-D"
+ if val == DRM_MODE_CONNECTOR_DVIA:
+ return "DVI-A"
+ if val == DRM_MODE_CONNECTOR_Composite:
+ return "Composite"
+ if val == DRM_MODE_CONNECTOR_SVIDEO:
+ return "SVIDEO"
+ if val == DRM_MODE_CONNECTOR_LVDS:
+ return "LVDS"
+ if val == DRM_MODE_CONNECTOR_Component:
+ return "Component"
+ if val == DRM_MODE_CONNECTOR_9PinDIN:
+ return "9PinDIN"
+ if val == DRM_MODE_CONNECTOR_DisplayPort:
+ return "DP"
+ if val == DRM_MODE_CONNECTOR_HDMIA:
+ return "HDMI-A"
+ if val == DRM_MODE_CONNECTOR_HDMIB:
+ return "HDMI-B"
+ if val == DRM_MODE_CONNECTOR_TV:
+ return "TV"
+ if val == DRM_MODE_CONNECTOR_eDP:
+ return "eDP"
+ if val == DRM_MODE_CONNECTOR_VIRTUAL:
+ return "Virtual"
+ if val == DRM_MODE_CONNECTOR_DSI:
+ return "DSI"
+
+def connection_status_to_name(val):
+ if val == DRM_MODE_CONNECTED:
+ return "connected"
+ if val == DRM_MODE_DISCONNECTED:
+ return "disconnected"
+ if val == DRM_MODE_UNKNOWNCONNECTION:
+ return "unknown"
+
+def init():
+ _l = cdll.LoadLibrary("libdrm.so.2")
+
+ _l.drmGetVersion.argtypes = [c_int] # fd
+ _l.drmGetVersion.restype = POINTER(drmVersionRec)
+ _l.drmFreeVersion.argtypes = [POINTER(drmVersionRec)]
+ _l.drmFreeVersion.restype = None
+
+ _l.drmModeGetResources.argtypes = [c_int] # fd
+ _l.drmModeGetResources.restype = POINTER(drmModeResRec)
+ _l.drmModeFreeResources.argtypes = [POINTER(drmModeResRec)]
+ _l.drmModeFreeResources.restype = None
+
+ _l.drmModeGetConnector.argtypes = [c_int, c_uint] # fd, id
+ _l.drmModeGetConnector.restype = POINTER(drmModeConnectorRec)
+ _l.drmModeFreeConnector.argtypes = [POINTER(drmModeConnectorRec)]
+ _l.drmModeFreeConnector.restype = None
+
+ _l.drmModeGetEncoder.argtype = None
+ _l.drmModeGetEncoder.restype = POINTER(drmModeEncoderRec)
+ _l.drmModeFreeEncoder.argtypes = [POINTER(drmModeEncoderRec)]
+ _l.drmModeFreeEncoder.restype = None
+
+ _l.drmModeGetCrtc.argtypes = [c_int, c_uint] # fd, id
+ _l.drmModeGetCrtc.restype = POINTER(drmModeCrtcRec)
+ _l.drmModeFreeCrtc.argtypes = [POINTER(drmModeCrtcRec)]
+ _l.drmModeFreeCrtc.restype = None
+
+ _l.drmIoctl.argtypes = [c_int, c_ulong, c_void_p] # fd, num, ptr
+ _l.drmIoctl.restype = c_int
+
+ _l.drmModeAddFB.argtypes = [c_int, c_uint, c_uint, c_char, c_char,
+ c_uint, c_uint, POINTER(c_uint)]
+ _l.drmModeAddFB.restype = c_int
+
+ _l.drmModeSetCrtc.argtypes = [c_int, c_uint, c_uint, c_uint, c_uint,
+ POINTER(c_uint), c_int,
+ POINTER(drmModeModeInfoRec)]
+ _l.drmModeSetCrtc.restype = c_int
+
+ _l.connector_type_to_name = connector_type_to_name
+
+ return _l
diff --git a/kernel/gfx/scripts/libdrm.pyc b/kernel/gfx/scripts/libdrm.pyc
new file mode 100644
index 0000000..8325375
--- /dev/null
+++ b/kernel/gfx/scripts/libdrm.pyc
Binary files differ
diff --git a/kernel/gfx/scripts/run-kms-test.py b/kernel/gfx/scripts/run-kms-test.py
new file mode 100755
index 0000000..9044c77
--- /dev/null
+++ b/kernel/gfx/scripts/run-kms-test.py
@@ -0,0 +1,479 @@
+#!/usr/bin/python
+# coding: utf-8
+#
+# run-kms-test.py: Very basic smoke tests for KMS
+#
+# These tests aim to exercise KMS to the point of answering the question,
+# 'is it working well enough to even bother with other tests?'.
+#
+# It has arguably been extended beyond the point of breakage already. Further
+# tests which aren't extremely basic smoketests, should instead be added to
+# intel-gpu-tools (which isn't actually Intel-only):
+# https://01.org/linuxgraphics/gfx-docs/igt/
+#
+#
+# Copyright © 2016 Collabora, Ltd.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+# Author: Daniel Stone <daniels@collabora.com>
+
+
+import argparse
+import ctypes
+import mmap
+import os
+import os.path
+import select
+import sys
+import time
+
+import pyudev
+import xmlrpclib
+from ctypes import *
+
+import libdrm
+
+drm = None
+drm_conn = None
+drm_res = None
+target_connector_status = {}
+
+RESULT_PASS = "pass" # everything worked, hooray
+RESULT_FAIL = "fail" # something is broken in this _specific_ test
+RESULT_SKIP = "skip" # don't even bother
+RESULT_ERR = "unknown" # assumptions and/or prereqs failed
+
+info_buf = ""
+
+def info(msg):
+ global info_buf
+ global test_name
+ info_buf += "# %s: %s\n" % (test_name, msg)
+
+def fail(reason):
+ global info_buf
+ global test_name
+ info_buf += "# %s: FAIL: %s\n" % (test_name, reason)
+
+def wait_for_hpd(udev_ctx, udev_mtr, port):
+ fd = udev_mtr.fileno()
+ ready = select.select([fd], [], [], 5.0)
+ if not fd in ready[0]:
+ return "timeout"
+
+ (action, device) = udev_mtr.receive_device()
+ if not action == "change":
+ return "unexpected action %s" % action
+ if not device == pyudev.Device.from_path(udev_ctx, "/sys/class/drm/card0"):
+ return "unexpected device %s" % device
+
+ device = pyudev.Device.from_path(udev_ctx, "/sys/class/drm/card0-%s" % port)
+ return device.attributes["status"]
+
+def validate_hpd():
+ global chamelium_port
+ global chamelium_server
+ global udev_thread_queue
+ global edid_file_name
+
+ (port_local, port_remote) = chamelium_port.split(":")
+
+ remote_input = -1
+ chamelium = xmlrpclib.Server(chamelium_server)
+ for i in chamelium.GetSupportedInputs():
+ if not chamelium.HasVideoSupport(i) or not chamelium.IsPhysicalPlugged(i):
+ continue
+ if chamelium.GetConnectorType(i) == port_remote:
+ remote_input = i
+ break
+
+ if remote_input == -1:
+ fail("Couldn't find input type %s on Chamelium" % port_remote)
+ return RESULT_FAIL
+
+ chamelium.SetDdcState(remote_input, True)
+ chamelium.Plug(remote_input)
+ time.sleep(1)
+
+ udev_ctx = pyudev.Context()
+ udev_mtr = pyudev.Monitor.from_netlink(udev_ctx)
+ udev_mtr.filter_by(subsystem="drm")
+ udev_mtr.start()
+
+ chamelium.Unplug(remote_input)
+ chamelium.SetDdcState(remote_input, False)
+ plugged = wait_for_hpd(udev_ctx, udev_mtr, port_local)
+ if plugged != "disconnected":
+ fail("expected %s to be disconnected, got %s" % (port_local, plugged))
+ return RESULT_FAIL
+
+ with open("%s/dell-u2415h-1080p.edid" % os.path.dirname(sys.argv[0]), "r") as edid_file:
+ edid_bin = xmlrpclib.Binary(edid_file.read())
+ edid_id = chamelium.CreateEdid(edid_bin)
+ chamelium.ApplyEdid(remote_input, edid_id)
+ chamelium.DestroyEdid(edid_id)
+
+ chamelium.SetDdcState(remote_input, True)
+ chamelium.Plug(remote_input)
+ plugged = wait_for_hpd(udev_ctx, udev_mtr, port_local)
+ if plugged != "connected":
+ fail("expected %s to be connected, got %s" % (port_local, plugged))
+ return RESULT_FAIL
+ target_connector_status[port_local] = (True, 1920, 1080)
+ #check_modes_list()
+
+ return RESULT_PASS
+
+
+def validate_display():
+ global chamelium_port, drm_fd
+
+ (port_local, port_remote) = chamelium_port.split(":")
+
+ width = 1920
+ height = 1080
+
+ create_ioc = libdrm.drmModeCreateDumbIoc()
+ create_ioc.width = width
+ create_ioc.height = height
+ create_ioc.bpp = 32
+ create_ioc.flags = 0
+ err = drm.drmIoctl(drm_fd, libdrm.DRM_IOCTL_MODE_CREATE_DUMB,
+ ctypes.pointer(create_ioc))
+ if err != 0:
+ fail("creating %d x %d dumb buffer failed: %d" % (width, height, err))
+ return RESULT_FAIL
+
+ map_ioc = libdrm.drmModeMapDumbIoc()
+ map_ioc.handle = create_ioc.handle
+ err = drm.drmIoctl(drm_fd, libdrm.DRM_IOCTL_MODE_MAP_DUMB,
+ ctypes.pointer(map_ioc))
+ if err != 0:
+ fail("getting dumb buffer map offset failed: %d" % (width, height, err))
+ return RESULT_FAIL
+
+ buf = mmap.mmap(drm_fd, create_ioc.size, offset=map_ioc.offset)
+
+ y = 0
+ while y < height:
+ x = 0
+ while x < create_ioc.width:
+ buf.write("\x00\xff\x00\xff")
+ x += 1
+ while (x + 1) * 4 < create_ioc.pitch:
+ buf.write("\x00\x00\xff\xff")
+ x += 1
+ y += 1
+ buf.close()
+
+ buf_id = ctypes.c_uint()
+ err = drm.drmModeAddFB(drm_fd, width, height, chr(32), chr(32),
+ create_ioc.pitch, create_ioc.handle,
+ ctypes.pointer(buf_id))
+ if err != 0:
+ fail("creating %d x %d framebuffer failed: %d" % (width, height, err))
+ return RESULT_FAIL
+
+ drm_res = drm_conn.get_resources()
+ for i in range(drm_res.count_connectors):
+ connector = drm_res.get_connector(drm_res.connectors[i])
+ name_c = "%s-%d" % \
+ (libdrm.connector_type_to_name(connector.connector_type),
+ connector.connector_type_id)
+ if name_c != port_local:
+ continue
+ for i in range(connector.count_modes):
+ mode = connector.modes[i]
+ if mode.hdisplay != width or mode.vdisplay != height:
+ continue
+ break
+
+ enc = connector.encoder_id
+ for i in range(drm_res.count_encoders):
+ encoder = drm_res.get_encoder(drm_res.encoders[i])
+ if encoder.encoder_id != enc:
+ continue
+
+ possible_crtcs = encoder.possible_crtcs
+ for i in range(drm_res.count_crtcs):
+ if not possible_crtcs & (1 << i):
+ continue
+ crtc = drm_res.get_crtc(drm_res.crtcs[i])
+ break
+
+ chamelium = xmlrpclib.Server(chamelium_server)
+
+ remote_input = -1
+ chamelium = xmlrpclib.Server(chamelium_server)
+ for i in chamelium.GetSupportedInputs():
+ if not chamelium.HasVideoSupport(i) or not chamelium.IsPhysicalPlugged(i):
+ continue
+ if chamelium.GetConnectorType(i) == port_remote:
+ remote_input = i
+ break
+
+ if remote_input == -1:
+ fail("Couldn't find input type %s on Chamelium" % port_remote)
+ return RESULT_FAIL
+
+ info("setting mode %d x %d on CRTC %d" % \
+ (mode.hdisplay, mode.vdisplay, crtc.crtc_id))
+ err = drm.drmModeSetCrtc(drm_fd, crtc.crtc_id, buf_id, 0, 0,
+ ctypes.pointer(ctypes.c_uint(connector.connector_id)), 1,
+ ctypes.pointer(mode))
+ time.sleep(0.5)
+
+ width_det, height_det = chamelium.DetectResolution(remote_input)
+ if width_det != width or height_det != height:
+ fail("output dimensions (%d x %d) do not match request (%d x %d)" % \
+ (width_det, height_det, width, height))
+ return RESULT_FAIL
+
+ area = [0, 0, width, height]
+ pixels = bytes(chamelium.DumpPixels(remote_input, *area).data)
+
+ y = 0
+ while y < height:
+ x = 0
+ while x < width:
+ offset = ((y * width) + x) * 3
+ if ord(pixels[offset]) != 0 or ord(pixels[offset + 1]) != 0xff or \
+ ord(pixels[offset + 2]):
+ fail("unexpected pixel content (%x, %x, %x) at (%d, %d)" % \
+ (ord(pixels[offset]), ord(pixels[offset + 1]), \
+ ord(pixels[offset + 2]), x, y))
+ return RESULT_FAIL
+ x += 1
+ y += 1
+
+ return RESULT_PASS
+
+# Get a list of target connectors, statuses and resolutions.
+def validate_device():
+ global device_name
+
+ # Devices with a 1366x768 panel which appears on eDP-1.
+ targets_eDP_1366x768 = (
+ "tegra124-nyan-big",
+ "tegra124-nyan-blaze"
+ )
+
+ # Devices with a 1366x768 panel which appears on LVDS-1 (despite being
+ # behind a eDP->LVDS bridge).
+ targets_LVDS_1366x768 = (
+ "snow", "exynos5250-snow",
+ "peach-pit", "exynos5420-peach-pit"
+ )
+
+ # Devices with a 1920x1080p panel which appears on eDP-1.
+ targets_eDP_1080p = (
+ "peach-pi", "exynos5800-peach-pi"
+ )
+
+ if device_name in targets_eDP_1366x768:
+ target_connector_status["eDP-1"] = (True, 1366, 768)
+ if device_name in targets_LVDS_1366x768:
+ target_connector_status["LVDS-1"] = (True, 1366, 768)
+ if device_name in targets_eDP_1080p:
+ target_connector_status["eDP-1"] = (True, 1920, 1080)
+
+ return RESULT_PASS
+
+
+def init_libdrm():
+ global drm
+ drm = libdrm.init()
+ return RESULT_PASS
+
+
+def drm_connect():
+ global drm_conn, drm_res, drm, drm_fd
+ drm_fd = os.open("/dev/dri/card0", os.O_RDWR)
+ drm_conn = libdrm.connect(drm, drm_fd)
+ drm_res = drm_conn.get_resources()
+ return RESULT_PASS
+
+def validate_one_connector(connector):
+ global target_connector_status
+ global test_name
+
+ name_c = "%s-%d" % \
+ (libdrm.connector_type_to_name(connector.connector_type),
+ connector.connector_type_id)
+ test_name = "validate-connector-%s" % name_c
+ status = libdrm.connection_status_to_name(connector.connection)
+
+ crtc = None
+ if connector.encoder_id:
+ encoder = drm_res.get_encoder(connector.encoder_id)
+ if encoder:
+ crtc = drm_res.get_crtc(encoder.crtc_id)
+
+ info("Connector ID %d is %s" % (connector.connector_id, status))
+ if crtc:
+ if crtc.mode_valid:
+ mode = "active (%d x %d)" % \
+ (crtc.mode.hdisplay, crtc.mode.vdisplay)
+ else:
+ mode = "inactive"
+ info(" CRTC %d (currently %s)" % (crtc.crtc_id, mode))
+
+ p_h = 0
+ p_v = 0
+ for i in range(connector.count_modes):
+ info(" mode (%d x %d), type 0x%x" % (connector.modes[i].hdisplay,
+ connector.modes[i].vdisplay,
+ connector.modes[i].type))
+ if connector.modes[i].type & libdrm.DRM_MODE_TYPE_PREFERRED or \
+ connector.modes[i].type & libdrm.DRM_MODE_TYPE_BUILTIN or \
+ connector.count_modes == 1:
+ p_h = connector.modes[i].hdisplay
+ p_v = connector.modes[i].vdisplay
+ if p_h and p_v:
+ info(" Preferred mode %d x %d" % (p_h, p_v))
+ elif connector.connection == libdrm.DRM_MODE_CONNECTED:
+ info(" no preferred mode, even though we're connected")
+
+ if name_c in target_connector_status:
+ (want_conn, want_h, want_v) = target_connector_status[name_c]
+ del target_connector_status[name_c]
+ if want_conn and \
+ not connector.connection == libdrm.DRM_MODE_CONNECTED:
+ fail("Connector %s not connected" % name_c)
+ return RESULT_FAIL
+ elif not want_conn and \
+ connector.connection == libdrm.DRM_MODE_CONNECTED:
+ fail("Connector %s unexpectedly connected" % name_c)
+ return RESULT_FAIL
+ if want_conn and (not p_h == want_h or not p_v == want_v):
+ fail("Wanted %d x %d on %s but got %d x %d" % \
+ (want_h, want_v, name_c, p_h, p_v))
+ return RESULT_FAIL
+
+ return RESULT_PASS
+
+
+def validate_connectors():
+ global target_connector_status
+ global test_name
+
+ ret = RESULT_PASS
+
+ for i in range(drm_res.count_connectors):
+ connector = drm_res.get_connector(drm_res.connectors[i])
+ old_test_name = test_name
+ tmp = validate_one_connector(connector)
+ test_name = old_test_name
+ if not tmp == RESULT_PASS and ret == RESULT_PASS:
+ ret = tmp
+
+ for conn in target_connector_status:
+ fail("Connector %s not found" % conn)
+ ret = RESULT_FAIL
+
+ return ret
+
+
+subtests = (
+ ("get_device_type", validate_device, ("device_name", None)),
+ ("init_libdrm", init_libdrm, ()),
+ ("drm_connect", drm_connect, ("init_libdrm", None)),
+ ("check_connector_status", validate_connectors, ("get_device_type", "drm_connect")),
+ ("check_hpd_chamelium", validate_hpd, ("drm_connect", "chamelium_port")),
+ ("check_display_chamelium", validate_display, ("check_hpd_chamelium", None))
+)
+
+
+def run_one_test(name, func, deps, prev_status):
+ global info_buf
+ global test_name
+ info_buf = ""
+ test_name = name
+
+ # Really lame prereq handling.
+ if deps:
+ for dep in deps:
+ if not dep:
+ continue
+ if not dep in prev_status:
+ fail("Prereq %s not in test list" % dep)
+ return RESULT_ERR
+ if prev_status[dep] == "skip":
+ fail("Prereq %s skipped; also skipping" % dep)
+ return RESULT_SKIP
+ if not prev_status[dep] == "pass":
+ fail("Prereq %s failed, skipping" % dep)
+ return RESULT_ERR
+
+ #try:
+ ret = func()
+ #except Exception as e:
+ # fail(name, "Exception raised: %s" % e)
+ # ret = RESULT_FAIL
+
+ if info_buf and not ret == RESULT_PASS:
+ print info_buf
+ return ret
+
+def run_all_tests():
+ global device_name
+ global chamelium_port
+
+ ret = 0
+ status = {}
+
+ if device_name:
+ status["device_name"] = "pass"
+ else:
+ status["device_name"] = "skip"
+ if chamelium_port:
+ status["chamelium_port"] = "pass"
+ else:
+ status["chamelium_port"] = "skip"
+
+ for test in subtests:
+ name = test[0]
+ func = test[1]
+ deps = test[2]
+ status[name] = run_one_test(name, func, deps, status)
+ if not status[name] == "pass" and not status[name] == "skip":
+ ret = 1
+ print "%s: %s" % (name, status[name])
+
+if __name__ == "__main__":
+ global device_name
+ global chamelium_port
+ global edid_file_name
+ global chamelium_server
+
+ if len(sys.argv) >= 2:
+ device_name = sys.argv[1]
+ else:
+ device_name = None
+
+ if len(sys.argv) >= 4:
+ chamelium_server = "http://%s:9992" % sys.argv[2]
+ chamelium_port = sys.argv[3]
+ else:
+ chamelium_server = None
+ chamelium_port = None
+
+ sys.exit(run_all_tests())