diff options
Diffstat (limited to 'drivers/gpu/drm/drm_probe_helper.c')
| -rw-r--r-- | drivers/gpu/drm/drm_probe_helper.c | 119 | 
1 files changed, 88 insertions, 31 deletions
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 5606bca3caa8..61d5c57f23e1 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -795,6 +795,86 @@ void drm_kms_helper_poll_fini(struct drm_device *dev)  }  EXPORT_SYMBOL(drm_kms_helper_poll_fini); +static bool check_connector_changed(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	enum drm_connector_status old_status; +	u64 old_epoch_counter; + +	/* Only handle HPD capable connectors. */ +	drm_WARN_ON(dev, !(connector->polled & DRM_CONNECTOR_POLL_HPD)); + +	drm_WARN_ON(dev, !mutex_is_locked(&dev->mode_config.mutex)); + +	old_status = connector->status; +	old_epoch_counter = connector->epoch_counter; +	connector->status = drm_helper_probe_detect(connector, NULL, false); + +	if (old_epoch_counter == connector->epoch_counter) { +		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Same epoch counter %llu\n", +			    connector->base.id, +			    connector->name, +			    connector->epoch_counter); + +		return false; +	} + +	drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s\n", +		    connector->base.id, +		    connector->name, +		    drm_get_connector_status_name(old_status), +		    drm_get_connector_status_name(connector->status)); + +	drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Changed epoch counter %llu => %llu\n", +		    connector->base.id, +		    connector->name, +		    old_epoch_counter, +		    connector->epoch_counter); + +	return true; +} + +/** + * drm_connector_helper_hpd_irq_event - hotplug processing + * @connector: drm_connector + * + * Drivers can use this helper function to run a detect cycle on a connector + * which has the DRM_CONNECTOR_POLL_HPD flag set in its &polled member. + * + * This helper function is useful for drivers which can track hotplug + * interrupts for a single connector. Drivers that want to send a + * hotplug event for all connectors or can't track hotplug interrupts + * per connector need to use drm_helper_hpd_irq_event(). + * + * This function must be called from process context with no mode + * setting locks held. + * + * Note that a connector can be both polled and probed from the hotplug + * handler, in case the hotplug interrupt is known to be unreliable. + * + * Returns: + * A boolean indicating whether the connector status changed or not + */ +bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	bool changed; + +	mutex_lock(&dev->mode_config.mutex); +	changed = check_connector_changed(connector); +	mutex_unlock(&dev->mode_config.mutex); + +	if (changed) { +		drm_kms_helper_hotplug_event(dev); +		drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Sent hotplug event\n", +			    connector->base.id, +			    connector->name); +	} + +	return changed; +} +EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event); +  /**   * drm_helper_hpd_irq_event - hotplug processing   * @dev: drm_device @@ -808,23 +888,25 @@ EXPORT_SYMBOL(drm_kms_helper_poll_fini);   * interrupts for each connector.   *   * Drivers which support hotplug interrupts for each connector individually and - * which have a more fine-grained detect logic should bypass this code and - * directly call drm_kms_helper_hotplug_event() in case the connector state - * changed. + * which have a more fine-grained detect logic can use + * drm_connector_helper_hpd_irq_event(). Alternatively, they should bypass this + * code and directly call drm_kms_helper_hotplug_event() in case the connector + * state changed.   *   * This function must be called from process context with no mode   * setting locks held.   *   * Note that a connector can be both polled and probed from the hotplug handler,   * in case the hotplug interrupt is known to be unreliable. + * + * Returns: + * A boolean indicating whether the connector status changed or not   */  bool drm_helper_hpd_irq_event(struct drm_device *dev)  {  	struct drm_connector *connector;  	struct drm_connector_list_iter conn_iter; -	enum drm_connector_status old_status;  	bool changed = false; -	u64 old_epoch_counter;  	if (!dev->mode_config.poll_enabled)  		return false; @@ -836,33 +918,8 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)  		if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))  			continue; -		old_status = connector->status; - -		old_epoch_counter = connector->epoch_counter; - -		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Old epoch counter %llu\n", connector->base.id, -			      connector->name, -			      old_epoch_counter); - -		connector->status = drm_helper_probe_detect(connector, NULL, false); -		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", -			      connector->base.id, -			      connector->name, -			      drm_get_connector_status_name(old_status), -			      drm_get_connector_status_name(connector->status)); - -		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] New epoch counter %llu\n", -			      connector->base.id, -			      connector->name, -			      connector->epoch_counter); - -		/* -		 * Check if epoch counter had changed, meaning that we need -		 * to send a uevent. -		 */ -		if (old_epoch_counter != connector->epoch_counter) +		if (check_connector_changed(connector))  			changed = true; -  	}  	drm_connector_list_iter_end(&conn_iter);  	mutex_unlock(&dev->mode_config.mutex);  |