xref: /openbmc/linux/drivers/gpu/drm/drm_panel.c (revision a686732d)
1aead40eaSThierry Reding /*
2aead40eaSThierry Reding  * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
3aead40eaSThierry Reding  *
4aead40eaSThierry Reding  * Permission is hereby granted, free of charge, to any person obtaining a
5aead40eaSThierry Reding  * copy of this software and associated documentation files (the "Software"),
6aead40eaSThierry Reding  * to deal in the Software without restriction, including without limitation
7aead40eaSThierry Reding  * the rights to use, copy, modify, merge, publish, distribute, sub license,
8aead40eaSThierry Reding  * and/or sell copies of the Software, and to permit persons to whom the
9aead40eaSThierry Reding  * Software is furnished to do so, subject to the following conditions:
10aead40eaSThierry Reding  *
11aead40eaSThierry Reding  * The above copyright notice and this permission notice (including the
12aead40eaSThierry Reding  * next paragraph) shall be included in all copies or substantial portions
13aead40eaSThierry Reding  * of the Software.
14aead40eaSThierry Reding  *
15aead40eaSThierry Reding  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16aead40eaSThierry Reding  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17aead40eaSThierry Reding  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18aead40eaSThierry Reding  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19aead40eaSThierry Reding  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20aead40eaSThierry Reding  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21aead40eaSThierry Reding  * DEALINGS IN THE SOFTWARE.
22aead40eaSThierry Reding  */
23aead40eaSThierry Reding 
24152dbdeaSSam Ravnborg #include <linux/backlight.h>
25aead40eaSThierry Reding #include <linux/err.h>
26aead40eaSThierry Reding #include <linux/module.h>
27aead40eaSThierry Reding 
28aead40eaSThierry Reding #include <drm/drm_crtc.h>
29aead40eaSThierry Reding #include <drm/drm_panel.h>
30152dbdeaSSam Ravnborg #include <drm/drm_print.h>
31aead40eaSThierry Reding 
32aead40eaSThierry Reding static DEFINE_MUTEX(panel_lock);
33aead40eaSThierry Reding static LIST_HEAD(panel_list);
34aead40eaSThierry Reding 
3583127f67SThierry Reding /**
3683127f67SThierry Reding  * DOC: drm panel
3783127f67SThierry Reding  *
3883127f67SThierry Reding  * The DRM panel helpers allow drivers to register panel objects with a
3983127f67SThierry Reding  * central registry and provide functions to retrieve those panels in display
4083127f67SThierry Reding  * drivers.
410aa5eb3aSDaniel Vetter  *
420aa5eb3aSDaniel Vetter  * For easy integration into drivers using the &drm_bridge infrastructure please
430aa5eb3aSDaniel Vetter  * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
4483127f67SThierry Reding  */
4583127f67SThierry Reding 
4683127f67SThierry Reding /**
4783127f67SThierry Reding  * drm_panel_init - initialize a panel
4883127f67SThierry Reding  * @panel: DRM panel
496dbe0c4bSLaurent Pinchart  * @dev: parent device of the panel
506dbe0c4bSLaurent Pinchart  * @funcs: panel operations
519a2654c0SLaurent Pinchart  * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
529a2654c0SLaurent Pinchart  *	the panel interface
5383127f67SThierry Reding  *
546dbe0c4bSLaurent Pinchart  * Initialize the panel structure for subsequent registration with
556dbe0c4bSLaurent Pinchart  * drm_panel_add().
5683127f67SThierry Reding  */
drm_panel_init(struct drm_panel * panel,struct device * dev,const struct drm_panel_funcs * funcs,int connector_type)576dbe0c4bSLaurent Pinchart void drm_panel_init(struct drm_panel *panel, struct device *dev,
589a2654c0SLaurent Pinchart 		    const struct drm_panel_funcs *funcs, int connector_type)
59aead40eaSThierry Reding {
60aead40eaSThierry Reding 	INIT_LIST_HEAD(&panel->list);
61de087416SDouglas Anderson 	INIT_LIST_HEAD(&panel->followers);
62de087416SDouglas Anderson 	mutex_init(&panel->follower_lock);
636dbe0c4bSLaurent Pinchart 	panel->dev = dev;
646dbe0c4bSLaurent Pinchart 	panel->funcs = funcs;
659a2654c0SLaurent Pinchart 	panel->connector_type = connector_type;
66aead40eaSThierry Reding }
67aead40eaSThierry Reding EXPORT_SYMBOL(drm_panel_init);
68aead40eaSThierry Reding 
6983127f67SThierry Reding /**
7083127f67SThierry Reding  * drm_panel_add - add a panel to the global registry
7183127f67SThierry Reding  * @panel: panel to add
7283127f67SThierry Reding  *
7383127f67SThierry Reding  * Add a panel to the global registry so that it can be looked up by display
7483127f67SThierry Reding  * drivers.
7583127f67SThierry Reding  */
drm_panel_add(struct drm_panel * panel)76c3ee8c65SBernard Zhao void drm_panel_add(struct drm_panel *panel)
77aead40eaSThierry Reding {
78aead40eaSThierry Reding 	mutex_lock(&panel_lock);
79aead40eaSThierry Reding 	list_add_tail(&panel->list, &panel_list);
80aead40eaSThierry Reding 	mutex_unlock(&panel_lock);
81aead40eaSThierry Reding }
82aead40eaSThierry Reding EXPORT_SYMBOL(drm_panel_add);
83aead40eaSThierry Reding 
8483127f67SThierry Reding /**
8583127f67SThierry Reding  * drm_panel_remove - remove a panel from the global registry
8683127f67SThierry Reding  * @panel: DRM panel
8783127f67SThierry Reding  *
8883127f67SThierry Reding  * Removes a panel from the global registry.
8983127f67SThierry Reding  */
drm_panel_remove(struct drm_panel * panel)90aead40eaSThierry Reding void drm_panel_remove(struct drm_panel *panel)
91aead40eaSThierry Reding {
92aead40eaSThierry Reding 	mutex_lock(&panel_lock);
93aead40eaSThierry Reding 	list_del_init(&panel->list);
94aead40eaSThierry Reding 	mutex_unlock(&panel_lock);
95aead40eaSThierry Reding }
96aead40eaSThierry Reding EXPORT_SYMBOL(drm_panel_remove);
97aead40eaSThierry Reding 
9883127f67SThierry Reding /**
997a833d30SSam Ravnborg  * drm_panel_prepare - power on a panel
1007a833d30SSam Ravnborg  * @panel: DRM panel
1017a833d30SSam Ravnborg  *
1027a833d30SSam Ravnborg  * Calling this function will enable power and deassert any reset signals to
1037a833d30SSam Ravnborg  * the panel. After this has completed it is possible to communicate with any
1047a833d30SSam Ravnborg  * integrated circuitry via a command bus.
1057a833d30SSam Ravnborg  *
1067a833d30SSam Ravnborg  * Return: 0 on success or a negative error code on failure.
1077a833d30SSam Ravnborg  */
drm_panel_prepare(struct drm_panel * panel)1087a833d30SSam Ravnborg int drm_panel_prepare(struct drm_panel *panel)
1097a833d30SSam Ravnborg {
110de087416SDouglas Anderson 	struct drm_panel_follower *follower;
111d2aacaf0SDouglas Anderson 	int ret;
112d2aacaf0SDouglas Anderson 
1135dce87a9SSam Ravnborg 	if (!panel)
1145dce87a9SSam Ravnborg 		return -EINVAL;
1155dce87a9SSam Ravnborg 
116d2aacaf0SDouglas Anderson 	if (panel->prepared) {
117d2aacaf0SDouglas Anderson 		dev_warn(panel->dev, "Skipping prepare of already prepared panel\n");
118d2aacaf0SDouglas Anderson 		return 0;
119d2aacaf0SDouglas Anderson 	}
120d2aacaf0SDouglas Anderson 
121de087416SDouglas Anderson 	mutex_lock(&panel->follower_lock);
122de087416SDouglas Anderson 
123d2aacaf0SDouglas Anderson 	if (panel->funcs && panel->funcs->prepare) {
124d2aacaf0SDouglas Anderson 		ret = panel->funcs->prepare(panel);
125d2aacaf0SDouglas Anderson 		if (ret < 0)
126de087416SDouglas Anderson 			goto exit;
127d2aacaf0SDouglas Anderson 	}
128d2aacaf0SDouglas Anderson 	panel->prepared = true;
1297a833d30SSam Ravnborg 
130de087416SDouglas Anderson 	list_for_each_entry(follower, &panel->followers, list) {
131de087416SDouglas Anderson 		ret = follower->funcs->panel_prepared(follower);
132de087416SDouglas Anderson 		if (ret < 0)
133de087416SDouglas Anderson 			dev_info(panel->dev, "%ps failed: %d\n",
134de087416SDouglas Anderson 				 follower->funcs->panel_prepared, ret);
135de087416SDouglas Anderson 	}
136de087416SDouglas Anderson 
137de087416SDouglas Anderson 	ret = 0;
138de087416SDouglas Anderson exit:
139de087416SDouglas Anderson 	mutex_unlock(&panel->follower_lock);
140de087416SDouglas Anderson 
141de087416SDouglas Anderson 	return ret;
1427a833d30SSam Ravnborg }
1437a833d30SSam Ravnborg EXPORT_SYMBOL(drm_panel_prepare);
1447a833d30SSam Ravnborg 
1457a833d30SSam Ravnborg /**
1467a833d30SSam Ravnborg  * drm_panel_unprepare - power off a panel
1477a833d30SSam Ravnborg  * @panel: DRM panel
1487a833d30SSam Ravnborg  *
1497a833d30SSam Ravnborg  * Calling this function will completely power off a panel (assert the panel's
1507a833d30SSam Ravnborg  * reset, turn off power supplies, ...). After this function has completed, it
1517a833d30SSam Ravnborg  * is usually no longer possible to communicate with the panel until another
1527a833d30SSam Ravnborg  * call to drm_panel_prepare().
1537a833d30SSam Ravnborg  *
1547a833d30SSam Ravnborg  * Return: 0 on success or a negative error code on failure.
1557a833d30SSam Ravnborg  */
drm_panel_unprepare(struct drm_panel * panel)1567a833d30SSam Ravnborg int drm_panel_unprepare(struct drm_panel *panel)
1577a833d30SSam Ravnborg {
158de087416SDouglas Anderson 	struct drm_panel_follower *follower;
159d2aacaf0SDouglas Anderson 	int ret;
160d2aacaf0SDouglas Anderson 
1615dce87a9SSam Ravnborg 	if (!panel)
1625dce87a9SSam Ravnborg 		return -EINVAL;
1635dce87a9SSam Ravnborg 
164d2aacaf0SDouglas Anderson 	if (!panel->prepared) {
165d2aacaf0SDouglas Anderson 		dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n");
166d2aacaf0SDouglas Anderson 		return 0;
167d2aacaf0SDouglas Anderson 	}
168d2aacaf0SDouglas Anderson 
169de087416SDouglas Anderson 	mutex_lock(&panel->follower_lock);
170de087416SDouglas Anderson 
171de087416SDouglas Anderson 	list_for_each_entry(follower, &panel->followers, list) {
172de087416SDouglas Anderson 		ret = follower->funcs->panel_unpreparing(follower);
173de087416SDouglas Anderson 		if (ret < 0)
174de087416SDouglas Anderson 			dev_info(panel->dev, "%ps failed: %d\n",
175de087416SDouglas Anderson 				 follower->funcs->panel_unpreparing, ret);
176de087416SDouglas Anderson 	}
177de087416SDouglas Anderson 
178d2aacaf0SDouglas Anderson 	if (panel->funcs && panel->funcs->unprepare) {
179d2aacaf0SDouglas Anderson 		ret = panel->funcs->unprepare(panel);
180d2aacaf0SDouglas Anderson 		if (ret < 0)
181de087416SDouglas Anderson 			goto exit;
182d2aacaf0SDouglas Anderson 	}
183d2aacaf0SDouglas Anderson 	panel->prepared = false;
1847a833d30SSam Ravnborg 
185de087416SDouglas Anderson 	ret = 0;
186de087416SDouglas Anderson exit:
187de087416SDouglas Anderson 	mutex_unlock(&panel->follower_lock);
188de087416SDouglas Anderson 
189de087416SDouglas Anderson 	return ret;
1907a833d30SSam Ravnborg }
1917a833d30SSam Ravnborg EXPORT_SYMBOL(drm_panel_unprepare);
1927a833d30SSam Ravnborg 
1937a833d30SSam Ravnborg /**
1947a833d30SSam Ravnborg  * drm_panel_enable - enable a panel
1957a833d30SSam Ravnborg  * @panel: DRM panel
1967a833d30SSam Ravnborg  *
1977a833d30SSam Ravnborg  * Calling this function will cause the panel display drivers to be turned on
1987a833d30SSam Ravnborg  * and the backlight to be enabled. Content will be visible on screen after
1997a833d30SSam Ravnborg  * this call completes.
2007a833d30SSam Ravnborg  *
2017a833d30SSam Ravnborg  * Return: 0 on success or a negative error code on failure.
2027a833d30SSam Ravnborg  */
drm_panel_enable(struct drm_panel * panel)2037a833d30SSam Ravnborg int drm_panel_enable(struct drm_panel *panel)
2047a833d30SSam Ravnborg {
205152dbdeaSSam Ravnborg 	int ret;
206152dbdeaSSam Ravnborg 
2075dce87a9SSam Ravnborg 	if (!panel)
2085dce87a9SSam Ravnborg 		return -EINVAL;
2095dce87a9SSam Ravnborg 
210d2aacaf0SDouglas Anderson 	if (panel->enabled) {
211d2aacaf0SDouglas Anderson 		dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
212d2aacaf0SDouglas Anderson 		return 0;
213d2aacaf0SDouglas Anderson 	}
214d2aacaf0SDouglas Anderson 
215152dbdeaSSam Ravnborg 	if (panel->funcs && panel->funcs->enable) {
216152dbdeaSSam Ravnborg 		ret = panel->funcs->enable(panel);
217152dbdeaSSam Ravnborg 		if (ret < 0)
218152dbdeaSSam Ravnborg 			return ret;
219152dbdeaSSam Ravnborg 	}
220d2aacaf0SDouglas Anderson 	panel->enabled = true;
221152dbdeaSSam Ravnborg 
222152dbdeaSSam Ravnborg 	ret = backlight_enable(panel->backlight);
223152dbdeaSSam Ravnborg 	if (ret < 0)
224152dbdeaSSam Ravnborg 		DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
225152dbdeaSSam Ravnborg 			     ret);
2267a833d30SSam Ravnborg 
2275dce87a9SSam Ravnborg 	return 0;
2287a833d30SSam Ravnborg }
2297a833d30SSam Ravnborg EXPORT_SYMBOL(drm_panel_enable);
2307a833d30SSam Ravnborg 
2317a833d30SSam Ravnborg /**
2327a833d30SSam Ravnborg  * drm_panel_disable - disable a panel
2337a833d30SSam Ravnborg  * @panel: DRM panel
2347a833d30SSam Ravnborg  *
2357a833d30SSam Ravnborg  * This will typically turn off the panel's backlight or disable the display
2367a833d30SSam Ravnborg  * drivers. For smart panels it should still be possible to communicate with
2377a833d30SSam Ravnborg  * the integrated circuitry via any command bus after this call.
2387a833d30SSam Ravnborg  *
2397a833d30SSam Ravnborg  * Return: 0 on success or a negative error code on failure.
2407a833d30SSam Ravnborg  */
drm_panel_disable(struct drm_panel * panel)2417a833d30SSam Ravnborg int drm_panel_disable(struct drm_panel *panel)
2427a833d30SSam Ravnborg {
243152dbdeaSSam Ravnborg 	int ret;
244152dbdeaSSam Ravnborg 
2455dce87a9SSam Ravnborg 	if (!panel)
2465dce87a9SSam Ravnborg 		return -EINVAL;
2475dce87a9SSam Ravnborg 
248d2aacaf0SDouglas Anderson 	if (!panel->enabled) {
249d2aacaf0SDouglas Anderson 		dev_warn(panel->dev, "Skipping disable of already disabled panel\n");
250d2aacaf0SDouglas Anderson 		return 0;
251d2aacaf0SDouglas Anderson 	}
252d2aacaf0SDouglas Anderson 
253152dbdeaSSam Ravnborg 	ret = backlight_disable(panel->backlight);
254152dbdeaSSam Ravnborg 	if (ret < 0)
255152dbdeaSSam Ravnborg 		DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
256152dbdeaSSam Ravnborg 			     ret);
257152dbdeaSSam Ravnborg 
258d2aacaf0SDouglas Anderson 	if (panel->funcs && panel->funcs->disable) {
259d2aacaf0SDouglas Anderson 		ret = panel->funcs->disable(panel);
260d2aacaf0SDouglas Anderson 		if (ret < 0)
261d2aacaf0SDouglas Anderson 			return ret;
262d2aacaf0SDouglas Anderson 	}
263d2aacaf0SDouglas Anderson 	panel->enabled = false;
2647a833d30SSam Ravnborg 
2655dce87a9SSam Ravnborg 	return 0;
2667a833d30SSam Ravnborg }
2677a833d30SSam Ravnborg EXPORT_SYMBOL(drm_panel_disable);
2687a833d30SSam Ravnborg 
2697a833d30SSam Ravnborg /**
2707a833d30SSam Ravnborg  * drm_panel_get_modes - probe the available display modes of a panel
2717a833d30SSam Ravnborg  * @panel: DRM panel
27206c4a9c2SSam Ravnborg  * @connector: DRM connector
2737a833d30SSam Ravnborg  *
2747a833d30SSam Ravnborg  * The modes probed from the panel are automatically added to the connector
2757a833d30SSam Ravnborg  * that the panel is attached to.
2767a833d30SSam Ravnborg  *
277a686732dSJani Nikula  * Return: The number of modes available from the panel on success, or 0 on
278a686732dSJani Nikula  * failure (no modes).
2797a833d30SSam Ravnborg  */
drm_panel_get_modes(struct drm_panel * panel,struct drm_connector * connector)28006c4a9c2SSam Ravnborg int drm_panel_get_modes(struct drm_panel *panel,
28106c4a9c2SSam Ravnborg 			struct drm_connector *connector)
2827a833d30SSam Ravnborg {
2835dce87a9SSam Ravnborg 	if (!panel)
284a686732dSJani Nikula 		return 0;
2855dce87a9SSam Ravnborg 
286a686732dSJani Nikula 	if (panel->funcs && panel->funcs->get_modes) {
287a686732dSJani Nikula 		int num;
2887a833d30SSam Ravnborg 
289a686732dSJani Nikula 		num = panel->funcs->get_modes(panel, connector);
290a686732dSJani Nikula 		if (num > 0)
291a686732dSJani Nikula 			return num;
292a686732dSJani Nikula 	}
293a686732dSJani Nikula 
294a686732dSJani Nikula 	return 0;
2957a833d30SSam Ravnborg }
2967a833d30SSam Ravnborg EXPORT_SYMBOL(drm_panel_get_modes);
2977a833d30SSam Ravnborg 
298aead40eaSThierry Reding #ifdef CONFIG_OF
29983127f67SThierry Reding /**
30083127f67SThierry Reding  * of_drm_find_panel - look up a panel using a device tree node
30183127f67SThierry Reding  * @np: device tree node of the panel
30283127f67SThierry Reding  *
30383127f67SThierry Reding  * Searches the set of registered panels for one that matches the given device
30483127f67SThierry Reding  * tree node. If a matching panel is found, return a pointer to it.
30583127f67SThierry Reding  *
30683127f67SThierry Reding  * Return: A pointer to the panel registered for the specified device tree
3075fa8e4a2SBoris Brezillon  * node or an ERR_PTR() if no panel matching the device tree node can be found.
3083eb3cd04SSean Paul  *
309c59eb3cfSBoris Brezillon  * Possible error codes returned by this function:
3103eb3cd04SSean Paul  *
311c59eb3cfSBoris Brezillon  * - EPROBE_DEFER: the panel device has not been probed yet, and the caller
312c59eb3cfSBoris Brezillon  *   should retry later
313c59eb3cfSBoris Brezillon  * - ENODEV: the device is not available (status != "okay" or "ok")
31483127f67SThierry Reding  */
of_drm_find_panel(const struct device_node * np)315327bc443SLaurent Pinchart struct drm_panel *of_drm_find_panel(const struct device_node *np)
316aead40eaSThierry Reding {
317aead40eaSThierry Reding 	struct drm_panel *panel;
318aead40eaSThierry Reding 
319c59eb3cfSBoris Brezillon 	if (!of_device_is_available(np))
320c59eb3cfSBoris Brezillon 		return ERR_PTR(-ENODEV);
321c59eb3cfSBoris Brezillon 
322aead40eaSThierry Reding 	mutex_lock(&panel_lock);
323aead40eaSThierry Reding 
324aead40eaSThierry Reding 	list_for_each_entry(panel, &panel_list, list) {
325aead40eaSThierry Reding 		if (panel->dev->of_node == np) {
326aead40eaSThierry Reding 			mutex_unlock(&panel_lock);
327aead40eaSThierry Reding 			return panel;
328aead40eaSThierry Reding 		}
329aead40eaSThierry Reding 	}
330aead40eaSThierry Reding 
331aead40eaSThierry Reding 	mutex_unlock(&panel_lock);
3325fa8e4a2SBoris Brezillon 	return ERR_PTR(-EPROBE_DEFER);
333aead40eaSThierry Reding }
334aead40eaSThierry Reding EXPORT_SYMBOL(of_drm_find_panel);
3355f3e7503SDerek Basehore 
3365f3e7503SDerek Basehore /**
3375f3e7503SDerek Basehore  * of_drm_get_panel_orientation - look up the orientation of the panel through
3385f3e7503SDerek Basehore  * the "rotation" binding from a device tree node
3395f3e7503SDerek Basehore  * @np: device tree node of the panel
3405f3e7503SDerek Basehore  * @orientation: orientation enum to be filled in
3415f3e7503SDerek Basehore  *
3425f3e7503SDerek Basehore  * Looks up the rotation of a panel in the device tree. The orientation of the
3435f3e7503SDerek Basehore  * panel is expressed as a property name "rotation" in the device tree. The
3445f3e7503SDerek Basehore  * rotation in the device tree is counter clockwise.
3455f3e7503SDerek Basehore  *
3465f3e7503SDerek Basehore  * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
3475f3e7503SDerek Basehore  * rotation property doesn't exist. Return a negative error code on failure.
3485f3e7503SDerek Basehore  */
of_drm_get_panel_orientation(const struct device_node * np,enum drm_panel_orientation * orientation)3495f3e7503SDerek Basehore int of_drm_get_panel_orientation(const struct device_node *np,
3505f3e7503SDerek Basehore 				 enum drm_panel_orientation *orientation)
3515f3e7503SDerek Basehore {
3525f3e7503SDerek Basehore 	int rotation, ret;
3535f3e7503SDerek Basehore 
3545f3e7503SDerek Basehore 	ret = of_property_read_u32(np, "rotation", &rotation);
3555f3e7503SDerek Basehore 	if (ret == -EINVAL) {
3565f3e7503SDerek Basehore 		/* Don't return an error if there's no rotation property. */
3575f3e7503SDerek Basehore 		*orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
3585f3e7503SDerek Basehore 		return 0;
3595f3e7503SDerek Basehore 	}
3605f3e7503SDerek Basehore 
3615f3e7503SDerek Basehore 	if (ret < 0)
3625f3e7503SDerek Basehore 		return ret;
3635f3e7503SDerek Basehore 
3645f3e7503SDerek Basehore 	if (rotation == 0)
3655f3e7503SDerek Basehore 		*orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
3665f3e7503SDerek Basehore 	else if (rotation == 90)
3675f3e7503SDerek Basehore 		*orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
3685f3e7503SDerek Basehore 	else if (rotation == 180)
3695f3e7503SDerek Basehore 		*orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
3705f3e7503SDerek Basehore 	else if (rotation == 270)
3715f3e7503SDerek Basehore 		*orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
3725f3e7503SDerek Basehore 	else
3735f3e7503SDerek Basehore 		return -EINVAL;
3745f3e7503SDerek Basehore 
3755f3e7503SDerek Basehore 	return 0;
3765f3e7503SDerek Basehore }
3775f3e7503SDerek Basehore EXPORT_SYMBOL(of_drm_get_panel_orientation);
378aead40eaSThierry Reding #endif
379aead40eaSThierry Reding 
380de087416SDouglas Anderson /**
381de087416SDouglas Anderson  * drm_is_panel_follower() - Check if the device is a panel follower
382de087416SDouglas Anderson  * @dev: The 'struct device' to check
383de087416SDouglas Anderson  *
384de087416SDouglas Anderson  * This checks to see if a device needs to be power sequenced together with
385de087416SDouglas Anderson  * a panel using the panel follower API.
386de087416SDouglas Anderson  * At the moment panels can only be followed on device tree enabled systems.
387de087416SDouglas Anderson  * The "panel" property of the follower points to the panel to be followed.
388de087416SDouglas Anderson  *
389de087416SDouglas Anderson  * Return: true if we should be power sequenced with a panel; false otherwise.
390de087416SDouglas Anderson  */
drm_is_panel_follower(struct device * dev)391de087416SDouglas Anderson bool drm_is_panel_follower(struct device *dev)
392de087416SDouglas Anderson {
393de087416SDouglas Anderson 	/*
394de087416SDouglas Anderson 	 * The "panel" property is actually a phandle, but for simplicity we
395de087416SDouglas Anderson 	 * don't bother trying to parse it here. We just need to know if the
396de087416SDouglas Anderson 	 * property is there.
397de087416SDouglas Anderson 	 */
398de087416SDouglas Anderson 	return of_property_read_bool(dev->of_node, "panel");
399de087416SDouglas Anderson }
400de087416SDouglas Anderson EXPORT_SYMBOL(drm_is_panel_follower);
401de087416SDouglas Anderson 
402de087416SDouglas Anderson /**
403de087416SDouglas Anderson  * drm_panel_add_follower() - Register something to follow panel state.
404de087416SDouglas Anderson  * @follower_dev: The 'struct device' for the follower.
405de087416SDouglas Anderson  * @follower:     The panel follower descriptor for the follower.
406de087416SDouglas Anderson  *
407de087416SDouglas Anderson  * A panel follower is called right after preparing the panel and right before
408de087416SDouglas Anderson  * unpreparing the panel. It's primary intention is to power on an associated
409de087416SDouglas Anderson  * touchscreen, though it could be used for any similar devices. Multiple
410de087416SDouglas Anderson  * devices are allowed the follow the same panel.
411de087416SDouglas Anderson  *
412de087416SDouglas Anderson  * If a follower is added to a panel that's already been turned on, the
413de087416SDouglas Anderson  * follower's prepare callback is called right away.
414de087416SDouglas Anderson  *
415de087416SDouglas Anderson  * At the moment panels can only be followed on device tree enabled systems.
416de087416SDouglas Anderson  * The "panel" property of the follower points to the panel to be followed.
417de087416SDouglas Anderson  *
418de087416SDouglas Anderson  * Return: 0 or an error code. Note that -ENODEV means that we detected that
419de087416SDouglas Anderson  *         follower_dev is not actually following a panel. The caller may
420de087416SDouglas Anderson  *         choose to ignore this return value if following a panel is optional.
421de087416SDouglas Anderson  */
drm_panel_add_follower(struct device * follower_dev,struct drm_panel_follower * follower)422de087416SDouglas Anderson int drm_panel_add_follower(struct device *follower_dev,
423de087416SDouglas Anderson 			   struct drm_panel_follower *follower)
424de087416SDouglas Anderson {
425de087416SDouglas Anderson 	struct device_node *panel_np;
426de087416SDouglas Anderson 	struct drm_panel *panel;
427de087416SDouglas Anderson 	int ret;
428de087416SDouglas Anderson 
429de087416SDouglas Anderson 	panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0);
430de087416SDouglas Anderson 	if (!panel_np)
431de087416SDouglas Anderson 		return -ENODEV;
432de087416SDouglas Anderson 
433de087416SDouglas Anderson 	panel = of_drm_find_panel(panel_np);
434de087416SDouglas Anderson 	of_node_put(panel_np);
435de087416SDouglas Anderson 	if (IS_ERR(panel))
436de087416SDouglas Anderson 		return PTR_ERR(panel);
437de087416SDouglas Anderson 
438de087416SDouglas Anderson 	get_device(panel->dev);
439de087416SDouglas Anderson 	follower->panel = panel;
440de087416SDouglas Anderson 
441de087416SDouglas Anderson 	mutex_lock(&panel->follower_lock);
442de087416SDouglas Anderson 
443de087416SDouglas Anderson 	list_add_tail(&follower->list, &panel->followers);
444de087416SDouglas Anderson 	if (panel->prepared) {
445de087416SDouglas Anderson 		ret = follower->funcs->panel_prepared(follower);
446de087416SDouglas Anderson 		if (ret < 0)
447de087416SDouglas Anderson 			dev_info(panel->dev, "%ps failed: %d\n",
448de087416SDouglas Anderson 				 follower->funcs->panel_prepared, ret);
449de087416SDouglas Anderson 	}
450de087416SDouglas Anderson 
451de087416SDouglas Anderson 	mutex_unlock(&panel->follower_lock);
452de087416SDouglas Anderson 
453de087416SDouglas Anderson 	return 0;
454de087416SDouglas Anderson }
455de087416SDouglas Anderson EXPORT_SYMBOL(drm_panel_add_follower);
456de087416SDouglas Anderson 
457de087416SDouglas Anderson /**
458de087416SDouglas Anderson  * drm_panel_remove_follower() - Reverse drm_panel_add_follower().
459de087416SDouglas Anderson  * @follower:     The panel follower descriptor for the follower.
460de087416SDouglas Anderson  *
461de087416SDouglas Anderson  * Undo drm_panel_add_follower(). This includes calling the follower's
462de087416SDouglas Anderson  * unprepare function if we're removed from a panel that's currently prepared.
463de087416SDouglas Anderson  *
464de087416SDouglas Anderson  * Return: 0 or an error code.
465de087416SDouglas Anderson  */
drm_panel_remove_follower(struct drm_panel_follower * follower)466de087416SDouglas Anderson void drm_panel_remove_follower(struct drm_panel_follower *follower)
467de087416SDouglas Anderson {
468de087416SDouglas Anderson 	struct drm_panel *panel = follower->panel;
469de087416SDouglas Anderson 	int ret;
470de087416SDouglas Anderson 
471de087416SDouglas Anderson 	mutex_lock(&panel->follower_lock);
472de087416SDouglas Anderson 
473de087416SDouglas Anderson 	if (panel->prepared) {
474de087416SDouglas Anderson 		ret = follower->funcs->panel_unpreparing(follower);
475de087416SDouglas Anderson 		if (ret < 0)
476de087416SDouglas Anderson 			dev_info(panel->dev, "%ps failed: %d\n",
477de087416SDouglas Anderson 				 follower->funcs->panel_unpreparing, ret);
478de087416SDouglas Anderson 	}
479de087416SDouglas Anderson 	list_del_init(&follower->list);
480de087416SDouglas Anderson 
481de087416SDouglas Anderson 	mutex_unlock(&panel->follower_lock);
482de087416SDouglas Anderson 
483de087416SDouglas Anderson 	put_device(panel->dev);
484de087416SDouglas Anderson }
485de087416SDouglas Anderson EXPORT_SYMBOL(drm_panel_remove_follower);
486de087416SDouglas Anderson 
drm_panel_remove_follower_void(void * follower)487de087416SDouglas Anderson static void drm_panel_remove_follower_void(void *follower)
488de087416SDouglas Anderson {
489de087416SDouglas Anderson 	drm_panel_remove_follower(follower);
490de087416SDouglas Anderson }
491de087416SDouglas Anderson 
492de087416SDouglas Anderson /**
493de087416SDouglas Anderson  * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower()
494de087416SDouglas Anderson  * @follower_dev: The 'struct device' for the follower.
495de087416SDouglas Anderson  * @follower:     The panel follower descriptor for the follower.
496de087416SDouglas Anderson  *
497de087416SDouglas Anderson  * Handles calling drm_panel_remove_follower() using devm on the follower_dev.
498de087416SDouglas Anderson  *
499de087416SDouglas Anderson  * Return: 0 or an error code.
500de087416SDouglas Anderson  */
devm_drm_panel_add_follower(struct device * follower_dev,struct drm_panel_follower * follower)501de087416SDouglas Anderson int devm_drm_panel_add_follower(struct device *follower_dev,
502de087416SDouglas Anderson 				struct drm_panel_follower *follower)
503de087416SDouglas Anderson {
504de087416SDouglas Anderson 	int ret;
505de087416SDouglas Anderson 
506de087416SDouglas Anderson 	ret = drm_panel_add_follower(follower_dev, follower);
507de087416SDouglas Anderson 	if (ret)
508de087416SDouglas Anderson 		return ret;
509de087416SDouglas Anderson 
510de087416SDouglas Anderson 	return devm_add_action_or_reset(follower_dev,
511de087416SDouglas Anderson 					drm_panel_remove_follower_void, follower);
512de087416SDouglas Anderson }
513de087416SDouglas Anderson EXPORT_SYMBOL(devm_drm_panel_add_follower);
514de087416SDouglas Anderson 
5154a34a9dcSAndy Shevchenko #if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
516152dbdeaSSam Ravnborg /**
517152dbdeaSSam Ravnborg  * drm_panel_of_backlight - use backlight device node for backlight
518152dbdeaSSam Ravnborg  * @panel: DRM panel
519152dbdeaSSam Ravnborg  *
520152dbdeaSSam Ravnborg  * Use this function to enable backlight handling if your panel
521152dbdeaSSam Ravnborg  * uses device tree and has a backlight phandle.
522152dbdeaSSam Ravnborg  *
523152dbdeaSSam Ravnborg  * When the panel is enabled backlight will be enabled after a
524152dbdeaSSam Ravnborg  * successful call to &drm_panel_funcs.enable()
525152dbdeaSSam Ravnborg  *
526152dbdeaSSam Ravnborg  * When the panel is disabled backlight will be disabled before the
527152dbdeaSSam Ravnborg  * call to &drm_panel_funcs.disable().
528152dbdeaSSam Ravnborg  *
529152dbdeaSSam Ravnborg  * A typical implementation for a panel driver supporting device tree
530152dbdeaSSam Ravnborg  * will call this function at probe time. Backlight will then be handled
531152dbdeaSSam Ravnborg  * transparently without requiring any intervention from the driver.
532152dbdeaSSam Ravnborg  * drm_panel_of_backlight() must be called after the call to drm_panel_init().
533152dbdeaSSam Ravnborg  *
534152dbdeaSSam Ravnborg  * Return: 0 on success or a negative error code on failure.
535152dbdeaSSam Ravnborg  */
drm_panel_of_backlight(struct drm_panel * panel)536152dbdeaSSam Ravnborg int drm_panel_of_backlight(struct drm_panel *panel)
537152dbdeaSSam Ravnborg {
538152dbdeaSSam Ravnborg 	struct backlight_device *backlight;
539152dbdeaSSam Ravnborg 
540152dbdeaSSam Ravnborg 	if (!panel || !panel->dev)
541152dbdeaSSam Ravnborg 		return -EINVAL;
542152dbdeaSSam Ravnborg 
543152dbdeaSSam Ravnborg 	backlight = devm_of_find_backlight(panel->dev);
544152dbdeaSSam Ravnborg 
545152dbdeaSSam Ravnborg 	if (IS_ERR(backlight))
546152dbdeaSSam Ravnborg 		return PTR_ERR(backlight);
547152dbdeaSSam Ravnborg 
548152dbdeaSSam Ravnborg 	panel->backlight = backlight;
549152dbdeaSSam Ravnborg 	return 0;
550152dbdeaSSam Ravnborg }
551152dbdeaSSam Ravnborg EXPORT_SYMBOL(drm_panel_of_backlight);
552152dbdeaSSam Ravnborg #endif
553152dbdeaSSam Ravnborg 
554aead40eaSThierry Reding MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
555aead40eaSThierry Reding MODULE_DESCRIPTION("DRM panel infrastructure");
556aead40eaSThierry Reding MODULE_LICENSE("GPL and additional rights");
557