video: tegra: make fliping asynchronous
authorErik Gilling <konkers@android.com>
Fri, 24 Sep 2010 05:52:30 +0000 (22:52 -0700)
committerColin Cross <ccross@android.com>
Thu, 7 Oct 2010 00:03:17 +0000 (17:03 -0700)
Signed-off-by: Erik Gilling <konkers@android.com>
Change-Id: I17d53717e7f16fb4c460902582b3762180676414

arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/fb.c

index ac9c8b4e0615243c17370920a805e192bafe8686..6cffdab676a98b19f72be9b1d0376b5cc6b03b5c 100644 (file)
@@ -158,6 +158,8 @@ void tegra_dc_enable(struct tegra_dc *dc);
 void tegra_dc_disable(struct tegra_dc *dc);
 
 u32 tegra_dc_get_syncpt_id(struct tegra_dc *dc);
+u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc);
+void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val);
 
 /* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
  * with differenct dcs in one call
index fc4f3fed09f4f59eb0aed2adb0b170d341de6d2c..6250dd1d4f2bc26d1c336bdfc87f331b1196728a 100644 (file)
@@ -337,6 +337,7 @@ struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win)
 }
 EXPORT_SYMBOL(tegra_dc_get_window);
 
+
 static int get_topmost_window(u32 *depths, unsigned long *wins)
 {
        int idx, best = -1;
@@ -412,12 +413,6 @@ static void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *bl
        }
 }
 
-u32 tegra_dc_get_syncpt_id(struct tegra_dc *dc)
-{
-       return dc->syncpt_id;
-}
-EXPORT_SYMBOL(tegra_dc_get_syncpt_id);
-
 /* does not support updating windows on multiple dcs in one call */
 int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 {
@@ -528,14 +523,39 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
        }
 
        tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
+       mutex_unlock(&dc->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(tegra_dc_update_windows);
+
+u32 tegra_dc_get_syncpt_id(struct tegra_dc *dc)
+{
+       return dc->syncpt_id;
+}
+EXPORT_SYMBOL(tegra_dc_get_syncpt_id);
 
-       dc->syncpt_max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt, dc->syncpt_id, 1);
+u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc)
+{
+       u32 max;
 
+       mutex_lock(&dc->lock);
+       max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt, dc->syncpt_id, 1);
+       dc->syncpt_max = max;
        mutex_unlock(&dc->lock);
 
-       return dc->syncpt_max;
+       return max;
+}
+
+void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, u32 val)
+{
+       mutex_lock(&dc->lock);
+       while (dc->syncpt_min < val) {
+               dc->syncpt_min++;
+               nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id);
+       }
+       mutex_unlock(&dc->lock);
 }
-EXPORT_SYMBOL(tegra_dc_update_windows);
 
 static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
                                             int n)
@@ -712,11 +732,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
                        tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
                }
 
-               while (dc->syncpt_min < dc->syncpt_max) {
-                       dc->syncpt_min++;
-                       nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt, dc->syncpt_id);
-               }
-
                if (completed)
                        wake_up(&dc->wq);
        }
@@ -794,6 +809,7 @@ static void tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
 
        tegra_dc_set_color_control(dc);
+
        dc->syncpt_id = disp_syncpt;
 
        dc->syncpt_min = dc->syncpt_max =
index cb0b1e8cc12e6987b0802a4c0b5761a2c067ab20..d8179970e48eaf8f3df02506c584a6c2577f719d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/file.h>
 #include <linux/nvhost.h>
 #include <linux/nvmap.h>
+#include <linux/workqueue.h>
 
 #include <asm/atomic.h>
 
@@ -51,6 +52,15 @@ struct tegra_fb_info {
 
        atomic_t                in_use;
        struct file             *nvmap_file;
+
+       struct workqueue_struct *flip_wq;
+};
+
+struct tegra_fb_flip_data {
+       struct work_struct              work;
+       struct tegra_fb_info            *fb;
+       struct tegra_fb_flip_args       args;
+       u32                             syncpt_max;
 };
 
 /* palette array used by the fbcon */
@@ -75,6 +85,8 @@ static int tegra_fb_release(struct fb_info *info, int user)
        if (tegra_fb->nvmap_file)
                fput(tegra_fb->nvmap_file);
 
+       flush_workqueue(tegra_fb->flip_wq);
+
        WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
 
        return 0;
@@ -323,27 +335,24 @@ static void tegra_fb_set_windowhandle(struct tegra_fb_info *tegra_fb,
        win->cur_handle = handle;
 }
 
