blob: 08517740e6a0236757b4a22b24c4e776090bc0bc [file] [log] [blame]
Eric Anholt13dfc052017-06-02 13:25:14 -07001/*
2 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
3 * Copyright (C) 2017 Broadcom
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 */
10
Eric Anholt13dfc052017-06-02 13:25:14 -070011#include <drm/drm_atomic_helper.h>
12#include <drm/drm_connector.h>
Eric Anholt13dfc052017-06-02 13:25:14 -070013#include <drm/drm_encoder.h>
14#include <drm/drm_modeset_helper_vtables.h>
15#include <drm/drm_panel.h>
Sam Ravnborg95b60802019-05-19 20:36:36 +020016#include <drm/drm_print.h>
Sabyasachi Gupta78666ba2019-05-16 20:55:56 +053017#include <drm/drm_probe_helper.h>
Eric Anholt13dfc052017-06-02 13:25:14 -070018
19struct panel_bridge {
20 struct drm_bridge bridge;
21 struct drm_connector connector;
22 struct drm_panel *panel;
23 u32 connector_type;
24};
25
26static inline struct panel_bridge *
27drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
28{
29 return container_of(bridge, struct panel_bridge, bridge);
30}
31
32static inline struct panel_bridge *
33drm_connector_to_panel_bridge(struct drm_connector *connector)
34{
35 return container_of(connector, struct panel_bridge, connector);
36}
37
38static int panel_bridge_connector_get_modes(struct drm_connector *connector)
39{
40 struct panel_bridge *panel_bridge =
41 drm_connector_to_panel_bridge(connector);
42
43 return drm_panel_get_modes(panel_bridge->panel);
44}
45
46static const struct drm_connector_helper_funcs
47panel_bridge_connector_helper_funcs = {
48 .get_modes = panel_bridge_connector_get_modes,
49};
50
51static const struct drm_connector_funcs panel_bridge_connector_funcs = {
Eric Anholt13dfc052017-06-02 13:25:14 -070052 .reset = drm_atomic_helper_connector_reset,
53 .fill_modes = drm_helper_probe_single_connector_modes,
54 .destroy = drm_connector_cleanup,
55 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
56 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
57};
58
59static int panel_bridge_attach(struct drm_bridge *bridge)
60{
61 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
62 struct drm_connector *connector = &panel_bridge->connector;
63 int ret;
64
65 if (!bridge->encoder) {
66 DRM_ERROR("Missing encoder\n");
67 return -ENODEV;
68 }
69
70 drm_connector_helper_add(connector,
71 &panel_bridge_connector_helper_funcs);
72
73 ret = drm_connector_init(bridge->dev, connector,
74 &panel_bridge_connector_funcs,
75 panel_bridge->connector_type);
76 if (ret) {
77 DRM_ERROR("Failed to initialize connector\n");
78 return ret;
79 }
80
Daniel Vettercde4c442018-07-09 10:40:07 +020081 drm_connector_attach_encoder(&panel_bridge->connector,
Eric Anholt13dfc052017-06-02 13:25:14 -070082 bridge->encoder);
83
84 ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
85 if (ret < 0)
86 return ret;
87
88 return 0;
89}
90
91static void panel_bridge_detach(struct drm_bridge *bridge)
92{
93 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
94
95 drm_panel_detach(panel_bridge->panel);
96}
97
98static void panel_bridge_pre_enable(struct drm_bridge *bridge)
99{
100 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
101
102 drm_panel_prepare(panel_bridge->panel);
103}
104
105static void panel_bridge_enable(struct drm_bridge *bridge)
106{
107 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
108
109 drm_panel_enable(panel_bridge->panel);
110}
111
112static void panel_bridge_disable(struct drm_bridge *bridge)
113{
114 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
115
116 drm_panel_disable(panel_bridge->panel);
117}
118
119static void panel_bridge_post_disable(struct drm_bridge *bridge)
120{
121 struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
122
123 drm_panel_unprepare(panel_bridge->panel);
124}
125
126static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
127 .attach = panel_bridge_attach,
128 .detach = panel_bridge_detach,
129 .pre_enable = panel_bridge_pre_enable,
130 .enable = panel_bridge_enable,
131 .disable = panel_bridge_disable,
132 .post_disable = panel_bridge_post_disable,
133};
134
135/**
Daniel Vetter0aa5eb32019-01-11 17:40:46 +0100136 * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
137 * just calls the appropriate functions from &drm_panel.
Eric Anholt13dfc052017-06-02 13:25:14 -0700138 *
139 * @panel: The drm_panel being wrapped. Must be non-NULL.
140 * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
141 * created.
142 *
143 * For drivers converting from directly using drm_panel: The expected
144 * usage pattern is that during either encoder module probe or DSI
145 * host attach, a drm_panel will be looked up through
146 * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to
147 * wrap that panel in the new bridge, and the result can then be
148 * passed to drm_bridge_attach(). The drm_panel_prepare() and related
149 * functions can be dropped from the encoder driver (they're now
150 * called by the KMS helpers before calling into the encoder), along
Daniel Vetter0aa5eb32019-01-11 17:40:46 +0100151 * with connector creation. When done with the bridge (after
152 * drm_mode_config_cleanup() if the bridge has already been attached), then
Eric Anholt13dfc052017-06-02 13:25:14 -0700153 * drm_panel_bridge_remove() to free it.
Daniel Vetter0aa5eb32019-01-11 17:40:46 +0100154 *
155 * See devm_drm_panel_bridge_add() for an automatically manged version of this
156 * function.
Eric Anholt13dfc052017-06-02 13:25:14 -0700157 */
158struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
159 u32 connector_type)
160{
161 struct panel_bridge *panel_bridge;
Eric Anholt13dfc052017-06-02 13:25:14 -0700162
163 if (!panel)
Eric Anholte6f0acb2017-06-15 10:54:23 -0700164 return ERR_PTR(-EINVAL);
Eric Anholt13dfc052017-06-02 13:25:14 -0700165
166 panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
167 GFP_KERNEL);
168 if (!panel_bridge)
169 return ERR_PTR(-ENOMEM);
170
171 panel_bridge->connector_type = connector_type;
172 panel_bridge->panel = panel;
173
174 panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
175#ifdef CONFIG_OF
176 panel_bridge->bridge.of_node = panel->dev->of_node;
177#endif
178
Inki Dae3a45d252017-07-03 17:42:22 +0900179 drm_bridge_add(&panel_bridge->bridge);
Eric Anholt13dfc052017-06-02 13:25:14 -0700180
181 return &panel_bridge->bridge;
182}
183EXPORT_SYMBOL(drm_panel_bridge_add);
184
185/**
186 * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
187 * created by drm_panel_bridge_add().
188 *
189 * @bridge: The drm_bridge being freed.
190 */
191void drm_panel_bridge_remove(struct drm_bridge *bridge)
192{
benjamin.gaignard@linaro.org6b0e2842017-10-02 11:32:31 +0200193 struct panel_bridge *panel_bridge;
194
195 if (!bridge)
196 return;
197
198 if (bridge->funcs != &panel_bridge_bridge_funcs)
199 return;
200
201 panel_bridge = drm_bridge_to_panel_bridge(bridge);
Eric Anholt13dfc052017-06-02 13:25:14 -0700202
203 drm_bridge_remove(bridge);
204 devm_kfree(panel_bridge->panel->dev, bridge);
205}
206EXPORT_SYMBOL(drm_panel_bridge_remove);
Eric Anholt67022222017-07-18 14:05:06 -0700207
208static void devm_drm_panel_bridge_release(struct device *dev, void *res)
209{
210 struct drm_bridge **bridge = res;
211
212 drm_panel_bridge_remove(*bridge);
213}
214
Daniel Vetter0aa5eb32019-01-11 17:40:46 +0100215/**
216 * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector
217 * that just calls the appropriate functions from &drm_panel.
218 * @dev: device to tie the bridge lifetime to
219 * @panel: The drm_panel being wrapped. Must be non-NULL.
220 * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
221 * created.
222 *
223 * This is the managed version of drm_panel_bridge_add() which automatically
224 * calls drm_panel_bridge_remove() when @dev is unbound.
225 */
Eric Anholt67022222017-07-18 14:05:06 -0700226struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
227 struct drm_panel *panel,
228 u32 connector_type)
229{
230 struct drm_bridge **ptr, *bridge;
231
232 ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
233 GFP_KERNEL);
234 if (!ptr)
235 return ERR_PTR(-ENOMEM);
236
237 bridge = drm_panel_bridge_add(panel, connector_type);
238 if (!IS_ERR(bridge)) {
239 *ptr = bridge;
240 devres_add(dev, ptr);
241 } else {
242 devres_free(ptr);
243 }
244
245 return bridge;
246}
247EXPORT_SYMBOL(devm_drm_panel_bridge_add);