2023-10-31 16:49:11

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 00/37] drm/connector: Create HDMI Connector infrastructure

Hi,

Here's a series that creates a subclass of drm_connector 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 an
infoframe-decode tool.

This series thus leverages the infoframe generation code to expose it
through debugfs.

This entire series has been tested on a Pi4, 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 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 (37):
drm/connector: Introduce an HDMI connector
drm/connector: hdmi: Create a custom 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/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: Move infoframe disable to separate function
drm/rockchip: inno_hdmi: Create mask retrieval functions
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 | 661 ++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_atomic_uapi.c | 4 +
drivers/gpu/drm/drm_connector.c | 208 ++++++++++
drivers/gpu/drm/drm_debugfs.c | 110 +++++
drivers/gpu/drm/rockchip/inno_hdmi.c | 409 +++++++-----------
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 203 +++++----
drivers/gpu/drm/vc4/vc4_hdmi.c | 624 ++++------------------------
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 | 248 +++++++++++
14 files changed, 1598 insertions(+), 944 deletions(-)
---
base-commit: ffc253263a1375a65fa6c9f62a893e9767fbebfa
change-id: 20230814-kms-hdmi-connector-state-616787e67927

Best regards,
--
Maxime Ripard <[email protected]>


2023-10-31 16:49:12

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 02/37] drm/connector: hdmi: Create a custom state

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 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 | 3 +++
3 files changed, 42 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 784e63d70a42..29cb64e9cf28 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -556,6 +556,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
@@ -605,6 +621,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 c86cd363d652..5961f2ad48b1 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1022,6 +1022,9 @@ struct drm_connector_state {
* DRM blob property for HDR output metadata
*/
struct drm_property_blob *hdr_output_metadata;
+
+ struct {
+ } hdmi;
};

/**

--
2.41.0

2023-10-31 16:49:22

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 05/37] drm/connector: hdmi: Add output BPC to the connector state

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 | 8 +++++++-
drivers/gpu/drm/drm_connector.c | 20 +++++++++++++++++++-
include/drm/drm_connector.h | 13 ++++++++++++-
4 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 55cb117cd49a..51aac1b2aaaf 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 c3f124af64dc..406ba358aa14 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -570,6 +570,11 @@ 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.output_bpc = max_bpc;
new_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
@@ -686,7 +691,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 36d728e811ae..508d1c667732 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;

@@ -487,6 +489,22 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (ret)
return ret;

+ if (max_bpc) {
+ if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
+ return -EINVAL;
+
+ /*
+ * drm_connector_attach_max_bpc_property() requires the
+ * connector to have a state.
+ */
+ if (connector->funcs->reset)
+ connector->funcs->reset(connector);
+
+ drm_connector_attach_hdr_output_metadata_property(connector);
+ drm_connector_attach_max_bpc_property(connector, 8, max_bpc);
+ connector->max_bpc = max_bpc;
+ }
+
return 0;
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index fc1fa754a40d..2d664b6ac0a6 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1059,6 +1059,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;
};

@@ -1703,6 +1708,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.
@@ -1942,7 +1952,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.41.0

2023-10-31 16:49:28

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 07/37] drm/connector: hdmi: Add HDMI compute clock helper

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 | 34 ++++++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 5 +++++
2 files changed, 39 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 9037e1b1b383..a96c9b54c554 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2955,6 +2955,40 @@ 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
+ */
+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;
+
+ 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/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 32f0b3b7383e..781a5726a26a 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;
@@ -2118,6 +2119,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.41.0

2023-10-31 16:49:31

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 06/37] drm/connector: hdmi: Add support for output format

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 | 4 +++-
drivers/gpu/drm/drm_connector.c | 28 ++++++++++++++++++++++++++++
include/drm/drm_connector.h | 16 ++++++++++++++++
4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 51aac1b2aaaf..0ebe1142dcfe 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 406ba358aa14..37262dd002c8 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -575,6 +575,7 @@ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
new_state->max_bpc = max_bpc;
new_state->max_requested_bpc = max_bpc;
new_state->hdmi.output_bpc = max_bpc;
+ new_state->hdmi.output_format = HDMI_COLORSPACE_RGB;
new_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
@@ -692,7 +693,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 508d1c667732..9037e1b1b383 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;
@@ -489,6 +491,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (ret)
return ret;

