From 5a03b6f2f40e28d4d7d2fbb78f70cdcd677f6d36 Mon Sep 17 00:00:00 2001 From: Daniel Solomon Date: Thu, 13 Mar 2014 15:14:36 -0700 Subject: video: tegra: dp: update link enable/disable Update SOR attach sequence and implement SOR detach sequence according to recommended guidelines. Change-Id: Id0d2395508a6e61847e1890ea85d4025300691eb Signed-off-by: Daniel Solomon Reviewed-on: http://git-master/r/381762 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Animesh Kishore Reviewed-by: Venkat Moganty --- drivers/video/tegra/dc/dc.c | 19 ++++ drivers/video/tegra/dc/dc_priv.h | 5 + drivers/video/tegra/dc/dp.c | 1 + drivers/video/tegra/dc/sor.c | 194 ++++++++++++++++++++++++++++++++++----- drivers/video/tegra/dc/sor.h | 2 + 5 files changed, 196 insertions(+), 25 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index c2f9dd37bb7f..f2098a1fa147 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -935,6 +935,25 @@ static inline void tegra_dc_create_debugfs(struct tegra_dc *dc) { }; static inline void tegra_dc_remove_debugfs(struct tegra_dc *dc) { }; #endif /* CONFIG_DEBUGFS */ +unsigned long tegra_dc_poll_register(struct tegra_dc *dc, u32 reg, u32 mask, + u32 exp_val, u32 poll_interval_us, u32 timeout_ms) +{ + unsigned long timeout_jf = jiffies + msecs_to_jiffies(timeout_ms); + u32 reg_val = 0; + + do { + usleep_range(poll_interval_us, poll_interval_us << 1); + reg_val = tegra_dc_readl(dc, reg); + } while (((reg_val & mask) != exp_val) && + time_after(timeout_jf, jiffies)); + + if ((reg_val & mask) == exp_val) + return 0; /* success */ + dev_err(&dc->ndev->dev, + "dc_poll_register 0x%x: timeout\n", reg); + return jiffies - timeout_jf + 1; +} + static int tegra_dc_set(struct tegra_dc *dc, int index) { int ret = 0; diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index b5ca15b103e2..9fa14399b179 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -398,6 +398,11 @@ void tegra_dc_release_dc_out(struct tegra_dc *dc); /* defined in dc.c, used in ext/dev.c */ void tegra_dc_call_flip_callback(void); +/* defined in dc.c, used in sor.c */ +unsigned long tegra_dc_poll_register(struct tegra_dc *dc, +u32 reg, u32 mask, u32 exp_val, u32 poll_interval_us, +u32 timeout_ms); + /* 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/dp.c b/drivers/video/tegra/dc/dp.c index 307221f25de0..aa9eaaf59a1c 100644 --- a/drivers/video/tegra/dc/dp.c +++ b/drivers/video/tegra/dc/dp.c @@ -2111,6 +2111,7 @@ static void tegra_dc_dp_disable(struct tegra_dc *dc) tegra_dpaux_pad_power(dp->dc, false); /* Power down SOR */ + tegra_dc_sor_detach(dp->sor); tegra_dc_sor_disable(dp->sor, false); tegra_dpaux_clk_disable(dp); diff --git a/drivers/video/tegra/dc/sor.c b/drivers/video/tegra/dc/sor.c index 8a5177fe4764..4df7000e080f 100644 --- a/drivers/video/tegra/dc/sor.c +++ b/drivers/video/tegra/dc/sor.c @@ -844,6 +844,17 @@ static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor, tegra_dc_sor_config_pwm(sor, 1024, 1024); } +static void tegra_dc_sor_general_act(struct tegra_dc *dc) +{ + tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + + if (tegra_dc_poll_register(dc, DC_CMD_STATE_CONTROL, + GENERAL_ACT_REQ, 0, 100, + TEGRA_DC_POLL_TIMEOUT_MS)) + dev_err(&dc->ndev->dev, + "dc timeout waiting for DC to stop\n"); +} + static void tegra_dc_sor_enable_dc(struct tegra_dc_sor_data *sor) { struct tegra_dc *dc = sor->dc; @@ -975,15 +986,24 @@ void tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor) tegra_sor_pad_power_up(sor, false); } +static void tegra_dc_sor_enable_sor(struct tegra_dc_sor_data *sor, bool enable) +{ + struct tegra_dc *dc = sor->dc; + u32 reg_val = tegra_dc_readl(sor->dc, DC_DISP_DISP_WIN_OPTIONS); + reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE; + tegra_dc_writel(dc, reg_val, DC_DISP_DISP_WIN_OPTIONS); +} void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor) { - u32 reg_val; struct tegra_dc *dc = sor->dc; + u32 reg_val; tegra_dc_get(dc); - tegra_dc_sor_enable_dc(sor); + reg_val = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); + tegra_dc_writel(dc, reg_val | WRITE_MUX_ACTIVE, DC_CMD_STATE_ACCESS); + tegra_dc_sor_config_panel(sor, false); tegra_dc_writel(dc, 0x9f00, DC_CMD_STATE_CONTROL); @@ -996,36 +1016,164 @@ void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor) 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_writel(dc, SOR_ENABLE, DC_DISP_DISP_WIN_OPTIONS); - - /* Attach head */ tegra_dc_sor_update(sor); - reg_val = NV_SOR_SUPER_STATE1_ASY_HEAD_OP_AWAKE | + + /* WAR for bug 1428181 */ + tegra_dc_sor_enable_sor(sor, true); + tegra_dc_sor_enable_sor(sor, false); + + /* Awake request */ + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ASY_HEAD_OP_AWAKE | NV_SOR_SUPER_STATE1_ASY_ORMODE_NORMAL | - NV_SOR_SUPER_STATE1_ATTACHED_NO; - tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, reg_val); + NV_SOR_SUPER_STATE1_ATTACHED_YES); tegra_dc_sor_super_update(sor); - reg_val |= NV_SOR_SUPER_STATE1_ATTACHED_YES; - tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, reg_val); - tegra_dc_sor_super_update(sor); + tegra_dc_sor_enable_dc(sor); + + tegra_dc_sor_enable_sor(sor, true); if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST, - NV_SOR_TEST_ATTACHED_DEFAULT_MASK, - NV_SOR_TEST_ATTACHED_TRUE, - 100, TEGRA_SOR_ATTACH_TIMEOUT_MS)) { - dev_err(&sor->dc->ndev->dev, - "dc timeout waiting for ATTACHED = TRUE\n"); + NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, + NV_SOR_TEST_ACT_HEAD_OPMODE_AWAKE, + 100, TEGRA_SOR_ATTACH_TIMEOUT_MS)) { + dev_err(&dc->ndev->dev, + "dc timeout waiting for OPMOD = AWAKE\n"); + } + + tegra_dc_writel(dc, reg_val, DC_CMD_STATE_ACCESS); + tegra_dc_put(dc); +} + +static struct tegra_dc_mode min_mode = { + .h_ref_to_sync = 0, + .v_ref_to_sync = 1, + .h_sync_width = 1, + .v_sync_width = 1, + .h_back_porch = 20, + .v_back_porch = 0, + .h_active = 16, + .v_active = 16, + .h_front_porch = 1, + .v_front_porch = 2, +}; + +/* Disable windows and set minimum raster timings */ +static void +tegra_dc_sor_disable_win_short_raster(struct tegra_dc *dc, int *dc_reg_ctx) +{ + int selected_windows, i; + + selected_windows = tegra_dc_readl(dc, DC_CMD_DISPLAY_WINDOW_HEADER); + + /* Store and clear window options */ + for_each_set_bit(i, &dc->valid_windows, DC_N_WINDOWS) { + tegra_dc_writel(dc, WINDOW_A_SELECT << i, + DC_CMD_DISPLAY_WINDOW_HEADER); + dc_reg_ctx[i] = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS); + tegra_dc_writel(dc, WIN_A_ACT_REQ << i, DC_CMD_STATE_CONTROL); } + tegra_dc_writel(dc, selected_windows, DC_CMD_DISPLAY_WINDOW_HEADER); + + /* Store current raster timings and set minimum timings */ + dc_reg_ctx[i++] = tegra_dc_readl(dc, DC_DISP_REF_TO_SYNC); + tegra_dc_writel(dc, min_mode.h_ref_to_sync | + (min_mode.v_ref_to_sync << 16), DC_DISP_REF_TO_SYNC); + + dc_reg_ctx[i++] = tegra_dc_readl(dc, DC_DISP_SYNC_WIDTH); + tegra_dc_writel(dc, min_mode.h_sync_width | + (min_mode.v_sync_width << 16), DC_DISP_SYNC_WIDTH); + + dc_reg_ctx[i++] = tegra_dc_readl(dc, DC_DISP_BACK_PORCH); + tegra_dc_writel(dc, min_mode.h_back_porch | + ((min_mode.v_back_porch - min_mode.v_ref_to_sync) << 16), + DC_DISP_BACK_PORCH); + + dc_reg_ctx[i++] = tegra_dc_readl(dc, DC_DISP_FRONT_PORCH); + tegra_dc_writel(dc, min_mode.h_front_porch | + ((min_mode.v_front_porch + min_mode.v_ref_to_sync) << 16), + DC_DISP_FRONT_PORCH); + + dc_reg_ctx[i++] = tegra_dc_readl(dc, DC_DISP_DISP_ACTIVE); + tegra_dc_writel(dc, min_mode.h_active | (min_mode.v_active << 16), + DC_DISP_DISP_ACTIVE); + + tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); +} + +/* Restore previous windows status and raster timings */ +static void +tegra_dc_sor_restore_win_and_raster(struct tegra_dc *dc, int *dc_reg_ctx) +{ + int selected_windows, i; + + selected_windows = tegra_dc_readl(dc, DC_CMD_DISPLAY_WINDOW_HEADER); + + for_each_set_bit(i, &dc->valid_windows, DC_N_WINDOWS) { + tegra_dc_writel(dc, WINDOW_A_SELECT << i, + DC_CMD_DISPLAY_WINDOW_HEADER); + tegra_dc_writel(dc, dc_reg_ctx[i], DC_WIN_WIN_OPTIONS); + tegra_dc_writel(dc, WIN_A_ACT_REQ << i, DC_CMD_STATE_CONTROL); + } + + tegra_dc_writel(dc, selected_windows, DC_CMD_DISPLAY_WINDOW_HEADER); + + tegra_dc_writel(dc, dc_reg_ctx[i++], DC_DISP_REF_TO_SYNC); + tegra_dc_writel(dc, dc_reg_ctx[i++], DC_DISP_SYNC_WIDTH); + tegra_dc_writel(dc, dc_reg_ctx[i++], DC_DISP_BACK_PORCH); + tegra_dc_writel(dc, dc_reg_ctx[i++], DC_DISP_FRONT_PORCH); + tegra_dc_writel(dc, dc_reg_ctx[i++], DC_DISP_DISP_ACTIVE); + + tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); +} + +void tegra_dc_sor_detach(struct tegra_dc_sor_data *sor) +{ + struct tegra_dc *dc = sor->dc; + int dc_reg_ctx[DC_N_WINDOWS + 5]; + unsigned long dc_int_mask; + + tegra_dc_get(dc); + + /* Sleep mode */ + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SLEEP | + NV_SOR_SUPER_STATE1_ASY_ORMODE_SAFE | + NV_SOR_SUPER_STATE1_ATTACHED_YES); + tegra_dc_sor_super_update(sor); + + tegra_dc_sor_disable_win_short_raster(dc, dc_reg_ctx); + if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST, - NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, - NV_SOR_TEST_ACT_HEAD_OPMODE_AWAKE, - 100, TEGRA_SOR_ATTACH_TIMEOUT_MS)) { - dev_err(&sor->dc->ndev->dev, - "dc timeout waiting for OPMOD = AWAKE\n"); + NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, + NV_SOR_TEST_ACT_HEAD_OPMODE_SLEEP, + 100, TEGRA_SOR_ATTACH_TIMEOUT_MS)) { + dev_err(&dc->ndev->dev, + "dc timeout waiting for OPMOD = SLEEP\n"); } + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SLEEP | + NV_SOR_SUPER_STATE1_ASY_ORMODE_SAFE | + NV_SOR_SUPER_STATE1_ATTACHED_NO); + + /* Mask DC interrupts during the 2 dummy frames required for detach */ + dc_int_mask = tegra_dc_readl(dc, DC_CMD_INT_MASK); + tegra_dc_writel(dc, 0, DC_CMD_INT_MASK); + + /* Stop DC->SOR path */ + tegra_dc_sor_enable_sor(sor, false); + tegra_dc_sor_general_act(dc); + + /* Stop DC */ + tegra_dc_writel(dc, DISP_CTRL_MODE_STOP, DC_CMD_DISPLAY_COMMAND); + tegra_dc_sor_general_act(dc); + + tegra_dc_sor_restore_win_and_raster(dc, dc_reg_ctx); + + tegra_dc_writel(dc, dc_int_mask, DC_CMD_INT_MASK); tegra_dc_put(dc); } @@ -1150,10 +1298,6 @@ void tegra_dc_sor_disable(struct tegra_dc_sor_data *sor, bool is_lvds) return; } - tegra_dc_writel(dc, 0, DC_DISP_DISP_WIN_OPTIONS); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); - tegra_sor_clk_disable(sor); /* Reset SOR clk */ tegra_periph_reset_assert(sor->sor_clk); diff --git a/drivers/video/tegra/dc/sor.h b/drivers/video/tegra/dc/sor.h index 057311215c52..ef82111c01a7 100644 --- a/drivers/video/tegra/dc/sor.h +++ b/drivers/video/tegra/dc/sor.h @@ -102,6 +102,7 @@ struct tegra_dc_sor_data { #define TEGRA_SOR_TIMEOUT_MS 1000 #define TEGRA_SOR_ATTACH_TIMEOUT_MS 100000 #define TEGRA_SOR_SEQ_BUSY_TIMEOUT_MS 10000 +#define TEGRA_DC_POLL_TIMEOUT_MS 50 #define CHECK_RET(x) \ do { \ @@ -117,6 +118,7 @@ struct tegra_dc_sor_data *tegra_dc_sor_init(struct tegra_dc *dc, void tegra_dc_sor_destroy(struct tegra_dc_sor_data *sor); void tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor); void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor); +void tegra_dc_sor_detach(struct tegra_dc_sor_data *sor); void tegra_dc_sor_enable_lvds(struct tegra_dc_sor_data *sor, bool balanced, bool conforming); void tegra_dc_sor_disable(struct tegra_dc_sor_data *sor, bool is_lvds); -- cgit v1.2.3