From abaae23e8c47a4fcd5c70f8766ce4b6663c68106 Mon Sep 17 00:00:00 2001 From: Jon Mayo Date: Tue, 10 Jan 2012 15:57:36 -0800 Subject: video: tegra: dc: consolidate underflow code Move underflow handling out of the irq handler and into a workqueue. Change-Id: I289d0a4c4e632a229e46d8e7f82e637409813807 Signed-off-by: Jon Mayo Reviewed-on: http://git-master/r/74427 Reviewed-by: Rohan Somvanshi Tested-by: Rohan Somvanshi Reviewed-on: http://git-master/r/75143 Reviewed-by: Automatic_Commit_Validation_User Tested-by: Varun Wadekar Reviewed-by: Varun Wadekar --- drivers/video/tegra/dc/dc.c | 94 ++++++++++++++++++++++------------------ drivers/video/tegra/dc/dc_priv.h | 1 + 2 files changed, 52 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); diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index ca25265bfdb8..f52743d7e351 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -138,6 +138,7 @@ struct tegra_dc { struct dentry *debugdir; #endif struct tegra_dc_lut fb_lut; + struct delayed_work underflow_work; }; static inline void tegra_dc_io_start(struct tegra_dc *dc) -- cgit v1.2.3