From bf132a4184143d5780ca747d024768e021b590d5 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Thu, 19 Jul 2012 12:44:08 -0700 Subject: video: tegra: dc: synchronize dsi clock-gating The one shot thread will clock gate the modules periodically. This will ensure relevant paths in dc driver have an active dc clock and dsi host. Bug 1013172 Change-Id: Ibb505e35044f31405c06cb9fa0d6fdf78aafd4a6 Signed-off-by: Rakesh Iyer Reviewed-on: http://git-master/r/117137 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jon Mayo GVS: Gerrit_Virtual_Submit Reviewed-by: Animesh Kishore Reviewed-by: Yu-Huan Hsu --- drivers/video/tegra/dc/csc.c | 2 + drivers/video/tegra/dc/dc.c | 92 ++++++++++++++++++++++++++-------------- drivers/video/tegra/dc/dc_priv.h | 14 ++++++ drivers/video/tegra/dc/dsi.c | 76 +++++++++++++++++++++++++-------- drivers/video/tegra/dc/nvsd.c | 5 ++- drivers/video/tegra/dc/window.c | 4 +- 6 files changed, 141 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/csc.c b/drivers/video/tegra/dc/csc.c index 74fa900352a1..09db5fee4c6f 100644 --- a/drivers/video/tegra/dc/csc.c +++ b/drivers/video/tegra/dc/csc.c @@ -54,10 +54,12 @@ int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx) return -EFAULT; } + tegra_dc_hold_dc_out(dc); tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, DC_CMD_DISPLAY_WINDOW_HEADER); tegra_dc_set_csc(dc, &dc->windows[win_idx].csc); + tegra_dc_release_dc_out(dc); mutex_unlock(&dc->lock); diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 1f7e2ce67682..dc263fbc301e 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -82,7 +82,7 @@ struct tegra_dc *tegra_dcs[TEGRA_MAX_DC]; DEFINE_MUTEX(tegra_dc_lock); DEFINE_MUTEX(shared_lock); -static inline void tegra_dc_clk_enable(struct tegra_dc *dc) +void tegra_dc_clk_enable(struct tegra_dc *dc) { if (!tegra_is_clk_enabled(dc->clk)) { clk_enable(dc->clk); @@ -90,7 +90,7 @@ static inline void tegra_dc_clk_enable(struct tegra_dc *dc) } } -static inline void tegra_dc_clk_disable(struct tegra_dc *dc) +void tegra_dc_clk_disable(struct tegra_dc *dc) { if (tegra_is_clk_enabled(dc->clk)) { clk_disable(dc->clk); @@ -98,6 +98,18 @@ static inline void tegra_dc_clk_disable(struct tegra_dc *dc) } } +void tegra_dc_hold_dc_out(struct tegra_dc *dc) +{ + if (dc->out_ops->hold) + dc->out_ops->hold(dc); +} + +void tegra_dc_release_dc_out(struct tegra_dc *dc) +{ + if (dc->out_ops->release) + dc->out_ops->release(dc); +} + #define DUMP_REG(a) do { \ snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \ #a, a, tegra_dc_readl(dc, a)); \ @@ -121,8 +133,9 @@ static void _dump_regs(struct tegra_dc *dc, void *data, int i; char buff[256]; + mutex_lock(&dc->lock); + tegra_dc_hold_dc_out(dc); tegra_dc_io_start(dc); - tegra_dc_clk_enable(dc); DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); DUMP_REG(DC_CMD_DISPLAY_COMMAND); @@ -272,8 +285,9 @@ 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_clk_disable(dc); tegra_dc_io_end(dc); + tegra_dc_release_dc_out(dc); + mutex_unlock(&dc->lock); } #undef DUMP_REG @@ -488,9 +502,13 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win) if (!dc->enabled) return 0; BUG_ON(win > DC_N_WINDOWS); + mutex_lock(&dc->lock); + 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); + mutex_unlock(&dc->lock); return GET_LINE_STRIDE(stride); } EXPORT_SYMBOL(tegra_dc_get_stride); @@ -550,17 +568,6 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc) } } -void tegra_dc_host_suspend(struct tegra_dc *dc) -{ - tegra_dsi_host_suspend(dc); - tegra_dc_clk_disable(dc); -} - -void tegra_dc_host_resume(struct tegra_dc *dc) { - tegra_dc_clk_enable(dc); - tegra_dsi_host_resume(dc); -} - static inline void disable_dc_irq(unsigned int irq) { disable_irq(irq); @@ -577,9 +584,11 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i) u32 max; mutex_lock(&dc->lock); + 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); mutex_unlock(&dc->lock); return max; @@ -588,11 +597,14 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i) void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val) { mutex_lock(&dc->lock); - if ( dc->enabled ) + if (dc->enabled) { + 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); + } mutex_unlock(&dc->lock); } @@ -609,8 +621,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg) return; } - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); + tegra_dc_hold_dc_out(dc); ctrl = ((cfg->period << PM_PERIOD_SHIFT) | (cfg->clk_div << PM_CLK_DIVIDER_SHIFT) | @@ -644,6 +655,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg) break; } tegra_dc_writel(dc, cmd_state, DC_CMD_STATE_ACCESS); + tegra_dc_release_dc_out(dc); mutex_unlock(&dc->lock); } EXPORT_SYMBOL(tegra_dc_config_pwm); @@ -790,6 +802,9 @@ EXPORT_SYMBOL(tegra_dc_get_out_max_pixclock); 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); val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA | @@ -797,15 +812,21 @@ void tegra_dc_enable_crc(struct tegra_dc *dc) tegra_dc_writel(dc, val, 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_release_dc_out(dc); + mutex_unlock(&dc->lock); } void tegra_dc_disable_crc(struct tegra_dc *dc) { + mutex_lock(&dc->lock); + 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); + mutex_unlock(&dc->lock); } u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc) @@ -821,7 +842,11 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc) * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */ mdelay(TEGRA_CRC_LATCHED_DELAY); + mutex_lock(&dc->lock); + tegra_dc_hold_dc_out(dc); crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED); + tegra_dc_release_dc_out(dc); + mutex_unlock(&dc->lock); crc_error: return crc; } @@ -860,6 +885,7 @@ static void tegra_dc_vblank(struct work_struct *work) return; } + 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() */ if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)) @@ -886,6 +912,7 @@ static void tegra_dc_vblank(struct work_struct *work) if (!dc->vblank_ref_count) tegra_dc_mask_interrupt(dc, V_BLANK_INT); + tegra_dc_release_dc_out(dc); mutex_unlock(&dc->lock); /* Do the actual brightness update outside of the mutex */ @@ -908,8 +935,8 @@ static void tegra_dc_one_shot_worker(struct work_struct *work) /* memory client has gone idle */ tegra_dc_clear_bandwidth(dc); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_suspend(dc); + if (dc->out_ops->idle) + dc->out_ops->idle(dc); mutex_unlock(&dc->lock); } @@ -1505,8 +1532,20 @@ void tegra_dc_blank(struct tegra_dc *dc) static void _tegra_dc_disable(struct tegra_dc *dc) { + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { + mutex_lock(&dc->one_shot_lock); + cancel_delayed_work_sync(&dc->one_shot_work); + } + + tegra_dc_hold_dc_out(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); } void tegra_dc_disable(struct tegra_dc *dc) @@ -1516,16 +1555,9 @@ void tegra_dc_disable(struct tegra_dc *dc) /* it's important that new underflow work isn't scheduled before the * lock is acquired. */ cancel_delayed_work_sync(&dc->underflow_work); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { - mutex_lock(&dc->one_shot_lock); - cancel_delayed_work_sync(&dc->one_shot_work); - } mutex_lock(&dc->lock); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); - if (dc->enabled) { dc->enabled = false; @@ -1538,8 +1570,6 @@ void tegra_dc_disable(struct tegra_dc *dc) #endif mutex_unlock(&dc->lock); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) - mutex_unlock(&dc->one_shot_lock); print_mode_info(dc, dc->mode); } @@ -1605,12 +1635,12 @@ static void tegra_dc_underflow_worker(struct work_struct *work) to_delayed_work(work), struct tegra_dc, underflow_work); mutex_lock(&dc->lock); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); + tegra_dc_hold_dc_out(dc); if (dc->enabled) { tegra_dc_underflow_handler(dc); } + tegra_dc_release_dc_out(dc); mutex_unlock(&dc->lock); } diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index fb1243593587..332c80f9cbb6 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -75,6 +75,12 @@ struct tegra_dc_out_ops { void (*enable)(struct tegra_dc *dc); /* disable output. dc clocks are on at this point */ void (*disable)(struct tegra_dc *dc); + /* hold output. keeps dc clocks on. */ + void (*hold)(struct tegra_dc *dc); + /* release output. dc clocks may turn off after this. */ + void (*release)(struct tegra_dc *dc); + /* idle routine of output. dc clocks may turn off after this. */ + void (*idle)(struct tegra_dc *dc); /* suspend output. dc clocks are on at this point */ void (*suspend)(struct tegra_dc *dc); /* resume output. dc clocks are on at this point */ @@ -369,6 +375,14 @@ void tegra_dc_set_out_pin_polars(struct tegra_dc *dc, /* defined in dc.c, used in bandwidth.c */ unsigned int tegra_dc_has_multiple_dc(void); +/* defined in dc.c, used in dsi.c */ +void tegra_dc_clk_enable(struct tegra_dc *dc); +void tegra_dc_clk_disable(struct tegra_dc *dc); + +/* defined in dc.c, used in nvsd.c and dsi.c */ +void tegra_dc_hold_dc_out(struct tegra_dc *dc); +void tegra_dc_release_dc_out(struct tegra_dc *dc); + /* defined in bandwidth.c, used in dc.c */ void tegra_dc_clear_bandwidth(struct tegra_dc *dc); void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new); diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index ba8a451a4b4f..935e98bc2f87 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -294,6 +294,9 @@ const u32 init_reg[] = { DSI_PKT_LEN_6_7, }; +static int tegra_dsi_host_suspend(struct tegra_dc *dc); +static int tegra_dsi_host_resume(struct tegra_dc *dc); + inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg) { unsigned long ret; @@ -2118,6 +2121,39 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi, return err; } +static void tegra_dc_dsi_hold_host(struct tegra_dc *dc) +{ + struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) { + /* + * The reference count should never be more than 1. + */ + BUG_ON(tegra_is_clk_enabled(dc->clk) > 1); + + if (dsi->host_suspended) + tegra_dsi_host_resume(dc); + + /* + * Take an extra refrence to count for the clk_disable in + * tegra_dc_release_host. + */ + clk_enable(dc->clk); + } +} + +static void tegra_dc_dsi_release_host(struct tegra_dc *dc) +{ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + clk_disable(dc->clk); +} + +static void tegra_dc_dsi_idle(struct tegra_dc *dc) +{ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dsi_host_suspend(dc); +} + int tegra_dsi_write_data(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi, u8 *pdata, u8 data_id, u16 data_len) @@ -2127,9 +2163,6 @@ int tegra_dsi_write_data(struct tegra_dc *dc, tegra_dc_io_start(dc); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); - init_status = tegra_dsi_prepare_host_transmission( dc, dsi, DSI_LP_OP_WRITE); if (IS_ERR_OR_NULL(init_status)) { @@ -2144,6 +2177,7 @@ fail: if (err < 0) dev_err(&dc->ndev->dev, "Failed to restore prev state\n"); tegra_dc_io_end(dc); + return err; } EXPORT_SYMBOL(tegra_dsi_write_data); @@ -2289,10 +2323,10 @@ 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); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); err = tegra_dsi_dcs_pkt_seq_ctrl_init(dsi, cmd); if (err < 0) { @@ -2313,6 +2347,7 @@ int tegra_dsi_start_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi, fail: tegra_dc_io_end(dc); + tegra_dc_dsi_release_host(dc); mutex_unlock(&dsi->lock); return err; @@ -2327,10 +2362,9 @@ void tegra_dsi_stop_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi) u32 cnt; mutex_lock(&dsi->lock); - tegra_dc_io_start(dc); + tegra_dc_dsi_hold_host(dc); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); + tegra_dc_io_start(dc); tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_INIT_SEQ_CONTROL); @@ -2339,6 +2373,8 @@ void tegra_dsi_stop_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi) tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + cnt); 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); @@ -2750,11 +2786,10 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) int err; u32 val; - tegra_dc_io_start(dc); mutex_lock(&dsi->lock); + tegra_dc_dsi_hold_host(dc); - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(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 @@ -2866,8 +2901,9 @@ 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: - mutex_unlock(&dsi->lock); tegra_dc_io_end(dc); + tegra_dc_dsi_release_host(dc); + mutex_unlock(&dsi->lock); } static void _tegra_dc_dsi_init(struct tegra_dc *dc) @@ -3261,8 +3297,7 @@ fail: return err; } - -int tegra_dsi_host_suspend(struct tegra_dc *dc) +static int tegra_dsi_host_suspend(struct tegra_dc *dc) { int err = 0; struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); @@ -3270,6 +3305,8 @@ int tegra_dsi_host_suspend(struct tegra_dc *dc) if (dsi->host_suspended) return 0; + dsi->host_suspended = true; + tegra_dsi_stop_dc_stream(dc, dsi); err = tegra_dsi_deep_sleep(dc, dsi, dsi->info.suspend_aggr); @@ -3277,13 +3314,12 @@ int tegra_dsi_host_suspend(struct tegra_dc *dc) dev_err(&dc->ndev->dev, "DSI failed to enter deep sleep\n"); - dsi->host_suspended = true; + tegra_dc_clk_disable(dc); return err; } - -int tegra_dsi_host_resume(struct tegra_dc *dc) +static int tegra_dsi_host_resume(struct tegra_dc *dc) { int val = 0; int err = 0; @@ -3292,6 +3328,7 @@ int tegra_dsi_host_resume(struct tegra_dc *dc) if (!dsi->host_suspended) return 0; + tegra_dc_clk_enable(dc); switch (dsi->info.suspend_aggr) { case DSI_HOST_SUSPEND_LV0: tegra_dsi_clk_enable(dsi); @@ -3336,9 +3373,9 @@ int tegra_dsi_host_resume(struct tegra_dc *dc) "is not supported.\n"); } + tegra_dsi_start_dc_stream(dc, dsi); dsi->enabled = true; dsi->host_suspended = false; - tegra_dsi_start_dc_stream(dc, dsi); fail: return err; } @@ -3432,6 +3469,9 @@ struct tegra_dc_out_ops tegra_dc_dsi_ops = { .destroy = tegra_dc_dsi_destroy, .enable = tegra_dc_dsi_enable, .disable = tegra_dc_dsi_disable, + .hold = tegra_dc_dsi_hold_host, + .release = tegra_dc_dsi_release_host, + .idle = tegra_dc_dsi_idle, #ifdef CONFIG_PM .suspend = tegra_dc_dsi_suspend, .resume = tegra_dc_dsi_resume, diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c index e3058b596f69..6e76ee0f1702 100644 --- a/drivers/video/tegra/dc/nvsd.c +++ b/drivers/video/tegra/dc/nvsd.c @@ -809,9 +809,12 @@ static ssize_t nvsd_settings_store(struct kobject *kobj, mutex_unlock(&dc->lock); return -ENODEV; } - mutex_unlock(&dc->lock); + tegra_dc_hold_dc_out(dc); nvsd_init(dc, sd_settings); + tegra_dc_release_dc_out(dc); + + mutex_unlock(&dc->lock); /* Update backlight state IFF we're disabling! */ if (!sd_settings->enable && sd_settings->bl_device) { diff --git a/drivers/video/tegra/dc/window.c b/drivers/video/tegra/dc/window.c index 5161dd4f7003..0b4350d49418 100644 --- a/drivers/video/tegra/dc/window.c +++ b/drivers/video/tegra/dc/window.c @@ -219,8 +219,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) return -EFAULT; } - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); + tegra_dc_hold_dc_out(dc); if (no_vsync) tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, @@ -424,6 +423,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask); + tegra_dc_release_dc_out(dc); mutex_unlock(&dc->lock); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) mutex_unlock(&dc->one_shot_lock); -- cgit v1.2.3