blob: b67902d43463b9851e47c1dd1c06a31f94ab79af [file] [log] [blame]
/*
* Copyright 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
*/
/**
* TEST: kms hdr
* Category: Display
* Description: Test HDR metadata interfaces and bpc switch
* Driver requirement: i915, xe
* Mega feature: HDR
* Test category: functionality test
*/
#include "igt.h"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include "igt_edid.h"
/**
* SUBTEST: bpc-switch
* Description: Tests switching between different display output bpc modes
* Functionality: colorspace
*
* SUBTEST: bpc-switch-dpms
* Description: Tests switching between different display output bpc modes with dpms
* Functionality: colorspace, dpms
*
* SUBTEST: bpc-switch-suspend
* Description: Tests switching between different display output bpc modes with suspend
* Functionality: colorspace, suspend
*
* SUBTEST: invalid-hdr
* Description: Test to ensure HDR is not enabled on non-HDR panel
* Functionality: colorspace, static_hdr
*
* SUBTEST: invalid-metadata-sizes
* Description: Tests invalid HDR metadata sizes
* Functionality: colorspace, static_hdr
*
* SUBTEST: static-toggle-dpms
* Description: Tests static toggle with dpms
* Functionality: colorspace, dpms, static_hdr
*
* SUBTEST: static-toggle-suspend
* Description: Tests static toggle with suspend
* Functionality: colorspace, static_hdr, suspend
*
* SUBTEST: brightness-with-hdr
* Description: Tests brightness with HDR
* Functionality: colorspace, static_hdr
*
* SUBTEST: static-%s
* Description: Tests %arg[1].
* Functionality: colorspace, static_hdr
*
* arg[1]:
*
* @swap: swapping static HDR metadata
* @toggle: entering and exiting HDR mode
*/
IGT_TEST_DESCRIPTION("Test HDR metadata interfaces and bpc switch");
/* HDR EDID parsing. */
#define CTA_EXTENSION_VERSION 0x03
#define HDR_STATIC_METADATA_BLOCK 0x06
#define USE_EXTENDED_TAG 0x07
#define BACKLIGHT_PATH "/sys/class/backlight"
/* DRM HDR definitions. Not in the UAPI header, unfortunately. */
enum hdmi_metadata_type {
HDMI_STATIC_METADATA_TYPE1 = 0,
};
enum hdmi_eotf {
HDMI_EOTF_TRADITIONAL_GAMMA_SDR,
HDMI_EOTF_TRADITIONAL_GAMMA_HDR,
HDMI_EOTF_SMPTE_ST2084,
};
/* Test flags. */
enum {
TEST_NONE = 1 << 0,
TEST_DPMS = 1 << 1,
TEST_SUSPEND = 1 << 2,
TEST_SWAP = 1 << 3,
TEST_INVALID_METADATA_SIZES = 1 << 4,
TEST_INVALID_HDR = 1 << 5,
TEST_BRIGHTNESS = 1 << 6,
};
/* BPC connector state. */
typedef struct output_bpc {
unsigned int current;
unsigned int maximum;
} output_bpc_t;
/* Common test data. */
typedef struct data {
igt_display_t display;
igt_plane_t *primary;
igt_output_t *output;
igt_pipe_t *pipe;
igt_pipe_crc_t *pipe_crc;
drmModeModeInfo *mode;
enum pipe pipe_id;
int fd;
int w;
int h;
} data_t;
/* Common test cleanup. */
static void test_fini(data_t *data)
{
igt_pipe_crc_free(data->pipe_crc);
igt_display_reset(&data->display);
}
static void test_cycle_flags(data_t *data, uint32_t test_flags)
{
if (test_flags & TEST_DPMS) {
kmstest_set_connector_dpms(data->fd,
data->output->config.connector,
DRM_MODE_DPMS_OFF);
kmstest_set_connector_dpms(data->fd,
data->output->config.connector,
DRM_MODE_DPMS_ON);
}
if (test_flags & TEST_SUSPEND)
igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
SUSPEND_TEST_NONE);
}
/* Fills the FB with a test HDR pattern. */
static void draw_hdr_pattern(igt_fb_t *fb)
{
cairo_t *cr = igt_get_cairo_ctx(fb->fd, fb);
igt_paint_color(cr, 0, 0, fb->width, fb->height, 1.0, 1.0, 1.0);
igt_paint_test_pattern(cr, fb->width, fb->height);
igt_put_cairo_ctx(cr);
}
/* Prepare test data. */
static void prepare_test(data_t *data, igt_output_t *output, enum pipe pipe)
{
igt_display_t *display = &data->display;
data->pipe_id = pipe;
data->pipe = &data->display.pipes[data->pipe_id];
igt_assert(data->pipe);
igt_display_reset(display);
data->output = output;
igt_assert(data->output);
data->mode = igt_output_get_mode(data->output);
igt_assert(data->mode);
data->primary =
igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_PRIMARY);
data->pipe_crc = igt_pipe_crc_new(data->fd, data->pipe_id,
IGT_PIPE_CRC_SOURCE_AUTO);
igt_output_set_pipe(data->output, data->pipe_id);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 10);
data->w = data->mode->hdisplay;
data->h = data->mode->vdisplay;
}
static void test_bpc_switch_on_output(data_t *data, enum pipe pipe,
igt_output_t *output,
uint32_t flags)
{
igt_display_t *display = &data->display;
igt_crc_t ref_crc, new_crc;
igt_fb_t afb;
int afb_id, ret;
/* 10-bit formats are slow, so limit the size. */
afb_id = igt_create_fb(data->fd, 512, 512,
DRM_FORMAT_XRGB2101010, DRM_FORMAT_MOD_LINEAR, &afb);
igt_assert(afb_id);
draw_hdr_pattern(&afb);
/* Plane may be required to fit fullscreen. Check it here and allow
* smaller plane size in following tests.
*/
igt_plane_set_fb(data->primary, &afb);
igt_plane_set_size(data->primary, data->w, data->h);
ret = igt_display_try_commit_atomic(display, DRM_MODE_ATOMIC_TEST_ONLY, NULL);
if (!ret) {
data->w = afb.width;
data->h = afb.height;
}
/* Start in 8bpc. */
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 8);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 8);
/*
* amdgpu requires a primary plane when the CRTC is enabled.
* However, some older Intel hardware (hsw) have scaling
* requirements that are not met by the plane, so remove it
* for non-AMD devices.
*/
if (!is_amdgpu_device(data->fd))
igt_plane_set_fb(data->primary, NULL);
/* Switch to 10bpc. */
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 10);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 10);
/* Verify that the CRC are equal after DPMS or suspend. */
igt_pipe_crc_collect_crc(data->pipe_crc, &ref_crc);
test_cycle_flags(data, flags);
igt_pipe_crc_collect_crc(data->pipe_crc, &new_crc);
/* Drop back to 8bpc. */
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 8);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 8);
/* CRC capture is clamped to 8bpc, so capture should match. */
igt_assert_crc_equal(&ref_crc, &new_crc);
test_fini(data);
igt_remove_fb(data->fd, &afb);
}
/* Returns true if an output supports max bpc property. */
static bool has_max_bpc(igt_output_t *output)
{
return igt_output_has_prop(output, IGT_CONNECTOR_MAX_BPC) &&
igt_output_get_prop(output, IGT_CONNECTOR_MAX_BPC);
}
static void test_bpc_switch(data_t *data, uint32_t flags)
{
igt_display_t *display = &data->display;
igt_output_t *output;
igt_display_reset(display);
for_each_connected_output(display, output) {
enum pipe pipe;
if (!has_max_bpc(output)) {
igt_info("%s: Doesn't support IGT_CONNECTOR_MAX_BPC.\n",
igt_output_name(output));
continue;
}
if (igt_get_output_max_bpc(data->fd, output->name) < 10) {
igt_info("%s: Doesn't support 10 bpc.\n", igt_output_name(output));
continue;
}
for_each_pipe(display, pipe) {
igt_output_set_pipe(output, pipe);
if (!intel_pipe_output_combo_valid(display)) {
igt_output_set_pipe(output, PIPE_NONE);
continue;
}
prepare_test(data, output, pipe);
if (is_intel_device(data->fd) &&
!igt_max_bpc_constraint(display, pipe, output, 10)) {
igt_info("%s: No suitable mode found to use 10 bpc.\n",
igt_output_name(output));
test_fini(data);
break;
}
data->mode = igt_output_get_mode(output);
data->w = data->mode->hdisplay;
data->h = data->mode->vdisplay;
igt_dynamic_f("pipe-%s-%s",
kmstest_pipe_name(pipe), output->name)
test_bpc_switch_on_output(data, pipe, output, flags);
/* One pipe is enough */
break;
}
}
}
static bool cta_block(const char *edid_ext)
{
/*
* Byte 1: 0x07 indicates Extended Tag
* Byte 2: 0x06 indicates HDMI Static Metadata Block
* Byte 3: bits 0 to 5 identify EOTF functions supported by sink
* where ET_0: Traditional Gamma - SDR Luminance Range
* ET_1: Traditional Gamma - HDR Luminance Range
* ET_2: SMPTE ST 2084
* ET_3: Hybrid Log-Gamma (HLG)
* ET_4 to ET_5: Reserved for future use
*/
if ((((edid_ext[0] & 0xe0) >> 5 == USE_EXTENDED_TAG) &&
(edid_ext[1] == HDR_STATIC_METADATA_BLOCK)) &&
((edid_ext[2] & HDMI_EOTF_TRADITIONAL_GAMMA_HDR) ||
(edid_ext[2] & HDMI_EOTF_SMPTE_ST2084)))
return true;
return false;
}
/* Returns true if panel supports HDR. */
static bool is_panel_hdr(data_t *data, igt_output_t *output)
{
bool ok;
int i, j, offset;
uint64_t edid_blob_id;
drmModePropertyBlobRes *edid_blob;
const struct edid_ext *edid_ext;
const struct edid *edid;
const struct edid_cea *edid_cea;
const char *cea_data;
bool ret = false;
ok = kmstest_get_property(data->fd, output->id,
DRM_MODE_OBJECT_CONNECTOR, "EDID",
NULL, &edid_blob_id, NULL);
if (!ok || !edid_blob_id)
return ret;
edid_blob = drmModeGetPropertyBlob(data->fd, edid_blob_id);
igt_assert(edid_blob);
edid = (const struct edid *) edid_blob->data;
igt_assert(edid);
drmModeFreePropertyBlob(edid_blob);
for (i = 0; i < edid->extensions_len; i++) {
edid_ext = &edid->extensions[i];
edid_cea = &edid_ext->data.cea;
/* HDR not defined in CTA Extension Version < 3. */
if ((edid_ext->tag != EDID_EXT_CEA) ||
(edid_cea->revision != CTA_EXTENSION_VERSION))
continue;
else {
offset = edid_cea->dtd_start;
cea_data = edid_cea->data;
for (j = 0; j < offset; j += (cea_data[j] & 0x1f) + 1) {
ret = cta_block(cea_data + j);
if (ret)
break;
}
}
}
return ret;
}
/* Sets the HDR output metadata prop. */
static void set_hdr_output_metadata(data_t *data,
struct hdr_output_metadata const *meta)
{
igt_output_replace_prop_blob(data->output,
IGT_CONNECTOR_HDR_OUTPUT_METADATA, meta,
meta ? sizeof(*meta) : 0);
}
/* Sets the HDR output metadata prop with invalid size. */
static int set_invalid_hdr_output_metadata(data_t *data,
struct hdr_output_metadata const *meta,
size_t length)
{
igt_output_replace_prop_blob(data->output,
IGT_CONNECTOR_HDR_OUTPUT_METADATA, meta,
meta ? length : 0);
return igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
}
/* Converts a double to 861-G spec FP format. */
static uint16_t calc_hdr_float(double val)
{
return (uint16_t)(val * 50000.0);
}
/* Fills some test values for ST2048 HDR output metadata.
*
* Note: there isn't really a standard for what the metadata is supposed
* to do on the display side of things. The display is free to ignore it
* and clip the output, use it to help tonemap to the content range,
* or do anything they want, really.
*/
static void fill_hdr_output_metadata_st2048(struct hdr_output_metadata *meta)
{
memset(meta, 0, sizeof(*meta));
meta->metadata_type = HDMI_STATIC_METADATA_TYPE1;
meta->hdmi_metadata_type1.eotf = HDMI_EOTF_SMPTE_ST2084;
/* Rec. 2020 */
meta->hdmi_metadata_type1.display_primaries[0].x =
calc_hdr_float(0.708); /* Red */
meta->hdmi_metadata_type1.display_primaries[0].y =
calc_hdr_float(0.292);
meta->hdmi_metadata_type1.display_primaries[1].x =
calc_hdr_float(0.170); /* Green */
meta->hdmi_metadata_type1.display_primaries[1].y =
calc_hdr_float(0.797);
meta->hdmi_metadata_type1.display_primaries[2].x =
calc_hdr_float(0.131); /* Blue */
meta->hdmi_metadata_type1.display_primaries[2].y =
calc_hdr_float(0.046);
meta->hdmi_metadata_type1.white_point.x = calc_hdr_float(0.3127);
meta->hdmi_metadata_type1.white_point.y = calc_hdr_float(0.3290);
meta->hdmi_metadata_type1.max_display_mastering_luminance =
1000; /* 1000 nits */
meta->hdmi_metadata_type1.min_display_mastering_luminance =
500; /* 0.05 nits */
meta->hdmi_metadata_type1.max_fall = 1000; /* 1000 nits */
meta->hdmi_metadata_type1.max_cll = 500; /* 500 nits */
}
static void adjust_brightness(data_t *data, uint32_t flags)
{
igt_backlight_context_t context;
int r_bright, w_bright;
snprintf(context.path, PATH_MAX, "intel_backlight");
snprintf(context.backlight_dir_path, PATH_MAX, "%s", BACKLIGHT_PATH);
igt_assert(igt_backlight_read(&context.max, "max_brightness", &context) > -1);
igt_assert(context.max);
igt_assert(igt_backlight_read(&context.old, "brightness", &context) > -1);
for (w_bright = 0; w_bright <= context.max ; w_bright += 50) {
igt_assert_eq(igt_backlight_write(w_bright, "brightness", &context), 0);
igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_eq(igt_backlight_read(&r_bright, "brightness", &context), 0);
igt_assert_eq(w_bright, r_bright);
}
igt_assert_eq(igt_backlight_write(context.old, "brightness", &context), 0);
}
static void test_static_toggle(data_t *data, enum pipe pipe,
igt_output_t *output,
uint32_t flags)
{
igt_display_t *display = &data->display;
struct hdr_output_metadata hdr;
igt_crc_t ref_crc, new_crc;
igt_fb_t afb;
int afb_id;
/* 10-bit formats are slow, so limit the size. */
afb_id = igt_create_fb(data->fd, 512, 512,
DRM_FORMAT_XRGB2101010, DRM_FORMAT_MOD_LINEAR, &afb);
igt_assert(afb_id);
draw_hdr_pattern(&afb);
fill_hdr_output_metadata_st2048(&hdr);
/* Start with no metadata. */
igt_plane_set_fb(data->primary, &afb);
igt_plane_set_size(data->primary, data->w, data->h);
set_hdr_output_metadata(data, NULL);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 8);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 8);
/* Apply HDR metadata and 10bpc. We expect a modeset for entering. */
set_hdr_output_metadata(data, &hdr);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 10);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
if (flags & TEST_INVALID_HDR) {
igt_assert_eq(system("dmesg|tail -n 1000|grep -E \"Unknown EOTF [0-9]+\""), 0);
goto cleanup;
}
if (flags & TEST_BRIGHTNESS) {
igt_require_f(is_intel_device(data->fd), "Only supported on Intel devices\n");
adjust_brightness(data, flags);
}
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 10);
/* Verify that the CRC are equal after DPMS or suspend. */
igt_pipe_crc_collect_crc(data->pipe_crc, &ref_crc);
test_cycle_flags(data, flags);
igt_pipe_crc_collect_crc(data->pipe_crc, &new_crc);
/* Disable HDR metadata and drop back to 8bpc. We expect a modeset for exiting. */
set_hdr_output_metadata(data, NULL);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 8);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 8);
igt_assert_crc_equal(&ref_crc, &new_crc);
cleanup:
test_fini(data);
igt_remove_fb(data->fd, &afb);
}
/* Fills some test values for HDR metadata targeting SDR. */
static void fill_hdr_output_metadata_sdr(struct hdr_output_metadata *meta)
{
memset(meta, 0, sizeof(*meta));
meta->metadata_type = HDMI_STATIC_METADATA_TYPE1;
meta->hdmi_metadata_type1.eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
/* Rec. 709 */
meta->hdmi_metadata_type1.display_primaries[0].x =
calc_hdr_float(0.640); /* Red */
meta->hdmi_metadata_type1.display_primaries[0].y =
calc_hdr_float(0.330);
meta->hdmi_metadata_type1.display_primaries[1].x =
calc_hdr_float(0.300); /* Green */
meta->hdmi_metadata_type1.display_primaries[1].y =
calc_hdr_float(0.600);
meta->hdmi_metadata_type1.display_primaries[2].x =
calc_hdr_float(0.150); /* Blue */
meta->hdmi_metadata_type1.display_primaries[2].y =
calc_hdr_float(0.006);
meta->hdmi_metadata_type1.white_point.x = calc_hdr_float(0.3127);
meta->hdmi_metadata_type1.white_point.y = calc_hdr_float(0.3290);
meta->hdmi_metadata_type1.max_display_mastering_luminance = 0;
meta->hdmi_metadata_type1.min_display_mastering_luminance = 0;
meta->hdmi_metadata_type1.max_fall = 0;
meta->hdmi_metadata_type1.max_cll = 0;
}
static void test_static_swap(data_t *data, enum pipe pipe, igt_output_t *output)
{
igt_display_t *display = &data->display;
igt_crc_t ref_crc, new_crc;
igt_fb_t afb;
int afb_id;
struct hdr_output_metadata hdr;
/* 10-bit formats are slow, so limit the size. */
afb_id = igt_create_fb(data->fd, 512, 512,
DRM_FORMAT_XRGB2101010, DRM_FORMAT_MOD_LINEAR, &afb);
igt_assert(afb_id);
draw_hdr_pattern(&afb);
/* Start in SDR. */
igt_plane_set_fb(data->primary, &afb);
igt_plane_set_size(data->primary, data->w, data->h);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 8);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 8);
/* Enter HDR, a modeset is allowed here. */
fill_hdr_output_metadata_st2048(&hdr);
set_hdr_output_metadata(data, &hdr);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 10);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 10);
igt_pipe_crc_collect_crc(data->pipe_crc, &ref_crc);
/* Change the mastering information, no modeset allowed
* for amd driver, whereas a modeset is required for intel
* driver. */
hdr.hdmi_metadata_type1.max_display_mastering_luminance = 200;
hdr.hdmi_metadata_type1.max_fall = 200;
hdr.hdmi_metadata_type1.max_cll = 100;
set_hdr_output_metadata(data, &hdr);
if (is_amdgpu_device(data->fd))
igt_display_commit_atomic(display, 0, NULL);
else
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
/* Enter SDR via metadata, no modeset allowed for
* amd driver, whereas a modeset is required for
* intel driver. */
fill_hdr_output_metadata_sdr(&hdr);
set_hdr_output_metadata(data, &hdr);
if (is_amdgpu_device(data->fd))
igt_display_commit_atomic(display, 0, NULL);
else
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_pipe_crc_collect_crc(data->pipe_crc, &new_crc);
/* Exit SDR and enter 8bpc, cleanup. */
set_hdr_output_metadata(data, NULL);
igt_output_set_prop_value(data->output, IGT_CONNECTOR_MAX_BPC, 8);
igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
igt_assert_output_bpc_equal(data->fd, pipe, output->name, 8);
/* Verify that the CRC didn't change while cycling metadata. */
igt_assert_crc_equal(&ref_crc, &new_crc);
test_fini(data);
igt_remove_fb(data->fd, &afb);
}
static void test_invalid_metadata_sizes(data_t *data, igt_output_t *output)
{
struct hdr_output_metadata hdr;
size_t metadata_size = sizeof(hdr);
fill_hdr_output_metadata_st2048(&hdr);
igt_assert_eq(set_invalid_hdr_output_metadata(data, &hdr, 1), -EINVAL);
igt_assert_eq(set_invalid_hdr_output_metadata(data, &hdr, metadata_size + 1), -EINVAL);
igt_assert_eq(set_invalid_hdr_output_metadata(data, &hdr, metadata_size - 1), -EINVAL);
igt_assert_eq(set_invalid_hdr_output_metadata(data, &hdr, metadata_size * 2), -EINVAL);
test_fini(data);
}
/* Returns true if an output supports HDR metadata property. */
static bool has_hdr(igt_output_t *output)
{
return igt_output_has_prop(output, IGT_CONNECTOR_HDR_OUTPUT_METADATA);
}
static void test_hdr(data_t *data, uint32_t flags)
{
igt_display_t *display = &data->display;
igt_output_t *output;
igt_display_reset(display);
for_each_connected_output(display, output) {
enum pipe pipe;
/* To test HDR, 10 bpc is required, so we need to
* set MAX_BPC property to 10bpc prior to setting
* HDR metadata property. Therefore, checking.
*/
if (!has_max_bpc(output) || !has_hdr(output)) {
igt_info("%s: Doesn't support IGT_CONNECTOR_MAX_BPC or IGT_CONNECTOR_HDR_OUTPUT_METADATA.\n",
igt_output_name(output));
continue;
}
/* For negative test, panel should be non-hdr. */
if ((flags & TEST_INVALID_HDR) && is_panel_hdr(data, output)) {
igt_info("%s: Can't run negative test on HDR panel.\n",
igt_output_name(output));
continue;
}
if ((flags & ~TEST_INVALID_HDR) && !is_panel_hdr(data, output)) {
igt_info("%s: Can't run HDR tests on non-HDR panel.\n",
igt_output_name(output));
continue;
}
if (igt_get_output_max_bpc(data->fd, output->name) < 10) {
igt_info("%s: Doesn't support 10 bpc.\n", igt_output_name(output));
continue;
}
for_each_pipe(display, pipe) {
igt_output_set_pipe(output, pipe);
if (!intel_pipe_output_combo_valid(display)) {
igt_output_set_pipe(output, PIPE_NONE);
continue;
}
prepare_test(data, output, pipe);
if (is_intel_device(data->fd) &&
!igt_max_bpc_constraint(display, pipe, output, 10)) {
igt_info("%s: No suitable mode found to use 10 bpc.\n",
igt_output_name(output));
test_fini(data);
break;
}
data->mode = igt_output_get_mode(output);
data->w = data->mode->hdisplay;
data->h = data->mode->vdisplay;
igt_dynamic_f("pipe-%s-%s",
kmstest_pipe_name(pipe), output->name) {
if (flags & (TEST_NONE | TEST_DPMS | TEST_SUSPEND |
TEST_INVALID_HDR | TEST_BRIGHTNESS))
test_static_toggle(data, pipe, output, flags);
if (flags & TEST_SWAP)
test_static_swap(data, pipe, output);
if (flags & TEST_INVALID_METADATA_SIZES)
test_invalid_metadata_sizes(data, output);
}
/* One pipe is enough */
break;
}
}
}
igt_main
{
data_t data = {};
igt_fixture {
data.fd = drm_open_driver_master(DRIVER_ANY);
kmstest_set_vt_graphics_mode();
igt_display_require(&data.display, data.fd);
igt_require(data.display.is_atomic);
igt_display_require_output(&data.display);
}
igt_describe("Tests switching between different display output bpc modes");
igt_subtest_with_dynamic("bpc-switch")
test_bpc_switch(&data, TEST_NONE);
igt_describe("Tests bpc switch with dpms");
igt_subtest_with_dynamic("bpc-switch-dpms")
test_bpc_switch(&data, TEST_DPMS);
igt_describe("Tests bpc switch with suspend");
igt_subtest_with_dynamic("bpc-switch-suspend")
test_bpc_switch(&data, TEST_SUSPEND);
igt_describe("Tests entering and exiting HDR mode");
igt_subtest_with_dynamic("static-toggle")
test_hdr(&data, TEST_NONE);
igt_describe("Tests static toggle with dpms");
igt_subtest_with_dynamic("static-toggle-dpms")
test_hdr(&data, TEST_DPMS);
igt_describe("Tests static toggle with suspend");
igt_subtest_with_dynamic("static-toggle-suspend")
test_hdr(&data, TEST_SUSPEND);
igt_describe("Tests brightness while in HDR mode");
igt_subtest_with_dynamic("brightness-with-hdr")
test_hdr(&data, TEST_BRIGHTNESS);
igt_describe("Tests swapping static HDR metadata");
igt_subtest_with_dynamic("static-swap")
test_hdr(&data, TEST_SWAP);
igt_describe("Tests invalid HDR metadata sizes");
igt_subtest_with_dynamic("invalid-metadata-sizes")
test_hdr(&data, TEST_INVALID_METADATA_SIZES);
igt_describe("Test to ensure HDR is not enabled on non-HDR panel");
igt_subtest_with_dynamic("invalid-hdr")
test_hdr(&data, TEST_INVALID_HDR);
igt_fixture {
igt_display_fini(&data.display);
drm_close_driver(data.fd);
}
}