video: tegra: dc: power optimize DC and host1x clk
Jon Mayo [Tue, 17 Jul 2012 22:56:44 +0000 (15:56 -0700)]
Use threaded IRQ to support enabling clocks in interrupt handling.
Use io_start and io_end to hold and release host1x clock.
Disable IRQ after it is first requested to balance enable/disable.
Use disable_irq_nosync() anywhere dc->lock is held to avoid deadlock.

Change tegra_dc_update_windows() to always be balanced with
tegra_dc_sync_windows(). Sync points (from host1x) are potentially lost if
clock gated after update, generally this only affects applications that
update at a slow frame rate.

To balance update and sync calls, Colormap/LUT code now performs a
sync_windows on a LUT change, this makes LUT changes slower and take effect
immediately.

Add a nosync version of tegra_dc_dsi_write_data to be used within dsi
module.

Bug 1036025
Bug 1031933
Bug 1030415
Bug 1029041
Bug 1028716
Bug 1025621
Bug 1020592
Bug 1013506
Bug 1002768
Bug 955184
Bug 929609
Bug 899059
Bug 887342

Change-Id: Idc9b4c2922ad3d476d57fdf760acae76f0c837e2
Signed-off-by: Jon Mayo <jmayo@nvidia.com>
Reviewed-on: http://git-master/r/146107
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Kevin Huang (Eng-SW) <kevinh@nvidia.com>

drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dsi.c
drivers/video/tegra/dc/ext/cursor.c
drivers/video/tegra/dc/hdmi.c
drivers/video/tegra/dc/lut.c
drivers/video/tegra/dc/nvsd.c
drivers/video/tegra/dc/rgb.c
drivers/video/tegra/dc/window.c

index 6c893ef..3041d97 100644 (file)
@@ -308,8 +308,8 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        char buff[256];
 
        mutex_lock(&dc->lock);
-       tegra_dc_hold_dc_out(dc);
        tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
 
        DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
        DUMP_REG(DC_CMD_DISPLAY_COMMAND);
@@ -463,8 +463,8 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        DUMP_REG(DC_COM_PM1_DUTY_CYCLE);
        DUMP_REG(DC_DISP_SD_CONTROL);
 
-       tegra_dc_io_end(dc);
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -681,11 +681,13 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
                return 0;
        BUG_ON(win > DC_N_WINDOWS);
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
        tegra_dc_writel(dc, WINDOW_A_SELECT << win,
                DC_CMD_DISPLAY_WINDOW_HEADER);
        stride = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
        return GET_LINE_STRIDE(stride);
 }
@@ -734,6 +736,7 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
        unsigned i;
        unsigned v0 = 128;
        unsigned v1 = 0;
+
        /* linear horizontal and vertical filters */
        for (i = 0; i < 16; i++) {
                tegra_dc_writel(dc, (v1 << 16) | (v0 << 8),
@@ -869,9 +872,11 @@ void tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable)
 #define tegra_dc_update_cmu(dc, cmu)
 #endif
 
-static inline void disable_dc_irq(unsigned int irq)
+/* disable_irq() blocks until handler completes, calling this function while
+ * holding dc->lock can deadlock. */
+static inline void disable_dc_irq(const struct tegra_dc *dc)
 {
-       disable_irq(irq);
+       disable_irq(dc->irq);
 }
 
 u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc, int i)
@@ -885,11 +890,13 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
        u32 max;
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
        max = nvhost_syncpt_incr_max_ext(dc->ndev,
                dc->syncpt[i].id, ((dc->enabled) ? 1 : 0));
        dc->syncpt[i].max = max;
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 
        return max;
@@ -899,12 +906,14 @@ void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val)
 {
        mutex_lock(&dc->lock);
        if (dc->enabled) {
+               tegra_dc_io_start(dc);
                tegra_dc_hold_dc_out(dc);
                while (dc->syncpt[i].min < val) {
                        dc->syncpt[i].min++;
                        nvhost_syncpt_cpu_incr_ext(dc->ndev, dc->syncpt[i].id);
                }
                tegra_dc_release_dc_out(dc);
+               tegra_dc_io_end(dc);
        }
        mutex_unlock(&dc->lock);
 }
