1package drm
2
3import (
4 "math"
5 "testing"
6)
7
8func TestEDIDHDR(t *testing.T) {
9 d := make([]byte, 256)
10 copy(d, "\x00\xff\xff\xff\xff\xff\xff\x00")
11 d[20] = 0xb0 // digital, 10 bits per color
12 d[25], d[26] = 0b_10_01_11_00, 0b_01_11_10_00
13 d[27], d[28] = 128, 64
14 d[29], d[30] = 32, 16
15 d[31], d[32] = 8, 4
16 d[33], d[34] = 2, 1
17 d[126] = 1
18 d[128], d[130] = 2, 15
19 // CTA extended colorimetry: BT2020RGB + BT2020YCC.
20 copy(d[132:], []byte{0xe3, 5, 0xc0, 0})
21 // EOTF SDR, PQ, HLG; static type 1; max 200, average 100, min 200/100.
22 copy(d[136:], []byte{0xe6, 6, 0x0d, 1, 64, 32, 255})
23 m := parseEDID(d)
24 if m.BitsPerColor != 10 || !m.HDR.SDR || !m.HDR.PQ || !m.HDR.HLG || !m.HDR.StaticType1 || !m.HDR.BT2020RGB || !m.HDR.BT2020YCC {
25 t.Fatalf("%+v", m)
26 }
27 if m.Chromaticity.RedX != float64(128*4+2)/1024 || m.Chromaticity.GreenY != float64(16*4)/1024 || m.Chromaticity.WhiteY != float64(1*4)/1024 {
28 t.Fatalf("chromaticity: %+v", m.Chromaticity)
29 }
30 if math.Abs(m.HDR.MaxLuminance-200) > 1e-9 || math.Abs(m.HDR.MaxFrameAverage-100) > 1e-9 || math.Abs(m.HDR.MinLuminance-2) > 1e-9 {
31 t.Fatalf("luminances: %+v", m.HDR)
32 }
33 for _, n := range []int{0, 7, 127, 128, 129, 255} {
34 got := parseEDID(d[:n])
35 if got.HDR.PQ {
36 t.Fatalf("truncated %d: %+v", n, got)
37 }
38 }
39 d[132] = 0xff // block exceeds DTD boundary
40 if parseEDID(d).HDR.PQ {
41 t.Fatal("parsed malformed block")
42 }
43 d[132] = 0xe3
44 d[130] = 3 // invalid CTA offset
45 if parseEDID(d).HDR.PQ {
46 t.Fatal("parsed invalid CTA offset")
47 }
48 d[130] = 15
49 d[136] = 0xe2 // HDR payload without mandatory descriptor byte
50 if parseEDID(d).HDR.PQ {
51 t.Fatal("parsed truncated HDR payload")
52 }
53 d[20] = 0 // analog input cannot specify digital bit depth
54 if parseEDID(d).BitsPerColor != 0 {
55 t.Fatal("analog input has bit depth")
56 }
57}
58
59func TestDetectHDR(t *testing.T) {
60 m := Monitor{HDR: HDRMetadata{PQ: true, BT2020RGB: true, StaticType1: true}}
61 p := connectorHDRProps{Metadata: 1, Colorspace: 2, MaxBPC: 3, HasDefault: true}
62 cases := []struct {
63 name string
64 m Monitor
65 p connectorHDRProps
66 reason string
67 }{
68 {"ready", m, p, ""},
69 {"no PQ", Monitor{HDR: HDRMetadata{BT2020RGB: true, StaticType1: true}}, p, "edid: no PQ"},
70 {"no RGB", Monitor{HDR: HDRMetadata{PQ: true, StaticType1: true}}, p, "edid: no BT2020RGB"},
71 {"no type 1", Monitor{HDR: HDRMetadata{PQ: true, BT2020RGB: true}}, p, "edid: no static metadata type 1"},
72 {"no metadata", m, connectorHDRProps{Colorspace: 2, MaxBPC: 3, HasDefault: true}, "connector: no HDR_OUTPUT_METADATA"},
73 {"no colorspace", m, connectorHDRProps{Metadata: 1, MaxBPC: 3}, "connector: no BT2020_RGB Colorspace"},
74 {"no bpc", m, connectorHDRProps{Metadata: 1, Colorspace: 2, HasDefault: true}, "connector: max bpc below 10"},
75 }
76 for _, tt := range cases {
77 t.Run(tt.name, func(t *testing.T) {
78 c := detectHDR(tt.m, tt.p)
79 if c.Capable != (tt.reason == "") || c.Reason != tt.reason {
80 t.Fatalf("%+v", c)
81 }
82 })
83 }
84}