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