@@ -922,6 +931,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
                return;
        }
 
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
 
        ctrl = ((cfg->period << PM_PERIOD_SHIFT) |
@@ -957,6 +967,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
        }
        tegra_dc_writel(dc, cmd_state, DC_CMD_STATE_ACCESS);
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 }
 EXPORT_SYMBOL(tegra_dc_config_pwm);
@@ -1105,8 +1116,8 @@ void tegra_dc_enable_crc(struct tegra_dc *dc)
        u32 val;
 
        mutex_lock(&dc->lock);
-       tegra_dc_hold_dc_out(dc);
        tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
 
        val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA |
                CRC_ENABLE_ENABLE;
@@ -1114,19 +1125,21 @@ void tegra_dc_enable_crc(struct tegra_dc *dc)
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 }
 
 void tegra_dc_disable_crc(struct tegra_dc *dc)
 {
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
        tegra_dc_writel(dc, 0x0, DC_COM_CRC_CONTROL);
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 
-       tegra_dc_io_end(dc);
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -1146,9 +1159,11 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
 #endif
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
        crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED);
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 crc_error:
        return crc;
@@ -1166,13 +1181,13 @@ static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
        return false;
 }
 
-static inline void enable_dc_irq(unsigned int irq)
+static inline void enable_dc_irq(const struct tegra_dc *dc)
 {
 #ifndef CONFIG_TEGRA_FPGA_PLATFORM
-       enable_irq(irq);
+       enable_irq(dc->irq);
 #else
        /* Always disable DC interrupts on FPGA. */
-       disable_irq(irq);
+       disable_irq(dc->irq);
 #endif
 }
 
@@ -1218,6 +1233,7 @@ static void tegra_dc_vblank(struct work_struct *work)
                return;
        }
 
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
        /* use the new frame's bandwidth setting instead of max(current, new),
         * skip this if we're using tegra_dc_one_shot_worker() */
@@ -1246,6 +1262,7 @@ static void tegra_dc_vblank(struct work_struct *work)
                tegra_dc_mask_interrupt(dc, V_BLANK_INT);
 
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 
        if (dc->out->sd_settings && !dc->out->sd_settings->bl_device &&
@@ -1274,8 +1291,11 @@ static void tegra_dc_one_shot_worker(struct work_struct *work)
        /* memory client has gone idle */
        tegra_dc_clear_bandwidth(dc);
 
-       if (dc->out_ops->idle)
+       if (dc->out_ops->idle) {
+               tegra_dc_io_start(dc);
                dc->out_ops->idle(dc);
+               tegra_dc_io_end(dc);
+       }
 
        mutex_unlock(&dc->lock);
 }
@@ -1285,13 +1305,13 @@ static void tegra_dc_one_shot_worker(struct work_struct *work)
 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;
        int i;
 
        dc->stats.underflows++;
@@ -1344,8 +1364,7 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
        /* 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);
+       tegra_dc_unmask_interrupt(dc, ALL_UF_INT);
        trace_underflow(dc);
 }
 
@@ -1418,12 +1437,19 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        unsigned long underflow_mask;
        u32 val;
 
+       mutex_lock(&dc->lock);
+       clk_enable(dc->clk);
+       tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
+
        if (!nvhost_module_powered_ext(nvhost_get_parent(dc->ndev))) {
                WARN(1, "IRQ when DC not powered!\n");
-               tegra_dc_io_start(dc);
                status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
                tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
+               tegra_dc_release_dc_out(dc);
                tegra_dc_io_end(dc);
+               clk_disable(dc->clk);
+               mutex_unlock(&dc->lock);
                return IRQ_HANDLED;
        }
 
@@ -1453,6 +1479,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        else
                tegra_dc_continuous_irq(dc, status);
 
+       tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
+       clk_disable(dc->clk);
+       mutex_unlock(&dc->lock);
+
        return IRQ_HANDLED;
 #else /* CONFIG_TEGRA_FPGA_PLATFORM */
        return IRQ_NONE;
@@ -1577,6 +1608,7 @@ static int tegra_dc_init(struct tegra_dc *dc)
        int i;
        int int_enable;
 
