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>
This commit is contained in:
Dmitry Osipenko
2018-05-04 17:39:59 +03:00
committed by Thierry Reding
parent acc6a3a9af
commit 3dae08bc07
3 changed files with 254 additions and 106 deletions

View File

@@ -163,28 +163,89 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
BLEND_COLOR_KEY_NONE;
u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
struct tegra_plane_state *state;
u32 blending[2];
unsigned int i;
state = to_tegra_plane_state(plane->base.state);
/* alpha contribution is 1 minus sum of overlapping windows */
for (i = 0; i < 3; i++) {
if (state->dependent[i])
background[i] |= BLEND_CONTROL_DEPENDENT;
}
/* enable alpha blending if pixel format has an alpha component */
if (!state->opaque)
foreground |= BLEND_CONTROL_ALPHA;
/*
* Disable blending and assume Window A is the bottom-most window,
* Window C is the top-most window and Window B is in the middle.
*/
/* disable blending for non-overlapping case */
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
switch (plane->index) {
state = to_tegra_plane_state(plane->base.state);
if (state->opaque) {
/*
* Since custom fix-weight blending isn't utilized and weight
* of top window is set to max, we can enforce dependent
* blending which in this case results in transparent bottom
* window if top window is opaque and if top window enables
* alpha blending, then bottom window is getting alpha value
* of 1 minus the sum of alpha components of the overlapping
* plane.
*/
background[0] |= BLEND_CONTROL_DEPENDENT;
background[1] |= BLEND_CONTROL_DEPENDENT;
/*
* The region where three windows overlap is the intersection
* of the two regions where two windows overlap. It contributes
* to the area if all of the windows on top of it have an alpha
* component.
*/
switch (state->base.normalized_zpos) {
case 0:
if (state->blending[0].alpha &&
state->blending[1].alpha)
background[2] |= BLEND_CONTROL_DEPENDENT;
break;
case 1:
background[2] |= BLEND_CONTROL_DEPENDENT;
break;
}
} else {
/*
* Enable alpha blending if pixel format has an alpha
* component.
*/
foreground |= BLEND_CONTROL_ALPHA;
/*
* If any of the windows on top of this window is opaque, it
* will completely conceal this window within that area. If
* top window has an alpha component, it is blended over the
* bottom window.
*/
for (i = 0; i < 2; i++) {
if (state->blending[i].alpha &&
state->blending[i].top)
background[i] |= BLEND_CONTROL_DEPENDENT;
}
switch (state->base.normalized_zpos) {
case 0:
if (state->blending[0].alpha &&
state->blending[1].alpha)
background[2] |= BLEND_CONTROL_DEPENDENT;
break;
case 1:
/*
* When both middle and topmost windows have an alpha,
* these windows a mixed together and then the result
* is blended over the bottom window.
*/
if (state->blending[0].alpha &&
state->blending[0].top)
background[2] |= BLEND_CONTROL_ALPHA;
if (state->blending[1].alpha &&
state->blending[1].top)
background[2] |= BLEND_CONTROL_ALPHA;
break;
}
}
switch (state->base.normalized_zpos) {
case 0:
tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
@@ -192,8 +253,21 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
break;
case 1:
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
/*
* If window B / C is topmost, then X / Y registers are
* matching the order of blending[...] state indices,
* otherwise a swap is required.
*/
if (!state->blending[0].top && state->blending[1].top) {
blending[0] = foreground;
blending[1] = background[1];
} else {
blending[0] = background[0];
blending[1] = foreground;
}
tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
break;
@@ -525,14 +599,14 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
struct tegra_bo_tiling *tiling = &plane_state->tiling;
struct tegra_plane *tegra = to_tegra_plane(plane);
struct tegra_dc *dc = to_tegra_dc(state->crtc);
unsigned int format;
int err;
/* no need for further checks if the plane is being disabled */
if (!state->crtc)
return 0;
err = tegra_plane_format(state->fb->format->format, &format,
err = tegra_plane_format(state->fb->format->format,
&plane_state->format,
&plane_state->swap);
if (err < 0)
return err;
@@ -544,21 +618,11 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
* be emulated by disabling alpha blending for the plane.
*/
if (!dc->soc->supports_blending) {
if (!tegra_plane_format_has_alpha(format)) {
err = tegra_plane_format_get_alpha(format, &format);
if (err < 0)
return err;
plane_state->opaque = true;
} else {
plane_state->opaque = false;
}
tegra_plane_check_dependent(tegra, plane_state);
err = tegra_plane_setup_legacy_state(tegra, plane_state);
if (err < 0)
return err;
}
plane_state->format = format;
err = tegra_fb_get_tiling(state->fb, tiling);
if (err < 0)
return err;
@@ -710,9 +774,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
if (dc->soc->supports_blending)
drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
return &plane->base;
}
@@ -989,9 +1051,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
if (dc->soc->supports_blending)
drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
return &plane->base;
}