From: Dave Stevenson <dave.stevenson@xxxxxxxxxxxxxxx>
Copy Intel's "Broadcast RGB" property semantics to add manual override
of the HDMI pixel range for monitors that don't abide by the content
of the AVI Infoframe.
Signed-off-by: Dave Stevenson <dave.stevenson@xxxxxxxxxxxxxxx>
Signed-off-by: Maxime Ripard <maxime@xxxxxxxxxx>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 87 ++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/vc4/vc4_hdmi.h | 15 ++++++++
2 files changed, 102 insertions(+)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 0eafaf0b76e5..488a4012d422 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -154,6 +154,11 @@ static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi,
{
struct drm_display_info *display = &vc4_hdmi->connector.display_info;
+ if (vc4_hdmi->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_LIMITED)
+ return false;
+ else if (vc4_hdmi->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_FULL)
+ return true;
+
return !display->is_hdmi ||
drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL;
}
@@ -515,8 +520,12 @@ 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)
@@ -539,6 +548,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
}
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)) {
struct drm_crtc_state *crtc_state;
@@ -552,6 +562,49 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
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);
+ 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;
+}
+
static void vc4_hdmi_connector_reset(struct drm_connector *connector)
{
struct vc4_hdmi_connector_state *old_state =
@@ -571,6 +624,7 @@ static void vc4_hdmi_connector_reset(struct drm_connector *connector)
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_tv_margins_reset(connector);
}
@@ -588,6 +642,7 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
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;
@@ -598,6 +653,8 @@ static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
.reset = vc4_hdmi_connector_reset,
.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_get_property = vc4_hdmi_connector_get_property,
+ .atomic_set_property = vc4_hdmi_connector_set_property,
};
static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
@@ -606,6 +663,33 @@ 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 int vc4_hdmi_connector_init(struct drm_device *dev,
struct vc4_hdmi *vc4_hdmi)
{
@@ -652,6 +736,8 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
if (vc4_hdmi->variant->supports_hdr)
drm_connector_attach_hdr_output_metadata_property(connector);
+ vc4_hdmi_attach_broadcast_rgb_property(dev, vc4_hdmi);
+
drm_connector_attach_encoder(connector, encoder);
return 0;
@@ -1690,6 +1776,7 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
mutex_lock(&vc4_hdmi->mutex);
drm_mode_copy(&vc4_hdmi->saved_adjusted_mode,
&crtc_state->adjusted_mode);
+ vc4_hdmi->broadcast_rgb = vc4_state->broadcast_rgb;
vc4_hdmi->output_bpc = vc4_state->output_bpc;
vc4_hdmi->output_format = vc4_state->output_format;
mutex_unlock(&vc4_hdmi->mutex);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index 023ea64ef006..d423f175339f 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -117,6 +117,12 @@ enum vc4_hdmi_output_format {
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;
@@ -129,6 +135,8 @@ 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;
@@ -221,6 +229,12 @@ struct vc4_hdmi {
* for use outside of KMS hooks. Protected by @mutex.
*/
enum vc4_hdmi_output_format output_format;
+
+ /**
+ * @broadcast_rgb: Copy of @vc4_connector_state.broadcast_rgb
+ * for use outside of KMS hooks. Protected by @mutex.
+ */
+ enum vc4_hdmi_broadcast_rgb broadcast_rgb;
};
static inline struct vc4_hdmi *
@@ -241,6 +255,7 @@ struct vc4_hdmi_connector_state {
unsigned long long tmds_char_rate;
unsigned int output_bpc;
enum vc4_hdmi_output_format output_format;
+ int broadcast_rgb;
};
static inline struct vc4_hdmi_connector_state *
Attachment:
OpenPGP_signature
Description: OpenPGP digital signature