+       tegra_dc_io_start(dc);
        tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
        if (dc->ndev->id == 0) {
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0A,
@@ -1655,14 +1687,19 @@ static int tegra_dc_init(struct tegra_dc *dc)
 
        trace_display_mode(dc, &dc->mode);
 
-       if (dc->mode.pclk)
-               if (tegra_dc_program_mode(dc, &dc->mode))
+       if (dc->mode.pclk) {
+               if (tegra_dc_program_mode(dc, &dc->mode)) {
+                       tegra_dc_io_end(dc);
                        return -EINVAL;
+               }
+       }
 
        /* Initialize SD AFTER the modeset.
           nvsd_init handles the sd_settings = NULL case. */
        nvsd_init(dc, dc->out->sd_settings);
 
+       tegra_dc_io_end(dc);
+
        return 0;
 }
 
@@ -1675,22 +1712,24 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
 
        tegra_dc_setup_clk(dc, dc->clk);
        tegra_dc_clk_enable(dc);
+       tegra_dc_io_start(dc);
 
        tegra_dc_power_on(dc);
 
        /* do not accept interrupts during initialization */
        tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
 
-       enable_dc_irq(dc->irq);
+       enable_dc_irq(dc);
 
        failed_init = tegra_dc_init(dc);
        if (failed_init) {
                tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
-               disable_irq(dc->irq);
+               disable_irq_nosync(dc->irq);
                tegra_dc_clear_bandwidth(dc);
                tegra_dc_clk_disable(dc);
                if (dc->out && dc->out->disable)
                        dc->out->disable();
+               tegra_dc_io_end(dc);
                return false;
        }
 
@@ -1710,6 +1749,7 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
        if (dc->out->postpoweron)
                dc->out->postpoweron();
 
+       tegra_dc_io_end(dc);
        return true;
 }
 
@@ -1726,10 +1766,10 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
 
        if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
                mutex_lock(&tegra_dcs[1]->lock);
-               disable_irq(tegra_dcs[1]->irq);
+               disable_irq_nosync(tegra_dcs[1]->irq);
        } else if (dc->ndev->id == 1 && tegra_dcs[0] != NULL) {
                mutex_lock(&tegra_dcs[0]->lock);
-               disable_irq(tegra_dcs[0]->irq);
+               disable_irq_nosync(tegra_dcs[0]->irq);
        }
 
        msleep(5);
@@ -1741,14 +1781,14 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
 #endif
 
        if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
-               enable_dc_irq(tegra_dcs[1]->irq);
+               enable_dc_irq(tegra_dcs[1]);
                mutex_unlock(&tegra_dcs[1]->lock);
        } else if (dc->ndev->id == 1 && tegra_dcs[0] != NULL) {
-               enable_dc_irq(tegra_dcs[0]->irq);
+               enable_dc_irq(tegra_dcs[0]);
                mutex_unlock(&tegra_dcs[0]->lock);
        }
 
-       enable_dc_irq(dc->irq);
+       enable_dc_irq(dc);
 
        if (tegra_dc_init(dc)) {
                dev_err(&dc->ndev->dev, "cannot initialize\n");
@@ -1817,12 +1857,12 @@ static bool _tegra_dc_enable(struct tegra_dc *dc)
        if (!dc->out)
                return false;
 
-       tegra_dc_io_start(dc);
+       if (dc->enabled)
+               return true;
 
-       if (!_tegra_dc_controller_enable(dc)) {
-               tegra_dc_io_end(dc);
+       if (!_tegra_dc_controller_enable(dc))
                return false;
-       }
+
        return true;
 }
 
@@ -1841,6 +1881,8 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 {
        unsigned i;
 
+       tegra_dc_hold_dc_out(dc);
+
        if (dc->out && dc->out->prepoweroff)
                dc->out->prepoweroff();
 
@@ -1848,11 +1890,14 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
                dc->out_ops->disable(dc);
 
        tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
-       disable_irq(dc->irq);
+
+       disable_irq_nosync(dc->irq);
 
        tegra_dc_clear_bandwidth(dc);
-       tegra_dc_clk_disable(dc);
+       if (dc->out_ops->release) /* ugly hack */
+               tegra_dc_release_dc_out(dc);
+       else
+               tegra_dc_clk_disable(dc);
 
        if (dc->out && dc->out->disable)
                dc->out->disable();
@@ -1933,13 +1978,10 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
                cancel_delayed_work_sync(&dc->one_shot_work);
        }
 