+ connector->hdmi.supported_formats = supported_formats;
+
if (max_bpc) {
if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
return -EINVAL;
@@ -1215,6 +1219,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/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 2d664b6ac0a6..32f0b3b7383e 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
@@ -1064,6 +1066,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;
};

@@ -1930,6 +1937,14 @@ struct drm_connector {

/** @hdr_sink_metadata: HDR Metadata Information read from sink */
struct hdr_sink_metadata hdr_sink_metadata;
+
+ 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)
@@ -1953,6 +1968,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.41.0

2023-10-31 16:49:33

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 08/37] drm/connector: hdmi: Calculate TMDS character rate

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 +++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 5 ++++
3 files changed, 50 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 0ebe1142dcfe..fed9d437a4a6 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 37262dd002c8..480fa3445f40 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -670,6 +670,41 @@ static bool hdmi_is_full_range(const struct drm_connector *connector,
return drm_default_rgb_quant_range(mode);
}

+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
@@ -689,9 +724,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/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 781a5726a26a..93613c57c6de 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1072,6 +1072,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.41.0

2023-10-31 16:49:35

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 01/37] drm/connector: Introduce an HDMI connector

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.

Let's create a new connector variant specifically dedicated to HDMI
controllers that will allow to abstract away the duplicated logic.

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 +++++++++++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 5 +++++
2 files changed, 44 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index c44d5bcf1284..997f8ab21d04 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/include/drm/drm_connector.h b/include/drm/drm_connector.h
index d300fde6c1a4..c86cd363d652 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1894,6 +1894,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.41.0

2023-10-31 16:49:38

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 03/37] drm/connector: hdmi: Add Broadcast RGB property

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 +++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 36 +++++++++++++++
6 files changed, 138 insertions(+), 1 deletion(-)

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 c277b198fa3f..b1b56dcaa76b 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 29cb64e9cf28..37193c6f9fca 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -569,6 +569,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);

@@ -636,6 +637,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 98d3b10c08ae..96798592bd64 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -798,6 +798,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);
@@ -881,6 +883,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 997f8ab21d04..36d728e811ae 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -1174,6 +1174,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
*
@@ -1640,6 +1663,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
@@ -2502,6 +2545,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/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 5961f2ad48b1..168009f588ab 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
@@ -1024,6 +1048,11 @@ struct drm_connector_state {
struct drm_property_blob *hdr_output_metadata;

struct {
+ /**
+ * @broadcast_rgb: Connector property to pass the
+ * Broadcast RGB selection value.
+ */
+ enum drm_hdmi_broadcast_rgb broadcast_rgb;
} hdmi;
};

@@ -1692,6 +1721,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)
@@ -2011,6 +2046,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.41.0

