Hi,

Since updating my snapshot machine last night, I'm seeing the folloing.
This is with one external monitor attached through the dock
(DisplayPort). The "timed out" lines appear when X starts and halt it
for a few minutes. Once X does come up, the error no longer appears. The
last line ("FIFO underrun") appeared about two minutes later during
regular operation.

    OpenBSD 6.5-current (GENERIC.MP) #32: Thu May 16 09:45:13 MDT 2019
    ...
    inteldrm0 at pci0 dev 2 function 0 "Intel HD Graphics 5500" rev 0x09
    drm0 at inteldrm0
    inteldrm0: msi
    ...
    [drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
    [drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
    [drm] *ERROR* CPU pipe B FIFO underrun

Find full dmesg attached. I'm also attaching output from a program I
have lying around that dumps various drm info, especially the
monitor/output topology.*

Regards,
pesco

* It currently errors out on a GETPROPBLOB ioctl somewhere late, which
  I would have to investigate, so the output is truncated - but the
  important info should be there.

OpenBSD 6.5-current (GENERIC.MP) #32: Thu May 16 09:45:13 MDT 2019
    dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 8277159936 (7893MB)
avail mem = 8016203776 (7644MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.7 @ 0xacbfd000 (65 entries)
bios0: vendor LENOVO version "N10ET36W (1.15 )" date 06/19/2015
bios0: LENOVO 20CLS2LK00
acpi0 at bios0: rev 2
acpi0: sleep states S0 S3 S4 S5
acpi0: tables DSDT FACP ASF! HPET ECDT APIC MCFG SSDT SSDT SSDT SSDT SSDT SSDT 
SSDT SSDT SSDT PCCT SSDT TCPA SSDT UEFI POAT BATB FPDT UEFI DMAR
acpi0: wakeup devices LID_(S4) SLPB(S3) IGBE(S4) EXP2(S4) XHCI(S3) EHC1(S3)
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpihpet0 at acpi0: 14318179 Hz
acpiec0 at acpi0
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz, 2095.48 MHz, 06-3d-04
cpu0: 
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,AVX2,SMEP,BMI2,ERMS,INVPCID,RDSEED,ADX,SMAP,PT,MD_CLEAR,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu0: 256KB 64b/line 8-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 10 var ranges, 88 fixed ranges
cpu0: apic clock running at 99MHz
cpu0: mwait min=64, max=64, C-substates=0.2.1.2.4.1.1.1, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz, 2095.16 MHz, 06-3d-04
cpu1: 
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,AVX2,SMEP,BMI2,ERMS,INVPCID,RDSEED,ADX,SMAP,PT,MD_CLEAR,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu1: 256KB 64b/line 8-way L2 cache
cpu1: smt 1, core 0, package 0
cpu2 at mainbus0: apid 2 (application processor)
cpu2: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz, 2095.16 MHz, 06-3d-04
cpu2: 
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,AVX2,SMEP,BMI2,ERMS,INVPCID,RDSEED,ADX,SMAP,PT,MD_CLEAR,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu2: 256KB 64b/line 8-way L2 cache
cpu2: smt 0, core 1, package 0
cpu3 at mainbus0: apid 3 (application processor)
cpu3: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz, 2095.16 MHz, 06-3d-04
cpu3: 
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,AVX2,SMEP,BMI2,ERMS,INVPCID,RDSEED,ADX,SMAP,PT,MD_CLEAR,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu3: 256KB 64b/line 8-way L2 cache
cpu3: smt 1, core 1, package 0
ioapic0 at mainbus0: apid 2 pa 0xfec00000, version 20, 40 pins
acpimcfg0 at acpi0
acpimcfg0: addr 0xf8000000, bus 0-63
acpiprt0 at acpi0: bus 0 (PCI0)
acpiprt1 at acpi0: bus -1 (PEG_)
acpiprt2 at acpi0: bus 2 (EXP1)
acpiprt3 at acpi0: bus 3 (EXP2)
acpiprt4 at acpi0: bus -1 (EXP3)
acpicpu0 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33), 
C1(1000@1 mwait.1), PSS
acpicpu1 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33), 
C1(1000@1 mwait.1), PSS
acpicpu2 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33), 
C1(1000@1 mwait.1), PSS
acpicpu3 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33), 
C1(1000@1 mwait.1), PSS
acpipwrres0 at acpi0: PUBS, resource for XHCI, EHC1
acpipwrres1 at acpi0: NVP3, resource for PEG_
acpipwrres2 at acpi0: NVP2, resource for PEG_
acpitz0 at acpi0: critical temperature is 128 degC
acpibtn0 at acpi0: LID_
acpibtn1 at acpi0: SLPB
acpipci0 at acpi0 PCI0: 0x00000000 0x00000011 0x00000001
acpicmos0 at acpi0
acpibat0 at acpi0: BAT0 model "45N1111" serial 16669 type LiP oem "SONY"
acpibat1 at acpi0: BAT1 model "45N1777" serial  3056 type LION oem "SANYO"
acpiac0 at acpi0: AC unit online
acpithinkpad0 at acpi0
tpm0 at acpi0: TPM_ addr 0xfed40000/0x5000, device 0x0000104a rev 0x4e
"PNP0C14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
"INT340F" at acpi0 not configured
acpivideo0 at acpi0: VID_
acpivout at acpivideo0 not configured
acpivideo1 at acpi0: VID_
cpu0: using VERW MDS workaround
cpu0: Enhanced SpeedStep 2095 MHz: speeds: 2201, 2200, 2100, 2000, 1800, 1700, 
1600, 1500, 1300, 1200, 1100, 1000, 900, 700, 600, 500 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel Core 5G Host" rev 0x09
inteldrm0 at pci0 dev 2 function 0 "Intel HD Graphics 5500" rev 0x09
drm0 at inteldrm0
inteldrm0: msi
azalia0 at pci0 dev 3 function 0 "Intel Core 5G HD Audio" rev 0x09: msi
azalia0: No codecs found
xhci0 at pci0 dev 20 function 0 "Intel 9 Series xHCI" rev 0x03: msi, xHCI 1.0
usb0 at xhci0: USB revision 3.0
uhub0 at usb0 configuration 1 interface 0 "Intel xHCI root hub" rev 3.00/1.00 
addr 1
"Intel 9 Series MEI" rev 0x03 at pci0 dev 22 function 0 not configured
em0 at pci0 dev 25 function 0 "Intel I218-LM" rev 0x03: msi, address 
50:7b:9d:11:24:e8
azalia1 at pci0 dev 27 function 0 "Intel 9 Series HD Audio" rev 0x03: msi
azalia1: codecs: Realtek ALC292
audio0 at azalia1
ppb0 at pci0 dev 28 function 0 "Intel 9 Series PCIE" rev 0xe3: msi
pci1 at ppb0 bus 2
rtsx0 at pci1 dev 0 function 0 "Realtek RTS5227 Card Reader" rev 0x01: msi
sdmmc0 at rtsx0: 4-bit, dma
ppb1 at pci0 dev 28 function 1 "Intel 9 Series PCIE" rev 0xe3: msi
pci2 at ppb1 bus 3
iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless AC 7265" rev 0x59, msi
pcib0 at pci0 dev 31 function 0 "Intel 9 Series LPC" rev 0x03
ahci0 at pci0 dev 31 function 2 "Intel 9 Series AHCI" rev 0x03: msi, AHCI 1.3
ahci0: port 0: 6.0Gb/s
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 0 lun 0: <ATA, Samsung SSD 850, EMT0> SCSI3 0/direct fixed 
naa.5002538d40242fcb
sd0: 476940MB, 512 bytes/sector, 976773168 sectors, thin
ichiic0 at pci0 dev 31 function 3 "Intel 9 Series SMBus" rev 0x03: apic 2 int 18
iic0 at ichiic0
pchtemp0 at pci0 dev 31 function 6 "Intel 9 Series Thermal" rev 0x03
isa0 at pcib0
isadma0 at isa0
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
wsmouse1 at pms0 mux 0
pms0: Synaptics clickpad, firmware 8.1, 0x1e2b1 0x943300
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
vmm0 at mainbus0: VMX/EPT
uhub1 at uhub0 port 3 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" 
rev 2.10/50.40 addr 2
uhidev0 at uhub1 port 2 configuration 1 interface 0 "Kensington Kensington 
Slimblade Trackball" rev 1.10/1.05 addr 3
uhidev0: iclass 3/1
ums0 at uhidev0: 2 buttons, Z dir
wsmouse2 at ums0 mux 0
uhidev1 at uhub1 port 3 configuration 1 interface 0 "Holtek USB Keyboard" rev 
1.10/1.10 addr 4
uhidev1: iclass 3/1
ukbd0 at uhidev1: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
uhidev2 at uhub1 port 3 configuration 1 interface 1 "Holtek USB Keyboard" rev 
1.10/1.10 addr 4
uhidev2: iclass 3/1, 2 report ids
uhid0 at uhidev2 reportid 1: input=6, output=0, feature=0
uhid1 at uhidev2 reportid 2: input=1, output=0, feature=0
uhub2 at uhub1 port 4 configuration 1 interface 0 "Lenovo Lenovo ThinkPad Dock" 
rev 2.00/0.01 addr 5
ugen0 at uhub0 port 5 "Generic EMV Smartcard Reader" rev 2.01/1.20 addr 6
usbd_free_xfer: xfer=0xfffffd824d855960 not free
ugen0: setting configuration index 0 failed
ugen1 at uhub0 port 6 "Validity Sensors VFS5011 Fingerprint Reader" rev 
1.10/0.78 addr 7
ugen2 at uhub0 port 7 "Intel Bluetooth" rev 2.01/0.01 addr 8
uhub3 at uhub0 port 14 configuration 1 interface 0 "LENOVO Lenovo ThinkPad 
Dock" rev 3.00/50.41 addr 9
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
sd1 at scsibus3 targ 1 lun 0: <OPENBSD, SR CRYPTO, 006> SCSI2 0/direct fixed
sd1: 409599MB, 512 bytes/sector, 838860209 sectors
root on sd1a (1aebccd650f38400.a) swap on sd1b dump on sd1b
inteldrm0: 1920x1080, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation), using wskbd0
wskbd1: connecting to wsdisplay0
wsdisplay0: screen 1-5 added (std, vt100 emulation)
iwm0: hw rev 0x210, fw ver 16.242414.0, address 94:65:9c:46:8a:f2
[drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:39:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:51:pipe ] cleanup_done timed out
[drm] *ERROR* [CRTC:63:pipe ] cleanup_done timed out
[drm] *ERROR* CPU pipe B FIFO underrun
/dev/drm0:
  drm v1.6.0, driver i915, Intel Graphics (20180719)
  auth id (magic number): 4
  bus id string: pci:0000:00:02.0
  device 2, function 0 at pci bus 0, domain 0
    vendor 8086, device 1616, subvendor 17aa, subdevice 2226
    revision 0
  capabilities:
    DUMB_BUFFER          = 1
    VBLANK_HIGH_CRTC     = 1
    DUMB_PREFERRED_DEPTH = 24
    DUMB_PREFER_SHADOW   = 1
    PRIME                = IMPORT, EXPORT
    TIMESTAMP_MONOTONIC  = 1
    ASYNC_PAGE_FLIP      = 0
    CURSOR_WIDTH         = 256
    CURSOR_HEIGHT        = 256
  set client capabilities:
  vblank no. 414576 reached at time 6922.895168s, waited 0.006048104s
    period 16.6ms, freq 60Hz
  modesetting: kernel
  3 crtcs, 10 connectors, 9 encoders, 0 framebuffers, 9 planes
    min width  0, max width  8192
    min height 0, max height 8192
  crtc 39: framebuffer 98, x-offset 1920
    mode 1920x1080 60.04Hz PHSYNC NVSYNC
         2106x1095 hsync 1968-2000 vsync 1083-1088, pixel clock 138460kHz
    fbuf 3840x1200 32 bpp, pitch 15360, depth 24, driver handle 1
    properties:
      ACTIVE: 1 (0 - 1)  RANGE
      MODE_ID:  BLOB
      OUT_FENCE_PTR: 136 (0 - 18446744073709551615)  RANGE
      DEGAMMA_LUT:  BLOB
      DEGAMMA_LUT_SIZE: 0 (0 - 4294967295)  IMMUTABLE RANGE
      CTM:  BLOB
      GAMMA_LUT:  BLOB
      GAMMA_LUT_SIZE: 0 (0 - 4294967295)  IMMUTABLE RANGE
  crtc 51: framebuffer 98
    mode 1920x1200 59.95Hz PHSYNC NVSYNC
         2080x1235 hsync 1968-2000 vsync 1203-1209, pixel clock 154000kHz
    fbuf 3840x1200 32 bpp, pitch 15360, depth 24, driver handle 2
    properties:
      ACTIVE: 1 (0 - 1)  RANGE
      MODE_ID:  BLOB
      OUT_FENCE_PTR: 132 (0 - 18446744073709551615)  RANGE
      DEGAMMA_LUT:  BLOB
      DEGAMMA_LUT_SIZE: 0 (0 - 4294967295)  IMMUTABLE RANGE
      CTM:  BLOB
      GAMMA_LUT:  BLOB
      GAMMA_LUT_SIZE: 0 (0 - 4294967295)  IMMUTABLE RANGE
  crtc 63: framebuffer 0, no mode
    properties:
      ACTIVE: 0 (0 - 1)  RANGE
      MODE_ID:  BLOB
      OUT_FENCE_PTR: 0 (0 - 18446744073709551615)  RANGE
      DEGAMMA_LUT:  BLOB
      DEGAMMA_LUT_SIZE: 0 (0 - 4294967295)  IMMUTABLE RANGE
      CTM:  BLOB
      GAMMA_LUT:  BLOB
      GAMMA_LUT_SIZE: 0 (0 - 4294967295)  IMMUTABLE RANGE
  connector 65: eDP, connected, 280mm x 160mm
    valid encoders: 64
    mode 1920x1080 ("1920x1080") 60.04Hz ("60Hz")  PREFERRED DRIVER PHSYNC 