-       tegra_dc_hold_dc_out(dc);
-
+       tegra_dc_io_start(dc);
        _tegra_dc_controller_disable(dc);
        tegra_dc_io_end(dc);
 
-       tegra_dc_release_dc_out(dc);
-
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                mutex_unlock(&dc->one_shot_lock);
 }
@@ -1966,6 +2008,7 @@ void tegra_dc_disable(struct tegra_dc *dc)
 #endif
 
        mutex_unlock(&dc->lock);
+       synchronize_irq(dc->irq);
        trace_display_mode(dc, &dc->mode);
 }
 
@@ -2031,12 +2074,14 @@ static void tegra_dc_underflow_worker(struct work_struct *work)
                to_delayed_work(work), struct tegra_dc, underflow_work);
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
 
        if (dc->enabled) {
                tegra_dc_underflow_handler(dc);
        }
        tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -2201,12 +2246,20 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        }
 
        /* interrupt handler must be registered before tegra_fb_register() */
-       if (request_irq(irq, tegra_dc_irq, 0,
+       if (request_threaded_irq(irq, NULL, tegra_dc_irq, IRQF_ONESHOT,
                        dev_name(&ndev->dev), dc)) {
                dev_err(&ndev->dev, "request_irq %d failed\n", irq);
                ret = -EBUSY;
                goto err_put_emc_clk;
        }
+       disable_dc_irq(dc);
+
+       mutex_lock(&dc->lock);
+       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
+               _tegra_dc_set_default_videomode(dc);
+               dc->enabled = _tegra_dc_enable(dc);
+       }
+       mutex_unlock(&dc->lock);
 
        mutex_lock(&dc->lock);
        if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
@@ -2237,9 +2290,11 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                        dc->pdata->fb->yres = mode->v_active;
                }
 
+               tegra_dc_io_start(dc);
                dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem);
                if (IS_ERR_OR_NULL(dc->fb))
                        dc->fb = NULL;
+               tegra_dc_io_end(dc);
        }
 
        if (dc->out && dc->out->hotplug_init)
@@ -2290,8 +2345,12 @@ static int __devexit tegra_dc_remove(struct nvhost_device *ndev)
        if (dc->ext)
                tegra_dc_ext_unregister(dc->ext);
 
+       mutex_lock(&dc->lock);
        if (dc->enabled)
                _tegra_dc_disable(dc);
+       dc->enabled = false;
+       mutex_unlock(&dc->lock);
+       synchronize_irq(dc->irq); /* wait for IRQ handlers to finish */
 
 #ifdef CONFIG_SWITCH
        switch_dev_unregister(&dc->modeset_switch);
@@ -2318,6 +2377,7 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
        tegra_dc_ext_disable(dc->ext);
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
 
        if (dc->out_ops && dc->out_ops->suspend)
                dc->out_ops->suspend(dc);
@@ -2337,7 +2397,9 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
                        msleep(100);
        }
 
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
+       synchronize_irq(dc->irq); /* wait for IRQ handlers to finish */
 
        return 0;
 }
@@ -2353,8 +2415,9 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
        dc->suspended = false;
 
        if (dc->enabled) {
+               dc->enabled = false;
                _tegra_dc_set_default_videomode(dc);
-               _tegra_dc_enable(dc);
+               dc->enabled = _tegra_dc_enable(dc);
        }
 
        if (dc->out && dc->out->hotplug_init)
index c8d569b..6e9627f 100644 (file)
@@ -175,21 +175,26 @@ static inline bool tegra_dc_is_yuv_planar(int fmt)
        return false;
 }
 
-static inline void tegra_dc_unmask_interrupt(struct tegra_dc *dc, u32 int_val)
+static inline u32 tegra_dc_unmask_interrupt(struct tegra_dc *dc, u32 int_val)
 {
        u32 val;
 
        val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val |= int_val;
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+       tegra_dc_writel(dc, val | int_val, DC_CMD_INT_MASK);
+       return val;
 }
 
