diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 1b295949122b..e8b3e85603e4 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -1961,6 +1961,30 @@ enable_packet_control: hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control); } +static void sde_hdmi_clear_hdr_infoframe(struct sde_hdmi *display) +{ + struct hdmi *hdmi; + struct drm_connector *connector; + u32 packet_control = 0; + + if (!display) { + SDE_ERROR("invalid input\n"); + return; + } + + hdmi = display->ctrl.ctrl; + connector = display->ctrl.ctrl->connector; + + if (!hdmi || !connector) { + SDE_ERROR("invalid input\n"); + return; + } + + packet_control = hdmi_read(hdmi, HDMI_GEN_PKT_CTRL); + packet_control &= ~HDMI_GEN_PKT_CTRL_CLR_MASK; + hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control); +} + int sde_hdmi_set_property(struct drm_connector *connector, struct drm_connector_state *state, int property_index, @@ -2305,15 +2329,28 @@ int sde_hdmi_pre_kickoff(struct drm_connector *connector, void *display, struct msm_display_kickoff_params *params) { + struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; + u8 hdr_op; if (!connector || !display || !params) { pr_err("Invalid params\n"); return -EINVAL; } - if (connector->hdr_supported) - sde_hdmi_panel_set_hdr_infoframe(display, - params->hdr_metadata); + hdr_ctrl = params->hdr_ctrl; + + hdr_op = sde_hdmi_hdr_get_ops(hdmi_display->curr_hdr_state, + hdr_ctrl->hdr_state); + + if (hdr_op == HDR_SEND_INFO) { + if (connector->hdr_supported) + sde_hdmi_panel_set_hdr_infoframe(display, + &hdr_ctrl->hdr_meta); + } else if (hdr_op == HDR_CLEAR_INFO) + sde_hdmi_clear_hdr_infoframe(display); + + hdmi_display->curr_hdr_state = hdr_ctrl->hdr_state; return 0; } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 743d34d05e57..bafb2b949a6b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -147,6 +147,7 @@ struct sde_hdmi { u32 hdcp22_present; u8 hdcp_status; u32 enc_lvl; + u8 curr_hdr_state; bool auth_state; bool sink_hdcp22_support; bool src_hdcp22_support; @@ -198,6 +199,8 @@ enum hdmi_tx_scdc_access_type { #define HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2 #define HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1 +#define HDMI_GEN_PKT_CTRL_CLR_MASK 0x7 + /* Maximum pixel clock rates for hdmi tx */ #define HDMI_DEFAULT_MAX_PCLK_RATE 148500 #define HDMI_TX_3_MAX_PCLK_RATE 297000 diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index a4c3c2e7ce46..62dd3aaf7078 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -518,12 +518,17 @@ static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct sde_connector *c_conn = to_sde_connector(hdmi->connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; /* need to update hdcp info here to ensure right HDCP support*/ sde_hdmi_update_hdcp_info(hdmi->connector); /* start HDCP authentication */ sde_hdmi_start_hdcp(hdmi->connector); + + /* reset HDR state */ + display->curr_hdr_state = HDR_DISABLE; } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c index ba47b7702efd..a291a1112aeb 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c @@ -68,6 +68,15 @@ static void sde_hdmi_hdcp2p2_ddc_clear_status(struct sde_hdmi *display) hdmi_write(hdmi, HDMI_HDCP2P2_DDC_STATUS, reg_val); } +static const char *sde_hdmi_hdr_sname(enum sde_hdmi_hdr_state hdr_state) +{ + switch (hdr_state) { + case HDR_DISABLE: return "HDR_DISABLE"; + case HDR_ENABLE: return "HDR_ENABLE"; + default: return "HDR_INVALID_STATE"; + } +} + /** * sde_hdmi_dump_regs - utility to dump HDMI regs * @hdmi_display: Pointer to private display handle @@ -898,3 +907,50 @@ int sde_hdmi_sink_dc_support(struct drm_connector *connector, return dc_format; } + +u8 sde_hdmi_hdr_get_ops(u8 curr_state, + u8 new_state) +{ + + /** There could be 3 valid state transitions: + * 1. HDR_DISABLE -> HDR_ENABLE + * + * In this transition, we shall start sending + * HDR metadata with metadata from the HDR clip + * + * 2. HDR_ENABLE -> HDR_ENABLE + * + * In this transition, we will keep sending + * HDR metadata but with EOTF and metadata as 0 + * + * 3. HDR_ENABLE -> HDR_DISABLE + * + * In this transition, we will stop sending + * metadata to the sink and clear PKT_CTRL register + * bits. + */ + + if ((curr_state == HDR_DISABLE) + && (new_state == HDR_ENABLE)) { + HDMI_UTIL_DEBUG("State changed %s ---> %s\n", + sde_hdmi_hdr_sname(curr_state), + sde_hdmi_hdr_sname(new_state)); + return HDR_SEND_INFO; + } else if ((curr_state == HDR_ENABLE) + && (new_state == HDR_ENABLE)) { + HDMI_UTIL_DEBUG("State changed %s ---> %s\n", + sde_hdmi_hdr_sname(curr_state), + sde_hdmi_hdr_sname(new_state)); + return HDR_SEND_INFO; + } else if ((curr_state == HDR_ENABLE) + && (new_state == HDR_DISABLE)) { + HDMI_UTIL_DEBUG("State changed %s ---> %s\n", + sde_hdmi_hdr_sname(curr_state), + sde_hdmi_hdr_sname(new_state)); + return HDR_CLEAR_INFO; + } + + HDMI_UTIL_DEBUG("Unsupported OR no state change\n"); + return HDR_UNSUPPORTED_OP; +} + diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h index 10effed54a14..1d89ae222a7b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h @@ -125,6 +125,17 @@ enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask { RXSTATUS_REAUTH_REQ = BIT(14), }; +enum sde_hdmi_hdr_state { + HDR_DISABLE, + HDR_ENABLE +}; + +enum sde_hdmi_hdr_op { + HDR_UNSUPPORTED_OP, + HDR_SEND_INFO, + HDR_CLEAR_INFO +}; + struct sde_hdmi_tx_hdcp2p2_ddc_data { enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask; u32 timeout_ms; @@ -170,4 +181,6 @@ bool sde_hdmi_validate_pixclk(struct drm_connector *connector, unsigned long pclk); int sde_hdmi_sink_dc_support(struct drm_connector *connector, struct drm_display_mode *mode); +u8 sde_hdmi_hdr_get_ops(u8 curr_state, + u8 new_state); #endif /* _SDE_HDMI_UTIL_H_ */ diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index ac15f399df7d..08868fce1cb0 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -144,7 +144,7 @@ enum msm_mdp_conn_property { /* blob properties, always put these first */ CONNECTOR_PROP_SDE_INFO, CONNECTOR_PROP_HDR_INFO, - CONNECTOR_PROP_HDR_METADATA, + CONNECTOR_PROP_HDR_CONTROL, /* # of blob properties */ CONNECTOR_PROP_BLOBCOUNT, @@ -237,10 +237,10 @@ struct msm_display_info { /** * struct - msm_display_kickoff_params - info for display features at kickoff - * @hdr_metadata: HDR metadata info passed from userspace + * @hdr_ctrl: HDR control info passed from userspace */ struct msm_display_kickoff_params { - struct drm_msm_ext_panel_hdr_metadata *hdr_metadata; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; }; /** diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 76e6e4ef6e7d..875513d2840f 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -83,7 +83,7 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) if (!c_conn->ops.pre_kickoff) return 0; - params.hdr_metadata = &c_state->hdr_meta; + params.hdr_ctrl = &c_state->hdr_ctrl; rc = c_conn->ops.pre_kickoff(connector, c_conn->display, ¶ms); @@ -247,6 +247,7 @@ static int _sde_connector_set_hdr_info( void *usr_ptr) { struct drm_connector *connector; + struct drm_msm_ext_panel_hdr_ctrl *hdr_ctrl; struct drm_msm_ext_panel_hdr_metadata *hdr_meta; int i; @@ -262,21 +263,26 @@ static int _sde_connector_set_hdr_info( return -ENOTSUPP; } - memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta)); + memset(&c_state->hdr_ctrl, 0, sizeof(c_state->hdr_ctrl)); if (!usr_ptr) { - SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n"); + SDE_DEBUG_CONN(c_conn, "hdr control cleared\n"); return 0; } - if (copy_from_user(&c_state->hdr_meta, + if (copy_from_user(&c_state->hdr_ctrl, (void __user *)usr_ptr, - sizeof(*hdr_meta))) { - SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n"); + sizeof(*hdr_ctrl))) { + SDE_ERROR_CONN(c_conn, "failed to copy hdr control\n"); return -EFAULT; } - hdr_meta = &c_state->hdr_meta; + hdr_ctrl = &c_state->hdr_ctrl; + + SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n", + hdr_ctrl->hdr_state); + + hdr_meta = &hdr_ctrl->hdr_meta; SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n", hdr_meta->hdr_supported); @@ -362,7 +368,7 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, SDE_ERROR("invalid topology_control: 0x%llX\n", val); } - if (idx == CONNECTOR_PROP_HDR_METADATA) { + if (idx == CONNECTOR_PROP_HDR_CONTROL) { rc = _sde_connector_set_hdr_info(c_conn, c_state, (void *)val); if (rc) SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc); @@ -718,8 +724,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, } msm_property_install_volatile_range(&c_conn->property_info, - "hdr_metadata", 0x0, 0, ~0, 0, - CONNECTOR_PROP_HDR_METADATA); + "hdr_control", 0x0, 0, ~0, 0, + CONNECTOR_PROP_HDR_CONTROL); msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", 0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index 19e2b8a3e41c..8257f29bd4b8 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -221,14 +221,14 @@ struct sde_connector { * @out_fb: Pointer to output frame buffer, if applicable * @aspace: Address space for accessing frame buffer objects, if applicable * @property_values: Local cache of current connector property values - * @hdr_meta: HDR metadata info passed from userspace + * @hdr_ctrl: HDR control info passed from userspace */ struct sde_connector_state { struct drm_connector_state base; struct drm_framebuffer *out_fb; struct msm_gem_address_space *aspace; uint64_t property_values[CONNECTOR_PROP_COUNT]; - struct drm_msm_ext_panel_hdr_metadata hdr_meta; + struct drm_msm_ext_panel_hdr_ctrl hdr_ctrl; }; /** diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 927c3626edb7..8b51873e7b08 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -83,6 +83,21 @@ struct drm_msm_ext_panel_hdr_metadata { __u32 max_average_light_level; /* max average light level */ }; +/** + * HDR Control + * This encapsulates the HDR metadata as well as a state control + * for the HDR metadata as required by the HDMI spec to send the + * relevant metadata depending on the state of the HDR playback. + * hdr_state: Controls HDR state, takes values ENABLE(1)/DISABLE(0) + * hdr_meta: Metadata sent by the userspace for the HDR clip + */ + +#define DRM_MSM_EXT_PANEL_HDR_CTRL +struct drm_msm_ext_panel_hdr_ctrl { + __u8 hdr_state; /* HDR state */ + struct drm_msm_ext_panel_hdr_metadata hdr_meta; /* HDR metadata */ +}; + /** * HDR sink properties * These are defined as per EDID spec and shall be used by the userspace