fixup: moduleparam.h/export.h (dsi.c)
[linux-2.6.git] / drivers / video / tegra / dc / dsi.c
index 927a423..d126ecf 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include <linux/export.h>
 
 #include <mach/clk.h>
 #include <mach/dc.h>
@@ -116,6 +118,8 @@ struct tegra_dc_dsi_data {
 
        struct dsi_status status;
 
+       struct dsi_phy_timing_inclk phy_timing;
+
        u8 driven_mode;
        u8 controller_index;
 
@@ -285,12 +289,14 @@ const u32 init_reg[] = {
 
 inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg)
 {
+       BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev));
        return readl(dsi->base + reg * 4);
 }
 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));
        writel(val, dsi->base + reg * 4);
 }
 EXPORT_SYMBOL(tegra_dsi_writel);
@@ -457,6 +463,13 @@ static void tegra_dsi_init_sw(struct tegra_dc *dc,
 
        /* Calculate minimum required pixel rate. */
        pixel_clk_hz = h_width_pixels * v_width_lines * dsi->info.refresh_rate;
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
+               if (dsi->info.rated_refresh_rate >= dsi->info.refresh_rate)
+                       dev_info(&dc->ndev->dev, "DSI: measured refresh rate "
+                               "should be larger than rated refresh rate.\n");
+               dc->mode.rated_pclk = h_width_pixels * v_width_lines *
+                                               dsi->info.rated_refresh_rate;
+       }
 
        /* Calculate minimum byte rate on DSI interface. */
        byte_clk_hz = (pixel_clk_hz * dsi->pixel_scaler_mul) /
@@ -508,63 +521,400 @@ static void tegra_dsi_init_sw(struct tegra_dc *dc,
 
 }
 
