kms_content_protection.c 9.29 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * Copyright © 2018 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 <poll.h>
#include <fcntl.h>
#include "igt.h"
#include "igt_sysfs.h"
29
#include "igt_kms.h"
30 31 32 33 34 35

IGT_TEST_DESCRIPTION("Test content protection (HDCP)");

struct data {
	int drm_fd;
	igt_display_t display;
36
	struct igt_fb red, green;
37 38
} data;

39 40 41 42 43 44 45 46 47 48
#define CP_UNDESIRED				0
#define CP_DESIRED				1
#define CP_ENABLED				2

#define LIC_PERIOD_MSEC				(4 * 1000)
/* Kernel retry count=3, Max time per authentication allowed = 6Sec */
#define KERNEL_AUTH_TIME_ALLOWED_MSEC		(3 *  6 * 1000)
#define KERNEL_DISABLE_TIME_ALLOWED_MSEC	(1 * 1000)
#define FLIP_EVENT_POLLING_TIMEOUT_MSEC		1000

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

static void flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
			 unsigned int tv_usec, void *_data)
{
	igt_debug("Flip event received.\n");
}

static int wait_flip_event(void)
{
	int rc;
	drmEventContext evctx;
	struct pollfd pfd;

	evctx.version = 2;
	evctx.vblank_handler = NULL;
	evctx.page_flip_handler = flip_handler;

	pfd.fd = data.drm_fd;
	pfd.events = POLLIN;
	pfd.revents = 0;

70
	rc = poll(&pfd, 1, FLIP_EVENT_POLLING_TIMEOUT_MSEC);
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
	switch (rc) {
	case 0:
		igt_info("Poll timeout. 1Sec.\n");
		rc = -ETIMEDOUT;
		break;
	case 1:
		rc = drmHandleEvent(data.drm_fd, &evctx);
		igt_assert_eq(rc, 0);
		rc = 0;
		break;
	default:
		igt_info("Unexpected poll rc %d\n", rc);
		rc = -1;
		break;
	}

	return rc;
}

static bool
wait_for_prop_value(igt_output_t *output, uint64_t expected,
		    uint32_t timeout_mSec)
{
	uint64_t val;
	int i;

	for (i = 0; i < timeout_mSec; i++) {
		val = igt_output_get_prop(output,
					  IGT_CONNECTOR_CONTENT_PROTECTION);
		if (val == expected)
			return true;
		usleep(1000);
	}
104 105
	igt_info("prop_value mismatch %" PRId64 " != %" PRId64 "\n",
		 val, expected);
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

	return false;
}

static void
commit_display_and_wait_for_flip(enum igt_commit_style s)
{
	int ret;
	uint32_t flag;

	if (s == COMMIT_ATOMIC) {
		flag = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET;
		igt_display_commit_atomic(&data.display, flag, NULL);

		ret = wait_flip_event();
		igt_assert_f(!ret, "wait_flip_event failed. %d\n", ret);
	} else {
		igt_display_commit2(&data.display, s);

		/* Wait for 50mSec */
		usleep(50 * 1000);
	}
}

130 131
static void modeset_with_fb(const enum pipe pipe, igt_output_t *output,
			    enum igt_commit_style s)
132 133 134 135 136 137 138 139 140 141 142 143 144
{
	igt_display_t *display = &data.display;
	drmModeModeInfo mode;
	igt_plane_t *primary;

	igt_assert(kmstest_get_connector_default_mode(
			display->drm_fd, output->config.connector, &mode));

	igt_output_override_mode(output, &mode);
	igt_output_set_pipe(output, pipe);

	igt_create_color_fb(display->drm_fd, mode.hdisplay, mode.vdisplay,
			    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
145
			    1.f, 0.f, 0.f, &data.red);
146 147
	igt_create_color_fb(display->drm_fd, mode.hdisplay, mode.vdisplay,
			    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
148
			    0.f, 1.f, 0.f, &data.green);
149 150 151

	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
	igt_display_commit2(display, s);
152
	igt_plane_set_fb(primary, &data.red);
153 154 155

	/* Wait for Flip completion before starting the HDCP authentication */
	commit_display_and_wait_for_flip(s);
156
}
157

158 159 160 161 162 163 164
static bool test_cp_enable(igt_output_t *output, enum igt_commit_style s)
{
	igt_display_t *display = &data.display;
	igt_plane_t *primary;
	bool ret;

	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
165

166
	igt_output_set_prop_value(output,
167
				  IGT_CONNECTOR_CONTENT_PROTECTION, CP_DESIRED);
168
	igt_display_commit2(display, s);
169

170 171
	ret = wait_for_prop_value(output, CP_ENABLED,
				  KERNEL_AUTH_TIME_ALLOWED_MSEC);
172 173
	if (ret) {
		igt_plane_set_fb(primary, &data.green);
174
		igt_display_commit2(display, s);
175
	}
176

177 178
	return ret;
}
179

180 181 182 183 184 185 186
static void test_cp_disable(igt_output_t *output, enum igt_commit_style s)
{
	igt_display_t *display = &data.display;
	igt_plane_t *primary;
	bool ret;

	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
187

188 189 190 191
	/*
	 * Even on HDCP enable failed scenario, IGT should exit leaving the
	 * "content protection" at "UNDESIRED".
	 */
192 193
	igt_output_set_prop_value(output, IGT_CONNECTOR_CONTENT_PROTECTION,
				  CP_UNDESIRED);
194
	igt_plane_set_fb(primary, &data.red);
195 196 197
	igt_display_commit2(display, s);

	/* Wait for HDCP to be disabled, before crtc off */
198 199
	ret = wait_for_prop_value(output, CP_UNDESIRED,
				  KERNEL_DISABLE_TIME_ALLOWED_MSEC);
200 201
	igt_assert_f(ret, "Content Protection not cleared\n");
}
202

