video: tegra: dc: power optimize DC and host1x clk
[linux-2.6.git] / drivers / video / tegra / dc / dc.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)