#include <drm/drm_atomic.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_connector.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include "../drm_crtc_internal.h"
#include <kunit/test.h>
#include "drm_kunit_edid.h"
struct drm_atomic_helper_connector_hdmi_priv {
struct drm_device drm;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_encoder encoder;
struct drm_connector connector;
const void *current_edid;
size_t current_edid_len;
int hdmi_update_failures;
};
#define connector_to_priv(c) \
container_of_const(c, struct drm_atomic_helper_connector_hdmi_priv, connector)
#define encoder_to_priv(e) \
container_of_const(e, struct drm_atomic_helper_connector_hdmi_priv, encoder)
static struct drm_display_mode *find_preferred_mode(struct drm_connector *connector)
{
struct drm_device *drm = connector->dev;
struct drm_display_mode *mode, *preferred;
mutex_lock(&drm->mode_config.mutex);
preferred = list_first_entry_or_null(&connector->modes, struct drm_display_mode, head);
list_for_each_entry(mode, &connector->modes, head)
if (mode->type & DRM_MODE_TYPE_PREFERRED)
preferred = mode;
mutex_unlock(&drm->mode_config.mutex);
return preferred;
}
static int set_connector_edid(struct kunit *test, struct drm_connector *connector,
const void *edid, size_t edid_len)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
connector_to_priv(connector);
struct drm_device *drm = connector->dev;
int ret;
priv->current_edid = edid;
priv->current_edid_len = edid_len;
mutex_lock(&drm->mode_config.mutex);
ret = connector->funcs->fill_modes(connector, 4096, 4096);
mutex_unlock(&drm->mode_config.mutex);
return ret;
}
static int accept_infoframe_clear_infoframe(struct drm_connector *connector)
{
return 0;
}
static int accept_infoframe_write_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return 0;
}
static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static enum drm_mode_status
reject_connector_tmds_char_rate_valid(const struct drm_connector *connector,
const struct drm_display_mode *mode,
unsigned long long tmds_rate)
{
return MODE_BAD;
}
static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = {
.tmds_char_rate_valid = reject_connector_tmds_char_rate_valid,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static enum drm_mode_status
reject_100mhz_connector_tmds_char_rate_valid(const struct drm_connector *connector,
const struct drm_display_mode *mode,
unsigned long long tmds_rate)
{
return (tmds_rate > 100ULL * 1000 * 1000) ? MODE_BAD : MODE_OK;
}
static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs = {
.tmds_char_rate_valid = reject_100mhz_connector_tmds_char_rate_valid,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static int dummy_connector_get_modes(struct drm_connector *connector)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
connector_to_priv(connector);
const struct drm_edid *edid;
unsigned int num_modes;
edid = drm_edid_alloc(priv->current_edid, priv->current_edid_len);
if (!edid)
return -EINVAL;
drm_edid_connector_update(connector, edid);
num_modes = drm_edid_connector_add_modes(connector);
drm_edid_free(edid);
return num_modes;
}
static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = {
.atomic_check = drm_atomic_helper_connector_hdmi_check,
.get_modes = dummy_connector_get_modes,
.mode_valid = drm_hdmi_connector_mode_valid,
};
static void dummy_hdmi_connector_reset(struct drm_connector *connector)
{
drm_atomic_helper_connector_reset(connector);
__drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
}
static const struct drm_connector_funcs dummy_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = dummy_hdmi_connector_reset,
};
static void test_encoder_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
encoder_to_priv(encoder);
int ret;
ret = drm_atomic_helper_connector_hdmi_update_infoframes(&priv->connector, state);
if (ret)
priv->hdmi_update_failures++;
}
static const struct drm_encoder_helper_funcs test_encoder_helper_funcs = {
.atomic_enable = test_encoder_atomic_enable,
};
static
struct drm_atomic_helper_connector_hdmi_priv *
__connector_hdmi_init(struct kunit *test,
unsigned int formats,
unsigned int max_bpc,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
const void *edid_data, size_t edid_len)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector *conn;
struct drm_encoder *enc;
struct drm_device *drm;
struct device *dev;
int ret;
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
priv = drm_kunit_helper_alloc_drm_device(test, dev,
struct drm_atomic_helper_connector_hdmi_priv, drm,
DRIVER_MODESET | DRIVER_ATOMIC);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
test->priv = priv;
drm = &priv->drm;
priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
NULL,
NULL,
NULL, 0,
NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->plane);
priv->crtc = drm_kunit_helper_create_crtc(test, drm,
priv->plane, NULL,
NULL,
NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->crtc);
enc = &priv->encoder;
ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
enc->possible_crtcs = drm_crtc_mask(priv->crtc);
conn = &priv->connector;
conn->ycbcr_420_allowed = !!(formats & BIT(HDMI_COLORSPACE_YUV420));
ret = drmm_connector_hdmi_init(drm, conn,
"Vendor", "Product",
&dummy_connector_funcs,
hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
NULL,
formats,
max_bpc);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_connector_helper_add(conn, &dummy_connector_helper_funcs);
drm_connector_attach_encoder(conn, enc);
drm_mode_config_reset(drm);
if (edid_data && edid_len) {
ret = set_connector_edid(test, &priv->connector, edid_data, edid_len);
KUNIT_ASSERT_GT(test, ret, 0);
}
return priv;
}
#define drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, formats, max_bpc, funcs, edid) \
__connector_hdmi_init(test, formats, max_bpc, funcs, edid, ARRAY_SIZE(edid))
static
struct drm_atomic_helper_connector_hdmi_priv *
drm_kunit_helper_connector_hdmi_init(struct kunit *test,
unsigned int formats,
unsigned int max_bpc)
{
return drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
formats,
max_bpc,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
}
static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *old_conn_state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
new_conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL;
KUNIT_ASSERT_NE(test,
old_conn_state->hdmi.broadcast_rgb,
new_conn_state->hdmi.broadcast_rgb);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
new_conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
KUNIT_EXPECT_EQ(test, new_conn_state->hdmi.broadcast_rgb, DRM_HDMI_BROADCAST_RGB_FULL);
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *old_conn_state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
new_conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
new_conn_state->hdmi.broadcast_rgb = old_conn_state->hdmi.broadcast_rgb;
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
new_conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
KUNIT_EXPECT_EQ(test,
old_conn_state->hdmi.broadcast_rgb,
new_conn_state->hdmi.broadcast_rgb);
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test,
conn_state->hdmi.broadcast_rgb,
DRM_HDMI_BROADCAST_RGB_AUTO);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *mode;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
drm_modeset_acquire_init(&ctx, 0);
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
KUNIT_ASSERT_NOT_NULL(test, mode);
crtc = priv->crtc;
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
mode,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test,
conn_state->hdmi.broadcast_rgb,
DRM_HDMI_BROADCAST_RGB_AUTO);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL;
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test,
conn_state->hdmi.broadcast_rgb,
DRM_HDMI_BROADCAST_RGB_FULL);
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *mode;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
drm_modeset_acquire_init(&ctx, 0);
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
KUNIT_ASSERT_NOT_NULL(test, mode);
crtc = priv->crtc;
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
mode,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL;
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test,
conn_state->hdmi.broadcast_rgb,
DRM_HDMI_BROADCAST_RGB_FULL);
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED;
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test,
conn_state->hdmi.broadcast_rgb,
DRM_HDMI_BROADCAST_RGB_LIMITED);
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *mode;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
drm_modeset_acquire_init(&ctx, 0);
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
KUNIT_ASSERT_NOT_NULL(test, mode);
crtc = priv->crtc;
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
mode,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED;
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test,
conn_state->hdmi.broadcast_rgb,
DRM_HDMI_BROADCAST_RGB_LIMITED);
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_broadcast_rgb_cea_mode_yuv420(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
enum drm_hdmi_broadcast_rgb broadcast_rgb;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_atomic_state *state;
struct drm_display_mode *mode;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
broadcast_rgb = *(enum drm_hdmi_broadcast_rgb *)test->param_value;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV420),
8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95);
KUNIT_ASSERT_NOT_NULL(test, mode);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
mode, &ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_conn_state:
conn_state = drm_atomic_get_connector_state(state, conn);
if (PTR_ERR(conn_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
conn_state->hdmi.broadcast_rgb = broadcast_rgb;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.broadcast_rgb, broadcast_rgb);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV420);
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static const enum drm_hdmi_broadcast_rgb check_broadcast_rgb_cea_mode_yuv420_tests[] = {
DRM_HDMI_BROADCAST_RGB_AUTO,
DRM_HDMI_BROADCAST_RGB_FULL,
DRM_HDMI_BROADCAST_RGB_LIMITED,
};
static void
check_broadcast_rgb_cea_mode_yuv420_desc(const enum drm_hdmi_broadcast_rgb *broadcast_rgb,
char *desc)
{
sprintf(desc, "%s", drm_hdmi_connector_get_broadcast_rgb_name(*broadcast_rgb));
}
KUNIT_ARRAY_PARAM(check_broadcast_rgb_cea_mode_yuv420,
check_broadcast_rgb_cea_mode_yuv420_tests,
check_broadcast_rgb_cea_mode_yuv420_desc);
static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *old_conn_state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
10,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
new_conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
new_conn_state->max_requested_bpc = 8;
KUNIT_ASSERT_NE(test,
old_conn_state->max_requested_bpc,
new_conn_state->max_requested_bpc);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
new_conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
KUNIT_ASSERT_NE(test,
old_conn_state->hdmi.output_bpc,
new_conn_state->hdmi.output_bpc);
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *old_conn_state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
10,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
new_conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
KUNIT_ASSERT_EQ(test,
new_conn_state->hdmi.output_bpc,
old_conn_state->hdmi.output_bpc);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
old_conn_state = drm_atomic_get_old_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state);
new_conn_state = drm_atomic_get_new_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
KUNIT_EXPECT_EQ(test,
old_conn_state->hdmi.output_bpc,
new_conn_state->hdmi.output_bpc);
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_dvi(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
12,
&dummy_connector_hdmi_funcs,
test_edid_dvi_1080p);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_FALSE(test, info->is_hdmi);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
10,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 10);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 12);
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_crtc_state *crtc_state;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
conn->hdmi.funcs = &reject_connector_hdmi_funcs;
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
crtc_state = drm_atomic_get_crtc_state(state, crtc);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->connectors_changed = true;
ret = drm_atomic_check_only(state);
KUNIT_EXPECT_LT(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_max_tmds_rate_bpc_fallback_rgb(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_max_tmds_rate_bpc_fallback_yuv420(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *yuv420_only_mode;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV420),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
KUNIT_ASSERT_TRUE(test, conn->ycbcr_420_allowed);
yuv420_only_mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95);
KUNIT_ASSERT_NOT_NULL(test, yuv420_only_mode);
KUNIT_ASSERT_TRUE(test, drm_mode_is_420_only(info, yuv420_only_mode));
rate = drm_hdmi_compute_mode_clock(yuv420_only_mode, 12, HDMI_COLORSPACE_YUV420);
KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(yuv420_only_mode, 10, HDMI_COLORSPACE_YUV420);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
yuv420_only_mode, &ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV420);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, yuv420_only_mode->clock * 625);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
rate = drm_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV420),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
KUNIT_ASSERT_TRUE(test, conn->ycbcr_420_allowed);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
KUNIT_ASSERT_TRUE(test, drm_mode_is_420_also(info, preferred));
rate = drm_hdmi_compute_mode_clock(preferred, 8, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV420);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
preferred, &ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_driver_unsupported_fallback_yuv420(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_info *info;
struct drm_display_mode *preferred, *yuv420_only_mode;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_FALSE(test, conn->ycbcr_420_allowed);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_ASSERT_FALSE(test, drm_mode_is_420_also(info, preferred));
yuv420_only_mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95);
KUNIT_ASSERT_NOT_NULL(test, yuv420_only_mode);
KUNIT_ASSERT_TRUE(test, drm_mode_is_420_only(info, yuv420_only_mode));
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
preferred, &ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
ret = drm_atomic_set_mode_for_crtc(crtc_state, yuv420_only_mode);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_LT(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *mode;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
KUNIT_ASSERT_NOT_NULL(test, mode);
rate = mode->clock * 1500;
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
crtc = priv->crtc;
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
mode,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_display_info *info;
struct drm_display_mode *preferred;
unsigned long long rate;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
12,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
info = &conn->display_info;
KUNIT_ASSERT_TRUE(test, info->is_hdmi);
KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, conn_state);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_disable_connector(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int ret;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm_modeset_acquire_init(&ctx, 0);
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm = &priv->drm;
crtc = priv->crtc;
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
crtc_state = drm_atomic_get_crtc_state(state, crtc);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->active = false;
ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
KUNIT_EXPECT_EQ(test, ret, 0);
conn_state = drm_atomic_get_connector_state(state, conn);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode),
KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode_vic_1),
KUNIT_CASE(drm_test_check_broadcast_rgb_full_cea_mode),
KUNIT_CASE(drm_test_check_broadcast_rgb_full_cea_mode_vic_1),
KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode),
KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1),
KUNIT_CASE_PARAM(drm_test_check_broadcast_rgb_cea_mode_yuv420,
check_broadcast_rgb_cea_mode_yuv420_gen_params),
KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_changed),
KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
KUNIT_CASE(drm_test_check_disable_connector),
KUNIT_CASE(drm_test_check_hdmi_funcs_reject_rate),
KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_rgb),
KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_yuv420),
KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422),
KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420),
KUNIT_CASE(drm_test_check_driver_unsupported_fallback_yuv420),
KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed),
KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed),
KUNIT_CASE(drm_test_check_output_bpc_dvi),
KUNIT_CASE(drm_test_check_output_bpc_format_vic_1),
KUNIT_CASE(drm_test_check_output_bpc_format_display_8bpc_only),
KUNIT_CASE(drm_test_check_output_bpc_format_display_rgb_only),
KUNIT_CASE(drm_test_check_output_bpc_format_driver_8bpc_only),
KUNIT_CASE(drm_test_check_output_bpc_format_driver_rgb_only),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_8bpc),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc),
{ }
};
static struct kunit_suite drm_atomic_helper_connector_hdmi_check_test_suite = {
.name = "drm_atomic_helper_connector_hdmi_check",
.test_cases = drm_atomic_helper_connector_hdmi_check_tests,
};
static void drm_test_check_broadcast_rgb_value(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
conn_state = conn->state;
KUNIT_EXPECT_EQ(test, conn_state->hdmi.broadcast_rgb, DRM_HDMI_BROADCAST_RGB_AUTO);
}
static void drm_test_check_bpc_8_value(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
conn_state = conn->state;
KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 8);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0);
}
static void drm_test_check_bpc_10_value(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
10);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
conn_state = conn->state;
KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 10);
KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 10);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0);
}
static void drm_test_check_bpc_12_value(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
12);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
conn_state = conn->state;
KUNIT_EXPECT_EQ(test, conn_state->max_bpc, 12);
KUNIT_EXPECT_EQ(test, conn_state->max_requested_bpc, 12);
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0);
}
static void drm_test_check_format_value(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
conn_state = conn->state;
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, 0);
}
static void drm_test_check_tmds_char_value(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB) |
BIT(HDMI_COLORSPACE_YUV422) |
BIT(HDMI_COLORSPACE_YUV444),
12);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
conn_state = conn->state;
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, 0);
}
static struct kunit_case drm_atomic_helper_connector_hdmi_reset_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_value),
KUNIT_CASE(drm_test_check_bpc_8_value),
KUNIT_CASE(drm_test_check_bpc_10_value),
KUNIT_CASE(drm_test_check_bpc_12_value),
KUNIT_CASE(drm_test_check_format_value),
KUNIT_CASE(drm_test_check_tmds_char_value),
{ }
};
static struct kunit_suite drm_atomic_helper_connector_hdmi_reset_test_suite = {
.name = "drm_atomic_helper_connector_hdmi_reset",
.test_cases = drm_atomic_helper_connector_hdmi_reset_tests,
};
static void drm_test_check_mode_valid(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector *conn;
struct drm_display_mode *preferred;
priv = drm_kunit_helper_connector_hdmi_init(test,
BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_EXPECT_EQ(test, preferred->hdisplay, 1920);
KUNIT_EXPECT_EQ(test, preferred->vdisplay, 1080);
KUNIT_EXPECT_EQ(test, preferred->clock, 148500);
}
static void drm_test_check_mode_valid_reject_rate(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_display_mode *preferred;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_100mhz_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
preferred = find_preferred_mode(&priv->connector);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_EXPECT_EQ(test, preferred->hdisplay, 640);
KUNIT_EXPECT_EQ(test, preferred->vdisplay, 480);
KUNIT_EXPECT_EQ(test, preferred->clock, 25200);
}
static void drm_test_check_mode_valid_reject(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector *conn;
struct drm_display_mode *preferred;
unsigned char no_edid[] = {};
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_connector_hdmi_funcs,
no_edid);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
ret = set_connector_edid(test, conn,
test_edid_hdmi_1080p_rgb_max_200mhz,
ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz));
KUNIT_ASSERT_EQ(test, ret, 0);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NULL(test, preferred);
}
static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector *conn;
struct drm_display_mode *preferred;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_100mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 100 * 1000);
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
KUNIT_EXPECT_EQ(test, preferred->hdisplay, 640);
KUNIT_EXPECT_EQ(test, preferred->vdisplay, 480);
KUNIT_EXPECT_EQ(test, preferred->clock, 25200);
}
static struct kunit_case drm_atomic_helper_connector_hdmi_mode_valid_tests[] = {
KUNIT_CASE(drm_test_check_mode_valid),
KUNIT_CASE(drm_test_check_mode_valid_reject),
KUNIT_CASE(drm_test_check_mode_valid_reject_rate),
KUNIT_CASE(drm_test_check_mode_valid_reject_max_clock),
{ }
};
static struct kunit_suite drm_atomic_helper_connector_hdmi_mode_valid_test_suite = {
.name = "drm_atomic_helper_connector_hdmi_mode_valid",
.test_cases = drm_atomic_helper_connector_hdmi_mode_valid_tests,
};
static void drm_test_check_infoframes(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_GE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static int reject_infoframe_write_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
return -EOPNOTSUPP;
}
static const struct drm_connector_hdmi_funcs reject_avi_infoframe_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
};
static void drm_test_check_reject_avi_infoframe(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_avi_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_NE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static const struct drm_connector_hdmi_funcs reject_hdr_infoframe_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdr_drm = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
},
};
static void drm_test_check_reject_hdr_infoframe_bpc_8(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_hdr_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_conn_state:
new_conn_state = drm_atomic_get_connector_state(state, conn);
if (PTR_ERR(new_conn_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
for (int i = 0; i < conn->base.properties->count; i++)
KUNIT_ASSERT_PTR_NE(test,
drm->mode_config.hdr_output_metadata_property,
conn->base.properties->properties[i]);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, old_hdmi_update_failures, priv->hdmi_update_failures);
new_conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.output_bpc, 8);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.infoframes.hdr_drm.set, false);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static void drm_test_check_reject_hdr_infoframe_bpc_10(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
struct hdr_output_metadata hdr_data;
struct drm_property_blob *hdr_blob;
bool replaced;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
10,
&reject_hdr_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_conn_state:
new_conn_state = drm_atomic_get_connector_state(state, conn);
if (PTR_ERR(new_conn_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_conn_state);
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
hdr_data.metadata_type = HDMI_STATIC_METADATA_TYPE1;
hdr_data.hdmi_metadata_type1.eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
hdr_data.hdmi_metadata_type1.metadata_type = HDMI_STATIC_METADATA_TYPE1;
hdr_blob = drm_property_create_blob(drm, sizeof(hdr_data), &hdr_data);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdr_blob);
ret = drm_property_replace_blob_from_id(drm,
&new_conn_state->hdr_output_metadata,
hdr_blob->base.id,
-1, sizeof(struct hdr_output_metadata), -1,
&replaced);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_ASSERT_EQ(test, replaced, true);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_LE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
new_conn_state = conn->state;
KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.output_bpc, 10);
KUNIT_ASSERT_EQ(test, new_conn_state->hdmi.infoframes.hdr_drm.set, true);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static const struct drm_connector_hdmi_funcs reject_audio_infoframe_hdmi_funcs = {
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.hdmi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
},
.audio = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
},
};
static void drm_test_check_reject_audio_infoframe(struct kunit *test)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
struct drm_display_mode *preferred;
struct drm_connector *conn;
struct drm_device *drm;
struct drm_crtc *crtc;
int old_hdmi_update_failures;
struct hdmi_audio_infoframe cea;
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
BIT(HDMI_COLORSPACE_RGB),
8,
&reject_audio_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
crtc = priv->crtc;
conn = &priv->connector;
preferred = find_preferred_mode(conn);
KUNIT_ASSERT_NOT_NULL(test, preferred);
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
if (ret == -EDEADLK) {
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_conn_enable;
}
KUNIT_ASSERT_EQ(test, ret, 0);
drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, old_hdmi_update_failures, priv->hdmi_update_failures);
hdmi_audio_infoframe_init(&cea);
cea.channels = 2;
cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(conn, &cea);
KUNIT_ASSERT_EQ(test, ret, -EOPNOTSUPP);
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
retry_crtc_state_2:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (PTR_ERR(crtc_state) == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state_2;
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
crtc_state->mode_changed = true;
old_hdmi_update_failures = priv->hdmi_update_failures;
ret = drm_atomic_check_only(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state_2;
}
KUNIT_ASSERT_EQ(test, ret, 0);
ret = drm_atomic_commit(state);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry_crtc_state_2;
}
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_LE(test, old_hdmi_update_failures, priv->hdmi_update_failures);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
static struct kunit_case drm_atomic_helper_connector_hdmi_infoframes_tests[] = {
KUNIT_CASE(drm_test_check_infoframes),
KUNIT_CASE(drm_test_check_reject_avi_infoframe),
KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_8),
KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_10),
KUNIT_CASE(drm_test_check_reject_audio_infoframe),
{ }
};
static struct kunit_suite drm_atomic_helper_connector_hdmi_infoframes_test_suite = {
.name = "drm_atomic_helper_connector_hdmi_infoframes",
.test_cases = drm_atomic_helper_connector_hdmi_infoframes_tests,
};
kunit_test_suites(
&drm_atomic_helper_connector_hdmi_check_test_suite,
&drm_atomic_helper_connector_hdmi_reset_test_suite,
&drm_atomic_helper_connector_hdmi_mode_valid_test_suite,
&drm_atomic_helper_connector_hdmi_infoframes_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_DESCRIPTION("Kunit test for drm_hdmi_state_helper functions");
MODULE_LICENSE("GPL");