203 204 205 206 207 208 209 210 211 212 213 214 215
static void test_cp_enable_with_retry(igt_output_t *output,
				      enum igt_commit_style s, int retry)
{
	bool ret;

	do {
		test_cp_disable(output, s);
		ret = test_cp_enable(output, s);

		if (!ret && --retry)
			igt_debug("Retry (%d/2) ...\n", 3 - retry);
	} while (retry && !ret);
	igt_assert_f(ret, "Content Protection not enabled\n");
216 217 218 219 220 221 222 223 224 225 226 227 228
}

static bool igt_pipe_is_free(igt_display_t *display, enum pipe pipe)
{
	int i;

	for (i = 0; i < display->n_outputs; i++)
		if (display->outputs[i].pending_pipe == pipe)
			return false;

	return true;
}

229 230 231 232 233
static void test_cp_lic(igt_output_t *output)
{
	bool ret;

	/* Wait for 4Secs (min 2 cycles of Link Integrity Check) */
234
	ret = wait_for_prop_value(output, CP_DESIRED, LIC_PERIOD_MSEC);
235 236
	igt_assert_f(!ret, "Content Protection LIC Failed\n");
}
237 238

static void test_content_protection_on_output(igt_output_t *output,
239 240
					      enum igt_commit_style s,
					      bool dpms_test)
241 242
{
	igt_display_t *display = &data.display;
243
	igt_plane_t *primary;
244
	enum pipe pipe;
245
	bool ret;
246 247 248 249 250 251 252 253 254 255 256 257 258 259

	for_each_pipe(display, pipe) {
		if (!igt_pipe_connector_valid(pipe, output))
			continue;

		/*
		 * If previous subtest of connector failed, pipe
		 * attached to that connector is not released.
		 * Because of that we have to choose the non
		 * attached pipe for this subtest.
		 */
		if (!igt_pipe_is_free(display, pipe))
			continue;

260 261
		modeset_with_fb(pipe, output, s);
		test_cp_enable_with_retry(output, s, 3);
262
		test_cp_lic(output);
263

264 265 266 267 268 269 270 271 272
		if (dpms_test) {
			igt_pipe_set_prop_value(display, pipe,
						IGT_CRTC_ACTIVE, 0);
			igt_display_commit2(display, s);

			igt_pipe_set_prop_value(display, pipe,
						IGT_CRTC_ACTIVE, 1);
			igt_display_commit2(display, s);

273 274
			ret = wait_for_prop_value(output, CP_ENABLED,
						  KERNEL_AUTH_TIME_ALLOWED_MSEC);
275 276 277 278 279
			if (!ret)
				test_cp_enable_with_retry(output, s, 2);
		}

		test_cp_disable(output, s);
280 281 282 283
		primary = igt_output_get_plane_type(output,
						    DRM_PLANE_TYPE_PRIMARY);
		igt_plane_set_fb(primary, NULL);
		igt_output_set_pipe(output, PIPE_NONE);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

		/*
		 * Testing a output with a pipe is enough for HDCP
		 * testing. No ROI in testing the connector with other
		 * pipes. So Break the loop on pipe.
		 */
		break;
	}
}

static void __debugfs_read(int fd, const char *param, char *buf, int len)
{
	len = igt_debugfs_simple_read(fd, param, buf, len);
	if (len < 0)
		igt_assert_eq(len, -ENODEV);
}

#define debugfs_read(fd, p, arr) __debugfs_read(fd, p, arr, sizeof(arr))

#define MAX_SINK_HDCP_CAP_BUF_LEN	500
static bool sink_hdcp_capable(igt_output_t *output)
{
	char buf[MAX_SINK_HDCP_CAP_BUF_LEN];
	int fd;

	fd = igt_debugfs_connector_dir(data.drm_fd, output->name, O_RDONLY);
	if (fd < 0)
		return false;

	debugfs_read(fd, "i915_hdcp_sink_capability", buf);
	close(fd);

	igt_debug("Sink capability: %s\n", buf);

	return strstr(buf, "HDCP1.4");
}


static void
323
test_content_protection(enum igt_commit_style s, bool dpms_test)
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
{
	igt_display_t *display = &data.display;
	igt_output_t *output;
	int valid_tests = 0;

	for_each_connected_output(display, output) {
		if (!output->props[IGT_CONNECTOR_CONTENT_PROTECTION])
			continue;

		igt_info("CP Test execution on %s\n", output->name);
		if (!sink_hdcp_capable(output)) {
			igt_info("\tSkip %s (Sink has no HDCP support)\n",
				 output->name);
			continue;
		}

340
		test_content_protection_on_output(output, s, dpms_test);
341 342 343 344 345 346 347 348 349 350 351 352 353
		valid_tests++;
	}

	igt_require_f(valid_tests, "No connector found with HDCP capability\n");
}

igt_main
{
	igt_fixture {
		igt_skip_on_simulation();

		data.drm_fd = drm_open_driver(DRIVER_ANY);

354
		igt_display_require(&data.display, data.drm_fd);
355 356 357
	}

	igt_subtest("legacy")
358
		test_content_protection(COMMIT_LEGACY, false);
359 360 361

	igt_subtest("atomic") {
		igt_require(data.display.is_atomic);
362 363 364 365 366 367
		test_content_protection(COMMIT_ATOMIC, false);
	}

	igt_subtest("atomic-dpms") {
		igt_require(data.display.is_atomic);
		test_content_protection(COMMIT_ATOMIC, true);
368 369 370 371 372
	}

	igt_fixture
		igt_display_fini(&data.display);
}