[igt-dev] [PATCH i-g-t 1/2] tools: intel display pnp loads
Anshuman Gupta
anshuman.gupta at intel.com
Thu May 27 16:50:39 UTC 2021
Adding a new tool to run various display pnp load
in igt envirnment. This will be useful to mesure power
with various display PnP loads.
Cc: Petri Latvala <petri.latvala at intel.com>
Signed-off-by: Anshuman Gupta <anshuman.gupta at intel.com>
---
tools/intel_display_pnp.c | 525 ++++++++++++++++++++++++++++++++++++++
tools/meson.build | 1 +
2 files changed, 526 insertions(+)
create mode 100644 tools/intel_display_pnp.c
diff --git a/tools/intel_display_pnp.c b/tools/intel_display_pnp.c
new file mode 100644
index 00000000..25a67a68
--- /dev/null
+++ b/tools/intel_display_pnp.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright © 2017 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.
+ *
+ * This programs runs display Power and Performance loads native to console and
+ * runs socwatch with the pnp load to get the PnP metrics.
+ *
+ * Authors:
+ * Anshuman Gupta <anshuman.gupta at intel.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+#include "igt.h"
+#include "igt_sysfs.h"
+#include "igt_psr.h"
+#include "igt_kmod.h"
+#include "intel_bufmgr.h"
+#include "limits.h"
+#include "time.h"
+
+typedef struct {
+ int drm_fd;
+ int msr_fd;
+ int debugfs_fd;
+ uint32_t devid;
+ char *pwr_dmn_info;
+ igt_display_t display;
+ struct igt_fb fb_white, fb_wr, fb_wb;
+ struct drm_mode_rect plane_update_clip_wr, plane_update_clip_wb;
+ enum psr_mode op_psr_mode;
+ drmModeModeInfo *mode;
+ igt_output_t *outputs[IGT_MAX_PIPES];
+} data_t;
+
+enum comp {
+ NO_COMPRESSION,
+ RENDER_COMPRESSION,
+ RENDER_COMPRESSION_CC,
+};
+
+typedef struct {
+ double r, g, b;
+} color_t;
+
+#define EDP_ON (1 << 0)
+#define PSR_ON (0x3 << 0)
+#define PSR2_ON (0x7 << 0)
+#define HDMI_ON (1 << 4)
+#define DP_ON (1 << 5)
+#define ANY_EXTERNAL_ON (HDMI_ON | DP_ON)
+#define PSR_MASK ((~PSR2_ON) | EDP_ON)
+#define SQUARE_SIZE 100
+#define MAX_LOADS 20
+
+int load_type = -1;
+int load_time;
+
+
+const char *load[MAX_LOADS] = {
+ "idle-display-off", "idle-display-on-psr",
+ "idle-display-on-edp-fbc", "idle-display-on-hdmi4k",
+ "idle-display-on-dp4k", "idle-display-on-edp-external",
+ "dpms-edp-on-external-off", "dpms-edp-off-external-on",
+ "psr2-idle-selective-updates-1fps", "psr2-selective-updates-30fps",
+ "kms-cube", "kms-cube-rc", "kms-cube-rc-clear-color", NULL
+ };
+
+enum loads {
+ IDLE_DISPLAY_OFF,
+ IDLE_DISPLAY_ON_PSR,
+ IDLE_DISPLAY_ON_EDP_FBC,
+ IDLE_DISPLAY_ON_HDMI_4K,
+ IDLE_DISPLAY_ON_DP_4K,
+ IDLE_DISPLAY_ON_EDP_EXTERNAL,
+ DPMS_EDP_ON_EXTERNAL_OFF,
+ DPMS_EDP_OFF_EXTERNAL_ON,
+ PSR2_IDLE_SELECTIVE_UPDATES_1FPS,
+ PSR2_SELECTIVE_UPDATES_30FPS,
+ KMS_CUBE,
+ KMS_CUBE_RC,
+ KMS_CUBE_RC_CC,
+};
+
+static int required_outputs(uint32_t flags)
+{
+ unsigned int count = 0;
+
+ while (flags) {
+ flags &= (flags - 1);
+ count++;
+ }
+ return count;
+}
+
+static void draw_rect(data_t *data, igt_fb_t *fb, int x, int y, int w, int h,
+ double r, double g, double b, double a)
+{
+ cairo_t *cr;
+
+ cr = igt_get_cairo_ctx(data->drm_fd, fb);
+ igt_paint_color_alpha(cr, x, y, w, h, r, g, b, a);
+ igt_put_cairo_ctx(cr);
+}
+
+static void set_clip(struct drm_mode_rect *clip, int x, int y, int width,
+ int height)
+{
+ clip->x1 = x;
+ clip->y1 = y;
+ clip->x2 = x + width;
+ clip->y2 = y + height;
+}
+
+static void
+create_color_fb_with_center_square(data_t *data, igt_fb_t *fb,
+ struct drm_mode_rect *clip, color_t color)
+{
+ int fb_id;
+ int x, y;
+ int width = SQUARE_SIZE;
+ int height = SQUARE_SIZE;
+
+ x = data->mode->hdisplay / 2 - SQUARE_SIZE / 2;
+ y = data->mode->vdisplay / 2 - SQUARE_SIZE / 2;
+
+ fb_id = igt_create_color_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+ DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
+ 1.0, 1.0, 1.0, fb);
+ igt_assert(fb_id);
+ draw_rect(data, fb, x, y, width, height, color.r, color.g, color.b, 1.0);
+ set_clip(clip, x, y, width, height);
+}
+
+static void
+setup_selective_update_fbs(data_t *data)
+{
+ color_t red = { 1.0, 0.0, 0.0 };
+ color_t blue = { 0.0, 0.0, 1.0 };
+
+ create_color_fb_with_center_square(data, &data->fb_wr,
+ &data->plane_update_clip_wr, red);
+ create_color_fb_with_center_square(data, &data->fb_wb,
+ &data->plane_update_clip_wb, blue);
+}
+
+static int get_compatible_outputs(data_t *data, uint32_t flags)
+{
+ igt_display_t *display = &data->display;
+ igt_output_t *output = NULL;
+ int valid_outputs = 0;
+ enum pipe pipe = PIPE_A;
+
+ for_each_connected_output(display, output) {
+ drmModeConnectorPtr c = output->config.connector;
+
+ if (!(flags ^ EDP_ON) && (c->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+ c->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+ c->connector_type == DRM_MODE_CONNECTOR_HDMIB))
+ continue;
+
+ if (!(flags ^ HDMI_ON) && (c->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+ c->connector_type == DRM_MODE_CONNECTOR_eDP))
+ continue;
+
+ if (!(flags ^ DP_ON) && (c->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ c->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+ c->connector_type == DRM_MODE_CONNECTOR_HDMIB))
+ continue;
+
+ if (flags & (EDP_ON | ANY_EXTERNAL_ON) &&
+ c->connector_type != DRM_MODE_CONNECTOR_eDP &&
+ c->connector_type != DRM_MODE_CONNECTOR_HDMIA &&
+ c->connector_type != DRM_MODE_CONNECTOR_HDMIB &&
+ c->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+ continue;
+
+ igt_assert_f(igt_pipe_connector_valid(pipe, output),
+ "Output-pipe combination invalid\n");
+ data->outputs[valid_outputs] = output;
+
+ pipe++;
+ valid_outputs++;
+ }
+
+ igt_info("valid outputs %d\n", valid_outputs);
+ igt_require_f(required_outputs(flags) > 1 ? valid_outputs > 1 : valid_outputs,
+ "Not found required outputs for the load\n");
+
+ return valid_outputs;
+}
+
+static void
+prepare_modeset_on_outputs(data_t *data, igt_output_t *output,
+ enum pipe pipe, bool dpms_off)
+{
+ drmModeConnectorPtr c = output->config.connector;
+ igt_plane_t *primary;
+ int i;
+
+ if (dpms_off) {
+ kmstest_set_connector_dpms(data->drm_fd, output->config.connector,
+ DRM_MODE_DPMS_OFF);
+ return;
+ }
+
+ data->mode = igt_output_get_mode(output);
+
+ if (c->connector_type != DRM_MODE_CONNECTOR_eDP) {
+ /* we want to run any load with 4k resolution to measure PnP with full throttle */
+ if (data->mode->hdisplay < 3840 && data->mode->vdisplay < 2160)
+ for (i = 0; i < c->count_modes; i++) {
+ if (c->modes[i].hdisplay >= 3840 &&
+ c->modes[i].vdisplay >= 2160) {
+ data->mode = &c->modes[i];
+ igt_output_override_mode(output,
+ data->mode);
+ break;
+ }
+ }
+
+ igt_require(data->mode->hdisplay >= 3840 &&
+ data->mode->vdisplay >= 2160);
+ }
+
+ igt_output_set_pipe(output, pipe);
+ primary = igt_output_get_plane_type(output,
+ DRM_PLANE_TYPE_PRIMARY);
+ igt_plane_set_fb(primary, NULL);
+ igt_create_color_fb(data->drm_fd,
+ data->mode->hdisplay, data->mode->vdisplay,
+ DRM_FORMAT_XRGB8888,
+ LOCAL_I915_FORMAT_MOD_X_TILED,
+ 1.0, 1.0, 1.0,
+ &data->fb_white);
+ igt_plane_set_fb(primary, &data->fb_white);
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+}
+
+static void idle_display_on(data_t *data, uint32_t flags)
+{
+ int valid_outputs, count = 0;
+ enum pipe pipe = PIPE_A;
+
+ valid_outputs = get_compatible_outputs(data, flags & PSR_MASK);
+
+ if ((flags & PSR2_ON) == PSR2_ON)
+ igt_require(psr_sink_support(data->drm_fd, data->debugfs_fd, PSR_MODE_2));
+ else if ((flags & PSR_ON) == PSR_ON)
+ igt_require(psr_sink_support(data->drm_fd, data->debugfs_fd, PSR_MODE_1));
+ else
+ psr_disable(data->drm_fd, data->debugfs_fd);
+
+ for (count = 0; count < valid_outputs; count++) {
+ prepare_modeset_on_outputs(data, data->outputs[count], pipe, false);
+ pipe++;
+ }
+
+ igt_info("**************idle display on, please start the power measurement**************\n");
+ sleep(load_time);
+}
+
+/*
+ * flips two fb_wr and fb_wb
+ * fb_wr all white with a red square at centre
+ * fb_wb all white with a blue square at centre.
+ * fps will determine the flip rate.
+ */
+static void selective_updates_flips(data_t *data, uint32_t flags, int fps)
+{
+ igt_output_t *output;
+ igt_plane_t *primary;
+ int delay;
+ time_t secs;
+ time_t startTime = time(NULL);
+
+ get_compatible_outputs(data, EDP_ON);
+ igt_require(psr_sink_support(data->drm_fd, data->debugfs_fd, PSR_MODE_2));
+
+ output = data->outputs[0];
+ prepare_modeset_on_outputs(data, output, PIPE_A, false);
+ setup_selective_update_fbs(data);
+
+ primary = igt_output_get_plane_type(output,
+ DRM_PLANE_TYPE_PRIMARY);
+ igt_plane_set_fb(primary, NULL);
+
+ /* Calculate delay to generate idle frame in usec*/
+ delay = ((1000 * 1000) / fps);
+ secs = load_time;
+ igt_info("**********selective update load started with fps %d,"
+ "please start the power measurement**********\n", fps);
+
+ while (time(NULL) - startTime < secs) {
+ igt_plane_set_fb(primary, &data->fb_wr);
+ igt_plane_replace_prop_blob(primary,
+ IGT_PLANE_FB_DAMAGE_CLIPS,
+ &data->plane_update_clip_wr,
+ sizeof(struct drm_mode_rect));
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+ usleep(delay);
+
+ igt_plane_set_fb(primary, &data->fb_wb);
+ igt_plane_replace_prop_blob(primary,
+ IGT_PLANE_FB_DAMAGE_CLIPS,
+ &data->plane_update_clip_wb,
+ sizeof(struct drm_mode_rect));
+ igt_display_commit2(&data->display, COMMIT_ATOMIC);
+ usleep(delay);
+ }
+}
+
+static void idle_display_on_with_dpms(data_t *data, uint32_t flags)
+{
+ drmModeConnectorPtr c;
+ int valid_outputs, count = 0;
+
+ valid_outputs = get_compatible_outputs(data, EDP_ON | ANY_EXTERNAL_ON);
+
+ for (count = 0; count < valid_outputs; count++) {
+ c = data->outputs[count]->config.connector;
+ if (flags & EDP_ON && c->connector_type == DRM_MODE_CONNECTOR_eDP)
+ prepare_modeset_on_outputs(data, data->outputs[count], PIPE_A, false);
+ else if (flags & ANY_EXTERNAL_ON && c->connector_type != DRM_MODE_CONNECTOR_eDP)
+ prepare_modeset_on_outputs(data, data->outputs[count], PIPE_A, false);
+ else
+ prepare_modeset_on_outputs(data, data->outputs[count], PIPE_A, true);
+ }
+
+ igt_info("****idle display on with another dpms_off, please start the power measurement****\n");
+ sleep(load_time);
+}
+
+static void idle_display_off(data_t *data)
+{
+ for (int i = 0; i < data->display.n_outputs; i++) {
+ kmstest_set_connector_dpms(data->drm_fd,
+ data->display.outputs[i].config.connector,
+ DRM_MODE_DPMS_OFF);
+ }
+
+ igt_info("**************idle display off, please start the power measurement**************\n");
+ sleep(load_time);
+}
+
+static void run_kms_cube(data_t *data, enum comp compression)
+{
+ FILE *fp;
+ char tmp[512];
+
+ /* close all fd so kms_cube can use DRM as master */
+ close(data->debugfs_fd);
+ close(data->msr_fd);
+ close(data->drm_fd);
+ igt_display_fini(&data->display);
+
+ if (compression == RENDER_COMPRESSION)
+ fp = popen("kmscube âmodifier 0x100000000000006", "r");
+ else if (compression == RENDER_COMPRESSION_CC)
+ fp = popen("kmscube âmodifier 0x100000000000007", "r");
+ else
+ fp = popen("kmscube", "r");
+
+ if (fp == NULL) {
+ igt_info("Failed to run powertop\n");
+ perror("popen");
+ return;
+ }
+
+ while (fgets(tmp, sizeof(tmp), fp) != NULL)
+ igt_info("%s\n", tmp);
+
+ pclose(fp);
+
+ igt_info("kms_cube, please start the power measurement\n");
+}
+
+const char *help_str =
+ " -h\t\tShow help\n"
+ " -l\t\tList supported PnP loads\n"
+ " -r\t\tProvide the load to be run\n"
+ " -t\t\tProvide time to run display PnP load in seconds.";
+
+const char *optstr = "lr:t:";
+
+static void usage(const char *name)
+{
+ igt_info("Usage: %s [options]\n", name);
+ igt_info("%s\n", help_str);
+}
+
+int main(int argc, char **argv)
+{
+ data_t data = {};
+ int fps, c, i;
+ bool kms_cube = false;
+
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ case 'l':
+ for (i = 0; i < MAX_LOADS; i++) {
+ if (!load[i])
+ break;
+ igt_info("%s\n", load[i]);
+ }
+ exit(EXIT_SUCCESS);
+ break;
+ case 'r':
+ for (i = 0; i < MAX_LOADS; i++) {
+ if (!load[i])
+ break;
+
+ if (!strcmp(load[i], optarg))
+ load_type = i;
+ }
+ break;
+ case 't':
+ load_time = atoi(optarg);
+ break;
+ default:
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
+ data.debugfs_fd = igt_debugfs_dir(data.drm_fd);
+ igt_require(data.debugfs_fd != -1);
+ kmstest_set_vt_graphics_mode();
+ data.devid = intel_get_drm_devid(data.drm_fd);
+ igt_pm_enable_sata_link_power_management();
+ igt_require(igt_setup_runtime_pm(data.drm_fd));
+ igt_require(igt_pm_dmc_loaded(data.debugfs_fd));
+ igt_display_require(&data.display, data.drm_fd);
+ /* Make sure our Kernel supports MSR and the module is loaded */
+ igt_require(igt_kmod_load("msr", NULL) == 0);
+
+ data.msr_fd = open("/dev/cpu/0/msr", O_RDONLY);
+ igt_assert_f(data.msr_fd >= 0,
+ "Can't open /dev/cpu/0/msr.\n");
+ igt_require(igt_pm_pc8_plus_residencies_enabled(data.msr_fd));
+ igt_require(load_time > 0);
+
+ switch (load_type) {
+ case IDLE_DISPLAY_OFF:
+ idle_display_off(&data);
+ break;
+ case IDLE_DISPLAY_ON_PSR:
+ idle_display_on(&data, PSR_ON);
+ break;
+ case IDLE_DISPLAY_ON_EDP_FBC:
+ idle_display_on(&data, EDP_ON);
+ break;
+ case IDLE_DISPLAY_ON_HDMI_4K:
+ idle_display_on(&data, HDMI_ON);
+ break;
+ case IDLE_DISPLAY_ON_DP_4K:
+ idle_display_on(&data, DP_ON);
+ break;
+ case IDLE_DISPLAY_ON_EDP_EXTERNAL:
+ idle_display_on(&data, PSR_ON | ANY_EXTERNAL_ON);
+ break;
+ case DPMS_EDP_ON_EXTERNAL_OFF:
+ idle_display_on_with_dpms(&data, PSR_ON);
+ break;
+ case DPMS_EDP_OFF_EXTERNAL_ON:
+ idle_display_on_with_dpms(&data, ANY_EXTERNAL_ON);
+ break;
+ case PSR2_IDLE_SELECTIVE_UPDATES_1FPS:
+ fps = 1;
+ selective_updates_flips(&data, PSR2_ON, fps);
+ break;
+ case PSR2_SELECTIVE_UPDATES_30FPS:
+ fps = 30;
+ selective_updates_flips(&data, PSR2_ON, fps);
+ break;
+ case KMS_CUBE:
+ kms_cube = true;
+ run_kms_cube(&data, NO_COMPRESSION);
+ break;
+ case KMS_CUBE_RC:
+ kms_cube = true;
+ run_kms_cube(&data, RENDER_COMPRESSION);
+ break;
+ case KMS_CUBE_RC_CC:
+ kms_cube = true;
+ run_kms_cube(&data, RENDER_COMPRESSION_CC);
+ break;
+ default:
+ igt_info("Not found any matching load\n");
+ }
+
+ if (!kms_cube) {
+ close(data.debugfs_fd);
+ close(data.msr_fd);
+ close(data.drm_fd);
+ igt_display_fini(&data.display);
+ }
+
+ igt_exit();
+}
diff --git a/tools/meson.build b/tools/meson.build
index b6b97534..1cc8727d 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -17,6 +17,7 @@ tools_progs = [
'intel_backlight',
'intel_bios_dumper',
'intel_display_crc',
+ 'intel_display_pnp',
'intel_display_poller',
'intel_forcewaked',
'intel_gpu_frequency',
--
2.26.2
More information about the igt-dev
mailing list