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:

committed by
Thierry Reding

parent
acc6a3a9af
commit
3dae08bc07
@@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane)
|
||||
|
||||
static void tegra_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
struct tegra_plane_state *state;
|
||||
|
||||
if (plane->state)
|
||||
@@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane)
|
||||
if (state) {
|
||||
plane->state = &state->base;
|
||||
plane->state->plane = plane;
|
||||
plane->state->zpos = p->index;
|
||||
plane->state->normalized_zpos = p->index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +58,8 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
|
||||
copy->swap = state->swap;
|
||||
copy->opaque = state->opaque;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
copy->dependent[i] = state->dependent[i];
|
||||
for (i = 0; i < 2; i++)
|
||||
copy->blending[i] = state->blending[i];
|
||||
|
||||
return ©->base;
|
||||
}
|
||||
@@ -267,24 +270,8 @@ static bool __drm_format_has_alpha(u32 format)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is applicable to Tegra20 and Tegra30 only where the opaque formats can
|
||||
* be emulated using the alpha formats and alpha blending disabled.
|
||||
*/
|
||||
bool tegra_plane_format_has_alpha(unsigned int format)
|
||||
{
|
||||
switch (format) {
|
||||
case WIN_COLOR_DEPTH_B5G5R5A1:
|
||||
case WIN_COLOR_DEPTH_A1B5G5R5:
|
||||
case WIN_COLOR_DEPTH_R8G8B8A8:
|
||||
case WIN_COLOR_DEPTH_B8G8R8A8:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
|
||||
static int tegra_plane_format_get_alpha(unsigned int opaque,
|
||||
unsigned int *alpha)
|
||||
{
|
||||
if (tegra_plane_format_is_yuv(opaque, NULL)) {
|
||||
*alpha = opaque;
|
||||
@@ -316,6 +303,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is applicable to Tegra20 and Tegra30 only where the opaque formats can
|
||||
* be emulated using the alpha formats and alpha blending disabled.
|
||||
*/
|
||||
static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
unsigned int format;
|
||||
int err;
|
||||
|
||||
switch (state->format) {
|
||||
case WIN_COLOR_DEPTH_B5G5R5A1:
|
||||
case WIN_COLOR_DEPTH_A1B5G5R5:
|
||||
case WIN_COLOR_DEPTH_R8G8B8A8:
|
||||
case WIN_COLOR_DEPTH_B8G8R8A8:
|
||||
state->opaque = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = tegra_plane_format_get_alpha(state->format, &format);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
state->format = format;
|
||||
state->opaque = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_plane_check_transparency(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *old, *plane_state;
|
||||
struct drm_plane *plane;
|
||||
|
||||
old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
|
||||
|
||||
/* check if zpos / transparency changed */
|
||||
if (old->normalized_zpos == state->base.normalized_zpos &&
|
||||
to_tegra_plane_state(old)->opaque == state->opaque)
|
||||
return 0;
|
||||
|
||||
/* include all sibling planes into this commit */
|
||||
drm_for_each_plane(plane, tegra->base.dev) {
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
|
||||
/* skip this plane and planes on different CRTCs */
|
||||
if (p == tegra || p->dc != tegra->dc)
|
||||
continue;
|
||||
|
||||
plane_state = drm_atomic_get_plane_state(state->base.state,
|
||||
plane);
|
||||
if (IS_ERR(plane_state))
|
||||
return PTR_ERR(plane_state);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
|
||||
struct tegra_plane *other)
|
||||
{
|
||||
@@ -336,61 +384,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
|
||||
return index;
|
||||
}
|
||||
|
||||
void tegra_plane_check_dependent(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
static void tegra_plane_update_transparency(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *old, *new;
|
||||
struct drm_plane_state *new;
|
||||
struct drm_plane *plane;
|
||||
unsigned int zpos[2];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
zpos[i] = 0;
|
||||
|
||||
for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {
|
||||
for_each_new_plane_in_state(state->base.state, plane, new, i) {
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
unsigned index;
|
||||
|
||||
/* skip this plane and planes on different CRTCs */
|
||||
if (p == tegra || new->crtc != state->base.crtc)
|
||||
if (p == tegra || p->dc != tegra->dc)
|
||||
continue;
|
||||
|
||||
index = tegra_plane_get_overlap_index(tegra, p);
|
||||
|
||||
state->dependent[index] = false;
|
||||
if (new->fb && __drm_format_has_alpha(new->fb->format->format))
|
||||
state->blending[index].alpha = true;
|
||||
else
|
||||
state->blending[index].alpha = false;
|
||||
|
||||
if (new->normalized_zpos > state->base.normalized_zpos)
|
||||
state->blending[index].top = true;
|
||||
else
|
||||
state->blending[index].top = false;
|
||||
|
||||
/*
|
||||
* If any of the other planes is on top of this plane and uses
|
||||
* a format with an alpha component, mark this plane as being
|
||||
* dependent, meaning it's alpha value will be 1 minus the sum
|
||||
* of alpha components of the overlapping planes.
|
||||
* Missing framebuffer means that plane is disabled, in this
|
||||
* case mark B / C window as top to be able to differentiate
|
||||
* windows indices order in regards to zPos for the middle
|
||||
* window X / Y registers programming.
|
||||
*/
|
||||
if (p->index > tegra->index) {
|
||||
if (__drm_format_has_alpha(new->fb->format->format))
|
||||
state->dependent[index] = true;
|
||||
|
||||
/* keep track of the Z position */
|
||||
zpos[index] = p->index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The region where three windows overlap is the intersection of the
|
||||
* two regions where two windows overlap. It contributes to the area
|
||||
* if any of the windows on top of it have an alpha component.
|
||||
*/
|
||||
for (i = 0; i < 2; i++)
|
||||
state->dependent[2] = state->dependent[2] ||
|
||||
state->dependent[i];
|
||||
|
||||
/*
|
||||
* However, if any of the windows on top of this window is opaque, it
|
||||
* will completely conceal this window within that area, so avoid the
|
||||
* window from contributing to the area.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (zpos[i] > tegra->index)
|
||||
state->dependent[2] = state->dependent[2] &&
|
||||
state->dependent[i];
|
||||
if (!new->fb)
|
||||
state->blending[index].top = (index == 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
struct tegra_plane_state *tegra_state;
|
||||
struct drm_plane_state *new;
|
||||
struct drm_plane *plane;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If planes zpos / transparency changed, sibling planes blending
|
||||
* state may require adjustment and in this case they will be included
|
||||
* into this atom commit, otherwise blending state is unchanged.
|
||||
*/
|
||||
err = tegra_plane_check_transparency(tegra, state);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* All planes are now in the atomic state, walk them up and update
|
||||
* transparency state for each plane.
|
||||
*/
|
||||
drm_for_each_plane(plane, tegra->base.dev) {
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
|
||||
/* skip planes on different CRTCs */
|
||||
if (p->dc != tegra->dc)
|
||||
continue;
|
||||
|
||||
new = drm_atomic_get_new_plane_state(state->base.state, plane);
|
||||
tegra_state = to_tegra_plane_state(new);
|
||||
|
||||
/*
|
||||
* There is no need to update blending state for the disabled
|
||||
* plane.
|
||||
*/
|
||||
if (new->fb)
|
||||
tegra_plane_update_transparency(p, tegra_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tegra_plane_setup_opacity(tegra, state);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = tegra_plane_setup_transparency(tegra, state);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user