diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 575d207fd550..2558bbcf9885 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1835,6 +1835,105 @@ hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock) return tmdsclock; } +#define HDR_LSB(n) ((n) & 0xff) +#define HDR_MSB(n) (((n) & 0xff00) >> 8) + +/* Set Dynamic Range and Mastering Infoframe */ +static void hdmi_config_hdr_infoframe(struct dw_hdmi *hdmi) +{ + struct hdmi_drm_infoframe frame; + struct hdr_static_metadata *hdr_metadata; + struct drm_connector_state *conn_state = hdmi->connector.state; + int ret; + + /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */ + if (hdmi->version < 0x211a) { + DRM_ERROR("Not support DRM Infoframe\n"); + return; + } + + hdmi_modb(hdmi, HDMI_FC_PACKET_DRM_TX_DEN, + HDMI_FC_PACKET_DRM_TX_EN_MASK, HDMI_FC_PACKET_TX_EN); + + if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) { + DRM_DEBUG("No need to set HDR metadata in infoframe\n"); + return; + } + + if (!conn_state->hdr_output_metadata) { + DRM_DEBUG("source metadata not set yet\n"); + return; + } + + hdr_metadata = (struct hdr_static_metadata *) + conn_state->hdr_output_metadata->data; + + if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf & + BIT(hdr_metadata->eotf))) { + DRM_ERROR("Not support EOTF %d\n", hdr_metadata->eotf); + return; + } + + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); + if (ret < 0) { + DRM_ERROR("couldn't set HDR metadata in infoframe\n"); + return; + } + + hdmi_writeb(hdmi, 1, 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_DRM_TX_EN, + HDMI_FC_PACKET_DRM_TX_EN_MASK, HDMI_FC_PACKET_TX_EN); + + if (conn_state->hdr_metadata_changed) + conn_state->hdr_metadata_changed = false; + + DRM_DEBUG("%s eotf %d end\n", __func__, hdr_metadata->eotf); +} + static void hdmi_av_composer(struct dw_hdmi *hdmi, const struct drm_display_mode *mode) { @@ -2201,6 +2300,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_hdr_infoframe(hdmi); } else { dev_dbg(hdmi->dev, "%s DVI mode\n", __func__); } @@ -2493,6 +2593,9 @@ dw_hdmi_connector_atomic_flush(struct drm_connector *connector, dw_hdmi_setup(hdmi, &hdmi->previous_mode); hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); hdmi->hdmi_data.update = false; + } else if (connector->state->hdr_metadata_changed && + hdmi->sink_is_hdmi) { + hdmi_config_hdr_infoframe(hdmi); } } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 6acb401ed2db..6655f253ae14 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 @@ -792,6 +824,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_DRM_TX_EN_MASK = 0x80, + HDMI_FC_PACKET_DRM_TX_EN = 0x80, + HDMI_FC_PACKET_DRM_TX_DEN = 0x00, + /* FC_AVICONF0-FC_AVICONF3 field values */ HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x03, HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00, diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index f64453298c94..93aa30671ea7 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1406,6 +1406,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, val, sizeof(struct hdr_output_metadata), -1, &replaced); + state->hdr_metadata_changed |= replaced; return ret; } else if (property == config->aspect_ratio_property) { state->picture_aspect_ratio = val; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 91050f4990e5..ab99fecc69dd 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3823,6 +3823,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, if (state->hdr_output_metadata) drm_property_blob_get(state->hdr_output_metadata); + state->hdr_metadata_changed = false; /* Don't copy over a writeback job, they are used only once */ state->writeback_job = NULL; } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 78c6768c4022..5afa36d26ed2 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -468,6 +468,8 @@ struct drm_connector_state { struct drm_property_blob *hdr_output_metadata; struct drm_property_blob *hdr_panel_blob_ptr; + + bool hdr_metadata_changed : 1; }; /**