summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/dc/dc.c
diff options
context:
space:
mode:
authorErik Gilling <konkers@android.com>2011-01-11 16:32:20 -0800
committerErik Gilling <konkers@android.com>2011-01-11 16:35:08 -0800
commit4ce07d6140b09aa49bfa823edbdd182ee74fa04e (patch)
tree7c31fec41714b253891fe30805552e6c573e8462 /drivers/video/tegra/dc/dc.c
parent8df53e45999fa918c6955d9d103d3ac26c6c5ca4 (diff)
video: tegra: work around overlay corruption on underflows
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>
Diffstat (limited to 'drivers/video/tegra/dc/dc.c')
-rw-r--r--drivers/video/tegra/dc/dc.c70
1 files changed, 68 insertions, 2 deletions
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 16789c21c649..29be689fcaff 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -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++) {