-static inline void tegra_dc_mask_interrupt(struct tegra_dc *dc, u32 int_val)
+static inline u32 tegra_dc_mask_interrupt(struct tegra_dc *dc, u32 int_val)
 {
        u32 val;
 
        val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val &= ~int_val;
+       tegra_dc_writel(dc, val & ~int_val, DC_CMD_INT_MASK);
+       return val;
+}
+
+static inline void tegra_dc_restore_interrupt(struct tegra_dc *dc, u32 val)
+{
        tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
 }
 
index 247bfa1..bcc4a03 100644 (file)
@@ -1632,8 +1632,7 @@ static int tegra_dsi_wait_frame_end(struct tegra_dc *dc,
        INIT_COMPLETION(dc->frame_end_complete);
 
        /* unmask frame end interrupt */
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, val | FRAME_END_INT, DC_CMD_INT_MASK);
+       val = tegra_dc_unmask_interrupt(dc, FRAME_END_INT);
 
        timeout = wait_for_completion_interruptible_timeout(
                        &dc->frame_end_complete,
@@ -2548,15 +2547,13 @@ static void tegra_dc_dsi_idle_work(struct work_struct *work)
                tegra_dsi_host_suspend(dsi->dc);
 }
 
-int tegra_dsi_write_data(struct tegra_dc *dc,
+static int tegra_dsi_write_data_nosync(struct tegra_dc *dc,
                        struct tegra_dc_dsi_data *dsi,
                        u8 *pdata, u8 data_id, u16 data_len)
 {
        int err = 0;
        struct dsi_status *init_status;
 
-       tegra_dc_io_start(dc);
-
        init_status = tegra_dsi_prepare_host_transmission(
                                dc, dsi, DSI_LP_OP_WRITE);
        if (IS_ERR_OR_NULL(init_status)) {
@@ -2570,10 +2567,27 @@ fail:
        err = tegra_dsi_restore_state(dc, dsi, init_status);
        if (err < 0)
                dev_err(&dc->ndev->dev, "Failed to restore prev state\n");
+
+       return err;
+}
+
+int tegra_dsi_write_data(struct tegra_dc *dc,
+                       struct tegra_dc_dsi_data *dsi,
+                       u8 *pdata, u8 data_id, u16 data_len)
+{
+       int err;
+
+       tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
+
+       err = tegra_dsi_write_data_nosync(dc, dsi, pdata, data_id, data_len);
+
+       tegra_dc_dsi_release_host(dc);
        tegra_dc_io_end(dc);
 
        return err;
 }
+
 EXPORT_SYMBOL(tegra_dsi_write_data);
 
 static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc,
@@ -2598,7 +2612,7 @@ static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc,
                } else if (cur_cmd->cmd_type == TEGRA_DSI_DELAY_MS) {
                        mdelay(cur_cmd->sp_len_dly.delay_ms);
                } else {
-                       err = tegra_dsi_write_data(dc, dsi,
+                       err = tegra_dsi_write_data_nosync(dc, dsi,
                                                cur_cmd->pdata,
                                                cur_cmd->data_id,
                                                cur_cmd->sp_len_dly.data_len);
@@ -2724,9 +2738,8 @@ int tegra_dsi_start_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi,
                return -EINVAL;
 
        mutex_lock(&dsi->lock);
-       tegra_dc_dsi_hold_host(dc);
-
        tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
 
 #if DSI_USE_SYNC_POINTS
        atomic_set(&dsi_syncpt_rst, 1);
@@ -2750,8 +2763,8 @@ int tegra_dsi_start_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi,
        tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_CONTROL);
 
 fail:
-       tegra_dc_io_end(dc);
        tegra_dc_dsi_release_host(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dsi->lock);
        return err;
 
@@ -2766,9 +2779,8 @@ void tegra_dsi_stop_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi)
        u32 cnt;
 
        mutex_lock(&dsi->lock);
-       tegra_dc_dsi_hold_host(dc);
-
        tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
 
        if (atomic_read(&dsi_syncpt_rst)) {
                tegra_dsi_wait_frame_end(dc, dsi, 2);
@@ -2782,9 +2794,9 @@ void tegra_dsi_stop_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi)
        for (cnt = 0; cnt < 8; cnt++)
                tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + cnt);
 
+       tegra_dc_dsi_release_host(dc);
        tegra_dc_io_end(dc);
 
