video: tegra: dsi: Fix host HS transmission
Animesh Kishore [Wed, 25 Jul 2012 11:55:39 +0000 (16:55 +0530)]
Fixing host transmission with HS enabled.

Bug 999141

Change-Id: I9dcc5282971830865dacf16dbbbebf4096aeb00e
Signed-off-by: Animesh Kishore <ankishore@nvidia.com>
Reviewed-on: http://git-master/r/118315
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

drivers/video/tegra/dc/dsi.c

index 935e98b..4a9e507 100644 (file)
@@ -334,6 +334,7 @@ static int dbg_dsi_show(struct seq_file *s, void *unused)
        DUMP_REG(DSI_CTXSW);
        DUMP_REG(DSI_POWER_CONTROL);
        DUMP_REG(DSI_INT_ENABLE);
+       DUMP_REG(DSI_HOST_DSI_CONTROL);
        DUMP_REG(DSI_CONTROL);
        DUMP_REG(DSI_SOL_DELAY);
        DUMP_REG(DSI_MAX_THRESHOLD);
@@ -1344,6 +1345,31 @@ static void tegra_dsi_reset_underflow_overflow
        }
 }
 
+static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi)
+{
+       u32 trigger;
+
+       tegra_dsi_writel(dsi,
+               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE),
+               DSI_POWER_CONTROL);
+       /* stabilization delay */
+       udelay(300);
+
+       tegra_dsi_writel(dsi,
+               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE),
+               DSI_POWER_CONTROL);
+       /* stabilization delay */
+       udelay(300);
+
+       /* dsi HW does not clear host trigger bit automatically
+        * on dsi interface disable if host fifo is empty or in mid
+        * of host transmission
+        */
+       trigger = tegra_dsi_readl(dsi, DSI_TRIGGER);
+       if (trigger)
+               tegra_dsi_writel(dsi, 0x0, DSI_TRIGGER);
+}
+
 static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc,
                                        struct tegra_dc_dsi_data *dsi)
 {
@@ -1362,13 +1388,13 @@ static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc,
        long timeout;
        u32 frame_period = DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate);
 
-       /* stop dc */
-       tegra_dsi_stop_dc_stream(dc, dsi);
+       INIT_COMPLETION(dc->frame_end_complete);
 
-       /* enable frame end interrupt */
+       /* unmask frame end interrupt */
        val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val |= FRAME_END_INT;
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+       tegra_dc_writel(dc, val | FRAME_END_INT, DC_CMD_INT_MASK);
+
+       tegra_dsi_stop_dc_stream(dc, dsi);
 
        /* wait for frame_end completion.
         * timeout is 2 frame duration to accomodate for
@@ -1378,9 +1404,14 @@ static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc,
                        &dc->frame_end_complete,
                        msecs_to_jiffies(2 * frame_period));
 
-       /* disable frame end interrupt */
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val &= ~FRAME_END_INT;
+       /* give 2 line time to dsi HW to catch up
+        * with pixels sent by dc
+        */
+       udelay(50);
+
+       tegra_dsi_soft_reset(dsi);
+
+       /* reinstate interrupt mask */
        tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
 
        if (timeout == 0)
@@ -1562,7 +1593,8 @@ static void tegra_dsi_set_control_reg_lp(struct tegra_dc_dsi_data *dsi)
        dsi->status.vtype = DSI_VIDEO_TYPE_NOT_INIT;
 }
 
-static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi)
+static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi,
+                                               u8 driven_mode)
 {
        u32 dsi_control;
        u32 host_dsi_control;
@@ -1574,7 +1606,7 @@ static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi)
        max_threshold = 0;
        dcs_cmd = 0;
 
