drm: Add atomic/plane helpers
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_atomic_helper.c
1 /*
2  * Copyright (C) 2014 Red Hat
3  * Copyright (C) 2014 Intel Corp.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  * Rob Clark <robdclark@gmail.com>
25  * Daniel Vetter <daniel.vetter@ffwll.ch>
26  */
27
28 #include <drm/drmP.h>
29 #include <drm/drm_atomic.h>
30 #include <drm/drm_plane_helper.h>
31 #include <drm/drm_crtc_helper.h>
32
33 static void
34 drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
35                                 struct drm_plane_state *plane_state,
36                                 struct drm_plane *plane)
37 {
38         struct drm_crtc_state *crtc_state;
39
40         if (plane->state->crtc) {
41                 crtc_state = state->crtc_states[drm_crtc_index(plane->crtc)];
42
43                 if (WARN_ON(!crtc_state))
44                         return;
45
46                 crtc_state->planes_changed = true;
47         }
48
49         if (plane_state->crtc) {
50                 crtc_state =
51                         state->crtc_states[drm_crtc_index(plane_state->crtc)];
52
53                 if (WARN_ON(!crtc_state))
54                         return;
55
56                 crtc_state->planes_changed = true;
57         }
58 }
59
60 /**
61  * drm_atomic_helper_check - validate state object
62  * @dev: DRM device
63  * @state: the driver state object
64  *
65  * Check the state object to see if the requested state is physically possible.
66  * Only crtcs and planes have check callbacks, so for any additional (global)
67  * checking that a driver needs it can simply wrap that around this function.
68  * Drivers without such needs can directly use this as their ->atomic_check()
69  * callback.
70  *
71  * RETURNS
72  * Zero for success or -errno
73  */
74 int drm_atomic_helper_check(struct drm_device *dev,
75                             struct drm_atomic_state *state)
76 {
77         int nplanes = dev->mode_config.num_total_plane;
78         int ncrtcs = dev->mode_config.num_crtc;
79         int i, ret = 0;
80
81         for (i = 0; i < nplanes; i++) {
82                 struct drm_plane_helper_funcs *funcs;
83                 struct drm_plane *plane = state->planes[i];
84                 struct drm_plane_state *plane_state = state->plane_states[i];
85
86                 if (!plane)
87                         continue;
88
89                 funcs = plane->helper_private;
90
91                 drm_atomic_helper_plane_changed(state, plane_state, plane);
92
93                 if (!funcs || !funcs->atomic_check)
94                         continue;
95
96                 ret = funcs->atomic_check(plane, plane_state);
97                 if (ret) {
98                         DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n",
99                                       plane->base.id);
100                         return ret;
101                 }
102         }
103
104         for (i = 0; i < ncrtcs; i++) {
105                 struct drm_crtc_helper_funcs *funcs;
106                 struct drm_crtc *crtc = state->crtcs[i];
107
108                 if (!crtc)
109                         continue;
110
111                 funcs = crtc->helper_private;
112
113                 if (!funcs || !funcs->atomic_check)
114                         continue;
115
116                 ret = funcs->atomic_check(crtc, state->crtc_states[i]);
117                 if (ret) {
118                         DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n",
119                                       crtc->base.id);
120                         return ret;
121                 }
122         }
123
124         return ret;
125 }
126 EXPORT_SYMBOL(drm_atomic_helper_check);
127
128 /**
129  * drm_atomic_helper_prepare_planes - prepare plane resources after commit
130  * @dev: DRM device
131  * @state: atomic state object with old state structures
132  *
133  * This function prepares plane state, specifically framebuffers, for the new
134  * configuration. If any failure is encountered this function will call
135  * ->cleanup_fb on any already successfully prepared framebuffer.
136  *
137  * Returns:
138  * 0 on success, negative error code on failure.
139  */
140 int drm_atomic_helper_prepare_planes(struct drm_device *dev,
141                                      struct drm_atomic_state *state)
142 {
143         int nplanes = dev->mode_config.num_total_plane;
144         int ret, i;
145
146         for (i = 0; i < nplanes; i++) {
147                 struct drm_plane_helper_funcs *funcs;
148                 struct drm_plane *plane = state->planes[i];
149                 struct drm_framebuffer *fb;
150
151                 if (!plane)
152                         continue;
153
154                 funcs = plane->helper_private;
155
156                 fb = state->plane_states[i]->fb;
157
158                 if (fb && funcs->prepare_fb) {
159                         ret = funcs->prepare_fb(plane, fb);
160                         if (ret)
161                                 goto fail;
162                 }
163         }
164
165         return 0;
166
167 fail:
168         for (i--; i >= 0; i--) {
169                 struct drm_plane_helper_funcs *funcs;
170                 struct drm_plane *plane = state->planes[i];
171                 struct drm_framebuffer *fb;
172
173                 if (!plane)
174                         continue;
175
176                 funcs = plane->helper_private;
177
178                 fb = state->plane_states[i]->fb;
179
180                 if (fb && funcs->cleanup_fb)
181                         funcs->cleanup_fb(plane, fb);
182
183         }
184
185         return ret;
186 }
187 EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
188
189 /**
190  * drm_atomic_helper_commit_planes - commit plane state
191  * @dev: DRM device
192  * @state: atomic state
193  *
194  * This function commits the new plane state using the plane and atomic helper
195  * functions for planes and crtcs. It assumes that the atomic state has already
196  * been pushed into the relevant object state pointers, since this step can no
197  * longer fail.
198  *
199  * It still requires the global state object @state to know which planes and
200  * crtcs need to be updated though.
201  */
202 void drm_atomic_helper_commit_planes(struct drm_device *dev,
203                                      struct drm_atomic_state *state)
204 {
205         int nplanes = dev->mode_config.num_total_plane;
206         int ncrtcs = dev->mode_config.num_crtc;
207         int i;
208
209         for (i = 0; i < ncrtcs; i++) {
210                 struct drm_crtc_helper_funcs *funcs;
211                 struct drm_crtc *crtc = state->crtcs[i];
212
213                 if (!crtc)
214                         continue;
215
216                 funcs = crtc->helper_private;
217
218                 if (!funcs || !funcs->atomic_begin)
219                         continue;
220
221                 funcs->atomic_begin(crtc);
222         }
223
224         for (i = 0; i < nplanes; i++) {
225                 struct drm_plane_helper_funcs *funcs;
226                 struct drm_plane *plane = state->planes[i];
227
228                 if (!plane)
229                         continue;
230
231                 funcs = plane->helper_private;
232
233                 if (!funcs || !funcs->atomic_update)
234                         continue;
235
236                 funcs->atomic_update(plane);
237         }
238
239         for (i = 0; i < ncrtcs; i++) {
240                 struct drm_crtc_helper_funcs *funcs;
241                 struct drm_crtc *crtc = state->crtcs[i];
242
243                 if (!crtc)
244                         continue;
245
246                 funcs = crtc->helper_private;
247
248                 if (!funcs || !funcs->atomic_flush)
249                         continue;
250
251                 funcs->atomic_flush(crtc);
252         }
253 }
254 EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
255
256 /**
257  * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
258  * @dev: DRM device
259  * @old_state: atomic state object with old state structures
260  *
261  * This function cleans up plane state, specifically framebuffers, from the old
262  * configuration. Hence the old configuration must be perserved in @old_state to
263  * be able to call this function.
264  *
265  * This function must also be called on the new state when the atomic update
266  * fails at any point after calling drm_atomic_helper_prepare_planes().
267  */
268 void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
269                                       struct drm_atomic_state *old_state)
270 {
271         int nplanes = dev->mode_config.num_total_plane;
272         int i;
273
274         for (i = 0; i < nplanes; i++) {
275                 struct drm_plane_helper_funcs *funcs;
276                 struct drm_plane *plane = old_state->planes[i];
277                 struct drm_framebuffer *old_fb;
278
279                 if (!plane)
280                         continue;
281
282                 funcs = plane->helper_private;
283
284                 old_fb = old_state->plane_states[i]->fb;
285
286                 if (old_fb && funcs->cleanup_fb)
287                         funcs->cleanup_fb(plane, old_fb);
288         }
289 }
290 EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
291
292 /**
293  * drm_atomic_helper_swap_state - store atomic state into current sw state
294  * @dev: DRM device
295  * @state: atomic state
296  *
297  * This function stores the atomic state into the current state pointers in all
298  * driver objects. It should be called after all failing steps have been done
299  * and succeeded, but before the actual hardware state is committed.
300  *
301  * For cleanup and error recovery the current state for all changed objects will
302  * be swaped into @state.
303  *
304  * With that sequence it fits perfectly into the plane prepare/cleanup sequence:
305  *
306  * 1. Call drm_atomic_helper_prepare_planes() with the staged atomic state.
307  *
308  * 2. Do any other steps that might fail.
309  *
310  * 3. Put the staged state into the current state pointers with this function.
311  *
312  * 4. Actually commit the hardware state.
313  *
314  * 5. Call drm_atomic_helper_cleanup_planes with @state, which since step 3
315  * contains the old state. Also do any other cleanup required with that state.
316  */
317 void drm_atomic_helper_swap_state(struct drm_device *dev,
318                                   struct drm_atomic_state *state)
319 {
320         int i;
321
322         for (i = 0; i < dev->mode_config.num_connector; i++) {
323                 struct drm_connector *connector = state->connectors[i];
324
325                 if (!connector)
326                         continue;
327
328                 connector->state->state = state;
329                 swap(state->connector_states[i], connector->state);
330                 connector->state->state = NULL;
331         }
332
333         for (i = 0; i < dev->mode_config.num_crtc; i++) {
334                 struct drm_crtc *crtc = state->crtcs[i];
335
336                 if (!crtc)
337                         continue;
338
339                 crtc->state->state = state;
340                 swap(state->crtc_states[i], crtc->state);
341                 crtc->state->state = NULL;
342         }
343
344         for (i = 0; i < dev->mode_config.num_total_plane; i++) {
345                 struct drm_plane *plane = state->planes[i];
346
347                 if (!plane)
348                         continue;
349
350                 plane->state->state = state;
351                 swap(state->plane_states[i], plane->state);
352                 plane->state->state = NULL;
353         }
354 }
355 EXPORT_SYMBOL(drm_atomic_helper_swap_state);