Line data Source code
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 0 : int drm_simple_encoder_init(struct drm_device *dev,
68 : struct drm_encoder *encoder,
69 : int encoder_type)
70 : {
71 0 : 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 :
77 0 : void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
78 : size_t offset, int encoder_type)
79 : {
80 0 : 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 0 : 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 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
92 0 : if (!pipe->funcs || !pipe->funcs->mode_valid)
93 : /* Anything goes */
94 : return MODE_OK;
95 :
96 0 : return pipe->funcs->mode_valid(pipe, mode);
97 : }
98 :
99 0 : static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
100 : struct drm_atomic_state *state)
101 : {
102 0 : struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
103 : int ret;
104 :
105 0 : if (!crtc_state->enable)
106 : goto out;
107 :
108 0 : ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
109 0 : if (ret)
110 : return ret;
111 :
112 : out:
113 0 : return drm_atomic_add_affected_planes(state, crtc);
114 : }
115 :
116 0 : 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 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
123 0 : if (!pipe->funcs || !pipe->funcs->enable)
124 : return;
125 :
126 0 : plane = &pipe->plane;
127 0 : pipe->funcs->enable(pipe, crtc->state, plane->state);
128 : }
129 :
130 0 : 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 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
136 0 : if (!pipe->funcs || !pipe->funcs->disable)
137 : return;
138 :
139 0 : 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 0 : static void drm_simple_kms_crtc_reset(struct drm_crtc *crtc)
150 : {
151 : struct drm_simple_display_pipe *pipe;
152 :
153 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
154 0 : if (!pipe->funcs || !pipe->funcs->reset_crtc)
155 0 : return drm_atomic_helper_crtc_reset(crtc);
156 :
157 0 : return pipe->funcs->reset_crtc(pipe);
158 : }
159 :
160 0 : 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 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
165 0 : if (!pipe->funcs || !pipe->funcs->duplicate_crtc_state)
166 0 : return drm_atomic_helper_crtc_duplicate_state(crtc);
167 :
168 0 : return pipe->funcs->duplicate_crtc_state(pipe);
169 : }
170 :
171 0 : 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 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
176 0 : if (!pipe->funcs || !pipe->funcs->destroy_crtc_state)
177 0 : drm_atomic_helper_crtc_destroy_state(crtc, state);
178 : else
179 0 : pipe->funcs->destroy_crtc_state(pipe, state);
180 0 : }
181 :
182 0 : static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
183 : {
184 : struct drm_simple_display_pipe *pipe;
185 :
186 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
187 0 : if (!pipe->funcs || !pipe->funcs->enable_vblank)
188 : return 0;
189 :
190 0 : return pipe->funcs->enable_vblank(pipe);
191 : }
192 :
193 0 : static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
194 : {
195 : struct drm_simple_display_pipe *pipe;
196 :
197 0 : pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
198 0 : if (!pipe->funcs || !pipe->funcs->disable_vblank)
199 : return;
200 :
201 0 : 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 0 : static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
216 : struct drm_atomic_state *state)
217 : {
218 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
225 0 : crtc_state = drm_atomic_get_new_crtc_state(state,
226 : &pipe->crtc);
227 :
228 0 : ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
229 : DRM_PLANE_NO_SCALING,
230 : DRM_PLANE_NO_SCALING,
231 : false, false);
232 0 : if (ret)
233 : return ret;
234 :
235 0 : if (!plane_state->visible)
236 : return 0;
237 :
238 0 : if (!pipe->funcs || !pipe->funcs->check)
239 : return 0;
240 :
241 0 : return pipe->funcs->check(pipe, plane_state, crtc_state);
242 : }
243 :
244 0 : static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
245 : struct drm_atomic_state *state)
246 : {
247 0 : struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state,
248 : plane);
249 : struct drm_simple_display_pipe *pipe;
250 :
251 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
252 0 : if (!pipe->funcs || !pipe->funcs->update)
253 : return;
254 :
255 0 : pipe->funcs->update(pipe, old_pstate);
256 : }
257 :
258 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
264 0 : if (!pipe->funcs || !pipe->funcs->prepare_fb) {
265 0 : if (WARN_ON_ONCE(!drm_core_check_feature(plane->dev, DRIVER_GEM)))
266 : return 0;
267 :
268 0 : WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb);
269 :
270 0 : return drm_gem_plane_helper_prepare_fb(plane, state);
271 : }
272 :
273 0 : return pipe->funcs->prepare_fb(pipe, state);
274 : }
275 :
276 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
282 0 : if (!pipe->funcs || !pipe->funcs->cleanup_fb)
283 : return;
284 :
285 0 : pipe->funcs->cleanup_fb(pipe, state);
286 : }
287 :
288 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
294 0 : if (!pipe->funcs || !pipe->funcs->begin_fb_access)
295 : return 0;
296 :
297 0 : return pipe->funcs->begin_fb_access(pipe, new_plane_state);
298 : }
299 :
300 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
306 0 : if (!pipe->funcs || !pipe->funcs->end_fb_access)
307 : return;
308 :
309 0 : pipe->funcs->end_fb_access(pipe, new_plane_state);
310 : }
311 :
312 0 : static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
313 : uint32_t format,
314 : uint64_t modifier)
315 : {
316 0 : 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 0 : static void drm_simple_kms_plane_reset(struct drm_plane *plane)
329 : {
330 : struct drm_simple_display_pipe *pipe;
331 :
332 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
333 0 : if (!pipe->funcs || !pipe->funcs->reset_plane)
334 0 : return drm_atomic_helper_plane_reset(plane);
335 :
336 0 : return pipe->funcs->reset_plane(pipe);
337 : }
338 :
339 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
344 0 : if (!pipe->funcs || !pipe->funcs->duplicate_plane_state)
345 0 : return drm_atomic_helper_plane_duplicate_state(plane);
346 :
347 0 : return pipe->funcs->duplicate_plane_state(pipe);
348 : }
349 :
350 0 : 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 0 : pipe = container_of(plane, struct drm_simple_display_pipe, plane);
356 0 : if (!pipe->funcs || !pipe->funcs->destroy_plane_state)
357 0 : drm_atomic_helper_plane_destroy_state(plane, state);
358 : else
359 0 : pipe->funcs->destroy_plane_state(pipe, state);
360 0 : }
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 0 : int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
387 : struct drm_bridge *bridge)
388 : {
389 0 : return drm_bridge_attach(&pipe->encoder, bridge, NULL, 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 0 : 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 0 : struct drm_encoder *encoder = &pipe->encoder;
425 0 : struct drm_plane *plane = &pipe->plane;
426 0 : struct drm_crtc *crtc = &pipe->crtc;
427 : int ret;
428 :
429 0 : pipe->connector = connector;
430 0 : pipe->funcs = funcs;
431 :
432 0 : drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
433 0 : ret = drm_universal_plane_init(dev, plane, 0,
434 : &drm_simple_kms_plane_funcs,
435 : formats, format_count,
436 : format_modifiers,
437 : DRM_PLANE_TYPE_PRIMARY, NULL);
438 0 : if (ret)
439 : return ret;
440 :
441 0 : drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
442 0 : ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
443 : &drm_simple_kms_crtc_funcs, NULL);
444 0 : if (ret)
445 : return ret;
446 :
447 0 : encoder->possible_crtcs = drm_crtc_mask(crtc);
448 0 : ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
449 0 : if (ret || !connector)
450 : return ret;
451 :
452 0 : return drm_connector_attach_encoder(connector, encoder);
453 : }
454 : EXPORT_SYMBOL(drm_simple_display_pipe_init);
455 :
456 : MODULE_LICENSE("GPL");
|