1 | /* |
2 | * Copyright (C) 2013, NVIDIA Corporation. All rights reserved. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sub license, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the |
12 | * next paragraph) shall be included in all copies or substantial portions |
13 | * of the Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
21 | * DEALINGS IN THE SOFTWARE. |
22 | */ |
23 | |
24 | #include <linux/backlight.h> |
25 | #include <linux/err.h> |
26 | #include <linux/module.h> |
27 | |
28 | #include <drm/drm_crtc.h> |
29 | #include <drm/drm_panel.h> |
30 | #include <drm/drm_print.h> |
31 | |
32 | static DEFINE_MUTEX(panel_lock); |
33 | static LIST_HEAD(panel_list); |
34 | |
35 | /** |
36 | * DOC: drm panel |
37 | * |
38 | * The DRM panel helpers allow drivers to register panel objects with a |
39 | * central registry and provide functions to retrieve those panels in display |
40 | * drivers. |
41 | * |
42 | * For easy integration into drivers using the &drm_bridge infrastructure please |
43 | * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add(). |
44 | */ |
45 | |
46 | /** |
47 | * drm_panel_init - initialize a panel |
48 | * @panel: DRM panel |
49 | * @dev: parent device of the panel |
50 | * @funcs: panel operations |
51 | * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to |
52 | * the panel interface |
53 | * |
54 | * Initialize the panel structure for subsequent registration with |
55 | * drm_panel_add(). |
56 | */ |
57 | void drm_panel_init(struct drm_panel *panel, struct device *dev, |
58 | const struct drm_panel_funcs *funcs, int connector_type) |
59 | { |
60 | INIT_LIST_HEAD(list: &panel->list); |
61 | INIT_LIST_HEAD(list: &panel->followers); |
62 | mutex_init(&panel->follower_lock); |
63 | panel->dev = dev; |
64 | panel->funcs = funcs; |
65 | panel->connector_type = connector_type; |
66 | } |
67 | EXPORT_SYMBOL(drm_panel_init); |
68 | |
69 | /** |
70 | * drm_panel_add - add a panel to the global registry |
71 | * @panel: panel to add |
72 | * |
73 | * Add a panel to the global registry so that it can be looked up by display |
74 | * drivers. |
75 | */ |
76 | void drm_panel_add(struct drm_panel *panel) |
77 | { |
78 | mutex_lock(&panel_lock); |
79 | list_add_tail(new: &panel->list, head: &panel_list); |
80 | mutex_unlock(lock: &panel_lock); |
81 | } |
82 | EXPORT_SYMBOL(drm_panel_add); |
83 | |
84 | /** |
85 | * drm_panel_remove - remove a panel from the global registry |
86 | * @panel: DRM panel |
87 | * |
88 | * Removes a panel from the global registry. |
89 | */ |
90 | void drm_panel_remove(struct drm_panel *panel) |
91 | { |
92 | mutex_lock(&panel_lock); |
93 | list_del_init(entry: &panel->list); |
94 | mutex_unlock(lock: &panel_lock); |
95 | } |
96 | EXPORT_SYMBOL(drm_panel_remove); |
97 | |
98 | /** |
99 | * drm_panel_prepare - power on a panel |
100 | * @panel: DRM panel |
101 | * |
102 | * Calling this function will enable power and deassert any reset signals to |
103 | * the panel. After this has completed it is possible to communicate with any |
104 | * integrated circuitry via a command bus. |
105 | * |
106 | * Return: 0 on success or a negative error code on failure. |
107 | */ |
108 | int drm_panel_prepare(struct drm_panel *panel) |
109 | { |
110 | struct drm_panel_follower *follower; |
111 | int ret; |
112 | |
113 | if (!panel) |
114 | return -EINVAL; |
115 | |
116 | if (panel->prepared) { |
117 | dev_warn(panel->dev, "Skipping prepare of already prepared panel\n" ); |
118 | return 0; |
119 | } |
120 | |
121 | mutex_lock(&panel->follower_lock); |
122 | |
123 | if (panel->funcs && panel->funcs->prepare) { |
124 | ret = panel->funcs->prepare(panel); |
125 | if (ret < 0) |
126 | goto exit; |
127 | } |
128 | panel->prepared = true; |
129 | |
130 | list_for_each_entry(follower, &panel->followers, list) { |
131 | ret = follower->funcs->panel_prepared(follower); |
132 | if (ret < 0) |
133 | dev_info(panel->dev, "%ps failed: %d\n" , |
134 | follower->funcs->panel_prepared, ret); |
135 | } |
136 | |
137 | ret = 0; |
138 | exit: |
139 | mutex_unlock(lock: &panel->follower_lock); |
140 | |
141 | return ret; |
142 | } |
143 | EXPORT_SYMBOL(drm_panel_prepare); |
144 | |
145 | /** |
146 | * drm_panel_unprepare - power off a panel |
147 | * @panel: DRM panel |
148 | * |
149 | * Calling this function will completely power off a panel (assert the panel's |
150 | * reset, turn off power supplies, ...). After this function has completed, it |
151 | * is usually no longer possible to communicate with the panel until another |
152 | * call to drm_panel_prepare(). |
153 | * |
154 | * Return: 0 on success or a negative error code on failure. |
155 | */ |
156 | int drm_panel_unprepare(struct drm_panel *panel) |
157 | { |
158 | struct drm_panel_follower *follower; |
159 | int ret; |
160 | |
161 | if (!panel) |
162 | return -EINVAL; |
163 | |
164 | if (!panel->prepared) { |
165 | dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n" ); |
166 | return 0; |
167 | } |
168 | |
169 | mutex_lock(&panel->follower_lock); |
170 | |
171 | list_for_each_entry(follower, &panel->followers, list) { |
172 | ret = follower->funcs->panel_unpreparing(follower); |
173 | if (ret < 0) |
174 | dev_info(panel->dev, "%ps failed: %d\n" , |
175 | follower->funcs->panel_unpreparing, ret); |
176 | } |
177 | |
178 | if (panel->funcs && panel->funcs->unprepare) { |
179 | ret = panel->funcs->unprepare(panel); |
180 | if (ret < 0) |
181 | goto exit; |
182 | } |
183 | panel->prepared = false; |
184 | |
185 | ret = 0; |
186 | exit: |
187 | mutex_unlock(lock: &panel->follower_lock); |
188 | |
189 | return ret; |
190 | } |
191 | EXPORT_SYMBOL(drm_panel_unprepare); |
192 | |
193 | /** |
194 | * drm_panel_enable - enable a panel |
195 | * @panel: DRM panel |
196 | * |
197 | * Calling this function will cause the panel display drivers to be turned on |
198 | * and the backlight to be enabled. Content will be visible on screen after |
199 | * this call completes. |
200 | * |
201 | * Return: 0 on success or a negative error code on failure. |
202 | */ |
203 | int drm_panel_enable(struct drm_panel *panel) |
204 | { |
205 | int ret; |
206 | |
207 | if (!panel) |
208 | return -EINVAL; |
209 | |
210 | if (panel->enabled) { |
211 | dev_warn(panel->dev, "Skipping enable of already enabled panel\n" ); |
212 | return 0; |
213 | } |
214 | |
215 | if (panel->funcs && panel->funcs->enable) { |
216 | ret = panel->funcs->enable(panel); |
217 | if (ret < 0) |
218 | return ret; |
219 | } |
220 | panel->enabled = true; |
221 | |
222 | ret = backlight_enable(bd: panel->backlight); |
223 | if (ret < 0) |
224 | DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n" , |
225 | ret); |
226 | |
227 | return 0; |
228 | } |
229 | EXPORT_SYMBOL(drm_panel_enable); |
230 | |
231 | /** |
232 | * drm_panel_disable - disable a panel |
233 | * @panel: DRM panel |
234 | * |
235 | * This will typically turn off the panel's backlight or disable the display |
236 | * drivers. For smart panels it should still be possible to communicate with |
237 | * the integrated circuitry via any command bus after this call. |
238 | * |
239 | * Return: 0 on success or a negative error code on failure. |
240 | */ |
241 | int drm_panel_disable(struct drm_panel *panel) |
242 | { |
243 | int ret; |
244 | |
245 | if (!panel) |
246 | return -EINVAL; |
247 | |
248 | if (!panel->enabled) { |
249 | dev_warn(panel->dev, "Skipping disable of already disabled panel\n" ); |
250 | return 0; |
251 | } |
252 | |
253 | ret = backlight_disable(bd: panel->backlight); |
254 | if (ret < 0) |
255 | DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n" , |
256 | ret); |
257 | |
258 | if (panel->funcs && panel->funcs->disable) { |
259 | ret = panel->funcs->disable(panel); |
260 | if (ret < 0) |
261 | return ret; |
262 | } |
263 | panel->enabled = false; |
264 | |
265 | return 0; |
266 | } |
267 | EXPORT_SYMBOL(drm_panel_disable); |
268 | |
269 | /** |
270 | * drm_panel_get_modes - probe the available display modes of a panel |
271 | * @panel: DRM panel |
272 | * @connector: DRM connector |
273 | * |
274 | * The modes probed from the panel are automatically added to the connector |
275 | * that the panel is attached to. |
276 | * |
277 | * Return: The number of modes available from the panel on success or a |
278 | * negative error code on failure. |
279 | */ |
280 | int drm_panel_get_modes(struct drm_panel *panel, |
281 | struct drm_connector *connector) |
282 | { |
283 | if (!panel) |
284 | return -EINVAL; |
285 | |
286 | if (panel->funcs && panel->funcs->get_modes) |
287 | return panel->funcs->get_modes(panel, connector); |
288 | |
289 | return -EOPNOTSUPP; |
290 | } |
291 | EXPORT_SYMBOL(drm_panel_get_modes); |
292 | |
293 | #ifdef CONFIG_OF |
294 | /** |
295 | * of_drm_find_panel - look up a panel using a device tree node |
296 | * @np: device tree node of the panel |
297 | * |
298 | * Searches the set of registered panels for one that matches the given device |
299 | * tree node. If a matching panel is found, return a pointer to it. |
300 | * |
301 | * Return: A pointer to the panel registered for the specified device tree |
302 | * node or an ERR_PTR() if no panel matching the device tree node can be found. |
303 | * |
304 | * Possible error codes returned by this function: |
305 | * |
306 | * - EPROBE_DEFER: the panel device has not been probed yet, and the caller |
307 | * should retry later |
308 | * - ENODEV: the device is not available (status != "okay" or "ok") |
309 | */ |
310 | struct drm_panel *of_drm_find_panel(const struct device_node *np) |
311 | { |
312 | struct drm_panel *panel; |
313 | |
314 | if (!of_device_is_available(device: np)) |
315 | return ERR_PTR(error: -ENODEV); |
316 | |
317 | mutex_lock(&panel_lock); |
318 | |
319 | list_for_each_entry(panel, &panel_list, list) { |
320 | if (panel->dev->of_node == np) { |
321 | mutex_unlock(lock: &panel_lock); |
322 | return panel; |
323 | } |
324 | } |
325 | |
326 | mutex_unlock(lock: &panel_lock); |
327 | return ERR_PTR(error: -EPROBE_DEFER); |
328 | } |
329 | EXPORT_SYMBOL(of_drm_find_panel); |
330 | |
331 | /** |
332 | * of_drm_get_panel_orientation - look up the orientation of the panel through |
333 | * the "rotation" binding from a device tree node |
334 | * @np: device tree node of the panel |
335 | * @orientation: orientation enum to be filled in |
336 | * |
337 | * Looks up the rotation of a panel in the device tree. The orientation of the |
338 | * panel is expressed as a property name "rotation" in the device tree. The |
339 | * rotation in the device tree is counter clockwise. |
340 | * |
341 | * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the |
342 | * rotation property doesn't exist. Return a negative error code on failure. |
343 | */ |
344 | int of_drm_get_panel_orientation(const struct device_node *np, |
345 | enum drm_panel_orientation *orientation) |
346 | { |
347 | int rotation, ret; |
348 | |
349 | ret = of_property_read_u32(np, propname: "rotation" , out_value: &rotation); |
350 | if (ret == -EINVAL) { |
351 | /* Don't return an error if there's no rotation property. */ |
352 | *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; |
353 | return 0; |
354 | } |
355 | |
356 | if (ret < 0) |
357 | return ret; |
358 | |
359 | if (rotation == 0) |
360 | *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; |
361 | else if (rotation == 90) |
362 | *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; |
363 | else if (rotation == 180) |
364 | *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; |
365 | else if (rotation == 270) |
366 | *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; |
367 | else |
368 | return -EINVAL; |
369 | |
370 | return 0; |
371 | } |
372 | EXPORT_SYMBOL(of_drm_get_panel_orientation); |
373 | #endif |
374 | |
375 | /** |
376 | * drm_is_panel_follower() - Check if the device is a panel follower |
377 | * @dev: The 'struct device' to check |
378 | * |
379 | * This checks to see if a device needs to be power sequenced together with |
380 | * a panel using the panel follower API. |
381 | * At the moment panels can only be followed on device tree enabled systems. |
382 | * The "panel" property of the follower points to the panel to be followed. |
383 | * |
384 | * Return: true if we should be power sequenced with a panel; false otherwise. |
385 | */ |
386 | bool drm_is_panel_follower(struct device *dev) |
387 | { |
388 | /* |
389 | * The "panel" property is actually a phandle, but for simplicity we |
390 | * don't bother trying to parse it here. We just need to know if the |
391 | * property is there. |
392 | */ |
393 | return of_property_read_bool(np: dev->of_node, propname: "panel" ); |
394 | } |
395 | EXPORT_SYMBOL(drm_is_panel_follower); |
396 | |
397 | /** |
398 | * drm_panel_add_follower() - Register something to follow panel state. |
399 | * @follower_dev: The 'struct device' for the follower. |
400 | * @follower: The panel follower descriptor for the follower. |
401 | * |
402 | * A panel follower is called right after preparing the panel and right before |
403 | * unpreparing the panel. It's primary intention is to power on an associated |
404 | * touchscreen, though it could be used for any similar devices. Multiple |
405 | * devices are allowed the follow the same panel. |
406 | * |
407 | * If a follower is added to a panel that's already been turned on, the |
408 | * follower's prepare callback is called right away. |
409 | * |
410 | * At the moment panels can only be followed on device tree enabled systems. |
411 | * The "panel" property of the follower points to the panel to be followed. |
412 | * |
413 | * Return: 0 or an error code. Note that -ENODEV means that we detected that |
414 | * follower_dev is not actually following a panel. The caller may |
415 | * choose to ignore this return value if following a panel is optional. |
416 | */ |
417 | int drm_panel_add_follower(struct device *follower_dev, |
418 | struct drm_panel_follower *follower) |
419 | { |
420 | struct device_node *panel_np; |
421 | struct drm_panel *panel; |
422 | int ret; |
423 | |
424 | panel_np = of_parse_phandle(np: follower_dev->of_node, phandle_name: "panel" , index: 0); |
425 | if (!panel_np) |
426 | return -ENODEV; |
427 | |
428 | panel = of_drm_find_panel(panel_np); |
429 | of_node_put(node: panel_np); |
430 | if (IS_ERR(ptr: panel)) |
431 | return PTR_ERR(ptr: panel); |
432 | |
433 | get_device(dev: panel->dev); |
434 | follower->panel = panel; |
435 | |
436 | mutex_lock(&panel->follower_lock); |
437 | |
438 | list_add_tail(new: &follower->list, head: &panel->followers); |
439 | if (panel->prepared) { |
440 | ret = follower->funcs->panel_prepared(follower); |
441 | if (ret < 0) |
442 | dev_info(panel->dev, "%ps failed: %d\n" , |
443 | follower->funcs->panel_prepared, ret); |
444 | } |
445 | |
446 | mutex_unlock(lock: &panel->follower_lock); |
447 | |
448 | return 0; |
449 | } |
450 | EXPORT_SYMBOL(drm_panel_add_follower); |
451 | |
452 | /** |
453 | * drm_panel_remove_follower() - Reverse drm_panel_add_follower(). |
454 | * @follower: The panel follower descriptor for the follower. |
455 | * |
456 | * Undo drm_panel_add_follower(). This includes calling the follower's |
457 | * unprepare function if we're removed from a panel that's currently prepared. |
458 | * |
459 | * Return: 0 or an error code. |
460 | */ |
461 | void drm_panel_remove_follower(struct drm_panel_follower *follower) |
462 | { |
463 | struct drm_panel *panel = follower->panel; |
464 | int ret; |
465 | |
466 | mutex_lock(&panel->follower_lock); |
467 | |
468 | if (panel->prepared) { |
469 | ret = follower->funcs->panel_unpreparing(follower); |
470 | if (ret < 0) |
471 | dev_info(panel->dev, "%ps failed: %d\n" , |
472 | follower->funcs->panel_unpreparing, ret); |
473 | } |
474 | list_del_init(entry: &follower->list); |
475 | |
476 | mutex_unlock(lock: &panel->follower_lock); |
477 | |
478 | put_device(dev: panel->dev); |
479 | } |
480 | EXPORT_SYMBOL(drm_panel_remove_follower); |
481 | |
482 | static void drm_panel_remove_follower_void(void *follower) |
483 | { |
484 | drm_panel_remove_follower(follower); |
485 | } |
486 | |
487 | /** |
488 | * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower() |
489 | * @follower_dev: The 'struct device' for the follower. |
490 | * @follower: The panel follower descriptor for the follower. |
491 | * |
492 | * Handles calling drm_panel_remove_follower() using devm on the follower_dev. |
493 | * |
494 | * Return: 0 or an error code. |
495 | */ |
496 | int devm_drm_panel_add_follower(struct device *follower_dev, |
497 | struct drm_panel_follower *follower) |
498 | { |
499 | int ret; |
500 | |
501 | ret = drm_panel_add_follower(follower_dev, follower); |
502 | if (ret) |
503 | return ret; |
504 | |
505 | return devm_add_action_or_reset(follower_dev, |
506 | drm_panel_remove_follower_void, follower); |
507 | } |
508 | EXPORT_SYMBOL(devm_drm_panel_add_follower); |
509 | |
510 | #if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) |
511 | /** |
512 | * drm_panel_of_backlight - use backlight device node for backlight |
513 | * @panel: DRM panel |
514 | * |
515 | * Use this function to enable backlight handling if your panel |
516 | * uses device tree and has a backlight phandle. |
517 | * |
518 | * When the panel is enabled backlight will be enabled after a |
519 | * successful call to &drm_panel_funcs.enable() |
520 | * |
521 | * When the panel is disabled backlight will be disabled before the |
522 | * call to &drm_panel_funcs.disable(). |
523 | * |
524 | * A typical implementation for a panel driver supporting device tree |
525 | * will call this function at probe time. Backlight will then be handled |
526 | * transparently without requiring any intervention from the driver. |
527 | * drm_panel_of_backlight() must be called after the call to drm_panel_init(). |
528 | * |
529 | * Return: 0 on success or a negative error code on failure. |
530 | */ |
531 | int drm_panel_of_backlight(struct drm_panel *panel) |
532 | { |
533 | struct backlight_device *backlight; |
534 | |
535 | if (!panel || !panel->dev) |
536 | return -EINVAL; |
537 | |
538 | backlight = devm_of_find_backlight(dev: panel->dev); |
539 | |
540 | if (IS_ERR(ptr: backlight)) |
541 | return PTR_ERR(ptr: backlight); |
542 | |
543 | panel->backlight = backlight; |
544 | return 0; |
545 | } |
546 | EXPORT_SYMBOL(drm_panel_of_backlight); |
547 | #endif |
548 | |
549 | MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>" ); |
550 | MODULE_DESCRIPTION("DRM panel infrastructure" ); |
551 | MODULE_LICENSE("GPL and additional rights" ); |
552 | |