video: tegra: work around overlay corruption on underflows
authorErik Gilling <konkers@android.com>
Wed, 12 Jan 2011 00:32:20 +0000 (16:32 -0800)
committerErik Gilling <konkers@android.com>
Wed, 12 Jan 2011 00:35:08 +0000 (16:35 -0800)
Overlays can get their internal state corrupted during and underflow
condition.  The only way to fix this state is to reset the DC.  If we get
4 consecutive frames with underflows, assume we're hosed and reset.

Change-Id: Icdf61517837c8570b8de35f585075de08aa35fe7
Signed-off-by: Erik Gilling <konkers@android.com>
Cc: Michael I. Gold <gold@nvidia.com>
arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h

index 254b732097ef6a5060324f4162d3a809f016423c..43a9ec1e141d8b6d47add2934ad8e32b1159308c 100644 (file)
@@ -101,6 +101,7 @@ struct tegra_dc_win {
        unsigned                z;
 
        int                     dirty;
+       int                     underflows;
        struct tegra_dc         *dc;
 
        struct nvmap_handle_ref *cur_handle;
index 16789c21c6494e7524cac0080fed0682eafb5f9d..29be689fcaff65d3d235dc6217cfce6ecb8a6dfd 100644 (file)
@@ -816,6 +816,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        struct tegra_dc *dc = ptr;
        unsigned long status;
        unsigned long val;
+       unsigned long underflow_mask;
        int i;
 
        status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
@@ -845,6 +846,45 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
                        wake_up(&dc->wq);
        }
 
+
+       /*
+        * Overlays can get thier internal state corrupted during and underflow
+        * condition.  The only way to fix this state is to reset the DC.
+        * if we get 4 consecutive frames with underflows, assume we're
+        * hosed and reset.
+        */
+       underflow_mask = status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT);
+       if (underflow_mask) {
+               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+               val |= V_BLANK_INT;
+               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+               dc->underflow_mask |= underflow_mask;
+       }
+
+       if (status & V_BLANK_INT) {
+               int i;
+
+               for (i = 0; i< DC_N_WINDOWS; i++) {
+                       if (dc->underflow_mask & (WIN_A_UF_INT <<i)) {
+                               dc->windows[i].underflows++;
+
+                               if (dc->windows[i].underflows > 4)
+                                       schedule_work(&dc->reset_work);
+                       } else {
+                               dc->windows[i].underflows = 0;
+                       }
+               }
+
+               if (!dc->underflow_mask) {
+                       val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+                       val &= ~V_BLANK_INT;
+                       tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+               }
+
+               dc->underflow_mask = 0;
+       }
+
+
        return IRQ_HANDLED;
 }
 
@@ -935,8 +975,14 @@ static void tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY);
        tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER);
 
-       tegra_dc_writel(dc, 0x00000002, DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, 0x00000000, DC_CMD_INT_ENABLE);
+       tegra_dc_writel(dc, (FRAME_END_INT |
+                            V_BLANK_INT |
+                            WIN_A_UF_INT |
+                            WIN_B_UF_INT |
+                            WIN_C_UF_INT), DC_CMD_INT_MASK);
+       tegra_dc_writel(dc, (WIN_A_UF_INT |
+                            WIN_B_UF_INT |
+                            WIN_C_UF_INT), DC_CMD_INT_ENABLE);
 
        tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
 
@@ -1031,6 +1077,25 @@ void tegra_dc_disable(struct tegra_dc *dc)
        mutex_unlock(&dc->lock);
 }
 
+static void tegra_dc_reset_worker(struct work_struct *work)
+{
+       struct tegra_dc *dc =
+               container_of(work, struct tegra_dc, reset_work);
+
+       dev_warn(&dc->ndev->dev, "overlay stuck in underflow state.  resetting.\n");
+
+       mutex_lock(&dc->lock);
+       _tegra_dc_disable(dc);
+
+       tegra_periph_reset_assert(dc->clk);
+       msleep(10);
+       tegra_periph_reset_deassert(dc->clk);
+
+       _tegra_dc_enable(dc);
+       mutex_unlock(&dc->lock);
+}
+
+
 static int tegra_dc_probe(struct nvhost_device *ndev)
 {
        struct tegra_dc *dc;
@@ -1120,6 +1185,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 
        mutex_init(&dc->lock);
        init_waitqueue_head(&dc->wq);
+       INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
 
        dc->n_windows = DC_N_WINDOWS;
        for (i = 0; i < dc->n_windows; i++) {
index 253d03f057d7a8c2f61954264e6a3707506a6ce6..3f7fdbff023b0fd22e69540bf3309ac826fffe15 100644 (file)
@@ -84,6 +84,9 @@ struct tegra_dc {
        u32                             syncpt_id;
        u32                             syncpt_min;
        u32                             syncpt_max;
+
+       unsigned long                   underflow_mask;
+       struct work_struct              reset_work;
 };
 
 static inline void tegra_dc_io_start(struct tegra_dc *dc)