-static int tegra_fb_flip(struct tegra_fb_info *tegra_fb,
-                        struct tegra_fb_flip_args *args)
+static void tegra_fb_flip_worker(struct work_struct *work)
 {
+       struct tegra_fb_flip_data *data =
+               container_of(work, struct tegra_fb_flip_data, work);
+       struct tegra_fb_info *tegra_fb = data->fb;
        struct tegra_dc_win *win;
        struct tegra_dc_win *wins[TEGRA_FB_FLIP_N_WINDOWS];
        struct tegra_dc_win **w = wins;
        struct tegra_dc *dc = tegra_fb->win->dc;
-       int err = 0;
        int i;
 
-       if (WARN_ON(!tegra_fb->nvmap_file))
-               return -EFAULT;
-
-       if (WARN_ON(!tegra_fb->ndev))
-               return -EFAULT;
 
        for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
-               int idx = args->win[i].index;
+               int idx = data->args.win[i].index;
                win = tegra_dc_get_window(dc, idx);
                if (win) {
-                       tegra_fb_set_windowattr(tegra_fb, win, &args->win[i]);
+                       tegra_fb_set_windowattr(tegra_fb, win,
+                                               &data->args.win[i]);
                        *w++ = win;
                } else if (idx != -1) {
                        dev_warn(&tegra_fb->ndev->dev,
@@ -351,18 +360,53 @@ static int tegra_fb_flip(struct tegra_fb_info *tegra_fb,
                }
        }
 
-       err = tegra_dc_update_windows(wins, w - wins);
-       if (err < 0)
-               return err;
+       tegra_dc_update_windows(wins, w - wins);
 
        for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
-               win = tegra_dc_get_window(dc, args->win[i].index);
+               win = tegra_dc_get_window(dc, data->args.win[i].index);
                if (win)
                        tegra_fb_set_windowhandle(tegra_fb, win,
-                                                 args->win[i].buff_id);
+                                                 data->args.win[i].buff_id);
        }
 
-       args->post_syncpt_val = err;
+       /* TODO: implement swapinterval here */
+       tegra_dc_sync_windows(wins, w - wins);
+
+       tegra_dc_incr_syncpt_min(tegra_fb->win->dc, data->syncpt_max);
+
+       kfree(data);
+}
+
+
+static int tegra_fb_flip(struct tegra_fb_info *tegra_fb,
+                        struct tegra_fb_flip_args *args)
+{
+       struct tegra_fb_flip_data *data;
+       u32 syncpt_max;
+
+       if (WARN_ON(!tegra_fb->nvmap_file))
+               return -EFAULT;
+
+       if (WARN_ON(!tegra_fb->ndev))
+               return -EFAULT;
+
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if (data == NULL) {
+               dev_err(&tegra_fb->ndev->dev,
+                       "can't allocate memory for flip\n");
+               return -ENOMEM;
+       }
+
+       INIT_WORK(&data->work, tegra_fb_flip_worker);
+       data->fb = tegra_fb;
+       memcpy(&data->args, args, sizeof(data->args));
+
+       syncpt_max = tegra_dc_incr_syncpt_max(tegra_fb->win->dc);
+       data->syncpt_max =  syncpt_max;
+
+       queue_work(tegra_fb->flip_wq, &data->work);
+
+       args->post_syncpt_val = syncpt_max;
        args->post_syncpt_id = tegra_dc_get_syncpt_id(tegra_fb->win->dc);
 
        return 0;
@@ -483,6 +527,12 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        tegra_fb->yres = fb_data->yres;
        atomic_set(&tegra_fb->in_use, 0);
 
+       tegra_fb->flip_wq = create_singlethread_workqueue("tegra_flip");
+       if (!tegra_fb->flip_wq) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
        if (fb_mem) {
                fb_size = resource_size(fb_mem);
                fb_phys = fb_mem->start;
@@ -490,7 +540,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                if (!fb_base) {
                        dev_err(&ndev->dev, "fb can't be mapped\n");
                        ret = -EBUSY;
-                       goto err_free;
+                       goto err_delete_wq;
                }
                tegra_fb->valid = true;
        }
@@ -559,6 +609,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 
 err_iounmap_fb:
        iounmap(fb_base);
+err_delete_wq:
+
 err_free:
        framebuffer_release(info);
 err:
@@ -570,6 +622,10 @@ void tegra_fb_unregister(struct tegra_fb_info *fb_info)
        struct fb_info *info = fb_info->info;
 
        unregister_framebuffer(info);
+
+       flush_workqueue(fb_info->flip_wq);
+       destroy_workqueue(fb_info->flip_wq);
+
        iounmap(info->screen_base);
        framebuffer_release(info);
 }