+#define SELECT_T_PHY(platform_t_phy_ns, default_phy, clk_ns, hw_inc) ( \
+(platform_t_phy_ns) ? ( \
+((DSI_CONVERT_T_PHY_NS_TO_T_PHY(platform_t_phy_ns, clk_ns, hw_inc)) < 0 ? 0 : \
+(DSI_CONVERT_T_PHY_NS_TO_T_PHY(platform_t_phy_ns, clk_ns, hw_inc)))) : \
+((default_phy) < 0 ? 0 : (default_phy)))
+
+static void tegra_dsi_get_clk_phy_timing(struct tegra_dc_dsi_data *dsi,
+               struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns)
+{
+       phy_timing_clk->t_tlpx = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tlpx_ns,
+               T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC);
+
+       phy_timing_clk->t_clktrail = SELECT_T_PHY(
+               dsi->info.phy_timing.t_clktrail_ns,
+               T_CLKTRAIL_DEFAULT(clk_ns), clk_ns, T_CLKTRAIL_HW_INC);
+
+       phy_timing_clk->t_clkpost = SELECT_T_PHY(
+               dsi->info.phy_timing.t_clkpost_ns,
+               T_CLKPOST_DEFAULT(clk_ns), clk_ns, T_CLKPOST_HW_INC);
+
+       phy_timing_clk->t_clkzero = SELECT_T_PHY(
+               dsi->info.phy_timing.t_clkzero_ns,
+               T_CLKZERO_DEFAULT(clk_ns), clk_ns, T_CLKZERO_HW_INC);
+
+       phy_timing_clk->t_clkprepare = SELECT_T_PHY(
+               dsi->info.phy_timing.t_clkprepare_ns,
+               T_CLKPREPARE_DEFAULT(clk_ns), clk_ns, T_CLKPREPARE_HW_INC);
+
+       phy_timing_clk->t_clkpre = SELECT_T_PHY(
+               dsi->info.phy_timing.t_clkpre_ns,
+               T_CLKPRE_DEFAULT, clk_ns, T_CLKPRE_HW_INC);
+}
+
+static void tegra_dsi_get_hs_phy_timing(struct tegra_dc_dsi_data *dsi,
+               struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns)
+{
+       phy_timing_clk->t_tlpx = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tlpx_ns,
+               T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC);
+
+       phy_timing_clk->t_hsdexit = SELECT_T_PHY(
+               dsi->info.phy_timing.t_hsdexit_ns,
+               T_HSEXIT_DEFAULT(clk_ns), clk_ns, T_HSEXIT_HW_INC);
+
+       phy_timing_clk->t_hstrail = SELECT_T_PHY(
+               dsi->info.phy_timing.t_hstrail_ns,
+               T_HSTRAIL_DEFAULT(clk_ns), clk_ns, T_HSTRAIL_HW_INC);
+
+       phy_timing_clk->t_datzero = SELECT_T_PHY(
+               dsi->info.phy_timing.t_datzero_ns,
+               T_DATZERO_DEFAULT(clk_ns), clk_ns, T_DATZERO_HW_INC);
+
+       phy_timing_clk->t_hsprepare = SELECT_T_PHY(
+               dsi->info.phy_timing.t_hsprepare_ns,
+               T_HSPREPARE_DEFAULT(clk_ns), clk_ns, T_HSPREPARE_HW_INC);
+}
+
+static void tegra_dsi_get_escape_phy_timing(struct tegra_dc_dsi_data *dsi,
+               struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns)
+{
+       phy_timing_clk->t_tlpx = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tlpx_ns,
+               T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC);
+}
+
+static void tegra_dsi_get_bta_phy_timing(struct tegra_dc_dsi_data *dsi,
+               struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns)
+{
+       phy_timing_clk->t_tlpx = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tlpx_ns,
+               T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC);
+
+       phy_timing_clk->t_taget = SELECT_T_PHY(
+               dsi->info.phy_timing.t_taget_ns,
+               T_TAGET_DEFAULT(clk_ns), clk_ns, T_TAGET_HW_INC);
+
+       phy_timing_clk->t_tasure = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tasure_ns,
+               T_TASURE_DEFAULT(clk_ns), clk_ns, T_TASURE_HW_INC);
+
+       phy_timing_clk->t_tago = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tago_ns,
+               T_TAGO_DEFAULT(clk_ns), clk_ns, T_TAGO_HW_INC);
+}
+
+static void tegra_dsi_get_ulps_phy_timing(struct tegra_dc_dsi_data *dsi,
+               struct dsi_phy_timing_inclk *phy_timing_clk, u32 clk_ns)
+{
+       phy_timing_clk->t_tlpx = SELECT_T_PHY(
+               dsi->info.phy_timing.t_tlpx_ns,
+               T_TLPX_DEFAULT(clk_ns), clk_ns, T_TLPX_HW_INC);
+
+       phy_timing_clk->t_wakeup = SELECT_T_PHY(
+               dsi->info.phy_timing.t_wakeup_ns,
+               T_WAKEUP_DEFAULT, clk_ns, T_WAKEUP_HW_INC);
+}
+
+#undef SELECT_T_PHY
+
 static void tegra_dsi_get_phy_timing(struct tegra_dc_dsi_data *dsi,
                                struct dsi_phy_timing_inclk *phy_timing_clk,
-                               u32 clk_ns)
+                               u32 clk_ns, u8 lphs)
+{
+       if (lphs == DSI_LPHS_IN_HS_MODE) {
+               tegra_dsi_get_clk_phy_timing(dsi, phy_timing_clk, clk_ns);
+               tegra_dsi_get_hs_phy_timing(dsi, phy_timing_clk, clk_ns);
+       } else {
+               /* default is LP mode */
+               tegra_dsi_get_escape_phy_timing(dsi, phy_timing_clk, clk_ns);
+               tegra_dsi_get_bta_phy_timing(dsi, phy_timing_clk, clk_ns);
+               tegra_dsi_get_ulps_phy_timing(dsi, phy_timing_clk, clk_ns);
+               if (dsi->info.enable_hs_clock_on_lp_cmd_mode)
+                       tegra_dsi_get_clk_phy_timing
+                               (dsi, phy_timing_clk, clk_ns);
+       }
+}
+
+static int tegra_dsi_mipi_phy_timing_range(struct tegra_dc_dsi_data *dsi,
+                               struct dsi_phy_timing_inclk *phy_timing,
+                               u32 clk_ns, u8 lphs)
 {
+#define CHECK_RANGE(val, min, max) ( \
+               ((min) == NOT_DEFINED ? 0 : (val) < (min)) || \
+               ((max) == NOT_DEFINED ? 0 : (val) > (max)) ? -EINVAL : 0)
+
+       int err = 0;
+
+       err = CHECK_RANGE(
+       DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC),
+                       MIPI_T_TLPX_NS_MIN, MIPI_T_TLPX_NS_MAX);
+       if (err < 0) {
+               dev_warn(&dsi->dc->ndev->dev,
+                       "dsi: Tlpx mipi range violated\n");
+               goto fail;
+       }
+
+       if (lphs == DSI_LPHS_IN_HS_MODE) {
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_hsdexit, clk_ns, T_HSEXIT_HW_INC),
+                       MIPI_T_HSEXIT_NS_MIN, MIPI_T_HSEXIT_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: HsExit mipi range violated\n");
+                       goto fail;
+               }
+
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_hstrail, clk_ns, T_HSTRAIL_HW_INC),
+                       MIPI_T_HSTRAIL_NS_MIN(clk_ns), MIPI_T_HSTRAIL_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: HsTrail mipi range violated\n");
+                       goto fail;
+               }
+
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_datzero, clk_ns, T_DATZERO_HW_INC),
+                       MIPI_T_HSZERO_NS_MIN, MIPI_T_HSZERO_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: HsZero mipi range violated\n");
+                       goto fail;
+               }
+
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_hsprepare, clk_ns, T_HSPREPARE_HW_INC),
+                       MIPI_T_HSPREPARE_NS_MIN(clk_ns),
+                       MIPI_T_HSPREPARE_NS_MAX(clk_ns));
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: HsPrepare mipi range violated\n");
+                       goto fail;
+               }
+
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_hsprepare, clk_ns, T_HSPREPARE_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_datzero, clk_ns, T_DATZERO_HW_INC),
+                       MIPI_T_HSPREPARE_ADD_HSZERO_NS_MIN(clk_ns),
+                       MIPI_T_HSPREPARE_ADD_HSZERO_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                       "dsi: HsPrepare + HsZero mipi range violated\n");
+                       goto fail;
+               }
+       } else {
+               /* default is LP mode */
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_wakeup, clk_ns, T_WAKEUP_HW_INC),
+                       MIPI_T_WAKEUP_NS_MIN, MIPI_T_WAKEUP_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: WakeUp mipi range violated\n");
+                       goto fail;
+               }
+
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_tasure, clk_ns, T_TASURE_HW_INC),
+                       MIPI_T_TASURE_NS_MIN(DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC)),
+                       MIPI_T_TASURE_NS_MAX(DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC)));
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: TaSure mipi range violated\n");
+                       goto fail;
+               }
+       }
+
+       if (lphs == DSI_LPHS_IN_HS_MODE ||
+               dsi->info.enable_hs_clock_on_lp_cmd_mode) {
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_clktrail, clk_ns, T_CLKTRAIL_HW_INC),
+                       MIPI_T_CLKTRAIL_NS_MIN, MIPI_T_CLKTRAIL_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: ClkTrail mipi range violated\n");
+                       goto fail;
+               }
+
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_clkpost, clk_ns, T_CLKPOST_HW_INC),
+                       MIPI_T_CLKPOST_NS_MIN(clk_ns), MIPI_T_CLKPOST_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: ClkPost mipi range violated\n");
+                       goto fail;
+               }
 