-       tegra_dc_dsi_release_host(dc);
        mutex_unlock(&dsi->lock);
 }
 EXPORT_SYMBOL(tegra_dsi_stop_host_cmd_v_blank_dcs);
@@ -3189,9 +3201,14 @@ static void _tegra_dc_dsi_enable(struct tegra_dc *dc)
        u32 i;
 
        mutex_lock(&dsi->lock);
+       tegra_dc_io_start(dc);
        tegra_dc_dsi_hold_host(dc);
 
-       tegra_dc_io_start(dc);
+       /* Stop DC stream before configuring DSI registers
+        * to avoid visible glitches on panel during transition
+        * from bootloader to kernel driver
+        */
+       tegra_dsi_stop_dc_stream(dc, dsi);
 
        if (dsi->enabled) {
                if (dsi->ulpm) {
@@ -3309,8 +3326,8 @@ static void _tegra_dc_dsi_enable(struct tegra_dc *dc)
        if (dsi->status.driven == DSI_DRIVEN_MODE_DC)
                tegra_dsi_start_dc_stream(dc, dsi);
 fail:
-       tegra_dc_io_end(dc);
        tegra_dc_dsi_release_host(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dsi->lock);
 }
 
@@ -3856,8 +3873,8 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc)
        int err;
        struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
 
-       tegra_dc_io_start(dc);
        mutex_lock(&dsi->lock);
+       tegra_dc_io_start(dc);
 
        if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
                tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi, 2);
index 6320c56..ebfa6eb 100644 (file)
@@ -129,12 +129,16 @@ int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
        ext->cursor.cur_handle = handle;
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
 
        set_cursor_image_hw(dc, args, phys_addr);
 
        tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 
+       tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        /* XXX sync here? */
 
        mutex_unlock(&dc->lock);
@@ -178,6 +182,8 @@ int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
        enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE);
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
 
        win_options = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
        if (!!(win_options & CURSOR_ENABLE) != enable) {
@@ -196,6 +202,8 @@ int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
        /* TODO: need to sync here?  hopefully can avoid this, but need to
         * figure out interaction w/ rest of GENERAL_ACT_REQ */
 
+       tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 
        mutex_unlock(&ext->cursor.lock);
@@ -229,12 +237,16 @@ int tegra_dc_ext_cursor_clip(struct tegra_dc_ext_user *user,
        }
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
 
        reg_val = tegra_dc_readl(dc, DC_DISP_CURSOR_START_ADDR);
        reg_val &= ~CURSOR_CLIP_SHIFT_BITS(3); /* Clear out the old value */
        tegra_dc_writel(dc, reg_val | CURSOR_CLIP_SHIFT_BITS(*args),
                        DC_DISP_CURSOR_START_ADDR);
 
+       tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
 
        mutex_unlock(&ext->cursor.lock);
index 8eea7ea..74fc3d6 100644 (file)
@@ -1489,10 +1489,7 @@ static void tegra_dc_hdmi_detect_worker(struct work_struct *work)
        /* Set default videomode on dc before enabling it*/
        tegra_dc_set_default_videomode(dc);
 #endif
-       tegra_dc_enable(dc);
-       msleep(5);
        if (!tegra_dc_hdmi_detect(dc)) {
-               tegra_dc_disable(dc);
                tegra_fb_update_monspecs(dc->fb, NULL, NULL);
 
                dc->connected = false;
@@ -1650,21 +1647,11 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
        }
 #endif
 
-       /* TODO: support non-hotplug */
-       if (request_irq(gpio_to_irq(dc->out->hotplug_gpio), tegra_dc_hdmi_irq,
-                       IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                       dev_name(&dc->ndev->dev), dc)) {
-               dev_err(&dc->ndev->dev, "hdmi: request_irq %d failed\n",
-                       gpio_to_irq(dc->out->hotplug_gpio));
-               err = -EBUSY;
-               goto err_put_clock;
-       }
-
        hdmi->edid = tegra_edid_create(dc->out->dcc_bus);
        if (IS_ERR_OR_NULL(hdmi->edid)) {
                dev_err(&dc->ndev->dev, "hdmi: can't create edid\n");
                err = PTR_ERR(hdmi->edid);
-               goto err_free_irq;
+               goto err_put_clock;
        }
 
 #ifdef CONFIG_TEGRA_NVHDCP
@@ -1719,14 +1706,25 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
 
        tegra_dc_hdmi_debug_create(hdmi);
 
+       /* TODO: support non-hotplug */
+       if (request_irq(gpio_to_irq(dc->out->hotplug_gpio), tegra_dc_hdmi_irq,
+               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+               dev_name(&dc->ndev->dev), dc)) {
+               dev_err(&dc->ndev->dev, "hdmi: request_irq %d failed\n",
+                       gpio_to_irq(dc->out->hotplug_gpio));
+               err = -EBUSY;
+               goto err_nvhdcp_destroy;
+       }
+
        return 0;
 
+err_nvhdcp_destroy:
+       if (hdmi->nvhdcp)
+               tegra_nvhdcp_destroy(hdmi->nvhdcp);
 #ifdef CONFIG_TEGRA_NVHDCP
 err_edid_destroy:
-       tegra_edid_destroy(hdmi->edid);
 #endif
-err_free_irq:
-       free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
+       tegra_edid_destroy(hdmi->edid);
 err_put_clock:
 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
        if (!IS_ERR_OR_NULL(hdmi->hda2hdmi_clk))
index 7ce8fc6..892d9f9 100644 (file)
@@ -85,8 +85,12 @@ static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr)
        struct tegra_dc_win *win = &dc->windows[win_idx];
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
+       tegra_dc_hold_dc_out(dc);
 
        if (!dc->enabled) {
+               tegra_dc_release_dc_out(dc);
+               tegra_dc_io_end(dc);
                mutex_unlock(&dc->lock);
                return -EFAULT;
        }
