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 performans 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 1031933
Bug 1002768
Bug 1036025
Bug 1013506
Bug 1025621
Bug 1028716
Bug 1029041
Bug 899059
Bug 887342
Bug 929609

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

drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dsi.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 da7f291..14c86b1 100644 (file)
@@ -134,8 +134,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);
@@ -285,8 +285,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);
 }
 
@@ -503,11 +503,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);
 }
@@ -556,6 +558,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),
@@ -568,9 +571,11 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
        }
 }
 
-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)
@@ -584,11 +589,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;
@@ -598,12 +605,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);
 }
@@ -621,6 +630,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) |
@@ -656,6 +666,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);
@@ -804,8 +815,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;
@@ -813,19 +824,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);
 }
 
@@ -843,9 +856,11 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
        mdelay(TEGRA_CRC_LATCHED_DELAY);
 
        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;
@@ -863,13 +878,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
 }
 
@@ -885,6 +900,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() */
@@ -913,6 +929,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);
 
        /* Do the actual brightness update outside of the mutex */
@@ -935,8 +952,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);
 }
@@ -946,13 +966,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++;
@@ -1015,8 +1035,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);
        print_underflow_info(dc);
 }
 
@@ -1062,12 +1081,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;
        }
 
@@ -1097,6 +1123,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;
@@ -1215,6 +1246,7 @@ static int tegra_dc_init(struct tegra_dc *dc)
 {
        int i;
 
+       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,
@@ -1277,14 +1309,19 @@ static int tegra_dc_init(struct tegra_dc *dc)
 
        print_mode_info(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;
 }
 
@@ -1297,20 +1334,22 @@ 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);
 
        /* 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;
        }
 
@@ -1330,6 +1369,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;
 }
 
@@ -1346,10 +1386,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);
@@ -1361,14 +1401,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");
@@ -1426,16 +1466,16 @@ static int _tegra_dc_set_default_videomode(struct tegra_dc *dc)
 
 static bool _tegra_dc_enable(struct tegra_dc *dc)
 {
+       if (dc->enabled)
+               return true;
+
        if (dc->mode.pclk == 0)
                return false;
 
        if (!dc->out)
                return false;
 
-       tegra_dc_io_start(dc);
-
        if (!_tegra_dc_controller_enable(dc)) {
-               tegra_dc_io_end(dc);
                return false;
        }
        return true;
@@ -1456,6 +1496,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();
 
@@ -1463,11 +1505,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();
@@ -1548,13 +1593,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);
 }
@@ -1581,6 +1623,7 @@ void tegra_dc_disable(struct tegra_dc *dc)
 #endif
 
        mutex_unlock(&dc->lock);
+       synchronize_irq(dc->irq);
        print_mode_info(dc, dc->mode);
 }
 
@@ -1646,12 +1689,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);
 }
 
@@ -1812,20 +1857,21 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                dc->ext = NULL;
        }
 
-       mutex_lock(&dc->lock);
-       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
-               dc->enabled = _tegra_dc_enable(dc);
-               _tegra_dc_set_default_videomode(dc);
-       }
-       mutex_unlock(&dc->lock);
-
        /* 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);
 
        tegra_dc_create_debugfs(dc);
 
@@ -1849,9 +1895,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)
@@ -1902,8 +1950,12 @@ static int 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);
@@ -1930,6 +1982,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);
@@ -1949,7 +2002,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;
 }
@@ -1965,8 +2020,9 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
        dc->suspended = false;
 
        if (dc->enabled) {
-               _tegra_dc_enable(dc);
+               dc->enabled = false;
                _tegra_dc_set_default_videomode(dc);
+               dc->enabled = _tegra_dc_enable(dc);
        }
 
        if (dc->out && dc->out->hotplug_init)
index 759d64d..7521158 100644 (file)
@@ -325,21 +325,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 4a9e507..f60184b 100644 (file)
@@ -1391,8 +1391,7 @@ static void tegra_dsi_stop_dc_stream_at_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);
 
        tegra_dsi_stop_dc_stream(dc, dsi);
 
@@ -1412,7 +1411,7 @@ static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc,
        tegra_dsi_soft_reset(dsi);
 
        /* reinstate interrupt mask */
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+       tegra_dc_restore_interrupt(dc, val); /* potentially a race? */
 
        if (timeout == 0)
                dev_warn(&dc->ndev->dev,
@@ -2142,15 +2141,13 @@ static void tegra_dc_dsi_idle(struct tegra_dc *dc)
                tegra_dsi_host_suspend(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)) {
@@ -2164,10 +2161,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,
@@ -2186,7 +2200,7 @@ static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc,
                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);
