2022-05-16 21:40:32 +00:00
|
|
|
From 22ce134cee72bd9ef4b02c8769f868309f2ab8fd Mon Sep 17 00:00:00 2001
|
|
|
|
From: Maxime Ripard <maxime@cerno.tech>
|
|
|
|
Date: Thu, 19 Aug 2021 14:37:04 +0200
|
|
|
|
Subject: [PATCH] drm/probe-helper: Create a HPD IRQ event helper for a
|
|
|
|
single connector
|
|
|
|
|
|
|
|
The drm_helper_hpd_irq_event() function is iterating over all the
|
|
|
|
connectors when an hotplug event is detected.
|
|
|
|
|
|
|
|
During that iteration, it will call each connector detect function and
|
|
|
|
figure out if its status changed.
|
|
|
|
|
|
|
|
Finally, if any connector changed, it will notify the user-space and the
|
|
|
|
clients that something changed on the DRM device.
|
|
|
|
|
|
|
|
This is supposed to be used for drivers that don't have a hotplug
|
|
|
|
interrupt for individual connectors. However, drivers that can use an
|
|
|
|
interrupt for a single connector are left in the dust and can either
|
|
|
|
reimplement the logic used during the iteration for each connector or
|
|
|
|
use that helper and iterate over all connectors all the time.
|
|
|
|
|
|
|
|
Since both are suboptimal, let's create a helper that will only perform
|
|
|
|
the status detection on a single connector.
|
|
|
|
|
|
|
|
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
Changes from v1:
|
|
|
|
- Rename the shared function
|
|
|
|
- Move the hotplug event notification out of the shared function
|
|
|
|
- Added missing locks
|
|
|
|
- Improve the documentation
|
|
|
|
- Switched to drm_dbg_kms
|
|
|
|
---
|
|
|
|
drivers/gpu/drm/drm_probe_helper.c | 120 ++++++++++++++++++++---------
|
|
|
|
include/drm/drm_probe_helper.h | 1 +
|
|
|
|
2 files changed, 86 insertions(+), 35 deletions(-)
|
|
|
|
|
|
|
|
--- a/drivers/gpu/drm/drm_probe_helper.c
|
|
|
|
+++ b/drivers/gpu/drm/drm_probe_helper.c
|
2023-05-12 16:33:18 +00:00
|
|
|
@@ -796,6 +796,86 @@ void drm_kms_helper_poll_fini(struct drm
|
2022-05-16 21:40:32 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
+ bool changed = false;
|
|
|
|
+
|
|
|
|
+ /* 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;
|
|
|
|
+
|
|
|
|
+ drm_dbg_kms(dev, "[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_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] 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)
|
|
|
|
+ changed = true;
|
|
|
|
+
|
|
|
|
+ return changed;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 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.
|
|
|
|
+ */
|
|
|
|
+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
|
2023-05-12 16:33:18 +00:00
|
|
|
@@ -809,9 +889,10 @@ EXPORT_SYMBOL(drm_kms_helper_poll_fini);
|
2022-05-16 21:40:32 +00:00
|
|
|
* 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.
|
2023-05-12 16:33:18 +00:00
|
|
|
@@ -823,9 +904,7 @@ bool drm_helper_hpd_irq_event(struct drm
|
2022-05-16 21:40:32 +00:00
|
|
|
{
|
|
|
|
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;
|
2023-05-12 16:33:18 +00:00
|
|
|
@@ -833,37 +912,8 @@ bool drm_helper_hpd_irq_event(struct drm
|
2022-05-16 21:40:32 +00:00
|
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
|
|
drm_connector_list_iter_begin(dev, &conn_iter);
|
|
|
|
drm_for_each_connector_iter(connector, &conn_iter) {
|
|
|
|
- /* Only handle HPD capable connectors. */
|
|
|
|
- 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);
|
|
|
|
--- a/include/drm/drm_probe_helper.h
|
|
|
|
+++ b/include/drm/drm_probe_helper.h
|
|
|
|
@@ -18,6 +18,7 @@ int drm_helper_probe_detect(struct drm_c
|
|
|
|
void drm_kms_helper_poll_init(struct drm_device *dev);
|
|
|
|
void drm_kms_helper_poll_fini(struct drm_device *dev);
|
|
|
|
bool drm_helper_hpd_irq_event(struct drm_device *dev);
|
|
|
|
+bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector);
|
|
|
|
void drm_kms_helper_hotplug_event(struct drm_device *dev);
|
|
|
|
|
|
|
|
void drm_kms_helper_poll_disable(struct drm_device *dev);
|