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