@@ -2311,9 +2325,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);
 
 
        err = tegra_dsi_dcs_pkt_seq_ctrl_init(dsi, cmd);
@@ -2334,8 +2347,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;
 
@@ -2350,9 +2363,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);
 
        tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_INIT_SEQ_CONTROL);
 
@@ -2360,9 +2372,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);
@@ -2778,9 +2790,9 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
        u32 val;
 
        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
@@ -2893,8 +2905,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);
 }
 
@@ -3377,8 +3389,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);
index 55d9163..9f5968f 100644 (file)
@@ -1462,10 +1462,7 @@ static void tegra_dc_hdmi_detect_worker(struct work_struct *work)
                container_of(to_delayed_work(work), struct tegra_dc_hdmi_data, work);
        struct tegra_dc *dc = hdmi->dc;
 
-       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;
@@ -1623,21 +1620,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
@@ -1692,14 +1679,23 @@ 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;
 
-#ifdef CONFIG_TEGRA_NVHDCP
+err_nvhdcp_destroy:
+       if (hdmi->nvhdcp)
+               tegra_nvhdcp_destroy(hdmi->nvhdcp);
 err_edid_destroy:
        tegra_edid_destroy(hdmi->edid);
-#endif
-err_free_irq:
-       free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
 err_put_clock:
 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
        if (!IS_ERR_OR_NULL(hdmi->hda2hdmi_clk))
index 7ce8fc6..2dfecfc 100644 (file)
@@ -109,6 +109,7 @@ 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);
 
        return 0;
 }
index 6e76ee0..2f33431 100644 (file)
@@ -174,7 +174,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);
                }
@@ -271,6 +273,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);
@@ -351,6 +354,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. */
@@ -362,6 +366,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;
        }
 
@@ -487,6 +492,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;
@@ -810,9 +816,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 0b4350d..48fef11 100644 (file)
@@ -31,11 +31,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;
 }
@@ -100,6 +105,7 @@ static void tegra_dc_set_blending(struct tegra_dc *dc,
 {
        unsigned long mask = BIT(DC_N_WINDOWS) - 1;
 
+       tegra_dc_io_start(dc);
        while (mask) {
                int idx = get_topmost_window(blend->z, &mask);
 
@@ -116,6 +122,7 @@ static void tegra_dc_set_blending(struct tegra_dc *dc,
                tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags),
                                DC_WIN_BLEND_3WIN_XY);
        }
+       tegra_dc_io_end(dc);
 }
 
 /* does not support syncing windows on multiple dcs in one call */
@@ -141,6 +148,8 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
        trace_printk("%s:After wait_event_interruptible_timeout\n",
                windows[0]->dc->ndev->name);
 #endif
+       /* tegra_dc_io_start() done in update_windows */
+       tegra_dc_io_end(windows[0]->dc);
        return ret;
 }
 EXPORT_SYMBOL(tegra_dc_sync_windows);
@@ -192,7 +201,8 @@ static inline u32 compute_initial_dda(fixed20_12 in)
        return dfixed_frac(in);
 }
 
-/* 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;
@@ -219,6 +229,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)
@@ -424,6 +435,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
        trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask);
 
        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);