Commit 3dae08bc authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding

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: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent acc6a3a9
...@@ -163,28 +163,89 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane) ...@@ -163,28 +163,89 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
BLEND_COLOR_KEY_NONE; BLEND_COLOR_KEY_NONE;
u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255); u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
struct tegra_plane_state *state; struct tegra_plane_state *state;
u32 blending[2];
unsigned int i; unsigned int i;
/* disable blending for non-overlapping case */
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
state = to_tegra_plane_state(plane->base.state); state = to_tegra_plane_state(plane->base.state);
/* alpha contribution is 1 minus sum of overlapping windows */ if (state->opaque) {
for (i = 0; i < 3; i++) { /*
if (state->dependent[i]) * Since custom fix-weight blending isn't utilized and weight
background[i] |= BLEND_CONTROL_DEPENDENT; * 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;
/* enable alpha blending if pixel format has an alpha component */ /*
if (!state->opaque) * 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; foreground |= BLEND_CONTROL_ALPHA;
/* /*
* Disable blending and assume Window A is the bottom-most window, * If any of the windows on top of this window is opaque, it
* Window C is the top-most window and Window B is in the middle. * will completely conceal this window within that area. If
*/ * top window has an alpha component, it is blended over the
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY); * bottom window.
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN); */
for (i = 0; i < 2; i++) {
if (state->blending[i].alpha &&
state->blending[i].top)
background[i] |= BLEND_CONTROL_DEPENDENT;
}
switch (plane->index) { 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: case 0:
tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X); tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y); 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) ...@@ -192,8 +253,21 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
break; break;
case 1: 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); tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
break; break;
...@@ -525,14 +599,14 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, ...@@ -525,14 +599,14 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
struct tegra_bo_tiling *tiling = &plane_state->tiling; struct tegra_bo_tiling *tiling = &plane_state->tiling;
struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_plane *tegra = to_tegra_plane(plane);
struct tegra_dc *dc = to_tegra_dc(state->crtc); struct tegra_dc *dc = to_tegra_dc(state->crtc);
unsigned int format;
int err; int err;
/* no need for further checks if the plane is being disabled */ /* no need for further checks if the plane is being disabled */
if (!state->crtc) if (!state->crtc)
return 0; 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); &plane_state->swap);
if (err < 0) if (err < 0)
return err; return err;
...@@ -544,21 +618,11 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, ...@@ -544,21 +618,11 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
* be emulated by disabling alpha blending for the plane. * be emulated by disabling alpha blending for the plane.
*/ */
if (!dc->soc->supports_blending) { if (!dc->soc->supports_blending) {
if (!tegra_plane_format_has_alpha(format)) { err = tegra_plane_setup_legacy_state(tegra, plane_state);
err = tegra_plane_format_get_alpha(format, &format); if (err < 0)
if (err < 0) return err;
return err;
plane_state->opaque = true;
} else {
plane_state->opaque = false;
}
tegra_plane_check_dependent(tegra, plane_state);
} }
plane_state->format = format;
err = tegra_fb_get_tiling(state->fb, tiling); err = tegra_fb_get_tiling(state->fb, tiling);
if (err < 0) if (err < 0)
return err; return err;
...@@ -710,9 +774,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, ...@@ -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); drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
if (dc->soc->supports_blending)
drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
return &plane->base; return &plane->base;
} }
...@@ -989,9 +1051,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, ...@@ -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); drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
if (dc->soc->supports_blending)
drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
return &plane->base; return &plane->base;
} }
......
...@@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane) ...@@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane)
static void tegra_plane_reset(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; struct tegra_plane_state *state;
if (plane->state) if (plane->state)
...@@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane) ...@@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane)
if (state) { if (state) {
plane->state = &state->base; plane->state = &state->base;
plane->state->plane = plane; 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) ...@@ -55,8 +58,8 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
copy->swap = state->swap; copy->swap = state->swap;
copy->opaque = state->opaque; copy->opaque = state->opaque;
for (i = 0; i < 3; i++) for (i = 0; i < 2; i++)
copy->dependent[i] = state->dependent[i]; copy->blending[i] = state->blending[i];
return &copy->base; return &copy->base;
} }
...@@ -267,24 +270,8 @@ static bool __drm_format_has_alpha(u32 format) ...@@ -267,24 +270,8 @@ static bool __drm_format_has_alpha(u32 format)
return false; return false;
} }
/* static int tegra_plane_format_get_alpha(unsigned int opaque,
* This is applicable to Tegra20 and Tegra30 only where the opaque formats can unsigned int *alpha)
* 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)
{ {
if (tegra_plane_format_is_yuv(opaque, NULL)) { if (tegra_plane_format_is_yuv(opaque, NULL)) {
*alpha = opaque; *alpha = opaque;
...@@ -316,6 +303,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) ...@@ -316,6 +303,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
return -EINVAL; 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, static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
struct tegra_plane *other) struct tegra_plane *other)
{ {
...@@ -336,61 +384,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, ...@@ -336,61 +384,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
return index; return index;
} }
void tegra_plane_check_dependent(struct tegra_plane *tegra, static void tegra_plane_update_transparency(struct tegra_plane *tegra,
struct tegra_plane_state *state) struct tegra_plane_state *state)
{ {
struct drm_plane_state *old, *new; struct drm_plane_state *new;
struct drm_plane *plane; struct drm_plane *plane;
unsigned int zpos[2];
unsigned int i; unsigned int i;
for (i = 0; i < 2; i++) for_each_new_plane_in_state(state->base.state, plane, new, i) {
zpos[i] = 0;
for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {
struct tegra_plane *p = to_tegra_plane(plane); struct tegra_plane *p = to_tegra_plane(plane);
unsigned index; unsigned index;
/* skip this plane and planes on different CRTCs */ /* skip this plane and planes on different CRTCs */
if (p == tegra || new->crtc != state->base.crtc) if (p == tegra || p->dc != tegra->dc)
continue; continue;
index = tegra_plane_get_overlap_index(tegra, p); 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 * Missing framebuffer means that plane is disabled, in this
* a format with an alpha component, mark this plane as being * case mark B / C window as top to be able to differentiate
* dependent, meaning it's alpha value will be 1 minus the sum * windows indices order in regards to zPos for the middle
* of alpha components of the overlapping planes. * window X / Y registers programming.
*/ */
if (p->index > tegra->index) { if (!new->fb)
if (__drm_format_has_alpha(new->fb->format->format)) state->blending[index].top = (index == 1);
state->dependent[index] = true;
/* keep track of the Z position */
zpos[index] = p->index;
}
} }
}
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;
/* /*
* The region where three windows overlap is the intersection of the * If planes zpos / transparency changed, sibling planes blending
* two regions where two windows overlap. It contributes to the area * state may require adjustment and in this case they will be included
* if any of the windows on top of it have an alpha component. * into this atom commit, otherwise blending state is unchanged.
*/ */
for (i = 0; i < 2; i++) err = tegra_plane_check_transparency(tegra, state);
state->dependent[2] = state->dependent[2] || if (err <= 0)
state->dependent[i]; return err;
/* /*
* However, if any of the windows on top of this window is opaque, it * All planes are now in the atomic state, walk them up and update
* will completely conceal this window within that area, so avoid the * transparency state for each plane.
* window from contributing to the area.
*/ */
for (i = 0; i < 2; i++) { drm_for_each_plane(plane, tegra->base.dev) {
if (zpos[i] > tegra->index) struct tegra_plane *p = to_tegra_plane(plane);
state->dependent[2] = state->dependent[2] &&
state->dependent[i]; /* 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;
} }
...@@ -34,6 +34,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) ...@@ -34,6 +34,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
return container_of(plane, struct tegra_plane, base); return container_of(plane, struct tegra_plane, base);
} }
struct tegra_plane_legacy_blending_state {
bool alpha;
bool top;
};
struct tegra_plane_state { struct tegra_plane_state {
struct drm_plane_state base; struct drm_plane_state base;
...@@ -42,8 +47,8 @@ struct tegra_plane_state { ...@@ -42,8 +47,8 @@ struct tegra_plane_state {
u32 swap; u32 swap;
/* used for legacy blending support only */ /* used for legacy blending support only */
struct tegra_plane_legacy_blending_state blending[2];
bool opaque; bool opaque;
bool dependent[3];
}; };
static inline struct tegra_plane_state * static inline struct tegra_plane_state *
...@@ -62,9 +67,7 @@ int tegra_plane_state_add(struct tegra_plane *plane, ...@@ -62,9 +67,7 @@ int tegra_plane_state_add(struct tegra_plane *plane,
int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap); int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap);
bool tegra_plane_format_is_yuv(unsigned int format, bool *planar); bool tegra_plane_format_is_yuv(unsigned int format, bool *planar);
bool tegra_plane_format_has_alpha(unsigned int format); int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha); struct tegra_plane_state *state);
void tegra_plane_check_dependent(struct tegra_plane *tegra,
struct tegra_plane_state *state);
#endif /* TEGRA_PLANE_H */ #endif /* TEGRA_PLANE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment