[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