Add support for configuring Dynamic Range and Mastering InfoFrame from the hdr_output_metadata connector property. This patch adds a drm_infoframe flag to dw_hdmi_plat_data that platform drivers use to signal when Dynamic Range and Mastering infoframes is supported. This flag is needed because Amlogic GXBB and GXL report same DW-HDMI version, and only GXL support DRM InfoFrame. These changes were based on work done by Zheng Yang <zhengyang@xxxxxxxxxxxxxx> to support DRM InfoFrame on the Rockchip 4.4 BSP kernel at [1] and [2] [1] https://github.com/rockchip-linux/kernel/tree/develop-4.4 [2] https://github.com/rockchip-linux/kernel/commit/d1943fde81ff41d7cca87f4a42f03992e90bddd5 Cc: Zheng Yang <zhengyang@xxxxxxxxxxxxxx> Signed-off-by: Jonas Karlman <jonas@xxxxxxxxx> --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 109 ++++++++++++++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 37 ++++++++ include/drm/bridge/dw_hdmi.h | 1 + 3 files changed, 147 insertions(+) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 284ce59be8f8..801bbbd732fd 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -24,6 +24,7 @@ #include <drm/drm_of.h> #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> #include <drm/drm_encoder_slave.h> @@ -1592,6 +1593,78 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, HDMI_FC_DATAUTO0_VSD_MASK); } +#define HDR_LSB(n) ((n) & 0xff) +#define HDR_MSB(n) (((n) & 0xff00) >> 8) + +static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi) +{ + const struct drm_connector_state *conn_state = hdmi->connector.state; + struct hdmi_drm_infoframe frame; + int ret; + + if (hdmi->version < 0x200a || !hdmi->plat_data->drm_infoframe) + return; + + hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE, + HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN); + + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); + if (ret < 0) + return; + + ret = hdmi_drm_infoframe_check(&frame); + if (WARN_ON(ret)) + return; + + hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0); + hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1); + hdmi_writeb(hdmi, frame.eotf, HDMI_FC_DRM_PB0); + hdmi_writeb(hdmi, frame.metadata_type, HDMI_FC_DRM_PB1); + hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[0].x), + HDMI_FC_DRM_PB2); + hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[0].x), + HDMI_FC_DRM_PB3); + hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[0].y), + HDMI_FC_DRM_PB4); + hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[0].y), + HDMI_FC_DRM_PB5); + hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[1].x), + HDMI_FC_DRM_PB6); + hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[1].x), + HDMI_FC_DRM_PB7); + hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[1].y), + HDMI_FC_DRM_PB8); + hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[1].y), + HDMI_FC_DRM_PB9); + hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[2].x), + HDMI_FC_DRM_PB10); + hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[2].x), + HDMI_FC_DRM_PB11); + hdmi_writeb(hdmi, HDR_LSB(frame.display_primaries[2].y), + HDMI_FC_DRM_PB12); + hdmi_writeb(hdmi, HDR_MSB(frame.display_primaries[2].y), + HDMI_FC_DRM_PB13); + hdmi_writeb(hdmi, HDR_LSB(frame.white_point.x), HDMI_FC_DRM_PB14); + hdmi_writeb(hdmi, HDR_MSB(frame.white_point.x), HDMI_FC_DRM_PB15); + hdmi_writeb(hdmi, HDR_LSB(frame.white_point.y), HDMI_FC_DRM_PB16); + hdmi_writeb(hdmi, HDR_MSB(frame.white_point.y), HDMI_FC_DRM_PB17); + hdmi_writeb(hdmi, HDR_LSB(frame.max_display_mastering_luminance), + HDMI_FC_DRM_PB18); + hdmi_writeb(hdmi, HDR_MSB(frame.max_display_mastering_luminance), + HDMI_FC_DRM_PB19); + hdmi_writeb(hdmi, HDR_LSB(frame.min_display_mastering_luminance), + HDMI_FC_DRM_PB20); + hdmi_writeb(hdmi, HDR_MSB(frame.min_display_mastering_luminance), + HDMI_FC_DRM_PB21); + hdmi_writeb(hdmi, HDR_LSB(frame.max_cll), HDMI_FC_DRM_PB22); + hdmi_writeb(hdmi, HDR_MSB(frame.max_cll), HDMI_FC_DRM_PB23); + hdmi_writeb(hdmi, HDR_LSB(frame.max_fall), HDMI_FC_DRM_PB24); + hdmi_writeb(hdmi, HDR_MSB(frame.max_fall), HDMI_FC_DRM_PB25); + hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP); + hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE, + HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN); +} + static void hdmi_av_composer(struct dw_hdmi *hdmi, const struct drm_display_mode *mode) { @@ -1963,6 +2036,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) /* HDMI Initialization Step F - Configure AVI InfoFrame */ hdmi_config_AVI(hdmi, mode); hdmi_config_vendor_specific_infoframe(hdmi, mode); + hdmi_config_drm_infoframe(hdmi); } else { dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); } @@ -2139,6 +2213,36 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) return ret; } +static bool blob_equal(const struct drm_property_blob *a, + const struct drm_property_blob *b) +{ + if (a && b) + return a->length == b->length && + !memcmp(a->data, b->data, a->length); + + return !a == !b; +} + +static int dw_hdmi_connector_atomic_check(struct drm_connector *conn, + struct drm_connector_state *new_state) +{ + struct drm_connector_state *old_state = + drm_atomic_get_old_connector_state(new_state->state, conn); + struct drm_crtc_state *crtc_state; + + if (!new_state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(new_state->state, + new_state->crtc); + + if (!blob_equal(new_state->hdr_output_metadata, + old_state->hdr_output_metadata)) + crtc_state->mode_changed = true; + + return 0; +} + static void dw_hdmi_connector_force(struct drm_connector *connector) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, @@ -2163,6 +2267,7 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { .get_modes = dw_hdmi_connector_get_modes, + .atomic_check = dw_hdmi_connector_atomic_check, }; static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) @@ -2179,6 +2284,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); + if (hdmi->version >= 0x200a && hdmi->plat_data->drm_infoframe) + drm_object_attach_property(&connector->base, + connector->dev->mode_config.hdr_output_metadata_property, 0); + drm_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 3f3c616eba97..d4efbec14f68 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h @@ -256,6 +256,7 @@ #define HDMI_FC_POL2 0x10DB #define HDMI_FC_PRCONF 0x10E0 #define HDMI_FC_SCRAMBLER_CTRL 0x10E1 +#define HDMI_FC_PACKET_TX_EN 0x10E3 #define HDMI_FC_GMD_STAT 0x1100 #define HDMI_FC_GMD_EN 0x1101 @@ -291,6 +292,37 @@ #define HDMI_FC_GMD_PB26 0x111F #define HDMI_FC_GMD_PB27 0x1120 +#define HDMI_FC_DRM_UP 0x1167 +#define HDMI_FC_DRM_HB0 0x1168 +#define HDMI_FC_DRM_HB1 0x1169 +#define HDMI_FC_DRM_PB0 0x116A +#define HDMI_FC_DRM_PB1 0x116B +#define HDMI_FC_DRM_PB2 0x116C +#define HDMI_FC_DRM_PB3 0x116D +#define HDMI_FC_DRM_PB4 0x116E +#define HDMI_FC_DRM_PB5 0x116F +#define HDMI_FC_DRM_PB6 0x1170 +#define HDMI_FC_DRM_PB7 0x1171 +#define HDMI_FC_DRM_PB8 0x1172 +#define HDMI_FC_DRM_PB9 0x1173 +#define HDMI_FC_DRM_PB10 0x1174 +#define HDMI_FC_DRM_PB11 0x1175 +#define HDMI_FC_DRM_PB12 0x1176 +#define HDMI_FC_DRM_PB13 0x1177 +#define HDMI_FC_DRM_PB14 0x1178 +#define HDMI_FC_DRM_PB15 0x1179 +#define HDMI_FC_DRM_PB16 0x117A +#define HDMI_FC_DRM_PB17 0x117B +#define HDMI_FC_DRM_PB18 0x117C +#define HDMI_FC_DRM_PB19 0x117D +#define HDMI_FC_DRM_PB20 0x117E +#define HDMI_FC_DRM_PB21 0x117F +#define HDMI_FC_DRM_PB22 0x1180 +#define HDMI_FC_DRM_PB23 0x1181 +#define HDMI_FC_DRM_PB24 0x1182 +#define HDMI_FC_DRM_PB25 0x1183 +#define HDMI_FC_DRM_PB26 0x1184 + #define HDMI_FC_DBGFORCE 0x1200 #define HDMI_FC_DBGAUD0CH0 0x1201 #define HDMI_FC_DBGAUD1CH0 0x1202 @@ -746,6 +778,11 @@ enum { HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F, HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0, +/* FC_PACKET_TX_EN field values */ + HDMI_FC_PACKET_TX_EN_DRM_MASK = 0x80, + HDMI_FC_PACKET_TX_EN_DRM_ENABLE = 0x80, + HDMI_FC_PACKET_TX_EN_DRM_DISABLE = 0x00, + /* FC_AVICONF0-FC_AVICONF3 field values */ HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03, HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00, diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 0f0e82638fbe..04d1ec60f218 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -131,6 +131,7 @@ struct dw_hdmi_plat_data { unsigned long input_bus_format; unsigned long input_bus_encoding; bool ycbcr_420_allowed; + bool drm_infoframe; /* Vendor PHY support */ const struct dw_hdmi_phy_ops *phy_ops; -- 2.17.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel