xref: /linux/drivers/video/fbdev/omap2/omapfb/dss/dsi.c (revision d34672777da3ea919e8adb0670ab91ddadf7dea0)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f76ee892STomi Valkeinen /*
3f76ee892STomi Valkeinen  * linux/drivers/video/omap2/dss/dsi.c
4f76ee892STomi Valkeinen  *
5f76ee892STomi Valkeinen  * Copyright (C) 2009 Nokia Corporation
6f76ee892STomi Valkeinen  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
7f76ee892STomi Valkeinen  */
8f76ee892STomi Valkeinen 
9f76ee892STomi Valkeinen #define DSS_SUBSYS_NAME "DSI"
10f76ee892STomi Valkeinen 
11f76ee892STomi Valkeinen #include <linux/kernel.h>
12f76ee892STomi Valkeinen #include <linux/io.h>
13f76ee892STomi Valkeinen #include <linux/clk.h>
14f76ee892STomi Valkeinen #include <linux/device.h>
15f76ee892STomi Valkeinen #include <linux/err.h>
16f76ee892STomi Valkeinen #include <linux/interrupt.h>
17f76ee892STomi Valkeinen #include <linux/delay.h>
18f76ee892STomi Valkeinen #include <linux/mutex.h>
19f76ee892STomi Valkeinen #include <linux/module.h>
20f76ee892STomi Valkeinen #include <linux/semaphore.h>
21f76ee892STomi Valkeinen #include <linux/seq_file.h>
22f76ee892STomi Valkeinen #include <linux/platform_device.h>
23f76ee892STomi Valkeinen #include <linux/regulator/consumer.h>
24f76ee892STomi Valkeinen #include <linux/wait.h>
25f76ee892STomi Valkeinen #include <linux/workqueue.h>
26f76ee892STomi Valkeinen #include <linux/sched.h>
27f76ee892STomi Valkeinen #include <linux/slab.h>
28f76ee892STomi Valkeinen #include <linux/debugfs.h>
29f76ee892STomi Valkeinen #include <linux/pm_runtime.h>
30f76ee892STomi Valkeinen #include <linux/of.h>
31*ada5caa4SKuninori Morimoto #include <linux/of_graph.h>
32f76ee892STomi Valkeinen #include <linux/of_platform.h>
33f76ee892STomi Valkeinen #include <linux/component.h>
34f76ee892STomi Valkeinen 
3562d9e44eSPeter Ujfalusi #include <video/omapfb_dss.h>
36f76ee892STomi Valkeinen #include <video/mipi_display.h>
37f76ee892STomi Valkeinen 
38f76ee892STomi Valkeinen #include "dss.h"
39f76ee892STomi Valkeinen #include "dss_features.h"
40f76ee892STomi Valkeinen 
41f76ee892STomi Valkeinen #define DSI_CATCH_MISSING_TE
42f76ee892STomi Valkeinen 
43f76ee892STomi Valkeinen struct dsi_reg { u16 module; u16 idx; };
44f76ee892STomi Valkeinen 
45f76ee892STomi Valkeinen #define DSI_REG(mod, idx)		((const struct dsi_reg) { mod, idx })
46f76ee892STomi Valkeinen 
47f76ee892STomi Valkeinen /* DSI Protocol Engine */
48f76ee892STomi Valkeinen 
49f76ee892STomi Valkeinen #define DSI_PROTO			0
50f76ee892STomi Valkeinen #define DSI_PROTO_SZ			0x200
51f76ee892STomi Valkeinen 
52f76ee892STomi Valkeinen #define DSI_REVISION			DSI_REG(DSI_PROTO, 0x0000)
53f76ee892STomi Valkeinen #define DSI_SYSCONFIG			DSI_REG(DSI_PROTO, 0x0010)
54f76ee892STomi Valkeinen #define DSI_SYSSTATUS			DSI_REG(DSI_PROTO, 0x0014)
55f76ee892STomi Valkeinen #define DSI_IRQSTATUS			DSI_REG(DSI_PROTO, 0x0018)
56f76ee892STomi Valkeinen #define DSI_IRQENABLE			DSI_REG(DSI_PROTO, 0x001C)
57f76ee892STomi Valkeinen #define DSI_CTRL			DSI_REG(DSI_PROTO, 0x0040)
58f76ee892STomi Valkeinen #define DSI_GNQ				DSI_REG(DSI_PROTO, 0x0044)
59f76ee892STomi Valkeinen #define DSI_COMPLEXIO_CFG1		DSI_REG(DSI_PROTO, 0x0048)
60f76ee892STomi Valkeinen #define DSI_COMPLEXIO_IRQ_STATUS	DSI_REG(DSI_PROTO, 0x004C)
61f76ee892STomi Valkeinen #define DSI_COMPLEXIO_IRQ_ENABLE	DSI_REG(DSI_PROTO, 0x0050)
62f76ee892STomi Valkeinen #define DSI_CLK_CTRL			DSI_REG(DSI_PROTO, 0x0054)
63f76ee892STomi Valkeinen #define DSI_TIMING1			DSI_REG(DSI_PROTO, 0x0058)
64f76ee892STomi Valkeinen #define DSI_TIMING2			DSI_REG(DSI_PROTO, 0x005C)
65f76ee892STomi Valkeinen #define DSI_VM_TIMING1			DSI_REG(DSI_PROTO, 0x0060)
66f76ee892STomi Valkeinen #define DSI_VM_TIMING2			DSI_REG(DSI_PROTO, 0x0064)
67f76ee892STomi Valkeinen #define DSI_VM_TIMING3			DSI_REG(DSI_PROTO, 0x0068)
68f76ee892STomi Valkeinen #define DSI_CLK_TIMING			DSI_REG(DSI_PROTO, 0x006C)
69f76ee892STomi Valkeinen #define DSI_TX_FIFO_VC_SIZE		DSI_REG(DSI_PROTO, 0x0070)
70f76ee892STomi Valkeinen #define DSI_RX_FIFO_VC_SIZE		DSI_REG(DSI_PROTO, 0x0074)
71f76ee892STomi Valkeinen #define DSI_COMPLEXIO_CFG2		DSI_REG(DSI_PROTO, 0x0078)
72f76ee892STomi Valkeinen #define DSI_RX_FIFO_VC_FULLNESS		DSI_REG(DSI_PROTO, 0x007C)
73f76ee892STomi Valkeinen #define DSI_VM_TIMING4			DSI_REG(DSI_PROTO, 0x0080)
74f76ee892STomi Valkeinen #define DSI_TX_FIFO_VC_EMPTINESS	DSI_REG(DSI_PROTO, 0x0084)
75f76ee892STomi Valkeinen #define DSI_VM_TIMING5			DSI_REG(DSI_PROTO, 0x0088)
76f76ee892STomi Valkeinen #define DSI_VM_TIMING6			DSI_REG(DSI_PROTO, 0x008C)
77f76ee892STomi Valkeinen #define DSI_VM_TIMING7			DSI_REG(DSI_PROTO, 0x0090)
78f76ee892STomi Valkeinen #define DSI_STOPCLK_TIMING		DSI_REG(DSI_PROTO, 0x0094)
79f76ee892STomi Valkeinen #define DSI_VC_CTRL(n)			DSI_REG(DSI_PROTO, 0x0100 + (n * 0x20))
80f76ee892STomi Valkeinen #define DSI_VC_TE(n)			DSI_REG(DSI_PROTO, 0x0104 + (n * 0x20))
81f76ee892STomi Valkeinen #define DSI_VC_LONG_PACKET_HEADER(n)	DSI_REG(DSI_PROTO, 0x0108 + (n * 0x20))
82f76ee892STomi Valkeinen #define DSI_VC_LONG_PACKET_PAYLOAD(n)	DSI_REG(DSI_PROTO, 0x010C + (n * 0x20))
83f76ee892STomi Valkeinen #define DSI_VC_SHORT_PACKET_HEADER(n)	DSI_REG(DSI_PROTO, 0x0110 + (n * 0x20))
84f76ee892STomi Valkeinen #define DSI_VC_IRQSTATUS(n)		DSI_REG(DSI_PROTO, 0x0118 + (n * 0x20))
85f76ee892STomi Valkeinen #define DSI_VC_IRQENABLE(n)		DSI_REG(DSI_PROTO, 0x011C + (n * 0x20))
86f76ee892STomi Valkeinen 
87f76ee892STomi Valkeinen /* DSIPHY_SCP */
88f76ee892STomi Valkeinen 
89f76ee892STomi Valkeinen #define DSI_PHY				1
90f76ee892STomi Valkeinen #define DSI_PHY_OFFSET			0x200
91f76ee892STomi Valkeinen #define DSI_PHY_SZ			0x40
92f76ee892STomi Valkeinen 
93f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG0			DSI_REG(DSI_PHY, 0x0000)
94f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG1			DSI_REG(DSI_PHY, 0x0004)
95f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG2			DSI_REG(DSI_PHY, 0x0008)
96f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG5			DSI_REG(DSI_PHY, 0x0014)
97f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG10		DSI_REG(DSI_PHY, 0x0028)
98f76ee892STomi Valkeinen 
99f76ee892STomi Valkeinen /* DSI_PLL_CTRL_SCP */
100f76ee892STomi Valkeinen 
101f76ee892STomi Valkeinen #define DSI_PLL				2
102f76ee892STomi Valkeinen #define DSI_PLL_OFFSET			0x300
103f76ee892STomi Valkeinen #define DSI_PLL_SZ			0x20
104f76ee892STomi Valkeinen 
105f76ee892STomi Valkeinen #define DSI_PLL_CONTROL			DSI_REG(DSI_PLL, 0x0000)
106f76ee892STomi Valkeinen #define DSI_PLL_STATUS			DSI_REG(DSI_PLL, 0x0004)
107f76ee892STomi Valkeinen #define DSI_PLL_GO			DSI_REG(DSI_PLL, 0x0008)
108f76ee892STomi Valkeinen #define DSI_PLL_CONFIGURATION1		DSI_REG(DSI_PLL, 0x000C)
109f76ee892STomi Valkeinen #define DSI_PLL_CONFIGURATION2		DSI_REG(DSI_PLL, 0x0010)
110f76ee892STomi Valkeinen 
111f76ee892STomi Valkeinen #define REG_GET(dsidev, idx, start, end) \
112f76ee892STomi Valkeinen 	FLD_GET(dsi_read_reg(dsidev, idx), start, end)
113f76ee892STomi Valkeinen 
114f76ee892STomi Valkeinen #define REG_FLD_MOD(dsidev, idx, val, start, end) \
115f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end))
116f76ee892STomi Valkeinen 
117f76ee892STomi Valkeinen /* Global interrupts */
118f76ee892STomi Valkeinen #define DSI_IRQ_VC0		(1 << 0)
119f76ee892STomi Valkeinen #define DSI_IRQ_VC1		(1 << 1)
120f76ee892STomi Valkeinen #define DSI_IRQ_VC2		(1 << 2)
121f76ee892STomi Valkeinen #define DSI_IRQ_VC3		(1 << 3)
122f76ee892STomi Valkeinen #define DSI_IRQ_WAKEUP		(1 << 4)
123f76ee892STomi Valkeinen #define DSI_IRQ_RESYNC		(1 << 5)
124f76ee892STomi Valkeinen #define DSI_IRQ_PLL_LOCK	(1 << 7)
125f76ee892STomi Valkeinen #define DSI_IRQ_PLL_UNLOCK	(1 << 8)
126f76ee892STomi Valkeinen #define DSI_IRQ_PLL_RECALL	(1 << 9)
127f76ee892STomi Valkeinen #define DSI_IRQ_COMPLEXIO_ERR	(1 << 10)
128f76ee892STomi Valkeinen #define DSI_IRQ_HS_TX_TIMEOUT	(1 << 14)
129f76ee892STomi Valkeinen #define DSI_IRQ_LP_RX_TIMEOUT	(1 << 15)
130f76ee892STomi Valkeinen #define DSI_IRQ_TE_TRIGGER	(1 << 16)
131f76ee892STomi Valkeinen #define DSI_IRQ_ACK_TRIGGER	(1 << 17)
132f76ee892STomi Valkeinen #define DSI_IRQ_SYNC_LOST	(1 << 18)
133f76ee892STomi Valkeinen #define DSI_IRQ_LDO_POWER_GOOD	(1 << 19)
134f76ee892STomi Valkeinen #define DSI_IRQ_TA_TIMEOUT	(1 << 20)
135f76ee892STomi Valkeinen #define DSI_IRQ_ERROR_MASK \
136f76ee892STomi Valkeinen 	(DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
137f76ee892STomi Valkeinen 	DSI_IRQ_TA_TIMEOUT)
138f76ee892STomi Valkeinen #define DSI_IRQ_CHANNEL_MASK	0xf
139f76ee892STomi Valkeinen 
140f76ee892STomi Valkeinen /* Virtual channel interrupts */
141f76ee892STomi Valkeinen #define DSI_VC_IRQ_CS		(1 << 0)
142f76ee892STomi Valkeinen #define DSI_VC_IRQ_ECC_CORR	(1 << 1)
143f76ee892STomi Valkeinen #define DSI_VC_IRQ_PACKET_SENT	(1 << 2)
144f76ee892STomi Valkeinen #define DSI_VC_IRQ_FIFO_TX_OVF	(1 << 3)
145f76ee892STomi Valkeinen #define DSI_VC_IRQ_FIFO_RX_OVF	(1 << 4)
146f76ee892STomi Valkeinen #define DSI_VC_IRQ_BTA		(1 << 5)
147f76ee892STomi Valkeinen #define DSI_VC_IRQ_ECC_NO_CORR	(1 << 6)
148f76ee892STomi Valkeinen #define DSI_VC_IRQ_FIFO_TX_UDF	(1 << 7)
149f76ee892STomi Valkeinen #define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
150f76ee892STomi Valkeinen #define DSI_VC_IRQ_ERROR_MASK \
151f76ee892STomi Valkeinen 	(DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
152f76ee892STomi Valkeinen 	DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
153f76ee892STomi Valkeinen 	DSI_VC_IRQ_FIFO_TX_UDF)
154f76ee892STomi Valkeinen 
155f76ee892STomi Valkeinen /* ComplexIO interrupts */
156f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC1		(1 << 0)
157f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC2		(1 << 1)
158f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC3		(1 << 2)
159f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC4		(1 << 3)
160f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC5		(1 << 4)
161f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC1		(1 << 5)
162f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC2		(1 << 6)
163f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC3		(1 << 7)
164f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC4		(1 << 8)
165f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC5		(1 << 9)
166f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL1		(1 << 10)
167f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL2		(1 << 11)
168f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL3		(1 << 12)
169f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL4		(1 << 13)
170f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL5		(1 << 14)
171f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS1		(1 << 15)
172f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS2		(1 << 16)
173f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS3		(1 << 17)
174f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS4		(1 << 18)
175f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS5		(1 << 19)
176f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_1	(1 << 20)
177f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_1	(1 << 21)
178f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_2	(1 << 22)
179f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_2	(1 << 23)
180f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_3	(1 << 24)
181f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3	(1 << 25)
182f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_4	(1 << 26)
183f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_4	(1 << 27)
184f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_5	(1 << 28)
185f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_5	(1 << 29)
186f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0	(1 << 30)
187f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1	(1 << 31)
188f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERROR_MASK \
189f76ee892STomi Valkeinen 	(DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \
190f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \
191f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRSYNCESC5 | \
192f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \
193f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \
194f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRESC5 | \
195f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \
196f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \
197f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTROL5 | \
198f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \
199f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \
200f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \
201f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \
202f76ee892STomi Valkeinen 	 DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5)
203f76ee892STomi Valkeinen 
204f76ee892STomi Valkeinen typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
205f76ee892STomi Valkeinen 
206f76ee892STomi Valkeinen static int dsi_display_init_dispc(struct platform_device *dsidev,
207f76ee892STomi Valkeinen 	struct omap_overlay_manager *mgr);
208f76ee892STomi Valkeinen static void dsi_display_uninit_dispc(struct platform_device *dsidev,
209f76ee892STomi Valkeinen 	struct omap_overlay_manager *mgr);
210f76ee892STomi Valkeinen 
211f76ee892STomi Valkeinen static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel);
212f76ee892STomi Valkeinen 
213f76ee892STomi Valkeinen /* DSI PLL HSDIV indices */
214f76ee892STomi Valkeinen #define HSDIV_DISPC	0
215f76ee892STomi Valkeinen #define HSDIV_DSI	1
216f76ee892STomi Valkeinen 
217f76ee892STomi Valkeinen #define DSI_MAX_NR_ISRS                2
218f76ee892STomi Valkeinen #define DSI_MAX_NR_LANES	5
219f76ee892STomi Valkeinen 
220f76ee892STomi Valkeinen enum dsi_lane_function {
221f76ee892STomi Valkeinen 	DSI_LANE_UNUSED	= 0,
222f76ee892STomi Valkeinen 	DSI_LANE_CLK,
223f76ee892STomi Valkeinen 	DSI_LANE_DATA1,
224f76ee892STomi Valkeinen 	DSI_LANE_DATA2,
225f76ee892STomi Valkeinen 	DSI_LANE_DATA3,
226f76ee892STomi Valkeinen 	DSI_LANE_DATA4,
227f76ee892STomi Valkeinen };
228f76ee892STomi Valkeinen 
229f76ee892STomi Valkeinen struct dsi_lane_config {
230f76ee892STomi Valkeinen 	enum dsi_lane_function function;
231f76ee892STomi Valkeinen 	u8 polarity;
232f76ee892STomi Valkeinen };
233f76ee892STomi Valkeinen 
234f76ee892STomi Valkeinen struct dsi_isr_data {
235f76ee892STomi Valkeinen 	omap_dsi_isr_t	isr;
236f76ee892STomi Valkeinen 	void		*arg;
237f76ee892STomi Valkeinen 	u32		mask;
238f76ee892STomi Valkeinen };
239f76ee892STomi Valkeinen 
240f76ee892STomi Valkeinen enum fifo_size {
241f76ee892STomi Valkeinen 	DSI_FIFO_SIZE_0		= 0,
242f76ee892STomi Valkeinen 	DSI_FIFO_SIZE_32	= 1,
243f76ee892STomi Valkeinen 	DSI_FIFO_SIZE_64	= 2,
244f76ee892STomi Valkeinen 	DSI_FIFO_SIZE_96	= 3,
245f76ee892STomi Valkeinen 	DSI_FIFO_SIZE_128	= 4,
246f76ee892STomi Valkeinen };
247f76ee892STomi Valkeinen 
248f76ee892STomi Valkeinen enum dsi_vc_source {
249f76ee892STomi Valkeinen 	DSI_VC_SOURCE_L4 = 0,
250f76ee892STomi Valkeinen 	DSI_VC_SOURCE_VP,
251f76ee892STomi Valkeinen };
252f76ee892STomi Valkeinen 
253f76ee892STomi Valkeinen struct dsi_irq_stats {
254f76ee892STomi Valkeinen 	unsigned long last_reset;
255f76ee892STomi Valkeinen 	unsigned irq_count;
256f76ee892STomi Valkeinen 	unsigned dsi_irqs[32];
257f76ee892STomi Valkeinen 	unsigned vc_irqs[4][32];
258f76ee892STomi Valkeinen 	unsigned cio_irqs[32];
259f76ee892STomi Valkeinen };
260f76ee892STomi Valkeinen 
261f76ee892STomi Valkeinen struct dsi_isr_tables {
262f76ee892STomi Valkeinen 	struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS];
263f76ee892STomi Valkeinen 	struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS];
264f76ee892STomi Valkeinen 	struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS];
265f76ee892STomi Valkeinen };
266f76ee892STomi Valkeinen 
267f76ee892STomi Valkeinen struct dsi_clk_calc_ctx {
268f76ee892STomi Valkeinen 	struct platform_device *dsidev;
269f76ee892STomi Valkeinen 	struct dss_pll *pll;
270f76ee892STomi Valkeinen 
271f76ee892STomi Valkeinen 	/* inputs */
272f76ee892STomi Valkeinen 
273f76ee892STomi Valkeinen 	const struct omap_dss_dsi_config *config;
274f76ee892STomi Valkeinen 
275f76ee892STomi Valkeinen 	unsigned long req_pck_min, req_pck_nom, req_pck_max;
276f76ee892STomi Valkeinen 
277f76ee892STomi Valkeinen 	/* outputs */
278f76ee892STomi Valkeinen 
279f76ee892STomi Valkeinen 	struct dss_pll_clock_info dsi_cinfo;
280f76ee892STomi Valkeinen 	struct dispc_clock_info dispc_cinfo;
281f76ee892STomi Valkeinen 
282f76ee892STomi Valkeinen 	struct omap_video_timings dispc_vm;
283f76ee892STomi Valkeinen 	struct omap_dss_dsi_videomode_timings dsi_vm;
284f76ee892STomi Valkeinen };
285f76ee892STomi Valkeinen 
286f76ee892STomi Valkeinen struct dsi_lp_clock_info {
287f76ee892STomi Valkeinen 	unsigned long lp_clk;
288f76ee892STomi Valkeinen 	u16 lp_clk_div;
289f76ee892STomi Valkeinen };
290f76ee892STomi Valkeinen 
291f76ee892STomi Valkeinen struct dsi_data {
292f76ee892STomi Valkeinen 	struct platform_device *pdev;
293f76ee892STomi Valkeinen 	void __iomem *proto_base;
294f76ee892STomi Valkeinen 	void __iomem *phy_base;
295f76ee892STomi Valkeinen 	void __iomem *pll_base;
296f76ee892STomi Valkeinen 
297f76ee892STomi Valkeinen 	int module_id;
298f76ee892STomi Valkeinen 
299f76ee892STomi Valkeinen 	int irq;
300f76ee892STomi Valkeinen 
301f76ee892STomi Valkeinen 	bool is_enabled;
302f76ee892STomi Valkeinen 
303f76ee892STomi Valkeinen 	struct clk *dss_clk;
304f76ee892STomi Valkeinen 
305f76ee892STomi Valkeinen 	struct dispc_clock_info user_dispc_cinfo;
306f76ee892STomi Valkeinen 	struct dss_pll_clock_info user_dsi_cinfo;
307f76ee892STomi Valkeinen 
308f76ee892STomi Valkeinen 	struct dsi_lp_clock_info user_lp_cinfo;
309f76ee892STomi Valkeinen 	struct dsi_lp_clock_info current_lp_cinfo;
310f76ee892STomi Valkeinen 
311f76ee892STomi Valkeinen 	struct dss_pll pll;
312f76ee892STomi Valkeinen 
313f76ee892STomi Valkeinen 	bool vdds_dsi_enabled;
314f76ee892STomi Valkeinen 	struct regulator *vdds_dsi_reg;
315f76ee892STomi Valkeinen 
316f76ee892STomi Valkeinen 	struct {
317f76ee892STomi Valkeinen 		enum dsi_vc_source source;
318f76ee892STomi Valkeinen 		struct omap_dss_device *dssdev;
319f76ee892STomi Valkeinen 		enum fifo_size tx_fifo_size;
320f76ee892STomi Valkeinen 		enum fifo_size rx_fifo_size;
321f76ee892STomi Valkeinen 		int vc_id;
322f76ee892STomi Valkeinen 	} vc[4];
323f76ee892STomi Valkeinen 
324f76ee892STomi Valkeinen 	struct mutex lock;
325f76ee892STomi Valkeinen 	struct semaphore bus_lock;
326f76ee892STomi Valkeinen 
327f76ee892STomi Valkeinen 	spinlock_t irq_lock;
328f76ee892STomi Valkeinen 	struct dsi_isr_tables isr_tables;
329f76ee892STomi Valkeinen 	/* space for a copy used by the interrupt handler */
330f76ee892STomi Valkeinen 	struct dsi_isr_tables isr_tables_copy;
331f76ee892STomi Valkeinen 
332f76ee892STomi Valkeinen 	int update_channel;
333f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE
334f76ee892STomi Valkeinen 	unsigned update_bytes;
335f76ee892STomi Valkeinen #endif
336f76ee892STomi Valkeinen 
337f76ee892STomi Valkeinen 	bool te_enabled;
338f76ee892STomi Valkeinen 	bool ulps_enabled;
339f76ee892STomi Valkeinen 
340f76ee892STomi Valkeinen 	void (*framedone_callback)(int, void *);
341f76ee892STomi Valkeinen 	void *framedone_data;
342f76ee892STomi Valkeinen 
343f76ee892STomi Valkeinen 	struct delayed_work framedone_timeout_work;
344f76ee892STomi Valkeinen 
345f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
346f76ee892STomi Valkeinen 	struct timer_list te_timer;
347f76ee892STomi Valkeinen #endif
348f76ee892STomi Valkeinen 
349f76ee892STomi Valkeinen 	unsigned long cache_req_pck;
350f76ee892STomi Valkeinen 	unsigned long cache_clk_freq;
351f76ee892STomi Valkeinen 	struct dss_pll_clock_info cache_cinfo;
352f76ee892STomi Valkeinen 
353f76ee892STomi Valkeinen 	u32		errors;
354f76ee892STomi Valkeinen 	spinlock_t	errors_lock;
355f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE
356f76ee892STomi Valkeinen 	ktime_t perf_setup_time;
357f76ee892STomi Valkeinen 	ktime_t perf_start_time;
358f76ee892STomi Valkeinen #endif
359f76ee892STomi Valkeinen 	int debug_read;
360f76ee892STomi Valkeinen 	int debug_write;
361f76ee892STomi Valkeinen 
36235b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
363f76ee892STomi Valkeinen 	spinlock_t irq_stats_lock;
364f76ee892STomi Valkeinen 	struct dsi_irq_stats irq_stats;
365f76ee892STomi Valkeinen #endif
366f76ee892STomi Valkeinen 
367f76ee892STomi Valkeinen 	unsigned num_lanes_supported;
368f76ee892STomi Valkeinen 	unsigned line_buffer_size;
369f76ee892STomi Valkeinen 
370f76ee892STomi Valkeinen 	struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
371f76ee892STomi Valkeinen 	unsigned num_lanes_used;
372f76ee892STomi Valkeinen 
373f76ee892STomi Valkeinen 	unsigned scp_clk_refcount;
374f76ee892STomi Valkeinen 
375f76ee892STomi Valkeinen 	struct dss_lcd_mgr_config mgr_config;
376f76ee892STomi Valkeinen 	struct omap_video_timings timings;
377f76ee892STomi Valkeinen 	enum omap_dss_dsi_pixel_format pix_fmt;
378f76ee892STomi Valkeinen 	enum omap_dss_dsi_mode mode;
379f76ee892STomi Valkeinen 	struct omap_dss_dsi_videomode_timings vm_timings;
380f76ee892STomi Valkeinen 
381f76ee892STomi Valkeinen 	struct omap_dss_device output;
382f76ee892STomi Valkeinen };
383f76ee892STomi Valkeinen 
384f76ee892STomi Valkeinen struct dsi_packet_sent_handler_data {
385f76ee892STomi Valkeinen 	struct platform_device *dsidev;
386f76ee892STomi Valkeinen 	struct completion *completion;
387f76ee892STomi Valkeinen };
388f76ee892STomi Valkeinen 
389f76ee892STomi Valkeinen struct dsi_module_id_data {
390f76ee892STomi Valkeinen 	u32 address;
391f76ee892STomi Valkeinen 	int id;
392f76ee892STomi Valkeinen };
393f76ee892STomi Valkeinen 
394f76ee892STomi Valkeinen static const struct of_device_id dsi_of_match[];
395f76ee892STomi Valkeinen 
396f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE
397f76ee892STomi Valkeinen static bool dsi_perf;
398f76ee892STomi Valkeinen module_param(dsi_perf, bool, 0644);
399f76ee892STomi Valkeinen #endif
400f76ee892STomi Valkeinen 
dsi_get_dsidrv_data(struct platform_device * dsidev)401f76ee892STomi Valkeinen static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev)
402f76ee892STomi Valkeinen {
4030b5e0f45SJulia Lawall 	return platform_get_drvdata(dsidev);
404f76ee892STomi Valkeinen }
405f76ee892STomi Valkeinen 
dsi_get_dsidev_from_dssdev(struct omap_dss_device * dssdev)406f76ee892STomi Valkeinen static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev)
407f76ee892STomi Valkeinen {
408f76ee892STomi Valkeinen 	return to_platform_device(dssdev->dev);
409f76ee892STomi Valkeinen }
410f76ee892STomi Valkeinen 
dsi_get_dsidev_from_id(int module)411f76ee892STomi Valkeinen static struct platform_device *dsi_get_dsidev_from_id(int module)
412f76ee892STomi Valkeinen {
413f76ee892STomi Valkeinen 	struct omap_dss_device *out;
414f76ee892STomi Valkeinen 	enum omap_dss_output_id	id;
415f76ee892STomi Valkeinen 
416f76ee892STomi Valkeinen 	switch (module) {
417f76ee892STomi Valkeinen 	case 0:
418f76ee892STomi Valkeinen 		id = OMAP_DSS_OUTPUT_DSI1;
419f76ee892STomi Valkeinen 		break;
420f76ee892STomi Valkeinen 	case 1:
421f76ee892STomi Valkeinen 		id = OMAP_DSS_OUTPUT_DSI2;
422f76ee892STomi Valkeinen 		break;
423f76ee892STomi Valkeinen 	default:
424f76ee892STomi Valkeinen 		return NULL;
425f76ee892STomi Valkeinen 	}
426f76ee892STomi Valkeinen 
427f76ee892STomi Valkeinen 	out = omap_dss_get_output(id);
428f76ee892STomi Valkeinen 
429f76ee892STomi Valkeinen 	return out ? to_platform_device(out->dev) : NULL;
430f76ee892STomi Valkeinen }
431f76ee892STomi Valkeinen 
dsi_write_reg(struct platform_device * dsidev,const struct dsi_reg idx,u32 val)432f76ee892STomi Valkeinen static inline void dsi_write_reg(struct platform_device *dsidev,
433f76ee892STomi Valkeinen 		const struct dsi_reg idx, u32 val)
434f76ee892STomi Valkeinen {
435f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
436f76ee892STomi Valkeinen 	void __iomem *base;
437f76ee892STomi Valkeinen 
438f76ee892STomi Valkeinen 	switch(idx.module) {
439f76ee892STomi Valkeinen 		case DSI_PROTO: base = dsi->proto_base; break;
440f76ee892STomi Valkeinen 		case DSI_PHY: base = dsi->phy_base; break;
441f76ee892STomi Valkeinen 		case DSI_PLL: base = dsi->pll_base; break;
442f76ee892STomi Valkeinen 		default: return;
443f76ee892STomi Valkeinen 	}
444f76ee892STomi Valkeinen 
445f76ee892STomi Valkeinen 	__raw_writel(val, base + idx.idx);
446f76ee892STomi Valkeinen }
447f76ee892STomi Valkeinen 
dsi_read_reg(struct platform_device * dsidev,const struct dsi_reg idx)448f76ee892STomi Valkeinen static inline u32 dsi_read_reg(struct platform_device *dsidev,
449f76ee892STomi Valkeinen 		const struct dsi_reg idx)
450f76ee892STomi Valkeinen {
451f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
452f76ee892STomi Valkeinen 	void __iomem *base;
453f76ee892STomi Valkeinen 
454f76ee892STomi Valkeinen 	switch(idx.module) {
455f76ee892STomi Valkeinen 		case DSI_PROTO: base = dsi->proto_base; break;
456f76ee892STomi Valkeinen 		case DSI_PHY: base = dsi->phy_base; break;
457f76ee892STomi Valkeinen 		case DSI_PLL: base = dsi->pll_base; break;
458f76ee892STomi Valkeinen 		default: return 0;
459f76ee892STomi Valkeinen 	}
460f76ee892STomi Valkeinen 
461f76ee892STomi Valkeinen 	return __raw_readl(base + idx.idx);
462f76ee892STomi Valkeinen }
463f76ee892STomi Valkeinen 
dsi_bus_lock(struct omap_dss_device * dssdev)464f76ee892STomi Valkeinen static void dsi_bus_lock(struct omap_dss_device *dssdev)
465f76ee892STomi Valkeinen {
466f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
467f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
468f76ee892STomi Valkeinen 
469f76ee892STomi Valkeinen 	down(&dsi->bus_lock);
470f76ee892STomi Valkeinen }
471f76ee892STomi Valkeinen 
dsi_bus_unlock(struct omap_dss_device * dssdev)472f76ee892STomi Valkeinen static void dsi_bus_unlock(struct omap_dss_device *dssdev)
473f76ee892STomi Valkeinen {
474f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
475f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
476f76ee892STomi Valkeinen 
477f76ee892STomi Valkeinen 	up(&dsi->bus_lock);
478f76ee892STomi Valkeinen }
479f76ee892STomi Valkeinen 
dsi_bus_is_locked(struct platform_device * dsidev)480f76ee892STomi Valkeinen static bool dsi_bus_is_locked(struct platform_device *dsidev)
481f76ee892STomi Valkeinen {
482f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
483f76ee892STomi Valkeinen 
484f76ee892STomi Valkeinen 	return dsi->bus_lock.count == 0;
485f76ee892STomi Valkeinen }
486f76ee892STomi Valkeinen 
dsi_completion_handler(void * data,u32 mask)487f76ee892STomi Valkeinen static void dsi_completion_handler(void *data, u32 mask)
488f76ee892STomi Valkeinen {
489f76ee892STomi Valkeinen 	complete((struct completion *)data);
490f76ee892STomi Valkeinen }
491f76ee892STomi Valkeinen 
wait_for_bit_change(struct platform_device * dsidev,const struct dsi_reg idx,int bitnum,int value)492f76ee892STomi Valkeinen static inline int wait_for_bit_change(struct platform_device *dsidev,
493f76ee892STomi Valkeinen 		const struct dsi_reg idx, int bitnum, int value)
494f76ee892STomi Valkeinen {
495f76ee892STomi Valkeinen 	unsigned long timeout;
496f76ee892STomi Valkeinen 	ktime_t wait;
497f76ee892STomi Valkeinen 	int t;
498f76ee892STomi Valkeinen 
499f76ee892STomi Valkeinen 	/* first busyloop to see if the bit changes right away */
500f76ee892STomi Valkeinen 	t = 100;
501f76ee892STomi Valkeinen 	while (t-- > 0) {
502f76ee892STomi Valkeinen 		if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
503f76ee892STomi Valkeinen 			return value;
504f76ee892STomi Valkeinen 	}
505f76ee892STomi Valkeinen 
506f76ee892STomi Valkeinen 	/* then loop for 500ms, sleeping for 1ms in between */
507f76ee892STomi Valkeinen 	timeout = jiffies + msecs_to_jiffies(500);
508f76ee892STomi Valkeinen 	while (time_before(jiffies, timeout)) {
509f76ee892STomi Valkeinen 		if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
510f76ee892STomi Valkeinen 			return value;
511f76ee892STomi Valkeinen 
512f76ee892STomi Valkeinen 		wait = ns_to_ktime(1000 * 1000);
513f76ee892STomi Valkeinen 		set_current_state(TASK_UNINTERRUPTIBLE);
514f76ee892STomi Valkeinen 		schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
515f76ee892STomi Valkeinen 	}
516f76ee892STomi Valkeinen 
517f76ee892STomi Valkeinen 	return !value;
518f76ee892STomi Valkeinen }
519f76ee892STomi Valkeinen 
dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)520f76ee892STomi Valkeinen u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
521f76ee892STomi Valkeinen {
522f76ee892STomi Valkeinen 	switch (fmt) {
523f76ee892STomi Valkeinen 	case OMAP_DSS_DSI_FMT_RGB888:
524f76ee892STomi Valkeinen 	case OMAP_DSS_DSI_FMT_RGB666:
525f76ee892STomi Valkeinen 		return 24;
526f76ee892STomi Valkeinen 	case OMAP_DSS_DSI_FMT_RGB666_PACKED:
527f76ee892STomi Valkeinen 		return 18;
528f76ee892STomi Valkeinen 	case OMAP_DSS_DSI_FMT_RGB565:
529f76ee892STomi Valkeinen 		return 16;
530f76ee892STomi Valkeinen 	default:
531f76ee892STomi Valkeinen 		BUG();
532f76ee892STomi Valkeinen 		return 0;
533f76ee892STomi Valkeinen 	}
534f76ee892STomi Valkeinen }
535f76ee892STomi Valkeinen 
536f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE
dsi_perf_mark_setup(struct platform_device * dsidev)537f76ee892STomi Valkeinen static void dsi_perf_mark_setup(struct platform_device *dsidev)
538f76ee892STomi Valkeinen {
539f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
540f76ee892STomi Valkeinen 	dsi->perf_setup_time = ktime_get();
541f76ee892STomi Valkeinen }
542f76ee892STomi Valkeinen 
dsi_perf_mark_start(struct platform_device * dsidev)543f76ee892STomi Valkeinen static void dsi_perf_mark_start(struct platform_device *dsidev)
544f76ee892STomi Valkeinen {
545f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
546f76ee892STomi Valkeinen 	dsi->perf_start_time = ktime_get();
547f76ee892STomi Valkeinen }
548f76ee892STomi Valkeinen 
dsi_perf_show(struct platform_device * dsidev,const char * name)549f76ee892STomi Valkeinen static void dsi_perf_show(struct platform_device *dsidev, const char *name)
550f76ee892STomi Valkeinen {
551f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
552f76ee892STomi Valkeinen 	ktime_t t, setup_time, trans_time;
553f76ee892STomi Valkeinen 	u32 total_bytes;
554f76ee892STomi Valkeinen 	u32 setup_us, trans_us, total_us;
555f76ee892STomi Valkeinen 
556f76ee892STomi Valkeinen 	if (!dsi_perf)
557f76ee892STomi Valkeinen 		return;
558f76ee892STomi Valkeinen 
559f76ee892STomi Valkeinen 	t = ktime_get();
560f76ee892STomi Valkeinen 
561f76ee892STomi Valkeinen 	setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time);
562f76ee892STomi Valkeinen 	setup_us = (u32)ktime_to_us(setup_time);
563f76ee892STomi Valkeinen 	if (setup_us == 0)
564f76ee892STomi Valkeinen 		setup_us = 1;
565f76ee892STomi Valkeinen 
566f76ee892STomi Valkeinen 	trans_time = ktime_sub(t, dsi->perf_start_time);
567f76ee892STomi Valkeinen 	trans_us = (u32)ktime_to_us(trans_time);
568f76ee892STomi Valkeinen 	if (trans_us == 0)
569f76ee892STomi Valkeinen 		trans_us = 1;
570f76ee892STomi Valkeinen 
571f76ee892STomi Valkeinen 	total_us = setup_us + trans_us;
572f76ee892STomi Valkeinen 
573f76ee892STomi Valkeinen 	total_bytes = dsi->update_bytes;
574f76ee892STomi Valkeinen 
575f76ee892STomi Valkeinen 	printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
576f76ee892STomi Valkeinen 			"%u bytes, %u kbytes/sec\n",
577f76ee892STomi Valkeinen 			name,
578f76ee892STomi Valkeinen 			setup_us,
579f76ee892STomi Valkeinen 			trans_us,
580f76ee892STomi Valkeinen 			total_us,
581f76ee892STomi Valkeinen 			1000*1000 / total_us,
582f76ee892STomi Valkeinen 			total_bytes,
583f76ee892STomi Valkeinen 			total_bytes * 1000 / total_us);
584f76ee892STomi Valkeinen }
585f76ee892STomi Valkeinen #else
dsi_perf_mark_setup(struct platform_device * dsidev)586f76ee892STomi Valkeinen static inline void dsi_perf_mark_setup(struct platform_device *dsidev)
587f76ee892STomi Valkeinen {
588f76ee892STomi Valkeinen }
589f76ee892STomi Valkeinen 
dsi_perf_mark_start(struct platform_device * dsidev)590f76ee892STomi Valkeinen static inline void dsi_perf_mark_start(struct platform_device *dsidev)
591f76ee892STomi Valkeinen {
592f76ee892STomi Valkeinen }
593f76ee892STomi Valkeinen 
dsi_perf_show(struct platform_device * dsidev,const char * name)594f76ee892STomi Valkeinen static inline void dsi_perf_show(struct platform_device *dsidev,
595f76ee892STomi Valkeinen 		const char *name)
596f76ee892STomi Valkeinen {
597f76ee892STomi Valkeinen }
598f76ee892STomi Valkeinen #endif
599f76ee892STomi Valkeinen 
600f76ee892STomi Valkeinen static int verbose_irq;
601f76ee892STomi Valkeinen 
print_irq_status(u32 status)602f76ee892STomi Valkeinen static void print_irq_status(u32 status)
603f76ee892STomi Valkeinen {
604f76ee892STomi Valkeinen 	if (status == 0)
605f76ee892STomi Valkeinen 		return;
606f76ee892STomi Valkeinen 
607f76ee892STomi Valkeinen 	if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0)
608f76ee892STomi Valkeinen 		return;
609f76ee892STomi Valkeinen 
610f76ee892STomi Valkeinen #define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : ""
611f76ee892STomi Valkeinen 
612f76ee892STomi Valkeinen 	pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
613f76ee892STomi Valkeinen 		status,
614f76ee892STomi Valkeinen 		verbose_irq ? PIS(VC0) : "",
615f76ee892STomi Valkeinen 		verbose_irq ? PIS(VC1) : "",
616f76ee892STomi Valkeinen 		verbose_irq ? PIS(VC2) : "",
617f76ee892STomi Valkeinen 		verbose_irq ? PIS(VC3) : "",
618f76ee892STomi Valkeinen 		PIS(WAKEUP),
619f76ee892STomi Valkeinen 		PIS(RESYNC),
620f76ee892STomi Valkeinen 		PIS(PLL_LOCK),
621f76ee892STomi Valkeinen 		PIS(PLL_UNLOCK),
622f76ee892STomi Valkeinen 		PIS(PLL_RECALL),
623f76ee892STomi Valkeinen 		PIS(COMPLEXIO_ERR),
624f76ee892STomi Valkeinen 		PIS(HS_TX_TIMEOUT),
625f76ee892STomi Valkeinen 		PIS(LP_RX_TIMEOUT),
626f76ee892STomi Valkeinen 		PIS(TE_TRIGGER),
627f76ee892STomi Valkeinen 		PIS(ACK_TRIGGER),
628f76ee892STomi Valkeinen 		PIS(SYNC_LOST),
629f76ee892STomi Valkeinen 		PIS(LDO_POWER_GOOD),
630f76ee892STomi Valkeinen 		PIS(TA_TIMEOUT));
631f76ee892STomi Valkeinen #undef PIS
632f76ee892STomi Valkeinen }
633f76ee892STomi Valkeinen 
print_irq_status_vc(int channel,u32 status)634f76ee892STomi Valkeinen static void print_irq_status_vc(int channel, u32 status)
635f76ee892STomi Valkeinen {
636f76ee892STomi Valkeinen 	if (status == 0)
637f76ee892STomi Valkeinen 		return;
638f76ee892STomi Valkeinen 
639f76ee892STomi Valkeinen 	if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
640f76ee892STomi Valkeinen 		return;
641f76ee892STomi Valkeinen 
642f76ee892STomi Valkeinen #define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : ""
643f76ee892STomi Valkeinen 
644f76ee892STomi Valkeinen 	pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n",
645f76ee892STomi Valkeinen 		channel,
646f76ee892STomi Valkeinen 		status,
647f76ee892STomi Valkeinen 		PIS(CS),
648f76ee892STomi Valkeinen 		PIS(ECC_CORR),
649f76ee892STomi Valkeinen 		PIS(ECC_NO_CORR),
650f76ee892STomi Valkeinen 		verbose_irq ? PIS(PACKET_SENT) : "",
651f76ee892STomi Valkeinen 		PIS(BTA),
652f76ee892STomi Valkeinen 		PIS(FIFO_TX_OVF),
653f76ee892STomi Valkeinen 		PIS(FIFO_RX_OVF),
654f76ee892STomi Valkeinen 		PIS(FIFO_TX_UDF),
655f76ee892STomi Valkeinen 		PIS(PP_BUSY_CHANGE));
656f76ee892STomi Valkeinen #undef PIS
657f76ee892STomi Valkeinen }
658f76ee892STomi Valkeinen 
print_irq_status_cio(u32 status)659f76ee892STomi Valkeinen static void print_irq_status_cio(u32 status)
660f76ee892STomi Valkeinen {
661f76ee892STomi Valkeinen 	if (status == 0)
662f76ee892STomi Valkeinen 		return;
663f76ee892STomi Valkeinen 
664f76ee892STomi Valkeinen #define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : ""
665f76ee892STomi Valkeinen 
666f76ee892STomi Valkeinen 	pr_debug("DSI CIO IRQ 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
667f76ee892STomi Valkeinen 		status,
668f76ee892STomi Valkeinen 		PIS(ERRSYNCESC1),
669f76ee892STomi Valkeinen 		PIS(ERRSYNCESC2),
670f76ee892STomi Valkeinen 		PIS(ERRSYNCESC3),
671f76ee892STomi Valkeinen 		PIS(ERRESC1),
672f76ee892STomi Valkeinen 		PIS(ERRESC2),
673f76ee892STomi Valkeinen 		PIS(ERRESC3),
674f76ee892STomi Valkeinen 		PIS(ERRCONTROL1),
675f76ee892STomi Valkeinen 		PIS(ERRCONTROL2),
676f76ee892STomi Valkeinen 		PIS(ERRCONTROL3),
677f76ee892STomi Valkeinen 		PIS(STATEULPS1),
678f76ee892STomi Valkeinen 		PIS(STATEULPS2),
679f76ee892STomi Valkeinen 		PIS(STATEULPS3),
680f76ee892STomi Valkeinen 		PIS(ERRCONTENTIONLP0_1),
681f76ee892STomi Valkeinen 		PIS(ERRCONTENTIONLP1_1),
682f76ee892STomi Valkeinen 		PIS(ERRCONTENTIONLP0_2),
683f76ee892STomi Valkeinen 		PIS(ERRCONTENTIONLP1_2),
684f76ee892STomi Valkeinen 		PIS(ERRCONTENTIONLP0_3),
685f76ee892STomi Valkeinen 		PIS(ERRCONTENTIONLP1_3),
686f76ee892STomi Valkeinen 		PIS(ULPSACTIVENOT_ALL0),
687f76ee892STomi Valkeinen 		PIS(ULPSACTIVENOT_ALL1));
688f76ee892STomi Valkeinen #undef PIS
689f76ee892STomi Valkeinen }
690f76ee892STomi Valkeinen 
69135b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
dsi_collect_irq_stats(struct platform_device * dsidev,u32 irqstatus,u32 * vcstatus,u32 ciostatus)692f76ee892STomi Valkeinen static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus,
693f76ee892STomi Valkeinen 		u32 *vcstatus, u32 ciostatus)
694f76ee892STomi Valkeinen {
695f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
696f76ee892STomi Valkeinen 	int i;
697f76ee892STomi Valkeinen 
698f76ee892STomi Valkeinen 	spin_lock(&dsi->irq_stats_lock);
699f76ee892STomi Valkeinen 
700f76ee892STomi Valkeinen 	dsi->irq_stats.irq_count++;
701f76ee892STomi Valkeinen 	dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs);
702f76ee892STomi Valkeinen 
703f76ee892STomi Valkeinen 	for (i = 0; i < 4; ++i)
704f76ee892STomi Valkeinen 		dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]);
705f76ee892STomi Valkeinen 
706f76ee892STomi Valkeinen 	dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs);
707f76ee892STomi Valkeinen 
708f76ee892STomi Valkeinen 	spin_unlock(&dsi->irq_stats_lock);
709f76ee892STomi Valkeinen }
710f76ee892STomi Valkeinen #else
711f76ee892STomi Valkeinen #define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus)
712f76ee892STomi Valkeinen #endif
713f76ee892STomi Valkeinen 
714f76ee892STomi Valkeinen static int debug_irq;
715f76ee892STomi Valkeinen 
dsi_handle_irq_errors(struct platform_device * dsidev,u32 irqstatus,u32 * vcstatus,u32 ciostatus)716f76ee892STomi Valkeinen static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus,
717f76ee892STomi Valkeinen 		u32 *vcstatus, u32 ciostatus)
718f76ee892STomi Valkeinen {
719f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
720f76ee892STomi Valkeinen 	int i;
721f76ee892STomi Valkeinen 
722f76ee892STomi Valkeinen 	if (irqstatus & DSI_IRQ_ERROR_MASK) {
723f76ee892STomi Valkeinen 		DSSERR("DSI error, irqstatus %x\n", irqstatus);
724f76ee892STomi Valkeinen 		print_irq_status(irqstatus);
725f76ee892STomi Valkeinen 		spin_lock(&dsi->errors_lock);
726f76ee892STomi Valkeinen 		dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK;
727f76ee892STomi Valkeinen 		spin_unlock(&dsi->errors_lock);
728f76ee892STomi Valkeinen 	} else if (debug_irq) {
729f76ee892STomi Valkeinen 		print_irq_status(irqstatus);
730f76ee892STomi Valkeinen 	}
731f76ee892STomi Valkeinen 
732f76ee892STomi Valkeinen 	for (i = 0; i < 4; ++i) {
733f76ee892STomi Valkeinen 		if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) {
734f76ee892STomi Valkeinen 			DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
735f76ee892STomi Valkeinen 				       i, vcstatus[i]);
736f76ee892STomi Valkeinen 			print_irq_status_vc(i, vcstatus[i]);
737f76ee892STomi Valkeinen 		} else if (debug_irq) {
738f76ee892STomi Valkeinen 			print_irq_status_vc(i, vcstatus[i]);
739f76ee892STomi Valkeinen 		}
740f76ee892STomi Valkeinen 	}
741f76ee892STomi Valkeinen 
742f76ee892STomi Valkeinen 	if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
743f76ee892STomi Valkeinen 		DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
744f76ee892STomi Valkeinen 		print_irq_status_cio(ciostatus);
745f76ee892STomi Valkeinen 	} else if (debug_irq) {
746f76ee892STomi Valkeinen 		print_irq_status_cio(ciostatus);
747f76ee892STomi Valkeinen 	}
748f76ee892STomi Valkeinen }
749f76ee892STomi Valkeinen 
dsi_call_isrs(struct dsi_isr_data * isr_array,unsigned isr_array_size,u32 irqstatus)750f76ee892STomi Valkeinen static void dsi_call_isrs(struct dsi_isr_data *isr_array,
751f76ee892STomi Valkeinen 		unsigned isr_array_size, u32 irqstatus)
752f76ee892STomi Valkeinen {
753f76ee892STomi Valkeinen 	struct dsi_isr_data *isr_data;
754f76ee892STomi Valkeinen 	int i;
755f76ee892STomi Valkeinen 
756f76ee892STomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
757f76ee892STomi Valkeinen 		isr_data = &isr_array[i];
758f76ee892STomi Valkeinen 		if (isr_data->isr && isr_data->mask & irqstatus)
759f76ee892STomi Valkeinen 			isr_data->isr(isr_data->arg, irqstatus);
760f76ee892STomi Valkeinen 	}
761f76ee892STomi Valkeinen }
762f76ee892STomi Valkeinen 
dsi_handle_isrs(struct dsi_isr_tables * isr_tables,u32 irqstatus,u32 * vcstatus,u32 ciostatus)763f76ee892STomi Valkeinen static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
764f76ee892STomi Valkeinen 		u32 irqstatus, u32 *vcstatus, u32 ciostatus)
765f76ee892STomi Valkeinen {
766f76ee892STomi Valkeinen 	int i;
767f76ee892STomi Valkeinen 
768f76ee892STomi Valkeinen 	dsi_call_isrs(isr_tables->isr_table,
769f76ee892STomi Valkeinen 			ARRAY_SIZE(isr_tables->isr_table),
770f76ee892STomi Valkeinen 			irqstatus);
771f76ee892STomi Valkeinen 
772f76ee892STomi Valkeinen 	for (i = 0; i < 4; ++i) {
773f76ee892STomi Valkeinen 		if (vcstatus[i] == 0)
774f76ee892STomi Valkeinen 			continue;
775f76ee892STomi Valkeinen 		dsi_call_isrs(isr_tables->isr_table_vc[i],
776f76ee892STomi Valkeinen 				ARRAY_SIZE(isr_tables->isr_table_vc[i]),
777f76ee892STomi Valkeinen 				vcstatus[i]);
778f76ee892STomi Valkeinen 	}
779f76ee892STomi Valkeinen 
780f76ee892STomi Valkeinen 	if (ciostatus != 0)
781f76ee892STomi Valkeinen 		dsi_call_isrs(isr_tables->isr_table_cio,
782f76ee892STomi Valkeinen 				ARRAY_SIZE(isr_tables->isr_table_cio),
783f76ee892STomi Valkeinen 				ciostatus);
784f76ee892STomi Valkeinen }
785f76ee892STomi Valkeinen 
omap_dsi_irq_handler(int irq,void * arg)786f76ee892STomi Valkeinen static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
787f76ee892STomi Valkeinen {
788f76ee892STomi Valkeinen 	struct platform_device *dsidev;
789f76ee892STomi Valkeinen 	struct dsi_data *dsi;
790f76ee892STomi Valkeinen 	u32 irqstatus, vcstatus[4], ciostatus;
791f76ee892STomi Valkeinen 	int i;
792f76ee892STomi Valkeinen 
793f76ee892STomi Valkeinen 	dsidev = (struct platform_device *) arg;
794f76ee892STomi Valkeinen 	dsi = dsi_get_dsidrv_data(dsidev);
795f76ee892STomi Valkeinen 
796f76ee892STomi Valkeinen 	if (!dsi->is_enabled)
797f76ee892STomi Valkeinen 		return IRQ_NONE;
798f76ee892STomi Valkeinen 
799f76ee892STomi Valkeinen 	spin_lock(&dsi->irq_lock);
800f76ee892STomi Valkeinen 
801f76ee892STomi Valkeinen 	irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS);
802f76ee892STomi Valkeinen 
803f76ee892STomi Valkeinen 	/* IRQ is not for us */
804f76ee892STomi Valkeinen 	if (!irqstatus) {
805f76ee892STomi Valkeinen 		spin_unlock(&dsi->irq_lock);
806f76ee892STomi Valkeinen 		return IRQ_NONE;
807f76ee892STomi Valkeinen 	}
808f76ee892STomi Valkeinen 
809f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
810f76ee892STomi Valkeinen 	/* flush posted write */
811f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, DSI_IRQSTATUS);
812f76ee892STomi Valkeinen 
813f76ee892STomi Valkeinen 	for (i = 0; i < 4; ++i) {
814f76ee892STomi Valkeinen 		if ((irqstatus & (1 << i)) == 0) {
815f76ee892STomi Valkeinen 			vcstatus[i] = 0;
816f76ee892STomi Valkeinen 			continue;
817f76ee892STomi Valkeinen 		}
818f76ee892STomi Valkeinen 
819f76ee892STomi Valkeinen 		vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
820f76ee892STomi Valkeinen 
821f76ee892STomi Valkeinen 		dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]);
822f76ee892STomi Valkeinen 		/* flush posted write */
823f76ee892STomi Valkeinen 		dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
824f76ee892STomi Valkeinen 	}
825f76ee892STomi Valkeinen 
826f76ee892STomi Valkeinen 	if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
827f76ee892STomi Valkeinen 		ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
828f76ee892STomi Valkeinen 
829f76ee892STomi Valkeinen 		dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
830f76ee892STomi Valkeinen 		/* flush posted write */
831f76ee892STomi Valkeinen 		dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
832f76ee892STomi Valkeinen 	} else {
833f76ee892STomi Valkeinen 		ciostatus = 0;
834f76ee892STomi Valkeinen 	}
835f76ee892STomi Valkeinen 
836f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
837f76ee892STomi Valkeinen 	if (irqstatus & DSI_IRQ_TE_TRIGGER)
838f76ee892STomi Valkeinen 		del_timer(&dsi->te_timer);
839f76ee892STomi Valkeinen #endif
840f76ee892STomi Valkeinen 
841f76ee892STomi Valkeinen 	/* make a copy and unlock, so that isrs can unregister
842f76ee892STomi Valkeinen 	 * themselves */
843f76ee892STomi Valkeinen 	memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
844f76ee892STomi Valkeinen 		sizeof(dsi->isr_tables));
845f76ee892STomi Valkeinen 
846f76ee892STomi Valkeinen 	spin_unlock(&dsi->irq_lock);
847f76ee892STomi Valkeinen 
848f76ee892STomi Valkeinen 	dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
849f76ee892STomi Valkeinen 
850f76ee892STomi Valkeinen 	dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus);
851f76ee892STomi Valkeinen 
852f76ee892STomi Valkeinen 	dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus);
853f76ee892STomi Valkeinen 
854f76ee892STomi Valkeinen 	return IRQ_HANDLED;
855f76ee892STomi Valkeinen }
856f76ee892STomi Valkeinen 
857f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_configure_irqs(struct platform_device * dsidev,struct dsi_isr_data * isr_array,unsigned isr_array_size,u32 default_mask,const struct dsi_reg enable_reg,const struct dsi_reg status_reg)858f76ee892STomi Valkeinen static void _omap_dsi_configure_irqs(struct platform_device *dsidev,
859f76ee892STomi Valkeinen 		struct dsi_isr_data *isr_array,
860f76ee892STomi Valkeinen 		unsigned isr_array_size, u32 default_mask,
861f76ee892STomi Valkeinen 		const struct dsi_reg enable_reg,
862f76ee892STomi Valkeinen 		const struct dsi_reg status_reg)
863f76ee892STomi Valkeinen {
864f76ee892STomi Valkeinen 	struct dsi_isr_data *isr_data;
865f76ee892STomi Valkeinen 	u32 mask;
866f76ee892STomi Valkeinen 	u32 old_mask;
867f76ee892STomi Valkeinen 	int i;
868f76ee892STomi Valkeinen 
869f76ee892STomi Valkeinen 	mask = default_mask;
870f76ee892STomi Valkeinen 
871f76ee892STomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
872f76ee892STomi Valkeinen 		isr_data = &isr_array[i];
873f76ee892STomi Valkeinen 
874f76ee892STomi Valkeinen 		if (isr_data->isr == NULL)
875f76ee892STomi Valkeinen 			continue;
876f76ee892STomi Valkeinen 
877f76ee892STomi Valkeinen 		mask |= isr_data->mask;
878f76ee892STomi Valkeinen 	}
879f76ee892STomi Valkeinen 
880f76ee892STomi Valkeinen 	old_mask = dsi_read_reg(dsidev, enable_reg);
881f76ee892STomi Valkeinen 	/* clear the irqstatus for newly enabled irqs */
882f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask);
883f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, enable_reg, mask);
884f76ee892STomi Valkeinen 
885f76ee892STomi Valkeinen 	/* flush posted writes */
886f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, enable_reg);
887f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, status_reg);
888f76ee892STomi Valkeinen }
889f76ee892STomi Valkeinen 
890f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_set_irqs(struct platform_device * dsidev)891f76ee892STomi Valkeinen static void _omap_dsi_set_irqs(struct platform_device *dsidev)
892f76ee892STomi Valkeinen {
893f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
894f76ee892STomi Valkeinen 	u32 mask = DSI_IRQ_ERROR_MASK;
895f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
896f76ee892STomi Valkeinen 	mask |= DSI_IRQ_TE_TRIGGER;
897f76ee892STomi Valkeinen #endif
898f76ee892STomi Valkeinen 	_omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table,
899f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table), mask,
900f76ee892STomi Valkeinen 			DSI_IRQENABLE, DSI_IRQSTATUS);
901f76ee892STomi Valkeinen }
902f76ee892STomi Valkeinen 
903f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_set_irqs_vc(struct platform_device * dsidev,int vc)904f76ee892STomi Valkeinen static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc)
905f76ee892STomi Valkeinen {
906f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
907f76ee892STomi Valkeinen 
908f76ee892STomi Valkeinen 	_omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc],
909f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]),
910f76ee892STomi Valkeinen 			DSI_VC_IRQ_ERROR_MASK,
911f76ee892STomi Valkeinen 			DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
912f76ee892STomi Valkeinen }
913f76ee892STomi Valkeinen 
914f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_set_irqs_cio(struct platform_device * dsidev)915f76ee892STomi Valkeinen static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev)
916f76ee892STomi Valkeinen {
917f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
918f76ee892STomi Valkeinen 
919f76ee892STomi Valkeinen 	_omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio,
920f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_cio),
921f76ee892STomi Valkeinen 			DSI_CIO_IRQ_ERROR_MASK,
922f76ee892STomi Valkeinen 			DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
923f76ee892STomi Valkeinen }
924f76ee892STomi Valkeinen 
_dsi_initialize_irq(struct platform_device * dsidev)925f76ee892STomi Valkeinen static void _dsi_initialize_irq(struct platform_device *dsidev)
926f76ee892STomi Valkeinen {
927f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
928f76ee892STomi Valkeinen 	unsigned long flags;
929f76ee892STomi Valkeinen 	int vc;
930f76ee892STomi Valkeinen 
931f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
932f76ee892STomi Valkeinen 
933f76ee892STomi Valkeinen 	memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables));
934f76ee892STomi Valkeinen 
935f76ee892STomi Valkeinen 	_omap_dsi_set_irqs(dsidev);
936f76ee892STomi Valkeinen 	for (vc = 0; vc < 4; ++vc)
937f76ee892STomi Valkeinen 		_omap_dsi_set_irqs_vc(dsidev, vc);
938f76ee892STomi Valkeinen 	_omap_dsi_set_irqs_cio(dsidev);
939f76ee892STomi Valkeinen 
940f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
941f76ee892STomi Valkeinen }
942f76ee892STomi Valkeinen 
_dsi_register_isr(omap_dsi_isr_t isr,void * arg,u32 mask,struct dsi_isr_data * isr_array,unsigned isr_array_size)943f76ee892STomi Valkeinen static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
944f76ee892STomi Valkeinen 		struct dsi_isr_data *isr_array, unsigned isr_array_size)
945f76ee892STomi Valkeinen {
946f76ee892STomi Valkeinen 	struct dsi_isr_data *isr_data;
947f76ee892STomi Valkeinen 	int free_idx;
948f76ee892STomi Valkeinen 	int i;
949f76ee892STomi Valkeinen 
950f76ee892STomi Valkeinen 	BUG_ON(isr == NULL);
951f76ee892STomi Valkeinen 
952f76ee892STomi Valkeinen 	/* check for duplicate entry and find a free slot */
953f76ee892STomi Valkeinen 	free_idx = -1;
954f76ee892STomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
955f76ee892STomi Valkeinen 		isr_data = &isr_array[i];
956f76ee892STomi Valkeinen 
957f76ee892STomi Valkeinen 		if (isr_data->isr == isr && isr_data->arg == arg &&
958f76ee892STomi Valkeinen 				isr_data->mask == mask) {
959f76ee892STomi Valkeinen 			return -EINVAL;
960f76ee892STomi Valkeinen 		}
961f76ee892STomi Valkeinen 
962f76ee892STomi Valkeinen 		if (isr_data->isr == NULL && free_idx == -1)
963f76ee892STomi Valkeinen 			free_idx = i;
964f76ee892STomi Valkeinen 	}
965f76ee892STomi Valkeinen 
966f76ee892STomi Valkeinen 	if (free_idx == -1)
967f76ee892STomi Valkeinen 		return -EBUSY;
968f76ee892STomi Valkeinen 
969f76ee892STomi Valkeinen 	isr_data = &isr_array[free_idx];
970f76ee892STomi Valkeinen 	isr_data->isr = isr;
971f76ee892STomi Valkeinen 	isr_data->arg = arg;
972f76ee892STomi Valkeinen 	isr_data->mask = mask;
973f76ee892STomi Valkeinen 
974f76ee892STomi Valkeinen 	return 0;
975f76ee892STomi Valkeinen }
976f76ee892STomi Valkeinen 
_dsi_unregister_isr(omap_dsi_isr_t isr,void * arg,u32 mask,struct dsi_isr_data * isr_array,unsigned isr_array_size)977f76ee892STomi Valkeinen static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
978f76ee892STomi Valkeinen 		struct dsi_isr_data *isr_array, unsigned isr_array_size)
979f76ee892STomi Valkeinen {
980f76ee892STomi Valkeinen 	struct dsi_isr_data *isr_data;
981f76ee892STomi Valkeinen 	int i;
982f76ee892STomi Valkeinen 
983f76ee892STomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
984f76ee892STomi Valkeinen 		isr_data = &isr_array[i];
985f76ee892STomi Valkeinen 		if (isr_data->isr != isr || isr_data->arg != arg ||
986f76ee892STomi Valkeinen 				isr_data->mask != mask)
987f76ee892STomi Valkeinen 			continue;
988f76ee892STomi Valkeinen 
989f76ee892STomi Valkeinen 		isr_data->isr = NULL;
990f76ee892STomi Valkeinen 		isr_data->arg = NULL;
991f76ee892STomi Valkeinen 		isr_data->mask = 0;
992f76ee892STomi Valkeinen 
993f76ee892STomi Valkeinen 		return 0;
994f76ee892STomi Valkeinen 	}
995f76ee892STomi Valkeinen 
996f76ee892STomi Valkeinen 	return -EINVAL;
997f76ee892STomi Valkeinen }
998f76ee892STomi Valkeinen 
dsi_register_isr(struct platform_device * dsidev,omap_dsi_isr_t isr,void * arg,u32 mask)999f76ee892STomi Valkeinen static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr,
1000f76ee892STomi Valkeinen 		void *arg, u32 mask)
1001f76ee892STomi Valkeinen {
1002f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1003f76ee892STomi Valkeinen 	unsigned long flags;
1004f76ee892STomi Valkeinen 	int r;
1005f76ee892STomi Valkeinen 
1006f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
1007f76ee892STomi Valkeinen 
1008f76ee892STomi Valkeinen 	r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table,
1009f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table));
1010f76ee892STomi Valkeinen 
1011f76ee892STomi Valkeinen 	if (r == 0)
1012f76ee892STomi Valkeinen 		_omap_dsi_set_irqs(dsidev);
1013f76ee892STomi Valkeinen 
1014f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
1015f76ee892STomi Valkeinen 
1016f76ee892STomi Valkeinen 	return r;
1017f76ee892STomi Valkeinen }
1018f76ee892STomi Valkeinen 
dsi_unregister_isr(struct platform_device * dsidev,omap_dsi_isr_t isr,void * arg,u32 mask)1019f76ee892STomi Valkeinen static int dsi_unregister_isr(struct platform_device *dsidev,
1020f76ee892STomi Valkeinen 		omap_dsi_isr_t isr, void *arg, u32 mask)
1021f76ee892STomi Valkeinen {
1022f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1023f76ee892STomi Valkeinen 	unsigned long flags;
1024f76ee892STomi Valkeinen 	int r;
1025f76ee892STomi Valkeinen 
1026f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
1027f76ee892STomi Valkeinen 
1028f76ee892STomi Valkeinen 	r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table,
1029f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table));
1030f76ee892STomi Valkeinen 
1031f76ee892STomi Valkeinen 	if (r == 0)
1032f76ee892STomi Valkeinen 		_omap_dsi_set_irqs(dsidev);
1033f76ee892STomi Valkeinen 
1034f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
1035f76ee892STomi Valkeinen 
1036f76ee892STomi Valkeinen 	return r;
1037f76ee892STomi Valkeinen }
1038f76ee892STomi Valkeinen 
dsi_register_isr_vc(struct platform_device * dsidev,int channel,omap_dsi_isr_t isr,void * arg,u32 mask)1039f76ee892STomi Valkeinen static int dsi_register_isr_vc(struct platform_device *dsidev, int channel,
1040f76ee892STomi Valkeinen 		omap_dsi_isr_t isr, void *arg, u32 mask)
1041f76ee892STomi Valkeinen {
1042f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1043f76ee892STomi Valkeinen 	unsigned long flags;
1044f76ee892STomi Valkeinen 	int r;
1045f76ee892STomi Valkeinen 
1046f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
1047f76ee892STomi Valkeinen 
1048f76ee892STomi Valkeinen 	r = _dsi_register_isr(isr, arg, mask,
1049f76ee892STomi Valkeinen 			dsi->isr_tables.isr_table_vc[channel],
1050f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
1051f76ee892STomi Valkeinen 
1052f76ee892STomi Valkeinen 	if (r == 0)
1053f76ee892STomi Valkeinen 		_omap_dsi_set_irqs_vc(dsidev, channel);
1054f76ee892STomi Valkeinen 
1055f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
1056f76ee892STomi Valkeinen 
1057f76ee892STomi Valkeinen 	return r;
1058f76ee892STomi Valkeinen }
1059f76ee892STomi Valkeinen 
dsi_unregister_isr_vc(struct platform_device * dsidev,int channel,omap_dsi_isr_t isr,void * arg,u32 mask)1060f76ee892STomi Valkeinen static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel,
1061f76ee892STomi Valkeinen 		omap_dsi_isr_t isr, void *arg, u32 mask)
1062f76ee892STomi Valkeinen {
1063f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1064f76ee892STomi Valkeinen 	unsigned long flags;
1065f76ee892STomi Valkeinen 	int r;
1066f76ee892STomi Valkeinen 
1067f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
1068f76ee892STomi Valkeinen 
1069f76ee892STomi Valkeinen 	r = _dsi_unregister_isr(isr, arg, mask,
1070f76ee892STomi Valkeinen 			dsi->isr_tables.isr_table_vc[channel],
1071f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
1072f76ee892STomi Valkeinen 
1073f76ee892STomi Valkeinen 	if (r == 0)
1074f76ee892STomi Valkeinen 		_omap_dsi_set_irqs_vc(dsidev, channel);
1075f76ee892STomi Valkeinen 
1076f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
1077f76ee892STomi Valkeinen 
1078f76ee892STomi Valkeinen 	return r;
1079f76ee892STomi Valkeinen }
1080f76ee892STomi Valkeinen 
dsi_register_isr_cio(struct platform_device * dsidev,omap_dsi_isr_t isr,void * arg,u32 mask)1081f76ee892STomi Valkeinen static int dsi_register_isr_cio(struct platform_device *dsidev,
1082f76ee892STomi Valkeinen 		omap_dsi_isr_t isr, void *arg, u32 mask)
1083f76ee892STomi Valkeinen {
1084f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1085f76ee892STomi Valkeinen 	unsigned long flags;
1086f76ee892STomi Valkeinen 	int r;
1087f76ee892STomi Valkeinen 
1088f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
1089f76ee892STomi Valkeinen 
1090f76ee892STomi Valkeinen 	r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
1091f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
1092f76ee892STomi Valkeinen 
1093f76ee892STomi Valkeinen 	if (r == 0)
1094f76ee892STomi Valkeinen 		_omap_dsi_set_irqs_cio(dsidev);
1095f76ee892STomi Valkeinen 
1096f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
1097f76ee892STomi Valkeinen 
1098f76ee892STomi Valkeinen 	return r;
1099f76ee892STomi Valkeinen }
1100f76ee892STomi Valkeinen 
dsi_unregister_isr_cio(struct platform_device * dsidev,omap_dsi_isr_t isr,void * arg,u32 mask)1101f76ee892STomi Valkeinen static int dsi_unregister_isr_cio(struct platform_device *dsidev,
1102f76ee892STomi Valkeinen 		omap_dsi_isr_t isr, void *arg, u32 mask)
1103f76ee892STomi Valkeinen {
1104f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1105f76ee892STomi Valkeinen 	unsigned long flags;
1106f76ee892STomi Valkeinen 	int r;
1107f76ee892STomi Valkeinen 
1108f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
1109f76ee892STomi Valkeinen 
1110f76ee892STomi Valkeinen 	r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
1111f76ee892STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
1112f76ee892STomi Valkeinen 
1113f76ee892STomi Valkeinen 	if (r == 0)
1114f76ee892STomi Valkeinen 		_omap_dsi_set_irqs_cio(dsidev);
1115f76ee892STomi Valkeinen 
1116f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
1117f76ee892STomi Valkeinen 
1118f76ee892STomi Valkeinen 	return r;
1119f76ee892STomi Valkeinen }
1120f76ee892STomi Valkeinen 
dsi_get_errors(struct platform_device * dsidev)1121f76ee892STomi Valkeinen static u32 dsi_get_errors(struct platform_device *dsidev)
1122f76ee892STomi Valkeinen {
1123f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1124f76ee892STomi Valkeinen 	unsigned long flags;
1125f76ee892STomi Valkeinen 	u32 e;
1126f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->errors_lock, flags);
1127f76ee892STomi Valkeinen 	e = dsi->errors;
1128f76ee892STomi Valkeinen 	dsi->errors = 0;
1129f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->errors_lock, flags);
1130f76ee892STomi Valkeinen 	return e;
1131f76ee892STomi Valkeinen }
1132f76ee892STomi Valkeinen 
dsi_runtime_get(struct platform_device * dsidev)1133f76ee892STomi Valkeinen static int dsi_runtime_get(struct platform_device *dsidev)
1134f76ee892STomi Valkeinen {
1135f76ee892STomi Valkeinen 	int r;
1136f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1137f76ee892STomi Valkeinen 
1138f76ee892STomi Valkeinen 	DSSDBG("dsi_runtime_get\n");
1139f76ee892STomi Valkeinen 
1140b0e07060SZhang Qilong 	r = pm_runtime_resume_and_get(&dsi->pdev->dev);
1141b0e07060SZhang Qilong 	if (WARN_ON(r < 0))
114278c2ce9bSAditya Pakki 		return r;
114378c2ce9bSAditya Pakki 	return 0;
1144f76ee892STomi Valkeinen }
1145f76ee892STomi Valkeinen 
dsi_runtime_put(struct platform_device * dsidev)1146f76ee892STomi Valkeinen static void dsi_runtime_put(struct platform_device *dsidev)
1147f76ee892STomi Valkeinen {
1148f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1149f76ee892STomi Valkeinen 	int r;
1150f76ee892STomi Valkeinen 
1151f76ee892STomi Valkeinen 	DSSDBG("dsi_runtime_put\n");
1152f76ee892STomi Valkeinen 
1153f76ee892STomi Valkeinen 	r = pm_runtime_put_sync(&dsi->pdev->dev);
1154f76ee892STomi Valkeinen 	WARN_ON(r < 0 && r != -ENOSYS);
1155f76ee892STomi Valkeinen }
1156f76ee892STomi Valkeinen 
dsi_regulator_init(struct platform_device * dsidev)1157f76ee892STomi Valkeinen static int dsi_regulator_init(struct platform_device *dsidev)
1158f76ee892STomi Valkeinen {
1159f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1160f76ee892STomi Valkeinen 	struct regulator *vdds_dsi;
1161f76ee892STomi Valkeinen 
1162f76ee892STomi Valkeinen 	if (dsi->vdds_dsi_reg != NULL)
1163f76ee892STomi Valkeinen 		return 0;
1164f76ee892STomi Valkeinen 
1165f76ee892STomi Valkeinen 	vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdd");
1166f76ee892STomi Valkeinen 
1167f76ee892STomi Valkeinen 	if (IS_ERR(vdds_dsi)) {
1168f76ee892STomi Valkeinen 		if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
1169f76ee892STomi Valkeinen 			DSSERR("can't get DSI VDD regulator\n");
1170f76ee892STomi Valkeinen 		return PTR_ERR(vdds_dsi);
1171f76ee892STomi Valkeinen 	}
1172f76ee892STomi Valkeinen 
1173f76ee892STomi Valkeinen 	dsi->vdds_dsi_reg = vdds_dsi;
1174f76ee892STomi Valkeinen 
1175f76ee892STomi Valkeinen 	return 0;
1176f76ee892STomi Valkeinen }
1177f76ee892STomi Valkeinen 
_dsi_print_reset_status(struct platform_device * dsidev)1178f76ee892STomi Valkeinen static void _dsi_print_reset_status(struct platform_device *dsidev)
1179f76ee892STomi Valkeinen {
1180f76ee892STomi Valkeinen 	int b0, b1, b2;
1181f76ee892STomi Valkeinen 
1182f76ee892STomi Valkeinen 	/* A dummy read using the SCP interface to any DSIPHY register is
1183f76ee892STomi Valkeinen 	 * required after DSIPHY reset to complete the reset of the DSI complex
1184f76ee892STomi Valkeinen 	 * I/O. */
1185c96da175SSam Ravnborg 	dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
1186f76ee892STomi Valkeinen 
1187f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) {
1188f76ee892STomi Valkeinen 		b0 = 28;
1189f76ee892STomi Valkeinen 		b1 = 27;
1190f76ee892STomi Valkeinen 		b2 = 26;
1191f76ee892STomi Valkeinen 	} else {
1192f76ee892STomi Valkeinen 		b0 = 24;
1193f76ee892STomi Valkeinen 		b1 = 25;
1194f76ee892STomi Valkeinen 		b2 = 26;
1195f76ee892STomi Valkeinen 	}
1196f76ee892STomi Valkeinen 
1197f76ee892STomi Valkeinen #define DSI_FLD_GET(fld, start, end)\
1198f76ee892STomi Valkeinen 	FLD_GET(dsi_read_reg(dsidev, DSI_##fld), start, end)
1199f76ee892STomi Valkeinen 
1200f76ee892STomi Valkeinen 	pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n",
1201f76ee892STomi Valkeinen 		DSI_FLD_GET(PLL_STATUS, 0, 0),
1202f76ee892STomi Valkeinen 		DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29),
1203f76ee892STomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, b0, b0),
1204f76ee892STomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, b1, b1),
1205f76ee892STomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, b2, b2),
1206f76ee892STomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, 29, 29),
1207f76ee892STomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, 30, 30),
1208f76ee892STomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, 31, 31));
1209f76ee892STomi Valkeinen 
1210f76ee892STomi Valkeinen #undef DSI_FLD_GET
1211f76ee892STomi Valkeinen }
1212f76ee892STomi Valkeinen 
dsi_if_enable(struct platform_device * dsidev,bool enable)1213f76ee892STomi Valkeinen static inline int dsi_if_enable(struct platform_device *dsidev, bool enable)
1214f76ee892STomi Valkeinen {
1215f76ee892STomi Valkeinen 	DSSDBG("dsi_if_enable(%d)\n", enable);
1216f76ee892STomi Valkeinen 
1217f76ee892STomi Valkeinen 	enable = enable ? 1 : 0;
1218f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */
1219f76ee892STomi Valkeinen 
1220f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) {
1221f76ee892STomi Valkeinen 			DSSERR("Failed to set dsi_if_enable to %d\n", enable);
1222f76ee892STomi Valkeinen 			return -EIO;
1223f76ee892STomi Valkeinen 	}
1224f76ee892STomi Valkeinen 
1225f76ee892STomi Valkeinen 	return 0;
1226f76ee892STomi Valkeinen }
1227f76ee892STomi Valkeinen 
dsi_get_pll_hsdiv_dispc_rate(struct platform_device * dsidev)1228f76ee892STomi Valkeinen static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
1229f76ee892STomi Valkeinen {
1230f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1231f76ee892STomi Valkeinen 
1232f76ee892STomi Valkeinen 	return dsi->pll.cinfo.clkout[HSDIV_DISPC];
1233f76ee892STomi Valkeinen }
1234f76ee892STomi Valkeinen 
dsi_get_pll_hsdiv_dsi_rate(struct platform_device * dsidev)1235f76ee892STomi Valkeinen static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev)
1236f76ee892STomi Valkeinen {
1237f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1238f76ee892STomi Valkeinen 
1239f76ee892STomi Valkeinen 	return dsi->pll.cinfo.clkout[HSDIV_DSI];
1240f76ee892STomi Valkeinen }
1241f76ee892STomi Valkeinen 
dsi_get_txbyteclkhs(struct platform_device * dsidev)1242f76ee892STomi Valkeinen static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev)
1243f76ee892STomi Valkeinen {
1244f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1245f76ee892STomi Valkeinen 
1246f76ee892STomi Valkeinen 	return dsi->pll.cinfo.clkdco / 16;
1247f76ee892STomi Valkeinen }
1248f76ee892STomi Valkeinen 
dsi_fclk_rate(struct platform_device * dsidev)1249f76ee892STomi Valkeinen static unsigned long dsi_fclk_rate(struct platform_device *dsidev)
1250f76ee892STomi Valkeinen {
1251f76ee892STomi Valkeinen 	unsigned long r;
1252f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1253f76ee892STomi Valkeinen 
1254f76ee892STomi Valkeinen 	if (dss_get_dsi_clk_source(dsi->module_id) == OMAP_DSS_CLK_SRC_FCK) {
1255f76ee892STomi Valkeinen 		/* DSI FCLK source is DSS_CLK_FCK */
1256f76ee892STomi Valkeinen 		r = clk_get_rate(dsi->dss_clk);
1257f76ee892STomi Valkeinen 	} else {
1258f76ee892STomi Valkeinen 		/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
1259f76ee892STomi Valkeinen 		r = dsi_get_pll_hsdiv_dsi_rate(dsidev);
1260f76ee892STomi Valkeinen 	}
1261f76ee892STomi Valkeinen 
1262f76ee892STomi Valkeinen 	return r;
1263f76ee892STomi Valkeinen }
1264f76ee892STomi Valkeinen 
dsi_lp_clock_calc(unsigned long dsi_fclk,unsigned long lp_clk_min,unsigned long lp_clk_max,struct dsi_lp_clock_info * lp_cinfo)1265f76ee892STomi Valkeinen static int dsi_lp_clock_calc(unsigned long dsi_fclk,
1266f76ee892STomi Valkeinen 		unsigned long lp_clk_min, unsigned long lp_clk_max,
1267f76ee892STomi Valkeinen 		struct dsi_lp_clock_info *lp_cinfo)
1268f76ee892STomi Valkeinen {
1269f76ee892STomi Valkeinen 	unsigned lp_clk_div;
1270f76ee892STomi Valkeinen 	unsigned long lp_clk;
1271f76ee892STomi Valkeinen 
1272f76ee892STomi Valkeinen 	lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2);
1273f76ee892STomi Valkeinen 	lp_clk = dsi_fclk / 2 / lp_clk_div;
1274f76ee892STomi Valkeinen 
1275f76ee892STomi Valkeinen 	if (lp_clk < lp_clk_min || lp_clk > lp_clk_max)
1276f76ee892STomi Valkeinen 		return -EINVAL;
1277f76ee892STomi Valkeinen 
1278f76ee892STomi Valkeinen 	lp_cinfo->lp_clk_div = lp_clk_div;
1279f76ee892STomi Valkeinen 	lp_cinfo->lp_clk = lp_clk;
1280f76ee892STomi Valkeinen 
1281f76ee892STomi Valkeinen 	return 0;
1282f76ee892STomi Valkeinen }
1283f76ee892STomi Valkeinen 
dsi_set_lp_clk_divisor(struct platform_device * dsidev)1284f76ee892STomi Valkeinen static int dsi_set_lp_clk_divisor(struct platform_device *dsidev)
1285f76ee892STomi Valkeinen {
1286f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1287f76ee892STomi Valkeinen 	unsigned long dsi_fclk;
1288f76ee892STomi Valkeinen 	unsigned lp_clk_div;
1289f76ee892STomi Valkeinen 	unsigned long lp_clk;
1290f76ee892STomi Valkeinen 	unsigned lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);
1291f76ee892STomi Valkeinen 
1292f76ee892STomi Valkeinen 
1293f76ee892STomi Valkeinen 	lp_clk_div = dsi->user_lp_cinfo.lp_clk_div;
1294f76ee892STomi Valkeinen 
1295f76ee892STomi Valkeinen 	if (lp_clk_div == 0 || lp_clk_div > lpdiv_max)
1296f76ee892STomi Valkeinen 		return -EINVAL;
1297f76ee892STomi Valkeinen 
1298f76ee892STomi Valkeinen 	dsi_fclk = dsi_fclk_rate(dsidev);
1299f76ee892STomi Valkeinen 
1300f76ee892STomi Valkeinen 	lp_clk = dsi_fclk / 2 / lp_clk_div;
1301f76ee892STomi Valkeinen 
1302f76ee892STomi Valkeinen 	DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
1303f76ee892STomi Valkeinen 	dsi->current_lp_cinfo.lp_clk = lp_clk;
1304f76ee892STomi Valkeinen 	dsi->current_lp_cinfo.lp_clk_div = lp_clk_div;
1305f76ee892STomi Valkeinen 
1306f76ee892STomi Valkeinen 	/* LP_CLK_DIVISOR */
1307f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0);
1308f76ee892STomi Valkeinen 
1309f76ee892STomi Valkeinen 	/* LP_RX_SYNCHRO_ENABLE */
1310f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21);
1311f76ee892STomi Valkeinen 
1312f76ee892STomi Valkeinen 	return 0;
1313f76ee892STomi Valkeinen }
1314f76ee892STomi Valkeinen 
dsi_enable_scp_clk(struct platform_device * dsidev)1315f76ee892STomi Valkeinen static void dsi_enable_scp_clk(struct platform_device *dsidev)
1316f76ee892STomi Valkeinen {
1317f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1318f76ee892STomi Valkeinen 
1319f76ee892STomi Valkeinen 	if (dsi->scp_clk_refcount++ == 0)
1320f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */
1321f76ee892STomi Valkeinen }
1322f76ee892STomi Valkeinen 
dsi_disable_scp_clk(struct platform_device * dsidev)1323f76ee892STomi Valkeinen static void dsi_disable_scp_clk(struct platform_device *dsidev)
1324f76ee892STomi Valkeinen {
1325f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1326f76ee892STomi Valkeinen 
1327f76ee892STomi Valkeinen 	WARN_ON(dsi->scp_clk_refcount == 0);
1328f76ee892STomi Valkeinen 	if (--dsi->scp_clk_refcount == 0)
1329f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */
1330f76ee892STomi Valkeinen }
1331f76ee892STomi Valkeinen 
1332f76ee892STomi Valkeinen enum dsi_pll_power_state {
1333f76ee892STomi Valkeinen 	DSI_PLL_POWER_OFF	= 0x0,
1334f76ee892STomi Valkeinen 	DSI_PLL_POWER_ON_HSCLK	= 0x1,
1335f76ee892STomi Valkeinen 	DSI_PLL_POWER_ON_ALL	= 0x2,
1336f76ee892STomi Valkeinen 	DSI_PLL_POWER_ON_DIV	= 0x3,
1337f76ee892STomi Valkeinen };
1338f76ee892STomi Valkeinen 
dsi_pll_power(struct platform_device * dsidev,enum dsi_pll_power_state state)1339f76ee892STomi Valkeinen static int dsi_pll_power(struct platform_device *dsidev,
1340f76ee892STomi Valkeinen 		enum dsi_pll_power_state state)
1341f76ee892STomi Valkeinen {
1342f76ee892STomi Valkeinen 	int t = 0;
1343f76ee892STomi Valkeinen 
1344f76ee892STomi Valkeinen 	/* DSI-PLL power command 0x3 is not working */
1345f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) &&
1346f76ee892STomi Valkeinen 			state == DSI_PLL_POWER_ON_DIV)
1347f76ee892STomi Valkeinen 		state = DSI_PLL_POWER_ON_ALL;
1348f76ee892STomi Valkeinen 
1349f76ee892STomi Valkeinen 	/* PLL_PWR_CMD */
1350f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30);
1351f76ee892STomi Valkeinen 
1352f76ee892STomi Valkeinen 	/* PLL_PWR_STATUS */
1353f76ee892STomi Valkeinen 	while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) {
1354f76ee892STomi Valkeinen 		if (++t > 1000) {
1355f76ee892STomi Valkeinen 			DSSERR("Failed to set DSI PLL power mode to %d\n",
1356f76ee892STomi Valkeinen 					state);
1357f76ee892STomi Valkeinen 			return -ENODEV;
1358f76ee892STomi Valkeinen 		}
1359f76ee892STomi Valkeinen 		udelay(1);
1360f76ee892STomi Valkeinen 	}
1361f76ee892STomi Valkeinen 
1362f76ee892STomi Valkeinen 	return 0;
1363f76ee892STomi Valkeinen }
1364f76ee892STomi Valkeinen 
1365f76ee892STomi Valkeinen 
dsi_pll_calc_dsi_fck(struct dss_pll_clock_info * cinfo)1366f76ee892STomi Valkeinen static void dsi_pll_calc_dsi_fck(struct dss_pll_clock_info *cinfo)
1367f76ee892STomi Valkeinen {
1368f76ee892STomi Valkeinen 	unsigned long max_dsi_fck;
1369f76ee892STomi Valkeinen 
1370f76ee892STomi Valkeinen 	max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK);
1371f76ee892STomi Valkeinen 
1372f76ee892STomi Valkeinen 	cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck);
1373f76ee892STomi Valkeinen 	cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI];
1374f76ee892STomi Valkeinen }
1375f76ee892STomi Valkeinen 
dsi_pll_enable(struct dss_pll * pll)1376f76ee892STomi Valkeinen static int dsi_pll_enable(struct dss_pll *pll)
1377f76ee892STomi Valkeinen {
1378f76ee892STomi Valkeinen 	struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
1379f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi->pdev;
1380f76ee892STomi Valkeinen 	int r = 0;
1381f76ee892STomi Valkeinen 
1382f76ee892STomi Valkeinen 	DSSDBG("PLL init\n");
1383f76ee892STomi Valkeinen 
1384f76ee892STomi Valkeinen 	r = dsi_regulator_init(dsidev);
1385f76ee892STomi Valkeinen 	if (r)
1386f76ee892STomi Valkeinen 		return r;
1387f76ee892STomi Valkeinen 
1388f76ee892STomi Valkeinen 	r = dsi_runtime_get(dsidev);
1389f76ee892STomi Valkeinen 	if (r)
1390f76ee892STomi Valkeinen 		return r;
1391f76ee892STomi Valkeinen 
1392f76ee892STomi Valkeinen 	/*
1393f76ee892STomi Valkeinen 	 * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
1394f76ee892STomi Valkeinen 	 */
1395f76ee892STomi Valkeinen 	dsi_enable_scp_clk(dsidev);
1396f76ee892STomi Valkeinen 
1397f76ee892STomi Valkeinen 	if (!dsi->vdds_dsi_enabled) {
1398f76ee892STomi Valkeinen 		r = regulator_enable(dsi->vdds_dsi_reg);
1399f76ee892STomi Valkeinen 		if (r)
1400f76ee892STomi Valkeinen 			goto err0;
1401f76ee892STomi Valkeinen 		dsi->vdds_dsi_enabled = true;
1402f76ee892STomi Valkeinen 	}
1403f76ee892STomi Valkeinen 
1404f76ee892STomi Valkeinen 	/* XXX PLL does not come out of reset without this... */
1405f76ee892STomi Valkeinen 	dispc_pck_free_enable(1);
1406f76ee892STomi Valkeinen 
1407f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) {
1408f76ee892STomi Valkeinen 		DSSERR("PLL not coming out of reset.\n");
1409f76ee892STomi Valkeinen 		r = -ENODEV;
1410f76ee892STomi Valkeinen 		dispc_pck_free_enable(0);
1411f76ee892STomi Valkeinen 		goto err1;
1412f76ee892STomi Valkeinen 	}
1413f76ee892STomi Valkeinen 
1414f76ee892STomi Valkeinen 	/* XXX ... but if left on, we get problems when planes do not
1415f76ee892STomi Valkeinen 	 * fill the whole display. No idea about this */
1416f76ee892STomi Valkeinen 	dispc_pck_free_enable(0);
1417f76ee892STomi Valkeinen 
1418f76ee892STomi Valkeinen 	r = dsi_pll_power(dsidev, DSI_PLL_POWER_ON_ALL);
1419f76ee892STomi Valkeinen 
1420f76ee892STomi Valkeinen 	if (r)
1421f76ee892STomi Valkeinen 		goto err1;
1422f76ee892STomi Valkeinen 
1423f76ee892STomi Valkeinen 	DSSDBG("PLL init done\n");
1424f76ee892STomi Valkeinen 
1425f76ee892STomi Valkeinen 	return 0;
1426f76ee892STomi Valkeinen err1:
1427f76ee892STomi Valkeinen 	if (dsi->vdds_dsi_enabled) {
1428f76ee892STomi Valkeinen 		regulator_disable(dsi->vdds_dsi_reg);
1429f76ee892STomi Valkeinen 		dsi->vdds_dsi_enabled = false;
1430f76ee892STomi Valkeinen 	}
1431f76ee892STomi Valkeinen err0:
1432f76ee892STomi Valkeinen 	dsi_disable_scp_clk(dsidev);
1433f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
1434f76ee892STomi Valkeinen 	return r;
1435f76ee892STomi Valkeinen }
1436f76ee892STomi Valkeinen 
dsi_pll_uninit(struct platform_device * dsidev,bool disconnect_lanes)1437f76ee892STomi Valkeinen static void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes)
1438f76ee892STomi Valkeinen {
1439f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1440f76ee892STomi Valkeinen 
1441f76ee892STomi Valkeinen 	dsi_pll_power(dsidev, DSI_PLL_POWER_OFF);
1442f76ee892STomi Valkeinen 	if (disconnect_lanes) {
1443f76ee892STomi Valkeinen 		WARN_ON(!dsi->vdds_dsi_enabled);
1444f76ee892STomi Valkeinen 		regulator_disable(dsi->vdds_dsi_reg);
1445f76ee892STomi Valkeinen 		dsi->vdds_dsi_enabled = false;
1446f76ee892STomi Valkeinen 	}
1447f76ee892STomi Valkeinen 
1448f76ee892STomi Valkeinen 	dsi_disable_scp_clk(dsidev);
1449f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
1450f76ee892STomi Valkeinen 
1451f76ee892STomi Valkeinen 	DSSDBG("PLL uninit done\n");
1452f76ee892STomi Valkeinen }
1453f76ee892STomi Valkeinen 
dsi_pll_disable(struct dss_pll * pll)1454f76ee892STomi Valkeinen static void dsi_pll_disable(struct dss_pll *pll)
1455f76ee892STomi Valkeinen {
1456f76ee892STomi Valkeinen 	struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
1457f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi->pdev;
1458f76ee892STomi Valkeinen 
1459f76ee892STomi Valkeinen 	dsi_pll_uninit(dsidev, true);
1460f76ee892STomi Valkeinen }
1461f76ee892STomi Valkeinen 
dsi_dump_dsidev_clocks(struct platform_device * dsidev,struct seq_file * s)1462f76ee892STomi Valkeinen static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
1463f76ee892STomi Valkeinen 		struct seq_file *s)
1464f76ee892STomi Valkeinen {
1465f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1466f76ee892STomi Valkeinen 	struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo;
1467f76ee892STomi Valkeinen 	enum omap_dss_clk_source dispc_clk_src, dsi_clk_src;
1468f76ee892STomi Valkeinen 	int dsi_module = dsi->module_id;
1469f76ee892STomi Valkeinen 	struct dss_pll *pll = &dsi->pll;
1470f76ee892STomi Valkeinen 
1471f76ee892STomi Valkeinen 	dispc_clk_src = dss_get_dispc_clk_source();
1472f76ee892STomi Valkeinen 	dsi_clk_src = dss_get_dsi_clk_source(dsi_module);
1473f76ee892STomi Valkeinen 
1474f76ee892STomi Valkeinen 	if (dsi_runtime_get(dsidev))
1475f76ee892STomi Valkeinen 		return;
1476f76ee892STomi Valkeinen 
1477f76ee892STomi Valkeinen 	seq_printf(s,	"- DSI%d PLL -\n", dsi_module + 1);
1478f76ee892STomi Valkeinen 
1479f76ee892STomi Valkeinen 	seq_printf(s,	"dsi pll clkin\t%lu\n", clk_get_rate(pll->clkin));
1480f76ee892STomi Valkeinen 
1481f76ee892STomi Valkeinen 	seq_printf(s,	"Fint\t\t%-16lun %u\n", cinfo->fint, cinfo->n);
1482f76ee892STomi Valkeinen 
1483f76ee892STomi Valkeinen 	seq_printf(s,	"CLKIN4DDR\t%-16lum %u\n",
1484f76ee892STomi Valkeinen 			cinfo->clkdco, cinfo->m);
1485f76ee892STomi Valkeinen 
1486f76ee892STomi Valkeinen 	seq_printf(s,	"DSI_PLL_HSDIV_DISPC (%s)\t%-16lum_dispc %u\t(%s)\n",
1487f76ee892STomi Valkeinen 			dss_feat_get_clk_source_name(dsi_module == 0 ?
1488f76ee892STomi Valkeinen 				OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
1489f76ee892STomi Valkeinen 				OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC),
1490f76ee892STomi Valkeinen 			cinfo->clkout[HSDIV_DISPC],
1491f76ee892STomi Valkeinen 			cinfo->mX[HSDIV_DISPC],
1492f76ee892STomi Valkeinen 			dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ?
1493f76ee892STomi Valkeinen 			"off" : "on");
1494f76ee892STomi Valkeinen 
1495f76ee892STomi Valkeinen 	seq_printf(s,	"DSI_PLL_HSDIV_DSI (%s)\t%-16lum_dsi %u\t(%s)\n",
1496f76ee892STomi Valkeinen 			dss_feat_get_clk_source_name(dsi_module == 0 ?
1497f76ee892STomi Valkeinen 				OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
1498f76ee892STomi Valkeinen 				OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI),
1499f76ee892STomi Valkeinen 			cinfo->clkout[HSDIV_DSI],
1500f76ee892STomi Valkeinen 			cinfo->mX[HSDIV_DSI],
1501f76ee892STomi Valkeinen 			dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ?
1502f76ee892STomi Valkeinen 			"off" : "on");
1503f76ee892STomi Valkeinen 
1504f76ee892STomi Valkeinen 	seq_printf(s,	"- DSI%d -\n", dsi_module + 1);
1505f76ee892STomi Valkeinen 
1506f76ee892STomi Valkeinen 	seq_printf(s,	"dsi fclk source = %s (%s)\n",
1507f76ee892STomi Valkeinen 			dss_get_generic_clk_source_name(dsi_clk_src),
1508f76ee892STomi Valkeinen 			dss_feat_get_clk_source_name(dsi_clk_src));
1509f76ee892STomi Valkeinen 
1510f76ee892STomi Valkeinen 	seq_printf(s,	"DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev));
1511f76ee892STomi Valkeinen 
1512f76ee892STomi Valkeinen 	seq_printf(s,	"DDR_CLK\t\t%lu\n",
1513f76ee892STomi Valkeinen 			cinfo->clkdco / 4);
1514f76ee892STomi Valkeinen 
1515f76ee892STomi Valkeinen 	seq_printf(s,	"TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev));
1516f76ee892STomi Valkeinen 
1517f76ee892STomi Valkeinen 	seq_printf(s,	"LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk);
1518f76ee892STomi Valkeinen 
1519f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
1520f76ee892STomi Valkeinen }
1521f76ee892STomi Valkeinen 
dsi_dump_clocks(struct seq_file * s)1522f76ee892STomi Valkeinen void dsi_dump_clocks(struct seq_file *s)
1523f76ee892STomi Valkeinen {
1524f76ee892STomi Valkeinen 	struct platform_device *dsidev;
1525f76ee892STomi Valkeinen 	int i;
1526f76ee892STomi Valkeinen 
1527f76ee892STomi Valkeinen 	for  (i = 0; i < MAX_NUM_DSI; i++) {
1528f76ee892STomi Valkeinen 		dsidev = dsi_get_dsidev_from_id(i);
1529f76ee892STomi Valkeinen 		if (dsidev)
1530f76ee892STomi Valkeinen 			dsi_dump_dsidev_clocks(dsidev, s);
1531f76ee892STomi Valkeinen 	}
1532f76ee892STomi Valkeinen }
1533f76ee892STomi Valkeinen 
153435b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
dsi_dump_dsidev_irqs(struct platform_device * dsidev,struct seq_file * s)1535f76ee892STomi Valkeinen static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
1536f76ee892STomi Valkeinen 		struct seq_file *s)
1537f76ee892STomi Valkeinen {
1538f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1539f76ee892STomi Valkeinen 	unsigned long flags;
1540634cf6eaSArnd Bergmann 	struct dsi_irq_stats *stats;
1541634cf6eaSArnd Bergmann 
1542634cf6eaSArnd Bergmann 	stats = kzalloc(sizeof(*stats), GFP_KERNEL);
1543634cf6eaSArnd Bergmann 	if (!stats) {
1544634cf6eaSArnd Bergmann 		seq_printf(s, "out of memory\n");
1545634cf6eaSArnd Bergmann 		return;
1546634cf6eaSArnd Bergmann 	}
1547f76ee892STomi Valkeinen 
1548f76ee892STomi Valkeinen 	spin_lock_irqsave(&dsi->irq_stats_lock, flags);
1549f76ee892STomi Valkeinen 
1550634cf6eaSArnd Bergmann 	*stats = dsi->irq_stats;
1551f76ee892STomi Valkeinen 	memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
1552f76ee892STomi Valkeinen 	dsi->irq_stats.last_reset = jiffies;
1553f76ee892STomi Valkeinen 
1554f76ee892STomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
1555f76ee892STomi Valkeinen 
1556f76ee892STomi Valkeinen 	seq_printf(s, "period %u ms\n",
1557634cf6eaSArnd Bergmann 			jiffies_to_msecs(jiffies - stats->last_reset));
1558f76ee892STomi Valkeinen 
1559634cf6eaSArnd Bergmann 	seq_printf(s, "irqs %d\n", stats->irq_count);
1560f76ee892STomi Valkeinen #define PIS(x) \
1561634cf6eaSArnd Bergmann 	seq_printf(s, "%-20s %10d\n", #x, stats->dsi_irqs[ffs(DSI_IRQ_##x)-1])
1562f76ee892STomi Valkeinen 
1563f76ee892STomi Valkeinen 	seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1);
1564f76ee892STomi Valkeinen 	PIS(VC0);
1565f76ee892STomi Valkeinen 	PIS(VC1);
1566f76ee892STomi Valkeinen 	PIS(VC2);
1567f76ee892STomi Valkeinen 	PIS(VC3);
1568f76ee892STomi Valkeinen 	PIS(WAKEUP);
1569f76ee892STomi Valkeinen 	PIS(RESYNC);
1570f76ee892STomi Valkeinen 	PIS(PLL_LOCK);
1571f76ee892STomi Valkeinen 	PIS(PLL_UNLOCK);
1572f76ee892STomi Valkeinen 	PIS(PLL_RECALL);
1573f76ee892STomi Valkeinen 	PIS(COMPLEXIO_ERR);
1574f76ee892STomi Valkeinen 	PIS(HS_TX_TIMEOUT);
1575f76ee892STomi Valkeinen 	PIS(LP_RX_TIMEOUT);
1576f76ee892STomi Valkeinen 	PIS(TE_TRIGGER);
1577f76ee892STomi Valkeinen 	PIS(ACK_TRIGGER);
1578f76ee892STomi Valkeinen 	PIS(SYNC_LOST);
1579f76ee892STomi Valkeinen 	PIS(LDO_POWER_GOOD);
1580f76ee892STomi Valkeinen 	PIS(TA_TIMEOUT);
1581f76ee892STomi Valkeinen #undef PIS
1582f76ee892STomi Valkeinen 
1583f76ee892STomi Valkeinen #define PIS(x) \
1584f76ee892STomi Valkeinen 	seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
1585634cf6eaSArnd Bergmann 			stats->vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
1586634cf6eaSArnd Bergmann 			stats->vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
1587634cf6eaSArnd Bergmann 			stats->vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
1588634cf6eaSArnd Bergmann 			stats->vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
1589f76ee892STomi Valkeinen 
1590f76ee892STomi Valkeinen 	seq_printf(s, "-- VC interrupts --\n");
1591f76ee892STomi Valkeinen 	PIS(CS);
1592f76ee892STomi Valkeinen 	PIS(ECC_CORR);
1593f76ee892STomi Valkeinen 	PIS(PACKET_SENT);
1594f76ee892STomi Valkeinen 	PIS(FIFO_TX_OVF);
1595f76ee892STomi Valkeinen 	PIS(FIFO_RX_OVF);
1596f76ee892STomi Valkeinen 	PIS(BTA);
1597f76ee892STomi Valkeinen 	PIS(ECC_NO_CORR);
1598f76ee892STomi Valkeinen 	PIS(FIFO_TX_UDF);
1599f76ee892STomi Valkeinen 	PIS(PP_BUSY_CHANGE);
1600f76ee892STomi Valkeinen #undef PIS
1601f76ee892STomi Valkeinen 
1602f76ee892STomi Valkeinen #define PIS(x) \
1603f76ee892STomi Valkeinen 	seq_printf(s, "%-20s %10d\n", #x, \
1604634cf6eaSArnd Bergmann 			stats->cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
1605f76ee892STomi Valkeinen 
1606f76ee892STomi Valkeinen 	seq_printf(s, "-- CIO interrupts --\n");
1607f76ee892STomi Valkeinen 	PIS(ERRSYNCESC1);
1608f76ee892STomi Valkeinen 	PIS(ERRSYNCESC2);
1609f76ee892STomi Valkeinen 	PIS(ERRSYNCESC3);
1610f76ee892STomi Valkeinen 	PIS(ERRESC1);
1611f76ee892STomi Valkeinen 	PIS(ERRESC2);
1612f76ee892STomi Valkeinen 	PIS(ERRESC3);
1613f76ee892STomi Valkeinen 	PIS(ERRCONTROL1);
1614f76ee892STomi Valkeinen 	PIS(ERRCONTROL2);
1615f76ee892STomi Valkeinen 	PIS(ERRCONTROL3);
1616f76ee892STomi Valkeinen 	PIS(STATEULPS1);
1617f76ee892STomi Valkeinen 	PIS(STATEULPS2);
1618f76ee892STomi Valkeinen 	PIS(STATEULPS3);
1619f76ee892STomi Valkeinen 	PIS(ERRCONTENTIONLP0_1);
1620f76ee892STomi Valkeinen 	PIS(ERRCONTENTIONLP1_1);
1621f76ee892STomi Valkeinen 	PIS(ERRCONTENTIONLP0_2);
1622f76ee892STomi Valkeinen 	PIS(ERRCONTENTIONLP1_2);
1623f76ee892STomi Valkeinen 	PIS(ERRCONTENTIONLP0_3);
1624f76ee892STomi Valkeinen 	PIS(ERRCONTENTIONLP1_3);
1625f76ee892STomi Valkeinen 	PIS(ULPSACTIVENOT_ALL0);
1626f76ee892STomi Valkeinen 	PIS(ULPSACTIVENOT_ALL1);
1627f76ee892STomi Valkeinen #undef PIS
1628634cf6eaSArnd Bergmann 
1629634cf6eaSArnd Bergmann 	kfree(stats);
1630f76ee892STomi Valkeinen }
1631f76ee892STomi Valkeinen 
dsi1_dump_irqs(struct seq_file * s)1632f76ee892STomi Valkeinen static void dsi1_dump_irqs(struct seq_file *s)
1633f76ee892STomi Valkeinen {
1634f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
1635f76ee892STomi Valkeinen 
1636f76ee892STomi Valkeinen 	dsi_dump_dsidev_irqs(dsidev, s);
1637f76ee892STomi Valkeinen }
1638f76ee892STomi Valkeinen 
dsi2_dump_irqs(struct seq_file * s)1639f76ee892STomi Valkeinen static void dsi2_dump_irqs(struct seq_file *s)
1640f76ee892STomi Valkeinen {
1641f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
1642f76ee892STomi Valkeinen 
1643f76ee892STomi Valkeinen 	dsi_dump_dsidev_irqs(dsidev, s);
1644f76ee892STomi Valkeinen }
1645f76ee892STomi Valkeinen #endif
1646f76ee892STomi Valkeinen 
dsi_dump_dsidev_regs(struct platform_device * dsidev,struct seq_file * s)1647f76ee892STomi Valkeinen static void dsi_dump_dsidev_regs(struct platform_device *dsidev,
1648f76ee892STomi Valkeinen 		struct seq_file *s)
1649f76ee892STomi Valkeinen {
1650f76ee892STomi Valkeinen #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r))
1651f76ee892STomi Valkeinen 
1652f76ee892STomi Valkeinen 	if (dsi_runtime_get(dsidev))
1653f76ee892STomi Valkeinen 		return;
1654f76ee892STomi Valkeinen 	dsi_enable_scp_clk(dsidev);
1655f76ee892STomi Valkeinen 
1656f76ee892STomi Valkeinen 	DUMPREG(DSI_REVISION);
1657f76ee892STomi Valkeinen 	DUMPREG(DSI_SYSCONFIG);
1658f76ee892STomi Valkeinen 	DUMPREG(DSI_SYSSTATUS);
1659f76ee892STomi Valkeinen 	DUMPREG(DSI_IRQSTATUS);
1660f76ee892STomi Valkeinen 	DUMPREG(DSI_IRQENABLE);
1661f76ee892STomi Valkeinen 	DUMPREG(DSI_CTRL);
1662f76ee892STomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_CFG1);
1663f76ee892STomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
1664f76ee892STomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
1665f76ee892STomi Valkeinen 	DUMPREG(DSI_CLK_CTRL);
1666f76ee892STomi Valkeinen 	DUMPREG(DSI_TIMING1);
1667f76ee892STomi Valkeinen 	DUMPREG(DSI_TIMING2);
1668f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING1);
1669f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING2);
1670f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING3);
1671f76ee892STomi Valkeinen 	DUMPREG(DSI_CLK_TIMING);
1672f76ee892STomi Valkeinen 	DUMPREG(DSI_TX_FIFO_VC_SIZE);
1673f76ee892STomi Valkeinen 	DUMPREG(DSI_RX_FIFO_VC_SIZE);
1674f76ee892STomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_CFG2);
1675f76ee892STomi Valkeinen 	DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
1676f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING4);
1677f76ee892STomi Valkeinen 	DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
1678f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING5);
1679f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING6);
1680f76ee892STomi Valkeinen 	DUMPREG(DSI_VM_TIMING7);
1681f76ee892STomi Valkeinen 	DUMPREG(DSI_STOPCLK_TIMING);
1682f76ee892STomi Valkeinen 
1683f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_CTRL(0));
1684f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_TE(0));
1685f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
1686f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
1687f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
1688f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(0));
1689f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(0));
1690f76ee892STomi Valkeinen 
1691f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_CTRL(1));
1692f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_TE(1));
1693f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
1694f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
1695f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
1696f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(1));
1697f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(1));
1698f76ee892STomi Valkeinen 
1699f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_CTRL(2));
1700f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_TE(2));
1701f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
1702f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
1703f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
1704f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(2));
1705f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(2));
1706f76ee892STomi Valkeinen 
1707f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_CTRL(3));
1708f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_TE(3));
1709f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
1710f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
1711f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
1712f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(3));
1713f76ee892STomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(3));
1714f76ee892STomi Valkeinen 
1715f76ee892STomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG0);
1716f76ee892STomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG1);
1717f76ee892STomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG2);
1718f76ee892STomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG5);
1719f76ee892STomi Valkeinen 
1720f76ee892STomi Valkeinen 	DUMPREG(DSI_PLL_CONTROL);
1721f76ee892STomi Valkeinen 	DUMPREG(DSI_PLL_STATUS);
1722f76ee892STomi Valkeinen 	DUMPREG(DSI_PLL_GO);
1723f76ee892STomi Valkeinen 	DUMPREG(DSI_PLL_CONFIGURATION1);
1724f76ee892STomi Valkeinen 	DUMPREG(DSI_PLL_CONFIGURATION2);
1725f76ee892STomi Valkeinen 
1726f76ee892STomi Valkeinen 	dsi_disable_scp_clk(dsidev);
1727f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
1728f76ee892STomi Valkeinen #undef DUMPREG
1729f76ee892STomi Valkeinen }
1730f76ee892STomi Valkeinen 
dsi1_dump_regs(struct seq_file * s)1731f76ee892STomi Valkeinen static void dsi1_dump_regs(struct seq_file *s)
1732f76ee892STomi Valkeinen {
1733f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
1734f76ee892STomi Valkeinen 
1735f76ee892STomi Valkeinen 	dsi_dump_dsidev_regs(dsidev, s);
1736f76ee892STomi Valkeinen }
1737f76ee892STomi Valkeinen 
dsi2_dump_regs(struct seq_file * s)1738f76ee892STomi Valkeinen static void dsi2_dump_regs(struct seq_file *s)
1739f76ee892STomi Valkeinen {
1740f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
1741f76ee892STomi Valkeinen 
1742f76ee892STomi Valkeinen 	dsi_dump_dsidev_regs(dsidev, s);
1743f76ee892STomi Valkeinen }
1744f76ee892STomi Valkeinen 
1745f76ee892STomi Valkeinen enum dsi_cio_power_state {
1746f76ee892STomi Valkeinen 	DSI_COMPLEXIO_POWER_OFF		= 0x0,
1747f76ee892STomi Valkeinen 	DSI_COMPLEXIO_POWER_ON		= 0x1,
1748f76ee892STomi Valkeinen 	DSI_COMPLEXIO_POWER_ULPS	= 0x2,
1749f76ee892STomi Valkeinen };
1750f76ee892STomi Valkeinen 
dsi_cio_power(struct platform_device * dsidev,enum dsi_cio_power_state state)1751f76ee892STomi Valkeinen static int dsi_cio_power(struct platform_device *dsidev,
1752f76ee892STomi Valkeinen 		enum dsi_cio_power_state state)
1753f76ee892STomi Valkeinen {
1754f76ee892STomi Valkeinen 	int t = 0;
1755f76ee892STomi Valkeinen 
1756f76ee892STomi Valkeinen 	/* PWR_CMD */
1757f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27);
1758f76ee892STomi Valkeinen 
1759f76ee892STomi Valkeinen 	/* PWR_STATUS */
1760f76ee892STomi Valkeinen 	while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1),
1761f76ee892STomi Valkeinen 			26, 25) != state) {
1762f76ee892STomi Valkeinen 		if (++t > 1000) {
1763f76ee892STomi Valkeinen 			DSSERR("failed to set complexio power state to "
1764f76ee892STomi Valkeinen 					"%d\n", state);
1765f76ee892STomi Valkeinen 			return -ENODEV;
1766f76ee892STomi Valkeinen 		}
1767f76ee892STomi Valkeinen 		udelay(1);
1768f76ee892STomi Valkeinen 	}
1769f76ee892STomi Valkeinen 
1770f76ee892STomi Valkeinen 	return 0;
1771f76ee892STomi Valkeinen }
1772f76ee892STomi Valkeinen 
dsi_get_line_buf_size(struct platform_device * dsidev)1773f76ee892STomi Valkeinen static unsigned dsi_get_line_buf_size(struct platform_device *dsidev)
1774f76ee892STomi Valkeinen {
1775f76ee892STomi Valkeinen 	int val;
1776f76ee892STomi Valkeinen 
1777f76ee892STomi Valkeinen 	/* line buffer on OMAP3 is 1024 x 24bits */
1778f76ee892STomi Valkeinen 	/* XXX: for some reason using full buffer size causes
1779f76ee892STomi Valkeinen 	 * considerable TX slowdown with update sizes that fill the
1780f76ee892STomi Valkeinen 	 * whole buffer */
1781f76ee892STomi Valkeinen 	if (!dss_has_feature(FEAT_DSI_GNQ))
1782f76ee892STomi Valkeinen 		return 1023 * 3;
1783f76ee892STomi Valkeinen 
1784f76ee892STomi Valkeinen 	val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */
1785f76ee892STomi Valkeinen 
1786f76ee892STomi Valkeinen 	switch (val) {
1787f76ee892STomi Valkeinen 	case 1:
1788f76ee892STomi Valkeinen 		return 512 * 3;		/* 512x24 bits */
1789f76ee892STomi Valkeinen 	case 2:
1790f76ee892STomi Valkeinen 		return 682 * 3;		/* 682x24 bits */
1791f76ee892STomi Valkeinen 	case 3:
1792f76ee892STomi Valkeinen 		return 853 * 3;		/* 853x24 bits */
1793f76ee892STomi Valkeinen 	case 4:
1794f76ee892STomi Valkeinen 		return 1024 * 3;	/* 1024x24 bits */
1795f76ee892STomi Valkeinen 	case 5:
1796f76ee892STomi Valkeinen 		return 1194 * 3;	/* 1194x24 bits */
1797f76ee892STomi Valkeinen 	case 6:
1798f76ee892STomi Valkeinen 		return 1365 * 3;	/* 1365x24 bits */
1799f76ee892STomi Valkeinen 	case 7:
1800f76ee892STomi Valkeinen 		return 1920 * 3;	/* 1920x24 bits */
1801f76ee892STomi Valkeinen 	default:
1802f76ee892STomi Valkeinen 		BUG();
1803f76ee892STomi Valkeinen 		return 0;
1804f76ee892STomi Valkeinen 	}
1805f76ee892STomi Valkeinen }
1806f76ee892STomi Valkeinen 
dsi_set_lane_config(struct platform_device * dsidev)1807f76ee892STomi Valkeinen static int dsi_set_lane_config(struct platform_device *dsidev)
1808f76ee892STomi Valkeinen {
1809f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1810f76ee892STomi Valkeinen 	static const u8 offsets[] = { 0, 4, 8, 12, 16 };
1811f76ee892STomi Valkeinen 	static const enum dsi_lane_function functions[] = {
1812f76ee892STomi Valkeinen 		DSI_LANE_CLK,
1813f76ee892STomi Valkeinen 		DSI_LANE_DATA1,
1814f76ee892STomi Valkeinen 		DSI_LANE_DATA2,
1815f76ee892STomi Valkeinen 		DSI_LANE_DATA3,
1816f76ee892STomi Valkeinen 		DSI_LANE_DATA4,
1817f76ee892STomi Valkeinen 	};
1818f76ee892STomi Valkeinen 	u32 r;
1819f76ee892STomi Valkeinen 	int i;
1820f76ee892STomi Valkeinen 
1821f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
1822f76ee892STomi Valkeinen 
1823f76ee892STomi Valkeinen 	for (i = 0; i < dsi->num_lanes_used; ++i) {
1824f76ee892STomi Valkeinen 		unsigned offset = offsets[i];
1825f76ee892STomi Valkeinen 		unsigned polarity, lane_number;
1826f76ee892STomi Valkeinen 		unsigned t;
1827f76ee892STomi Valkeinen 
1828f76ee892STomi Valkeinen 		for (t = 0; t < dsi->num_lanes_supported; ++t)
1829f76ee892STomi Valkeinen 			if (dsi->lanes[t].function == functions[i])
1830f76ee892STomi Valkeinen 				break;
1831f76ee892STomi Valkeinen 
1832f76ee892STomi Valkeinen 		if (t == dsi->num_lanes_supported)
1833f76ee892STomi Valkeinen 			return -EINVAL;
1834f76ee892STomi Valkeinen 
1835f76ee892STomi Valkeinen 		lane_number = t;
1836f76ee892STomi Valkeinen 		polarity = dsi->lanes[t].polarity;
1837f76ee892STomi Valkeinen 
1838f76ee892STomi Valkeinen 		r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
1839f76ee892STomi Valkeinen 		r = FLD_MOD(r, polarity, offset + 3, offset + 3);
1840f76ee892STomi Valkeinen 	}
1841f76ee892STomi Valkeinen 
1842f76ee892STomi Valkeinen 	/* clear the unused lanes */
1843f76ee892STomi Valkeinen 	for (; i < dsi->num_lanes_supported; ++i) {
1844f76ee892STomi Valkeinen 		unsigned offset = offsets[i];
1845f76ee892STomi Valkeinen 
1846f76ee892STomi Valkeinen 		r = FLD_MOD(r, 0, offset + 2, offset);
1847f76ee892STomi Valkeinen 		r = FLD_MOD(r, 0, offset + 3, offset + 3);
1848f76ee892STomi Valkeinen 	}
1849f76ee892STomi Valkeinen 
1850f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r);
1851f76ee892STomi Valkeinen 
1852f76ee892STomi Valkeinen 	return 0;
1853f76ee892STomi Valkeinen }
1854f76ee892STomi Valkeinen 
ns2ddr(struct platform_device * dsidev,unsigned ns)1855f76ee892STomi Valkeinen static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns)
1856f76ee892STomi Valkeinen {
1857f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1858f76ee892STomi Valkeinen 
1859f76ee892STomi Valkeinen 	/* convert time in ns to ddr ticks, rounding up */
1860f76ee892STomi Valkeinen 	unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
1861f76ee892STomi Valkeinen 	return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
1862f76ee892STomi Valkeinen }
1863f76ee892STomi Valkeinen 
ddr2ns(struct platform_device * dsidev,unsigned ddr)1864f76ee892STomi Valkeinen static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr)
1865f76ee892STomi Valkeinen {
1866f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1867f76ee892STomi Valkeinen 
1868f76ee892STomi Valkeinen 	unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
1869f76ee892STomi Valkeinen 	return ddr * 1000 * 1000 / (ddr_clk / 1000);
1870f76ee892STomi Valkeinen }
1871f76ee892STomi Valkeinen 
dsi_cio_timings(struct platform_device * dsidev)1872f76ee892STomi Valkeinen static void dsi_cio_timings(struct platform_device *dsidev)
1873f76ee892STomi Valkeinen {
1874f76ee892STomi Valkeinen 	u32 r;
1875f76ee892STomi Valkeinen 	u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
1876f76ee892STomi Valkeinen 	u32 tlpx_half, tclk_trail, tclk_zero;
1877f76ee892STomi Valkeinen 	u32 tclk_prepare;
1878f76ee892STomi Valkeinen 
1879f76ee892STomi Valkeinen 	/* calculate timings */
1880f76ee892STomi Valkeinen 
1881f76ee892STomi Valkeinen 	/* 1 * DDR_CLK = 2 * UI */
1882f76ee892STomi Valkeinen 
1883f76ee892STomi Valkeinen 	/* min 40ns + 4*UI	max 85ns + 6*UI */
1884f76ee892STomi Valkeinen 	ths_prepare = ns2ddr(dsidev, 70) + 2;
1885f76ee892STomi Valkeinen 
1886f76ee892STomi Valkeinen 	/* min 145ns + 10*UI */
1887f76ee892STomi Valkeinen 	ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2;
1888f76ee892STomi Valkeinen 
1889f76ee892STomi Valkeinen 	/* min max(8*UI, 60ns+4*UI) */
1890f76ee892STomi Valkeinen 	ths_trail = ns2ddr(dsidev, 60) + 5;
1891f76ee892STomi Valkeinen 
1892f76ee892STomi Valkeinen 	/* min 100ns */
1893f76ee892STomi Valkeinen 	ths_exit = ns2ddr(dsidev, 145);
1894f76ee892STomi Valkeinen 
1895f76ee892STomi Valkeinen 	/* tlpx min 50n */
1896f76ee892STomi Valkeinen 	tlpx_half = ns2ddr(dsidev, 25);
1897f76ee892STomi Valkeinen 
1898f76ee892STomi Valkeinen 	/* min 60ns */
1899f76ee892STomi Valkeinen 	tclk_trail = ns2ddr(dsidev, 60) + 2;
1900f76ee892STomi Valkeinen 
1901f76ee892STomi Valkeinen 	/* min 38ns, max 95ns */
1902f76ee892STomi Valkeinen 	tclk_prepare = ns2ddr(dsidev, 65);
1903f76ee892STomi Valkeinen 
1904f76ee892STomi Valkeinen 	/* min tclk-prepare + tclk-zero = 300ns */
1905f76ee892STomi Valkeinen 	tclk_zero = ns2ddr(dsidev, 260);
1906f76ee892STomi Valkeinen 
1907f76ee892STomi Valkeinen 	DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
1908f76ee892STomi Valkeinen 		ths_prepare, ddr2ns(dsidev, ths_prepare),
1909f76ee892STomi Valkeinen 		ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero));
1910f76ee892STomi Valkeinen 	DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
1911f76ee892STomi Valkeinen 			ths_trail, ddr2ns(dsidev, ths_trail),
1912f76ee892STomi Valkeinen 			ths_exit, ddr2ns(dsidev, ths_exit));
1913f76ee892STomi Valkeinen 
1914f76ee892STomi Valkeinen 	DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
1915f76ee892STomi Valkeinen 			"tclk_zero %u (%uns)\n",
1916f76ee892STomi Valkeinen 			tlpx_half, ddr2ns(dsidev, tlpx_half),
1917f76ee892STomi Valkeinen 			tclk_trail, ddr2ns(dsidev, tclk_trail),
1918f76ee892STomi Valkeinen 			tclk_zero, ddr2ns(dsidev, tclk_zero));
1919f76ee892STomi Valkeinen 	DSSDBG("tclk_prepare %u (%uns)\n",
1920f76ee892STomi Valkeinen 			tclk_prepare, ddr2ns(dsidev, tclk_prepare));
1921f76ee892STomi Valkeinen 
1922f76ee892STomi Valkeinen 	/* program timings */
1923f76ee892STomi Valkeinen 
1924f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
1925f76ee892STomi Valkeinen 	r = FLD_MOD(r, ths_prepare, 31, 24);
1926f76ee892STomi Valkeinen 	r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
1927f76ee892STomi Valkeinen 	r = FLD_MOD(r, ths_trail, 15, 8);
1928f76ee892STomi Valkeinen 	r = FLD_MOD(r, ths_exit, 7, 0);
1929f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r);
1930f76ee892STomi Valkeinen 
1931f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
1932f76ee892STomi Valkeinen 	r = FLD_MOD(r, tlpx_half, 20, 16);
1933f76ee892STomi Valkeinen 	r = FLD_MOD(r, tclk_trail, 15, 8);
1934f76ee892STomi Valkeinen 	r = FLD_MOD(r, tclk_zero, 7, 0);
1935f76ee892STomi Valkeinen 
1936f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_PHY_DCC)) {
1937f76ee892STomi Valkeinen 		r = FLD_MOD(r, 0, 21, 21);	/* DCCEN = disable */
1938f76ee892STomi Valkeinen 		r = FLD_MOD(r, 1, 22, 22);	/* CLKINP_DIVBY2EN = enable */
1939f76ee892STomi Valkeinen 		r = FLD_MOD(r, 1, 23, 23);	/* CLKINP_SEL = enable */
1940f76ee892STomi Valkeinen 	}
1941f76ee892STomi Valkeinen 
1942f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r);
1943f76ee892STomi Valkeinen 
1944f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
1945f76ee892STomi Valkeinen 	r = FLD_MOD(r, tclk_prepare, 7, 0);
1946f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r);
1947f76ee892STomi Valkeinen }
1948f76ee892STomi Valkeinen 
1949f76ee892STomi Valkeinen /* lane masks have lane 0 at lsb. mask_p for positive lines, n for negative */
dsi_cio_enable_lane_override(struct platform_device * dsidev,unsigned mask_p,unsigned mask_n)1950f76ee892STomi Valkeinen static void dsi_cio_enable_lane_override(struct platform_device *dsidev,
1951f76ee892STomi Valkeinen 		unsigned mask_p, unsigned mask_n)
1952f76ee892STomi Valkeinen {
1953f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1954f76ee892STomi Valkeinen 	int i;
1955f76ee892STomi Valkeinen 	u32 l;
1956f76ee892STomi Valkeinen 	u8 lptxscp_start = dsi->num_lanes_supported == 3 ? 22 : 26;
1957f76ee892STomi Valkeinen 
1958f76ee892STomi Valkeinen 	l = 0;
1959f76ee892STomi Valkeinen 
1960f76ee892STomi Valkeinen 	for (i = 0; i < dsi->num_lanes_supported; ++i) {
1961f76ee892STomi Valkeinen 		unsigned p = dsi->lanes[i].polarity;
1962f76ee892STomi Valkeinen 
1963f76ee892STomi Valkeinen 		if (mask_p & (1 << i))
1964f76ee892STomi Valkeinen 			l |= 1 << (i * 2 + (p ? 0 : 1));
1965f76ee892STomi Valkeinen 
1966f76ee892STomi Valkeinen 		if (mask_n & (1 << i))
1967f76ee892STomi Valkeinen 			l |= 1 << (i * 2 + (p ? 1 : 0));
1968f76ee892STomi Valkeinen 	}
1969f76ee892STomi Valkeinen 
1970f76ee892STomi Valkeinen 	/*
1971f76ee892STomi Valkeinen 	 * Bits in REGLPTXSCPDAT4TO0DXDY:
1972f76ee892STomi Valkeinen 	 * 17: DY0 18: DX0
1973f76ee892STomi Valkeinen 	 * 19: DY1 20: DX1
1974f76ee892STomi Valkeinen 	 * 21: DY2 22: DX2
1975f76ee892STomi Valkeinen 	 * 23: DY3 24: DX3
1976f76ee892STomi Valkeinen 	 * 25: DY4 26: DX4
1977f76ee892STomi Valkeinen 	 */
1978f76ee892STomi Valkeinen 
1979f76ee892STomi Valkeinen 	/* Set the lane override configuration */
1980f76ee892STomi Valkeinen 
1981f76ee892STomi Valkeinen 	/* REGLPTXSCPDAT4TO0DXDY */
1982f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17);
1983f76ee892STomi Valkeinen 
1984f76ee892STomi Valkeinen 	/* Enable lane override */
1985f76ee892STomi Valkeinen 
1986f76ee892STomi Valkeinen 	/* ENLPTXSCPDAT */
1987f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27);
1988f76ee892STomi Valkeinen }
1989f76ee892STomi Valkeinen 
dsi_cio_disable_lane_override(struct platform_device * dsidev)1990f76ee892STomi Valkeinen static void dsi_cio_disable_lane_override(struct platform_device *dsidev)
1991f76ee892STomi Valkeinen {
1992f76ee892STomi Valkeinen 	/* Disable lane override */
1993f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */
1994f76ee892STomi Valkeinen 	/* Reset the lane override configuration */
1995f76ee892STomi Valkeinen 	/* REGLPTXSCPDAT4TO0DXDY */
1996f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17);
1997f76ee892STomi Valkeinen }
1998f76ee892STomi Valkeinen 
dsi_cio_wait_tx_clk_esc_reset(struct platform_device * dsidev)1999f76ee892STomi Valkeinen static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev)
2000f76ee892STomi Valkeinen {
2001f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2002f76ee892STomi Valkeinen 	int t, i;
2003f76ee892STomi Valkeinen 	bool in_use[DSI_MAX_NR_LANES];
2004f76ee892STomi Valkeinen 	static const u8 offsets_old[] = { 28, 27, 26 };
2005f76ee892STomi Valkeinen 	static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
2006f76ee892STomi Valkeinen 	const u8 *offsets;
2007f76ee892STomi Valkeinen 
2008f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC))
2009f76ee892STomi Valkeinen 		offsets = offsets_old;
2010f76ee892STomi Valkeinen 	else
2011f76ee892STomi Valkeinen 		offsets = offsets_new;
2012f76ee892STomi Valkeinen 
2013f76ee892STomi Valkeinen 	for (i = 0; i < dsi->num_lanes_supported; ++i)
2014f76ee892STomi Valkeinen 		in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
2015f76ee892STomi Valkeinen 
2016f76ee892STomi Valkeinen 	t = 100000;
2017f76ee892STomi Valkeinen 	while (true) {
2018f76ee892STomi Valkeinen 		u32 l;
2019f76ee892STomi Valkeinen 		int ok;
2020f76ee892STomi Valkeinen 
2021f76ee892STomi Valkeinen 		l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
2022f76ee892STomi Valkeinen 
2023f76ee892STomi Valkeinen 		ok = 0;
2024f76ee892STomi Valkeinen 		for (i = 0; i < dsi->num_lanes_supported; ++i) {
2025f76ee892STomi Valkeinen 			if (!in_use[i] || (l & (1 << offsets[i])))
2026f76ee892STomi Valkeinen 				ok++;
2027f76ee892STomi Valkeinen 		}
2028f76ee892STomi Valkeinen 
2029f76ee892STomi Valkeinen 		if (ok == dsi->num_lanes_supported)
2030f76ee892STomi Valkeinen 			break;
2031f76ee892STomi Valkeinen 
2032f76ee892STomi Valkeinen 		if (--t == 0) {
2033f76ee892STomi Valkeinen 			for (i = 0; i < dsi->num_lanes_supported; ++i) {
2034f76ee892STomi Valkeinen 				if (!in_use[i] || (l & (1 << offsets[i])))
2035f76ee892STomi Valkeinen 					continue;
2036f76ee892STomi Valkeinen 
2037f76ee892STomi Valkeinen 				DSSERR("CIO TXCLKESC%d domain not coming " \
2038f76ee892STomi Valkeinen 						"out of reset\n", i);
2039f76ee892STomi Valkeinen 			}
2040f76ee892STomi Valkeinen 			return -EIO;
2041f76ee892STomi Valkeinen 		}
2042f76ee892STomi Valkeinen 	}
2043f76ee892STomi Valkeinen 
2044f76ee892STomi Valkeinen 	return 0;
2045f76ee892STomi Valkeinen }
2046f76ee892STomi Valkeinen 
2047f76ee892STomi Valkeinen /* return bitmask of enabled lanes, lane0 being the lsb */
dsi_get_lane_mask(struct platform_device * dsidev)2048f76ee892STomi Valkeinen static unsigned dsi_get_lane_mask(struct platform_device *dsidev)
2049f76ee892STomi Valkeinen {
2050f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2051f76ee892STomi Valkeinen 	unsigned mask = 0;
2052f76ee892STomi Valkeinen 	int i;
2053f76ee892STomi Valkeinen 
2054f76ee892STomi Valkeinen 	for (i = 0; i < dsi->num_lanes_supported; ++i) {
2055f76ee892STomi Valkeinen 		if (dsi->lanes[i].function != DSI_LANE_UNUSED)
2056f76ee892STomi Valkeinen 			mask |= 1 << i;
2057f76ee892STomi Valkeinen 	}
2058f76ee892STomi Valkeinen 
2059f76ee892STomi Valkeinen 	return mask;
2060f76ee892STomi Valkeinen }
2061f76ee892STomi Valkeinen 
dsi_cio_init(struct platform_device * dsidev)2062f76ee892STomi Valkeinen static int dsi_cio_init(struct platform_device *dsidev)
2063f76ee892STomi Valkeinen {
2064f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2065f76ee892STomi Valkeinen 	int r;
2066f76ee892STomi Valkeinen 	u32 l;
2067f76ee892STomi Valkeinen 
2068f76ee892STomi Valkeinen 	DSSDBG("DSI CIO init starts");
2069f76ee892STomi Valkeinen 
2070f76ee892STomi Valkeinen 	r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
2071f76ee892STomi Valkeinen 	if (r)
2072f76ee892STomi Valkeinen 		return r;
2073f76ee892STomi Valkeinen 
2074f76ee892STomi Valkeinen 	dsi_enable_scp_clk(dsidev);
2075f76ee892STomi Valkeinen 
2076f76ee892STomi Valkeinen 	/* A dummy read using the SCP interface to any DSIPHY register is
2077f76ee892STomi Valkeinen 	 * required after DSIPHY reset to complete the reset of the DSI complex
2078f76ee892STomi Valkeinen 	 * I/O. */
2079f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
2080f76ee892STomi Valkeinen 
2081f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) {
2082f76ee892STomi Valkeinen 		DSSERR("CIO SCP Clock domain not coming out of reset.\n");
2083f76ee892STomi Valkeinen 		r = -EIO;
2084f76ee892STomi Valkeinen 		goto err_scp_clk_dom;
2085f76ee892STomi Valkeinen 	}
2086f76ee892STomi Valkeinen 
2087f76ee892STomi Valkeinen 	r = dsi_set_lane_config(dsidev);
2088f76ee892STomi Valkeinen 	if (r)
2089f76ee892STomi Valkeinen 		goto err_scp_clk_dom;
2090f76ee892STomi Valkeinen 
2091f76ee892STomi Valkeinen 	/* set TX STOP MODE timer to maximum for this operation */
2092f76ee892STomi Valkeinen 	l = dsi_read_reg(dsidev, DSI_TIMING1);
2093f76ee892STomi Valkeinen 	l = FLD_MOD(l, 1, 15, 15);	/* FORCE_TX_STOP_MODE_IO */
2094f76ee892STomi Valkeinen 	l = FLD_MOD(l, 1, 14, 14);	/* STOP_STATE_X16_IO */
2095f76ee892STomi Valkeinen 	l = FLD_MOD(l, 1, 13, 13);	/* STOP_STATE_X4_IO */
2096f76ee892STomi Valkeinen 	l = FLD_MOD(l, 0x1fff, 12, 0);	/* STOP_STATE_COUNTER_IO */
2097f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TIMING1, l);
2098f76ee892STomi Valkeinen 
2099f76ee892STomi Valkeinen 	if (dsi->ulps_enabled) {
2100f76ee892STomi Valkeinen 		unsigned mask_p;
2101f76ee892STomi Valkeinen 		int i;
2102f76ee892STomi Valkeinen 
2103f76ee892STomi Valkeinen 		DSSDBG("manual ulps exit\n");
2104f76ee892STomi Valkeinen 
2105f76ee892STomi Valkeinen 		/* ULPS is exited by Mark-1 state for 1ms, followed by
2106f76ee892STomi Valkeinen 		 * stop state. DSS HW cannot do this via the normal
2107f76ee892STomi Valkeinen 		 * ULPS exit sequence, as after reset the DSS HW thinks
2108f76ee892STomi Valkeinen 		 * that we are not in ULPS mode, and refuses to send the
2109f76ee892STomi Valkeinen 		 * sequence. So we need to send the ULPS exit sequence
2110f76ee892STomi Valkeinen 		 * manually by setting positive lines high and negative lines
2111f76ee892STomi Valkeinen 		 * low for 1ms.
2112f76ee892STomi Valkeinen 		 */
2113f76ee892STomi Valkeinen 
2114f76ee892STomi Valkeinen 		mask_p = 0;
2115f76ee892STomi Valkeinen 
2116f76ee892STomi Valkeinen 		for (i = 0; i < dsi->num_lanes_supported; ++i) {
2117f76ee892STomi Valkeinen 			if (dsi->lanes[i].function == DSI_LANE_UNUSED)
2118f76ee892STomi Valkeinen 				continue;
2119f76ee892STomi Valkeinen 			mask_p |= 1 << i;
2120f76ee892STomi Valkeinen 		}
2121f76ee892STomi Valkeinen 
2122f76ee892STomi Valkeinen 		dsi_cio_enable_lane_override(dsidev, mask_p, 0);
2123f76ee892STomi Valkeinen 	}
2124f76ee892STomi Valkeinen 
2125f76ee892STomi Valkeinen 	r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON);
2126f76ee892STomi Valkeinen 	if (r)
2127f76ee892STomi Valkeinen 		goto err_cio_pwr;
2128f76ee892STomi Valkeinen 
2129f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
2130f76ee892STomi Valkeinen 		DSSERR("CIO PWR clock domain not coming out of reset.\n");
2131f76ee892STomi Valkeinen 		r = -ENODEV;
2132f76ee892STomi Valkeinen 		goto err_cio_pwr_dom;
2133f76ee892STomi Valkeinen 	}
2134f76ee892STomi Valkeinen 
2135f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, true);
2136f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, false);
2137f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
2138f76ee892STomi Valkeinen 
2139f76ee892STomi Valkeinen 	r = dsi_cio_wait_tx_clk_esc_reset(dsidev);
2140f76ee892STomi Valkeinen 	if (r)
2141f76ee892STomi Valkeinen 		goto err_tx_clk_esc_rst;
2142f76ee892STomi Valkeinen 
2143f76ee892STomi Valkeinen 	if (dsi->ulps_enabled) {
2144f76ee892STomi Valkeinen 		/* Keep Mark-1 state for 1ms (as per DSI spec) */
2145f76ee892STomi Valkeinen 		ktime_t wait = ns_to_ktime(1000 * 1000);
2146f76ee892STomi Valkeinen 		set_current_state(TASK_UNINTERRUPTIBLE);
2147f76ee892STomi Valkeinen 		schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
2148f76ee892STomi Valkeinen 
2149f76ee892STomi Valkeinen 		/* Disable the override. The lanes should be set to Mark-11
2150f76ee892STomi Valkeinen 		 * state by the HW */
2151f76ee892STomi Valkeinen 		dsi_cio_disable_lane_override(dsidev);
2152f76ee892STomi Valkeinen 	}
2153f76ee892STomi Valkeinen 
2154f76ee892STomi Valkeinen 	/* FORCE_TX_STOP_MODE_IO */
2155f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15);
2156f76ee892STomi Valkeinen 
2157f76ee892STomi Valkeinen 	dsi_cio_timings(dsidev);
2158f76ee892STomi Valkeinen 
2159f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
2160f76ee892STomi Valkeinen 		/* DDR_CLK_ALWAYS_ON */
2161f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_CLK_CTRL,
2162f76ee892STomi Valkeinen 			dsi->vm_timings.ddr_clk_always_on, 13, 13);
2163f76ee892STomi Valkeinen 	}
2164f76ee892STomi Valkeinen 
2165f76ee892STomi Valkeinen 	dsi->ulps_enabled = false;
2166f76ee892STomi Valkeinen 
2167f76ee892STomi Valkeinen 	DSSDBG("CIO init done\n");
2168f76ee892STomi Valkeinen 
2169f76ee892STomi Valkeinen 	return 0;
2170f76ee892STomi Valkeinen 
2171f76ee892STomi Valkeinen err_tx_clk_esc_rst:
2172f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */
2173f76ee892STomi Valkeinen err_cio_pwr_dom:
2174f76ee892STomi Valkeinen 	dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
2175f76ee892STomi Valkeinen err_cio_pwr:
2176f76ee892STomi Valkeinen 	if (dsi->ulps_enabled)
2177f76ee892STomi Valkeinen 		dsi_cio_disable_lane_override(dsidev);
2178f76ee892STomi Valkeinen err_scp_clk_dom:
2179f76ee892STomi Valkeinen 	dsi_disable_scp_clk(dsidev);
2180f76ee892STomi Valkeinen 	dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
2181f76ee892STomi Valkeinen 	return r;
2182f76ee892STomi Valkeinen }
2183f76ee892STomi Valkeinen 
dsi_cio_uninit(struct platform_device * dsidev)2184f76ee892STomi Valkeinen static void dsi_cio_uninit(struct platform_device *dsidev)
2185f76ee892STomi Valkeinen {
2186f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2187f76ee892STomi Valkeinen 
2188f76ee892STomi Valkeinen 	/* DDR_CLK_ALWAYS_ON */
2189f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
2190f76ee892STomi Valkeinen 
2191f76ee892STomi Valkeinen 	dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
2192f76ee892STomi Valkeinen 	dsi_disable_scp_clk(dsidev);
2193f76ee892STomi Valkeinen 	dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
2194f76ee892STomi Valkeinen }
2195f76ee892STomi Valkeinen 
dsi_config_tx_fifo(struct platform_device * dsidev,enum fifo_size size1,enum fifo_size size2,enum fifo_size size3,enum fifo_size size4)2196f76ee892STomi Valkeinen static void dsi_config_tx_fifo(struct platform_device *dsidev,
2197f76ee892STomi Valkeinen 		enum fifo_size size1, enum fifo_size size2,
2198f76ee892STomi Valkeinen 		enum fifo_size size3, enum fifo_size size4)
2199f76ee892STomi Valkeinen {
2200f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2201f76ee892STomi Valkeinen 	u32 r = 0;
2202f76ee892STomi Valkeinen 	int add = 0;
2203f76ee892STomi Valkeinen 	int i;
2204f76ee892STomi Valkeinen 
2205f76ee892STomi Valkeinen 	dsi->vc[0].tx_fifo_size = size1;
2206f76ee892STomi Valkeinen 	dsi->vc[1].tx_fifo_size = size2;
2207f76ee892STomi Valkeinen 	dsi->vc[2].tx_fifo_size = size3;
2208f76ee892STomi Valkeinen 	dsi->vc[3].tx_fifo_size = size4;
2209f76ee892STomi Valkeinen 
2210f76ee892STomi Valkeinen 	for (i = 0; i < 4; i++) {
2211f76ee892STomi Valkeinen 		u8 v;
2212f76ee892STomi Valkeinen 		int size = dsi->vc[i].tx_fifo_size;
2213f76ee892STomi Valkeinen 
2214f76ee892STomi Valkeinen 		if (add + size > 4) {
2215f76ee892STomi Valkeinen 			DSSERR("Illegal FIFO configuration\n");
2216f76ee892STomi Valkeinen 			BUG();
2217f76ee892STomi Valkeinen 			return;
2218f76ee892STomi Valkeinen 		}
2219f76ee892STomi Valkeinen 
2220f76ee892STomi Valkeinen 		v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
2221f76ee892STomi Valkeinen 		r |= v << (8 * i);
2222f76ee892STomi Valkeinen 		/*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
2223f76ee892STomi Valkeinen 		add += size;
2224f76ee892STomi Valkeinen 	}
2225f76ee892STomi Valkeinen 
2226f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r);
2227f76ee892STomi Valkeinen }
2228f76ee892STomi Valkeinen 
dsi_config_rx_fifo(struct platform_device * dsidev,enum fifo_size size1,enum fifo_size size2,enum fifo_size size3,enum fifo_size size4)2229f76ee892STomi Valkeinen static void dsi_config_rx_fifo(struct platform_device *dsidev,
2230f76ee892STomi Valkeinen 		enum fifo_size size1, enum fifo_size size2,
2231f76ee892STomi Valkeinen 		enum fifo_size size3, enum fifo_size size4)
2232f76ee892STomi Valkeinen {
2233f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2234f76ee892STomi Valkeinen 	u32 r = 0;
2235f76ee892STomi Valkeinen 	int add = 0;
2236f76ee892STomi Valkeinen 	int i;
2237f76ee892STomi Valkeinen 
2238f76ee892STomi Valkeinen 	dsi->vc[0].rx_fifo_size = size1;
2239f76ee892STomi Valkeinen 	dsi->vc[1].rx_fifo_size = size2;
2240f76ee892STomi Valkeinen 	dsi->vc[2].rx_fifo_size = size3;
2241f76ee892STomi Valkeinen 	dsi->vc[3].rx_fifo_size = size4;
2242f76ee892STomi Valkeinen 
2243f76ee892STomi Valkeinen 	for (i = 0; i < 4; i++) {
2244f76ee892STomi Valkeinen 		u8 v;
2245f76ee892STomi Valkeinen 		int size = dsi->vc[i].rx_fifo_size;
2246f76ee892STomi Valkeinen 
2247f76ee892STomi Valkeinen 		if (add + size > 4) {
2248f76ee892STomi Valkeinen 			DSSERR("Illegal FIFO configuration\n");
2249f76ee892STomi Valkeinen 			BUG();
2250f76ee892STomi Valkeinen 			return;
2251f76ee892STomi Valkeinen 		}
2252f76ee892STomi Valkeinen 
2253f76ee892STomi Valkeinen 		v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
2254f76ee892STomi Valkeinen 		r |= v << (8 * i);
2255f76ee892STomi Valkeinen 		/*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
2256f76ee892STomi Valkeinen 		add += size;
2257f76ee892STomi Valkeinen 	}
2258f76ee892STomi Valkeinen 
2259f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r);
2260f76ee892STomi Valkeinen }
2261f76ee892STomi Valkeinen 
dsi_force_tx_stop_mode_io(struct platform_device * dsidev)2262f76ee892STomi Valkeinen static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev)
2263f76ee892STomi Valkeinen {
2264f76ee892STomi Valkeinen 	u32 r;
2265f76ee892STomi Valkeinen 
2266f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_TIMING1);
2267f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);	/* FORCE_TX_STOP_MODE_IO */
2268f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TIMING1, r);
2269f76ee892STomi Valkeinen 
2270f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) {
2271f76ee892STomi Valkeinen 		DSSERR("TX_STOP bit not going down\n");
2272f76ee892STomi Valkeinen 		return -EIO;
2273f76ee892STomi Valkeinen 	}
2274f76ee892STomi Valkeinen 
2275f76ee892STomi Valkeinen 	return 0;
2276f76ee892STomi Valkeinen }
2277f76ee892STomi Valkeinen 
dsi_vc_is_enabled(struct platform_device * dsidev,int channel)2278f76ee892STomi Valkeinen static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel)
2279f76ee892STomi Valkeinen {
2280f76ee892STomi Valkeinen 	return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0);
2281f76ee892STomi Valkeinen }
2282f76ee892STomi Valkeinen 
dsi_packet_sent_handler_vp(void * data,u32 mask)2283f76ee892STomi Valkeinen static void dsi_packet_sent_handler_vp(void *data, u32 mask)
2284f76ee892STomi Valkeinen {
2285f76ee892STomi Valkeinen 	struct dsi_packet_sent_handler_data *vp_data =
2286f76ee892STomi Valkeinen 		(struct dsi_packet_sent_handler_data *) data;
2287f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev);
2288f76ee892STomi Valkeinen 	const int channel = dsi->update_channel;
2289f76ee892STomi Valkeinen 	u8 bit = dsi->te_enabled ? 30 : 31;
2290f76ee892STomi Valkeinen 
2291f76ee892STomi Valkeinen 	if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0)
2292f76ee892STomi Valkeinen 		complete(vp_data->completion);
2293f76ee892STomi Valkeinen }
2294f76ee892STomi Valkeinen 
dsi_sync_vc_vp(struct platform_device * dsidev,int channel)2295f76ee892STomi Valkeinen static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel)
2296f76ee892STomi Valkeinen {
2297f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2298f76ee892STomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
2299f76ee892STomi Valkeinen 	struct dsi_packet_sent_handler_data vp_data = {
2300f76ee892STomi Valkeinen 		.dsidev = dsidev,
2301f76ee892STomi Valkeinen 		.completion = &completion
2302f76ee892STomi Valkeinen 	};
2303f76ee892STomi Valkeinen 	int r = 0;
2304f76ee892STomi Valkeinen 	u8 bit;
2305f76ee892STomi Valkeinen 
2306f76ee892STomi Valkeinen 	bit = dsi->te_enabled ? 30 : 31;
2307f76ee892STomi Valkeinen 
2308f76ee892STomi Valkeinen 	r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
2309f76ee892STomi Valkeinen 		&vp_data, DSI_VC_IRQ_PACKET_SENT);
2310f76ee892STomi Valkeinen 	if (r)
2311f76ee892STomi Valkeinen 		goto err0;
2312f76ee892STomi Valkeinen 
2313f76ee892STomi Valkeinen 	/* Wait for completion only if TE_EN/TE_START is still set */
2314f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) {
2315f76ee892STomi Valkeinen 		if (wait_for_completion_timeout(&completion,
2316f76ee892STomi Valkeinen 				msecs_to_jiffies(10)) == 0) {
2317f76ee892STomi Valkeinen 			DSSERR("Failed to complete previous frame transfer\n");
2318f76ee892STomi Valkeinen 			r = -EIO;
2319f76ee892STomi Valkeinen 			goto err1;
2320f76ee892STomi Valkeinen 		}
2321f76ee892STomi Valkeinen 	}
2322f76ee892STomi Valkeinen 
2323f76ee892STomi Valkeinen 	dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
2324f76ee892STomi Valkeinen 		&vp_data, DSI_VC_IRQ_PACKET_SENT);
2325f76ee892STomi Valkeinen 
2326f76ee892STomi Valkeinen 	return 0;
2327f76ee892STomi Valkeinen err1:
2328f76ee892STomi Valkeinen 	dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
2329f76ee892STomi Valkeinen 		&vp_data, DSI_VC_IRQ_PACKET_SENT);
2330f76ee892STomi Valkeinen err0:
2331f76ee892STomi Valkeinen 	return r;
2332f76ee892STomi Valkeinen }
2333f76ee892STomi Valkeinen 
dsi_packet_sent_handler_l4(void * data,u32 mask)2334f76ee892STomi Valkeinen static void dsi_packet_sent_handler_l4(void *data, u32 mask)
2335f76ee892STomi Valkeinen {
2336f76ee892STomi Valkeinen 	struct dsi_packet_sent_handler_data *l4_data =
2337f76ee892STomi Valkeinen 		(struct dsi_packet_sent_handler_data *) data;
2338f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev);
2339f76ee892STomi Valkeinen 	const int channel = dsi->update_channel;
2340f76ee892STomi Valkeinen 
2341f76ee892STomi Valkeinen 	if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0)
2342f76ee892STomi Valkeinen 		complete(l4_data->completion);
2343f76ee892STomi Valkeinen }
2344f76ee892STomi Valkeinen 
dsi_sync_vc_l4(struct platform_device * dsidev,int channel)2345f76ee892STomi Valkeinen static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel)
2346f76ee892STomi Valkeinen {
2347f76ee892STomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
2348f76ee892STomi Valkeinen 	struct dsi_packet_sent_handler_data l4_data = {
2349f76ee892STomi Valkeinen 		.dsidev = dsidev,
2350f76ee892STomi Valkeinen 		.completion = &completion
2351f76ee892STomi Valkeinen 	};
2352f76ee892STomi Valkeinen 	int r = 0;
2353f76ee892STomi Valkeinen 
2354f76ee892STomi Valkeinen 	r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
2355f76ee892STomi Valkeinen 		&l4_data, DSI_VC_IRQ_PACKET_SENT);
2356f76ee892STomi Valkeinen 	if (r)
2357f76ee892STomi Valkeinen 		goto err0;
2358f76ee892STomi Valkeinen 
2359f76ee892STomi Valkeinen 	/* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */
2360f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) {
2361f76ee892STomi Valkeinen 		if (wait_for_completion_timeout(&completion,
2362f76ee892STomi Valkeinen 				msecs_to_jiffies(10)) == 0) {
2363f76ee892STomi Valkeinen 			DSSERR("Failed to complete previous l4 transfer\n");
2364f76ee892STomi Valkeinen 			r = -EIO;
2365f76ee892STomi Valkeinen 			goto err1;
2366f76ee892STomi Valkeinen 		}
2367f76ee892STomi Valkeinen 	}
2368f76ee892STomi Valkeinen 
2369f76ee892STomi Valkeinen 	dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
2370f76ee892STomi Valkeinen 		&l4_data, DSI_VC_IRQ_PACKET_SENT);
2371f76ee892STomi Valkeinen 
2372f76ee892STomi Valkeinen 	return 0;
2373f76ee892STomi Valkeinen err1:
2374f76ee892STomi Valkeinen 	dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
2375f76ee892STomi Valkeinen 		&l4_data, DSI_VC_IRQ_PACKET_SENT);
2376f76ee892STomi Valkeinen err0:
2377f76ee892STomi Valkeinen 	return r;
2378f76ee892STomi Valkeinen }
2379f76ee892STomi Valkeinen 
dsi_sync_vc(struct platform_device * dsidev,int channel)2380f76ee892STomi Valkeinen static int dsi_sync_vc(struct platform_device *dsidev, int channel)
2381f76ee892STomi Valkeinen {
2382f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2383f76ee892STomi Valkeinen 
2384f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
2385f76ee892STomi Valkeinen 
2386f76ee892STomi Valkeinen 	if (!dsi_vc_is_enabled(dsidev, channel))
2387f76ee892STomi Valkeinen 		return 0;
2388f76ee892STomi Valkeinen 
2389f76ee892STomi Valkeinen 	switch (dsi->vc[channel].source) {
2390f76ee892STomi Valkeinen 	case DSI_VC_SOURCE_VP:
2391f76ee892STomi Valkeinen 		return dsi_sync_vc_vp(dsidev, channel);
2392f76ee892STomi Valkeinen 	case DSI_VC_SOURCE_L4:
2393f76ee892STomi Valkeinen 		return dsi_sync_vc_l4(dsidev, channel);
2394f76ee892STomi Valkeinen 	default:
2395f76ee892STomi Valkeinen 		BUG();
2396f76ee892STomi Valkeinen 		return -EINVAL;
2397f76ee892STomi Valkeinen 	}
2398f76ee892STomi Valkeinen }
2399f76ee892STomi Valkeinen 
dsi_vc_enable(struct platform_device * dsidev,int channel,bool enable)2400f76ee892STomi Valkeinen static int dsi_vc_enable(struct platform_device *dsidev, int channel,
2401f76ee892STomi Valkeinen 		bool enable)
2402f76ee892STomi Valkeinen {
2403f76ee892STomi Valkeinen 	DSSDBG("dsi_vc_enable channel %d, enable %d\n",
2404f76ee892STomi Valkeinen 			channel, enable);
2405f76ee892STomi Valkeinen 
2406f76ee892STomi Valkeinen 	enable = enable ? 1 : 0;
2407f76ee892STomi Valkeinen 
2408f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0);
2409f76ee892STomi Valkeinen 
2410f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel),
2411f76ee892STomi Valkeinen 		0, enable) != enable) {
2412f76ee892STomi Valkeinen 			DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
2413f76ee892STomi Valkeinen 			return -EIO;
2414f76ee892STomi Valkeinen 	}
2415f76ee892STomi Valkeinen 
2416f76ee892STomi Valkeinen 	return 0;
2417f76ee892STomi Valkeinen }
2418f76ee892STomi Valkeinen 
dsi_vc_initial_config(struct platform_device * dsidev,int channel)2419f76ee892STomi Valkeinen static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
2420f76ee892STomi Valkeinen {
2421f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2422f76ee892STomi Valkeinen 	u32 r;
2423f76ee892STomi Valkeinen 
2424f76ee892STomi Valkeinen 	DSSDBG("Initial config of virtual channel %d", channel);
2425f76ee892STomi Valkeinen 
2426f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
2427f76ee892STomi Valkeinen 
2428f76ee892STomi Valkeinen 	if (FLD_GET(r, 15, 15)) /* VC_BUSY */
2429f76ee892STomi Valkeinen 		DSSERR("VC(%d) busy when trying to configure it!\n",
2430f76ee892STomi Valkeinen 				channel);
2431f76ee892STomi Valkeinen 
2432f76ee892STomi Valkeinen 	r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
2433f76ee892STomi Valkeinen 	r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN  */
2434f76ee892STomi Valkeinen 	r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
2435f76ee892STomi Valkeinen 	r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
2436f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
2437f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
2438f76ee892STomi Valkeinen 	r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
2439f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH))
2440f76ee892STomi Valkeinen 		r = FLD_MOD(r, 3, 11, 10);	/* OCP_WIDTH = 32 bit */
2441f76ee892STomi Valkeinen 
2442f76ee892STomi Valkeinen 	r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
2443f76ee892STomi Valkeinen 	r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
2444f76ee892STomi Valkeinen 
2445f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r);
2446f76ee892STomi Valkeinen 
2447f76ee892STomi Valkeinen 	dsi->vc[channel].source = DSI_VC_SOURCE_L4;
2448f76ee892STomi Valkeinen }
2449f76ee892STomi Valkeinen 
dsi_vc_config_source(struct platform_device * dsidev,int channel,enum dsi_vc_source source)2450f76ee892STomi Valkeinen static int dsi_vc_config_source(struct platform_device *dsidev, int channel,
2451f76ee892STomi Valkeinen 		enum dsi_vc_source source)
2452f76ee892STomi Valkeinen {
2453f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2454f76ee892STomi Valkeinen 
2455f76ee892STomi Valkeinen 	if (dsi->vc[channel].source == source)
2456f76ee892STomi Valkeinen 		return 0;
2457f76ee892STomi Valkeinen 
2458f76ee892STomi Valkeinen 	DSSDBG("Source config of virtual channel %d", channel);
2459f76ee892STomi Valkeinen 
2460f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, channel);
2461f76ee892STomi Valkeinen 
2462f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, channel, 0);
2463f76ee892STomi Valkeinen 
2464f76ee892STomi Valkeinen 	/* VC_BUSY */
2465f76ee892STomi Valkeinen 	if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) {
2466f76ee892STomi Valkeinen 		DSSERR("vc(%d) busy when trying to config for VP\n", channel);
2467f76ee892STomi Valkeinen 		return -EIO;
2468f76ee892STomi Valkeinen 	}
2469f76ee892STomi Valkeinen 
2470f76ee892STomi Valkeinen 	/* SOURCE, 0 = L4, 1 = video port */
2471f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1);
2472f76ee892STomi Valkeinen 
2473f76ee892STomi Valkeinen 	/* DCS_CMD_ENABLE */
2474f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
2475f76ee892STomi Valkeinen 		bool enable = source == DSI_VC_SOURCE_VP;
2476f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30);
2477f76ee892STomi Valkeinen 	}
2478f76ee892STomi Valkeinen 
2479f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, channel, 1);
2480f76ee892STomi Valkeinen 
2481f76ee892STomi Valkeinen 	dsi->vc[channel].source = source;
2482f76ee892STomi Valkeinen 
2483f76ee892STomi Valkeinen 	return 0;
2484f76ee892STomi Valkeinen }
2485f76ee892STomi Valkeinen 
dsi_vc_enable_hs(struct omap_dss_device * dssdev,int channel,bool enable)2486f76ee892STomi Valkeinen static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
2487f76ee892STomi Valkeinen 		bool enable)
2488f76ee892STomi Valkeinen {
2489f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2490f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2491f76ee892STomi Valkeinen 
2492f76ee892STomi Valkeinen 	DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
2493f76ee892STomi Valkeinen 
2494f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
2495f76ee892STomi Valkeinen 
2496f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, channel, 0);
2497f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, 0);
2498f76ee892STomi Valkeinen 
2499f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9);
2500f76ee892STomi Valkeinen 
2501f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, channel, 1);
2502f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, 1);
2503f76ee892STomi Valkeinen 
2504f76ee892STomi Valkeinen 	dsi_force_tx_stop_mode_io(dsidev);
2505f76ee892STomi Valkeinen 
2506f76ee892STomi Valkeinen 	/* start the DDR clock by sending a NULL packet */
2507f76ee892STomi Valkeinen 	if (dsi->vm_timings.ddr_clk_always_on && enable)
2508f76ee892STomi Valkeinen 		dsi_vc_send_null(dssdev, channel);
2509f76ee892STomi Valkeinen }
2510f76ee892STomi Valkeinen 
dsi_vc_flush_long_data(struct platform_device * dsidev,int channel)2511f76ee892STomi Valkeinen static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel)
2512f76ee892STomi Valkeinen {
2513f76ee892STomi Valkeinen 	while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
2514f76ee892STomi Valkeinen 		u32 val;
2515f76ee892STomi Valkeinen 		val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
2516f76ee892STomi Valkeinen 		DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
2517f76ee892STomi Valkeinen 				(val >> 0) & 0xff,
2518f76ee892STomi Valkeinen 				(val >> 8) & 0xff,
2519f76ee892STomi Valkeinen 				(val >> 16) & 0xff,
2520f76ee892STomi Valkeinen 				(val >> 24) & 0xff);
2521f76ee892STomi Valkeinen 	}
2522f76ee892STomi Valkeinen }
2523f76ee892STomi Valkeinen 
dsi_show_rx_ack_with_err(u16 err)2524f76ee892STomi Valkeinen static void dsi_show_rx_ack_with_err(u16 err)
2525f76ee892STomi Valkeinen {
2526f76ee892STomi Valkeinen 	DSSERR("\tACK with ERROR (%#x):\n", err);
2527f76ee892STomi Valkeinen 	if (err & (1 << 0))
2528f76ee892STomi Valkeinen 		DSSERR("\t\tSoT Error\n");
2529f76ee892STomi Valkeinen 	if (err & (1 << 1))
2530f76ee892STomi Valkeinen 		DSSERR("\t\tSoT Sync Error\n");
2531f76ee892STomi Valkeinen 	if (err & (1 << 2))
2532f76ee892STomi Valkeinen 		DSSERR("\t\tEoT Sync Error\n");
2533f76ee892STomi Valkeinen 	if (err & (1 << 3))
2534f76ee892STomi Valkeinen 		DSSERR("\t\tEscape Mode Entry Command Error\n");
2535f76ee892STomi Valkeinen 	if (err & (1 << 4))
2536f76ee892STomi Valkeinen 		DSSERR("\t\tLP Transmit Sync Error\n");
2537f76ee892STomi Valkeinen 	if (err & (1 << 5))
2538f76ee892STomi Valkeinen 		DSSERR("\t\tHS Receive Timeout Error\n");
2539f76ee892STomi Valkeinen 	if (err & (1 << 6))
2540f76ee892STomi Valkeinen 		DSSERR("\t\tFalse Control Error\n");
2541f76ee892STomi Valkeinen 	if (err & (1 << 7))
2542f76ee892STomi Valkeinen 		DSSERR("\t\t(reserved7)\n");
2543f76ee892STomi Valkeinen 	if (err & (1 << 8))
2544f76ee892STomi Valkeinen 		DSSERR("\t\tECC Error, single-bit (corrected)\n");
2545f76ee892STomi Valkeinen 	if (err & (1 << 9))
2546f76ee892STomi Valkeinen 		DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
2547f76ee892STomi Valkeinen 	if (err & (1 << 10))
2548f76ee892STomi Valkeinen 		DSSERR("\t\tChecksum Error\n");
2549f76ee892STomi Valkeinen 	if (err & (1 << 11))
2550f76ee892STomi Valkeinen 		DSSERR("\t\tData type not recognized\n");
2551f76ee892STomi Valkeinen 	if (err & (1 << 12))
2552f76ee892STomi Valkeinen 		DSSERR("\t\tInvalid VC ID\n");
2553f76ee892STomi Valkeinen 	if (err & (1 << 13))
2554f76ee892STomi Valkeinen 		DSSERR("\t\tInvalid Transmission Length\n");
2555f76ee892STomi Valkeinen 	if (err & (1 << 14))
2556f76ee892STomi Valkeinen 		DSSERR("\t\t(reserved14)\n");
2557f76ee892STomi Valkeinen 	if (err & (1 << 15))
2558f76ee892STomi Valkeinen 		DSSERR("\t\tDSI Protocol Violation\n");
2559f76ee892STomi Valkeinen }
2560f76ee892STomi Valkeinen 
dsi_vc_flush_receive_data(struct platform_device * dsidev,int channel)2561f76ee892STomi Valkeinen static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev,
2562f76ee892STomi Valkeinen 		int channel)
2563f76ee892STomi Valkeinen {
2564f76ee892STomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2565f76ee892STomi Valkeinen 	while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
2566f76ee892STomi Valkeinen 		u32 val;
2567f76ee892STomi Valkeinen 		u8 dt;
2568f76ee892STomi Valkeinen 		val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
2569f76ee892STomi Valkeinen 		DSSERR("\trawval %#08x\n", val);
2570f76ee892STomi Valkeinen 		dt = FLD_GET(val, 5, 0);
2571f76ee892STomi Valkeinen 		if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
2572f76ee892STomi Valkeinen 			u16 err = FLD_GET(val, 23, 8);
2573f76ee892STomi Valkeinen 			dsi_show_rx_ack_with_err(err);
2574f76ee892STomi Valkeinen 		} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
2575f76ee892STomi Valkeinen 			DSSERR("\tDCS short response, 1 byte: %#x\n",
2576f76ee892STomi Valkeinen 					FLD_GET(val, 23, 8));
2577f76ee892STomi Valkeinen 		} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
2578f76ee892STomi Valkeinen 			DSSERR("\tDCS short response, 2 byte: %#x\n",
2579f76ee892STomi Valkeinen 					FLD_GET(val, 23, 8));
2580f76ee892STomi Valkeinen 		} else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
2581f76ee892STomi Valkeinen 			DSSERR("\tDCS long response, len %d\n",
2582f76ee892STomi Valkeinen 					FLD_GET(val, 23, 8));
2583f76ee892STomi Valkeinen 			dsi_vc_flush_long_data(dsidev, channel);
2584f76ee892STomi Valkeinen 		} else {
2585f76ee892STomi Valkeinen 			DSSERR("\tunknown datatype 0x%02x\n", dt);
2586f76ee892STomi Valkeinen 		}
2587f76ee892STomi Valkeinen 	}
2588f76ee892STomi Valkeinen 	return 0;
2589f76ee892STomi Valkeinen }
2590f76ee892STomi Valkeinen 
dsi_vc_send_bta(struct platform_device * dsidev,int channel)2591f76ee892STomi Valkeinen static int dsi_vc_send_bta(struct platform_device *dsidev, int channel)
2592f76ee892STomi Valkeinen {
2593f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2594f76ee892STomi Valkeinen 
2595f76ee892STomi Valkeinen 	if (dsi->debug_write || dsi->debug_read)
2596f76ee892STomi Valkeinen 		DSSDBG("dsi_vc_send_bta %d\n", channel);
2597f76ee892STomi Valkeinen 
2598f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
2599f76ee892STomi Valkeinen 
2600f76ee892STomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2601f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
2602f76ee892STomi Valkeinen 		DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
2603f76ee892STomi Valkeinen 		dsi_vc_flush_receive_data(dsidev, channel);
2604f76ee892STomi Valkeinen 	}
2605f76ee892STomi Valkeinen 
2606f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
2607f76ee892STomi Valkeinen 
2608f76ee892STomi Valkeinen 	/* flush posted write */
2609f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
2610f76ee892STomi Valkeinen 
2611f76ee892STomi Valkeinen 	return 0;
2612f76ee892STomi Valkeinen }
2613f76ee892STomi Valkeinen 
dsi_vc_send_bta_sync(struct omap_dss_device * dssdev,int channel)2614f76ee892STomi Valkeinen static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel)
2615f76ee892STomi Valkeinen {
2616f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2617f76ee892STomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
2618f76ee892STomi Valkeinen 	int r = 0;
2619f76ee892STomi Valkeinen 	u32 err;
2620f76ee892STomi Valkeinen 
2621f76ee892STomi Valkeinen 	r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler,
2622f76ee892STomi Valkeinen 			&completion, DSI_VC_IRQ_BTA);
2623f76ee892STomi Valkeinen 	if (r)
2624f76ee892STomi Valkeinen 		goto err0;
2625f76ee892STomi Valkeinen 
2626f76ee892STomi Valkeinen 	r = dsi_register_isr(dsidev, dsi_completion_handler, &completion,
2627f76ee892STomi Valkeinen 			DSI_IRQ_ERROR_MASK);
2628f76ee892STomi Valkeinen 	if (r)
2629f76ee892STomi Valkeinen 		goto err1;
2630f76ee892STomi Valkeinen 
2631f76ee892STomi Valkeinen 	r = dsi_vc_send_bta(dsidev, channel);
2632f76ee892STomi Valkeinen 	if (r)
2633f76ee892STomi Valkeinen 		goto err2;
2634f76ee892STomi Valkeinen 
2635f76ee892STomi Valkeinen 	if (wait_for_completion_timeout(&completion,
2636f76ee892STomi Valkeinen 				msecs_to_jiffies(500)) == 0) {
2637f76ee892STomi Valkeinen 		DSSERR("Failed to receive BTA\n");
2638f76ee892STomi Valkeinen 		r = -EIO;
2639f76ee892STomi Valkeinen 		goto err2;
2640f76ee892STomi Valkeinen 	}
2641f76ee892STomi Valkeinen 
2642f76ee892STomi Valkeinen 	err = dsi_get_errors(dsidev);
2643f76ee892STomi Valkeinen 	if (err) {
2644f76ee892STomi Valkeinen 		DSSERR("Error while sending BTA: %x\n", err);
2645f76ee892STomi Valkeinen 		r = -EIO;
2646f76ee892STomi Valkeinen 		goto err2;
2647f76ee892STomi Valkeinen 	}
2648f76ee892STomi Valkeinen err2:
2649f76ee892STomi Valkeinen 	dsi_unregister_isr(dsidev, dsi_completion_handler, &completion,
2650f76ee892STomi Valkeinen 			DSI_IRQ_ERROR_MASK);
2651f76ee892STomi Valkeinen err1:
2652f76ee892STomi Valkeinen 	dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler,
2653f76ee892STomi Valkeinen 			&completion, DSI_VC_IRQ_BTA);
2654f76ee892STomi Valkeinen err0:
2655f76ee892STomi Valkeinen 	return r;
2656f76ee892STomi Valkeinen }
2657f76ee892STomi Valkeinen 
dsi_vc_write_long_header(struct platform_device * dsidev,int channel,u8 data_type,u16 len,u8 ecc)2658f76ee892STomi Valkeinen static inline void dsi_vc_write_long_header(struct platform_device *dsidev,
2659f76ee892STomi Valkeinen 		int channel, u8 data_type, u16 len, u8 ecc)
2660f76ee892STomi Valkeinen {
2661f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2662f76ee892STomi Valkeinen 	u32 val;
2663f76ee892STomi Valkeinen 	u8 data_id;
2664f76ee892STomi Valkeinen 
2665f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
2666f76ee892STomi Valkeinen 
2667f76ee892STomi Valkeinen 	data_id = data_type | dsi->vc[channel].vc_id << 6;
2668f76ee892STomi Valkeinen 
2669f76ee892STomi Valkeinen 	val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
2670f76ee892STomi Valkeinen 		FLD_VAL(ecc, 31, 24);
2671f76ee892STomi Valkeinen 
2672f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val);
2673f76ee892STomi Valkeinen }
2674f76ee892STomi Valkeinen 
dsi_vc_write_long_payload(struct platform_device * dsidev,int channel,u8 b1,u8 b2,u8 b3,u8 b4)2675f76ee892STomi Valkeinen static inline void dsi_vc_write_long_payload(struct platform_device *dsidev,
2676f76ee892STomi Valkeinen 		int channel, u8 b1, u8 b2, u8 b3, u8 b4)
2677f76ee892STomi Valkeinen {
2678f76ee892STomi Valkeinen 	u32 val;
2679f76ee892STomi Valkeinen 
2680f76ee892STomi Valkeinen 	val = b4 << 24 | b3 << 16 | b2 << 8  | b1 << 0;
2681f76ee892STomi Valkeinen 
2682f76ee892STomi Valkeinen /*	DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
2683f76ee892STomi Valkeinen 			b1, b2, b3, b4, val); */
2684f76ee892STomi Valkeinen 
2685f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
2686f76ee892STomi Valkeinen }
2687f76ee892STomi Valkeinen 
dsi_vc_send_long(struct platform_device * dsidev,int channel,u8 data_type,u8 * data,u16 len,u8 ecc)2688f76ee892STomi Valkeinen static int dsi_vc_send_long(struct platform_device *dsidev, int channel,
2689f76ee892STomi Valkeinen 		u8 data_type, u8 *data, u16 len, u8 ecc)
2690f76ee892STomi Valkeinen {
2691f76ee892STomi Valkeinen 	/*u32 val; */
2692f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2693f76ee892STomi Valkeinen 	int i;
2694f76ee892STomi Valkeinen 	u8 *p;
2695f76ee892STomi Valkeinen 	int r = 0;
2696f76ee892STomi Valkeinen 	u8 b1, b2, b3, b4;
2697f76ee892STomi Valkeinen 
2698f76ee892STomi Valkeinen 	if (dsi->debug_write)
2699f76ee892STomi Valkeinen 		DSSDBG("dsi_vc_send_long, %d bytes\n", len);
2700f76ee892STomi Valkeinen 
2701f76ee892STomi Valkeinen 	/* len + header */
2702f76ee892STomi Valkeinen 	if (dsi->vc[channel].tx_fifo_size * 32 * 4 < len + 4) {
2703f76ee892STomi Valkeinen 		DSSERR("unable to send long packet: packet too long.\n");
2704f76ee892STomi Valkeinen 		return -EINVAL;
2705f76ee892STomi Valkeinen 	}
2706f76ee892STomi Valkeinen 
2707f76ee892STomi Valkeinen 	dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
2708f76ee892STomi Valkeinen 
2709f76ee892STomi Valkeinen 	dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc);
2710f76ee892STomi Valkeinen 
2711f76ee892STomi Valkeinen 	p = data;
2712f76ee892STomi Valkeinen 	for (i = 0; i < len >> 2; i++) {
2713f76ee892STomi Valkeinen 		if (dsi->debug_write)
2714f76ee892STomi Valkeinen 			DSSDBG("\tsending full packet %d\n", i);
2715f76ee892STomi Valkeinen 
2716f76ee892STomi Valkeinen 		b1 = *p++;
2717f76ee892STomi Valkeinen 		b2 = *p++;
2718f76ee892STomi Valkeinen 		b3 = *p++;
2719f76ee892STomi Valkeinen 		b4 = *p++;
2720f76ee892STomi Valkeinen 
2721f76ee892STomi Valkeinen 		dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4);
2722f76ee892STomi Valkeinen 	}
2723f76ee892STomi Valkeinen 
2724f76ee892STomi Valkeinen 	i = len % 4;
2725f76ee892STomi Valkeinen 	if (i) {
2726f76ee892STomi Valkeinen 		b1 = 0; b2 = 0; b3 = 0;
2727f76ee892STomi Valkeinen 
2728f76ee892STomi Valkeinen 		if (dsi->debug_write)
2729f76ee892STomi Valkeinen 			DSSDBG("\tsending remainder bytes %d\n", i);
2730f76ee892STomi Valkeinen 
2731f76ee892STomi Valkeinen 		switch (i) {
2732f76ee892STomi Valkeinen 		case 3:
2733f76ee892STomi Valkeinen 			b1 = *p++;
2734f76ee892STomi Valkeinen 			b2 = *p++;
2735f76ee892STomi Valkeinen 			b3 = *p++;
2736f76ee892STomi Valkeinen 			break;
2737f76ee892STomi Valkeinen 		case 2:
2738f76ee892STomi Valkeinen 			b1 = *p++;
2739f76ee892STomi Valkeinen 			b2 = *p++;
2740f76ee892STomi Valkeinen 			break;
2741f76ee892STomi Valkeinen 		case 1:
2742f76ee892STomi Valkeinen 			b1 = *p++;
2743f76ee892STomi Valkeinen 			break;
2744f76ee892STomi Valkeinen 		}
2745f76ee892STomi Valkeinen 
2746f76ee892STomi Valkeinen 		dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0);
2747f76ee892STomi Valkeinen 	}
2748f76ee892STomi Valkeinen 
2749f76ee892STomi Valkeinen 	return r;
2750f76ee892STomi Valkeinen }
2751f76ee892STomi Valkeinen 
dsi_vc_send_short(struct platform_device * dsidev,int channel,u8 data_type,u16 data,u8 ecc)2752f76ee892STomi Valkeinen static int dsi_vc_send_short(struct platform_device *dsidev, int channel,
2753f76ee892STomi Valkeinen 		u8 data_type, u16 data, u8 ecc)
2754f76ee892STomi Valkeinen {
2755f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2756f76ee892STomi Valkeinen 	u32 r;
2757f76ee892STomi Valkeinen 	u8 data_id;
2758f76ee892STomi Valkeinen 
2759f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
2760f76ee892STomi Valkeinen 
2761f76ee892STomi Valkeinen 	if (dsi->debug_write)
2762f76ee892STomi Valkeinen 		DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
2763f76ee892STomi Valkeinen 				channel,
2764f76ee892STomi Valkeinen 				data_type, data & 0xff, (data >> 8) & 0xff);
2765f76ee892STomi Valkeinen 
2766f76ee892STomi Valkeinen 	dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
2767f76ee892STomi Valkeinen 
2768f76ee892STomi Valkeinen 	if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) {
2769f76ee892STomi Valkeinen 		DSSERR("ERROR FIFO FULL, aborting transfer\n");
2770f76ee892STomi Valkeinen 		return -EINVAL;
2771f76ee892STomi Valkeinen 	}
2772f76ee892STomi Valkeinen 
2773f76ee892STomi Valkeinen 	data_id = data_type | dsi->vc[channel].vc_id << 6;
2774f76ee892STomi Valkeinen 
2775f76ee892STomi Valkeinen 	r = (data_id << 0) | (data << 8) | (ecc << 24);
2776f76ee892STomi Valkeinen 
2777f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r);
2778f76ee892STomi Valkeinen 
2779f76ee892STomi Valkeinen 	return 0;
2780f76ee892STomi Valkeinen }
2781f76ee892STomi Valkeinen 
dsi_vc_send_null(struct omap_dss_device * dssdev,int channel)2782f76ee892STomi Valkeinen static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel)
2783f76ee892STomi Valkeinen {
2784f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2785f76ee892STomi Valkeinen 
2786f76ee892STomi Valkeinen 	return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL,
2787f76ee892STomi Valkeinen 		0, 0);
2788f76ee892STomi Valkeinen }
2789f76ee892STomi Valkeinen 
dsi_vc_write_nosync_common(struct platform_device * dsidev,int channel,u8 * data,int len,enum dss_dsi_content_type type)2790f76ee892STomi Valkeinen static int dsi_vc_write_nosync_common(struct platform_device *dsidev,
2791f76ee892STomi Valkeinen 		int channel, u8 *data, int len, enum dss_dsi_content_type type)
2792f76ee892STomi Valkeinen {
2793f76ee892STomi Valkeinen 	int r;
2794f76ee892STomi Valkeinen 
2795f76ee892STomi Valkeinen 	if (len == 0) {
2796f76ee892STomi Valkeinen 		BUG_ON(type == DSS_DSI_CONTENT_DCS);
2797f76ee892STomi Valkeinen 		r = dsi_vc_send_short(dsidev, channel,
2798f76ee892STomi Valkeinen 				MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0);
2799f76ee892STomi Valkeinen 	} else if (len == 1) {
2800f76ee892STomi Valkeinen 		r = dsi_vc_send_short(dsidev, channel,
2801f76ee892STomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ?
2802f76ee892STomi Valkeinen 				MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM :
2803f76ee892STomi Valkeinen 				MIPI_DSI_DCS_SHORT_WRITE, data[0], 0);
2804f76ee892STomi Valkeinen 	} else if (len == 2) {
2805f76ee892STomi Valkeinen 		r = dsi_vc_send_short(dsidev, channel,
2806f76ee892STomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ?
2807f76ee892STomi Valkeinen 				MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM :
2808f76ee892STomi Valkeinen 				MIPI_DSI_DCS_SHORT_WRITE_PARAM,
2809f76ee892STomi Valkeinen 				data[0] | (data[1] << 8), 0);
2810f76ee892STomi Valkeinen 	} else {
2811f76ee892STomi Valkeinen 		r = dsi_vc_send_long(dsidev, channel,
2812f76ee892STomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ?
2813f76ee892STomi Valkeinen 				MIPI_DSI_GENERIC_LONG_WRITE :
2814f76ee892STomi Valkeinen 				MIPI_DSI_DCS_LONG_WRITE, data, len, 0);
2815f76ee892STomi Valkeinen 	}
2816f76ee892STomi Valkeinen 
2817f76ee892STomi Valkeinen 	return r;
2818f76ee892STomi Valkeinen }
2819f76ee892STomi Valkeinen 
dsi_vc_dcs_write_nosync(struct omap_dss_device * dssdev,int channel,u8 * data,int len)2820f76ee892STomi Valkeinen static int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel,
2821f76ee892STomi Valkeinen 		u8 *data, int len)
2822f76ee892STomi Valkeinen {
2823f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2824f76ee892STomi Valkeinen 
2825f76ee892STomi Valkeinen 	return dsi_vc_write_nosync_common(dsidev, channel, data, len,
2826f76ee892STomi Valkeinen 			DSS_DSI_CONTENT_DCS);
2827f76ee892STomi Valkeinen }
2828f76ee892STomi Valkeinen 
dsi_vc_generic_write_nosync(struct omap_dss_device * dssdev,int channel,u8 * data,int len)2829f76ee892STomi Valkeinen static int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel,
2830f76ee892STomi Valkeinen 		u8 *data, int len)
2831f76ee892STomi Valkeinen {
2832f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2833f76ee892STomi Valkeinen 
2834f76ee892STomi Valkeinen 	return dsi_vc_write_nosync_common(dsidev, channel, data, len,
2835f76ee892STomi Valkeinen 			DSS_DSI_CONTENT_GENERIC);
2836f76ee892STomi Valkeinen }
2837f76ee892STomi Valkeinen 
dsi_vc_write_common(struct omap_dss_device * dssdev,int channel,u8 * data,int len,enum dss_dsi_content_type type)2838f76ee892STomi Valkeinen static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel,
2839f76ee892STomi Valkeinen 		u8 *data, int len, enum dss_dsi_content_type type)
2840f76ee892STomi Valkeinen {
2841f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2842f76ee892STomi Valkeinen 	int r;
2843f76ee892STomi Valkeinen 
2844f76ee892STomi Valkeinen 	r = dsi_vc_write_nosync_common(dsidev, channel, data, len, type);
2845f76ee892STomi Valkeinen 	if (r)
2846f76ee892STomi Valkeinen 		goto err;
2847f76ee892STomi Valkeinen 
2848f76ee892STomi Valkeinen 	r = dsi_vc_send_bta_sync(dssdev, channel);
2849f76ee892STomi Valkeinen 	if (r)
2850f76ee892STomi Valkeinen 		goto err;
2851f76ee892STomi Valkeinen 
2852f76ee892STomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2853f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
2854f76ee892STomi Valkeinen 		DSSERR("rx fifo not empty after write, dumping data:\n");
2855f76ee892STomi Valkeinen 		dsi_vc_flush_receive_data(dsidev, channel);
2856f76ee892STomi Valkeinen 		r = -EIO;
2857f76ee892STomi Valkeinen 		goto err;
2858f76ee892STomi Valkeinen 	}
2859f76ee892STomi Valkeinen 
2860f76ee892STomi Valkeinen 	return 0;
2861f76ee892STomi Valkeinen err:
2862f76ee892STomi Valkeinen 	DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n",
2863f76ee892STomi Valkeinen 			channel, data[0], len);
2864f76ee892STomi Valkeinen 	return r;
2865f76ee892STomi Valkeinen }
2866f76ee892STomi Valkeinen 
dsi_vc_dcs_write(struct omap_dss_device * dssdev,int channel,u8 * data,int len)2867f76ee892STomi Valkeinen static int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
2868f76ee892STomi Valkeinen 		int len)
2869f76ee892STomi Valkeinen {
2870f76ee892STomi Valkeinen 	return dsi_vc_write_common(dssdev, channel, data, len,
2871f76ee892STomi Valkeinen 			DSS_DSI_CONTENT_DCS);
2872f76ee892STomi Valkeinen }
2873f76ee892STomi Valkeinen 
dsi_vc_generic_write(struct omap_dss_device * dssdev,int channel,u8 * data,int len)2874f76ee892STomi Valkeinen static int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data,
2875f76ee892STomi Valkeinen 		int len)
2876f76ee892STomi Valkeinen {
2877f76ee892STomi Valkeinen 	return dsi_vc_write_common(dssdev, channel, data, len,
2878f76ee892STomi Valkeinen 			DSS_DSI_CONTENT_GENERIC);
2879f76ee892STomi Valkeinen }
2880f76ee892STomi Valkeinen 
dsi_vc_dcs_send_read_request(struct platform_device * dsidev,int channel,u8 dcs_cmd)2881f76ee892STomi Valkeinen static int dsi_vc_dcs_send_read_request(struct platform_device *dsidev,
2882f76ee892STomi Valkeinen 		int channel, u8 dcs_cmd)
2883f76ee892STomi Valkeinen {
2884f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2885f76ee892STomi Valkeinen 	int r;
2886f76ee892STomi Valkeinen 
2887f76ee892STomi Valkeinen 	if (dsi->debug_read)
2888f76ee892STomi Valkeinen 		DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n",
2889f76ee892STomi Valkeinen 			channel, dcs_cmd);
2890f76ee892STomi Valkeinen 
2891f76ee892STomi Valkeinen 	r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0);
2892f76ee892STomi Valkeinen 	if (r) {
2893f76ee892STomi Valkeinen 		DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)"
2894f76ee892STomi Valkeinen 			" failed\n", channel, dcs_cmd);
2895f76ee892STomi Valkeinen 		return r;
2896f76ee892STomi Valkeinen 	}
2897f76ee892STomi Valkeinen 
2898f76ee892STomi Valkeinen 	return 0;
2899f76ee892STomi Valkeinen }
2900f76ee892STomi Valkeinen 
dsi_vc_generic_send_read_request(struct platform_device * dsidev,int channel,u8 * reqdata,int reqlen)2901f76ee892STomi Valkeinen static int dsi_vc_generic_send_read_request(struct platform_device *dsidev,
2902f76ee892STomi Valkeinen 		int channel, u8 *reqdata, int reqlen)
2903f76ee892STomi Valkeinen {
2904f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2905f76ee892STomi Valkeinen 	u16 data;
2906f76ee892STomi Valkeinen 	u8 data_type;
2907f76ee892STomi Valkeinen 	int r;
2908f76ee892STomi Valkeinen 
2909f76ee892STomi Valkeinen 	if (dsi->debug_read)
2910f76ee892STomi Valkeinen 		DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n",
2911f76ee892STomi Valkeinen 			channel, reqlen);
2912f76ee892STomi Valkeinen 
2913f76ee892STomi Valkeinen 	if (reqlen == 0) {
2914f76ee892STomi Valkeinen 		data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
2915f76ee892STomi Valkeinen 		data = 0;
2916f76ee892STomi Valkeinen 	} else if (reqlen == 1) {
2917f76ee892STomi Valkeinen 		data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
2918f76ee892STomi Valkeinen 		data = reqdata[0];
2919f76ee892STomi Valkeinen 	} else if (reqlen == 2) {
2920f76ee892STomi Valkeinen 		data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
2921f76ee892STomi Valkeinen 		data = reqdata[0] | (reqdata[1] << 8);
2922f76ee892STomi Valkeinen 	} else {
2923f76ee892STomi Valkeinen 		BUG();
2924f76ee892STomi Valkeinen 		return -EINVAL;
2925f76ee892STomi Valkeinen 	}
2926f76ee892STomi Valkeinen 
2927f76ee892STomi Valkeinen 	r = dsi_vc_send_short(dsidev, channel, data_type, data, 0);
2928f76ee892STomi Valkeinen 	if (r) {
2929f76ee892STomi Valkeinen 		DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)"
2930f76ee892STomi Valkeinen 			" failed\n", channel, reqlen);
2931f76ee892STomi Valkeinen 		return r;
2932f76ee892STomi Valkeinen 	}
2933f76ee892STomi Valkeinen 
2934f76ee892STomi Valkeinen 	return 0;
2935f76ee892STomi Valkeinen }
2936f76ee892STomi Valkeinen 
dsi_vc_read_rx_fifo(struct platform_device * dsidev,int channel,u8 * buf,int buflen,enum dss_dsi_content_type type)2937f76ee892STomi Valkeinen static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel,
2938f76ee892STomi Valkeinen 		u8 *buf, int buflen, enum dss_dsi_content_type type)
2939f76ee892STomi Valkeinen {
2940f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2941f76ee892STomi Valkeinen 	u32 val;
2942f76ee892STomi Valkeinen 	u8 dt;
2943f76ee892STomi Valkeinen 	int r;
2944f76ee892STomi Valkeinen 
2945f76ee892STomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2946f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) {
2947f76ee892STomi Valkeinen 		DSSERR("RX fifo empty when trying to read.\n");
2948f76ee892STomi Valkeinen 		r = -EIO;
2949f76ee892STomi Valkeinen 		goto err;
2950f76ee892STomi Valkeinen 	}
2951f76ee892STomi Valkeinen 
2952f76ee892STomi Valkeinen 	val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
2953f76ee892STomi Valkeinen 	if (dsi->debug_read)
2954f76ee892STomi Valkeinen 		DSSDBG("\theader: %08x\n", val);
2955f76ee892STomi Valkeinen 	dt = FLD_GET(val, 5, 0);
2956f76ee892STomi Valkeinen 	if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
2957f76ee892STomi Valkeinen 		u16 err = FLD_GET(val, 23, 8);
2958f76ee892STomi Valkeinen 		dsi_show_rx_ack_with_err(err);
2959f76ee892STomi Valkeinen 		r = -EIO;
2960f76ee892STomi Valkeinen 		goto err;
2961f76ee892STomi Valkeinen 
2962f76ee892STomi Valkeinen 	} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
2963f76ee892STomi Valkeinen 			MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
2964f76ee892STomi Valkeinen 			MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
2965f76ee892STomi Valkeinen 		u8 data = FLD_GET(val, 15, 8);
2966f76ee892STomi Valkeinen 		if (dsi->debug_read)
2967f76ee892STomi Valkeinen 			DSSDBG("\t%s short response, 1 byte: %02x\n",
2968f76ee892STomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
2969f76ee892STomi Valkeinen 				"DCS", data);
2970f76ee892STomi Valkeinen 
2971f76ee892STomi Valkeinen 		if (buflen < 1) {
2972f76ee892STomi Valkeinen 			r = -EIO;
2973f76ee892STomi Valkeinen 			goto err;
2974f76ee892STomi Valkeinen 		}
2975f76ee892STomi Valkeinen 
2976f76ee892STomi Valkeinen 		buf[0] = data;
2977f76ee892STomi Valkeinen 
2978f76ee892STomi Valkeinen 		return 1;
2979f76ee892STomi Valkeinen 	} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
2980f76ee892STomi Valkeinen 			MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
2981f76ee892STomi Valkeinen 			MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
2982f76ee892STomi Valkeinen 		u16 data = FLD_GET(val, 23, 8);
2983f76ee892STomi Valkeinen 		if (dsi->debug_read)
2984f76ee892STomi Valkeinen 			DSSDBG("\t%s short response, 2 byte: %04x\n",
2985f76ee892STomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
2986f76ee892STomi Valkeinen 				"DCS", data);
2987f76ee892STomi Valkeinen 
2988f76ee892STomi Valkeinen 		if (buflen < 2) {
2989f76ee892STomi Valkeinen 			r = -EIO;
2990f76ee892STomi Valkeinen 			goto err;
2991f76ee892STomi Valkeinen 		}
2992f76ee892STomi Valkeinen 
2993f76ee892STomi Valkeinen 		buf[0] = data & 0xff;
2994f76ee892STomi Valkeinen 		buf[1] = (data >> 8) & 0xff;
2995f76ee892STomi Valkeinen 
2996f76ee892STomi Valkeinen 		return 2;
2997f76ee892STomi Valkeinen 	} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
2998f76ee892STomi Valkeinen 			MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
2999f76ee892STomi Valkeinen 			MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
3000f76ee892STomi Valkeinen 		int w;
3001f76ee892STomi Valkeinen 		int len = FLD_GET(val, 23, 8);
3002f76ee892STomi Valkeinen 		if (dsi->debug_read)
3003f76ee892STomi Valkeinen 			DSSDBG("\t%s long response, len %d\n",
3004f76ee892STomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3005f76ee892STomi Valkeinen 				"DCS", len);
3006f76ee892STomi Valkeinen 
3007f76ee892STomi Valkeinen 		if (len > buflen) {
3008f76ee892STomi Valkeinen 			r = -EIO;
3009f76ee892STomi Valkeinen 			goto err;
3010f76ee892STomi Valkeinen 		}
3011f76ee892STomi Valkeinen 
3012f76ee892STomi Valkeinen 		/* two byte checksum ends the packet, not included in len */
3013f76ee892STomi Valkeinen 		for (w = 0; w < len + 2;) {
3014f76ee892STomi Valkeinen 			int b;
3015f76ee892STomi Valkeinen 			val = dsi_read_reg(dsidev,
3016f76ee892STomi Valkeinen 				DSI_VC_SHORT_PACKET_HEADER(channel));
3017f76ee892STomi Valkeinen 			if (dsi->debug_read)
3018f76ee892STomi Valkeinen 				DSSDBG("\t\t%02x %02x %02x %02x\n",
3019f76ee892STomi Valkeinen 						(val >> 0) & 0xff,
3020f76ee892STomi Valkeinen 						(val >> 8) & 0xff,
3021f76ee892STomi Valkeinen 						(val >> 16) & 0xff,
3022f76ee892STomi Valkeinen 						(val >> 24) & 0xff);
3023f76ee892STomi Valkeinen 
3024f76ee892STomi Valkeinen 			for (b = 0; b < 4; ++b) {
3025f76ee892STomi Valkeinen 				if (w < len)
3026f76ee892STomi Valkeinen 					buf[w] = (val >> (b * 8)) & 0xff;
3027f76ee892STomi Valkeinen 				/* we discard the 2 byte checksum */
3028f76ee892STomi Valkeinen 				++w;
3029f76ee892STomi Valkeinen 			}
3030f76ee892STomi Valkeinen 		}
3031f76ee892STomi Valkeinen 
3032f76ee892STomi Valkeinen 		return len;
3033f76ee892STomi Valkeinen 	} else {
3034f76ee892STomi Valkeinen 		DSSERR("\tunknown datatype 0x%02x\n", dt);
3035f76ee892STomi Valkeinen 		r = -EIO;
3036f76ee892STomi Valkeinen 		goto err;
3037f76ee892STomi Valkeinen 	}
3038f76ee892STomi Valkeinen 
3039f76ee892STomi Valkeinen err:
3040f76ee892STomi Valkeinen 	DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel,
3041f76ee892STomi Valkeinen 		type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
3042f76ee892STomi Valkeinen 
3043f76ee892STomi Valkeinen 	return r;
3044f76ee892STomi Valkeinen }
3045f76ee892STomi Valkeinen 
dsi_vc_dcs_read(struct omap_dss_device * dssdev,int channel,u8 dcs_cmd,u8 * buf,int buflen)3046f76ee892STomi Valkeinen static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
3047f76ee892STomi Valkeinen 		u8 *buf, int buflen)
3048f76ee892STomi Valkeinen {
3049f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3050f76ee892STomi Valkeinen 	int r;
3051f76ee892STomi Valkeinen 
3052f76ee892STomi Valkeinen 	r = dsi_vc_dcs_send_read_request(dsidev, channel, dcs_cmd);
3053f76ee892STomi Valkeinen 	if (r)
3054f76ee892STomi Valkeinen 		goto err;
3055f76ee892STomi Valkeinen 
3056f76ee892STomi Valkeinen 	r = dsi_vc_send_bta_sync(dssdev, channel);
3057f76ee892STomi Valkeinen 	if (r)
3058f76ee892STomi Valkeinen 		goto err;
3059f76ee892STomi Valkeinen 
3060f76ee892STomi Valkeinen 	r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
3061f76ee892STomi Valkeinen 		DSS_DSI_CONTENT_DCS);
3062f76ee892STomi Valkeinen 	if (r < 0)
3063f76ee892STomi Valkeinen 		goto err;
3064f76ee892STomi Valkeinen 
3065f76ee892STomi Valkeinen 	if (r != buflen) {
3066f76ee892STomi Valkeinen 		r = -EIO;
3067f76ee892STomi Valkeinen 		goto err;
3068f76ee892STomi Valkeinen 	}
3069f76ee892STomi Valkeinen 
3070f76ee892STomi Valkeinen 	return 0;
3071f76ee892STomi Valkeinen err:
3072f76ee892STomi Valkeinen 	DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd);
3073f76ee892STomi Valkeinen 	return r;
3074f76ee892STomi Valkeinen }
3075f76ee892STomi Valkeinen 
dsi_vc_generic_read(struct omap_dss_device * dssdev,int channel,u8 * reqdata,int reqlen,u8 * buf,int buflen)3076f76ee892STomi Valkeinen static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel,
3077f76ee892STomi Valkeinen 		u8 *reqdata, int reqlen, u8 *buf, int buflen)
3078f76ee892STomi Valkeinen {
3079f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3080f76ee892STomi Valkeinen 	int r;
3081f76ee892STomi Valkeinen 
3082f76ee892STomi Valkeinen 	r = dsi_vc_generic_send_read_request(dsidev, channel, reqdata, reqlen);
3083f76ee892STomi Valkeinen 	if (r)
3084f76ee892STomi Valkeinen 		return r;
3085f76ee892STomi Valkeinen 
3086f76ee892STomi Valkeinen 	r = dsi_vc_send_bta_sync(dssdev, channel);
3087f76ee892STomi Valkeinen 	if (r)
3088f76ee892STomi Valkeinen 		return r;
3089f76ee892STomi Valkeinen 
3090f76ee892STomi Valkeinen 	r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
3091f76ee892STomi Valkeinen 		DSS_DSI_CONTENT_GENERIC);
3092f76ee892STomi Valkeinen 	if (r < 0)
3093f76ee892STomi Valkeinen 		return r;
3094f76ee892STomi Valkeinen 
3095f76ee892STomi Valkeinen 	if (r != buflen) {
3096f76ee892STomi Valkeinen 		r = -EIO;
3097f76ee892STomi Valkeinen 		return r;
3098f76ee892STomi Valkeinen 	}
3099f76ee892STomi Valkeinen 
3100f76ee892STomi Valkeinen 	return 0;
3101f76ee892STomi Valkeinen }
3102f76ee892STomi Valkeinen 
dsi_vc_set_max_rx_packet_size(struct omap_dss_device * dssdev,int channel,u16 len)3103f76ee892STomi Valkeinen static int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel,
3104f76ee892STomi Valkeinen 		u16 len)
3105f76ee892STomi Valkeinen {
3106f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3107f76ee892STomi Valkeinen 
3108f76ee892STomi Valkeinen 	return dsi_vc_send_short(dsidev, channel,
3109f76ee892STomi Valkeinen 			MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
3110f76ee892STomi Valkeinen }
3111f76ee892STomi Valkeinen 
dsi_enter_ulps(struct platform_device * dsidev)3112f76ee892STomi Valkeinen static int dsi_enter_ulps(struct platform_device *dsidev)
3113f76ee892STomi Valkeinen {
3114f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3115f76ee892STomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
3116f76ee892STomi Valkeinen 	int r, i;
3117f76ee892STomi Valkeinen 	unsigned mask;
3118f76ee892STomi Valkeinen 
3119f76ee892STomi Valkeinen 	DSSDBG("Entering ULPS");
3120f76ee892STomi Valkeinen 
3121f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
3122f76ee892STomi Valkeinen 
3123f76ee892STomi Valkeinen 	WARN_ON(dsi->ulps_enabled);
3124f76ee892STomi Valkeinen 
3125f76ee892STomi Valkeinen 	if (dsi->ulps_enabled)
3126f76ee892STomi Valkeinen 		return 0;
3127f76ee892STomi Valkeinen 
3128f76ee892STomi Valkeinen 	/* DDR_CLK_ALWAYS_ON */
3129f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) {
3130f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, 0);
3131f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
3132f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, 1);
3133f76ee892STomi Valkeinen 	}
3134f76ee892STomi Valkeinen 
3135f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 0);
3136f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 1);
3137f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 2);
3138f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 3);
3139f76ee892STomi Valkeinen 
3140f76ee892STomi Valkeinen 	dsi_force_tx_stop_mode_io(dsidev);
3141f76ee892STomi Valkeinen 
3142f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 0, false);
3143f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 1, false);
3144f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 2, false);
3145f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 3, false);
3146f76ee892STomi Valkeinen 
3147f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) {	/* HS_BUSY */
3148f76ee892STomi Valkeinen 		DSSERR("HS busy when enabling ULPS\n");
3149f76ee892STomi Valkeinen 		return -EIO;
3150f76ee892STomi Valkeinen 	}
3151f76ee892STomi Valkeinen 
3152f76ee892STomi Valkeinen 	if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) {	/* LP_BUSY */
3153f76ee892STomi Valkeinen 		DSSERR("LP busy when enabling ULPS\n");
3154f76ee892STomi Valkeinen 		return -EIO;
3155f76ee892STomi Valkeinen 	}
3156f76ee892STomi Valkeinen 
3157f76ee892STomi Valkeinen 	r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion,
3158f76ee892STomi Valkeinen 			DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3159f76ee892STomi Valkeinen 	if (r)
3160f76ee892STomi Valkeinen 		return r;
3161f76ee892STomi Valkeinen 
3162f76ee892STomi Valkeinen 	mask = 0;
3163f76ee892STomi Valkeinen 
3164f76ee892STomi Valkeinen 	for (i = 0; i < dsi->num_lanes_supported; ++i) {
3165f76ee892STomi Valkeinen 		if (dsi->lanes[i].function == DSI_LANE_UNUSED)
3166f76ee892STomi Valkeinen 			continue;
3167f76ee892STomi Valkeinen 		mask |= 1 << i;
3168f76ee892STomi Valkeinen 	}
3169f76ee892STomi Valkeinen 	/* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */
3170f76ee892STomi Valkeinen 	/* LANEx_ULPS_SIG2 */
3171f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, mask, 9, 5);
3172f76ee892STomi Valkeinen 
3173f76ee892STomi Valkeinen 	/* flush posted write and wait for SCP interface to finish the write */
3174f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
3175f76ee892STomi Valkeinen 
3176f76ee892STomi Valkeinen 	if (wait_for_completion_timeout(&completion,
3177f76ee892STomi Valkeinen 				msecs_to_jiffies(1000)) == 0) {
3178f76ee892STomi Valkeinen 		DSSERR("ULPS enable timeout\n");
3179f76ee892STomi Valkeinen 		r = -EIO;
3180f76ee892STomi Valkeinen 		goto err;
3181f76ee892STomi Valkeinen 	}
3182f76ee892STomi Valkeinen 
3183f76ee892STomi Valkeinen 	dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
3184f76ee892STomi Valkeinen 			DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3185f76ee892STomi Valkeinen 
3186f76ee892STomi Valkeinen 	/* Reset LANEx_ULPS_SIG2 */
3187f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, 0, 9, 5);
3188f76ee892STomi Valkeinen 
3189f76ee892STomi Valkeinen 	/* flush posted write and wait for SCP interface to finish the write */
3190f76ee892STomi Valkeinen 	dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
3191f76ee892STomi Valkeinen 
3192f76ee892STomi Valkeinen 	dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
3193f76ee892STomi Valkeinen 
3194f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, false);
3195f76ee892STomi Valkeinen 
3196f76ee892STomi Valkeinen 	dsi->ulps_enabled = true;
3197f76ee892STomi Valkeinen 
3198f76ee892STomi Valkeinen 	return 0;
3199f76ee892STomi Valkeinen 
3200f76ee892STomi Valkeinen err:
3201f76ee892STomi Valkeinen 	dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
3202f76ee892STomi Valkeinen 			DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3203f76ee892STomi Valkeinen 	return r;
3204f76ee892STomi Valkeinen }
3205f76ee892STomi Valkeinen 
dsi_set_lp_rx_timeout(struct platform_device * dsidev,unsigned ticks,bool x4,bool x16)3206f76ee892STomi Valkeinen static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
3207f76ee892STomi Valkeinen 		unsigned ticks, bool x4, bool x16)
3208f76ee892STomi Valkeinen {
3209f76ee892STomi Valkeinen 	unsigned long fck;
3210f76ee892STomi Valkeinen 	unsigned long total_ticks;
3211f76ee892STomi Valkeinen 	u32 r;
3212f76ee892STomi Valkeinen 
3213f76ee892STomi Valkeinen 	BUG_ON(ticks > 0x1fff);
3214f76ee892STomi Valkeinen 
3215f76ee892STomi Valkeinen 	/* ticks in DSI_FCK */
3216f76ee892STomi Valkeinen 	fck = dsi_fclk_rate(dsidev);
3217f76ee892STomi Valkeinen 
3218f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_TIMING2);
3219f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);	/* LP_RX_TO */
3220f76ee892STomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 14, 14);	/* LP_RX_TO_X16 */
3221f76ee892STomi Valkeinen 	r = FLD_MOD(r, x4 ? 1 : 0, 13, 13);	/* LP_RX_TO_X4 */
3222f76ee892STomi Valkeinen 	r = FLD_MOD(r, ticks, 12, 0);	/* LP_RX_COUNTER */
3223f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TIMING2, r);
3224f76ee892STomi Valkeinen 
3225f76ee892STomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3226f76ee892STomi Valkeinen 
3227f76ee892STomi Valkeinen 	DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
3228f76ee892STomi Valkeinen 			total_ticks,
3229f76ee892STomi Valkeinen 			ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3230f76ee892STomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
3231f76ee892STomi Valkeinen }
3232f76ee892STomi Valkeinen 
dsi_set_ta_timeout(struct platform_device * dsidev,unsigned ticks,bool x8,bool x16)3233f76ee892STomi Valkeinen static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
3234f76ee892STomi Valkeinen 		bool x8, bool x16)
3235f76ee892STomi Valkeinen {
3236f76ee892STomi Valkeinen 	unsigned long fck;
3237f76ee892STomi Valkeinen 	unsigned long total_ticks;
3238f76ee892STomi Valkeinen 	u32 r;
3239f76ee892STomi Valkeinen 
3240f76ee892STomi Valkeinen 	BUG_ON(ticks > 0x1fff);
3241f76ee892STomi Valkeinen 
3242f76ee892STomi Valkeinen 	/* ticks in DSI_FCK */
3243f76ee892STomi Valkeinen 	fck = dsi_fclk_rate(dsidev);
3244f76ee892STomi Valkeinen 
3245f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_TIMING1);
3246f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 31, 31);	/* TA_TO */
3247f76ee892STomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 30, 30);	/* TA_TO_X16 */
3248f76ee892STomi Valkeinen 	r = FLD_MOD(r, x8 ? 1 : 0, 29, 29);	/* TA_TO_X8 */
3249f76ee892STomi Valkeinen 	r = FLD_MOD(r, ticks, 28, 16);	/* TA_TO_COUNTER */
3250f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TIMING1, r);
3251f76ee892STomi Valkeinen 
3252f76ee892STomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
3253f76ee892STomi Valkeinen 
3254f76ee892STomi Valkeinen 	DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
3255f76ee892STomi Valkeinen 			total_ticks,
3256f76ee892STomi Valkeinen 			ticks, x8 ? " x8" : "", x16 ? " x16" : "",
3257f76ee892STomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
3258f76ee892STomi Valkeinen }
3259f76ee892STomi Valkeinen 
dsi_set_stop_state_counter(struct platform_device * dsidev,unsigned ticks,bool x4,bool x16)3260f76ee892STomi Valkeinen static void dsi_set_stop_state_counter(struct platform_device *dsidev,
3261f76ee892STomi Valkeinen 		unsigned ticks, bool x4, bool x16)
3262f76ee892STomi Valkeinen {
3263f76ee892STomi Valkeinen 	unsigned long fck;
3264f76ee892STomi Valkeinen 	unsigned long total_ticks;
3265f76ee892STomi Valkeinen 	u32 r;
3266f76ee892STomi Valkeinen 
3267f76ee892STomi Valkeinen 	BUG_ON(ticks > 0x1fff);
3268f76ee892STomi Valkeinen 
3269f76ee892STomi Valkeinen 	/* ticks in DSI_FCK */
3270f76ee892STomi Valkeinen 	fck = dsi_fclk_rate(dsidev);
3271f76ee892STomi Valkeinen 
3272f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_TIMING1);
3273f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);	/* FORCE_TX_STOP_MODE_IO */
3274f76ee892STomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 14, 14);	/* STOP_STATE_X16_IO */
3275f76ee892STomi Valkeinen 	r = FLD_MOD(r, x4 ? 1 : 0, 13, 13);	/* STOP_STATE_X4_IO */
3276f76ee892STomi Valkeinen 	r = FLD_MOD(r, ticks, 12, 0);	/* STOP_STATE_COUNTER_IO */
3277f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TIMING1, r);
3278f76ee892STomi Valkeinen 
3279f76ee892STomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3280f76ee892STomi Valkeinen 
3281f76ee892STomi Valkeinen 	DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
3282f76ee892STomi Valkeinen 			total_ticks,
3283f76ee892STomi Valkeinen 			ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3284f76ee892STomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
3285f76ee892STomi Valkeinen }
3286f76ee892STomi Valkeinen 
dsi_set_hs_tx_timeout(struct platform_device * dsidev,unsigned ticks,bool x4,bool x16)3287f76ee892STomi Valkeinen static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
3288f76ee892STomi Valkeinen 		unsigned ticks, bool x4, bool x16)
3289f76ee892STomi Valkeinen {
3290f76ee892STomi Valkeinen 	unsigned long fck;
3291f76ee892STomi Valkeinen 	unsigned long total_ticks;
3292f76ee892STomi Valkeinen 	u32 r;
3293f76ee892STomi Valkeinen 
3294f76ee892STomi Valkeinen 	BUG_ON(ticks > 0x1fff);
3295f76ee892STomi Valkeinen 
3296f76ee892STomi Valkeinen 	/* ticks in TxByteClkHS */
3297f76ee892STomi Valkeinen 	fck = dsi_get_txbyteclkhs(dsidev);
3298f76ee892STomi Valkeinen 
3299f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_TIMING2);
3300f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 31, 31);	/* HS_TX_TO */
3301f76ee892STomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 30, 30);	/* HS_TX_TO_X16 */
3302f76ee892STomi Valkeinen 	r = FLD_MOD(r, x4 ? 1 : 0, 29, 29);	/* HS_TX_TO_X8 (4 really) */
3303f76ee892STomi Valkeinen 	r = FLD_MOD(r, ticks, 28, 16);	/* HS_TX_TO_COUNTER */
3304f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_TIMING2, r);
3305f76ee892STomi Valkeinen 
3306f76ee892STomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3307f76ee892STomi Valkeinen 
3308f76ee892STomi Valkeinen 	DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
3309f76ee892STomi Valkeinen 			total_ticks,
3310f76ee892STomi Valkeinen 			ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3311f76ee892STomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
3312f76ee892STomi Valkeinen }
3313f76ee892STomi Valkeinen 
dsi_config_vp_num_line_buffers(struct platform_device * dsidev)3314f76ee892STomi Valkeinen static void dsi_config_vp_num_line_buffers(struct platform_device *dsidev)
3315f76ee892STomi Valkeinen {
3316f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3317f76ee892STomi Valkeinen 	int num_line_buffers;
3318f76ee892STomi Valkeinen 
3319f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3320f76ee892STomi Valkeinen 		int bpp = dsi_get_pixel_size(dsi->pix_fmt);
3321f76ee892STomi Valkeinen 		struct omap_video_timings *timings = &dsi->timings;
3322f76ee892STomi Valkeinen 		/*
3323f76ee892STomi Valkeinen 		 * Don't use line buffers if width is greater than the video
3324f76ee892STomi Valkeinen 		 * port's line buffer size
3325f76ee892STomi Valkeinen 		 */
3326f76ee892STomi Valkeinen 		if (dsi->line_buffer_size <= timings->x_res * bpp / 8)
3327f76ee892STomi Valkeinen 			num_line_buffers = 0;
3328f76ee892STomi Valkeinen 		else
3329f76ee892STomi Valkeinen 			num_line_buffers = 2;
3330f76ee892STomi Valkeinen 	} else {
3331f76ee892STomi Valkeinen 		/* Use maximum number of line buffers in command mode */
3332f76ee892STomi Valkeinen 		num_line_buffers = 2;
3333f76ee892STomi Valkeinen 	}
3334f76ee892STomi Valkeinen 
3335f76ee892STomi Valkeinen 	/* LINE_BUFFER */
3336f76ee892STomi Valkeinen 	REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12);
3337f76ee892STomi Valkeinen }
3338f76ee892STomi Valkeinen 
dsi_config_vp_sync_events(struct platform_device * dsidev)3339f76ee892STomi Valkeinen static void dsi_config_vp_sync_events(struct platform_device *dsidev)
3340f76ee892STomi Valkeinen {
3341f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3342f76ee892STomi Valkeinen 	bool sync_end;
3343f76ee892STomi Valkeinen 	u32 r;
3344f76ee892STomi Valkeinen 
3345f76ee892STomi Valkeinen 	if (dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE)
3346f76ee892STomi Valkeinen 		sync_end = true;
3347f76ee892STomi Valkeinen 	else
3348f76ee892STomi Valkeinen 		sync_end = false;
3349f76ee892STomi Valkeinen 
3350f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CTRL);
3351f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 9, 9);		/* VP_DE_POL */
3352f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 10, 10);		/* VP_HSYNC_POL */
3353f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 11, 11);		/* VP_VSYNC_POL */
3354f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);		/* VP_VSYNC_START */
3355f76ee892STomi Valkeinen 	r = FLD_MOD(r, sync_end, 16, 16);	/* VP_VSYNC_END */
3356f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 17, 17);		/* VP_HSYNC_START */
3357f76ee892STomi Valkeinen 	r = FLD_MOD(r, sync_end, 18, 18);	/* VP_HSYNC_END */
3358f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_CTRL, r);
3359f76ee892STomi Valkeinen }
3360f76ee892STomi Valkeinen 
dsi_config_blanking_modes(struct platform_device * dsidev)3361f76ee892STomi Valkeinen static void dsi_config_blanking_modes(struct platform_device *dsidev)
3362f76ee892STomi Valkeinen {
3363f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3364f76ee892STomi Valkeinen 	int blanking_mode = dsi->vm_timings.blanking_mode;
3365f76ee892STomi Valkeinen 	int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode;
3366f76ee892STomi Valkeinen 	int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode;
3367f76ee892STomi Valkeinen 	int hsa_blanking_mode = dsi->vm_timings.hsa_blanking_mode;
3368f76ee892STomi Valkeinen 	u32 r;
3369f76ee892STomi Valkeinen 
3370f76ee892STomi Valkeinen 	/*
3371f76ee892STomi Valkeinen 	 * 0 = TX FIFO packets sent or LPS in corresponding blanking periods
3372f76ee892STomi Valkeinen 	 * 1 = Long blanking packets are sent in corresponding blanking periods
3373f76ee892STomi Valkeinen 	 */
3374f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CTRL);
3375f76ee892STomi Valkeinen 	r = FLD_MOD(r, blanking_mode, 20, 20);		/* BLANKING_MODE */
3376f76ee892STomi Valkeinen 	r = FLD_MOD(r, hfp_blanking_mode, 21, 21);	/* HFP_BLANKING */
3377f76ee892STomi Valkeinen 	r = FLD_MOD(r, hbp_blanking_mode, 22, 22);	/* HBP_BLANKING */
3378f76ee892STomi Valkeinen 	r = FLD_MOD(r, hsa_blanking_mode, 23, 23);	/* HSA_BLANKING */
3379f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_CTRL, r);
3380f76ee892STomi Valkeinen }
3381f76ee892STomi Valkeinen 
3382f76ee892STomi Valkeinen /*
3383f76ee892STomi Valkeinen  * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3
3384f76ee892STomi Valkeinen  * results in maximum transition time for data and clock lanes to enter and
3385f76ee892STomi Valkeinen  * exit HS mode. Hence, this is the scenario where the least amount of command
3386f76ee892STomi Valkeinen  * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS
3387f76ee892STomi Valkeinen  * clock cycles that can be used to interleave command mode data in HS so that
3388f76ee892STomi Valkeinen  * all scenarios are satisfied.
3389f76ee892STomi Valkeinen  */
dsi_compute_interleave_hs(int blank,bool ddr_alwon,int enter_hs,int exit_hs,int exiths_clk,int ddr_pre,int ddr_post)3390f76ee892STomi Valkeinen static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs,
3391f76ee892STomi Valkeinen 		int exit_hs, int exiths_clk, int ddr_pre, int ddr_post)
3392f76ee892STomi Valkeinen {
3393f76ee892STomi Valkeinen 	int transition;
3394f76ee892STomi Valkeinen 
3395f76ee892STomi Valkeinen 	/*
3396f76ee892STomi Valkeinen 	 * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition
3397f76ee892STomi Valkeinen 	 * time of data lanes only, if it isn't set, we need to consider HS
3398f76ee892STomi Valkeinen 	 * transition time of both data and clock lanes. HS transition time
3399f76ee892STomi Valkeinen 	 * of Scenario 3 is considered.
3400f76ee892STomi Valkeinen 	 */
3401f76ee892STomi Valkeinen 	if (ddr_alwon) {
3402f76ee892STomi Valkeinen 		transition = enter_hs + exit_hs + max(enter_hs, 2) + 1;
3403f76ee892STomi Valkeinen 	} else {
3404f76ee892STomi Valkeinen 		int trans1, trans2;
3405f76ee892STomi Valkeinen 		trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1;
3406f76ee892STomi Valkeinen 		trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre +
3407f76ee892STomi Valkeinen 				enter_hs + 1;
3408f76ee892STomi Valkeinen 		transition = max(trans1, trans2);
3409f76ee892STomi Valkeinen 	}
3410f76ee892STomi Valkeinen 
3411f76ee892STomi Valkeinen 	return blank > transition ? blank - transition : 0;
3412f76ee892STomi Valkeinen }
3413f76ee892STomi Valkeinen 
3414f76ee892STomi Valkeinen /*
3415f76ee892STomi Valkeinen  * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1
3416f76ee892STomi Valkeinen  * results in maximum transition time for data lanes to enter and exit LP mode.
3417f76ee892STomi Valkeinen  * Hence, this is the scenario where the least amount of command mode data can
3418f76ee892STomi Valkeinen  * be interleaved. We program the minimum amount of bytes that can be
3419f76ee892STomi Valkeinen  * interleaved in LP so that all scenarios are satisfied.
3420f76ee892STomi Valkeinen  */
dsi_compute_interleave_lp(int blank,int enter_hs,int exit_hs,int lp_clk_div,int tdsi_fclk)3421f76ee892STomi Valkeinen static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs,
3422f76ee892STomi Valkeinen 		int lp_clk_div, int tdsi_fclk)
3423f76ee892STomi Valkeinen {
3424f76ee892STomi Valkeinen 	int trans_lp;	/* time required for a LP transition, in TXBYTECLKHS */
3425f76ee892STomi Valkeinen 	int tlp_avail;	/* time left for interleaving commands, in CLKIN4DDR */
3426f76ee892STomi Valkeinen 	int ttxclkesc;	/* period of LP transmit escape clock, in CLKIN4DDR */
3427f76ee892STomi Valkeinen 	int thsbyte_clk = 16;	/* Period of TXBYTECLKHS clock, in CLKIN4DDR */
3428f76ee892STomi Valkeinen 	int lp_inter;	/* cmd mode data that can be interleaved, in bytes */
3429f76ee892STomi Valkeinen 
3430f76ee892STomi Valkeinen 	/* maximum LP transition time according to Scenario 1 */
3431f76ee892STomi Valkeinen 	trans_lp = exit_hs + max(enter_hs, 2) + 1;
3432f76ee892STomi Valkeinen 
3433f76ee892STomi Valkeinen 	/* CLKIN4DDR = 16 * TXBYTECLKHS */
3434f76ee892STomi Valkeinen 	tlp_avail = thsbyte_clk * (blank - trans_lp);
3435f76ee892STomi Valkeinen 
3436f76ee892STomi Valkeinen 	ttxclkesc = tdsi_fclk * lp_clk_div;
3437f76ee892STomi Valkeinen 
3438f76ee892STomi Valkeinen 	lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc -
3439f76ee892STomi Valkeinen 			26) / 16;
3440f76ee892STomi Valkeinen 
3441f76ee892STomi Valkeinen 	return max(lp_inter, 0);
3442f76ee892STomi Valkeinen }
3443f76ee892STomi Valkeinen 
dsi_config_cmd_mode_interleaving(struct platform_device * dsidev)3444f76ee892STomi Valkeinen static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev)
3445f76ee892STomi Valkeinen {
3446f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3447f76ee892STomi Valkeinen 	int blanking_mode;
3448f76ee892STomi Valkeinen 	int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode;
3449f76ee892STomi Valkeinen 	int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div;
3450f76ee892STomi Valkeinen 	int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat;
3451f76ee892STomi Valkeinen 	int tclk_trail, ths_exit, exiths_clk;
3452f76ee892STomi Valkeinen 	bool ddr_alwon;
3453f76ee892STomi Valkeinen 	struct omap_video_timings *timings = &dsi->timings;
3454f76ee892STomi Valkeinen 	int bpp = dsi_get_pixel_size(dsi->pix_fmt);
3455f76ee892STomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
3456f76ee892STomi Valkeinen 	int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1;
3457f76ee892STomi Valkeinen 	int hsa_interleave_hs = 0, hsa_interleave_lp = 0;
3458f76ee892STomi Valkeinen 	int hfp_interleave_hs = 0, hfp_interleave_lp = 0;
3459f76ee892STomi Valkeinen 	int hbp_interleave_hs = 0, hbp_interleave_lp = 0;
3460f76ee892STomi Valkeinen 	int bl_interleave_hs = 0, bl_interleave_lp = 0;
3461f76ee892STomi Valkeinen 	u32 r;
3462f76ee892STomi Valkeinen 
3463f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CTRL);
3464f76ee892STomi Valkeinen 	blanking_mode = FLD_GET(r, 20, 20);
3465f76ee892STomi Valkeinen 	hfp_blanking_mode = FLD_GET(r, 21, 21);
3466f76ee892STomi Valkeinen 	hbp_blanking_mode = FLD_GET(r, 22, 22);
3467f76ee892STomi Valkeinen 	hsa_blanking_mode = FLD_GET(r, 23, 23);
3468f76ee892STomi Valkeinen 
3469f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
3470f76ee892STomi Valkeinen 	hbp = FLD_GET(r, 11, 0);
3471f76ee892STomi Valkeinen 	hfp = FLD_GET(r, 23, 12);
3472f76ee892STomi Valkeinen 	hsa = FLD_GET(r, 31, 24);
3473f76ee892STomi Valkeinen 
3474f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
3475f76ee892STomi Valkeinen 	ddr_clk_post = FLD_GET(r, 7, 0);
3476f76ee892STomi Valkeinen 	ddr_clk_pre = FLD_GET(r, 15, 8);
3477f76ee892STomi Valkeinen 
3478f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_VM_TIMING7);
3479f76ee892STomi Valkeinen 	exit_hs_mode_lat = FLD_GET(r, 15, 0);
3480f76ee892STomi Valkeinen 	enter_hs_mode_lat = FLD_GET(r, 31, 16);
3481f76ee892STomi Valkeinen 
3482f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CLK_CTRL);
3483f76ee892STomi Valkeinen 	lp_clk_div = FLD_GET(r, 12, 0);
3484f76ee892STomi Valkeinen 	ddr_alwon = FLD_GET(r, 13, 13);
3485f76ee892STomi Valkeinen 
3486f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
3487f76ee892STomi Valkeinen 	ths_exit = FLD_GET(r, 7, 0);
3488f76ee892STomi Valkeinen 
3489f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
3490f76ee892STomi Valkeinen 	tclk_trail = FLD_GET(r, 15, 8);
3491f76ee892STomi Valkeinen 
3492f76ee892STomi Valkeinen 	exiths_clk = ths_exit + tclk_trail;
3493f76ee892STomi Valkeinen 
3494f76ee892STomi Valkeinen 	width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8);
3495f76ee892STomi Valkeinen 	bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl);
3496f76ee892STomi Valkeinen 
3497f76ee892STomi Valkeinen 	if (!hsa_blanking_mode) {
3498f76ee892STomi Valkeinen 		hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon,
3499f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3500f76ee892STomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
3501f76ee892STomi Valkeinen 		hsa_interleave_lp = dsi_compute_interleave_lp(hsa,
3502f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3503f76ee892STomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
3504f76ee892STomi Valkeinen 	}
3505f76ee892STomi Valkeinen 
3506f76ee892STomi Valkeinen 	if (!hfp_blanking_mode) {
3507f76ee892STomi Valkeinen 		hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon,
3508f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3509f76ee892STomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
3510f76ee892STomi Valkeinen 		hfp_interleave_lp = dsi_compute_interleave_lp(hfp,
3511f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3512f76ee892STomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
3513f76ee892STomi Valkeinen 	}
3514f76ee892STomi Valkeinen 
3515f76ee892STomi Valkeinen 	if (!hbp_blanking_mode) {
3516f76ee892STomi Valkeinen 		hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon,
3517f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3518f76ee892STomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
3519f76ee892STomi Valkeinen 
3520f76ee892STomi Valkeinen 		hbp_interleave_lp = dsi_compute_interleave_lp(hbp,
3521f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3522f76ee892STomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
3523f76ee892STomi Valkeinen 	}
3524f76ee892STomi Valkeinen 
3525f76ee892STomi Valkeinen 	if (!blanking_mode) {
3526f76ee892STomi Valkeinen 		bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon,
3527f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3528f76ee892STomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
3529f76ee892STomi Valkeinen 
3530f76ee892STomi Valkeinen 		bl_interleave_lp = dsi_compute_interleave_lp(bllp,
3531f76ee892STomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
3532f76ee892STomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
3533f76ee892STomi Valkeinen 	}
3534f76ee892STomi Valkeinen 
3535f76ee892STomi Valkeinen 	DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n",
3536f76ee892STomi Valkeinen 		hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs,
3537f76ee892STomi Valkeinen 		bl_interleave_hs);
3538f76ee892STomi Valkeinen 
3539f76ee892STomi Valkeinen 	DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n",
3540f76ee892STomi Valkeinen 		hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp,
3541f76ee892STomi Valkeinen 		bl_interleave_lp);
3542f76ee892STomi Valkeinen 
3543f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_VM_TIMING4);
3544f76ee892STomi Valkeinen 	r = FLD_MOD(r, hsa_interleave_hs, 23, 16);
3545f76ee892STomi Valkeinen 	r = FLD_MOD(r, hfp_interleave_hs, 15, 8);
3546f76ee892STomi Valkeinen 	r = FLD_MOD(r, hbp_interleave_hs, 7, 0);
3547f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VM_TIMING4, r);
3548f76ee892STomi Valkeinen 
3549f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_VM_TIMING5);
3550f76ee892STomi Valkeinen 	r = FLD_MOD(r, hsa_interleave_lp, 23, 16);
3551f76ee892STomi Valkeinen 	r = FLD_MOD(r, hfp_interleave_lp, 15, 8);
3552f76ee892STomi Valkeinen 	r = FLD_MOD(r, hbp_interleave_lp, 7, 0);
3553f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VM_TIMING5, r);
3554f76ee892STomi Valkeinen 
3555f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_VM_TIMING6);
3556f76ee892STomi Valkeinen 	r = FLD_MOD(r, bl_interleave_hs, 31, 15);
3557f76ee892STomi Valkeinen 	r = FLD_MOD(r, bl_interleave_lp, 16, 0);
3558f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VM_TIMING6, r);
3559f76ee892STomi Valkeinen }
3560f76ee892STomi Valkeinen 
dsi_proto_config(struct platform_device * dsidev)3561f76ee892STomi Valkeinen static int dsi_proto_config(struct platform_device *dsidev)
3562f76ee892STomi Valkeinen {
3563f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3564f76ee892STomi Valkeinen 	u32 r;
3565f76ee892STomi Valkeinen 	int buswidth = 0;
3566f76ee892STomi Valkeinen 
3567f76ee892STomi Valkeinen 	dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32,
3568f76ee892STomi Valkeinen 			DSI_FIFO_SIZE_32,
3569f76ee892STomi Valkeinen 			DSI_FIFO_SIZE_32,
3570f76ee892STomi Valkeinen 			DSI_FIFO_SIZE_32);
3571f76ee892STomi Valkeinen 
3572f76ee892STomi Valkeinen 	dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32,
3573f76ee892STomi Valkeinen 			DSI_FIFO_SIZE_32,
3574f76ee892STomi Valkeinen 			DSI_FIFO_SIZE_32,
3575f76ee892STomi Valkeinen 			DSI_FIFO_SIZE_32);
3576f76ee892STomi Valkeinen 
3577f76ee892STomi Valkeinen 	/* XXX what values for the timeouts? */
3578f76ee892STomi Valkeinen 	dsi_set_stop_state_counter(dsidev, 0x1000, false, false);
3579f76ee892STomi Valkeinen 	dsi_set_ta_timeout(dsidev, 0x1fff, true, true);
3580f76ee892STomi Valkeinen 	dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true);
3581f76ee892STomi Valkeinen 	dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true);
3582f76ee892STomi Valkeinen 
3583f76ee892STomi Valkeinen 	switch (dsi_get_pixel_size(dsi->pix_fmt)) {
3584f76ee892STomi Valkeinen 	case 16:
3585f76ee892STomi Valkeinen 		buswidth = 0;
3586f76ee892STomi Valkeinen 		break;
3587f76ee892STomi Valkeinen 	case 18:
3588f76ee892STomi Valkeinen 		buswidth = 1;
3589f76ee892STomi Valkeinen 		break;
3590f76ee892STomi Valkeinen 	case 24:
3591f76ee892STomi Valkeinen 		buswidth = 2;
3592f76ee892STomi Valkeinen 		break;
3593f76ee892STomi Valkeinen 	default:
3594f76ee892STomi Valkeinen 		BUG();
3595f76ee892STomi Valkeinen 		return -EINVAL;
3596f76ee892STomi Valkeinen 	}
3597f76ee892STomi Valkeinen 
3598f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CTRL);
3599f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 1, 1);	/* CS_RX_EN */
3600f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 2, 2);	/* ECC_RX_EN */
3601f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 3, 3);	/* TX_FIFO_ARBITRATION */
3602f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 4, 4);	/* VP_CLK_RATIO, always 1, see errata*/
3603f76ee892STomi Valkeinen 	r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
3604f76ee892STomi Valkeinen 	r = FLD_MOD(r, 0, 8, 8);	/* VP_CLK_POL */
3605f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 14, 14);	/* TRIGGER_RESET_MODE */
3606f76ee892STomi Valkeinen 	r = FLD_MOD(r, 1, 19, 19);	/* EOT_ENABLE */
3607f76ee892STomi Valkeinen 	if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
3608f76ee892STomi Valkeinen 		r = FLD_MOD(r, 1, 24, 24);	/* DCS_CMD_ENABLE */
3609f76ee892STomi Valkeinen 		/* DCS_CMD_CODE, 1=start, 0=continue */
3610f76ee892STomi Valkeinen 		r = FLD_MOD(r, 0, 25, 25);
3611f76ee892STomi Valkeinen 	}
3612f76ee892STomi Valkeinen 
3613f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_CTRL, r);
3614f76ee892STomi Valkeinen 
3615f76ee892STomi Valkeinen 	dsi_config_vp_num_line_buffers(dsidev);
3616f76ee892STomi Valkeinen 
3617f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3618f76ee892STomi Valkeinen 		dsi_config_vp_sync_events(dsidev);
3619f76ee892STomi Valkeinen 		dsi_config_blanking_modes(dsidev);
3620f76ee892STomi Valkeinen 		dsi_config_cmd_mode_interleaving(dsidev);
3621f76ee892STomi Valkeinen 	}
3622f76ee892STomi Valkeinen 
3623f76ee892STomi Valkeinen 	dsi_vc_initial_config(dsidev, 0);
3624f76ee892STomi Valkeinen 	dsi_vc_initial_config(dsidev, 1);
3625f76ee892STomi Valkeinen 	dsi_vc_initial_config(dsidev, 2);
3626f76ee892STomi Valkeinen 	dsi_vc_initial_config(dsidev, 3);
3627f76ee892STomi Valkeinen 
3628f76ee892STomi Valkeinen 	return 0;
3629f76ee892STomi Valkeinen }
3630f76ee892STomi Valkeinen 
dsi_proto_timings(struct platform_device * dsidev)3631f76ee892STomi Valkeinen static void dsi_proto_timings(struct platform_device *dsidev)
3632f76ee892STomi Valkeinen {
3633f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3634c96da175SSam Ravnborg 	unsigned tlpx, tclk_zero, tclk_prepare;
3635f76ee892STomi Valkeinen 	unsigned tclk_pre, tclk_post;
3636f76ee892STomi Valkeinen 	unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
3637f76ee892STomi Valkeinen 	unsigned ths_trail, ths_exit;
3638f76ee892STomi Valkeinen 	unsigned ddr_clk_pre, ddr_clk_post;
3639f76ee892STomi Valkeinen 	unsigned enter_hs_mode_lat, exit_hs_mode_lat;
3640f76ee892STomi Valkeinen 	unsigned ths_eot;
3641f76ee892STomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
3642f76ee892STomi Valkeinen 	u32 r;
3643f76ee892STomi Valkeinen 
3644f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
3645f76ee892STomi Valkeinen 	ths_prepare = FLD_GET(r, 31, 24);
3646f76ee892STomi Valkeinen 	ths_prepare_ths_zero = FLD_GET(r, 23, 16);
3647f76ee892STomi Valkeinen 	ths_zero = ths_prepare_ths_zero - ths_prepare;
3648f76ee892STomi Valkeinen 	ths_trail = FLD_GET(r, 15, 8);
3649f76ee892STomi Valkeinen 	ths_exit = FLD_GET(r, 7, 0);
3650f76ee892STomi Valkeinen 
3651f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
3652f76ee892STomi Valkeinen 	tlpx = FLD_GET(r, 20, 16) * 2;
3653f76ee892STomi Valkeinen 	tclk_zero = FLD_GET(r, 7, 0);
3654f76ee892STomi Valkeinen 
3655f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
3656f76ee892STomi Valkeinen 	tclk_prepare = FLD_GET(r, 7, 0);
3657f76ee892STomi Valkeinen 
3658f76ee892STomi Valkeinen 	/* min 8*UI */
3659f76ee892STomi Valkeinen 	tclk_pre = 20;
3660f76ee892STomi Valkeinen 	/* min 60ns + 52*UI */
3661f76ee892STomi Valkeinen 	tclk_post = ns2ddr(dsidev, 60) + 26;
3662f76ee892STomi Valkeinen 
3663f76ee892STomi Valkeinen 	ths_eot = DIV_ROUND_UP(4, ndl);
3664f76ee892STomi Valkeinen 
3665f76ee892STomi Valkeinen 	ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
3666f76ee892STomi Valkeinen 			4);
3667f76ee892STomi Valkeinen 	ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
3668f76ee892STomi Valkeinen 
3669f76ee892STomi Valkeinen 	BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
3670f76ee892STomi Valkeinen 	BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
3671f76ee892STomi Valkeinen 
3672f76ee892STomi Valkeinen 	r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
3673f76ee892STomi Valkeinen 	r = FLD_MOD(r, ddr_clk_pre, 15, 8);
3674f76ee892STomi Valkeinen 	r = FLD_MOD(r, ddr_clk_post, 7, 0);
3675f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_CLK_TIMING, r);
3676f76ee892STomi Valkeinen 
3677f76ee892STomi Valkeinen 	DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
3678f76ee892STomi Valkeinen 			ddr_clk_pre,
3679f76ee892STomi Valkeinen 			ddr_clk_post);
3680f76ee892STomi Valkeinen 
3681f76ee892STomi Valkeinen 	enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
3682f76ee892STomi Valkeinen 		DIV_ROUND_UP(ths_prepare, 4) +
3683f76ee892STomi Valkeinen 		DIV_ROUND_UP(ths_zero + 3, 4);
3684f76ee892STomi Valkeinen 
3685f76ee892STomi Valkeinen 	exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
3686f76ee892STomi Valkeinen 
3687f76ee892STomi Valkeinen 	r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
3688f76ee892STomi Valkeinen 		FLD_VAL(exit_hs_mode_lat, 15, 0);
3689f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VM_TIMING7, r);
3690f76ee892STomi Valkeinen 
3691f76ee892STomi Valkeinen 	DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
3692f76ee892STomi Valkeinen 			enter_hs_mode_lat, exit_hs_mode_lat);
3693f76ee892STomi Valkeinen 
3694f76ee892STomi Valkeinen 	 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3695f76ee892STomi Valkeinen 		/* TODO: Implement a video mode check_timings function */
3696f76ee892STomi Valkeinen 		int hsa = dsi->vm_timings.hsa;
3697f76ee892STomi Valkeinen 		int hfp = dsi->vm_timings.hfp;
3698f76ee892STomi Valkeinen 		int hbp = dsi->vm_timings.hbp;
3699f76ee892STomi Valkeinen 		int vsa = dsi->vm_timings.vsa;
3700f76ee892STomi Valkeinen 		int vfp = dsi->vm_timings.vfp;
3701f76ee892STomi Valkeinen 		int vbp = dsi->vm_timings.vbp;
3702f76ee892STomi Valkeinen 		int window_sync = dsi->vm_timings.window_sync;
3703f76ee892STomi Valkeinen 		bool hsync_end;
3704f76ee892STomi Valkeinen 		struct omap_video_timings *timings = &dsi->timings;
3705f76ee892STomi Valkeinen 		int bpp = dsi_get_pixel_size(dsi->pix_fmt);
3706f76ee892STomi Valkeinen 		int tl, t_he, width_bytes;
3707f76ee892STomi Valkeinen 
3708f76ee892STomi Valkeinen 		hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE;
3709f76ee892STomi Valkeinen 		t_he = hsync_end ?
3710f76ee892STomi Valkeinen 			((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
3711f76ee892STomi Valkeinen 
3712f76ee892STomi Valkeinen 		width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8);
3713f76ee892STomi Valkeinen 
3714f76ee892STomi Valkeinen 		/* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
3715f76ee892STomi Valkeinen 		tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
3716f76ee892STomi Valkeinen 			DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
3717f76ee892STomi Valkeinen 
3718f76ee892STomi Valkeinen 		DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
3719f76ee892STomi Valkeinen 			hfp, hsync_end ? hsa : 0, tl);
3720f76ee892STomi Valkeinen 		DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
3721f76ee892STomi Valkeinen 			vsa, timings->y_res);
3722f76ee892STomi Valkeinen 
3723f76ee892STomi Valkeinen 		r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
3724f76ee892STomi Valkeinen 		r = FLD_MOD(r, hbp, 11, 0);	/* HBP */
3725f76ee892STomi Valkeinen 		r = FLD_MOD(r, hfp, 23, 12);	/* HFP */
3726f76ee892STomi Valkeinen 		r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24);	/* HSA */
3727f76ee892STomi Valkeinen 		dsi_write_reg(dsidev, DSI_VM_TIMING1, r);
3728f76ee892STomi Valkeinen 
3729f76ee892STomi Valkeinen 		r = dsi_read_reg(dsidev, DSI_VM_TIMING2);
3730f76ee892STomi Valkeinen 		r = FLD_MOD(r, vbp, 7, 0);	/* VBP */
3731f76ee892STomi Valkeinen 		r = FLD_MOD(r, vfp, 15, 8);	/* VFP */
3732f76ee892STomi Valkeinen 		r = FLD_MOD(r, vsa, 23, 16);	/* VSA */
3733f76ee892STomi Valkeinen 		r = FLD_MOD(r, window_sync, 27, 24);	/* WINDOW_SYNC */
3734f76ee892STomi Valkeinen 		dsi_write_reg(dsidev, DSI_VM_TIMING2, r);
3735f76ee892STomi Valkeinen 
3736f76ee892STomi Valkeinen 		r = dsi_read_reg(dsidev, DSI_VM_TIMING3);
3737f76ee892STomi Valkeinen 		r = FLD_MOD(r, timings->y_res, 14, 0);	/* VACT */
3738f76ee892STomi Valkeinen 		r = FLD_MOD(r, tl, 31, 16);		/* TL */
3739f76ee892STomi Valkeinen 		dsi_write_reg(dsidev, DSI_VM_TIMING3, r);
3740f76ee892STomi Valkeinen 	}
3741f76ee892STomi Valkeinen }
3742f76ee892STomi Valkeinen 
dsi_configure_pins(struct omap_dss_device * dssdev,const struct omap_dsi_pin_config * pin_cfg)3743f76ee892STomi Valkeinen static int dsi_configure_pins(struct omap_dss_device *dssdev,
3744f76ee892STomi Valkeinen 		const struct omap_dsi_pin_config *pin_cfg)
3745f76ee892STomi Valkeinen {
3746f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3747f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3748f76ee892STomi Valkeinen 	int num_pins;
3749f76ee892STomi Valkeinen 	const int *pins;
3750f76ee892STomi Valkeinen 	struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
3751f76ee892STomi Valkeinen 	int num_lanes;
3752f76ee892STomi Valkeinen 	int i;
3753f76ee892STomi Valkeinen 
3754f76ee892STomi Valkeinen 	static const enum dsi_lane_function functions[] = {
3755f76ee892STomi Valkeinen 		DSI_LANE_CLK,
3756f76ee892STomi Valkeinen 		DSI_LANE_DATA1,
3757f76ee892STomi Valkeinen 		DSI_LANE_DATA2,
3758f76ee892STomi Valkeinen 		DSI_LANE_DATA3,
3759f76ee892STomi Valkeinen 		DSI_LANE_DATA4,
3760f76ee892STomi Valkeinen 	};
3761f76ee892STomi Valkeinen 
3762f76ee892STomi Valkeinen 	num_pins = pin_cfg->num_pins;
3763f76ee892STomi Valkeinen 	pins = pin_cfg->pins;
3764f76ee892STomi Valkeinen 
3765f76ee892STomi Valkeinen 	if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2
3766f76ee892STomi Valkeinen 			|| num_pins % 2 != 0)
3767f76ee892STomi Valkeinen 		return -EINVAL;
3768f76ee892STomi Valkeinen 
3769f76ee892STomi Valkeinen 	for (i = 0; i < DSI_MAX_NR_LANES; ++i)
3770f76ee892STomi Valkeinen 		lanes[i].function = DSI_LANE_UNUSED;
3771f76ee892STomi Valkeinen 
3772f76ee892STomi Valkeinen 	num_lanes = 0;
3773f76ee892STomi Valkeinen 
3774f76ee892STomi Valkeinen 	for (i = 0; i < num_pins; i += 2) {
3775f76ee892STomi Valkeinen 		u8 lane, pol;
3776f76ee892STomi Valkeinen 		int dx, dy;
3777f76ee892STomi Valkeinen 
3778f76ee892STomi Valkeinen 		dx = pins[i];
3779f76ee892STomi Valkeinen 		dy = pins[i + 1];
3780f76ee892STomi Valkeinen 
3781f76ee892STomi Valkeinen 		if (dx < 0 || dx >= dsi->num_lanes_supported * 2)
3782f76ee892STomi Valkeinen 			return -EINVAL;
3783f76ee892STomi Valkeinen 
3784f76ee892STomi Valkeinen 		if (dy < 0 || dy >= dsi->num_lanes_supported * 2)
3785f76ee892STomi Valkeinen 			return -EINVAL;
3786f76ee892STomi Valkeinen 
3787f76ee892STomi Valkeinen 		if (dx & 1) {
3788f76ee892STomi Valkeinen 			if (dy != dx - 1)
3789f76ee892STomi Valkeinen 				return -EINVAL;
3790f76ee892STomi Valkeinen 			pol = 1;
3791f76ee892STomi Valkeinen 		} else {
3792f76ee892STomi Valkeinen 			if (dy != dx + 1)
3793f76ee892STomi Valkeinen 				return -EINVAL;
3794f76ee892STomi Valkeinen 			pol = 0;
3795f76ee892STomi Valkeinen 		}
3796f76ee892STomi Valkeinen 
3797f76ee892STomi Valkeinen 		lane = dx / 2;
3798f76ee892STomi Valkeinen 
3799f76ee892STomi Valkeinen 		lanes[lane].function = functions[i / 2];
3800f76ee892STomi Valkeinen 		lanes[lane].polarity = pol;
3801f76ee892STomi Valkeinen 		num_lanes++;
3802f76ee892STomi Valkeinen 	}
3803f76ee892STomi Valkeinen 
3804f76ee892STomi Valkeinen 	memcpy(dsi->lanes, lanes, sizeof(dsi->lanes));
3805f76ee892STomi Valkeinen 	dsi->num_lanes_used = num_lanes;
3806f76ee892STomi Valkeinen 
3807f76ee892STomi Valkeinen 	return 0;
3808f76ee892STomi Valkeinen }
3809f76ee892STomi Valkeinen 
dsi_enable_video_output(struct omap_dss_device * dssdev,int channel)3810f76ee892STomi Valkeinen static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
3811f76ee892STomi Valkeinen {
3812f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3813f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3814f76ee892STomi Valkeinen 	struct omap_overlay_manager *mgr = dsi->output.manager;
3815f76ee892STomi Valkeinen 	int bpp = dsi_get_pixel_size(dsi->pix_fmt);
3816f76ee892STomi Valkeinen 	struct omap_dss_device *out = &dsi->output;
3817f76ee892STomi Valkeinen 	u8 data_type;
3818f76ee892STomi Valkeinen 	u16 word_count;
3819f76ee892STomi Valkeinen 	int r;
3820f76ee892STomi Valkeinen 
3821f76ee892STomi Valkeinen 	if (out->manager == NULL) {
3822f76ee892STomi Valkeinen 		DSSERR("failed to enable display: no output/manager\n");
3823f76ee892STomi Valkeinen 		return -ENODEV;
3824f76ee892STomi Valkeinen 	}
3825f76ee892STomi Valkeinen 
3826f76ee892STomi Valkeinen 	r = dsi_display_init_dispc(dsidev, mgr);
3827f76ee892STomi Valkeinen 	if (r)
3828f76ee892STomi Valkeinen 		goto err_init_dispc;
3829f76ee892STomi Valkeinen 
3830f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3831f76ee892STomi Valkeinen 		switch (dsi->pix_fmt) {
3832f76ee892STomi Valkeinen 		case OMAP_DSS_DSI_FMT_RGB888:
3833f76ee892STomi Valkeinen 			data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
3834f76ee892STomi Valkeinen 			break;
3835f76ee892STomi Valkeinen 		case OMAP_DSS_DSI_FMT_RGB666:
3836f76ee892STomi Valkeinen 			data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
3837f76ee892STomi Valkeinen 			break;
3838f76ee892STomi Valkeinen 		case OMAP_DSS_DSI_FMT_RGB666_PACKED:
3839f76ee892STomi Valkeinen 			data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
3840f76ee892STomi Valkeinen 			break;
3841f76ee892STomi Valkeinen 		case OMAP_DSS_DSI_FMT_RGB565:
3842f76ee892STomi Valkeinen 			data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
3843f76ee892STomi Valkeinen 			break;
3844f76ee892STomi Valkeinen 		default:
3845f76ee892STomi Valkeinen 			r = -EINVAL;
3846f76ee892STomi Valkeinen 			goto err_pix_fmt;
3847f76ee892STomi Valkeinen 		}
3848f76ee892STomi Valkeinen 
3849f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, false);
3850f76ee892STomi Valkeinen 		dsi_vc_enable(dsidev, channel, false);
3851f76ee892STomi Valkeinen 
3852f76ee892STomi Valkeinen 		/* MODE, 1 = video mode */
3853f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
3854f76ee892STomi Valkeinen 
3855f76ee892STomi Valkeinen 		word_count = DIV_ROUND_UP(dsi->timings.x_res * bpp, 8);
3856f76ee892STomi Valkeinen 
3857f76ee892STomi Valkeinen 		dsi_vc_write_long_header(dsidev, channel, data_type,
3858f76ee892STomi Valkeinen 				word_count, 0);
3859f76ee892STomi Valkeinen 
3860f76ee892STomi Valkeinen 		dsi_vc_enable(dsidev, channel, true);
3861f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, true);
3862f76ee892STomi Valkeinen 	}
3863f76ee892STomi Valkeinen 
3864f76ee892STomi Valkeinen 	r = dss_mgr_enable(mgr);
3865f76ee892STomi Valkeinen 	if (r)
3866f76ee892STomi Valkeinen 		goto err_mgr_enable;
3867f76ee892STomi Valkeinen 
3868f76ee892STomi Valkeinen 	return 0;
3869f76ee892STomi Valkeinen 
3870f76ee892STomi Valkeinen err_mgr_enable:
3871f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3872f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, false);
3873f76ee892STomi Valkeinen 		dsi_vc_enable(dsidev, channel, false);
3874f76ee892STomi Valkeinen 	}
3875f76ee892STomi Valkeinen err_pix_fmt:
3876f76ee892STomi Valkeinen 	dsi_display_uninit_dispc(dsidev, mgr);
3877f76ee892STomi Valkeinen err_init_dispc:
3878f76ee892STomi Valkeinen 	return r;
3879f76ee892STomi Valkeinen }
3880f76ee892STomi Valkeinen 
dsi_disable_video_output(struct omap_dss_device * dssdev,int channel)3881f76ee892STomi Valkeinen static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel)
3882f76ee892STomi Valkeinen {
3883f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3884f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3885f76ee892STomi Valkeinen 	struct omap_overlay_manager *mgr = dsi->output.manager;
3886f76ee892STomi Valkeinen 
3887f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3888f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, false);
3889f76ee892STomi Valkeinen 		dsi_vc_enable(dsidev, channel, false);
3890f76ee892STomi Valkeinen 
3891f76ee892STomi Valkeinen 		/* MODE, 0 = command mode */
3892f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
3893f76ee892STomi Valkeinen 
3894f76ee892STomi Valkeinen 		dsi_vc_enable(dsidev, channel, true);
3895f76ee892STomi Valkeinen 		dsi_if_enable(dsidev, true);
3896f76ee892STomi Valkeinen 	}
3897f76ee892STomi Valkeinen 
3898f76ee892STomi Valkeinen 	dss_mgr_disable(mgr);
3899f76ee892STomi Valkeinen 
3900f76ee892STomi Valkeinen 	dsi_display_uninit_dispc(dsidev, mgr);
3901f76ee892STomi Valkeinen }
3902f76ee892STomi Valkeinen 
dsi_update_screen_dispc(struct platform_device * dsidev)3903f76ee892STomi Valkeinen static void dsi_update_screen_dispc(struct platform_device *dsidev)
3904f76ee892STomi Valkeinen {
3905f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3906f76ee892STomi Valkeinen 	struct omap_overlay_manager *mgr = dsi->output.manager;
3907f76ee892STomi Valkeinen 	unsigned bytespp;
3908f76ee892STomi Valkeinen 	unsigned bytespl;
3909f76ee892STomi Valkeinen 	unsigned bytespf;
3910f76ee892STomi Valkeinen 	unsigned total_len;
3911f76ee892STomi Valkeinen 	unsigned packet_payload;
3912f76ee892STomi Valkeinen 	unsigned packet_len;
3913f76ee892STomi Valkeinen 	u32 l;
3914f76ee892STomi Valkeinen 	int r;
3915f76ee892STomi Valkeinen 	const unsigned channel = dsi->update_channel;
3916f76ee892STomi Valkeinen 	const unsigned line_buf_size = dsi->line_buffer_size;
3917f76ee892STomi Valkeinen 	u16 w = dsi->timings.x_res;
3918f76ee892STomi Valkeinen 	u16 h = dsi->timings.y_res;
3919f76ee892STomi Valkeinen 
3920f76ee892STomi Valkeinen 	DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
3921f76ee892STomi Valkeinen 
3922f76ee892STomi Valkeinen 	dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP);
3923f76ee892STomi Valkeinen 
3924f76ee892STomi Valkeinen 	bytespp	= dsi_get_pixel_size(dsi->pix_fmt) / 8;
3925f76ee892STomi Valkeinen 	bytespl = w * bytespp;
3926f76ee892STomi Valkeinen 	bytespf = bytespl * h;
3927f76ee892STomi Valkeinen 
3928f76ee892STomi Valkeinen 	/* NOTE: packet_payload has to be equal to N * bytespl, where N is
3929f76ee892STomi Valkeinen 	 * number of lines in a packet.  See errata about VP_CLK_RATIO */
3930f76ee892STomi Valkeinen 
3931f76ee892STomi Valkeinen 	if (bytespf < line_buf_size)
3932f76ee892STomi Valkeinen 		packet_payload = bytespf;
3933f76ee892STomi Valkeinen 	else
3934f76ee892STomi Valkeinen 		packet_payload = (line_buf_size) / bytespl * bytespl;
3935f76ee892STomi Valkeinen 
3936f76ee892STomi Valkeinen 	packet_len = packet_payload + 1;	/* 1 byte for DCS cmd */
3937f76ee892STomi Valkeinen 	total_len = (bytespf / packet_payload) * packet_len;
3938f76ee892STomi Valkeinen 
3939f76ee892STomi Valkeinen 	if (bytespf % packet_payload)
3940f76ee892STomi Valkeinen 		total_len += (bytespf % packet_payload) + 1;
3941f76ee892STomi Valkeinen 
3942f76ee892STomi Valkeinen 	l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
3943f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
3944f76ee892STomi Valkeinen 
3945f76ee892STomi Valkeinen 	dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE,
3946f76ee892STomi Valkeinen 		packet_len, 0);
3947f76ee892STomi Valkeinen 
3948f76ee892STomi Valkeinen 	if (dsi->te_enabled)
3949f76ee892STomi Valkeinen 		l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
3950f76ee892STomi Valkeinen 	else
3951f76ee892STomi Valkeinen 		l = FLD_MOD(l, 1, 31, 31); /* TE_START */
3952f76ee892STomi Valkeinen 	dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
3953f76ee892STomi Valkeinen 
3954f76ee892STomi Valkeinen 	/* We put SIDLEMODE to no-idle for the duration of the transfer,
3955f76ee892STomi Valkeinen 	 * because DSS interrupts are not capable of waking up the CPU and the
3956f76ee892STomi Valkeinen 	 * framedone interrupt could be delayed for quite a long time. I think
3957f76ee892STomi Valkeinen 	 * the same goes for any DSS interrupts, but for some reason I have not
3958f76ee892STomi Valkeinen 	 * seen the problem anywhere else than here.
3959f76ee892STomi Valkeinen 	 */
3960f76ee892STomi Valkeinen 	dispc_disable_sidle();
3961f76ee892STomi Valkeinen 
3962f76ee892STomi Valkeinen 	dsi_perf_mark_start(dsidev);
3963f76ee892STomi Valkeinen 
3964f76ee892STomi Valkeinen 	r = schedule_delayed_work(&dsi->framedone_timeout_work,
3965f76ee892STomi Valkeinen 		msecs_to_jiffies(250));
3966f76ee892STomi Valkeinen 	BUG_ON(r == 0);
3967f76ee892STomi Valkeinen 
3968f76ee892STomi Valkeinen 	dss_mgr_set_timings(mgr, &dsi->timings);
3969f76ee892STomi Valkeinen 
3970f76ee892STomi Valkeinen 	dss_mgr_start_update(mgr);
3971f76ee892STomi Valkeinen 
3972f76ee892STomi Valkeinen 	if (dsi->te_enabled) {
3973f76ee892STomi Valkeinen 		/* disable LP_RX_TO, so that we can receive TE.  Time to wait
3974f76ee892STomi Valkeinen 		 * for TE is longer than the timer allows */
3975f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
3976f76ee892STomi Valkeinen 
3977f76ee892STomi Valkeinen 		dsi_vc_send_bta(dsidev, channel);
3978f76ee892STomi Valkeinen 
3979f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
3980f76ee892STomi Valkeinen 		mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
3981f76ee892STomi Valkeinen #endif
3982f76ee892STomi Valkeinen 	}
3983f76ee892STomi Valkeinen }
3984f76ee892STomi Valkeinen 
3985f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
dsi_te_timeout(struct timer_list * unused)39866c789357SKees Cook static void dsi_te_timeout(struct timer_list *unused)
3987f76ee892STomi Valkeinen {
3988f76ee892STomi Valkeinen 	DSSERR("TE not received for 250ms!\n");
3989f76ee892STomi Valkeinen }
3990f76ee892STomi Valkeinen #endif
3991f76ee892STomi Valkeinen 
dsi_handle_framedone(struct platform_device * dsidev,int error)3992f76ee892STomi Valkeinen static void dsi_handle_framedone(struct platform_device *dsidev, int error)
3993f76ee892STomi Valkeinen {
3994f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3995f76ee892STomi Valkeinen 
3996f76ee892STomi Valkeinen 	/* SIDLEMODE back to smart-idle */
3997f76ee892STomi Valkeinen 	dispc_enable_sidle();
3998f76ee892STomi Valkeinen 
3999f76ee892STomi Valkeinen 	if (dsi->te_enabled) {
4000f76ee892STomi Valkeinen 		/* enable LP_RX_TO again after the TE */
4001f76ee892STomi Valkeinen 		REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
4002f76ee892STomi Valkeinen 	}
4003f76ee892STomi Valkeinen 
4004f76ee892STomi Valkeinen 	dsi->framedone_callback(error, dsi->framedone_data);
4005f76ee892STomi Valkeinen 
4006f76ee892STomi Valkeinen 	if (!error)
4007f76ee892STomi Valkeinen 		dsi_perf_show(dsidev, "DISPC");
4008f76ee892STomi Valkeinen }
4009f76ee892STomi Valkeinen 
dsi_framedone_timeout_work_callback(struct work_struct * work)4010f76ee892STomi Valkeinen static void dsi_framedone_timeout_work_callback(struct work_struct *work)
4011f76ee892STomi Valkeinen {
4012f76ee892STomi Valkeinen 	struct dsi_data *dsi = container_of(work, struct dsi_data,
4013f76ee892STomi Valkeinen 			framedone_timeout_work.work);
4014f76ee892STomi Valkeinen 	/* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
4015f76ee892STomi Valkeinen 	 * 250ms which would conflict with this timeout work. What should be
4016f76ee892STomi Valkeinen 	 * done is first cancel the transfer on the HW, and then cancel the
4017f76ee892STomi Valkeinen 	 * possibly scheduled framedone work. However, cancelling the transfer
4018f76ee892STomi Valkeinen 	 * on the HW is buggy, and would probably require resetting the whole
4019f76ee892STomi Valkeinen 	 * DSI */
4020f76ee892STomi Valkeinen 
4021f76ee892STomi Valkeinen 	DSSERR("Framedone not received for 250ms!\n");
4022f76ee892STomi Valkeinen 
4023f76ee892STomi Valkeinen 	dsi_handle_framedone(dsi->pdev, -ETIMEDOUT);
4024f76ee892STomi Valkeinen }
4025f76ee892STomi Valkeinen 
dsi_framedone_irq_callback(void * data)4026f76ee892STomi Valkeinen static void dsi_framedone_irq_callback(void *data)
4027f76ee892STomi Valkeinen {
4028f76ee892STomi Valkeinen 	struct platform_device *dsidev = (struct platform_device *) data;
4029f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4030f76ee892STomi Valkeinen 
4031f76ee892STomi Valkeinen 	/* Note: We get FRAMEDONE when DISPC has finished sending pixels and
4032f76ee892STomi Valkeinen 	 * turns itself off. However, DSI still has the pixels in its buffers,
4033f76ee892STomi Valkeinen 	 * and is sending the data.
4034f76ee892STomi Valkeinen 	 */
4035f76ee892STomi Valkeinen 
4036f76ee892STomi Valkeinen 	cancel_delayed_work(&dsi->framedone_timeout_work);
4037f76ee892STomi Valkeinen 
4038f76ee892STomi Valkeinen 	dsi_handle_framedone(dsidev, 0);
4039f76ee892STomi Valkeinen }
4040f76ee892STomi Valkeinen 
dsi_update(struct omap_dss_device * dssdev,int channel,void (* callback)(int,void *),void * data)4041f76ee892STomi Valkeinen static int dsi_update(struct omap_dss_device *dssdev, int channel,
4042f76ee892STomi Valkeinen 		void (*callback)(int, void *), void *data)
4043f76ee892STomi Valkeinen {
4044f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4045f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4046f76ee892STomi Valkeinen 
4047f76ee892STomi Valkeinen 	dsi_perf_mark_setup(dsidev);
4048f76ee892STomi Valkeinen 
4049f76ee892STomi Valkeinen 	dsi->update_channel = channel;
4050f76ee892STomi Valkeinen 
4051f76ee892STomi Valkeinen 	dsi->framedone_callback = callback;
4052f76ee892STomi Valkeinen 	dsi->framedone_data = data;
4053f76ee892STomi Valkeinen 
4054f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE
4055c96da175SSam Ravnborg 	dsi->update_bytes = dsi->timings.x_res * dsi->timings.y_res *
4056f76ee892STomi Valkeinen 		dsi_get_pixel_size(dsi->pix_fmt) / 8;
4057f76ee892STomi Valkeinen #endif
4058f76ee892STomi Valkeinen 	dsi_update_screen_dispc(dsidev);
4059f76ee892STomi Valkeinen 
4060f76ee892STomi Valkeinen 	return 0;
4061f76ee892STomi Valkeinen }
4062f76ee892STomi Valkeinen 
4063f76ee892STomi Valkeinen /* Display funcs */
4064f76ee892STomi Valkeinen 
dsi_configure_dispc_clocks(struct platform_device * dsidev)4065f76ee892STomi Valkeinen static int dsi_configure_dispc_clocks(struct platform_device *dsidev)
4066f76ee892STomi Valkeinen {
4067f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4068f76ee892STomi Valkeinen 	struct dispc_clock_info dispc_cinfo;
4069f76ee892STomi Valkeinen 	int r;
4070f76ee892STomi Valkeinen 	unsigned long fck;
4071f76ee892STomi Valkeinen 
4072f76ee892STomi Valkeinen 	fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
4073f76ee892STomi Valkeinen 
4074f76ee892STomi Valkeinen 	dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div;
4075f76ee892STomi Valkeinen 	dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div;
4076f76ee892STomi Valkeinen 
4077f76ee892STomi Valkeinen 	r = dispc_calc_clock_rates(fck, &dispc_cinfo);
4078f76ee892STomi Valkeinen 	if (r) {
4079f76ee892STomi Valkeinen 		DSSERR("Failed to calc dispc clocks\n");
4080f76ee892STomi Valkeinen 		return r;
4081f76ee892STomi Valkeinen 	}
4082f76ee892STomi Valkeinen 
4083f76ee892STomi Valkeinen 	dsi->mgr_config.clock_info = dispc_cinfo;
4084f76ee892STomi Valkeinen 
4085f76ee892STomi Valkeinen 	return 0;
4086f76ee892STomi Valkeinen }
4087f76ee892STomi Valkeinen 
dsi_display_init_dispc(struct platform_device * dsidev,struct omap_overlay_manager * mgr)4088f76ee892STomi Valkeinen static int dsi_display_init_dispc(struct platform_device *dsidev,
4089f76ee892STomi Valkeinen 		struct omap_overlay_manager *mgr)
4090f76ee892STomi Valkeinen {
4091f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4092f76ee892STomi Valkeinen 	int r;
4093f76ee892STomi Valkeinen 
4094f76ee892STomi Valkeinen 	dss_select_lcd_clk_source(mgr->id, dsi->module_id == 0 ?
4095f76ee892STomi Valkeinen 			OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
4096f76ee892STomi Valkeinen 			OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC);
4097f76ee892STomi Valkeinen 
4098f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
4099f76ee892STomi Valkeinen 		r = dss_mgr_register_framedone_handler(mgr,
4100f76ee892STomi Valkeinen 				dsi_framedone_irq_callback, dsidev);
4101f76ee892STomi Valkeinen 		if (r) {
4102f76ee892STomi Valkeinen 			DSSERR("can't register FRAMEDONE handler\n");
4103f76ee892STomi Valkeinen 			goto err;
4104f76ee892STomi Valkeinen 		}
4105f76ee892STomi Valkeinen 
4106f76ee892STomi Valkeinen 		dsi->mgr_config.stallmode = true;
4107f76ee892STomi Valkeinen 		dsi->mgr_config.fifohandcheck = true;
4108f76ee892STomi Valkeinen 	} else {
4109f76ee892STomi Valkeinen 		dsi->mgr_config.stallmode = false;
4110f76ee892STomi Valkeinen 		dsi->mgr_config.fifohandcheck = false;
4111f76ee892STomi Valkeinen 	}
4112f76ee892STomi Valkeinen 
4113f76ee892STomi Valkeinen 	/*
4114f76ee892STomi Valkeinen 	 * override interlace, logic level and edge related parameters in
4115f76ee892STomi Valkeinen 	 * omap_video_timings with default values
4116f76ee892STomi Valkeinen 	 */
4117f76ee892STomi Valkeinen 	dsi->timings.interlace = false;
4118f76ee892STomi Valkeinen 	dsi->timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
4119f76ee892STomi Valkeinen 	dsi->timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
4120f76ee892STomi Valkeinen 	dsi->timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
4121f76ee892STomi Valkeinen 	dsi->timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH;
4122f76ee892STomi Valkeinen 	dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE;
4123f76ee892STomi Valkeinen 
4124f76ee892STomi Valkeinen 	dss_mgr_set_timings(mgr, &dsi->timings);
4125f76ee892STomi Valkeinen 
4126f76ee892STomi Valkeinen 	r = dsi_configure_dispc_clocks(dsidev);
4127f76ee892STomi Valkeinen 	if (r)
4128f76ee892STomi Valkeinen 		goto err1;
4129f76ee892STomi Valkeinen 
4130f76ee892STomi Valkeinen 	dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
4131f76ee892STomi Valkeinen 	dsi->mgr_config.video_port_width =
4132f76ee892STomi Valkeinen 			dsi_get_pixel_size(dsi->pix_fmt);
4133f76ee892STomi Valkeinen 	dsi->mgr_config.lcden_sig_polarity = 0;
4134f76ee892STomi Valkeinen 
4135f76ee892STomi Valkeinen 	dss_mgr_set_lcd_config(mgr, &dsi->mgr_config);
4136f76ee892STomi Valkeinen 
4137f76ee892STomi Valkeinen 	return 0;
4138f76ee892STomi Valkeinen err1:
4139f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
4140f76ee892STomi Valkeinen 		dss_mgr_unregister_framedone_handler(mgr,
4141f76ee892STomi Valkeinen 				dsi_framedone_irq_callback, dsidev);
4142f76ee892STomi Valkeinen err:
4143f76ee892STomi Valkeinen 	dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
4144f76ee892STomi Valkeinen 	return r;
4145f76ee892STomi Valkeinen }
4146f76ee892STomi Valkeinen 
dsi_display_uninit_dispc(struct platform_device * dsidev,struct omap_overlay_manager * mgr)4147f76ee892STomi Valkeinen static void dsi_display_uninit_dispc(struct platform_device *dsidev,
4148f76ee892STomi Valkeinen 		struct omap_overlay_manager *mgr)
4149f76ee892STomi Valkeinen {
4150f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4151f76ee892STomi Valkeinen 
4152f76ee892STomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
4153f76ee892STomi Valkeinen 		dss_mgr_unregister_framedone_handler(mgr,
4154f76ee892STomi Valkeinen 				dsi_framedone_irq_callback, dsidev);
4155f76ee892STomi Valkeinen 
4156f76ee892STomi Valkeinen 	dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
4157f76ee892STomi Valkeinen }
4158f76ee892STomi Valkeinen 
dsi_configure_dsi_clocks(struct platform_device * dsidev)4159f76ee892STomi Valkeinen static int dsi_configure_dsi_clocks(struct platform_device *dsidev)
4160f76ee892STomi Valkeinen {
4161f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4162f76ee892STomi Valkeinen 	struct dss_pll_clock_info cinfo;
4163f76ee892STomi Valkeinen 	int r;
4164f76ee892STomi Valkeinen 
4165f76ee892STomi Valkeinen 	cinfo = dsi->user_dsi_cinfo;
4166f76ee892STomi Valkeinen 
4167f76ee892STomi Valkeinen 	r = dss_pll_set_config(&dsi->pll, &cinfo);
4168f76ee892STomi Valkeinen 	if (r) {
4169f76ee892STomi Valkeinen 		DSSERR("Failed to set dsi clocks\n");
4170f76ee892STomi Valkeinen 		return r;
4171f76ee892STomi Valkeinen 	}
4172f76ee892STomi Valkeinen 
4173f76ee892STomi Valkeinen 	return 0;
4174f76ee892STomi Valkeinen }
4175f76ee892STomi Valkeinen 
dsi_display_init_dsi(struct platform_device * dsidev)4176f76ee892STomi Valkeinen static int dsi_display_init_dsi(struct platform_device *dsidev)
4177f76ee892STomi Valkeinen {
4178f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4179f76ee892STomi Valkeinen 	int r;
4180f76ee892STomi Valkeinen 
4181f76ee892STomi Valkeinen 	r = dss_pll_enable(&dsi->pll);
4182f76ee892STomi Valkeinen 	if (r)
4183f76ee892STomi Valkeinen 		goto err0;
4184f76ee892STomi Valkeinen 
4185f76ee892STomi Valkeinen 	r = dsi_configure_dsi_clocks(dsidev);
4186f76ee892STomi Valkeinen 	if (r)
4187f76ee892STomi Valkeinen 		goto err1;
4188f76ee892STomi Valkeinen 
4189f76ee892STomi Valkeinen 	dss_select_dsi_clk_source(dsi->module_id, dsi->module_id == 0 ?
4190f76ee892STomi Valkeinen 			OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
4191f76ee892STomi Valkeinen 			OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI);
4192f76ee892STomi Valkeinen 
4193f76ee892STomi Valkeinen 	DSSDBG("PLL OK\n");
4194f76ee892STomi Valkeinen 
4195f76ee892STomi Valkeinen 	r = dsi_cio_init(dsidev);
4196f76ee892STomi Valkeinen 	if (r)
4197f76ee892STomi Valkeinen 		goto err2;
4198f76ee892STomi Valkeinen 
4199f76ee892STomi Valkeinen 	_dsi_print_reset_status(dsidev);
4200f76ee892STomi Valkeinen 
4201f76ee892STomi Valkeinen 	dsi_proto_timings(dsidev);
4202f76ee892STomi Valkeinen 	dsi_set_lp_clk_divisor(dsidev);
4203f76ee892STomi Valkeinen 
4204f76ee892STomi Valkeinen 	if (1)
4205f76ee892STomi Valkeinen 		_dsi_print_reset_status(dsidev);
4206f76ee892STomi Valkeinen 
4207f76ee892STomi Valkeinen 	r = dsi_proto_config(dsidev);
4208f76ee892STomi Valkeinen 	if (r)
4209f76ee892STomi Valkeinen 		goto err3;
4210f76ee892STomi Valkeinen 
4211f76ee892STomi Valkeinen 	/* enable interface */
4212f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 0, 1);
4213f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 1, 1);
4214f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 2, 1);
4215f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 3, 1);
4216f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, 1);
4217f76ee892STomi Valkeinen 	dsi_force_tx_stop_mode_io(dsidev);
4218f76ee892STomi Valkeinen 
4219f76ee892STomi Valkeinen 	return 0;
4220f76ee892STomi Valkeinen err3:
4221f76ee892STomi Valkeinen 	dsi_cio_uninit(dsidev);
4222f76ee892STomi Valkeinen err2:
4223f76ee892STomi Valkeinen 	dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK);
4224f76ee892STomi Valkeinen err1:
4225f76ee892STomi Valkeinen 	dss_pll_disable(&dsi->pll);
4226f76ee892STomi Valkeinen err0:
4227f76ee892STomi Valkeinen 	return r;
4228f76ee892STomi Valkeinen }
4229f76ee892STomi Valkeinen 
dsi_display_uninit_dsi(struct platform_device * dsidev,bool disconnect_lanes,bool enter_ulps)4230f76ee892STomi Valkeinen static void dsi_display_uninit_dsi(struct platform_device *dsidev,
4231f76ee892STomi Valkeinen 		bool disconnect_lanes, bool enter_ulps)
4232f76ee892STomi Valkeinen {
4233f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4234f76ee892STomi Valkeinen 
4235f76ee892STomi Valkeinen 	if (enter_ulps && !dsi->ulps_enabled)
4236f76ee892STomi Valkeinen 		dsi_enter_ulps(dsidev);
4237f76ee892STomi Valkeinen 
4238f76ee892STomi Valkeinen 	/* disable interface */
4239f76ee892STomi Valkeinen 	dsi_if_enable(dsidev, 0);
4240f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 0, 0);
4241f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 1, 0);
4242f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 2, 0);
4243f76ee892STomi Valkeinen 	dsi_vc_enable(dsidev, 3, 0);
4244f76ee892STomi Valkeinen 
4245f76ee892STomi Valkeinen 	dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK);
4246f76ee892STomi Valkeinen 	dsi_cio_uninit(dsidev);
4247f76ee892STomi Valkeinen 	dsi_pll_uninit(dsidev, disconnect_lanes);
4248f76ee892STomi Valkeinen }
4249f76ee892STomi Valkeinen 
dsi_display_enable(struct omap_dss_device * dssdev)4250f76ee892STomi Valkeinen static int dsi_display_enable(struct omap_dss_device *dssdev)
4251f76ee892STomi Valkeinen {
4252f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4253f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4254f76ee892STomi Valkeinen 	int r = 0;
4255f76ee892STomi Valkeinen 
4256f76ee892STomi Valkeinen 	DSSDBG("dsi_display_enable\n");
4257f76ee892STomi Valkeinen 
4258f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
4259f76ee892STomi Valkeinen 
4260f76ee892STomi Valkeinen 	mutex_lock(&dsi->lock);
4261f76ee892STomi Valkeinen 
4262f76ee892STomi Valkeinen 	r = dsi_runtime_get(dsidev);
4263f76ee892STomi Valkeinen 	if (r)
4264f76ee892STomi Valkeinen 		goto err_get_dsi;
4265f76ee892STomi Valkeinen 
4266f76ee892STomi Valkeinen 	_dsi_initialize_irq(dsidev);
4267f76ee892STomi Valkeinen 
4268f76ee892STomi Valkeinen 	r = dsi_display_init_dsi(dsidev);
4269f76ee892STomi Valkeinen 	if (r)
4270f76ee892STomi Valkeinen 		goto err_init_dsi;
4271f76ee892STomi Valkeinen 
4272f76ee892STomi Valkeinen 	mutex_unlock(&dsi->lock);
4273f76ee892STomi Valkeinen 
4274f76ee892STomi Valkeinen 	return 0;
4275f76ee892STomi Valkeinen 
4276f76ee892STomi Valkeinen err_init_dsi:
4277f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
4278f76ee892STomi Valkeinen err_get_dsi:
4279f76ee892STomi Valkeinen 	mutex_unlock(&dsi->lock);
4280f76ee892STomi Valkeinen 	DSSDBG("dsi_display_enable FAILED\n");
4281f76ee892STomi Valkeinen 	return r;
4282f76ee892STomi Valkeinen }
4283f76ee892STomi Valkeinen 
dsi_display_disable(struct omap_dss_device * dssdev,bool disconnect_lanes,bool enter_ulps)4284f76ee892STomi Valkeinen static void dsi_display_disable(struct omap_dss_device *dssdev,
4285f76ee892STomi Valkeinen 		bool disconnect_lanes, bool enter_ulps)
4286f76ee892STomi Valkeinen {
4287f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4288f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4289f76ee892STomi Valkeinen 
4290f76ee892STomi Valkeinen 	DSSDBG("dsi_display_disable\n");
4291f76ee892STomi Valkeinen 
4292f76ee892STomi Valkeinen 	WARN_ON(!dsi_bus_is_locked(dsidev));
4293f76ee892STomi Valkeinen 
4294f76ee892STomi Valkeinen 	mutex_lock(&dsi->lock);
4295f76ee892STomi Valkeinen 
4296f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 0);
4297f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 1);
4298f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 2);
4299f76ee892STomi Valkeinen 	dsi_sync_vc(dsidev, 3);
4300f76ee892STomi Valkeinen 
4301f76ee892STomi Valkeinen 	dsi_display_uninit_dsi(dsidev, disconnect_lanes, enter_ulps);
4302f76ee892STomi Valkeinen 
4303f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
4304f76ee892STomi Valkeinen 
4305f76ee892STomi Valkeinen 	mutex_unlock(&dsi->lock);
4306f76ee892STomi Valkeinen }
4307f76ee892STomi Valkeinen 
dsi_enable_te(struct omap_dss_device * dssdev,bool enable)4308f76ee892STomi Valkeinen static int dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
4309f76ee892STomi Valkeinen {
4310f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4311f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4312f76ee892STomi Valkeinen 
4313f76ee892STomi Valkeinen 	dsi->te_enabled = enable;
4314f76ee892STomi Valkeinen 	return 0;
4315f76ee892STomi Valkeinen }
4316f76ee892STomi Valkeinen 
4317f76ee892STomi Valkeinen #ifdef PRINT_VERBOSE_VM_TIMINGS
print_dsi_vm(const char * str,const struct omap_dss_dsi_videomode_timings * t)4318f76ee892STomi Valkeinen static void print_dsi_vm(const char *str,
4319f76ee892STomi Valkeinen 		const struct omap_dss_dsi_videomode_timings *t)
4320f76ee892STomi Valkeinen {
4321f76ee892STomi Valkeinen 	unsigned long byteclk = t->hsclk / 4;
4322f76ee892STomi Valkeinen 	int bl, wc, pps, tot;
4323f76ee892STomi Valkeinen 
4324f76ee892STomi Valkeinen 	wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
4325f76ee892STomi Valkeinen 	pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
4326f76ee892STomi Valkeinen 	bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
4327f76ee892STomi Valkeinen 	tot = bl + pps;
4328f76ee892STomi Valkeinen 
4329f76ee892STomi Valkeinen #define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
4330f76ee892STomi Valkeinen 
4331f76ee892STomi Valkeinen 	pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, "
4332f76ee892STomi Valkeinen 			"%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
4333f76ee892STomi Valkeinen 			str,
4334f76ee892STomi Valkeinen 			byteclk,
4335f76ee892STomi Valkeinen 			t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
4336f76ee892STomi Valkeinen 			bl, pps, tot,
4337f76ee892STomi Valkeinen 			TO_DSI_T(t->hss),
4338f76ee892STomi Valkeinen 			TO_DSI_T(t->hsa),
4339f76ee892STomi Valkeinen 			TO_DSI_T(t->hse),
4340f76ee892STomi Valkeinen 			TO_DSI_T(t->hbp),
4341f76ee892STomi Valkeinen 			TO_DSI_T(pps),
4342f76ee892STomi Valkeinen 			TO_DSI_T(t->hfp),
4343f76ee892STomi Valkeinen 
4344f76ee892STomi Valkeinen 			TO_DSI_T(bl),
4345f76ee892STomi Valkeinen 			TO_DSI_T(pps),
4346f76ee892STomi Valkeinen 
4347f76ee892STomi Valkeinen 			TO_DSI_T(tot));
4348f76ee892STomi Valkeinen #undef TO_DSI_T
4349f76ee892STomi Valkeinen }
4350f76ee892STomi Valkeinen 
print_dispc_vm(const char * str,const struct omap_video_timings * t)4351f76ee892STomi Valkeinen static void print_dispc_vm(const char *str, const struct omap_video_timings *t)
4352f76ee892STomi Valkeinen {
4353f76ee892STomi Valkeinen 	unsigned long pck = t->pixelclock;
4354f76ee892STomi Valkeinen 	int hact, bl, tot;
4355f76ee892STomi Valkeinen 
4356f76ee892STomi Valkeinen 	hact = t->x_res;
4357f76ee892STomi Valkeinen 	bl = t->hsw + t->hbp + t->hfp;
4358f76ee892STomi Valkeinen 	tot = hact + bl;
4359f76ee892STomi Valkeinen 
4360f76ee892STomi Valkeinen #define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
4361f76ee892STomi Valkeinen 
4362f76ee892STomi Valkeinen 	pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, "
4363f76ee892STomi Valkeinen 			"%u/%u/%u/%u = %u + %u = %u\n",
4364f76ee892STomi Valkeinen 			str,
4365f76ee892STomi Valkeinen 			pck,
4366f76ee892STomi Valkeinen 			t->hsw, t->hbp, hact, t->hfp,
4367f76ee892STomi Valkeinen 			bl, hact, tot,
4368f76ee892STomi Valkeinen 			TO_DISPC_T(t->hsw),
4369f76ee892STomi Valkeinen 			TO_DISPC_T(t->hbp),
4370f76ee892STomi Valkeinen 			TO_DISPC_T(hact),
4371f76ee892STomi Valkeinen 			TO_DISPC_T(t->hfp),
4372f76ee892STomi Valkeinen 			TO_DISPC_T(bl),
4373f76ee892STomi Valkeinen 			TO_DISPC_T(hact),
4374f76ee892STomi Valkeinen 			TO_DISPC_T(tot));
4375f76ee892STomi Valkeinen #undef TO_DISPC_T
4376f76ee892STomi Valkeinen }
4377f76ee892STomi Valkeinen 
4378f76ee892STomi Valkeinen /* note: this is not quite accurate */
print_dsi_dispc_vm(const char * str,const struct omap_dss_dsi_videomode_timings * t)4379f76ee892STomi Valkeinen static void print_dsi_dispc_vm(const char *str,
4380f76ee892STomi Valkeinen 		const struct omap_dss_dsi_videomode_timings *t)
4381f76ee892STomi Valkeinen {
4382f76ee892STomi Valkeinen 	struct omap_video_timings vm = { 0 };
4383f76ee892STomi Valkeinen 	unsigned long byteclk = t->hsclk / 4;
4384f76ee892STomi Valkeinen 	unsigned long pck;
4385f76ee892STomi Valkeinen 	u64 dsi_tput;
4386f76ee892STomi Valkeinen 	int dsi_hact, dsi_htot;
4387f76ee892STomi Valkeinen 
4388f76ee892STomi Valkeinen 	dsi_tput = (u64)byteclk * t->ndl * 8;
4389f76ee892STomi Valkeinen 	pck = (u32)div64_u64(dsi_tput, t->bitspp);
4390f76ee892STomi Valkeinen 	dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
4391f76ee892STomi Valkeinen 	dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
4392f76ee892STomi Valkeinen 
4393f76ee892STomi Valkeinen 	vm.pixelclock = pck;
4394f76ee892STomi Valkeinen 	vm.hsw = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
4395f76ee892STomi Valkeinen 	vm.hbp = div64_u64((u64)t->hbp * pck, byteclk);
4396f76ee892STomi Valkeinen 	vm.hfp = div64_u64((u64)t->hfp * pck, byteclk);
4397f76ee892STomi Valkeinen 	vm.x_res = t->hact;
4398f76ee892STomi Valkeinen 
4399f76ee892STomi Valkeinen 	print_dispc_vm(str, &vm);
4400f76ee892STomi Valkeinen }
4401f76ee892STomi Valkeinen #endif /* PRINT_VERBOSE_VM_TIMINGS */
4402f76ee892STomi Valkeinen 
dsi_cm_calc_dispc_cb(int lckd,int pckd,unsigned long lck,unsigned long pck,void * data)4403f76ee892STomi Valkeinen static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
4404f76ee892STomi Valkeinen 		unsigned long pck, void *data)
4405f76ee892STomi Valkeinen {
4406f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
4407f76ee892STomi Valkeinen 	struct omap_video_timings *t = &ctx->dispc_vm;
4408f76ee892STomi Valkeinen 
4409f76ee892STomi Valkeinen 	ctx->dispc_cinfo.lck_div = lckd;
4410f76ee892STomi Valkeinen 	ctx->dispc_cinfo.pck_div = pckd;
4411f76ee892STomi Valkeinen 	ctx->dispc_cinfo.lck = lck;
4412f76ee892STomi Valkeinen 	ctx->dispc_cinfo.pck = pck;
4413f76ee892STomi Valkeinen 
4414f76ee892STomi Valkeinen 	*t = *ctx->config->timings;
4415f76ee892STomi Valkeinen 	t->pixelclock = pck;
4416f76ee892STomi Valkeinen 	t->x_res = ctx->config->timings->x_res;
4417f76ee892STomi Valkeinen 	t->y_res = ctx->config->timings->y_res;
4418f76ee892STomi Valkeinen 	t->hsw = t->hfp = t->hbp = t->vsw = 1;
4419f76ee892STomi Valkeinen 	t->vfp = t->vbp = 0;
4420f76ee892STomi Valkeinen 
4421f76ee892STomi Valkeinen 	return true;
4422f76ee892STomi Valkeinen }
4423f76ee892STomi Valkeinen 
dsi_cm_calc_hsdiv_cb(int m_dispc,unsigned long dispc,void * data)4424f76ee892STomi Valkeinen static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
4425f76ee892STomi Valkeinen 		void *data)
4426f76ee892STomi Valkeinen {
4427f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
4428f76ee892STomi Valkeinen 
4429f76ee892STomi Valkeinen 	ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
4430f76ee892STomi Valkeinen 	ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
4431f76ee892STomi Valkeinen 
4432f76ee892STomi Valkeinen 	return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max,
4433f76ee892STomi Valkeinen 			dsi_cm_calc_dispc_cb, ctx);
4434f76ee892STomi Valkeinen }
4435f76ee892STomi Valkeinen 
dsi_cm_calc_pll_cb(int n,int m,unsigned long fint,unsigned long clkdco,void * data)4436f76ee892STomi Valkeinen static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint,
4437f76ee892STomi Valkeinen 		unsigned long clkdco, void *data)
4438f76ee892STomi Valkeinen {
4439f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
4440f76ee892STomi Valkeinen 
4441f76ee892STomi Valkeinen 	ctx->dsi_cinfo.n = n;
4442f76ee892STomi Valkeinen 	ctx->dsi_cinfo.m = m;
4443f76ee892STomi Valkeinen 	ctx->dsi_cinfo.fint = fint;
4444f76ee892STomi Valkeinen 	ctx->dsi_cinfo.clkdco = clkdco;
4445f76ee892STomi Valkeinen 
4446f76ee892STomi Valkeinen 	return dss_pll_hsdiv_calc(ctx->pll, clkdco, ctx->req_pck_min,
4447f76ee892STomi Valkeinen 			dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
4448f76ee892STomi Valkeinen 			dsi_cm_calc_hsdiv_cb, ctx);
4449f76ee892STomi Valkeinen }
4450f76ee892STomi Valkeinen 
dsi_cm_calc(struct dsi_data * dsi,const struct omap_dss_dsi_config * cfg,struct dsi_clk_calc_ctx * ctx)4451f76ee892STomi Valkeinen static bool dsi_cm_calc(struct dsi_data *dsi,
4452f76ee892STomi Valkeinen 		const struct omap_dss_dsi_config *cfg,
4453f76ee892STomi Valkeinen 		struct dsi_clk_calc_ctx *ctx)
4454f76ee892STomi Valkeinen {
4455f76ee892STomi Valkeinen 	unsigned long clkin;
4456f76ee892STomi Valkeinen 	int bitspp, ndl;
4457f76ee892STomi Valkeinen 	unsigned long pll_min, pll_max;
4458f76ee892STomi Valkeinen 	unsigned long pck, txbyteclk;
4459f76ee892STomi Valkeinen 
4460f76ee892STomi Valkeinen 	clkin = clk_get_rate(dsi->pll.clkin);
4461f76ee892STomi Valkeinen 	bitspp = dsi_get_pixel_size(cfg->pixel_format);
4462f76ee892STomi Valkeinen 	ndl = dsi->num_lanes_used - 1;
4463f76ee892STomi Valkeinen 
4464f76ee892STomi Valkeinen 	/*
4465f76ee892STomi Valkeinen 	 * Here we should calculate minimum txbyteclk to be able to send the
4466f76ee892STomi Valkeinen 	 * frame in time, and also to handle TE. That's not very simple, though,
4467f76ee892STomi Valkeinen 	 * especially as we go to LP between each pixel packet due to HW
4468f76ee892STomi Valkeinen 	 * "feature". So let's just estimate very roughly and multiply by 1.5.
4469f76ee892STomi Valkeinen 	 */
4470f76ee892STomi Valkeinen 	pck = cfg->timings->pixelclock;
4471f76ee892STomi Valkeinen 	pck = pck * 3 / 2;
4472f76ee892STomi Valkeinen 	txbyteclk = pck * bitspp / 8 / ndl;
4473f76ee892STomi Valkeinen 
4474f76ee892STomi Valkeinen 	memset(ctx, 0, sizeof(*ctx));
4475f76ee892STomi Valkeinen 	ctx->dsidev = dsi->pdev;
4476f76ee892STomi Valkeinen 	ctx->pll = &dsi->pll;
4477f76ee892STomi Valkeinen 	ctx->config = cfg;
4478f76ee892STomi Valkeinen 	ctx->req_pck_min = pck;
4479f76ee892STomi Valkeinen 	ctx->req_pck_nom = pck;
4480f76ee892STomi Valkeinen 	ctx->req_pck_max = pck * 3 / 2;
4481f76ee892STomi Valkeinen 
4482f76ee892STomi Valkeinen 	pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4);
4483f76ee892STomi Valkeinen 	pll_max = cfg->hs_clk_max * 4;
4484f76ee892STomi Valkeinen 
4485f76ee892STomi Valkeinen 	return dss_pll_calc(ctx->pll, clkin,
4486f76ee892STomi Valkeinen 			pll_min, pll_max,
4487f76ee892STomi Valkeinen 			dsi_cm_calc_pll_cb, ctx);
4488f76ee892STomi Valkeinen }
4489f76ee892STomi Valkeinen 
dsi_vm_calc_blanking(struct dsi_clk_calc_ctx * ctx)4490f76ee892STomi Valkeinen static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
4491f76ee892STomi Valkeinen {
4492f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(ctx->dsidev);
4493f76ee892STomi Valkeinen 	const struct omap_dss_dsi_config *cfg = ctx->config;
4494f76ee892STomi Valkeinen 	int bitspp = dsi_get_pixel_size(cfg->pixel_format);
4495f76ee892STomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
4496f76ee892STomi Valkeinen 	unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4;
4497f76ee892STomi Valkeinen 	unsigned long byteclk = hsclk / 4;
4498f76ee892STomi Valkeinen 
4499f76ee892STomi Valkeinen 	unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max;
4500f76ee892STomi Valkeinen 	int xres;
4501f76ee892STomi Valkeinen 	int panel_htot, panel_hbl; /* pixels */
4502f76ee892STomi Valkeinen 	int dispc_htot, dispc_hbl; /* pixels */
4503f76ee892STomi Valkeinen 	int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */
4504f76ee892STomi Valkeinen 	int hfp, hsa, hbp;
4505f76ee892STomi Valkeinen 	const struct omap_video_timings *req_vm;
4506f76ee892STomi Valkeinen 	struct omap_video_timings *dispc_vm;
4507f76ee892STomi Valkeinen 	struct omap_dss_dsi_videomode_timings *dsi_vm;
4508f76ee892STomi Valkeinen 	u64 dsi_tput, dispc_tput;
4509f76ee892STomi Valkeinen 
4510f76ee892STomi Valkeinen 	dsi_tput = (u64)byteclk * ndl * 8;
4511f76ee892STomi Valkeinen 
4512f76ee892STomi Valkeinen 	req_vm = cfg->timings;
4513f76ee892STomi Valkeinen 	req_pck_min = ctx->req_pck_min;
4514f76ee892STomi Valkeinen 	req_pck_max = ctx->req_pck_max;
4515f76ee892STomi Valkeinen 	req_pck_nom = ctx->req_pck_nom;
4516f76ee892STomi Valkeinen 
4517f76ee892STomi Valkeinen 	dispc_pck = ctx->dispc_cinfo.pck;
4518f76ee892STomi Valkeinen 	dispc_tput = (u64)dispc_pck * bitspp;
4519f76ee892STomi Valkeinen 
4520f76ee892STomi Valkeinen 	xres = req_vm->x_res;
4521f76ee892STomi Valkeinen 
4522f76ee892STomi Valkeinen 	panel_hbl = req_vm->hfp + req_vm->hbp + req_vm->hsw;
4523f76ee892STomi Valkeinen 	panel_htot = xres + panel_hbl;
4524f76ee892STomi Valkeinen 
4525f76ee892STomi Valkeinen 	dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl);
4526f76ee892STomi Valkeinen 
4527f76ee892STomi Valkeinen 	/*
4528f76ee892STomi Valkeinen 	 * When there are no line buffers, DISPC and DSI must have the
4529f76ee892STomi Valkeinen 	 * same tput. Otherwise DISPC tput needs to be higher than DSI's.
4530f76ee892STomi Valkeinen 	 */
4531f76ee892STomi Valkeinen 	if (dsi->line_buffer_size < xres * bitspp / 8) {
4532f76ee892STomi Valkeinen 		if (dispc_tput != dsi_tput)
4533f76ee892STomi Valkeinen 			return false;
4534f76ee892STomi Valkeinen 	} else {
4535f76ee892STomi Valkeinen 		if (dispc_tput < dsi_tput)
4536f76ee892STomi Valkeinen 			return false;
4537f76ee892STomi Valkeinen 	}
4538f76ee892STomi Valkeinen 
4539f76ee892STomi Valkeinen 	/* DSI tput must be over the min requirement */
4540f76ee892STomi Valkeinen 	if (dsi_tput < (u64)bitspp * req_pck_min)
4541f76ee892STomi Valkeinen 		return false;
4542f76ee892STomi Valkeinen 
4543f76ee892STomi Valkeinen 	/* When non-burst mode, DSI tput must be below max requirement. */
4544f76ee892STomi Valkeinen 	if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) {
4545f76ee892STomi Valkeinen 		if (dsi_tput > (u64)bitspp * req_pck_max)
4546f76ee892STomi Valkeinen 			return false;
4547f76ee892STomi Valkeinen 	}
4548f76ee892STomi Valkeinen 
4549f76ee892STomi Valkeinen 	hss = DIV_ROUND_UP(4, ndl);
4550f76ee892STomi Valkeinen 
4551f76ee892STomi Valkeinen 	if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
4552f76ee892STomi Valkeinen 		if (ndl == 3 && req_vm->hsw == 0)
4553f76ee892STomi Valkeinen 			hse = 1;
4554f76ee892STomi Valkeinen 		else
4555f76ee892STomi Valkeinen 			hse = DIV_ROUND_UP(4, ndl);
4556f76ee892STomi Valkeinen 	} else {
4557f76ee892STomi Valkeinen 		hse = 0;
4558f76ee892STomi Valkeinen 	}
4559f76ee892STomi Valkeinen 
4560f76ee892STomi Valkeinen 	/* DSI htot to match the panel's nominal pck */
4561f76ee892STomi Valkeinen 	dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom);
4562f76ee892STomi Valkeinen 
4563f76ee892STomi Valkeinen 	/* fail if there would be no time for blanking */
4564f76ee892STomi Valkeinen 	if (dsi_htot < hss + hse + dsi_hact)
4565f76ee892STomi Valkeinen 		return false;
4566f76ee892STomi Valkeinen 
4567f76ee892STomi Valkeinen 	/* total DSI blanking needed to achieve panel's TL */
4568f76ee892STomi Valkeinen 	dsi_hbl = dsi_htot - dsi_hact;
4569f76ee892STomi Valkeinen 
4570f76ee892STomi Valkeinen 	/* DISPC htot to match the DSI TL */
4571f76ee892STomi Valkeinen 	dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk);
4572f76ee892STomi Valkeinen 
4573f76ee892STomi Valkeinen 	/* verify that the DSI and DISPC TLs are the same */
4574f76ee892STomi Valkeinen 	if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk)
4575f76ee892STomi Valkeinen 		return false;
4576f76ee892STomi Valkeinen 
4577f76ee892STomi Valkeinen 	dispc_hbl = dispc_htot - xres;
4578f76ee892STomi Valkeinen 
4579f76ee892STomi Valkeinen 	/* setup DSI videomode */
4580f76ee892STomi Valkeinen 
4581f76ee892STomi Valkeinen 	dsi_vm = &ctx->dsi_vm;
4582f76ee892STomi Valkeinen 	memset(dsi_vm, 0, sizeof(*dsi_vm));
4583f76ee892STomi Valkeinen 
4584f76ee892STomi Valkeinen 	dsi_vm->hsclk = hsclk;
4585f76ee892STomi Valkeinen 
4586f76ee892STomi Valkeinen 	dsi_vm->ndl = ndl;
4587f76ee892STomi Valkeinen 	dsi_vm->bitspp = bitspp;
4588f76ee892STomi Valkeinen 
4589f76ee892STomi Valkeinen 	if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) {
4590f76ee892STomi Valkeinen 		hsa = 0;
4591f76ee892STomi Valkeinen 	} else if (ndl == 3 && req_vm->hsw == 0) {
4592f76ee892STomi Valkeinen 		hsa = 0;
4593f76ee892STomi Valkeinen 	} else {
4594f76ee892STomi Valkeinen 		hsa = div64_u64((u64)req_vm->hsw * byteclk, req_pck_nom);
4595f76ee892STomi Valkeinen 		hsa = max(hsa - hse, 1);
4596f76ee892STomi Valkeinen 	}
4597f76ee892STomi Valkeinen 
4598f76ee892STomi Valkeinen 	hbp = div64_u64((u64)req_vm->hbp * byteclk, req_pck_nom);
4599f76ee892STomi Valkeinen 	hbp = max(hbp, 1);
4600f76ee892STomi Valkeinen 
4601f76ee892STomi Valkeinen 	hfp = dsi_hbl - (hss + hsa + hse + hbp);
4602f76ee892STomi Valkeinen 	if (hfp < 1) {
4603f76ee892STomi Valkeinen 		int t;
4604f76ee892STomi Valkeinen 		/* we need to take cycles from hbp */
4605f76ee892STomi Valkeinen 
4606f76ee892STomi Valkeinen 		t = 1 - hfp;
4607f76ee892STomi Valkeinen 		hbp = max(hbp - t, 1);
4608f76ee892STomi Valkeinen 		hfp = dsi_hbl - (hss + hsa + hse + hbp);
4609f76ee892STomi Valkeinen 
4610f76ee892STomi Valkeinen 		if (hfp < 1 && hsa > 0) {
4611f76ee892STomi Valkeinen 			/* we need to take cycles from hsa */
4612f76ee892STomi Valkeinen 			t = 1 - hfp;
4613f76ee892STomi Valkeinen 			hsa = max(hsa - t, 1);
4614f76ee892STomi Valkeinen 			hfp = dsi_hbl - (hss + hsa + hse + hbp);
4615f76ee892STomi Valkeinen 		}
4616f76ee892STomi Valkeinen 	}
4617f76ee892STomi Valkeinen 
4618f76ee892STomi Valkeinen 	if (hfp < 1)
4619f76ee892STomi Valkeinen 		return false;
4620f76ee892STomi Valkeinen 
4621f76ee892STomi Valkeinen 	dsi_vm->hss = hss;
4622f76ee892STomi Valkeinen 	dsi_vm->hsa = hsa;
4623f76ee892STomi Valkeinen 	dsi_vm->hse = hse;
4624f76ee892STomi Valkeinen 	dsi_vm->hbp = hbp;
4625f76ee892STomi Valkeinen 	dsi_vm->hact = xres;
4626f76ee892STomi Valkeinen 	dsi_vm->hfp = hfp;
4627f76ee892STomi Valkeinen 
4628f76ee892STomi Valkeinen 	dsi_vm->vsa = req_vm->vsw;
4629f76ee892STomi Valkeinen 	dsi_vm->vbp = req_vm->vbp;
4630f76ee892STomi Valkeinen 	dsi_vm->vact = req_vm->y_res;
4631f76ee892STomi Valkeinen 	dsi_vm->vfp = req_vm->vfp;
4632f76ee892STomi Valkeinen 
4633f76ee892STomi Valkeinen 	dsi_vm->trans_mode = cfg->trans_mode;
4634f76ee892STomi Valkeinen 
4635f76ee892STomi Valkeinen 	dsi_vm->blanking_mode = 0;
4636f76ee892STomi Valkeinen 	dsi_vm->hsa_blanking_mode = 1;
4637f76ee892STomi Valkeinen 	dsi_vm->hfp_blanking_mode = 1;
4638f76ee892STomi Valkeinen 	dsi_vm->hbp_blanking_mode = 1;
4639f76ee892STomi Valkeinen 
4640f76ee892STomi Valkeinen 	dsi_vm->ddr_clk_always_on = cfg->ddr_clk_always_on;
4641f76ee892STomi Valkeinen 	dsi_vm->window_sync = 4;
4642f76ee892STomi Valkeinen 
4643f76ee892STomi Valkeinen 	/* setup DISPC videomode */
4644f76ee892STomi Valkeinen 
4645f76ee892STomi Valkeinen 	dispc_vm = &ctx->dispc_vm;
4646f76ee892STomi Valkeinen 	*dispc_vm = *req_vm;
4647f76ee892STomi Valkeinen 	dispc_vm->pixelclock = dispc_pck;
4648f76ee892STomi Valkeinen 
4649f76ee892STomi Valkeinen 	if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
4650f76ee892STomi Valkeinen 		hsa = div64_u64((u64)req_vm->hsw * dispc_pck,
4651f76ee892STomi Valkeinen 				req_pck_nom);
4652f76ee892STomi Valkeinen 		hsa = max(hsa, 1);
4653f76ee892STomi Valkeinen 	} else {
4654f76ee892STomi Valkeinen 		hsa = 1;
4655f76ee892STomi Valkeinen 	}
4656f76ee892STomi Valkeinen 
4657f76ee892STomi Valkeinen 	hbp = div64_u64((u64)req_vm->hbp * dispc_pck, req_pck_nom);
4658f76ee892STomi Valkeinen 	hbp = max(hbp, 1);
4659f76ee892STomi Valkeinen 
4660f76ee892STomi Valkeinen 	hfp = dispc_hbl - hsa - hbp;
4661f76ee892STomi Valkeinen 	if (hfp < 1) {
4662f76ee892STomi Valkeinen 		int t;
4663f76ee892STomi Valkeinen 		/* we need to take cycles from hbp */
4664f76ee892STomi Valkeinen 
4665f76ee892STomi Valkeinen 		t = 1 - hfp;
4666f76ee892STomi Valkeinen 		hbp = max(hbp - t, 1);
4667f76ee892STomi Valkeinen 		hfp = dispc_hbl - hsa - hbp;
4668f76ee892STomi Valkeinen 
4669f76ee892STomi Valkeinen 		if (hfp < 1) {
4670f76ee892STomi Valkeinen 			/* we need to take cycles from hsa */
4671f76ee892STomi Valkeinen 			t = 1 - hfp;
4672f76ee892STomi Valkeinen 			hsa = max(hsa - t, 1);
4673f76ee892STomi Valkeinen 			hfp = dispc_hbl - hsa - hbp;
4674f76ee892STomi Valkeinen 		}
4675f76ee892STomi Valkeinen 	}
4676f76ee892STomi Valkeinen 
4677f76ee892STomi Valkeinen 	if (hfp < 1)
4678f76ee892STomi Valkeinen 		return false;
4679f76ee892STomi Valkeinen 
4680f76ee892STomi Valkeinen 	dispc_vm->hfp = hfp;
4681f76ee892STomi Valkeinen 	dispc_vm->hsw = hsa;
4682f76ee892STomi Valkeinen 	dispc_vm->hbp = hbp;
4683f76ee892STomi Valkeinen 
4684f76ee892STomi Valkeinen 	return true;
4685f76ee892STomi Valkeinen }
4686f76ee892STomi Valkeinen 
4687f76ee892STomi Valkeinen 
dsi_vm_calc_dispc_cb(int lckd,int pckd,unsigned long lck,unsigned long pck,void * data)4688f76ee892STomi Valkeinen static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
4689f76ee892STomi Valkeinen 		unsigned long pck, void *data)
4690f76ee892STomi Valkeinen {
4691f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
4692f76ee892STomi Valkeinen 
4693f76ee892STomi Valkeinen 	ctx->dispc_cinfo.lck_div = lckd;
4694f76ee892STomi Valkeinen 	ctx->dispc_cinfo.pck_div = pckd;
4695f76ee892STomi Valkeinen 	ctx->dispc_cinfo.lck = lck;
4696f76ee892STomi Valkeinen 	ctx->dispc_cinfo.pck = pck;
4697f76ee892STomi Valkeinen 
4698f76ee892STomi Valkeinen 	if (dsi_vm_calc_blanking(ctx) == false)
4699f76ee892STomi Valkeinen 		return false;
4700f76ee892STomi Valkeinen 
4701f76ee892STomi Valkeinen #ifdef PRINT_VERBOSE_VM_TIMINGS
4702f76ee892STomi Valkeinen 	print_dispc_vm("dispc", &ctx->dispc_vm);
4703f76ee892STomi Valkeinen 	print_dsi_vm("dsi  ", &ctx->dsi_vm);
4704f76ee892STomi Valkeinen 	print_dispc_vm("req  ", ctx->config->timings);
4705f76ee892STomi Valkeinen 	print_dsi_dispc_vm("act  ", &ctx->dsi_vm);
4706f76ee892STomi Valkeinen #endif
4707f76ee892STomi Valkeinen 
4708f76ee892STomi Valkeinen 	return true;
4709f76ee892STomi Valkeinen }
4710f76ee892STomi Valkeinen 
dsi_vm_calc_hsdiv_cb(int m_dispc,unsigned long dispc,void * data)4711f76ee892STomi Valkeinen static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
4712f76ee892STomi Valkeinen 		void *data)
4713f76ee892STomi Valkeinen {
4714f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
4715f76ee892STomi Valkeinen 	unsigned long pck_max;
4716f76ee892STomi Valkeinen 
4717f76ee892STomi Valkeinen 	ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
4718f76ee892STomi Valkeinen 	ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
4719f76ee892STomi Valkeinen 
4720f76ee892STomi Valkeinen 	/*
4721f76ee892STomi Valkeinen 	 * In burst mode we can let the dispc pck be arbitrarily high, but it
4722f76ee892STomi Valkeinen 	 * limits our scaling abilities. So for now, don't aim too high.
4723f76ee892STomi Valkeinen 	 */
4724f76ee892STomi Valkeinen 
4725f76ee892STomi Valkeinen 	if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE)
4726f76ee892STomi Valkeinen 		pck_max = ctx->req_pck_max + 10000000;
4727f76ee892STomi Valkeinen 	else
4728f76ee892STomi Valkeinen 		pck_max = ctx->req_pck_max;
4729f76ee892STomi Valkeinen 
4730f76ee892STomi Valkeinen 	return dispc_div_calc(dispc, ctx->req_pck_min, pck_max,
4731f76ee892STomi Valkeinen 			dsi_vm_calc_dispc_cb, ctx);
4732f76ee892STomi Valkeinen }
4733f76ee892STomi Valkeinen 
dsi_vm_calc_pll_cb(int n,int m,unsigned long fint,unsigned long clkdco,void * data)4734f76ee892STomi Valkeinen static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint,
4735f76ee892STomi Valkeinen 		unsigned long clkdco, void *data)
4736f76ee892STomi Valkeinen {
4737f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
4738f76ee892STomi Valkeinen 
4739f76ee892STomi Valkeinen 	ctx->dsi_cinfo.n = n;
4740f76ee892STomi Valkeinen 	ctx->dsi_cinfo.m = m;
4741f76ee892STomi Valkeinen 	ctx->dsi_cinfo.fint = fint;
4742f76ee892STomi Valkeinen 	ctx->dsi_cinfo.clkdco = clkdco;
4743f76ee892STomi Valkeinen 
4744f76ee892STomi Valkeinen 	return dss_pll_hsdiv_calc(ctx->pll, clkdco, ctx->req_pck_min,
4745f76ee892STomi Valkeinen 			dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
4746f76ee892STomi Valkeinen 			dsi_vm_calc_hsdiv_cb, ctx);
4747f76ee892STomi Valkeinen }
4748f76ee892STomi Valkeinen 
dsi_vm_calc(struct dsi_data * dsi,const struct omap_dss_dsi_config * cfg,struct dsi_clk_calc_ctx * ctx)4749f76ee892STomi Valkeinen static bool dsi_vm_calc(struct dsi_data *dsi,
4750f76ee892STomi Valkeinen 		const struct omap_dss_dsi_config *cfg,
4751f76ee892STomi Valkeinen 		struct dsi_clk_calc_ctx *ctx)
4752f76ee892STomi Valkeinen {
4753f76ee892STomi Valkeinen 	const struct omap_video_timings *t = cfg->timings;
4754f76ee892STomi Valkeinen 	unsigned long clkin;
4755f76ee892STomi Valkeinen 	unsigned long pll_min;
4756f76ee892STomi Valkeinen 	unsigned long pll_max;
4757f76ee892STomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
4758f76ee892STomi Valkeinen 	int bitspp = dsi_get_pixel_size(cfg->pixel_format);
4759f76ee892STomi Valkeinen 	unsigned long byteclk_min;
4760f76ee892STomi Valkeinen 
4761f76ee892STomi Valkeinen 	clkin = clk_get_rate(dsi->pll.clkin);
4762f76ee892STomi Valkeinen 
4763f76ee892STomi Valkeinen 	memset(ctx, 0, sizeof(*ctx));
4764f76ee892STomi Valkeinen 	ctx->dsidev = dsi->pdev;
4765f76ee892STomi Valkeinen 	ctx->pll = &dsi->pll;
4766f76ee892STomi Valkeinen 	ctx->config = cfg;
4767f76ee892STomi Valkeinen 
4768f76ee892STomi Valkeinen 	/* these limits should come from the panel driver */
4769f76ee892STomi Valkeinen 	ctx->req_pck_min = t->pixelclock - 1000;
4770f76ee892STomi Valkeinen 	ctx->req_pck_nom = t->pixelclock;
4771f76ee892STomi Valkeinen 	ctx->req_pck_max = t->pixelclock + 1000;
4772f76ee892STomi Valkeinen 
4773f76ee892STomi Valkeinen 	byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
4774f76ee892STomi Valkeinen 	pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
4775f76ee892STomi Valkeinen 
4776f76ee892STomi Valkeinen 	if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) {
4777f76ee892STomi Valkeinen 		pll_max = cfg->hs_clk_max * 4;
4778f76ee892STomi Valkeinen 	} else {
4779f76ee892STomi Valkeinen 		unsigned long byteclk_max;
4780f76ee892STomi Valkeinen 		byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp,
4781f76ee892STomi Valkeinen 				ndl * 8);
4782f76ee892STomi Valkeinen 
4783f76ee892STomi Valkeinen 		pll_max = byteclk_max * 4 * 4;
4784f76ee892STomi Valkeinen 	}
4785f76ee892STomi Valkeinen 
4786f76ee892STomi Valkeinen 	return dss_pll_calc(ctx->pll, clkin,
4787f76ee892STomi Valkeinen 			pll_min, pll_max,
4788f76ee892STomi Valkeinen 			dsi_vm_calc_pll_cb, ctx);
4789f76ee892STomi Valkeinen }
4790f76ee892STomi Valkeinen 
dsi_set_config(struct omap_dss_device * dssdev,const struct omap_dss_dsi_config * config)4791f76ee892STomi Valkeinen static int dsi_set_config(struct omap_dss_device *dssdev,
4792f76ee892STomi Valkeinen 		const struct omap_dss_dsi_config *config)
4793f76ee892STomi Valkeinen {
4794f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4795f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4796f76ee892STomi Valkeinen 	struct dsi_clk_calc_ctx ctx;
4797f76ee892STomi Valkeinen 	bool ok;
4798f76ee892STomi Valkeinen 	int r;
4799f76ee892STomi Valkeinen 
4800f76ee892STomi Valkeinen 	mutex_lock(&dsi->lock);
4801f76ee892STomi Valkeinen 
4802f76ee892STomi Valkeinen 	dsi->pix_fmt = config->pixel_format;
4803f76ee892STomi Valkeinen 	dsi->mode = config->mode;
4804f76ee892STomi Valkeinen 
4805f76ee892STomi Valkeinen 	if (config->mode == OMAP_DSS_DSI_VIDEO_MODE)
4806f76ee892STomi Valkeinen 		ok = dsi_vm_calc(dsi, config, &ctx);
4807f76ee892STomi Valkeinen 	else
4808f76ee892STomi Valkeinen 		ok = dsi_cm_calc(dsi, config, &ctx);
4809f76ee892STomi Valkeinen 
4810f76ee892STomi Valkeinen 	if (!ok) {
4811f76ee892STomi Valkeinen 		DSSERR("failed to find suitable DSI clock settings\n");
4812f76ee892STomi Valkeinen 		r = -EINVAL;
4813f76ee892STomi Valkeinen 		goto err;
4814f76ee892STomi Valkeinen 	}
4815f76ee892STomi Valkeinen 
4816f76ee892STomi Valkeinen 	dsi_pll_calc_dsi_fck(&ctx.dsi_cinfo);
4817f76ee892STomi Valkeinen 
4818f76ee892STomi Valkeinen 	r = dsi_lp_clock_calc(ctx.dsi_cinfo.clkout[HSDIV_DSI],
4819f76ee892STomi Valkeinen 		config->lp_clk_min, config->lp_clk_max, &dsi->user_lp_cinfo);
4820f76ee892STomi Valkeinen 	if (r) {
4821f76ee892STomi Valkeinen 		DSSERR("failed to find suitable DSI LP clock settings\n");
4822f76ee892STomi Valkeinen 		goto err;
4823f76ee892STomi Valkeinen 	}
4824f76ee892STomi Valkeinen 
4825f76ee892STomi Valkeinen 	dsi->user_dsi_cinfo = ctx.dsi_cinfo;
4826f76ee892STomi Valkeinen 	dsi->user_dispc_cinfo = ctx.dispc_cinfo;
4827f76ee892STomi Valkeinen 
4828f76ee892STomi Valkeinen 	dsi->timings = ctx.dispc_vm;
4829f76ee892STomi Valkeinen 	dsi->vm_timings = ctx.dsi_vm;
4830f76ee892STomi Valkeinen 
4831f76ee892STomi Valkeinen 	mutex_unlock(&dsi->lock);
4832f76ee892STomi Valkeinen 
4833f76ee892STomi Valkeinen 	return 0;
4834f76ee892STomi Valkeinen err:
4835f76ee892STomi Valkeinen 	mutex_unlock(&dsi->lock);
4836f76ee892STomi Valkeinen 
4837f76ee892STomi Valkeinen 	return r;
4838f76ee892STomi Valkeinen }
4839f76ee892STomi Valkeinen 
4840f76ee892STomi Valkeinen /*
4841f76ee892STomi Valkeinen  * Return a hardcoded channel for the DSI output. This should work for
4842f76ee892STomi Valkeinen  * current use cases, but this can be later expanded to either resolve
4843f76ee892STomi Valkeinen  * the channel in some more dynamic manner, or get the channel as a user
4844f76ee892STomi Valkeinen  * parameter.
4845f76ee892STomi Valkeinen  */
dsi_get_channel(int module_id)4846f76ee892STomi Valkeinen static enum omap_channel dsi_get_channel(int module_id)
4847f76ee892STomi Valkeinen {
4848f76ee892STomi Valkeinen 	switch (omapdss_get_version()) {
4849f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP24xx:
4850f76ee892STomi Valkeinen 	case OMAPDSS_VER_AM43xx:
4851f76ee892STomi Valkeinen 		DSSWARN("DSI not supported\n");
4852f76ee892STomi Valkeinen 		return OMAP_DSS_CHANNEL_LCD;
4853f76ee892STomi Valkeinen 
4854f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP34xx_ES1:
4855f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP34xx_ES3:
4856f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP3630:
4857f76ee892STomi Valkeinen 	case OMAPDSS_VER_AM35xx:
4858f76ee892STomi Valkeinen 		return OMAP_DSS_CHANNEL_LCD;
4859f76ee892STomi Valkeinen 
4860f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP4430_ES1:
4861f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP4430_ES2:
4862f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP4:
4863f76ee892STomi Valkeinen 		switch (module_id) {
4864f76ee892STomi Valkeinen 		case 0:
4865f76ee892STomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
4866f76ee892STomi Valkeinen 		case 1:
4867f76ee892STomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD2;
4868f76ee892STomi Valkeinen 		default:
4869f76ee892STomi Valkeinen 			DSSWARN("unsupported module id\n");
4870f76ee892STomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
4871f76ee892STomi Valkeinen 		}
4872f76ee892STomi Valkeinen 
4873f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP5:
4874f76ee892STomi Valkeinen 		switch (module_id) {
4875f76ee892STomi Valkeinen 		case 0:
4876f76ee892STomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
4877f76ee892STomi Valkeinen 		case 1:
4878f76ee892STomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD3;
4879f76ee892STomi Valkeinen 		default:
4880f76ee892STomi Valkeinen 			DSSWARN("unsupported module id\n");
4881f76ee892STomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
4882f76ee892STomi Valkeinen 		}
4883f76ee892STomi Valkeinen 
4884f76ee892STomi Valkeinen 	default:
4885f76ee892STomi Valkeinen 		DSSWARN("unsupported DSS version\n");
4886f76ee892STomi Valkeinen 		return OMAP_DSS_CHANNEL_LCD;
4887f76ee892STomi Valkeinen 	}
4888f76ee892STomi Valkeinen }
4889f76ee892STomi Valkeinen 
dsi_request_vc(struct omap_dss_device * dssdev,int * channel)4890f76ee892STomi Valkeinen static int dsi_request_vc(struct omap_dss_device *dssdev, int *channel)
4891f76ee892STomi Valkeinen {
4892f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4893f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4894f76ee892STomi Valkeinen 	int i;
4895f76ee892STomi Valkeinen 
4896f76ee892STomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
4897f76ee892STomi Valkeinen 		if (!dsi->vc[i].dssdev) {
4898f76ee892STomi Valkeinen 			dsi->vc[i].dssdev = dssdev;
4899f76ee892STomi Valkeinen 			*channel = i;
4900f76ee892STomi Valkeinen 			return 0;
4901f76ee892STomi Valkeinen 		}
4902f76ee892STomi Valkeinen 	}
4903f76ee892STomi Valkeinen 
4904f76ee892STomi Valkeinen 	DSSERR("cannot get VC for display %s", dssdev->name);
4905f76ee892STomi Valkeinen 	return -ENOSPC;
4906f76ee892STomi Valkeinen }
4907f76ee892STomi Valkeinen 
dsi_set_vc_id(struct omap_dss_device * dssdev,int channel,int vc_id)4908f76ee892STomi Valkeinen static int dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id)
4909f76ee892STomi Valkeinen {
4910f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4911f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4912f76ee892STomi Valkeinen 
4913f76ee892STomi Valkeinen 	if (vc_id < 0 || vc_id > 3) {
4914f76ee892STomi Valkeinen 		DSSERR("VC ID out of range\n");
4915f76ee892STomi Valkeinen 		return -EINVAL;
4916f76ee892STomi Valkeinen 	}
4917f76ee892STomi Valkeinen 
4918f76ee892STomi Valkeinen 	if (channel < 0 || channel > 3) {
4919f76ee892STomi Valkeinen 		DSSERR("Virtual Channel out of range\n");
4920f76ee892STomi Valkeinen 		return -EINVAL;
4921f76ee892STomi Valkeinen 	}
4922f76ee892STomi Valkeinen 
4923f76ee892STomi Valkeinen 	if (dsi->vc[channel].dssdev != dssdev) {
4924f76ee892STomi Valkeinen 		DSSERR("Virtual Channel not allocated to display %s\n",
4925f76ee892STomi Valkeinen 			dssdev->name);
4926f76ee892STomi Valkeinen 		return -EINVAL;
4927f76ee892STomi Valkeinen 	}
4928f76ee892STomi Valkeinen 
4929f76ee892STomi Valkeinen 	dsi->vc[channel].vc_id = vc_id;
4930f76ee892STomi Valkeinen 
4931f76ee892STomi Valkeinen 	return 0;
4932f76ee892STomi Valkeinen }
4933f76ee892STomi Valkeinen 
dsi_release_vc(struct omap_dss_device * dssdev,int channel)4934f76ee892STomi Valkeinen static void dsi_release_vc(struct omap_dss_device *dssdev, int channel)
4935f76ee892STomi Valkeinen {
4936f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4937f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4938f76ee892STomi Valkeinen 
4939f76ee892STomi Valkeinen 	if ((channel >= 0 && channel <= 3) &&
4940f76ee892STomi Valkeinen 		dsi->vc[channel].dssdev == dssdev) {
4941f76ee892STomi Valkeinen 		dsi->vc[channel].dssdev = NULL;
4942f76ee892STomi Valkeinen 		dsi->vc[channel].vc_id = 0;
4943f76ee892STomi Valkeinen 	}
4944f76ee892STomi Valkeinen }
4945f76ee892STomi Valkeinen 
4946f76ee892STomi Valkeinen 
dsi_get_clocks(struct platform_device * dsidev)4947f76ee892STomi Valkeinen static int dsi_get_clocks(struct platform_device *dsidev)
4948f76ee892STomi Valkeinen {
4949f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4950f76ee892STomi Valkeinen 	struct clk *clk;
4951f76ee892STomi Valkeinen 
4952f76ee892STomi Valkeinen 	clk = devm_clk_get(&dsidev->dev, "fck");
4953f76ee892STomi Valkeinen 	if (IS_ERR(clk)) {
4954f76ee892STomi Valkeinen 		DSSERR("can't get fck\n");
4955f76ee892STomi Valkeinen 		return PTR_ERR(clk);
4956f76ee892STomi Valkeinen 	}
4957f76ee892STomi Valkeinen 
4958f76ee892STomi Valkeinen 	dsi->dss_clk = clk;
4959f76ee892STomi Valkeinen 
4960f76ee892STomi Valkeinen 	return 0;
4961f76ee892STomi Valkeinen }
4962f76ee892STomi Valkeinen 
dsi_connect(struct omap_dss_device * dssdev,struct omap_dss_device * dst)4963f76ee892STomi Valkeinen static int dsi_connect(struct omap_dss_device *dssdev,
4964f76ee892STomi Valkeinen 		struct omap_dss_device *dst)
4965f76ee892STomi Valkeinen {
4966f76ee892STomi Valkeinen 	struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4967f76ee892STomi Valkeinen 	struct omap_overlay_manager *mgr;
4968f76ee892STomi Valkeinen 	int r;
4969f76ee892STomi Valkeinen 
4970f76ee892STomi Valkeinen 	r = dsi_regulator_init(dsidev);
4971f76ee892STomi Valkeinen 	if (r)
4972f76ee892STomi Valkeinen 		return r;
4973f76ee892STomi Valkeinen 
4974f76ee892STomi Valkeinen 	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
4975f76ee892STomi Valkeinen 	if (!mgr)
4976f76ee892STomi Valkeinen 		return -ENODEV;
4977f76ee892STomi Valkeinen 
4978f76ee892STomi Valkeinen 	r = dss_mgr_connect(mgr, dssdev);
4979f76ee892STomi Valkeinen 	if (r)
4980f76ee892STomi Valkeinen 		return r;
4981f76ee892STomi Valkeinen 
4982f76ee892STomi Valkeinen 	r = omapdss_output_set_device(dssdev, dst);
4983f76ee892STomi Valkeinen 	if (r) {
4984f76ee892STomi Valkeinen 		DSSERR("failed to connect output to new device: %s\n",
4985f76ee892STomi Valkeinen 				dssdev->name);
4986f76ee892STomi Valkeinen 		dss_mgr_disconnect(mgr, dssdev);
4987f76ee892STomi Valkeinen 		return r;
4988f76ee892STomi Valkeinen 	}
4989f76ee892STomi Valkeinen 
4990f76ee892STomi Valkeinen 	return 0;
4991f76ee892STomi Valkeinen }
4992f76ee892STomi Valkeinen 
dsi_disconnect(struct omap_dss_device * dssdev,struct omap_dss_device * dst)4993f76ee892STomi Valkeinen static void dsi_disconnect(struct omap_dss_device *dssdev,
4994f76ee892STomi Valkeinen 		struct omap_dss_device *dst)
4995f76ee892STomi Valkeinen {
4996f76ee892STomi Valkeinen 	WARN_ON(dst != dssdev->dst);
4997f76ee892STomi Valkeinen 
4998f76ee892STomi Valkeinen 	if (dst != dssdev->dst)
4999f76ee892STomi Valkeinen 		return;
5000f76ee892STomi Valkeinen 
5001f76ee892STomi Valkeinen 	omapdss_output_unset_device(dssdev);
5002f76ee892STomi Valkeinen 
5003f76ee892STomi Valkeinen 	if (dssdev->manager)
5004f76ee892STomi Valkeinen 		dss_mgr_disconnect(dssdev->manager, dssdev);
5005f76ee892STomi Valkeinen }
5006f76ee892STomi Valkeinen 
5007f76ee892STomi Valkeinen static const struct omapdss_dsi_ops dsi_ops = {
5008f76ee892STomi Valkeinen 	.connect = dsi_connect,
5009f76ee892STomi Valkeinen 	.disconnect = dsi_disconnect,
5010f76ee892STomi Valkeinen 
5011f76ee892STomi Valkeinen 	.bus_lock = dsi_bus_lock,
5012f76ee892STomi Valkeinen 	.bus_unlock = dsi_bus_unlock,
5013f76ee892STomi Valkeinen 
5014f76ee892STomi Valkeinen 	.enable = dsi_display_enable,
5015f76ee892STomi Valkeinen 	.disable = dsi_display_disable,
5016f76ee892STomi Valkeinen 
5017f76ee892STomi Valkeinen 	.enable_hs = dsi_vc_enable_hs,
5018f76ee892STomi Valkeinen 
5019f76ee892STomi Valkeinen 	.configure_pins = dsi_configure_pins,
5020f76ee892STomi Valkeinen 	.set_config = dsi_set_config,
5021f76ee892STomi Valkeinen 
5022f76ee892STomi Valkeinen 	.enable_video_output = dsi_enable_video_output,
5023f76ee892STomi Valkeinen 	.disable_video_output = dsi_disable_video_output,
5024f76ee892STomi Valkeinen 
5025f76ee892STomi Valkeinen 	.update = dsi_update,
5026f76ee892STomi Valkeinen 
5027f76ee892STomi Valkeinen 	.enable_te = dsi_enable_te,
5028f76ee892STomi Valkeinen 
5029f76ee892STomi Valkeinen 	.request_vc = dsi_request_vc,
5030f76ee892STomi Valkeinen 	.set_vc_id = dsi_set_vc_id,
5031f76ee892STomi Valkeinen 	.release_vc = dsi_release_vc,
5032f76ee892STomi Valkeinen 
5033f76ee892STomi Valkeinen 	.dcs_write = dsi_vc_dcs_write,
5034f76ee892STomi Valkeinen 	.dcs_write_nosync = dsi_vc_dcs_write_nosync,
5035f76ee892STomi Valkeinen 	.dcs_read = dsi_vc_dcs_read,
5036f76ee892STomi Valkeinen 
5037f76ee892STomi Valkeinen 	.gen_write = dsi_vc_generic_write,
5038f76ee892STomi Valkeinen 	.gen_write_nosync = dsi_vc_generic_write_nosync,
5039f76ee892STomi Valkeinen 	.gen_read = dsi_vc_generic_read,
5040f76ee892STomi Valkeinen 
5041f76ee892STomi Valkeinen 	.bta_sync = dsi_vc_send_bta_sync,
5042f76ee892STomi Valkeinen 
5043f76ee892STomi Valkeinen 	.set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size,
5044f76ee892STomi Valkeinen };
5045f76ee892STomi Valkeinen 
dsi_init_output(struct platform_device * dsidev)5046f76ee892STomi Valkeinen static void dsi_init_output(struct platform_device *dsidev)
5047f76ee892STomi Valkeinen {
5048f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5049f76ee892STomi Valkeinen 	struct omap_dss_device *out = &dsi->output;
5050f76ee892STomi Valkeinen 
5051f76ee892STomi Valkeinen 	out->dev = &dsidev->dev;
5052f76ee892STomi Valkeinen 	out->id = dsi->module_id == 0 ?
5053f76ee892STomi Valkeinen 			OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
5054f76ee892STomi Valkeinen 
5055f76ee892STomi Valkeinen 	out->output_type = OMAP_DISPLAY_TYPE_DSI;
5056f76ee892STomi Valkeinen 	out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
5057f76ee892STomi Valkeinen 	out->dispc_channel = dsi_get_channel(dsi->module_id);
5058f76ee892STomi Valkeinen 	out->ops.dsi = &dsi_ops;
5059f76ee892STomi Valkeinen 	out->owner = THIS_MODULE;
5060f76ee892STomi Valkeinen 
5061f76ee892STomi Valkeinen 	omapdss_register_output(out);
5062f76ee892STomi Valkeinen }
5063f76ee892STomi Valkeinen 
dsi_uninit_output(struct platform_device * dsidev)5064f76ee892STomi Valkeinen static void dsi_uninit_output(struct platform_device *dsidev)
5065f76ee892STomi Valkeinen {
5066f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5067f76ee892STomi Valkeinen 	struct omap_dss_device *out = &dsi->output;
5068f76ee892STomi Valkeinen 
5069f76ee892STomi Valkeinen 	omapdss_unregister_output(out);
5070f76ee892STomi Valkeinen }
5071f76ee892STomi Valkeinen 
dsi_probe_of(struct platform_device * pdev)5072f76ee892STomi Valkeinen static int dsi_probe_of(struct platform_device *pdev)
5073f76ee892STomi Valkeinen {
5074f76ee892STomi Valkeinen 	struct device_node *node = pdev->dev.of_node;
5075f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5076f76ee892STomi Valkeinen 	struct property *prop;
5077f76ee892STomi Valkeinen 	u32 lane_arr[10];
5078f76ee892STomi Valkeinen 	int len, num_pins;
5079f76ee892STomi Valkeinen 	int r, i;
5080f76ee892STomi Valkeinen 	struct device_node *ep;
5081f76ee892STomi Valkeinen 	struct omap_dsi_pin_config pin_cfg;
5082f76ee892STomi Valkeinen 
5083*ada5caa4SKuninori Morimoto 	ep = of_graph_get_endpoint_by_regs(node, 0, -1);
5084f76ee892STomi Valkeinen 	if (!ep)
5085f76ee892STomi Valkeinen 		return 0;
5086f76ee892STomi Valkeinen 
5087f76ee892STomi Valkeinen 	prop = of_find_property(ep, "lanes", &len);
5088f76ee892STomi Valkeinen 	if (prop == NULL) {
5089f76ee892STomi Valkeinen 		dev_err(&pdev->dev, "failed to find lane data\n");
5090f76ee892STomi Valkeinen 		r = -EINVAL;
5091f76ee892STomi Valkeinen 		goto err;
5092f76ee892STomi Valkeinen 	}
5093f76ee892STomi Valkeinen 
5094f76ee892STomi Valkeinen 	num_pins = len / sizeof(u32);
5095f76ee892STomi Valkeinen 
5096f76ee892STomi Valkeinen 	if (num_pins < 4 || num_pins % 2 != 0 ||
5097f76ee892STomi Valkeinen 		num_pins > dsi->num_lanes_supported * 2) {
5098f76ee892STomi Valkeinen 		dev_err(&pdev->dev, "bad number of lanes\n");
5099f76ee892STomi Valkeinen 		r = -EINVAL;
5100f76ee892STomi Valkeinen 		goto err;
5101f76ee892STomi Valkeinen 	}
5102f76ee892STomi Valkeinen 
5103f76ee892STomi Valkeinen 	r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins);
5104f76ee892STomi Valkeinen 	if (r) {
5105f76ee892STomi Valkeinen 		dev_err(&pdev->dev, "failed to read lane data\n");
5106f76ee892STomi Valkeinen 		goto err;
5107f76ee892STomi Valkeinen 	}
5108f76ee892STomi Valkeinen 
5109f76ee892STomi Valkeinen 	pin_cfg.num_pins = num_pins;
5110f76ee892STomi Valkeinen 	for (i = 0; i < num_pins; ++i)
5111f76ee892STomi Valkeinen 		pin_cfg.pins[i] = (int)lane_arr[i];
5112f76ee892STomi Valkeinen 
5113f76ee892STomi Valkeinen 	r = dsi_configure_pins(&dsi->output, &pin_cfg);
5114f76ee892STomi Valkeinen 	if (r) {
5115f76ee892STomi Valkeinen 		dev_err(&pdev->dev, "failed to configure pins");
5116f76ee892STomi Valkeinen 		goto err;
5117f76ee892STomi Valkeinen 	}
5118f76ee892STomi Valkeinen 
5119f76ee892STomi Valkeinen 	of_node_put(ep);
5120f76ee892STomi Valkeinen 
5121f76ee892STomi Valkeinen 	return 0;
5122f76ee892STomi Valkeinen 
5123f76ee892STomi Valkeinen err:
5124f76ee892STomi Valkeinen 	of_node_put(ep);
5125f76ee892STomi Valkeinen 	return r;
5126f76ee892STomi Valkeinen }
5127f76ee892STomi Valkeinen 
5128f76ee892STomi Valkeinen static const struct dss_pll_ops dsi_pll_ops = {
5129f76ee892STomi Valkeinen 	.enable = dsi_pll_enable,
5130f76ee892STomi Valkeinen 	.disable = dsi_pll_disable,
5131f76ee892STomi Valkeinen 	.set_config = dss_pll_write_config_type_a,
5132f76ee892STomi Valkeinen };
5133f76ee892STomi Valkeinen 
5134f76ee892STomi Valkeinen static const struct dss_pll_hw dss_omap3_dsi_pll_hw = {
5135f76ee892STomi Valkeinen 	.n_max = (1 << 7) - 1,
5136f76ee892STomi Valkeinen 	.m_max = (1 << 11) - 1,
5137f76ee892STomi Valkeinen 	.mX_max = (1 << 4) - 1,
5138f76ee892STomi Valkeinen 	.fint_min = 750000,
5139f76ee892STomi Valkeinen 	.fint_max = 2100000,
5140f76ee892STomi Valkeinen 	.clkdco_low = 1000000000,
5141f76ee892STomi Valkeinen 	.clkdco_max = 1800000000,
5142f76ee892STomi Valkeinen 
5143f76ee892STomi Valkeinen 	.n_msb = 7,
5144f76ee892STomi Valkeinen 	.n_lsb = 1,
5145f76ee892STomi Valkeinen 	.m_msb = 18,
5146f76ee892STomi Valkeinen 	.m_lsb = 8,
5147f76ee892STomi Valkeinen 
5148f76ee892STomi Valkeinen 	.mX_msb[0] = 22,
5149f76ee892STomi Valkeinen 	.mX_lsb[0] = 19,
5150f76ee892STomi Valkeinen 	.mX_msb[1] = 26,
5151f76ee892STomi Valkeinen 	.mX_lsb[1] = 23,
5152f76ee892STomi Valkeinen 
5153f76ee892STomi Valkeinen 	.has_stopmode = true,
5154f76ee892STomi Valkeinen 	.has_freqsel = true,
5155f76ee892STomi Valkeinen 	.has_selfreqdco = false,
5156f76ee892STomi Valkeinen 	.has_refsel = false,
5157f76ee892STomi Valkeinen };
5158f76ee892STomi Valkeinen 
5159f76ee892STomi Valkeinen static const struct dss_pll_hw dss_omap4_dsi_pll_hw = {
5160f76ee892STomi Valkeinen 	.n_max = (1 << 8) - 1,
5161f76ee892STomi Valkeinen 	.m_max = (1 << 12) - 1,
5162f76ee892STomi Valkeinen 	.mX_max = (1 << 5) - 1,
5163f76ee892STomi Valkeinen 	.fint_min = 500000,
5164f76ee892STomi Valkeinen 	.fint_max = 2500000,
5165f76ee892STomi Valkeinen 	.clkdco_low = 1000000000,
5166f76ee892STomi Valkeinen 	.clkdco_max = 1800000000,
5167f76ee892STomi Valkeinen 
5168f76ee892STomi Valkeinen 	.n_msb = 8,
5169f76ee892STomi Valkeinen 	.n_lsb = 1,
5170f76ee892STomi Valkeinen 	.m_msb = 20,
5171f76ee892STomi Valkeinen 	.m_lsb = 9,
5172f76ee892STomi Valkeinen 
5173f76ee892STomi Valkeinen 	.mX_msb[0] = 25,
5174f76ee892STomi Valkeinen 	.mX_lsb[0] = 21,
5175f76ee892STomi Valkeinen 	.mX_msb[1] = 30,
5176f76ee892STomi Valkeinen 	.mX_lsb[1] = 26,
5177f76ee892STomi Valkeinen 
5178f76ee892STomi Valkeinen 	.has_stopmode = true,
5179f76ee892STomi Valkeinen 	.has_freqsel = false,
5180f76ee892STomi Valkeinen 	.has_selfreqdco = false,
5181f76ee892STomi Valkeinen 	.has_refsel = false,
5182f76ee892STomi Valkeinen };
5183f76ee892STomi Valkeinen 
5184f76ee892STomi Valkeinen static const struct dss_pll_hw dss_omap5_dsi_pll_hw = {
5185f76ee892STomi Valkeinen 	.n_max = (1 << 8) - 1,
5186f76ee892STomi Valkeinen 	.m_max = (1 << 12) - 1,
5187f76ee892STomi Valkeinen 	.mX_max = (1 << 5) - 1,
5188f76ee892STomi Valkeinen 	.fint_min = 150000,
5189f76ee892STomi Valkeinen 	.fint_max = 52000000,
5190f76ee892STomi Valkeinen 	.clkdco_low = 1000000000,
5191f76ee892STomi Valkeinen 	.clkdco_max = 1800000000,
5192f76ee892STomi Valkeinen 
5193f76ee892STomi Valkeinen 	.n_msb = 8,
5194f76ee892STomi Valkeinen 	.n_lsb = 1,
5195f76ee892STomi Valkeinen 	.m_msb = 20,
5196f76ee892STomi Valkeinen 	.m_lsb = 9,
5197f76ee892STomi Valkeinen 
5198f76ee892STomi Valkeinen 	.mX_msb[0] = 25,
5199f76ee892STomi Valkeinen 	.mX_lsb[0] = 21,
5200f76ee892STomi Valkeinen 	.mX_msb[1] = 30,
5201f76ee892STomi Valkeinen 	.mX_lsb[1] = 26,
5202f76ee892STomi Valkeinen 
5203f76ee892STomi Valkeinen 	.has_stopmode = true,
5204f76ee892STomi Valkeinen 	.has_freqsel = false,
5205f76ee892STomi Valkeinen 	.has_selfreqdco = true,
5206f76ee892STomi Valkeinen 	.has_refsel = true,
5207f76ee892STomi Valkeinen };
5208f76ee892STomi Valkeinen 
dsi_init_pll_data(struct platform_device * dsidev)5209f76ee892STomi Valkeinen static int dsi_init_pll_data(struct platform_device *dsidev)
5210f76ee892STomi Valkeinen {
5211f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5212f76ee892STomi Valkeinen 	struct dss_pll *pll = &dsi->pll;
5213f76ee892STomi Valkeinen 	struct clk *clk;
5214f76ee892STomi Valkeinen 	int r;
5215f76ee892STomi Valkeinen 
5216f76ee892STomi Valkeinen 	clk = devm_clk_get(&dsidev->dev, "sys_clk");
5217f76ee892STomi Valkeinen 	if (IS_ERR(clk)) {
5218f76ee892STomi Valkeinen 		DSSERR("can't get sys_clk\n");
5219f76ee892STomi Valkeinen 		return PTR_ERR(clk);
5220f76ee892STomi Valkeinen 	}
5221f76ee892STomi Valkeinen 
5222f76ee892STomi Valkeinen 	pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1";
5223f76ee892STomi Valkeinen 	pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2;
5224f76ee892STomi Valkeinen 	pll->clkin = clk;
5225f76ee892STomi Valkeinen 	pll->base = dsi->pll_base;
5226f76ee892STomi Valkeinen 
5227f76ee892STomi Valkeinen 	switch (omapdss_get_version()) {
5228f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP34xx_ES1:
5229f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP34xx_ES3:
5230f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP3630:
5231f76ee892STomi Valkeinen 	case OMAPDSS_VER_AM35xx:
5232f76ee892STomi Valkeinen 		pll->hw = &dss_omap3_dsi_pll_hw;
5233f76ee892STomi Valkeinen 		break;
5234f76ee892STomi Valkeinen 
5235f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP4430_ES1:
5236f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP4430_ES2:
5237f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP4:
5238f76ee892STomi Valkeinen 		pll->hw = &dss_omap4_dsi_pll_hw;
5239f76ee892STomi Valkeinen 		break;
5240f76ee892STomi Valkeinen 
5241f76ee892STomi Valkeinen 	case OMAPDSS_VER_OMAP5:
5242f76ee892STomi Valkeinen 		pll->hw = &dss_omap5_dsi_pll_hw;
5243f76ee892STomi Valkeinen 		break;
5244f76ee892STomi Valkeinen 
5245f76ee892STomi Valkeinen 	default:
5246f76ee892STomi Valkeinen 		return -ENODEV;
5247f76ee892STomi Valkeinen 	}
5248f76ee892STomi Valkeinen 
5249f76ee892STomi Valkeinen 	pll->ops = &dsi_pll_ops;
5250f76ee892STomi Valkeinen 
5251f76ee892STomi Valkeinen 	r = dss_pll_register(pll);
5252f76ee892STomi Valkeinen 	if (r)
5253f76ee892STomi Valkeinen 		return r;
5254f76ee892STomi Valkeinen 
5255f76ee892STomi Valkeinen 	return 0;
5256f76ee892STomi Valkeinen }
5257f76ee892STomi Valkeinen 
5258f76ee892STomi Valkeinen /* DSI1 HW IP initialisation */
dsi_bind(struct device * dev,struct device * master,void * data)5259f76ee892STomi Valkeinen static int dsi_bind(struct device *dev, struct device *master, void *data)
5260f76ee892STomi Valkeinen {
5261f76ee892STomi Valkeinen 	struct platform_device *dsidev = to_platform_device(dev);
5262f76ee892STomi Valkeinen 	u32 rev;
5263f76ee892STomi Valkeinen 	int r, i;
5264f76ee892STomi Valkeinen 	struct dsi_data *dsi;
5265f76ee892STomi Valkeinen 	struct resource *dsi_mem;
5266f76ee892STomi Valkeinen 	struct resource *res;
5267f76ee892STomi Valkeinen 	struct resource temp_res;
5268f76ee892STomi Valkeinen 
5269f76ee892STomi Valkeinen 	dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL);
5270f76ee892STomi Valkeinen 	if (!dsi)
5271f76ee892STomi Valkeinen 		return -ENOMEM;
5272f76ee892STomi Valkeinen 
5273f76ee892STomi Valkeinen 	dsi->pdev = dsidev;
52740b5e0f45SJulia Lawall 	platform_set_drvdata(dsidev, dsi);
5275f76ee892STomi Valkeinen 
5276f76ee892STomi Valkeinen 	spin_lock_init(&dsi->irq_lock);
5277f76ee892STomi Valkeinen 	spin_lock_init(&dsi->errors_lock);
5278f76ee892STomi Valkeinen 	dsi->errors = 0;
5279f76ee892STomi Valkeinen 
528035b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
5281f76ee892STomi Valkeinen 	spin_lock_init(&dsi->irq_stats_lock);
5282f76ee892STomi Valkeinen 	dsi->irq_stats.last_reset = jiffies;
5283f76ee892STomi Valkeinen #endif
5284f76ee892STomi Valkeinen 
5285f76ee892STomi Valkeinen 	mutex_init(&dsi->lock);
5286f76ee892STomi Valkeinen 	sema_init(&dsi->bus_lock, 1);
5287f76ee892STomi Valkeinen 
5288f76ee892STomi Valkeinen 	INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work,
5289f76ee892STomi Valkeinen 			     dsi_framedone_timeout_work_callback);
5290f76ee892STomi Valkeinen 
5291f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
52926c789357SKees Cook 	timer_setup(&dsi->te_timer, dsi_te_timeout, 0);
5293f76ee892STomi Valkeinen #endif
5294f76ee892STomi Valkeinen 
5295f76ee892STomi Valkeinen 	res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto");
5296f76ee892STomi Valkeinen 	if (!res) {
5297f76ee892STomi Valkeinen 		res = platform_get_resource(dsidev, IORESOURCE_MEM, 0);
5298f76ee892STomi Valkeinen 		if (!res) {
5299f76ee892STomi Valkeinen 			DSSERR("can't get IORESOURCE_MEM DSI\n");
5300f76ee892STomi Valkeinen 			return -EINVAL;
5301f76ee892STomi Valkeinen 		}
5302f76ee892STomi Valkeinen 
5303f76ee892STomi Valkeinen 		temp_res.start = res->start;
5304f76ee892STomi Valkeinen 		temp_res.end = temp_res.start + DSI_PROTO_SZ - 1;
5305f76ee892STomi Valkeinen 		res = &temp_res;
5306f76ee892STomi Valkeinen 	}
5307f76ee892STomi Valkeinen 
5308f76ee892STomi Valkeinen 	dsi_mem = res;
5309f76ee892STomi Valkeinen 
5310f76ee892STomi Valkeinen 	dsi->proto_base = devm_ioremap(&dsidev->dev, res->start,
5311f76ee892STomi Valkeinen 		resource_size(res));
5312f76ee892STomi Valkeinen 	if (!dsi->proto_base) {
5313f76ee892STomi Valkeinen 		DSSERR("can't ioremap DSI protocol engine\n");
5314f76ee892STomi Valkeinen 		return -ENOMEM;
5315f76ee892STomi Valkeinen 	}
5316f76ee892STomi Valkeinen 
5317f76ee892STomi Valkeinen 	res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "phy");
5318f76ee892STomi Valkeinen 	if (!res) {
5319f76ee892STomi Valkeinen 		res = platform_get_resource(dsidev, IORESOURCE_MEM, 0);
5320f76ee892STomi Valkeinen 		if (!res) {
5321f76ee892STomi Valkeinen 			DSSERR("can't get IORESOURCE_MEM DSI\n");
5322f76ee892STomi Valkeinen 			return -EINVAL;
5323f76ee892STomi Valkeinen 		}
5324f76ee892STomi Valkeinen 
5325f76ee892STomi Valkeinen 		temp_res.start = res->start + DSI_PHY_OFFSET;
5326f76ee892STomi Valkeinen 		temp_res.end = temp_res.start + DSI_PHY_SZ - 1;
5327f76ee892STomi Valkeinen 		res = &temp_res;
5328f76ee892STomi Valkeinen 	}
5329f76ee892STomi Valkeinen 
5330f76ee892STomi Valkeinen 	dsi->phy_base = devm_ioremap(&dsidev->dev, res->start,
5331f76ee892STomi Valkeinen 		resource_size(res));
533243da7575SWei Yongjun 	if (!dsi->phy_base) {
5333f76ee892STomi Valkeinen 		DSSERR("can't ioremap DSI PHY\n");
5334f76ee892STomi Valkeinen 		return -ENOMEM;
5335f76ee892STomi Valkeinen 	}
5336f76ee892STomi Valkeinen 
5337f76ee892STomi Valkeinen 	res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "pll");
5338f76ee892STomi Valkeinen 	if (!res) {
5339f76ee892STomi Valkeinen 		res = platform_get_resource(dsidev, IORESOURCE_MEM, 0);
5340f76ee892STomi Valkeinen 		if (!res) {
5341f76ee892STomi Valkeinen 			DSSERR("can't get IORESOURCE_MEM DSI\n");
5342f76ee892STomi Valkeinen 			return -EINVAL;
5343f76ee892STomi Valkeinen 		}
5344f76ee892STomi Valkeinen 
5345f76ee892STomi Valkeinen 		temp_res.start = res->start + DSI_PLL_OFFSET;
5346f76ee892STomi Valkeinen 		temp_res.end = temp_res.start + DSI_PLL_SZ - 1;
5347f76ee892STomi Valkeinen 		res = &temp_res;
5348f76ee892STomi Valkeinen 	}
5349f76ee892STomi Valkeinen 
5350f76ee892STomi Valkeinen 	dsi->pll_base = devm_ioremap(&dsidev->dev, res->start,
5351f76ee892STomi Valkeinen 		resource_size(res));
535243da7575SWei Yongjun 	if (!dsi->pll_base) {
5353f76ee892STomi Valkeinen 		DSSERR("can't ioremap DSI PLL\n");
5354f76ee892STomi Valkeinen 		return -ENOMEM;
5355f76ee892STomi Valkeinen 	}
5356f76ee892STomi Valkeinen 
5357f76ee892STomi Valkeinen 	dsi->irq = platform_get_irq(dsi->pdev, 0);
5358f76ee892STomi Valkeinen 	if (dsi->irq < 0) {
5359f76ee892STomi Valkeinen 		DSSERR("platform_get_irq failed\n");
5360f76ee892STomi Valkeinen 		return -ENODEV;
5361f76ee892STomi Valkeinen 	}
5362f76ee892STomi Valkeinen 
5363f76ee892STomi Valkeinen 	r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
5364f76ee892STomi Valkeinen 			     IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
5365f76ee892STomi Valkeinen 	if (r < 0) {
5366f76ee892STomi Valkeinen 		DSSERR("request_irq failed\n");
5367f76ee892STomi Valkeinen 		return r;
5368f76ee892STomi Valkeinen 	}
5369f76ee892STomi Valkeinen 
5370f76ee892STomi Valkeinen 	if (dsidev->dev.of_node) {
5371f76ee892STomi Valkeinen 		const struct of_device_id *match;
5372f76ee892STomi Valkeinen 		const struct dsi_module_id_data *d;
5373f76ee892STomi Valkeinen 
5374f76ee892STomi Valkeinen 		match = of_match_node(dsi_of_match, dsidev->dev.of_node);
5375f76ee892STomi Valkeinen 		if (!match) {
5376f76ee892STomi Valkeinen 			DSSERR("unsupported DSI module\n");
5377f76ee892STomi Valkeinen 			return -ENODEV;
5378f76ee892STomi Valkeinen 		}
5379f76ee892STomi Valkeinen 
5380f76ee892STomi Valkeinen 		d = match->data;
5381f76ee892STomi Valkeinen 
5382f76ee892STomi Valkeinen 		while (d->address != 0 && d->address != dsi_mem->start)
5383f76ee892STomi Valkeinen 			d++;
5384f76ee892STomi Valkeinen 
5385f76ee892STomi Valkeinen 		if (d->address == 0) {
5386f76ee892STomi Valkeinen 			DSSERR("unsupported DSI module\n");
5387f76ee892STomi Valkeinen 			return -ENODEV;
5388f76ee892STomi Valkeinen 		}
5389f76ee892STomi Valkeinen 
5390f76ee892STomi Valkeinen 		dsi->module_id = d->id;
5391f76ee892STomi Valkeinen 	} else {
5392f76ee892STomi Valkeinen 		dsi->module_id = dsidev->id;
5393f76ee892STomi Valkeinen 	}
5394f76ee892STomi Valkeinen 
5395f76ee892STomi Valkeinen 	/* DSI VCs initialization */
5396f76ee892STomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
5397f76ee892STomi Valkeinen 		dsi->vc[i].source = DSI_VC_SOURCE_L4;
5398f76ee892STomi Valkeinen 		dsi->vc[i].dssdev = NULL;
5399f76ee892STomi Valkeinen 		dsi->vc[i].vc_id = 0;
5400f76ee892STomi Valkeinen 	}
5401f76ee892STomi Valkeinen 
5402f76ee892STomi Valkeinen 	r = dsi_get_clocks(dsidev);
5403f76ee892STomi Valkeinen 	if (r)
5404f76ee892STomi Valkeinen 		return r;
5405f76ee892STomi Valkeinen 
5406f76ee892STomi Valkeinen 	dsi_init_pll_data(dsidev);
5407f76ee892STomi Valkeinen 
5408f76ee892STomi Valkeinen 	pm_runtime_enable(&dsidev->dev);
5409f76ee892STomi Valkeinen 
5410f76ee892STomi Valkeinen 	r = dsi_runtime_get(dsidev);
5411f76ee892STomi Valkeinen 	if (r)
5412f76ee892STomi Valkeinen 		goto err_runtime_get;
5413f76ee892STomi Valkeinen 
5414f76ee892STomi Valkeinen 	rev = dsi_read_reg(dsidev, DSI_REVISION);
5415f76ee892STomi Valkeinen 	dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
5416f76ee892STomi Valkeinen 	       FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
5417f76ee892STomi Valkeinen 
5418f76ee892STomi Valkeinen 	/* DSI on OMAP3 doesn't have register DSI_GNQ, set number
5419f76ee892STomi Valkeinen 	 * of data to 3 by default */
5420f76ee892STomi Valkeinen 	if (dss_has_feature(FEAT_DSI_GNQ))
5421f76ee892STomi Valkeinen 		/* NB_DATA_LANES */
5422f76ee892STomi Valkeinen 		dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9);
5423f76ee892STomi Valkeinen 	else
5424f76ee892STomi Valkeinen 		dsi->num_lanes_supported = 3;
5425f76ee892STomi Valkeinen 
5426f76ee892STomi Valkeinen 	dsi->line_buffer_size = dsi_get_line_buf_size(dsidev);
5427f76ee892STomi Valkeinen 
5428f76ee892STomi Valkeinen 	dsi_init_output(dsidev);
5429f76ee892STomi Valkeinen 
5430f76ee892STomi Valkeinen 	if (dsidev->dev.of_node) {
5431f76ee892STomi Valkeinen 		r = dsi_probe_of(dsidev);
5432f76ee892STomi Valkeinen 		if (r) {
5433f76ee892STomi Valkeinen 			DSSERR("Invalid DSI DT data\n");
5434f76ee892STomi Valkeinen 			goto err_probe_of;
5435f76ee892STomi Valkeinen 		}
5436f76ee892STomi Valkeinen 
5437f76ee892STomi Valkeinen 		r = of_platform_populate(dsidev->dev.of_node, NULL, NULL,
5438f76ee892STomi Valkeinen 			&dsidev->dev);
5439f76ee892STomi Valkeinen 		if (r)
5440f76ee892STomi Valkeinen 			DSSERR("Failed to populate DSI child devices: %d\n", r);
5441f76ee892STomi Valkeinen 	}
5442f76ee892STomi Valkeinen 
5443f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
5444f76ee892STomi Valkeinen 
5445f76ee892STomi Valkeinen 	if (dsi->module_id == 0)
5446f76ee892STomi Valkeinen 		dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs);
5447f76ee892STomi Valkeinen 	else if (dsi->module_id == 1)
5448f76ee892STomi Valkeinen 		dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs);
5449f76ee892STomi Valkeinen 
545035b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
5451f76ee892STomi Valkeinen 	if (dsi->module_id == 0)
5452f76ee892STomi Valkeinen 		dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs);
5453f76ee892STomi Valkeinen 	else if (dsi->module_id == 1)
5454f76ee892STomi Valkeinen 		dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs);
5455f76ee892STomi Valkeinen #endif
5456f76ee892STomi Valkeinen 
5457f76ee892STomi Valkeinen 	return 0;
5458f76ee892STomi Valkeinen 
5459f76ee892STomi Valkeinen err_probe_of:
5460f76ee892STomi Valkeinen 	dsi_uninit_output(dsidev);
5461f76ee892STomi Valkeinen 	dsi_runtime_put(dsidev);
5462f76ee892STomi Valkeinen 
5463f76ee892STomi Valkeinen err_runtime_get:
5464f76ee892STomi Valkeinen 	pm_runtime_disable(&dsidev->dev);
5465f76ee892STomi Valkeinen 	return r;
5466f76ee892STomi Valkeinen }
5467f76ee892STomi Valkeinen 
dsi_unbind(struct device * dev,struct device * master,void * data)5468f76ee892STomi Valkeinen static void dsi_unbind(struct device *dev, struct device *master, void *data)
5469f76ee892STomi Valkeinen {
5470f76ee892STomi Valkeinen 	struct platform_device *dsidev = to_platform_device(dev);
5471f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5472f76ee892STomi Valkeinen 
5473f76ee892STomi Valkeinen 	of_platform_depopulate(&dsidev->dev);
5474f76ee892STomi Valkeinen 
5475f76ee892STomi Valkeinen 	WARN_ON(dsi->scp_clk_refcount > 0);
5476f76ee892STomi Valkeinen 
5477f76ee892STomi Valkeinen 	dss_pll_unregister(&dsi->pll);
5478f76ee892STomi Valkeinen 
5479f76ee892STomi Valkeinen 	dsi_uninit_output(dsidev);
5480f76ee892STomi Valkeinen 
5481f76ee892STomi Valkeinen 	pm_runtime_disable(&dsidev->dev);
5482f76ee892STomi Valkeinen 
5483f76ee892STomi Valkeinen 	if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
5484f76ee892STomi Valkeinen 		regulator_disable(dsi->vdds_dsi_reg);
5485f76ee892STomi Valkeinen 		dsi->vdds_dsi_enabled = false;
5486f76ee892STomi Valkeinen 	}
5487f76ee892STomi Valkeinen }
5488f76ee892STomi Valkeinen 
5489f76ee892STomi Valkeinen static const struct component_ops dsi_component_ops = {
5490f76ee892STomi Valkeinen 	.bind	= dsi_bind,
5491f76ee892STomi Valkeinen 	.unbind	= dsi_unbind,
5492f76ee892STomi Valkeinen };
5493f76ee892STomi Valkeinen 
dsi_probe(struct platform_device * pdev)5494f76ee892STomi Valkeinen static int dsi_probe(struct platform_device *pdev)
5495f76ee892STomi Valkeinen {
5496f76ee892STomi Valkeinen 	return component_add(&pdev->dev, &dsi_component_ops);
5497f76ee892STomi Valkeinen }
5498f76ee892STomi Valkeinen 
dsi_remove(struct platform_device * pdev)5499dc6b77baSUwe Kleine-König static void dsi_remove(struct platform_device *pdev)
5500f76ee892STomi Valkeinen {
5501f76ee892STomi Valkeinen 	component_del(&pdev->dev, &dsi_component_ops);
5502f76ee892STomi Valkeinen }
5503f76ee892STomi Valkeinen 
dsi_runtime_suspend(struct device * dev)5504f76ee892STomi Valkeinen static int dsi_runtime_suspend(struct device *dev)
5505f76ee892STomi Valkeinen {
5506f76ee892STomi Valkeinen 	struct platform_device *pdev = to_platform_device(dev);
5507f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5508f76ee892STomi Valkeinen 
5509f76ee892STomi Valkeinen 	dsi->is_enabled = false;
5510f76ee892STomi Valkeinen 	/* ensure the irq handler sees the is_enabled value */
5511f76ee892STomi Valkeinen 	smp_wmb();
5512f76ee892STomi Valkeinen 	/* wait for current handler to finish before turning the DSI off */
5513f76ee892STomi Valkeinen 	synchronize_irq(dsi->irq);
5514f76ee892STomi Valkeinen 
5515f76ee892STomi Valkeinen 	dispc_runtime_put();
5516f76ee892STomi Valkeinen 
5517f76ee892STomi Valkeinen 	return 0;
5518f76ee892STomi Valkeinen }
5519f76ee892STomi Valkeinen 
dsi_runtime_resume(struct device * dev)5520f76ee892STomi Valkeinen static int dsi_runtime_resume(struct device *dev)
5521f76ee892STomi Valkeinen {
5522f76ee892STomi Valkeinen 	struct platform_device *pdev = to_platform_device(dev);
5523f76ee892STomi Valkeinen 	struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5524f76ee892STomi Valkeinen 	int r;
5525f76ee892STomi Valkeinen 
5526f76ee892STomi Valkeinen 	r = dispc_runtime_get();
5527f76ee892STomi Valkeinen 	if (r)
5528f76ee892STomi Valkeinen 		return r;
5529f76ee892STomi Valkeinen 
5530f76ee892STomi Valkeinen 	dsi->is_enabled = true;
5531f76ee892STomi Valkeinen 	/* ensure the irq handler sees the is_enabled value */
5532f76ee892STomi Valkeinen 	smp_wmb();
5533f76ee892STomi Valkeinen 
5534f76ee892STomi Valkeinen 	return 0;
5535f76ee892STomi Valkeinen }
5536f76ee892STomi Valkeinen 
5537f76ee892STomi Valkeinen static const struct dev_pm_ops dsi_pm_ops = {
5538f76ee892STomi Valkeinen 	.runtime_suspend = dsi_runtime_suspend,
5539f76ee892STomi Valkeinen 	.runtime_resume = dsi_runtime_resume,
5540f76ee892STomi Valkeinen };
5541f76ee892STomi Valkeinen 
5542f76ee892STomi Valkeinen static const struct dsi_module_id_data dsi_of_data_omap3[] = {
5543f76ee892STomi Valkeinen 	{ .address = 0x4804fc00, .id = 0, },
5544f76ee892STomi Valkeinen 	{ },
5545f76ee892STomi Valkeinen };
5546f76ee892STomi Valkeinen 
5547f76ee892STomi Valkeinen static const struct dsi_module_id_data dsi_of_data_omap4[] = {
5548f76ee892STomi Valkeinen 	{ .address = 0x58004000, .id = 0, },
5549f76ee892STomi Valkeinen 	{ .address = 0x58005000, .id = 1, },
5550f76ee892STomi Valkeinen 	{ },
5551f76ee892STomi Valkeinen };
5552f76ee892STomi Valkeinen 
5553f76ee892STomi Valkeinen static const struct dsi_module_id_data dsi_of_data_omap5[] = {
5554f76ee892STomi Valkeinen 	{ .address = 0x58004000, .id = 0, },
5555f76ee892STomi Valkeinen 	{ .address = 0x58009000, .id = 1, },
5556f76ee892STomi Valkeinen 	{ },
5557f76ee892STomi Valkeinen };
5558f76ee892STomi Valkeinen 
5559f76ee892STomi Valkeinen static const struct of_device_id dsi_of_match[] = {
5560f76ee892STomi Valkeinen 	{ .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, },
5561f76ee892STomi Valkeinen 	{ .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, },
5562f76ee892STomi Valkeinen 	{ .compatible = "ti,omap5-dsi", .data = dsi_of_data_omap5, },
5563f76ee892STomi Valkeinen 	{},
5564f76ee892STomi Valkeinen };
5565f76ee892STomi Valkeinen 
5566f76ee892STomi Valkeinen static struct platform_driver omap_dsihw_driver = {
5567f76ee892STomi Valkeinen 	.probe		= dsi_probe,
5568dc6b77baSUwe Kleine-König 	.remove_new	= dsi_remove,
5569f76ee892STomi Valkeinen 	.driver         = {
5570f76ee892STomi Valkeinen 		.name   = "omapdss_dsi",
5571f76ee892STomi Valkeinen 		.pm	= &dsi_pm_ops,
5572f76ee892STomi Valkeinen 		.of_match_table = dsi_of_match,
5573f76ee892STomi Valkeinen 		.suppress_bind_attrs = true,
5574f76ee892STomi Valkeinen 	},
5575f76ee892STomi Valkeinen };
5576f76ee892STomi Valkeinen 
dsi_init_platform_driver(void)5577f76ee892STomi Valkeinen int __init dsi_init_platform_driver(void)
5578f76ee892STomi Valkeinen {
5579f76ee892STomi Valkeinen 	return platform_driver_register(&omap_dsihw_driver);
5580f76ee892STomi Valkeinen }
5581f76ee892STomi Valkeinen 
dsi_uninit_platform_driver(void)5582f76ee892STomi Valkeinen void dsi_uninit_platform_driver(void)
5583f76ee892STomi Valkeinen {
5584f76ee892STomi Valkeinen 	platform_driver_unregister(&omap_dsihw_driver);
5585f76ee892STomi Valkeinen }
5586