Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
4 : : * Copyright (C) 2017 Broadcom
5 : : */
6 : :
7 : : #include <drm/drm_atomic_helper.h>
8 : : #include <drm/drm_bridge.h>
9 : : #include <drm/drm_connector.h>
10 : : #include <drm/drm_encoder.h>
11 : : #include <drm/drm_modeset_helper_vtables.h>
12 : : #include <drm/drm_panel.h>
13 : : #include <drm/drm_print.h>
14 : : #include <drm/drm_probe_helper.h>
15 : :
16 : : struct panel_bridge {
17 : : struct drm_bridge bridge;
18 : : struct drm_connector connector;
19 : : struct drm_panel *panel;
20 : : u32 connector_type;
21 : : };
22 : :
23 : : static inline struct panel_bridge *
24 : 0 : drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
25 : : {
26 : 0 : return container_of(bridge, struct panel_bridge, bridge);
27 : : }
28 : :
29 : : static inline struct panel_bridge *
30 : 0 : drm_connector_to_panel_bridge(struct drm_connector *connector)
31 : : {
32 : 0 : return container_of(connector, struct panel_bridge, connector);
33 : : }
34 : :
35 : 0 : static int panel_bridge_connector_get_modes(struct drm_connector *connector)
36 : : {
37 : 0 : struct panel_bridge *panel_bridge =
38 : : drm_connector_to_panel_bridge(connector);
39 : :
40 : 0 : return drm_panel_get_modes(panel_bridge->panel, connector);
41 : : }
42 : :
43 : : static const struct drm_connector_helper_funcs
44 : : panel_bridge_connector_helper_funcs = {
45 : : .get_modes = panel_bridge_connector_get_modes,
46 : : };
47 : :
48 : : static const struct drm_connector_funcs panel_bridge_connector_funcs = {
49 : : .reset = drm_atomic_helper_connector_reset,
50 : : .fill_modes = drm_helper_probe_single_connector_modes,
51 : : .destroy = drm_connector_cleanup,
52 : : .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
53 : : .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
54 : : };
55 : :
56 : 0 : static int panel_bridge_attach(struct drm_bridge *bridge)
57 : : {
58 : 0 : struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
59 : 0 : struct drm_connector *connector = &panel_bridge->connector;
60 : 0 : int ret;
61 : :
62 [ # # ]: 0 : if (!bridge->encoder) {
63 : 0 : DRM_ERROR("Missing encoder\n");
64 : 0 : return -ENODEV;
65 : : }
66 : :
67 : 0 : drm_connector_helper_add(connector,
68 : : &panel_bridge_connector_helper_funcs);
69 : :
70 : 0 : ret = drm_connector_init(bridge->dev, connector,
71 : : &panel_bridge_connector_funcs,
72 : 0 : panel_bridge->connector_type);
73 [ # # ]: 0 : if (ret) {
74 : 0 : DRM_ERROR("Failed to initialize connector\n");
75 : 0 : return ret;
76 : : }
77 : :
78 : 0 : drm_connector_attach_encoder(&panel_bridge->connector,
79 : : bridge->encoder);
80 : :
81 : 0 : ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
82 : 0 : if (ret < 0)
83 : : return ret;
84 : :
85 : : return 0;
86 : : }
87 : :
88 : 0 : static void panel_bridge_detach(struct drm_bridge *bridge)
89 : : {
90 : 0 : struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
91 : :
92 : 0 : drm_panel_detach(panel_bridge->panel);
93 : 0 : }
94 : :
95 : 0 : static void panel_bridge_pre_enable(struct drm_bridge *bridge)
96 : : {
97 : 0 : struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
98 : :
99 : 0 : drm_panel_prepare(panel_bridge->panel);
100 : 0 : }
101 : :
102 : 0 : static void panel_bridge_enable(struct drm_bridge *bridge)
103 : : {
104 : 0 : struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
105 : :
106 : 0 : drm_panel_enable(panel_bridge->panel);
107 : 0 : }
108 : :
109 : 0 : static void panel_bridge_disable(struct drm_bridge *bridge)
110 : : {
111 : 0 : struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
112 : :
113 : 0 : drm_panel_disable(panel_bridge->panel);
114 : 0 : }
115 : :
116 : 0 : static void panel_bridge_post_disable(struct drm_bridge *bridge)
117 : : {
118 : 0 : struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
119 : :
120 : 0 : drm_panel_unprepare(panel_bridge->panel);
121 : 0 : }
122 : :
123 : : static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
124 : : .attach = panel_bridge_attach,
125 : : .detach = panel_bridge_detach,
126 : : .pre_enable = panel_bridge_pre_enable,
127 : : .enable = panel_bridge_enable,
128 : : .disable = panel_bridge_disable,
129 : : .post_disable = panel_bridge_post_disable,
130 : : };
131 : :
132 : : /**
133 : : * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
134 : : * just calls the appropriate functions from &drm_panel.
135 : : *
136 : : * @panel: The drm_panel being wrapped. Must be non-NULL.
137 : : *
138 : : * For drivers converting from directly using drm_panel: The expected
139 : : * usage pattern is that during either encoder module probe or DSI
140 : : * host attach, a drm_panel will be looked up through
141 : : * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to
142 : : * wrap that panel in the new bridge, and the result can then be
143 : : * passed to drm_bridge_attach(). The drm_panel_prepare() and related
144 : : * functions can be dropped from the encoder driver (they're now
145 : : * called by the KMS helpers before calling into the encoder), along
146 : : * with connector creation. When done with the bridge (after
147 : : * drm_mode_config_cleanup() if the bridge has already been attached), then
148 : : * drm_panel_bridge_remove() to free it.
149 : : *
150 : : * The connector type is set to @panel->connector_type, which must be set to a
151 : : * known type. Calling this function with a panel whose connector type is
152 : : * DRM_MODE_CONNECTOR_Unknown will return NULL.
153 : : *
154 : : * See devm_drm_panel_bridge_add() for an automatically manged version of this
155 : : * function.
156 : : */
157 : 0 : struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel)
158 : : {
159 [ # # # # ]: 0 : if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
160 : : return NULL;
161 : :
162 : 0 : return drm_panel_bridge_add_typed(panel, panel->connector_type);
163 : : }
164 : : EXPORT_SYMBOL(drm_panel_bridge_add);
165 : :
166 : : /**
167 : : * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with
168 : : * an explicit connector type.
169 : : * @panel: The drm_panel being wrapped. Must be non-NULL.
170 : : * @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
171 : : *
172 : : * This is just like drm_panel_bridge_add(), but forces the connector type to
173 : : * @connector_type instead of infering it from the panel.
174 : : *
175 : : * This function is deprecated and should not be used in new drivers. Use
176 : : * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they
177 : : * don't report a connector type.
178 : : */
179 : 0 : struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
180 : : u32 connector_type)
181 : : {
182 : 0 : struct panel_bridge *panel_bridge;
183 : :
184 [ # # ]: 0 : if (!panel)
185 : : return ERR_PTR(-EINVAL);
186 : :
187 : 0 : panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
188 : : GFP_KERNEL);
189 [ # # ]: 0 : if (!panel_bridge)
190 : : return ERR_PTR(-ENOMEM);
191 : :
192 : 0 : panel_bridge->connector_type = connector_type;
193 : 0 : panel_bridge->panel = panel;
194 : :
195 : 0 : panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
196 : : #ifdef CONFIG_OF
197 : : panel_bridge->bridge.of_node = panel->dev->of_node;
198 : : #endif
199 : :
200 : 0 : drm_bridge_add(&panel_bridge->bridge);
201 : :
202 : 0 : return &panel_bridge->bridge;
203 : : }
204 : : EXPORT_SYMBOL(drm_panel_bridge_add_typed);
205 : :
206 : : /**
207 : : * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
208 : : * created by drm_panel_bridge_add().
209 : : *
210 : : * @bridge: The drm_bridge being freed.
211 : : */
212 : 0 : void drm_panel_bridge_remove(struct drm_bridge *bridge)
213 : : {
214 : 0 : struct panel_bridge *panel_bridge;
215 : :
216 [ # # ]: 0 : if (!bridge)
217 : : return;
218 : :
219 [ # # ]: 0 : if (bridge->funcs != &panel_bridge_bridge_funcs)
220 : : return;
221 : :
222 : 0 : panel_bridge = drm_bridge_to_panel_bridge(bridge);
223 : :
224 : 0 : drm_bridge_remove(bridge);
225 : 0 : devm_kfree(panel_bridge->panel->dev, bridge);
226 : : }
227 : : EXPORT_SYMBOL(drm_panel_bridge_remove);
228 : :
229 : 0 : static void devm_drm_panel_bridge_release(struct device *dev, void *res)
230 : : {
231 : 0 : struct drm_bridge **bridge = res;
232 : :
233 : 0 : drm_panel_bridge_remove(*bridge);
234 : 0 : }
235 : :
236 : : /**
237 : : * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector
238 : : * that just calls the appropriate functions from &drm_panel.
239 : : * @dev: device to tie the bridge lifetime to
240 : : * @panel: The drm_panel being wrapped. Must be non-NULL.
241 : : *
242 : : * This is the managed version of drm_panel_bridge_add() which automatically
243 : : * calls drm_panel_bridge_remove() when @dev is unbound.
244 : : */
245 : 0 : struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
246 : : struct drm_panel *panel)
247 : : {
248 [ # # # # ]: 0 : if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
249 : : return NULL;
250 : :
251 : 0 : return devm_drm_panel_bridge_add_typed(dev, panel,
252 : : panel->connector_type);
253 : : }
254 : : EXPORT_SYMBOL(devm_drm_panel_bridge_add);
255 : :
256 : : /**
257 : : * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and
258 : : * &drm_connector with an explicit connector type.
259 : : * @dev: device to tie the bridge lifetime to
260 : : * @panel: The drm_panel being wrapped. Must be non-NULL.
261 : : * @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
262 : : *
263 : : * This is just like devm_drm_panel_bridge_add(), but forces the connector type
264 : : * to @connector_type instead of infering it from the panel.
265 : : *
266 : : * This function is deprecated and should not be used in new drivers. Use
267 : : * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if
268 : : * they don't report a connector type.
269 : : */
270 : 0 : struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
271 : : struct drm_panel *panel,
272 : : u32 connector_type)
273 : : {
274 : 0 : struct drm_bridge **ptr, *bridge;
275 : :
276 : 0 : ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
277 : : GFP_KERNEL);
278 [ # # ]: 0 : if (!ptr)
279 : : return ERR_PTR(-ENOMEM);
280 : :
281 : 0 : bridge = drm_panel_bridge_add_typed(panel, connector_type);
282 [ # # ]: 0 : if (!IS_ERR(bridge)) {
283 : 0 : *ptr = bridge;
284 : 0 : devres_add(dev, ptr);
285 : : } else {
286 : 0 : devres_free(ptr);
287 : : }
288 : :
289 : : return bridge;
290 : : }
291 : : EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
292 : :
293 : : /**
294 : : * drm_panel_bridge_connector - return the connector for the panel bridge
295 : : *
296 : : * drm_panel_bridge creates the connector.
297 : : * This function gives external access to the connector.
298 : : *
299 : : * Returns: Pointer to drm_connector
300 : : */
301 : 0 : struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
302 : : {
303 : 0 : struct panel_bridge *panel_bridge;
304 : :
305 : 0 : panel_bridge = drm_bridge_to_panel_bridge(bridge);
306 : :
307 : 0 : return &panel_bridge->connector;
308 : : }
309 : : EXPORT_SYMBOL(drm_panel_bridge_connector);
|