-       phy_timing_clk->t_hsdexit = dsi->info.phy_timing.t_hsdexit_ns ?
-                       (dsi->info.phy_timing.t_hsdexit_ns / clk_ns) :
-                       (T_HSEXIT_DEFAULT(clk_ns));
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_clkzero, clk_ns, T_CLKZERO_HW_INC),
+                       MIPI_T_CLKZERO_NS_MIN, MIPI_T_CLKZERO_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: ClkZero mipi range violated\n");
+                       goto fail;
+               }
 
-       phy_timing_clk->t_hstrail = dsi->info.phy_timing.t_hstrail_ns ?
-                       (dsi->info.phy_timing.t_hstrail_ns / clk_ns) :
-                       (T_HSTRAIL_DEFAULT(clk_ns));
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_clkprepare, clk_ns, T_CLKPREPARE_HW_INC),
+                       MIPI_T_CLKPREPARE_NS_MIN, MIPI_T_CLKPREPARE_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: ClkPrepare mipi range violated\n");
+                       goto fail;
+               }
 
-       phy_timing_clk->t_datzero = dsi->info.phy_timing.t_datzero_ns ?
-                       (dsi->info.phy_timing.t_datzero_ns / clk_ns) :
-                       (T_DATZERO_DEFAULT(clk_ns));
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_clkpre, clk_ns, T_CLKPRE_HW_INC),
+                       MIPI_T_CLKPRE_NS_MIN, MIPI_T_CLKPRE_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: ClkPre mipi range violated\n");
+                       goto fail;
+               }
 