-       if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) {
+       if (driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) {
                dsi_control |= DSI_CTRL_HOST_DRIVEN;
                host_dsi_control |= HOST_DSI_CTRL_HOST_DRIVEN;
                max_threshold =
@@ -1586,17 +1618,19 @@ static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi)
                max_threshold =
                        DSI_MAX_THRESHOLD_MAX_THRESHOLD(DSI_VIDEO_FIFO_DEPTH);
                dsi->status.driven = DSI_DRIVEN_MODE_DC;
-       }
 
-       if (dsi->info.video_data_type == TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) {
-               dsi_control |= DSI_CTRL_CMD_MODE;
-               dcs_cmd = DSI_DCS_CMDS_LT5_DCS_CMD(DSI_WRITE_MEMORY_START)|
-                       DSI_DCS_CMDS_LT3_DCS_CMD(DSI_WRITE_MEMORY_CONTINUE);
-               dsi->status.vtype = DSI_VIDEO_TYPE_CMD_MODE;
-
-       } else {
-               dsi_control |= DSI_CTRL_VIDEO_MODE;
-               dsi->status.vtype = DSI_VIDEO_TYPE_VIDEO_MODE;
+               if (dsi->info.video_data_type ==
+                       TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) {
+                       dsi_control |= DSI_CTRL_CMD_MODE;
+                       dcs_cmd = DSI_DCS_CMDS_LT5_DCS_CMD(
+                               DSI_WRITE_MEMORY_START)|
+                               DSI_DCS_CMDS_LT3_DCS_CMD(
+                               DSI_WRITE_MEMORY_CONTINUE);
+                       dsi->status.vtype = DSI_VIDEO_TYPE_CMD_MODE;
+               } else {
+                       dsi_control |= DSI_CTRL_VIDEO_MODE;
+                       dsi->status.vtype = DSI_VIDEO_TYPE_VIDEO_MODE;
+               }
        }
 
        tegra_dsi_writel(dsi, max_threshold, DSI_MAX_THRESHOLD);
@@ -1737,6 +1771,7 @@ static int tegra_dsi_set_to_lp_mode(struct tegra_dc *dc,
 
        dsi->status.lphs = DSI_LPHS_IN_LP_MODE;
        dsi->status.lp_op = lp_op;
+       dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_HOST;
 success:
        err = 0;
 fail:
@@ -1744,7 +1779,8 @@ fail:
 }
 
 static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
-                                       struct tegra_dc_dsi_data *dsi)
+                                       struct tegra_dc_dsi_data *dsi,
+                                       u8 driven_mode)
 {
        int err;
 
@@ -1753,9 +1789,12 @@ static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
                goto fail;
        }
 
-       if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE)
+       if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE &&
+               dsi->driven_mode == driven_mode)
                goto success;
 
+       dsi->driven_mode = driven_mode;
+
        if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
                tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
 
@@ -1770,14 +1809,14 @@ static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
 
        tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_HS_MODE);
 
-       if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_DC) {
+       if (driven_mode == TEGRA_DSI_DRIVEN_BY_DC) {
                tegra_dsi_set_pkt_seq(dc, dsi);
                tegra_dsi_set_pkt_length(dc, dsi);
                tegra_dsi_set_sol_delay(dc, dsi);
                tegra_dsi_set_dc_clk(dc, dsi);
        }
 
-       tegra_dsi_set_control_reg_hs(dsi);
+       tegra_dsi_set_control_reg_hs(dsi, driven_mode);
 
        if (dsi->status.clk_out == DSI_PHYCLK_OUT_DIS ||
                dsi->info.enable_hs_clock_on_lp_cmd_mode)
@@ -1848,35 +1887,6 @@ fail:
        return (err < 0 ? true : false);
 }
 
