summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/dc/dc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/dc/dc.c')
-rw-r--r--drivers/video/tegra/dc/dc.c94
1 files changed, 51 insertions, 43 deletions
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 98c518ad9b14..3d834ebc1716 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -1974,10 +1974,30 @@ static void tegra_dc_vblank(struct work_struct *work)
}
}
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
+/* return an arbitrarily large number if count overflow occurs.
+ * make it a nice base-10 number to show up in stats output */
+static u64 tegra_dc_underflow_count(struct tegra_dc *dc, unsigned reg)
+{
+ unsigned count = tegra_dc_readl(dc, reg);
+ tegra_dc_writel(dc, 0, reg);
+ return ((count & 0x80000000) == 0) ? count : 10000000000ll;
+}
+
static void tegra_dc_underflow_handler(struct tegra_dc *dc)
{
- u32 val, i;
+ u32 val;
+ int i;
+
+ dc->stats.underflows++;
+ if (dc->underflow_mask & WIN_A_UF_INT)
+ dc->stats.underflows_a += tegra_dc_underflow_count(dc,
+ DC_WINBUF_AD_UFLOW_STATUS);
+ if (dc->underflow_mask & WIN_B_UF_INT)
+ dc->stats.underflows_b += tegra_dc_underflow_count(dc,
+ DC_WINBUF_BD_UFLOW_STATUS);
+ if (dc->underflow_mask & WIN_C_UF_INT)
+ dc->stats.underflows_c += tegra_dc_underflow_count(dc,
+ DC_WINBUF_CD_UFLOW_STATUS);
/* Check for any underflow reset conditions */
for (i = 0; i < DC_N_WINDOWS; i++) {
@@ -1993,21 +2013,14 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
}
}
- if (!dc->underflow_mask) {
- /* If we have no underflow to check, go ahead
- and disable the interrupt */
- val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
- if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
- val &= ~FRAME_END_INT;
- else
- val &= ~V_BLANK_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
- }
-
/* Clear the underflow mask now that we've checked it. */
+ tegra_dc_writel(dc, dc->underflow_mask, DC_CMD_INT_STATUS);
dc->underflow_mask = 0;
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, val | ALL_UF_INT, DC_CMD_INT_MASK);
}
+#ifndef CONFIG_TEGRA_FPGA_PLATFORM
static void tegra_dc_trigger_windows(struct tegra_dc *dc)
{
u32 val, i;
@@ -2063,10 +2076,7 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
schedule_work(&dc->vblank_work);
}
- /* Check underflow at frame end */
if (status & FRAME_END_INT) {
- tegra_dc_underflow_handler(dc);
-
/* Mark the frame_end as complete. */
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
@@ -2076,9 +2086,6 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
{
if (status & V_BLANK_INT) {
- /* Check underflow */
- tegra_dc_underflow_handler(dc);
-
/* Schedule any additional bottom-half vblank actvities. */
schedule_work(&dc->vblank_work);
}
@@ -2093,22 +2100,13 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
}
#endif
-/* return an arbitrarily large number if count overflow occurs.
- * make it a nice base-10 number to show up in stats output */
-static u64 tegra_dc_underflow_count(struct tegra_dc *dc, unsigned reg)
-{
- unsigned count = tegra_dc_readl(dc, reg);
- tegra_dc_writel(dc, 0, reg);
- return ((count & 0x80000000) == 0) ? count : 10000000000ll;
-}
-
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
{
#ifndef CONFIG_TEGRA_FPGA_PLATFORM
struct tegra_dc *dc = ptr;
unsigned long status;
- unsigned long val;
unsigned long underflow_mask;
+ u32 val;
if (!nvhost_module_powered(dc->ndev->host->dev)) {
WARN(1, "IRQ when DC not powered!\n");
@@ -2119,8 +2117,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
return IRQ_HANDLED;
}
+ /* clear all status flags except underflow, save those for the worker */
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
- tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
+ tegra_dc_writel(dc, status & ~ALL_UF_INT, DC_CMD_INT_STATUS);
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, val & ~ALL_UF_INT, DC_CMD_INT_MASK);
/*
* Overlays can get thier internal state corrupted during and underflow
@@ -2130,21 +2131,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
*/
underflow_mask = status & ALL_UF_INT;
+ /* Check underflow */
if (underflow_mask) {
- val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
- val |= V_BLANK_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
dc->underflow_mask |= underflow_mask;
- dc->stats.underflows++;
- if (status & WIN_A_UF_INT)
- dc->stats.underflows_a += tegra_dc_underflow_count(dc,
- DC_WINBUF_AD_UFLOW_STATUS);
- if (status & WIN_B_UF_INT)
- dc->stats.underflows_b += tegra_dc_underflow_count(dc,
- DC_WINBUF_BD_UFLOW_STATUS);
- if (status & WIN_C_UF_INT)
- dc->stats.underflows_c += tegra_dc_underflow_count(dc,
- DC_WINBUF_CD_UFLOW_STATUS);
+ schedule_delayed_work(&dc->underflow_work,
+ msecs_to_jiffies(1));
}
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
@@ -2541,6 +2532,10 @@ void tegra_dc_disable(struct tegra_dc *dc)
tegra_dc_ext_disable(dc->ext);
+ /* it's important that new underflow work isn't scheduled before the
+ * lock is acquired. */
+ cancel_delayed_work_sync(&dc->underflow_work);
+
mutex_lock(&dc->lock);
if (dc->enabled) {
@@ -2603,6 +2598,18 @@ unlock:
}
#endif
+static void tegra_dc_underflow_worker(struct work_struct *work)
+{
+ struct tegra_dc *dc = container_of(
+ to_delayed_work(work), struct tegra_dc, underflow_work);
+
+ mutex_lock(&dc->lock);
+ if (dc->enabled) {
+ tegra_dc_underflow_handler(dc);
+ }
+ mutex_unlock(&dc->lock);
+}
+
#ifdef CONFIG_SWITCH
static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
{
@@ -2710,6 +2717,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
#endif
INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
+ INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker);
tegra_dc_init_lut_defaults(&dc->fb_lut);