NVSYNC
         2106x1095 hsync 1968-2000 vsync 1083-1088, pixel clock 138460kHz
    properties:
      EDID: 
"\x00\xff\xff\xff\xff\xff\xff\x000\xe47\x04\x00\x00\x00\x00\x00\x17\x01\x04\x95\x1c\x10x\x02\xbf\x05\x9aYU\x8e&\x1dPT\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x166\x80\xbap8\x0f@0
 
5\x00\x14\x9c\x10\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x00LG
 Display\x0a  \x00\x00\x00\xfe\x00LP125WF2-SPB2\x00\xdd"  IMMUTABLE BLOB
      DPMS: [On], Standby, Suspend, Off  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 39 (flags=0x80000040, count_values=1)  OBJECT
      Broadcast RGB: [Automatic], Full, Limited 16:235  ENUM
      scaling mode: Full, Center, [Full aspect]  ENUM
      Backlight: 12 (0 - 15)  RANGE
  connector 72: DisplayPort, disconnected
    valid encoders: 71
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: On, Standby, Suspend, [Off]  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      audio: force-dvi, off, [auto], on  ENUM
      Broadcast RGB: [Automatic], Full, Limited 16:235  ENUM
      Content Protection: [Undesired], Desired, Enabled  ENUM
  connector 78: HDMIA, disconnected
    valid encoders: 71
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: On, Standby, Suspend, [Off]  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      audio: force-dvi, off, [auto], on  ENUM
      Broadcast RGB: [Automatic], Full, Limited 16:235  ENUM
      aspect ratio: [Automatic], 4:3, 16:9  ENUM
      content type: [No Data], Graphics, Photo, Cinema, Game  ENUM
      Content Protection: [Undesired], Desired, Enabled  ENUM
  connector 83: DisplayPort, disconnected
    valid encoders: 82
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: On, Standby, Suspend, [Off]  ENUM
      link-status: Good, [Bad]  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      audio: force-dvi, off, [auto], on  ENUM
      Broadcast RGB: [Automatic], Full, Limited 16:235  ENUM
      Content Protection: [Undesired], Desired, Enabled  ENUM
  connector 88: HDMIA, disconnected
    valid encoders: 82
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: On, Standby, Suspend, [Off]  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      audio: force-dvi, off, [auto], on  ENUM
      Broadcast RGB: [Automatic], Full, Limited 16:235  ENUM
      aspect ratio: [Automatic], 4:3, 16:9  ENUM
      content type: [No Data], Graphics, Photo, Cinema, Game  ENUM
      Content Protection: [Undesired], Desired, Enabled  ENUM
  connector 94: DisplayPort, connected, 520mm x 320mm
    valid encoders: 84 85 86
    mode 1920x1200 ("1920x1200") 59.95Hz ("60Hz")  PREFERRED DRIVER PHSYNC 
