82086a13fe77026b9ffdbebf8298acd8c169ac6b
[openwrt/staging/rmilecki.git] /
1 From 22ce134cee72bd9ef4b02c8769f868309f2ab8fd Mon Sep 17 00:00:00 2001
2 From: Maxime Ripard <maxime@cerno.tech>
3 Date: Thu, 19 Aug 2021 14:37:04 +0200
4 Subject: [PATCH] drm/probe-helper: Create a HPD IRQ event helper for a
5 single connector
6
7 The drm_helper_hpd_irq_event() function is iterating over all the
8 connectors when an hotplug event is detected.
9
10 During that iteration, it will call each connector detect function and
11 figure out if its status changed.
12
13 Finally, if any connector changed, it will notify the user-space and the
14 clients that something changed on the DRM device.
15
16 This is supposed to be used for drivers that don't have a hotplug
17 interrupt for individual connectors. However, drivers that can use an
18 interrupt for a single connector are left in the dust and can either
19 reimplement the logic used during the iteration for each connector or
20 use that helper and iterate over all connectors all the time.
21
22 Since both are suboptimal, let's create a helper that will only perform
23 the status detection on a single connector.
24
25 Signed-off-by: Maxime Ripard <maxime@cerno.tech>
26
27 ---
28
29 Changes from v1:
30 - Rename the shared function
31 - Move the hotplug event notification out of the shared function
32 - Added missing locks
33 - Improve the documentation
34 - Switched to drm_dbg_kms
35 ---
36 drivers/gpu/drm/drm_probe_helper.c | 120 ++++++++++++++++++++---------
37 include/drm/drm_probe_helper.h | 1 +
38 2 files changed, 86 insertions(+), 35 deletions(-)
39
40 --- a/drivers/gpu/drm/drm_probe_helper.c
41 +++ b/drivers/gpu/drm/drm_probe_helper.c
42 @@ -796,6 +796,86 @@ void drm_kms_helper_poll_fini(struct drm
43 }
44 EXPORT_SYMBOL(drm_kms_helper_poll_fini);
45
46 +static bool check_connector_changed(struct drm_connector *connector)
47 +{
48 + struct drm_device *dev = connector->dev;
49 + enum drm_connector_status old_status;
50 + u64 old_epoch_counter;
51 + bool changed = false;
52 +
53 + /* Only handle HPD capable connectors. */
54 + drm_WARN_ON(dev, !(connector->polled & DRM_CONNECTOR_POLL_HPD));
55 +
56 + drm_WARN_ON(dev, !mutex_is_locked(&dev->mode_config.mutex));
57 +
58 + old_status = connector->status;
59 + old_epoch_counter = connector->epoch_counter;
60 +
61 + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Old epoch counter %llu\n",
62 + connector->base.id,
63 + connector->name,
64 + old_epoch_counter);
65 +
66 + connector->status = drm_helper_probe_detect(connector, NULL, false);
67 + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s\n",
68 + connector->base.id,
69 + connector->name,
70 + drm_get_connector_status_name(old_status),
71 + drm_get_connector_status_name(connector->status));
72 +
73 + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] New epoch counter %llu\n",
74 + connector->base.id,
75 + connector->name,
76 + connector->epoch_counter);
77 +
78 + /*
79 + * Check if epoch counter had changed, meaning that we need
80 + * to send a uevent.
81 + */
82 + if (old_epoch_counter != connector->epoch_counter)
83 + changed = true;
84 +
85 + return changed;
86 +}
87 +
88 +/**
89 + * drm_connector_helper_hpd_irq_event - hotplug processing
90 + * @connector: drm_connector
91 + *
92 + * Drivers can use this helper function to run a detect cycle on a connector
93 + * which has the DRM_CONNECTOR_POLL_HPD flag set in its &polled member.
94 + *
95 + * This helper function is useful for drivers which can track hotplug
96 + * interrupts for a single connector. Drivers that want to send a
97 + * hotplug event for all connectors or can't track hotplug interrupts
98 + * per connector need to use drm_helper_hpd_irq_event().
99 + *
100 + * This function must be called from process context with no mode
101 + * setting locks held.
102 + *
103 + * Note that a connector can be both polled and probed from the hotplug
104 + * handler, in case the hotplug interrupt is known to be unreliable.
105 + */
106 +bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector)
107 +{
108 + struct drm_device *dev = connector->dev;
109 + bool changed;
110 +
111 + mutex_lock(&dev->mode_config.mutex);
112 + changed = check_connector_changed(connector);
113 + mutex_unlock(&dev->mode_config.mutex);
114 +
115 + if (changed) {
116 + drm_kms_helper_hotplug_event(dev);
117 + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Sent hotplug event\n",
118 + connector->base.id,
119 + connector->name);
120 + }
121 +
122 + return changed;
123 +}
124 +EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event);
125 +
126 /**
127 * drm_helper_hpd_irq_event - hotplug processing
128 * @dev: drm_device
129 @@ -809,9 +889,10 @@ EXPORT_SYMBOL(drm_kms_helper_poll_fini);
130 * interrupts for each connector.
131 *
132 * Drivers which support hotplug interrupts for each connector individually and
133 - * which have a more fine-grained detect logic should bypass this code and
134 - * directly call drm_kms_helper_hotplug_event() in case the connector state
135 - * changed.
136 + * which have a more fine-grained detect logic can use
137 + * drm_connector_helper_hpd_irq_event(). Alternatively, they should bypass this
138 + * code and directly call drm_kms_helper_hotplug_event() in case the connector
139 + * state changed.
140 *
141 * This function must be called from process context with no mode
142 * setting locks held.
143 @@ -823,9 +904,7 @@ bool drm_helper_hpd_irq_event(struct drm
144 {
145 struct drm_connector *connector;
146 struct drm_connector_list_iter conn_iter;
147 - enum drm_connector_status old_status;
148 bool changed = false;
149 - u64 old_epoch_counter;
150
151 if (!dev->mode_config.poll_enabled)
152 return false;
153 @@ -833,37 +912,8 @@ bool drm_helper_hpd_irq_event(struct drm
154 mutex_lock(&dev->mode_config.mutex);
155 drm_connector_list_iter_begin(dev, &conn_iter);
156 drm_for_each_connector_iter(connector, &conn_iter) {
157 - /* Only handle HPD capable connectors. */
158 - if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
159 - continue;
160 -
161 - old_status = connector->status;
162 -
163 - old_epoch_counter = connector->epoch_counter;
164 -
165 - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Old epoch counter %llu\n", connector->base.id,
166 - connector->name,
167 - old_epoch_counter);
168 -
169 - connector->status = drm_helper_probe_detect(connector, NULL, false);
170 - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
171 - connector->base.id,
172 - connector->name,
173 - drm_get_connector_status_name(old_status),
174 - drm_get_connector_status_name(connector->status));
175 -
176 - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] New epoch counter %llu\n",
177 - connector->base.id,
178 - connector->name,
179 - connector->epoch_counter);
180 -
181 - /*
182 - * Check if epoch counter had changed, meaning that we need
183 - * to send a uevent.
184 - */
185 - if (old_epoch_counter != connector->epoch_counter)
186 + if (check_connector_changed(connector))
187 changed = true;
188 -
189 }
190 drm_connector_list_iter_end(&conn_iter);
191 mutex_unlock(&dev->mode_config.mutex);
192 --- a/include/drm/drm_probe_helper.h
193 +++ b/include/drm/drm_probe_helper.h
194 @@ -18,6 +18,7 @@ int drm_helper_probe_detect(struct drm_c
195 void drm_kms_helper_poll_init(struct drm_device *dev);
196 void drm_kms_helper_poll_fini(struct drm_device *dev);
197 bool drm_helper_hpd_irq_event(struct drm_device *dev);
198 +bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector);
199 void drm_kms_helper_hotplug_event(struct drm_device *dev);
200
201 void drm_kms_helper_poll_disable(struct drm_device *dev);