-static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi)
-{
-       u32 trigger;
-       u32 status;
-
-       tegra_dsi_writel(dsi,
-               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE),
-               DSI_POWER_CONTROL);
-       /* stabilization delay */
-       udelay(300);
-
-       tegra_dsi_writel(dsi,
-               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE),
-               DSI_POWER_CONTROL);
-       /* stabilization delay */
-       udelay(300);
-
-       /* dsi HW does not clear host trigger bit automatically
-        * on dsi interface disable if host fifo is empty
-        */
-       trigger = tegra_dsi_readl(dsi, DSI_TRIGGER);
-       status = tegra_dsi_readl(dsi, DSI_STATUS);
-       if (trigger & DSI_TRIGGER_HOST_TRIGGER(0x1) &&
-               status & DSI_STATUS_IDLE(0x1)) {
-               trigger &= ~(DSI_TRIGGER_HOST_TRIGGER(0x1));
-               tegra_dsi_writel(dsi, trigger, DSI_TRIGGER);
-       }
-}
-
 static void tegra_dsi_reset_read_count(struct tegra_dc_dsi_data *dsi)
 {
        u32 val;
@@ -1895,49 +1905,42 @@ static struct dsi_status *tegra_dsi_save_state_switch_to_host_cmd_mode(
                                                struct tegra_dc *dc,
                                                u8 lp_op)
 {
-       struct dsi_status *init_status;
+       struct dsi_status *init_status = NULL;
        int err;
 
+       if (dsi->status.init != DSI_MODULE_INIT ||
+               dsi->status.lphs == DSI_LPHS_NOT_INIT) {
+               err = -EPERM;
+               goto fail;
+       }
+
        init_status = kzalloc(sizeof(*init_status), GFP_KERNEL);
        if (!init_status)
                return ERR_PTR(-ENOMEM);
 
        *init_status = dsi->status;
 
-       if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE) {
-               if (dsi->status.driven == DSI_DRIVEN_MODE_DC) {
-                       if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
-                               tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
-                       dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_HOST;
-                       if (dsi->info.hs_cmd_mode_supported) {
-                               err = tegra_dsi_set_to_hs_mode(dc, dsi);
-                               if (err < 0) {
-                                       dev_err(&dc->ndev->dev,
-                                       "Switch to HS host mode failed\n");
-                                       goto fail;
-                               }
-                       }
-               }
-               if (!dsi->info.hs_cmd_mode_supported) {
-                       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");
-                               goto fail;
-                       }
-               }
-       } else if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE) {
-               if (dsi->status.lp_op != lp_op) {
-                       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");
-                               goto fail;
-                       }
+       if (dsi->info.hs_cmd_mode_supported) {
+               err = tegra_dsi_set_to_hs_mode(dc, dsi,
+                               TEGRA_DSI_DRIVEN_BY_HOST);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev,
+                       "Switch to HS host mode failed\n");
+                       goto fail;
                }
+
+               goto success;
        }
 
+       if (dsi->status.lp_op != lp_op) {
+               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");
+                       goto fail;
+               }
+       }
+success:
        return init_status;
 fail:
        kfree(init_status);
@@ -1951,6 +1954,7 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
 {
        int err = 0;
        struct dsi_status *init_status;
+       bool restart_dc_stream = false;
 
        if (dsi->status.init != DSI_MODULE_INIT ||
                dsi->ulpm) {
@@ -1958,12 +1962,13 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
                goto fail;
        }
 
+       if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) {
+               restart_dc_stream = true;
+               tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
+       }
+
        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");
@@ -1990,6 +1995,9 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
                goto fail;
        }
 
+       if (restart_dc_stream)
+               init_status->dc_stream = DSI_DC_STREAM_ENABLE;
+
        return init_status;
 fail:
        return ERR_PTR(err);