NVSYNC
         2080x1235 hsync 1968-2000 vsync 1203-1209, pixel clock 154000kHz
    mode 1920x1080 ("1920x1080") 60.00Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         2200x1125 hsync 2008-2052 vsync 1084-1089, pixel clock 148500kHz
    mode 1920x1080 ("1920x1080") 60.00Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         2200x1125 hsync 2008-2052 vsync 1084-1089, pixel clock 148500kHz
    mode 1920x1080 ("1920x1080") 59.94Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         2200x1125 hsync 2008-2052 vsync 1084-1089, pixel clock 148352kHz
    mode 1920x1080 ("1920x1080") 50.00Hz ("50Hz")  DRIVER PHSYNC PVSYNC
         2640x1125 hsync 2448-2492 vsync 1084-1089, pixel clock 148500kHz
    mode 1920x1080 ("1920x1080") 24.00Hz ("24Hz")  DRIVER PHSYNC PVSYNC
         2750x1125 hsync 2558-2602 vsync 1084-1089, pixel clock 74250kHz
    mode 1920x1080 ("1920x1080") 23.98Hz ("24Hz")  DRIVER PHSYNC PVSYNC
         2750x1125 hsync 2558-2602 vsync 1084-1089, pixel clock 74176kHz
    mode 1600x1200 ("1600x1200") 60.00Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         2160x1250 hsync 1664-1856 vsync 1201-1204, pixel clock 162000kHz
    mode 1280x1024 ("1280x1024") 75.02Hz ("75Hz")  DRIVER PHSYNC PVSYNC
         1688x1066 hsync 1296-1440 vsync 1025-1028, pixel clock 135000kHz
    mode 1280x1024 ("1280x1024") 60.02Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         1688x1066 hsync 1328-1440 vsync 1025-1028, pixel clock 108000kHz
    mode 1152x864 ("1152x864") 75.00Hz ("75Hz")  DRIVER PHSYNC PVSYNC
         1600x900 hsync 1216-1344 vsync 865-868, pixel clock 108000kHz
    mode 1280x720 ("1280x720") 60.00Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         1650x750 hsync 1390-1430 vsync 725-730, pixel clock 74250kHz
    mode 1280x720 ("1280x720") 60.00Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         1650x750 hsync 1390-1430 vsync 725-730, pixel clock 74250kHz
    mode 1280x720 ("1280x720") 59.94Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         1650x750 hsync 1390-1430 vsync 725-730, pixel clock 74176kHz
    mode 1280x720 ("1280x720") 50.00Hz ("50Hz")  DRIVER PHSYNC PVSYNC
         1980x750 hsync 1720-1760 vsync 725-730, pixel clock 74250kHz
    mode 1024x768 ("1024x768") 75.03Hz ("75Hz")  DRIVER PHSYNC PVSYNC
         1312x800 hsync 1040-1136 vsync 769-772, pixel clock 78750kHz
    mode 1024x768 ("1024x768") 60.00Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         1344x806 hsync 1048-1184 vsync 771-777, pixel clock 65000kHz
    mode 800x600 ("800x600") 75.00Hz ("75Hz")  DRIVER PHSYNC PVSYNC
         1056x625 hsync 816-896 vsync 601-604, pixel clock 49500kHz
    mode 800x600 ("800x600") 60.32Hz ("60Hz")  DRIVER PHSYNC PVSYNC
         1056x628 hsync 840-968 vsync 601-605, pixel clock 40000kHz
    mode 720x576 ("720x576") 50.00Hz ("50Hz")  DRIVER NHSYNC NVSYNC
         864x625 hsync 732-796 vsync 581-586, pixel clock 27000kHz
    mode 720x576 ("720x576") 50.00Hz ("50Hz")  DRIVER NHSYNC NVSYNC
         864x625 hsync 732-796 vsync 581-586, pixel clock 27000kHz
    mode 720x480 ("720x480") 60.00Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         858x525 hsync 736-798 vsync 489-495, pixel clock 27027kHz
    mode 720x480 ("720x480") 60.00Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         858x525 hsync 736-798 vsync 489-495, pixel clock 27027kHz
    mode 720x480 ("720x480") 59.94Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         858x525 hsync 736-798 vsync 489-495, pixel clock 27000kHz
    mode 720x480 ("720x480") 59.94Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         858x525 hsync 736-798 vsync 489-495, pixel clock 27000kHz
    mode 720x480 ("720x480") 59.94Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         858x525 hsync 736-798 vsync 489-495, pixel clock 27000kHz
    mode 640x480 ("640x480") 75.00Hz ("75Hz")  DRIVER NHSYNC NVSYNC
         840x500 hsync 656-720 vsync 481-484, pixel clock 31500kHz
    mode 640x480 ("640x480") 60.00Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         800x525 hsync 656-752 vsync 490-492, pixel clock 25200kHz
    mode 640x480 ("640x480") 59.94Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         800x525 hsync 656-752 vsync 490-492, pixel clock 25175kHz
    mode 640x480 ("640x480") 59.94Hz ("60Hz")  DRIVER NHSYNC NVSYNC
         800x525 hsync 656-752 vsync 490-492, pixel clock 25175kHz
    mode 720x400 ("720x400") 70.08Hz ("70Hz")  DRIVER NHSYNC PVSYNC
         900x449 hsync 738-846 vsync 412-414, pixel clock 28320kHz
    properties:
      EDID: 
