Setup the HDMI connector on the MSM HDMI outputs. Make use of atomic_check hook and of the provided Infoframe infrastructure. Note: for now only AVI Infoframes are enabled. Audio Infoframes are currenly handled separately. This will be fixed for the final version. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> --- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 107 ++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index d839c71091dc..ac42574db8f7 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -68,23 +68,15 @@ static void power_off(struct drm_bridge *bridge) #define AVI_IFRAME_LINE_NUMBER 1 -static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) +static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi, + const u8 *buffer, size_t len) { - struct drm_crtc *crtc = hdmi->encoder->crtc; - const struct drm_display_mode *mode = &crtc->state->adjusted_mode; - union hdmi_infoframe frame; - u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; u32 val; - int len; - drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, - hdmi->connector, mode); - - len = hdmi_infoframe_pack(&frame, buffer, sizeof(buffer)); - if (len < 0) { + if (len != HDMI_INFOFRAME_SIZE(AVI)) { DRM_DEV_ERROR(&hdmi->pdev->dev, "failed to configure avi infoframe\n"); - return; + return -EINVAL; } /* @@ -124,6 +116,55 @@ static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK; val |= HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER); hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); + + return 0; +} + +static int msm_hdmi_bridge_clear_infoframe(struct drm_connector *connector, + enum hdmi_infoframe_type type) +{ + struct hdmi_bridge *hdmi_bridge = connector->hdmi.data; + struct hdmi *hdmi = hdmi_bridge->hdmi; + u32 val; + + val = hdmi_read(REG_HDMI_INFOFRAME_CTRL0); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + val &= ~(HDMI_INFOFRAME_CTRL0_AVI_SEND | + HDMI_INFOFRAME_CTRL0_AVI_CONT); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + val &= ~(HDMI_INFOFRAME_CTRL0_AUDIO_SEND | + HDMI_INFOFRAME_CTRL0_AUDIO_CONT | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE); + break; + default: + drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type); + } + + hdmi_write(REG_HDMI_INFOFRAME_CTRL0, val); + + return 0; +} + +static int msm_hdmi_bridge_write_infoframe(struct drm_connector *connector, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) +{ + struct hdmi_bridge *hdmi_bridge = connector->hdmi.data; + struct hdmi *hdmi = hdmi_bridge->hdmi; + + msm_hdmi_bridge_clear_infoframe(connector, type); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + return msm_hdmi_config_avi_infoframe(hdmi, buffer, len); + default: + drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type); + return 0; + } } static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, @@ -132,6 +173,10 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; struct hdmi_phy *phy = hdmi->phy; + struct drm_encoder *encoder = bridge->encoder; + struct drm_atomic_state *state = old_bridge_state->base.state; + struct drm_connector *connector = + drm_atomic_get_new_connector_for_encoder(state, encoder); DBG("power up"); @@ -139,12 +184,13 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, msm_hdmi_phy_resource_enable(phy); msm_hdmi_power_on(bridge); hdmi->power_on = true; - if (hdmi->hdmi_mode) { - msm_hdmi_config_avi_infoframe(hdmi); - msm_hdmi_audio_update(hdmi); - } } + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); + + if (hdmi->hdmi_mode) + msm_hdmi_audio_update(hdmi); + msm_hdmi_phy_powerup(phy, hdmi->pixclock); msm_hdmi_set_mode(hdmi, true); @@ -177,6 +223,15 @@ static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge, } } +static int msm_hdmi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return drm_atomic_helper_connector_hdmi_check(conn_state->connector, + conn_state->state); +} + static void msm_hdmi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) @@ -300,16 +355,36 @@ static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge return 0; } +static const struct drm_connector_hdmi_funcs msm_hdmi_bridge_hdmi_funcs = { + .clear_infoframe = msm_hdmi_bridge_clear_infoframe, + .write_infoframe = msm_hdmi_bridge_write_infoframe, +}; + +static int msm_hdmi_bridge_setup_connector(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + if (connector->hdmi.data) + return -EBUSY; + + connector->hdmi.data = to_hdmi_bridge(bridge); + + return drm_connector_hdmi_setup(connector, "Qualcomm", "Snapdragon", + &msm_hdmi_bridge_hdmi_funcs, + BIT(HDMI_COLORSPACE_RGB), 8); +} + static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_check = msm_hdmi_bridge_atomic_check, .atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable, .atomic_post_disable = msm_hdmi_bridge_atomic_post_disable, .mode_set = msm_hdmi_bridge_mode_set, .mode_valid = msm_hdmi_bridge_mode_valid, .edid_read = msm_hdmi_bridge_edid_read, .detect = msm_hdmi_bridge_detect, + .setup_connector = msm_hdmi_bridge_setup_connector, }; static void -- 2.39.2