diff options
Diffstat (limited to 'drivers/video/tegra/dc/dsi.c')
-rw-r--r-- | drivers/video/tegra/dc/dsi.c | 360 |
1 files changed, 281 insertions, 79 deletions
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 4b055ec529c6..7ee9375f58f1 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/dsi.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -14,23 +14,25 @@ * */ +#include <linux/kernel.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/fb.h> #include <linux/gpio.h> #include <linux/interrupt.h> -#include <linux/kernel.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/nvhost.h> #include <mach/clk.h> #include <mach/dc.h> #include <mach/fb.h> #include <mach/csi.h> +#include <mach/iomap.h> #include <linux/nvhost.h> #include "dc_reg.h" @@ -38,6 +40,9 @@ #include "dsi_regs.h" #include "dsi.h" +#define APB_MISC_GP_MIPI_PAD_CTRL_0 (TEGRA_APB_MISC_BASE + 0x820) +#define DSIB_MODE_ENABLE 0x2 + #define DSI_USE_SYNC_POINTS 1 #define S_TO_MS(x) (1000 * (x)) @@ -109,6 +114,7 @@ struct tegra_dc_dsi_data { struct clk *dc_clk; struct clk *dsi_clk; + struct clk *dsi_fixed_clk; bool clk_ref; struct mutex lock; @@ -120,6 +126,10 @@ struct tegra_dc_dsi_data { struct dsi_phy_timing_inclk phy_timing; + bool ulpm; + bool enabled; + bool host_suspended; + u8 driven_mode; u8 controller_index; @@ -141,9 +151,6 @@ struct tegra_dc_dsi_data { u32 current_dsi_clk_khz; u32 dsi_control_val; - - bool ulpm; - bool enabled; }; const u32 dsi_pkt_seq_reg[NUMOF_PKT_SEQ] = { @@ -291,7 +298,7 @@ inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg) { unsigned long ret; - BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev)); + BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dsi->dc->ndev))); ret = readl(dsi->base + reg * 4); trace_printk("readl %p=%#08lx\n", dsi->base + reg * 4, ret); return ret; @@ -300,7 +307,7 @@ EXPORT_SYMBOL(tegra_dsi_readl); inline void tegra_dsi_writel(struct tegra_dc_dsi_data *dsi, u32 val, u32 reg) { - BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev)); + BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dsi->dc->ndev))); trace_printk("writel %p=%#08x\n", dsi->base + reg * 4, val); writel(val, dsi->base + reg * 4); } @@ -411,6 +418,22 @@ static inline void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi) { } #endif +static inline void tegra_dsi_clk_enable(struct tegra_dc_dsi_data *dsi) +{ + if (!tegra_is_clk_enabled(dsi->dsi_clk)) { + clk_enable(dsi->dsi_clk); + clk_enable(dsi->dsi_fixed_clk); + } +} + +static inline void tegra_dsi_clk_disable(struct tegra_dc_dsi_data *dsi) +{ + if (tegra_is_clk_enabled(dsi->dsi_clk)) { + clk_disable(dsi->dsi_clk); + clk_disable(dsi->dsi_fixed_clk); + } +} + static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi) { u32 val; @@ -418,17 +441,15 @@ static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi) ret = 0; - dsi->syncpt_val = nvhost_syncpt_read( - &nvhost_get_host(dsi->dc->ndev)->syncpt, - dsi->syncpt_id); + dsi->syncpt_val = nvhost_syncpt_read_ext(dsi->dc->ndev, dsi->syncpt_id); val = DSI_INCR_SYNCPT_COND(OP_DONE) | DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); /* TODO: Use interrupt rather than polling */ - ret = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt, - dsi->syncpt_id, dsi->syncpt_val + 1); + ret = nvhost_syncpt_wait_timeout_ext(dsi->dc->ndev, dsi->syncpt_id, + dsi->syncpt_val + 1, MAX_SCHEDULE_TIMEOUT, NULL); if (ret < 0) { dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n"); goto fail; @@ -1052,7 +1073,7 @@ static u32 tegra_dsi_sol_delay_burst(struct tegra_dc *dc, u32 dsi_to_pixel_clk_ratio; u32 temp; u32 temp1; - u32 mipi_clk_adj_kHz; + u32 mipi_clk_adj_kHz = 0; u32 sol_delay; struct tegra_dc_mode *dc_modes = &dc->mode; @@ -1450,11 +1471,9 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc, /* Enable DSI clock */ tegra_dc_setup_clk(dc, dsi->dsi_clk); - if (!dsi->clk_ref) { - dsi->clk_ref = true; - clk_enable(dsi->dsi_clk); - tegra_periph_reset_deassert(dsi->dsi_clk); - } + tegra_dsi_clk_enable(dsi); + tegra_periph_reset_deassert(dsi->dsi_clk); + dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000; dsi->current_bit_clk_ns = 1000*1000 / (dsi->current_dsi_clk_khz * 2); } @@ -1619,6 +1638,15 @@ static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi) tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG); } +static void tegra_dsi_panelB_enable(void) +{ + unsigned int val; + + val = readl(IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0)); + val |= DSIB_MODE_ENABLE; + writel(val, (IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0))); +} + static int tegra_dsi_init_hw(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) { @@ -1632,7 +1660,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc, tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_lp_clk_khz); if (dsi->info.dsi_instance) { - /* TODO:Set the misc register*/ + tegra_dsi_panelB_enable(); } /* TODO: only need to change the timing for bta */ @@ -1929,6 +1957,10 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission( if (tegra_dsi_host_busy(dsi)) { tegra_dsi_soft_reset(dsi); + + /* WAR to stop host write in middle */ + tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_TRIGGER); + if (tegra_dsi_host_busy(dsi)) { err = -EBUSY; dev_err(&dc->ndev->dev, "DSI host busy\n"); @@ -2174,6 +2206,9 @@ int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len) int err = 0, count = 0; struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + data_len_orig = data_len; if (pdata != NULL) { while (data_len) { @@ -2240,9 +2275,7 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); #if DSI_USE_SYNC_POINTS - /* FIXME: Workaround for nvhost_syncpt_read */ - dsi->syncpt_val = nvhost_syncpt_update_min( - &nvhost_get_host(dsi->dc->ndev)->syncpt, + dsi->syncpt_val = nvhost_syncpt_read_ext(dsi->dc->ndev, dsi->syncpt_id); val = DSI_INCR_SYNCPT_COND(OP_DONE) | @@ -2250,8 +2283,8 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); /* TODO: Use interrupt rather than polling */ - err = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt, - dsi->syncpt_id, dsi->syncpt_val + 1); + err = nvhost_syncpt_wait_timeout_ext(dsi->dc->ndev, dsi->syncpt_id, + dsi->syncpt_val + 1, MAX_SCHEDULE_TIMEOUT, NULL); if (err < 0) dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n"); @@ -2582,6 +2615,53 @@ fail: } +static void tegra_dsi_send_dc_frames(struct tegra_dc *dc, + struct tegra_dc_dsi_data *dsi, + int no_of_frames) +{ + int err; + u32 frame_period = DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate); + u8 lp_op = dsi->status.lp_op; + bool switch_to_lp = (dsi->status.lphs == DSI_LPHS_IN_LP_MODE); + + if (dsi->status.lphs != DSI_LPHS_IN_HS_MODE) { + err = tegra_dsi_set_to_hs_mode(dc, dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "Switch to HS host mode failed\n"); + return; + } + } + + /* + * Some panels need DC frames be sent under certain + * conditions. We are working on the right fix for this + * requirement, while using this current fix. + */ + tegra_dsi_start_dc_stream(dc, dsi); + + /* + * Send frames in Continuous or One-shot mode. + */ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { + while (no_of_frames--) { + tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG, + DC_CMD_STATE_CONTROL); + mdelay(frame_period); + } + } else + mdelay(no_of_frames * frame_period); + + tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); + + if (switch_to_lp) { + err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op); + if (err < 0) + dev_err(&dc->ndev->dev, + "DSI failed to go to LP mode\n"); + } +} + static void tegra_dc_dsi_enable(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); @@ -2591,6 +2671,8 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) tegra_dc_io_start(dc); mutex_lock(&dsi->lock); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); /* Stop DC stream before configuring DSI registers * to avoid visible glitches on panel during transition * from bootloader to kernel driver @@ -2607,6 +2689,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) } if (dsi->info.panel_reset) { + /* + * Certain panels need dc frames be sent before + * waking panel. + */ + if (dsi->info.panel_send_dc_frames) + tegra_dsi_send_dc_frames(dc, dsi, 2); + err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, dsi->info.n_init_cmd); @@ -2660,6 +2749,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) } } + /* + * Certain panels need dc frames be sent before + * waking panel. + */ + if (dsi->info.panel_send_dc_frames) + tegra_dsi_send_dc_frames(dc, dsi, 2); + err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); if (err < 0) { dev_err(&dc->ndev->dev, @@ -2732,8 +2828,8 @@ static int tegra_dc_dsi_cp_info(struct tegra_dc_dsi_data *dsi, struct tegra_dsi_out *p_dsi) { struct tegra_dsi_cmd *p_init_cmd; - struct tegra_dsi_cmd *p_early_suspend_cmd; - struct tegra_dsi_cmd *p_late_resume_cmd; + struct tegra_dsi_cmd *p_early_suspend_cmd = NULL; + struct tegra_dsi_cmd *p_late_resume_cmd = NULL; struct tegra_dsi_cmd *p_suspend_cmd; int err; @@ -2861,6 +2957,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) void __iomem *base; struct clk *dc_clk = NULL; struct clk *dsi_clk = NULL; + struct clk *dsi_fixed_clk = NULL; struct tegra_dsi_out *dsi_pdata; int err; @@ -2903,8 +3000,9 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) dsi_clk = clk_get(&dc->ndev->dev, "dsib"); else dsi_clk = clk_get(&dc->ndev->dev, "dsia"); + dsi_fixed_clk = clk_get(&dc->ndev->dev, "dsi-fixed"); - if (IS_ERR_OR_NULL(dsi_clk)) { + if (IS_ERR_OR_NULL(dsi_clk) || IS_ERR_OR_NULL(dsi_fixed_clk)) { dev_err(&dc->ndev->dev, "dsi: can't get clock\n"); err = -EBUSY; goto err_release_regs; @@ -2924,6 +3022,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) dsi->base_res = base_res; dsi->dc_clk = dc_clk; dsi->dsi_clk = dsi_clk; + dsi->dsi_fixed_clk = dsi_fixed_clk; err = tegra_dc_dsi_cp_info(dsi, dsi_pdata); if (err < 0) @@ -2937,6 +3036,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) err_dsi_data: err_clk_put: clk_put(dsi_clk); + clk_put(dsi_fixed_clk); err_release_regs: release_resource(base_res); err_free_dsi: @@ -2983,78 +3083,180 @@ static void tegra_dc_dsi_destroy(struct tegra_dc *dc) kfree(dsi); } -static int tegra_dsi_deep_sleep(struct tegra_dc *dc, - struct tegra_dc_dsi_data *dsi) +static void tegra_dsi_config_phy_clk(struct tegra_dc_dsi_data *dsi, + u32 settings) { - int err = 0; - int val; struct clk *parent_clk = NULL; struct clk *base_clk = NULL; + /* Disable dsi fast and slow clock */ + parent_clk = clk_get_parent(dsi->dsi_clk); + base_clk = clk_get_parent(parent_clk); + if (dsi->info.dsi_instance) + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_CSI_OUT_ENB, + settings); + else + tegra_clk_cfg_ex(base_clk, + TEGRA_CLK_PLLD_DSI_OUT_ENB, + settings); +} + +static int tegra_dsi_deep_sleep(struct tegra_dc *dc, + struct tegra_dc_dsi_data *dsi, u32 suspend_aggr) +{ + int val = 0; + int err = 0; + if (!dsi->enabled) { err = -EPERM; goto fail; } - err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); - if (err < 0) { - dev_err(&dc->ndev->dev, - "DSI failed to go to LP mode\n"); - goto fail; - } + switch (suspend_aggr) { + case DSI_SUSPEND_FULL: + /* Suspend DSI panel */ + err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to go to LP mode\n"); + goto fail; + } - /* Suspend panel */ - err = tegra_dsi_send_panel_cmd(dc, dsi, - dsi->info.dsi_suspend_cmd, - dsi->info.n_suspend_cmd); - if (err < 0) { - dev_err(&dc->ndev->dev, - "dsi: Error sending suspend cmd\n"); - goto fail; - } + err = tegra_dsi_send_panel_cmd(dc, dsi, + dsi->info.dsi_suspend_cmd, + dsi->info.n_suspend_cmd); + /* + * Certain panels need dc frames be sent after + * putting panel to sleep. + */ + if (dsi->info.panel_send_dc_frames) + tegra_dsi_send_dc_frames(dc, dsi, 2); - if (!dsi->ulpm) { - err = tegra_dsi_enter_ulpm(dsi); if (err < 0) { dev_err(&dc->ndev->dev, - "DSI failed to enter ulpm\n"); + "dsi: Error sending suspend cmd\n"); goto fail; } + case DSI_HOST_SUSPEND_LV2: + /* Set DSI to ULPM and suspend pads. DSI will be set to the + * lowest power state in this level. */ + if (!dsi->ulpm) { + err = tegra_dsi_enter_ulpm(dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to enter ulpm\n"); + goto fail; + } + } + + val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + + /* Suspend core-logic */ + val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); + tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); + case DSI_HOST_SUSPEND_LV1: + /* Disable dsi fast and slow clock */ + tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_DISABLE); + case DSI_HOST_SUSPEND_LV0: + /* Disable dsi source clock */ + tegra_dsi_clk_disable(dsi); + break; + case DSI_NO_SUSPEND: + break; + default: + dev_err(&dc->ndev->dev, "DSI suspend aggressiveness" + "is not supported.\n"); } - /* - * Suspend pad - * It is ok to overwrite previous value of DSI_PAD_CONTROL reg - * because it will be restored properly in resume sequence - */ - val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | - DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | - DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); - tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + dsi->enabled = false; - /* Suspend core-logic */ - val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); - tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); + return 0; +fail: + return err; +} - /* Disable dsi fast and slow clock */ - parent_clk = clk_get_parent(dsi->dsi_clk); - base_clk = clk_get_parent(parent_clk); - if (dsi->info.dsi_instance) - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_CSI_OUT_ENB, - 0); - else - tegra_clk_cfg_ex(base_clk, - TEGRA_CLK_PLLD_DSI_OUT_ENB, - 0); - /* Disable dsi source clock */ - clk_disable(dsi->dsi_clk); +int tegra_dsi_host_suspend(struct tegra_dc *dc) +{ + int err = 0; + struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); - dsi->clk_ref = false; - dsi->enabled = false; + if (dsi->host_suspended) + return 0; - return 0; + tegra_dsi_stop_dc_stream(dc, dsi); + + err = tegra_dsi_deep_sleep(dc, dsi, dsi->info.suspend_aggr); + if (err < 0) + dev_err(&dc->ndev->dev, + "DSI failed to enter deep sleep\n"); + + dsi->host_suspended = true; + + return err; +} + + +int tegra_dsi_host_resume(struct tegra_dc *dc) +{ + int val = 0; + int err = 0; + struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + + if (!dsi->host_suspended) + return 0; + + switch (dsi->info.suspend_aggr) { + case DSI_HOST_SUSPEND_LV0: + tegra_dsi_clk_enable(dsi); + break; + case DSI_HOST_SUSPEND_LV1: + tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_ENABLE); + tegra_dsi_clk_enable(dsi); + break; + case DSI_HOST_SUSPEND_LV2: + tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_ENABLE); + tegra_dsi_clk_enable(dsi); + + tegra_dsi_writel(dsi, + DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE), + DSI_POWER_CONTROL); + + if (dsi->ulpm) { + err = tegra_dsi_enter_ulpm(dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to enter ulpm\n"); + goto fail; + } + + val = tegra_dsi_readl(dsi, DSI_PAD_CONTROL); + val &= ~(DSI_PAD_CONTROL_PAD_PDIO(0x3) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0x1)); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + + if (tegra_dsi_exit_ulpm(dsi) < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to exit ulpm\n"); + goto fail; + } + } + break; + case DSI_NO_SUSPEND: + break; + default: + dev_err(&dc->ndev->dev, "DSI suspend aggressivenes" + "is not supported.\n"); + } + + dsi->enabled = true; + dsi->host_suspended = false; + tegra_dsi_start_dc_stream(dc, dsi); fail: return err; } @@ -3071,7 +3273,7 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc) tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); if (dsi->info.power_saving_suspend) { - if (tegra_dsi_deep_sleep(dc, dsi) < 0) { + if (tegra_dsi_deep_sleep(dc, dsi, DSI_SUSPEND_FULL) < 0) { dev_err(&dc->ndev->dev, "DSI failed to enter deep sleep\n"); goto fail; @@ -3124,7 +3326,7 @@ static void tegra_dc_dsi_suspend(struct tegra_dc *dc) } } - if (tegra_dsi_deep_sleep(dc, dsi) < 0) { + if (tegra_dsi_deep_sleep(dc, dsi, DSI_SUSPEND_FULL) < 0) { dev_err(&dc->ndev->dev, "DSI failed to enter deep sleep\n"); goto fail; |