tegra dc: fix premult dst alpha blending
Francis Hart [Wed, 23 Mar 2011 10:30:15 +0000 (12:30 +0200)]
Register assignments were wrong for incoming overlay blend
parameters. DisplayManagerLite policy sets src combine mode
to premult to signal dst premult aplha blending. But the DC
expects premult set for the dst window for this mode.

Bug 796009

Original-Change-Id: I5929bc4cd362ac60c7a9f14bb4ae83da04b857ab
Reviewed-on: http://git-master/r/26835
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>

Rebase-Id: R750658116e021f7023e2cfff0eb0e35fc3da3695

drivers/video/tegra/dc/overlay.c
include/linux/tegra_overlay.h

index b76d87c..c915c2d 100644 (file)
@@ -59,6 +59,8 @@ struct tegra_overlay_info {
 
        struct tegra_dc         *dc;
 
+       struct tegra_dc_blend   blend;
+
        struct workqueue_struct *flip_wq;
 
        /* Big enough for tegra_dc%u when %u < 10 */
@@ -83,6 +85,7 @@ struct tegra_overlay_flip_data {
        struct tegra_overlay_info       *overlay;
        struct tegra_overlay_flip_win   win[TEGRA_FB_FLIP_N_WINDOWS];
        u32                             syncpt_max;
+       u32                             flags;
 };
 
 /* Overlay window manipulation */
@@ -181,10 +184,54 @@ static int tegra_overlay_set_windowattr(struct tegra_overlay_info *overlay,
                                           msecs_to_jiffies(500));
        }
 
+       /* Store the blend state incase we need to reorder later */
+       overlay->blend.z[win->idx] = win->z;
+       overlay->blend.flags[win->idx] = win->flags & TEGRA_WIN_BLEND_FLAGS_MASK;
 
        return 0;
 }
 
+/* overlay policy for premult is dst alpha, which needs reassignment */
+/* of blend settings for the DC */
+static void tegra_overlay_blend_reorder(struct tegra_dc_blend *blend,
+                                       struct tegra_dc_win *windows[])
+{
+       int idx, below;
+
+       /* Copy across the original blend state to each window */
+       for (idx = 0; idx < DC_N_WINDOWS; idx++) {
+               windows[idx]->z = blend->z[idx];
+               windows[idx]->flags &= ~TEGRA_WIN_BLEND_FLAGS_MASK;
+               windows[idx]->flags |= blend->flags[idx];
+       }
+
+       /* Find a window with PreMult */
+       for (idx = 0; idx < DC_N_WINDOWS; idx++) {
+               if (blend->flags[idx] == TEGRA_WIN_FLAG_BLEND_PREMULT)
+                       break;
+       }
+       if (idx == DC_N_WINDOWS)
+               return;
+
+       /* Find the window directly below it */
+       for (below = 0; below < DC_N_WINDOWS; below++) {
+               if (below == idx)
+                       continue;
+               if (blend->z[below] > blend->z[idx])
+                       break;
+       }
+       if (below == DC_N_WINDOWS)
+               return;
+
+       /* Switch the flags and the ordering */
+       windows[idx]->z = blend->z[below];
+       windows[idx]->flags &= ~TEGRA_WIN_BLEND_FLAGS_MASK;
+       windows[idx]->flags |= blend->flags[below];
+       windows[below]->z = blend->z[idx];
+       windows[below]->flags &= ~TEGRA_WIN_BLEND_FLAGS_MASK;
+       windows[below]->flags |= blend->flags[idx];
+}
+
 static void tegra_overlay_flip_worker(struct work_struct *work)
 {
        struct tegra_overlay_flip_data *data =
@@ -230,9 +277,20 @@ static void tegra_overlay_flip_worker(struct work_struct *work)
 #endif
        }
 
-       tegra_dc_update_windows(wins, nr_win);
-       /* TODO: implement swapinterval here */
-       tegra_dc_sync_windows(wins, nr_win);
+       if (data->flags & TEGRA_OVERLAY_FLIP_FLAG_BLEND_REORDER) {
+               struct tegra_dc_win *dcwins[DC_N_WINDOWS];
+
+               for (i = 0; i < DC_N_WINDOWS; i++)
+                       dcwins[i] = tegra_dc_get_window(overlay->dc, i);
+
+               tegra_overlay_blend_reorder(&overlay->blend, dcwins);
+               tegra_dc_update_windows(dcwins, DC_N_WINDOWS);
+               tegra_dc_sync_windows(dcwins, DC_N_WINDOWS);
+       } else {
+               tegra_dc_update_windows(wins, nr_win);
+               /* TODO: implement swapinterval here */
+               tegra_dc_sync_windows(wins, nr_win);
+       }
 
        tegra_dc_incr_syncpt_min(overlay->dc, data->syncpt_max);
 
@@ -266,6 +324,7 @@ static int tegra_overlay_flip(struct tegra_overlay_info *overlay,
 
        INIT_WORK(&data->work, tegra_overlay_flip_worker);
        data->overlay = overlay;
+       data->flags = args->flags;
 
        for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
                flip_win = &data->win[i];
@@ -355,6 +414,7 @@ static void tegra_overlay_put_locked(struct overlay_client *client, int idx)
        flip_args.win[0].buff_id = 0;
        flip_args.win[1].index = -1;
        flip_args.win[2].index = -1;
+       flip_args.flags = 0;
 
        tegra_overlay_flip(dev, &flip_args, NULL);
        if (dev->dc->mode.pclk != 0)
index 673a40f..74c4fac 100644 (file)
@@ -48,10 +48,13 @@ struct tegra_overlay_windowattr {
        __u32   flags;
 };
 
+#define TEGRA_OVERLAY_FLIP_FLAG_BLEND_REORDER (1 << 0)
+
 struct tegra_overlay_flip_args {
        struct tegra_overlay_windowattr win[TEGRA_FB_FLIP_N_WINDOWS];
        __u32 post_syncpt_id;
        __u32 post_syncpt_val;
+       __u32 flags;
 };
 
 #define TEGRA_OVERLAY_IOCTL_MAGIC              'O'