From d2e04763196a52c5136adf9793cf4b5a8530076b Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Wed, 11 Oct 2017 18:09:13 +0800 Subject: [PATCH] drm: bridge: dw-hdmi: Implement connector atomic_flush Introduce dw_hdmi_connector_atomic_flush to implement connector atomic_flush. Only when enc_in_encoding/enc_out_encoding/enc_in_bus_format/ enc_out_bus_format changed, dw_hdmi_setup is called. Introduce previous_pixelclock/previous_tmdsclock/mtmdsclock to determine whether PHY needs initialization. If phy is power off, or mpixelclock/mtmdsclock is different to previous value, phy is neet to be reinitialized. Change-Id: I1984fb188ba486de18f6d51b7a51320bbf2bc27d Signed-off-by: Zheng Yang --- drivers/gpu/drm/bridge/dw-hdmi.c | 81 +++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 9ce47096cbd2..8c0394f8b250 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -147,10 +147,11 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = { struct hdmi_vmode { bool mdataenablepolarity; + unsigned int previous_pixelclock; unsigned int mpixelclock; unsigned int mpixelrepetitioninput; unsigned int mpixelrepetitionoutput; - + unsigned int previous_tmdsclock; unsigned int mtmdsclock; }; @@ -1806,6 +1807,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; unsigned int hdisplay, vdisplay; + vmode->previous_pixelclock = vmode->mpixelclock; vmode->mpixelclock = mode->crtc_clock * 1000; if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) vmode->mpixelclock /= 2; @@ -1813,7 +1815,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, DRM_MODE_FLAG_3D_FRAME_PACKING) vmode->mpixelclock *= 2; dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); - + vmode->previous_tmdsclock = vmode->mtmdsclock; vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock); dev_dbg(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock); @@ -2118,11 +2120,17 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) hdmi_av_composer(hdmi, mode); /* HDMI Initializateion Step B.2 */ - ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); - if (ret) - return ret; - hdmi->phy.enabled = true; - + if (!hdmi->phy.enabled || + hdmi->hdmi_data.video_mode.previous_pixelclock != + hdmi->hdmi_data.video_mode.mpixelclock || + hdmi->hdmi_data.video_mode.previous_tmdsclock != + hdmi->hdmi_data.video_mode.mtmdsclock) { + ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, + &hdmi->previous_mode); + if (ret) + return ret; + hdmi->phy.enabled = true; + } /* HDMI Initialization Step B.3 */ dw_hdmi_enable_video_path(hdmi); @@ -2405,6 +2413,64 @@ static void dw_hdmi_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } +static void +dw_hdmi_connector_atomic_flush(struct drm_connector *connector, + struct drm_connector_state *conn_state) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, + connector); + void *data = hdmi->plat_data->phy_data; + struct drm_display_mode *mode = NULL; + unsigned int enc_in_bus_format; + unsigned int enc_out_bus_format; + unsigned int enc_in_encoding; + unsigned int enc_out_encoding; + + if (!hdmi->hpd_state || !conn_state->crtc) + return; + + DRM_DEBUG("%s\n", __func__); + + /* + * If HDMI is enabled in uboot, it's need to record + * drm_display_mode and set phy status to enabled. + */ + if (!hdmi->hdmi_data.video_mode.mpixelclock) { + mode = &conn_state->crtc->mode; + memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); + hdmi->hdmi_data.video_mode.mpixelclock = mode->clock; + hdmi->hdmi_data.video_mode.previous_pixelclock = mode->clock; + hdmi->hdmi_data.video_mode.previous_tmdsclock = mode->clock; + hdmi->phy.enabled = true; + return; + } + + if (hdmi->plat_data->get_enc_in_encoding) + enc_in_encoding = hdmi->plat_data->get_enc_in_encoding(data); + else + enc_in_encoding = hdmi->hdmi_data.enc_in_encoding; + if (hdmi->plat_data->get_enc_out_encoding) + enc_out_encoding = hdmi->plat_data->get_enc_out_encoding(data); + else + enc_out_encoding = hdmi->hdmi_data.enc_out_encoding; + if (hdmi->plat_data->get_input_bus_format) + enc_in_bus_format = + hdmi->plat_data->get_input_bus_format(data); + else + enc_in_bus_format = hdmi->hdmi_data.enc_in_bus_format; + if (hdmi->plat_data->get_output_bus_format) + enc_out_bus_format = + hdmi->plat_data->get_output_bus_format(data); + else + enc_out_bus_format = hdmi->hdmi_data.enc_out_bus_format; + + if (enc_in_encoding != hdmi->hdmi_data.enc_in_encoding || + enc_out_encoding != hdmi->hdmi_data.enc_out_encoding || + enc_in_bus_format != hdmi->hdmi_data.enc_in_bus_format || + enc_out_bus_format != hdmi->hdmi_data.enc_out_bus_format) + dw_hdmi_setup(hdmi, &hdmi->previous_mode); +} + static int dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, @@ -2495,6 +2561,7 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = .get_modes = dw_hdmi_connector_get_modes, .mode_valid = dw_hdmi_connector_mode_valid, .best_encoder = dw_hdmi_connector_best_encoder, + .atomic_flush = dw_hdmi_connector_atomic_flush, }; static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {