Hi,
Here's a series that creates some extra infrastructure specifically
targeted at HDMI controllers.
The idea behind this series came from a recent discussion on IRC during
which we discussed infoframes generation of i915 vs everything else.
Infoframes generation code still requires some decent boilerplate, with
each driver doing some variation of it.
In parallel, while working on vc4, we ended up converting a lot of i915
logic (mostly around format / bpc selection, and scrambler setup) to
apply on top of a driver that relies only on helpers.
While currently sitting in the vc4 driver, none of that logic actually
relies on any driver or hardware-specific behaviour.
The only missing piece to make it shareable are a bunch of extra
variables stored in a state (current bpc, format, RGB range selection,
etc.).
The initial implementation was relying on some generic subclass of
drm_connector to address HDMI connectors, with a bunch of helpers that
will take care of all the "HDMI Spec" related code. Scrambler setup is
missing at the moment but can easily be plugged in.
The feedback was that creating a connector subclass like was done for
writeback would prevent the adoption of those helpers since it couldn't
be used in all situations (like when the connector driver can implement
multiple output) and required more churn to cast between the
drm_connector and its subclass. The decision was thus to provide a set
of helper and to store the required variables in drm_connector and
drm_connector_state. This what has been implemented now.
Hans Verkuil also expressed interest in implementing a mechanism in v4l2
to retrieve infoframes from HDMI receiver and implementing a tool to
decode (and eventually check) infoframes. His current work on
edid-decode to enable that based on that series can be found here:
https://git.linuxtv.org/hverkuil/edid-decode.git/log/?h=hverkuil
And some more context here:
https://lore.kernel.org/dri-devel/[email protected]/
This series thus leverages the infoframe generation code to expose it
through debugfs.
I also used the occasion to unit-test everything but the infoframe
generation, which can come later once I get a proper understanding of
what the infoframe are supposed to look like. This required to add some
extra kunit helpers and infrastructure to have multiple EDIDs and allow
each test to run with a particular set of capabilities.
This entire series has been tested on a Pi4, passes all its unittests
(125 new tests), and has only been build-tested for sunxi and rockchip.
Let me know what you think,
Maxime
Signed-off-by: Maxime Ripard <[email protected]>
---
Changes in v5:
- Dropped the connector init arg checking patch, and the related kunit
tests
- Dropped HDMI Vendor infoframes in rockchip inno_hdmi
- Fixed the build warnings
- Link to v4: https://lore.kernel.org/r/[email protected]
Changes in v4:
- Create unit tests for everything but infoframes
- Fix a number of bugs identified by the unit tests
- Rename DRM (Dynamic Range and Mastering) infoframe file to HDR_DRM
- Drop RFC status
- Link to v3: https://lore.kernel.org/r/[email protected]
Changes in v3:
- Made sure the series work on the RaspberryPi4
- Handle YUV420 in the char clock rate computation
- Use the maximum bpc value the connector allows at reset
- Expose the RGB Limited vs Full Range value in the connector state
instead of through a helper
- Fix Broadcast RGB documentation
- Add more debug logging
- Small fixes here and there
- Link to v2: https://lore.kernel.org/r/[email protected]
Changes in v2:
- Change from a subclass to a set of helpers for drm_connector and
drm_connector state
- Don't assume that all drivers support RGB, YUV420 and YUV422 but make
them provide a bitfield instead.
- Don't assume that all drivers support the Broadcast RGB property but
make them call the registration helper.
- Document the Broacast RGB property
- Convert the inno_hdmi and sun4i_hdmi driver.
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Maxime Ripard (44):
drm/tests: helpers: Include missing drm_drv header
drm/tests: helpers: Add atomic helpers
drm/tests: Add helper to create mock plane
drm/tests: Add helper to create mock crtc
drm/tests: connector: Add tests for drmm_connector_init
drm/connector: Introduce an HDMI connector initialization function
drm/connector: hdmi: Create an HDMI sub-state
drm/connector: hdmi: Add Broadcast RGB property
drm/connector: hdmi: Add RGB Quantization Range to the connector state
drm/connector: hdmi: Add output BPC to the connector state
drm/connector: hdmi: Add support for output format
drm/connector: hdmi: Add HDMI compute clock helper
drm/connector: hdmi: Calculate TMDS character rate
drm/connector: hdmi: Add custom hook to filter TMDS character rate
drm/connector: hdmi: Compute bpc and format automatically
drm/connector: hdmi: Add Infoframes generation
drm/connector: hdmi: Create Infoframe DebugFS entries
drm/vc4: hdmi: Create destroy state implementation
drm/vc4: hdmi: Switch to HDMI connector
drm/vc4: tests: Remove vc4_dummy_plane structure
drm/vc4: tests: Convert to plane creation helper
drm/rockchip: inno_hdmi: Remove useless mode_fixup
drm/rockchip: inno_hdmi: Remove useless copy of drm_display_mode
drm/rockchip: inno_hdmi: Switch encoder hooks to atomic
drm/rockchip: inno_hdmi: Get rid of mode_set
drm/rockchip: inno_hdmi: no need to store vic
drm/rockchip: inno_hdmi: Remove unneeded has audio flag
drm/rockchip: inno_hdmi: Remove useless input format
drm/rockchip: inno_hdmi: Remove useless output format
drm/rockchip: inno_hdmi: Remove useless colorimetry
drm/rockchip: inno_hdmi: Remove useless enum
drm/rockchip: inno_hdmi: Remove tmds rate from structure
drm/rockchip: inno_hdmi: Remove useless coeff_csc matrix
drm/rockchip: inno_hdmi: Remove useless mode_valid
drm/rockchip: inno_hdmi: Drop HDMI Vendor Infoframe support
drm/rockchip: inno_hdmi: Move infoframe disable to separate function
drm/rockchip: inno_hdmi: Switch to infoframe type
drm/rockchip: inno_hdmi: Remove unused drm device pointer
drm/rockchip: inno_hdmi: Switch to HDMI connector
drm/sun4i: hdmi: Convert encoder to atomic
drm/sun4i: hdmi: Move mode_set into enable
drm/sun4i: hdmi: Switch to container_of_const
drm/sun4i: hdmi: Consolidate atomic_check and mode_valid
drm/sun4i: hdmi: Switch to HDMI connector
Documentation/gpu/kms-properties.csv | 1 -
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/drm_atomic.c | 11 +
drivers/gpu/drm/drm_atomic_state_helper.c | 659 ++++++++
drivers/gpu/drm/drm_atomic_uapi.c | 4 +
drivers/gpu/drm/drm_connector.c | 243 +++
drivers/gpu/drm/drm_debugfs.c | 110 ++
drivers/gpu/drm/rockchip/inno_hdmi.c | 346 +---
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 203 ++-
drivers/gpu/drm/tests/Makefile | 1 +
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 1728 ++++++++++++++++++++
drivers/gpu/drm/tests/drm_connector_test.c | 1227 +++++++++++++-
drivers/gpu/drm/tests/drm_kunit_edid.h | 482 ++++++
drivers/gpu/drm/tests/drm_kunit_helpers.c | 150 ++
drivers/gpu/drm/vc4/tests/vc4_mock.c | 6 +-
drivers/gpu/drm/vc4/tests/vc4_mock.h | 9 +-
drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 44 +-
drivers/gpu/drm/vc4/vc4_hdmi.c | 626 +------
drivers/gpu/drm/vc4/vc4_hdmi.h | 44 +-
drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 6 +-
include/drm/drm_atomic_state_helper.h | 12 +
include/drm/drm_connector.h | 257 +++
include/drm/drm_kunit_helpers.h | 23 +
23 files changed, 5202 insertions(+), 991 deletions(-)
---
base-commit: 90d50b8d85834e73536fdccd5aa913b30494fef0
change-id: 20230814-kms-hdmi-connector-state-616787e67927
Best regards,
--
Maxime Ripard <[email protected]>
We have a few functions declared in our kunit helpers header, some of
them dereferencing the struct drm_driver.
However, we don't include the drm_drv.h header file defining that
structure, leading to compilation errors if we don't include both
headers.
Fixes: d98780310719 ("drm/tests: helpers: Allow to pass a custom drm_driver")
Signed-off-by: Maxime Ripard <[email protected]>
---
include/drm/drm_kunit_helpers.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h
index ba483c87f0e7..3ae19892229d 100644
--- a/include/drm/drm_kunit_helpers.h
+++ b/include/drm/drm_kunit_helpers.h
@@ -3,6 +3,8 @@
#ifndef DRM_KUNIT_HELPERS_H_
#define DRM_KUNIT_HELPERS_H_
+#include <drm/drm_drv.h>
+
#include <linux/device.h>
#include <kunit/test.h>
--
2.43.0
We're going to need a full-blown, functional, KMS device to test more
components of the atomic modesetting infrastructure.
Let's add a new helper to create a dumb, mocked, primary plane. By
default, it will create a linear XRGB8888 plane, using the default
helpers.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/tests/drm_kunit_helpers.c | 85 +++++++++++++++++++++++++++++++
include/drm/drm_kunit_helpers.h | 11 ++++
2 files changed, 96 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index 272a3ba46d60..e0778a7ec260 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -3,6 +3,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>
@@ -236,5 +237,89 @@ drm_kunit_helper_atomic_state_alloc(struct kunit *test,
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc);
+static const uint32_t default_plane_formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
+static const uint64_t default_plane_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
+static const struct drm_plane_helper_funcs default_plane_helper_funcs = {
+};
+
+static const struct drm_plane_funcs default_plane_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .reset = drm_atomic_helper_plane_reset,
+};
+
+/**
+ * drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test
+ * @test: The test context object
+ * @drm: The device to alloc the plane for
+ * @funcs: Callbacks for the new plane. Optional.
+ * @helper_funcs: Helpers callbacks for the new plane. Optional.
+ * @formats: array of supported formats (DRM_FORMAT\_\*). Optional.
+ * @num_formats: number of elements in @formats
+ * @modifiers: array of struct drm_format modifiers terminated by
+ * DRM_FORMAT_MOD_INVALID. Optional.
+ *
+ * This allocates and initializes a mock struct &drm_plane meant to be
+ * part of a mock device for a KUnit test.
+ *
+ * Resources will be cleaned up automatically.
+ *
+ * @funcs will default to the default helpers implementations.
+ * @helper_funcs will default to an empty implementation. @formats will
+ * default to XRGB8888 only. @modifiers will default to a linear
+ * modifier only.
+ *
+ * Returns:
+ * A pointer to the new plane, or an ERR_PTR() otherwise.
+ */
+struct drm_plane *
+drm_kunit_helper_create_primary_plane(struct kunit *test,
+ struct drm_device *drm,
+ const struct drm_plane_funcs *funcs,
+ const struct drm_plane_helper_funcs *helper_funcs,
+ const uint32_t *formats,
+ unsigned int num_formats,
+ const uint64_t *modifiers)
+{
+ struct drm_plane *plane;
+
+ if (!funcs)
+ funcs = &default_plane_funcs;
+
+ if (!helper_funcs)
+ helper_funcs = &default_plane_helper_funcs;
+
+ if (!formats || !num_formats) {
+ formats = default_plane_formats;
+ num_formats = ARRAY_SIZE(default_plane_formats);
+ }
+
+ if (!modifiers)
+ modifiers = default_plane_modifiers;
+
+ plane = __drmm_universal_plane_alloc(drm,
+ sizeof(struct drm_plane), 0,
+ 0,
+ funcs,
+ formats,
+ num_formats,
+ default_plane_modifiers,
+ DRM_PLANE_TYPE_PRIMARY,
+ NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
+
+ drm_plane_helper_add(plane, helper_funcs);
+
+ return plane;
+}
+EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);
+
MODULE_AUTHOR("Maxime Ripard <[email protected]>");
MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h
index 3ae19892229d..38667d624aa8 100644
--- a/include/drm/drm_kunit_helpers.h
+++ b/include/drm/drm_kunit_helpers.h
@@ -10,6 +10,8 @@
#include <kunit/test.h>
struct drm_device;
+struct drm_plane_funcs;
+struct drm_plane_helper_funcs;
struct kunit;
struct device *drm_kunit_helper_alloc_device(struct kunit *test);
@@ -99,4 +101,13 @@ drm_kunit_helper_atomic_state_alloc(struct kunit *test,
struct drm_device *drm,
struct drm_modeset_acquire_ctx *ctx);
+struct drm_plane *
+drm_kunit_helper_create_primary_plane(struct kunit *test,
+ struct drm_device *drm,
+ const struct drm_plane_funcs *funcs,
+ const struct drm_plane_helper_funcs *helper_funcs,
+ const uint32_t *formats,
+ unsigned int num_formats,
+ const uint64_t *modifiers);
+
#endif // DRM_KUNIT_HELPERS_H_
--
2.43.0
We're going to need a full-blown, functional, KMS device to test more
components of the atomic modesetting infrastructure.
Let's add a new helper to create a dumb, mocked, CRTC. By default it
will create a CRTC relying only on the default helpers, but drivers are
free to deviate from that.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/tests/drm_kunit_helpers.c | 62 +++++++++++++++++++++++++++++++
include/drm/drm_kunit_helpers.h | 10 +++++
2 files changed, 72 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index e0778a7ec260..ca513235b5e2 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -321,5 +321,67 @@ drm_kunit_helper_create_primary_plane(struct kunit *test,
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);
+static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {
+};
+
+static const struct drm_crtc_funcs default_crtc_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .reset = drm_atomic_helper_crtc_reset,
+};
+
+/**
+ * drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test
+ * @test: The test context object
+ * @drm: The device to alloc the plane for
+ * @primary: Primary plane for CRTC
+ * @cursor: Cursor plane for CRTC. Optional.
+ * @funcs: Callbacks for the new plane. Optional.
+ * @helper_funcs: Helpers callbacks for the new plane. Optional.
+ *
+ * This allocates and initializes a mock struct &drm_crtc meant to be
+ * part of a mock device for a KUnit test.
+ *
+ * Resources will be cleaned up automatically.
+ *
+ * @funcs will default to the default helpers implementations.
+ * @helper_funcs will default to an empty implementation.
+ *
+ * Returns:
+ * A pointer to the new CRTC, or an ERR_PTR() otherwise.
+ */
+struct drm_crtc *
+drm_kunit_helper_create_crtc(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_plane *primary,
+ struct drm_plane *cursor,
+ const struct drm_crtc_funcs *funcs,
+ const struct drm_crtc_helper_funcs *helper_funcs)
+{
+ struct drm_crtc *crtc;
+ int ret;
+
+ if (!funcs)
+ funcs = &default_crtc_funcs;
+
+ if (!helper_funcs)
+ helper_funcs = &default_crtc_helper_funcs;
+
+ crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, crtc);
+
+ ret = drmm_crtc_init_with_planes(drm, crtc,
+ primary,
+ cursor,
+ funcs,
+ NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_crtc_helper_add(crtc, helper_funcs);
+
+ return crtc;
+}
+EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc);
+
MODULE_AUTHOR("Maxime Ripard <[email protected]>");
MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h
index 38667d624aa8..6e99627edf45 100644
--- a/include/drm/drm_kunit_helpers.h
+++ b/include/drm/drm_kunit_helpers.h
@@ -9,6 +9,8 @@
#include <kunit/test.h>
+struct drm_crtc_funcs;
+struct drm_crtc_helper_funcs;
struct drm_device;
struct drm_plane_funcs;
struct drm_plane_helper_funcs;
@@ -110,4 +112,12 @@ drm_kunit_helper_create_primary_plane(struct kunit *test,
unsigned int num_formats,
const uint64_t *modifiers);
+struct drm_crtc *
+drm_kunit_helper_create_crtc(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_plane *primary,
+ struct drm_plane *cursor,
+ const struct drm_crtc_funcs *funcs,
+ const struct drm_crtc_helper_funcs *helper_funcs);
+
#endif // DRM_KUNIT_HELPERS_H_
--
2.43.0
drmm_connector_init is the preferred function to initialize a
drm_connector structure. Let's add a bunch of unit tests for it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/tests/drm_connector_test.c | 170 ++++++++++++++++++++++++++++-
1 file changed, 169 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index c66aa2dc8d9d..a268847be8d1 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -3,10 +3,175 @@
* Kunit test for drm_modes functions
*/
+#include <linux/i2c.h>
+
+#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_kunit_helpers.h>
#include <kunit/test.h>
+struct drm_connector_init_priv {
+ struct drm_device drm;
+ struct drm_connector connector;
+ struct i2c_adapter ddc;
+};
+
+static const struct drm_connector_funcs dummy_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+static int dummy_ddc_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ return num;
+}
+
+static u32 dummy_ddc_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm dummy_ddc_algorithm = {
+ .master_xfer = dummy_ddc_xfer,
+ .functionality = dummy_ddc_func,
+};
+
+static void i2c_del_adapter_wrapper(void *ptr)
+{
+ struct i2c_adapter *adap = ptr;
+
+ i2c_del_adapter(adap);
+}
+
+static int drm_test_connector_init(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv;
+ 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_connector_init_priv, drm,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ strscpy(priv->ddc.name, "dummy-connector-ddc", sizeof(priv->ddc.name));
+ priv->ddc.owner = THIS_MODULE;
+ priv->ddc.algo = &dummy_ddc_algorithm;
+ priv->ddc.dev.parent = dev;
+
+ ret = i2c_add_adapter(&priv->ddc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_add_action_or_reset(test, i2c_del_adapter_wrapper, &priv->ddc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ test->priv = priv;
+ return 0;
+}
+
+/*
+ * Test that the registration of a bog standard connector works as
+ * expected and doesn't report any error.
+ */
+static void drm_test_drmm_connector_init(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector without a DDC adapter
+ * doesn't report any error.
+ */
+static void drm_test_drmm_connector_init_null_ddc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ NULL);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector succeeds for all possible
+ * connector types.
+ */
+static void drm_test_drmm_connector_init_type_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ unsigned int connector_type = *(unsigned int *)test->param_value;
+ int ret;
+
+ ret = drmm_connector_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ connector_type,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static const unsigned int drm_connector_init_type_valid_tests[] = {
+ DRM_MODE_CONNECTOR_Unknown,
+ DRM_MODE_CONNECTOR_VGA,
+ DRM_MODE_CONNECTOR_DVII,
+ DRM_MODE_CONNECTOR_DVID,
+ DRM_MODE_CONNECTOR_DVIA,
+ DRM_MODE_CONNECTOR_Composite,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ DRM_MODE_CONNECTOR_LVDS,
+ DRM_MODE_CONNECTOR_Component,
+ DRM_MODE_CONNECTOR_9PinDIN,
+ DRM_MODE_CONNECTOR_DisplayPort,
+ DRM_MODE_CONNECTOR_HDMIA,
+ DRM_MODE_CONNECTOR_HDMIB,
+ DRM_MODE_CONNECTOR_TV,
+ DRM_MODE_CONNECTOR_eDP,
+ DRM_MODE_CONNECTOR_VIRTUAL,
+ DRM_MODE_CONNECTOR_DSI,
+ DRM_MODE_CONNECTOR_DPI,
+ DRM_MODE_CONNECTOR_WRITEBACK,
+ DRM_MODE_CONNECTOR_SPI,
+ DRM_MODE_CONNECTOR_USB,
+};
+
+static void drm_connector_init_type_desc(const unsigned int *type, char *desc)
+{
+ sprintf(desc, "%s", drm_get_connector_type_name(*type));
+}
+
+KUNIT_ARRAY_PARAM(drm_connector_init_type_valid,
+ drm_connector_init_type_valid_tests,
+ drm_connector_init_type_desc);
+
+static struct kunit_case drmm_connector_init_tests[] = {
+ KUNIT_CASE(drm_test_drmm_connector_init),
+ KUNIT_CASE(drm_test_drmm_connector_init_null_ddc),
+ KUNIT_CASE_PARAM(drm_test_drmm_connector_init_type_valid,
+ drm_connector_init_type_valid_gen_params),
+ { }
+};
+
+static struct kunit_suite drmm_connector_init_test_suite = {
+ .name = "drmm_connector_init",
+ .init = drm_test_connector_init,
+ .test_cases = drmm_connector_init_tests,
+};
+
struct drm_get_tv_mode_from_name_test {
const char *name;
enum drm_connector_tv_mode expected_mode;
@@ -70,7 +235,10 @@ static struct kunit_suite drm_get_tv_mode_from_name_test_suite = {
.test_cases = drm_get_tv_mode_from_name_tests,
};
-kunit_test_suite(drm_get_tv_mode_from_name_test_suite);
+kunit_test_suites(
+ &drmm_connector_init_test_suite,
+ &drm_get_tv_mode_from_name_test_suite
+);
MODULE_AUTHOR("Maxime Ripard <[email protected]>");
MODULE_LICENSE("GPL");
--
2.43.0
The mock device we were creating was missing any of the driver-wide
helpers. That was fine before since we weren't testing the atomic state
path, but we're going to start, so let's use the default
implementations.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/tests/drm_kunit_helpers.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index bccb33b900f3..272a3ba46d60 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>
@@ -13,6 +14,8 @@
#define KUNIT_DEVICE_NAME "drm-kunit-mock-device"
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
};
static int fake_probe(struct platform_device *pdev)
--
2.43.0
A lot of the various HDMI drivers duplicate some logic that depends on
the HDMI spec itself and not really a particular hardware
implementation.
Output BPC or format selection, infoframe generation are good examples
of such areas.
This creates a lot of boilerplate, with a lot of variations, which makes
it hard for userspace to rely on, and makes it difficult to get it right
for drivers.
In the next patches, we'll add a lot of infrastructure around the
drm_connector and drm_connector_state structures, which will allow to
abstract away the duplicated logic. This infrastructure comes with a few
requirements though, and thus we need a new initialization function.
Hopefully, this will make drivers simpler to handle, and their behaviour
more consistent.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_connector.c | 39 +++++++++
drivers/gpu/drm/tests/drm_connector_test.c | 123 +++++++++++++++++++++++++++++
include/drm/drm_connector.h | 5 ++
3 files changed, 167 insertions(+)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index b0516505f7ae..d9961cce8245 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -452,6 +452,45 @@ int drmm_connector_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drmm_connector_init);
+/**
+ * drmm_connector_hdmi_init - Init a preallocated HDMI connector
+ * @dev: DRM device
+ * @connector: A pointer to the HDMI connector to init
+ * @funcs: callbacks for this connector
+ * @connector_type: user visible type of the connector
+ * @ddc: optional pointer to the associated ddc adapter
+ *
+ * Initialises a preallocated HDMI connector. Connectors can be
+ * subclassed as part of driver connector objects.
+ *
+ * Cleanup is automatically handled with a call to
+ * drm_connector_cleanup() in a DRM-managed action.
+ *
+ * The connector structure should be allocated with drmm_kzalloc().
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_connector_hdmi_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type,
+ struct i2c_adapter *ddc)
+{
+ int ret;
+
+ if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+ connector_type == DRM_MODE_CONNECTOR_HDMIB))
+ return -EINVAL;
+
+ ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(drmm_connector_hdmi_init);
+
/**
* drm_connector_attach_edid_property - attach edid property.
* @connector: the connector
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index a268847be8d1..8f070cacab3b 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -172,6 +172,128 @@ static struct kunit_suite drmm_connector_init_test_suite = {
.test_cases = drmm_connector_init_tests,
};
+/*
+ * Test that the registration of a bog standard connector works as
+ * expected and doesn't report any error.
+ */
+static void drm_test_connector_hdmi_init_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector without a DDC adapter
+ * doesn't report any error.
+ */
+static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ NULL);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the registration of an HDMI connector with an HDMI
+ * connector type succeeds.
+ */
+static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ unsigned int connector_type = *(unsigned int *)test->param_value;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ connector_type,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static const unsigned int drm_connector_hdmi_init_type_valid_tests[] = {
+ DRM_MODE_CONNECTOR_HDMIA,
+ DRM_MODE_CONNECTOR_HDMIB,
+};
+
+static void drm_connector_hdmi_init_type_desc(const unsigned int *type, char *desc)
+{
+ sprintf(desc, "%s", drm_get_connector_type_name(*type));
+}
+
+KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_valid,
+ drm_connector_hdmi_init_type_valid_tests,
+ drm_connector_hdmi_init_type_desc);
+
+/*
+ * Test that the registration of an HDMI connector with an !HDMI
+ * connector type fails.
+ */
+static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ unsigned int connector_type = *(unsigned int *)test->param_value;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ connector_type,
+ &priv->ddc);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+static const unsigned int drm_connector_hdmi_init_type_invalid_tests[] = {
+ DRM_MODE_CONNECTOR_Unknown,
+ DRM_MODE_CONNECTOR_VGA,
+ DRM_MODE_CONNECTOR_DVII,
+ DRM_MODE_CONNECTOR_DVID,
+ DRM_MODE_CONNECTOR_DVIA,
+ DRM_MODE_CONNECTOR_Composite,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ DRM_MODE_CONNECTOR_LVDS,
+ DRM_MODE_CONNECTOR_Component,
+ DRM_MODE_CONNECTOR_9PinDIN,
+ DRM_MODE_CONNECTOR_DisplayPort,
+ DRM_MODE_CONNECTOR_TV,
+ DRM_MODE_CONNECTOR_eDP,
+ DRM_MODE_CONNECTOR_VIRTUAL,
+ DRM_MODE_CONNECTOR_DSI,
+ DRM_MODE_CONNECTOR_DPI,
+ DRM_MODE_CONNECTOR_WRITEBACK,
+ DRM_MODE_CONNECTOR_SPI,
+ DRM_MODE_CONNECTOR_USB,
+};
+
+KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid,
+ drm_connector_hdmi_init_type_invalid_tests,
+ drm_connector_hdmi_init_type_desc);
+
+static struct kunit_case drmm_connector_hdmi_init_tests[] = {
+ KUNIT_CASE(drm_test_connector_hdmi_init_valid),
+ KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
+ KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
+ drm_connector_hdmi_init_type_valid_gen_params),
+ KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_invalid,
+ drm_connector_hdmi_init_type_invalid_gen_params),
+ { }
+};
+
+static struct kunit_suite drmm_connector_hdmi_init_test_suite = {
+ .name = "drmm_connector_hdmi_init",
+ .init = drm_test_connector_init,
+ .test_cases = drmm_connector_hdmi_init_tests,
+};
+
struct drm_get_tv_mode_from_name_test {
const char *name;
enum drm_connector_tv_mode expected_mode;
@@ -236,6 +358,7 @@ static struct kunit_suite drm_get_tv_mode_from_name_test_suite = {
};
kunit_test_suites(
+ &drmm_connector_hdmi_init_test_suite,
&drmm_connector_init_test_suite,
&drm_get_tv_mode_from_name_test_suite
);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index fe88d7fc6b8f..4491c4c2fb6e 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1904,6 +1904,11 @@ int drmm_connector_init(struct drm_device *dev,
const struct drm_connector_funcs *funcs,
int connector_type,
struct i2c_adapter *ddc);
+int drmm_connector_hdmi_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type,
+ struct i2c_adapter *ddc);
void drm_connector_attach_edid_property(struct drm_connector *connector);
int drm_connector_register(struct drm_connector *connector);
void drm_connector_unregister(struct drm_connector *connector);
--
2.43.0
The next features we will need to share across drivers will need to
store some parameters for drivers to use, such as the selected output
format.
Let's create a new connector sub-state dedicated to HDMI controllers,
that will eventually store everything we need.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic_state_helper.c | 35 +++++++++++++++++++++++++++++++
include/drm/drm_atomic_state_helper.h | 4 ++++
include/drm/drm_connector.h | 7 +++++++
3 files changed, 46 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 54975de44a0e..e69c0cc1c6da 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -570,6 +570,22 @@ void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset);
+/**
+ * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources
+ * @connector: DRM connector
+ * @new_state: connector state to reset
+ *
+ * Initializes all HDMI resources from a @drm_connector_state without
+ * actually allocating it. This is useful for HDMI drivers, in
+ * combination with __drm_atomic_helper_connector_reset() or
+ * drm_atomic_helper_connector_reset().
+ */
+void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
+ struct drm_connector_state *new_state)
+{
+}
+EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
+
/**
* drm_atomic_helper_connector_tv_check - Validate an analog TV connector state
* @connector: DRM Connector
@@ -619,6 +635,25 @@ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check);
+/**
+ * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
+ * @connector: DRM Connector
+ * @state: the DRM State object
+ *
+ * Provides a default connector state check handler for HDMI connectors.
+ * Checks that a desired connector update is valid, and updates various
+ * fields of derived state.
+ *
+ * RETURNS:
+ * Zero on success, or an errno code otherwise.
+ */
+int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ return 0;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check);
+
/**
* __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
* @connector: connector object
diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h
index b9740edb2658..d59d2b3aef9a 100644
--- a/include/drm/drm_atomic_state_helper.h
+++ b/include/drm/drm_atomic_state_helper.h
@@ -71,7 +71,11 @@ void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_
void __drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state);
void drm_atomic_helper_connector_reset(struct drm_connector *connector);
+void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
+ struct drm_connector_state *new_state);
void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector);
+int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
+ struct drm_atomic_state *state);
int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
struct drm_atomic_state *state);
void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connector);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 4491c4c2fb6e..000a2a156619 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1031,6 +1031,13 @@ struct drm_connector_state {
* DRM blob property for HDR output metadata
*/
struct drm_property_blob *hdr_output_metadata;
+
+ /**
+ * @hdmi: HDMI-related variable and properties. Filled by
+ * @drm_atomic_helper_connector_hdmi_check().
+ */
+ struct {
+ } hdmi;
};
/**
--
2.43.0
Just like BPC, we'll add support for automatic selection of the output
format for HDMI connectors.
Let's add the needed defaults and fields for now.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic.c | 2 +
drivers/gpu/drm/drm_atomic_state_helper.c | 3 +-
drivers/gpu/drm/drm_connector.c | 31 ++++++
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 90 ++++++++++++++---
drivers/gpu/drm/tests/drm_connector_test.c | 109 ++++++++++++++++++++-
include/drm/drm_connector.h | 19 ++++
6 files changed, 238 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 76c63ed04af4..7aaa2a4d70d9 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1148,6 +1148,8 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb));
drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n');
drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
+ drm_printf(p, "\toutput_format=%s\n",
+ drm_hdmi_connector_get_output_format_name(state->hdmi.output_format));
}
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 883bdc0349c0..92e1b087c3d0 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -705,7 +705,8 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
- old_state->hdmi.output_bpc != new_state->hdmi.output_bpc) {
+ old_state->hdmi.output_bpc != new_state->hdmi.output_bpc ||
+ old_state->hdmi.output_format != new_state->hdmi.output_format) {
struct drm_crtc *crtc = new_state->crtc;
struct drm_crtc_state *crtc_state;
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 139ac3d8160c..a72f38b6dbc8 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -459,6 +459,7 @@ EXPORT_SYMBOL(drmm_connector_init);
* @funcs: callbacks for this connector
* @connector_type: user visible type of the connector
* @ddc: optional pointer to the associated ddc adapter
+ * @supported_formats: Bitmask of @hdmi_colorspace listing supported output formats
* @max_bpc: Maximum bits per char the HDMI connector supports
*
* Initialises a preallocated HDMI connector. Connectors can be
@@ -477,6 +478,7 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
const struct drm_connector_funcs *funcs,
int connector_type,
struct i2c_adapter *ddc,
+ unsigned long supported_formats,
unsigned int max_bpc)
{
int ret;
@@ -485,6 +487,9 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
connector_type == DRM_MODE_CONNECTOR_HDMIB))
return -EINVAL;
+ if (!supported_formats || !(supported_formats & BIT(HDMI_COLORSPACE_RGB)))
+ return -EINVAL;
+
if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
return -EINVAL;
@@ -492,6 +497,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (ret)
return ret;
+ connector->hdmi.supported_formats = supported_formats;
+
/*
* drm_connector_attach_max_bpc_property() requires the
* connector to have a state.
@@ -1224,6 +1231,30 @@ drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_
}
EXPORT_SYMBOL(drm_hdmi_connector_get_broadcast_rgb_name);
+static const char * const output_format_str[] = {
+ [HDMI_COLORSPACE_RGB] = "RGB",
+ [HDMI_COLORSPACE_YUV420] = "YUV 4:2:0",
+ [HDMI_COLORSPACE_YUV422] = "YUV 4:2:2",
+ [HDMI_COLORSPACE_YUV444] = "YUV 4:4:4",
+};
+
+/*
+ * drm_hdmi_connector_get_output_format_name() - Return a string for HDMI connector output format
+ * @fmt: Output format to compute name of
+ *
+ * Returns: the name of the output format, or NULL if the type is not
+ * valid.
+ */
+const char *
+drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt)
+{
+ if (fmt >= ARRAY_SIZE(output_format_str))
+ return NULL;
+
+ return output_format_str[fmt];
+}
+EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name);
+
/**
* DOC: standard connector properties
*
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index a1b0e6914cf8..4e2ec436987b 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -147,6 +147,7 @@ static const struct drm_connector_funcs dummy_connector_funcs = {
static
struct drm_atomic_helper_connector_hdmi_priv *
drm_atomic_helper_connector_hdmi_init(struct kunit *test,
+ unsigned int formats,
unsigned int max_bpc)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
@@ -190,6 +191,7 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test,
&dummy_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
NULL,
+ formats,
max_bpc);
KUNIT_ASSERT_EQ(test, ret, 0);
@@ -225,7 +227,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
@@ -286,7 +290,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
@@ -347,7 +353,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -401,7 +409,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -454,7 +464,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -510,7 +522,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -565,7 +579,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -621,7 +637,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -678,7 +696,9 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 10);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 10);
KUNIT_ASSERT_NOT_NULL(test, priv);
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
@@ -745,7 +765,9 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test, 10);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 10);
KUNIT_ASSERT_NOT_NULL(test, priv);
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
@@ -802,6 +824,15 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed),
KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed),
+ /*
+ * TODO: We should have tests to check that a change in the
+ * format triggers a CRTC mode change just like we do for the
+ * RGB Quantization and BPC.
+ *
+ * However, we don't have any way to control which format gets
+ * picked up aside from changing the BPC or mode which would
+ * already trigger a mode change.
+ */
{ }
};
@@ -820,7 +851,9 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -840,7 +873,9 @@ static void drm_test_check_bpc_8_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_atomic_helper_connector_hdmi_init(test, 8);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -862,7 +897,9 @@ static void drm_test_check_bpc_10_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_atomic_helper_connector_hdmi_init(test, 10);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 10);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -884,7 +921,9 @@ static void drm_test_check_bpc_12_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_atomic_helper_connector_hdmi_init(test, 12);
+ priv = drm_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 12);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -894,11 +933,34 @@ static void drm_test_check_bpc_12_value(struct kunit *test)
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0);
}
+/*
+ * Test that the value of the output format property out of reset is set
+ * to RGB, even if the driver supports more than that.
+ */
+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_atomic_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, HDMI_COLORSPACE_RGB);
+}
+
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),
{ }
};
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index fef7d53d34d4..fa6fe8084107 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -187,6 +187,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -204,6 +205,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
NULL,
+ BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -221,6 +223,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
9);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -238,6 +241,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
0);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -259,6 +263,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -292,6 +297,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
10);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -325,6 +331,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
12);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -341,6 +348,42 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
}
+/*
+ * Test that the registration of an HDMI connector with no supported
+ * format fails.
+ */
+static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ 0,
+ 8);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of an HDMI connector not listing RGB as a
+ * supported format fails.
+ */
+static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_YUV422),
+ 8);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
/*
* Test that the registration of an HDMI connector with an HDMI
* connector type succeeds.
@@ -355,6 +398,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
&dummy_funcs,
connector_type,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -387,6 +431,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
&dummy_funcs,
connector_type,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -424,6 +469,8 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE(drm_test_connector_hdmi_init_bpc_12),
KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid),
KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null),
+ KUNIT_CASE(drm_test_connector_hdmi_init_formats_empty),
+ KUNIT_CASE(drm_test_connector_hdmi_init_formats_no_rgb),
KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
drm_connector_hdmi_init_type_valid_gen_params),
@@ -558,6 +605,64 @@ static struct kunit_suite drm_hdmi_connector_get_broadcast_rgb_name_test_suite =
.test_cases = drm_hdmi_connector_get_broadcast_rgb_name_tests,
};
+struct drm_hdmi_connector_get_output_format_name_test {
+ unsigned int kind;
+ const char *expected_name;
+};
+
+#define OUTPUT_FORMAT_TEST(_kind, _name) \
+ { \
+ .kind = _kind, \
+ .expected_name = _name, \
+ }
+
+static void drm_test_drm_hdmi_connector_get_output_format_name(struct kunit *test)
+{
+ const struct drm_hdmi_connector_get_output_format_name_test *params =
+ test->param_value;
+
+ KUNIT_EXPECT_STREQ(test,
+ drm_hdmi_connector_get_output_format_name(params->kind),
+ params->expected_name);
+}
+
+static const
+struct drm_hdmi_connector_get_output_format_name_test
+drm_hdmi_connector_get_output_format_name_valid_tests[] = {
+ OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_RGB, "RGB"),
+ OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV420, "YUV 4:2:0"),
+ OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV422, "YUV 4:2:2"),
+ OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV444, "YUV 4:4:4"),
+};
+
+static void
+drm_hdmi_connector_get_output_format_name_valid_desc(const struct drm_hdmi_connector_get_output_format_name_test *t,
+ char *desc)
+{
+ sprintf(desc, "%s", t->expected_name);
+}
+
+KUNIT_ARRAY_PARAM(drm_hdmi_connector_get_output_format_name_valid,
+ drm_hdmi_connector_get_output_format_name_valid_tests,
+ drm_hdmi_connector_get_output_format_name_valid_desc);
+
+static void drm_test_drm_hdmi_connector_get_output_format_name_invalid(struct kunit *test)
+{
+ KUNIT_EXPECT_NULL(test, drm_hdmi_connector_get_output_format_name(4));
+};
+
+static struct kunit_case drm_hdmi_connector_get_output_format_name_tests[] = {
+ KUNIT_CASE_PARAM(drm_test_drm_hdmi_connector_get_output_format_name,
+ drm_hdmi_connector_get_output_format_name_valid_gen_params),
+ KUNIT_CASE(drm_test_drm_hdmi_connector_get_output_format_name_invalid),
+ { }
+};
+
+static struct kunit_suite drm_hdmi_connector_get_output_format_name_test_suite = {
+ .name = "drm_hdmi_connector_get_output_format_name",
+ .test_cases = drm_hdmi_connector_get_output_format_name_tests,
+};
+
static void drm_test_drm_connector_attach_broadcast_rgb_property(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
@@ -590,6 +695,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
8);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -618,7 +724,8 @@ kunit_test_suites(
&drmm_connector_init_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
+ &drm_hdmi_connector_get_broadcast_rgb_name_test_suite,
+ &drm_hdmi_connector_get_output_format_name_test_suite
);
MODULE_AUTHOR("Maxime Ripard <[email protected]>");
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1b1b6aed04ee..74db5ce47e01 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -391,6 +391,8 @@ enum drm_hdmi_broadcast_rgb {
const char *
drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_rgb);
+const char *
+drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt);
/**
* struct drm_monitor_range_info - Panel's Monitor range in EDID for
@@ -1077,6 +1079,11 @@ struct drm_connector_state {
* @output_bpc: Bits per color channel to output.
*/
unsigned int output_bpc;
+
+ /**
+ * @output_format: Pixel format to output in.
+ */
+ enum hdmi_colorspace output_format;
} hdmi;
};
@@ -1944,6 +1951,17 @@ struct drm_connector {
/** @hdr_sink_metadata: HDR Metadata Information read from sink */
struct hdr_sink_metadata hdr_sink_metadata;
+
+ /**
+ * @hdmi: HDMI-related variable and properties.
+ */
+ struct {
+ /**
+ * @supported_formats: Bitmask of @hdmi_colorspace
+ * supported by the controller.
+ */
+ unsigned long supported_formats;
+ } hdmi;
};
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
@@ -1967,6 +1985,7 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
const struct drm_connector_funcs *funcs,
int connector_type,
struct i2c_adapter *ddc,
+ unsigned long supported_formats,
unsigned int max_bpc);
void drm_connector_attach_edid_property(struct drm_connector *connector);
int drm_connector_register(struct drm_connector *connector);
--
2.43.0
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.
Signed-off-by: Maxime Ripard <[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
HDMI controller drivers will need to figure out the RGB range they need
to configure based on a mode and property values. Let's expose that in
the HDMI connector state so drivers can just use that value.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic.c | 4 +-
drivers/gpu/drm/drm_atomic_state_helper.c | 44 +++
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 335 +++++++++++++++++++++
include/drm/drm_atomic_state_helper.h | 1 +
include/drm/drm_connector.h | 6 +
5 files changed, 389 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 1465a7f09a0b..e4ad53e64e07 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1143,9 +1143,11 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
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)
+ 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));
+ drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n');
+ }
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
if (state->writeback_job && state->writeback_job->fb)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 10d98620a358..4a7114978c47 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -31,6 +31,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
@@ -636,6 +637,47 @@ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check);
+static const struct drm_display_mode *
+connector_state_get_mode(const struct drm_connector_state *conn_state)
+{
+ struct drm_atomic_state *state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+
+ state = conn_state->state;
+ if (!state)
+ return NULL;
+
+ crtc = conn_state->crtc;
+ if (!crtc)
+ return NULL;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!crtc_state)
+ return NULL;
+
+ return &crtc_state->mode;
+}
+
+static bool hdmi_is_full_range(const struct drm_connector *connector,
+ const struct drm_connector_state *state)
+{
+ const struct drm_display_info *display = &connector->display_info;
+ const struct drm_display_mode *mode =
+ connector_state_get_mode(state);
+
+ if (state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_FULL)
+ return true;
+
+ if (state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
+ return false;
+
+ if (!display->is_hdmi)
+ return true;
+
+ return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false;
+}
+
/**
* drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
* @connector: DRM Connector
@@ -656,6 +698,8 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
struct drm_connector_state *new_state =
drm_atomic_get_new_connector_state(state, connector);
+ new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
+
if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb) {
struct drm_crtc *crtc = new_state->crtc;
struct drm_crtc_state *crtc_state;
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index 21e6f796ee13..7750c3d214a4 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -328,7 +328,342 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
}
+/*
+ * Test that for an HDMI connector, with an HDMI monitor, if the
+ * Broadcast RGB property is set to auto with a mode that isn't the
+ * VIC-1 mode, we will get a limited RGB Quantization Range.
+ */
+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_atomic_helper_connector_hdmi_init(test);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
+
+ 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);
+
+ 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_connector_state(state, conn);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range);
+}
+
+/*
+ * Test that for an HDMI connector, with an HDMI monitor, if the
+ * Broadcast RGB property is set to auto with a VIC-1 mode, we will get
+ * a full RGB Quantization Range.
+ */
+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_atomic_helper_connector_hdmi_init(test);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ mode = drm_display_mode_from_cea_vic(drm, 1);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, mode, 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);
+
+ 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_connector_state(state, conn);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_full_range);
+}
+
+/*
+ * Test that for an HDMI connector, with an HDMI monitor, if the
+ * Broadcast RGB property is set to full with a mode that isn't the
+ * VIC-1 mode, we will get a full RGB Quantization Range.
+ */
+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_atomic_helper_connector_hdmi_init(test);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
+
+ 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);
+
+ 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_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_TRUE(test, conn_state->hdmi.is_full_range);
+}
+
+/*
+ * Test that for an HDMI connector, with an HDMI monitor, if the
+ * Broadcast RGB property is set to full with a VIC-1 mode, we will get
+ * a full RGB Quantization Range.
+ */
+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_atomic_helper_connector_hdmi_init(test);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ mode = drm_display_mode_from_cea_vic(drm, 1);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, mode, 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);
+
+ 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_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_TRUE(test, conn_state->hdmi.is_full_range);
+}
+
+/*
+ * Test that for an HDMI connector, with an HDMI monitor, if the
+ * Broadcast RGB property is set to limited with a mode that isn't the
+ * VIC-1 mode, we will get a limited RGB Quantization Range.
+ */
+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_atomic_helper_connector_hdmi_init(test);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
+
+ 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);
+
+ 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_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_FALSE(test, conn_state->hdmi.is_full_range);
+}
+
+/*
+ * Test that for an HDMI connector, with an HDMI monitor, if the
+ * Broadcast RGB property is set to limited with a VIC-1 mode, we will
+ * get a limited RGB Quantization Range.
+ */
+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_atomic_helper_connector_hdmi_init(test);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ mode = drm_display_mode_from_cea_vic(drm, 1);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, mode, 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);
+
+ 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_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_FALSE(test, conn_state->hdmi.is_full_range);
+}
+
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(drm_test_check_broadcast_rgb_crtc_mode_changed),
KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
{ }
diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h
index d59d2b3aef9a..8072ac18e536 100644
--- a/include/drm/drm_atomic_state_helper.h
+++ b/include/drm/drm_atomic_state_helper.h
@@ -88,6 +88,7 @@ void
__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state);
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state);
+
void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj,
struct drm_private_state *state);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 3867a4c01b78..76eecd449fb8 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1066,6 +1066,12 @@ struct drm_connector_state {
* Broadcast RGB selection value.
*/
enum drm_hdmi_broadcast_rgb broadcast_rgb;
+
+ /**
+ * @is_full_range: Is the output supposed to use a full
+ * RGB Quantization Range or not?
+ */
+ bool is_full_range;
} hdmi;
};
--
2.43.0
A lot of HDMI drivers have some variation of the formula to calculate
the TMDS character rate from a mode, but few of them actually take all
parameters into account.
Let's create a helper to provide that rate taking all parameters into
account.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_connector.c | 59 ++++++
drivers/gpu/drm/tests/drm_connector_test.c | 323 +++++++++++++++++++++++++++++
include/drm/drm_connector.h | 5 +
3 files changed, 387 insertions(+)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index a72f38b6dbc8..667326b09acc 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2973,6 +2973,65 @@ void drm_connector_update_privacy_screen(const struct drm_connector_state *conne
}
EXPORT_SYMBOL(drm_connector_update_privacy_screen);
+/**
+ * drm_connector_hdmi_compute_mode_clock() - Computes the TMDS Character Rate
+ * @mode: Display mode to compute the clock for
+ * @bpc: Bits per character
+ * @fmt: Output Pixel Format used
+ *
+ * Returns the TMDS Character Rate for a given mode, bpc count and output format.
+ *
+ * RETURNS:
+ * The TMDS Character Rate, in Hertz, or 0 on error.
+ */
+unsigned long long
+drm_connector_hdmi_compute_mode_clock(const struct drm_display_mode *mode,
+ unsigned int bpc,
+ enum hdmi_colorspace fmt)
+{
+ unsigned long long clock = mode->clock * 1000ULL;
+ unsigned int vic = drm_match_cea_mode(mode);
+
+ /*
+ * CTA-861-G Spec, section 5.4 - Color Coding and Quantization
+ * mandates that VIC 1 always uses 8 bpc.
+ */
+ if (vic == 1 && bpc != 8)
+ return 0;
+
+ /*
+ * HDMI 2.0 Spec, section 7.1 - YCbCr 4:2:0 Pixel Encoding
+ * specifies that YUV420 encoding is only available for those
+ * VICs.
+ */
+ if (fmt == HDMI_COLORSPACE_YUV420 &&
+ !(vic == 96 || vic == 97 || vic == 101 ||
+ vic == 102 || vic == 106 || vic == 107))
+ return 0;
+
+ /*
+ * HDMI 1.4b Spec, section 6.2.3 - Pixel Encoding Requirements
+ * specifies that YUV422 is 36-bit only.
+ */
+ if (fmt == HDMI_COLORSPACE_YUV422 && bpc != 12)
+ return 0;
+
+ if (fmt == HDMI_COLORSPACE_YUV420)
+ clock = clock / 2;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ clock = clock * 2;
+
+ if (fmt == HDMI_COLORSPACE_YUV422)
+ bpc = 8;
+
+ clock = clock * bpc;
+ do_div(clock, 8);
+
+ return clock;
+}
+EXPORT_SYMBOL(drm_connector_hdmi_compute_mode_clock);
+
int drm_connector_set_obj_prop(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t value)
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index fa6fe8084107..0a838924a546 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -8,7 +8,9 @@
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
#include <drm/drm_kunit_helpers.h>
+#include <drm/drm_modes.h>
#include <kunit/test.h>
@@ -719,10 +721,331 @@ static struct kunit_suite drm_connector_attach_broadcast_rgb_property_test_suite
.test_cases = drm_connector_attach_broadcast_rgb_property_tests,
};
+/*
+ * Test that for a given mode, with 8bpc and an RGB output the TMDS
+ * character rate is equal to the mode pixel clock.
+ */
+static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ unsigned long long rate;
+ struct drm_device *drm = &priv->drm;
+
+ mode = drm_display_mode_from_cea_vic(drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, mode->clock * 1000ULL, rate);
+}
+
+/*
+ * Test that for a given mode, with 10bpc and an RGB output the TMDS
+ * character rate is equal to 1.25 times the mode pixel clock.
+ */
+static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ unsigned long long rate;
+ struct drm_device *drm = &priv->drm;
+
+ mode = drm_display_mode_from_cea_vic(drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, mode->clock * 1250, rate);
+}
+
+/*
+ * Test that for the VIC-1 mode, with 10bpc and an RGB output the TMDS
+ * character rate computation fails.
+ */
+static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc_vic_1(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ unsigned long long rate;
+ struct drm_device *drm = &priv->drm;
+
+ mode = drm_display_mode_from_cea_vic(drm, 1);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB);
+ KUNIT_EXPECT_EQ(test, rate, 0);
+}
+
+/*
+ * Test that for a given mode, with 12bpc and an RGB output the TMDS
+ * character rate is equal to 1.5 times the mode pixel clock.
+ */
+static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ unsigned long long rate;
+ struct drm_device *drm = &priv->drm;
+
+ mode = drm_display_mode_from_cea_vic(drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, mode->clock * 1500, rate);
+}
+
+/*
+ * Test that for the VIC-1 mode, with 12bpc and an RGB output the TMDS
+ * character rate computation fails.
+ */
+static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc_vic_1(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ unsigned long long rate;
+ struct drm_device *drm = &priv->drm;
+
+ mode = drm_display_mode_from_cea_vic(drm, 1);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_EXPECT_EQ(test, rate, 0);
+}
+
+/*
+ * Test that for a mode with the pixel repetition flag, the TMDS
+ * character rate is indeed double the mode pixel clock.
+ */
+static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_double(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ unsigned long long rate;
+ struct drm_device *drm = &priv->drm;
+
+ mode = drm_display_mode_from_cea_vic(drm, 6);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_TRUE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) * 2, rate);
+}
+
+/*
+ * Test that the TMDS character rate computation for the VIC modes
+ * explicitly listed in the spec as supporting YUV420 succeed and return
+ * half the mode pixel clock.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv420_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned long long rate;
+ unsigned int vic = *(unsigned int *)test->param_value;
+
+ mode = drm_display_mode_from_cea_vic(drm, vic);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV420);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) / 2, rate);
+}
+
+static const unsigned int drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[] = {
+ 96, 97, 101, 102, 106, 107,
+};
+
+static void drm_connector_hdmi_compute_mode_clock_yuv420_vic_desc(const unsigned int *vic, char *desc)
+{
+ sprintf(desc, "VIC %u", *vic);
+}
+
+KUNIT_ARRAY_PARAM(drm_connector_hdmi_compute_mode_clock_yuv420_valid,
+ drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests,
+ drm_connector_hdmi_compute_mode_clock_yuv420_vic_desc);
+
+/*
+ * Test that trying to compute the TMDS char rate with the YUV420 format
+ * for a mode that doesn't support the YUV420 encoding returns an error.
+ *
+ * TODO: We should probably test this with all the VIC but the
+ * explicitly supported ones. Since the list of VIC is quite long and
+ * not linear, the best way to support it at the moment would be to
+ * create a custom gen_params function that would only return valid
+ * VICs. At the moment, that function expects to get a pointer back
+ * however, and compilers don't really like casting between integer and
+ * pointers.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv420_invalid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned long long rate;
+
+ mode = drm_display_mode_from_cea_vic(drm, 42);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV420);
+ KUNIT_EXPECT_EQ(test, rate, 0);
+}
+
+/*
+ * Test that for a given mode listed supporting it and an YUV420 output
+ * with 10bpc, the TMDS character rate is equal to 0.625 times the mode
+ * pixel clock.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned int vic =
+ drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0];
+ unsigned long long rate;
+
+ mode = drm_display_mode_from_cea_vic(drm, vic);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_YUV420);
+ KUNIT_ASSERT_GT(test, rate, 0);
+
+ KUNIT_EXPECT_EQ(test, mode->clock * 625, rate);
+}
+
+/*
+ * Test that for a given mode listed supporting it and an YUV420 output
+ * with 12bpc, the TMDS character rate is equal to 0.75 times the mode
+ * pixel clock.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned int vic =
+ drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0];
+ unsigned long long rate;
+
+ mode = drm_display_mode_from_cea_vic(drm, vic);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_YUV420);
+ KUNIT_ASSERT_GT(test, rate, 0);
+
+ KUNIT_EXPECT_EQ(test, mode->clock * 750, rate);
+}
+
+/*
+ * Test that for a given mode, the computation of the TMDS character
+ * rate with 8bpc and a YUV422 output fails.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned long long rate;
+
+ mode = drm_display_mode_from_cea_vic(drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV422);
+ KUNIT_EXPECT_EQ(test, rate, 0);
+}
+
+/*
+ * Test that for a given mode, the computation of the TMDS character
+ * rate with 10bpc and a YUV422 output fails.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned long long rate;
+
+ mode = drm_display_mode_from_cea_vic(drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_YUV422);
+ KUNIT_EXPECT_EQ(test, rate, 0);
+}
+
+/*
+ * Test that for a given mode, the computation of the TMDS character
+ * rate with 12bpc and a YUV422 output succeeds and returns a rate equal
+ * to the mode pixel clock.
+ */
+static void drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_display_mode *mode;
+ struct drm_device *drm = &priv->drm;
+ unsigned long long rate;
+
+ mode = drm_display_mode_from_cea_vic(drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_YUV422);
+ KUNIT_ASSERT_GT(test, rate, 0);
+ KUNIT_EXPECT_EQ(test, mode->clock * 1000, rate);
+}
+
+static struct kunit_case drm_connector_hdmi_compute_mode_clock_tests[] = {
+ KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb),
+ KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc),
+ KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc_vic_1),
+ KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc),
+ KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc_vic_1),
+ KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_double),
+ KUNIT_CASE_PARAM(drm_test_connector_hdmi_compute_mode_clock_yuv420_valid,
+ drm_connector_hdmi_compute_mode_clock_yuv420_valid_gen_params),
+ KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_invalid),
+ KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc),
+ KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc),
+ KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc),
+ KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc),
+ KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc),
+ { }
+};
+
+static struct kunit_suite drm_connector_hdmi_compute_mode_clock_test_suite = {
+ .name = "drm_test_connector_hdmi_compute_mode_clock",
+ .init = drm_test_connector_init,
+ .test_cases = drm_connector_hdmi_compute_mode_clock_tests,
+};
+
kunit_test_suites(
&drmm_connector_hdmi_init_test_suite,
&drmm_connector_init_test_suite,
&drm_connector_attach_broadcast_rgb_property_test_suite,
+ &drm_connector_hdmi_compute_mode_clock_test_suite,
&drm_get_tv_mode_from_name_test_suite,
&drm_hdmi_connector_get_broadcast_rgb_name_test_suite,
&drm_hdmi_connector_get_output_format_name_test_suite
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 74db5ce47e01..0cc5a8732664 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -38,6 +38,7 @@ struct drm_connector_helper_funcs;
struct drm_modeset_acquire_ctx;
struct drm_device;
struct drm_crtc;
+struct drm_display_mode;
struct drm_encoder;
struct drm_panel;
struct drm_property;
@@ -2136,6 +2137,10 @@ void drm_connector_attach_privacy_screen_properties(struct drm_connector *conn);
void drm_connector_attach_privacy_screen_provider(
struct drm_connector *connector, struct drm_privacy_screen *priv);
void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state);
+unsigned long long
+drm_connector_hdmi_compute_mode_clock(const struct drm_display_mode *mode,
+ unsigned int bpc,
+ enum hdmi_colorspace fmt);
/**
* struct drm_tile_group - Tile group metadata
--
2.43.0
Now that we have all the infrastructure needed, we can add some code
that will, for a given connector state and mode, compute the best output
format and bpc.
The algorithm is the same one than the one already found in i915 and
vc4.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic_state_helper.c | 183 ++++++-
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 529 ++++++++++++++++++++-
drivers/gpu/drm/tests/drm_kunit_edid.h | 160 +++++++
3 files changed, 860 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index a36edda590f8..2442b5a2d94f 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -682,6 +682,96 @@ static bool hdmi_is_full_range(const struct drm_connector *connector,
return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false;
}
+static bool
+sink_supports_format_bpc(const struct drm_connector *connector,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode,
+ unsigned int format, unsigned int bpc)
+{
+ struct drm_device *dev = connector->dev;
+ u8 vic = drm_match_cea_mode(mode);
+
+ if (vic == 1 && bpc != 8) {
+ drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
+ return false;
+ }
+
+ if (!info->is_hdmi &&
+ (format != HDMI_COLORSPACE_RGB || bpc != 8)) {
+ drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
+ return false;
+ }
+
+ if (!(connector->hdmi.supported_formats & BIT(format))) {
+ drm_dbg(dev, "%s format unsupported by the connector.\n",
+ drm_hdmi_connector_get_output_format_name(format));
+ return false;
+ }
+
+ switch (format) {
+ case HDMI_COLORSPACE_RGB:
+ drm_dbg(dev, "RGB Format, checking the constraints.\n");
+
+ if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
+ return false;
+
+ if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
+ drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
+ return false;
+ }
+
+ if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
+ drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
+ return false;
+ }
+
+ drm_dbg(dev, "RGB format supported in that configuration.\n");
+
+ return true;
+
+ case HDMI_COLORSPACE_YUV422:
+ drm_dbg(dev, "YUV422 format, checking the constraints.\n");
+
+ if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
+ drm_dbg(dev, "Sink doesn't support YUV422.\n");
+ return false;
+ }
+
+ if (bpc != 12) {
+ drm_dbg(dev, "YUV422 only supports 12 bpc.\n");
+ return false;
+ }
+
+ drm_dbg(dev, "YUV422 format supported in that configuration.\n");
+
+ return true;
+
+ case HDMI_COLORSPACE_YUV444:
+ drm_dbg(dev, "YUV444 format, checking the constraints.\n");
+
+ if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) {
+ drm_dbg(dev, "Sink doesn't support YUV444.\n");
+ return false;
+ }
+
+ if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) {
+ drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
+ return false;
+ }
+
+ if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) {
+ drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
+ return false;
+ }
+
+ drm_dbg(dev, "YUV444 format supported in that configuration.\n");
+
+ return true;
+ }
+
+ return false;
+}
+
static enum drm_mode_status
hdmi_clock_valid(const struct drm_connector *connector,
const struct drm_display_mode *mode,
@@ -726,6 +816,95 @@ hdmi_compute_clock(const struct drm_connector *connector,
return 0;
}
+static bool
+hdmi_try_format_bpc(const struct drm_connector *connector,
+ struct drm_connector_state *state,
+ const struct drm_display_mode *mode,
+ unsigned int bpc, enum hdmi_colorspace fmt)
+{
+ const struct drm_display_info *info = &connector->display_info;
+ struct drm_device *dev = connector->dev;
+ int ret;
+
+ drm_dbg(dev, "Trying %s output format\n",
+ drm_hdmi_connector_get_output_format_name(fmt));
+
+ if (!sink_supports_format_bpc(connector, info, mode, fmt, bpc)) {
+ drm_dbg(dev, "%s output format not supported with %u bpc\n",
+ drm_hdmi_connector_get_output_format_name(fmt), bpc);
+ return false;
+ }
+
+ ret = hdmi_compute_clock(connector, state, mode, bpc, fmt);
+ if (ret) {
+ drm_dbg(dev, "Couldn't compute clock for %s output format and %u bpc\n",
+ drm_hdmi_connector_get_output_format_name(fmt), bpc);
+ return false;
+ }
+
+ drm_dbg(dev, "%s output format supported with %u (TMDS char rate: %llu Hz)\n",
+ drm_hdmi_connector_get_output_format_name(fmt), bpc, state->hdmi.tmds_char_rate);
+
+ return true;
+}
+
+static int
+hdmi_compute_format(const struct drm_connector *connector,
+ struct drm_connector_state *state,
+ const struct drm_display_mode *mode,
+ unsigned int bpc)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (hdmi_try_format_bpc(connector, state, mode, bpc, HDMI_COLORSPACE_RGB)) {
+ state->hdmi.output_format = HDMI_COLORSPACE_RGB;
+ return 0;
+ }
+
+ if (hdmi_try_format_bpc(connector, state, mode, bpc, HDMI_COLORSPACE_YUV422)) {
+ state->hdmi.output_format = HDMI_COLORSPACE_YUV422;
+ return 0;
+ }
+
+ drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n");
+
+ return -EINVAL;
+}
+
+static int
+hdmi_compute_config(const struct drm_connector *connector,
+ struct drm_connector_state *state,
+ const struct drm_display_mode *mode)
+{
+ struct drm_device *dev = connector->dev;
+ unsigned int max_bpc = clamp_t(unsigned int,
+ state->max_bpc,
+ 8, connector->max_bpc);
+ unsigned int bpc;
+ int ret;
+
+ for (bpc = max_bpc; bpc >= 8; bpc -= 2) {
+ drm_dbg(dev, "Trying with a %d bpc output\n", bpc);
+
+ ret = hdmi_compute_format(connector, state, mode, bpc);
+ if (ret)
+ continue;
+
+ state->hdmi.output_bpc = bpc;
+
+ drm_dbg(dev,
+ "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n",
+ mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
+ state->hdmi.output_bpc,
+ drm_hdmi_connector_get_output_format_name(state->hdmi.output_format),
+ state->hdmi.tmds_char_rate);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
/**
* drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
* @connector: DRM Connector
@@ -751,9 +930,7 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
- ret = hdmi_compute_clock(connector, new_state, mode,
- new_state->hdmi.output_bpc,
- new_state->hdmi.output_format);
+ ret = hdmi_compute_config(connector, new_state, mode);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index e7dbdd4a4e7f..860e34b00fee 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -70,9 +70,6 @@ static int light_up_connector(struct kunit *test,
conn_state = drm_atomic_get_connector_state(state, connector);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
- conn_state->hdmi.output_bpc = connector->max_bpc;
- conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB;
-
ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -720,10 +717,15 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
10);
KUNIT_ASSERT_NOT_NULL(test, priv);
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
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);
@@ -741,11 +743,11 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
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.output_bpc = 8;
+ new_conn_state->max_requested_bpc = 8;
KUNIT_ASSERT_NE(test,
- old_conn_state->hdmi.output_bpc,
- new_conn_state->hdmi.output_bpc);
+ old_conn_state->max_requested_bpc,
+ new_conn_state->max_requested_bpc);
ret = drm_atomic_check_only(state);
KUNIT_ASSERT_EQ(test, ret, 0);
@@ -789,10 +791,15 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
10);
KUNIT_ASSERT_NOT_NULL(test, priv);
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
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);
@@ -832,6 +839,56 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
}
+/*
+ * Test that if we have an HDMI connector but a !HDMI display, we always
+ * output RGB with 8 bpc.
+ */
+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_atomic_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;
+ ret = set_connector_edid(test, conn,
+ test_edid_dvi_1080p,
+ ARRAY_SIZE(test_edid_dvi_1080p));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_FALSE(test, info->is_hdmi);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ 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);
+
+ 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);
+}
+
/*
* Test that when doing a commit which would use RGB 8bpc, the TMDS
* clock rate stored in the connector state is equal to the mode clock
@@ -1024,6 +1081,452 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
KUNIT_EXPECT_LT(test, ret, 0);
}
+/*
+ * Test that if:
+ * - We have an HDMI connector supporting RGB only
+ * - The chosen mode has a TMDS character rate higher than the display
+ * supports in RGB/12bpc
+ * - The chosen mode has a TMDS character rate lower than the display
+ * supports in RGB/10bpc.
+ *
+ * Then we will pick the latter, and the computed TMDS character rate
+ * will be equal to 1.25 times the mode pixel clock.
+ */
+static void drm_test_check_max_tmds_rate_bpc_fallback(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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 12);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
+
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
+/*
+ * Test that if:
+ * - We have an HDMI connector supporting both RGB and YUV422 and up to
+ * 12 bpc
+ * - The chosen mode has a TMDS character rate higher than the display
+ * supports in RGB/12bpc
+ * - The chosen mode has a TMDS character rate lower than the display
+ * supports in YUV422/12bpc.
+ *
+ * Then we will pick the latter, and the computed TMDS character rate
+ * will be equal to the mode pixel clock.
+ */
+static void drm_test_check_max_tmds_rate_format_fallback(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_atomic_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;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
+
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
+
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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, 12);
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV422);
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000);
+}
+
+/*
+ * Test that if a driver and screen supports RGB and YUV formats, and we
+ * try to set the VIC 1 mode, we end up with 8bpc RGB even if we could
+ * have had a higher bpc.
+ */
+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_atomic_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;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ mode = drm_display_mode_from_cea_vic(drm, 1);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ /*
+ * NOTE: We can't use drm_connector_hdmi_compute_mode_clock()
+ * here because we're trying to get the rate of an invalid
+ * configuration.
+ *
+ * Thus, we have to calculate the rate by hand.
+ */
+ rate = mode->clock * 1500;
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, mode, ctx);
+ 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);
+}
+
+/*
+ * Test that if a driver supports only RGB but the screen also supports
+ * YUV formats, we only end up with an RGB format.
+ */
+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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 12);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ /*
+ * We're making sure that YUV422 would be the preferred option
+ * here: we're always favouring higher bpc, we can't have RGB
+ * because the TMDS character rate exceeds the maximum supported
+ * by the display, and YUV422 works for that display.
+ *
+ * But since the driver only supports RGB, we should fallback to
+ * a lower bpc with RGB.
+ */
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
+
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
+/*
+ * Test that if a screen supports only RGB but the driver also supports
+ * YUV formats, we only end up with an RGB format.
+ */
+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_atomic_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;
+ 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);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ /*
+ * We're making sure that YUV422 would be the preferred option
+ * here: we're always favouring higher bpc, we can't have RGB
+ * because the TMDS character rate exceeds the maximum supported
+ * by the display, and YUV422 works for that display.
+ *
+ * But since the display only supports RGB, we should fallback to
+ * a lower bpc with RGB.
+ */
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
+
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
+/*
+ * Test that if a display supports higher bpc but the driver only
+ * supports 8 bpc, we only end up with 8 bpc even if we could have had a
+ * higher bpc.
+ */
+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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ /*
+ * We're making sure that we have headroom on the TMDS character
+ * clock to actually use 12bpc.
+ */
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
+/*
+ * Test that if a driver supports higher bpc but the display only
+ * supports 8 bpc, we only end up with 8 bpc even if we could have had a
+ * higher bpc.
+ */
+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_atomic_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;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_max_340mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_340mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ /*
+ * We're making sure that we have headroom on the TMDS character
+ * clock to actually use 12bpc.
+ */
+ rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
+ KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
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),
@@ -1034,8 +1537,16 @@ 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),
KUNIT_CASE(drm_test_check_hdmi_funcs_reject_rate),
+ KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback),
+ KUNIT_CASE(drm_test_check_max_tmds_rate_format_fallback),
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),
@@ -1167,7 +1678,7 @@ static void drm_test_check_format_value(struct kunit *test)
conn = &priv->connector;
conn_state = conn->state;
- KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, 0);
}
/*
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h
index 24f3377ef0f0..3e3527c58c31 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.h
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.h
@@ -1,6 +1,64 @@
#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 0a 00 00 00 00 00 00 00
+ * 00 00 00 00 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 00 ab
+ *
+ * ----------------
+ *
+ * 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
+ * RGB color 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: none
+ * 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:
+ * Checksum: 0xab
+ */
+const unsigned char test_edid_dvi_1080p[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 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, 0x1e, 0x46, 0x0f, 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, 0x00, 0xab
+};
+
/*
* edid-decode (hex):
*
@@ -103,6 +161,108 @@ const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
0x00, 0x00, 0x00, 0xd0
};
+/*
+ * 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: 340 MHz
+ * Extended HDMI video details:
+ * Checksum: 0xd0 Unused space in Extension Block: 100 bytes
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = {
+ 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, 0x44, 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
+};
+
/*
* edid-decode (hex):
*
--
2.43.0
Most of the HDMI controllers have an upper TMDS character rate limit
they can't exceed. On "embedded"-grade display controllers, it will
typically be lower than what high-grade monitors can provide these days,
so drivers will filter the TMDS character rate based on the controller
capabilities.
To make that easier to handle for drivers, let's provide an optional
hook to be implemented by drivers so they can tell the HDMI controller
helpers if a given TMDS character rate is reachable for them or not.
This will then be useful to figure out the best format and bpc count for
a given mode.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic_state_helper.c | 9 +++
drivers/gpu/drm/drm_connector.c | 4 ++
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 69 ++++++++++++++++++++++
drivers/gpu/drm/tests/drm_connector_test.c | 15 +++++
include/drm/drm_connector.h | 30 ++++++++++
5 files changed, 127 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 74bc3cc53c2d..a36edda590f8 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -687,11 +687,20 @@ hdmi_clock_valid(const struct drm_connector *connector,
const struct drm_display_mode *mode,
unsigned long long clock)
{
+ const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
const struct drm_display_info *info = &connector->display_info;
if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000)
return MODE_CLOCK_HIGH;
+ if (funcs && funcs->tmds_char_rate_valid) {
+ enum drm_mode_status status;
+
+ status = funcs->tmds_char_rate_valid(connector, mode, clock);
+ if (status != MODE_OK)
+ return status;
+ }
+
return MODE_OK;
}
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 667326b09acc..9f314fee26ce 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -457,6 +457,7 @@ EXPORT_SYMBOL(drmm_connector_init);
* @dev: DRM device
* @connector: A pointer to the HDMI connector to init
* @funcs: callbacks for this connector
+ * @hdmi_funcs: HDMI-related callbacks for this connector
* @connector_type: user visible type of the connector
* @ddc: optional pointer to the associated ddc adapter
* @supported_formats: Bitmask of @hdmi_colorspace listing supported output formats
@@ -476,6 +477,7 @@ EXPORT_SYMBOL(drmm_connector_init);
int drmm_connector_hdmi_init(struct drm_device *dev,
struct drm_connector *connector,
const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
int connector_type,
struct i2c_adapter *ddc,
unsigned long supported_formats,
@@ -512,6 +514,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (max_bpc > 8)
drm_connector_attach_hdr_output_metadata_property(connector);
+ connector->hdmi.funcs = hdmi_funcs;
+
return 0;
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index d76fafb91025..e7dbdd4a4e7f 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -110,6 +110,21 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto
return 0;
}
+static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = {
+};
+
+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,
+};
+
static int dummy_connector_get_modes(struct drm_connector *connector)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
@@ -192,6 +207,7 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test,
conn = &priv->connector;
ret = drmm_connector_hdmi_init(drm, conn,
&dummy_connector_funcs,
+ &dummy_connector_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
NULL,
formats,
@@ -956,6 +972,58 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500);
}
+/*
+ * Test that if we filter a rate through our hook, it's indeed rejected
+ * by the whole atomic_check logic.
+ *
+ * We do so by first doing a commit on the pipeline to make sure that it
+ * works, change the HDMI helpers pointer, and then try the same commit
+ * again to see if it fails as it should.
+ */
+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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ 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);
+
+ /* You shouldn't be doing that at home. */
+ 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);
+}
+
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),
@@ -965,6 +1033,7 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1),
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_hdmi_funcs_reject_rate),
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_tmds_char_rate_rgb_8bpc),
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 0a838924a546..6a3651b08c81 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -22,6 +22,9 @@ struct drm_connector_init_priv {
struct i2c_adapter ddc;
};
+static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = {
+};
+
static const struct drm_connector_funcs dummy_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -187,6 +190,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -205,6 +209,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
NULL,
BIT(HDMI_COLORSPACE_RGB),
@@ -223,6 +228,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -241,6 +247,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -263,6 +270,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -297,6 +305,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -331,6 +340,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -361,6 +371,7 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
0,
@@ -379,6 +390,7 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_YUV422),
@@ -398,6 +410,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
connector_type,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -431,6 +444,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
connector_type,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
@@ -695,6 +709,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
+ &dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc,
BIT(HDMI_COLORSPACE_RGB),
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 59016d9c17f5..3eaf4d54364d 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1093,6 +1093,30 @@ struct drm_connector_state {
} hdmi;
};
+/**
+ * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions
+ */
+struct drm_connector_hdmi_funcs {
+ /**
+ * @tmds_char_rate_valid:
+ *
+ * This callback is invoked at atomic_check time to figure out
+ * whether a particular TMDS character rate is supported by the
+ * driver.
+ *
+ * The @tmds_char_rate_valid callback is optional.
+ *
+ * Returns:
+ *
+ * Either &drm_mode_status.MODE_OK or one of the failure reasons
+ * in &enum drm_mode_status.
+ */
+ enum drm_mode_status
+ (*tmds_char_rate_valid)(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate);
+};
+
/**
* struct drm_connector_funcs - control connectors on a given device
*
@@ -1967,6 +1991,11 @@ struct drm_connector {
* supported by the controller.
*/
unsigned long supported_formats;
+
+ /**
+ * @funcs: HDMI connector Control Functions
+ */
+ const struct drm_connector_hdmi_funcs *funcs;
} hdmi;
};
@@ -1989,6 +2018,7 @@ int drmm_connector_init(struct drm_device *dev,
int drmm_connector_hdmi_init(struct drm_device *dev,
struct drm_connector *connector,
const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
int connector_type,
struct i2c_adapter *ddc,
unsigned long supported_formats,
--
2.43.0
Infoframes in KMS is usually handled by a bunch of low-level helpers
that require quite some boilerplate for drivers. This leads to
discrepancies with how drivers generate them, and which are actually
sent.
Now that we have everything needed to generate them in the HDMI
connector state, we can generate them in our common logic so that
drivers can simply reuse what we precomputed.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/drm_atomic_state_helper.c | 327 +++++++++++++++++++++
drivers/gpu/drm/drm_connector.c | 16 +
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 1 +
drivers/gpu/drm/tests/drm_connector_test.c | 231 +++++++++++++++
include/drm/drm_atomic_state_helper.h | 7 +
include/drm/drm_connector.h | 133 +++++++++
7 files changed, 716 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 31cfe2c2a2af..acf733426bde 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -100,6 +100,7 @@ config DRM_KUNIT_TEST
config DRM_KMS_HELPER
tristate
depends on DRM
+ select DRM_DISPLAY_HDMI_HELPER
help
CRTC helpers for KMS drivers.
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 2442b5a2d94f..78e5d3463570 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -38,6 +38,8 @@
#include <drm/drm_vblank.h>
#include <drm/drm_writeback.h>
+#include <drm/display/drm_hdmi_helper.h>
+
#include <linux/slab.h>
#include <linux/dma-fence.h>
@@ -905,6 +907,142 @@ hdmi_compute_config(const struct drm_connector *connector,
return -EINVAL;
}
+static int hdmi_generate_avi_infoframe(const struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ const struct drm_display_mode *mode =
+ connector_state_get_mode(state);
+ struct drm_connector_hdmi_infoframe *infoframe =
+ &state->hdmi.infoframes.avi;
+ struct hdmi_avi_infoframe *frame =
+ &infoframe->data.avi;
+ bool is_full_range = state->hdmi.is_full_range;
+ enum hdmi_quantization_range rgb_quant_range =
+ is_full_range ? HDMI_QUANTIZATION_RANGE_FULL : HDMI_QUANTIZATION_RANGE_LIMITED;
+ int ret;
+
+ ret = drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode);
+ if (ret)
+ return ret;
+
+ frame->colorspace = state->hdmi.output_format;
+
+ drm_hdmi_avi_infoframe_quant_range(frame, connector, mode, rgb_quant_range);
+ drm_hdmi_avi_infoframe_colorimetry(frame, state);
+ drm_hdmi_avi_infoframe_bars(frame, state);
+
+ infoframe->set = true;
+
+ return 0;
+}
+
+static int hdmi_generate_spd_infoframe(const struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ struct drm_connector_hdmi_infoframe *infoframe =
+ &state->hdmi.infoframes.spd;
+ struct hdmi_spd_infoframe *frame =
+ &infoframe->data.spd;
+ int ret;
+
+ ret = hdmi_spd_infoframe_init(frame,
+ connector->hdmi.vendor,
+ connector->hdmi.product);
+ if (ret)
+ return ret;
+
+ frame->sdi = HDMI_SPD_SDI_PC;
+
+ infoframe->set = true;
+
+ return 0;
+}
+
+static int hdmi_generate_hdr_infoframe(const struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ struct drm_connector_hdmi_infoframe *infoframe =
+ &state->hdmi.infoframes.hdr_drm;
+ struct hdmi_drm_infoframe *frame =
+ &infoframe->data.drm;
+ int ret;
+
+ if (connector->max_bpc < 10)
+ return 0;
+
+ if (!state->hdr_output_metadata)
+ return 0;
+
+ ret = drm_hdmi_infoframe_set_hdr_metadata(frame, state);
+ if (ret)
+ return ret;
+
+ infoframe->set = true;
+
+ return 0;
+}
+
+static int hdmi_generate_hdmi_vendor_infoframe(const struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ const struct drm_display_mode *mode =
+ connector_state_get_mode(state);
+ struct drm_connector_hdmi_infoframe *infoframe =
+ &state->hdmi.infoframes.hdmi;
+ struct hdmi_vendor_infoframe *frame =
+ &infoframe->data.vendor.hdmi;
+ int ret;
+
+ ret = drm_hdmi_vendor_infoframe_from_display_mode(frame, connector, mode);
+ if (ret) {
+ if (ret == -EINVAL)
+ return 0;
+
+ return ret;
+ }
+
+ infoframe->set = true;
+
+ return 0;
+}
+
+static int
+hdmi_generate_infoframes(const struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ const struct drm_display_info *info = &connector->display_info;
+ int ret;
+
+ if (!info->is_hdmi)
+ return 0;
+
+ if (!info->has_hdmi_infoframe)
+ return 0;
+
+ ret = hdmi_generate_avi_infoframe(connector, state);
+ if (ret)
+ return ret;
+
+ ret = hdmi_generate_spd_infoframe(connector, state);
+ if (ret)
+ return ret;
+
+ /*
+ * Audio Infoframes will be generated by ALSA, and updated by
+ * drm_atomic_helper_connector_hdmi_update_audio_infoframe().
+ */
+
+ ret = hdmi_generate_hdr_infoframe(connector, state);
+ if (ret)
+ return ret;
+
+ ret = hdmi_generate_hdmi_vendor_infoframe(connector, state);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
/**
* drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
* @connector: DRM Connector
@@ -934,6 +1072,10 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
if (ret)
return ret;
+ ret = hdmi_generate_infoframes(connector, new_state);
+ if (ret)
+ return ret;
+
if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
old_state->hdmi.output_bpc != new_state->hdmi.output_bpc ||
old_state->hdmi.output_format != new_state->hdmi.output_format) {
@@ -951,6 +1093,191 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check);
+#define HDMI_MAX_INFOFRAME_SIZE 29
+
+static int clear_device_infoframe(struct drm_connector *connector,
+ enum hdmi_infoframe_type type)
+{
+ const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
+
+ if (!funcs || !funcs->clear_infoframe)
+ return 0;
+
+ return funcs->clear_infoframe(connector, type);
+}
+
+static int clear_infoframe(struct drm_connector *connector,
+ struct drm_connector_hdmi_infoframe *conn_frame,
+ struct drm_connector_hdmi_infoframe *old_frame)
+{
+ int ret;
+
+ ret = clear_device_infoframe(connector, old_frame->data.any.type);
+ if (ret)
+ return ret;
+
+ memset(old_frame, 0, sizeof(*old_frame));
+
+ return 0;
+}
+
+static int write_device_infoframe(struct drm_connector *connector,
+ union hdmi_infoframe *frame)
+{
+ const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
+ u8 buffer[HDMI_MAX_INFOFRAME_SIZE];
+ int len;
+
+ if (!funcs || !funcs->write_infoframe)
+ return -ENOSYS;
+
+ len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
+ if (len < 0)
+ return len;
+
+ return funcs->write_infoframe(connector, frame->any.type, buffer, len);
+}
+
+static int write_infoframe(struct drm_connector *connector,
+ struct drm_connector_hdmi_infoframe *conn_frame,
+ struct drm_connector_hdmi_infoframe *new_frame)
+{
+ int ret;
+
+ ret = write_device_infoframe(connector, &new_frame->data);
+ if (ret)
+ return ret;
+
+ if (conn_frame)
+ memcpy(conn_frame, new_frame, sizeof(*conn_frame));
+
+ return 0;
+}
+
+static int write_or_clear_infoframe(struct drm_connector *connector,
+ struct drm_connector_hdmi_infoframe *conn_frame,
+ struct drm_connector_hdmi_infoframe *old_frame,
+ struct drm_connector_hdmi_infoframe *new_frame)
+{
+ if (new_frame->set)
+ return write_infoframe(connector, conn_frame, new_frame);
+
+ if (old_frame->set && !new_frame->set)
+ return clear_infoframe(connector, conn_frame, old_frame);
+
+ return 0;
+}
+
+#define UPDATE_INFOFRAME(c, os, ns, i) \
+ write_or_clear_infoframe(c, \
+ &(c)->hdmi.infoframes.i, \
+ &(os)->hdmi.infoframes.i, \
+ &(ns)->hdmi.infoframes.i)
+
+/**
+ * drm_atomic_helper_connector_hdmi_update_infoframes - Update the Infoframes
+ * @connector: A pointer to the HDMI connector
+ * @state: The HDMI connector state to generate the infoframe from
+ *
+ * This function is meant for HDMI connector drivers to write their
+ * infoframes. It will typically be used in a
+ * @drm_connector_helper_funcs.atomic_enable implementation.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_atomic_helper_connector_hdmi_update_infoframes(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);
+ struct drm_display_info *info = &connector->display_info;
+ int ret;
+
+ if (!info->is_hdmi)
+ return 0;
+
+ if (!info->has_hdmi_infoframe)
+ return 0;
+
+ mutex_lock(&connector->hdmi.infoframes.lock);
+
+ ret = UPDATE_INFOFRAME(connector, old_state, new_state, avi);
+ if (ret)
+ goto out;
+
+ if (connector->hdmi.infoframes.audio.set) {
+ ret = write_infoframe(connector,
+ NULL,
+ &connector->hdmi.infoframes.audio);
+ if (ret)
+ goto out;
+ }
+
+ ret = UPDATE_INFOFRAME(connector, old_state, new_state, hdr_drm);
+ if (ret)
+ goto out;
+
+ ret = UPDATE_INFOFRAME(connector, old_state, new_state, spd);
+ if (ret)
+ goto out;
+
+ ret = UPDATE_INFOFRAME(connector, old_state, new_state, hdmi);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&connector->hdmi.infoframes.lock);
+ return ret;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_infoframes);
+
+#undef UPDATE_INFOFRAME
+#undef UPDATE_INFOFRAME_TOGGLE
+
+/**
+ * drm_atomic_helper_connector_hdmi_update_audio_infoframe - Update the Audio Infoframe
+ * @connector: A pointer to the HDMI connector
+ * @frame: A pointer to the audio infoframe to write
+ *
+ * This function is meant for HDMI connector drivers to update their
+ * audio infoframe. It will typically be used in one of the ALSA hooks
+ * (most likely prepare).
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int
+drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector,
+ struct hdmi_audio_infoframe *frame)
+{
+ struct drm_connector_hdmi_infoframe infoframe = {};
+ struct drm_display_info *info = &connector->display_info;
+ int ret;
+
+ if (!info->is_hdmi)
+ return 0;
+
+ if (!info->has_hdmi_infoframe)
+ return 0;
+
+ memcpy(&infoframe.data, frame, sizeof(infoframe.data));
+ infoframe.set = true;
+
+ mutex_lock(&connector->hdmi.infoframes.lock);
+
+ ret = write_infoframe(connector,
+ &connector->hdmi.infoframes.audio,
+ &infoframe);
+
+ mutex_unlock(&connector->hdmi.infoframes.lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe);
+
/**
* __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
* @connector: connector object
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 9f314fee26ce..1f783f82423b 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -456,6 +456,8 @@ EXPORT_SYMBOL(drmm_connector_init);
* drmm_connector_hdmi_init - Init a preallocated HDMI connector
* @dev: DRM device
* @connector: A pointer to the HDMI connector to init
+ * @vendor: HDMI Controller Vendor name
+ * @product: HDMI Controller Product name
* @funcs: callbacks for this connector
* @hdmi_funcs: HDMI-related callbacks for this connector
* @connector_type: user visible type of the connector
@@ -476,6 +478,7 @@ EXPORT_SYMBOL(drmm_connector_init);
*/
int drmm_connector_hdmi_init(struct drm_device *dev,
struct drm_connector *connector,
+ const char *vendor, const char *product,
const struct drm_connector_funcs *funcs,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
int connector_type,
@@ -485,6 +488,13 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
{
int ret;
+ if (!vendor || !product)
+ return -EINVAL;
+
+ if ((strlen(vendor) > DRM_CONNECTOR_HDMI_VENDOR_LEN) ||
+ (strlen(product) > DRM_CONNECTOR_HDMI_PRODUCT_LEN))
+ return -EINVAL;
+
if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA ||
connector_type == DRM_MODE_CONNECTOR_HDMIB))
return -EINVAL;
@@ -500,6 +510,12 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
return ret;
connector->hdmi.supported_formats = supported_formats;
+ strtomem_pad(connector->hdmi.vendor, vendor, 0);
+ strtomem_pad(connector->hdmi.product, product, 0);
+
+ ret = drmm_mutex_init(dev, &connector->hdmi.infoframes.lock);
+ if (ret)
+ return ret;
/*
* drm_connector_attach_max_bpc_property() requires the
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index 860e34b00fee..1eaa83af9dd0 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -203,6 +203,7 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test,
conn = &priv->connector;
ret = drmm_connector_hdmi_init(drm, conn,
+ "Vendor", "Product",
&dummy_connector_funcs,
&dummy_connector_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 6a3651b08c81..07066b704b36 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -189,6 +189,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -208,6 +209,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -217,6 +219,217 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
KUNIT_EXPECT_EQ(test, ret, 0);
}
+/*
+ * Test that the registration of an HDMI connector with a NULL vendor
+ * fails.
+ */
+static void drm_test_connector_hdmi_init_null_vendor(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ NULL, "Product",
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of an HDMI connector with a NULL product
+ * fails.
+ */
+static void drm_test_connector_hdmi_init_null_product(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", NULL,
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector with a valid, shorter than
+ * the max length, product name succeeds, and is stored padded with 0.
+ */
+static void drm_test_connector_hdmi_init_product_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = {
+ 'P', 'r', 'o', 'd',
+ };
+ const char *product_name = "Prod";
+ int ret;
+
+ KUNIT_ASSERT_LT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", product_name,
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_MEMEQ(test,
+ priv->connector.hdmi.product,
+ expected_product,
+ sizeof(priv->connector.hdmi.product));
+}
+
+/*
+ * Test that the registration of a connector with a valid, at max
+ * length, product name succeeds, and is stored padded without any
+ * trailing \0.
+ */
+static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = {
+ 'P', 'r', 'o', 'd', 'u', 'c', 't',
+ 'P', 'r', 'o', 'd', 'u', 'c', 't',
+ 'P', 'r',
+ };
+ const char *product_name = "ProductProductPr";
+ int ret;
+
+ KUNIT_ASSERT_EQ(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", product_name,
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_MEMEQ(test,
+ priv->connector.hdmi.product,
+ expected_product,
+ sizeof(priv->connector.hdmi.product));
+}
+
+/*
+ * Test that the registration of a connector with a product name larger
+ * than the maximum length fails.
+ */
+static void drm_test_connector_hdmi_init_product_length_too_long(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const char *product_name = "ProductProductProduct";
+ int ret;
+
+ KUNIT_ASSERT_GT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", product_name,
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector with a vendor name smaller
+ * than the maximum length succeeds, and is stored padded with zeros.
+ */
+static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = {
+ 'V', 'e', 'n', 'd',
+ };
+ const char *vendor_name = "Vend";
+ int ret;
+
+ KUNIT_ASSERT_LT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ vendor_name, "Product",
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_MEMEQ(test,
+ priv->connector.hdmi.vendor,
+ expected_vendor,
+ sizeof(priv->connector.hdmi.vendor));
+}
+
+/*
+ * Test that the registration of a connector with a vendor name at the
+ * maximum length succeeds, and is stored padded without the trailing
+ * zero.
+ */
+static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = {
+ 'V', 'e', 'n', 'd', 'o', 'r',
+ 'V', 'e',
+ };
+ const char *vendor_name = "VendorVe";
+ int ret;
+
+ KUNIT_ASSERT_EQ(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ vendor_name, "Product",
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_MEMEQ(test,
+ priv->connector.hdmi.vendor,
+ expected_vendor,
+ sizeof(priv->connector.hdmi.vendor));
+}
+
+/*
+ * Test that the registration of a connector with a vendor name larger
+ * than the maximum length fails.
+ */
+static void drm_test_connector_hdmi_init_vendor_length_too_long(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const char *vendor_name = "VendorVendor";
+ int ret;
+
+ KUNIT_ASSERT_GT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ vendor_name, "Product",
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
/*
* Test that the registration of a connector with an invalid maximum bpc
* count fails.
@@ -227,6 +440,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -246,6 +460,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -269,6 +484,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -304,6 +520,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -339,6 +556,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -370,6 +588,7 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -389,6 +608,7 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
@@ -409,6 +629,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
connector_type,
@@ -443,6 +664,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
connector_type,
@@ -488,6 +710,14 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE(drm_test_connector_hdmi_init_formats_empty),
KUNIT_CASE(drm_test_connector_hdmi_init_formats_no_rgb),
KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
+ KUNIT_CASE(drm_test_connector_hdmi_init_null_product),
+ KUNIT_CASE(drm_test_connector_hdmi_init_null_vendor),
+ KUNIT_CASE(drm_test_connector_hdmi_init_product_length_exact),
+ KUNIT_CASE(drm_test_connector_hdmi_init_product_length_too_long),
+ KUNIT_CASE(drm_test_connector_hdmi_init_product_valid),
+ KUNIT_CASE(drm_test_connector_hdmi_init_vendor_length_exact),
+ KUNIT_CASE(drm_test_connector_hdmi_init_vendor_length_too_long),
+ KUNIT_CASE(drm_test_connector_hdmi_init_vendor_valid),
KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
drm_connector_hdmi_init_type_valid_gen_params),
KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_invalid,
@@ -708,6 +938,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
int ret;
ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ "Vendor", "Product",
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h
index 8072ac18e536..22f083968aa8 100644
--- a/include/drm/drm_atomic_state_helper.h
+++ b/include/drm/drm_atomic_state_helper.h
@@ -40,6 +40,8 @@ struct drm_private_state;
struct drm_modeset_acquire_ctx;
struct drm_device;
+struct hdmi_audio_infoframe;
+
void __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *state,
struct drm_crtc *crtc);
void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc,
@@ -89,6 +91,11 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state);
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state);
+int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector,
+ struct hdmi_audio_infoframe *frame);
+int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector,
+ struct drm_atomic_state *state);
+
void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj,
struct drm_private_state *state);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 3eaf4d54364d..5964ef283022 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -914,6 +914,21 @@ struct drm_tv_connector_state {
unsigned int hue;
};
+/**
+ * struct drm_connector_hdmi_infoframe - HDMI Infoframe container
+ */
+struct drm_connector_hdmi_infoframe {
+ /**
+ * @data: HDMI Infoframe structure
+ */
+ union hdmi_infoframe data;
+
+ /**
+ * @set: Is the content of @data valid?
+ */
+ bool set;
+};
+
/**
* struct drm_connector_state - mutable connector state
*/
@@ -1070,6 +1085,35 @@ struct drm_connector_state {
*/
enum drm_hdmi_broadcast_rgb broadcast_rgb;
+ /**
+ * @infoframes: HDMI Infoframes matching that state
+ */
+ struct {
+ /**
+ * @avi: AVI Infoframes structure matching our
+ * state.
+ */
+ struct drm_connector_hdmi_infoframe avi;
+
+ /**
+ * @hdr_drm: DRM (Dynamic Range and Mastering)
+ * Infoframes structure matching our state.
+ */
+ struct drm_connector_hdmi_infoframe hdr_drm;
+
+ /**
+ * @spd: SPD Infoframes structure matching our
+ * state.
+ */
+ struct drm_connector_hdmi_infoframe spd;
+
+ /**
+ * @vendor: HDMI Vendor Infoframes structure
+ * matching our state.
+ */
+ struct drm_connector_hdmi_infoframe hdmi;
+ } infoframes;
+
/**
* @is_full_range: Is the output supposed to use a full
* RGB Quantization Range or not?
@@ -1115,6 +1159,41 @@ struct drm_connector_hdmi_funcs {
(*tmds_char_rate_valid)(const struct drm_connector *connector,
const struct drm_display_mode *mode,
unsigned long long tmds_rate);
+
+ /**
+ * @clear_infoframe:
+ *
+ * This callback is invoked through
+ * @drm_atomic_helper_hdmi_connector_update_infoframes during a
+ * commit to clear the infoframes into the hardware. It will be
+ * called multiple times, once for every disabled infoframe
+ * type.
+ *
+ * The @clear_infoframe callback is optional.
+ *
+ * Returns:
+ * 0 on success, a negative error code otherwise
+ */
+ int (*clear_infoframe)(struct drm_connector *connector,
+ enum hdmi_infoframe_type type);
+
+ /**
+ * @write_infoframe:
+ *
+ * This callback is invoked through
+ * @drm_atomic_helper_hdmi_connector_update_infoframes during a
+ * commit to program the infoframes into the hardware. It will
+ * be called multiple times, once for every updated infoframe
+ * type.
+ *
+ * The @write_infoframe callback is mandatory.
+ *
+ * Returns:
+ * 0 on success, a negative error code otherwise
+ */
+ int (*write_infoframe)(struct drm_connector *connector,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len);
};
/**
@@ -1986,6 +2065,18 @@ struct drm_connector {
* @hdmi: HDMI-related variable and properties.
*/
struct {
+#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8
+ /**
+ * @vendor: HDMI Controller Vendor Name
+ */
+ unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] __nonstring;
+
+#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16
+ /**
+ * @product: HDMI Controller Product Name
+ */
+ unsigned char product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] __nonstring;
+
/**
* @supported_formats: Bitmask of @hdmi_colorspace
* supported by the controller.
@@ -1996,6 +2087,47 @@ struct drm_connector {
* @funcs: HDMI connector Control Functions
*/
const struct drm_connector_hdmi_funcs *funcs;
+
+ /**
+ * @infoframes: Current Infoframes output by the connector
+ */
+ struct {
+ /**
+ * @lock: Mutex protecting against concurrent access to
+ * the infoframes, most notably between KMS and ALSA.
+ */
+ struct mutex lock;
+
+ /**
+ * @audio: Current Audio Infoframes structure. Protected
+ * by @lock.
+ */
+ struct drm_connector_hdmi_infoframe audio;
+
+ /**
+ * @avi: Current AVI Infoframes structure. Protected by
+ * @lock.
+ */
+ struct drm_connector_hdmi_infoframe avi;
+
+ /**
+ * @hdr_drm: Current DRM (Dynamic Range and Mastering)
+ * Infoframes structure. Protected by @lock.
+ */
+ struct drm_connector_hdmi_infoframe hdr_drm;
+
+ /**
+ * @spd: Current SPD Infoframes structure. Protected by
+ * @lock.
+ */
+ struct drm_connector_hdmi_infoframe spd;
+
+ /**
+ * @vendor: Current HDMI Vendor Infoframes structure.
+ * Protected by @lock.
+ */
+ struct drm_connector_hdmi_infoframe hdmi;
+ } infoframes;
} hdmi;
};
@@ -2017,6 +2149,7 @@ int drmm_connector_init(struct drm_device *dev,
struct i2c_adapter *ddc);
int drmm_connector_hdmi_init(struct drm_device *dev,
struct drm_connector *connector,
+ const char *vendor, const char *product,
const struct drm_connector_funcs *funcs,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
int connector_type,
--
2.43.0
We'll add automatic selection of the output BPC in a following patch,
but let's add it to the HDMI connector state already.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic.c | 1 +
drivers/gpu/drm/drm_atomic_state_helper.c | 7 +-
drivers/gpu/drm/drm_connector.c | 20 +-
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 227 ++++++++++++++++++++-
drivers/gpu/drm/tests/drm_connector_test.c | 153 +++++++++++++-
include/drm/drm_connector.h | 13 +-
6 files changed, 402 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index e4ad53e64e07..76c63ed04af4 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1147,6 +1147,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
drm_printf(p, "\tbroadcast_rgb=%s\n",
drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb));
drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n');
+ drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
}
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 4a7114978c47..883bdc0349c0 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -584,6 +584,10 @@ 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)
{
+ unsigned int max_bpc = connector->max_bpc;
+
+ new_state->max_bpc = max_bpc;
+ new_state->max_requested_bpc = max_bpc;
new_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
@@ -700,7 +704,8 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
- if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb) {
+ if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
+ old_state->hdmi.output_bpc != new_state->hdmi.output_bpc) {
struct drm_crtc *crtc = new_state->crtc;
struct drm_crtc_state *crtc_state;
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 929b0a911f62..139ac3d8160c 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -459,6 +459,7 @@ EXPORT_SYMBOL(drmm_connector_init);
* @funcs: callbacks for this connector
* @connector_type: user visible type of the connector
* @ddc: optional pointer to the associated ddc adapter
+ * @max_bpc: Maximum bits per char the HDMI connector supports
*
* Initialises a preallocated HDMI connector. Connectors can be
* subclassed as part of driver connector objects.
@@ -475,7 +476,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
struct drm_connector *connector,
const struct drm_connector_funcs *funcs,
int connector_type,
- struct i2c_adapter *ddc)
+ struct i2c_adapter *ddc,
+ unsigned int max_bpc)
{
int ret;
@@ -483,10 +485,26 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
connector_type == DRM_MODE_CONNECTOR_HDMIB))
return -EINVAL;
+ if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
+ return -EINVAL;
+
ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
if (ret)
return ret;
+ /*
+ * drm_connector_attach_max_bpc_property() requires the
+ * connector to have a state.
+ */
+ if (connector->funcs->reset)
+ connector->funcs->reset(connector);
+
+ drm_connector_attach_max_bpc_property(connector, 8, max_bpc);
+ connector->max_bpc = max_bpc;
+
+ if (max_bpc > 8)
+ drm_connector_attach_hdr_output_metadata_property(connector);
+
return 0;
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index 7750c3d214a4..a1b0e6914cf8 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -146,7 +146,8 @@ static const struct drm_connector_funcs dummy_connector_funcs = {
static
struct drm_atomic_helper_connector_hdmi_priv *
-drm_atomic_helper_connector_hdmi_init(struct kunit *test)
+drm_atomic_helper_connector_hdmi_init(struct kunit *test,
+ unsigned int max_bpc)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector *conn;
@@ -188,7 +189,8 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test)
ret = drmm_connector_hdmi_init(drm, conn,
&dummy_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- NULL);
+ NULL,
+ max_bpc);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_connector_helper_add(conn, &dummy_connector_helper_funcs);
@@ -223,7 +225,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
@@ -284,7 +286,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
@@ -345,7 +347,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -399,7 +401,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -452,7 +454,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -508,7 +510,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -563,7 +565,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -619,7 +621,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
struct drm_crtc *crtc;
int ret;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -657,6 +659,138 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range);
}
+/*
+ * Test that if we change the maximum bpc 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_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_atomic_helper_connector_hdmi_init(test, 10);
+ 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.output_bpc = 8;
+
+ KUNIT_ASSERT_NE(test,
+ old_conn_state->hdmi.output_bpc,
+ new_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_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);
+}
+
+/*
+ * Test that if we set the output bpc 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_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_atomic_helper_connector_hdmi_init(test, 10);
+ 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);
+
+ 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);
+}
+
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),
@@ -666,6 +800,8 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1),
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_output_bpc_crtc_mode_changed),
+ KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed),
{ }
};
@@ -684,7 +820,7 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_atomic_helper_connector_hdmi_init(test);
+ priv = drm_atomic_helper_connector_hdmi_init(test, 8);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -692,8 +828,77 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
KUNIT_EXPECT_EQ(test, conn_state->hdmi.broadcast_rgb, DRM_HDMI_BROADCAST_RGB_AUTO);
}
+/*
+ * Test that if the connector was initialised with a maximum bpc of 8,
+ * the value of the max_bpc and max_requested_bpc properties out of
+ * reset are also set to 8, and output_bpc is set to 0 and will be
+ * filled at atomic_check time.
+ */
+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_atomic_helper_connector_hdmi_init(test, 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);
+}
+
+/*
+ * Test that if the connector was initialised with a maximum bpc of 10,
+ * the value of the max_bpc and max_requested_bpc properties out of
+ * reset are also set to 10, and output_bpc is set to 0 and will be
+ * filled at atomic_check time.
+ */
+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_atomic_helper_connector_hdmi_init(test, 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);
+}
+
+/*
+ * Test that if the connector was initialised with a maximum bpc of 12,
+ * the value of the max_bpc and max_requested_bpc properties out of
+ * reset are also set to 12, and output_bpc is set to 0 and will be
+ * filled at atomic_check time.
+ */
+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_atomic_helper_connector_hdmi_init(test, 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 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),
{ }
};
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 41d33dea30af..fef7d53d34d4 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -186,7 +186,8 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc);
+ &priv->ddc,
+ 8);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -202,10 +203,144 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- NULL);
+ NULL,
+ 8);
KUNIT_EXPECT_EQ(test, ret, 0);
}
+/*
+ * Test that the registration of a connector with an invalid maximum bpc
+ * count fails.
+ */
+static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ 9);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector with a null maximum bpc
+ * count fails.
+ */
+static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ 0);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector with a maximum bpc count of
+ * 8 succeeds, registers the max bpc property, but doesn't register the
+ * HDR output metadata one.
+ */
+static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ struct drm_connector *connector = &priv->connector;
+ struct drm_property *prop;
+ uint64_t val;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ 8);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ prop = connector->max_bpc_property;
+ KUNIT_ASSERT_NOT_NULL(test, prop);
+ KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
+
+ ret = drm_object_property_get_value(&connector->base, prop, &val);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, val, 8);
+
+ prop = priv->drm.mode_config.hdr_output_metadata_property;
+ KUNIT_ASSERT_NOT_NULL(test, prop);
+ KUNIT_EXPECT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
+}
+
+/*
+ * Test that the registration of a connector with a maximum bpc count of
+ * 10 succeeds and registers the max bpc and HDR output metadata
+ * properties.
+ */
+static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ struct drm_connector *connector = &priv->connector;
+ struct drm_property *prop;
+ uint64_t val;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ 10);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ prop = connector->max_bpc_property;
+ KUNIT_ASSERT_NOT_NULL(test, prop);
+ KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
+
+ ret = drm_object_property_get_value(&connector->base, prop, &val);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, val, 10);
+
+ prop = priv->drm.mode_config.hdr_output_metadata_property;
+ KUNIT_ASSERT_NOT_NULL(test, prop);
+ KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
+}
+
+/*
+ * Test that the registration of a connector with a maximum bpc count of
+ * 12 succeeds and registers the max bpc and HDR output metadata
+ * properties.
+ */
+static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ struct drm_connector *connector = &priv->connector;
+ struct drm_property *prop;
+ uint64_t val;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc,
+ 12);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ prop = connector->max_bpc_property;
+ KUNIT_ASSERT_NOT_NULL(test, prop);
+ KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
+
+ ret = drm_object_property_get_value(&connector->base, prop, &val);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, val, 12);
+
+ prop = priv->drm.mode_config.hdr_output_metadata_property;
+ KUNIT_ASSERT_NOT_NULL(test, prop);
+ KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
+}
+
/*
* Test that the registration of an HDMI connector with an HDMI
* connector type succeeds.
@@ -219,7 +354,8 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
connector_type,
- &priv->ddc);
+ &priv->ddc,
+ 8);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -250,7 +386,8 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
connector_type,
- &priv->ddc);
+ &priv->ddc,
+ 8);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -282,6 +419,11 @@ KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid,
static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE(drm_test_connector_hdmi_init_valid),
+ KUNIT_CASE(drm_test_connector_hdmi_init_bpc_8),
+ KUNIT_CASE(drm_test_connector_hdmi_init_bpc_10),
+ KUNIT_CASE(drm_test_connector_hdmi_init_bpc_12),
+ KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid),
+ KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null),
KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
drm_connector_hdmi_init_type_valid_gen_params),
@@ -447,7 +589,8 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc);
+ &priv->ddc,
+ 8);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_connector_attach_broadcast_rgb_property(connector);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 76eecd449fb8..1b1b6aed04ee 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1072,6 +1072,11 @@ struct drm_connector_state {
* RGB Quantization Range or not?
*/
bool is_full_range;
+
+ /**
+ * @output_bpc: Bits per color channel to output.
+ */
+ unsigned int output_bpc;
} hdmi;
};
@@ -1717,6 +1722,11 @@ struct drm_connector {
*/
struct drm_property_blob *path_blob_ptr;
+ /**
+ * @max_bpc: Maximum bits per color channel the connector supports.
+ */
+ unsigned int max_bpc;
+
/**
* @max_bpc_property: Default connector property for the max bpc to be
* driven out of the connector.
@@ -1956,7 +1966,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
struct drm_connector *connector,
const struct drm_connector_funcs *funcs,
int connector_type,
- struct i2c_adapter *ddc);
+ struct i2c_adapter *ddc,
+ unsigned int max_bpc);
void drm_connector_attach_edid_property(struct drm_connector *connector);
int drm_connector_register(struct drm_connector *connector);
void drm_connector_unregister(struct drm_connector *connector);
--
2.43.0
The mode's VIC is only ever used in the inno_hdmi_setup() function so
there's no need to store it in the main structure.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 4db18195246e..294f0d442c0c 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"
struct hdmi_data_info {
- int vic;
bool sink_has_audio;
unsigned int enc_in_format;
unsigned int enc_out_format;
@@ -443,16 +442,15 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
struct drm_display_info *display = &hdmi->connector.display_info;
-
- hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+ u8 vic = drm_match_cea_mode(mode);
hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
- if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
- (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
- (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
- (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+ if ((vic == 6) || (vic == 7) ||
+ (vic == 21) || (vic == 22) ||
+ (vic == 2) || (vic == 3) ||
+ (vic == 17) || (vic == 18))
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
else
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
--
2.43.0
Similarly to the input format, the driver has a lot of code to deal with
various output format, but the driver hardcodes it to RGB always.
Let's get rid of the dead code.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 57 ++++--------------------------------
1 file changed, 6 insertions(+), 51 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index e0696ab16da3..0c6c550e0ce7 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"
struct hdmi_data_info {
- unsigned int enc_out_format;
unsigned int colorimetry;
};
@@ -296,26 +295,14 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
&hdmi->connector,
mode);
-
- if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
- frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
- else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
- frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
- else
- frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+ frame.avi.colorspace = HDMI_COLORSPACE_RGB;
return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{
- struct hdmi_data_info *data = &hdmi->hdmi_data;
- int c0_c2_change = 0;
- int csc_enable = 0;
- int csc_mode = 0;
- int auto_csc = 0;
int value;
- int i;
/* Input video mode is SDR RGB24bit, data enable signal from external */
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
@@ -327,43 +314,13 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
- if (HDMI_COLORSPACE_RGB == data->enc_out_format) {
- value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
-
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
- m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
- v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
- v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
- return 0;
- }
-
- if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
- if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
- csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
- auto_csc = AUTO_CSC_DISABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_ENABLE;
- }
- } else {
- if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
- csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
- auto_csc = AUTO_CSC_DISABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_ENABLE;
- }
- }
-
- for (i = 0; i < 24; i++)
- hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
- coeff_csc[csc_mode][i]);
-
- value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+ value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
- m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
- v_VIDEO_C0_C2_SWAP(c0_c2_change));
+ hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+ m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+ v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+ v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
return 0;
}
@@ -425,8 +382,6 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_info *display = &hdmi->connector.display_info;
u8 vic = drm_match_cea_mode(mode);
- hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
-
if ((vic == 6) || (vic == 7) ||
(vic == 21) || (vic == 22) ||
(vic == 2) || (vic == 3) ||
--
2.43.0
The new HDMI connector infrastructure allows us to remove a lot of
boilerplate, so let's switch to it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 638 +++++--------------------------------
drivers/gpu/drm/vc4/vc4_hdmi.h | 44 +--
drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 6 +-
3 files changed, 86 insertions(+), 602 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index f05e2c95a60d..3e3a2afdc730 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -109,25 +109,6 @@
#define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000)
-static const char * const output_format_str[] = {
- [VC4_HDMI_OUTPUT_RGB] = "RGB",
- [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0",
- [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2",
- [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4",
-};
-
-static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt)
-{
- if (fmt >= ARRAY_SIZE(output_format_str))
- return "invalid";
-
- return output_format_str[fmt];
-}
-
-static unsigned long long
-vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
- unsigned int bpc, enum vc4_hdmi_output_format fmt);
-
static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
{
struct drm_display_info *display = &vc4_hdmi->connector.display_info;
@@ -146,28 +127,13 @@ static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
unsigned int bpc,
- enum vc4_hdmi_output_format fmt)
+ enum hdmi_colorspace fmt)
{
- unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
+ unsigned long long clock = drm_connector_hdmi_compute_mode_clock(mode, bpc, fmt);
return clock > HDMI_14_MAX_TMDS_CLK;
}
-static bool vc4_hdmi_is_full_range(struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_state)
-{
- const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
- struct drm_display_info *display = &vc4_hdmi->connector.display_info;
-
- if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_LIMITED)
- return false;
- else if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_FULL)
- return true;
-
- return !display->is_hdmi ||
- drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL;
-}
-
static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
{
struct drm_debugfs_entry *entry = m->private;
@@ -519,7 +485,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
const struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
- if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) {
+ if (vc4_hdmi_mode_needs_scrambling(mode, 8, HDMI_COLORSPACE_RGB)) {
drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
}
@@ -534,12 +500,8 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
{
struct drm_connector_state *old_state =
drm_atomic_get_old_connector_state(state, connector);
- struct vc4_hdmi_connector_state *old_vc4_state =
- conn_state_to_vc4_hdmi_conn_state(old_state);
struct drm_connector_state *new_state =
drm_atomic_get_new_connector_state(state, connector);
- struct vc4_hdmi_connector_state *new_vc4_state =
- conn_state_to_vc4_hdmi_conn_state(new_state);
struct drm_crtc *crtc = new_state->crtc;
if (!crtc)
@@ -571,9 +533,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
return ret;
}
- if (old_state->colorspace != new_state->colorspace ||
- old_vc4_state->broadcast_rgb != new_vc4_state->broadcast_rgb ||
- !drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
+ if (old_state->colorspace != new_state->colorspace) {
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
@@ -583,112 +543,21 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
crtc_state->mode_changed = true;
}
- return 0;
-}
-
-static int vc4_hdmi_connector_get_property(struct drm_connector *connector,
- const struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t *val)
-{
- struct drm_device *drm = connector->dev;
- struct vc4_hdmi *vc4_hdmi =
- connector_to_vc4_hdmi(connector);
- const struct vc4_hdmi_connector_state *vc4_conn_state =
- conn_state_to_vc4_hdmi_conn_state(state);
-
- if (property == vc4_hdmi->broadcast_rgb_property) {
- *val = vc4_conn_state->broadcast_rgb;
- } else {
- drm_dbg(drm, "Unknown property [PROP:%d:%s]\n",
- property->base.id, property->name);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vc4_hdmi_connector_set_property(struct drm_connector *connector,
- struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t val)
-{
- struct drm_device *drm = connector->dev;
- struct vc4_hdmi *vc4_hdmi =
- connector_to_vc4_hdmi(connector);
- struct vc4_hdmi_connector_state *vc4_conn_state =
- conn_state_to_vc4_hdmi_conn_state(state);
-
- if (property == vc4_hdmi->broadcast_rgb_property) {
- vc4_conn_state->broadcast_rgb = val;
- return 0;
- }
-
- drm_dbg(drm, "Unknown property [PROP:%d:%s]\n",
- property->base.id, property->name);
- return -EINVAL;
+ return drm_atomic_helper_connector_hdmi_check(connector, state);
}
static void vc4_hdmi_connector_reset(struct drm_connector *connector)
{
- struct vc4_hdmi_connector_state *old_state =
- conn_state_to_vc4_hdmi_conn_state(connector->state);
- struct vc4_hdmi_connector_state *new_state =
- kzalloc(sizeof(*new_state), GFP_KERNEL);
-
- if (connector->state)
- __drm_atomic_helper_connector_destroy_state(connector->state);
-
- kfree(old_state);
- __drm_atomic_helper_connector_reset(connector, &new_state->base);
-
- if (!new_state)
- return;
-
- new_state->base.max_bpc = 8;
- new_state->base.max_requested_bpc = 8;
- new_state->output_format = VC4_HDMI_OUTPUT_RGB;
- new_state->broadcast_rgb = VC4_HDMI_BROADCAST_RGB_AUTO;
+ drm_atomic_helper_connector_reset(connector);
+ __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
drm_atomic_helper_connector_tv_margins_reset(connector);
}
-static struct drm_connector_state *
-vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
-{
- struct drm_connector_state *conn_state = connector->state;
- struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
- struct vc4_hdmi_connector_state *new_state;
-
- new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
- if (!new_state)
- return NULL;
-
- new_state->tmds_char_rate = vc4_state->tmds_char_rate;
- new_state->output_bpc = vc4_state->output_bpc;
- new_state->output_format = vc4_state->output_format;
- new_state->broadcast_rgb = vc4_state->broadcast_rgb;
- __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
-
- return &new_state->base;
-}
-
-static void vc4_hdmi_connector_destroy_state(struct drm_connector *connector,
- struct drm_connector_state *state)
-{
- struct vc4_hdmi_connector_state *vc4_state =
- conn_state_to_vc4_hdmi_conn_state(state);
-
- __drm_atomic_helper_connector_destroy_state(state);
- kfree(vc4_state);
-}
-
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = vc4_hdmi_connector_reset,
- .atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
- .atomic_destroy_state = vc4_hdmi_connector_destroy_state,
- .atomic_get_property = vc4_hdmi_connector_get_property,
- .atomic_set_property = vc4_hdmi_connector_set_property,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
@@ -697,32 +566,7 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs =
.atomic_check = vc4_hdmi_connector_atomic_check,
};
-static const struct drm_prop_enum_list broadcast_rgb_names[] = {
- { VC4_HDMI_BROADCAST_RGB_AUTO, "Automatic" },
- { VC4_HDMI_BROADCAST_RGB_FULL, "Full" },
- { VC4_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235" },
-};
-
-static void
-vc4_hdmi_attach_broadcast_rgb_property(struct drm_device *dev,
- struct vc4_hdmi *vc4_hdmi)
-{
- struct drm_property *prop = vc4_hdmi->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;
-
- vc4_hdmi->broadcast_rgb_property = prop;
- }
-
- drm_object_attach_property(&vc4_hdmi->connector.base, prop,
- VC4_HDMI_BROADCAST_RGB_AUTO);
-}
+static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs;
static int vc4_hdmi_connector_init(struct drm_device *dev,
struct vc4_hdmi *vc4_hdmi)
@@ -731,10 +575,16 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
int ret;
- ret = drmm_connector_init(dev, connector,
- &vc4_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA,
- vc4_hdmi->ddc);
+ ret = drmm_connector_hdmi_init(dev, connector,
+ "Broadcom", "Videocore",
+ &vc4_hdmi_connector_funcs,
+ &vc4_hdmi_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ vc4_hdmi->ddc,
+ BIT(HDMI_COLORSPACE_RGB) |
+ BIT(HDMI_COLORSPACE_YUV422) |
+ BIT(HDMI_COLORSPACE_YUV444),
+ 12);
if (ret)
return ret;
@@ -758,7 +608,6 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
drm_connector_attach_colorspace_property(connector);
drm_connector_attach_tv_margin_properties(connector);
- drm_connector_attach_max_bpc_property(connector, 8, 12);
connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT);
@@ -767,21 +616,19 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
connector->doublescan_allowed = 0;
connector->stereo_allowed = 1;
- if (vc4_hdmi->variant->supports_hdr)
- drm_connector_attach_hdr_output_metadata_property(connector);
-
- vc4_hdmi_attach_broadcast_rgb_property(dev, vc4_hdmi);
+ ret = drm_connector_attach_broadcast_rgb_property(connector);
+ if (ret)
+ return ret;
drm_connector_attach_encoder(connector, encoder);
return 0;
}
-static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
+static int vc4_hdmi_stop_packet(struct vc4_hdmi *vc4_hdmi,
enum hdmi_infoframe_type type,
bool poll)
{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct drm_device *drm = vc4_hdmi->connector.dev;
u32 packet_id = type - 0x80;
unsigned long flags;
@@ -805,12 +652,13 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
return ret;
}
-static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
- union hdmi_infoframe *frame)
+static int vc4_hdmi_write_infoframe(struct drm_connector *connector,
+ enum hdmi_infoframe_type type,
+ const u8 *infoframe, size_t len)
{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_device *drm = vc4_hdmi->connector.dev;
- u32 packet_id = frame->any.type - 0x80;
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
+ struct drm_device *drm = connector->dev;
+ u32 packet_id = type - 0x80;
const struct vc4_hdmi_register *ram_packet_start =
&vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
@@ -820,22 +668,25 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
ram_packet_start->reg);
uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {};
unsigned long flags;
- ssize_t len, i;
+ ssize_t i;
int ret;
int idx;
if (!drm_dev_enter(drm, &idx))
- return;
+ return 0;
+
+ if (len > sizeof(buffer)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(buffer, infoframe, len);
WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
VC4_HDMI_RAM_PACKET_ENABLE),
"Packet RAM has to be on to store the packet.");
- len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
- if (len < 0)
- goto out;
-
- ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true);
+ ret = vc4_hdmi_stop_packet(vc4_hdmi, type, true);
if (ret) {
DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
goto out;
@@ -877,130 +728,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
out:
drm_dev_exit(idx);
-}
-
-static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
- enum vc4_hdmi_output_format fmt)
-{
- switch (fmt) {
- case VC4_HDMI_OUTPUT_RGB:
- frame->colorspace = HDMI_COLORSPACE_RGB;
- break;
-
- case VC4_HDMI_OUTPUT_YUV420:
- frame->colorspace = HDMI_COLORSPACE_YUV420;
- break;
-
- case VC4_HDMI_OUTPUT_YUV422:
- frame->colorspace = HDMI_COLORSPACE_YUV422;
- break;
-
- case VC4_HDMI_OUTPUT_YUV444:
- frame->colorspace = HDMI_COLORSPACE_YUV444;
- break;
-
- default:
- break;
- }
-}
-
-static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
-{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_connector *connector = &vc4_hdmi->connector;
- struct drm_connector_state *cstate = connector->state;
- struct vc4_hdmi_connector_state *vc4_state =
- conn_state_to_vc4_hdmi_conn_state(cstate);
- const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
- union hdmi_infoframe frame;
- int ret;
-
- lockdep_assert_held(&vc4_hdmi->mutex);
-
- ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
- connector, mode);
- if (ret < 0) {
- DRM_ERROR("couldn't fill AVI infoframe\n");
- return;
- }
-
- drm_hdmi_avi_infoframe_quant_range(&frame.avi,
- connector, mode,
- vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ?
- HDMI_QUANTIZATION_RANGE_FULL :
- HDMI_QUANTIZATION_RANGE_LIMITED);
- drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate);
- vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format);
- drm_hdmi_avi_infoframe_bars(&frame.avi, cstate);
-
- vc4_hdmi_write_infoframe(encoder, &frame);
-}
-
-static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
-{
- union hdmi_infoframe frame;
- int ret;
-
- ret = hdmi_spd_infoframe_init(&frame.spd, "Broadcom", "Videocore");
- if (ret < 0) {
- DRM_ERROR("couldn't fill SPD infoframe\n");
- return;
- }
-
- frame.spd.sdi = HDMI_SPD_SDI_PC;
-
- vc4_hdmi_write_infoframe(encoder, &frame);
-}
-
-static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
-{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct hdmi_audio_infoframe *audio = &vc4_hdmi->audio.infoframe;
- union hdmi_infoframe frame;
-
- memcpy(&frame.audio, audio, sizeof(*audio));
-
- if (vc4_hdmi->packet_ram_enabled)
- vc4_hdmi_write_infoframe(encoder, &frame);
-}
-
-static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder)
-{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_connector *connector = &vc4_hdmi->connector;
- struct drm_connector_state *conn_state = connector->state;
- union hdmi_infoframe frame;
-
- lockdep_assert_held(&vc4_hdmi->mutex);
-
- if (!vc4_hdmi->variant->supports_hdr)
- return;
-
- if (!conn_state->hdr_output_metadata)
- return;
-
- if (drm_hdmi_infoframe_set_hdr_metadata(&frame.drm, conn_state))
- return;
-
- vc4_hdmi_write_infoframe(encoder, &frame);
-}
-
-static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
-{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
-
- lockdep_assert_held(&vc4_hdmi->mutex);
-
- vc4_hdmi_set_avi_infoframe(encoder);
- vc4_hdmi_set_spd_infoframe(encoder);
- /*
- * If audio was streaming, then we need to reenabled the audio
- * infoframe here during encoder_enable.
- */
- if (vc4_hdmi->audio.streaming)
- vc4_hdmi_set_audio_infoframe(encoder);
-
- vc4_hdmi_set_hdr_infoframe(encoder);
+ return ret;
}
#define SCRAMBLING_POLLING_DELAY_MS 1000
@@ -1169,9 +897,8 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
struct drm_connector_state *state,
const struct drm_display_mode *mode)
{
- struct vc4_hdmi_connector_state *vc4_state =
- conn_state_to_vc4_hdmi_conn_state(state);
struct drm_device *drm = vc4_hdmi->connector.dev;
+ bool is_lim_range = !state->hdmi.is_full_range;
unsigned long flags;
u32 csc_ctl;
int idx;
@@ -1184,7 +911,7 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
VC4_HD_CSC_CTL_ORDER);
- if (!vc4_hdmi_is_full_range(vc4_hdmi, vc4_state)) {
+ if (is_lim_range) {
/* CEA VICs other than #1 requre limited range RGB
* output unless overridden by an AVI infoframe.
* Apply a colorspace conversion to squash 0-255 down
@@ -1407,9 +1134,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
const struct drm_display_mode *mode)
{
struct drm_device *drm = vc4_hdmi->connector.dev;
- struct vc4_hdmi_connector_state *vc4_state =
- conn_state_to_vc4_hdmi_conn_state(state);
- unsigned int lim_range = vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ? 0 : 1;
+ unsigned int lim_range = state->hdmi.is_full_range ? 0 : 1;
unsigned long flags;
const u16 (*csc)[4];
u32 if_cfg = 0;
@@ -1424,14 +1149,14 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
- switch (vc4_state->output_format) {
- case VC4_HDMI_OUTPUT_YUV444:
+ switch (state->hdmi.output_format) {
+ case HDMI_COLORSPACE_YUV444:
csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range);
vc5_hdmi_set_csc_coeffs_swap(vc4_hdmi, csc);
break;
- case VC4_HDMI_OUTPUT_YUV422:
+ case HDMI_COLORSPACE_YUV422:
csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, state->colorspace, !!lim_range);
csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD,
@@ -1448,7 +1173,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
vc5_hdmi_set_csc_coeffs(vc4_hdmi, csc);
break;
- case VC4_HDMI_OUTPUT_RGB:
+ case HDMI_COLORSPACE_RGB:
if_xbar = 0x354021;
vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_rgb[lim_range]);
@@ -1537,8 +1262,6 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
const struct drm_display_mode *mode)
{
struct drm_device *drm = vc4_hdmi->connector.dev;
- const struct vc4_hdmi_connector_state *vc4_state =
- conn_state_to_vc4_hdmi_conn_state(state);
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
@@ -1590,7 +1313,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
HDMI_WRITE(HDMI_VERTB0, vertb_even);
HDMI_WRITE(HDMI_VERTB1, vertb);
- switch (vc4_state->output_bpc) {
+ switch (state->hdmi.output_bpc) {
case 12:
gcp = 6;
break;
@@ -1607,7 +1330,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
* YCC422 is always 36-bit and not considered deep colour so
* doesn't signal in GCP.
*/
- if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) {
+ if (state->hdmi.output_format == HDMI_COLORSPACE_YUV422) {
gcp = 0;
}
@@ -1691,10 +1414,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_connector_state *conn_state =
drm_atomic_get_new_connector_state(state, connector);
- struct vc4_hdmi_connector_state *vc4_conn_state =
- conn_state_to_vc4_hdmi_conn_state(conn_state);
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
- unsigned long tmds_char_rate = vc4_conn_state->tmds_char_rate;
+ unsigned long tmds_char_rate = conn_state->hdmi.tmds_char_rate;
unsigned long bvb_rate, hsm_rate;
unsigned long flags;
int ret;
@@ -1771,7 +1492,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
}
if (vc4_hdmi->variant->phy_init)
- vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state);
+ vc4_hdmi->variant->phy_init(vc4_hdmi, conn_state);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
@@ -1836,7 +1557,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_device *drm = vc4_hdmi->connector.dev;
+ struct drm_connector *connector = &vc4_hdmi->connector;
+ struct drm_device *drm = connector->dev;
const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
struct drm_display_info *display = &vc4_hdmi->connector.display_info;
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
@@ -1902,7 +1624,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
vc4_hdmi->packet_ram_enabled = true;
- vc4_hdmi_set_infoframes(encoder);
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
}
vc4_hdmi_recenter_fifo(vc4_hdmi);
@@ -1919,108 +1641,21 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_connector_state *conn_state)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct vc4_hdmi_connector_state *vc4_state =
- conn_state_to_vc4_hdmi_conn_state(conn_state);
mutex_lock(&vc4_hdmi->mutex);
drm_mode_copy(&vc4_hdmi->saved_adjusted_mode,
&crtc_state->adjusted_mode);
- vc4_hdmi->output_bpc = vc4_state->output_bpc;
- vc4_hdmi->output_format = vc4_state->output_format;
+ vc4_hdmi->output_bpc = conn_state->hdmi.output_bpc;
+ vc4_hdmi->output_format = conn_state->hdmi.output_format;
mutex_unlock(&vc4_hdmi->mutex);
}
-static bool
-vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode,
- unsigned int format, unsigned int bpc)
-{
- struct drm_device *dev = vc4_hdmi->connector.dev;
- u8 vic = drm_match_cea_mode(mode);
-
- if (vic == 1 && bpc != 8) {
- drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
- return false;
- }
-
- if (!info->is_hdmi &&
- (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) {
- drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
- return false;
- }
-
- switch (format) {
- case VC4_HDMI_OUTPUT_RGB:
- drm_dbg(dev, "RGB Format, checking the constraints.\n");
-
- if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
- return false;
-
- if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
- drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
- return false;
- }
-
- if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
- drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
- return false;
- }
-
- drm_dbg(dev, "RGB format supported in that configuration.\n");
-
- return true;
-
- case VC4_HDMI_OUTPUT_YUV422:
- drm_dbg(dev, "YUV422 format, checking the constraints.\n");
-
- if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
- drm_dbg(dev, "Sink doesn't support YUV422.\n");
- return false;
- }
-
- if (bpc != 12) {
- drm_dbg(dev, "YUV422 only supports 12 bpc.\n");
- return false;
- }
-
- drm_dbg(dev, "YUV422 format supported in that configuration.\n");
-
- return true;
-
- case VC4_HDMI_OUTPUT_YUV444:
- drm_dbg(dev, "YUV444 format, checking the constraints.\n");
-
- if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) {
- drm_dbg(dev, "Sink doesn't support YUV444.\n");
- return false;
- }
-
- if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) {
- drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
- return false;
- }
-
- if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) {
- drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
- return false;
- }
-
- drm_dbg(dev, "YUV444 format supported in that configuration.\n");
-
- return true;
- }
-
- return false;
-}
-
static enum drm_mode_status
-vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
- const struct drm_display_mode *mode,
- unsigned long long clock)
+vc4_hdmi_connector_clock_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long clock)
{
- const struct drm_connector *connector = &vc4_hdmi->connector;
- const struct drm_display_info *info = &connector->display_info;
+ const struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
if (clock > vc4_hdmi->variant->max_pixel_clock)
@@ -2035,125 +1670,13 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
drm_mode_vrefresh(mode) >= 50)
return MODE_CLOCK_HIGH;
- if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000))
- return MODE_CLOCK_HIGH;
-
return MODE_OK;
}
-static unsigned long long
-vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
- unsigned int bpc,
- enum vc4_hdmi_output_format fmt)
-{
- unsigned long long clock = mode->clock * 1000ULL;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- clock = clock * 2;
-
- if (fmt == VC4_HDMI_OUTPUT_YUV422)
- bpc = 8;
-
- clock = clock * bpc;
- do_div(clock, 8);
-
- return clock;
-}
-
-static int
-vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_state,
- const struct drm_display_mode *mode,
- unsigned int bpc, unsigned int fmt)
-{
- unsigned long long clock;
-
- clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
- if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock) != MODE_OK)
- return -EINVAL;
-
- vc4_state->tmds_char_rate = clock;
-
- return 0;
-}
-
-static int
-vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_state,
- const struct drm_display_mode *mode,
- unsigned int bpc)
-{
- struct drm_device *dev = vc4_hdmi->connector.dev;
- const struct drm_connector *connector = &vc4_hdmi->connector;
- const struct drm_display_info *info = &connector->display_info;
- unsigned int format;
-
- drm_dbg(dev, "Trying with an RGB output\n");
-
- format = VC4_HDMI_OUTPUT_RGB;
- if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
- int ret;
-
- ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
- mode, bpc, format);
- if (!ret) {
- vc4_state->output_format = format;
- return 0;
- }
- }
-
- drm_dbg(dev, "Failed, Trying with an YUV422 output\n");
-
- format = VC4_HDMI_OUTPUT_YUV422;
- if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
- int ret;
-
- ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
- mode, bpc, format);
- if (!ret) {
- vc4_state->output_format = format;
- return 0;
- }
- }
-
- drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n");
-
- return -EINVAL;
-}
-
-static int
-vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_state,
- const struct drm_display_mode *mode)
-{
- struct drm_device *dev = vc4_hdmi->connector.dev;
- struct drm_connector_state *conn_state = &vc4_state->base;
- unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12);
- unsigned int bpc;
- int ret;
-
- for (bpc = max_bpc; bpc >= 8; bpc -= 2) {
- drm_dbg(dev, "Trying with a %d bpc output\n", bpc);
-
- ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state,
- mode, bpc);
- if (ret)
- continue;
-
- vc4_state->output_bpc = bpc;
-
- drm_dbg(dev,
- "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n",
- mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
- vc4_state->output_bpc,
- vc4_hdmi_output_fmt_str(vc4_state->output_format),
- vc4_state->tmds_char_rate);
-
- break;
- }
-
- return ret;
-}
+static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs = {
+ .tmds_char_rate_valid = vc4_hdmi_connector_clock_valid,
+ .write_infoframe = vc4_hdmi_write_infoframe,
+};
#define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
#define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL
@@ -2163,16 +1686,9 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_connector *connector = &vc4_hdmi->connector;
- struct drm_connector_state *old_conn_state =
- drm_atomic_get_old_connector_state(conn_state->state, connector);
- struct vc4_hdmi_connector_state *old_vc4_state =
- conn_state_to_vc4_hdmi_conn_state(old_conn_state);
- struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
unsigned long long tmds_char_rate = mode->clock * 1000;
unsigned long long tmds_bit_rate;
- int ret;
if (vc4_hdmi->variant->unsupported_odd_h_timings) {
if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
@@ -2208,15 +1724,6 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
tmds_char_rate = mode->clock * 1000;
}
- ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode);
- if (ret)
- return ret;
-
- /* vc4_hdmi_encoder_compute_config may have changed output_bpc and/or output_format */
- if (vc4_state->output_bpc != old_vc4_state->output_bpc ||
- vc4_state->output_format != old_vc4_state->output_format)
- crtc_state->mode_changed = true;
-
return 0;
}
@@ -2225,6 +1732,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
const struct drm_display_mode *mode)
{
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ unsigned long long rate;
if (vc4_hdmi->variant->unsupported_odd_h_timings &&
!(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
@@ -2232,7 +1740,8 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
(mode->hsync_end % 2) || (mode->htotal % 2)))
return MODE_H_ILLEGAL;
- return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, mode->clock * 1000);
+ rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
+ return vc4_hdmi_connector_clock_valid(&vc4_hdmi->connector, mode, rate);
}
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
@@ -2424,7 +1933,6 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data)
static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
{
- struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
struct device *dev = &vc4_hdmi->pdev->dev;
unsigned long flags;
int ret;
@@ -2432,7 +1940,7 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
lockdep_assert_held(&vc4_hdmi->mutex);
vc4_hdmi->audio.streaming = false;
- ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false);
+ ret = vc4_hdmi_stop_packet(vc4_hdmi, HDMI_INFOFRAME_TYPE_AUDIO, false);
if (ret)
dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
@@ -2523,7 +2031,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
{
struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
struct drm_device *drm = vc4_hdmi->connector.dev;
- struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
+ struct drm_connector *connector = &vc4_hdmi->connector;
unsigned int sample_rate = params->sample_rate;
unsigned int channels = params->channels;
unsigned long flags;
@@ -2600,8 +2108,10 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
- memcpy(&vc4_hdmi->audio.infoframe, ¶ms->cea, sizeof(params->cea));
- vc4_hdmi_set_audio_infoframe(encoder);
+ ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
+ ¶ms->cea);
+ if (ret)
+ goto out_dev_exit;
out_dev_exit:
drm_dev_exit(idx);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index 934d5d61485a..b37f1d2c3fe5 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -10,7 +10,6 @@
struct vc4_hdmi;
struct vc4_hdmi_register;
-struct vc4_hdmi_connector_state;
enum vc4_hdmi_phy_channel {
PHY_LANE_0 = 0,
@@ -76,7 +75,7 @@ struct vc4_hdmi_variant {
/* Callback to initialize the PHY according to the connector state */
void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_conn_state);
+ struct drm_connector_state *conn_state);
/* Callback to disable the PHY */
void (*phy_disable)(struct vc4_hdmi *vc4_hdmi);
@@ -110,19 +109,6 @@ struct vc4_hdmi_audio {
bool streaming;
};
-enum vc4_hdmi_output_format {
- VC4_HDMI_OUTPUT_RGB,
- VC4_HDMI_OUTPUT_YUV422,
- VC4_HDMI_OUTPUT_YUV444,
- VC4_HDMI_OUTPUT_YUV420,
-};
-
-enum vc4_hdmi_broadcast_rgb {
- VC4_HDMI_BROADCAST_RGB_AUTO,
- VC4_HDMI_BROADCAST_RGB_FULL,
- VC4_HDMI_BROADCAST_RGB_LIMITED,
-};
-
/* General HDMI hardware state. */
struct vc4_hdmi {
struct vc4_hdmi_audio audio;
@@ -135,8 +121,6 @@ struct vc4_hdmi {
struct delayed_work scrambling_work;
- struct drm_property *broadcast_rgb_property;
-
struct i2c_adapter *ddc;
void __iomem *hdmicore_regs;
void __iomem *hd_regs;
@@ -218,16 +202,17 @@ struct vc4_hdmi {
bool scdc_enabled;
/**
- * @output_bpc: Copy of @vc4_connector_state.output_bpc for use
- * outside of KMS hooks. Protected by @mutex.
+ * @output_bpc: Copy of @drm_connector_state.hdmi.output_bpc for
+ * use outside of KMS hooks. Protected by @mutex.
*/
unsigned int output_bpc;
/**
- * @output_format: Copy of @vc4_connector_state.output_format
- * for use outside of KMS hooks. Protected by @mutex.
+ * @output_format: Copy of
+ * @drm_connector_state.hdmi.output_format for use outside of
+ * KMS hooks. Protected by @mutex.
*/
- enum vc4_hdmi_output_format output_format;
+ enum hdmi_colorspace output_format;
};
#define connector_to_vc4_hdmi(_connector) \
@@ -240,25 +225,14 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder)
return container_of_const(_encoder, struct vc4_hdmi, encoder);
}
-struct vc4_hdmi_connector_state {
- struct drm_connector_state base;
- unsigned long long tmds_char_rate;
- unsigned int output_bpc;
- enum vc4_hdmi_output_format output_format;
- enum vc4_hdmi_broadcast_rgb broadcast_rgb;
-};
-
-#define conn_state_to_vc4_hdmi_conn_state(_state) \
- container_of_const(_state, struct vc4_hdmi_connector_state, base)
-
void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_conn_state);
+ struct drm_connector_state *conn_state);
void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *vc4_conn_state);
+ struct drm_connector_state *conn_state);
void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
index ec24999bf96d..1f5507fc7a03 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
@@ -128,7 +128,7 @@
#define OSCILLATOR_FREQUENCY 54000000
void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *conn_state)
+ struct drm_connector_state *conn_state)
{
unsigned long flags;
@@ -361,11 +361,11 @@ static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi)
}
void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
- struct vc4_hdmi_connector_state *conn_state)
+ struct drm_connector_state *conn_state)
{
const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings;
const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
- unsigned long long pixel_freq = conn_state->tmds_char_rate;
+ unsigned long long pixel_freq = conn_state->hdmi.tmds_char_rate;
unsigned long long vco_freq;
unsigned char word_sel;
unsigned long flags;
--
2.43.0
The inno_hdmi mode_valid implementation always return MODE_OK which is
what the core assumes when we don't have an implementation.
Let's get rid of it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index e4fe02b79a98..59b2b676b7b8 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -400,13 +400,6 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
return ret;
}
-static enum drm_mode_status
-inno_hdmi_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static int
inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY)
@@ -431,7 +424,6 @@ static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
.get_modes = inno_hdmi_connector_get_modes,
- .mode_valid = inno_hdmi_connector_mode_valid,
};
static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
--
2.43.0
We're not doing anything special in atomic_mode_set so we can simply
merge it into atomic_enable.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index cc86d273ca4a..4db18195246e 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -490,21 +490,22 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
return 0;
}
-static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode;
- struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
-
- inno_hdmi_setup(hdmi, adj_mode);
-}
-
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ conn_state = drm_atomic_get_new_connector_state(state, &hdmi->connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (WARN_ON(!crtc_state))
+ return;
+
+ inno_hdmi_setup(hdmi, &crtc_state->adjusted_mode);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
@@ -533,7 +534,6 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
.atomic_check = inno_hdmi_encoder_atomic_check,
.atomic_enable = inno_hdmi_encoder_enable,
.atomic_disable = inno_hdmi_encoder_disable,
- .atomic_mode_set = inno_hdmi_encoder_mode_set,
};
static enum drm_connector_status
--
2.43.0
The code to upload infoframes to the controller uses a weird construct
which, based on the previous function call return code, will either
disable or enable that infoframe.
In order to get rid of that argument, let's split the function to
disable the infoframe into a separate function and make it obvious what
we are doing in the error path.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 47 ++++++++++++++++++++++++------------
1 file changed, 31 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 9c64f94ede6f..b68cd3a6a0d1 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -156,34 +156,44 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
-static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
- union hdmi_infoframe *frame, u32 frame_index)
+static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, u32 frame_index)
{
struct drm_connector *connector = &hdmi->connector;
if (frame_index != INFOFRAME_AVI) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", frame_index);
- return 0;
+ return;
}
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+}
- if (setup_rc >= 0) {
- u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
- ssize_t rc, i;
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
+ union hdmi_infoframe *frame, u32 frame_index)
+{
+ struct drm_connector *connector = &hdmi->connector;
+ u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+ ssize_t rc, i;
- rc = hdmi_infoframe_pack(frame, packed_frame,
- sizeof(packed_frame));
- if (rc < 0)
- return rc;
-
- for (i = 0; i < rc; i++)
- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
- packed_frame[i]);
+ if (frame_index != INFOFRAME_AVI) {
+ drm_err(connector->dev,
+ "Unsupported infoframe type: %u\n", frame_index);
+ return 0;
}
- return setup_rc;
+ inno_hdmi_disable_frame(hdmi, frame_index);
+
+ rc = hdmi_infoframe_pack(frame, packed_frame,
+ sizeof(packed_frame));
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i < rc; i++)
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+ packed_frame[i]);
+
+ return 0;
}
static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
@@ -195,9 +205,14 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
&hdmi->connector,
mode);
+ if (rc) {
+ inno_hdmi_disable_frame(hdmi, INFOFRAME_AVI);
+ return rc;
+ }
+
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI);
+ return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_AVI);
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
--
2.43.0
We're not doing anything special in atomic_mode_set so we can simply
merge it into atomic_enable.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 38 +++++++++++++---------------------
1 file changed, 14 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 799a26215cc2..bae69d696765 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -103,33 +103,11 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
struct drm_display_info *display = &hdmi->connector.display_info;
+ unsigned int x, y;
u32 val = 0;
DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
- clk_prepare_enable(hdmi->tmds_clk);
-
- sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
- writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
-
- val = SUN4I_HDMI_VID_CTRL_ENABLE;
- if (display->is_hdmi)
- val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
-
- writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
-}
-
-static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- const struct drm_display_mode *mode = &crtc_state->mode;
- struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- unsigned int x, y;
- u32 val;
-
clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000);
clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000);
@@ -181,6 +159,19 @@ static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
+
+ clk_prepare_enable(hdmi->tmds_clk);
+
+ sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
+ val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
+ val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
+ writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
+
+ val = SUN4I_HDMI_VID_CTRL_ENABLE;
+ if (display->is_hdmi)
+ val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
+
+ writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
}
static enum drm_mode_status sun4i_hdmi_mode_valid(struct drm_encoder *encoder,
@@ -206,7 +197,6 @@ static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
.atomic_check = sun4i_hdmi_atomic_check,
.atomic_disable = sun4i_hdmi_disable,
.atomic_enable = sun4i_hdmi_enable,
- .atomic_mode_set = sun4i_hdmi_mode_set,
.mode_valid = sun4i_hdmi_mode_valid,
};
--
2.43.0
The coeff_csc matrix isn't used anymore, let's remove it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 70 ------------------------------------
1 file changed, 70 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 35f44e556fcf..e4fe02b79a98 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -64,76 +64,6 @@ static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
return container_of(connector, struct inno_hdmi, connector);
}
-static const char coeff_csc[][24] = {
- /*
- * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
- * R = 1.164*Y + 1.596*V - 204
- * G = 1.164*Y - 0.391*U - 0.813*V + 154
- * B = 1.164*Y + 2.018*U - 258
- */
- {
- 0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
- 0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
- 0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
- },
- /*
- * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
- * R = Y + 1.402*V - 248
- * G = Y - 0.344*U - 0.714*V + 135
- * B = Y + 1.772*U - 227
- */
- {
- 0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
- 0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
- 0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
- },
- /*
- * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
- * R = 1.164*Y + 1.793*V - 248
- * G = 1.164*Y - 0.213*U - 0.534*V + 77
- * B = 1.164*Y + 2.115*U - 289
- */
- {
- 0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
- 0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
- 0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
- },
-
- /*
- * RGB2YUV:601 SD mode:
- * Cb = -0.291G - 0.148R + 0.439B + 128
- * Y = 0.504G + 0.257R + 0.098B + 16
- * Cr = -0.368G + 0.439R - 0.071B + 128
- */
- {
- 0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
- 0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
- 0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
- },
- /*
- * RGB2YUV:709 HD mode:
- * Cb = - 0.338G - 0.101R + 0.439B + 128
- * Y = 0.614G + 0.183R + 0.062B + 16
- * Cr = - 0.399G + 0.439R - 0.040B + 128
- */
- {
- 0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
- 0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
- 0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
- },
- /*
- * RGB[0:255]2RGB[16:235]:
- * R' = R x (235-16)/255 + 16;
- * G' = G x (235-16)/255 + 16;
- * B' = B x (235-16)/255 + 16;
- */
- {
- 0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
- 0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
- },
-};
-
static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
{
return readl_relaxed(hdmi->regs + (offset) * 0x04);
--
2.43.0
The new HDMI connector infrastructure allows to remove some boilerplate,
especially to generate infoframes. Let's switch to it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 80 ++++++++++++++++++++++------------
1 file changed, 51 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index b7cf369b1906..8a9106a39f23 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -36,30 +36,24 @@
#define drm_connector_to_sun4i_hdmi(c) \
container_of_const(c, struct sun4i_hdmi, connector)
-static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
- struct drm_display_mode *mode)
+static int sun4i_hdmi_write_infoframe(struct drm_connector *connector,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len)
{
- struct hdmi_avi_infoframe frame;
- u8 buffer[17];
- int i, ret;
+ struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
+ int i;
- ret = drm_hdmi_avi_infoframe_from_display_mode(&frame,
- &hdmi->connector, mode);
- if (ret < 0) {
- DRM_ERROR("Failed to get infoframes from mode\n");
- return ret;
+ if (type != HDMI_INFOFRAME_TYPE_AVI) {
+ drm_err(connector->dev,
+ "Unsupported infoframe type: %u\n", type);
+ return 0;
}
- ret = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
- if (ret < 0) {
- DRM_ERROR("Failed to pack infoframes\n");
- return ret;
- }
-
- for (i = 0; i < sizeof(buffer); i++)
+ for (i = 0; i < len; i++)
writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i));
return 0;
+
}
static void sun4i_hdmi_disable(struct drm_encoder *encoder,
@@ -82,14 +76,18 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
{
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- struct drm_display_info *display = &hdmi->connector.display_info;
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_display_info *display = &connector->display_info;
+ struct drm_connector_state *conn_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ unsigned long long tmds_rate = conn_state->hdmi.tmds_char_rate;
unsigned int x, y;
u32 val = 0;
DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
- clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000);
- clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000);
+ clk_set_rate(hdmi->mod_clk, tmds_rate);
+ clk_set_rate(hdmi->tmds_clk, tmds_rate);
/* Set input sync enable */
writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC,
@@ -142,7 +140,8 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
clk_prepare_enable(hdmi->tmds_clk);
- sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+
val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
@@ -195,7 +194,7 @@ static int sun4i_hdmi_connector_atomic_check(struct drm_connector *connector,
enum drm_mode_status status;
status = sun4i_hdmi_connector_clock_valid(connector, mode,
- mode->clock * 1000);
+ conn_state->hdmi.tmds_char_rate);
if (status != MODE_OK)
return -EINVAL;
@@ -206,8 +205,11 @@ static enum drm_mode_status
sun4i_hdmi_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- return sun4i_hdmi_connector_clock_valid(connector, mode,
- mode->clock * 1000);
+ unsigned long long rate =
+ drm_connector_hdmi_compute_mode_clock(mode, 8,
+ HDMI_COLORSPACE_RGB);
+
+ return sun4i_hdmi_connector_clock_valid(connector, mode, rate);
}
static int sun4i_hdmi_get_modes(struct drm_connector *connector)
@@ -253,6 +255,11 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev)
return ddc;
}
+static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = {
+ .tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid,
+ .write_infoframe = sun4i_hdmi_write_infoframe,
+};
+
static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {
.atomic_check = sun4i_hdmi_connector_atomic_check,
.mode_valid = sun4i_hdmi_connector_mode_valid,
@@ -274,11 +281,17 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
return connector_status_connected;
}
+static void sun4i_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 sun4i_hdmi_connector_funcs = {
.detect = sun4i_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
+ .reset = sun4i_hdmi_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
@@ -637,10 +650,19 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
drm_connector_helper_add(&hdmi->connector,
&sun4i_hdmi_connector_helper_funcs);
- ret = drm_connector_init_with_ddc(drm, &hdmi->connector,
- &sun4i_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA,
- hdmi->ddc_i2c);
+ ret = drmm_connector_hdmi_init(drm, &hdmi->connector,
+ /*
+ * NOTE: Those are likely to be
+ * wrong, but I couldn't find the
+ * actual ones in the BSP.
+ */
+ "AW", "HDMI",
+ &sun4i_hdmi_connector_funcs,
+ &sun4i_hdmi_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ hdmi->ddc_i2c,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
if (ret) {
dev_err(dev,
"Couldn't initialise the HDMI connector\n");
--
2.43.0
The inno_hdmi encoder still uses the !atomic variants of enable, disable
and modeset. Convert to their atomic equivalents.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 4f5844fc115a..cc86d273ca4a 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -491,22 +491,25 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
}
static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
+ struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode;
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
inno_hdmi_setup(hdmi, adj_mode);
}
-static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
-static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
@@ -527,10 +530,10 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
}
static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
- .enable = inno_hdmi_encoder_enable,
- .disable = inno_hdmi_encoder_disable,
- .mode_set = inno_hdmi_encoder_mode_set,
- .atomic_check = inno_hdmi_encoder_atomic_check,
+ .atomic_check = inno_hdmi_encoder_atomic_check,
+ .atomic_enable = inno_hdmi_encoder_enable,
+ .atomic_disable = inno_hdmi_encoder_disable,
+ .atomic_mode_set = inno_hdmi_encoder_mode_set,
};
static enum drm_connector_status
--
2.43.0
The inno_hdmi driver relies on its own internal infoframe type matching
the hardware.
This works fine, but in order to make further reworks easier, let's
switch to the HDMI spec definition of those types.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index b68cd3a6a0d1..77b530f377c0 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -156,33 +156,34 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
-static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, u32 frame_index)
+static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
+ enum hdmi_infoframe_type type)
{
struct drm_connector *connector = &hdmi->connector;
- if (frame_index != INFOFRAME_AVI) {
+ if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
- "Unsupported infoframe type: %u\n", frame_index);
+ "Unsupported infoframe type: %u\n", type);
return;
}
- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
}
static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
- union hdmi_infoframe *frame, u32 frame_index)
+ union hdmi_infoframe *frame, enum hdmi_infoframe_type type)
{
struct drm_connector *connector = &hdmi->connector;
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;
- if (frame_index != INFOFRAME_AVI) {
+ if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
- "Unsupported infoframe type: %u\n", frame_index);
+ "Unsupported infoframe type: %u\n", type);
return 0;
}
- inno_hdmi_disable_frame(hdmi, frame_index);
+ inno_hdmi_disable_frame(hdmi, type);
rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
@@ -206,13 +207,13 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
&hdmi->connector,
mode);
if (rc) {
- inno_hdmi_disable_frame(hdmi, INFOFRAME_AVI);
+ inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_AVI);
return rc;
}
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
- return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_AVI);
+ return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI);
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
--
2.43.0
The CSC_* enum has no users left, so let's remove it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index c342bc8b3a23..f05417c6b637 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -66,15 +66,6 @@ static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
return container_of(connector, struct inno_hdmi, connector);
}
-enum {
- CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
- CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
- CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
- CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
- CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
- CSC_RGB_0_255_TO_RGB_16_235_8BIT,
-};
-
static const char coeff_csc[][24] = {
/*
* YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
--
2.43.0
The mode_fixup implementation doesn't do anything, so we can simply
remove it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 6e5b922a121e..cc48cbf85f31 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -517,13 +517,6 @@ static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
}
-static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
-{
- return true;
-}
-
static int
inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
@@ -540,7 +533,6 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
.enable = inno_hdmi_encoder_enable,
.disable = inno_hdmi_encoder_disable,
- .mode_fixup = inno_hdmi_encoder_mode_fixup,
.mode_set = inno_hdmi_encoder_mode_set,
.atomic_check = inno_hdmi_encoder_atomic_check,
};
--
2.43.0
There has been some discussions recently about the infoframes sent by
drivers and if they were properly generated.
In parallel, there's been some interest in creating an infoframe-decode
tool similar to edid-decode.
Both would be much easier if we were to expose the infoframes programmed
in the hardware. It won't be perfect since we have no guarantee that
it's actually what goes through the wire, but it's the best we can do.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_debugfs.c | 110 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 110 insertions(+)
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 02e7481758c0..dab54f14d313 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -522,6 +522,114 @@ static const struct file_operations drm_connector_fops = {
.write = connector_write
};
+struct debugfs_wrapper {
+ struct drm_connector *connector;
+ struct drm_connector_hdmi_infoframe *frame;
+};
+
+#define HDMI_MAX_INFOFRAME_SIZE 29
+
+static ssize_t
+infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos)
+{
+ const struct debugfs_wrapper *wrapper = filp->private_data;
+ struct drm_connector *connector = wrapper->connector;
+ struct drm_connector_hdmi_infoframe *infoframe = wrapper->frame;
+ union hdmi_infoframe *frame = &infoframe->data;
+ u8 buf[HDMI_MAX_INFOFRAME_SIZE];
+ ssize_t len = 0;
+
+ mutex_lock(&connector->hdmi.infoframes.lock);
+
+ if (!infoframe->set)
+ goto out;
+
+ len = hdmi_infoframe_pack(frame, buf, sizeof(buf));
+ if (len < 0)
+ goto out;
+
+ len = simple_read_from_buffer(ubuf, count, ppos, buf, len);
+
+out:
+ mutex_unlock(&connector->hdmi.infoframes.lock);
+ return len;
+}
+
+static const struct file_operations infoframe_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = infoframe_read,
+};
+
+static int create_hdmi_infoframe_file(struct drm_connector *connector,
+ struct dentry *parent,
+ const char *filename,
+ struct drm_connector_hdmi_infoframe *frame)
+{
+ struct drm_device *dev = connector->dev;
+ struct debugfs_wrapper *wrapper;
+ struct dentry *file;
+
+ wrapper = drmm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
+ if (!wrapper)
+ return -ENOMEM;
+
+ wrapper->connector = connector;
+ wrapper->frame = frame;
+
+ file = debugfs_create_file(filename, 0400, parent, wrapper, &infoframe_fops);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ return 0;
+}
+
+#define CREATE_HDMI_INFOFRAME_FILE(c, p, i) \
+ create_hdmi_infoframe_file(c, p, #i, &(c)->hdmi.infoframes.i)
+
+static int create_hdmi_infoframe_files(struct drm_connector *connector,
+ struct dentry *parent)
+{
+ int ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, audio);
+ if (ret)
+ return ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, avi);
+ if (ret)
+ return ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, hdr_drm);
+ if (ret)
+ return ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, spd);
+ if (ret)
+ return ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, hdmi);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void hdmi_debugfs_add(struct drm_connector *connector)
+{
+ struct dentry *dir;
+
+ if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+ connector->connector_type == DRM_MODE_CONNECTOR_HDMIB))
+ return;
+
+ dir = debugfs_create_dir("infoframes", connector->debugfs_entry);
+ if (IS_ERR(dir))
+ return;
+
+ create_hdmi_infoframe_files(connector, dir);
+}
+
void drm_debugfs_connector_add(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -549,6 +657,8 @@ void drm_debugfs_connector_add(struct drm_connector *connector)
debugfs_create_file("output_bpc", 0444, root, connector,
&output_bpc_fops);
+ hdmi_debugfs_add(connector);
+
if (connector->funcs->debugfs_init)
connector->funcs->debugfs_init(connector, root);
}
--
2.43.0
Even though we were rolling our own custom state for the vc4 HDMI
controller driver, we were still using the generic helper to destroy
that state.
It was mostly working since the underlying state is the first member of
our state so the pointers are probably equal in all relevant cases, but
it's still fragile so let's fix this properly.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 25c9c71256d3..f05e2c95a60d 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -672,11 +672,21 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
return &new_state->base;
}
+static void vc4_hdmi_connector_destroy_state(struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ struct vc4_hdmi_connector_state *vc4_state =
+ conn_state_to_vc4_hdmi_conn_state(state);
+
+ __drm_atomic_helper_connector_destroy_state(state);
+ kfree(vc4_state);
+}
+
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = vc4_hdmi_connector_reset,
.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_destroy_state = vc4_hdmi_connector_destroy_state,
.atomic_get_property = vc4_hdmi_connector_get_property,
.atomic_set_property = vc4_hdmi_connector_set_property,
};
--
2.43.0
The colorimetry field of hdmi_data_info is not used anywhere so we can
get rid of it. This was the last field left in that structure so we can
get rid of it too.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 0c6c550e0ce7..c342bc8b3a23 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -27,10 +27,6 @@
#include "inno_hdmi.h"
-struct hdmi_data_info {
- unsigned int colorimetry;
-};
-
struct inno_hdmi_i2c {
struct i2c_adapter adap;
@@ -56,8 +52,6 @@ struct inno_hdmi {
struct i2c_adapter *ddc;
unsigned int tmds_rate;
-
- struct hdmi_data_info hdmi_data;
};
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -380,15 +374,6 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
struct drm_display_info *display = &hdmi->connector.display_info;
- u8 vic = drm_match_cea_mode(mode);
-
- if ((vic == 6) || (vic == 7) ||
- (vic == 21) || (vic == 22) ||
- (vic == 2) || (vic == 3) ||
- (vic == 17) || (vic == 18))
- hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
- else
- hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
/* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
--
2.43.0
The driver maintains a copy of the adjusted mode but doesn't use it
anywhere. Remove it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index cc48cbf85f31..4f5844fc115a 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -62,7 +62,6 @@ struct inno_hdmi {
unsigned int tmds_rate;
struct hdmi_data_info hdmi_data;
- struct drm_display_mode previous_mode;
};
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -498,9 +497,6 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
inno_hdmi_setup(hdmi, adj_mode);
-
- /* Store the display mode for plugin/DPMS poweron events */
- drm_mode_copy(&hdmi->previous_mode, adj_mode);
}
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
--
2.43.0
The HDMI vendor infoframe is only meant to be sent with 4k60 modes and
higher, but the controller doesn't support them. Let's drop them from
the kernel.
Suggested-by: Johan Jonker <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 35 ++++++++++-------------------------
1 file changed, 10 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 59b2b676b7b8..9c64f94ede6f 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -157,11 +157,15 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
}
static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
- union hdmi_infoframe *frame, u32 frame_index,
- u32 mask, u32 disable, u32 enable)
+ union hdmi_infoframe *frame, u32 frame_index)
{
- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+ struct drm_connector *connector = &hdmi->connector;
+
+ if (frame_index != INFOFRAME_AVI) {
+ drm_err(connector->dev,
+ "Unsupported infoframe type: %u\n", frame_index);
+ return 0;
+ }
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
@@ -177,28 +181,11 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
for (i = 0; i < rc; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
packed_frame[i]);
-
- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
}
return setup_rc;
}
-static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
-{
- union hdmi_infoframe frame;
- int rc;
-
- rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
- &hdmi->connector,
- mode);
-
- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
- m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
-}
-
static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
@@ -210,7 +197,7 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
mode);
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+ return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI);
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
@@ -306,10 +293,8 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
inno_hdmi_config_video_csc(hdmi);
- if (display->is_hdmi) {
+ if (display->is_hdmi)
inno_hdmi_config_video_avi(hdmi, mode);
- inno_hdmi_config_video_vsi(hdmi, mode);
- }
/*
* When IP controller have configured to an accurate video
--
2.43.0
The driver has a lot of logic to deal with multiple input formats, but
hardcodes it to RGB. This means that most of that code has been dead
code, so let's get rid of it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 39 +++++++++---------------------------
1 file changed, 10 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 3908dc09d85f..e0696ab16da3 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"
struct hdmi_data_info {
- unsigned int enc_in_format;
unsigned int enc_out_format;
unsigned int colorimetry;
};
@@ -328,47 +327,30 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
- if (data->enc_in_format == data->enc_out_format) {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
- (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
- value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+ if (HDMI_COLORSPACE_RGB == data->enc_out_format) {
+ value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
- m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
- v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
- v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
- return 0;
- }
+ hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+ m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+ v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+ v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+ return 0;
}
if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
- (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+ if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
- } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
- (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
- csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
- auto_csc = AUTO_CSC_ENABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_DISABLE;
}
} else {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
- (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+ if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
- } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
- (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
- csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
- auto_csc = AUTO_CSC_ENABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_DISABLE;
}
}
@@ -443,7 +425,6 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_info *display = &hdmi->connector.display_info;
u8 vic = drm_match_cea_mode(mode);
- hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
if ((vic == 6) || (vic == 7) ||
--
2.43.0
Now that we have a plane create helper for kunit mocked drivers, let's
convert to it in vc4.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 34 +++++++-----------------------
1 file changed, 8 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
index 973f5f929097..14357db82238 100644
--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
@@ -1,43 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
-#include <drm/drm_atomic_state_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_kunit_helpers.h>
#include <drm/drm_plane.h>
#include <kunit/test.h>
#include "vc4_mock.h"
-static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = {
-};
-
-static const struct drm_plane_funcs vc4_dummy_plane_funcs = {
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .reset = drm_atomic_helper_plane_reset,
-};
-
-static const uint32_t vc4_dummy_plane_formats[] = {
- DRM_FORMAT_XRGB8888,
-};
-
struct drm_plane *vc4_dummy_plane(struct kunit *test, struct drm_device *drm,
enum drm_plane_type type)
{
struct drm_plane *plane;
- plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0,
- 0,
- &vc4_dummy_plane_funcs,
- vc4_dummy_plane_formats,
- ARRAY_SIZE(vc4_dummy_plane_formats),
- NULL,
- DRM_PLANE_TYPE_PRIMARY,
- NULL);
+ KUNIT_ASSERT_EQ(test, type, DRM_PLANE_TYPE_PRIMARY);
+
+ plane = drm_kunit_helper_create_primary_plane(test, drm,
+ NULL,
+ NULL,
+ NULL, 0,
+ NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
- drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs);
-
return plane;
}
--
2.43.0
The sink_has_audio flag is not used anywhere in the driver so let's get
rid of it. It's redundant with drm_display_info.has_audio anyway.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 294f0d442c0c..3908dc09d85f 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"
struct hdmi_data_info {
- bool sink_has_audio;
unsigned int enc_in_format;
unsigned int enc_out_format;
unsigned int colorimetry;
@@ -554,7 +553,6 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
edid = drm_get_edid(connector, hdmi->ddc);
if (edid) {
- hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
--
2.43.0
Most HDMI drivers have some code to calculate the TMDS character rate,
usually to adjust an internal clock to match what the mode requires.
Since the TMDS character rates mostly depends on the resolution, whether
we need to repeat pixels or not, the bpc count and the format, we can
now derive it from the HDMI connector state that stores all those infos
and remove the duplication from drivers.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/drm_atomic.c | 1 +
drivers/gpu/drm/drm_atomic_state_helper.c | 44 +++++
.../gpu/drm/tests/drm_atomic_state_helper_test.c | 169 ++++++++++++++++
drivers/gpu/drm/tests/drm_kunit_edid.h | 216 +++++++++++++++++++++
include/drm/drm_connector.h | 5 +
5 files changed, 435 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 7aaa2a4d70d9..4f6493f91eed 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1150,6 +1150,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
drm_printf(p, "\toutput_format=%s\n",
drm_hdmi_connector_get_output_format_name(state->hdmi.output_format));
+ drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate);
}
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 92e1b087c3d0..74bc3cc53c2d 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -682,6 +682,41 @@ static bool hdmi_is_full_range(const struct drm_connector *connector,
return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false;
}
+static enum drm_mode_status
+hdmi_clock_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long clock)
+{
+ const struct drm_display_info *info = &connector->display_info;
+
+ if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static int
+hdmi_compute_clock(const struct drm_connector *connector,
+ struct drm_connector_state *state,
+ const struct drm_display_mode *mode,
+ unsigned int bpc, enum hdmi_colorspace fmt)
+{
+ enum drm_mode_status status;
+ unsigned long long clock;
+
+ clock = drm_connector_hdmi_compute_mode_clock(mode, bpc, fmt);
+ if (!clock)
+ return -EINVAL;
+
+ status = hdmi_clock_valid(connector, mode, clock);
+ if (status != MODE_OK)
+ return -EINVAL;
+
+ state->hdmi.tmds_char_rate = clock;
+
+ return 0;
+}
+
/**
* drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
* @connector: DRM Connector
@@ -701,9 +736,18 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
drm_atomic_get_old_connector_state(state, connector);
struct drm_connector_state *new_state =
drm_atomic_get_new_connector_state(state, connector);
+ const struct drm_display_mode *mode =
+ connector_state_get_mode(new_state);
+ int ret;
new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
+ ret = hdmi_compute_clock(connector, new_state, mode,
+ new_state->hdmi.output_bpc,
+ new_state->hdmi.output_format);
+ if (ret)
+ return ret;
+
if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
old_state->hdmi.output_bpc != new_state->hdmi.output_bpc ||
old_state->hdmi.output_format != new_state->hdmi.output_format) {
diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
index 4e2ec436987b..d76fafb91025 100644
--- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
@@ -70,6 +70,9 @@ static int light_up_connector(struct kunit *test,
conn_state = drm_atomic_get_connector_state(state, connector);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+ conn_state->hdmi.output_bpc = connector->max_bpc;
+ conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB;
+
ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -813,6 +816,146 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
}
+/*
+ * Test that when doing a commit which would use RGB 8bpc, the TMDS
+ * clock rate stored in the connector state is equal to the mode clock
+ */
+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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
+ 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);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
+/*
+ * Test that when doing a commit which would use RGB 10bpc, the TMDS
+ * clock rate stored in the connector state is equal to 1.25 times the
+ * mode pixel clock
+ */
+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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 10);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
+/*
+ * Test that when doing a commit which would use RGB 12bpc, the TMDS
+ * clock rate stored in the connector state is equal to 1.5 times the
+ * mode pixel clock
+ */
+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_atomic_helper_connector_hdmi_init(test,
+ BIT(HDMI_COLORSPACE_RGB),
+ 12);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ ret = set_connector_edid(test, conn,
+ test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz,
+ ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ctx = drm_kunit_helper_acquire_ctx_alloc(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
+ 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);
+}
+
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),
@@ -824,6 +967,9 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
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_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),
/*
* TODO: We should have tests to check that a change in the
* format triggers a CRTC mode change just like we do for the
@@ -955,12 +1101,35 @@ static void drm_test_check_format_value(struct kunit *test)
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
}
+/*
+ * Test that the value of the output format property out of reset is set
+ * to 0, and will be computed at atomic_check time.
+ */
+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_atomic_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),
{ }
};
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h
index 2bba316de064..24f3377ef0f0 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.h
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.h
@@ -103,4 +103,220 @@ const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
0x00, 0x00, 0x00, 0xd0
};
+/*
+ * 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 1a 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 7a
+ *
+ * 02 03 1b b1 e3 05 00 20 41 10 e2 00 ca 6d 03 0c
+ * 00 12 34 78 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 a8
+ *
+ * ----------------
+ *
+ * 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
+ * Undefined display color type
+ * 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: 0x7a
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * 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: Selectable (via AVI YQ)
+ * 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
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 200 MHz
+ * Extended HDMI video details:
+ * Checksum: 0xa8 Unused space in Extension Block: 100 bytes
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_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,
+ 0x1a, 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, 0x1e, 0x46, 0x0f, 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, 0x7a, 0x02, 0x03, 0x1b, 0xb1,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x6d, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x78, 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, 0xa8
+};
+
+/*
+ * 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 0a 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 8a
+ *
+ * 02 03 1b b1 e3 05 00 20 41 10 e2 00 ca 6d 03 0c
+ * 00 12 34 78 44 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 8c
+ *
+ * ----------------
+ *
+ * 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
+ * RGB color 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: 0x8a
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * 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: Selectable (via AVI YQ)
+ * 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
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 340 MHz
+ * Extended HDMI video details:
+ * Checksum: 0x8c Unused space in Extension Block: 100 bytes
+ */
+const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
+ 0x0a, 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, 0x1e, 0x46, 0x0f, 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, 0x8a, 0x02, 0x03, 0x1b, 0xb1,
+ 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x6d, 0x03, 0x0c,
+ 0x00, 0x12, 0x34, 0x78, 0x44, 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, 0x8c
+};
+
#endif // DRM_KUNIT_EDID_H_
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 0cc5a8732664..59016d9c17f5 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1085,6 +1085,11 @@ struct drm_connector_state {
* @output_format: Pixel format to output in.
*/
enum hdmi_colorspace output_format;
+
+ /**
+ * @tmds_char_rate: TMDS Character Rate, in Hz.
+ */
+ unsigned long long tmds_char_rate;
} hdmi;
};
--
2.43.0
The vc4_dummy_plane structure is an exact equivalent to vc4_plane, so we
don't need it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/vc4/tests/vc4_mock.c | 6 ++----
drivers/gpu/drm/vc4/tests/vc4_mock.h | 9 ++-------
drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 14 +++++---------
3 files changed, 9 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c
index 63ca46f4cb35..1c1be39141ba 100644
--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
@@ -109,16 +109,14 @@ static const struct vc4_mock_desc vc5_mock =
static int __build_one_pipe(struct kunit *test, struct drm_device *drm,
const struct vc4_mock_pipe_desc *pipe)
{
- struct vc4_dummy_plane *dummy_plane;
struct drm_plane *plane;
struct vc4_dummy_crtc *dummy_crtc;
struct drm_crtc *crtc;
unsigned int i;
- dummy_plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);
+ plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
- plane = &dummy_plane->plane.base;
dummy_crtc = vc4_mock_pv(test, drm, plane, pipe->data);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_crtc);
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h
index 2d0b339bd9f3..002b6218960c 100644
--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
@@ -21,13 +21,8 @@ struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test,
return NULL;
}
-struct vc4_dummy_plane {
- struct vc4_plane plane;
-};
-
-struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
- struct drm_device *drm,
- enum drm_plane_type type);
+struct drm_plane *vc4_dummy_plane(struct kunit *test, struct drm_device *drm,
+ enum drm_plane_type type);
struct vc4_dummy_crtc {
struct vc4_crtc crtc;
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
index 62b18f5f41db..973f5f929097 100644
--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
@@ -22,15 +22,12 @@ static const uint32_t vc4_dummy_plane_formats[] = {
DRM_FORMAT_XRGB8888,
};
-struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
- struct drm_device *drm,
- enum drm_plane_type type)
+struct drm_plane *vc4_dummy_plane(struct kunit *test, struct drm_device *drm,
+ enum drm_plane_type type)
{
- struct vc4_dummy_plane *dummy_plane;
struct drm_plane *plane;
- dummy_plane = drmm_universal_plane_alloc(drm,
- struct vc4_dummy_plane, plane.base,
+ plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0,
0,
&vc4_dummy_plane_funcs,
vc4_dummy_plane_formats,
@@ -38,10 +35,9 @@ struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
NULL,
DRM_PLANE_TYPE_PRIMARY,
NULL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
- plane = &dummy_plane->plane.base;
drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs);
- return dummy_plane;
+ return plane;
}
--
2.43.0
The new HDMI connector infrastructure allows to remove some boilerplate,
especially to generate infoframes. Let's switch to it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 86 ++++++++++++++++++------------------
1 file changed, 42 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 0f3ec6eaf270..97fb4268bd7f 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -155,26 +155,29 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
-static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
- enum hdmi_infoframe_type type)
+static int inno_hdmi_disable_frame(struct drm_connector *connector,
+ enum hdmi_infoframe_type type)
{
- struct drm_connector *connector = &hdmi->connector;
+ struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", type);
- return;
+ return 0;
}
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+
+ return 0;
}
-static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
- union hdmi_infoframe *frame, enum hdmi_infoframe_type type)
+static int inno_hdmi_upload_frame(struct drm_connector *connector,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len)
{
- struct drm_connector *connector = &hdmi->connector;
+ struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
- ssize_t rc, i;
+ ssize_t i;
if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
@@ -182,38 +185,19 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
return 0;
}
- inno_hdmi_disable_frame(hdmi, type);
+ inno_hdmi_disable_frame(connector, type);
- rc = hdmi_infoframe_pack(frame, packed_frame,
- sizeof(packed_frame));
- if (rc < 0)
- return rc;
-
- for (i = 0; i < rc; i++)
+ for (i = 0; i < len; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
packed_frame[i]);
return 0;
}
-static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
-{
- union hdmi_infoframe frame;
- int rc;
-
- rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
- &hdmi->connector,
- mode);
- if (rc) {
- inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_AVI);
- return rc;
- }
-
- frame.avi.colorspace = HDMI_COLORSPACE_RGB;
-
- return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI);
-}
+static const struct drm_connector_hdmi_funcs inno_hdmi_hdmi_connector_funcs = {
+ .clear_infoframe = inno_hdmi_disable_frame,
+ .write_infoframe = inno_hdmi_upload_frame,
+};
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{
@@ -292,9 +276,12 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
}
static int inno_hdmi_setup(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
+ struct drm_crtc_state *new_crtc_state,
+ struct drm_connector_state *new_conn_state)
{
- struct drm_display_info *display = &hdmi->connector.display_info;
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_display_info *display = &connector->display_info;
+ struct drm_display_mode *mode = &new_crtc_state->adjusted_mode;
/* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
@@ -308,8 +295,8 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
inno_hdmi_config_video_csc(hdmi);
- if (display->is_hdmi)
- inno_hdmi_config_video_avi(hdmi, mode);
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector,
+ new_conn_state->state);
/*
* When IP controller have configured to an accurate video
@@ -317,7 +304,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel
* clock rate, and reconfigure the DDC clock.
*/
- inno_hdmi_i2c_init(hdmi, mode->clock * 1000);
+ inno_hdmi_i2c_init(hdmi, new_conn_state->hdmi.tmds_char_rate);
/* Unmute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
@@ -341,7 +328,7 @@ static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
if (WARN_ON(!crtc_state))
return;
- inno_hdmi_setup(hdmi, &crtc_state->adjusted_mode);
+ inno_hdmi_setup(hdmi, crtc_state, conn_state);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
@@ -407,6 +394,12 @@ inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
}
+static void inno_hdmi_connector_reset(struct drm_connector *connector)
+{
+ drm_atomic_helper_connector_reset(connector);
+ __drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
+}
+
static void inno_hdmi_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
@@ -417,12 +410,13 @@ static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
.fill_modes = inno_hdmi_probe_single_connector_modes,
.detect = inno_hdmi_connector_detect,
.destroy = inno_hdmi_connector_destroy,
- .reset = drm_atomic_helper_connector_reset,
+ .reset = inno_hdmi_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+ .atomic_check = drm_atomic_helper_connector_hdmi_check,
.get_modes = inno_hdmi_connector_get_modes,
};
@@ -449,10 +443,14 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
drm_connector_helper_add(&hdmi->connector,
&inno_hdmi_connector_helper_funcs);
- drm_connector_init_with_ddc(drm, &hdmi->connector,
- &inno_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA,
- hdmi->ddc);
+ drmm_connector_hdmi_init(drm, &hdmi->connector,
+ "Rockchip", "Inno HDMI",
+ &inno_hdmi_connector_funcs,
+ &inno_hdmi_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ hdmi->ddc,
+ BIT(HDMI_COLORSPACE_RGB),
+ 8);
drm_connector_attach_encoder(&hdmi->connector, encoder);
--
2.43.0
The drm_dev field in the inno_hdmi struct stores a pointer to the DRM
device but is never used anywhere in the driver. Let's remove it.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 77b530f377c0..0f3ec6eaf270 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -39,7 +39,6 @@ struct inno_hdmi_i2c {
struct inno_hdmi {
struct device *dev;
- struct drm_device *drm_dev;
int irq;
struct clk *pclk;
@@ -648,7 +647,6 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
return -ENOMEM;
hdmi->dev = dev;
- hdmi->drm_dev = drm;
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hdmi->regs))
--
2.43.0
atomic_check and mode_valid do not check for the same things which can
lead to surprising result if the userspace commits a mode that didn't go
through mode_valid. Let's merge the two implementations into a function
called by both.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 74 +++++++++++++++++++++-------------
1 file changed, 47 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index c276d984da6b..b7cf369b1906 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -62,18 +62,6 @@ static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
return 0;
}
-static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_display_mode *mode = &crtc_state->mode;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- return -EINVAL;
-
- return 0;
-}
-
static void sun4i_hdmi_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
@@ -166,31 +154,61 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
}
-static enum drm_mode_status sun4i_hdmi_mode_valid(struct drm_encoder *encoder,
- const struct drm_display_mode *mode)
+static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
+ .atomic_disable = sun4i_hdmi_disable,
+ .atomic_enable = sun4i_hdmi_enable,
+};
+
+static enum drm_mode_status
+sun4i_hdmi_connector_clock_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long clock)
{
- struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- unsigned long rate = mode->clock * 1000;
- unsigned long diff = rate / 200; /* +-0.5% allowed by HDMI spec */
+ const struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
+ unsigned long diff = clock / 200; /* +-0.5% allowed by HDMI spec */
long rounded_rate;
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
/* 165 MHz is the typical max pixelclock frequency for HDMI <= 1.2 */
- if (rate > 165000000)
+ if (clock > 165000000)
return MODE_CLOCK_HIGH;
- rounded_rate = clk_round_rate(hdmi->tmds_clk, rate);
+
+ rounded_rate = clk_round_rate(hdmi->tmds_clk, clock);
if (rounded_rate > 0 &&
- max_t(unsigned long, rounded_rate, rate) -
- min_t(unsigned long, rounded_rate, rate) < diff)
+ max_t(unsigned long, rounded_rate, clock) -
+ min_t(unsigned long, rounded_rate, clock) < diff)
return MODE_OK;
+
return MODE_NOCLOCK;
}
-static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
- .atomic_check = sun4i_hdmi_atomic_check,
- .atomic_disable = sun4i_hdmi_disable,
- .atomic_enable = sun4i_hdmi_enable,
- .mode_valid = sun4i_hdmi_mode_valid,
-};
+static int sun4i_hdmi_connector_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector_state *conn_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ struct drm_crtc *crtc = conn_state->crtc;
+ struct drm_crtc_state *crtc_state = crtc->state;
+ struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ enum drm_mode_status status;
+
+ status = sun4i_hdmi_connector_clock_valid(connector, mode,
+ mode->clock * 1000);
+ if (status != MODE_OK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static enum drm_mode_status
+sun4i_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return sun4i_hdmi_connector_clock_valid(connector, mode,
+ mode->clock * 1000);
+}
static int sun4i_hdmi_get_modes(struct drm_connector *connector)
{
@@ -236,6 +254,8 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev)
}
static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {
+ .atomic_check = sun4i_hdmi_connector_atomic_check,
+ .mode_valid = sun4i_hdmi_connector_mode_valid,
.get_modes = sun4i_hdmi_get_modes,
};
--
2.43.0
container_of_const() allows to preserve the pointer constness and is
thus more flexible than inline functions.
Let's switch all our instances of container_of() to container_of_const().
Reviewed-by: Sui Jingfeng <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index bae69d696765..c276d984da6b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -30,19 +30,11 @@
#include "sun4i_drv.h"
#include "sun4i_hdmi.h"
-static inline struct sun4i_hdmi *
-drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
-{
- return container_of(encoder, struct sun4i_hdmi,
- encoder);
-}
+#define drm_encoder_to_sun4i_hdmi(e) \
+ container_of_const(e, struct sun4i_hdmi, encoder)
-static inline struct sun4i_hdmi *
-drm_connector_to_sun4i_hdmi(struct drm_connector *connector)
-{
- return container_of(connector, struct sun4i_hdmi,
- connector);
-}
+#define drm_connector_to_sun4i_hdmi(c) \
+ container_of_const(c, struct sun4i_hdmi, connector)
static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
struct drm_display_mode *mode)
--
2.43.0
The sun4i_hdmi driver still uses the non-atomic variants of the encoder
hooks, so let's convert to their atomic equivalents.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 152375f3de2e..799a26215cc2 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -82,7 +82,8 @@ static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
return 0;
}
-static void sun4i_hdmi_disable(struct drm_encoder *encoder)
+static void sun4i_hdmi_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
u32 val;
@@ -96,7 +97,8 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder)
clk_disable_unprepare(hdmi->tmds_clk);
}
-static void sun4i_hdmi_enable(struct drm_encoder *encoder)
+static void sun4i_hdmi_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
@@ -120,9 +122,10 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder)
}
static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
+ const struct drm_display_mode *mode = &crtc_state->mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
unsigned int x, y;
u32 val;
@@ -201,9 +204,9 @@ static enum drm_mode_status sun4i_hdmi_mode_valid(struct drm_encoder *encoder,
static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
.atomic_check = sun4i_hdmi_atomic_check,
- .disable = sun4i_hdmi_disable,
- .enable = sun4i_hdmi_enable,
- .mode_set = sun4i_hdmi_mode_set,
+ .atomic_disable = sun4i_hdmi_disable,
+ .atomic_enable = sun4i_hdmi_enable,
+ .atomic_mode_set = sun4i_hdmi_mode_set,
.mode_valid = sun4i_hdmi_mode_valid,
};
--
2.43.0
The tmds_rate field in the inno_hdmi structure is used mostly to
configure the internal i2c controller divider through a call to the
inno_hdmi_i2c_init() function.
We can simply make that rate an argument to that function, which also
removes a workaround to initialize the divider at probe time when we
don't have a mode yet.
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index f05417c6b637..35f44e556fcf 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -50,8 +50,6 @@ struct inno_hdmi {
struct inno_hdmi_i2c *i2c;
struct i2c_adapter *ddc;
-
- unsigned int tmds_rate;
};
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -155,11 +153,11 @@ static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
hdmi_writeb(hdmi, offset, temp);
}
-static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi, unsigned long long rate)
{
- int ddc_bus_freq;
+ unsigned long long ddc_bus_freq = rate >> 2;
- ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+ do_div(ddc_bus_freq, HDMI_SCL_RATE);
hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
@@ -389,8 +387,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel
* clock rate, and reconfigure the DDC clock.
*/
- hdmi->tmds_rate = mode->clock * 1000;
- inno_hdmi_i2c_init(hdmi);
+ inno_hdmi_i2c_init(hdmi, mode->clock * 1000);
/* Unmute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
@@ -768,8 +765,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
* PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
* and reconfigure the DDC clock.
*/
- hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
- inno_hdmi_i2c_init(hdmi);
+ inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->pclk));
ret = inno_hdmi_register(drm, hdmi);
if (ret)
--
2.43.0
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> Even though we were rolling our own custom state for the vc4 HDMI
> controller driver, we were still using the generic helper to destroy
> that state.
>
> It was mostly working since the underlying state is the first member of
> our state so the pointers are probably equal in all relevant cases, but
> it's still fragile so let's fix this properly.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/vc4/vc4_hdmi.c | 12 +++++++++++-
> 1 file changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 25c9c71256d3..f05e2c95a60d 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -672,11 +672,21 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
> return &new_state->base;
> }
>
> +static void vc4_hdmi_connector_destroy_state(struct drm_connector *connector,
> + struct drm_connector_state *state)
> +{
> + struct vc4_hdmi_connector_state *vc4_state =
> + conn_state_to_vc4_hdmi_conn_state(state);
> +
> + __drm_atomic_helper_connector_destroy_state(state);
> + kfree(vc4_state);
> +}
> +
> static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
> .fill_modes = drm_helper_probe_single_connector_modes,
> .reset = vc4_hdmi_connector_reset,
> .atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
> - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> + .atomic_destroy_state = vc4_hdmi_connector_destroy_state,
> .atomic_get_property = vc4_hdmi_connector_get_property,
> .atomic_set_property = vc4_hdmi_connector_set_property,
> };
>
> --
> 2.43.0
>
On Thu, 07 Dec 2023 16:49:41 +0100, Maxime Ripard wrote:
> Even though we were rolling our own custom state for the vc4 HDMI
> controller driver, we were still using the generic helper to destroy
> that state.
>
> It was mostly working since the underlying state is the first member of
> our state so the pointers are probably equal in all relevant cases, but
> it's still fragile so let's fix this properly.
>
> [...]
Applied to drm/drm-misc (drm-misc-next).
Thanks!
Maxime
Hi Dave,
On Tue, Dec 12, 2023 at 11:40:26AM +0000, Dave Stevenson wrote:
> On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
> >
> > Even though we were rolling our own custom state for the vc4 HDMI
> > controller driver, we were still using the generic helper to destroy
> > that state.
> >
> > It was mostly working since the underlying state is the first member of
> > our state so the pointers are probably equal in all relevant cases, but
> > it's still fragile so let's fix this properly.
> >
> > Signed-off-by: Maxime Ripard <[email protected]>
>
> Reviewed-by: Dave Stevenson <[email protected]>
Thanks for the review, I've applied that patch now.
Could you also review the framework changes (patches 6 to 16) ?
Thanks!
Maxime
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> The next features we will need to share across drivers will need to
> store some parameters for drivers to use, such as the selected output
> format.
>
> Let's create a new connector sub-state dedicated to HDMI controllers,
> that will eventually store everything we need.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic_state_helper.c | 35 +++++++++++++++++++++++++++++++
> include/drm/drm_atomic_state_helper.h | 4 ++++
> include/drm/drm_connector.h | 7 +++++++
> 3 files changed, 46 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 54975de44a0e..e69c0cc1c6da 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -570,6 +570,22 @@ void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector)
> }
> EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset);
>
> +/**
> + * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources
> + * @connector: DRM connector
> + * @new_state: connector state to reset
> + *
> + * Initializes all HDMI resources from a @drm_connector_state without
> + * actually allocating it. This is useful for HDMI drivers, in
> + * combination with __drm_atomic_helper_connector_reset() or
> + * drm_atomic_helper_connector_reset().
> + */
> +void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
> + struct drm_connector_state *new_state)
> +{
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
> +
> /**
> * drm_atomic_helper_connector_tv_check - Validate an analog TV connector state
> * @connector: DRM Connector
> @@ -619,6 +635,25 @@ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
> }
> EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check);
>
> +/**
> + * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
> + * @connector: DRM Connector
> + * @state: the DRM State object
> + *
> + * Provides a default connector state check handler for HDMI connectors.
> + * Checks that a desired connector update is valid, and updates various
> + * fields of derived state.
> + *
> + * RETURNS:
> + * Zero on success, or an errno code otherwise.
> + */
> +int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
> + struct drm_atomic_state *state)
> +{
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check);
> +
> /**
> * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
> * @connector: connector object
> diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h
> index b9740edb2658..d59d2b3aef9a 100644
> --- a/include/drm/drm_atomic_state_helper.h
> +++ b/include/drm/drm_atomic_state_helper.h
> @@ -71,7 +71,11 @@ void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_
> void __drm_atomic_helper_connector_reset(struct drm_connector *connector,
> struct drm_connector_state *conn_state);
> void drm_atomic_helper_connector_reset(struct drm_connector *connector);
> +void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
> + struct drm_connector_state *new_state);
> void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector);
> +int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
> + struct drm_atomic_state *state);
> int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
> struct drm_atomic_state *state);
> void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connector);
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 4491c4c2fb6e..000a2a156619 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1031,6 +1031,13 @@ struct drm_connector_state {
> * DRM blob property for HDR output metadata
> */
> struct drm_property_blob *hdr_output_metadata;
> +
> + /**
> + * @hdmi: HDMI-related variable and properties. Filled by
> + * @drm_atomic_helper_connector_hdmi_check().
> + */
> + struct {
> + } hdmi;
> };
>
> /**
>
> --
> 2.43.0
>
Hi Maxime
As requested, I'm just going through patches 6-16.
I will say that I've been less thorough in checking the kunit test
code in this series than the core code changes, and I'm trusting that
all the unit tests pass.
I get a build failure on the complete series for arm64 with the
standard defconfig
depmod: ERROR: Cycle detected: drm_display_helper -> drm_kms_helper ->
drm_display_helper
depmod: ERROR: Cycle detected: drm
depmod: ERROR: Found 2 modules in dependency cycles!
I haven't followed it through as to the reason, but obviously that
will need to be addressed.
Dave
On Thu, 7 Dec 2023 at 15:49, Maxime Ripard <[email protected]> wrote:
>
> A lot of the various HDMI drivers duplicate some logic that depends on
> the HDMI spec itself and not really a particular hardware
> implementation.
>
> Output BPC or format selection, infoframe generation are good examples
> of such areas.
>
> This creates a lot of boilerplate, with a lot of variations, which makes
> it hard for userspace to rely on, and makes it difficult to get it right
> for drivers.
>
> In the next patches, we'll add a lot of infrastructure around the
> drm_connector and drm_connector_state structures, which will allow to
> abstract away the duplicated logic. This infrastructure comes with a few
> requirements though, and thus we need a new initialization function.
>
> Hopefully, this will make drivers simpler to handle, and their behaviour
> more consistent.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_connector.c | 39 +++++++++
> drivers/gpu/drm/tests/drm_connector_test.c | 123 +++++++++++++++++++++++++++++
> include/drm/drm_connector.h | 5 ++
> 3 files changed, 167 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index b0516505f7ae..d9961cce8245 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -452,6 +452,45 @@ int drmm_connector_init(struct drm_device *dev,
> }
> EXPORT_SYMBOL(drmm_connector_init);
>
> +/**
> + * drmm_connector_hdmi_init - Init a preallocated HDMI connector
> + * @dev: DRM device
> + * @connector: A pointer to the HDMI connector to init
> + * @funcs: callbacks for this connector
> + * @connector_type: user visible type of the connector
> + * @ddc: optional pointer to the associated ddc adapter
> + *
> + * Initialises a preallocated HDMI connector. Connectors can be
> + * subclassed as part of driver connector objects.
> + *
> + * Cleanup is automatically handled with a call to
> + * drm_connector_cleanup() in a DRM-managed action.
> + *
> + * The connector structure should be allocated with drmm_kzalloc().
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +int drmm_connector_hdmi_init(struct drm_device *dev,
> + struct drm_connector *connector,
> + const struct drm_connector_funcs *funcs,
> + int connector_type,
> + struct i2c_adapter *ddc)
> +{
> + int ret;
> +
> + if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA ||
> + connector_type == DRM_MODE_CONNECTOR_HDMIB))
> + return -EINVAL;
> +
> + ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drmm_connector_hdmi_init);
> +
> /**
> * drm_connector_attach_edid_property - attach edid property.
> * @connector: the connector
> diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
> index a268847be8d1..8f070cacab3b 100644
> --- a/drivers/gpu/drm/tests/drm_connector_test.c
> +++ b/drivers/gpu/drm/tests/drm_connector_test.c
> @@ -172,6 +172,128 @@ static struct kunit_suite drmm_connector_init_test_suite = {
> .test_cases = drmm_connector_init_tests,
> };
>
> +/*
> + * Test that the registration of a bog standard connector works as
> + * expected and doesn't report any error.
> + */
> +static void drm_test_connector_hdmi_init_valid(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +}
> +
> +/*
> + * Test that the registration of a connector without a DDC adapter
> + * doesn't report any error.
> + */
> +static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + NULL);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +}
> +
> +/*
> + * Test that the registration of an HDMI connector with an HDMI
> + * connector type succeeds.
> + */
> +static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + unsigned int connector_type = *(unsigned int *)test->param_value;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + connector_type,
> + &priv->ddc);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +}
> +
> +static const unsigned int drm_connector_hdmi_init_type_valid_tests[] = {
> + DRM_MODE_CONNECTOR_HDMIA,
> + DRM_MODE_CONNECTOR_HDMIB,
> +};
> +
> +static void drm_connector_hdmi_init_type_desc(const unsigned int *type, char *desc)
> +{
> + sprintf(desc, "%s", drm_get_connector_type_name(*type));
> +}
> +
> +KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_valid,
> + drm_connector_hdmi_init_type_valid_tests,
> + drm_connector_hdmi_init_type_desc);
> +
> +/*
> + * Test that the registration of an HDMI connector with an !HDMI
> + * connector type fails.
> + */
> +static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + unsigned int connector_type = *(unsigned int *)test->param_value;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + connector_type,
> + &priv->ddc);
> + KUNIT_EXPECT_LT(test, ret, 0);
> +}
> +
> +static const unsigned int drm_connector_hdmi_init_type_invalid_tests[] = {
> + DRM_MODE_CONNECTOR_Unknown,
> + DRM_MODE_CONNECTOR_VGA,
> + DRM_MODE_CONNECTOR_DVII,
> + DRM_MODE_CONNECTOR_DVID,
> + DRM_MODE_CONNECTOR_DVIA,
> + DRM_MODE_CONNECTOR_Composite,
> + DRM_MODE_CONNECTOR_SVIDEO,
> + DRM_MODE_CONNECTOR_LVDS,
> + DRM_MODE_CONNECTOR_Component,
> + DRM_MODE_CONNECTOR_9PinDIN,
> + DRM_MODE_CONNECTOR_DisplayPort,
> + DRM_MODE_CONNECTOR_TV,
> + DRM_MODE_CONNECTOR_eDP,
> + DRM_MODE_CONNECTOR_VIRTUAL,
> + DRM_MODE_CONNECTOR_DSI,
> + DRM_MODE_CONNECTOR_DPI,
> + DRM_MODE_CONNECTOR_WRITEBACK,
> + DRM_MODE_CONNECTOR_SPI,
> + DRM_MODE_CONNECTOR_USB,
> +};
> +
> +KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid,
> + drm_connector_hdmi_init_type_invalid_tests,
> + drm_connector_hdmi_init_type_desc);
> +
> +static struct kunit_case drmm_connector_hdmi_init_tests[] = {
> + KUNIT_CASE(drm_test_connector_hdmi_init_valid),
> + KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
> + KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
> + drm_connector_hdmi_init_type_valid_gen_params),
> + KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_invalid,
> + drm_connector_hdmi_init_type_invalid_gen_params),
> + { }
> +};
> +
> +static struct kunit_suite drmm_connector_hdmi_init_test_suite = {
> + .name = "drmm_connector_hdmi_init",
> + .init = drm_test_connector_init,
> + .test_cases = drmm_connector_hdmi_init_tests,
> +};
> +
> struct drm_get_tv_mode_from_name_test {
> const char *name;
> enum drm_connector_tv_mode expected_mode;
> @@ -236,6 +358,7 @@ static struct kunit_suite drm_get_tv_mode_from_name_test_suite = {
> };
>
> kunit_test_suites(
> + &drmm_connector_hdmi_init_test_suite,
> &drmm_connector_init_test_suite,
> &drm_get_tv_mode_from_name_test_suite
> );
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index fe88d7fc6b8f..4491c4c2fb6e 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1904,6 +1904,11 @@ int drmm_connector_init(struct drm_device *dev,
> const struct drm_connector_funcs *funcs,
> int connector_type,
> struct i2c_adapter *ddc);
> +int drmm_connector_hdmi_init(struct drm_device *dev,
> + struct drm_connector *connector,
> + const struct drm_connector_funcs *funcs,
> + int connector_type,
> + struct i2c_adapter *ddc);
> void drm_connector_attach_edid_property(struct drm_connector *connector);
> int drm_connector_register(struct drm_connector *connector);
> void drm_connector_unregister(struct drm_connector *connector);
>
> --
> 2.43.0
>
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
>
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> HDMI controller drivers will need to figure out the RGB range they need
> to configure based on a mode and property values. Let's expose that in
> the HDMI connector state so drivers can just use that value.
Again a mention that we're also adding unit tests wouldn't be amiss.
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic.c | 4 +-
> drivers/gpu/drm/drm_atomic_state_helper.c | 44 +++
> .../gpu/drm/tests/drm_atomic_state_helper_test.c | 335 +++++++++++++++++++++
> include/drm/drm_atomic_state_helper.h | 1 +
> include/drm/drm_connector.h | 6 +
> 5 files changed, 389 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 1465a7f09a0b..e4ad53e64e07 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1143,9 +1143,11 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
> 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)
> + 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));
> + drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n');
> + }
>
> if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
> if (state->writeback_job && state->writeback_job->fb)
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 10d98620a358..4a7114978c47 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -31,6 +31,7 @@
> #include <drm/drm_connector.h>
> #include <drm/drm_crtc.h>
> #include <drm/drm_device.h>
> +#include <drm/drm_edid.h>
> #include <drm/drm_framebuffer.h>
> #include <drm/drm_plane.h>
> #include <drm/drm_print.h>
> @@ -636,6 +637,47 @@ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
> }
> EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check);
>
> +static const struct drm_display_mode *
> +connector_state_get_mode(const struct drm_connector_state *conn_state)
> +{
> + struct drm_atomic_state *state;
> + struct drm_crtc_state *crtc_state;
> + struct drm_crtc *crtc;
> +
> + state = conn_state->state;
> + if (!state)
> + return NULL;
> +
> + crtc = conn_state->crtc;
> + if (!crtc)
> + return NULL;
> +
> + crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> + if (!crtc_state)
> + return NULL;
> +
> + return &crtc_state->mode;
> +}
> +
> +static bool hdmi_is_full_range(const struct drm_connector *connector,
> + const struct drm_connector_state *state)
> +{
> + const struct drm_display_info *display = &connector->display_info;
> + const struct drm_display_mode *mode =
> + connector_state_get_mode(state);
> +
> + if (state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_FULL)
> + return true;
> +
> + if (state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
> + return false;
> +
> + if (!display->is_hdmi)
> + return true;
> +
> + return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false;
> +}
> +
> /**
> * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
> * @connector: DRM Connector
> @@ -656,6 +698,8 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
> struct drm_connector_state *new_state =
> drm_atomic_get_new_connector_state(state, connector);
>
> + new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
> +
> if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb) {
> struct drm_crtc *crtc = new_state->crtc;
> struct drm_crtc_state *crtc_state;
> diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> index 21e6f796ee13..7750c3d214a4 100644
> --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> @@ -328,7 +328,342 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
> KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
> }
>
> +/*
> + * Test that for an HDMI connector, with an HDMI monitor, if the
> + * Broadcast RGB property is set to auto with a mode that isn't the
> + * VIC-1 mode, we will get a limited RGB Quantization Range.
> + */
> +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_atomic_helper_connector_hdmi_init(test);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
> +
> + 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);
> +
> + 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_connector_state(state, conn);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
> +
> + KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range);
> +}
> +
> +/*
> + * Test that for an HDMI connector, with an HDMI monitor, if the
> + * Broadcast RGB property is set to auto with a VIC-1 mode, we will get
> + * a full RGB Quantization Range.
> + */
> +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_atomic_helper_connector_hdmi_init(test);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + mode = drm_display_mode_from_cea_vic(drm, 1);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, mode, 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);
> +
> + 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_connector_state(state, conn);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
> +
> + KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_full_range);
> +}
> +
> +/*
> + * Test that for an HDMI connector, with an HDMI monitor, if the
> + * Broadcast RGB property is set to full with a mode that isn't the
> + * VIC-1 mode, we will get a full RGB Quantization Range.
> + */
> +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_atomic_helper_connector_hdmi_init(test);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
> +
> + 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);
> +
> + 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_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_TRUE(test, conn_state->hdmi.is_full_range);
> +}
> +
> +/*
> + * Test that for an HDMI connector, with an HDMI monitor, if the
> + * Broadcast RGB property is set to full with a VIC-1 mode, we will get
> + * a full RGB Quantization Range.
> + */
> +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_atomic_helper_connector_hdmi_init(test);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + mode = drm_display_mode_from_cea_vic(drm, 1);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, mode, 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);
> +
> + 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_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_TRUE(test, conn_state->hdmi.is_full_range);
> +}
> +
> +/*
> + * Test that for an HDMI connector, with an HDMI monitor, if the
> + * Broadcast RGB property is set to limited with a mode that isn't the
> + * VIC-1 mode, we will get a limited RGB Quantization Range.
> + */
> +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_atomic_helper_connector_hdmi_init(test);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
> +
> + 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);
> +
> + 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_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_FALSE(test, conn_state->hdmi.is_full_range);
> +}
> +
> +/*
> + * Test that for an HDMI connector, with an HDMI monitor, if the
> + * Broadcast RGB property is set to limited with a VIC-1 mode, we will
> + * get a limited RGB Quantization Range.
> + */
> +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_atomic_helper_connector_hdmi_init(test);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + mode = drm_display_mode_from_cea_vic(drm, 1);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, mode, 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);
> +
> + 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_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_FALSE(test, conn_state->hdmi.is_full_range);
> +}
> +
> 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(drm_test_check_broadcast_rgb_crtc_mode_changed),
> KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
> { }
> diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h
> index d59d2b3aef9a..8072ac18e536 100644
> --- a/include/drm/drm_atomic_state_helper.h
> +++ b/include/drm/drm_atomic_state_helper.h
> @@ -88,6 +88,7 @@ void
> __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state);
> void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
> struct drm_connector_state *state);
> +
> void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj,
> struct drm_private_state *state);
>
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 3867a4c01b78..76eecd449fb8 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1066,6 +1066,12 @@ struct drm_connector_state {
> * Broadcast RGB selection value.
> */
> enum drm_hdmi_broadcast_rgb broadcast_rgb;
> +
> + /**
> + * @is_full_range: Is the output supposed to use a full
> + * RGB Quantization Range or not?
> + */
> + bool is_full_range;
> } hdmi;
> };
>
>
> --
> 2.43.0
>
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> We'll add automatic selection of the output BPC in a following patch,
> but let's add it to the HDMI connector state already.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic.c | 1 +
> drivers/gpu/drm/drm_atomic_state_helper.c | 7 +-
> drivers/gpu/drm/drm_connector.c | 20 +-
> .../gpu/drm/tests/drm_atomic_state_helper_test.c | 227 ++++++++++++++++++++-
> drivers/gpu/drm/tests/drm_connector_test.c | 153 +++++++++++++-
> include/drm/drm_connector.h | 13 +-
> 6 files changed, 402 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index e4ad53e64e07..76c63ed04af4 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1147,6 +1147,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
> drm_printf(p, "\tbroadcast_rgb=%s\n",
> drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb));
> drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n');
> + drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
> }
>
> if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 4a7114978c47..883bdc0349c0 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -584,6 +584,10 @@ 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)
> {
> + unsigned int max_bpc = connector->max_bpc;
> +
> + new_state->max_bpc = max_bpc;
> + new_state->max_requested_bpc = max_bpc;
> new_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
> }
> EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
> @@ -700,7 +704,8 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
>
> new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
>
> - if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb) {
> + if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
> + old_state->hdmi.output_bpc != new_state->hdmi.output_bpc) {
> struct drm_crtc *crtc = new_state->crtc;
> struct drm_crtc_state *crtc_state;
>
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 929b0a911f62..139ac3d8160c 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -459,6 +459,7 @@ EXPORT_SYMBOL(drmm_connector_init);
> * @funcs: callbacks for this connector
> * @connector_type: user visible type of the connector
> * @ddc: optional pointer to the associated ddc adapter
> + * @max_bpc: Maximum bits per char the HDMI connector supports
> *
> * Initialises a preallocated HDMI connector. Connectors can be
> * subclassed as part of driver connector objects.
> @@ -475,7 +476,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> struct drm_connector *connector,
> const struct drm_connector_funcs *funcs,
> int connector_type,
> - struct i2c_adapter *ddc)
> + struct i2c_adapter *ddc,
> + unsigned int max_bpc)
> {
> int ret;
>
> @@ -483,10 +485,26 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> connector_type == DRM_MODE_CONNECTOR_HDMIB))
> return -EINVAL;
>
> + if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
> + return -EINVAL;
> +
> ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
> if (ret)
> return ret;
>
> + /*
> + * drm_connector_attach_max_bpc_property() requires the
> + * connector to have a state.
> + */
> + if (connector->funcs->reset)
> + connector->funcs->reset(connector);
> +
> + drm_connector_attach_max_bpc_property(connector, 8, max_bpc);
> + connector->max_bpc = max_bpc;
> +
> + if (max_bpc > 8)
> + drm_connector_attach_hdr_output_metadata_property(connector);
> +
> return 0;
> }
> EXPORT_SYMBOL(drmm_connector_hdmi_init);
> diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> index 7750c3d214a4..a1b0e6914cf8 100644
> --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> @@ -146,7 +146,8 @@ static const struct drm_connector_funcs dummy_connector_funcs = {
>
> static
> struct drm_atomic_helper_connector_hdmi_priv *
> -drm_atomic_helper_connector_hdmi_init(struct kunit *test)
> +drm_atomic_helper_connector_hdmi_init(struct kunit *test,
> + unsigned int max_bpc)
> {
> struct drm_atomic_helper_connector_hdmi_priv *priv;
> struct drm_connector *conn;
> @@ -188,7 +189,8 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test)
> ret = drmm_connector_hdmi_init(drm, conn,
> &dummy_connector_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> - NULL);
> + NULL,
> + max_bpc);
> KUNIT_ASSERT_EQ(test, ret, 0);
>
> drm_connector_helper_add(conn, &dummy_connector_helper_funcs);
> @@ -223,7 +225,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> @@ -284,7 +286,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> @@ -345,7 +347,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -399,7 +401,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -452,7 +454,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -508,7 +510,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -563,7 +565,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -619,7 +621,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -657,6 +659,138 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
> KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_full_range);
> }
>
> +/*
> + * Test that if we change the maximum bpc 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_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_atomic_helper_connector_hdmi_init(test, 10);
> + 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.output_bpc = 8;
> +
> + KUNIT_ASSERT_NE(test,
> + old_conn_state->hdmi.output_bpc,
> + new_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_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);
> +}
> +
> +/*
> + * Test that if we set the output bpc 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_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_atomic_helper_connector_hdmi_init(test, 10);
> + 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);
> +
> + 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);
> +}
> +
> 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),
> @@ -666,6 +800,8 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
> KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1),
> 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_output_bpc_crtc_mode_changed),
> + KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed),
> { }
> };
>
> @@ -684,7 +820,7 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
> struct drm_connector_state *conn_state;
> struct drm_connector *conn;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test);
> + priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -692,8 +828,77 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
> KUNIT_EXPECT_EQ(test, conn_state->hdmi.broadcast_rgb, DRM_HDMI_BROADCAST_RGB_AUTO);
> }
>
> +/*
> + * Test that if the connector was initialised with a maximum bpc of 8,
> + * the value of the max_bpc and max_requested_bpc properties out of
> + * reset are also set to 8, and output_bpc is set to 0 and will be
> + * filled at atomic_check time.
> + */
> +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_atomic_helper_connector_hdmi_init(test, 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);
> +}
> +
> +/*
> + * Test that if the connector was initialised with a maximum bpc of 10,
> + * the value of the max_bpc and max_requested_bpc properties out of
> + * reset are also set to 10, and output_bpc is set to 0 and will be
> + * filled at atomic_check time.
> + */
> +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_atomic_helper_connector_hdmi_init(test, 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);
> +}
> +
> +/*
> + * Test that if the connector was initialised with a maximum bpc of 12,
> + * the value of the max_bpc and max_requested_bpc properties out of
> + * reset are also set to 12, and output_bpc is set to 0 and will be
> + * filled at atomic_check time.
> + */
> +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_atomic_helper_connector_hdmi_init(test, 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 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),
> { }
> };
>
> diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
> index 41d33dea30af..fef7d53d34d4 100644
> --- a/drivers/gpu/drm/tests/drm_connector_test.c
> +++ b/drivers/gpu/drm/tests/drm_connector_test.c
> @@ -186,7 +186,8 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> - &priv->ddc);
> + &priv->ddc,
> + 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
> }
>
> @@ -202,10 +203,144 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> - NULL);
> + NULL,
> + 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
> }
>
> +/*
> + * Test that the registration of a connector with an invalid maximum bpc
> + * count fails.
> + */
> +static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + 9);
> + KUNIT_EXPECT_LT(test, ret, 0);
> +}
> +
> +/*
> + * Test that the registration of a connector with a null maximum bpc
> + * count fails.
> + */
> +static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + 0);
> + KUNIT_EXPECT_LT(test, ret, 0);
> +}
> +
> +/*
> + * Test that the registration of a connector with a maximum bpc count of
> + * 8 succeeds, registers the max bpc property, but doesn't register the
> + * HDR output metadata one.
> + */
> +static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + struct drm_connector *connector = &priv->connector;
> + struct drm_property *prop;
> + uint64_t val;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + 8);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +
> + prop = connector->max_bpc_property;
> + KUNIT_ASSERT_NOT_NULL(test, prop);
> + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> +
> + ret = drm_object_property_get_value(&connector->base, prop, &val);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> + KUNIT_EXPECT_EQ(test, val, 8);
> +
> + prop = priv->drm.mode_config.hdr_output_metadata_property;
> + KUNIT_ASSERT_NOT_NULL(test, prop);
> + KUNIT_EXPECT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> +}
> +
> +/*
> + * Test that the registration of a connector with a maximum bpc count of
> + * 10 succeeds and registers the max bpc and HDR output metadata
> + * properties.
> + */
> +static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + struct drm_connector *connector = &priv->connector;
> + struct drm_property *prop;
> + uint64_t val;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + 10);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +
> + prop = connector->max_bpc_property;
> + KUNIT_ASSERT_NOT_NULL(test, prop);
> + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> +
> + ret = drm_object_property_get_value(&connector->base, prop, &val);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> + KUNIT_EXPECT_EQ(test, val, 10);
> +
> + prop = priv->drm.mode_config.hdr_output_metadata_property;
> + KUNIT_ASSERT_NOT_NULL(test, prop);
> + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> +}
> +
> +/*
> + * Test that the registration of a connector with a maximum bpc count of
> + * 12 succeeds and registers the max bpc and HDR output metadata
> + * properties.
> + */
> +static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + struct drm_connector *connector = &priv->connector;
> + struct drm_property *prop;
> + uint64_t val;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + 12);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +
> + prop = connector->max_bpc_property;
> + KUNIT_ASSERT_NOT_NULL(test, prop);
> + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> +
> + ret = drm_object_property_get_value(&connector->base, prop, &val);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> + KUNIT_EXPECT_EQ(test, val, 12);
> +
> + prop = priv->drm.mode_config.hdr_output_metadata_property;
> + KUNIT_ASSERT_NOT_NULL(test, prop);
> + KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> +}
> +
> /*
> * Test that the registration of an HDMI connector with an HDMI
> * connector type succeeds.
> @@ -219,7 +354,8 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> connector_type,
> - &priv->ddc);
> + &priv->ddc,
> + 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
> }
>
> @@ -250,7 +386,8 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> connector_type,
> - &priv->ddc);
> + &priv->ddc,
> + 8);
> KUNIT_EXPECT_LT(test, ret, 0);
> }
>
> @@ -282,6 +419,11 @@ KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid,
>
> static struct kunit_case drmm_connector_hdmi_init_tests[] = {
> KUNIT_CASE(drm_test_connector_hdmi_init_valid),
> + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_8),
> + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_10),
> + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_12),
> + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid),
> + KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null),
> KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
> KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
> drm_connector_hdmi_init_type_valid_gen_params),
> @@ -447,7 +589,8 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
> ret = drmm_connector_hdmi_init(&priv->drm, connector,
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> - &priv->ddc);
> + &priv->ddc,
> + 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> ret = drm_connector_attach_broadcast_rgb_property(connector);
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 76eecd449fb8..1b1b6aed04ee 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1072,6 +1072,11 @@ struct drm_connector_state {
> * RGB Quantization Range or not?
> */
> bool is_full_range;
> +
> + /**
> + * @output_bpc: Bits per color channel to output.
> + */
> + unsigned int output_bpc;
> } hdmi;
> };
>
> @@ -1717,6 +1722,11 @@ struct drm_connector {
> */
> struct drm_property_blob *path_blob_ptr;
>
> + /**
> + * @max_bpc: Maximum bits per color channel the connector supports.
> + */
> + unsigned int max_bpc;
> +
> /**
> * @max_bpc_property: Default connector property for the max bpc to be
> * driven out of the connector.
> @@ -1956,7 +1966,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> struct drm_connector *connector,
> const struct drm_connector_funcs *funcs,
> int connector_type,
> - struct i2c_adapter *ddc);
> + struct i2c_adapter *ddc,
> + unsigned int max_bpc);
> void drm_connector_attach_edid_property(struct drm_connector *connector);
> int drm_connector_register(struct drm_connector *connector);
> void drm_connector_unregister(struct drm_connector *connector);
>
> --
> 2.43.0
>
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> Just like BPC, we'll add support for automatic selection of the output
> format for HDMI connectors.
>
> Let's add the needed defaults and fields for now.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic.c | 2 +
> drivers/gpu/drm/drm_atomic_state_helper.c | 3 +-
> drivers/gpu/drm/drm_connector.c | 31 ++++++
> .../gpu/drm/tests/drm_atomic_state_helper_test.c | 90 ++++++++++++++---
> drivers/gpu/drm/tests/drm_connector_test.c | 109 ++++++++++++++++++++-
> include/drm/drm_connector.h | 19 ++++
> 6 files changed, 238 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 76c63ed04af4..7aaa2a4d70d9 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1148,6 +1148,8 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
> drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb));
> drm_printf(p, "\tis_full_range=%c\n", state->hdmi.is_full_range ? 'y' : 'n');
> drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
> + drm_printf(p, "\toutput_format=%s\n",
> + drm_hdmi_connector_get_output_format_name(state->hdmi.output_format));
> }
>
> if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 883bdc0349c0..92e1b087c3d0 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -705,7 +705,8 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
> new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
>
> if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
> - old_state->hdmi.output_bpc != new_state->hdmi.output_bpc) {
> + old_state->hdmi.output_bpc != new_state->hdmi.output_bpc ||
> + old_state->hdmi.output_format != new_state->hdmi.output_format) {
> struct drm_crtc *crtc = new_state->crtc;
> struct drm_crtc_state *crtc_state;
>
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 139ac3d8160c..a72f38b6dbc8 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -459,6 +459,7 @@ EXPORT_SYMBOL(drmm_connector_init);
> * @funcs: callbacks for this connector
> * @connector_type: user visible type of the connector
> * @ddc: optional pointer to the associated ddc adapter
> + * @supported_formats: Bitmask of @hdmi_colorspace listing supported output formats
> * @max_bpc: Maximum bits per char the HDMI connector supports
> *
> * Initialises a preallocated HDMI connector. Connectors can be
> @@ -477,6 +478,7 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> const struct drm_connector_funcs *funcs,
> int connector_type,
> struct i2c_adapter *ddc,
> + unsigned long supported_formats,
> unsigned int max_bpc)
> {
> int ret;
> @@ -485,6 +487,9 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> connector_type == DRM_MODE_CONNECTOR_HDMIB))
> return -EINVAL;
>
> + if (!supported_formats || !(supported_formats & BIT(HDMI_COLORSPACE_RGB)))
> + return -EINVAL;
> +
> if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
> return -EINVAL;
>
> @@ -492,6 +497,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> if (ret)
> return ret;
>
> + connector->hdmi.supported_formats = supported_formats;
> +
> /*
> * drm_connector_attach_max_bpc_property() requires the
> * connector to have a state.
> @@ -1224,6 +1231,30 @@ drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_
> }
> EXPORT_SYMBOL(drm_hdmi_connector_get_broadcast_rgb_name);
>
> +static const char * const output_format_str[] = {
> + [HDMI_COLORSPACE_RGB] = "RGB",
> + [HDMI_COLORSPACE_YUV420] = "YUV 4:2:0",
> + [HDMI_COLORSPACE_YUV422] = "YUV 4:2:2",
> + [HDMI_COLORSPACE_YUV444] = "YUV 4:4:4",
> +};
> +
> +/*
> + * drm_hdmi_connector_get_output_format_name() - Return a string for HDMI connector output format
> + * @fmt: Output format to compute name of
> + *
> + * Returns: the name of the output format, or NULL if the type is not
> + * valid.
> + */
> +const char *
> +drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt)
> +{
> + if (fmt >= ARRAY_SIZE(output_format_str))
> + return NULL;
> +
> + return output_format_str[fmt];
> +}
> +EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name);
> +
> /**
> * DOC: standard connector properties
> *
> diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> index a1b0e6914cf8..4e2ec436987b 100644
> --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> @@ -147,6 +147,7 @@ static const struct drm_connector_funcs dummy_connector_funcs = {
> static
> struct drm_atomic_helper_connector_hdmi_priv *
> drm_atomic_helper_connector_hdmi_init(struct kunit *test,
> + unsigned int formats,
> unsigned int max_bpc)
> {
> struct drm_atomic_helper_connector_hdmi_priv *priv;
> @@ -190,6 +191,7 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test,
> &dummy_connector_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> NULL,
> + formats,
> max_bpc);
> KUNIT_ASSERT_EQ(test, ret, 0);
>
> @@ -225,7 +227,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> @@ -286,7 +290,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> @@ -347,7 +353,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -401,7 +409,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -454,7 +464,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -510,7 +522,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -565,7 +579,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -621,7 +637,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -678,7 +696,9 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 10);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 10);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> @@ -745,7 +765,9 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
> struct drm_crtc *crtc;
> int ret;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 10);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 10);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> @@ -802,6 +824,15 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
> KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
> KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_changed),
> KUNIT_CASE(drm_test_check_output_bpc_crtc_mode_not_changed),
> + /*
> + * TODO: We should have tests to check that a change in the
> + * format triggers a CRTC mode change just like we do for the
> + * RGB Quantization and BPC.
> + *
> + * However, we don't have any way to control which format gets
> + * picked up aside from changing the BPC or mode which would
> + * already trigger a mode change.
> + */
> { }
> };
>
> @@ -820,7 +851,9 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
> struct drm_connector_state *conn_state;
> struct drm_connector *conn;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -840,7 +873,9 @@ static void drm_test_check_bpc_8_value(struct kunit *test)
> struct drm_connector_state *conn_state;
> struct drm_connector *conn;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 8);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -862,7 +897,9 @@ static void drm_test_check_bpc_10_value(struct kunit *test)
> struct drm_connector_state *conn_state;
> struct drm_connector *conn;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 10);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 10);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -884,7 +921,9 @@ static void drm_test_check_bpc_12_value(struct kunit *test)
> struct drm_connector_state *conn_state;
> struct drm_connector *conn;
>
> - priv = drm_atomic_helper_connector_hdmi_init(test, 12);
> + priv = drm_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 12);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> conn = &priv->connector;
> @@ -894,11 +933,34 @@ static void drm_test_check_bpc_12_value(struct kunit *test)
> KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 0);
> }
>
> +/*
> + * Test that the value of the output format property out of reset is set
> + * to RGB, even if the driver supports more than that.
> + */
> +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_atomic_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, HDMI_COLORSPACE_RGB);
> +}
> +
> 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),
> { }
> };
>
> diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
> index fef7d53d34d4..fa6fe8084107 100644
> --- a/drivers/gpu/drm/tests/drm_connector_test.c
> +++ b/drivers/gpu/drm/tests/drm_connector_test.c
> @@ -187,6 +187,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
> }
> @@ -204,6 +205,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> NULL,
> + BIT(HDMI_COLORSPACE_RGB),
> 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
> }
> @@ -221,6 +223,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 9);
> KUNIT_EXPECT_LT(test, ret, 0);
> }
> @@ -238,6 +241,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 0);
> KUNIT_EXPECT_LT(test, ret, 0);
> }
> @@ -259,6 +263,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> @@ -292,6 +297,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 10);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> @@ -325,6 +331,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 12);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> @@ -341,6 +348,42 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
> KUNIT_EXPECT_NOT_NULL(test, drm_mode_obj_find_prop_id(&connector->base, prop->base.id));
> }
>
> +/*
> + * Test that the registration of an HDMI connector with no supported
> + * format fails.
> + */
> +static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + 0,
> + 8);
> + KUNIT_EXPECT_LT(test, ret, 0);
> +}
> +
> +/*
> + * Test that the registration of an HDMI connector not listing RGB as a
> + * supported format fails.
> + */
> +static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + int ret;
> +
> + ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> + &dummy_funcs,
> + DRM_MODE_CONNECTOR_HDMIA,
> + &priv->ddc,
> + BIT(HDMI_COLORSPACE_YUV422),
> + 8);
> + KUNIT_EXPECT_LT(test, ret, 0);
> +}
> +
> /*
> * Test that the registration of an HDMI connector with an HDMI
> * connector type succeeds.
> @@ -355,6 +398,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
> &dummy_funcs,
> connector_type,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
> }
> @@ -387,6 +431,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
> &dummy_funcs,
> connector_type,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 8);
> KUNIT_EXPECT_LT(test, ret, 0);
> }
> @@ -424,6 +469,8 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = {
> KUNIT_CASE(drm_test_connector_hdmi_init_bpc_12),
> KUNIT_CASE(drm_test_connector_hdmi_init_bpc_invalid),
> KUNIT_CASE(drm_test_connector_hdmi_init_bpc_null),
> + KUNIT_CASE(drm_test_connector_hdmi_init_formats_empty),
> + KUNIT_CASE(drm_test_connector_hdmi_init_formats_no_rgb),
> KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
> KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_valid,
> drm_connector_hdmi_init_type_valid_gen_params),
> @@ -558,6 +605,64 @@ static struct kunit_suite drm_hdmi_connector_get_broadcast_rgb_name_test_suite =
> .test_cases = drm_hdmi_connector_get_broadcast_rgb_name_tests,
> };
>
> +struct drm_hdmi_connector_get_output_format_name_test {
> + unsigned int kind;
> + const char *expected_name;
> +};
> +
> +#define OUTPUT_FORMAT_TEST(_kind, _name) \
> + { \
> + .kind = _kind, \
> + .expected_name = _name, \
> + }
> +
> +static void drm_test_drm_hdmi_connector_get_output_format_name(struct kunit *test)
> +{
> + const struct drm_hdmi_connector_get_output_format_name_test *params =
> + test->param_value;
> +
> + KUNIT_EXPECT_STREQ(test,
> + drm_hdmi_connector_get_output_format_name(params->kind),
> + params->expected_name);
> +}
> +
> +static const
> +struct drm_hdmi_connector_get_output_format_name_test
> +drm_hdmi_connector_get_output_format_name_valid_tests[] = {
> + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_RGB, "RGB"),
> + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV420, "YUV 4:2:0"),
> + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV422, "YUV 4:2:2"),
> + OUTPUT_FORMAT_TEST(HDMI_COLORSPACE_YUV444, "YUV 4:4:4"),
> +};
> +
> +static void
> +drm_hdmi_connector_get_output_format_name_valid_desc(const struct drm_hdmi_connector_get_output_format_name_test *t,
> + char *desc)
> +{
> + sprintf(desc, "%s", t->expected_name);
> +}
> +
> +KUNIT_ARRAY_PARAM(drm_hdmi_connector_get_output_format_name_valid,
> + drm_hdmi_connector_get_output_format_name_valid_tests,
> + drm_hdmi_connector_get_output_format_name_valid_desc);
> +
> +static void drm_test_drm_hdmi_connector_get_output_format_name_invalid(struct kunit *test)
> +{
> + KUNIT_EXPECT_NULL(test, drm_hdmi_connector_get_output_format_name(4));
> +};
> +
> +static struct kunit_case drm_hdmi_connector_get_output_format_name_tests[] = {
> + KUNIT_CASE_PARAM(drm_test_drm_hdmi_connector_get_output_format_name,
> + drm_hdmi_connector_get_output_format_name_valid_gen_params),
> + KUNIT_CASE(drm_test_drm_hdmi_connector_get_output_format_name_invalid),
> + { }
> +};
> +
> +static struct kunit_suite drm_hdmi_connector_get_output_format_name_test_suite = {
> + .name = "drm_hdmi_connector_get_output_format_name",
> + .test_cases = drm_hdmi_connector_get_output_format_name_tests,
> +};
> +
> static void drm_test_drm_connector_attach_broadcast_rgb_property(struct kunit *test)
> {
> struct drm_connector_init_priv *priv = test->priv;
> @@ -590,6 +695,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
> &dummy_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> + BIT(HDMI_COLORSPACE_RGB),
> 8);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> @@ -618,7 +724,8 @@ kunit_test_suites(
> &drmm_connector_init_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
> + &drm_hdmi_connector_get_broadcast_rgb_name_test_suite,
> + &drm_hdmi_connector_get_output_format_name_test_suite
> );
>
> MODULE_AUTHOR("Maxime Ripard <[email protected]>");
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 1b1b6aed04ee..74db5ce47e01 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -391,6 +391,8 @@ enum drm_hdmi_broadcast_rgb {
>
> const char *
> drm_hdmi_connector_get_broadcast_rgb_name(enum drm_hdmi_broadcast_rgb broadcast_rgb);
> +const char *
> +drm_hdmi_connector_get_output_format_name(enum hdmi_colorspace fmt);
>
> /**
> * struct drm_monitor_range_info - Panel's Monitor range in EDID for
> @@ -1077,6 +1079,11 @@ struct drm_connector_state {
> * @output_bpc: Bits per color channel to output.
> */
> unsigned int output_bpc;
> +
> + /**
> + * @output_format: Pixel format to output in.
> + */
> + enum hdmi_colorspace output_format;
> } hdmi;
> };
>
> @@ -1944,6 +1951,17 @@ struct drm_connector {
>
> /** @hdr_sink_metadata: HDR Metadata Information read from sink */
> struct hdr_sink_metadata hdr_sink_metadata;
> +
> + /**
> + * @hdmi: HDMI-related variable and properties.
> + */
> + struct {
> + /**
> + * @supported_formats: Bitmask of @hdmi_colorspace
> + * supported by the controller.
> + */
> + unsigned long supported_formats;
> + } hdmi;
> };
>
> #define obj_to_connector(x) container_of(x, struct drm_connector, base)
> @@ -1967,6 +1985,7 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> const struct drm_connector_funcs *funcs,
> int connector_type,
> struct i2c_adapter *ddc,
> + unsigned long supported_formats,
> unsigned int max_bpc);
> void drm_connector_attach_edid_property(struct drm_connector *connector);
> int drm_connector_register(struct drm_connector *connector);
>
> --
> 2.43.0
>
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> A lot of HDMI drivers have some variation of the formula to calculate
> the TMDS character rate from a mode, but few of them actually take all
> parameters into account.
>
> Let's create a helper to provide that rate taking all parameters into
> account.
>
> Signed-off-by: Maxime Ripard <[email protected]>
> ---
> drivers/gpu/drm/drm_connector.c | 59 ++++++
> drivers/gpu/drm/tests/drm_connector_test.c | 323 +++++++++++++++++++++++++++++
> include/drm/drm_connector.h | 5 +
> 3 files changed, 387 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index a72f38b6dbc8..667326b09acc 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -2973,6 +2973,65 @@ void drm_connector_update_privacy_screen(const struct drm_connector_state *conne
> }
> EXPORT_SYMBOL(drm_connector_update_privacy_screen);
>
> +/**
> + * drm_connector_hdmi_compute_mode_clock() - Computes the TMDS Character Rate
> + * @mode: Display mode to compute the clock for
> + * @bpc: Bits per character
> + * @fmt: Output Pixel Format used
> + *
> + * Returns the TMDS Character Rate for a given mode, bpc count and output format.
> + *
> + * RETURNS:
> + * The TMDS Character Rate, in Hertz, or 0 on error.
> + */
> +unsigned long long
> +drm_connector_hdmi_compute_mode_clock(const struct drm_display_mode *mode,
> + unsigned int bpc,
> + enum hdmi_colorspace fmt)
> +{
> + unsigned long long clock = mode->clock * 1000ULL;
> + unsigned int vic = drm_match_cea_mode(mode);
> +
> + /*
> + * CTA-861-G Spec, section 5.4 - Color Coding and Quantization
> + * mandates that VIC 1 always uses 8 bpc.
> + */
> + if (vic == 1 && bpc != 8)
> + return 0;
> +
> + /*
> + * HDMI 2.0 Spec, section 7.1 - YCbCr 4:2:0 Pixel Encoding
> + * specifies that YUV420 encoding is only available for those
> + * VICs.
> + */
> + if (fmt == HDMI_COLORSPACE_YUV420 &&
> + !(vic == 96 || vic == 97 || vic == 101 ||
> + vic == 102 || vic == 106 || vic == 107))
> + return 0;
> +
> + /*
> + * HDMI 1.4b Spec, section 6.2.3 - Pixel Encoding Requirements
> + * specifies that YUV422 is 36-bit only.
> + */
> + if (fmt == HDMI_COLORSPACE_YUV422 && bpc != 12)
> + return 0;
> +
> + if (fmt == HDMI_COLORSPACE_YUV420)
> + clock = clock / 2;
> +
> + if (mode->flags & DRM_MODE_FLAG_DBLCLK)
> + clock = clock * 2;
> +
> + if (fmt == HDMI_COLORSPACE_YUV422)
> + bpc = 8;
Possibly a comment on why we have these munges for 420 and 422 as they
aren't immediately obvious.
Reviewed-by: Dave Stevenson <[email protected]>
> +
> + clock = clock * bpc;
> + do_div(clock, 8);
> +
> + return clock;
> +}
> +EXPORT_SYMBOL(drm_connector_hdmi_compute_mode_clock);
> +
> int drm_connector_set_obj_prop(struct drm_mode_object *obj,
> struct drm_property *property,
> uint64_t value)
> diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
> index fa6fe8084107..0a838924a546 100644
> --- a/drivers/gpu/drm/tests/drm_connector_test.c
> +++ b/drivers/gpu/drm/tests/drm_connector_test.c
> @@ -8,7 +8,9 @@
> #include <drm/drm_atomic_state_helper.h>
> #include <drm/drm_connector.h>
> #include <drm/drm_drv.h>
> +#include <drm/drm_edid.h>
> #include <drm/drm_kunit_helpers.h>
> +#include <drm/drm_modes.h>
>
> #include <kunit/test.h>
>
> @@ -719,10 +721,331 @@ static struct kunit_suite drm_connector_attach_broadcast_rgb_property_test_suite
> .test_cases = drm_connector_attach_broadcast_rgb_property_tests,
> };
>
> +/*
> + * Test that for a given mode, with 8bpc and an RGB output the TMDS
> + * character rate is equal to the mode pixel clock.
> + */
> +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + unsigned long long rate;
> + struct drm_device *drm = &priv->drm;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 16);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, 0);
> + KUNIT_EXPECT_EQ(test, mode->clock * 1000ULL, rate);
> +}
> +
> +/*
> + * Test that for a given mode, with 10bpc and an RGB output the TMDS
> + * character rate is equal to 1.25 times the mode pixel clock.
> + */
> +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + unsigned long long rate;
> + struct drm_device *drm = &priv->drm;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 16);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, 0);
> + KUNIT_EXPECT_EQ(test, mode->clock * 1250, rate);
> +}
> +
> +/*
> + * Test that for the VIC-1 mode, with 10bpc and an RGB output the TMDS
> + * character rate computation fails.
> + */
> +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc_vic_1(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + unsigned long long rate;
> + struct drm_device *drm = &priv->drm;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 1);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB);
> + KUNIT_EXPECT_EQ(test, rate, 0);
> +}
> +
> +/*
> + * Test that for a given mode, with 12bpc and an RGB output the TMDS
> + * character rate is equal to 1.5 times the mode pixel clock.
> + */
> +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + unsigned long long rate;
> + struct drm_device *drm = &priv->drm;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 16);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, 0);
> + KUNIT_EXPECT_EQ(test, mode->clock * 1500, rate);
> +}
> +
> +/*
> + * Test that for the VIC-1 mode, with 12bpc and an RGB output the TMDS
> + * character rate computation fails.
> + */
> +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc_vic_1(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + unsigned long long rate;
> + struct drm_device *drm = &priv->drm;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 1);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_EXPECT_EQ(test, rate, 0);
> +}
> +
> +/*
> + * Test that for a mode with the pixel repetition flag, the TMDS
> + * character rate is indeed double the mode pixel clock.
> + */
> +static void drm_test_drm_connector_hdmi_compute_mode_clock_rgb_double(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + unsigned long long rate;
> + struct drm_device *drm = &priv->drm;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 6);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_TRUE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, 0);
> + KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) * 2, rate);
> +}
> +
> +/*
> + * Test that the TMDS character rate computation for the VIC modes
> + * explicitly listed in the spec as supporting YUV420 succeed and return
> + * half the mode pixel clock.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_valid(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned long long rate;
> + unsigned int vic = *(unsigned int *)test->param_value;
> +
> + mode = drm_display_mode_from_cea_vic(drm, vic);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV420);
> + KUNIT_ASSERT_GT(test, rate, 0);
> + KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) / 2, rate);
> +}
> +
> +static const unsigned int drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[] = {
> + 96, 97, 101, 102, 106, 107,
> +};
> +
> +static void drm_connector_hdmi_compute_mode_clock_yuv420_vic_desc(const unsigned int *vic, char *desc)
> +{
> + sprintf(desc, "VIC %u", *vic);
> +}
> +
> +KUNIT_ARRAY_PARAM(drm_connector_hdmi_compute_mode_clock_yuv420_valid,
> + drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests,
> + drm_connector_hdmi_compute_mode_clock_yuv420_vic_desc);
> +
> +/*
> + * Test that trying to compute the TMDS char rate with the YUV420 format
> + * for a mode that doesn't support the YUV420 encoding returns an error.
> + *
> + * TODO: We should probably test this with all the VIC but the
> + * explicitly supported ones. Since the list of VIC is quite long and
> + * not linear, the best way to support it at the moment would be to
> + * create a custom gen_params function that would only return valid
> + * VICs. At the moment, that function expects to get a pointer back
> + * however, and compilers don't really like casting between integer and
> + * pointers.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_invalid(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned long long rate;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 42);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV420);
> + KUNIT_EXPECT_EQ(test, rate, 0);
> +}
> +
> +/*
> + * Test that for a given mode listed supporting it and an YUV420 output
> + * with 10bpc, the TMDS character rate is equal to 0.625 times the mode
> + * pixel clock.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned int vic =
> + drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0];
> + unsigned long long rate;
> +
> + mode = drm_display_mode_from_cea_vic(drm, vic);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_YUV420);
> + KUNIT_ASSERT_GT(test, rate, 0);
> +
> + KUNIT_EXPECT_EQ(test, mode->clock * 625, rate);
> +}
> +
> +/*
> + * Test that for a given mode listed supporting it and an YUV420 output
> + * with 12bpc, the TMDS character rate is equal to 0.75 times the mode
> + * pixel clock.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned int vic =
> + drm_connector_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0];
> + unsigned long long rate;
> +
> + mode = drm_display_mode_from_cea_vic(drm, vic);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_YUV420);
> + KUNIT_ASSERT_GT(test, rate, 0);
> +
> + KUNIT_EXPECT_EQ(test, mode->clock * 750, rate);
> +}
> +
> +/*
> + * Test that for a given mode, the computation of the TMDS character
> + * rate with 8bpc and a YUV422 output fails.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned long long rate;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 16);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_YUV422);
> + KUNIT_EXPECT_EQ(test, rate, 0);
> +}
> +
> +/*
> + * Test that for a given mode, the computation of the TMDS character
> + * rate with 10bpc and a YUV422 output fails.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned long long rate;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 16);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_YUV422);
> + KUNIT_EXPECT_EQ(test, rate, 0);
> +}
> +
> +/*
> + * Test that for a given mode, the computation of the TMDS character
> + * rate with 12bpc and a YUV422 output succeeds and returns a rate equal
> + * to the mode pixel clock.
> + */
> +static void drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc(struct kunit *test)
> +{
> + struct drm_connector_init_priv *priv = test->priv;
> + const struct drm_display_mode *mode;
> + struct drm_device *drm = &priv->drm;
> + unsigned long long rate;
> +
> + mode = drm_display_mode_from_cea_vic(drm, 16);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_YUV422);
> + KUNIT_ASSERT_GT(test, rate, 0);
> + KUNIT_EXPECT_EQ(test, mode->clock * 1000, rate);
> +}
> +
> +static struct kunit_case drm_connector_hdmi_compute_mode_clock_tests[] = {
> + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb),
> + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc),
> + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_10bpc_vic_1),
> + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc),
> + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_12bpc_vic_1),
> + KUNIT_CASE(drm_test_drm_connector_hdmi_compute_mode_clock_rgb_double),
> + KUNIT_CASE_PARAM(drm_test_connector_hdmi_compute_mode_clock_yuv420_valid,
> + drm_connector_hdmi_compute_mode_clock_yuv420_valid_gen_params),
> + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_invalid),
> + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc),
> + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc),
> + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc),
> + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc),
> + KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc),
> + { }
> +};
> +
> +static struct kunit_suite drm_connector_hdmi_compute_mode_clock_test_suite = {
> + .name = "drm_test_connector_hdmi_compute_mode_clock",
> + .init = drm_test_connector_init,
> + .test_cases = drm_connector_hdmi_compute_mode_clock_tests,
> +};
> +
> kunit_test_suites(
> &drmm_connector_hdmi_init_test_suite,
> &drmm_connector_init_test_suite,
> &drm_connector_attach_broadcast_rgb_property_test_suite,
> + &drm_connector_hdmi_compute_mode_clock_test_suite,
> &drm_get_tv_mode_from_name_test_suite,
> &drm_hdmi_connector_get_broadcast_rgb_name_test_suite,
> &drm_hdmi_connector_get_output_format_name_test_suite
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 74db5ce47e01..0cc5a8732664 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -38,6 +38,7 @@ struct drm_connector_helper_funcs;
> struct drm_modeset_acquire_ctx;
> struct drm_device;
> struct drm_crtc;
> +struct drm_display_mode;
> struct drm_encoder;
> struct drm_panel;
> struct drm_property;
> @@ -2136,6 +2137,10 @@ void drm_connector_attach_privacy_screen_properties(struct drm_connector *conn);
> void drm_connector_attach_privacy_screen_provider(
> struct drm_connector *connector, struct drm_privacy_screen *priv);
> void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state);
> +unsigned long long
> +drm_connector_hdmi_compute_mode_clock(const struct drm_display_mode *mode,
> + unsigned int bpc,
> + enum hdmi_colorspace fmt);
>
> /**
> * struct drm_tile_group - Tile group metadata
>
> --
> 2.43.0
>
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> Most HDMI drivers have some code to calculate the TMDS character rate,
> usually to adjust an internal clock to match what the mode requires.
>
> Since the TMDS character rates mostly depends on the resolution, whether
> we need to repeat pixels or not, the bpc count and the format, we can
> now derive it from the HDMI connector state that stores all those infos
> and remove the duplication from drivers.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic.c | 1 +
> drivers/gpu/drm/drm_atomic_state_helper.c | 44 +++++
> .../gpu/drm/tests/drm_atomic_state_helper_test.c | 169 ++++++++++++++++
> drivers/gpu/drm/tests/drm_kunit_edid.h | 216 +++++++++++++++++++++
> include/drm/drm_connector.h | 5 +
> 5 files changed, 435 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 7aaa2a4d70d9..4f6493f91eed 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1150,6 +1150,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
> drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
> drm_printf(p, "\toutput_format=%s\n",
> drm_hdmi_connector_get_output_format_name(state->hdmi.output_format));
> + drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate);
> }
>
> if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 92e1b087c3d0..74bc3cc53c2d 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -682,6 +682,41 @@ static bool hdmi_is_full_range(const struct drm_connector *connector,
> return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false;
> }
>
> +static enum drm_mode_status
> +hdmi_clock_valid(const struct drm_connector *connector,
> + const struct drm_display_mode *mode,
> + unsigned long long clock)
> +{
> + const struct drm_display_info *info = &connector->display_info;
> +
> + if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000)
> + return MODE_CLOCK_HIGH;
> +
> + return MODE_OK;
> +}
> +
> +static int
> +hdmi_compute_clock(const struct drm_connector *connector,
> + struct drm_connector_state *state,
> + const struct drm_display_mode *mode,
> + unsigned int bpc, enum hdmi_colorspace fmt)
> +{
> + enum drm_mode_status status;
> + unsigned long long clock;
> +
> + clock = drm_connector_hdmi_compute_mode_clock(mode, bpc, fmt);
> + if (!clock)
> + return -EINVAL;
> +
> + status = hdmi_clock_valid(connector, mode, clock);
> + if (status != MODE_OK)
> + return -EINVAL;
> +
> + state->hdmi.tmds_char_rate = clock;
> +
> + return 0;
> +}
> +
> /**
> * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
> * @connector: DRM Connector
> @@ -701,9 +736,18 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
> drm_atomic_get_old_connector_state(state, connector);
> struct drm_connector_state *new_state =
> drm_atomic_get_new_connector_state(state, connector);
> + const struct drm_display_mode *mode =
> + connector_state_get_mode(new_state);
> + int ret;
>
> new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
>
> + ret = hdmi_compute_clock(connector, new_state, mode,
> + new_state->hdmi.output_bpc,
> + new_state->hdmi.output_format);
> + if (ret)
> + return ret;
> +
> if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||
> old_state->hdmi.output_bpc != new_state->hdmi.output_bpc ||
> old_state->hdmi.output_format != new_state->hdmi.output_format) {
> diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> index 4e2ec436987b..d76fafb91025 100644
> --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> @@ -70,6 +70,9 @@ static int light_up_connector(struct kunit *test,
> conn_state = drm_atomic_get_connector_state(state, connector);
> KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
>
> + conn_state->hdmi.output_bpc = connector->max_bpc;
> + conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB;
> +
> ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> @@ -813,6 +816,146 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
> KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
> }
>
> +/*
> + * Test that when doing a commit which would use RGB 8bpc, the TMDS
> + * clock rate stored in the connector state is equal to the mode clock
> + */
> +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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> + 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);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that when doing a commit which would use RGB 10bpc, the TMDS
> + * clock rate stored in the connector state is equal to 1.25 times the
> + * mode pixel clock
> + */
> +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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 10);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that when doing a commit which would use RGB 12bpc, the TMDS
> + * clock rate stored in the connector state is equal to 1.5 times the
> + * mode pixel clock
> + */
> +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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 12);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> 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),
> @@ -824,6 +967,9 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
> KUNIT_CASE(drm_test_check_broadcast_rgb_crtc_mode_not_changed),
> 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_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),
> /*
> * TODO: We should have tests to check that a change in the
> * format triggers a CRTC mode change just like we do for the
> @@ -955,12 +1101,35 @@ static void drm_test_check_format_value(struct kunit *test)
> KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
> }
>
> +/*
> + * Test that the value of the output format property out of reset is set
> + * to 0, and will be computed at atomic_check time.
> + */
> +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_atomic_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),
> { }
> };
>
> diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h
> index 2bba316de064..24f3377ef0f0 100644
> --- a/drivers/gpu/drm/tests/drm_kunit_edid.h
> +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h
> @@ -103,4 +103,220 @@ const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
> 0x00, 0x00, 0x00, 0xd0
> };
>
> +/*
> + * 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 1a 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 7a
> + *
> + * 02 03 1b b1 e3 05 00 20 41 10 e2 00 ca 6d 03 0c
> + * 00 12 34 78 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 a8
> + *
> + * ----------------
> + *
> + * 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
> + * Undefined display color type
> + * 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: 0x7a
> + *
> + * ----------------
> + *
> + * Block 1, CTA-861 Extension Block:
> + * Revision: 3
> + * Underscans IT Video Formats by default
> + * Supports YCbCr 4:4:4
> + * Supports YCbCr 4:2:2
> + * 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: Selectable (via AVI YQ)
> + * 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
> + * DC_48bit
> + * DC_36bit
> + * DC_30bit
> + * DC_Y444
> + * Maximum TMDS clock: 200 MHz
> + * Extended HDMI video details:
> + * Checksum: 0xa8 Unused space in Extension Block: 100 bytes
> + */
> +const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_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,
> + 0x1a, 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, 0x1e, 0x46, 0x0f, 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, 0x7a, 0x02, 0x03, 0x1b, 0xb1,
> + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x6d, 0x03, 0x0c,
> + 0x00, 0x12, 0x34, 0x78, 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, 0xa8
> +};
> +
> +/*
> + * 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 0a 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 8a
> + *
> + * 02 03 1b b1 e3 05 00 20 41 10 e2 00 ca 6d 03 0c
> + * 00 12 34 78 44 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 8c
> + *
> + * ----------------
> + *
> + * 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
> + * RGB color 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: 0x8a
> + *
> + * ----------------
> + *
> + * Block 1, CTA-861 Extension Block:
> + * Revision: 3
> + * Underscans IT Video Formats by default
> + * Supports YCbCr 4:4:4
> + * Supports YCbCr 4:2:2
> + * 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: Selectable (via AVI YQ)
> + * 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
> + * DC_48bit
> + * DC_36bit
> + * DC_30bit
> + * DC_Y444
> + * Maximum TMDS clock: 340 MHz
> + * Extended HDMI video details:
> + * Checksum: 0x8c Unused space in Extension Block: 100 bytes
> + */
> +const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = {
> + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
> + 0x0a, 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, 0x1e, 0x46, 0x0f, 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, 0x8a, 0x02, 0x03, 0x1b, 0xb1,
> + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x6d, 0x03, 0x0c,
> + 0x00, 0x12, 0x34, 0x78, 0x44, 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, 0x8c
> +};
> +
> #endif // DRM_KUNIT_EDID_H_
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 0cc5a8732664..59016d9c17f5 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1085,6 +1085,11 @@ struct drm_connector_state {
> * @output_format: Pixel format to output in.
> */
> enum hdmi_colorspace output_format;
> +
> + /**
> + * @tmds_char_rate: TMDS Character Rate, in Hz.
> + */
> + unsigned long long tmds_char_rate;
> } hdmi;
> };
>
>
> --
> 2.43.0
>
>
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> Most of the HDMI controllers have an upper TMDS character rate limit
> they can't exceed. On "embedded"-grade display controllers, it will
> typically be lower than what high-grade monitors can provide these days,
> so drivers will filter the TMDS character rate based on the controller
> capabilities.
>
> To make that easier to handle for drivers, let's provide an optional
> hook to be implemented by drivers so they can tell the HDMI controller
> helpers if a given TMDS character rate is reachable for them or not.
>
> This will then be useful to figure out the best format and bpc count for
> a given mode.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Reviewed-by: Dave Stevenson <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic_state_helper.c | 9 +++
> drivers/gpu/drm/drm_connector.c | 4 ++
> .../gpu/drm/tests/drm_atomic_state_helper_test.c | 69 ++++++++++++++++++++++
> drivers/gpu/drm/tests/drm_connector_test.c | 15 +++++
> include/drm/drm_connector.h | 30 ++++++++++
> 5 files changed, 127 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index 74bc3cc53c2d..a36edda590f8 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -687,11 +687,20 @@ hdmi_clock_valid(const struct drm_connector *connector,
> const struct drm_display_mode *mode,
> unsigned long long clock)
> {
> + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
> const struct drm_display_info *info = &connector->display_info;
>
> if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000)
> return MODE_CLOCK_HIGH;
>
> + if (funcs && funcs->tmds_char_rate_valid) {
> + enum drm_mode_status status;
> +
> + status = funcs->tmds_char_rate_valid(connector, mode, clock);
> + if (status != MODE_OK)
> + return status;
> + }
> +
> return MODE_OK;
> }
>
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 667326b09acc..9f314fee26ce 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -457,6 +457,7 @@ EXPORT_SYMBOL(drmm_connector_init);
> * @dev: DRM device
> * @connector: A pointer to the HDMI connector to init
> * @funcs: callbacks for this connector
> + * @hdmi_funcs: HDMI-related callbacks for this connector
> * @connector_type: user visible type of the connector
> * @ddc: optional pointer to the associated ddc adapter
> * @supported_formats: Bitmask of @hdmi_colorspace listing supported output formats
> @@ -476,6 +477,7 @@ EXPORT_SYMBOL(drmm_connector_init);
> int drmm_connector_hdmi_init(struct drm_device *dev,
> struct drm_connector *connector,
> const struct drm_connector_funcs *funcs,
> + const struct drm_connector_hdmi_funcs *hdmi_funcs,
> int connector_type,
> struct i2c_adapter *ddc,
> unsigned long supported_formats,
> @@ -512,6 +514,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> if (max_bpc > 8)
> drm_connector_attach_hdr_output_metadata_property(connector);
>
> + connector->hdmi.funcs = hdmi_funcs;
> +
> return 0;
> }
> EXPORT_SYMBOL(drmm_connector_hdmi_init);
> diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> index d76fafb91025..e7dbdd4a4e7f 100644
> --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> @@ -110,6 +110,21 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto
> return 0;
> }
>
> +static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = {
> +};
> +
> +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,
> +};
> +
> static int dummy_connector_get_modes(struct drm_connector *connector)
> {
> struct drm_atomic_helper_connector_hdmi_priv *priv =
> @@ -192,6 +207,7 @@ drm_atomic_helper_connector_hdmi_init(struct kunit *test,
> conn = &priv->connector;
> ret = drmm_connector_hdmi_init(drm, conn,
> &dummy_connector_funcs,
> + &dummy_connector_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> NULL,
> formats,
> @@ -956,6 +972,58 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
> KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500);
> }
>
> +/*
> + * Test that if we filter a rate through our hook, it's indeed rejected
> + * by the whole atomic_check logic.
> + *
> + * We do so by first doing a commit on the pipeline to make sure that it
> + * works, change the HDMI helpers pointer, and then try the same commit
> + * again to see if it fails as it should.
> + */
> +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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> + 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);
> +
> + /* You shouldn't be doing that at home. */
> + 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);
> +}
> +
> 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),
> @@ -965,6 +1033,7 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
> KUNIT_CASE(drm_test_check_broadcast_rgb_limited_cea_mode_vic_1),
> 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_hdmi_funcs_reject_rate),
> 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_tmds_char_rate_rgb_8bpc),
> diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
> index 0a838924a546..6a3651b08c81 100644
> --- a/drivers/gpu/drm/tests/drm_connector_test.c
> +++ b/drivers/gpu/drm/tests/drm_connector_test.c
> @@ -22,6 +22,9 @@ struct drm_connector_init_priv {
> struct i2c_adapter ddc;
> };
>
> +static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = {
> +};
> +
> static const struct drm_connector_funcs dummy_funcs = {
> .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> @@ -187,6 +190,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -205,6 +209,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> NULL,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -223,6 +228,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -241,6 +247,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -263,6 +270,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -297,6 +305,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -331,6 +340,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -361,6 +371,7 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> 0,
> @@ -379,6 +390,7 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_YUV422),
> @@ -398,6 +410,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> connector_type,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -431,6 +444,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
>
> ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> connector_type,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> @@ -695,6 +709,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
>
> ret = drmm_connector_hdmi_init(&priv->drm, connector,
> &dummy_funcs,
> + &dummy_hdmi_funcs,
> DRM_MODE_CONNECTOR_HDMIA,
> &priv->ddc,
> BIT(HDMI_COLORSPACE_RGB),
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 59016d9c17f5..3eaf4d54364d 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1093,6 +1093,30 @@ struct drm_connector_state {
> } hdmi;
> };
>
> +/**
> + * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions
> + */
> +struct drm_connector_hdmi_funcs {
> + /**
> + * @tmds_char_rate_valid:
> + *
> + * This callback is invoked at atomic_check time to figure out
> + * whether a particular TMDS character rate is supported by the
> + * driver.
> + *
> + * The @tmds_char_rate_valid callback is optional.
> + *
> + * Returns:
> + *
> + * Either &drm_mode_status.MODE_OK or one of the failure reasons
> + * in &enum drm_mode_status.
> + */
> + enum drm_mode_status
> + (*tmds_char_rate_valid)(const struct drm_connector *connector,
> + const struct drm_display_mode *mode,
> + unsigned long long tmds_rate);
> +};
> +
> /**
> * struct drm_connector_funcs - control connectors on a given device
> *
> @@ -1967,6 +1991,11 @@ struct drm_connector {
> * supported by the controller.
> */
> unsigned long supported_formats;
> +
> + /**
> + * @funcs: HDMI connector Control Functions
> + */
> + const struct drm_connector_hdmi_funcs *funcs;
> } hdmi;
> };
>
> @@ -1989,6 +2018,7 @@ int drmm_connector_init(struct drm_device *dev,
> int drmm_connector_hdmi_init(struct drm_device *dev,
> struct drm_connector *connector,
> const struct drm_connector_funcs *funcs,
> + const struct drm_connector_hdmi_funcs *hdmi_funcs,
> int connector_type,
> struct i2c_adapter *ddc,
> unsigned long supported_formats,
>
> --
> 2.43.0
>
Hi Maxime
On Thu, 7 Dec 2023 at 15:50, Maxime Ripard <[email protected]> wrote:
>
> Now that we have all the infrastructure needed, we can add some code
> that will, for a given connector state and mode, compute the best output
> format and bpc.
>
> The algorithm is the same one than the one already found in i915 and
> vc4.
We seem to have some extra words in this sentence.
"The algorithm is the same as that already found in i915 and vc4."?
Possibly "equivalent to" instead of "the same as", as i915 is slightly
different.
> Signed-off-by: Maxime Ripard <[email protected]>
> ---
> drivers/gpu/drm/drm_atomic_state_helper.c | 183 ++++++-
> .../gpu/drm/tests/drm_atomic_state_helper_test.c | 529 ++++++++++++++++++++-
> drivers/gpu/drm/tests/drm_kunit_edid.h | 160 +++++++
> 3 files changed, 860 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
> index a36edda590f8..2442b5a2d94f 100644
> --- a/drivers/gpu/drm/drm_atomic_state_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
> @@ -682,6 +682,96 @@ static bool hdmi_is_full_range(const struct drm_connector *connector,
> return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL ? true : false;
> }
>
> +static bool
> +sink_supports_format_bpc(const struct drm_connector *connector,
> + const struct drm_display_info *info,
> + const struct drm_display_mode *mode,
> + unsigned int format, unsigned int bpc)
> +{
> + struct drm_device *dev = connector->dev;
> + u8 vic = drm_match_cea_mode(mode);
> +
> + if (vic == 1 && bpc != 8) {
> + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
> + return false;
> + }
> +
> + if (!info->is_hdmi &&
> + (format != HDMI_COLORSPACE_RGB || bpc != 8)) {
> + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
> + return false;
> + }
> +
> + if (!(connector->hdmi.supported_formats & BIT(format))) {
> + drm_dbg(dev, "%s format unsupported by the connector.\n",
> + drm_hdmi_connector_get_output_format_name(format));
> + return false;
> + }
> +
> + switch (format) {
> + case HDMI_COLORSPACE_RGB:
> + drm_dbg(dev, "RGB Format, checking the constraints.\n");
> +
> + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
> + return false;
We've dropped this check from vc4 in our downstream kernel as it stops
you using the prebaked EDIDs (eg drm.edid_firmware=edid/1024x768.bin),
or any other EDID that is defined as an analog monitor.
The EDID parsing bombs out at [1], so info->color_formats gets left at 0.
RGB is mandatory for both DVI and HDMI, so rejecting it seems overly fussy.
I don't see i915 making use of info->color_formats at all. The
equivalent function looks to be intel_hdmi_sink_bpc_possible [2] which
just accepts anything for 8bpc output.
Dave
PS I'll have to defer looking at patch 16 for another day - it just
needs a bit more analysis than I can fit in today.
[1] https://elixir.free-electrons.com/linux/latest/source/drivers/gpu/drm/drm_edid.c#L6533
[2] https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/i915/display/intel_hdmi.c#L1909
> +
> + if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
> + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
> + return false;
> + }
> +
> + if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
> + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
> + return false;
> + }
> +
> + drm_dbg(dev, "RGB format supported in that configuration.\n");
> +
> + return true;
> +
> + case HDMI_COLORSPACE_YUV422:
> + drm_dbg(dev, "YUV422 format, checking the constraints.\n");
> +
> + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
> + drm_dbg(dev, "Sink doesn't support YUV422.\n");
> + return false;
> + }
> +
> + if (bpc != 12) {
> + drm_dbg(dev, "YUV422 only supports 12 bpc.\n");
> + return false;
> + }
> +
> + drm_dbg(dev, "YUV422 format supported in that configuration.\n");
> +
> + return true;
> +
> + case HDMI_COLORSPACE_YUV444:
> + drm_dbg(dev, "YUV444 format, checking the constraints.\n");
> +
> + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) {
> + drm_dbg(dev, "Sink doesn't support YUV444.\n");
> + return false;
> + }
> +
> + if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) {
> + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
> + return false;
> + }
> +
> + if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) {
> + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
> + return false;
> + }
> +
> + drm_dbg(dev, "YUV444 format supported in that configuration.\n");
> +
> + return true;
> + }
> +
> + return false;
> +}
> +
> static enum drm_mode_status
> hdmi_clock_valid(const struct drm_connector *connector,
> const struct drm_display_mode *mode,
> @@ -726,6 +816,95 @@ hdmi_compute_clock(const struct drm_connector *connector,
> return 0;
> }
>
> +static bool
> +hdmi_try_format_bpc(const struct drm_connector *connector,
> + struct drm_connector_state *state,
> + const struct drm_display_mode *mode,
> + unsigned int bpc, enum hdmi_colorspace fmt)
> +{
> + const struct drm_display_info *info = &connector->display_info;
> + struct drm_device *dev = connector->dev;
> + int ret;
> +
> + drm_dbg(dev, "Trying %s output format\n",
> + drm_hdmi_connector_get_output_format_name(fmt));
> +
> + if (!sink_supports_format_bpc(connector, info, mode, fmt, bpc)) {
> + drm_dbg(dev, "%s output format not supported with %u bpc\n",
> + drm_hdmi_connector_get_output_format_name(fmt), bpc);
> + return false;
> + }
> +
> + ret = hdmi_compute_clock(connector, state, mode, bpc, fmt);
> + if (ret) {
> + drm_dbg(dev, "Couldn't compute clock for %s output format and %u bpc\n",
> + drm_hdmi_connector_get_output_format_name(fmt), bpc);
> + return false;
> + }
> +
> + drm_dbg(dev, "%s output format supported with %u (TMDS char rate: %llu Hz)\n",
> + drm_hdmi_connector_get_output_format_name(fmt), bpc, state->hdmi.tmds_char_rate);
> +
> + return true;
> +}
> +
> +static int
> +hdmi_compute_format(const struct drm_connector *connector,
> + struct drm_connector_state *state,
> + const struct drm_display_mode *mode,
> + unsigned int bpc)
> +{
> + struct drm_device *dev = connector->dev;
> +
> + if (hdmi_try_format_bpc(connector, state, mode, bpc, HDMI_COLORSPACE_RGB)) {
> + state->hdmi.output_format = HDMI_COLORSPACE_RGB;
> + return 0;
> + }
> +
> + if (hdmi_try_format_bpc(connector, state, mode, bpc, HDMI_COLORSPACE_YUV422)) {
> + state->hdmi.output_format = HDMI_COLORSPACE_YUV422;
> + return 0;
> + }
> +
> + drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n");
> +
> + return -EINVAL;
> +}
> +
> +static int
> +hdmi_compute_config(const struct drm_connector *connector,
> + struct drm_connector_state *state,
> + const struct drm_display_mode *mode)
> +{
> + struct drm_device *dev = connector->dev;
> + unsigned int max_bpc = clamp_t(unsigned int,
> + state->max_bpc,
> + 8, connector->max_bpc);
> + unsigned int bpc;
> + int ret;
> +
> + for (bpc = max_bpc; bpc >= 8; bpc -= 2) {
> + drm_dbg(dev, "Trying with a %d bpc output\n", bpc);
> +
> + ret = hdmi_compute_format(connector, state, mode, bpc);
> + if (ret)
> + continue;
> +
> + state->hdmi.output_bpc = bpc;
> +
> + drm_dbg(dev,
> + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n",
> + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
> + state->hdmi.output_bpc,
> + drm_hdmi_connector_get_output_format_name(state->hdmi.output_format),
> + state->hdmi.tmds_char_rate);
> +
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> /**
> * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
> * @connector: DRM Connector
> @@ -751,9 +930,7 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
>
> new_state->hdmi.is_full_range = hdmi_is_full_range(connector, new_state);
>
> - ret = hdmi_compute_clock(connector, new_state, mode,
> - new_state->hdmi.output_bpc,
> - new_state->hdmi.output_format);
> + ret = hdmi_compute_config(connector, new_state, mode);
> if (ret)
> return ret;
>
> diff --git a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> index e7dbdd4a4e7f..860e34b00fee 100644
> --- a/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_atomic_state_helper_test.c
> @@ -70,9 +70,6 @@ static int light_up_connector(struct kunit *test,
> conn_state = drm_atomic_get_connector_state(state, connector);
> KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
>
> - conn_state->hdmi.output_bpc = connector->max_bpc;
> - conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB;
> -
> ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
> KUNIT_EXPECT_EQ(test, ret, 0);
>
> @@ -720,10 +717,15 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
> 10);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> 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);
>
> @@ -741,11 +743,11 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
> 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.output_bpc = 8;
> + new_conn_state->max_requested_bpc = 8;
>
> KUNIT_ASSERT_NE(test,
> - old_conn_state->hdmi.output_bpc,
> - new_conn_state->hdmi.output_bpc);
> + old_conn_state->max_requested_bpc,
> + new_conn_state->max_requested_bpc);
>
> ret = drm_atomic_check_only(state);
> KUNIT_ASSERT_EQ(test, ret, 0);
> @@ -789,10 +791,15 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
> 10);
> KUNIT_ASSERT_NOT_NULL(test, priv);
>
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> 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);
>
> @@ -832,6 +839,56 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
> KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
> }
>
> +/*
> + * Test that if we have an HDMI connector but a !HDMI display, we always
> + * output RGB with 8 bpc.
> + */
> +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_atomic_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;
> + ret = set_connector_edid(test, conn,
> + test_edid_dvi_1080p,
> + ARRAY_SIZE(test_edid_dvi_1080p));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_FALSE(test, info->is_hdmi);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + 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);
> +
> + 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);
> +}
> +
> /*
> * Test that when doing a commit which would use RGB 8bpc, the TMDS
> * clock rate stored in the connector state is equal to the mode clock
> @@ -1024,6 +1081,452 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
> KUNIT_EXPECT_LT(test, ret, 0);
> }
>
> +/*
> + * Test that if:
> + * - We have an HDMI connector supporting RGB only
> + * - The chosen mode has a TMDS character rate higher than the display
> + * supports in RGB/12bpc
> + * - The chosen mode has a TMDS character rate lower than the display
> + * supports in RGB/10bpc.
> + *
> + * Then we will pick the latter, and the computed TMDS character rate
> + * will be equal to 1.25 times the mode pixel clock.
> + */
> +static void drm_test_check_max_tmds_rate_bpc_fallback(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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 12);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that if:
> + * - We have an HDMI connector supporting both RGB and YUV422 and up to
> + * 12 bpc
> + * - The chosen mode has a TMDS character rate higher than the display
> + * supports in RGB/12bpc
> + * - The chosen mode has a TMDS character rate lower than the display
> + * supports in YUV422/12bpc.
> + *
> + * Then we will pick the latter, and the computed TMDS character rate
> + * will be equal to the mode pixel clock.
> + */
> +static void drm_test_check_max_tmds_rate_format_fallback(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_atomic_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;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> + KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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, 12);
> + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_YUV422);
> + KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000);
> +}
> +
> +/*
> + * Test that if a driver and screen supports RGB and YUV formats, and we
> + * try to set the VIC 1 mode, we end up with 8bpc RGB even if we could
> + * have had a higher bpc.
> + */
> +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_atomic_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;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + mode = drm_display_mode_from_cea_vic(drm, 1);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + /*
> + * NOTE: We can't use drm_connector_hdmi_compute_mode_clock()
> + * here because we're trying to get the rate of an invalid
> + * configuration.
> + *
> + * Thus, we have to calculate the rate by hand.
> + */
> + rate = mode->clock * 1500;
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, mode, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that if a driver supports only RGB but the screen also supports
> + * YUV formats, we only end up with an RGB format.
> + */
> +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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 12);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> +
> + /*
> + * We're making sure that YUV422 would be the preferred option
> + * here: we're always favouring higher bpc, we can't have RGB
> + * because the TMDS character rate exceeds the maximum supported
> + * by the display, and YUV422 works for that display.
> + *
> + * But since the driver only supports RGB, we should fallback to
> + * a lower bpc with RGB.
> + */
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that if a screen supports only RGB but the driver also supports
> + * YUV formats, we only end up with an RGB format.
> + */
> +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_atomic_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;
> + 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);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> +
> + /*
> + * We're making sure that YUV422 would be the preferred option
> + * here: we're always favouring higher bpc, we can't have RGB
> + * because the TMDS character rate exceeds the maximum supported
> + * by the display, and YUV422 works for that display.
> + *
> + * But since the display only supports RGB, we should fallback to
> + * a lower bpc with RGB.
> + */
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_GT(test, rate, info->max_tmds_clock * 1000);
> +
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that if a display supports higher bpc but the driver only
> + * supports 8 bpc, we only end up with 8 bpc even if we could have had a
> + * higher bpc.
> + */
> +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_atomic_helper_connector_hdmi_init(test,
> + BIT(HDMI_COLORSPACE_RGB),
> + 8);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> +
> + /*
> + * We're making sure that we have headroom on the TMDS character
> + * clock to actually use 12bpc.
> + */
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> +/*
> + * Test that if a driver supports higher bpc but the display only
> + * supports 8 bpc, we only end up with 8 bpc even if we could have had a
> + * higher bpc.
> + */
> +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_atomic_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;
> + ret = set_connector_edid(test, conn,
> + test_edid_hdmi_1080p_rgb_max_340mhz,
> + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_340mhz));
> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + info = &conn->display_info;
> + KUNIT_ASSERT_TRUE(test, info->is_hdmi);
> + KUNIT_ASSERT_GT(test, info->max_tmds_clock, 0);
> +
> + ctx = drm_kunit_helper_acquire_ctx_alloc(test);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
> +
> + preferred = find_preferred_mode(conn);
> + KUNIT_ASSERT_NOT_NULL(test, preferred);
> +
> + /*
> + * We're making sure that we have headroom on the TMDS character
> + * clock to actually use 12bpc.
> + */
> + rate = drm_connector_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
> + KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
> +
> + drm = &priv->drm;
> + crtc = priv->crtc;
> + ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
> + 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);
> +}
> +
> 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),
> @@ -1034,8 +1537,16 @@ 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),
> KUNIT_CASE(drm_test_check_hdmi_funcs_reject_rate),
> + KUNIT_CASE(drm_test_check_max_tmds_rate_bpc_fallback),
> + KUNIT_CASE(drm_test_check_max_tmds_rate_format_fallback),
> 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),
> @@ -1167,7 +1678,7 @@ static void drm_test_check_format_value(struct kunit *test)
>
> conn = &priv->connector;
> conn_state = conn->state;
> - KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
> + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, 0);
> }
>
> /*
> diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h
> index 24f3377ef0f0..3e3527c58c31 100644
> --- a/drivers/gpu/drm/tests/drm_kunit_edid.h
> +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h
> @@ -1,6 +1,64 @@
> #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 0a 00 00 00 00 00 00 00
> + * 00 00 00 00 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 00 ab
> + *
> + * ----------------
> + *
> + * 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
> + * RGB color 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: none
> + * 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:
> + * Checksum: 0xab
> + */
> +const unsigned char test_edid_dvi_1080p[] = {
> + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78,
> + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 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, 0x1e, 0x46, 0x0f, 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, 0x00, 0xab
> +};
> +
> /*
> * edid-decode (hex):
> *
> @@ -103,6 +161,108 @@ const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = {
> 0x00, 0x00, 0x00, 0xd0
> };
>
> +/*
> + * 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: 340 MHz
> + * Extended HDMI video details:
> + * Checksum: 0xd0 Unused space in Extension Block: 100 bytes
> + */
> +const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = {
> + 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, 0x44, 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
> +};
> +
> /*
> * edid-decode (hex):
> *
>
> --
> 2.43.0
>
Hi,
LGTM,
On 2023/12/7 23:50, Maxime Ripard wrote:
> The sun4i_hdmi driver still uses the non-atomic variants of the encoder
> hooks, so let's convert to their atomic equivalents.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Acked-by: Sui Jingfeng <[email protected]>
Hi,
On 2023/12/7 23:50, Maxime Ripard wrote:
> We're not doing anything special in atomic_mode_set so we can simply
> merge it into atomic_enable.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Acked-by: Sui Jingfeng <[email protected]>
Hi,
On 2023/12/7 23:50, Maxime Ripard wrote:
> atomic_check and mode_valid do not check for the same things which can
> lead to surprising result if the userspace commits a mode that didn't go
> through mode_valid. Let's merge the two implementations into a function
> called by both.
>
> Signed-off-by: Maxime Ripard <[email protected]>
Acked-by: Sui Jingfeng <[email protected]>
Hi Dave,
On Thu, Dec 14, 2023 at 02:43:37PM +0000, Dave Stevenson wrote:
> 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.
Thanks for your review. Does that mean that you would prefer the tests
to be in a separate patch?
Maxime
Hi Maxime
On Fri, 12 Jan 2024 at 13:59, Maxime Ripard <[email protected]> wrote:
>
> Hi Dave,
>
> On Thu, Dec 14, 2023 at 02:43:37PM +0000, Dave Stevenson wrote:
> > 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.
>
> Thanks for your review. Does that mean that you would prefer the tests
> to be in a separate patch?
If there was a need for a respin, then I think ideally yes, or at
least a mention in the commit text ("Let's plumb it into the newly
created HDMI connector*, and add appropriate unit tests*").
Overall I'm not that fussed though.
Dave
On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
>
> Signed-off-by: Maxime Ripard <[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().
> + *
This is a good time to document this in more detail. There might be two
different things being affected:
1. The signalling (InfoFrame/SDP/...)
2. The color pipeline processing
All values of Broadcast RGB always affect the color pipeline processing
such that a full-range input to the CRTC is converted to either full- or
limited-range, depending on what the monitor is supposed to accept.
When automatic is selected, does that mean that there is no signalling,
or that the signalling matches what the monitor is supposed to accept
according to the spec? Also, is this really HDMI specific?
When full or limited is selected and the monitor doesn't support the
signalling, what happens?
> * 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
>
On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
> >
> > Signed-off-by: Maxime Ripard <[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().
> > + *
>
> This is a good time to document this in more detail. There might be two
> different things being affected:
>
> 1. The signalling (InfoFrame/SDP/...)
> 2. The color pipeline processing
>
> All values of Broadcast RGB always affect the color pipeline processing
> such that a full-range input to the CRTC is converted to either full- or
> limited-range, depending on what the monitor is supposed to accept.
>
> When automatic is selected, does that mean that there is no signalling,
> or that the signalling matches what the monitor is supposed to accept
> according to the spec? Also, is this really HDMI specific?
>
> When full or limited is selected and the monitor doesn't support the
> signalling, what happens?
Forgot to mention: user-space still has no control over RGB vs YCbCr on
the cable, so is this only affecting RGB? If not, how does it affect
YCbCr?
>
> > * 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
> >
On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
> >
> > Signed-off-by: Maxime Ripard <[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().
> > + *
>
> This is a good time to document this in more detail.
I have the feeling that it already is documented in more detail. But
anyway, last time we discussed it the answer was basically to not bother
and just merge the thing. So I'm getting some mixed signals here.
> There might be two different things being affected:
>
> 1. The signalling (InfoFrame/SDP/...)
> 2. The color pipeline processing
>
> All values of Broadcast RGB always affect the color pipeline processing
> such that a full-range input to the CRTC is converted to either full- or
> limited-range, depending on what the monitor is supposed to accept.
>
> When automatic is selected, does that mean that there is no signalling,
> or that the signalling matches what the monitor is supposed to accept
> according to the spec?
The doc states that "Infoframes will be generated according to that
value". Is it ambiguous?
> Also, is this really HDMI specific?
Probably not, but it can easily be expanded to other connector types
when needs be.
> When full or limited is selected and the monitor doesn't support the
> signalling, what happens?
I would expect colors to be off
Maxime
On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> > On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
> > >
> > > Signed-off-by: Maxime Ripard <[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().
> > > + *
> >
> > This is a good time to document this in more detail. There might be two
> > different things being affected:
> >
> > 1. The signalling (InfoFrame/SDP/...)
> > 2. The color pipeline processing
> >
> > All values of Broadcast RGB always affect the color pipeline processing
> > such that a full-range input to the CRTC is converted to either full- or
> > limited-range, depending on what the monitor is supposed to accept.
> >
> > When automatic is selected, does that mean that there is no signalling,
> > or that the signalling matches what the monitor is supposed to accept
> > according to the spec? Also, is this really HDMI specific?
> >
> > When full or limited is selected and the monitor doesn't support the
> > signalling, what happens?
>
> Forgot to mention: user-space still has no control over RGB vs YCbCr on
> the cable, so is this only affecting RGB? If not, how does it affect
> YCbCr?
i915 only supports it for RGB indeed:
https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/i915/display/intel_hdmi.c#L2150
Maxime
On Mon, Jan 15, 2024 at 04:25:41PM +0100, Maxime Ripard wrote:
> On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> > On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
> > >
> > > Signed-off-by: Maxime Ripard <[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().
> > > + *
> >
> > This is a good time to document this in more detail.
>
> I have the feeling that it already is documented in more detail. But
> anyway, last time we discussed it the answer was basically to not bother
> and just merge the thing. So I'm getting some mixed signals here.
I'm all for merging and not trying to improve the property but
documenting it in more detail is definitely something I want to see.
> > There might be two different things being affected:
> >
> > 1. The signalling (InfoFrame/SDP/...)
> > 2. The color pipeline processing
> >
> > All values of Broadcast RGB always affect the color pipeline processing
> > such that a full-range input to the CRTC is converted to either full- or
> > limited-range, depending on what the monitor is supposed to accept.
> >
> > When automatic is selected, does that mean that there is no signalling,
> > or that the signalling matches what the monitor is supposed to accept
> > according to the spec?
>
> The doc states that "Infoframes will be generated according to that
> value". Is it ambiguous?
>
> > Also, is this really HDMI specific?
>
> Probably not, but it can easily be expanded to other connector types
> when needs be.
>
> > When full or limited is selected and the monitor doesn't support the
> > signalling, what happens?
>
> I would expect colors to be off
>
> Maxime
On Thu, Dec 14, 2023 at 03:10:43PM +0000, Dave Stevenson wrote:
> > +static bool
> > +sink_supports_format_bpc(const struct drm_connector *connector,
> > + const struct drm_display_info *info,
> > + const struct drm_display_mode *mode,
> > + unsigned int format, unsigned int bpc)
> > +{
> > + struct drm_device *dev = connector->dev;
> > + u8 vic = drm_match_cea_mode(mode);
> > +
> > + if (vic == 1 && bpc != 8) {
> > + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
> > + return false;
> > + }
> > +
> > + if (!info->is_hdmi &&
> > + (format != HDMI_COLORSPACE_RGB || bpc != 8)) {
> > + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
> > + return false;
> > + }
> > +
> > + if (!(connector->hdmi.supported_formats & BIT(format))) {
> > + drm_dbg(dev, "%s format unsupported by the connector.\n",
> > + drm_hdmi_connector_get_output_format_name(format));
> > + return false;
> > + }
> > +
> > + switch (format) {
> > + case HDMI_COLORSPACE_RGB:
> > + drm_dbg(dev, "RGB Format, checking the constraints.\n");
> > +
> > + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
> > + return false;
>
> We've dropped this check from vc4 in our downstream kernel as it stops
> you using the prebaked EDIDs (eg drm.edid_firmware=edid/1024x768.bin),
> or any other EDID that is defined as an analog monitor.
> The EDID parsing bombs out at [1], so info->color_formats gets left at 0.
Right, but it only does so if the display isn't defined as a digital display...
> RGB is mandatory for both DVI and HDMI, so rejecting it seems overly fussy.
... which is required for both DVI and HDMI.
And sure enough, if we decode that EDID:
edid-decode (hex):
00 ff ff ff ff ff ff 00 31 d8 00 00 00 00 00 00
05 16 01 03 6d 23 1a 78 ea 5e c0 a4 59 4a 98 25
20 50 54 00 08 00 61 40 01 01 01 01 01 01 01 01
01 01 01 01 01 01 64 19 00 40 41 00 26 30 08 90
36 00 63 0a 11 00 00 18 00 00 00 ff 00 4c 69 6e
75 78 20 23 30 0a 20 20 20 20 00 00 00 fd 00 3b
3d 2f 31 07 00 0a 20 20 20 20 20 20 00 00 00 fc
00 4c 69 6e 75 78 20 58 47 41 0a 20 20 20 00 55
----------------
Block 0, Base EDID:
EDID Structure Version & Revision: 1.3
Vendor & Product Identification:
Manufacturer: LNX
Model: 0
Made in: week 5 of 2012
Basic Display Parameters & Features:
Analog display
Signal Level Standard: 0.700 : 0.000 : 0.700 V p-p
Blank level equals black level
Sync: Separate Composite Serration
Maximum image size: 35 cm x 26 cm
Gamma: 2.20
DPMS levels: Standby Suspend Off
RGB color display
First detailed timing is the preferred timing
Color Characteristics:
Red : 0.6416, 0.3486
Green: 0.2919, 0.5957
Blue : 0.1474, 0.1250
White: 0.3125, 0.3281
Established Timings I & II:
DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz
Standard Timings:
DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz
Detailed Timing Descriptors:
DTD 1: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz (355 mm x 266 mm)
Hfront 8 Hsync 144 Hback 168 Hpol N
Vfront 3 Vsync 6 Vback 29 Vpol N
Display Product Serial Number: 'Linux #0'
Display Range Limits:
Monitor ranges (GTF): 59-61 Hz V, 47-49 kHz H, max dotclock 70 MHz
Display Product Name: 'Linux XGA'
Checksum: 0x55
----------------
Warnings:
Block 0, Base EDID:
Detailed Timing Descriptor #1: DTD is similar but not identical to DMT 0x10.
EDID conformity: PASS
So, if anything, it's the EDID that needs to be updated, not the code there.
Maxime
Hi Maxime
On Thu, 1 Feb 2024 at 12:51, Maxime Ripard <[email protected]> wrote:
>
> On Thu, Dec 14, 2023 at 03:10:43PM +0000, Dave Stevenson wrote:
> > > +static bool
> > > +sink_supports_format_bpc(const struct drm_connector *connector,
> > > + const struct drm_display_info *info,
> > > + const struct drm_display_mode *mode,
> > > + unsigned int format, unsigned int bpc)
> > > +{
> > > + struct drm_device *dev = connector->dev;
> > > + u8 vic = drm_match_cea_mode(mode);
> > > +
> > > + if (vic == 1 && bpc != 8) {
> > > + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
> > > + return false;
> > > + }
> > > +
> > > + if (!info->is_hdmi &&
> > > + (format != HDMI_COLORSPACE_RGB || bpc != 8)) {
> > > + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
> > > + return false;
> > > + }
> > > +
> > > + if (!(connector->hdmi.supported_formats & BIT(format))) {
> > > + drm_dbg(dev, "%s format unsupported by the connector.\n",
> > > + drm_hdmi_connector_get_output_format_name(format));
> > > + return false;
> > > + }
> > > +
> > > + switch (format) {
> > > + case HDMI_COLORSPACE_RGB:
> > > + drm_dbg(dev, "RGB Format, checking the constraints.\n");
> > > +
> > > + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
> > > + return false;
> >
> > We've dropped this check from vc4 in our downstream kernel as it stops
> > you using the prebaked EDIDs (eg drm.edid_firmware=edid/1024x768.bin),
> > or any other EDID that is defined as an analog monitor.
> > The EDID parsing bombs out at [1], so info->color_formats gets left at 0.
>
> Right, but it only does so if the display isn't defined as a digital display...
>
> > RGB is mandatory for both DVI and HDMI, so rejecting it seems overly fussy.
>
> ... which is required for both DVI and HDMI.
>
> And sure enough, if we decode that EDID:
>
> edid-decode (hex):
>
> 00 ff ff ff ff ff ff 00 31 d8 00 00 00 00 00 00
> 05 16 01 03 6d 23 1a 78 ea 5e c0 a4 59 4a 98 25
> 20 50 54 00 08 00 61 40 01 01 01 01 01 01 01 01
> 01 01 01 01 01 01 64 19 00 40 41 00 26 30 08 90
> 36 00 63 0a 11 00 00 18 00 00 00 ff 00 4c 69 6e
> 75 78 20 23 30 0a 20 20 20 20 00 00 00 fd 00 3b
> 3d 2f 31 07 00 0a 20 20 20 20 20 20 00 00 00 fc
> 00 4c 69 6e 75 78 20 58 47 41 0a 20 20 20 00 55
>
> ----------------
>
> Block 0, Base EDID:
> EDID Structure Version & Revision: 1.3
> Vendor & Product Identification:
> Manufacturer: LNX
> Model: 0
> Made in: week 5 of 2012
> Basic Display Parameters & Features:
> Analog display
> Signal Level Standard: 0.700 : 0.000 : 0.700 V p-p
> Blank level equals black level
> Sync: Separate Composite Serration
> Maximum image size: 35 cm x 26 cm
> Gamma: 2.20
> DPMS levels: Standby Suspend Off
> RGB color display
> First detailed timing is the preferred timing
> Color Characteristics:
> Red : 0.6416, 0.3486
> Green: 0.2919, 0.5957
> Blue : 0.1474, 0.1250
> White: 0.3125, 0.3281
> Established Timings I & II:
> DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz
> Standard Timings:
> DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz
> Detailed Timing Descriptors:
> DTD 1: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz (355 mm x 266 mm)
> Hfront 8 Hsync 144 Hback 168 Hpol N
> Vfront 3 Vsync 6 Vback 29 Vpol N
> Display Product Serial Number: 'Linux #0'
> Display Range Limits:
> Monitor ranges (GTF): 59-61 Hz V, 47-49 kHz H, max dotclock 70 MHz
> Display Product Name: 'Linux XGA'
> Checksum: 0x55
>
> ----------------
>
> Warnings:
>
> Block 0, Base EDID:
> Detailed Timing Descriptor #1: DTD is similar but not identical to DMT 0x10.
>
> EDID conformity: PASS
>
> So, if anything, it's the EDID that needs to be updated, not the code there.
So are these EDIDs only valid for VGA outputs and another set needs to
be added for HDMI monitors?
Having drm.edid_firmware=edid/1024x768.bin works on an HDMI connector
prior to this patch, so presumably drm_edid_loader needs to
automatically switch between the existing (analog) and new (digital)
EDIDs based on the connector type? Or are you requiring users to
change the strings they use?
Cheers.
Dave
On Thu, Feb 01, 2024 at 03:33:24PM +0000, Dave Stevenson wrote:
> Hi Maxime
>
> On Thu, 1 Feb 2024 at 12:51, Maxime Ripard <[email protected]> wrote:
> >
> > On Thu, Dec 14, 2023 at 03:10:43PM +0000, Dave Stevenson wrote:
> > > > +static bool
> > > > +sink_supports_format_bpc(const struct drm_connector *connector,
> > > > + const struct drm_display_info *info,
> > > > + const struct drm_display_mode *mode,
> > > > + unsigned int format, unsigned int bpc)
> > > > +{
> > > > + struct drm_device *dev = connector->dev;
> > > > + u8 vic = drm_match_cea_mode(mode);
> > > > +
> > > > + if (vic == 1 && bpc != 8) {
> > > > + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
> > > > + return false;
> > > > + }
> > > > +
> > > > + if (!info->is_hdmi &&
> > > > + (format != HDMI_COLORSPACE_RGB || bpc != 8)) {
> > > > + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
> > > > + return false;
> > > > + }
> > > > +
> > > > + if (!(connector->hdmi.supported_formats & BIT(format))) {
> > > > + drm_dbg(dev, "%s format unsupported by the connector.\n",
> > > > + drm_hdmi_connector_get_output_format_name(format));
> > > > + return false;
> > > > + }
> > > > +
> > > > + switch (format) {
> > > > + case HDMI_COLORSPACE_RGB:
> > > > + drm_dbg(dev, "RGB Format, checking the constraints.\n");
> > > > +
> > > > + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
> > > > + return false;
> > >
> > > We've dropped this check from vc4 in our downstream kernel as it stops
> > > you using the prebaked EDIDs (eg drm.edid_firmware=edid/1024x768.bin),
> > > or any other EDID that is defined as an analog monitor.
> > > The EDID parsing bombs out at [1], so info->color_formats gets left at 0.
> >
> > Right, but it only does so if the display isn't defined as a digital display...
> >
> > > RGB is mandatory for both DVI and HDMI, so rejecting it seems overly fussy.
> >
> > ... which is required for both DVI and HDMI.
> >
> > And sure enough, if we decode that EDID:
> >
> > edid-decode (hex):
> >
> > 00 ff ff ff ff ff ff 00 31 d8 00 00 00 00 00 00
> > 05 16 01 03 6d 23 1a 78 ea 5e c0 a4 59 4a 98 25
> > 20 50 54 00 08 00 61 40 01 01 01 01 01 01 01 01
> > 01 01 01 01 01 01 64 19 00 40 41 00 26 30 08 90
> > 36 00 63 0a 11 00 00 18 00 00 00 ff 00 4c 69 6e
> > 75 78 20 23 30 0a 20 20 20 20 00 00 00 fd 00 3b
> > 3d 2f 31 07 00 0a 20 20 20 20 20 20 00 00 00 fc
> > 00 4c 69 6e 75 78 20 58 47 41 0a 20 20 20 00 55
> >
> > ----------------
> >
> > Block 0, Base EDID:
> > EDID Structure Version & Revision: 1.3
> > Vendor & Product Identification:
> > Manufacturer: LNX
> > Model: 0
> > Made in: week 5 of 2012
> > Basic Display Parameters & Features:
> > Analog display
> > Signal Level Standard: 0.700 : 0.000 : 0.700 V p-p
> > Blank level equals black level
> > Sync: Separate Composite Serration
> > Maximum image size: 35 cm x 26 cm
> > Gamma: 2.20
> > DPMS levels: Standby Suspend Off
> > RGB color display
> > First detailed timing is the preferred timing
> > Color Characteristics:
> > Red : 0.6416, 0.3486
> > Green: 0.2919, 0.5957
> > Blue : 0.1474, 0.1250
> > White: 0.3125, 0.3281
> > Established Timings I & II:
> > DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz
> > Standard Timings:
> > DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz
> > Detailed Timing Descriptors:
> > DTD 1: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz (355 mm x 266 mm)
> > Hfront 8 Hsync 144 Hback 168 Hpol N
> > Vfront 3 Vsync 6 Vback 29 Vpol N
> > Display Product Serial Number: 'Linux #0'
> > Display Range Limits:
> > Monitor ranges (GTF): 59-61 Hz V, 47-49 kHz H, max dotclock 70 MHz
> > Display Product Name: 'Linux XGA'
> > Checksum: 0x55
> >
> > ----------------
> >
> > Warnings:
> >
> > Block 0, Base EDID:
> > Detailed Timing Descriptor #1: DTD is similar but not identical to DMT 0x10.
> >
> > EDID conformity: PASS
> >
> > So, if anything, it's the EDID that needs to be updated, not the code there.
>
> So are these EDIDs only valid for VGA outputs and another set needs to
> be added for HDMI monitors?
>
> Having drm.edid_firmware=edid/1024x768.bin works on an HDMI connector
> prior to this patch, so presumably drm_edid_loader needs to
> automatically switch between the existing (analog) and new (digital)
> EDIDs based on the connector type? Or are you requiring users to
> change the strings they use?
We've discussed that on IRC today. I'm not sure there was a conclusion
other than "well this doesn't seem right". I think we should at least
provide different EDIDs depending on the connector type indeed, but
there was also a few discussions that arose:
- Is it useful to have embedded EDIDs in the kernel at all, and could
we just get rid of it?
- Should we expose those EDIDs to userspace, and what happens to the
compositor when we do?
- The current way to generate those EDIDs isn't... optimal? Should we
get rid of that as well?
Anyway, all of those issues have been here for a while so I don't really
expect this series to fix that.
Maxime
On Mon, 15 Jan 2024, Sebastian Wick <[email protected]> wrote:
> On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
>>
>> Signed-off-by: Maxime Ripard <[email protected]>
[snip]
>> @@ -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().
>> + *
>
> This is a good time to document this in more detail. There might be two
> different things being affected:
>
> 1. The signalling (InfoFrame/SDP/...)
> 2. The color pipeline processing
>
> All values of Broadcast RGB always affect the color pipeline processing
> such that a full-range input to the CRTC is converted to either full- or
> limited-range, depending on what the monitor is supposed to accept.
>
> When automatic is selected, does that mean that there is no signalling,
> or that the signalling matches what the monitor is supposed to accept
> according to the spec? Also, is this really HDMI specific?
Automatic is based on the mode as described in the specs
below. Basically certain modes are expected to be broadcast range, and
others full range.
I don't remember why we don't use the full range if the display
indicates it supports selectable quantization range in Video
Capabilities Data Block. It's quite possible there are displays that
declare support but don't. Cc: Ville.
- HDMI 1.4b section 6.6 Video Quantization Ranges
- HDMI 2.1 section 7.3 Video Quantization Ranges
- DP 2.1 (and earlier) section 5.1.1.1 Video Colorimetry
- CTA-861-H (and earlier) section 5.1 Default Encoding Parameters and
section 6.4.3 Quantization Range
> When full or limited is selected and the monitor doesn't support the
> signalling, what happens?
1) Limited selected, display expects full, colors seem washed out.
2) Full selected, display expects limited, black screen possible.
We receive the occasional bug report for 1, because there are displays
that incorrectly expect full when spec says it should be limited. We
reject the bug reports, because erring the other way can lead to black
screens.
BR,
Jani.
--
Jani Nikula, Intel
On 02/02/2024 12:04, Jani Nikula wrote:
> On Mon, 15 Jan 2024, Sebastian Wick <[email protected]> wrote:
>> On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
>>>
>>> Signed-off-by: Maxime Ripard <[email protected]>
>
> [snip]
>
>>> @@ -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().
>>> + *
>>
>> This is a good time to document this in more detail. There might be two
>> different things being affected:
>>
>> 1. The signalling (InfoFrame/SDP/...)
>> 2. The color pipeline processing
>>
>> All values of Broadcast RGB always affect the color pipeline processing
>> such that a full-range input to the CRTC is converted to either full- or
>> limited-range, depending on what the monitor is supposed to accept.
>>
>> When automatic is selected, does that mean that there is no signalling,
>> or that the signalling matches what the monitor is supposed to accept
>> according to the spec? Also, is this really HDMI specific?
>
> Automatic is based on the mode as described in the specs
> below. Basically certain modes are expected to be broadcast range, and
> others full range.
>
> I don't remember why we don't use the full range if the display
> indicates it supports selectable quantization range in Video
> Capabilities Data Block. It's quite possible there are displays that
> declare support but don't. Cc: Ville.
I have not seen such displays. Enabling RGB Selectable Quantization Range
is something that a vendor has to do explicitly, so it is reasonable to
expect that it works, otherwise there would be no point to that flag!
Transmitting full range if possible gives a better picture quality and
so is recommended.
>
> - HDMI 1.4b section 6.6 Video Quantization Ranges
>
> - HDMI 2.1 section 7.3 Video Quantization Ranges
>
> - DP 2.1 (and earlier) section 5.1.1.1 Video Colorimetry
>
> - CTA-861-H (and earlier) section 5.1 Default Encoding Parameters and
> section 6.4.3 Quantization Range
Note that CTA-861-H deprecated the use of Default Range in the AVI
InfoFrame, instead the source should always signal limited or full range
in the Q field.
Regards,
Hans
>
>> When full or limited is selected and the monitor doesn't support the
>> signalling, what happens?
>
> 1) Limited selected, display expects full, colors seem washed out.
>
> 2) Full selected, display expects limited, black screen possible.
>
> We receive the occasional bug report for 1, because there are displays
> that incorrectly expect full when spec says it should be limited. We
> reject the bug reports, because erring the other way can lead to black
> screens.
>
>
> BR,
> Jani.
>
>
>
Hi,
On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > /**
> > > * 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().
> > > + *
> >
> > This is a good time to document this in more detail. There might be two
> > different things being affected:
> >
> > 1. The signalling (InfoFrame/SDP/...)
> > 2. The color pipeline processing
> >
> > All values of Broadcast RGB always affect the color pipeline processing
> > such that a full-range input to the CRTC is converted to either full- or
> > limited-range, depending on what the monitor is supposed to accept.
> >
> > When automatic is selected, does that mean that there is no signalling,
> > or that the signalling matches what the monitor is supposed to accept
> > according to the spec? Also, is this really HDMI specific?
> >
> > When full or limited is selected and the monitor doesn't support the
> > signalling, what happens?
>
> Forgot to mention: user-space still has no control over RGB vs YCbCr on
> the cable, so is this only affecting RGB? If not, how does it affect
> YCbCr?
So I dug a bit into both the i915 and vc4 drivers, and it looks like if
we're using a YCbCr format, i915 will always use a limited range while
vc4 will follow the value of the property.
I guess the best way to reconcile that would be to state that it also
controls the YCbCr range, and i915 can choose to reject/adjust the
configurations it can't support.
Does that make sense?
Maxime
On Thu, 01 Feb 2024, Maxime Ripard <[email protected]> wrote:
> We've discussed that on IRC today. I'm not sure there was a conclusion
> other than "well this doesn't seem right". I think we should at least
> provide different EDIDs depending on the connector type indeed, but
> there was also a few discussions that arose:
>
> - Is it useful to have embedded EDIDs in the kernel at all, and could
> we just get rid of it?
>
> - Should we expose those EDIDs to userspace, and what happens to the
> compositor when we do?
>
> - The current way to generate those EDIDs isn't... optimal? Should we
> get rid of that as well?
>
> Anyway, all of those issues have been here for a while so I don't really
> expect this series to fix that.
IMO the direction should be towards deprecating and removing the builtin
firmware EDIDs from the kernel instead of adding more or expanding on
them. They were only ever meant to be the immediate aid to get something
on screen so the user could provide a proper EDID via userspace.
BR,
Jani.
--
Jani Nikula, Intel
Hi Sebastian,
On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> > /**
> > * 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().
> > + *
>
> This is a good time to document this in more detail. There might be two
> different things being affected:
>
> 1. The signalling (InfoFrame/SDP/...)
> 2. The color pipeline processing
>
> All values of Broadcast RGB always affect the color pipeline processing
> such that a full-range input to the CRTC is converted to either full- or
> limited-range, depending on what the monitor is supposed to accept.
>
> When automatic is selected, does that mean that there is no signalling,
> or that the signalling matches what the monitor is supposed to accept
> according to the spec? Also, is this really HDMI specific?
>
> When full or limited is selected and the monitor doesn't support the
> signalling, what happens?
Leaving the YCbCr vs RGB discussion aside, would this be better ?
* Broadcast RGB (HDMI specific)
* Indicates the Quantization Range (Full vs Limited) used. The color
* processing pipeline will be adjusted to match the value of the
* property, and the Infoframes will be generated and sent accordingly.
*
* The value of this property can be one of the following:
*
* Automatic:
* The quantization range is selected automatically based on the
* mode according to the HDMI specifications (HDMI 1.4b - Section
* 6.6 - Video Quantization Ranges).
*
* Full:
* Full quantization range is forced.
*
* Limited 16:235:
* Limited quantization range is forced. Unlike the name suggests,
* this works for any number of bits-per-component.
*
* Property values other than Automatic can result in colors being off (if
* limited is selected but the display expects full), or a black screen
* (if full is selected but the display expects limited).
*
* Drivers can set up this property by calling
* drm_connector_attach_broadcast_rgb_property().
Thanks!
Maxime
On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > Hi,
> >
> > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > /**
> > > > > * 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().
> > > > > + *
> > > >
> > > > This is a good time to document this in more detail. There might be two
> > > > different things being affected:
> > > >
> > > > 1. The signalling (InfoFrame/SDP/...)
> > > > 2. The color pipeline processing
> > > >
> > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > such that a full-range input to the CRTC is converted to either full- or
> > > > limited-range, depending on what the monitor is supposed to accept.
> > > >
> > > > When automatic is selected, does that mean that there is no signalling,
> > > > or that the signalling matches what the monitor is supposed to accept
> > > > according to the spec? Also, is this really HDMI specific?
> > > >
> > > > When full or limited is selected and the monitor doesn't support the
> > > > signalling, what happens?
> > >
> > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > the cable, so is this only affecting RGB? If not, how does it affect
> > > YCbCr?
> >
> > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > we're using a YCbCr format, i915 will always use a limited range while
> > vc4 will follow the value of the property.
>
> The property is literally called "Broadcast *RGB*".
> That should explain why it's only affecting RGB.
Right. And the limited range option is called "Limited 16:235" despite
being usable on bpc > 8 bits. Naming errors occurs, and history happens
to make names inconsistent too, that's fine and not an argument in
itself.
> Full range YCbCr is a much rarer beast so we've never bothered
> to enable it.
vc4 supports it.
> Eg. with DP it only became possible with the introduction of the VSC
> SDP (and I don't recall if there's additional capability checks that
> are also required). With DP MSA signalling full range YCbCr is not
> possible at all.
This is for HDMI only.
> I don't recall right now what the HDMI requirements are.
HDMI has supported it for a while, and it's defined (for example) in the
HDMI 1.4 spec in Section 6.6 - Video Quantization Ranges. It supports
limited and full range on both RGB and YCbCr, as long as the EDIDs state
so and the Infoframes signal it.
Maxime
On Fri, Feb 02, 2024 at 12:20:21PM +0100, Hans Verkuil wrote:
> On 02/02/2024 12:04, Jani Nikula wrote:
> > On Mon, 15 Jan 2024, Sebastian Wick <[email protected]> wrote:
> >> On Thu, Dec 07, 2023 at 04:49:31PM +0100, Maxime Ripard 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.
> >>>
> >>> Signed-off-by: Maxime Ripard <[email protected]>
> >
> > [snip]
> >
> >>> @@ -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().
> >>> + *
> >>
> >> This is a good time to document this in more detail. There might be two
> >> different things being affected:
> >>
> >> 1. The signalling (InfoFrame/SDP/...)
> >> 2. The color pipeline processing
> >>
> >> All values of Broadcast RGB always affect the color pipeline processing
> >> such that a full-range input to the CRTC is converted to either full- or
> >> limited-range, depending on what the monitor is supposed to accept.
> >>
> >> When automatic is selected, does that mean that there is no signalling,
> >> or that the signalling matches what the monitor is supposed to accept
> >> according to the spec? Also, is this really HDMI specific?
> >
> > Automatic is based on the mode as described in the specs
> > below. Basically certain modes are expected to be broadcast range, and
> > others full range.
> >
> > I don't remember why we don't use the full range if the display
> > indicates it supports selectable quantization range in Video
> > Capabilities Data Block. It's quite possible there are displays that
> > declare support but don't. Cc: Ville.
>
> I have not seen such displays. Enabling RGB Selectable Quantization Range
> is something that a vendor has to do explicitly, so it is reasonable to
> expect that it works, otherwise there would be no point to that flag!
>
> Transmitting full range if possible gives a better picture quality and
> so is recommended.
>
> >
> > - HDMI 1.4b section 6.6 Video Quantization Ranges
> >
> > - HDMI 2.1 section 7.3 Video Quantization Ranges
> >
> > - DP 2.1 (and earlier) section 5.1.1.1 Video Colorimetry
> >
> > - CTA-861-H (and earlier) section 5.1 Default Encoding Parameters and
> > section 6.4.3 Quantization Range
>
> Note that CTA-861-H deprecated the use of Default Range in the AVI
> InfoFrame, instead the source should always signal limited or full range
> in the Q field.
Only because the QS=1 is now mandatory IIRC.
We do always set Q bit explicitly when QS==1.
But yeah, I guess we could always go for full range
by default when QS==1. Or maybe we even did that at
some point in the past and it broke some things?
Can't recall anymore.
Anyways, QS=1 being mandatory is a clear improvement
in CTA-861, but some other CTA-861 rule updates are
more or less pointless as you can't in general detect
which version of the spec the sink claims to implement.
Eg. we already had cases where following the new CTA-861-F
rules for the YQ bit when transmitting RGB broke some
displays. And we are now forced to use the presence of
HDMI 2.0+ capabilities as a heuristic to detect CTA-851-F+.
(see drm_hdmi_avi_infoframe_quant_range()). It's pretty
nasty.
And I think there is some other infoframe related issue
(something to do with VICs IIRC) where we'd need to derive
the CTA-861 version eg. from the set of advertised VICs
in the EDID. I might have even written some code for that
at some point but never finished it. So it's still
broken in the current code. Can't recall the specific
details right now unfortunately.
--
Ville Syrj?l?
Intel
On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> Hi,
>
> On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > /**
> > > > * 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().
> > > > + *
> > >
> > > This is a good time to document this in more detail. There might be two
> > > different things being affected:
> > >
> > > 1. The signalling (InfoFrame/SDP/...)
> > > 2. The color pipeline processing
> > >
> > > All values of Broadcast RGB always affect the color pipeline processing
> > > such that a full-range input to the CRTC is converted to either full- or
> > > limited-range, depending on what the monitor is supposed to accept.
> > >
> > > When automatic is selected, does that mean that there is no signalling,
> > > or that the signalling matches what the monitor is supposed to accept
> > > according to the spec? Also, is this really HDMI specific?
> > >
> > > When full or limited is selected and the monitor doesn't support the
> > > signalling, what happens?
> >
> > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > the cable, so is this only affecting RGB? If not, how does it affect
> > YCbCr?
>
> So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> we're using a YCbCr format, i915 will always use a limited range while
> vc4 will follow the value of the property.
The property is literally called "Broadcast *RGB*".
That should explain why it's only affecting RGB.
Full range YCbCr is a much rarer beast so we've never bothered
to enable it. Eg. with DP it only became possible with the
introduction of the VSC SDP (and I don't recall if there's
additional capability checks that are also required). With
DP MSA signalling full range YCbCr is not possible at all.
I don't recall right now what the HDMI requirements are.
--
Ville Syrj?l?
Intel
On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > Hi,
> > >
> > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > /**
> > > > > > * 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().
> > > > > > + *
> > > > >
> > > > > This is a good time to document this in more detail. There might be two
> > > > > different things being affected:
> > > > >
> > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > 2. The color pipeline processing
> > > > >
> > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > >
> > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > according to the spec? Also, is this really HDMI specific?
> > > > >
> > > > > When full or limited is selected and the monitor doesn't support the
> > > > > signalling, what happens?
> > > >
> > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > YCbCr?
> > >
> > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > we're using a YCbCr format, i915 will always use a limited range while
> > > vc4 will follow the value of the property.
> >
> > The property is literally called "Broadcast *RGB*".
> > That should explain why it's only affecting RGB.
>
> Right. And the limited range option is called "Limited 16:235" despite
> being usable on bpc > 8 bits. Naming errors occurs, and history happens
> to make names inconsistent too, that's fine and not an argument in
> itself.
>
> > Full range YCbCr is a much rarer beast so we've never bothered
> > to enable it.
>
> vc4 supports it.
Someone implemented it incorrectly then.
>
> > Eg. with DP it only became possible with the introduction of the VSC
> > SDP (and I don't recall if there's additional capability checks that
> > are also required). With DP MSA signalling full range YCbCr is not
> > possible at all.
>
> This is for HDMI only.
>
> > I don't recall right now what the HDMI requirements are.
>
> HDMI has supported it for a while, and it's defined (for example) in the
> HDMI 1.4 spec in Section 6.6 - Video Quantization Ranges. It supports
> limited and full range on both RGB and YCbCr, as long as the EDIDs state
> so and the Infoframes signal it.
I think a good reason for not using a simple boolean like this
YCbCr is that it doesn't cover the color encoding part at all,
which is probably more important than the quantization range.
So we need a new property anyway.
--
Ville Syrj?l?
Intel
On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > Hi,
> > > >
> > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > /**
> > > > > > > * 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().
> > > > > > > + *
> > > > > >
> > > > > > This is a good time to document this in more detail. There might be two
> > > > > > different things being affected:
> > > > > >
> > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > 2. The color pipeline processing
> > > > > >
> > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > >
> > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > >
> > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > signalling, what happens?
> > > > >
> > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > YCbCr?
> > > >
> > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > vc4 will follow the value of the property.
> > >
> > > The property is literally called "Broadcast *RGB*".
> > > That should explain why it's only affecting RGB.
> >
> > Right. And the limited range option is called "Limited 16:235" despite
> > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > to make names inconsistent too, that's fine and not an argument in
> > itself.
> >
> > > Full range YCbCr is a much rarer beast so we've never bothered
> > > to enable it.
> >
> > vc4 supports it.
>
> Someone implemented it incorrectly then.
Incorrectly according to what documentation / specification? I'm sorry,
but I find it super ironic that i915 gets to do its own thing, not
document any of it, and when people try to clean things up they get told
that we got it all wrong.
> > > Eg. with DP it only became possible with the introduction of the VSC
> > > SDP (and I don't recall if there's additional capability checks that
> > > are also required). With DP MSA signalling full range YCbCr is not
> > > possible at all.
> >
> > This is for HDMI only.
> >
> > > I don't recall right now what the HDMI requirements are.
> >
> > HDMI has supported it for a while, and it's defined (for example) in the
> > HDMI 1.4 spec in Section 6.6 - Video Quantization Ranges. It supports
> > limited and full range on both RGB and YCbCr, as long as the EDIDs state
> > so and the Infoframes signal it.
>
> I think a good reason for not using a simple boolean like this
> YCbCr is that it doesn't cover the color encoding part at all,
> which is probably more important than the quantization range.
> So we need a new property anyway.
This isn't what is being discussed here, and as I've shown you, is
completely orthogonal as far as HDMI is concerned.
Maxime
On Fri, Feb 02, 2024 at 04:49:04PM +0100, Maxime Ripard wrote:
> Hi Sebastian,
>
> On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> > > /**
> > > * 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().
> > > + *
> >
> > This is a good time to document this in more detail. There might be two
> > different things being affected:
> >
> > 1. The signalling (InfoFrame/SDP/...)
> > 2. The color pipeline processing
> >
> > All values of Broadcast RGB always affect the color pipeline processing
> > such that a full-range input to the CRTC is converted to either full- or
> > limited-range, depending on what the monitor is supposed to accept.
> >
> > When automatic is selected, does that mean that there is no signalling,
> > or that the signalling matches what the monitor is supposed to accept
> > according to the spec? Also, is this really HDMI specific?
> >
> > When full or limited is selected and the monitor doesn't support the
> > signalling, what happens?
>
> Leaving the YCbCr vs RGB discussion aside, would this be better ?
Yes, it is. Thanks.
We do have to resolve the YCbCr vs RGB issue though.
> * Broadcast RGB (HDMI specific)
> * Indicates the Quantization Range (Full vs Limited) used. The color
> * processing pipeline will be adjusted to match the value of the
Ah, another thing no note here is that the CRTC as configured by user
space must always produce full range pixels.
> * property, and the Infoframes will be generated and sent accordingly.
> *
> * The value of this property can be one of the following:
> *
> * Automatic:
> * The quantization range is selected automatically based on the
> * mode according to the HDMI specifications (HDMI 1.4b - Section
> * 6.6 - Video Quantization Ranges).
> *
> * Full:
> * Full quantization range is forced.
> *
> * Limited 16:235:
> * Limited quantization range is forced. Unlike the name suggests,
> * this works for any number of bits-per-component.
> *
> * Property values other than Automatic can result in colors being off (if
> * limited is selected but the display expects full), or a black screen
> * (if full is selected but the display expects limited).
> *
> * Drivers can set up this property by calling
> * drm_connector_attach_broadcast_rgb_property().
>
> Thanks!
> Maxime
On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > Hi,
> > > > >
> > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > /**
> > > > > > > > * 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().
> > > > > > > > + *
> > > > > > >
> > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > different things being affected:
> > > > > > >
> > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > 2. The color pipeline processing
> > > > > > >
> > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > >
> > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > >
> > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > signalling, what happens?
> > > > > >
> > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > YCbCr?
> > > > >
> > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > vc4 will follow the value of the property.
> > > >
> > > > The property is literally called "Broadcast *RGB*".
> > > > That should explain why it's only affecting RGB.
> > >
> > > Right. And the limited range option is called "Limited 16:235" despite
> > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > to make names inconsistent too, that's fine and not an argument in
> > > itself.
> > >
> > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > to enable it.
> > >
> > > vc4 supports it.
> >
> > Someone implemented it incorrectly then.
>
> Incorrectly according to what documentation / specification? I'm sorry,
> but I find it super ironic that i915 gets to do its own thing, not
> document any of it, and when people try to clean things up they get told
> that we got it all wrong.
FWIW, this was an i915 property and if another driver uses the same
property name it must have the same behavior. Yes, it isn't standardized
and yes, it's not documented (hence this effort here) but it's still on
vc4 to make the property compatible.
Trying to make the property handle YCbCr is very much in the "let's try
to fix the property" territory that I want to avoid, so I'm in favor of
adjusting vc4.
> > > > Eg. with DP it only became possible with the introduction of the VSC
> > > > SDP (and I don't recall if there's additional capability checks that
> > > > are also required). With DP MSA signalling full range YCbCr is not
> > > > possible at all.
> > >
> > > This is for HDMI only.
> > >
> > > > I don't recall right now what the HDMI requirements are.
> > >
> > > HDMI has supported it for a while, and it's defined (for example) in the
> > > HDMI 1.4 spec in Section 6.6 - Video Quantization Ranges. It supports
> > > limited and full range on both RGB and YCbCr, as long as the EDIDs state
> > > so and the Infoframes signal it.
> >
> > I think a good reason for not using a simple boolean like this
> > YCbCr is that it doesn't cover the color encoding part at all,
> > which is probably more important than the quantization range.
> > So we need a new property anyway.
>
> This isn't what is being discussed here, and as I've shown you, is
> completely orthogonal as far as HDMI is concerned.
>
> Maxime
On Fri, Feb 09, 2024 at 09:30:46PM +0100, Sebastian Wick wrote:
> On Fri, Feb 02, 2024 at 04:49:04PM +0100, Maxime Ripard wrote:
> > Hi Sebastian,
> >
> > On Mon, Jan 15, 2024 at 03:33:08PM +0100, Sebastian Wick wrote:
> > > > /**
> > > > * 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().
> > > > + *
> > >
> > > This is a good time to document this in more detail. There might be two
> > > different things being affected:
> > >
> > > 1. The signalling (InfoFrame/SDP/...)
> > > 2. The color pipeline processing
> > >
> > > All values of Broadcast RGB always affect the color pipeline processing
> > > such that a full-range input to the CRTC is converted to either full- or
> > > limited-range, depending on what the monitor is supposed to accept.
> > >
> > > When automatic is selected, does that mean that there is no signalling,
> > > or that the signalling matches what the monitor is supposed to accept
> > > according to the spec? Also, is this really HDMI specific?
> > >
> > > When full or limited is selected and the monitor doesn't support the
> > > signalling, what happens?
> >
> > Leaving the YCbCr vs RGB discussion aside, would this be better ?
>
> Yes, it is. Thanks.
Great, it'll be in the next version.
> We do have to resolve the YCbCr vs RGB issue though.
>
> > * Broadcast RGB (HDMI specific)
> > * Indicates the Quantization Range (Full vs Limited) used. The color
> > * processing pipeline will be adjusted to match the value of the
>
> Ah, another thing no note here is that the CRTC as configured by user
> space must always produce full range pixels.
Added too
Thanks
On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > Hi,
> > > > > >
> > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > /**
> > > > > > > > > * 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().
> > > > > > > > > + *
> > > > > > > >
> > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > different things being affected:
> > > > > > > >
> > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > 2. The color pipeline processing
> > > > > > > >
> > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > >
> > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > >
> > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > signalling, what happens?
> > > > > > >
> > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > YCbCr?
> > > > > >
> > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > vc4 will follow the value of the property.
> > > > >
> > > > > The property is literally called "Broadcast *RGB*".
> > > > > That should explain why it's only affecting RGB.
> > > >
> > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > to make names inconsistent too, that's fine and not an argument in
> > > > itself.
> > > >
> > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > to enable it.
> > > >
> > > > vc4 supports it.
> > >
> > > Someone implemented it incorrectly then.
> >
> > Incorrectly according to what documentation / specification? I'm sorry,
> > but I find it super ironic that i915 gets to do its own thing, not
> > document any of it, and when people try to clean things up they get told
> > that we got it all wrong.
>
> FWIW, this was an i915 property and if another driver uses the same
> property name it must have the same behavior. Yes, it isn't standardized
> and yes, it's not documented (hence this effort here) but it's still on
> vc4 to make the property compatible.
How is it not compatible? It's a superset of what i915 provides, but
it's strictly compatible with it.
I would argue that i915 is the broken one since userspace could force a
full range output, but since the driver takes the YUV vs RGB decision
itself and only supports limited range for YUV, the driver would
effectively ignore that user-space property, without the user-space
being able to tell it was ignored in the first place.
> Trying to make the property handle YCbCr is very much in the "let's try
> to fix the property" territory that I want to avoid, so I'm in favor of
> adjusting vc4.
Breaking the ABI in the process. For something that is explicitly
supported by the spec, the driver, and the hardware. On a property that
never said it wasn't meant to be used that way, and with semantics based
on a driver that never provided a way to check those restrictions in the
first place.
And it's not like i915 is going to use that code anyway.
Maxime
On 12/02/2024 16:49, Ville Syrjälä wrote:
> On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
>> On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
>>> On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
>>>> On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrjälä wrote:
>>>>> On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
>>>>>> On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrjälä wrote:
>>>>>>> On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
>>>>>>>>>>> /**
>>>>>>>>>>> * 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().
>>>>>>>>>>> + *
>>>>>>>>>>
>>>>>>>>>> This is a good time to document this in more detail. There might be two
>>>>>>>>>> different things being affected:
>>>>>>>>>>
>>>>>>>>>> 1. The signalling (InfoFrame/SDP/...)
>>>>>>>>>> 2. The color pipeline processing
>>>>>>>>>>
>>>>>>>>>> All values of Broadcast RGB always affect the color pipeline processing
>>>>>>>>>> such that a full-range input to the CRTC is converted to either full- or
>>>>>>>>>> limited-range, depending on what the monitor is supposed to accept.
>>>>>>>>>>
>>>>>>>>>> When automatic is selected, does that mean that there is no signalling,
>>>>>>>>>> or that the signalling matches what the monitor is supposed to accept
>>>>>>>>>> according to the spec? Also, is this really HDMI specific?
>>>>>>>>>>
>>>>>>>>>> When full or limited is selected and the monitor doesn't support the
>>>>>>>>>> signalling, what happens?
>>>>>>>>>
>>>>>>>>> Forgot to mention: user-space still has no control over RGB vs YCbCr on
>>>>>>>>> the cable, so is this only affecting RGB? If not, how does it affect
>>>>>>>>> YCbCr?
>>>>>>>>
>>>>>>>> So I dug a bit into both the i915 and vc4 drivers, and it looks like if
>>>>>>>> we're using a YCbCr format, i915 will always use a limited range while
>>>>>>>> vc4 will follow the value of the property.
>>>>>>>
>>>>>>> The property is literally called "Broadcast *RGB*".
>>>>>>> That should explain why it's only affecting RGB.
>>>>>>
>>>>>> Right. And the limited range option is called "Limited 16:235" despite
>>>>>> being usable on bpc > 8 bits. Naming errors occurs, and history happens
>>>>>> to make names inconsistent too, that's fine and not an argument in
>>>>>> itself.
>>>>>>
>>>>>>> Full range YCbCr is a much rarer beast so we've never bothered
>>>>>>> to enable it.
>>>>>>
>>>>>> vc4 supports it.
>>>>>
>>>>> Someone implemented it incorrectly then.
>>>>
>>>> Incorrectly according to what documentation / specification? I'm sorry,
>>>> but I find it super ironic that i915 gets to do its own thing, not
>>>> document any of it, and when people try to clean things up they get told
>>>> that we got it all wrong.
>>>
>>> FWIW, this was an i915 property and if another driver uses the same
>>> property name it must have the same behavior. Yes, it isn't standardized
>>> and yes, it's not documented (hence this effort here) but it's still on
>>> vc4 to make the property compatible.
>>
>> How is it not compatible? It's a superset of what i915 provides, but
>> it's strictly compatible with it.
>
> No it is not. Eg. what happens if you set the thing to full range for
> RGB (which you must on many broken monitors), and then the kernel
> automagically switches to YCbCr (for whatever reason) but the monitor
> doesn't support full range YCbCr? Answer: you get crap output.
The Broadcast RGB setting is really specific to RGB output. That's where
you need it, since due to messed up standards in the past it is common to
have to override this.
For YCbCr it is not needed since it is always limited range in practice.
If there is ever a need to support full range YCbCr, then a new "Broadcast YCbCr"
setting should be created.
The only place were you see full range YCbCr being used is in combination with
JPEG codecs, since JPEG uses full range YCbCr. But it does not normally occur
on video output interfaces.
Regards,
Hans
>
>>
>> I would argue that i915 is the broken one since userspace could force a
>> full range output, but since the driver takes the YUV vs RGB decision
>> itself and only supports limited range for YUV, the driver would
>> effectively ignore that user-space property, without the user-space
>> being able to tell it was ignored in the first place.
>>
>>> Trying to make the property handle YCbCr is very much in the "let's try
>>> to fix the property" territory that I want to avoid, so I'm in favor of
>>> adjusting vc4.
>>
>> Breaking the ABI in the process. For something that is explicitly
>> supported by the spec, the driver, and the hardware. On a property that
>> never said it wasn't meant to be used that way, and with semantics based
>> on a driver that never provided a way to check those restrictions in the
>> first place.
>>
>> And it's not like i915 is going to use that code anyway.
>>
>> Maxime
>
>
>
On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > Hi,
> > > > > > > >
> > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > /**
> > > > > > > > > > > * 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().
> > > > > > > > > > > + *
> > > > > > > > > >
> > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > different things being affected:
> > > > > > > > > >
> > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > >
> > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > >
> > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > >
> > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > signalling, what happens?
> > > > > > > > >
> > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > YCbCr?
> > > > > > > >
> > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > vc4 will follow the value of the property.
> > > > > > >
> > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > That should explain why it's only affecting RGB.
> > > > > >
> > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > itself.
> > > > > >
> > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > to enable it.
> > > > > >
> > > > > > vc4 supports it.
> > > > >
> > > > > Someone implemented it incorrectly then.
> > > >
> > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > document any of it, and when people try to clean things up they get told
> > > > that we got it all wrong.
> > >
> > > FWIW, this was an i915 property and if another driver uses the same
> > > property name it must have the same behavior. Yes, it isn't standardized
> > > and yes, it's not documented (hence this effort here) but it's still on
> > > vc4 to make the property compatible.
> >
> > How is it not compatible? It's a superset of what i915 provides, but
> > it's strictly compatible with it.
>
> No it is not.
The property is compatible with i915 interpretation of it, whether you
like it or not. And that's what Sebastian was referring to.
> Eg. what happens if you set the thing to full range for RGB (which you
> must on many broken monitors), and then the kernel automagically
> switches to YCbCr (for whatever reason) but the monitor doesn't
> support full range YCbCr? Answer: you get crap output.
And that part is just moving goalposts.
Maxime
On Mon, Feb 12, 2024 at 05:39:03PM +0100, Hans Verkuil wrote:
> On 12/02/2024 16:49, Ville Syrj?l? wrote:
> > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> >> On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> >>> On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> >>>> On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> >>>>> On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> >>>>>> On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> >>>>>>> On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> >>>>>>>> Hi,
> >>>>>>>>
> >>>>>>>> On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> >>>>>>>>>>> /**
> >>>>>>>>>>> * 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().
> >>>>>>>>>>> + *
> >>>>>>>>>>
> >>>>>>>>>> This is a good time to document this in more detail. There might be two
> >>>>>>>>>> different things being affected:
> >>>>>>>>>>
> >>>>>>>>>> 1. The signalling (InfoFrame/SDP/...)
> >>>>>>>>>> 2. The color pipeline processing
> >>>>>>>>>>
> >>>>>>>>>> All values of Broadcast RGB always affect the color pipeline processing
> >>>>>>>>>> such that a full-range input to the CRTC is converted to either full- or
> >>>>>>>>>> limited-range, depending on what the monitor is supposed to accept.
> >>>>>>>>>>
> >>>>>>>>>> When automatic is selected, does that mean that there is no signalling,
> >>>>>>>>>> or that the signalling matches what the monitor is supposed to accept
> >>>>>>>>>> according to the spec? Also, is this really HDMI specific?
> >>>>>>>>>>
> >>>>>>>>>> When full or limited is selected and the monitor doesn't support the
> >>>>>>>>>> signalling, what happens?
> >>>>>>>>>
> >>>>>>>>> Forgot to mention: user-space still has no control over RGB vs YCbCr on
> >>>>>>>>> the cable, so is this only affecting RGB? If not, how does it affect
> >>>>>>>>> YCbCr?
> >>>>>>>>
> >>>>>>>> So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> >>>>>>>> we're using a YCbCr format, i915 will always use a limited range while
> >>>>>>>> vc4 will follow the value of the property.
> >>>>>>>
> >>>>>>> The property is literally called "Broadcast *RGB*".
> >>>>>>> That should explain why it's only affecting RGB.
> >>>>>>
> >>>>>> Right. And the limited range option is called "Limited 16:235" despite
> >>>>>> being usable on bpc > 8 bits. Naming errors occurs, and history happens
> >>>>>> to make names inconsistent too, that's fine and not an argument in
> >>>>>> itself.
> >>>>>>
> >>>>>>> Full range YCbCr is a much rarer beast so we've never bothered
> >>>>>>> to enable it.
> >>>>>>
> >>>>>> vc4 supports it.
> >>>>>
> >>>>> Someone implemented it incorrectly then.
> >>>>
> >>>> Incorrectly according to what documentation / specification? I'm sorry,
> >>>> but I find it super ironic that i915 gets to do its own thing, not
> >>>> document any of it, and when people try to clean things up they get told
> >>>> that we got it all wrong.
> >>>
> >>> FWIW, this was an i915 property and if another driver uses the same
> >>> property name it must have the same behavior. Yes, it isn't standardized
> >>> and yes, it's not documented (hence this effort here) but it's still on
> >>> vc4 to make the property compatible.
> >>
> >> How is it not compatible? It's a superset of what i915 provides, but
> >> it's strictly compatible with it.
> >
> > No it is not. Eg. what happens if you set the thing to full range for
> > RGB (which you must on many broken monitors), and then the kernel
> > automagically switches to YCbCr (for whatever reason) but the monitor
> > doesn't support full range YCbCr? Answer: you get crap output.
>
> The Broadcast RGB setting is really specific to RGB output. That's where
> you need it, since due to messed up standards in the past it is common to
> have to override this.
>
> For YCbCr it is not needed since it is always limited range in practice.
> If there is ever a need to support full range YCbCr, then a new "Broadcast YCbCr"
> setting should be created.
We can't implement that, really. The userspace has no way to tell if RGB
or YUV is going to be used, so it wouldn't have an idea of what property
it needs to set.
Maxime
On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > Hi,
> > > > > > > > >
> > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > /**
> > > > > > > > > > > > * 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().
> > > > > > > > > > > > + *
> > > > > > > > > > >
> > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > different things being affected:
> > > > > > > > > > >
> > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > >
> > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > >
> > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > >
> > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > signalling, what happens?
> > > > > > > > > >
> > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > YCbCr?
> > > > > > > > >
> > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > vc4 will follow the value of the property.
> > > > > > > >
> > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > That should explain why it's only affecting RGB.
> > > > > > >
> > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > itself.
> > > > > > >
> > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > to enable it.
> > > > > > >
> > > > > > > vc4 supports it.
> > > > > >
> > > > > > Someone implemented it incorrectly then.
> > > > >
> > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > document any of it, and when people try to clean things up they get told
> > > > > that we got it all wrong.
> > > >
> > > > FWIW, this was an i915 property and if another driver uses the same
> > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > vc4 to make the property compatible.
> > >
> > > How is it not compatible? It's a superset of what i915 provides, but
> > > it's strictly compatible with it.
> >
> > No it is not.
>
> The property is compatible with i915 interpretation of it, whether you
> like it or not. And that's what Sebastian was referring to.
>
> > Eg. what happens if you set the thing to full range for RGB (which you
> > must on many broken monitors), and then the kernel automagically
> > switches to YCbCr (for whatever reason) but the monitor doesn't
> > support full range YCbCr? Answer: you get crap output.
>
> And that part is just moving goalposts.
But it's really not. The Broadcast RGB property kind of works from a
user space perspective because it's a workaround for broken sinks. If a
sink expects limited range we can force full range. If this however
affects YCbCr modes as well, then this isn't a workaround for broken RGB
range anymore because it now breaks YCbCr.
Sorry, but vc4 just has to change.
And again: let's please stop trying to improve the property.
>
> Maxime
On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > Hi,
> > > > > > >
> > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > /**
> > > > > > > > > > * 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().
> > > > > > > > > > + *
> > > > > > > > >
> > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > different things being affected:
> > > > > > > > >
> > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > 2. The color pipeline processing
> > > > > > > > >
> > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > >
> > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > >
> > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > signalling, what happens?
> > > > > > > >
> > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > YCbCr?
> > > > > > >
> > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > vc4 will follow the value of the property.
> > > > > >
> > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > That should explain why it's only affecting RGB.
> > > > >
> > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > itself.
> > > > >
> > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > to enable it.
> > > > >
> > > > > vc4 supports it.
> > > >
> > > > Someone implemented it incorrectly then.
> > >
> > > Incorrectly according to what documentation / specification? I'm sorry,
> > > but I find it super ironic that i915 gets to do its own thing, not
> > > document any of it, and when people try to clean things up they get told
> > > that we got it all wrong.
> >
> > FWIW, this was an i915 property and if another driver uses the same
> > property name it must have the same behavior. Yes, it isn't standardized
> > and yes, it's not documented (hence this effort here) but it's still on
> > vc4 to make the property compatible.
>
> How is it not compatible? It's a superset of what i915 provides, but
> it's strictly compatible with it.
No it is not. Eg. what happens if you set the thing to full range for
RGB (which you must on many broken monitors), and then the kernel
automagically switches to YCbCr (for whatever reason) but the monitor
doesn't support full range YCbCr? Answer: you get crap output.
>
> I would argue that i915 is the broken one since userspace could force a
> full range output, but since the driver takes the YUV vs RGB decision
> itself and only supports limited range for YUV, the driver would
> effectively ignore that user-space property, without the user-space
> being able to tell it was ignored in the first place.
>
> > Trying to make the property handle YCbCr is very much in the "let's try
> > to fix the property" territory that I want to avoid, so I'm in favor of
> > adjusting vc4.
>
> Breaking the ABI in the process. For something that is explicitly
> supported by the spec, the driver, and the hardware. On a property that
> never said it wasn't meant to be used that way, and with semantics based
> on a driver that never provided a way to check those restrictions in the
> first place.
>
> And it's not like i915 is going to use that code anyway.
>
> Maxime
--
Ville Syrj?l?
Intel
On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > Hi,
> > > > > > > > >
> > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > /**
> > > > > > > > > > > > * 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().
> > > > > > > > > > > > + *
> > > > > > > > > > >
> > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > different things being affected:
> > > > > > > > > > >
> > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > >
> > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > >
> > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > >
> > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > signalling, what happens?
> > > > > > > > > >
> > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > YCbCr?
> > > > > > > > >
> > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > vc4 will follow the value of the property.
> > > > > > > >
> > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > That should explain why it's only affecting RGB.
> > > > > > >
> > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > itself.
> > > > > > >
> > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > to enable it.
> > > > > > >
> > > > > > > vc4 supports it.
> > > > > >
> > > > > > Someone implemented it incorrectly then.
> > > > >
> > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > document any of it, and when people try to clean things up they get told
> > > > > that we got it all wrong.
> > > >
> > > > FWIW, this was an i915 property and if another driver uses the same
> > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > vc4 to make the property compatible.
> > >
> > > How is it not compatible? It's a superset of what i915 provides, but
> > > it's strictly compatible with it.
> >
> > No it is not.
>
> The property is compatible with i915 interpretation of it, whether you
> like it or not. And that's what Sebastian was referring to.
>
> > Eg. what happens if you set the thing to full range for RGB (which you
> > must on many broken monitors), and then the kernel automagically
> > switches to YCbCr (for whatever reason) but the monitor doesn't
> > support full range YCbCr? Answer: you get crap output.
>
> And that part is just moving goalposts.
No. Allowing users to get correct colors with broken displays
is the sole reason why this property even exists.
--
Ville Syrj?l?
Intel
On Mon, Feb 12, 2024 at 06:06:18PM +0100, Sebastian Wick wrote:
> On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > Hi,
> > > > > > > > > >
> > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > /**
> > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > + *
> > > > > > > > > > > >
> > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > different things being affected:
> > > > > > > > > > > >
> > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > >
> > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > >
> > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > >
> > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > >
> > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > YCbCr?
> > > > > > > > > >
> > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > >
> > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > >
> > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > itself.
> > > > > > > >
> > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > to enable it.
> > > > > > > >
> > > > > > > > vc4 supports it.
> > > > > > >
> > > > > > > Someone implemented it incorrectly then.
> > > > > >
> > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > document any of it, and when people try to clean things up they get told
> > > > > > that we got it all wrong.
> > > > >
> > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > vc4 to make the property compatible.
> > > >
> > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > it's strictly compatible with it.
> > >
> > > No it is not.
> >
> > The property is compatible with i915 interpretation of it, whether you
> > like it or not. And that's what Sebastian was referring to.
> >
> > > Eg. what happens if you set the thing to full range for RGB (which you
> > > must on many broken monitors), and then the kernel automagically
> > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > support full range YCbCr? Answer: you get crap output.
> >
> > And that part is just moving goalposts.
>
> But it's really not.
It really is. This whole discussion started by "well it would be nice if
we made that property handled by the core", and we're now at the "we
need to deal with broken YCbCr displays and i915 opinion about them"
stage. After creating documentation, unit tests, etc. It's the textbook
definition of moving goalposts. And while i915 won't be affected by all
that work.
That series has been stuck for multiple iterations on pointless and
mundane debates while the biggest part and whole point of it is not
getting any review. So yeah, sorry, it's frustrating.
> The Broadcast RGB property kind of works from a user space perspective
> because it's a workaround for broken sinks. If a sink expects limited
> range we can force full range. If this however affects YCbCr modes as
> well, then this isn't a workaround for broken RGB range anymore
> because it now breaks YCbCr.
Or, you know, it's a workaround for broken YCbCr display.
> Sorry, but vc4 just has to change.
>
> And again: let's please stop trying to improve the property.
I'm not. I'm super close to just dropping that patch entirely and keep
the current situation that seems to work fine for everyone.
Maxime
On Tue, Feb 13, 2024 at 10:38:56AM +0200, Ville Syrjälä wrote:
> On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrjälä wrote:
> > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrjälä wrote:
> > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrjälä wrote:
> > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > Hi,
> > > > > > > > > >
> > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > /**
> > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > + *
> > > > > > > > > > > >
> > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > different things being affected:
> > > > > > > > > > > >
> > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > >
> > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > >
> > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > >
> > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > >
> > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > YCbCr?
> > > > > > > > > >
> > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > >
> > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > >
> > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > itself.
> > > > > > > >
> > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > to enable it.
> > > > > > > >
> > > > > > > > vc4 supports it.
> > > > > > >
> > > > > > > Someone implemented it incorrectly then.
> > > > > >
> > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > document any of it, and when people try to clean things up they get told
> > > > > > that we got it all wrong.
> > > > >
> > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > vc4 to make the property compatible.
> > > >
> > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > it's strictly compatible with it.
> > >
> > > No it is not.
> >
> > The property is compatible with i915 interpretation of it, whether you
> > like it or not. And that's what Sebastian was referring to.
> >
> > > Eg. what happens if you set the thing to full range for RGB (which you
> > > must on many broken monitors), and then the kernel automagically
> > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > support full range YCbCr? Answer: you get crap output.
> >
> > And that part is just moving goalposts.
>
> No. Allowing users to get correct colors with broken displays
> is the sole reason why this property even exists.
HDMI 1.4, Section 6.6 - Video Quantization Ranges:
If the sink’s EDID declares a selectable YCC Quantization Range
(QY=1), then it shall expect limited range pixel values if it receives
AVI YQ=0 and it shall expect full range pixel values if it receives
AVI YQ=1. For other values of YQ, the sink shall expect pixel values
with the default range for the transmitted video format.
So, the only concern you have is if the EDID has QY set to 1 but the
monitor doesn't actually support it? If so, could we qualify the monitor
as a "broken display" and thus would require that property to apply to
YUV too?
Maxime
On Thu, Feb 15, 2024 at 11:53:17AM +0100, Maxime Ripard wrote:
> On Tue, Feb 13, 2024 at 10:38:56AM +0200, Ville Syrjälä wrote:
> > On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrjälä wrote:
> > > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrjälä wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrjälä wrote:
> > > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > Hi,
> > > > > > > > > > >
> > > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > > /**
> > > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > > + *
> > > > > > > > > > > > >
> > > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > > different things being affected:
> > > > > > > > > > > > >
> > > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > > >
> > > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > > >
> > > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > > >
> > > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > > >
> > > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > > YCbCr?
> > > > > > > > > > >
> > > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > > >
> > > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > > >
> > > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > > itself.
> > > > > > > > >
> > > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > > to enable it.
> > > > > > > > >
> > > > > > > > > vc4 supports it.
> > > > > > > >
> > > > > > > > Someone implemented it incorrectly then.
> > > > > > >
> > > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > > document any of it, and when people try to clean things up they get told
> > > > > > > that we got it all wrong.
> > > > > >
> > > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > > vc4 to make the property compatible.
> > > > >
> > > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > > it's strictly compatible with it.
> > > >
> > > > No it is not.
> > >
> > > The property is compatible with i915 interpretation of it, whether you
> > > like it or not. And that's what Sebastian was referring to.
> > >
> > > > Eg. what happens if you set the thing to full range for RGB (which you
> > > > must on many broken monitors), and then the kernel automagically
> > > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > > support full range YCbCr? Answer: you get crap output.
> > >
> > > And that part is just moving goalposts.
> >
> > No. Allowing users to get correct colors with broken displays
> > is the sole reason why this property even exists.
>
> HDMI 1.4, Section 6.6 - Video Quantization Ranges:
>
> If the sink’s EDID declares a selectable YCC Quantization Range
> (QY=1), then it shall expect limited range pixel values if it receives
> AVI YQ=0 and it shall expect full range pixel values if it receives
> AVI YQ=1. For other values of YQ, the sink shall expect pixel values
> with the default range for the transmitted video format.
>
> So, the only concern you have is if the EDID has QY set to 1 but the
> monitor doesn't actually support it? If so, could we qualify the monitor
> as a "broken display" and thus would require that property to apply to
> YUV too?
Sinks that declare a selectable quantization range are not the
problem, or at least I don't recall ever seeing one that lied about
that. The problem is the sinks that don't have selectable quantization
range, and which implement the default rules incorrectly. The only way
to get correct colors on those is for the user to override the
quantization range manually.
Typically TVs get it mostly right (though I have at least one that
also expects limited range for 640x480 which is not correct), and
many (perhaps even most?) computer displays get it wrong (as in
they always assume RGB to be full range).
We could in theory quirk those, but the quirk list would be enormous,
and fragile to maintain because the user can also shoot themselves in
the foot here by frobbing with the "black level"/etc. settings on the
display itself. So we'd surely end up with lots of false positives
on the quirk list.
--
Ville Syrjälä
Intel
On Thu, Feb 15, 2024 at 12:00:01PM +0100, Maxime Ripard wrote:
> On Mon, Feb 12, 2024 at 06:06:18PM +0100, Sebastian Wick wrote:
> > On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > Hi,
> > > > > > > > > > >
> > > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > > /**
> > > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > > + *
> > > > > > > > > > > > >
> > > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > > different things being affected:
> > > > > > > > > > > > >
> > > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > > >
> > > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > > >
> > > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > > >
> > > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > > >
> > > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > > YCbCr?
> > > > > > > > > > >
> > > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > > >
> > > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > > >
> > > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > > itself.
> > > > > > > > >
> > > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > > to enable it.
> > > > > > > > >
> > > > > > > > > vc4 supports it.
> > > > > > > >
> > > > > > > > Someone implemented it incorrectly then.
> > > > > > >
> > > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > > document any of it, and when people try to clean things up they get told
> > > > > > > that we got it all wrong.
> > > > > >
> > > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > > vc4 to make the property compatible.
> > > > >
> > > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > > it's strictly compatible with it.
> > > >
> > > > No it is not.
> > >
> > > The property is compatible with i915 interpretation of it, whether you
> > > like it or not. And that's what Sebastian was referring to.
> > >
> > > > Eg. what happens if you set the thing to full range for RGB (which you
> > > > must on many broken monitors), and then the kernel automagically
> > > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > > support full range YCbCr? Answer: you get crap output.
> > >
> > > And that part is just moving goalposts.
> >
> > But it's really not.
>
> It really is. This whole discussion started by "well it would be nice if
> we made that property handled by the core", and we're now at the "we
> need to deal with broken YCbCr displays and i915 opinion about them"
> stage. After creating documentation, unit tests, etc. It's the textbook
> definition of moving goalposts. And while i915 won't be affected by all
> that work.
Sorry, but what you're saying is just not true.
The Broadcast RGB property is an Intel specific property. It lacked
documentation but the user space contract exists and it based on how
i915 implemented it. By changing the semantics you're breaking user
space. The documentation has to document the current contract between
i915 and user space, not whatever you want the property to be like.
I get that you're frustrated that you have to do work while i915 doesn't
but none of that is relevant for what the property is and how user space
expects it to work.
> That series has been stuck for multiple iterations on pointless and
> mundane debates while the biggest part and whole point of it is not
> getting any review. So yeah, sorry, it's frustrating.
I'm reviewing the parts that I can, and that's the uAPI. I find it
really offensive that you're saying that this is pointless and mundate.
The uAPI is your end product, if it can't be used, everything you do in
your driver is utterly pointless.
> > The Broadcast RGB property kind of works from a user space perspective
> > because it's a workaround for broken sinks. If a sink expects limited
> > range we can force full range. If this however affects YCbCr modes as
> > well, then this isn't a workaround for broken RGB range anymore
> > because it now breaks YCbCr.
>
> Or, you know, it's a workaround for broken YCbCr display.
Displays can accept both RGB and YCbCr signals, drivers can chose
whichever they want, and user space can not influence or even know which
one is being used.
The automatic selection of the range is very different between RGB and
YCbCr. If user space forces the range to a specific value and the driver
for whatever reason switches from RGB to YCbCr or the other way around,
this forcing of the range will most likely be incorrect.
This is what we're talking about when we say that the semantics of the
vc4 Broadcast RGB property is broken. User space literally cannot use it
consistenly. By restricting it to RGB signals, user space can user it
consistently and fix monitors that do not follow the automatic
quantization range algorithm correctly. Yes, if there is an issue with
the quantization range of a YCbCr signal then this property doesn't
help, but it never tried to help those cases.
> > Sorry, but vc4 just has to change.
> >
> > And again: let's please stop trying to improve the property.
>
> I'm not. I'm super close to just dropping that patch entirely and keep
> the current situation that seems to work fine for everyone.
I mean, vc4 doesn't work fine apparently. You're just lucky that no one
reported issues to you.
All you have to do is adjust the documentation to say that Broadcast RGB
only affects RGB signalling. Yes, vc4 has a bug according to the docs
then, but that's okay. Fix it at some point.
> Maxime
On Mon, Feb 19, 2024 at 03:01:44PM +0100, Sebastian Wick wrote:
> On Thu, Feb 15, 2024 at 12:00:01PM +0100, Maxime Ripard wrote:
> > On Mon, Feb 12, 2024 at 06:06:18PM +0100, Sebastian Wick wrote:
> > > On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > > > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > > > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > > Hi,
> > > > > > > > > > > >
> > > > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > > > /**
> > > > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > > > + *
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > > > different things being affected:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > > > >
> > > > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > > > YCbCr?
> > > > > > > > > > > >
> > > > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > > > >
> > > > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > > > >
> > > > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > > > itself.
> > > > > > > > > >
> > > > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > > > to enable it.
> > > > > > > > > >
> > > > > > > > > > vc4 supports it.
> > > > > > > > >
> > > > > > > > > Someone implemented it incorrectly then.
> > > > > > > >
> > > > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > > > document any of it, and when people try to clean things up they get told
> > > > > > > > that we got it all wrong.
> > > > > > >
> > > > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > > > vc4 to make the property compatible.
> > > > > >
> > > > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > > > it's strictly compatible with it.
> > > > >
> > > > > No it is not.
> > > >
> > > > The property is compatible with i915 interpretation of it, whether you
> > > > like it or not. And that's what Sebastian was referring to.
> > > >
> > > > > Eg. what happens if you set the thing to full range for RGB (which you
> > > > > must on many broken monitors), and then the kernel automagically
> > > > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > > > support full range YCbCr? Answer: you get crap output.
> > > >
> > > > And that part is just moving goalposts.
> > >
> > > But it's really not.
> >
> > It really is. This whole discussion started by "well it would be nice if
> > we made that property handled by the core", and we're now at the "we
> > need to deal with broken YCbCr displays and i915 opinion about them"
> > stage. After creating documentation, unit tests, etc. It's the textbook
> > definition of moving goalposts. And while i915 won't be affected by all
> > that work.
>
> Sorry, but what you're saying is just not true.
>
> The Broadcast RGB property is an Intel specific property.
No, it's not. vc4 has been using it for a year now.
> It lacked documentation but the user space contract exists and it
> based on how i915 implemented it. By changing the semantics you're
> breaking user space. The documentation has to document the current
> contract between i915 and user space, not whatever you want the
> property to be like.
>
> I get that you're frustrated that you have to do work while i915 doesn't
> but none of that is relevant for what the property is and how user space
> expects it to work.
That's not it, really. I don't mind doing the work. I do mind losing
functionalities on something that was working fine. And getting the
blame for something that is, at best, just as much of an documentation
issue on i915 devs.
> > That series has been stuck for multiple iterations on pointless and
> > mundane debates while the biggest part and whole point of it is not
> > getting any review. So yeah, sorry, it's frustrating.
>
> I'm reviewing the parts that I can, and that's the uAPI. I find it
> really offensive that you're saying that this is pointless and mundate.
I'm sorry I offended you, but I was talking about the whole debate
itself, not the uAPI. The uAPI itself exists. It's already there, it's
used in the wild on several drivers, and several user-space components.
What that patch does is trying to document it, and test it. It's a net
benefit. Is it perfect? Probably not.
It's a net benefit nonetheless. The part where I mostly disagree with
you I guess (and what we've actually been arguing obut) is trying to get
something perfect (to the best of our knowledge) out of it.
Anyway, I'll just shut up and to do the work I guess.
> The uAPI is your end product, if it can't be used, everything you do in
> your driver is utterly pointless.
>
> > > The Broadcast RGB property kind of works from a user space perspective
> > > because it's a workaround for broken sinks. If a sink expects limited
> > > range we can force full range. If this however affects YCbCr modes as
> > > well, then this isn't a workaround for broken RGB range anymore
> > > because it now breaks YCbCr.
> >
> > Or, you know, it's a workaround for broken YCbCr display.
>
> Displays can accept both RGB and YCbCr signals, drivers can chose
> whichever they want, and user space can not influence or even know which
> one is being used.
>
> The automatic selection of the range is very different between RGB and
> YCbCr. If user space forces the range to a specific value and the driver
> for whatever reason switches from RGB to YCbCr or the other way around,
> this forcing of the range will most likely be incorrect.
>
> This is what we're talking about when we say that the semantics of the
> vc4 Broadcast RGB property is broken. User space literally cannot use it
> consistenly. By restricting it to RGB signals, user space can user it
> consistently and fix monitors that do not follow the automatic
> quantization range algorithm correctly. Yes, if there is an issue with
> the quantization range of a YCbCr signal then this property doesn't
> help, but it never tried to help those cases.
Ack, thanks
Maxime
On Thu, Feb 22, 2024 at 02:58:45PM +0200, Ville Syrj?l? wrote:
> On Thu, Feb 22, 2024 at 11:54:04AM +0100, Maxime Ripard wrote:
> > On Mon, Feb 19, 2024 at 03:01:44PM +0100, Sebastian Wick wrote:
> > > On Thu, Feb 15, 2024 at 12:00:01PM +0100, Maxime Ripard wrote:
> > > > On Mon, Feb 12, 2024 at 06:06:18PM +0100, Sebastian Wick wrote:
> > > > > On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > > > > > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > > > > > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > > > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > > > > Hi,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > > > > > /**
> > > > > > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > > > > > + *
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > > > > > different things being affected:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > > > > > YCbCr?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > > > > > >
> > > > > > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > > > > > >
> > > > > > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > > > > > itself.
> > > > > > > > > > > >
> > > > > > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > > > > > to enable it.
> > > > > > > > > > > >
> > > > > > > > > > > > vc4 supports it.
> > > > > > > > > > >
> > > > > > > > > > > Someone implemented it incorrectly then.
> > > > > > > > > >
> > > > > > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > > > > > document any of it, and when people try to clean things up they get told
> > > > > > > > > > that we got it all wrong.
> > > > > > > > >
> > > > > > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > > > > > vc4 to make the property compatible.
> > > > > > > >
> > > > > > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > > > > > it's strictly compatible with it.
> > > > > > >
> > > > > > > No it is not.
> > > > > >
> > > > > > The property is compatible with i915 interpretation of it, whether you
> > > > > > like it or not. And that's what Sebastian was referring to.
> > > > > >
> > > > > > > Eg. what happens if you set the thing to full range for RGB (which you
> > > > > > > must on many broken monitors), and then the kernel automagically
> > > > > > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > > > > > support full range YCbCr? Answer: you get crap output.
> > > > > >
> > > > > > And that part is just moving goalposts.
> > > > >
> > > > > But it's really not.
> > > >
> > > > It really is. This whole discussion started by "well it would be nice if
> > > > we made that property handled by the core", and we're now at the "we
> > > > need to deal with broken YCbCr displays and i915 opinion about them"
> > > > stage. After creating documentation, unit tests, etc. It's the textbook
> > > > definition of moving goalposts. And while i915 won't be affected by all
> > > > that work.
> > >
> > > Sorry, but what you're saying is just not true.
> > >
> > > The Broadcast RGB property is an Intel specific property.
> >
> > No, it's not. vc4 has been using it for a year now.
> >
> > > It lacked documentation but the user space contract exists and it
> > > based on how i915 implemented it. By changing the semantics you're
> > > breaking user space. The documentation has to document the current
> > > contract between i915 and user space, not whatever you want the
> > > property to be like.
> > >
> > > I get that you're frustrated that you have to do work while i915 doesn't
> > > but none of that is relevant for what the property is and how user space
> > > expects it to work.
> >
> > That's not it, really. I don't mind doing the work. I do mind losing
> > functionalities on something that was working fine. And getting the
> > blame for something that is, at best, just as much of an documentation
> > issue on i915 devs.
>
> We've had a couple of these cases recently where people have taken
> some old property implemented by i915 and implemented it differently
> in some other driver. Dunno if the reason was that people didn't try
> to understand what i915 is doing and why, or they misundestood it,
> or they understood it but decided to ignore it anyway.
>
> Unfortunately having undocumented corners in the uapi is simply
> a fact of life when dealing with a >15 year old legacy codebase.
> Also there were basically no rules regarding properties in the
> past, so everyone just added random properties whenever they
> felt like it.
>
> I think going forward we should probably lay down some extra
> ground rules; if an old undocumented uapi is being extended
> to cover more than one driver we must first figure out what
> the de facto semantics are, and document things properly
> before anything else gets done.
That would be great. The documentation already has requirements for new
properties. Adding the requirement for extending driver-specific
properties to more drivers there would be great and make it "official".
>
> --
> Ville Syrj?l?
> Intel
>
On Thu, Feb 22, 2024 at 02:58:45PM +0200, Ville Syrj?l? wrote:
> On Thu, Feb 22, 2024 at 11:54:04AM +0100, Maxime Ripard wrote:
> > On Mon, Feb 19, 2024 at 03:01:44PM +0100, Sebastian Wick wrote:
> > > On Thu, Feb 15, 2024 at 12:00:01PM +0100, Maxime Ripard wrote:
> > > > On Mon, Feb 12, 2024 at 06:06:18PM +0100, Sebastian Wick wrote:
> > > > > On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > > > > > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > > > > > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > > > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > > > > Hi,
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > > > > > /**
> > > > > > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > > > > > + *
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > > > > > different things being affected:
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > > > > > YCbCr?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > > > > > >
> > > > > > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > > > > > >
> > > > > > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > > > > > itself.
> > > > > > > > > > > >
> > > > > > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > > > > > to enable it.
> > > > > > > > > > > >
> > > > > > > > > > > > vc4 supports it.
> > > > > > > > > > >
> > > > > > > > > > > Someone implemented it incorrectly then.
> > > > > > > > > >
> > > > > > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > > > > > document any of it, and when people try to clean things up they get told
> > > > > > > > > > that we got it all wrong.
> > > > > > > > >
> > > > > > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > > > > > vc4 to make the property compatible.
> > > > > > > >
> > > > > > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > > > > > it's strictly compatible with it.
> > > > > > >
> > > > > > > No it is not.
> > > > > >
> > > > > > The property is compatible with i915 interpretation of it, whether you
> > > > > > like it or not. And that's what Sebastian was referring to.
> > > > > >
> > > > > > > Eg. what happens if you set the thing to full range for RGB (which you
> > > > > > > must on many broken monitors), and then the kernel automagically
> > > > > > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > > > > > support full range YCbCr? Answer: you get crap output.
> > > > > >
> > > > > > And that part is just moving goalposts.
> > > > >
> > > > > But it's really not.
> > > >
> > > > It really is. This whole discussion started by "well it would be nice if
> > > > we made that property handled by the core", and we're now at the "we
> > > > need to deal with broken YCbCr displays and i915 opinion about them"
> > > > stage. After creating documentation, unit tests, etc. It's the textbook
> > > > definition of moving goalposts. And while i915 won't be affected by all
> > > > that work.
> > >
> > > Sorry, but what you're saying is just not true.
> > >
> > > The Broadcast RGB property is an Intel specific property.
> >
> > No, it's not. vc4 has been using it for a year now.
> >
> > > It lacked documentation but the user space contract exists and it
> > > based on how i915 implemented it. By changing the semantics you're
> > > breaking user space. The documentation has to document the current
> > > contract between i915 and user space, not whatever you want the
> > > property to be like.
> > >
> > > I get that you're frustrated that you have to do work while i915 doesn't
> > > but none of that is relevant for what the property is and how user space
> > > expects it to work.
> >
> > That's not it, really. I don't mind doing the work. I do mind losing
> > functionalities on something that was working fine. And getting the
> > blame for something that is, at best, just as much of an documentation
> > issue on i915 devs.
>
> We've had a couple of these cases recently where people have taken
> some old property implemented by i915 and implemented it differently
> in some other driver. Dunno if the reason was that people didn't try
> to understand what i915 is doing and why, or they misundestood it,
> or they understood it but decided to ignore it anyway.
I can't tell for the other cases, but in this particular case it's
definitely in the misunderstanding category. And implying that we didn't
even try to understand it, or that we didn't consult anyone when the
patches were posted on the ML for months doesn't seem fair either.
> Unfortunately having undocumented corners in the uapi is simply
> a fact of life when dealing with a >15 year old legacy codebase.
> Also there were basically no rules regarding properties in the
> past, so everyone just added random properties whenever they
> felt like it.
>
> I think going forward we should probably lay down some extra
> ground rules; if an old undocumented uapi is being extended
> to cover more than one driver we must first figure out what
> the de facto semantics are, and document things properly
> before anything else gets done.
That sounds reasonable, but you (not you personally, but the i915 team
in general) also have to engage, you can't just impose that on everyone
else, and then just hope they will figure it out perfectly without your
help.
I think that whole story is a testament to that.
Maxime
On Thu, Feb 22, 2024 at 11:54:04AM +0100, Maxime Ripard wrote:
> On Mon, Feb 19, 2024 at 03:01:44PM +0100, Sebastian Wick wrote:
> > On Thu, Feb 15, 2024 at 12:00:01PM +0100, Maxime Ripard wrote:
> > > On Mon, Feb 12, 2024 at 06:06:18PM +0100, Sebastian Wick wrote:
> > > > On Mon, Feb 12, 2024 at 05:53:48PM +0100, Maxime Ripard wrote:
> > > > > On Mon, Feb 12, 2024 at 05:49:33PM +0200, Ville Syrj?l? wrote:
> > > > > > On Mon, Feb 12, 2024 at 11:01:07AM +0100, Maxime Ripard wrote:
> > > > > > > On Fri, Feb 09, 2024 at 09:34:35PM +0100, Sebastian Wick wrote:
> > > > > > > > On Mon, Feb 05, 2024 at 10:39:38AM +0100, Maxime Ripard wrote:
> > > > > > > > > On Fri, Feb 02, 2024 at 06:37:52PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > On Fri, Feb 02, 2024 at 04:59:30PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > On Fri, Feb 02, 2024 at 05:40:47PM +0200, Ville Syrj?l? wrote:
> > > > > > > > > > > > On Fri, Feb 02, 2024 at 02:01:39PM +0100, Maxime Ripard wrote:
> > > > > > > > > > > > > Hi,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Mon, Jan 15, 2024 at 03:37:20PM +0100, Sebastian Wick wrote:
> > > > > > > > > > > > > > > > /**
> > > > > > > > > > > > > > > > * 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().
> > > > > > > > > > > > > > > > + *
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > This is a good time to document this in more detail. There might be two
> > > > > > > > > > > > > > > different things being affected:
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > 1. The signalling (InfoFrame/SDP/...)
> > > > > > > > > > > > > > > 2. The color pipeline processing
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > All values of Broadcast RGB always affect the color pipeline processing
> > > > > > > > > > > > > > > such that a full-range input to the CRTC is converted to either full- or
> > > > > > > > > > > > > > > limited-range, depending on what the monitor is supposed to accept.
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > When automatic is selected, does that mean that there is no signalling,
> > > > > > > > > > > > > > > or that the signalling matches what the monitor is supposed to accept
> > > > > > > > > > > > > > > according to the spec? Also, is this really HDMI specific?
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > When full or limited is selected and the monitor doesn't support the
> > > > > > > > > > > > > > > signalling, what happens?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Forgot to mention: user-space still has no control over RGB vs YCbCr on
> > > > > > > > > > > > > > the cable, so is this only affecting RGB? If not, how does it affect
> > > > > > > > > > > > > > YCbCr?
> > > > > > > > > > > > >
> > > > > > > > > > > > > So I dug a bit into both the i915 and vc4 drivers, and it looks like if
> > > > > > > > > > > > > we're using a YCbCr format, i915 will always use a limited range while
> > > > > > > > > > > > > vc4 will follow the value of the property.
> > > > > > > > > > > >
> > > > > > > > > > > > The property is literally called "Broadcast *RGB*".
> > > > > > > > > > > > That should explain why it's only affecting RGB.
> > > > > > > > > > >
> > > > > > > > > > > Right. And the limited range option is called "Limited 16:235" despite
> > > > > > > > > > > being usable on bpc > 8 bits. Naming errors occurs, and history happens
> > > > > > > > > > > to make names inconsistent too, that's fine and not an argument in
> > > > > > > > > > > itself.
> > > > > > > > > > >
> > > > > > > > > > > > Full range YCbCr is a much rarer beast so we've never bothered
> > > > > > > > > > > > to enable it.
> > > > > > > > > > >
> > > > > > > > > > > vc4 supports it.
> > > > > > > > > >
> > > > > > > > > > Someone implemented it incorrectly then.
> > > > > > > > >
> > > > > > > > > Incorrectly according to what documentation / specification? I'm sorry,
> > > > > > > > > but I find it super ironic that i915 gets to do its own thing, not
> > > > > > > > > document any of it, and when people try to clean things up they get told
> > > > > > > > > that we got it all wrong.
> > > > > > > >
> > > > > > > > FWIW, this was an i915 property and if another driver uses the same
> > > > > > > > property name it must have the same behavior. Yes, it isn't standardized
> > > > > > > > and yes, it's not documented (hence this effort here) but it's still on
> > > > > > > > vc4 to make the property compatible.
> > > > > > >
> > > > > > > How is it not compatible? It's a superset of what i915 provides, but
> > > > > > > it's strictly compatible with it.
> > > > > >
> > > > > > No it is not.
> > > > >
> > > > > The property is compatible with i915 interpretation of it, whether you
> > > > > like it or not. And that's what Sebastian was referring to.
> > > > >
> > > > > > Eg. what happens if you set the thing to full range for RGB (which you
> > > > > > must on many broken monitors), and then the kernel automagically
> > > > > > switches to YCbCr (for whatever reason) but the monitor doesn't
> > > > > > support full range YCbCr? Answer: you get crap output.
> > > > >
> > > > > And that part is just moving goalposts.
> > > >
> > > > But it's really not.
> > >
> > > It really is. This whole discussion started by "well it would be nice if
> > > we made that property handled by the core", and we're now at the "we
> > > need to deal with broken YCbCr displays and i915 opinion about them"
> > > stage. After creating documentation, unit tests, etc. It's the textbook
> > > definition of moving goalposts. And while i915 won't be affected by all
> > > that work.
> >
> > Sorry, but what you're saying is just not true.
> >
> > The Broadcast RGB property is an Intel specific property.
>
> No, it's not. vc4 has been using it for a year now.
>
> > It lacked documentation but the user space contract exists and it
> > based on how i915 implemented it. By changing the semantics you're
> > breaking user space. The documentation has to document the current
> > contract between i915 and user space, not whatever you want the
> > property to be like.
> >
> > I get that you're frustrated that you have to do work while i915 doesn't
> > but none of that is relevant for what the property is and how user space
> > expects it to work.
>
> That's not it, really. I don't mind doing the work. I do mind losing
> functionalities on something that was working fine. And getting the
> blame for something that is, at best, just as much of an documentation
> issue on i915 devs.
We've had a couple of these cases recently where people have taken
some old property implemented by i915 and implemented it differently
in some other driver. Dunno if the reason was that people didn't try
to understand what i915 is doing and why, or they misundestood it,
or they understood it but decided to ignore it anyway.
Unfortunately having undocumented corners in the uapi is simply
a fact of life when dealing with a >15 year old legacy codebase.
Also there were basically no rules regarding properties in the
past, so everyone just added random properties whenever they
felt like it.
I think going forward we should probably lay down some extra
ground rules; if an old undocumented uapi is being extended
to cover more than one driver we must first figure out what
the de facto semantics are, and document things properly
before anything else gets done.
--
Ville Syrj?l?
Intel