-       phy_timing_clk->t_hsprepr = dsi->info.phy_timing.t_hsprepr_ns ?
-                       (dsi->info.phy_timing.t_hsprepr_ns / clk_ns) :
-                       (T_HSPREPR_DEFAULT(clk_ns));
+               err = CHECK_RANGE(
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_clkprepare, clk_ns, T_CLKPREPARE_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+                       phy_timing->t_clkzero, clk_ns, T_CLKZERO_HW_INC),
+                       MIPI_T_CLKPREPARE_ADD_CLKZERO_NS_MIN,
+                       MIPI_T_CLKPREPARE_ADD_CLKZERO_NS_MAX);
+               if (err < 0) {
+                       dev_warn(&dsi->dc->ndev->dev,
+                       "dsi: ClkPrepare + ClkZero mipi range violated\n");
+                       goto fail;
+               }
+       }
+fail:
+#undef CHECK_RANGE
+       return err;
+}
+
+static int tegra_dsi_hs_phy_len(struct tegra_dc_dsi_data *dsi,
+                               struct dsi_phy_timing_inclk *phy_timing,
+                               u32 clk_ns, u8 lphs)
+{
+       u32 hs_t_phy_ns;
+       u32 clk_t_phy_ns;
+       u32 t_phy_ns;
+       u32 h_blank_ns;
+       struct tegra_dc_mode *modes;
+       u32 t_pix_ns;
+       int err = 0;
+
+       if (!(lphs == DSI_LPHS_IN_HS_MODE))
+               goto fail;
 
-       phy_timing_clk->t_clktrail = dsi->info.phy_timing.t_clktrail_ns ?
-                               (dsi->info.phy_timing.t_clktrail_ns / clk_ns) :
-                               (T_CLKTRAIL_DEFAULT(clk_ns));
+       modes = dsi->dc->out->modes;
+       t_pix_ns = clk_ns * BITS_PER_BYTE *
+               dsi->pixel_scaler_mul / dsi->pixel_scaler_div;
+
+       hs_t_phy_ns =
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_hsprepare, clk_ns, T_HSPREPARE_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_datzero, clk_ns, T_DATZERO_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_hstrail, clk_ns, T_HSTRAIL_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_hsdexit, clk_ns, T_HSEXIT_HW_INC);
+
+       clk_t_phy_ns =
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_clkpost, clk_ns, T_CLKPOST_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_clktrail, clk_ns, T_CLKTRAIL_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_hsdexit, clk_ns, T_HSEXIT_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_clkprepare, clk_ns, T_CLKPREPARE_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_clkzero, clk_ns, T_CLKZERO_HW_INC) +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_clkpre, clk_ns, T_CLKPRE_HW_INC);
+
+       h_blank_ns = t_pix_ns * (modes->h_sync_width + modes->h_back_porch +
+                                               modes->h_front_porch);
+
+       /* Extra tlpx and byte cycle required by dsi HW */
+       t_phy_ns = dsi->info.n_data_lanes * (hs_t_phy_ns + clk_t_phy_ns +
+               DSI_CONVERT_T_PHY_TO_T_PHY_NS(
+               phy_timing->t_tlpx, clk_ns, T_TLPX_HW_INC) +
+               clk_ns * BITS_PER_BYTE);
+
+       if (h_blank_ns < t_phy_ns) {
+               err = -EINVAL;
+               dev_err(&dsi->dc->ndev->dev,
+                       "dsi: Hblank is smaller than HS trans phy timing\n");
+               goto fail;
+       }
 