@@ -109,7 +113,10 @@ static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr)
        mutex_unlock(&dc->lock);
 
        tegra_dc_update_windows(&win, 1);
+       tegra_dc_sync_windows(&win, 1);
 
+       tegra_dc_release_dc_out(dc);
+       tegra_dc_io_end(dc);
        return 0;
 }
 
index dfd8852..05cc36c 100644 (file)
@@ -196,7 +196,9 @@ static bool nvsd_phase_in_adjustments(struct tegra_dc *dc,
                        /* Set manual k value */
                        man_k = SD_MAN_K_R(cur_k) |
                                SD_MAN_K_G(cur_k) | SD_MAN_K_B(cur_k);
+                       tegra_dc_io_start(dc);
                        tegra_dc_writel(dc, man_k, DC_DISP_SD_MAN_K_VALUES);
+                       tegra_dc_io_end(dc);
                        /* Set manual brightness value */
                        atomic_set(sd_brightness, cur_sd_brightness);
                }
@@ -293,6 +295,7 @@ static void nvsd_cmd_handler(struct tegra_dc_sd_settings *settings,
                                val &= ~SD_BIN_WIDTH_MASK;
                                val |= bw;
                        }
+
                        tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
 
                        nvsd_phase_in_luts(settings, dc);
@@ -374,6 +377,7 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings)
        u32 bw_idx = 0;
        /* TODO: check if HW says SD's available */
 
+       tegra_dc_io_start(dc);
        /* If SD's not present or disabled, clear the register and return. */
        if (!settings || settings->enable == 0) {
                /* clear the brightness val, too. */
@@ -385,6 +389,7 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings)
                if (settings)
                        settings->phase_settings_step = 0;
                tegra_dc_writel(dc, 0, DC_DISP_SD_CONTROL);
+               tegra_dc_io_end(dc);
                return;
        }
 
@@ -572,6 +577,7 @@ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings)
        /* Finally, Write SD Control */
        tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL);
        dev_dbg(&dc->ndev->dev, "  SD_CONTROL: 0x%08x\n", val);
+       tegra_dc_io_end(dc);
 
        /* set the brightness pointer */
        sd_brightness = settings->sd_brightness;
@@ -1083,9 +1089,11 @@ static ssize_t nvsd_settings_store(struct kobject *kobj,
                                return -ENODEV;
                        }
 
+                       tegra_dc_io_start(dc);
                        tegra_dc_hold_dc_out(dc);
                        nvsd_init(dc, sd_settings);
                        tegra_dc_release_dc_out(dc);
+                       tegra_dc_io_end(dc);
 
                        mutex_unlock(&dc->lock);
 
