From patchwork Tue May 27 09:09:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [1/4] tools/displaytop: Add Display Top - a terminal-based display pipeline monitor From: Adith Narein T X-Patchwork-Id: 655763 Message-Id: <20250527090938.1765854-2-adith.narein.t@intel.com> To: intel-gfx-trybot@lists.freedesktop.org Cc: chaitanya.kumar.borah@intel.com, adith.narein.t@intel.com Date: Tue, 27 May 2025 14:39:31 +0530 This patch introduces `displaytop`, a terminal-based tool designed for monitoring and debugging the display pipeline in real time. The initial feature focuses on providing detailed display configuration information, including: - CRTCs - Connectors - Encoders - Planes - Framebuffers These components are presented in an organized format using a terminal UI built with `ncurses`, giving developers and validation engineers a fast and accessible way to inspect the current state of the display system. Signed-off-by: Adith Narein T --- tools/displaytop/README.md | 32 + tools/displaytop/include/data.h | 37 + tools/displaytop/include/display.h | 46 + tools/displaytop/include/log.h | 45 + tools/displaytop/include/node.h | 126 +++ tools/displaytop/include/populate.h | 37 + tools/displaytop/include/utils.h | 115 +++ tools/displaytop/meson.build | 101 ++ tools/displaytop/src/data.c | 29 + tools/displaytop/src/display.c | 482 ++++++++++ tools/displaytop/src/display_configuration.c | 877 ++++++++++++++++++ tools/displaytop/src/display_dump.c | 215 +++++ tools/displaytop/src/display_search_bar.c | 282 ++++++ tools/displaytop/src/display_summary.c | 308 ++++++ tools/displaytop/src/log.c | 84 ++ tools/displaytop/src/main.c | 63 ++ tools/displaytop/src/node.c | 140 +++ tools/displaytop/src/populate.c | 34 + .../displaytop/src/populate_display_config.c | 295 ++++++ tools/displaytop/src/utils.c | 102 ++ tools/displaytop/src/utils_cli.c | 70 ++ tools/displaytop/src/utils_display.c | 195 ++++ tools/displaytop/src/utils_driver.c | 189 ++++ tools/displaytop/src/utils_drm.c | 585 ++++++++++++ tools/displaytop/src/utils_search.c | 49 + tools/meson.build | 1 + 26 files changed, 4539 insertions(+) create mode 100644 tools/displaytop/README.md create mode 100644 tools/displaytop/include/data.h create mode 100644 tools/displaytop/include/display.h create mode 100644 tools/displaytop/include/log.h create mode 100644 tools/displaytop/include/node.h create mode 100644 tools/displaytop/include/populate.h create mode 100644 tools/displaytop/include/utils.h create mode 100644 tools/displaytop/meson.build create mode 100644 tools/displaytop/src/data.c create mode 100644 tools/displaytop/src/display.c create mode 100644 tools/displaytop/src/display_configuration.c create mode 100644 tools/displaytop/src/display_dump.c create mode 100644 tools/displaytop/src/display_search_bar.c create mode 100644 tools/displaytop/src/display_summary.c create mode 100644 tools/displaytop/src/log.c create mode 100644 tools/displaytop/src/main.c create mode 100644 tools/displaytop/src/node.c create mode 100644 tools/displaytop/src/populate.c create mode 100644 tools/displaytop/src/populate_display_config.c create mode 100644 tools/displaytop/src/utils.c create mode 100644 tools/displaytop/src/utils_cli.c create mode 100644 tools/displaytop/src/utils_display.c create mode 100644 tools/displaytop/src/utils_driver.c create mode 100644 tools/displaytop/src/utils_drm.c create mode 100644 tools/displaytop/src/utils_search.c diff --git a/tools/displaytop/README.md b/tools/displaytop/README.md new file mode 100644 index 000000000..52128d34e --- /dev/null +++ b/tools/displaytop/README.md @@ -0,0 +1,32 @@ +# **DisplayTop** + +A terminal-based tool for monitoring and debugging the display pipeline. + +## **Features** + +- Terminal UI using `ncurses` +- Dump feature for all the menus +- Real-time display Configuration + +--- + +## **Quick Start** + +Build igt-gpu-tools as normal and then go to `build/tools/displaytop` and run sudo `./displaytop`. +Note: Running the tool without sudo will result in loss of some features. + +--- + +### Compatibility with Meson build + +Alterantively display top can also be built with meson setup +and run from the build/tools/displaytop + +--- + +## Logs & Dumps + +The log for the display top tool and the dumps initiated by the user are +stored in the `/tmp` folder as `displaytop.log` and `/displaytop_dump` respectively. + +--- \ No newline at end of file diff --git a/tools/displaytop/include/data.h b/tools/displaytop/include/data.h new file mode 100644 index 000000000..8666a1de0 --- /dev/null +++ b/tools/displaytop/include/data.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2025 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 DATA_H +#define DATA_H + +#include "node.h" + +extern Node *root; /* shared thorugh out the program */ + +#define DUMP_DIR "/tmp/displaytop_dump" + +#define DRM_DIR "/dev/dri/" +#define DRM_PRIMARY_PREFIX "card" +#define DRM_RENDER_PREFIX "renderD" + +#endif \ No newline at end of file diff --git a/tools/displaytop/include/display.h b/tools/displaytop/include/display.h new file mode 100644 index 000000000..db77ef785 --- /dev/null +++ b/tools/displaytop/include/display.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2025 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 DISPLAY_H +#define DISPLAY_H + +#include "log.h" +#include "node.h" +#include "utils.h" + +void display_win(WINDOW *win, Node *node); + +Node *display_search_bar(Node *head); +void display_dump_menu(Node *head); + +void display_crtc(WINDOW *pad, Node *node, int *content_line); +void display_plane(WINDOW *pad, Node *node, int *content_line); +void display_encoder(WINDOW *pad, Node *node, int *content_line); +void display_formats(WINDOW *pad, Node *node, int *content_line); +void display_connector(WINDOW *pad, Node *node, int *content_line); +void display_informats(WINDOW *pad, Node *node, int *content_line); +void display_framebuffer(WINDOW *pad, Node *node, int *content_line); + +void display_summary(WINDOW *pad, Node *node, int *content_line); + +#endif \ No newline at end of file diff --git a/tools/displaytop/include/log.h b/tools/displaytop/include/log.h new file mode 100644 index 000000000..bfc37e543 --- /dev/null +++ b/tools/displaytop/include/log.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2025 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 LOG_H +#define LOG_H + +#include +#include +#include + +#define LOG_FILE "/tmp/displaytop.log" + +typedef enum +{ + LOG_INFO, + LOG_WARNING, + LOG_ERROR +} LogLevel; + +void init_log_system(void); +void close_log_system(void); + +void log_message(int level, const char *format, ...) __attribute__((format(gnu_printf, 2, 3))); + +#endif diff --git a/tools/displaytop/include/node.h b/tools/displaytop/include/node.h new file mode 100644 index 000000000..e0ab2c5cd --- /dev/null +++ b/tools/displaytop/include/node.h @@ -0,0 +1,126 @@ +/* + * Copyright © 2025 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 NODE_H +#define NODE_H + +#include +#include +#include + +/** + * @struct Node + * @brief Represents a node in a tree structure. + * + * This structure is used to represent a node in a tree, where each node can have a name, + * a display function, a parent node, and an array of child nodes. + * + * @var Node::name + * A character array to store the name of the node. The name can be up to 49 characters long, + * with the 50th character reserved for the null terminator. + * + * @var Node::display_function + * A pointer to a function that takes a WINDOW pointer and a variable number of arguments, + * and returns an integer. This function is used to display the node. + * + * @var Node::parent + * A pointer to the parent node. If the node is the root, this will be NULL. + * + * @var Node::children + * A pointer to an array of child nodes. If the node has no children, this will be NULL. + * + * @var Node::children_size + * An integer representing the number of children the node has. + */ +typedef struct Node +{ + char name[50]; + void (*display_function)(WINDOW *, struct Node *, int *); + struct Node *parent; + struct Node *children; + int children_size; +} Node; + +/** + * @brief Creates a new node with the given name, display function, and parent. + * + * This function allocates memory for a new node, initializes its name, display function, + * parent, and sets its children to NULL and children_size to 0. + * + * @param name The name of the node. + * @param display_function A pointer to the display function associated with the node. + * @param parent A pointer to the parent node. + * @return A pointer to the newly created node. + */ +Node *create_node(const char *name, void (*display_function)(WINDOW *, Node *, int *), Node *parent); + +/** + * @brief Adds a child node to the specified parent node. + * + * This function reallocates memory for the parent's children array to accommodate the new child, + * assigns the child to the next available position in the array, and increments the children_size. + * + * @param parent A pointer to the parent node. + * @param child A pointer to the child node to be added. + */ +void add_child(Node *parent, Node *child); + +/** + * @brief Frees the memory allocated for the tree recursively. + * + * This function recursively frees the memory allocated for the children of the given root node, + * then frees the memory allocated for the children array and the root node itself. + * + * @param root A pointer to the root node of the tree. + */ +void free_tree(Node *root); + +/** + * @brief Prints the path from the given node. + * + * This function takes a pointer to a Node structure and prints the path + * associated with that node. The exact format and details of the path + * are dependent on the implementation of the Node structure and the + * printPath function. + * + * @param node A pointer to the Node whose path is to be printed. + */ +void print_path(Node *node); + +/** + * @brief Gets the path from the given node. + * + * This function takes a pointer to a Node structure and returns the path + * associated with that node. The exact format and details of the path + * are dependent on the implementation of the Node structure and the + * getPath function. + * + * @param node A pointer to the Node whose path is to be returned. + * @return A string representing the path of the node. + */ +char *get_path(Node *node); + +void count_nodes(Node *node, int *count); + + +#endif diff --git a/tools/displaytop/include/populate.h b/tools/displaytop/include/populate.h new file mode 100644 index 000000000..5a5241355 --- /dev/null +++ b/tools/displaytop/include/populate.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2025 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 POPULATE_H +#define POPULATE_H + +#include "node.h" +#include "data.h" +#include "utils.h" +#include "utils.h" +#include "display.h" + +void populate_data(void); + +void initialize_display_config(void); + +#endif \ No newline at end of file diff --git a/tools/displaytop/include/utils.h b/tools/displaytop/include/utils.h new file mode 100644 index 000000000..eef5df5f3 --- /dev/null +++ b/tools/displaytop/include/utils.h @@ -0,0 +1,115 @@ +/* + * Copyright © 2025 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 UTILS_H +#define UTILS_H +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "node.h" +#include "data.h" +#include "display.h" + +extern int drm_fd; + +/* utils_cli.c */ +void handle_cli_args(int argc, char *argv[]); + +/* utils_driver.c*/ +void check_and_load_driver(void); + +/* utils_display.c */ +void set_string(char *dest, const char *src, size_t size); +int check_size_change(WINDOW *win, int *height, int *width); + +void single_file_dump(Node *node, const char *filePath, bool single); +void search_nodes(Node *root, const char *searchInput, Node *results); + +void print_bold_text(WINDOW *win, int line, int col, const char *text, ...) + __attribute__((format(printf, 4, 5))); + +void print_dim_text(WINDOW *win, int line, int col, const char *text, ...) + __attribute__((format(printf, 4, 5))); + +void print_red_text(WINDOW *win, int line, int col, const char *text, ...) + __attribute__((format(printf, 4, 5))); + +void print_green_text(WINDOW *win, int line, int col, const char *text, ...) + __attribute__((format(printf, 4, 5))); + +void draw_header(WINDOW *win, int winWidth, const char *title, const char *helper); +void render_progress_bar(WINDOW *win, int current, int total); + +void print_wrapped_text(WINDOW *pad, int *line, int start, int size, const char *text, bool enclose_with_pipe); + +/* utils.c*/ +void ensure_dump_directory(void); +void strip_whitespace(char *str); +char *read_file(const char *filename); + +char *get_file_path(const char *filename); + +/* utils_drm.c */ +char *find_drm_device(bool primary); +void open_primary_drm_device(void); +void close_primary_drm_device(void); + +const char *get_drm_object_type_name(uint32_t object_type); +const char *get_format_str(uint32_t format); +const char *get_basic_modifier_str(uint64_t modifier); +const char *get_connector_type_name(uint32_t connector_type); +const char *get_encoder_type_name(uint32_t encoder_type); + +#endif \ No newline at end of file diff --git a/tools/displaytop/meson.build b/tools/displaytop/meson.build new file mode 100644 index 000000000..45720165a --- /dev/null +++ b/tools/displaytop/meson.build @@ -0,0 +1,101 @@ +# Meson build definition for DisplayTop tool + +# This block of code must be UNCOMMENTED when building displaytop as a standalone project +# project('displaytop', 'c', +# version: '1.0', +# default_options: ['warning_level=2', 'c_std=c11'] +# ) +# cc = meson.get_compiler('c') + +# Compiler flags +cflags = [ + '-Wall', '-Wextra', + '-Iinclude', '-I/usr/include/libdrm', '-I/usr/local/include', + '-DMESON_SOURCE_ROOT="' + meson.source_root() + '"' +] + +# Include directories +inc_dirs = include_directories( + 'include', + '/usr/include/libdrm', + '/usr/local/include' +) + +libdrm_dep = dependency('libdrm', required: true) +ncurses_dep = dependency('ncurses', required: false) +menu_dep = dependency('menu', required: false) +form_dep = dependency('form', required: false) +panel_dep = dependency('panel', required: false) +cjson_dep = cc.find_library('cjson', required: false) + +have_displaytop = ncurses_dep.found() and menu_dep.found() and form_dep.found() and panel_dep.found() and cjson_dep.found() + +if have_displaytop + message('All dependencies found. Building displaytop.') + + deps = [ + libdrm_dep, + ncurses_dep, + menu_dep, + form_dep, + panel_dep, + ] + # Source files + source = files( + 'src/data.c', + 'src/display.c', + 'src/display_configuration.c', + 'src/display_dump.c', + 'src/display_search_bar.c', + 'src/display_summary.c', + 'src/log.c', + 'src/main.c', + 'src/node.c', + 'src/populate.c', + 'src/populate_display_config.c', + 'src/utils.c', + 'src/utils_cli.c', + 'src/utils_display.c', + 'src/utils_driver.c', + 'src/utils_drm.c', + 'src/utils_search.c', + ) + + if meson.is_subproject() + # SUBPROJECT MODE + # This block is used when the project is included as a subproject + # inside a larger Meson-based project (via subproject('displaytop')) + + # Define a static library + displaytop_lib = static_library( + 'displaytop', source, + c_args: cflags, + include_directories: inc_dirs, + dependencies: deps + [cjson_dep], + ) + + # Declare the dependency so parent projects can link to this + displaytop_dep = declare_dependency( + link_with: displaytop_lib, + include_directories: inc_dirs, + dependencies: deps + [cjson_dep], + ) + + else + # STANDALONE MODE + # This block builds and installs the executable when run independently. + # If you’re including this as a subproject in another Meson build, + # COMMENT OUT the `project()` line at the top and this `else` block. + + executable( + 'displaytop', source, + install: true, + c_args: cflags, + include_directories: inc_dirs, + dependencies: deps + [cjson_dep], + ) + endif + +else + message('Skipping displaytop: missing required optional dependencies.') +endif \ No newline at end of file diff --git a/tools/displaytop/src/data.c b/tools/displaytop/src/data.c new file mode 100644 index 000000000..3aed6292b --- /dev/null +++ b/tools/displaytop/src/data.c @@ -0,0 +1,29 @@ +/* + * Copyright © 2025 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 "data.h" +#include "display.h" + +/* This file holds all the global variables that are being used in the tool */ + +Node *root; \ No newline at end of file diff --git a/tools/displaytop/src/display.c b/tools/displaytop/src/display.c new file mode 100644 index 000000000..a40fbf8af --- /dev/null +++ b/tools/displaytop/src/display.c @@ -0,0 +1,482 @@ +/* + * Copyright © 2025 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 "display.h" +#include "data.h" +#include "utils.h" + +#define DRM_DEVICE "/dev/dri/card0" + +static int display_exit_menu(void) +{ + int height = 8, width = 40; + int starty = (LINES - height) / 2; + int startx = (COLS - width) / 2; + int ch; + const char *message = "Are you sure you want to quit?"; + + WINDOW *overlay = newwin(LINES, COLS, 0, 0); + WINDOW *confirm_win = newwin(height, width, starty, startx); + + wbkgd(overlay, COLOR_PAIR(0)); + wclear(overlay); + wrefresh(overlay); + + wbkgd(confirm_win, COLOR_PAIR(2)); + box(confirm_win, 0, 0); + + mvwprintw(confirm_win, 2, (width - strlen(message)) / 2, "%s", message); + + wattron(confirm_win, A_REVERSE); + mvwprintw(confirm_win, 4, width / 2 - 5, "[Y]es"); + mvwprintw(confirm_win, 4, width / 2 + 2, "[N]o"); + wattroff(confirm_win, A_REVERSE); + + wrefresh(confirm_win); + + while ((ch = wgetch(confirm_win))) + { + if (ch == 'y' || ch == 'Y') + { + delwin(confirm_win); + delwin(overlay); + return 1; + } + else if (ch == 'n' || ch == 'N' || ch == 27) + { + delwin(confirm_win); + delwin(overlay); + return 0; + } + } + + delwin(confirm_win); + delwin(overlay); + return 0; +} + +static void display_help_menu(void) +{ + int row = 2; + const char *title = " Help "; + WINDOW *help_win = newwin(LINES, COLS, 0, 0); + wbkgd(help_win, COLOR_PAIR(6)); + wclear(help_win); + box(help_win, 0, 0); + + mvwprintw(help_win, 0, (COLS - strlen(title)) / 2, "%s", title); + + print_bold_text(help_win, row++, 4, "Key Bindings:"); + row++; + print_bold_text(help_win, row, 6, "[Q]"); + mvwprintw(help_win, row++, 18, "- Quit the application with a confirmation prompt."); + print_bold_text(help_win, row, 6, "[H]"); + mvwprintw(help_win, row++, 18, "- Open the help menu to view key bindings and descriptions."); + print_bold_text(help_win, row, 6, "[UP/DOWN]"); + mvwprintw(help_win, row++, 18, "- Navigate through menu items or scroll content."); + print_bold_text(help_win, row, 6, "[TAB]"); + mvwprintw(help_win, row++, 18, "- Toggle focus between the menu and display content."); + print_bold_text(help_win, row, 6, "[ENTER]"); + mvwprintw(help_win, row++, 18, "- Select a menu item or confirm an action."); + print_bold_text(help_win, row, 6, "[ESC]"); + mvwprintw(help_win, row++, 18, "- Go back, cancel an action, or exit the application."); + print_bold_text(help_win, row, 6, "[S]"); + mvwprintw(help_win, row++, 18, "- Search for specific items in the menu."); + print_bold_text(help_win, row, 6, "[P]"); + mvwprintw(help_win, row++, 18, "- Save the current page's content to a file."); + print_bold_text(help_win, row, 6, "[D]"); + mvwprintw(help_win, row++, 18, "- Save all nested content starting from the current menu."); + print_bold_text(help_win, row, 6, "[R]"); + mvwprintw(help_win, row++, 18, "- Refresh the display to show recent updates."); + + row++; + mvwprintw(help_win, row++, 4, "Press any key to return..."); + + wrefresh(help_win); + wgetch(help_win); + + delwin(help_win); +} + +void display_win(WINDOW *win, Node *node) +{ + Node *head = node; + + int is_running; + int highlighted_index = 0; + + bool focus_on_menu = true; + bool refresh = true; + + int win_height, win_width; + + int content_height; + int content_y_start; + + int mpx; + int menu_pad_pos; + int menu_pad_width; + int menu_pad_height; + WINDOW *menu_pad = NULL; + + int dpx; + int display_pad_pos; + int display_pad_width; + int display_pad_height; + WINDOW *display_pad = NULL; + + const char *path = get_path(head); + const char *status_text = "[TAB] Switch Focus [S] Search [ESC] Back [R] Refresh [H] Help"; + + int count; + + int ch; + + while (1) + { + wclear(win); + box(win, 0, 0); + wbkgd(win, COLOR_PAIR(2)); + getmaxyx(win, win_height, win_width); + + if (head->display_function == NULL && head->children_size == 0) + { + mvwprintw(win, 4, 2, "It's so Empty at %s :(", head->name); + mvwprintw(win, 8, 2, "Press 'e' to go back."); + wrefresh(win); + } + + is_running = 1; + + log_message(LOG_INFO, "Displaying %s", head->name); + + while (is_running) + { + if (check_size_change(win, &win_height, &win_width) == 1 || refresh) + { + log_message(LOG_INFO, "Window size changed. Refreshing display."); + refresh = false; + + content_height = win_height - 6; + content_y_start = 4; + + mpx = 1; + menu_pad_pos = 0; + menu_pad_height = head->children_size > content_height ? head->children_size + 5 : content_height; + menu_pad_width = (int)((win_width - 3) * 0.3); + + dpx = menu_pad_width + 2; + display_pad_pos = 0; + display_pad_height = 1000; + display_pad_width = win_width - 3 - menu_pad_width; + + if (menu_pad != NULL) + { + delwin(menu_pad); + menu_pad = NULL; + } + + menu_pad = newpad(menu_pad_height, menu_pad_width); + prefresh(menu_pad, menu_pad_pos, 0, content_y_start, 1, content_height + content_y_start - 1, menu_pad_width + 1); + + if (head->display_function != NULL) + { + if (display_pad != NULL) + { + delwin(display_pad); + display_pad = NULL; + } + + count = display_pad_height; + display_pad = newpad(display_pad_height, display_pad_width); + + head->display_function(display_pad, head, &count); + + if (count == 0) + { + log_message(LOG_INFO, "Exiting %s", head->name); + if (head != NULL && head->parent != NULL) + { + head = head->parent; + focus_on_menu = true; + highlighted_index = 0; + display_pad_pos = 0; + is_running = 0; + } + else + { + return; + } + continue; + } + + if (count < display_pad_height) + display_pad_height = count + 5; + + prefresh(display_pad, display_pad_pos, 0, content_y_start, dpx, content_height + content_y_start - 1, 2 + menu_pad_width + display_pad_width); + } + + wclear(win); + box(win, 0, 0); + + path = get_path(head); + + draw_header(win, win_width, path, status_text); + + mvwvline(win, 3, menu_pad_width + 1, ACS_VLINE, win_height - 4); + wrefresh(win); + } + + if (head->display_function != NULL) + { + if (display_pad_pos > 0) + print_bold_text(win, 3, menu_pad_width + 2, "..."); + else + mvwprintw(win, 3, menu_pad_width + 2, " "); + + if (display_pad_pos < display_pad_height - content_height) + print_bold_text(win, content_height + content_y_start, menu_pad_width + 2, "..."); + else + mvwprintw(win, content_height + content_y_start, menu_pad_width + 2, " "); + + print_dim_text(win, 3, win_width - 15, "%d lines", display_pad_height); + prefresh(display_pad, display_pad_pos, 0, content_y_start, dpx, content_height + content_y_start - 1, 2 + menu_pad_width + display_pad_width); + } + + if (focus_on_menu) + { + print_bold_text(win, 3, win_width - 5, " "); + wattron(win, A_REVERSE); + print_bold_text(win, 3, menu_pad_width - 3, "***"); + wattroff(win, A_REVERSE); + } + else + { + print_bold_text(win, 3, menu_pad_width - 3, " "); + wattron(win, A_REVERSE); + print_bold_text(win, 3, win_width - 5, "***"); + wattroff(win, A_REVERSE); + } + + if (head->children_size > 0) + { + if (head->display_function == NULL) + focus_on_menu = true; + print_dim_text(win, 3, menu_pad_width - 15, "%d Menus", head->children_size); + for (int i = 0; i < head->children_size; ++i) + { + if (highlighted_index == i && focus_on_menu) + wattron(menu_pad, A_REVERSE); + mvwprintw(menu_pad, i, 1, "%d. %s", i + 1, head->children[i].name); + wattroff(menu_pad, A_REVERSE); + } + } + + if (highlighted_index == head->children_size && focus_on_menu) + wattron(menu_pad, A_REVERSE); + + if (head->parent != NULL) + print_dim_text(menu_pad, head->children_size, 1, "Go Back"); + else + print_dim_text(menu_pad, head->children_size, 1, "Exit"); + + wattroff(menu_pad, A_REVERSE); + + prefresh(menu_pad, menu_pad_pos, 0, content_y_start, mpx, content_height + content_y_start - 1, menu_pad_width + 1); + + ch = wgetch(win); + switch (ch) + { + case KEY_UP: + if (!focus_on_menu && display_pad_pos > 0) + display_pad_pos--; + + else if (focus_on_menu) + { + if (highlighted_index == 0) + { + highlighted_index = head->children_size; + menu_pad_pos = menu_pad_height - content_height; + } + else + { + highlighted_index--; + + if (highlighted_index > menu_pad_height - content_height) + { + menu_pad_pos = menu_pad_height - content_height; + } + else + { + menu_pad_pos--; + } + } + } + break; + + case KEY_DOWN: + if (!focus_on_menu && display_pad_pos < display_pad_height - content_height) + display_pad_pos++; + + else if (focus_on_menu) + { + if (highlighted_index == head->children_size) + { + highlighted_index = 0; + menu_pad_pos = 0; + } + else + { + highlighted_index++; + + if (highlighted_index > menu_pad_height - content_height) + { + menu_pad_pos = menu_pad_height - content_height; + } + else + { + menu_pad_pos++; + } + } + } + break; + + case 's': + case 'S': + log_message(LOG_INFO, "Searching %s", head->name); + head = display_search_bar(head); + refresh = true; + break; + + case 'p': + case 'P': + single_file_dump(head, "./Dump", true); + refresh = true; + break; + + case 'd': + case 'D': + display_dump_menu(head); + refresh = true; + break; + + case 'h': + case 'H': + display_help_menu(); + refresh = true; + break; + + case 'q': + case 'Q': + if (display_exit_menu()) + { + log_message(LOG_INFO, "Exiting DisplayTop"); + return; + } + else + { + refresh = true; + } + break; + + case 27: + log_message(LOG_INFO, "Exiting %s", head->name); + if (head != NULL && head->parent != NULL) + { + head = head->parent; + focus_on_menu = true; + highlighted_index = 0; + display_pad_pos = 0; + is_running = 0; + } + else if (display_exit_menu()) + { + log_message(LOG_INFO, "Exiting DisplayTop"); + return; + } + else + { + refresh = true; + } + break; + + case 'r': + case 'R': + log_message(LOG_INFO, "Refreshing %s", head->name); + refresh = true; + break; + + case '\n': + if (focus_on_menu && head != NULL) + { + if (highlighted_index >= 0 && highlighted_index < head->children_size) + { + head = &head->children[highlighted_index]; + highlighted_index = 0; + display_pad_pos = 0; + is_running = 0; + refresh = true; + } + else if (highlighted_index == head->children_size) + { + if (head != NULL && head->parent != NULL) + { + head = head->parent; + focus_on_menu = true; + highlighted_index = 0; + display_pad_pos = 0; + is_running = 0; + } + else if (display_exit_menu()) + { + log_message(LOG_INFO, "Exiting DisplayTop"); + return; + } + else + { + refresh = true; + } + } + } + break; + + case '\t': + focus_on_menu = !focus_on_menu; + break; + } + } + + refresh = true; + + if (display_pad != NULL) + { + delwin(display_pad); + display_pad = NULL; + } + if (menu_pad != NULL) + { + delwin(menu_pad); + menu_pad = NULL; + } + } +} diff --git a/tools/displaytop/src/display_configuration.c b/tools/displaytop/src/display_configuration.c new file mode 100644 index 000000000..8f366d7df --- /dev/null +++ b/tools/displaytop/src/display_configuration.c @@ -0,0 +1,877 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" + +void display_crtc(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModeRes *resources = NULL; + drmModeObjectProperties *props = NULL; + drmModePlaneRes *plane_resources = NULL; + drmModeCrtc *crtc = NULL; + int crtc_index = -1; + char *endptr; + + if (drm_fd < 0) + { + print_red_text(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + resources = drmModeGetResources(drm_fd); + if (!resources) + { + print_red_text(pad, line++, 1, "Failed to get DRM resources\n"); + goto err; + } + + crtc_index = strtol(node->name + 4, &endptr, 10); + if (*endptr != '\0' || crtc_index < 0 || crtc_index >= resources->count_crtcs) + { + print_red_text(pad, line++, 1, "Invalid CRTC name: %s\n", node->name); + drmModeFreeResources(resources); + goto err; + } + + crtc = drmModeGetCrtc(drm_fd, resources->crtcs[crtc_index]); + if (!crtc) + { + print_red_text(pad, line++, 1, "Failed to get CRTC %d\n", crtc_index); + drmModeFreeResources(resources); + goto err; + } + + mvwprintw(pad, line++, 1, "CRTC ID: %d", crtc->crtc_id); + mvwprintw(pad, line++, 1, "Framebuffer ID: %d", crtc->buffer_id); + mvwprintw(pad, line++, 1, "Position(X, Y): (%d, %d)", crtc->x, crtc->y); + mvwprintw(pad, line++, 1, "Mode: "); + if (crtc->mode_valid) + print_green_text(pad, -1, -1, "VALID"); + else + print_red_text(pad, -1, -1, "INVALID"); + + if (crtc->mode_valid) + { + drmModeModeInfo *mode = &crtc->mode; + mvwprintw(pad, line++, 3, "Name: %s", mode->name); + mvwprintw(pad, line++, 3, "Resolution: %dx%d", mode->hdisplay, mode->vdisplay); + mvwprintw(pad, line++, 3, "Refresh Rate: %.2f Hz", mode->clock / (float)(mode->htotal * mode->vtotal)); + mvwprintw(pad, line++, 3, "Type: 0x%x", mode->type); + mvwprintw(pad, line++, 3, "Flags: 0x%x", mode->flags); + } + + mvwprintw(pad, line++, 1, "Gamma size: %u", crtc->gamma_size); + + props = drmModeObjectGetProperties(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!props) + { + print_red_text(pad, line++, 1, "Failed to get properties for CRTC %u\n", crtc->crtc_id); + drmModeFreeCrtc(crtc); + drmModeFreeResources(resources); + goto err; + } + + plane_resources = drmModeGetPlaneResources(drm_fd); + if (!plane_resources) + { + print_red_text(pad, line++, 3, "Failed to get plane resources"); + return; + } + + line++; + mvwprintw(pad, line++, 1, "Possible Planes for CRTC %d:", crtc->crtc_id); + line++; + + wattron(pad, A_DIM); + for (size_t i = 0; i < plane_resources->count_planes; i++) + { + drmModePlane *plane = drmModeGetPlane(drm_fd, plane_resources->planes[i]); + if (plane) + { + if (plane->possible_crtcs & (1 << crtc_index)) + { + mvwprintw(pad, line++, 3, "Plane %ld: ID %d, %s", + i, plane->plane_id, plane->crtc_id ? "Active" : "Inactive"); + } + drmModeFreePlane(plane); + } + else + { + print_red_text(pad, line++, 3, "Plane %ld: Failed to get plane", i); + } + } + wattroff(pad, A_DIM); + drmModeFreePlaneResources(plane_resources); + + line++; + mvwprintw(pad, line++, 1, "Properties for CRTC %u:", crtc->crtc_id); + line++; + + wattron(pad, A_DIM); + for (uint32_t i = 0; i < props->count_props; i++) + { + drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); + if (prop) + { + uint64_t value = props->prop_values[i]; + int remaining_width = getmaxx(pad) - getcurx(pad); + char value_str[256] = {0}; + + const char *immutability = (prop->flags & DRM_MODE_PROP_IMMUTABLE) ? " immutable" : ""; + const char *atomicity = (prop->flags & DRM_MODE_PROP_ATOMIC) ? " atomic" : " "; + + if (prop->flags & DRM_MODE_PROP_BLOB) + { + snprintf(value_str, sizeof(value_str), "blob = %lu", value); + } + else if (prop->flags & DRM_MODE_PROP_ENUM) + { + snprintf(value_str, sizeof(value_str), "enum ("); + for (int j = 0; j < prop->count_enums; j++) + { + if (j > 0) + { + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), ", "); + } + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), "%s", prop->enums[j].name); + } + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), ") = %s", prop->enums[value].name); + } + else if (prop->flags & DRM_MODE_PROP_RANGE) + { + snprintf(value_str, sizeof(value_str), "range [%lu, %lu] = %lu", + prop->values[0], prop->values[1], value); + } + else if (prop->flags & DRM_MODE_PROP_OBJECT) + { + snprintf(value_str, sizeof(value_str), "object %s = %lu", + get_drm_object_type_name(prop->values[0]), value); + } + else + { + snprintf(value_str, sizeof(value_str), "%lu", value); + } + + wattroff(pad, A_DIM); + mvwprintw(pad, line++, 2, "%d. %s", i, prop->name); + wattron(pad, A_DIM); + wprintw(pad, "%s%s: ", immutability, atomicity); + + if ((int)strlen(value_str) > remaining_width) + { + char truncated_value[remaining_width + 1]; + strncpy(truncated_value, value_str, remaining_width); + truncated_value[remaining_width] = '\0'; + wprintw(pad, "%s", truncated_value); + mvwprintw(pad, line++, 2, "%s", value_str + remaining_width); + } + else + { + wprintw(pad, "%s", value_str); + } + + drmModeFreeProperty(prop); + } + } + wattroff(pad, A_DIM); + + drmModeFreeObjectProperties(props); + + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + drmModeFreeCrtc(crtc); + drmModeFreeResources(resources); + return; + +err: + wrefresh(pad); + wgetch(pad); + return; +} + +void display_connector(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModeRes *resources = NULL; + drmModeConnector *connector = NULL; + int connector_index = -1; + char *endptr; + + if (drm_fd < 0) + { + print_red_text(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + resources = drmModeGetResources(drm_fd); + if (!resources) + { + print_red_text(pad, line++, 1, "Failed to get DRM resources\n"); + goto err; + } + + connector_index = strtol(node->name + 9, &endptr, 10); + + if (*endptr != '\0' || connector_index < 0 || connector_index >= resources->count_connectors) + { + mvwprintw(pad, line++, 1, "Invalid Connector name: %s\n", node->name); + drmModeFreeResources(resources); + goto err; + } + + connector = drmModeGetConnector(drm_fd, resources->connectors[connector_index]); + if (!connector) + { + mvwprintw(pad, line++, 1, "Failed to get Connector %d\n", connector_index); + drmModeFreeResources(resources); + goto err; + } + + mvwprintw(pad, line++, 1, "Connector ID: %d", connector->connector_id); + mvwprintw(pad, line++, 1, "No. of Encoders: %d", connector->count_encoders); + mvwprintw(pad, line++, 1, "Encoder ID: %d", connector->encoder_id); + + line++; + mvwprintw(pad, line++, 1, "Possible Encoders: "); + for (int i = 0; i < connector->count_encoders; i++) + { + mvwprintw(pad, line++, 3, "Encoder %d: %d", i, connector->encoders[i]); + } + + line++; + mvwprintw(pad, line++, 1, "Connection: "); + if (connector->connection == DRM_MODE_CONNECTED) + print_green_text(pad, -1, -1, "Connected"); + else + print_red_text(pad, -1, -1, "Disconnected"); + + line++; + mvwprintw(pad, line++, 1, "Connector Type: %s", get_connector_type_name(connector->connector_type)); + mvwprintw(pad, line++, 1, "Connector Type ID: %d", connector->connector_type_id); + + mvwprintw(pad, line++, 1, "Subpixel Order: %d", connector->subpixel); + mvwprintw(pad, line++, 1, "MM Height: %d", connector->mmHeight); + mvwprintw(pad, line++, 1, "MM Width: %d", connector->mmWidth); + + mvwprintw(pad, line++, 1, "Modes: %d", connector->count_modes); + wattron(pad, A_DIM); + + line++; + for (int i = 0; i < connector->count_modes; i++) + + if (connector->modes && i < connector->count_modes) + { + const drmModeModeInfo *mode = &connector->modes[i]; + + const char *preferred = (mode->type & DRM_MODE_TYPE_PREFERRED) ? " preferred" : ""; + const char *driver = (mode->type & DRM_MODE_TYPE_DRIVER) ? " driver" : ""; + const char *phsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? " phsync" : " nhsync"; + const char *pvsync = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? " pvsync" : " nvsync"; + const char *interlace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? " interlace" : ""; + + double ratio = (double)mode->hdisplay / mode->vdisplay; + double refresh_rate = 0.0; + const char *aspect_ratio = NULL; + + if (mode->vrefresh > 0) + { + refresh_rate = mode->vrefresh; + } + else if (mode->clock > 0 && mode->vtotal > 0) + { + refresh_rate = (double)(mode->clock * 1000) / (mode->htotal * mode->vtotal); + } + + if (fabs(ratio - 16.0 / 9.0) < 0.01) + { + aspect_ratio = " 16:9"; + } + else if (fabs(ratio - 4.0 / 3.0) < 0.01) + { + aspect_ratio = " 4:3"; + } + else + { + aspect_ratio = ""; + } + + mvwprintw(pad, line++, 2, + "%ux%u @%.2f%s%s%s%s%s%s", + mode->hdisplay, mode->vdisplay, refresh_rate, + driver, preferred, phsync, pvsync, interlace, aspect_ratio); + } + + wattroff(pad, A_DIM); + + mvwprintw(pad, line++, 1, "No. of Properties: %d", connector->count_props); + + wattron(pad, A_DIM); + for (int i = 0; i < connector->count_props; i++) + { + drmModePropertyPtr prop = drmModeGetProperty(drm_fd, connector->props[i]); + if (prop) + { + char value_str[256] = {0}; + uint64_t value = connector->prop_values[i]; + int remaining_width = getmaxx(pad) - getcurx(pad); + const char *atomicity = (prop->flags & DRM_MODE_PROP_ATOMIC) ? " atomic" : " "; + const char *immutability = (prop->flags & DRM_MODE_PROP_IMMUTABLE) ? " immutable" : ""; + + if (prop->flags & DRM_MODE_PROP_BLOB) + { + snprintf(value_str, sizeof(value_str), "blob = %lu", value); + } + else if (prop->flags & DRM_MODE_PROP_ENUM) + { + + snprintf(value_str, sizeof(value_str), "enum ("); + for (int j = 0; j < prop->count_enums; j++) + { + if (j > 0) + { + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), ", "); + } + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), "%s", prop->enums[j].name); + } + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), ") = %s", prop->enums[value].name); + } + else if (prop->flags & DRM_MODE_PROP_RANGE) + { + snprintf(value_str, sizeof(value_str), "range [%lu, %lu] = %lu", + prop->values[0], prop->values[1], value); + } + else if (prop->flags & DRM_MODE_PROP_OBJECT) + { + snprintf(value_str, sizeof(value_str), "object %s = %lu", + get_drm_object_type_name(prop->values[0]), value); + } + else + { + snprintf(value_str, sizeof(value_str), "%lu", value); + } + + wattroff(pad, A_DIM); + mvwprintw(pad, line++, 2, "%d. %s", i, prop->name); + wattron(pad, A_DIM); + wprintw(pad, "%s%s: ", immutability, atomicity); + + if ((int)strlen(value_str) > remaining_width) + { + char truncated_value[remaining_width + 1]; + strncpy(truncated_value, value_str, remaining_width); + truncated_value[remaining_width] = '\0'; + wprintw(pad, "%s", truncated_value); + mvwprintw(pad, line++, 2, "%s", value_str + remaining_width); + } + else + { + wprintw(pad, "%s", value_str); + } + + drmModeFreeProperty(prop); + } + } + wattroff(pad, A_DIM); + + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + drmModeFreeConnector(connector); + drmModeFreeResources(resources); + return; + +err: + wrefresh(pad); + wgetch(pad); + return; +} + +void display_encoder(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModeRes *resources = NULL; + drmModeEncoder *encoder = NULL; + int encoder_id = -1; + char *endptr; + + if (drm_fd < 0) + { + mvwprintw(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + resources = drmModeGetResources(drm_fd); + if (!resources) + { + mvwprintw(pad, line++, 1, "Failed to get DRM resources\n"); + goto err; + } + + encoder_id = strtol(node->name + 7, &endptr, 10); + if (*endptr != '\0' || encoder_id < 0 || encoder_id >= resources->count_encoders) + { + mvwprintw(pad, line++, 1, "Invalid Encoder name: %s\n", node->name); + drmModeFreeResources(resources); + goto err; + } + + encoder = drmModeGetEncoder(drm_fd, resources->encoders[encoder_id]); + if (!encoder) + { + mvwprintw(pad, line++, 1, "Failed to get Encoder %d\n", encoder_id); + drmModeFreeResources(resources); + goto err; + } + + line++; + mvwprintw(pad, line++, 1, "Encoder Type: "); + if (encoder->encoder_type == DRM_MODE_ENCODER_NONE) + print_red_text(pad, -1, -1, "NONE"); + else + print_bold_text(pad, -1, -1, "%s", get_encoder_type_name(encoder->encoder_type)); + line++; + + mvwprintw(pad, line++, 1, "Encoder ID: %d", encoder->encoder_id); + mvwprintw(pad, line++, 1, "CRTC ID: %d", encoder->crtc_id); + mvwprintw(pad, line++, 1, "Possible CRTCs: %d", encoder->possible_crtcs); + mvwprintw(pad, line++, 1, "Possible Clones: %d", encoder->possible_clones); + + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + drmModeFreeEncoder(encoder); + drmModeFreeResources(resources); + return; + +err: + wrefresh(pad); + wgetch(pad); + return; +} + +void display_framebuffer(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModeRes *resources = NULL; + drmModeFB *fb = NULL; + int framebuffer_id = -1; + char *endptr; + + if (drm_fd < 0) + { + mvwprintw(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + resources = drmModeGetResources(drm_fd); + if (!resources) + { + mvwprintw(pad, line++, 1, "Failed to get DRM resources\n"); + goto err; + } + + framebuffer_id = strtol(node->name + 11, &endptr, 10); + if (*endptr != '\0' || framebuffer_id < 0 || framebuffer_id >= resources->count_fbs) + { + mvwprintw(pad, line++, 1, "Invalid Framebuffer name: %s\n", node->name); + drmModeFreeResources(resources); + goto err; + } + + fb = drmModeGetFB(drm_fd, resources->fbs[framebuffer_id]); + if (!fb) + { + mvwprintw(pad, line++, 1, "Failed to get Framebuffer %d\n", framebuffer_id); + drmModeFreeResources(resources); + goto err; + } + + mvwprintw(pad, line++, 1, "Framebuffer ID: %d", fb->fb_id); + mvwprintw(pad, line++, 1, "Width: %d", fb->width); + mvwprintw(pad, line++, 1, "Height: %d", fb->height); + mvwprintw(pad, line++, 1, "Pitch: %d", fb->pitch); + mvwprintw(pad, line++, 1, "BPP: %d", fb->bpp); + mvwprintw(pad, line++, 1, "Depth: %d", fb->depth); + mvwprintw(pad, line++, 1, "Handle: %d", fb->handle); + + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + drmModeFreeFB(fb); + drmModeFreeResources(resources); + return; + +err: + wrefresh(pad); + wgetch(pad); + return; +} + +void display_plane(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModePlaneRes *plane_resources = NULL; + drmModePlane *plane = NULL; + int plane_index = -1; + char *endptr; + drmModeObjectProperties *props = NULL; + + if (drm_fd < 0) + { + mvwprintw(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + plane_resources = drmModeGetPlaneResources(drm_fd); + if (!plane_resources) + { + mvwprintw(pad, line++, 1, "Failed to get DRM plane resources\n"); + goto err; + } + + plane_index = strtol(node->name + 5, &endptr, 10); + if (*endptr != '\0' || plane_index < 0 || plane_index >= (int)plane_resources->count_planes) + { + mvwprintw(pad, line++, 1, "Invalid Plane name: %s\n", node->name); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + plane = drmModeGetPlane(drm_fd, plane_resources->planes[plane_index]); + if (!plane) + { + mvwprintw(pad, line++, 1, "Failed to get Plane %d\n", plane_index); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + mvwprintw(pad, line++, 1, "Plane Index: %d", plane_index); + mvwprintw(pad, line++, 1, "Plane ID: %d", plane->plane_id); + mvwprintw(pad, line++, 1, "CRTC ID: %d", plane->crtc_id); + mvwprintw(pad, line++, 1, "Framebuffer ID: %d", plane->fb_id); + mvwprintw(pad, line++, 1, "Possible CRTCs: %d", plane->possible_crtcs); + mvwprintw(pad, line++, 1, "Gamma size: %u", plane->gamma_size); + mvwprintw(pad, line++, 1, "CRTC Position: (%d, %d)", plane->crtc_x, plane->crtc_y); + + mvwprintw(pad, line++, 1, "Possible CRTC IDs: "); + for (int i = 0; i < 32; i++) + { + if (plane->possible_crtcs & (1 << i)) + { + mvwprintw(pad, line++, 3, "CRTC %d", i); + } + } + line++; + + mvwprintw(pad, line++, 1, "Number of formats: %u", plane->count_formats); + line++; + + props = drmModeObjectGetProperties(drm_fd, plane->plane_id, DRM_MODE_OBJECT_PLANE); + if (!props) + { + mvwprintw(pad, line++, 1, "Failed to get properties for Plane %u\n", plane->plane_id); + drmModeFreePlane(plane); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + mvwprintw(pad, line++, 1, "Properties for Plane %u:", plane->plane_id); + line++; + + for (uint32_t i = 0; i < props->count_props; i++) + { + drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); + if (prop) + { + char value_str[256] = {0}; + uint64_t value = props->prop_values[i]; + int remaining_width = getmaxx(pad) - getcurx(pad); + const char *immutability = (prop->flags & DRM_MODE_PROP_IMMUTABLE) ? " immutable" : ""; + const char *atomicity = (prop->flags & DRM_MODE_PROP_ATOMIC) ? " atomic" : " "; + + if (prop->flags & DRM_MODE_PROP_BLOB) + { + snprintf(value_str, sizeof(value_str), "blob = %lu", value); + } + else if (prop->flags & DRM_MODE_PROP_ENUM) + { + snprintf(value_str, sizeof(value_str), "enum ("); + for (int j = 0; j < prop->count_enums; j++) + { + if (j > 0) + { + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), ", "); + } + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), "%s", prop->enums[j].name); + } + snprintf(value_str + strlen(value_str), sizeof(value_str) - strlen(value_str), ") = %s", prop->enums[value].name); + } + else if (prop->flags & DRM_MODE_PROP_RANGE) + { + snprintf(value_str, sizeof(value_str), "range [%lu, %lu] = %lu", + prop->values[0], prop->values[1], value); + } + else if (prop->flags & DRM_MODE_PROP_OBJECT) + { + snprintf(value_str, sizeof(value_str), "object %s = %lu", + get_drm_object_type_name(prop->values[0]), value); + } + else + { + snprintf(value_str, sizeof(value_str), "%lu", value); + } + + wattroff(pad, A_DIM); + mvwprintw(pad, line++, 2, "%d. %s", i, prop->name); + wattron(pad, A_DIM); + wprintw(pad, "%s%s: ", immutability, atomicity); + + if ((int)strlen(value_str) > remaining_width) + { + char truncated_value[remaining_width + 1]; + strncpy(truncated_value, value_str, remaining_width); + truncated_value[remaining_width] = '\0'; + wprintw(pad, "%s", truncated_value); + mvwprintw(pad, line++, 2, "%s", value_str + remaining_width); + } + else + { + wprintw(pad, "%s", value_str); + } + + drmModeFreeProperty(prop); + } + } + + drmModeFreeObjectProperties(props); + + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + drmModeFreePlane(plane); + drmModeFreePlaneResources(plane_resources); + return; + +err: + wrefresh(pad); + wgetch(pad); + return; +} + +void display_informats(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModePlaneRes *plane_resources = NULL; + drmModePlane *plane = NULL; + drmModeObjectProperties *props = NULL; + int plane_index = -1; + + if (drm_fd < 0) + { + mvwprintw(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + plane_resources = drmModeGetPlaneResources(drm_fd); + if (!plane_resources) + { + mvwprintw(pad, line++, 1, "Failed to get DRM plane resources\n"); + goto err; + } + + if (strncmp(node->name, "IN_FORMATS", 10) == 0) + { + char *endptr; + plane_index = strtol(node->name + 10, &endptr, 10); + if (*endptr != '\0' || plane_index < 0 || plane_index >= (int)plane_resources->count_planes) + { + mvwprintw(pad, line++, 1, "Invalid Plane Index: %s\n", node->name); + drmModeFreePlaneResources(plane_resources); + goto err; + } + } + else + { + mvwprintw(pad, line++, 1, "Invalid format for node->name: %s\n", node->name); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + plane = drmModeGetPlane(drm_fd, plane_resources->planes[plane_index]); + if (!plane) + { + mvwprintw(pad, line++, 1, "Failed to get Plane %d\n", plane_index); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + mvwprintw(pad, line++, 1, "Plane Index: %d", plane_index); + mvwprintw(pad, line++, 1, "Plane ID: %d", plane->plane_id); + + props = drmModeObjectGetProperties(drm_fd, plane->plane_id, DRM_MODE_OBJECT_PLANE); + if (!props) + { + mvwprintw(pad, line++, 1, "Failed to get properties for Plane %u\n", plane->plane_id); + drmModeFreePlane(plane); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + mvwprintw(pad, line++, 1, "IN_FORMATS for Plane %u:", plane->plane_id); + line++; + + for (uint32_t i = 0; i < props->count_props; i++) + { + drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]); + if (prop) + { + uint64_t value = props->prop_values[i]; + + if (prop->flags & DRM_MODE_PROP_BLOB && strcmp(prop->name, "IN_FORMATS") == 0) + { + drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm_fd, value); + struct drm_format_modifier_blob *data = NULL; + uint32_t *fmts = NULL; + struct drm_format_modifier *mods = NULL; + + if (!blob) + { + log_message(LOG_ERROR, "drmModeGetPropertyBlob"); + return; + } + + data = blob->data; + + fmts = (uint32_t *)((char *)data + data->formats_offset); + mods = (struct drm_format_modifier *)((char *)data + data->modifiers_offset); + + for (uint32_t j = 0; j < data->count_modifiers; j++) + { + mvwprintw(pad, line++, 1, "Modifier %u: %s", j, get_basic_modifier_str(mods[j].modifier)); + mvwprintw(pad, line++, 3, "Formats: "); + wattron(pad, A_DIM); + for (uint32_t k = 0; k < data->count_formats; k++) + { + if (mods[j].formats & (1ULL << k)) + { + mvwprintw(pad, line++, 5, "%s (0x%08x)", get_format_str(fmts[k]), fmts[k]); + } + } + wattroff(pad, A_DIM); + } + + drmModeFreePropertyBlob(blob); + } + } + } + drmModeFreeObjectProperties(props); + drmModeFreePlane(plane); + drmModeFreePlaneResources(plane_resources); + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + return; + +err: + wrefresh(pad); + return; +} + +void display_formats(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + drmModePlaneRes *plane_resources = NULL; + drmModePlane *plane = NULL; + int plane_index = -1; + + if (drm_fd < 0) + { + mvwprintw(pad, line++, 1, "Failed to open DRM Device!\n"); + goto err; + } + + plane_resources = drmModeGetPlaneResources(drm_fd); + if (!plane_resources) + { + mvwprintw(pad, line++, 1, "Failed to get DRM plane resources\n"); + goto err; + } + + if (strncmp(node->name, "FORMATS", 7) == 0) + { + char *endptr; + plane_index = strtol(node->name + 7, &endptr, 10); + if (*endptr != '\0' || plane_index < 0 || plane_index >= (int)plane_resources->count_planes) + { + mvwprintw(pad, line++, 1, "Invalid Plane Index: %s\n", node->name); + drmModeFreePlaneResources(plane_resources); + goto err; + } + } + else + { + mvwprintw(pad, line++, 1, "Invalid format for node->name: %s\n", node->name); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + plane = drmModeGetPlane(drm_fd, plane_resources->planes[plane_index]); + if (!plane) + { + mvwprintw(pad, line++, 1, "Failed to get Plane %d\n", plane_index); + drmModeFreePlaneResources(plane_resources); + goto err; + } + + mvwprintw(pad, line++, 1, "Plane ID: %d", plane->plane_id); + + line++; + mvwprintw(pad, line++, 1, "No. of Plane formats %u:", plane->count_formats); + line++; + + if (plane->count_formats > 0) + { + wattron(pad, A_DIM); + for (uint32_t i = 0; i < plane->count_formats; i++) + { + mvwprintw(pad, line++, 3, "%s (0x%08x)", get_format_str(plane->formats[i]), plane->formats[i]); + } + line++; + wattroff(pad, A_DIM); + } + + drmModeFreePlane(plane); + drmModeFreePlaneResources(plane_resources); + + mvwprintw(pad, ++line, 1, "Press 'e' to go back."); + + *content_line = line; + return; + +err: + wrefresh(pad); + return; +} diff --git a/tools/displaytop/src/display_dump.c b/tools/displaytop/src/display_dump.c new file mode 100644 index 000000000..f7ff84dbc --- /dev/null +++ b/tools/displaytop/src/display_dump.c @@ -0,0 +1,215 @@ +/* + * Copyright © 2025 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 "node.h" +#include "utils.h" + +void single_file_dump(Node *node, const char *filePath, bool single) +{ + time_t now; + struct tm *t; + char dump_file_path[512]; + FILE *file; + const int padHeight = 1000; + const int padWidth = 200; + WINDOW *virtualWin; + int line = 0; + char buffer[padWidth + 1]; + + if (!node || node->name[0] == '\0' || !node->display_function) + { + log_message(LOG_ERROR, "Invalid node provided for dumping"); + return; + } + + now = time(NULL); + t = localtime(&now); + + if (single) + { + snprintf(dump_file_path, sizeof(dump_file_path), "%s/%s_%04d-%02d-%02d-%02d.%02d.%02d.txt", + filePath, node->name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + else + { + snprintf(dump_file_path, sizeof(dump_file_path), "%s/%s.txt", filePath, node->name); + } + + file = fopen(dump_file_path, "w"); + if (!file) + { + log_message(LOG_ERROR, "Failed to open file: %s", dump_file_path); + return; + } + chmod(dump_file_path, 0777); + + virtualWin = newwin(padHeight, padWidth, 0, 0); + if (!virtualWin) + { + log_message(LOG_ERROR, "Failed to create virtual window"); + fclose(file); + return; + } + + node->display_function(virtualWin, node, &line); + + fprintf(file, "Dump Time: %04d-%02d-%02d %02d:%02d:%02d\n\n", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + + for (int y = 0; y < line; ++y) + { + wmove(virtualWin, y, 0); + winnstr(virtualWin, buffer, padWidth); + buffer[padWidth] = '\0'; + fprintf(file, "%s\n", buffer); + } + + log_message(LOG_INFO, "Dumped node '%s' to %s", node->name, dump_file_path); + + delwin(virtualWin); + fclose(file); +} + +static void recursive_dump_with_progress(Node *node, const char *parentDir, + int *current_count, int total_count, WINDOW *progress_win) +{ + char nodeDir[512]; + bool hasChildren; + time_t now; + struct tm *t; + + if (!node) + return; + + hasChildren = (node->children_size > 0); + + if (hasChildren) + { + now = time(NULL); + t = localtime(&now); + + snprintf(nodeDir, sizeof(nodeDir), "%s/%s(%04d-%02d-%02d-%02d.%02d.%02d)", + parentDir, node->name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + + if (mkdir(nodeDir, 0777) == -1 && errno != EEXIST) + { + log_message(LOG_ERROR, "Error creating directory %s: %s", nodeDir, strerror(errno)); + return; + } + (*current_count)++; + } + else + { + snprintf(nodeDir, sizeof(nodeDir), "%s", parentDir); + } + + if (node->display_function) + { + single_file_dump(node, nodeDir, false); + (*current_count)++; + } + + render_progress_bar(progress_win, *current_count, total_count); + + for (int i = 0; i < node->children_size; ++i) + { + recursive_dump_with_progress(&node->children[i], nodeDir, + current_count, total_count, progress_win); + } +} + +void display_dump_menu(Node *head) +{ + int total_nodes; + int winHeight, winWidth; + WINDOW *win; + int line; + int input; + int current_count; + + if (!head) + { + log_message(LOG_ERROR, "Head node is NULL, cannot dump."); + return; + } + + total_nodes = 0; + count_nodes(head, &total_nodes); + + if (total_nodes == 0) + { + log_message(LOG_INFO, "No nodes found to dump."); + return; + } + + getmaxyx(stdscr, winHeight, winWidth); + + win = newwin(winHeight, winWidth, 0, 0); + wbkgd(win, COLOR_PAIR(6)); + box(win, 0, 0); + + draw_header(win, winWidth, "DUMP MENU", "Press ESC to exit"); + + line = 4; + wattron(win, A_BOLD); + mvwprintw(win, line++, 2, "You are about to dump %d nodes into files.", total_nodes); + line++; + mvwprintw(win, line++, 2, "Dumping menus under the path: %s", head->name); + line++; + wattroff(win, A_BOLD); + mvwprintw(win, line++, 2, "Proceed with dump? (y to confirm, ESC to cancel)"); + wrefresh(win); + + input = wgetch(win); + if (input != 'y' && input != 'Y') + { + delwin(win); + return; + } + + log_message(LOG_INFO, "Starting dump from node: %s", head->name); + + if (mkdir(DUMP_DIR, 0777) == -1 && errno != EEXIST) + { + log_message(LOG_ERROR, "Failed to create dump directory: %s", strerror(errno)); + delwin(win); + return; + } + + line++; + mvwprintw(win, line++, 2, "Progress:"); + wrefresh(win); + + current_count = 0; + recursive_dump_with_progress(head, DUMP_DIR, ¤t_count, total_nodes, win); + + mvwprintw(win, line + 2, 2, "Dumping is over. Press any key to go back."); + wrefresh(win); + getch(); + + delwin(win); + endwin(); +} \ No newline at end of file diff --git a/tools/displaytop/src/display_search_bar.c b/tools/displaytop/src/display_search_bar.c new file mode 100644 index 000000000..3d119b20f --- /dev/null +++ b/tools/displaytop/src/display_search_bar.c @@ -0,0 +1,282 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" + +/** + * @brief Constructs a search path from a given node to the head node. + * + * This function constructs a search path string from the given node to the head node. + * The path is constructed by traversing from the given node up to the head node, + * appending each node's name to the path string, separated by " > ". + * + * @param node Pointer to the starting node. + * @param head Pointer to the head node until which path is required. + * @return A dynamically allocated string representing the search path. + * The caller is responsible for freeing the allocated memory. + * Returns NULL if either node or head is NULL. + */ +static char *construct_search_path(Node *node, Node *head) +{ + char buffer[1024]; + char temp[1024]; + char *path; + Node *current; + + if (node == NULL || head == NULL) + return NULL; + + buffer[0] = '\0'; + path = NULL; + current = node; + + while (current && strcmp(current->name, head->name) != 0) + { + if (snprintf(temp, sizeof(temp), "%s%s%s", current->name, buffer[0] ? " > " : "", buffer) >= (int)sizeof(temp)) + { + log_message(LOG_ERROR, "Path string truncated: %s%s%s", current->name, buffer[0] ? " > " : "", buffer); + return NULL; + } + strncpy(buffer, temp, sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\0'; + + current = current->parent; + } + + path = (char *)malloc(strlen(buffer) + 1); + if (path) + { + strcpy(path, buffer); + } + + return path; +} + +/** + * @brief Displays the children of a given node in a new window. + * + * This function displays the children of a given node in a new window. + * The user can navigate through the children using the arrow keys and select a child by pressing Enter. + * The function returns the selected child node. + * + * @param node Pointer to the node whose children are to be displayed. + * @param head Pointer to the head node (current location in DisplayTop). + * @return Pointer to the selected child node. + * Returns NULL if either node or node->children is NULL. + */ +static Node *display_children(Node *node, Node *head) +{ + int rows, cols; + int line; + int pad_pos; + int highlighted_index; + int ch; + WINDOW *pad; + + if (node == NULL || node->children == NULL) + return NULL; + + getmaxyx(stdscr, rows, cols); + + line = 0; + pad_pos = 0; + highlighted_index = 0; + pad = newpad(100, cols - 2); + + if (pad == NULL) + return NULL; + + wbkgd(pad, COLOR_PAIR(6)); + + if (node->children_size > 0) + { + line++; + + for (int i = 0; i < node->children_size; ++i) + { + if (highlighted_index == i) + wattron(pad, A_REVERSE); + + mvwprintw(pad, line + i, 1, "%d. %s - %s", i + 1, node->children[i].name, construct_search_path(&node->children[i], head)); + wattroff(pad, A_REVERSE); + } + prefresh(pad, pad_pos, 0, 8, 1, rows - 2, cols - 2); + } + + while ((ch = getch()) != 27) + { + if (pad_pos > 0) + mvwprintw(pad, 0, cols - 5, "..."); + else + mvwprintw(pad, 0, cols - 5, " "); + + if (pad_pos < node->children_size - (rows - 8)) + mvwprintw(pad, rows - 1, cols - 5, "..."); + else + mvwprintw(pad, rows - 1, cols - 5, " "); + + switch (ch) + { + case KEY_UP: + if (highlighted_index > 0) + { + highlighted_index--; + pad_pos = (highlighted_index < pad_pos) ? highlighted_index : pad_pos; + } + break; + case KEY_DOWN: + if (highlighted_index < node->children_size - 1) + { + highlighted_index++; + pad_pos = (highlighted_index >= pad_pos + rows - 8) ? highlighted_index - rows + 8 : pad_pos; + } + break; + case '\n': + delwin(pad); + return &node->children[highlighted_index]; + default: + break; + } + + for (int i = 0; i < node->children_size; ++i) + { + if (highlighted_index == i) + wattron(pad, A_REVERSE); + + mvwprintw(pad, line + i, 1, "%d. %s - %s", i + 1, node->children[i].name, construct_search_path(&node->children[i], head)); + wattroff(pad, A_REVERSE); + } + + prefresh(pad, pad_pos, 0, 8, 1, rows - 2, cols - 2); + } + + delwin(pad); + return head; +} + +Node *display_search_bar(Node *head) +{ + int rows, cols; + WINDOW *win; + FIELD *fields[2]; + FORM *form; + char input_buffer[256]; + int ch, pos; + Node *results; + Node *result; + + memset(input_buffer, 0, sizeof(input_buffer)); + pos = 0; + + getmaxyx(stdscr, rows, cols); + win = newwin(rows, cols, 0, 0); + wbkgd(win, COLOR_PAIR(6)); + keypad(win, TRUE); + box(win, 0, 0); + + print_bold_text(win, 1, 1, "Search Menu"); + mvwprintw(win, 1, (cols - strlen("Searching under: %s")) / 2, "Searching under: %s", head->name); + mvwprintw(win, 1, cols - strlen("Press 'Esc' to exit ") - 1, "Press 'Esc' to exit"); + mvwhline(win, 2, 1, 0, cols - 2); + mvwhline(win, 6, 1, 0, cols - 2); + + fields[0] = new_field(1, cols - 4, 1, 1, 0, 0); + fields[1] = NULL; + + set_field_back(fields[0], A_UNDERLINE); + field_opts_off(fields[0], O_AUTOSKIP); + + form = new_form(fields); + set_form_win(form, win); + set_form_sub(form, derwin(win, 3, cols - 2, 3, 1)); + + wbkgd(form_sub(form), COLOR_PAIR(7)); + box(form_sub(form), 0, 0); + post_form(form); + wrefresh(win); + + while ((ch = getch()) != 27) + { + switch (ch) + { + case KEY_BACKSPACE: + case 127: + case 8: + if (pos > 0) + { + pos--; + input_buffer[pos] = '\0'; + form_driver(form, REQ_DEL_PREV); + } + break; + case '\n': + if (pos > 0) + { + results = create_node("Search Results", NULL, NULL); + search_nodes(head, input_buffer, results); + + if (results->children_size != 0) + { + unpost_form(form); + print_bold_text(win, 7, 2, "%d hits", results->children_size); + wrefresh(win); + result = display_children(results, head); + free_form(form); + free_field(fields[0]); + delwin(win); + endwin(); + return result; + } + else + { + print_bold_text(win, 7, 2, "No results found"); + } + } + break; + default: + if (pos < (int)(sizeof(input_buffer) - 1)) + { + input_buffer[pos++] = ch; + input_buffer[pos] = '\0'; + form_driver(form, ch); + } + break; + } + wrefresh(win); + } + + unpost_form(form); + wrefresh(win); + + getch(); + free_form(form); + free_field(fields[0]); + delwin(win); + endwin(); + + return head; +} diff --git a/tools/displaytop/src/display_summary.c b/tools/displaytop/src/display_summary.c new file mode 100644 index 000000000..a22ed097c --- /dev/null +++ b/tools/displaytop/src/display_summary.c @@ -0,0 +1,308 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" + +#define MAX_WIDTH getmaxx(pad) +#define MAX_ENCODERS_PER_ROW ((MAX_WIDTH - 11) / 8) + +static void display_encoders(WINDOW *pad, drmModeRes *resources,int *line) +{ + int col_width = 6; + int max_per_row = MAX_ENCODERS_PER_ROW; + int start, end, i; + drmModeEncoder *encoder; + + wattron(pad, A_BOLD); + mvwprintw(pad, (*line)++, 1, "Encoders - %d", resources->count_encoders); + wattroff(pad, A_BOLD); + + for (start = 0; start < resources->count_encoders; start += max_per_row) + { + end = (start + max_per_row < resources->count_encoders) ? (start + max_per_row) : resources->count_encoders; + + mvwprintw(pad, (*line)++, 2, "Encoder ID |"); + for (i = start; i < end; i++) + { + encoder = drmModeGetEncoder(drm_fd, resources->encoders[i]); + if (encoder) + { + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*d", col_width, encoder->encoder_id); + wprintw(pad, "|"); + drmModeFreeEncoder(encoder); + } + else + { + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*s", col_width, "N/A"); + wprintw(pad, "|"); + } + } + + mvwprintw(pad, (*line)++, 2, "Type |"); + for (i = start; i < end; i++) + { + encoder = drmModeGetEncoder(drm_fd, resources->encoders[i]); + if (encoder) + { + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*s", col_width, get_encoder_type_name(encoder->encoder_type)); + wprintw(pad, "|"); + drmModeFreeEncoder(encoder); + } + else + { + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*s", col_width, "N/A"); + wprintw(pad, "|"); + } + } + + mvwprintw(pad, (*line)++, 2, "CRTC ID |"); + for (i = start; i < end; i++) + { + encoder = drmModeGetEncoder(drm_fd, resources->encoders[i]); + if (encoder) + { + if (encoder->crtc_id != 0) + { + wattron(pad, A_BOLD | COLOR_PAIR(2)); + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*d", col_width, encoder->crtc_id); + wattroff(pad, A_BOLD | COLOR_PAIR(2)); + wprintw(pad, "|"); + } + else + { + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*d", col_width, encoder->crtc_id); + wprintw(pad, "|"); + } + drmModeFreeEncoder(encoder); + } + else + { + mvwprintw(pad, (*line) - 1, 14 + ((i - start) * (col_width + 1)), "%*s", col_width, "N/A"); + wprintw(pad, "|"); + } + } + + (*line) += 1; + } +} + +void display_summary(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + int col_width; + drmModeRes *resources = NULL; + drmModeCrtc *crtc = NULL; + drmVersionPtr version = NULL; + uint64_t value; + drmDevicePtr devices[8]; + int device_count; + + wclear(pad); + + print_bold_text(pad, line++, 1, "%s Summary", node->name); + line++; + + print_dim_text(pad, line++, 1, "Fetching details for card: %s", find_drm_device(true)); + line++; + + if (drm_fd < 0) + { + mvwprintw(pad, line++, 2, "Failed to open DRM Device!\n"); + goto error; + } + + resources = drmModeGetResources(drm_fd); + + if (!resources) + { + mvwprintw(pad, line++, 2, "Failed to get DRM resources\n"); + goto error; + } + + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "CRTCs - %d", resources->count_crtcs); + wattroff(pad, A_BOLD); + + col_width = 10; + mvwprintw(pad, line++, 2, "CRTC ID |"); + for (int i = 0; i < resources->count_crtcs; i++) + { + crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]); + + if (crtc) + { + if (crtc->mode_valid) + { + wattron(pad, A_BOLD | COLOR_PAIR(2)); + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*d", col_width, crtc->crtc_id); + wattroff(pad, A_BOLD | COLOR_PAIR(2)); + wprintw(pad, "|"); + } + else + { + wattron(pad, A_BOLD | COLOR_PAIR(3)); + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*d", col_width, crtc->crtc_id); + wattroff(pad, A_BOLD | COLOR_PAIR(3)); + wprintw(pad, "|"); + } + drmModeFreeCrtc(crtc); + } + else + { + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*s|", col_width, "N/A"); + } + } + line++; + + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "Connectors - %d", resources->count_connectors); + wattroff(pad, A_BOLD); + + col_width = 10; + mvwprintw(pad, line++, 2, "Connectors|"); + for (int i = 0; i < resources->count_connectors; i++) + { + drmModeConnector *connector = drmModeGetConnector(drm_fd, resources->connectors[i]); + if (connector) + { + if (connector->connection == DRM_MODE_CONNECTED) + { + wattron(pad, A_BOLD | COLOR_PAIR(2)); + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*d", col_width, connector->connector_id); + wattroff(pad, A_BOLD | COLOR_PAIR(2)); + wprintw(pad, "|"); + } + else + { + wattron(pad, A_BOLD | COLOR_PAIR(3)); + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*d", col_width, connector->connector_id); + wattroff(pad, A_BOLD | COLOR_PAIR(3)); + wprintw(pad, "|"); + } + drmModeFreeConnector(connector); + } + else + { + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*s|", col_width, "N/A"); + } + } + + mvwprintw(pad, line++, 2, "Type |"); + for (int i = 0; i < resources->count_connectors; i++) + { + drmModeConnector *connector = drmModeGetConnector(drm_fd, resources->connectors[i]); + if (connector) + { + const char *type_name = get_connector_type_name(connector->connector_type); + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*s|", col_width, type_name); + drmModeFreeConnector(connector); + } + else + { + mvwprintw(pad, line - 1, 13 + (i * (col_width + 1)), "%*s|", col_width, "N/A"); + } + } + line++; + + display_encoders(pad, resources, &line); + + version = drmGetVersion(drm_fd); + if (version) + { + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "DRM Driver: %s", version->name); + wattroff(pad, A_BOLD); + drmFreeVersion(version); + } + else + { + mvwprintw(pad, line++, 2, "Failed to get DRM version"); + } + + drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &value); + mvwprintw(pad, line++, 2, "DRM_CAP_TIMESTAMP_MONOTONIC: %lu", value); + + drmGetCap(drm_fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &value); + mvwprintw(pad, line++, 2, "DRM_CAP_CRTC_IN_VBLANK_EVENT: %lu", value); + + drmGetCap(drm_fd, DRM_CAP_ASYNC_PAGE_FLIP, &value); + mvwprintw(pad, line++, 2, "DRM_CAP_ASYNC_PAGE_FLIP: %lu", value); + + drmGetCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, &value); + mvwprintw(pad, line++, 2, "DRM_CLIENT_CAP_ATOMIC: %lu", value); + + drmGetCap(drm_fd, DRM_CAP_ADDFB2_MODIFIERS, &value); + mvwprintw(pad, line++, 2, "DRM_CAP_ADDFB2_MODIFIERS: %lu", value); + line++; + + device_count = drmGetDevices2(0, devices, 8); + if (device_count < 0) + { + mvwprintw(pad, line++, 2, "Failed to get DRM devices"); + } + else + { + for (int i = 0; i < device_count; i++) + { + if (devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY)) + { + char available_nodes[256] = ""; + + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "Device: PCI %04x:%04x", devices[i]->deviceinfo.pci->vendor_id, devices[i]->deviceinfo.pci->device_id); + wattroff(pad, A_BOLD); + + if (devices[i]->available_nodes & (1 << DRM_NODE_PRIMARY)) + { + strcat(available_nodes, "primary, "); + } + if (devices[i]->available_nodes & (1 << DRM_NODE_RENDER)) + { + strcat(available_nodes, "render, "); + } + if (strlen(available_nodes) > 0) + { + available_nodes[strlen(available_nodes) - 2] = '\0'; + } + mvwprintw(pad, line++, 2, "Available nodes: %s", available_nodes); + } + } + drmFreeDevices(devices, device_count); + } + + drmModeFreeResources(resources); + + *content_line = line; + return; + +error: + if (resources) + { + drmModeFreeResources(resources); + } + + wrefresh(pad); + *content_line = line; + return; +} diff --git a/tools/displaytop/src/log.c b/tools/displaytop/src/log.c new file mode 100644 index 000000000..6d38fee07 --- /dev/null +++ b/tools/displaytop/src/log.c @@ -0,0 +1,84 @@ +/* + * Copyright © 2025 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 "log.h" + +static FILE *log_file = NULL; + +void init_log_system(void) +{ + log_file = fopen(LOG_FILE, "a"); + if (!log_file) + { + fprintf(stderr, "Failed to open log file: %s\n", LOG_FILE); + } +} + +void log_message(int level, const char *format, ...) +{ + time_t now; + struct tm *t; + char time_str[20]; + const char *level_str; + va_list args; + + if (!log_file) + return; + + now = time(NULL); + t = localtime(&now); + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", t); + + switch (level) + { + case LOG_INFO: + level_str = "INFO"; + break; + case LOG_WARNING: + level_str = "WARNING"; + break; + case LOG_ERROR: + level_str = "ERROR"; + break; + default: + level_str = "UNKNOWN"; + break; + } + + va_start(args, format); + fprintf(log_file, "[%s] [%s] ", time_str, level_str); + vfprintf(log_file, format, args); + fprintf(log_file, "\n"); + va_end(args); + + fflush(log_file); +} + +void close_log_system(void) +{ + if (log_file) + { + fclose(log_file); + log_file = NULL; + } +} diff --git a/tools/displaytop/src/main.c b/tools/displaytop/src/main.c new file mode 100644 index 000000000..32b273e70 --- /dev/null +++ b/tools/displaytop/src/main.c @@ -0,0 +1,63 @@ +/* + * Copyright © 2025 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 "node.h" +#include "data.h" +#include "display.h" +#include "populate.h" +#include "utils.h" + +int main(int argc, char *argv[]) +{ + handle_cli_args(argc, argv); + + init_log_system(); + + initscr(); + cbreak(); + noecho(); + start_color(); + set_escdelay(0); + keypad(stdscr, TRUE); + + init_pair(1, COLOR_WHITE, COLOR_BLACK); + init_pair(2, COLOR_GREEN, COLOR_BLACK); + init_pair(3, COLOR_RED, COLOR_BLACK); + init_pair(4, COLOR_BLACK, COLOR_GREEN); + init_pair(5, COLOR_BLACK, COLOR_RED); + init_pair(6, COLOR_BLUE, COLOR_BLACK); + init_pair(7, COLOR_BLACK, COLOR_BLUE); + + check_and_load_driver(); + open_primary_drm_device(); + populate_data(); + display_win(stdscr, root); + + endwin(); + close_primary_drm_device(); + close_log_system(); + + return 0; +} diff --git a/tools/displaytop/src/node.c b/tools/displaytop/src/node.c new file mode 100644 index 000000000..4f1ca33a1 --- /dev/null +++ b/tools/displaytop/src/node.c @@ -0,0 +1,140 @@ +/* + * Copyright © 2025 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 "node.h" + +Node* create_node(const char* name, void (*display_function)(WINDOW*, Node*, int*), Node* parent) +{ + Node *new_node = (Node *)malloc(sizeof(Node)); + strncpy(new_node->name, name, sizeof(new_node->name) - 1); + new_node->name[sizeof(new_node->name) - 1] = '\0'; + new_node->display_function = display_function; + new_node->parent = parent; + new_node->children = NULL; + new_node->children_size = 0; + return new_node; +} + +void add_child(Node *parent, Node *child) +{ + parent->children = (Node *)realloc(parent->children, sizeof(Node) * (parent->children_size + 1)); + parent->children[parent->children_size] = *child; + parent->children_size++; +} + +void free_tree(Node *root) +{ + int i; + + if (!root) + return; + + for (i = 0; i < root->children_size; i++) + { + free_tree(&root->children[i]); + } + free(root->children); + free(root); +} + +void print_path(Node *node) +{ + if (node->parent) + { + print_path(node->parent); + printw(" > %s", node->name); + } + else + { + printw("%s", node->name); + } +} + +char *get_path(Node *node) +{ + size_t path_size; + Node *current; + char *path; + char *new_path; + + if (!node) + { + return NULL; + } + + path_size = 1; + current = node; + while (current) + { + path_size += strlen(current->name) + 3; + current = current->parent; + } + + path = (char *)malloc(path_size); + if (!path) + { + return NULL; + } + path[0] = '\0'; + + current = node; + while (current) + { + new_path = (char *)malloc(strlen(current->name) + strlen(path) + 4); + if (!new_path) + { + free(path); + return NULL; + } + + if (path[0] == '\0') + { + snprintf(new_path, strlen(current->name) + 1, "%s", current->name); + } + else + { + snprintf(new_path, strlen(current->name) + strlen(path) + 4, "%s > %s", current->name, path); + } + + free(path); + path = new_path; + current = current->parent; + } + + return path; +} + +void count_nodes(Node *node, int *count) +{ + int i; + + if (node == NULL) + return; + + (*count)++; + + for (i = 0; i < node->children_size; i++) + { + count_nodes(&node->children[i], count); + } +} \ No newline at end of file diff --git a/tools/displaytop/src/populate.c b/tools/displaytop/src/populate.c new file mode 100644 index 000000000..fe4be42cf --- /dev/null +++ b/tools/displaytop/src/populate.c @@ -0,0 +1,34 @@ +/* + * Copyright © 2025 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 "data.h" +#include "populate.h" + +#define DRM_DEVICE "/dev/dri/card0" + +void populate_data(void) +{ + root = create_node("Display Top", display_summary, NULL); + + initialize_display_config(); +} diff --git a/tools/displaytop/src/populate_display_config.c b/tools/displaytop/src/populate_display_config.c new file mode 100644 index 000000000..a44efaf9e --- /dev/null +++ b/tools/displaytop/src/populate_display_config.c @@ -0,0 +1,295 @@ +/* + * Copyright © 2025 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 "populate.h" + +int create_crtc_nodes(drmModeRes *resources, Node *parent_node, int filter); +int create_connector_nodes(drmModeRes *resources, Node *parent_node, int filter); +int create_encoder_nodes(drmModeRes *resources, Node *parent_node, int filter); +int create_framebuffer_nodes(drmModeRes *resources, Node *parent_node, int filter); +int create_plane_nodes(Node *parent_node, int filter); + +int create_crtc_nodes(drmModeRes *resources, Node *parent_node, int filter) +{ + int crtc_count; + int i; + char crtc_name[10]; + Node *crtc_node; + drmModeCrtc *crtc; + + crtc_count = resources->count_crtcs; + log_message(LOG_INFO, "Creating CRTC nodes"); + + for (i = 0; i < crtc_count; ++i) + { + if (filter != -1 && i != filter) + { + continue; + } + + snprintf(crtc_name, sizeof(crtc_name), "CRTC%d", i % 100); + crtc_node = create_node(crtc_name, display_crtc, parent_node); + + crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]); + if (!crtc) + { + log_message(LOG_ERROR, "drmModeGetCrtc failed for CRTC %d", i % 100); + drmModeFreeResources(resources); + return 0; + } + + create_plane_nodes(crtc_node, i); + create_encoder_nodes(resources, crtc_node, i); + create_connector_nodes(resources, crtc_node, i); + + add_child(parent_node, crtc_node); + + drmModeFreeCrtc(crtc); + } + + return crtc_count; +} + +int create_connector_nodes(drmModeRes *resources, Node *parent_node, int filter) +{ + int connector_count; + int i; + char connector_name[15]; + Node *connector_node; + + connector_count = resources->count_connectors; + log_message(LOG_INFO, "Creating Connector nodes"); + + for (i = 0; i < connector_count; ++i) + { + if (filter != -1 && i != filter) + { + continue; + } + + snprintf(connector_name, sizeof(connector_name), "Connector%d", i % 100); + connector_node = create_node(connector_name, display_connector, parent_node); + add_child(parent_node, connector_node); + } + + return connector_count; +} + +int create_encoder_nodes(drmModeRes *resources, Node *parent_node, int filter) +{ + int encoder_count; + int i; + char encoder_name[15]; + Node *encoder_node; + + encoder_count = resources->count_encoders; + log_message(LOG_INFO, "Creating Encoder nodes"); + + for (i = 0; i < encoder_count; ++i) + { + if (filter != -1 && i != filter) + { + continue; + } + + snprintf(encoder_name, sizeof(encoder_name), "Encoder%d", i % 100); + encoder_node = create_node(encoder_name, display_encoder, parent_node); + add_child(parent_node, encoder_node); + } + + return encoder_count; +} + +int create_framebuffer_nodes(drmModeRes *resources, Node *parent_node, int filter) +{ + int framebuffer_count; + int i; + char framebuffer_name[15]; + Node *framebuffer_node; + + framebuffer_count = resources->count_fbs; + log_message(LOG_INFO, "Creating Framebuffer nodes for %d framebuffers", framebuffer_count); + + for (i = 0; i < framebuffer_count; ++i) + { + if (filter != -1 && i != filter) + { + continue; + } + + snprintf(framebuffer_name, sizeof(framebuffer_name), "Framebuffer%d", i % 100); + framebuffer_node = create_node(framebuffer_name, display_framebuffer, parent_node); + add_child(parent_node, framebuffer_node); + } + + return framebuffer_count; +} + +int create_plane_nodes(Node *parent_node, int filter) +{ + drmModePlaneRes *plane_resources; + int plane_count; + int i; + drmModePlane *plane; + char plane_name[10]; + char in_formats_name[15]; + char out_formats_name[15]; + Node *plane_node; + Node *in_formats_node; + Node *out_formats_node; + + plane_resources = drmModeGetPlaneResources(drm_fd); + if (!plane_resources) + { + log_message(LOG_ERROR, "drmModeGetPlaneResources failed"); + return 0; + } + + plane_count = plane_resources->count_planes; + log_message(LOG_INFO, "Creating Plane nodes"); + + for (i = 0; i < plane_count; ++i) + { + plane = drmModeGetPlane(drm_fd, plane_resources->planes[i]); + if (!plane) + { + log_message(LOG_ERROR, "drmModeGetPlane failed for Plane %d", i % 100); + drmModeFreePlaneResources(plane_resources); + return 0; + } + + if (filter != -1 && !(plane->possible_crtcs & (1 << filter))) + { + drmModeFreePlane(plane); + continue; + } + + snprintf(plane_name, sizeof(plane_name), "Plane%d", i % 100); + plane_node = create_node(plane_name, display_plane, parent_node); + + snprintf(in_formats_name, sizeof(in_formats_name), "IN_FORMATS%d", i % 100); + in_formats_node = create_node(in_formats_name, display_informats, plane_node); + add_child(plane_node, in_formats_node); + + snprintf(out_formats_name, sizeof(out_formats_name), "FORMATS%d", i % 100); + out_formats_node = create_node(out_formats_name, display_formats, plane_node); + add_child(plane_node, out_formats_node); + + add_child(parent_node, plane_node); + drmModeFreePlane(plane); + } + + drmModeFreePlaneResources(plane_resources); + return plane_count; +} + +void initialize_display_config(void) +{ + Node *display_config; + Node *crtc_nodes; + Node *plane_nodes; + Node *connector_nodes; + Node *encoder_nodes; + Node *framebuffer_nodes; + drmModeRes *resources; + + log_message(LOG_INFO, "Initializing Display Configuration"); + + display_config = create_node("Display Configuration", NULL, root); + if (!display_config) + { + log_message(LOG_ERROR, "Failed to create Display Configuration node"); + return; + } + + if (drm_fd < 0) + { + log_message(LOG_ERROR, "Failed to open primary DRM device"); + return; + } + + resources = drmModeGetResources(drm_fd); + if (!resources) + { + log_message(LOG_ERROR, "drmModeGetResources failed"); + return; + } + + /* Create CRTC nodes */ + crtc_nodes = create_node("CRTCs", NULL, display_config); + if (!crtc_nodes) + { + log_message(LOG_ERROR, "Failed to create CRTC nodes"); + drmModeFreeResources(resources); + return; + } + create_crtc_nodes(resources, crtc_nodes, -1); + add_child(display_config, crtc_nodes); + + /* Create Plane nodes */ + plane_nodes = create_node("Planes", NULL, display_config); + if (!plane_nodes) + { + log_message(LOG_ERROR, "Failed to create Plane nodes"); + drmModeFreeResources(resources); + return; + } + create_plane_nodes(plane_nodes, -1); + add_child(display_config, plane_nodes); + + /* Create Connector nodes */ + connector_nodes = create_node("Connectors", NULL, display_config); + if (!connector_nodes) + { + log_message(LOG_ERROR, "Failed to create Connector nodes"); + drmModeFreeResources(resources); + return; + } + create_connector_nodes(resources, connector_nodes, -1); + add_child(display_config, connector_nodes); + + /* Create Encoder nodes */ + encoder_nodes = create_node("Encoders", NULL, display_config); + if (!encoder_nodes) + { + log_message(LOG_ERROR, "Failed to create Encoder nodes"); + drmModeFreeResources(resources); + return; + } + create_encoder_nodes(resources, encoder_nodes, -1); + add_child(display_config, encoder_nodes); + + /* Initialize Framebuffers */ + framebuffer_nodes = create_node("Framebuffers", NULL, display_config); + if (!framebuffer_nodes) + { + log_message(LOG_ERROR, "Failed to create Framebuffer nodes"); + drmModeFreeResources(resources); + return; + } + create_framebuffer_nodes(resources, framebuffer_nodes, -1); + add_child(display_config, framebuffer_nodes); + + add_child(root, display_config); + drmModeFreeResources(resources); +} \ No newline at end of file diff --git a/tools/displaytop/src/utils.c b/tools/displaytop/src/utils.c new file mode 100644 index 000000000..ec8c8a649 --- /dev/null +++ b/tools/displaytop/src/utils.c @@ -0,0 +1,102 @@ +/* + * Copyright © 2025 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 "utils.h" + +char *read_file(const char *filename) +{ + FILE *file; + long length; + char *data; + + file = fopen(filename, "rb"); + if (!file) + { + log_message(LOG_ERROR, "File opening failed"); + return NULL; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + + data = (char *)malloc(length + 1); + if (data) + { + fread(data, 1, length, file); + data[length] = '\0'; + } + + fclose(file); + return data; +} + +void strip_whitespace(char *str) +{ + char *start; + char *end; + + start = str; + + while (isspace((unsigned char)*start)) + start++; + + if (*start == 0) + { + *str = 0; + return; + } + + end = start + strlen(start) - 1; + while (end > start && isspace((unsigned char)*end)) + end--; + + *(end + 1) = 0; + + if (start != str) + memmove(str, start, end - start + 2); +} + +void ensure_dump_directory(void) +{ + struct stat st; + int stat_result; + int mkdir_result; + + stat_result = stat(DUMP_DIR, &st); + if (stat_result == 0 && S_ISDIR(st.st_mode)) + { + log_message(LOG_INFO, "Directory %s already exists.\n", DUMP_DIR); + return; + } + + mkdir_result = mkdir(DUMP_DIR, 0755); + if (mkdir_result == 0) + { + log_message(LOG_INFO, "Directory %s created successfully.\n", DUMP_DIR); + } + else + { + log_message(LOG_ERROR, "mkdir failed: %s\n", strerror(errno)); + } +} \ No newline at end of file diff --git a/tools/displaytop/src/utils_cli.c b/tools/displaytop/src/utils_cli.c new file mode 100644 index 000000000..348169b4d --- /dev/null +++ b/tools/displaytop/src/utils_cli.c @@ -0,0 +1,70 @@ +/* + * Copyright © 2025 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 "utils.h" + +static void print_help(void) +{ + printf("Usage: sudo ./displaytop \n\n"); + + printf("for help: ./displaytop [OPTIONS]\n\n"); + printf("Options:\n"); + printf(" --help Show this help message and exit\n"); + printf(" --version Show version information\n"); + printf(" for more help read the documentation at the /docs folder. \n\n"); + + printf("ALL YOU NEED TO DO IS RUN THE TOOL WITH SUDO PERMISSION.\n\n"); + + printf("> Note:\n"); + printf("> - Running DisplayTop without `sudo` may result in some menu options not being visible.\n"); + printf("> - If the respective JSON files for MMIO and DPCD registers are not present, the corresponding menus will not be available.\n"); + printf("> - The Ftrace menu will not be able to provide register name information without the aforementioned JSON files.\n\n"); +} + +static void print_version(void) +{ + printf("DisplayTop v1.0.0\n"); +} + +void handle_cli_args(int argc, char *argv[]) +{ + if (argc > 1) + { + if (strcmp(argv[1], "--help") == 0) + { + print_help(); + exit(0); + } + else if (strcmp(argv[1], "--version") == 0) + { + print_version(); + exit(0); + } + else + { + printf("Unknown option: %s\n", argv[1]); + printf("Use --help for usage information.\n"); + exit(1); + } + } +} diff --git a/tools/displaytop/src/utils_display.c b/tools/displaytop/src/utils_display.c new file mode 100644 index 000000000..a1c9ad983 --- /dev/null +++ b/tools/displaytop/src/utils_display.c @@ -0,0 +1,195 @@ +/* + * Copyright © 2025 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 "utils.h" + +void draw_header(WINDOW *win, int winWidth, const char *title, const char *helper) +{ + werase(win); + box(win, 0, 0); + + wattron(win, A_BOLD); + mvwprintw(win, 1, 2, "%s", title); + mvwprintw(win, 1, winWidth - strlen(helper) - 2, "%s", helper); + wattroff(win, A_BOLD); + + mvwhline(win, 2, 1, ACS_HLINE, winWidth - 2); + wrefresh(win); +} + +void render_progress_bar(WINDOW *win, int current, int total) +{ + float progress; + int bar_start_y, bar_start_x, bar_width, filled, percent, i; + + if (total == 0) + return; + + progress = (float)current / total; + if (progress > 1.0f) + progress = 1.0f; + + bar_start_y = 10; + bar_start_x = 2; + bar_width = getmaxx(win) - 14; + if (bar_width < 1) + return; + + filled = (int)(progress * bar_width + 0.5f); + + wattron(win, A_BOLD); + mvwprintw(win, bar_start_y, bar_start_x, "["); + wattroff(win, A_BOLD); + + for (i = 0; i < bar_width; ++i) + { + if (i < filled) + { + wattron(win, COLOR_PAIR(7)); + waddch(win, ' '); + wattroff(win, COLOR_PAIR(7)); + } + else + { + wattron(win, COLOR_PAIR(2)); + waddch(win, ' '); + wattroff(win, COLOR_PAIR(2)); + } + } + + wattron(win, A_BOLD); + waddch(win, ']'); + percent = (int)(progress * 100.0f + 0.5f); + mvwprintw(win, bar_start_y, bar_start_x + bar_width + 2, "%3d%%", percent); + wattroff(win, A_BOLD); + + wrefresh(win); +} + +void print_bold_text(WINDOW *win, int line, int col, const char *text, ...) +{ + va_list args; + va_start(args, text); + + wattron(win, A_BOLD); + wmove(win, line, col); + vw_printw(win, text, args); + wattroff(win, A_BOLD); + + va_end(args); +} + +void print_dim_text(WINDOW *win, int line, int col, const char *text, ...) +{ + va_list args; + va_start(args, text); + + wattron(win, A_DIM); + wmove(win, line, col); + vw_printw(win, text, args); + wattroff(win, A_DIM); + + va_end(args); +} + +void print_red_text(WINDOW *win, int line, int col, const char *text, ...) +{ + va_list args; + va_start(args, text); + + wattron(win, COLOR_PAIR(3)); + wmove(win, line, col); + vw_printw(win, text, args); + wattroff(win, COLOR_PAIR(3)); + + va_end(args); +} + +void print_green_text(WINDOW *win, int line, int col, const char *text, ...) +{ + va_list args; + va_start(args, text); + + wattron(win, COLOR_PAIR(2)); + wmove(win, line, col); + vw_printw(win, text, args); + wattroff(win, COLOR_PAIR(2)); + + va_end(args); +} + +void set_string(char *dest, const char *src, size_t size) +{ + strncpy(dest, src, size - 1); + dest[size - 1] = '\0'; +} + +int check_size_change(WINDOW *win, int *height, int *width) +{ + int new_height, new_width; + getmaxyx(win, new_height, new_width); + move(1, 25); + if (new_height != *height || new_width != *width) + { + *height = new_height; + *width = new_width; + return 1; + } + return 0; +} + +void print_wrapped_text(WINDOW *pad, int *line, int start, int size, const char *text, bool enclose_with_pipe) +{ + const char *ptr; + int len, adjust_len; + + if (!text || !pad || !line || size <= 0) + return; + + ptr = text; + + while (*ptr) + { + len = ((int)strlen(ptr) > size) ? size - 1 : (int)strlen(ptr); + + /* To ensure truncation doesn't break any words */ + if (len < (int)strlen(ptr) && ptr[len] != ' ' && ptr[len - 1] != ' ') + { + adjust_len = len; + while (adjust_len > 0 && ptr[adjust_len] != ' ') + adjust_len--; + + if (adjust_len > 0) + len = adjust_len; + } + + if (enclose_with_pipe) + mvwprintw(pad, (*line)++, start, "| %-*.*s |", size, len, ptr); + else + mvwprintw(pad, (*line)++, start, "%-*.*s", size, len, ptr); + + ptr += len; + while (*ptr == ' ') + ptr++; + } +} \ No newline at end of file diff --git a/tools/displaytop/src/utils_driver.c b/tools/displaytop/src/utils_driver.c new file mode 100644 index 000000000..7b24d2f41 --- /dev/null +++ b/tools/displaytop/src/utils_driver.c @@ -0,0 +1,189 @@ +/* + * Copyright © 2025 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 "utils.h" + +static int is_driver_loaded(const char *driver) +{ + FILE *fp; + char buffer[1024]; + int found = 0; + + if (!driver) + { + log_message(LOG_ERROR, "Driver name is NULL."); + return 0; + } + + fp = fopen("/proc/modules", "r"); + if (!fp) + { + perror("Error opening /proc/modules"); + return 0; + } + + while (fgets(buffer, sizeof(buffer), fp)) + { + if (strncmp(buffer, driver, strlen(driver)) == 0 && buffer[strlen(driver)] == ' ') + { + found = 1; + break; + } + } + + fclose(fp); + return found; +} + +static void load_driver(const char *driver) +{ + pid_t pid; + + pid = fork(); + if (pid == 0) + { + execlp("sudo", "sudo", "modprobe", driver, NULL); + exit(EXIT_FAILURE); + } + else if (pid < 0) + { + perror("Failed to fork process for modprobe"); + } + else + { + wait(NULL); + } +} + +static void show_driver_menu(void) +{ + int key; + int rows, cols; + WINDOW *win; + const char *options[] = {"Load i915", "Load xe", "Exit"}; + int total_options = sizeof(options) / sizeof(options[0]); + int highlighted_index = 0; + int i; + int x_pos; + const char *driver; + + getmaxyx(stdscr, rows, cols); + + win = newwin(rows, cols, 0, 0); + if (!win) + { + log_message(LOG_ERROR, "Failed to create driver loading window."); + return; + } + + if (!has_colors() || !COLOR_PAIR(2)) + { + log_message(LOG_ERROR, "Colors not initialized properly."); + delwin(win); + return; + } + + cbreak(); + keypad(win, TRUE); + refresh(); + + wbkgd(win, COLOR_PAIR(2)); + box(win, 0, 0); + + while (1) + { + werase(win); + box(win, 0, 0); + mvwprintw(win, 2, (cols - 30) / 2, "Display Top - Select a Driver to Load"); + + for (i = 0; i < total_options; i++) + { + x_pos = (cols - strlen(options[i])) / 2; + if (x_pos < 0) + x_pos = 0; + + if (i == highlighted_index) + wattron(win, A_REVERSE); + + mvwprintw(win, rows / 2 + i, x_pos, "%s", options[i]); + wattroff(win, A_REVERSE); + } + + mvwprintw(win, rows - 2, 2, "[UP/DOWN] Navigate [ENTER] Select [ESC] Exit"); + wrefresh(win); + + key = wgetch(win); + + switch (key) + { + case KEY_UP: + highlighted_index = (highlighted_index == 0) ? total_options - 1 : highlighted_index - 1; + break; + case KEY_DOWN: + highlighted_index = (highlighted_index == total_options - 1) ? 0 : highlighted_index + 1; + break; + case '\n': + if (highlighted_index == 0 || highlighted_index == 1) + { + driver = (highlighted_index == 0) ? "i915" : "xe"; + werase(win); + box(win, 0, 0); + mvwprintw(win, rows / 2, (cols - 30) / 2, "Loading driver: %s...", driver); + wrefresh(win); + + load_driver(driver); + + werase(win); + box(win, 0, 0); + mvwprintw(win, rows / 2, (cols - 30) / 2, "Driver %s loaded successfully.", driver); + mvwprintw(win, rows - 2, 2, "Press any key to continue..."); + wrefresh(win); + + wgetch(win); + } + delwin(win); + return; + case 27: + delwin(win); + return; + default: + break; + } + } +} + +void check_and_load_driver(void) +{ + if (is_driver_loaded("i915")) + { + log_message(LOG_INFO, "i915 driver is already loaded."); + } + else if (is_driver_loaded("xe")) + { + log_message(LOG_INFO, "xe driver is already loaded."); + } + else + { + show_driver_menu(); + } +} diff --git a/tools/displaytop/src/utils_drm.c b/tools/displaytop/src/utils_drm.c new file mode 100644 index 000000000..fe5d8bc68 --- /dev/null +++ b/tools/displaytop/src/utils_drm.c @@ -0,0 +1,585 @@ +/* + * Copyright © 2025 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 "utils.h" + +int drm_fd = -1; +#define MODE_INFO_STRING_LEN 128 + +/** + * @brief Finds a DRM (Direct Rendering Manager) device path based on the specified type. + * + * This function searches the /dev/dri directory for a DRM device file that matches + * the specified type (primary or render). It returns the full path to the first matching + * device file found. + * + * @param primary A boolean indicating the type of DRM device to search for: + * - true: Search for a primary DRM device (prefix "card*"). + * - false: Search for a render DRM device (prefix "renderD*"). + * + * @return A dynamically allocated string containing the full path to the DRM device, + * or NULL if no matching device is found or an error occurs. The caller is + * responsible for freeing the returned string. + * + * @note Logs an error message if the /dev/dri directory cannot be opened, memory + * allocation fails, or no matching device is found. + */ +char *find_drm_device(bool primary) +{ + DIR *drm_dir; + const char *prefix; + char *device_path; + struct dirent *entry; + + drm_dir = opendir(DRM_DIR); + if (!drm_dir) + { + log_message(LOG_ERROR, "Failed to open /dev/dri directory"); + return NULL; + } + + prefix = primary ? DRM_PRIMARY_PREFIX : DRM_RENDER_PREFIX; + device_path = NULL; + + while ((entry = readdir(drm_dir)) != NULL) + { + if (strncmp(entry->d_name, prefix, strlen(prefix)) == 0) + { + size_t path_len = strlen(DRM_DIR) + strlen(entry->d_name) + 1; + device_path = malloc(path_len); + if (!device_path) + { + log_message(LOG_ERROR, "Failed to allocate memory for device path"); + break; + } + snprintf(device_path, path_len, "%s%s", DRM_DIR, entry->d_name); + break; + } + } + + closedir(drm_dir); + + if (!device_path) + log_message(LOG_ERROR, "No DRM %s devices found in %s", primary ? "primary (card*)" : "render (renderD*)", DRM_DIR); + + return device_path; +} + +/** + * @brief Opens the primary DRM (Direct Rendering Manager) device and configures it. + * + * This function locates and opens the primary DRM device, ensuring it is accessible + * for rendering operations. It also attempts to drop DRM master privileges if held + * and sets the client capability for universal planes. + * + * @note The function logs messages at different levels (error, warning, info) to + * indicate the success or failure of various operations. + * + * Steps performed: + * 1. Finds the DRM device path using `find_drm_device`. + * 2. Opens the DRM device with read/write permissions and the close-on-exec flag. + * 3. Logs an error if the device cannot be opened. + * 4. Attempts to drop DRM master privileges using `drmDropMaster` and logs the result. + * 5. Sets the client capability for universal planes using `drmSetClientCap`. + * + * @warning Ensure that the `find_drm_device` function and `log_message` utility + * are implemented correctly for this function to work as expected. + * + * @see drmDropMaster, drmSetClientCap + */ +void open_primary_drm_device(void) +{ + char *device_path = find_drm_device(true); + drm_fd = open(device_path, O_RDWR | O_CLOEXEC); + + if (drm_fd < 0) + log_message(LOG_ERROR, "Failed to open DRM device"); + + if (drmDropMaster(drm_fd) < 0) + log_message(LOG_WARNING, "Master already being held by something else!"); + else + log_message(LOG_INFO, "Dropped DRM master successfully"); + + drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); +} + +/** + * @brief Closes the primary DRM (Direct Rendering Manager) device. + * + * This function checks if the DRM device file descriptor (`drm_fd`) is valid + * (greater than or equal to 0). If valid, it closes the file descriptor, + * resets it to -1, and logs a message indicating that the DRM device has been + * successfully closed. + * + * @note Ensure that `drm_fd` is properly initialized before calling this + * function. Closing an already closed or invalid file descriptor may lead to + * undefined behavior. + */ +void close_primary_drm_device(void) +{ + if (drm_fd >= 0) + { + close(drm_fd); + drm_fd = -1; + log_message(LOG_INFO, "Closed DRM device"); + } +} + +const char *get_drm_object_type_name(uint32_t object_type) +{ + switch (object_type) + { + case DRM_MODE_OBJECT_CRTC: + return "CRTC"; + case DRM_MODE_OBJECT_CONNECTOR: + return "Connector"; + case DRM_MODE_OBJECT_ENCODER: + return "Encoder"; + case DRM_MODE_OBJECT_PLANE: + return "Plane"; + case DRM_MODE_OBJECT_PROPERTY: + return "Property"; + case DRM_MODE_OBJECT_FB: + return "Framebuffer"; + default: + return "Unknown"; + } +} + +const char *get_connector_type_name(uint32_t connector_type) +{ + switch (connector_type) + { + case DRM_MODE_CONNECTOR_VGA: + return "VGA"; + case DRM_MODE_CONNECTOR_DVII: + return "DVI-I"; + case DRM_MODE_CONNECTOR_DVID: + return "DVI-D"; + case DRM_MODE_CONNECTOR_DVIA: + return "DVI-A"; + case DRM_MODE_CONNECTOR_Composite: + return "Composite"; + case DRM_MODE_CONNECTOR_SVIDEO: + return "S-Video"; + case DRM_MODE_CONNECTOR_LVDS: + return "LVDS"; + case DRM_MODE_CONNECTOR_Component: + return "Component"; + case DRM_MODE_CONNECTOR_9PinDIN: + return "9-pin DIN"; + case DRM_MODE_CONNECTOR_DisplayPort: + return "DP"; + case DRM_MODE_CONNECTOR_HDMIA: + return "HDMI-A"; + case DRM_MODE_CONNECTOR_HDMIB: + return "HDMI-B"; + case DRM_MODE_CONNECTOR_TV: + return "TV"; + case DRM_MODE_CONNECTOR_eDP: + return "eDP"; + case DRM_MODE_CONNECTOR_VIRTUAL: + return "Virtual"; + case DRM_MODE_CONNECTOR_DSI: + return "DSI"; + case DRM_MODE_CONNECTOR_DPI: + return "DPI"; + default: + return "Unknown"; + } +} + +const char *get_encoder_type_name(uint32_t encoder_type) +{ + switch (encoder_type) + { + case DRM_MODE_ENCODER_NONE: + return "None"; + case DRM_MODE_ENCODER_DAC: + return "DAC"; + case DRM_MODE_ENCODER_TMDS: + return "TMDS"; + case DRM_MODE_ENCODER_LVDS: + return "LVDS"; + case DRM_MODE_ENCODER_TVDAC: + return "TVDAC"; + case DRM_MODE_ENCODER_VIRTUAL: + return "Virtual"; + case DRM_MODE_ENCODER_DSI: + return "DSI"; + case DRM_MODE_ENCODER_DPMST: + return "DPMST"; + default: + return "Unknown"; + } +} + +const char *get_format_str(uint32_t format) +{ + switch (format) + { + case DRM_FORMAT_INVALID: + return "INVALID"; + case DRM_FORMAT_C1: + return "C1"; + case DRM_FORMAT_C2: + return "C2"; + case DRM_FORMAT_C4: + return "C4"; + case DRM_FORMAT_C8: + return "C8"; + case DRM_FORMAT_D1: + return "D1"; + case DRM_FORMAT_D2: + return "D2"; + case DRM_FORMAT_D4: + return "D4"; + case DRM_FORMAT_D8: + return "D8"; + case DRM_FORMAT_R1: + return "R1"; + case DRM_FORMAT_R2: + return "R2"; + case DRM_FORMAT_R4: + return "R4"; + case DRM_FORMAT_R8: + return "R8"; + case DRM_FORMAT_R10: + return "R10"; + case DRM_FORMAT_R12: + return "R12"; + case DRM_FORMAT_R16: + return "R16"; + case DRM_FORMAT_RG88: + return "RG88"; + case DRM_FORMAT_GR88: + return "GR88"; + case DRM_FORMAT_RG1616: + return "RG1616"; + case DRM_FORMAT_GR1616: + return "GR1616"; + case DRM_FORMAT_RGB332: + return "RGB332"; + case DRM_FORMAT_BGR233: + return "BGR233"; + case DRM_FORMAT_XRGB4444: + return "XRGB4444"; + case DRM_FORMAT_XBGR4444: + return "XBGR4444"; + case DRM_FORMAT_RGBX4444: + return "RGBX4444"; + case DRM_FORMAT_BGRX4444: + return "BGRX4444"; + case DRM_FORMAT_ARGB4444: + return "ARGB4444"; + case DRM_FORMAT_ABGR4444: + return "ABGR4444"; + case DRM_FORMAT_RGBA4444: + return "RGBA4444"; + case DRM_FORMAT_BGRA4444: + return "BGRA4444"; + case DRM_FORMAT_XRGB1555: + return "XRGB1555"; + case DRM_FORMAT_XBGR1555: + return "XBGR1555"; + case DRM_FORMAT_RGBX5551: + return "RGBX5551"; + case DRM_FORMAT_BGRX5551: + return "BGRX5551"; + case DRM_FORMAT_ARGB1555: + return "ARGB1555"; + case DRM_FORMAT_ABGR1555: + return "ABGR1555"; + case DRM_FORMAT_RGBA5551: + return "RGBA5551"; + case DRM_FORMAT_BGRA5551: + return "BGRA5551"; + case DRM_FORMAT_RGB565: + return "RGB565"; + case DRM_FORMAT_BGR565: + return "BGR565"; + case DRM_FORMAT_RGB888: + return "RGB888"; + case DRM_FORMAT_BGR888: + return "BGR888"; + case DRM_FORMAT_XRGB8888: + return "XRGB8888"; + case DRM_FORMAT_XBGR8888: + return "XBGR8888"; + case DRM_FORMAT_RGBX8888: + return "RGBX8888"; + case DRM_FORMAT_BGRX8888: + return "BGRX8888"; + case DRM_FORMAT_ARGB8888: + return "ARGB8888"; + case DRM_FORMAT_ABGR8888: + return "ABGR8888"; + case DRM_FORMAT_RGBA8888: + return "RGBA8888"; + case DRM_FORMAT_BGRA8888: + return "BGRA8888"; + case DRM_FORMAT_XRGB2101010: + return "XRGB2101010"; + case DRM_FORMAT_XBGR2101010: + return "XBGR2101010"; + case DRM_FORMAT_RGBX1010102: + return "RGBX1010102"; + case DRM_FORMAT_BGRX1010102: + return "BGRX1010102"; + case DRM_FORMAT_ARGB2101010: + return "ARGB2101010"; + case DRM_FORMAT_ABGR2101010: + return "ABGR2101010"; + case DRM_FORMAT_RGBA1010102: + return "RGBA1010102"; + case DRM_FORMAT_BGRA1010102: + return "BGRA1010102"; + case DRM_FORMAT_XRGB16161616: + return "XRGB16161616"; + case DRM_FORMAT_XBGR16161616: + return "XBGR16161616"; + case DRM_FORMAT_ARGB16161616: + return "ARGB16161616"; + case DRM_FORMAT_ABGR16161616: + return "ABGR16161616"; + case DRM_FORMAT_XRGB16161616F: + return "XRGB16161616F"; + case DRM_FORMAT_XBGR16161616F: + return "XBGR16161616F"; + case DRM_FORMAT_ARGB16161616F: + return "ARGB16161616F"; + case DRM_FORMAT_ABGR16161616F: + return "ABGR16161616F"; + case DRM_FORMAT_AXBXGXRX106106106106: + return "AXBXGXRX106106106106"; + case DRM_FORMAT_YUYV: + return "YUYV"; + case DRM_FORMAT_YVYU: + return "YVYU"; + case DRM_FORMAT_UYVY: + return "UYVY"; + case DRM_FORMAT_VYUY: + return "VYUY"; + case DRM_FORMAT_AYUV: + return "AYUV"; + case DRM_FORMAT_AVUY8888: + return "AVUY8888"; + case DRM_FORMAT_XYUV8888: + return "XYUV8888"; + case DRM_FORMAT_XVUY8888: + return "XVUY8888"; + case DRM_FORMAT_VUY888: + return "VUY888"; + case DRM_FORMAT_VUY101010: + return "VUY101010"; + case DRM_FORMAT_Y210: + return "Y210"; + case DRM_FORMAT_Y212: + return "Y212"; + case DRM_FORMAT_Y216: + return "Y216"; + case DRM_FORMAT_Y410: + return "Y410"; + case DRM_FORMAT_Y412: + return "Y412"; + case DRM_FORMAT_Y416: + return "Y416"; + case DRM_FORMAT_XVYU2101010: + return "XVYU2101010"; + case DRM_FORMAT_XVYU12_16161616: + return "XVYU12_16161616"; + case DRM_FORMAT_XVYU16161616: + return "XVYU16161616"; + case DRM_FORMAT_Y0L0: + return "Y0L0"; + case DRM_FORMAT_X0L0: + return "X0L0"; + case DRM_FORMAT_Y0L2: + return "Y0L2"; + case DRM_FORMAT_X0L2: + return "X0L2"; + case DRM_FORMAT_YUV420_8BIT: + return "YUV420_8BIT"; + case DRM_FORMAT_YUV420_10BIT: + return "YUV420_10BIT"; + case DRM_FORMAT_XRGB8888_A8: + return "XRGB8888_A8"; + case DRM_FORMAT_XBGR8888_A8: + return "XBGR8888_A8"; + case DRM_FORMAT_RGBX8888_A8: + return "RGBX8888_A8"; + case DRM_FORMAT_BGRX8888_A8: + return "BGRX8888_A8"; + case DRM_FORMAT_RGB888_A8: + return "RGB888_A8"; + case DRM_FORMAT_BGR888_A8: + return "BGR888_A8"; + case DRM_FORMAT_RGB565_A8: + return "RGB565_A8"; + case DRM_FORMAT_BGR565_A8: + return "BGR565_A8"; + case DRM_FORMAT_NV12: + return "NV12"; + case DRM_FORMAT_NV21: + return "NV21"; + case DRM_FORMAT_NV16: + return "NV16"; + case DRM_FORMAT_NV61: + return "NV61"; + case DRM_FORMAT_NV24: + return "NV24"; + case DRM_FORMAT_NV42: + return "NV42"; + case DRM_FORMAT_NV15: + return "NV15"; + case DRM_FORMAT_NV20: + return "NV20"; + case DRM_FORMAT_NV30: + return "NV30"; + case DRM_FORMAT_P210: + return "P210"; + case DRM_FORMAT_P010: + return "P010"; + case DRM_FORMAT_P012: + return "P012"; + case DRM_FORMAT_P016: + return "P016"; + case DRM_FORMAT_P030: + return "P030"; + case DRM_FORMAT_Q410: + return "Q410"; + case DRM_FORMAT_Q401: + return "Q401"; + case DRM_FORMAT_YUV410: + return "YUV410"; + case DRM_FORMAT_YVU410: + return "YVU410"; + case DRM_FORMAT_YUV411: + return "YUV411"; + case DRM_FORMAT_YVU411: + return "YVU411"; + case DRM_FORMAT_YUV420: + return "YUV420"; + case DRM_FORMAT_YVU420: + return "YVU420"; + case DRM_FORMAT_YUV422: + return "YUV422"; + case DRM_FORMAT_YVU422: + return "YVU422"; + case DRM_FORMAT_YUV444: + return "YUV444"; + case DRM_FORMAT_YVU444: + return "YVU444"; + default: + return "unknown"; + } +} + +const char *get_basic_modifier_str(uint64_t modifier) +{ + switch (modifier) + { + case I915_FORMAT_MOD_X_TILED: + return "I915_FORMAT_MOD_X_TILED"; + case I915_FORMAT_MOD_Y_TILED: + return "I915_FORMAT_MOD_Y_TILED"; + case I915_FORMAT_MOD_Yf_TILED: + return "I915_FORMAT_MOD_Yf_TILED"; + case I915_FORMAT_MOD_Y_TILED_CCS: + return "I915_FORMAT_MOD_Y_TILED_CCS"; + case I915_FORMAT_MOD_Yf_TILED_CCS: + return "I915_FORMAT_MOD_Yf_TILED_CCS"; + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: + return "I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS"; + case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: + return "I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS"; + case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: + return "I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC"; + case I915_FORMAT_MOD_4_TILED: + return "I915_FORMAT_MOD_4_TILED"; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + return "I915_FORMAT_MOD_4_TILED_DG2_RC_CCS"; + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + return "I915_FORMAT_MOD_4_TILED_DG2_MC_CCS"; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + return "I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC"; + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS: + return "I915_FORMAT_MOD_4_TILED_MTL_RC_CCS"; + case I915_FORMAT_MOD_4_TILED_MTL_MC_CCS: + return "I915_FORMAT_MOD_4_TILED_MTL_MC_CCS"; + case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC: + return "I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC"; + case DRM_FORMAT_MOD_INVALID: + return "DRM_FORMAT_MOD_INVALID"; + case DRM_FORMAT_MOD_LINEAR: + return "DRM_FORMAT_MOD_LINEAR"; + case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: + return "DRM_FORMAT_MOD_SAMSUNG_64_32_TILE"; + case DRM_FORMAT_MOD_SAMSUNG_16_16_TILE: + return "DRM_FORMAT_MOD_SAMSUNG_16_16_TILE"; + case DRM_FORMAT_MOD_QCOM_COMPRESSED: + return "DRM_FORMAT_MOD_QCOM_COMPRESSED"; + case DRM_FORMAT_MOD_QCOM_TILED3: + return "DRM_FORMAT_MOD_QCOM_TILED3"; + case DRM_FORMAT_MOD_QCOM_TILED2: + return "DRM_FORMAT_MOD_QCOM_TILED2"; + case DRM_FORMAT_MOD_VIVANTE_TILED: + return "DRM_FORMAT_MOD_VIVANTE_TILED"; + case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: + return "DRM_FORMAT_MOD_VIVANTE_SUPER_TILED"; + case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED: + return "DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED"; + case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED: + return "DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED"; + case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: + return "DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED"; + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB: + return "DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB"; + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB: + return "DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB"; + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB: + return "DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB"; + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB: + return "DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB"; + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB: + return "DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB"; + case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB: + return "DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB"; + case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: + return "DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED"; + case DRM_FORMAT_MOD_BROADCOM_SAND32: + return "DRM_FORMAT_MOD_BROADCOM_SAND32"; + case DRM_FORMAT_MOD_BROADCOM_SAND64: + return "DRM_FORMAT_MOD_BROADCOM_SAND64"; + case DRM_FORMAT_MOD_BROADCOM_SAND128: + return "DRM_FORMAT_MOD_BROADCOM_SAND128"; + case DRM_FORMAT_MOD_BROADCOM_SAND256: + return "DRM_FORMAT_MOD_BROADCOM_SAND256"; + case DRM_FORMAT_MOD_BROADCOM_UIF: + return "DRM_FORMAT_MOD_BROADCOM_UIF"; + case DRM_FORMAT_MOD_ALLWINNER_TILED: + return "DRM_FORMAT_MOD_ALLWINNER_TILED"; + default: + return "unknown"; + } +} diff --git a/tools/displaytop/src/utils_search.c b/tools/displaytop/src/utils_search.c new file mode 100644 index 000000000..449347b51 --- /dev/null +++ b/tools/displaytop/src/utils_search.c @@ -0,0 +1,49 @@ +/* + * Copyright © 2025 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 "node.h" +#include "utils.h" + +void search_nodes(Node *node, const char *searchInput, Node *results) +{ + if (node == NULL) + { + return; + } + + if (strstr(node->name, searchInput) != NULL) + { + add_child(results, node); + return; + } + + for (int i = 0; i < node->children_size; i++) + { + search_nodes(&node->children[i], searchInput, results); + } + + return; +} \ No newline at end of file diff --git a/tools/meson.build b/tools/meson.build index f091af380..a5b02550d 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -121,3 +121,4 @@ endif subdir('i915-perf') subdir('xe-perf') subdir('null_state_gen') +subdir('displaytop') From patchwork Tue May 27 09:09:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [2/4] tools/displaytop: Add Display debugfs entry for display top From: Adith Narein T X-Patchwork-Id: 655764 Message-Id: <20250527090938.1765854-3-adith.narein.t@intel.com> To: intel-gfx-trybot@lists.freedesktop.org Cc: chaitanya.kumar.borah@intel.com, adith.narein.t@intel.com Date: Tue, 27 May 2025 14:39:32 +0530 This patch adds support for reading, displaying & dumping of display-related debugfs entries within the DisplayTop tool. Users can now browse and dump relevant i915 debugfs entries related to the display pipeline, enabling easier real-time inspection and analysis of driver-level state. Signed-off-by: Adith Narein T --- tools/displaytop/README.md | 1 + tools/displaytop/include/display.h | 2 + tools/displaytop/include/populate.h | 1 + tools/displaytop/meson.build | 2 + tools/displaytop/src/display_debugfs.c | 120 +++++++++++++++ tools/displaytop/src/populate.c | 1 + .../displaytop/src/populate_display_debugfs.c | 145 ++++++++++++++++++ 7 files changed, 272 insertions(+) create mode 100644 tools/displaytop/src/display_debugfs.c create mode 100644 tools/displaytop/src/populate_display_debugfs.c diff --git a/tools/displaytop/README.md b/tools/displaytop/README.md index 52128d34e..2c9005302 100644 --- a/tools/displaytop/README.md +++ b/tools/displaytop/README.md @@ -7,6 +7,7 @@ A terminal-based tool for monitoring and debugging the display pipeline. - Terminal UI using `ncurses` - Dump feature for all the menus - Real-time display Configuration +- view Live Display Debugfs in a terminal ui --- diff --git a/tools/displaytop/include/display.h b/tools/displaytop/include/display.h index db77ef785..dc9e9b4d2 100644 --- a/tools/displaytop/include/display.h +++ b/tools/displaytop/include/display.h @@ -43,4 +43,6 @@ void display_framebuffer(WINDOW *pad, Node *node, int *content_line); void display_summary(WINDOW *pad, Node *node, int *content_line); +void display_debugfs_file(WINDOW *pad, Node *node, int *content_line); + #endif \ No newline at end of file diff --git a/tools/displaytop/include/populate.h b/tools/displaytop/include/populate.h index 5a5241355..fee62bbb1 100644 --- a/tools/displaytop/include/populate.h +++ b/tools/displaytop/include/populate.h @@ -33,5 +33,6 @@ void populate_data(void); void initialize_display_config(void); +void initialize_display_debugfs(void); #endif \ No newline at end of file diff --git a/tools/displaytop/meson.build b/tools/displaytop/meson.build index 45720165a..09f14063d 100644 --- a/tools/displaytop/meson.build +++ b/tools/displaytop/meson.build @@ -59,6 +59,8 @@ if have_displaytop 'src/utils_driver.c', 'src/utils_drm.c', 'src/utils_search.c', + 'src/populate_display_debugfs.c', + 'src/display_debugfs.c', ) if meson.is_subproject() diff --git a/tools/displaytop/src/display_debugfs.c b/tools/displaytop/src/display_debugfs.c new file mode 100644 index 000000000..d195e2fbe --- /dev/null +++ b/tools/displaytop/src/display_debugfs.c @@ -0,0 +1,120 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" + +#define DEBUGFS_BASE_PATH "/sys/kernel/debug/dri/0000:00:02.0" + +static void build_full_path(Node *node, char *full_path, size_t size) +{ + int i; + int depth = 0; + char segments[20][256]; + char relative_path[1024] = {0}; + + node = node; + while (node && node->parent && strcmp(node->name, "Display Debugfs") != 0) + { + char *name = node->name; + size_t name_len = strlen(name); + + if (name_len > 0 && name[name_len - 1] == '*') + { + snprintf(segments[depth], sizeof(segments[depth]), "%.*s", (int)(name_len - 1), name); + } + else + { + snprintf(segments[depth], sizeof(segments[depth]), "%s", name); + } + + depth++; + node = node->parent; + } + + for (i = depth - 1; i >= 0; i--) + { + strncat(relative_path, "/", sizeof(relative_path) - strlen(relative_path) - 1); + strncat(relative_path, segments[i], sizeof(relative_path) - strlen(relative_path) - 1); + } + + snprintf(full_path, size, "%s%s", DEBUGFS_BASE_PATH, relative_path); +} + +void display_debugfs_file(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + int max_x = getmaxx(pad) - 4; + int start, break_point; + + FILE *file; + char temp[1024]; + char buffer[1024]; + char full_file_path[1024] = {0}; + + build_full_path(node, full_file_path, sizeof(full_file_path)); + + file = fopen(full_file_path, "r"); + if (!file) + { + print_red_text(pad, line++, 1, "Error opening file: %s", full_file_path); + *content_line = line; + return; + } + + while (fgets(buffer, sizeof(buffer), file)) + { + buffer[strcspn(buffer, "\n")] = '\0'; + + start = 0; + while (start < (int)strlen(buffer)) + { + break_point = start + max_x; + if (break_point < (int)strlen(buffer)) + { + while (break_point > start && !isspace(buffer[break_point])) + break_point--; + + if (break_point == start) + break_point = start + max_x; + } + else + { + break_point = strlen(buffer); + } + + strncpy(temp, buffer + start, break_point - start); + temp[break_point - start] = '\0'; + + mvwprintw(pad, line++, 2, "%s", temp); + + start = (buffer[break_point] == ' ') ? break_point + 1 : break_point; + } + } + + line++; + mvwprintw(pad, line++, 2, "-----------------EOF-------------------"); + + *content_line = line; + fclose(file); +} \ No newline at end of file diff --git a/tools/displaytop/src/populate.c b/tools/displaytop/src/populate.c index fe4be42cf..fd7fca48f 100644 --- a/tools/displaytop/src/populate.c +++ b/tools/displaytop/src/populate.c @@ -31,4 +31,5 @@ void populate_data(void) root = create_node("Display Top", display_summary, NULL); initialize_display_config(); + initialize_display_debugfs(); } diff --git a/tools/displaytop/src/populate_display_debugfs.c b/tools/displaytop/src/populate_display_debugfs.c new file mode 100644 index 000000000..8fb46d1db --- /dev/null +++ b/tools/displaytop/src/populate_display_debugfs.c @@ -0,0 +1,145 @@ +/* + * Copyright © 2025 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 "populate.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUGFS_DRI_PATH "/sys/kernel/debug/dri" + +static char *find_debugfs_dir(void) +{ + DIR *dir; + struct dirent *entry; + char *selected_path = NULL; + + dir = opendir(DEBUGFS_DRI_PATH); + if (!dir) + { + log_message(LOG_ERROR, "Failed to open debugfs dri directory"); + return NULL; + } + + while ((entry = readdir(dir)) != NULL) + { + if (!isdigit(entry->d_name[0])) + continue; + + selected_path = malloc(512); + if (!selected_path) + { + log_message(LOG_ERROR, "Memory allocation failed"); + closedir(dir); + return NULL; + } + + snprintf(selected_path, 512, "%s/%s", DEBUGFS_DRI_PATH, entry->d_name); + closedir(dir); + return selected_path; + } + + closedir(dir); + return NULL; +} + +static void populate_debugfs_recursive(Node *parent, const char *currentPath) +{ + DIR *dir; + struct dirent *entry; + char full_path[1024]; + char dir_name_with_asterisk[1024]; + struct stat pathStat; + Node *fileNode; + Node *dirNode; + + dir = opendir(currentPath); + if (!dir) + { + log_message(LOG_ERROR, "Failed to open directory: %s", currentPath); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + snprintf(full_path, sizeof(full_path), "%s/%s", currentPath, entry->d_name); + + if (stat(full_path, &pathStat) == -1) + { + log_message(LOG_WARNING, "Could not stat path: %s", full_path); + continue; + } + + if (S_ISREG(pathStat.st_mode)) + { + fileNode = create_node(entry->d_name, display_debugfs_file, parent); + add_child(parent, fileNode); + } + else if (S_ISDIR(pathStat.st_mode)) + { + snprintf(dir_name_with_asterisk, sizeof(dir_name_with_asterisk), "%s*", entry->d_name); + + dirNode = create_node(dir_name_with_asterisk, NULL, parent); + populate_debugfs_recursive(dirNode, full_path); + add_child(parent, dirNode); + } + } + + closedir(dir); +} + +static void populate_debugfs(Node *parent) +{ + char *selected_path = find_debugfs_dir(); + if (!selected_path) + return; + + populate_debugfs_recursive(parent, selected_path); + free(selected_path); +} + +void initialize_display_debugfs(void) +{ + Node *display_debugfs; + + display_debugfs = create_node("Display Debugfs", NULL, root); + populate_debugfs(display_debugfs); + + if (display_debugfs->children_size == 0) + { + free(display_debugfs); + return; + } + + add_child(root, display_debugfs); +} From patchwork Tue May 27 09:09:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [3/4] tools/displaytop: Add DPCD register read support From: Adith Narein T X-Patchwork-Id: 655765 Message-Id: <20250527090938.1765854-4-adith.narein.t@intel.com> To: intel-gfx-trybot@lists.freedesktop.org Cc: chaitanya.kumar.borah@intel.com, adith.narein.t@intel.com Date: Tue, 27 May 2025 14:39:33 +0530 This patch introduces DPCD (DisplayPort Configuration Data) register read support to the DisplayTop tool. It allows users to inspect the state of connected DisplayPort sinks via a terminal-based UI. The implementation uses the DRM subsystem’s drm_dp.h header to provide official DPCD register addresses and bitfield definitions. This ensures accurate decoding and interpretation of register contents. Features included in this patch: - DPCD register dump per connected DisplayPort output - Bitfield parsing and labeling using drm_dp.h macros Signed-off-by: Adith Narein T --- tools/displaytop/README.md | 1 + tools/displaytop/data/dpcd.json | 4661 ++++++++++++++++++++++++ tools/displaytop/include/data.h | 14 + tools/displaytop/include/display.h | 1 + tools/displaytop/include/populate.h | 1 + tools/displaytop/include/utils.h | 8 +- tools/displaytop/meson.build | 5 +- tools/displaytop/src/data.c | 15 +- tools/displaytop/src/display_DPCD.c | 277 ++ tools/displaytop/src/populate.c | 1 + tools/displaytop/src/populate_DPCD.c | 250 ++ tools/displaytop/src/utils.c | 66 + tools/displaytop/src/utils_registers.c | 97 + 13 files changed, 5394 insertions(+), 3 deletions(-) create mode 100644 tools/displaytop/data/dpcd.json create mode 100644 tools/displaytop/src/display_DPCD.c create mode 100644 tools/displaytop/src/populate_DPCD.c create mode 100644 tools/displaytop/src/utils_registers.c diff --git a/tools/displaytop/README.md b/tools/displaytop/README.md index 2c9005302..cd3b004d6 100644 --- a/tools/displaytop/README.md +++ b/tools/displaytop/README.md @@ -8,6 +8,7 @@ A terminal-based tool for monitoring and debugging the display pipeline. - Dump feature for all the menus - Real-time display Configuration - view Live Display Debugfs in a terminal ui +- Read & view DPCD register value & bitfield information --- diff --git a/tools/displaytop/data/dpcd.json b/tools/displaytop/data/dpcd.json new file mode 100644 index 000000000..c5d23023a --- /dev/null +++ b/tools/displaytop/data/dpcd.json @@ -0,0 +1,4661 @@ +{ + "DP_DPCD": { + "DP_DPCD_REV": { + "address": "0x000", + "bitfields": [ + { + "name": "DP_DPCD_REV_10", + "value": "0x10" + }, + { + "name": "DP_DPCD_REV_11", + "value": "0x11" + }, + { + "name": "DP_DPCD_REV_12", + "value": "0x12" + }, + { + "name": "DP_DPCD_REV_13", + "value": "0x13" + }, + { + "name": "DP_DPCD_REV_14", + "value": "0x14" + } + ] + }, + "DP_DPCD_MAX_ADD": { + "address": "0xfffff" + } + }, + "DP_MAX": { + "DP_MAX_LINK_RATE": { + "address": "0x001" + }, + "DP_MAX_LANE_COUNT": { + "address": "0x002", + "bitfields": [ + { + "name": "DP_MAX_LANE_COUNT_MASK", + "value": "0x1f" + }, + { + "name": "DP_TPS3_SUPPORTED", + "value": "(1 << 6) /* 1.2 */" + }, + { + "name": "DP_ENHANCED_FRAME_CAP", + "value": "(1 << 7)" + } + ] + }, + "DP_MAX_DOWNSPREAD": { + "address": "0x003", + "bitfields": [ + { + "name": "DP_MAX_DOWNSPREAD_0_5", + "value": "(1 << 0)" + }, + { + "name": "DP_STREAM_REGENERATION_STATUS_CAP", + "value": "(1 << 1) /* 2.0 */" + }, + { + "name": "DP_NO_AUX_HANDSHAKE_LINK_TRAINING", + "value": "(1 << 6)" + }, + { + "name": "DP_TPS4_SUPPORTED", + "value": "(1 << 7)" + } + ] + }, + "DP_MAX_DOWNSTREAM_PORTS": { + "address": "0x10" + }, + "DP_MAX_LINK_RATE_PHY_REPEATER": { + "address": "0xf0001" + }, + "DP_MAX_LANE_COUNT_PHY_REPEATER": { + "address": "0xf0004" + } + }, + "DP_NORP": { + "address": "0x004" + }, + "DP_DOWNSTREAMPORT_PRESENT": { + "address": "0x005", + "bitfields": [ + { + "name": "DP_DWN_STRM_PORT_PRESENT", + "value": "(1 << 0)" + }, + { + "name": "DP_DWN_STRM_PORT_TYPE_MASK", + "value": "0x06" + }, + { + "name": "DP_DWN_STRM_PORT_TYPE_DP", + "value": "(0 << 1)" + }, + { + "name": "DP_DWN_STRM_PORT_TYPE_ANALOG", + "value": "(1 << 1)" + }, + { + "name": "DP_DWN_STRM_PORT_TYPE_TMDS", + "value": "(2 << 1)" + }, + { + "name": "DP_DWN_STRM_PORT_TYPE_OTHER", + "value": "(3 << 1)" + }, + { + "name": "DP_FORMAT_CONVERSION", + "value": "(1 << 3)" + }, + { + "name": "DP_DETAILED_CAP_INFO_AVAILABLE", + "value": "(1 << 4) /* DPI */" + } + ] + }, + "DP_MAIN": { + "DP_MAIN_LINK_CHANNEL_CODING": { + "address": "0x006", + "bitfields": [ + { + "name": "DP_CAP_ANSI_8B10B", + "value": "(1 << 0)" + }, + { + "name": "DP_CAP_ANSI_128B132B", + "value": "(1 << 1) /* 2.0 */" + } + ] + }, + "DP_MAIN_LINK_CHANNEL_CODING_SET": { + "address": "0x108", + "bitfields": [ + { + "name": "DP_SET_ANSI_8B10B", + "value": "(1 << 0)" + }, + { + "name": "DP_SET_ANSI_128B132B", + "value": "(1 << 1)" + } + ] + }, + "DP_MAIN_LINK_CHANNEL_CODING_PHY_REPEATER": { + "address": "0xf0006", + "bitfields": [ + { + "name": "DP_PHY_REPEATER_128B132B_SUPPORTED", + "value": "(1 << 0)" + } + ] + } + }, + "DP_DOWN_STREAM_PORT_COUNT": { + "address": "0x007", + "bitfields": [ + { + "name": "DP_PORT_COUNT_MASK", + "value": "0x0f" + }, + { + "name": "DP_MSA_TIMING_PAR_IGNORED", + "value": "(1 << 6) /* eDP */" + }, + { + "name": "DP_OUI_SUPPORT", + "value": "(1 << 7)" + } + ] + }, + "DP_RECEIVE": { + "DP_RECEIVE_PORT_0_CAP_0": { + "address": "0x008", + "bitfields": [ + { + "name": "DP_LOCAL_EDID_PRESENT", + "value": "(1 << 1)" + }, + { + "name": "DP_ASSOCIATED_TO_PRECEDING_PORT", + "value": "(1 << 2)" + }, + { + "name": "DP_HBLANK_EXPANSION_CAPABLE", + "value": "(1 << 3)" + } + ] + }, + "DP_RECEIVE_PORT_0_BUFFER_SIZE": { + "address": "0x009" + }, + "DP_RECEIVE_PORT_1_CAP_0": { + "address": "0x00a" + }, + "DP_RECEIVE_PORT_1_BUFFER_SIZE": { + "address": "0x00b" + } + }, + "DP_I2C": { + "DP_I2C_SPEED_CAP": { + "address": "0x00c", + "bitfields": [ + { + "name": "DP_I2C_SPEED_1K", + "value": "0x01" + }, + { + "name": "DP_I2C_SPEED_5K", + "value": "0x02" + }, + { + "name": "DP_I2C_SPEED_10K", + "value": "0x04" + }, + { + "name": "DP_I2C_SPEED_100K", + "value": "0x08" + }, + { + "name": "DP_I2C_SPEED_400K", + "value": "0x10" + }, + { + "name": "DP_I2C_SPEED_1M", + "value": "0x20" + } + ] + }, + "DP_I2C_SPEED_CONTROL_STATUS": { + "address": "0x109" + } + }, + "DP_EDP": { + "DP_EDP_CONFIGURATION_CAP": { + "address": "0x00d", + "bitfields": [ + { + "name": "DP_ALTERNATE_SCRAMBLER_RESET_CAP", + "value": "(1 << 0)" + }, + { + "name": "DP_FRAMING_CHANGE_CAP", + "value": "(1 << 1)" + }, + { + "name": "DP_DPCD_DISPLAY_CONTROL_CAPABLE", + "value": "(1 << 3) /* edp v1.2 or higher */" + } + ] + }, + "DP_EDP_CONFIGURATION_SET": { + "address": "0x10a", + "bitfields": [ + { + "name": "DP_ALTERNATE_SCRAMBLER_RESET_ENABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_FRAMING_CHANGE_ENABLE", + "value": "(1 << 1)" + }, + { + "name": "DP_PANEL_SELF_TEST_ENABLE", + "value": "(1 << 7)" + } + ] + }, + "DP_EDP_DPCD_REV": { + "address": "0x700", + "bitfields": [ + { + "name": "DP_EDP_11", + "value": "0x00" + }, + { + "name": "DP_EDP_12", + "value": "0x01" + }, + { + "name": "DP_EDP_13", + "value": "0x02" + }, + { + "name": "DP_EDP_14", + "value": "0x03" + }, + { + "name": "DP_EDP_14a", + "value": "0x04 /* eDP 1.4a */" + }, + { + "name": "DP_EDP_14b", + "value": "0x05 /* eDP 1.4b */" + }, + { + "name": "DP_EDP_15", + "value": "0x06 /* eDP 1.5 */" + } + ] + }, + "DP_EDP_GENERAL_CAP_1": { + "address": "0x701", + "bitfields": [ + { + "name": "DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP", + "value": "(1 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_PIN_ENABLE_CAP", + "value": "(1 << 1)" + }, + { + "name": "DP_EDP_BACKLIGHT_AUX_ENABLE_CAP", + "value": "(1 << 2)" + }, + { + "name": "DP_EDP_PANEL_SELF_TEST_PIN_ENABLE_CAP", + "value": "(1 << 3)" + }, + { + "name": "DP_EDP_PANEL_SELF_TEST_AUX_ENABLE_CAP", + "value": "(1 << 4)" + }, + { + "name": "DP_EDP_FRC_ENABLE_CAP", + "value": "(1 << 5)" + }, + { + "name": "DP_EDP_COLOR_ENGINE_CAP", + "value": "(1 << 6)" + }, + { + "name": "DP_EDP_SET_POWER_CAP", + "value": "(1 << 7)" + } + ] + }, + "DP_EDP_BACKLIGHT_ADJUSTMENT_CAP": { + "address": "0x702", + "bitfields": [ + { + "name": "DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP", + "value": "(1 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP", + "value": "(1 << 1)" + }, + { + "name": "DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT", + "value": "(1 << 2)" + }, + { + "name": "DP_EDP_BACKLIGHT_AUX_PWM_PRODUCT_CAP", + "value": "(1 << 3)" + }, + { + "name": "DP_EDP_BACKLIGHT_FREQ_PWM_PIN_PASSTHRU_CAP", + "value": "(1 << 4)" + }, + { + "name": "DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP", + "value": "(1 << 5)" + }, + { + "name": "DP_EDP_DYNAMIC_BACKLIGHT_CAP", + "value": "(1 << 6)" + }, + { + "name": "DP_EDP_VBLANK_BACKLIGHT_UPDATE_CAP", + "value": "(1 << 7)" + } + ] + }, + "DP_EDP_GENERAL_CAP_2": { + "address": "0x703", + "bitfields": [ + { + "name": "DP_EDP_OVERDRIVE_ENGINE_ENABLED", + "value": "(1 << 0)" + }, + { + "name": "DP_EDP_PANEL_LUMINANCE_CONTROL_CAPABLE", + "value": "(1 << 4)" + } + ] + }, + "DP_EDP_GENERAL_CAP_3": { + "address": "0x704", + "bitfields": [ + { + "name": "DP_EDP_X_REGION_CAP_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_EDP_X_REGION_CAP_SHIFT", + "value": "0" + }, + { + "name": "DP_EDP_Y_REGION_CAP_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_EDP_Y_REGION_CAP_SHIFT", + "value": "4" + } + ] + }, + "DP_EDP_DISPLAY_CONTROL_REGISTER": { + "address": "0x720", + "bitfields": [ + { + "name": "DP_EDP_BACKLIGHT_ENABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_EDP_BLACK_VIDEO_ENABLE", + "value": "(1 << 1)" + }, + { + "name": "DP_EDP_FRC_ENABLE", + "value": "(1 << 2)" + }, + { + "name": "DP_EDP_COLOR_ENGINE_ENABLE", + "value": "(1 << 3)" + }, + { + "name": "DP_EDP_VBLANK_BACKLIGHT_UPDATE_ENABLE", + "value": "(1 << 7)" + } + ] + }, + "DP_EDP_BACKLIGHT_MODE_SET_REGISTER": { + "address": "0x721", + "bitfields": [ + { + "name": "DP_EDP_BACKLIGHT_CONTROL_MODE_MASK", + "value": "(3 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_CONTROL_MODE_PWM", + "value": "(0 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET", + "value": "(1 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD", + "value": "(2 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT", + "value": "(3 << 0)" + }, + { + "name": "DP_EDP_BACKLIGHT_FREQ_PWM_PIN_PASSTHRU_ENABLE", + "value": "(1 << 2)" + }, + { + "name": "DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE", + "value": "(1 << 3)" + }, + { + "name": "DP_EDP_DYNAMIC_BACKLIGHT_ENABLE", + "value": "(1 << 4)" + }, + { + "name": "DP_EDP_REGIONAL_BACKLIGHT_ENABLE", + "value": "(1 << 5)" + }, + { + "name": "DP_EDP_UPDATE_REGION_BRIGHTNESS", + "value": "(1 << 6) /* eDP 1.4 */" + }, + { + "name": "DP_EDP_PANEL_LUMINANCE_CONTROL_ENABLE", + "value": "(1 << 7)" + } + ] + }, + "DP_EDP_BACKLIGHT_BRIGHTNESS_MSB": { + "address": "0x722" + }, + "DP_EDP_BACKLIGHT_BRIGHTNESS_LSB": { + "address": "0x723" + }, + "DP_EDP_PWMGEN_BIT_COUNT": { + "address": "0x724" + }, + "DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN": { + "address": "0x725" + }, + "DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX": { + "address": "0x726", + "bitfields": [ + { + "name": "DP_EDP_PWMGEN_BIT_COUNT_MASK", + "value": "(0x1f << 0)" + } + ] + }, + "DP_EDP_BACKLIGHT_CONTROL_STATUS": { + "address": "0x727" + }, + "DP_EDP_BACKLIGHT_FREQ_SET": { + "address": "0x728", + "bitfields": [ + { + "name": "DP_EDP_BACKLIGHT_FREQ_BASE_KHZ", + "value": "27000" + } + ] + }, + "DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MSB": { + "address": "0x72a" + }, + "DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MID": { + "address": "0x72b" + }, + "DP_EDP_BACKLIGHT_FREQ_CAP_MIN_LSB": { + "address": "0x72c" + }, + "DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MSB": { + "address": "0x72d" + }, + "DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MID": { + "address": "0x72e" + }, + "DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB": { + "address": "0x72f" + }, + "DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET": { + "address": "0x732" + }, + "DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET": { + "address": "0x733" + }, + "DP_EDP_PANEL_TARGET_LUMINANCE_VALUE": { + "address": "0x734" + }, + "DP_EDP_REGIONAL_BACKLIGHT_BASE": { + "address": "0x740" + }, + "DP_EDP_REGIONAL_BACKLIGHT_0": { + "address": "0x741" + }, + "DP_EDP_MSO_LINK_CAPABILITIES": { + "address": "0x7a4", + "bitfields": [ + { + "name": "DP_EDP_MSO_NUMBER_OF_LINKS_MASK", + "value": "(7 << 0)" + }, + { + "name": "DP_EDP_MSO_NUMBER_OF_LINKS_SHIFT", + "value": "0" + }, + { + "name": "DP_EDP_MSO_INDEPENDENT_LINK_BIT", + "value": "(1 << 3)" + } + ] + } + }, + "DP_TRAINING": { + "DP_TRAINING_AUX_RD_INTERVAL": { + "address": "0x00e", + "bitfields": [ + { + "name": "DP_TRAINING_AUX_RD_MASK", + "value": "0x7F /* DP 1.3 */" + }, + { + "name": "DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT", + "value": "(1 << 7) /* DP 1.3 */" + } + ] + }, + "DP_TRAINING_PATTERN_SET": { + "address": "0x102", + "bitfields": [ + { + "name": "DP_TRAINING_PATTERN_DISABLE", + "value": "0" + }, + { + "name": "DP_TRAINING_PATTERN_1", + "value": "1" + }, + { + "name": "DP_TRAINING_PATTERN_2", + "value": "2" + }, + { + "name": "DP_TRAINING_PATTERN_2_CDS", + "value": "3\t /* 2.0 E11 */" + }, + { + "name": "DP_TRAINING_PATTERN_3", + "value": "3\t /* 1.2 */" + }, + { + "name": "DP_TRAINING_PATTERN_4", + "value": "7 /* 1.4 */" + }, + { + "name": "DP_TRAINING_PATTERN_MASK", + "value": "0x3" + }, + { + "name": "DP_TRAINING_PATTERN_MASK_1_4", + "value": "0xf" + }, + { + "name": "DP_LINK_QUAL_PATTERN_11_DISABLE", + "value": "(0 << 2)" + }, + { + "name": "DP_LINK_QUAL_PATTERN_11_D10_2", + "value": "(1 << 2)" + }, + { + "name": "DP_LINK_QUAL_PATTERN_11_ERROR_RATE", + "value": "(2 << 2)" + }, + { + "name": "DP_LINK_QUAL_PATTERN_11_PRBS7", + "value": "(3 << 2)" + }, + { + "name": "DP_LINK_QUAL_PATTERN_11_MASK", + "value": "(3 << 2)" + }, + { + "name": "DP_RECOVERED_CLOCK_OUT_EN", + "value": "(1 << 4)" + }, + { + "name": "DP_LINK_SCRAMBLING_DISABLE", + "value": "(1 << 5)" + }, + { + "name": "DP_SYMBOL_ERROR_COUNT_BOTH", + "value": "(0 << 6)" + }, + { + "name": "DP_SYMBOL_ERROR_COUNT_DISPARITY", + "value": "(1 << 6)" + }, + { + "name": "DP_SYMBOL_ERROR_COUNT_SYMBOL", + "value": "(2 << 6)" + }, + { + "name": "DP_SYMBOL_ERROR_COUNT_MASK", + "value": "(3 << 6)" + } + ] + }, + "DP_TRAINING_LANE0_SET": { + "address": "0x103" + }, + "DP_TRAINING_LANE1_SET": { + "address": "0x104" + }, + "DP_TRAINING_LANE2_SET": { + "address": "0x105" + }, + "DP_TRAINING_LANE3_SET": { + "address": "0x106", + "bitfields": [ + { + "name": "DP_TRAIN_VOLTAGE_SWING_MASK", + "value": "0x3" + }, + { + "name": "DP_TRAIN_VOLTAGE_SWING_SHIFT", + "value": "0" + }, + { + "name": "DP_TRAIN_MAX_SWING_REACHED", + "value": "(1 << 2)" + }, + { + "name": "DP_TRAIN_VOLTAGE_SWING_LEVEL_0", + "value": "(0 << 0)" + }, + { + "name": "DP_TRAIN_VOLTAGE_SWING_LEVEL_1", + "value": "(1 << 0)" + }, + { + "name": "DP_TRAIN_VOLTAGE_SWING_LEVEL_2", + "value": "(2 << 0)" + }, + { + "name": "DP_TRAIN_VOLTAGE_SWING_LEVEL_3", + "value": "(3 << 0)" + }, + { + "name": "DP_TRAIN_PRE_EMPHASIS_MASK", + "value": "(3 << 3)" + }, + { + "name": "DP_TRAIN_PRE_EMPH_LEVEL_0", + "value": "(0 << 3)" + }, + { + "name": "DP_TRAIN_PRE_EMPH_LEVEL_1", + "value": "(1 << 3)" + }, + { + "name": "DP_TRAIN_PRE_EMPH_LEVEL_2", + "value": "(2 << 3)" + }, + { + "name": "DP_TRAIN_PRE_EMPH_LEVEL_3", + "value": "(3 << 3)" + }, + { + "name": "DP_TRAIN_PRE_EMPHASIS_SHIFT", + "value": "3" + }, + { + "name": "DP_TRAIN_MAX_PRE_EMPHASIS_REACHED", + "value": "(1 << 5)" + }, + { + "name": "DP_TX_FFE_PRESET_VALUE_MASK", + "value": "(0xf << 0) /* 2.0 128b/132b Link Layer */" + } + ] + }, + "DP_TRAINING_LANE0_1_SET2": { + "address": "0x10f" + }, + "DP_TRAINING_LANE2_3_SET2": { + "address": "0x110", + "bitfields": [ + { + "name": "DP_LANE02_POST_CURSOR2_SET_MASK", + "value": "(3 << 0)" + }, + { + "name": "DP_LANE02_MAX_POST_CURSOR2_REACHED", + "value": "(1 << 2)" + }, + { + "name": "DP_LANE13_POST_CURSOR2_SET_MASK", + "value": "(3 << 4)" + }, + { + "name": "DP_LANE13_MAX_POST_CURSOR2_REACHED", + "value": "(1 << 6)" + } + ] + }, + "DP_TRAINING_PATTERN_SET_PHY_REPEATER1": { + "address": "0xf0010" + }, + "DP_TRAINING_LANE0_SET_PHY_REPEATER1": { + "address": "0xf0011" + }, + "DP_TRAINING_LANE1_SET_PHY_REPEATER1": { + "address": "0xf0012" + }, + "DP_TRAINING_LANE2_SET_PHY_REPEATER1": { + "address": "0xf0013" + }, + "DP_TRAINING_LANE3_SET_PHY_REPEATER1": { + "address": "0xf0014" + }, + "DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1": { + "address": "0xf0020" + } + }, + "DP_ADAPTER": { + "DP_ADAPTER_CAP": { + "address": "0x00f", + "bitfields": [ + { + "name": "DP_FORCE_LOAD_SENSE_CAP", + "value": "(1 << 0)" + }, + { + "name": "DP_ALTERNATE_I2C_PATTERN_CAP", + "value": "(1 << 1)" + } + ] + }, + "DP_ADAPTER_CTRL": { + "address": "0x1a0", + "bitfields": [ + { + "name": "DP_ADAPTER_CTRL_FORCE_LOAD_SENSE", + "value": "(1 << 0)" + } + ] + } + }, + "DP_SUPPORTED_LINK_RATES": { + "address": "0x010", + "bitfields": [ + { + "name": "DP_MAX_SUPPORTED_RATES", + "value": "8\t /* 16-bit little-endian */" + } + ] + }, + "DP_FAUX_CAP": { + "address": "0x020", + "bitfields": [ + { + "name": "DP_FAUX_CAP_1", + "value": "(1 << 0)" + } + ] + }, + "DP_SINK": { + "DP_SINK_VIDEO_FALLBACK_FORMATS": { + "address": "0x020", + "bitfields": [ + { + "name": "DP_FALLBACK_1024x768_60HZ_24BPP", + "value": "(1 << 0)" + }, + { + "name": "DP_FALLBACK_1280x720_60HZ_24BPP", + "value": "(1 << 1)" + }, + { + "name": "DP_FALLBACK_1920x1080_60HZ_24BPP", + "value": "(1 << 2)" + } + ] + }, + "DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP": { + "address": "0x02f", + "bitfields": [ + { + "name": "DP_AUX_FRAME_SYNC_CAP", + "value": "(1 << 0)" + } + ] + }, + "DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF": { + "address": "0x117", + "bitfields": [ + { + "name": "DP_AUX_FRAME_SYNC_ENABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_IRQ_HPD_ENABLE", + "value": "(1 << 1)" + } + ] + }, + "DP_SINK_COUNT": { + "address": "0x200", + "bitfields": [ + { + "name": "DP_SINK_CP_READY", + "value": "(1 << 6)" + } + ] + }, + "DP_SINK_STATUS": { + "address": "0x205", + "bitfields": [ + { + "name": "DP_RECEIVE_PORT_0_STATUS", + "value": "(1 << 0)" + }, + { + "name": "DP_RECEIVE_PORT_1_STATUS", + "value": "(1 << 1)" + }, + { + "name": "DP_STREAM_REGENERATION_STATUS", + "value": "(1 << 2) /* 2.0 */" + }, + { + "name": "DP_INTRA_HOP_AUX_REPLY_INDICATION", + "value": "(1 << 3) /* 2.0 */" + } + ] + }, + "DP_SINK_OUI": { + "address": "0x400" + }, + "DP_SINK_COUNT_ESI": { + "address": "0x2002" + }, + "DP_SINK_STATUS_ESI": { + "address": "0x200f" + }, + "DP_SINK_DEVICE_PR_AND_FRAME_LOCK_STATUS": { + "address": "0x2022", + "bitfields": [ + { + "name": "DP_SINK_DEVICE_PANEL_REPLAY_STATUS_MASK", + "value": "(7 << 0)" + }, + { + "name": "DP_SINK_FRAME_LOCKED_SHIFT", + "value": "3" + }, + { + "name": "DP_SINK_FRAME_LOCKED_MASK", + "value": "(3 << 3)" + }, + { + "name": "DP_SINK_FRAME_LOCKED_STATUS_VALID_SHIFT", + "value": "5" + }, + { + "name": "DP_SINK_FRAME_LOCKED_STATUS_VALID_MASK", + "value": "(1 << 5)" + } + ] + }, + "DP_SINK_EVENT_NOTIFY": { + "address": "0x30" + } + }, + "DP_MSTM": { + "DP_MSTM_CAP": { + "address": "0x021", + "bitfields": [ + { + "name": "DP_MST_CAP", + "value": "(1 << 0)" + }, + { + "name": "DP_SINGLE_STREAM_SIDEBAND_MSG", + "value": "(1 << 1) /* 2.0 */" + } + ] + }, + "DP_MSTM_CTRL": { + "address": "0x111", + "bitfields": [ + { + "name": "DP_MST_EN", + "value": "(1 << 0)" + }, + { + "name": "DP_UP_REQ_EN", + "value": "(1 << 1)" + }, + { + "name": "DP_UPSTREAM_IS_SRC", + "value": "(1 << 2)" + } + ] + } + }, + "DP_NUMBER_OF_AUDIO_ENDPOINTS": { + "address": "0x022" + }, + "DP_AV_GRANULARITY": { + "address": "0x023", + "bitfields": [ + { + "name": "DP_AG_FACTOR_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_AG_FACTOR_3MS", + "value": "(0 << 0)" + }, + { + "name": "DP_AG_FACTOR_2MS", + "value": "(1 << 0)" + }, + { + "name": "DP_AG_FACTOR_1MS", + "value": "(2 << 0)" + }, + { + "name": "DP_AG_FACTOR_500US", + "value": "(3 << 0)" + }, + { + "name": "DP_AG_FACTOR_200US", + "value": "(4 << 0)" + }, + { + "name": "DP_AG_FACTOR_100US", + "value": "(5 << 0)" + }, + { + "name": "DP_AG_FACTOR_10US", + "value": "(6 << 0)" + }, + { + "name": "DP_AG_FACTOR_1US", + "value": "(7 << 0)" + }, + { + "name": "DP_VG_FACTOR_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_VG_FACTOR_3MS", + "value": "(0 << 4)" + }, + { + "name": "DP_VG_FACTOR_2MS", + "value": "(1 << 4)" + }, + { + "name": "DP_VG_FACTOR_1MS", + "value": "(2 << 4)" + }, + { + "name": "DP_VG_FACTOR_500US", + "value": "(3 << 4)" + }, + { + "name": "DP_VG_FACTOR_200US", + "value": "(4 << 4)" + }, + { + "name": "DP_VG_FACTOR_100US", + "value": "(5 << 4)" + } + ] + }, + "DP_AUD": { + "DP_AUD_DEC_LAT0": { + "address": "0x024" + }, + "DP_AUD_DEC_LAT1": { + "address": "0x025" + }, + "DP_AUD_PP_LAT0": { + "address": "0x026" + }, + "DP_AUD_PP_LAT1": { + "address": "0x027" + }, + "DP_AUD_DEL_INS0": { + "address": "0x02b" + }, + "DP_AUD_DEL_INS1": { + "address": "0x02c" + }, + "DP_AUD_DEL_INS2": { + "address": "0x02d" + } + }, + "DP_VID": { + "DP_VID_INTER_LAT": { + "address": "0x028" + }, + "DP_VID_PROG_LAT": { + "address": "0x029" + } + }, + "DP_REP_LAT": { + "address": "0x02a" + }, + "DP_RECEIVER": { + "DP_RECEIVER_ALPM_CAP": { + "address": "0x02e", + "bitfields": [ + { + "name": "DP_ALPM_CAP", + "value": "(1 << 0)" + }, + { + "name": "DP_ALPM_PM_STATE_2A_SUPPORT", + "value": "(1 << 1) /* eDP 1.5 */" + }, + { + "name": "DP_ALPM_AUX_LESS_CAP", + "value": "(1 << 2) /* eDP 1.5 */" + } + ] + }, + "DP_RECEIVER_ALPM_CONFIG": { + "address": "0x116", + "bitfields": [ + { + "name": "DP_ALPM_ENABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE", + "value": "(1 << 1) /* eDP 1.5 */" + }, + { + "name": "DP_ALPM_MODE_AUX_LESS", + "value": "(1 << 2) /* eDP 1.5 */" + } + ] + }, + "DP_RECEIVER_ALPM_STATUS": { + "address": "0x200b", + "bitfields": [ + { + "name": "DP_ALPM_LOCK_TIMEOUT_ERROR", + "value": "(1 << 0)" + } + ] + }, + "DP_RECEIVER_CAP_SIZE": { + "address": "0xf" + } + }, + "DP_GUID": { + "address": "0x030" + }, + "DP_DSC": { + "DP_DSC_SUPPORT": { + "address": "0x060", + "bitfields": [ + { + "name": "DP_DSC_DECOMPRESSION_IS_SUPPORTED", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_PASSTHROUGH_IS_SUPPORTED", + "value": "(1 << 1)" + }, + { + "name": "DP_DSC_DYNAMIC_PPS_UPDATE_SUPPORT_COMP_TO_COMP", + "value": "(1 << 2)" + }, + { + "name": "DP_DSC_DYNAMIC_PPS_UPDATE_SUPPORT_UNCOMP_TO_COMP", + "value": "(1 << 3)" + } + ] + }, + "DP_DSC_REV": { + "address": "0x061", + "bitfields": [ + { + "name": "DP_DSC_MAJOR_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_DSC_MINOR_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_DSC_MAJOR_SHIFT", + "value": "0" + }, + { + "name": "DP_DSC_MINOR_SHIFT", + "value": "4" + } + ] + }, + "DP_DSC_RC_BUF_BLK_SIZE": { + "address": "0x062", + "bitfields": [ + { + "name": "DP_DSC_RC_BUF_BLK_SIZE_1", + "value": "0x0" + }, + { + "name": "DP_DSC_RC_BUF_BLK_SIZE_4", + "value": "0x1" + }, + { + "name": "DP_DSC_RC_BUF_BLK_SIZE_16", + "value": "0x2" + }, + { + "name": "DP_DSC_RC_BUF_BLK_SIZE_64", + "value": "0x3" + } + ] + }, + "DP_DSC_RC_BUF_SIZE": { + "address": "0x063" + }, + "DP_DSC_SLICE_CAP_1": { + "address": "0x064", + "bitfields": [ + { + "name": "DP_DSC_1_PER_DP_DSC_SINK", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_2_PER_DP_DSC_SINK", + "value": "(1 << 1)" + }, + { + "name": "DP_DSC_4_PER_DP_DSC_SINK", + "value": "(1 << 3)" + }, + { + "name": "DP_DSC_6_PER_DP_DSC_SINK", + "value": "(1 << 4)" + }, + { + "name": "DP_DSC_8_PER_DP_DSC_SINK", + "value": "(1 << 5)" + }, + { + "name": "DP_DSC_10_PER_DP_DSC_SINK", + "value": "(1 << 6)" + }, + { + "name": "DP_DSC_12_PER_DP_DSC_SINK", + "value": "(1 << 7)" + } + ] + }, + "DP_DSC_LINE_BUF_BIT_DEPTH": { + "address": "0x065", + "bitfields": [ + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_9", + "value": "0x0" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_10", + "value": "0x1" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_11", + "value": "0x2" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_12", + "value": "0x3" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_13", + "value": "0x4" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_14", + "value": "0x5" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_15", + "value": "0x6" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_16", + "value": "0x7" + }, + { + "name": "DP_DSC_LINE_BUF_BIT_DEPTH_8", + "value": "0x8" + } + ] + }, + "DP_DSC_BLK_PREDICTION_SUPPORT": { + "address": "0x066", + "bitfields": [ + { + "name": "DP_DSC_BLK_PREDICTION_IS_SUPPORTED", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_RGB_COLOR_CONV_BYPASS_SUPPORT", + "value": "(1 << 1)" + } + ] + }, + "DP_DSC_MAX_BITS_PER_PIXEL_LOW": { + "address": "0x067" + }, + "DP_DSC_MAX_BITS_PER_PIXEL_HI": { + "address": "0x068", + "bitfields": [ + { + "name": "DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK", + "value": "(0x3 << 0)" + }, + { + "name": "DP_DSC_MAX_BPP_DELTA_VERSION_MASK", + "value": "(0x3 << 5)\t/* eDP 1.5 & DP 2.0 */" + }, + { + "name": "DP_DSC_MAX_BPP_DELTA_AVAILABILITY", + "value": "(1 << 7)\t/* eDP 1.5 & DP 2.0 */" + } + ] + }, + "DP_DSC_DEC_COLOR_FORMAT_CAP": { + "address": "0x069", + "bitfields": [ + { + "name": "DP_DSC_RGB", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_YCbCr444", + "value": "(1 << 1)" + }, + { + "name": "DP_DSC_YCbCr422_Simple", + "value": "(1 << 2)" + }, + { + "name": "DP_DSC_YCbCr422_Native", + "value": "(1 << 3)" + }, + { + "name": "DP_DSC_YCbCr420_Native", + "value": "(1 << 4)" + } + ] + }, + "DP_DSC_DEC_COLOR_DEPTH_CAP": { + "address": "0x06A", + "bitfields": [ + { + "name": "DP_DSC_8_BPC", + "value": "(1 << 1)" + }, + { + "name": "DP_DSC_10_BPC", + "value": "(1 << 2)" + }, + { + "name": "DP_DSC_12_BPC", + "value": "(1 << 3)" + } + ] + }, + "DP_DSC_PEAK_THROUGHPUT": { + "address": "0x06B", + "bitfields": [ + { + "name": "DP_DSC_THROUGHPUT_MODE_0_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_SHIFT", + "value": "0" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED", + "value": "0" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_340", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_400", + "value": "(2 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_450", + "value": "(3 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_500", + "value": "(4 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_550", + "value": "(5 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_600", + "value": "(6 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_650", + "value": "(7 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_700", + "value": "(8 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_750", + "value": "(9 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_800", + "value": "(10 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_850", + "value": "(11 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_900", + "value": "(12 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_950", + "value": "(13 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_1000", + "value": "(14 << 0)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_0_170", + "value": "(15 << 0) /* 1.4a */" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_SHIFT", + "value": "4" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_UNSUPPORTED", + "value": "0" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_340", + "value": "(1 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_400", + "value": "(2 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_450", + "value": "(3 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_500", + "value": "(4 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_550", + "value": "(5 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_600", + "value": "(6 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_650", + "value": "(7 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_700", + "value": "(8 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_750", + "value": "(9 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_800", + "value": "(10 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_850", + "value": "(11 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_900", + "value": "(12 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_950", + "value": "(13 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_1000", + "value": "(14 << 4)" + }, + { + "name": "DP_DSC_THROUGHPUT_MODE_1_170", + "value": "(15 << 4)" + } + ] + }, + "DP_DSC_MAX_SLICE_WIDTH": { + "address": "0x06C", + "bitfields": [ + { + "name": "DP_DSC_MIN_SLICE_WIDTH_VALUE", + "value": "2560" + }, + { + "name": "DP_DSC_SLICE_WIDTH_MULTIPLIER", + "value": "320" + } + ] + }, + "DP_DSC_SLICE_CAP_2": { + "address": "0x06D", + "bitfields": [ + { + "name": "DP_DSC_16_PER_DP_DSC_SINK", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_20_PER_DP_DSC_SINK", + "value": "(1 << 1)" + }, + { + "name": "DP_DSC_24_PER_DP_DSC_SINK", + "value": "(1 << 2)" + } + ] + }, + "DP_DSC_BITS_PER_PIXEL_INC": { + "address": "0x06F", + "bitfields": [ + { + "name": "DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK", + "value": "0x1f" + }, + { + "name": "DP_DSC_RGB_YCbCr420_MAX_BPP_DELTA_MASK", + "value": "0xe0" + }, + { + "name": "DP_DSC_BITS_PER_PIXEL_1_16", + "value": "0x0" + }, + { + "name": "DP_DSC_BITS_PER_PIXEL_1_8", + "value": "0x1" + }, + { + "name": "DP_DSC_BITS_PER_PIXEL_1_4", + "value": "0x2" + }, + { + "name": "DP_DSC_BITS_PER_PIXEL_1_2", + "value": "0x3" + }, + { + "name": "DP_DSC_BITS_PER_PIXEL_1_1", + "value": "0x4" + } + ] + }, + "DP_DSC_BRANCH_OVERALL_THROUGHPUT_0": { + "address": "0x0a0" + }, + "DP_DSC_BRANCH_OVERALL_THROUGHPUT_1": { + "address": "0x0a1" + }, + "DP_DSC_BRANCH_MAX_LINE_WIDTH": { + "address": "0x0a2" + }, + "DP_DSC_ENABLE": { + "address": "0x160", + "bitfields": [ + { + "name": "DP_DECOMPRESSION_EN", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_PASSTHROUGH_EN", + "value": "(1 << 1)" + } + ] + }, + "DP_DSC_CONFIGURATION": { + "address": "0x161" + }, + "DP_DSC_SUPPORT_AND_DSC_DECODER_COUNT": { + "address": "0x2260", + "bitfields": [ + { + "name": "DP_DSC_DECODER_COUNT_MASK", + "value": "(0b111 << 5)" + }, + { + "name": "DP_DSC_DECODER_COUNT_SHIFT", + "value": "5" + } + ] + }, + "DP_DSC_MAX_SLICE_COUNT_AND_AGGREGATION_0": { + "address": "0x2270", + "bitfields": [ + { + "name": "DP_DSC_DECODER_0_MAXIMUM_SLICE_COUNT_MASK", + "value": "(1 << 0)" + }, + { + "name": "DP_DSC_DECODER_0_AGGREGATION_SUPPORT_MASK", + "value": "(0b111 << 1)" + }, + { + "name": "DP_DSC_DECODER_0_AGGREGATION_SUPPORT_SHIFT", + "value": "1" + } + ] + }, + "DP_DSC_RECEIVER_CAP_SIZE": { + "address": "0x10", + "bitfields": [ + { + "name": "EDP_PSR_RECEIVER_CAP_SIZE", + "value": "2" + }, + { + "name": "EDP_DISPLAY_CTL_CAP_SIZE", + "value": "5" + }, + { + "name": "DP_LTTPR_COMMON_CAP_SIZE", + "value": "8" + }, + { + "name": "DP_LTTPR_PHY_CAP_SIZE", + "value": "3" + } + ] + } + }, + "DP_PSR": { + "DP_PSR_SUPPORT": { + "address": "0x070", + "bitfields": [ + { + "name": "DP_PSR_IS_SUPPORTED", + "value": "1" + }, + { + "name": "DP_PSR2_IS_SUPPORTED", + "value": "2\t /* eDP 1.4 */" + }, + { + "name": "DP_PSR2_WITH_Y_COORD_IS_SUPPORTED", + "value": "3\t /* eDP 1.4a */" + }, + { + "name": "DP_PSR2_WITH_Y_COORD_ET_SUPPORTED", + "value": "4\t /* eDP 1.5, adopted eDP 1.4b SCR */" + } + ] + }, + "DP_PSR_CAPS": { + "address": "0x071", + "bitfields": [ + { + "name": "DP_PSR_NO_TRAIN_ON_EXIT", + "value": "1" + }, + { + "name": "DP_PSR_SETUP_TIME_330", + "value": "(0 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_275", + "value": "(1 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_220", + "value": "(2 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_165", + "value": "(3 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_110", + "value": "(4 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_55", + "value": "(5 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_0", + "value": "(6 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_MASK", + "value": "(7 << 1)" + }, + { + "name": "DP_PSR_SETUP_TIME_SHIFT", + "value": "1" + }, + { + "name": "DP_PSR2_SU_Y_COORDINATE_REQUIRED", + "value": "(1 << 4) /* eDP 1.4a */" + }, + { + "name": "DP_PSR2_SU_GRANULARITY_REQUIRED", + "value": "(1 << 5) /* eDP 1.4b */" + }, + { + "name": "DP_PSR2_SU_AUX_FRAME_SYNC_NOT_NEEDED", + "value": "(1 << 6)/* eDP 1.5, adopted eDP 1.4b SCR */" + } + ] + }, + "DP_PSR_EN_CFG": { + "address": "0x170", + "bitfields": [ + { + "name": "DP_PSR_ENABLE", + "value": "BIT(0)" + }, + { + "name": "DP_PSR_MAIN_LINK_ACTIVE", + "value": "BIT(1)" + }, + { + "name": "DP_PSR_CRC_VERIFICATION", + "value": "BIT(2)" + }, + { + "name": "DP_PSR_FRAME_CAPTURE", + "value": "BIT(3)" + }, + { + "name": "DP_PSR_SU_REGION_SCANLINE_CAPTURE", + "value": "BIT(4) /* eDP 1.4a */" + }, + { + "name": "DP_PSR_IRQ_HPD_WITH_CRC_ERRORS", + "value": "BIT(5) /* eDP 1.4a */" + }, + { + "name": "DP_PSR_ENABLE_PSR2", + "value": "BIT(6) /* eDP 1.4a */" + }, + { + "name": "DP_PSR_ENABLE_SU_REGION_ET", + "value": "BIT(7) /* eDP 1.5 */" + } + ] + }, + "DP_PSR_ERROR_STATUS": { + "address": "0x2006", + "bitfields": [ + { + "name": "DP_PSR_LINK_CRC_ERROR", + "value": "(1 << 0)" + }, + { + "name": "DP_PSR_RFB_STORAGE_ERROR", + "value": "(1 << 1)" + }, + { + "name": "DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR", + "value": "(1 << 2) /* eDP 1.4 */" + } + ] + }, + "DP_PSR_ESI": { + "address": "0x2007", + "bitfields": [ + { + "name": "DP_PSR_CAPS_CHANGE", + "value": "(1 << 0)" + } + ] + }, + "DP_PSR_STATUS": { + "address": "0x2008", + "bitfields": [ + { + "name": "DP_PSR_SINK_INACTIVE", + "value": "0" + }, + { + "name": "DP_PSR_SINK_ACTIVE_SRC_SYNCED", + "value": "1" + }, + { + "name": "DP_PSR_SINK_ACTIVE_RFB", + "value": "2" + }, + { + "name": "DP_PSR_SINK_ACTIVE_SINK_SYNCED", + "value": "3" + }, + { + "name": "DP_PSR_SINK_ACTIVE_RESYNC", + "value": "4" + }, + { + "name": "DP_PSR_SINK_INTERNAL_ERROR", + "value": "7" + }, + { + "name": "DP_PSR_SINK_STATE_MASK", + "value": "0x07" + } + ] + } + }, + "DP_PSR2": { + "DP_PSR2_SU_X_GRANULARITY": { + "address": "0x072" + }, + "DP_PSR2_SU_Y_GRANULARITY": { + "address": "0x074" + } + }, + "DP_DOWNSTREAM_PORT_0": { + "address": "0x80", + "bitfields": [ + { + "name": "DP_DS_PORT_TYPE_MASK", + "value": "(7 << 0)" + }, + { + "name": "DP_DS_PORT_TYPE_DP", + "value": "0" + }, + { + "name": "DP_DS_PORT_TYPE_VGA", + "value": "1" + }, + { + "name": "DP_DS_PORT_TYPE_DVI", + "value": "2" + }, + { + "name": "DP_DS_PORT_TYPE_HDMI", + "value": "3" + }, + { + "name": "DP_DS_PORT_TYPE_NON_EDID", + "value": "4" + }, + { + "name": "DP_DS_PORT_TYPE_DP_DUALMODE", + "value": "5" + }, + { + "name": "DP_DS_PORT_TYPE_WIRELESS", + "value": "6" + }, + { + "name": "DP_DS_PORT_HPD", + "value": "(1 << 3)" + }, + { + "name": "DP_DS_NON_EDID_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_DS_NON_EDID_720x480i_60", + "value": "(1 << 4)" + }, + { + "name": "DP_DS_NON_EDID_720x480i_50", + "value": "(2 << 4)" + }, + { + "name": "DP_DS_NON_EDID_1920x1080i_60", + "value": "(3 << 4)" + }, + { + "name": "DP_DS_NON_EDID_1920x1080i_50", + "value": "(4 << 4)" + }, + { + "name": "DP_DS_NON_EDID_1280x720_60", + "value": "(5 << 4)" + }, + { + "name": "DP_DS_NON_EDID_1280x720_50", + "value": "(7 << 4)" + }, + { + "name": "DP_DS_MAX_BPC_MASK", + "value": "(3 << 0)" + }, + { + "name": "DP_DS_8BPC", + "value": "0" + }, + { + "name": "DP_DS_10BPC", + "value": "1" + }, + { + "name": "DP_DS_12BPC", + "value": "2" + }, + { + "name": "DP_DS_16BPC", + "value": "3" + }, + { + "name": "DP_PCON_MAX_FRL_BW", + "value": "(7 << 2)" + }, + { + "name": "DP_PCON_MAX_0GBPS", + "value": "(0 << 2)" + }, + { + "name": "DP_PCON_MAX_9GBPS", + "value": "(1 << 2)" + }, + { + "name": "DP_PCON_MAX_18GBPS", + "value": "(2 << 2)" + }, + { + "name": "DP_PCON_MAX_24GBPS", + "value": "(3 << 2)" + }, + { + "name": "DP_PCON_MAX_32GBPS", + "value": "(4 << 2)" + }, + { + "name": "DP_PCON_MAX_40GBPS", + "value": "(5 << 2)" + }, + { + "name": "DP_PCON_MAX_48GBPS", + "value": "(6 << 2)" + }, + { + "name": "DP_PCON_SOURCE_CTL_MODE", + "value": "(1 << 5)" + }, + { + "name": "DP_DS_DVI_DUAL_LINK", + "value": "(1 << 1)" + }, + { + "name": "DP_DS_DVI_HIGH_COLOR_DEPTH", + "value": "(1 << 2)" + }, + { + "name": "DP_DS_HDMI_FRAME_SEQ_TO_FRAME_PACK", + "value": "(1 << 0)" + }, + { + "name": "DP_DS_HDMI_YCBCR422_PASS_THROUGH", + "value": "(1 << 1)" + }, + { + "name": "DP_DS_HDMI_YCBCR420_PASS_THROUGH", + "value": "(1 << 2)" + }, + { + "name": "DP_DS_HDMI_YCBCR444_TO_422_CONV", + "value": "(1 << 3)" + }, + { + "name": "DP_DS_HDMI_YCBCR444_TO_420_CONV", + "value": "(1 << 4)" + }, + { + "name": "DP_DS_HDMI_BT601_RGB_YCBCR_CONV", + "value": "(1 << 5)" + }, + { + "name": "DP_DS_HDMI_BT709_RGB_YCBCR_CONV", + "value": "(1 << 6)" + }, + { + "name": "DP_DS_HDMI_BT2020_RGB_YCBCR_CONV", + "value": "(1 << 7)" + } + ] + }, + "DP_FEC": { + "DP_FEC_CAPABILITY": { + "address": "0x090", + "bitfields": [ + { + "name": "DP_FEC_CAPABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_FEC_UNCORR_BLK_ERROR_COUNT_CAP", + "value": "(1 << 1)" + }, + { + "name": "DP_FEC_CORR_BLK_ERROR_COUNT_CAP", + "value": "(1 << 2)" + }, + { + "name": "DP_FEC_BIT_ERROR_COUNT_CAP", + "value": "(1 << 3)" + } + ] + }, + "DP_FEC_CAPABILITY_1": { + "address": "0x091" + }, + "DP_FEC_CONFIGURATION": { + "address": "0x120", + "bitfields": [ + { + "name": "DP_FEC_READY", + "value": "(1 << 0)" + }, + { + "name": "DP_FEC_ERR_COUNT_SEL_MASK", + "value": "(7 << 1)" + }, + { + "name": "DP_FEC_ERR_COUNT_DIS", + "value": "(0 << 1)" + }, + { + "name": "DP_FEC_UNCORR_BLK_ERROR_COUNT", + "value": "(1 << 1)" + }, + { + "name": "DP_FEC_CORR_BLK_ERROR_COUNT", + "value": "(2 << 1)" + }, + { + "name": "DP_FEC_BIT_ERROR_COUNT", + "value": "(3 << 1)" + }, + { + "name": "DP_FEC_LANE_SELECT_MASK", + "value": "(3 << 4)" + }, + { + "name": "DP_FEC_LANE_0_SELECT", + "value": "(0 << 4)" + }, + { + "name": "DP_FEC_LANE_1_SELECT", + "value": "(1 << 4)" + }, + { + "name": "DP_FEC_LANE_2_SELECT", + "value": "(2 << 4)" + }, + { + "name": "DP_FEC_LANE_3_SELECT", + "value": "(3 << 4)" + } + ] + }, + "DP_FEC_STATUS": { + "address": "0x280", + "bitfields": [ + { + "name": "DP_FEC_DECODE_EN_DETECTED", + "value": "(1 << 0)" + }, + { + "name": "DP_FEC_DECODE_DIS_DETECTED", + "value": "(1 << 1)" + } + ] + }, + "DP_FEC_ERROR_COUNT_LSB": { + "address": "0x0281" + }, + "DP_FEC_ERROR_COUNT_MSB": { + "address": "0x0282", + "bitfields": [ + { + "name": "DP_FEC_ERROR_COUNT_MASK", + "value": "0x7F" + }, + { + "name": "DP_FEC_ERR_COUNT_VALID", + "value": "(1 << 7)" + } + ] + }, + "DP_FEC_STATUS_PHY_REPEATER1": { + "address": "0xf0290" + }, + "DP_FEC_ERROR_COUNT_PHY_REPEATER1": { + "address": "0xf0291" + }, + "DP_FEC_CAPABILITY_PHY_REPEATER1": { + "address": "0xf0294" + } + }, + "DP_PCON": { + "DP_PCON_DSC_ENCODER_CAP_SIZE": { + "address": "0xD" + }, + "DP_PCON_DSC_ENCODER": { + "address": "0x092", + "bitfields": [ + { + "name": "DP_PCON_DSC_ENCODER_SUPPORTED", + "value": "(1 << 0)" + }, + { + "name": "DP_PCON_DSC_PPS_ENC_OVERRIDE", + "value": "(1 << 1)" + } + ] + }, + "DP_PCON_DSC_VERSION": { + "address": "0x093", + "bitfields": [ + { + "name": "DP_PCON_DSC_MAJOR_MASK", + "value": "(0xF << 0)" + }, + { + "name": "DP_PCON_DSC_MINOR_MASK", + "value": "(0xF << 4)" + }, + { + "name": "DP_PCON_DSC_MAJOR_SHIFT", + "value": "0" + }, + { + "name": "DP_PCON_DSC_MINOR_SHIFT", + "value": "4" + } + ] + }, + "DP_PCON_DSC_RC_BUF_BLK_INFO": { + "address": "0x094", + "bitfields": [ + { + "name": "DP_PCON_DSC_RC_BUF_BLK_SIZE", + "value": "(0x3 << 0)" + }, + { + "name": "DP_PCON_DSC_RC_BUF_BLK_1KB", + "value": "0" + }, + { + "name": "DP_PCON_DSC_RC_BUF_BLK_4KB", + "value": "1" + }, + { + "name": "DP_PCON_DSC_RC_BUF_BLK_16KB", + "value": "2" + }, + { + "name": "DP_PCON_DSC_RC_BUF_BLK_64KB", + "value": "3" + } + ] + }, + "DP_PCON_DSC_RC_BUF_SIZE": { + "address": "0x095" + }, + "DP_PCON_DSC_SLICE_CAP_1": { + "address": "0x096", + "bitfields": [ + { + "name": "DP_PCON_DSC_1_PER_DSC_ENC", + "value": "(0x1 << 0)" + }, + { + "name": "DP_PCON_DSC_2_PER_DSC_ENC", + "value": "(0x1 << 1)" + }, + { + "name": "DP_PCON_DSC_4_PER_DSC_ENC", + "value": "(0x1 << 3)" + }, + { + "name": "DP_PCON_DSC_6_PER_DSC_ENC", + "value": "(0x1 << 4)" + }, + { + "name": "DP_PCON_DSC_8_PER_DSC_ENC", + "value": "(0x1 << 5)" + }, + { + "name": "DP_PCON_DSC_10_PER_DSC_ENC", + "value": "(0x1 << 6)" + }, + { + "name": "DP_PCON_DSC_12_PER_DSC_ENC", + "value": "(0x1 << 7)" + } + ] + }, + "DP_PCON_DSC_BUF_BIT_DEPTH": { + "address": "0x097", + "bitfields": [ + { + "name": "DP_PCON_DSC_BIT_DEPTH_MASK", + "value": "(0xF << 0)" + }, + { + "name": "DP_PCON_DSC_DEPTH_9_BITS", + "value": "0" + }, + { + "name": "DP_PCON_DSC_DEPTH_10_BITS", + "value": "1" + }, + { + "name": "DP_PCON_DSC_DEPTH_11_BITS", + "value": "2" + }, + { + "name": "DP_PCON_DSC_DEPTH_12_BITS", + "value": "3" + }, + { + "name": "DP_PCON_DSC_DEPTH_13_BITS", + "value": "4" + }, + { + "name": "DP_PCON_DSC_DEPTH_14_BITS", + "value": "5" + }, + { + "name": "DP_PCON_DSC_DEPTH_15_BITS", + "value": "6" + }, + { + "name": "DP_PCON_DSC_DEPTH_16_BITS", + "value": "7" + }, + { + "name": "DP_PCON_DSC_DEPTH_8_BITS", + "value": "8" + } + ] + }, + "DP_PCON_DSC_BLOCK_PREDICTION": { + "address": "0x098", + "bitfields": [ + { + "name": "DP_PCON_DSC_BLOCK_PRED_SUPPORT", + "value": "(0x1 << 0)" + } + ] + }, + "DP_PCON_DSC_ENC_COLOR_FMT_CAP": { + "address": "0x099", + "bitfields": [ + { + "name": "DP_PCON_DSC_ENC_RGB", + "value": "(0x1 << 0)" + }, + { + "name": "DP_PCON_DSC_ENC_YUV444", + "value": "(0x1 << 1)" + }, + { + "name": "DP_PCON_DSC_ENC_YUV422_S", + "value": "(0x1 << 2)" + }, + { + "name": "DP_PCON_DSC_ENC_YUV422_N", + "value": "(0x1 << 3)" + }, + { + "name": "DP_PCON_DSC_ENC_YUV420_N", + "value": "(0x1 << 4)" + } + ] + }, + "DP_PCON_DSC_ENC_COLOR_DEPTH_CAP": { + "address": "0x09A", + "bitfields": [ + { + "name": "DP_PCON_DSC_ENC_8BPC", + "value": "(0x1 << 1)" + }, + { + "name": "DP_PCON_DSC_ENC_10BPC", + "value": "(0x1 << 2)" + }, + { + "name": "DP_PCON_DSC_ENC_12BPC", + "value": "(0x1 << 3)" + } + ] + }, + "DP_PCON_DSC_MAX_SLICE_WIDTH": { + "address": "0x09B" + }, + "DP_PCON_DSC_SLICE_CAP_2": { + "address": "0x09C", + "bitfields": [ + { + "name": "DP_PCON_DSC_16_PER_DSC_ENC", + "value": "(0x1 << 0)" + }, + { + "name": "DP_PCON_DSC_20_PER_DSC_ENC", + "value": "(0x1 << 1)" + }, + { + "name": "DP_PCON_DSC_24_PER_DSC_ENC", + "value": "(0x1 << 2)" + } + ] + }, + "DP_PCON_DSC_BPP_INCR": { + "address": "0x09E", + "bitfields": [ + { + "name": "DP_PCON_DSC_BPP_INCR_MASK", + "value": "(0x7 << 0)" + }, + { + "name": "DP_PCON_DSC_ONE_16TH_BPP", + "value": "0" + }, + { + "name": "DP_PCON_DSC_ONE_8TH_BPP", + "value": "1" + }, + { + "name": "DP_PCON_DSC_ONE_4TH_BPP", + "value": "2" + }, + { + "name": "DP_PCON_DSC_ONE_HALF_BPP", + "value": "3" + }, + { + "name": "DP_PCON_DSC_ONE_BPP", + "value": "4" + } + ] + }, + "DP_PCON_HDMI_LINK_CONFIG_1": { + "address": "0x305A", + "bitfields": [ + { + "name": "DP_PCON_ENABLE_MAX_FRL_BW", + "value": "(7 << 0)" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_0GBPS", + "value": "0" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_9GBPS", + "value": "1" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_18GBPS", + "value": "2" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_24GBPS", + "value": "3" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_32GBPS", + "value": "4" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_40GBPS", + "value": "5" + }, + { + "name": "DP_PCON_ENABLE_MAX_BW_48GBPS", + "value": "6" + }, + { + "name": "DP_PCON_ENABLE_SOURCE_CTL_MODE", + "value": "(1 << 3)" + }, + { + "name": "DP_PCON_ENABLE_CONCURRENT_LINK", + "value": "(1 << 4)" + }, + { + "name": "DP_PCON_ENABLE_SEQUENTIAL_LINK", + "value": "(0 << 4)" + }, + { + "name": "DP_PCON_ENABLE_LINK_FRL_MODE", + "value": "(1 << 5)" + }, + { + "name": "DP_PCON_ENABLE_HPD_READY", + "value": "(1 << 6)" + }, + { + "name": "DP_PCON_ENABLE_HDMI_LINK", + "value": "(1 << 7)" + } + ] + }, + "DP_PCON_HDMI_LINK_CONFIG_2": { + "address": "0x305B", + "bitfields": [ + { + "name": "DP_PCON_MAX_LINK_BW_MASK", + "value": "(0x3F << 0)" + }, + { + "name": "DP_PCON_FRL_BW_MASK_9GBPS", + "value": "(1 << 0)" + }, + { + "name": "DP_PCON_FRL_BW_MASK_18GBPS", + "value": "(1 << 1)" + }, + { + "name": "DP_PCON_FRL_BW_MASK_24GBPS", + "value": "(1 << 2)" + }, + { + "name": "DP_PCON_FRL_BW_MASK_32GBPS", + "value": "(1 << 3)" + }, + { + "name": "DP_PCON_FRL_BW_MASK_40GBPS", + "value": "(1 << 4)" + }, + { + "name": "DP_PCON_FRL_BW_MASK_48GBPS", + "value": "(1 << 5)" + }, + { + "name": "DP_PCON_FRL_LINK_TRAIN_EXTENDED", + "value": "(1 << 6)" + }, + { + "name": "DP_PCON_FRL_LINK_TRAIN_NORMAL", + "value": "(0 << 6)" + } + ] + }, + "DP_PCON_HDMI_TX_LINK_STATUS": { + "address": "0x303B", + "bitfields": [ + { + "name": "DP_PCON_HDMI_TX_LINK_ACTIVE", + "value": "(1 << 0)" + }, + { + "name": "DP_PCON_FRL_READY", + "value": "(1 << 1)" + } + ] + }, + "DP_PCON_HDMI_POST_FRL_STATUS": { + "address": "0x3036", + "bitfields": [ + { + "name": "DP_PCON_HDMI_LINK_MODE", + "value": "(1 << 0)" + }, + { + "name": "DP_PCON_HDMI_MODE_TMDS", + "value": "0" + }, + { + "name": "DP_PCON_HDMI_MODE_FRL", + "value": "1" + }, + { + "name": "DP_PCON_HDMI_FRL_TRAINED_BW", + "value": "(0x3F << 1)" + }, + { + "name": "DP_PCON_FRL_TRAINED_BW_9GBPS", + "value": "(1 << 1)" + }, + { + "name": "DP_PCON_FRL_TRAINED_BW_18GBPS", + "value": "(1 << 2)" + }, + { + "name": "DP_PCON_FRL_TRAINED_BW_24GBPS", + "value": "(1 << 3)" + }, + { + "name": "DP_PCON_FRL_TRAINED_BW_32GBPS", + "value": "(1 << 4)" + }, + { + "name": "DP_PCON_FRL_TRAINED_BW_40GBPS", + "value": "(1 << 5)" + }, + { + "name": "DP_PCON_FRL_TRAINED_BW_48GBPS", + "value": "(1 << 6)" + } + ] + }, + "DP_PCON_HDMI_ERROR_STATUS_LN0": { + "address": "0x3037" + }, + "DP_PCON_HDMI_ERROR_STATUS_LN1": { + "address": "0x3038" + }, + "DP_PCON_HDMI_ERROR_STATUS_LN2": { + "address": "0x3039" + }, + "DP_PCON_HDMI_ERROR_STATUS_LN3": { + "address": "0x303A", + "bitfields": [ + { + "name": "DP_PCON_HDMI_ERROR_COUNT_MASK", + "value": "(0x7 << 0)" + }, + { + "name": "DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS", + "value": "(1 << 0)" + }, + { + "name": "DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS", + "value": "(1 << 1)" + }, + { + "name": "DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS", + "value": "(1 << 2)" + } + ] + }, + "DP_PCON_HDMI_PPS_OVERRIDE_BASE": { + "address": "0x3100" + }, + "DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT": { + "address": "0x3180" + }, + "DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH": { + "address": "0x3182" + }, + "DP_PCON_HDMI_PPS_OVRD_BPP": { + "address": "0x3184" + } + }, + "DP_DFP_CAPABILITY_EXTENSION_SUPPORT": { + "address": "0x0a3" + }, + "DP_PANEL": { + "DP_PANEL_REPLAY_CAP": { + "address": "0x0b0", + "bitfields": [ + { + "name": "DP_PANEL_REPLAY_SUPPORT", + "value": "(1 << 0)" + }, + { + "name": "DP_PANEL_REPLAY_SU_SUPPORT", + "value": "(1 << 1)" + }, + { + "name": "DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT", + "value": "(1 << 2) /* eDP 1.5 */" + } + ] + }, + "DP_PANEL_PANEL_REPLAY_CAPABILITY": { + "address": "0xb1", + "bitfields": [ + { + "name": "DP_PANEL_PANEL_REPLAY_SU_GRANULARITY_REQUIRED", + "value": "(1 << 5)" + } + ] + }, + "DP_PANEL_PANEL_REPLAY_X_GRANULARITY": { + "address": "0xb2" + }, + "DP_PANEL_PANEL_REPLAY_Y_GRANULARITY": { + "address": "0xb4" + }, + "DP_PANEL_REPLAY_ERROR_STATUS": { + "address": "0x2020", + "bitfields": [ + { + "name": "DP_PANEL_REPLAY_LINK_CRC_ERROR", + "value": "(1 << 0)" + }, + { + "name": "DP_PANEL_REPLAY_RFB_STORAGE_ERROR", + "value": "(1 << 1)" + }, + { + "name": "DP_PANEL_REPLAY_VSC_SDP_UNCORRECTABLE_ERROR", + "value": "(1 << 2)" + } + ] + } + }, + "DP_LINK": { + "DP_LINK_BW_SET": { + "address": "0x100", + "bitfields": [ + { + "name": "DP_LINK_RATE_TABLE", + "value": "0x00 /* eDP 1.4 */" + }, + { + "name": "DP_LINK_BW_1_62", + "value": "0x06" + }, + { + "name": "DP_LINK_BW_2_7", + "value": "0x0a" + }, + { + "name": "DP_LINK_BW_5_4", + "value": "0x14 /* 1.2 */" + }, + { + "name": "DP_LINK_BW_8_1", + "value": "0x1e /* 1.4 */" + }, + { + "name": "DP_LINK_BW_10", + "value": "0x01 /* 2.0 128b/132b Link Layer */" + }, + { + "name": "DP_LINK_BW_13_5", + "value": "0x04 /* 2.0 128b/132b Link Layer */" + }, + { + "name": "DP_LINK_BW_20", + "value": "0x02 /* 2.0 128b/132b Link Layer */" + } + ] + }, + "DP_LINK_QUAL_LANE0_SET": { + "address": "0x10b" + }, + "DP_LINK_QUAL_LANE1_SET": { + "address": "0x10c" + }, + "DP_LINK_QUAL_LANE2_SET": { + "address": "0x10d" + }, + "DP_LINK_QUAL_LANE3_SET": { + "address": "0x10e", + "bitfields": [ + { + "name": "DP_LINK_QUAL_PATTERN_DISABLE", + "value": "0" + }, + { + "name": "DP_LINK_QUAL_PATTERN_D10_2", + "value": "1" + }, + { + "name": "DP_LINK_QUAL_PATTERN_ERROR_RATE", + "value": "2" + }, + { + "name": "DP_LINK_QUAL_PATTERN_PRBS7", + "value": "3" + }, + { + "name": "DP_LINK_QUAL_PATTERN_80BIT_CUSTOM", + "value": "4" + }, + { + "name": "DP_LINK_QUAL_PATTERN_CP2520_PAT_1", + "value": "5" + }, + { + "name": "DP_LINK_QUAL_PATTERN_CP2520_PAT_2", + "value": "6" + }, + { + "name": "DP_LINK_QUAL_PATTERN_CP2520_PAT_3", + "value": "7" + }, + { + "name": "DP_LINK_QUAL_PATTERN_128B132B_TPS1", + "value": "0x08" + }, + { + "name": "DP_LINK_QUAL_PATTERN_128B132B_TPS2", + "value": "0x10" + }, + { + "name": "DP_LINK_QUAL_PATTERN_PRSBS9", + "value": "0x18" + }, + { + "name": "DP_LINK_QUAL_PATTERN_PRSBS11", + "value": "0x20" + }, + { + "name": "DP_LINK_QUAL_PATTERN_PRSBS15", + "value": "0x28" + }, + { + "name": "DP_LINK_QUAL_PATTERN_PRSBS23", + "value": "0x30" + }, + { + "name": "DP_LINK_QUAL_PATTERN_PRSBS31", + "value": "0x38" + }, + { + "name": "DP_LINK_QUAL_PATTERN_CUSTOM", + "value": "0x40" + }, + { + "name": "DP_LINK_QUAL_PATTERN_SQUARE", + "value": "0x48" + }, + { + "name": "DP_LINK_QUAL_PATTERN_SQUARE_PRESHOOT_DISABLED", + "value": "0x49" + }, + { + "name": "DP_LINK_QUAL_PATTERN_SQUARE_DEEMPHASIS_DISABLED", + "value": "0x4a" + }, + { + "name": "DP_LINK_QUAL_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED", + "value": "0x4b" + } + ] + }, + "DP_LINK_RATE_SET": { + "address": "0x115", + "bitfields": [ + { + "name": "DP_LINK_RATE_SET_SHIFT", + "value": "0" + }, + { + "name": "DP_LINK_RATE_SET_MASK", + "value": "(7 << 0)" + } + ] + }, + "DP_LINK_SERVICE_IRQ_VECTOR_ESI0": { + "address": "0x2005", + "bitfields": [ + { + "name": "RX_CAP_CHANGED", + "value": "(1 << 0)" + }, + { + "name": "LINK_STATUS_CHANGED", + "value": "(1 << 1)" + }, + { + "name": "STREAM_STATUS_CHANGED", + "value": "(1 << 2)" + }, + { + "name": "HDMI_LINK_STATUS_CHANGED", + "value": "(1 << 3)" + }, + { + "name": "CONNECTED_OFF_ENTRY_REQUESTED", + "value": "(1 << 4)" + }, + { + "name": "DP_TUNNELING_IRQ", + "value": "(1 << 5)" + } + ] + }, + "DP_LINK_ADDRESS": { + "address": "0x01" + }, + "DP_LINK_CONSTANT_N_VALUE": { + "address": "0x8000", + "bitfields": [ + { + "name": "DP_LINK_STATUS_SIZE", + "value": "6" + } + ] + } + }, + "DP_LANE": { + "DP_LANE_COUNT_SET": { + "address": "0x101", + "bitfields": [ + { + "name": "DP_LANE_COUNT_MASK", + "value": "0x0f" + }, + { + "name": "DP_LANE_COUNT_ENHANCED_FRAME_EN", + "value": "(1 << 7)" + } + ] + }, + "DP_LANE_ALIGN_STATUS_UPDATED": { + "address": "0x204", + "bitfields": [ + { + "name": "DP_INTERLANE_ALIGN_DONE", + "value": "(1 << 0)" + }, + { + "name": "DP_128B132B_DPRX_EQ_INTERLANE_ALIGN_DONE", + "value": "(1 << 2) /* 2.0 E11 */" + }, + { + "name": "DP_128B132B_DPRX_CDS_INTERLANE_ALIGN_DONE", + "value": "(1 << 3) /* 2.0 E11 */" + }, + { + "name": "DP_128B132B_LT_FAILED", + "value": "(1 << 4) /* 2.0 E11 */" + }, + { + "name": "DP_DOWNSTREAM_PORT_STATUS_CHANGED", + "value": "(1 << 6)" + }, + { + "name": "DP_LINK_STATUS_UPDATED", + "value": "(1 << 7)" + } + ] + }, + "DP_LANE_ALIGN_STATUS_UPDATED_ESI": { + "address": "0x200e" + }, + "DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1": { + "address": "0xf0032" + } + }, + "DP_DOWNSPREAD_CTRL": { + "address": "0x107", + "bitfields": [ + { + "name": "DP_SPREAD_AMP_0_5", + "value": "(1 << 4)" + }, + { + "name": "DP_FIXED_VTOTAL_AS_SDP_EN_IN_PR_ACTIVE", + "value": "(1 << 6)" + }, + { + "name": "DP_MSA_TIMING_PAR_IGNORE_EN", + "value": "(1 << 7) /* eDP */" + } + ] + }, + "DP_AUDIO": { + "DP_AUDIO_DELAY0": { + "address": "0x112" + }, + "DP_AUDIO_DELAY1": { + "address": "0x113" + }, + "DP_AUDIO_DELAY2": { + "address": "0x114" + } + }, + "DP_UPSTREAM_DEVICE_DP_PWR_NEED": { + "address": "0x118", + "bitfields": [ + { + "name": "DP_PWR_NOT_NEEDED", + "value": "(1 << 0)" + } + ] + }, + "DP_EXTENDED": { + "DP_EXTENDED_DPRX_SLEEP_WAKE_TIMEOUT_GRANT": { + "address": "0x119", + "bitfields": [ + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_GRANTED", + "value": "(1 << 0)" + } + ] + }, + "DP_EXTENDED_DPRX_SLEEP_WAKE_TIMEOUT_REQUEST": { + "address": "0x2211", + "bitfields": [ + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_MASK", + "value": "0xff" + }, + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_1_MS", + "value": "0x00" + }, + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_20_MS", + "value": "0x01" + }, + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_40_MS", + "value": "0x02" + }, + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_60_MS", + "value": "0x03" + }, + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_80_MS", + "value": "0x04" + }, + { + "name": "DP_DPRX_SLEEP_WAKE_TIMEOUT_PERIOD_100_MS", + "value": "0x05" + } + ] + } + }, + "DP_SDP": { + "DP_SDP_ERROR_DETECTION_CONFIGURATION": { + "address": "0x121", + "bitfields": [ + { + "name": "DP_SDP_CRC16_128B132B_EN", + "value": "BIT(0)" + } + ] + }, + "DP_SDP_AUDIO_TIMESTAMP": { + "address": "0x01" + }, + "DP_SDP_AUDIO_STREAM": { + "address": "0x02" + }, + "DP_SDP_EXTENSION": { + "address": "0x04" + }, + "DP_SDP_AUDIO_COPYMANAGEMENT": { + "address": "0x05" + }, + "DP_SDP_ISRC": { + "address": "0x06" + }, + "DP_SDP_VSC": { + "address": "0x07" + }, + "DP_SDP_ADAPTIVE_SYNC": { + "address": "0x22" + }, + "DP_SDP_PPS": { + "address": "0x10" + }, + "DP_SDP_VSC_EXT_VESA": { + "address": "0x20" + }, + "DP_SDP_VSC_EXT_CEA": { + "address": "0x21" + }, + "DP_SDP_AUDIO_INFOFRAME_HB2": { + "address": "0x1b" + }, + "DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1": { + "address": "0x7F", + "bitfields": [ + { + "name": "EDP_VSC_PSR_STATE_ACTIVE", + "value": "(1<<0)" + }, + { + "name": "EDP_VSC_PSR_UPDATE_RFB", + "value": "(1<<1)" + }, + { + "name": "EDP_VSC_PSR_CRC_VALUES_VALID", + "value": "(1<<2)" + } + ] + } + }, + "DP_BRANCH": { + "DP_BRANCH_DEVICE_CTRL": { + "address": "0x1a1", + "bitfields": [ + { + "name": "DP_BRANCH_DEVICE_IRQ_HPD", + "value": "(1 << 0)" + } + ] + }, + "DP_BRANCH_OUI": { + "address": "0x500" + }, + "DP_BRANCH_ID": { + "address": "0x503" + }, + "DP_BRANCH_REVISION_START": { + "address": "0x509" + }, + "DP_BRANCH_HW_REV": { + "address": "0x509" + }, + "DP_BRANCH_SW_REV": { + "address": "0x50A" + }, + "DP_BRANCH_OUI_HEADER_SIZE": { + "address": "0xc" + } + }, + "PANEL_REPLAY": { + "PANEL_REPLAY_CONFIG": { + "address": "0x1b0", + "bitfields": [ + { + "name": "DP_PANEL_REPLAY_ENABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_PANEL_REPLAY_VSC_SDP_CRC_EN", + "value": "(1 << 1) /* eDP 1.5 */" + }, + { + "name": "DP_PANEL_REPLAY_UNRECOVERABLE_ERROR_EN", + "value": "(1 << 3)" + }, + { + "name": "DP_PANEL_REPLAY_RFB_STORAGE_ERROR_EN", + "value": "(1 << 4)" + }, + { + "name": "DP_PANEL_REPLAY_ACTIVE_FRAME_CRC_ERROR_EN", + "value": "(1 << 5)" + }, + { + "name": "DP_PANEL_REPLAY_SU_ENABLE", + "value": "(1 << 6)" + }, + { + "name": "DP_PANEL_REPLAY_ENABLE_SU_REGION_ET", + "value": "(1 << 7) /* DP 2.1 */" + } + ] + }, + "PANEL_REPLAY_CONFIG2": { + "address": "0x1b1", + "bitfields": [ + { + "name": "DP_PANEL_REPLAY_SINK_REFRESH_RATE_UNLOCK_GRANTED", + "value": "(1 << 0)" + }, + { + "name": "DP_PANEL_REPLAY_CRC_VERIFICATION", + "value": "(1 << 1)" + }, + { + "name": "DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_EN", + "value": "(1 << 2)" + }, + { + "name": "DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_VAL_SEL_SHIFT", + "value": "3" + }, + { + "name": "DP_PANEL_REPLAY_SU_Y_GRANULARITY_EXTENDED_VAL_SEL_MASK", + "value": "(0xf << 3)" + }, + { + "name": "DP_PANEL_REPLAY_SU_REGION_SCANLINE_CAPTURE", + "value": "(1 << 7)" + } + ] + } + }, + "DP_PAYLOAD": { + "DP_PAYLOAD_ALLOCATE_SET": { + "address": "0x1c0" + }, + "DP_PAYLOAD_ALLOCATE_START_TIME_SLOT": { + "address": "0x1c1" + }, + "DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT": { + "address": "0x1c2" + }, + "DP_PAYLOAD_TABLE_UPDATE_STATUS": { + "address": "0x2c0", + "bitfields": [ + { + "name": "DP_PAYLOAD_TABLE_UPDATED", + "value": "(1 << 0)" + }, + { + "name": "DP_PAYLOAD_ACT_HANDLED", + "value": "(1 << 1)" + } + ] + } + }, + "DP_DEVICE": { + "DP_DEVICE_SERVICE_IRQ_VECTOR": { + "address": "0x201", + "bitfields": [ + { + "name": "DP_REMOTE_CONTROL_COMMAND_PENDING", + "value": "(1 << 0)" + }, + { + "name": "DP_AUTOMATED_TEST_REQUEST", + "value": "(1 << 1)" + }, + { + "name": "DP_CP_IRQ", + "value": "(1 << 2)" + }, + { + "name": "DP_MCCS_IRQ", + "value": "(1 << 3)" + }, + { + "name": "DP_DOWN_REP_MSG_RDY", + "value": "(1 << 4) /* 1.2 MST */" + }, + { + "name": "DP_UP_REQ_MSG_RDY", + "value": "(1 << 5) /* 1.2 MST */" + }, + { + "name": "DP_SINK_SPECIFIC_IRQ", + "value": "(1 << 6)" + } + ] + }, + "DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0": { + "address": "0x2003" + }, + "DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1": { + "address": "0x2004", + "bitfields": [ + { + "name": "DP_RX_GTC_MSTR_REQ_STATUS_CHANGE", + "value": "(1 << 0)" + }, + { + "name": "DP_LOCK_ACQUISITION_REQUEST", + "value": "(1 << 1)" + }, + { + "name": "DP_CEC_IRQ", + "value": "(1 << 2)" + } + ] + } + }, + "DP_LANE0": { + "DP_LANE0_1_STATUS": { + "address": "0x202" + }, + "DP_LANE0_1_STATUS_ESI": { + "address": "0x200c" + }, + "DP_LANE0_1_STATUS_PHY_REPEATER1": { + "address": "0xf0030" + } + }, + "DP_LANE2": { + "DP_LANE2_3_STATUS": { + "address": "0x203", + "bitfields": [ + { + "name": "DP_LANE_CR_DONE", + "value": "(1 << 0)" + }, + { + "name": "DP_LANE_CHANNEL_EQ_DONE", + "value": "(1 << 1)" + }, + { + "name": "DP_LANE_SYMBOL_LOCKED", + "value": "(1 << 2)" + }, + { + "name": "DP_CHANNEL_EQ_BITS", + "value": "(DP_LANE_CR_DONE |\t\t\\" + } + ] + }, + "DP_LANE2_3_STATUS_ESI": { + "address": "0x200d" + }, + "DP_LANE2_3_STATUS_PHY_REPEATER1": { + "address": "0xf0031" + } + }, + "DP_ADJUST": { + "DP_ADJUST_REQUEST_LANE0_1": { + "address": "0x206" + }, + "DP_ADJUST_REQUEST_LANE2_3": { + "address": "0x207", + "bitfields": [ + { + "name": "DP_ADJUST_VOLTAGE_SWING_LANE0_MASK", + "value": "0x03" + }, + { + "name": "DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT", + "value": "0" + }, + { + "name": "DP_ADJUST_PRE_EMPHASIS_LANE0_MASK", + "value": "0x0c" + }, + { + "name": "DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT", + "value": "2" + }, + { + "name": "DP_ADJUST_VOLTAGE_SWING_LANE1_MASK", + "value": "0x30" + }, + { + "name": "DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT", + "value": "4" + }, + { + "name": "DP_ADJUST_PRE_EMPHASIS_LANE1_MASK", + "value": "0xc0" + }, + { + "name": "DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT", + "value": "6" + }, + { + "name": "DP_ADJUST_TX_FFE_PRESET_LANE0_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT", + "value": "0" + }, + { + "name": "DP_ADJUST_TX_FFE_PRESET_LANE1_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT", + "value": "4" + } + ] + }, + "DP_ADJUST_REQUEST_POST_CURSOR2": { + "address": "0x20c", + "bitfields": [ + { + "name": "DP_ADJUST_POST_CURSOR2_LANE0_MASK", + "value": "0x03" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE0_SHIFT", + "value": "0" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE1_MASK", + "value": "0x0c" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE1_SHIFT", + "value": "2" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE2_MASK", + "value": "0x30" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE2_SHIFT", + "value": "4" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE3_MASK", + "value": "0xc0" + }, + { + "name": "DP_ADJUST_POST_CURSOR2_LANE3_SHIFT", + "value": "6" + } + ] + }, + "DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1": { + "address": "0xf0033" + }, + "DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1": { + "address": "0xf0034" + } + }, + "DP_TEST": { + "DP_TEST_REQUEST": { + "address": "0x218", + "bitfields": [ + { + "name": "DP_TEST_LINK_TRAINING", + "value": "(1 << 0)" + }, + { + "name": "DP_TEST_LINK_VIDEO_PATTERN", + "value": "(1 << 1)" + }, + { + "name": "DP_TEST_LINK_EDID_READ", + "value": "(1 << 2)" + }, + { + "name": "DP_TEST_LINK_PHY_TEST_PATTERN", + "value": "(1 << 3) /* DPCD >= 1.1 */" + }, + { + "name": "DP_TEST_LINK_FAUX_PATTERN", + "value": "(1 << 4) /* DPCD >= 1.2 */" + }, + { + "name": "DP_TEST_LINK_AUDIO_PATTERN", + "value": "(1 << 5) /* DPCD >= 1.2 */" + }, + { + "name": "DP_TEST_LINK_AUDIO_DISABLED_VIDEO", + "value": "(1 << 6) /* DPCD >= 1.2 */" + } + ] + }, + "DP_TEST_LINK_RATE": { + "address": "0x219", + "bitfields": [ + { + "name": "DP_LINK_RATE_162", + "value": "(0x6)" + }, + { + "name": "DP_LINK_RATE_27", + "value": "(0xa)" + } + ] + }, + "DP_TEST_LANE_COUNT": { + "address": "0x220" + }, + "DP_TEST_PATTERN": { + "address": "0x221", + "bitfields": [ + { + "name": "DP_NO_TEST_PATTERN", + "value": "0x0" + }, + { + "name": "DP_COLOR_RAMP", + "value": "0x1" + }, + { + "name": "DP_BLACK_AND_WHITE_VERTICAL_LINES", + "value": "0x2" + }, + { + "name": "DP_COLOR_SQUARE", + "value": "0x3" + } + ] + }, + "DP_TEST_H_TOTAL_HI": { + "address": "0x222" + }, + "DP_TEST_H_TOTAL_LO": { + "address": "0x223" + }, + "DP_TEST_V_TOTAL_HI": { + "address": "0x224" + }, + "DP_TEST_V_TOTAL_LO": { + "address": "0x225" + }, + "DP_TEST_H_START_HI": { + "address": "0x226" + }, + "DP_TEST_H_START_LO": { + "address": "0x227" + }, + "DP_TEST_V_START_HI": { + "address": "0x228" + }, + "DP_TEST_V_START_LO": { + "address": "0x229" + }, + "DP_TEST_HSYNC_HI": { + "address": "0x22A", + "bitfields": [ + { + "name": "DP_TEST_HSYNC_POLARITY", + "value": "(1 << 7)" + }, + { + "name": "DP_TEST_HSYNC_WIDTH_HI_MASK", + "value": "(127 << 0)" + } + ] + }, + "DP_TEST_HSYNC_WIDTH_LO": { + "address": "0x22B" + }, + "DP_TEST_VSYNC_HI": { + "address": "0x22C", + "bitfields": [ + { + "name": "DP_TEST_VSYNC_POLARITY", + "value": "(1 << 7)" + }, + { + "name": "DP_TEST_VSYNC_WIDTH_HI_MASK", + "value": "(127 << 0)" + } + ] + }, + "DP_TEST_VSYNC_WIDTH_LO": { + "address": "0x22D" + }, + "DP_TEST_H_WIDTH_HI": { + "address": "0x22E" + }, + "DP_TEST_H_WIDTH_LO": { + "address": "0x22F" + }, + "DP_TEST_V_HEIGHT_HI": { + "address": "0x230" + }, + "DP_TEST_V_HEIGHT_LO": { + "address": "0x231" + }, + "DP_TEST_MISC0": { + "address": "0x232" + }, + "DP_TEST_MISC1": { + "address": "0x233", + "bitfields": [ + { + "name": "DP_TEST_REFRESH_DENOMINATOR", + "value": "(1 << 0)" + }, + { + "name": "DP_TEST_INTERLACED", + "value": "(1 << 1)" + } + ] + }, + "DP_TEST_REFRESH_RATE_NUMERATOR": { + "address": "0x234" + }, + "DP_TEST_CRC_R_CR": { + "address": "0x240" + }, + "DP_TEST_CRC_G_Y": { + "address": "0x242" + }, + "DP_TEST_CRC_B_CB": { + "address": "0x244" + }, + "DP_TEST_SINK_MISC": { + "address": "0x246", + "bitfields": [ + { + "name": "DP_TEST_CRC_SUPPORTED", + "value": "(1 << 5)" + }, + { + "name": "DP_TEST_COUNT_MASK", + "value": "0xf" + } + ] + }, + "DP_TEST_HBR2_SCRAMBLER_RESET": { + "address": "0x24A" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_7_0": { + "address": "0x250" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_15_8": { + "address": "0x251" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_23_16": { + "address": "0x252" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_31_24": { + "address": "0x253" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_39_32": { + "address": "0x254" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_47_40": { + "address": "0x255" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_55_48": { + "address": "0x256" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_63_56": { + "address": "0x257" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_71_64": { + "address": "0x258" + }, + "DP_TEST_80BIT_CUSTOM_PATTERN_79_72": { + "address": "0x259" + }, + "DP_TEST_RESPONSE": { + "address": "0x260", + "bitfields": [ + { + "name": "DP_TEST_ACK", + "value": "(1 << 0)" + }, + { + "name": "DP_TEST_NAK", + "value": "(1 << 1)" + }, + { + "name": "DP_TEST_EDID_CHECKSUM_WRITE", + "value": "(1 << 2)" + } + ] + }, + "DP_TEST_EDID_CHECKSUM": { + "address": "0x261" + }, + "DP_TEST_SINK": { + "address": "0x270", + "bitfields": [ + { + "name": "DP_TEST_SINK_START", + "value": "(1 << 0)" + } + ] + }, + "DP_TEST_AUDIO_MODE": { + "address": "0x271" + }, + "DP_TEST_AUDIO_PATTERN_TYPE": { + "address": "0x272" + }, + "DP_TEST_AUDIO_PERIOD_CH1": { + "address": "0x273" + }, + "DP_TEST_AUDIO_PERIOD_CH2": { + "address": "0x274" + }, + "DP_TEST_AUDIO_PERIOD_CH3": { + "address": "0x275" + }, + "DP_TEST_AUDIO_PERIOD_CH4": { + "address": "0x276" + }, + "DP_TEST_AUDIO_PERIOD_CH5": { + "address": "0x277" + }, + "DP_TEST_AUDIO_PERIOD_CH6": { + "address": "0x278" + }, + "DP_TEST_AUDIO_PERIOD_CH7": { + "address": "0x279" + }, + "DP_TEST_AUDIO_PERIOD_CH8": { + "address": "0x27A" + }, + "DP_TEST_264BIT_CUSTOM_PATTERN_7_0": { + "address": "0x2230" + }, + "DP_TEST_264BIT_CUSTOM_PATTERN_263_256": { + "address": "0x2250" + } + }, + "DP_PHY": { + "DP_PHY_TEST_PATTERN": { + "address": "0x248", + "bitfields": [ + { + "name": "DP_PHY_TEST_PATTERN_SEL_MASK", + "value": "0x7" + }, + { + "name": "DP_PHY_TEST_PATTERN_NONE", + "value": "0x0" + }, + { + "name": "DP_PHY_TEST_PATTERN_D10_2", + "value": "0x1" + }, + { + "name": "DP_PHY_TEST_PATTERN_ERROR_COUNT", + "value": "0x2" + }, + { + "name": "DP_PHY_TEST_PATTERN_PRBS7", + "value": "0x3" + }, + { + "name": "DP_PHY_TEST_PATTERN_80BIT_CUSTOM", + "value": "0x4" + }, + { + "name": "DP_PHY_TEST_PATTERN_CP2520", + "value": "0x5" + } + ] + }, + "DP_PHY_SQUARE_PATTERN": { + "address": "0x249" + }, + "DP_PHY_REPEATER_CNT": { + "address": "0xf0002" + }, + "DP_PHY_REPEATER_MODE": { + "address": "0xf0003" + }, + "DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT": { + "address": "0xf0005", + "bitfields": [ + { + "name": "DP_EXTENDED_WAKE_TIMEOUT_REQUEST_MASK", + "value": "0x7f" + }, + { + "name": "DP_EXTENDED_WAKE_TIMEOUT_GRANT", + "value": "(1 << 7)" + } + ] + }, + "DP_PHY_REPEATER_128B132B_RATES": { + "address": "0xf0007" + }, + "DP_PHY_REPEATER_EQ_DONE": { + "address": "0xf0008" + }, + "DP_PHY_REPEATER_MODE_TRANSPARENT": { + "address": "0x55" + }, + "DP_PHY_REPEATER_MODE_NON_TRANSPARENT": { + "address": "0xaa", + "bitfields": [ + { + "name": "DP_HDCP_2_2_AKE_INIT_OFFSET", + "value": "DP_HDCP_2_2_REG_RTX_OFFSET" + }, + { + "name": "DP_HDCP_2_2_AKE_SEND_CERT_OFFSET", + "value": "DP_HDCP_2_2_REG_CERT_RX_OFFSET" + }, + { + "name": "DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET", + "value": "DP_HDCP_2_2_REG_EKPUB_KM_OFFSET" + }, + { + "name": "DP_HDCP_2_2_AKE_STORED_KM_OFFSET", + "value": "DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET" + }, + { + "name": "DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET", + "value": "DP_HDCP_2_2_REG_HPRIME_OFFSET" + }, + { + "name": "DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET", + "value": "\\" + }, + { + "name": "DP_HDCP_2_2_LC_INIT_OFFSET", + "value": "DP_HDCP_2_2_REG_RN_OFFSET" + }, + { + "name": "DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET", + "value": "DP_HDCP_2_2_REG_LPRIME_OFFSET" + }, + { + "name": "DP_HDCP_2_2_SKE_SEND_EKS_OFFSET", + "value": "DP_HDCP_2_2_REG_EDKEY_KS_OFFSET" + }, + { + "name": "DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET", + "value": "DP_HDCP_2_2_REG_RXINFO_OFFSET" + }, + { + "name": "DP_HDCP_2_2_REP_SEND_ACK_OFFSET", + "value": "DP_HDCP_2_2_REG_V_OFFSET" + }, + { + "name": "DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET", + "value": "DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET" + }, + { + "name": "DP_HDCP_2_2_REP_STREAM_READY_OFFSET", + "value": "DP_HDCP_2_2_REG_MPRIME_OFFSET" + }, + { + "name": "HDCP_2_2_DP_RXSTATUS_LEN", + "value": "1" + } + ] + } + }, + "DP_VC_PAYLOAD_ID_SLOT_1": { + "address": "0x2c1" + }, + "DP_SOURCE_OUI": { + "address": "0x300" + }, + "DP_SET_POWER": { + "address": "0x600", + "bitfields": [ + { + "name": "DP_SET_POWER_D0", + "value": "0x1" + }, + { + "name": "DP_SET_POWER_D3", + "value": "0x2" + }, + { + "name": "DP_SET_POWER_MASK", + "value": "0x3" + }, + { + "name": "DP_SET_POWER_D3_AUX_ON", + "value": "0x5" + } + ] + }, + "DP_SIDEBAND": { + "DP_SIDEBAND_MSG_DOWN_REQ_BASE": { + "address": "0x1000" + }, + "DP_SIDEBAND_MSG_UP_REP_BASE": { + "address": "0x1200" + }, + "DP_SIDEBAND_MSG_DOWN_REP_BASE": { + "address": "0x1400" + }, + "DP_SIDEBAND_MSG_UP_REQ_BASE": { + "address": "0x1600" + }, + "DP_SIDEBAND_REPLY_ACK": { + "address": "0x00" + }, + "DP_SIDEBAND_REPLY_NAK": { + "address": "0x01" + } + }, + "DP_SYNCHRONIZATION_LATENCY_IN_SINK": { + "address": "0x2009", + "bitfields": [ + { + "name": "DP_MAX_RESYNC_FRAME_COUNT_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_MAX_RESYNC_FRAME_COUNT_SHIFT", + "value": "0" + }, + { + "name": "DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_MASK", + "value": "(0xf << 4)" + }, + { + "name": "DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_SHIFT", + "value": "4" + } + ] + }, + "DP_LAST_RECEIVED_PSR_SDP": { + "address": "0x200a", + "bitfields": [ + { + "name": "DP_PSR_STATE_BIT", + "value": "(1 << 0) /* eDP 1.2 */" + }, + { + "name": "DP_UPDATE_RFB_BIT", + "value": "(1 << 1) /* eDP 1.2 */" + }, + { + "name": "DP_CRC_VALID_BIT", + "value": "(1 << 2) /* eDP 1.2 */" + }, + { + "name": "DP_SU_VALID", + "value": "(1 << 3) /* eDP 1.4 */" + }, + { + "name": "DP_FIRST_SCAN_LINE_SU_REGION", + "value": "(1 << 4) /* eDP 1.4 */" + }, + { + "name": "DP_LAST_SCAN_LINE_SU_REGION", + "value": "(1 << 5) /* eDP 1.4 */" + }, + { + "name": "DP_Y_COORDINATE_VALID", + "value": "(1 << 6) /* eDP 1.4a */" + } + ] + }, + "DP_DP13_DPCD_REV": { + "address": "0x2200" + }, + "DP_DPRX": { + "DP_DPRX_FEATURE_ENUMERATION_LIST": { + "address": "0x2210", + "bitfields": [ + { + "name": "DP_GTC_CAP", + "value": "(1 << 0) /* DP 1.3 */" + }, + { + "name": "DP_SST_SPLIT_SDP_CAP", + "value": "(1 << 1) /* DP 1.4 */" + }, + { + "name": "DP_AV_SYNC_CAP", + "value": "(1 << 2) /* DP 1.3 */" + }, + { + "name": "DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED", + "value": "(1 << 3) /* DP 1.3 */" + }, + { + "name": "DP_VSC_EXT_VESA_SDP_SUPPORTED", + "value": "(1 << 4) /* DP 1.4 */" + }, + { + "name": "DP_VSC_EXT_VESA_SDP_CHAINING_SUPPORTED", + "value": "(1 << 5) /* DP 1.4 */" + }, + { + "name": "DP_VSC_EXT_CEA_SDP_SUPPORTED", + "value": "(1 << 6) /* DP 1.4 */" + }, + { + "name": "DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED", + "value": "(1 << 7) /* DP 1.4 */" + } + ] + }, + "DP_DPRX_FEATURE_ENUMERATION_LIST_CONT_1": { + "address": "0x2214", + "bitfields": [ + { + "name": "DP_ADAPTIVE_SYNC_SDP_SUPPORTED", + "value": "(1 << 0)" + }, + { + "name": "DP_ADAPTIVE_SYNC_SDP_OPERATION_MODE", + "value": "GENMASK(1, 0)" + }, + { + "name": "DP_ADAPTIVE_SYNC_SDP_LENGTH", + "value": "GENMASK(5, 0)" + }, + { + "name": "DP_AS_SDP_FIRST_HALF_LINE_OR_3840_PIXEL_CYCLE_WINDOW_NOT_SUPPORTED", + "value": "(1 << 1)" + }, + { + "name": "DP_VSC_EXT_SDP_FRAMEWORK_VERSION_1_SUPPORTED", + "value": "(1 << 4)" + } + ] + } + }, + "DP_128B132B": { + "DP_128B132B_SUPPORTED_LINK_RATES": { + "address": "0x2215", + "bitfields": [ + { + "name": "DP_UHBR10", + "value": "(1 << 0)" + }, + { + "name": "DP_UHBR20", + "value": "(1 << 1)" + }, + { + "name": "DP_UHBR13_5", + "value": "(1 << 2)" + } + ] + }, + "DP_128B132B_TRAINING_AUX_RD_INTERVAL": { + "address": "0x2216", + "bitfields": [ + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_1MS_UNIT", + "value": "(1 << 7)" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK", + "value": "0x7f" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US", + "value": "0x00" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS", + "value": "0x01" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS", + "value": "0x02" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS", + "value": "0x03" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS", + "value": "0x04" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS", + "value": "0x05" + }, + { + "name": "DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS", + "value": "0x06" + } + ] + }, + "DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1": { + "address": "0xf0022" + } + }, + "DP_CEC": { + "DP_CEC_TUNNELING_CAPABILITY": { + "address": "0x3000", + "bitfields": [ + { + "name": "DP_CEC_TUNNELING_CAPABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_CEC_SNOOPING_CAPABLE", + "value": "(1 << 1)" + }, + { + "name": "DP_CEC_MULTIPLE_LA_CAPABLE", + "value": "(1 << 2)" + } + ] + }, + "DP_CEC_TUNNELING_CONTROL": { + "address": "0x3001", + "bitfields": [ + { + "name": "DP_CEC_TUNNELING_ENABLE", + "value": "(1 << 0)" + }, + { + "name": "DP_CEC_SNOOPING_ENABLE", + "value": "(1 << 1)" + } + ] + }, + "DP_CEC_RX_MESSAGE_INFO": { + "address": "0x3002", + "bitfields": [ + { + "name": "DP_CEC_RX_MESSAGE_LEN_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_CEC_RX_MESSAGE_LEN_SHIFT", + "value": "0" + }, + { + "name": "DP_CEC_RX_MESSAGE_HPD_STATE", + "value": "(1 << 4)" + }, + { + "name": "DP_CEC_RX_MESSAGE_HPD_LOST", + "value": "(1 << 5)" + }, + { + "name": "DP_CEC_RX_MESSAGE_ACKED", + "value": "(1 << 6)" + }, + { + "name": "DP_CEC_RX_MESSAGE_ENDED", + "value": "(1 << 7)" + } + ] + }, + "DP_CEC_TX_MESSAGE_INFO": { + "address": "0x3003", + "bitfields": [ + { + "name": "DP_CEC_TX_MESSAGE_LEN_MASK", + "value": "(0xf << 0)" + }, + { + "name": "DP_CEC_TX_MESSAGE_LEN_SHIFT", + "value": "0" + }, + { + "name": "DP_CEC_TX_RETRY_COUNT_MASK", + "value": "(0x7 << 4)" + }, + { + "name": "DP_CEC_TX_RETRY_COUNT_SHIFT", + "value": "4" + }, + { + "name": "DP_CEC_TX_MESSAGE_SEND", + "value": "(1 << 7)" + } + ] + }, + "DP_CEC_TUNNELING_IRQ_FLAGS": { + "address": "0x3004", + "bitfields": [ + { + "name": "DP_CEC_RX_MESSAGE_INFO_VALID", + "value": "(1 << 0)" + }, + { + "name": "DP_CEC_RX_MESSAGE_OVERFLOW", + "value": "(1 << 1)" + }, + { + "name": "DP_CEC_TX_MESSAGE_SENT", + "value": "(1 << 4)" + }, + { + "name": "DP_CEC_TX_LINE_ERROR", + "value": "(1 << 5)" + }, + { + "name": "DP_CEC_TX_ADDRESS_NACK_ERROR", + "value": "(1 << 6)" + }, + { + "name": "DP_CEC_TX_DATA_NACK_ERROR", + "value": "(1 << 7)" + } + ] + }, + "DP_CEC_LOGICAL_ADDRESS_MASK": { + "address": "0x300E", + "bitfields": [ + { + "name": "DP_CEC_LOGICAL_ADDRESS_0", + "value": "(1 << 0)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_1", + "value": "(1 << 1)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_2", + "value": "(1 << 2)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_3", + "value": "(1 << 3)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_4", + "value": "(1 << 4)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_5", + "value": "(1 << 5)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_6", + "value": "(1 << 6)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_7", + "value": "(1 << 7)" + } + ] + }, + "DP_CEC_LOGICAL_ADDRESS_MASK_2": { + "address": "0x300F", + "bitfields": [ + { + "name": "DP_CEC_LOGICAL_ADDRESS_8", + "value": "(1 << 0)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_9", + "value": "(1 << 1)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_10", + "value": "(1 << 2)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_11", + "value": "(1 << 3)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_12", + "value": "(1 << 4)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_13", + "value": "(1 << 5)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_14", + "value": "(1 << 6)" + }, + { + "name": "DP_CEC_LOGICAL_ADDRESS_15", + "value": "(1 << 7)" + } + ] + }, + "DP_CEC_RX_MESSAGE_BUFFER": { + "address": "0x3010" + }, + "DP_CEC_TX_MESSAGE_BUFFER": { + "address": "0x3020" + }, + "DP_CEC_MESSAGE_BUFFER_LENGTH": { + "address": "0x10" + } + }, + "DP_PROTOCOL": { + "DP_PROTOCOL_CONVERTER_CONTROL_0": { + "address": "0x3050", + "bitfields": [ + { + "name": "DP_HDMI_DVI_OUTPUT_CONFIG", + "value": "(1 << 0) /* DP 1.3 */" + } + ] + }, + "DP_PROTOCOL_CONVERTER_CONTROL_1": { + "address": "0x3051", + "bitfields": [ + { + "name": "DP_CONVERSION_TO_YCBCR420_ENABLE", + "value": "(1 << 0) /* DP 1.3 */" + }, + { + "name": "DP_HDMI_EDID_PROCESSING_DISABLE", + "value": "(1 << 1) /* DP 1.4 */" + }, + { + "name": "DP_HDMI_AUTONOMOUS_SCRAMBLING_DISABLE", + "value": "(1 << 2) /* DP 1.4 */" + }, + { + "name": "DP_HDMI_FORCE_SCRAMBLING", + "value": "(1 << 3) /* DP 1.4 */" + } + ] + }, + "DP_PROTOCOL_CONVERTER_CONTROL_2": { + "address": "0x3052", + "bitfields": [ + { + "name": "DP_CONVERSION_TO_YCBCR422_ENABLE", + "value": "(1 << 0) /* DP 1.3 */" + }, + { + "name": "DP_PCON_ENABLE_DSC_ENCODER", + "value": "(1 << 1)" + }, + { + "name": "DP_PCON_ENCODER_PPS_OVERRIDE_MASK", + "value": "(0x3 << 2)" + }, + { + "name": "DP_PCON_ENC_PPS_OVERRIDE_DISABLED", + "value": "0" + }, + { + "name": "DP_PCON_ENC_PPS_OVERRIDE_EN_PARAMS", + "value": "1" + }, + { + "name": "DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER", + "value": "2" + }, + { + "name": "DP_CONVERSION_RGB_YCBCR_MASK", + "value": "(7 << 4)" + }, + { + "name": "DP_CONVERSION_BT601_RGB_YCBCR_ENABLE", + "value": "(1 << 4)" + }, + { + "name": "DP_CONVERSION_BT709_RGB_YCBCR_ENABLE", + "value": "(1 << 5)" + }, + { + "name": "DP_CONVERSION_BT2020_RGB_YCBCR_ENABLE", + "value": "(1 << 6)" + } + ] + } + }, + "DP_HDCP": { + "DP_HDCP_2_2_REG_RTX_OFFSET": { + "address": "0x69000" + }, + "DP_HDCP_2_2_REG_TXCAPS_OFFSET": { + "address": "0x69008" + }, + "DP_HDCP_2_2_REG_CERT_RX_OFFSET": { + "address": "0x6900B" + }, + "DP_HDCP_2_2_REG_RRX_OFFSET": { + "address": "0x69215" + }, + "DP_HDCP_2_2_REG_RX_CAPS_OFFSET": { + "address": "0x6921D" + }, + "DP_HDCP_2_2_REG_EKPUB_KM_OFFSET": { + "address": "0x69220" + }, + "DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET": { + "address": "0x692A0" + }, + "DP_HDCP_2_2_REG_M_OFFSET": { + "address": "0x692B0" + }, + "DP_HDCP_2_2_REG_HPRIME_OFFSET": { + "address": "0x692C0" + }, + "DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET": { + "address": "0x692E0" + }, + "DP_HDCP_2_2_REG_RN_OFFSET": { + "address": "0x692F0" + }, + "DP_HDCP_2_2_REG_LPRIME_OFFSET": { + "address": "0x692F8" + }, + "DP_HDCP_2_2_REG_EDKEY_KS_OFFSET": { + "address": "0x69318" + }, + "DP_HDCP_2_2_REG_RIV_OFFSET": { + "address": "0x69328" + }, + "DP_HDCP_2_2_REG_RXINFO_OFFSET": { + "address": "0x69330" + }, + "DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET": { + "address": "0x69332" + }, + "DP_HDCP_2_2_REG_VPRIME_OFFSET": { + "address": "0x69335" + }, + "DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET": { + "address": "0x69345" + }, + "DP_HDCP_2_2_REG_V_OFFSET": { + "address": "0x693E0" + }, + "DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET": { + "address": "0x693F0" + }, + "DP_HDCP_2_2_REG_K_OFFSET": { + "address": "0x693F3" + }, + "DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET": { + "address": "0x693F5" + }, + "DP_HDCP_2_2_REG_MPRIME_OFFSET": { + "address": "0x69473" + }, + "DP_HDCP_2_2_REG_RXSTATUS_OFFSET": { + "address": "0x69493" + }, + "DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET": { + "address": "0x69494" + }, + "DP_HDCP_2_2_REG_DBG_OFFSET": { + "address": "0x69518" + } + }, + "DP_TUNNELING": { + "DP_TUNNELING_OUI": { + "address": "0xe0000", + "bitfields": [ + { + "name": "DP_TUNNELING_OUI_BYTES", + "value": "3" + } + ] + }, + "DP_TUNNELING_DEV_ID": { + "address": "0xe0003", + "bitfields": [ + { + "name": "DP_TUNNELING_DEV_ID_BYTES", + "value": "6" + } + ] + }, + "DP_TUNNELING_HW_REV": { + "address": "0xe0009", + "bitfields": [ + { + "name": "DP_TUNNELING_HW_REV_MAJOR_SHIFT", + "value": "4" + }, + { + "name": "DP_TUNNELING_HW_REV_MAJOR_MASK", + "value": "(0xf << DP_TUNNELING_HW_REV_MAJOR_SHIFT)" + }, + { + "name": "DP_TUNNELING_HW_REV_MINOR_SHIFT", + "value": "0" + }, + { + "name": "DP_TUNNELING_HW_REV_MINOR_MASK", + "value": "(0xf << DP_TUNNELING_HW_REV_MINOR_SHIFT)" + } + ] + }, + "DP_TUNNELING_SW_REV_MAJOR": { + "address": "0xe000a" + }, + "DP_TUNNELING_SW_REV_MINOR": { + "address": "0xe000b" + }, + "DP_TUNNELING_CAPABILITIES": { + "address": "0xe000d", + "bitfields": [ + { + "name": "DP_IN_BW_ALLOCATION_MODE_SUPPORT", + "value": "(1 << 7)" + }, + { + "name": "DP_PANEL_REPLAY_OPTIMIZATION_SUPPORT", + "value": "(1 << 6)" + }, + { + "name": "DP_TUNNELING_SUPPORT", + "value": "(1 << 0)" + } + ] + }, + "DP_TUNNELING_STATUS": { + "address": "0xe0025", + "bitfields": [ + { + "name": "DP_BW_ALLOCATION_CAPABILITY_CHANGED", + "value": "(1 << 3)" + }, + { + "name": "DP_ESTIMATED_BW_CHANGED", + "value": "(1 << 2)" + }, + { + "name": "DP_BW_REQUEST_SUCCEEDED", + "value": "(1 << 1)" + }, + { + "name": "DP_BW_REQUEST_FAILED", + "value": "(1 << 0)" + } + ] + }, + "DP_TUNNELING_MAX_LINK_RATE": { + "address": "0xe0028" + }, + "DP_TUNNELING_MAX_LANE_COUNT": { + "address": "0xe0029" + }, + "DP_TUNNELING_MAX_LANE_COUNT_MASK": { + "address": "0x1f" + } + }, + "DP_IN": { + "DP_IN_ADAPTER_INFO": { + "address": "0xe000e", + "bitfields": [ + { + "name": "DP_IN_ADAPTER_NUMBER_BITS", + "value": "7" + }, + { + "name": "DP_IN_ADAPTER_NUMBER_MASK", + "value": "((1 << DP_IN_ADAPTER_NUMBER_BITS) - 1)" + } + ] + }, + "DP_IN_ADAPTER_TUNNEL_INFORMATION": { + "address": "0xe0021", + "bitfields": [ + { + "name": "DP_GROUP_ID_BITS", + "value": "3" + }, + { + "name": "DP_GROUP_ID_MASK", + "value": "((1 << DP_GROUP_ID_BITS) - 1)" + } + ] + } + }, + "DP_USB4": { + "DP_USB4_DRIVER_ID": { + "address": "0xe000f", + "bitfields": [ + { + "name": "DP_USB4_DRIVER_ID_BITS", + "value": "4" + }, + { + "name": "DP_USB4_DRIVER_ID_MASK", + "value": "((1 << DP_USB4_DRIVER_ID_BITS) - 1)" + } + ] + }, + "DP_USB4_DRIVER_BW_CAPABILITY": { + "address": "0xe0020", + "bitfields": [ + { + "name": "DP_USB4_DRIVER_BW_ALLOCATION_MODE_SUPPORT", + "value": "(1 << 7)" + } + ] + } + }, + "DP_BW": { + "DP_BW_GRANULARITY": { + "address": "0xe0022" + }, + "DP_BW_GRANULARITY_MASK": { + "address": "0x3" + } + }, + "DP_ESTIMATED_BW": { + "address": "0xe0023" + }, + "DP_ALLOCATED_BW": { + "address": "0xe0024" + }, + "DP_DPTX_BW_ALLOCATION_MODE_CONTROL": { + "address": "0xe0030", + "bitfields": [ + { + "name": "DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE", + "value": "(1 << 7)" + }, + { + "name": "DP_UNMASK_BW_ALLOCATION_IRQ", + "value": "(1 << 6)" + } + ] + }, + "DP_REQUEST_BW": { + "address": "0xe0031", + "bitfields": [ + { + "name": "MAX_DP_REQUEST_BW", + "value": "255" + } + ] + }, + "DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV": { + "address": "0xf0000" + }, + "DP_Repeater_FEC_CAPABILITY": { + "address": "0xf0004" + }, + "_": { + "__DP_LTTPR1_BASE": { + "address": "0xf0010" + }, + "__DP_LTTPR2_BASE": { + "address": "0xf0060" + }, + "__DP_FEC1_BASE": { + "address": "0xf0290" + }, + "__DP_FEC2_BASE": { + "address": "0xf0298" + } + }, + "DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1": { + "address": "0xf0021", + "bitfields": [ + { + "name": "DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED", + "value": "BIT(0)" + }, + { + "name": "DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED", + "value": "BIT(1)" + } + ] + }, + "DP_SYMBOL": { + "DP_SYMBOL_ERROR_COUNT_LANE0_PHY_REPEATER1": { + "address": "0xf0035" + }, + "DP_SYMBOL_ERROR_COUNT_LANE1_PHY_REPEATER1": { + "address": "0xf0037" + }, + "DP_SYMBOL_ERROR_COUNT_LANE2_PHY_REPEATER1": { + "address": "0xf0039" + }, + "DP_SYMBOL_ERROR_COUNT_LANE3_PHY_REPEATER1": { + "address": "0xf003b" + } + }, + "DP_OUI_PHY_REPEATER1": { + "address": "0xf003d" + }, + "DP_LTTPR_MAX_ADD": { + "address": "0xf02ff" + }, + "DP_PEER": { + "DP_PEER_DEVICE_NONE": { + "address": "0x0" + }, + "DP_PEER_DEVICE_SOURCE_OR_SST": { + "address": "0x1" + }, + "DP_PEER_DEVICE_MST_BRANCHING": { + "address": "0x2" + }, + "DP_PEER_DEVICE_SST_SINK": { + "address": "0x3" + }, + "DP_PEER_DEVICE_DP_LEGACY_CONV": { + "address": "0x4" + } + }, + "DP_GET_MSG_TRANSACTION_VERSION": { + "address": "0x00" + }, + "DP_CONNECTION_STATUS_NOTIFY": { + "address": "0x02" + }, + "DP_ENUM_PATH_RESOURCES": { + "address": "0x10" + }, + "DP_ALLOCATE_PAYLOAD": { + "address": "0x11" + }, + "DP_QUERY": { + "DP_QUERY_PAYLOAD": { + "address": "0x12" + }, + "DP_QUERY_STREAM_ENC_STATUS": { + "address": "0x38", + "bitfields": [ + { + "name": "DP_QUERY_STREAM_ENC_STATUS_STATE_NO_EXIST", + "value": "0" + }, + { + "name": "DP_QUERY_STREAM_ENC_STATUS_STATE_INACTIVE", + "value": "1" + }, + { + "name": "DP_QUERY_STREAM_ENC_STATUS_STATE_ACTIVE", + "value": "2" + } + ] + } + }, + "DP_RESOURCE_STATUS_NOTIFY": { + "address": "0x13" + }, + "DP_CLEAR_PAYLOAD_ID_TABLE": { + "address": "0x14" + }, + "DP_REMOTE": { + "DP_REMOTE_DPCD_READ": { + "address": "0x20" + }, + "DP_REMOTE_DPCD_WRITE": { + "address": "0x21" + }, + "DP_REMOTE_I2C_READ": { + "address": "0x22" + }, + "DP_REMOTE_I2C_WRITE": { + "address": "0x23" + } + }, + "DP_POWER": { + "DP_POWER_UP_PHY": { + "address": "0x24" + }, + "DP_POWER_DOWN_PHY": { + "address": "0x25" + } + }, + "DP_NAK": { + "DP_NAK_WRITE_FAILURE": { + "address": "0x01" + }, + "DP_NAK_INVALID_READ": { + "address": "0x02" + }, + "DP_NAK_CRC_FAILURE": { + "address": "0x03" + }, + "DP_NAK_BAD_PARAM": { + "address": "0x04" + }, + "DP_NAK_DEFER": { + "address": "0x05" + }, + "DP_NAK_LINK_FAILURE": { + "address": "0x06" + }, + "DP_NAK_NO_RESOURCES": { + "address": "0x07" + }, + "DP_NAK_DPCD_FAIL": { + "address": "0x08" + }, + "DP_NAK_I2C_NAK": { + "address": "0x09" + }, + "DP_NAK_ALLOCATE_FAIL": { + "address": "0x0a", + "bitfields": [ + { + "name": "MODE_I2C_START", + "value": "1" + }, + { + "name": "MODE_I2C_WRITE", + "value": "2" + }, + { + "name": "MODE_I2C_READ", + "value": "4" + }, + { + "name": "MODE_I2C_STOP", + "value": "8" + }, + { + "name": "DP_MST_PHYSICAL_PORT_0", + "value": "0" + }, + { + "name": "DP_MST_LOGICAL_PORT_0", + "value": "8" + } + ] + } + }, + "EDP_SDP": { + "EDP_SDP_HEADER_REVISION_MASK": { + "address": "0x1F" + }, + "EDP_SDP_HEADER_VALID_PAYLOAD_BYTES": { + "address": "0x1F" + } + } +} \ No newline at end of file diff --git a/tools/displaytop/include/data.h b/tools/displaytop/include/data.h index 8666a1de0..ff2728b37 100644 --- a/tools/displaytop/include/data.h +++ b/tools/displaytop/include/data.h @@ -34,4 +34,18 @@ extern Node *root; /* shared thorugh out the program */ #define DRM_PRIMARY_PREFIX "card" #define DRM_RENDER_PREFIX "renderD" +#define DPCD_SIZE 1 +#define DPCD_PATH "/dev" +#define DPCD_JSON_FILE "dpcd.json" +#define DATA_SUBDIR "/tools/displaytop/data/" + +typedef struct +{ + char *card_name; + char *dp_name; +} DP_mapping; + +DP_mapping **get_dp_mapping_storage(void); +int *get_dp_mapping_count_ptr(void); + #endif \ No newline at end of file diff --git a/tools/displaytop/include/display.h b/tools/displaytop/include/display.h index dc9e9b4d2..b8f997c69 100644 --- a/tools/displaytop/include/display.h +++ b/tools/displaytop/include/display.h @@ -44,5 +44,6 @@ void display_framebuffer(WINDOW *pad, Node *node, int *content_line); void display_summary(WINDOW *pad, Node *node, int *content_line); void display_debugfs_file(WINDOW *pad, Node *node, int *content_line); +void display_dpcd(WINDOW *pad, Node *node, int *content_line); #endif \ No newline at end of file diff --git a/tools/displaytop/include/populate.h b/tools/displaytop/include/populate.h index fee62bbb1..b164f2f78 100644 --- a/tools/displaytop/include/populate.h +++ b/tools/displaytop/include/populate.h @@ -34,5 +34,6 @@ void populate_data(void); void initialize_display_config(void); void initialize_display_debugfs(void); +void initialize_DPCD(void); #endif \ No newline at end of file diff --git a/tools/displaytop/include/utils.h b/tools/displaytop/include/utils.h index eef5df5f3..cb3e90f33 100644 --- a/tools/displaytop/include/utils.h +++ b/tools/displaytop/include/utils.h @@ -57,6 +57,8 @@ #include #include +#include + #include "log.h" #include "node.h" #include "data.h" @@ -99,7 +101,7 @@ void ensure_dump_directory(void); void strip_whitespace(char *str); char *read_file(const char *filename); -char *get_file_path(const char *filename); +char *get_data_file_path(const char *filename); /* utils_drm.c */ char *find_drm_device(bool primary); @@ -112,4 +114,8 @@ const char *get_basic_modifier_str(uint64_t modifier); const char *get_connector_type_name(uint32_t connector_type); const char *get_encoder_type_name(uint32_t encoder_type); +int read_dpcd_registers(const char *aux_path, unsigned char *buffer, size_t size, uint64_t offset); + +uint64_t parse_hex_address(const char *hex_str); + #endif \ No newline at end of file diff --git a/tools/displaytop/meson.build b/tools/displaytop/meson.build index 09f14063d..083929f26 100644 --- a/tools/displaytop/meson.build +++ b/tools/displaytop/meson.build @@ -61,6 +61,9 @@ if have_displaytop 'src/utils_search.c', 'src/populate_display_debugfs.c', 'src/display_debugfs.c', + 'src/display_DPCD.c', + 'src/populate_DPCD.c', + 'src/utils_registers.c', ) if meson.is_subproject() @@ -100,4 +103,4 @@ if have_displaytop else message('Skipping displaytop: missing required optional dependencies.') -endif \ No newline at end of file +endif diff --git a/tools/displaytop/src/data.c b/tools/displaytop/src/data.c index 3aed6292b..62e9994fc 100644 --- a/tools/displaytop/src/data.c +++ b/tools/displaytop/src/data.c @@ -26,4 +26,17 @@ /* This file holds all the global variables that are being used in the tool */ -Node *root; \ No newline at end of file +Node *root; + +static DP_mapping *dp_mappings = NULL; +static int dp_mapping_count = 0; + +DP_mapping **get_dp_mapping_storage(void) +{ + return &dp_mappings; +} + +int *get_dp_mapping_count_ptr(void) +{ + return &dp_mapping_count; +} \ No newline at end of file diff --git a/tools/displaytop/src/display_DPCD.c b/tools/displaytop/src/display_DPCD.c new file mode 100644 index 000000000..39d6a8c8d --- /dev/null +++ b/tools/displaytop/src/display_DPCD.c @@ -0,0 +1,277 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" +#include "data.h" + +/** + * @brief Reads DPCD (DisplayPort Configuration Data) registers. + * + * This function reads a specified number of bytes from the DPCD registers + * starting at a given offset and stores the data in the provided buffer. + * + * @param aux_path The path to the AUX device file. + * @param buffer A pointer to the buffer where the read data will be stored. + * @param size The number of bytes to read from the DPCD registers. + * @param offset The starting offset in the DPCD registers from which to begin reading. + * @return An integer indicating the success or failure of the read operation. + * Typically, 0 indicates success, while a negative value indicates an error. + */ +int read_dpcd_registers(const char *aux_path, unsigned char *buffer, size_t size, uint64_t offset) +{ + FILE *fp = NULL; + size_t bytes_read = 0; + + fp = fopen(aux_path, "rb"); + if (!fp) + { + log_message(LOG_ERROR, "Failed to open DPCD AUX path: %s", aux_path); + return -1; + } + + if (fseek(fp, offset, SEEK_SET) != 0) + { + log_message(LOG_ERROR, "Failed to seek to offset %ld in %s", offset, aux_path); + fclose(fp); + return -1; + } + + bytes_read = fread(buffer, 1, size, fp); + fclose(fp); + + if (bytes_read != size) + { + log_message(LOG_WARNING, "Expected %zu bytes but read %zu bytes", size, bytes_read); + } + + return (int)bytes_read; +} + +/** + * @brief Displays DPCD (DisplayPort Configuration Data) information on a given pad window. + * + * This function takes a pad window, a JSON object representing the DPCD register data, + * and a pointer to an integer representing the current line number. It displays the + * DPCD data on the pad window, starting from the specified line. + * + * @param pad A pointer to the pad window where the DPCD data will be displayed. + * @param reg A cJSON object containing the DPCD register data. + * @param line A pointer to an integer representing the current line number. This will be + * updated as the function writes data to the pad window. + */ +static void display_dpcd_data(WINDOW *pad, cJSON *reg, int *line) +{ + int width, col_widths[2], bitFieldCount = 0; + cJSON *bitField, *bitFieldItem, *name, *value; + char *name_buffer, *value_buffer; + + if (!cJSON_IsObject(reg)) + { + print_red_text(pad, (*line)++, 1, "Invalid JSON object"); + return; + } + + bitField = cJSON_GetObjectItem(reg, "bitfields"); + if (!bitField) + { + print_red_text(pad, (*line)++, 1, "Bitfields not found in Register"); + return; + } + + print_bold_text(pad, (*line)++, 1, "Bitfield Information: "); + (*line)++; + + width = getmaxx(pad); + width -= 2; + width -= 3; + + col_widths[0] = (int)(width * 0.30f) - 2; + col_widths[1] = (int)(width * 0.70f) - 2; + + if (cJSON_IsArray(bitField)) + bitFieldCount = cJSON_GetArraySize(bitField); + else + bitFieldCount = 1; + + wattron(pad, A_BOLD); + mvwprintw(pad, (*line)++, 1, "| %-*s | %-*s |", col_widths[0], "BitField", col_widths[1], "Value & Description"); + wattroff(pad, A_BOLD); + + for (int i = 0; i < bitFieldCount; i++) + { + if (cJSON_IsArray(bitField)) + bitFieldItem = cJSON_GetArrayItem(bitField, i); + else + bitFieldItem = bitField; + + name = cJSON_GetObjectItem(bitFieldItem, "name"); + value = cJSON_GetObjectItem(bitFieldItem, "value"); + + name_buffer = name && cJSON_IsString(name) ? strdup(name->valuestring) : strdup("NA"); + value_buffer = value && cJSON_IsString(value) ? strdup(value->valuestring) : strdup("NA"); + + mvwprintw(pad, (*line)++, 1, "| %-*s | %-*s |", col_widths[0], name_buffer, col_widths[1], value_buffer); + + free(value_buffer); + free(name_buffer); + } +} + +/** + * @brief Displays the DPCD (DisplayPort Configuration Data) information for a given node. + * + * This function finds the parent node with "DP" in its name, retrieves the corresponding + * DPCD AUX path, reads the DPCD JSON file to get the register offset, and then reads + * and displays the DPCD register data. + * + * @param pad The window pad where the information will be displayed. + * @param node The node for which the DPCD information is to be displayed. + * @param content_line Pointer to an integer that will be updated with the current line number. + * + * The function performs the following steps: + * 1. Finds the parent node with "DP" in its name. + * 2. Retrieves the corresponding DPCD AUX path from the DP mappings. + * 3. Reads the DPCD JSON file to get the register offset for the node. + * 4. Reads the DPCD register data from the DPCD AUX path. + * 5. Displays the DPCD register data and additional information on the pad. + * + * If any step fails, an appropriate error message is displayed on the pad. + */ +void display_dpcd(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + Node *parent = node; + DP_mapping *mappings = *get_dp_mapping_storage(); + int count = *get_dp_mapping_count_ptr(); + char dpcd_aux_path[256] = ""; + char *json_data = NULL; + cJSON *json = NULL, *node_item = NULL, *Address = NULL; + uint64_t offset = 0; + unsigned char dpcd_buffer[DPCD_SIZE] = {0}; + + while (parent != NULL && strstr(parent->name, "DP ") == NULL) + { + parent = parent->parent; + } + + if (parent == NULL) + { + print_red_text(pad, line++, 1, "No parent with 'DP' found"); + *content_line = line; + return; + } + + for (int i = 0; i < count; i++) + { + if (strcmp(mappings[i].card_name, parent->name) == 0) + { + snprintf(dpcd_aux_path, sizeof(dpcd_aux_path), "/dev/%s", mappings[i].dp_name); + break; + } + } + + print_bold_text(pad, line++, 1, "DPCD AUX Path: %s\n", dpcd_aux_path); + + json_data = read_file(get_data_file_path(DPCD_JSON_FILE)); + if (!json_data) + { + print_red_text(pad, line++, 1, "Failed to read DPCD JSON file"); + *content_line = line; + return; + } + + json = cJSON_Parse(json_data); + if (!json) + { + print_red_text(pad, line++, 1, "Failed to parse DPCD JSON content"); + free(json_data); + *content_line = line; + return; + } + + node_item = cJSON_GetObjectItemCaseSensitive(json, node->name); + if (!node_item) + { + cJSON *child = NULL; + cJSON_ArrayForEach(child, json) + { + node_item = cJSON_GetObjectItemCaseSensitive(child, node->name); + if (node_item) + { + break; + } + } + } + + if (!node_item) + { + print_red_text(pad, line++, 1, "Node item '%s' not found in JSON", node->name); + cJSON_Delete(json); + free(json_data); + *content_line = line; + return; + } + + Address = cJSON_GetObjectItem(node_item, "address"); + if (Address) + { + offset = parse_hex_address(Address->valuestring); + } + else + { + print_red_text(pad, line++, 1, "Address not found in Register"); + return; + } + + line++; + print_bold_text(pad, line++, 1, "DPCD Register: %s", node->name); + print_green_text(pad, line++, 1, "DPCD Register Offset: 0x%lx", offset); + line++; + + if (read_dpcd_registers(dpcd_aux_path, dpcd_buffer, DPCD_SIZE, offset) > 0) + { + print_bold_text(pad, line++, 1, "DPCD Data:"); + print_bold_text(pad, line++, 1, "-------------------------------------------------"); + for (int i = 0; i < DPCD_SIZE; i++) + { + mvwprintw(pad, line++, 1, "0x%02x (", dpcd_buffer[i]); + for (int bit = 7; bit >= 0; bit--) + { + wprintw(pad, "%d", (dpcd_buffer[i] >> bit) & 1); + } + wprintw(pad, ")"); + } + print_bold_text(pad, line++, 1, "-------------------------------------------------"); + } + else + { + print_red_text(pad, line++, 1, "Failed to read DPCD registers"); + } + + line++; + display_dpcd_data(pad, node_item, &line); + + *content_line = line; + return; +} diff --git a/tools/displaytop/src/populate.c b/tools/displaytop/src/populate.c index fd7fca48f..947e9dbc3 100644 --- a/tools/displaytop/src/populate.c +++ b/tools/displaytop/src/populate.c @@ -32,4 +32,5 @@ void populate_data(void) initialize_display_config(); initialize_display_debugfs(); + initialize_DPCD(); } diff --git a/tools/displaytop/src/populate_DPCD.c b/tools/displaytop/src/populate_DPCD.c new file mode 100644 index 000000000..25c3faedb --- /dev/null +++ b/tools/displaytop/src/populate_DPCD.c @@ -0,0 +1,250 @@ +/* + * Copyright © 2025 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 "data.h" +#include "populate.h" + +#define DRM_PATH "/sys/class/drm" + +static char *find_dp_card(const char *dp_aux) +{ + struct dirent *entry; + DIR *dp; + DP_mapping **dp_mappings; + int *dp_mapping_count; + char *result; + char aux_path[512]; + char *dp_name; + char *formatted_name; + char *p; + + dp = opendir(DRM_PATH); + if (!dp) + { + log_message(LOG_ERROR, "Failed to open DRM path"); + return NULL; + } + + dp_mappings = get_dp_mapping_storage(); + dp_mapping_count = get_dp_mapping_count_ptr(); + result = NULL; + + while ((entry = readdir(dp)) != NULL) + { + if (strstr(entry->d_name, "card") && strstr(entry->d_name, "-DP")) + { + snprintf(aux_path, sizeof(aux_path), "%s/%s/%s", DRM_PATH, entry->d_name, dp_aux); + + if (access(aux_path, F_OK) == 0) + { + *dp_mappings = realloc(*dp_mappings, (*dp_mapping_count + 1) * sizeof(DP_mapping)); + if (!*dp_mappings) + { + log_message(LOG_ERROR, "Failed to allocate memory for DP mappings"); + closedir(dp); + return NULL; + } + + dp_name = strstr(entry->d_name, "-DP") + 1; + formatted_name = strdup(dp_name); + if (!formatted_name) + { + log_message(LOG_ERROR, "Failed to allocate memory for formatted name"); + closedir(dp); + return NULL; + } + + for (p = formatted_name; *p; p++) + { + if (*p == '-') + { + *p = ' '; + } + } + + (*dp_mappings)[*dp_mapping_count].card_name = strdup(formatted_name); + (*dp_mappings)[*dp_mapping_count].dp_name = strdup(dp_aux); + if (!(*dp_mappings)[*dp_mapping_count].card_name || !(*dp_mappings)[*dp_mapping_count].dp_name) + { + log_message(LOG_ERROR, "Failed to allocate memory for DP mapping names"); + free(formatted_name); + closedir(dp); + return NULL; + } + (*dp_mapping_count)++; + + result = strdup(formatted_name); + free(formatted_name); + break; + } + } + } + closedir(dp); + return result; +} + +static void process_DPCD_json_Helper(cJSON *json, Node *parentNode) +{ + cJSON *reg = NULL; + const char *name; + Node *currentNode; + cJSON *address; + + cJSON_ArrayForEach(reg, json) + { + name = reg->string; + if (name) + { + currentNode = create_node(name, display_dpcd, parentNode); + + address = cJSON_GetObjectItem(reg, "address"); + if (!address) + { + currentNode->display_function = NULL; + } + + if (cJSON_IsObject(reg) && !address) + { + process_DPCD_json_Helper(reg, currentNode); + } + add_child(parentNode, currentNode); + } + } +} + +static void process_DPCD_json(Node *parentNode) +{ + FILE *file; + long fileSize; + char *jsonContent; + cJSON *json; + + log_message(LOG_INFO, "Opening DPCD json file"); + file = fopen(get_data_file_path(DPCD_JSON_FILE), "r"); + if (!file) + { + log_message(LOG_ERROR, "Failed to open DPCD json file"); + return; + } + + log_message(LOG_INFO, "Reading DPCD json file"); + fseek(file, 0, SEEK_END); + fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + + jsonContent = malloc(fileSize + 1); + if (!jsonContent) + { + log_message(LOG_ERROR, "Failed to allocate memory for json content"); + fclose(file); + return; + } + + fread(jsonContent, 1, fileSize, file); + jsonContent[fileSize] = '\0'; + fclose(file); + + log_message(LOG_INFO, "Parsing DPCD json content"); + json = cJSON_Parse(jsonContent); + if (!json) + { + log_message(LOG_ERROR, "Failed to parse DPCD json"); + free(jsonContent); + return; + } + + process_DPCD_json_Helper(json, parentNode); + + log_message(LOG_INFO, "Cleaning up"); + cJSON_Delete(json); + free(jsonContent); +} + +void initialize_DPCD(void) +{ + DIR *dir; + struct dirent *entry; + static char dpcd_path[256]; + Node *DPCD; + char *dp_aux; + + if (access(get_data_file_path(DPCD_JSON_FILE), F_OK) != 0) + { + log_message(LOG_ERROR, "DPCD json file does not exist"); + return; + } + + DPCD = create_node("DPCD Registers", NULL, root); + if (!DPCD) + { + log_message(LOG_ERROR, "Failed to create DPCD node"); + log_message(LOG_ERROR, "malloc"); + return; + } + + dir = opendir(DPCD_PATH); + if (!dir) + { + log_message(LOG_ERROR, "Failed to open debugfs dri directory (Run as root?)"); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + if (strstr(entry->d_name, "drm_dp") != NULL) + { + Node *dpcdNode; + + if (snprintf(dpcd_path, sizeof(dpcd_path), "%s/%s", DPCD_PATH, entry->d_name) >= (int)sizeof(dpcd_path)) + { + log_message(LOG_ERROR, "DPCD AUX path truncated: %s/%s", DPCD_PATH, entry->d_name); + continue; + } + + log_message(LOG_INFO, "Found DPCD AUX path: %s", dpcd_path); + + dp_aux = find_dp_card(entry->d_name); + if (!dp_aux) + { + log_message(LOG_ERROR, "Failed to find DP card for %s", entry->d_name); + continue; + } + + dpcdNode = create_node(dp_aux, NULL, DPCD); + if (!dpcdNode) + { + log_message(LOG_ERROR, "Failed to create node for %s", dp_aux); + free(dp_aux); + closedir(dir); + return; + } + + process_DPCD_json(dpcdNode); + add_child(DPCD, dpcdNode); + free(dp_aux); + } + } + + closedir(dir); + add_child(root, DPCD); +} \ No newline at end of file diff --git a/tools/displaytop/src/utils.c b/tools/displaytop/src/utils.c index ec8c8a649..6d1b305b4 100644 --- a/tools/displaytop/src/utils.c +++ b/tools/displaytop/src/utils.c @@ -23,6 +23,72 @@ #include "utils.h" +#include +#include +#include +#include +#include + +char *get_data_file_path(const char *filename) { + static char final_path[PATH_MAX]; + char exe_path[PATH_MAX]; + char base_path[PATH_MAX]; + int written =0; + ssize_t len; + char *match; + char *cursor; + char *after; + size_t base_len; + size_t required_len; + + len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len == -1) { + log_message(LOG_ERROR, "readlink failed: %s\n", strerror(errno)); + return NULL; + } + exe_path[len] = '\0'; + + match = NULL; + cursor = exe_path; + while ((cursor = strstr(cursor, "igt-gpu-tools"))) { + char next_char = cursor[strlen("igt-gpu-tools")]; + if (next_char == '/' || next_char == '-' || next_char == '\0') { + match = cursor; + } + cursor += strlen("igt-gpu-tools"); + } + + if (!match) { + log_message(LOG_ERROR, "Could not find igt-gpu-tools directory in path: %s\n", exe_path); + return NULL; + } + + after = strchr(match, '/'); + base_len = after ? (size_t)(after - exe_path) : strlen(exe_path); + + if (base_len >= sizeof(base_path)) { + log_message(LOG_ERROR, "Base path too long.\n"); + return NULL; + } + + memcpy(base_path, exe_path, base_len); + base_path[base_len] = '\0'; + + required_len = base_len + strlen(DATA_SUBDIR) + strlen(filename) + 1; + if (required_len >= sizeof(final_path)) { + log_message(LOG_ERROR, "Data file path too long. Buffer would overflow.\n"); + return NULL; + } + + written = snprintf(final_path, sizeof(final_path), "%s%s%s", base_path, DATA_SUBDIR, filename); + if (written < 0 || (size_t)written >= sizeof(final_path)) { + log_message(LOG_ERROR, "snprintf failed or truncated.\n"); + return NULL; + } + + return final_path; +} + char *read_file(const char *filename) { FILE *file; diff --git a/tools/displaytop/src/utils_registers.c b/tools/displaytop/src/utils_registers.c new file mode 100644 index 000000000..75b3d31e4 --- /dev/null +++ b/tools/displaytop/src/utils_registers.c @@ -0,0 +1,97 @@ +/* + * Copyright © 2025 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 "utils.h" + +/** + * @brief Parses a hexadecimal address string and converts it to a 64-bit unsigned integer. + * + * This function takes a hexadecimal string representation of an address, optionally + * cleans it by removing a trailing 'h' or 'H', and converts it to a uint64_t value. + * It also handles strings prefixed with "0x" or "0X". + * + * @param hex_str A pointer to the hexadecimal string to be parsed. The string can + * optionally end with 'h' or 'H' and may start with "0x" or "0X". + * Passing NULL or an empty string will result in an error. + * + * @return The parsed 64-bit unsigned integer address. If the input string is invalid + * or the conversion fails, the function logs an error and returns 0. + * + * @note The function logs errors using the `log_message` function with the `LOG_ERROR` level + * in the following cases: + * - The input string is NULL or empty. + * - Memory allocation fails while cleaning the input string. + * - The input string cannot be converted to a valid hexadecimal number. + * + * @note The caller is responsible for ensuring that the input string is properly null-terminated. + */ +uint64_t parse_hex_address(const char *hex_str) +{ + size_t len; + char *cleaned = NULL; + char *start = NULL; + char *endptr; + uint64_t address = 0; + + if (!hex_str || *hex_str == '\0') + { + log_message(LOG_ERROR, "Invalid hex string: NULL or empty"); + return 0; + } + + len = strlen(hex_str); + + if (hex_str[len - 1] == 'h' || hex_str[len - 1] == 'H') + { + cleaned = strndup(hex_str, len - 1); + } + else + { + cleaned = strdup(hex_str); + } + + if (!cleaned) + { + log_message(LOG_ERROR, "Memory allocation failed while cleaning hex string"); + return 0; + } + + start = cleaned; + if (cleaned[0] == '0' && (cleaned[1] == 'x' || cleaned[1] == 'X')) + { + start += 2; + } + + errno = 0; + address = strtoull(start, &endptr, 16); + + if (errno != 0 || *endptr != '\0') + { + log_message(LOG_ERROR, "Invalid hex string: conversion failed"); + free(cleaned); + return 0; + } + + free(cleaned); + return address; +} From patchwork Tue May 27 09:09:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [4/4] tools/displaytop: Add ftrace support to Display Top From: Adith Narein T X-Patchwork-Id: 655766 Message-Id: <20250527090938.1765854-5-adith.narein.t@intel.com> To: intel-gfx-trybot@lists.freedesktop.org Cc: chaitanya.kumar.borah@intel.com, adith.narein.t@intel.com Date: Tue, 27 May 2025 14:39:34 +0530 This patch adds support for live tracing using ftrace in the DisplayTop tool. It enables real-time monitoring with options for filtering, dumping, and clearing the trace data. Key features introduced: - Live ftrace output visualization within the terminal UI - Filter trace logs by register address for focused debugging - Dump current trace buffer to a file for offline analysis - Clear trace buffer on demand via the UI Signed-off-by: Adith Narein T --- tools/displaytop/README.md | 2 + tools/displaytop/include/data.h | 1 + tools/displaytop/include/display.h | 5 + tools/displaytop/include/populate.h | 1 + tools/displaytop/include/utils.h | 4 + tools/displaytop/meson.build | 6 + tools/displaytop/src/display_dump_tracing.c | 135 ++++++++ tools/displaytop/src/display_filter_tracing.c | 318 ++++++++++++++++++ tools/displaytop/src/display_ftrace.c | 97 ++++++ tools/displaytop/src/display_live_tracing.c | 124 +++++++ tools/displaytop/src/populate.c | 1 + tools/displaytop/src/populate_ftrace.c | 78 +++++ tools/displaytop/src/utils_trace.c | 67 ++++ 13 files changed, 839 insertions(+) create mode 100644 tools/displaytop/src/display_dump_tracing.c create mode 100644 tools/displaytop/src/display_filter_tracing.c create mode 100644 tools/displaytop/src/display_ftrace.c create mode 100644 tools/displaytop/src/display_live_tracing.c create mode 100644 tools/displaytop/src/populate_ftrace.c create mode 100644 tools/displaytop/src/utils_trace.c diff --git a/tools/displaytop/README.md b/tools/displaytop/README.md index cd3b004d6..03f0fd0bc 100644 --- a/tools/displaytop/README.md +++ b/tools/displaytop/README.md @@ -9,6 +9,8 @@ A terminal-based tool for monitoring and debugging the display pipeline. - Real-time display Configuration - view Live Display Debugfs in a terminal ui - Read & view DPCD register value & bitfield information +- Formatted Live trace using Ftrace +- filter the formatted ftrace by register address/range --- diff --git a/tools/displaytop/include/data.h b/tools/displaytop/include/data.h index ff2728b37..22acbab99 100644 --- a/tools/displaytop/include/data.h +++ b/tools/displaytop/include/data.h @@ -47,5 +47,6 @@ typedef struct DP_mapping **get_dp_mapping_storage(void); int *get_dp_mapping_count_ptr(void); +#define TRACE_PATH "/sys/kernel/debug/tracing/trace" #endif \ No newline at end of file diff --git a/tools/displaytop/include/display.h b/tools/displaytop/include/display.h index b8f997c69..b3d457a25 100644 --- a/tools/displaytop/include/display.h +++ b/tools/displaytop/include/display.h @@ -45,5 +45,10 @@ void display_summary(WINDOW *pad, Node *node, int *content_line); void display_debugfs_file(WINDOW *pad, Node *node, int *content_line); void display_dpcd(WINDOW *pad, Node *node, int *content_line); +void display_ftrace_options(WINDOW *pad, Node *node, int *content_line); +void display_live_tracing(WINDOW *pad, Node *node, int *content_line); +void display_dump_tracing(WINDOW *pad, Node *node, int *content_line); +void display_filter_tracing(WINDOW *pad, Node *node, int *content_line); +void display_clear_ftrace(WINDOW *pad, Node *node, int *content_line); #endif \ No newline at end of file diff --git a/tools/displaytop/include/populate.h b/tools/displaytop/include/populate.h index b164f2f78..dc0cbc5b2 100644 --- a/tools/displaytop/include/populate.h +++ b/tools/displaytop/include/populate.h @@ -35,5 +35,6 @@ void populate_data(void); void initialize_display_config(void); void initialize_display_debugfs(void); void initialize_DPCD(void); +void initialize_ftrace(void); #endif \ No newline at end of file diff --git a/tools/displaytop/include/utils.h b/tools/displaytop/include/utils.h index cb3e90f33..4199d3226 100644 --- a/tools/displaytop/include/utils.h +++ b/tools/displaytop/include/utils.h @@ -117,5 +117,9 @@ const char *get_encoder_type_name(uint32_t encoder_type); int read_dpcd_registers(const char *aux_path, unsigned char *buffer, size_t size, uint64_t offset); uint64_t parse_hex_address(const char *hex_str); +/* utils_trace.c */ +void free_cache(void); +int clear_ftrace(void); +void ensure_tracing_on(void); #endif \ No newline at end of file diff --git a/tools/displaytop/meson.build b/tools/displaytop/meson.build index 083929f26..2b3faba4e 100644 --- a/tools/displaytop/meson.build +++ b/tools/displaytop/meson.build @@ -64,6 +64,12 @@ if have_displaytop 'src/display_DPCD.c', 'src/populate_DPCD.c', 'src/utils_registers.c', + 'src/utils_trace.c', + 'src/populate_ftrace.c', + 'src/display_ftrace.c', + 'src/display_live_tracing.c', + 'src/display_dump_tracing.c', + 'src/display_filter_tracing.c', ) if meson.is_subproject() diff --git a/tools/displaytop/src/display_dump_tracing.c b/tools/displaytop/src/display_dump_tracing.c new file mode 100644 index 000000000..9e5ff6ecf --- /dev/null +++ b/tools/displaytop/src/display_dump_tracing.c @@ -0,0 +1,135 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" + +void display_dump_tracing(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + FILE *trace_fp = NULL, *dump_fp = NULL; + char buffer[512]; + time_t now; + struct tm *t = NULL; + char dump_file_path[256]; + int width, col_widths[6]; + int total_count = 0; + WINDOW *progress_win = NULL; + size_t log_count = 0; + int current_count = 0; + + now = time(NULL); + t = localtime(&now); + log_message(LOG_INFO, "Dumping trace logs for node: %s", node->name); + snprintf(dump_file_path, sizeof(dump_file_path), DUMP_DIR "/Ftrace_dump_"); + strftime(dump_file_path + strlen(dump_file_path), sizeof(dump_file_path) - strlen(dump_file_path), "%Y-%m-%d-%H.%M.%S.txt", t); + + ensure_tracing_on(); + ensure_dump_directory(); + + trace_fp = fopen(TRACE_PATH, "r"); + if (!trace_fp) + { + log_message(LOG_ERROR, "Error opening trace file: %s", strerror(errno)); + *content_line = line; + return; + } + + dump_fp = fopen(dump_file_path, "w"); + if (!dump_fp) + { + log_message(LOG_ERROR, "Error creating dump file: %s", strerror(errno)); + fclose(trace_fp); + *content_line = line; + return; + } + chmod(dump_file_path, 0777); + + width = getmaxx(pad) - 8; + + col_widths[0] = width * 0.10f; + col_widths[1] = width * 0.05f; + col_widths[2] = width * 0.10f; + col_widths[3] = width * 0.08f; + col_widths[4] = width * 0.10f; + col_widths[5] = width * 0.25f; + + wclear(pad); + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "Ftrace Formatted & Dumped!"); + wattroff(pad, A_BOLD); + + while (fgets(buffer, sizeof(buffer), trace_fp) != NULL) + { + if (strstr(buffer, "i915_reg_rw") != NULL) + total_count++; + } + rewind(trace_fp); + + progress_win = newwin(getmaxy(stdscr), getmaxx(stdscr), 0, 0); + wbkgd(progress_win, COLOR_PAIR(6)); + wclear(progress_win); + box(progress_win, 0, 0); + print_bold_text(progress_win, 8, 2, "DUMPING IS IN PROGRESS :)"); + wrefresh(progress_win); + + while (fgets(buffer, sizeof(buffer), trace_fp) != NULL) + { + char process[32], op[8]; + int pid, cpu, reg, val1, val2, len; + double timestamp; + char value_str[64]; + + if (strstr(buffer, "i915_reg_rw") == NULL) + continue; + + if (sscanf(buffer, "%31[^-]-%d [%d] %*s %lf: i915_reg_rw: %7s reg=0x%x, len=%d, val=(0x%x, 0x%x)", + process, &pid, &cpu, ×tamp, op, ®, &len, &val1, &val2) == 9) + { + strip_whitespace(process); + strip_whitespace(op); + + snprintf(value_str, sizeof(value_str), "(0x%X, 0x%X)", val1, val2); + + fprintf(dump_fp, "| %-*s | %-*d | %-*.6f | %-*s | 0x%-*X | %-*s |\n", + col_widths[0], process, + col_widths[1], cpu, + col_widths[2], timestamp, + col_widths[3], op, + col_widths[4] - 2, reg, + col_widths[5], value_str); + + log_count++; + current_count++; + render_progress_bar(progress_win, log_count, total_count); + } + } + + mvwprintw(pad, line + 2, 1, "Dump saved to: %s", dump_file_path); + *content_line = line; + + fclose(trace_fp); + fclose(dump_fp); + + delwin(progress_win); +} \ No newline at end of file diff --git a/tools/displaytop/src/display_filter_tracing.c b/tools/displaytop/src/display_filter_tracing.c new file mode 100644 index 000000000..66837457f --- /dev/null +++ b/tools/displaytop/src/display_filter_tracing.c @@ -0,0 +1,318 @@ +/* + * Copyright © 2025 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 "display.h" +#include "utils.h" + +static char *get_range_input(void) +{ + int rows, cols; + WINDOW *overlay; + FIELD *fields[2]; + FORM *form; + char *input_buffer; + int ch, pos; + + rows = cols = 0; + overlay = NULL; + fields[0] = fields[1] = NULL; + form = NULL; + input_buffer = NULL; + ch = pos = 0; + + wclear(stdscr); + wrefresh(stdscr); + getmaxyx(stdscr, rows, cols); + overlay = newwin(rows, cols, 0, 0); + wbkgd(overlay, COLOR_PAIR(6)); + keypad(overlay, TRUE); + box(overlay, 0, 0); + wrefresh(overlay); + + print_bold_text(overlay, 1, 1, "Trace Log Filtering - Instructions"); + mvwprintw(overlay, 3, 2, "1. Enter a single register address (e.g., 0x40000)"); + mvwprintw(overlay, 4, 2, "2. Enter an address range (e.g., 0x40000-0x40400)"); + mvwprintw(overlay, 5, 2, "3. Press ENTER to apply filter, ESC to exit."); + mvwhline(overlay, 6, 1, 0, cols - 2); + + fields[0] = new_field(1, cols - 4, 1, 1, 0, 0); + fields[1] = NULL; + + set_field_back(fields[0], A_UNDERLINE); + field_opts_off(fields[0], O_AUTOSKIP); + + form = new_form(fields); + set_form_win(form, overlay); + set_form_sub(form, derwin(overlay, 3, cols - 2, 7, 1)); + + wbkgd(form_sub(form), COLOR_PAIR(7)); + box(form_sub(form), 0, 0); + post_form(form); + wrefresh(overlay); + + input_buffer = malloc(256); + if (!input_buffer) + { + log_message(LOG_ERROR, "Memory allocation failed for input buffer"); + return NULL; + } + memset(input_buffer, 0, 256); + + pos = 0; + + while ((ch = getch()) != 27) + { + switch (ch) + { + case KEY_BACKSPACE: + case 127: + case 8: + if (pos > 0) + { + pos--; + input_buffer[pos] = '\0'; + form_driver(form, REQ_DEL_PREV); + } + break; + case '\n': + if (pos > 0) + { + unpost_form(form); + free_form(form); + free_field(fields[0]); + wclear(overlay); + wrefresh(overlay); + delwin(overlay); + return input_buffer; + } + break; + default: + if (pos < 255) + { + input_buffer[pos++] = ch; + input_buffer[pos] = '\0'; + form_driver(form, ch); + } + break; + } + wrefresh(overlay); + } + + if (form) + unpost_form(form), free_form(form); + if (fields[0]) + free_field(fields[0]); + if (overlay) + wclear(overlay), wrefresh(overlay), delwin(overlay); + if (input_buffer) + free(input_buffer); + + return NULL; +} +void display_filter_tracing(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + char buffer[512]; + size_t log_count = 0; + time_t now = time(NULL); + uint32_t start_addr = 0, end_addr = 0; + FILE *trace_fp = NULL; + FILE *dump_fp = NULL; + char dump_file_path[256]; + char *input_buffer = NULL; + int width = getmaxx(pad) - 9; + int col_widths[6]; + int total_count = 0; + int current_count = 0; + WINDOW *progress_win = NULL; + struct tm *t = NULL; + + col_widths[0] = width * 0.12f; + col_widths[1] = width * 0.06f; + col_widths[2] = width * 0.13f; + col_widths[3] = width * 0.10f; + col_widths[4] = width * 0.15f; + col_widths[5] = width * 0.44f; + + t = localtime(&now); + ensure_dump_directory(); + strftime(dump_file_path, sizeof(dump_file_path), DUMP_DIR "/filtered_trace_dump_%Y-%m-%d_%H.%M.%S.txt", t); + + *content_line = line; + line = 3; + + ensure_tracing_on(); + + print_bold_text(pad, line++, 1, "%s", node->name); + line++; + + input_buffer = get_range_input(); + if (!input_buffer || strlen(input_buffer) == 0) + { + log_message(LOG_ERROR, "Failed to get range input"); + free(input_buffer); + return; + } + + if (strchr(input_buffer, '-') != NULL) + { + if (sscanf(input_buffer, "0x%x-0x%x", &start_addr, &end_addr) != 2) + { + print_bold_text(pad, line++, 2, "Error: Invalid address range! Please retry."); + free(input_buffer); + return; + } + if (start_addr > end_addr) + { + print_bold_text(pad, line++, 2, "Error: Invalid range! Please retry."); + free(input_buffer); + return; + } + } + else + { + if (sscanf(input_buffer, "0x%x", &start_addr) != 1) + { + print_bold_text(pad, line++, 2, "Error: Invalid address! Please retry."); + free(input_buffer); + return; + } + end_addr = start_addr; + } + + trace_fp = fopen(TRACE_PATH, "r"); + if (!trace_fp) + { + log_message(LOG_ERROR, "Error opening trace file: %s", strerror(errno)); + goto cleanup; + } + + dump_fp = fopen(dump_file_path, "w"); + if (!dump_fp) + { + log_message(LOG_ERROR, "Error creating dump file: %s", strerror(errno)); + goto cleanup; + } + + wclear(pad); + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "| %-*s | %-*s | %-*s | %-*s | %-*s | %-*s |", + col_widths[0], "Process", + col_widths[1], "CPU", + col_widths[2], "Timestamp", + col_widths[3], "R/W", + col_widths[4], "Reg", + col_widths[5], "Value"); + wattroff(pad, A_BOLD); + + while (fgets(buffer, sizeof(buffer), trace_fp) != NULL) + { + if (strstr(buffer, "i915_reg_rw") != NULL) + total_count++; + } + rewind(trace_fp); + + progress_win = newwin(getmaxy(stdscr), getmaxx(stdscr), 0, 0); + wbkgd(progress_win, COLOR_PAIR(6)); + wclear(progress_win); + box(progress_win, 0, 0); + print_bold_text(progress_win, 8, 2, "FILTERING IS IN PROGRESS :)"); + wrefresh(progress_win); + + while (fgets(buffer, sizeof(buffer), trace_fp) != NULL) + { + uint32_t reg; + double timestamp; + char value_str[64]; + char process[32], op[8]; + int pid, cpu, val1, val2, len; + + if (strstr(buffer, "i915_reg_rw") == NULL) + continue; + + if (sscanf(buffer, "%31[^-]-%d [%d] %*s %lf: i915_reg_rw: %7s reg=0x%x, len=%d, val=(0x%x, 0x%x)", + process, &pid, &cpu, ×tamp, op, ®, &len, &val1, &val2) == 9) + { + strip_whitespace(process); + strip_whitespace(op); + + snprintf(value_str, sizeof(value_str), "(0x%X, 0x%X)", val1, val2); + + if (reg >= start_addr && reg <= end_addr) + { + log_count++; + + mvwprintw(pad, line++, 1, "| %-*s | %-*d | %-*.2f | %-*s | 0x%-*X | %-*s |", + col_widths[0], process, + col_widths[1], cpu, + col_widths[2], timestamp, + col_widths[3], op, + col_widths[4] - 2, reg, + col_widths[5], value_str); + + fprintf(dump_fp, "| %-*s | %-*d | %-*.2f | %-*s | 0x%-*X | %-*s |\n", + col_widths[0], process, + col_widths[1], cpu, + col_widths[2], timestamp, + col_widths[3], op, + col_widths[4] - 2, reg, + col_widths[5], value_str); + } + + if (line >= getmaxy(pad) - 2) + { + wscrl(pad, 1); + line--; + } + + current_count++; + render_progress_bar(progress_win, current_count, total_count); + } + } + + wattron(pad, A_BOLD | COLOR_PAIR(2)); + mvwprintw(pad, 1, 1, "Filtered range: 0x%X - 0x%X, Hits: %zu", start_addr, end_addr, log_count); + wattroff(pad, A_BOLD | COLOR_PAIR(2)); + + if (log_count == 0) + { + remove(dump_file_path); + mvwprintw(pad, line + 2, 1, "No matching logs found. Dump file not created."); + } + else + { + mvwprintw(pad, line + 2, 1, "Dump saved to: %s", dump_file_path); + } + + *content_line = line; + +cleanup: + if (trace_fp) + fclose(trace_fp); + if (dump_fp) + fclose(dump_fp); + if (input_buffer) + free(input_buffer); +} diff --git a/tools/displaytop/src/display_ftrace.c b/tools/displaytop/src/display_ftrace.c new file mode 100644 index 000000000..779f8b347 --- /dev/null +++ b/tools/displaytop/src/display_ftrace.c @@ -0,0 +1,97 @@ +/* + * Copyright © 2025 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 "node.h" +#include "utils.h" +#include "display.h" + +void display_ftrace_options(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + int size = getmaxx(pad) - 2; + + print_bold_text(pad, line++, 1, "%s Menu", node->name); + line++; + + print_bold_text(pad, line++, 1, "1. Live Tracing"); + wattron(pad, A_DIM); + print_wrapped_text(pad, &line, 1, size, "Formats and displays the LAST 1000 lines of the ftrace logs, refresh (key r) to see the changes updated", false); + wattroff(pad, A_DIM); + line++; + + print_bold_text(pad, line++, 1, "2. Dump Tracing"); + wattron(pad, A_DIM); + print_wrapped_text(pad, &line, 1, size, "Formats and displays the FIRST 1000 lines but also dumps the entire formatted ", false); + wattroff(pad, A_DIM); + line++; + + print_bold_text(pad, line++, 1, "3. Register Range Tracing"); + wattron(pad, A_DIM); + print_wrapped_text(pad, &line, 1, size, "Filters & formats your desired register range, displays the first 1000 lines in Display Top. FILTERED RESULTS ARE DUMPED BY DEFAULT", false); + wattroff(pad, A_DIM); + line++; + + print_bold_text(pad, line++, 1, "4. Clear Ftrace"); + wattron(pad, A_DIM); + print_wrapped_text(pad, &line, 1, size, "Clears the ftrace buffer", false); + wattroff(pad, A_DIM); + line++; + + *content_line = line; +} + +/** + * @brief Clears the Ftrace buffer and updates the display with the result. + * + * This function is responsible for clearing the Ftrace buffer by calling + * the `clear_ftrace` function. It updates the provided `pad` window with + * messages indicating the success or failure of the operation. The number + * of lines written to the `pad` is updated in the `content_line` parameter. + * + * @param pad A pointer to the `WINDOW` object where the messages will be displayed. + * @param node A pointer to a `Node` object (currently unused in this function). + * @param content_line A pointer to an integer that will be updated with the + * number of lines written to the `pad`. + */ +void display_clear_ftrace(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + int status; + + print_bold_text(pad, line++, 1, "%s Menu", node->name); + + line++; + status = clear_ftrace(); + if (status == 0) + { + print_green_text(pad, line++, 2, "Ftrace cleared successfully."); + } + else + { + print_red_text(pad, line++, 2, "Failed to clear Ftrace."); + } + line++; + + wrefresh(pad); + *content_line = line; +} \ No newline at end of file diff --git a/tools/displaytop/src/display_live_tracing.c b/tools/displaytop/src/display_live_tracing.c new file mode 100644 index 000000000..bd4b8ac5e --- /dev/null +++ b/tools/displaytop/src/display_live_tracing.c @@ -0,0 +1,124 @@ +/* + * Copyright © 2025 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 "display.h" + +void display_live_tracing(WINDOW *pad, Node *node, int *content_line) +{ + int line = 0; + FILE *trace; + char buffer[256]; + int width; + int col_widths[6]; + char *log_lines[1000]; + size_t log_count = 0; + + print_bold_text(pad, line++, 1, "%s", node->name); + line++; + + trace = popen("cat /sys/kernel/debug/tracing/trace", "r"); + if (!trace) + { + log_message(LOG_ERROR, "Failed to open trace file: %s", strerror(errno)); + *content_line = line; + return; + } + + width = getmaxx(pad) - 2 - 6; + + col_widths[0] = (int)(width * 0.10f) - 2; + col_widths[1] = (int)(width * 0.05f) - 2; + col_widths[2] = (int)(width * 0.12f) - 2; + col_widths[3] = (int)(width * 0.06f) - 2; + col_widths[4] = (int)(width * 0.10f) - 2; + col_widths[5] = (int)(width * 0.40f) - 2; + + while (fgets(buffer, sizeof(buffer), trace) != NULL) + { + char process[32], op[8], value_str[64]; + double timestamp; + int pid, cpu, reg, val1, val2, len; + char *log_line; + + if (strstr(buffer, "i915_reg_rw") == NULL) + continue; + + if (sscanf(buffer, "%31[^-]-%d [%d] %*[^0-9] %lf: i915_reg_rw: %7s reg=0x%x, len=%d, val=(0x%x, 0x%x)", + process, &pid, &cpu, ×tamp, op, ®, &len, &val1, &val2) == 9) + { + strip_whitespace(process); + strip_whitespace(op); + + snprintf(value_str, sizeof(value_str), "(0x%X, 0x%X)", val1, val2); + + log_line = malloc(1024); + snprintf(log_line, 1024, "| %-*s | %-*d | %-*.6f | %-*s | 0x%-*X | %-*s |", + col_widths[0], process, + col_widths[1], cpu, + col_widths[2], timestamp, + col_widths[3], op, + col_widths[4] - 2, reg, + col_widths[5], value_str); + + if (log_count < 1000) + { + log_lines[log_count++] = log_line; + } + else + { + free(log_lines[0]); + memmove(log_lines, log_lines + 1, (999) * sizeof(char *)); + log_lines[999] = log_line; + } + } + } + + wclear(pad); + line = 0; + + wattron(pad, A_BOLD); + mvwprintw(pad, line++, 1, "| %-*s | %-*s | %-*s | %-*s | %-*s | %-*s |", + col_widths[0], "Process", + col_widths[1], "CPU", + col_widths[2], "Timestamp", + col_widths[3], "Op", + col_widths[4], "Reg", + col_widths[5], "Value"); + wattroff(pad, A_BOLD); + + for (size_t i = 0; i < log_count; i++) + { + mvwprintw(pad, line++, 1, "%s", log_lines[i]); + } + + wrefresh(pad); + + for (size_t i = 0; i < log_count; i++) + { + free(log_lines[i]); + } + + pclose(trace); + + *content_line = line; +} \ No newline at end of file diff --git a/tools/displaytop/src/populate.c b/tools/displaytop/src/populate.c index 947e9dbc3..6344ced00 100644 --- a/tools/displaytop/src/populate.c +++ b/tools/displaytop/src/populate.c @@ -33,4 +33,5 @@ void populate_data(void) initialize_display_config(); initialize_display_debugfs(); initialize_DPCD(); + initialize_ftrace(); } diff --git a/tools/displaytop/src/populate_ftrace.c b/tools/displaytop/src/populate_ftrace.c new file mode 100644 index 000000000..cd963d141 --- /dev/null +++ b/tools/displaytop/src/populate_ftrace.c @@ -0,0 +1,78 @@ +/* + * Copyright © 2025 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 "data.h" +#include "display.h" +#include "populate.h" + +void initialize_ftrace(void) +{ + Node *ftrace; + Node *live_tracing; + Node *dump_tracing; + Node *filter_tracing; + Node *clear_ftrace; + + ftrace = create_node("Ftrace", display_ftrace_options, root); + if (!ftrace) + { + log_message(LOG_ERROR, "Failed to create Ftrace node"); + return; + } + + live_tracing = create_node("Live Tracing", display_live_tracing, ftrace); + if (!live_tracing) + { + log_message(LOG_ERROR, "Failed to create Live Tracing node"); + return; + } + add_child(ftrace, live_tracing); + + dump_tracing = create_node("Dump Tracing", display_dump_tracing, ftrace); + if (!dump_tracing) + { + log_message(LOG_ERROR, "Failed to create Dump Tracing node"); + return; + } + add_child(ftrace, dump_tracing); + + filter_tracing = create_node("Filter Tracing", display_filter_tracing, ftrace); + if (!filter_tracing) + { + log_message(LOG_ERROR, "Failed to create Register Range Tracing node"); + return; + } + add_child(ftrace, filter_tracing); + + clear_ftrace = create_node("Clear Ftrace", display_clear_ftrace, ftrace); + if (!clear_ftrace) + { + log_message(LOG_ERROR, "Failed to create Clear Ftrace node"); + return; + } + add_child(ftrace, clear_ftrace); + + add_child(root, ftrace); + + return; +} \ No newline at end of file diff --git a/tools/displaytop/src/utils_trace.c b/tools/displaytop/src/utils_trace.c new file mode 100644 index 000000000..a04f5e090 --- /dev/null +++ b/tools/displaytop/src/utils_trace.c @@ -0,0 +1,67 @@ +/* + * Copyright © 2025 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 "utils.h" +#include "display.h" + +void ensure_tracing_on(void) +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/events/i915/i915_reg_rw/enable", "w"); + if (fp == NULL || fprintf(fp, "1") < 0 || fclose(fp) != 0) + { + log_message(LOG_ERROR, "Error enabling i915_reg_rw event\n"); + } + + fp = fopen("/sys/kernel/debug/tracing/current_tracer", "w"); + if (fp == NULL || fprintf(fp, "nop") < 0 || fclose(fp) != 0) + { + log_message(LOG_ERROR, "Error setting current tracer to nop\n"); + } + + fp = fopen("/sys/kernel/debug/tracing/tracing_on", "w"); + if (fp == NULL || fprintf(fp, "1") < 0 || fclose(fp) != 0) + { + log_message(LOG_ERROR, "Error enabling tracing\n"); + } + + log_message(LOG_INFO, "Tracing enabled\n"); +} + +int clear_ftrace(void) +{ + FILE *fp; + + fp = NULL; + + fp = fopen(TRACE_PATH, "w"); + if (fp == NULL || fprintf(fp, " ") < 0 || fclose(fp) != 0) + { + log_message(LOG_ERROR, "Error clearing trace\n"); + return -1; + } + + log_message(LOG_INFO, "Trace cleared\n"); + return 0; +} \ No newline at end of file