Commit | Line | Data |
---|---|---|
5e20bdf3 LP |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
4 | */ | |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/slab.h> | |
9 | ||
10 | #include <drm/drm_atomic_state_helper.h> | |
11 | #include <drm/drm_bridge.h> | |
12 | #include <drm/drm_bridge_connector.h> | |
13 | #include <drm/drm_connector.h> | |
14 | #include <drm/drm_device.h> | |
15 | #include <drm/drm_edid.h> | |
16 | #include <drm/drm_modeset_helper_vtables.h> | |
17 | #include <drm/drm_probe_helper.h> | |
18 | ||
19 | /** | |
20 | * DOC: overview | |
21 | * | |
22 | * The DRM bridge connector helper object provides a DRM connector | |
23 | * implementation that wraps a chain of &struct drm_bridge. The connector | |
24 | * operations are fully implemented based on the operations of the bridges in | |
25 | * the chain, and don't require any intervention from the display controller | |
26 | * driver at runtime. | |
27 | * | |
28 | * To use the helper, display controller drivers create a bridge connector with | |
29 | * a call to drm_bridge_connector_init(). This associates the newly created | |
30 | * connector with the chain of bridges passed to the function and registers it | |
31 | * with the DRM device. At that point the connector becomes fully usable, no | |
32 | * further operation is needed. | |
33 | * | |
34 | * The DRM bridge connector operations are implemented based on the operations | |
35 | * provided by the bridges in the chain. Each connector operation is delegated | |
36 | * to the bridge closest to the connector (at the end of the chain) that | |
37 | * provides the relevant functionality. | |
38 | * | |
39 | * To make use of this helper, all bridges in the chain shall report bridge | |
40 | * operation flags (&drm_bridge->ops) and bridge output type | |
41 | * (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach | |
42 | * flag (none of the bridges shall create a DRM connector directly). | |
43 | */ | |
44 | ||
45 | /** | |
46 | * struct drm_bridge_connector - A connector backed by a chain of bridges | |
47 | */ | |
48 | struct drm_bridge_connector { | |
49 | /** | |
50 | * @base: The base DRM connector | |
51 | */ | |
52 | struct drm_connector base; | |
53 | /** | |
54 | * @encoder: | |
55 | * | |
56 | * The encoder at the start of the bridges chain. | |
57 | */ | |
58 | struct drm_encoder *encoder; | |
59 | /** | |
60 | * @bridge_edid: | |
61 | * | |
62 | * The last bridge in the chain (closest to the connector) that provides | |
63 | * EDID read support, if any (see &DRM_BRIDGE_OP_EDID). | |
64 | */ | |
65 | struct drm_bridge *bridge_edid; | |
66 | /** | |
67 | * @bridge_hpd: | |
68 | * | |
69 | * The last bridge in the chain (closest to the connector) that provides | |
70 | * hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD). | |
71 | */ | |
72 | struct drm_bridge *bridge_hpd; | |
73 | /** | |
74 | * @bridge_detect: | |
75 | * | |
76 | * The last bridge in the chain (closest to the connector) that provides | |
77 | * connector detection, if any (see &DRM_BRIDGE_OP_DETECT). | |
78 | */ | |
79 | struct drm_bridge *bridge_detect; | |
80 | /** | |
81 | * @bridge_modes: | |
82 | * | |
83 | * The last bridge in the chain (closest to the connector) that provides | |
84 | * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES). | |
85 | */ | |
86 | struct drm_bridge *bridge_modes; | |
87 | }; | |
88 | ||
89 | #define to_drm_bridge_connector(x) \ | |
90 | container_of(x, struct drm_bridge_connector, base) | |
91 | ||
92 | /* ----------------------------------------------------------------------------- | |
93 | * Bridge Connector Hot-Plug Handling | |
94 | */ | |
95 | ||
96 | static void drm_bridge_connector_hpd_notify(struct drm_connector *connector, | |
97 | enum drm_connector_status status) | |
98 | { | |
99 | struct drm_bridge_connector *bridge_connector = | |
100 | to_drm_bridge_connector(connector); | |
101 | struct drm_bridge *bridge; | |
102 | ||
103 | /* Notify all bridges in the pipeline of hotplug events. */ | |
104 | drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) { | |
105 | if (bridge->funcs->hpd_notify) | |
106 | bridge->funcs->hpd_notify(bridge, status); | |
107 | } | |
108 | } | |
109 | ||
110 | static void drm_bridge_connector_hpd_cb(void *cb_data, | |
111 | enum drm_connector_status status) | |
112 | { | |
113 | struct drm_bridge_connector *drm_bridge_connector = cb_data; | |
114 | struct drm_connector *connector = &drm_bridge_connector->base; | |
115 | struct drm_device *dev = connector->dev; | |
116 | enum drm_connector_status old_status; | |
117 | ||
118 | mutex_lock(&dev->mode_config.mutex); | |
119 | old_status = connector->status; | |
120 | connector->status = status; | |
121 | mutex_unlock(&dev->mode_config.mutex); | |
122 | ||
123 | if (old_status == status) | |
124 | return; | |
125 | ||
126 | drm_bridge_connector_hpd_notify(connector, status); | |
127 | ||
128 | drm_kms_helper_hotplug_event(dev); | |
129 | } | |
130 | ||
131 | /** | |
132 | * drm_bridge_connector_enable_hpd - Enable hot-plug detection for the connector | |
133 | * @connector: The DRM bridge connector | |
134 | * | |
135 | * This function enables hot-plug detection for the given bridge connector. | |
136 | * This is typically used by display drivers in their resume handler. | |
137 | */ | |
138 | void drm_bridge_connector_enable_hpd(struct drm_connector *connector) | |
139 | { | |
140 | struct drm_bridge_connector *bridge_connector = | |
141 | to_drm_bridge_connector(connector); | |
142 | struct drm_bridge *hpd = bridge_connector->bridge_hpd; | |
143 | ||
144 | if (hpd) | |
145 | drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb, | |
146 | bridge_connector); | |
147 | } | |
148 | EXPORT_SYMBOL_GPL(drm_bridge_connector_enable_hpd); | |
149 | ||
150 | /** | |
151 | * drm_bridge_connector_disable_hpd - Disable hot-plug detection for the | |
152 | * connector | |
153 | * @connector: The DRM bridge connector | |
154 | * | |
155 | * This function disables hot-plug detection for the given bridge connector. | |
156 | * This is typically used by display drivers in their suspend handler. | |
157 | */ | |
158 | void drm_bridge_connector_disable_hpd(struct drm_connector *connector) | |
159 | { | |
160 | struct drm_bridge_connector *bridge_connector = | |
161 | to_drm_bridge_connector(connector); | |
162 | struct drm_bridge *hpd = bridge_connector->bridge_hpd; | |
163 | ||
164 | if (hpd) | |
165 | drm_bridge_hpd_disable(hpd); | |
166 | } | |
167 | EXPORT_SYMBOL_GPL(drm_bridge_connector_disable_hpd); | |
168 | ||
169 | /* ----------------------------------------------------------------------------- | |
170 | * Bridge Connector Functions | |
171 | */ | |
172 | ||
173 | static enum drm_connector_status | |
174 | drm_bridge_connector_detect(struct drm_connector *connector, bool force) | |
175 | { | |
176 | struct drm_bridge_connector *bridge_connector = | |
177 | to_drm_bridge_connector(connector); | |
178 | struct drm_bridge *detect = bridge_connector->bridge_detect; | |
179 | enum drm_connector_status status; | |
180 | ||
181 | if (detect) { | |
182 | status = detect->funcs->detect(detect); | |
183 | ||
184 | drm_bridge_connector_hpd_notify(connector, status); | |
185 | } else { | |
186 | switch (connector->connector_type) { | |
187 | case DRM_MODE_CONNECTOR_DPI: | |
188 | case DRM_MODE_CONNECTOR_LVDS: | |
189 | case DRM_MODE_CONNECTOR_DSI: | |
c5589b39 | 190 | case DRM_MODE_CONNECTOR_eDP: |
5e20bdf3 LP |
191 | status = connector_status_connected; |
192 | break; | |
193 | default: | |
194 | status = connector_status_unknown; | |
195 | break; | |
196 | } | |
197 | } | |
198 | ||
199 | return status; | |
200 | } | |
201 | ||
202 | static void drm_bridge_connector_destroy(struct drm_connector *connector) | |
203 | { | |
204 | struct drm_bridge_connector *bridge_connector = | |
205 | to_drm_bridge_connector(connector); | |
206 | ||
207 | if (bridge_connector->bridge_hpd) { | |
208 | struct drm_bridge *hpd = bridge_connector->bridge_hpd; | |
209 | ||
210 | drm_bridge_hpd_disable(hpd); | |
211 | } | |
212 | ||
213 | drm_connector_unregister(connector); | |
214 | drm_connector_cleanup(connector); | |
215 | ||
216 | kfree(bridge_connector); | |
217 | } | |
218 | ||
219 | static const struct drm_connector_funcs drm_bridge_connector_funcs = { | |
220 | .reset = drm_atomic_helper_connector_reset, | |
221 | .detect = drm_bridge_connector_detect, | |
222 | .fill_modes = drm_helper_probe_single_connector_modes, | |
223 | .destroy = drm_bridge_connector_destroy, | |
224 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
225 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
226 | }; | |
227 | ||
228 | /* ----------------------------------------------------------------------------- | |
229 | * Bridge Connector Helper Functions | |
230 | */ | |
231 | ||
232 | static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector, | |
233 | struct drm_bridge *bridge) | |
234 | { | |
235 | enum drm_connector_status status; | |
236 | struct edid *edid; | |
237 | int n; | |
238 | ||
239 | status = drm_bridge_connector_detect(connector, false); | |
240 | if (status != connector_status_connected) | |
241 | goto no_edid; | |
242 | ||
243 | edid = bridge->funcs->get_edid(bridge, connector); | |
95d7a1a6 | 244 | if (!drm_edid_is_valid(edid)) { |
5e20bdf3 LP |
245 | kfree(edid); |
246 | goto no_edid; | |
247 | } | |
248 | ||
249 | drm_connector_update_edid_property(connector, edid); | |
250 | n = drm_add_edid_modes(connector, edid); | |
251 | ||
252 | kfree(edid); | |
253 | return n; | |
254 | ||
255 | no_edid: | |
256 | drm_connector_update_edid_property(connector, NULL); | |
257 | return 0; | |
258 | } | |
259 | ||
260 | static int drm_bridge_connector_get_modes(struct drm_connector *connector) | |
261 | { | |
262 | struct drm_bridge_connector *bridge_connector = | |
263 | to_drm_bridge_connector(connector); | |
264 | struct drm_bridge *bridge; | |
265 | ||
266 | /* | |
267 | * If display exposes EDID, then we parse that in the normal way to | |
268 | * build table of supported modes. | |
269 | */ | |
270 | bridge = bridge_connector->bridge_edid; | |
271 | if (bridge) | |
272 | return drm_bridge_connector_get_modes_edid(connector, bridge); | |
273 | ||
274 | /* | |
275 | * Otherwise if the display pipeline reports modes (e.g. with a fixed | |
276 | * resolution panel or an analog TV output), query it. | |
277 | */ | |
278 | bridge = bridge_connector->bridge_modes; | |
279 | if (bridge) | |
280 | return bridge->funcs->get_modes(bridge, connector); | |
281 | ||
282 | /* | |
283 | * We can't retrieve modes, which can happen for instance for a DVI or | |
284 | * VGA output with the DDC bus unconnected. The KMS core will add the | |
285 | * default modes. | |
286 | */ | |
287 | return 0; | |
288 | } | |
289 | ||
290 | static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = { | |
291 | .get_modes = drm_bridge_connector_get_modes, | |
292 | /* No need for .mode_valid(), the bridges are checked by the core. */ | |
293 | }; | |
294 | ||
295 | /* ----------------------------------------------------------------------------- | |
296 | * Bridge Connector Initialisation | |
297 | */ | |
298 | ||
299 | /** | |
300 | * drm_bridge_connector_init - Initialise a connector for a chain of bridges | |
301 | * @drm: the DRM device | |
302 | * @encoder: the encoder where the bridge chain starts | |
303 | * | |
304 | * Allocate, initialise and register a &drm_bridge_connector with the @drm | |
305 | * device. The connector is associated with a chain of bridges that starts at | |
306 | * the @encoder. All bridges in the chain shall report bridge operation flags | |
307 | * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of | |
308 | * them may create a DRM connector directly. | |
309 | * | |
310 | * Returns a pointer to the new connector on success, or a negative error | |
311 | * pointer otherwise. | |
312 | */ | |
313 | struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, | |
314 | struct drm_encoder *encoder) | |
315 | { | |
316 | struct drm_bridge_connector *bridge_connector; | |
317 | struct drm_connector *connector; | |
318 | struct i2c_adapter *ddc = NULL; | |
319 | struct drm_bridge *bridge; | |
320 | int connector_type; | |
321 | ||
322 | bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL); | |
323 | if (!bridge_connector) | |
324 | return ERR_PTR(-ENOMEM); | |
325 | ||
326 | bridge_connector->encoder = encoder; | |
327 | ||
328 | /* | |
329 | * TODO: Handle doublescan_allowed, stereo_allowed and | |
330 | * ycbcr_420_allowed. | |
331 | */ | |
332 | connector = &bridge_connector->base; | |
333 | connector->interlace_allowed = true; | |
334 | ||
335 | /* | |
336 | * Initialise connector status handling. First locate the furthest | |
337 | * bridges in the pipeline that support HPD and output detection. Then | |
338 | * initialise the connector polling mode, using HPD if available and | |
339 | * falling back to polling if supported. If neither HPD nor output | |
340 | * detection are available, we don't support hotplug detection at all. | |
341 | */ | |
342 | connector_type = DRM_MODE_CONNECTOR_Unknown; | |
343 | drm_for_each_bridge_in_chain(encoder, bridge) { | |
344 | if (!bridge->interlace_allowed) | |
345 | connector->interlace_allowed = false; | |
346 | ||
347 | if (bridge->ops & DRM_BRIDGE_OP_EDID) | |
348 | bridge_connector->bridge_edid = bridge; | |
349 | if (bridge->ops & DRM_BRIDGE_OP_HPD) | |
350 | bridge_connector->bridge_hpd = bridge; | |
351 | if (bridge->ops & DRM_BRIDGE_OP_DETECT) | |
352 | bridge_connector->bridge_detect = bridge; | |
353 | if (bridge->ops & DRM_BRIDGE_OP_MODES) | |
354 | bridge_connector->bridge_modes = bridge; | |
355 | ||
356 | if (!drm_bridge_get_next_bridge(bridge)) | |
357 | connector_type = bridge->type; | |
358 | ||
359 | if (bridge->ddc) | |
360 | ddc = bridge->ddc; | |
361 | } | |
362 | ||
363 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) { | |
364 | kfree(bridge_connector); | |
365 | return ERR_PTR(-EINVAL); | |
366 | } | |
367 | ||
368 | drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs, | |
369 | connector_type, ddc); | |
370 | drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); | |
371 | ||
372 | if (bridge_connector->bridge_hpd) | |
373 | connector->polled = DRM_CONNECTOR_POLL_HPD; | |
374 | else if (bridge_connector->bridge_detect) | |
375 | connector->polled = DRM_CONNECTOR_POLL_CONNECT | |
376 | | DRM_CONNECTOR_POLL_DISCONNECT; | |
377 | ||
378 | return connector; | |
379 | } | |
380 | EXPORT_SYMBOL_GPL(drm_bridge_connector_init); |