2023-10-31 16:49:40

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 04/37] drm/connector: hdmi: Add RGB Quantization Range to the connector state

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 +++++++++++++++++++++++++++++++
include/drm/drm_atomic_state_helper.h | 1 +
include/drm/drm_connector.h | 6 +++++
4 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index b1b56dcaa76b..55cb117cd49a 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 37193c6f9fca..c3f124af64dc 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>
@@ -622,6 +623,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);
+}
+
/**
* drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
* @connector: DRM Connector
@@ -642,6 +684,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/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 168009f588ab..fc1fa754a40d 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1053,6 +1053,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.41.0

2023-10-31 16:49:56

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 09/37] drm/connector: hdmi: Add custom hook to filter TMDS character rate

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 ++++
include/drm/drm_connector.h | 30 ++++++++++++++++++++++++++++++
3 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 480fa3445f40..0dd1dad99cce 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -675,11 +675,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 a96c9b54c554..6dd5a82df884 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,
@@ -509,6 +511,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
connector->max_bpc = max_bpc;
}

+ connector->hdmi.funcs = hdmi_funcs;
+
return 0;
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 93613c57c6de..5005b4c196ac 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1080,6 +1080,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
*
@@ -1950,6 +1974,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;
};

@@ -1972,6 +2001,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.41.0

2023-10-31 16:49:58

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 13/37] drm/vc4: hdmi: Create destroy state implementation

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.41.0

2023-10-31 16:49:58

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 14/37] drm/vc4: hdmi: Switch to HDMI connector

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 | 636 +++++--------------------------------
drivers/gpu/drm/vc4/vc4_hdmi.h | 44 +--
drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 6 +-
3 files changed, 84 insertions(+), 602 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index f05e2c95a60d..1c2258c0e0e7 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,23 @@ 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))
+ 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 +726,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 +895,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 +909,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 +1132,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 +1147,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 +1171,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 +1260,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 +1311,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 +1328,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 +1412,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 +1490,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 +1555,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 +1622,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 +1639,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 +1668,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 +1684,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 +1722,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 +1730,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 +1738,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 +1931,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 +1938,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 +2029,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 +2106,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, &params->cea, sizeof(params->cea));
- vc4_hdmi_set_audio_infoframe(encoder);
+ ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
+ &params->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.41.0

2023-10-31 16:49:59

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 12/37] drm/connector: hdmi: Create Infoframe DebugFS entries

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 2de43ff3ce0a..3c65b1d3f926 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -538,6 +538,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, drm);
+ if (ret)
+ return ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, spd);
+ if (ret)
+ return ret;
+
+ ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, vendor);
+ 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_minor *minor = connector->dev->primary;
@@ -565,6 +673,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.41.0

2023-10-31 16:50:01

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 10/37] drm/connector: hdmi: Compute bpc and format automatically

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 | 185 +++++++++++++++++++++++++++++-
1 file changed, 181 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 0dd1dad99cce..1420d9fdfa35 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -670,6 +670,96 @@ static bool hdmi_is_full_range(const struct drm_connector *connector,
return drm_default_rgb_quant_range(mode);
}

+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,
@@ -714,6 +804,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
@@ -739,10 +918,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);

- ret = hdmi_compute_clock(connector, new_state, mode,
- new_state->hdmi.output_bpc,
- new_state->hdmi.output_format);
- if (!ret)
+ ret = hdmi_compute_config(connector, new_state, mode);
+ if (ret)
return ret;

if (old_state->hdmi.broadcast_rgb != new_state->hdmi.broadcast_rgb ||

--
2.41.0

2023-10-31 16:50:02

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 16/37] drm/rockchip: inno_hdmi: Remove useless copy of drm_display_mode

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.41.0

2023-10-31 16:50:53

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 11/37] drm/connector: hdmi: Add Infoframes generation

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 | 9 +
include/drm/drm_atomic_state_helper.h | 7 +
include/drm/drm_connector.h | 131 ++++++++++++
5 files changed, 475 insertions(+)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 3caa020391c7..b82a38fc8355 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -99,6 +99,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 1420d9fdfa35..c8331a779d74 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>

@@ -893,6 +895,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.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_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.vendor;
+ 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_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
@@ -922,6 +1060,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) {
@@ -939,6 +1081,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, 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, vendor);
+ 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 6dd5a82df884..bc82738306d9 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,
@@ -494,6 +497,12 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
return ret;

connector->hdmi.supported_formats = supported_formats;
+ strscpy(connector->hdmi.vendor, vendor, sizeof(connector->hdmi.vendor));
+ strscpy(connector->hdmi.product, product, sizeof(connector->hdmi.product));
+
+ ret = drmm_mutex_init(dev, &connector->hdmi.infoframes.lock);
+ if (ret)
+ return ret;

if (max_bpc) {
if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
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 5005b4c196ac..459eea45af29 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -905,6 +905,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
*/
@@ -1057,6 +1072,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;
+
+ /**
+ * @drm: DRM Infoframes structure matching our
+ * state.
+ */
+ struct drm_connector_hdmi_infoframe drm;
+
+ /**
+ * @spd: SPD Infoframes structure matching our
+ * state.
+ */
+ struct drm_connector_hdmi_infoframe spd;
+
+ /**
+ * @vendor: Vendor Infoframes structure matching
+ * our state.
+ */
+ struct drm_connector_hdmi_infoframe vendor;
+ } infoframes;
+
/**
* @is_full_range: Is the output supposed to use a full
* RGB Quantization Range or not?
@@ -1102,6 +1146,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);
};

/**
@@ -1969,6 +2048,16 @@ struct drm_connector {
struct hdr_sink_metadata hdr_sink_metadata;

struct {
+ /**
+ * @vendor: HDMI Controller Vendor Name
+ */
+ char vendor[8];
+
+ /**
+ * @product: HDMI Controller Product Name
+ */
+ char product[16];
+
/**
* @supported_formats: Bitmask of @hdmi_colorspace
* supported by the controller.
@@ -1979,6 +2068,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;
+
+ /**
+ * @drm: Current DRM Infoframes structure. Protected by
+ * @lock.
+ */
+ struct drm_connector_hdmi_infoframe drm;
+
+ /**
+ * @spd: Current SPD Infoframes structure. Protected by
+ * @lock.
+ */
+ struct drm_connector_hdmi_infoframe spd;
+
+ /**
+ * @vendor: Current Vendor Infoframes structure.
+ * Protected by @lock.
+ */
+ struct drm_connector_hdmi_infoframe vendor;
+ } infoframes;
} hdmi;
};

