summaryrefslogtreecommitdiff
path: root/drivers/video/tegra
diff options
context:
space:
mode:
authorAnimesh Kishore <ankishore@nvidia.com>2012-07-25 17:25:39 +0530
committerSimone Willett <swillett@nvidia.com>2012-07-30 21:03:01 -0700
commit9c74bdcc6e4e0ac478f4766461ed4ed87f8ba07d (patch)
tree38c67d8f712459de86bb9d79ba68605f0531b51c /drivers/video/tegra
parentcfb86bf491e3cf6baad2f164fae4880185f0e22c (diff)
video: tegra: dsi: Fix host HS transmission
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>
Diffstat (limited to 'drivers/video/tegra')
-rw-r--r--drivers/video/tegra/dc/dsi.c239
1 files changed, 115 insertions, 124 deletions
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index 935e98b..4a9e507 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);