diff --git a/kms/connector.py b/kms/connector.py index 1246fc8..51a7284 100644 --- a/kms/connector.py +++ b/kms/connector.py @@ -68,6 +68,21 @@ def __init__(self, card: Card, id, idx) -> None: def connected(self): return self.connector_res.connection in (kms.uapi.DRM_MODE_CONNECTED, kms.uapi.DRM_MODE_UNKNOWNCONNECTION) + def refresh_modes(self): + res = kms.uapi.drm_mode_get_connector(connector_id=self.id) + + fcntl.ioctl(self.card.fd, kms.uapi.DRM_IOCTL_MODE_GETCONNECTOR, res, True) + + modes = (kms.uapi.drm_mode_modeinfo * res.count_modes)() + res.modes_ptr = ctypes.addressof(modes) + + res.count_props = 0 + res.count_encoders = 0 + + fcntl.ioctl(self.card.fd, kms.uapi.DRM_IOCTL_MODE_GETCONNECTOR, res, True) + + self.modes = [kms.VideoMode(m) for m in modes] + def get_default_mode(self): return self.modes[0] diff --git a/kms/framebuffer.py b/kms/framebuffer.py index 2c4c71b..b321173 100644 --- a/kms/framebuffer.py +++ b/kms/framebuffer.py @@ -5,6 +5,7 @@ import mmap import os import weakref +from math import ceil from typing import TYPE_CHECKING @@ -64,13 +65,21 @@ class DumbFramebuffer(Framebuffer): def __init__(self, card: Card, width: int, height: int, format: kms.PixelFormat) -> None: planes = [] - assert width % format.pixelspergroup == 0 + assert width % format.pixelspergroup[0] == 0 + + # DRM_IOCTL_MODE_CREATE_DUMB takes a 'bpp' (bits-per-pixel) argument, + # which is then used with the width and height to allocate the buffer. + # This doesn't work for pixel formats where the average bits-per-pixel + # is not an integer (e.g. XV15) + # + # So, we instead use the number of bits per (horizontal) pixel group as + # the 'bpp' argument, and adjust the width accordingly. for pi in format.planes: creq = kms.uapi.drm_mode_create_dumb() - creq.width = width - creq.height = height // pi.verticalsubsampling - creq.bpp = pi.bytespergroup * 8 // format.pixelspergroup + creq.width = int(ceil(width / format.pixelspergroup[0])) + creq.height = int(ceil(height / format.pixelspergroup[1])) * pi.linespergroup + creq.bpp = pi.bytespergroup * 8 fcntl.ioctl(card.fd, kms.uapi.DRM_IOCTL_MODE_CREATE_DUMB, creq, True) diff --git a/utils/hpd.py b/utils/hpd.py new file mode 100755 index 0000000..a53cce9 --- /dev/null +++ b/utils/hpd.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import pprint +import pyudev + +import kms + +card = kms.Card() +connectors = card.connectors + +context = pyudev.Context() + +dev = pyudev.Devices.from_name(context, 'drm', 'card0') + +monitor = pyudev.Monitor.from_netlink(context) +monitor.filter_by('drm') + +for device in iter(monitor.poll, None): + if 'HOTPLUG' in device: + print('== HPD ==') + for conn in connectors: + conn.refresh_modes() + strs = (conn.fullname, + ['{}x{}'.format(m.hdisplay, m.vdisplay) for m in conn.modes]) + pprint.pprint(strs, compact=True, width=120) diff --git a/utils/kmstest.py b/utils/kmstest.py index 5db6218..b7c976a 100755 --- a/utils/kmstest.py +++ b/utils/kmstest.py @@ -40,6 +40,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('-c', '--connector', default='') parser.add_argument('--dmabuf', nargs='?', const='reserved', metavar='HEAP', help='use dmabuf') + parser.add_argument('-f', '--format', default='XRGB8888') args = parser.parse_args() card = kms.Card() @@ -54,7 +55,8 @@ def main(): modeb = mode.to_blob(card) - fmt = kms.PixelFormats.XRGB8888 + fmt = kms.PixelFormats.find_by_name(args.format) + width = mode.hdisplay height = mode.vdisplay @@ -70,12 +72,13 @@ def main(): else: fb = kms.DumbFramebuffer(card, width, height, fmt) - ts1 = time.perf_counter() - fb.begin_cpu_access('w') - draw_test_pattern(fb) - fb.end_cpu_access() - ts2 = time.perf_counter() - print(f'Drawing took {(ts2 - ts1) * 1000:.4f} ms') + if fmt == kms.PixelFormats.XRGB8888: + ts1 = time.perf_counter() + fb.begin_cpu_access('w') + draw_test_pattern(fb) + fb.end_cpu_access() + ts2 = time.perf_counter() + print(f'Drawing took {(ts2 - ts1) * 1000:.4f} ms') req = kms.AtomicReq(card)