@@ -2000,6 +2130,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.41.0

2023-10-31 16:50:55

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 18/37] drm/rockchip: inno_hdmi: Get rid of mode_set

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.41.0

2023-10-31 16:51:03

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 15/37] drm/rockchip: inno_hdmi: Remove useless mode_fixup

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.41.0

2023-10-31 16:51:27

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 17/37] drm/rockchip: inno_hdmi: Switch encoder hooks to atomic

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.41.0

2023-10-31 16:52:36

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 21/37] drm/rockchip: inno_hdmi: Remove useless input format

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.41.0

2023-10-31 16:52:43

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 19/37] drm/rockchip: inno_hdmi: no need to store vic

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.41.0

2023-10-31 16:52:44

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 22/37] drm/rockchip: inno_hdmi: Remove useless output format

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.41.0

2023-10-31 16:52:58

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 23/37] drm/rockchip: inno_hdmi: Remove useless colorimetry

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.41.0

2023-10-31 16:53:14

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 24/37] drm/rockchip: inno_hdmi: Remove useless enum

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.41.0

2023-10-31 16:53:27

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 25/37] drm/rockchip: inno_hdmi: Remove tmds rate from structure

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.41.0

2023-10-31 16:53:42

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 26/37] drm/rockchip: inno_hdmi: Remove useless coeff_csc matrix

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.41.0

2023-10-31 16:54:54

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 34/37] drm/sun4i: hdmi: Move mode_set into enable

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.41.0

2023-10-31 16:54:55

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 27/37] drm/rockchip: inno_hdmi: Remove useless mode_valid

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.41.0

2023-10-31 16:55:02

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 28/37] drm/rockchip: inno_hdmi: Move infoframe disable to separate function

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 | 53 +++++++++++++++++++++++-------------
1 file changed, 34 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 59b2b676b7b8..e3a421d98dd4 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -156,33 +156,38 @@ 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,
- u32 mask, u32 disable, u32 enable)
+static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
+ u32 frame_index,
+ u32 mask, u32 disable)
{
if (mask)
hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);

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,
+ u32 mask, u32 disable, u32 enable)
+{
+ 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;
+ inno_hdmi_disable_frame(hdmi, frame_index, mask, disable);

- for (i = 0; i < rc; i++)
- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
- packed_frame[i]);
+ rc = hdmi_infoframe_pack(frame, packed_frame,
+ sizeof(packed_frame));
+ if (rc < 0)
+ return rc;

- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
- }
+ for (i = 0; i < rc; i++)
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+ packed_frame[i]);

- return setup_rc;
+ if (mask)
+ hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+
+ return 0;
}

static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
@@ -194,8 +199,13 @@ static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
&hdmi->connector,
mode);
+ if (rc) {
+ inno_hdmi_disable_frame(hdmi, INFOFRAME_VSI,
+ m_PACKET_VSI_EN, v_PACKET_VSI_EN(0));
+ return rc;
+ }

- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
+ return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_VSI,
m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
}

@@ -208,9 +218,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, 0, 0);
+ return rc;
+ }
+
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, &frame, INFOFRAME_AVI, 0, 0, 0);
}

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)

--
2.41.0

2023-10-31 16:55:02

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 35/37] drm/sun4i: hdmi: Switch to container_of_const

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().

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.41.0

2023-10-31 16:55:05

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 36/37] drm/sun4i: hdmi: Consolidate atomic_check and mode_valid

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.41.0

2023-10-31 16:55:48

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 29/37] drm/rockchip: inno_hdmi: Create mask retrieval functions

The register mask and bits to enable or disable a given infoframe
depends on its type.

