pax_global_header00006660000000000000000000000064143731207020014511gustar00rootroot0000000000000052 comment=92b031749c0fe84ef5cdf895067b84a829920e25 libdisplay-info-0.1.1/000077500000000000000000000000001437312070200145755ustar00rootroot00000000000000libdisplay-info-0.1.1/.editorconfig000066400000000000000000000004011437312070200172450ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true indent_style = tab indent_size = 8 max_line_length = 80 [*.py] indent_style = space indent_size = 4 [*.yml] indent_style = space indent_size = 2 libdisplay-info-0.1.1/.gitlab-ci.yml000066400000000000000000000064661437312070200172450ustar00rootroot00000000000000include: - project: 'freedesktop/ci-templates' ref: 3f37cc0e461f5b0c815409bf6f55759f26a74e9c file: - '/templates/ci-fairy.yml' - '/templates/alpine.yml' # When updating the prepare-container step, make sure to bump # FDO_DISTRIBUTION_TAG, otherwise the container won't get rebuilt. # To force a rebuild of the container, use: # $ git push -f -o ci.variable="FDO_FORCE_REBUILD=1" variables: FDO_UPSTREAM_REPO: 'emersion/libdisplay-info' FDO_DISTRIBUTION_TAG: '2023-01-31.0' stages: - "Contribution checks" - "Prepare container" - "Build and test" - "Publish" check-mr: extends: .fdo.ci-fairy stage: "Contribution checks" script: - ci-fairy check-commits --signed-off-by - ci-fairy check-merge-request --require-allow-collaboration rules: - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"' when: always - when: never check-test-data: extends: .fdo.ci-fairy stage: "Contribution checks" script: - | for f in ./test/data/*.edid; do grep -q "^$(basename ${f%.edid})\s" ./test/data/README.md [[ $? -ne 0 ]] && echo "$f not in README.md" && exit 1 done exit 0 rules: - when: on_success prepare-container: extends: .fdo.container-build@alpine@x86_64 stage: "Prepare container" variables: FDO_BASE_IMAGE: alpine:3.17 FDO_DISTRIBUTION_PACKAGES: | build-base clang compiler-rt meson make git gcovr py3-pygments go hwdata FDO_DISTRIBUTION_EXEC: | git clone git://linuxtv.org/edid-decode.git cd edid-decode git checkout 915b0ce5329f417d2c3f84ddab3d443dd0e01b61 make make install cd .. rm -rf edid-decode go install git.sr.ht/~emersion/gyosu@latest mv ~/go/bin/gyosu /usr/bin/ rm -rf ~/go rules: - when: on_success build-gcc: extends: .fdo.distribution-image@alpine stage: "Build and test" script: - CC=gcc meson setup build/ --fatal-meson-warnings -Dwerror=true -Db_coverage=true - ninja -C build/ - ninja -C build/ test - ninja -C build/ -j1 coverage-xml coverage-html artifacts: when: always paths: - build/meson-logs/ reports: junit: build/meson-logs/testlog.junit.xml coverage_report: coverage_format: cobertura path: build/meson-logs/coverage.xml rules: - when: on_success build-clang: extends: .fdo.distribution-image@alpine stage: "Build and test" script: - CC=clang meson setup build/ --fatal-meson-warnings -Dwerror=true -Db_sanitize=address,undefined -Db_lundef=false - ninja -C build/ - ninja -C build/ test artifacts: when: always paths: - build/meson-logs/ reports: junit: build/meson-logs/testlog.junit.xml rules: - when: on_success build-docs: extends: .fdo.distribution-image@alpine stage: "Build and test" script: - gyosu -I$PWD/include/ -fexported-symbols='di_*' -ffile-prefix-map=$PWD/include/= -fsite-name=libdisplay-info -o public $PWD/include/libdisplay-info/ artifacts: paths: - public/ rules: - when: on_success pages: extends: .fdo.distribution-image@alpine stage: "Publish" script: - "true" artifacts: paths: - public/ rules: - if: '$CI_PROJECT_PATH == "emersion/libdisplay-info" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' when: on_success - when: never libdisplay-info-0.1.1/LICENSE000066400000000000000000000021011437312070200155740ustar00rootroot00000000000000MIT License Copyright (c) 2022 The libdisplay-info Contributors 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 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. libdisplay-info-0.1.1/README.md000066400000000000000000000060651437312070200160630ustar00rootroot00000000000000# libdisplay-info EDID and DisplayID library. Goals: - Provide a set of high-level, easy-to-use, opinionated functions as well as low-level functions to access detailed information. - Simplicity and correctness over performance and resource usage. - Well-tested and fuzzed. Documentation is available on the [website]. ## Using The public API headers are categorised as either high-level or low-level API as per the comments in the header files. Users of libdisplay-info should prefer high-level API over low-level API when possible. If high-level API lacks needed features, please propose additions to the high-level API upstream before using low-level API to get what you need. If the additions are rejected, you are welcome to use the low-level API. This policy is aimed to propagate best practises when interpreting EDID and DisplayID information which can often be cryptic or even inconsistent. libdisplay-info uses [semantic versioning]. The public API is not yet stable. ## Contributing Open issues and merge requests on the [GitLab project]. In general, the [Wayland contribution guidelines] should be followed. In particular, each commit must carry a Signed-off-by tag to denote that the submitter adheres to the [Developer Certificate of Origin 1.1]. This project follows the [freedesktop.org Contributor Covenant]. ## Building libdisplay-info has the following dependencies: - [hwdata] for the PNP ID database used at build-time only. libdisplay-info is built using [Meson]: meson setup build/ ninja -C build/ ## Testing The low-level EDID library is tested against [edid-decode]. `test/data/` contains a small collection of EDID blobs and diffs between upstream `edid-decode` and our `di-edid-decode` clone. Our CI ensures the diffs are up-to-date. A patch should never make the diffs grow larger. To re-generate the test data, run `ninja -C build/ gen-test-data`. To run the test suite locally, you need to use [edid-decode] of the git revision mentioned in `.gitlab-ci.yml`. Otherwise you may experience false failures. The latest code coverage report is available on [GitLab CI][coverage]. ## Fuzzing To fuzz libdisplay-info with [AFL], the library needs to be instrumented: CC=afl-gcc meson build/ ninja -C build/ afl-fuzz -i test/data/ -o afl/ build/di-edid-decode/di-edid-decode [website]: https://emersion.pages.freedesktop.org/libdisplay-info/ [semantic versioning]: https://semver.org/ [GitLab project]: https://gitlab.freedesktop.org/emersion/libdisplay-info [Wayland contribution guidelines]: https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/CONTRIBUTING.md [Developer Certificate of Origin 1.1]: https://developercertificate.org/ [freedesktop.org Contributor Covenant]: https://www.freedesktop.org/wiki/CodeOfConduct/ [hwdata]: https://github.com/vcrhonek/hwdata [Meson]: https://mesonbuild.com/ [coverage]: https://gitlab.freedesktop.org/emersion/libdisplay-info/-/jobs/artifacts/main/file/build/meson-logs/coveragereport/index.html?job=build-gcc [edid-decode]: https://git.linuxtv.org/edid-decode.git/ [AFL]: https://lcamtuf.coredump.cx/afl/ libdisplay-info-0.1.1/cta-vic-table.c000066400000000000000000001722261437312070200173660ustar00rootroot00000000000000/* DO NOT EDIT! This file has been generated by gen-cta-vic.py from ANSI-CTA-861-H-Final.pdf. */ #include "cta.h" const struct di_cta_video_format _di_cta_video_formats[] = { [60] = { .vic = 60, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 59400000, .h_front = 1760, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [65] = { .vic = 65, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 59400000, .h_front = 1760, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [61] = { .vic = 61, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 2420, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [66] = { .vic = 66, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 2420, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [62] = { .vic = 62, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 1760, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [67] = { .vic = 67, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 1760, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [108] = { .vic = 108, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 90000000, .h_front = 960, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [109] = { .vic = 109, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 90000000, .h_front = 960, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [32] = { .vic = 32, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 638, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [72] = { .vic = 72, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 638, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [33] = { .vic = 33, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [73] = { .vic = 73, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [34] = { .vic = 34, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [74] = { .vic = 74, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [111] = { .vic = 111, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 638, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [112] = { .vic = 112, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 638, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [79] = { .vic = 79, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 59400000, .h_front = 1360, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [80] = { .vic = 80, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 59400000, .h_front = 1228, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [81] = { .vic = 81, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 59400000, .h_front = 700, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [110] = { .vic = 110, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 99000000, .h_front = 810, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [86] = { .vic = 86, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 99000000, .h_front = 998, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 11, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [87] = { .vic = 87, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 90000000, .h_front = 448, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [88] = { .vic = 88, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 118800000, .h_front = 768, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [113] = { .vic = 113, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 198000000, .h_front = 998, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 11, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [93] = { .vic = 93, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 1276, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [103] = { .vic = 103, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 1276, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [94] = { .vic = 94, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 1056, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [104] = { .vic = 104, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 1056, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [95] = { .vic = 95, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 176, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [105] = { .vic = 105, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 176, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [114] = { .vic = 114, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 1276, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [116] = { .vic = 116, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 1276, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [98] = { .vic = 98, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 1020, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [99] = { .vic = 99, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 968, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [100] = { .vic = 100, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 88, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [115] = { .vic = 115, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 1020, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [121] = { .vic = 121, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 396000000, .h_front = 1996, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 22, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [122] = { .vic = 122, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 396000000, .h_front = 1696, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 22, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [123] = { .vic = 123, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 396000000, .h_front = 664, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 22, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [124] = { .vic = 124, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 742500000, .h_front = 746, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 297, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [194] = { .vic = 194, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 2552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [202] = { .vic = 202, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 2552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [195] = { .vic = 195, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 2352, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [203] = { .vic = 203, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 2352, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [196] = { .vic = 196, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [204] = { .vic = 204, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [197] = { .vic = 197, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2376000000, .h_front = 2552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [205] = { .vic = 205, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2376000000, .h_front = 2552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [210] = { .vic = 210, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1485000000, .h_front = 1492, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 594, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [211] = { .vic = 211, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1485000000, .h_front = 2492, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [212] = { .vic = 212, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 1485000000, .h_front = 288, .h_sync = 176, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [213] = { .vic = 213, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2970000000, .h_front = 1492, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 594, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [17] = { .vic = 17, .h_active = 720, .v_active = 576, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 12, .h_sync = 64, .h_back = 68, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [18] = { .vic = 18, .h_active = 720, .v_active = 576, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 12, .h_sync = 64, .h_back = 68, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [19] = { .vic = 19, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 440, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [68] = { .vic = 68, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 440, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [20] = { .vic = 20, .h_active = 1920, .v_active = 1080, .interlaced = true, .pixel_clock_hz = 74250000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 2, .v_sync = 5, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [21] = { .vic = 21, .h_active = 1440, .v_active = 576, .interlaced = true, .pixel_clock_hz = 27000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [22] = { .vic = 22, .h_active = 1440, .v_active = 576, .interlaced = true, .pixel_clock_hz = 27000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [23] = { .vic = 23, .h_active = 1440, .v_active = 288, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 424, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [24] = { .vic = 24, .h_active = 1440, .v_active = 288, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 424, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [25] = { .vic = 25, .h_active = 2880, .v_active = 576, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 48, .h_sync = 252, .h_back = 276, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [26] = { .vic = 26, .h_active = 2880, .v_active = 576, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 48, .h_sync = 252, .h_back = 276, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [27] = { .vic = 27, .h_active = 2880, .v_active = 288, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 48, .h_sync = 252, .h_back = 276, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 424, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [28] = { .vic = 28, .h_active = 2880, .v_active = 288, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 48, .h_sync = 252, .h_back = 276, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 424, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [29] = { .vic = 29, .h_active = 1440, .v_active = 576, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 24, .h_sync = 128, .h_back = 136, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [30] = { .vic = 30, .h_active = 1440, .v_active = 576, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 24, .h_sync = 128, .h_back = 136, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [31] = { .vic = 31, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [75] = { .vic = 75, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [37] = { .vic = 37, .h_active = 2880, .v_active = 576, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 48, .h_sync = 256, .h_back = 272, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [38] = { .vic = 38, .h_active = 2880, .v_active = 576, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 48, .h_sync = 256, .h_back = 272, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [39] = { .vic = 39, .h_active = 1920, .v_active = 1080, .interlaced = true, .pixel_clock_hz = 72000000, .h_front = 32, .h_sync = 168, .h_back = 184, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 23, .v_sync = 5, .v_back = 57, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [82] = { .vic = 82, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 82500000, .h_front = 260, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [89] = { .vic = 89, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 185625000, .h_front = 548, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [96] = { .vic = 96, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 1056, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [106] = { .vic = 106, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 1056, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [101] = { .vic = 101, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 968, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [125] = { .vic = 125, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 742500000, .h_front = 1096, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [198] = { .vic = 198, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2376000000, .h_front = 2352, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [206] = { .vic = 206, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2376000000, .h_front = 2352, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [214] = { .vic = 214, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2970000000, .h_front = 2492, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [1] = { .vic = 1, .h_active = 640, .v_active = 480, .interlaced = false, .pixel_clock_hz = 25175000, .h_front = 16, .h_sync = 96, .h_back = 48, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 10, .v_sync = 2, .v_back = 33, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [2] = { .vic = 2, .h_active = 720, .v_active = 480, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 16, .h_sync = 62, .h_back = 60, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [3] = { .vic = 3, .h_active = 720, .v_active = 480, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 16, .h_sync = 62, .h_back = 60, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [4] = { .vic = 4, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 110, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [69] = { .vic = 69, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 74250000, .h_front = 110, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [5] = { .vic = 5, .h_active = 1920, .v_active = 1080, .interlaced = true, .pixel_clock_hz = 74250000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 2, .v_sync = 5, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [6] = { .vic = 6, .h_active = 1440, .v_active = 480, .interlaced = true, .pixel_clock_hz = 27000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [7] = { .vic = 7, .h_active = 1440, .v_active = 480, .interlaced = true, .pixel_clock_hz = 27000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [8] = { .vic = 8, .h_active = 1440, .v_active = 240, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 521, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [9] = { .vic = 9, .h_active = 1440, .v_active = 240, .interlaced = false, .pixel_clock_hz = 27000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 521, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [10] = { .vic = 10, .h_active = 2880, .v_active = 480, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 76, .h_sync = 248, .h_back = 228, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [11] = { .vic = 11, .h_active = 2880, .v_active = 480, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 76, .h_sync = 248, .h_back = 228, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [12] = { .vic = 12, .h_active = 2880, .v_active = 240, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 76, .h_sync = 248, .h_back = 228, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 521, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [13] = { .vic = 13, .h_active = 2880, .v_active = 240, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 76, .h_sync = 248, .h_back = 228, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 521, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [14] = { .vic = 14, .h_active = 1440, .v_active = 480, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 32, .h_sync = 124, .h_back = 120, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [15] = { .vic = 15, .h_active = 1440, .v_active = 480, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 32, .h_sync = 124, .h_back = 120, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [16] = { .vic = 16, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [76] = { .vic = 76, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [35] = { .vic = 35, .h_active = 2880, .v_active = 480, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 64, .h_sync = 248, .h_back = 240, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [36] = { .vic = 36, .h_active = 2880, .v_active = 480, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 64, .h_sync = 248, .h_back = 240, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [83] = { .vic = 83, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 99000000, .h_front = 260, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [90] = { .vic = 90, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 198000000, .h_front = 248, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 11, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [97] = { .vic = 97, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 176, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [107] = { .vic = 107, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 176, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [102] = { .vic = 102, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 594000000, .h_front = 88, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [126] = { .vic = 126, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 742500000, .h_front = 164, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [199] = { .vic = 199, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2376000000, .h_front = 552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [207] = { .vic = 207, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2376000000, .h_front = 552, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 44, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [215] = { .vic = 215, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 2970000000, .h_front = 288, .h_sync = 176, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [40] = { .vic = 40, .h_active = 1920, .v_active = 1080, .interlaced = true, .pixel_clock_hz = 148500000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 2, .v_sync = 5, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [41] = { .vic = 41, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 440, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [70] = { .vic = 70, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 440, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [42] = { .vic = 42, .h_active = 720, .v_active = 576, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 12, .h_sync = 64, .h_back = 68, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [43] = { .vic = 43, .h_active = 720, .v_active = 576, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 12, .h_sync = 64, .h_back = 68, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [44] = { .vic = 44, .h_active = 1440, .v_active = 576, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [45] = { .vic = 45, .h_active = 1440, .v_active = 576, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [64] = { .vic = 64, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [77] = { .vic = 77, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 528, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [84] = { .vic = 84, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 165000000, .h_front = 60, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 95, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [91] = { .vic = 91, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 371250000, .h_front = 218, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 161, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [117] = { .vic = 117, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 1056, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [119] = { .vic = 119, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 1056, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [127] = { .vic = 127, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1485000000, .h_front = 1096, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [200] = { .vic = 200, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 4752000000, .h_front = 2112, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [208] = { .vic = 208, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 4752000000, .h_front = 2112, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [216] = { .vic = 216, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 5940000000, .h_front = 2192, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [218] = { .vic = 218, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 800, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [46] = { .vic = 46, .h_active = 1920, .v_active = 1080, .interlaced = true, .pixel_clock_hz = 148500000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 2, .v_sync = 5, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [47] = { .vic = 47, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 110, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [71] = { .vic = 71, .h_active = 1280, .v_active = 720, .interlaced = false, .pixel_clock_hz = 148500000, .h_front = 110, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 20, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [48] = { .vic = 48, .h_active = 720, .v_active = 480, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 16, .h_sync = 62, .h_back = 60, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [49] = { .vic = 49, .h_active = 720, .v_active = 480, .interlaced = false, .pixel_clock_hz = 54000000, .h_front = 16, .h_sync = 62, .h_back = 60, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [50] = { .vic = 50, .h_active = 1440, .v_active = 480, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [51] = { .vic = 51, .h_active = 1440, .v_active = 480, .interlaced = true, .pixel_clock_hz = 54000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [63] = { .vic = 63, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [78] = { .vic = 78, .h_active = 1920, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 297000000, .h_front = 88, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 36, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [85] = { .vic = 85, .h_active = 1680, .v_active = 720, .interlaced = false, .pixel_clock_hz = 198000000, .h_front = 60, .h_sync = 40, .h_back = 220, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 5, .v_sync = 5, .v_back = 95, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [92] = { .vic = 92, .h_active = 2560, .v_active = 1080, .interlaced = false, .pixel_clock_hz = 495000000, .h_front = 548, .h_sync = 44, .h_back = 148, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 4, .v_sync = 5, .v_back = 161, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [118] = { .vic = 118, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 176, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [120] = { .vic = 120, .h_active = 3840, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 176, .h_sync = 88, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [193] = { .vic = 193, .h_active = 5120, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1485000000, .h_front = 164, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [201] = { .vic = 201, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 4752000000, .h_front = 352, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [209] = { .vic = 209, .h_active = 7680, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 4752000000, .h_front = 352, .h_sync = 176, .h_back = 592, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [217] = { .vic = 217, .h_active = 10240, .v_active = 4320, .interlaced = false, .pixel_clock_hz = 5940000000, .h_front = 288, .h_sync = 176, .h_back = 296, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 16, .v_sync = 20, .v_back = 144, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, }, [219] = { .vic = 219, .h_active = 4096, .v_active = 2160, .interlaced = false, .pixel_clock_hz = 1188000000, .h_front = 88, .h_sync = 88, .h_back = 128, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .v_front = 8, .v_sync = 10, .v_back = 72, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, }, [52] = { .vic = 52, .h_active = 720, .v_active = 576, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 12, .h_sync = 64, .h_back = 68, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [53] = { .vic = 53, .h_active = 720, .v_active = 576, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 12, .h_sync = 64, .h_back = 68, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 5, .v_sync = 5, .v_back = 39, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [54] = { .vic = 54, .h_active = 1440, .v_active = 576, .interlaced = true, .pixel_clock_hz = 108000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [55] = { .vic = 55, .h_active = 1440, .v_active = 576, .interlaced = true, .pixel_clock_hz = 108000000, .h_front = 24, .h_sync = 126, .h_back = 138, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 2, .v_sync = 3, .v_back = 19, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [56] = { .vic = 56, .h_active = 720, .v_active = 480, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 16, .h_sync = 62, .h_back = 60, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [57] = { .vic = 57, .h_active = 720, .v_active = 480, .interlaced = false, .pixel_clock_hz = 108000000, .h_front = 16, .h_sync = 62, .h_back = 60, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 9, .v_sync = 6, .v_back = 30, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, [58] = { .vic = 58, .h_active = 1440, .v_active = 480, .interlaced = true, .pixel_clock_hz = 108000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, }, [59] = { .vic = 59, .h_active = 1440, .v_active = 480, .interlaced = true, .pixel_clock_hz = 108000000, .h_front = 38, .h_sync = 124, .h_back = 114, .h_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .v_front = 4, .v_sync = 3, .v_back = 15, .v_sync_polarity = DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, .picture_aspect_ratio = DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, }, }; const size_t _di_cta_video_formats_len = 220; libdisplay-info-0.1.1/cta.c000066400000000000000000001346451437312070200155250ustar00rootroot00000000000000#include #include #include #include #include #include #include "bits.h" #include "cta.h" #include "log.h" #include "edid.h" /** * Number of bytes in the CTA header (tag + revision + DTD offset + flags). */ #define CTA_HEADER_SIZE 4 /** * Exclusive upper bound for the detailed timing definitions in the CTA block. */ #define CTA_DTD_END 127 /** * Number of bytes in a CTA short audio descriptor. */ #define CTA_SAD_SIZE 3 const struct di_cta_video_format * di_cta_video_format_from_vic(uint8_t vic) { if (vic > _di_cta_video_formats_len || _di_cta_video_formats[vic].vic == 0) return NULL; return &_di_cta_video_formats[vic]; } static void add_failure(struct di_edid_cta *cta, const char fmt[], ...) { va_list args; va_start(args, fmt); _di_logger_va_add_failure(cta->logger, fmt, args); va_end(args); } static void add_failure_until(struct di_edid_cta *cta, int revision, const char fmt[], ...) { va_list args; if (cta->revision > revision) { return; } va_start(args, fmt); _di_logger_va_add_failure(cta->logger, fmt, args); va_end(args); } static struct di_cta_svd * parse_svd(struct di_edid_cta *cta, uint8_t raw, const char *prefix) { struct di_cta_svd svd, *svd_ptr; if (raw == 0 || raw == 128 || raw >= 254) { /* Reserved */ add_failure_until(cta, 3, "%s: Unknown VIC %" PRIu8 ".", prefix, raw); return NULL; } else if (raw <= 127 || raw >= 193) { svd = (struct di_cta_svd) { .vic = raw, }; } else { svd = (struct di_cta_svd) { .vic = get_bit_range(raw, 6, 0), .native = true, }; } svd_ptr = calloc(1, sizeof(*svd_ptr)); if (!svd_ptr) return NULL; *svd_ptr = svd; return svd_ptr; } static bool parse_video_block(struct di_edid_cta *cta, struct di_cta_video_block *video, const uint8_t *data, size_t size) { size_t i; struct di_cta_svd *svd; if (size == 0) add_failure(cta, "Video Data Block: Empty Data Block"); for (i = 0; i < size; i++) { svd = parse_svd(cta, data[i], "Video Data Block"); if (!svd) continue; assert(video->svds_len < EDID_CTA_MAX_VIDEO_BLOCK_ENTRIES); video->svds[video->svds_len++] = svd; } return true; } static bool parse_ycbcr420_block(struct di_edid_cta *cta, struct di_cta_video_block *ycbcr420, const uint8_t *data, size_t size) { size_t i; struct di_cta_svd *svd; if (size == 0) add_failure(cta, "YCbCr 4:2:0 Video Data Block: Empty Data Block"); for (i = 0; i < size; i++) { svd = parse_svd(cta, data[i], "YCbCr 4:2:0 Video Data Block"); if (!svd) continue; assert(ycbcr420->svds_len < EDID_CTA_MAX_VIDEO_BLOCK_ENTRIES); ycbcr420->svds[ycbcr420->svds_len++] = svd; } return true; } static bool parse_sad_format(struct di_edid_cta *cta, const uint8_t data[static CTA_SAD_SIZE], enum di_cta_audio_format *format) { uint8_t code, code_ext; code = get_bit_range(data[0], 6, 3); switch (code) { case 0x0: add_failure_until(cta, 3, "Audio Data Block: Audio Format Code 0x00 is reserved."); return false; case 0x1: *format = DI_CTA_AUDIO_FORMAT_LPCM; break; case 0x2: *format = DI_CTA_AUDIO_FORMAT_AC3; break; case 0x3: *format = DI_CTA_AUDIO_FORMAT_MPEG1; break; case 0x4: *format = DI_CTA_AUDIO_FORMAT_MP3; break; case 0x5: *format = DI_CTA_AUDIO_FORMAT_MPEG2; break; case 0x6: *format = DI_CTA_AUDIO_FORMAT_AAC_LC; break; case 0x7: *format = DI_CTA_AUDIO_FORMAT_DTS; break; case 0x8: *format = DI_CTA_AUDIO_FORMAT_ATRAC; break; case 0x9: *format = DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO; break; case 0xA: *format = DI_CTA_AUDIO_FORMAT_ENHANCED_AC3; break; case 0xB: *format = DI_CTA_AUDIO_FORMAT_DTS_HD; break; case 0xC: *format = DI_CTA_AUDIO_FORMAT_MAT; break; case 0xD: *format = DI_CTA_AUDIO_FORMAT_DST; break; case 0xE: *format = DI_CTA_AUDIO_FORMAT_WMA_PRO; break; case 0xF: code_ext = get_bit_range(data[2], 7, 3); switch (code_ext) { case 0x04: *format = DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC; break; case 0x05: *format = DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2; break; case 0x06: *format = DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC; break; case 0x07: *format = DI_CTA_AUDIO_FORMAT_DRA; break; case 0x08: *format = DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND; break; case 0x0A: *format = DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND; break; case 0x0B: *format = DI_CTA_AUDIO_FORMAT_MPEGH_3D; break; case 0x0C: *format = DI_CTA_AUDIO_FORMAT_AC4; break; case 0x0D: *format = DI_CTA_AUDIO_FORMAT_LPCM_3D; break; default: add_failure_until(cta, 3, "Audio Data Block: Unknown Audio Ext Format 0x%02x.", code_ext); return false; } break; default: add_failure_until(cta, 3, "Audio Data Block: Unknown Audio Format 0x%02x.", code); return false; } return true; } static bool parse_sad(struct di_edid_cta *cta, struct di_cta_audio_block *audio, const uint8_t data[static CTA_SAD_SIZE]) { enum di_cta_audio_format format; struct di_cta_sad_priv *priv; struct di_cta_sad *sad; struct di_cta_sad_sample_rates *sample_rates; struct di_cta_sad_lpcm *lpcm; struct di_cta_sad_mpegh_3d *mpegh_3d; struct di_cta_sad_mpeg_aac *mpeg_aac; struct di_cta_sad_mpeg_surround *mpeg_surround; struct di_cta_sad_mpeg_aac_le *mpeg_aac_le; struct di_cta_sad_enhanced_ac3 *enhanced_ac3; struct di_cta_sad_mat *mat; struct di_cta_sad_wma_pro *wma_pro; if (!parse_sad_format(cta, data, &format)) return true; priv = calloc(1, sizeof(*priv)); if (!priv) return false; sad = &priv->base; sample_rates = &priv->supported_sample_rates; lpcm = &priv->lpcm; mpegh_3d = &priv->mpegh_3d; mpeg_aac = &priv->mpeg_aac; mpeg_surround = &priv->mpeg_surround; mpeg_aac_le = &priv->mpeg_aac_le; enhanced_ac3 = &priv->enhanced_ac3; mat = &priv->mat; wma_pro = &priv->wma_pro; sad->format = format; /* TODO: Find DRA documentation */ switch (format) { case DI_CTA_AUDIO_FORMAT_LPCM: case DI_CTA_AUDIO_FORMAT_AC3: case DI_CTA_AUDIO_FORMAT_MPEG1: case DI_CTA_AUDIO_FORMAT_MP3: case DI_CTA_AUDIO_FORMAT_MPEG2: case DI_CTA_AUDIO_FORMAT_AAC_LC: case DI_CTA_AUDIO_FORMAT_DTS: case DI_CTA_AUDIO_FORMAT_ATRAC: case DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO: case DI_CTA_AUDIO_FORMAT_ENHANCED_AC3: case DI_CTA_AUDIO_FORMAT_DTS_HD: case DI_CTA_AUDIO_FORMAT_MAT: case DI_CTA_AUDIO_FORMAT_DST: case DI_CTA_AUDIO_FORMAT_WMA_PRO: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC: /* DRA is not documented but this is what edid-decode does */ case DI_CTA_AUDIO_FORMAT_DRA: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND: sad->max_channels = get_bit_range(data[0], 2, 0) + 1; break; case DI_CTA_AUDIO_FORMAT_LPCM_3D: sad->max_channels = (get_bit_range(data[0], 2, 0) | (get_bit_range(data[0], 7, 7) << 3) | (get_bit_range(data[1], 7, 7) << 4)) + 1; break; case DI_CTA_AUDIO_FORMAT_MPEGH_3D: case DI_CTA_AUDIO_FORMAT_AC4: break; } switch (format) { case DI_CTA_AUDIO_FORMAT_LPCM: case DI_CTA_AUDIO_FORMAT_AC3: case DI_CTA_AUDIO_FORMAT_MPEG1: case DI_CTA_AUDIO_FORMAT_MP3: case DI_CTA_AUDIO_FORMAT_MPEG2: case DI_CTA_AUDIO_FORMAT_AAC_LC: case DI_CTA_AUDIO_FORMAT_DTS: case DI_CTA_AUDIO_FORMAT_ATRAC: case DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO: case DI_CTA_AUDIO_FORMAT_ENHANCED_AC3: case DI_CTA_AUDIO_FORMAT_DTS_HD: case DI_CTA_AUDIO_FORMAT_MAT: case DI_CTA_AUDIO_FORMAT_DST: case DI_CTA_AUDIO_FORMAT_WMA_PRO: /* DRA is not documented but this is what edid-decode does */ case DI_CTA_AUDIO_FORMAT_DRA: case DI_CTA_AUDIO_FORMAT_MPEGH_3D: case DI_CTA_AUDIO_FORMAT_LPCM_3D: sample_rates->has_192_khz = has_bit(data[1], 6); sample_rates->has_176_4_khz = has_bit(data[1], 5); /* fallthrough */ case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND: sample_rates->has_96_khz = has_bit(data[1], 4); sample_rates->has_88_2_khz = has_bit(data[1], 3); sample_rates->has_48_khz = has_bit(data[1], 2); sample_rates->has_44_1_khz = has_bit(data[1], 1); sample_rates->has_32_khz = has_bit(data[1], 0); break; case DI_CTA_AUDIO_FORMAT_AC4: sample_rates->has_192_khz = has_bit(data[1], 6); sample_rates->has_96_khz = has_bit(data[1], 4); sample_rates->has_48_khz = has_bit(data[1], 2); sample_rates->has_44_1_khz = has_bit(data[1], 1); break; } sad->supported_sample_rates = sample_rates; switch (format) { case DI_CTA_AUDIO_FORMAT_AC3: case DI_CTA_AUDIO_FORMAT_MPEG1: case DI_CTA_AUDIO_FORMAT_MP3: case DI_CTA_AUDIO_FORMAT_MPEG2: case DI_CTA_AUDIO_FORMAT_AAC_LC: case DI_CTA_AUDIO_FORMAT_DTS: case DI_CTA_AUDIO_FORMAT_ATRAC: sad->max_bitrate_kbs = data[2] * 8; break; default: break; } switch (format) { case DI_CTA_AUDIO_FORMAT_LPCM: case DI_CTA_AUDIO_FORMAT_LPCM_3D: lpcm->has_sample_size_24_bits = has_bit(data[2], 2); lpcm->has_sample_size_20_bits = has_bit(data[2], 1); lpcm->has_sample_size_16_bits = has_bit(data[2], 0); sad->lpcm = lpcm; default: break; } switch (format) { case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND: mpeg_aac->has_frame_length_1024 = has_bit(data[2], 2); mpeg_aac->has_frame_length_960 = has_bit(data[2], 1); sad->mpeg_aac = mpeg_aac; break; default: break; } if (format == DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC) { mpeg_aac_le->supports_multichannel_sound = has_bit(data[2], 0); sad->mpeg_aac_le = mpeg_aac_le; } switch (format) { case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND: mpeg_surround->signaling = has_bit(data[2], 0); sad->mpeg_surround = mpeg_surround; break; default: break; } if (format == DI_CTA_AUDIO_FORMAT_MPEGH_3D) { mpegh_3d->low_complexity_profile = has_bit(data[2], 0); mpegh_3d->baseline_profile = has_bit(data[2], 1); mpegh_3d->level = get_bit_range(data[0], 2, 0); if (mpegh_3d->level > DI_CTA_SAD_MPEGH_3D_LEVEL_5) { add_failure_until(cta, 3, "Unknown MPEG-H 3D Audio Level 0x%02x.", mpegh_3d->level); mpegh_3d->level = DI_CTA_SAD_MPEGH_3D_LEVEL_UNSPECIFIED; } sad->mpegh_3d = mpegh_3d; } if (format == DI_CTA_AUDIO_FORMAT_ENHANCED_AC3) { enhanced_ac3->supports_joint_object_coding = has_bit(data[2], 0); enhanced_ac3->supports_joint_object_coding_ACMOD28 = has_bit(data[2], 1); sad->enhanced_ac3 = enhanced_ac3; } if (format == DI_CTA_AUDIO_FORMAT_MAT) { mat->supports_object_audio_and_channel_based = has_bit(data[2], 0); if (mat->supports_object_audio_and_channel_based) mat->requires_hash_calculation = !has_bit(data[2], 0); sad->mat = mat; } if (format == DI_CTA_AUDIO_FORMAT_WMA_PRO) { wma_pro->profile = get_bit_range(data[2], 2, 0); sad->wma_pro = wma_pro; } switch (format) { case DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO: case DI_CTA_AUDIO_FORMAT_DTS_HD: case DI_CTA_AUDIO_FORMAT_DST: /* TODO data[2] 7:0 contains unknown Audio Format Code dependent value */ break; default: break; } if (format == DI_CTA_AUDIO_FORMAT_AC4) { /* TODO data[2] 2:0 contains unknown Audio Format Code dependent value */ } switch (format) { case DI_CTA_AUDIO_FORMAT_LPCM: case DI_CTA_AUDIO_FORMAT_WMA_PRO: if (has_bit(data[0], 7) || has_bit(data[1], 7) || get_bit_range(data[2], 7, 3) != 0) add_failure_until(cta, 3, "Bits F17, F27, F37:F33 must be 0."); break; case DI_CTA_AUDIO_FORMAT_AC3: case DI_CTA_AUDIO_FORMAT_MPEG1: case DI_CTA_AUDIO_FORMAT_MP3: case DI_CTA_AUDIO_FORMAT_MPEG2: case DI_CTA_AUDIO_FORMAT_AAC_LC: case DI_CTA_AUDIO_FORMAT_DTS: case DI_CTA_AUDIO_FORMAT_ATRAC: case DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO: case DI_CTA_AUDIO_FORMAT_ENHANCED_AC3: case DI_CTA_AUDIO_FORMAT_DTS_HD: case DI_CTA_AUDIO_FORMAT_MAT: case DI_CTA_AUDIO_FORMAT_DST: if (has_bit(data[0], 7) || has_bit(data[1], 7)) add_failure_until(cta, 3, "Bits F17, F27 must be 0."); break; case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC: case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND: case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND: if (has_bit(data[0], 7) || get_bit_range(data[2], 7, 5) != 0) add_failure_until(cta, 3, "Bits F17, F27:F25 must be 0."); break; case DI_CTA_AUDIO_FORMAT_MPEGH_3D: if (has_bit(data[0], 7) || has_bit(data[1], 7) || has_bit(data[2], 2)) add_failure_until(cta, 3, "Bits F17, F27, F32 must be 0."); break; case DI_CTA_AUDIO_FORMAT_AC4: if ((data[0] & 0x87) != 0 || (data[1] & 0xA9) != 0) add_failure_until(cta, 3, "Bits F17, F12:F10, F27, F25, F23, " "F20 must be 0."); break; /* DRA documentation missing */ case DI_CTA_AUDIO_FORMAT_DRA: case DI_CTA_AUDIO_FORMAT_LPCM_3D: break; } assert(audio->sads_len < EDID_CTA_MAX_AUDIO_BLOCK_ENTRIES); audio->sads[audio->sads_len++] = priv; return true; } static bool parse_audio_block(struct di_edid_cta *cta, struct di_cta_audio_block *audio, const uint8_t *data, size_t size) { size_t i; if (size % 3 != 0) add_failure(cta, "Broken CTA-861 audio block length %d.", size); for (i = 0; i + 3 <= size; i += 3) { if (!parse_sad(cta, audio, &data[i])) return false; } return true; } static bool parse_speaker_alloc_block(struct di_edid_cta *cta, struct di_cta_speaker_alloc_block *speaker_alloc, const uint8_t *data, size_t size) { bool rlc_rrc; if (size < 3) { add_failure(cta, "Speaker Allocation Data Block: Empty Data Block with length %zu.", size); return false; } speaker_alloc->flw_frw = has_bit(data[0], 7); rlc_rrc = has_bit(data[0], 6); speaker_alloc->flc_frc = has_bit(data[0], 5); speaker_alloc->bc = has_bit(data[0], 4); speaker_alloc->bl_br = has_bit(data[0], 3); speaker_alloc->fc = has_bit(data[0], 2); speaker_alloc->lfe1 = has_bit(data[0], 1); speaker_alloc->fl_fr = has_bit(data[0], 0); if (rlc_rrc) { if (cta->revision >= 3) add_failure(cta, "Speaker Allocation Data Block: Deprecated bit F16 must be 0."); else speaker_alloc->bl_br = true; } speaker_alloc->tpsil_tpsir = has_bit(data[1], 7); speaker_alloc->sil_sir = has_bit(data[1], 6); speaker_alloc->tpbc = has_bit(data[1], 5); speaker_alloc->lfe2 = has_bit(data[1], 4); speaker_alloc->ls_rs = has_bit(data[1], 3); speaker_alloc->tpfc = has_bit(data[1], 2); speaker_alloc->tpc = has_bit(data[1], 1); speaker_alloc->tpfl_tpfr = has_bit(data[1], 0); if (get_bit_range(data[2], 7, 4) != 0) add_failure(cta, "Speaker Allocation Data Block: Bits F37, F36, F34 must be 0."); if (cta->revision >= 3 && has_bit(data[2], 3)) add_failure(cta, "Speaker Allocation Data Block: Deprecated bit F33 must be 0."); speaker_alloc->btfl_btfr = has_bit(data[2], 2); speaker_alloc->btfc = has_bit(data[2], 1); speaker_alloc->tpbl_tpbr = has_bit(data[2], 0); return true; } static bool parse_video_cap_block(struct di_edid_cta *cta, struct di_cta_video_cap_block *video_cap, const uint8_t *data, size_t size) { if (size < 1) { add_failure(cta, "Video Capability Data Block: Empty Data Block with length %u.", size); return false; } video_cap->selectable_ycc_quantization_range = has_bit(data[0], 7); video_cap->selectable_rgb_quantization_range = has_bit(data[0], 6); video_cap->pt_over_underscan = get_bit_range(data[0], 5, 4); video_cap->it_over_underscan = get_bit_range(data[0], 3, 2); video_cap->ce_over_underscan = get_bit_range(data[0], 1, 0); if (!video_cap->selectable_rgb_quantization_range && cta->revision >= 3) add_failure(cta, "Video Capability Data Block: Set Selectable RGB Quantization to avoid interop issues."); /* TODO: add failure if selectable_ycc_quantization_range is unset, * the sink supports YCbCr formats and the revision is 3+ */ switch (video_cap->it_over_underscan) { case DI_CTA_VIDEO_CAP_ALWAYS_OVERSCAN: if (cta->flags.it_underscan) add_failure(cta, "Video Capability Data Block: IT video formats are always overscanned, but bit 7 of Byte 3 of the CTA-861 Extension header is set to underscanned."); break; case DI_CTA_VIDEO_CAP_ALWAYS_UNDERSCAN: if (!cta->flags.it_underscan) add_failure(cta, "Video Capability Data Block: IT video formats are always underscanned, but bit 7 of Byte 3 of the CTA-861 Extension header is set to overscanned."); default: break; } return true; } static bool check_vesa_dddb_num_channels(enum di_cta_vesa_dddb_interface_type interface, uint8_t num_channels) { switch (interface) { case DI_CTA_VESA_DDDB_INTERFACE_VGA: case DI_CTA_VESA_DDDB_INTERFACE_NAVI_V: case DI_CTA_VESA_DDDB_INTERFACE_NAVI_D: return num_channels == 0; case DI_CTA_VESA_DDDB_INTERFACE_LVDS: case DI_CTA_VESA_DDDB_INTERFACE_RSDS: return true; case DI_CTA_VESA_DDDB_INTERFACE_DVI_D: return num_channels == 1 || num_channels == 2; case DI_CTA_VESA_DDDB_INTERFACE_DVI_I_ANALOG: return num_channels == 0; case DI_CTA_VESA_DDDB_INTERFACE_DVI_I_DIGITAL: return num_channels == 1 || num_channels == 2; case DI_CTA_VESA_DDDB_INTERFACE_HDMI_A: return num_channels == 1; case DI_CTA_VESA_DDDB_INTERFACE_HDMI_B: return num_channels == 2; case DI_CTA_VESA_DDDB_INTERFACE_MDDI: return num_channels == 1 || num_channels == 2; case DI_CTA_VESA_DDDB_INTERFACE_DISPLAYPORT: return num_channels == 1 || num_channels == 2 || num_channels == 4; case DI_CTA_VESA_DDDB_INTERFACE_IEEE_1394: case DI_CTA_VESA_DDDB_INTERFACE_M1_ANALOG: return num_channels == 0; case DI_CTA_VESA_DDDB_INTERFACE_M1_DIGITAL: return num_channels == 1 || num_channels == 2; } abort(); /* unreachable */ } static void parse_vesa_dddb_additional_primary_chromaticity(struct di_cta_vesa_dddb_additional_primary_chromaticity *coords, uint8_t low, const uint8_t high[static 2]) { uint16_t raw_x, raw_y; /* only 10 bits are used */ raw_x = (uint16_t) ((high[0] << 2) | get_bit_range(low, 3, 2)); raw_y = (uint16_t) ((high[1] << 2) | get_bit_range(low, 1, 0)); *coords = (struct di_cta_vesa_dddb_additional_primary_chromaticity) { .x = (float) raw_x / 1024, .y = (float) raw_y / 1024, }; } static bool parse_vesa_dddb(struct di_edid_cta *cta, struct di_cta_vesa_dddb *dddb, const uint8_t *data, size_t size) { const size_t offset = 2; /* CTA block header */ uint8_t interface_type, num_channels, content_protection, scan_direction, subpixel_layout; if (size + offset != 32) { add_failure(cta, "VESA Video Display Device Data Block: Invalid length %u.", size); return false; } interface_type = get_bit_range(data[0x02 - offset], 7, 4); num_channels = get_bit_range(data[0x02 - offset], 3, 0); switch (interface_type) { case 0x0: /* Analog */ /* Special case: num_channels contains the detailed interface * type. */ switch (num_channels) { case 0x0: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_VGA; break; case 0x1: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_NAVI_V; break; case 0x2: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_NAVI_D; break; default: add_failure(cta, "VESA Video Display Device Data Block: Unknown analog interface type 0x%x.", num_channels); return false; } num_channels = 0; break; case 0x1: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_LVDS; break; case 0x2: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_RSDS; break; case 0x3: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_DVI_D; break; case 0x4: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_DVI_I_ANALOG; break; case 0x5: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_DVI_I_DIGITAL; break; case 0x6: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_HDMI_A; break; case 0x7: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_HDMI_B; break; case 0x8: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_MDDI; break; case 0x9: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_DISPLAYPORT; break; case 0xA: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_IEEE_1394; break; case 0xB: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_M1_ANALOG; break; case 0xC: dddb->interface_type = DI_CTA_VESA_DDDB_INTERFACE_M1_DIGITAL; break; default: add_failure(cta, "VESA Video Display Device Data Block: Unknown interface type 0x%x.", interface_type); return false; } if (check_vesa_dddb_num_channels(dddb->interface_type, num_channels)) dddb->num_channels = num_channels; else add_failure(cta, "VESA Video Display Device Data Block: Invalid number of lanes/channels %u.", num_channels); dddb->interface_version = get_bit_range(data[0x03 - offset], 7, 4); dddb->interface_release = get_bit_range(data[0x03 - offset], 3, 0); content_protection = data[0x04 - offset]; switch (content_protection) { case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_NONE: case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_HDCP: case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DTCP: case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DPCP: dddb->content_protection = content_protection; break; default: add_failure(cta, "VESA Video Display Device Data Block: Invalid content protection 0x%x.", content_protection); } dddb->min_clock_freq_mhz = get_bit_range(data[0x05 - offset], 7, 2); dddb->max_clock_freq_mhz = (get_bit_range(data[0x05 - offset], 1, 0) << 8) | data[0x06 - offset]; if (dddb->min_clock_freq_mhz > dddb->max_clock_freq_mhz) { add_failure(cta, "VESA Video Display Device Data Block: Minimum clock frequency (%d MHz) greater than maximum (%d MHz).", dddb->min_clock_freq_mhz, dddb->max_clock_freq_mhz); dddb->min_clock_freq_mhz = dddb->max_clock_freq_mhz = 0; } dddb->native_horiz_pixels = data[0x07 - offset] | (data[0x08 - offset] << 8); dddb->native_vert_pixels = data[0x09 - offset] | (data[0x0A - offset] << 8); dddb->aspect_ratio = (float)data[0x0B - offset] / 100 + 1; dddb->default_orientation = get_bit_range(data[0x0C - offset], 7, 6); dddb->rotation_cap = get_bit_range(data[0x0C - offset], 5, 4); dddb->zero_pixel_location = get_bit_range(data[0x0C - offset], 3, 2); scan_direction = get_bit_range(data[0x0C - offset], 1, 0); if (scan_direction != 3) dddb->scan_direction = scan_direction; else add_failure(cta, "VESA Video Display Device Data Block: Invalid scan direction 0x%x.", scan_direction); subpixel_layout = data[0x0D - offset]; switch (subpixel_layout) { case DI_CTA_VESA_DDDB_SUBPIXEL_UNDEFINED: case DI_CTA_VESA_DDDB_SUBPIXEL_RGB_VERT: case DI_CTA_VESA_DDDB_SUBPIXEL_RGB_HORIZ: case DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_VERT: case DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_HORIZ: case DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_RGGB: case DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_GBRG: case DI_CTA_VESA_DDDB_SUBPIXEL_DELTA_RGB: case DI_CTA_VESA_DDDB_SUBPIXEL_MOSAIC: case DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_ANY: case DI_CTA_VESA_DDDB_SUBPIXEL_FIVE: case DI_CTA_VESA_DDDB_SUBPIXEL_SIX: case DI_CTA_VESA_DDDB_SUBPIXEL_CLAIRVOYANTE_PENTILE: dddb->subpixel_layout = subpixel_layout; break; default: add_failure(cta, "VESA Video Display Device Data Block: Invalid subpixel layout 0x%x.", subpixel_layout); } dddb->horiz_pitch_mm = (float)data[0x0E - offset] * 0.01f; dddb->vert_pitch_mm = (float)data[0x0F - offset] * 0.01f; dddb->dithering_type = get_bit_range(data[0x10 - offset], 7, 6); dddb->direct_drive = has_bit(data[0x10 - offset], 5); dddb->overdrive_not_recommended = has_bit(data[0x10 - offset], 4); dddb->deinterlacing = has_bit(data[0x10 - offset], 3); if (get_bit_range(data[0x10 - offset], 2, 0) != 0) add_failure(cta, "VESA Video Display Device Data Block: Reserved miscellaneous display capabilities bits 2-0 must be 0."); dddb->audio_support = has_bit(data[0x11 - offset], 7); dddb->separate_audio_inputs = has_bit(data[0x11 - offset], 6); dddb->audio_input_override = has_bit(data[0x11 - offset], 5); if (get_bit_range(data[0x11 - offset], 4, 0) != 0) add_failure(cta, "VESA Video Display Device Data Block: Reserved audio bits 4-0 must be 0."); dddb->audio_delay_provided = data[0x12 - offset] != 0; dddb->audio_delay_ms = 2 * get_bit_range(data[0x12 - offset], 6, 0); if (!has_bit(data[0x12 - offset], 7)) dddb->audio_delay_ms = -dddb->audio_delay_ms; dddb->frame_rate_conversion = get_bit_range(data[0x13 - offset], 7, 6); dddb->frame_rate_range_hz = get_bit_range(data[0x13 - offset], 5, 0); dddb->frame_rate_native_hz = data[0x14 - offset]; dddb->bit_depth_interface = get_bit_range(data[0x15 - offset], 7, 4) + 1; dddb->bit_depth_display = get_bit_range(data[0x15 - offset], 3, 0) + 1; dddb->additional_primary_chromaticities_len = get_bit_range(data[0x17 - offset], 1, 0); parse_vesa_dddb_additional_primary_chromaticity(&dddb->additional_primary_chromaticities[0], get_bit_range(data[0x16 - offset], 7, 4), &data[0x18 - offset]); parse_vesa_dddb_additional_primary_chromaticity(&dddb->additional_primary_chromaticities[1], get_bit_range(data[0x16 - offset], 3, 0), &data[0x1A - offset]); parse_vesa_dddb_additional_primary_chromaticity(&dddb->additional_primary_chromaticities[2], get_bit_range(data[0x17 - offset], 7, 4), &data[0x1C - offset]); if (get_bit_range(data[0x17 - offset], 3, 2) != 0) add_failure(cta, "VESA Video Display Device Data Block: Reserved additional primary chromaticities bits 3-2 of byte 0x17 must be 0."); dddb->resp_time_transition = has_bit(data[0x1E - offset], 7); dddb->resp_time_ms = get_bit_range(data[0x1E - offset], 6, 0); dddb->overscan_horiz_pct = get_bit_range(data[0x1F - offset], 7, 4); dddb->overscan_vert_pct = get_bit_range(data[0x1F - offset], 3, 0); return true; } static bool parse_colorimetry_block(struct di_edid_cta *cta, struct di_cta_colorimetry_block *colorimetry, const uint8_t *data, size_t size) { if (size < 2) { add_failure(cta, "Colorimetry Data Block: Empty Data Block with length %u.", size); return false; } colorimetry->bt2020_rgb = has_bit(data[0], 7); colorimetry->bt2020_ycc = has_bit(data[0], 6); colorimetry->bt2020_cycc = has_bit(data[0], 5); colorimetry->oprgb = has_bit(data[0], 4); colorimetry->opycc_601 = has_bit(data[0], 3); colorimetry->sycc_601 = has_bit(data[0], 2); colorimetry->xvycc_709 = has_bit(data[0], 1); colorimetry->xvycc_601 = has_bit(data[0], 0); colorimetry->st2113_rgb = has_bit(data[1], 7); colorimetry->ictcp = has_bit(data[1], 6); if (get_bit_range(data[1], 5, 0) != 0) add_failure_until(cta, 3, "Colorimetry Data Block: Reserved bits MD0-MD3 must be 0."); return true; } static float parse_max_luminance(uint8_t raw) { if (raw == 0) return 0; return 50 * powf(2, (float) raw / 32); } static float parse_min_luminance(uint8_t raw, float max) { if (raw == 0) return 0; return max * powf((float) raw / 255, 2) / 100; } static bool parse_hdr_static_metadata_block(struct di_edid_cta *cta, struct di_cta_hdr_static_metadata_block_priv *metadata, const uint8_t *data, size_t size) { uint8_t eotfs, descriptors; if (size < 2) { add_failure(cta, "HDR Static Metadata Data Block: Empty Data Block with length %u.", size); return false; } metadata->base.eotfs = &metadata->eotfs; metadata->base.descriptors = &metadata->descriptors; eotfs = data[0]; metadata->eotfs.traditional_sdr = has_bit(eotfs, 0); metadata->eotfs.traditional_hdr = has_bit(eotfs, 1); metadata->eotfs.pq = has_bit(eotfs, 2); metadata->eotfs.hlg = has_bit(eotfs, 3); if (get_bit_range(eotfs, 7, 4)) add_failure_until(cta, 3, "HDR Static Metadata Data Block: Unknown EOTF."); descriptors = data[1]; metadata->descriptors.type1 = has_bit(descriptors, 0); if (get_bit_range(descriptors, 7, 1)) add_failure_until(cta, 3, "HDR Static Metadata Data Block: Unknown descriptor type."); if (size > 2) metadata->base.desired_content_max_luminance = parse_max_luminance(data[2]); if (size > 3) metadata->base.desired_content_max_frame_avg_luminance = parse_max_luminance(data[3]); if (size > 4) { if (metadata->base.desired_content_max_luminance == 0) add_failure(cta, "HDR Static Metadata Data Block: Desired content min luminance is set, but max luminance is unset."); else metadata->base.desired_content_min_luminance = parse_min_luminance(data[4], metadata->base.desired_content_max_luminance); } return true; } static bool parse_hdr_dynamic_metadata_block(struct di_edid_cta *cta, struct di_cta_hdr_dynamic_metadata_block_priv *priv, const uint8_t *data, size_t size) { struct di_cta_hdr_dynamic_metadata_block *base; struct di_cta_hdr_dynamic_metadata_block_type1 *type1; struct di_cta_hdr_dynamic_metadata_block_type2 *type2; struct di_cta_hdr_dynamic_metadata_block_type3 *type3; struct di_cta_hdr_dynamic_metadata_block_type4 *type4; struct di_cta_hdr_dynamic_metadata_block_type256 *type256; size_t length; int type; base = &priv->base; type1 = &priv->type1; type2 = &priv->type2; type3 = &priv->type3; type4 = &priv->type4; type256 = &priv->type256; if (size < 3) { add_failure(cta, "HDR Dynamic Metadata Data Block: Empty Data Block with length %u.", size); return false; } while (size >= 3) { length = data[0]; if (size < length + 1) { add_failure(cta, "HDR Dynamic Metadata Data Block: Length of type bigger than block size."); return false; } if (length < 2) { add_failure(cta, "HDR Dynamic Metadata Data Block: Type has wrong length."); return false; } type = (data[2] << 8) | data[1]; switch (type) { case 0x0001: if (length < 3) { add_failure(cta, "HDR Dynamic Metadata Data Block: Type 1 missing Support Flags."); break; } if (length != 3) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 1 length must be 3."); type1->type_1_hdr_metadata_version = get_bit_range(data[3], 3, 0); base->type1 = type1; if (get_bit_range(data[3], 7, 4) != 0) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 1 support flags bits 7-4 must be 0."); break; case 0x0002: if (length < 3) { add_failure(cta, "HDR Dynamic Metadata Data Block: Type 2 missing Support Flags."); break; } if (length != 3) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 2 length must be 3."); type2->ts_103_433_spec_version = get_bit_range(data[3], 3, 0); if (type2->ts_103_433_spec_version == 0) { add_failure(cta, "HDR Dynamic Metadata Data Block: Type 2 spec version of 0 is not allowed."); break; } type2->ts_103_433_1_capable = has_bit(data[3], 4); type2->ts_103_433_2_capable = has_bit(data[3], 5); type2->ts_103_433_3_capable = has_bit(data[3], 6); base->type2 = type2; if (has_bit(data[3], 7) != 0) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 1 support flags bit 7 must be 0."); break; case 0x0003: if (length != 2) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 3 length must be 2."); base->type3 = type3; break; case 0x0004: if (length < 3) { add_failure(cta, "HDR Dynamic Metadata Data Block: Type 4 missing Support Flags."); break; } if (length != 3) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 4 length must be 3."); type4->type_4_hdr_metadata_version = get_bit_range(data[3], 3, 0); base->type4 = type4; if (get_bit_range(data[3], 7, 4) != 0) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 4 support flags bits 7-4 must be 0."); break; case 0x0100: if (length < 3) { add_failure(cta, "HDR Dynamic Metadata Data Block: Type 256 missing Support Flags."); break; } if (length != 3) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 256 length must be 3."); type256->graphics_overlay_flag_version = get_bit_range(data[3], 3, 0); base->type256 = type256; if (get_bit_range(data[3], 7, 4) != 0) add_failure(cta, "HDR Dynamic Metadata Data Block: Type 256 support flags bits 7-4 must be 0."); break; default: add_failure(cta, "HDR Dynamic Metadata Data Block: Unknown Type 0x%04x.", type); break; } size -= length + 1; data += length + 1; } return true; } static bool parse_vesa_transfer_characteristics_block(struct di_edid_cta *cta, struct di_cta_vesa_transfer_characteristics *tf, const uint8_t *data, size_t size) { size_t i; if (size != 7 && size != 15 && size != 31) { add_failure(cta, "Invalid length %u.", size); return false; } tf->points_len = (uint8_t) size + 1; tf->usage = get_bit_range(data[0], 7, 6); tf->points[0] = get_bit_range(data[0], 5, 0) / 1023.0f; for (i = 1; i < size; i++) tf->points[i] = tf->points[i - 1] + data[i] / 1023.0f; tf->points[i] = 1.0f; return true; } static void parse_ycbcr420_cap_map(struct di_edid_cta *cta, struct di_cta_ycbcr420_cap_map *ycbcr420_cap_map, const uint8_t *data, size_t size) { if (size == 0) { ycbcr420_cap_map->all = true; return; } assert(size <= sizeof(ycbcr420_cap_map->svd_bitmap)); memcpy(ycbcr420_cap_map->svd_bitmap, data, size); } static struct di_cta_infoframe_descriptor * parse_infoframe(struct di_edid_cta *cta, uint8_t type, const uint8_t *data, size_t size) { struct di_cta_infoframe_descriptor infoframe = {0}; struct di_cta_infoframe_descriptor *ifp; if (type >= 8 && type <= 0x1f) { add_failure(cta, "InfoFrame Data Block: Type code %u is reserved.", type); return NULL; } if (type >= 0x20) { add_failure(cta, "InfoFrame Data Block: Type code %u is forbidden.", type); return NULL; } if (type == 1) { /* No known vendor specific InfoFrames, yet */ return NULL; } else { switch (type) { case 0x02: infoframe.type = DI_CTA_INFOFRAME_TYPE_AUXILIARY_VIDEO_INFORMATION; break; case 0x03: infoframe.type = DI_CTA_INFOFRAME_TYPE_SOURCE_PRODUCT_DESCRIPTION; break; case 0x04: infoframe.type = DI_CTA_INFOFRAME_TYPE_AUDIO; break; case 0x05: infoframe.type = DI_CTA_INFOFRAME_TYPE_MPEG_SOURCE; break; case 0x06: infoframe.type = DI_CTA_INFOFRAME_TYPE_NTSC_VBI; break; case 0x07: infoframe.type = DI_CTA_INFOFRAME_TYPE_DYNAMIC_RANGE_AND_MASTERING; break; default: abort(); /* unreachable */ } } ifp = calloc(1, sizeof(*ifp)); if (!ifp) return NULL; *ifp = infoframe; return ifp; } static bool parse_infoframe_block(struct di_edid_cta *cta, struct di_cta_infoframe_block_priv *ifb, const uint8_t *data, size_t size) { size_t index = 0, length; uint8_t type; struct di_cta_infoframe_descriptor *infoframe; if (size < 2) { add_failure(cta, "InfoFrame Data Block: Empty Data Block with length %u.", size); return false; } ifb->block.num_simultaneous_vsifs = data[1] + 1; ifb->block.infoframes = (const struct di_cta_infoframe_descriptor *const *)ifb->infoframes; index = get_bit_range(data[0], 7, 5) + 2; if (get_bit_range(data[0], 4, 0) != 0) add_failure(cta, "InfoFrame Data Block: InfoFrame Processing " "Descriptor Header bits F14-F10 shall be 0."); while (true) { if (index == size) break; if (index > size) { add_failure(cta, "InfoFrame Data Block: Payload length exceeds block size."); return false; } length = get_bit_range(data[index], 7, 5); type = get_bit_range(data[index], 4, 0); if (type == 0) { add_failure(cta, "InfoFrame Data Block: Short InfoFrame Descriptor with type 0 is forbidden."); return false; } else if (type == 1) { length += 4; } else { length += 1; } if (index + length > size) { add_failure(cta, "InfoFrame Data Block: Payload length exceeds block size."); return false; } infoframe = parse_infoframe(cta, type, &data[index], length); if (infoframe) { assert(ifb->infoframes_len < EDID_CTA_INFOFRAME_BLOCK_ENTRIES); ifb->infoframes[ifb->infoframes_len++] = infoframe; } index += length; } return true; } static void destroy_data_block(struct di_cta_data_block *data_block) { size_t i; struct di_cta_video_block *video; struct di_cta_audio_block *audio; struct di_cta_infoframe_block_priv *infoframe; switch (data_block->tag) { case DI_CTA_DATA_BLOCK_VIDEO: video = &data_block->video; for (i = 0; i < video->svds_len; i++) free(video->svds[i]); break; case DI_CTA_DATA_BLOCK_YCBCR420: video = &data_block->ycbcr420; for (i = 0; i < video->svds_len; i++) free(video->svds[i]); break; case DI_CTA_DATA_BLOCK_AUDIO: audio = &data_block->audio; for (i = 0; i < audio->sads_len; i++) free(audio->sads[i]); break; case DI_CTA_DATA_BLOCK_INFOFRAME: infoframe = &data_block->infoframe; for (i = 0; i < infoframe->infoframes_len; i++) free(infoframe->infoframes[i]); break; default: break; /* Nothing to do */ } free(data_block); } static bool parse_data_block(struct di_edid_cta *cta, uint8_t raw_tag, const uint8_t *data, size_t size) { enum di_cta_data_block_tag tag; uint8_t extended_tag; struct di_cta_data_block *data_block; data_block = calloc(1, sizeof(*data_block)); if (!data_block) { return false; } switch (raw_tag) { case 1: tag = DI_CTA_DATA_BLOCK_AUDIO; if (!parse_audio_block(cta, &data_block->audio, data, size)) goto error; break; case 2: tag = DI_CTA_DATA_BLOCK_VIDEO; if (!parse_video_block(cta, &data_block->video, data, size)) goto error; break; case 3: /* Vendor-Specific Data Block */ goto skip; case 4: tag = DI_CTA_DATA_BLOCK_SPEAKER_ALLOC; if (!parse_speaker_alloc_block(cta, &data_block->speaker_alloc, data, size)) goto error; break; case 5: tag = DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC; if (!parse_vesa_transfer_characteristics_block(cta, &data_block->vesa_transfer_characteristics, data, size)) goto error; break; case 6: tag = DI_CTA_DATA_BLOCK_VIDEO_FORMAT; break; case 7: /* Use Extended Tag */ if (size < 1) { add_failure(cta, "Empty block with extended tag."); goto skip; } extended_tag = data[0]; data = &data[1]; size--; switch (extended_tag) { case 0: tag = DI_CTA_DATA_BLOCK_VIDEO_CAP; if (!parse_video_cap_block(cta, &data_block->video_cap, data, size)) goto skip; break; case 2: tag = DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE; if (!parse_vesa_dddb(cta, &data_block->vesa_dddb, data, size)) goto skip; break; case 5: tag = DI_CTA_DATA_BLOCK_COLORIMETRY; if (!parse_colorimetry_block(cta, &data_block->colorimetry, data, size)) goto skip; break; case 6: tag = DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA; if (!parse_hdr_static_metadata_block(cta, &data_block->hdr_static_metadata, data, size)) goto skip; break; case 7: tag = DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA; if (!parse_hdr_dynamic_metadata_block(cta, &data_block->hdr_dynamic_metadata, data, size)) goto skip; break; case 8: tag = DI_CTA_DATA_BLOCK_NATIVE_VIDEO_RESOLUTION; break; case 13: tag = DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF; break; case 14: tag = DI_CTA_DATA_BLOCK_YCBCR420; if (!parse_ycbcr420_block(cta, &data_block->ycbcr420, data, size)) goto skip; break; case 15: tag = DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP; parse_ycbcr420_cap_map(cta, &data_block->ycbcr420_cap_map, data, size); break; case 18: tag = DI_CTA_DATA_BLOCK_HDMI_AUDIO; break; case 19: tag = DI_CTA_DATA_BLOCK_ROOM_CONFIG; break; case 20: tag = DI_CTA_DATA_BLOCK_SPEAKER_LOCATION; break; case 32: tag = DI_CTA_DATA_BLOCK_INFOFRAME; if (!parse_infoframe_block(cta, &data_block->infoframe, data, size)) goto skip; break; case 34: tag = DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII; break; case 35: tag = DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VIII; break; case 42: tag = DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_X; break; case 120: tag = DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE; break; case 121: tag = DI_CTA_DATA_BLOCK_HDMI_SINK_CAP; break; case 1: /* Vendor-Specific Video Data Block */ case 17: /* Vendor-Specific Audio Data Block */ goto skip; default: /* Reserved */ add_failure_until(cta, 3, "Unknown CTA-861 Data Block (extended tag 0x"PRIx8", length %zu).", extended_tag, size); goto skip; } break; default: /* Reserved */ add_failure_until(cta, 3, "Unknown CTA-861 Data Block (tag 0x"PRIx8", length %zu).", raw_tag, size); goto skip; } data_block->tag = tag; assert(cta->data_blocks_len < EDID_CTA_MAX_DATA_BLOCKS); cta->data_blocks[cta->data_blocks_len++] = data_block; return true; skip: free(data_block); return true; error: destroy_data_block(data_block); return false; } bool _di_edid_cta_parse(struct di_edid_cta *cta, const uint8_t *data, size_t size, struct di_logger *logger) { uint8_t flags, dtd_start; uint8_t data_block_header, data_block_tag, data_block_size; size_t i; struct di_edid_detailed_timing_def_priv *detailed_timing_def; assert(size == 128); assert(data[0] == 0x02); cta->logger = logger; cta->revision = data[1]; dtd_start = data[2]; flags = data[3]; if (cta->revision >= 2) { cta->flags.it_underscan = has_bit(flags, 7); cta->flags.basic_audio = has_bit(flags, 6); cta->flags.ycc444 = has_bit(flags, 5); cta->flags.ycc422 = has_bit(flags, 4); cta->flags.native_dtds = get_bit_range(flags, 3, 0); } else if (flags != 0) { /* Reserved */ add_failure(cta, "Non-zero byte 3."); } if (dtd_start == 0) { return true; } else if (dtd_start < CTA_HEADER_SIZE || dtd_start >= size) { errno = EINVAL; return false; } i = CTA_HEADER_SIZE; while (i < dtd_start) { data_block_header = data[i]; data_block_tag = get_bit_range(data_block_header, 7, 5); data_block_size = get_bit_range(data_block_header, 4, 0); if (i + 1 + data_block_size > dtd_start) { _di_edid_cta_finish(cta); errno = EINVAL; return false; } if (!parse_data_block(cta, data_block_tag, &data[i + 1], data_block_size)) { _di_edid_cta_finish(cta); return false; } i += 1 + data_block_size; } if (i != dtd_start) add_failure(cta, "Offset is %"PRIu8", but should be %zu.", dtd_start, i); for (i = dtd_start; i + EDID_BYTE_DESCRIPTOR_SIZE <= CTA_DTD_END; i += EDID_BYTE_DESCRIPTOR_SIZE) { if (data[i] == 0) break; detailed_timing_def = _di_edid_parse_detailed_timing_def(&data[i]); if (!detailed_timing_def) { _di_edid_cta_finish(cta); return false; } assert(cta->detailed_timing_defs_len < EDID_CTA_MAX_DETAILED_TIMING_DEFS); cta->detailed_timing_defs[cta->detailed_timing_defs_len++] = detailed_timing_def; } /* All padding bytes after the last DTD must be zero */ while (i < CTA_DTD_END) { if (data[i] != 0) { add_failure(cta, "Padding: Contains non-zero bytes."); break; } i++; } cta->logger = NULL; return true; } void _di_edid_cta_finish(struct di_edid_cta *cta) { size_t i; for (i = 0; i < cta->data_blocks_len; i++) { destroy_data_block(cta->data_blocks[i]); } for (i = 0; i < cta->detailed_timing_defs_len; i++) { free(cta->detailed_timing_defs[i]); } } int di_edid_cta_get_revision(const struct di_edid_cta *cta) { return cta->revision; } const struct di_edid_cta_flags * di_edid_cta_get_flags(const struct di_edid_cta *cta) { return &cta->flags; } const struct di_cta_data_block *const * di_edid_cta_get_data_blocks(const struct di_edid_cta *cta) { return (const struct di_cta_data_block *const *) cta->data_blocks; } enum di_cta_data_block_tag di_cta_data_block_get_tag(const struct di_cta_data_block *block) { return block->tag; } const struct di_cta_svd *const * di_cta_data_block_get_svds(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_VIDEO) { return NULL; } return (const struct di_cta_svd *const *) block->video.svds; } const struct di_cta_svd *const * di_cta_data_block_get_ycbcr420_svds(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_YCBCR420) { return NULL; } return (const struct di_cta_svd *const *) block->ycbcr420.svds; } const struct di_cta_sad *const * di_cta_data_block_get_sads(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_AUDIO) { return NULL; } return (const struct di_cta_sad *const *) block->audio.sads; } const struct di_cta_speaker_alloc_block * di_cta_data_block_get_speaker_alloc(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_SPEAKER_ALLOC) { return NULL; } return &block->speaker_alloc; } const struct di_cta_colorimetry_block * di_cta_data_block_get_colorimetry(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_COLORIMETRY) { return NULL; } return &block->colorimetry; } const struct di_cta_hdr_static_metadata_block * di_cta_data_block_get_hdr_static_metadata(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA) { return NULL; } return &block->hdr_static_metadata.base; } const struct di_cta_hdr_dynamic_metadata_block * di_cta_data_block_get_hdr_dynamic_metadata(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA) { return NULL; } return &block->hdr_dynamic_metadata.base; } const struct di_cta_video_cap_block * di_cta_data_block_get_video_cap(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_VIDEO_CAP) { return NULL; } return &block->video_cap; } const struct di_cta_vesa_dddb * di_cta_data_block_get_vesa_dddb(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE) { return NULL; } return &block->vesa_dddb; } bool di_cta_ycbcr420_cap_map_supported(const struct di_cta_ycbcr420_cap_map *cap_map, size_t svd_index) { size_t byte, bit; if (cap_map->all) return true; byte = svd_index / 8; bit = svd_index % 8; if (byte >= EDID_CTA_MAX_YCBCR420_CAP_MAP_BLOCK_ENTRIES) return false; return cap_map->svd_bitmap[byte] & (1 << bit); } const struct di_cta_ycbcr420_cap_map * di_cta_data_block_get_ycbcr420_cap_map(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP) { return NULL; } return &block->ycbcr420_cap_map; } const struct di_cta_infoframe_block * di_cta_data_block_get_infoframe(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_INFOFRAME) { return NULL; } return &block->infoframe.block; } const struct di_edid_detailed_timing_def *const * di_edid_cta_get_detailed_timing_defs(const struct di_edid_cta *cta) { return (const struct di_edid_detailed_timing_def *const *) cta->detailed_timing_defs; } const struct di_cta_vesa_transfer_characteristics * di_cta_data_block_get_vesa_transfer_characteristics(const struct di_cta_data_block *block) { if (block->tag != DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC) { return NULL; } return &block->vesa_transfer_characteristics; } libdisplay-info-0.1.1/cvt.c000066400000000000000000000127651437312070200155500ustar00rootroot00000000000000/* * Copyright 2006-2012 Red Hat, Inc. * Copyright 2018-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Originally imported from edid-decode. */ #include #include /** * The size of the top and bottom overscan margin as a percentage of the active * vertical image. */ #define MARGIN_PERC 1.8 #define MIN_VSYNC_BP 550.0 #define MIN_V_PORCH 3 /** * Minimum vertical backporch for CVT and CVT RBv1. */ #define MIN_V_BPORCH 7 /** * Fixed vertical backporch for CVT RBv2 and RBv3. */ #define FIXED_V_BPORCH 6 #define C_PRIME 30.0 #define M_PRIME 300.0 /** * Minimum VBlank period (in µs) for RB timings. */ #define RB_MIN_VBLANK 460.0 void di_cvt_compute(struct di_cvt_timing *t, const struct di_cvt_options *options) { enum di_cvt_reduced_blanking_version rb = options->red_blank_ver; double cell_gran, h_pixels_rnd, v_lines_rnd, hor_margin, vert_margin, interlace, total_active_pixels, v_field_rate_rqd, clock_step, h_blank, rb_v_fporch, refresh_multiplier, rb_min_vblank, h_sync, v_sync, pixel_freq, v_blank, v_sync_bp, additional_hblank, h_period_est, ideal_duty_cycle, total_pixels, vbi_lines, rb_v_bporch, rb_min_vbi, total_v_lines, freq, h_front_porch, v_back_porch, act_h_freq; cell_gran = rb == DI_CVT_REDUCED_BLANKING_V2 ? 1 : 8; h_pixels_rnd = floor(options->h_pixels / cell_gran) * cell_gran; v_lines_rnd = options->int_rqd ? floor(options->v_lines / 2.0) : options->v_lines; hor_margin = options->margins_rqd ? floor((h_pixels_rnd * MARGIN_PERC / 100.0) / cell_gran) * cell_gran : 0; vert_margin = options->margins_rqd ? floor(MARGIN_PERC / 100.0 * v_lines_rnd) : 0; interlace = options->int_rqd ? 0.5 : 0; total_active_pixels = h_pixels_rnd + hor_margin * 2; v_field_rate_rqd = options->int_rqd ? options->ip_freq_rqd * 2 : options->ip_freq_rqd; clock_step = rb >= DI_CVT_REDUCED_BLANKING_V2 ? 0.001 : 0.25; h_blank = rb == DI_CVT_REDUCED_BLANKING_V1 ? 160 : 80; rb_v_fporch = rb == DI_CVT_REDUCED_BLANKING_V1 ? 3 : 1; refresh_multiplier = (rb == DI_CVT_REDUCED_BLANKING_V2 && options->video_opt) ? 1000.0 / 1001.0 : 1; rb_min_vblank = rb == DI_CVT_REDUCED_BLANKING_V3 ? options->vblank : RB_MIN_VBLANK; if (rb_min_vblank < RB_MIN_VBLANK) rb_min_vblank = RB_MIN_VBLANK; h_sync = 32; if (rb == DI_CVT_REDUCED_BLANKING_V3) { additional_hblank = options->additional_hblank; if (additional_hblank < 0) additional_hblank = 0; else if (additional_hblank > 120) additional_hblank = 120; h_blank += additional_hblank; } /* Determine VSync Width from aspect ratio */ if ((options->v_lines * 4 / 3) == options->h_pixels) v_sync = 4; else if ((options->v_lines * 16 / 9) == options->h_pixels) v_sync = 5; else if ((options->v_lines * 16 / 10) == options->h_pixels) v_sync = 6; else if (!(options->v_lines % 4) && ((options->v_lines * 5 / 4) == options->h_pixels)) v_sync = 7; else if ((options->v_lines * 15 / 9) == options->h_pixels) v_sync = 7; else /* Custom */ v_sync = 10; if (rb >= DI_CVT_REDUCED_BLANKING_V2) v_sync = 8; if (rb == DI_CVT_REDUCED_BLANKING_NONE) { h_period_est = (1.0 / v_field_rate_rqd - MIN_VSYNC_BP / 1000000.0) / (v_lines_rnd + vert_margin * 2 + MIN_V_PORCH + interlace) * 1000000.0; v_sync_bp = floor(MIN_VSYNC_BP / h_period_est) + 1; if (v_sync_bp < v_sync + MIN_V_BPORCH) v_sync_bp = v_sync + MIN_V_BPORCH; v_blank = v_sync_bp + MIN_V_PORCH; total_v_lines = v_lines_rnd + vert_margin * 2 + v_sync_bp + interlace + MIN_V_PORCH; ideal_duty_cycle = C_PRIME - M_PRIME * h_period_est / 1000.0; if (ideal_duty_cycle < 20) ideal_duty_cycle = 20; h_blank = floor(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2 * cell_gran)) * 2 * cell_gran; total_pixels = total_active_pixels + h_blank; h_sync = floor(total_pixels * 0.08 / cell_gran) * cell_gran; pixel_freq = floor(total_pixels / h_period_est / clock_step) * clock_step; } else { h_period_est = (1000000.0 / v_field_rate_rqd - rb_min_vblank) / (v_lines_rnd + vert_margin * 2); vbi_lines = floor(rb_min_vblank / h_period_est) + 1; rb_v_bporch = rb == DI_CVT_REDUCED_BLANKING_V1 ? MIN_V_BPORCH : FIXED_V_BPORCH; rb_min_vbi = rb_v_fporch + v_sync + rb_v_bporch; v_blank = vbi_lines < rb_min_vbi ? rb_min_vbi : vbi_lines; total_v_lines = v_blank + v_lines_rnd + vert_margin * 2 + interlace; if (rb == DI_CVT_REDUCED_BLANKING_V3 && options->early_vsync_rqd) rb_v_bporch = floor(vbi_lines / 2.0); if (rb == DI_CVT_REDUCED_BLANKING_V1) v_sync_bp = v_blank - rb_v_fporch; else v_sync_bp = v_sync + rb_v_bporch; total_pixels = h_blank + total_active_pixels; freq = v_field_rate_rqd * total_v_lines * total_pixels * refresh_multiplier; if (rb == DI_CVT_REDUCED_BLANKING_V3) pixel_freq = ceil(freq / 1000000.0 / clock_step) * clock_step; else pixel_freq = floor(freq / 1000000.0 / clock_step) * clock_step; } if (rb >= DI_CVT_REDUCED_BLANKING_V2) h_front_porch = 8; else h_front_porch = (h_blank / 2.0) - h_sync; v_back_porch = v_sync_bp - v_sync; act_h_freq = 1000 * pixel_freq / total_pixels; *t = (struct di_cvt_timing){ .act_pixel_freq = pixel_freq, .total_active_pixels = total_active_pixels, .v_lines_rnd = v_lines_rnd, .h_front_porch = h_front_porch, .h_sync = h_sync, .h_back_porch = h_blank - h_front_porch - h_sync, .v_front_porch = v_blank - v_back_porch - v_sync, .v_back_porch = v_back_porch, .v_sync = v_sync, .act_frame_rate = 1000 * act_h_freq / total_v_lines, }; } libdisplay-info-0.1.1/di-edid-decode/000077500000000000000000000000001437312070200173155ustar00rootroot00000000000000libdisplay-info-0.1.1/di-edid-decode/cta.c000066400000000000000000000745361437312070200202470ustar00rootroot00000000000000#include #include #include #include #include #include "di-edid-decode.h" static const char * video_format_picture_aspect_ratio_name(enum di_cta_video_format_picture_aspect_ratio ar) { switch (ar) { case DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3: return " 4:3 "; case DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9: return " 16:9 "; case DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27: return " 64:27 "; case DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135: return "256:135"; } abort(); /* unreachable */ } static void print_vic(uint8_t vic) { const struct di_cta_video_format *fmt; int32_t h_blank, v_blank, v_active; double refresh, h_freq_hz, pixel_clock_mhz, h_total, v_total; char buf[10]; printf(" VIC %3" PRIu8, vic); fmt = di_cta_video_format_from_vic(vic); if (fmt == NULL) return; v_active = fmt->v_active; if (fmt->interlaced) v_active /= 2; h_blank = fmt->h_front + fmt->h_sync + fmt->h_back; v_blank = fmt->v_front + fmt->v_sync + fmt->v_back; h_total = fmt->h_active + h_blank; v_total = v_active + v_blank; if (fmt->interlaced) v_total += 0.5; refresh = (double) fmt->pixel_clock_hz / (h_total * v_total); h_freq_hz = (double) fmt->pixel_clock_hz / h_total; pixel_clock_mhz = (double) fmt->pixel_clock_hz / (1000 * 1000); snprintf(buf, sizeof(buf), "%d%s", fmt->v_active, fmt->interlaced ? "i" : ""); printf(":"); printf(" %5dx%-5s", fmt->h_active, buf); printf(" %10.6f Hz", refresh); printf(" %s", video_format_picture_aspect_ratio_name(fmt->picture_aspect_ratio)); printf(" %8.3f kHz %13.6f MHz", h_freq_hz / 1000, pixel_clock_mhz); } static void printf_cta_svd(const struct di_cta_svd *svd) { print_vic(svd->vic); if (svd->native) printf(" (native)"); printf("\n"); } static void printf_cta_svds(const struct di_cta_svd *const *svds) { size_t i; for (i = 0; svds[i] != NULL; i++) printf_cta_svd(svds[i]); } static const char * vesa_dddb_interface_type_name(enum di_cta_vesa_dddb_interface_type type) { switch (type) { case DI_CTA_VESA_DDDB_INTERFACE_VGA: return "Analog (15HD/VGA)"; case DI_CTA_VESA_DDDB_INTERFACE_NAVI_V: return "Analog (VESA NAVI-V (15HD))"; case DI_CTA_VESA_DDDB_INTERFACE_NAVI_D: return "Analog (VESA NAVI-D)"; case DI_CTA_VESA_DDDB_INTERFACE_LVDS: return "LVDS"; case DI_CTA_VESA_DDDB_INTERFACE_RSDS: return "RSDS"; case DI_CTA_VESA_DDDB_INTERFACE_DVI_D: return "DVI-D"; case DI_CTA_VESA_DDDB_INTERFACE_DVI_I_ANALOG: return "DVI-I analog"; case DI_CTA_VESA_DDDB_INTERFACE_DVI_I_DIGITAL: return "DVI-I digital"; case DI_CTA_VESA_DDDB_INTERFACE_HDMI_A: return "HDMI-A"; case DI_CTA_VESA_DDDB_INTERFACE_HDMI_B: return "HDMI-B"; case DI_CTA_VESA_DDDB_INTERFACE_MDDI: return "MDDI"; case DI_CTA_VESA_DDDB_INTERFACE_DISPLAYPORT: return "DisplayPort"; case DI_CTA_VESA_DDDB_INTERFACE_IEEE_1394: return "IEEE-1394"; case DI_CTA_VESA_DDDB_INTERFACE_M1_ANALOG: return "M1 analog"; case DI_CTA_VESA_DDDB_INTERFACE_M1_DIGITAL: return "M1 digital"; } abort(); /* unreachable */ } static const char * vesa_dddb_content_protection_name(enum di_cta_vesa_dddb_content_protection cp) { switch (cp) { case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_NONE: return "None"; case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_HDCP: return "HDCP"; case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DTCP: return "DTCP"; case DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DPCP: return "DPCP"; } abort(); /* unreachable */ } static const char * vesa_dddb_default_orientation_name(enum di_cta_vesa_dddb_default_orientation orientation) { switch (orientation) { case DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_LANDSCAPE: return "Landscape"; case DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_PORTAIT: return "Portrait"; case DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_UNFIXED: return "Not Fixed"; case DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_UNDEFINED: return "Undefined"; } abort(); /* unreachable */ } static const char * vesa_dddb_rotation_cap_name(enum di_cta_vesa_dddb_rotation_cap rot) { switch (rot) { case DI_CTA_VESA_DDDB_ROTATION_CAP_NONE: return "None"; case DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_CLOCKWISE: return "Can rotate 90 degrees clockwise"; case DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_COUNTERCLOCKWISE: return "Can rotate 90 degrees counterclockwise"; case DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_EITHER: return "Can rotate 90 degrees in either direction"; } abort(); /* unreachable */ } static const char * vesa_dddb_zero_pixel_location_name(enum di_cta_vesa_dddb_zero_pixel_location loc) { switch (loc) { case DI_CTA_VESA_DDDB_ZERO_PIXEL_UPPER_LEFT: return "Upper Left"; case DI_CTA_VESA_DDDB_ZERO_PIXEL_UPPER_RIGHT: return "Upper Right"; case DI_CTA_VESA_DDDB_ZERO_PIXEL_LOWER_LEFT: return "Lower Left"; case DI_CTA_VESA_DDDB_ZERO_PIXEL_LOWER_RIGHT: return "Lower Right"; } abort(); /* unreachable */ } static const char * vesa_dddb_scan_direction_name(enum di_cta_vesa_dddb_scan_direction dir) { switch (dir) { case DI_CTA_VESA_DDDB_SCAN_DIRECTION_UNDEFINED: return "Not defined"; case DI_CTA_VESA_DDDB_SCAN_DIRECTION_FAST_LONG_SLOW_SHORT: return "Fast Scan is on the Major (Long) Axis and Slow Scan is on the Minor Axis"; case DI_CTA_VESA_DDDB_SCAN_DIRECTION_FAST_SHORT_SLOW_LONG: return "Fast Scan is on the Minor (Short) Axis and Slow Scan is on the Major Axis"; } abort(); /* unreachable */ } static const char * vesa_dddb_subpixel_layout_name(enum di_cta_vesa_dddb_subpixel_layout subpixel) { switch (subpixel) { case DI_CTA_VESA_DDDB_SUBPIXEL_UNDEFINED: return "Not defined"; case DI_CTA_VESA_DDDB_SUBPIXEL_RGB_VERT: return "RGB vertical stripes"; case DI_CTA_VESA_DDDB_SUBPIXEL_RGB_HORIZ: return "RGB horizontal stripes"; case DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_VERT: return "Vertical stripes using primary order"; case DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_HORIZ: return "Horizontal stripes using primary order"; case DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_RGGB: return "Quad sub-pixels, red at top left"; case DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_GBRG: return "Quad sub-pixels, red at bottom left"; case DI_CTA_VESA_DDDB_SUBPIXEL_DELTA_RGB: return "Delta (triad) RGB sub-pixels"; case DI_CTA_VESA_DDDB_SUBPIXEL_MOSAIC: return "Mosaic"; case DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_ANY: return "Quad sub-pixels, RGB + 1 additional color"; case DI_CTA_VESA_DDDB_SUBPIXEL_FIVE: return "Five sub-pixels, RGB + 2 additional colors"; case DI_CTA_VESA_DDDB_SUBPIXEL_SIX: return "Six sub-pixels, RGB + 3 additional colors"; case DI_CTA_VESA_DDDB_SUBPIXEL_CLAIRVOYANTE_PENTILE: return "Clairvoyante, Inc. PenTile Matrix (tm) layout"; } abort(); /* unreachable */ } static const char * vesa_dddb_dithering_type_name(enum di_cta_vesa_dddb_dithering_type dithering) { switch (dithering) { case DI_CTA_VESA_DDDB_DITHERING_NONE: return "None"; case DI_CTA_VESA_DDDB_DITHERING_SPACIAL: return "Spacial"; case DI_CTA_VESA_DDDB_DITHERING_TEMPORAL: return "Temporal"; case DI_CTA_VESA_DDDB_DITHERING_SPATIAL_AND_TEMPORAL: return "Spatial and Temporal"; } abort(); /* unreachable */ } static const char * vesa_dddb_frame_rate_conversion_name(enum di_cta_vesa_dddb_frame_rate_conversion conv) { switch (conv) { case DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_NONE: return "None"; case DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_SINGLE_BUFFERING: return "Single Buffering"; case DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_DOUBLE_BUFFERING: return "Double Buffering"; case DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_ADVANCED: return "Advanced Frame Rate Conversion"; } abort(); /* unreachable */ } static float truncate_chromaticity_coord(float coord) { return floorf(coord * 10000) / 10000; } static const char * vesa_dddb_resp_time_transition_name(enum di_cta_vesa_dddb_resp_time_transition t) { switch (t) { case DI_CTA_VESA_DDDB_RESP_TIME_BLACK_TO_WHITE: return "Black -> White"; case DI_CTA_VESA_DDDB_RESP_TIME_WHITE_TO_BLACK: return "White -> Black"; } abort(); /* unreachable */ } static void print_cta_vesa_dddb(const struct di_cta_vesa_dddb *dddb) { size_t i; printf(" Interface Type: %s", vesa_dddb_interface_type_name(dddb->interface_type)); if (dddb->num_channels != 0) { const char *kind; switch (dddb->interface_type) { case DI_CTA_VESA_DDDB_INTERFACE_LVDS: case DI_CTA_VESA_DDDB_INTERFACE_RSDS: kind = "lanes"; break; default: kind = "channels"; break; } printf(" %d %s", dddb->num_channels, kind); } printf("\n"); printf(" Interface Standard Version: %d.%d\n", dddb->interface_version, dddb->interface_release); printf(" Content Protection Support: %s\n", vesa_dddb_content_protection_name(dddb->content_protection)); printf(" Minimum Clock Frequency: %d MHz\n", dddb->min_clock_freq_mhz); printf(" Maximum Clock Frequency: %d MHz\n", dddb->max_clock_freq_mhz); printf(" Device Native Pixel Format: %dx%d\n", dddb->native_horiz_pixels, dddb->native_vert_pixels); printf(" Aspect Ratio: %.2f\n", dddb->aspect_ratio); printf(" Default Orientation: %s\n", vesa_dddb_default_orientation_name(dddb->default_orientation)); printf(" Rotation Capability: %s\n", vesa_dddb_rotation_cap_name(dddb->rotation_cap)); printf(" Zero Pixel Location: %s\n", vesa_dddb_zero_pixel_location_name(dddb->zero_pixel_location)); printf(" Scan Direction: %s\n", vesa_dddb_scan_direction_name(dddb->scan_direction)); printf(" Subpixel Information: %s\n", vesa_dddb_subpixel_layout_name(dddb->subpixel_layout)); printf(" Horizontal and vertical dot/pixel pitch: %.2f x %.2f mm\n", dddb->horiz_pitch_mm, dddb->vert_pitch_mm); printf(" Dithering: %s\n", vesa_dddb_dithering_type_name(dddb->dithering_type)); printf(" Direct Drive: %s\n", dddb->direct_drive ? "Yes" : "No"); printf(" Overdrive %srecommended\n", dddb->overdrive_not_recommended ? "not " : ""); printf(" Deinterlacing: %s\n", dddb->deinterlacing ? "Yes" : "No"); printf(" Audio Support: %s\n", dddb->audio_support ? "Yes" : "No"); printf(" Separate Audio Inputs Provided: %s\n", dddb->separate_audio_inputs ? "Yes" : "No"); printf(" Audio Input Override: %s\n", dddb->audio_input_override ? "Yes" : "No"); if (dddb->audio_delay_provided) printf(" Audio Delay: %d ms\n", dddb->audio_delay_ms); else printf(" Audio Delay: no information provided\n"); printf(" Frame Rate/Mode Conversion: %s\n", vesa_dddb_frame_rate_conversion_name(dddb->frame_rate_conversion)); if (dddb->frame_rate_range_hz != 0) printf(" Frame Rate Range: %d fps +/- %d fps\n", dddb->frame_rate_native_hz, dddb->frame_rate_range_hz); else printf(" Nominal Frame Rate: %d fps\n", dddb->frame_rate_native_hz); printf(" Color Bit Depth: %d @ interface, %d @ display\n", dddb->bit_depth_interface, dddb->bit_depth_display); if (dddb->additional_primary_chromaticities_len > 0) { printf(" Additional Primary Chromaticities:\n"); for (i = 0; i < dddb->additional_primary_chromaticities_len; i++) printf(" Primary %zu: %.4f, %.4f\n", 4 + i, truncate_chromaticity_coord(dddb->additional_primary_chromaticities[i].x), truncate_chromaticity_coord(dddb->additional_primary_chromaticities[i].y)); } printf(" Response Time %s: %d ms\n", vesa_dddb_resp_time_transition_name(dddb->resp_time_transition), dddb->resp_time_ms); printf(" Overscan: %d%% x %d%%\n", dddb->overscan_horiz_pct, dddb->overscan_vert_pct); } static uint8_t encode_max_luminance(float max) { if (max == 0) return 0; return (uint8_t) (log2f(max / 50) * 32); } static uint8_t encode_min_luminance(float min, float max) { if (min == 0) return 0; return (uint8_t) (255 * sqrtf(min / max * 100)); } static void print_cta_hdr_static_metadata(const struct di_cta_hdr_static_metadata_block *metadata) { printf(" Electro optical transfer functions:\n"); if (metadata->eotfs->traditional_sdr) printf(" Traditional gamma - SDR luminance range\n"); if (metadata->eotfs->traditional_hdr) printf(" Traditional gamma - HDR luminance range\n"); if (metadata->eotfs->pq) printf(" SMPTE ST2084\n"); if (metadata->eotfs->hlg) printf(" Hybrid Log-Gamma\n"); printf(" Supported static metadata descriptors:\n"); if (metadata->descriptors->type1) printf(" Static metadata type 1\n"); /* TODO: figure out a way to print raw values? */ if (metadata->desired_content_max_luminance != 0) printf(" Desired content max luminance: %" PRIu8 " (%.3f cd/m^2)\n", encode_max_luminance(metadata->desired_content_max_luminance), metadata->desired_content_max_luminance); if (metadata->desired_content_max_frame_avg_luminance != 0) printf(" Desired content max frame-average luminance: %" PRIu8 " (%.3f cd/m^2)\n", encode_max_luminance(metadata->desired_content_max_frame_avg_luminance), metadata->desired_content_max_frame_avg_luminance); if (metadata->desired_content_min_luminance != 0) printf(" Desired content min luminance: %" PRIu8 " (%.3f cd/m^2)\n", encode_min_luminance(metadata->desired_content_min_luminance, metadata->desired_content_max_luminance), metadata->desired_content_min_luminance); } static void print_cta_hdr_dynamic_metadata(const struct di_cta_hdr_dynamic_metadata_block *metadata) { if (metadata->type1) { printf(" HDR Dynamic Metadata Type 1\n"); printf(" Version: %d\n", metadata->type1->type_1_hdr_metadata_version); } if (metadata->type2) { printf(" HDR Dynamic Metadata Type 2\n"); printf(" Version: %d\n", metadata->type2->ts_103_433_spec_version); if (metadata->type2->ts_103_433_1_capable) printf(" ETSI TS 103 433-1 capable\n"); if (metadata->type2->ts_103_433_2_capable) printf(" ETSI TS 103 433-2 [i.12] capable\n"); if (metadata->type2->ts_103_433_3_capable) printf(" ETSI TS 103 433-3 [i.13] capable\n"); } if (metadata->type3) { printf(" HDR Dynamic Metadata Type 3\n"); } if (metadata->type4) { printf(" HDR Dynamic Metadata Type 4\n"); printf(" Version: %d\n", metadata->type4->type_4_hdr_metadata_version); } if (metadata->type256) { printf(" HDR Dynamic Metadata Type 256\n"); printf(" Version: %d\n", metadata->type256->graphics_overlay_flag_version); } } static void print_cta_vesa_transfer_characteristics(const struct di_cta_vesa_transfer_characteristics *tf) { size_t i; switch (tf->usage) { case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_WHITE: printf(" White"); break; case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_RED: printf(" Red"); break; case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_GREEN: printf(" Green"); break; case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_BLUE: printf(" Blue"); break; } printf(" transfer characteristics:"); for (i = 0; i < tf->points_len; i++) printf(" %u", (uint16_t) roundf(tf->points[i] * 1023.0f)); printf("\n"); uncommon_features.cta_transfer_characteristics = true; } static const char * cta_audio_format_name(enum di_cta_audio_format format) { switch (format) { case DI_CTA_AUDIO_FORMAT_LPCM: return "Linear PCM"; case DI_CTA_AUDIO_FORMAT_AC3: return "AC-3"; case DI_CTA_AUDIO_FORMAT_MPEG1: return "MPEG 1 (Layers 1 & 2)"; case DI_CTA_AUDIO_FORMAT_MP3: return "MPEG 1 Layer 3 (MP3)"; case DI_CTA_AUDIO_FORMAT_MPEG2: return "MPEG2 (multichannel)"; case DI_CTA_AUDIO_FORMAT_AAC_LC: return "AAC LC"; case DI_CTA_AUDIO_FORMAT_DTS: return "DTS"; case DI_CTA_AUDIO_FORMAT_ATRAC: return "ATRAC"; case DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO: return "One Bit Audio"; case DI_CTA_AUDIO_FORMAT_ENHANCED_AC3: return "Enhanced AC-3 (DD+)"; case DI_CTA_AUDIO_FORMAT_DTS_HD: return "DTS-HD"; case DI_CTA_AUDIO_FORMAT_MAT: return "MAT (MLP)"; case DI_CTA_AUDIO_FORMAT_DST: return "DST"; case DI_CTA_AUDIO_FORMAT_WMA_PRO: return "WMA Pro"; case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC: return "MPEG-4 HE AAC"; case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2: return "MPEG-4 HE AAC v2"; case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC: return "MPEG-4 AAC LC"; case DI_CTA_AUDIO_FORMAT_DRA: return "DRA"; case DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND: return "MPEG-4 HE AAC + MPEG Surround"; case DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND: return "MPEG-4 AAC LC + MPEG Surround"; case DI_CTA_AUDIO_FORMAT_MPEGH_3D: return "MPEG-H 3D Audio"; case DI_CTA_AUDIO_FORMAT_AC4: return "AC-4"; case DI_CTA_AUDIO_FORMAT_LPCM_3D: return "L-PCM 3D Audio"; } abort(); } static const char * cta_sad_mpegh_3d_level_name(enum di_cta_sad_mpegh_3d_level level) { switch (level) { case DI_CTA_SAD_MPEGH_3D_LEVEL_UNSPECIFIED: return "Unspecified"; case DI_CTA_SAD_MPEGH_3D_LEVEL_1: return "Level 1"; case DI_CTA_SAD_MPEGH_3D_LEVEL_2: return "Level 2"; case DI_CTA_SAD_MPEGH_3D_LEVEL_3: return "Level 3"; case DI_CTA_SAD_MPEGH_3D_LEVEL_4: return "Level 4"; case DI_CTA_SAD_MPEGH_3D_LEVEL_5: return "Level 5"; } abort(); } static void print_cta_sads(const struct di_cta_sad *const *sads) { size_t i; const struct di_cta_sad *sad; for (i = 0; sads[i] != NULL; i++) { sad = sads[i]; printf(" %s:\n", cta_audio_format_name(sad->format)); if (sad->max_channels != 0) printf(" Max channels: %d\n", sad->max_channels); if (sad->mpegh_3d) printf(" MPEG-H 3D Audio Level: %s\n", cta_sad_mpegh_3d_level_name(sad->mpegh_3d->level)); printf(" Supported sample rates (kHz):"); if (sad->supported_sample_rates->has_192_khz) printf(" 192"); if (sad->supported_sample_rates->has_176_4_khz) printf(" 176.4"); if (sad->supported_sample_rates->has_96_khz) printf(" 96"); if (sad->supported_sample_rates->has_88_2_khz) printf(" 88.2"); if (sad->supported_sample_rates->has_48_khz) printf(" 48"); if (sad->supported_sample_rates->has_44_1_khz) printf(" 44.1"); if (sad->supported_sample_rates->has_32_khz) printf(" 32"); printf("\n"); if (sad->lpcm) { printf(" Supported sample sizes (bits):"); if (sad->lpcm->has_sample_size_24_bits) printf(" 24"); if (sad->lpcm->has_sample_size_20_bits) printf(" 20"); if (sad->lpcm->has_sample_size_16_bits) printf(" 16"); printf("\n"); } if (sad->max_bitrate_kbs != 0) printf(" Maximum bit rate: %d kb/s\n", sad->max_bitrate_kbs); if (sad->enhanced_ac3 && sad->enhanced_ac3->supports_joint_object_coding) printf(" Supports Joint Object Coding\n"); if (sad->enhanced_ac3 && sad->enhanced_ac3->supports_joint_object_coding_ACMOD28) printf(" Supports Joint Object Coding with ACMOD28\n"); if (sad->mat) { if (sad->mat->supports_object_audio_and_channel_based) { printf(" Supports Dolby TrueHD, object audio PCM and channel-based PCM\n"); printf(" Hash calculation %srequired for object audio PCM or channel-based PCM\n", sad->mat->requires_hash_calculation ? "" : "not "); } else { printf(" Supports only Dolby TrueHD\n"); } } if (sad->wma_pro) { printf(" Profile: %u\n",sad->wma_pro->profile); } if (sad->mpegh_3d && sad->mpegh_3d->low_complexity_profile) printf(" Supports MPEG-H 3D Audio Low Complexity Profile\n"); if (sad->mpegh_3d && sad->mpegh_3d->baseline_profile) printf(" Supports MPEG-H 3D Audio Baseline Profile\n"); if (sad->mpeg_aac) { printf(" AAC audio frame lengths:%s%s\n", sad->mpeg_aac->has_frame_length_1024 ? " 1024_TL" : "", sad->mpeg_aac->has_frame_length_960 ? " 960_TL" : ""); } if (sad->mpeg_surround) { printf(" Supports %s signaled MPEG Surround data\n", sad->mpeg_surround->signaling == DI_CTA_SAD_MPEG_SURROUND_SIGNALING_IMPLICIT ? "only implicitly" : "implicitly and explicitly"); } if (sad->mpeg_aac_le && sad->mpeg_aac_le->supports_multichannel_sound) printf(" Supports 22.2ch System H\n"); } } static void print_ycbcr420_cap_map(const struct di_edid_cta *cta, const struct di_cta_ycbcr420_cap_map *map) { const struct di_cta_data_block *const *data_blocks; const struct di_cta_data_block *data_block; enum di_cta_data_block_tag tag; const struct di_cta_svd *const *svds; size_t i, j, svd_index = 0; data_blocks = di_edid_cta_get_data_blocks(cta); for (i = 0; data_blocks[i] != NULL; i++) { data_block = data_blocks[i]; tag = di_cta_data_block_get_tag(data_block); if (tag != DI_CTA_DATA_BLOCK_VIDEO) continue; svds = di_cta_data_block_get_svds(data_block); for (j = 0; svds[j] != NULL; j++) { if (di_cta_ycbcr420_cap_map_supported(map, svd_index)) printf_cta_svd(svds[j]); svd_index++; } } } static const char * cta_infoframe_type_name(enum di_cta_infoframe_type type) { switch (type) { case DI_CTA_INFOFRAME_TYPE_AUXILIARY_VIDEO_INFORMATION: return "Auxiliary Video Information InfoFrame (2)"; case DI_CTA_INFOFRAME_TYPE_SOURCE_PRODUCT_DESCRIPTION: return "Source Product Description InfoFrame (3)"; case DI_CTA_INFOFRAME_TYPE_AUDIO: return "Audio InfoFrame (4)"; case DI_CTA_INFOFRAME_TYPE_MPEG_SOURCE: return "MPEG Source InfoFrame (5)"; case DI_CTA_INFOFRAME_TYPE_NTSC_VBI: return "NTSC VBI InfoFrame (6)"; case DI_CTA_INFOFRAME_TYPE_DYNAMIC_RANGE_AND_MASTERING: return "Dynamic Range and Mastering InfoFrame (7)"; } abort(); } static void print_infoframes(const struct di_cta_infoframe_descriptor *const *infoframes) { size_t i; const struct di_cta_infoframe_descriptor *infoframe; for (i = 0; infoframes[i] != NULL; i++) { infoframe = infoframes[i]; printf(" %s\n", cta_infoframe_type_name(infoframe->type)); } } static const char * cta_data_block_tag_name(enum di_cta_data_block_tag tag) { switch (tag) { case DI_CTA_DATA_BLOCK_AUDIO: return "Audio Data Block"; case DI_CTA_DATA_BLOCK_VIDEO: return "Video Data Block"; case DI_CTA_DATA_BLOCK_SPEAKER_ALLOC: return "Speaker Allocation Data Block"; case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC: return "VESA Display Transfer Characteristics Data Block"; case DI_CTA_DATA_BLOCK_VIDEO_FORMAT: return "Video Format Data Block"; case DI_CTA_DATA_BLOCK_VIDEO_CAP: return "Video Capability Data Block"; case DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE: return "VESA Video Display Device Data Block"; case DI_CTA_DATA_BLOCK_COLORIMETRY: return "Colorimetry Data Block"; case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA: return "HDR Static Metadata Data Block"; case DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA: return "HDR Dynamic Metadata Data Block"; case DI_CTA_DATA_BLOCK_NATIVE_VIDEO_RESOLUTION: return "Native Video Resolution Data Block"; case DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF: return "Video Format Preference Data Block"; case DI_CTA_DATA_BLOCK_YCBCR420: return "YCbCr 4:2:0 Video Data Block"; case DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP: return "YCbCr 4:2:0 Capability Map Data Block"; case DI_CTA_DATA_BLOCK_HDMI_AUDIO: return "HDMI Audio Data Block"; case DI_CTA_DATA_BLOCK_ROOM_CONFIG: return "Room Configuration Data Block"; case DI_CTA_DATA_BLOCK_SPEAKER_LOCATION: return "Speaker Location Data Block"; case DI_CTA_DATA_BLOCK_INFOFRAME: return "InfoFrame Data Block"; case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII: return "DisplayID Type VII Video Timing Data Block"; case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VIII: return "DisplayID Type VIII Video Timing Data Block"; case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_X: return "DisplayID Type X Video Timing Data Block"; case DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE : return "HDMI Forum EDID Extension Override Data Block"; case DI_CTA_DATA_BLOCK_HDMI_SINK_CAP: return "HDMI Forum Sink Capability Data Block"; } return "Unknown CTA-861 Data Block"; } static const char * video_cap_over_underscan_name(enum di_cta_video_cap_over_underscan over_underscan, const char *unknown) { switch (over_underscan) { case DI_CTA_VIDEO_CAP_UNKNOWN_OVER_UNDERSCAN: return unknown; case DI_CTA_VIDEO_CAP_ALWAYS_OVERSCAN: return "Always Overscanned"; case DI_CTA_VIDEO_CAP_ALWAYS_UNDERSCAN: return "Always Underscanned"; case DI_CTA_VIDEO_CAP_BOTH_OVER_UNDERSCAN: return "Supports both over- and underscan"; } abort(); } void print_cta(const struct di_edid_cta *cta) { const struct di_edid_cta_flags *cta_flags; const struct di_cta_data_block *const *data_blocks; const struct di_cta_data_block *data_block; enum di_cta_data_block_tag data_block_tag; const struct di_cta_svd *const *svds; const struct di_cta_speaker_alloc_block *speaker_alloc; const struct di_cta_video_cap_block *video_cap; const struct di_cta_vesa_dddb *vesa_dddb; const struct di_cta_colorimetry_block *colorimetry; const struct di_cta_hdr_static_metadata_block *hdr_static_metadata; const struct di_cta_hdr_dynamic_metadata_block *hdr_dynamic_metadata; const struct di_cta_vesa_transfer_characteristics *transfer_characteristics; const struct di_cta_sad *const *sads; const struct di_cta_ycbcr420_cap_map *ycbcr420_cap_map; const struct di_cta_infoframe_block *infoframe; size_t i; const struct di_edid_detailed_timing_def *const *detailed_timing_defs; printf(" Revision: %d\n", di_edid_cta_get_revision(cta)); cta_flags = di_edid_cta_get_flags(cta); if (cta_flags->it_underscan) { printf(" Underscans IT Video Formats by default\n"); } if (cta_flags->basic_audio) { printf(" Basic audio support\n"); } if (cta_flags->ycc444) { printf(" Supports YCbCr 4:4:4\n"); } if (cta_flags->ycc422) { printf(" Supports YCbCr 4:2:2\n"); } printf(" Native detailed modes: %d\n", cta_flags->native_dtds); data_blocks = di_edid_cta_get_data_blocks(cta); for (i = 0; data_blocks[i] != NULL; i++) { data_block = data_blocks[i]; data_block_tag = di_cta_data_block_get_tag(data_block); printf(" %s:\n", cta_data_block_tag_name(data_block_tag)); switch (data_block_tag) { case DI_CTA_DATA_BLOCK_VIDEO: svds = di_cta_data_block_get_svds(data_block); printf_cta_svds(svds); break; case DI_CTA_DATA_BLOCK_YCBCR420: svds = di_cta_data_block_get_ycbcr420_svds(data_block); printf_cta_svds(svds); break; case DI_CTA_DATA_BLOCK_SPEAKER_ALLOC: speaker_alloc = di_cta_data_block_get_speaker_alloc(data_block); if (speaker_alloc->flw_frw) printf(" FLw/FRw - Front Left/Right Wide\n"); if (speaker_alloc->flc_frc) printf(" FLc/FRc - Front Left/Right of Center\n"); if (speaker_alloc->bc) printf(" BC - Back Center\n"); if (speaker_alloc->bl_br) printf(" BL/BR - Back Left/Right\n"); if (speaker_alloc->fc) printf(" FC - Front Center\n"); if (speaker_alloc->lfe1) printf(" LFE1 - Low Frequency Effects 1\n"); if (speaker_alloc->fl_fr) printf(" FL/FR - Front Left/Right\n"); if (speaker_alloc->tpsil_tpsir) printf(" TpSiL/TpSiR - Top Side Left/Right\n"); if (speaker_alloc->sil_sir) printf(" SiL/SiR - Side Left/Right\n"); if (speaker_alloc->tpbc) printf(" TpBC - Top Back Center\n"); if (speaker_alloc->lfe2) printf(" LFE2 - Low Frequency Effects 2\n"); if (speaker_alloc->ls_rs) printf(" LS/RS - Left/Right Surround\n"); if (speaker_alloc->tpfc) printf(" TpFC - Top Front Center\n"); if (speaker_alloc->tpc) printf(" TpC - Top Center\n"); if (speaker_alloc->tpfl_tpfr) printf(" TpFL/TpFR - Top Front Left/Right\n"); if (speaker_alloc->btfl_btfr) printf(" BtFL/BtFR - Bottom Front Left/Right\n"); if (speaker_alloc->btfc) printf(" BtFC - Bottom Front Center\n"); if (speaker_alloc->tpbl_tpbr) printf(" TpBL/TpBR - Top Back Left/Right\n"); break; case DI_CTA_DATA_BLOCK_VIDEO_CAP: video_cap = di_cta_data_block_get_video_cap(data_block); printf(" YCbCr quantization: %s\n", video_cap->selectable_ycc_quantization_range ? "Selectable (via AVI YQ)" : "No Data"); printf(" RGB quantization: %s\n", video_cap->selectable_ycc_quantization_range ? "Selectable (via AVI Q)" : "No Data"); printf(" PT scan behavior: %s\n", video_cap_over_underscan_name(video_cap->pt_over_underscan, "No Data")); printf(" IT scan behavior: %s\n", video_cap_over_underscan_name(video_cap->it_over_underscan, "IT video formats not supported")); printf(" CE scan behavior: %s\n", video_cap_over_underscan_name(video_cap->ce_over_underscan, "CE video formats not supported")); break; case DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE: vesa_dddb = di_cta_data_block_get_vesa_dddb(data_block); print_cta_vesa_dddb(vesa_dddb); break; case DI_CTA_DATA_BLOCK_COLORIMETRY: colorimetry = di_cta_data_block_get_colorimetry(data_block); if (colorimetry->xvycc_601) printf(" xvYCC601\n"); if (colorimetry->xvycc_709) printf(" xvYCC709\n"); if (colorimetry->sycc_601) printf(" sYCC601\n"); if (colorimetry->opycc_601) printf(" opYCC601\n"); if (colorimetry->oprgb) printf(" opRGB\n"); if (colorimetry->bt2020_cycc) printf(" BT2020cYCC\n"); if (colorimetry->bt2020_ycc) printf(" BT2020YCC\n"); if (colorimetry->bt2020_rgb) printf(" BT2020RGB\n"); if (colorimetry->ictcp) printf(" ICtCp\n"); if (colorimetry->st2113_rgb) printf(" ST2113RGB\n"); break; case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA: hdr_static_metadata = di_cta_data_block_get_hdr_static_metadata(data_block); print_cta_hdr_static_metadata(hdr_static_metadata); break; case DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA: hdr_dynamic_metadata = di_cta_data_block_get_hdr_dynamic_metadata(data_block); print_cta_hdr_dynamic_metadata(hdr_dynamic_metadata); break; case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC: transfer_characteristics = di_cta_data_block_get_vesa_transfer_characteristics(data_block); print_cta_vesa_transfer_characteristics(transfer_characteristics); break; case DI_CTA_DATA_BLOCK_AUDIO: sads = di_cta_data_block_get_sads(data_block); print_cta_sads(sads); break; case DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP: ycbcr420_cap_map = di_cta_data_block_get_ycbcr420_cap_map(data_block); print_ycbcr420_cap_map(cta, ycbcr420_cap_map); break; case DI_CTA_DATA_BLOCK_INFOFRAME: infoframe = di_cta_data_block_get_infoframe(data_block); printf(" VSIFs: %d\n", infoframe->num_simultaneous_vsifs - 1); print_infoframes(infoframe->infoframes); break; default: break; /* Ignore */ } } detailed_timing_defs = di_edid_cta_get_detailed_timing_defs(cta); if (detailed_timing_defs[0]) { printf(" Detailed Timing Descriptors:\n"); } for (i = 0; detailed_timing_defs[i] != NULL; i++) { print_detailed_timing_def(detailed_timing_defs[i]); } } libdisplay-info-0.1.1/di-edid-decode/displayid.c000066400000000000000000000276121437312070200214530ustar00rootroot00000000000000#include #include #include #include #include "di-edid-decode.h" static bool is_displayid_base_block = true; static void print_displayid_display_params(const struct di_displayid_display_params *params) { printf(" Image size: %.1f mm x %.1f mm\n", params->horiz_image_mm, params->vert_image_mm); printf(" Display native pixel format: %dx%d\n", params->horiz_pixels, params->vert_pixels); printf(" Feature support flags:\n"); if (params->features->audio) printf(" Audio support on video interface\n"); if (params->features->separate_audio_inputs) printf(" Separate audio inputs provided\n"); if (params->features->audio_input_override) printf(" Audio input override\n"); if (params->features->power_management) printf(" Power management (DPM)\n"); if (params->features->fixed_timing) printf(" Fixed timing\n"); if (params->features->fixed_pixel_format) printf(" Fixed pixel format\n"); if (params->features->ai) printf(" Support ACP, ISRC1, or ISRC2packets\n"); if (params->features->deinterlacing) printf(" De-interlacing\n"); if (params->gamma != 0) printf(" Gamma: %.2f\n", params->gamma); printf(" Aspect ratio: %.2f\n", params->aspect_ratio); printf(" Dynamic bpc native: %d\n", params->bits_per_color_native); printf(" Dynamic bpc overall: %d\n", params->bits_per_color_overall); } static void get_displayid_type_i_timing_aspect_ratio(enum di_displayid_type_i_timing_aspect_ratio ratio, int *horiz, int *vert) { switch (ratio) { case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_1_1: *horiz = *vert = 1; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_5_4: *horiz = 5; *vert = 4; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_4_3: *horiz = 4; *vert = 3; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_15_9: *horiz = 15; *vert = 9; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_9: *horiz = 16; *vert = 9; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_10: *horiz = 16; *vert = 10; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_64_27: *horiz = 64; *vert = 27; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_256_135: *horiz = 256; *vert = 135; return; case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED: *horiz = *vert = 0; return; } abort(); /* Unreachable */ } static const char * displayid_type_i_timing_stereo_3d_name(enum di_displayid_type_i_timing_stereo_3d stereo_3d) { switch (stereo_3d) { case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_NEVER: return "no 3D stereo"; case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_ALWAYS: return "3D stereo"; case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_USER: return "3D stereo depends on user action"; } abort(); /* Unreachable */ } static const char * displayid_type_i_timing_sync_polarity_name(enum di_displayid_type_i_timing_sync_polarity pol) { switch (pol) { case DI_DISPLAYID_TYPE_I_TIMING_SYNC_NEGATIVE: return "N"; case DI_DISPLAYID_TYPE_I_TIMING_SYNC_POSITIVE: return "P"; } abort(); /* Unreachable */ } static void print_displayid_type_i_timing(const struct di_displayid_type_i_timing *t) { int horiz_total, vert_total; int horiz_back_porch, vert_back_porch; int horiz_ratio, vert_ratio; double pixel_clock_hz, refresh, horiz_freq_hz; get_displayid_type_i_timing_aspect_ratio(t->aspect_ratio, &horiz_ratio, &vert_ratio); horiz_total = t->horiz_active + t->horiz_blank; vert_total = t->vert_active + t->vert_blank; pixel_clock_hz = t->pixel_clock_mhz * 1000 * 1000; refresh = pixel_clock_hz / (horiz_total * vert_total); horiz_freq_hz = pixel_clock_hz / horiz_total; printf(" DTD:"); printf(" %5dx%-5d", t->horiz_active, t->vert_active); if (t->interlaced) { printf("i"); } printf(" %10.6f Hz", refresh); printf(" %3d:%-3d", horiz_ratio, vert_ratio); printf(" %8.3f kHz %13.6f MHz", horiz_freq_hz / 1000, t->pixel_clock_mhz); printf(" (aspect "); if (t->aspect_ratio == DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED) printf("undefined"); else printf("%d:%d", horiz_ratio, vert_ratio); printf(", %s", displayid_type_i_timing_stereo_3d_name(t->stereo_3d)); if (t->preferred) printf(", preferred"); printf(")\n"); horiz_back_porch = t->horiz_blank - t->horiz_sync_width - t->horiz_offset; printf(" Hfront %4d Hsync %3d Hback %4d Hpol %s", t->horiz_offset, t->horiz_sync_width, horiz_back_porch, displayid_type_i_timing_sync_polarity_name(t->horiz_sync_polarity)); printf("\n"); vert_back_porch = t->vert_blank - t->vert_sync_width - t->vert_offset; printf(" Vfront %4d Vsync %3d Vback %4d Vpol %s", t->vert_offset, t->vert_sync_width, vert_back_porch, displayid_type_i_timing_sync_polarity_name(t->vert_sync_polarity)); printf("\n"); } static void print_displayid_type_i_timing_block(const struct di_displayid_data_block *data_block) { size_t i; const struct di_displayid_type_i_timing *const *timings; timings = di_displayid_data_block_get_type_i_timings(data_block); for (i = 0; timings[i] != NULL; i++) print_displayid_type_i_timing(timings[i]); } static const char * displayid_tiled_topo_missing_recv_behavior_name(enum di_displayid_tiled_topo_missing_recv_behavior behavior) { switch (behavior) { case DI_DISPLAYID_TILED_TOPO_MISSING_RECV_UNDEF: return "Undefined"; case DI_DISPLAYID_TILED_TOPO_MISSING_RECV_TILE_ONLY: return "Image is displayed at the Tile Location"; } abort(); /* unreachable */ } static const char * displayid_tiled_topo_single_recv_behavior_name(enum di_displayid_tiled_topo_single_recv_behavior behavior) { switch (behavior) { case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_UNDEF: return "Undefined"; case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_TILE_ONLY: return "Image is displayed at the Tile Location"; case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_SCALED: return "Image is scaled to fit the entire tiled display"; case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_CLONED: return "Image is cloned to all other tiles"; } abort(); /* unreachable */ } static void print_displayid_tiled_topo(const struct di_displayid_tiled_topo *tiled_topo) { printf(" Capabilities:\n"); printf(" Behavior if it is the only tile: %s\n", displayid_tiled_topo_single_recv_behavior_name(tiled_topo->caps->single_recv_behavior)); printf(" Behavior if more than one tile and fewer than total number of tiles: %s\n", displayid_tiled_topo_missing_recv_behavior_name(tiled_topo->caps->missing_recv_behavior)); if (tiled_topo->caps->single_enclosure) printf(" Tiled display consists of a single physical display enclosure\n"); else printf(" Tiled display consists of multiple physical display enclosures\n"); printf(" Num horizontal tiles: %d Num vertical tiles: %d\n", tiled_topo->total_horiz_tiles, tiled_topo->total_vert_tiles); printf(" Tile location: %d, %d\n", tiled_topo->horiz_tile_location - 1, tiled_topo->vert_tile_location - 1); printf(" Tile resolution: %dx%d\n", tiled_topo->horiz_tile_pixels, tiled_topo->vert_tile_lines); if (tiled_topo->bezel != NULL) { printf(" Top bevel size: %.1f pixels\n", tiled_topo->bezel->top_px); printf(" Bottom bevel size: %.1f pixels\n", tiled_topo->bezel->bottom_px); printf(" Right bevel size: %.1f pixels\n", tiled_topo->bezel->right_px); printf(" Left bevel size: %.1f pixels\n", tiled_topo->bezel->left_px); } printf(" Tiled Display Manufacturer/Vendor ID: %.3s\n", tiled_topo->vendor_id); printf(" Tiled Display Product ID Code: %" PRIu16 "\n", tiled_topo->product_code); printf(" Tiled Display Serial Number: %" PRIu32 "\n", tiled_topo->serial_number); } static const char * displayid_product_type_name(enum di_displayid_product_type type) { switch (type) { case DI_DISPLAYID_PRODUCT_TYPE_EXTENSION: return "Extension Section"; case DI_DISPLAYID_PRODUCT_TYPE_TEST: return "Test Structure; test equipment only"; case DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL: return "Display panel or other transducer, LCD or PDP module, etc."; case DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY: return "Standalone display device"; case DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER: return "Television receiver"; case DI_DISPLAYID_PRODUCT_TYPE_REPEATER: return "Repeater/translator"; case DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE: return "DIRECT DRIVE monitor"; } abort(); } static const char * displayid_data_block_tag_name(enum di_displayid_data_block_tag tag) { switch (tag) { case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID: return "Product Identification Data Block (0x00)"; case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS: return "Display Parameters Data Block (0x01)"; case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT: return "Color Characteristics Data Block"; case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING: return "Video Timing Modes Type 1 - Detailed Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING: return "Video Timing Modes Type 2 - Detailed Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING: return "Video Timing Modes Type 3 - Short Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING: return "Video Timing Modes Type 4 - DMT Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_VESA_TIMING: return "Supported Timing Modes Type 1 - VESA DMT Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_CEA_TIMING: return "Supported Timing Modes Type 2 - CTA-861 Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS: return "Video Timing Range Data Block"; case DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL: return "Product Serial Number Data Block"; case DI_DISPLAYID_DATA_BLOCK_ASCII_STRING: return "GP ASCII String Data Block"; case DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA: return "Display Device Data Data Block"; case DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ: return "Interface Power Sequencing Data Block"; case DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT: return "Transfer Characteristics Data Block"; case DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE: return "Display Interface Data Block"; case DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE: return "Stereo Display Interface Data Block (0x10)"; case DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING: return "Video Timing Modes Type 5 - Short Timings Data Block"; case DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO: return "Tiled Display Topology Data Block (0x12)"; case DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING: return "Video Timing Modes Type 6 - Detailed Timings Data Block"; } abort(); } void print_displayid(const struct di_displayid *displayid) { const struct di_displayid_data_block *const *data_blocks; const struct di_displayid_data_block *data_block; enum di_displayid_data_block_tag tag; size_t i; const struct di_displayid_display_params *display_params; const struct di_displayid_tiled_topo *tiled_topo; printf(" Version: %d.%d\n", di_displayid_get_version(displayid), di_displayid_get_revision(displayid)); if (is_displayid_base_block) printf(" Display Product Type: %s\n", displayid_product_type_name(di_displayid_get_product_type(displayid))); is_displayid_base_block = false; data_blocks = di_displayid_get_data_blocks(displayid); for (i = 0; data_blocks[i] != NULL; i++) { data_block = data_blocks[i]; tag = di_displayid_data_block_get_tag(data_block); printf(" %s:\n", displayid_data_block_tag_name(tag)); switch (tag) { case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS: display_params = di_displayid_data_block_get_display_params(data_block); print_displayid_display_params(display_params); break; case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING: print_displayid_type_i_timing_block(data_block); break; case DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO: tiled_topo = di_displayid_data_block_get_tiled_topo(data_block); print_displayid_tiled_topo(tiled_topo); break; default: break; /* Ignore */ } } } libdisplay-info-0.1.1/di-edid-decode/edid.c000066400000000000000000001007531437312070200203740ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "di-edid-decode.h" static size_t num_detailed_timing_defs = 0; static const char * standard_timing_aspect_ratio_name(enum di_edid_standard_timing_aspect_ratio aspect_ratio) { switch (aspect_ratio) { case DI_EDID_STANDARD_TIMING_16_10: return "16:10"; case DI_EDID_STANDARD_TIMING_4_3: return " 4:3 "; case DI_EDID_STANDARD_TIMING_5_4: return " 5:4 "; case DI_EDID_STANDARD_TIMING_16_9: return "16:9 "; } abort(); } static void print_standard_timing(const struct di_edid_standard_timing *t) { int32_t vert_video; const struct di_dmt_timing *dmt; int hbl, vbl, horiz_total, vert_total; double refresh, horiz_freq_hz, pixel_clock_mhz, pixel_clock_khz; struct di_gtf_options gtf_options; struct di_gtf_timing gtf; vert_video = di_edid_standard_timing_get_vert_video(t); dmt = di_edid_standard_timing_get_dmt(t); printf(" "); if (dmt) { hbl = dmt->horiz_blank - 2 * dmt->horiz_border; vbl = dmt->vert_blank - 2 * dmt->vert_border; horiz_total = dmt->horiz_video + hbl; vert_total = dmt->vert_video + vbl; refresh = (double) dmt->pixel_clock_hz / (horiz_total * vert_total); horiz_freq_hz = (double) dmt->pixel_clock_hz / horiz_total; pixel_clock_mhz = (double) dmt->pixel_clock_hz / (1000 * 1000); printf("DMT 0x%02x", dmt ? dmt->dmt_id : 0); } else { /* TODO: CVT timings */ gtf_options = (struct di_gtf_options) { .h_pixels = t->horiz_video, .v_lines = vert_video, .ip_param = DI_GTF_IP_PARAM_V_FRAME_RATE, .ip_freq_rqd = t->refresh_rate_hz, .m = DI_GTF_DEFAULT_M, .c = DI_GTF_DEFAULT_C, .k = DI_GTF_DEFAULT_K, .j = DI_GTF_DEFAULT_J, }; di_gtf_compute(>f, >f_options); hbl = gtf.h_front_porch + gtf.h_sync + gtf.h_back_porch + 2 * gtf.h_border; vbl = gtf.v_front_porch + gtf.v_sync + gtf.v_back_porch + 2 * gtf.v_border; horiz_total = gtf.h_pixels + hbl; vert_total = gtf.v_lines + vbl; /* Upstream edid-decode rounds the pixel clock to kHz... */ pixel_clock_khz = round(gtf.pixel_freq_mhz * 1000); refresh = (pixel_clock_khz * 1000) / (horiz_total * vert_total); horiz_freq_hz = (pixel_clock_khz * 1000) / horiz_total; pixel_clock_mhz = pixel_clock_khz / 1000; printf("GTF "); } printf(":"); printf(" %5dx%-5d", t->horiz_video, vert_video); printf(" %10.6f Hz", refresh); printf(" %s ", standard_timing_aspect_ratio_name(t->aspect_ratio)); printf(" %8.3f kHz %13.6f MHz", horiz_freq_hz / 1000, pixel_clock_mhz); if (dmt != NULL && dmt->reduced_blanking) printf(" (RB)"); printf("\n"); } static int gcd(int a, int b) { int tmp; while (b) { tmp = b; b = a % b; a = tmp; } return a; } static void compute_aspect_ratio(int width, int height, int *horiz_ratio, int *vert_ratio) { int d; d = gcd(width, height); if (d == 0) { *horiz_ratio = *vert_ratio = 0; } else { *horiz_ratio = width / d; *vert_ratio = height / d; } if (*horiz_ratio == 8 && *vert_ratio == 5) { *horiz_ratio = 16; *vert_ratio = 10; } } /** * Join a list of strings into a comma-separated string. * * The list must be NULL-terminated. */ static char * join_str(const char *l[]) { char *out = NULL; size_t out_size = 0, i; FILE *f; f = open_memstream(&out, &out_size); if (!f) { return NULL; } for (i = 0; l[i] != NULL; i++) { if (i > 0) { fprintf(f, ", "); } fprintf(f, "%s", l[i]); } fclose(f); return out; } static bool has_established_timings_i_ii(const struct di_edid_established_timings_i_ii *timings) { return timings->has_720x400_70hz || timings->has_720x400_88hz || timings->has_640x480_60hz || timings->has_640x480_67hz || timings->has_640x480_72hz || timings->has_640x480_75hz || timings->has_800x600_56hz || timings->has_800x600_60hz || timings->has_800x600_72hz || timings->has_800x600_75hz || timings->has_832x624_75hz || timings->has_1024x768_87hz_interlaced || timings->has_1024x768_60hz || timings->has_1024x768_70hz || timings->has_1024x768_75hz || timings->has_1280x1024_75hz || timings->has_1152x870_75hz; } static const char * detailed_timing_def_stereo_name(enum di_edid_detailed_timing_def_stereo stereo) { switch (stereo) { case DI_EDID_DETAILED_TIMING_DEF_STEREO_NONE: return "none"; case DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_RIGHT: return "field sequential L/R"; case DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_LEFT: return "field sequential R/L"; case DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_RIGHT: return "interleaved right even"; case DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_LEFT: return "interleaved left even"; case DI_EDID_DETAILED_TIMING_DEF_STEREO_4_WAY_INTERLEAVED: return "four way interleaved"; case DI_EDID_DETAILED_TIMING_DEF_STEREO_SIDE_BY_SIDE_INTERLEAVED: return "side by side interleaved"; } abort(); } static const char * detailed_timing_def_signal_type_name(enum di_edid_detailed_timing_def_signal_type type) { switch (type) { case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_ANALOG_COMPOSITE: return "analog composite"; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_BIPOLAR_ANALOG_COMPOSITE: return "bipolar analog composite"; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_COMPOSITE: return "digital composite"; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_SEPARATE: /* edid-decode doesn't print anything in this case */ return NULL; } abort(); } static bool detailed_timing_def_sync_serrations(const struct di_edid_detailed_timing_def *def) { switch (def->signal_type) { case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_ANALOG_COMPOSITE: return def->analog_composite->sync_serrations; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_BIPOLAR_ANALOG_COMPOSITE: return def->bipolar_analog_composite->sync_serrations; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_COMPOSITE: return def->digital_composite->sync_serrations; default: return false; } } static bool detailed_timing_def_sync_on_green(const struct di_edid_detailed_timing_def *def) { switch (def->signal_type) { case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_ANALOG_COMPOSITE: return def->analog_composite->sync_on_green; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_BIPOLAR_ANALOG_COMPOSITE: return def->bipolar_analog_composite->sync_on_green; default: return false; } } static const char * detailed_timing_def_sync_polarity_name(enum di_edid_detailed_timing_def_sync_polarity polarity) { switch (polarity) { case DI_EDID_DETAILED_TIMING_DEF_SYNC_NEGATIVE: return "N"; case DI_EDID_DETAILED_TIMING_DEF_SYNC_POSITIVE: return "P"; } abort(); } void print_detailed_timing_def(const struct di_edid_detailed_timing_def *def) { int hbl, vbl, horiz_total, vert_total; int horiz_back_porch, vert_back_porch; int horiz_ratio, vert_ratio; double refresh, horiz_freq_hz; const char *flags[32] = {0}; const char *signal_type_name; char size_mm[64]; size_t flags_len = 0; enum di_edid_detailed_timing_def_sync_polarity polarity; hbl = def->horiz_blank - 2 * def->horiz_border; vbl = def->vert_blank - 2 * def->vert_border; horiz_total = def->horiz_video + hbl; vert_total = def->vert_video + vbl; refresh = (double) def->pixel_clock_hz / (horiz_total * vert_total); horiz_freq_hz = (double) def->pixel_clock_hz / horiz_total; compute_aspect_ratio(def->horiz_video, def->vert_video, &horiz_ratio, &vert_ratio); signal_type_name = detailed_timing_def_signal_type_name(def->signal_type); if (signal_type_name != NULL) { flags[flags_len++] = signal_type_name; } if (detailed_timing_def_sync_serrations(def)) { flags[flags_len++] = "serrate"; } if (detailed_timing_def_sync_on_green(def)) { flags[flags_len++] = "sync-on-green"; } if (def->stereo != DI_EDID_DETAILED_TIMING_DEF_STEREO_NONE) { flags[flags_len++] = detailed_timing_def_stereo_name(def->stereo); } if (def->horiz_image_mm != 0 || def->vert_image_mm != 0) { snprintf(size_mm, sizeof(size_mm), "%d mm x %d mm", def->horiz_image_mm, def->vert_image_mm); flags[flags_len++] = size_mm; } assert(flags_len < sizeof(flags) / sizeof(flags[0])); printf(" DTD %zu:", ++num_detailed_timing_defs); printf(" %5dx%-5d", def->horiz_video, def->vert_video); if (def->interlaced) { printf("i"); } printf(" %10.6f Hz", refresh); printf(" %3u:%-3u", horiz_ratio, vert_ratio); printf(" %8.3f kHz %13.6f MHz", horiz_freq_hz / 1000, (double) def->pixel_clock_hz / (1000 * 1000)); if (flags_len > 0) { char *flags_str = join_str(flags); printf(" (%s)", flags_str); free(flags_str); } printf("\n"); horiz_back_porch = hbl - def->horiz_sync_pulse - def->horiz_front_porch; printf(" Hfront %4d Hsync %3d Hback %4d", def->horiz_front_porch, def->horiz_sync_pulse, horiz_back_porch); if (def->horiz_border != 0) { printf(" Hborder %d", def->horiz_border); } if (def->signal_type == DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_COMPOSITE) { polarity = def->digital_composite->sync_horiz_polarity; printf(" Hpol %s", detailed_timing_def_sync_polarity_name(polarity)); } else if (def->signal_type == DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_SEPARATE) { polarity = def->digital_separate->sync_horiz_polarity; printf(" Hpol %s", detailed_timing_def_sync_polarity_name(polarity)); } printf("\n"); vert_back_porch = vbl - def->vert_sync_pulse - def->vert_front_porch; printf(" Vfront %4u Vsync %3u Vback %4d", def->vert_front_porch, def->vert_sync_pulse, vert_back_porch); if (def->vert_border != 0) { printf(" Vborder %d", def->vert_border); } if (def->signal_type == DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_SEPARATE) { polarity = def->digital_separate->sync_vert_polarity; printf(" Vpol %s", detailed_timing_def_sync_polarity_name(polarity)); } printf("\n"); } static const char * display_desc_tag_name(enum di_edid_display_descriptor_tag tag) { switch (tag) { case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL: return "Display Product Serial Number"; case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING: return "Alphanumeric Data String"; case DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS: return "Display Range Limits"; case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME: return "Display Product Name"; case DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT: return "Color Point Data"; case DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS: return "Standard Timing Identifications"; case DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA: return "Display Color Management Data"; case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES: return "CVT 3 Byte Timing Codes"; case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III: return "Established timings III"; case DI_EDID_DISPLAY_DESCRIPTOR_DUMMY: return "Dummy Descriptor"; } abort(); } static const char * display_range_limits_type_name(enum di_edid_display_range_limits_type type) { switch (type) { case DI_EDID_DISPLAY_RANGE_LIMITS_BARE: return "Bare Limits"; case DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF: return "GTF"; case DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF: return "Secondary GTF"; case DI_EDID_DISPLAY_RANGE_LIMITS_CVT: return "CVT"; } abort(); } static const char * cvt_aspect_ratio_name(enum di_edid_cvt_aspect_ratio aspect_ratio) { switch (aspect_ratio) { case DI_EDID_CVT_ASPECT_RATIO_4_3: return "4:3"; case DI_EDID_CVT_ASPECT_RATIO_16_9: return "16:9"; case DI_EDID_CVT_ASPECT_RATIO_16_10: return "16:10"; case DI_EDID_CVT_ASPECT_RATIO_5_4: return "5:4"; case DI_EDID_CVT_ASPECT_RATIO_15_9: return "15:9"; } abort(); } static float truncate_chromaticity_coord(float coord) { return floorf(coord * 10000) / 10000; } static void print_color_point(const struct di_edid_color_point *c) { printf("Index: %u White: %.4f, %.4f ", c->index, truncate_chromaticity_coord(c->white_x), truncate_chromaticity_coord(c->white_y)); if (c->gamma != 0) { printf("Gamma: %.2f\n", c->gamma); } else { printf("Gamma: is defined in an extension block\n"); } } static void print_dmt_timing(const struct di_dmt_timing *t) { int hbl, vbl, horiz_total, vert_total, horiz_ratio, vert_ratio; double refresh, horiz_freq_hz, pixel_clock_mhz; compute_aspect_ratio(t->horiz_video, t->vert_video, &horiz_ratio, &vert_ratio); hbl = t->horiz_blank - 2 * t->horiz_border; vbl = t->vert_blank - 2 * t->vert_border; horiz_total = t->horiz_video + hbl; vert_total = t->vert_video + vbl; refresh = (double) t->pixel_clock_hz / (horiz_total * vert_total); horiz_freq_hz = (double) t->pixel_clock_hz / horiz_total; pixel_clock_mhz = (double) t->pixel_clock_hz / (1000 * 1000); printf(" DMT 0x%02x:", t->dmt_id); printf(" %5dx%-5d", t->horiz_video, t->vert_video); printf(" %10.6f Hz", refresh); printf(" %3u:%-3u", horiz_ratio, vert_ratio); printf(" %8.3f kHz %13.6f MHz", horiz_freq_hz / 1000, pixel_clock_mhz); if (t->reduced_blanking) printf(" (RB)"); printf("\n"); } static void print_cvt_timing(struct di_cvt_timing *t, struct di_cvt_options *options, int hratio, int vratio, bool preferred, bool rb) { double hbl, htotal; hbl = t->h_front_porch + t->h_sync + t->h_back_porch; htotal = t->total_active_pixels + hbl; printf(" CVT: %5dx%-5d", (int)options->h_pixels, (int)options->v_lines); printf(" %10.6f Hz", t->act_frame_rate); printf(" %3u:%-3u", hratio, vratio); printf(" %8.3f kHz %13.6f MHz", t->act_pixel_freq * 1000 / htotal, (double) t->act_pixel_freq); if (preferred || rb) { printf(" (%s%s%s)", rb ? "RB" : "", (preferred && rb) ? ", " : "", preferred ? "preferred vertical rate" : ""); } printf("\n"); } static void print_cvt_timing_code(const struct di_edid_cvt_timing_code *t) { struct di_cvt_timing timing; struct di_cvt_options options; enum di_edid_cvt_timing_code_preferred_vrate pref = t->preferred_vertical_rate; int hratio, vratio; options.int_rqd = false; options.margins_rqd = false; options.v_lines = t->addressable_lines_per_field; switch (t->aspect_ratio) { case DI_EDID_CVT_TIMING_CODE_4_3: hratio = 4; vratio = 3; break; case DI_EDID_CVT_TIMING_CODE_16_9: hratio = 16; vratio = 9; break; case DI_EDID_CVT_TIMING_CODE_16_10: hratio = 16; vratio = 10; break; case DI_EDID_CVT_TIMING_CODE_15_9: hratio = 15; vratio = 9; break; } options.h_pixels = 8 * (((options.v_lines * hratio) / vratio) / 8); if (t->supports_50hz_sb) { options.ip_freq_rqd = 50; options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE; di_cvt_compute(&timing, &options); print_cvt_timing(&timing, &options, hratio, vratio, pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ, false); } if (t->supports_60hz_sb) { options.ip_freq_rqd = 60; options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE; di_cvt_compute(&timing, &options); print_cvt_timing(&timing, &options, hratio, vratio, pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ && !t->supports_60hz_rb, false); } if (t->supports_75hz_sb) { options.ip_freq_rqd = 75; options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE; di_cvt_compute(&timing, &options); print_cvt_timing(&timing, &options, hratio, vratio, pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ, false); } if (t->supports_85hz_sb) { options.ip_freq_rqd = 85; options.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE; di_cvt_compute(&timing, &options); print_cvt_timing(&timing, &options, hratio, vratio, pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ, false); } if (t->supports_60hz_rb) { options.ip_freq_rqd = 60; options.red_blank_ver = DI_CVT_REDUCED_BLANKING_V1; di_cvt_compute(&timing, &options); print_cvt_timing(&timing, &options, hratio, vratio, pref == DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ, true); } } static void print_display_desc(const struct di_edid *edid, const struct di_edid_display_descriptor *desc) { enum di_edid_display_descriptor_tag tag; const char *tag_name, *str; const struct di_edid_display_range_limits *range_limits; enum di_edid_display_range_limits_type range_limits_type; const struct di_edid_standard_timing *const *standard_timings; const struct di_edid_color_point *const *color_points; const struct di_dmt_timing *const *established_timings_iii; const struct di_edid_color_management_data *color_management_data; const struct di_edid_cvt_timing_code *const *cvt_timings; size_t i; tag = di_edid_display_descriptor_get_tag(desc); tag_name = display_desc_tag_name(tag); printf(" %s:", tag_name); switch (tag) { case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL: case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING: case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME: str = di_edid_display_descriptor_get_string(desc); printf(" '%s'\n", str); break; case DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS: range_limits = di_edid_display_descriptor_get_range_limits(desc); range_limits_type = range_limits->type; if (di_edid_get_revision(edid) < 4 && range_limits_type == DI_EDID_DISPLAY_RANGE_LIMITS_BARE) { /* edid-decode always prints "GTF" for EDID 1.3 and * earlier even if the display doesn't support it */ range_limits_type = DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF; } printf("\n Monitor ranges (%s): %d-%d Hz V, %d-%d kHz H", display_range_limits_type_name(range_limits_type), range_limits->min_vert_rate_hz, range_limits->max_vert_rate_hz, range_limits->min_horiz_rate_hz / 1000, range_limits->max_horiz_rate_hz / 1000); if (range_limits->max_pixel_clock_hz != 0) { printf(", max dotclock %d MHz", range_limits->max_pixel_clock_hz / (1000 * 1000)); } printf("\n"); switch (range_limits_type) { case DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF: printf(" GTF Secondary Curve Block:\n"); printf(" Start frequency: %u kHz\n", range_limits->secondary_gtf->start_freq_hz / 1000); printf(" C: %.1f%%\n", range_limits->secondary_gtf->c); printf(" M: %u%%/kHz\n", (int) range_limits->secondary_gtf->m); printf(" K: %u\n", (int) range_limits->secondary_gtf->k); printf(" J: %.1f%%\n", range_limits->secondary_gtf->j); break; case DI_EDID_DISPLAY_RANGE_LIMITS_CVT: printf(" CVT version %d.%d\n", range_limits->cvt->version, range_limits->cvt->revision); if (range_limits->cvt->max_horiz_px != 0) printf(" Max active pixels per line: %d\n", range_limits->cvt->max_horiz_px); printf(" Supported aspect ratios:"); if (range_limits->cvt->supported_aspect_ratio & DI_EDID_CVT_ASPECT_RATIO_4_3) printf(" 4:3"); if (range_limits->cvt->supported_aspect_ratio & DI_EDID_CVT_ASPECT_RATIO_16_9) printf(" 16:9"); if (range_limits->cvt->supported_aspect_ratio & DI_EDID_CVT_ASPECT_RATIO_16_10) printf(" 16:10"); if (range_limits->cvt->supported_aspect_ratio & DI_EDID_CVT_ASPECT_RATIO_5_4) printf(" 5:4"); if (range_limits->cvt->supported_aspect_ratio & DI_EDID_CVT_ASPECT_RATIO_15_9) printf(" 15:9"); printf("\n"); printf(" Preferred aspect ratio: %s\n", cvt_aspect_ratio_name(range_limits->cvt->preferred_aspect_ratio)); if (range_limits->cvt->standard_blanking) printf(" Supports CVT standard blanking\n"); if (range_limits->cvt->reduced_blanking) printf(" Supports CVT reduced blanking\n"); if (range_limits->cvt->supported_scaling != 0) { printf(" Supported display scaling:\n"); if (range_limits->cvt->supported_scaling & DI_EDID_CVT_SCALING_HORIZ_SHRINK) printf(" Horizontal shrink\n"); if (range_limits->cvt->supported_scaling & DI_EDID_CVT_SCALING_HORIZ_STRETCH) printf(" Horizontal stretch\n"); if (range_limits->cvt->supported_scaling & DI_EDID_CVT_SCALING_VERT_SHRINK) printf(" Vertical shrink\n"); if (range_limits->cvt->supported_scaling & DI_EDID_CVT_SCALING_VERT_STRETCH) printf(" Vertical stretch\n"); } printf(" Preferred vertical refresh: %d Hz\n", range_limits->cvt->preferred_vert_refresh_hz); break; default: break; } break; case DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS: standard_timings = di_edid_display_descriptor_get_standard_timings(desc); printf("\n"); for (i = 0; standard_timings[i] != NULL; i++) { printf(" "); print_standard_timing(standard_timings[i]); } break; case DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT: color_points = di_edid_display_descriptor_get_color_points(desc); for (i = 0; color_points[i] != NULL; i++) { printf(" "); print_color_point(color_points[i]); } uncommon_features.color_point_descriptor = true; break; case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III: established_timings_iii = di_edid_display_descriptor_get_established_timings_iii(desc); printf("\n"); for (i = 0; established_timings_iii[i] != NULL; i++) { print_dmt_timing(established_timings_iii[i]); } break; case DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA: color_management_data = di_edid_display_descriptor_get_color_management_data(desc); printf(" Version : %d\n", color_management_data->version); printf(" Red a3 : %.2f\n", color_management_data->red_a3); printf(" Red a2 : %.2f\n", color_management_data->red_a2); printf(" Green a3: %.2f\n", color_management_data->green_a3); printf(" Green a2: %.2f\n", color_management_data->green_a2); printf(" Blue a3 : %.2f\n", color_management_data->blue_a3); printf(" Blue a2 : %.2f\n", color_management_data->blue_a2); uncommon_features.color_management_data = true; break; case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES: cvt_timings = di_edid_display_descriptor_get_cvt_timing_codes(desc); printf("\n"); for (i = 0; cvt_timings[i] != NULL; i++) print_cvt_timing_code(cvt_timings[i]); break; default: printf("\n"); break; /* TODO: print other tags */ } } static const char * analog_signal_level_std_name(enum di_edid_video_input_analog_signal_level_std std) { switch (std) { case DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_0: return "0.700 : 0.300 : 1.000 V p-p"; case DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_1: return "0.714 : 0.286 : 1.000 V p-p"; case DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_2: return "1.000 : 0.400 : 1.400 V p-p"; case DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_3: return "0.700 : 0.000 : 0.700 V p-p"; } abort(); } static const char * digital_interface_name(enum di_edid_video_input_digital_interface interface) { switch (interface) { case DI_EDID_VIDEO_INPUT_DIGITAL_UNDEFINED: return "Digital interface is not defined"; case DI_EDID_VIDEO_INPUT_DIGITAL_DVI: return "DVI interface"; case DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_A: return "HDMI-a interface"; case DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_B: return "HDMI-b interface"; case DI_EDID_VIDEO_INPUT_DIGITAL_MDDI: return "MDDI interface"; case DI_EDID_VIDEO_INPUT_DIGITAL_DISPLAYPORT: return "DisplayPort interface"; } abort(); } static const char * display_color_type_name(enum di_edid_display_color_type type) { switch (type) { case DI_EDID_DISPLAY_COLOR_MONOCHROME: return "Monochrome or grayscale display"; case DI_EDID_DISPLAY_COLOR_RGB: return "RGB color display"; case DI_EDID_DISPLAY_COLOR_NON_RGB: return "Non-RGB color display"; case DI_EDID_DISPLAY_COLOR_UNDEFINED: return "Undefined display color type"; } abort(); } void print_edid(const struct di_edid *edid) { const struct di_edid_vendor_product *vendor_product; const struct di_edid_video_input_analog *video_input_analog; const struct di_edid_video_input_digital *video_input_digital; const struct di_edid_screen_size *screen_size; float gamma; const struct di_edid_dpms *dpms; enum di_edid_display_color_type display_color_type; const struct di_edid_color_encoding_formats *color_encoding_formats; const struct di_edid_misc_features *misc_features; const struct di_edid_chromaticity_coords *chromaticity_coords; const struct di_edid_established_timings_i_ii *established_timings_i_ii; const struct di_edid_standard_timing *const *standard_timings; const struct di_edid_detailed_timing_def *const *detailed_timing_defs; const struct di_edid_display_descriptor *const *display_descs; size_t i; printf("Block 0, Base EDID:\n"); printf(" EDID Structure Version & Revision: %d.%d\n", di_edid_get_version(edid), di_edid_get_revision(edid)); vendor_product = di_edid_get_vendor_product(edid); printf(" Vendor & Product Identification:\n"); printf(" Manufacturer: %.3s\n", vendor_product->manufacturer); printf(" Model: %" PRIu16 "\n", vendor_product->product); if (vendor_product->serial != 0) { printf(" Serial Number: %" PRIu32 "\n", vendor_product->serial); } if (vendor_product->model_year != 0) { printf(" Model year: %d\n", vendor_product->model_year); } else { printf(" Made in: week %d of %d\n", vendor_product->manufacture_week, vendor_product->manufacture_year); } printf(" Basic Display Parameters & Features:\n"); video_input_analog = di_edid_get_video_input_analog(edid); if (video_input_analog) { printf(" Analog display\n"); printf(" Signal Level Standard: %s\n", analog_signal_level_std_name(video_input_analog->signal_level_std)); switch (video_input_analog->video_setup) { case DI_EDID_VIDEO_INPUT_ANALOG_BLANK_LEVEL_EQ_BLACK: printf(" Blank level equals black level\n"); break; case DI_EDID_VIDEO_INPUT_ANALOG_BLANK_TO_BLACK_SETUP_PEDESTAL: printf(" Blank-to-black setup/pedestal\n"); break; } printf(" Sync:"); if (video_input_analog->sync_separate) printf(" Separate"); if (video_input_analog->sync_composite) printf(" Composite"); if (video_input_analog->sync_on_green) printf(" SyncOnGreen"); if (video_input_analog->sync_serrations) printf(" Serration"); printf("\n"); } video_input_digital = di_edid_get_video_input_digital(edid); if (video_input_digital) { printf(" Digital display\n"); if (di_edid_get_revision(edid) >= 4) { if (video_input_digital->color_bit_depth == 0) { printf(" Color depth is undefined\n"); } else { printf(" Bits per primary color channel: %d\n", video_input_digital->color_bit_depth); } printf(" %s\n", digital_interface_name(video_input_digital->interface)); } if (video_input_digital->dfp1) printf(" DFP 1.x compatible TMDS\n"); } screen_size = di_edid_get_screen_size(edid); if (screen_size->width_cm > 0) { printf(" Maximum image size: %d cm x %d cm\n", screen_size->width_cm, screen_size->height_cm); } else if (screen_size->landscape_aspect_ratio > 0) { printf(" Aspect ratio: %.2f (landscape)\n", screen_size->landscape_aspect_ratio); } else if (screen_size->portait_aspect_ratio > 0) { printf(" Aspect ratio: %.2f (portrait)\n", screen_size->portait_aspect_ratio); } else { printf(" Image size is variable\n"); } gamma = di_edid_get_basic_gamma(edid); if (gamma != 0) { printf(" Gamma: %.2f\n", gamma); } else { printf(" Gamma is defined in an extension block\n"); } dpms = di_edid_get_dpms(edid); if (dpms->standby || dpms->suspend || dpms->off) { printf(" DPMS levels:"); if (dpms->standby) { printf(" Standby"); } if (dpms->suspend) { printf(" Suspend"); } if (dpms->off) { printf(" Off"); } printf("\n"); } if (!video_input_digital || di_edid_get_revision(edid) < 4) { display_color_type = di_edid_get_display_color_type(edid); printf(" %s\n", display_color_type_name(display_color_type)); } color_encoding_formats = di_edid_get_color_encoding_formats(edid); if (color_encoding_formats) { assert(color_encoding_formats->rgb444); printf(" Supported color formats: RGB 4:4:4"); if (color_encoding_formats->ycrcb444) { printf(", YCrCb 4:4:4"); } if (color_encoding_formats->ycrcb422) { printf(", YCrCb 4:2:2"); } printf("\n"); } misc_features = di_edid_get_misc_features(edid); if (misc_features->srgb_is_primary) { printf(" Default (sRGB) color space is primary color space\n"); } if (di_edid_get_revision(edid) >= 4) { assert(misc_features->has_preferred_timing); if (misc_features->preferred_timing_is_native) { printf(" First detailed timing includes the native " "pixel format and preferred refresh rate\n"); } else { printf(" First detailed timing does not include the " "native pixel format and preferred refresh rate\n"); } } else { if (misc_features->has_preferred_timing) { printf(" First detailed timing is the preferred timing\n"); } } if (misc_features->continuous_freq) { printf(" Display is continuous frequency\n"); } if (misc_features->default_gtf) { printf(" Supports GTF timings within operating range\n"); } /* edid-decode truncates the result, but %f rounds it */ chromaticity_coords = di_edid_get_chromaticity_coords(edid); printf(" Color Characteristics:\n"); printf(" Red : %.4f, %.4f\n", truncate_chromaticity_coord(chromaticity_coords->red_x), truncate_chromaticity_coord(chromaticity_coords->red_y)); printf(" Green: %.4f, %.4f\n", truncate_chromaticity_coord(chromaticity_coords->green_x), truncate_chromaticity_coord(chromaticity_coords->green_y)); printf(" Blue : %.4f, %.4f\n", truncate_chromaticity_coord(chromaticity_coords->blue_x), truncate_chromaticity_coord(chromaticity_coords->blue_y)); printf(" White: %.4f, %.4f\n", truncate_chromaticity_coord(chromaticity_coords->white_x), truncate_chromaticity_coord(chromaticity_coords->white_y)); printf(" Established Timings I & II:"); established_timings_i_ii = di_edid_get_established_timings_i_ii(edid); if (!has_established_timings_i_ii(established_timings_i_ii)) { printf(" none"); } printf("\n"); if (established_timings_i_ii->has_720x400_70hz) printf(" IBM : 720x400 70.081663 Hz 9:5 31.467 kHz 28.320000 MHz\n"); if (established_timings_i_ii->has_720x400_88hz) printf(" IBM : 720x400 87.849542 Hz 9:5 39.444 kHz 35.500000 MHz\n"); if (established_timings_i_ii->has_640x480_60hz) printf(" DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz\n"); if (established_timings_i_ii->has_640x480_67hz) printf(" Apple : 640x480 66.666667 Hz 4:3 35.000 kHz 30.240000 MHz\n"); if (established_timings_i_ii->has_640x480_72hz) printf(" DMT 0x05: 640x480 72.808802 Hz 4:3 37.861 kHz 31.500000 MHz\n"); if (established_timings_i_ii->has_640x480_75hz) printf(" DMT 0x06: 640x480 75.000000 Hz 4:3 37.500 kHz 31.500000 MHz\n"); if (established_timings_i_ii->has_800x600_56hz) printf(" DMT 0x08: 800x600 56.250000 Hz 4:3 35.156 kHz 36.000000 MHz\n"); if (established_timings_i_ii->has_800x600_60hz) printf(" DMT 0x09: 800x600 60.316541 Hz 4:3 37.879 kHz 40.000000 MHz\n"); if (established_timings_i_ii->has_800x600_72hz) printf(" DMT 0x0a: 800x600 72.187572 Hz 4:3 48.077 kHz 50.000000 MHz\n"); if (established_timings_i_ii->has_800x600_75hz) printf(" DMT 0x0b: 800x600 75.000000 Hz 4:3 46.875 kHz 49.500000 MHz\n"); if (established_timings_i_ii->has_832x624_75hz) printf(" Apple : 832x624 74.551266 Hz 4:3 49.726 kHz 57.284000 MHz\n"); if (established_timings_i_ii->has_1024x768_87hz_interlaced) printf(" DMT 0x0f: 1024x768i 86.957532 Hz 4:3 35.522 kHz 44.900000 MHz\n"); if (established_timings_i_ii->has_1024x768_60hz) printf(" DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz\n"); if (established_timings_i_ii->has_1024x768_70hz) printf(" DMT 0x11: 1024x768 70.069359 Hz 4:3 56.476 kHz 75.000000 MHz\n"); if (established_timings_i_ii->has_1024x768_75hz) printf(" DMT 0x12: 1024x768 75.028582 Hz 4:3 60.023 kHz 78.750000 MHz\n"); if (established_timings_i_ii->has_1280x1024_75hz) printf(" DMT 0x24: 1280x1024 75.024675 Hz 5:4 79.976 kHz 135.000000 MHz\n"); if (established_timings_i_ii->has_1152x870_75hz) printf(" Apple : 1152x870 75.061550 Hz 192:145 68.681 kHz 100.000000 MHz\n"); printf(" Standard Timings:"); standard_timings = di_edid_get_standard_timings(edid); if (standard_timings[0] == NULL) { printf(" none"); } printf("\n"); for (i = 0; standard_timings[i] != NULL; i++) { print_standard_timing(standard_timings[i]); } printf(" Detailed Timing Descriptors:\n"); detailed_timing_defs = di_edid_get_detailed_timing_defs(edid); for (i = 0; detailed_timing_defs[i] != NULL; i++) { print_detailed_timing_def(detailed_timing_defs[i]); } display_descs = di_edid_get_display_descriptors(edid); for (i = 0; display_descs[i] != NULL; i++) { print_display_desc(edid, display_descs[i]); } } libdisplay-info-0.1.1/di-edid-decode/main.c000066400000000000000000000101041437312070200204010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "di-edid-decode.h" struct uncommon_features uncommon_features = {0}; static const struct option long_options[] = { { "help", no_argument, 0, 'h' }, { 0, 0, 0, 0 } }; static void usage(void) { fprintf(stderr, "Usage: di-edid-decode [in]\n" " [in]: EDID file to parse. Read from standard input (stdin),\n" " if none given.\n" "Example : di-edid-decode /sys/class/drm/card0-DP-2/edid \n" "\nOptions:\n" "-h, --help display the help message\n"); } static const char * ext_tag_name(enum di_edid_ext_tag tag) { switch (tag) { case DI_EDID_EXT_CEA: return "CTA-861 Extension Block"; case DI_EDID_EXT_VTB: return "Video Timing Extension Block"; case DI_EDID_EXT_DI: return "Display Information Extension Block"; case DI_EDID_EXT_LS: return "Localized String Extension Block"; case DI_EDID_EXT_DPVL: return "Digital Packet Video Link Extension"; case DI_EDID_EXT_BLOCK_MAP: return "Block Map Extension Block"; case DI_EDID_EXT_VENDOR: return "Manufacturer-Specific Extension Block"; case DI_EDID_EXT_DISPLAYID: return "DisplayID Extension Block"; } abort(); } static void print_ext(const struct di_edid_ext *ext, size_t ext_index) { const char *tag_name; tag_name = ext_tag_name(di_edid_ext_get_tag(ext)); printf("\n----------------\n\n"); printf("Block %zu, %s:\n", ext_index + 1, tag_name); switch (di_edid_ext_get_tag(ext)) { case DI_EDID_EXT_CEA: print_cta(di_edid_ext_get_cta(ext)); break; case DI_EDID_EXT_DISPLAYID: print_displayid(di_edid_ext_get_displayid(ext)); break; default: break; /* Ignore */ } } static size_t edid_checksum_index(size_t block_index) { return 128 * (block_index + 1) - 1; } int main(int argc, char *argv[]) { FILE *in; static uint8_t raw[32 * 1024]; size_t size = 0; const struct di_edid *edid; struct di_info *info; const struct di_edid_ext *const *exts; const char *failure_msg; size_t i; int opt; in = stdin; while (1) { int option_index = 0; opt = getopt_long(argc, argv, "h", long_options, &option_index); if (opt == -1) break; switch (opt) { case 'h': usage(); return -1; default: usage(); return -1; } } if (argc > 1) { in = fopen(argv[1], "r"); if (!in) { perror("failed to open input file"); return 1; } } while (!feof(in)) { size += fread(&raw[size], 1, sizeof(raw) - size, in); if (ferror(in)) { perror("fread failed"); return 1; } else if (size >= sizeof(raw)) { fprintf(stderr, "input too large\n"); return 1; } } fclose(in); info = di_info_parse_edid(raw, size); if (!info) { perror("di_edid_parse failed"); return 1; } edid = di_info_get_edid(info); print_edid(edid); exts = di_edid_get_extensions(edid); for (i = 0; exts[i] != NULL; i++); if (i > 0) { printf(" Extension blocks: %zu\n", i); } printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(0)]); for (i = 0; exts[i] != NULL; i++) { print_ext(exts[i], i); printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(i + 1)]); } printf("\n----------------\n\n"); failure_msg = di_info_get_failure_msg(info); if (failure_msg) { printf("Failures:\n\n%s", failure_msg); printf("EDID conformity: FAIL\n"); } else { printf("EDID conformity: PASS\n"); } if (uncommon_features.color_point_descriptor) { fprintf(stderr, "The EDID blob contains an uncommon Color " "Point Descriptor. Please share the EDID blob " "with upstream!\n"); } if (uncommon_features.color_management_data) { fprintf(stderr, "The EDID blob contains an uncommon Color " "Management Data Descriptor. Please share the " "EDID blob with upstream!\n"); } if (uncommon_features.cta_transfer_characteristics) { fprintf(stderr, "The EDID blob contains an uncommon CTA VESA " "Display Transfer Characteristic data block. " "Please share the EDID blob with upstream!\n"); } di_info_destroy(info); return failure_msg ? 254 : 0; } libdisplay-info-0.1.1/di-edid-decode/meson.build000066400000000000000000000002341437312070200214560ustar00rootroot00000000000000di_edid_decode = executable( 'di-edid-decode', [ 'cta.c', 'displayid.c', 'edid.c', 'main.c', ], dependencies: [di_dep, math], install: true, ) libdisplay-info-0.1.1/displayid.c000066400000000000000000000410731437312070200167300ustar00rootroot00000000000000#include #include #include #include #include #include "bits.h" #include "displayid.h" /** * The size of the mandatory fields in a DisplayID section. */ #define DISPLAYID_MIN_SIZE 5 /** * The maximum size of a DisplayID section. */ #define DISPLAYID_MAX_SIZE 256 /** * The size of a DisplayID data block header (tag, revision and size). */ #define DISPLAYID_DATA_BLOCK_HEADER_SIZE 3 /** * The size of a DisplayID type I timing. */ #define DISPLAYID_TYPE_I_TIMING_SIZE 20 static bool is_all_zeroes(const uint8_t *data, size_t size) { size_t i; for (i = 0; i < size; i++) { if (data[i] != 0) return false; } return true; } static void add_failure(struct di_displayid *displayid, const char fmt[], ...) { va_list args; va_start(args, fmt); _di_logger_va_add_failure(displayid->logger, fmt, args); va_end(args); } static void check_data_block_revision(struct di_displayid *displayid, const uint8_t data[static DISPLAYID_DATA_BLOCK_HEADER_SIZE], const char *block_name, uint8_t max_revision) { uint8_t revision, flags; flags = get_bit_range(data[0x01], 7, 3); revision = get_bit_range(data[0x01], 2, 0); if (revision > max_revision) { add_failure(displayid, "%s: Unexpected revision (%u != %u).", block_name, revision, max_revision); } if (flags != 0) { add_failure(displayid, "%s: Unexpected flags (0x%02x).", block_name, flags); } } static bool parse_display_params_block(struct di_displayid *displayid, struct di_displayid_display_params_priv *priv, const uint8_t *data, size_t size) { struct di_displayid_display_params *params = &priv->base; uint8_t raw_features; check_data_block_revision(displayid, data, "Display Parameters Data Block", 0); if (size != 0x0F) { add_failure(displayid, "Display Parameters Data Block: DisplayID payload length is different than expected (%zu != %zu)", size, 0x0F); return false; } params->horiz_image_mm = 0.1f * (float)(data[0x03] | (data[0x04] << 8)); params->vert_image_mm = 0.1f * (float)(data[0x05] | (data[0x06] << 8)); params->horiz_pixels = data[0x07] | (data[0x08] << 8); params->vert_pixels = data[0x09] | (data[0x0A] << 8); raw_features = data[0x0B]; params->features = &priv->features; priv->features.audio = has_bit(raw_features, 7); priv->features.separate_audio_inputs = has_bit(raw_features, 6); priv->features.audio_input_override = has_bit(raw_features, 5); priv->features.power_management = has_bit(raw_features, 4); priv->features.fixed_timing = has_bit(raw_features, 3); priv->features.fixed_pixel_format = has_bit(raw_features, 2); priv->features.ai = has_bit(raw_features, 1); priv->features.deinterlacing = has_bit(raw_features, 0); if (data[0x0C] != 0xFF) params->gamma = (float)data[0x0C] / 100 + 1; params->aspect_ratio = (float)data[0x0D] / 100 + 1; params->bits_per_color_overall = get_bit_range(data[0x0E], 7, 4) + 1; params->bits_per_color_native = get_bit_range(data[0x0E], 3, 0) + 1; return true; } static bool parse_type_i_timing(struct di_displayid *displayid, struct di_displayid_data_block *data_block, const uint8_t data[static DISPLAYID_TYPE_I_TIMING_SIZE]) { int raw_pixel_clock; uint8_t stereo_3d, aspect_ratio; struct di_displayid_type_i_timing *t = calloc(1, sizeof(*t)); if (t == NULL) { return false; } raw_pixel_clock = data[0] | (data[1] << 8) | (data[2] << 16); t->pixel_clock_mhz = (double)(1 + raw_pixel_clock) * 0.01; t->preferred = has_bit(data[3], 7); t->interlaced = has_bit(data[3], 4); stereo_3d = get_bit_range(data[3], 6, 5); switch (stereo_3d) { case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_NEVER: case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_ALWAYS: case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_USER: t->stereo_3d = stereo_3d; break; default: add_failure(displayid, "Video Timing Modes Type 1 - Detailed Timings Data Block: Reserved stereo 0x%02x.", stereo_3d); break; } aspect_ratio = get_bit_range(data[3], 3, 0); switch (aspect_ratio) { case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_1_1: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_5_4: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_4_3: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_15_9: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_9: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_10: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_64_27: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_256_135: case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED: t->aspect_ratio = aspect_ratio; break; default: t->aspect_ratio = DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED; add_failure(displayid, "Video Timing Modes Type 1 - Detailed Timings Data Block: Unknown aspect 0x%02x.", aspect_ratio); break; } t->horiz_active = 1 + (data[4] | (data[5] << 8)); t->horiz_blank = 1 + (data[6] | (data[7] << 8)); t->horiz_offset = 1 + (data[8] | (get_bit_range(data[9], 6, 0) << 8)); t->horiz_sync_polarity = has_bit(data[9], 7); t->horiz_sync_width = 1 + (data[10] | (data[11] << 8)); t->vert_active = 1 + (data[12] | (data[13] << 8)); t->vert_blank = 1 + (data[14] | (data[15] << 8)); t->vert_offset = 1 + (data[16] | (get_bit_range(data[17], 6, 0) << 8)); t->vert_sync_polarity = has_bit(data[17], 7); t->vert_sync_width = 1 + (data[18] | (data[19] << 8)); assert(data_block->type_i_timings_len < DISPLAYID_MAX_TYPE_I_TIMINGS); data_block->type_i_timings[data_block->type_i_timings_len++] = t; return true; } static bool parse_type_i_timing_block(struct di_displayid *displayid, struct di_displayid_data_block *data_block, const uint8_t *data, size_t size) { size_t i; check_data_block_revision(displayid, data, "Video Timing Modes Type 1 - Detailed Timings Data Block", 1); if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_I_TIMING_SIZE != 0) { add_failure(displayid, "Video Timing Modes Type 1 - Detailed Timings Data Block: payload size not divisible by element size."); } for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE; i + DISPLAYID_TYPE_I_TIMING_SIZE <= size; i += DISPLAYID_TYPE_I_TIMING_SIZE) { if (!parse_type_i_timing(displayid, data_block, &data[i])) { return false; } } return true; } static bool parse_tiled_topo_block(struct di_displayid *displayid, struct di_displayid_tiled_topo_priv *priv, const uint8_t *data, size_t size) { struct di_displayid_tiled_topo *tiled_topo = &priv->base; uint8_t raw_caps, raw_missing_recv_behavior, raw_single_recv_behavior; bool has_bezel; float px_mult; check_data_block_revision(displayid, data, "Tiled Display Topology Data Block", 0); if (size - DISPLAYID_DATA_BLOCK_HEADER_SIZE != 22) { add_failure(displayid, "Tiled Display Topology Data Block: DisplayID payload length is different than expected (%zu != %zu)", size - DISPLAYID_DATA_BLOCK_HEADER_SIZE, 22); return false; } raw_caps = data[0x03]; tiled_topo->caps = &priv->caps; priv->caps.single_enclosure = has_bit(raw_caps, 7); has_bezel = has_bit(raw_caps, 6); raw_missing_recv_behavior = get_bit_range(raw_caps, 4, 3); raw_single_recv_behavior = get_bit_range(raw_caps, 2, 0); if (has_bit(raw_caps, 5)) { add_failure(displayid, "Tiled Display Topology Data Block: Capability bit 5 is reserved."); } switch (raw_missing_recv_behavior) { case DI_DISPLAYID_TILED_TOPO_MISSING_RECV_UNDEF: case DI_DISPLAYID_TILED_TOPO_MISSING_RECV_TILE_ONLY: priv->caps.missing_recv_behavior = raw_missing_recv_behavior; break; default: add_failure(displayid, "Tiled Display Topology Data Block: Behavior if more than one tile and fewer than total number of tiles set to reserved value 0x%02x.", raw_missing_recv_behavior); break; } switch (raw_single_recv_behavior) { case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_UNDEF: case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_TILE_ONLY: case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_SCALED: case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_CLONED: priv->caps.single_recv_behavior = raw_single_recv_behavior; break; default: add_failure(displayid, "Tiled Display Topology Data Block: Behavior if it is the only tile set to reserved value 0x%02x.", raw_single_recv_behavior); break; } tiled_topo->total_horiz_tiles = 1 + (get_bit_range(data[0x04], 7, 4) | (get_bit_range(data[0x06], 7, 6) << 4)); tiled_topo->total_vert_tiles = 1 + (get_bit_range(data[0x04], 3, 0) | (get_bit_range(data[0x06], 5, 4) << 4)); tiled_topo->horiz_tile_location = 1 + (get_bit_range(data[0x05], 7, 4) | (get_bit_range(data[0x06], 3, 2) << 4)); tiled_topo->vert_tile_location = 1 + (get_bit_range(data[0x05], 3, 0) | (get_bit_range(data[0x06], 1, 0) << 4)); tiled_topo->horiz_tile_pixels = 1 + (data[0x07] | (data[0x08] << 8)); tiled_topo->vert_tile_lines = 1 + (data[0x09] | (data[0x0A] << 8)); px_mult = data[0x0B]; if (has_bezel && px_mult == 0) { add_failure(displayid, "Tiled Display Topology Data Block: Bezel information bit is set, but the pixel multiplier is zero."); has_bezel = false; } if (has_bezel) { tiled_topo->bezel = &priv->bezel; priv->bezel.top_px = px_mult * data[0x0C] * 0.1f; priv->bezel.bottom_px = px_mult * data[0x0D] * 0.1f; priv->bezel.right_px = px_mult * data[0x0E] * 0.1f; priv->bezel.left_px = px_mult * data[0x0F] * 0.1f; } else { if (px_mult != 0) add_failure(displayid, "Tiled Display Topology Data Block: No bezel information, but the pixel multiplier is non-zero."); if (!is_all_zeroes(&data[0x0C], 4)) add_failure(displayid, "Tiled Display Topology Data Block: No bezel information, but the bezel size is non-zero."); } memcpy(tiled_topo->vendor_id, &data[0x10], 3); tiled_topo->product_code = data[0x13] | (uint16_t)(data[0x14] << 8); tiled_topo->serial_number = data[0x15] | (uint32_t)(data[0x16] << 8) | (uint32_t)(data[0x17] << 16) | (uint32_t)(data[0x18] << 24); return true; } static ssize_t parse_data_block(struct di_displayid *displayid, const uint8_t *data, size_t size) { uint8_t tag; size_t data_block_size; struct di_displayid_data_block *data_block = NULL; assert(size >= DISPLAYID_DATA_BLOCK_HEADER_SIZE); tag = data[0x00]; data_block_size = (size_t) data[0x02] + DISPLAYID_DATA_BLOCK_HEADER_SIZE; if (data_block_size > size) { add_failure(displayid, "The length of this DisplayID data block (%d) exceeds the number of bytes remaining (%zu)", data_block_size, size); goto skip; } data_block = calloc(1, sizeof(*data_block)); if (!data_block) goto error; switch (tag) { case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS: if (!parse_display_params_block(displayid, &data_block->display_params, data, data_block_size)) goto error; break; case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING: if (!parse_type_i_timing_block(displayid, data_block, data, data_block_size)) goto error; break; case DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO: if (!parse_tiled_topo_block(displayid, &data_block->tiled_topo, data, data_block_size)) goto skip; break; case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID: case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT: case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING: case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING: case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING: case DI_DISPLAYID_DATA_BLOCK_VESA_TIMING: case DI_DISPLAYID_DATA_BLOCK_CEA_TIMING: case DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS: case DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL: case DI_DISPLAYID_DATA_BLOCK_ASCII_STRING: case DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA: case DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ: case DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT: case DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE: case DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE: case DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING: case DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING: break; /* Supported */ case 0x7F: goto skip; /* Vendor-specific */ default: add_failure(displayid, "Unknown DisplayID Data Block (0x%" PRIx8 ", length %" PRIu8 ")", tag, data_block_size - DISPLAYID_DATA_BLOCK_HEADER_SIZE); goto skip; } data_block->tag = tag; assert(displayid->data_blocks_len < DISPLAYID_MAX_DATA_BLOCKS); displayid->data_blocks[displayid->data_blocks_len++] = data_block; return (ssize_t) data_block_size; skip: free(data_block); return (ssize_t) data_block_size; error: free(data_block); return -1; } static bool is_data_block_end(const uint8_t *data, size_t size) { if (size < DISPLAYID_DATA_BLOCK_HEADER_SIZE) return true; return is_all_zeroes(data, DISPLAYID_DATA_BLOCK_HEADER_SIZE); } static bool validate_checksum(const uint8_t *data, size_t size) { uint8_t sum = 0; size_t i; for (i = 0; i < size; i++) { sum += data[i]; } return sum == 0; } bool _di_displayid_parse(struct di_displayid *displayid, const uint8_t *data, size_t size, struct di_logger *logger) { size_t section_size, i, max_data_block_size; ssize_t data_block_size; uint8_t product_type; if (size < DISPLAYID_MIN_SIZE) { errno = EINVAL; return false; } displayid->logger = logger; displayid->version = get_bit_range(data[0x00], 7, 4); displayid->revision = get_bit_range(data[0x00], 3, 0); if (displayid->version == 0 || displayid->version > 1) { errno = ENOTSUP; return false; } section_size = (size_t) data[0x01] + DISPLAYID_MIN_SIZE; if (section_size > DISPLAYID_MAX_SIZE || section_size > size) { errno = EINVAL; return false; } if (!validate_checksum(data, section_size)) { errno = EINVAL; return false; } product_type = data[0x02]; switch (product_type) { case DI_DISPLAYID_PRODUCT_TYPE_EXTENSION: case DI_DISPLAYID_PRODUCT_TYPE_TEST: case DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL: case DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY: case DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER: case DI_DISPLAYID_PRODUCT_TYPE_REPEATER: case DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE: displayid->product_type = product_type; break; default: errno = EINVAL; return false; } i = DISPLAYID_MIN_SIZE - 1; max_data_block_size = 0; while (i < section_size - 1) { max_data_block_size = section_size - 1 - i; if (is_data_block_end(&data[i], max_data_block_size)) break; data_block_size = parse_data_block(displayid, &data[i], max_data_block_size); if (data_block_size < 0) return false; assert(data_block_size > 0); i += (size_t) data_block_size; } if (!is_all_zeroes(&data[i], max_data_block_size)) { if (max_data_block_size < DISPLAYID_DATA_BLOCK_HEADER_SIZE) add_failure(displayid, "Not enough bytes remain (%zu) for a DisplayID data block and the DisplayID filler is non-0.", max_data_block_size); else add_failure(displayid, "Padding: Contains non-zero bytes."); } displayid->logger = NULL; return true; } static void destroy_data_block(struct di_displayid_data_block *data_block) { size_t i; switch (data_block->tag) { case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING: for (i = 0; i < data_block->type_i_timings_len; i++) free(data_block->type_i_timings[i]); break; default: break; /* Nothing to do */ } free(data_block); } void _di_displayid_finish(struct di_displayid *displayid) { size_t i; for (i = 0; i < displayid->data_blocks_len; i++) destroy_data_block(displayid->data_blocks[i]); } int di_displayid_get_version(const struct di_displayid *displayid) { return displayid->version; } int di_displayid_get_revision(const struct di_displayid *displayid) { return displayid->revision; } enum di_displayid_product_type di_displayid_get_product_type(const struct di_displayid *displayid) { return displayid->product_type; } enum di_displayid_data_block_tag di_displayid_data_block_get_tag(const struct di_displayid_data_block *data_block) { return data_block->tag; } const struct di_displayid_display_params * di_displayid_data_block_get_display_params(const struct di_displayid_data_block *data_block) { if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS) { return NULL; } return &data_block->display_params.base; } const struct di_displayid_type_i_timing *const * di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *data_block) { if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING) { return NULL; } return (const struct di_displayid_type_i_timing *const *) data_block->type_i_timings; } const struct di_displayid_tiled_topo * di_displayid_data_block_get_tiled_topo(const struct di_displayid_data_block *data_block) { if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO) { return NULL; } return &data_block->tiled_topo.base; } const struct di_displayid_data_block *const * di_displayid_get_data_blocks(const struct di_displayid *displayid) { return (const struct di_displayid_data_block *const *) displayid->data_blocks; } libdisplay-info-0.1.1/dmt-table.c000066400000000000000000001004011437312070200166060ustar00rootroot00000000000000/* DO NOT EDIT! This file has been generated by gen-dmt.py from DMT-r1-v13.pdf. */ #include "dmt.h" const struct di_dmt_timing _di_dmt_timings[] = { { .dmt_id = 0x01, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 640, .vert_video = 350, .refresh_rate_hz = 85.0, .pixel_clock_hz = 31500000, .horiz_blank = 192, .horiz_front_porch = 32, .horiz_sync_pulse = 64, .horiz_border = 0, .vert_blank = 95, .vert_front_porch = 32, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x02, .edid_std_id = 0x3119, .cvt_id = 0, .horiz_video = 640, .vert_video = 400, .refresh_rate_hz = 85.0, .pixel_clock_hz = 31500000, .horiz_blank = 192, .horiz_front_porch = 32, .horiz_sync_pulse = 64, .horiz_border = 0, .vert_blank = 45, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x03, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 720, .vert_video = 400, .refresh_rate_hz = 85.0, .pixel_clock_hz = 35500000, .horiz_blank = 216, .horiz_front_porch = 36, .horiz_sync_pulse = 72, .horiz_border = 0, .vert_blank = 46, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x04, .edid_std_id = 0x3140, .cvt_id = 0, .horiz_video = 640, .vert_video = 480, .refresh_rate_hz = 60.0, .pixel_clock_hz = 25175000, .horiz_blank = 144, .horiz_front_porch = 8, .horiz_sync_pulse = 96, .horiz_border = 8, .vert_blank = 29, .vert_front_porch = 2, .vert_sync_pulse = 2, .vert_border = 8, .reduced_blanking = false, }, { .dmt_id = 0x05, .edid_std_id = 0x314C, .cvt_id = 0, .horiz_video = 640, .vert_video = 480, .refresh_rate_hz = 72.0, .pixel_clock_hz = 31500000, .horiz_blank = 176, .horiz_front_porch = 16, .horiz_sync_pulse = 40, .horiz_border = 8, .vert_blank = 24, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 8, .reduced_blanking = false, }, { .dmt_id = 0x06, .edid_std_id = 0x314F, .cvt_id = 0, .horiz_video = 640, .vert_video = 480, .refresh_rate_hz = 75.0, .pixel_clock_hz = 31500000, .horiz_blank = 200, .horiz_front_porch = 16, .horiz_sync_pulse = 64, .horiz_border = 0, .vert_blank = 20, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x07, .edid_std_id = 0x3159, .cvt_id = 0, .horiz_video = 640, .vert_video = 480, .refresh_rate_hz = 85.0, .pixel_clock_hz = 36000000, .horiz_blank = 192, .horiz_front_porch = 56, .horiz_sync_pulse = 56, .horiz_border = 0, .vert_blank = 29, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x08, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 800, .vert_video = 600, .refresh_rate_hz = 56.0, .pixel_clock_hz = 36000000, .horiz_blank = 224, .horiz_front_porch = 24, .horiz_sync_pulse = 72, .horiz_border = 0, .vert_blank = 25, .vert_front_porch = 1, .vert_sync_pulse = 2, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x09, .edid_std_id = 0x4540, .cvt_id = 0, .horiz_video = 800, .vert_video = 600, .refresh_rate_hz = 60.0, .pixel_clock_hz = 40000000, .horiz_blank = 256, .horiz_front_porch = 40, .horiz_sync_pulse = 128, .horiz_border = 0, .vert_blank = 28, .vert_front_porch = 1, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x0A, .edid_std_id = 0x454C, .cvt_id = 0, .horiz_video = 800, .vert_video = 600, .refresh_rate_hz = 72.0, .pixel_clock_hz = 50000000, .horiz_blank = 240, .horiz_front_porch = 56, .horiz_sync_pulse = 120, .horiz_border = 0, .vert_blank = 66, .vert_front_porch = 37, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x0B, .edid_std_id = 0x454F, .cvt_id = 0, .horiz_video = 800, .vert_video = 600, .refresh_rate_hz = 75.0, .pixel_clock_hz = 49500000, .horiz_blank = 256, .horiz_front_porch = 16, .horiz_sync_pulse = 80, .horiz_border = 0, .vert_blank = 25, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x0C, .edid_std_id = 0x4559, .cvt_id = 0, .horiz_video = 800, .vert_video = 600, .refresh_rate_hz = 85.0, .pixel_clock_hz = 56250000, .horiz_blank = 248, .horiz_front_porch = 32, .horiz_sync_pulse = 64, .horiz_border = 0, .vert_blank = 31, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x0D, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 800, .vert_video = 600, .refresh_rate_hz = 120.0, .pixel_clock_hz = 73250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 36, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x0E, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 848, .vert_video = 480, .refresh_rate_hz = 60.0, .pixel_clock_hz = 33750000, .horiz_blank = 240, .horiz_front_porch = 16, .horiz_sync_pulse = 112, .horiz_border = 0, .vert_blank = 37, .vert_front_porch = 6, .vert_sync_pulse = 8, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x0F, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1024, .vert_video = 768, .refresh_rate_hz = 43.0, .pixel_clock_hz = 44900000, .horiz_blank = 240, .horiz_front_porch = 8, .horiz_sync_pulse = 176, .horiz_border = 0, .vert_blank = 24, .vert_front_porch = 0, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x10, .edid_std_id = 0x6140, .cvt_id = 0, .horiz_video = 1024, .vert_video = 768, .refresh_rate_hz = 60.0, .pixel_clock_hz = 65000000, .horiz_blank = 320, .horiz_front_porch = 24, .horiz_sync_pulse = 136, .horiz_border = 0, .vert_blank = 38, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x11, .edid_std_id = 0x614A, .cvt_id = 0, .horiz_video = 1024, .vert_video = 768, .refresh_rate_hz = 70.0, .pixel_clock_hz = 75000000, .horiz_blank = 304, .horiz_front_porch = 24, .horiz_sync_pulse = 136, .horiz_border = 0, .vert_blank = 38, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x12, .edid_std_id = 0x614F, .cvt_id = 0, .horiz_video = 1024, .vert_video = 768, .refresh_rate_hz = 75.0, .pixel_clock_hz = 78750000, .horiz_blank = 288, .horiz_front_porch = 16, .horiz_sync_pulse = 96, .horiz_border = 0, .vert_blank = 32, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x13, .edid_std_id = 0x6159, .cvt_id = 0, .horiz_video = 1024, .vert_video = 768, .refresh_rate_hz = 85.0, .pixel_clock_hz = 94500000, .horiz_blank = 352, .horiz_front_porch = 48, .horiz_sync_pulse = 96, .horiz_border = 0, .vert_blank = 40, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x14, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1024, .vert_video = 768, .refresh_rate_hz = 120.0, .pixel_clock_hz = 115500000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 45, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x15, .edid_std_id = 0x714F, .cvt_id = 0, .horiz_video = 1152, .vert_video = 864, .refresh_rate_hz = 75.0, .pixel_clock_hz = 108000000, .horiz_blank = 448, .horiz_front_porch = 64, .horiz_sync_pulse = 128, .horiz_border = 0, .vert_blank = 36, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x55, .edid_std_id = 0x81C0, .cvt_id = 0, .horiz_video = 1280, .vert_video = 720, .refresh_rate_hz = 60.0, .pixel_clock_hz = 74250000, .horiz_blank = 370, .horiz_front_porch = 110, .horiz_sync_pulse = 40, .horiz_border = 0, .vert_blank = 30, .vert_front_porch = 5, .vert_sync_pulse = 5, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x16, .edid_std_id = 0, .cvt_id = 0x7F1C21, .horiz_video = 1280, .vert_video = 768, .refresh_rate_hz = 60.0, .pixel_clock_hz = 68250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 22, .vert_front_porch = 3, .vert_sync_pulse = 7, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x17, .edid_std_id = 0, .cvt_id = 0x7F1C28, .horiz_video = 1280, .vert_video = 768, .refresh_rate_hz = 60.0, .pixel_clock_hz = 79500000, .horiz_blank = 384, .horiz_front_porch = 64, .horiz_sync_pulse = 128, .horiz_border = 0, .vert_blank = 30, .vert_front_porch = 3, .vert_sync_pulse = 7, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x18, .edid_std_id = 0, .cvt_id = 0x7F1C44, .horiz_video = 1280, .vert_video = 768, .refresh_rate_hz = 75.0, .pixel_clock_hz = 102250000, .horiz_blank = 416, .horiz_front_porch = 80, .horiz_sync_pulse = 128, .horiz_border = 0, .vert_blank = 37, .vert_front_porch = 3, .vert_sync_pulse = 7, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x19, .edid_std_id = 0, .cvt_id = 0x7F1C62, .horiz_video = 1280, .vert_video = 768, .refresh_rate_hz = 85.0, .pixel_clock_hz = 117500000, .horiz_blank = 432, .horiz_front_porch = 80, .horiz_sync_pulse = 136, .horiz_border = 0, .vert_blank = 41, .vert_front_porch = 3, .vert_sync_pulse = 7, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x1A, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1280, .vert_video = 768, .refresh_rate_hz = 120.0, .pixel_clock_hz = 140250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 45, .vert_front_porch = 3, .vert_sync_pulse = 7, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x1B, .edid_std_id = 0, .cvt_id = 0x8F1821, .horiz_video = 1280, .vert_video = 800, .refresh_rate_hz = 60.0, .pixel_clock_hz = 71000000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 23, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x1C, .edid_std_id = 0x8100, .cvt_id = 0x8F1828, .horiz_video = 1280, .vert_video = 800, .refresh_rate_hz = 60.0, .pixel_clock_hz = 83500000, .horiz_blank = 400, .horiz_front_porch = 72, .horiz_sync_pulse = 128, .horiz_border = 0, .vert_blank = 31, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x1D, .edid_std_id = 0x810F, .cvt_id = 0x8F1844, .horiz_video = 1280, .vert_video = 800, .refresh_rate_hz = 75.0, .pixel_clock_hz = 106500000, .horiz_blank = 416, .horiz_front_porch = 80, .horiz_sync_pulse = 128, .horiz_border = 0, .vert_blank = 38, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x1E, .edid_std_id = 0x8119, .cvt_id = 0x8F1862, .horiz_video = 1280, .vert_video = 800, .refresh_rate_hz = 85.0, .pixel_clock_hz = 122500000, .horiz_blank = 432, .horiz_front_porch = 80, .horiz_sync_pulse = 136, .horiz_border = 0, .vert_blank = 43, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x1F, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1280, .vert_video = 800, .refresh_rate_hz = 120.0, .pixel_clock_hz = 146250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 47, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x20, .edid_std_id = 0x8140, .cvt_id = 0, .horiz_video = 1280, .vert_video = 960, .refresh_rate_hz = 60.0, .pixel_clock_hz = 108000000, .horiz_blank = 520, .horiz_front_porch = 96, .horiz_sync_pulse = 112, .horiz_border = 0, .vert_blank = 40, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x21, .edid_std_id = 0x8159, .cvt_id = 0, .horiz_video = 1280, .vert_video = 960, .refresh_rate_hz = 85.0, .pixel_clock_hz = 148500000, .horiz_blank = 448, .horiz_front_porch = 64, .horiz_sync_pulse = 160, .horiz_border = 0, .vert_blank = 51, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x22, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1280, .vert_video = 960, .refresh_rate_hz = 120.0, .pixel_clock_hz = 175500000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 57, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x23, .edid_std_id = 0x8180, .cvt_id = 0, .horiz_video = 1280, .vert_video = 1024, .refresh_rate_hz = 60.0, .pixel_clock_hz = 108000000, .horiz_blank = 408, .horiz_front_porch = 48, .horiz_sync_pulse = 112, .horiz_border = 0, .vert_blank = 42, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x24, .edid_std_id = 0x818F, .cvt_id = 0, .horiz_video = 1280, .vert_video = 1024, .refresh_rate_hz = 75.0, .pixel_clock_hz = 135000000, .horiz_blank = 408, .horiz_front_porch = 16, .horiz_sync_pulse = 144, .horiz_border = 0, .vert_blank = 42, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x25, .edid_std_id = 0x8199, .cvt_id = 0, .horiz_video = 1280, .vert_video = 1024, .refresh_rate_hz = 85.0, .pixel_clock_hz = 157500000, .horiz_blank = 448, .horiz_front_porch = 64, .horiz_sync_pulse = 160, .horiz_border = 0, .vert_blank = 48, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x26, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1280, .vert_video = 1024, .refresh_rate_hz = 120.0, .pixel_clock_hz = 187250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 60, .vert_front_porch = 3, .vert_sync_pulse = 7, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x27, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1360, .vert_video = 768, .refresh_rate_hz = 60.0, .pixel_clock_hz = 85500000, .horiz_blank = 432, .horiz_front_porch = 64, .horiz_sync_pulse = 112, .horiz_border = 0, .vert_blank = 27, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x28, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1360, .vert_video = 768, .refresh_rate_hz = 120.0, .pixel_clock_hz = 148250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 45, .vert_front_porch = 3, .vert_sync_pulse = 5, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x51, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1366, .vert_video = 768, .refresh_rate_hz = 60.0, .pixel_clock_hz = 85500000, .horiz_blank = 426, .horiz_front_porch = 70, .horiz_sync_pulse = 143, .horiz_border = 0, .vert_blank = 30, .vert_front_porch = 3, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x56, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1366, .vert_video = 768, .refresh_rate_hz = 60.0, .pixel_clock_hz = 72000000, .horiz_blank = 134, .horiz_front_porch = 14, .horiz_sync_pulse = 56, .horiz_border = 0, .vert_blank = 32, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x29, .edid_std_id = 0, .cvt_id = 0x0C2021, .horiz_video = 1400, .vert_video = 1050, .refresh_rate_hz = 60.0, .pixel_clock_hz = 101000000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 30, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x2A, .edid_std_id = 0x9040, .cvt_id = 0x0C2028, .horiz_video = 1400, .vert_video = 1050, .refresh_rate_hz = 60.0, .pixel_clock_hz = 121750000, .horiz_blank = 464, .horiz_front_porch = 88, .horiz_sync_pulse = 144, .horiz_border = 0, .vert_blank = 39, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x2B, .edid_std_id = 0x904F, .cvt_id = 0x0C2044, .horiz_video = 1400, .vert_video = 1050, .refresh_rate_hz = 75.0, .pixel_clock_hz = 156000000, .horiz_blank = 496, .horiz_front_porch = 104, .horiz_sync_pulse = 144, .horiz_border = 0, .vert_blank = 49, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x2C, .edid_std_id = 0x9059, .cvt_id = 0x0C2062, .horiz_video = 1400, .vert_video = 1050, .refresh_rate_hz = 85.0, .pixel_clock_hz = 179500000, .horiz_blank = 512, .horiz_front_porch = 104, .horiz_sync_pulse = 152, .horiz_border = 0, .vert_blank = 55, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x2D, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1400, .vert_video = 1050, .refresh_rate_hz = 120.0, .pixel_clock_hz = 208000000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 62, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x2E, .edid_std_id = 0, .cvt_id = 0xC11821, .horiz_video = 1440, .vert_video = 900, .refresh_rate_hz = 60.0, .pixel_clock_hz = 88750000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 26, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x2F, .edid_std_id = 0x9500, .cvt_id = 0xC11828, .horiz_video = 1440, .vert_video = 900, .refresh_rate_hz = 60.0, .pixel_clock_hz = 106500000, .horiz_blank = 464, .horiz_front_porch = 80, .horiz_sync_pulse = 152, .horiz_border = 0, .vert_blank = 34, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x30, .edid_std_id = 0x950F, .cvt_id = 0xC11844, .horiz_video = 1440, .vert_video = 900, .refresh_rate_hz = 75.0, .pixel_clock_hz = 136750000, .horiz_blank = 496, .horiz_front_porch = 96, .horiz_sync_pulse = 152, .horiz_border = 0, .vert_blank = 42, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x31, .edid_std_id = 0x9519, .cvt_id = 0xC11868, .horiz_video = 1440, .vert_video = 900, .refresh_rate_hz = 85.0, .pixel_clock_hz = 157000000, .horiz_blank = 512, .horiz_front_porch = 104, .horiz_sync_pulse = 152, .horiz_border = 0, .vert_blank = 48, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x32, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1440, .vert_video = 900, .refresh_rate_hz = 120.0, .pixel_clock_hz = 182750000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 53, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x53, .edid_std_id = 0xA9C0, .cvt_id = 0, .horiz_video = 1600, .vert_video = 900, .refresh_rate_hz = 60.0, .pixel_clock_hz = 108000000, .horiz_blank = 200, .horiz_front_porch = 24, .horiz_sync_pulse = 80, .horiz_border = 0, .vert_blank = 100, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x33, .edid_std_id = 0xA940, .cvt_id = 0, .horiz_video = 1600, .vert_video = 1200, .refresh_rate_hz = 60.0, .pixel_clock_hz = 162000000, .horiz_blank = 560, .horiz_front_porch = 64, .horiz_sync_pulse = 192, .horiz_border = 0, .vert_blank = 50, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x34, .edid_std_id = 0xA945, .cvt_id = 0, .horiz_video = 1600, .vert_video = 1200, .refresh_rate_hz = 65.0, .pixel_clock_hz = 175500000, .horiz_blank = 560, .horiz_front_porch = 64, .horiz_sync_pulse = 192, .horiz_border = 0, .vert_blank = 50, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x35, .edid_std_id = 0xA94A, .cvt_id = 0, .horiz_video = 1600, .vert_video = 1200, .refresh_rate_hz = 70.0, .pixel_clock_hz = 189000000, .horiz_blank = 560, .horiz_front_porch = 64, .horiz_sync_pulse = 192, .horiz_border = 0, .vert_blank = 50, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x36, .edid_std_id = 0xA94F, .cvt_id = 0, .horiz_video = 1600, .vert_video = 1200, .refresh_rate_hz = 75.0, .pixel_clock_hz = 202500000, .horiz_blank = 560, .horiz_front_porch = 64, .horiz_sync_pulse = 192, .horiz_border = 0, .vert_blank = 50, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x37, .edid_std_id = 0xA959, .cvt_id = 0, .horiz_video = 1600, .vert_video = 1200, .refresh_rate_hz = 85.0, .pixel_clock_hz = 229500000, .horiz_blank = 560, .horiz_front_porch = 64, .horiz_sync_pulse = 192, .horiz_border = 0, .vert_blank = 50, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x38, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1600, .vert_video = 1200, .refresh_rate_hz = 120.0, .pixel_clock_hz = 268250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 71, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x39, .edid_std_id = 0, .cvt_id = 0x0C2821, .horiz_video = 1680, .vert_video = 1050, .refresh_rate_hz = 60.0, .pixel_clock_hz = 119000000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 30, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x3A, .edid_std_id = 0xB300, .cvt_id = 0x0C2828, .horiz_video = 1680, .vert_video = 1050, .refresh_rate_hz = 60.0, .pixel_clock_hz = 146250000, .horiz_blank = 560, .horiz_front_porch = 104, .horiz_sync_pulse = 176, .horiz_border = 0, .vert_blank = 39, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x3B, .edid_std_id = 0xB30F, .cvt_id = 0x0C2844, .horiz_video = 1680, .vert_video = 1050, .refresh_rate_hz = 75.0, .pixel_clock_hz = 187000000, .horiz_blank = 592, .horiz_front_porch = 120, .horiz_sync_pulse = 176, .horiz_border = 0, .vert_blank = 49, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x3C, .edid_std_id = 0xB319, .cvt_id = 0x0C2868, .horiz_video = 1680, .vert_video = 1050, .refresh_rate_hz = 85.0, .pixel_clock_hz = 214750000, .horiz_blank = 608, .horiz_front_porch = 128, .horiz_sync_pulse = 176, .horiz_border = 0, .vert_blank = 55, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x3D, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1680, .vert_video = 1050, .refresh_rate_hz = 120.0, .pixel_clock_hz = 245500000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 62, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x3E, .edid_std_id = 0xC140, .cvt_id = 0, .horiz_video = 1792, .vert_video = 1344, .refresh_rate_hz = 60.0, .pixel_clock_hz = 204750000, .horiz_blank = 656, .horiz_front_porch = 128, .horiz_sync_pulse = 200, .horiz_border = 0, .vert_blank = 50, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x3F, .edid_std_id = 0xC14F, .cvt_id = 0, .horiz_video = 1792, .vert_video = 1344, .refresh_rate_hz = 75.0, .pixel_clock_hz = 261000000, .horiz_blank = 664, .horiz_front_porch = 96, .horiz_sync_pulse = 216, .horiz_border = 0, .vert_blank = 73, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x40, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1792, .vert_video = 1344, .refresh_rate_hz = 120.0, .pixel_clock_hz = 333250000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 79, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x41, .edid_std_id = 0xC940, .cvt_id = 0, .horiz_video = 1856, .vert_video = 1392, .refresh_rate_hz = 60.0, .pixel_clock_hz = 218250000, .horiz_blank = 672, .horiz_front_porch = 96, .horiz_sync_pulse = 224, .horiz_border = 0, .vert_blank = 47, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x42, .edid_std_id = 0xC94F, .cvt_id = 0, .horiz_video = 1856, .vert_video = 1392, .refresh_rate_hz = 75.0, .pixel_clock_hz = 288000000, .horiz_blank = 704, .horiz_front_porch = 128, .horiz_sync_pulse = 224, .horiz_border = 0, .vert_blank = 108, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x43, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1856, .vert_video = 1392, .refresh_rate_hz = 120.0, .pixel_clock_hz = 356500000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 82, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x52, .edid_std_id = 0xD1C0, .cvt_id = 0, .horiz_video = 1920, .vert_video = 1080, .refresh_rate_hz = 60.0, .pixel_clock_hz = 148500000, .horiz_blank = 280, .horiz_front_porch = 88, .horiz_sync_pulse = 44, .horiz_border = 0, .vert_blank = 45, .vert_front_porch = 4, .vert_sync_pulse = 5, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x44, .edid_std_id = 0, .cvt_id = 0x572821, .horiz_video = 1920, .vert_video = 1200, .refresh_rate_hz = 60.0, .pixel_clock_hz = 154000000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 35, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x45, .edid_std_id = 0xD100, .cvt_id = 0x572828, .horiz_video = 1920, .vert_video = 1200, .refresh_rate_hz = 60.0, .pixel_clock_hz = 193250000, .horiz_blank = 672, .horiz_front_porch = 136, .horiz_sync_pulse = 200, .horiz_border = 0, .vert_blank = 45, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x46, .edid_std_id = 0xD10F, .cvt_id = 0x572844, .horiz_video = 1920, .vert_video = 1200, .refresh_rate_hz = 75.0, .pixel_clock_hz = 245250000, .horiz_blank = 688, .horiz_front_porch = 136, .horiz_sync_pulse = 208, .horiz_border = 0, .vert_blank = 55, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x47, .edid_std_id = 0xD119, .cvt_id = 0x572862, .horiz_video = 1920, .vert_video = 1200, .refresh_rate_hz = 85.0, .pixel_clock_hz = 281250000, .horiz_blank = 704, .horiz_front_porch = 144, .horiz_sync_pulse = 208, .horiz_border = 0, .vert_blank = 62, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x48, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1920, .vert_video = 1200, .refresh_rate_hz = 120.0, .pixel_clock_hz = 317000000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 71, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x49, .edid_std_id = 0xD140, .cvt_id = 0, .horiz_video = 1920, .vert_video = 1440, .refresh_rate_hz = 60.0, .pixel_clock_hz = 234000000, .horiz_blank = 680, .horiz_front_porch = 128, .horiz_sync_pulse = 208, .horiz_border = 0, .vert_blank = 60, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x4A, .edid_std_id = 0xD14F, .cvt_id = 0, .horiz_video = 1920, .vert_video = 1440, .refresh_rate_hz = 75.0, .pixel_clock_hz = 297000000, .horiz_blank = 720, .horiz_front_porch = 144, .horiz_sync_pulse = 224, .horiz_border = 0, .vert_blank = 60, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x4B, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 1920, .vert_video = 1440, .refresh_rate_hz = 120.0, .pixel_clock_hz = 380500000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 85, .vert_front_porch = 3, .vert_sync_pulse = 4, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x54, .edid_std_id = 0xE1C0, .cvt_id = 0, .horiz_video = 2048, .vert_video = 1152, .refresh_rate_hz = 60.0, .pixel_clock_hz = 162000000, .horiz_blank = 202, .horiz_front_porch = 26, .horiz_sync_pulse = 80, .horiz_border = 0, .vert_blank = 48, .vert_front_porch = 1, .vert_sync_pulse = 3, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x4C, .edid_std_id = 0, .cvt_id = 0x1F3821, .horiz_video = 2560, .vert_video = 1600, .refresh_rate_hz = 60.0, .pixel_clock_hz = 268500000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 46, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, { .dmt_id = 0x4D, .edid_std_id = 0, .cvt_id = 0x1F3828, .horiz_video = 2560, .vert_video = 1600, .refresh_rate_hz = 60.0, .pixel_clock_hz = 348500000, .horiz_blank = 944, .horiz_front_porch = 192, .horiz_sync_pulse = 280, .horiz_border = 0, .vert_blank = 58, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x4E, .edid_std_id = 0, .cvt_id = 0x1F3844, .horiz_video = 2560, .vert_video = 1600, .refresh_rate_hz = 75.0, .pixel_clock_hz = 443250000, .horiz_blank = 976, .horiz_front_porch = 208, .horiz_sync_pulse = 280, .horiz_border = 0, .vert_blank = 72, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x4F, .edid_std_id = 0, .cvt_id = 0x1F3862, .horiz_video = 2560, .vert_video = 1600, .refresh_rate_hz = 85.0, .pixel_clock_hz = 505250000, .horiz_blank = 976, .horiz_front_porch = 208, .horiz_sync_pulse = 280, .horiz_border = 0, .vert_blank = 82, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = false, }, { .dmt_id = 0x50, .edid_std_id = 0, .cvt_id = 0, .horiz_video = 2560, .vert_video = 1600, .refresh_rate_hz = 120.0, .pixel_clock_hz = 552750000, .horiz_blank = 160, .horiz_front_porch = 48, .horiz_sync_pulse = 32, .horiz_border = 0, .vert_blank = 94, .vert_front_porch = 3, .vert_sync_pulse = 6, .vert_border = 0, .reduced_blanking = true, }, }; const size_t _di_dmt_timings_len = 86; libdisplay-info-0.1.1/edid.c000066400000000000000000001262711437312070200156570ustar00rootroot00000000000000#include #include #include #include #include #include "bits.h" #include "dmt.h" #include "edid.h" #include "log.h" /** * The size of an EDID block, defined in section 2.2. */ #define EDID_BLOCK_SIZE 128 /** * The size of an EDID standard timing, defined in section 3.9. */ #define EDID_STANDARD_TIMING_SIZE 2 /** * The size of an EDID CVT timing code, defined in section 3.10.3.8. */ #define EDID_CVT_TIMING_CODE_SIZE 3 /** * Fixed EDID header, defined in section 3.1. */ static const uint8_t header[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; static void add_failure(struct di_edid *edid, const char fmt[], ...) { va_list args; va_start(args, fmt); _di_logger_va_add_failure(edid->logger, fmt, args); va_end(args); } static void add_failure_until(struct di_edid *edid, int revision, const char fmt[], ...) { va_list args; if (edid->revision > revision) { return; } va_start(args, fmt); _di_logger_va_add_failure(edid->logger, fmt, args); va_end(args); } static void parse_version_revision(const uint8_t data[static EDID_BLOCK_SIZE], int *version, int *revision) { *version = (int) data[0x12]; *revision = (int) data[0x13]; } static size_t parse_ext_count(const uint8_t data[static EDID_BLOCK_SIZE]) { return data[0x7E]; } static bool validate_block_checksum(const uint8_t data[static EDID_BLOCK_SIZE]) { uint8_t sum = 0; size_t i; for (i = 0; i < EDID_BLOCK_SIZE; i++) { sum += data[i]; } return sum == 0; } static void parse_vendor_product(struct di_edid *edid, const uint8_t data[static EDID_BLOCK_SIZE]) { struct di_edid_vendor_product *out = &edid->vendor_product; uint16_t man, raw_week, raw_year; int year = 0; /* The ASCII 3-letter manufacturer code is encoded in 5-bit codes. */ man = (uint16_t) ((data[0x08] << 8) | data[0x09]); out->manufacturer[0] = ((man >> 10) & 0x1F) + '@'; out->manufacturer[1] = ((man >> 5) & 0x1F) + '@'; out->manufacturer[2] = ((man >> 0) & 0x1F) + '@'; out->product = (uint16_t) (data[0x0A] | (data[0x0B] << 8)); out->serial = (uint32_t) (data[0x0C] | (data[0x0D] << 8) | (data[0x0E] << 16) | (data[0x0F] << 24)); raw_week = data[0x10]; raw_year = data[0x11]; if (raw_year >= 0x10 || edid->revision < 4) { year = data[0x11] + 1990; } else if (edid->revision == 4) { add_failure(edid, "Year set to reserved value."); } if (raw_week == 0xFF) { /* Special flag for model year */ out->model_year = year; } else { out->manufacture_year = year; if (raw_week > 54) { add_failure_until(edid, 4, "Invalid week %u of manufacture.", raw_week); } else if (raw_week > 0) { out->manufacture_week = raw_week; } } } static void parse_video_input_digital(struct di_edid *edid, uint8_t video_input) { uint8_t color_bit_depth, interface; struct di_edid_video_input_digital *digital = &edid->video_input_digital; if (edid->revision < 2) { if (get_bit_range(video_input, 6, 0) != 0) add_failure(edid, "Digital Video Interface Standard set to reserved value 0x%02x.", video_input); return; } if (edid->revision < 4) { if (get_bit_range(video_input, 6, 1) != 0) add_failure(edid, "Digital Video Interface Standard set to reserved value 0x%02x.", video_input); digital->dfp1 = has_bit(video_input, 0); return; } color_bit_depth = get_bit_range(video_input, 6, 4); if (color_bit_depth == 0x07) { /* Reserved */ add_failure_until(edid, 4, "Color Bit Depth set to reserved value."); } else if (color_bit_depth != 0) { digital->color_bit_depth = 2 * color_bit_depth + 4; } interface = get_bit_range(video_input, 3, 0); switch (interface) { case DI_EDID_VIDEO_INPUT_DIGITAL_UNDEFINED: case DI_EDID_VIDEO_INPUT_DIGITAL_DVI: case DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_A: case DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_B: case DI_EDID_VIDEO_INPUT_DIGITAL_MDDI: case DI_EDID_VIDEO_INPUT_DIGITAL_DISPLAYPORT: digital->interface = interface; break; default: add_failure_until(edid, 4, "Digital Video Interface Standard set to reserved value 0x%02x.", interface); digital->interface = DI_EDID_VIDEO_INPUT_DIGITAL_UNDEFINED; break; } } static void parse_video_input_analog(struct di_edid *edid, uint8_t video_input) { struct di_edid_video_input_analog *analog = &edid->video_input_analog; analog->signal_level_std = get_bit_range(video_input, 6, 5); analog->video_setup = has_bit(video_input, 4); analog->sync_separate = has_bit(video_input, 3); analog->sync_composite = has_bit(video_input, 2); analog->sync_on_green = has_bit(video_input, 1); analog->sync_serrations = has_bit(video_input, 0); } static void parse_basic_params_features(struct di_edid *edid, const uint8_t data[static EDID_BLOCK_SIZE]) { uint8_t video_input, width, height, features; struct di_edid_screen_size *screen_size = &edid->screen_size; video_input = data[0x14]; edid->is_digital = has_bit(video_input, 7); if (edid->is_digital) { parse_video_input_digital(edid, video_input); } else { parse_video_input_analog(edid, video_input); } /* v1.3 says screen size is undefined if either byte is zero, v1.4 says * screen size and aspect ratio are undefined if both bytes are zero and * encodes the aspect ratio if either byte is zero. */ width = data[0x15]; height = data[0x16]; if (width > 0 && height > 0) { screen_size->width_cm = width; screen_size->height_cm = height; } else if (edid->revision >= 4) { if (width > 0) { screen_size->landscape_aspect_ratio = ((float) width + 99) / 100; } else if (height > 0) { screen_size->portait_aspect_ratio = ((float) height + 99) / 100; } } if (data[0x17] != 0xFF) { edid->gamma = ((float) data[0x17] + 100) / 100; } else { edid->gamma = 0; } features = data[0x18]; edid->dpms.standby = has_bit(features, 7); edid->dpms.suspend = has_bit(features, 6); edid->dpms.off = has_bit(features, 5); if (edid->is_digital && edid->revision >= 4) { edid->color_encoding_formats.rgb444 = true; edid->color_encoding_formats.ycrcb444 = has_bit(features, 3); edid->color_encoding_formats.ycrcb422 = has_bit(features, 4); edid->display_color_type = DI_EDID_DISPLAY_COLOR_UNDEFINED; } else { edid->display_color_type = get_bit_range(features, 4, 3); } if (edid->revision >= 4) { edid->misc_features.has_preferred_timing = true; edid->misc_features.continuous_freq = has_bit(features, 0); edid->misc_features.preferred_timing_is_native = has_bit(features, 1); } else { edid->misc_features.default_gtf = has_bit(features, 0); edid->misc_features.has_preferred_timing = has_bit(features, 1); } edid->misc_features.srgb_is_primary = has_bit(features, 2); } static float decode_chromaticity_coord(uint8_t hi, uint8_t lo) { uint16_t raw; /* only 10 bits are used */ raw = (uint16_t) ((hi << 2) | lo); return (float) raw / 1024; } static void parse_chromaticity_coords(struct di_edid *edid, const uint8_t data[static EDID_BLOCK_SIZE]) { uint8_t lo; bool all_set, any_set; struct di_edid_chromaticity_coords *coords; coords = &edid->chromaticity_coords; lo = data[0x19]; coords->red_x = decode_chromaticity_coord(data[0x1B], get_bit_range(lo, 7, 6)); coords->red_y = decode_chromaticity_coord(data[0x1C], get_bit_range(lo, 5, 4)); coords->green_x = decode_chromaticity_coord(data[0x1D], get_bit_range(lo, 3, 2)); coords->green_y = decode_chromaticity_coord(data[0x1E], get_bit_range(lo, 1, 0)); lo = data[0x1A]; coords->blue_x = decode_chromaticity_coord(data[0x1F], get_bit_range(lo, 7, 6)); coords->blue_y = decode_chromaticity_coord(data[0x20], get_bit_range(lo, 5, 4)); coords->white_x = decode_chromaticity_coord(data[0x21], get_bit_range(lo, 3, 2)); coords->white_y = decode_chromaticity_coord(data[0x22], get_bit_range(lo, 1, 0)); /* Either all primaries coords must be set, either none must be set */ any_set = coords->red_x != 0 || coords->red_y != 0 || coords->green_x != 0 || coords->green_y != 0 || coords->blue_x != 0 || coords->blue_y != 0; all_set = coords->red_x != 0 && coords->red_y != 0 && coords->green_x != 0 && coords->green_y != 0 && coords->blue_x != 0 && coords->blue_y != 0; if (any_set && !all_set) { add_failure(edid, "Some but not all primaries coordinates are unset."); } /* Both white-point coords must be set */ if (coords->white_x == 0 || coords->white_y == 0) { add_failure(edid, "White-point coordinates are unset."); } } static void parse_established_timings_i_ii(struct di_edid *edid, const uint8_t data[static EDID_BLOCK_SIZE]) { struct di_edid_established_timings_i_ii *timings = &edid->established_timings_i_ii; timings->has_720x400_70hz = has_bit(data[0x23], 7); timings->has_720x400_88hz = has_bit(data[0x23], 6); timings->has_640x480_60hz = has_bit(data[0x23], 5); timings->has_640x480_67hz = has_bit(data[0x23], 4); timings->has_640x480_72hz = has_bit(data[0x23], 3); timings->has_640x480_75hz = has_bit(data[0x23], 2); timings->has_800x600_56hz = has_bit(data[0x23], 1); timings->has_800x600_60hz = has_bit(data[0x23], 0); /* Established timings II */ timings->has_800x600_72hz = has_bit(data[0x24], 7); timings->has_800x600_75hz = has_bit(data[0x24], 6); timings->has_832x624_75hz = has_bit(data[0x24], 5); timings->has_1024x768_87hz_interlaced = has_bit(data[0x24], 4); timings->has_1024x768_60hz = has_bit(data[0x24], 3); timings->has_1024x768_70hz = has_bit(data[0x24], 2); timings->has_1024x768_75hz = has_bit(data[0x24], 1); timings->has_1280x1024_75hz = has_bit(data[0x24], 0); timings->has_1152x870_75hz = has_bit(data[0x25], 7); /* TODO: manufacturer specified timings in bits 6:0 */ } static bool parse_standard_timing(struct di_edid *edid, const uint8_t data[static EDID_STANDARD_TIMING_SIZE], struct di_edid_standard_timing **out) { struct di_edid_standard_timing *t; *out = NULL; if (data[0] == 0x01 && data[1] == 0x01) { /* Unused */ return true; } if (data[0] == 0x00) { add_failure_until(edid, 4, "Use 0x0101 as the invalid Standard Timings code, not 0x%02x%02x.", data[0], data[1]); return true; } t = calloc(1, sizeof(*t)); if (!t) { return false; } t->horiz_video = ((int32_t) data[0] + 31) * 8; t->aspect_ratio = get_bit_range(data[1], 7, 6); t->refresh_rate_hz = (int32_t) get_bit_range(data[1], 5, 0) + 60; *out = t; return true; } struct di_edid_detailed_timing_def_priv * _di_edid_parse_detailed_timing_def(const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE]) { struct di_edid_detailed_timing_def_priv *priv; struct di_edid_detailed_timing_def *def; struct di_edid_detailed_timing_analog_composite *analog_composite; struct di_edid_detailed_timing_bipolar_analog_composite *bipolar_analog_composite; struct di_edid_detailed_timing_digital_composite *digital_composite; struct di_edid_detailed_timing_digital_separate *digital_separate; int raw; uint8_t flags, stereo_hi, stereo_lo; priv = calloc(1, sizeof(*priv)); if (!priv) { return NULL; } def = &priv->base; raw = (data[1] << 8) | data[0]; def->pixel_clock_hz = raw * 10 * 1000; def->horiz_video = (get_bit_range(data[4], 7, 4) << 8) | data[2]; def->horiz_blank = (get_bit_range(data[4], 3, 0) << 8) | data[3]; def->vert_video = (get_bit_range(data[7], 7, 4) << 8) | data[5]; def->vert_blank = (get_bit_range(data[7], 3, 0) << 8) | data[6]; def->horiz_front_porch = (get_bit_range(data[11], 7, 6) << 8) | data[8]; def->horiz_sync_pulse = (get_bit_range(data[11], 5, 4) << 8) | data[9]; def->vert_front_porch = (get_bit_range(data[11], 3, 2) << 4) | get_bit_range(data[10], 7, 4); def->vert_sync_pulse = (get_bit_range(data[11], 1, 0) << 4) | get_bit_range(data[10], 3, 0); def->horiz_image_mm = (get_bit_range(data[14], 7, 4) << 8) | data[12]; def->vert_image_mm = (get_bit_range(data[14], 3, 0) << 8) | data[13]; if ((def->horiz_image_mm == 16 && def->vert_image_mm == 9) || (def->horiz_image_mm == 4 && def->vert_image_mm == 3)) { /* Table 3.21 note 18.2: these are special cases and define the * aspect ratio rather than the size in mm. * TODO: expose these values */ def->horiz_image_mm = def->vert_image_mm = 0; } def->horiz_border = data[15]; def->vert_border = data[16]; flags = data[17]; def->interlaced = has_bit(flags, 7); stereo_hi = get_bit_range(flags, 6, 5); stereo_lo = get_bit_range(flags, 0, 0); if (stereo_hi == 0) { def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_NONE; } else { switch ((stereo_hi << 1) | stereo_lo) { case (1 << 1) | 0: def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_RIGHT; break; case (2 << 1) | 0: def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_LEFT; break; case (1 << 1) | 1: def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_RIGHT; break; case (2 << 1) | 1: def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_LEFT; break; case (3 << 1) | 0: def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_4_WAY_INTERLEAVED; break; case (3 << 1) | 1: def->stereo = DI_EDID_DETAILED_TIMING_DEF_STEREO_SIDE_BY_SIDE_INTERLEAVED; break; default: abort(); /* unreachable */ } } def->signal_type = get_bit_range(flags, 4, 3); switch (def->signal_type) { case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_ANALOG_COMPOSITE: analog_composite = &priv->analog_composite; analog_composite->sync_serrations = has_bit(flags, 2); analog_composite->sync_on_green = !has_bit(flags, 1); def->analog_composite = analog_composite; break; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_BIPOLAR_ANALOG_COMPOSITE: bipolar_analog_composite = &priv->bipolar_analog_composite; bipolar_analog_composite->sync_serrations = has_bit(flags, 2); bipolar_analog_composite->sync_on_green = !has_bit(flags, 1); def->bipolar_analog_composite = bipolar_analog_composite; break; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_COMPOSITE: digital_composite = &priv->digital_composite; digital_composite->sync_serrations = has_bit(flags, 2); digital_composite->sync_horiz_polarity = has_bit(flags, 1); def->digital_composite = digital_composite; break; case DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_SEPARATE: digital_separate = &priv->digital_separate; digital_separate->sync_vert_polarity = has_bit(flags, 2); digital_separate->sync_horiz_polarity = has_bit(flags, 1); def->digital_separate = digital_separate; break; } return priv; } static bool decode_display_range_limits_offset(struct di_edid *edid, uint8_t flags, int *max_offset, int *min_offset) { switch (flags) { case 0x00: /* No offset */ break; case 0x02: *max_offset = 255; break; case 0x03: *max_offset = 255; *min_offset = 255; break; default: add_failure_until(edid, 4, "Range offset flags set to reserved value 0x%02x.", flags); return false; } return true; } static bool parse_display_range_limits(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE], struct di_edid_display_range_limits_priv *priv) { uint8_t offset_flags, vert_offset_flags, horiz_offset_flags; uint8_t support_flags, preferred_aspect_ratio; int max_vert_offset = 0, min_vert_offset = 0; int max_horiz_offset = 0, min_horiz_offset = 0; size_t i; struct di_edid_display_range_limits *base; struct di_edid_display_range_limits_secondary_gtf *secondary_gtf; struct di_edid_display_range_limits_cvt *cvt; base = &priv->base; offset_flags = data[4]; if (edid->revision >= 4) { vert_offset_flags = get_bit_range(offset_flags, 1, 0); horiz_offset_flags = get_bit_range(offset_flags, 3, 2); if (!decode_display_range_limits_offset(edid, vert_offset_flags, &max_vert_offset, &min_vert_offset)) { return false; } if (!decode_display_range_limits_offset(edid, horiz_offset_flags, &max_horiz_offset, &min_horiz_offset)) { return false; } if (edid->revision <= 4 && get_bit_range(offset_flags, 7, 4) != 0) { add_failure(edid, "Display Range Limits: Bits 7:4 of the range offset flags are reserved."); } } else if (offset_flags != 0) { add_failure(edid, "Display Range Limits: Range offset flags are unsupported in EDID 1.3."); } if (edid->revision <= 4 && (data[5] == 0 || data[6] == 0 || data[7] == 0 || data[8] == 0)) { add_failure(edid, "Display Range Limits: Range limits set to reserved values."); return false; } base->min_vert_rate_hz = data[5] + min_vert_offset; base->max_vert_rate_hz = data[6] + max_vert_offset; base->min_horiz_rate_hz = (data[7] + min_horiz_offset) * 1000; base->max_horiz_rate_hz = (data[8] + max_horiz_offset) * 1000; if (base->min_vert_rate_hz > base->max_vert_rate_hz) { add_failure(edid, "Display Range Limits: Min vertical rate > max vertical rate."); return false; } if (base->min_horiz_rate_hz > base->max_horiz_rate_hz) { add_failure(edid, "Display Range Limits: Min horizontal freq > max horizontal freq."); return false; } base->max_pixel_clock_hz = (int32_t) data[9] * 10 * 1000 * 1000; if (edid->revision == 4 && base->max_pixel_clock_hz == 0) { add_failure(edid, "Display Range Limits: EDID 1.4 block does not set max dotclock."); } support_flags = data[10]; switch (support_flags) { case 0x00: /* For EDID 1.4 and later, always indicates support for default * GTF. For EDID 1.3 and earlier, a misc features bit indicates * support for default GTF. */ if (edid->revision >= 4 || edid->misc_features.default_gtf) { base->type = DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF; } else { base->type = DI_EDID_DISPLAY_RANGE_LIMITS_BARE; } break; case 0x01: if (edid->revision < 4) { /* Reserved */ add_failure(edid, "Display Range Limits: 'Bare Limits' is not allowed for EDID < 1.4."); return false; } base->type = DI_EDID_DISPLAY_RANGE_LIMITS_BARE; break; case 0x02: base->type = DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF; break; case 0x04: if (edid->revision < 4) { /* Reserved */ add_failure(edid, "Display Range Limits: 'CVT' is not allowed for EDID < 1.4."); return false; } base->type = DI_EDID_DISPLAY_RANGE_LIMITS_CVT; break; default: /* Reserved */ if (edid->revision <= 4) { add_failure(edid, "Display Range Limits: Unknown range class (0x%02x).", support_flags); return false; } base->type = DI_EDID_DISPLAY_RANGE_LIMITS_BARE; break; } /* Some types require the display to support continuous frequencies, but * this flag is only set for EDID 1.4 and later */ if (edid->revision >= 4 && !edid->misc_features.continuous_freq) { switch (base->type) { case DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF: case DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF: add_failure(edid, "Display Range Limits: GTF can't be combined with non-continuous frequencies."); return false; case DI_EDID_DISPLAY_RANGE_LIMITS_CVT: add_failure(edid, "Display Range Limits: CVT can't be combined with non-continuous frequencies."); return false; default: break; } } switch (base->type) { case DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF: secondary_gtf = &priv->secondary_gtf; if (data[11] != 0) add_failure(edid, "Display Range Limits: Byte 11 is 0x%02x instead of 0x00.", data[11]); secondary_gtf->start_freq_hz = data[12] * 2 * 1000; secondary_gtf->c = (float) data[13] / 2; secondary_gtf->m = (float) ((data[15] << 8) | data[14]); secondary_gtf->k = (float) data[16]; secondary_gtf->j = (float) data[17] / 2; base->secondary_gtf = secondary_gtf; break; case DI_EDID_DISPLAY_RANGE_LIMITS_CVT: cvt = &priv->cvt; cvt->version = get_bit_range(data[11], 7, 4); cvt->revision = get_bit_range(data[11], 3, 0); base->max_pixel_clock_hz -= get_bit_range(data[12], 7, 2) * 250 * 1000; cvt->max_horiz_px = 8 * ((get_bit_range(data[12], 1, 0) << 8) | data[13]); cvt->supported_aspect_ratio = data[14]; if (get_bit_range(data[14], 2, 0) != 0) add_failure_until(edid, 4, "Display Range Limits: Reserved bits of byte 14 are non-zero."); preferred_aspect_ratio = get_bit_range(data[15], 7, 5); switch (preferred_aspect_ratio) { case 0: cvt->preferred_aspect_ratio = DI_EDID_CVT_ASPECT_RATIO_4_3; break; case 1: cvt->preferred_aspect_ratio = DI_EDID_CVT_ASPECT_RATIO_16_9; break; case 2: cvt->preferred_aspect_ratio = DI_EDID_CVT_ASPECT_RATIO_16_10; break; case 3: cvt->preferred_aspect_ratio = DI_EDID_CVT_ASPECT_RATIO_5_4; break; case 4: cvt->preferred_aspect_ratio = DI_EDID_CVT_ASPECT_RATIO_15_9; break; default: /* Reserved */ add_failure_until(edid, 4, "Display Range Limits: Invalid preferred aspect ratio 0x%02x.", preferred_aspect_ratio); return false; } cvt->standard_blanking = has_bit(data[15], 3); cvt->reduced_blanking = has_bit(data[15], 4); if (get_bit_range(data[15], 2, 0) != 0) add_failure_until(edid, 4, "Display Range Limits: Reserved bits of byte 15 are non-zero."); cvt->supported_scaling = data[16]; if (get_bit_range(data[16], 3, 0) != 0) add_failure_until(edid, 4, "Display Range Limits: Reserved bits of byte 16 are non-zero."); cvt->preferred_vert_refresh_hz = data[17]; if (cvt->preferred_vert_refresh_hz == 0) { add_failure_until(edid, 4, "Display Range Limits: Preferred vertical refresh rate must be specified."); return false; } base->cvt = cvt; break; case DI_EDID_DISPLAY_RANGE_LIMITS_BARE: case DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF: if (data[11] != 0x0A) add_failure(edid, "Display Range Limits: Byte 11 is 0x%02x instead of 0x0a.", data[11]); for (i = 12; i < EDID_BYTE_DESCRIPTOR_SIZE; i++) { if (data[i] != 0x20) { add_failure(edid, "Display Range Limits: Bytes 12-17 must be 0x20."); break; } } break; } return true; } static bool parse_standard_timings_descriptor(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE], struct di_edid_display_descriptor *desc) { struct di_edid_standard_timing *t; size_t i; const uint8_t *timing_data; for (i = 0; i < EDID_MAX_DESCRIPTOR_STANDARD_TIMING_COUNT; i++) { timing_data = &data[5 + i * EDID_STANDARD_TIMING_SIZE]; if (!parse_standard_timing(edid, timing_data, &t)) return false; if (t) { assert(desc->standard_timings_len < EDID_MAX_DESCRIPTOR_STANDARD_TIMING_COUNT); desc->standard_timings[desc->standard_timings_len++] = t; } } if (data[17] != 0x0A) add_failure_until(edid, 4, "Standard Timing Identifications: Last byte must be a line feed."); return true; } /** * Mapping table for established timings III. * * Contains one entry per bit, with the value set to the DMT ID. */ static const uint8_t established_timings_iii[] = { /* 0x06 */ 0x01, /* 640 x 350 @ 85 Hz */ 0x02, /* 640 x 400 @ 85 Hz */ 0x03, /* 720 x 400 @ 85 Hz */ 0x07, /* 640 x 480 @ 85 Hz */ 0x0e, /* 848 x 480 @ 60 Hz */ 0x0c, /* 800 x 600 @ 85 Hz */ 0x13, /* 1024 x 768 @ 85 Hz */ 0x15, /* 1152 x 864 @ 75 Hz */ /* 0x07 */ 0x16, /* 1280 x 768 @ 60 Hz (RB) */ 0x17, /* 1280 x 768 @ 60 Hz */ 0x18, /* 1280 x 768 @ 75 Hz */ 0x19, /* 1280 x 768 @ 85 Hz */ 0x20, /* 1280 x 960 @ 60 Hz */ 0x21, /* 1280 x 960 @ 85 Hz */ 0x23, /* 1280 x 1024 @ 60 Hz */ 0x25, /* 1280 x 1024 @ 85 Hz */ /* 0x08 */ 0x27, /* 1360 x 768 @ 60 Hz */ 0x2e, /* 1440 x 900 @ 60 Hz (RB) */ 0x2f, /* 1440 x 900 @ 60 Hz */ 0x30, /* 1440 x 900 @ 75 Hz */ 0x31, /* 1440 x 900 @ 85 Hz */ 0x29, /* 1400 x 1050 @ 60 Hz (RB) */ 0x2a, /* 1400 x 1050 @ 60 Hz */ 0x2b, /* 1400 x 1050 @ 75 Hz */ /* 0x09 */ 0x2c, /* 1400 x 1050 @ 85 Hz */ 0x39, /* 1680 x 1050 @ 60 Hz (RB) */ 0x3a, /* 1680 x 1050 @ 60 Hz */ 0x3b, /* 1680 x 1050 @ 75 Hz */ 0x3c, /* 1680 x 1050 @ 85 Hz */ 0x33, /* 1600 x 1200 @ 60 Hz */ 0x34, /* 1600 x 1200 @ 65 Hz */ 0x35, /* 1600 x 1200 @ 70 Hz */ /* 0x0a */ 0x36, /* 1600 x 1200 @ 75 Hz */ 0x37, /* 1600 x 1200 @ 85 Hz */ 0x3e, /* 1792 x 1344 @ 60 Hz */ 0x3f, /* 1792 x 1344 @ 75 Hz */ 0x41, /* 1856 x 1392 @ 60 Hz */ 0x42, /* 1856 x 1392 @ 75 Hz */ 0x44, /* 1920 x 1200 @ 60 Hz (RB) */ 0x45, /* 1920 x 1200 @ 60 Hz */ /* 0x0b */ 0x46, /* 1920 x 1200 @ 75 Hz */ 0x47, /* 1920 x 1200 @ 85 Hz */ 0x49, /* 1920 x 1440 @ 60 Hz */ 0x4a, /* 1920 x 1440 @ 75 Hz */ }; static_assert(EDID_MAX_DESCRIPTOR_ESTABLISHED_TIMING_III_COUNT == sizeof(established_timings_iii) / sizeof(established_timings_iii[0]), "Invalid number of established timings III in table"); static const struct di_dmt_timing * get_dmt_timing(uint8_t dmt_id) { size_t i; const struct di_dmt_timing *t; for (i = 0; i < _di_dmt_timings_len; i++) { t = &_di_dmt_timings[i]; if (t->dmt_id == dmt_id) return t; } return NULL; } static void parse_established_timings_iii_descriptor(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE], struct di_edid_display_descriptor *desc) { size_t i, offset, bit; uint8_t dmt_id; const struct di_dmt_timing *t; bool has_zeroes; if (edid->revision < 4) add_failure(edid, "Established timings III: Not allowed for EDID < 1.4."); for (i = 0; i < EDID_MAX_DESCRIPTOR_ESTABLISHED_TIMING_III_COUNT; i++) { dmt_id = established_timings_iii[i]; offset = 0x06 + i / 8; bit = 7 - i % 8; assert(offset < EDID_BYTE_DESCRIPTOR_SIZE); if (has_bit(data[offset], bit)) { t = get_dmt_timing(dmt_id); assert(t != NULL); desc->established_timings_iii[desc->established_timings_iii_len++] = t; } } has_zeroes = get_bit_range(data[11], 3, 0) == 0; for (i = 12; i < EDID_BYTE_DESCRIPTOR_SIZE; i++) { has_zeroes = has_zeroes && data[i] == 0; } if (!has_zeroes) { add_failure_until(edid, 4, "Established timings III: Reserved bits must be set to zero."); } } static bool parse_color_point_descriptor(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE], struct di_edid_display_descriptor *desc) { struct di_edid_color_point *c; if (data[5] == 0) { add_failure(edid, "White Point Index Number set to reserved value 0"); } c = calloc(1, sizeof(*c)); if (!c) { return false; } c->index = data[5]; c->white_x = decode_chromaticity_coord(data[7], get_bit_range(data[6], 3, 2)); c->white_y = decode_chromaticity_coord(data[8], get_bit_range(data[6], 1, 0)); if (data[9] != 0xFF) { c->gamma = ((float) data[9] + 100) / 100; } desc->color_points[desc->color_points_len++] = c; if (data[10] == 0) { return true; } c = calloc(1, sizeof(*c)); if (!c) { return false; } c->index = data[10]; c->white_x = decode_chromaticity_coord(data[12], get_bit_range(data[11], 3, 2)); c->white_y = decode_chromaticity_coord(data[13], get_bit_range(data[11], 1, 0)); if (data[14] != 0xFF) { c->gamma = ((float) data[14] + 100) / 100; } desc->color_points[desc->color_points_len++] = c; return true; } static void parse_color_management_data_descriptor(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE], struct di_edid_display_descriptor *desc) { desc->dcm_data.version = data[5]; desc->dcm_data.red_a3 = (uint16_t)(data[6] | (data[7] << 8)) / 100.0f; desc->dcm_data.red_a2 = (uint16_t)(data[8] | (data[9] << 8)) / 100.0f; desc->dcm_data.green_a3 = (uint16_t)(data[10] | (data[11] << 8)) / 100.0f; desc->dcm_data.green_a2 = (uint16_t)(data[12] | (data[13] << 8)) / 100.0f; desc->dcm_data.blue_a3 = (uint16_t)(data[14] | (data[15] << 8)) / 100.0f; desc->dcm_data.blue_a2 = (uint16_t)(data[16] | (data[17] << 8)) / 100.0f; if (desc->dcm_data.version != 3) { add_failure_until(edid, 4, "Color Management Data version must be 3"); } } static bool is_cvt_timing_code_preferred_vrate_supported(const struct di_edid_cvt_timing_code *t) { switch (t->preferred_vertical_rate) { case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ: return t->supports_50hz_sb; case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ: return t->supports_60hz_sb || t->supports_60hz_rb; case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ: return t->supports_75hz_sb; case DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ: return t->supports_85hz_sb; } abort(); /* unreachable */ } static bool parse_cvt_timing_code(struct di_edid *edid, const uint8_t data[static EDID_CVT_TIMING_CODE_SIZE], struct di_edid_cvt_timing_code **out, bool first) { struct di_edid_cvt_timing_code *t; int32_t raw; *out = NULL; if (!first && data[0] == 0 && data[1] == 0 && data[2] == 0) { /* Unused */ return true; } if (data[0] == 0) { add_failure(edid, "CVT byte 0 is 0, which is a reserved value."); } t = calloc(1, sizeof(*t)); if (!t) { return false; } raw = (int32_t)(data[0] | (get_bit_range(data[1], 7, 4) << 8)); t->addressable_lines_per_field = (raw + 1) * 2; t->aspect_ratio = get_bit_range(data[1], 3, 2); if (get_bit_range(data[1], 1, 0) != 0) { add_failure(edid, "Reserved bits of CVT byte 1 are non-zero."); } t->supports_50hz_sb = has_bit(data[2], 4); t->supports_60hz_sb = has_bit(data[2], 3); t->supports_75hz_sb = has_bit(data[2], 2); t->supports_85hz_sb = has_bit(data[2], 1); t->supports_60hz_rb = has_bit(data[2], 0); if (get_bit_range(data[2], 4, 0) == 0) { add_failure(edid, "CVT byte 2 does not support any vertical rates."); } t->preferred_vertical_rate = get_bit_range(data[2], 6, 5); if (has_bit(data[2], 7) != 0) { add_failure(edid, "Reserved bit of CVT byte 2 is non-zero."); } if (!is_cvt_timing_code_preferred_vrate_supported(t)) add_failure(edid, "The preferred CVT Vertical Rate is not supported."); *out = t; return true; } static bool parse_cvt_timing_codes_descriptor(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE], struct di_edid_display_descriptor *desc) { struct di_edid_cvt_timing_code *t; size_t i; const uint8_t *timing_data; if (data[5] != 1) { add_failure_until(edid, 4, "Invalid version number %u.", data[5]); } for (i = 0; i < EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT; i++) { timing_data = &data[6 + i * EDID_CVT_TIMING_CODE_SIZE]; if (!parse_cvt_timing_code(edid, timing_data, &t, !i)) return false; if (t) { assert(desc->cvt_timing_codes_len < EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT); desc->cvt_timing_codes[desc->cvt_timing_codes_len++] = t; } } return true; } static bool parse_byte_descriptor(struct di_edid *edid, const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE]) { struct di_edid_display_descriptor *desc; struct di_edid_detailed_timing_def_priv *detailed_timing_def; uint8_t tag; char *newline; if (data[0] || data[1]) { if (edid->display_descriptors_len > 0) { /* A detailed timing descriptor is not allowed after a * display descriptor per note 3 of table 3.20. */ add_failure(edid, "Invalid detailed timing descriptor ordering."); } detailed_timing_def = _di_edid_parse_detailed_timing_def(data); if (!detailed_timing_def) { return false; } assert(edid->detailed_timing_defs_len < EDID_BYTE_DESCRIPTOR_COUNT); edid->detailed_timing_defs[edid->detailed_timing_defs_len++] = detailed_timing_def; return true; } if (edid->revision >= 3 && edid->revision <= 4 && edid->detailed_timing_defs_len == 0) { /* Per section 3.10.1 */ add_failure(edid, "The first byte descriptor must contain the preferred timing."); } desc = calloc(1, sizeof(*desc)); if (!desc) { return false; } tag = data[3]; switch (tag) { case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL: case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING: case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME: memcpy(desc->str, &data[5], 13); /* A newline (if any) indicates the end of the string. */ newline = strchr(desc->str, '\n'); if (newline) { newline[0] = '\0'; } break; case DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS: if (!parse_display_range_limits(edid, data, &desc->range_limits)) { free(desc); return true; } break; case DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS: if (!parse_standard_timings_descriptor(edid, data, desc)) { free(desc); return false; } break; case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III: parse_established_timings_iii_descriptor(edid, data, desc); break; case DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT: if (!parse_color_point_descriptor(edid, data, desc)) { free(desc); return false; } break; case DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA: parse_color_management_data_descriptor(edid, data, desc); break; case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES: if (!parse_cvt_timing_codes_descriptor(edid, data, desc)) { free(desc); return false; } break; case DI_EDID_DISPLAY_DESCRIPTOR_DUMMY: break; /* Ignore */ default: free(desc); if (tag <= 0x0F) { /* Manufacturer-specific */ } else { add_failure_until(edid, 4, "Unknown Type 0x%02hhx.", tag); } return true; } desc->tag = tag; assert(edid->display_descriptors_len < EDID_BYTE_DESCRIPTOR_COUNT); edid->display_descriptors[edid->display_descriptors_len++] = desc; return true; } static bool parse_ext(struct di_edid *edid, const uint8_t data[static EDID_BLOCK_SIZE]) { struct di_edid_ext *ext; uint8_t tag; struct di_logger logger; char section_name[64]; if (!validate_block_checksum(data)) { errno = EINVAL; return false; } ext = calloc(1, sizeof(*ext)); if (!ext) { return false; } tag = data[0x00]; switch (tag) { case DI_EDID_EXT_CEA: snprintf(section_name, sizeof(section_name), "Block %zu, CTA-861 Extension Block", edid->exts_len + 1); logger = (struct di_logger) { .f = edid->logger->f, .section = section_name, }; if (!_di_edid_cta_parse(&ext->cta, data, EDID_BLOCK_SIZE, &logger)) { free(ext); return false; } break; case DI_EDID_EXT_VTB: case DI_EDID_EXT_DI: case DI_EDID_EXT_LS: case DI_EDID_EXT_DPVL: case DI_EDID_EXT_BLOCK_MAP: case DI_EDID_EXT_VENDOR: /* Supported */ break; case DI_EDID_EXT_DISPLAYID: snprintf(section_name, sizeof(section_name), "Block %zu, DisplayID Extension Block", edid->exts_len + 1); logger = (struct di_logger) { .f = edid->logger->f, .section = section_name, }; if (!_di_displayid_parse(&ext->displayid, &data[1], EDID_BLOCK_SIZE - 2, &logger)) { free(ext); return false; } break; default: /* Unsupported */ free(ext); add_failure_until(edid, 4, "Unknown Extension Block."); return true; } ext->tag = tag; assert(edid->exts_len < EDID_MAX_BLOCK_COUNT - 1); edid->exts[edid->exts_len++] = ext; return true; } struct di_edid * _di_edid_parse(const void *data, size_t size, FILE *failure_msg_file) { struct di_edid *edid; struct di_logger logger; int version, revision; size_t exts_len, i; const uint8_t *standard_timing_data, *byte_desc_data, *ext_data; struct di_edid_standard_timing *standard_timing; if (size < EDID_BLOCK_SIZE || size > EDID_MAX_BLOCK_COUNT * EDID_BLOCK_SIZE || size % EDID_BLOCK_SIZE != 0) { errno = EINVAL; return NULL; } if (memcmp(data, header, sizeof(header)) != 0) { errno = EINVAL; return NULL; } parse_version_revision(data, &version, &revision); if (version != 1) { /* Only EDID version 1 is supported -- as per section 2.1.7 * subsequent versions break the structure */ errno = ENOTSUP; return NULL; } if (!validate_block_checksum(data)) { errno = EINVAL; return NULL; } exts_len = size / EDID_BLOCK_SIZE - 1; if (exts_len != parse_ext_count(data)) { errno = EINVAL; return NULL; } edid = calloc(1, sizeof(*edid)); if (!edid) { return NULL; } logger = (struct di_logger) { .f = failure_msg_file, .section = "Block 0, Base EDID", }; edid->logger = &logger; edid->version = version; edid->revision = revision; parse_vendor_product(edid, data); parse_basic_params_features(edid, data); parse_chromaticity_coords(edid, data); parse_established_timings_i_ii(edid, data); for (i = 0; i < EDID_MAX_STANDARD_TIMING_COUNT; i++) { standard_timing_data = (const uint8_t *) data + 0x26 + i * EDID_STANDARD_TIMING_SIZE; if (!parse_standard_timing(edid, standard_timing_data, &standard_timing)) { _di_edid_destroy(edid); return NULL; } if (standard_timing) { assert(edid->standard_timings_len < EDID_MAX_STANDARD_TIMING_COUNT); edid->standard_timings[edid->standard_timings_len++] = standard_timing; } } for (i = 0; i < EDID_BYTE_DESCRIPTOR_COUNT; i++) { byte_desc_data = (const uint8_t *) data + 0x36 + i * EDID_BYTE_DESCRIPTOR_SIZE; if (!parse_byte_descriptor(edid, byte_desc_data)) { _di_edid_destroy(edid); return NULL; } } for (i = 0; i < exts_len; i++) { ext_data = (const uint8_t *) data + (i + 1) * EDID_BLOCK_SIZE; if (!parse_ext(edid, ext_data)) { _di_edid_destroy(edid); return NULL; } } edid->logger = NULL; return edid; } static void destroy_display_descriptor(struct di_edid_display_descriptor *desc) { size_t i; switch (desc->tag) { case DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS: for (i = 0; i < desc->standard_timings_len; i++) { free(desc->standard_timings[i]); } break; case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES: for (i = 0; i < desc->cvt_timing_codes_len; i++) { free(desc->cvt_timing_codes[i]); } break; default: break; /* Nothing to do */ } free(desc); } void _di_edid_destroy(struct di_edid *edid) { size_t i; struct di_edid_ext *ext; for (i = 0; i < edid->standard_timings_len; i++) { free(edid->standard_timings[i]); } for (i = 0; i < edid->detailed_timing_defs_len; i++) { free(edid->detailed_timing_defs[i]); } for (i = 0; i < edid->display_descriptors_len; i++) { destroy_display_descriptor(edid->display_descriptors[i]); } for (i = 0; edid->exts[i] != NULL; i++) { ext = edid->exts[i]; switch (ext->tag) { case DI_EDID_EXT_CEA: _di_edid_cta_finish(&ext->cta); break; case DI_EDID_EXT_DISPLAYID: _di_displayid_finish(&ext->displayid); break; default: break; /* Nothing to do */ } free(ext); } free(edid); } int di_edid_get_version(const struct di_edid *edid) { return edid->version; } int di_edid_get_revision(const struct di_edid *edid) { return edid->revision; } const struct di_edid_vendor_product * di_edid_get_vendor_product(const struct di_edid *edid) { return &edid->vendor_product; } const struct di_edid_video_input_analog * di_edid_get_video_input_analog(const struct di_edid *edid) { return edid->is_digital ? NULL : &edid->video_input_analog; } const struct di_edid_video_input_digital * di_edid_get_video_input_digital(const struct di_edid *edid) { return edid->is_digital ? &edid->video_input_digital : NULL; } const struct di_edid_screen_size * di_edid_get_screen_size(const struct di_edid *edid) { return &edid->screen_size; } float di_edid_get_basic_gamma(const struct di_edid *edid) { return edid->gamma; } const struct di_edid_dpms * di_edid_get_dpms(const struct di_edid *edid) { return &edid->dpms; } enum di_edid_display_color_type di_edid_get_display_color_type(const struct di_edid *edid) { return edid->display_color_type; } const struct di_edid_color_encoding_formats * di_edid_get_color_encoding_formats(const struct di_edid *edid) { /* If color encoding formats are specified, RGB 4:4:4 is always * supported. */ return edid->color_encoding_formats.rgb444 ? &edid->color_encoding_formats : NULL; } const struct di_edid_misc_features * di_edid_get_misc_features(const struct di_edid *edid) { return &edid->misc_features; } const struct di_edid_chromaticity_coords * di_edid_get_chromaticity_coords(const struct di_edid *edid) { return &edid->chromaticity_coords; } const struct di_edid_established_timings_i_ii * di_edid_get_established_timings_i_ii(const struct di_edid *edid) { return &edid->established_timings_i_ii; } int32_t di_edid_standard_timing_get_vert_video(const struct di_edid_standard_timing *t) { switch (t->aspect_ratio) { case DI_EDID_STANDARD_TIMING_16_10: return t->horiz_video * 10 / 16; case DI_EDID_STANDARD_TIMING_4_3: return t->horiz_video * 3 / 4; case DI_EDID_STANDARD_TIMING_5_4: return t->horiz_video * 4 / 5; case DI_EDID_STANDARD_TIMING_16_9: return t->horiz_video * 9 / 16; } abort(); /* unreachable */ } const struct di_dmt_timing * di_edid_standard_timing_get_dmt(const struct di_edid_standard_timing *t) { int32_t vert_video; size_t i; const struct di_dmt_timing *dmt; vert_video = di_edid_standard_timing_get_vert_video(t); for (i = 0; i < _di_dmt_timings_len; i++) { dmt = &_di_dmt_timings[i]; if (dmt->horiz_video == t->horiz_video && dmt->vert_video == vert_video && dmt->refresh_rate_hz == (float)t->refresh_rate_hz && dmt->edid_std_id != 0) { return dmt; } } return 0; } const struct di_edid_standard_timing *const * di_edid_get_standard_timings(const struct di_edid *edid) { return (const struct di_edid_standard_timing *const *) &edid->standard_timings; } const struct di_edid_detailed_timing_def *const * di_edid_get_detailed_timing_defs(const struct di_edid *edid) { return (const struct di_edid_detailed_timing_def *const *) &edid->detailed_timing_defs; } const struct di_edid_display_descriptor *const * di_edid_get_display_descriptors(const struct di_edid *edid) { return (const struct di_edid_display_descriptor *const *) &edid->display_descriptors; } enum di_edid_display_descriptor_tag di_edid_display_descriptor_get_tag(const struct di_edid_display_descriptor *desc) { return desc->tag; } const char * di_edid_display_descriptor_get_string(const struct di_edid_display_descriptor *desc) { switch (desc->tag) { case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL: case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING: case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME: return desc->str; default: return NULL; } } const struct di_edid_display_range_limits * di_edid_display_descriptor_get_range_limits(const struct di_edid_display_descriptor *desc) { if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS) { return NULL; } return &desc->range_limits.base; } const struct di_edid_standard_timing *const * di_edid_display_descriptor_get_standard_timings(const struct di_edid_display_descriptor *desc) { if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS) { return NULL; } return (const struct di_edid_standard_timing *const *) desc->standard_timings; } const struct di_edid_color_point *const * di_edid_display_descriptor_get_color_points(const struct di_edid_display_descriptor *desc) { if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT) { return NULL; } return (const struct di_edid_color_point *const *) desc->color_points; } const struct di_dmt_timing *const * di_edid_display_descriptor_get_established_timings_iii(const struct di_edid_display_descriptor *desc) { if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III) { return NULL; } return desc->established_timings_iii; } const struct di_edid_color_management_data * di_edid_display_descriptor_get_color_management_data(const struct di_edid_display_descriptor *desc) { if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA) { return NULL; } return &desc->dcm_data; } const struct di_edid_cvt_timing_code *const * di_edid_display_descriptor_get_cvt_timing_codes(const struct di_edid_display_descriptor *desc) { if (desc->tag != DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES) { return NULL; } return (const struct di_edid_cvt_timing_code *const *) desc->cvt_timing_codes; } const struct di_edid_ext *const * di_edid_get_extensions(const struct di_edid *edid) { return (const struct di_edid_ext *const *) edid->exts; } enum di_edid_ext_tag di_edid_ext_get_tag(const struct di_edid_ext *ext) { return ext->tag; } const struct di_edid_cta * di_edid_ext_get_cta(const struct di_edid_ext *ext) { if (ext->tag != DI_EDID_EXT_CEA) { return NULL; } return &ext->cta; } const struct di_displayid * di_edid_ext_get_displayid(const struct di_edid_ext *ext) { if (ext->tag != DI_EDID_EXT_DISPLAYID) { return NULL; } return &ext->displayid; } libdisplay-info-0.1.1/gcovr.cfg000066400000000000000000000000501437312070200163710ustar00rootroot00000000000000exclude = subprojects/ exclude = build/ libdisplay-info-0.1.1/gtf.c000066400000000000000000000107451437312070200155300ustar00rootroot00000000000000/* * Copyright 2006-2012 Red Hat, Inc. * Copyright 2018-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Originally imported from edid-decode. */ #include #include /** * The assumed character cell granularity of the graphics system, in pixels. */ #define CELL_GRAN 8.0 /** * The size of the top and bottom overscan margin as a percentage of the active * vertical image. */ #define MARGIN_PERC 1.8 /** * The minimum front porch in lines (vertical) and character cells (horizontal). */ #define MIN_PORCH 1.0 /** * The width of the V sync in lines. */ #define V_SYNC_RQD 3.0 /** * The width of the H sync as a percentage of the total line period. */ #define H_SYNC_PERC 8.0 /** * Minimum time of vertical sync + back porch interval (µs). */ #define MIN_VSYNC_BP 550.0 void di_gtf_compute(struct di_gtf_timing *t, const struct di_gtf_options *options) { double c_prime, m_prime, h_pixels_rnd, v_lines_rnd, h_margin, v_margin, interlace, total_active_pixels, pixel_freq, h_blank_pixels, total_pixels, v_sync_bp, v_field_rate_rqd, h_period_est, total_v_lines, v_field_rate_est, h_period, ideal_duty_cycle, h_freq, ideal_h_period, v_back_porch, h_sync, h_front_porch; /* C' and M' are part of the Blanking Duty Cycle computation */ c_prime = ((options->c - options->j) * options->k / 256.0) + options->j; m_prime = options->k / 256.0 * options->m; h_pixels_rnd = round(options->h_pixels / CELL_GRAN) * CELL_GRAN; v_lines_rnd = options->int_rqd ? round(options->v_lines / 2.0) : options->v_lines; h_margin = options->margins_rqd ? round(h_pixels_rnd * MARGIN_PERC / 100.0 / CELL_GRAN) * CELL_GRAN : 0; v_margin = options->margins_rqd ? round(MARGIN_PERC / 100.0 * v_lines_rnd) : 0; interlace = options->int_rqd ? 0.5 : 0; total_active_pixels = h_pixels_rnd + h_margin * 2; switch (options->ip_param) { case DI_GTF_IP_PARAM_V_FRAME_RATE: // vertical frame frequency (Hz) v_field_rate_rqd = options->int_rqd ? options->ip_freq_rqd * 2 : options->ip_freq_rqd; h_period_est = (1.0 / v_field_rate_rqd - MIN_VSYNC_BP / 1000000.0) / (v_lines_rnd + v_margin * 2 + MIN_PORCH + interlace) * 1000000.0; v_sync_bp = round(MIN_VSYNC_BP / h_period_est); total_v_lines = v_lines_rnd + v_margin * 2 + v_sync_bp + interlace + MIN_PORCH; v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0; h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est); ideal_duty_cycle = c_prime - m_prime * h_period / 1000.0; h_blank_pixels = round(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2 * CELL_GRAN)) * 2 * CELL_GRAN; total_pixels = total_active_pixels + h_blank_pixels; pixel_freq = total_pixels / h_period; break; case DI_GTF_IP_PARAM_H_FREQ: // horizontal frequency (kHz) h_freq = options->ip_freq_rqd; v_sync_bp = round(MIN_VSYNC_BP * h_freq / 1000.0); ideal_duty_cycle = c_prime - m_prime / h_freq; h_blank_pixels = round(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2 * CELL_GRAN)) * 2 * CELL_GRAN; total_pixels = total_active_pixels + h_blank_pixels; pixel_freq = total_pixels * h_freq / 1000.0; break; case DI_GTF_IP_PARAM_H_PIXELS: // pixel clock rate (MHz) pixel_freq = options->ip_freq_rqd; ideal_h_period = (c_prime - 100.0 + sqrt((100.0 - c_prime) * (100.0 - c_prime) + 0.4 * m_prime * (total_active_pixels + h_margin * 2) / pixel_freq)) / 2.0 / m_prime * 1000.0; ideal_duty_cycle = c_prime - m_prime * ideal_h_period / 1000.0; h_blank_pixels = round(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2 * CELL_GRAN)) * 2 * CELL_GRAN; total_pixels = total_active_pixels + h_blank_pixels; h_freq = pixel_freq / total_pixels * 1000.0; v_sync_bp = round(MIN_VSYNC_BP * h_freq / 1000.0); break; } v_back_porch = v_sync_bp - V_SYNC_RQD; h_sync = round(H_SYNC_PERC / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN; h_front_porch = h_blank_pixels / 2.0 - h_sync; *t = (struct di_gtf_timing) { .h_pixels = (int) h_pixels_rnd, .v_lines = options->v_lines, .v_sync = V_SYNC_RQD, .h_sync = (int) h_sync, .v_front_porch = MIN_PORCH, .v_back_porch = (int) v_back_porch, .h_front_porch = (int) h_front_porch, .h_back_porch = (int) (h_front_porch + h_sync), .h_border = (int) h_margin, .v_border = (int) v_margin, .pixel_freq_mhz = pixel_freq, }; } libdisplay-info-0.1.1/include/000077500000000000000000000000001437312070200162205ustar00rootroot00000000000000libdisplay-info-0.1.1/include/bits.h000066400000000000000000000012471437312070200173360ustar00rootroot00000000000000#ifndef BITS_H #define BITS_H /** * Utility functions to operate on bits. */ #include #include #include #include /** * Check whether a byte has a bit set. */ static inline bool has_bit(uint8_t val, size_t index) { return val & (1 << index); } /** * Extract a bit range from a byte. * * Both offsets are inclusive, start from zero, and high must be greater than low. */ static inline uint8_t get_bit_range(uint8_t val, size_t high, size_t low) { size_t n; uint8_t bitmask; assert(high <= 7 && high >= low); n = high - low + 1; bitmask = (uint8_t) ((1 << n) - 1); return (uint8_t) (val >> low) & bitmask; } #endif libdisplay-info-0.1.1/include/cta.h000066400000000000000000000124331437312070200171430ustar00rootroot00000000000000#ifndef CTA_H #define CTA_H /** * Private header for the low-level CTA API. */ #include #include #include #include /** * The maximum number of data blocks in an EDID CTA block. * * Each data block takes at least 1 byte, the CTA block can hold 128 bytes, and * the mandatory fields take up 5 bytes (4 header bytes + checksum). */ #define EDID_CTA_MAX_DATA_BLOCKS 123 /** * The maximum number of detailed timing definitions included in an EDID CTA * block. * * The CTA extension leaves at most 122 bytes for timings, and each timing takes * 18 bytes. */ #define EDID_CTA_MAX_DETAILED_TIMING_DEFS 6 /** * The maximum number of SVD entries in a video data block. * * Each data block has its size described in a 5-bit field, so its maximum size * is 63 bytes, and each SVD uses 1 byte. */ #define EDID_CTA_MAX_VIDEO_BLOCK_ENTRIES 63 /** * The maximum number of SAD entries in an audio data block. * * Each data block has its size described in a 5-bit field, so its maximum size * is 63 bytes, and each SAD uses 3 bytes. */ #define EDID_CTA_MAX_AUDIO_BLOCK_ENTRIES 21 /** * The maximum number of Capability Bit Map entries in a YCbCr 4:2:0 video data * block. * * Each data block has its size described in a 5-bit field, so its maximum size * is 63 bytes, and each Capability Bit Map uses 1 byte. */ #define EDID_CTA_MAX_YCBCR420_CAP_MAP_BLOCK_ENTRIES 63 /** * The maximum number of Short InfoFrame Descriptor or Short Vendor-Specific * InfoFrame Descriptor entries in a InfoFrame data block. * * Each data block has its size described in a 5-bit field, so its maximum size * is 63 bytes, the header takes up at least 2 bytes and the smallest Short * InfoFrame Descriptor is 1 byte. */ #define EDID_CTA_INFOFRAME_BLOCK_ENTRIES 61 struct di_edid_cta { int revision; struct di_edid_cta_flags flags; /* NULL-terminated */ struct di_cta_data_block *data_blocks[EDID_CTA_MAX_DATA_BLOCKS + 1]; size_t data_blocks_len; /* NULL-terminated */ struct di_edid_detailed_timing_def_priv *detailed_timing_defs[EDID_CTA_MAX_DETAILED_TIMING_DEFS + 1]; size_t detailed_timing_defs_len; struct di_logger *logger; }; struct di_cta_hdr_static_metadata_block_priv { struct di_cta_hdr_static_metadata_block base; struct di_cta_hdr_static_metadata_block_eotfs eotfs; struct di_cta_hdr_static_metadata_block_descriptors descriptors; }; struct di_cta_hdr_dynamic_metadata_block_type3 { uint8_t unused; }; struct di_cta_hdr_dynamic_metadata_block_priv { struct di_cta_hdr_dynamic_metadata_block base; struct di_cta_hdr_dynamic_metadata_block_type1 type1; struct di_cta_hdr_dynamic_metadata_block_type2 type2; struct di_cta_hdr_dynamic_metadata_block_type3 type3; struct di_cta_hdr_dynamic_metadata_block_type4 type4; struct di_cta_hdr_dynamic_metadata_block_type256 type256; }; struct di_cta_video_block { /* NULL-terminated */ struct di_cta_svd *svds[EDID_CTA_MAX_VIDEO_BLOCK_ENTRIES + 1]; size_t svds_len; }; struct di_cta_sad_priv { struct di_cta_sad base; struct di_cta_sad_sample_rates supported_sample_rates; struct di_cta_sad_lpcm lpcm; struct di_cta_sad_mpegh_3d mpegh_3d; struct di_cta_sad_mpeg_aac mpeg_aac; struct di_cta_sad_mpeg_surround mpeg_surround; struct di_cta_sad_mpeg_aac_le mpeg_aac_le; struct di_cta_sad_enhanced_ac3 enhanced_ac3; struct di_cta_sad_mat mat; struct di_cta_sad_wma_pro wma_pro; }; struct di_cta_audio_block { /* NULL-terminated */ struct di_cta_sad_priv *sads[EDID_CTA_MAX_AUDIO_BLOCK_ENTRIES + 1]; size_t sads_len; }; struct di_cta_ycbcr420_cap_map { bool all; uint8_t svd_bitmap[EDID_CTA_MAX_YCBCR420_CAP_MAP_BLOCK_ENTRIES]; }; struct di_cta_infoframe_block_priv { struct di_cta_infoframe_block block; struct di_cta_infoframe_descriptor *infoframes[EDID_CTA_INFOFRAME_BLOCK_ENTRIES + 1]; size_t infoframes_len; }; struct di_cta_data_block { enum di_cta_data_block_tag tag; /* Used for DI_CTA_DATA_BLOCK_VIDEO */ struct di_cta_video_block video; /* Used for DI_CTA_DATA_BLOCK_YCBCR420 */ struct di_cta_video_block ycbcr420; /* used for DI_CTA_DATA_BLOCK_AUDIO */ struct di_cta_audio_block audio; /* Used for DI_CTA_DATA_BLOCK_SPEAKER_ALLOC */ struct di_cta_speaker_alloc_block speaker_alloc; /* Used for DI_CTA_DATA_BLOCK_VIDEO_CAP */ struct di_cta_video_cap_block video_cap; /* Used for DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE */ struct di_cta_vesa_dddb vesa_dddb; /* Used for DI_CTA_DATA_BLOCK_COLORIMETRY */ struct di_cta_colorimetry_block colorimetry; /* Used for DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA */ struct di_cta_hdr_static_metadata_block_priv hdr_static_metadata; /* Used for DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA */ struct di_cta_hdr_dynamic_metadata_block_priv hdr_dynamic_metadata; /* Used for DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC */ struct di_cta_vesa_transfer_characteristics vesa_transfer_characteristics; /* Used for DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP */ struct di_cta_ycbcr420_cap_map ycbcr420_cap_map; /* Used for DI_CTA_DATA_BLOCK_INFOFRAME */ struct di_cta_infoframe_block_priv infoframe; }; extern const struct di_cta_video_format _di_cta_video_formats[]; extern const size_t _di_cta_video_formats_len; bool _di_edid_cta_parse(struct di_edid_cta *cta, const uint8_t *data, size_t size, struct di_logger *logger); void _di_edid_cta_finish(struct di_edid_cta *cta); #endif libdisplay-info-0.1.1/include/di-edid-decode.h000066400000000000000000000011131437312070200211050ustar00rootroot00000000000000#ifndef DI_EDID_DECODE_H #define DI_EDID_DECODE_H #include struct uncommon_features { bool color_point_descriptor; bool color_management_data; bool cta_transfer_characteristics; }; extern struct uncommon_features uncommon_features; struct di_edid; struct di_edid_detailed_timing_def; struct di_edid_cta; struct di_displayid; void print_edid(const struct di_edid *edid); void print_detailed_timing_def(const struct di_edid_detailed_timing_def *def); void print_cta(const struct di_edid_cta *cta); void print_displayid(const struct di_displayid *displayid); #endif libdisplay-info-0.1.1/include/displayid.h000066400000000000000000000034131437312070200203540ustar00rootroot00000000000000#ifndef DISPLAYID_H #define DISPLAYID_H /** * Private header for the low-level DisplayID API. */ #include #include #include #include #include "log.h" /** * The maximum number of data blocks in a DisplayID section. * * A DisplayID section has a payload size of 251 bytes, and each data block has * a minimum size of 3 bytes. */ #define DISPLAYID_MAX_DATA_BLOCKS 83 /** * The maximum number of type I timings in a data block. * * A DisplayID data block has a maximum payload size of 248 bytes, and each type * I timing takes up 20 bytes. */ #define DISPLAYID_MAX_TYPE_I_TIMINGS 12 struct di_displayid { int version, revision; enum di_displayid_product_type product_type; struct di_displayid_data_block *data_blocks[DISPLAYID_MAX_DATA_BLOCKS + 1]; size_t data_blocks_len; struct di_logger *logger; }; struct di_displayid_display_params_priv { struct di_displayid_display_params base; struct di_displayid_display_params_features features; }; struct di_displayid_tiled_topo_priv { struct di_displayid_tiled_topo base; struct di_displayid_tiled_topo_caps caps; struct di_displayid_tiled_topo_bezel bezel; }; struct di_displayid_data_block { enum di_displayid_data_block_tag tag; /* Used for TYPE_I_TIMING, NULL-terminated */ struct di_displayid_type_i_timing *type_i_timings[DISPLAYID_MAX_TYPE_I_TIMINGS + 1]; size_t type_i_timings_len; /* Used for DISPLAY_PARAMS */ struct di_displayid_display_params_priv display_params; /* Used for TILED_DISPLAY_TOPO */ struct di_displayid_tiled_topo_priv tiled_topo; }; bool _di_displayid_parse(struct di_displayid *displayid, const uint8_t *data, size_t size, struct di_logger *logger); void _di_displayid_finish(struct di_displayid *displayid); #endif libdisplay-info-0.1.1/include/dmt.h000066400000000000000000000003711437312070200171560ustar00rootroot00000000000000#ifndef DMT_H #define DMT_H /** * Private header for VESA Display Monitor Timing. */ #include #include extern const struct di_dmt_timing _di_dmt_timings[]; extern const size_t _di_dmt_timings_len; #endif libdisplay-info-0.1.1/include/edid.h000066400000000000000000000115511437312070200173010ustar00rootroot00000000000000#ifndef EDID_H #define EDID_H /** * Private header for the low-level EDID API. */ #include #include #include #include #include "cta.h" #include "displayid.h" /** * The maximum number of EDID blocks (including the base block), defined in * section 2.2.1. */ #define EDID_MAX_BLOCK_COUNT 256 /** * The maximum number of EDID standard timings, defined in section 3.9. */ #define EDID_MAX_STANDARD_TIMING_COUNT 8 /** * The number of EDID byte descriptors, defined in section 3.10. */ #define EDID_BYTE_DESCRIPTOR_COUNT 4 /** * The size of an EDID byte descriptor, defined in section 3.10. */ #define EDID_BYTE_DESCRIPTOR_SIZE 18 /** * The maximum number of standard timings in an EDID display descriptor, defined * in section 3.10.3.6. */ #define EDID_MAX_DESCRIPTOR_STANDARD_TIMING_COUNT 6 /** * The maximum number of color points in an EDID color point descriptor, defined * in section 3.10.3.5. */ #define EDID_MAX_DESCRIPTOR_COLOR_POINT_COUNT 2 /** * The maximum number of established timings III in an EDID display descriptor, * defined in section 3.10.3.9. */ #define EDID_MAX_DESCRIPTOR_ESTABLISHED_TIMING_III_COUNT 44 /** * The maximum number of CVT timing codes in an EDID display descriptor, * defined in section 3.10.3.8. */ #define EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT 4 struct di_edid_detailed_timing_def_priv { struct di_edid_detailed_timing_def base; struct di_edid_detailed_timing_analog_composite analog_composite; struct di_edid_detailed_timing_bipolar_analog_composite bipolar_analog_composite; struct di_edid_detailed_timing_digital_composite digital_composite; struct di_edid_detailed_timing_digital_separate digital_separate; }; struct di_edid { struct di_edid_vendor_product vendor_product; int version, revision; bool is_digital; struct di_edid_video_input_analog video_input_analog; struct di_edid_video_input_digital video_input_digital; struct di_edid_screen_size screen_size; float gamma; struct di_edid_dpms dpms; enum di_edid_display_color_type display_color_type; struct di_edid_color_encoding_formats color_encoding_formats; struct di_edid_misc_features misc_features; struct di_edid_chromaticity_coords chromaticity_coords; struct di_edid_established_timings_i_ii established_timings_i_ii; /* NULL-terminated */ struct di_edid_standard_timing *standard_timings[EDID_MAX_STANDARD_TIMING_COUNT + 1]; size_t standard_timings_len; /* NULL-terminated */ struct di_edid_detailed_timing_def_priv *detailed_timing_defs[EDID_BYTE_DESCRIPTOR_COUNT + 1]; size_t detailed_timing_defs_len; /* NULL-terminated */ struct di_edid_display_descriptor *display_descriptors[EDID_BYTE_DESCRIPTOR_COUNT + 1]; size_t display_descriptors_len; /* NULL-terminated, doesn't include the base block */ struct di_edid_ext *exts[EDID_MAX_BLOCK_COUNT]; size_t exts_len; struct di_logger *logger; }; struct di_edid_display_range_limits_priv { struct di_edid_display_range_limits base; struct di_edid_display_range_limits_secondary_gtf secondary_gtf; struct di_edid_display_range_limits_cvt cvt; }; struct di_edid_display_descriptor { enum di_edid_display_descriptor_tag tag; /* Used for PRODUCT_SERIAL, DATA_STRING and PRODUCT_NAME, * zero-terminated */ char str[14]; /* Used for RANGE_LIMITS */ struct di_edid_display_range_limits_priv range_limits; /* Used for STD_TIMING_IDS, NULL-terminated */ struct di_edid_standard_timing *standard_timings[EDID_MAX_DESCRIPTOR_STANDARD_TIMING_COUNT + 1]; size_t standard_timings_len; /* Used for COLOR_POINT, NULL-terminated */ struct di_edid_color_point *color_points[EDID_MAX_DESCRIPTOR_COLOR_POINT_COUNT + 1]; size_t color_points_len; /* Used for ESTABLISHED_TIMINGS_III, NULL-terminated */ const struct di_dmt_timing *established_timings_iii[EDID_MAX_DESCRIPTOR_ESTABLISHED_TIMING_III_COUNT + 1]; size_t established_timings_iii_len; /* Used for DCM_DATA */ struct di_edid_color_management_data dcm_data; /* Used for DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES, NULL-terminated */ struct di_edid_cvt_timing_code *cvt_timing_codes[EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT + 1]; size_t cvt_timing_codes_len; }; struct di_edid_ext { enum di_edid_ext_tag tag; /* Used for DI_EDID_EXT_CEA */ struct di_edid_cta cta; /* Used for DI_EDID_EXT_DISPLAYID */ struct di_displayid displayid; }; /** * Create an EDID data structure. * * Callers do not need to keep the provided data pointer valid after calling * this function. Callers should destroy the returned pointer via * di_edid_destroy(). */ struct di_edid * _di_edid_parse(const void *data, size_t size, FILE *failure_msg_file); /** * Destroy an EDID data structure. */ void _di_edid_destroy(struct di_edid *edid); /** * Parse an EDID detailed timing definition. */ struct di_edid_detailed_timing_def_priv * _di_edid_parse_detailed_timing_def(const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE]); #endif libdisplay-info-0.1.1/include/info.h000066400000000000000000000002751437312070200173300ustar00rootroot00000000000000#ifndef INFO_H #define INFO_H /** * Private header for the high-level API. */ #include struct di_info { struct di_edid *edid; char *failure_msg; }; #endif libdisplay-info-0.1.1/include/libdisplay-info/000077500000000000000000000000001437312070200213055ustar00rootroot00000000000000libdisplay-info-0.1.1/include/libdisplay-info/cta.h000066400000000000000000000753771437312070200222500ustar00rootroot00000000000000#ifndef DI_CTA_H #define DI_CTA_H /** * libdisplay-info's low-level API for Consumer Technology Association * standards. * * The library implements CTA-861-H, available at: * https://shop.cta.tech/collections/standards/products/a-dtv-profile-for-uncompressed-high-speed-digital-interfaces-cta-861-h */ #include #include #include /** * CTA video format picture aspect ratio. */ enum di_cta_video_format_picture_aspect_ratio { DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, /* 4:3 */ DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, /* 16:9 */ DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, /* 64:27 */ DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, /* 256:135 */ }; /** * CTA video format sync pulse polarity. */ enum di_cta_video_format_sync_polarity { DI_CTA_VIDEO_FORMAT_SYNC_NEGATIVE, /* Negative */ DI_CTA_VIDEO_FORMAT_SYNC_POSITIVE, /* Positive */ }; /** * A CTA-861 video format, defined in section 4. */ struct di_cta_video_format { /* Video Identification Code (VIC) */ uint8_t vic; /* Horizontal/vertical active pixels/lines */ int32_t h_active, v_active; /* Horizontal/vertical front porch */ int32_t h_front, v_front; /* Horizontal/vertical sync pulse */ int32_t h_sync, v_sync; /* Horizontal/vertical back porch */ int32_t h_back, v_back; /* Horizontal/vertical sync pulse polarity */ enum di_cta_video_format_sync_polarity h_sync_polarity, v_sync_polarity; /* Pixel clock in Hz */ int64_t pixel_clock_hz; /* Whether this timing is interlaced */ bool interlaced; /* Picture aspect ratio */ enum di_cta_video_format_picture_aspect_ratio picture_aspect_ratio; }; /** * Get a CTA-861 video format from a VIC. * * Returns NULL if the VIC is unknown. */ const struct di_cta_video_format * di_cta_video_format_from_vic(uint8_t vic); /** * EDID CTA-861 extension block. */ struct di_edid_cta; /** * Get the CTA extension revision (also referred to as "version" by the * specification). */ int di_edid_cta_get_revision(const struct di_edid_cta *cta); /** * Miscellaneous EDID CTA flags, defined in section 7.3.3. * * For CTA revision 1, all of the fields are zero. */ struct di_edid_cta_flags { /* Sink underscans IT Video Formats by default */ bool it_underscan; /* Sink supports Basic Audio */ bool basic_audio; /* Sink supports YCbCr 4:4:4 in addition to RGB */ bool ycc444; /* Sink supports YCbCr 4:2:2 in addition to RGB */ bool ycc422; /* Total number of native detailed timing descriptors */ int native_dtds; }; /** * Get miscellaneous CTA flags. */ const struct di_edid_cta_flags * di_edid_cta_get_flags(const struct di_edid_cta *cta); /** * CTA data block, defined in section 7.4. */ struct di_cta_data_block; /** * Get CTA data blocks. * * The returned array is NULL-terminated. */ const struct di_cta_data_block *const * di_edid_cta_get_data_blocks(const struct di_edid_cta *cta); /** * CTA data block tag. * * Note, the enum values don't match the specification. */ enum di_cta_data_block_tag { /* Audio Data Block */ DI_CTA_DATA_BLOCK_AUDIO = 1, /* Video Data Block */ DI_CTA_DATA_BLOCK_VIDEO, /* Speaker Allocation Data Block */ DI_CTA_DATA_BLOCK_SPEAKER_ALLOC, /* VESA Display Transfer Characteristic Data Block */ DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC, /* Video Format Data Block */ DI_CTA_DATA_BLOCK_VIDEO_FORMAT, /* Video Capability Data Block */ DI_CTA_DATA_BLOCK_VIDEO_CAP, /* VESA Display Device Data Block */ DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE, /* Colorimetry Data Block */ DI_CTA_DATA_BLOCK_COLORIMETRY, /* HDR Static Metadata Data Block */ DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA, /* HDR Dynamic Metadata Data Block */ DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA, /* Native Video Resolution Data Block */ DI_CTA_DATA_BLOCK_NATIVE_VIDEO_RESOLUTION, /* Video Format Preference Data Block */ DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF, /* YCbCr 4:2:0 Video Data Block */ DI_CTA_DATA_BLOCK_YCBCR420, /* YCbCr 4:2:0 Capability Map Data Block */ DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP, /* HDMI Audio Data Block */ DI_CTA_DATA_BLOCK_HDMI_AUDIO, /* Room Configuration Data Block */ DI_CTA_DATA_BLOCK_ROOM_CONFIG, /* Speaker Location Data Block */ DI_CTA_DATA_BLOCK_SPEAKER_LOCATION, /* InfoFrame Data Block */ DI_CTA_DATA_BLOCK_INFOFRAME, /* DisplayID Type VII Video Timing Data Block */ DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII, /* DisplayID Type VIII Video Timing Data Block */ DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VIII, /* DisplayID Type X Video Timing Data Block */ DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_X, /* HDMI Forum EDID Extension Override Data Block */ DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE, /* HDMI Forum Sink Capability Data Block */ DI_CTA_DATA_BLOCK_HDMI_SINK_CAP, }; /** * Get the tag of the CTA data block. */ enum di_cta_data_block_tag di_cta_data_block_get_tag(const struct di_cta_data_block *block); /** * Audio formats, defined in tables 37 and 39. * * Note, the enum values don't match the specification. */ enum di_cta_audio_format { /* L-PCM */ DI_CTA_AUDIO_FORMAT_LPCM = 1, /* AC-3 */ DI_CTA_AUDIO_FORMAT_AC3, /* MPEG-1 (layers 1 & 2) */ DI_CTA_AUDIO_FORMAT_MPEG1, /* MP3 */ DI_CTA_AUDIO_FORMAT_MP3, /* MPEG-2 */ DI_CTA_AUDIO_FORMAT_MPEG2, /* AAC LC */ DI_CTA_AUDIO_FORMAT_AAC_LC, /* DTS */ DI_CTA_AUDIO_FORMAT_DTS, /* ATRAC */ DI_CTA_AUDIO_FORMAT_ATRAC, /* One Bit Audio */ DI_CTA_AUDIO_FORMAT_ONE_BIT_AUDIO, /* Enhanced AC-3 */ DI_CTA_AUDIO_FORMAT_ENHANCED_AC3, /* DTS-HD and DTS-UHD */ DI_CTA_AUDIO_FORMAT_DTS_HD, /* MAT */ DI_CTA_AUDIO_FORMAT_MAT, /* DST */ DI_CTA_AUDIO_FORMAT_DST, /* WMA Pro */ DI_CTA_AUDIO_FORMAT_WMA_PRO, /* MPEG-4 HE AAC */ DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC, /* MPEG-4 HE AAC v2 */ DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2, /* MPEG-4 AAC LC */ DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC, /* DRA */ DI_CTA_AUDIO_FORMAT_DRA, /* MPEG-4 HE AAC + MPEG Surround */ DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND, /* MPEG-4 AAC LC + MPEG Surround */ DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND, /* MPEG-H 3D Audio */ DI_CTA_AUDIO_FORMAT_MPEGH_3D, /* AC-4 */ DI_CTA_AUDIO_FORMAT_AC4, /* L-PCM 3D Audio */ DI_CTA_AUDIO_FORMAT_LPCM_3D, }; struct di_cta_sad_sample_rates { bool has_192_khz; /* 192 kHz */ bool has_176_4_khz; /* 176.4 kHz */ bool has_96_khz; /* 96 kHz */ bool has_88_2_khz; /* 88.2 kHz */ bool has_48_khz; /* 48 kHz */ bool has_44_1_khz; /* 44.1 kHz */ bool has_32_khz; /* 32 kHz */ }; enum di_cta_sad_mpegh_3d_level { DI_CTA_SAD_MPEGH_3D_LEVEL_UNSPECIFIED = 0, DI_CTA_SAD_MPEGH_3D_LEVEL_1 = 1, DI_CTA_SAD_MPEGH_3D_LEVEL_2 = 2, DI_CTA_SAD_MPEGH_3D_LEVEL_3 = 3, DI_CTA_SAD_MPEGH_3D_LEVEL_4 = 4, DI_CTA_SAD_MPEGH_3D_LEVEL_5 = 5, }; struct di_cta_sad_mpegh_3d { /* Maximum supported MPEG-H 3D level, zero if unspecified */ enum di_cta_sad_mpegh_3d_level level; /* True if MPEG-H 3D Audio Low Complexity Profile is supported */ bool low_complexity_profile; /* True if MPEG-H 3D Audio Baseline Profile is supported */ bool baseline_profile; }; struct di_cta_sad_mpeg_aac { /* True if AAC audio frame lengths of 960 samples is supported */ bool has_frame_length_960; /* True if AAC audio frame lengths of 1024 samples is supported */ bool has_frame_length_1024; }; enum di_cta_sad_mpeg_surround_signaling { /* Only implicitly signaled MPEG Surround data supported */ DI_CTA_SAD_MPEG_SURROUND_SIGNALING_IMPLICIT = 0, /* Implicitly and explicitly signaled MPEG Surround data supported */ DI_CTA_SAD_MPEG_SURROUND_SIGNALING_IMPLICIT_AND_EXPLICIT = 1, }; struct di_cta_sad_mpeg_surround { /* MPEG Surround signaling */ enum di_cta_sad_mpeg_surround_signaling signaling; }; struct di_cta_sad_mpeg_aac_le { /* True if Rec. ITU-R BS.2051 System H 22.2 multichannel sound is supported */ bool supports_multichannel_sound; }; struct di_cta_sad_lpcm { bool has_sample_size_24_bits; /* 24 bits */ bool has_sample_size_20_bits; /* 20 bits */ bool has_sample_size_16_bits; /* 16 bits */ }; struct di_cta_sad_enhanced_ac3 { bool supports_joint_object_coding; bool supports_joint_object_coding_ACMOD28; }; struct di_cta_sad_mat { bool supports_object_audio_and_channel_based; bool requires_hash_calculation; }; struct di_cta_sad_wma_pro { int profile; }; /** * A CTA short audio descriptor (SAD), defined in section 7.5.2. */ struct di_cta_sad { /* Format */ enum di_cta_audio_format format; /* Maximum number of channels, zero if unset */ int32_t max_channels; /* Supported sample rates */ const struct di_cta_sad_sample_rates *supported_sample_rates; /* Maximum bitrate (kb/s), zero if unset */ int32_t max_bitrate_kbs; /* Additional metadata for LPCM, NULL unless format is * DI_CTA_AUDIO_FORMAT_LPCM or DI_CTA_AUDIO_FORMAT_LPCM_3D */ const struct di_cta_sad_lpcm *lpcm; /* Additional metadata for MPEG-H 3D Audio, NULL unless format is * DI_CTA_AUDIO_FORMAT_MPEGH_3D */ const struct di_cta_sad_mpegh_3d *mpegh_3d; /* Additional metadata for MPEG4 AAC, NULL unless format is * DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC or * DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_V2 or * DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC or * DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND or * DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND */ const struct di_cta_sad_mpeg_aac *mpeg_aac; /* Additional metadata for MPEG4 Surround, NULL unless format is * DI_CTA_AUDIO_FORMAT_MPEG4_HE_AAC_MPEG_SURROUND or * DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC_MPEG_SURROUND */ const struct di_cta_sad_mpeg_surround *mpeg_surround; /* Additional metadata for MPEG4 AAC LC, NULL unless format is * DI_CTA_AUDIO_FORMAT_MPEG4_AAC_LC */ const struct di_cta_sad_mpeg_aac_le *mpeg_aac_le; /* Additional metadata for Enhanced AC-3, NULL unless format is * DI_CTA_AUDIO_FORMAT_ENHANCED_AC3 */ const struct di_cta_sad_enhanced_ac3 *enhanced_ac3; /* Additional metadata for Dolby MAT, NULL unless format is * DI_CTA_AUDIO_FORMAT_MAT */ const struct di_cta_sad_mat *mat; /* Additional metadata for WMA Pro, NULL unless format is * DI_CTA_AUDIO_FORMAT_WMA_PRO */ const struct di_cta_sad_wma_pro *wma_pro; }; /** * Get an array of short audio descriptors from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_AUDIO. * * The returned array is NULL-terminated. */ const struct di_cta_sad *const * di_cta_data_block_get_sads(const struct di_cta_data_block *data_block); /** * Speaker allocation data block (SADB), defined in section 7.5.3. * * This block indicates which speakers are present. See figure 6 for the meaning * of the fields. */ struct di_cta_speaker_alloc_block { bool flw_frw; /* FLw/FRw - Front Left/Right Wide */ bool flc_frc; /* FLc/FRc - Front Left/Right of Center */ bool bc; /* BC - Back Center */ bool bl_br; /* BL/BR - Back Left/Right */ bool fc; /* FC - Front Center */ bool lfe1; /* LFE1 - Low Frequency Effects 1 */ bool fl_fr; /* FL/FR - Front Left/Right */ bool tpsil_tpsir; /* TpSiL/TpSiR - Top Side Left/Right */ bool sil_sir; /* SiL/SiR - Side Left/Right */ bool tpbc; /* TpBC - Top Back Center */ bool lfe2; /* LFE2 - Low Frequency Effects 2 */ bool ls_rs; /* LS/RS - Left/Right Surround*/ bool tpfc; /* TpFC - Top Front Center */ bool tpc; /* TpC - Top Center*/ bool tpfl_tpfr; /* TpFL/TpFR - Top Front Left/Right */ bool btfl_btfr; /* BtFL/BtFR - Bottom Front Left/Right */ bool btfc; /* BtFC - Bottom Front Center */ bool tpbl_tpbr; /* TpBL/TpBR - Top Back Left/Right */ }; /** * Get the speaker allocation from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_SPEAKER_ALLOC. */ const struct di_cta_speaker_alloc_block * di_cta_data_block_get_speaker_alloc(const struct di_cta_data_block *block); /** * Over- and underscan capability. */ enum di_cta_video_cap_over_underscan { /* No data */ DI_CTA_VIDEO_CAP_UNKNOWN_OVER_UNDERSCAN = 0x00, /* Always overscanned */ DI_CTA_VIDEO_CAP_ALWAYS_OVERSCAN = 0x01, /* Always underscanned */ DI_CTA_VIDEO_CAP_ALWAYS_UNDERSCAN = 0x02, /* Supports both over- and underscan */ DI_CTA_VIDEO_CAP_BOTH_OVER_UNDERSCAN = 0x03, }; /** * Video capability data block (VCDB), defined in section 7.5.6. */ struct di_cta_video_cap_block { /* If set to true, YCC quantization range is selectable (via AVI YQ). */ bool selectable_ycc_quantization_range; /* If set to true, RGB quantization range is selectable (via AVI Q). */ bool selectable_rgb_quantization_range; /* Overscan/underscan behavior for PT video formats (if set to unknown, * use the IT/CE behavior) */ enum di_cta_video_cap_over_underscan pt_over_underscan; /* Overscan/underscan behavior for IT video formats */ enum di_cta_video_cap_over_underscan it_over_underscan; /* Overscan/underscan behavior for CE video formats */ enum di_cta_video_cap_over_underscan ce_over_underscan; }; /** * Get the video capabilities from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_VIDEO_CAP. */ const struct di_cta_video_cap_block * di_cta_data_block_get_video_cap(const struct di_cta_data_block *block); /** * Interface types, defined in VESA DDDB section 2.3.1 and 2.3.2. * * Note, the enum values don't match the specification. */ enum di_cta_vesa_dddb_interface_type { DI_CTA_VESA_DDDB_INTERFACE_VGA, /* 15HD/VGA */ DI_CTA_VESA_DDDB_INTERFACE_NAVI_V, /* VESA NAVI-V */ DI_CTA_VESA_DDDB_INTERFACE_NAVI_D, /* VESA NAVI-D */ DI_CTA_VESA_DDDB_INTERFACE_LVDS, /* LVDS */ DI_CTA_VESA_DDDB_INTERFACE_RSDS, /* RSDS */ DI_CTA_VESA_DDDB_INTERFACE_DVI_D, /* DVI-D */ DI_CTA_VESA_DDDB_INTERFACE_DVI_I_ANALOG, /* DVI-I analog */ DI_CTA_VESA_DDDB_INTERFACE_DVI_I_DIGITAL, /* DVI-I digital */ DI_CTA_VESA_DDDB_INTERFACE_HDMI_A, /* HDMI-A */ DI_CTA_VESA_DDDB_INTERFACE_HDMI_B, /* HDMI-B */ DI_CTA_VESA_DDDB_INTERFACE_MDDI, /* MDDI */ DI_CTA_VESA_DDDB_INTERFACE_DISPLAYPORT, /* DisplayPort */ DI_CTA_VESA_DDDB_INTERFACE_IEEE_1394, /* IEEE-1394 */ DI_CTA_VESA_DDDB_INTERFACE_M1_ANALOG, /* M1 analog */ DI_CTA_VESA_DDDB_INTERFACE_M1_DIGITAL, /* M1 digital */ }; enum di_cta_vesa_dddb_content_protection { DI_CTA_VESA_DDDB_CONTENT_PROTECTION_NONE = 0x00, /* None */ DI_CTA_VESA_DDDB_CONTENT_PROTECTION_HDCP = 0x01, /* HDCP */ DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DTCP = 0x02, /* DTCP */ DI_CTA_VESA_DDDB_CONTENT_PROTECTION_DPCP = 0x03, /* DPCP */ }; enum di_cta_vesa_dddb_default_orientation { DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_LANDSCAPE = 0, /* Landscape */ DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_PORTAIT = 1, /* Portrait */ DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_UNFIXED = 2, /* Not fixed, may be rotated by the user */ DI_CTA_VESA_DDDB_DEFAULT_ORIENTATION_UNDEFINED = 3, /* Undefined */ }; enum di_cta_vesa_dddb_rotation_cap { DI_CTA_VESA_DDDB_ROTATION_CAP_NONE = 0, /* No rotation capability */ DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_CLOCKWISE = 1, /* 90 degrees clockwise */ DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_COUNTERCLOCKWISE = 2, /* 90 degrees counterclockwise */ DI_CTA_VESA_DDDB_ROTATION_CAP_90DEG_EITHER = 3, /* 90 degrees in either direction */ }; enum di_cta_vesa_dddb_zero_pixel_location { DI_CTA_VESA_DDDB_ZERO_PIXEL_UPPER_LEFT = 0, /* Upper left corner */ DI_CTA_VESA_DDDB_ZERO_PIXEL_UPPER_RIGHT = 1, /* Upper right corner */ DI_CTA_VESA_DDDB_ZERO_PIXEL_LOWER_LEFT = 2, /* Lower left corner */ DI_CTA_VESA_DDDB_ZERO_PIXEL_LOWER_RIGHT = 3, /* Lower right corner */ }; enum di_cta_vesa_dddb_scan_direction { /* Undefined */ DI_CTA_VESA_DDDB_SCAN_DIRECTION_UNDEFINED = 0, /* Fast (line) scan is along the long axis, slow (frame or field) scan * is along the short axis */ DI_CTA_VESA_DDDB_SCAN_DIRECTION_FAST_LONG_SLOW_SHORT = 1, /* Fast (line) scan is along the short axis, slow (frame or field) scan * is along the long axis */ DI_CTA_VESA_DDDB_SCAN_DIRECTION_FAST_SHORT_SLOW_LONG = 2, }; /** * Subpixel layout, defined in VESA DDDB section 2.9. * * For layouts with more than 3 subpixels, the color coordinates of the * additional subpixels are defined in the additional primary chromaticities. */ enum di_cta_vesa_dddb_subpixel_layout { /* Undefined */ DI_CTA_VESA_DDDB_SUBPIXEL_UNDEFINED = 0x00, /* Red, green, blue vertical stripes */ DI_CTA_VESA_DDDB_SUBPIXEL_RGB_VERT = 0x01, /* Red, green, blue horizontal stripes */ DI_CTA_VESA_DDDB_SUBPIXEL_RGB_HORIZ = 0x02, /* Vertical stripes with the primary ordering given by the order of the * chromaticity information in the base EDID */ DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_VERT = 0x03, /* Horizontal stripes with the primary ordering given by the order of * the chromaticity information in the base EDID */ DI_CTA_VESA_DDDB_SUBPIXEL_EDID_CHROM_HORIZ = 0x04, /* Quad subpixels: * R G * G B */ DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_RGGB = 0x05, /* Quad subpixels: * G B * R G */ DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_GBRG = 0x06, /* Delta (triad) RGB subpixels */ DI_CTA_VESA_DDDB_SUBPIXEL_DELTA_RGB = 0x07, /* Mosaic */ DI_CTA_VESA_DDDB_SUBPIXEL_MOSAIC = 0x08, /* Quad subpixels: one each of red, green, blue, and one additional * color (including white) in any order */ DI_CTA_VESA_DDDB_SUBPIXEL_QUAD_ANY = 0x09, /* Five subpixels, including RGB subpixels aligned as in the case of * DI_CTA_VESA_DDDB_SUBPIXEL_RGB_VERT, with two additional subpixels * located above or below this group */ DI_CTA_VESA_DDDB_SUBPIXEL_FIVE = 0x0A, /* Six subpixels, including RGB subpixels aligned as in the case of * DI_CTA_VESA_DDDB_SUBPIXEL_RGB_VERT, with three additional subpixels * located above or below this group */ DI_CTA_VESA_DDDB_SUBPIXEL_SIX = 0x0B, /* Clairvoyante, Inc. PenTile Matrix™ layout */ DI_CTA_VESA_DDDB_SUBPIXEL_CLAIRVOYANTE_PENTILE = 0x0C, }; enum di_cta_vesa_dddb_dithering_type { DI_CTA_VESA_DDDB_DITHERING_NONE = 0, /* None */ DI_CTA_VESA_DDDB_DITHERING_SPACIAL = 1, /* Spacial */ DI_CTA_VESA_DDDB_DITHERING_TEMPORAL = 2, /* Temporal */ DI_CTA_VESA_DDDB_DITHERING_SPATIAL_AND_TEMPORAL = 3, /* Spacial and temporal */ }; struct di_cta_vesa_dddb_additional_primary_chromaticity { float x, y; }; enum di_cta_vesa_dddb_frame_rate_conversion { /* No dedicated rate conversion hardware is provided */ DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_NONE = 0, /* Frame rate conversion is supported, tearing or other artifacts may * be visible */ DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_SINGLE_BUFFERING = 1, /* Frame rate conversion is supported, input frames may be duplicated or * dropped */ DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_DOUBLE_BUFFERING = 2, /* Frame rate conversion is supported via a more advanced technique * (e.g. inter-frame interpolation) */ DI_CTA_VESA_DDDB_FRAME_RATE_CONVERSION_ADVANCED = 3, }; enum di_cta_vesa_dddb_resp_time_transition { DI_CTA_VESA_DDDB_RESP_TIME_BLACK_TO_WHITE = 0, /* Black to white */ DI_CTA_VESA_DDDB_RESP_TIME_WHITE_TO_BLACK = 1, /* White to black */ }; /** * VESA Display Device Data Block (DDDB), defined in VESA Display Device Data * Block (DDDB) Standard version 1. */ struct di_cta_vesa_dddb { /* Interface type */ enum di_cta_vesa_dddb_interface_type interface_type; /* Number of lanes/channels, zero if N/A */ int32_t num_channels; /* Interface standard version and release number */ int32_t interface_version, interface_release; /* Content protection support */ enum di_cta_vesa_dddb_content_protection content_protection; /* Minimum and maximum clock frequency (in mega-hertz), zero if unset * (ie. maximum and minimum as permitted under the appropriate interface * specification or standard) */ int32_t min_clock_freq_mhz, max_clock_freq_mhz; /* Device native pixel format, zero if unset */ int32_t native_horiz_pixels, native_vert_pixels; /* Aspect ratio taken as long axis divided by short axis */ float aspect_ratio; /* Default orientation */ enum di_cta_vesa_dddb_default_orientation default_orientation; /* Rotation capability */ enum di_cta_vesa_dddb_rotation_cap rotation_cap; /* Zero pixel location */ enum di_cta_vesa_dddb_zero_pixel_location zero_pixel_location; /* Scan direction */ enum di_cta_vesa_dddb_scan_direction scan_direction; /* Subpixel layout */ enum di_cta_vesa_dddb_subpixel_layout subpixel_layout; /* Horizontal and vertical dot/pixel pitch (in millimeters) */ float horiz_pitch_mm, vert_pitch_mm; /* Dithering type */ enum di_cta_vesa_dddb_dithering_type dithering_type; /* Direct drive: no scaling, de-interlacing, frame-rate conversion, etc. * between this interface and the panel or other display device */ bool direct_drive; /* Overdrive not recommended: the source should not apply "overdrive" to * the video signal */ bool overdrive_not_recommended; /* Display can deinterlaced video input */ bool deinterlacing; /* Video interface supports audio */ bool audio_support; /* Audio inputs are provided separately from the video interface */ bool separate_audio_inputs; /* Audio information received via the video interface will automatically * override any other audio input channels provided */ bool audio_input_override; /* True if audio delay information is provided */ bool audio_delay_provided; /* Audio delay (in milliseconds, may be negative), with the maximum * absolute value of 254 ms */ int32_t audio_delay_ms; /* Frame rate/mode conversion */ enum di_cta_vesa_dddb_frame_rate_conversion frame_rate_conversion; /* Frame rate range (in Hz), with the maximum value of 63 Hz */ int32_t frame_rate_range_hz; /* Native/nominal rate (in Hz) */ int32_t frame_rate_native_hz; /* Color bit depth for the interface and display device */ int32_t bit_depth_interface, bit_depth_display; /* Number of additional primary color chromaticities */ size_t additional_primary_chromaticities_len; /* Additional primary color chromaticities given as 1931 CIE xy color * space coordinates (also defines the color of subpixels for some * subpixel layouts) */ struct di_cta_vesa_dddb_additional_primary_chromaticity additional_primary_chromaticities[3]; /* Response time transition */ enum di_cta_vesa_dddb_resp_time_transition resp_time_transition; /* Response time (in milliseconds), with the maximum value of 127 ms */ int32_t resp_time_ms; /* Overscan horizontal and vertical percentage */ int32_t overscan_horiz_pct, overscan_vert_pct; }; /** * Get the VESA Display Device Data Block (DDDB) from a CTA data block. * * Returns NULL if the data block tag is not * DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE. */ const struct di_cta_vesa_dddb * di_cta_data_block_get_vesa_dddb(const struct di_cta_data_block *block); /** * CTA colorimetry data block, defined in section 7.5.5. */ struct di_cta_colorimetry_block { /* Standard Definition Colorimetry based on IEC 61966-2-4 */ bool xvycc_601; /* High Definition Colorimetry based on IEC 61966-2-4 */ bool xvycc_709; /* Colorimetry based on IEC 61966-2-1/Amendment 1 */ bool sycc_601; /* Colorimetry based on IEC 61966-2-5, Annex A */ bool opycc_601; /* Colorimetry based on IEC 61966-2-5 */ bool oprgb; /* Colorimetry based on Rec. ITU-R BT.2020 Y'cC'bcC'rc */ bool bt2020_cycc; /* Colorimetry based on Rec. ITU-R BT.2020 Y'C'bC'r */ bool bt2020_ycc; /* Colorimetry based on Rec. ITU-R BT.2020 R'G'B' */ bool bt2020_rgb; /* Colorimetry based on SMPTE ST 2113 R'G'B' */ bool st2113_rgb; /* Colorimetry based on Rec. ITU-R BT.2100 ICtCp */ bool ictcp; }; /** * Get the colorimetry data from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_COLORIMETRY. */ const struct di_cta_colorimetry_block * di_cta_data_block_get_colorimetry(const struct di_cta_data_block *block); /** * Supported Electro-Optical Transfer Functions for a CTA HDR static metadata * block. */ struct di_cta_hdr_static_metadata_block_eotfs { /* Traditional gamma - SDR luminance range */ bool traditional_sdr; /* Traditional gamma - HDR luminance range */ bool traditional_hdr; /* Perceptual Quantization (PQ) based on SMPTE ST 2084 */ bool pq; /* Hybrid Log-Gamma (HLG) based on Rec. ITU-R BT.2100 */ bool hlg; }; /** * Supported static metadata descriptors for a CTA HDR static metadata block. */ struct di_cta_hdr_static_metadata_block_descriptors { /* Static Metadata Type 1 */ bool type1; }; /** * CTA HDR static metadata block, defined in section 7.5.13. */ struct di_cta_hdr_static_metadata_block { /* Desired content max luminance (cd/m²), zero if unset */ float desired_content_max_luminance; /* Desired content max frame-average luminance (cd/m²), zero if unset */ float desired_content_max_frame_avg_luminance; /* Desired content min luminance (cd/m²), zero if unset */ float desired_content_min_luminance; /* Supported EOFTs */ const struct di_cta_hdr_static_metadata_block_eotfs *eotfs; /* Supported descriptors */ const struct di_cta_hdr_static_metadata_block_descriptors *descriptors; }; /** * Get the HDR static metadata from a CTA data block. * * Returns NULL if the data block tag is not * DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA. */ const struct di_cta_hdr_static_metadata_block * di_cta_data_block_get_hdr_static_metadata(const struct di_cta_data_block *block); /* Additional HDR Dynamic Metadata Type 1 information */ struct di_cta_hdr_dynamic_metadata_block_type1 { uint8_t type_1_hdr_metadata_version; }; /* Additional HDR Dynamic Metadata Type 2 (ETSI TS 103 433-1) information. * Defined in ETSI TS 103 433-1 Annex G.2 HDR Dynamic Metadata Data Block. */ struct di_cta_hdr_dynamic_metadata_block_type2 { uint8_t ts_103_433_spec_version; bool ts_103_433_1_capable; bool ts_103_433_2_capable; bool ts_103_433_3_capable; }; /* Additional HDR Dynamic Metadata Type 3 information */ struct di_cta_hdr_dynamic_metadata_block_type3; /* Additional HDR Dynamic Metadata Type 4 information */ struct di_cta_hdr_dynamic_metadata_block_type4 { uint8_t type_4_hdr_metadata_version; }; /* Additional HDR Dynamic Metadata Type 256 information */ struct di_cta_hdr_dynamic_metadata_block_type256 { uint8_t graphics_overlay_flag_version; }; /** * CTA HDR dynamic metadata block, defined in section 7.5.14. */ struct di_cta_hdr_dynamic_metadata_block { /* non-NULL if Dynamic Metadata Type 1 is supported. */ const struct di_cta_hdr_dynamic_metadata_block_type1 *type1; /* non-NULL if Dynamic Metadata Type 2 is supported. */ const struct di_cta_hdr_dynamic_metadata_block_type2 *type2; /* non-NULL if Dynamic Metadata Type 3 is supported. */ const struct di_cta_hdr_dynamic_metadata_block_type3 *type3; /* non-NULL if Dynamic Metadata Type 4 is supported. */ const struct di_cta_hdr_dynamic_metadata_block_type4 *type4; /* non-NULL if Dynamic Metadata Type 256 (0x0100) is supported. */ const struct di_cta_hdr_dynamic_metadata_block_type256 *type256; }; /** * Get the HDR dynamic metadata from a CTA data block. * * Returns NULL if the data block tag is not * DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA. */ const struct di_cta_hdr_dynamic_metadata_block * di_cta_data_block_get_hdr_dynamic_metadata(const struct di_cta_data_block *block); /** * A Short Video Descriptor (SVD). */ struct di_cta_svd { /* Video Identification Code (VIC) */ uint8_t vic; /* Whether this is a native video format */ bool native; }; /** * Get an array of short video descriptors from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_VIDEO. * * The returned array is NULL-terminated. */ const struct di_cta_svd *const * di_cta_data_block_get_svds(const struct di_cta_data_block *block); /** * Get an array of short video descriptors which only allow YCbCr 4:2:0 sampling * mode from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_YCBCR420. * * The returned array is NULL-terminated. */ const struct di_cta_svd *const * di_cta_data_block_get_ycbcr420_svds(const struct di_cta_data_block *block); enum di_cta_vesa_transfer_characteristics_usage { /* White transfer characteristic */ DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_WHITE = 0, /* Red transfer characteristic */ DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_RED = 1, /* Green transfer characteristic */ DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_GREEN = 2, /* Blue transfer characteristic */ DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_BLUE = 3, }; /** * VESA Display Transfer Characteristic Data Block, defined in VESA Display * Transfer Characteristics Data Block Standard Version 1.0 * * Contains 8, 16 or 32 evenly distributed points on the input axis describing * the normalized relative luminance at that input. The first value includes the * relative black level luminance. */ struct di_cta_vesa_transfer_characteristics { enum di_cta_vesa_transfer_characteristics_usage usage; uint8_t points_len; float points[32]; }; /** * Get the Display Transfer Characteristic from a CTA data block. * * Returns NULL if the data block tag is not * DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC. * * Upstream is not aware of any EDID blob containing a Display Transfer * Characteristic data block. * If such a blob is found, please share it with upstream! */ const struct di_cta_vesa_transfer_characteristics * di_cta_data_block_get_vesa_transfer_characteristics(const struct di_cta_data_block *block); /** * CTA YCbCr 4:2:0 Capability Map block, defined in section 7.5.11. */ struct di_cta_ycbcr420_cap_map; /** * Returns true if the SVD in regular Video Data Blocks at index `svd_index` * supports YCbCr 4:2:0 subsampling. */ bool di_cta_ycbcr420_cap_map_supported(const struct di_cta_ycbcr420_cap_map *cap_map, size_t svd_index); /** * Get the YCbCr 4:2:0 Capability Map from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP. */ const struct di_cta_ycbcr420_cap_map * di_cta_data_block_get_ycbcr420_cap_map(const struct di_cta_data_block *block); /** * InfoFrame types, defined in table 7. * * Note, the enum values don't match the specification. */ enum di_cta_infoframe_type { /* Auxiliary Video Information, defined in section 6.4. */ DI_CTA_INFOFRAME_TYPE_AUXILIARY_VIDEO_INFORMATION, /* Source Product Description, defined in section 6.5. */ DI_CTA_INFOFRAME_TYPE_SOURCE_PRODUCT_DESCRIPTION, /* Audio, defined in section 6.6. */ DI_CTA_INFOFRAME_TYPE_AUDIO, /* MPEG Source, defined in section 6.7. */ DI_CTA_INFOFRAME_TYPE_MPEG_SOURCE, /* NTSC VBI, defined in section 6.8. */ DI_CTA_INFOFRAME_TYPE_NTSC_VBI, /* Dynamic Range and Mastering, defined in section 6.9. */ DI_CTA_INFOFRAME_TYPE_DYNAMIC_RANGE_AND_MASTERING, }; /** * CTA InfoFrame descriptor, defined in section 7.5.9. */ struct di_cta_infoframe_descriptor { /* Type of InfoFrame */ enum di_cta_infoframe_type type; }; /** * CTA InfoFrame processing, defined in section 7.5.9. */ struct di_cta_infoframe_block { /* Number of Vendor-specific InfoFrames that can be received * simultaneously */ int num_simultaneous_vsifs; /* Supported InfoFrames. The array is NULL-terminated. */ const struct di_cta_infoframe_descriptor *const *infoframes; }; /** * Get the InfoFrame information from a CTA data block. * * Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_INFOFRAME. */ const struct di_cta_infoframe_block * di_cta_data_block_get_infoframe(const struct di_cta_data_block *block); /** * Get a list of EDID detailed timing definitions. * * The returned array is NULL-terminated. */ const struct di_edid_detailed_timing_def *const * di_edid_cta_get_detailed_timing_defs(const struct di_edid_cta *cta); #endif libdisplay-info-0.1.1/include/libdisplay-info/cvt.h000066400000000000000000000042601437312070200222540ustar00rootroot00000000000000#ifndef DI_CVT_H #define DI_CVT_H /** * Low-level API for VESA Coordinated Video Timings (CVT) version 2.0. */ #include #include enum di_cvt_reduced_blanking_version { DI_CVT_REDUCED_BLANKING_NONE, DI_CVT_REDUCED_BLANKING_V1, DI_CVT_REDUCED_BLANKING_V2, DI_CVT_REDUCED_BLANKING_V3, }; /** * Input parameters, defined in table 3-1. */ struct di_cvt_options { /* Version of the reduced blanking timing formula to be used */ enum di_cvt_reduced_blanking_version red_blank_ver; /* Desired active (visible) horizontal pixels and vertical lines per * frame */ int32_t h_pixels, v_lines; /* Target vertical refresh rate (in Hz) */ double ip_freq_rqd; /* Whether to generate a "video-optimized" timing variant (RBv2 only) */ bool video_opt; /* Desired VBlank time (in µs, RBv3 only, must be greater than 460) */ double vblank; /* Desired additional number of pixels to add to the base HBlank * duration (RBv3 only, must be a multiple of 8 between 0 and 120) */ int32_t additional_hblank; /* Indicates whether the VSync location is early (RBv3 only) */ bool early_vsync_rqd; /* Whether interlaced is required (no RB and RBv1 only) */ bool int_rqd; /* Whether margins are required (no RB and RBv1 only) */ bool margins_rqd; }; /** * Output parameters, defined in table 3-4. */ struct di_cvt_timing { /* Pixel clock frequency (in MHz) */ double act_pixel_freq; /* Total number of active (visible) pixels per line */ double total_active_pixels; /* Total number of active (visible) vertical lines per frame */ double v_lines_rnd; /* Number of pixels in the horizontal front porch period */ double h_front_porch; /* Number of pixels in the HSync period */ double h_sync; /* Number of pixels in the horizontal back porch period */ double h_back_porch; /* Number of lines in the vertical front porch period */ double v_front_porch; /* Number of lines in the VSync period */ double v_sync; /* Number of lines in the vertical back porch period */ double v_back_porch; /* Frame rate (in Hz) */ double act_frame_rate; }; /** * Compute a timing via the CVT formula. */ void di_cvt_compute(struct di_cvt_timing *t, const struct di_cvt_options *options); #endif libdisplay-info-0.1.1/include/libdisplay-info/displayid.h000066400000000000000000000235731437312070200234520ustar00rootroot00000000000000#ifndef DI_DISPLAYID_H #define DI_DISPLAYID_H /** * libdisplay-info's low-level API for VESA Display Identification Data * (DisplayID). * * The library implements DisplayID version 1.3, available at: * https://vesa.org/vesa-standards/ */ #include #include /** * DisplayID data structure. */ struct di_displayid; /** * Get the DisplayID version. */ int di_displayid_get_version(const struct di_displayid *displayid); /** * Get the DisplayID revision. */ int di_displayid_get_revision(const struct di_displayid *displayid); /** * Product type identifier, defined in section 2.3. */ enum di_displayid_product_type { /* Extension section */ DI_DISPLAYID_PRODUCT_TYPE_EXTENSION = 0x00, /* Test structure or equipment */ DI_DISPLAYID_PRODUCT_TYPE_TEST = 0x01, /* Display panel or other transducer, LCD or PDP module, etc. */ DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL = 0x02, /* Standalone display device, desktop monitor, TV monitor, etc. */ DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY = 0x03, /* Television receiver */ DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER = 0x04, /* Repeater/translator */ DI_DISPLAYID_PRODUCT_TYPE_REPEATER = 0x05, /* Direct drive monitor */ DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE = 0x06, }; /** * Get the DisplayID product type. */ enum di_displayid_product_type di_displayid_get_product_type(const struct di_displayid *displayid); /** * DisplayID data block tag. */ enum di_displayid_data_block_tag { /*Product Identification Data Block */ DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID = 0x00, /* Display Parameters Data Block */ DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS = 0x01, /* Color Characteristics Data Block */ DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT = 0x02, /* Video Timing Modes Type I - Detailed Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING = 0x03, /* Video Timing Modes Type II - Detailed Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING = 0x04, /* Video Timing Modes Type III - Short Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING = 0x05, /* Video Timing Modes Type IV - DMT Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING = 0x06, /* Supported Timing Modes - VESA DMT Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_VESA_TIMING = 0x07, /* Supported Timing Modes - CTA-861 Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_CEA_TIMING = 0x08, /* Video Timing Range Data Block */ DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS = 0x09, /* Product Serial Number Data Block */ DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL = 0x0A, /* General-purpose ASCII String Data Block */ DI_DISPLAYID_DATA_BLOCK_ASCII_STRING = 0x0B, /* Display Device Data Data Block */ DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA = 0x0C, /* Interface Power Sequencing Data Block */ DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ = 0x0D, /* Transfer Characteristics Data Block */ DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT = 0x0E, /* Display Interface Data Block */ DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE = 0x0F, /* Stereo Display Interface Data Block */ DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE = 0x10, /* Video Timing Modes Type V - Short Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING = 0x11, /* Tiled Display Topology Data Block */ DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO = 0x12, /* Video Timing Modes Type VI - Detailed Timings Data Block */ DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING = 0x13, }; /** * A DisplayID data block. */ struct di_displayid_data_block; /** * Get a DisplayID data block tag. */ enum di_displayid_data_block_tag di_displayid_data_block_get_tag(const struct di_displayid_data_block *data_block); /** * Display parameters feature support flags, defined in section 4.2.3. */ struct di_displayid_display_params_features { /* Audio support on video interface */ bool audio; /* Audio inputs are provided separately from the video interface */ bool separate_audio_inputs; /* Audio information received via the video interface will automatically * override any other audio input channels provided */ bool audio_input_override; /* Display supports the VESA Display Power Management (DPM) standard */ bool power_management; /* Display is capable of only a single fixed timing */ bool fixed_timing; /* Display is capable of supporting timings at only a single fixed pixel * format */ bool fixed_pixel_format; /* Display supports ACP, ISRC1 or ISRC2 packets */ bool ai; /* Display by default will de-interlace any interlaced video input */ bool deinterlacing; }; /** * Display parameters data block, defined in section 4.2. */ struct di_displayid_display_params { /* Image size in millimeters accurate to the thenths place, zero if unset */ float horiz_image_mm, vert_image_mm; /* Native format size in pixels, zero if unset */ int32_t horiz_pixels, vert_pixels; /* Feature flags */ const struct di_displayid_display_params_features *features; /* Transfer characteristic gamma, zero if unset */ float gamma; /* Aspect ratio (long axis divided by short axis) */ float aspect_ratio; /* Color bit depth (dynamic range) */ int32_t bits_per_color_overall, bits_per_color_native; }; /** * Get display parameters from a DisplayID data block. * * Returns NULL if the data block tag isn't * DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS. */ const struct di_displayid_display_params * di_displayid_data_block_get_display_params(const struct di_displayid_data_block *data_block); enum di_displayid_type_i_timing_stereo_3d { /* This timing is always displayed monoscopic (no stereo) */ DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_NEVER = 0x00, /* This timing is always displayed in stereo */ DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_ALWAYS = 0x01, /* This timing is displayed in mono or stereo depending on a user action * (wearing the stereo glasses, etc.) */ DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_USER = 0x02, }; enum di_displayid_type_i_timing_aspect_ratio { DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_1_1 = 0x00, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_5_4 = 0x01, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_4_3 = 0x02, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_15_9 = 0x03, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_9 = 0x04, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_10 = 0x05, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_64_27 = 0x06, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_256_135 = 0x07, DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED = 0x08, }; enum di_displayid_type_i_timing_sync_polarity { DI_DISPLAYID_TYPE_I_TIMING_SYNC_NEGATIVE = 0x00, DI_DISPLAYID_TYPE_I_TIMING_SYNC_POSITIVE = 0x01, }; /** * Type I timing, defined in section 4.4.1. */ struct di_displayid_type_i_timing { double pixel_clock_mhz; /* mega-hertz */ bool preferred; enum di_displayid_type_i_timing_stereo_3d stereo_3d; bool interlaced; enum di_displayid_type_i_timing_aspect_ratio aspect_ratio; int32_t horiz_active, vert_active; int32_t horiz_blank, vert_blank; int32_t horiz_offset, vert_offset; int32_t horiz_sync_width, vert_sync_width; enum di_displayid_type_i_timing_sync_polarity horiz_sync_polarity, vert_sync_polarity; }; /** * Get type I timings from a DisplayID data block. * * The returned array is NULL-terminated. * * Returns NULL if the data block tag isn't * DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING. */ const struct di_displayid_type_i_timing *const * di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *data_block); /** * Behavior when more than 1 tile and less than total number of tiles are driven * by the source. */ enum di_displayid_tiled_topo_missing_recv_behavior { /* Undefined */ DI_DISPLAYID_TILED_TOPO_MISSING_RECV_UNDEF = 0, /* The image is displayed on the tile only */ DI_DISPLAYID_TILED_TOPO_MISSING_RECV_TILE_ONLY = 1, }; /** * Behavior of this tile when it is the only tile receiving an image from the * source. */ enum di_displayid_tiled_topo_single_recv_behavior { /* Undefined */ DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_UNDEF = 0, /* Image is displayed on the tile only */ DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_TILE_ONLY = 1, /* Image is scaled to fit the entire tiled display */ DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_SCALED = 2, /* Image is cloned to all other tiles of the entire tiled display */ DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_CLONED = 3, }; /** * Tiled display capabilities. */ struct di_displayid_tiled_topo_caps { /* The tiled display is within a single physical display enclosure */ bool single_enclosure; /* Behavior when subsets of the tiles of the entire tiled display are * receiving images from source */ enum di_displayid_tiled_topo_missing_recv_behavior missing_recv_behavior; enum di_displayid_tiled_topo_single_recv_behavior single_recv_behavior; }; /** * Tiled display bezel information. * * The lengths are measured in pixels, accurate to the tenths place. */ struct di_displayid_tiled_topo_bezel { float top_px, bottom_px, right_px, left_px; }; /** * Tiled display topology, defined in section 4.14. */ struct di_displayid_tiled_topo { /* Capabilities */ const struct di_displayid_tiled_topo_caps *caps; /* Total number of horizontal/vertical tiles */ int32_t total_horiz_tiles, total_vert_tiles; /* Horizontal/vertical tile location */ int32_t horiz_tile_location, vert_tile_location; /* Horizontal/vertical size in pixels */ int32_t horiz_tile_pixels, vert_tile_lines; /* Bezel information, NULL if unset */ const struct di_displayid_tiled_topo_bezel *bezel; /* Vendor PnP ID, product code and serial number */ char vendor_id[3]; uint16_t product_code; uint32_t serial_number; }; /** * Get tiled display topology from a DisplayID data block. * * Returns NULL if the data block tag isn't * DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO. */ const struct di_displayid_tiled_topo * di_displayid_data_block_get_tiled_topo(const struct di_displayid_data_block *data_block); /** * Get DisplayID data blocks. * * The returned array is NULL-terminated. */ const struct di_displayid_data_block *const * di_displayid_get_data_blocks(const struct di_displayid *displayid); #endif libdisplay-info-0.1.1/include/libdisplay-info/dmt.h000066400000000000000000000020451437312070200222430ustar00rootroot00000000000000#ifndef DI_DMT_H #define DI_DMT_H /** * libdisplay-info's low-level API for VESA Display Monitor Timing (DMT). * * The library implements VESA DMT version 1.0 revision 13. */ #include #include /** * A DMT timing. */ struct di_dmt_timing { /* DMT ID */ uint8_t dmt_id; /* EDID standard timing 2-byte code, zero if unset */ uint16_t edid_std_id; /* CVT 3-byte code, zero if unset */ uint32_t cvt_id; /* Addressable pixels */ int32_t horiz_video, vert_video; /* Field Refresh Rate in Hz */ float refresh_rate_hz; /* Pixel clock in Hz */ int32_t pixel_clock_hz; /* Horizontal/Vertical Blanking in pixels/lines */ int32_t horiz_blank, vert_blank; /* Horizontal/Vertical Front Porch in pixels/lines */ int32_t horiz_front_porch, vert_front_porch; /* Horizontal/Vertical Sync Pulse Width in pixels/lines */ int32_t horiz_sync_pulse, vert_sync_pulse; /* Horizontal/Vertical Border in pixels/lines */ int32_t horiz_border, vert_border; /* Whether the timing has reduced blanking */ bool reduced_blanking; }; #endif libdisplay-info-0.1.1/include/libdisplay-info/edid.h000066400000000000000000000571721437312070200223770ustar00rootroot00000000000000#ifndef DI_EDID_H #define DI_EDID_H /** * libdisplay-info's low-level API for Extended Display Identification * Data (EDID). * * EDID 1.4 is defined in VESA Enhanced Extended Display Identification Data * Standard release A revision 2. */ #include #include #include /** * EDID data structure. */ struct di_edid; /** * Get the EDID version. */ int di_edid_get_version(const struct di_edid *edid); /** * Get the EDID revision. */ int di_edid_get_revision(const struct di_edid *edid); /** * EDID vendor & product identification. */ struct di_edid_vendor_product { char manufacturer[3]; uint16_t product; uint32_t serial; /* zero if unset */ /* These fields are zero if unset */ int manufacture_week; int manufacture_year; int model_year; }; const struct di_edid_vendor_product * di_edid_get_vendor_product(const struct di_edid *edid); /** * EDID analog signal level standard. */ enum di_edid_video_input_analog_signal_level_std { /* 0.700 : 0.300 : 1.000 V p-p */ DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_0 = 0x00, /* 0.714 : 0.286 : 1.000 V p-p */ DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_1 = 0x01, /* 1.000 : 0.400 : 1.400 V p-p */ DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_2 = 0x02, /* 0.700 : 0.000 : 0.700 V p-p */ DI_EDID_VIDEO_INPUT_ANALOG_SIGNAL_LEVEL_3 = 0x03, }; /** * EDID analog video setup. */ enum di_edid_video_input_analog_video_setup { /* Blank level equals black level */ DI_EDID_VIDEO_INPUT_ANALOG_BLANK_LEVEL_EQ_BLACK = 0, /* Blank-to-black setup or pedestal */ DI_EDID_VIDEO_INPUT_ANALOG_BLANK_TO_BLACK_SETUP_PEDESTAL = 1, }; /** * EDID analog video input basic information, defined in section 3.6.1. */ struct di_edid_video_input_analog { enum di_edid_video_input_analog_signal_level_std signal_level_std; enum di_edid_video_input_analog_video_setup video_setup; /* Separate Sync H & V Signals are supported */ bool sync_separate; /* Composite Sync Signal on Horizontal is supported */ bool sync_composite; /* Composite Sync Signal on Green Video is supported */ bool sync_on_green; /* Serration on the Vertical Sync is supported */ bool sync_serrations; }; /** * Get the analog video input basic information. * * Returns NULL if this isn't an analog display. */ const struct di_edid_video_input_analog * di_edid_get_video_input_analog(const struct di_edid *edid); /** * Digital video input interface standard. */ enum di_edid_video_input_digital_interface { DI_EDID_VIDEO_INPUT_DIGITAL_UNDEFINED = 0x00, DI_EDID_VIDEO_INPUT_DIGITAL_DVI = 0x01, DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_A = 0x02, DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_B = 0x03, DI_EDID_VIDEO_INPUT_DIGITAL_MDDI = 0x04, DI_EDID_VIDEO_INPUT_DIGITAL_DISPLAYPORT = 0x05, }; /** * EDID digital video input basic information, defined in section 3.6.1. */ struct di_edid_video_input_digital { /* EDID 1.2 and 1.3 */ bool dfp1; /* Compatible with VESA DFP 1.x TDMS CRGB */ /* EDID 1.4 and later */ int color_bit_depth; /* zero if undefined */ enum di_edid_video_input_digital_interface interface; }; /** * Get the digital video input basic information. * * Returns NULL if this isn't a digital display. */ const struct di_edid_video_input_digital * di_edid_get_video_input_digital(const struct di_edid *edid); struct di_edid_screen_size { /* Physical size in centimeters, zero if unset */ int width_cm, height_cm; /* Aspect ratio rounded to the hundredth decimal place, zero if unset. */ float landscape_aspect_ratio, portait_aspect_ratio; }; /** * Get the screen size. */ const struct di_edid_screen_size * di_edid_get_screen_size(const struct di_edid *edid); /** * Get the display transfer characteristics from the basic EDID parameters, also * known as "gamma". * * Returns 0 if unset (ie, stored in an extension block). */ float di_edid_get_basic_gamma(const struct di_edid *edid); /** * Supported legacy Display Power Management Signaling (DPMS) states, defined in * section 3.6.4. * * Display Power Management (DPM) compliant displays only support "off". */ struct di_edid_dpms { bool standby; bool suspend; bool off; }; /** * Get the set of supported legacy DPMS states. */ const struct di_edid_dpms * di_edid_get_dpms(const struct di_edid *edid); /** * Display color type. */ enum di_edid_display_color_type { /* Monochrome or grayscale display */ DI_EDID_DISPLAY_COLOR_MONOCHROME = 0x00, /* RGB color display */ DI_EDID_DISPLAY_COLOR_RGB = 0x01, /* Non-RGB color display */ DI_EDID_DISPLAY_COLOR_NON_RGB = 0x02, /* Undefined */ DI_EDID_DISPLAY_COLOR_UNDEFINED = 0x03, }; /** * Get the display color type. * * For digital displays using EDID 1.4 and later, DI_EDID_DISPLAY_COLOR_UNDEFINED * is always returned. */ enum di_edid_display_color_type di_edid_get_display_color_type(const struct di_edid *edid); /** * Basic color encoding formats, defined in section 3.6.4. */ struct di_edid_color_encoding_formats { bool rgb444; /* RGB 4:4:4 */ bool ycrcb444; /* YCrCb 4:4:4 */ bool ycrcb422; /* YCrCb 4:2:2 */ }; /** * Get the set of supported color encoding formats. * * Returns NULL if the display is analog or if the color encoding formats are * not specified. */ const struct di_edid_color_encoding_formats * di_edid_get_color_encoding_formats(const struct di_edid *edid); /** * Miscellaneous basic features, defined in section 3.6.4. * * Note, the enum values don't match the specification. */ struct di_edid_misc_features { /** * First detailed timing is the preferred timing. * * Always set for EDID 1.4 and later. */ bool has_preferred_timing; /** * GTF using the default parameters is supported. * * Never set for EDID 1.4 and later. */ bool default_gtf; /** * sRGB standard default color space is primary color space. */ bool srgb_is_primary; /** * Preferred timing mode includes native pixel format and rate. * * Never set for EDID 1.3 and earlier. */ bool preferred_timing_is_native; /** * GTF or CVT generated timings within the display's range limits are * accepted. * * Never set for EDID 1.3 and earlier. */ bool continuous_freq; }; /** * Get the set of miscellaneous basic features. */ const struct di_edid_misc_features * di_edid_get_misc_features(const struct di_edid *edid); /** * EDID display chromaticity coordinates, defined in section 3.7. * * The values are accurate to the thousandth place. The red, green and blue * values are zero for monochrome displays. */ struct di_edid_chromaticity_coords { float red_x, red_y; float green_x, green_y; float blue_x, blue_y; float white_x, white_y; }; /** * Get chromaticity coordinates. */ const struct di_edid_chromaticity_coords * di_edid_get_chromaticity_coords(const struct di_edid *edid); /** * Established timings I and II, defined in section 3.8. */ struct di_edid_established_timings_i_ii { /* Established timings I */ bool has_720x400_70hz; /* 720 x 400 @ 70Hz (IBM, VGA) */ bool has_720x400_88hz; /* 720 x 400 @ 88Hz (IBM, XGA2) */ bool has_640x480_60hz; /* 640 x 480 @ 60Hz (IBM, VGA) */ bool has_640x480_67hz; /* 640 x 480 @ 67Hz (Apple, Mac II) */ bool has_640x480_72hz; /* 640 x 480 @ 72Hz (VESA) */ bool has_640x480_75hz; /* 640 x 480 @ 75Hz (VESA) */ bool has_800x600_56hz; /* 800 x 600 @ 56Hz (VESA) */ bool has_800x600_60hz; /* 800 x 600 @ 60Hz (VESA) */ /* Established timings II */ bool has_800x600_72hz; /* 800 x 600 @ 72Hz (VESA) */ bool has_800x600_75hz; /* 800 x 600 @ 75Hz (VESA) */ bool has_832x624_75hz; /* 832 x 624 @ 75Hz (Apple, Mac II) */ bool has_1024x768_87hz_interlaced; /* 1024 x 768 @ 87Hz interlaced (IBM) */ bool has_1024x768_60hz; /* 1024 x 768 @ 60Hz (VESA) */ bool has_1024x768_70hz; /* 1024 x 768 @ 70Hz (VESA) */ bool has_1024x768_75hz; /* 1024 x 768 @ 75Hz (VESA) */ bool has_1280x1024_75hz; /* 1280 x 1024 @ 75Hz (VESA) */ bool has_1152x870_75hz; /* 1152 x 870 @ 75Hz (Apple, Mac II) */ }; /** * Get established timings I and II. */ const struct di_edid_established_timings_i_ii * di_edid_get_established_timings_i_ii(const struct di_edid *edid); /** * Aspect ratio for an EDID standard timing. */ enum di_edid_standard_timing_aspect_ratio { DI_EDID_STANDARD_TIMING_16_10 = 0, /* 16:10 */ DI_EDID_STANDARD_TIMING_4_3 = 1, /* 4:3 */ DI_EDID_STANDARD_TIMING_5_4 = 2, /* 5:4 */ DI_EDID_STANDARD_TIMING_16_9 = 3, /* 16:9 */ }; /** * EDID standard timing, defined in section 3.9. */ struct di_edid_standard_timing { /* Horizontal addressable pixels */ int32_t horiz_video; /* Aspect ratio */ enum di_edid_standard_timing_aspect_ratio aspect_ratio; /* Field Refresh Rate in Hz */ int32_t refresh_rate_hz; }; /** * Get the vertical addressable line count of an EDID standard timing. */ int32_t di_edid_standard_timing_get_vert_video(const struct di_edid_standard_timing *t); /* See */ struct di_dmt_timing; /** * Get the VESA Display Monitor Timing (DMT), if any. * * NULL is returned if the standard timing doesn't have a DMT. */ const struct di_dmt_timing * di_edid_standard_timing_get_dmt(const struct di_edid_standard_timing *t); /** * Get a list of EDID standard timings. * * The returned array is NULL-terminated. */ const struct di_edid_standard_timing *const * di_edid_get_standard_timings(const struct di_edid *edid); /** * Stereo viewing support. */ enum di_edid_detailed_timing_def_stereo { /* Normal Display – No Stereo */ DI_EDID_DETAILED_TIMING_DEF_STEREO_NONE, /* Field sequential stereo, right image when stereo sync signal = 1 */ DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_RIGHT, /* Field sequential stereo, left image when stereo sync signal = 1 */ DI_EDID_DETAILED_TIMING_DEF_STEREO_FIELD_SEQ_LEFT, /* 2-way interleaved stereo, right image on even lines */ DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_RIGHT, /* 2-way interleaved stereo, left image on even lines */ DI_EDID_DETAILED_TIMING_DEF_STEREO_2_WAY_INTERLEAVED_LEFT, /* 4-way interleaved stereo */ DI_EDID_DETAILED_TIMING_DEF_STEREO_4_WAY_INTERLEAVED, /* Side-by-Side interleaved stereo */ DI_EDID_DETAILED_TIMING_DEF_STEREO_SIDE_BY_SIDE_INTERLEAVED, }; /** * Signal definitions for EDID detailed timings, defined in notes for table 3.22. */ enum di_edid_detailed_timing_def_signal_type { /* Analog composite */ DI_EDID_DETAILED_TIMING_DEF_SIGNAL_ANALOG_COMPOSITE = 0x00, /* Bipolar analog composite */ DI_EDID_DETAILED_TIMING_DEF_SIGNAL_BIPOLAR_ANALOG_COMPOSITE = 0x01, /* Digital composite */ DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_COMPOSITE = 0x02, /* Digital separate */ DI_EDID_DETAILED_TIMING_DEF_SIGNAL_DIGITAL_SEPARATE = 0x03, }; /** * Digital separate sync polarity for EDID detailed timings. */ enum di_edid_detailed_timing_def_sync_polarity { DI_EDID_DETAILED_TIMING_DEF_SYNC_NEGATIVE = 0, DI_EDID_DETAILED_TIMING_DEF_SYNC_POSITIVE = 1, }; /** * Flags for ANALOG_COMPOSITE signals */ struct di_edid_detailed_timing_analog_composite { /* Sync with serrations (H-sync during V-sync) */ bool sync_serrations; /* Sync on green signal only (as opposed to all three * RGB video signals) */ bool sync_on_green; }; /** * Flags for BIPOLAR_ANALOG_COMPOSITE signals */ struct di_edid_detailed_timing_bipolar_analog_composite { /* Sync with serrations (H-sync during V-sync) */ bool sync_serrations; /* Sync on green signal only (as opposed to all three * RGB video signals) */ bool sync_on_green; }; /** * Flags for DIGITAL_COMPOSITE signals */ struct di_edid_detailed_timing_digital_composite { /* Sync with serrations (H-sync during V-sync) */ bool sync_serrations; /* Horizontal polarity (outside of V-sync) */ enum di_edid_detailed_timing_def_sync_polarity sync_horiz_polarity; }; /** * Flags for DIGITAL_SEPARATE signals */ struct di_edid_detailed_timing_digital_separate { /* Vertical polarity */ enum di_edid_detailed_timing_def_sync_polarity sync_vert_polarity; /* Horizontal polarity (outside of V-sync) */ enum di_edid_detailed_timing_def_sync_polarity sync_horiz_polarity; }; /** * EDID detailed timing definition, defined in section 3.10.2. */ struct di_edid_detailed_timing_def { /* Pixel clock */ int32_t pixel_clock_hz; /* Horizontal/Vertical Addressable Video in pixels/lines */ int32_t horiz_video, vert_video; /* Horizontal/Vertical Blanking in pixels/lines */ int32_t horiz_blank, vert_blank; /* Horizontal/Vertical Front Porch in pixels/lines */ int32_t horiz_front_porch, vert_front_porch; /* Horizontal/Vertical Sync Pulse Width in pixels/lines */ int32_t horiz_sync_pulse, vert_sync_pulse; /* Horizontal/Vertical Addressable Video Image Size in mm, zero if unset */ int32_t horiz_image_mm, vert_image_mm; /* Horizontal/Vertical Border in pixels/lines */ int32_t horiz_border, vert_border; /* Interlaced signal */ bool interlaced; /* Stereo Viewing Support */ enum di_edid_detailed_timing_def_stereo stereo; /* Signal type */ enum di_edid_detailed_timing_def_signal_type signal_type; /* Flags for ANALOG_COMPOSITE signals, NULL otherwise */ const struct di_edid_detailed_timing_analog_composite *analog_composite; /* Flags for BIPOLAR_ANALOG_COMPOSITE signals, NULL otherwise */ const struct di_edid_detailed_timing_bipolar_analog_composite *bipolar_analog_composite; /* Flags for DIGITAL_COMPOSITE signals, NULL otherwise */ const struct di_edid_detailed_timing_digital_composite *digital_composite; /* Flags for DIGITAL_SEPARATE signals, NULL otherwise */ const struct di_edid_detailed_timing_digital_separate *digital_separate; }; /** * Get a list of EDID detailed timing definitions. * * The returned array is NULL-terminated. */ const struct di_edid_detailed_timing_def *const * di_edid_get_detailed_timing_defs(const struct di_edid *edid); /** * EDID display descriptor. */ struct di_edid_display_descriptor; /** * Get a list of EDID display descriptors. * * The returned array is NULL-terminated. */ const struct di_edid_display_descriptor *const * di_edid_get_display_descriptors(const struct di_edid *edid); /** * EDID display descriptor tag, defined in section 3.10.3. */ enum di_edid_display_descriptor_tag { /* Display Product Serial Number */ DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL = 0xFF, /* Alphanumeric Data String (ASCII) */ DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING = 0xFE, /* Display Range Limits */ DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS = 0xFD, /* Display Product Name */ DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME = 0xFC, /* Color Point Data */ DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT = 0xFB, /* Standard Timing Identifications */ DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS = 0xFA, /* Display Color Management (DCM) Data */ DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA = 0xF9, /* CVT 3 Byte Timing Codes */ DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES = 0xF8, /* Established Timings III */ DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III = 0xF7, /* Dummy Descriptor */ DI_EDID_DISPLAY_DESCRIPTOR_DUMMY = 0x10, }; /** * Get the tag of an EDID display descriptor. */ enum di_edid_display_descriptor_tag di_edid_display_descriptor_get_tag(const struct di_edid_display_descriptor *desc); /** * Get the contents of a product serial number, a data string, or a product name * display descriptor. * * Returns NULL if the display descriptor tag isn't either * DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL_NUMBER, * DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING or * DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME. */ const char * di_edid_display_descriptor_get_string(const struct di_edid_display_descriptor *desc); /** * EDID display range limits type. * * The values do not match the EDID specification. * * The CVT entry was introduced in EDID 1.4. */ enum di_edid_display_range_limits_type { /* Range Limits Only - no additional timing information is provided */ DI_EDID_DISPLAY_RANGE_LIMITS_BARE, /* Default GTF supported */ DI_EDID_DISPLAY_RANGE_LIMITS_DEFAULT_GTF, /* Secondary GTF supported */ DI_EDID_DISPLAY_RANGE_LIMITS_SECONDARY_GTF, /* CVT supported */ DI_EDID_DISPLAY_RANGE_LIMITS_CVT, }; struct di_edid_display_range_limits_secondary_gtf { /* Start break frequency in Hz */ int start_freq_hz; /* C, M, K, J parameters */ float c, m, k, j; }; enum di_edid_cvt_aspect_ratio { DI_EDID_CVT_ASPECT_RATIO_4_3 = 1 << 7, DI_EDID_CVT_ASPECT_RATIO_16_9 = 1 << 6, DI_EDID_CVT_ASPECT_RATIO_16_10 = 1 << 5, DI_EDID_CVT_ASPECT_RATIO_5_4 = 1 << 4, DI_EDID_CVT_ASPECT_RATIO_15_9 = 1 << 3, }; enum di_edid_cvt_scaling { DI_EDID_CVT_SCALING_HORIZ_SHRINK = 1 << 7, DI_EDID_CVT_SCALING_HORIZ_STRETCH = 1 << 6, DI_EDID_CVT_SCALING_VERT_SHRINK = 1 << 5, DI_EDID_CVT_SCALING_VERT_STRETCH = 1 << 4, }; struct di_edid_display_range_limits_cvt { int32_t version, revision; /* Maximum active pixels per line, zero for no limit */ int32_t max_horiz_px; /* Supported aspect ratio, bitfield of enum di_edid_cvt_aspect_ratio */ uint32_t supported_aspect_ratio; /* Preferred aspect ratio */ enum di_edid_cvt_aspect_ratio preferred_aspect_ratio; /* Whether standard CVT blanking is supported */ bool standard_blanking; /* Whether reduced CVT blanking is supported */ bool reduced_blanking; /* Supported types of display scaling, bitfield of * enum di_edid_cvt_scaling */ uint32_t supported_scaling; /* Preferred vertical refresh rate, in Hz */ int32_t preferred_vert_refresh_hz; }; /** * EDID display range limits, defined in section 3.10.3.3.1. */ struct di_edid_display_range_limits { /* Vertical rate limits in Hz, from 1 Hz to 510 Hz */ int32_t min_vert_rate_hz, max_vert_rate_hz; /* Horizontal rate limits in Hz, from 1 KHz to 510 KHz, rounded to the * nearest multiple of 1 KHz */ int32_t min_horiz_rate_hz, max_horiz_rate_hz; /* Maximum pixel clock in Hz, zero if unset, rounded to the nearest * multiple of 0.25 MHz if CVT, otherwise to the nearest multiple of * 10 MHz */ int32_t max_pixel_clock_hz; enum di_edid_display_range_limits_type type; /* For SECONDARY_GTF limits, NULL otherwise */ const struct di_edid_display_range_limits_secondary_gtf *secondary_gtf; /* For CVT limits, NULL otherwise */ const struct di_edid_display_range_limits_cvt *cvt; }; /** * Get the contents of a display range limits descriptor. * * Returns NULL if the display descriptor tag isn't * DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS. */ const struct di_edid_display_range_limits * di_edid_display_descriptor_get_range_limits(const struct di_edid_display_descriptor *desc); /** * Get a standard timing list from an EDID display descriptor. * * The returned array is NULL-terminated. * * Returns NULL if the display descriptor tag isn't * DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS. */ const struct di_edid_standard_timing *const * di_edid_display_descriptor_get_standard_timings(const struct di_edid_display_descriptor *desc); /** * EDID Color Points, defined in section 3.10.3.5. */ struct di_edid_color_point { /* White point index number */ int index; /* The white chromaticity values, accurate to the thousandth place */ float white_x, white_y; /* Gamma, zero if unset (ie, stored in an extension block) */ float gamma; }; /** * Get a color point list from an EDID display descriptor. * * The returned array is NULL-terminated. * * Returns NULL if the display descriptor tag isn't * DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT. * * Upstream is not aware of any EDID blob containing Color Point Descriptors. * If such a blob is found, please share it with upstream! */ const struct di_edid_color_point *const * di_edid_display_descriptor_get_color_points(const struct di_edid_display_descriptor *desc); /** * Get a list of established timings III from an EDID display descriptor. * * The returned array is NULL-terminated. * * Returns NULL if the display descriptor tag isn't * DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III. */ const struct di_dmt_timing *const * di_edid_display_descriptor_get_established_timings_iii(const struct di_edid_display_descriptor *desc); /** * EDID display Color Management Data, defined in section 3.10.3.7 * * Contains the coefficients for the function `L = a₃ × v³ + a₂ × v²` * describing the luminance response L to some voltage v [0, 0.7] for each color * channel. * * For more information see VESA DCM Standard, Version 1; January 6, 2003 */ struct di_edid_color_management_data { int version; /* red polynomial coefficient a3 */ float red_a3; /* red polynomial coefficient a2 */ float red_a2; /* green polynomial coefficient a3 */ float green_a3; /* green polynomial coefficient a2 */ float green_a2; /* blue polynomial coefficient a3 */ float blue_a3; /* blue polynomial coefficient a2 */ float blue_a2; }; /** * Get the contents of a Display Color Management (DCM) Data descriptor. * * Returns NULL if the display descriptor tag isn't * DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA. * * Upstream is not aware of any EDID blob containing DCM Data descriptors. * If such a blob is found, please share it with upstream! */ const struct di_edid_color_management_data * di_edid_display_descriptor_get_color_management_data(const struct di_edid_display_descriptor *desc); /** * Aspect ratio for an EDID CVT Timing Code. */ enum di_edid_cvt_timing_code_aspect_ratio { DI_EDID_CVT_TIMING_CODE_4_3 = 0, /* 4:3 */ DI_EDID_CVT_TIMING_CODE_16_9 = 1, /* 16:9 */ DI_EDID_CVT_TIMING_CODE_16_10 = 2, /* 16:10 */ DI_EDID_CVT_TIMING_CODE_15_9 = 3, /* 15:9 */ }; /** * Preferred Vertical Rate for an EDID CVT Timing Code. */ enum di_edid_cvt_timing_code_preferred_vrate { DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_50HZ = 0, /* 50 Hz */ DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_60HZ = 1, /* 60 Hz */ DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_75HZ = 2, /* 75 Hz */ DI_EDID_CVT_TIMING_CODE_PREFERRED_VRATE_85HZ = 3, /* 85 Hz */ }; /** * EDID CVT Timing Code, defined in section 3.10.3.8 * * For more information see VESA Coordinated Video Timings (CVT) Standard. */ struct di_edid_cvt_timing_code { int32_t addressable_lines_per_field; enum di_edid_cvt_timing_code_aspect_ratio aspect_ratio; bool supports_50hz_sb; bool supports_60hz_sb; bool supports_75hz_sb; bool supports_85hz_sb; bool supports_60hz_rb; enum di_edid_cvt_timing_code_preferred_vrate preferred_vertical_rate; }; /** * Get a list of CVT timing codes from an EDID display descriptor. * The highest priority code comes first, the lowest priority code last. * * The returned array is NULL-terminated. * * Returns NULL if the display descriptor tag isn't * DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES. */ const struct di_edid_cvt_timing_code *const * di_edid_display_descriptor_get_cvt_timing_codes(const struct di_edid_display_descriptor *desc); /** * Get a list of EDID extensions. * * The returned array is NULL-terminated. */ const struct di_edid_ext *const * di_edid_get_extensions(const struct di_edid *edid); /** * EDID extension block tags, defined in section 2.2.4. */ enum di_edid_ext_tag { /* CEA 861 Series Extension */ DI_EDID_EXT_CEA = 0x02, /* Video Timing Block Extension */ DI_EDID_EXT_VTB = 0x10, /* Display Information Extension */ DI_EDID_EXT_DI = 0x40, /* Localized String Extension */ DI_EDID_EXT_LS = 0x50, /* Digital Packet Video Link Extension */ DI_EDID_EXT_DPVL = 0x60, /* Extension Block Map */ DI_EDID_EXT_BLOCK_MAP = 0xF0, /* Extension defined by the display manufacturer */ DI_EDID_EXT_VENDOR = 0xFF, /* DisplayID Extension */ DI_EDID_EXT_DISPLAYID = 0x70, }; /** * EDID extension block. */ struct di_edid_ext; /** * Get the tag of an EDID extension block. */ enum di_edid_ext_tag di_edid_ext_get_tag(const struct di_edid_ext *ext); /* See */ struct di_edid_cta; /** * Get a CTA-861 extension block. * * Returns NULL if the extension block tag is not DI_EDID_EXT_CEA. */ const struct di_edid_cta * di_edid_ext_get_cta(const struct di_edid_ext *ext); /* See */ struct di_displayid; /** * Get a DisplayID extension block. * * Returns NULL if the extension block tag is not DI_EDID_EXT_DISPLAYID. */ const struct di_displayid * di_edid_ext_get_displayid(const struct di_edid_ext *ext); #endif libdisplay-info-0.1.1/include/libdisplay-info/gtf.h000066400000000000000000000032431437312070200222400ustar00rootroot00000000000000#ifndef DI_GTF_H #define DI_GTF_H #include /** * Low-level API for Generalized Timing Formula Standard version 1.1. */ /** * Type of frequency parameter used in di_gtf_options.ip_freq_rqd. */ enum di_gtf_ip_param { /* Vertical frame frequency (Hz) */ DI_GTF_IP_PARAM_V_FRAME_RATE, /* Horizontal frequency (kHz) */ DI_GTF_IP_PARAM_H_FREQ, /* Pixel clock rate (MHz) */ DI_GTF_IP_PARAM_H_PIXELS, }; /** * Input options for GTF. */ struct di_gtf_options { /* Number of active image pixels displayed on a line, not including any * margin */ int h_pixels; /* Number of vertical lines in the displayed image */ int v_lines; /* Whether margins are required */ bool margins_rqd; /* Indicates which frequency parameter is specified in ip_freq_rqd */ enum di_gtf_ip_param ip_param; /* Vertical frame frequency (in Hz), horizontal frequency (in kHz) or * pixel clock rate (in MHz) */ double ip_freq_rqd; /* Whether interlaced is required */ bool int_rqd; /* Blanking formula gradient */ double m; /* Blanking formula offset */ double c; /* Blanking formula scaling factor */ double k; /* Blanking formula scaling factor weighting */ double j; }; #define DI_GTF_DEFAULT_M 600.0 #define DI_GTF_DEFAULT_C 40.0 #define DI_GTF_DEFAULT_K 128.0 #define DI_GTF_DEFAULT_J 20.0 /** * Output timing data for GTF. */ struct di_gtf_timing { int h_pixels, v_lines; int h_sync, v_sync; int h_front_porch, h_back_porch; int v_front_porch, v_back_porch; int h_border, v_border; double pixel_freq_mhz; /* in mega-hertz */ }; /** * Compute a timing via the GTF formula. */ void di_gtf_compute(struct di_gtf_timing *t, const struct di_gtf_options *options); #endif libdisplay-info-0.1.1/include/libdisplay-info/info.h000066400000000000000000000054461437312070200224220ustar00rootroot00000000000000#ifndef DI_INFO_H #define DI_INFO_H #include /** * libdisplay-info's high-level API. */ /** * Information about a display device. * * This includes at least one EDID or DisplayID blob. * * Use di_info_parse_edid() to create a struct di_info from an EDID blob. * DisplayID blobs are not yet supported. */ struct di_info; /** * Parse an EDID blob. * * Callers do not need to keep the provided data pointer valid after calling * this function. Callers should destroy the returned pointer via * di_info_destroy(). */ struct di_info * di_info_parse_edid(const void *data, size_t size); /** * Destroy a display device information structure. */ void di_info_destroy(struct di_info *info); /** * Returns the EDID the display device information was constructed with. * * The returned struct di_edid can be used to query low-level EDID information, * see . Users should prefer the high-level API if * possible. * * NULL is returned if the struct di_info doesn't contain an EDID. The returned * struct di_edid is valid until di_info_destroy(). */ const struct di_edid * di_info_get_edid(const struct di_info *info); /** * Get the failure messages for this blob. * * NULL is returned if the blob conforms to the relevant specifications. */ const char * di_info_get_failure_msg(const struct di_info *info); /** * Get the make of the display device. * * This is the manufacturer name, either company name or PNP ID. * This string is informational and not meant to be used in programmatic * decisions, configuration keys, etc. * * The string is in UTF-8 and may contain any characters except ASCII control * codes. * * The caller is responsible for free'ing the returned string. * NULL is returned if the information is not available. */ char * di_info_get_make(const struct di_info *info); /** * Get the model of the display device. * * This is the product name/model string or product number. * This string is informational and not meant to be used in programmatic * decisions, configuration keys, etc. * * The string is in UTF-8 and may contain any characters except ASCII control * codes. * * The caller is responsible for free'ing the returned string. * NULL is returned if the information is not available. */ char * di_info_get_model(const struct di_info *info); /** * Get the serial of the display device. * * This is the product serial string or the serial number. * This string is informational and not meant to be used in programmatic * decisions, configuration keys, etc. * * The string is in UTF-8 and may contain any characters except ASCII control * codes. * * The caller is responsible for free'ing the returned string. * NULL is returned if the information is not available. */ char * di_info_get_serial(const struct di_info *info); #endif libdisplay-info-0.1.1/include/log.h000066400000000000000000000004551437312070200171560ustar00rootroot00000000000000#ifndef LOG_H #define LOG_H /** * Private logging utilities. */ #include #include #include struct di_logger { FILE *f; const char *section; bool initialized; }; void _di_logger_va_add_failure(struct di_logger *logger, const char fmt[], va_list args); #endif libdisplay-info-0.1.1/include/memory-stream.h000066400000000000000000000010361437312070200211720ustar00rootroot00000000000000#ifndef MEMORY_STREAM_H #define MEMORY_STREAM_H /** * Utility functions for memory streams. */ #include #include #include struct memory_stream { FILE *fp; char *str; size_t str_len; }; bool memory_stream_open(struct memory_stream *m); char * memory_stream_close(struct memory_stream *m); /** * A small cleanup helper that simply * calls free(memory_stream_close(m)) to avoid * any dangling string pointers in cleanup/error paths. */ void memory_stream_cleanup(struct memory_stream *m); #endif libdisplay-info-0.1.1/info.c000066400000000000000000000074141437312070200157020ustar00rootroot00000000000000#include #include #include #include #include #include "edid.h" #include "info.h" #include "log.h" #include "memory-stream.h" /* Generated file pnp-id-table.c: */ const char * pnp_id_table(const char *key); struct di_info * di_info_parse_edid(const void *data, size_t size) { struct memory_stream failure_msg; struct di_edid *edid; struct di_info *info; char *failure_msg_str = NULL; if (!memory_stream_open(&failure_msg)) return NULL; edid = _di_edid_parse(data, size, failure_msg.fp); if (!edid) goto err_failure_msg_file; info = calloc(1, sizeof(*info)); if (!info) goto err_edid; info->edid = edid; failure_msg_str = memory_stream_close(&failure_msg); if (failure_msg_str && failure_msg_str[0] != '\0') info->failure_msg = failure_msg_str; else free(failure_msg_str); return info; err_edid: _di_edid_destroy(edid); err_failure_msg_file: memory_stream_cleanup(&failure_msg); return NULL; } void di_info_destroy(struct di_info *info) { _di_edid_destroy(info->edid); free(info->failure_msg); free(info); } const struct di_edid * di_info_get_edid(const struct di_info *info) { return info->edid; } const char * di_info_get_failure_msg(const struct di_info *info) { return info->failure_msg; } static void encode_ascii_byte(FILE *out, char ch) { uint8_t c = (uint8_t)ch; /* * Replace ASCII control codes and non-7-bit codes * with an escape string. The result is guaranteed to be valid * UTF-8. */ if (c < 0x20 || c >= 0x7f) fprintf(out, "\\x%02x", c); else fputc(c, out); } static void encode_ascii_string(FILE *out, const char *str) { size_t len = strlen(str); size_t i; for (i = 0; i < len; i++) encode_ascii_byte(out, str[i]); } char * di_info_get_make(const struct di_info *info) { const struct di_edid_vendor_product *evp; char pnp_id[(sizeof(evp->manufacturer)) + 1] = { 0, }; const char *manuf; struct memory_stream m; if (!info->edid) return NULL; if (!memory_stream_open(&m)) return NULL; evp = di_edid_get_vendor_product(info->edid); memcpy(pnp_id, evp->manufacturer, sizeof(evp->manufacturer)); manuf = pnp_id_table(pnp_id); if (manuf) { encode_ascii_string(m.fp, manuf); return memory_stream_close(&m); } fputs("PNP(", m.fp); encode_ascii_string(m.fp, pnp_id); fputs(")", m.fp); return memory_stream_close(&m); } char * di_info_get_model(const struct di_info *info) { const struct di_edid_vendor_product *evp; const struct di_edid_display_descriptor *const *desc; struct memory_stream m; size_t i; if (!info->edid) return NULL; if (!memory_stream_open(&m)) return NULL; desc = di_edid_get_display_descriptors(info->edid); for (i = 0; desc[i]; i++) { if (di_edid_display_descriptor_get_tag(desc[i]) == DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME) { encode_ascii_string(m.fp, di_edid_display_descriptor_get_string(desc[i])); return memory_stream_close(&m); } } evp = di_edid_get_vendor_product(info->edid); fprintf(m.fp, "0x%04" PRIX16, evp->product); return memory_stream_close(&m); } char * di_info_get_serial(const struct di_info *info) { const struct di_edid_display_descriptor *const *desc; const struct di_edid_vendor_product *evp; struct memory_stream m; size_t i; if (!info->edid) return NULL; if (!memory_stream_open(&m)) return NULL; desc = di_edid_get_display_descriptors(info->edid); for (i = 0; desc[i]; i++) { if (di_edid_display_descriptor_get_tag(desc[i]) == DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL) { encode_ascii_string(m.fp, di_edid_display_descriptor_get_string(desc[i])); return memory_stream_close(&m); } } evp = di_edid_get_vendor_product(info->edid); if (evp->serial != 0) { fprintf(m.fp, "0x%08" PRIX32, evp->serial); return memory_stream_close(&m); } memory_stream_cleanup(&m); return NULL; } libdisplay-info-0.1.1/libdisplay-info.map000066400000000000000000000000371437312070200203610ustar00rootroot00000000000000{ global: di_*; local: *; }; libdisplay-info-0.1.1/log.c000066400000000000000000000005641437312070200155270ustar00rootroot00000000000000#include "log.h" void _di_logger_va_add_failure(struct di_logger *logger, const char fmt[], va_list args) { if (!logger->initialized) { if (ftell(logger->f) > 0) { fprintf(logger->f, "\n"); } fprintf(logger->f, "%s:\n", logger->section); logger->initialized = true; } fprintf(logger->f, " "); vfprintf(logger->f, fmt, args); fprintf(logger->f, "\n"); } libdisplay-info-0.1.1/memory-stream.c000066400000000000000000000007761437312070200175540ustar00rootroot00000000000000#include #include "memory-stream.h" bool memory_stream_open(struct memory_stream *m) { *m = (struct memory_stream){ 0 }; m->fp = open_memstream(&m->str, &m->str_len); return m->fp != NULL; } char * memory_stream_close(struct memory_stream *m) { char *str; int ret; ret = fclose(m->fp); str = m->str; *m = (struct memory_stream){ 0 }; if (ret != 0) { free(str); str = NULL; } return str; } void memory_stream_cleanup(struct memory_stream *m) { free(memory_stream_close(m)); } libdisplay-info-0.1.1/meson.build000066400000000000000000000041611437312070200167410ustar00rootroot00000000000000project( 'libdisplay-info', 'c', version: '0.1.1', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ 'c_std=c11', 'warning_level=3', ], ) assert(meson.project_version().split('.')[0] == '0') dep_hwdata = dependency('hwdata', required: false, native: true) if dep_hwdata.found() hwdata_dir = dep_hwdata.get_variable(pkgconfig: 'pkgdatadir') pnp_ids = files(hwdata_dir / 'pnp.ids') else pnp_ids = files('/usr/share/hwdata/pnp.ids') endif gen_search_table = find_program('tool/gen-search-table.py') pnp_id_table = custom_target( 'pnp-id-table.c', command: [ gen_search_table, pnp_ids, '@OUTPUT@', 'pnp_id_table' ], output: 'pnp-id-table.c', ) cc = meson.get_compiler('c') math = cc.find_library('m', required: false) add_project_arguments(['-D_POSIX_C_SOURCE=200809L'], language: 'c') add_project_arguments(cc.get_supported_arguments([ '-Wundef', '-Wmissing-prototypes', '-Walloca', '-Wdeclaration-after-statement', '-Wconversion', '-Wno-unused-parameter', '-Wno-missing-field-initializers', '-Werror=implicit', ]), language: 'c') symbols_file = 'libdisplay-info.map' symbols_flag = '-Wl,--version-script,@0@'.format(meson.current_source_dir() / symbols_file) di_lib = library( 'display-info', [ 'cta.c', 'cta-vic-table.c', 'cvt.c', 'displayid.c', 'dmt-table.c', 'edid.c', 'gtf.c', 'info.c', 'log.c', 'memory-stream.c', pnp_id_table, ], include_directories: include_directories('include'), dependencies: [math], link_args: symbols_flag, link_depends: symbols_file, install: true, version: meson.project_version(), soversion: meson.project_version().split('.')[1], ) install_subdir( 'include/libdisplay-info', install_dir: get_option('includedir'), ) pkgconfig = import('pkgconfig') pkgconfig.generate( di_lib, filebase: 'libdisplay-info', name: 'libdisplay-info', url: 'https://gitlab.freedesktop.org/emersion/libdisplay-info', description: 'EDID and DisplayID library', ) di_dep = declare_dependency( link_with: di_lib, include_directories: include_directories('include'), ) meson.override_dependency('libdisplay-info', di_dep) subdir('di-edid-decode') subdir('test') libdisplay-info-0.1.1/release.sh000077500000000000000000000036771437312070200165710ustar00rootroot00000000000000#!/bin/sh -eu build_dir=build-release if ! type glab >/dev/null; then echo "glab is needed to create a release" exit 1 fi case "$(git rev-parse --abbrev-ref HEAD)" in main | [0-9]*.[0-9]*) ;; *) echo "Not on the main or a stable branch" exit 1 esac if [ -n "$(git log @{upstream}..)" ]; then echo "The main branch has unpushed commits" exit 1 fi meson_options="" if [ -e "$build_dir" ]; then meson_options="$meson_options --wipe" fi meson setup "$build_dir" $meson_options prev_version="$(git describe --tags --abbrev=0 || git rev-list --max-parents=0 --first-parent HEAD)" version="$(meson introspect "$build_dir" --projectinfo | jq -r .version)" if [ "$version" = "$prev_version" ]; then echo "Version not bumped" exit 1 fi name="$(meson introspect "$build_dir" --projectinfo | jq -r .descriptive_name)" if [ "$name" = "" ]; then echo "Cannot determine project name" exit 1 fi ninja -C "$build_dir" dist archive_name="$name-$version.tar.xz" archive_path="$build_dir/meson-dist/$archive_name" gpg --detach-sig "$archive_path" sha256="$(cd $build_dir/meson-dist && sha256sum $archive_name)" sha512="$(cd $build_dir/meson-dist && sha512sum $archive_name)" archive_url="https://gitlab.freedesktop.org/emersion/$name/-/releases/$version/downloads/$archive_name" announce_path="$build_dir/meson-dist/$name-$version-announce.eml" current_branch=$(git branch --show-current) remote_name=$(git config --get branch.${current_branch}.remote) cat >"$announce_path" < Subject: [ANNOUNCE] $name $version `git shortlog --no-merges "$prev_version.."` git tag: $version $archive_url SHA256: $sha256 SHA512: $sha512 PGP: $archive_url.sig EOF echo "Release announcement written to $announce_path" echo -n "Release $name $version? [y/N] " read answer if [ "$answer" != "y" ]; then exit 1 fi git tag -s -m "$version" "$version" git push "$remote_name" "$version" glab release create "$version" "$archive_path"* --notes "" libdisplay-info-0.1.1/releasing.md000066400000000000000000000027131437312070200170730ustar00rootroot00000000000000# Releasing To make a release of libdisplay-info, follow these steps. 0. Verify the test suites and codebase checks pass. All of the tests should either pass or skip. ninja -C build/ test 1. Update the first stanza of `meson.build` to the intended version. Then commit your changes: RELEASE_NUMBER="x.y.z" RELEASE_NAME="[alpha|beta|RC1|RC2|official|point]" git status git commit meson.build -m "build: bump to version $RELEASE_NUMBER for the $RELEASE_NAME release" git push 2. Run the `release.sh` script to generate the tarballs, sign and upload them, and generate a release announcement template. 3. Compose the release announcements. The script will generate a libdisplay-info-x.y.z.announce file with a list of changes and tags. Prepend these with a human-readable listing of the most notable changes. For x.y.0 releases, indicate the schedule for the x.y+1.0 release. 4. PGP sign the release announcement and send it to . For x.y.0 releases, also create the release series x.y branch. The x.y branch is for bug fixes and conservative changes to the x.y.0 release, and is where we create x.y.z releases from. Creating the x.y branch opens up main for new development and lets new development move on. git branch x.y [sha] git push origin x.y For stable branches, we commit fixes to main first, then `git cherry-pick -x` them back to the stable branch. libdisplay-info-0.1.1/test/000077500000000000000000000000001437312070200155545ustar00rootroot00000000000000libdisplay-info-0.1.1/test/data/000077500000000000000000000000001437312070200164655ustar00rootroot00000000000000libdisplay-info-0.1.1/test/data/LICENSE.CC-BY-4.0000066400000000000000000000443401437312070200205720ustar00rootroot00000000000000Attribution 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. libdisplay-info-0.1.1/test/data/README.md000066400000000000000000000031671437312070200177530ustar00rootroot00000000000000# Test data The following blobs originate from the [linuxhw/EDID] repository and are licensed under the [Creative Commons Attribution 4.0 International][CC-BY-4.0] license: name | source | source commit ---- | ------ | ------------- acer-p1276 | ./Analog/Acer/ACR1616/4108C2D271EB | cff7fe4d44 goldstar-ite6604-hdmi | ./Digital/Goldstar/GSM7666/FE91A60D5B6E | cff7fe4d44 msi-mag321curv-dp | ./Digital/MSI/MSI3DA2/9BD6EC4AFDBD | cff7fe4d44 panasonic-mei96a2-dp | ./Digital/Panasonic/MEI96A2/7CA47BE65E1E | cff7fe4d44 samsung-s27a950d-dp | ./Digital/Samsung/SAM079F/6D343EDB39F8 | cff7fe4d44 sun-gh19ps-dvi | ./Digital/Sun/SUN058C/34B2E3B9A052 | cff7fe4d44 viewsonic-vp2768-dp | ./Digital/ViewSonic/VSC2034/6B81BEA28A1E | cff7fe4d44 philips-ftv-490 | ./Digital/Philips/PHL01EA/03A4224323A6 | cff7fe4d44 hitachi-55r6+ | ./Digital/Hitachi/HEC0000/58C711CBA787 | cff7fe4d44 The following blobs originate from the [edid-decode] repository and are under the same license as the rest of the libdisplay-info project: name | source | source commit ---- | ------ | ------------- apple-xdr-dp | ./data/apple-xdr-6k-tile0 | 894eefd custom-uncommon-cta-vesa | ./test/cta-vesa.test | 894eefd dell-2408wfp-dp | ./data/dell-2408wfp-dp | 894eefd samsung-q800t-hdmi2.0 | ./data/samsung-q800t-hdmi2.0 | 894eefd cta-timings | ./test/cta-timings.test | 894eefd The other blobs are under the same license as the rest of the libdisplay-info project: name | source ---- | ------ cvt | handcrafted hp-5dq99aa-hdmi | display [linuxhw/EDID]: https://github.com/linuxhw/EDID [edid-decode]: https://git.linuxtv.org/edid-decode.git [CC-BY-4.0]: LICENSE.CC-BY-4.0 libdisplay-info-0.1.1/test/data/acer-p1276.diff000066400000000000000000000016731437312070200210150ustar00rootroot00000000000000--- ref +++ di @@ -65,23 +65,8 @@ DTD 2: 1366x768 59.789541 Hz 683:384 47.712 kHz 85.500000 MHz Hfront 70 Hsync 143 Hback 213 Hpol P Vfront 3 Vsync 3 Vback 24 Vpol P -Checksum: 0xab Unused space in Extension Block: 105 bytes +Checksum: 0xab ---------------- -Warnings: - -Block 0, Base EDID: - Basic Display Parameters & Features: sRGB is signaled, but the gamma != 2.2. -Block 1, CTA-861 Extension Block: - IT Video Formats are overscanned by default, but normally this should be underscanned. - Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0. - -Failures: - -Block 0, Base EDID: - Basic Display Parameters & Features: sRGB is signaled, but the chromaticities do not match. -Block 1, CTA-861 Extension Block: - Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues. - -EDID conformity: FAIL +EDID conformity: PASS libdisplay-info-0.1.1/test/data/acer-p1276.edid000066400000000000000000000004001437312070200207750ustar00rootroot00000000000000r>/A\X%S_?π1|E|a|@d@A&062xd P1276 JGG110015900 f!VQ0F3libdisplay-info-0.1.1/test/data/acer-p1276.print000066400000000000000000000000721437312070200212310ustar00rootroot00000000000000make: Acer Technologies model: P1276 serial: JGG110015900 libdisplay-info-0.1.1/test/data/apple-xdr-dp.diff000066400000000000000000000126101437312070200216140ustar00rootroot00000000000000--- ref +++ di @@ -62,16 +62,13 @@ DTD 6: 3840x2160 47.951737 Hz 16:9 134.696 kHz 528.010000 MHz (699 mm x 393 mm) Hfront 8 Hsync 32 Hback 40 Hpol P Vfront 59 Vsync 8 Vback 582 Vpol N -Checksum: 0x01 Unused space in Extension Block: 58 bytes +Checksum: 0x01 ---------------- Block 2, DisplayID Extension Block: Version: 1.2 - Extension Count: 0 Display Product Type: Extension Section - ContainerID Data Block: - Container ID: dfde542f-1339-444b-ad7d-7071d131bff8 Display Parameters Data Block (0x01): Image size: 699.4 mm x 393.4 mm Display native pixel format: 6016x3384 @@ -81,9 +78,6 @@ Aspect ratio: 1.78 Dynamic bpc native: 12 Dynamic bpc overall: 12 - Vendor-Specific Data Block (0x7f) (Apple), OUI 00-10-FA: - Type: 4, Version: 1 - 00 00 '..' Tiled Display Topology Data Block (0x12): Capabilities: Behavior if it is the only tile: Image is scaled to fit the entire tiled display @@ -95,11 +89,6 @@ Tiled Display Manufacturer/Vendor ID: APP Tiled Display Product ID Code: 44591 Tiled Display Serial Number: 621612546 - Vendor-Specific Data Block (0x7e) (VESA), OUI 3A-02-92: - Data Structure Type: DP - Default Colorspace and EOTF Handling: Native as specified in the Display Parameters DB - Number of Pixels in Hor Pix Cnt Overlapping an Adjacent Panel: 0 - Multi-SST Operation: Not Supported Video Timing Modes Type 1 - Detailed Timings Data Block: DTD: 3840x2160 59.999545 Hz 16:9 134.699 kHz 528.020000 MHz (aspect 16:9, no 3D stereo) Hfront 8 Hsync 32 Hback 40 Hpol P @@ -107,14 +96,12 @@ DTD: 3008x3384 59.999726 Hz 0:0 210.959 kHz 648.910000 MHz (aspect undefined, no 3D stereo) Hfront 8 Hsync 32 Hback 28 Hpol P Vfront 118 Vsync 8 Vback 6 Vpol N - Checksum: 0x8c Checksum: 0x90 ---------------- Block 3, DisplayID Extension Block: Version: 1.2 - Extension Count: 0 Video Timing Modes Type 1 - Detailed Timings Data Block: DTD: 2560x2880 59.999451 Hz 0:0 179.578 kHz 481.270000 MHz (aspect undefined, no 3D stereo) Hfront 8 Hsync 32 Hback 80 Hpol P @@ -131,14 +118,12 @@ DTD: 2560x2880 47.951349 Hz 0:0 179.530 kHz 481.140000 MHz (aspect undefined, no 3D stereo) Hfront 8 Hsync 32 Hback 80 Hpol P Vfront 850 Vsync 8 Vback 6 Vpol N - Checksum: 0x93 Checksum: 0x90 ---------------- Block 4, DisplayID Extension Block: Version: 1.2 - Extension Count: 0 Video Timing Modes Type 1 - Detailed Timings Data Block: DTD: 3008x3384 59.999726 Hz 0:0 210.959 kHz 648.910000 MHz (aspect undefined, no 3D stereo) Hfront 8 Hsync 32 Hback 28 Hpol P @@ -155,14 +140,12 @@ DTD: 3008x3384 47.951701 Hz 0:0 210.940 kHz 648.850000 MHz (aspect undefined, no 3D stereo) Hfront 8 Hsync 32 Hback 28 Hpol P Vfront 1001 Vsync 8 Vback 6 Vpol N - Checksum: 0x7e Checksum: 0x90 ---------------- Block 5, DisplayID Extension Block: Version: 1.2 - Extension Count: 0 Video Timing Modes Type 1 - Detailed Timings Data Block: DTD: 5120x2880 59.999614 Hz 16:9 179.579 kHz 933.810000 MHz (aspect 16:9, no 3D stereo) Hfront 8 Hsync 32 Hback 40 Hpol P @@ -179,14 +162,12 @@ DTD: 5120x2880 47.951594 Hz 16:9 179.531 kHz 933.560000 MHz (aspect 16:9, no 3D stereo) Hfront 8 Hsync 32 Hback 40 Hpol P Vfront 850 Vsync 8 Vback 6 Vpol N - Checksum: 0x0a Checksum: 0x90 ---------------- Block 6, DisplayID Extension Block: Version: 1.2 - Extension Count: 0 Video Timing Modes Type 1 - Detailed Timings Data Block: DTD: 6016x3384 59.999899 Hz 16:9 210.960 kHz 1286.010000 MHz (aspect 16:9, no 3D stereo, preferred) Hfront 8 Hsync 32 Hback 40 Hpol P @@ -203,30 +184,13 @@ DTD: 6016x3384 47.951798 Hz 16:9 210.940 kHz 1285.890000 MHz (aspect 16:9, no 3D stereo) Hfront 8 Hsync 32 Hback 40 Hpol P Vfront 1001 Vsync 8 Vback 6 Vpol N - Checksum: 0xb4 Checksum: 0x90 ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - Colorimetry Data Block: Set the sRGB colorimetry bit to avoid interop issues. -Block 2, DisplayID Extension Block: - Vendor-Specific Data Block (0x7f) (Apple), OUI 00-10-FA: Expected PNP ID but found OUI. - Failures: -Block 1, CTA-861 Extension Block: - Required 640x480p60 timings are missing in the established timings and the SVD list (VIC 1). - Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues. Block 2, DisplayID Extension Block: - DisplayID Base Block has no product type. - Expected 0 DisplayID Extension Block, but got 4. - ContainerID Data Block: Use of DisplayID v2.0 tag for DisplayID v1.2. - Vendor-Specific Data Block (0x7f) (Apple), OUI 00-10-FA: Endian-ness (le) of OUI is different than expected (be). - Vendor-Specific Data Block (0x7e) (VESA), OUI 3A-02-92: Use of DisplayID v2.0 tag for DisplayID v1.2. -EDID: - DisplayID: Missing DisplayID Product Identification Data Block. - + Unknown DisplayID Data Block (0x29, length 16) + Unknown DisplayID Data Block (0x7e, length 5) EDID conformity: FAIL libdisplay-info-0.1.1/test/data/apple-xdr-dp.edid000066400000000000000000000016001437312070200216060ustar00rootroot00000000000000. %F'xRC&PTBPpU x!BPp !\P9P !ProDisplayXDRk<PpW !=Pp !APp !py)T/9DK}pq1 R^8 xN 7 APP/ %~:(AOoTFz C7 upyd w? pb w? rd w?  w? \N w? _Qpydz C7 up C7 xw C7 B4z C7 t C7 ~pydlO? pblO? rdlO? lO? \NlO? _Q pydXO7 uEO7 xRO7 B4XO7 LO7 libdisplay-info-0.1.1/test/data/apple-xdr-dp.print000066400000000000000000000001011437312070200220300ustar00rootroot00000000000000make: Apple Computer Inc model: ProDisplayXDR serial: 0x250D0E02 libdisplay-info-0.1.1/test/data/cta-timings.diff000066400000000000000000000062411437312070200215410ustar00rootroot00000000000000--- ref +++ di @@ -52,8 +52,6 @@ ---------------- Block 1, Block Map Extension Block: - Block 2: CTA-861 Extension Block - Block 3: CTA-861 Extension Block Checksum: 0x0c ---------------- @@ -88,21 +86,6 @@ Max channels: 2 Supported sample rates (kHz): 48 44.1 32 Supported sample sizes (bits): 24 20 16 - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - Source physical address: 1.0.0.0 - Maximum TMDS clock: 300 MHz - Supported Content Types: - Graphics - Extended HDMI video details: - HDMI VICs: - HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - HDMI VIC 2: 3840x2160 25.000000 Hz 16:9 56.250 kHz 297.000000 MHz - HDMI VIC 3: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz - Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: - Version: 1 - Maximum TMDS Character Rate: 600 MHz - SCDC Present - Supports UHD VIC Video Capability Data Block: YCbCr quantization: Selectable (via AVI YQ) RGB quantization: Selectable (via AVI Q) @@ -110,31 +93,15 @@ IT scan behavior: Always Underscanned CE scan behavior: Always Underscanned Video Format Preference Data Block: - VTDB 1 - VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz - VIC 114: 3840x2160 48.000000 Hz 16:9 108.000 kHz 594.000000 MHz - DTD 1 - DTD 3 - DMT 0x48 - VTDB 2 - RID 7@60p YCbCr 4:2:0 Capability Map Data Block: VIC 96: 3840x2160 50.000000 Hz 16:9 112.500 kHz 594.000000 MHz YCbCr 4:2:0 Video Data Block: VIC 114: 3840x2160 48.000000 Hz 16:9 108.000 kHz 594.000000 MHz DisplayID Type VII Video Timing Data Block: - VTDB 1: 5120x2160 60.000000 Hz 1:1 133.320 kHz 693.264000 MHz (aspect 1:1, no 3D stereo) - Hfront 8 Hsync 32 Hback 40 Hpol P - Vfront 48 Vsync 8 Vback 6 Vpol N DisplayID Type VIII Video Timing Data Block: - DMT 0x48: 1920x1200 119.908612 Hz 16:10 152.404 kHz 317.000000 MHz (RB) DisplayID Type X Video Timing Data Block: - VTDB 2: 5120x1440 50.000000 Hz 32:9 73.700 kHz 383.240000 MHz (RBv3, aspect 32:9, no 3D stereo) Video Format Data Block: - RID 7@30p: 3840x1080 30.000000 Hz 32:9 33.000 kHz 134.112000 MHz - RID 7@60p: 3840x1080 60.000000 Hz 32:9 67.200 kHz 268.800000 MHz Native Video Resolution Data Block: - VTDB 1 Detailed Timing Descriptors: DTD 2: 3840x2160 59.996625 Hz 16:9 133.312 kHz 533.250000 MHz (960 mm x 540 mm) Hfront 48 Hsync 32 Hback 80 Hpol P @@ -152,9 +119,9 @@ Native detailed modes: 1 Detailed Timing Descriptors: DTD 3: 1280x720 59.855126 Hz 16:9 44.772 kHz 74.500000 MHz (analog composite, sync-on-green, 960 mm x 540 mm) - Hfront 64 Hsync 128 Hback 192 Hpol N - Vfront 3 Vsync 5 Vback 20 Vpol N -Checksum: 0xef Unused space in Extension Block: 105 bytes + Hfront 64 Hsync 128 Hback 192 + Vfront 3 Vsync 5 Vback 20 +Checksum: 0xef ---------------- libdisplay-info-0.1.1/test/data/cta-timings.edid000066400000000000000000000010001437312070200215220ustar00rootroot0000000000000014"`6xTL&PT/1YEY@@@0pZX2xF hdmi-4k-600  mQa`_^]"! # m 0 52IQ @52libdisplay-info-0.1.1/test/data/cta-timings.print000066400000000000000000000000751437312070200217640ustar00rootroot00000000000000make: The Linux Foundation model: hdmi-4k-600 serial: {null} libdisplay-info-0.1.1/test/data/custom-uncommon-cta-vesa.diff000066400000000000000000000015771437312070200241750ustar00rootroot00000000000000--- ref +++ di @@ -106,21 +106,8 @@ DTD 2: 3840x2160 59.996625 Hz 16:9 133.312 kHz 533.250000 MHz (960 mm x 652 mm) Hfront 48 Hsync 32 Hback 80 Hpol P Vfront 3 Vsync 5 Vback 54 Vpol P -Checksum: 0x78 Unused space in Extension Block: 48 bytes +Checksum: 0x78 ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - Video Data Block: For improved preferred timing interoperability, set 'Native detailed modes' to 1. - Video Capability Data Block: S_PT is equal to S_IT and S_CE, so should be set to 0 instead. - -Failures: - -Block 1, CTA-861 Extension Block: - Detailed Timing Descriptor #2: Mismatch of image size 960x652 mm vs display size 960x540 mm. -EDID: - Base EDID: The DTD max image size is 960x652mm, which is larger than the display size 960.0x540.0mm. - -EDID conformity: FAIL +EDID conformity: PASS libdisplay-info-0.1.1/test/data/custom-uncommon-cta-vesa.edid000066400000000000000000000004001437312070200241520ustar00rootroot0000000000000014"`6xTL&PT/1YEY@@@0pZX2U< hdmi-4k-600 =Aa# ,4?<`CKlDM;B @< Ѐ0 52xlibdisplay-info-0.1.1/test/data/custom-uncommon-cta-vesa.print000066400000000000000000000000751437312070200244110ustar00rootroot00000000000000make: The Linux Foundation model: hdmi-4k-600 serial: {null} libdisplay-info-0.1.1/test/data/cvt.edid000066400000000000000000000002001437312070200201000ustar00rootroot0000000000000014"`6xTL&PT/1YEY@@@0pZX2U< hdmi-1080p $)libdisplay-info-0.1.1/test/data/cvt.print000066400000000000000000000000741437312070200203400ustar00rootroot00000000000000make: The Linux Foundation model: hdmi-1080p serial: {null} libdisplay-info-0.1.1/test/data/dell-2408wfp-dp.edid000066400000000000000000000002001437312070200217350ustar00rootroot00000000000000+SM12/4 x"%Q0&PTK@qO(!:q8-@X,ED!q X,%Ď!NQ0@7!libdisplay-info-0.1.1/test/data/goldstar-ite6604-hdmi.print000066400000000000000000000000671437312070200234030ustar00rootroot00000000000000make: LG Electronics model: ITE6604 serial: 0x01010101 libdisplay-info-0.1.1/test/data/hitachi-55r6+.diff000066400000000000000000000056511437312070200215110ustar00rootroot00000000000000--- ref +++ di @@ -102,26 +102,6 @@ Colorimetry Data Block: BT2020YCC BT2020RGB - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - Source physical address: 3.0.0.0 - Supports_AI - DC_36bit - DC_30bit - DC_Y444 - Maximum TMDS clock: 300 MHz - Extended HDMI video details: - Base EDID image size is in units of 1 cm - HDMI VICs: - HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - HDMI VIC 3: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz - HDMI VIC 4: 4096x2160 24.000000 Hz 256:135 54.000 kHz 297.000000 MHz - Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: - Version: 1 - Maximum TMDS Character Rate: 600 MHz - SCDC Present - Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding - Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding - Supports Auto Low-Latency Mode HDR Static Metadata Data Block: Electro optical transfer functions: Traditional gamma - SDR luminance range @@ -129,23 +109,6 @@ Hybrid Log-Gamma Supported static metadata descriptors: Static metadata type 1 - Vendor-Specific Video Data Block (Dolby), OUI 00-D0-46: - Version: 2 (12 bytes) - Supports global dimming - DM Version: 3.x - Backlt Min Luma: 100 cd/m^2 - Interface: Standard + Low-Latency - Supports 10b 12b 444: Not supported - Target Min PQ v2: 200 (0.05768953 cd/m^2) - Target Max PQ v2: 2445 (245 cd/m^2) - Unique Rx, Ry: 0.62500000, 0.33203125 - Unique Gx, Gy: 0.30468750, 0.61328125 - Unique Bx, By: 0.15234375, 0.05078125 - Vendor-Specific Audio Data Block (Dolby), OUI 00-D0-46: - Version: 1 (7 bytes) - Height speaker zone present - Surround speaker zone present - Center speaker zone present YCbCr 4:2:0 Capability Map Data Block: VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz VIC 102: 4096x2160 60.000000 Hz 256:135 135.000 kHz 594.000000 MHz @@ -156,22 +119,8 @@ DTD 3: 1360x768 60.015162 Hz 85:48 47.712 kHz 85.500000 MHz Hfront 64 Hsync 112 Hback 256 Hpol P Vfront 3 Vsync 6 Vback 18 Vpol P -Checksum: 0x4d Unused space in Extension Block: 10 bytes +Checksum: 0x4d ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - IT Video Formats are overscanned by default, but normally this should be underscanned. - Colorimetry Data Block: Set the sRGB colorimetry bit to avoid interop issues. - -Failures: - -Block 0, Base EDID: - Detailed Timing Descriptor #1: Mismatch of image size 800x450 mm vs display size 1220x680 mm. - Detailed Timing Descriptor #2: Mismatch of image size 800x450 mm vs display size 1220x680 mm. -Block 1, CTA-861 Extension Block: - Detailed Timing Descriptor #3: Mismatch of image size vs display size: image size is not set, but display size is. - -EDID conformity: FAIL +EDID conformity: PASS libdisplay-info-0.1.1/test/data/hitachi-55r6+.edid000066400000000000000000000004001437312070200214710ustar00rootroot00000000000000 zDx UR%GJqO@0pZX 1t0pZX 155R6+ K< @cqPa_]fdb" ><, PWgm 0< `h]x FDW2:Fp f!PQ0@p6Mlibdisplay-info-0.1.1/test/data/hitachi-55r6+.print000066400000000000000000000001011437312070200217160ustar00rootroot00000000000000make: Hisense Electric Co., Ltd. model: 55R6+ serial: 0x000001D8 libdisplay-info-0.1.1/test/data/hp-5dq99aa-hdmi.diff000066400000000000000000000032451437312070200220240ustar00rootroot00000000000000--- ref +++ di @@ -60,23 +60,12 @@ VIC 18: 720x576 50.000000 Hz 16:9 31.250 kHz 27.000000 MHz VIC 17: 720x576 50.000000 Hz 4:3 31.250 kHz 27.000000 MHz VIC 1: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - Source physical address: 1.0.0.0 - DC_36bit - DC_30bit - DC_Y444 - Maximum TMDS clock: 310 MHz Video Capability Data Block: YCbCr quantization: No Data RGB quantization: No Data PT scan behavior: Always Underscanned IT scan behavior: Always Underscanned CE scan behavior: Supports both over- and underscan - Vendor-Specific Data Block (AMD), OUI 00-00-1A: - Version: 1.1 - Minimum Refresh Rate: 46 Hz - Maximum Refresh Rate: 75 Hz - Flags 1.x: 0xed (MCCS) Colorimetry Data Block: BT2020cYCC BT2020YCC @@ -104,20 +93,12 @@ DTD 5: 1920x1080 74.972503 Hz 16:9 83.894 kHz 174.500000 MHz (597 mm x 339 mm) Hfront 48 Hsync 32 Hback 80 Hpol P Vfront 3 Vsync 5 Vback 31 Vpol N -Checksum: 0xa3 Unused space in Extension Block: 10 bytes +Checksum: 0xa3 ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - Video Capability Data Block: Set Selectable YCbCr Quantization to avoid interop issues. - Colorimetry Data Block: Set the sRGB colorimetry bit to avoid interop issues. - Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0. - Failures: Block 1, CTA-861 Extension Block: Video Capability Data Block: Set Selectable RGB Quantization to avoid interop issues. - EDID conformity: FAIL libdisplay-info-0.1.1/test/data/hp-5dq99aa-hdmi.edid000066400000000000000000000004001437312070200220070ustar00rootroot00000000000000"6 <"x*cESJ& PT@V^)P0 5US!.Kp HP 27 QD CN49120J6N  -Ig 8>+h.KsZ:q8-@X,EUS!:r8-@,EUS!v4P0 5US!*Dp8'@0 5US!libdisplay-info-0.1.1/test/data/hp-5dq99aa-hdmi.print000066400000000000000000000000611437312070200222410ustar00rootroot00000000000000make: HP Inc. model: HP 27 QD serial: CN49120J6N libdisplay-info-0.1.1/test/data/msi-mag321curv-dp.diff000066400000000000000000000055711437312070200224100ustar00rootroot00000000000000--- ref +++ di @@ -97,30 +97,12 @@ Supported sample sizes (bits): 24 20 16 Speaker Allocation Data Block: FL/FR - Front Left/Right - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - Source physical address: 1.0.0.0 - DC_36bit - DC_30bit - DC_Y444 - Maximum TMDS clock: 300 MHz - Extended HDMI video details: - HDMI VICs: - HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - HDMI VIC 2: 3840x2160 25.000000 Hz 16:9 56.250 kHz 297.000000 MHz - HDMI VIC 3: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz - Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: - Version: 1 - Maximum TMDS Character Rate: 600 MHz - SCDC Present - Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding - Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding Colorimetry Data Block: xvYCC601 xvYCC709 BT2020cYCC BT2020YCC BT2020RGB - Gamut Boundary Description Metadata Profile P0 YCbCr 4:2:0 Capability Map Data Block: VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz VIC 96: 3840x2160 50.000000 Hz 16:9 112.500 kHz 594.000000 MHz @@ -133,7 +115,6 @@ Static metadata type 1 Desired content max luminance: 92 (366.802 cd/m^2) Desired content max frame-average luminance: 92 (366.802 cd/m^2) - Desired content min luminance: 0 (0.000 cd/m^2) Detailed Timing Descriptors: DTD 2: 2560x1440 59.950550 Hz 16:9 88.787 kHz 241.500000 MHz (700 mm x 390 mm) Hfront 48 Hsync 32 Hback 80 Hpol P @@ -144,28 +125,12 @@ DTD 4: 1280x768 59.870228 Hz 5:3 47.776 kHz 79.500000 MHz (700 mm x 390 mm) Hfront 64 Hsync 128 Hback 192 Hpol N Vfront 3 Vsync 7 Vback 20 Vpol P -Checksum: 0x4a Unused space in Extension Block: 5 bytes +Checksum: 0x4a ---------------- -Warnings: - -Block 0, Base EDID: - Display Range Limits: GTF support is deprecated in EDID 1.4. -Block 1, CTA-861 Extension Block: - Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0. -EDID: - Base EDID: Some timings are out of range of the Monitor Ranges: - Vertical Freq: 24.000 - 75.062 Hz (Monitor: 40.000 - 60.000 Hz) - Horizontal Freq: 27.000 - 135.000 kHz (Monitor: 135.000 - 135.000 kHz) - Failures: -Block 0, Base EDID: - Basic Display Parameters & Features: sRGB is signaled, but the chromaticities do not match. Block 1, CTA-861 Extension Block: - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: The HDMI Specification requires EDID 1.3 instead of 1.4. - HDMI VIC Codes must have their CTA-861 VIC equivalents in the VSB. - Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues. - + Colorimetry Data Block: Reserved bits MD0-MD3 must be 0. EDID conformity: FAIL libdisplay-info-0.1.1/test/data/msi-mag321curv-dp.edid000066400000000000000000000004001437312070200223670ustar00rootroot000000000000006i=$F'xťVP&PTqO@Mp>0 5!(<< MAG321CURV DA2A019360041DQ !"_a`# m 8< `g]x\\V^)P0 5!f!VQ0F3!Q0@7!Jlibdisplay-info-0.1.1/test/data/msi-mag321curv-dp.print000066400000000000000000000000701437312070200226210ustar00rootroot00000000000000make: Microstep model: MAG321CURV serial: DA2A019360041 libdisplay-info-0.1.1/test/data/panasonic-mei96a2-dp.diff000066400000000000000000000002231437312070200230420ustar00rootroot00000000000000--- ref +++ di @@ -33,9 +33,4 @@ ---------------- -Warnings: - -Block 0, Base EDID: - Missing Display Product Name. - EDID conformity: PASS libdisplay-info-0.1.1/test/data/panasonic-mei96a2-dp.edid000066400000000000000000000002001437312070200230320ustar00rootroot000000000000004xkeQK'PTd]PX 5dQX 5.>Y^ VVX14T058J10 libdisplay-info-0.1.1/test/data/panasonic-mei96a2-dp.print000066400000000000000000000000761437312070200232740ustar00rootroot00000000000000make: Panasonic Industry Company model: 0x96A2 serial: {null} libdisplay-info-0.1.1/test/data/philips-ftv-490.diff000066400000000000000000000064361437312070200221070ustar00rootroot00000000000000--- ref +++ di @@ -92,13 +92,10 @@ Supported sample rates (kHz): 48 44.1 32 Supports Joint Object Coding AC-4: - Max channels: 1 Supported sample rates (kHz): 48 44.1 - Audio Format Code dependent value: 0 DTS-HD: Max channels: 6 Supported sample rates (kHz): 48 44.1 32 - Audio Format Code dependent value: 0x00 MAT (MLP): Max channels: 8 Supported sample rates (kHz): 48 @@ -106,29 +103,9 @@ Hash calculation not required for object audio PCM or channel-based PCM Speaker Allocation Data Block: FL/FR - Front Left/Right - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - Source physical address: 3.0.0.0 - DC_36bit - DC_30bit - DC_Y444 - Maximum TMDS clock: 300 MHz - Video latency: 83 ms - Audio latency: 83 ms - Extended HDMI video details: - HDMI VICs: - HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - HDMI VIC 2: 3840x2160 25.000000 Hz 16:9 56.250 kHz 297.000000 MHz - HDMI VIC 3: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz - Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: - Version: 1 - Maximum TMDS Character Rate: 600 MHz - SCDC Present - Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding - Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding - Supports Auto Low-Latency Mode Video Capability Data Block: YCbCr quantization: No Data - RGB quantization: Selectable (via AVI Q) + RGB quantization: No Data PT scan behavior: No Data IT scan behavior: Always Underscanned CE scan behavior: Always Overscanned @@ -148,42 +125,12 @@ HDR Dynamic Metadata Data Block: HDR Dynamic Metadata Type 4 Version: 4 - Vendor-Specific Video Data Block (HDR10+), OUI 90-84-8B: - Application Version: 1 - Vendor-Specific Video Data Block (Dolby), OUI 00-D0-46: - Version: 2 (12 bytes) - Supports global dimming - DM Version: 3.x - Backlt Min Luma: 100 cd/m^2 - Interface: Standard + Low-Latency + Low-Latency-HDMI - Supports 10b 12b 444: 10 bit - Target Min PQ v2: 380 (0.27136414 cd/m^2) - Target Max PQ v2: 2640 (385 cd/m^2) - Unique Rx, Ry: 0.64062500, 0.33593750 - Unique Gx, Gy: 0.31640625, 0.60937500 - Unique Bx, By: 0.14453125, 0.04296875 - Vendor-Specific Audio Data Block (Dolby), OUI 00-D0-46: - Version: 1 (7 bytes) - Height speaker zone present - Surround speaker zone present - Center speaker zone present -Checksum: 0x6f Unused space in Extension Block: 2 bytes +Checksum: 0x6f ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - Video Capability Data Block: Set Selectable YCbCr Quantization to avoid interop issues. - Colorimetry Data Block: Set the sRGB colorimetry bit to avoid interop issues. -EDID: - CTA-861: Native progressive resolution of 1920x1080 is smaller than the max preferred progressive resolution 3840x2160. - Failures: -EDID: - Base EDID: Some timings are out of range of the Monitor Ranges: - Vertical Freq: 24.000 - 60.317 Hz (Monitor: 48.000 - 62.000 Hz) - Horizontal Freq: 15.625 - 135.000 kHz (Monitor: 15.000 - 70.000 kHz) - +Block 1, CTA-861 Extension Block: + HDR Dynamic Metadata Data Block: Type 4 length must be 3. EDID conformity: FAIL libdisplay-info-0.1.1/test/data/philips-ftv-490.edid000066400000000000000000000004001437312070200220650ustar00rootroot00000000000000A .Qx c US% GJ!@@@:q8-@X,E*S:r8-@,E*SPhilips FTV 0>F< p}Wa`_^] "!5 P=Wx`]go 08<))`h]xI FDK9%Fpolibdisplay-info-0.1.1/test/data/philips-ftv-490.print000066400000000000000000000001211437312070200223140ustar00rootroot00000000000000make: Philips Consumer Electronics Company model: Philips FTV serial: 0x01010101 libdisplay-info-0.1.1/test/data/samsung-q800t-hdmi2.0.diff000066400000000000000000000063571437312070200230200ustar00rootroot00000000000000--- ref +++ di @@ -103,27 +103,13 @@ FL/FR - Front Left/Right Video Capability Data Block: YCbCr quantization: No Data - RGB quantization: Selectable (via AVI Q) + RGB quantization: No Data PT scan behavior: No Data IT scan behavior: Supports both over- and underscan CE scan behavior: Supports both over- and underscan Colorimetry Data Block: xvYCC601 xvYCC709 - Gamut Boundary Description Metadata Profile P0 - Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - Source physical address: 4.0.0.0 - Supports_AI - DC_36bit - DC_30bit - DC_Y444 - Maximum TMDS clock: 300 MHz - Extended HDMI video details: - HDMI VICs: - HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - HDMI VIC 2: 3840x2160 25.000000 Hz 16:9 56.250 kHz 297.000000 MHz - HDMI VIC 3: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz - HDMI VIC 4: 4096x2160 24.000000 Hz 256:135 54.000 kHz 297.000000 MHz HDR Static Metadata Data Block: Electro optical transfer functions: Traditional gamma - SDR luminance range @@ -136,36 +122,19 @@ VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz VIC 101: 4096x2160 50.000000 Hz 256:135 112.500 kHz 594.000000 MHz VIC 102: 4096x2160 60.000000 Hz 256:135 135.000 kHz 594.000000 MHz - Vendor-Specific Video Data Block (HDR10+), OUI 90-84-8B: - Application Version: 1 Detailed Timing Descriptors: - DTD 3: 1920x1080i 50.000000 Hz 16:9 28.125 kHz 74.250000 MHz (1872 mm x 1053 mm) + DTD 3: 1920x540 i 50.044484 Hz 32:9 28.125 kHz 74.250000 MHz (1872 mm x 1053 mm) Hfront 528 Hsync 44 Hback 148 Hpol P - Vfront 2 Vsync 5 Vback 15 Vpol P Vfront +0.5 Odd Field - Vfront 2 Vsync 5 Vback 15 Vpol P Vback +0.5 Even Field + Vfront 2 Vsync 5 Vback 15 Vpol P DTD 4: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1872 mm x 1053 mm) Hfront 88 Hsync 44 Hback 148 Hpol P Vfront 4 Vsync 5 Vback 36 Vpol P -Checksum: 0x10 Unused space in Extension Block: 11 bytes +Checksum: 0x10 ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - Video Data Block: For improved preferred timing interoperability, set 'Native detailed modes' to 1. - Video Capability Data Block: Set Selectable YCbCr Quantization to avoid interop issues. - Colorimetry Data Block: Set the sRGB colorimetry bit to avoid interop issues. - Failures: -Block 0, Base EDID: - Detailed Timing Descriptor #1: Mismatch of image size 1872x1053 mm vs display size 1650x930 mm. - Detailed Timing Descriptor #2: Mismatch of image size 1872x1053 mm vs display size 1650x930 mm. Block 1, CTA-861 Extension Block: - Detailed Timing Descriptor #3: Mismatch of image size 1872x1053 mm vs display size 1650x930 mm. - Detailed Timing Descriptor #4: Mismatch of image size 1872x1053 mm vs display size 1650x930 mm. -EDID: - Base EDID: The DTD max image size is 1872x1053mm, which is larger than the display size 1650.0x930.0mm. - + Colorimetry Data Block: Reserved bits MD0-MD3 must be 0. EDID conformity: FAIL libdisplay-info-0.1.1/test/data/samsung-q800t-hdmi2.0.edid000066400000000000000000000004001437312070200227740ustar00rootroot00000000000000L->p]x 3PE' HHqOt0pZXPtV^)P0 5Ptx SAMSUNG bPT_ !"]^bd?@, PWgTOn @<  `aefr ,%Pt:q8-@X,EPtlibdisplay-info-0.1.1/test/data/samsung-q800t-hdmi2.0.print000066400000000000000000000001011437312070200232210ustar00rootroot00000000000000make: Samsung Electric Company model: SAMSUNG serial: 0x01000E00 libdisplay-info-0.1.1/test/data/samsung-s27a950d-dp.diff000066400000000000000000000023511437312070200225520ustar00rootroot00000000000000--- ref +++ di @@ -27,8 +27,6 @@ DTD 2: 1920x1080 99.930409 Hz 16:9 113.221 kHz 235.500000 MHz (field sequential R/L, 598 mm x 336 mm) Hfront 48 Hsync 32 Hback 80 Hpol P Vfront 3 Vsync 5 Vback 45 Vpol N - Display Range Limits: - Monitor ranges (GTF): 24-120 Hz V, 26-140 kHz H, max dotclock 300 MHz Display Product Name: 'S27A950D' Extension blocks: 1 Checksum: 0x1b @@ -47,23 +45,12 @@ Supported sample sizes (bits): 24 20 16 Speaker Allocation Data Block: FL/FR - Front Left/Right -Checksum: 0x71 Unused space in Extension Block: 115 bytes +Checksum: 0x71 ---------------- -Warnings: - -Block 0, Base EDID: - Display Range Limits: GTF support is deprecated in EDID 1.4. -Block 1, CTA-861 Extension Block: - Add a Colorimetry Data Block with the sRGB colorimetry bit set to avoid interop issues. - Failures: Block 0, Base EDID: Display Range Limits: GTF can't be combined with non-continuous frequencies. -Block 1, CTA-861 Extension Block: - Required 640x480p60 timings are missing in the established timings and the SVD list (VIC 1). - Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues. - EDID conformity: FAIL libdisplay-info-0.1.1/test/data/samsung-s27a950d-dp.edid000066400000000000000000000004001437312070200225400ustar00rootroot00000000000000L-<"x"`AVJ%PTop8@@0 5VP!^[p85@0 5VP!Zx S27A950D  # qlibdisplay-info-0.1.1/test/data/samsung-s27a950d-dp.print000066400000000000000000000000761437312070200230000ustar00rootroot00000000000000make: Samsung Electric Company model: S27A950D serial: {null} libdisplay-info-0.1.1/test/data/sun-gh19ps-dvi.diff000066400000000000000000000010351437312070200220140ustar00rootroot00000000000000--- ref +++ di @@ -50,15 +50,4 @@ ---------------- -Warnings: - -Block 0, Base EDID: - Standard Timings: Standard Timing 1152x921 has a dubious odd vertical resolution. - Detailed Timing Descriptor #1: DTD is similar but not identical to DMT 0x23. -EDID: - Base EDID: Some timings are out of range of the Monitor Ranges: - Vertical Freq: 56.250 - 76.000 Hz (Monitor: 56.000 - 75.000 Hz) - Maximum Clock: 141.822 MHz (Monitor: 140.000 MHz) - Could be due to a Monitor Range off-by-one rounding issue - EDID conformity: PASS libdisplay-info-0.1.1/test/data/sun-gh19ps-dvi.edid000066400000000000000000000002001437312070200220020ustar00rootroot00000000000000NMR &xTL&PTq0*Q*@0p|,8KQ GH19PS 0432MR0406 !libdisplay-info-0.1.1/test/data/sun-gh19ps-dvi.print000066400000000000000000000001031437312070200222330ustar00rootroot00000000000000make: Sun Electronics Corporation model: GH19PS serial: 0432MR0406 libdisplay-info-0.1.1/test/data/viewsonic-vp2768-dp.diff000066400000000000000000000040471437312070200227050ustar00rootroot00000000000000--- ref +++ di @@ -104,10 +104,9 @@ DTD 2: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (597 mm x 336 mm) Hfront 88 Hsync 44 Hback 148 Hpol P Vfront 4 Vsync 5 Vback 36 Vpol P - DTD 3: 1920x1080i 60.000000 Hz 16:9 33.750 kHz 74.250000 MHz (597 mm x 336 mm) + DTD 3: 1920x540 i 60.053381 Hz 32:9 33.750 kHz 74.250000 MHz (597 mm x 336 mm) Hfront 88 Hsync 44 Hback 148 Hpol P - Vfront 2 Vsync 5 Vback 15 Vpol P Vfront +0.5 Odd Field - Vfront 2 Vsync 5 Vback 15 Vpol P Vback +0.5 Even Field + Vfront 2 Vsync 5 Vback 15 Vpol P DTD 4: 1280x720 60.000000 Hz 16:9 45.000 kHz 74.250000 MHz (597 mm x 336 mm) Hfront 110 Hsync 40 Hback 220 Hpol P Vfront 5 Vsync 5 Vback 20 Vpol P @@ -117,28 +116,14 @@ DTD 6: 1920x1080 50.000000 Hz 16:9 56.250 kHz 148.500000 MHz (597 mm x 336 mm) Hfront 528 Hsync 44 Hback 148 Hpol P Vfront 4 Vsync 5 Vback 36 Vpol P -Checksum: 0xa6 Unused space in Extension Block: 10 bytes +Checksum: 0xa6 ---------------- -Warnings: - -Block 1, CTA-861 Extension Block: - Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0. -EDID: - Base EDID: Some timings are out of range of the Monitor Ranges: - Vertical Freq: 24.000 - 75.062 Hz (Monitor: 50.000 - 75.000 Hz) - Failures: Block 0, Base EDID: - Basic Display Parameters & Features: sRGB is signaled, but the chromaticities do not match. Display Range Limits: Reserved bits of byte 14 are non-zero. Display Range Limits: Reserved bits of byte 15 are non-zero. Display Range Limits: Reserved bits of byte 16 are non-zero. -Block 1, CTA-861 Extension Block: - Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues. -EDID: - CTA-861: Native progressive timings are a mix of several resolutions. - EDID conformity: FAIL libdisplay-info-0.1.1/test/data/viewsonic-vp2768-dp.edid000066400000000000000000000004001437312070200226670ustar00rootroot00000000000000Zc4 <"x?feTL&PT@V^)P0 5UP!UY5171500307 2KZ ;<VP2768 SeriesN !"# :q8-@X,EUP!q X,%UP!rQ n(UUP! Њ ->UP!:r8-@,EUP!libdisplay-info-0.1.1/test/data/viewsonic-vp2768-dp.print000066400000000000000000000001061437312070200231210ustar00rootroot00000000000000make: ViewSonic Corporation model: VP2768 Series serial: UY5171500307 libdisplay-info-0.1.1/test/di-edid-print.c000066400000000000000000000022211437312070200203460ustar00rootroot00000000000000#include #include #include #include static const char * str_or_null(const char *str) { return str ? str : "{null}"; } static void print_info(const struct di_info *info) { char *str; str = di_info_get_make(info); printf("make: %s\n", str_or_null(str)); free(str); str = di_info_get_model(info); printf("model: %s\n", str_or_null(str)); free(str); str = di_info_get_serial(info); printf("serial: %s\n", str_or_null(str)); free(str); } int main(int argc, char *argv[]) { FILE *in; static uint8_t raw[32 * 1024]; size_t size = 0; struct di_info *info; in = stdin; if (argc > 1) { in = fopen(argv[1], "r"); if (!in) { perror("failed to open input file"); return 1; } } while (!feof(in)) { size += fread(&raw[size], 1, sizeof(raw) - size, in); if (ferror(in)) { perror("fread failed"); return 1; } else if (size >= sizeof(raw)) { fprintf(stderr, "input too large\n"); return 1; } } fclose(in); info = di_info_parse_edid(raw, size); if (!info) { perror("di_edid_parse failed"); return 1; } print_info(info); di_info_destroy(info); return 0; } libdisplay-info-0.1.1/test/edid-decode-check.sh000077500000000000000000000005671437312070200213240ustar00rootroot00000000000000#!/bin/sh -eu workdir="$(mktemp -d)" cleanup() { rm -rf "$workdir" } trap cleanup EXIT edid="$1" diff="${edid%.edid}.diff" "$REF_EDID_DECODE" --skip-hex-dump --check --skip-sha <"$edid" >"$workdir/ref" || [ $? = 254 ] "$DI_EDID_DECODE" <"$edid" >"$workdir/di" || [ $? = 254 ] if [ -f "$diff" ]; then patch "$workdir/ref" "$diff" fi diff -u "$workdir/ref" "$workdir/di" libdisplay-info-0.1.1/test/edid-decode-diff.sh000077500000000000000000000013201437312070200211430ustar00rootroot00000000000000#!/bin/sh -eu REF_EDID_DECODE="${REF_EDID_DECODE:-edid-decode}" BUILDDIR="${BUILDDIR:-./build}" DI_EDID_DECODE="${DI_EDID_DECODE:-${BUILDDIR}/di-edid-decode/di-edid-decode}" DI_EDID_PRINT="${DI_EDID_PRINT:-${BUILDDIR}/test/di-edid-print}" workdir="$(mktemp -d)" cleanup() { rm -rf "$workdir" } trap cleanup EXIT for edid in "$@"; do diff="${edid%.edid}.diff" "$REF_EDID_DECODE" --skip-hex-dump --check --skip-sha <"$edid" >"$workdir/ref" || [ $? = 254 ] "$DI_EDID_DECODE" <"$edid" >"$workdir/di" || [ $? = 254 ] if ! diff -u --label ref "$workdir/ref" --label di "$workdir/di" >"$workdir/diff"; then cp "$workdir/diff" "$diff" else rm -f "$diff" fi "$DI_EDID_PRINT" <"$edid" >"${edid%.edid}.print" done libdisplay-info-0.1.1/test/edid-print-check.sh000077500000000000000000000003211437312070200212210ustar00rootroot00000000000000#!/bin/sh -eu workdir="$(mktemp -d)" cleanup() { rm -rf "$workdir" } trap cleanup EXIT edid="$1" ref="${edid%.edid}.print" "$DI_EDID_PRINT" <"$edid" >"$workdir/printout" diff -u "$ref" "$workdir/printout" libdisplay-info-0.1.1/test/meson.build000066400000000000000000000027701437312070200177240ustar00rootroot00000000000000ref_edid_decode = find_program('edid-decode', native: true, required: false) if not ref_edid_decode.found() test('edid-decode-not-found', find_program('false')) subdir_done() endif test_harness = find_program('./edid-decode-check.sh', native: true) di_edid_print = executable( 'di-edid-print', 'di-edid-print.c', dependencies: di_dep, install: false, ) print_harness = find_program('./edid-print-check.sh', native: true) test_cases = [ 'acer-p1276', 'apple-xdr-dp', 'custom-uncommon-cta-vesa', 'dell-2408wfp-dp', 'goldstar-ite6604-hdmi', 'hp-5dq99aa-hdmi', 'msi-mag321curv-dp', 'panasonic-mei96a2-dp', 'samsung-s27a950d-dp', 'sun-gh19ps-dvi', 'viewsonic-vp2768-dp', 'cvt', 'philips-ftv-490', 'samsung-q800t-hdmi2.0', 'cta-timings', 'hitachi-55r6+', ] test_env = [ 'REF_EDID_DECODE=' + ref_edid_decode.full_path(), 'DI_EDID_DECODE=' + di_edid_decode.full_path(), 'DI_EDID_PRINT=' + di_edid_print.full_path(), ] foreach tc : test_cases test( 'decode-' + tc, test_harness, args: [files('data/' + tc + '.edid')], env: test_env, depends: [di_edid_decode], ) test( 'print-' + tc, print_harness, args: [files('data/' + tc + '.edid')], env: test_env, depends: [di_edid_print], ) endforeach test_gen = find_program('./edid-decode-diff.sh', native: true) gen_targets = [] foreach tc : test_cases gen_targets += files('data/' + tc + '.edid') endforeach run_target( 'gen-test-data', command: [test_gen] + gen_targets, depends: [di_edid_decode, di_edid_print], env: test_env, ) libdisplay-info-0.1.1/tool/000077500000000000000000000000001437312070200155525ustar00rootroot00000000000000libdisplay-info-0.1.1/tool/gen-cta-vic.py000077500000000000000000000106131437312070200202250ustar00rootroot00000000000000#!/usr/bin/env python3 import os import subprocess import sys if len(sys.argv) != 2: print("usage: gen-cta-vic.py ", file=sys.stderr) sys.exit(1) in_path = sys.argv[1] in_basename = os.path.basename(in_path) tool_dir = os.path.dirname(os.path.realpath(__file__)) out_path = tool_dir + "/../cta-vic-table.c" # Page numbers for CTA-861-H pages = { "timing": (41, 43), "sync": (44, 46), "aspect_ratio": (55, 58), } def extract_pages(page_range): pages = [] page = "" cmd = ["pdftotext", "-f", str(page_range[0]), "-l", str(page_range[1]), "-layout", in_path, "-"] for l in subprocess.check_output(cmd, text=True): if l.startswith("\f"): pages.append(page) page = l[1:] else: page += l return pages def extract_table(page): lines = [l.strip() for l in page.splitlines()] rows = [] for l in lines: fields = [field.strip() for field in l.split(" ") if field != ""] rows.append(fields) return rows def parse_vic_list(s): return [int(vic.strip()) for vic in s.split(",")] def parse_hactive(s): # Some hactive pixel values have a footnote marker if s == "14402" or s == "28802": s = s[:-1] return int(s) def parse_interlaced(s): if s == "Prog": return "false" elif s == "Int": return "true" else: assert(False) def parse_timing_table(page, format_table): assert("Table 1 - Video Format Timings — Detailed Timing Information" in page) for fields in extract_table(page): if len(fields) != 11 or fields[0] == "VIC": continue for vic in parse_vic_list(fields[0]): format_table[vic] = { "vic": vic, "h_active": parse_hactive(fields[1]), "v_active": int(fields[2]), "interlaced": parse_interlaced(fields[3]), "pixel_clock_hz": int(float(fields[10]) * 1000 * 1000), } def parse_polarity(pol): if pol == "P": return "POSITIVE" elif pol == "N": return "NEGATIVE" else: assert(False) def parse_sync_table(page, format_table): assert("Table 2 - Video Format Timings — Detailed Sync Information" in page) for fields in extract_table(page): if len(fields) < 12: continue for vic in parse_vic_list(fields[0]): fmt = format_table[vic] fmt["h_front"] = int(fields[2]) fmt["h_sync"] = int(fields[3]) fmt["h_back"] = int(fields[4]) fmt["h_sync_polarity"] = "DI_CTA_VIDEO_FORMAT_SYNC_" + parse_polarity(fields[5]) fmt["v_front"] = int(fields[6]) fmt["v_sync"] = int(fields[7]) fmt["v_back"] = int(fields[8]) fmt["v_sync_polarity"] = "DI_CTA_VIDEO_FORMAT_SYNC_" + parse_polarity(fields[9]) def parse_aspect_ratio_table(page, format_table): assert("Table 3 - Video Formats — Video ID Code and Aspect Ratios" in page) for fields in extract_table(page): if len(fields) != 5: continue vic = int(fields[0]) fmt = format_table[vic] pic_ar = fields[3] if pic_ar == "64:276": # 64:27 has a footnote pic_ar = pic_ar[:-1] fmt["picture_aspect_ratio"] = "DI_CTA_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_" + pic_ar.replace(":", "_") format_table = {} for page in extract_pages(pages["timing"]): parse_timing_table(page, format_table) for page in extract_pages(pages["sync"]): parse_sync_table(page, format_table) for page in extract_pages(pages["aspect_ratio"]): parse_aspect_ratio_table(page, format_table) max_vic = 0 for vic in format_table: if vic > max_vic: max_vic = vic # Sanity check for vic in format_table: fmt = format_table[vic] assert("h_sync" in fmt) assert("picture_aspect_ratio" in fmt) with open(out_path, "w+") as f: f.write("/* DO NOT EDIT! This file has been generated by gen-cta-vic.py from {}. */\n\n".format(in_basename)) f.write('#include "cta.h"\n\n') f.write("const struct di_cta_video_format _di_cta_video_formats[] = {\n") for vic in format_table: f.write("\t[{}] = {{\n".format(vic)) for k, v in format_table[vic].items(): f.write("\t\t.{} = {},\n".format(k, v)) f.write("\t},\n") f.write("};\n\n") f.write("const size_t _di_cta_video_formats_len = {};\n".format(max_vic + 1)) libdisplay-info-0.1.1/tool/gen-dmt.py000077500000000000000000000113711437312070200174650ustar00rootroot00000000000000#!/usr/bin/env python3 import os import subprocess import sys def parse_hex_byte(s): assert(s.endswith("h")) s = s[:-1] assert(0 <= int(s, 16) <= 255) return "0x" + s def parse_hex_list(s): if s == "n/a": return None if s.startswith("("): assert(s.endswith(")h")) s = s[1:-2] l = [b.strip() for b in s.split(",")] l = [b[:-1] if b.endswith("h") else b for b in l] for b in l: assert(0 <= int(b, 16) <= 255) return "0x" + "".join(l) def parse_pixel_param(l): l = l[2].split(" ") assert(len(l) >= 2 and l[1] == "Pixels") return int(l[0]) def parse_line_param(l): l = l[1].split(" ") assert(len(l) >= 2 and l[1] == "lines") return int(l[0]) def parse_timing(page): lines = [l.strip() for l in page.splitlines()] if len(lines) < 6: return None try: i = lines.index("Detailed Timing Parameters") except ValueError: return None raw_metadata = lines[1:i] raw_params = lines[i+1:] metadata = {} for l in raw_metadata: if not ": " in l: continue k, v = l.split(":", 1) metadata[k.strip()] = v.strip() assert("Resolution" in metadata and "EDID ID" in metadata) if "Proposed" in metadata and not "Adopted" in metadata: print("skipping proposed but not adopted timing: " + metadata["Resolution"], file=sys.stderr) return None params = {} for l in raw_params: if not " = " in l: continue tokens = [l.strip() for l in l.split("=")] params[tokens[0]] = tokens[1:] assert("Timing Name" in params) res = metadata["Resolution"] size, rest = res.split(" at ", 1) horiz_video, vert_video = size.split("x", 1) refresh_rate_hz, rest = rest.split(" Hz ", 1) reduced_blanking = "reduced blanking" in rest.lower() horiz_video = int(horiz_video.strip()) vert_video = int(vert_video.strip()) refresh_rate_hz = float(refresh_rate_hz.strip()) ids = {} for kv in metadata["EDID ID"].split(";"): k, v = kv.split(":") ids[k.strip()] = v.strip() assert("DMT ID" in ids) dmt_id = parse_hex_byte(ids["DMT ID"]) edid_std_id = parse_hex_list(ids["Std. 2 Byte Code"]) cvt_id = parse_hex_list(ids["CVT 3 Byte Code"]) pixel_clock_mhz = float(params["Pixel Clock"][0].split(";")[0]) horiz_blank = parse_pixel_param(params["Hor Blank Time"]) horiz_front_porch = parse_pixel_param(params["// H Front Porch"]) horiz_sync_pulse = parse_pixel_param(params["Hor Sync Time"]) horiz_border = parse_pixel_param(params["// H Right Border"]) assert(horiz_border == parse_pixel_param(params["// H Left Border"])) vert_blank = parse_line_param(params["Ver Blank Time"]) vert_front_porch = parse_line_param(params["// V Front Porch"]) vert_sync_pulse = parse_line_param(params["Ver Sync Time"]) vert_border = parse_line_param(params["// V Bottom Border"]) assert(vert_border == parse_line_param(params["// V Top Border"])) return { "dmt_id": dmt_id, "edid_std_id": 0 if edid_std_id is None else edid_std_id, "cvt_id": 0 if cvt_id is None else cvt_id, "horiz_video": horiz_video, "vert_video": vert_video, "refresh_rate_hz": refresh_rate_hz, "pixel_clock_hz": int(pixel_clock_mhz * 1000 * 1000), "horiz_blank": horiz_blank, "horiz_front_porch": horiz_front_porch, "horiz_sync_pulse": horiz_sync_pulse, "horiz_border": horiz_border, "vert_blank": vert_blank, "vert_front_porch": vert_front_porch, "vert_sync_pulse": vert_sync_pulse, "vert_border": vert_border, "reduced_blanking": "true" if reduced_blanking else "false", } if len(sys.argv) != 2: print("usage: gen-dmt.py ", file=sys.stderr) sys.exit(1) in_path = sys.argv[1] in_basename = os.path.basename(in_path) tool_dir = os.path.dirname(os.path.realpath(__file__)) out_path = tool_dir + "/../dmt-table.c" cmd = ["pdftotext", "-nodiag", "-layout", in_path, "-"] page = "" timings = [] for l in subprocess.check_output(cmd, text=True): if l.startswith("\f"): t = parse_timing(page) if t is not None: timings.append(t) page = l[1:] else: page += l with open(out_path, "w+") as f: f.write("/* DO NOT EDIT! This file has been generated by gen-dmt.py from {}. */\n\n".format(in_basename)) f.write('#include "dmt.h"\n\n') f.write("const struct di_dmt_timing _di_dmt_timings[] = {\n") for t in timings: f.write("\t{\n") for k, v in t.items(): f.write("\t\t.{} = {},\n".format(k, v)) f.write("\t},\n") f.write("};\n\n") f.write("const size_t _di_dmt_timings_len = {};\n".format(len(timings))) libdisplay-info-0.1.1/tool/gen-search-table.py000077500000000000000000000045231437312070200212340ustar00rootroot00000000000000#!/usr/bin/env python3 """ string to string mapping table License: MIT Copyright (c) 2020 Simon Ser Copyright 2022 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 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. """ import sys def escape_for_c(s): l = [c if c.isalnum() or c in ' .,' else '\\%03o' % ord(c) for c in s] return ''.join(l) if len(sys.argv) != 4: print('usage: ' + sys.argv[0] + ' ', file=sys.stderr) sys.exit(1) infile = sys.argv[1] outfile = sys.argv[2] ident = sys.argv[3] records = {} with open(infile, 'r') as f: for line in f: [pnpid, name] = line.split(maxsplit=1) if len(pnpid) != 3: print("Warning: skipping invalid PNP ID %s" % (repr(pnpid)), file=sys.stderr) continue records[pnpid] = escape_for_c(name.strip()) with open(outfile, 'w') as f: f.write( f''' #include #include const char * {ident}(const char *key); const char * {ident}(const char *key) {{ size_t len = strlen(key); size_t i; uint32_t u = 0; if (len > 4) return NULL; for (i = 0; i < len; i++) u = (u << 8) | (uint8_t)key[i]; switch (u) {{ ''') for id in sorted(records.keys()): u = 0 for c in id: u = (u << 8) | ord(c) f.write(f' case {u}: return "{records[id]}";\n') f.write( f''' default: return NULL; }} }} ''')