"\x00\xff\xff\xff\xff\xff\xff\x00\x10\xacF\xf0LKKA-\x19\x01\x04\xb54 
x:\x1d\xf5\xaeO5\xb3%\x0dPT\xa5K\x00\x81\x80\xa9@\xd1\x00qO\x01\x01\x01\x01\x01\x01\x01\x01(<\x80\xa0p\xb0#@0
 
6\x00\x06D!\x00\x00\x1a\x00\x00\x00\xff\x0084K965B6AKKL\x0a\x00\x00\x00\xfc\x00DELL
 U2413\x0a  \x00\x00\x00\xfd\x008L\x1eQ\x11\x00\x0a      
\x01(\x02\x03\x1d\xf1P\x90\x05\x04\x03\x02\x07\x16\x01\x1f\x12\x13\x14 
\x15\x11\x06#\x09\x1f\x07\x83\x01\x00\x00\x02:\x80\x18q8-@X,E\x00\x06D!\x00\x00\x1e\x01\x1d\x80\x18q\x1c\x16
 X,%\x00\x06D!\x00\x00\x9e\x01\x1d\x00rQ\xd0\x1e 
n(U\x00\x06D!\x00\x00\x1e\x8c\x0a\xd0\x8a 
\xe0-\x10\x10>\x96\x00\x06D!\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09"
  IMMUTABLE BLOB
      DPMS: [On], Standby, Suspend, Off  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 51 (flags=0x80000040, count_values=1)  OBJECT
      PATH: "mst:83-1-8\x00"  IMMUTABLE BLOB
      TILE:  IMMUTABLE BLOB
  connector 96: DisplayPort, disconnected
    valid encoders: 84 85 86
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: [On], Standby, Suspend, Off  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      PATH: "mst:83-1-1\x00"  IMMUTABLE BLOB
      TILE:  IMMUTABLE BLOB
  connector 93: DisplayPort, disconnected
    valid encoders: 84 85 86
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: [On], Standby, Suspend, Off  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      PATH: "mst:83-1\x00"  IMMUTABLE BLOB
      TILE:  IMMUTABLE BLOB
  connector 120: DisplayPort, disconnected
    valid encoders: 84 85 86
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: [On], Standby, Suspend, Off  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      PATH: "mst:83-2\x00"  IMMUTABLE BLOB
      TILE:  IMMUTABLE BLOB
  connector 126: DisplayPort, disconnected
    valid encoders: 84 85 86
    properties:
      EDID:  IMMUTABLE BLOB
      DPMS: [On], Standby, Suspend, Off  ENUM
      link-status: [Good], Bad  ENUM
      non-desktop: 0 (0 - 1)  IMMUTABLE RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      PATH: "mst:83-3\x00"  IMMUTABLE BLOB
      TILE:  IMMUTABLE BLOB
  encoder 64: TMDS, active on crtc 39
    possible crtcs: 39 51 63
    possible clones: 64
  encoder 71: TMDS, inactive
    possible crtcs: 39 51 63
    possible clones: 71
  encoder 73: DPMST, inactive
    possible crtcs: 39 51 63
    possible clones: 73
  encoder 74: DPMST, inactive
    possible crtcs: 39 51 63
    possible clones: 74
  encoder 75: DPMST, inactive
    possible crtcs: 39 51 63
    possible clones: 75
  encoder 82: TMDS, inactive
    possible crtcs: 39 51 63
    possible clones: 82
  encoder 84: DPMST, inactive
    possible crtcs: 39 51 63
    possible clones: 84
  encoder 85: DPMST, active on crtc 51
    possible crtcs: 39 51 63
    possible clones: 85
  encoder 86: DPMST, inactive
    possible crtcs: 39 51 63
    possible clones: 86
  plane 28: active on crtc 39 using framebuffer 98
    possible crtcs: 39
    supported formats: C8   RG16 XR24 XB24 XR30 XB30
    properties:
      type: Overlay, [Primary], Cursor  IMMUTABLE ENUM
      FB_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      IN_FENCE_FD: 98 (flags=0x80000080, count_values=2)  SIGNED_RANGE
      CRTC_ID: 0 (flags=0x80000040, count_values=1)  OBJECT
      CRTC_X: 4294967295 (flags=0x80000080, count_values=2)  SIGNED_RANGE
      CRTC_Y: 4294967295 (flags=0x80000080, count_values=2)  SIGNED_RANGE
      CRTC_W: 39 (0 - 2147483647)  RANGE
      CRTC_H: 0 (0 - 2147483647)  RANGE
      SRC_X: 0 (0 - 4294967295)  RANGE
      SRC_Y: 0 (0 - 4294967295)  RANGE
      SRC_W: 0 (0 - 4294967295)  RANGE
      SRC_H: 0 (0 - 4294967295)  RANGE
      IN_FORMATS:
// drminfo - interrogate kernel graphics driver
// pesco 2018

#include <fcntl.h>
#include <sys/ioctl.h>
#include <err.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>
#include "drm.h"

#include <unistd.h>


// XXX from dev/pci/drm/drm_crtc.h

enum drm_connector_status {
        connector_status_connected = 1,
        connector_status_disconnected = 2,
        connector_status_unknown = 3,
};

#define DRM_MODE_OBJECT_CRTC 0xcccccccc
#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0
#define DRM_MODE_OBJECT_MODE 0xdededede
#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0
#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
#define DRM_MODE_OBJECT_ANY 0



// The following ioctls declared in drm.h are Linux-only:
//
// IRQ_BUSID
// GET_MAP
// GET_CLIENT
// GET_STATS
//
// ADD_MAP
// RM_MAP
//
// SET_SAREA_CTX (noop on other systems)
// GET_SAREA_CTX (noop)
//
// SET_MASTER (noop)
// DROP_MASTER (noop)
//
// ADD_CTX
// RM_CTX
// GET_CTX
// SWITCH_CTX (noop)
// NEW_CTX (noop)
// RES_CTX
//
// LOCK
// UNLOCK
//
// ADD_BUFS
// MARK_BUFS (noop)
// INFO_BUFS (noop)
// MAP_BUFS
// FREE_BUFS
// DMA
//
// CONTROL
//
// AGP_ACQUIRE
// AGP_RELEASE
// AGP_ENABLE
// AGP_INFO
// AGP_ALLOC
// AGP_FREE
// AGP_BIND
// AGP_UNBIND
//
// SG_ALLOC
// SG_FREE


// The following ioctls are unimplemented in general (as of OpenBSD 6.4):
//
// BLOCK (noop)
// UNBLOCK (noop)
//
// MOD_CTX (noop)
//
// ADD_DRAW (noop)
// RM_DRAW (noop)
//
// FINISH (noop)
//
// UPDATE_DRAW (noop)
//
// ATTACHMODE (noop)
// DETACHMODE (noop)
//
// ATOMIC (missing from ioctl table)
// CREATEPROPBLOB (missing)
// DESTROYPROPBLOB (missing)


// This leaves as our actual API:
//
// VERSION
//   get API and driver version info
// GET_UNIQUE
//   get a unique "bus id" string for the device
// GET_MAGIC
//   get an id ("magic number") for this client for authentication purposes
// GET_CAP
//   query device capabiliities
// SET_CLIENT_CAP
//   enable specific driver features for this client
// SET_VERSION
//   demand a certain major/minor version
//
// SET_UNIQUE
//   deprecated since drm v1.1, always fails with EBUSY
// AUTH_MAGIC
//   mark the client with the given magic number as authenticated
//
// GET_PCIINFO (OpenBSD only)
//   get detailed PCI bus location and identification of the device
//
// WAIT_VBLANK
//   wait for a vertical retrace, with various options
//
// MODESET_CTL
//   used by user-space drivers to signal when they perform modesetting
//
// GEM_CLOSE
// GEM_FLINK
// GEM_OPEN
//
// PRIME_HANDLE_TO_FD
//   convert handle of a shareable buffer to a file descriptor
// PRIME_FD_TO_HANDLE
//   convert a file descriptor back to a buffer handle
//
// MODE_GETRESOURCES
//   get number and ids of framebuffers, crtcs, encoders, connectors
// MODE_GETPLANERESOURCES
//   get number and ids of image planes
// MODE_GETCRTC
//   get configuration of a given CRTC
// MODE_SETCRTC
//   modify configuration of a given CRTC
// MODE_GETPLANE
//   get configuration of a given plane
// MODE_SETPLANE
//   modify configuration of a given plane
// MODE_CURSOR
//   set CRTC's cursor configuration
// MODE_CURSOR2
//   set CRTC's cursor configuration, including cursor "hotspot"
// MODE_GETGAMMA
//   get the gamma table
// MODE_SETGAMMA
//   set the gamma table
// MODE_GETENCODER
//   get encoder configuration
// MODE_GETCONNECTOR
//   get connector configuration
// MODE_ATTACHMODE
// MODE_DETACHMODE
// MODE_GETPROPERTY
//   get property metadata (name, value range, etc.)
// MODE_SETPROPERTY
//   set property values on connector objects
// MODE_GETPROPBLOB
//   get the value of a "blob"-type property
// MODE_GETFB
//   get framebuffer configuration
// MODE_ADDFB
// MODE_ADDFB2
// MODE_RMFB
// MODE_PAGE_FLIP
//   switch a crtc to a different framebuffer
// MODE_DIRTYFB
//   request a framebuffer redraw on hardware that doesn't do so automatically
// MODE_CREATE_DUMB
// MODE_MAP_DUMB
// MODE_DESTROY_DUMB
// MODE_OBJ_GETPROPERTIES
//   get properties of various driver objects
// MODE_OBJ_SETPROPERTY
//   modify properties of various driver objects



uint32_t *fb_ids, *crtc_ids, *connector_ids, *encoder_ids, *plane_ids;
int nfbs, ncrtcs, nconnectors, nencoders, nplanes;
int devfd;


#define ioc(req,arg) if(ioctl(devfd, DRM_IOCTL_##req, arg) < 0) err(1, #req)
#define PU PRIu32

void *alloc(size_t nmemb, size_t size)
{
    void *p = calloc(nmemb, size);
    if(!p) err(1, "alloc");
    return p;
}

void print_modeinfo(struct drm_mode_modeinfo m);
void print_obj_properties(uint32_t obj_id, uint32_t obj_type);
void print_property(uint32_t prop, uint64_t value);
void print_byte(uint8_t c);
void print_framebuffer(uint32_t fb_id);
void print_fbinfo(struct drm_mode_fb_cmd f);

int main(int argc, char **argv)
{
    const char *file = "/dev/drm0";

    if(argc>1)
        file = argv[1];

    if((devfd = open(file, O_RDWR)) < 0)
        err(1, "%s", file);
    printf("%s:\n", file);


    // VERSION
    {
        char name[100], date[100], desc[100];
        struct drm_version v;

        v.name_len = sizeof(name)-1;
        v.date_len = sizeof(date)-1;
        v.desc_len = sizeof(desc)-1;
        v.name = name;
        v.date = date;
        v.desc = desc;

        ioc(VERSION, &v);
        v.name[v.name_len] = '\0';
        v.date[v.date_len] = '\0';
        v.desc[v.desc_len] = '\0';

        printf("  drm v%d.%d.%d, driver %s, %s (%s)\n",
               v.version_major, v.version_minor, v.version_patchlevel,
               v.name, v.desc, v.date);
    }


    // GET_MAGIC
    // AUTH_MAGIC
    {
        struct drm_auth auth;
        ioc(GET_MAGIC, &auth);
        printf("  auth id (magic number): %u\n", auth.magic);

        int r = ioctl(devfd, DRM_IOCTL_AUTH_MAGIC, &auth);
        perror("  authenticate self");
    }


    // GET_UNIQUE
    {
        char unique[100];
        struct drm_unique u;

        u.unique_len = sizeof(unique)-1;
        u.unique = unique;

        ioc(GET_UNIQUE, &u);
        printf("  bus id string: %s\n", u.unique);
    }


    // GET_PCIINFO
    {
        struct drm_pciinfo pi;
        ioc(GET_PCIINFO, &pi);
        printf("  device %d, function %d at pci bus %d, domain %d\n",
               (int)pi.dev, (int)pi.func, (int)pi.bus, (int)pi.domain);
        printf("    vendor %04x, device %04x, subvendor %04x, subdevice %04x\n",
               (unsigned)pi.vendor_id, (unsigned)pi.device_id,
               (unsigned)pi.subvendor_id, (unsigned)pi.subdevice_id);
        printf("    revision %d\n", (int)pi.revision_id);
    }


    // GET_CAP
    {
        struct drm_get_cap gc;

        #define printcap(cap) do {                                      \
                gc.capability = DRM_CAP_##cap;                          \
                ioc(GET_CAP, &gc);                                      \
                printf("    %-20s = %lld\n", #cap, (long long)gc.value);  \
            } while(0)

        printf("  capabilities:\n");
        printcap(DUMB_BUFFER);
        printcap(VBLANK_HIGH_CRTC);
        printcap(DUMB_PREFERRED_DEPTH);
        printcap(DUMB_PREFER_SHADOW);

        gc.capability = DRM_CAP_PRIME;
        ioc(GET_CAP, &gc);
        printf("    %-20s = %s%s\n", "PRIME",
               gc.value & DRM_PRIME_CAP_IMPORT ? "IMPORT, " : "",
               gc.value & DRM_PRIME_CAP_EXPORT ? "EXPORT" : "");

        printcap(TIMESTAMP_MONOTONIC);
        printcap(ASYNC_PAGE_FLIP);
        printcap(CURSOR_WIDTH);
        printcap(CURSOR_HEIGHT);

        #undef printcap
    }


    // SET_CLIENT_CAP
    {
        struct drm_set_client_cap c;

        #define setcap(cap) do {                            \
                c.capability = DRM_CLIENT_CAP_##cap;        \
                c.value = 1;                                \
                ioctl(devfd, DRM_IOCTL_SET_CLIENT_CAP, &c); \
                perror("    " #cap);                        \
            } while(0)

        printf("  set client capabilities:\n");
        setcap(STEREO_3D);
        setcap(UNIVERSAL_PLANES);
        setcap(ATOMIC);

        #undef setcap
    }


    // WAIT_VBLANK
    {
        union drm_wait_vblank vb;
        struct timespec t0,t1;

        vb.request.type = _DRM_VBLANK_RELATIVE;
        vb.request.sequence = 1;
        clock_gettime(CLOCK_MONOTONIC, &t0);
        ioc(WAIT_VBLANK, &vb);
        clock_gettime(CLOCK_MONOTONIC, &t1);
        printf("  vblank no. %u reached at time %ld.%06lds, waited 
%lld.%09lds\n",
               vb.reply.sequence, vb.reply.tval_sec, vb.reply.tval_usec,
               t1.tv_sec-t0.tv_sec, t1.tv_nsec-t0.tv_nsec);

        vb.request.type = _DRM_VBLANK_RELATIVE | _DRM_VBLANK_NEXTONMISS;
        vb.request.sequence = 1;
        ioc(WAIT_VBLANK, &vb);
        vb.request.sequence++;
        clock_gettime(CLOCK_MONOTONIC, &t0);
        ioc(WAIT_VBLANK, &vb);
        clock_gettime(CLOCK_MONOTONIC, &t1);
        double period = (t1.tv_nsec-t0.tv_nsec)/100000/10.0;
        printf("    period %.1fms, freq %.0fHz\n", period, round(1000/period));
    }


    // MODESET_CTL
    {
        struct drm_modeset_ctl c = {.crtc=0, .cmd=0};

        // with KMS, this ioctl is a no-op for any argument.
        // cmd=0 is invalid, so with user-space modesetting, it will fail
        int r = ioctl(devfd, DRM_IOCTL_MODESET_CTL, &c);
        printf("  modesetting: %s\n", r<0 ? "user space" : "kernel");
    }


    // === begin KMS stuff ===


    // MODE_GETRESOURCES
    // MODE_GETPLANERESOURCES
    {
        struct drm_mode_card_res r = {0};

        #define doalloc(what) do { \
                n##what##s = r.count_##what##s; \
                what##_ids = alloc(n##what##s, sizeof(*what##_ids)); \
                r.what##_id_ptr = (uintptr_t)what##_ids; \
            } while(0)

        ioc(MODE_GETRESOURCES, &r);
        doalloc(fb);
        doalloc(crtc);
        doalloc(encoder);
        doalloc(connector);
        ioc(MODE_GETRESOURCES, &r);

        {
            struct drm_mode_get_plane_res r = {0};

            ioc(MODE_GETPLANERESOURCES, &r);
            doalloc(plane);
            ioc(MODE_GETPLANERESOURCES, &r);
        }

        #undef doalloc

        printf("  %"PU" crtcs, %"PU" connectors, %"PU" encoders, "
                 "%"PU" framebuffers, %"PU" planes\n"
               "    min width  %u, max width  %u\n"
               "    min height %u, max height %u\n",
               ncrtcs, nconnectors, nencoders,
               nfbs, nplanes,
               r.min_width, r.max_width,
               r.min_height, r.max_height);
    }


    // MODE_GETCRTC
    // MODE_GETFB
    // MODE_OBJ_GETPROPERTIES
    {
        struct drm_mode_crtc c;

        for(int i=0; i<ncrtcs; i++) {
            c.crtc_id = crtc_ids[i];
            
            printf("  crtc %"PU": ", c.crtc_id);
            ioc(MODE_GETCRTC, &c);
            // NB: set_connectors_ptr and count_connectors are for SETCRTC

            printf("framebuffer %"PU, c.fb_id);
            if(c.x || c.y) printf(",");
            if(c.x) printf(" x-offset %"PU, c.x);
            if(c.y) printf(" y-offset %"PU, c.y);
            if(c.mode_valid) {
                printf("\n");
                print_modeinfo(c.mode);
            } else {
                printf(", no mode\n");
            }

            if(c.fb_id) {
                printf("    fbuf ");
                print_framebuffer(c.fb_id);
            }

            print_obj_properties(c.crtc_id, DRM_MODE_OBJECT_CRTC);
        }
    }


    // MODE_GETCONNECTOR
    // MODE_GETPROPERTY
    // MODE_GETPROPBLOB
    {
        struct drm_mode_get_connector c;
        const char *type, *status;

        for(int i=0; i<nconnectors; i++) {
            c.encoders_ptr = c.modes_ptr = c.props_ptr = c.prop_values_ptr = 0;
            c.count_modes = c.count_props = c.count_encoders = 0;
            c.connector_id = connector_ids[i];
            ioc(MODE_GETCONNECTOR, &c);

            type = status = "*unrecognized*";
            switch(c.connector_type) {
                #define docase(T) case DRM_MODE_CONNECTOR_##T: type = #T; break;
                docase(Unknown)
                docase(VGA)
                docase(DVII)
                docase(DVID)
                docase(DVIA)
                docase(Composite)
                docase(SVIDEO)
                docase(LVDS)
                docase(Component)
                docase(9PinDIN)
                docase(DisplayPort)
                docase(HDMIA)
                docase(HDMIB)
                docase(TV)
                docase(eDP)
                docase(VIRTUAL)
                docase(DSI)
                #undef docase
            }
            switch(c.connection) {
                case connector_status_connected: status = "connected"; break;
                case connector_status_disconnected: status = "disconnected"; 
break;
                case connector_status_unknown: status = "unknown status"; break;
            }
            printf("  connector %"PU": %s, %s", c.connector_id, type, status);
            if(c.mm_width || c.mm_height)
                printf(", %"PU"mm x %"PU"mm", c.mm_width, c.mm_height);
            printf("\n");
            // XXX how to use this?
            //printf("    connector type id: %"PU"\n", c.connector_type_id);
            if(c.subpixel)
                printf("    subpixel layout: %"PU"\n", c.subpixel);

            // get encoders, modes, props
            c.encoders_ptr = (uintptr_t)alloc(c.count_encoders, 
sizeof(uint32_t));
            c.modes_ptr = (uintptr_t)alloc(c.count_modes, sizeof(struct 
drm_mode_modeinfo));
            c.props_ptr = (uintptr_t)alloc(c.count_props, sizeof(uint32_t));
            c.prop_values_ptr = (uintptr_t)alloc(c.count_props, 
sizeof(uint64_t));
            ioc(MODE_GETCONNECTOR, &c);

            printf("    valid encoders:");
            for(int j=0; j<c.count_encoders; j++)
                printf(" %"PU, ((uint32_t *)c.encoders_ptr)[j]);
            printf("\n");

            for(int j=0; j<c.count_modes; j++)
                print_modeinfo(((struct drm_mode_modeinfo *)c.modes_ptr)[j]);

            if(c.count_props > 0) {
                uint32_t *props = (uint32_t *)c.props_ptr;
                uint64_t *values = (uint64_t *)c.prop_values_ptr;

                printf("    properties:\n");
                for(int j=0; j<c.count_props; j++)
                    print_property(props[j], values[j]);
            }
        }
    }


    // MODE_GETENCODER
    {
        struct drm_mode_get_encoder e;
        const char *type;

        for(int i=0; i<nencoders; i++) {
            e.encoder_id = encoder_ids[i];
            ioc(MODE_GETENCODER, &e);

            type = "*unrecognized*";
            switch(e.encoder_type) {
                #define docase(T) case DRM_MODE_ENCODER_##T: type = #T; break;
                docase(NONE)
                docase(DAC)
                docase(TMDS)
                docase(LVDS)
                docase(TVDAC)
                docase(VIRTUAL)
                docase(DSI)
                docase(DPMST)
                #undef docase
            }
            
            printf("  encoder %"PU": %s, ", e.encoder_id, type);
            if(e.crtc_id)
                printf("active on crtc %"PU"\n", e.crtc_id);
            else
                printf("inactive\n");
            printf("    possible crtcs:");
            for(int j=0; j<32; j++)
                if(e.possible_crtcs & (1UL<<j))
                    printf(" %d", crtc_ids[j]);
            printf("\n");
            printf("    possible clones:");
            for(int j=0; j<32; j++)
                if(e.possible_clones & (1UL<<j))
                    printf(" %d", encoder_ids[j]);
            printf("\n");

            print_obj_properties(e.encoder_id, DRM_MODE_OBJECT_ENCODER);
        }
    }


    // MODE_GETFB
    {
        for(int i=0; i<nfbs; i++) {
            printf("  framebuffer %"PU": ", fb_ids[i]);
            print_framebuffer(fb_ids[i]);
        }
    }


    // MODE_GETPLANE
    {
        struct drm_mode_get_plane p;

        for(int i=0; i<nplanes; i++) {
            p.plane_id = plane_ids[i];
            p.count_format_types = 0;

            printf("  plane %"PU": ", p.plane_id);
            ioc(MODE_GETPLANE, &p);

            if(!p.crtc_id && !p.fb_id)
                printf("inactive\n");
            else 
                printf("active on crtc %"PU" using framebuffer %"PU"\n",
                       p.crtc_id, p.fb_id);

            printf("    possible crtcs:");
            for(int j=0; j<32; j++)
                if(p.possible_crtcs & (1UL<<j)) printf(" %d", crtc_ids[j]);
            printf("\n");

            printf("    supported formats:");
            p.format_type_ptr = (uintptr_t)alloc(p.count_format_types, 4);
            ioc(MODE_GETPLANE, &p);
            for(int j=0; j<p.count_format_types; j++)
                printf(" %.4s", (char *)&((uint32_t *)p.format_type_ptr)[j]);
            printf("\n");

            if(p.gamma_size > 0)
                printf("    gamma size %"PU"\n", p.gamma_size);

            print_obj_properties(p.plane_id, DRM_MODE_OBJECT_PLANE);
        }
    }


    // XXX experimentation XXX
    printf("\n--------\n");
    {
        struct drm_mode_fb_cmd fb;
        struct drm_mode_create_dumb cd;
        struct drm_mode_destroy_dumb dd;
        struct drm_mode_set_plane sp;

        cd.width = 1920;
        cd.height = 1080;
        cd.bpp = 32;
        cd.flags = 0;
        ioc(MODE_CREATE_DUMB, &cd);
        printf("created buf: handle %"PU", pitch %"PU", size %"PRIu64"\n",
               cd.handle, cd.pitch, cd.size);

        fb.width = cd.width;
        fb.height = cd.height;
        fb.pitch = cd.pitch;
        fb.bpp = cd.bpp;
        fb.depth = 24;
        fb.handle = cd.handle;
        ioc(MODE_ADDFB, &fb);
        printf("added fb %"PU": ", fb.fb_id);
        print_fbinfo(fb);

        sp.plane_id = plane_ids[0];
        sp.crtc_id = crtc_ids[0];
        sp.fb_id = fb.fb_id;
        sp.flags = 0;
        sp.crtc_x = 100;
        sp.crtc_y = 100;
        sp.crtc_w = 1920-200;
        sp.crtc_h = 1080-200;
        sp.src_x = sp.crtc_x;
        sp.src_y = sp.crtc_y;
        sp.src_w = sp.crtc_w<<16;
        sp.src_h = sp.crtc_h<<16;
        ioc(MODE_SETPLANE, &sp);
        printf("enabled plane %"PU" using fb %"PU"\n", sp.plane_id, sp.fb_id);
        sleep(1);

        sp.fb_id = 0;
        ioc(MODE_SETPLANE, &sp);
        printf("disabled plane %"PU"\n", sp.plane_id);

        ioc(MODE_RMFB, &fb.fb_id);
        printf("removed fb %"PU"\n", fb.fb_id);

        dd.handle = cd.handle;
        ioc(MODE_DESTROY_DUMB, &dd);
        printf("destroyed buf %"PU"\n", dd.handle);
    }


    return 0;
}

void print_modeinfo(struct drm_mode_modeinfo m)
{
    printf("    mode %"PU"x%"PU, m.hdisplay, m.vdisplay);
    if(m.name[0])
        printf(" (\"%s\")", m.name);
    printf(" %.2fHz", (m.clock*1000.0) / (m.htotal*m.vtotal));
    if(m.vrefresh)
        printf(" (\"%"PU"Hz\") ", m.vrefresh);
    
    // pretty-print type/flags
    #define dotype(F) if(m.type & DRM_MODE_TYPE_##F) printf(" %s", #F)
    #define doflag(F) if(m.flags & DRM_MODE_FLAG_##F) printf(" %s", #F)

    dotype(BUILTIN);
    dotype(CLOCK_C);
    dotype(CRTC_C);
    dotype(PREFERRED);
    dotype(DEFAULT);
    dotype(USERDEF);
    dotype(DRIVER);

    doflag(PHSYNC);
    doflag(NHSYNC);
    doflag(PVSYNC);
    doflag(NVSYNC);
    doflag(INTERLACE);
    doflag(DBLSCAN);
    doflag(CSYNC);
    doflag(PCSYNC);
    doflag(NCSYNC);
    doflag(HSKEW);
    doflag(BCAST);
    doflag(PIXMUX);
    doflag(DBLCLK);
    doflag(CLKDIV2);

    #undef dotype
    #undef doflag

    printf("\n");

    printf("         %"PU"x%"PU" hsync %"PU"-%"PU" vsync %"PU"-%"PU,
           m.htotal, m.vtotal,
           m.hsync_start, m.hsync_end,
           m.vsync_start, m.vsync_end);
    if(m.hskew) printf(" hskew %"PU, m.hskew);
    if(m.vscan) printf(" vscan %"PU, m.vscan);
    printf(", pixel clock %"PU"kHz\n", m.clock);
}


// MODE_GETPROPERTY
// MODE_GETPROPBLOB
//
void print_property(uint32_t prop, uint64_t value)
{
    struct drm_mode_get_property p;
    struct drm_mode_property_enum *en;
    uint64_t *values;

    p.prop_id = prop;
    p.values_ptr = p.enum_blob_ptr = 0;
    p.count_values = p.count_enum_blobs = 0;
    ioc(MODE_GETPROPERTY, &p);

    printf("      %s:", p.name);

    if(p.flags & DRM_MODE_PROP_ENUM) {
        en = alloc(p.count_enum_blobs, sizeof(struct drm_mode_property_enum));
        p.enum_blob_ptr = (uintptr_t)en;
        p.count_values = 0;
        ioc(MODE_GETPROPERTY, &p);

        for(int i=0; i<p.count_enum_blobs; i++) {
            if(i>0)
                printf(",");
            if(en[i].value == value)
                printf(" [%s]", en[i].name);
            else
                printf(" %s", en[i].name);
        }
    }
    else if(p.flags & DRM_MODE_PROP_BITMASK) {
        en = alloc(p.count_enum_blobs, sizeof(struct drm_mode_property_enum));
        p.enum_blob_ptr = (uintptr_t)en;
        p.count_values = 0;
        ioc(MODE_GETPROPERTY, &p);

        for(int i=0; i<p.count_enum_blobs; i++) {
            if(i>0)
                printf(",");
            if(value & (1ULL << en[i].value))
                printf(" [%s]", en[i].name);
            else
                printf(" %s", en[i].name);
        }
    }
    else if(p.flags & DRM_MODE_PROP_BLOB) {
        struct drm_mode_get_blob b;

        if(value) {
            b.blob_id = value;
            b.length = b.data = 0;
            ioc(MODE_GETPROPBLOB, &b);
            b.data = (uintptr_t)alloc(b.length, 1);
            ioc(MODE_GETPROPBLOB, &b);

            printf(" \"");
            for(int i=0; i<b.length; i++)
                print_byte(((uint8_t *)b.data)[i]);
            printf("\"");
        }
    }
    else if((p.flags & DRM_MODE_PROP_RANGE) && p.count_values == 2) {
        values = alloc(p.count_values, sizeof(uint64_t));
        p.values_ptr = (uintptr_t)values;
        p.count_enum_blobs = 0;
        ioc(MODE_GETPROPERTY, &p);

        printf(" %"PRIu64" (%"PRIu64" - %"PRIu64")",
               value, values[0], values[1]);
    }
    else {
        printf(" %"PRIu64" (flags=0x%"PRIx32", count_values=%"PU")",
               value, p.flags, p.count_values);
    }

    #define doflag(F) if(p.flags & DRM_MODE_PROP_##F) printf(" %s", #F)
    #define dotype(T) case DRM_MODE_PROP_##T: printf(" %s", #T); break;

    printf(" ");
    doflag(PENDING);
    doflag(IMMUTABLE);
    doflag(RANGE);
    doflag(ENUM);
    doflag(BLOB);
    doflag(BITMASK);

    switch(p.flags & DRM_MODE_PROP_EXTENDED_TYPE) {
        dotype(OBJECT)
        dotype(SIGNED_RANGE)
    }

    #undef doflag
    #undef dotype

    printf("\n");
}

void print_byte(uint8_t c)
{
    if(isprint(c))
        printf("%c", c);
    else
        printf("\\x%.2x", (unsigned)c);
}

// MODE_OBJ_GETPROPERTIES
//
void print_obj_properties(uint32_t obj_id, uint32_t obj_type)
{
    struct drm_mode_obj_get_properties p;
    uint32_t *props, *values;
    int r;

    p.props_ptr = p.prop_values_ptr = p.count_props = 0;
    p.obj_id = obj_id;
    p.obj_type = obj_type;
    r = ioctl(devfd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &p);
    if(r<0 && errno == EINVAL)
        return; // no properties on this object

    if(p.count_props > 0) {
        props = alloc(p.count_props, sizeof(uint32_t));
        values = alloc(p.count_props, sizeof(uint64_t));
        p.props_ptr = (uintptr_t)props;
        p.prop_values_ptr = (uintptr_t)values;
        ioc(MODE_OBJ_GETPROPERTIES, &p);

        printf("    properties:\n");
        for(int j=0; j<p.count_props; j++)
            print_property(props[j], values[j]);
    }
}

// MODE_GETFB
//
void print_framebuffer(uint32_t fb_id)
{
    struct drm_mode_fb_cmd f;

    f.fb_id = fb_id;
    ioc(MODE_GETFB, &f);
    print_fbinfo(f);
}

void print_fbinfo(struct drm_mode_fb_cmd f)
{
    printf("%"PU"x%"PU" %"PU" bpp, pitch %"PU", depth %"PU
           ", driver handle %"PU"\n",
           f.width, f.height, f.bpp, f.pitch, f.depth, f.handle);
}

Reply via email to