[Intel-gfx] [PATCH] drm/i915: Fix hpd handling for pins with two encoders

Lyude Paul lyude at redhat.com
Thu Nov 8 23:35:10 UTC 2018


lgtm

Reviewed-by: Lyude Paul <lyude at redhat.com>

On Thu, 2018-11-08 at 22:04 +0200, Ville Syrjala wrote:
> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
> 
> In my haste to remove irq_port[] I accidentally changed the
> way we deal with hpd pins that are shared by multiple encoders
> (DP and HDMI for pre-DDI platforms). Previously we would only
> handle such pins via ->hpd_pulse(), but now we queue up the
> hotplug work for the HDMI encoder directly. Worse yet, we now
> count each hpd twice and this increment the hpd storm count
> twice as fast. This can lead to spurious storms being detected.
> 
> Go back to the old way of doing things, ie. delegate to
> ->hpd_pulse() for any pin which has an encoder with that hook
> implemented. I don't really like the idea of adding irq_port[]
> back so let's loop through the encoders first to check if we
> have an encoder with ->hpd_pulse() for the pin, and then go
> through all the pins and decided on the correct course of action
> based on the earlier findings.
> 
> I have occasionally toyed with the idea of unifying the pre-DDI
> HDMI and DP encoders into a single encoder as well. Besides the
> hotplug processing it would have the other benefit of preventing
> userspace from trying to enable both encoders at the same time.
> That is simply illegal as they share the same clock/data pins.
> We have some testcases that will attempt that and thus fail on
> many older machines. But for now let's stick to fixing just the
> hotplug code.
> 
> Cc: stable at vger.kernel.org # 4.19+
> Cc: Lyude Paul <lyude at redhat.com>
> Cc: Rodrigo Vivi <rodrigo.vivi at intel.com>
> Fixes: b6ca3eee18ba ("drm/i915: Nuke dev_priv->irq_port[]")
> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
>  drivers/gpu/drm/i915/intel_hotplug.c | 55 +++++++++++++++++++++-------
>  1 file changed, 42 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_hotplug.c
> b/drivers/gpu/drm/i915/intel_hotplug.c
> index 42e61e10f517..e24174d08fed 100644
> --- a/drivers/gpu/drm/i915/intel_hotplug.c
> +++ b/drivers/gpu/drm/i915/intel_hotplug.c
> @@ -414,33 +414,54 @@ void intel_hpd_irq_handler(struct drm_i915_private
> *dev_priv,
>  	struct intel_encoder *encoder;
>  	bool storm_detected = false;
>  	bool queue_dig = false, queue_hp = false;
> +	u32 long_hpd_pulse_mask = 0;
> +	u32 short_hpd_pulse_mask = 0;
> +	enum hpd_pin pin;
>  
>  	if (!pin_mask)
>  		return;
>  
>  	spin_lock(&dev_priv->irq_lock);
> +
> +	/*
> +	 * Determine whether ->hpd_pulse() exists for each pin, and
> +	 * whether we have a short or a long pulse. This is needed
> +	 * as each pin may have up to two encoders (HDMI and DP) and
> +	 * only the one of them (DP) will have ->hpd_pulse().
> +	 */
>  	for_each_intel_encoder(&dev_priv->drm, encoder) {
> -		enum hpd_pin pin = encoder->hpd_pin;
>  		bool has_hpd_pulse = intel_encoder_has_hpd_pulse(encoder);
> -		bool long_hpd = true;
> +		enum port port = encoder->port;
> +		bool long_hpd;
>  
> +		pin = encoder->hpd_pin;
>  		if (!(BIT(pin) & pin_mask))
>  			continue;
>  
> -		if (has_hpd_pulse) {
> -			enum port port = encoder->port;
> +		if (!has_hpd_pulse)
> +			continue;
>  
> -			long_hpd = long_mask & BIT(pin);
> +		long_hpd = long_mask & BIT(pin);
>  
> -			DRM_DEBUG_DRIVER("digital hpd port %c - %s\n",
> port_name(port),
> -					 long_hpd ? "long" : "short");
> -			queue_dig = true;
> -			if (long_hpd)
> -				dev_priv->hotplug.long_port_mask |= (1 <<
> port);
> -			else
> -				dev_priv->hotplug.short_port_mask |= (1 <<
> port);
> +		DRM_DEBUG_DRIVER("digital hpd port %c - %s\n",
> port_name(port),
> +				 long_hpd ? "long" : "short");
> +		queue_dig = true;
>  
> +		if (long_hpd) {
> +			long_hpd_pulse_mask |= BIT(pin);
> +			dev_priv->hotplug.long_port_mask |= BIT(port);
> +		} else {
> +			short_hpd_pulse_mask |= BIT(pin);
> +			dev_priv->hotplug.short_port_mask |= BIT(port);
>  		}
> +	}
> +
> +	/* Now process each pin just once */
> +	for_each_hpd_pin(pin) {
> +		bool long_hpd;
> +
> +		if (!(BIT(pin) & pin_mask))
> +			continue;
>  
>  		if (dev_priv->hotplug.stats[pin].state == HPD_DISABLED) {
>  			/*
> @@ -457,8 +478,16 @@ void intel_hpd_irq_handler(struct drm_i915_private
> *dev_priv,
>  		if (dev_priv->hotplug.stats[pin].state != HPD_ENABLED)
>  			continue;
>  
> -		if (!has_hpd_pulse) {
> +		/*
> +		 * Delegate to ->hpd_pulse() if one of the encoders for this
> +		 * pin has it, otherwise let the hotplug_work deal with this
> +		 * pin directly.
> +		 */
> +		if (((short_hpd_pulse_mask | long_hpd_pulse_mask) & BIT(pin)))
> {
> +			long_hpd = long_hpd_pulse_mask & BIT(pin);
> +		} else {
>  			dev_priv->hotplug.event_bits |= BIT(pin);
> +			long_hpd = true;
>  			queue_hp = true;
>  		}
>  
-- 
Cheers,
	Lyude Paul



More information about the Intel-gfx mailing list