Skip to content

Commit

Permalink
drm/tegra: dc: Implement legacy blending
Browse files Browse the repository at this point in the history
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 commit 71835ca ("drm/tegra: fb: Force alpha formats") since
the opaque formats are now supported.

Reported-by: Dmitry Osipenko <digetx@gmail.com>
Fixes: 7772fda ("drm/tegra: Support ARGB and ABGR formats")
Signed-off-by: Thierry Reding <treding@nvidia.com>
  • Loading branch information
Thierry Reding committed Dec 21, 2017
1 parent 4c69ac1 commit ebae8d0
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 25 deletions.
81 changes: 68 additions & 13 deletions drivers/gpu/drm/tegra/dc.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,30 +154,53 @@ static inline u32 compute_initial_dda(unsigned int in)

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,
* 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, 0xffff00, DC_WIN_BLEND_1WIN);
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);

switch (plane->index) {
case 0:
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
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[2], DC_WIN_BLEND_3WIN_XY);
break;

case 1:
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
break;

case 2:
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y);
tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
break;
}
}
Expand Down Expand Up @@ -353,6 +376,11 @@ static const u32 tegra20_primary_formats[] = {
DRM_FORMAT_RGBA5551,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_ARGB8888,
/* non-native formats */
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_XRGB8888,
};

static const u32 tegra114_primary_formats[] = {
Expand Down Expand Up @@ -409,18 +437,40 @@ 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,
&plane_state->format,
err = tegra_plane_format(state->fb->format->format, &format,
&plane_state->swap);
if (err < 0)
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);
if (err < 0)
return err;
Expand Down Expand Up @@ -737,6 +787,11 @@ static const u32 tegra20_overlay_formats[] = {
DRM_FORMAT_RGBA5551,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_ARGB8888,
/* non-native formats */
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_XRGB8888,
/* planar formats */
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
Expand Down
12 changes: 12 additions & 0 deletions drivers/gpu/drm/tegra/dc.h
Original file line number Diff line number Diff line change
Expand Up @@ -649,8 +649,20 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_WIN_DV_CONTROL 0x70e

#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 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 BLEND_CONTROL_DEPENDENT (2 << 2)

#define DC_WIN_BLEND_2WIN_Y 0x712
#define DC_WIN_BLEND_3WIN_XY 0x713

Expand Down
12 changes: 0 additions & 12 deletions drivers/gpu/drm/tegra/fb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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,
sizes->surface_depth);

Expand Down
138 changes: 138 additions & 0 deletions drivers/gpu/drm/tegra/plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *copy;
unsigned int i;

copy = kmalloc(sizeof(*copy), GFP_KERNEL);
if (!copy)
Expand All @@ -52,6 +53,10 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
copy->tiling = state->tiling;
copy->format = state->format;
copy->swap = state->swap;
copy->opaque = state->opaque;

for (i = 0; i < 3; i++)
copy->dependent[i] = state->dependent[i];

return &copy->base;
}
Expand Down Expand Up @@ -238,3 +243,136 @@ bool tegra_plane_format_is_yuv(unsigned int format, bool *planar)

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];
}
}
8 changes: 8 additions & 0 deletions drivers/gpu/drm/tegra/plane.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ struct tegra_plane_state {
struct tegra_bo_tiling tiling;
u32 format;
u32 swap;

/* used for legacy blending support only */
bool opaque;
bool dependent[3];
};

static inline struct tegra_plane_state *
Expand All @@ -58,5 +62,9 @@ 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);

#endif /* TEGRA_PLANE_H */

0 comments on commit ebae8d0

Please sign in to comment.