On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote: > > The i915 driver has a property to force the RGB range of an HDMI output. > The vc4 driver then implemented the same property with the same > semantics. KWin has support for it, and a PR for mutter is also there to > support it. > > Both drivers implementing the same property with the same semantics, > plus the userspace having support for it, is proof enough that it's > pretty much a de-facto standard now and we can provide helpers for it. > > Let's plumb it into the newly created HDMI connector.
To have such a significant proportion of the patch being kunit tests when there was no reference to such in the commit text was slightly unexpected. > Signed-off-by: Maxime Ripard <[email protected]> Reviewed-by: Dave Stevenson <[email protected]> > --- > Documentation/gpu/kms-properties.csv | 1 - > drivers/gpu/drm/drm_atomic.c | 5 + > drivers/gpu/drm/drm_atomic_state_helper.c | 17 + > drivers/gpu/drm/drm_atomic_uapi.c | 4 + > drivers/gpu/drm/drm_connector.c | 76 +++++ > drivers/gpu/drm/tests/Makefile | 1 + > .../gpu/drm/tests/drm_atomic_state_helper_test.c | 376 > +++++++++++++++++++++ > drivers/gpu/drm/tests/drm_connector_test.c | 117 ++++++- > drivers/gpu/drm/tests/drm_kunit_edid.h | 106 ++++++ > include/drm/drm_connector.h | 36 ++ > 10 files changed, 737 insertions(+), 2 deletions(-) > > diff --git a/Documentation/gpu/kms-properties.csv > b/Documentation/gpu/kms-properties.csv > index 0f9590834829..caef14c532d4 100644 > --- a/Documentation/gpu/kms-properties.csv > +++ b/Documentation/gpu/kms-properties.csv > @@ -17,7 +17,6 @@ Owner Module/Drivers,Group,Property Name,Type,Property > Values,Object attached,De > ,Virtual GPU,“suggested X”,RANGE,"Min=0, Max=0xffffffff",Connector,property > to suggest an X offset for a connector > ,,“suggested Y”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest > an Y offset for a connector > ,Optional,"""aspect ratio""",ENUM,"{ ""None"", ""4:3"", ""16:9"" > }",Connector,TDB > -i915,Generic,"""Broadcast RGB""",ENUM,"{ ""Automatic"", ""Full"", ""Limited > 16:235"" }",Connector,"When this property is set to Limited 16:235 and CTM is > set, the hardware will be programmed with the result of the multiplication of > CTM by the limited range matrix to ensure the pixels normally in the range > 0..1.0 are remapped to the range 16/255..235/255." > ,,“audio”,ENUM,"{ ""force-dvi"", ""off"", ""auto"", ""on"" }",Connector,TBD > ,SDVO-TV,“mode”,ENUM,"{ ""NTSC_M"", ""NTSC_J"", ""NTSC_443"", ""PAL_B"" } > etc.",Connector,TBD > ,,"""left_margin""",RANGE,"Min=0, Max= SDVO dependent",Connector,TBD > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c > index c31fc0b48c31..1465a7f09a0b 100644 > --- a/drivers/gpu/drm/drm_atomic.c > +++ b/drivers/gpu/drm/drm_atomic.c > @@ -1142,6 +1142,11 @@ static void drm_atomic_connector_print_state(struct > drm_printer *p, > drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc); > drm_printf(p, "\tcolorspace=%s\n", > drm_get_colorspace_name(state->colorspace)); > > + if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || > + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) > + drm_printf(p, "\tbroadcast_rgb=%s\n", > + > drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); > + > if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) > if (state->writeback_job && state->writeback_job->fb) > drm_printf(p, "\tfb=%d\n", > state->writeback_job->fb->base.id); > diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c > b/drivers/gpu/drm/drm_atomic_state_helper.c > index e69c0cc1c6da..10d98620a358 100644 > --- a/drivers/gpu/drm/drm_atomic_state_helper.c > +++ b/drivers/gpu/drm/drm_atomic_state_helper.c > @@ -583,6 +583,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); > void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector > *connector, > struct drm_connector_state > *new_state) > { > + new_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; > } > EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); > > @@ -650,6 +651,22 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check); > int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, > struct drm_atomic_state *state) > { > + struct drm_connector_state *old_state = > + drm_atomic_get_old_connector_state(state, connector); > + struct drm_connector_state *new_state = > + drm_atomic_get_new_connector_state(state, connector); > + > + if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb) { > + struct drm_crtc *crtc = new_state->crtc; > + struct drm_crtc_state *crtc_state; > + > + crtc_state = drm_atomic_get_crtc_state(state, crtc); > + if (IS_ERR(crtc_state)) > + return PTR_ERR(crtc_state); > + > + crtc_state->mode_changed = true; > + } > + > return 0; > } > EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check); > diff --git a/drivers/gpu/drm/drm_atomic_uapi.c > b/drivers/gpu/drm/drm_atomic_uapi.c > index aee4a65d4959..3eb4f4bc8b71 100644 > --- a/drivers/gpu/drm/drm_atomic_uapi.c > +++ b/drivers/gpu/drm/drm_atomic_uapi.c > @@ -818,6 +818,8 @@ static int drm_atomic_connector_set_property(struct > drm_connector *connector, > state->max_requested_bpc = val; > } else if (property == connector->privacy_screen_sw_state_property) { > state->privacy_screen_sw_state = val; > + } else if (property == connector->broadcast_rgb_property) { > + state->hdmi.broadcast_rgb = val; > } else if (connector->funcs->atomic_set_property) { > return connector->funcs->atomic_set_property(connector, > state, property, val); > @@ -901,6 +903,8 @@ drm_atomic_connector_get_property(struct drm_connector > *connector, > *val = state->max_requested_bpc; > } else if (property == connector->privacy_screen_sw_state_property) { > *val = state->privacy_screen_sw_state; > + } else if (property == connector->broadcast_rgb_property) { > + *val = state->hdmi.broadcast_rgb; > } else if (connector->funcs->atomic_get_property) { > return connector->funcs->atomic_get_property(connector, > state, property, val); > diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c > index d9961cce8245..929b0a911f62 100644 > --- a/drivers/gpu/drm/drm_connector.c > +++ b/drivers/gpu/drm/drm_connector.c > @@ -1183,6 +1183,29 @@ static const u32 dp_colorspaces = > BIT(DRM_MODE_COLORIMETRY_BT2020_CYCC) | > BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); > > +static const struct drm_prop_enum_list broadcast_rgb_names[] = { > + { DRM_HDMI_BROADCAST_RGB_AUTO, "Automatic" }, > + { DRM_HDMI_BROADCAST_RGB_FULL, "Full" }, > + { DRM_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235" }, > +}; > + > +/* > + * drm_hdmi_connector_get_broadcast_rgb_name - Return a string for HDMI > connector RGB broadcast selection > + * @broadcast_rgb: Broadcast RGB selection to compute name of > + * > + * Returns: the name of the Broadcast RGB selection, or NULL if the type > + * is not valid. > + */ > +const char * > +drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb > broadcast_rgb) > +{ > + if (broadcast_rgb > DRM_HDMI_BROADCAST_RGB_LIMITED) > + return NULL; > + > + return broadcast_rgb_names[broadcast_rgb].name; > +} > +EXPORT_SYMBOL(drm_hdmi_connector_get_broadcast_rgb_name); > + > /** > * DOC: standard connector properties > * > @@ -1655,6 +1678,26 @@ > EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); > /** > * DOC: HDMI connector properties > * > + * Broadcast RGB > + * Indicates the RGB Quantization Range (Full vs Limited) used. > + * Infoframes will be generated according to that value. > + * > + * The value of this property can be one of the following: > + * > + * Automatic: > + * RGB Range is selected automatically based on the mode > + * according to the HDMI specifications. > + * > + * Full: > + * Full RGB Range is forced. > + * > + * Limited 16:235: > + * Limited RGB Range is forced. Unlike the name suggests, > + * this works for any number of bits-per-component. > + * > + * Drivers can set up this property by calling > + * drm_connector_attach_broadcast_rgb_property(). > + * > * content type (HDMI specific): > * Indicates content type setting to be used in HDMI infoframes to > indicate > * content type for the external device, so that it adjusts its display > @@ -2517,6 +2560,39 @@ int > drm_connector_attach_hdr_output_metadata_property(struct drm_connector *conn > } > EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property); > > +/** > + * drm_connector_attach_broadcast_rgb_property - attach "Broadcast RGB" > property > + * @connector: connector to attach the property on. > + * > + * This is used to add support for forcing the RGB range on a connector > + * > + * Returns: > + * Zero on success, negative errno on failure. > + */ > +int drm_connector_attach_broadcast_rgb_property(struct drm_connector > *connector) > +{ > + struct drm_device *dev = connector->dev; > + struct drm_property *prop; > + > + prop = connector->broadcast_rgb_property; > + if (!prop) { > + prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, > + "Broadcast RGB", > + broadcast_rgb_names, > + > ARRAY_SIZE(broadcast_rgb_names)); > + if (!prop) > + return -EINVAL; > + > + connector->broadcast_rgb_property = prop; > + } > + > + drm_object_attach_property(&connector->base, prop, > + DRM_HDMI_BROADCAST_RGB_AUTO); > + > + return 0; > +} > +EXPORT_SYMBOL(drm_connector_attach_broadcast_rgb_property); > + > /** > * drm_connector_attach_colorspace_property - attach "Colorspace" property > * @connector: connector to attach the property on. > diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile > index d6183b3d7688..b29ddfd90596 100644 > --- a/drivers/gpu/drm/tests/Makefile > +++ b/drivers/gpu/drm/tests/Makefile > @@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \ > drm_kunit_helpers.o > > obj-$(CONFIG_DRM_KUNIT_TEST) += \ > + drm_atomic_state_helper_test.o \ > drm_buddy_test.o \ > drm_cmdline_parser_test.o \ > drm_connector_test.o \ > diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c > b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c > new file mode 100644 > index 000000000000..21e6f796ee13 > --- /dev/null > +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c > @@ -0,0 +1,376 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Kunit test for drm_atomic_state_helper functions > + */ > + > +#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_probe_helper.h> > + > +#include <drm/drm_print.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 char *current_edid; > + size_t current_edid_len; > +}; > + > +#define connector_to_priv(c) \ > + container_of_const(c, struct drm_atomic_helper_connector_hdmi_priv, > connector) > + > +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(&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 light_up_connector(struct kunit *test, > + struct drm_device *drm, > + struct drm_crtc *crtc, > + struct drm_connector *connector, > + struct drm_display_mode *mode, > + struct drm_modeset_acquire_ctx *ctx) > +{ > + struct drm_atomic_state *state; > + struct drm_connector_state *conn_state; > + struct drm_crtc_state *crtc_state; > + int ret; > + > + 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, connector); > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); > + > + ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); > + KUNIT_EXPECT_EQ(test, ret, 0); > + > + crtc_state = drm_atomic_get_crtc_state(state, crtc); > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); > + > + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); > + KUNIT_EXPECT_EQ(test, ret, 0); > + > + crtc_state->enable = true; > + crtc_state->active = true; > + > + ret = drm_atomic_commit(state); > + KUNIT_ASSERT_EQ(test, ret, 0); > + > + return 0; > +} > + > +static int set_connector_edid(struct kunit *test, struct drm_connector > *connector, > + const char *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); > + KUNIT_ASSERT_GT(test, ret, 0); > + > + return 0; > +} > + > +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, > +}; > + > +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 > +struct drm_atomic_helper_connector_hdmi_priv * > +drm_atomic_helper_connector_hdmi_init(struct kunit *test) > +{ > + 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; > + ret = drmm_connector_hdmi_init(drm, conn, > + &dummy_connector_funcs, > + DRM_MODE_CONNECTOR_HDMIA, > + NULL); > + 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); > + > + 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); > + > + return priv; > +} > + > +/* > + * Test that if we change the RGB quantization property to a different > + * value, we trigger a mode change on the connector's CRTC, which will > + * in turn disable/enable the connector. > + */ > +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_atomic_helper_connector_hdmi_init(test); > + KUNIT_ASSERT_NOT_NULL(test, priv); > + > + ctx = drm_kunit_helper_acquire_ctx_alloc(test); > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); > + > + conn = &priv->connector; > + preferred = find_preferred_mode(conn); > + KUNIT_ASSERT_NOT_NULL(test, preferred); > + > + drm = &priv->drm; > + crtc = priv->crtc; > + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); > + 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); > +} > + > +/* > + * Test that if we set the RGB quantization property to the same value, > + * we don't trigger a mode change on the connector's CRTC and leave the > + * connector unaffected. > + */ > +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_atomic_helper_connector_hdmi_init(test); > + KUNIT_ASSERT_NOT_NULL(test, priv); > + > + ctx = drm_kunit_helper_acquire_ctx_alloc(test); > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); > + > + conn = &priv->connector; > + preferred = find_preferred_mode(conn); > + KUNIT_ASSERT_NOT_NULL(test, preferred); > + > + drm = &priv->drm; > + crtc = priv->crtc; > + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx); > + 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); > +} > + > +static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { > + KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_changed), > + KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed), > + { } > +}; > + > +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, > +}; > + > +/* > + * Test that the value of the Broadcast RGB property out of reset is set > + * to auto. > + */ > +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_atomic_helper_connector_hdmi_init(test); > + 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 struct kunit_case drm_atomic_helper_connector_hdmi_reset_tests[] = { > + KUNIT_CASE(drm_test_check_broadcast_rgb_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, > +}; > + > +kunit_test_suites( > + &drm_atomic_helper_connector_hdmi_check_test_suite, > + &drm_atomic_helper_connector_hdmi_reset_test_suite, > +); > + > +MODULE_AUTHOR("Maxime Ripard <[email protected]>"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/tests/drm_connector_test.c > b/drivers/gpu/drm/tests/drm_connector_test.c > index 8f070cacab3b..41d33dea30af 100644 > --- a/drivers/gpu/drm/tests/drm_connector_test.c > +++ b/drivers/gpu/drm/tests/drm_connector_test.c > @@ -12,6 +12,8 @@ > > #include <kunit/test.h> > > +#include "../drm_crtc_internal.h" > + > struct drm_connector_init_priv { > struct drm_device drm; > struct drm_connector connector; > @@ -357,10 +359,123 @@ static struct kunit_suite > drm_get_tv_mode_from_name_test_suite = { > .test_cases = drm_get_tv_mode_from_name_tests, > }; > > +struct drm_hdmi_connector_get_broadcast_rgb_name_test { > + unsigned int kind; > + const char *expected_name; > +}; > + > +#define BROADCAST_RGB_TEST(_kind, _name) \ > + { \ > + .kind = _kind, \ > + .expected_name = _name, \ > + } > + > +static void drm_test_drm_hdmi_connector_get_broadcast_rgb_name(struct kunit > *test) > +{ > + const struct drm_hdmi_connector_get_broadcast_rgb_name_test *params = > + test->param_value; > + > + KUNIT_EXPECT_STREQ(test, > + > drm_hdmi_connector_get_broadcast_rgb_name(params->kind), > + params->expected_name); > +} > + > +static const > +struct drm_hdmi_connector_get_broadcast_rgb_name_test > +drm_hdmi_connector_get_broadcast_rgb_name_valid_tests[] = { > + BROADCAST_RGB_TEST(DRM_HDMI_BROADCAST_RGB_AUTO, "Automatic"), > + BROADCAST_RGB_TEST(DRM_HDMI_BROADCAST_RGB_FULL, "Full"), > + BROADCAST_RGB_TEST(DRM_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235"), > +}; > + > +static void > +drm_hdmi_connector_get_broadcast_rgb_name_valid_desc(const struct > drm_hdmi_connector_get_broadcast_rgb_name_test *t, > + char *desc) > +{ > + sprintf(desc, "%s", t->expected_name); > +} > + > +KUNIT_ARRAY_PARAM(drm_hdmi_connector_get_broadcast_rgb_name_valid, > + drm_hdmi_connector_get_broadcast_rgb_name_valid_tests, > + drm_hdmi_connector_get_broadcast_rgb_name_valid_desc); > + > +static void > drm_test_drm_hdmi_connector_get_broadcast_rgb_name_invalid(struct kunit *test) > +{ > + KUNIT_EXPECT_NULL(test, drm_hdmi_connector_get_broadcast_rgb_name(3)); > +}; > + > +static struct kunit_case drm_hdmi_connector_get_broadcast_rgb_name_tests[] = > { > + KUNIT_CASE_PARAM(drm_test_drm_hdmi_connector_get_broadcast_rgb_name, > + > drm_hdmi_connector_get_broadcast_rgb_name_valid_gen_params), > + > KUNIT_CASE(drm_test_drm_hdmi_connector_get_broadcast_rgb_name_invalid), > + { } > +}; > + > +static struct kunit_suite > drm_hdmi_connector_get_broadcast_rgb_name_test_suite = { > + .name = "drm_hdmi_connector_get_broadcast_rgb_name", > + .test_cases = drm_hdmi_connector_get_broadcast_rgb_name_tests, > +}; > + > +static void drm_test_drm_connector_attach_broadcast_rgb_property(struct > kunit *test) > +{ > + struct drm_connector_init_priv *priv = test->priv; > + struct drm_connector *connector = &priv->connector; > + struct drm_property *prop; > + int ret; > + > + ret = drmm_connector_init(&priv->drm, connector, > + &dummy_funcs, > + DRM_MODE_CONNECTOR_HDMIA, > + &priv->ddc); > + KUNIT_ASSERT_EQ(test, ret, 0); > + > + ret = drm_connector_attach_broadcast_rgb_property(connector); > + KUNIT_ASSERT_EQ(test, ret, 0); > + > + prop = connector->broadcast_rgb_property; > + KUNIT_ASSERT_NOT_NULL(test, prop); > + KUNIT_EXPECT_NOT_NULL(test, > drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); > +} > + > +static void > drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(struct > kunit *test) > +{ > + struct drm_connector_init_priv *priv = test->priv; > + struct drm_connector *connector = &priv->connector; > + struct drm_property *prop; > + int ret; > + > + ret = drmm_connector_hdmi_init(&priv->drm, connector, > + &dummy_funcs, > + DRM_MODE_CONNECTOR_HDMIA, > + &priv->ddc); > + KUNIT_EXPECT_EQ(test, ret, 0); > + > + ret = drm_connector_attach_broadcast_rgb_property(connector); > + KUNIT_ASSERT_EQ(test, ret, 0); > + > + prop = connector->broadcast_rgb_property; > + KUNIT_ASSERT_NOT_NULL(test, prop); > + KUNIT_EXPECT_NOT_NULL(test, > drm_mode_obj_find_prop_id(&connector->base, prop->base.id)); > +} > + > +static struct kunit_case drm_connector_attach_broadcast_rgb_property_tests[] > = { > + KUNIT_CASE(drm_test_drm_connector_attach_broadcast_rgb_property), > + > KUNIT_CASE(drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector), > + { } > +}; > + > +static struct kunit_suite > drm_connector_attach_broadcast_rgb_property_test_suite = { > + .name = "drm_connector_attach_broadcast_rgb_property", > + .init = drm_test_connector_init, > + .test_cases = drm_connector_attach_broadcast_rgb_property_tests, > +}; > + > kunit_test_suites( > &drmm_connector_hdmi_init_test_suite, > &drmm_connector_init_test_suite, > - &drm_get_tv_mode_from_name_test_suite > + &drm_connector_attach_broadcast_rgb_property_test_suite, > + &drm_get_tv_mode_from_name_test_suite, > + &drm_hdmi_connector_get_broadcast_rgb_name_test_suite > ); > > MODULE_AUTHOR("Maxime Ripard <[email protected]>"); > diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h > b/drivers/gpu/drm/tests/drm_kunit_edid.h > new file mode 100644 > index 000000000000..2bba316de064 > --- /dev/null > +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h > @@ -0,0 +1,106 @@ > +#ifndef DRM_KUNIT_EDID_H_ > +#define DRM_KUNIT_EDID_H_ > + > +/* > + * edid-decode (hex): > + * > + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 > + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 > + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 > + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c > + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 > + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 > + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 > + * > + * 02 03 1b 81 e3 05 00 20 41 10 e2 00 4a 6d 03 0c > + * 00 12 34 00 28 20 00 00 00 00 00 00 00 00 00 00 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 > + * > + * ---------------- > + * > + * Block 0, Base EDID: > + * EDID Structure Version & Revision: 1.3 > + * Vendor & Product Identification: > + * Manufacturer: LNX > + * Model: 42 > + * Made in: 2023 > + * Basic Display Parameters & Features: > + * Digital display > + * DFP 1.x compatible TMDS > + * Maximum image size: 160 cm x 90 cm > + * Gamma: 2.20 > + * Monochrome or grayscale display > + * First detailed timing is the preferred timing > + * Color Characteristics: > + * Red : 0.0000, 0.0000 > + * Green: 0.0000, 0.0000 > + * Blue : 0.0000, 0.0000 > + * White: 0.0000, 0.0000 > + * Established Timings I & II: > + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz > 25.175000 MHz > + * Standard Timings: none > + * Detailed Timing Descriptors: > + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 > MHz (1600 mm x 900 mm) > + * Hfront 88 Hsync 44 Hback 148 Hpol P > + * Vfront 4 Vsync 5 Vback 36 Vpol P > + * Display Product Name: 'Test EDID' > + * Display Range Limits: > + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz > + * Dummy Descriptor: > + * Extension blocks: 1 > + * Checksum: 0x92 > + * > + * ---------------- > + * > + * Block 1, CTA-861 Extension Block: > + * Revision: 3 > + * Underscans IT Video Formats by default > + * Native detailed modes: 1 > + * Colorimetry Data Block: > + * sRGB > + * Video Data Block: > + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 > MHz > + * Video Capability Data Block: > + * YCbCr quantization: No Data > + * RGB quantization: Selectable (via AVI Q) > + * PT scan behavior: No Data > + * IT scan behavior: Always Underscanned > + * CE scan behavior: Always Underscanned > + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: > + * Source physical address: 1.2.3.4 > + * Maximum TMDS clock: 200 MHz > + * Extended HDMI video details: > + * Checksum: 0xd0 Unused space in Extension Block: 100 bytes > + */ > +const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { > + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, > + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, > + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, > + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, > + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, > + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, > + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, > + 0x46, 0x00, 0x00, 0xc4, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, > + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x02, 0x03, 0x1b, 0x81, > + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x6d, 0x03, 0x0c, > + 0x00, 0x12, 0x34, 0x00, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0xd0 > +}; > + > +#endif // DRM_KUNIT_EDID_H_ > diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h > index 000a2a156619..3867a4c01b78 100644 > --- a/include/drm/drm_connector.h > +++ b/include/drm/drm_connector.h > @@ -368,6 +368,30 @@ enum drm_panel_orientation { > DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, > }; > > +/** > + * enum drm_hdmi_broadcast_rgb - Broadcast RGB Selection for an HDMI > @drm_connector > + */ > +enum drm_hdmi_broadcast_rgb { > + /** > + * @DRM_HDMI_BROADCAST_RGB_AUTO: The RGB range is selected > + * automatically based on the mode. > + */ > + DRM_HDMI_BROADCAST_RGB_AUTO, > + > + /** > + * @DRM_HDMI_BROADCAST_RGB_FULL: Full range RGB is forced. > + */ > + DRM_HDMI_BROADCAST_RGB_FULL, > + > + /** > + * @DRM_HDMI_BROADCAST_RGB_LIMITED: Limited range RGB is forced. > + */ > + DRM_HDMI_BROADCAST_RGB_LIMITED, > +}; > + > +const char * > +drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb > broadcast_rgb); > + > /** > * struct drm_monitor_range_info - Panel's Monitor range in EDID for > * &drm_display_info > @@ -1037,6 +1061,11 @@ struct drm_connector_state { > * @drm_atomic_helper_connector_hdmi_check(). > */ > struct { > + /** > + * @broadcast_rgb: Connector property to pass the > + * Broadcast RGB selection value. > + */ > + enum drm_hdmi_broadcast_rgb broadcast_rgb; > } hdmi; > }; > > @@ -1706,6 +1735,12 @@ struct drm_connector { > */ > struct drm_property *privacy_screen_hw_state_property; > > + /** > + * @broadcast_rgb_property: Connector property to set the > + * Broadcast RGB selection to output with. > + */ > + struct drm_property *broadcast_rgb_property; > + > #define DRM_CONNECTOR_POLL_HPD (1 << 0) > #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) > #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) > @@ -2026,6 +2061,7 @@ int drm_connector_attach_scaling_mode_property(struct > drm_connector *connector, > u32 scaling_mode_mask); > int drm_connector_attach_vrr_capable_property( > struct drm_connector *connector); > +int drm_connector_attach_broadcast_rgb_property(struct drm_connector > *connector); > int drm_connector_attach_colorspace_property(struct drm_connector > *connector); > int drm_connector_attach_hdr_output_metadata_property(struct drm_connector > *connector); > bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state > *old_state, > > -- > 2.43.0 >
