blob: 9fdeb213198765af9cb11c46f9d47086539d09d9 [file] [log] [blame]
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001/*
2 * linux/drivers/video/omap2/dss/dsi.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#define DSS_SUBSYS_NAME "DSI"
21
22#include <linux/kernel.h>
23#include <linux/io.h>
24#include <linux/clk.h>
25#include <linux/device.h>
26#include <linux/err.h>
27#include <linux/interrupt.h>
28#include <linux/delay.h>
29#include <linux/mutex.h>
Paul Gortmaker355b2002011-07-03 16:17:28 -040030#include <linux/module.h>
Tomi Valkeinenb9eb5d72010-01-11 16:33:56 +020031#include <linux/semaphore.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020032#include <linux/seq_file.h>
33#include <linux/platform_device.h>
34#include <linux/regulator/consumer.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020035#include <linux/wait.h>
Tomi Valkeinen18946f62010-01-12 14:16:41 +020036#include <linux/workqueue.h>
Tomi Valkeinen40885ab2010-07-28 15:53:38 +030037#include <linux/sched.h>
Archit Tanejaf1da39d2011-05-12 17:26:27 +053038#include <linux/slab.h>
Archit Taneja5a8b5722011-05-12 17:26:29 +053039#include <linux/debugfs.h>
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030040#include <linux/pm_runtime.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020041
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030042#include <video/omapdss.h>
Archit Taneja7a7c48f2011-08-25 18:25:03 +053043#include <video/mipi_display.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020044#include <plat/clock.h>
45
46#include "dss.h"
Archit Taneja819d8072011-03-01 11:54:00 +053047#include "dss_features.h"
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020048
49/*#define VERBOSE_IRQ*/
50#define DSI_CATCH_MISSING_TE
51
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020052struct dsi_reg { u16 idx; };
53
54#define DSI_REG(idx) ((const struct dsi_reg) { idx })
55
56#define DSI_SZ_REGS SZ_1K
57/* DSI Protocol Engine */
58
59#define DSI_REVISION DSI_REG(0x0000)
60#define DSI_SYSCONFIG DSI_REG(0x0010)
61#define DSI_SYSSTATUS DSI_REG(0x0014)
62#define DSI_IRQSTATUS DSI_REG(0x0018)
63#define DSI_IRQENABLE DSI_REG(0x001C)
64#define DSI_CTRL DSI_REG(0x0040)
Archit Taneja75d72472011-05-16 15:17:08 +053065#define DSI_GNQ DSI_REG(0x0044)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020066#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048)
67#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C)
68#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050)
69#define DSI_CLK_CTRL DSI_REG(0x0054)
70#define DSI_TIMING1 DSI_REG(0x0058)
71#define DSI_TIMING2 DSI_REG(0x005C)
72#define DSI_VM_TIMING1 DSI_REG(0x0060)
73#define DSI_VM_TIMING2 DSI_REG(0x0064)
74#define DSI_VM_TIMING3 DSI_REG(0x0068)
75#define DSI_CLK_TIMING DSI_REG(0x006C)
76#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070)
77#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074)
78#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078)
79#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C)
80#define DSI_VM_TIMING4 DSI_REG(0x0080)
81#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084)
82#define DSI_VM_TIMING5 DSI_REG(0x0088)
83#define DSI_VM_TIMING6 DSI_REG(0x008C)
84#define DSI_VM_TIMING7 DSI_REG(0x0090)
85#define DSI_STOPCLK_TIMING DSI_REG(0x0094)
86#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20))
87#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20))
88#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20))
89#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20))
90#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20))
91#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20))
92#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20))
93
94/* DSIPHY_SCP */
95
96#define DSI_DSIPHY_CFG0 DSI_REG(0x200 + 0x0000)
97#define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004)
98#define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008)
99#define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014)
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +0300100#define DSI_DSIPHY_CFG10 DSI_REG(0x200 + 0x0028)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200101
102/* DSI_PLL_CTRL_SCP */
103
104#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000)
105#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004)
106#define DSI_PLL_GO DSI_REG(0x300 + 0x0008)
107#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C)
108#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010)
109
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530110#define REG_GET(dsidev, idx, start, end) \
111 FLD_GET(dsi_read_reg(dsidev, idx), start, end)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200112
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530113#define REG_FLD_MOD(dsidev, idx, val, start, end) \
114 dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end))
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200115
116/* Global interrupts */
117#define DSI_IRQ_VC0 (1 << 0)
118#define DSI_IRQ_VC1 (1 << 1)
119#define DSI_IRQ_VC2 (1 << 2)
120#define DSI_IRQ_VC3 (1 << 3)
121#define DSI_IRQ_WAKEUP (1 << 4)
122#define DSI_IRQ_RESYNC (1 << 5)
123#define DSI_IRQ_PLL_LOCK (1 << 7)
124#define DSI_IRQ_PLL_UNLOCK (1 << 8)
125#define DSI_IRQ_PLL_RECALL (1 << 9)
126#define DSI_IRQ_COMPLEXIO_ERR (1 << 10)
127#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14)
128#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15)
129#define DSI_IRQ_TE_TRIGGER (1 << 16)
130#define DSI_IRQ_ACK_TRIGGER (1 << 17)
131#define DSI_IRQ_SYNC_LOST (1 << 18)
132#define DSI_IRQ_LDO_POWER_GOOD (1 << 19)
133#define DSI_IRQ_TA_TIMEOUT (1 << 20)
134#define DSI_IRQ_ERROR_MASK \
135 (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
Archit Taneja8af6ff02011-09-05 16:48:27 +0530136 DSI_IRQ_TA_TIMEOUT | DSI_IRQ_SYNC_LOST)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200137#define DSI_IRQ_CHANNEL_MASK 0xf
138
139/* Virtual channel interrupts */
140#define DSI_VC_IRQ_CS (1 << 0)
141#define DSI_VC_IRQ_ECC_CORR (1 << 1)
142#define DSI_VC_IRQ_PACKET_SENT (1 << 2)
143#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3)
144#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4)
145#define DSI_VC_IRQ_BTA (1 << 5)
146#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6)
147#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7)
148#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
149#define DSI_VC_IRQ_ERROR_MASK \
150 (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
151 DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
152 DSI_VC_IRQ_FIFO_TX_UDF)
153
154/* ComplexIO interrupts */
155#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0)
156#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1)
157#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200158#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3)
159#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200160#define DSI_CIO_IRQ_ERRESC1 (1 << 5)
161#define DSI_CIO_IRQ_ERRESC2 (1 << 6)
162#define DSI_CIO_IRQ_ERRESC3 (1 << 7)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200163#define DSI_CIO_IRQ_ERRESC4 (1 << 8)
164#define DSI_CIO_IRQ_ERRESC5 (1 << 9)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200165#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10)
166#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11)
167#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200168#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13)
169#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200170#define DSI_CIO_IRQ_STATEULPS1 (1 << 15)
171#define DSI_CIO_IRQ_STATEULPS2 (1 << 16)
172#define DSI_CIO_IRQ_STATEULPS3 (1 << 17)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200173#define DSI_CIO_IRQ_STATEULPS4 (1 << 18)
174#define DSI_CIO_IRQ_STATEULPS5 (1 << 19)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200175#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20)
176#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21)
177#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22)
178#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23)
179#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24)
180#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200181#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26)
182#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27)
183#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28)
184#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200185#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
186#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
Tomi Valkeinenbbecb502010-05-10 14:35:33 +0300187#define DSI_CIO_IRQ_ERROR_MASK \
188 (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \
Tomi Valkeinen67056152011-03-24 16:30:17 +0200189 DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \
190 DSI_CIO_IRQ_ERRSYNCESC5 | \
191 DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \
192 DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \
193 DSI_CIO_IRQ_ERRESC5 | \
194 DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \
195 DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \
196 DSI_CIO_IRQ_ERRCONTROL5 | \
Tomi Valkeinenbbecb502010-05-10 14:35:33 +0300197 DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \
198 DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \
Tomi Valkeinen67056152011-03-24 16:30:17 +0200199 DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \
200 DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \
201 DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200202
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200203typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
204
205#define DSI_MAX_NR_ISRS 2
Tomi Valkeinen739a7f42011-10-13 11:22:06 +0300206#define DSI_MAX_NR_LANES 5
207
208enum dsi_lane_function {
209 DSI_LANE_UNUSED = 0,
210 DSI_LANE_CLK,
211 DSI_LANE_DATA1,
212 DSI_LANE_DATA2,
213 DSI_LANE_DATA3,
214 DSI_LANE_DATA4,
215};
216
217struct dsi_lane_config {
218 enum dsi_lane_function function;
219 u8 polarity;
220};
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200221
222struct dsi_isr_data {
223 omap_dsi_isr_t isr;
224 void *arg;
225 u32 mask;
226};
227
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200228enum fifo_size {
229 DSI_FIFO_SIZE_0 = 0,
230 DSI_FIFO_SIZE_32 = 1,
231 DSI_FIFO_SIZE_64 = 2,
232 DSI_FIFO_SIZE_96 = 3,
233 DSI_FIFO_SIZE_128 = 4,
234};
235
Archit Tanejad6049142011-08-22 11:58:08 +0530236enum dsi_vc_source {
237 DSI_VC_SOURCE_L4 = 0,
238 DSI_VC_SOURCE_VP,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200239};
240
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200241struct dsi_irq_stats {
242 unsigned long last_reset;
243 unsigned irq_count;
244 unsigned dsi_irqs[32];
245 unsigned vc_irqs[4][32];
246 unsigned cio_irqs[32];
247};
248
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200249struct dsi_isr_tables {
250 struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS];
251 struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS];
252 struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS];
253};
254
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530255struct dsi_data {
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +0000256 struct platform_device *pdev;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200257 void __iomem *base;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300258
archit tanejaaffe3602011-02-23 08:41:03 +0000259 int irq;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200260
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300261 struct clk *dss_clk;
262 struct clk *sys_clk;
263
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200264 struct dsi_clock_info current_cinfo;
265
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +0300266 bool vdds_dsi_enabled;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200267 struct regulator *vdds_dsi_reg;
268
269 struct {
Archit Tanejad6049142011-08-22 11:58:08 +0530270 enum dsi_vc_source source;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200271 struct omap_dss_device *dssdev;
272 enum fifo_size fifo_size;
Archit Taneja5ee3c142011-03-02 12:35:53 +0530273 int vc_id;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200274 } vc[4];
275
276 struct mutex lock;
Tomi Valkeinenb9eb5d72010-01-11 16:33:56 +0200277 struct semaphore bus_lock;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200278
279 unsigned pll_locked;
280
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200281 spinlock_t irq_lock;
282 struct dsi_isr_tables isr_tables;
283 /* space for a copy used by the interrupt handler */
284 struct dsi_isr_tables isr_tables_copy;
285
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200286 int update_channel;
Tomi Valkeinen5476e742011-11-03 16:34:20 +0200287#ifdef DEBUG
288 unsigned update_bytes;
289#endif
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200290
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200291 bool te_enabled;
Tomi Valkeinen40885ab2010-07-28 15:53:38 +0300292 bool ulps_enabled;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200293
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200294 void (*framedone_callback)(int, void *);
295 void *framedone_data;
296
297 struct delayed_work framedone_timeout_work;
298
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200299#ifdef DSI_CATCH_MISSING_TE
300 struct timer_list te_timer;
301#endif
302
303 unsigned long cache_req_pck;
304 unsigned long cache_clk_freq;
305 struct dsi_clock_info cache_cinfo;
306
307 u32 errors;
308 spinlock_t errors_lock;
309#ifdef DEBUG
310 ktime_t perf_setup_time;
311 ktime_t perf_start_time;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200312#endif
313 int debug_read;
314 int debug_write;
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200315
316#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
317 spinlock_t irq_stats_lock;
318 struct dsi_irq_stats irq_stats;
319#endif
Taneja, Archit49641112011-03-14 23:28:23 -0500320 /* DSI PLL Parameter Ranges */
321 unsigned long regm_max, regn_max;
322 unsigned long regm_dispc_max, regm_dsi_max;
323 unsigned long fint_min, fint_max;
324 unsigned long lpdiv_max;
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +0300325
Tomi Valkeinend9820852011-10-12 15:05:59 +0300326 unsigned num_lanes_supported;
Archit Taneja75d72472011-05-16 15:17:08 +0530327
Tomi Valkeinen739a7f42011-10-13 11:22:06 +0300328 struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
329 unsigned num_lanes_used;
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +0300330
331 unsigned scp_clk_refcount;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530332};
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200333
Archit Taneja2e868db2011-05-12 17:26:28 +0530334struct dsi_packet_sent_handler_data {
335 struct platform_device *dsidev;
336 struct completion *completion;
337};
338
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530339static struct platform_device *dsi_pdev_map[MAX_NUM_DSI];
340
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200341#ifdef DEBUG
Rusty Russell90ab5ee2012-01-13 09:32:20 +1030342static bool dsi_perf;
343module_param(dsi_perf, bool, 0644);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200344#endif
345
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530346static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev)
347{
348 return dev_get_drvdata(&dsidev->dev);
349}
350
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530351static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev)
352{
353 return dsi_pdev_map[dssdev->phy.dsi.module];
354}
355
356struct platform_device *dsi_get_dsidev_from_id(int module)
357{
358 return dsi_pdev_map[module];
359}
360
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +0300361static inline int dsi_get_dsidev_id(struct platform_device *dsidev)
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530362{
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +0300363 return dsidev->id;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530364}
365
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530366static inline void dsi_write_reg(struct platform_device *dsidev,
367 const struct dsi_reg idx, u32 val)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200368{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530369 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
370
371 __raw_writel(val, dsi->base + idx.idx);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200372}
373
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530374static inline u32 dsi_read_reg(struct platform_device *dsidev,
375 const struct dsi_reg idx)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200376{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530377 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
378
379 return __raw_readl(dsi->base + idx.idx);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200380}
381
Archit Taneja1ffefe72011-05-12 17:26:24 +0530382void dsi_bus_lock(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200383{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530384 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
385 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
386
387 down(&dsi->bus_lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200388}
389EXPORT_SYMBOL(dsi_bus_lock);
390
Archit Taneja1ffefe72011-05-12 17:26:24 +0530391void dsi_bus_unlock(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200392{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530393 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
394 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
395
396 up(&dsi->bus_lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200397}
398EXPORT_SYMBOL(dsi_bus_unlock);
399
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530400static bool dsi_bus_is_locked(struct platform_device *dsidev)
Tomi Valkeinen4f765022010-01-18 16:27:52 +0200401{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530402 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
403
404 return dsi->bus_lock.count == 0;
Tomi Valkeinen4f765022010-01-18 16:27:52 +0200405}
406
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +0200407static void dsi_completion_handler(void *data, u32 mask)
408{
409 complete((struct completion *)data);
410}
411
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530412static inline int wait_for_bit_change(struct platform_device *dsidev,
413 const struct dsi_reg idx, int bitnum, int value)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200414{
Tomi Valkeinen3b984092011-10-13 19:06:49 +0300415 unsigned long timeout;
416 ktime_t wait;
417 int t;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200418
Tomi Valkeinen3b984092011-10-13 19:06:49 +0300419 /* first busyloop to see if the bit changes right away */
420 t = 100;
421 while (t-- > 0) {
422 if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
423 return value;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200424 }
425
Tomi Valkeinen3b984092011-10-13 19:06:49 +0300426 /* then loop for 500ms, sleeping for 1ms in between */
427 timeout = jiffies + msecs_to_jiffies(500);
428 while (time_before(jiffies, timeout)) {
429 if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
430 return value;
431
432 wait = ns_to_ktime(1000 * 1000);
433 set_current_state(TASK_UNINTERRUPTIBLE);
434 schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
435 }
436
437 return !value;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200438}
439
Archit Tanejaa3b3cc22011-09-08 18:42:16 +0530440u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
441{
442 switch (fmt) {
443 case OMAP_DSS_DSI_FMT_RGB888:
444 case OMAP_DSS_DSI_FMT_RGB666:
445 return 24;
446 case OMAP_DSS_DSI_FMT_RGB666_PACKED:
447 return 18;
448 case OMAP_DSS_DSI_FMT_RGB565:
449 return 16;
450 default:
451 BUG();
452 }
453}
454
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200455#ifdef DEBUG
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530456static void dsi_perf_mark_setup(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200457{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530458 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
459 dsi->perf_setup_time = ktime_get();
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200460}
461
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530462static void dsi_perf_mark_start(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200463{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530464 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
465 dsi->perf_start_time = ktime_get();
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200466}
467
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530468static void dsi_perf_show(struct platform_device *dsidev, const char *name)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200469{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530470 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200471 ktime_t t, setup_time, trans_time;
472 u32 total_bytes;
473 u32 setup_us, trans_us, total_us;
474
475 if (!dsi_perf)
476 return;
477
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200478 t = ktime_get();
479
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530480 setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200481 setup_us = (u32)ktime_to_us(setup_time);
482 if (setup_us == 0)
483 setup_us = 1;
484
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530485 trans_time = ktime_sub(t, dsi->perf_start_time);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200486 trans_us = (u32)ktime_to_us(trans_time);
487 if (trans_us == 0)
488 trans_us = 1;
489
490 total_us = setup_us + trans_us;
491
Tomi Valkeinen5476e742011-11-03 16:34:20 +0200492 total_bytes = dsi->update_bytes;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200493
Tomi Valkeinen1bbb2752010-01-11 16:41:10 +0200494 printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
495 "%u bytes, %u kbytes/sec\n",
496 name,
497 setup_us,
498 trans_us,
499 total_us,
500 1000*1000 / total_us,
501 total_bytes,
502 total_bytes * 1000 / total_us);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200503}
504#else
Tomi Valkeinen4a9a5e32011-05-23 16:36:09 +0300505static inline void dsi_perf_mark_setup(struct platform_device *dsidev)
506{
507}
508
509static inline void dsi_perf_mark_start(struct platform_device *dsidev)
510{
511}
512
513static inline void dsi_perf_show(struct platform_device *dsidev,
514 const char *name)
515{
516}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200517#endif
518
519static void print_irq_status(u32 status)
520{
Tomi Valkeinend80d4992011-03-02 15:53:07 +0200521 if (status == 0)
522 return;
523
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200524#ifndef VERBOSE_IRQ
525 if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0)
526 return;
527#endif
528 printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status);
529
530#define PIS(x) \
531 if (status & DSI_IRQ_##x) \
532 printk(#x " ");
533#ifdef VERBOSE_IRQ
534 PIS(VC0);
535 PIS(VC1);
536 PIS(VC2);
537 PIS(VC3);
538#endif
539 PIS(WAKEUP);
540 PIS(RESYNC);
541 PIS(PLL_LOCK);
542 PIS(PLL_UNLOCK);
543 PIS(PLL_RECALL);
544 PIS(COMPLEXIO_ERR);
545 PIS(HS_TX_TIMEOUT);
546 PIS(LP_RX_TIMEOUT);
547 PIS(TE_TRIGGER);
548 PIS(ACK_TRIGGER);
549 PIS(SYNC_LOST);
550 PIS(LDO_POWER_GOOD);
551 PIS(TA_TIMEOUT);
552#undef PIS
553
554 printk("\n");
555}
556
557static void print_irq_status_vc(int channel, u32 status)
558{
Tomi Valkeinend80d4992011-03-02 15:53:07 +0200559 if (status == 0)
560 return;
561
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200562#ifndef VERBOSE_IRQ
563 if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
564 return;
565#endif
566 printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status);
567
568#define PIS(x) \
569 if (status & DSI_VC_IRQ_##x) \
570 printk(#x " ");
571 PIS(CS);
572 PIS(ECC_CORR);
573#ifdef VERBOSE_IRQ
574 PIS(PACKET_SENT);
575#endif
576 PIS(FIFO_TX_OVF);
577 PIS(FIFO_RX_OVF);
578 PIS(BTA);
579 PIS(ECC_NO_CORR);
580 PIS(FIFO_TX_UDF);
581 PIS(PP_BUSY_CHANGE);
582#undef PIS
583 printk("\n");
584}
585
586static void print_irq_status_cio(u32 status)
587{
Tomi Valkeinend80d4992011-03-02 15:53:07 +0200588 if (status == 0)
589 return;
590
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200591 printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status);
592
593#define PIS(x) \
594 if (status & DSI_CIO_IRQ_##x) \
595 printk(#x " ");
596 PIS(ERRSYNCESC1);
597 PIS(ERRSYNCESC2);
598 PIS(ERRSYNCESC3);
599 PIS(ERRESC1);
600 PIS(ERRESC2);
601 PIS(ERRESC3);
602 PIS(ERRCONTROL1);
603 PIS(ERRCONTROL2);
604 PIS(ERRCONTROL3);
605 PIS(STATEULPS1);
606 PIS(STATEULPS2);
607 PIS(STATEULPS3);
608 PIS(ERRCONTENTIONLP0_1);
609 PIS(ERRCONTENTIONLP1_1);
610 PIS(ERRCONTENTIONLP0_2);
611 PIS(ERRCONTENTIONLP1_2);
612 PIS(ERRCONTENTIONLP0_3);
613 PIS(ERRCONTENTIONLP1_3);
614 PIS(ULPSACTIVENOT_ALL0);
615 PIS(ULPSACTIVENOT_ALL1);
616#undef PIS
617
618 printk("\n");
619}
620
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200621#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530622static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus,
623 u32 *vcstatus, u32 ciostatus)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200624{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530625 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200626 int i;
627
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530628 spin_lock(&dsi->irq_stats_lock);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200629
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530630 dsi->irq_stats.irq_count++;
631 dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200632
633 for (i = 0; i < 4; ++i)
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530634 dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200635
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530636 dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200637
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530638 spin_unlock(&dsi->irq_stats_lock);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200639}
640#else
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530641#define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus)
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200642#endif
643
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200644static int debug_irq;
645
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530646static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus,
647 u32 *vcstatus, u32 ciostatus)
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200648{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530649 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200650 int i;
651
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200652 if (irqstatus & DSI_IRQ_ERROR_MASK) {
653 DSSERR("DSI error, irqstatus %x\n", irqstatus);
654 print_irq_status(irqstatus);
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530655 spin_lock(&dsi->errors_lock);
656 dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK;
657 spin_unlock(&dsi->errors_lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200658 } else if (debug_irq) {
659 print_irq_status(irqstatus);
660 }
661
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200662 for (i = 0; i < 4; ++i) {
663 if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) {
664 DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
665 i, vcstatus[i]);
666 print_irq_status_vc(i, vcstatus[i]);
667 } else if (debug_irq) {
668 print_irq_status_vc(i, vcstatus[i]);
669 }
670 }
671
672 if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
673 DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
674 print_irq_status_cio(ciostatus);
675 } else if (debug_irq) {
676 print_irq_status_cio(ciostatus);
677 }
678}
679
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200680static void dsi_call_isrs(struct dsi_isr_data *isr_array,
681 unsigned isr_array_size, u32 irqstatus)
682{
683 struct dsi_isr_data *isr_data;
684 int i;
685
686 for (i = 0; i < isr_array_size; i++) {
687 isr_data = &isr_array[i];
688 if (isr_data->isr && isr_data->mask & irqstatus)
689 isr_data->isr(isr_data->arg, irqstatus);
690 }
691}
692
693static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
694 u32 irqstatus, u32 *vcstatus, u32 ciostatus)
695{
696 int i;
697
698 dsi_call_isrs(isr_tables->isr_table,
699 ARRAY_SIZE(isr_tables->isr_table),
700 irqstatus);
701
702 for (i = 0; i < 4; ++i) {
703 if (vcstatus[i] == 0)
704 continue;
705 dsi_call_isrs(isr_tables->isr_table_vc[i],
706 ARRAY_SIZE(isr_tables->isr_table_vc[i]),
707 vcstatus[i]);
708 }
709
710 if (ciostatus != 0)
711 dsi_call_isrs(isr_tables->isr_table_cio,
712 ARRAY_SIZE(isr_tables->isr_table_cio),
713 ciostatus);
714}
715
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200716static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
717{
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530718 struct platform_device *dsidev;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530719 struct dsi_data *dsi;
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200720 u32 irqstatus, vcstatus[4], ciostatus;
721 int i;
722
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530723 dsidev = (struct platform_device *) arg;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530724 dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530725
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530726 spin_lock(&dsi->irq_lock);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200727
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530728 irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200729
730 /* IRQ is not for us */
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200731 if (!irqstatus) {
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530732 spin_unlock(&dsi->irq_lock);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200733 return IRQ_NONE;
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200734 }
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200735
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530736 dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200737 /* flush posted write */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530738 dsi_read_reg(dsidev, DSI_IRQSTATUS);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200739
740 for (i = 0; i < 4; ++i) {
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200741 if ((irqstatus & (1 << i)) == 0) {
742 vcstatus[i] = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200743 continue;
Tomi Valkeinenab83b142010-06-09 15:31:01 +0300744 }
745
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530746 vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200747
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530748 dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200749 /* flush posted write */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530750 dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200751 }
752
753 if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530754 ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200755
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530756 dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200757 /* flush posted write */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530758 dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200759 } else {
760 ciostatus = 0;
761 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200762
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200763#ifdef DSI_CATCH_MISSING_TE
764 if (irqstatus & DSI_IRQ_TE_TRIGGER)
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530765 del_timer(&dsi->te_timer);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200766#endif
767
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200768 /* make a copy and unlock, so that isrs can unregister
769 * themselves */
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530770 memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
771 sizeof(dsi->isr_tables));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200772
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530773 spin_unlock(&dsi->irq_lock);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200774
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530775 dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200776
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530777 dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200778
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530779 dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200780
archit tanejaaffe3602011-02-23 08:41:03 +0000781 return IRQ_HANDLED;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200782}
783
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530784/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530785static void _omap_dsi_configure_irqs(struct platform_device *dsidev,
786 struct dsi_isr_data *isr_array,
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200787 unsigned isr_array_size, u32 default_mask,
788 const struct dsi_reg enable_reg,
789 const struct dsi_reg status_reg)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200790{
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200791 struct dsi_isr_data *isr_data;
792 u32 mask;
793 u32 old_mask;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200794 int i;
795
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200796 mask = default_mask;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200797
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200798 for (i = 0; i < isr_array_size; i++) {
799 isr_data = &isr_array[i];
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200800
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200801 if (isr_data->isr == NULL)
802 continue;
803
804 mask |= isr_data->mask;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200805 }
806
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530807 old_mask = dsi_read_reg(dsidev, enable_reg);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200808 /* clear the irqstatus for newly enabled irqs */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530809 dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask);
810 dsi_write_reg(dsidev, enable_reg, mask);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200811
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200812 /* flush posted writes */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530813 dsi_read_reg(dsidev, enable_reg);
814 dsi_read_reg(dsidev, status_reg);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200815}
816
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530817/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530818static void _omap_dsi_set_irqs(struct platform_device *dsidev)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200819{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530820 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200821 u32 mask = DSI_IRQ_ERROR_MASK;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200822#ifdef DSI_CATCH_MISSING_TE
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200823 mask |= DSI_IRQ_TE_TRIGGER;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200824#endif
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530825 _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table,
826 ARRAY_SIZE(dsi->isr_tables.isr_table), mask,
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200827 DSI_IRQENABLE, DSI_IRQSTATUS);
828}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200829
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530830/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530831static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200832{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530833 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
834
835 _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc],
836 ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]),
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200837 DSI_VC_IRQ_ERROR_MASK,
838 DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
839}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200840
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530841/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530842static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200843{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530844 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
845
846 _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio,
847 ARRAY_SIZE(dsi->isr_tables.isr_table_cio),
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200848 DSI_CIO_IRQ_ERROR_MASK,
849 DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
850}
851
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530852static void _dsi_initialize_irq(struct platform_device *dsidev)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200853{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530854 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200855 unsigned long flags;
856 int vc;
857
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530858 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200859
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530860 memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200861
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530862 _omap_dsi_set_irqs(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200863 for (vc = 0; vc < 4; ++vc)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530864 _omap_dsi_set_irqs_vc(dsidev, vc);
865 _omap_dsi_set_irqs_cio(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200866
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530867 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200868}
869
870static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
871 struct dsi_isr_data *isr_array, unsigned isr_array_size)
872{
873 struct dsi_isr_data *isr_data;
874 int free_idx;
875 int i;
876
877 BUG_ON(isr == NULL);
878
879 /* check for duplicate entry and find a free slot */
880 free_idx = -1;
881 for (i = 0; i < isr_array_size; i++) {
882 isr_data = &isr_array[i];
883
884 if (isr_data->isr == isr && isr_data->arg == arg &&
885 isr_data->mask == mask) {
886 return -EINVAL;
887 }
888
889 if (isr_data->isr == NULL && free_idx == -1)
890 free_idx = i;
891 }
892
893 if (free_idx == -1)
894 return -EBUSY;
895
896 isr_data = &isr_array[free_idx];
897 isr_data->isr = isr;
898 isr_data->arg = arg;
899 isr_data->mask = mask;
900
901 return 0;
902}
903
904static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
905 struct dsi_isr_data *isr_array, unsigned isr_array_size)
906{
907 struct dsi_isr_data *isr_data;
908 int i;
909
910 for (i = 0; i < isr_array_size; i++) {
911 isr_data = &isr_array[i];
912 if (isr_data->isr != isr || isr_data->arg != arg ||
913 isr_data->mask != mask)
914 continue;
915
916 isr_data->isr = NULL;
917 isr_data->arg = NULL;
918 isr_data->mask = 0;
919
920 return 0;
921 }
922
923 return -EINVAL;
924}
925
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530926static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr,
927 void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200928{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530929 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200930 unsigned long flags;
931 int r;
932
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530933 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200934
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530935 r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table,
936 ARRAY_SIZE(dsi->isr_tables.isr_table));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200937
938 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530939 _omap_dsi_set_irqs(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200940
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530941 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200942
943 return r;
944}
945
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530946static int dsi_unregister_isr(struct platform_device *dsidev,
947 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200948{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530949 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200950 unsigned long flags;
951 int r;
952
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530953 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200954
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530955 r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table,
956 ARRAY_SIZE(dsi->isr_tables.isr_table));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200957
958 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530959 _omap_dsi_set_irqs(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200960
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530961 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200962
963 return r;
964}
965
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530966static int dsi_register_isr_vc(struct platform_device *dsidev, int channel,
967 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200968{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530969 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200970 unsigned long flags;
971 int r;
972
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530973 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200974
975 r = _dsi_register_isr(isr, arg, mask,
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530976 dsi->isr_tables.isr_table_vc[channel],
977 ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200978
979 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530980 _omap_dsi_set_irqs_vc(dsidev, channel);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200981
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530982 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200983
984 return r;
985}
986
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530987static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel,
988 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200989{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530990 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200991 unsigned long flags;
992 int r;
993
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530994 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200995
996 r = _dsi_unregister_isr(isr, arg, mask,
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530997 dsi->isr_tables.isr_table_vc[channel],
998 ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200999
1000 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301001 _omap_dsi_set_irqs_vc(dsidev, channel);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001002
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301003 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001004
1005 return r;
1006}
1007
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301008static int dsi_register_isr_cio(struct platform_device *dsidev,
1009 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001010{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301011 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001012 unsigned long flags;
1013 int r;
1014
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301015 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001016
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301017 r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
1018 ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001019
1020 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301021 _omap_dsi_set_irqs_cio(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001022
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301023 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001024
1025 return r;
1026}
1027
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301028static int dsi_unregister_isr_cio(struct platform_device *dsidev,
1029 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001030{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301031 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001032 unsigned long flags;
1033 int r;
1034
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301035 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001036
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301037 r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
1038 ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001039
1040 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301041 _omap_dsi_set_irqs_cio(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001042
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301043 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001044
1045 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001046}
1047
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301048static u32 dsi_get_errors(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001049{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301050 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001051 unsigned long flags;
1052 u32 e;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301053 spin_lock_irqsave(&dsi->errors_lock, flags);
1054 e = dsi->errors;
1055 dsi->errors = 0;
1056 spin_unlock_irqrestore(&dsi->errors_lock, flags);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001057 return e;
1058}
1059
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001060int dsi_runtime_get(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001061{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001062 int r;
1063 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1064
1065 DSSDBG("dsi_runtime_get\n");
1066
1067 r = pm_runtime_get_sync(&dsi->pdev->dev);
1068 WARN_ON(r < 0);
1069 return r < 0 ? r : 0;
1070}
1071
1072void dsi_runtime_put(struct platform_device *dsidev)
1073{
1074 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1075 int r;
1076
1077 DSSDBG("dsi_runtime_put\n");
1078
Tomi Valkeinen0eaf9f52012-01-23 13:23:08 +02001079 r = pm_runtime_put_sync(&dsi->pdev->dev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001080 WARN_ON(r < 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001081}
1082
1083/* source clock for DSI PLL. this could also be PCLKFREE */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301084static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
1085 bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001086{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301087 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1088
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001089 if (enable)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001090 clk_enable(dsi->sys_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001091 else
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001092 clk_disable(dsi->sys_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001093
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301094 if (enable && dsi->pll_locked) {
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301095 if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001096 DSSERR("cannot lock PLL when enabling clocks\n");
1097 }
1098}
1099
1100#ifdef DEBUG
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301101static void _dsi_print_reset_status(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001102{
1103 u32 l;
Tomi Valkeinenc335cbf2010-10-07 13:27:42 +03001104 int b0, b1, b2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001105
1106 if (!dss_debug)
1107 return;
1108
1109 /* A dummy read using the SCP interface to any DSIPHY register is
1110 * required after DSIPHY reset to complete the reset of the DSI complex
1111 * I/O. */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301112 l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001113
1114 printk(KERN_DEBUG "DSI resets: ");
1115
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301116 l = dsi_read_reg(dsidev, DSI_PLL_STATUS);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001117 printk("PLL (%d) ", FLD_GET(l, 0, 0));
1118
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301119 l = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001120 printk("CIO (%d) ", FLD_GET(l, 29, 29));
1121
Tomi Valkeinenc335cbf2010-10-07 13:27:42 +03001122 if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) {
1123 b0 = 28;
1124 b1 = 27;
1125 b2 = 26;
1126 } else {
1127 b0 = 24;
1128 b1 = 25;
1129 b2 = 26;
1130 }
1131
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301132 l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinenc335cbf2010-10-07 13:27:42 +03001133 printk("PHY (%x%x%x, %d, %d, %d)\n",
1134 FLD_GET(l, b0, b0),
1135 FLD_GET(l, b1, b1),
1136 FLD_GET(l, b2, b2),
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001137 FLD_GET(l, 29, 29),
1138 FLD_GET(l, 30, 30),
1139 FLD_GET(l, 31, 31));
1140}
1141#else
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301142#define _dsi_print_reset_status(x)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001143#endif
1144
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301145static inline int dsi_if_enable(struct platform_device *dsidev, bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001146{
1147 DSSDBG("dsi_if_enable(%d)\n", enable);
1148
1149 enable = enable ? 1 : 0;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301150 REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001151
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301152 if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001153 DSSERR("Failed to set dsi_if_enable to %d\n", enable);
1154 return -EIO;
1155 }
1156
1157 return 0;
1158}
1159
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301160unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001161{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301162 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1163
1164 return dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001165}
1166
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301167static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001168{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301169 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1170
1171 return dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001172}
1173
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301174static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001175{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301176 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1177
1178 return dsi->current_cinfo.clkin4ddr / 16;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001179}
1180
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301181static unsigned long dsi_fclk_rate(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001182{
1183 unsigned long r;
Archit Taneja5a8b5722011-05-12 17:26:29 +05301184 int dsi_module = dsi_get_dsidev_id(dsidev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001185 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001186
Archit Taneja5a8b5722011-05-12 17:26:29 +05301187 if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) {
Archit Taneja1bb47832011-02-24 14:17:30 +05301188 /* DSI FCLK source is DSS_CLK_FCK */
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001189 r = clk_get_rate(dsi->dss_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001190 } else {
Archit Taneja1bb47832011-02-24 14:17:30 +05301191 /* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301192 r = dsi_get_pll_hsdiv_dsi_rate(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001193 }
1194
1195 return r;
1196}
1197
1198static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev)
1199{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301200 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301201 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001202 unsigned long dsi_fclk;
1203 unsigned lp_clk_div;
1204 unsigned long lp_clk;
1205
Tomi Valkeinenc6940a32011-02-22 13:36:10 +02001206 lp_clk_div = dssdev->clocks.dsi.lp_clk_div;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001207
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301208 if (lp_clk_div == 0 || lp_clk_div > dsi->lpdiv_max)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001209 return -EINVAL;
1210
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301211 dsi_fclk = dsi_fclk_rate(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001212
1213 lp_clk = dsi_fclk / 2 / lp_clk_div;
1214
1215 DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301216 dsi->current_cinfo.lp_clk = lp_clk;
1217 dsi->current_cinfo.lp_clk_div = lp_clk_div;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001218
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301219 /* LP_CLK_DIVISOR */
1220 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001221
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301222 /* LP_RX_SYNCHRO_ENABLE */
1223 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001224
1225 return 0;
1226}
1227
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301228static void dsi_enable_scp_clk(struct platform_device *dsidev)
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001229{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301230 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1231
1232 if (dsi->scp_clk_refcount++ == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301233 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001234}
1235
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301236static void dsi_disable_scp_clk(struct platform_device *dsidev)
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001237{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301238 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1239
1240 WARN_ON(dsi->scp_clk_refcount == 0);
1241 if (--dsi->scp_clk_refcount == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301242 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001243}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001244
1245enum dsi_pll_power_state {
1246 DSI_PLL_POWER_OFF = 0x0,
1247 DSI_PLL_POWER_ON_HSCLK = 0x1,
1248 DSI_PLL_POWER_ON_ALL = 0x2,
1249 DSI_PLL_POWER_ON_DIV = 0x3,
1250};
1251
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301252static int dsi_pll_power(struct platform_device *dsidev,
1253 enum dsi_pll_power_state state)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001254{
1255 int t = 0;
1256
Tomi Valkeinenc94dfe052011-04-15 10:42:59 +03001257 /* DSI-PLL power command 0x3 is not working */
1258 if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) &&
1259 state == DSI_PLL_POWER_ON_DIV)
1260 state = DSI_PLL_POWER_ON_ALL;
1261
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301262 /* PLL_PWR_CMD */
1263 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001264
1265 /* PLL_PWR_STATUS */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301266 while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02001267 if (++t > 1000) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001268 DSSERR("Failed to set DSI PLL power mode to %d\n",
1269 state);
1270 return -ENODEV;
1271 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02001272 udelay(1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001273 }
1274
1275 return 0;
1276}
1277
1278/* calculate clock rates using dividers in cinfo */
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001279static int dsi_calc_clock_rates(struct platform_device *dsidev,
Sumit Semwalff1b2cd2010-12-02 11:27:11 +00001280 struct dsi_clock_info *cinfo)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001281{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301282 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1283
1284 if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001285 return -EINVAL;
1286
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301287 if (cinfo->regm == 0 || cinfo->regm > dsi->regm_max)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001288 return -EINVAL;
1289
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301290 if (cinfo->regm_dispc > dsi->regm_dispc_max)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001291 return -EINVAL;
1292
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301293 if (cinfo->regm_dsi > dsi->regm_dsi_max)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001294 return -EINVAL;
1295
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001296 cinfo->clkin = clk_get_rate(dsi->sys_clk);
1297 cinfo->fint = cinfo->clkin / cinfo->regn;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001298
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301299 if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001300 return -EINVAL;
1301
1302 cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint;
1303
1304 if (cinfo->clkin4ddr > 1800 * 1000 * 1000)
1305 return -EINVAL;
1306
Archit Taneja1bb47832011-02-24 14:17:30 +05301307 if (cinfo->regm_dispc > 0)
1308 cinfo->dsi_pll_hsdiv_dispc_clk =
1309 cinfo->clkin4ddr / cinfo->regm_dispc;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001310 else
Archit Taneja1bb47832011-02-24 14:17:30 +05301311 cinfo->dsi_pll_hsdiv_dispc_clk = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001312
Archit Taneja1bb47832011-02-24 14:17:30 +05301313 if (cinfo->regm_dsi > 0)
1314 cinfo->dsi_pll_hsdiv_dsi_clk =
1315 cinfo->clkin4ddr / cinfo->regm_dsi;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001316 else
Archit Taneja1bb47832011-02-24 14:17:30 +05301317 cinfo->dsi_pll_hsdiv_dsi_clk = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001318
1319 return 0;
1320}
1321
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301322int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
1323 unsigned long req_pck, struct dsi_clock_info *dsi_cinfo,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001324 struct dispc_clock_info *dispc_cinfo)
1325{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301326 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001327 struct dsi_clock_info cur, best;
1328 struct dispc_clock_info best_dispc;
1329 int min_fck_per_pck;
1330 int match = 0;
Archit Taneja1bb47832011-02-24 14:17:30 +05301331 unsigned long dss_sys_clk, max_dss_fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001332
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001333 dss_sys_clk = clk_get_rate(dsi->sys_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001334
Taneja, Archit31ef8232011-03-14 23:28:22 -05001335 max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
Archit Taneja819d8072011-03-01 11:54:00 +05301336
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301337 if (req_pck == dsi->cache_req_pck &&
1338 dsi->cache_cinfo.clkin == dss_sys_clk) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001339 DSSDBG("DSI clock info found from cache\n");
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301340 *dsi_cinfo = dsi->cache_cinfo;
Archit Taneja1bb47832011-02-24 14:17:30 +05301341 dispc_find_clk_divs(is_tft, req_pck,
1342 dsi_cinfo->dsi_pll_hsdiv_dispc_clk, dispc_cinfo);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001343 return 0;
1344 }
1345
1346 min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
1347
1348 if (min_fck_per_pck &&
Archit Taneja819d8072011-03-01 11:54:00 +05301349 req_pck * min_fck_per_pck > max_dss_fck) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001350 DSSERR("Requested pixel clock not possible with the current "
1351 "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
1352 "the constraint off.\n");
1353 min_fck_per_pck = 0;
1354 }
1355
1356 DSSDBG("dsi_pll_calc\n");
1357
1358retry:
1359 memset(&best, 0, sizeof(best));
1360 memset(&best_dispc, 0, sizeof(best_dispc));
1361
1362 memset(&cur, 0, sizeof(cur));
Archit Taneja1bb47832011-02-24 14:17:30 +05301363 cur.clkin = dss_sys_clk;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001364
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001365 /* 0.75MHz < Fint = clkin / regn < 2.1MHz */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001366 /* To reduce PLL lock time, keep Fint high (around 2 MHz) */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301367 for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) {
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001368 cur.fint = cur.clkin / cur.regn;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001369
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301370 if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001371 continue;
1372
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001373 /* DSIPHY(MHz) = (2 * regm / regn) * clkin */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301374 for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001375 unsigned long a, b;
1376
1377 a = 2 * cur.regm * (cur.clkin/1000);
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001378 b = cur.regn;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001379 cur.clkin4ddr = a / b * 1000;
1380
1381 if (cur.clkin4ddr > 1800 * 1000 * 1000)
1382 break;
1383
Archit Taneja1bb47832011-02-24 14:17:30 +05301384 /* dsi_pll_hsdiv_dispc_clk(MHz) =
1385 * DSIPHY(MHz) / regm_dispc < 173MHz/186Mhz */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301386 for (cur.regm_dispc = 1; cur.regm_dispc <
1387 dsi->regm_dispc_max; ++cur.regm_dispc) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001388 struct dispc_clock_info cur_dispc;
Archit Taneja1bb47832011-02-24 14:17:30 +05301389 cur.dsi_pll_hsdiv_dispc_clk =
1390 cur.clkin4ddr / cur.regm_dispc;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001391
1392 /* this will narrow down the search a bit,
1393 * but still give pixclocks below what was
1394 * requested */
Archit Taneja1bb47832011-02-24 14:17:30 +05301395 if (cur.dsi_pll_hsdiv_dispc_clk < req_pck)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001396 break;
1397
Archit Taneja1bb47832011-02-24 14:17:30 +05301398 if (cur.dsi_pll_hsdiv_dispc_clk > max_dss_fck)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001399 continue;
1400
1401 if (min_fck_per_pck &&
Archit Taneja1bb47832011-02-24 14:17:30 +05301402 cur.dsi_pll_hsdiv_dispc_clk <
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001403 req_pck * min_fck_per_pck)
1404 continue;
1405
1406 match = 1;
1407
1408 dispc_find_clk_divs(is_tft, req_pck,
Archit Taneja1bb47832011-02-24 14:17:30 +05301409 cur.dsi_pll_hsdiv_dispc_clk,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001410 &cur_dispc);
1411
1412 if (abs(cur_dispc.pck - req_pck) <
1413 abs(best_dispc.pck - req_pck)) {
1414 best = cur;
1415 best_dispc = cur_dispc;
1416
1417 if (cur_dispc.pck == req_pck)
1418 goto found;
1419 }
1420 }
1421 }
1422 }
1423found:
1424 if (!match) {
1425 if (min_fck_per_pck) {
1426 DSSERR("Could not find suitable clock settings.\n"
1427 "Turning FCK/PCK constraint off and"
1428 "trying again.\n");
1429 min_fck_per_pck = 0;
1430 goto retry;
1431 }
1432
1433 DSSERR("Could not find suitable clock settings.\n");
1434
1435 return -EINVAL;
1436 }
1437
Archit Taneja1bb47832011-02-24 14:17:30 +05301438 /* dsi_pll_hsdiv_dsi_clk (regm_dsi) is not used */
1439 best.regm_dsi = 0;
1440 best.dsi_pll_hsdiv_dsi_clk = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001441
1442 if (dsi_cinfo)
1443 *dsi_cinfo = best;
1444 if (dispc_cinfo)
1445 *dispc_cinfo = best_dispc;
1446
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301447 dsi->cache_req_pck = req_pck;
1448 dsi->cache_clk_freq = 0;
1449 dsi->cache_cinfo = best;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001450
1451 return 0;
1452}
1453
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301454int dsi_pll_set_clock_div(struct platform_device *dsidev,
1455 struct dsi_clock_info *cinfo)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001456{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301457 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001458 int r = 0;
1459 u32 l;
Archit Taneja9613c022011-03-22 06:33:36 -05001460 int f = 0;
Taneja, Archit49641112011-03-14 23:28:23 -05001461 u8 regn_start, regn_end, regm_start, regm_end;
1462 u8 regm_dispc_start, regm_dispc_end, regm_dsi_start, regm_dsi_end;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001463
1464 DSSDBGF();
1465
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001466 dsi->current_cinfo.clkin = cinfo->clkin;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301467 dsi->current_cinfo.fint = cinfo->fint;
1468 dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr;
1469 dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk =
Archit Taneja1bb47832011-02-24 14:17:30 +05301470 cinfo->dsi_pll_hsdiv_dispc_clk;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301471 dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk =
Archit Taneja1bb47832011-02-24 14:17:30 +05301472 cinfo->dsi_pll_hsdiv_dsi_clk;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001473
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301474 dsi->current_cinfo.regn = cinfo->regn;
1475 dsi->current_cinfo.regm = cinfo->regm;
1476 dsi->current_cinfo.regm_dispc = cinfo->regm_dispc;
1477 dsi->current_cinfo.regm_dsi = cinfo->regm_dsi;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001478
1479 DSSDBG("DSI Fint %ld\n", cinfo->fint);
1480
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001481 DSSDBG("clkin rate %ld\n", cinfo->clkin);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001482
1483 /* DSIPHY == CLKIN4DDR */
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001484 DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu = %lu\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001485 cinfo->regm,
1486 cinfo->regn,
1487 cinfo->clkin,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001488 cinfo->clkin4ddr);
1489
1490 DSSDBG("Data rate on 1 DSI lane %ld Mbps\n",
1491 cinfo->clkin4ddr / 1000 / 1000 / 2);
1492
1493 DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4);
1494
Archit Taneja1bb47832011-02-24 14:17:30 +05301495 DSSDBG("regm_dispc = %d, %s (%s) = %lu\n", cinfo->regm_dispc,
Archit Taneja89a35e52011-04-12 13:52:23 +05301496 dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC),
1497 dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC),
Archit Taneja1bb47832011-02-24 14:17:30 +05301498 cinfo->dsi_pll_hsdiv_dispc_clk);
1499 DSSDBG("regm_dsi = %d, %s (%s) = %lu\n", cinfo->regm_dsi,
Archit Taneja89a35e52011-04-12 13:52:23 +05301500 dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI),
1501 dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI),
Archit Taneja1bb47832011-02-24 14:17:30 +05301502 cinfo->dsi_pll_hsdiv_dsi_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001503
Taneja, Archit49641112011-03-14 23:28:23 -05001504 dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGN, &regn_start, &regn_end);
1505 dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM, &regm_start, &regm_end);
1506 dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DISPC, &regm_dispc_start,
1507 &regm_dispc_end);
1508 dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DSI, &regm_dsi_start,
1509 &regm_dsi_end);
1510
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301511 /* DSI_PLL_AUTOMODE = manual */
1512 REG_FLD_MOD(dsidev, DSI_PLL_CONTROL, 0, 0, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001513
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301514 l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001515 l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */
Taneja, Archit49641112011-03-14 23:28:23 -05001516 /* DSI_PLL_REGN */
1517 l = FLD_MOD(l, cinfo->regn - 1, regn_start, regn_end);
1518 /* DSI_PLL_REGM */
1519 l = FLD_MOD(l, cinfo->regm, regm_start, regm_end);
1520 /* DSI_CLOCK_DIV */
Archit Taneja1bb47832011-02-24 14:17:30 +05301521 l = FLD_MOD(l, cinfo->regm_dispc > 0 ? cinfo->regm_dispc - 1 : 0,
Taneja, Archit49641112011-03-14 23:28:23 -05001522 regm_dispc_start, regm_dispc_end);
1523 /* DSIPROTO_CLOCK_DIV */
Archit Taneja1bb47832011-02-24 14:17:30 +05301524 l = FLD_MOD(l, cinfo->regm_dsi > 0 ? cinfo->regm_dsi - 1 : 0,
Taneja, Archit49641112011-03-14 23:28:23 -05001525 regm_dsi_start, regm_dsi_end);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301526 dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION1, l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001527
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301528 BUG_ON(cinfo->fint < dsi->fint_min || cinfo->fint > dsi->fint_max);
Archit Taneja9613c022011-03-22 06:33:36 -05001529
1530 if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) {
1531 f = cinfo->fint < 1000000 ? 0x3 :
1532 cinfo->fint < 1250000 ? 0x4 :
1533 cinfo->fint < 1500000 ? 0x5 :
1534 cinfo->fint < 1750000 ? 0x6 :
1535 0x7;
1536 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001537
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301538 l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2);
Archit Taneja9613c022011-03-22 06:33:36 -05001539
1540 if (dss_has_feature(FEAT_DSI_PLL_FREQSEL))
1541 l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001542 l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
1543 l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */
1544 l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301545 dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001546
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301547 REG_FLD_MOD(dsidev, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001548
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301549 if (wait_for_bit_change(dsidev, DSI_PLL_GO, 0, 0) != 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001550 DSSERR("dsi pll go bit not going down.\n");
1551 r = -EIO;
1552 goto err;
1553 }
1554
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301555 if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001556 DSSERR("cannot lock PLL\n");
1557 r = -EIO;
1558 goto err;
1559 }
1560
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301561 dsi->pll_locked = 1;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001562
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301563 l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001564 l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */
1565 l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */
1566 l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */
1567 l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */
1568 l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */
1569 l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */
1570 l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
1571 l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */
1572 l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */
1573 l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */
1574 l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */
1575 l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */
1576 l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */
1577 l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301578 dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001579
1580 DSSDBG("PLL config done\n");
1581err:
1582 return r;
1583}
1584
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301585int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
1586 bool enable_hsdiv)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001587{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301588 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001589 int r = 0;
1590 enum dsi_pll_power_state pwstate;
1591
1592 DSSDBG("PLL init\n");
1593
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301594 if (dsi->vdds_dsi_reg == NULL) {
Tomi Valkeinenf2988ab2011-03-02 10:06:48 +02001595 struct regulator *vdds_dsi;
1596
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301597 vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi");
Tomi Valkeinenf2988ab2011-03-02 10:06:48 +02001598
1599 if (IS_ERR(vdds_dsi)) {
1600 DSSERR("can't get VDDS_DSI regulator\n");
1601 return PTR_ERR(vdds_dsi);
1602 }
1603
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301604 dsi->vdds_dsi_reg = vdds_dsi;
Tomi Valkeinenf2988ab2011-03-02 10:06:48 +02001605 }
Tomi Valkeinenf2988ab2011-03-02 10:06:48 +02001606
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301607 dsi_enable_pll_clock(dsidev, 1);
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001608 /*
1609 * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
1610 */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301611 dsi_enable_scp_clk(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001612
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301613 if (!dsi->vdds_dsi_enabled) {
1614 r = regulator_enable(dsi->vdds_dsi_reg);
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001615 if (r)
1616 goto err0;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301617 dsi->vdds_dsi_enabled = true;
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001618 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001619
1620 /* XXX PLL does not come out of reset without this... */
1621 dispc_pck_free_enable(1);
1622
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301623 if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001624 DSSERR("PLL not coming out of reset.\n");
1625 r = -ENODEV;
Ville Syrjälä481dfa02010-04-22 22:50:04 +02001626 dispc_pck_free_enable(0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001627 goto err1;
1628 }
1629
1630 /* XXX ... but if left on, we get problems when planes do not
1631 * fill the whole display. No idea about this */
1632 dispc_pck_free_enable(0);
1633
1634 if (enable_hsclk && enable_hsdiv)
1635 pwstate = DSI_PLL_POWER_ON_ALL;
1636 else if (enable_hsclk)
1637 pwstate = DSI_PLL_POWER_ON_HSCLK;
1638 else if (enable_hsdiv)
1639 pwstate = DSI_PLL_POWER_ON_DIV;
1640 else
1641 pwstate = DSI_PLL_POWER_OFF;
1642
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301643 r = dsi_pll_power(dsidev, pwstate);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001644
1645 if (r)
1646 goto err1;
1647
1648 DSSDBG("PLL init done\n");
1649
1650 return 0;
1651err1:
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301652 if (dsi->vdds_dsi_enabled) {
1653 regulator_disable(dsi->vdds_dsi_reg);
1654 dsi->vdds_dsi_enabled = false;
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001655 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001656err0:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301657 dsi_disable_scp_clk(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301658 dsi_enable_pll_clock(dsidev, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001659 return r;
1660}
1661
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301662void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001663{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301664 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1665
1666 dsi->pll_locked = 0;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301667 dsi_pll_power(dsidev, DSI_PLL_POWER_OFF);
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001668 if (disconnect_lanes) {
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301669 WARN_ON(!dsi->vdds_dsi_enabled);
1670 regulator_disable(dsi->vdds_dsi_reg);
1671 dsi->vdds_dsi_enabled = false;
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001672 }
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001673
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301674 dsi_disable_scp_clk(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301675 dsi_enable_pll_clock(dsidev, 0);
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001676
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001677 DSSDBG("PLL uninit done\n");
1678}
1679
Archit Taneja5a8b5722011-05-12 17:26:29 +05301680static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
1681 struct seq_file *s)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001682{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301683 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1684 struct dsi_clock_info *cinfo = &dsi->current_cinfo;
Archit Taneja89a35e52011-04-12 13:52:23 +05301685 enum omap_dss_clk_source dispc_clk_src, dsi_clk_src;
Archit Taneja5a8b5722011-05-12 17:26:29 +05301686 int dsi_module = dsi_get_dsidev_id(dsidev);
Archit Taneja067a57e2011-03-02 11:57:25 +05301687
1688 dispc_clk_src = dss_get_dispc_clk_source();
Archit Taneja5a8b5722011-05-12 17:26:29 +05301689 dsi_clk_src = dss_get_dsi_clk_source(dsi_module);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001690
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001691 if (dsi_runtime_get(dsidev))
1692 return;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001693
Archit Taneja5a8b5722011-05-12 17:26:29 +05301694 seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001695
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02001696 seq_printf(s, "dsi pll clkin\t%lu\n", cinfo->clkin);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001697
1698 seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn);
1699
1700 seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n",
1701 cinfo->clkin4ddr, cinfo->regm);
1702
Archit Taneja84309f12011-12-12 11:47:41 +05301703 seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16luregm_dispc %u\t(%s)\n",
1704 dss_feat_get_clk_source_name(dsi_module == 0 ?
1705 OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
1706 OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC),
Archit Taneja1bb47832011-02-24 14:17:30 +05301707 cinfo->dsi_pll_hsdiv_dispc_clk,
1708 cinfo->regm_dispc,
Archit Taneja89a35e52011-04-12 13:52:23 +05301709 dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ?
Tomi Valkeinen63cf28a2010-02-23 17:40:00 +02001710 "off" : "on");
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001711
Archit Taneja84309f12011-12-12 11:47:41 +05301712 seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16luregm_dsi %u\t(%s)\n",
1713 dss_feat_get_clk_source_name(dsi_module == 0 ?
1714 OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
1715 OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI),
Archit Taneja1bb47832011-02-24 14:17:30 +05301716 cinfo->dsi_pll_hsdiv_dsi_clk,
1717 cinfo->regm_dsi,
Archit Taneja89a35e52011-04-12 13:52:23 +05301718 dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ?
Tomi Valkeinen63cf28a2010-02-23 17:40:00 +02001719 "off" : "on");
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001720
Archit Taneja5a8b5722011-05-12 17:26:29 +05301721 seq_printf(s, "- DSI%d -\n", dsi_module + 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001722
Archit Taneja067a57e2011-03-02 11:57:25 +05301723 seq_printf(s, "dsi fclk source = %s (%s)\n",
1724 dss_get_generic_clk_source_name(dsi_clk_src),
1725 dss_feat_get_clk_source_name(dsi_clk_src));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001726
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301727 seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001728
1729 seq_printf(s, "DDR_CLK\t\t%lu\n",
1730 cinfo->clkin4ddr / 4);
1731
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301732 seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001733
1734 seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk);
1735
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001736 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001737}
1738
Archit Taneja5a8b5722011-05-12 17:26:29 +05301739void dsi_dump_clocks(struct seq_file *s)
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001740{
Archit Taneja5a8b5722011-05-12 17:26:29 +05301741 struct platform_device *dsidev;
1742 int i;
1743
1744 for (i = 0; i < MAX_NUM_DSI; i++) {
1745 dsidev = dsi_get_dsidev_from_id(i);
1746 if (dsidev)
1747 dsi_dump_dsidev_clocks(dsidev, s);
1748 }
1749}
1750
1751#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
1752static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
1753 struct seq_file *s)
1754{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301755 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001756 unsigned long flags;
1757 struct dsi_irq_stats stats;
Archit Taneja5a8b5722011-05-12 17:26:29 +05301758 int dsi_module = dsi_get_dsidev_id(dsidev);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001759
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301760 spin_lock_irqsave(&dsi->irq_stats_lock, flags);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001761
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301762 stats = dsi->irq_stats;
1763 memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
1764 dsi->irq_stats.last_reset = jiffies;
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001765
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301766 spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001767
1768 seq_printf(s, "period %u ms\n",
1769 jiffies_to_msecs(jiffies - stats.last_reset));
1770
1771 seq_printf(s, "irqs %d\n", stats.irq_count);
1772#define PIS(x) \
1773 seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]);
1774
Archit Taneja5a8b5722011-05-12 17:26:29 +05301775 seq_printf(s, "-- DSI%d interrupts --\n", dsi_module + 1);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001776 PIS(VC0);
1777 PIS(VC1);
1778 PIS(VC2);
1779 PIS(VC3);
1780 PIS(WAKEUP);
1781 PIS(RESYNC);
1782 PIS(PLL_LOCK);
1783 PIS(PLL_UNLOCK);
1784 PIS(PLL_RECALL);
1785 PIS(COMPLEXIO_ERR);
1786 PIS(HS_TX_TIMEOUT);
1787 PIS(LP_RX_TIMEOUT);
1788 PIS(TE_TRIGGER);
1789 PIS(ACK_TRIGGER);
1790 PIS(SYNC_LOST);
1791 PIS(LDO_POWER_GOOD);
1792 PIS(TA_TIMEOUT);
1793#undef PIS
1794
1795#define PIS(x) \
1796 seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
1797 stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
1798 stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
1799 stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
1800 stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
1801
1802 seq_printf(s, "-- VC interrupts --\n");
1803 PIS(CS);
1804 PIS(ECC_CORR);
1805 PIS(PACKET_SENT);
1806 PIS(FIFO_TX_OVF);
1807 PIS(FIFO_RX_OVF);
1808 PIS(BTA);
1809 PIS(ECC_NO_CORR);
1810 PIS(FIFO_TX_UDF);
1811 PIS(PP_BUSY_CHANGE);
1812#undef PIS
1813
1814#define PIS(x) \
1815 seq_printf(s, "%-20s %10d\n", #x, \
1816 stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
1817
1818 seq_printf(s, "-- CIO interrupts --\n");
1819 PIS(ERRSYNCESC1);
1820 PIS(ERRSYNCESC2);
1821 PIS(ERRSYNCESC3);
1822 PIS(ERRESC1);
1823 PIS(ERRESC2);
1824 PIS(ERRESC3);
1825 PIS(ERRCONTROL1);
1826 PIS(ERRCONTROL2);
1827 PIS(ERRCONTROL3);
1828 PIS(STATEULPS1);
1829 PIS(STATEULPS2);
1830 PIS(STATEULPS3);
1831 PIS(ERRCONTENTIONLP0_1);
1832 PIS(ERRCONTENTIONLP1_1);
1833 PIS(ERRCONTENTIONLP0_2);
1834 PIS(ERRCONTENTIONLP1_2);
1835 PIS(ERRCONTENTIONLP0_3);
1836 PIS(ERRCONTENTIONLP1_3);
1837 PIS(ULPSACTIVENOT_ALL0);
1838 PIS(ULPSACTIVENOT_ALL1);
1839#undef PIS
1840}
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001841
Archit Taneja5a8b5722011-05-12 17:26:29 +05301842static void dsi1_dump_irqs(struct seq_file *s)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001843{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301844 struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
1845
Archit Taneja5a8b5722011-05-12 17:26:29 +05301846 dsi_dump_dsidev_irqs(dsidev, s);
1847}
1848
1849static void dsi2_dump_irqs(struct seq_file *s)
1850{
1851 struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
1852
1853 dsi_dump_dsidev_irqs(dsidev, s);
1854}
1855
1856void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir,
1857 const struct file_operations *debug_fops)
1858{
1859 struct platform_device *dsidev;
1860
1861 dsidev = dsi_get_dsidev_from_id(0);
1862 if (dsidev)
1863 debugfs_create_file("dsi1_irqs", S_IRUGO, debugfs_dir,
1864 &dsi1_dump_irqs, debug_fops);
1865
1866 dsidev = dsi_get_dsidev_from_id(1);
1867 if (dsidev)
1868 debugfs_create_file("dsi2_irqs", S_IRUGO, debugfs_dir,
1869 &dsi2_dump_irqs, debug_fops);
1870}
1871#endif
1872
1873static void dsi_dump_dsidev_regs(struct platform_device *dsidev,
1874 struct seq_file *s)
1875{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301876#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r))
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001877
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001878 if (dsi_runtime_get(dsidev))
1879 return;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301880 dsi_enable_scp_clk(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001881
1882 DUMPREG(DSI_REVISION);
1883 DUMPREG(DSI_SYSCONFIG);
1884 DUMPREG(DSI_SYSSTATUS);
1885 DUMPREG(DSI_IRQSTATUS);
1886 DUMPREG(DSI_IRQENABLE);
1887 DUMPREG(DSI_CTRL);
1888 DUMPREG(DSI_COMPLEXIO_CFG1);
1889 DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
1890 DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
1891 DUMPREG(DSI_CLK_CTRL);
1892 DUMPREG(DSI_TIMING1);
1893 DUMPREG(DSI_TIMING2);
1894 DUMPREG(DSI_VM_TIMING1);
1895 DUMPREG(DSI_VM_TIMING2);
1896 DUMPREG(DSI_VM_TIMING3);
1897 DUMPREG(DSI_CLK_TIMING);
1898 DUMPREG(DSI_TX_FIFO_VC_SIZE);
1899 DUMPREG(DSI_RX_FIFO_VC_SIZE);
1900 DUMPREG(DSI_COMPLEXIO_CFG2);
1901 DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
1902 DUMPREG(DSI_VM_TIMING4);
1903 DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
1904 DUMPREG(DSI_VM_TIMING5);
1905 DUMPREG(DSI_VM_TIMING6);
1906 DUMPREG(DSI_VM_TIMING7);
1907 DUMPREG(DSI_STOPCLK_TIMING);
1908
1909 DUMPREG(DSI_VC_CTRL(0));
1910 DUMPREG(DSI_VC_TE(0));
1911 DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
1912 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
1913 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
1914 DUMPREG(DSI_VC_IRQSTATUS(0));
1915 DUMPREG(DSI_VC_IRQENABLE(0));
1916
1917 DUMPREG(DSI_VC_CTRL(1));
1918 DUMPREG(DSI_VC_TE(1));
1919 DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
1920 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
1921 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
1922 DUMPREG(DSI_VC_IRQSTATUS(1));
1923 DUMPREG(DSI_VC_IRQENABLE(1));
1924
1925 DUMPREG(DSI_VC_CTRL(2));
1926 DUMPREG(DSI_VC_TE(2));
1927 DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
1928 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
1929 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
1930 DUMPREG(DSI_VC_IRQSTATUS(2));
1931 DUMPREG(DSI_VC_IRQENABLE(2));
1932
1933 DUMPREG(DSI_VC_CTRL(3));
1934 DUMPREG(DSI_VC_TE(3));
1935 DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
1936 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
1937 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
1938 DUMPREG(DSI_VC_IRQSTATUS(3));
1939 DUMPREG(DSI_VC_IRQENABLE(3));
1940
1941 DUMPREG(DSI_DSIPHY_CFG0);
1942 DUMPREG(DSI_DSIPHY_CFG1);
1943 DUMPREG(DSI_DSIPHY_CFG2);
1944 DUMPREG(DSI_DSIPHY_CFG5);
1945
1946 DUMPREG(DSI_PLL_CONTROL);
1947 DUMPREG(DSI_PLL_STATUS);
1948 DUMPREG(DSI_PLL_GO);
1949 DUMPREG(DSI_PLL_CONFIGURATION1);
1950 DUMPREG(DSI_PLL_CONFIGURATION2);
1951
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301952 dsi_disable_scp_clk(dsidev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001953 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001954#undef DUMPREG
1955}
1956
Archit Taneja5a8b5722011-05-12 17:26:29 +05301957static void dsi1_dump_regs(struct seq_file *s)
1958{
1959 struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
1960
1961 dsi_dump_dsidev_regs(dsidev, s);
1962}
1963
1964static void dsi2_dump_regs(struct seq_file *s)
1965{
1966 struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
1967
1968 dsi_dump_dsidev_regs(dsidev, s);
1969}
1970
1971void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir,
1972 const struct file_operations *debug_fops)
1973{
1974 struct platform_device *dsidev;
1975
1976 dsidev = dsi_get_dsidev_from_id(0);
1977 if (dsidev)
1978 debugfs_create_file("dsi1_regs", S_IRUGO, debugfs_dir,
1979 &dsi1_dump_regs, debug_fops);
1980
1981 dsidev = dsi_get_dsidev_from_id(1);
1982 if (dsidev)
1983 debugfs_create_file("dsi2_regs", S_IRUGO, debugfs_dir,
1984 &dsi2_dump_regs, debug_fops);
1985}
Tomi Valkeinencc5c1852010-10-06 15:18:13 +03001986enum dsi_cio_power_state {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001987 DSI_COMPLEXIO_POWER_OFF = 0x0,
1988 DSI_COMPLEXIO_POWER_ON = 0x1,
1989 DSI_COMPLEXIO_POWER_ULPS = 0x2,
1990};
1991
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301992static int dsi_cio_power(struct platform_device *dsidev,
1993 enum dsi_cio_power_state state)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001994{
1995 int t = 0;
1996
1997 /* PWR_CMD */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301998 REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001999
2000 /* PWR_STATUS */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302001 while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1),
2002 26, 25) != state) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02002003 if (++t > 1000) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002004 DSSERR("failed to set complexio power state to "
2005 "%d\n", state);
2006 return -ENODEV;
2007 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02002008 udelay(1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002009 }
2010
2011 return 0;
2012}
2013
Archit Taneja0c65622b2011-05-16 15:17:09 +05302014static unsigned dsi_get_line_buf_size(struct platform_device *dsidev)
2015{
2016 int val;
2017
2018 /* line buffer on OMAP3 is 1024 x 24bits */
2019 /* XXX: for some reason using full buffer size causes
2020 * considerable TX slowdown with update sizes that fill the
2021 * whole buffer */
2022 if (!dss_has_feature(FEAT_DSI_GNQ))
2023 return 1023 * 3;
2024
2025 val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */
2026
2027 switch (val) {
2028 case 1:
2029 return 512 * 3; /* 512x24 bits */
2030 case 2:
2031 return 682 * 3; /* 682x24 bits */
2032 case 3:
2033 return 853 * 3; /* 853x24 bits */
2034 case 4:
2035 return 1024 * 3; /* 1024x24 bits */
2036 case 5:
2037 return 1194 * 3; /* 1194x24 bits */
2038 case 6:
2039 return 1365 * 3; /* 1365x24 bits */
2040 default:
2041 BUG();
2042 }
2043}
2044
Tomi Valkeinen48368392011-10-13 11:22:39 +03002045static int dsi_set_lane_config(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002046{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302047 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen48368392011-10-13 11:22:39 +03002048 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2049 static const u8 offsets[] = { 0, 4, 8, 12, 16 };
2050 static const enum dsi_lane_function functions[] = {
2051 DSI_LANE_CLK,
2052 DSI_LANE_DATA1,
2053 DSI_LANE_DATA2,
2054 DSI_LANE_DATA3,
2055 DSI_LANE_DATA4,
2056 };
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002057 u32 r;
Tomi Valkeinen48368392011-10-13 11:22:39 +03002058 int i;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002059
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302060 r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
Archit Taneja75d72472011-05-16 15:17:08 +05302061
Tomi Valkeinen48368392011-10-13 11:22:39 +03002062 for (i = 0; i < dsi->num_lanes_used; ++i) {
2063 unsigned offset = offsets[i];
2064 unsigned polarity, lane_number;
2065 unsigned t;
Archit Taneja75d72472011-05-16 15:17:08 +05302066
Tomi Valkeinen48368392011-10-13 11:22:39 +03002067 for (t = 0; t < dsi->num_lanes_supported; ++t)
2068 if (dsi->lanes[t].function == functions[i])
2069 break;
2070
2071 if (t == dsi->num_lanes_supported)
2072 return -EINVAL;
2073
2074 lane_number = t;
2075 polarity = dsi->lanes[t].polarity;
2076
2077 r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
2078 r = FLD_MOD(r, polarity, offset + 3, offset + 3);
Archit Taneja75d72472011-05-16 15:17:08 +05302079 }
Tomi Valkeinen48368392011-10-13 11:22:39 +03002080
2081 /* clear the unused lanes */
2082 for (; i < dsi->num_lanes_supported; ++i) {
2083 unsigned offset = offsets[i];
2084
2085 r = FLD_MOD(r, 0, offset + 2, offset);
2086 r = FLD_MOD(r, 0, offset + 3, offset + 3);
2087 }
2088
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302089 dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002090
Tomi Valkeinen48368392011-10-13 11:22:39 +03002091 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002092}
2093
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302094static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002095{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302096 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2097
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002098 /* convert time in ns to ddr ticks, rounding up */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302099 unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002100 return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
2101}
2102
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302103static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002104{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302105 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2106
2107 unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002108 return ddr * 1000 * 1000 / (ddr_clk / 1000);
2109}
2110
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302111static void dsi_cio_timings(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002112{
2113 u32 r;
2114 u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
2115 u32 tlpx_half, tclk_trail, tclk_zero;
2116 u32 tclk_prepare;
2117
2118 /* calculate timings */
2119
2120 /* 1 * DDR_CLK = 2 * UI */
2121
2122 /* min 40ns + 4*UI max 85ns + 6*UI */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302123 ths_prepare = ns2ddr(dsidev, 70) + 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002124
2125 /* min 145ns + 10*UI */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302126 ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002127
2128 /* min max(8*UI, 60ns+4*UI) */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302129 ths_trail = ns2ddr(dsidev, 60) + 5;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002130
2131 /* min 100ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302132 ths_exit = ns2ddr(dsidev, 145);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002133
2134 /* tlpx min 50n */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302135 tlpx_half = ns2ddr(dsidev, 25);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002136
2137 /* min 60ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302138 tclk_trail = ns2ddr(dsidev, 60) + 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002139
2140 /* min 38ns, max 95ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302141 tclk_prepare = ns2ddr(dsidev, 65);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002142
2143 /* min tclk-prepare + tclk-zero = 300ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302144 tclk_zero = ns2ddr(dsidev, 260);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002145
2146 DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302147 ths_prepare, ddr2ns(dsidev, ths_prepare),
2148 ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002149 DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302150 ths_trail, ddr2ns(dsidev, ths_trail),
2151 ths_exit, ddr2ns(dsidev, ths_exit));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002152
2153 DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
2154 "tclk_zero %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302155 tlpx_half, ddr2ns(dsidev, tlpx_half),
2156 tclk_trail, ddr2ns(dsidev, tclk_trail),
2157 tclk_zero, ddr2ns(dsidev, tclk_zero));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002158 DSSDBG("tclk_prepare %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302159 tclk_prepare, ddr2ns(dsidev, tclk_prepare));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002160
2161 /* program timings */
2162
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302163 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002164 r = FLD_MOD(r, ths_prepare, 31, 24);
2165 r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
2166 r = FLD_MOD(r, ths_trail, 15, 8);
2167 r = FLD_MOD(r, ths_exit, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302168 dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002169
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302170 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002171 r = FLD_MOD(r, tlpx_half, 22, 16);
2172 r = FLD_MOD(r, tclk_trail, 15, 8);
2173 r = FLD_MOD(r, tclk_zero, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302174 dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002175
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302176 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002177 r = FLD_MOD(r, tclk_prepare, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302178 dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002179}
2180
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002181/* lane masks have lane 0 at lsb. mask_p for positive lines, n for negative */
Tomi Valkeinencc5c1852010-10-06 15:18:13 +03002182static void dsi_cio_enable_lane_override(struct omap_dss_device *dssdev,
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002183 unsigned mask_p, unsigned mask_n)
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002184{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302185 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Taneja75d72472011-05-16 15:17:08 +05302186 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002187 int i;
2188 u32 l;
Tomi Valkeinend9820852011-10-12 15:05:59 +03002189 u8 lptxscp_start = dsi->num_lanes_supported == 3 ? 22 : 26;
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002190
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002191 l = 0;
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002192
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002193 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2194 unsigned p = dsi->lanes[i].polarity;
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002195
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002196 if (mask_p & (1 << i))
2197 l |= 1 << (i * 2 + (p ? 0 : 1));
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002198
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002199 if (mask_n & (1 << i))
2200 l |= 1 << (i * 2 + (p ? 1 : 0));
2201 }
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002202
2203 /*
2204 * Bits in REGLPTXSCPDAT4TO0DXDY:
2205 * 17: DY0 18: DX0
2206 * 19: DY1 20: DX1
2207 * 21: DY2 22: DX2
Archit Taneja75d72472011-05-16 15:17:08 +05302208 * 23: DY3 24: DX3
2209 * 25: DY4 26: DX4
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002210 */
2211
2212 /* Set the lane override configuration */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302213
2214 /* REGLPTXSCPDAT4TO0DXDY */
Archit Taneja75d72472011-05-16 15:17:08 +05302215 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17);
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002216
2217 /* Enable lane override */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302218
2219 /* ENLPTXSCPDAT */
2220 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27);
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002221}
2222
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302223static void dsi_cio_disable_lane_override(struct platform_device *dsidev)
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002224{
2225 /* Disable lane override */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302226 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002227 /* Reset the lane override configuration */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302228 /* REGLPTXSCPDAT4TO0DXDY */
2229 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17);
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002230}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002231
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002232static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev)
2233{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302234 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002235 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2236 int t, i;
2237 bool in_use[DSI_MAX_NR_LANES];
2238 static const u8 offsets_old[] = { 28, 27, 26 };
2239 static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
2240 const u8 *offsets;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002241
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002242 if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC))
2243 offsets = offsets_old;
2244 else
2245 offsets = offsets_new;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002246
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002247 for (i = 0; i < dsi->num_lanes_supported; ++i)
2248 in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002249
2250 t = 100000;
2251 while (true) {
2252 u32 l;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002253 int ok;
2254
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302255 l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002256
2257 ok = 0;
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002258 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2259 if (!in_use[i] || (l & (1 << offsets[i])))
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002260 ok++;
2261 }
2262
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002263 if (ok == dsi->num_lanes_supported)
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002264 break;
2265
2266 if (--t == 0) {
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002267 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2268 if (!in_use[i] || (l & (1 << offsets[i])))
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002269 continue;
2270
2271 DSSERR("CIO TXCLKESC%d domain not coming " \
2272 "out of reset\n", i);
2273 }
2274 return -EIO;
2275 }
2276 }
2277
2278 return 0;
2279}
2280
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002281/* return bitmask of enabled lanes, lane0 being the lsb */
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002282static unsigned dsi_get_lane_mask(struct omap_dss_device *dssdev)
2283{
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002284 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2285 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2286 unsigned mask = 0;
2287 int i;
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002288
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002289 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2290 if (dsi->lanes[i].function != DSI_LANE_UNUSED)
2291 mask |= 1 << i;
2292 }
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002293
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002294 return mask;
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002295}
2296
Tomi Valkeinencc5c1852010-10-06 15:18:13 +03002297static int dsi_cio_init(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002298{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302299 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302300 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002301 int r;
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002302 u32 l;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002303
Tomi Valkeinencc5c1852010-10-06 15:18:13 +03002304 DSSDBGF();
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002305
Tomi Valkeinen00928ea2012-02-20 11:50:06 +02002306 r = dss_dsi_enable_pads(dsi_get_dsidev_id(dsidev), dsi_get_lane_mask(dssdev));
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002307 if (r)
2308 return r;
Tomi Valkeinend1f58572010-07-30 11:57:57 +03002309
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302310 dsi_enable_scp_clk(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002311
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002312 /* A dummy read using the SCP interface to any DSIPHY register is
2313 * required after DSIPHY reset to complete the reset of the DSI complex
2314 * I/O. */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302315 dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002316
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302317 if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) {
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002318 DSSERR("CIO SCP Clock domain not coming out of reset.\n");
2319 r = -EIO;
2320 goto err_scp_clk_dom;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002321 }
2322
Tomi Valkeinen48368392011-10-13 11:22:39 +03002323 r = dsi_set_lane_config(dssdev);
2324 if (r)
2325 goto err_scp_clk_dom;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002326
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002327 /* set TX STOP MODE timer to maximum for this operation */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302328 l = dsi_read_reg(dsidev, DSI_TIMING1);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002329 l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
2330 l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */
2331 l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */
2332 l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302333 dsi_write_reg(dsidev, DSI_TIMING1, l);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002334
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302335 if (dsi->ulps_enabled) {
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002336 unsigned mask_p;
2337 int i;
Archit Taneja75d72472011-05-16 15:17:08 +05302338
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002339 DSSDBG("manual ulps exit\n");
2340
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002341 /* ULPS is exited by Mark-1 state for 1ms, followed by
2342 * stop state. DSS HW cannot do this via the normal
2343 * ULPS exit sequence, as after reset the DSS HW thinks
2344 * that we are not in ULPS mode, and refuses to send the
2345 * sequence. So we need to send the ULPS exit sequence
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002346 * manually by setting positive lines high and negative lines
2347 * low for 1ms.
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002348 */
2349
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002350 mask_p = 0;
Archit Taneja75d72472011-05-16 15:17:08 +05302351
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002352 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2353 if (dsi->lanes[i].function == DSI_LANE_UNUSED)
2354 continue;
2355 mask_p |= 1 << i;
2356 }
Archit Taneja75d72472011-05-16 15:17:08 +05302357
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002358 dsi_cio_enable_lane_override(dssdev, mask_p, 0);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002359 }
2360
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302361 r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002362 if (r)
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002363 goto err_cio_pwr;
2364
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302365 if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002366 DSSERR("CIO PWR clock domain not coming out of reset.\n");
2367 r = -ENODEV;
2368 goto err_cio_pwr_dom;
2369 }
2370
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302371 dsi_if_enable(dsidev, true);
2372 dsi_if_enable(dsidev, false);
2373 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002374
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002375 r = dsi_cio_wait_tx_clk_esc_reset(dssdev);
2376 if (r)
2377 goto err_tx_clk_esc_rst;
2378
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302379 if (dsi->ulps_enabled) {
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002380 /* Keep Mark-1 state for 1ms (as per DSI spec) */
2381 ktime_t wait = ns_to_ktime(1000 * 1000);
2382 set_current_state(TASK_UNINTERRUPTIBLE);
2383 schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
2384
2385 /* Disable the override. The lanes should be set to Mark-11
2386 * state by the HW */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302387 dsi_cio_disable_lane_override(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002388 }
2389
2390 /* FORCE_TX_STOP_MODE_IO */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302391 REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002392
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302393 dsi_cio_timings(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002394
Archit Taneja8af6ff02011-09-05 16:48:27 +05302395 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
2396 /* DDR_CLK_ALWAYS_ON */
2397 REG_FLD_MOD(dsidev, DSI_CLK_CTRL,
2398 dssdev->panel.dsi_vm_data.ddr_clk_always_on, 13, 13);
2399 }
2400
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302401 dsi->ulps_enabled = false;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002402
2403 DSSDBG("CIO init done\n");
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002404
2405 return 0;
2406
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002407err_tx_clk_esc_rst:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302408 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002409err_cio_pwr_dom:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302410 dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002411err_cio_pwr:
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302412 if (dsi->ulps_enabled)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302413 dsi_cio_disable_lane_override(dsidev);
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002414err_scp_clk_dom:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302415 dsi_disable_scp_clk(dsidev);
Tomi Valkeinen00928ea2012-02-20 11:50:06 +02002416 dss_dsi_disable_pads(dsi_get_dsidev_id(dsidev), dsi_get_lane_mask(dssdev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002417 return r;
2418}
2419
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002420static void dsi_cio_uninit(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002421{
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002422 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302423
Archit Taneja8af6ff02011-09-05 16:48:27 +05302424 /* DDR_CLK_ALWAYS_ON */
2425 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
2426
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302427 dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
2428 dsi_disable_scp_clk(dsidev);
Tomi Valkeinen00928ea2012-02-20 11:50:06 +02002429 dss_dsi_disable_pads(dsi_get_dsidev_id(dsidev), dsi_get_lane_mask(dssdev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002430}
2431
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302432static void dsi_config_tx_fifo(struct platform_device *dsidev,
2433 enum fifo_size size1, enum fifo_size size2,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002434 enum fifo_size size3, enum fifo_size size4)
2435{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302436 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002437 u32 r = 0;
2438 int add = 0;
2439 int i;
2440
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302441 dsi->vc[0].fifo_size = size1;
2442 dsi->vc[1].fifo_size = size2;
2443 dsi->vc[2].fifo_size = size3;
2444 dsi->vc[3].fifo_size = size4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002445
2446 for (i = 0; i < 4; i++) {
2447 u8 v;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302448 int size = dsi->vc[i].fifo_size;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002449
2450 if (add + size > 4) {
2451 DSSERR("Illegal FIFO configuration\n");
2452 BUG();
2453 }
2454
2455 v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
2456 r |= v << (8 * i);
2457 /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
2458 add += size;
2459 }
2460
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302461 dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002462}
2463
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302464static void dsi_config_rx_fifo(struct platform_device *dsidev,
2465 enum fifo_size size1, enum fifo_size size2,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002466 enum fifo_size size3, enum fifo_size size4)
2467{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302468 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002469 u32 r = 0;
2470 int add = 0;
2471 int i;
2472
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302473 dsi->vc[0].fifo_size = size1;
2474 dsi->vc[1].fifo_size = size2;
2475 dsi->vc[2].fifo_size = size3;
2476 dsi->vc[3].fifo_size = size4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002477
2478 for (i = 0; i < 4; i++) {
2479 u8 v;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302480 int size = dsi->vc[i].fifo_size;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002481
2482 if (add + size > 4) {
2483 DSSERR("Illegal FIFO configuration\n");
2484 BUG();
2485 }
2486
2487 v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
2488 r |= v << (8 * i);
2489 /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
2490 add += size;
2491 }
2492
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302493 dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002494}
2495
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302496static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002497{
2498 u32 r;
2499
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302500 r = dsi_read_reg(dsidev, DSI_TIMING1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002501 r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302502 dsi_write_reg(dsidev, DSI_TIMING1, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002503
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302504 if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002505 DSSERR("TX_STOP bit not going down\n");
2506 return -EIO;
2507 }
2508
2509 return 0;
2510}
2511
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302512static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002513{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302514 return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002515}
2516
2517static void dsi_packet_sent_handler_vp(void *data, u32 mask)
2518{
Archit Taneja2e868db2011-05-12 17:26:28 +05302519 struct dsi_packet_sent_handler_data *vp_data =
2520 (struct dsi_packet_sent_handler_data *) data;
2521 struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302522 const int channel = dsi->update_channel;
2523 u8 bit = dsi->te_enabled ? 30 : 31;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002524
Archit Taneja2e868db2011-05-12 17:26:28 +05302525 if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0)
2526 complete(vp_data->completion);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002527}
2528
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302529static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002530{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302531 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja2e868db2011-05-12 17:26:28 +05302532 DECLARE_COMPLETION_ONSTACK(completion);
2533 struct dsi_packet_sent_handler_data vp_data = { dsidev, &completion };
Archit Tanejacf398fb2011-03-23 09:59:34 +00002534 int r = 0;
2535 u8 bit;
2536
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302537 bit = dsi->te_enabled ? 30 : 31;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002538
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302539 r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
Archit Taneja2e868db2011-05-12 17:26:28 +05302540 &vp_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002541 if (r)
2542 goto err0;
2543
2544 /* Wait for completion only if TE_EN/TE_START is still set */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302545 if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) {
Archit Tanejacf398fb2011-03-23 09:59:34 +00002546 if (wait_for_completion_timeout(&completion,
2547 msecs_to_jiffies(10)) == 0) {
2548 DSSERR("Failed to complete previous frame transfer\n");
2549 r = -EIO;
2550 goto err1;
2551 }
2552 }
2553
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302554 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
Archit Taneja2e868db2011-05-12 17:26:28 +05302555 &vp_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002556
2557 return 0;
2558err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302559 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
Archit Taneja2e868db2011-05-12 17:26:28 +05302560 &vp_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002561err0:
2562 return r;
2563}
2564
2565static void dsi_packet_sent_handler_l4(void *data, u32 mask)
2566{
Archit Taneja2e868db2011-05-12 17:26:28 +05302567 struct dsi_packet_sent_handler_data *l4_data =
2568 (struct dsi_packet_sent_handler_data *) data;
2569 struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302570 const int channel = dsi->update_channel;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002571
Archit Taneja2e868db2011-05-12 17:26:28 +05302572 if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0)
2573 complete(l4_data->completion);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002574}
2575
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302576static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002577{
Archit Taneja2e868db2011-05-12 17:26:28 +05302578 DECLARE_COMPLETION_ONSTACK(completion);
2579 struct dsi_packet_sent_handler_data l4_data = { dsidev, &completion };
Archit Tanejacf398fb2011-03-23 09:59:34 +00002580 int r = 0;
2581
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302582 r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
Archit Taneja2e868db2011-05-12 17:26:28 +05302583 &l4_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002584 if (r)
2585 goto err0;
2586
2587 /* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302588 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) {
Archit Tanejacf398fb2011-03-23 09:59:34 +00002589 if (wait_for_completion_timeout(&completion,
2590 msecs_to_jiffies(10)) == 0) {
2591 DSSERR("Failed to complete previous l4 transfer\n");
2592 r = -EIO;
2593 goto err1;
2594 }
2595 }
2596
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302597 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
Archit Taneja2e868db2011-05-12 17:26:28 +05302598 &l4_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002599
2600 return 0;
2601err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302602 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
Archit Taneja2e868db2011-05-12 17:26:28 +05302603 &l4_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002604err0:
2605 return r;
2606}
2607
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302608static int dsi_sync_vc(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002609{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302610 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2611
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302612 WARN_ON(!dsi_bus_is_locked(dsidev));
Archit Tanejacf398fb2011-03-23 09:59:34 +00002613
2614 WARN_ON(in_interrupt());
2615
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302616 if (!dsi_vc_is_enabled(dsidev, channel))
Archit Tanejacf398fb2011-03-23 09:59:34 +00002617 return 0;
2618
Archit Tanejad6049142011-08-22 11:58:08 +05302619 switch (dsi->vc[channel].source) {
2620 case DSI_VC_SOURCE_VP:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302621 return dsi_sync_vc_vp(dsidev, channel);
Archit Tanejad6049142011-08-22 11:58:08 +05302622 case DSI_VC_SOURCE_L4:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302623 return dsi_sync_vc_l4(dsidev, channel);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002624 default:
2625 BUG();
2626 }
2627}
2628
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302629static int dsi_vc_enable(struct platform_device *dsidev, int channel,
2630 bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002631{
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02002632 DSSDBG("dsi_vc_enable channel %d, enable %d\n",
2633 channel, enable);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002634
2635 enable = enable ? 1 : 0;
2636
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302637 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002638
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302639 if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel),
2640 0, enable) != enable) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002641 DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
2642 return -EIO;
2643 }
2644
2645 return 0;
2646}
2647
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302648static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002649{
2650 u32 r;
2651
2652 DSSDBGF("%d", channel);
2653
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302654 r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002655
2656 if (FLD_GET(r, 15, 15)) /* VC_BUSY */
2657 DSSERR("VC(%d) busy when trying to configure it!\n",
2658 channel);
2659
2660 r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
2661 r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
2662 r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
2663 r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
2664 r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
2665 r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
2666 r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
Archit Taneja9613c022011-03-22 06:33:36 -05002667 if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH))
2668 r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002669
2670 r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
2671 r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
2672
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302673 dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002674}
2675
Archit Tanejad6049142011-08-22 11:58:08 +05302676static int dsi_vc_config_source(struct platform_device *dsidev, int channel,
2677 enum dsi_vc_source source)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002678{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302679 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2680
Archit Tanejad6049142011-08-22 11:58:08 +05302681 if (dsi->vc[channel].source == source)
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002682 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002683
2684 DSSDBGF("%d", channel);
2685
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302686 dsi_sync_vc(dsidev, channel);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002687
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302688 dsi_vc_enable(dsidev, channel, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002689
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002690 /* VC_BUSY */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302691 if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002692 DSSERR("vc(%d) busy when trying to config for VP\n", channel);
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002693 return -EIO;
2694 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002695
Archit Tanejad6049142011-08-22 11:58:08 +05302696 /* SOURCE, 0 = L4, 1 = video port */
2697 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002698
Archit Taneja9613c022011-03-22 06:33:36 -05002699 /* DCS_CMD_ENABLE */
Archit Tanejad6049142011-08-22 11:58:08 +05302700 if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
2701 bool enable = source == DSI_VC_SOURCE_VP;
2702 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30);
2703 }
Archit Taneja9613c022011-03-22 06:33:36 -05002704
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302705 dsi_vc_enable(dsidev, channel, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002706
Archit Tanejad6049142011-08-22 11:58:08 +05302707 dsi->vc[channel].source = source;
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002708
2709 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002710}
2711
Archit Taneja1ffefe72011-05-12 17:26:24 +05302712void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
2713 bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002714{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302715 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2716
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002717 DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
2718
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302719 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen61140c92010-01-12 16:00:30 +02002720
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302721 dsi_vc_enable(dsidev, channel, 0);
2722 dsi_if_enable(dsidev, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002723
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302724 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002725
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302726 dsi_vc_enable(dsidev, channel, 1);
2727 dsi_if_enable(dsidev, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002728
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302729 dsi_force_tx_stop_mode_io(dsidev);
Archit Taneja8af6ff02011-09-05 16:48:27 +05302730
2731 /* start the DDR clock by sending a NULL packet */
2732 if (dssdev->panel.dsi_vm_data.ddr_clk_always_on && enable)
2733 dsi_vc_send_null(dssdev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002734}
Tomi Valkeinen61140c92010-01-12 16:00:30 +02002735EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002736
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302737static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002738{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302739 while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002740 u32 val;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302741 val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002742 DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
2743 (val >> 0) & 0xff,
2744 (val >> 8) & 0xff,
2745 (val >> 16) & 0xff,
2746 (val >> 24) & 0xff);
2747 }
2748}
2749
2750static void dsi_show_rx_ack_with_err(u16 err)
2751{
2752 DSSERR("\tACK with ERROR (%#x):\n", err);
2753 if (err & (1 << 0))
2754 DSSERR("\t\tSoT Error\n");
2755 if (err & (1 << 1))
2756 DSSERR("\t\tSoT Sync Error\n");
2757 if (err & (1 << 2))
2758 DSSERR("\t\tEoT Sync Error\n");
2759 if (err & (1 << 3))
2760 DSSERR("\t\tEscape Mode Entry Command Error\n");
2761 if (err & (1 << 4))
2762 DSSERR("\t\tLP Transmit Sync Error\n");
2763 if (err & (1 << 5))
2764 DSSERR("\t\tHS Receive Timeout Error\n");
2765 if (err & (1 << 6))
2766 DSSERR("\t\tFalse Control Error\n");
2767 if (err & (1 << 7))
2768 DSSERR("\t\t(reserved7)\n");
2769 if (err & (1 << 8))
2770 DSSERR("\t\tECC Error, single-bit (corrected)\n");
2771 if (err & (1 << 9))
2772 DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
2773 if (err & (1 << 10))
2774 DSSERR("\t\tChecksum Error\n");
2775 if (err & (1 << 11))
2776 DSSERR("\t\tData type not recognized\n");
2777 if (err & (1 << 12))
2778 DSSERR("\t\tInvalid VC ID\n");
2779 if (err & (1 << 13))
2780 DSSERR("\t\tInvalid Transmission Length\n");
2781 if (err & (1 << 14))
2782 DSSERR("\t\t(reserved14)\n");
2783 if (err & (1 << 15))
2784 DSSERR("\t\tDSI Protocol Violation\n");
2785}
2786
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302787static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev,
2788 int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002789{
2790 /* RX_FIFO_NOT_EMPTY */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302791 while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002792 u32 val;
2793 u8 dt;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302794 val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002795 DSSERR("\trawval %#08x\n", val);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002796 dt = FLD_GET(val, 5, 0);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302797 if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002798 u16 err = FLD_GET(val, 23, 8);
2799 dsi_show_rx_ack_with_err(err);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302800 } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002801 DSSERR("\tDCS short response, 1 byte: %#x\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002802 FLD_GET(val, 23, 8));
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302803 } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002804 DSSERR("\tDCS short response, 2 byte: %#x\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002805 FLD_GET(val, 23, 8));
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302806 } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002807 DSSERR("\tDCS long response, len %d\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002808 FLD_GET(val, 23, 8));
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302809 dsi_vc_flush_long_data(dsidev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002810 } else {
2811 DSSERR("\tunknown datatype 0x%02x\n", dt);
2812 }
2813 }
2814 return 0;
2815}
2816
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302817static int dsi_vc_send_bta(struct platform_device *dsidev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002818{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302819 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2820
2821 if (dsi->debug_write || dsi->debug_read)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002822 DSSDBG("dsi_vc_send_bta %d\n", channel);
2823
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302824 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002825
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302826 /* RX_FIFO_NOT_EMPTY */
2827 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002828 DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302829 dsi_vc_flush_receive_data(dsidev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002830 }
2831
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302832 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002833
Tomi Valkeinen968f8e92011-10-12 10:13:14 +03002834 /* flush posted write */
2835 dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
2836
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002837 return 0;
2838}
2839
Archit Taneja1ffefe72011-05-12 17:26:24 +05302840int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002841{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302842 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002843 DECLARE_COMPLETION_ONSTACK(completion);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002844 int r = 0;
2845 u32 err;
2846
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302847 r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler,
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002848 &completion, DSI_VC_IRQ_BTA);
2849 if (r)
2850 goto err0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002851
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302852 r = dsi_register_isr(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002853 DSI_IRQ_ERROR_MASK);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002854 if (r)
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002855 goto err1;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002856
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302857 r = dsi_vc_send_bta(dsidev, channel);
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002858 if (r)
2859 goto err2;
2860
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002861 if (wait_for_completion_timeout(&completion,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002862 msecs_to_jiffies(500)) == 0) {
2863 DSSERR("Failed to receive BTA\n");
2864 r = -EIO;
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002865 goto err2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002866 }
2867
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302868 err = dsi_get_errors(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002869 if (err) {
2870 DSSERR("Error while sending BTA: %x\n", err);
2871 r = -EIO;
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002872 goto err2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002873 }
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002874err2:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302875 dsi_unregister_isr(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002876 DSI_IRQ_ERROR_MASK);
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002877err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302878 dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler,
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002879 &completion, DSI_VC_IRQ_BTA);
2880err0:
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002881 return r;
2882}
2883EXPORT_SYMBOL(dsi_vc_send_bta_sync);
2884
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302885static inline void dsi_vc_write_long_header(struct platform_device *dsidev,
2886 int channel, u8 data_type, u16 len, u8 ecc)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002887{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302888 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002889 u32 val;
2890 u8 data_id;
2891
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302892 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002893
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302894 data_id = data_type | dsi->vc[channel].vc_id << 6;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002895
2896 val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
2897 FLD_VAL(ecc, 31, 24);
2898
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302899 dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002900}
2901
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302902static inline void dsi_vc_write_long_payload(struct platform_device *dsidev,
2903 int channel, u8 b1, u8 b2, u8 b3, u8 b4)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002904{
2905 u32 val;
2906
2907 val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0;
2908
2909/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
2910 b1, b2, b3, b4, val); */
2911
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302912 dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002913}
2914
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302915static int dsi_vc_send_long(struct platform_device *dsidev, int channel,
2916 u8 data_type, u8 *data, u16 len, u8 ecc)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002917{
2918 /*u32 val; */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302919 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002920 int i;
2921 u8 *p;
2922 int r = 0;
2923 u8 b1, b2, b3, b4;
2924
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302925 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002926 DSSDBG("dsi_vc_send_long, %d bytes\n", len);
2927
2928 /* len + header */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302929 if (dsi->vc[channel].fifo_size * 32 * 4 < len + 4) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002930 DSSERR("unable to send long packet: packet too long.\n");
2931 return -EINVAL;
2932 }
2933
Archit Tanejad6049142011-08-22 11:58:08 +05302934 dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002935
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302936 dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002937
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002938 p = data;
2939 for (i = 0; i < len >> 2; i++) {
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302940 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002941 DSSDBG("\tsending full packet %d\n", i);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002942
2943 b1 = *p++;
2944 b2 = *p++;
2945 b3 = *p++;
2946 b4 = *p++;
2947
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302948 dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002949 }
2950
2951 i = len % 4;
2952 if (i) {
2953 b1 = 0; b2 = 0; b3 = 0;
2954
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302955 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002956 DSSDBG("\tsending remainder bytes %d\n", i);
2957
2958 switch (i) {
2959 case 3:
2960 b1 = *p++;
2961 b2 = *p++;
2962 b3 = *p++;
2963 break;
2964 case 2:
2965 b1 = *p++;
2966 b2 = *p++;
2967 break;
2968 case 1:
2969 b1 = *p++;
2970 break;
2971 }
2972
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302973 dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002974 }
2975
2976 return r;
2977}
2978
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302979static int dsi_vc_send_short(struct platform_device *dsidev, int channel,
2980 u8 data_type, u16 data, u8 ecc)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002981{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302982 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002983 u32 r;
2984 u8 data_id;
2985
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302986 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002987
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302988 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002989 DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
2990 channel,
2991 data_type, data & 0xff, (data >> 8) & 0xff);
2992
Archit Tanejad6049142011-08-22 11:58:08 +05302993 dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002994
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302995 if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002996 DSSERR("ERROR FIFO FULL, aborting transfer\n");
2997 return -EINVAL;
2998 }
2999
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303000 data_id = data_type | dsi->vc[channel].vc_id << 6;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003001
3002 r = (data_id << 0) | (data << 8) | (ecc << 24);
3003
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303004 dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003005
3006 return 0;
3007}
3008
Archit Taneja1ffefe72011-05-12 17:26:24 +05303009int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003010{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303011 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303012
Archit Taneja18b7d092011-09-05 17:01:08 +05303013 return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL,
3014 0, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003015}
3016EXPORT_SYMBOL(dsi_vc_send_null);
3017
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303018static int dsi_vc_write_nosync_common(struct omap_dss_device *dssdev,
3019 int channel, u8 *data, int len, enum dss_dsi_content_type type)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003020{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303021 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003022 int r;
3023
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303024 if (len == 0) {
3025 BUG_ON(type == DSS_DSI_CONTENT_DCS);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303026 r = dsi_vc_send_short(dsidev, channel,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303027 MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0);
3028 } else if (len == 1) {
3029 r = dsi_vc_send_short(dsidev, channel,
3030 type == DSS_DSI_CONTENT_GENERIC ?
3031 MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM :
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303032 MIPI_DSI_DCS_SHORT_WRITE, data[0], 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003033 } else if (len == 2) {
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303034 r = dsi_vc_send_short(dsidev, channel,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303035 type == DSS_DSI_CONTENT_GENERIC ?
3036 MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM :
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303037 MIPI_DSI_DCS_SHORT_WRITE_PARAM,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003038 data[0] | (data[1] << 8), 0);
3039 } else {
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303040 r = dsi_vc_send_long(dsidev, channel,
3041 type == DSS_DSI_CONTENT_GENERIC ?
3042 MIPI_DSI_GENERIC_LONG_WRITE :
3043 MIPI_DSI_DCS_LONG_WRITE, data, len, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003044 }
3045
3046 return r;
3047}
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303048
3049int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel,
3050 u8 *data, int len)
3051{
3052 return dsi_vc_write_nosync_common(dssdev, channel, data, len,
3053 DSS_DSI_CONTENT_DCS);
3054}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003055EXPORT_SYMBOL(dsi_vc_dcs_write_nosync);
3056
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303057int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel,
3058 u8 *data, int len)
3059{
3060 return dsi_vc_write_nosync_common(dssdev, channel, data, len,
3061 DSS_DSI_CONTENT_GENERIC);
3062}
3063EXPORT_SYMBOL(dsi_vc_generic_write_nosync);
3064
3065static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel,
3066 u8 *data, int len, enum dss_dsi_content_type type)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003067{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303068 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003069 int r;
3070
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303071 r = dsi_vc_write_nosync_common(dssdev, channel, data, len, type);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003072 if (r)
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003073 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003074
Archit Taneja1ffefe72011-05-12 17:26:24 +05303075 r = dsi_vc_send_bta_sync(dssdev, channel);
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003076 if (r)
3077 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003078
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303079 /* RX_FIFO_NOT_EMPTY */
3080 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinenb63ac1e2010-04-09 13:20:57 +03003081 DSSERR("rx fifo not empty after write, dumping data:\n");
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303082 dsi_vc_flush_receive_data(dsidev, channel);
Tomi Valkeinenb63ac1e2010-04-09 13:20:57 +03003083 r = -EIO;
3084 goto err;
3085 }
3086
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003087 return 0;
3088err:
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303089 DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n",
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003090 channel, data[0], len);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003091 return r;
3092}
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303093
3094int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
3095 int len)
3096{
3097 return dsi_vc_write_common(dssdev, channel, data, len,
3098 DSS_DSI_CONTENT_DCS);
3099}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003100EXPORT_SYMBOL(dsi_vc_dcs_write);
3101
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303102int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data,
3103 int len)
3104{
3105 return dsi_vc_write_common(dssdev, channel, data, len,
3106 DSS_DSI_CONTENT_GENERIC);
3107}
3108EXPORT_SYMBOL(dsi_vc_generic_write);
3109
Archit Taneja1ffefe72011-05-12 17:26:24 +05303110int dsi_vc_dcs_write_0(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd)
Tomi Valkeinen828c48f2009-12-16 14:53:15 +02003111{
Archit Taneja1ffefe72011-05-12 17:26:24 +05303112 return dsi_vc_dcs_write(dssdev, channel, &dcs_cmd, 1);
Tomi Valkeinen828c48f2009-12-16 14:53:15 +02003113}
3114EXPORT_SYMBOL(dsi_vc_dcs_write_0);
3115
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303116int dsi_vc_generic_write_0(struct omap_dss_device *dssdev, int channel)
3117{
3118 return dsi_vc_generic_write(dssdev, channel, NULL, 0);
3119}
3120EXPORT_SYMBOL(dsi_vc_generic_write_0);
3121
Archit Taneja1ffefe72011-05-12 17:26:24 +05303122int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
3123 u8 param)
Tomi Valkeinen828c48f2009-12-16 14:53:15 +02003124{
3125 u8 buf[2];
3126 buf[0] = dcs_cmd;
3127 buf[1] = param;
Archit Taneja1ffefe72011-05-12 17:26:24 +05303128 return dsi_vc_dcs_write(dssdev, channel, buf, 2);
Tomi Valkeinen828c48f2009-12-16 14:53:15 +02003129}
3130EXPORT_SYMBOL(dsi_vc_dcs_write_1);
3131
Archit Taneja6ff8aa32011-08-25 18:35:58 +05303132int dsi_vc_generic_write_1(struct omap_dss_device *dssdev, int channel,
3133 u8 param)
3134{
3135 return dsi_vc_generic_write(dssdev, channel, &param, 1);
3136}
3137EXPORT_SYMBOL(dsi_vc_generic_write_1);
3138
3139int dsi_vc_generic_write_2(struct omap_dss_device *dssdev, int channel,
3140 u8 param1, u8 param2)
3141{
3142 u8 buf[2];
3143 buf[0] = param1;
3144 buf[1] = param2;
3145 return dsi_vc_generic_write(dssdev, channel, buf, 2);
3146}
3147EXPORT_SYMBOL(dsi_vc_generic_write_2);
3148
Archit Tanejab8509752011-08-30 15:48:23 +05303149static int dsi_vc_dcs_send_read_request(struct omap_dss_device *dssdev,
3150 int channel, u8 dcs_cmd)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003151{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303152 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303153 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejab8509752011-08-30 15:48:23 +05303154 int r;
3155
3156 if (dsi->debug_read)
3157 DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n",
3158 channel, dcs_cmd);
3159
3160 r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0);
3161 if (r) {
3162 DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)"
3163 " failed\n", channel, dcs_cmd);
3164 return r;
3165 }
3166
3167 return 0;
3168}
3169
Archit Tanejab3b89c02011-08-30 16:07:39 +05303170static int dsi_vc_generic_send_read_request(struct omap_dss_device *dssdev,
3171 int channel, u8 *reqdata, int reqlen)
3172{
3173 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3174 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3175 u16 data;
3176 u8 data_type;
3177 int r;
3178
3179 if (dsi->debug_read)
3180 DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n",
3181 channel, reqlen);
3182
3183 if (reqlen == 0) {
3184 data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
3185 data = 0;
3186 } else if (reqlen == 1) {
3187 data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
3188 data = reqdata[0];
3189 } else if (reqlen == 2) {
3190 data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
3191 data = reqdata[0] | (reqdata[1] << 8);
3192 } else {
3193 BUG();
3194 }
3195
3196 r = dsi_vc_send_short(dsidev, channel, data_type, data, 0);
3197 if (r) {
3198 DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)"
3199 " failed\n", channel, reqlen);
3200 return r;
3201 }
3202
3203 return 0;
3204}
3205
3206static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel,
3207 u8 *buf, int buflen, enum dss_dsi_content_type type)
Archit Tanejab8509752011-08-30 15:48:23 +05303208{
3209 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003210 u32 val;
3211 u8 dt;
3212 int r;
3213
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003214 /* RX_FIFO_NOT_EMPTY */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303215 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003216 DSSERR("RX fifo empty when trying to read.\n");
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003217 r = -EIO;
3218 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003219 }
3220
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303221 val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303222 if (dsi->debug_read)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003223 DSSDBG("\theader: %08x\n", val);
3224 dt = FLD_GET(val, 5, 0);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303225 if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003226 u16 err = FLD_GET(val, 23, 8);
3227 dsi_show_rx_ack_with_err(err);
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003228 r = -EIO;
3229 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003230
Archit Tanejab3b89c02011-08-30 16:07:39 +05303231 } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
3232 MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
3233 MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003234 u8 data = FLD_GET(val, 15, 8);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303235 if (dsi->debug_read)
Archit Tanejab3b89c02011-08-30 16:07:39 +05303236 DSSDBG("\t%s short response, 1 byte: %02x\n",
3237 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3238 "DCS", data);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003239
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003240 if (buflen < 1) {
3241 r = -EIO;
3242 goto err;
3243 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003244
3245 buf[0] = data;
3246
3247 return 1;
Archit Tanejab3b89c02011-08-30 16:07:39 +05303248 } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
3249 MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
3250 MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003251 u16 data = FLD_GET(val, 23, 8);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303252 if (dsi->debug_read)
Archit Tanejab3b89c02011-08-30 16:07:39 +05303253 DSSDBG("\t%s short response, 2 byte: %04x\n",
3254 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3255 "DCS", data);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003256
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003257 if (buflen < 2) {
3258 r = -EIO;
3259 goto err;
3260 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003261
3262 buf[0] = data & 0xff;
3263 buf[1] = (data >> 8) & 0xff;
3264
3265 return 2;
Archit Tanejab3b89c02011-08-30 16:07:39 +05303266 } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
3267 MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
3268 MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003269 int w;
3270 int len = FLD_GET(val, 23, 8);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303271 if (dsi->debug_read)
Archit Tanejab3b89c02011-08-30 16:07:39 +05303272 DSSDBG("\t%s long response, len %d\n",
3273 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3274 "DCS", len);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003275
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003276 if (len > buflen) {
3277 r = -EIO;
3278 goto err;
3279 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003280
3281 /* two byte checksum ends the packet, not included in len */
3282 for (w = 0; w < len + 2;) {
3283 int b;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303284 val = dsi_read_reg(dsidev,
3285 DSI_VC_SHORT_PACKET_HEADER(channel));
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303286 if (dsi->debug_read)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003287 DSSDBG("\t\t%02x %02x %02x %02x\n",
3288 (val >> 0) & 0xff,
3289 (val >> 8) & 0xff,
3290 (val >> 16) & 0xff,
3291 (val >> 24) & 0xff);
3292
3293 for (b = 0; b < 4; ++b) {
3294 if (w < len)
3295 buf[w] = (val >> (b * 8)) & 0xff;
3296 /* we discard the 2 byte checksum */
3297 ++w;
3298 }
3299 }
3300
3301 return len;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003302 } else {
3303 DSSERR("\tunknown datatype 0x%02x\n", dt);
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003304 r = -EIO;
3305 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003306 }
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003307
3308 BUG();
3309err:
Archit Tanejab3b89c02011-08-30 16:07:39 +05303310 DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel,
3311 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003312
Archit Tanejab8509752011-08-30 15:48:23 +05303313 return r;
3314}
3315
3316int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
3317 u8 *buf, int buflen)
3318{
3319 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3320 int r;
3321
3322 r = dsi_vc_dcs_send_read_request(dssdev, channel, dcs_cmd);
3323 if (r)
3324 goto err;
3325
3326 r = dsi_vc_send_bta_sync(dssdev, channel);
3327 if (r)
3328 goto err;
3329
Archit Tanejab3b89c02011-08-30 16:07:39 +05303330 r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
3331 DSS_DSI_CONTENT_DCS);
Archit Tanejab8509752011-08-30 15:48:23 +05303332 if (r < 0)
3333 goto err;
3334
3335 if (r != buflen) {
3336 r = -EIO;
3337 goto err;
3338 }
3339
3340 return 0;
3341err:
3342 DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd);
3343 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003344}
3345EXPORT_SYMBOL(dsi_vc_dcs_read);
3346
Archit Tanejab3b89c02011-08-30 16:07:39 +05303347static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel,
3348 u8 *reqdata, int reqlen, u8 *buf, int buflen)
3349{
3350 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3351 int r;
3352
3353 r = dsi_vc_generic_send_read_request(dssdev, channel, reqdata, reqlen);
3354 if (r)
3355 return r;
3356
3357 r = dsi_vc_send_bta_sync(dssdev, channel);
3358 if (r)
3359 return r;
3360
3361 r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
3362 DSS_DSI_CONTENT_GENERIC);
3363 if (r < 0)
3364 return r;
3365
3366 if (r != buflen) {
3367 r = -EIO;
3368 return r;
3369 }
3370
3371 return 0;
3372}
3373
3374int dsi_vc_generic_read_0(struct omap_dss_device *dssdev, int channel, u8 *buf,
3375 int buflen)
3376{
3377 int r;
3378
3379 r = dsi_vc_generic_read(dssdev, channel, NULL, 0, buf, buflen);
3380 if (r) {
3381 DSSERR("dsi_vc_generic_read_0(ch %d) failed\n", channel);
3382 return r;
3383 }
3384
3385 return 0;
3386}
3387EXPORT_SYMBOL(dsi_vc_generic_read_0);
3388
3389int dsi_vc_generic_read_1(struct omap_dss_device *dssdev, int channel, u8 param,
3390 u8 *buf, int buflen)
3391{
3392 int r;
3393
3394 r = dsi_vc_generic_read(dssdev, channel, &param, 1, buf, buflen);
3395 if (r) {
3396 DSSERR("dsi_vc_generic_read_1(ch %d) failed\n", channel);
3397 return r;
3398 }
3399
3400 return 0;
3401}
3402EXPORT_SYMBOL(dsi_vc_generic_read_1);
3403
3404int dsi_vc_generic_read_2(struct omap_dss_device *dssdev, int channel,
3405 u8 param1, u8 param2, u8 *buf, int buflen)
3406{
3407 int r;
3408 u8 reqdata[2];
3409
3410 reqdata[0] = param1;
3411 reqdata[1] = param2;
3412
3413 r = dsi_vc_generic_read(dssdev, channel, reqdata, 2, buf, buflen);
3414 if (r) {
3415 DSSERR("dsi_vc_generic_read_2(ch %d) failed\n", channel);
3416 return r;
3417 }
3418
3419 return 0;
3420}
3421EXPORT_SYMBOL(dsi_vc_generic_read_2);
3422
Archit Taneja1ffefe72011-05-12 17:26:24 +05303423int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel,
3424 u16 len)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003425{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303426 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3427
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303428 return dsi_vc_send_short(dsidev, channel,
3429 MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003430}
3431EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
3432
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303433static int dsi_enter_ulps(struct platform_device *dsidev)
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003434{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303435 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003436 DECLARE_COMPLETION_ONSTACK(completion);
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003437 int r, i;
3438 unsigned mask;
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003439
3440 DSSDBGF();
3441
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303442 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003443
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303444 WARN_ON(dsi->ulps_enabled);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003445
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303446 if (dsi->ulps_enabled)
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003447 return 0;
3448
Tomi Valkeinen6cc78aa2011-10-13 19:22:43 +03003449 /* DDR_CLK_ALWAYS_ON */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303450 if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) {
Tomi Valkeinen6cc78aa2011-10-13 19:22:43 +03003451 dsi_if_enable(dsidev, 0);
3452 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
3453 dsi_if_enable(dsidev, 1);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003454 }
3455
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303456 dsi_sync_vc(dsidev, 0);
3457 dsi_sync_vc(dsidev, 1);
3458 dsi_sync_vc(dsidev, 2);
3459 dsi_sync_vc(dsidev, 3);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003460
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303461 dsi_force_tx_stop_mode_io(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003462
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303463 dsi_vc_enable(dsidev, 0, false);
3464 dsi_vc_enable(dsidev, 1, false);
3465 dsi_vc_enable(dsidev, 2, false);
3466 dsi_vc_enable(dsidev, 3, false);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003467
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303468 if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003469 DSSERR("HS busy when enabling ULPS\n");
3470 return -EIO;
3471 }
3472
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303473 if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003474 DSSERR("LP busy when enabling ULPS\n");
3475 return -EIO;
3476 }
3477
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303478 r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003479 DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3480 if (r)
3481 return r;
3482
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003483 mask = 0;
3484
3485 for (i = 0; i < dsi->num_lanes_supported; ++i) {
3486 if (dsi->lanes[i].function == DSI_LANE_UNUSED)
3487 continue;
3488 mask |= 1 << i;
3489 }
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003490 /* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */
3491 /* LANEx_ULPS_SIG2 */
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003492 REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, mask, 9, 5);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003493
Tomi Valkeinena702c852011-10-12 10:10:21 +03003494 /* flush posted write and wait for SCP interface to finish the write */
3495 dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003496
3497 if (wait_for_completion_timeout(&completion,
3498 msecs_to_jiffies(1000)) == 0) {
3499 DSSERR("ULPS enable timeout\n");
3500 r = -EIO;
3501 goto err;
3502 }
3503
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303504 dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003505 DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3506
Tomi Valkeinen8ef0e612011-05-31 16:55:47 +03003507 /* Reset LANEx_ULPS_SIG2 */
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003508 REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, 0, 9, 5);
Tomi Valkeinen8ef0e612011-05-31 16:55:47 +03003509
Tomi Valkeinena702c852011-10-12 10:10:21 +03003510 /* flush posted write and wait for SCP interface to finish the write */
3511 dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003512
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303513 dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003514
3515 dsi_if_enable(dsidev, false);
3516
3517 dsi->ulps_enabled = true;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303518
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003519 return 0;
3520
3521err:
3522 dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303523 DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3524 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003525}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003526
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003527static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
3528 unsigned ticks, bool x4, bool x16)
3529{
3530 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003531 unsigned long total_ticks;
3532 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303533
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003534 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303535
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003536 /* ticks in DSI_FCK */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003537 fck = dsi_fclk_rate(dsidev);
3538
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003539 r = dsi_read_reg(dsidev, DSI_TIMING2);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303540 r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003541 r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003542 r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */
3543 r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
3544 dsi_write_reg(dsidev, DSI_TIMING2, r);
3545
3546 total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3547
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003548 DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
3549 total_ticks,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303550 ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3551 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003552}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003553
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003554static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
3555 bool x8, bool x16)
3556{
3557 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003558 unsigned long total_ticks;
3559 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303560
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003561 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303562
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003563 /* ticks in DSI_FCK */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003564 fck = dsi_fclk_rate(dsidev);
3565
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003566 r = dsi_read_reg(dsidev, DSI_TIMING1);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303567 r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003568 r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003569 r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */
3570 r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
3571 dsi_write_reg(dsidev, DSI_TIMING1, r);
3572
3573 total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
3574
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003575 DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
3576 total_ticks,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303577 ticks, x8 ? " x8" : "", x16 ? " x16" : "",
3578 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003579}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003580
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003581static void dsi_set_stop_state_counter(struct platform_device *dsidev,
3582 unsigned ticks, bool x4, bool x16)
3583{
3584 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003585 unsigned long total_ticks;
3586 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303587
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003588 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303589
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003590 /* ticks in DSI_FCK */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003591 fck = dsi_fclk_rate(dsidev);
3592
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003593 r = dsi_read_reg(dsidev, DSI_TIMING1);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303594 r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003595 r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003596 r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */
3597 r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
3598 dsi_write_reg(dsidev, DSI_TIMING1, r);
3599
3600 total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3601
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003602 DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
3603 total_ticks,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303604 ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3605 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003606}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003607
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003608static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
3609 unsigned ticks, bool x4, bool x16)
3610{
3611 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003612 unsigned long total_ticks;
3613 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303614
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003615 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303616
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003617 /* ticks in TxByteClkHS */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003618 fck = dsi_get_txbyteclkhs(dsidev);
3619
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003620 r = dsi_read_reg(dsidev, DSI_TIMING2);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303621 r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003622 r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003623 r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */
3624 r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
3625 dsi_write_reg(dsidev, DSI_TIMING2, r);
3626
3627 total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3628
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003629 DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
3630 total_ticks,
3631 ticks, x4 ? " x4" : "", x16 ? " x16" : "",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303632 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003633}
Archit Taneja8af6ff02011-09-05 16:48:27 +05303634
3635static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev)
3636{
3637 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3638 int num_line_buffers;
3639
3640 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
3641 int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
3642 unsigned line_buf_size = dsi_get_line_buf_size(dsidev);
3643 struct omap_video_timings *timings = &dssdev->panel.timings;
3644 /*
3645 * Don't use line buffers if width is greater than the video
3646 * port's line buffer size
3647 */
3648 if (line_buf_size <= timings->x_res * bpp / 8)
3649 num_line_buffers = 0;
3650 else
3651 num_line_buffers = 2;
3652 } else {
3653 /* Use maximum number of line buffers in command mode */
3654 num_line_buffers = 2;
3655 }
3656
3657 /* LINE_BUFFER */
3658 REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12);
3659}
3660
3661static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev)
3662{
3663 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3664 int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol;
3665 int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol;
3666 int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol;
3667 bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end;
3668 bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end;
3669 u32 r;
3670
3671 r = dsi_read_reg(dsidev, DSI_CTRL);
3672 r = FLD_MOD(r, de_pol, 9, 9); /* VP_DE_POL */
3673 r = FLD_MOD(r, hsync_pol, 10, 10); /* VP_HSYNC_POL */
3674 r = FLD_MOD(r, vsync_pol, 11, 11); /* VP_VSYNC_POL */
3675 r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
3676 r = FLD_MOD(r, vsync_end, 16, 16); /* VP_VSYNC_END */
3677 r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
3678 r = FLD_MOD(r, hsync_end, 18, 18); /* VP_HSYNC_END */
3679 dsi_write_reg(dsidev, DSI_CTRL, r);
3680}
3681
3682static void dsi_config_blanking_modes(struct omap_dss_device *dssdev)
3683{
3684 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3685 int blanking_mode = dssdev->panel.dsi_vm_data.blanking_mode;
3686 int hfp_blanking_mode = dssdev->panel.dsi_vm_data.hfp_blanking_mode;
3687 int hbp_blanking_mode = dssdev->panel.dsi_vm_data.hbp_blanking_mode;
3688 int hsa_blanking_mode = dssdev->panel.dsi_vm_data.hsa_blanking_mode;
3689 u32 r;
3690
3691 /*
3692 * 0 = TX FIFO packets sent or LPS in corresponding blanking periods
3693 * 1 = Long blanking packets are sent in corresponding blanking periods
3694 */
3695 r = dsi_read_reg(dsidev, DSI_CTRL);
3696 r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */
3697 r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */
3698 r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */
3699 r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */
3700 dsi_write_reg(dsidev, DSI_CTRL, r);
3701}
3702
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003703static int dsi_proto_config(struct omap_dss_device *dssdev)
3704{
3705 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3706 u32 r;
3707 int buswidth = 0;
3708
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303709 dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32,
Tomi Valkeinendd8079d2009-12-16 16:49:03 +02003710 DSI_FIFO_SIZE_32,
3711 DSI_FIFO_SIZE_32,
3712 DSI_FIFO_SIZE_32);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003713
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303714 dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32,
Tomi Valkeinendd8079d2009-12-16 16:49:03 +02003715 DSI_FIFO_SIZE_32,
3716 DSI_FIFO_SIZE_32,
3717 DSI_FIFO_SIZE_32);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003718
3719 /* XXX what values for the timeouts? */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303720 dsi_set_stop_state_counter(dsidev, 0x1000, false, false);
3721 dsi_set_ta_timeout(dsidev, 0x1fff, true, true);
3722 dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true);
3723 dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003724
Archit Tanejaa3b3cc22011-09-08 18:42:16 +05303725 switch (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003726 case 16:
3727 buswidth = 0;
3728 break;
3729 case 18:
3730 buswidth = 1;
3731 break;
3732 case 24:
3733 buswidth = 2;
3734 break;
3735 default:
3736 BUG();
3737 }
3738
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303739 r = dsi_read_reg(dsidev, DSI_CTRL);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003740 r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */
3741 r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */
3742 r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
3743 r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
3744 r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
3745 r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003746 r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
3747 r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
Archit Taneja9613c022011-03-22 06:33:36 -05003748 if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
3749 r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */
3750 /* DCS_CMD_CODE, 1=start, 0=continue */
3751 r = FLD_MOD(r, 0, 25, 25);
3752 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003753
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303754 dsi_write_reg(dsidev, DSI_CTRL, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003755
Archit Taneja8af6ff02011-09-05 16:48:27 +05303756 dsi_config_vp_num_line_buffers(dssdev);
3757
3758 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
3759 dsi_config_vp_sync_events(dssdev);
3760 dsi_config_blanking_modes(dssdev);
3761 }
3762
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303763 dsi_vc_initial_config(dsidev, 0);
3764 dsi_vc_initial_config(dsidev, 1);
3765 dsi_vc_initial_config(dsidev, 2);
3766 dsi_vc_initial_config(dsidev, 3);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003767
3768 return 0;
3769}
3770
3771static void dsi_proto_timings(struct omap_dss_device *dssdev)
3772{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303773 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinendb186442011-10-13 16:12:29 +03003774 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003775 unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail;
3776 unsigned tclk_pre, tclk_post;
3777 unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
3778 unsigned ths_trail, ths_exit;
3779 unsigned ddr_clk_pre, ddr_clk_post;
3780 unsigned enter_hs_mode_lat, exit_hs_mode_lat;
3781 unsigned ths_eot;
Tomi Valkeinendb186442011-10-13 16:12:29 +03003782 int ndl = dsi->num_lanes_used - 1;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003783 u32 r;
3784
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303785 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003786 ths_prepare = FLD_GET(r, 31, 24);
3787 ths_prepare_ths_zero = FLD_GET(r, 23, 16);
3788 ths_zero = ths_prepare_ths_zero - ths_prepare;
3789 ths_trail = FLD_GET(r, 15, 8);
3790 ths_exit = FLD_GET(r, 7, 0);
3791
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303792 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003793 tlpx = FLD_GET(r, 22, 16) * 2;
3794 tclk_trail = FLD_GET(r, 15, 8);
3795 tclk_zero = FLD_GET(r, 7, 0);
3796
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303797 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003798 tclk_prepare = FLD_GET(r, 7, 0);
3799
3800 /* min 8*UI */
3801 tclk_pre = 20;
3802 /* min 60ns + 52*UI */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303803 tclk_post = ns2ddr(dsidev, 60) + 26;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003804
Archit Taneja8af6ff02011-09-05 16:48:27 +05303805 ths_eot = DIV_ROUND_UP(4, ndl);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003806
3807 ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
3808 4);
3809 ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
3810
3811 BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
3812 BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
3813
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303814 r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003815 r = FLD_MOD(r, ddr_clk_pre, 15, 8);
3816 r = FLD_MOD(r, ddr_clk_post, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303817 dsi_write_reg(dsidev, DSI_CLK_TIMING, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003818
3819 DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
3820 ddr_clk_pre,
3821 ddr_clk_post);
3822
3823 enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
3824 DIV_ROUND_UP(ths_prepare, 4) +
3825 DIV_ROUND_UP(ths_zero + 3, 4);
3826
3827 exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
3828
3829 r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
3830 FLD_VAL(exit_hs_mode_lat, 15, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303831 dsi_write_reg(dsidev, DSI_VM_TIMING7, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003832
3833 DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
3834 enter_hs_mode_lat, exit_hs_mode_lat);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303835
3836 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
3837 /* TODO: Implement a video mode check_timings function */
3838 int hsa = dssdev->panel.dsi_vm_data.hsa;
3839 int hfp = dssdev->panel.dsi_vm_data.hfp;
3840 int hbp = dssdev->panel.dsi_vm_data.hbp;
3841 int vsa = dssdev->panel.dsi_vm_data.vsa;
3842 int vfp = dssdev->panel.dsi_vm_data.vfp;
3843 int vbp = dssdev->panel.dsi_vm_data.vbp;
3844 int window_sync = dssdev->panel.dsi_vm_data.window_sync;
3845 bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end;
3846 struct omap_video_timings *timings = &dssdev->panel.timings;
3847 int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
3848 int tl, t_he, width_bytes;
3849
3850 t_he = hsync_end ?
3851 ((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
3852
3853 width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8);
3854
3855 /* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
3856 tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
3857 DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
3858
3859 DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
3860 hfp, hsync_end ? hsa : 0, tl);
3861 DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
3862 vsa, timings->y_res);
3863
3864 r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
3865 r = FLD_MOD(r, hbp, 11, 0); /* HBP */
3866 r = FLD_MOD(r, hfp, 23, 12); /* HFP */
3867 r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */
3868 dsi_write_reg(dsidev, DSI_VM_TIMING1, r);
3869
3870 r = dsi_read_reg(dsidev, DSI_VM_TIMING2);
3871 r = FLD_MOD(r, vbp, 7, 0); /* VBP */
3872 r = FLD_MOD(r, vfp, 15, 8); /* VFP */
3873 r = FLD_MOD(r, vsa, 23, 16); /* VSA */
3874 r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */
3875 dsi_write_reg(dsidev, DSI_VM_TIMING2, r);
3876
3877 r = dsi_read_reg(dsidev, DSI_VM_TIMING3);
3878 r = FLD_MOD(r, timings->y_res, 14, 0); /* VACT */
3879 r = FLD_MOD(r, tl, 31, 16); /* TL */
3880 dsi_write_reg(dsidev, DSI_VM_TIMING3, r);
3881 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003882}
3883
Tomi Valkeinene4a9e942012-03-28 15:58:56 +03003884int omapdss_dsi_configure_pins(struct omap_dss_device *dssdev,
3885 const struct omap_dsi_pin_config *pin_cfg)
3886{
3887 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3888 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3889 int num_pins;
3890 const int *pins;
3891 struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
3892 int num_lanes;
3893 int i;
3894
3895 static const enum dsi_lane_function functions[] = {
3896 DSI_LANE_CLK,
3897 DSI_LANE_DATA1,
3898 DSI_LANE_DATA2,
3899 DSI_LANE_DATA3,
3900 DSI_LANE_DATA4,
3901 };
3902
3903 num_pins = pin_cfg->num_pins;
3904 pins = pin_cfg->pins;
3905
3906 if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2
3907 || num_pins % 2 != 0)
3908 return -EINVAL;
3909
3910 for (i = 0; i < DSI_MAX_NR_LANES; ++i)
3911 lanes[i].function = DSI_LANE_UNUSED;
3912
3913 num_lanes = 0;
3914
3915 for (i = 0; i < num_pins; i += 2) {
3916 u8 lane, pol;
3917 int dx, dy;
3918
3919 dx = pins[i];
3920 dy = pins[i + 1];
3921
3922 if (dx < 0 || dx >= dsi->num_lanes_supported * 2)
3923 return -EINVAL;
3924
3925 if (dy < 0 || dy >= dsi->num_lanes_supported * 2)
3926 return -EINVAL;
3927
3928 if (dx & 1) {
3929 if (dy != dx - 1)
3930 return -EINVAL;
3931 pol = 1;
3932 } else {
3933 if (dy != dx + 1)
3934 return -EINVAL;
3935 pol = 0;
3936 }
3937
3938 lane = dx / 2;
3939
3940 lanes[lane].function = functions[i / 2];
3941 lanes[lane].polarity = pol;
3942 num_lanes++;
3943 }
3944
3945 memcpy(dsi->lanes, lanes, sizeof(dsi->lanes));
3946 dsi->num_lanes_used = num_lanes;
3947
3948 return 0;
3949}
3950EXPORT_SYMBOL(omapdss_dsi_configure_pins);
3951
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003952int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303953{
3954 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3955 int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
3956 u8 data_type;
3957 u16 word_count;
Tomi Valkeinen33ca2372011-11-21 13:42:58 +02003958 int r;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303959
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003960 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
3961 switch (dssdev->panel.dsi_pix_fmt) {
3962 case OMAP_DSS_DSI_FMT_RGB888:
3963 data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
3964 break;
3965 case OMAP_DSS_DSI_FMT_RGB666:
3966 data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
3967 break;
3968 case OMAP_DSS_DSI_FMT_RGB666_PACKED:
3969 data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
3970 break;
3971 case OMAP_DSS_DSI_FMT_RGB565:
3972 data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
3973 break;
3974 default:
3975 BUG();
3976 };
Archit Taneja8af6ff02011-09-05 16:48:27 +05303977
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003978 dsi_if_enable(dsidev, false);
3979 dsi_vc_enable(dsidev, channel, false);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303980
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003981 /* MODE, 1 = video mode */
3982 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303983
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003984 word_count = DIV_ROUND_UP(dssdev->panel.timings.x_res * bpp, 8);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303985
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003986 dsi_vc_write_long_header(dsidev, channel, data_type,
3987 word_count, 0);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303988
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003989 dsi_vc_enable(dsidev, channel, true);
3990 dsi_if_enable(dsidev, true);
3991 }
Archit Taneja8af6ff02011-09-05 16:48:27 +05303992
Tomi Valkeinen33ca2372011-11-21 13:42:58 +02003993 r = dss_mgr_enable(dssdev->manager);
3994 if (r) {
3995 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
3996 dsi_if_enable(dsidev, false);
3997 dsi_vc_enable(dsidev, channel, false);
3998 }
3999
4000 return r;
4001 }
Archit Taneja8af6ff02011-09-05 16:48:27 +05304002
4003 return 0;
4004}
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02004005EXPORT_SYMBOL(dsi_enable_video_output);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304006
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02004007void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel)
Archit Taneja8af6ff02011-09-05 16:48:27 +05304008{
4009 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4010
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02004011 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
4012 dsi_if_enable(dsidev, false);
4013 dsi_vc_enable(dsidev, channel, false);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304014
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02004015 /* MODE, 0 = command mode */
4016 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304017
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02004018 dsi_vc_enable(dsidev, channel, true);
4019 dsi_if_enable(dsidev, true);
4020 }
Archit Taneja8af6ff02011-09-05 16:48:27 +05304021
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +02004022 dss_mgr_disable(dssdev->manager);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304023}
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02004024EXPORT_SYMBOL(dsi_disable_video_output);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304025
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004026static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004027 u16 w, u16 h)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004028{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304029 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304030 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004031 unsigned bytespp;
4032 unsigned bytespl;
4033 unsigned bytespf;
4034 unsigned total_len;
4035 unsigned packet_payload;
4036 unsigned packet_len;
4037 u32 l;
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004038 int r;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304039 const unsigned channel = dsi->update_channel;
Archit Taneja0c65622b2011-05-16 15:17:09 +05304040 const unsigned line_buf_size = dsi_get_line_buf_size(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004041
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004042 DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004043
Archit Tanejad6049142011-08-22 11:58:08 +05304044 dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004045
Archit Tanejaa3b3cc22011-09-08 18:42:16 +05304046 bytespp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004047 bytespl = w * bytespp;
4048 bytespf = bytespl * h;
4049
4050 /* NOTE: packet_payload has to be equal to N * bytespl, where N is
4051 * number of lines in a packet. See errata about VP_CLK_RATIO */
4052
4053 if (bytespf < line_buf_size)
4054 packet_payload = bytespf;
4055 else
4056 packet_payload = (line_buf_size) / bytespl * bytespl;
4057
4058 packet_len = packet_payload + 1; /* 1 byte for DCS cmd */
4059 total_len = (bytespf / packet_payload) * packet_len;
4060
4061 if (bytespf % packet_payload)
4062 total_len += (bytespf % packet_payload) + 1;
4063
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004064 l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304065 dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004066
Archit Taneja7a7c48f2011-08-25 18:25:03 +05304067 dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304068 packet_len, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004069
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304070 if (dsi->te_enabled)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004071 l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
4072 else
4073 l = FLD_MOD(l, 1, 31, 31); /* TE_START */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304074 dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004075
4076 /* We put SIDLEMODE to no-idle for the duration of the transfer,
4077 * because DSS interrupts are not capable of waking up the CPU and the
4078 * framedone interrupt could be delayed for quite a long time. I think
4079 * the same goes for any DSS interrupts, but for some reason I have not
4080 * seen the problem anywhere else than here.
4081 */
4082 dispc_disable_sidle();
4083
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304084 dsi_perf_mark_start(dsidev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004085
Archit Taneja49dbf582011-05-16 15:17:07 +05304086 r = schedule_delayed_work(&dsi->framedone_timeout_work,
4087 msecs_to_jiffies(250));
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004088 BUG_ON(r == 0);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004089
Tomi Valkeinen1cb00172011-11-18 11:14:01 +02004090 dss_mgr_start_update(dssdev->manager);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004091
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304092 if (dsi->te_enabled) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004093 /* disable LP_RX_TO, so that we can receive TE. Time to wait
4094 * for TE is longer than the timer allows */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304095 REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004096
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304097 dsi_vc_send_bta(dsidev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004098
4099#ifdef DSI_CATCH_MISSING_TE
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304100 mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004101#endif
4102 }
4103}
4104
4105#ifdef DSI_CATCH_MISSING_TE
4106static void dsi_te_timeout(unsigned long arg)
4107{
4108 DSSERR("TE not received for 250ms!\n");
4109}
4110#endif
4111
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304112static void dsi_handle_framedone(struct platform_device *dsidev, int error)
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004113{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304114 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4115
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004116 /* SIDLEMODE back to smart-idle */
4117 dispc_enable_sidle();
4118
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304119 if (dsi->te_enabled) {
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004120 /* enable LP_RX_TO again after the TE */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304121 REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004122 }
4123
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304124 dsi->framedone_callback(error, dsi->framedone_data);
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004125
4126 if (!error)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304127 dsi_perf_show(dsidev, "DISPC");
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004128}
4129
4130static void dsi_framedone_timeout_work_callback(struct work_struct *work)
4131{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304132 struct dsi_data *dsi = container_of(work, struct dsi_data,
4133 framedone_timeout_work.work);
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004134 /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
4135 * 250ms which would conflict with this timeout work. What should be
4136 * done is first cancel the transfer on the HW, and then cancel the
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004137 * possibly scheduled framedone work. However, cancelling the transfer
4138 * on the HW is buggy, and would probably require resetting the whole
4139 * DSI */
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004140
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004141 DSSERR("Framedone not received for 250ms!\n");
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004142
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304143 dsi_handle_framedone(dsi->pdev, -ETIMEDOUT);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004144}
4145
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004146static void dsi_framedone_irq_callback(void *data, u32 mask)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004147{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304148 struct omap_dss_device *dssdev = (struct omap_dss_device *) data;
4149 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304150 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4151
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004152 /* Note: We get FRAMEDONE when DISPC has finished sending pixels and
4153 * turns itself off. However, DSI still has the pixels in its buffers,
4154 * and is sending the data.
4155 */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004156
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304157 __cancel_delayed_work(&dsi->framedone_timeout_work);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004158
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304159 dsi_handle_framedone(dsidev, 0);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004160}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004161
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004162int omap_dsi_update(struct omap_dss_device *dssdev, int channel,
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004163 void (*callback)(int, void *), void *data)
4164{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304165 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304166 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004167 u16 dw, dh;
4168
4169 dsi_perf_mark_setup(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304170
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304171 dsi->update_channel = channel;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004172
Tomi Valkeinen4a9e78a2011-08-15 11:22:21 +03004173 dsi->framedone_callback = callback;
4174 dsi->framedone_data = data;
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004175
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004176 dssdev->driver->get_resolution(dssdev, &dw, &dh);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004177
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004178#ifdef DEBUG
4179 dsi->update_bytes = dw * dh *
4180 dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8;
4181#endif
4182 dsi_update_screen_dispc(dssdev, dw, dh);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004183
4184 return 0;
4185}
4186EXPORT_SYMBOL(omap_dsi_update);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004187
4188/* Display funcs */
4189
4190static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
4191{
4192 int r;
Archit Taneja5a8b5722011-05-12 17:26:29 +05304193
Archit Taneja8af6ff02011-09-05 16:48:27 +05304194 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004195 u16 dw, dh;
Archit Taneja8af6ff02011-09-05 16:48:27 +05304196 u32 irq;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004197 struct omap_video_timings timings = {
4198 .hsw = 1,
4199 .hfp = 1,
4200 .hbp = 1,
4201 .vsw = 1,
4202 .vfp = 0,
4203 .vbp = 0,
4204 };
4205
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004206 dssdev->driver->get_resolution(dssdev, &dw, &dh);
4207 timings.x_res = dw;
4208 timings.y_res = dh;
4209
Archit Taneja8af6ff02011-09-05 16:48:27 +05304210 irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
4211 DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
4212
4213 r = omap_dispc_register_isr(dsi_framedone_irq_callback,
4214 (void *) dssdev, irq);
4215 if (r) {
4216 DSSERR("can't get FRAMEDONE irq\n");
4217 return r;
4218 }
4219
4220 dispc_mgr_enable_stallmode(dssdev->manager->id, true);
4221 dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1);
4222
Archit Taneja41721162012-04-26 20:10:46 +05304223 dss_mgr_set_timings(dssdev->manager, &timings);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304224 } else {
4225 dispc_mgr_enable_stallmode(dssdev->manager->id, false);
4226 dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0);
4227
Archit Taneja41721162012-04-26 20:10:46 +05304228 dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004229 }
4230
Archit Taneja8af6ff02011-09-05 16:48:27 +05304231 dispc_mgr_set_lcd_display_type(dssdev->manager->id,
4232 OMAP_DSS_LCD_DISPLAY_TFT);
4233 dispc_mgr_set_tft_data_lines(dssdev->manager->id,
4234 dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004235 return 0;
4236}
4237
4238static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
4239{
Archit Taneja8af6ff02011-09-05 16:48:27 +05304240 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
4241 u32 irq;
Archit Taneja5a8b5722011-05-12 17:26:29 +05304242
Archit Taneja8af6ff02011-09-05 16:48:27 +05304243 irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
4244 DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
Archit Taneja5a8b5722011-05-12 17:26:29 +05304245
Archit Taneja8af6ff02011-09-05 16:48:27 +05304246 omap_dispc_unregister_isr(dsi_framedone_irq_callback,
4247 (void *) dssdev, irq);
4248 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004249}
4250
4251static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
4252{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304253 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004254 struct dsi_clock_info cinfo;
4255 int r;
4256
Tomi Valkeinenc6940a32011-02-22 13:36:10 +02004257 cinfo.regn = dssdev->clocks.dsi.regn;
4258 cinfo.regm = dssdev->clocks.dsi.regm;
4259 cinfo.regm_dispc = dssdev->clocks.dsi.regm_dispc;
4260 cinfo.regm_dsi = dssdev->clocks.dsi.regm_dsi;
Tomi Valkeinenb6e695a2012-03-15 15:22:58 +02004261 r = dsi_calc_clock_rates(dsidev, &cinfo);
Ville Syrjäläebf0a3f2010-04-22 22:50:05 +02004262 if (r) {
4263 DSSERR("Failed to calc dsi clocks\n");
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004264 return r;
Ville Syrjäläebf0a3f2010-04-22 22:50:05 +02004265 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004266
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304267 r = dsi_pll_set_clock_div(dsidev, &cinfo);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004268 if (r) {
4269 DSSERR("Failed to set dsi clocks\n");
4270 return r;
4271 }
4272
4273 return 0;
4274}
4275
4276static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
4277{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304278 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004279 struct dispc_clock_info dispc_cinfo;
4280 int r;
4281 unsigned long long fck;
4282
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304283 fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004284
Archit Tanejae8881662011-04-12 13:52:24 +05304285 dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
4286 dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004287
4288 r = dispc_calc_clock_rates(fck, &dispc_cinfo);
4289 if (r) {
4290 DSSERR("Failed to calc dispc clocks\n");
4291 return r;
4292 }
4293
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03004294 r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004295 if (r) {
4296 DSSERR("Failed to set dispc clocks\n");
4297 return r;
4298 }
4299
4300 return 0;
4301}
4302
4303static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
4304{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304305 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Taneja5a8b5722011-05-12 17:26:29 +05304306 int dsi_module = dsi_get_dsidev_id(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004307 int r;
4308
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304309 r = dsi_pll_init(dsidev, true, true);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004310 if (r)
4311 goto err0;
4312
4313 r = dsi_configure_dsi_clocks(dssdev);
4314 if (r)
4315 goto err1;
4316
Archit Tanejae8881662011-04-12 13:52:24 +05304317 dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
Archit Taneja5a8b5722011-05-12 17:26:29 +05304318 dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src);
Archit Taneja9613c022011-03-22 06:33:36 -05004319 dss_select_lcd_clk_source(dssdev->manager->id,
Archit Tanejae8881662011-04-12 13:52:24 +05304320 dssdev->clocks.dispc.channel.lcd_clk_src);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004321
4322 DSSDBG("PLL OK\n");
4323
4324 r = dsi_configure_dispc_clocks(dssdev);
4325 if (r)
4326 goto err2;
4327
Tomi Valkeinencc5c1852010-10-06 15:18:13 +03004328 r = dsi_cio_init(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004329 if (r)
4330 goto err2;
4331
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304332 _dsi_print_reset_status(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004333
4334 dsi_proto_timings(dssdev);
4335 dsi_set_lp_clk_divisor(dssdev);
4336
4337 if (1)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304338 _dsi_print_reset_status(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004339
4340 r = dsi_proto_config(dssdev);
4341 if (r)
4342 goto err3;
4343
4344 /* enable interface */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304345 dsi_vc_enable(dsidev, 0, 1);
4346 dsi_vc_enable(dsidev, 1, 1);
4347 dsi_vc_enable(dsidev, 2, 1);
4348 dsi_vc_enable(dsidev, 3, 1);
4349 dsi_if_enable(dsidev, 1);
4350 dsi_force_tx_stop_mode_io(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004351
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004352 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004353err3:
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03004354 dsi_cio_uninit(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004355err2:
Archit Taneja89a35e52011-04-12 13:52:23 +05304356 dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
Archit Taneja5a8b5722011-05-12 17:26:29 +05304357 dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK);
Tomi Valkeinen5e785092011-08-10 11:25:36 +03004358 dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK);
4359
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004360err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304361 dsi_pll_uninit(dsidev, true);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004362err0:
4363 return r;
4364}
4365
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03004366static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev,
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03004367 bool disconnect_lanes, bool enter_ulps)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004368{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304369 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304370 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja5a8b5722011-05-12 17:26:29 +05304371 int dsi_module = dsi_get_dsidev_id(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304372
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304373 if (enter_ulps && !dsi->ulps_enabled)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304374 dsi_enter_ulps(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03004375
Ville Syrjäläd7370102010-04-22 22:50:09 +02004376 /* disable interface */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304377 dsi_if_enable(dsidev, 0);
4378 dsi_vc_enable(dsidev, 0, 0);
4379 dsi_vc_enable(dsidev, 1, 0);
4380 dsi_vc_enable(dsidev, 2, 0);
4381 dsi_vc_enable(dsidev, 3, 0);
Ville Syrjäläd7370102010-04-22 22:50:09 +02004382
Archit Taneja89a35e52011-04-12 13:52:23 +05304383 dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
Archit Taneja5a8b5722011-05-12 17:26:29 +05304384 dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK);
Tomi Valkeinen5e785092011-08-10 11:25:36 +03004385 dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK);
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03004386 dsi_cio_uninit(dssdev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304387 dsi_pll_uninit(dsidev, disconnect_lanes);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004388}
4389
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004390int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004391{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304392 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304393 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004394 int r = 0;
4395
4396 DSSDBG("dsi_display_enable\n");
4397
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304398 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004399
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304400 mutex_lock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004401
Tomi Valkeinen05e1d602011-06-23 16:38:21 +03004402 if (dssdev->manager == NULL) {
4403 DSSERR("failed to enable display: no manager\n");
4404 r = -ENODEV;
4405 goto err_start_dev;
4406 }
4407
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004408 r = omap_dss_start_device(dssdev);
4409 if (r) {
4410 DSSERR("failed to start device\n");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004411 goto err_start_dev;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004412 }
4413
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004414 r = dsi_runtime_get(dsidev);
4415 if (r)
4416 goto err_get_dsi;
4417
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304418 dsi_enable_pll_clock(dsidev, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004419
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004420 _dsi_initialize_irq(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004421
4422 r = dsi_display_init_dispc(dssdev);
4423 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004424 goto err_init_dispc;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004425
4426 r = dsi_display_init_dsi(dssdev);
4427 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004428 goto err_init_dsi;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004429
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304430 mutex_unlock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004431
4432 return 0;
4433
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004434err_init_dsi:
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004435 dsi_display_uninit_dispc(dssdev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004436err_init_dispc:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304437 dsi_enable_pll_clock(dsidev, 0);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004438 dsi_runtime_put(dsidev);
4439err_get_dsi:
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004440 omap_dss_stop_device(dssdev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004441err_start_dev:
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304442 mutex_unlock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004443 DSSDBG("dsi_display_enable FAILED\n");
4444 return r;
4445}
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004446EXPORT_SYMBOL(omapdss_dsi_display_enable);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004447
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03004448void omapdss_dsi_display_disable(struct omap_dss_device *dssdev,
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03004449 bool disconnect_lanes, bool enter_ulps)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004450{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304451 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304452 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304453
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004454 DSSDBG("dsi_display_disable\n");
4455
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304456 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004457
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304458 mutex_lock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004459
Tomi Valkeinen15ffa1d2011-06-16 14:34:06 +03004460 dsi_sync_vc(dsidev, 0);
4461 dsi_sync_vc(dsidev, 1);
4462 dsi_sync_vc(dsidev, 2);
4463 dsi_sync_vc(dsidev, 3);
4464
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004465 dsi_display_uninit_dispc(dssdev);
4466
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03004467 dsi_display_uninit_dsi(dssdev, disconnect_lanes, enter_ulps);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004468
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004469 dsi_runtime_put(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304470 dsi_enable_pll_clock(dsidev, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004471
4472 omap_dss_stop_device(dssdev);
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004473
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304474 mutex_unlock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004475}
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004476EXPORT_SYMBOL(omapdss_dsi_display_disable);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004477
Tomi Valkeinen225b6502010-01-11 15:11:01 +02004478int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004479{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304480 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4481 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4482
4483 dsi->te_enabled = enable;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02004484 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004485}
Tomi Valkeinen225b6502010-01-11 15:11:01 +02004486EXPORT_SYMBOL(omapdss_dsi_enable_te);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004487
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004488int dsi_init_display(struct omap_dss_device *dssdev)
4489{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304490 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4491 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4492
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004493 DSSDBG("DSI init\n");
4494
Archit Taneja7e951ee2011-07-22 12:45:04 +05304495 if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
4496 dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
4497 OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
4498 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004499
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304500 if (dsi->vdds_dsi_reg == NULL) {
Tomi Valkeinen5f42f2ce2011-02-22 15:53:46 +02004501 struct regulator *vdds_dsi;
4502
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304503 vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi");
Tomi Valkeinen5f42f2ce2011-02-22 15:53:46 +02004504
4505 if (IS_ERR(vdds_dsi)) {
4506 DSSERR("can't get VDDS_DSI regulator\n");
4507 return PTR_ERR(vdds_dsi);
4508 }
4509
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304510 dsi->vdds_dsi_reg = vdds_dsi;
Tomi Valkeinen5f42f2ce2011-02-22 15:53:46 +02004511 }
4512
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004513 return 0;
4514}
4515
Archit Taneja5ee3c142011-03-02 12:35:53 +05304516int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel)
4517{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304518 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4519 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja5ee3c142011-03-02 12:35:53 +05304520 int i;
4521
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304522 for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
4523 if (!dsi->vc[i].dssdev) {
4524 dsi->vc[i].dssdev = dssdev;
Archit Taneja5ee3c142011-03-02 12:35:53 +05304525 *channel = i;
4526 return 0;
4527 }
4528 }
4529
4530 DSSERR("cannot get VC for display %s", dssdev->name);
4531 return -ENOSPC;
4532}
4533EXPORT_SYMBOL(omap_dsi_request_vc);
4534
4535int omap_dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id)
4536{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304537 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4538 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4539
Archit Taneja5ee3c142011-03-02 12:35:53 +05304540 if (vc_id < 0 || vc_id > 3) {
4541 DSSERR("VC ID out of range\n");
4542 return -EINVAL;
4543 }
4544
4545 if (channel < 0 || channel > 3) {
4546 DSSERR("Virtual Channel out of range\n");
4547 return -EINVAL;
4548 }
4549
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304550 if (dsi->vc[channel].dssdev != dssdev) {
Archit Taneja5ee3c142011-03-02 12:35:53 +05304551 DSSERR("Virtual Channel not allocated to display %s\n",
4552 dssdev->name);
4553 return -EINVAL;
4554 }
4555
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304556 dsi->vc[channel].vc_id = vc_id;
Archit Taneja5ee3c142011-03-02 12:35:53 +05304557
4558 return 0;
4559}
4560EXPORT_SYMBOL(omap_dsi_set_vc_id);
4561
4562void omap_dsi_release_vc(struct omap_dss_device *dssdev, int channel)
4563{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304564 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4565 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4566
Archit Taneja5ee3c142011-03-02 12:35:53 +05304567 if ((channel >= 0 && channel <= 3) &&
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304568 dsi->vc[channel].dssdev == dssdev) {
4569 dsi->vc[channel].dssdev = NULL;
4570 dsi->vc[channel].vc_id = 0;
Archit Taneja5ee3c142011-03-02 12:35:53 +05304571 }
4572}
4573EXPORT_SYMBOL(omap_dsi_release_vc);
4574
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304575void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev)
Tomi Valkeinene406f902010-06-09 15:28:12 +03004576{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304577 if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 7, 1) != 1)
Archit Taneja067a57e2011-03-02 11:57:25 +05304578 DSSERR("%s (%s) not active\n",
Archit Taneja89a35e52011-04-12 13:52:23 +05304579 dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC),
4580 dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC));
Tomi Valkeinene406f902010-06-09 15:28:12 +03004581}
4582
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304583void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev)
Tomi Valkeinene406f902010-06-09 15:28:12 +03004584{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304585 if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 8, 1) != 1)
Archit Taneja067a57e2011-03-02 11:57:25 +05304586 DSSERR("%s (%s) not active\n",
Archit Taneja89a35e52011-04-12 13:52:23 +05304587 dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI),
4588 dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI));
Tomi Valkeinene406f902010-06-09 15:28:12 +03004589}
4590
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304591static void dsi_calc_clock_param_ranges(struct platform_device *dsidev)
Taneja, Archit49641112011-03-14 23:28:23 -05004592{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304593 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4594
4595 dsi->regn_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGN);
4596 dsi->regm_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM);
4597 dsi->regm_dispc_max =
4598 dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DISPC);
4599 dsi->regm_dsi_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DSI);
4600 dsi->fint_min = dss_feat_get_param_min(FEAT_PARAM_DSIPLL_FINT);
4601 dsi->fint_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_FINT);
4602 dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);
Taneja, Archit49641112011-03-14 23:28:23 -05004603}
4604
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004605static int dsi_get_clocks(struct platform_device *dsidev)
4606{
4607 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4608 struct clk *clk;
4609
4610 clk = clk_get(&dsidev->dev, "fck");
4611 if (IS_ERR(clk)) {
4612 DSSERR("can't get fck\n");
4613 return PTR_ERR(clk);
4614 }
4615
4616 dsi->dss_clk = clk;
4617
Tomi Valkeinenbfe4f8d2011-08-04 11:22:54 +03004618 clk = clk_get(&dsidev->dev, "sys_clk");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004619 if (IS_ERR(clk)) {
4620 DSSERR("can't get sys_clk\n");
4621 clk_put(dsi->dss_clk);
4622 dsi->dss_clk = NULL;
4623 return PTR_ERR(clk);
4624 }
4625
4626 dsi->sys_clk = clk;
4627
4628 return 0;
4629}
4630
4631static void dsi_put_clocks(struct platform_device *dsidev)
4632{
4633 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4634
4635 if (dsi->dss_clk)
4636 clk_put(dsi->dss_clk);
4637 if (dsi->sys_clk)
4638 clk_put(dsi->sys_clk);
4639}
4640
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03004641/* DSI1 HW IP initialisation */
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03004642static int omap_dsihw_probe(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004643{
4644 u32 rev;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304645 int r, i, dsi_module = dsi_get_dsidev_id(dsidev);
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +00004646 struct resource *dsi_mem;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304647 struct dsi_data *dsi;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004648
Julia Lawall6e2a14d2012-01-24 14:00:45 +01004649 dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL);
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004650 if (!dsi)
4651 return -ENOMEM;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304652
4653 dsi->pdev = dsidev;
4654 dsi_pdev_map[dsi_module] = dsidev;
4655 dev_set_drvdata(&dsidev->dev, dsi);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304656
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304657 spin_lock_init(&dsi->irq_lock);
4658 spin_lock_init(&dsi->errors_lock);
4659 dsi->errors = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004660
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02004661#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304662 spin_lock_init(&dsi->irq_stats_lock);
4663 dsi->irq_stats.last_reset = jiffies;
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02004664#endif
4665
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304666 mutex_init(&dsi->lock);
4667 sema_init(&dsi->bus_lock, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004668
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304669 INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work,
4670 dsi_framedone_timeout_work_callback);
4671
4672#ifdef DSI_CATCH_MISSING_TE
4673 init_timer(&dsi->te_timer);
4674 dsi->te_timer.function = dsi_te_timeout;
4675 dsi->te_timer.data = 0;
4676#endif
4677 dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0);
4678 if (!dsi_mem) {
4679 DSSERR("can't get IORESOURCE_MEM DSI\n");
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004680 return -EINVAL;
archit tanejaaffe3602011-02-23 08:41:03 +00004681 }
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004682
Julia Lawall6e2a14d2012-01-24 14:00:45 +01004683 dsi->base = devm_ioremap(&dsidev->dev, dsi_mem->start,
4684 resource_size(dsi_mem));
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304685 if (!dsi->base) {
4686 DSSERR("can't ioremap DSI\n");
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004687 return -ENOMEM;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304688 }
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004689
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304690 dsi->irq = platform_get_irq(dsi->pdev, 0);
4691 if (dsi->irq < 0) {
4692 DSSERR("platform_get_irq failed\n");
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004693 return -ENODEV;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304694 }
archit tanejaaffe3602011-02-23 08:41:03 +00004695
Julia Lawall6e2a14d2012-01-24 14:00:45 +01004696 r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
4697 IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
archit tanejaaffe3602011-02-23 08:41:03 +00004698 if (r < 0) {
4699 DSSERR("request_irq failed\n");
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004700 return r;
archit tanejaaffe3602011-02-23 08:41:03 +00004701 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004702
Archit Taneja5ee3c142011-03-02 12:35:53 +05304703 /* DSI VCs initialization */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304704 for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
Archit Tanejad6049142011-08-22 11:58:08 +05304705 dsi->vc[i].source = DSI_VC_SOURCE_L4;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304706 dsi->vc[i].dssdev = NULL;
4707 dsi->vc[i].vc_id = 0;
Archit Taneja5ee3c142011-03-02 12:35:53 +05304708 }
4709
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304710 dsi_calc_clock_param_ranges(dsidev);
Taneja, Archit49641112011-03-14 23:28:23 -05004711
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004712 r = dsi_get_clocks(dsidev);
4713 if (r)
4714 return r;
4715
4716 pm_runtime_enable(&dsidev->dev);
4717
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004718 r = dsi_runtime_get(dsidev);
4719 if (r)
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004720 goto err_runtime_get;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004721
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304722 rev = dsi_read_reg(dsidev, DSI_REVISION);
4723 dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004724 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
4725
Tomi Valkeinend9820852011-10-12 15:05:59 +03004726 /* DSI on OMAP3 doesn't have register DSI_GNQ, set number
4727 * of data to 3 by default */
4728 if (dss_has_feature(FEAT_DSI_GNQ))
4729 /* NB_DATA_LANES */
4730 dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9);
4731 else
4732 dsi->num_lanes_supported = 3;
Archit Taneja75d72472011-05-16 15:17:08 +05304733
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004734 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004735
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004736 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004737
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004738err_runtime_get:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004739 pm_runtime_disable(&dsidev->dev);
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02004740 dsi_put_clocks(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004741 return r;
4742}
4743
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03004744static int omap_dsihw_remove(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004745{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304746 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4747
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03004748 WARN_ON(dsi->scp_clk_refcount > 0);
4749
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004750 pm_runtime_disable(&dsidev->dev);
4751
4752 dsi_put_clocks(dsidev);
4753
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304754 if (dsi->vdds_dsi_reg != NULL) {
4755 if (dsi->vdds_dsi_enabled) {
4756 regulator_disable(dsi->vdds_dsi_reg);
4757 dsi->vdds_dsi_enabled = false;
Tomi Valkeinen88257b22010-12-20 16:26:22 +02004758 }
4759
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304760 regulator_put(dsi->vdds_dsi_reg);
4761 dsi->vdds_dsi_reg = NULL;
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004762 }
4763
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004764 return 0;
4765}
4766
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004767static int dsi_runtime_suspend(struct device *dev)
4768{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004769 dispc_runtime_put();
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004770
4771 return 0;
4772}
4773
4774static int dsi_runtime_resume(struct device *dev)
4775{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004776 int r;
4777
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004778 r = dispc_runtime_get();
4779 if (r)
Tomi Valkeinen852f0832012-02-17 17:58:04 +02004780 return r;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004781
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004782 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004783}
4784
4785static const struct dev_pm_ops dsi_pm_ops = {
4786 .runtime_suspend = dsi_runtime_suspend,
4787 .runtime_resume = dsi_runtime_resume,
4788};
4789
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03004790static struct platform_driver omap_dsihw_driver = {
4791 .probe = omap_dsihw_probe,
4792 .remove = omap_dsihw_remove,
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004793 .driver = {
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03004794 .name = "omapdss_dsi",
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004795 .owner = THIS_MODULE,
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004796 .pm = &dsi_pm_ops,
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004797 },
4798};
4799
4800int dsi_init_platform_driver(void)
4801{
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03004802 return platform_driver_register(&omap_dsihw_driver);
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004803}
4804
4805void dsi_uninit_platform_driver(void)
4806{
Tomi Valkeinen04c742c2012-02-23 15:32:37 +02004807 platform_driver_unregister(&omap_dsihw_driver);
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00004808}