The recipe was not building libdisplay-info. It synthesized its own
meson.build declaring `version: '0.2.3'` and compiled a hand-written
stub: one di.c and four headers, 518 lines, 20 exported functions.
0.2.3 does not exist upstream -- the tags are 0.1.0, 0.1.1, 0.2.0, 0.3.0
and 0.4.0. That fabricated version satisfied kwin's
pkg_check_modules(libdisplayinfo REQUIRED IMPORTED_TARGET libdisplay-info>=0.2.0)
so kwin configured cleanly and then failed to compile utils/edid.cpp on
di_info_get_default_color_primaries, di_info_get_hdr_static_metadata,
di_info_get_supported_signal_colorimetry and di_edid_display_descriptor.
A version gate satisfied by a declaration rather than an implementation.
Now vendors upstream 0.4.0 (gitlab.freedesktop.org/emersion/libdisplay-info):
14 C files, 10898 lines, 10 headers, 94 exported symbols. 0.4.0 rather than
0.2.0 because the colorimetry and HDR static metadata accessors kwin needs
post-date the 0.2.x series -- and they feed the real colour-primaries and
HDR path of the display stack, so a stub returning NULL would have been
wrong at runtime even if it had compiled.
Red Bear delta is one hunk in source/meson.build: the unconditional
subdir('di-edid-decode') and subdir('test') are commented out. Both build
auxiliary executables and a shell test harness that are not part of the
runtime and do not cross-compile for Redox. Library, headers and
pkg-config are untouched.
Verified: builds clean, stages libdisplay-info.so.0.4.0 with all four
symbols kwin needs, pkg-config reports 0.4.0, and kwin's utils/edid.cpp
now compiles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,55 +2,40 @@
|
||||
path = "source"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
script = '''
|
||||
DYNAMIC_INIT
|
||||
|
||||
python3 - <<'PY' "${COOKBOOK_SOURCE}"
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
source_root = Path(sys.argv[1])
|
||||
meson_build = source_root / "meson.build"
|
||||
|
||||
if not meson_build.exists():
|
||||
meson_build.write_text(
|
||||
"""project('libdisplay-info', 'c',
|
||||
version: '0.2.3',
|
||||
meson_version: '>= 0.54.0',
|
||||
default_options: ['warning_level=1', 'buildtype=debugoptimized'])
|
||||
|
||||
inc = include_directories('include')
|
||||
|
||||
libdisplay_info = shared_library('display-info',
|
||||
'di.c',
|
||||
include_directories: inc,
|
||||
version: meson.project_version(),
|
||||
install: true)
|
||||
|
||||
install_headers(
|
||||
'include/libdisplay-info/cta.h',
|
||||
'include/libdisplay-info/displayid.h',
|
||||
'include/libdisplay-info/edid.h',
|
||||
'include/libdisplay-info/info.h',
|
||||
subdir: 'libdisplay-info',
|
||||
)
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(
|
||||
libdisplay_info,
|
||||
name: 'libdisplay-info',
|
||||
description: 'Display identification data parsing library',
|
||||
filebase: 'libdisplay-info',
|
||||
version: meson.project_version(),
|
||||
)
|
||||
"""
|
||||
)
|
||||
PY
|
||||
|
||||
cookbook_meson
|
||||
'''
|
||||
template = "meson"
|
||||
|
||||
[package]
|
||||
version = "0.2.3"
|
||||
description = "libdisplay-info — EDID and display descriptor parsing library. Real C implementation (v6.0 2026) handling base EDID vendor/product, strings, physical size, chromaticity, detailed timings, and preferred-timing metadata. CTA/DisplayID remain unsupported (bounded by Red Bear's current DRM/KMS validation surface). Replaces the prior libdisplay-info-stub that always returned NULL."
|
||||
version = "0.4.0+rb0.3.2"
|
||||
description = """
|
||||
libdisplay-info — EDID and DisplayID parsing library.
|
||||
|
||||
Upstream libdisplay-info 0.4.0 (gitlab.freedesktop.org/emersion/libdisplay-info),
|
||||
vendored under Red Bear's local-fork convention.
|
||||
|
||||
This REPLACES a hand-written stub. The stub was 518 lines across one di.c and
|
||||
four headers, exposing 20 functions, and it synthesized its own meson.build
|
||||
declaring `version: '0.2.3'` -- a version that does not exist upstream (the tags
|
||||
are 0.1.0, 0.1.1, 0.2.0, 0.3.0, 0.4.0). That fabricated version satisfied
|
||||
kwin's `pkg_check_modules(libdisplay-info>=0.2.0)` while the API behind it was
|
||||
absent, so kwin configured successfully and then failed to compile:
|
||||
|
||||
utils/edid.cpp: 'di_info_get_default_color_primaries' was not declared
|
||||
utils/edid.cpp: 'di_info_get_hdr_static_metadata' was not declared
|
||||
utils/edid.cpp: 'di_info_get_supported_signal_colorimetry' was not declared
|
||||
utils/edid.cpp: 'di_edid_display_descriptor' does not name a type
|
||||
|
||||
Those four are why 0.4.0 rather than 0.2.0: the colorimetry and HDR static
|
||||
metadata accessors kwin needs post-date the 0.2.x series. They feed the real
|
||||
colour-primaries and HDR path of the display stack, so a stub returning NULL
|
||||
would have been wrong at runtime even had it compiled.
|
||||
|
||||
Red Bear delta (one hunk, in source/meson.build): the unconditional
|
||||
`subdir('di-edid-decode')` and `subdir('test')` are commented out. Both build
|
||||
auxiliary executables and a shell test harness that are not part of the runtime
|
||||
and do not cross-compile cleanly for Redox. The library, its headers and its
|
||||
pkg-config file are unaffected.
|
||||
|
||||
The pnp-id table is generated at build time by tool/gen-search-table.py from
|
||||
hwdata's pnp.ids, resolved via the host's `hwdata` pkg-config (native: true),
|
||||
falling back to /usr/share/hwdata/pnp.ids.
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
root = 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 = 100
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -0,0 +1,158 @@
|
||||
include:
|
||||
- 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: '2025-01-20.0'
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_PIPELINE_SOURCE == 'push'
|
||||
|
||||
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
|
||||
rules:
|
||||
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
|
||||
when: always
|
||||
- when: never
|
||||
|
||||
prepare-container:
|
||||
extends: .fdo.container-build@alpine@x86_64
|
||||
stage: "Prepare container"
|
||||
variables:
|
||||
V4L_UTILS_COMMIT: 1316a80455ef70889bea89491f37cd69170f3ee7
|
||||
FDO_BASE_IMAGE: alpine:latest
|
||||
FDO_DISTRIBUTION_PACKAGES: |
|
||||
build-base clang compiler-rt meson make git gcovr py3-pygments go hwdata
|
||||
perl argp-standalone gettext-dev linux-headers
|
||||
FDO_DISTRIBUTION_EXEC: |
|
||||
grep $V4L_UTILS_COMMIT ./subprojects/v4l-utils.wrap >/dev/null || \
|
||||
(echo "Keep v4l-utils.wrap and CI in sync!" && exit 1)
|
||||
git clone https://git.linuxtv.org/v4l-utils.git
|
||||
cd v4l-utils
|
||||
git checkout $V4L_UTILS_COMMIT
|
||||
meson setup build/ \
|
||||
-Dauto_features=disabled \
|
||||
-Dv4l-plugins=false \
|
||||
-Dv4l-wrappers=false \
|
||||
-Dv4l2-compliance-libv4l=false \
|
||||
-Dv4l2-ctl-libv4l=false \
|
||||
-Dv4l2-ctl-stream-to=false
|
||||
ninja -C build/
|
||||
ninja -C build/ 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
|
||||
|
||||
testing-check:
|
||||
extends: .fdo.distribution-image@alpine
|
||||
stage: "Build and test"
|
||||
script:
|
||||
- |
|
||||
# Check if the origin of all test EDIDs is documented
|
||||
for f in ./test/data/*.edid; do
|
||||
grep -q "^$(basename ${f%.edid})\s" ./test/data/README.md ||
|
||||
(echo "$f not in README.md" && exit 1)
|
||||
done
|
||||
# Check if the test references checked in the repo match what we have in CI
|
||||
meson setup build/
|
||||
ninja -C build/ gen-test-data
|
||||
git diff --quiet ||
|
||||
(echo "Checked in reference output does not match generated reference output" && exit 1)
|
||||
rules:
|
||||
- when: on_success
|
||||
|
||||
build-gcc:
|
||||
extends: .fdo.distribution-image@alpine
|
||||
stage: "Build and test"
|
||||
script:
|
||||
- CC=gcc
|
||||
CFLAGS=" -Wnull-dereference -fanalyzer -O1"
|
||||
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/
|
||||
- UBSAN_OPTIONS=halt_on_error=1 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
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT 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.
|
||||
@@ -0,0 +1,141 @@
|
||||
# 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]. Discuss and ask
|
||||
questions in the [#wayland] IRC channel on OFTC.
|
||||
|
||||
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].
|
||||
|
||||
## Specifications
|
||||
|
||||
Both EDID and DisplayID are defined by VESA and the specifications are publicly
|
||||
available. There are multiple versions defined and they have multiple
|
||||
mechanisms for extending the base specification. Some of the extensions also
|
||||
have mechanisms for additional extensions. This sometimes makes it hard to find
|
||||
where a particular data structure is defined.
|
||||
|
||||
The raw data is usually read from the Display Data Channel (DDC) but other
|
||||
methods of delivery exist.
|
||||
|
||||
Available freely [from VESA directly] are the base EDID specifications, VESA
|
||||
specified EDID extensions, as well as the base DisplayID specifications. EDID
|
||||
also specifies how DisplayID can be embedded in EDID.
|
||||
|
||||
* VESA Enhanced Extended Display Identification Data (E-EDID) Standard;
|
||||
Release A, Revision 1; February 9, 2000; Defines EDID Structure Version 1,
|
||||
Revision 3
|
||||
* VESA Enhanced Extended Display Identification Data (E-EDID) Standard;
|
||||
Release A, Revision 2; September 25, 2006; Defines EDID Structure Version 1,
|
||||
Revision 4
|
||||
* VESA Video Timing Block Extension Data (VTB-EXT) Standard; Release A;
|
||||
November 24, 2003
|
||||
* VESA Enhanced EDID Localized String Extension Standard; Release A;
|
||||
July 10, 2003
|
||||
* VESA Display Transfer Characteristics Data Block (DTCDB) Standard;
|
||||
Version 1.0; August 31, 2006
|
||||
* VESA Display Information Extension Block (DI-EXT) Standard; Release A;
|
||||
August 21, 2001
|
||||
* VESA Display Device Data Block (DDDB) Standard; Version 1;
|
||||
September 25, 2006
|
||||
* VESA Display Identification Data (DisplayID) Standard; Version 1.3;
|
||||
July 5, 2013
|
||||
* VESA Display Identification Data (DisplayID) Standard; Version 2.0;
|
||||
11 September, 2017
|
||||
* VESA Display Identification Data (DisplayID) Standard; Version 2.1;
|
||||
18 November, 2021
|
||||
* VESA Display Identification Data (DisplayID) Standard; Version 2.1a;
|
||||
18 March, 2024
|
||||
|
||||
The most common extension to both EDID and DisplayID is CTA-861 which can be
|
||||
downloaded for free [from CTA].
|
||||
|
||||
* A DTV Profile for Uncompressed High Speed Digital Interfaces
|
||||
(CTA-861-E, CTA-861-F, CTA-861-G, CTA-861-H, CTA-861-I)
|
||||
* HDR Static Metadata Extensions (CTA-861.3-A)
|
||||
|
||||
The CTA-861 specification allows for Vendor Specific Data Blocks (VSDB). Their
|
||||
specifications are often proprietary and have to be reverse engineered. Some
|
||||
specifications are available publicly.
|
||||
|
||||
* HDMI and HDMI Forum VSDB are specified in the HDMI specification
|
||||
* Microsoft EDID Extension for [HMDs and Specialized Monitors]
|
||||
|
||||
The libdisplay-info structures of the low-level API always point to the
|
||||
relevant specification and section therein.
|
||||
|
||||
## 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, build `edid-decode` at the Git revision mentioned in
|
||||
`.gitlab-ci.yml`, put the executable in `PATH`, and run
|
||||
`ninja -C build/ gen-test-data`.
|
||||
|
||||
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]: ircs://irc.oftc.net/#wayland
|
||||
[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/
|
||||
[from VESA directly]: https://vesa.org/vesa-standards/
|
||||
[from CTA]: https://www.cta.tech/SearchResults?search=CTA-861
|
||||
[HMDs and Specialized Monitors]: https://learn.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension
|
||||
[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/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
#include <libdisplay-info/cta-vic.h>
|
||||
|
||||
extern const struct di_cta_vic_video_format _di_cta_vic_video_formats[];
|
||||
extern const size_t _di_cta_vic_video_formats_len;
|
||||
|
||||
const struct di_cta_vic_video_format *
|
||||
di_cta_vic_video_format_from_vic(struct di_cta_vic vic)
|
||||
{
|
||||
if (vic.code >= _di_cta_vic_video_formats_len ||
|
||||
_di_cta_vic_video_formats[vic.code].vic.code == 0)
|
||||
return NULL;
|
||||
return &_di_cta_vic_video_formats[vic.code];
|
||||
}
|
||||
|
||||
struct di_cta_vic
|
||||
di_cta_vic_video_format_to_vic(const struct di_cta_vic_video_format *format)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < _di_cta_vic_video_formats_len; i++) {
|
||||
const struct di_cta_vic_video_format *candidate = &_di_cta_vic_video_formats[i];
|
||||
|
||||
if (candidate->vic.code != 0 &&
|
||||
candidate->h_active == format->h_active &&
|
||||
candidate->v_active == format->v_active &&
|
||||
candidate->h_sync == format->h_sync &&
|
||||
candidate->v_sync == format->v_sync &&
|
||||
candidate->h_back == format->h_back &&
|
||||
candidate->v_back == format->v_back &&
|
||||
candidate->h_sync_polarity == format->h_sync_polarity &&
|
||||
candidate->v_sync_polarity == format->v_sync_polarity &&
|
||||
candidate->pixel_clock_hz == format->pixel_clock_hz &&
|
||||
candidate->interlaced == format->interlaced &&
|
||||
candidate->picture_aspect_ratio == format->picture_aspect_ratio)
|
||||
return candidate->vic;
|
||||
}
|
||||
|
||||
return (struct di_cta_vic) { .code = 0 };
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
|
||||
#include <libdisplay-info/cvt.h>
|
||||
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
}
|
||||
@@ -1,401 +0,0 @@
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/libdisplay-info/info.h"
|
||||
|
||||
#define EDID_BLOCK_SIZE 128
|
||||
#define EDID_DESCRIPTOR_COUNT 4
|
||||
#define EDID_DESCRIPTOR_OFFSET 54
|
||||
|
||||
struct di_edid {
|
||||
struct di_edid_vendor_product vendor_product;
|
||||
struct di_edid_chromaticity_coords chromaticity;
|
||||
struct di_edid_screen_size screen_size;
|
||||
struct di_edid_misc_features misc_features;
|
||||
bool has_chromaticity;
|
||||
struct di_edid_detailed_timing_def *detailed_timing_storage;
|
||||
const struct di_edid_detailed_timing_def **detailed_timings;
|
||||
const struct di_edid_ext **extensions;
|
||||
};
|
||||
|
||||
struct di_info {
|
||||
struct di_edid edid;
|
||||
char *model;
|
||||
char *serial;
|
||||
};
|
||||
|
||||
static const uint8_t edid_header[8] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
||||
static const struct di_edid_detailed_timing_def *empty_detailed_timings[] = { NULL };
|
||||
static const struct di_edid_ext *empty_edid_exts[] = { NULL };
|
||||
static const struct di_cta_data_block *empty_cta_blocks[] = { NULL };
|
||||
static const struct di_displayid_data_block *empty_displayid_blocks[] = { NULL };
|
||||
static const struct di_displayid_type_i_ii_vii_timing *empty_timings[] = { NULL };
|
||||
static const struct di_edid_screen_size empty_screen_size = { 0, 0 };
|
||||
|
||||
static char *dup_string(const char *value)
|
||||
{
|
||||
size_t len;
|
||||
char *copy;
|
||||
|
||||
if (!value) {
|
||||
value = "";
|
||||
}
|
||||
|
||||
len = strlen(value) + 1;
|
||||
copy = malloc(len);
|
||||
if (!copy) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(copy, value, len);
|
||||
return copy;
|
||||
}
|
||||
|
||||
static bool has_nonzero_bytes(const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool validate_edid_block(const uint8_t *block)
|
||||
{
|
||||
uint8_t checksum = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < EDID_BLOCK_SIZE; i++) {
|
||||
checksum = (uint8_t)(checksum + block[i]);
|
||||
}
|
||||
|
||||
return checksum == 0;
|
||||
}
|
||||
|
||||
static uint16_t read_le_u16(const uint8_t *data)
|
||||
{
|
||||
return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
|
||||
}
|
||||
|
||||
static uint32_t read_le_u32(const uint8_t *data)
|
||||
{
|
||||
return (uint32_t)data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
|
||||
}
|
||||
|
||||
static float decode_chromaticity_value(uint8_t high_bits, uint8_t low_bits)
|
||||
{
|
||||
return (float)(((unsigned int)high_bits << 2) | low_bits) / 1024.0f;
|
||||
}
|
||||
|
||||
static void decode_manufacturer_id(const uint8_t *base, char manufacturer[4])
|
||||
{
|
||||
manufacturer[0] = (char)('A' + ((base[8] >> 2) & 0x1f) - 1);
|
||||
manufacturer[1] = (char)('A' + (((base[8] & 0x03) << 3) | ((base[9] >> 5) & 0x07)) - 1);
|
||||
manufacturer[2] = (char)('A' + (base[9] & 0x1f) - 1);
|
||||
manufacturer[3] = '\0';
|
||||
|
||||
if (!isupper((unsigned char)manufacturer[0]) || !isupper((unsigned char)manufacturer[1]) || !isupper((unsigned char)manufacturer[2])) {
|
||||
memcpy(manufacturer, "???", 4);
|
||||
}
|
||||
}
|
||||
|
||||
static char *parse_descriptor_string(const uint8_t *descriptor)
|
||||
{
|
||||
char buffer[14];
|
||||
size_t out_len = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 5; i < 18; i++) {
|
||||
const uint8_t value = descriptor[i];
|
||||
|
||||
if (value == 0x00 || value == 0x0a || value == 0x0d) {
|
||||
break;
|
||||
}
|
||||
if (value < 0x20 || value > 0x7e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer[out_len++] = (char)value;
|
||||
}
|
||||
|
||||
while (out_len > 0 && isspace((unsigned char)buffer[out_len - 1])) {
|
||||
out_len--;
|
||||
}
|
||||
buffer[out_len] = '\0';
|
||||
|
||||
return dup_string(buffer);
|
||||
}
|
||||
|
||||
static bool parse_detailed_timing(const uint8_t *descriptor, struct di_edid_detailed_timing_def *timing)
|
||||
{
|
||||
const uint16_t pixel_clock = read_le_u16(descriptor);
|
||||
|
||||
if (pixel_clock == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
timing->horiz_video = (int)(descriptor[2] | ((descriptor[4] & 0xf0) << 4));
|
||||
timing->vert_video = (int)(descriptor[5] | ((descriptor[7] & 0xf0) << 4));
|
||||
timing->horiz_image_mm = (int)(descriptor[12] | ((descriptor[14] & 0xf0) << 4));
|
||||
timing->vert_image_mm = (int)(descriptor[13] | ((descriptor[14] & 0x0f) << 8));
|
||||
|
||||
return timing->horiz_video > 0 && timing->vert_video > 0;
|
||||
}
|
||||
|
||||
static bool populate_detailed_timings(struct di_edid *edid, const uint8_t *base)
|
||||
{
|
||||
struct di_edid_detailed_timing_def parsed[EDID_DESCRIPTOR_COUNT];
|
||||
size_t count = 0;
|
||||
size_t i;
|
||||
|
||||
edid->detailed_timings = empty_detailed_timings;
|
||||
|
||||
for (i = 0; i < EDID_DESCRIPTOR_COUNT; i++) {
|
||||
const uint8_t *descriptor = base + EDID_DESCRIPTOR_OFFSET + (i * 18);
|
||||
|
||||
if (parse_detailed_timing(descriptor, &parsed[count])) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
edid->detailed_timing_storage = calloc(count, sizeof(*edid->detailed_timing_storage));
|
||||
edid->detailed_timings = calloc(count + 1, sizeof(*edid->detailed_timings));
|
||||
if (!edid->detailed_timing_storage || !edid->detailed_timings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
edid->detailed_timing_storage[i] = parsed[i];
|
||||
edid->detailed_timings[i] = &edid->detailed_timing_storage[i];
|
||||
}
|
||||
|
||||
edid->detailed_timings[count] = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void populate_vendor_product(struct di_edid *edid, const uint8_t *base)
|
||||
{
|
||||
const int manufacture_year = (int)base[17] + 1990;
|
||||
const bool has_model_year = base[16] == 0xff;
|
||||
|
||||
decode_manufacturer_id(base, edid->vendor_product.manufacturer);
|
||||
edid->vendor_product.product = (int)read_le_u16(base + 10);
|
||||
edid->vendor_product.serial = (int)read_le_u32(base + 12);
|
||||
edid->vendor_product.manufacture_week = has_model_year ? 0 : (int)base[16];
|
||||
edid->vendor_product.manufacture_year = has_model_year ? 0 : manufacture_year;
|
||||
edid->vendor_product.model_year = has_model_year ? manufacture_year : 0;
|
||||
}
|
||||
|
||||
static void populate_chromaticity(struct di_edid *edid, const uint8_t *base)
|
||||
{
|
||||
if (!has_nonzero_bytes(base + 25, 10)) {
|
||||
edid->has_chromaticity = false;
|
||||
return;
|
||||
}
|
||||
|
||||
edid->chromaticity.red_x = decode_chromaticity_value(base[27], (base[25] >> 6) & 0x03);
|
||||
edid->chromaticity.red_y = decode_chromaticity_value(base[28], (base[25] >> 4) & 0x03);
|
||||
edid->chromaticity.green_x = decode_chromaticity_value(base[29], (base[25] >> 2) & 0x03);
|
||||
edid->chromaticity.green_y = decode_chromaticity_value(base[30], base[25] & 0x03);
|
||||
edid->chromaticity.blue_x = decode_chromaticity_value(base[31], (base[26] >> 6) & 0x03);
|
||||
edid->chromaticity.blue_y = decode_chromaticity_value(base[32], (base[26] >> 4) & 0x03);
|
||||
edid->chromaticity.white_x = decode_chromaticity_value(base[33], (base[26] >> 2) & 0x03);
|
||||
edid->chromaticity.white_y = decode_chromaticity_value(base[34], base[26] & 0x03);
|
||||
edid->has_chromaticity = true;
|
||||
}
|
||||
|
||||
static bool populate_strings(struct di_info *info, const uint8_t *base)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < EDID_DESCRIPTOR_COUNT; i++) {
|
||||
const uint8_t *descriptor = base + EDID_DESCRIPTOR_OFFSET + (i * 18);
|
||||
|
||||
if (descriptor[0] != 0x00 || descriptor[1] != 0x00 || descriptor[2] != 0x00) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (descriptor[3] == 0xfc && !info->model) {
|
||||
info->model = parse_descriptor_string(descriptor);
|
||||
} else if (descriptor[3] == 0xff && !info->serial) {
|
||||
info->serial = parse_descriptor_string(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!info->model) {
|
||||
info->model = dup_string("");
|
||||
}
|
||||
if (!info->serial) {
|
||||
if (info->edid.vendor_product.serial != 0) {
|
||||
char serial_buffer[32];
|
||||
|
||||
snprintf(serial_buffer, sizeof(serial_buffer), "%u", (unsigned int)info->edid.vendor_product.serial);
|
||||
info->serial = dup_string(serial_buffer);
|
||||
} else {
|
||||
info->serial = dup_string("");
|
||||
}
|
||||
}
|
||||
|
||||
return info->model && info->serial;
|
||||
}
|
||||
|
||||
const struct di_info *di_info_parse_edid(const void *data, size_t size)
|
||||
{
|
||||
const uint8_t *base = data;
|
||||
struct di_info *info;
|
||||
|
||||
if (!base || size < EDID_BLOCK_SIZE) {
|
||||
return NULL;
|
||||
}
|
||||
if (memcmp(base, edid_header, sizeof(edid_header)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (!validate_edid_block(base)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = calloc(1, sizeof(*info));
|
||||
if (!info) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info->edid.screen_size.width_cm = (int)base[21];
|
||||
info->edid.screen_size.height_cm = (int)base[22];
|
||||
info->edid.misc_features.preferred_timing_is_native = (base[24] & 0x02) != 0;
|
||||
info->edid.extensions = empty_edid_exts;
|
||||
|
||||
populate_vendor_product(&info->edid, base);
|
||||
populate_chromaticity(&info->edid, base);
|
||||
if (!populate_detailed_timings(&info->edid, base) || !populate_strings(info, base)) {
|
||||
di_info_destroy(info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void di_info_destroy(const struct di_info *info)
|
||||
{
|
||||
struct di_info *mutable_info = (struct di_info *)info;
|
||||
|
||||
if (!mutable_info) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(mutable_info->model);
|
||||
free(mutable_info->serial);
|
||||
if (mutable_info->edid.detailed_timings != empty_detailed_timings) {
|
||||
free((void *)mutable_info->edid.detailed_timings);
|
||||
}
|
||||
free(mutable_info->edid.detailed_timing_storage);
|
||||
free(mutable_info);
|
||||
}
|
||||
|
||||
const struct di_edid *di_info_get_edid(const struct di_info *info)
|
||||
{
|
||||
return info ? &info->edid : NULL;
|
||||
}
|
||||
|
||||
char *di_info_get_model(const struct di_info *info)
|
||||
{
|
||||
return dup_string(info ? info->model : "");
|
||||
}
|
||||
|
||||
char *di_info_get_serial(const struct di_info *info)
|
||||
{
|
||||
return dup_string(info ? info->serial : "");
|
||||
}
|
||||
|
||||
const struct di_edid_detailed_timing_def *const *di_edid_get_detailed_timing_defs(const struct di_edid *edid)
|
||||
{
|
||||
return edid && edid->detailed_timings ? edid->detailed_timings : empty_detailed_timings;
|
||||
}
|
||||
|
||||
const struct di_edid_screen_size *di_edid_get_screen_size(const struct di_edid *edid)
|
||||
{
|
||||
return edid ? &edid->screen_size : &empty_screen_size;
|
||||
}
|
||||
|
||||
const struct di_edid_vendor_product *di_edid_get_vendor_product(const struct di_edid *edid)
|
||||
{
|
||||
return edid ? &edid->vendor_product : NULL;
|
||||
}
|
||||
|
||||
const struct di_edid_chromaticity_coords *di_edid_get_chromaticity_coords(const struct di_edid *edid)
|
||||
{
|
||||
return edid && edid->has_chromaticity ? &edid->chromaticity : NULL;
|
||||
}
|
||||
|
||||
const struct di_edid_ext *const *di_edid_get_extensions(const struct di_edid *edid)
|
||||
{
|
||||
return edid && edid->extensions ? edid->extensions : empty_edid_exts;
|
||||
}
|
||||
|
||||
const struct di_edid_misc_features *di_edid_get_misc_features(const struct di_edid *edid)
|
||||
{
|
||||
return edid ? &edid->misc_features : NULL;
|
||||
}
|
||||
|
||||
const struct di_edid_cta *di_edid_ext_get_cta(const struct di_edid_ext *ext)
|
||||
{
|
||||
(void)ext;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct di_displayid *di_edid_ext_get_displayid(const struct di_edid_ext *ext)
|
||||
{
|
||||
(void)ext;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct di_cta_data_block *const *di_edid_cta_get_data_blocks(const struct di_edid_cta *cta)
|
||||
{
|
||||
(void)cta;
|
||||
return empty_cta_blocks;
|
||||
}
|
||||
|
||||
const struct di_cta_hdr_static_metadata_block *di_cta_data_block_get_hdr_static_metadata(const struct di_cta_data_block *block)
|
||||
{
|
||||
(void)block;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct di_cta_colorimetry_block *di_cta_data_block_get_colorimetry(const struct di_cta_data_block *block)
|
||||
{
|
||||
(void)block;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct di_displayid_data_block *const *di_displayid_get_data_blocks(const struct di_displayid *displayid)
|
||||
{
|
||||
(void)displayid;
|
||||
return empty_displayid_blocks;
|
||||
}
|
||||
|
||||
const struct di_displayid_display_params *di_displayid_data_block_get_display_params(const struct di_displayid_data_block *block)
|
||||
{
|
||||
(void)block;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *block)
|
||||
{
|
||||
(void)block;
|
||||
return empty_timings;
|
||||
}
|
||||
|
||||
const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block *block)
|
||||
{
|
||||
(void)block;
|
||||
return empty_timings;
|
||||
}
|
||||
@@ -0,0 +1,920 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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
|
||||
/**
|
||||
* The size of a DisplayID type II timing.
|
||||
*/
|
||||
#define DISPLAYID_TYPE_II_TIMING_SIZE 11
|
||||
/**
|
||||
* The size of a DisplayID type III timing.
|
||||
*/
|
||||
#define DISPLAYID_TYPE_III_TIMING_SIZE 3
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_data_block_flags_reserved(struct di_displayid *displayid,
|
||||
const uint8_t data[static DISPLAYID_DATA_BLOCK_HEADER_SIZE],
|
||||
const char *block_name)
|
||||
{
|
||||
uint8_t flags;
|
||||
|
||||
flags = get_bit_range(data[0x01], 7, 3);
|
||||
|
||||
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);
|
||||
check_data_block_flags_reserved(displayid, data, "Display Parameters Data Block");
|
||||
|
||||
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
|
||||
timing_aspect_ratio_is_valid(uint8_t raw)
|
||||
{
|
||||
switch (raw) {
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_1_1:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_5_4:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_4_3:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_15_9:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_16_9:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_16_10:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_64_27:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_256_135:
|
||||
case DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_di_displayid_parse_type_1_7_timing(struct di_displayid_type_i_ii_vii_timing *t,
|
||||
struct di_logger *logger,
|
||||
const char *prefix,
|
||||
const uint8_t *data,
|
||||
bool is_type7)
|
||||
{
|
||||
int raw_pixel_clock;
|
||||
uint8_t stereo_3d, aspect_ratio;
|
||||
|
||||
raw_pixel_clock = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
if (is_type7)
|
||||
t->pixel_clock_mhz = (double)(1 + raw_pixel_clock) * 0.001;
|
||||
else
|
||||
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_II_VII_TIMING_STEREO_3D_NEVER:
|
||||
case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_ALWAYS:
|
||||
case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_USER:
|
||||
t->stereo_3d = stereo_3d;
|
||||
break;
|
||||
default:
|
||||
_di_logger_add_failure(logger, "%s: Reserved stereo 0x%02x.",
|
||||
prefix, stereo_3d);
|
||||
break;
|
||||
}
|
||||
|
||||
aspect_ratio = get_bit_range(data[3], 3, 0);
|
||||
if (timing_aspect_ratio_is_valid(aspect_ratio)) {
|
||||
t->aspect_ratio = aspect_ratio;
|
||||
} else {
|
||||
t->aspect_ratio = DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED;
|
||||
_di_logger_add_failure(logger, "%s: Unknown aspect 0x%02x.",
|
||||
prefix, aspect_ratio);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
struct di_displayid_type_i_ii_vii_timing timing, *t;
|
||||
|
||||
_di_displayid_parse_type_1_7_timing(&timing, displayid->logger,
|
||||
"Video Timing Modes Type 1 - Detailed Timings Data Block",
|
||||
data, false);
|
||||
|
||||
t = calloc(1, sizeof(*t));
|
||||
if (t == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*t = timing;
|
||||
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);
|
||||
check_data_block_flags_reserved(displayid, data,
|
||||
"Video Timing Modes Type 1 - Detailed Timings Data Block");
|
||||
|
||||
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_type_ii_timing(struct di_displayid *displayid,
|
||||
struct di_displayid_data_block *data_block,
|
||||
const uint8_t data[static DISPLAYID_TYPE_II_TIMING_SIZE])
|
||||
{
|
||||
int raw_pixel_clock;
|
||||
uint8_t stereo_3d;
|
||||
|
||||
struct di_displayid_type_i_ii_vii_timing *t = calloc(1, sizeof(*t));
|
||||
if (t == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
t->aspect_ratio = DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED;
|
||||
|
||||
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_II_VII_TIMING_STEREO_3D_NEVER:
|
||||
case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_ALWAYS:
|
||||
case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_USER:
|
||||
t->stereo_3d = stereo_3d;
|
||||
break;
|
||||
default:
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 2 - Detailed Timings Data Block: Reserved stereo 0x%02x.",
|
||||
stereo_3d);
|
||||
break;
|
||||
}
|
||||
|
||||
t->horiz_sync_polarity = has_bit(data[3], 3);
|
||||
t->vert_sync_polarity = has_bit(data[3], 2);
|
||||
|
||||
if (get_bit_range(data[3], 1, 0) != 0) {
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 2 - Detailed Timings Data Block: "
|
||||
"Timing Options bit 1-0 are reserved.");
|
||||
}
|
||||
|
||||
t->horiz_active = 8 + 8 * (data[4] | (get_bit_range(data[5], 0, 0) << 8));
|
||||
t->horiz_blank = 8 + 8 * get_bit_range(data[5], 7, 1);
|
||||
t->horiz_offset = 8 + 8 * get_bit_range(data[6], 7, 4);
|
||||
t->horiz_sync_width = 8 + 8 * get_bit_range(data[6], 3, 0);
|
||||
t->vert_active = 1 + (data[7] | (get_bit_range(data[8], 3, 0) << 8));
|
||||
if (get_bit_range(data[8], 7, 4) != 0) {
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 2 - Detailed Timings Data Block: "
|
||||
"Vertical Active Image bits 7-4 are reserved.");
|
||||
}
|
||||
t->vert_blank = 1 + data[9];
|
||||
t->vert_offset = 1 + get_bit_range(data[9], 7, 4);
|
||||
t->vert_sync_width = 1 + get_bit_range(data[9], 3, 0);
|
||||
|
||||
assert(data_block->type_ii_timings_len < DISPLAYID_MAX_TYPE_II_TIMINGS);
|
||||
data_block->type_ii_timings[data_block->type_ii_timings_len++] = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_type_ii_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 2 - Detailed Timings Data Block",
|
||||
0);
|
||||
check_data_block_flags_reserved(displayid, data,
|
||||
"Video Timing Modes Type 2 - Detailed Timings Data Block");
|
||||
|
||||
if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_II_TIMING_SIZE != 0) {
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 2 - Detailed Timings Data Block: payload size not divisible by element size.");
|
||||
}
|
||||
|
||||
for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE;
|
||||
i + DISPLAYID_TYPE_II_TIMING_SIZE <= size;
|
||||
i += DISPLAYID_TYPE_II_TIMING_SIZE) {
|
||||
if (!parse_type_ii_timing(displayid, data_block, &data[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_type_iv_timing_block(struct di_displayid *displayid,
|
||||
struct di_displayid_type_iv_timing *type_iv_timing,
|
||||
const uint8_t *data, size_t size)
|
||||
{
|
||||
uint8_t flags;
|
||||
uint8_t type;
|
||||
size_t i;
|
||||
uint8_t code;
|
||||
struct di_dmt_code *dmts = NULL;
|
||||
struct di_cta_vic *cta_vics = NULL;
|
||||
struct di_hdmi_vic *hdmi_vics = NULL;
|
||||
|
||||
check_data_block_revision(displayid, data,
|
||||
"Video Timing Modes Type 4 - Short Timings Data Block",
|
||||
1);
|
||||
|
||||
flags = get_bit_range(data[0x01], 5, 3);
|
||||
if (flags != 0) {
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 4 - Short Timings Data Block: Unexpected flags (0x%02x).",
|
||||
flags);
|
||||
}
|
||||
|
||||
type = get_bit_range(data[0x01], 7, 6);
|
||||
switch (type) {
|
||||
case DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_DMT:
|
||||
case DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_CTA:
|
||||
case DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_HDMI:
|
||||
type_iv_timing->type = type;
|
||||
break;
|
||||
default:
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 4 - Short Timings Data Block: Reserved Timing Code Type 0x%02x.",
|
||||
type);
|
||||
break;
|
||||
}
|
||||
|
||||
type_iv_timing->codes_len = size - DISPLAYID_DATA_BLOCK_HEADER_SIZE;
|
||||
if (type_iv_timing->codes_len == 0) {
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 4 - Short Timings Data Block: No Timing Code present.");
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_DMT:
|
||||
dmts = calloc(type_iv_timing->codes_len, sizeof(*dmts));
|
||||
if (!dmts)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < type_iv_timing->codes_len; i++) {
|
||||
code = data[DISPLAYID_DATA_BLOCK_HEADER_SIZE + i];
|
||||
dmts[i] = (struct di_dmt_code) { .code = code };
|
||||
}
|
||||
break;
|
||||
case DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_CTA:
|
||||
cta_vics = calloc(type_iv_timing->codes_len, sizeof(*cta_vics));
|
||||
if (!cta_vics)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < type_iv_timing->codes_len; i++) {
|
||||
code = data[DISPLAYID_DATA_BLOCK_HEADER_SIZE + i];
|
||||
cta_vics[i] = (struct di_cta_vic) { .code = code };
|
||||
}
|
||||
break;
|
||||
case DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_HDMI:
|
||||
hdmi_vics = calloc(type_iv_timing->codes_len, sizeof(*hdmi_vics));
|
||||
if (!hdmi_vics)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < type_iv_timing->codes_len; i++) {
|
||||
code = data[DISPLAYID_DATA_BLOCK_HEADER_SIZE + i];
|
||||
hdmi_vics[i] = (struct di_hdmi_vic) { .code = code };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
type_iv_timing->dmts = dmts;
|
||||
type_iv_timing->cta_vics = cta_vics;
|
||||
type_iv_timing->hdmi_vics = hdmi_vics;
|
||||
|
||||
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);
|
||||
check_data_block_flags_reserved(displayid, data, "Tiled Display Topology Data Block");
|
||||
|
||||
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 = (uint16_t)(data[0x13] | (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 bool
|
||||
parse_type_iii_timing(struct di_displayid *displayid,
|
||||
struct di_displayid_data_block *data_block,
|
||||
const uint8_t data[static DISPLAYID_TYPE_III_TIMING_SIZE])
|
||||
{
|
||||
struct di_displayid_type_iii_timing *t;
|
||||
uint8_t algo, aspect_ratio;
|
||||
|
||||
t = calloc(1, sizeof(*t));
|
||||
if (t == NULL)
|
||||
return false;
|
||||
|
||||
t->preferred = has_bit(data[0], 7);
|
||||
|
||||
algo = get_bit_range(data[0], 6, 4);
|
||||
switch (algo) {
|
||||
case DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING:
|
||||
case DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING:
|
||||
t->algo = algo;
|
||||
break;
|
||||
default:
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 3 - Short Timings Data Block: Reserved algorithm 0x%02x.",
|
||||
algo);
|
||||
goto error_reserved;
|
||||
}
|
||||
|
||||
aspect_ratio = get_bit_range(data[0], 3, 0);
|
||||
if (timing_aspect_ratio_is_valid(aspect_ratio)) {
|
||||
t->aspect_ratio = aspect_ratio;
|
||||
} else {
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 3 - Short Timings Data Block: Reserved aspect ratio 0x%02x.",
|
||||
aspect_ratio);
|
||||
goto error_reserved;
|
||||
}
|
||||
|
||||
t->horiz_active = ((int32_t)data[1] + 1) * 8;
|
||||
|
||||
t->interlaced = has_bit(data[2], 7);
|
||||
t->refresh_rate_hz = (int32_t)get_bit_range(data[2], 6, 0) + 1;
|
||||
|
||||
assert(data_block->type_iii_timings_len < DISPLAYID_MAX_TYPE_III_TIMINGS);
|
||||
data_block->type_iii_timings[data_block->type_iii_timings_len++] = t;
|
||||
return true;
|
||||
|
||||
error_reserved:
|
||||
free(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_type_iii_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 3 - Short Timings Data Block",
|
||||
1);
|
||||
|
||||
if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_III_TIMING_SIZE != 0)
|
||||
add_failure(displayid,
|
||||
"Video Timing Modes Type 3 - Short Timings Data Block: payload size not divisible by element size.");
|
||||
|
||||
for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE;
|
||||
i + DISPLAYID_TYPE_III_TIMING_SIZE <= size;
|
||||
i += DISPLAYID_TYPE_III_TIMING_SIZE) {
|
||||
if (!parse_type_iii_timing(displayid, data_block, &data[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
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_TYPE_II_TIMING:
|
||||
if (!parse_type_ii_timing_block(displayid, data_block, data, data_block_size))
|
||||
goto error;
|
||||
break;
|
||||
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
|
||||
if (!parse_type_iii_timing_block(displayid, data_block, data, data_block_size))
|
||||
goto error;
|
||||
break;
|
||||
case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING:
|
||||
if (!parse_type_iv_timing_block(displayid,
|
||||
&data_block->type_iv_timing,
|
||||
data, data_block_size))
|
||||
goto error;
|
||||
break;
|
||||
case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID:
|
||||
case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT:
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
_di_displayid_parse_version(const uint8_t *data, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return 0;
|
||||
return get_bit_range(data[0x00], 7, 4);
|
||||
}
|
||||
|
||||
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 = _di_displayid_parse_version(data, size);
|
||||
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;
|
||||
case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING:
|
||||
for (i = 0; i < data_block->type_ii_timings_len; i++)
|
||||
free(data_block->type_ii_timings[i]);
|
||||
break;
|
||||
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
|
||||
for (i = 0; i < data_block->type_iii_timings_len; i++)
|
||||
free(data_block->type_iii_timings[i]);
|
||||
break;
|
||||
case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING:
|
||||
free((struct di_dmt_code *) data_block->type_iv_timing.dmts);
|
||||
free((struct di_cta_vic *) data_block->type_iv_timing.cta_vics);
|
||||
free((struct di_hdmi_vic *) data_block->type_iv_timing.hdmi_vics);
|
||||
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_ii_vii_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_ii_vii_timing *const *) data_block->type_i_timings;
|
||||
}
|
||||
|
||||
const struct di_displayid_type_i_ii_vii_timing *const *
|
||||
di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block *data_block)
|
||||
{
|
||||
if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING) {
|
||||
return NULL;
|
||||
}
|
||||
return (const struct di_displayid_type_i_ii_vii_timing *const *) data_block->type_ii_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_type_iii_timing *const *
|
||||
di_displayid_data_block_get_type_iii_timings(const struct di_displayid_data_block *data_block)
|
||||
{
|
||||
if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING) {
|
||||
return NULL;
|
||||
}
|
||||
return (const struct di_displayid_type_iii_timing *const *) data_block->type_iii_timings;
|
||||
}
|
||||
|
||||
const struct di_displayid_type_iv_timing *
|
||||
di_displayid_data_block_get_type_iv_timing(const struct di_displayid_data_block *data_block)
|
||||
{
|
||||
if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING) {
|
||||
return NULL;
|
||||
}
|
||||
return &data_block->type_iv_timing;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "bits.h"
|
||||
#include "displayid.h"
|
||||
#include "displayid2.h"
|
||||
|
||||
/**
|
||||
* The size of a DisplayID v2 section header.
|
||||
*/
|
||||
#define DISPLAYID2_HEADER_SIZE 4
|
||||
/**
|
||||
* The size of the mandatory fields in a DisplayID v2 section (header + checksum).
|
||||
*/
|
||||
#define DISPLAYID2_MIN_SIZE (DISPLAYID2_HEADER_SIZE + 1)
|
||||
/**
|
||||
* The maximum size of a DisplayID v2 section.
|
||||
*/
|
||||
#define DISPLAYID2_MAX_SIZE 256
|
||||
/**
|
||||
* The size of a DisplayID v2 data block header (tag, revision and size).
|
||||
*/
|
||||
#define DISPLAYID2_DATA_BLOCK_HEADER_SIZE 3
|
||||
|
||||
static void
|
||||
add_failure(struct di_displayid2 *displayid2, const char fmt[], ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
_di_logger_va_add_failure(displayid2->logger, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_cta861_block(struct di_displayid2 *displayid2, struct di_displayid2_data_block *data_block,
|
||||
const uint8_t *data, size_t size)
|
||||
{
|
||||
struct di_cta cta;
|
||||
size_t i;
|
||||
uint8_t data_block_tag, data_block_size;
|
||||
struct di_cta_data_block *cta_data_block;
|
||||
|
||||
cta = (struct di_cta){
|
||||
/* The DisplayID block doesn't specify the CTA revision. Use
|
||||
* the latest one to silence warnings. */
|
||||
.revision = 3,
|
||||
.logger = displayid2->logger,
|
||||
};
|
||||
|
||||
i = 0x03;
|
||||
while (i < size) {
|
||||
data_block_size = get_bit_range(data[i], 4, 0);
|
||||
data_block_tag = get_bit_range(data[i], 7, 5);
|
||||
|
||||
if (data_block_size > size - i - 1) {
|
||||
add_failure(displayid2,
|
||||
"CTA data block size (%" PRIu8 ") exceeds number of remaining bytes (%zu)",
|
||||
data_block_size, size - i - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_di_cta_data_block_parse(&cta, data_block_tag,
|
||||
&data[i + 1], data_block_size, &cta_data_block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cta_data_block != NULL) {
|
||||
assert(data_block->cta861.data_blocks_len < DISPLAYID2_CTA861_MAX_DATA_BLOCKS);
|
||||
data_block->cta861.data_blocks[data_block->cta861.data_blocks_len++] = cta_data_block;
|
||||
}
|
||||
|
||||
i += 1 + data_block_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
parse_data_block(struct di_displayid2 *displayid2, const uint8_t *data,
|
||||
size_t size)
|
||||
{
|
||||
uint8_t tag;
|
||||
size_t data_block_size;
|
||||
struct di_displayid2_data_block *data_block = NULL;
|
||||
|
||||
assert(size >= DISPLAYID2_DATA_BLOCK_HEADER_SIZE);
|
||||
|
||||
tag = data[0x00];
|
||||
data_block_size = (size_t) data[0x02] + DISPLAYID2_DATA_BLOCK_HEADER_SIZE;
|
||||
if (data_block_size > size) {
|
||||
add_failure(displayid2,
|
||||
"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_DISPLAYID2_DATA_BLOCK_CTA861:
|
||||
if (!parse_cta861_block(displayid2, data_block, data, data_block_size)) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case DI_DISPLAYID2_DATA_BLOCK_PRODUCT_ID:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_DISPLAY_PARAMS:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_TYPE_VII_TIMING:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_TYPE_VIII_TIMING:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_TYPE_IX_TIMING:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_DYN_TIMING_RANGE_LIMITS:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_DISPLAY_INTERFACE_FEATURES:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_STEREO_DISPLAY_INTERFACE:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_TILED_DISPLAY_TOPO:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_CONTAINERID:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_TYPE_X_TIMING:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_ADAPTIVE_SYNC:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_ARVR_HMD:
|
||||
case DI_DISPLAYID2_DATA_BLOCK_ARVR_LAYER:
|
||||
break; /* Supported */
|
||||
case 0x7E:
|
||||
goto skip; /* Vendor-specific */
|
||||
default:
|
||||
add_failure(displayid2,
|
||||
"Unknown DisplayID v2 Data Block (0x%" PRIx8 ", length %" PRIu8 ")",
|
||||
tag, data_block_size - DISPLAYID2_DATA_BLOCK_HEADER_SIZE);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
data_block->tag = tag;
|
||||
|
||||
assert(displayid2->data_blocks_len < DISPLAYID2_MAX_DATA_BLOCKS);
|
||||
displayid2->data_blocks[displayid2->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_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 bool
|
||||
is_data_block_end(const uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < DISPLAYID2_DATA_BLOCK_HEADER_SIZE)
|
||||
return true;
|
||||
return is_all_zeroes(data, DISPLAYID2_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_displayid2_parse(struct di_displayid2 *displayid2, 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;
|
||||
int version;
|
||||
uint8_t product_primary_use_case;
|
||||
|
||||
if (size < DISPLAYID2_MIN_SIZE) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
displayid2->logger = logger;
|
||||
|
||||
version = _di_displayid_parse_version(data, size);
|
||||
displayid2->revision = get_bit_range(data[0x00], 3, 0);
|
||||
if (version != 2) {
|
||||
errno = ENOTSUP;
|
||||
return false;
|
||||
}
|
||||
|
||||
section_size = (size_t) data[0x01] + DISPLAYID2_MIN_SIZE;
|
||||
if (section_size > DISPLAYID2_MAX_SIZE || section_size > size) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validate_checksum(data, section_size)) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
product_primary_use_case = data[0x02];
|
||||
switch (product_primary_use_case) {
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_EXTENSION:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_TEST:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_GENERIC:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_TV:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_DESKTOP_PRODUCTIVITY:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_DESKTOP_GAMING:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_PRESENTATION:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_HMD_VR:
|
||||
case DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_HMD_AR:
|
||||
displayid2->product_primary_use_case = product_primary_use_case;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
i = DISPLAYID2_HEADER_SIZE;
|
||||
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(displayid2, &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 < DISPLAYID2_DATA_BLOCK_HEADER_SIZE) {
|
||||
add_failure(displayid2,
|
||||
"Not enough bytes remain (%zu) for a DisplayID data block and the DisplayID filler is non-0.",
|
||||
max_data_block_size);
|
||||
} else {
|
||||
add_failure(displayid2, "Padding: Contains non-zero bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
displayid2->logger = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
data_block_destroy(struct di_displayid2_data_block *data_block)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < data_block->cta861.data_blocks_len; i++)
|
||||
_di_cta_data_block_destroy(data_block->cta861.data_blocks[i]);
|
||||
|
||||
free(data_block);
|
||||
}
|
||||
|
||||
void
|
||||
_di_displayid2_finish(struct di_displayid2 *displayid2)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < displayid2->data_blocks_len; i++)
|
||||
data_block_destroy(displayid2->data_blocks[i]);
|
||||
}
|
||||
|
||||
int
|
||||
di_displayid2_get_revision(const struct di_displayid2 *displayid2)
|
||||
{
|
||||
return displayid2->revision;
|
||||
}
|
||||
|
||||
enum di_displayid2_product_primary_use_case
|
||||
di_displayid2_get_product_primary_use_case(const struct di_displayid2 *displayid2)
|
||||
{
|
||||
return displayid2->product_primary_use_case;
|
||||
}
|
||||
|
||||
const struct di_displayid2_data_block *const *
|
||||
di_displayid2_get_data_blocks(const struct di_displayid2 *displayid2)
|
||||
{
|
||||
return (const struct di_displayid2_data_block *const *) displayid2->data_blocks;
|
||||
}
|
||||
|
||||
enum di_displayid2_data_block_tag
|
||||
di_displayid2_data_block_get_tag(const struct di_displayid2_data_block *data_block)
|
||||
{
|
||||
return data_block->tag;
|
||||
}
|
||||
|
||||
const struct di_cta_data_block *const *
|
||||
di_displayid2_data_block_get_cta_data_blocks(const struct di_displayid2_data_block *data_block)
|
||||
{
|
||||
if (data_block->tag != DI_DISPLAYID2_DATA_BLOCK_CTA861) {
|
||||
return NULL;
|
||||
}
|
||||
return (const struct di_cta_data_block *const *) data_block->cta861.data_blocks;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <libdisplay-info/dmt.h>
|
||||
#include <libdisplay-info/edid.h>
|
||||
|
||||
extern const struct di_dmt_timing _di_dmt_timings[];
|
||||
extern const size_t _di_dmt_timings_len;
|
||||
|
||||
const struct di_dmt_timing *
|
||||
di_dmt_timing_from_code(struct di_dmt_code dmt_code)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < _di_dmt_timings_len; i++) {
|
||||
const struct di_dmt_timing *t = &_di_dmt_timings[i];
|
||||
|
||||
if (t->dmt_code.code == dmt_code.code)
|
||||
return t;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct di_dmt_code
|
||||
di_dmt_timing_to_code(const struct di_dmt_timing *timing)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < _di_dmt_timings_len; i++) {
|
||||
const struct di_dmt_timing *t = &_di_dmt_timings[i];
|
||||
|
||||
if (t->horiz_video == timing->horiz_video &&
|
||||
t->vert_video == timing->vert_video &&
|
||||
fabs(t->refresh_rate_hz - timing->refresh_rate_hz) <= FLT_EPSILON &&
|
||||
t->pixel_clock_hz == timing->pixel_clock_hz &&
|
||||
t->horiz_blank == timing->horiz_blank &&
|
||||
t->vert_blank == timing->vert_blank &&
|
||||
t->horiz_front_porch == timing->horiz_front_porch &&
|
||||
t->vert_front_porch == timing->vert_front_porch &&
|
||||
t->horiz_sync_pulse == timing->horiz_sync_pulse &&
|
||||
t->vert_sync_pulse == timing->vert_sync_pulse &&
|
||||
t->horiz_border == timing->horiz_border &&
|
||||
t->vert_border == timing->vert_border &&
|
||||
t->reduced_blanking == timing->reduced_blanking)
|
||||
return t->dmt_code;
|
||||
}
|
||||
|
||||
return (struct di_dmt_code) { .code = 0 };
|
||||
}
|
||||
|
||||
const struct di_dmt_timing *
|
||||
di_dmt_timing_from_edid_standard_timing(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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
|
||||
#include <libdisplay-info/gtf.h>
|
||||
|
||||
/**
|
||||
* 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, 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;
|
||||
double v_sync_bp = 0.0, h_blank_pixels = 0.0, total_pixels = 0.0, pixel_freq = 0.0;
|
||||
|
||||
/* 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,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <libdisplay-info/hdmi-vic.h>
|
||||
|
||||
/**
|
||||
* HDMI video format table. 8.2.3.1 section of the 1.4b HDMI spec.
|
||||
*/
|
||||
static const struct di_hdmi_vic_video_format _di_hdmi_vic_video_formats[] = {
|
||||
{
|
||||
.vic = { .code = 1 },
|
||||
.h_active = 3840,
|
||||
.v_active = 2160,
|
||||
.pixel_clock_hz = 297000000,
|
||||
.h_front = 176,
|
||||
.h_sync = 88,
|
||||
.h_back = 296,
|
||||
.v_front = 8,
|
||||
.v_sync = 10,
|
||||
.v_back = 72,
|
||||
},
|
||||
{
|
||||
.vic = { .code = 2 },
|
||||
.h_active = 3840,
|
||||
.v_active = 2160,
|
||||
.pixel_clock_hz = 297000000,
|
||||
.h_front = 1056,
|
||||
.h_sync = 88,
|
||||
.h_back = 296,
|
||||
.v_front = 8,
|
||||
.v_sync = 10,
|
||||
.v_back = 72,
|
||||
},
|
||||
{
|
||||
.vic = { .code = 3 },
|
||||
.h_active = 3840,
|
||||
.v_active = 2160,
|
||||
.pixel_clock_hz = 297000000,
|
||||
.h_front = 1276,
|
||||
.h_sync = 88,
|
||||
.h_back = 296,
|
||||
.v_front = 8,
|
||||
.v_sync = 10,
|
||||
.v_back = 72,
|
||||
},
|
||||
{
|
||||
.vic = { .code = 4 },
|
||||
.h_active = 4096,
|
||||
.v_active = 2160,
|
||||
.pixel_clock_hz = 297000000,
|
||||
.h_front = 1020,
|
||||
.h_sync = 88,
|
||||
.h_back = 296,
|
||||
.v_front = 8,
|
||||
.v_sync = 10,
|
||||
.v_back = 72,
|
||||
},
|
||||
};
|
||||
|
||||
static const size_t _di_hdmi_vic_video_formats_len =
|
||||
sizeof(_di_hdmi_vic_video_formats) / sizeof(_di_hdmi_vic_video_formats[0]);
|
||||
|
||||
const struct di_hdmi_vic_video_format *
|
||||
di_hdmi_vic_video_format_from_vic(struct di_hdmi_vic vic)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < _di_hdmi_vic_video_formats_len; i++) {
|
||||
if (_di_hdmi_vic_video_formats[i].vic.code == vic.code)
|
||||
return &_di_hdmi_vic_video_formats[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct di_hdmi_vic
|
||||
di_hdmi_vic_video_format_to_vic(const struct di_hdmi_vic_video_format *format)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < _di_hdmi_vic_video_formats_len; i++) {
|
||||
const struct di_hdmi_vic_video_format *candidate = &_di_hdmi_vic_video_formats[i];
|
||||
|
||||
if (candidate->vic.code != 0 &&
|
||||
candidate->h_active == format->h_active &&
|
||||
candidate->v_active == format->v_active &&
|
||||
candidate->h_front == format->h_front &&
|
||||
candidate->v_front == format->v_front &&
|
||||
candidate->h_sync == format->h_sync &&
|
||||
candidate->v_sync == format->v_sync &&
|
||||
candidate->h_back == format->h_back &&
|
||||
candidate->v_back == format->v_back &&
|
||||
candidate->pixel_clock_hz == format->pixel_clock_hz)
|
||||
return candidate->vic;
|
||||
}
|
||||
|
||||
return (struct di_hdmi_vic) { .code = 0 };
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef BITS_H
|
||||
#define BITS_H
|
||||
|
||||
/**
|
||||
* Utility functions to operate on bits.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -0,0 +1,287 @@
|
||||
#ifndef CTA_H
|
||||
#define CTA_H
|
||||
|
||||
/**
|
||||
* Private header for the low-level CTA API.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libdisplay-info/cta.h>
|
||||
#include <displayid.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* The maximum number of Speaker Location Descriptors in a Speaker Location data
|
||||
* block.
|
||||
*
|
||||
* Each data block has its size described in a 5-bit field, so its maximum size
|
||||
* is 63 bytes, and each Speaker Location Descriptors uses at least 2 byte.
|
||||
*/
|
||||
#define EDID_CTA_MAX_SPEAKER_LOCATION_BLOCK_ENTRIES 31
|
||||
/**
|
||||
* The maximum number of SVR entries in a video format preference block.
|
||||
*
|
||||
* Each data block has its size described in a 5-bit field, so its maximum size
|
||||
* is 63 bytes, and each SVR uses 1 byte.
|
||||
*/
|
||||
#define EDID_CTA_MAX_VIDEO_FORMAT_PREF_BLOCK_ENTRIES 63
|
||||
/**
|
||||
* The maximum number of format entries in a HDMI audio block.
|
||||
*
|
||||
* Each data block has its size described in a 5-bit field, so its maximum size
|
||||
* is 63 bytes, the header takes up 2 bytes and each format entry uses 4 bytes.
|
||||
*/
|
||||
#define EDID_CTA_MAX_HDMI_AUDIO_BLOCK_ENTRIES 15
|
||||
|
||||
/**
|
||||
* Context for CTA parsing functions.
|
||||
*/
|
||||
struct di_cta {
|
||||
int revision;
|
||||
const struct di_edid_cta_flags *flags; /* may be NULL for non-EDID blocks */
|
||||
|
||||
struct di_logger *logger;
|
||||
};
|
||||
|
||||
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_cta_hdr_static_metadata_block_priv {
|
||||
struct di_cta_hdr_static_metadata_block base;
|
||||
struct di_cta_hdr_static_metadata_eotfs eotfs;
|
||||
struct di_cta_hdr_static_metadata_descriptors descriptors;
|
||||
};
|
||||
|
||||
struct di_cta_hdr_dynamic_metadata_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_type1 type1;
|
||||
struct di_cta_hdr_dynamic_metadata_type2 type2;
|
||||
struct di_cta_hdr_dynamic_metadata_type3 type3;
|
||||
struct di_cta_hdr_dynamic_metadata_type4 type4;
|
||||
struct di_cta_hdr_dynamic_metadata_type256 type256;
|
||||
};
|
||||
|
||||
struct di_cta_video_block_priv {
|
||||
struct di_cta_video_block base;
|
||||
/* NULL-terminated */
|
||||
struct di_cta_svd *svds[EDID_CTA_MAX_VIDEO_BLOCK_ENTRIES + 1];
|
||||
size_t svds_len;
|
||||
};
|
||||
|
||||
struct di_cta_ycbcr420_video_block_priv {
|
||||
struct di_cta_ycbcr420_video_block base;
|
||||
/* 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_priv {
|
||||
struct di_cta_audio_block audio;
|
||||
/* 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_block {
|
||||
bool all;
|
||||
uint8_t svd_bitmap[EDID_CTA_MAX_YCBCR420_CAP_MAP_BLOCK_ENTRIES];
|
||||
};
|
||||
|
||||
|
||||
struct di_cta_hdmi_audio_block_priv {
|
||||
struct di_cta_hdmi_audio_block base;
|
||||
struct di_cta_hdmi_audio_multi_stream ms;
|
||||
struct di_cta_hdmi_audio_3d a3d;
|
||||
/* NULL-terminated */
|
||||
struct di_cta_sad_priv *sads[EDID_CTA_MAX_HDMI_AUDIO_BLOCK_ENTRIES + 1];
|
||||
size_t sads_len;
|
||||
};
|
||||
|
||||
struct di_cta_infoframe_block_priv {
|
||||
struct di_cta_infoframe_block base;
|
||||
/* NULL-terminated */
|
||||
struct di_cta_infoframe_descriptor *infoframes[EDID_CTA_INFOFRAME_BLOCK_ENTRIES + 1];
|
||||
size_t infoframes_len;
|
||||
};
|
||||
|
||||
struct di_cta_speaker_location_priv {
|
||||
struct di_cta_speaker_location_block base;
|
||||
/* NULL-terminated */
|
||||
struct di_cta_speaker_location_descriptor *locations[EDID_CTA_MAX_SPEAKER_LOCATION_BLOCK_ENTRIES + 1];
|
||||
size_t locations_len;
|
||||
};
|
||||
|
||||
struct di_cta_video_format_pref_priv {
|
||||
struct di_cta_video_format_pref_block base;
|
||||
/* NULL-terminated */
|
||||
struct di_cta_svr *svrs[EDID_CTA_MAX_VIDEO_FORMAT_PREF_BLOCK_ENTRIES + 1];
|
||||
size_t svrs_len;
|
||||
};
|
||||
|
||||
struct di_cta_type_vii_timing_priv {
|
||||
struct di_cta_type_vii_timing_block base;
|
||||
struct di_displayid_type_i_ii_vii_timing timing;
|
||||
};
|
||||
|
||||
struct di_cta_vendor_hdmi_block_priv {
|
||||
struct di_cta_vendor_hdmi_block base;
|
||||
/* HDMI VIC's. */
|
||||
struct di_hdmi_vic *vics;
|
||||
};
|
||||
|
||||
struct di_cta_dolby_video_block_priv {
|
||||
struct di_cta_dolby_video_block base;
|
||||
struct di_cta_dolby_video_block_v0 v0;
|
||||
struct di_cta_dolby_video_block_v1 v1;
|
||||
struct di_cta_dolby_video_block_v2 v2;
|
||||
};
|
||||
|
||||
struct di_cta_vendor_hdmi_forum_block_priv {
|
||||
struct di_cta_vendor_hdmi_forum_block base;
|
||||
struct di_cta_hdmi_dsc dsc;
|
||||
};
|
||||
|
||||
struct di_cta_hdmi_forum_sink_cap_priv {
|
||||
struct di_cta_hdmi_forum_sink_cap base;
|
||||
struct di_cta_hdmi_dsc dsc;
|
||||
};
|
||||
|
||||
struct di_cta_data_block {
|
||||
enum di_cta_data_block_tag tag;
|
||||
|
||||
/* Used for DI_CTA_DATA_BLOCK_VIDEO */
|
||||
struct di_cta_video_block_priv video;
|
||||
/* Used for DI_CTA_DATA_BLOCK_YCBCR420 */
|
||||
struct di_cta_ycbcr420_video_block_priv ycbcr420;
|
||||
/* used for DI_CTA_DATA_BLOCK_AUDIO */
|
||||
struct di_cta_audio_block_priv 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_display_device_block vesa_display_device;
|
||||
/* 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_block vesa_transfer_characteristics;
|
||||
/* Used for DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP */
|
||||
struct di_cta_ycbcr420_cap_map_block ycbcr420_cap_map;
|
||||
/* Used for DI_CTA_DATA_BLOCK_HDMI_AUDIO */
|
||||
struct di_cta_hdmi_audio_block_priv hdmi_audio;
|
||||
/* Used for DI_CTA_DATA_BLOCK_INFOFRAME */
|
||||
struct di_cta_infoframe_block_priv infoframe;
|
||||
/* Used for DI_CTA_DATA_BLOCK_ROOM_CONFIG */
|
||||
struct di_cta_room_configuration_block room_config;
|
||||
/* Used for DI_CTA_DATA_BLOCK_SPEAKER_LOCATION */
|
||||
struct di_cta_speaker_location_priv speaker_location;
|
||||
/* Used for DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF */
|
||||
struct di_cta_video_format_pref_priv video_format_pref;
|
||||
/* Used for DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII */
|
||||
struct di_cta_type_vii_timing_priv did_vii_timing;
|
||||
/* Used for DI_CTA_DATA_BLOCK_VENDOR_HDMI */
|
||||
struct di_cta_vendor_hdmi_block_priv vendor_hdmi;
|
||||
/* Used for DI_CTA_DATA_BLOCK_HDR10PLUS */
|
||||
struct di_cta_hdr10plus_block hdr10plus;
|
||||
/* Used for DI_CTA_DATA_BLOCK_DOLBY_VIDEO */
|
||||
struct di_cta_dolby_video_block_priv dolby_video;
|
||||
/* Used for DI_CTA_DATA_BLOCK_HDMI_SINK_CAP */
|
||||
struct di_cta_hdmi_forum_sink_cap_priv hdmi_sink_cap;
|
||||
/* Used for DI_CTA_DATA_BLOCK_VENDOR_HDMI_FORUM */
|
||||
struct di_cta_vendor_hdmi_forum_block_priv vendor_hdmi_forum;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
bool
|
||||
_di_cta_data_block_parse(struct di_cta *cta, uint8_t raw_tag, const uint8_t *data, size_t size,
|
||||
struct di_cta_data_block **data_block_out);
|
||||
|
||||
void
|
||||
_di_cta_data_block_destroy(struct di_cta_data_block *data_block);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef DI_EDID_DECODE_H
|
||||
#define DI_EDID_DECODE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <libdisplay-info/edid.h>
|
||||
#include <libdisplay-info/cta.h>
|
||||
#include <libdisplay-info/cvt.h>
|
||||
#include <libdisplay-info/displayid.h>
|
||||
|
||||
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_cta_data_block(const struct di_cta_data_block *data_block,
|
||||
const struct di_cta_data_block *const *data_blocks);
|
||||
|
||||
void
|
||||
print_displayid(const struct di_displayid *displayid);
|
||||
|
||||
void
|
||||
print_displayid2(const struct di_displayid2 *displayid2);
|
||||
|
||||
void
|
||||
print_displayid_type_i_ii_vii_timing(const struct di_displayid_type_i_ii_vii_timing *t,
|
||||
int indent, const char *prefix);
|
||||
|
||||
void
|
||||
print_cta_vic_timing(struct di_cta_vic vic);
|
||||
|
||||
void
|
||||
print_hdmi_vic_timing(const struct di_hdmi_vic vic);
|
||||
|
||||
void
|
||||
print_dmt_timing_code(struct di_dmt_code dmt_code);
|
||||
|
||||
void
|
||||
print_cvt_timing(struct di_cvt_timing *t, struct di_cvt_options *options,
|
||||
int hratio, int vratio, bool preferred, bool rb);
|
||||
|
||||
void
|
||||
compute_aspect_ratio(int width, int height, int *horiz_ratio, int *vert_ratio);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef DISPLAYID_H
|
||||
#define DISPLAYID_H
|
||||
|
||||
/**
|
||||
* Private header for the low-level DisplayID API.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libdisplay-info/displayid.h>
|
||||
|
||||
#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
|
||||
/**
|
||||
* The maximum number of type II timings in a data block.
|
||||
*
|
||||
* A DisplayID data block has a maximum payload size of 248 bytes, and each type
|
||||
* I timing takes up 11 bytes.
|
||||
*/
|
||||
#define DISPLAYID_MAX_TYPE_II_TIMINGS 22
|
||||
/**
|
||||
* The maxiumum number of type III timings in a data block.
|
||||
*
|
||||
* A DisplayID data block has a maximum payload size of 248 bytes, and each type
|
||||
* III timing takes up 3 bytes.
|
||||
*/
|
||||
#define DISPLAYID_MAX_TYPE_III_TIMINGS 82
|
||||
|
||||
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_ii_vii_timing *type_i_timings[DISPLAYID_MAX_TYPE_I_TIMINGS + 1];
|
||||
size_t type_i_timings_len;
|
||||
|
||||
/* Used for TYPE_II_TIMING, NULL-terminated */
|
||||
struct di_displayid_type_i_ii_vii_timing *type_ii_timings[DISPLAYID_MAX_TYPE_II_TIMINGS + 1];
|
||||
size_t type_ii_timings_len;
|
||||
|
||||
/* Used for TYPE_III_TIMING, NULL-terminated */
|
||||
struct di_displayid_type_iii_timing *type_iii_timings[DISPLAYID_MAX_TYPE_III_TIMINGS + 1];
|
||||
size_t type_iii_timings_len;
|
||||
|
||||
/* Used for TYPE_IV_TIMING */
|
||||
struct di_displayid_type_iv_timing type_iv_timing;
|
||||
|
||||
/* 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);
|
||||
|
||||
void
|
||||
_di_displayid_parse_type_1_7_timing(struct di_displayid_type_i_ii_vii_timing *timing,
|
||||
struct di_logger *logger,
|
||||
const char *prefix,
|
||||
const uint8_t *data,
|
||||
bool is_type7);
|
||||
|
||||
int
|
||||
_di_displayid_parse_version(const uint8_t *data, size_t size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef DISPLAYID2_H
|
||||
#define DISPLAYID2_H
|
||||
|
||||
/**
|
||||
* Private header for the low-level DisplayID v2 API.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libdisplay-info/displayid2.h>
|
||||
|
||||
#include "cta.h"
|
||||
#include "log.h"
|
||||
|
||||
/**
|
||||
* The maximum number of data blocks in a DisplayID v2 section.
|
||||
*
|
||||
* A DisplayID v2 section has a maximum payload size of 251 bytes (256 bytes
|
||||
* maximum size, 5 bytes header), and each data block has a minimum size of
|
||||
* 3 bytes.
|
||||
*/
|
||||
#define DISPLAYID2_MAX_DATA_BLOCKS 83
|
||||
|
||||
/**
|
||||
* The maximum number of CTA data blocks in a DisplayID v2 CTA-861 data block.
|
||||
*
|
||||
* A DisplayID v2 section has a maximum payload size of 251 bytes (256 bytes
|
||||
* maximum size, 5 bytes header), a DisplayID v2 data block has a maximum
|
||||
* payload size of 248 bytes (3 bytes header), and each CTA data block takes at
|
||||
* least 1 byte.
|
||||
*/
|
||||
#define DISPLAYID2_CTA861_MAX_DATA_BLOCKS 248
|
||||
|
||||
struct di_displayid2 {
|
||||
int revision;
|
||||
enum di_displayid2_product_primary_use_case product_primary_use_case;
|
||||
|
||||
struct di_displayid2_data_block *data_blocks[DISPLAYID2_MAX_DATA_BLOCKS + 1];
|
||||
size_t data_blocks_len;
|
||||
|
||||
struct di_logger *logger;
|
||||
};
|
||||
|
||||
struct di_displayid2_data_block {
|
||||
enum di_displayid2_data_block_tag tag;
|
||||
|
||||
/* Used for DI_DISPLAYID2_DATA_BLOCK_CTA861 */
|
||||
struct {
|
||||
/* NULL-terminated */
|
||||
struct di_cta_data_block *data_blocks[DISPLAYID2_CTA861_MAX_DATA_BLOCKS + 1];
|
||||
size_t data_blocks_len;
|
||||
} cta861;
|
||||
};
|
||||
|
||||
bool
|
||||
_di_displayid2_parse(struct di_displayid2 *displayid2, const uint8_t *data,
|
||||
size_t size, struct di_logger *logger);
|
||||
|
||||
void
|
||||
_di_displayid2_finish(struct di_displayid2 *displayid2);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,160 @@
|
||||
#ifndef EDID_H
|
||||
#define EDID_H
|
||||
|
||||
/**
|
||||
* Private header for the low-level EDID API.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libdisplay-info/edid.h>
|
||||
|
||||
#include "cta.h"
|
||||
#include "displayid.h"
|
||||
#include "displayid2.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 */
|
||||
struct di_edid_established_timings_iii established_timings_iii;
|
||||
/* Used for DCM_DATA */
|
||||
struct di_edid_color_management_data dcm_data;
|
||||
/* Used for 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 */
|
||||
int displayid_version;
|
||||
struct di_displayid displayid;
|
||||
struct di_displayid2 displayid2;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef INFO_H
|
||||
#define INFO_H
|
||||
|
||||
/**
|
||||
* Private header for the high-level API.
|
||||
*/
|
||||
|
||||
#include <libdisplay-info/info.h>
|
||||
|
||||
/**
|
||||
* All information here is derived from low-level information contained in
|
||||
* struct di_info. These are exposed by the high-level API only.
|
||||
*/
|
||||
struct di_derived_info {
|
||||
struct di_hdr_static_metadata hdr_static_metadata;
|
||||
struct di_color_primaries color_primaries;
|
||||
struct di_supported_signal_colorimetry supported_signal_colorimetry;
|
||||
};
|
||||
|
||||
struct di_info {
|
||||
struct di_edid *edid;
|
||||
|
||||
char *failure_msg;
|
||||
|
||||
struct di_derived_info derived;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef DI_CTA_VIC_H
|
||||
#define DI_CTA_VIC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Low-level API for CTA Video Identification Codes (CTA VIC).
|
||||
*/
|
||||
|
||||
/**
|
||||
* A CTA Video Identification Code (VIC).
|
||||
*/
|
||||
struct di_cta_vic {
|
||||
/* Video Identification Code (VIC) */
|
||||
uint8_t code;
|
||||
};
|
||||
|
||||
/**
|
||||
* CTA video format picture aspect ratio.
|
||||
*/
|
||||
enum di_cta_vic_video_format_picture_aspect_ratio {
|
||||
DI_CTA_VIC_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_4_3, /* 4:3 */
|
||||
DI_CTA_VIC_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_16_9, /* 16:9 */
|
||||
DI_CTA_VIC_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_64_27, /* 64:27 */
|
||||
DI_CTA_VIC_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_256_135, /* 256:135 */
|
||||
};
|
||||
|
||||
/**
|
||||
* CTA video format sync pulse polarity.
|
||||
*/
|
||||
enum di_cta_vic_video_format_sync_polarity {
|
||||
DI_CTA_VIC_VIDEO_FORMAT_SYNC_NEGATIVE, /* Negative */
|
||||
DI_CTA_VIC_VIDEO_FORMAT_SYNC_POSITIVE, /* Positive */
|
||||
};
|
||||
|
||||
/**
|
||||
* A CTA-861 video format, defined in section 4.
|
||||
*/
|
||||
struct di_cta_vic_video_format {
|
||||
/* Video Identification Code (VIC) */
|
||||
struct di_cta_vic 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_vic_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_vic_video_format_picture_aspect_ratio picture_aspect_ratio;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a CTA-861 video format from a CTA VIC.
|
||||
*
|
||||
* Returns NULL if the CTA VIC is unknown.
|
||||
*/
|
||||
const struct di_cta_vic_video_format *
|
||||
di_cta_vic_video_format_from_vic(struct di_cta_vic vic);
|
||||
|
||||
/**
|
||||
* Get a CTA VIC from a CTA-861 video format.
|
||||
*
|
||||
* Returns 0 if the video format is unknown.
|
||||
*/
|
||||
struct di_cta_vic
|
||||
di_cta_vic_video_format_to_vic(const struct di_cta_vic_video_format *format);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,76 @@
|
||||
#ifndef DI_CVT_H
|
||||
#define DI_CVT_H
|
||||
|
||||
/**
|
||||
* Low-level API for VESA Coordinated Video Timings (CVT) version 2.0.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
@@ -1,3 +1,399 @@
|
||||
#pragma once
|
||||
#ifndef DI_DISPLAYID_H
|
||||
#define DI_DISPLAYID_H
|
||||
|
||||
#include "info.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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libdisplay-info/dmt.h>
|
||||
#include <libdisplay-info/hdmi-vic.h>
|
||||
#include <libdisplay-info/cta-vic.h>
|
||||
|
||||
/**
|
||||
* 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_ii_vii_timing_stereo_3d {
|
||||
/* This timing is always displayed monoscopic (no stereo) */
|
||||
DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_NEVER = 0x00,
|
||||
/* This timing is always displayed in stereo */
|
||||
DI_DISPLAYID_TYPE_I_II_VII_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_II_VII_TIMING_STEREO_3D_USER = 0x02,
|
||||
};
|
||||
|
||||
enum di_displayid_timing_aspect_ratio {
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_1_1 = 0x00,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_5_4 = 0x01,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_4_3 = 0x02,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_15_9 = 0x03,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_16_9 = 0x04,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_16_10 = 0x05,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_64_27 = 0x06,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_256_135 = 0x07,
|
||||
DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED = 0x08,
|
||||
};
|
||||
|
||||
enum di_displayid_type_i_ii_vii_timing_sync_polarity {
|
||||
DI_DISPLAYID_TYPE_I_II_VII_TIMING_SYNC_NEGATIVE = 0x00,
|
||||
DI_DISPLAYID_TYPE_I_II_VII_TIMING_SYNC_POSITIVE = 0x01,
|
||||
};
|
||||
|
||||
/**
|
||||
* Type I timing, defined in DisplayID 1.3 section 4.4.1 and
|
||||
* Type II timing, defined in DisplayID 1.3 section 4.4.2 and
|
||||
* Type VII timing, defined in DisplayID 2.0 section 4.3.1.
|
||||
*/
|
||||
struct di_displayid_type_i_ii_vii_timing {
|
||||
double pixel_clock_mhz; /* mega-hertz */
|
||||
bool preferred;
|
||||
enum di_displayid_type_i_ii_vii_timing_stereo_3d stereo_3d;
|
||||
bool interlaced;
|
||||
enum di_displayid_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_ii_vii_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_ii_vii_timing *const *
|
||||
di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *data_block);
|
||||
|
||||
/**
|
||||
* Get type II 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_II_TIMING.
|
||||
*/
|
||||
const struct di_displayid_type_i_ii_vii_timing *const *
|
||||
di_displayid_data_block_get_type_ii_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);
|
||||
|
||||
/**
|
||||
* Formula/algorithm for type III timings.
|
||||
*/
|
||||
enum di_displayid_type_iii_timing_algo {
|
||||
/* VESA CVT, standard blanking */
|
||||
DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING = 0,
|
||||
/* VESA CVT, reduced blanking */
|
||||
DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Type III timing, defined in section 4.4.3.
|
||||
*/
|
||||
struct di_displayid_type_iii_timing {
|
||||
bool preferred;
|
||||
enum di_displayid_type_iii_timing_algo algo;
|
||||
enum di_displayid_timing_aspect_ratio aspect_ratio;
|
||||
/* Horizontal Active Image (in pixels) */
|
||||
int32_t horiz_active;
|
||||
bool interlaced;
|
||||
/* Frame/Field Refresh Rate (in Hz) */
|
||||
int32_t refresh_rate_hz;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get type III 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_III_TIMING.
|
||||
*/
|
||||
const struct di_displayid_type_iii_timing *const *
|
||||
di_displayid_data_block_get_type_iii_timings(const struct di_displayid_data_block *data_block);
|
||||
|
||||
/**
|
||||
* Type IV timing, Timing Code Type
|
||||
*/
|
||||
enum di_displayid_type_iv_timing_code_type {
|
||||
/* DMT Timing Code */
|
||||
DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_DMT = 0,
|
||||
/* CTA/CEA VIC Timing Code */
|
||||
DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_CTA = 1,
|
||||
/* HDMI VIC Timing Code */
|
||||
DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_HDMI = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Type IV timing, defined in section 4.4.4.
|
||||
*/
|
||||
struct di_displayid_type_iv_timing {
|
||||
/* Type of codes */
|
||||
enum di_displayid_type_iv_timing_code_type type;
|
||||
/* Number of codes in one of dmts, cta_vics or hdmi_vics, depending on the value of type*/
|
||||
size_t codes_len;
|
||||
/* DMT Timing Codes, if type is DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_DMT, NULL otherwise */
|
||||
const struct di_dmt_code *dmts;
|
||||
/* CTA VICs, if type is DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_CTA, NULL otherwise */
|
||||
const struct di_cta_vic *cta_vics;
|
||||
/* HDMI VICs, if type is DI_DISPLAYID_TYPE_IV_TIMING_CODE_TYPE_VIC_HDMI, NULL otherwise */
|
||||
const struct di_hdmi_vic *hdmi_vics;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get type IV timing 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_IV_TIMING.
|
||||
*/
|
||||
const struct di_displayid_type_iv_timing *
|
||||
di_displayid_data_block_get_type_iv_timing(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
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef DI_DISPLAYID2_H
|
||||
#define DI_DISPLAYID2_H
|
||||
|
||||
/**
|
||||
* libdisplay-info's low-level API for VESA Display Identification Data
|
||||
* (DisplayID) version 2.
|
||||
*
|
||||
* The library implements DisplayID version 2.1, available at:
|
||||
* https://vesa.org/vesa-standards/
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* DisplayID v2 data structure.
|
||||
*/
|
||||
struct di_displayid2;
|
||||
|
||||
/**
|
||||
* Get the DisplayID v2 revision.
|
||||
*/
|
||||
int
|
||||
di_displayid2_get_revision(const struct di_displayid2 *displayid2);
|
||||
|
||||
/**
|
||||
* Get DisplayID v2 data blocks.
|
||||
*
|
||||
* The returned array is NULL-terminated.
|
||||
*/
|
||||
const struct di_displayid2_data_block *const *
|
||||
di_displayid2_get_data_blocks(const struct di_displayid2 *displayid2);
|
||||
|
||||
/**
|
||||
* Product primary use case identifier, defined in table 2-3.
|
||||
*/
|
||||
enum di_displayid2_product_primary_use_case {
|
||||
/* Extension section */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_EXTENSION = 0x00,
|
||||
/* Test structure */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_TEST = 0x01,
|
||||
/* Generic display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_GENERIC = 0x02,
|
||||
/* Television display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_TV = 0x03,
|
||||
/* Desktop productivity display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_DESKTOP_PRODUCTIVITY = 0x04,
|
||||
/* Desktop gaming display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_DESKTOP_GAMING = 0x05,
|
||||
/* Presentation display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_PRESENTATION = 0x06,
|
||||
/* Head-mounted Virtual Reality (VR) display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_HMD_VR = 0x07,
|
||||
/* Head-mounted Augmented Reality (AR) display */
|
||||
DI_DISPLAYID2_PRODUCT_PRIMARY_USE_CASE_HMD_AR = 0x08,
|
||||
};
|
||||
|
||||
/**
|
||||
* DisplayID v2 data block tag.
|
||||
*/
|
||||
enum di_displayid2_data_block_tag {
|
||||
/* Product Identification Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_PRODUCT_ID = 0x20,
|
||||
/* Display Parameters Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_DISPLAY_PARAMS = 0x21,
|
||||
/* Type VII Timing – Detailed Timing Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_TYPE_VII_TIMING = 0x22,
|
||||
/* Type VIII Timing – Enumerated Timing Code Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_TYPE_VIII_TIMING = 0x23,
|
||||
/* Type IX Timing – Formula-based Timing Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_TYPE_IX_TIMING = 0x24,
|
||||
/* Dynamic Video Timing Range Limits Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_DYN_TIMING_RANGE_LIMITS = 0x25,
|
||||
/* Display Interface Features Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_DISPLAY_INTERFACE_FEATURES = 0x26,
|
||||
/* Stereo Display Interface Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_STEREO_DISPLAY_INTERFACE = 0x27,
|
||||
/* Tiled Display Topology Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_TILED_DISPLAY_TOPO = 0x28,
|
||||
/* ContainerID Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_CONTAINERID = 0x29,
|
||||
/* Type X Timing – Formula-based Timing Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_TYPE_X_TIMING = 0x2A,
|
||||
/* Adaptive-Sync Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_ADAPTIVE_SYNC = 0x2B,
|
||||
/* ARVR_HMD Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_ARVR_HMD = 0x2C,
|
||||
/* ARVR_Layer Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_ARVR_LAYER = 0x2D,
|
||||
/* CTA-861 Data Block Encapsulation DisplayID Data Block */
|
||||
DI_DISPLAYID2_DATA_BLOCK_CTA861 = 0x81,
|
||||
};
|
||||
|
||||
/**
|
||||
* A DisplayID v2 data block.
|
||||
*/
|
||||
struct di_displayid2_data_block;
|
||||
|
||||
/**
|
||||
* Get a DisplayID v2 data block tag.
|
||||
*/
|
||||
enum di_displayid2_data_block_tag
|
||||
di_displayid2_data_block_get_tag(const struct di_displayid2_data_block *data_block);
|
||||
|
||||
/**
|
||||
* Get the DisplayID v2 product primary use case.
|
||||
*/
|
||||
enum di_displayid2_product_primary_use_case
|
||||
di_displayid2_get_product_primary_use_case(const struct di_displayid2 *displayid2);
|
||||
|
||||
/**
|
||||
* Get CTA data blocks from a DisplayID v2 CTA-861 data block.
|
||||
*
|
||||
* The returned array is NULL-terminated.
|
||||
*
|
||||
* Returns NULL if the data block tag isn't DI_DISPLAYID2_DATA_BLOCK_CTA861.
|
||||
*/
|
||||
const struct di_cta_data_block *const *
|
||||
di_displayid2_data_block_get_cta_data_blocks(const struct di_displayid2_data_block *data_block);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
#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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* A DMT Timing Code.
|
||||
*/
|
||||
struct di_dmt_code {
|
||||
/* Timing Code */
|
||||
uint8_t code;
|
||||
};
|
||||
|
||||
/**
|
||||
* A DMT timing.
|
||||
*/
|
||||
struct di_dmt_timing {
|
||||
/* DMT Timing Code */
|
||||
struct di_dmt_code dmt_code;
|
||||
/* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a VESA Display Monitor Timing (DMT) from a DMT Timing Code.
|
||||
*
|
||||
* Returns NULL if the DMT Timing Code is unknown.
|
||||
*/
|
||||
const struct di_dmt_timing *
|
||||
di_dmt_timing_from_code(struct di_dmt_code dmt_code);
|
||||
|
||||
/**
|
||||
* Get a DMT Timing Code from a VESA Display Monitor Timing (DMT).
|
||||
*
|
||||
* Returns a code of 0 if the DMT Timing Code is unknown.
|
||||
*/
|
||||
struct di_dmt_code
|
||||
di_dmt_timing_to_code(const struct di_dmt_timing *timing);
|
||||
|
||||
/* See <libdisplay-info/edid.h> */
|
||||
struct di_edid_standard_timing;
|
||||
|
||||
/**
|
||||
* Get a 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_dmt_timing_from_edid_standard_timing(const struct di_edid_standard_timing *t);
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,833 @@
|
||||
#pragma once
|
||||
#ifndef DI_EDID_H
|
||||
#define DI_EDID_H
|
||||
|
||||
#include "info.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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
int64_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);
|
||||
|
||||
/**
|
||||
* Established timings III
|
||||
*/
|
||||
struct di_edid_established_timings_iii {
|
||||
size_t dmt_codes_len;
|
||||
struct di_dmt_code *dmt_codes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the established timings III from an EDID display descriptor.
|
||||
*
|
||||
* Returns NULL if the display descriptor tag isn't
|
||||
* DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III.
|
||||
*/
|
||||
const struct di_edid_established_timings_iii *
|
||||
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 <libdisplay-info/cta.h> */
|
||||
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 <libdisplay-info/displayid.h> */
|
||||
struct di_displayid;
|
||||
|
||||
/**
|
||||
* Get a DisplayID extension block.
|
||||
*
|
||||
* Returns NULL if the extension block tag is not DI_EDID_EXT_DISPLAYID or if
|
||||
* the block does not contain a version 1 DisplayID blob.
|
||||
*/
|
||||
const struct di_displayid *
|
||||
di_edid_ext_get_displayid(const struct di_edid_ext *ext);
|
||||
|
||||
/* See <libdisplay-info/displayid2.h> */
|
||||
struct di_displayid2;
|
||||
|
||||
/**
|
||||
* Get a DisplayID v2 extension block.
|
||||
*
|
||||
* Returns NULL if the extension block tag is not DI_EDID_EXT_DISPLAYID or if
|
||||
* the block does not contain a version 2 DisplayID blob.
|
||||
*/
|
||||
const struct di_displayid2 *
|
||||
di_edid_ext_get_displayid2(const struct di_edid_ext *ext);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef DI_GTF_H
|
||||
#define DI_GTF_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef DI_HDMI_VIC_H
|
||||
#define DI_HDMI_VIC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Low-level API for HDMI Video Identification Codes (HDMI VIC).
|
||||
*/
|
||||
|
||||
/**
|
||||
* A HDMI Video Identification Code (VIC).
|
||||
*/
|
||||
struct di_hdmi_vic {
|
||||
/* Video Identification Code (VIC) */
|
||||
uint8_t code;
|
||||
};
|
||||
|
||||
/**
|
||||
* A HDMI video format, not to be confused with a CTA-861 video format.
|
||||
*/
|
||||
struct di_hdmi_vic_video_format {
|
||||
/* Video Identification Code (VIC) */
|
||||
struct di_hdmi_vic 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;
|
||||
/* Pixel clock in Hz */
|
||||
int64_t pixel_clock_hz;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a HDMI video format from a HDMI VIC.
|
||||
*
|
||||
* Returns NULL if the HDMI VIC is unknown.
|
||||
*/
|
||||
const struct di_hdmi_vic_video_format *
|
||||
di_hdmi_vic_video_format_from_vic(struct di_hdmi_vic vic);
|
||||
|
||||
/**
|
||||
* Get a HDMI VIC from a HDMI video format.
|
||||
*
|
||||
* Returns 0 if the video format is unknown.
|
||||
*/
|
||||
struct di_hdmi_vic
|
||||
di_hdmi_vic_video_format_to_vic(const struct di_hdmi_vic_video_format *format);
|
||||
|
||||
#endif
|
||||
@@ -1,108 +1,247 @@
|
||||
#pragma once
|
||||
#ifndef DI_INFO_H
|
||||
#define DI_INFO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**
|
||||
* 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;
|
||||
struct di_edid;
|
||||
struct di_edid_ext;
|
||||
struct di_edid_cta;
|
||||
struct di_displayid;
|
||||
struct di_cta_data_block;
|
||||
struct di_displayid_data_block;
|
||||
|
||||
struct di_edid_vendor_product {
|
||||
char manufacturer[4];
|
||||
int product;
|
||||
int serial;
|
||||
int manufacture_week;
|
||||
int manufacture_year;
|
||||
int model_year;
|
||||
/**
|
||||
* 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 <libdisplay-info/edid.h>. 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);
|
||||
|
||||
/**
|
||||
* Display HDR static metadata
|
||||
*/
|
||||
struct di_hdr_static_metadata {
|
||||
/* 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 metadata types: */
|
||||
|
||||
/* Static Metadata Type 1 */
|
||||
bool type1;
|
||||
|
||||
/* Supported EOFTs: */
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
struct di_edid_chromaticity_coords {
|
||||
float red_x;
|
||||
float red_y;
|
||||
float green_x;
|
||||
float green_y;
|
||||
float blue_x;
|
||||
float blue_y;
|
||||
float white_x;
|
||||
float white_y;
|
||||
/**
|
||||
* Get HDR static metadata support information as defined in ANSI/CTA-861-H
|
||||
* as HDR Static Metadata Data Block.
|
||||
*
|
||||
* The returned pointer is owned by the struct di_info passed in. It remains
|
||||
* valid only as long as the di_info exists, and must not be freed by the
|
||||
* caller.
|
||||
*
|
||||
* This function does not return NULL. When HDR static metadata does not exist,
|
||||
* all luminance fields are zero and only traditional_sdr is flagged as
|
||||
* supported.
|
||||
*/
|
||||
const struct di_hdr_static_metadata *
|
||||
di_info_get_hdr_static_metadata(const struct di_info *info);
|
||||
|
||||
/** CIE 1931 2-degree observer chromaticity coordinates */
|
||||
struct di_chromaticity_cie1931 {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct di_cta_hdr_static_metadata_eotfs {
|
||||
bool pq;
|
||||
/** Display color primaries and default white point */
|
||||
struct di_color_primaries {
|
||||
/* Are primary[] given */
|
||||
bool has_primaries;
|
||||
|
||||
/* Is default_white given */
|
||||
bool has_default_white_point;
|
||||
|
||||
/* Chromaticities of the primaries in RGB order
|
||||
*
|
||||
* Either all values are given, or they are all zeroes.
|
||||
*/
|
||||
struct di_chromaticity_cie1931 primary[3];
|
||||
|
||||
/* Default white point chromaticity
|
||||
*
|
||||
* If non-zero, this should be the display white point when it has
|
||||
* been reset to its factory defaults. Either both x and y are given,
|
||||
* or they are both zero.
|
||||
*/
|
||||
struct di_chromaticity_cie1931 default_white;
|
||||
};
|
||||
|
||||
struct di_cta_hdr_static_metadata_block {
|
||||
int desired_content_min_luminance;
|
||||
int desired_content_max_luminance;
|
||||
int desired_content_max_frame_avg_luminance;
|
||||
const struct di_cta_hdr_static_metadata_eotfs *eotfs;
|
||||
/**
|
||||
* Get display color primaries and default white point
|
||||
*
|
||||
* Get the parameters of the default RGB colorimetry mode which is always
|
||||
* supported. Primaries for monochrome displays might be all zeroes.
|
||||
*
|
||||
* These primaries might not be display's physical primaries, but only the
|
||||
* primaries of the default RGB colorimetry signal when using IT Video Format
|
||||
* (ANSI/CTA-861-H, Section 5).
|
||||
*
|
||||
* The returned pointer is owned by the struct di_info passed in. It remains
|
||||
* valid only as long as the di_info exists, and must not be freed by the
|
||||
* caller.
|
||||
*
|
||||
* This function does not return NULL.
|
||||
*/
|
||||
const struct di_color_primaries *
|
||||
di_info_get_default_color_primaries(const struct di_info *info);
|
||||
|
||||
/** Additional signal colorimetry encodings supported by the display */
|
||||
struct di_supported_signal_colorimetry {
|
||||
/* Rec. ITU-R BT.2020 constant luminance YCbCr */
|
||||
bool bt2020_cycc;
|
||||
/* Rec. ITU-R BT.2020 non-constant luminance YCbCr */
|
||||
bool bt2020_ycc;
|
||||
/* Rec. ITU-R BT.2020 RGB */
|
||||
bool bt2020_rgb;
|
||||
/* SMPTE ST 2113 RGB: P3D65 and P3DCI */
|
||||
bool st2113_rgb;
|
||||
/* Rec. ITU-R BT.2100 ICtCp HDR (with PQ and/or HLG) */
|
||||
bool ictcp;
|
||||
};
|
||||
|
||||
struct di_cta_colorimetry_block {
|
||||
bool bt2020_rgb;
|
||||
};
|
||||
/**
|
||||
* Get signal colorimetry encodings supported by the display
|
||||
*
|
||||
* These signal colorimetry encodings are supported in addition to the
|
||||
* display's default RGB colorimetry. When you wish to use one of the additional
|
||||
* encodings, they need to be explicitly enabled in the video signal. How to
|
||||
* do that is specific to the signalling used, e.g. HDMI.
|
||||
*
|
||||
* Signal colorimetry encoding provides the color space that the signal is
|
||||
* encoded for. This includes primary and white point chromaticities, and the
|
||||
* YCbCr-RGB conversion if necessary. Also the transfer function is implied
|
||||
* unless explicitly set otherwise, e.g. with HDR static metadata.
|
||||
* See ANSI/CTA-861-H for details.
|
||||
*
|
||||
* The signal color volume can be considerably larger than the physically
|
||||
* displayable color volume.
|
||||
*
|
||||
* The returned pointer is owned by the struct di_info passed in. It remains
|
||||
* valid only as long as the di_info exists, and must not be freed by the
|
||||
* caller.
|
||||
*
|
||||
* This function does not return NULL.
|
||||
*/
|
||||
const struct di_supported_signal_colorimetry *
|
||||
di_info_get_supported_signal_colorimetry(const struct di_info *info);
|
||||
|
||||
struct di_displayid_display_params {
|
||||
int horiz_pixels;
|
||||
int vert_pixels;
|
||||
};
|
||||
/**
|
||||
* Get display default transfer characteristic exponent (gamma)
|
||||
*
|
||||
* This should be the display gamma value when the display has been reset to
|
||||
* its factory defaults, and it is driven with the default RGB colorimetry.
|
||||
* The value is zero when unknown.
|
||||
*/
|
||||
float
|
||||
di_info_get_default_gamma(const struct di_info *info);
|
||||
|
||||
struct di_displayid_type_i_ii_vii_timing {
|
||||
bool preferred;
|
||||
int horiz_active;
|
||||
int vert_active;
|
||||
};
|
||||
|
||||
struct di_edid_misc_features {
|
||||
bool preferred_timing_is_native;
|
||||
};
|
||||
|
||||
struct di_edid_detailed_timing_def {
|
||||
int horiz_image_mm;
|
||||
int vert_image_mm;
|
||||
int horiz_video;
|
||||
int vert_video;
|
||||
};
|
||||
|
||||
struct di_edid_screen_size {
|
||||
int width_cm;
|
||||
int height_cm;
|
||||
};
|
||||
|
||||
const struct di_info *di_info_parse_edid(const void *data, size_t size);
|
||||
void di_info_destroy(const struct di_info *info);
|
||||
const struct di_edid *di_info_get_edid(const struct di_info *info);
|
||||
char *di_info_get_model(const struct di_info *info);
|
||||
char *di_info_get_serial(const struct di_info *info);
|
||||
|
||||
const struct di_edid_detailed_timing_def *const *di_edid_get_detailed_timing_defs(const struct di_edid *edid);
|
||||
const struct di_edid_screen_size *di_edid_get_screen_size(const struct di_edid *edid);
|
||||
const struct di_edid_vendor_product *di_edid_get_vendor_product(const struct di_edid *edid);
|
||||
const struct di_edid_chromaticity_coords *di_edid_get_chromaticity_coords(const struct di_edid *edid);
|
||||
const struct di_edid_ext *const *di_edid_get_extensions(const struct di_edid *edid);
|
||||
const struct di_edid_misc_features *di_edid_get_misc_features(const struct di_edid *edid);
|
||||
|
||||
const struct di_edid_cta *di_edid_ext_get_cta(const struct di_edid_ext *ext);
|
||||
const struct di_displayid *di_edid_ext_get_displayid(const struct di_edid_ext *ext);
|
||||
|
||||
const struct di_cta_data_block *const *di_edid_cta_get_data_blocks(const struct di_edid_cta *cta);
|
||||
const struct di_cta_hdr_static_metadata_block *di_cta_data_block_get_hdr_static_metadata(const struct di_cta_data_block *block);
|
||||
const struct di_cta_colorimetry_block *di_cta_data_block_get_colorimetry(const struct di_cta_data_block *block);
|
||||
|
||||
const struct di_displayid_data_block *const *di_displayid_get_data_blocks(const struct di_displayid *displayid);
|
||||
const struct di_displayid_display_params *di_displayid_data_block_get_display_params(const struct di_displayid_data_block *block);
|
||||
const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *block);
|
||||
const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block *block);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
/**
|
||||
* Private logging utilities.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
|
||||
void
|
||||
_di_logger_add_failure(struct di_logger *logger, const char fmt[], ...);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef MEMORY_STREAM_H
|
||||
#define MEMORY_STREAM_H
|
||||
|
||||
/**
|
||||
* Utility functions for memory streams.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,483 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "edid.h"
|
||||
#include "info.h"
|
||||
#include "memory-stream.h"
|
||||
|
||||
/* Generated file pnp-id-table.c: */
|
||||
const char *
|
||||
pnp_id_table(const char *key);
|
||||
|
||||
static bool
|
||||
cta_data_block_allowed_multiple(enum di_cta_data_block_tag tag)
|
||||
{
|
||||
/* See CTA-861-H, 7.6 Multiple Instances of Data Blocks. */
|
||||
switch (tag) {
|
||||
case DI_CTA_DATA_BLOCK_SPEAKER_ALLOC:
|
||||
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC:
|
||||
case DI_CTA_DATA_BLOCK_VIDEO_CAP:
|
||||
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE:
|
||||
case DI_CTA_DATA_BLOCK_COLORIMETRY:
|
||||
case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA:
|
||||
case DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF:
|
||||
case DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP:
|
||||
case DI_CTA_DATA_BLOCK_HDMI_AUDIO:
|
||||
case DI_CTA_DATA_BLOCK_ROOM_CONFIG:
|
||||
case DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE:
|
||||
case DI_CTA_DATA_BLOCK_HDMI_SINK_CAP:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct di_cta_data_block *
|
||||
find_cta_data_block(const struct di_cta_data_block *const *blocks, enum di_cta_data_block_tag tag)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; blocks[i] != NULL; i++) {
|
||||
if (di_cta_data_block_get_tag(blocks[i]) == tag)
|
||||
return blocks[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct di_cta_data_block *
|
||||
displayid2_get_cta_data_block(const struct di_displayid2 *displayid2, enum di_cta_data_block_tag tag)
|
||||
{
|
||||
size_t i;
|
||||
const struct di_displayid2_data_block *const *data_blocks;
|
||||
const struct di_cta_data_block *const *cta_data_blocks, *match;
|
||||
|
||||
data_blocks = di_displayid2_get_data_blocks(displayid2);
|
||||
for (i = 0; data_blocks[i] != NULL; i++) {
|
||||
cta_data_blocks = di_displayid2_data_block_get_cta_data_blocks(data_blocks[i]);
|
||||
if (cta_data_blocks == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match = find_cta_data_block(cta_data_blocks, tag);
|
||||
if (match != NULL) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct di_cta_data_block *
|
||||
edid_get_cta_data_block(const struct di_edid *edid, enum di_cta_data_block_tag tag)
|
||||
{
|
||||
const struct di_edid_ext *const *ext;
|
||||
|
||||
/*
|
||||
* Here we do not handle blocks that are allowed to occur in
|
||||
* multiple instances.
|
||||
*/
|
||||
assert(!cta_data_block_allowed_multiple(tag));
|
||||
|
||||
for (ext = di_edid_get_extensions(edid); *ext; ext++) {
|
||||
const struct di_edid_cta *cta;
|
||||
const struct di_displayid2 *displayid2;
|
||||
const struct di_cta_data_block *match;
|
||||
|
||||
match = NULL;
|
||||
switch (di_edid_ext_get_tag(*ext)) {
|
||||
case DI_EDID_EXT_CEA:
|
||||
cta = di_edid_ext_get_cta(*ext);
|
||||
match = find_cta_data_block(di_edid_cta_get_data_blocks(cta), tag);
|
||||
break;
|
||||
case DI_EDID_EXT_DISPLAYID:
|
||||
displayid2 = di_edid_ext_get_displayid2(*ext);
|
||||
if (displayid2 == NULL) {
|
||||
break;
|
||||
}
|
||||
match = displayid2_get_cta_data_block(displayid2, tag);
|
||||
break;
|
||||
default:
|
||||
break; /* Ignore */
|
||||
}
|
||||
|
||||
if (match != NULL) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
derive_edid_hdr_static_metadata(const struct di_edid *edid,
|
||||
struct di_hdr_static_metadata *hsm)
|
||||
{
|
||||
const struct di_cta_data_block *block;
|
||||
const struct di_cta_hdr_static_metadata_block *cta_hsm;
|
||||
|
||||
/* By default, everything unset and only traditional gamma supported. */
|
||||
hsm->traditional_sdr = true;
|
||||
|
||||
block = edid_get_cta_data_block(edid, DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA);
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
cta_hsm = di_cta_data_block_get_hdr_static_metadata(block);
|
||||
assert(cta_hsm);
|
||||
|
||||
hsm->desired_content_max_luminance = cta_hsm->desired_content_max_luminance;
|
||||
hsm->desired_content_max_frame_avg_luminance = cta_hsm->desired_content_max_frame_avg_luminance;
|
||||
hsm->desired_content_min_luminance = cta_hsm->desired_content_min_luminance;
|
||||
hsm->type1 = cta_hsm->descriptors->type1;
|
||||
hsm->traditional_sdr = cta_hsm->eotfs->traditional_sdr;
|
||||
hsm->traditional_hdr = cta_hsm->eotfs->traditional_hdr;
|
||||
hsm->pq = cta_hsm->eotfs->pq;
|
||||
hsm->hlg = cta_hsm->eotfs->hlg;
|
||||
}
|
||||
|
||||
static void
|
||||
derive_edid_color_primaries(const struct di_edid *edid,
|
||||
struct di_color_primaries *cc)
|
||||
{
|
||||
const struct di_edid_chromaticity_coords *cm;
|
||||
const struct di_edid_misc_features *misc;
|
||||
|
||||
/* Trust the flag more than the fields. */
|
||||
misc = di_edid_get_misc_features(edid);
|
||||
if (misc->srgb_is_primary) {
|
||||
/*
|
||||
* https://www.w3.org/Graphics/Color/sRGB.html
|
||||
* for lack of access to IEC 61966-2-1
|
||||
*/
|
||||
cc->primary[0].x = 0.640f; /* red */
|
||||
cc->primary[0].y = 0.330f;
|
||||
cc->primary[1].x = 0.300f; /* green */
|
||||
cc->primary[1].y = 0.600f;
|
||||
cc->primary[2].x = 0.150f; /* blue */
|
||||
cc->primary[2].y = 0.060f;
|
||||
cc->has_primaries = true;
|
||||
cc->default_white.x = 0.3127f; /* D65 */
|
||||
cc->default_white.y = 0.3290f;
|
||||
cc->has_default_white_point = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cm = di_edid_get_chromaticity_coords(edid);
|
||||
|
||||
/*
|
||||
* Broken EDID might have only partial values.
|
||||
* Require all values to report anything.
|
||||
*/
|
||||
if (cm->red_x > 0.0f &&
|
||||
cm->red_y > 0.0f &&
|
||||
cm->green_x > 0.0f &&
|
||||
cm->green_y > 0.0f &&
|
||||
cm->blue_x > 0.0f &&
|
||||
cm->blue_y > 0.0f) {
|
||||
cc->primary[0].x = cm->red_x;
|
||||
cc->primary[0].y = cm->red_y;
|
||||
cc->primary[1].x = cm->green_x;
|
||||
cc->primary[1].y = cm->green_y;
|
||||
cc->primary[2].x = cm->blue_x;
|
||||
cc->primary[2].y = cm->blue_y;
|
||||
cc->has_primaries = true;
|
||||
}
|
||||
if (cm->white_x > 0.0f && cm->white_y > 0.0f) {
|
||||
cc->default_white.x = cm->white_x;
|
||||
cc->default_white.y = cm->white_y;
|
||||
cc->has_default_white_point = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
derive_edid_supported_signal_colorimetry(const struct di_edid *edid,
|
||||
struct di_supported_signal_colorimetry *ssc)
|
||||
{
|
||||
const struct di_cta_data_block *block;
|
||||
const struct di_cta_colorimetry_block *cm;
|
||||
|
||||
/* Defaults to all unsupported. */
|
||||
|
||||
block = edid_get_cta_data_block(edid, DI_CTA_DATA_BLOCK_COLORIMETRY);
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
cm = di_cta_data_block_get_colorimetry(block);
|
||||
assert(cm);
|
||||
|
||||
ssc->bt2020_cycc = cm->bt2020_cycc;
|
||||
ssc->bt2020_ycc = cm->bt2020_ycc;
|
||||
ssc->bt2020_rgb = cm->bt2020_rgb;
|
||||
ssc->st2113_rgb = cm->st2113_rgb;
|
||||
ssc->ictcp = cm->ictcp;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
derive_edid_hdr_static_metadata(info->edid, &info->derived.hdr_static_metadata);
|
||||
derive_edid_color_primaries(info->edid, &info->derived.color_primaries);
|
||||
derive_edid_supported_signal_colorimetry(info->edid, &info->derived.supported_signal_colorimetry);
|
||||
|
||||
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;
|
||||
enum di_edid_display_descriptor_tag tag;
|
||||
const char *str;
|
||||
|
||||
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++) {
|
||||
tag = di_edid_display_descriptor_get_tag(desc[i]);
|
||||
if (tag != DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME)
|
||||
continue;
|
||||
str = di_edid_display_descriptor_get_string(desc[i]);
|
||||
if (str[0] == '\0')
|
||||
continue;
|
||||
encode_ascii_string(m.fp, str);
|
||||
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;
|
||||
enum di_edid_display_descriptor_tag tag;
|
||||
const char *str;
|
||||
|
||||
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++) {
|
||||
tag = di_edid_display_descriptor_get_tag(desc[i]);
|
||||
if (tag != DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL)
|
||||
continue;
|
||||
str = di_edid_display_descriptor_get_string(desc[i]);
|
||||
if (str[0] == '\0')
|
||||
continue;
|
||||
encode_ascii_string(m.fp, str);
|
||||
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;
|
||||
}
|
||||
|
||||
const struct di_hdr_static_metadata *
|
||||
di_info_get_hdr_static_metadata(const struct di_info *info)
|
||||
{
|
||||
return &info->derived.hdr_static_metadata;
|
||||
}
|
||||
|
||||
const struct di_color_primaries *
|
||||
di_info_get_default_color_primaries(const struct di_info *info)
|
||||
{
|
||||
return &info->derived.color_primaries;
|
||||
}
|
||||
|
||||
const struct di_supported_signal_colorimetry *
|
||||
di_info_get_supported_signal_colorimetry(const struct di_info *info)
|
||||
{
|
||||
return &info->derived.supported_signal_colorimetry;
|
||||
}
|
||||
|
||||
static const struct di_displayid *
|
||||
edid_get_displayid(const struct di_edid *edid)
|
||||
{
|
||||
const struct di_edid_ext *const *ext;
|
||||
|
||||
for (ext = di_edid_get_extensions(edid); *ext; ext++) {
|
||||
enum di_edid_ext_tag tag = di_edid_ext_get_tag(*ext);
|
||||
|
||||
if (tag == DI_EDID_EXT_DISPLAYID)
|
||||
return di_edid_ext_get_displayid(*ext);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct di_displayid_display_params *
|
||||
displayid_get_display_params(const struct di_displayid *did)
|
||||
{
|
||||
const struct di_displayid_data_block *const *block =
|
||||
di_displayid_get_data_blocks(did);
|
||||
|
||||
for (; *block; block++) {
|
||||
enum di_displayid_data_block_tag tag = di_displayid_data_block_get_tag(*block);
|
||||
|
||||
if (tag == DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS)
|
||||
return di_displayid_data_block_get_display_params(*block);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float
|
||||
di_info_get_default_gamma(const struct di_info *info)
|
||||
{
|
||||
const struct di_edid *edid;
|
||||
const struct di_displayid *did;
|
||||
const struct di_edid_misc_features *misc;
|
||||
|
||||
edid = di_info_get_edid(info);
|
||||
if (!edid)
|
||||
return 0.0f;
|
||||
|
||||
did = edid_get_displayid(edid);
|
||||
if (did) {
|
||||
const struct di_displayid_display_params *did_params;
|
||||
|
||||
did_params = displayid_get_display_params(did);
|
||||
if (did_params)
|
||||
return did_params->gamma;
|
||||
}
|
||||
|
||||
/* Trust the flag more than the gamma field value. */
|
||||
misc = di_edid_get_misc_features(edid);
|
||||
if (misc->srgb_is_primary)
|
||||
return 2.2f;
|
||||
|
||||
return di_edid_get_basic_gamma(edid);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
global:
|
||||
di_*;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes calling the functions that wrap _di_logger_va_add_failure() (e.g.
|
||||
* add_failure() in edid.c) is not possible, because we want to use a specific
|
||||
* logger (and not e.g. edid->logger). This allow us to log with a custom
|
||||
* logger. Avoid using this if not necessary.
|
||||
*/
|
||||
void
|
||||
_di_logger_add_failure(struct di_logger *logger, const char fmt[], ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
_di_logger_va_add_failure(logger, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
@@ -1,29 +1,109 @@
|
||||
project('libdisplay-info', 'c',
|
||||
version: '0.2.3',
|
||||
meson_version: '>= 0.54.0',
|
||||
default_options: ['warning_level=1', 'buildtype=debugoptimized'])
|
||||
|
||||
inc = include_directories('include')
|
||||
|
||||
libdisplay_info = shared_library('display-info',
|
||||
'di.c',
|
||||
include_directories: inc,
|
||||
version: meson.project_version(),
|
||||
install: true)
|
||||
|
||||
install_headers(
|
||||
'include/libdisplay-info/cta.h',
|
||||
'include/libdisplay-info/displayid.h',
|
||||
'include/libdisplay-info/edid.h',
|
||||
'include/libdisplay-info/info.h',
|
||||
subdir: 'libdisplay-info',
|
||||
project(
|
||||
'libdisplay-info',
|
||||
'c',
|
||||
version: '0.4.0',
|
||||
license: 'MIT',
|
||||
meson_version: '>= 0.57.0',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
'warning_level=3',
|
||||
'wrap_mode=nodownload',
|
||||
],
|
||||
)
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(
|
||||
libdisplay_info,
|
||||
name: 'libdisplay-info',
|
||||
description: 'Display identification data parsing library',
|
||||
filebase: 'libdisplay-info',
|
||||
version: meson.project_version(),
|
||||
version = meson.project_version().split('-')[0]
|
||||
version_major = version.split('.')[0]
|
||||
version_minor = version.split('.')[1]
|
||||
assert(version_major == '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',
|
||||
'cvt.c',
|
||||
'displayid.c',
|
||||
'displayid2.c',
|
||||
'dmt.c',
|
||||
'dmt-table.c',
|
||||
'edid.c',
|
||||
'gtf.c',
|
||||
'info.c',
|
||||
'log.c',
|
||||
'memory-stream.c',
|
||||
'cta-vic.c',
|
||||
'cta-vic-table.c',
|
||||
'hdmi-vic.c',
|
||||
pnp_id_table,
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [math],
|
||||
link_args: symbols_flag,
|
||||
link_depends: symbols_file,
|
||||
install: true,
|
||||
version: version,
|
||||
soversion: version_minor,
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
# Red Bear: the library is what the distribution consumes. di-edid-decode and
|
||||
# test/ build extra host-side executables and a shell test harness that are not
|
||||
# part of the runtime and do not cross-compile cleanly for Redox.
|
||||
# subdir('di-edid-decode')
|
||||
# subdir('test')
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh -eu
|
||||
|
||||
if ! lsmod | grep i2c_dev >/dev/null 2>&1; then
|
||||
echo "kernel module i2c-dev must be loaded"
|
||||
exit
|
||||
fi
|
||||
|
||||
if ! command -v i2cdump >/dev/null 2>&1; then
|
||||
echo "i2cdump from i2c-tools must be installed"
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
for connector in $(find /sys/class/drm/ -maxdepth 1 -name 'card[0-9]-*'); do
|
||||
bus=$(find "$connector/" -maxdepth 1 -mindepth 1 -type d -name 'i2c-*' -print -quit | sed -n 's/.*\/i2c\-\(.*\)/\1/p')
|
||||
if [ "$bus" = "" ]; then
|
||||
bus=$(find "$connector/ddc/i2c-dev/" -maxdepth 1 -mindepth 1 -type d -name 'i2c-*' -print -quit | sed -n 's/.*\/i2c\-\(.*\)/\1/p')
|
||||
fi
|
||||
if [ "$bus" = "" ]; then
|
||||
echo "Connector $(basename $connector) does not have an i2c bus. Skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Dumping connector $(basename $connector) i2c bus address 0x50"
|
||||
i2cdump -y $bus 0x50 b
|
||||
|
||||
echo "Dumping connector $(basename $connector) i2c bus address 0x51"
|
||||
i2cdump -y $bus 0x51 b
|
||||
|
||||
echo "Dumping connector $(basename $connector) i2c bus address 0x54"
|
||||
i2cdump -y $bus 0x54 b
|
||||
|
||||
echo "Dumping connector $(basename $connector) i2c bus address 0x55"
|
||||
i2cdump -y $bus 0x55 b
|
||||
done
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pdfplumber
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("usage: gen-cta-vic.py <CTA-861-I PDF>", 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-I
|
||||
pages = {
|
||||
"timing": (42, 45),
|
||||
"aspect_ratio": (54, 57),
|
||||
}
|
||||
|
||||
def extract_rows(pdf, page_range):
|
||||
(first, last) = page_range
|
||||
rows = []
|
||||
for page in pdf.pages:
|
||||
if page.page_number < first or page.page_number > last:
|
||||
continue
|
||||
table = page.extract_table()
|
||||
rows += table[1:] # strip header
|
||||
return rows
|
||||
|
||||
def parse_vic_list(s):
|
||||
return [int(vic.strip()) for vic in s.split(",")]
|
||||
|
||||
def parse_interlaced(s):
|
||||
if s == "Prog":
|
||||
return "false"
|
||||
elif s == "Int":
|
||||
return "true"
|
||||
else:
|
||||
assert(False)
|
||||
|
||||
def parse_polarity(pol):
|
||||
if pol == "P":
|
||||
return "POSITIVE"
|
||||
elif pol == "N":
|
||||
return "NEGATIVE"
|
||||
else:
|
||||
assert(False)
|
||||
|
||||
pages_list = []
|
||||
for type, (first, last) in pages.items():
|
||||
pages_list += list(range(first, last + 1))
|
||||
|
||||
with pdfplumber.open(in_path, pages=pages_list) as pdf:
|
||||
timing_rows = extract_rows(pdf, pages["timing"])
|
||||
aspect_ratio_rows = extract_rows(pdf, pages["aspect_ratio"])
|
||||
|
||||
format_table = {}
|
||||
for row in timing_rows:
|
||||
if not row[1]:
|
||||
continue
|
||||
for vic in parse_vic_list(row[1]):
|
||||
# Note, some values have a footnote marker
|
||||
format_table[vic] = {
|
||||
"h_active": int(row[3].rstrip("b")),
|
||||
"v_active": int(row[4]),
|
||||
"interlaced": parse_interlaced(row[5]),
|
||||
"pixel_clock_hz": int(float(row[20]) * 1000 * 1000),
|
||||
"h_front": int(row[8]),
|
||||
"h_sync": int(row[9]),
|
||||
"h_back": int(row[10]),
|
||||
"h_sync_polarity": "DI_CTA_VIC_VIDEO_FORMAT_SYNC_" + parse_polarity(row[11]),
|
||||
"v_front": int(row[14]),
|
||||
"v_sync": int(row[15]),
|
||||
"v_back": int(row[16]),
|
||||
"v_sync_polarity": "DI_CTA_VIC_VIDEO_FORMAT_SYNC_" + parse_polarity(row[17]),
|
||||
}
|
||||
|
||||
for row in aspect_ratio_rows:
|
||||
if len(row) == 15:
|
||||
# last two pages are extracted differently by pdfplumber due to
|
||||
# grayed-out rows ("Forbidden" and "Reserved for the Future")
|
||||
row = [row[0], row[3], row[6], row[9], row[12]]
|
||||
if not row[3]:
|
||||
continue
|
||||
vic = int(row[0])
|
||||
fmt = format_table[vic]
|
||||
# Note, some values have a footnote marker
|
||||
pic_ar = row[3].rstrip("f").replace(":", "_")
|
||||
fmt["picture_aspect_ratio"] = "DI_CTA_VIC_VIDEO_FORMAT_PICTURE_ASPECT_RATIO_" + pic_ar
|
||||
|
||||
max_vic = 0
|
||||
for vic in format_table:
|
||||
if vic > max_vic:
|
||||
max_vic = vic
|
||||
|
||||
# Sanity check
|
||||
for vic, fmt in format_table.items():
|
||||
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 <libdisplay-info/cta-vic.h>\n\n')
|
||||
f.write("const struct di_cta_vic_video_format _di_cta_vic_video_formats[] = {\n")
|
||||
for vic in format_table:
|
||||
f.write("\t[{}] = {{\n".format(vic))
|
||||
f.write("\t\t.vic = {{ .code = {} }},\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_vic_video_formats_len = {};\n".format(max_vic + 1))
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
#!/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_code": "{{ .code = {} }}".format(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 <DMT PDF>", 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 <libdisplay-info/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)))
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/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] + ' <infile> <outfile> <ident>', 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 <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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;
|
||||
}}
|
||||
}}
|
||||
''')
|
||||
Reference in New Issue
Block a user