1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 Noralf Trønnes |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/slab.h> |
8 | |
9 | #include <drm/drm_atomic.h> |
10 | #include <drm/drm_atomic_helper.h> |
11 | #include <drm/drm_bridge.h> |
12 | #include <drm/drm_drv.h> |
13 | #include <drm/drm_gem_atomic_helper.h> |
14 | #include <drm/drm_managed.h> |
15 | #include <drm/drm_probe_helper.h> |
16 | #include <drm/drm_simple_kms_helper.h> |
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 |
26 | * pipeline is represented by &struct drm_simple_display_pipe and binds |
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. |
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. |
36 | */ |
37 | |
38 | static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = { |
39 | .destroy = drm_encoder_cleanup, |
40 | }; |
41 | |
42 | /** |
43 | * drm_simple_encoder_init - Initialize a preallocated encoder with |
44 | * basic functionality. |
45 | * @dev: drm device |
46 | * @encoder: the encoder to initialize |
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 | * |
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 | * |
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. |
63 | * |
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 | funcs: &drm_simple_encoder_funcs_cleanup, |
73 | encoder_type, NULL); |
74 | } |
75 | EXPORT_SYMBOL(drm_simple_encoder_init); |
76 | |
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 | |
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 | |
96 | return pipe->funcs->mode_valid(pipe, mode); |
97 | } |
98 | |
99 | static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, |
100 | struct drm_atomic_state *state) |
101 | { |
102 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
103 | int ret; |
104 | |
105 | if (!crtc_state->enable) |
106 | goto out; |
107 | |
108 | ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); |
109 | if (ret) |
110 | return ret; |
111 | |
112 | out: |
113 | return drm_atomic_add_affected_planes(state, crtc); |
114 | } |
115 | |
116 | static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc, |
117 | struct drm_atomic_state *state) |
118 | { |
119 | struct drm_plane *plane; |
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 | |
126 | plane = &pipe->plane; |
127 | pipe->funcs->enable(pipe, crtc->state, plane->state); |
128 | } |
129 | |
130 | static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc, |
131 | struct drm_atomic_state *state) |
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 = { |
143 | .mode_valid = drm_simple_kms_crtc_mode_valid, |
144 | .atomic_check = drm_simple_kms_crtc_check, |
145 | .atomic_enable = drm_simple_kms_crtc_enable, |
146 | .atomic_disable = drm_simple_kms_crtc_disable, |
147 | }; |
148 | |
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 | |
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 | |
204 | static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { |
205 | .reset = drm_simple_kms_crtc_reset, |
206 | .destroy = drm_crtc_cleanup, |
207 | .set_config = drm_atomic_helper_set_config, |
208 | .page_flip = drm_atomic_helper_page_flip, |
209 | .atomic_duplicate_state = drm_simple_kms_crtc_duplicate_state, |
210 | .atomic_destroy_state = drm_simple_kms_crtc_destroy_state, |
211 | .enable_vblank = drm_simple_kms_crtc_enable_vblank, |
212 | .disable_vblank = drm_simple_kms_crtc_disable_vblank, |
213 | }; |
214 | |
215 | static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, |
216 | struct drm_atomic_state *state) |
217 | { |
218 | struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, |
219 | plane); |
220 | struct drm_simple_display_pipe *pipe; |
221 | struct drm_crtc_state *crtc_state; |
222 | int ret; |
223 | |
224 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); |
225 | crtc_state = drm_atomic_get_new_crtc_state(state, |
226 | crtc: &pipe->crtc); |
227 | |
228 | ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, |
229 | DRM_PLANE_NO_SCALING, |
230 | DRM_PLANE_NO_SCALING, |
231 | can_position: false, can_update_disabled: false); |
232 | if (ret) |
233 | return ret; |
234 | |
235 | if (!plane_state->visible) |
236 | return 0; |
237 | |
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, |
245 | struct drm_atomic_state *state) |
246 | { |
247 | struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, |
248 | plane); |
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 | |
255 | pipe->funcs->update(pipe, old_pstate); |
256 | } |
257 | |
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); |
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_plane_helper_prepare_fb(plane, state); |
271 | } |
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 | |
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 | |
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 | |
319 | static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { |
320 | .prepare_fb = drm_simple_kms_plane_prepare_fb, |
321 | .cleanup_fb = drm_simple_kms_plane_cleanup_fb, |
322 | .begin_fb_access = drm_simple_kms_plane_begin_fb_access, |
323 | .end_fb_access = drm_simple_kms_plane_end_fb_access, |
324 | .atomic_check = drm_simple_kms_plane_atomic_check, |
325 | .atomic_update = drm_simple_kms_plane_atomic_update, |
326 | }; |
327 | |
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 | |
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, |
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, |
369 | .format_mod_supported = drm_simple_kms_format_mod_supported, |
370 | }; |
371 | |
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 | { |
389 | return drm_bridge_attach(encoder: &pipe->encoder, bridge, NULL, flags: 0); |
390 | } |
391 | EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); |
392 | |
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) |
398 | * @formats: array of supported formats (DRM_FORMAT\_\*) |
399 | * @format_count: number of elements in @formats |
400 | * @format_modifiers: array of formats modifiers |
401 | * @connector: connector to attach and register (optional) |
402 | * |
403 | * Sets up a display pipeline which consist of a really simple |
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 | * |
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, |
421 | const uint64_t *format_modifiers, |
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, funcs: &drm_simple_kms_plane_helper_funcs); |
433 | ret = drm_universal_plane_init(dev, plane, possible_crtcs: 0, |
434 | funcs: &drm_simple_kms_plane_funcs, |
435 | formats, format_count, |
436 | format_modifiers, |
437 | type: DRM_PLANE_TYPE_PRIMARY, NULL); |
438 | if (ret) |
439 | return ret; |
440 | |
441 | drm_crtc_helper_add(crtc, funcs: &drm_simple_kms_crtc_helper_funcs); |
442 | ret = drm_crtc_init_with_planes(dev, crtc, primary: plane, NULL, |
443 | funcs: &drm_simple_kms_crtc_funcs, NULL); |
444 | if (ret) |
445 | return ret; |
446 | |
447 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
448 | ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE); |
449 | if (ret || !connector) |
450 | return ret; |
451 | |
452 | return drm_connector_attach_encoder(connector, encoder); |
453 | } |
454 | EXPORT_SYMBOL(drm_simple_display_pipe_init); |
455 | |
456 | MODULE_LICENSE("GPL" ); |
457 | |