diff options
author | Dmitry Osipenko <digetx@gmail.com> | 2018-05-04 10:39:59 -0400 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2018-05-17 08:08:44 -0400 |
commit | 3dae08bc076b93487ed2df50bcfa892113e89d9d (patch) | |
tree | ddbb3a4b0de1dca357b921ff037aedaea72a59bf /drivers/gpu/drm/tegra/plane.c | |
parent | acc6a3a9afdd4e0537342012656cdb5c4a3127c5 (diff) |
drm/tegra: plane: Implement zpos plane property for older Tegras
Older Tegra's do not support plane's Z position handling in hardware,
but the hardware provides knobs to implement it in software.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/tegra/plane.c')
-rw-r--r-- | drivers/gpu/drm/tegra/plane.c | 193 |
1 files changed, 139 insertions, 54 deletions
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c index 176ef46c615c..0406c2ef432c 100644 --- a/drivers/gpu/drm/tegra/plane.c +++ b/drivers/gpu/drm/tegra/plane.c | |||
@@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane) | |||
23 | 23 | ||
24 | static void tegra_plane_reset(struct drm_plane *plane) | 24 | static void tegra_plane_reset(struct drm_plane *plane) |
25 | { | 25 | { |
26 | struct tegra_plane *p = to_tegra_plane(plane); | ||
26 | struct tegra_plane_state *state; | 27 | struct tegra_plane_state *state; |
27 | 28 | ||
28 | if (plane->state) | 29 | if (plane->state) |
@@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane) | |||
35 | if (state) { | 36 | if (state) { |
36 | plane->state = &state->base; | 37 | plane->state = &state->base; |
37 | plane->state->plane = plane; | 38 | plane->state->plane = plane; |
39 | plane->state->zpos = p->index; | ||
40 | plane->state->normalized_zpos = p->index; | ||
38 | } | 41 | } |
39 | } | 42 | } |
40 | 43 | ||
@@ -55,8 +58,8 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) | |||
55 | copy->swap = state->swap; | 58 | copy->swap = state->swap; |
56 | copy->opaque = state->opaque; | 59 | copy->opaque = state->opaque; |
57 | 60 | ||
58 | for (i = 0; i < 3; i++) | 61 | for (i = 0; i < 2; i++) |
59 | copy->dependent[i] = state->dependent[i]; | 62 | copy->blending[i] = state->blending[i]; |
60 | 63 | ||
61 | return ©->base; | 64 | return ©->base; |
62 | } | 65 | } |
@@ -267,24 +270,8 @@ static bool __drm_format_has_alpha(u32 format) | |||
267 | return false; | 270 | return false; |
268 | } | 271 | } |
269 | 272 | ||
270 | /* | 273 | static int tegra_plane_format_get_alpha(unsigned int opaque, |
271 | * This is applicable to Tegra20 and Tegra30 only where the opaque formats can | 274 | unsigned int *alpha) |
272 | * be emulated using the alpha formats and alpha blending disabled. | ||
273 | */ | ||
274 | bool tegra_plane_format_has_alpha(unsigned int format) | ||
275 | { | ||
276 | switch (format) { | ||
277 | case WIN_COLOR_DEPTH_B5G5R5A1: | ||
278 | case WIN_COLOR_DEPTH_A1B5G5R5: | ||
279 | case WIN_COLOR_DEPTH_R8G8B8A8: | ||
280 | case WIN_COLOR_DEPTH_B8G8R8A8: | ||
281 | return true; | ||
282 | } | ||
283 | |||
284 | return false; | ||
285 | } | ||
286 | |||
287 | int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) | ||
288 | { | 275 | { |
289 | if (tegra_plane_format_is_yuv(opaque, NULL)) { | 276 | if (tegra_plane_format_is_yuv(opaque, NULL)) { |
290 | *alpha = opaque; | 277 | *alpha = opaque; |
@@ -316,6 +303,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) | |||
316 | return -EINVAL; | 303 | return -EINVAL; |
317 | } | 304 | } |
318 | 305 | ||
306 | /* | ||
307 | * This is applicable to Tegra20 and Tegra30 only where the opaque formats can | ||
308 | * be emulated using the alpha formats and alpha blending disabled. | ||
309 | */ | ||
310 | static int tegra_plane_setup_opacity(struct tegra_plane *tegra, | ||
311 | struct tegra_plane_state *state) | ||
312 | { | ||
313 | unsigned int format; | ||
314 | int err; | ||
315 | |||
316 | switch (state->format) { | ||
317 | case WIN_COLOR_DEPTH_B5G5R5A1: | ||
318 | case WIN_COLOR_DEPTH_A1B5G5R5: | ||
319 | case WIN_COLOR_DEPTH_R8G8B8A8: | ||
320 | case WIN_COLOR_DEPTH_B8G8R8A8: | ||
321 | state->opaque = false; | ||
322 | break; | ||
323 | |||
324 | default: | ||
325 | err = tegra_plane_format_get_alpha(state->format, &format); | ||
326 | if (err < 0) | ||
327 | return err; | ||
328 | |||
329 | state->format = format; | ||
330 | state->opaque = true; | ||
331 | break; | ||
332 | } | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int tegra_plane_check_transparency(struct tegra_plane *tegra, | ||
338 | struct tegra_plane_state *state) | ||
339 | { | ||
340 | struct drm_plane_state *old, *plane_state; | ||
341 | struct drm_plane *plane; | ||
342 | |||
343 | old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base); | ||
344 | |||
345 | /* check if zpos / transparency changed */ | ||
346 | if (old->normalized_zpos == state->base.normalized_zpos && | ||
347 | to_tegra_plane_state(old)->opaque == state->opaque) | ||
348 | return 0; | ||
349 | |||
350 | /* include all sibling planes into this commit */ | ||
351 | drm_for_each_plane(plane, tegra->base.dev) { | ||
352 | struct tegra_plane *p = to_tegra_plane(plane); | ||
353 | |||
354 | /* skip this plane and planes on different CRTCs */ | ||
355 | if (p == tegra || p->dc != tegra->dc) | ||
356 | continue; | ||
357 | |||
358 | plane_state = drm_atomic_get_plane_state(state->base.state, | ||
359 | plane); | ||
360 | if (IS_ERR(plane_state)) | ||
361 | return PTR_ERR(plane_state); | ||
362 | } | ||
363 | |||
364 | return 1; | ||
365 | } | ||
366 | |||
319 | static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, | 367 | static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, |
320 | struct tegra_plane *other) | 368 | struct tegra_plane *other) |
321 | { | 369 | { |
@@ -336,61 +384,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, | |||
336 | return index; | 384 | return index; |
337 | } | 385 | } |
338 | 386 | ||
339 | void tegra_plane_check_dependent(struct tegra_plane *tegra, | 387 | static void tegra_plane_update_transparency(struct tegra_plane *tegra, |
340 | struct tegra_plane_state *state) | 388 | struct tegra_plane_state *state) |
341 | { | 389 | { |
342 | struct drm_plane_state *old, *new; | 390 | struct drm_plane_state *new; |
343 | struct drm_plane *plane; | 391 | struct drm_plane *plane; |
344 | unsigned int zpos[2]; | ||
345 | unsigned int i; | 392 | unsigned int i; |
346 | 393 | ||
347 | for (i = 0; i < 2; i++) | 394 | for_each_new_plane_in_state(state->base.state, plane, new, i) { |
348 | zpos[i] = 0; | ||
349 | |||
350 | for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { | ||
351 | struct tegra_plane *p = to_tegra_plane(plane); | 395 | struct tegra_plane *p = to_tegra_plane(plane); |
352 | unsigned index; | 396 | unsigned index; |
353 | 397 | ||
354 | /* skip this plane and planes on different CRTCs */ | 398 | /* skip this plane and planes on different CRTCs */ |
355 | if (p == tegra || new->crtc != state->base.crtc) | 399 | if (p == tegra || p->dc != tegra->dc) |
356 | continue; | 400 | continue; |
357 | 401 | ||
358 | index = tegra_plane_get_overlap_index(tegra, p); | 402 | index = tegra_plane_get_overlap_index(tegra, p); |
359 | 403 | ||
360 | state->dependent[index] = false; | 404 | if (new->fb && __drm_format_has_alpha(new->fb->format->format)) |
405 | state->blending[index].alpha = true; | ||
406 | else | ||
407 | state->blending[index].alpha = false; | ||
408 | |||
409 | if (new->normalized_zpos > state->base.normalized_zpos) | ||
410 | state->blending[index].top = true; | ||
411 | else | ||
412 | state->blending[index].top = false; | ||
361 | 413 | ||
362 | /* | 414 | /* |
363 | * If any of the other planes is on top of this plane and uses | 415 | * Missing framebuffer means that plane is disabled, in this |
364 | * a format with an alpha component, mark this plane as being | 416 | * case mark B / C window as top to be able to differentiate |
365 | * dependent, meaning it's alpha value will be 1 minus the sum | 417 | * windows indices order in regards to zPos for the middle |
366 | * of alpha components of the overlapping planes. | 418 | * window X / Y registers programming. |
367 | */ | 419 | */ |
368 | if (p->index > tegra->index) { | 420 | if (!new->fb) |
369 | if (__drm_format_has_alpha(new->fb->format->format)) | 421 | state->blending[index].top = (index == 1); |
370 | state->dependent[index] = true; | ||
371 | |||
372 | /* keep track of the Z position */ | ||
373 | zpos[index] = p->index; | ||
374 | } | ||
375 | } | 422 | } |
423 | } | ||
424 | |||
425 | static int tegra_plane_setup_transparency(struct tegra_plane *tegra, | ||
426 | struct tegra_plane_state *state) | ||
427 | { | ||
428 | struct tegra_plane_state *tegra_state; | ||
429 | struct drm_plane_state *new; | ||
430 | struct drm_plane *plane; | ||
431 | int err; | ||
376 | 432 | ||
377 | /* | 433 | /* |
378 | * The region where three windows overlap is the intersection of the | 434 | * If planes zpos / transparency changed, sibling planes blending |
379 | * two regions where two windows overlap. It contributes to the area | 435 | * state may require adjustment and in this case they will be included |
380 | * if any of the windows on top of it have an alpha component. | 436 | * into this atom commit, otherwise blending state is unchanged. |
381 | */ | 437 | */ |
382 | for (i = 0; i < 2; i++) | 438 | err = tegra_plane_check_transparency(tegra, state); |
383 | state->dependent[2] = state->dependent[2] || | 439 | if (err <= 0) |
384 | state->dependent[i]; | 440 | return err; |
385 | 441 | ||
386 | /* | 442 | /* |
387 | * However, if any of the windows on top of this window is opaque, it | 443 | * All planes are now in the atomic state, walk them up and update |
388 | * will completely conceal this window within that area, so avoid the | 444 | * transparency state for each plane. |
389 | * window from contributing to the area. | ||
390 | */ | 445 | */ |
391 | for (i = 0; i < 2; i++) { | 446 | drm_for_each_plane(plane, tegra->base.dev) { |
392 | if (zpos[i] > tegra->index) | 447 | struct tegra_plane *p = to_tegra_plane(plane); |
393 | state->dependent[2] = state->dependent[2] && | 448 | |
394 | state->dependent[i]; | 449 | /* skip planes on different CRTCs */ |
450 | if (p->dc != tegra->dc) | ||
451 | continue; | ||
452 | |||
453 | new = drm_atomic_get_new_plane_state(state->base.state, plane); | ||
454 | tegra_state = to_tegra_plane_state(new); | ||
455 | |||
456 | /* | ||
457 | * There is no need to update blending state for the disabled | ||
458 | * plane. | ||
459 | */ | ||
460 | if (new->fb) | ||
461 | tegra_plane_update_transparency(p, tegra_state); | ||
395 | } | 462 | } |
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, | ||
468 | struct tegra_plane_state *state) | ||
469 | { | ||
470 | int err; | ||
471 | |||
472 | err = tegra_plane_setup_opacity(tegra, state); | ||
473 | if (err < 0) | ||
474 | return err; | ||
475 | |||
476 | err = tegra_plane_setup_transparency(tegra, state); | ||
477 | if (err < 0) | ||
478 | return err; | ||
479 | |||
480 | return 0; | ||
396 | } | 481 | } |