diff options
author | Thierry Reding <treding@nvidia.com> | 2017-12-20 03:39:14 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2017-12-21 08:55:55 -0500 |
commit | ebae8d07435ae91314f4a28d69b530d09c625815 (patch) | |
tree | fbe41c4c436502a2f073a3c352d3b14a3886cf15 | |
parent | 4c69ac12e39d3d623686d1c421b05d604e1c6db9 (diff) |
drm/tegra: dc: Implement legacy blending
This implements alpha blending on legacy display controllers (Tegra20,
Tegra30 and Tegra114). While it's theoretically possible to support the
zpos property to enable userspace to specify the Z-order of each plane
individually, this is not currently supported and the same fixed Z-
order as previously defined is used.
Reverts commit 71835caa00e8 ("drm/tegra: fb: Force alpha formats") since
the opaque formats are now supported.
Reported-by: Dmitry Osipenko <digetx@gmail.com>
Fixes: 7772fdaef939 ("drm/tegra: Support ARGB and ABGR formats")
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 81 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.h | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/fb.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/plane.c | 138 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/plane.h | 8 |
5 files changed, 226 insertions, 25 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2a0c1e93f82e..4507063029e0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c | |||
@@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in) | |||
154 | 154 | ||
155 | static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) | 155 | static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) |
156 | { | 156 | { |
157 | u32 background[3] = { | ||
158 | BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, | ||
159 | BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, | ||
160 | BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE, | ||
161 | }; | ||
162 | u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) | | ||
163 | BLEND_COLOR_KEY_NONE; | ||
164 | u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); | ||
165 | struct tegra_plane_state *state; | ||
166 | unsigned int i; | ||
167 | |||
168 | state = to_tegra_plane_state(plane->base.state); | ||
169 | |||
170 | /* alpha contribution is 1 minus sum of overlapping windows */ | ||
171 | for (i = 0; i < 3; i++) { | ||
172 | if (state->dependent[i]) | ||
173 | background[i] |= BLEND_CONTROL_DEPENDENT; | ||
174 | } | ||
175 | |||
176 | /* enable alpha blending if pixel format has an alpha component */ | ||
177 | if (!state->opaque) | ||
178 | foreground |= BLEND_CONTROL_ALPHA; | ||
179 | |||
157 | /* | 180 | /* |
158 | * Disable blending and assume Window A is the bottom-most window, | 181 | * Disable blending and assume Window A is the bottom-most window, |
159 | * Window C is the top-most window and Window B is in the middle. | 182 | * Window C is the top-most window and Window B is in the middle. |
160 | */ | 183 | */ |
161 | tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY); | 184 | tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); |
162 | tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN); | 185 | tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); |
163 | 186 | ||
164 | switch (plane->index) { | 187 | switch (plane->index) { |
165 | case 0: | 188 | case 0: |
166 | tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X); | 189 | tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); |
167 | tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); | 190 | tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); |
168 | tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); | 191 | tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); |
169 | break; | 192 | break; |
170 | 193 | ||
171 | case 1: | 194 | case 1: |
172 | tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); | 195 | tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); |
173 | tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y); | 196 | tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); |
174 | tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY); | 197 | tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY); |
175 | break; | 198 | break; |
176 | 199 | ||
177 | case 2: | 200 | case 2: |
178 | tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X); | 201 | tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X); |
179 | tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y); | 202 | tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y); |
180 | tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY); | 203 | tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY); |
181 | break; | 204 | break; |
182 | } | 205 | } |
183 | } | 206 | } |
@@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = { | |||
353 | DRM_FORMAT_RGBA5551, | 376 | DRM_FORMAT_RGBA5551, |
354 | DRM_FORMAT_ABGR8888, | 377 | DRM_FORMAT_ABGR8888, |
355 | DRM_FORMAT_ARGB8888, | 378 | DRM_FORMAT_ARGB8888, |
379 | /* non-native formats */ | ||
380 | DRM_FORMAT_XRGB1555, | ||
381 | DRM_FORMAT_RGBX5551, | ||
382 | DRM_FORMAT_XBGR8888, | ||
383 | DRM_FORMAT_XRGB8888, | ||
356 | }; | 384 | }; |
357 | 385 | ||
358 | static const u32 tegra114_primary_formats[] = { | 386 | static const u32 tegra114_primary_formats[] = { |
@@ -409,18 +437,40 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, | |||
409 | struct tegra_bo_tiling *tiling = &plane_state->tiling; | 437 | struct tegra_bo_tiling *tiling = &plane_state->tiling; |
410 | struct tegra_plane *tegra = to_tegra_plane(plane); | 438 | struct tegra_plane *tegra = to_tegra_plane(plane); |
411 | struct tegra_dc *dc = to_tegra_dc(state->crtc); | 439 | struct tegra_dc *dc = to_tegra_dc(state->crtc); |
440 | unsigned int format; | ||
412 | int err; | 441 | int err; |
413 | 442 | ||
414 | /* no need for further checks if the plane is being disabled */ | 443 | /* no need for further checks if the plane is being disabled */ |
415 | if (!state->crtc) | 444 | if (!state->crtc) |
416 | return 0; | 445 | return 0; |
417 | 446 | ||
418 | err = tegra_plane_format(state->fb->format->format, | 447 | err = tegra_plane_format(state->fb->format->format, &format, |
419 | &plane_state->format, | ||
420 | &plane_state->swap); | 448 | &plane_state->swap); |
421 | if (err < 0) | 449 | if (err < 0) |
422 | return err; | 450 | return err; |
423 | 451 | ||
452 | /* | ||
453 | * Tegra20 and Tegra30 are special cases here because they support | ||
454 | * only variants of specific formats with an alpha component, but not | ||
455 | * the corresponding opaque formats. However, the opaque formats can | ||
456 | * be emulated by disabling alpha blending for the plane. | ||
457 | */ | ||
458 | if (!dc->soc->supports_blending) { | ||
459 | if (!tegra_plane_format_has_alpha(format)) { | ||
460 | err = tegra_plane_format_get_alpha(format, &format); | ||
461 | if (err < 0) | ||
462 | return err; | ||
463 | |||
464 | plane_state->opaque = true; | ||
465 | } else { | ||
466 | plane_state->opaque = false; | ||
467 | } | ||
468 | |||
469 | tegra_plane_check_dependent(tegra, plane_state); | ||
470 | } | ||
471 | |||
472 | plane_state->format = format; | ||
473 | |||
424 | err = tegra_fb_get_tiling(state->fb, tiling); | 474 | err = tegra_fb_get_tiling(state->fb, tiling); |
425 | if (err < 0) | 475 | if (err < 0) |
426 | return err; | 476 | return err; |
@@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = { | |||
737 | DRM_FORMAT_RGBA5551, | 787 | DRM_FORMAT_RGBA5551, |
738 | DRM_FORMAT_ABGR8888, | 788 | DRM_FORMAT_ABGR8888, |
739 | DRM_FORMAT_ARGB8888, | 789 | DRM_FORMAT_ARGB8888, |
790 | /* non-native formats */ | ||
791 | DRM_FORMAT_XRGB1555, | ||
792 | DRM_FORMAT_RGBX5551, | ||
793 | DRM_FORMAT_XBGR8888, | ||
794 | DRM_FORMAT_XRGB8888, | ||
740 | /* planar formats */ | 795 | /* planar formats */ |
741 | DRM_FORMAT_UYVY, | 796 | DRM_FORMAT_UYVY, |
742 | DRM_FORMAT_YUYV, | 797 | DRM_FORMAT_YUYV, |
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e2831e96ea96..096a81ad6d8d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h | |||
@@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); | |||
649 | #define DC_WIN_DV_CONTROL 0x70e | 649 | #define DC_WIN_DV_CONTROL 0x70e |
650 | 650 | ||
651 | #define DC_WIN_BLEND_NOKEY 0x70f | 651 | #define DC_WIN_BLEND_NOKEY 0x70f |
652 | #define BLEND_WEIGHT1(x) (((x) & 0xff) << 16) | ||
653 | #define BLEND_WEIGHT0(x) (((x) & 0xff) << 8) | ||
654 | |||
652 | #define DC_WIN_BLEND_1WIN 0x710 | 655 | #define DC_WIN_BLEND_1WIN 0x710 |
656 | #define BLEND_CONTROL_FIX (0 << 2) | ||
657 | #define BLEND_CONTROL_ALPHA (1 << 2) | ||
658 | #define BLEND_COLOR_KEY_NONE (0 << 0) | ||
659 | #define BLEND_COLOR_KEY_0 (1 << 0) | ||
660 | #define BLEND_COLOR_KEY_1 (2 << 0) | ||
661 | #define BLEND_COLOR_KEY_BOTH (3 << 0) | ||
662 | |||
653 | #define DC_WIN_BLEND_2WIN_X 0x711 | 663 | #define DC_WIN_BLEND_2WIN_X 0x711 |
664 | #define BLEND_CONTROL_DEPENDENT (2 << 2) | ||
665 | |||
654 | #define DC_WIN_BLEND_2WIN_Y 0x712 | 666 | #define DC_WIN_BLEND_2WIN_Y 0x712 |
655 | #define DC_WIN_BLEND_3WIN_XY 0x713 | 667 | #define DC_WIN_BLEND_3WIN_XY 0x713 |
656 | 668 | ||
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1af4ef9241f1..e05fde7172f8 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c | |||
@@ -254,18 +254,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, | |||
254 | cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, | 254 | cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, |
255 | tegra->pitch_align); | 255 | tegra->pitch_align); |
256 | 256 | ||
257 | /* | ||
258 | * Early generations of Tegra (Tegra20 and Tegra30) do not support any | ||
259 | * of the X* or *X formats, only their A* or *A equivalents. Force the | ||
260 | * legacy framebuffer format to include an alpha component so that the | ||
261 | * framebuffer emulation can be supported on all generations. | ||
262 | */ | ||
263 | if (sizes->surface_bpp == 32 && sizes->surface_depth == 24) | ||
264 | sizes->surface_depth = 32; | ||
265 | |||
266 | if (sizes->surface_bpp == 16 && sizes->surface_depth == 15) | ||
267 | sizes->surface_depth = 16; | ||
268 | |||
269 | cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | 257 | cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, |
270 | sizes->surface_depth); | 258 | sizes->surface_depth); |
271 | 259 | ||
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 9146aead973b..154b4d337d0a 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c | |||
@@ -43,6 +43,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) | |||
43 | { | 43 | { |
44 | struct tegra_plane_state *state = to_tegra_plane_state(plane->state); | 44 | struct tegra_plane_state *state = to_tegra_plane_state(plane->state); |
45 | struct tegra_plane_state *copy; | 45 | struct tegra_plane_state *copy; |
46 | unsigned int i; | ||
46 | 47 | ||
47 | copy = kmalloc(sizeof(*copy), GFP_KERNEL); | 48 | copy = kmalloc(sizeof(*copy), GFP_KERNEL); |
48 | if (!copy) | 49 | if (!copy) |
@@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) | |||
52 | copy->tiling = state->tiling; | 53 | copy->tiling = state->tiling; |
53 | copy->format = state->format; | 54 | copy->format = state->format; |
54 | copy->swap = state->swap; | 55 | copy->swap = state->swap; |
56 | copy->opaque = state->opaque; | ||
57 | |||
58 | for (i = 0; i < 3; i++) | ||
59 | copy->dependent[i] = state->dependent[i]; | ||
55 | 60 | ||
56 | return ©->base; | 61 | return ©->base; |
57 | } | 62 | } |
@@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) | |||
238 | 243 | ||
239 | return false; | 244 | return false; |
240 | } | 245 | } |
246 | |||
247 | static bool __drm_format_has_alpha(u32 format) | ||
248 | { | ||
249 | switch (format) { | ||
250 | case DRM_FORMAT_ARGB1555: | ||
251 | case DRM_FORMAT_RGBA5551: | ||
252 | case DRM_FORMAT_ABGR8888: | ||
253 | case DRM_FORMAT_ARGB8888: | ||
254 | return true; | ||
255 | } | ||
256 | |||
257 | return false; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * This is applicable to Tegra20 and Tegra30 only where the opaque formats can | ||
262 | * be emulated using the alpha formats and alpha blending disabled. | ||
263 | */ | ||
264 | bool tegra_plane_format_has_alpha(unsigned int format) | ||
265 | { | ||
266 | switch (format) { | ||
267 | case WIN_COLOR_DEPTH_B5G5R5A1: | ||
268 | case WIN_COLOR_DEPTH_A1B5G5R5: | ||
269 | case WIN_COLOR_DEPTH_R8G8B8A8: | ||
270 | case WIN_COLOR_DEPTH_B8G8R8A8: | ||
271 | return true; | ||
272 | } | ||
273 | |||
274 | return false; | ||
275 | } | ||
276 | |||
277 | int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) | ||
278 | { | ||
279 | switch (opaque) { | ||
280 | case WIN_COLOR_DEPTH_B5G5R5X1: | ||
281 | *alpha = WIN_COLOR_DEPTH_B5G5R5A1; | ||
282 | return 0; | ||
283 | |||
284 | case WIN_COLOR_DEPTH_X1B5G5R5: | ||
285 | *alpha = WIN_COLOR_DEPTH_A1B5G5R5; | ||
286 | return 0; | ||
287 | |||
288 | case WIN_COLOR_DEPTH_R8G8B8X8: | ||
289 | *alpha = WIN_COLOR_DEPTH_R8G8B8A8; | ||
290 | return 0; | ||
291 | |||
292 | case WIN_COLOR_DEPTH_B8G8R8X8: | ||
293 | *alpha = WIN_COLOR_DEPTH_B8G8R8A8; | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | return -EINVAL; | ||
298 | } | ||
299 | |||
300 | unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, | ||
301 | struct tegra_plane *other) | ||
302 | { | ||
303 | unsigned int index = 0, i; | ||
304 | |||
305 | WARN_ON(plane == other); | ||
306 | |||
307 | for (i = 0; i < 3; i++) { | ||
308 | if (i == plane->index) | ||
309 | continue; | ||
310 | |||
311 | if (i == other->index) | ||
312 | break; | ||
313 | |||
314 | index++; | ||
315 | } | ||
316 | |||
317 | return index; | ||
318 | } | ||
319 | |||
320 | void tegra_plane_check_dependent(struct tegra_plane *tegra, | ||
321 | struct tegra_plane_state *state) | ||
322 | { | ||
323 | struct drm_plane_state *old, *new; | ||
324 | struct drm_plane *plane; | ||
325 | unsigned int zpos[2]; | ||
326 | unsigned int i; | ||
327 | |||
328 | for (i = 0; i < 3; i++) | ||
329 | state->dependent[i] = false; | ||
330 | |||
331 | for (i = 0; i < 2; i++) | ||
332 | zpos[i] = 0; | ||
333 | |||
334 | for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { | ||
335 | struct tegra_plane *p = to_tegra_plane(plane); | ||
336 | unsigned index; | ||
337 | |||
338 | /* skip this plane and planes on different CRTCs */ | ||
339 | if (p == tegra || new->crtc != state->base.crtc) | ||
340 | continue; | ||
341 | |||
342 | index = tegra_plane_get_overlap_index(tegra, p); | ||
343 | |||
344 | /* | ||
345 | * If any of the other planes is on top of this plane and uses | ||
346 | * a format with an alpha component, mark this plane as being | ||
347 | * dependent, meaning it's alpha value will be 1 minus the sum | ||
348 | * of alpha components of the overlapping planes. | ||
349 | */ | ||
350 | if (p->index > tegra->index) { | ||
351 | if (__drm_format_has_alpha(new->fb->format->format)) | ||
352 | state->dependent[index] = true; | ||
353 | |||
354 | /* keep track of the Z position */ | ||
355 | zpos[index] = p->index; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * The region where three windows overlap is the intersection of the | ||
361 | * two regions where two windows overlap. It contributes to the area | ||
362 | * if any of the windows on top of it have an alpha component. | ||
363 | */ | ||
364 | for (i = 0; i < 2; i++) | ||
365 | state->dependent[2] = state->dependent[2] || | ||
366 | state->dependent[i]; | ||
367 | |||
368 | /* | ||
369 | * However, if any of the windows on top of this window is opaque, it | ||
370 | * will completely conceal this window within that area, so avoid the | ||
371 | * window from contributing to the area. | ||
372 | */ | ||
373 | for (i = 0; i < 2; i++) { | ||
374 | if (zpos[i] > tegra->index) | ||
375 | state->dependent[2] = state->dependent[2] && | ||
376 | state->dependent[i]; | ||
377 | } | ||
378 | } | ||
diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h index dca66cb95d25..6938719e7e5d 100644 --- a/drivers/gpu/drm/tegra/plane.h +++ b/drivers/gpu/drm/tegra/plane.h | |||
@@ -40,6 +40,10 @@ struct tegra_plane_state { | |||
40 | struct tegra_bo_tiling tiling; | 40 | struct tegra_bo_tiling tiling; |
41 | u32 format; | 41 | u32 format; |
42 | u32 swap; | 42 | u32 swap; |
43 | |||
44 | /* used for legacy blending support only */ | ||
45 | bool opaque; | ||
46 | bool dependent[3]; | ||
43 | }; | 47 | }; |
44 | 48 | ||
45 | static inline struct tegra_plane_state * | 49 | static inline struct tegra_plane_state * |
@@ -58,5 +62,9 @@ int tegra_plane_state_add(struct tegra_plane *plane, | |||
58 | 62 | ||
59 | int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); | 63 | int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); |
60 | bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); | 64 | bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); |
65 | bool tegra_plane_format_has_alpha(unsigned int format); | ||
66 | int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha); | ||
67 | void tegra_plane_check_dependent(struct tegra_plane *tegra, | ||
68 | struct tegra_plane_state *state); | ||
61 | 69 | ||
62 | #endif /* TEGRA_PLANE_H */ | 70 | #endif /* TEGRA_PLANE_H */ |