On Wed, 2018-01-17 at 21:21 +0200, Ville Syrjala wrote:
> From: Ville Syrjälä <ville.syrj...@linux.intel.com>
> 
> The LG 4k TV I have doesn't deassert HPD when I turn the TV off, but
> when I turn it back on it will pulse the HPD line. By that time it has
> forgotten everything we told it about scrambling and the clock ratio.
> Hence if we want to get a picture out if it again we have to tell it
> whether we're currently sending scrambled data or not. Implement
> that via the encoder->hotplug() hook.
> 
> v2: Force a full modeset to not follow the HDMI 2.0 spec more
>     closely (Shashank)
> 
> Cc: Shashank Sharma <shashank.sha...@intel.com>
> Cc: Maarten Lankhorst <maarten.lankho...@linux.intel.com>
> Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/intel_ddi.c | 146
> ++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 145 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c
> b/drivers/gpu/drm/i915/intel_ddi.c
> index 1aeae3e97013..25793bdc692f 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -25,6 +25,7 @@
>   *
>   */
>  
> +#include <drm/drm_scdc_helper.h>
>  #include "i915_drv.h"
>  #include "intel_drv.h"
>  
> @@ -2756,6 +2757,146 @@ intel_ddi_init_dp_connector(struct intel_digital_port
> *intel_dig_port)
>       return connector;
>  }
>  
> +static int modeset_pipe(struct drm_crtc *crtc,
> +                     struct drm_modeset_acquire_ctx *ctx)
> +{
> +     struct drm_atomic_state *state;
> +     struct drm_crtc_state *crtc_state;
> +     int ret;
> +
> +     state = drm_atomic_state_alloc(crtc->dev);
> +     if (!state)
> +             return -ENOMEM;
> +
> +     state->acquire_ctx = ctx;
> +
> +     crtc_state = drm_atomic_get_crtc_state(state, crtc);
> +     if (IS_ERR(crtc_state)) {
> +             ret = PTR_ERR(crtc_state);
> +             goto out;
> +     }
> +
> +     crtc_state->mode_changed = true;
> +
> +     ret = drm_atomic_add_affected_connectors(state, crtc);
> +     if (ret)
> +             goto out;
> +
> +     ret = drm_atomic_add_affected_planes(state, crtc);
> +     if (ret)
> +             goto out;
> +
> +     ret = drm_atomic_commit(state);
> +     if (ret)
> +             goto out;
> +
> +     return 0;
> +
> + out:
> +     drm_atomic_state_put(state);
> +
> +     return ret;
> +}
> +
> +static int intel_hdmi_reset_link(struct intel_encoder *encoder,
> +                              struct drm_modeset_acquire_ctx *ctx)
> +{
> +     struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
> +     struct intel_hdmi *hdmi = enc_to_intel_hdmi(&encoder->base);
> +     struct intel_connector *connector = hdmi->attached_connector;
> +     struct i2c_adapter *adapter =
> +             intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus);
> +     struct drm_connector_state *conn_state;
> +     struct intel_crtc_state *crtc_state;
> +     struct intel_crtc *crtc;
> +     u8 config;
> +     int ret;
> +
> +     if (!connector || connector->base.status !=
> connector_status_connected)
> +             return 0;
> +
> +     ret = drm_modeset_lock(&dev_priv->drm.mode_config.connection_mutex,
> ctx);
> +     if (ret)
> +             return ret;
> +
> +     conn_state = connector->base.state;
> +
> +     crtc = to_intel_crtc(conn_state->crtc);
> +     if (!crtc)
> +             return 0;
> +
> +     ret = drm_modeset_lock(&crtc->base.mutex, ctx);
> +     if (ret)
> +             return ret;
> +
> +     crtc_state = to_intel_crtc_state(crtc->base.state);
> +
> +     WARN_ON(!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI));
> +
> +     if (!crtc_state->base.active)
> +             return 0;
> +
> +     if (!crtc_state->hdmi_high_tmds_clock_ratio &&
> +         !crtc_state->hdmi_scrambling)
> +             return 0;
> +
> +     if (conn_state->commit &&
> +         !try_wait_for_completion(&conn_state->commit->hw_done))
> +             return 0;
Probably should make a helper function for this since we use this in both DP and
HDMI codepaths

> +
> +     ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
> +     if (ret < 0) {
> +             DRM_ERROR("Failed to read TMDS config: %d\n", ret);
> +             return 0;
> +     }
> +
> +     if (!!(config & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40) ==
> +         crtc_state->hdmi_high_tmds_clock_ratio &&
> +         !!(config & SCDC_SCRAMBLING_ENABLE) ==
> +         crtc_state->hdmi_scrambling)
> +             return 0;
> +
> +     /*
> +      * HDMI 2.0 says that one should not send scrambled data
> +      * prior to configuring the sink scrambling, and that
> +      * TMDS clock/data transmission should be suspended when
> +      * changing the TMDS clock rate in the sink. So let's
> +      * just do a full modeset here, even though some sinks
> +      * would be perfectly happy if were to just reconfigure
> +      * the SCDC settings on the fly.
> +      */
> +     return modeset_pipe(&crtc->base, ctx);
> +}
> +
> +static bool intel_ddi_hotplug(struct intel_encoder *encoder,
> +                           struct intel_connector *connector)
> +{
> +     struct drm_modeset_acquire_ctx ctx;
> +     bool changed;
> +     int ret;
> +
> +     changed = intel_encoder_hotplug(encoder, connector);
> +
> +     drm_modeset_acquire_init(&ctx, 0);
> +
> +     for (;;) {
> +             ret = intel_hdmi_reset_link(encoder, &ctx);
> +
> +             if (ret == -EDEADLK) {
> +                     drm_modeset_backoff(&ctx);
> +                     continue;
> +             }
> +
> +             break;
> +     }
> +
> +     drm_modeset_drop_locks(&ctx);
> +     drm_modeset_acquire_fini(&ctx);
> +     WARN(ret, "Acquiring modeset locks failed with %i\n", ret);
> +
> +     return changed;
> +}
> +
>  static struct intel_connector *
>  intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port)
>  {
> @@ -2866,7 +3007,10 @@ void intel_ddi_init(struct drm_i915_private *dev_priv,
> enum port port)
>       drm_encoder_init(&dev_priv->drm, encoder, &intel_ddi_funcs,
>                        DRM_MODE_ENCODER_TMDS, "DDI %c", port_name(port));
>  
> -     intel_encoder->hotplug = intel_encoder_hotplug;
> +     if (init_hdmi)
> +             intel_encoder->hotplug = intel_ddi_hotplug;
> +     else
> +             intel_encoder->hotplug = intel_encoder_hotplug;
>       intel_encoder->compute_output_type = intel_ddi_compute_output_type;
>       intel_encoder->compute_config = intel_ddi_compute_config;
>       intel_encoder->enable = intel_enable_ddi;
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to