-       phy_timing_clk->t_clkpost = dsi->info.phy_timing.t_clkpost_ns ?
-                               (dsi->info.phy_timing.t_clkpost_ns / clk_ns) :
-                               (T_CLKPOST_DEFAULT(clk_ns));
+       return 0;
+fail:
+       return err;
+}
 
-       phy_timing_clk->t_clkzero = dsi->info.phy_timing.t_clkzero_ns ?
-                               (dsi->info.phy_timing.t_clkzero_ns / clk_ns) :
-                               (T_CLKZERO_DEFAULT(clk_ns));
+static int tegra_dsi_constraint_phy_timing(struct tegra_dc_dsi_data *dsi,
+                               struct dsi_phy_timing_inclk *phy_timing,
+                               u32 clk_ns, u8 lphs)
+{
+       int err = 0;
 
-       phy_timing_clk->t_tlpx = dsi->info.phy_timing.t_tlpx_ns ?
-                               (dsi->info.phy_timing.t_tlpx_ns / clk_ns) :
-                               (T_TLPX_DEFAULT(clk_ns));
+       err = tegra_dsi_mipi_phy_timing_range(dsi, phy_timing, clk_ns, lphs);
+       if (err < 0) {
+               dev_warn(&dsi->dc->ndev->dev, "dsi: mipi range violated\n");
+               goto fail;
+       }
 
-       phy_timing_clk->t_clkpre = T_CLKPRE_DEFAULT(clk_ns);
-       phy_timing_clk->t_clkprepare = T_CLKPREPARE_DEFAULT(clk_ns);
-       phy_timing_clk->t_wakeup = T_WAKEUP_DEFAULT(clk_ns);
+       err = tegra_dsi_hs_phy_len(dsi, phy_timing, clk_ns, lphs);
+       if (err < 0) {
+               dev_err(&dsi->dc->ndev->dev, "dsi: Hblank too short\n");
+               goto fail;
+       }
 
-       phy_timing_clk->t_taget = 5 * phy_timing_clk->t_tlpx;
-       phy_timing_clk->t_tasure = 2 * phy_timing_clk->t_tlpx;
-       phy_timing_clk->t_tago = 4 * phy_timing_clk->t_tlpx;
+       /* TODO: add more contraints */
+fail:
+       return err;
 }
 
-static void tegra_dsi_set_phy_timing(struct tegra_dc_dsi_data *dsi)
+static void tegra_dsi_set_phy_timing(struct tegra_dc_dsi_data *dsi, u8 lphs)
 {
        u32 val;
-       struct dsi_phy_timing_inclk phy_timing;
+       struct dsi_phy_timing_inclk phy_timing = dsi->phy_timing;
 
-       tegra_dsi_get_phy_timing(dsi, &phy_timing, dsi->current_bit_clk_ns);
+       tegra_dsi_get_phy_timing
+               (dsi, &phy_timing, dsi->current_bit_clk_ns, lphs);
+
+       tegra_dsi_constraint_phy_timing(dsi, &phy_timing,
+                                       dsi->current_bit_clk_ns, lphs);
 
        val = DSI_PHY_TIMING_0_THSDEXIT(phy_timing.t_hsdexit) |
                        DSI_PHY_TIMING_0_THSTRAIL(phy_timing.t_hstrail) |
                        DSI_PHY_TIMING_0_TDATZERO(phy_timing.t_datzero) |
-                       DSI_PHY_TIMING_0_THSPREPR(phy_timing.t_hsprepr);
+                       DSI_PHY_TIMING_0_THSPREPR(phy_timing.t_hsprepare);
        tegra_dsi_writel(dsi, val, DSI_PHY_TIMING_0);
 
        val = DSI_PHY_TIMING_1_TCLKTRAIL(phy_timing.t_clktrail) |
@@ -582,6 +932,8 @@ static void tegra_dsi_set_phy_timing(struct tegra_dc_dsi_data *dsi)
                        DSI_BTA_TIMING_TTASURE(phy_timing.t_tasure) |
                        DSI_BTA_TIMING_TTAGO(phy_timing.t_tago);
        tegra_dsi_writel(dsi, val, DSI_BTA_TIMING);
+
+       dsi->phy_timing = phy_timing;
 }
 
 static u32 tegra_dsi_sol_delay_burst(struct tegra_dc *dc,
@@ -641,8 +993,10 @@ static void tegra_dsi_set_sol_delay(struct tegra_dc *dc,
        if (dsi->info.video_burst_mode == TEGRA_DSI_VIDEO_NONE_BURST_MODE ||
                dsi->info.video_burst_mode ==
                                TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END) {
-               sol_delay = NUMOF_BIT_PER_BYTE * dsi->pixel_scaler_mul /
-                       (dsi->pixel_scaler_div * dsi->info.n_data_lanes);
+#define VIDEO_FIFO_LATENCY_PIXEL_CLK 8
+               sol_delay = VIDEO_FIFO_LATENCY_PIXEL_CLK *
+                       dsi->pixel_scaler_mul / dsi->pixel_scaler_div;
+#undef VIDEO_FIFO_LATENCY_PIXEL_CLK
                dsi->status.clk_burst = DSI_CLK_BURST_NONE_BURST;
        } else {
                sol_delay = tegra_dsi_sol_delay_burst(dc, dsi);
@@ -835,6 +1189,27 @@ static void tegra_dsi_set_pkt_seq(struct tegra_dc *dc,
        }
 }
 
+static void tegra_dsi_reset_underflow_overflow
+                               (struct tegra_dc_dsi_data *dsi)
+{
+       u32 val;
+
+       val = tegra_dsi_readl(dsi, DSI_STATUS);
+       val &= (DSI_STATUS_LB_OVERFLOW(0x1) | DSI_STATUS_LB_UNDERFLOW(0x1));
+       if (val) {
+               if (val & DSI_STATUS_LB_OVERFLOW(0x1))
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: video fifo overflow. Resetting flag\n");
+               if (val & DSI_STATUS_LB_UNDERFLOW(0x1))
+                       dev_warn(&dsi->dc->ndev->dev,
+                               "dsi: video fifo underflow. Resetting flag\n");
+               val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL);
+               val |= DSI_HOST_CONTROL_FIFO_STAT_RESET(0x1);
+               tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL);
+               udelay(5);
+       }
+}
+
 static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc,
                                        struct tegra_dc_dsi_data *dsi)
 {
@@ -877,6 +1252,8 @@ static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc,
        if (timeout == 0)
                dev_warn(&dc->ndev->dev,
                        "DC doesn't stop at end of frame.\n");
+
+       tegra_dsi_reset_underflow_overflow(dsi);
 }
 
 static void tegra_dsi_start_dc_stream(struct tegra_dc *dc,
@@ -931,7 +1308,7 @@ static void tegra_dsi_set_dc_clk(struct tegra_dc *dc,
        /* Get the corresponding register value of shift_clk_div. */
        shift_clk_div_register = dsi->shift_clk_div * 2 - 2;
 
-#ifdef CONFIG_TEGRA_FPGA_PLATFORM
+#ifndef CONFIG_TEGRA_SILICON_PLATFORM
        shift_clk_div_register = 1;
 #endif
 
@@ -1127,6 +1504,9 @@ static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi)
 
        val = PAD_DRIV_DN_REF(0x5) | PAD_DRIV_UP_REF(0x7);
        tegra_vi_csi_writel(val, CSI_MIPIBIAS_PAD_CONFIG);
+
+       val = PAD_CIL_PDVREG(0x0);
+       tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG);
 }
 
 static int tegra_dsi_init_hw(struct tegra_dc *dc,
@@ -1146,7 +1526,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc,
        }
 
        /* TODO: only need to change the timing for bta */