@@ -1999,50 +2007,30 @@ static int tegra_dsi_restore_state(struct tegra_dc *dc,
                                struct tegra_dc_dsi_data *dsi,
                                struct dsi_status *init_status)
 {
-       bool switch_back_to_dc_mode = false;
-       bool switch_back_to_hs_mode = false;
-       bool restart_dc_stream;
        int err = 0;
 
-       switch_back_to_dc_mode = (dsi->status.driven ==
-                               DSI_DRIVEN_MODE_HOST &&
-                               init_status->driven ==
-                               DSI_DRIVEN_MODE_DC);
-       switch_back_to_hs_mode = (dsi->status.lphs ==
-                               DSI_LPHS_IN_LP_MODE &&
-                               init_status->lphs ==
-                               DSI_LPHS_IN_HS_MODE);
-       restart_dc_stream = (dsi->status.dc_stream ==
-                               DSI_DC_STREAM_DISABLE &&
-                               init_status->dc_stream ==
-                               DSI_DC_STREAM_ENABLE);
-
-       if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE &&
-               init_status->lphs == DSI_LPHS_IN_LP_MODE) {
-               if (dsi->status.lp_op != init_status->lp_op) {
-                       err =
-                       tegra_dsi_set_to_lp_mode(dc, dsi, init_status->lp_op);
-                       if (err < 0) {
-                               dev_err(&dc->ndev->dev,
-                                       "Failed to config LP mode\n");
-                               goto fail;
-                       }
+       if (init_status->lphs == DSI_LPHS_IN_LP_MODE) {
+               err = tegra_dsi_set_to_lp_mode(dc, dsi, init_status->lp_op);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev,
+                               "Failed to config LP mode\n");
+                       goto fail;
                }
                goto success;
        }
 
-       if (switch_back_to_dc_mode)
-               dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_DC;
-       if (switch_back_to_dc_mode || switch_back_to_hs_mode) {
-               err = tegra_dsi_set_to_hs_mode(dc, dsi);
+       if (init_status->lphs == DSI_LPHS_IN_HS_MODE) {
+               u8 driven = (init_status->driven == DSI_DRIVEN_MODE_DC) ?
+                       TEGRA_DSI_DRIVEN_BY_DC : TEGRA_DSI_DRIVEN_BY_HOST;
+               err = tegra_dsi_set_to_hs_mode(dc, dsi, driven);
                if (err < 0) {
                        dev_err(&dc->ndev->dev, "Failed to config HS mode\n");
                        goto fail;
                }
        }
-       if (restart_dc_stream)
-               tegra_dsi_start_dc_stream(dc, dsi);
 
+       if (init_status->dc_stream == DSI_DC_STREAM_ENABLE)
+               tegra_dsi_start_dc_stream(dc, dsi);
 success:
 fail:
        kfree(init_status);
@@ -2547,6 +2535,7 @@ int tegra_dsi_read_data(struct tegra_dc *dc,
        int err = 0;
        struct dsi_status *init_status;
 
+       mutex_lock(&dsi->lock);
        tegra_dc_io_start(dc);
 
        init_status = tegra_dsi_prepare_host_transmission(
@@ -2605,6 +2594,7 @@ fail:
        if (err < 0)
                dev_err(&dc->ndev->dev, "Failed to restore prev state\n");
        tegra_dc_io_end(dc);
+       mutex_unlock(&dsi->lock);
        return err;
 }
 EXPORT_SYMBOL(tegra_dsi_read_data);
@@ -2743,7 +2733,8 @@ static void tegra_dsi_send_dc_frames(struct tegra_dc *dc,
        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);
+               err = tegra_dsi_set_to_hs_mode(dc, dsi,
+                               TEGRA_DSI_DRIVEN_BY_DC);
                if (err < 0) {
                        dev_err(&dc->ndev->dev,
                                "Switch to HS host mode failed\n");
@@ -2888,7 +2879,8 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
                        goto fail;
                }
 
-               err = tegra_dsi_set_to_hs_mode(dc, dsi);
+               err = tegra_dsi_set_to_hs_mode(dc, dsi,
+                               TEGRA_DSI_DRIVEN_BY_DC);
                if (err < 0) {
                        dev_err(&dc->ndev->dev,
                                "dsi: not able to set to hs mode\n");
@@ -3417,7 +3409,6 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc)
                        }
                }
        }
-
 fail:
        mutex_unlock(&dsi->lock);
        tegra_dc_io_end(dc);