index b4097d9..36dbbdf 100644 (file)
@@ -97,6 +97,7 @@ static void tegra_dc_rgb_enable(struct tegra_dc *dc)
        int i;
        u32 out_sel_pintable[ARRAY_SIZE(tegra_dc_rgb_enable_out_sel_pintable)];
 
+       tegra_dc_io_start(dc);
        tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
                        PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
                        DC_CMD_DISPLAY_POWER_CONTROL);
@@ -150,13 +151,16 @@ static void tegra_dc_rgb_enable(struct tegra_dc *dc)
        /* Inform DC register updated */
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+       tegra_dc_io_end(dc);
 }
 
 static void tegra_dc_rgb_disable(struct tegra_dc *dc)
 {
+       tegra_dc_io_start(dc);
        tegra_dc_writel(dc, 0x00000000, DC_CMD_DISPLAY_POWER_CONTROL);
 
        tegra_dc_write_table(dc, tegra_dc_rgb_disable_pintable);
+       tegra_dc_io_end(dc);
 }
 
 struct tegra_dc_out_ops tegra_dc_rgb_ops = {
index d20d7f2..a51b78e 100644 (file)
@@ -34,11 +34,16 @@ static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
                                             int n)
 {
        int i;
+       struct tegra_dc *dc = windows[0]->dc;
 
+       mutex_lock(&dc->lock);
        for (i = 0; i < n; i++) {
-               if (windows[i]->dirty)
+               if (windows[i]->dirty) {
+                       mutex_unlock(&dc->lock);
                        return false;
+               }
        }
+       mutex_unlock(&dc->lock);
 
        return true;
 }
@@ -117,6 +122,7 @@ static void tegra_dc_blend_parallel(struct tegra_dc *dc,
        int win_num = dc->gen1_blend_num;
        unsigned long mask = BIT(win_num) - 1;
 
+       tegra_dc_io_start(dc);
        while (mask) {
                int idx = get_topmost_window(blend->z, &mask, win_num);
 
@@ -133,6 +139,7 @@ static void tegra_dc_blend_parallel(struct tegra_dc *dc,
                tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags,
                                win_num), DC_WIN_BLEND_3WIN_XY);
        }
+       tegra_dc_io_end(dc);
 }
 
 static void tegra_dc_blend_sequential(struct tegra_dc *dc,
@@ -140,6 +147,7 @@ static void tegra_dc_blend_sequential(struct tegra_dc *dc,
 {
        int i;
 
+       tegra_dc_io_start(dc);
        for (i = 0; i < DC_N_WINDOWS; i++) {
                if (!tegra_dc_feature_is_gen2_blender(dc, i))
                        continue;
@@ -189,6 +197,7 @@ static void tegra_dc_blend_sequential(struct tegra_dc *dc,
                                        DC_WINBUF_BLEND_LAYER_CONTROL);
                }
        }
+       tegra_dc_io_end(dc);
 }
 
 /* does not support syncing windows on multiple dcs in one call */
@@ -211,6 +220,8 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
                tegra_dc_windows_are_clean(windows, n),
                HZ);
 #endif
+       /* tegra_dc_io_start() done in update_windows */
+       tegra_dc_io_end(windows[0]->dc);
        return ret;
 }
 EXPORT_SYMBOL(tegra_dc_sync_windows);
@@ -301,7 +312,8 @@ static inline void tegra_dc_update_scaling(struct tegra_dc *dc,
                H_DDA_INC(h_dda), DC_WIN_DDA_INCREMENT);
 }
 
-/* does not support updating windows on multiple dcs in one call */
+/* Does not support updating windows on multiple dcs in one call.
+ * Requires a matching sync_windows to avoid leaking ref-count on clocks. */
 int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 {
        struct tegra_dc *dc;
@@ -330,6 +342,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                return -EFAULT;
        }
 
+       tegra_dc_io_start(dc);
        tegra_dc_hold_dc_out(dc);
 
        if (no_vsync)
@@ -529,6 +542,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
        tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
 
        tegra_dc_release_dc_out(dc);
+       /* tegra_dc_io_end() is called in tegra_dc_sync_windows() */
        mutex_unlock(&dc->lock);
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                mutex_unlock(&dc->one_shot_lock);