From 9c74bdcc6e4e0ac478f4766461ed4ed87f8ba07d Mon Sep 17 00:00:00 2001 From: Animesh Kishore Date: Wed, 25 Jul 2012 17:25:39 +0530 Subject: video: tegra: dsi: Fix host HS transmission Fixing host transmission with HS enabled. Bug 999141 Change-Id: I9dcc5282971830865dacf16dbbbebf4096aeb00e Signed-off-by: Animesh Kishore Reviewed-on: http://git-master/r/118315 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/video/tegra/dc/dsi.c | 239 +++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 124 deletions(-) (limited to 'drivers/video/tegra') diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 935e98bc2f87..4a9e507bcdb9 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -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); -- cgit v1.2.3