drm/tegra: dc: Implement legacy blending
This implements alpha blending on legacy display controllers (Tegra20, Tegra30 and Tegra114). While it's theoretically possible to support the zpos property to enable userspace to specify the Z-order of each plane individually, this is not currently supported and the same fixed Z- order as previously defined is used. Reverts commit71835caa00
("drm/tegra: fb: Force alpha formats") since the opaque formats are now supported. Reported-by: Dmitry Osipenko <digetx@gmail.com> Fixes:7772fdaef9
("drm/tegra: Support ARGB and ABGR formats") Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
4c69ac12e3
commit
ebae8d0743
@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in)
|
|||||||
|
|
||||||
static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
|
static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
|
||||||
{
|
{
|
||||||
|
u32 background[3] = {
|
||||||
|
BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
|
||||||
|
BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
|
||||||
|
BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
|
||||||
|
};
|
||||||
|
u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
|
||||||
|
BLEND_COLOR_KEY_NONE;
|
||||||
|
u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
|
||||||
|
struct tegra_plane_state *state;
|
||||||
|
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,
|
* 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.
|
* Window C is the top-most window and Window B is in the middle.
|
||||||
*/
|
*/
|
||||||
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY);
|
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
|
||||||
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN);
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
|
||||||
|
|
||||||
switch (plane->index) {
|
switch (plane->index) {
|
||||||
case 0:
|
case 0:
|
||||||
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X);
|
tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
|
||||||
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
|
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
|
||||||
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
|
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
|
||||||
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
|
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
|
||||||
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
|
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
|
||||||
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y);
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
|
||||||
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY);
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = {
|
|||||||
DRM_FORMAT_RGBA5551,
|
DRM_FORMAT_RGBA5551,
|
||||||
DRM_FORMAT_ABGR8888,
|
DRM_FORMAT_ABGR8888,
|
||||||
DRM_FORMAT_ARGB8888,
|
DRM_FORMAT_ARGB8888,
|
||||||
|
/* non-native formats */
|
||||||
|
DRM_FORMAT_XRGB1555,
|
||||||
|
DRM_FORMAT_RGBX5551,
|
||||||
|
DRM_FORMAT_XBGR8888,
|
||||||
|
DRM_FORMAT_XRGB8888,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 tegra114_primary_formats[] = {
|
static const u32 tegra114_primary_formats[] = {
|
||||||
@ -409,18 +437,40 @@ 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,
|
err = tegra_plane_format(state->fb->format->format, &format,
|
||||||
&plane_state->format,
|
|
||||||
&plane_state->swap);
|
&plane_state->swap);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tegra20 and Tegra30 are special cases here because they support
|
||||||
|
* only variants of specific formats with an alpha component, but not
|
||||||
|
* the corresponding opaque formats. However, the opaque formats can
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = {
|
|||||||
DRM_FORMAT_RGBA5551,
|
DRM_FORMAT_RGBA5551,
|
||||||
DRM_FORMAT_ABGR8888,
|
DRM_FORMAT_ABGR8888,
|
||||||
DRM_FORMAT_ARGB8888,
|
DRM_FORMAT_ARGB8888,
|
||||||
|
/* non-native formats */
|
||||||
|
DRM_FORMAT_XRGB1555,
|
||||||
|
DRM_FORMAT_RGBX5551,
|
||||||
|
DRM_FORMAT_XBGR8888,
|
||||||
|
DRM_FORMAT_XRGB8888,
|
||||||
/* planar formats */
|
/* planar formats */
|
||||||
DRM_FORMAT_UYVY,
|
DRM_FORMAT_UYVY,
|
||||||
DRM_FORMAT_YUYV,
|
DRM_FORMAT_YUYV,
|
||||||
|
@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
|
|||||||
#define DC_WIN_DV_CONTROL 0x70e
|
#define DC_WIN_DV_CONTROL 0x70e
|
||||||
|
|
||||||
#define DC_WIN_BLEND_NOKEY 0x70f
|
#define DC_WIN_BLEND_NOKEY 0x70f
|
||||||
|
#define BLEND_WEIGHT1(x) (((x) & 0xff) << 16)
|
||||||
|
#define BLEND_WEIGHT0(x) (((x) & 0xff) << 8)
|
||||||
|
|
||||||
#define DC_WIN_BLEND_1WIN 0x710
|
#define DC_WIN_BLEND_1WIN 0x710
|
||||||
|
#define BLEND_CONTROL_FIX (0 << 2)
|
||||||
|
#define BLEND_CONTROL_ALPHA (1 << 2)
|
||||||
|
#define BLEND_COLOR_KEY_NONE (0 << 0)
|
||||||
|
#define BLEND_COLOR_KEY_0 (1 << 0)
|
||||||
|
#define BLEND_COLOR_KEY_1 (2 << 0)
|
||||||
|
#define BLEND_COLOR_KEY_BOTH (3 << 0)
|
||||||
|
|
||||||
#define DC_WIN_BLEND_2WIN_X 0x711
|
#define DC_WIN_BLEND_2WIN_X 0x711
|
||||||
|
#define BLEND_CONTROL_DEPENDENT (2 << 2)
|
||||||
|
|
||||||
#define DC_WIN_BLEND_2WIN_Y 0x712
|
#define DC_WIN_BLEND_2WIN_Y 0x712
|
||||||
#define DC_WIN_BLEND_3WIN_XY 0x713
|
#define DC_WIN_BLEND_3WIN_XY 0x713
|
||||||
|
|
||||||
|
@ -254,18 +254,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
|
|||||||
cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
|
cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
|
||||||
tegra->pitch_align);
|
tegra->pitch_align);
|
||||||
|
|
||||||
/*
|
|
||||||
* Early generations of Tegra (Tegra20 and Tegra30) do not support any
|
|
||||||
* of the X* or *X formats, only their A* or *A equivalents. Force the
|
|
||||||
* legacy framebuffer format to include an alpha component so that the
|
|
||||||
* framebuffer emulation can be supported on all generations.
|
|
||||||
*/
|
|
||||||
if (sizes->surface_bpp == 32 && sizes->surface_depth == 24)
|
|
||||||
sizes->surface_depth = 32;
|
|
||||||
|
|
||||||
if (sizes->surface_bpp == 16 && sizes->surface_depth == 15)
|
|
||||||
sizes->surface_depth = 16;
|
|
||||||
|
|
||||||
cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||||||
sizes->surface_depth);
|
sizes->surface_depth);
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
|
|||||||
{
|
{
|
||||||
struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
|
struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
|
||||||
struct tegra_plane_state *copy;
|
struct tegra_plane_state *copy;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
|
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
|
||||||
if (!copy)
|
if (!copy)
|
||||||
@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
|
|||||||
copy->tiling = state->tiling;
|
copy->tiling = state->tiling;
|
||||||
copy->format = state->format;
|
copy->format = state->format;
|
||||||
copy->swap = state->swap;
|
copy->swap = state->swap;
|
||||||
|
copy->opaque = state->opaque;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
copy->dependent[i] = state->dependent[i];
|
||||||
|
|
||||||
return ©->base;
|
return ©->base;
|
||||||
}
|
}
|
||||||
@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool __drm_format_has_alpha(u32 format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case DRM_FORMAT_ARGB1555:
|
||||||
|
case DRM_FORMAT_RGBA5551:
|
||||||
|
case DRM_FORMAT_ABGR8888:
|
||||||
|
case DRM_FORMAT_ARGB8888:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
switch (opaque) {
|
||||||
|
case WIN_COLOR_DEPTH_B5G5R5X1:
|
||||||
|
*alpha = WIN_COLOR_DEPTH_B5G5R5A1;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WIN_COLOR_DEPTH_X1B5G5R5:
|
||||||
|
*alpha = WIN_COLOR_DEPTH_A1B5G5R5;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WIN_COLOR_DEPTH_R8G8B8X8:
|
||||||
|
*alpha = WIN_COLOR_DEPTH_R8G8B8A8;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WIN_COLOR_DEPTH_B8G8R8X8:
|
||||||
|
*alpha = WIN_COLOR_DEPTH_B8G8R8A8;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
|
||||||
|
struct tegra_plane *other)
|
||||||
|
{
|
||||||
|
unsigned int index = 0, i;
|
||||||
|
|
||||||
|
WARN_ON(plane == other);
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (i == plane->index)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (i == other->index)
|
||||||
|
break;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tegra_plane_check_dependent(struct tegra_plane *tegra,
|
||||||
|
struct tegra_plane_state *state)
|
||||||
|
{
|
||||||
|
struct drm_plane_state *old, *new;
|
||||||
|
struct drm_plane *plane;
|
||||||
|
unsigned int zpos[2];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
state->dependent[i] = false;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; 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);
|
||||||
|
unsigned index;
|
||||||
|
|
||||||
|
/* skip this plane and planes on different CRTCs */
|
||||||
|
if (p == tegra || new->crtc != state->base.crtc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
index = tegra_plane_get_overlap_index(tegra, p);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -40,6 +40,10 @@ struct tegra_plane_state {
|
|||||||
struct tegra_bo_tiling tiling;
|
struct tegra_bo_tiling tiling;
|
||||||
u32 format;
|
u32 format;
|
||||||
u32 swap;
|
u32 swap;
|
||||||
|
|
||||||
|
/* used for legacy blending support only */
|
||||||
|
bool opaque;
|
||||||
|
bool dependent[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct tegra_plane_state *
|
static inline struct tegra_plane_state *
|
||||||
@ -58,5 +62,9 @@ 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_format_get_alpha(unsigned int opaque, unsigned int *alpha);
|
||||||
|
void tegra_plane_check_dependent(struct tegra_plane *tegra,
|
||||||
|
struct tegra_plane_state *state);
|
||||||
|
|
||||||
#endif /* TEGRA_PLANE_H */
|
#endif /* TEGRA_PLANE_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user