diff options
-rw-r--r-- | drivers/video/omap2/dss/dsi.c | 141 |
1 files changed, 123 insertions, 18 deletions
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 4223164f6507..e2ae1e7372f2 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -33,6 +33,7 @@ #include <linux/regulator/consumer.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/sched.h> #include <video/omapdss.h> #include <plat/clock.h> @@ -268,6 +269,7 @@ static struct struct dsi_update_region update_region; bool te_enabled; + bool ulps_enabled; struct workqueue_struct *workqueue; @@ -1925,9 +1927,13 @@ static void dsi_disable_lane_override(void) static int dsi_complexio_init(struct omap_dss_device *dssdev) { int r = 0; + u32 l; DSSDBG("dsi_complexio_init\n"); + if (dsi.ulps_enabled) + DSSDBG("manual ulps exit\n"); + /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ @@ -1941,11 +1947,49 @@ static int dsi_complexio_init(struct omap_dss_device *dssdev) dsi_complexio_config(dssdev); - r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + dsi_if_enable(true); + dsi_if_enable(false); + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + + /* set TX STOP MODE timer to maximum for this operation */ + l = dsi_read_reg(DSI_TIMING1); + l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */ + l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */ + l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */ + dsi_write_reg(DSI_TIMING1, l); + + if (dsi.ulps_enabled) { + /* ULPS is exited by Mark-1 state for 1ms, followed by + * stop state. DSS HW cannot do this via the normal + * ULPS exit sequence, as after reset the DSS HW thinks + * that we are not in ULPS mode, and refuses to send the + * sequence. So we need to send the ULPS exit sequence + * manually. + */ + + dsi_enable_lane_override(dssdev, + DSI_CLK_P | DSI_DATA1_P | DSI_DATA2_P); + } + r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); if (r) goto err; + if (dsi.ulps_enabled) { + /* Keep Mark-1 state for 1ms (as per DSI spec) */ + ktime_t wait = ns_to_ktime(1000 * 1000); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&wait, HRTIMER_MODE_REL); + + /* Disable the override. The lanes should be set to Mark-11 + * state by the HW */ + dsi_disable_lane_override(); + } + + /* FORCE_TX_STOP_MODE_IO */ + REG_FLD_MOD(DSI_TIMING1, 0, 15, 15); + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { DSSERR("ComplexIO not coming out of reset.\n"); r = -ENODEV; @@ -1954,23 +1998,7 @@ static int dsi_complexio_init(struct omap_dss_device *dssdev) dsi_complexio_timings(); - /* - The configuration of the DSI complex I/O (number of data lanes, - position, differential order) should not be changed while - DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the - hardware to recognize a new configuration of the complex I/O (done - in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow - this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next - reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] - LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN - bit to 1. If the sequence is not followed, the DSi complex I/O - configuration is undetermined. - */ - dsi_if_enable(1); - dsi_if_enable(0); - REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ - dsi_if_enable(1); - dsi_if_enable(0); + dsi.ulps_enabled = false; DSSDBG("CIO init done\n"); err: @@ -2793,6 +2821,80 @@ int dsi_vc_set_max_rx_packet_size(int channel, u16 len) } EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); +static int dsi_enter_ulps(void) +{ + DECLARE_COMPLETION_ONSTACK(completion); + int r; + + DSSDBGF(); + + WARN_ON(!dsi_bus_is_locked()); + + WARN_ON(dsi.ulps_enabled); + + if (dsi.ulps_enabled) + return 0; + + if (REG_GET(DSI_CLK_CTRL, 13, 13)) { + DSSERR("DDR_CLK_ALWAYS_ON enabled when entering ULPS\n"); + return -EIO; + } + + dsi_sync_vc(0); + dsi_sync_vc(1); + dsi_sync_vc(2); + dsi_sync_vc(3); + + dsi_force_tx_stop_mode_io(); + + dsi_vc_enable(0, false); + dsi_vc_enable(1, false); + dsi_vc_enable(2, false); + dsi_vc_enable(3, false); + + if (REG_GET(DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */ + DSSERR("HS busy when enabling ULPS\n"); + return -EIO; + } + + if (REG_GET(DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */ + DSSERR("LP busy when enabling ULPS\n"); + return -EIO; + } + + r = dsi_register_isr_cio(dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + if (r) + return r; + + /* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */ + /* LANEx_ULPS_SIG2 */ + REG_FLD_MOD(DSI_COMPLEXIO_CFG2, (1 << 0) | (1 << 1) | (1 << 2), 7, 5); + + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(1000)) == 0) { + DSSERR("ULPS enable timeout\n"); + r = -EIO; + goto err; + } + + dsi_unregister_isr_cio(dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + + dsi_complexio_power(DSI_COMPLEXIO_POWER_ULPS); + + dsi_if_enable(false); + + dsi.ulps_enabled = true; + + return 0; + +err: + dsi_unregister_isr_cio(dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + return r; +} + static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) { unsigned long fck; @@ -3547,6 +3649,9 @@ err0: static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) { + if (!dsi.ulps_enabled) + dsi_enter_ulps(); + /* disable interface */ dsi_if_enable(0); dsi_vc_enable(0, 0); |