]>
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: | |
190 | status = connector_status_connected; | |
191 | break; | |
192 | default: | |
193 | status = connector_status_unknown; | |
194 | break; | |
195 | } | |
196 | } | |
197 | ||
198 | return status; | |
199 | } | |
200 | ||
201 | static void drm_bridge_connector_destroy(struct drm_connector *connector) | |
202 | { | |
203 | struct drm_bridge_connector *bridge_connector = | |
204 | to_drm_bridge_connector(connector); | |
205 | ||
206 | if (bridge_connector->bridge_hpd) { | |
207 | struct drm_bridge *hpd = bridge_connector->bridge_hpd; | |
208 | ||
209 | drm_bridge_hpd_disable(hpd); | |
210 | } | |
211 | ||
212 | drm_connector_unregister(connector); | |
213 | drm_connector_cleanup(connector); | |
214 | ||
215 | kfree(bridge_connector); | |
216 | } | |
217 | ||
218 | static const struct drm_connector_funcs drm_bridge_connector_funcs = { | |
219 | .reset = drm_atomic_helper_connector_reset, | |
220 | .detect = drm_bridge_connector_detect, | |
221 | .fill_modes = drm_helper_probe_single_connector_modes, | |
222 | .destroy = drm_bridge_connector_destroy, | |
223 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
224 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
225 | }; | |
226 | ||
227 | /* ----------------------------------------------------------------------------- | |
228 | * Bridge Connector Helper Functions | |
229 | */ | |
230 | ||
231 | static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector, | |
232 | struct drm_bridge *bridge) | |
233 | { | |
234 | enum drm_connector_status status; | |
235 | struct edid *edid; | |
236 | int n; | |
237 | ||
238 | status = drm_bridge_connector_detect(connector, false); | |
239 | if (status != connector_status_connected) | |
240 | goto no_edid; | |
241 | ||
242 | edid = bridge->funcs->get_edid(bridge, connector); | |
243 | if (!edid || !drm_edid_is_valid(edid)) { | |
244 | kfree(edid); | |
245 | goto no_edid; | |
246 | } | |
247 | ||
248 | drm_connector_update_edid_property(connector, edid); | |
249 | n = drm_add_edid_modes(connector, edid); | |
250 | ||
251 | kfree(edid); | |
252 | return n; | |
253 | ||
254 | no_edid: | |
255 | drm_connector_update_edid_property(connector, NULL); | |
256 | return 0; | |
257 | } | |
258 | ||
259 | static int drm_bridge_connector_get_modes(struct drm_connector *connector) | |
260 | { | |
261 | struct drm_bridge_connector *bridge_connector = | |
262 | to_drm_bridge_connector(connector); | |
263 | struct drm_bridge *bridge; | |
264 | ||
265 | /* | |
266 | * If display exposes EDID, then we parse that in the normal way to | |
267 | * build table of supported modes. | |
268 | */ | |
269 | bridge = bridge_connector->bridge_edid; | |
270 | if (bridge) | |
271 | return drm_bridge_connector_get_modes_edid(connector, bridge); | |
272 | ||
273 | /* | |
274 | * Otherwise if the display pipeline reports modes (e.g. with a fixed | |
275 | * resolution panel or an analog TV output), query it. | |
276 | */ | |
277 | bridge = bridge_connector->bridge_modes; | |
278 | if (bridge) | |
279 | return bridge->funcs->get_modes(bridge, connector); | |
280 | ||
281 | /* | |
282 | * We can't retrieve modes, which can happen for instance for a DVI or | |
283 | * VGA output with the DDC bus unconnected. The KMS core will add the | |
284 | * default modes. | |
285 | */ | |
286 | return 0; | |
287 | } | |
288 | ||
289 | static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = { | |
290 | .get_modes = drm_bridge_connector_get_modes, | |
291 | /* No need for .mode_valid(), the bridges are checked by the core. */ | |
292 | }; | |
293 | ||
294 | /* ----------------------------------------------------------------------------- | |
295 | * Bridge Connector Initialisation | |
296 | */ | |
297 | ||
298 | /** | |
299 | * drm_bridge_connector_init - Initialise a connector for a chain of bridges | |
300 | * @drm: the DRM device | |
301 | * @encoder: the encoder where the bridge chain starts | |
302 | * | |
303 | * Allocate, initialise and register a &drm_bridge_connector with the @drm | |
304 | * device. The connector is associated with a chain of bridges that starts at | |
305 | * the @encoder. All bridges in the chain shall report bridge operation flags | |
306 | * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of | |
307 | * them may create a DRM connector directly. | |
308 | * | |
309 | * Returns a pointer to the new connector on success, or a negative error | |
310 | * pointer otherwise. | |
311 | */ | |
312 | struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, | |
313 | struct drm_encoder *encoder) | |
314 | { | |
315 | struct drm_bridge_connector *bridge_connector; | |
316 | struct drm_connector *connector; | |
317 | struct i2c_adapter *ddc = NULL; | |
318 | struct drm_bridge *bridge; | |
319 | int connector_type; | |
320 | ||
321 | bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL); | |
322 | if (!bridge_connector) | |
323 | return ERR_PTR(-ENOMEM); | |
324 | ||
325 | bridge_connector->encoder = encoder; | |
326 | ||
327 | /* | |
328 | * TODO: Handle doublescan_allowed, stereo_allowed and | |
329 | * ycbcr_420_allowed. | |
330 | */ | |
331 | connector = &bridge_connector->base; | |
332 | connector->interlace_allowed = true; | |
333 | ||
334 | /* | |
335 | * Initialise connector status handling. First locate the furthest | |
336 | * bridges in the pipeline that support HPD and output detection. Then | |
337 | * initialise the connector polling mode, using HPD if available and | |
338 | * falling back to polling if supported. If neither HPD nor output | |
339 | * detection are available, we don't support hotplug detection at all. | |
340 | */ | |
341 | connector_type = DRM_MODE_CONNECTOR_Unknown; | |
342 | drm_for_each_bridge_in_chain(encoder, bridge) { | |
343 | if (!bridge->interlace_allowed) | |
344 | connector->interlace_allowed = false; | |
345 | ||
346 | if (bridge->ops & DRM_BRIDGE_OP_EDID) | |
347 | bridge_connector->bridge_edid = bridge; | |
348 | if (bridge->ops & DRM_BRIDGE_OP_HPD) | |
349 | bridge_connector->bridge_hpd = bridge; | |
350 | if (bridge->ops & DRM_BRIDGE_OP_DETECT) | |
351 | bridge_connector->bridge_detect = bridge; | |
352 | if (bridge->ops & DRM_BRIDGE_OP_MODES) | |
353 | bridge_connector->bridge_modes = bridge; | |
354 | ||
355 | if (!drm_bridge_get_next_bridge(bridge)) | |
356 | connector_type = bridge->type; | |
357 | ||
358 | if (bridge->ddc) | |
359 | ddc = bridge->ddc; | |
360 | } | |
361 | ||
362 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) { | |
363 | kfree(bridge_connector); | |
364 | return ERR_PTR(-EINVAL); | |
365 | } | |
366 | ||
367 | drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs, | |
368 | connector_type, ddc); | |
369 | drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); | |
370 | ||
371 | if (bridge_connector->bridge_hpd) | |
372 | connector->polled = DRM_CONNECTOR_POLL_HPD; | |
373 | else if (bridge_connector->bridge_detect) | |
374 | connector->polled = DRM_CONNECTOR_POLL_CONNECT | |
375 | | DRM_CONNECTOR_POLL_DISCONNECT; | |
376 | ||
377 | return connector; | |
378 | } | |
379 | EXPORT_SYMBOL_GPL(drm_bridge_connector_init); |