-       tegra_dsi_set_phy_timing(dsi);
+       tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_LP_MODE);
 
        if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
                tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
@@ -1206,6 +1586,8 @@ static int tegra_dsi_set_to_lp_mode(struct tegra_dc *dc,
                tegra_dsi_set_timeout(dsi);
        }
 
+       tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_LP_MODE);
+
        tegra_dsi_set_control_reg_lp(dsi);
 
        if ((dsi->status.clk_out == DSI_PHYCLK_OUT_DIS) &&
@@ -1245,7 +1627,7 @@ static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
                tegra_dsi_set_timeout(dsi);
        }
 
-       tegra_dsi_set_phy_timing(dsi);
+       tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_HS_MODE);
 
        if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_DC) {
                tegra_dsi_set_pkt_seq(dc, dsi);
@@ -1325,22 +1707,6 @@ fail:
        return (err < 0 ? true : false);
 }
 
-static void tegra_dsi_reset_underflow_overflow
-                               (struct tegra_dc_dsi_data *dsi)
-{
-       u32 val;
-
-       val = tegra_dsi_readl(dsi, DSI_STATUS);
-       val &= (DSI_STATUS_LB_OVERFLOW(0x1) | DSI_STATUS_LB_UNDERFLOW(0x1));
-       if (val) {
-               dev_warn(&dsi->dc->ndev->dev, "Reset overflow/underflow\n");
-               val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL);
-               val |= DSI_HOST_CONTROL_FIFO_STAT_RESET(0x1);
-               tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL);
-               ndelay(200);
-       }
-}
-
 static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi)
 {
        tegra_dsi_writel(dsi,
@@ -1446,8 +1812,6 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
                }
        }
 
-       tegra_dsi_reset_underflow_overflow(dsi);
-
        if (lp_op == DSI_LP_OP_READ)
                tegra_dsi_reset_read_count(dsi);
 
@@ -2151,10 +2515,19 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
                                        "DSI failed to enter ulpm\n");
                                goto fail;
                        }
-                       val = DSI_PAD_CONTROL_PAD_PDIO(0) |
+
+                       val = tegra_dsi_readl(dsi, DSI_PAD_CONTROL);
+
+                       /* erase bits we're about to set */
+                       val &= ~(DSI_PAD_CONTROL_PAD_PDIO(0x3) |
+                               DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) |
+                               DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0x1));
+
+                       val |= (DSI_PAD_CONTROL_PAD_PDIO(0) |
                                DSI_PAD_CONTROL_PAD_PDIO_CLK(0) |
                                DSI_PAD_CONTROL_PAD_PULLDN_ENAB
-                                               (TEGRA_DSI_DISABLE);
+                                               (TEGRA_DSI_DISABLE));
+
                        tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL);
                        if (tegra_dsi_exit_ulpm(dsi) < 0) {
                                dev_err(&dc->ndev->dev,
@@ -2524,8 +2897,11 @@ static int tegra_dsi_deep_sleep(struct tegra_dc *dc,
                }
        }
 
-       /* Suspend pad */
-       val = tegra_dsi_readl(dsi, DSI_PAD_CONTROL);
+       /*
+        * 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);