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:
parent
acc6a3a9af
commit
3dae08bc07
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
|
|||
return container_of(plane, struct tegra_plane, base);
|
||||
}
|
||||
|
||||
struct tegra_plane_legacy_blending_state {
|
||||
bool alpha;
|
||||
bool top;
|
||||
};
|
||||
|
||||
struct tegra_plane_state {
|
||||
struct drm_plane_state base;
|
||||
|
||||
|
@ -42,8 +47,8 @@ struct tegra_plane_state {
|
|||
u32 swap;
|
||||
|
||||
/* used for legacy blending support only */
|
||||
struct tegra_plane_legacy_blending_state blending[2];
|
||||
bool opaque;
|
||||
bool dependent[3];
|
||||
};
|
||||
|
||||
static inline struct tegra_plane_state *
|
||||
|
@ -62,9 +67,7 @@ int tegra_plane_state_add(struct tegra_plane *plane,
|
|||
|
||||
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_has_alpha(unsigned int format);
|
||||
int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha);
|
||||
void tegra_plane_check_dependent(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state);
|
||||
int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state);
|
||||
|
||||
#endif /* TEGRA_PLANE_H */
|
||||
|
|
Loading…
Reference in New Issue