diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 58dba18..b60c133 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,131 +1,131 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -CommunityCodeOfConduct AT intel DOT com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +CommunityCodeOfConduct AT intel DOT com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f682f4e..fe70f1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,57 +1,57 @@ -# Contributing - -### License - - is licensed under the terms in [LICENSE]. By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. - -### Sign your work - -Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify -the below (from [developercertificate.org](http://developercertificate.org/)): - -``` -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -Then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -Use your real name (sorry, no pseudonyms or anonymous contributions.) - -If you set your `user.name` and `user.email` git configs, you can sign your -commit automatically with `git commit -s`. +# Contributing + +### License + + is licensed under the terms in [LICENSE]. By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. + +### Sign your work + +Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/LICENSE b/LICENSE index 166631f..288e41d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: MIT -MIT License - -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. +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: MIT +MIT License + +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. diff --git a/Makefile b/Makefile index c3c04e8..53e1b0c 100644 --- a/Makefile +++ b/Makefile @@ -1,37 +1,37 @@ -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: MIT - -#DIRS = $(shell find . -maxdepth 1 -type d -not -path "./.git" \ -# -not -path "." -not -path "./release" -not -path "./cmn" | sort) -DIRS = lib test synctest vbltest -.PHONY: $(DIRS) - -MAKE += --no-print-directory - -all: - @for dir in $(DIRS); do \ - $(MAKE) -C $$dir; \ - done - -debug: - @for dir in $(DIRS); do \ - $(MAKE) -C $$dir debug; \ - done - -doxygen: - @mkdir -p output/doxygen - @( cat resources/swgen_doxy ; echo "PROJECT_NUMBER=$(VERSION)" ) | doxygen - -clean: - @for dir in $(DIRS); do \ - $(MAKE) -C $$dir clean; \ - done - -distclean: - @rm -rf output - -release: - @rm -rf output/release - @$(MAKE) clean - @$(MAKE) - @mkdir -p output/release - @cp lib/*.so resources/gPTP.cfg test/vsync_test synctest/synctest vbltest/vbltest output/release +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: MIT + +#DIRS = $(shell find . -maxdepth 1 -type d -not -path "./.git" \ +# -not -path "." -not -path "./release" -not -path "./cmn" | sort) +DIRS = lib test synctest vbltest +.PHONY: $(DIRS) + +MAKE += --no-print-directory + +all: + @for dir in $(DIRS); do \ + $(MAKE) -C $$dir; \ + done + +debug: + @for dir in $(DIRS); do \ + $(MAKE) -C $$dir debug; \ + done + +doxygen: + @mkdir -p output/doxygen + @( cat resources/swgen_doxy ; echo "PROJECT_NUMBER=$(VERSION)" ) | doxygen - +clean: + @for dir in $(DIRS); do \ + $(MAKE) -C $$dir clean; \ + done + +distclean: + @rm -rf output + +release: + @rm -rf output/release + @$(MAKE) clean + @$(MAKE) + @mkdir -p output/release + @cp lib/*.so resources/gPTP.cfg test/vsync_test synctest/synctest vbltest/vbltest output/release diff --git a/README.md b/README.md index f067a54..b2a4beb 100644 --- a/README.md +++ b/README.md @@ -1,564 +1,623 @@ -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/intel-retail/automated-vending/badge)](https://api.securityscorecards.dev/projects/github.com/intel-retail/automated-vending) - - Copyright (C) 2023 Intel Corporation - SPDX-License-Identifier: MIT - -# Introduction SW Genlock - -SW Genlock is a software solution designed to synchronize the display outputs of multiple computer systems to single digit microsecond precision, enabling seamless visual outputs across an array of screens. This technology is particularly beneficial in settings that require highly synchronized video outputs, such as video walls, virtual reality setups, digital signage, and other multi-display configurations. - -## Key Features of SW Genlock - -- **High Precision Synchronization**: Ensures that vertical sync (vblank) intervals occur simultaneously across all connected systems, maintaining the integrity of visual displays. -- **Flexible Integration**: Offers both dynamic (.so) and static (.a) linking options to accommodate various application needs. -- **Comprehensive Tools**: Includes a suite of tools and libraries that facilitate the synchronization process, such as obtaining vblank timestamps and adjusting the phase-locked loop (PLL) frequencies. -- **Extensive Compatibility**: Supports systems equipped with Precision Time Protocol (PTP), allowing for precise clock synchronization over network infrastructures. -- **User-Centric Utilities**: Provides reference applications that demonstrate effective usage of the library, assisting users in integrating Genlock capabilities into their own projects. - -The SW Genlock system operates by aligning the internal clocks of all systems involved, ensuring that every display refresh is triggered simultaneously. This alignment is achieved through the utilization of industry-standard protocols and our advanced proprietary algorithms, which manage the synchronization over ethernet or direct network connections. - -## Known Issues - -Users of this SW Genlock libraries may need to adjust the value for SHIFT. In testing, on various displays a valaue -that is too large can cause flickering on screen when synchronized. To resolve this, adjust the value of SHIFT. The adjustment can be made by: - -1) Editing the default value in [lib/common.h](./lib/common.h) -2) Providing a new value for SHIFT when running the sample application: - -```bash -synctest -s -``` - -# Installation - -Installing SW Genlock involves a few straightforward steps. This section guides through the process of setting up SW Genlock on target systems, ensuring that user have everything needed to start synchronizing displays. - -## System Requirements - -Before the user begin the installation, ensure the systems meet the following criteria: - -The build system assumes the following, prior to executing the build steps: - -1) The system has an [Ubuntu OS](https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview) -1) Libraries installed: `sudo apt install -y git build-essential make` -1) [Disable secure boot](https://wiki.ubuntu.com/UEFI/SecureBoot/DKMS) -1) MUST apply the [PLL kernel patch](./resources/0001-dpll-Provide-a-command-line-param-to-config-if-PLLs-.patch). - Compile the kernel and update either the entire kernel or just the i915.ko module, depending on the Linux configuration. Note: In some setups, the i915.ko module is integrated into the - initrd.img, and updating it in /lib/module/... might not reflect the changes. - - Following the application of the patch, edit the grub settings: - ```console - sudo vi /etc/grub/default - # add i915.share_dplls=0 to the GRUB_CMDLINE_LINUX options: - # GRUB_CMDLINE_LINUX="i915.share_dplls=0" - # Save and exit, then update grub - update-grub - ``` - -1) Apply **[the monotonic timstamp patch](./resources/0001-Revert-drm-vblank-remove-drm_timestamp_monotonic-par.patch)** - to Linux kernel i915 driver which allows it to provide vsync timestamps in real time - instead of the default monotonic time.
- Note that the monotonic timestamp patch is generated based on Linux v6.4 and has been tested on Linux v6.4 and v6.5.
- Please follow the steps to disable monotonic timestamp after installing the local built Linux image on a target. Compile the kernel and update either the entire kernel or just the drm.ko module based on the installed Linux configuration.
- 1. Add ```drm.timestamp_monotonic=0``` option to **GRUB_CMDLINE_LINUX** in /etc/default/grub - 2. Update **GRUB_DEFAULT** in /etc/default/grub to address the local built Linux image - 3. Run ```update-grub``` to apply the change - 4. Reboot a target -1) Turn off NTP time synchronization service by using this command: - ```timedatectl set-ntp no``` -1) All the involved systems must support PTP time synchronization via ethernet (e.g ptp4l, phc2sys) - To verify if an ethernet interface supports PTP, the `ethtool` linux tool can be used. Run the following command, replacing `eth0` with the relevant interface name: - - ```console - ethtool -T eth0 - ``` - - If the interface supports PTP, the output will display specific PTP hardware clock information. - To ensure proper synchronization, the Ethernet interfaces on the involved systems should be directly connected either via a crossover cable or through a PTP-supported router or switch. If the systems only have a single network interface, it may be necessary to add a separate network adapter, such as a USB-to-Ethernet or USB-to-WiFi dongle, to maintain SSH access and connectivity to external networks. -1) Displays must be at same refresh rate (e.g 60Hz) - -## Build steps - -1) Git clone this repository -1) `sudo apt install libdrm-dev libpciaccess-dev` - 1) If the directory /usr/include/drm does not exist, it may be necessary to run the command`sudo apt install -y linux-libc-dev` -2) Type `make` from the main directory. It compiles everything and creates library and executable binaries in respective folders along with code which can then be copied to the target systems. - -# Software Components - -## Vsync Library - -The Vsync library is distributed in both dynamic (.so) and static (.a) binary formats, allowing users to choose between dynamic and static linking depending on their application requirements. This library includes essential functions such as retrieving vblank timestamps and adjusting the vblank timestamp drift by specified microseconds. Additionally, all operations related to Phase-Locked Loop (PLL) programming are encapsulated within the library, providing a comprehensive suite of tools for managing display synchronization. - -## Reference Applications - -Accompanying the library, three reference applications are provided to demonstrate how to utilize the library effectively. These include: - -- A `vsync test` app that runs in either primary mode or secondary mode. primary mode is run on a single PC whereas all other PCs are run as secondary mode with parameters pointing to primary mode system ethernet address. The communication is done either in ethernet mode or IP address mode. - -```console -Usage: ./vsync_test [-m mode] [-i interface] [-c mac_address] [-d delta] [-p pipe] [-s shift] [-v loglevel] [-h] -Options: - -m mode Mode of operation: pri, sec (default: pri) - -i interface Network interface to listen on (primary) or connect to (secondary) (default: 127.0.0.1) - -c mac_address MAC address of the network interface to connect to. Applicable to ethernet interface mode only. - -d delta Drift time in microseconds to allow before pll reprogramming (default: 100 us) - -p pipe Pipe to get stamps for. 4 (all) or 0,1,2 ... (default: 0) - -s shift PLL frequency change fraction (default: 0.1) - -v loglevel Log level: error, warning, info, debug or trace (default: info) - -h Display this help message -``` - -* A `vbltest` app to print average vblank period between sync. This is useful in verification and validation. - -```console -[INFO] Vbltest Version: 2.0.0 - Usage: ./vbltest [-p pipe] [-c vsync_count] [-v loglevel] [-h] - Options: - -p pipe Pipe to get stamps for. 0,1,2 ... (default: 0) - -c vsync_count Number of vsyncs to get timestamp for (default: 300) - -v loglevel Log level: error, warning, info, debug or trace (default: info) - -h Display this help message -``` - -* A `synctest` app to drift vblank clock on a single display by certain period such as 1000 microseconds (or 1.0 ms). - -```console -[INFO] Synctest Version: 2.0.0 -Usage: ./synctest [-p pipe] [-d delta] [-s shift] [-v loglevel] [-h] -Options: --p pipe Pipe to get stamps for. 0,1,2 ... (default: 0) --d delta Drift time in us to achieve (default: 1000 us) e.g 1000 us = 1.0 ms --s shift PLL frequency change fraction (default: 0.1) --v loglevel Log level: error, warning, info, debug or trace (default: info) --h Display this help message -``` - -**synctest sample output:** - -```console -./synctest -[INFO] Synctest Version: 2.0.0 -[INFO] DRM Info: -[INFO] CRTCs found: 4 -[INFO] Pipe: 0, CRTC ID: 80, Mode Valid: Yes, Mode Name: , Position: ( 0, 0), Resolution: 1920x1080, Refresh Rate: 60.00 Hz -[INFO] Pipe: 1, CRTC ID: 131, Mode Valid: No, Mode Name: , Position: ( 0, 0), Resolution: 0x0 , Refresh Rate: 0.00 Hz -[INFO] Pipe: 2, CRTC ID: 182, Mode Valid: No, Mode Name: , Position: ( 0, 0), Resolution: 0x0 , Refresh Rate: 0.00 Hz -[INFO] Pipe: 3, CRTC ID: 233, Mode Valid: No, Mode Name: , Position: ( 0, 0), Resolution: 0x0 , Refresh Rate: 0.00 Hz -[INFO] Connectors found: 6 -[INFO] Connector: 0 (ID: 236 ), Type: 11 (HDMI-A ), Type ID: 1 , Connection: Disconnected -[INFO] Connector: 1 (ID: 246 ), Type: 11 (HDMI-A ), Type ID: 2 , Connection: Connected -[INFO] Encoder ID: 245, CRTC ID: 80 -[INFO] Connector: 2 (ID: 250 ), Type: 10 (DisplayPort ), Type ID: 1 , Connection: Disconnected -[INFO] Connector: 3 (ID: 259 ), Type: 11 (HDMI-A ), Type ID: 3 , Connection: Disconnected -[INFO] Connector: 4 (ID: 263 ), Type: 10 (DisplayPort ), Type ID: 2 , Connection: Disconnected -[INFO] Connector: 5 (ID: 272 ), Type: 10 (DisplayPort ), Type ID: 3 , Connection: Disconnected -[INFO] VBlank interval before starting synchronization: 16.667000 ms -[INFO] VBlank interval during synchronization -> -[INFO] VBlank interval on pipe 0 is 16.6650 ms -[INFO] VBlank interval on pipe 0 is 16.6650 ms -[INFO] VBlank interval on pipe 0 is 16.6650 ms -[INFO] VBlank interval on pipe 0 is 16.6650 ms -[INFO] VBlank interval on pipe 0 is 16.6640 ms -[INFO] VBlank interval on pipe 0 is 16.6650 ms -[INFO] VBlank interval on pipe 0 is 16.6650 ms -[INFO] VBlank interval on pipe 0 is 16.6660 ms -[INFO] VBlank interval after synchronization ends: 16.667000 ms -``` - -# Generating Doxygen documents - -Please install doxygen and graphviz packages before generating Doxygen documents: - ```sudo apt install doxygen graphviz``` - -1. Type ```make doxygen VERSION="1.2.3"``` from the main directory. It will generate SW Genlock doxygen documents - to output/doxygen folder. Change the version to be that of the release number for the project. -2. Open output/doxygen/html/index.html with a web-browser - -# Running on single system - -The reference applications vbltest and synctest are designed to operate on a single system. The vbltest app displays the average vblank period for a specific pipe, while synctest allows user to introduce a specific drift in the vblank timing for a pipe. Synctest serves as a practical tool to verify the effectiveness of the synchronization methodology. - -1) On the system, go to the directory where these files exist and set environment variable: -2) For dynamic linking with the .so library, it's necessary to set the LD_LIBRARY_PATH environment variable so that applications can locate the vsync dynamic library. This step is not required when using static linking. - -```console -export LD_LIBRARY_PATH=`pwd`/lib -`````` - -3) On the primary system, run accompanied reference apps as follows: -4) e.g synctest - ```console - ./synctest - ``` - -Building of this program has been successfully tested on both Ubuntu 2x and Fedora 30.
- -# Synchronization between two systems - -Synchronizing displays across two systems involves a two-step process. Firstly, the Real Time Clocks of both systems need to be kept in synchronized state using the ptp4l Linux tool. Subsequently, the vsync test app should be run in primary mode on one system and in secondary mode on the other, ensuring that the vblank of the secondary system remains synchronized with that of the primary system. - -## Synchronizing Clocks between two systems - -The SW Genlock feature requires that all participating systems' clocks be synchronized to the nanosecond level. Kernel patches included with the software return vblank timestamps based on the system clock rather than the kernel's monotonic clock, which tracks time from when the kernel is loaded. Accurate synchronization is crucial because SW Genlock shares vblank timestamps from the primary system with secondary systems. This synchronization is achieved using the Linux ptp4l tool. There should be a network cable directly connecting the ethernet ports of the two -systems. - -### Clock Configuration in PTP-Supported Systems - -Systems supporting PTP typically have two clocks: the system's real-time clock and another clock on the Ethernet card. The synchronization process involves: - -1. **Primary System Real-Time Clock Synchronization**: - - Initially synchronize the primary system's real-time clock with an NTP server. - - Then sync the Ethernet PTP clock with the primary system's real-time clock. - -2. **Secondary Systems Synchronization**: - - Sync the secondary systems' Ethernet PTP clocks with the primary's Ethernet PTP clock. - - Sync their system real-time clocks with their Ethernet PTP clocks, which are already synchronized with the primary system. - -This setup ensures all secondary system's real-time clocks are synchronized with the primary system's real-time clock. Synchronization within a system (system real-time clock to Ethernet PTP clock) is managed using the `phc2sys` tool, while inter-system synchronization (across Ethernet interfaces) utilizes the `ptp4l` tool. - -
PTP4L Setup
PTP4L Setup.
- -It's also important to turn off NTP time synchronization service by using this command: - ```$ timedatectl set-ntp no``` - -### Command Sets for PTP time Synchronization - -Assuming the primary Ethernet interface is named `enp87s0` and the secondary interface is named `enp89s0`, the following commands are to be executed under root or with sudo: - -**Primary System Commands:** - -- **ptp4l:** - -```shell -ptp4l -i enp87s0 -m -f ./resources/gPTP.cfg -``` - -- **phc2sys:** - -```shell -phc2sys -c enp87s0 -s CLOCK_REALTIME -w -O 0 -m -f ./resources/gPTP.cfg -``` - -**Secondary System Commands:** - -- **ptp4l:** - -```shell -ptp4l -i enp89s0 -s -m -f ./resources/gPTP.cfg -``` - -- **phc2sys:** - -```shell -phc2sys -s enp89s0 -c CLOCK_REALTIME -O 0 -m -f ./resources/gPTP.cfg -``` - -The output of ptp4l on the primary system after running the above command should look - like this:
- -```console -ptp4l[13416.983]: selected /dev/ptp1 as PTP clock -ptp4l[13417.002]: port 1: INITIALIZING to LISTENING on INIT_COMPLETE -ptp4l[13417.003]: port 0: INITIALIZING to LISTENING on INIT_COMPLETE -ptp4l[13420.445]: port 1: LISTENING to MASTER on ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES -ptp4l[13420.445]: selected local clock 844709.fffe.04f07f as best master -ptp4l[13420.445]: assuming the grand master role -``` - -The output of ptp4l on the secondary system after running the above command should -look like this:
- -```console -ptp4l[14816.313]: selected /dev/ptp1 as PTP clock -ptp4l[14816.328]: port 1: INITIALIZING to LISTENING on INIT_COMPLETE -ptp4l[14816.328]: port 0: INITIALIZING to LISTENING on INIT_COMPLETE -ptp4l[14820.220]: port 1: new foreign master 844709.fffe.04f07f-1 -ptp4l[14820.250]: selected local clock 844709.fffe.04eb0e as best master -ptp4l[14822.220]: selected best master clock 844709.fffe.04f07f -ptp4l[14822.220]: port 1: LISTENING to UNCALIBRATED on RS_SLAVE -ptp4l[14822.650]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED -ptp4l[14823.275]: rms 158899 max 317730 freq -11742 +/- 3075 delay 14 +/- 0 -ptp4l[14824.276]: rms 820 max 1257 freq -9067 +/- 1113 delay 14 +/- 0 -ptp4l[14825.277]: rms 1366 max 1436 freq -6582 +/- 359 delay 13 +/- 0 -ptp4l[14826.278]: rms 866 max 1148 freq -6099 +/- 34 delay 13 +/- 0 -ptp4l[14827.279]: rms 280 max 465 freq -6364 +/- 102 delay 12 +/- 0 -ptp4l[14828.280]: rms 52 max 80 freq -6666 +/- 65 delay 12 +/- 0 -ptp4l[14829.280]: rms 82 max 88 freq -6805 +/- 21 delay 12 +/- 0 -ptp4l[14830.281]: rms 50 max 69 freq -6829 +/- 3 delay 12 +/- 0 -ptp4l[14831.282]: rms 15 max 28 freq -6811 +/- 7 delay 12 +/- 0 -ptp4l[14832.283]: rms 4 max 8 freq -6795 +/- 5 delay 12 +/- 0 -ptp4l[14833.284]: rms 7 max 10 freq -6783 +/- 3 delay 12 +/- 0 -ptp4l[14834.285]: rms 2 max 5 freq -6788 +/- 3 delay 13 +/- 0 -ptp4l[14835.286]: rms 3 max 5 freq -6793 +/- 3 delay 13 +/- 0 -``` - -Please make it sure that the secondary sytem refers to the primary's precision time - by checking if there is the **"new foreign master"** message in a log
- Note that the rms value should be decreasing with each line and should go - down to single digits. - -For user convenience, an automation Bash script is available in the scripts folder to facilitate ptp4l synchronization on both primary and secondary systems. Users simply need to update the `config.sh` file with relevant details such as the secondary system's address, username, and Ethernet address, among others. After these updates, run the scripts/run_ptp.sh script from the primary system. This script will automatically execute the ptp4l and phc2sys tools on both the primary and secondary systems in their respective modes. Note that the script requires the secondary system to enable passwordless SSH to execute ptp4l commands through the SSH interface. - -Since the ptp4l and phc2sys tools require root privileges or sudo access, users need to provide their sudo password in the config.sh file. As an alternative, setting up passwordless sudo can streamline this process. Here's how to enable passwordless sudo: - -Open a terminal and type `sudo visudo` to edit the sudoers file. -Add the following line to the file at the end, replacing username with actual username: - -```bash -username ALL=(ALL) NOPASSWD: ALL -``` - -Save and close the file to apply the changes. -This adjustment allows the automation scripts to run without requiring a sudo password each time, simplifying the setup for ptp4l and phc2sys synchronizations. - -## Synchronizing Display VBlanks - -Once the system clocks are synchronized, user can proceed to run the vsync test tool on both the primary and secondary systems. -Installing and running the programs:
- -1. Copy the release folder contents to both primary and secondary systems.
-2. On both systems, go to the directory where these files exist and set environment variable: -export LD_LIBRARY_PATH=.
-3. On the primary system, run it as follows: - ```console - ./vsync_test -m pri -i [PTP_ETH_Interface] - ``` - -5. On the secondary system, run it as follows: - ```console - ./vsync_test -m sec -i PTP_ETH_Interface -c [Primary_ETH_MAC_Addr] -d [sync after # us of drift] - ``` - -This program runs in server mode on the primary system and in client mode on the -secondary system. - -
VBlank synchronization setup
VBlank synchronization setup.
- -If using PTP protocol to communicate between primary and secondary, there are some extra -parameters required. The primary system must identify the PTP Interface (ex: enp176s0). The -secondary system also requires its PTP interface as well as this port's ethernet MAC address. -An example of PTP communication between primary and secondary looks like this: -On the primary system, run it as follows: - ```console - ./vsync_test -m pri -i enp176s0 - ``` -On the secondary system, run it as follows: - ```console - ./vsync_test -m sec -i enp176s0 -c 84:47:09:04:eb:0e - ``` - -In the above examples, we were just sync'ing the secondary system once with the primary. -However, due to reference clock differences, we see that there is a drift pretty much as -soon as we have sync'd them. We also have another capability which allows us to resync -as soon as it goes above a threshold. For example: -On the primary system, run it as follows: - ```./vsync_test -m pri -i enp176s0```
-On the secondary system, run it as follows: - ```./vsync_test -m sec -i enp176s0 -c 84:47:09:04:eb:0e 100``` - -In the above example, secondary system would sync with the primary once but after that -it would constantly check to see if the drift is going above 100 us. As soon as it does, -the secondary system would automatically resync with the primary. It is recommended to -select a large number like 60 or 100 since if we use a smaller number, then we will be -resyncing all the time. On the other hand, if we chose a really big number like 1000 us, -then we are allowing the systems to get out of sync from each other for 1 ms. On a -Tiger Lake system, 100 us difference is usually happening in around 60-70 seconds or -about a minute. So this program would automatically resync with the primary every minute -or so. - -Similar to ptp4l, an automation script is provided to setup vsync primary and secondary on both systems. see ./scripts/run_vsync.sh. Make sure to update config.sh accordingly unless if it's already updated for run_ptp.sh. - -Similar to ptp4l, For user convenience, an automation Bash script is available in the scripts folder to facilitate vsync synchronization on both primary and secondary systems. Users simply need to update the `config.sh` file with relevant details such as the secondary system's address, username, and Ethernet address, among others. After these updates, run the scripts/run_vsync.sh script from the primary system. This script will automatically copy vblank_sync to secondary under ~/.vblanksync directory and execute the vblank_sync tool on both the primary and secondary systems in their respective modes. Note that the script requires the secondary system to enable passwordless SSH to execute ptp4l commands through the SSH interface. - -```console -./scripts/run_vsync.sh -``` - -The console will display logs from both systems, but each process, including ptp4l and phc2sys, will also generate its own separate log file. These files can be found in the ./logs directory at the root level. - -## Running vsync_test as a Regular User without sudo - -While the ptp synchronization section provides methods for configuring passwordless sudo, it may be preferable to operate without using sudo or root privileges. The `vsync_test` application, which requires access to protected system resources for memory-mapped I/O (MMIO) operations at `/sys/bus/pci/devices/0000:00:02.0/resource0`, can be configured to run under regular user permissions. This can be achieved by modifying system permissions either through direct file permission changes or by adjusting user group memberships. Note that the resource path might change in future versions of the kernel. The resource path can be verified by running the application with the `strace` tool in Linux as a regular user and checking for permission denied errors or by examining `/proc/processid/maps` for resource mappings. - -### Option 1: Modifying File Permissions - -Altering the file permissions to allow all users to read and write to the device resource removes the requirement for root access. This method should be used with caution as it can lower the security level of the system, especially in environments with multiple users or in production settings. - -To change the permissions, the following command can be executed: - -```bash -sudo chmod o+rw /sys/bus/pci/devices/0000:00:02.0/resource0 -``` - -This command adjusts the permissions to permit all users (o) read and write (rw) access to the specified resource. - -### Option 2: Configuring User Group Permissions and Creating a Persistent udev Rule - -A more secure method is to assign the resource to a specific user group, such as the `video` group, and add the user to that group. This method confines permissions to a controlled group of users. To ensure the changes persist after a reboot, a `udev` rule can be created. - -#### Step 1: Add the Resource to the Video Group - -Change the group ownership of the resource file to the `video` group using the following command: - -```bash -sudo chgrp video /sys/bus/pci/devices/0000:00:02.0/resource0 -``` - -#### Step 2: Modify Group Permissions - -Set the group permissions to allow read and write access with: - -```bash -sudo chmod g+rw /sys/bus/pci/devices/0000:00:02.0/resource0 -``` - -#### Step 3: Add the User to the Video Group - -Add the user to the video group using this command (replace username with the actual user name): - -```bash -sudo usermod -a -G video username -``` - -Log out and log back in, or start a new session, for the group changes to take effect. - -#### Step 4: Create a udev Rule for Persistent Permissions - -To make the permissions persist after reboot, create a `udev` rule by adding a new rule file in `/etc/udev/rules.d/`: - -```bash -echo 'ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:02.0", RUN+="/bin/chgrp video %S%p/resource0", RUN+="/bin/chmod 0660 %S%p/resource0"' | sudo tee /etc/udev/rules.d/99-vsync-access.rules -``` - -This rule ensures that the permissions are correctly set when the system boots. - -#### Step 5: Reload udev Rules - -After creating the rule, reload the udev rules to apply them immediately without rebooting: - -```bash -sudo udevadm control --reload-rules && sudo udevadm trigger -s pci --action=add -``` - -#### Verifying the Changes - -After applying one of the above methods, confirm that the user has the necessary permissions by running the vsync_test application without elevated privileges. If the setup is correct, the application should run without requiring sudo or root access. - -## Running ptp4l as a Regular User without sudo - -Similarly, the ptp4l command can be executed by a regular user. This can be done by either altering the permissions or modifying the group for `/dev/ptp0`, or by creating a udev rule to ensure persistent permissions. - -```bash -echo 'SUBSYSTEM=="ptp", KERNEL=="ptp0", GROUP="video", MODE="0660"' | sudo tee /etc/udev/rules.d/99-ptp-access.rules -``` - -Reload udev Rules - -```bash -sudo udevadm control --reload-rules && sudo udevadm trigger -``` - -However, `phc2sys` will still need to be run with root or sudo privileges as it involves modifying system time. Running it under a normal user account could be possible if permissions to update the system clock are granted to the application. - -# Debug Tools - -When performing debugging, it is helpful to have the following libraries installed: - -```bash -apt install -y intel-gpu-tools edid-decode -``` - -## Debug Tool Usage - -When encountering unexpected behavior that requires additional debugging, the installed tools can assist in narrowing down the potential problem. - -**[intel-gpu-tools](https://cgit.freedesktop.org/xorg/app/intel-gpu-tools/)** -Provides tools to read and write to registers, it must be run as the root user. Example: - -```console -intel_reg dump --all > /tmp/reg_dump.txt - -# output excerpt - GEN6_RP_CONTROL (0x0000a024): 0x00000400 -Gen6 disabled - GEN6_RPNSWREQ (0x0000a008): 0x150a8000 - GEN6_RP_DOWN_TIMEOUT (0x0000a010): 0x00000000 - GEN6_RP_INTERRUPT_LIMITS (0x0000a014): 0x00000000 - GEN6_RP_UP_THRESHOLD (0x0000a02c): 0x00000000 - GEN6_RP_UP_EI (0x0000a068): 0x00000000 - GEN6_RP_DOWN_EI (0x0000a06c): 0x00000000 - GEN6_RP_IDLE_HYSTERSIS (0x0000a070): 0x00000000 - GEN6_RC_STATE (0x0000a094): 0x00040000 - GEN6_RC_CONTROL (0x0000a090): 0x00040000 - GEN6_RC1_WAKE_RATE_LIMIT (0x0000a098): 0x00000000 - GEN6_RC6_WAKE_RATE_LIMIT (0x0000a09c): 0x00000000 - GEN6_RC_EVALUATION_INTERVAL (0x0000a0a8): 0x00000000 - GEN6_RC_IDLE_HYSTERSIS (0x0000a0ac): 0x00000019 - GEN6_RC_SLEEP (0x0000a0b0): 0x00000000 - GEN6_RC1e_THRESHOLD (0x0000a0b4): 0x00000000 - GEN6_RC6_THRESHOLD (0x0000a0b8): 0x00000000 -... - -intel_reg read 0x00062400 - PIPE_DDI_FUNC_CTL_C (0x00062400): 0x00000000 (disabled, no port, HDMI, 8 bpc, -VSync, -HSync, EDP A ON, x1) -``` - -**[edid-decode](https://manpages.debian.org/unstable/edid-decode/edid-decode.1.en.html)** -is a tool used to parse the Extended Display Identification Data (EDID) from monitors and display devices. EDID contains metadata about the display's capabilities, such as supported resolutions, manufacturer, serial number, and other attributes. - -To operate edid-decode, it is typically necessary to supply a binary EDID file or data. Here's an example of how to use it: - -```console -cat /sys/class/drm/card0-HDMI-A-1/edid > edid.bin - -# run edid-decode to parse and display the information -edid-decode edid.bin - -# sample output -Extracted contents: -header: 00 ff ff ff ff ff ff 00 -serial number: 10 ac 64 a4 4c 30 30 32 -version: 01 03 -basic params: 80 34 20 78 2e -chroma info: c5 c4 a3 57 4a 9c 25 12 50 54 -established: bf ef 80 -standard: 71 4f 81 00 81 40 81 80 95 00 a9 40 b3 00 01 01 -descriptor 1: 4d d0 00 a0 f8 70 3e 80 30 20 35 00 55 50 21 00 00 1a -descriptor 2: 02 3a 80 18 71 38 2d 40 58 2c 45 00 55 50 21 00 00 1e -descriptor 3: 00 00 00 ff 00 43 32 30 30 38 30 4e 50 30 0a 20 20 20 -descriptor 4: 00 00 00 fd 00 32 4b 1e 53 11 00 0a 20 20 20 20 20 20 -extensions: 01 -checksum: 1c - -Manufacturer: DEL Model a464 Serial Number 808909324 -Made week 12 of 2008 -EDID version: 1.3 -Digital display -Maximum image size: 52 cm x 32 cm -Gamma: 2.20 -DPMS levels: Standby Suspend Off -RGB color display -First detailed timing is preferred timing -Display x,y Chromaticity: - Red: 0.6396, 0.3300 - Green: 0.2998, 0.5996 - Blue: 0.1503, 0.0595 - White: 0.3134, 0.3291 -Established timings supported: - 720x400@70Hz - 640x480@60Hz - 640x480@67Hz - 640x480@72Hz - 640x480@75Hz - 800x600@56Hz - 800x600@60Hz - ... -Standard timings supported: - 1152x864@75Hz - 1280x800@60Hz - 1280x960@60Hz - ... -Detailed mode: Clock 148.500 MHz, 509 mm x 286 mm - 1920 2008 2052 2200 hborder 0 - 1080 1084 1089 -``` + Copyright (C) 2025 Intel Corporation + SPDX-License-Identifier: MIT + +# Introduction to SW Genlock +SW Genlock is a software-based solution designed to **synchronize the display refresh (vblank) signals** across multiple computer systems with high-precision microsecond-level accuracy. It ensures that the vertical blanking intervals (vblanks) of all connected displays occur in tight alignment, enabling seamless, tear-free visual output across multiple screens. Such synchronization is critical in environments like video walls, virtual reality setups, digital signage, and other multi-display configurations where visual continuity is essential. + +In use cases such as digital signage, there are typically **two levels of synchronization** required: + +* **Low-level synchronization** of the display hardware to align the vblanks across systems, and + +* **High-level synchronization** by the application to ensure that decoded frames are delivered in sync with those vblanks. + +SW Genlock addresses the **first, low-level synchronization** layer, making sure that the vblank signals are precisely aligned across all participating systems. This provides a reliable foundation for applications to build on and manage frame-level synchronization effectively. + +In addition to precise timing, SW Genlock features an `adaptive clock alignment mechanism` that allows secondary systems to gradually adjust their internal clocks to match the primary system. Through permanent, micro-scale adjustments controlled by a tunable learning rate, the system "learns" and adapts over time, reducing synchronization overhead and increasing long-term stability. This makes SW Genlock ideal for continuous, low-maintenance operation across varied hardware environments. + +## Key Features of SW Genlock + +- **High Precision Synchronization**: Ensures that vertical sync (vblank) intervals occur simultaneously across all connected systems, maintaining the integrity of visual displays. +- **Flexible Integration**: Offers both dynamic (.so) and static (.a) linking options to accommodate various application needs. +- **Comprehensive Tools**: Includes a suite of tools and libraries that facilitate the synchronization process, such as obtaining vblank timestamps and adjusting the phase-locked loop (PLL) frequencies. +- **Extensive Compatibility**: SW Genlock supports a wide range of system configurations, including mixed platform generations (e.g., Tiger Lake, Panther Lake) and various Linux distributions such as Ubuntu 22.04 and 24.04. It offers flexibility in real-time clock synchronization setups by supporting both Precision Time Protocol (PTP) for high-precision environments and Chrony for simpler or less time-sensitive deployments. This makes it suitable for diverse hardware and OS combinations across development and production environments. +- **Adaptive Clock Alignment:** Implements optional, micro-scale permanent clock adjustments using a tunable learning rate. This adaptive mechanism helps secondary systems gradually align with the primary clock, reducing sync frequency and improving long-term stability with minimal overhead. +- **User-Centric Utilities**: Provides reference applications that demonstrate effective usage of the library, assisting users in integrating Genlock capabilities into their own projects. + +The SW Genlock system operates by aligning the internal clocks of all systems involved, ensuring that every display refresh is triggered simultaneously. This alignment is achieved through the utilization of industry-standard protocols and our advanced algorithms, which manage the synchronization over ethernet or direct network connections. + +
+
+ Synchronization +
Figure: SW GenLock
+
+
+ +# Supported Platforms + +Platforms based on the following architectures should be supported: + +- Tiger Lake +- Alder Lake +- Raptor Lake +- Meteor Lake +- Arrow Lake +- Panther Lake + +## Important Notes +* This tool requires root privileges (e.g., via sudo) by default, as it accesses the PCI address space directly. Alternatively, users may configure appropriate permissions or use cgroup-based access control to avoid running as root. Please note that the accompanying reference applications do not handle this, and it is the user's responsibility to ensure proper permissions are set up. + +* The vsync_test reference application provided is intended for demonstration purposes only. It uses unencrypted socket communication to send and receive VBlank timestamps. Users are strongly encouraged to implement appropriate encryption and security measures for any production or real-world deployment. + +# Installation + +Installing SW Genlock involves a few straightforward steps. This section guides through the process of setting up SW Genlock on target systems, ensuring that user have everything needed to start synchronizing displays. + +## System Requirements + +Before the user begin the installation, ensure the systems meet the following criteria: + +The build system assumes the following, prior to executing the build steps: + +1) The system has [Ubuntu 22.04 or 24.04](https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview). Other Linux flavors should work too. +1) Libraries installed: `sudo apt install -y git build-essential make` +1) [Disable secure boot](https://wiki.ubuntu.com/UEFI/SecureBoot/DKMS) +1) MUST apply the [PLL kernel patch](./resources/shared_dpll-kernel-6.xx.patch) located in resources folder. + + Apply shared_dpll-kernel-x.x.patch according to kernel version. e.g shared_dpll-kernel-6.10+.patch applicable from 6.10 till 6.13. 6.14 has its own patch in resource directory. + + e.g patch -p1 < /resources/shared_dpll-kernel-6.10+.patch + + Compile the kernel and update either the entire kernel or just the i915.ko/xe.ko module, depending on the Linux configuration. Note: In some setups, the i915.ko/xe.ko module is integrated into the + initrd.img, and updating it in /lib/module/... might not reflect the changes. + - Following the application of the patch, edit the grub settings: + ```console + $ sudo vi /etc/grub/default + # add i915.share_dplls=0 to the GRUB_CMDLINE_LINUX options: + # GRUB_CMDLINE_LINUX="i915.share_dplls=0" + # Save and exit, then update grub + update-grub + ``` + +1) Apply **[the monotonic timstamp patch](./resources/0001-Revert-drm-vblank-remove-drm_timestamp_monotonic-par.patch)** + to Linux kernel drm module which allows it to provide vsync timestamps in real time + instead of the default monotonic time.
+ Note that the monotonic timestamp patch is generated based on Linux v6.4 and has been tested upto v6.15.
+ Please follow the steps to disable monotonic timestamp after installing the local built Linux image on a target. Compile the kernel and update either the entire kernel or just the drm.ko module based on the installed Linux configuration.
+ 1. Add ```drm.timestamp_monotonic=0``` option to **GRUB_CMDLINE_LINUX** in /etc/default/grub + 2. Update **GRUB_DEFAULT** in /etc/default/grub to address the local built Linux image + 3. Run ```update-grub``` to apply the change + 4. Reboot a target +1) Turn off NTP time synchronization service by using this command: + ```timedatectl set-ntp no``` +1) All the involved systems should support PTP time synchronization via ethernet (e.g ptp4l, phc2sys) + To verify if an ethernet interface supports PTP, the `ethtool` linux tool can be used. Run the following command, replacing `eth0` with the relevant interface name: + + ```console + $ ethtool -T eth0 + ``` + + If the interface supports PTP, the output will display specific PTP hardware clock information. + To ensure proper synchronization, the Ethernet interfaces on the involved systems should be directly connected either via a crossover cable or through a PTP-supported router or switch. If the systems only have a single network interface, it may be necessary to add a separate network adapter, such as a USB-to-Ethernet or USB-to-WiFi dongle, to maintain SSH access and connectivity to external networks. + + Alternatively, `Chrony` can be used for real-time clock synchronization. While it offers easier setup and broader compatibility, its precision is generally lower, and time drift may not be as stable compared to hardware-assisted PTP. + +1) Displays must be at same refresh rate (e.g 60Hz) + +## Build steps + +1) Git clone this repository +1) `sudo apt install libdrm-dev libpciaccess-dev` + 1) If the directory /usr/include/drm does not exist, it may be necessary to run the command`sudo apt install -y linux-libc-dev` +2) Type `make` from the main directory. It compiles everything and creates library and executable binaries in respective folders along with code which can then be copied to the target systems. + +# Software Components + +## Vsync Library + +The Vsync library is distributed in both dynamic (.so) and static (.a) binary formats, allowing users to choose between dynamic and static linking depending on their application requirements. This library includes essential functions such as retrieving vblank timestamps and adjusting the vblank timestamp drift by specified microseconds. Additionally, all operations related to Phase-Locked Loop (PLL) programming are encapsulated within the library, providing a comprehensive suite of tools for managing display synchronization. + +### 🔧 Static Library (.a) +When linking against the static library (libvsyncalter.a), the final binary must be linked using g++ rather than gcc. This is because the static library contains C++ object code but does not include the C++ standard library symbols (e.g., operator new, std::mutex, virtual tables). + +```bash +$ g++ -o app main.c libvsyncalter.a -lpciaccess -ldrm -lrt -lm +``` + +Linking with gcc in this case will typically result in undefined reference errors to C++ runtime symbols. + +### 🔧 Shared Library (.so) +When using the shared library version (libvsyncalter.so), the final binary can be linked using either gcc or g++. All necessary C++ symbols are already resolved and included in the shared object. + +The system dynamic linker will load the required C++ runtime (libstdc++) at runtime. + +```bash +$ gcc -o app main.c -L. -lvsyncalter -lpciaccess -ldrm -lrt -lm +``` + +This allows the shared library to be used safely from C code, since the C++ functions in vsync lib are exposed with C-compatible linkage (i.e., using extern "C" in headers). The reference applications are linked using g++, while the unit tests are compiled with gcc to ensure compatibility with the pure C interface. + +## Reference Applications + +Accompanying the library, three reference applications are provided to demonstrate how to utilize the library effectively. These include: + +- A `vsync test` app that runs in either primary mode or secondary mode. primary mode is run on a single PC whereas all other PCs are run as secondary mode with parameters pointing to primary mode system ethernet address. The communication is done either in ethernet mode or IP address mode. + +```console +Usage: ./vsync_test [-m mode] [-i interface] [-c mac_address] [-d delta] [-p pipe] [-s shift] [-v loglevel] [-h] +Options: + -m mode Mode of operation: pri, sec (default: pri) + -i interface Network interface to listen on (primary) or connect to (secondary) (default: 127.0.0.1) + -c mac_address MAC address of the network interface to connect to. Applicable to ethernet interface mode only. + -d delta Drift time in microseconds to allow before pll reprogramming (default: 100 us) + -p pipe Pipe to get stamps for. 4 (all) or 0,1,2 ... (default: 0) + -s shift PLL frequency change fraction as percentage (default: 0.01 = 0.01%) + -x shift2 PLL frequency change fraction for large drift (default: 0.0; Disabled) + -f frequency PLL clock value to set at start (default -> Do Not Set : 0.0) + -e device Device string (default: /dev/dri/card0) + -v loglevel Log level: error, warning, info, debug or trace (default: info) + -k time_period Time period in seconds during which learning rate will be applied. (default: 240 sec) + -l learning_rate Learning rate for convergence. Secondary mode only. e.g 0.00001 (default: 0.0 Disabled) + -o overshoot_ratio Allow the clock to go beyond zero alignment by a ratio of the delta (value between 0 and 1). + For example, with -o 0.5 and delta=500, the target offset becomes -250 us in the apposite direction (default: 0.0) + -t step_threshold Delta threshold in microseconds to trigger stepping mode (default: 1000 us) + -w step_wait Wait in milliseconds between steps (default: 50 ms) + -n Use DP M & N Path. (default: no) + -h Display this help message +``` + +* A `vbltest` app to print average vblank period between sync. This is useful in verification and validation. + +```console +[INFO] Vbltest Version: 2.0.0 + Usage: ./vbltest [-p pipe] [-c vsync_count] [-v loglevel] [-h] + Options: + -p pipe Pipe to get stamps for. 0,1,2 ... (default: 0) + -c vsync_count Number of vsyncs to get timestamp for (default: 300) + -e device Device string (default: /dev/dri/card0) + -l loop Loop mode: 0 = no loop, 1 = loop (default: 0) + -v loglevel Log level: error, warning, info, debug or trace (default: info) + -h Display this help message +``` + +* A `synctest` app to drift vblank clock on a single display by certain period such as 1000 microseconds (or 1.0 ms). + +```console +[INFO] Synctest Version: 2.0.0 +Usage: ./synctest [-p pipe] [-d delta] [-s shift] [-v loglevel] [-h] +Options: + -p pipe Pipe to get stamps for. 0,1,2 ... (default: 0) + -d delta Drift time in us to achieve (default: 1000 us) e.g 1000 us = 1.0 ms + -s shift PLL frequency change fraction (default: 0.01) + -x shift2 PLL frequency change fraction for large drift (default: 0.0; Disabled) + -e device Device string (default: /dev/dri/card0) + -f frequency Clock value to directly set (default -> Do not set : 0.0) + -v loglevel Log level: error, warning, info, debug or trace (default: info) + -t step_threshold Delta threshold in microseconds to trigger stepping mode (default: 1000 us) + -w step_wait Wait in milliseconds between steps (default: 50 ms) + --no-reset Do no reset to original values. Keep modified PLL frequency and exit (default: reset) + --no-commit Do no commit changes. Just print (default: commit) + -m Use DP M & N Path. (default: no) + -h Display this help message +``` + +**synctest sample output:** + +```console +$ ./synctest +[INFO] Synctest Version: 2.0.0 +[INFO] DRM Info: +[INFO] CRTCs found: 4 +[INFO] Pipe: 0, CRTC ID: 80, Mode Valid: Yes, Mode Name: , Position: ( 0, 0), Resolution: 1920x1080, Refresh Rate: 60.00 Hz +[INFO] Pipe: 1, CRTC ID: 131, Mode Valid: No, Mode Name: , Position: ( 0, 0), Resolution: 0x0 , Refresh Rate: 0.00 Hz +[INFO] Pipe: 2, CRTC ID: 182, Mode Valid: No, Mode Name: , Position: ( 0, 0), Resolution: 0x0 , Refresh Rate: 0.00 Hz +[INFO] Pipe: 3, CRTC ID: 233, Mode Valid: No, Mode Name: , Position: ( 0, 0), Resolution: 0x0 , Refresh Rate: 0.00 Hz +[INFO] Connectors found: 6 +[INFO] Connector: 0 (ID: 236 ), Type: 11 (HDMI-A ), Type ID: 1 , Connection: Disconnected +[INFO] Connector: 1 (ID: 246 ), Type: 11 (HDMI-A ), Type ID: 2 , Connection: Connected +[INFO] Encoder ID: 245, CRTC ID: 80 +[INFO] Connector: 2 (ID: 250 ), Type: 10 (DisplayPort ), Type ID: 1 , Connection: Disconnected +[INFO] Connector: 3 (ID: 259 ), Type: 11 (HDMI-A ), Type ID: 3 , Connection: Disconnected +[INFO] Connector: 4 (ID: 263 ), Type: 10 (DisplayPort ), Type ID: 2 , Connection: Disconnected +[INFO] Connector: 5 (ID: 272 ), Type: 10 (DisplayPort ), Type ID: 3 , Connection: Disconnected +[INFO] VBlank interval before starting synchronization: 16.667000 ms +[INFO] VBlank interval during synchronization -> +[INFO] VBlank interval on pipe 0 is 16.6650 ms +[INFO] VBlank interval on pipe 0 is 16.6650 ms +[INFO] VBlank interval on pipe 0 is 16.6650 ms +[INFO] VBlank interval on pipe 0 is 16.6650 ms +[INFO] VBlank interval on pipe 0 is 16.6640 ms +[INFO] VBlank interval on pipe 0 is 16.6650 ms +[INFO] VBlank interval on pipe 0 is 16.6650 ms +[INFO] VBlank interval on pipe 0 is 16.6660 ms +[INFO] VBlank interval after synchronization ends: 16.667000 ms +``` + +# Generating Doxygen documents + +Please install doxygen and graphviz packages before generating Doxygen documents: + ```sudo apt install doxygen graphviz``` + +1. Type ```make doxygen VERSION="1.2.3"``` from the main directory. It will generate SW Genlock doxygen documents + to output/doxygen folder. Change the version to be that of the release number for the project. +2. Open output/doxygen/html/index.html with a web-browser + +# Running on single system + +The reference applications vbltest and synctest are designed to operate on a single system. The vbltest app displays the average vblank period for a specific pipe, while synctest allows user to introduce a specific drift in the vblank timing for a pipe. Synctest serves as a practical tool to verify the effectiveness of the synchronization methodology. + +1) On the system, go to the directory where these files exist and set environment variable: +2) For dynamic linking with the .so library, it's necessary to set the LD_LIBRARY_PATH environment variable so that applications can locate the vsync dynamic library. This step is not required when using static linking. + +```console +$ export LD_LIBRARY_PATH=`pwd`/lib +`````` + +3) On the primary system, run accompanied reference apps as follows: +4) e.g synctest + ```console + $ ./synctest + ``` + +The synctest application offers two specialized modes as command line parameters: + +`--no-reset`: Applies new PLL clock values to the registers without reverting them after the timer expires. Use this mode for persistent changes, particularly during debugging and troubleshooting. + +`--no-commit`: Calculates and displays the new PLL clock values without writing them to the registers. This allows for safe previewing of changes before applying them. + +Building of this program has been successfully tested on both Ubuntu 2x and Fedora 30.
+ +## Direct PLL Clock Setting with Stepped Adjustment + +User can also update pll clock directly via vsync lib function call. The newly added set_pll_clock API, allowing for precise and direct control over PLL clock frequencies. To handle large frequency changes gracefully, the implementation incorporates an stepped adjustment mechanism. When a significant difference exists between the current and desired PLL clock frequencies, the function automatically calculates the optimal number of steps and adjusts the frequency incrementally based on given shift factor. This ensures stability and reliable locking without blank screens, even during substantial frequency transitions. The synctest application has been updated to demonstrate the usage of this new set_pll_clock API, providing a practical reference implementation for developers. For more details refer to the set_pll_clock api documentation. Current PLL clock value can be inferred by running synctest in regular mode which will print the value in console. User then can run synctest with -f option and desired PLL clock as an absolute floating point value parameter. + +```shell + $./synctest -f 8100.532 +``` + +# Synchronization Between Two Systems + +Synchronizing displays across two systems involves a two-step process. Firstly, the Real Time Clocks of both systems need to be kept in synchronized state using the ptp4l Linux tool. Subsequently, the vsync test app should be run in primary mode on one system and in secondary mode on the other, ensuring that the vblank of the secondary system remains synchronized with that of the primary system. + +## Synchronizing Real-time Clocks Between Two Systems + +For SW Genlock to function effectively, all participating systems must have their system clocks synchronized with **microsecond-level accuracy**. This is critical because SW Genlock shares vblank timestamps from the primary system with secondary systems, and these timestamps are based on the system clock, not the kernel’s monotonic clock (which starts counting from kernel boot time). + +SW Genlock works with real time clocks synchronized using either PTP (Precision Time Protocol) or Chrony, depending on the system capabilities and hardware environment: + +* **PTP** offers high-precision synchronization (sub-microsecond to low microsecond) and is the recommended option for best results. It typically requires hardware timestamping and a direct Ethernet connection between systems. + +* **Chrony**, while generally offering lower precision (typically within tens of microseconds), has been successfully tested with SW Genlock and provides sufficiently accurate synchronization in many practical scenarios. + +The setup instructions for both synchronization methods are available in the docs folder of this repository: + +- [PTP Setup Guide](docs/ptp.md) +- [Chrony Setup Guide](docs/chrony.md) + +For best results, especially in display-critical environments, it’s recommended to use a direct network cable between the Ethernet ports of the systems involved. + + +## Synchronizing Display VBlanks + +Once the system clocks are synchronized, user can proceed to run the vsync test tool on both the primary and secondary systems. +Installing and running the programs:
+ +1. Copy the release folder contents to both primary and secondary systems.
+2. On both systems, go to the directory where these files exist and set environment variable: +export LD_LIBRARY_PATH=.
+3. On the primary system, run it as follows: + ```console + $ ./vsync_test -m pri -i [PTP_ETH_Interface or Local IP Address] + ``` + +5. On the secondary system, run it as follows: + ```console + $ ./vsync_test -m sec -i [PTP_ETH_Interface or Remote IP Address] -c [Primary_ETH_MAC_Addr] -d [sync after # us of drift] + ``` + +This program runs in server mode on the primary system and in client mode on the +secondary system. + +
+
VBlank synchronization setup +
Figure: VBlank synchronization setup.
+
+ +If using PTP protocol to communicate between primary and secondary, there are some extra +parameters required. The primary system must identify the PTP Interface (ex: enp176s0). The +secondary system also requires its PTP interface as well as this port's ethernet MAC address. +An example of PTP communication between primary and secondary looks like this: +On the primary system, run it as follows: + +```console + $ ./vsync_test -m pri -i enp176s0 +``` +On the secondary system, run it as follows: +```console + $ ./vsync_test -m sec -i enp176s0 -c 84:47:09:04:eb:0e +``` + +In the above examples, we were just sync'ing the secondary system once with the primary. +However, due to reference clock differences, we see that there is a drift pretty much as +soon as we have sync'd them. We also have another capability which allows us to resync +as soon as it goes above a threshold. For example: +On the primary system, run it as follows: + ```./vsync_test -m pri -i enp176s0```
+On the secondary system, run it as follows: + ```./vsync_test -m sec -i enp176s0 -c 84:47:09:04:eb:0e 100``` + +The secondary system would sync with the primary once but after that +it would constantly check to see if the drift is going above 100 us. As soon as it does, +the secondary system would automatically resync with the primary. It is recommended to +select a large number like 60 or 100 since if we use a smaller number, then we will be +resyncing all the time. On the other hand, if we chose a really big number like 1000 us, +then we are allowing the systems to get out of sync from each other for 1 ms. On a +Tiger Lake system, 100 us difference is usually happening in around 60-70 seconds or +about a minute. So this program would automatically resync with the primary every minute +or so. + +The app features a learning mode as a secondary mode option, where it gradually adjusts the PLL clock to align closely with the primary clock. This adjustment occurs at a specified learning rate, and the app continuously modifies the secondary clock over several iterations and a set duration until it closely mirrors the primary clock, significantly delaying any drift. You can set the learning rate through a command-line parameter, with the default setting being 0, which disables this feature. The time_period parameter defines the duration within which the app responds to any drift by adjusting the clock. If the drift happens after this period has expired, the app does not make permanent adjustments to the PLL clock, assuming it has already closely synchronized with the primary display. + +Similar to ptp4l, For user convenience, an automation Bash script is available in the scripts folder to facilitate vsync synchronization on both primary and secondary systems. Users simply need to update the `config.sh` file with relevant details such as the secondary system's address, username, and Ethernet address, among others. After these updates, run the scripts/run_vsync.sh script from the primary system. This script will automatically copy vblank_sync to secondary under ~/.vblanksync directory and execute the vblank_sync tool on both the primary and secondary systems in their respective modes. Note that the script requires the secondary system to enable passwordless SSH to execute vsyncs commands through the SSH interface. + +```console +$ ./scripts/run_vsync.sh +``` + +# SW GenLock Key Features + +## Drift Correction Logic for larger drift +When the time delta between displays exceeds a certain threshold (see `-t` command line parameter), the system supports a two-phase adjustment mechanism to quickly reduce the drift. + +The shift value is the desired % difference from current frequency.  0.01 means 0.01% difference (either increase or decrease depending on which side the drift is). If a shift2 value is configured (typically larger than shift), it will be used to accelerate the convergence. However, applying a large shift directly may cause display blanking. To avoid this, the implementation internally breaks shift2 into smaller increments (based on shift) and applies them step-by-step to reach the desired frequency smoothly. There is a minor delay added to settle down frequency before next step (see `-w` command line parameter). + +After a configured timer expires, the same logic is applied in reverse to gradually return the system to its original frequency. + +If shift2 is not set (i.e., shift2 = 0), then only shift is used, and no internal stepping is performed. This results in slower convergence but avoids any risk of display instability. + +Using shift2 is particularly beneficial during the initial run, when VBlank timestamps between displays may be significantly misaligned. It helps reduce the initial drift quickly, and subsequent synchronization steps use the regular shift value to fine-tune the timing. + +When a very small shift is used together with a defined step size and a slightly longer wait time between steps, a minor overshoot is expected. In typical use cases with continuous drift monitoring, this overshoot is automatically corrected over time. However, when testing with synctest, which runs only a single adjustment cycle, this correction does not take place. Users are advised to experiment and tune the shift, step threshold and wait values according to their specific hardware setup. + +## Adaptive Clock Adjustment via Learning Rate (Secondary Mode) + +One of the key features of the Software Genlock system is the ability of a secondary system to gradually align its clock with the primary system through **incremental, permanent adjustments**. This mechanism helps reduce the frequency of synchronization events over time. + +When the **learning rate** parameter is provided via command line (e.g., `0.00001`), the secondary system does not only apply temporary drift corrections — it also updates its base clock gradually in the direction of the drift. This behavior helps the local clock "learn" and adapt to the long-term behavior of the primary system. + +### How It Works + +- After each detected clock drift and adjustment, the secondary clock is **permanently nudged** toward the primary clock using the learning rate. +- The adjustment is calculated as: + new_clock_offset = current_offset + (drift * learning_rate) +- Over time, this reduces the overall drift and **increases the interval between needed sync events**, resulting in a more stable and low-overhead synchronization model. + +This tiny correction is applied to the secondary clock base value. With repeated learning cycles, the secondary system adapts and remains closer to the primary clock with minimal intervention. + +This learning mechanism is optional and tunable via the command line parameter -l, allowing fine-grained control over synchronization behavior based on system stability and timing accuracy needs. + +## 📈 Step-Based Frequency Correction +To achieve smooth and compliant synchronization, SW GenLock applies clock frequency corrections in controlled steps, allowing fast convergence without violating PHY or PLL constraints. + +🔹 **Shift 1** – Fine-Grained Correction +Shift 1 represents a small, controlled adjustment to the secondary clock’s frequency, defined as a percentage of the base PLL frequency—typically between 0.01% to 0.1%. This step is used continuously to either slightly speed up or slow down the clock to stay in sync with the primary. It ensures the corrections stay within the compliance limits of the underlying PHY and PLL, enabling stable and gradual convergence. + +🔹 **Shift 2** – Rapid Drift Recovery +Shift 2 is triggered when the drift between primary and secondary clocks crosses a defined threshold, such as 1 millisecond. It calculates a larger correction to close the timing gap more quickly. However, instead of applying this large adjustment at once, the correction is split into multiple smaller Shift 1 steps to maintain safe operating limits and avoid abrupt changes that could disrupt system behavior. + +This two-step correction model ensures the secondary clock reaches alignment with the primary efficiently and within hardware limitations, even under large initial drift conditions. + + +## Offset Overshoot Control +This feature allows the secondary clock to intentionally overshoot the ideal alignment point (zero delta) within the permitted drift range. Controlled via the -o parameter (default: 0.0, range: 0.0 to 1.0), it defines how far in the opposite direction the clock is allowed to go before beginning convergence. For example, setting -o 0.5 with a delta of 500 µs shifts the sync target to -250 µs, helping reduce the frequency of corrections and avoiding abrupt PLL adjustments. This results in smoother synchronization and longer stable intervals. + + +## Data collection and Graph generation +The tool logs key synchronization metrics in CSV format, such as time between sync events, delta values at the point of sync trigger, and the applied PLL frequency. A Python script is included to generate plots that help visualize the system’s behavior over long durations. It is recommended to use a virtual environment (especially on Ubuntu 24.04 or later) to avoid conflicts with system packages. You can create and activate a virtual environment as follows: + +```bash +python3 -m venv venv +source venv/bin/activate +pip install matplotlib +``` + +Now run + +```bash +(venv)$ python ./scripts/plot_sync_interval.py ./sync_pipe_0_20250610_110827.csv +``` + +
+
Long hour synchronization with Chrony +
Figure: Long hour synchronization with Chrony
+
+ +**To read graph:​** The data points show the duration between sync events. With a learning time window of 480 seconds (8 minutes), the secondary PLL clock continued adjusting for approximately the first 1.5 hours. Once the sync interval exceeded 8 minutes, learning stopped, and the system remained stable, consistently maintaining sync intervals longer than 8 minutes. A single dip observed around the 4-hour mark is likely due to a transient issue with Chrony. + +This data proves valuable for debugging, performance tuning, and validating sync stability. Additionally, the logged PLL frequency can be reused as a command-line parameter to initialize the application with an optimal frequency, allowing it to skip the learning phase and achieve quicker synchronization. + +## Running vsync_test as a Regular User without sudo + +While the ptp synchronization section provides methods for configuring passwordless sudo, it may be preferable to operate without using sudo or root privileges. The `vsync_test` application, which requires access to protected system resources for memory-mapped I/O (MMIO) operations at `/sys/bus/pci/devices/0000:00:02.0/resource0`, can be configured to run under regular user permissions. This can be achieved by modifying system permissions either through direct file permission changes or by adjusting user group memberships. Note that the resource path might change in future versions of the kernel. The resource path can be verified by running the application with the `strace` tool in Linux as a regular user and checking for permission denied errors or by examining `/proc/processid/maps` for resource mappings. + +### Option 1: Modifying File Permissions + +Altering the file permissions to allow all users to read and write to the device resource removes the requirement for root access. This method should be used with caution as it can lower the security level of the system, especially in environments with multiple users or in production settings. + +To change the permissions, the following command can be executed: + +```bash +sudo chmod o+rw /sys/bus/pci/devices/0000:00:02.0/resource0 +``` + +This command adjusts the permissions to permit all users (o) read and write (rw) access to the specified resource. + +### Option 2: Configuring User Group Permissions and Creating a Persistent udev Rule + +A more secure method is to assign the resource to a specific user group, such as the `video` group, and add the user to that group. This method confines permissions to a controlled group of users. To ensure the changes persist after a reboot, a `udev` rule can be created. + +#### Step 1: Add the Resource to the Video Group + +Change the group ownership of the resource file to the `video` group using the following command: + +```bash +sudo chgrp video /sys/bus/pci/devices/0000:00:02.0/resource0 +``` + +#### Step 2: Modify Group Permissions + +Set the group permissions to allow read and write access with: + +```bash +sudo chmod g+rw /sys/bus/pci/devices/0000:00:02.0/resource0 +``` + +#### Step 3: Add the User to the Video Group + +Add the user to the video group using this command (replace username with the actual user name): + +```bash +sudo usermod -a -G video username +``` + +Log out and log back in, or start a new session, for the group changes to take effect. + +#### Step 4: Create a udev Rule for Persistent Permissions + +To make the permissions persist after reboot, create a `udev` rule by adding a new rule file in `/etc/udev/rules.d/`: + +```bash +echo 'ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:00:02.0", RUN+="/bin/chgrp video %S%p/resource0", RUN+="/bin/chmod 0660 %S%p/resource0"' | sudo tee /etc/udev/rules.d/99-vsync-access.rules +``` + +This rule ensures that the permissions are correctly set when the system boots. + +#### Step 5: Reload udev Rules + +After creating the rule, reload the udev rules to apply them immediately without rebooting: + +```bash +sudo udevadm control --reload-rules && sudo udevadm trigger -s pci --action=add +``` + +#### Verifying the Changes + +After applying one of the above methods, confirm that the user has the necessary permissions by running the vsync_test application without elevated privileges. If the setup is correct, the application should run without requiring sudo or root access. + +## Running ptp4l as a Regular User without sudo + +Similarly, the ptp4l command can be executed by a regular user. This can be done by either altering the permissions or modifying the group for `/dev/ptp0`, or by creating a udev rule to ensure persistent permissions. + +```bash +echo 'SUBSYSTEM=="ptp", KERNEL=="ptp0", GROUP="video", MODE="0660"' | sudo tee /etc/udev/rules.d/99-ptp-access.rules +``` + +Reload udev Rules + +```bash +sudo udevadm control --reload-rules && sudo udevadm trigger +``` + +However, `phc2sys` will still need to be run with root or sudo privileges as it involves modifying system time. Running it under a normal user account could be possible if permissions to update the system clock are granted to the application. + +# Debug Tools + +When performing debugging, it is helpful to have the following libraries installed: + +```bash +apt install -y intel-gpu-tools edid-decode +``` + +## Debug Tool Usage + +When encountering unexpected behavior that requires additional debugging, the installed tools can assist in narrowing down the potential problem. + +**[intel-gpu-tools](https://cgit.freedesktop.org/xorg/app/intel-gpu-tools/)** +Provides tools to read and write to registers, it must be run as the root user. Example: + +```console +$ intel_reg dump --all > /tmp/reg_dump.txt + +# output excerpt + GEN6_RP_CONTROL (0x0000a024): 0x00000400 +Gen6 disabled + GEN6_RPNSWREQ (0x0000a008): 0x150a8000 + GEN6_RP_DOWN_TIMEOUT (0x0000a010): 0x00000000 + GEN6_RP_INTERRUPT_LIMITS (0x0000a014): 0x00000000 + GEN6_RP_UP_THRESHOLD (0x0000a02c): 0x00000000 + GEN6_RP_UP_EI (0x0000a068): 0x00000000 + GEN6_RP_DOWN_EI (0x0000a06c): 0x00000000 + GEN6_RP_IDLE_HYSTERSIS (0x0000a070): 0x00000000 + GEN6_RC_STATE (0x0000a094): 0x00040000 + GEN6_RC_CONTROL (0x0000a090): 0x00040000 + GEN6_RC1_WAKE_RATE_LIMIT (0x0000a098): 0x00000000 + GEN6_RC6_WAKE_RATE_LIMIT (0x0000a09c): 0x00000000 + GEN6_RC_EVALUATION_INTERVAL (0x0000a0a8): 0x00000000 + GEN6_RC_IDLE_HYSTERSIS (0x0000a0ac): 0x00000019 + GEN6_RC_SLEEP (0x0000a0b0): 0x00000000 + GEN6_RC1e_THRESHOLD (0x0000a0b4): 0x00000000 + GEN6_RC6_THRESHOLD (0x0000a0b8): 0x00000000 +... + +intel_reg read 0x00062400 + PIPE_DDI_FUNC_CTL_C (0x00062400): 0x00000000 (disabled, no port, HDMI, 8 bpc, -VSync, -HSync, EDP A ON, x1) +``` + +**[edid-decode](https://manpages.debian.org/unstable/edid-decode/edid-decode.1.en.html)** +is a tool used to parse the Extended Display Identification Data (EDID) from monitors and display devices. EDID contains metadata about the display's capabilities, such as supported resolutions, manufacturer, serial number, and other attributes. + +To operate edid-decode, it is typically necessary to supply a binary EDID file or data. Here's an example of how to use it: + +```console +$ cat /sys/class/drm/card0-HDMI-A-1/edid > edid.bin + +# run edid-decode to parse and display the information +edid-decode edid.bin + +# sample output +Extracted contents: +header: 00 ff ff ff ff ff ff 00 +serial number: 10 ac 64 a4 4c 30 30 32 +version: 01 03 +basic params: 80 34 20 78 2e +chroma info: c5 c4 a3 57 4a 9c 25 12 50 54 +established: bf ef 80 +standard: 71 4f 81 00 81 40 81 80 95 00 a9 40 b3 00 01 01 +descriptor 1: 4d d0 00 a0 f8 70 3e 80 30 20 35 00 55 50 21 00 00 1a +descriptor 2: 02 3a 80 18 71 38 2d 40 58 2c 45 00 55 50 21 00 00 1e +descriptor 3: 00 00 00 ff 00 43 32 30 30 38 30 4e 50 30 0a 20 20 20 +descriptor 4: 00 00 00 fd 00 32 4b 1e 53 11 00 0a 20 20 20 20 20 20 +extensions: 01 +checksum: 1c + +Manufacturer: DEL Model a464 Serial Number 808909324 +Made week 12 of 2008 +EDID version: 1.3 +Digital display +Maximum image size: 52 cm x 32 cm +Gamma: 2.20 +DPMS levels: Standby Suspend Off +RGB color display +First detailed timing is preferred timing +Display x,y Chromaticity: + Red: 0.6396, 0.3300 + Green: 0.2998, 0.5996 + Blue: 0.1503, 0.0595 + White: 0.3134, 0.3291 +Established timings supported: + 720x400@70Hz + 640x480@60Hz + 640x480@67Hz + 640x480@72Hz + 640x480@75Hz + 800x600@56Hz + 800x600@60Hz + ... +Standard timings supported: + 1152x864@75Hz + 1280x800@60Hz + 1280x960@60Hz + ... +Detailed mode: Clock 148.500 MHz, 509 mm x 286 mm + 1920 2008 2052 2200 hborder 0 + 1080 1084 1089 +``` diff --git a/cmn/debug.h b/cmn/debug.h index 58c195a..d1155a5 100644 --- a/cmn/debug.h +++ b/cmn/debug.h @@ -1,83 +1,109 @@ -/* - * Copyright © 2024 Intel Corporation - * - * 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 (including the next - * paragraph) 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. - * - */ - -#ifndef _DEBUG_H -#define _DEBUG_H - -enum log_level { - LOG_LEVEL_NONE, - LOG_LEVEL_ERROR, - LOG_LEVEL_WARNING, - LOG_LEVEL_INFO, - LOG_LEVEL_DEBUG, - LOG_LEVEL_TRACE, -}; - -extern log_level dbg_lvl; - -void set_log_mode(const char* mode); -void set_log_level(log_level level); -void set_log_level(const char* log_level); -void log_message(log_level level, const char* format, ...); - -#define PRINT(format, ...) \ - log_message(LOG_LEVEL_NONE, format, ##__VA_ARGS__) - -#define ERR(format, ...) \ - log_message(LOG_LEVEL_ERROR, format, ##__VA_ARGS__) - -#define WARNING(format, ...) \ - log_message(LOG_LEVEL_WARNING, format, ##__VA_ARGS__) - -#define INFO(format, ...) \ - log_message(LOG_LEVEL_INFO, format, ##__VA_ARGS__) - -#define DBG(format, ...) \ - log_message(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) - -#define TRACE(format, ...) \ - log_message(LOG_LEVEL_TRACE, format, ##__VA_ARGS__) - - -#ifdef __cplusplus -class tracer { -private: - char *m_func_name; -public: - tracer(const char *func_name) - { - TRACE(">>> %s\n", func_name); - m_func_name = (char *) func_name; - } - ~tracer() { TRACE("<<< %s\n", m_func_name); } -}; -#endif - -#ifdef __cplusplus -#define TRACING() tracer trace(__FUNCTION__); -#else -#define TRACING() -#endif - -#endif // _DEBUG_H +/* + * Copyright © 2024 Intel Corporation + * + * 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 (including the next + * paragraph) 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. + * + */ + +#ifndef _DEBUG_H +#define _DEBUG_H + +#include +#include +typedef enum { + LOG_LEVEL_NONE, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_TRACE, +} log_level; + +extern log_level dbg_lvl; + +void log_message(log_level level, const char* format, ...); + +#define PRINT(format, ...) \ + log_message(LOG_LEVEL_NONE, format, ##__VA_ARGS__) + +#define ERR(format, ...) \ + log_message(LOG_LEVEL_ERROR, format, ##__VA_ARGS__) + +#define WARNING(format, ...) \ + log_message(LOG_LEVEL_WARNING, format, ##__VA_ARGS__) + +#define INFO(format, ...) \ + log_message(LOG_LEVEL_INFO, format, ##__VA_ARGS__) + +#define DBG(format, ...) \ + log_message(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) + +#define TRACE(format, ...) \ + log_message(LOG_LEVEL_TRACE, format, ##__VA_ARGS__) + +/* +The existing trace object initialization code used __FUNCTION__, +which provides only the function name without the class name. + +This macro extracts both the function and class names for use in TRACE logging. +Including the class name is necessary to distinguish functions with the same name +in different classes. + +Example: + combo::program_phy + dkl::program_phy +*/ +#define FUNCTION_NAME ([]() -> const char* { \ + static char buffer[128]; /* Buffer to store extracted function name */ \ + const char *func = __PRETTY_FUNCTION__; \ + const char *start = strstr(func, "::"); /* Locate "::" which separates class and function */ \ + if (!start) return func; /* If "::" not found, return full function signature */ \ + while (start > func && *(start - 1) != ' ') --start; /* Move back to the first character of class name */ \ + const char *end = strchr(start, '('); /* Locate '(' at the end of function name */ \ + if (!end) return func; /* Edge case: If '(' not found, return original */ \ + int len = (int)(end - start); \ + if (len < 0 || len >= (int)sizeof(buffer)) return func; /* Prevent buffer overflow */ \ + strncpy(buffer, start, len); \ + buffer[len] = '\0'; /* Null-terminate */ \ + return buffer; \ +}()) + + +#ifdef __cplusplus +class tracer { +private: + const char *m_func_name; +public: + tracer(const char *func_name) : m_func_name(func_name) { + TRACE(">>> %s\n", m_func_name); + } + ~tracer() { + TRACE("<<< %s\n", m_func_name); + } +}; +#endif + +#ifdef __cplusplus +#define TRACING() tracer trace(FUNCTION_NAME ? FUNCTION_NAME : "UnknownFunction"); +#else +#define TRACING() +#endif + +#endif // _DEBUG_H diff --git a/cmn/version.h b/cmn/version.h index ab4de6b..48cf5b9 100644 --- a/cmn/version.h +++ b/cmn/version.h @@ -1,49 +1,49 @@ -/* - * Copyright © 2024 Intel Corporation - * - * 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 (including the next - * paragraph) 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. - * - */ - -#ifndef _VERSION_H -#define _VERSION_H - -#include -#include - -#define VERSION_MAJOR 2 -#define VERSION_MINOR 0 -#define VERSION_PATCH 0 - -/** - * @brief Constructs a version string from major, minor, and patch numbers. - * - * @return A string representing the version in the format "major.minor.patch". - */ -static inline std::string get_version() -{ - std::ostringstream version_stream; - version_stream << VERSION_MAJOR << '.' - << VERSION_MINOR << '.' - << VERSION_PATCH; - return version_stream.str(); -} - -#endif +/* + * Copyright © 2024 Intel Corporation + * + * 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 (including the next + * paragraph) 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. + * + */ + +#ifndef _VERSION_H +#define _VERSION_H + +#include +#include + +#define VERSION_MAJOR 4 +#define VERSION_MINOR 0 +#define VERSION_PATCH 0 + +/** + * @brief Constructs a version string from major, minor, and patch numbers. + * + * @return A string representing the version in the format "major.minor.patch". + */ +static inline std::string get_version() +{ + std::ostringstream version_stream; + version_stream << VERSION_MAJOR << '.' + << VERSION_MINOR << '.' + << VERSION_PATCH; + return version_stream.str(); +} + +#endif diff --git a/cmn/vsyncalter.h b/cmn/vsyncalter.h index bb0e28b..afb2d1d 100644 --- a/cmn/vsyncalter.h +++ b/cmn/vsyncalter.h @@ -1,39 +1,70 @@ -/* - * Copyright © 2024 Intel Corporation - * - * 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 (including the next - * paragraph) 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. - * - */ - -#ifndef _VSYNCALTER_H -#define _VSYNCALTER_H - -#define ONE_VSYNC_PERIOD_IN_MS 16.666 -#define MAX_TIMESTAMPS 100 -#define ALL_PIPES 4 - -int vsync_lib_init(); -void vsync_lib_uninit(); -void synchronize_vsync(double time_diff, int pipe = ALL_PIPES, double shift = 0.01); -int get_vsync(long *vsync_array, int size, int pipe = 0); -double get_vblank_interval(int pipe); -void shutdown_lib(void); - -#endif +/* + * Copyright © 2024 Intel Corporation + * + * 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 (including the next + * paragraph) 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. + * + */ + +#ifndef _VSYNCALTER_H +#define _VSYNCALTER_H + +#include +#include +#include + +#define VSYNC_ONE_VSYNC_PERIOD_IN_MS 16.666 +#define VSYNC_MAX_TIMESTAMPS 100 +#define VSYNC_ALL_PIPES 4 +#define VSYNC_DEFAULT_DEVICE "/dev/dri/card0" +#define VSYNC_DEFAULT_PIPE 0 +#define VSYNC_DEFAULT_SHIFT 0.01 +#define VSYNC_DEFAULT_SHIFT2 0.1 +#define VSYNC_DEFAULT_RESET true +#define VSYNC_DEFAULT_COMMIT true +#define VSYNC_DEFAULT_WAIT_IN_MS 50 +#define VSYNC_TIME_DELTA_FOR_STEP 1000 + +#ifdef __cplusplus +extern "C" { +#endif + +int vsync_lib_init(const char *device_str, bool dp_m_n); +int vsync_lib_uninit(); +int synchronize_vsync(double time_diff, int pipe, double shift, double shift2, + int step_threshold, int wait_between_steps, bool reset, + bool commit); +int get_vsync(const char *device_str, uint64_t *vsync_array, int size, int pipe); +double get_vblank_interval(const char *device_str, int pipe, int size); +int set_pll_clock(double pll_clock, int pipe, double shift, + uint32_t wait_between_steps); +double get_pll_clock(int pipe); +bool get_phy_name(int pipe, char* out_name, size_t out_size); +int print_drm_info(const char *device_str); +void shutdown_lib(void); +const char* find_first_dri_card(void); +int set_log_mode(const char* mode); +int set_log_level(log_level level); +int set_log_level_str(const char* log_level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/docs/chrony.md b/docs/chrony.md new file mode 100644 index 0000000..85b487e --- /dev/null +++ b/docs/chrony.md @@ -0,0 +1,119 @@ + +# Synchronization Real-time clock Using Chrony (For Non-PTP Interfaces) + +For systems with Ethernet adapters that do not support PTP (Precision Time Protocol), **Chrony** offers a practical alternative for clock synchronization. This method ensures consistent time alignment across systems required by the software genlock feature. + +However, it is important to note that **Chrony-based synchronization may not achieve the same level of accuracy as hardware-assisted PTP**, as it relies entirely on software. Its precision can be affected by factors such as **network latency, interface speed, and overall system load**. For applications requiring tight synchronization margins, PTP remains the preferred option when supported. + +#### Primary System (Time Server) + +1. **Install Chrony**: + ```bash + sudo apt install chrony + ``` +2. **Edit the configuration file /etc/chrony/chrony.conf**: + ``` + # Allow clients from the subnet 192.168.1.0 + allow 192.168.1.0/24 # + + # (Optional) Bind to a specific network interface + binddevice + hwtimestamp + local stratum 8 + + # Make sure primary Chrony doesn't sync with any external pool/server. Disable/remove all of them + # pool pool.ntp.org iburst + # server corp.intel.com iburst + ``` + +3. **Restart the Chrony service**: + ```bash + sudo systemctl restart chrony + ``` + +4. **(If a firewall is active) Allow NTP traffic**: + ```bash + sudo ufw allow 123/udp + ``` + +#### Secondary System (Time Client) + +1. **Install Chrony**: + ```bash + sudo apt install chrony + ``` +2. **Edit the configuration file /etc/chrony/chrony.conf**: + Replace any existing pool or server entries with: + ``` + server iburst minpoll 1 maxpoll 1 + binddevice + hwtimestamp + ``` + +4. **Restart the Chrony service**: + ```bash + sudo systemctl restart chrony + ``` + +5. **(If a firewall is active) Allow NTP traffic**: + ```bash + sudo ufw allow 123/udp + +#### Verifying Synchronization (on the Client) +Use the following commands to verify synchronization status: + +##### On Primary: +```bash +$ chronyc tracking +Reference ID : 7F7F0101 () +Stratum : 8 + +$ chronyc sources +MS Name/IP address Stratum Poll Reach LastRx Last sample +=============================================================================== + +``` +The reference ID in the () should be blank and sources list should be empty. + +##### On Secondary: + +The output should list the primary system as the time source (Assuming secondary system address is 192.168.1.100): +```bash +$ chronyc tracking +Reference ID : C0A80A02 (192.168.1.4) +Stratum : 9 + +$ chronyc sources +MS Name/IP address Stratum Poll Reach LastRx Last sample +=============================================================================== +^* 192.168.1.4 8 9 377 354 +7943ns[+7967ns] +/- 45us +``` +The reference ID in the () should point to primary IP address and sources list should only have primary IP entry. The ^* symbol indicates that synchronization is active and successful. + +##### Set Real-Time Scheduling Priority + +Chrony should be run with real-time scheduling policy (SCHED_FIFO) and high priority. On both primary and secondary run + +```bash +$ sudo systemctl edit chrony +``` + +Add the following: + +``` +### Editing /etc/systemd/system/chrony.service.d/override.conf +### Anything between here and the comment below will become the new contents of the file + +[Service] +Nice=-20 +CPUSchedulingPolicy=FIFO + +### Lines below this comment will be discarded +``` + +Then reload and restart: + +``` +$ sudo systemctl daemon-reexec +$ sudo systemctl restart chrony +``` diff --git a/docs/images/directory_tree.png b/docs/images/directory_tree.png new file mode 100644 index 0000000..42b1a18 Binary files /dev/null and b/docs/images/directory_tree.png differ diff --git a/docs/images/graph.png b/docs/images/graph.png new file mode 100644 index 0000000..284d1e9 Binary files /dev/null and b/docs/images/graph.png differ diff --git a/docs/images/ptp4l.png b/docs/images/ptp4l.png new file mode 100644 index 0000000..0cc09e7 Binary files /dev/null and b/docs/images/ptp4l.png differ diff --git a/docs/images/swgenlock.png b/docs/images/swgenlock.png new file mode 100644 index 0000000..1169c12 Binary files /dev/null and b/docs/images/swgenlock.png differ diff --git a/docs/images/vsync_test.png b/docs/images/vsync_test.png new file mode 100644 index 0000000..cd0f951 Binary files /dev/null and b/docs/images/vsync_test.png differ diff --git a/docs/ptp.md b/docs/ptp.md new file mode 100644 index 0000000..1ad869d --- /dev/null +++ b/docs/ptp.md @@ -0,0 +1,120 @@ + +# Real-Clock Configuration in PTP-Supported Systems + +Systems supporting PTP typically have two clocks: the system's real-time clock and another clock on the Ethernet card. The synchronization process involves: + +1. **Primary System Real-Time Clock Synchronization**: + - Initially synchronize the primary system's real-time clock with an NTP server. + - Then sync the Ethernet PTP clock with the primary system's real-time clock. + +2. **Secondary Systems Synchronization**: + - Sync the secondary systems' Ethernet PTP clocks with the primary's Ethernet PTP clock. + - Sync their system real-time clocks with their Ethernet PTP clocks, which are already synchronized with the primary system. + +This setup ensures all secondary system's real-time clocks are synchronized with the primary system's real-time clock. Synchronization within a system (system real-time clock to Ethernet PTP clock) is managed using the `phc2sys` tool, while inter-system synchronization (across Ethernet interfaces) utilizes the `ptp4l` tool. + +
+
+ PTP4L Setup +
Figure: PTP4L Setup.
+
+
+ +It's also important to turn off NTP time synchronization service by using this command: + ```$ timedatectl set-ntp no``` + +### Command Sets for PTP time Synchronization + +Assuming the primary Ethernet interface is named `enp87s0` and the secondary interface is named `enp89s0`, the following commands are to be executed under root or with sudo: + +**Primary System Commands:** + +- **ptp4l:** + +```shell +$ ptp4l -i enp87s0 -m -f ./resources/gPTP.cfg +``` + +- **phc2sys:** + +```shell +$ phc2sys -c enp87s0 -s CLOCK_REALTIME -w -O 0 -m -f ./resources/gPTP.cfg +``` + +**Secondary System Commands:** + +- **ptp4l:** + +```shell +$ ptp4l -i enp89s0 -s -m -f ./resources/gPTP.cfg +``` + +- **phc2sys:** + +```shell +$ phc2sys -s enp89s0 -c CLOCK_REALTIME -O 0 -m -f ./resources/gPTP.cfg +``` + +The output of ptp4l on the primary system after running the above command should look + like this:
+ +```console +ptp4l[13416.983]: selected /dev/ptp1 as PTP clock +ptp4l[13417.002]: port 1: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l[13417.003]: port 0: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l[13420.445]: port 1: LISTENING to MASTER on ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES +ptp4l[13420.445]: selected local clock 844709.fffe.04f07f as best master +ptp4l[13420.445]: assuming the grand master role +``` + +The output of ptp4l on the secondary system after running the above command should +look like this:
+ +```console +ptp4l[14816.313]: selected /dev/ptp1 as PTP clock +ptp4l[14816.328]: port 1: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l[14816.328]: port 0: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l[14820.220]: port 1: new foreign master 844709.fffe.04f07f-1 +ptp4l[14820.250]: selected local clock 844709.fffe.04eb0e as best master +ptp4l[14822.220]: selected best master clock 844709.fffe.04f07f +ptp4l[14822.220]: port 1: LISTENING to UNCALIBRATED on RS_SLAVE +ptp4l[14822.650]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED +ptp4l[14823.275]: rms 158899 max 317730 freq -11742 +/- 3075 delay 14 +/- 0 +ptp4l[14824.276]: rms 820 max 1257 freq -9067 +/- 1113 delay 14 +/- 0 +ptp4l[14825.277]: rms 1366 max 1436 freq -6582 +/- 359 delay 13 +/- 0 +ptp4l[14826.278]: rms 866 max 1148 freq -6099 +/- 34 delay 13 +/- 0 +ptp4l[14827.279]: rms 280 max 465 freq -6364 +/- 102 delay 12 +/- 0 +ptp4l[14828.280]: rms 52 max 80 freq -6666 +/- 65 delay 12 +/- 0 +ptp4l[14829.280]: rms 82 max 88 freq -6805 +/- 21 delay 12 +/- 0 +ptp4l[14830.281]: rms 50 max 69 freq -6829 +/- 3 delay 12 +/- 0 +ptp4l[14831.282]: rms 15 max 28 freq -6811 +/- 7 delay 12 +/- 0 +ptp4l[14832.283]: rms 4 max 8 freq -6795 +/- 5 delay 12 +/- 0 +ptp4l[14833.284]: rms 7 max 10 freq -6783 +/- 3 delay 12 +/- 0 +ptp4l[14834.285]: rms 2 max 5 freq -6788 +/- 3 delay 13 +/- 0 +ptp4l[14835.286]: rms 3 max 5 freq -6793 +/- 3 delay 13 +/- 0 +``` + +Please make it sure that the secondary sytem refers to the primary's precision time + by checking if there is the **"new foreign master"** message in a log
+ Note that the rms value should be decreasing with each line and should go + down to single digits. + +For user convenience, an automation Bash script is available in the scripts folder to facilitate ptp4l synchronization on both primary and secondary systems. Users simply need to update the `config.sh` file with relevant details such as the secondary system's address, username, and Ethernet address, among others. After these updates, run the scripts/run_ptp.sh script from the primary system. This script will automatically execute the ptp4l and phc2sys tools on both the primary and secondary systems in their respective modes. Note that the script requires the secondary system to enable passwordless SSH to execute ptp4l commands through the SSH interface. + +```console +$ ./scripts/run_ptp.sh +``` + +The console will display logs from both systems, but each process, including ptp4l and phc2sys, will also generate its own separate log file. These files can be found in the ./logs directory at the root level. + +Since the ptp4l and phc2sys tools require root privileges or sudo access, users need to provide their sudo password in the config.sh file. As an alternative, setting up passwordless sudo can streamline this process. Here's how to enable passwordless sudo: + +Open a terminal and type `sudo visudo` to edit the sudoers file. +Add the following line to the file at the end, replacing username with actual username: + +```bash +username ALL=(ALL) NOPASSWD: ALL +``` + +Save and close the file to apply the changes. +This adjustment allows the automation scripts to run without requiring a sudo password each time, simplifying the setup for ptp4l and phc2sys synchronizations. diff --git a/lib/Makefile b/lib/Makefile index 7fecc7d..1da48a0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,63 +1,70 @@ -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: MIT - -# Compiler and Flags -CXX := g++ -CXXFLAGS := -Wall -fPIC -I../cmn -Iplatforms -Iinclude -I. -I/usr/include/drm -LIBS :=-lrt -ldrm -lpciaccess -# Directories -SRCDIR := . -OBJDIR := obj -INCLUDEDIR := include - -# Target libraries -TARGET_SHARED := libvsyncalter.so -TARGET_STATIC := libvsyncalter.a - -# Find source files, create a list of object files -SOURCES := $(wildcard $(SRCDIR)/*.cpp) -HEADERS := $(wildcard $(SRCDIR)/*.h ../cmn/*.h) -OBJECTS := $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o) -DEPS := $(OBJECTS:.o=.d) # Dependency files corresponding to object files - -# Check if VERBOSE is set to 1, if not, silence the commands -ifeq ($(VERBOSE),1) -CMD_PREFIX= -else -CMD_PREFIX=@ -endif - -# Default target -all: $(TARGET_SHARED) $(TARGET_STATIC) - -# Shared library -$(TARGET_SHARED): $(OBJECTS) - $(CMD_PREFIX)echo "Creating shared library..." - $(CMD_PREFIX)$(CXX) -Wall -shared $(DBG_FLAGS) -o $@ $^ $(LIBS) -# Static library -$(TARGET_STATIC): $(OBJECTS) - $(CMD_PREFIX)echo "Creating static library..." - $(CMD_PREFIX)ar rcs $@ $^ - -# Compile source files into object files -$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(HEADERS) - $(CMD_PREFIX)echo "Compiling $<" - $(CMD_PREFIX)mkdir -p $(OBJDIR) - $(CMD_PREFIX)$(CXX) $(CXXFLAGS) $(DBG_FLAGS) -c $< -o $@ - -# Include the .d files --include $(DEPS) - -debug: - @export DBG_FLAGS='-g -D DEBUGON'; \ - $(MAKE) - -# Clean up build artifacts -clean: - $(CMD_PREFIX)echo "Cleaning..." - $(CMD_PREFIX)rm -f $(TARGET_SHARED) $(TARGET_STATIC) $(OBJECTS) $(DEPS) - $(CMD_PREFIX)rm -rf $(OBJDIR) - - -# Phony targets -.PHONY: all clean +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: MIT + +# Compiler and Flags +CXX := g++ +CXXFLAGS := -Wall -fPIC -I../cmn -Iplatforms -Iinclude -I. -I/usr/include/drm +LIBS :=-lrt -ldrm -lpciaccess +# Directories +SRCDIR := . +OBJDIR := obj +INCLUDEDIR := include + +COVERAGE_FLAGS := -fprofile-arcs -ftest-coverage -O0 -g + +# Target libraries +TARGET_SHARED := libvsyncalter.so +TARGET_STATIC := libvsyncalter.a + +# Find source files, create a list of object files +SOURCES := $(wildcard $(SRCDIR)/*.cpp) +HEADERS := $(wildcard $(SRCDIR)/*.h ../cmn/*.h $(SRCDIR)/platforms/*.h) +OBJECTS := $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o) +DEPS := $(OBJECTS:.o=.d) # Dependency files corresponding to object files + +# Check if VERBOSE is set to 1, if not, silence the commands +ifeq ($(VERBOSE),1) +CMD_PREFIX= +else +CMD_PREFIX=@ +endif + +# Default target +all: $(TARGET_SHARED) $(TARGET_STATIC) + +# Shared library +$(TARGET_SHARED): $(OBJECTS) + $(CMD_PREFIX)echo "Creating shared library..." + $(CMD_PREFIX)$(CXX) -Wall -shared $(CXXFLAGS) $(DBG_FLAGS) -o $@ $^ $(LIBS) +# Static library +$(TARGET_STATIC): $(OBJECTS) + $(CMD_PREFIX)echo "Creating static library..." + $(CMD_PREFIX)ar rcs $@ $^ + +# Compile source files into object files +$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(HEADERS) + $(CMD_PREFIX)echo "Compiling $<" + $(CMD_PREFIX)mkdir -p $(OBJDIR) + $(CMD_PREFIX)$(CXX) $(CXXFLAGS) $(DBG_FLAGS) -c $< -o $@ + +# Include the .d files +-include $(DEPS) + +coverage: + @echo "Building static and shared libraries with coverage flags..." + $(MAKE) clean + $(MAKE) CXXFLAGS="$(CXXFLAGS) $(COVERAGE_FLAGS)" all + +debug: + @export DBG_FLAGS='-g -O0 -D DEBUGON'; \ + $(MAKE) + +# Clean up build artifacts +clean: + $(CMD_PREFIX)echo "Cleaning..." + $(CMD_PREFIX)rm -f $(TARGET_SHARED) $(TARGET_STATIC) $(OBJECTS) $(DEPS) + $(CMD_PREFIX)rm -rf $(OBJDIR) + + +# Phony targets +.PHONY: all clean diff --git a/lib/c10.cpp b/lib/c10.cpp index 5f1fb66..eb42963 100644 --- a/lib/c10.cpp +++ b/lib/c10.cpp @@ -1,734 +1,239 @@ -/* - * Copyright © 2024 Intel Corporation - * - * 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 (including the next - * paragraph) 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. - * - */ - -#include -#include -#include -#include -#include -#include "mmio.h" -#include "c10.h" - - -/** - * @brief - * Constructor for c10 class - * @param ds - */ -c10::c10(ddi_sel *ds) -{ - - /* - According to BSpecs, MTL and PTL DDIs for C10 correspond to ports A and B. - Consequently, it is reasonable to deduce the port number from de_clk as clk-1. - However, this assumption might not hold for future platforms, where it - may be necessary to derive the port number directly from register values. - For guidance on this approach, refer to combo.cpp. - - BSpecs: - Port Usage Capability Alternate Names - DDI A | Port A | eDP, DP, HDMI DDIA, Port A - DDI B | Port B | eDP, DP, HDMI DDIB, Port B - */ - u32 port = ds->de_clk - 1; - - int pll_state; - for(int i = 0; i < C10_PLL_REG_COUNT; i++) { - pll_state = __intel_cx0_read(port, 0, PHY_C10_VDR_PLL(i)); - DBG("c10: pll_state[%d] = 0x%X, %d\n",i, pll_state,pll_state); - } - - c10_reg.done=1; - ds->phy_data = &c10_reg; - - set_ds(ds); - set_init(true); - -} - -/** - * @brief - * This is an inline function to multiply two 32-bit numbers - * and return the result as a 64-bit number. - * @param a - The first 32-bit number - * @param b - The second 32-bit number - */ -static inline unsigned long long mul_u32_u32(u32 a, u32 b) -{ - return (unsigned long long)a * b; -} - - -/** -* @brief -* This function calculates pll values to achieve desired drift using the given shift value. -* It programs the MMIO registers of the PHY to achieve the desired drift and also sets a timer -* to reset the MMIO registers to their original values after a certain time period. -* @param time_diff - This is the time difference in between the primary and the -* secondary systems in ms. If master is ahead of the slave , then the time -* difference is a positive number otherwise negative. -* @param shift - Fraction value to be used during the calculation. -* @return void -*/ -void c10::program_phy(double time_diff, double shift) -{ - ddi_sel *ds = get_ds(); - c10_phy_reg *c10_phy = (c10_phy_reg *) ds->phy_data; - - int pll_state[C10_PLL_REG_COUNT]; - int mpll_frac_rem_15_0, mpll_frac_den_15_0, mpll_frac_quot_15_0, mpll_multiplier_11_0, ref_clk_mpll_div_2_0; - int pll_freq; - - for(int i = 0; i < C10_PLL_REG_COUNT; i++) { - pll_state[i] = __intel_cx0_read(ds->de_clk - 1, 0, PHY_C10_VDR_PLL(i)); - c10_phy->pll_state_old[i] = pll_state[i]; - DBG("c10: pll_state[%d] = 0x%X, %d\n", i, pll_state[i],pll_state[i]); - } - - - - if(time_diff < 0) { - shift *= -1; - } - - int steps = CALC_STEPS_TO_SYNC(time_diff, shift); - DBG("steps are %d\n", steps); - - mpll_frac_den_15_0 = pll_state[C10_PLL_REG_DEN_HIGH] << 8 | pll_state[C10_PLL_REG_DEN_LOW]; - mpll_frac_quot_15_0 = pll_state[C10_PLL_REG_QUOT_HIGH] << 8 | pll_state[C10_PLL_REG_QUOT_LOW]; - mpll_frac_rem_15_0 = pll_state[C10_PLL_REG_REM_HIGH] << 8 | pll_state[C10_PLL_REG_REM_LOW]; - mpll_multiplier_11_0 = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, pll_state[C10_PLL_REG_MULTIPLIER]) << 8 | - pll_state[2]) / 2 + 16; - ref_clk_mpll_div_2_0 = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, pll_state[C10_PLL_REG_TXCLKDIV]); - - pll_freq = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(REF_CLK_FREQ * 1000, (mpll_multiplier_11_0 << 16) + mpll_frac_quot_15_0) + - DIV_ROUND_CLOSEST(REF_CLK_FREQ * 1000 * mpll_frac_rem_15_0, mpll_frac_den_15_0), - 10 << (ref_clk_mpll_div_2_0 + 16)); - - double new_pll_freq = pll_freq + (shift * pll_freq / 100); - unsigned long long temp = new_pll_freq * (10 << (ref_clk_mpll_div_2_0 + 16)); - temp -= DIV_ROUND_CLOSEST(REF_CLK_FREQ * 1000 * mpll_frac_rem_15_0, mpll_frac_den_15_0); - temp /= (REF_CLK_FREQ * 1000); - temp -= ((unsigned long long)mpll_multiplier_11_0 << 16); - int new_mpll_frac_quot_15_0 = (int) temp; - c10_phy->pll_state[C10_PLL_REG_QUOT_LOW] = new_mpll_frac_quot_15_0 & GENMASK(7, 0); - c10_phy->pll_state[C10_PLL_REG_QUOT_HIGH] = new_mpll_frac_quot_15_0 >> 8; - - DBG("Old Quotient = 0x%X\n", mpll_frac_quot_15_0); - DBG("Denominator = 0x%X\n", mpll_frac_den_15_0); - DBG("Remainder = 0x%X\n", mpll_frac_rem_15_0); - DBG("Multiplier = 0x%X\n", mpll_multiplier_11_0); - - c10_phy->done = 0; - user_info *ui = new user_info(this, c10_phy); - make_timer((long) steps, ui, reset_phy_regs); - - program_mmio(c10_phy, 1); -} - -/** -* @brief -* This is a helper class function to act as callback and triggers resetting of C10 -* Phy MMIO registers to their original value. It gets executed whenever a timer expires. -* We program MMIO registers of the PHY in this function becase we have waited for a certain -* time period to get the primary and secondary systems vsync in sync and now -* it is time to reprogram the default values for the secondary system's PHYs. -* @param sig - The signal that fired -* @param *si - A pointer to a siginfo_t, which is a structure containing -* further information about the signal -* @param *uc - This is a pointer to a ucontext_t structure, cast to void *. -* The structure pointed to by this field contains signal context information -* that was saved on the user-space stack by the kernel -* @return void -*/ -void c10::reset_phy_regs(int sig, siginfo_t *si, void *uc) -{ - user_info *ui = (user_info *) si->si_value.sival_ptr; - if(!ui) { - return; - } - - DBG("timer done\n"); - c10_phy_reg *dr = (c10_phy_reg *) ui->get_reg(); - c10 *d = (c10 *) ui->get_type(); - - d->reset_phy_regs(dr); - delete ui; -} - -/** -* @brief -* This is an object function called by the timer callback and actually -* calls program_mmio(). -* @param dr - c10 phy register object -* @return void -*/ - void c10::reset_phy_regs(c10_phy_reg *dr) -{ - program_mmio(dr, 0); - dr->done = 1; -} - - -/** -* @brief -* This function waits until the C10 programming is -* finished. There is a timer for which time the new values will remain in -* effect. After that timer expires, the original values will be restored. -* @param t - The timer which needs to be deleted -* @return void -*/ -void c10::wait_until_done() -{ - TRACING(); - - ddi_sel *ds = get_ds(); - c10_phy_reg *c10_phy = (c10_phy_reg *) ds->phy_data; - - while(!c10_phy->done && !lib_client_done) { - usleep(1000); // Wait for 1 ms - } - - // Restore original values in case of app termination - if (lib_client_done) { - reset_phy_regs(c10_phy); - } - - timer_delete(get_timer()); -} - -/** -* @brief -* This function programs the MMIO (Memory-Mapped I/O) registers of the C10 PHY. -* It writes the specified values to the PHY's MMIO registers to configure its operation. -* -* @param pr - A pointer to a c10_phy_reg structure, which holds the MMIO register values to be programmed. -* @param mod - A modifier value that affects how the MMIO registers are programmed. -* @return void -*/ -void c10::program_mmio(c10_phy_reg *pr, int mod) -{ - - ddi_sel *ds = get_ds(); - u32 port = ds->de_clk - 1 ; - - intel_cx0_rmw(port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), - 0, C10_VDR_CTRL_MSGBUS_ACCESS, - MB_WRITE_COMMITTED); - - /* Custom width needs to be programmed to 0 for both the phy lanes */ - intel_cx0_rmw(port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CUSTOM_WIDTH, - C10_VDR_CUSTOM_WIDTH_MASK, C10_VDR_CUSTOM_WIDTH_8_10, - MB_WRITE_COMMITTED); - intel_cx0_rmw(port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), - 0, C10_VDR_CTRL_UPDATE_CFG, - MB_WRITE_COMMITTED); - - - __intel_cx0_write(port, 0, PHY_C10_VDR_PLL(C10_PLL_REG_QUOT_LOW), - mod ? pr->pll_state[C10_PLL_REG_QUOT_LOW] : pr->pll_state_old[C10_PLL_REG_QUOT_LOW], - MB_WRITE_COMMITTED); - - __intel_cx0_write(port, 0, PHY_C10_VDR_PLL(C10_PLL_REG_QUOT_HIGH), - mod ? pr->pll_state[C10_PLL_REG_QUOT_HIGH] : pr->pll_state_old[C10_PLL_REG_QUOT_HIGH], - MB_WRITE_COMMITTED); - - intel_cx0_rmw(port, INTEL_CX0_LANE0, PHY_C10_VDR_CONTROL(1), - 0, C10_VDR_CTRL_MASTER_LANE | C10_VDR_CTRL_UPDATE_CFG, - MB_WRITE_COMMITTED); - -} - - -/** - * @brief - * This routine waits until the target register @reg contains the expected - * @value after applying the @mask, i.e. it waits until :: - * - * (intel_uncore_read_fw(uncore, reg) & mask) == value - * - * Otherwise, the wait will timeout after @slow_timeout_ms milliseconds. - * For atomic context @slow_timeout_ms must be zero and @fast_timeout_us - * must be not larger than 20,0000 microseconds. - * - * Note that this routine assumes the caller holds forcewake asserted, it is - * not suitable for very long waits. See intel_wait_for_register() if you - * wish to wait without holding forcewake for the duration (i.e. you expect - * the wait to be slow). - * - * @param uncore - the struct intel_uncore - * @param reg - the register to read - * @param mask - mask to apply to register value - * @param value - expected value - * @param fast_timeout_us - fast timeout in microsecond for atomic/tight wait - * @param slow_timeout_ms - slow timeout in millisecond - * @param out_value - optional placeholder to hold registry value - * @return int - 0 if the register matches the desired condition, or -ETIMEDOUT. - * - */ -int c10::__intel_wait_for_register_fw( - u32 reg, - u32 mask, - u32 value, - u32 *out_value) -{ - u32 reg_value = 0; - int ret = 0; - - reg_value = READ_OFFSET_DWORD(reg); - - if (out_value) - *out_value = reg_value; - - return ret; -} - -/** - * @brief - * This routine waits until the target register @reg contains the expected - * @value after applying the @mask, i.e. it waits until :: - * - * (intel_uncore_read(uncore, reg) & mask) == value - * - * Otherwise, the wait will timeout after @timeout_ms milliseconds. - * - * @param reg - the register to read - * @param mask - mask to apply to register value - * @param value - expected value - * @param fast_timeout_us - fast timeout in microsecond for atomic/tight wait - * @param slow_timeout_ms - slow timeout in millisecond - * @param out_value - optional placeholder to hold registry value - * @return int - 0 if the register matches the desired condition, or -ETIMEDOUT. - */ -int c10::__intel_wait_for_register( - u32 reg, - u32 mask, - u32 value, - u32 *out_value) -{ - u32 reg_value; - int ret; - - ret = __intel_wait_for_register_fw(reg, mask, value, ®_value); - - if (out_value) - *out_value = reg_value; - - return ret; -} - -/** - * @brief - * Wait for register to match expected value - * @param reg - register to wait on - * @param mask - mask to apply to register value - * @param value - expected value - * @param timeout_ms - - * @return int - 0 if the register matches the desired condition, or -ETIMEDOUT. - */ -int c10::intel_wait_for_register(u32 reg, - u32 mask, - u32 value, - unsigned int timeout_ms) -{ - return __intel_wait_for_register(reg, mask, value, NULL); -} - -/** - * @brief - * This function waits until the target register @reg contains the expected - * @param reg - register to wait on - * @param mask - mask to apply to register value - * @param value - expected value - * @param out_value - optional placeholder to hold registry value - * @return int - 0 if the register matches the desired condition, or -ETIMEDOUT. - */ -int c10::__intel_de_wait_for_register(u32 reg, - u32 mask, u32 value, - u32 *out_value) -{ - int out = __intel_wait_for_register(reg, mask, value, out_value); - if(out_value) { - DBG("Wait: reg: 0x%X, mask = 0x%X, value = 0x%X, out_value = 0x%X\n", reg, mask, value, *out_value); - } else { - DBG("Wait: reg: 0x%X, mask = 0x%X, value = 0x%X\n", reg, mask, value); - } - return out; -} - -/** - * @brief - * This function waits until the target register @reg contains the expected - * @param reg - register to wait on - * @param mask - mask to apply to register value - * @param value - expected value - * @param timeout - timeout in milliseconds - * @return int - 0 if the register matches the desired condition, or -ETIMEDOUT. - */ -int c10::intel_de_wait_for_register(u32 reg, u32 mask, u32 value, unsigned int timeout) -{ - return intel_wait_for_register(reg, mask, value, timeout); -} - -/** - * @brief - * This function performs read modify write operation. It clears certain bits - * from the register. - * @param reg - The address or identifier of the register to be modified. - * @param clear - A bitmask that specifies which bits to clear (set to 0) in the register. - * @param set - A bitmask that specifies which bits to set (set to 1) in the register. - * @return u32 - old value - */ -u32 c10::intel_uncore_rmw(u32 reg, u32 clear, u32 set) -{ - u32 old, val; - - old = READ_OFFSET_DWORD(reg); - val = (old & ~clear) | set; - WRITE_OFFSET_DWORD(reg, val); - return old; -} - -/** - * @brief - * This function performs read modify write operation by calling intel_uncore_rmw. - * @param reg - The address or identifier of the register to be modified. - * @param clear - A bitmask that specifies which bits to clear (set to 0) in the register. - * @param set - A bitmask that specifies which bits to set (set to 1) in the register. - * @return u32 - old value - */ -u32 c10::intel_de_rmw(u32 reg, u32 clear, u32 set) -{ - DBG("RMW: reg: 0x%X, clear = 0x%X, set = 0x%X\n", reg, clear, set); - return intel_uncore_rmw(reg, clear, set); -} - -/** - * @brief - * This function waits until the target register @reg is cleared. - * @param reg - register to wait on - * @param mask - mask to apply to register value - * @param timeout - timeout in milliseconds - * @return int - 0 if the register matches the desired condition, or -ETIMEDOUT. - */ -int c10::intel_de_wait_for_clear(int reg, u32 mask, unsigned int timeout) -{ - return intel_de_wait_for_register(reg, mask, 0, timeout); -} - -/** - * @brief - * This function reads the value from the register. - * @param reg - The address or identifier of the register to be read. - * @return u32 - - */ -u32 c10::intel_de_read(u32 reg) -{ - u32 val = READ_OFFSET_DWORD(reg); - DBG("%s: %d reg: 0x%X, value = 0x%X\n", __FUNCTION__, __LINE__, reg, val); - return val; -} - -/** - * @brief - * This function writes the value to the register. - * @param reg - The address or identifier of the register to be written. - * @param val - The value to be written to the register. - */ -void c10::intel_de_write(u32 reg, u32 val) -{ - DBG("Write: reg: 0x%X, val = 0x%X\n", reg, val); - WRITE_OFFSET_DWORD(reg, val); -} - -/** - * @brief - * This function clears response ready flag by calling other helper function. - * @param port - port number - * @param lane - lane number - */ -void c10::intel_clear_response_ready_flag(u32 port, int lane) -{ - intel_de_rmw(XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane), - 0, XELPDP_PORT_P2M_RESPONSE_READY | XELPDP_PORT_P2M_ERROR_SET); -} - -/** - * @brief - * This function resets the bus by writing desired values to registers. - * @param port - port - * @param lane - lane - */ -void c10::intel_cx0_bus_reset(int port, int lane) -{ - intel_de_write(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_RESET); - - if (intel_de_wait_for_clear(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_RESET, - XELPDP_MSGBUS_TIMEOUT_SLOW)) { - ERR("Failed to bring port %d to idle.\n", port); - return; - } - - intel_clear_response_ready_flag(port, lane); -} - -/** - * @brief - * This function waits for the acknowledgement from the device. - * @param port - port - * @param command - command to send - * @param lane - lane - * @param val - value - * @return int - */ -int c10::intel_cx0_wait_for_ack(u32 port, int command, int lane, u32 *val) -{ - if (__intel_de_wait_for_register( - XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane), - XELPDP_PORT_P2M_RESPONSE_READY, - XELPDP_PORT_P2M_RESPONSE_READY, - val)) { - ERR("Port %d Timeout waiting for message ACK. Status: 0x%x\n", port, *val); - return -ETIMEDOUT; - } - - if (*val & XELPDP_PORT_P2M_ERROR_SET) { - ERR("Port %d Error occurred during %s command. Status: 0x%x\n", port, - command == XELPDP_PORT_P2M_COMMAND_READ_ACK ? "read" : "write", *val); - intel_cx0_bus_reset(port, lane); - return -EINVAL; - } - - if (REG_FIELD_GET(XELPDP_PORT_P2M_COMMAND_TYPE_MASK, *val) != (u32) command) { - ERR("Port %d Not a %s response. MSGBUS Status: 0x%x.\n", port, - command == XELPDP_PORT_P2M_COMMAND_READ_ACK ? "read" : "write", *val); - intel_cx0_bus_reset(port, lane); - return -EINVAL; - } - - return 0; -} - -/** - * @brief - * This function reads the value from c10 registers using bus mechanism introduced in MTL/PTL. - * @param port - target port - * @param lane - target lane - * @param addr - address - * @return int - */ -int c10::__intel_cx0_read_once(u32 port, int lane, u16 addr) -{ - int ack; - u32 val; - - if (intel_de_wait_for_clear(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_PENDING, - XELPDP_MSGBUS_TIMEOUT_SLOW)) { - ERR("Port %d Timeout waiting for previous transaction to complete. Reset the bus and retry.\n", port); - intel_cx0_bus_reset(port, lane); - return -ETIMEDOUT; - } - - intel_de_write(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_PENDING | - XELPDP_PORT_M2P_COMMAND_READ | - XELPDP_PORT_M2P_ADDRESS(addr)); - - ack = intel_cx0_wait_for_ack(port, XELPDP_PORT_P2M_COMMAND_READ_ACK, lane, &val); - if (ack < 0) { - intel_cx0_bus_reset(port, lane); - return ack; - } - - intel_clear_response_ready_flag(port, lane); - - return REG_FIELD_GET(XELPDP_PORT_P2M_DATA_MASK, val); -} - -/** - * @brief - * This function reads the value from c10 registers. It's a wrapper call to __intel_cx0_read_once - * multiple times for accurate reading. - * @param port - target port - * @param lane - lane - * @param addr - address - * @return u8 - register value - */ -u8 c10::__intel_cx0_read(u32 port, int lane, u16 addr) -{ - int i, status; - - /* 3 tries is assumed to be enough to read successfully */ - for (i = 0; i < 3; i++) { - status = __intel_cx0_read_once(port, lane, addr); - - if (status >= 0) - return status; - } - - ERR("Port %d Read %04x failed after %d retries.\n", - port, addr, i); - - return 0; -} - -/** - * @brief - * This function writes the value to c10 registers using bus mechanism introduced in MTL/PTL. - * @param port - target port - * @param lane - lane - * @param addr - address - * @param data - value to write - * @param committed - flag to indicate if the write is committed or not - * @return int - 0 on success, negative value on failure - */ -int c10::__intel_cx0_write_once(u32 port, int lane, u16 addr, u8 data, bool committed) -{ - u32 val; - - if (intel_de_wait_for_clear(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_PENDING, - XELPDP_MSGBUS_TIMEOUT_SLOW)) { - ERR("Port %d Timeout waiting for previous transaction to complete. Resetting the bus.\n", port); - intel_cx0_bus_reset(port, lane); - return -ETIMEDOUT; - } - - intel_de_write(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_PENDING | - (committed ? XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED : - XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED) | - XELPDP_PORT_M2P_DATA(data) | - XELPDP_PORT_M2P_ADDRESS(addr)); - - if (intel_de_wait_for_clear(XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), - XELPDP_PORT_M2P_TRANSACTION_PENDING, - XELPDP_MSGBUS_TIMEOUT_SLOW)) { - ERR("Port %d Timeout waiting for write to complete. Resetting the bus.\n", port); - intel_cx0_bus_reset(port, lane); - return -ETIMEDOUT; - } - - if (committed) { - if (intel_cx0_wait_for_ack(port, XELPDP_PORT_P2M_COMMAND_WRITE_ACK, lane, &val) < 0) { - intel_cx0_bus_reset(port, lane); - return -EINVAL; - } - } else if ((intel_de_read(XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane)) & - XELPDP_PORT_P2M_ERROR_SET)) { - ERR("Port %d Error occurred during write command.\n", port); - intel_cx0_bus_reset(port, lane); - return -EINVAL; - } - - intel_clear_response_ready_flag(port, lane); - - return 0; -} - -/** - * @brief - * This function writes value to c10 registers. It's a wrapper call to __intel_cx0_write_once - * multiple times for accurate writing. - * @param port - target port - * @param lane - lane - * @param addr - address - * @param data - value to write - * @param committed - flag to indicate if the write is committed or not - * @return u8 - register value - */ -void c10::__intel_cx0_write(u32 port, int lane, u16 addr, u8 data, bool committed) -{ - int i, status; - - /* 3 tries is assumed to be enough to write successfully */ - for (i = 0; i < 3; i++) { - status = __intel_cx0_write_once(port, lane, addr, data, committed); - - if (status == 0) - return; - } - - ERR("Port %d Write %04x failed after %d retries.\n", port, addr, i); -} - -/** - * @brief - * This function performs read modify write operation by calling other helper function. - * @param port - port - * @param lane - lane number - * @param addr - address to write - * @param clear - bits to clear - * @param set - bits to set - * @param committed - flag to indicate if the write is committed or not - */ -void c10::__intel_cx0_rmw(u32 port, int lane, u16 addr, u8 clear, u8 set, bool committed) -{ - u8 old, val; - - old = __intel_cx0_read(port, lane, addr); - val = (old & ~clear) | set; - - if (val != old) - __intel_cx0_write(port, lane, addr, val, committed); -} - -/** - * @brief - * This function performs read modify write operation on multiple lanes based on provided mask. - * @param port - port number - * @param lane_mask - lane mask - * @param addr - address to read/write - * @param clear - bits to clear - * @param set - bits to set - * @param committed - flag to indicate if the write is committed or not - */ -void c10::intel_cx0_rmw(u32 port, u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed) -{ - u8 lane; - - for_each_cx0_lane_in_mask(lane_mask, lane) - __intel_cx0_rmw(port, lane, addr, clear, set, committed); -} - -/** - * @brief - * This function performs perform write on cx0 registers for multiple lanes. - * @param port - port number - * @param lane_mask - lane mask - * @param addr - address to read/write - * @param data - value to write - * @param committed - flag to indicate if the write is committed or not - */ -void c10::intel_cx0_write(u32 port, u8 lane_mask, u16 addr, u8 data, bool committed) -{ - int lane; - - for_each_cx0_lane_in_mask(lane_mask, lane) - __intel_cx0_write(port, lane, addr, data, committed); -} - +/* + * Copyright © 2024 Intel Corporation + * + * 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 (including the next + * paragraph) 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. + * + */ + +#include +#include +#include +#include +#include +#include "mmio.h" +#include "c10.h" + + /** + * @brief + * Constructor for c10 class + * @param ds + * @param pipe + */ +c10::c10(ddi_sel* ds, int _pipe) : phys(_pipe) +{ + TRACING(); + if (!ds) { + ERR("ddi_sel pointer is null\n"); + return; + } + + memset(&c10_reg, 0, sizeof(c10_reg)); + ds->phy_data = &c10_reg; + set_ds(ds); + set_init(true); + done = 1; + u32 port = ds->dpll_num - 1; + INFO("c10: port = %d\n", port); + phy_type = C10; +} + +/** + * @brief + * This is an inline function to multiply two 32-bit numbers + * and return the result as a 64-bit number. + * @param a - The first 32-bit number + * @param b - The second 32-bit number + */ +static inline unsigned long long mul_u32_u32(u32 a, u32 b) +{ + return (unsigned long long)a * b; +} + +/** + * @brief + * This function called by base phy class and calculates the PLL + * clock frequency for C10 PHY. + * @param None + * @return double - The calculated PLL clock + */ +double c10::calculate_pll_clock() +{ + TRACING(); + ddi_sel* ds = get_ds(); + if (!ds) { + ERR("Invalid ddi_sel\n"); + return 0; + } + + uint32_t mpll_frac_rem_15_0, mpll_frac_den_15_0, mpll_frac_quot_15_0, mpll_multiplier_11_0, ref_clk_mpll_div_2_0; + mpll_frac_den_15_0 = c10_reg.pll_state_orig[C10_PLL_REG_DEN_HIGH] << 8 | c10_reg.pll_state_orig[C10_PLL_REG_DEN_LOW]; + mpll_frac_quot_15_0 = c10_reg.pll_state_orig[C10_PLL_REG_QUOT_HIGH] << 8 | c10_reg.pll_state_orig[C10_PLL_REG_QUOT_LOW]; + mpll_frac_rem_15_0 = c10_reg.pll_state_orig[C10_PLL_REG_REM_HIGH] << 8 | c10_reg.pll_state_orig[C10_PLL_REG_REM_LOW]; + mpll_multiplier_11_0 = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, c10_reg.pll_state_orig[C10_PLL_REG_MULTIPLIER]) << 8 | + c10_reg.pll_state_orig[2]) / 2 + 16; + ref_clk_mpll_div_2_0 = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, c10_reg.pll_state_orig[C10_PLL_REG_TXCLKDIV]); + + const double ref_clk_freq_khz = REF_CLK_FREQ * 1000; + unsigned long long numenator = mul_u32_u32(ref_clk_freq_khz, (mpll_multiplier_11_0 << 16) + mpll_frac_quot_15_0) + + DIV_ROUND_CLOSEST(ref_clk_freq_khz * mpll_frac_rem_15_0, mpll_frac_den_15_0); + unsigned long long denominator = 10 << (ref_clk_mpll_div_2_0 + 16); + double pll_freq = numenator/(double)denominator; + return pll_freq; +} + +/** + * @brief + * This function calculates the feedback dividers for the PLL frequency + * and store the values in the c10_reg.pll_state_mod array + * @param pll_freq - The desired PLL frequency + * @return 0 - success, non zero - failure + */ +int c10::calculate_feedback_dividers(double pll_freq) +{ + TRACING(); + const double ref_clk_freq_khz = REF_CLK_FREQ * 1000; + uint32_t ref_clk_mpll_div_2_0 = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, c10_reg.pll_state_orig[C10_PLL_REG_TXCLKDIV]); + uint32_t mpll_frac_rem_15_0 = c10_reg.pll_state_orig[C10_PLL_REG_REM_HIGH] << 8 | c10_reg.pll_state_orig[C10_PLL_REG_REM_LOW]; + uint32_t mpll_frac_den_15_0 = c10_reg.pll_state_orig[C10_PLL_REG_DEN_HIGH] << 8 | c10_reg.pll_state_orig[C10_PLL_REG_DEN_LOW]; + uint32_t mpll_multiplier_11_0 = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, c10_reg.pll_state_orig[C10_PLL_REG_MULTIPLIER]) << 8 | c10_reg.pll_state_orig[2]) / 2 + 16; + unsigned long long temp = pll_freq * (10 << (ref_clk_mpll_div_2_0 + 16)); + temp -= DIV_ROUND_CLOSEST_ULL(ref_clk_freq_khz * mpll_frac_rem_15_0, mpll_frac_den_15_0); + temp /= (ref_clk_freq_khz); + temp -= ((unsigned long long)mpll_multiplier_11_0 << 16); + int new_mpll_frac_quot_15_0 = (int)temp; + c10_reg.pll_state_mod[C10_PLL_REG_QUOT_LOW] = new_mpll_frac_quot_15_0 & GENMASK(7, 0); + c10_reg.pll_state_mod[C10_PLL_REG_QUOT_HIGH] = new_mpll_frac_quot_15_0 >> 8; + + INFO("\tQuot Low: 0x%X [%d] -> 0x%X [%d]\n", + c10_reg.pll_state_orig[C10_PLL_REG_QUOT_LOW], c10_reg.pll_state_orig[C10_PLL_REG_QUOT_LOW], + (int)c10_reg.pll_state_mod[C10_PLL_REG_QUOT_LOW], (int)c10_reg.pll_state_mod[C10_PLL_REG_QUOT_LOW]); + INFO("\tQuot High: 0x%X [%d] -> 0x%X [%d]\n", + c10_reg.pll_state_orig[C10_PLL_REG_QUOT_HIGH], c10_reg.pll_state_orig[C10_PLL_REG_QUOT_HIGH], + (int)c10_reg.pll_state_mod[C10_PLL_REG_QUOT_HIGH], (int)c10_reg.pll_state_mod[C10_PLL_REG_QUOT_HIGH]); + + return 0; +} + +/** + * @brief + * This function prints the register value + * @param None + * @return void + */ +void c10::print_registers() +{ + TRACING(); + PRINT("Original Value ->\n"); + for (int i = 0; i < C10_PLL_REG_COUNT; i++) { + PRINT("c10: pll_state[%d] = 0x%X, %d\n", i, + static_cast(c10_reg.pll_state_orig[i]), + static_cast(c10_reg.pll_state_orig[i])); + } + PRINT("Updated Value ->\n"); + for (int i = 0; i < C10_PLL_REG_COUNT; i++) { + PRINT("c10: pll_state[%d] = 0x%X, %d\n", i, + static_cast(c10_reg.pll_state_mod[i]), + static_cast(c10_reg.pll_state_mod[i])); + } +} + +/** + * @brief + * This function reads the C10 Phy MMIO registers + * @param None + * @return void + */ +void c10::read_registers() +{ + TRACING(); + ddi_sel* ds = get_ds(); + if (!ds) { + ERR("Invalid ddi_sel\n"); + return; + } + + /* + According to BSpecs, MTL and PTL DDIs for C10 correspond to ports A and B. + Consequently, it is reasonable to deduce the port number from de_clk as clk-1. + However, this assumption might not hold for future platforms, where it + may be necessary to derive the port number directly from register values. + For guidance on this approach, refer to combo.cpp. + + BSpecs: + Port Usage Capability Alternate Names + DDI A | Port A | eDP, DP, HDMI DDIA, Port A + DDI B | Port B | eDP, DP, HDMI DDIB, Port B + */ + + // dpll_num holds port number. + // MTL has port number same as ddi_select. However PTL is different. + u32 port = ds->dpll_num - 1; + int pll_state[C10_PLL_REG_COUNT] = { 0 }; + for (int i = 0; i < C10_PLL_REG_COUNT; i++) { + pll_state[i] = intel_cx0_read(port, INTEL_CX0_LANE0, PHY_C10_VDR_PLL(i)); + c10_reg.pll_state_orig[i] = c10_reg.pll_state_mod[i] = pll_state[i]; + DBG("c10: pll_state[%d] = 0x%X, %d\n", i, pll_state[i], pll_state[i]); + } +} + +/** + * @brief + * This function programs the C10 Phy MMIO registers + * needed to move avsync period for a system. + * @param mod - This parameter tells the function whether to program the original + * values or the modified ones. + * - 0 = Original + * - 1 = modified + * @return 0 - success, non zero - failure + */ +int c10::program_mmio(int mod) +{ + TRACING(); + ddi_sel* ds = get_ds(); + + u32 port = ds->dpll_num - 1; + + intel_cx0_rmw(port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MSGBUS_ACCESS, + MB_WRITE_COMMITTED); + + /* Custom width needs to be programmed to 0 for both the phy lanes */ + intel_cx0_rmw(port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CUSTOM_WIDTH, + C10_VDR_CUSTOM_WIDTH_MASK, C10_VDR_CUSTOM_WIDTH_8_10, + MB_WRITE_COMMITTED); + intel_cx0_rmw(port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_UPDATE_CFG, + MB_WRITE_COMMITTED); + + + intel_cx0_write(port, INTEL_CX0_LANE0, PHY_C10_VDR_PLL(C10_PLL_REG_QUOT_LOW), + mod ? c10_reg.pll_state_mod[C10_PLL_REG_QUOT_LOW] : c10_reg.pll_state_orig[C10_PLL_REG_QUOT_LOW], + MB_WRITE_COMMITTED); + + intel_cx0_write(port, INTEL_CX0_LANE0, PHY_C10_VDR_PLL(C10_PLL_REG_QUOT_HIGH), + mod ? c10_reg.pll_state_mod[C10_PLL_REG_QUOT_HIGH] : c10_reg.pll_state_orig[C10_PLL_REG_QUOT_HIGH], + MB_WRITE_COMMITTED); + + intel_cx0_rmw(port, INTEL_CX0_LANE0, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MASTER_LANE | C10_VDR_CTRL_UPDATE_CFG, + MB_WRITE_COMMITTED); + + return 0; +} diff --git a/lib/c10.h b/lib/c10.h index e15c436..8219ed4 100644 --- a/lib/c10.h +++ b/lib/c10.h @@ -1,454 +1,57 @@ -/* - * Copyright © 2024 Intel Corporation - * - * 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 (including the next - * paragraph) 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. - * - */ - -#ifndef _C10_H -#define _C10_H - -#include "common.h" - -#define _DDI_CLK_VALFREQ_A 0x64030 -#define _DDI_CLK_VALFREQ_B 0x64130 -#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A 0x64040 -#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B 0x64140 -#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1 0x16F240 -#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2 0x16F440 -#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) - -enum port { - PORT_NONE = -1, - - PORT_A = 0, - PORT_B, - PORT_C, - PORT_D, - PORT_E, - PORT_F, - PORT_G, - PORT_H, - PORT_I, - - /* tgl+ */ - PORT_TC1 = PORT_D, - PORT_TC2, - PORT_TC3, - PORT_TC4, - PORT_TC5, - PORT_TC6, - - /* XE_LPD repositions D/E offsets and bitfields */ - PORT_D_XELPD = PORT_TC5, - PORT_E_XELPD, - - I915_MAX_PORTS -}; - - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#define U32_MAX ((u32)~0U) -/** - * is_power_of_2() - check if a value is a power of two - * @n: the value to check - * - * Determine whether some value is a power of two, where zero is - * *not* considered a power of two. - * Return: true if @n is a power of 2, otherwise false. - */ -static inline __attribute__((const)) -bool is_power_of_2(unsigned long n) -{ - return (n != 0 && ((n & (n - 1)) == 0)); -} -#define IS_POWER_OF_2 is_power_of_2 -#define __is_constexpr(x) \ - (sizeof(int) == sizeof(*(int *)(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) - -#define MB_WRITE_COMMITTED true -#define MB_WRITE_UNCOMMITTED false - -/* - * Like _PICK_EVEN(), but supports 2 ranges of evenly spaced address offsets. - * @__c_index corresponds to the index in which the second range starts to be - * used. Using math interval notation, the first range is used for indexes [ 0, - * @__c_index), while the second range is used for [ @__c_index, ... ). Example: - * - * #define _FOO_A 0xf000 - * #define _FOO_B 0xf004 - * #define _FOO_C 0xf008 - * #define _SUPER_FOO_A 0xa000 - * #define _SUPER_FOO_B 0xa100 - * #define FOO(x) _MMIO(_PICK_EVEN_2RANGES(x, 3, \ - * _FOO_A, _FOO_B, \ - * _SUPER_FOO_A, _SUPER_FOO_B)) - * - * This expands to: - * 0: 0xf000, - * 1: 0xf004, - * 2: 0xf008, - * 3: 0xa000, - * 4: 0xa100, - * 5: 0xa200, - * ... - */ -#define _PICK_EVEN_2RANGES(__index, __c_index, __a, __b, __c, __d) \ - (((__index) < (__c_index) ? _PICK_EVEN(__index, __a, __b) : \ - _PICK_EVEN((__index) - (__c_index), __c, __d))) - - -#define XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) (_PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2) + (lane) * 4) -#define PORT_CLOCK_CTL(phy_num) (_XELPDP_PORT_CLOCK_CTL_A + phy_num * 0x100) -#define PORT_M2P_MSGBUS_CTL(phy_num) (_XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A + phy_num * 0x100) -#define __bf_shf(x) (__builtin_ffsll(x) - 1) - -#define _XELPDP_PORT_MSGBUS_TIMER_LN0_A 0x640d8 -#define _XELPDP_PORT_MSGBUS_TIMER_LN0_B 0x641d8 -#define _XELPDP_PORT_MSGBUS_TIMER_LN0_USBC1 0x16f258 -#define _XELPDP_PORT_MSGBUS_TIMER_LN0_USBC2 0x16f458 -#define _XELPDP_PORT_MSGBUS_TIMER(port, lane) (_PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_MSGBUS_TIMER_LN0_A, \ - _XELPDP_PORT_MSGBUS_TIMER_LN0_B, \ - _XELPDP_PORT_MSGBUS_TIMER_LN0_USBC1, \ - _XELPDP_PORT_MSGBUS_TIMER_LN0_USBC2) + (lane) * 4) -#define XELPDP_PORT_MSGBUS_TIMER(port, lane) \ - _XELPDP_PORT_MSGBUS_TIMER(port, lane) -#define XELPDP_PORT_MSGBUS_TIMER_VAL_MASK REG_GENMASK(23, 0) -#define XELPDP_PORT_MSGBUS_TIMER_VAL REG_FIELD_PREP(XELPDP_PORT_MSGBUS_TIMER_VAL_MASK, 0xa000) - - -/** - * REG_FIELD_PREP() - Prepare a u32 bitfield value - * @__mask: shifted mask defining the field's length and position - * @__val: value to put in the field - * - * Local copy of FIELD_PREP() to generate an integer constant expression, force - * u32 and for consistency with REG_FIELD_GET(), REG_BIT() and REG_GENMASK(). - * - * @return: @__val masked and shifted into the field defined by @__mask. - */ -#define REG_FIELD_PREP(__mask, __val) \ - ((u32)(((typeof(__mask))(__val) << __bf_shf(__mask)) & (__mask))) - - -/** - * REG_FIELD_PREP8() - Prepare a u8 bitfield value - * @__mask: shifted mask defining the field's length and position - * @__val: value to put in the field - * - * Local copy of FIELD_PREP() to generate an integer constant expression, force - * u8 and for consistency with REG_FIELD_GET8(), REG_BIT8() and REG_GENMASK8(). - * - * @return: @__val masked and shifted into the field defined by @__mask. - */ -#define REG_FIELD_PREP8(__mask, __val) \ - ((u8)((((typeof(__mask))(__val) << __bf_shf(__mask)) & (__mask))) - - -/** - * FIELD_GET() - extract a bitfield element - * @_mask: shifted mask defining the field's length and position - * @_reg: value of entire bitfield - * - * FIELD_GET() extracts the field specified by @_mask from the - * bitfield passed in as @_reg by masking and shifting it down. - */ -#define FIELD_GET(_mask, _reg) \ - ({ \ - (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ - }) - - -/** - * REG_FIELD_GET() - Extract a u32 bitfield value - * @__mask: shifted mask defining the field's length and position - * @__val: value to extract the bitfield value from - * - * Local wrapper for FIELD_GET() to force u32 and for consistency with - * REG_FIELD_PREP(), REG_BIT() and REG_GENMASK(). - * - * @return: Masked and shifted value of the field defined by @__mask in @__val. - */ -#define REG_FIELD_GET(__mask, __val) ((u32)FIELD_GET(__mask, __val)) -#define REG_FIELD_GET8(__mask, __val) ((u8)FIELD_GET(__mask, __val)) - -#define XELPDP_PORT_M2P_COMMAND_TYPE_MASK GENMASK(30, 27) -#define XELPDP_PORT_M2P_ADDRESS_MASK GENMASK(11, 0) -#define XELPDP_PORT_M2P_COMMAND_READ REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3) -#define XELPDP_PORT_M2P_ADDRESS(val) REG_FIELD_PREP(XELPDP_PORT_M2P_ADDRESS_MASK, val) -#define INTEL_CX0_LANE0 BIT(0) -#define INTEL_CX0_LANE1 BIT(1) -#define INTEL_CX0_BOTH_LANES (INTEL_CX0_LANE1 | INTEL_CX0_LANE0) -#define XELPDP_SSC_ENABLE_PLLB BIT(0) -#define XELPDP_SSC_ENABLE_PLLA BIT(1) -#define XELPDP_LANE1_PHY_CLOCK_SELECT BIT(8) -#define XELPDP_FORWARD_CLOCK_UNGATE BIT(10) -#define XELPDP_PORT_M2P_TRANSACTION_RESET BIT(15) -#define XELPDP_PORT_P2M_ERROR_SET BIT(15) -#define XELPDP_PORT_REVERSAL BIT(16) -#define XELPDP_PORT_BUF_SOC_PHY_READY BIT(24) -#define XELPDP_PORT_P2M_RESPONSE_READY BIT(31) -#define XELPDP_PORT_M2P_TRANSACTION_PENDING BIT(31) -#define XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US 100 -#define XELPDP_PORT_RESET_START_TIMEOUT_US 5 -#define XELPDP_REFCLK_ENABLE_TIMEOUT_US 1 -#define CX0_P2_STATE_RESET 0x2 -#define _XELPDP_PORT_BUF_CTL1_LN0_A 0x64004 -#define _XELPDP_PORT_BUF_CTL1_LN0_B 0x64104 -#define _XELPDP_PORT_BUF_CTL1_LN0_USBC1 0x16F200 -#define _XELPDP_PORT_BUF_CTL1_LN0_USBC2 0x16F400 -#define XELPDP_PORT_BUF_CTL1(port) _PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_BUF_CTL1_LN0_A, \ - _XELPDP_PORT_BUF_CTL1_LN0_B, \ - _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ - _XELPDP_PORT_BUF_CTL1_LN0_USBC2) -#define XELPDP_PORT_BUF_CTL2(port) (_PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_BUF_CTL1_LN0_A, \ - _XELPDP_PORT_BUF_CTL1_LN0_B, \ - _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ - _XELPDP_PORT_BUF_CTL1_LN0_USBC2) + 4) -#define XELPDP_PORT_BUF_CTL3(port) (_PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_BUF_CTL1_LN0_A, \ - _XELPDP_PORT_BUF_CTL1_LN0_B, \ - _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ - _XELPDP_PORT_BUF_CTL1_LN0_USBC2) + 8) -#define XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane) (_PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \ - _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2) + (lane) * 4 + 8) -#define _XELPDP_PORT_CLOCK_CTL_A 0x640E0 -#define _XELPDP_PORT_CLOCK_CTL_B 0x641E0 -#define _XELPDP_PORT_CLOCK_CTL_USBC1 0x16F260 -#define _XELPDP_PORT_CLOCK_CTL_USBC2 0x16F460 -#define XELPDP_PORT_CLOCK_CTL(port) _PICK_EVEN_2RANGES(port, PORT_TC1, \ - _XELPDP_PORT_CLOCK_CTL_A, \ - _XELPDP_PORT_CLOCK_CTL_B, \ - _XELPDP_PORT_CLOCK_CTL_USBC1, \ - _XELPDP_PORT_CLOCK_CTL_USBC2) -#define XELPDP_MSGBUS_TIMEOUT_SLOW 1 -#define XELPDP_DDI_CLOCK_SELECT_MAXPCLK 0x8 -#define XELPDP_DDI_CLOCK_SELECT_DIV18CLK 0x9 -#define XELPDP_DDI_CLOCK_SELECT(val) REG_FIELD_PREP(XELPDP_DDI_CLOCK_SELECT_MASK, val) -#define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1) -#define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2) -#define XELPDP_LANE_POWERDOWN_UPDATE(lane) _PICK(lane, BIT(25), BIT(24)) -#define XELPDP_LANE_PHY_CURRENT_STATUS(lane) _PICK(lane, BIT(29), BIT(28)) -#define XELPDP_LANE_PIPE_RESET(lane) _PICK(lane, BIT(31), BIT(30)) -#define XELPDP_LANE_PCLK_REFCLK_REQUEST(lane) BIT(29 - ((lane) * 4)) -#define XELPDP_LANE_PCLK_REFCLK_ACK(lane) BIT(28 - ((lane) * 4)) -#define XELPDP_LANE_POWERDOWN_NEW_STATE_MASK GENMASK(3, 0) -#define XELPDP_POWER_STATE_ACTIVE_MASK GENMASK(3, 0) -#define XELPDP_POWER_STATE_READY_MASK GENMASK(7, 4) -#define XELPDP_PLL_LANE_STAGGERING_DELAY_MASK GENMASK(15, 8) -#define XELPDP_DDI_CLOCK_SELECT_MASK GENMASK(15, 12) -#define _XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK GENMASK(19, 16) -#define XELPDP_PORT_M2P_DATA_MASK GENMASK(23, 16) -#define XELPDP_PORT_P2M_DATA_MASK GENMASK(23, 16) -#define _XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK GENMASK(23, 20) -#define XELPDP_PORT_P2M_COMMAND_TYPE_MASK GENMASK(30, 27) -#define _XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(_XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val) -#define _XELPDP_LANE1_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(_XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK, val) -#define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val) -#define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val) -#define XELPDP_LANE_POWERDOWN_NEW_STATE(lane, val) _PICK(lane, \ - _XELPDP_LANE0_POWERDOWN_NEW_STATE(val), \ - _XELPDP_LANE1_POWERDOWN_NEW_STATE(val)) -#define XELPDP_POWER_STATE_READY(val) REG_FIELD_PREP(XELPDP_POWER_STATE_READY_MASK, val) -#define CX0_P0_STATE_ACTIVE 0x0 -#define CX0_P2_STATE_READY 0x2 -#define CX0_P2PG_STATE_DISABLE 0x9 -#define CX0_P4PG_STATE_DISABLE 0xC -#define CX0_P2_STATE_RESET 0x2 -#define XELPDP_PORT_RESET_END_TIMEOUT 15 -#define XELPDP_PORT_P2M_COMMAND_READ_ACK 0x4 -#define XELPDP_PORT_P2M_COMMAND_WRITE_ACK 0x5 -#define XELPDP_PORT_M2P_DATA(val) REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val) - - -/* C10 Vendor Registers */ -#define PHY_C10_VDR_PLL(idx) (0xC00 + (idx)) -#define C10_PLL0_FRACEN REG_BIT8(4) -#define C10_PLL3_MULTIPLIERH_MASK REG_GENMASK8(3, 0) -#define C10_PLL15_TXCLKDIV_MASK REG_GENMASK8(2, 0) -#define C10_PLL15_HDMIDIV_MASK REG_GENMASK8(5, 3) -#define PHY_C10_VDR_CMN(idx) (0xC20 + (idx)) -#define C10_CMN0_REF_RANGE REG_FIELD_PREP(REG_GENMASK(4, 0), 1) -#define C10_CMN0_REF_CLK_MPLLB_DIV REG_FIELD_PREP(REG_GENMASK(7, 5), 1) -#define C10_CMN3_TXVBOOST_MASK REG_GENMASK8(7, 5) -#define C10_CMN3_TXVBOOST(val) REG_FIELD_PREP8(C10_CMN3_TXVBOOST_MASK, val) -#define PHY_C10_VDR_TX(idx) (0xC30 + (idx)) -#define C10_TX0_TX_MPLLB_SEL REG_BIT(4) -#define C10_TX1_TERMCTL_MASK REG_GENMASK8(7, 5) -#define C10_TX1_TERMCTL(val) REG_FIELD_PREP8(C10_TX1_TERMCTL_MASK, val) -#define PHY_C10_VDR_CONTROL(idx) (0xC70 + (idx) - 1) -#define C10_VDR_CTRL_MSGBUS_ACCESS REG_BIT8(2) -#define C10_VDR_CTRL_MASTER_LANE REG_BIT8(1) -#define C10_VDR_CTRL_UPDATE_CFG REG_BIT8(0) -#define PHY_C10_VDR_CUSTOM_WIDTH 0xD02 -#define C10_VDR_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0) -#define C10_VDR_CUSTOM_WIDTH_8_10 REG_FIELD_PREP(C10_VDR_CUSTOM_WIDTH_MASK, 0) -#define PHY_C10_VDR_OVRD 0xD71 -#define PHY_C10_VDR_OVRD_TX1 REG_BIT8(0) -#define PHY_C10_VDR_OVRD_TX2 REG_BIT8(2) -#define PHY_C10_VDR_PRE_OVRD_TX1 0xD80 -#define C10_PHY_OVRD_LEVEL_MASK REG_GENMASK8(5, 0) -#define C10_PHY_OVRD_LEVEL(val) REG_FIELD_PREP8(C10_PHY_OVRD_LEVEL_MASK, val) -#define PHY_CX0_VDROVRD_CTL(lane, tx, control) \ - (PHY_C10_VDR_PRE_OVRD_TX1 + \ - ((lane) ^ (tx)) * 0x10 + (control)) -#define TRANS_DDI_MODE_SELECT_MASK (7 << 24) -#define TRANS_DDI_MODE_SELECT_HDMI (0 << 24) -#define TRANS_DDI_MODE_SELECT_DVI (1 << 24) -#define TRANS_DDI_MODE_SELECT_DP_SST (2 << 24) -#define TRANS_DDI_MODE_SELECT_DP_MST (3 << 24) - - -/* these are outputs from the chip - integrated only - external chips are via DVO or SDVO output */ -enum intel_output_type { - INTEL_OUTPUT_UNUSED = 0, - INTEL_OUTPUT_ANALOG = 1, - INTEL_OUTPUT_DVO = 2, - INTEL_OUTPUT_SDVO = 3, - INTEL_OUTPUT_LVDS = 4, - INTEL_OUTPUT_TVOUT = 5, - INTEL_OUTPUT_HDMI = 6, - INTEL_OUTPUT_DP = 7, - INTEL_OUTPUT_EDP = 8, - INTEL_OUTPUT_DSI = 9, - INTEL_OUTPUT_DDI = 10, - INTEL_OUTPUT_DP_MST = 11, -}; - -/* - * Add the pseudo keyword 'fallthrough' so case statement blocks - * must end with any of these keywords: - * break; - * fallthrough; - * continue; - * goto