This is currently passed as an argument to the function that writes an
infoframe, but let's create a helper function to retrieve them based on
the type to make further reworks easier.

Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 75 ++++++++++++++++++++++++++++++------
1 file changed, 63 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index e3a421d98dd4..bc7fb1278cb2 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -156,10 +156,62 @@ 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,
- u32 mask, u32 disable)
+static u32 inno_hdmi_get_frame_mask(struct inno_hdmi *hdmi,
+ u32 frame_index)
{
+ struct drm_device *drm = hdmi->connector.dev;
+
+ switch (frame_index) {
+ case INFOFRAME_VSI:
+ return m_PACKET_VSI_EN;
+ case INFOFRAME_AVI:
+ return 0;
+ default:
+ drm_err(drm, "Unknown infoframe type: %u\n", frame_index);
+ }
+
+ return 0;
+}
+
+static u32 inno_hdmi_get_frame_disable(struct inno_hdmi *hdmi,
+ u32 frame_index)
+{
+ struct drm_device *drm = hdmi->connector.dev;
+
+ switch (frame_index) {
+ case INFOFRAME_VSI:
+ return v_PACKET_VSI_EN(0);
+ case INFOFRAME_AVI:
+ return 0;
+ default:
+ drm_err(drm, "Unknown infoframe type: %u\n", frame_index);
+ }
+
+ return 0;
+}
+
+static u32 inno_hdmi_get_frame_enable(struct inno_hdmi *hdmi,
+ u32 frame_index)
+{
+ struct drm_device *drm = hdmi->connector.dev;
+
+ switch (frame_index) {
+ case INFOFRAME_VSI:
+ return v_PACKET_VSI_EN(1);
+ case INFOFRAME_AVI:
+ return 0;
+ default:
+ drm_err(drm, "Unknown infoframe type: %u\n", frame_index);
+ }
+
+ return 0;
+}
+
+static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, u32 frame_index)
+{
+ u32 disable = inno_hdmi_get_frame_disable(hdmi, frame_index);
+ u32 mask = inno_hdmi_get_frame_mask(hdmi, frame_index);
+
if (mask)
hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);

@@ -167,13 +219,14 @@ static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
}

static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
- union hdmi_infoframe *frame, u32 frame_index,
- u32 mask, u32 disable, u32 enable)
+ union hdmi_infoframe *frame, u32 frame_index)
{
+ u32 enable = inno_hdmi_get_frame_enable(hdmi, frame_index);
+ u32 mask = inno_hdmi_get_frame_mask(hdmi, frame_index);
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;

- inno_hdmi_disable_frame(hdmi, frame_index, mask, disable);
+ inno_hdmi_disable_frame(hdmi, frame_index);

rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
@@ -200,13 +253,11 @@ static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
&hdmi->connector,
mode);
if (rc) {
- inno_hdmi_disable_frame(hdmi, INFOFRAME_VSI,
- m_PACKET_VSI_EN, v_PACKET_VSI_EN(0));
+ inno_hdmi_disable_frame(hdmi, INFOFRAME_VSI);
return rc;
}

- return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_VSI,
- m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
+ return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_VSI);
}

static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
@@ -219,13 +270,13 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
&hdmi->connector,
mode);
if (rc) {
- inno_hdmi_disable_frame(hdmi, INFOFRAME_AVI, 0, 0);
+ inno_hdmi_disable_frame(hdmi, INFOFRAME_AVI);
return rc;
}

frame.avi.colorspace = HDMI_COLORSPACE_RGB;

- return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_AVI, 0, 0, 0);
+ return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_AVI);
}

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)

--
2.41.0

2023-10-31 16:55:49

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 37/37] drm/sun4i: hdmi: Switch to HDMI connector

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.41.0

2023-10-31 16:56:15

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 30/37] drm/rockchip: inno_hdmi: Switch to infoframe type

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 | 71 +++++++++++++++++++++++-------------
1 file changed, 45 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index bc7fb1278cb2..ed1d10efbef4 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -156,61 +156,80 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}

