@@ -154,32 +154,12 @@ static inline u32 compute_initial_dda(unsigned int in)
static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
{
- /*
- * 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);
-
- 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);
- 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);
- break;
+ u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
+ BLEND_COLOR_KEY_NONE;
+ u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
- 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);
- break;
- }
+ tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
+ tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
}
static void tegra_plane_setup_blending(struct tegra_plane *plane,
@@ -353,6 +333,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[] = {
@@ -409,18 +394,38 @@ 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;
+ }
+
+ err = tegra_plane_update_blending_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;
@@ -737,6 +742,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,
@@ -924,6 +934,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
struct tegra_dc_state *state = to_dc_state(crtc->state);
struct tegra_dc_state *copy;
+ unsigned int i;
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
if (!copy)
@@ -935,6 +946,9 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
copy->div = state->div;
copy->planes = state->planes;
+ for (i = 0; i < 3; i++)
+ copy->blend[i] = state->blend[i];
+
return ©->base;
}
@@ -1687,8 +1701,30 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
{
struct tegra_dc_state *state = to_dc_state(crtc->state);
struct tegra_dc *dc = to_tegra_dc(crtc);
+ struct tegra_dc_blend_state *bs;
+ struct tegra_plane *plane;
+ struct drm_plane *p;
u32 value;
+ if (!dc->soc->supports_blending) {
+ drm_for_each_plane(p, crtc->dev) {
+ if (!(p->possible_crtcs & (1 << dc->pipe)))
+ continue;
+
+ plane = to_tegra_plane(p);
+ bs = &state->blend[plane->index];
+
+ tegra_plane_writel(plane, bs->to_win_x,
+ DC_WIN_BLEND_2WIN_X);
+
+ tegra_plane_writel(plane,bs->to_win_y,
+ DC_WIN_BLEND_2WIN_Y);
+
+ tegra_plane_writel(plane, bs->to_win_xy,
+ DC_WIN_BLEND_3WIN_XY);
+ }
+ }
+
value = state->planes << 8 | GENERAL_UPDATE;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
@@ -18,6 +18,13 @@
struct tegra_output;
+struct tegra_dc_blend_state {
+ u32 to_win_x;
+ u32 to_win_y;
+ u32 to_win_xy;
+ bool opaque;
+};
+
struct tegra_dc_state {
struct drm_crtc_state base;
@@ -26,6 +33,8 @@ struct tegra_dc_state {
unsigned int div;
u32 planes;
+
+ struct tegra_dc_blend_state blend[3];
};
static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -649,8 +658,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
@@ -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);
@@ -238,3 +238,148 @@ 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_ARGB4444:
+ 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_B4G4R4A4:
+ case WIN_COLOR_DEPTH_A1B5G5R5:
+ case WIN_COLOR_DEPTH_B5G5R5A1:
+ 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;
+}
+
+int tegra_plane_update_blending_state(struct tegra_plane *tegra,
+ struct tegra_plane_state *state)
+{
+ u32 blend_transparent = BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0);
+ u32 blend_opaque = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
+ struct tegra_dc_blend_state *blend_state;
+ struct tegra_dc_blend_state *win_a_state;
+ struct tegra_dc_blend_state *win_b_state;
+ struct tegra_dc_blend_state *win_c_state;
+ struct tegra_dc_state *dc_state;
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_crtc_state(state->base.state,
+ state->base.crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ dc_state = to_dc_state(crtc_state);
+ blend_state = &dc_state->blend[tegra->index];
+ blend_state->opaque = !__drm_format_has_alpha(
+ state->base.fb->format->format);
+
+ win_a_state = &dc_state->blend[0];
+ win_b_state = &dc_state->blend[1];
+ win_c_state = &dc_state->blend[2];
+
+ /* setup blending state for window A */
+
+ if (win_b_state->opaque) {
+ win_a_state->to_win_x = blend_transparent;
+ } else {
+ if (win_a_state->opaque)
+ win_a_state->to_win_x = BLEND_CONTROL_DEPENDENT;
+ else
+ win_a_state->to_win_x = BLEND_CONTROL_ALPHA;
+ }
+
+ if (win_c_state->opaque) {
+ win_a_state->to_win_y = blend_transparent;
+ } else {
+ if (win_a_state->opaque)
+ win_a_state->to_win_y = BLEND_CONTROL_DEPENDENT;
+ else
+ win_a_state->to_win_y = BLEND_CONTROL_ALPHA;
+ }
+
+ if (win_b_state->opaque || win_c_state->opaque) {
+ win_a_state->to_win_xy = blend_transparent;
+ } else {
+ if (win_a_state->opaque)
+ win_a_state->to_win_xy = BLEND_CONTROL_DEPENDENT;
+ else
+ win_a_state->to_win_xy = BLEND_CONTROL_ALPHA;
+ }
+
+ /* setup blending state for window B */
+
+ if (win_b_state->opaque)
+ win_b_state->to_win_x = blend_opaque;
+ else
+ win_b_state->to_win_x = BLEND_CONTROL_ALPHA;
+
+ if (win_c_state->opaque) {
+ win_b_state->to_win_y = blend_transparent;
+ win_b_state->to_win_xy = blend_transparent;
+ } else {
+ if (win_b_state->opaque) {
+ win_b_state->to_win_y = BLEND_CONTROL_DEPENDENT;
+ win_b_state->to_win_xy = BLEND_CONTROL_DEPENDENT;
+ } else {
+ win_b_state->to_win_y = BLEND_CONTROL_ALPHA;
+ win_b_state->to_win_xy = BLEND_CONTROL_ALPHA;
+ }
+ }
+
+ /* setup blending state for window C */
+
+ if (win_c_state->opaque) {
+ win_c_state->to_win_x = blend_opaque;
+ win_c_state->to_win_y = blend_opaque;
+ win_c_state->to_win_xy = blend_opaque;
+ } else {
+ win_c_state->to_win_x = BLEND_CONTROL_ALPHA;
+ win_c_state->to_win_y = BLEND_CONTROL_ALPHA;
+ win_c_state->to_win_xy = BLEND_CONTROL_ALPHA;
+ }
+
+ return 0;
+}
@@ -58,5 +58,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);
+int tegra_plane_update_blending_state(struct tegra_plane *tegra,
+ struct tegra_plane_state *state);
#endif /* TEGRA_PLANE_H */