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 0 on |
278 | * failure (no modes). |
279 | */ |
280 | int drm_panel_get_modes(struct drm_panel *panel, |
281 | struct drm_connector *connector) |
282 | { |
283 | if (!panel) |
284 | return 0; |
285 | |
286 | if (panel->funcs && panel->funcs->get_modes) { |
287 | int num; |
288 | |
289 | num = panel->funcs->get_modes(panel, connector); |
290 | if (num > 0) |
291 | return num; |
292 | } |
293 | |
294 | return 0; |
295 | } |
296 | EXPORT_SYMBOL(drm_panel_get_modes); |
297 | |
298 | #ifdef CONFIG_OF |
299 | /** |
300 | * of_drm_find_panel - look up a panel using a device tree node |
301 | * @np: device tree node of the panel |
302 | * |
303 | * Searches the set of registered panels for one that matches the given device |
304 | * tree node. If a matching panel is found, return a pointer to it. |
305 | * |
306 | * Return: A pointer to the panel registered for the specified device tree |
307 | * node or an ERR_PTR() if no panel matching the device tree node can be found. |
308 | * |
309 | * Possible error codes returned by this function: |
310 | * |
311 | * - EPROBE_DEFER: the panel device has not been probed yet, and the caller |
312 | * should retry later |
313 | * - ENODEV: the device is not available (status != "okay" or "ok") |
314 | */ |
315 | struct drm_panel *of_drm_find_panel(const struct device_node *np) |
316 | { |
317 | struct drm_panel *panel; |
318 | |
319 | if (!of_device_is_available(device: np)) |
320 | return ERR_PTR(error: -ENODEV); |
321 | |
322 | mutex_lock(&panel_lock); |
323 | |
324 | list_for_each_entry(panel, &panel_list, list) { |
325 | if (panel->dev->of_node == np) { |
326 | mutex_unlock(lock: &panel_lock); |
327 | return panel; |
328 | } |
329 | } |
330 | |
331 | mutex_unlock(lock: &panel_lock); |
332 | return ERR_PTR(error: -EPROBE_DEFER); |
333 | } |
334 | EXPORT_SYMBOL(of_drm_find_panel); |
335 | |
336 | /** |
337 | * of_drm_get_panel_orientation - look up the orientation of the panel through |
338 | * the "rotation" binding from a device tree node |
339 | * @np: device tree node of the panel |
340 | * @orientation: orientation enum to be filled in |
341 | * |
342 | * Looks up the rotation of a panel in the device tree. The orientation of the |
343 | * panel is expressed as a property name "rotation" in the device tree. The |
344 | * rotation in the device tree is counter clockwise. |
345 | * |
346 | * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the |
347 | * rotation property doesn't exist. Return a negative error code on failure. |
348 | */ |
349 | int of_drm_get_panel_orientation(const struct device_node *np, |
350 | enum drm_panel_orientation *orientation) |
351 | { |
352 | int rotation, ret; |
353 | |
354 | ret = of_property_read_u32(np, propname: "rotation" , out_value: &rotation); |
355 | if (ret == -EINVAL) { |
356 | /* Don't return an error if there's no rotation property. */ |
357 | *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; |
358 | return 0; |
359 | } |
360 | |
361 | if (ret < 0) |
362 | return ret; |
363 | |
364 | if (rotation == 0) |
365 | *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; |
366 | else if (rotation == 90) |
367 | *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; |
368 | else if (rotation == 180) |
369 | *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; |
370 | else if (rotation == 270) |
371 | *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; |
372 | else |
373 | return -EINVAL; |
374 | |
375 | return 0; |
376 | } |
377 | EXPORT_SYMBOL(of_drm_get_panel_orientation); |
378 | #endif |
379 | |
380 | /** |
381 | * drm_is_panel_follower() - Check if the device is a panel follower |
382 | * @dev: The 'struct device' to check |
383 | * |
384 | * This checks to see if a device needs to be power sequenced together with |
385 | * a panel using the panel follower API. |
386 | * At the moment panels can only be followed on device tree enabled systems. |
387 | * The "panel" property of the follower points to the panel to be followed. |
388 | * |
389 | * Return: true if we should be power sequenced with a panel; false otherwise. |
390 | */ |
391 | bool drm_is_panel_follower(struct device *dev) |
392 | { |
393 | /* |
394 | * The "panel" property is actually a phandle, but for simplicity we |
395 | * don't bother trying to parse it here. We just need to know if the |
396 | * property is there. |
397 | */ |
398 | return of_property_read_bool(np: dev->of_node, propname: "panel" ); |
399 | } |
400 | EXPORT_SYMBOL(drm_is_panel_follower); |
401 | |
402 | /** |
403 | * drm_panel_add_follower() - Register something to follow panel state. |
404 | * @follower_dev: The 'struct device' for the follower. |
405 | * @follower: The panel follower descriptor for the follower. |
406 | * |
407 | * A panel follower is called right after preparing the panel and right before |
408 | * unpreparing the panel. It's primary intention is to power on an associated |
409 | * touchscreen, though it could be used for any similar devices. Multiple |
410 | * devices are allowed the follow the same panel. |
411 | * |
412 | * If a follower is added to a panel that's already been turned on, the |
413 | * follower's prepare callback is called right away. |
414 | * |
415 | * At the moment panels can only be followed on device tree enabled systems. |
416 | * The "panel" property of the follower points to the panel to be followed. |
417 | * |
418 | * Return: 0 or an error code. Note that -ENODEV means that we detected that |
419 | * follower_dev is not actually following a panel. The caller may |
420 | * choose to ignore this return value if following a panel is optional. |
421 | */ |
422 | int drm_panel_add_follower(struct device *follower_dev, |
423 | struct drm_panel_follower *follower) |
424 | { |
425 | struct device_node *panel_np; |
426 | struct drm_panel *panel; |
427 | int ret; |
428 | |
429 | panel_np = of_parse_phandle(np: follower_dev->of_node, phandle_name: "panel" , index: 0); |
430 | if (!panel_np) |
431 | return -ENODEV; |
432 | |
433 | panel = of_drm_find_panel(panel_np); |
434 | of_node_put(node: panel_np); |
435 | if (IS_ERR(ptr: panel)) |
436 | return PTR_ERR(ptr: panel); |
437 | |
438 | get_device(dev: panel->dev); |
439 | follower->panel = panel; |
440 | |
441 | mutex_lock(&panel->follower_lock); |
442 | |
443 | list_add_tail(new: &follower->list, head: &panel->followers); |
444 | if (panel->prepared) { |
445 | ret = follower->funcs->panel_prepared(follower); |
446 | if (ret < 0) |
447 | dev_info(panel->dev, "%ps failed: %d\n" , |
448 | follower->funcs->panel_prepared, ret); |
449 | } |
450 | |
451 | mutex_unlock(lock: &panel->follower_lock); |
452 | |
453 | return 0; |
454 | } |
455 | EXPORT_SYMBOL(drm_panel_add_follower); |
456 | |
457 | /** |
458 | * drm_panel_remove_follower() - Reverse drm_panel_add_follower(). |
459 | * @follower: The panel follower descriptor for the follower. |
460 | * |
461 | * Undo drm_panel_add_follower(). This includes calling the follower's |
462 | * unprepare function if we're removed from a panel that's currently prepared. |
463 | * |
464 | * Return: 0 or an error code. |
465 | */ |
466 | void drm_panel_remove_follower(struct drm_panel_follower *follower) |
467 | { |
468 | struct drm_panel *panel = follower->panel; |
469 | int ret; |
470 | |
471 | mutex_lock(&panel->follower_lock); |
472 | |
473 | if (panel->prepared) { |
474 | ret = follower->funcs->panel_unpreparing(follower); |
475 | if (ret < 0) |
476 | dev_info(panel->dev, "%ps failed: %d\n" , |
477 | follower->funcs->panel_unpreparing, ret); |
478 | } |
479 | list_del_init(entry: &follower->list); |
480 | |
481 | mutex_unlock(lock: &panel->follower_lock); |
482 | |
483 | put_device(dev: panel->dev); |
484 | } |
485 | EXPORT_SYMBOL(drm_panel_remove_follower); |
486 | |
487 | static void drm_panel_remove_follower_void(void *follower) |
488 | { |
489 | drm_panel_remove_follower(follower); |
490 | } |
491 | |
492 | /** |
493 | * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower() |
494 | * @follower_dev: The 'struct device' for the follower. |
495 | * @follower: The panel follower descriptor for the follower. |
496 | * |
497 | * Handles calling drm_panel_remove_follower() using devm on the follower_dev. |
498 | * |
499 | * Return: 0 or an error code. |
500 | */ |
501 | int devm_drm_panel_add_follower(struct device *follower_dev, |
502 | struct drm_panel_follower *follower) |
503 | { |
504 | int ret; |
505 | |
506 | ret = drm_panel_add_follower(follower_dev, follower); |
507 | if (ret) |
508 | return ret; |
509 | |
510 | return devm_add_action_or_reset(follower_dev, |
511 | drm_panel_remove_follower_void, follower); |
512 | } |
513 | EXPORT_SYMBOL(devm_drm_panel_add_follower); |
514 | |
515 | #if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) |
516 | /** |
517 | * drm_panel_of_backlight - use backlight device node for backlight |
518 | * @panel: DRM panel |
519 | * |
520 | * Use this function to enable backlight handling if your panel |
521 | * uses device tree and has a backlight phandle. |
522 | * |
523 | * When the panel is enabled backlight will be enabled after a |
524 | * successful call to &drm_panel_funcs.enable() |
525 | * |
526 | * When the panel is disabled backlight will be disabled before the |
527 | * call to &drm_panel_funcs.disable(). |
528 | * |
529 | * A typical implementation for a panel driver supporting device tree |
530 | * will call this function at probe time. Backlight will then be handled |
531 | * transparently without requiring any intervention from the driver. |
532 | * drm_panel_of_backlight() must be called after the call to drm_panel_init(). |
533 | * |
534 | * Return: 0 on success or a negative error code on failure. |
535 | */ |
536 | int drm_panel_of_backlight(struct drm_panel *panel) |
537 | { |
538 | struct backlight_device *backlight; |
539 | |
540 | if (!panel || !panel->dev) |
541 | return -EINVAL; |
542 | |
543 | backlight = devm_of_find_backlight(dev: panel->dev); |
544 | |
545 | if (IS_ERR(ptr: backlight)) |
546 | return PTR_ERR(ptr: backlight); |
547 | |
548 | panel->backlight = backlight; |
549 | return 0; |
550 | } |
551 | EXPORT_SYMBOL(drm_panel_of_backlight); |
552 | #endif |
553 | |
554 | MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>" ); |
555 | MODULE_DESCRIPTION("DRM panel infrastructure" ); |
556 | MODULE_LICENSE("GPL and additional rights" ); |
557 | |