]> Git Repo - linux.git/blob - drivers/gpu/drm/tegra/plane.c
Merge tag 'kvmarm-fixes-5.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmar...
[linux.git] / drivers / gpu / drm / tegra / plane.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2017 NVIDIA CORPORATION.  All rights reserved.
4  */
5
6 #include <drm/drm_atomic.h>
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_fourcc.h>
9 #include <drm/drm_plane_helper.h>
10
11 #include "dc.h"
12 #include "plane.h"
13
14 static void tegra_plane_destroy(struct drm_plane *plane)
15 {
16         struct tegra_plane *p = to_tegra_plane(plane);
17
18         drm_plane_cleanup(plane);
19         kfree(p);
20 }
21
22 static void tegra_plane_reset(struct drm_plane *plane)
23 {
24         struct tegra_plane *p = to_tegra_plane(plane);
25         struct tegra_plane_state *state;
26
27         if (plane->state)
28                 __drm_atomic_helper_plane_destroy_state(plane->state);
29
30         kfree(plane->state);
31         plane->state = NULL;
32
33         state = kzalloc(sizeof(*state), GFP_KERNEL);
34         if (state) {
35                 plane->state = &state->base;
36                 plane->state->plane = plane;
37                 plane->state->zpos = p->index;
38                 plane->state->normalized_zpos = p->index;
39         }
40 }
41
42 static struct drm_plane_state *
43 tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
44 {
45         struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
46         struct tegra_plane_state *copy;
47         unsigned int i;
48
49         copy = kmalloc(sizeof(*copy), GFP_KERNEL);
50         if (!copy)
51                 return NULL;
52
53         __drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
54         copy->tiling = state->tiling;
55         copy->format = state->format;
56         copy->swap = state->swap;
57         copy->bottom_up = state->bottom_up;
58         copy->opaque = state->opaque;
59
60         for (i = 0; i < 2; i++)
61                 copy->blending[i] = state->blending[i];
62
63         return &copy->base;
64 }
65
66 static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
67                                              struct drm_plane_state *state)
68 {
69         __drm_atomic_helper_plane_destroy_state(state);
70         kfree(state);
71 }
72
73 static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
74                                              uint32_t format,
75                                              uint64_t modifier)
76 {
77         const struct drm_format_info *info = drm_format_info(format);
78
79         if (modifier == DRM_FORMAT_MOD_LINEAR)
80                 return true;
81
82         if (info->num_planes == 1)
83                 return true;
84
85         return false;
86 }
87
88 const struct drm_plane_funcs tegra_plane_funcs = {
89         .update_plane = drm_atomic_helper_update_plane,
90         .disable_plane = drm_atomic_helper_disable_plane,
91         .destroy = tegra_plane_destroy,
92         .reset = tegra_plane_reset,
93         .atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
94         .atomic_destroy_state = tegra_plane_atomic_destroy_state,
95         .format_mod_supported = tegra_plane_format_mod_supported,
96 };
97
98 int tegra_plane_state_add(struct tegra_plane *plane,
99                           struct drm_plane_state *state)
100 {
101         struct drm_crtc_state *crtc_state;
102         struct tegra_dc_state *tegra;
103         int err;
104
105         /* Propagate errors from allocation or locking failures. */
106         crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
107         if (IS_ERR(crtc_state))
108                 return PTR_ERR(crtc_state);
109
110         /* Check plane state for visibility and calculate clipping bounds */
111         err = drm_atomic_helper_check_plane_state(state, crtc_state,
112                                                   0, INT_MAX, true, true);
113         if (err < 0)
114                 return err;
115
116         tegra = to_dc_state(crtc_state);
117
118         tegra->planes |= WIN_A_ACT_REQ << plane->index;
119
120         return 0;
121 }
122
123 int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap)
124 {
125         /* assume no swapping of fetched data */
126         if (swap)
127                 *swap = BYTE_SWAP_NOSWAP;
128
129         switch (fourcc) {
130         case DRM_FORMAT_ARGB4444:
131                 *format = WIN_COLOR_DEPTH_B4G4R4A4;
132                 break;
133
134         case DRM_FORMAT_ARGB1555:
135                 *format = WIN_COLOR_DEPTH_B5G5R5A1;
136                 break;
137
138         case DRM_FORMAT_RGB565:
139                 *format = WIN_COLOR_DEPTH_B5G6R5;
140                 break;
141
142         case DRM_FORMAT_RGBA5551:
143                 *format = WIN_COLOR_DEPTH_A1B5G5R5;
144                 break;
145
146         case DRM_FORMAT_ARGB8888:
147                 *format = WIN_COLOR_DEPTH_B8G8R8A8;
148                 break;
149
150         case DRM_FORMAT_ABGR8888:
151                 *format = WIN_COLOR_DEPTH_R8G8B8A8;
152                 break;
153
154         case DRM_FORMAT_ABGR4444:
155                 *format = WIN_COLOR_DEPTH_R4G4B4A4;
156                 break;
157
158         case DRM_FORMAT_ABGR1555:
159                 *format = WIN_COLOR_DEPTH_R5G5B5A;
160                 break;
161
162         case DRM_FORMAT_BGRA5551:
163                 *format = WIN_COLOR_DEPTH_AR5G5B5;
164                 break;
165
166         case DRM_FORMAT_XRGB1555:
167                 *format = WIN_COLOR_DEPTH_B5G5R5X1;
168                 break;
169
170         case DRM_FORMAT_RGBX5551:
171                 *format = WIN_COLOR_DEPTH_X1B5G5R5;
172                 break;
173
174         case DRM_FORMAT_XBGR1555:
175                 *format = WIN_COLOR_DEPTH_R5G5B5X1;
176                 break;
177
178         case DRM_FORMAT_BGRX5551:
179                 *format = WIN_COLOR_DEPTH_X1R5G5B5;
180                 break;
181
182         case DRM_FORMAT_BGR565:
183                 *format = WIN_COLOR_DEPTH_R5G6B5;
184                 break;
185
186         case DRM_FORMAT_BGRA8888:
187                 *format = WIN_COLOR_DEPTH_A8R8G8B8;
188                 break;
189
190         case DRM_FORMAT_RGBA8888:
191                 *format = WIN_COLOR_DEPTH_A8B8G8R8;
192                 break;
193
194         case DRM_FORMAT_XRGB8888:
195                 *format = WIN_COLOR_DEPTH_B8G8R8X8;
196                 break;
197
198         case DRM_FORMAT_XBGR8888:
199                 *format = WIN_COLOR_DEPTH_R8G8B8X8;
200                 break;
201
202         case DRM_FORMAT_UYVY:
203                 *format = WIN_COLOR_DEPTH_YCbCr422;
204                 break;
205
206         case DRM_FORMAT_YUYV:
207                 if (!swap)
208                         return -EINVAL;
209
210                 *format = WIN_COLOR_DEPTH_YCbCr422;
211                 *swap = BYTE_SWAP_SWAP2;
212                 break;
213
214         case DRM_FORMAT_YUV420:
215                 *format = WIN_COLOR_DEPTH_YCbCr420P;
216                 break;
217
218         case DRM_FORMAT_YUV422:
219                 *format = WIN_COLOR_DEPTH_YCbCr422P;
220                 break;
221
222         default:
223                 return -EINVAL;
224         }
225
226         return 0;
227 }
228
229 bool tegra_plane_format_is_yuv(unsigned int format, bool *planar)
230 {
231         switch (format) {
232         case WIN_COLOR_DEPTH_YCbCr422:
233         case WIN_COLOR_DEPTH_YUV422:
234                 if (planar)
235                         *planar = false;
236
237                 return true;
238
239         case WIN_COLOR_DEPTH_YCbCr420P:
240         case WIN_COLOR_DEPTH_YUV420P:
241         case WIN_COLOR_DEPTH_YCbCr422P:
242         case WIN_COLOR_DEPTH_YUV422P:
243         case WIN_COLOR_DEPTH_YCbCr422R:
244         case WIN_COLOR_DEPTH_YUV422R:
245         case WIN_COLOR_DEPTH_YCbCr422RA:
246         case WIN_COLOR_DEPTH_YUV422RA:
247                 if (planar)
248                         *planar = true;
249
250                 return true;
251         }
252
253         if (planar)
254                 *planar = false;
255
256         return false;
257 }
258
259 static bool __drm_format_has_alpha(u32 format)
260 {
261         switch (format) {
262         case DRM_FORMAT_ARGB1555:
263         case DRM_FORMAT_RGBA5551:
264         case DRM_FORMAT_ABGR8888:
265         case DRM_FORMAT_ARGB8888:
266                 return true;
267         }
268
269         return false;
270 }
271
272 static int tegra_plane_format_get_alpha(unsigned int opaque,
273                                         unsigned int *alpha)
274 {
275         if (tegra_plane_format_is_yuv(opaque, NULL)) {
276                 *alpha = opaque;
277                 return 0;
278         }
279
280         switch (opaque) {
281         case WIN_COLOR_DEPTH_B5G5R5X1:
282                 *alpha = WIN_COLOR_DEPTH_B5G5R5A1;
283                 return 0;
284
285         case WIN_COLOR_DEPTH_X1B5G5R5:
286                 *alpha = WIN_COLOR_DEPTH_A1B5G5R5;
287                 return 0;
288
289         case WIN_COLOR_DEPTH_R8G8B8X8:
290                 *alpha = WIN_COLOR_DEPTH_R8G8B8A8;
291                 return 0;
292
293         case WIN_COLOR_DEPTH_B8G8R8X8:
294                 *alpha = WIN_COLOR_DEPTH_B8G8R8A8;
295                 return 0;
296
297         case WIN_COLOR_DEPTH_B5G6R5:
298                 *alpha = opaque;
299                 return 0;
300         }
301
302         return -EINVAL;
303 }
304
305 /*
306  * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
307  * be emulated using the alpha formats and alpha blending disabled.
308  */
309 static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
310                                      struct tegra_plane_state *state)
311 {
312         unsigned int format;
313         int err;
314
315         switch (state->format) {
316         case WIN_COLOR_DEPTH_B5G5R5A1:
317         case WIN_COLOR_DEPTH_A1B5G5R5:
318         case WIN_COLOR_DEPTH_R8G8B8A8:
319         case WIN_COLOR_DEPTH_B8G8R8A8:
320                 state->opaque = false;
321                 break;
322
323         default:
324                 err = tegra_plane_format_get_alpha(state->format, &format);
325                 if (err < 0)
326                         return err;
327
328                 state->format = format;
329                 state->opaque = true;
330                 break;
331         }
332
333         return 0;
334 }
335
336 static int tegra_plane_check_transparency(struct tegra_plane *tegra,
337                                           struct tegra_plane_state *state)
338 {
339         struct drm_plane_state *old, *plane_state;
340         struct drm_plane *plane;
341
342         old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
343
344         /* check if zpos / transparency changed */
345         if (old->normalized_zpos == state->base.normalized_zpos &&
346             to_tegra_plane_state(old)->opaque == state->opaque)
347                 return 0;
348
349         /* include all sibling planes into this commit */
350         drm_for_each_plane(plane, tegra->base.dev) {
351                 struct tegra_plane *p = to_tegra_plane(plane);
352
353                 /* skip this plane and planes on different CRTCs */
354                 if (p == tegra || p->dc != tegra->dc)
355                         continue;
356
357                 plane_state = drm_atomic_get_plane_state(state->base.state,
358                                                          plane);
359                 if (IS_ERR(plane_state))
360                         return PTR_ERR(plane_state);
361         }
362
363         return 1;
364 }
365
366 static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
367                                                   struct tegra_plane *other)
368 {
369         unsigned int index = 0, i;
370
371         WARN_ON(plane == other);
372
373         for (i = 0; i < 3; i++) {
374                 if (i == plane->index)
375                         continue;
376
377                 if (i == other->index)
378                         break;
379
380                 index++;
381         }
382
383         return index;
384 }
385
386 static void tegra_plane_update_transparency(struct tegra_plane *tegra,
387                                             struct tegra_plane_state *state)
388 {
389         struct drm_plane_state *new;
390         struct drm_plane *plane;
391         unsigned int i;
392
393         for_each_new_plane_in_state(state->base.state, plane, new, i) {
394                 struct tegra_plane *p = to_tegra_plane(plane);
395                 unsigned index;
396
397                 /* skip this plane and planes on different CRTCs */
398                 if (p == tegra || p->dc != tegra->dc)
399                         continue;
400
401                 index = tegra_plane_get_overlap_index(tegra, p);
402
403                 if (new->fb && __drm_format_has_alpha(new->fb->format->format))
404                         state->blending[index].alpha = true;
405                 else
406                         state->blending[index].alpha = false;
407
408                 if (new->normalized_zpos > state->base.normalized_zpos)
409                         state->blending[index].top = true;
410                 else
411                         state->blending[index].top = false;
412
413                 /*
414                  * Missing framebuffer means that plane is disabled, in this
415                  * case mark B / C window as top to be able to differentiate
416                  * windows indices order in regards to zPos for the middle
417                  * window X / Y registers programming.
418                  */
419                 if (!new->fb)
420                         state->blending[index].top = (index == 1);
421         }
422 }
423
424 static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
425                                           struct tegra_plane_state *state)
426 {
427         struct tegra_plane_state *tegra_state;
428         struct drm_plane_state *new;
429         struct drm_plane *plane;
430         int err;
431
432         /*
433          * If planes zpos / transparency changed, sibling planes blending
434          * state may require adjustment and in this case they will be included
435          * into this atom commit, otherwise blending state is unchanged.
436          */
437         err = tegra_plane_check_transparency(tegra, state);
438         if (err <= 0)
439                 return err;
440
441         /*
442          * All planes are now in the atomic state, walk them up and update
443          * transparency state for each plane.
444          */
445         drm_for_each_plane(plane, tegra->base.dev) {
446                 struct tegra_plane *p = to_tegra_plane(plane);
447
448                 /* skip planes on different CRTCs */
449                 if (p->dc != tegra->dc)
450                         continue;
451
452                 new = drm_atomic_get_new_plane_state(state->base.state, plane);
453                 tegra_state = to_tegra_plane_state(new);
454
455                 /*
456                  * There is no need to update blending state for the disabled
457                  * plane.
458                  */
459                 if (new->fb)
460                         tegra_plane_update_transparency(p, tegra_state);
461         }
462
463         return 0;
464 }
465
466 int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
467                                    struct tegra_plane_state *state)
468 {
469         int err;
470
471         err = tegra_plane_setup_opacity(tegra, state);
472         if (err < 0)
473                 return err;
474
475         err = tegra_plane_setup_transparency(tegra, state);
476         if (err < 0)
477                 return err;
478
479         return 0;
480 }
This page took 0.063377 seconds and 4 git commands to generate.