summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibEDID/EDID.h
blob: f5479253cdb62541e87ae9cfae6f2734dc036be9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
/*
 * Copyright (c) 2022, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/ByteBuffer.h>
#include <AK/ByteReader.h>
#include <AK/Concepts.h>
#include <AK/Endian.h>
#include <AK/Error.h>
#include <AK/FixedPoint.h>
#include <AK/Forward.h>
#include <AK/Span.h>
#include <AK/Vector.h>
#include <LibEDID/DMT.h>
#include <LibEDID/Definitions.h>
#include <LibEDID/VIC.h>

#ifdef KERNEL
#    include <Kernel/Library/KString.h>
#else
#    include <AK/DeprecatedString.h>
#endif

namespace EDID {

namespace Definitions {
struct EDID;
struct DetailedTiming;
struct DisplayDescriptor;
struct ExtensionBlock;
}

class Parser final {
    friend class CEA861ExtensionBlock;

public:
    static constexpr size_t BufferSize = 128;
    using RawBytes = unsigned char[BufferSize];

protected:
    class DisplayFeatures {
    public:
        bool supports_standby() const { return (m_features & (1 << 7)) != 0; }
        bool supports_suspend() const { return (m_features & (1 << 6)) != 0; }
        bool supports_off() const { return (m_features & (1 << 5)) != 0; }

        bool preferred_timing_mode_includes_pixel_format_and_refresh_rate() const
        {
            if (m_edid_revision < 4)
                return true; // Bit 1 must be set to 1
            return (m_features & (1 << 1)) != 0;
        }

        bool srgb_is_default_color_space() const { return (m_features & (1 << 2)) != 0; }

        enum class Frequency : u8 {
            Continuous,
            NonContinuous,
            DefaultGTF,
            VESA_DMT
        };
        Frequency frequency() const
        {
            if (m_edid_revision < 4)
                return ((m_features & 1) != 0) ? Frequency::DefaultGTF : Frequency::VESA_DMT;
            return ((m_features & 1) != 0) ? Frequency::Continuous : Frequency::NonContinuous;
        }

    protected:
        DisplayFeatures(u8 features, u8 edid_revision)
            : m_features(features)
            , m_edid_revision(edid_revision)
        {
        }

        u8 m_features { 0 };
        u8 m_edid_revision { 0 };
    };

public:
    static ErrorOr<Parser> from_bytes(ReadonlyBytes);
    static ErrorOr<Parser> from_bytes(ByteBuffer&&);

#ifndef KERNEL
    static ErrorOr<Parser> from_display_connector_device(int);
    static ErrorOr<Parser> from_display_connector_device(DeprecatedString const&);
#endif

    StringView legacy_manufacturer_id() const;
#ifndef KERNEL
    DeprecatedString manufacturer_name() const;
#endif

    u16 product_code() const;
    u32 serial_number() const;

    class DigitalDisplayFeatures final : public DisplayFeatures {
        friend class Parser;

    public:
        enum class SupportedColorEncodings : u8 {
            RGB444,
            RGB444_YCrCb444,
            RGB444_YCrCb422,
            RGB444_YCrCb444_YCrCb422
        };

        SupportedColorEncodings supported_color_encodings() const { return (SupportedColorEncodings)((m_features >> 3) & 3); }

    private:
        DigitalDisplayFeatures(u8 features, u8 edid_revision)
            : DisplayFeatures(features, edid_revision)
        {
        }
    };

    class AnalogDisplayFeatures final : public DisplayFeatures {
        friend class Parser;

    public:
        enum class DisplayColorType : u8 {
            MonochromeOrGrayscale,
            RGB,
            NonRGB,
            Undefined
        };

        DisplayColorType display_color_type() const { return (DisplayColorType)((m_features >> 3) & 3); }

    private:
        AnalogDisplayFeatures(u8 features, u8 edid_revision)
            : DisplayFeatures(features, edid_revision)
        {
        }
    };

    class DigitalDisplay final {
        friend class Parser;

    public:
        enum class ColorBitDepth : u8 {
            Undefined = 0,
            BPP_6,
            BPP_8,
            BPP_10,
            BPP_12,
            BPP_14,
            BPP_16,
            Reserved
        };
        enum class SupportedInterface : u8 {
            Undefined = 0,
            DVI,
            HDMI_A,
            HDMI_B,
            MDDI,
            DisplayPort,
            Reserved
        };

        ColorBitDepth color_bit_depth() const { return (ColorBitDepth)((m_video_input_definition >> 4) & 7); }
        SupportedInterface supported_interface() const { return ((m_video_input_definition & 0xf) <= 5) ? (SupportedInterface)(m_video_input_definition & 0xf) : SupportedInterface::Reserved; }

        DigitalDisplayFeatures const& features() { return m_features; }

    private:
        DigitalDisplay(u8 video_input_definition, u8 features, u8 edid_revision)
            : m_video_input_definition(video_input_definition)
            , m_features(features, edid_revision)
        {
        }

        u8 m_video_input_definition { 0 };
        DigitalDisplayFeatures m_features;
    };
    Optional<DigitalDisplay> digital_display() const;

    class AnalogDisplay final {
        friend class Parser;

    public:
        bool separate_sync_h_and_v_supported() const { return (m_video_input_definition & (1 << 3)) != 0; }

    private:
        AnalogDisplay(u8 video_input_definition, u8 features, u8 edid_revision)
            : m_video_input_definition(video_input_definition)
            , m_features(features, edid_revision)
        {
        }

        u8 m_video_input_definition { 0 };
        AnalogDisplayFeatures m_features;
    };
    Optional<AnalogDisplay> analog_display() const;

    class ScreenSize final {
        friend class Parser;

    public:
        unsigned horizontal_cm() const { return m_horizontal_cm; }
        unsigned vertical_cm() const { return m_vertical_cm; }

    private:
        ScreenSize(u8 horizontal_cm, u8 vertical_cm)
            : m_horizontal_cm(horizontal_cm)
            , m_vertical_cm(vertical_cm)
        {
        }

        u8 m_horizontal_cm { 0 };
        u8 m_vertical_cm { 0 };
    };
    Optional<ScreenSize> screen_size() const;

    class ScreenAspectRatio final {
        friend class Parser;

    public:
        enum class Orientation {
            Landscape,
            Portrait
        };

        Orientation orientation() const { return m_orientation; }
        auto ratio() const { return m_ratio; }

    private:
        ScreenAspectRatio(Orientation orientation, FixedPoint<16> ratio)
            : m_orientation(orientation)
            , m_ratio(ratio)
        {
        }

        Orientation m_orientation { Orientation::Landscape };
        FixedPoint<16> m_ratio {};
    };
    Optional<ScreenAspectRatio> aspect_ratio() const;

    Optional<FixedPoint<16>> gamma() const;

    class EstablishedTiming final {
        friend class Parser;

    public:
        enum class Source {
            IBM,
            Apple,
            VESA,
            Manufacturer
        };

        ALWAYS_INLINE Source source() const { return m_source; }
        ALWAYS_INLINE unsigned width() const { return m_width; };
        ALWAYS_INLINE unsigned height() const { return m_height; }

        ALWAYS_INLINE unsigned refresh_rate() const
        {
            if (m_source == Source::Manufacturer)
                return 0;
            return m_refresh_rate_or_manufacturer_specific;
        }

        ALWAYS_INLINE u8 manufacturer_specific() const
        {
            VERIFY(m_source == Source::Manufacturer);
            return m_refresh_rate_or_manufacturer_specific;
        }

        ALWAYS_INLINE u8 dmt_id() const { return m_dmt_id; }

    private:
        constexpr EstablishedTiming(Source source, u16 width, u16 height, u8 refresh_rate_or_manufacturer_specific, u8 dmt_id = 0)
            : m_source(source)
            , m_width(width)
            , m_height(height)
            , m_refresh_rate_or_manufacturer_specific(refresh_rate_or_manufacturer_specific)
            , m_dmt_id(dmt_id)
        {
        }

        Source m_source { Source::IBM };
        u16 m_width { 0 };
        u16 m_height { 0 };
        u8 m_refresh_rate_or_manufacturer_specific { 0 };
        u8 m_dmt_id { 0 };
    };

    ErrorOr<IterationDecision> for_each_established_timing(Function<IterationDecision(EstablishedTiming const&)>) const;

    class StandardTiming final {
        friend class Parser;

    public:
        enum class AspectRatio {
            AR_16_10,
            AR_4_3,
            AR_5_4,
            AR_16_9
        };
        unsigned width() const { return m_width; }
        unsigned height() const { return m_height; }
        unsigned refresh_rate() const { return m_refresh_rate; }
        AspectRatio aspect_ratio() const { return m_aspect_ratio; }
        u8 dmt_id() const { return m_dmt_id; }

    private:
        constexpr StandardTiming(u16 width, u16 height, u8 refresh_rate, AspectRatio aspect_ratio, u8 dmt_id)
            : m_width(width)
            , m_height(height)
            , m_refresh_rate(refresh_rate)
            , m_aspect_ratio(aspect_ratio)
            , m_dmt_id(dmt_id)
        {
        }

        u16 m_width { 0 };
        u16 m_height { 0 };
        u8 m_refresh_rate { 0 };
        AspectRatio m_aspect_ratio { AspectRatio::AR_16_10 };
        u8 m_dmt_id { 0 };
    };

    ErrorOr<IterationDecision> for_each_standard_timing(Function<IterationDecision(StandardTiming const&)>) const;

    class DetailedTiming final {
        friend class Parser;
        friend class CEA861ExtensionBlock;

    public:
        u32 pixel_clock_khz() const;
        u16 horizontal_addressable_pixels() const;
        u16 horizontal_blanking_pixels() const;
        u16 vertical_addressable_lines() const;
        u16 vertical_blanking_lines() const;
        u16 horizontal_front_porch_pixels() const;
        ALWAYS_INLINE u16 horizontal_back_porch_pixels() const { return horizontal_blanking_pixels() - horizontal_sync_pulse_width_pixels() - horizontal_front_porch_pixels(); }
        u16 horizontal_sync_pulse_width_pixels() const;
        u16 vertical_front_porch_lines() const;
        ALWAYS_INLINE u16 vertical_back_porch_lines() const { return vertical_blanking_lines() - vertical_sync_pulse_width_lines() - vertical_front_porch_lines(); }
        u16 vertical_sync_pulse_width_lines() const;
        u16 horizontal_image_size_mm() const;
        u16 vertical_image_size_mm() const;
        u8 horizontal_right_or_left_border_pixels() const;
        u8 vertical_top_or_bottom_border_lines() const;

        bool is_interlaced() const;
        FixedPoint<16, u32> refresh_rate() const;

    private:
        DetailedTiming(Parser const& edid, Definitions::DetailedTiming const* detailed_timings)
            : m_edid(edid)
            , m_detailed_timings(*detailed_timings)
        {
        }

        u16 vertical_addressable_lines_raw() const;

        Parser const& m_edid;
        Definitions::DetailedTiming const& m_detailed_timings;
    };

    ErrorOr<IterationDecision> for_each_detailed_timing(Function<IterationDecision(DetailedTiming const&, unsigned)>) const;
    Optional<DetailedTiming> detailed_timing(size_t) const;

#ifndef KERNEL
    DeprecatedString display_product_name() const;
    DeprecatedString display_product_serial_number() const;
#endif

    ErrorOr<IterationDecision> for_each_short_video_descriptor(Function<IterationDecision(unsigned, bool, VIC::Details const&)>) const;

    class CoordinatedVideoTiming final {
        friend class Parser;

    public:
        enum class AspectRatio : u8 {
            AR_4_3 = 0,
            AR_16_9 = 1,
            AR_16_10 = 2,
            AR_15_9 = 3
        };

        u16 horizontal_addressable_pixels() const;
        u16 vertical_addressable_lines() const;
        AspectRatio aspect_ratio() const;
        u16 preferred_refresh_rate();

        ALWAYS_INLINE DMT::CVT cvt_code() const { return m_cvt; }

    private:
        CoordinatedVideoTiming(DMT::CVT const& cvt)
            : m_cvt(cvt)
        {
        }

        DMT::CVT m_cvt;
    };

    ErrorOr<IterationDecision> for_each_coordinated_video_timing(Function<IterationDecision(CoordinatedVideoTiming const&)>) const;

    ErrorOr<IterationDecision> for_each_extension_block(Function<IterationDecision(unsigned, u8, u8, ReadonlyBytes)>) const;

    struct SupportedResolution {
        unsigned width { 0 };
        unsigned height { 0 };
        struct RefreshRate {
            FixedPoint<16, u32> rate;
            bool preferred { false };

            bool operator<(RefreshRate const& rhs) const { return rate < rhs.rate; }
        };
        Vector<RefreshRate, 4> refresh_rates;
    };
    ErrorOr<Vector<SupportedResolution>> supported_resolutions() const;

    Parser() = default;
    Parser(Parser&&) = default;
    Parser(Parser const&);
    Parser& operator=(Parser&&);
    Parser& operator=(Parser const&);

    bool operator==(Parser const& other) const;

    StringView version() const;

    auto bytes() const { return m_bytes; }

private:
    Parser(ReadonlyBytes);
    Parser(ByteBuffer&&);

    ErrorOr<void> parse();

    template<typename T>
    T read_host(T const*) const;

    template<Integral T>
    requires(sizeof(T) > 1)
    T read_le(T const*) const;

    template<Integral T>
    requires(sizeof(T) > 1)
    T read_be(T const*) const;

    Definitions::EDID const& raw_edid() const;
    ErrorOr<IterationDecision> for_each_display_descriptor(Function<IterationDecision(u8, Definitions::DisplayDescriptor const&)>) const;

    ByteBuffer m_bytes_buffer;
    ReadonlyBytes m_bytes;
    u8 m_revision { 0 };
#ifdef KERNEL
    OwnPtr<Kernel::KString> m_version;
#else
    DeprecatedString m_version;
#endif
    char m_legacy_manufacturer_id[4] {};
    bool m_legacy_manufacturer_id_valid { false };
};

}