xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_panel.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: drm_panel.c,v 1.5 2021/12/18 23:44:57 riastradh Exp $	*/
2 
3 /*
4  * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sub license,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial portions
15  * of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <sys/cdefs.h>
27 __KERNEL_RCSID(0, "$NetBSD: drm_panel.c,v 1.5 2021/12/18 23:44:57 riastradh Exp $");
28 
29 #include <linux/backlight.h>
30 #include <linux/err.h>
31 #include <linux/module.h>
32 
33 #include <drm/drm_crtc.h>
34 #include <drm/drm_panel.h>
35 #include <drm/drm_print.h>
36 
37 #ifdef __NetBSD__
38 static struct mutex panel_lock;
39 static struct list_head panel_list = LIST_HEAD_INIT(panel_list);
40 #else
41 static DEFINE_MUTEX(panel_lock);
42 static LIST_HEAD(panel_list);
43 #endif
44 
45 #ifdef __NetBSD__
drm_panel_init_lock(void)46 void drm_panel_init_lock(void)
47 {
48 	linux_mutex_init(&panel_lock);
49 }
drm_panel_fini_lock(void)50 void drm_panel_fini_lock(void)
51 {
52 	linux_mutex_destroy(&panel_lock);
53 }
54 #endif
55 
56 /**
57  * DOC: drm panel
58  *
59  * The DRM panel helpers allow drivers to register panel objects with a
60  * central registry and provide functions to retrieve those panels in display
61  * drivers.
62  *
63  * For easy integration into drivers using the &drm_bridge infrastructure please
64  * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
65  */
66 
67 /**
68  * drm_panel_init - initialize a panel
69  * @panel: DRM panel
70  * @dev: parent device of the panel
71  * @funcs: panel operations
72  * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
73  *	the panel interface
74  *
75  * Initialize the panel structure for subsequent registration with
76  * drm_panel_add().
77  */
drm_panel_init(struct drm_panel * panel,struct device * dev,const struct drm_panel_funcs * funcs,int connector_type)78 void drm_panel_init(struct drm_panel *panel, struct device *dev,
79 		    const struct drm_panel_funcs *funcs, int connector_type)
80 {
81 	INIT_LIST_HEAD(&panel->list);
82 	panel->dev = dev;
83 	panel->funcs = funcs;
84 	panel->connector_type = connector_type;
85 }
86 EXPORT_SYMBOL(drm_panel_init);
87 
88 /**
89  * drm_panel_add - add a panel to the global registry
90  * @panel: panel to add
91  *
92  * Add a panel to the global registry so that it can be looked up by display
93  * drivers.
94  *
95  * Return: 0 on success or a negative error code on failure.
96  */
drm_panel_add(struct drm_panel * panel)97 int drm_panel_add(struct drm_panel *panel)
98 {
99 	mutex_lock(&panel_lock);
100 	list_add_tail(&panel->list, &panel_list);
101 	mutex_unlock(&panel_lock);
102 
103 	return 0;
104 }
105 EXPORT_SYMBOL(drm_panel_add);
106 
107 /**
108  * drm_panel_remove - remove a panel from the global registry
109  * @panel: DRM panel
110  *
111  * Removes a panel from the global registry.
112  */
drm_panel_remove(struct drm_panel * panel)113 void drm_panel_remove(struct drm_panel *panel)
114 {
115 	mutex_lock(&panel_lock);
116 	list_del_init(&panel->list);
117 	mutex_unlock(&panel_lock);
118 }
119 EXPORT_SYMBOL(drm_panel_remove);
120 
121 /**
122  * drm_panel_attach - attach a panel to a connector
123  * @panel: DRM panel
124  * @connector: DRM connector
125  *
126  * After obtaining a pointer to a DRM panel a display driver calls this
127  * function to attach a panel to a connector.
128  *
129  * An error is returned if the panel is already attached to another connector.
130  *
131  * When unloading, the driver should detach from the panel by calling
132  * drm_panel_detach().
133  *
134  * Return: 0 on success or a negative error code on failure.
135  */
drm_panel_attach(struct drm_panel * panel,struct drm_connector * connector)136 int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)
137 {
138 	return 0;
139 }
140 EXPORT_SYMBOL(drm_panel_attach);
141 
142 /**
143  * drm_panel_detach - detach a panel from a connector
144  * @panel: DRM panel
145  *
146  * Detaches a panel from the connector it is attached to. If a panel is not
147  * attached to any connector this is effectively a no-op.
148  *
149  * This function should not be called by the panel device itself. It
150  * is only for the drm device that called drm_panel_attach().
151  */
drm_panel_detach(struct drm_panel * panel)152 void drm_panel_detach(struct drm_panel *panel)
153 {
154 }
155 EXPORT_SYMBOL(drm_panel_detach);
156 
157 /**
158  * drm_panel_prepare - power on a panel
159  * @panel: DRM panel
160  *
161  * Calling this function will enable power and deassert any reset signals to
162  * the panel. After this has completed it is possible to communicate with any
163  * integrated circuitry via a command bus.
164  *
165  * Return: 0 on success or a negative error code on failure.
166  */
drm_panel_prepare(struct drm_panel * panel)167 int drm_panel_prepare(struct drm_panel *panel)
168 {
169 	if (!panel)
170 		return -EINVAL;
171 
172 	if (panel->funcs && panel->funcs->prepare)
173 		return panel->funcs->prepare(panel);
174 
175 	return 0;
176 }
177 EXPORT_SYMBOL(drm_panel_prepare);
178 
179 /**
180  * drm_panel_unprepare - power off a panel
181  * @panel: DRM panel
182  *
183  * Calling this function will completely power off a panel (assert the panel's
184  * reset, turn off power supplies, ...). After this function has completed, it
185  * is usually no longer possible to communicate with the panel until another
186  * call to drm_panel_prepare().
187  *
188  * Return: 0 on success or a negative error code on failure.
189  */
drm_panel_unprepare(struct drm_panel * panel)190 int drm_panel_unprepare(struct drm_panel *panel)
191 {
192 	if (!panel)
193 		return -EINVAL;
194 
195 	if (panel->funcs && panel->funcs->unprepare)
196 		return panel->funcs->unprepare(panel);
197 
198 	return 0;
199 }
200 EXPORT_SYMBOL(drm_panel_unprepare);
201 
202 /**
203  * drm_panel_enable - enable a panel
204  * @panel: DRM panel
205  *
206  * Calling this function will cause the panel display drivers to be turned on
207  * and the backlight to be enabled. Content will be visible on screen after
208  * this call completes.
209  *
210  * Return: 0 on success or a negative error code on failure.
211  */
drm_panel_enable(struct drm_panel * panel)212 int drm_panel_enable(struct drm_panel *panel)
213 {
214 	int ret;
215 
216 	if (!panel)
217 		return -EINVAL;
218 
219 	if (panel->funcs && panel->funcs->enable) {
220 		ret = panel->funcs->enable(panel);
221 		if (ret < 0)
222 			return ret;
223 	}
224 
225 	ret = backlight_enable(panel->backlight);
226 	if (ret < 0)
227 		DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
228 			     ret);
229 
230 	return 0;
231 }
232 EXPORT_SYMBOL(drm_panel_enable);
233 
234 /**
235  * drm_panel_disable - disable a panel
236  * @panel: DRM panel
237  *
238  * This will typically turn off the panel's backlight or disable the display
239  * drivers. For smart panels it should still be possible to communicate with
240  * the integrated circuitry via any command bus after this call.
241  *
242  * Return: 0 on success or a negative error code on failure.
243  */
drm_panel_disable(struct drm_panel * panel)244 int drm_panel_disable(struct drm_panel *panel)
245 {
246 	int ret;
247 
248 	if (!panel)
249 		return -EINVAL;
250 
251 	ret = backlight_disable(panel->backlight);
252 	if (ret < 0)
253 		DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
254 			     ret);
255 
256 	if (panel->funcs && panel->funcs->disable)
257 		return panel->funcs->disable(panel);
258 
259 	return 0;
260 }
261 EXPORT_SYMBOL(drm_panel_disable);
262 
263 /**
264  * drm_panel_get_modes - probe the available display modes of a panel
265  * @panel: DRM panel
266  * @connector: DRM connector
267  *
268  * The modes probed from the panel are automatically added to the connector
269  * that the panel is attached to.
270  *
271  * Return: The number of modes available from the panel on success or a
272  * negative error code on failure.
273  */
drm_panel_get_modes(struct drm_panel * panel,struct drm_connector * connector)274 int drm_panel_get_modes(struct drm_panel *panel,
275 			struct drm_connector *connector)
276 {
277 	if (!panel)
278 		return -EINVAL;
279 
280 	if (panel->funcs && panel->funcs->get_modes)
281 		return panel->funcs->get_modes(panel, connector);
282 
283 	return -EOPNOTSUPP;
284 }
285 EXPORT_SYMBOL(drm_panel_get_modes);
286 
287 #ifdef CONFIG_OF
288 /**
289  * of_drm_find_panel - look up a panel using a device tree node
290  * @np: device tree node of the panel
291  *
292  * Searches the set of registered panels for one that matches the given device
293  * tree node. If a matching panel is found, return a pointer to it.
294  *
295  * Return: A pointer to the panel registered for the specified device tree
296  * node or an ERR_PTR() if no panel matching the device tree node can be found.
297  *
298  * Possible error codes returned by this function:
299  *
300  * - EPROBE_DEFER: the panel device has not been probed yet, and the caller
301  *   should retry later
302  * - ENODEV: the device is not available (status != "okay" or "ok")
303  */
of_drm_find_panel(const struct device_node * np)304 struct drm_panel *of_drm_find_panel(const struct device_node *np)
305 {
306 	struct drm_panel *panel;
307 
308 	if (!of_device_is_available(np))
309 		return ERR_PTR(-ENODEV);
310 
311 	mutex_lock(&panel_lock);
312 
313 	list_for_each_entry(panel, &panel_list, list) {
314 		if (panel->dev->of_node == np) {
315 			mutex_unlock(&panel_lock);
316 			return panel;
317 		}
318 	}
319 
320 	mutex_unlock(&panel_lock);
321 	return ERR_PTR(-EPROBE_DEFER);
322 }
323 EXPORT_SYMBOL(of_drm_find_panel);
324 #endif
325 
326 #if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
327 /**
328  * drm_panel_of_backlight - use backlight device node for backlight
329  * @panel: DRM panel
330  *
331  * Use this function to enable backlight handling if your panel
332  * uses device tree and has a backlight phandle.
333  *
334  * When the panel is enabled backlight will be enabled after a
335  * successful call to &drm_panel_funcs.enable()
336  *
337  * When the panel is disabled backlight will be disabled before the
338  * call to &drm_panel_funcs.disable().
339  *
340  * A typical implementation for a panel driver supporting device tree
341  * will call this function at probe time. Backlight will then be handled
342  * transparently without requiring any intervention from the driver.
343  * drm_panel_of_backlight() must be called after the call to drm_panel_init().
344  *
345  * Return: 0 on success or a negative error code on failure.
346  */
drm_panel_of_backlight(struct drm_panel * panel)347 int drm_panel_of_backlight(struct drm_panel *panel)
348 {
349 	struct backlight_device *backlight;
350 
351 	if (!panel || !panel->dev)
352 		return -EINVAL;
353 
354 	backlight = devm_of_find_backlight(panel->dev);
355 
356 	if (IS_ERR(backlight))
357 		return PTR_ERR(backlight);
358 
359 	panel->backlight = backlight;
360 	return 0;
361 }
362 EXPORT_SYMBOL(drm_panel_of_backlight);
363 #endif
364 
365 MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
366 MODULE_DESCRIPTION("DRM panel infrastructure");
367 MODULE_LICENSE("GPL and additional rights");
368