+static u32 inno_hdmi_get_frame_index(struct inno_hdmi *hdmi,
+ enum hdmi_infoframe_type type)
+{
+ struct drm_device *drm = hdmi->connector.dev;
+
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ return INFOFRAME_VSI;
+ case HDMI_INFOFRAME_TYPE_AVI:
+ return INFOFRAME_AVI;
+ default:
+ drm_err(drm, "Unknown infoframe type: %u\n", type);
+ }
+
+ return 0;
+}
+
static u32 inno_hdmi_get_frame_mask(struct inno_hdmi *hdmi,
- u32 frame_index)
+ enum hdmi_infoframe_type type)
{
struct drm_device *drm = hdmi->connector.dev;

- switch (frame_index) {
- case INFOFRAME_VSI:
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_VENDOR:
return m_PACKET_VSI_EN;
- case INFOFRAME_AVI:
+ case HDMI_INFOFRAME_TYPE_AVI:
return 0;
default:
- drm_err(drm, "Unknown infoframe type: %u\n", frame_index);
+ drm_err(drm, "Unknown infoframe type: %u\n", type);
}

return 0;
}

static u32 inno_hdmi_get_frame_disable(struct inno_hdmi *hdmi,
- u32 frame_index)
+ enum hdmi_infoframe_type type)
{
struct drm_device *drm = hdmi->connector.dev;

- switch (frame_index) {
- case INFOFRAME_VSI:
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_VENDOR:
return v_PACKET_VSI_EN(0);
- case INFOFRAME_AVI:
+ case HDMI_INFOFRAME_TYPE_AVI:
return 0;
default:
- drm_err(drm, "Unknown infoframe type: %u\n", frame_index);
+ drm_err(drm, "Unknown infoframe type: %u\n", type);
}

return 0;
}

static u32 inno_hdmi_get_frame_enable(struct inno_hdmi *hdmi,
- u32 frame_index)
+ enum hdmi_infoframe_type type)
{
struct drm_device *drm = hdmi->connector.dev;

- switch (frame_index) {
- case INFOFRAME_VSI:
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_VENDOR:
return v_PACKET_VSI_EN(1);
- case INFOFRAME_AVI:
+ case HDMI_INFOFRAME_TYPE_AVI:
return 0;
default:
- drm_err(drm, "Unknown infoframe type: %u\n", frame_index);
+ drm_err(drm, "Unknown infoframe type: %u\n", type);
}

return 0;
}

-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)
{
- u32 disable = inno_hdmi_get_frame_disable(hdmi, frame_index);
- u32 mask = inno_hdmi_get_frame_mask(hdmi, frame_index);
+ u32 frame_index = inno_hdmi_get_frame_index(hdmi, type);
+ u32 disable = inno_hdmi_get_frame_disable(hdmi, type);
+ u32 mask = inno_hdmi_get_frame_mask(hdmi, type);

if (mask)
hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
@@ -219,14 +238,14 @@ static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, u32 frame_index)
}

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)
{
- u32 enable = inno_hdmi_get_frame_enable(hdmi, frame_index);
- u32 mask = inno_hdmi_get_frame_mask(hdmi, frame_index);
+ u32 enable = inno_hdmi_get_frame_enable(hdmi, type);
+ u32 mask = inno_hdmi_get_frame_mask(hdmi, type);
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;

- inno_hdmi_disable_frame(hdmi, frame_index);
+ inno_hdmi_disable_frame(hdmi, type);

rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
@@ -253,11 +272,11 @@ static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
&hdmi->connector,
mode);
if (rc) {
- inno_hdmi_disable_frame(hdmi, INFOFRAME_VSI);
+ inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_VENDOR);
return rc;
}

- return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_VSI);
+ return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_VENDOR);
}

static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
@@ -270,13 +289,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.41.0

2023-10-31 16:56:25

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 31/37] drm/rockchip: inno_hdmi: Remove unused drm device pointer

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 ed1d10efbef4..d0b463d166fb 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;
@@ -732,7 +731,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.41.0

2023-10-31 16:56:25

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 20/37] drm/rockchip: inno_hdmi: Remove unneeded has audio flag

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.41.0

2023-10-31 18:16:05

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 32/37] drm/rockchip: inno_hdmi: Switch to HDMI connector

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 | 101 ++++++++++++++---------------------
1 file changed, 41 insertions(+), 60 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index d0b463d166fb..d5661ef5e00b 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -223,9 +223,10 @@ static u32 inno_hdmi_get_frame_enable(struct inno_hdmi *hdmi,
return 0;
}

-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 inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
u32 frame_index = inno_hdmi_get_frame_index(hdmi, type);
u32 disable = inno_hdmi_get_frame_disable(hdmi, type);
u32 mask = inno_hdmi_get_frame_mask(hdmi, type);
@@ -234,24 +235,23 @@ static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);

hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+ 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 inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
u32 enable = inno_hdmi_get_frame_enable(hdmi, type);
u32 mask = inno_hdmi_get_frame_mask(hdmi, type);
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
- ssize_t rc, i;
+ ssize_t i;

- 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]);

@@ -261,41 +261,10 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
return 0;
}

-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);
- if (rc) {
- inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_VENDOR);
- return rc;
- }
-
- return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_VENDOR);
-}
-
-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)
{
@@ -374,9 +343,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,
@@ -390,10 +362,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);
- inno_hdmi_config_video_vsi(hdmi, mode);
- }
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector,
+ new_conn_state->state);

/*
* When IP controller have configured to an accurate video
@@ -401,7 +371,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,
@@ -425,7 +395,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);
}

@@ -491,6 +461,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);
@@ -501,12 +477,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,
};

@@ -533,10 +510,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.41.0

2023-10-31 18:16:27

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH RFC v3 33/37] drm/sun4i: hdmi: Convert encoder to atomic

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.41.0

2023-11-03 09:06:28

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH RFC v3 12/37] drm/connector: hdmi: Create Infoframe DebugFS entries

Hi Maxime,

Thank you for posting v3, this time it runs fine on my RPi 4, thank you for
fixing that.

I'll start working on a conformity checker for this.

I have a few remarks:

On 31/10/2023 17:48, Maxime Ripard wrote:
> 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 2de43ff3ce0a..3c65b1d3f926 100644
> --- a/drivers/gpu/drm/drm_debugfs.c
> +++ b/drivers/gpu/drm/drm_debugfs.c
> @@ -538,6 +538,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, drm);

Hmm, I had to look into the code to figure out that 'drm' stands for
Dynamic Range and Mastering InfoFrame. While entirely correct, it is
also very confusing in the context of the 'drm' subsystem.

I am not quite certain what the best approach is here.

Internally in the drm code it is talking about 'hdr' or 'hdr metadata',
but that's a bit confusing as well since there is also an HDR Dynamic
Metadata Extended InfoFrame defined in CTA-861, even though support for
that is not (yet) implemented in drm.

At minimum there should be a comment in the code explaining what drm
stands for in this context.

One option to consider is renaming this file to hdr_drm, thus indicating
that this is HDR related.

> + if (ret)
> + return ret;
> +
> + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, spd);
> + if (ret)
> + return ret;
> +
> + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, vendor);

There may be multiple vendor specific InfoFrames in the future, so how
would that be handled? Perhaps add a comment here that currently only one
vendor specific InfoFrame is supported, but suggest how to handle multiple
VSIFs in the future.

What would actually be nice (although probably not that easy to fix) is if
the name of the file would be "vendor-XXXXXX' where 'XXXXXX' is the IEEE OUI
number.

Regards,

Hans

> + 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_minor *minor = connector->dev->primary;
> @@ -565,6 +673,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);
> }
>

2023-11-06 14:46:22

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH RFC v3 12/37] drm/connector: hdmi: Create Infoframe DebugFS entries

Hi Hans,

On Fri, Nov 03, 2023 at 10:05:18AM +0100, Hans Verkuil wrote:
> Hi Maxime,
>
> Thank you for posting v3, this time it runs fine on my RPi 4, thank you for
> fixing that.
>
> I'll start working on a conformity checker for this.

Awesome :)

> > +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, drm);
>
> Hmm, I had to look into the code to figure out that 'drm' stands for
> Dynamic Range and Mastering InfoFrame. While entirely correct, it is
> also very confusing in the context of the 'drm' subsystem.
>
> I am not quite certain what the best approach is here.
>
> Internally in the drm code it is talking about 'hdr' or 'hdr metadata',
> but that's a bit confusing as well since there is also an HDR Dynamic
> Metadata Extended InfoFrame defined in CTA-861, even though support for
> that is not (yet) implemented in drm.
>
> At minimum there should be a comment in the code explaining what drm
> stands for in this context.
>
> One option to consider is renaming this file to hdr_drm, thus indicating
> that this is HDR related.

I ended up doing both, thanks for the suggestion

> > + if (ret)
> > + return ret;
> > +
> > + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, spd);
> > + if (ret)
> > + return ret;
> > +
> > + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, vendor);
>
> There may be multiple vendor specific InfoFrames in the future, so how
> would that be handled? Perhaps add a comment here that currently only one
> vendor specific InfoFrame is supported, but suggest how to handle multiple
> VSIFs in the future.
>
> What would actually be nice (although probably not that easy to fix) is if
> the name of the file would be "vendor-XXXXXX' where 'XXXXXX' is the IEEE OUI
> number.

I guess it's not entirely clear to me what that would look like. In
order for the framework to create the debugfs files, we would need some
enumeration mechanism (probably through a callback?), and then the
driver would generate the entire content of that file.

Which makes me question whether the framework should be initialised at
all? Maybe the simpler would be to just have drivers maintain their own
debugfs files and storing the content in their own, private, structure.

Maxime


Attachments:
(No filename) (2.59 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-06 14:56:13

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH RFC v3 12/37] drm/connector: hdmi: Create Infoframe DebugFS entries

On 06/11/2023 15:44, Maxime Ripard wrote:
> Hi Hans,
>
> On Fri, Nov 03, 2023 at 10:05:18AM +0100, Hans Verkuil wrote:
>> Hi Maxime,
>>
>> Thank you for posting v3, this time it runs fine on my RPi 4, thank you for
>> fixing that.
>>
>> I'll start working on a conformity checker for this.
>
> Awesome :)

And anyone who is interested can find the work-in-progress for that here:

git://linuxtv.org/hverkuil/edid-decode.git

Use the -I option to point to the file containing the infoframe to parse
it. Later it will also check the conformity of the infoframe against the
display's EDID. That's why this is part of edid-decode since you need an
EDID parser to verify if the InfoFrames are also conform the standards.

>
>>> +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, drm);
>>
>> Hmm, I had to look into the code to figure out that 'drm' stands for
>> Dynamic Range and Mastering InfoFrame. While entirely correct, it is
>> also very confusing in the context of the 'drm' subsystem.
>>
>> I am not quite certain what the best approach is here.
>>
>> Internally in the drm code it is talking about 'hdr' or 'hdr metadata',
>> but that's a bit confusing as well since there is also an HDR Dynamic
>> Metadata Extended InfoFrame defined in CTA-861, even though support for
>> that is not (yet) implemented in drm.
>>
>> At minimum there should be a comment in the code explaining what drm
>> stands for in this context.
>>
>> One option to consider is renaming this file to hdr_drm, thus indicating
>> that this is HDR related.
>
> I ended up doing both, thanks for the suggestion
>
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, spd);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = CREATE_HDMI_INFOFRAME_FILE(connector, parent, vendor);
>>
>> There may be multiple vendor specific InfoFrames in the future, so how
>> would that be handled? Perhaps add a comment here that currently only one
>> vendor specific InfoFrame is supported, but suggest how to handle multiple
>> VSIFs in the future.
>>
>> What would actually be nice (although probably not that easy to fix) is if
>> the name of the file would be "vendor-XXXXXX' where 'XXXXXX' is the IEEE OUI
>> number.
>
> I guess it's not entirely clear to me what that would look like. In
> order for the framework to create the debugfs files, we would need some
> enumeration mechanism (probably through a callback?), and then the
> driver would generate the entire content of that file.
>
> Which makes me question whether the framework should be initialised at
> all? Maybe the simpler would be to just have drivers maintain their own
> debugfs files and storing the content in their own, private, structure.

It's a good question. And I also would like to use this for HDMI receivers
(so using it in the media subsystem).

Let me try and find some time this week or next week to dig into this a bit
more and see if I can implement it on the media side as well. See what would
be the best approach here.

It would not surprise me if the debugfs part should move to video/hdmi.c
since that's already used by both drm and media.

Regards,

Hans

>
> Maxime

2023-11-27 09:41:45

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH RFC v3 22/37] drm/rockchip: inno_hdmi: Remove useless output format

Hi,

On Sat, Nov 25, 2023 at 11:00:52AM +0100, Johan Jonker wrote:
> In stead of further cripplingRockchip HDMI drivers one could also make
> it functional based? on EDID info.

I'm not crippling it, it was dead code. This has no functional impact
whatsoever.

> To start with the output could you turn RGB888 input and switch between
> RGB444, YCBCR444 and YCBCR422 output.

I don't have any hardware to test that out

Maxime


Attachments:
(No filename) (442.00 B)
signature.asc (235.00 B)
Download all attachments