Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
5b809074 NT |
2 | /* |
3 | * Copyright (C) 2016 Noralf Trønnes | |
5b809074 NT |
4 | */ |
5 | ||
0500c04e SR |
6 | #include <linux/module.h> |
7 | #include <linux/slab.h> | |
8 | ||
5b809074 NT |
9 | #include <drm/drm_atomic.h> |
10 | #include <drm/drm_atomic_helper.h> | |
ee68c743 | 11 | #include <drm/drm_bridge.h> |
40cfc7fc DV |
12 | #include <drm/drm_drv.h> |
13 | #include <drm/drm_gem_atomic_helper.h> | |
59abba48 | 14 | #include <drm/drm_managed.h> |
fcd70cd3 | 15 | #include <drm/drm_probe_helper.h> |
5b809074 | 16 | #include <drm/drm_simple_kms_helper.h> |
5b809074 NT |
17 | |
18 | /** | |
19 | * DOC: overview | |
20 | * | |
21 | * This helper library provides helpers for drivers for simple display | |
22 | * hardware. | |
23 | * | |
24 | * drm_simple_display_pipe_init() initializes a simple display pipeline | |
25 | * which has only one full-screen scanout buffer feeding one output. The | |
ea0dd85a | 26 | * pipeline is represented by &struct drm_simple_display_pipe and binds |
5b809074 NT |
27 | * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed |
28 | * entity. Some flexibility for code reuse is provided through a separately | |
29 | * allocated &drm_connector object and supporting optional &drm_bridge | |
30 | * encoder drivers. | |
63170ac6 TZ |
31 | * |
32 | * Many drivers require only a very simple encoder that fulfills the minimum | |
33 | * requirements of the display pipeline and does not add additional | |
34 | * functionality. The function drm_simple_encoder_init() provides an | |
35 | * implementation of such an encoder. | |
5b809074 NT |
36 | */ |
37 | ||
63170ac6 | 38 | static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = { |
5b809074 NT |
39 | .destroy = drm_encoder_cleanup, |
40 | }; | |
41 | ||
63170ac6 | 42 | /** |
2cb5974d TZ |
43 | * drm_simple_encoder_init - Initialize a preallocated encoder with |
44 | * basic functionality. | |
63170ac6 | 45 | * @dev: drm device |
2cb5974d | 46 | * @encoder: the encoder to initialize |
63170ac6 TZ |
47 | * @encoder_type: user visible type of the encoder |
48 | * | |
49 | * Initialises a preallocated encoder that has no further functionality. | |
50 | * Settings for possible CRTC and clones are left to their initial values. | |
51 | * The encoder will be cleaned up automatically as part of the mode-setting | |
52 | * cleanup. | |
53 | * | |
2cb5974d TZ |
54 | * The caller of drm_simple_encoder_init() is responsible for freeing |
55 | * the encoder's memory after the encoder has been cleaned up. At the | |
56 | * moment this only works reliably if the encoder data structure is | |
57 | * stored in the device structure. Free the encoder's memory as part of | |
58 | * the device release function. | |
59 | * | |
59abba48 PZ |
60 | * Note: consider using drmm_simple_encoder_alloc() instead of |
61 | * drm_simple_encoder_init() to let the DRM managed resource infrastructure | |
62 | * take care of cleanup and deallocation. | |
2cb5974d | 63 | * |
63170ac6 TZ |
64 | * Returns: |
65 | * Zero on success, error code on failure. | |
66 | */ | |
67 | int drm_simple_encoder_init(struct drm_device *dev, | |
68 | struct drm_encoder *encoder, | |
69 | int encoder_type) | |
70 | { | |
71 | return drm_encoder_init(dev, encoder, | |
72 | &drm_simple_encoder_funcs_cleanup, | |
73 | encoder_type, NULL); | |
74 | } | |
75 | EXPORT_SYMBOL(drm_simple_encoder_init); | |
76 | ||
59abba48 PZ |
77 | void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size, |
78 | size_t offset, int encoder_type) | |
79 | { | |
80 | return __drmm_encoder_alloc(dev, size, offset, NULL, encoder_type, | |
81 | NULL); | |
82 | } | |
83 | EXPORT_SYMBOL(__drmm_simple_encoder_alloc); | |
84 | ||
40275dc4 LW |
85 | static enum drm_mode_status |
86 | drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc, | |
87 | const struct drm_display_mode *mode) | |
88 | { | |
89 | struct drm_simple_display_pipe *pipe; | |
90 | ||
91 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
92 | if (!pipe->funcs || !pipe->funcs->mode_valid) | |
93 | /* Anything goes */ | |
94 | return MODE_OK; | |
95 | ||
62db7d1e | 96 | return pipe->funcs->mode_valid(pipe, mode); |
40275dc4 LW |
97 | } |
98 | ||
6dcf0de7 | 99 | static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, |
29b77ad7 | 100 | struct drm_atomic_state *state) |
6dcf0de7 | 101 | { |
dc2cdd17 TZ |
102 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
103 | int ret; | |
765831dc | 104 | |
8f2fd57d TZ |
105 | if (!crtc_state->enable) |
106 | goto out; | |
107 | ||
108 | ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); | |
dc2cdd17 TZ |
109 | if (ret) |
110 | return ret; | |
765831dc | 111 | |
8f2fd57d | 112 | out: |
d74252bb | 113 | return drm_atomic_add_affected_planes(state, crtc); |
6dcf0de7 DV |
114 | } |
115 | ||
0b20a0f8 | 116 | static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc, |
351f950d | 117 | struct drm_atomic_state *state) |
5b809074 | 118 | { |
0c9c7fd0 | 119 | struct drm_plane *plane; |
5b809074 NT |
120 | struct drm_simple_display_pipe *pipe; |
121 | ||
122 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
123 | if (!pipe->funcs || !pipe->funcs->enable) | |
124 | return; | |
125 | ||
0c9c7fd0 VS |
126 | plane = &pipe->plane; |
127 | pipe->funcs->enable(pipe, crtc->state, plane->state); | |
5b809074 NT |
128 | } |
129 | ||
64581714 | 130 | static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc, |
351f950d | 131 | struct drm_atomic_state *state) |
5b809074 NT |
132 | { |
133 | struct drm_simple_display_pipe *pipe; | |
134 | ||
135 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
136 | if (!pipe->funcs || !pipe->funcs->disable) | |
137 | return; | |
138 | ||
139 | pipe->funcs->disable(pipe); | |
140 | } | |
141 | ||
142 | static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { | |
40275dc4 | 143 | .mode_valid = drm_simple_kms_crtc_mode_valid, |
6dcf0de7 | 144 | .atomic_check = drm_simple_kms_crtc_check, |
0b20a0f8 | 145 | .atomic_enable = drm_simple_kms_crtc_enable, |
64581714 | 146 | .atomic_disable = drm_simple_kms_crtc_disable, |
5b809074 NT |
147 | }; |
148 | ||
38c5af44 TZ |
149 | static void drm_simple_kms_crtc_reset(struct drm_crtc *crtc) |
150 | { | |
151 | struct drm_simple_display_pipe *pipe; | |
152 | ||
153 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
154 | if (!pipe->funcs || !pipe->funcs->reset_crtc) | |
155 | return drm_atomic_helper_crtc_reset(crtc); | |
156 | ||
157 | return pipe->funcs->reset_crtc(pipe); | |
158 | } | |
159 | ||
160 | static struct drm_crtc_state *drm_simple_kms_crtc_duplicate_state(struct drm_crtc *crtc) | |
161 | { | |
162 | struct drm_simple_display_pipe *pipe; | |
163 | ||
164 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
165 | if (!pipe->funcs || !pipe->funcs->duplicate_crtc_state) | |
166 | return drm_atomic_helper_crtc_duplicate_state(crtc); | |
167 | ||
168 | return pipe->funcs->duplicate_crtc_state(pipe); | |
169 | } | |
170 | ||
171 | static void drm_simple_kms_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) | |
172 | { | |
173 | struct drm_simple_display_pipe *pipe; | |
174 | ||
175 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
176 | if (!pipe->funcs || !pipe->funcs->destroy_crtc_state) | |
177 | drm_atomic_helper_crtc_destroy_state(crtc, state); | |
178 | else | |
179 | pipe->funcs->destroy_crtc_state(pipe, state); | |
180 | } | |
181 | ||
ac86cba9 OA |
182 | static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc) |
183 | { | |
184 | struct drm_simple_display_pipe *pipe; | |
185 | ||
186 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
187 | if (!pipe->funcs || !pipe->funcs->enable_vblank) | |
188 | return 0; | |
189 | ||
190 | return pipe->funcs->enable_vblank(pipe); | |
191 | } | |
192 | ||
193 | static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc) | |
194 | { | |
195 | struct drm_simple_display_pipe *pipe; | |
196 | ||
197 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
198 | if (!pipe->funcs || !pipe->funcs->disable_vblank) | |
199 | return; | |
200 | ||
201 | pipe->funcs->disable_vblank(pipe); | |
202 | } | |
203 | ||
5b809074 | 204 | static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { |
38c5af44 | 205 | .reset = drm_simple_kms_crtc_reset, |
5b809074 NT |
206 | .destroy = drm_crtc_cleanup, |
207 | .set_config = drm_atomic_helper_set_config, | |
208 | .page_flip = drm_atomic_helper_page_flip, | |
38c5af44 TZ |
209 | .atomic_duplicate_state = drm_simple_kms_crtc_duplicate_state, |
210 | .atomic_destroy_state = drm_simple_kms_crtc_destroy_state, | |
ac86cba9 OA |
211 | .enable_vblank = drm_simple_kms_crtc_enable_vblank, |
212 | .disable_vblank = drm_simple_kms_crtc_disable_vblank, | |
5b809074 NT |
213 | }; |
214 | ||
215 | static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, | |
7c11b99a | 216 | struct drm_atomic_state *state) |
5b809074 | 217 | { |
7c11b99a MR |
218 | struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, |
219 | plane); | |
5b809074 NT |
220 | struct drm_simple_display_pipe *pipe; |
221 | struct drm_crtc_state *crtc_state; | |
5b809074 NT |
222 | int ret; |
223 | ||
224 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
dec92020 | 225 | crtc_state = drm_atomic_get_new_crtc_state(state, |
b4d93679 | 226 | &pipe->crtc); |
4be12cc2 | 227 | |
a01cb8ba | 228 | ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, |
cce32e4e TZ |
229 | DRM_PLANE_NO_SCALING, |
230 | DRM_PLANE_NO_SCALING, | |
dc2cdd17 | 231 | false, false); |
5b809074 NT |
232 | if (ret) |
233 | return ret; | |
234 | ||
4be12cc2 | 235 | if (!plane_state->visible) |
4751cf73 OA |
236 | return 0; |
237 | ||
5b809074 NT |
238 | if (!pipe->funcs || !pipe->funcs->check) |
239 | return 0; | |
240 | ||
241 | return pipe->funcs->check(pipe, plane_state, crtc_state); | |
242 | } | |
243 | ||
244 | static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane, | |
977697e2 | 245 | struct drm_atomic_state *state) |
5b809074 | 246 | { |
977697e2 MR |
247 | struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, |
248 | plane); | |
5b809074 NT |
249 | struct drm_simple_display_pipe *pipe; |
250 | ||
251 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
252 | if (!pipe->funcs || !pipe->funcs->update) | |
253 | return; | |
254 | ||
bcd2ba02 | 255 | pipe->funcs->update(pipe, old_pstate); |
5b809074 NT |
256 | } |
257 | ||
7d83a155 MV |
258 | static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane, |
259 | struct drm_plane_state *state) | |
260 | { | |
261 | struct drm_simple_display_pipe *pipe; | |
262 | ||
263 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
40cfc7fc DV |
264 | if (!pipe->funcs || !pipe->funcs->prepare_fb) { |
265 | if (WARN_ON_ONCE(!drm_core_check_feature(plane->dev, DRIVER_GEM))) | |
266 | return 0; | |
267 | ||
268 | WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb); | |
269 | ||
270 | return drm_gem_simple_display_pipe_prepare_fb(pipe, state); | |
271 | } | |
7d83a155 MV |
272 | |
273 | return pipe->funcs->prepare_fb(pipe, state); | |
274 | } | |
275 | ||
276 | static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane, | |
277 | struct drm_plane_state *state) | |
278 | { | |
279 | struct drm_simple_display_pipe *pipe; | |
280 | ||
281 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
282 | if (!pipe->funcs || !pipe->funcs->cleanup_fb) | |
283 | return; | |
284 | ||
285 | pipe->funcs->cleanup_fb(pipe, state); | |
286 | } | |
287 | ||
94d879ea TZ |
288 | static int drm_simple_kms_plane_begin_fb_access(struct drm_plane *plane, |
289 | struct drm_plane_state *new_plane_state) | |
290 | { | |
291 | struct drm_simple_display_pipe *pipe; | |
292 | ||
293 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
294 | if (!pipe->funcs || !pipe->funcs->begin_fb_access) | |
295 | return 0; | |
296 | ||
297 | return pipe->funcs->begin_fb_access(pipe, new_plane_state); | |
298 | } | |
299 | ||
300 | static void drm_simple_kms_plane_end_fb_access(struct drm_plane *plane, | |
301 | struct drm_plane_state *new_plane_state) | |
302 | { | |
303 | struct drm_simple_display_pipe *pipe; | |
304 | ||
305 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
306 | if (!pipe->funcs || !pipe->funcs->end_fb_access) | |
307 | return; | |
308 | ||
309 | pipe->funcs->end_fb_access(pipe, new_plane_state); | |
310 | } | |
311 | ||
dff906c3 EA |
312 | static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane, |
313 | uint32_t format, | |
314 | uint64_t modifier) | |
315 | { | |
316 | return modifier == DRM_FORMAT_MOD_LINEAR; | |
317 | } | |
318 | ||
5b809074 | 319 | static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { |
7d83a155 MV |
320 | .prepare_fb = drm_simple_kms_plane_prepare_fb, |
321 | .cleanup_fb = drm_simple_kms_plane_cleanup_fb, | |
94d879ea TZ |
322 | .begin_fb_access = drm_simple_kms_plane_begin_fb_access, |
323 | .end_fb_access = drm_simple_kms_plane_end_fb_access, | |
5b809074 NT |
324 | .atomic_check = drm_simple_kms_plane_atomic_check, |
325 | .atomic_update = drm_simple_kms_plane_atomic_update, | |
326 | }; | |
327 | ||
40f302ad TZ |
328 | static void drm_simple_kms_plane_reset(struct drm_plane *plane) |
329 | { | |
330 | struct drm_simple_display_pipe *pipe; | |
331 | ||
332 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
333 | if (!pipe->funcs || !pipe->funcs->reset_plane) | |
334 | return drm_atomic_helper_plane_reset(plane); | |
335 | ||
336 | return pipe->funcs->reset_plane(pipe); | |
337 | } | |
338 | ||
339 | static struct drm_plane_state *drm_simple_kms_plane_duplicate_state(struct drm_plane *plane) | |
340 | { | |
341 | struct drm_simple_display_pipe *pipe; | |
342 | ||
343 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
344 | if (!pipe->funcs || !pipe->funcs->duplicate_plane_state) | |
345 | return drm_atomic_helper_plane_duplicate_state(plane); | |
346 | ||
347 | return pipe->funcs->duplicate_plane_state(pipe); | |
348 | } | |
349 | ||
350 | static void drm_simple_kms_plane_destroy_state(struct drm_plane *plane, | |
351 | struct drm_plane_state *state) | |
352 | { | |
353 | struct drm_simple_display_pipe *pipe; | |
354 | ||
355 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
356 | if (!pipe->funcs || !pipe->funcs->destroy_plane_state) | |
357 | drm_atomic_helper_plane_destroy_state(plane, state); | |
358 | else | |
359 | pipe->funcs->destroy_plane_state(pipe, state); | |
360 | } | |
361 | ||
5b809074 NT |
362 | static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { |
363 | .update_plane = drm_atomic_helper_update_plane, | |
364 | .disable_plane = drm_atomic_helper_disable_plane, | |
365 | .destroy = drm_plane_cleanup, | |
40f302ad TZ |
366 | .reset = drm_simple_kms_plane_reset, |
367 | .atomic_duplicate_state = drm_simple_kms_plane_duplicate_state, | |
368 | .atomic_destroy_state = drm_simple_kms_plane_destroy_state, | |
dff906c3 | 369 | .format_mod_supported = drm_simple_kms_format_mod_supported, |
5b809074 NT |
370 | }; |
371 | ||
315486c6 AM |
372 | /** |
373 | * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe | |
374 | * @pipe: simple display pipe object | |
375 | * @bridge: bridge to attach | |
376 | * | |
377 | * Makes it possible to still use the drm_simple_display_pipe helpers when | |
378 | * a DRM bridge has to be used. | |
379 | * | |
380 | * Note that you probably want to initialize the pipe by passing a NULL | |
381 | * connector to drm_simple_display_pipe_init(). | |
382 | * | |
383 | * Returns: | |
384 | * Zero on success, negative error code on failure. | |
385 | */ | |
386 | int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, | |
387 | struct drm_bridge *bridge) | |
388 | { | |
a25b988f | 389 | return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0); |
315486c6 AM |
390 | } |
391 | EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); | |
392 | ||
5b809074 NT |
393 | /** |
394 | * drm_simple_display_pipe_init - Initialize a simple display pipeline | |
395 | * @dev: DRM device | |
396 | * @pipe: simple display pipe object to initialize | |
397 | * @funcs: callbacks for the display pipe (optional) | |
62cacc79 | 398 | * @formats: array of supported formats (DRM_FORMAT\_\*) |
5b809074 | 399 | * @format_count: number of elements in @formats |
e6fc3b68 | 400 | * @format_modifiers: array of formats modifiers |
4f993973 | 401 | * @connector: connector to attach and register (optional) |
5b809074 NT |
402 | * |
403 | * Sets up a display pipeline which consist of a really simple | |
4f993973 AM |
404 | * plane-crtc-encoder pipe. |
405 | * | |
406 | * If a connector is supplied, the pipe will be coupled with the provided | |
407 | * connector. You may supply a NULL connector when using drm bridges, that | |
408 | * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). | |
409 | * | |
5b809074 NT |
410 | * Teardown of a simple display pipe is all handled automatically by the drm |
411 | * core through calling drm_mode_config_cleanup(). Drivers afterwards need to | |
412 | * release the memory for the structure themselves. | |
413 | * | |
414 | * Returns: | |
415 | * Zero on success, negative error code on failure. | |
416 | */ | |
417 | int drm_simple_display_pipe_init(struct drm_device *dev, | |
418 | struct drm_simple_display_pipe *pipe, | |
419 | const struct drm_simple_display_pipe_funcs *funcs, | |
420 | const uint32_t *formats, unsigned int format_count, | |
e6fc3b68 | 421 | const uint64_t *format_modifiers, |
5b809074 NT |
422 | struct drm_connector *connector) |
423 | { | |
424 | struct drm_encoder *encoder = &pipe->encoder; | |
425 | struct drm_plane *plane = &pipe->plane; | |
426 | struct drm_crtc *crtc = &pipe->crtc; | |
427 | int ret; | |
428 | ||
429 | pipe->connector = connector; | |
430 | pipe->funcs = funcs; | |
431 | ||
432 | drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs); | |
433 | ret = drm_universal_plane_init(dev, plane, 0, | |
434 | &drm_simple_kms_plane_funcs, | |
435 | formats, format_count, | |
e6fc3b68 | 436 | format_modifiers, |
5b809074 NT |
437 | DRM_PLANE_TYPE_PRIMARY, NULL); |
438 | if (ret) | |
439 | return ret; | |
440 | ||
441 | drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs); | |
442 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, | |
443 | &drm_simple_kms_crtc_funcs, NULL); | |
444 | if (ret) | |
445 | return ret; | |
446 | ||
6a52193b | 447 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
63170ac6 | 448 | ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE); |
4f993973 | 449 | if (ret || !connector) |
5b809074 NT |
450 | return ret; |
451 | ||
cde4c44d | 452 | return drm_connector_attach_encoder(connector, encoder); |
5b809074 NT |
453 | } |
454 | EXPORT_SYMBOL(drm_simple_display_pipe_init); | |
455 | ||
456 | MODULE_LICENSE("GPL"); |