blob: 1af2c5329c99152a050c23246f0df59a9903c953 [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>
Laurent Pinchart9e1305d2017-08-05 01:43:53 +030023#include <linux/mfd/syscon.h>
24#include <linux/regmap.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020025#include <linux/io.h>
26#include <linux/clk.h>
27#include <linux/device.h>
28#include <linux/err.h>
29#include <linux/interrupt.h>
30#include <linux/delay.h>
31#include <linux/mutex.h>
Paul Gortmaker355b2002011-07-03 16:17:28 -040032#include <linux/module.h>
Tomi Valkeinenb9eb5d72010-01-11 16:33:56 +020033#include <linux/semaphore.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020034#include <linux/seq_file.h>
35#include <linux/platform_device.h>
36#include <linux/regulator/consumer.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020037#include <linux/wait.h>
Tomi Valkeinen18946f62010-01-12 14:16:41 +020038#include <linux/workqueue.h>
Tomi Valkeinen40885ab2010-07-28 15:53:38 +030039#include <linux/sched.h>
Archit Tanejaf1da39d2011-05-12 17:26:27 +053040#include <linux/slab.h>
Archit Taneja5a8b5722011-05-12 17:26:29 +053041#include <linux/debugfs.h>
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +030042#include <linux/pm_runtime.h>
Tomi Valkeinen6274a612012-08-21 15:35:42 +030043#include <linux/of.h>
Rob Herring09bffa62017-03-22 08:26:08 -050044#include <linux/of_graph.h>
Tomi Valkeinen6274a612012-08-21 15:35:42 +030045#include <linux/of_platform.h>
Tomi Valkeinen736e60d2015-06-04 15:22:23 +030046#include <linux/component.h>
Laurent Pinchart44d8ca12017-08-05 01:44:10 +030047#include <linux/sys_soc.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020048
Archit Taneja7a7c48f2011-08-25 18:25:03 +053049#include <video/mipi_display.h>
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020050
Peter Ujfalusi32043da2016-05-27 14:40:49 +030051#include "omapdss.h"
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020052#include "dss.h"
Archit Taneja819d8072011-03-01 11:54:00 +053053#include "dss_features.h"
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020054
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020055#define DSI_CATCH_MISSING_TE
56
Tomi Valkeinen68104462013-12-17 13:53:28 +020057struct dsi_reg { u16 module; u16 idx; };
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020058
Tomi Valkeinen68104462013-12-17 13:53:28 +020059#define DSI_REG(mod, idx) ((const struct dsi_reg) { mod, idx })
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020060
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +020061/* DSI Protocol Engine */
62
Tomi Valkeinen68104462013-12-17 13:53:28 +020063#define DSI_PROTO 0
64#define DSI_PROTO_SZ 0x200
65
66#define DSI_REVISION DSI_REG(DSI_PROTO, 0x0000)
67#define DSI_SYSCONFIG DSI_REG(DSI_PROTO, 0x0010)
68#define DSI_SYSSTATUS DSI_REG(DSI_PROTO, 0x0014)
69#define DSI_IRQSTATUS DSI_REG(DSI_PROTO, 0x0018)
70#define DSI_IRQENABLE DSI_REG(DSI_PROTO, 0x001C)
71#define DSI_CTRL DSI_REG(DSI_PROTO, 0x0040)
72#define DSI_GNQ DSI_REG(DSI_PROTO, 0x0044)
73#define DSI_COMPLEXIO_CFG1 DSI_REG(DSI_PROTO, 0x0048)
74#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(DSI_PROTO, 0x004C)
75#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(DSI_PROTO, 0x0050)
76#define DSI_CLK_CTRL DSI_REG(DSI_PROTO, 0x0054)
77#define DSI_TIMING1 DSI_REG(DSI_PROTO, 0x0058)
78#define DSI_TIMING2 DSI_REG(DSI_PROTO, 0x005C)
79#define DSI_VM_TIMING1 DSI_REG(DSI_PROTO, 0x0060)
80#define DSI_VM_TIMING2 DSI_REG(DSI_PROTO, 0x0064)
81#define DSI_VM_TIMING3 DSI_REG(DSI_PROTO, 0x0068)
82#define DSI_CLK_TIMING DSI_REG(DSI_PROTO, 0x006C)
83#define DSI_TX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0070)
84#define DSI_RX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0074)
85#define DSI_COMPLEXIO_CFG2 DSI_REG(DSI_PROTO, 0x0078)
86#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(DSI_PROTO, 0x007C)
87#define DSI_VM_TIMING4 DSI_REG(DSI_PROTO, 0x0080)
88#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(DSI_PROTO, 0x0084)
89#define DSI_VM_TIMING5 DSI_REG(DSI_PROTO, 0x0088)
90#define DSI_VM_TIMING6 DSI_REG(DSI_PROTO, 0x008C)
91#define DSI_VM_TIMING7 DSI_REG(DSI_PROTO, 0x0090)
92#define DSI_STOPCLK_TIMING DSI_REG(DSI_PROTO, 0x0094)
93#define DSI_VC_CTRL(n) DSI_REG(DSI_PROTO, 0x0100 + (n * 0x20))
94#define DSI_VC_TE(n) DSI_REG(DSI_PROTO, 0x0104 + (n * 0x20))
95#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0108 + (n * 0x20))
96#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(DSI_PROTO, 0x010C + (n * 0x20))
97#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0110 + (n * 0x20))
98#define DSI_VC_IRQSTATUS(n) DSI_REG(DSI_PROTO, 0x0118 + (n * 0x20))
99#define DSI_VC_IRQENABLE(n) DSI_REG(DSI_PROTO, 0x011C + (n * 0x20))
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200100
101/* DSIPHY_SCP */
102
Tomi Valkeinen68104462013-12-17 13:53:28 +0200103#define DSI_PHY 1
104#define DSI_PHY_OFFSET 0x200
105#define DSI_PHY_SZ 0x40
106
107#define DSI_DSIPHY_CFG0 DSI_REG(DSI_PHY, 0x0000)
108#define DSI_DSIPHY_CFG1 DSI_REG(DSI_PHY, 0x0004)
109#define DSI_DSIPHY_CFG2 DSI_REG(DSI_PHY, 0x0008)
110#define DSI_DSIPHY_CFG5 DSI_REG(DSI_PHY, 0x0014)
111#define DSI_DSIPHY_CFG10 DSI_REG(DSI_PHY, 0x0028)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200112
113/* DSI_PLL_CTRL_SCP */
114
Tomi Valkeinen68104462013-12-17 13:53:28 +0200115#define DSI_PLL 2
116#define DSI_PLL_OFFSET 0x300
117#define DSI_PLL_SZ 0x20
118
119#define DSI_PLL_CONTROL DSI_REG(DSI_PLL, 0x0000)
120#define DSI_PLL_STATUS DSI_REG(DSI_PLL, 0x0004)
121#define DSI_PLL_GO DSI_REG(DSI_PLL, 0x0008)
122#define DSI_PLL_CONFIGURATION1 DSI_REG(DSI_PLL, 0x000C)
123#define DSI_PLL_CONFIGURATION2 DSI_REG(DSI_PLL, 0x0010)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200124
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530125#define REG_GET(dsidev, idx, start, end) \
126 FLD_GET(dsi_read_reg(dsidev, idx), start, end)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200127
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530128#define REG_FLD_MOD(dsidev, idx, val, start, end) \
129 dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end))
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200130
131/* Global interrupts */
132#define DSI_IRQ_VC0 (1 << 0)
133#define DSI_IRQ_VC1 (1 << 1)
134#define DSI_IRQ_VC2 (1 << 2)
135#define DSI_IRQ_VC3 (1 << 3)
136#define DSI_IRQ_WAKEUP (1 << 4)
137#define DSI_IRQ_RESYNC (1 << 5)
138#define DSI_IRQ_PLL_LOCK (1 << 7)
139#define DSI_IRQ_PLL_UNLOCK (1 << 8)
140#define DSI_IRQ_PLL_RECALL (1 << 9)
141#define DSI_IRQ_COMPLEXIO_ERR (1 << 10)
142#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14)
143#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15)
144#define DSI_IRQ_TE_TRIGGER (1 << 16)
145#define DSI_IRQ_ACK_TRIGGER (1 << 17)
146#define DSI_IRQ_SYNC_LOST (1 << 18)
147#define DSI_IRQ_LDO_POWER_GOOD (1 << 19)
148#define DSI_IRQ_TA_TIMEOUT (1 << 20)
149#define DSI_IRQ_ERROR_MASK \
150 (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
Dan Carpenter00355412015-11-23 21:22:36 +0300151 DSI_IRQ_TA_TIMEOUT)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200152#define DSI_IRQ_CHANNEL_MASK 0xf
153
154/* Virtual channel interrupts */
155#define DSI_VC_IRQ_CS (1 << 0)
156#define DSI_VC_IRQ_ECC_CORR (1 << 1)
157#define DSI_VC_IRQ_PACKET_SENT (1 << 2)
158#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3)
159#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4)
160#define DSI_VC_IRQ_BTA (1 << 5)
161#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6)
162#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7)
163#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
164#define DSI_VC_IRQ_ERROR_MASK \
165 (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
166 DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
167 DSI_VC_IRQ_FIFO_TX_UDF)
168
169/* ComplexIO interrupts */
170#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0)
171#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1)
172#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200173#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3)
174#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200175#define DSI_CIO_IRQ_ERRESC1 (1 << 5)
176#define DSI_CIO_IRQ_ERRESC2 (1 << 6)
177#define DSI_CIO_IRQ_ERRESC3 (1 << 7)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200178#define DSI_CIO_IRQ_ERRESC4 (1 << 8)
179#define DSI_CIO_IRQ_ERRESC5 (1 << 9)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200180#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10)
181#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11)
182#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200183#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13)
184#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200185#define DSI_CIO_IRQ_STATEULPS1 (1 << 15)
186#define DSI_CIO_IRQ_STATEULPS2 (1 << 16)
187#define DSI_CIO_IRQ_STATEULPS3 (1 << 17)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200188#define DSI_CIO_IRQ_STATEULPS4 (1 << 18)
189#define DSI_CIO_IRQ_STATEULPS5 (1 << 19)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200190#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20)
191#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21)
192#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22)
193#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23)
194#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24)
195#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
Tomi Valkeinen67056152011-03-24 16:30:17 +0200196#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26)
197#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27)
198#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28)
199#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200200#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
201#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
Tomi Valkeinenbbecb502010-05-10 14:35:33 +0300202#define DSI_CIO_IRQ_ERROR_MASK \
203 (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \
Tomi Valkeinen67056152011-03-24 16:30:17 +0200204 DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \
205 DSI_CIO_IRQ_ERRSYNCESC5 | \
206 DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \
207 DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \
208 DSI_CIO_IRQ_ERRESC5 | \
209 DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \
210 DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \
211 DSI_CIO_IRQ_ERRCONTROL5 | \
Tomi Valkeinenbbecb502010-05-10 14:35:33 +0300212 DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \
213 DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \
Tomi Valkeinen67056152011-03-24 16:30:17 +0200214 DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \
215 DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \
216 DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200217
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200218typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
219
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +0200220static int dsi_display_init_dispc(struct platform_device *dsidev,
Tomi Valkeinen0674d382015-11-05 10:01:02 +0200221 enum omap_channel channel);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +0200222static void dsi_display_uninit_dispc(struct platform_device *dsidev,
Tomi Valkeinen0674d382015-11-05 10:01:02 +0200223 enum omap_channel channel);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +0200224
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +0300225static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel);
226
Tomi Valkeinenacf604b2014-11-07 13:13:24 +0200227/* DSI PLL HSDIV indices */
228#define HSDIV_DISPC 0
229#define HSDIV_DSI 1
230
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200231#define DSI_MAX_NR_ISRS 2
Tomi Valkeinen739a7f42011-10-13 11:22:06 +0300232#define DSI_MAX_NR_LANES 5
233
Laurent Pinchart742e6932017-08-05 01:43:57 +0300234enum dsi_model {
235 DSI_MODEL_OMAP3,
236 DSI_MODEL_OMAP4,
237 DSI_MODEL_OMAP5,
238};
239
Tomi Valkeinen739a7f42011-10-13 11:22:06 +0300240enum dsi_lane_function {
241 DSI_LANE_UNUSED = 0,
242 DSI_LANE_CLK,
243 DSI_LANE_DATA1,
244 DSI_LANE_DATA2,
245 DSI_LANE_DATA3,
246 DSI_LANE_DATA4,
247};
248
249struct dsi_lane_config {
250 enum dsi_lane_function function;
251 u8 polarity;
252};
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200253
254struct dsi_isr_data {
255 omap_dsi_isr_t isr;
256 void *arg;
257 u32 mask;
258};
259
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200260enum fifo_size {
261 DSI_FIFO_SIZE_0 = 0,
262 DSI_FIFO_SIZE_32 = 1,
263 DSI_FIFO_SIZE_64 = 2,
264 DSI_FIFO_SIZE_96 = 3,
265 DSI_FIFO_SIZE_128 = 4,
266};
267
Archit Tanejad6049142011-08-22 11:58:08 +0530268enum dsi_vc_source {
269 DSI_VC_SOURCE_L4 = 0,
270 DSI_VC_SOURCE_VP,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200271};
272
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200273struct dsi_irq_stats {
274 unsigned long last_reset;
275 unsigned irq_count;
276 unsigned dsi_irqs[32];
277 unsigned vc_irqs[4][32];
278 unsigned cio_irqs[32];
279};
280
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200281struct dsi_isr_tables {
282 struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS];
283 struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS];
284 struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS];
285};
286
Tomi Valkeinenf1e00012013-03-05 17:21:35 +0200287struct dsi_clk_calc_ctx {
288 struct platform_device *dsidev;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300289 struct dss_pll *pll;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +0200290
291 /* inputs */
292
293 const struct omap_dss_dsi_config *config;
294
295 unsigned long req_pck_min, req_pck_nom, req_pck_max;
296
297 /* outputs */
298
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300299 struct dss_pll_clock_info dsi_cinfo;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +0200300 struct dispc_clock_info dispc_cinfo;
301
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300302 struct videomode vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +0200303 struct omap_dss_dsi_videomode_timings dsi_vm;
304};
305
Tomi Valkeinen7b71c412014-08-06 15:45:26 +0300306struct dsi_lp_clock_info {
307 unsigned long lp_clk;
308 u16 lp_clk_div;
309};
310
Laurent Pinchart742e6932017-08-05 01:43:57 +0300311struct dsi_module_id_data {
312 u32 address;
313 int id;
314};
315
Laurent Pinchart44d8ca12017-08-05 01:44:10 +0300316enum dsi_quirks {
317 DSI_QUIRK_PLL_PWR_BUG = (1 << 0), /* DSI-PLL power command 0x3 is not working */
318 DSI_QUIRK_DCS_CMD_CONFIG_VC = (1 << 1),
319 DSI_QUIRK_VC_OCP_WIDTH = (1 << 2),
320 DSI_QUIRK_REVERSE_TXCLKESC = (1 << 3),
321 DSI_QUIRK_GNQ = (1 << 4),
322 DSI_QUIRK_PHY_DCC = (1 << 5),
323};
324
Laurent Pinchart742e6932017-08-05 01:43:57 +0300325struct dsi_of_data {
326 enum dsi_model model;
327 const struct dss_pll_hw *pll_hw;
328 const struct dsi_module_id_data *modules;
Laurent Pinchart44d8ca12017-08-05 01:44:10 +0300329 enum dsi_quirks quirks;
Laurent Pinchart742e6932017-08-05 01:43:57 +0300330};
331
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530332struct dsi_data {
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +0000333 struct platform_device *pdev;
Tomi Valkeinen68104462013-12-17 13:53:28 +0200334 void __iomem *proto_base;
335 void __iomem *phy_base;
336 void __iomem *pll_base;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300337
Laurent Pinchart742e6932017-08-05 01:43:57 +0300338 const struct dsi_of_data *data;
Tomi Valkeinen11ee9602012-03-09 16:07:39 +0200339 int module_id;
340
archit tanejaaffe3602011-02-23 08:41:03 +0000341 int irq;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200342
Tomi Valkeinen0925afc2014-04-11 13:49:55 +0300343 bool is_enabled;
344
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300345 struct clk *dss_clk;
Laurent Pinchart9e1305d2017-08-05 01:43:53 +0300346 struct regmap *syscon;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +0300347
Tomi Valkeinena0d269e2012-11-27 17:05:54 +0200348 struct dispc_clock_info user_dispc_cinfo;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300349 struct dss_pll_clock_info user_dsi_cinfo;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200350
Tomi Valkeinen7b71c412014-08-06 15:45:26 +0300351 struct dsi_lp_clock_info user_lp_cinfo;
352 struct dsi_lp_clock_info current_lp_cinfo;
353
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300354 struct dss_pll pll;
355
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +0300356 bool vdds_dsi_enabled;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200357 struct regulator *vdds_dsi_reg;
358
359 struct {
Archit Tanejad6049142011-08-22 11:58:08 +0530360 enum dsi_vc_source source;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200361 struct omap_dss_device *dssdev;
Tomi Valkeinen558c73e2013-09-25 14:40:06 +0300362 enum fifo_size tx_fifo_size;
363 enum fifo_size rx_fifo_size;
Archit Taneja5ee3c142011-03-02 12:35:53 +0530364 int vc_id;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200365 } vc[4];
366
367 struct mutex lock;
Tomi Valkeinenb9eb5d72010-01-11 16:33:56 +0200368 struct semaphore bus_lock;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200369
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200370 spinlock_t irq_lock;
371 struct dsi_isr_tables isr_tables;
372 /* space for a copy used by the interrupt handler */
373 struct dsi_isr_tables isr_tables_copy;
374
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200375 int update_channel;
Tomi Valkeinen477fed72013-10-02 14:41:24 +0300376#ifdef DSI_PERF_MEASURE
Tomi Valkeinen5476e742011-11-03 16:34:20 +0200377 unsigned update_bytes;
378#endif
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200379
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200380 bool te_enabled;
Tomi Valkeinen40885ab2010-07-28 15:53:38 +0300381 bool ulps_enabled;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200382
Tomi Valkeinen18946f62010-01-12 14:16:41 +0200383 void (*framedone_callback)(int, void *);
384 void *framedone_data;
385
386 struct delayed_work framedone_timeout_work;
387
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200388#ifdef DSI_CATCH_MISSING_TE
389 struct timer_list te_timer;
390#endif
391
392 unsigned long cache_req_pck;
393 unsigned long cache_clk_freq;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300394 struct dss_pll_clock_info cache_cinfo;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200395
396 u32 errors;
397 spinlock_t errors_lock;
Tomi Valkeinen477fed72013-10-02 14:41:24 +0300398#ifdef DSI_PERF_MEASURE
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200399 ktime_t perf_setup_time;
400 ktime_t perf_start_time;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200401#endif
402 int debug_read;
403 int debug_write;
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200404
405#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
406 spinlock_t irq_stats_lock;
407 struct dsi_irq_stats irq_stats;
408#endif
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +0300409
Tomi Valkeinend9820852011-10-12 15:05:59 +0300410 unsigned num_lanes_supported;
Tomi Valkeinen99322572013-03-05 10:37:02 +0200411 unsigned line_buffer_size;
Archit Taneja75d72472011-05-16 15:17:08 +0530412
Tomi Valkeinen739a7f42011-10-13 11:22:06 +0300413 struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
414 unsigned num_lanes_used;
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +0300415
416 unsigned scp_clk_refcount;
Archit Taneja7d2572f2012-06-29 14:31:07 +0530417
418 struct dss_lcd_mgr_config mgr_config;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300419 struct videomode vm;
Archit Taneja02c39602012-08-10 15:01:33 +0530420 enum omap_dss_dsi_pixel_format pix_fmt;
Archit Tanejadca2b152012-08-16 18:02:00 +0530421 enum omap_dss_dsi_mode mode;
Archit Taneja0b3ffe32012-08-13 22:13:39 +0530422 struct omap_dss_dsi_videomode_timings vm_timings;
Archit Taneja81b87f52012-09-26 16:30:49 +0530423
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300424 struct omap_dss_device output;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530425};
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200426
Archit Taneja2e868db2011-05-12 17:26:28 +0530427struct dsi_packet_sent_handler_data {
428 struct platform_device *dsidev;
429 struct completion *completion;
430};
431
Tomi Valkeinen477fed72013-10-02 14:41:24 +0300432#ifdef DSI_PERF_MEASURE
Rusty Russell90ab5ee2012-01-13 09:32:20 +1030433static bool dsi_perf;
434module_param(dsi_perf, bool, 0644);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200435#endif
436
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530437static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev)
438{
439 return dev_get_drvdata(&dsidev->dev);
440}
441
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530442static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev)
443{
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +0300444 return to_platform_device(dssdev->dev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530445}
446
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +0300447static struct platform_device *dsi_get_dsidev_from_id(int module)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530448{
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300449 struct omap_dss_device *out;
Archit Taneja400e65d2012-07-04 13:48:34 +0530450 enum omap_dss_output_id id;
451
Tomi Valkeinen78e7f252012-10-15 12:48:11 +0300452 switch (module) {
453 case 0:
454 id = OMAP_DSS_OUTPUT_DSI1;
455 break;
456 case 1:
457 id = OMAP_DSS_OUTPUT_DSI2;
458 break;
459 default:
460 return NULL;
461 }
Archit Taneja400e65d2012-07-04 13:48:34 +0530462
463 out = omap_dss_get_output(id);
464
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +0300465 return out ? to_platform_device(out->dev) : NULL;
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530466}
467
468static inline void dsi_write_reg(struct platform_device *dsidev,
469 const struct dsi_reg idx, u32 val)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200470{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530471 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen68104462013-12-17 13:53:28 +0200472 void __iomem *base;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530473
Tomi Valkeinen68104462013-12-17 13:53:28 +0200474 switch(idx.module) {
475 case DSI_PROTO: base = dsi->proto_base; break;
476 case DSI_PHY: base = dsi->phy_base; break;
477 case DSI_PLL: base = dsi->pll_base; break;
478 default: return;
479 }
480
481 __raw_writel(val, base + idx.idx);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200482}
483
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530484static inline u32 dsi_read_reg(struct platform_device *dsidev,
485 const struct dsi_reg idx)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200486{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530487 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen68104462013-12-17 13:53:28 +0200488 void __iomem *base;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530489
Tomi Valkeinen68104462013-12-17 13:53:28 +0200490 switch(idx.module) {
491 case DSI_PROTO: base = dsi->proto_base; break;
492 case DSI_PHY: base = dsi->phy_base; break;
493 case DSI_PLL: base = dsi->pll_base; break;
494 default: return 0;
495 }
496
497 return __raw_readl(base + idx.idx);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200498}
499
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +0300500static void dsi_bus_lock(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200501{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530502 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
503 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
504
505 down(&dsi->bus_lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200506}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200507
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +0300508static void dsi_bus_unlock(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200509{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530510 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
511 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
512
513 up(&dsi->bus_lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200514}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200515
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530516static bool dsi_bus_is_locked(struct platform_device *dsidev)
Tomi Valkeinen4f765022010-01-18 16:27:52 +0200517{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530518 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
519
520 return dsi->bus_lock.count == 0;
Tomi Valkeinen4f765022010-01-18 16:27:52 +0200521}
522
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +0200523static void dsi_completion_handler(void *data, u32 mask)
524{
525 complete((struct completion *)data);
526}
527
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530528static inline int wait_for_bit_change(struct platform_device *dsidev,
529 const struct dsi_reg idx, int bitnum, int value)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200530{
Tomi Valkeinen3b984092011-10-13 19:06:49 +0300531 unsigned long timeout;
532 ktime_t wait;
533 int t;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200534
Tomi Valkeinen3b984092011-10-13 19:06:49 +0300535 /* first busyloop to see if the bit changes right away */
536 t = 100;
537 while (t-- > 0) {
538 if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
539 return value;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200540 }
541
Tomi Valkeinen3b984092011-10-13 19:06:49 +0300542 /* then loop for 500ms, sleeping for 1ms in between */
543 timeout = jiffies + msecs_to_jiffies(500);
544 while (time_before(jiffies, timeout)) {
545 if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
546 return value;
547
548 wait = ns_to_ktime(1000 * 1000);
549 set_current_state(TASK_UNINTERRUPTIBLE);
550 schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
551 }
552
553 return !value;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200554}
555
Tomi Valkeinen892fdcb2015-11-10 15:50:53 +0200556static u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
Archit Tanejaa3b3cc22011-09-08 18:42:16 +0530557{
558 switch (fmt) {
559 case OMAP_DSS_DSI_FMT_RGB888:
560 case OMAP_DSS_DSI_FMT_RGB666:
561 return 24;
562 case OMAP_DSS_DSI_FMT_RGB666_PACKED:
563 return 18;
564 case OMAP_DSS_DSI_FMT_RGB565:
565 return 16;
566 default:
567 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +0300568 return 0;
Archit Tanejaa3b3cc22011-09-08 18:42:16 +0530569 }
570}
571
Tomi Valkeinen477fed72013-10-02 14:41:24 +0300572#ifdef DSI_PERF_MEASURE
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530573static void dsi_perf_mark_setup(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200574{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530575 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
576 dsi->perf_setup_time = ktime_get();
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200577}
578
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530579static void dsi_perf_mark_start(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200580{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530581 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
582 dsi->perf_start_time = ktime_get();
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200583}
584
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530585static void dsi_perf_show(struct platform_device *dsidev, const char *name)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200586{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530587 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200588 ktime_t t, setup_time, trans_time;
589 u32 total_bytes;
590 u32 setup_us, trans_us, total_us;
591
592 if (!dsi_perf)
593 return;
594
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200595 t = ktime_get();
596
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530597 setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200598 setup_us = (u32)ktime_to_us(setup_time);
599 if (setup_us == 0)
600 setup_us = 1;
601
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530602 trans_time = ktime_sub(t, dsi->perf_start_time);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200603 trans_us = (u32)ktime_to_us(trans_time);
604 if (trans_us == 0)
605 trans_us = 1;
606
607 total_us = setup_us + trans_us;
608
Tomi Valkeinen5476e742011-11-03 16:34:20 +0200609 total_bytes = dsi->update_bytes;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200610
Joe Perches8dfe1622017-02-28 04:55:54 -0800611 pr_info("DSI(%s): %u us + %u us = %u us (%uHz), %u bytes, %u kbytes/sec\n",
612 name,
613 setup_us,
614 trans_us,
615 total_us,
616 1000 * 1000 / total_us,
617 total_bytes,
618 total_bytes * 1000 / total_us);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200619}
620#else
Tomi Valkeinen4a9a5e32011-05-23 16:36:09 +0300621static inline void dsi_perf_mark_setup(struct platform_device *dsidev)
622{
623}
624
625static inline void dsi_perf_mark_start(struct platform_device *dsidev)
626{
627}
628
629static inline void dsi_perf_show(struct platform_device *dsidev,
630 const char *name)
631{
632}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200633#endif
634
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530635static int verbose_irq;
636
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200637static void print_irq_status(u32 status)
638{
Tomi Valkeinend80d4992011-03-02 15:53:07 +0200639 if (status == 0)
640 return;
641
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530642 if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200643 return;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200644
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530645#define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : ""
646
647 pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
648 status,
649 verbose_irq ? PIS(VC0) : "",
650 verbose_irq ? PIS(VC1) : "",
651 verbose_irq ? PIS(VC2) : "",
652 verbose_irq ? PIS(VC3) : "",
653 PIS(WAKEUP),
654 PIS(RESYNC),
655 PIS(PLL_LOCK),
656 PIS(PLL_UNLOCK),
657 PIS(PLL_RECALL),
658 PIS(COMPLEXIO_ERR),
659 PIS(HS_TX_TIMEOUT),
660 PIS(LP_RX_TIMEOUT),
661 PIS(TE_TRIGGER),
662 PIS(ACK_TRIGGER),
663 PIS(SYNC_LOST),
664 PIS(LDO_POWER_GOOD),
665 PIS(TA_TIMEOUT));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200666#undef PIS
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200667}
668
669static void print_irq_status_vc(int channel, u32 status)
670{
Tomi Valkeinend80d4992011-03-02 15:53:07 +0200671 if (status == 0)
672 return;
673
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530674 if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200675 return;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200676
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530677#define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : ""
678
679 pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n",
680 channel,
681 status,
682 PIS(CS),
683 PIS(ECC_CORR),
684 PIS(ECC_NO_CORR),
685 verbose_irq ? PIS(PACKET_SENT) : "",
686 PIS(BTA),
687 PIS(FIFO_TX_OVF),
688 PIS(FIFO_RX_OVF),
689 PIS(FIFO_TX_UDF),
690 PIS(PP_BUSY_CHANGE));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200691#undef PIS
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200692}
693
694static void print_irq_status_cio(u32 status)
695{
Tomi Valkeinend80d4992011-03-02 15:53:07 +0200696 if (status == 0)
697 return;
698
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530699#define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : ""
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200700
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +0530701 pr_debug("DSI CIO IRQ 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
702 status,
703 PIS(ERRSYNCESC1),
704 PIS(ERRSYNCESC2),
705 PIS(ERRSYNCESC3),
706 PIS(ERRESC1),
707 PIS(ERRESC2),
708 PIS(ERRESC3),
709 PIS(ERRCONTROL1),
710 PIS(ERRCONTROL2),
711 PIS(ERRCONTROL3),
712 PIS(STATEULPS1),
713 PIS(STATEULPS2),
714 PIS(STATEULPS3),
715 PIS(ERRCONTENTIONLP0_1),
716 PIS(ERRCONTENTIONLP1_1),
717 PIS(ERRCONTENTIONLP0_2),
718 PIS(ERRCONTENTIONLP1_2),
719 PIS(ERRCONTENTIONLP0_3),
720 PIS(ERRCONTENTIONLP1_3),
721 PIS(ULPSACTIVENOT_ALL0),
722 PIS(ULPSACTIVENOT_ALL1));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200723#undef PIS
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200724}
725
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200726#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530727static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus,
728 u32 *vcstatus, u32 ciostatus)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200729{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530730 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200731 int i;
732
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530733 spin_lock(&dsi->irq_stats_lock);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200734
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530735 dsi->irq_stats.irq_count++;
736 dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200737
738 for (i = 0; i < 4; ++i)
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530739 dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200740
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530741 dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200742
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530743 spin_unlock(&dsi->irq_stats_lock);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200744}
745#else
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530746#define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus)
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200747#endif
748
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200749static int debug_irq;
750
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530751static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus,
752 u32 *vcstatus, u32 ciostatus)
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200753{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530754 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200755 int i;
756
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200757 if (irqstatus & DSI_IRQ_ERROR_MASK) {
758 DSSERR("DSI error, irqstatus %x\n", irqstatus);
759 print_irq_status(irqstatus);
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530760 spin_lock(&dsi->errors_lock);
761 dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK;
762 spin_unlock(&dsi->errors_lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200763 } else if (debug_irq) {
764 print_irq_status(irqstatus);
765 }
766
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200767 for (i = 0; i < 4; ++i) {
768 if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) {
769 DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
770 i, vcstatus[i]);
771 print_irq_status_vc(i, vcstatus[i]);
772 } else if (debug_irq) {
773 print_irq_status_vc(i, vcstatus[i]);
774 }
775 }
776
777 if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
778 DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
779 print_irq_status_cio(ciostatus);
780 } else if (debug_irq) {
781 print_irq_status_cio(ciostatus);
782 }
783}
784
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200785static void dsi_call_isrs(struct dsi_isr_data *isr_array,
786 unsigned isr_array_size, u32 irqstatus)
787{
788 struct dsi_isr_data *isr_data;
789 int i;
790
791 for (i = 0; i < isr_array_size; i++) {
792 isr_data = &isr_array[i];
793 if (isr_data->isr && isr_data->mask & irqstatus)
794 isr_data->isr(isr_data->arg, irqstatus);
795 }
796}
797
798static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
799 u32 irqstatus, u32 *vcstatus, u32 ciostatus)
800{
801 int i;
802
803 dsi_call_isrs(isr_tables->isr_table,
804 ARRAY_SIZE(isr_tables->isr_table),
805 irqstatus);
806
807 for (i = 0; i < 4; ++i) {
808 if (vcstatus[i] == 0)
809 continue;
810 dsi_call_isrs(isr_tables->isr_table_vc[i],
811 ARRAY_SIZE(isr_tables->isr_table_vc[i]),
812 vcstatus[i]);
813 }
814
815 if (ciostatus != 0)
816 dsi_call_isrs(isr_tables->isr_table_cio,
817 ARRAY_SIZE(isr_tables->isr_table_cio),
818 ciostatus);
819}
820
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200821static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
822{
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530823 struct platform_device *dsidev;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530824 struct dsi_data *dsi;
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200825 u32 irqstatus, vcstatus[4], ciostatus;
826 int i;
827
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530828 dsidev = (struct platform_device *) arg;
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530829 dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530830
Tomi Valkeinen0925afc2014-04-11 13:49:55 +0300831 if (!dsi->is_enabled)
832 return IRQ_NONE;
833
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530834 spin_lock(&dsi->irq_lock);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200835
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530836 irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200837
838 /* IRQ is not for us */
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200839 if (!irqstatus) {
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530840 spin_unlock(&dsi->irq_lock);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200841 return IRQ_NONE;
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200842 }
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200843
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530844 dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200845 /* flush posted write */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530846 dsi_read_reg(dsidev, DSI_IRQSTATUS);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200847
848 for (i = 0; i < 4; ++i) {
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200849 if ((irqstatus & (1 << i)) == 0) {
850 vcstatus[i] = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200851 continue;
Tomi Valkeinenab83b142010-06-09 15:31:01 +0300852 }
853
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530854 vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200855
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530856 dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200857 /* flush posted write */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530858 dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200859 }
860
861 if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530862 ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200863
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530864 dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200865 /* flush posted write */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530866 dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200867 } else {
868 ciostatus = 0;
869 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200870
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200871#ifdef DSI_CATCH_MISSING_TE
872 if (irqstatus & DSI_IRQ_TE_TRIGGER)
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530873 del_timer(&dsi->te_timer);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200874#endif
875
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200876 /* make a copy and unlock, so that isrs can unregister
877 * themselves */
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530878 memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
879 sizeof(dsi->isr_tables));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200880
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530881 spin_unlock(&dsi->irq_lock);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200882
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530883 dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200884
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530885 dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +0200886
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530887 dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus);
Tomi Valkeinen69b281a2011-03-02 14:44:27 +0200888
archit tanejaaffe3602011-02-23 08:41:03 +0000889 return IRQ_HANDLED;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200890}
891
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530892/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530893static void _omap_dsi_configure_irqs(struct platform_device *dsidev,
894 struct dsi_isr_data *isr_array,
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200895 unsigned isr_array_size, u32 default_mask,
896 const struct dsi_reg enable_reg,
897 const struct dsi_reg status_reg)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200898{
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200899 struct dsi_isr_data *isr_data;
900 u32 mask;
901 u32 old_mask;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200902 int i;
903
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200904 mask = default_mask;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200905
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200906 for (i = 0; i < isr_array_size; i++) {
907 isr_data = &isr_array[i];
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200908
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200909 if (isr_data->isr == NULL)
910 continue;
911
912 mask |= isr_data->mask;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200913 }
914
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530915 old_mask = dsi_read_reg(dsidev, enable_reg);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200916 /* clear the irqstatus for newly enabled irqs */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530917 dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask);
918 dsi_write_reg(dsidev, enable_reg, mask);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200919
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200920 /* flush posted writes */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530921 dsi_read_reg(dsidev, enable_reg);
922 dsi_read_reg(dsidev, status_reg);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200923}
924
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530925/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530926static void _omap_dsi_set_irqs(struct platform_device *dsidev)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200927{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530928 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200929 u32 mask = DSI_IRQ_ERROR_MASK;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200930#ifdef DSI_CATCH_MISSING_TE
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200931 mask |= DSI_IRQ_TE_TRIGGER;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200932#endif
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530933 _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table,
934 ARRAY_SIZE(dsi->isr_tables.isr_table), mask,
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200935 DSI_IRQENABLE, DSI_IRQSTATUS);
936}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200937
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530938/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530939static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200940{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530941 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
942
943 _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc],
944 ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]),
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200945 DSI_VC_IRQ_ERROR_MASK,
946 DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
947}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +0200948
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530949/* dsi->irq_lock has to be locked by the caller */
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530950static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200951{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530952 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
953
954 _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio,
955 ARRAY_SIZE(dsi->isr_tables.isr_table_cio),
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200956 DSI_CIO_IRQ_ERROR_MASK,
957 DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
958}
959
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530960static void _dsi_initialize_irq(struct platform_device *dsidev)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200961{
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530962 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200963 unsigned long flags;
964 int vc;
965
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530966 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200967
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530968 memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200969
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530970 _omap_dsi_set_irqs(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200971 for (vc = 0; vc < 4; ++vc)
Archit Tanejaa72b64b2011-05-12 17:26:26 +0530972 _omap_dsi_set_irqs_vc(dsidev, vc);
973 _omap_dsi_set_irqs_cio(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200974
Archit Tanejaf1da39d2011-05-12 17:26:27 +0530975 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +0200976}
977
978static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
979 struct dsi_isr_data *isr_array, unsigned isr_array_size)
980{
981 struct dsi_isr_data *isr_data;
982 int free_idx;
983 int i;
984
985 BUG_ON(isr == NULL);
986
987 /* check for duplicate entry and find a free slot */
988 free_idx = -1;
989 for (i = 0; i < isr_array_size; i++) {
990 isr_data = &isr_array[i];
991
992 if (isr_data->isr == isr && isr_data->arg == arg &&
993 isr_data->mask == mask) {
994 return -EINVAL;
995 }
996
997 if (isr_data->isr == NULL && free_idx == -1)
998 free_idx = i;
999 }
1000
1001 if (free_idx == -1)
1002 return -EBUSY;
1003
1004 isr_data = &isr_array[free_idx];
1005 isr_data->isr = isr;
1006 isr_data->arg = arg;
1007 isr_data->mask = mask;
1008
1009 return 0;
1010}
1011
1012static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
1013 struct dsi_isr_data *isr_array, unsigned isr_array_size)
1014{
1015 struct dsi_isr_data *isr_data;
1016 int i;
1017
1018 for (i = 0; i < isr_array_size; i++) {
1019 isr_data = &isr_array[i];
1020 if (isr_data->isr != isr || isr_data->arg != arg ||
1021 isr_data->mask != mask)
1022 continue;
1023
1024 isr_data->isr = NULL;
1025 isr_data->arg = NULL;
1026 isr_data->mask = 0;
1027
1028 return 0;
1029 }
1030
1031 return -EINVAL;
1032}
1033
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301034static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr,
1035 void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001036{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301037 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001038 unsigned long flags;
1039 int r;
1040
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301041 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001042
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301043 r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table,
1044 ARRAY_SIZE(dsi->isr_tables.isr_table));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001045
1046 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301047 _omap_dsi_set_irqs(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001048
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301049 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001050
1051 return r;
1052}
1053
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301054static int dsi_unregister_isr(struct platform_device *dsidev,
1055 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001056{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301057 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001058 unsigned long flags;
1059 int r;
1060
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301061 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001062
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301063 r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table,
1064 ARRAY_SIZE(dsi->isr_tables.isr_table));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001065
1066 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301067 _omap_dsi_set_irqs(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001068
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301069 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001070
1071 return r;
1072}
1073
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301074static int dsi_register_isr_vc(struct platform_device *dsidev, int channel,
1075 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001076{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301077 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001078 unsigned long flags;
1079 int r;
1080
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301081 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001082
1083 r = _dsi_register_isr(isr, arg, mask,
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301084 dsi->isr_tables.isr_table_vc[channel],
1085 ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001086
1087 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301088 _omap_dsi_set_irqs_vc(dsidev, channel);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001089
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301090 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001091
1092 return r;
1093}
1094
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301095static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel,
1096 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001097{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301098 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001099 unsigned long flags;
1100 int r;
1101
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301102 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001103
1104 r = _dsi_unregister_isr(isr, arg, mask,
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301105 dsi->isr_tables.isr_table_vc[channel],
1106 ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001107
1108 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301109 _omap_dsi_set_irqs_vc(dsidev, channel);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001110
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301111 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001112
1113 return r;
1114}
1115
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301116static int dsi_register_isr_cio(struct platform_device *dsidev,
1117 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001118{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301119 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001120 unsigned long flags;
1121 int r;
1122
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301123 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001124
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301125 r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
1126 ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001127
1128 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301129 _omap_dsi_set_irqs_cio(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001130
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301131 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001132
1133 return r;
1134}
1135
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301136static int dsi_unregister_isr_cio(struct platform_device *dsidev,
1137 omap_dsi_isr_t isr, void *arg, u32 mask)
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001138{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301139 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001140 unsigned long flags;
1141 int r;
1142
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301143 spin_lock_irqsave(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001144
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301145 r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
1146 ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001147
1148 if (r == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301149 _omap_dsi_set_irqs_cio(dsidev);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001150
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301151 spin_unlock_irqrestore(&dsi->irq_lock, flags);
Tomi Valkeinen4ae2ddd2011-03-02 14:47:04 +02001152
1153 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001154}
1155
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301156static u32 dsi_get_errors(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001157{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301158 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001159 unsigned long flags;
1160 u32 e;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301161 spin_lock_irqsave(&dsi->errors_lock, flags);
1162 e = dsi->errors;
1163 dsi->errors = 0;
1164 spin_unlock_irqrestore(&dsi->errors_lock, flags);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001165 return e;
1166}
1167
Tomi Valkeinenf76b1782014-08-08 10:04:31 +03001168static int dsi_runtime_get(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001169{
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001170 int r;
1171 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1172
1173 DSSDBG("dsi_runtime_get\n");
1174
1175 r = pm_runtime_get_sync(&dsi->pdev->dev);
1176 WARN_ON(r < 0);
1177 return r < 0 ? r : 0;
1178}
1179
Tomi Valkeinenf76b1782014-08-08 10:04:31 +03001180static void dsi_runtime_put(struct platform_device *dsidev)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001181{
1182 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1183 int r;
1184
1185 DSSDBG("dsi_runtime_put\n");
1186
Tomi Valkeinen0eaf9f52012-01-23 13:23:08 +02001187 r = pm_runtime_put_sync(&dsi->pdev->dev);
Tomi Valkeinen5be3aeb2012-06-27 16:37:18 +03001188 WARN_ON(r < 0 && r != -ENOSYS);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001189}
1190
Tomi Valkeinenb2541c42013-05-03 13:42:24 +03001191static int dsi_regulator_init(struct platform_device *dsidev)
1192{
1193 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1194 struct regulator *vdds_dsi;
1195
1196 if (dsi->vdds_dsi_reg != NULL)
1197 return 0;
1198
Tomi Valkeinen931d4bd2013-06-10 14:05:10 +03001199 vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdd");
Tomi Valkeinenb2541c42013-05-03 13:42:24 +03001200
1201 if (IS_ERR(vdds_dsi)) {
Tomi Valkeinen40359a92013-12-19 16:15:34 +02001202 if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
Tomi Valkeinen931d4bd2013-06-10 14:05:10 +03001203 DSSERR("can't get DSI VDD regulator\n");
Tomi Valkeinenb2541c42013-05-03 13:42:24 +03001204 return PTR_ERR(vdds_dsi);
1205 }
1206
1207 dsi->vdds_dsi_reg = vdds_dsi;
1208
1209 return 0;
1210}
1211
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301212static void _dsi_print_reset_status(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001213{
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001214 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001215 u32 l;
Tomi Valkeinenc335cbf2010-10-07 13:27:42 +03001216 int b0, b1, b2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001217
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001218 /* A dummy read using the SCP interface to any DSIPHY register is
1219 * required after DSIPHY reset to complete the reset of the DSI complex
1220 * I/O. */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301221 l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001222
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001223 if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC) {
Tomi Valkeinenc335cbf2010-10-07 13:27:42 +03001224 b0 = 28;
1225 b1 = 27;
1226 b2 = 26;
1227 } else {
1228 b0 = 24;
1229 b1 = 25;
1230 b2 = 26;
1231 }
1232
Chandrabhanu Mahapatraf30be7d2012-09-29 12:33:05 +05301233#define DSI_FLD_GET(fld, start, end)\
1234 FLD_GET(dsi_read_reg(dsidev, DSI_##fld), start, end)
1235
1236 pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n",
1237 DSI_FLD_GET(PLL_STATUS, 0, 0),
1238 DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29),
1239 DSI_FLD_GET(DSIPHY_CFG5, b0, b0),
1240 DSI_FLD_GET(DSIPHY_CFG5, b1, b1),
1241 DSI_FLD_GET(DSIPHY_CFG5, b2, b2),
1242 DSI_FLD_GET(DSIPHY_CFG5, 29, 29),
1243 DSI_FLD_GET(DSIPHY_CFG5, 30, 30),
1244 DSI_FLD_GET(DSIPHY_CFG5, 31, 31));
1245
1246#undef DSI_FLD_GET
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001247}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001248
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301249static inline int dsi_if_enable(struct platform_device *dsidev, bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001250{
1251 DSSDBG("dsi_if_enable(%d)\n", enable);
1252
1253 enable = enable ? 1 : 0;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301254 REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001255
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301256 if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001257 DSSERR("Failed to set dsi_if_enable to %d\n", enable);
1258 return -EIO;
1259 }
1260
1261 return 0;
1262}
1263
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001264static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001265{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301266 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1267
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001268 return dsi->pll.cinfo.clkout[HSDIV_DISPC];
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001269}
1270
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301271static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001272{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301273 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1274
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001275 return dsi->pll.cinfo.clkout[HSDIV_DSI];
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001276}
1277
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301278static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001279{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301280 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1281
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001282 return dsi->pll.cinfo.clkdco / 16;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001283}
1284
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301285static unsigned long dsi_fclk_rate(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001286{
1287 unsigned long r;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001288 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001289
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001290 if (dss_get_dsi_clk_source(dsi->module_id) == DSS_CLK_SRC_FCK) {
Archit Taneja1bb47832011-02-24 14:17:30 +05301291 /* DSI FCLK source is DSS_CLK_FCK */
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001292 r = clk_get_rate(dsi->dss_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001293 } else {
Archit Taneja1bb47832011-02-24 14:17:30 +05301294 /* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301295 r = dsi_get_pll_hsdiv_dsi_rate(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001296 }
1297
1298 return r;
1299}
1300
Tomi Valkeinen7b71c412014-08-06 15:45:26 +03001301static int dsi_lp_clock_calc(unsigned long dsi_fclk,
1302 unsigned long lp_clk_min, unsigned long lp_clk_max,
1303 struct dsi_lp_clock_info *lp_cinfo)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001304{
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02001305 unsigned lp_clk_div;
1306 unsigned long lp_clk;
1307
1308 lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2);
1309 lp_clk = dsi_fclk / 2 / lp_clk_div;
1310
1311 if (lp_clk < lp_clk_min || lp_clk > lp_clk_max)
1312 return -EINVAL;
1313
Tomi Valkeinen7b71c412014-08-06 15:45:26 +03001314 lp_cinfo->lp_clk_div = lp_clk_div;
1315 lp_cinfo->lp_clk = lp_clk;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02001316
1317 return 0;
1318}
1319
Tomi Valkeinen57612172012-11-27 17:32:36 +02001320static int dsi_set_lp_clk_divisor(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001321{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301322 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001323 unsigned long dsi_fclk;
1324 unsigned lp_clk_div;
1325 unsigned long lp_clk;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001326 unsigned lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);
1327
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001328
Tomi Valkeinen7b71c412014-08-06 15:45:26 +03001329 lp_clk_div = dsi->user_lp_cinfo.lp_clk_div;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001330
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001331 if (lp_clk_div == 0 || lp_clk_div > lpdiv_max)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001332 return -EINVAL;
1333
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301334 dsi_fclk = dsi_fclk_rate(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001335
1336 lp_clk = dsi_fclk / 2 / lp_clk_div;
1337
1338 DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
Tomi Valkeinen7b71c412014-08-06 15:45:26 +03001339 dsi->current_lp_cinfo.lp_clk = lp_clk;
1340 dsi->current_lp_cinfo.lp_clk_div = lp_clk_div;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001341
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301342 /* LP_CLK_DIVISOR */
1343 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001344
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301345 /* LP_RX_SYNCHRO_ENABLE */
1346 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001347
1348 return 0;
1349}
1350
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301351static void dsi_enable_scp_clk(struct platform_device *dsidev)
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001352{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301353 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1354
1355 if (dsi->scp_clk_refcount++ == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301356 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001357}
1358
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301359static void dsi_disable_scp_clk(struct platform_device *dsidev)
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001360{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301361 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1362
1363 WARN_ON(dsi->scp_clk_refcount == 0);
1364 if (--dsi->scp_clk_refcount == 0)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301365 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001366}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001367
1368enum dsi_pll_power_state {
1369 DSI_PLL_POWER_OFF = 0x0,
1370 DSI_PLL_POWER_ON_HSCLK = 0x1,
1371 DSI_PLL_POWER_ON_ALL = 0x2,
1372 DSI_PLL_POWER_ON_DIV = 0x3,
1373};
1374
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301375static int dsi_pll_power(struct platform_device *dsidev,
1376 enum dsi_pll_power_state state)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001377{
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001378 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001379 int t = 0;
1380
Tomi Valkeinenc94dfe052011-04-15 10:42:59 +03001381 /* DSI-PLL power command 0x3 is not working */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001382 if ((dsi->data->quirks & DSI_QUIRK_PLL_PWR_BUG) &&
1383 state == DSI_PLL_POWER_ON_DIV)
Tomi Valkeinenc94dfe052011-04-15 10:42:59 +03001384 state = DSI_PLL_POWER_ON_ALL;
1385
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301386 /* PLL_PWR_CMD */
1387 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001388
1389 /* PLL_PWR_STATUS */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301390 while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02001391 if (++t > 1000) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001392 DSSERR("Failed to set DSI PLL power mode to %d\n",
1393 state);
1394 return -ENODEV;
1395 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02001396 udelay(1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001397 }
1398
1399 return 0;
1400}
1401
Tomi Valkeinen72658f02013-03-05 16:39:00 +02001402
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001403static void dsi_pll_calc_dsi_fck(struct dss_pll_clock_info *cinfo)
Tomi Valkeinend66b1582012-09-24 15:15:06 +03001404{
1405 unsigned long max_dsi_fck;
1406
1407 max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK);
1408
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001409 cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck);
1410 cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI];
Tomi Valkeinend66b1582012-09-24 15:15:06 +03001411}
1412
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001413static int dsi_pll_enable(struct dss_pll *pll)
Tomi Valkeinen544bfb62014-08-04 13:46:05 +03001414{
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001415 struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
1416 struct platform_device *dsidev = dsi->pdev;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001417 int r = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001418
1419 DSSDBG("PLL init\n");
1420
Tomi Valkeinenb2541c42013-05-03 13:42:24 +03001421 r = dsi_regulator_init(dsidev);
1422 if (r)
1423 return r;
Tomi Valkeinenf2988ab2011-03-02 10:06:48 +02001424
Tomi Valkeinenf76b1782014-08-08 10:04:31 +03001425 r = dsi_runtime_get(dsidev);
1426 if (r)
1427 return r;
1428
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001429 /*
1430 * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
1431 */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301432 dsi_enable_scp_clk(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001433
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301434 if (!dsi->vdds_dsi_enabled) {
1435 r = regulator_enable(dsi->vdds_dsi_reg);
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001436 if (r)
1437 goto err0;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301438 dsi->vdds_dsi_enabled = true;
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001439 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001440
1441 /* XXX PLL does not come out of reset without this... */
1442 dispc_pck_free_enable(1);
1443
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301444 if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001445 DSSERR("PLL not coming out of reset.\n");
1446 r = -ENODEV;
Ville Syrjälä481dfa02010-04-22 22:50:04 +02001447 dispc_pck_free_enable(0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001448 goto err1;
1449 }
1450
1451 /* XXX ... but if left on, we get problems when planes do not
1452 * fill the whole display. No idea about this */
1453 dispc_pck_free_enable(0);
1454
Tomi Valkeinen1a7f4bf2014-08-06 13:31:47 +03001455 r = dsi_pll_power(dsidev, DSI_PLL_POWER_ON_ALL);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001456
1457 if (r)
1458 goto err1;
1459
1460 DSSDBG("PLL init done\n");
1461
1462 return 0;
1463err1:
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301464 if (dsi->vdds_dsi_enabled) {
1465 regulator_disable(dsi->vdds_dsi_reg);
1466 dsi->vdds_dsi_enabled = false;
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001467 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001468err0:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301469 dsi_disable_scp_clk(dsidev);
Tomi Valkeinenf76b1782014-08-08 10:04:31 +03001470 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001471 return r;
1472}
1473
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001474static void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001475{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301476 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1477
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301478 dsi_pll_power(dsidev, DSI_PLL_POWER_OFF);
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001479 if (disconnect_lanes) {
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301480 WARN_ON(!dsi->vdds_dsi_enabled);
1481 regulator_disable(dsi->vdds_dsi_reg);
1482 dsi->vdds_dsi_enabled = false;
Tomi Valkeinen2a89dc12010-07-30 12:39:34 +03001483 }
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001484
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301485 dsi_disable_scp_clk(dsidev);
Tomi Valkeinenf76b1782014-08-08 10:04:31 +03001486 dsi_runtime_put(dsidev);
Tomi Valkeinen24c1ae42011-04-13 17:12:52 +03001487
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001488 DSSDBG("PLL uninit done\n");
1489}
1490
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001491static void dsi_pll_disable(struct dss_pll *pll)
1492{
1493 struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
1494 struct platform_device *dsidev = dsi->pdev;
1495
1496 dsi_pll_uninit(dsidev, true);
1497}
1498
Archit Taneja5a8b5722011-05-12 17:26:29 +05301499static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
1500 struct seq_file *s)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001501{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301502 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001503 struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo;
Tomi Valkeinendc0352d2016-05-17 13:45:09 +03001504 enum dss_clk_source dispc_clk_src, dsi_clk_src;
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02001505 int dsi_module = dsi->module_id;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001506 struct dss_pll *pll = &dsi->pll;
Archit Taneja067a57e2011-03-02 11:57:25 +05301507
1508 dispc_clk_src = dss_get_dispc_clk_source();
Archit Taneja5a8b5722011-05-12 17:26:29 +05301509 dsi_clk_src = dss_get_dsi_clk_source(dsi_module);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001510
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001511 if (dsi_runtime_get(dsidev))
1512 return;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001513
Archit Taneja5a8b5722011-05-12 17:26:29 +05301514 seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001515
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001516 seq_printf(s, "dsi pll clkin\t%lu\n", clk_get_rate(pll->clkin));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001517
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001518 seq_printf(s, "Fint\t\t%-16lun %u\n", cinfo->fint, cinfo->n);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001519
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001520 seq_printf(s, "CLKIN4DDR\t%-16lum %u\n",
1521 cinfo->clkdco, cinfo->m);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001522
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001523 seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16lum_dispc %u\t(%s)\n",
Tomi Valkeinen407bd562016-05-17 13:50:55 +03001524 dss_get_clk_source_name(dsi_module == 0 ?
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001525 DSS_CLK_SRC_PLL1_1 :
1526 DSS_CLK_SRC_PLL2_1),
Tomi Valkeinenacf604b2014-11-07 13:13:24 +02001527 cinfo->clkout[HSDIV_DISPC],
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001528 cinfo->mX[HSDIV_DISPC],
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001529 dispc_clk_src == DSS_CLK_SRC_FCK ?
Tomi Valkeinen63cf28a2010-02-23 17:40:00 +02001530 "off" : "on");
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001531
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001532 seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16lum_dsi %u\t(%s)\n",
Tomi Valkeinen407bd562016-05-17 13:50:55 +03001533 dss_get_clk_source_name(dsi_module == 0 ?
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001534 DSS_CLK_SRC_PLL1_2 :
1535 DSS_CLK_SRC_PLL2_2),
Tomi Valkeinenacf604b2014-11-07 13:13:24 +02001536 cinfo->clkout[HSDIV_DSI],
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001537 cinfo->mX[HSDIV_DSI],
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03001538 dsi_clk_src == DSS_CLK_SRC_FCK ?
Tomi Valkeinen63cf28a2010-02-23 17:40:00 +02001539 "off" : "on");
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001540
Archit Taneja5a8b5722011-05-12 17:26:29 +05301541 seq_printf(s, "- DSI%d -\n", dsi_module + 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001542
Tomi Valkeinen557a1542016-05-17 13:49:18 +03001543 seq_printf(s, "dsi fclk source = %s\n",
Tomi Valkeinen407bd562016-05-17 13:50:55 +03001544 dss_get_clk_source_name(dsi_clk_src));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001545
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301546 seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001547
1548 seq_printf(s, "DDR_CLK\t\t%lu\n",
Tomi Valkeinen4a38aed2014-11-07 13:08:16 +02001549 cinfo->clkdco / 4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001550
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301551 seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001552
Tomi Valkeinen7b71c412014-08-06 15:45:26 +03001553 seq_printf(s, "LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001554
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001555 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001556}
1557
Archit Taneja5a8b5722011-05-12 17:26:29 +05301558void dsi_dump_clocks(struct seq_file *s)
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001559{
Archit Taneja5a8b5722011-05-12 17:26:29 +05301560 struct platform_device *dsidev;
1561 int i;
1562
1563 for (i = 0; i < MAX_NUM_DSI; i++) {
1564 dsidev = dsi_get_dsidev_from_id(i);
1565 if (dsidev)
1566 dsi_dump_dsidev_clocks(dsidev, s);
1567 }
1568}
1569
1570#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
1571static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
1572 struct seq_file *s)
1573{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301574 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001575 unsigned long flags;
1576 struct dsi_irq_stats stats;
1577
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301578 spin_lock_irqsave(&dsi->irq_stats_lock, flags);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001579
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301580 stats = dsi->irq_stats;
1581 memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
1582 dsi->irq_stats.last_reset = jiffies;
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001583
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301584 spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001585
1586 seq_printf(s, "period %u ms\n",
1587 jiffies_to_msecs(jiffies - stats.last_reset));
1588
1589 seq_printf(s, "irqs %d\n", stats.irq_count);
1590#define PIS(x) \
1591 seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]);
1592
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02001593 seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1);
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001594 PIS(VC0);
1595 PIS(VC1);
1596 PIS(VC2);
1597 PIS(VC3);
1598 PIS(WAKEUP);
1599 PIS(RESYNC);
1600 PIS(PLL_LOCK);
1601 PIS(PLL_UNLOCK);
1602 PIS(PLL_RECALL);
1603 PIS(COMPLEXIO_ERR);
1604 PIS(HS_TX_TIMEOUT);
1605 PIS(LP_RX_TIMEOUT);
1606 PIS(TE_TRIGGER);
1607 PIS(ACK_TRIGGER);
1608 PIS(SYNC_LOST);
1609 PIS(LDO_POWER_GOOD);
1610 PIS(TA_TIMEOUT);
1611#undef PIS
1612
1613#define PIS(x) \
1614 seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
1615 stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
1616 stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
1617 stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
1618 stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
1619
1620 seq_printf(s, "-- VC interrupts --\n");
1621 PIS(CS);
1622 PIS(ECC_CORR);
1623 PIS(PACKET_SENT);
1624 PIS(FIFO_TX_OVF);
1625 PIS(FIFO_RX_OVF);
1626 PIS(BTA);
1627 PIS(ECC_NO_CORR);
1628 PIS(FIFO_TX_UDF);
1629 PIS(PP_BUSY_CHANGE);
1630#undef PIS
1631
1632#define PIS(x) \
1633 seq_printf(s, "%-20s %10d\n", #x, \
1634 stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
1635
1636 seq_printf(s, "-- CIO interrupts --\n");
1637 PIS(ERRSYNCESC1);
1638 PIS(ERRSYNCESC2);
1639 PIS(ERRSYNCESC3);
1640 PIS(ERRESC1);
1641 PIS(ERRESC2);
1642 PIS(ERRESC3);
1643 PIS(ERRCONTROL1);
1644 PIS(ERRCONTROL2);
1645 PIS(ERRCONTROL3);
1646 PIS(STATEULPS1);
1647 PIS(STATEULPS2);
1648 PIS(STATEULPS3);
1649 PIS(ERRCONTENTIONLP0_1);
1650 PIS(ERRCONTENTIONLP1_1);
1651 PIS(ERRCONTENTIONLP0_2);
1652 PIS(ERRCONTENTIONLP1_2);
1653 PIS(ERRCONTENTIONLP0_3);
1654 PIS(ERRCONTENTIONLP1_3);
1655 PIS(ULPSACTIVENOT_ALL0);
1656 PIS(ULPSACTIVENOT_ALL1);
1657#undef PIS
1658}
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02001659
Archit Taneja5a8b5722011-05-12 17:26:29 +05301660static void dsi1_dump_irqs(struct seq_file *s)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001661{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301662 struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
1663
Archit Taneja5a8b5722011-05-12 17:26:29 +05301664 dsi_dump_dsidev_irqs(dsidev, s);
1665}
1666
1667static void dsi2_dump_irqs(struct seq_file *s)
1668{
1669 struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
1670
1671 dsi_dump_dsidev_irqs(dsidev, s);
1672}
Archit Taneja5a8b5722011-05-12 17:26:29 +05301673#endif
1674
1675static void dsi_dump_dsidev_regs(struct platform_device *dsidev,
1676 struct seq_file *s)
1677{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301678#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r))
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001679
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001680 if (dsi_runtime_get(dsidev))
1681 return;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301682 dsi_enable_scp_clk(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001683
1684 DUMPREG(DSI_REVISION);
1685 DUMPREG(DSI_SYSCONFIG);
1686 DUMPREG(DSI_SYSSTATUS);
1687 DUMPREG(DSI_IRQSTATUS);
1688 DUMPREG(DSI_IRQENABLE);
1689 DUMPREG(DSI_CTRL);
1690 DUMPREG(DSI_COMPLEXIO_CFG1);
1691 DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
1692 DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
1693 DUMPREG(DSI_CLK_CTRL);
1694 DUMPREG(DSI_TIMING1);
1695 DUMPREG(DSI_TIMING2);
1696 DUMPREG(DSI_VM_TIMING1);
1697 DUMPREG(DSI_VM_TIMING2);
1698 DUMPREG(DSI_VM_TIMING3);
1699 DUMPREG(DSI_CLK_TIMING);
1700 DUMPREG(DSI_TX_FIFO_VC_SIZE);
1701 DUMPREG(DSI_RX_FIFO_VC_SIZE);
1702 DUMPREG(DSI_COMPLEXIO_CFG2);
1703 DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
1704 DUMPREG(DSI_VM_TIMING4);
1705 DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
1706 DUMPREG(DSI_VM_TIMING5);
1707 DUMPREG(DSI_VM_TIMING6);
1708 DUMPREG(DSI_VM_TIMING7);
1709 DUMPREG(DSI_STOPCLK_TIMING);
1710
1711 DUMPREG(DSI_VC_CTRL(0));
1712 DUMPREG(DSI_VC_TE(0));
1713 DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
1714 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
1715 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
1716 DUMPREG(DSI_VC_IRQSTATUS(0));
1717 DUMPREG(DSI_VC_IRQENABLE(0));
1718
1719 DUMPREG(DSI_VC_CTRL(1));
1720 DUMPREG(DSI_VC_TE(1));
1721 DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
1722 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
1723 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
1724 DUMPREG(DSI_VC_IRQSTATUS(1));
1725 DUMPREG(DSI_VC_IRQENABLE(1));
1726
1727 DUMPREG(DSI_VC_CTRL(2));
1728 DUMPREG(DSI_VC_TE(2));
1729 DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
1730 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
1731 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
1732 DUMPREG(DSI_VC_IRQSTATUS(2));
1733 DUMPREG(DSI_VC_IRQENABLE(2));
1734
1735 DUMPREG(DSI_VC_CTRL(3));
1736 DUMPREG(DSI_VC_TE(3));
1737 DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
1738 DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
1739 DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
1740 DUMPREG(DSI_VC_IRQSTATUS(3));
1741 DUMPREG(DSI_VC_IRQENABLE(3));
1742
1743 DUMPREG(DSI_DSIPHY_CFG0);
1744 DUMPREG(DSI_DSIPHY_CFG1);
1745 DUMPREG(DSI_DSIPHY_CFG2);
1746 DUMPREG(DSI_DSIPHY_CFG5);
1747
1748 DUMPREG(DSI_PLL_CONTROL);
1749 DUMPREG(DSI_PLL_STATUS);
1750 DUMPREG(DSI_PLL_GO);
1751 DUMPREG(DSI_PLL_CONFIGURATION1);
1752 DUMPREG(DSI_PLL_CONFIGURATION2);
1753
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301754 dsi_disable_scp_clk(dsidev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001755 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001756#undef DUMPREG
1757}
1758
Archit Taneja5a8b5722011-05-12 17:26:29 +05301759static void dsi1_dump_regs(struct seq_file *s)
1760{
1761 struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
1762
1763 dsi_dump_dsidev_regs(dsidev, s);
1764}
1765
1766static void dsi2_dump_regs(struct seq_file *s)
1767{
1768 struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
1769
1770 dsi_dump_dsidev_regs(dsidev, s);
1771}
1772
Tomi Valkeinencc5c1852010-10-06 15:18:13 +03001773enum dsi_cio_power_state {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001774 DSI_COMPLEXIO_POWER_OFF = 0x0,
1775 DSI_COMPLEXIO_POWER_ON = 0x1,
1776 DSI_COMPLEXIO_POWER_ULPS = 0x2,
1777};
1778
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301779static int dsi_cio_power(struct platform_device *dsidev,
1780 enum dsi_cio_power_state state)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001781{
1782 int t = 0;
1783
1784 /* PWR_CMD */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301785 REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001786
1787 /* PWR_STATUS */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301788 while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1),
1789 26, 25) != state) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02001790 if (++t > 1000) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001791 DSSERR("failed to set complexio power state to "
1792 "%d\n", state);
1793 return -ENODEV;
1794 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +02001795 udelay(1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001796 }
1797
1798 return 0;
1799}
1800
Archit Taneja0c656222011-05-16 15:17:09 +05301801static unsigned dsi_get_line_buf_size(struct platform_device *dsidev)
1802{
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001803 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja0c656222011-05-16 15:17:09 +05301804 int val;
1805
1806 /* line buffer on OMAP3 is 1024 x 24bits */
1807 /* XXX: for some reason using full buffer size causes
1808 * considerable TX slowdown with update sizes that fill the
1809 * whole buffer */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001810 if (!(dsi->data->quirks & DSI_QUIRK_GNQ))
Archit Taneja0c656222011-05-16 15:17:09 +05301811 return 1023 * 3;
1812
1813 val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */
1814
1815 switch (val) {
1816 case 1:
1817 return 512 * 3; /* 512x24 bits */
1818 case 2:
1819 return 682 * 3; /* 682x24 bits */
1820 case 3:
1821 return 853 * 3; /* 853x24 bits */
1822 case 4:
1823 return 1024 * 3; /* 1024x24 bits */
1824 case 5:
1825 return 1194 * 3; /* 1194x24 bits */
1826 case 6:
1827 return 1365 * 3; /* 1365x24 bits */
Tomi Valkeinen2ac80fb2012-08-22 16:00:47 +03001828 case 7:
1829 return 1920 * 3; /* 1920x24 bits */
Archit Taneja0c656222011-05-16 15:17:09 +05301830 default:
1831 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +03001832 return 0;
Archit Taneja0c656222011-05-16 15:17:09 +05301833 }
1834}
1835
Archit Taneja9e7e9372012-08-14 12:29:22 +05301836static int dsi_set_lane_config(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001837{
Tomi Valkeinen48368392011-10-13 11:22:39 +03001838 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1839 static const u8 offsets[] = { 0, 4, 8, 12, 16 };
1840 static const enum dsi_lane_function functions[] = {
1841 DSI_LANE_CLK,
1842 DSI_LANE_DATA1,
1843 DSI_LANE_DATA2,
1844 DSI_LANE_DATA3,
1845 DSI_LANE_DATA4,
1846 };
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001847 u32 r;
Tomi Valkeinen48368392011-10-13 11:22:39 +03001848 int i;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001849
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301850 r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
Archit Taneja75d72472011-05-16 15:17:08 +05301851
Tomi Valkeinen48368392011-10-13 11:22:39 +03001852 for (i = 0; i < dsi->num_lanes_used; ++i) {
1853 unsigned offset = offsets[i];
1854 unsigned polarity, lane_number;
1855 unsigned t;
Archit Taneja75d72472011-05-16 15:17:08 +05301856
Tomi Valkeinen48368392011-10-13 11:22:39 +03001857 for (t = 0; t < dsi->num_lanes_supported; ++t)
1858 if (dsi->lanes[t].function == functions[i])
1859 break;
1860
1861 if (t == dsi->num_lanes_supported)
1862 return -EINVAL;
1863
1864 lane_number = t;
1865 polarity = dsi->lanes[t].polarity;
1866
1867 r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
1868 r = FLD_MOD(r, polarity, offset + 3, offset + 3);
Archit Taneja75d72472011-05-16 15:17:08 +05301869 }
Tomi Valkeinen48368392011-10-13 11:22:39 +03001870
1871 /* clear the unused lanes */
1872 for (; i < dsi->num_lanes_supported; ++i) {
1873 unsigned offset = offsets[i];
1874
1875 r = FLD_MOD(r, 0, offset + 2, offset);
1876 r = FLD_MOD(r, 0, offset + 3, offset + 3);
1877 }
1878
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301879 dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001880
Tomi Valkeinen48368392011-10-13 11:22:39 +03001881 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001882}
1883
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301884static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001885{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301886 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1887
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001888 /* convert time in ns to ddr ticks, rounding up */
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001889 unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001890 return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
1891}
1892
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301893static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001894{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05301895 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
1896
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03001897 unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001898 return ddr * 1000 * 1000 / (ddr_clk / 1000);
1899}
1900
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301901static void dsi_cio_timings(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001902{
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001903 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001904 u32 r;
1905 u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
1906 u32 tlpx_half, tclk_trail, tclk_zero;
1907 u32 tclk_prepare;
1908
1909 /* calculate timings */
1910
1911 /* 1 * DDR_CLK = 2 * UI */
1912
1913 /* min 40ns + 4*UI max 85ns + 6*UI */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301914 ths_prepare = ns2ddr(dsidev, 70) + 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001915
1916 /* min 145ns + 10*UI */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301917 ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001918
1919 /* min max(8*UI, 60ns+4*UI) */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301920 ths_trail = ns2ddr(dsidev, 60) + 5;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001921
1922 /* min 100ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301923 ths_exit = ns2ddr(dsidev, 145);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001924
1925 /* tlpx min 50n */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301926 tlpx_half = ns2ddr(dsidev, 25);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001927
1928 /* min 60ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301929 tclk_trail = ns2ddr(dsidev, 60) + 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001930
1931 /* min 38ns, max 95ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301932 tclk_prepare = ns2ddr(dsidev, 65);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001933
1934 /* min tclk-prepare + tclk-zero = 300ns */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301935 tclk_zero = ns2ddr(dsidev, 260);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001936
1937 DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301938 ths_prepare, ddr2ns(dsidev, ths_prepare),
1939 ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001940 DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301941 ths_trail, ddr2ns(dsidev, ths_trail),
1942 ths_exit, ddr2ns(dsidev, ths_exit));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001943
1944 DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
1945 "tclk_zero %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301946 tlpx_half, ddr2ns(dsidev, tlpx_half),
1947 tclk_trail, ddr2ns(dsidev, tclk_trail),
1948 tclk_zero, ddr2ns(dsidev, tclk_zero));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001949 DSSDBG("tclk_prepare %u (%uns)\n",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301950 tclk_prepare, ddr2ns(dsidev, tclk_prepare));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001951
1952 /* program timings */
1953
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301954 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001955 r = FLD_MOD(r, ths_prepare, 31, 24);
1956 r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
1957 r = FLD_MOD(r, ths_trail, 15, 8);
1958 r = FLD_MOD(r, ths_exit, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301959 dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001960
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301961 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
Tomi Valkeinene84dc1c2012-09-24 09:34:52 +03001962 r = FLD_MOD(r, tlpx_half, 20, 16);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001963 r = FLD_MOD(r, tclk_trail, 15, 8);
1964 r = FLD_MOD(r, tclk_zero, 7, 0);
Tomi Valkeinen77ccbfb2012-09-24 15:15:57 +03001965
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03001966 if (dsi->data->quirks & DSI_QUIRK_PHY_DCC) {
Tomi Valkeinen77ccbfb2012-09-24 15:15:57 +03001967 r = FLD_MOD(r, 0, 21, 21); /* DCCEN = disable */
1968 r = FLD_MOD(r, 1, 22, 22); /* CLKINP_DIVBY2EN = enable */
1969 r = FLD_MOD(r, 1, 23, 23); /* CLKINP_SEL = enable */
1970 }
1971
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301972 dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001973
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301974 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001975 r = FLD_MOD(r, tclk_prepare, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05301976 dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02001977}
1978
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001979/* lane masks have lane 0 at lsb. mask_p for positive lines, n for negative */
Archit Taneja9e7e9372012-08-14 12:29:22 +05301980static void dsi_cio_enable_lane_override(struct platform_device *dsidev,
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001981 unsigned mask_p, unsigned mask_n)
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03001982{
Archit Taneja75d72472011-05-16 15:17:08 +05301983 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001984 int i;
1985 u32 l;
Tomi Valkeinend9820852011-10-12 15:05:59 +03001986 u8 lptxscp_start = dsi->num_lanes_supported == 3 ? 22 : 26;
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03001987
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001988 l = 0;
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03001989
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001990 for (i = 0; i < dsi->num_lanes_supported; ++i) {
1991 unsigned p = dsi->lanes[i].polarity;
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03001992
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001993 if (mask_p & (1 << i))
1994 l |= 1 << (i * 2 + (p ? 0 : 1));
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03001995
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03001996 if (mask_n & (1 << i))
1997 l |= 1 << (i * 2 + (p ? 1 : 0));
1998 }
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03001999
2000 /*
2001 * Bits in REGLPTXSCPDAT4TO0DXDY:
2002 * 17: DY0 18: DX0
2003 * 19: DY1 20: DX1
2004 * 21: DY2 22: DX2
Archit Taneja75d72472011-05-16 15:17:08 +05302005 * 23: DY3 24: DX3
2006 * 25: DY4 26: DX4
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002007 */
2008
2009 /* Set the lane override configuration */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302010
2011 /* REGLPTXSCPDAT4TO0DXDY */
Archit Taneja75d72472011-05-16 15:17:08 +05302012 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17);
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002013
2014 /* Enable lane override */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302015
2016 /* ENLPTXSCPDAT */
2017 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27);
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002018}
2019
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302020static void dsi_cio_disable_lane_override(struct platform_device *dsidev)
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002021{
2022 /* Disable lane override */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302023 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002024 /* Reset the lane override configuration */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302025 /* REGLPTXSCPDAT4TO0DXDY */
2026 REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17);
Tomi Valkeinen0a0ee462010-07-27 11:11:48 +03002027}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002028
Archit Taneja9e7e9372012-08-14 12:29:22 +05302029static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev)
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002030{
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002031 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2032 int t, i;
2033 bool in_use[DSI_MAX_NR_LANES];
2034 static const u8 offsets_old[] = { 28, 27, 26 };
2035 static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
2036 const u8 *offsets;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002037
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03002038 if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC)
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002039 offsets = offsets_old;
2040 else
2041 offsets = offsets_new;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002042
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002043 for (i = 0; i < dsi->num_lanes_supported; ++i)
2044 in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002045
2046 t = 100000;
2047 while (true) {
2048 u32 l;
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002049 int ok;
2050
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302051 l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002052
2053 ok = 0;
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002054 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2055 if (!in_use[i] || (l & (1 << offsets[i])))
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002056 ok++;
2057 }
2058
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002059 if (ok == dsi->num_lanes_supported)
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002060 break;
2061
2062 if (--t == 0) {
Tomi Valkeinen8dc07662011-10-13 15:26:50 +03002063 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2064 if (!in_use[i] || (l & (1 << offsets[i])))
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002065 continue;
2066
2067 DSSERR("CIO TXCLKESC%d domain not coming " \
2068 "out of reset\n", i);
2069 }
2070 return -EIO;
2071 }
2072 }
2073
2074 return 0;
2075}
2076
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002077/* return bitmask of enabled lanes, lane0 being the lsb */
Archit Taneja9e7e9372012-08-14 12:29:22 +05302078static unsigned dsi_get_lane_mask(struct platform_device *dsidev)
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002079{
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002080 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2081 unsigned mask = 0;
2082 int i;
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002083
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002084 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2085 if (dsi->lanes[i].function != DSI_LANE_UNUSED)
2086 mask |= 1 << i;
2087 }
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002088
Tomi Valkeinen85f17e82011-10-13 15:12:23 +03002089 return mask;
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002090}
2091
Laurent Pinchart9e1305d2017-08-05 01:43:53 +03002092/* OMAP4 CONTROL_DSIPHY */
2093#define OMAP4_DSIPHY_SYSCON_OFFSET 0x78
2094
2095#define OMAP4_DSI2_LANEENABLE_SHIFT 29
2096#define OMAP4_DSI2_LANEENABLE_MASK (0x7 << 29)
2097#define OMAP4_DSI1_LANEENABLE_SHIFT 24
2098#define OMAP4_DSI1_LANEENABLE_MASK (0x1f << 24)
2099#define OMAP4_DSI1_PIPD_SHIFT 19
2100#define OMAP4_DSI1_PIPD_MASK (0x1f << 19)
2101#define OMAP4_DSI2_PIPD_SHIFT 14
2102#define OMAP4_DSI2_PIPD_MASK (0x1f << 14)
2103
2104static int dsi_omap4_mux_pads(struct dsi_data *dsi, unsigned int lanes)
2105{
2106 u32 enable_mask, enable_shift;
2107 u32 pipd_mask, pipd_shift;
2108 u32 reg;
2109
2110 if (!dsi->syscon)
2111 return 0;
2112
2113 if (dsi->module_id == 0) {
2114 enable_mask = OMAP4_DSI1_LANEENABLE_MASK;
2115 enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT;
2116 pipd_mask = OMAP4_DSI1_PIPD_MASK;
2117 pipd_shift = OMAP4_DSI1_PIPD_SHIFT;
2118 } else if (dsi->module_id == 1) {
2119 enable_mask = OMAP4_DSI2_LANEENABLE_MASK;
2120 enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT;
2121 pipd_mask = OMAP4_DSI2_PIPD_MASK;
2122 pipd_shift = OMAP4_DSI2_PIPD_SHIFT;
2123 } else {
2124 return -ENODEV;
2125 }
2126
2127 regmap_read(dsi->syscon, OMAP4_DSIPHY_SYSCON_OFFSET, &reg);
2128
2129 reg &= ~enable_mask;
2130 reg &= ~pipd_mask;
2131
2132 reg |= (lanes << enable_shift) & enable_mask;
2133 reg |= (lanes << pipd_shift) & pipd_mask;
2134
2135 regmap_write(dsi->syscon, OMAP4_DSIPHY_SYSCON_OFFSET, reg);
2136
2137 return 0;
2138}
2139
2140static int dsi_enable_pads(struct dsi_data *dsi, unsigned int lane_mask)
2141{
2142 return dsi_omap4_mux_pads(dsi, lane_mask);
2143}
2144
2145static void dsi_disable_pads(struct dsi_data *dsi)
2146{
2147 dsi_omap4_mux_pads(dsi, 0);
2148}
2149
Archit Taneja9e7e9372012-08-14 12:29:22 +05302150static int dsi_cio_init(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002151{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302152 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002153 int r;
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002154 u32 l;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002155
Chandrabhanu Mahapatra702d2672012-09-24 17:12:58 +05302156 DSSDBG("DSI CIO init starts");
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002157
Laurent Pinchart9e1305d2017-08-05 01:43:53 +03002158 r = dsi_enable_pads(dsi, dsi_get_lane_mask(dsidev));
Tomi Valkeinen5bc416c2011-06-15 15:21:12 +03002159 if (r)
2160 return r;
Tomi Valkeinend1f5857e2010-07-30 11:57:57 +03002161
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302162 dsi_enable_scp_clk(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002163
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002164 /* A dummy read using the SCP interface to any DSIPHY register is
2165 * required after DSIPHY reset to complete the reset of the DSI complex
2166 * I/O. */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302167 dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002168
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302169 if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) {
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002170 DSSERR("CIO SCP Clock domain not coming out of reset.\n");
2171 r = -EIO;
2172 goto err_scp_clk_dom;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002173 }
2174
Archit Taneja9e7e9372012-08-14 12:29:22 +05302175 r = dsi_set_lane_config(dsidev);
Tomi Valkeinen48368392011-10-13 11:22:39 +03002176 if (r)
2177 goto err_scp_clk_dom;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002178
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002179 /* set TX STOP MODE timer to maximum for this operation */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302180 l = dsi_read_reg(dsidev, DSI_TIMING1);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002181 l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
2182 l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */
2183 l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */
2184 l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302185 dsi_write_reg(dsidev, DSI_TIMING1, l);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002186
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302187 if (dsi->ulps_enabled) {
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002188 unsigned mask_p;
2189 int i;
Archit Taneja75d72472011-05-16 15:17:08 +05302190
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002191 DSSDBG("manual ulps exit\n");
2192
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002193 /* ULPS is exited by Mark-1 state for 1ms, followed by
2194 * stop state. DSS HW cannot do this via the normal
2195 * ULPS exit sequence, as after reset the DSS HW thinks
2196 * that we are not in ULPS mode, and refuses to send the
2197 * sequence. So we need to send the ULPS exit sequence
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002198 * manually by setting positive lines high and negative lines
2199 * low for 1ms.
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002200 */
2201
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002202 mask_p = 0;
Archit Taneja75d72472011-05-16 15:17:08 +05302203
Tomi Valkeinen9b4362f2011-10-13 16:06:43 +03002204 for (i = 0; i < dsi->num_lanes_supported; ++i) {
2205 if (dsi->lanes[i].function == DSI_LANE_UNUSED)
2206 continue;
2207 mask_p |= 1 << i;
2208 }
Archit Taneja75d72472011-05-16 15:17:08 +05302209
Archit Taneja9e7e9372012-08-14 12:29:22 +05302210 dsi_cio_enable_lane_override(dsidev, mask_p, 0);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002211 }
2212
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302213 r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002214 if (r)
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002215 goto err_cio_pwr;
2216
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302217 if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002218 DSSERR("CIO PWR clock domain not coming out of reset.\n");
2219 r = -ENODEV;
2220 goto err_cio_pwr_dom;
2221 }
2222
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302223 dsi_if_enable(dsidev, true);
2224 dsi_if_enable(dsidev, false);
2225 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002226
Archit Taneja9e7e9372012-08-14 12:29:22 +05302227 r = dsi_cio_wait_tx_clk_esc_reset(dsidev);
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002228 if (r)
2229 goto err_tx_clk_esc_rst;
2230
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302231 if (dsi->ulps_enabled) {
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002232 /* Keep Mark-1 state for 1ms (as per DSI spec) */
2233 ktime_t wait = ns_to_ktime(1000 * 1000);
2234 set_current_state(TASK_UNINTERRUPTIBLE);
2235 schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
2236
2237 /* Disable the override. The lanes should be set to Mark-11
2238 * state by the HW */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302239 dsi_cio_disable_lane_override(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002240 }
2241
2242 /* FORCE_TX_STOP_MODE_IO */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302243 REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03002244
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302245 dsi_cio_timings(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002246
Archit Tanejadca2b152012-08-16 18:02:00 +05302247 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
Archit Taneja8af6ff02011-09-05 16:48:27 +05302248 /* DDR_CLK_ALWAYS_ON */
2249 REG_FLD_MOD(dsidev, DSI_CLK_CTRL,
Archit Taneja0b3ffe32012-08-13 22:13:39 +05302250 dsi->vm_timings.ddr_clk_always_on, 13, 13);
Archit Taneja8af6ff02011-09-05 16:48:27 +05302251 }
2252
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302253 dsi->ulps_enabled = false;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002254
2255 DSSDBG("CIO init done\n");
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002256
2257 return 0;
2258
Tomi Valkeinen03329ac2010-10-07 13:59:22 +03002259err_tx_clk_esc_rst:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302260 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002261err_cio_pwr_dom:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302262 dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002263err_cio_pwr:
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302264 if (dsi->ulps_enabled)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302265 dsi_cio_disable_lane_override(dsidev);
Tomi Valkeinen65c62bb2011-04-15 11:58:41 +03002266err_scp_clk_dom:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302267 dsi_disable_scp_clk(dsidev);
Laurent Pinchart9e1305d2017-08-05 01:43:53 +03002268 dsi_disable_pads(dsi);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002269 return r;
2270}
2271
Archit Taneja9e7e9372012-08-14 12:29:22 +05302272static void dsi_cio_uninit(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002273{
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02002274 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302275
Archit Taneja8af6ff02011-09-05 16:48:27 +05302276 /* DDR_CLK_ALWAYS_ON */
2277 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
2278
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302279 dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
2280 dsi_disable_scp_clk(dsidev);
Laurent Pinchart9e1305d2017-08-05 01:43:53 +03002281 dsi_disable_pads(dsi);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002282}
2283
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302284static void dsi_config_tx_fifo(struct platform_device *dsidev,
2285 enum fifo_size size1, enum fifo_size size2,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002286 enum fifo_size size3, enum fifo_size size4)
2287{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302288 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002289 u32 r = 0;
2290 int add = 0;
2291 int i;
2292
Tomi Valkeinen558c73e2013-09-25 14:40:06 +03002293 dsi->vc[0].tx_fifo_size = size1;
2294 dsi->vc[1].tx_fifo_size = size2;
2295 dsi->vc[2].tx_fifo_size = size3;
2296 dsi->vc[3].tx_fifo_size = size4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002297
2298 for (i = 0; i < 4; i++) {
2299 u8 v;
Tomi Valkeinen558c73e2013-09-25 14:40:06 +03002300 int size = dsi->vc[i].tx_fifo_size;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002301
2302 if (add + size > 4) {
2303 DSSERR("Illegal FIFO configuration\n");
2304 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +03002305 return;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002306 }
2307
2308 v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
2309 r |= v << (8 * i);
2310 /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
2311 add += size;
2312 }
2313
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302314 dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002315}
2316
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302317static void dsi_config_rx_fifo(struct platform_device *dsidev,
2318 enum fifo_size size1, enum fifo_size size2,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002319 enum fifo_size size3, enum fifo_size size4)
2320{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302321 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002322 u32 r = 0;
2323 int add = 0;
2324 int i;
2325
Tomi Valkeinen558c73e2013-09-25 14:40:06 +03002326 dsi->vc[0].rx_fifo_size = size1;
2327 dsi->vc[1].rx_fifo_size = size2;
2328 dsi->vc[2].rx_fifo_size = size3;
2329 dsi->vc[3].rx_fifo_size = size4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002330
2331 for (i = 0; i < 4; i++) {
2332 u8 v;
Tomi Valkeinen558c73e2013-09-25 14:40:06 +03002333 int size = dsi->vc[i].rx_fifo_size;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002334
2335 if (add + size > 4) {
2336 DSSERR("Illegal FIFO configuration\n");
2337 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +03002338 return;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002339 }
2340
2341 v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
2342 r |= v << (8 * i);
2343 /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
2344 add += size;
2345 }
2346
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302347 dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002348}
2349
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302350static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002351{
2352 u32 r;
2353
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302354 r = dsi_read_reg(dsidev, DSI_TIMING1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002355 r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302356 dsi_write_reg(dsidev, DSI_TIMING1, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002357
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302358 if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002359 DSSERR("TX_STOP bit not going down\n");
2360 return -EIO;
2361 }
2362
2363 return 0;
2364}
2365
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302366static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002367{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302368 return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002369}
2370
2371static void dsi_packet_sent_handler_vp(void *data, u32 mask)
2372{
Archit Taneja2e868db2011-05-12 17:26:28 +05302373 struct dsi_packet_sent_handler_data *vp_data =
2374 (struct dsi_packet_sent_handler_data *) data;
2375 struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302376 const int channel = dsi->update_channel;
2377 u8 bit = dsi->te_enabled ? 30 : 31;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002378
Archit Taneja2e868db2011-05-12 17:26:28 +05302379 if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0)
2380 complete(vp_data->completion);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002381}
2382
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302383static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002384{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302385 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja2e868db2011-05-12 17:26:28 +05302386 DECLARE_COMPLETION_ONSTACK(completion);
Julia Lawall39917f02014-08-23 13:20:29 +02002387 struct dsi_packet_sent_handler_data vp_data = {
2388 .dsidev = dsidev,
2389 .completion = &completion
2390 };
Archit Tanejacf398fb2011-03-23 09:59:34 +00002391 int r = 0;
2392 u8 bit;
2393
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302394 bit = dsi->te_enabled ? 30 : 31;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002395
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302396 r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
Archit Taneja2e868db2011-05-12 17:26:28 +05302397 &vp_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002398 if (r)
2399 goto err0;
2400
2401 /* Wait for completion only if TE_EN/TE_START is still set */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302402 if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) {
Archit Tanejacf398fb2011-03-23 09:59:34 +00002403 if (wait_for_completion_timeout(&completion,
2404 msecs_to_jiffies(10)) == 0) {
2405 DSSERR("Failed to complete previous frame transfer\n");
2406 r = -EIO;
2407 goto err1;
2408 }
2409 }
2410
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302411 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
Archit Taneja2e868db2011-05-12 17:26:28 +05302412 &vp_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002413
2414 return 0;
2415err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302416 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
Archit Taneja2e868db2011-05-12 17:26:28 +05302417 &vp_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002418err0:
2419 return r;
2420}
2421
2422static void dsi_packet_sent_handler_l4(void *data, u32 mask)
2423{
Archit Taneja2e868db2011-05-12 17:26:28 +05302424 struct dsi_packet_sent_handler_data *l4_data =
2425 (struct dsi_packet_sent_handler_data *) data;
2426 struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302427 const int channel = dsi->update_channel;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002428
Archit Taneja2e868db2011-05-12 17:26:28 +05302429 if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0)
2430 complete(l4_data->completion);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002431}
2432
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302433static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002434{
Archit Taneja2e868db2011-05-12 17:26:28 +05302435 DECLARE_COMPLETION_ONSTACK(completion);
Julia Lawall39917f02014-08-23 13:20:29 +02002436 struct dsi_packet_sent_handler_data l4_data = {
2437 .dsidev = dsidev,
2438 .completion = &completion
2439 };
Archit Tanejacf398fb2011-03-23 09:59:34 +00002440 int r = 0;
2441
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302442 r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
Archit Taneja2e868db2011-05-12 17:26:28 +05302443 &l4_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002444 if (r)
2445 goto err0;
2446
2447 /* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302448 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) {
Archit Tanejacf398fb2011-03-23 09:59:34 +00002449 if (wait_for_completion_timeout(&completion,
2450 msecs_to_jiffies(10)) == 0) {
2451 DSSERR("Failed to complete previous l4 transfer\n");
2452 r = -EIO;
2453 goto err1;
2454 }
2455 }
2456
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302457 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
Archit Taneja2e868db2011-05-12 17:26:28 +05302458 &l4_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002459
2460 return 0;
2461err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302462 dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
Archit Taneja2e868db2011-05-12 17:26:28 +05302463 &l4_data, DSI_VC_IRQ_PACKET_SENT);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002464err0:
2465 return r;
2466}
2467
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302468static int dsi_sync_vc(struct platform_device *dsidev, int channel)
Archit Tanejacf398fb2011-03-23 09:59:34 +00002469{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302470 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2471
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302472 WARN_ON(!dsi_bus_is_locked(dsidev));
Archit Tanejacf398fb2011-03-23 09:59:34 +00002473
2474 WARN_ON(in_interrupt());
2475
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302476 if (!dsi_vc_is_enabled(dsidev, channel))
Archit Tanejacf398fb2011-03-23 09:59:34 +00002477 return 0;
2478
Archit Tanejad6049142011-08-22 11:58:08 +05302479 switch (dsi->vc[channel].source) {
2480 case DSI_VC_SOURCE_VP:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302481 return dsi_sync_vc_vp(dsidev, channel);
Archit Tanejad6049142011-08-22 11:58:08 +05302482 case DSI_VC_SOURCE_L4:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302483 return dsi_sync_vc_l4(dsidev, channel);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002484 default:
2485 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +03002486 return -EINVAL;
Archit Tanejacf398fb2011-03-23 09:59:34 +00002487 }
2488}
2489
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302490static int dsi_vc_enable(struct platform_device *dsidev, int channel,
2491 bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002492{
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +02002493 DSSDBG("dsi_vc_enable channel %d, enable %d\n",
2494 channel, enable);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002495
2496 enable = enable ? 1 : 0;
2497
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302498 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002499
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302500 if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel),
2501 0, enable) != enable) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002502 DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
2503 return -EIO;
2504 }
2505
2506 return 0;
2507}
2508
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302509static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002510{
Tomi Valkeinen2c1a3ea2013-02-22 13:42:59 +02002511 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002512 u32 r;
2513
Chandrabhanu Mahapatra702d2672012-09-24 17:12:58 +05302514 DSSDBG("Initial config of virtual channel %d", channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002515
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302516 r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002517
2518 if (FLD_GET(r, 15, 15)) /* VC_BUSY */
2519 DSSERR("VC(%d) busy when trying to configure it!\n",
2520 channel);
2521
2522 r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
2523 r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
2524 r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
2525 r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
2526 r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
2527 r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
2528 r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03002529 if (dsi->data->quirks & DSI_QUIRK_VC_OCP_WIDTH)
Archit Taneja9613c022011-03-22 06:33:36 -05002530 r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002531
2532 r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
2533 r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
2534
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302535 dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r);
Tomi Valkeinen2c1a3ea2013-02-22 13:42:59 +02002536
2537 dsi->vc[channel].source = DSI_VC_SOURCE_L4;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002538}
2539
Archit Tanejad6049142011-08-22 11:58:08 +05302540static int dsi_vc_config_source(struct platform_device *dsidev, int channel,
2541 enum dsi_vc_source source)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002542{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302543 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2544
Archit Tanejad6049142011-08-22 11:58:08 +05302545 if (dsi->vc[channel].source == source)
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002546 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002547
Chandrabhanu Mahapatra702d2672012-09-24 17:12:58 +05302548 DSSDBG("Source config of virtual channel %d", channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002549
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302550 dsi_sync_vc(dsidev, channel);
Archit Tanejacf398fb2011-03-23 09:59:34 +00002551
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302552 dsi_vc_enable(dsidev, channel, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002553
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002554 /* VC_BUSY */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302555 if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002556 DSSERR("vc(%d) busy when trying to config for VP\n", channel);
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002557 return -EIO;
2558 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002559
Archit Tanejad6049142011-08-22 11:58:08 +05302560 /* SOURCE, 0 = L4, 1 = video port */
2561 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002562
Archit Taneja9613c022011-03-22 06:33:36 -05002563 /* DCS_CMD_ENABLE */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03002564 if (dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC) {
Archit Tanejad6049142011-08-22 11:58:08 +05302565 bool enable = source == DSI_VC_SOURCE_VP;
2566 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30);
2567 }
Archit Taneja9613c022011-03-22 06:33:36 -05002568
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302569 dsi_vc_enable(dsidev, channel, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002570
Archit Tanejad6049142011-08-22 11:58:08 +05302571 dsi->vc[channel].source = source;
Tomi Valkeinen9ecd9682010-04-30 11:24:33 +03002572
2573 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002574}
2575
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002576static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
Archit Taneja1ffefe72011-05-12 17:26:24 +05302577 bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002578{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302579 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Taneja0b3ffe32012-08-13 22:13:39 +05302580 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302581
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002582 DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
2583
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302584 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen61140c92010-01-12 16:00:30 +02002585
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302586 dsi_vc_enable(dsidev, channel, 0);
2587 dsi_if_enable(dsidev, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002588
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302589 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002590
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302591 dsi_vc_enable(dsidev, channel, 1);
2592 dsi_if_enable(dsidev, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002593
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302594 dsi_force_tx_stop_mode_io(dsidev);
Archit Taneja8af6ff02011-09-05 16:48:27 +05302595
2596 /* start the DDR clock by sending a NULL packet */
Archit Taneja0b3ffe32012-08-13 22:13:39 +05302597 if (dsi->vm_timings.ddr_clk_always_on && enable)
Archit Taneja8af6ff02011-09-05 16:48:27 +05302598 dsi_vc_send_null(dssdev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002599}
2600
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302601static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002602{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302603 while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002604 u32 val;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302605 val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002606 DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
2607 (val >> 0) & 0xff,
2608 (val >> 8) & 0xff,
2609 (val >> 16) & 0xff,
2610 (val >> 24) & 0xff);
2611 }
2612}
2613
2614static void dsi_show_rx_ack_with_err(u16 err)
2615{
2616 DSSERR("\tACK with ERROR (%#x):\n", err);
2617 if (err & (1 << 0))
2618 DSSERR("\t\tSoT Error\n");
2619 if (err & (1 << 1))
2620 DSSERR("\t\tSoT Sync Error\n");
2621 if (err & (1 << 2))
2622 DSSERR("\t\tEoT Sync Error\n");
2623 if (err & (1 << 3))
2624 DSSERR("\t\tEscape Mode Entry Command Error\n");
2625 if (err & (1 << 4))
2626 DSSERR("\t\tLP Transmit Sync Error\n");
2627 if (err & (1 << 5))
2628 DSSERR("\t\tHS Receive Timeout Error\n");
2629 if (err & (1 << 6))
2630 DSSERR("\t\tFalse Control Error\n");
2631 if (err & (1 << 7))
2632 DSSERR("\t\t(reserved7)\n");
2633 if (err & (1 << 8))
2634 DSSERR("\t\tECC Error, single-bit (corrected)\n");
2635 if (err & (1 << 9))
2636 DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
2637 if (err & (1 << 10))
2638 DSSERR("\t\tChecksum Error\n");
2639 if (err & (1 << 11))
2640 DSSERR("\t\tData type not recognized\n");
2641 if (err & (1 << 12))
2642 DSSERR("\t\tInvalid VC ID\n");
2643 if (err & (1 << 13))
2644 DSSERR("\t\tInvalid Transmission Length\n");
2645 if (err & (1 << 14))
2646 DSSERR("\t\t(reserved14)\n");
2647 if (err & (1 << 15))
2648 DSSERR("\t\tDSI Protocol Violation\n");
2649}
2650
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302651static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev,
2652 int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002653{
2654 /* RX_FIFO_NOT_EMPTY */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302655 while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002656 u32 val;
2657 u8 dt;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302658 val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002659 DSSERR("\trawval %#08x\n", val);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002660 dt = FLD_GET(val, 5, 0);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302661 if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002662 u16 err = FLD_GET(val, 23, 8);
2663 dsi_show_rx_ack_with_err(err);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302664 } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002665 DSSERR("\tDCS short response, 1 byte: %#x\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002666 FLD_GET(val, 23, 8));
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302667 } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002668 DSSERR("\tDCS short response, 2 byte: %#x\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002669 FLD_GET(val, 23, 8));
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302670 } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
Tomi Valkeinen86a78672010-03-16 16:19:06 +02002671 DSSERR("\tDCS long response, len %d\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002672 FLD_GET(val, 23, 8));
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302673 dsi_vc_flush_long_data(dsidev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002674 } else {
2675 DSSERR("\tunknown datatype 0x%02x\n", dt);
2676 }
2677 }
2678 return 0;
2679}
2680
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302681static int dsi_vc_send_bta(struct platform_device *dsidev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002682{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302683 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2684
2685 if (dsi->debug_write || dsi->debug_read)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002686 DSSDBG("dsi_vc_send_bta %d\n", channel);
2687
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302688 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002689
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302690 /* RX_FIFO_NOT_EMPTY */
2691 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002692 DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302693 dsi_vc_flush_receive_data(dsidev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002694 }
2695
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302696 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002697
Tomi Valkeinen968f8e92011-10-12 10:13:14 +03002698 /* flush posted write */
2699 dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
2700
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002701 return 0;
2702}
2703
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002704static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002705{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302706 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002707 DECLARE_COMPLETION_ONSTACK(completion);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002708 int r = 0;
2709 u32 err;
2710
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302711 r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler,
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002712 &completion, DSI_VC_IRQ_BTA);
2713 if (r)
2714 goto err0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002715
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302716 r = dsi_register_isr(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002717 DSI_IRQ_ERROR_MASK);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002718 if (r)
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002719 goto err1;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002720
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302721 r = dsi_vc_send_bta(dsidev, channel);
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002722 if (r)
2723 goto err2;
2724
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002725 if (wait_for_completion_timeout(&completion,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002726 msecs_to_jiffies(500)) == 0) {
2727 DSSERR("Failed to receive BTA\n");
2728 r = -EIO;
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002729 goto err2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002730 }
2731
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302732 err = dsi_get_errors(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002733 if (err) {
2734 DSSERR("Error while sending BTA: %x\n", err);
2735 r = -EIO;
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002736 goto err2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002737 }
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002738err2:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302739 dsi_unregister_isr(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen773b30b2010-10-08 16:15:25 +03002740 DSI_IRQ_ERROR_MASK);
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002741err1:
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302742 dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler,
Tomi Valkeinenf36a06e2011-03-02 14:48:41 +02002743 &completion, DSI_VC_IRQ_BTA);
2744err0:
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002745 return r;
2746}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002747
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302748static inline void dsi_vc_write_long_header(struct platform_device *dsidev,
2749 int channel, u8 data_type, u16 len, u8 ecc)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002750{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302751 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002752 u32 val;
2753 u8 data_id;
2754
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302755 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002756
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302757 data_id = data_type | dsi->vc[channel].vc_id << 6;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002758
2759 val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
2760 FLD_VAL(ecc, 31, 24);
2761
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302762 dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002763}
2764
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302765static inline void dsi_vc_write_long_payload(struct platform_device *dsidev,
2766 int channel, u8 b1, u8 b2, u8 b3, u8 b4)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002767{
2768 u32 val;
2769
2770 val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0;
2771
2772/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
2773 b1, b2, b3, b4, val); */
2774
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302775 dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002776}
2777
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302778static int dsi_vc_send_long(struct platform_device *dsidev, int channel,
2779 u8 data_type, u8 *data, u16 len, u8 ecc)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002780{
2781 /*u32 val; */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302782 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002783 int i;
2784 u8 *p;
2785 int r = 0;
2786 u8 b1, b2, b3, b4;
2787
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302788 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002789 DSSDBG("dsi_vc_send_long, %d bytes\n", len);
2790
2791 /* len + header */
Tomi Valkeinen558c73e2013-09-25 14:40:06 +03002792 if (dsi->vc[channel].tx_fifo_size * 32 * 4 < len + 4) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002793 DSSERR("unable to send long packet: packet too long.\n");
2794 return -EINVAL;
2795 }
2796
Archit Tanejad6049142011-08-22 11:58:08 +05302797 dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002798
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302799 dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002800
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002801 p = data;
2802 for (i = 0; i < len >> 2; i++) {
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302803 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002804 DSSDBG("\tsending full packet %d\n", i);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002805
2806 b1 = *p++;
2807 b2 = *p++;
2808 b3 = *p++;
2809 b4 = *p++;
2810
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302811 dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002812 }
2813
2814 i = len % 4;
2815 if (i) {
2816 b1 = 0; b2 = 0; b3 = 0;
2817
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302818 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002819 DSSDBG("\tsending remainder bytes %d\n", i);
2820
2821 switch (i) {
2822 case 3:
2823 b1 = *p++;
2824 b2 = *p++;
2825 b3 = *p++;
2826 break;
2827 case 2:
2828 b1 = *p++;
2829 b2 = *p++;
2830 break;
2831 case 1:
2832 b1 = *p++;
2833 break;
2834 }
2835
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302836 dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002837 }
2838
2839 return r;
2840}
2841
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302842static int dsi_vc_send_short(struct platform_device *dsidev, int channel,
2843 u8 data_type, u16 data, u8 ecc)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002844{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302845 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002846 u32 r;
2847 u8 data_id;
2848
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302849 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002850
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302851 if (dsi->debug_write)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002852 DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
2853 channel,
2854 data_type, data & 0xff, (data >> 8) & 0xff);
2855
Archit Tanejad6049142011-08-22 11:58:08 +05302856 dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002857
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302858 if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002859 DSSERR("ERROR FIFO FULL, aborting transfer\n");
2860 return -EINVAL;
2861 }
2862
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302863 data_id = data_type | dsi->vc[channel].vc_id << 6;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002864
2865 r = (data_id << 0) | (data << 8) | (ecc << 24);
2866
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302867 dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002868
2869 return 0;
2870}
2871
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002872static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002873{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302874 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302875
Archit Taneja18b7d092011-09-05 17:01:08 +05302876 return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL,
2877 0, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002878}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002879
Archit Taneja9e7e9372012-08-14 12:29:22 +05302880static int dsi_vc_write_nosync_common(struct platform_device *dsidev,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302881 int channel, u8 *data, int len, enum dss_dsi_content_type type)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002882{
2883 int r;
2884
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302885 if (len == 0) {
2886 BUG_ON(type == DSS_DSI_CONTENT_DCS);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302887 r = dsi_vc_send_short(dsidev, channel,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302888 MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0);
2889 } else if (len == 1) {
2890 r = dsi_vc_send_short(dsidev, channel,
2891 type == DSS_DSI_CONTENT_GENERIC ?
2892 MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM :
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302893 MIPI_DSI_DCS_SHORT_WRITE, data[0], 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002894 } else if (len == 2) {
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302895 r = dsi_vc_send_short(dsidev, channel,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302896 type == DSS_DSI_CONTENT_GENERIC ?
2897 MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM :
Archit Taneja7a7c48f2011-08-25 18:25:03 +05302898 MIPI_DSI_DCS_SHORT_WRITE_PARAM,
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002899 data[0] | (data[1] << 8), 0);
2900 } else {
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302901 r = dsi_vc_send_long(dsidev, channel,
2902 type == DSS_DSI_CONTENT_GENERIC ?
2903 MIPI_DSI_GENERIC_LONG_WRITE :
2904 MIPI_DSI_DCS_LONG_WRITE, data, len, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002905 }
2906
2907 return r;
2908}
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302909
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002910static int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302911 u8 *data, int len)
2912{
Archit Taneja9e7e9372012-08-14 12:29:22 +05302913 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2914
2915 return dsi_vc_write_nosync_common(dsidev, channel, data, len,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302916 DSS_DSI_CONTENT_DCS);
2917}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002918
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002919static int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302920 u8 *data, int len)
2921{
Archit Taneja9e7e9372012-08-14 12:29:22 +05302922 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
2923
2924 return dsi_vc_write_nosync_common(dsidev, channel, data, len,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302925 DSS_DSI_CONTENT_GENERIC);
2926}
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302927
2928static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel,
2929 u8 *data, int len, enum dss_dsi_content_type type)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002930{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302931 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002932 int r;
2933
Archit Taneja9e7e9372012-08-14 12:29:22 +05302934 r = dsi_vc_write_nosync_common(dsidev, channel, data, len, type);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002935 if (r)
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02002936 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002937
Archit Taneja1ffefe72011-05-12 17:26:24 +05302938 r = dsi_vc_send_bta_sync(dssdev, channel);
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02002939 if (r)
2940 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002941
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302942 /* RX_FIFO_NOT_EMPTY */
2943 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
Tomi Valkeinenb63ac1e2010-04-09 13:20:57 +03002944 DSSERR("rx fifo not empty after write, dumping data:\n");
Archit Tanejaa72b64b2011-05-12 17:26:26 +05302945 dsi_vc_flush_receive_data(dsidev, channel);
Tomi Valkeinenb63ac1e2010-04-09 13:20:57 +03002946 r = -EIO;
2947 goto err;
2948 }
2949
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02002950 return 0;
2951err:
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302952 DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n",
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02002953 channel, data[0], len);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002954 return r;
2955}
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302956
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002957static int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302958 int len)
2959{
2960 return dsi_vc_write_common(dssdev, channel, data, len,
2961 DSS_DSI_CONTENT_DCS);
2962}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002963
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03002964static int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data,
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302965 int len)
2966{
2967 return dsi_vc_write_common(dssdev, channel, data, len,
2968 DSS_DSI_CONTENT_GENERIC);
2969}
Archit Taneja6ff8aa32011-08-25 18:35:58 +05302970
Archit Taneja9e7e9372012-08-14 12:29:22 +05302971static int dsi_vc_dcs_send_read_request(struct platform_device *dsidev,
Archit Tanejab8509752011-08-30 15:48:23 +05302972 int channel, u8 dcs_cmd)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02002973{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05302974 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejab8509752011-08-30 15:48:23 +05302975 int r;
2976
2977 if (dsi->debug_read)
2978 DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n",
2979 channel, dcs_cmd);
2980
2981 r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0);
2982 if (r) {
2983 DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)"
2984 " failed\n", channel, dcs_cmd);
2985 return r;
2986 }
2987
2988 return 0;
2989}
2990
Archit Taneja9e7e9372012-08-14 12:29:22 +05302991static int dsi_vc_generic_send_read_request(struct platform_device *dsidev,
Archit Tanejab3b89c02011-08-30 16:07:39 +05302992 int channel, u8 *reqdata, int reqlen)
2993{
Archit Tanejab3b89c02011-08-30 16:07:39 +05302994 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
2995 u16 data;
2996 u8 data_type;
2997 int r;
2998
2999 if (dsi->debug_read)
3000 DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n",
3001 channel, reqlen);
3002
3003 if (reqlen == 0) {
3004 data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
3005 data = 0;
3006 } else if (reqlen == 1) {
3007 data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
3008 data = reqdata[0];
3009 } else if (reqlen == 2) {
3010 data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
3011 data = reqdata[0] | (reqdata[1] << 8);
3012 } else {
3013 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +03003014 return -EINVAL;
Archit Tanejab3b89c02011-08-30 16:07:39 +05303015 }
3016
3017 r = dsi_vc_send_short(dsidev, channel, data_type, data, 0);
3018 if (r) {
3019 DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)"
3020 " failed\n", channel, reqlen);
3021 return r;
3022 }
3023
3024 return 0;
3025}
3026
3027static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel,
3028 u8 *buf, int buflen, enum dss_dsi_content_type type)
Archit Tanejab8509752011-08-30 15:48:23 +05303029{
3030 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003031 u32 val;
3032 u8 dt;
3033 int r;
3034
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003035 /* RX_FIFO_NOT_EMPTY */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303036 if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003037 DSSERR("RX fifo empty when trying to read.\n");
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003038 r = -EIO;
3039 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003040 }
3041
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303042 val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303043 if (dsi->debug_read)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003044 DSSDBG("\theader: %08x\n", val);
3045 dt = FLD_GET(val, 5, 0);
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303046 if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003047 u16 err = FLD_GET(val, 23, 8);
3048 dsi_show_rx_ack_with_err(err);
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003049 r = -EIO;
3050 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003051
Archit Tanejab3b89c02011-08-30 16:07:39 +05303052 } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
3053 MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
3054 MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003055 u8 data = FLD_GET(val, 15, 8);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303056 if (dsi->debug_read)
Archit Tanejab3b89c02011-08-30 16:07:39 +05303057 DSSDBG("\t%s short response, 1 byte: %02x\n",
3058 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3059 "DCS", data);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003060
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003061 if (buflen < 1) {
3062 r = -EIO;
3063 goto err;
3064 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003065
3066 buf[0] = data;
3067
3068 return 1;
Archit Tanejab3b89c02011-08-30 16:07:39 +05303069 } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
3070 MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
3071 MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003072 u16 data = FLD_GET(val, 23, 8);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303073 if (dsi->debug_read)
Archit Tanejab3b89c02011-08-30 16:07:39 +05303074 DSSDBG("\t%s short response, 2 byte: %04x\n",
3075 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3076 "DCS", data);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003077
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003078 if (buflen < 2) {
3079 r = -EIO;
3080 goto err;
3081 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003082
3083 buf[0] = data & 0xff;
3084 buf[1] = (data >> 8) & 0xff;
3085
3086 return 2;
Archit Tanejab3b89c02011-08-30 16:07:39 +05303087 } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
3088 MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
3089 MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003090 int w;
3091 int len = FLD_GET(val, 23, 8);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303092 if (dsi->debug_read)
Archit Tanejab3b89c02011-08-30 16:07:39 +05303093 DSSDBG("\t%s long response, len %d\n",
3094 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
3095 "DCS", len);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003096
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003097 if (len > buflen) {
3098 r = -EIO;
3099 goto err;
3100 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003101
3102 /* two byte checksum ends the packet, not included in len */
3103 for (w = 0; w < len + 2;) {
3104 int b;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303105 val = dsi_read_reg(dsidev,
3106 DSI_VC_SHORT_PACKET_HEADER(channel));
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303107 if (dsi->debug_read)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003108 DSSDBG("\t\t%02x %02x %02x %02x\n",
3109 (val >> 0) & 0xff,
3110 (val >> 8) & 0xff,
3111 (val >> 16) & 0xff,
3112 (val >> 24) & 0xff);
3113
3114 for (b = 0; b < 4; ++b) {
3115 if (w < len)
3116 buf[w] = (val >> (b * 8)) & 0xff;
3117 /* we discard the 2 byte checksum */
3118 ++w;
3119 }
3120 }
3121
3122 return len;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003123 } else {
3124 DSSERR("\tunknown datatype 0x%02x\n", dt);
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003125 r = -EIO;
3126 goto err;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003127 }
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003128
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003129err:
Archit Tanejab3b89c02011-08-30 16:07:39 +05303130 DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel,
3131 type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
Tomi Valkeinen5d68e032010-02-26 11:32:56 +02003132
Archit Tanejab8509752011-08-30 15:48:23 +05303133 return r;
3134}
3135
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03003136static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
Archit Tanejab8509752011-08-30 15:48:23 +05303137 u8 *buf, int buflen)
3138{
3139 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3140 int r;
3141
Archit Taneja9e7e9372012-08-14 12:29:22 +05303142 r = dsi_vc_dcs_send_read_request(dsidev, channel, dcs_cmd);
Archit Tanejab8509752011-08-30 15:48:23 +05303143 if (r)
3144 goto err;
3145
3146 r = dsi_vc_send_bta_sync(dssdev, channel);
3147 if (r)
3148 goto err;
3149
Archit Tanejab3b89c02011-08-30 16:07:39 +05303150 r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
3151 DSS_DSI_CONTENT_DCS);
Archit Tanejab8509752011-08-30 15:48:23 +05303152 if (r < 0)
3153 goto err;
3154
3155 if (r != buflen) {
3156 r = -EIO;
3157 goto err;
3158 }
3159
3160 return 0;
3161err:
3162 DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd);
3163 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003164}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003165
Archit Tanejab3b89c02011-08-30 16:07:39 +05303166static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel,
3167 u8 *reqdata, int reqlen, u8 *buf, int buflen)
3168{
3169 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3170 int r;
3171
Archit Taneja9e7e9372012-08-14 12:29:22 +05303172 r = dsi_vc_generic_send_read_request(dsidev, channel, reqdata, reqlen);
Archit Tanejab3b89c02011-08-30 16:07:39 +05303173 if (r)
3174 return r;
3175
3176 r = dsi_vc_send_bta_sync(dssdev, channel);
3177 if (r)
3178 return r;
3179
3180 r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
3181 DSS_DSI_CONTENT_GENERIC);
3182 if (r < 0)
3183 return r;
3184
3185 if (r != buflen) {
3186 r = -EIO;
3187 return r;
3188 }
3189
3190 return 0;
3191}
3192
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03003193static int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel,
Archit Taneja1ffefe72011-05-12 17:26:24 +05303194 u16 len)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003195{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303196 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3197
Archit Taneja7a7c48f2011-08-25 18:25:03 +05303198 return dsi_vc_send_short(dsidev, channel,
3199 MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003200}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003201
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303202static int dsi_enter_ulps(struct platform_device *dsidev)
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003203{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303204 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003205 DECLARE_COMPLETION_ONSTACK(completion);
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003206 int r, i;
3207 unsigned mask;
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003208
Chandrabhanu Mahapatra702d2672012-09-24 17:12:58 +05303209 DSSDBG("Entering ULPS");
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003210
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303211 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003212
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303213 WARN_ON(dsi->ulps_enabled);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003214
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303215 if (dsi->ulps_enabled)
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003216 return 0;
3217
Tomi Valkeinen6cc78aa2011-10-13 19:22:43 +03003218 /* DDR_CLK_ALWAYS_ON */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303219 if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) {
Tomi Valkeinen6cc78aa2011-10-13 19:22:43 +03003220 dsi_if_enable(dsidev, 0);
3221 REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
3222 dsi_if_enable(dsidev, 1);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003223 }
3224
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303225 dsi_sync_vc(dsidev, 0);
3226 dsi_sync_vc(dsidev, 1);
3227 dsi_sync_vc(dsidev, 2);
3228 dsi_sync_vc(dsidev, 3);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003229
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303230 dsi_force_tx_stop_mode_io(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003231
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303232 dsi_vc_enable(dsidev, 0, false);
3233 dsi_vc_enable(dsidev, 1, false);
3234 dsi_vc_enable(dsidev, 2, false);
3235 dsi_vc_enable(dsidev, 3, false);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003236
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303237 if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003238 DSSERR("HS busy when enabling ULPS\n");
3239 return -EIO;
3240 }
3241
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303242 if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003243 DSSERR("LP busy when enabling ULPS\n");
3244 return -EIO;
3245 }
3246
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303247 r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003248 DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3249 if (r)
3250 return r;
3251
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003252 mask = 0;
3253
3254 for (i = 0; i < dsi->num_lanes_supported; ++i) {
3255 if (dsi->lanes[i].function == DSI_LANE_UNUSED)
3256 continue;
3257 mask |= 1 << i;
3258 }
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003259 /* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */
3260 /* LANEx_ULPS_SIG2 */
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003261 REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, mask, 9, 5);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003262
Tomi Valkeinena702c852011-10-12 10:10:21 +03003263 /* flush posted write and wait for SCP interface to finish the write */
3264 dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003265
3266 if (wait_for_completion_timeout(&completion,
3267 msecs_to_jiffies(1000)) == 0) {
3268 DSSERR("ULPS enable timeout\n");
3269 r = -EIO;
3270 goto err;
3271 }
3272
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303273 dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003274 DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3275
Tomi Valkeinen8ef0e612011-05-31 16:55:47 +03003276 /* Reset LANEx_ULPS_SIG2 */
Tomi Valkeinen522a0c22011-10-13 16:18:52 +03003277 REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, 0, 9, 5);
Tomi Valkeinen8ef0e612011-05-31 16:55:47 +03003278
Tomi Valkeinena702c852011-10-12 10:10:21 +03003279 /* flush posted write and wait for SCP interface to finish the write */
3280 dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003281
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303282 dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003283
3284 dsi_if_enable(dsidev, false);
3285
3286 dsi->ulps_enabled = true;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303287
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03003288 return 0;
3289
3290err:
3291 dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303292 DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
3293 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003294}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003295
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003296static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
3297 unsigned ticks, bool x4, bool x16)
3298{
3299 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003300 unsigned long total_ticks;
3301 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303302
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003303 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303304
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003305 /* ticks in DSI_FCK */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003306 fck = dsi_fclk_rate(dsidev);
3307
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003308 r = dsi_read_reg(dsidev, DSI_TIMING2);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303309 r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003310 r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003311 r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */
3312 r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
3313 dsi_write_reg(dsidev, DSI_TIMING2, r);
3314
3315 total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3316
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003317 DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
3318 total_ticks,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303319 ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3320 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003321}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003322
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003323static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
3324 bool x8, bool x16)
3325{
3326 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003327 unsigned long total_ticks;
3328 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303329
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003330 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303331
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003332 /* ticks in DSI_FCK */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003333 fck = dsi_fclk_rate(dsidev);
3334
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003335 r = dsi_read_reg(dsidev, DSI_TIMING1);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303336 r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003337 r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003338 r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */
3339 r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
3340 dsi_write_reg(dsidev, DSI_TIMING1, r);
3341
3342 total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
3343
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003344 DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
3345 total_ticks,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303346 ticks, x8 ? " x8" : "", x16 ? " x16" : "",
3347 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003348}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003349
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003350static void dsi_set_stop_state_counter(struct platform_device *dsidev,
3351 unsigned ticks, bool x4, bool x16)
3352{
3353 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003354 unsigned long total_ticks;
3355 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303356
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003357 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303358
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003359 /* ticks in DSI_FCK */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003360 fck = dsi_fclk_rate(dsidev);
3361
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003362 r = dsi_read_reg(dsidev, DSI_TIMING1);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303363 r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003364 r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003365 r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */
3366 r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
3367 dsi_write_reg(dsidev, DSI_TIMING1, r);
3368
3369 total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3370
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003371 DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
3372 total_ticks,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303373 ticks, x4 ? " x4" : "", x16 ? " x16" : "",
3374 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003375}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003376
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003377static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
3378 unsigned ticks, bool x4, bool x16)
3379{
3380 unsigned long fck;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003381 unsigned long total_ticks;
3382 u32 r;
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303383
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003384 BUG_ON(ticks > 0x1fff);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303385
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003386 /* ticks in TxByteClkHS */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003387 fck = dsi_get_txbyteclkhs(dsidev);
3388
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003389 r = dsi_read_reg(dsidev, DSI_TIMING2);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303390 r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003391 r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */
Tomi Valkeinen4ffa3572010-04-12 10:40:12 +03003392 r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */
3393 r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
3394 dsi_write_reg(dsidev, DSI_TIMING2, r);
3395
3396 total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
3397
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003398 DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
3399 total_ticks,
3400 ticks, x4 ? " x4" : "", x16 ? " x16" : "",
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303401 (total_ticks * 1000) / (fck / 1000 / 1000));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003402}
Archit Taneja8af6ff02011-09-05 16:48:27 +05303403
Archit Taneja9e7e9372012-08-14 12:29:22 +05303404static void dsi_config_vp_num_line_buffers(struct platform_device *dsidev)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303405{
Archit Tanejadca2b152012-08-16 18:02:00 +05303406 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303407 int num_line_buffers;
3408
Archit Tanejadca2b152012-08-16 18:02:00 +05303409 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
Archit Taneja02c39602012-08-10 15:01:33 +05303410 int bpp = dsi_get_pixel_size(dsi->pix_fmt);
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003411 struct videomode *vm = &dsi->vm;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303412 /*
3413 * Don't use line buffers if width is greater than the video
3414 * port's line buffer size
3415 */
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003416 if (dsi->line_buffer_size <= vm->hactive * bpp / 8)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303417 num_line_buffers = 0;
3418 else
3419 num_line_buffers = 2;
3420 } else {
3421 /* Use maximum number of line buffers in command mode */
3422 num_line_buffers = 2;
3423 }
3424
3425 /* LINE_BUFFER */
3426 REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12);
3427}
3428
Archit Taneja9e7e9372012-08-14 12:29:22 +05303429static void dsi_config_vp_sync_events(struct platform_device *dsidev)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303430{
Archit Taneja0b3ffe32012-08-13 22:13:39 +05303431 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen478d7df2013-03-05 16:29:36 +02003432 bool sync_end;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303433 u32 r;
3434
Tomi Valkeinen478d7df2013-03-05 16:29:36 +02003435 if (dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE)
3436 sync_end = true;
3437 else
3438 sync_end = false;
3439
Archit Taneja8af6ff02011-09-05 16:48:27 +05303440 r = dsi_read_reg(dsidev, DSI_CTRL);
Archit Tanejabd5a7b12012-06-26 12:38:31 +05303441 r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */
3442 r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */
3443 r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */
Archit Taneja8af6ff02011-09-05 16:48:27 +05303444 r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
Tomi Valkeinen478d7df2013-03-05 16:29:36 +02003445 r = FLD_MOD(r, sync_end, 16, 16); /* VP_VSYNC_END */
Archit Taneja8af6ff02011-09-05 16:48:27 +05303446 r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
Tomi Valkeinen478d7df2013-03-05 16:29:36 +02003447 r = FLD_MOD(r, sync_end, 18, 18); /* VP_HSYNC_END */
Archit Taneja8af6ff02011-09-05 16:48:27 +05303448 dsi_write_reg(dsidev, DSI_CTRL, r);
3449}
3450
Archit Taneja9e7e9372012-08-14 12:29:22 +05303451static void dsi_config_blanking_modes(struct platform_device *dsidev)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303452{
Archit Taneja0b3ffe32012-08-13 22:13:39 +05303453 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3454 int blanking_mode = dsi->vm_timings.blanking_mode;
3455 int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode;
3456 int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode;
3457 int hsa_blanking_mode = dsi->vm_timings.hsa_blanking_mode;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303458 u32 r;
3459
3460 /*
3461 * 0 = TX FIFO packets sent or LPS in corresponding blanking periods
3462 * 1 = Long blanking packets are sent in corresponding blanking periods
3463 */
3464 r = dsi_read_reg(dsidev, DSI_CTRL);
3465 r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */
3466 r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */
3467 r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */
3468 r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */
3469 dsi_write_reg(dsidev, DSI_CTRL, r);
3470}
3471
Archit Taneja6f28c292012-05-15 11:32:18 +05303472/*
3473 * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3
3474 * results in maximum transition time for data and clock lanes to enter and
3475 * exit HS mode. Hence, this is the scenario where the least amount of command
3476 * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS
3477 * clock cycles that can be used to interleave command mode data in HS so that
3478 * all scenarios are satisfied.
3479 */
3480static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs,
3481 int exit_hs, int exiths_clk, int ddr_pre, int ddr_post)
3482{
3483 int transition;
3484
3485 /*
3486 * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition
3487 * time of data lanes only, if it isn't set, we need to consider HS
3488 * transition time of both data and clock lanes. HS transition time
3489 * of Scenario 3 is considered.
3490 */
3491 if (ddr_alwon) {
3492 transition = enter_hs + exit_hs + max(enter_hs, 2) + 1;
3493 } else {
3494 int trans1, trans2;
3495 trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1;
3496 trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre +
3497 enter_hs + 1;
3498 transition = max(trans1, trans2);
3499 }
3500
3501 return blank > transition ? blank - transition : 0;
3502}
3503
3504/*
3505 * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1
3506 * results in maximum transition time for data lanes to enter and exit LP mode.
3507 * Hence, this is the scenario where the least amount of command mode data can
3508 * be interleaved. We program the minimum amount of bytes that can be
3509 * interleaved in LP so that all scenarios are satisfied.
3510 */
3511static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs,
3512 int lp_clk_div, int tdsi_fclk)
3513{
3514 int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */
3515 int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */
3516 int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */
3517 int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */
3518 int lp_inter; /* cmd mode data that can be interleaved, in bytes */
3519
3520 /* maximum LP transition time according to Scenario 1 */
3521 trans_lp = exit_hs + max(enter_hs, 2) + 1;
3522
3523 /* CLKIN4DDR = 16 * TXBYTECLKHS */
3524 tlp_avail = thsbyte_clk * (blank - trans_lp);
3525
Archit Taneja2e063c32012-06-04 13:36:34 +05303526 ttxclkesc = tdsi_fclk * lp_clk_div;
Archit Taneja6f28c292012-05-15 11:32:18 +05303527
3528 lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc -
3529 26) / 16;
3530
3531 return max(lp_inter, 0);
3532}
3533
Tomi Valkeinen57612172012-11-27 17:32:36 +02003534static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev)
Archit Taneja6f28c292012-05-15 11:32:18 +05303535{
Archit Taneja6f28c292012-05-15 11:32:18 +05303536 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3537 int blanking_mode;
3538 int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode;
3539 int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div;
3540 int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat;
3541 int tclk_trail, ths_exit, exiths_clk;
3542 bool ddr_alwon;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003543 struct videomode *vm = &dsi->vm;
Archit Taneja02c39602012-08-10 15:01:33 +05303544 int bpp = dsi_get_pixel_size(dsi->pix_fmt);
Archit Taneja6f28c292012-05-15 11:32:18 +05303545 int ndl = dsi->num_lanes_used - 1;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03003546 int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1;
Archit Taneja6f28c292012-05-15 11:32:18 +05303547 int hsa_interleave_hs = 0, hsa_interleave_lp = 0;
3548 int hfp_interleave_hs = 0, hfp_interleave_lp = 0;
3549 int hbp_interleave_hs = 0, hbp_interleave_lp = 0;
3550 int bl_interleave_hs = 0, bl_interleave_lp = 0;
3551 u32 r;
3552
3553 r = dsi_read_reg(dsidev, DSI_CTRL);
3554 blanking_mode = FLD_GET(r, 20, 20);
3555 hfp_blanking_mode = FLD_GET(r, 21, 21);
3556 hbp_blanking_mode = FLD_GET(r, 22, 22);
3557 hsa_blanking_mode = FLD_GET(r, 23, 23);
3558
3559 r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
3560 hbp = FLD_GET(r, 11, 0);
3561 hfp = FLD_GET(r, 23, 12);
3562 hsa = FLD_GET(r, 31, 24);
3563
3564 r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
3565 ddr_clk_post = FLD_GET(r, 7, 0);
3566 ddr_clk_pre = FLD_GET(r, 15, 8);
3567
3568 r = dsi_read_reg(dsidev, DSI_VM_TIMING7);
3569 exit_hs_mode_lat = FLD_GET(r, 15, 0);
3570 enter_hs_mode_lat = FLD_GET(r, 31, 16);
3571
3572 r = dsi_read_reg(dsidev, DSI_CLK_CTRL);
3573 lp_clk_div = FLD_GET(r, 12, 0);
3574 ddr_alwon = FLD_GET(r, 13, 13);
3575
3576 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
3577 ths_exit = FLD_GET(r, 7, 0);
3578
3579 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
3580 tclk_trail = FLD_GET(r, 15, 8);
3581
3582 exiths_clk = ths_exit + tclk_trail;
3583
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003584 width_bytes = DIV_ROUND_UP(vm->hactive * bpp, 8);
Archit Taneja6f28c292012-05-15 11:32:18 +05303585 bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl);
3586
3587 if (!hsa_blanking_mode) {
3588 hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon,
3589 enter_hs_mode_lat, exit_hs_mode_lat,
3590 exiths_clk, ddr_clk_pre, ddr_clk_post);
3591 hsa_interleave_lp = dsi_compute_interleave_lp(hsa,
3592 enter_hs_mode_lat, exit_hs_mode_lat,
3593 lp_clk_div, dsi_fclk_hsdiv);
3594 }
3595
3596 if (!hfp_blanking_mode) {
3597 hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon,
3598 enter_hs_mode_lat, exit_hs_mode_lat,
3599 exiths_clk, ddr_clk_pre, ddr_clk_post);
3600 hfp_interleave_lp = dsi_compute_interleave_lp(hfp,
3601 enter_hs_mode_lat, exit_hs_mode_lat,
3602 lp_clk_div, dsi_fclk_hsdiv);
3603 }
3604
3605 if (!hbp_blanking_mode) {
3606 hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon,
3607 enter_hs_mode_lat, exit_hs_mode_lat,
3608 exiths_clk, ddr_clk_pre, ddr_clk_post);
3609
3610 hbp_interleave_lp = dsi_compute_interleave_lp(hbp,
3611 enter_hs_mode_lat, exit_hs_mode_lat,
3612 lp_clk_div, dsi_fclk_hsdiv);
3613 }
3614
3615 if (!blanking_mode) {
3616 bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon,
3617 enter_hs_mode_lat, exit_hs_mode_lat,
3618 exiths_clk, ddr_clk_pre, ddr_clk_post);
3619
3620 bl_interleave_lp = dsi_compute_interleave_lp(bllp,
3621 enter_hs_mode_lat, exit_hs_mode_lat,
3622 lp_clk_div, dsi_fclk_hsdiv);
3623 }
3624
3625 DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n",
3626 hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs,
3627 bl_interleave_hs);
3628
3629 DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n",
3630 hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp,
3631 bl_interleave_lp);
3632
3633 r = dsi_read_reg(dsidev, DSI_VM_TIMING4);
3634 r = FLD_MOD(r, hsa_interleave_hs, 23, 16);
3635 r = FLD_MOD(r, hfp_interleave_hs, 15, 8);
3636 r = FLD_MOD(r, hbp_interleave_hs, 7, 0);
3637 dsi_write_reg(dsidev, DSI_VM_TIMING4, r);
3638
3639 r = dsi_read_reg(dsidev, DSI_VM_TIMING5);
3640 r = FLD_MOD(r, hsa_interleave_lp, 23, 16);
3641 r = FLD_MOD(r, hfp_interleave_lp, 15, 8);
3642 r = FLD_MOD(r, hbp_interleave_lp, 7, 0);
3643 dsi_write_reg(dsidev, DSI_VM_TIMING5, r);
3644
3645 r = dsi_read_reg(dsidev, DSI_VM_TIMING6);
3646 r = FLD_MOD(r, bl_interleave_hs, 31, 15);
3647 r = FLD_MOD(r, bl_interleave_lp, 16, 0);
3648 dsi_write_reg(dsidev, DSI_VM_TIMING6, r);
3649}
3650
Tomi Valkeinen57612172012-11-27 17:32:36 +02003651static int dsi_proto_config(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003652{
Archit Taneja02c39602012-08-10 15:01:33 +05303653 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003654 u32 r;
3655 int buswidth = 0;
3656
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303657 dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32,
Tomi Valkeinendd8079d2009-12-16 16:49:03 +02003658 DSI_FIFO_SIZE_32,
3659 DSI_FIFO_SIZE_32,
3660 DSI_FIFO_SIZE_32);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003661
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303662 dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32,
Tomi Valkeinendd8079d2009-12-16 16:49:03 +02003663 DSI_FIFO_SIZE_32,
3664 DSI_FIFO_SIZE_32,
3665 DSI_FIFO_SIZE_32);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003666
3667 /* XXX what values for the timeouts? */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303668 dsi_set_stop_state_counter(dsidev, 0x1000, false, false);
3669 dsi_set_ta_timeout(dsidev, 0x1fff, true, true);
3670 dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true);
3671 dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003672
Archit Taneja02c39602012-08-10 15:01:33 +05303673 switch (dsi_get_pixel_size(dsi->pix_fmt)) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003674 case 16:
3675 buswidth = 0;
3676 break;
3677 case 18:
3678 buswidth = 1;
3679 break;
3680 case 24:
3681 buswidth = 2;
3682 break;
3683 default:
3684 BUG();
Tomi Valkeinenc6eee962012-05-18 11:47:02 +03003685 return -EINVAL;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003686 }
3687
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303688 r = dsi_read_reg(dsidev, DSI_CTRL);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003689 r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */
3690 r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */
3691 r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
3692 r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
3693 r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
3694 r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003695 r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
3696 r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03003697 if (!(dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC)) {
Archit Taneja9613c022011-03-22 06:33:36 -05003698 r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */
3699 /* DCS_CMD_CODE, 1=start, 0=continue */
3700 r = FLD_MOD(r, 0, 25, 25);
3701 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003702
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303703 dsi_write_reg(dsidev, DSI_CTRL, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003704
Archit Taneja9e7e9372012-08-14 12:29:22 +05303705 dsi_config_vp_num_line_buffers(dsidev);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303706
Archit Tanejadca2b152012-08-16 18:02:00 +05303707 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
Archit Taneja9e7e9372012-08-14 12:29:22 +05303708 dsi_config_vp_sync_events(dsidev);
3709 dsi_config_blanking_modes(dsidev);
Tomi Valkeinen57612172012-11-27 17:32:36 +02003710 dsi_config_cmd_mode_interleaving(dsidev);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303711 }
3712
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303713 dsi_vc_initial_config(dsidev, 0);
3714 dsi_vc_initial_config(dsidev, 1);
3715 dsi_vc_initial_config(dsidev, 2);
3716 dsi_vc_initial_config(dsidev, 3);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003717
3718 return 0;
3719}
3720
Archit Taneja9e7e9372012-08-14 12:29:22 +05303721static void dsi_proto_timings(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003722{
Tomi Valkeinendb186442011-10-13 16:12:29 +03003723 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003724 unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail;
3725 unsigned tclk_pre, tclk_post;
3726 unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
3727 unsigned ths_trail, ths_exit;
3728 unsigned ddr_clk_pre, ddr_clk_post;
3729 unsigned enter_hs_mode_lat, exit_hs_mode_lat;
3730 unsigned ths_eot;
Tomi Valkeinendb186442011-10-13 16:12:29 +03003731 int ndl = dsi->num_lanes_used - 1;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003732 u32 r;
3733
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303734 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003735 ths_prepare = FLD_GET(r, 31, 24);
3736 ths_prepare_ths_zero = FLD_GET(r, 23, 16);
3737 ths_zero = ths_prepare_ths_zero - ths_prepare;
3738 ths_trail = FLD_GET(r, 15, 8);
3739 ths_exit = FLD_GET(r, 7, 0);
3740
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303741 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
Tomi Valkeinene84dc1c2012-09-24 09:34:52 +03003742 tlpx = FLD_GET(r, 20, 16) * 2;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003743 tclk_trail = FLD_GET(r, 15, 8);
3744 tclk_zero = FLD_GET(r, 7, 0);
3745
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303746 r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003747 tclk_prepare = FLD_GET(r, 7, 0);
3748
3749 /* min 8*UI */
3750 tclk_pre = 20;
3751 /* min 60ns + 52*UI */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303752 tclk_post = ns2ddr(dsidev, 60) + 26;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003753
Archit Taneja8af6ff02011-09-05 16:48:27 +05303754 ths_eot = DIV_ROUND_UP(4, ndl);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003755
3756 ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
3757 4);
3758 ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
3759
3760 BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
3761 BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
3762
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303763 r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003764 r = FLD_MOD(r, ddr_clk_pre, 15, 8);
3765 r = FLD_MOD(r, ddr_clk_post, 7, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303766 dsi_write_reg(dsidev, DSI_CLK_TIMING, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003767
3768 DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
3769 ddr_clk_pre,
3770 ddr_clk_post);
3771
3772 enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
3773 DIV_ROUND_UP(ths_prepare, 4) +
3774 DIV_ROUND_UP(ths_zero + 3, 4);
3775
3776 exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
3777
3778 r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
3779 FLD_VAL(exit_hs_mode_lat, 15, 0);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05303780 dsi_write_reg(dsidev, DSI_VM_TIMING7, r);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003781
3782 DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
3783 enter_hs_mode_lat, exit_hs_mode_lat);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303784
Archit Tanejadca2b152012-08-16 18:02:00 +05303785 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
Archit Taneja8af6ff02011-09-05 16:48:27 +05303786 /* TODO: Implement a video mode check_timings function */
Archit Taneja0b3ffe32012-08-13 22:13:39 +05303787 int hsa = dsi->vm_timings.hsa;
3788 int hfp = dsi->vm_timings.hfp;
3789 int hbp = dsi->vm_timings.hbp;
3790 int vsa = dsi->vm_timings.vsa;
3791 int vfp = dsi->vm_timings.vfp;
3792 int vbp = dsi->vm_timings.vbp;
3793 int window_sync = dsi->vm_timings.window_sync;
Tomi Valkeinen478d7df2013-03-05 16:29:36 +02003794 bool hsync_end;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003795 struct videomode *vm = &dsi->vm;
Archit Taneja02c39602012-08-10 15:01:33 +05303796 int bpp = dsi_get_pixel_size(dsi->pix_fmt);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303797 int tl, t_he, width_bytes;
3798
Tomi Valkeinen478d7df2013-03-05 16:29:36 +02003799 hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303800 t_he = hsync_end ?
3801 ((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
3802
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003803 width_bytes = DIV_ROUND_UP(vm->hactive * bpp, 8);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303804
3805 /* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
3806 tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
3807 DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
3808
3809 DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
3810 hfp, hsync_end ? hsa : 0, tl);
3811 DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003812 vsa, vm->vactive);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303813
3814 r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
3815 r = FLD_MOD(r, hbp, 11, 0); /* HBP */
3816 r = FLD_MOD(r, hfp, 23, 12); /* HFP */
3817 r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */
3818 dsi_write_reg(dsidev, DSI_VM_TIMING1, r);
3819
3820 r = dsi_read_reg(dsidev, DSI_VM_TIMING2);
3821 r = FLD_MOD(r, vbp, 7, 0); /* VBP */
3822 r = FLD_MOD(r, vfp, 15, 8); /* VFP */
3823 r = FLD_MOD(r, vsa, 23, 16); /* VSA */
3824 r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */
3825 dsi_write_reg(dsidev, DSI_VM_TIMING2, r);
3826
3827 r = dsi_read_reg(dsidev, DSI_VM_TIMING3);
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003828 r = FLD_MOD(r, vm->vactive, 14, 0); /* VACT */
Archit Taneja8af6ff02011-09-05 16:48:27 +05303829 r = FLD_MOD(r, tl, 31, 16); /* TL */
3830 dsi_write_reg(dsidev, DSI_VM_TIMING3, r);
3831 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003832}
3833
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03003834static int dsi_configure_pins(struct omap_dss_device *dssdev,
Tomi Valkeinene4a9e942012-03-28 15:58:56 +03003835 const struct omap_dsi_pin_config *pin_cfg)
3836{
3837 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
3838 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
3839 int num_pins;
3840 const int *pins;
3841 struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
3842 int num_lanes;
3843 int i;
3844
3845 static const enum dsi_lane_function functions[] = {
3846 DSI_LANE_CLK,
3847 DSI_LANE_DATA1,
3848 DSI_LANE_DATA2,
3849 DSI_LANE_DATA3,
3850 DSI_LANE_DATA4,
3851 };
3852
3853 num_pins = pin_cfg->num_pins;
3854 pins = pin_cfg->pins;
3855
3856 if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2
3857 || num_pins % 2 != 0)
3858 return -EINVAL;
3859
3860 for (i = 0; i < DSI_MAX_NR_LANES; ++i)
3861 lanes[i].function = DSI_LANE_UNUSED;
3862
3863 num_lanes = 0;
3864
3865 for (i = 0; i < num_pins; i += 2) {
3866 u8 lane, pol;
3867 int dx, dy;
3868
3869 dx = pins[i];
3870 dy = pins[i + 1];
3871
3872 if (dx < 0 || dx >= dsi->num_lanes_supported * 2)
3873 return -EINVAL;
3874
3875 if (dy < 0 || dy >= dsi->num_lanes_supported * 2)
3876 return -EINVAL;
3877
3878 if (dx & 1) {
3879 if (dy != dx - 1)
3880 return -EINVAL;
3881 pol = 1;
3882 } else {
3883 if (dy != dx + 1)
3884 return -EINVAL;
3885 pol = 0;
3886 }
3887
3888 lane = dx / 2;
3889
3890 lanes[lane].function = functions[i / 2];
3891 lanes[lane].polarity = pol;
3892 num_lanes++;
3893 }
3894
3895 memcpy(dsi->lanes, lanes, sizeof(dsi->lanes));
3896 dsi->num_lanes_used = num_lanes;
3897
3898 return 0;
3899}
Tomi Valkeinene4a9e942012-03-28 15:58:56 +03003900
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03003901static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303902{
3903 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejae67458a2012-08-13 14:17:30 +05303904 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003905 enum omap_channel dispc_channel = dssdev->dispc_channel;
Archit Taneja02c39602012-08-10 15:01:33 +05303906 int bpp = dsi_get_pixel_size(dsi->pix_fmt);
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03003907 struct omap_dss_device *out = &dsi->output;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303908 u8 data_type;
3909 u16 word_count;
Tomi Valkeinen33ca2372011-11-21 13:42:58 +02003910 int r;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303911
Tomi Valkeinenf1504ad2015-11-05 09:34:51 +02003912 if (!out->dispc_channel_connected) {
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003913 DSSERR("failed to enable display: no output/manager\n");
3914 return -ENODEV;
3915 }
3916
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003917 r = dsi_display_init_dispc(dsidev, dispc_channel);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003918 if (r)
3919 goto err_init_dispc;
3920
Archit Tanejadca2b152012-08-16 18:02:00 +05303921 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
Archit Taneja02c39602012-08-10 15:01:33 +05303922 switch (dsi->pix_fmt) {
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003923 case OMAP_DSS_DSI_FMT_RGB888:
3924 data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
3925 break;
3926 case OMAP_DSS_DSI_FMT_RGB666:
3927 data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
3928 break;
3929 case OMAP_DSS_DSI_FMT_RGB666_PACKED:
3930 data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
3931 break;
3932 case OMAP_DSS_DSI_FMT_RGB565:
3933 data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
3934 break;
3935 default:
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003936 r = -EINVAL;
3937 goto err_pix_fmt;
Joe Perchescf6ac4ce2013-10-08 16:23:24 -07003938 }
Archit Taneja8af6ff02011-09-05 16:48:27 +05303939
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003940 dsi_if_enable(dsidev, false);
3941 dsi_vc_enable(dsidev, channel, false);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303942
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003943 /* MODE, 1 = video mode */
3944 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303945
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03003946 word_count = DIV_ROUND_UP(dsi->vm.hactive * bpp, 8);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303947
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003948 dsi_vc_write_long_header(dsidev, channel, data_type,
3949 word_count, 0);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303950
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003951 dsi_vc_enable(dsidev, channel, true);
3952 dsi_if_enable(dsidev, true);
3953 }
Archit Taneja8af6ff02011-09-05 16:48:27 +05303954
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003955 r = dss_mgr_enable(dispc_channel);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003956 if (r)
3957 goto err_mgr_enable;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303958
3959 return 0;
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003960
3961err_mgr_enable:
3962 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3963 dsi_if_enable(dsidev, false);
3964 dsi_vc_enable(dsidev, channel, false);
3965 }
3966err_pix_fmt:
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003967 dsi_display_uninit_dispc(dsidev, dispc_channel);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003968err_init_dispc:
3969 return r;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303970}
Archit Taneja8af6ff02011-09-05 16:48:27 +05303971
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03003972static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel)
Archit Taneja8af6ff02011-09-05 16:48:27 +05303973{
3974 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejadca2b152012-08-16 18:02:00 +05303975 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003976 enum omap_channel dispc_channel = dssdev->dispc_channel;
Archit Taneja8af6ff02011-09-05 16:48:27 +05303977
Archit Tanejadca2b152012-08-16 18:02:00 +05303978 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003979 dsi_if_enable(dsidev, false);
3980 dsi_vc_enable(dsidev, channel, false);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303981
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003982 /* MODE, 0 = command mode */
3983 REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303984
Tomi Valkeinen9a147a62011-11-09 15:30:11 +02003985 dsi_vc_enable(dsidev, channel, true);
3986 dsi_if_enable(dsidev, true);
3987 }
Archit Taneja8af6ff02011-09-05 16:48:27 +05303988
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003989 dss_mgr_disable(dispc_channel);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02003990
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003991 dsi_display_uninit_dispc(dsidev, dispc_channel);
Archit Taneja8af6ff02011-09-05 16:48:27 +05303992}
Archit Taneja8af6ff02011-09-05 16:48:27 +05303993
Tomi Valkeinen57612172012-11-27 17:32:36 +02003994static void dsi_update_screen_dispc(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003995{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05303996 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen0674d382015-11-05 10:01:02 +02003997 enum omap_channel dispc_channel = dsi->output.dispc_channel;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02003998 unsigned bytespp;
3999 unsigned bytespl;
4000 unsigned bytespf;
4001 unsigned total_len;
4002 unsigned packet_payload;
4003 unsigned packet_len;
4004 u32 l;
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004005 int r;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304006 const unsigned channel = dsi->update_channel;
Tomi Valkeinen99322572013-03-05 10:37:02 +02004007 const unsigned line_buf_size = dsi->line_buffer_size;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004008 u16 w = dsi->vm.hactive;
4009 u16 h = dsi->vm.vactive;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004010
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004011 DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004012
Archit Tanejad6049142011-08-22 11:58:08 +05304013 dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004014
Archit Taneja02c39602012-08-10 15:01:33 +05304015 bytespp = dsi_get_pixel_size(dsi->pix_fmt) / 8;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004016 bytespl = w * bytespp;
4017 bytespf = bytespl * h;
4018
4019 /* NOTE: packet_payload has to be equal to N * bytespl, where N is
4020 * number of lines in a packet. See errata about VP_CLK_RATIO */
4021
4022 if (bytespf < line_buf_size)
4023 packet_payload = bytespf;
4024 else
4025 packet_payload = (line_buf_size) / bytespl * bytespl;
4026
4027 packet_len = packet_payload + 1; /* 1 byte for DCS cmd */
4028 total_len = (bytespf / packet_payload) * packet_len;
4029
4030 if (bytespf % packet_payload)
4031 total_len += (bytespf % packet_payload) + 1;
4032
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004033 l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304034 dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004035
Archit Taneja7a7c48f2011-08-25 18:25:03 +05304036 dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE,
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304037 packet_len, 0);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004038
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304039 if (dsi->te_enabled)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004040 l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
4041 else
4042 l = FLD_MOD(l, 1, 31, 31); /* TE_START */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304043 dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004044
4045 /* We put SIDLEMODE to no-idle for the duration of the transfer,
4046 * because DSS interrupts are not capable of waking up the CPU and the
4047 * framedone interrupt could be delayed for quite a long time. I think
4048 * the same goes for any DSS interrupts, but for some reason I have not
4049 * seen the problem anywhere else than here.
4050 */
4051 dispc_disable_sidle();
4052
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304053 dsi_perf_mark_start(dsidev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004054
Archit Taneja49dbf582011-05-16 15:17:07 +05304055 r = schedule_delayed_work(&dsi->framedone_timeout_work,
4056 msecs_to_jiffies(250));
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004057 BUG_ON(r == 0);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004058
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004059 dss_mgr_set_timings(dispc_channel, &dsi->vm);
Archit Taneja55cd63a2012-08-09 15:41:13 +05304060
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004061 dss_mgr_start_update(dispc_channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004062
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304063 if (dsi->te_enabled) {
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004064 /* disable LP_RX_TO, so that we can receive TE. Time to wait
4065 * for TE is longer than the timer allows */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304066 REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004067
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304068 dsi_vc_send_bta(dsidev, channel);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004069
4070#ifdef DSI_CATCH_MISSING_TE
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304071 mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004072#endif
4073 }
4074}
4075
4076#ifdef DSI_CATCH_MISSING_TE
4077static void dsi_te_timeout(unsigned long arg)
4078{
4079 DSSERR("TE not received for 250ms!\n");
4080}
4081#endif
4082
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304083static void dsi_handle_framedone(struct platform_device *dsidev, int error)
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004084{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304085 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4086
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004087 /* SIDLEMODE back to smart-idle */
4088 dispc_enable_sidle();
4089
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304090 if (dsi->te_enabled) {
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004091 /* enable LP_RX_TO again after the TE */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304092 REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004093 }
4094
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304095 dsi->framedone_callback(error, dsi->framedone_data);
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004096
4097 if (!error)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304098 dsi_perf_show(dsidev, "DISPC");
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004099}
4100
4101static void dsi_framedone_timeout_work_callback(struct work_struct *work)
4102{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304103 struct dsi_data *dsi = container_of(work, struct dsi_data,
4104 framedone_timeout_work.work);
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004105 /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
4106 * 250ms which would conflict with this timeout work. What should be
4107 * done is first cancel the transfer on the HW, and then cancel the
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004108 * possibly scheduled framedone work. However, cancelling the transfer
4109 * on the HW is buggy, and would probably require resetting the whole
4110 * DSI */
Tomi Valkeinen0f16aa02010-04-12 09:57:19 +03004111
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004112 DSSERR("Framedone not received for 250ms!\n");
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004113
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304114 dsi_handle_framedone(dsi->pdev, -ETIMEDOUT);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004115}
4116
Tomi Valkeinen15502022012-10-10 13:59:07 +03004117static void dsi_framedone_irq_callback(void *data)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004118{
Archit Taneja9e7e9372012-08-14 12:29:22 +05304119 struct platform_device *dsidev = (struct platform_device *) data;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304120 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4121
Tomi Valkeinenab83b142010-06-09 15:31:01 +03004122 /* Note: We get FRAMEDONE when DISPC has finished sending pixels and
4123 * turns itself off. However, DSI still has the pixels in its buffers,
4124 * and is sending the data.
4125 */
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004126
Tejun Heo136b5722012-08-21 13:18:24 -07004127 cancel_delayed_work(&dsi->framedone_timeout_work);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004128
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304129 dsi_handle_framedone(dsidev, 0);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004130}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004131
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004132static int dsi_update(struct omap_dss_device *dssdev, int channel,
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004133 void (*callback)(int, void *), void *data)
4134{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304135 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304136 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004137 u16 dw, dh;
4138
4139 dsi_perf_mark_setup(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304140
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304141 dsi->update_channel = channel;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004142
Tomi Valkeinen4a9e78a2011-08-15 11:22:21 +03004143 dsi->framedone_callback = callback;
4144 dsi->framedone_data = data;
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004145
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004146 dw = dsi->vm.hactive;
4147 dh = dsi->vm.vactive;
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004148
Tomi Valkeinen477fed72013-10-02 14:41:24 +03004149#ifdef DSI_PERF_MEASURE
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004150 dsi->update_bytes = dw * dh *
Archit Taneja02c39602012-08-10 15:01:33 +05304151 dsi_get_pixel_size(dsi->pix_fmt) / 8;
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004152#endif
Tomi Valkeinen57612172012-11-27 17:32:36 +02004153 dsi_update_screen_dispc(dsidev);
Tomi Valkeinen18946f62010-01-12 14:16:41 +02004154
4155 return 0;
4156}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004157
4158/* Display funcs */
4159
Tomi Valkeinen57612172012-11-27 17:32:36 +02004160static int dsi_configure_dispc_clocks(struct platform_device *dsidev)
Archit Taneja7d2572f2012-06-29 14:31:07 +05304161{
Archit Taneja7d2572f2012-06-29 14:31:07 +05304162 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4163 struct dispc_clock_info dispc_cinfo;
4164 int r;
Tomi Valkeinen17518182013-03-07 11:21:45 +02004165 unsigned long fck;
Archit Taneja7d2572f2012-06-29 14:31:07 +05304166
4167 fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
4168
Tomi Valkeinena0d269e2012-11-27 17:05:54 +02004169 dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div;
4170 dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div;
Archit Taneja7d2572f2012-06-29 14:31:07 +05304171
4172 r = dispc_calc_clock_rates(fck, &dispc_cinfo);
4173 if (r) {
4174 DSSERR("Failed to calc dispc clocks\n");
4175 return r;
4176 }
4177
4178 dsi->mgr_config.clock_info = dispc_cinfo;
4179
4180 return 0;
4181}
4182
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02004183static int dsi_display_init_dispc(struct platform_device *dsidev,
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004184 enum omap_channel channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004185{
Archit Taneja7d2572f2012-06-29 14:31:07 +05304186 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja7d2572f2012-06-29 14:31:07 +05304187 int r;
Archit Taneja5a8b5722011-05-12 17:26:29 +05304188
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004189 dss_select_lcd_clk_source(channel, dsi->module_id == 0 ?
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03004190 DSS_CLK_SRC_PLL1_1 :
4191 DSS_CLK_SRC_PLL2_1);
Tomi Valkeinen5476e742011-11-03 16:34:20 +02004192
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004193 if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004194 r = dss_mgr_register_framedone_handler(channel,
Tomi Valkeinen15502022012-10-10 13:59:07 +03004195 dsi_framedone_irq_callback, dsidev);
Archit Taneja8af6ff02011-09-05 16:48:27 +05304196 if (r) {
Tomi Valkeinen15502022012-10-10 13:59:07 +03004197 DSSERR("can't register FRAMEDONE handler\n");
Archit Taneja7d2572f2012-06-29 14:31:07 +05304198 goto err;
Archit Taneja8af6ff02011-09-05 16:48:27 +05304199 }
4200
Archit Taneja7d2572f2012-06-29 14:31:07 +05304201 dsi->mgr_config.stallmode = true;
4202 dsi->mgr_config.fifohandcheck = true;
Archit Taneja8af6ff02011-09-05 16:48:27 +05304203 } else {
Archit Taneja7d2572f2012-06-29 14:31:07 +05304204 dsi->mgr_config.stallmode = false;
4205 dsi->mgr_config.fifohandcheck = false;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004206 }
4207
Archit Tanejabd5a7b12012-06-26 12:38:31 +05304208 /*
4209 * override interlace, logic level and edge related parameters in
Peter Ujfalusi4520ff22016-09-22 14:07:03 +03004210 * videomode with default values
Archit Tanejabd5a7b12012-06-26 12:38:31 +05304211 */
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004212 dsi->vm.flags &= ~DISPLAY_FLAGS_INTERLACED;
4213 dsi->vm.flags &= ~DISPLAY_FLAGS_HSYNC_LOW;
4214 dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
4215 dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW;
4216 dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH;
4217 dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE;
4218 dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
4219 dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW;
4220 dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH;
4221 dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE;
4222 dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
Archit Tanejabd5a7b12012-06-26 12:38:31 +05304223
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004224 dss_mgr_set_timings(channel, &dsi->vm);
Archit Tanejabd5a7b12012-06-26 12:38:31 +05304225
Tomi Valkeinen57612172012-11-27 17:32:36 +02004226 r = dsi_configure_dispc_clocks(dsidev);
Archit Taneja7d2572f2012-06-29 14:31:07 +05304227 if (r)
4228 goto err1;
4229
4230 dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
4231 dsi->mgr_config.video_port_width =
Archit Taneja02c39602012-08-10 15:01:33 +05304232 dsi_get_pixel_size(dsi->pix_fmt);
Archit Taneja7d2572f2012-06-29 14:31:07 +05304233 dsi->mgr_config.lcden_sig_polarity = 0;
4234
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004235 dss_mgr_set_lcd_config(channel, &dsi->mgr_config);
Archit Tanejad21f43b2012-06-21 09:45:11 +05304236
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004237 return 0;
Archit Taneja7d2572f2012-06-29 14:31:07 +05304238err1:
Archit Tanejadca2b152012-08-16 18:02:00 +05304239 if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004240 dss_mgr_unregister_framedone_handler(channel,
Tomi Valkeinen15502022012-10-10 13:59:07 +03004241 dsi_framedone_irq_callback, dsidev);
Archit Taneja7d2572f2012-06-29 14:31:07 +05304242err:
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03004243 dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK);
Archit Taneja7d2572f2012-06-29 14:31:07 +05304244 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004245}
4246
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02004247static void dsi_display_uninit_dispc(struct platform_device *dsidev,
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004248 enum omap_channel channel)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004249{
Archit Tanejadca2b152012-08-16 18:02:00 +05304250 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4251
Tomi Valkeinen15502022012-10-10 13:59:07 +03004252 if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
Tomi Valkeinen0674d382015-11-05 10:01:02 +02004253 dss_mgr_unregister_framedone_handler(channel,
Tomi Valkeinen15502022012-10-10 13:59:07 +03004254 dsi_framedone_irq_callback, dsidev);
Tomi Valkeinenb7dec9b2013-02-22 12:58:35 +02004255
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03004256 dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004257}
4258
Tomi Valkeinen57612172012-11-27 17:32:36 +02004259static int dsi_configure_dsi_clocks(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004260{
Tomi Valkeinena0d269e2012-11-27 17:05:54 +02004261 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004262 struct dss_pll_clock_info cinfo;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004263 int r;
4264
Tomi Valkeinena0d269e2012-11-27 17:05:54 +02004265 cinfo = dsi->user_dsi_cinfo;
4266
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004267 r = dss_pll_set_config(&dsi->pll, &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
Tomi Valkeinen57612172012-11-27 17:32:36 +02004276static int dsi_display_init_dsi(struct platform_device *dsidev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004277{
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02004278 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004279 int r;
4280
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004281 r = dss_pll_enable(&dsi->pll);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004282 if (r)
4283 goto err0;
4284
Tomi Valkeinen57612172012-11-27 17:32:36 +02004285 r = dsi_configure_dsi_clocks(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004286 if (r)
4287 goto err1;
4288
Tomi Valkeinen4ce9e332013-03-05 17:11:16 +02004289 dss_select_dsi_clk_source(dsi->module_id, dsi->module_id == 0 ?
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03004290 DSS_CLK_SRC_PLL1_2 :
4291 DSS_CLK_SRC_PLL2_2);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004292
4293 DSSDBG("PLL OK\n");
4294
Archit Taneja9e7e9372012-08-14 12:29:22 +05304295 r = dsi_cio_init(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004296 if (r)
4297 goto err2;
4298
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304299 _dsi_print_reset_status(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004300
Archit Taneja9e7e9372012-08-14 12:29:22 +05304301 dsi_proto_timings(dsidev);
Tomi Valkeinen57612172012-11-27 17:32:36 +02004302 dsi_set_lp_clk_divisor(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004303
4304 if (1)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304305 _dsi_print_reset_status(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004306
Tomi Valkeinen57612172012-11-27 17:32:36 +02004307 r = dsi_proto_config(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004308 if (r)
4309 goto err3;
4310
4311 /* enable interface */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304312 dsi_vc_enable(dsidev, 0, 1);
4313 dsi_vc_enable(dsidev, 1, 1);
4314 dsi_vc_enable(dsidev, 2, 1);
4315 dsi_vc_enable(dsidev, 3, 1);
4316 dsi_if_enable(dsidev, 1);
4317 dsi_force_tx_stop_mode_io(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004318
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004319 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004320err3:
Archit Taneja9e7e9372012-08-14 12:29:22 +05304321 dsi_cio_uninit(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004322err2:
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03004323 dss_select_dsi_clk_source(dsi->module_id, DSS_CLK_SRC_FCK);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004324err1:
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004325 dss_pll_disable(&dsi->pll);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004326err0:
4327 return r;
4328}
4329
Tomi Valkeinen57612172012-11-27 17:32:36 +02004330static void dsi_display_uninit_dsi(struct platform_device *dsidev,
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03004331 bool disconnect_lanes, bool enter_ulps)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004332{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304333 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304334
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304335 if (enter_ulps && !dsi->ulps_enabled)
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304336 dsi_enter_ulps(dsidev);
Tomi Valkeinen40885ab2010-07-28 15:53:38 +03004337
Ville Syrjäläd7370102010-04-22 22:50:09 +02004338 /* disable interface */
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304339 dsi_if_enable(dsidev, 0);
4340 dsi_vc_enable(dsidev, 0, 0);
4341 dsi_vc_enable(dsidev, 1, 0);
4342 dsi_vc_enable(dsidev, 2, 0);
4343 dsi_vc_enable(dsidev, 3, 0);
Ville Syrjäläd7370102010-04-22 22:50:09 +02004344
Tomi Valkeinen3b63ca72016-05-17 14:01:10 +03004345 dss_select_dsi_clk_source(dsi->module_id, DSS_CLK_SRC_FCK);
Archit Taneja9e7e9372012-08-14 12:29:22 +05304346 dsi_cio_uninit(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304347 dsi_pll_uninit(dsidev, disconnect_lanes);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004348}
4349
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004350static int dsi_display_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004351{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304352 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304353 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004354 int r = 0;
4355
4356 DSSDBG("dsi_display_enable\n");
4357
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304358 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004359
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304360 mutex_lock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004361
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004362 r = dsi_runtime_get(dsidev);
4363 if (r)
4364 goto err_get_dsi;
4365
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004366 _dsi_initialize_irq(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004367
Tomi Valkeinen57612172012-11-27 17:32:36 +02004368 r = dsi_display_init_dsi(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004369 if (r)
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004370 goto err_init_dsi;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004371
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304372 mutex_unlock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004373
4374 return 0;
4375
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004376err_init_dsi:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004377 dsi_runtime_put(dsidev);
4378err_get_dsi:
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304379 mutex_unlock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004380 DSSDBG("dsi_display_enable FAILED\n");
4381 return r;
4382}
4383
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004384static void dsi_display_disable(struct omap_dss_device *dssdev,
Tomi Valkeinen22d6d672010-10-11 11:33:30 +03004385 bool disconnect_lanes, bool enter_ulps)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004386{
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304387 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304388 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304389
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004390 DSSDBG("dsi_display_disable\n");
4391
Archit Tanejaa72b64b2011-05-12 17:26:26 +05304392 WARN_ON(!dsi_bus_is_locked(dsidev));
Tomi Valkeinen37ac60e2010-01-12 15:12:07 +02004393
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304394 mutex_lock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004395
Tomi Valkeinen15ffa1d2011-06-16 14:34:06 +03004396 dsi_sync_vc(dsidev, 0);
4397 dsi_sync_vc(dsidev, 1);
4398 dsi_sync_vc(dsidev, 2);
4399 dsi_sync_vc(dsidev, 3);
4400
Tomi Valkeinen57612172012-11-27 17:32:36 +02004401 dsi_display_uninit_dsi(dsidev, disconnect_lanes, enter_ulps);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004402
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03004403 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004404
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304405 mutex_unlock(&dsi->lock);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004406}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004407
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004408static int dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004409{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304410 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4411 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
4412
4413 dsi->te_enabled = enable;
Tomi Valkeinen225b6502010-01-11 15:11:01 +02004414 return 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004415}
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004416
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004417#ifdef PRINT_VERBOSE_VM_TIMINGS
4418static void print_dsi_vm(const char *str,
4419 const struct omap_dss_dsi_videomode_timings *t)
4420{
4421 unsigned long byteclk = t->hsclk / 4;
4422 int bl, wc, pps, tot;
4423
4424 wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
4425 pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004426 bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004427 tot = bl + pps;
4428
4429#define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
4430
4431 pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, "
4432 "%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
4433 str,
4434 byteclk,
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004435 t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004436 bl, pps, tot,
4437 TO_DSI_T(t->hss),
4438 TO_DSI_T(t->hsa),
4439 TO_DSI_T(t->hse),
4440 TO_DSI_T(t->hbp),
4441 TO_DSI_T(pps),
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004442 TO_DSI_T(t->hfp),
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004443
4444 TO_DSI_T(bl),
4445 TO_DSI_T(pps),
4446
4447 TO_DSI_T(tot));
4448#undef TO_DSI_T
4449}
4450
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004451static void print_dispc_vm(const char *str, const struct videomode *vm)
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004452{
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004453 unsigned long pck = vm->pixelclock;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004454 int hact, bl, tot;
4455
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004456 hact = vm->hactive;
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004457 bl = vm->hsync_len + vm->hback_porch + vm->hfront_porch;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004458 tot = hact + bl;
4459
4460#define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
4461
4462 pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, "
4463 "%u/%u/%u/%u = %u + %u = %u\n",
4464 str,
4465 pck,
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004466 vm->hsync_len, vm->hback_porch, hact, vm->hfront_porch,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004467 bl, hact, tot,
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004468 TO_DISPC_T(vm->hsync_len),
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004469 TO_DISPC_T(vm->hback_porch),
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004470 TO_DISPC_T(hact),
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004471 TO_DISPC_T(vm->hfront_porch),
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004472 TO_DISPC_T(bl),
4473 TO_DISPC_T(hact),
4474 TO_DISPC_T(tot));
4475#undef TO_DISPC_T
4476}
4477
4478/* note: this is not quite accurate */
4479static void print_dsi_dispc_vm(const char *str,
4480 const struct omap_dss_dsi_videomode_timings *t)
4481{
Peter Ujfalusi4520ff22016-09-22 14:07:03 +03004482 struct videomode vm = { 0 };
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004483 unsigned long byteclk = t->hsclk / 4;
4484 unsigned long pck;
4485 u64 dsi_tput;
4486 int dsi_hact, dsi_htot;
4487
4488 dsi_tput = (u64)byteclk * t->ndl * 8;
4489 pck = (u32)div64_u64(dsi_tput, t->bitspp);
4490 dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004491 dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004492
Tomi Valkeinend8d789412013-04-10 14:12:14 +03004493 vm.pixelclock = pck;
Peter Ujfalusi4dc22502016-09-22 14:06:48 +03004494 vm.hsync_len = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
H. Nikolaus Schaller7e6d80d2016-12-26 20:23:19 +01004495 vm.hback_porch = div64_u64((u64)t->hbp * pck, byteclk);
4496 vm.hfront_porch = div64_u64((u64)t->hfp * pck, byteclk);
Peter Ujfalusi81899062016-09-22 14:06:46 +03004497 vm.hactive = t->hact;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004498
4499 print_dispc_vm(str, &vm);
4500}
4501#endif /* PRINT_VERBOSE_VM_TIMINGS */
4502
4503static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
4504 unsigned long pck, void *data)
4505{
4506 struct dsi_clk_calc_ctx *ctx = data;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004507 struct videomode *vm = &ctx->vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004508
4509 ctx->dispc_cinfo.lck_div = lckd;
4510 ctx->dispc_cinfo.pck_div = pckd;
4511 ctx->dispc_cinfo.lck = lck;
4512 ctx->dispc_cinfo.pck = pck;
4513
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004514 *vm = *ctx->config->vm;
4515 vm->pixelclock = pck;
4516 vm->hactive = ctx->config->vm->hactive;
4517 vm->vactive = ctx->config->vm->vactive;
4518 vm->hsync_len = vm->hfront_porch = vm->hback_porch = vm->vsync_len = 1;
4519 vm->vfront_porch = vm->vback_porch = 0;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004520
4521 return true;
4522}
4523
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004524static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004525 void *data)
4526{
4527 struct dsi_clk_calc_ctx *ctx = data;
4528
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004529 ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
Tomi Valkeinenacf604b2014-11-07 13:13:24 +02004530 ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004531
4532 return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max,
4533 dsi_cm_calc_dispc_cb, ctx);
4534}
4535
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004536static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint,
4537 unsigned long clkdco, void *data)
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004538{
4539 struct dsi_clk_calc_ctx *ctx = data;
4540
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004541 ctx->dsi_cinfo.n = n;
4542 ctx->dsi_cinfo.m = m;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004543 ctx->dsi_cinfo.fint = fint;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004544 ctx->dsi_cinfo.clkdco = clkdco;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004545
Tomi Valkeinencd0715f2016-05-17 21:23:37 +03004546 return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min,
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004547 dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004548 dsi_cm_calc_hsdiv_cb, ctx);
4549}
4550
4551static bool dsi_cm_calc(struct dsi_data *dsi,
4552 const struct omap_dss_dsi_config *cfg,
4553 struct dsi_clk_calc_ctx *ctx)
4554{
4555 unsigned long clkin;
4556 int bitspp, ndl;
4557 unsigned long pll_min, pll_max;
4558 unsigned long pck, txbyteclk;
4559
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004560 clkin = clk_get_rate(dsi->pll.clkin);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004561 bitspp = dsi_get_pixel_size(cfg->pixel_format);
4562 ndl = dsi->num_lanes_used - 1;
4563
4564 /*
4565 * Here we should calculate minimum txbyteclk to be able to send the
4566 * frame in time, and also to handle TE. That's not very simple, though,
4567 * especially as we go to LP between each pixel packet due to HW
4568 * "feature". So let's just estimate very roughly and multiply by 1.5.
4569 */
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004570 pck = cfg->vm->pixelclock;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004571 pck = pck * 3 / 2;
4572 txbyteclk = pck * bitspp / 8 / ndl;
4573
4574 memset(ctx, 0, sizeof(*ctx));
4575 ctx->dsidev = dsi->pdev;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004576 ctx->pll = &dsi->pll;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004577 ctx->config = cfg;
4578 ctx->req_pck_min = pck;
4579 ctx->req_pck_nom = pck;
4580 ctx->req_pck_max = pck * 3 / 2;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004581
4582 pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4);
4583 pll_max = cfg->hs_clk_max * 4;
4584
Tomi Valkeinencd0715f2016-05-17 21:23:37 +03004585 return dss_pll_calc_a(ctx->pll, clkin,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004586 pll_min, pll_max,
4587 dsi_cm_calc_pll_cb, ctx);
4588}
4589
4590static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
4591{
4592 struct dsi_data *dsi = dsi_get_dsidrv_data(ctx->dsidev);
4593 const struct omap_dss_dsi_config *cfg = ctx->config;
4594 int bitspp = dsi_get_pixel_size(cfg->pixel_format);
4595 int ndl = dsi->num_lanes_used - 1;
Tomi Valkeinen4a38aed2014-11-07 13:08:16 +02004596 unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004597 unsigned long byteclk = hsclk / 4;
4598
4599 unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max;
4600 int xres;
4601 int panel_htot, panel_hbl; /* pixels */
4602 int dispc_htot, dispc_hbl; /* pixels */
4603 int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */
4604 int hfp, hsa, hbp;
Peter Ujfalusi4520ff22016-09-22 14:07:03 +03004605 const struct videomode *req_vm;
4606 struct videomode *dispc_vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004607 struct omap_dss_dsi_videomode_timings *dsi_vm;
4608 u64 dsi_tput, dispc_tput;
4609
4610 dsi_tput = (u64)byteclk * ndl * 8;
4611
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004612 req_vm = cfg->vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004613 req_pck_min = ctx->req_pck_min;
4614 req_pck_max = ctx->req_pck_max;
4615 req_pck_nom = ctx->req_pck_nom;
4616
4617 dispc_pck = ctx->dispc_cinfo.pck;
4618 dispc_tput = (u64)dispc_pck * bitspp;
4619
Peter Ujfalusi81899062016-09-22 14:06:46 +03004620 xres = req_vm->hactive;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004621
Peter Ujfalusia85f4a82016-09-22 14:06:50 +03004622 panel_hbl = req_vm->hfront_porch + req_vm->hback_porch +
4623 req_vm->hsync_len;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004624 panel_htot = xres + panel_hbl;
4625
4626 dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl);
4627
4628 /*
4629 * When there are no line buffers, DISPC and DSI must have the
4630 * same tput. Otherwise DISPC tput needs to be higher than DSI's.
4631 */
4632 if (dsi->line_buffer_size < xres * bitspp / 8) {
4633 if (dispc_tput != dsi_tput)
4634 return false;
4635 } else {
4636 if (dispc_tput < dsi_tput)
4637 return false;
4638 }
4639
4640 /* DSI tput must be over the min requirement */
4641 if (dsi_tput < (u64)bitspp * req_pck_min)
4642 return false;
4643
4644 /* When non-burst mode, DSI tput must be below max requirement. */
4645 if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) {
4646 if (dsi_tput > (u64)bitspp * req_pck_max)
4647 return false;
4648 }
4649
4650 hss = DIV_ROUND_UP(4, ndl);
4651
4652 if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
Peter Ujfalusi4dc22502016-09-22 14:06:48 +03004653 if (ndl == 3 && req_vm->hsync_len == 0)
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004654 hse = 1;
4655 else
4656 hse = DIV_ROUND_UP(4, ndl);
4657 } else {
4658 hse = 0;
4659 }
4660
4661 /* DSI htot to match the panel's nominal pck */
4662 dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom);
4663
4664 /* fail if there would be no time for blanking */
4665 if (dsi_htot < hss + hse + dsi_hact)
4666 return false;
4667
4668 /* total DSI blanking needed to achieve panel's TL */
4669 dsi_hbl = dsi_htot - dsi_hact;
4670
4671 /* DISPC htot to match the DSI TL */
4672 dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk);
4673
4674 /* verify that the DSI and DISPC TLs are the same */
4675 if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk)
4676 return false;
4677
4678 dispc_hbl = dispc_htot - xres;
4679
4680 /* setup DSI videomode */
4681
4682 dsi_vm = &ctx->dsi_vm;
4683 memset(dsi_vm, 0, sizeof(*dsi_vm));
4684
4685 dsi_vm->hsclk = hsclk;
4686
4687 dsi_vm->ndl = ndl;
4688 dsi_vm->bitspp = bitspp;
4689
4690 if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) {
4691 hsa = 0;
Peter Ujfalusi4dc22502016-09-22 14:06:48 +03004692 } else if (ndl == 3 && req_vm->hsync_len == 0) {
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004693 hsa = 0;
4694 } else {
Peter Ujfalusi4dc22502016-09-22 14:06:48 +03004695 hsa = div64_u64((u64)req_vm->hsync_len * byteclk, req_pck_nom);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004696 hsa = max(hsa - hse, 1);
4697 }
4698
Peter Ujfalusia85f4a82016-09-22 14:06:50 +03004699 hbp = div64_u64((u64)req_vm->hback_porch * byteclk, req_pck_nom);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004700 hbp = max(hbp, 1);
4701
4702 hfp = dsi_hbl - (hss + hsa + hse + hbp);
4703 if (hfp < 1) {
4704 int t;
4705 /* we need to take cycles from hbp */
4706
4707 t = 1 - hfp;
4708 hbp = max(hbp - t, 1);
4709 hfp = dsi_hbl - (hss + hsa + hse + hbp);
4710
4711 if (hfp < 1 && hsa > 0) {
4712 /* we need to take cycles from hsa */
4713 t = 1 - hfp;
4714 hsa = max(hsa - t, 1);
4715 hfp = dsi_hbl - (hss + hsa + hse + hbp);
4716 }
4717 }
4718
4719 if (hfp < 1)
4720 return false;
4721
4722 dsi_vm->hss = hss;
4723 dsi_vm->hsa = hsa;
4724 dsi_vm->hse = hse;
4725 dsi_vm->hbp = hbp;
4726 dsi_vm->hact = xres;
4727 dsi_vm->hfp = hfp;
4728
Peter Ujfalusid5bcf0a2016-09-22 14:06:51 +03004729 dsi_vm->vsa = req_vm->vsync_len;
Peter Ujfalusi458540c2016-09-22 14:06:53 +03004730 dsi_vm->vbp = req_vm->vback_porch;
Peter Ujfalusifb7f3c42016-09-22 14:06:47 +03004731 dsi_vm->vact = req_vm->vactive;
Peter Ujfalusi0996c682016-09-22 14:06:52 +03004732 dsi_vm->vfp = req_vm->vfront_porch;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004733
4734 dsi_vm->trans_mode = cfg->trans_mode;
4735
4736 dsi_vm->blanking_mode = 0;
4737 dsi_vm->hsa_blanking_mode = 1;
4738 dsi_vm->hfp_blanking_mode = 1;
4739 dsi_vm->hbp_blanking_mode = 1;
4740
4741 dsi_vm->ddr_clk_always_on = cfg->ddr_clk_always_on;
4742 dsi_vm->window_sync = 4;
4743
4744 /* setup DISPC videomode */
4745
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004746 dispc_vm = &ctx->vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004747 *dispc_vm = *req_vm;
Tomi Valkeinend8d789412013-04-10 14:12:14 +03004748 dispc_vm->pixelclock = dispc_pck;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004749
4750 if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
Peter Ujfalusi4dc22502016-09-22 14:06:48 +03004751 hsa = div64_u64((u64)req_vm->hsync_len * dispc_pck,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004752 req_pck_nom);
4753 hsa = max(hsa, 1);
4754 } else {
4755 hsa = 1;
4756 }
4757
Peter Ujfalusia85f4a82016-09-22 14:06:50 +03004758 hbp = div64_u64((u64)req_vm->hback_porch * dispc_pck, req_pck_nom);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004759 hbp = max(hbp, 1);
4760
4761 hfp = dispc_hbl - hsa - hbp;
4762 if (hfp < 1) {
4763 int t;
4764 /* we need to take cycles from hbp */
4765
4766 t = 1 - hfp;
4767 hbp = max(hbp - t, 1);
4768 hfp = dispc_hbl - hsa - hbp;
4769
4770 if (hfp < 1) {
4771 /* we need to take cycles from hsa */
4772 t = 1 - hfp;
4773 hsa = max(hsa - t, 1);
4774 hfp = dispc_hbl - hsa - hbp;
4775 }
4776 }
4777
4778 if (hfp < 1)
4779 return false;
4780
Peter Ujfalusi0a30e152016-09-22 14:06:49 +03004781 dispc_vm->hfront_porch = hfp;
Peter Ujfalusi4dc22502016-09-22 14:06:48 +03004782 dispc_vm->hsync_len = hsa;
Peter Ujfalusia85f4a82016-09-22 14:06:50 +03004783 dispc_vm->hback_porch = hbp;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004784
4785 return true;
4786}
4787
4788
4789static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
4790 unsigned long pck, void *data)
4791{
4792 struct dsi_clk_calc_ctx *ctx = data;
4793
4794 ctx->dispc_cinfo.lck_div = lckd;
4795 ctx->dispc_cinfo.pck_div = pckd;
4796 ctx->dispc_cinfo.lck = lck;
4797 ctx->dispc_cinfo.pck = pck;
4798
4799 if (dsi_vm_calc_blanking(ctx) == false)
4800 return false;
4801
4802#ifdef PRINT_VERBOSE_VM_TIMINGS
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004803 print_dispc_vm("dispc", &ctx->vm);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004804 print_dsi_vm("dsi ", &ctx->dsi_vm);
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004805 print_dispc_vm("req ", ctx->config->vm);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004806 print_dsi_dispc_vm("act ", &ctx->dsi_vm);
4807#endif
4808
4809 return true;
4810}
4811
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004812static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004813 void *data)
4814{
4815 struct dsi_clk_calc_ctx *ctx = data;
4816 unsigned long pck_max;
4817
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004818 ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
Tomi Valkeinenacf604b2014-11-07 13:13:24 +02004819 ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004820
4821 /*
4822 * In burst mode we can let the dispc pck be arbitrarily high, but it
4823 * limits our scaling abilities. So for now, don't aim too high.
4824 */
4825
4826 if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE)
4827 pck_max = ctx->req_pck_max + 10000000;
4828 else
4829 pck_max = ctx->req_pck_max;
4830
4831 return dispc_div_calc(dispc, ctx->req_pck_min, pck_max,
4832 dsi_vm_calc_dispc_cb, ctx);
4833}
4834
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004835static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint,
4836 unsigned long clkdco, void *data)
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004837{
4838 struct dsi_clk_calc_ctx *ctx = data;
4839
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004840 ctx->dsi_cinfo.n = n;
4841 ctx->dsi_cinfo.m = m;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004842 ctx->dsi_cinfo.fint = fint;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004843 ctx->dsi_cinfo.clkdco = clkdco;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004844
Tomi Valkeinencd0715f2016-05-17 21:23:37 +03004845 return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min,
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004846 dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004847 dsi_vm_calc_hsdiv_cb, ctx);
4848}
4849
4850static bool dsi_vm_calc(struct dsi_data *dsi,
4851 const struct omap_dss_dsi_config *cfg,
4852 struct dsi_clk_calc_ctx *ctx)
4853{
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004854 const struct videomode *vm = cfg->vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004855 unsigned long clkin;
4856 unsigned long pll_min;
4857 unsigned long pll_max;
4858 int ndl = dsi->num_lanes_used - 1;
4859 int bitspp = dsi_get_pixel_size(cfg->pixel_format);
4860 unsigned long byteclk_min;
4861
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004862 clkin = clk_get_rate(dsi->pll.clkin);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004863
4864 memset(ctx, 0, sizeof(*ctx));
4865 ctx->dsidev = dsi->pdev;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03004866 ctx->pll = &dsi->pll;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004867 ctx->config = cfg;
4868
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004869 /* these limits should come from the panel driver */
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004870 ctx->req_pck_min = vm->pixelclock - 1000;
4871 ctx->req_pck_nom = vm->pixelclock;
4872 ctx->req_pck_max = vm->pixelclock + 1000;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004873
4874 byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
4875 pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
4876
4877 if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) {
4878 pll_max = cfg->hs_clk_max * 4;
4879 } else {
4880 unsigned long byteclk_max;
4881 byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp,
4882 ndl * 8);
4883
4884 pll_max = byteclk_max * 4 * 4;
4885 }
4886
Tomi Valkeinencd0715f2016-05-17 21:23:37 +03004887 return dss_pll_calc_a(ctx->pll, clkin,
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004888 pll_min, pll_max,
4889 dsi_vm_calc_pll_cb, ctx);
4890}
4891
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004892static int dsi_set_config(struct omap_dss_device *dssdev,
Tomi Valkeinen777f05c2013-03-06 11:10:29 +02004893 const struct omap_dss_dsi_config *config)
Archit Tanejae67458a2012-08-13 14:17:30 +05304894{
4895 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4896 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004897 struct dsi_clk_calc_ctx ctx;
4898 bool ok;
4899 int r;
Archit Tanejae67458a2012-08-13 14:17:30 +05304900
4901 mutex_lock(&dsi->lock);
4902
Tomi Valkeinen777f05c2013-03-06 11:10:29 +02004903 dsi->pix_fmt = config->pixel_format;
4904 dsi->mode = config->mode;
4905
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004906 if (config->mode == OMAP_DSS_DSI_VIDEO_MODE)
4907 ok = dsi_vm_calc(dsi, config, &ctx);
4908 else
4909 ok = dsi_cm_calc(dsi, config, &ctx);
4910
4911 if (!ok) {
4912 DSSERR("failed to find suitable DSI clock settings\n");
4913 r = -EINVAL;
4914 goto err;
4915 }
4916
4917 dsi_pll_calc_dsi_fck(&ctx.dsi_cinfo);
4918
Tomi Valkeinenacf604b2014-11-07 13:13:24 +02004919 r = dsi_lp_clock_calc(ctx.dsi_cinfo.clkout[HSDIV_DSI],
Tomi Valkeinen7b71c412014-08-06 15:45:26 +03004920 config->lp_clk_min, config->lp_clk_max, &dsi->user_lp_cinfo);
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004921 if (r) {
4922 DSSERR("failed to find suitable DSI LP clock settings\n");
4923 goto err;
4924 }
4925
4926 dsi->user_dsi_cinfo = ctx.dsi_cinfo;
4927 dsi->user_dispc_cinfo = ctx.dispc_cinfo;
4928
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +03004929 dsi->vm = ctx.vm;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004930 dsi->vm_timings = ctx.dsi_vm;
Archit Tanejae67458a2012-08-13 14:17:30 +05304931
4932 mutex_unlock(&dsi->lock);
Archit Tanejae67458a2012-08-13 14:17:30 +05304933
Tomi Valkeinen777f05c2013-03-06 11:10:29 +02004934 return 0;
Tomi Valkeinenf1e00012013-03-05 17:21:35 +02004935err:
4936 mutex_unlock(&dsi->lock);
4937
4938 return r;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02004939}
Archit Taneja0b3ffe32012-08-13 22:13:39 +05304940
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +02004941/*
4942 * Return a hardcoded channel for the DSI output. This should work for
4943 * current use cases, but this can be later expanded to either resolve
4944 * the channel in some more dynamic manner, or get the channel as a user
4945 * parameter.
4946 */
Laurent Pinchart742e6932017-08-05 01:43:57 +03004947static enum omap_channel dsi_get_channel(struct dsi_data *dsi)
Archit Tanejae3525742012-08-09 15:23:43 +05304948{
Laurent Pinchart742e6932017-08-05 01:43:57 +03004949 switch (dsi->data->model) {
4950 case DSI_MODEL_OMAP3:
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +02004951 return OMAP_DSS_CHANNEL_LCD;
Archit Tanejae3525742012-08-09 15:23:43 +05304952
Laurent Pinchart742e6932017-08-05 01:43:57 +03004953 case DSI_MODEL_OMAP4:
4954 switch (dsi->module_id) {
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +02004955 case 0:
4956 return OMAP_DSS_CHANNEL_LCD;
4957 case 1:
4958 return OMAP_DSS_CHANNEL_LCD2;
4959 default:
4960 DSSWARN("unsupported module id\n");
4961 return OMAP_DSS_CHANNEL_LCD;
4962 }
Archit Tanejae3525742012-08-09 15:23:43 +05304963
Laurent Pinchart742e6932017-08-05 01:43:57 +03004964 case DSI_MODEL_OMAP5:
4965 switch (dsi->module_id) {
Tomi Valkeinen2eea5ae2013-02-13 11:23:54 +02004966 case 0:
4967 return OMAP_DSS_CHANNEL_LCD;
4968 case 1:
4969 return OMAP_DSS_CHANNEL_LCD3;
4970 default:
4971 DSSWARN("unsupported module id\n");
4972 return OMAP_DSS_CHANNEL_LCD;
4973 }
4974
4975 default:
4976 DSSWARN("unsupported DSS version\n");
4977 return OMAP_DSS_CHANNEL_LCD;
4978 }
Archit Taneja02c39602012-08-10 15:01:33 +05304979}
Tomi Valkeinen5f42f2c2011-02-22 15:53:46 +02004980
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004981static int dsi_request_vc(struct omap_dss_device *dssdev, int *channel)
Archit Taneja5ee3c142011-03-02 12:35:53 +05304982{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304983 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
4984 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Archit Taneja5ee3c142011-03-02 12:35:53 +05304985 int i;
4986
Archit Tanejaf1da39d2011-05-12 17:26:27 +05304987 for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
4988 if (!dsi->vc[i].dssdev) {
4989 dsi->vc[i].dssdev = dssdev;
Archit Taneja5ee3c142011-03-02 12:35:53 +05304990 *channel = i;
4991 return 0;
4992 }
4993 }
4994
4995 DSSERR("cannot get VC for display %s", dssdev->name);
4996 return -ENOSPC;
4997}
Archit Taneja5ee3c142011-03-02 12:35:53 +05304998
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03004999static int dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id)
Archit Taneja5ee3c142011-03-02 12:35:53 +05305000{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305001 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
5002 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5003
Archit Taneja5ee3c142011-03-02 12:35:53 +05305004 if (vc_id < 0 || vc_id > 3) {
5005 DSSERR("VC ID out of range\n");
5006 return -EINVAL;
5007 }
5008
5009 if (channel < 0 || channel > 3) {
5010 DSSERR("Virtual Channel out of range\n");
5011 return -EINVAL;
5012 }
5013
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305014 if (dsi->vc[channel].dssdev != dssdev) {
Archit Taneja5ee3c142011-03-02 12:35:53 +05305015 DSSERR("Virtual Channel not allocated to display %s\n",
5016 dssdev->name);
5017 return -EINVAL;
5018 }
5019
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305020 dsi->vc[channel].vc_id = vc_id;
Archit Taneja5ee3c142011-03-02 12:35:53 +05305021
5022 return 0;
5023}
Archit Taneja5ee3c142011-03-02 12:35:53 +05305024
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005025static void dsi_release_vc(struct omap_dss_device *dssdev, int channel)
Archit Taneja5ee3c142011-03-02 12:35:53 +05305026{
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305027 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
5028 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5029
Archit Taneja5ee3c142011-03-02 12:35:53 +05305030 if ((channel >= 0 && channel <= 3) &&
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305031 dsi->vc[channel].dssdev == dssdev) {
5032 dsi->vc[channel].dssdev = NULL;
5033 dsi->vc[channel].vc_id = 0;
Archit Taneja5ee3c142011-03-02 12:35:53 +05305034 }
5035}
Archit Taneja5ee3c142011-03-02 12:35:53 +05305036
Tomi Valkeinene406f902010-06-09 15:28:12 +03005037
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005038static int dsi_get_clocks(struct platform_device *dsidev)
5039{
5040 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5041 struct clk *clk;
5042
Sachin Kamat5303b3a2013-04-02 14:33:00 +03005043 clk = devm_clk_get(&dsidev->dev, "fck");
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005044 if (IS_ERR(clk)) {
5045 DSSERR("can't get fck\n");
5046 return PTR_ERR(clk);
5047 }
5048
5049 dsi->dss_clk = clk;
5050
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005051 return 0;
5052}
5053
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005054static int dsi_connect(struct omap_dss_device *dssdev,
5055 struct omap_dss_device *dst)
5056{
5057 struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
Tomi Valkeinen0674d382015-11-05 10:01:02 +02005058 enum omap_channel dispc_channel = dssdev->dispc_channel;
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005059 int r;
5060
5061 r = dsi_regulator_init(dsidev);
5062 if (r)
5063 return r;
5064
Tomi Valkeinen0674d382015-11-05 10:01:02 +02005065 r = dss_mgr_connect(dispc_channel, dssdev);
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005066 if (r)
5067 return r;
5068
5069 r = omapdss_output_set_device(dssdev, dst);
5070 if (r) {
5071 DSSERR("failed to connect output to new device: %s\n",
5072 dssdev->name);
Tomi Valkeinen0674d382015-11-05 10:01:02 +02005073 dss_mgr_disconnect(dispc_channel, dssdev);
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005074 return r;
5075 }
5076
5077 return 0;
5078}
5079
5080static void dsi_disconnect(struct omap_dss_device *dssdev,
5081 struct omap_dss_device *dst)
5082{
Tomi Valkeinen0674d382015-11-05 10:01:02 +02005083 enum omap_channel dispc_channel = dssdev->dispc_channel;
5084
Tomi Valkeinen9560dc102013-07-24 13:06:54 +03005085 WARN_ON(dst != dssdev->dst);
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005086
Tomi Valkeinen9560dc102013-07-24 13:06:54 +03005087 if (dst != dssdev->dst)
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005088 return;
5089
5090 omapdss_output_unset_device(dssdev);
5091
Tomi Valkeinen0674d382015-11-05 10:01:02 +02005092 dss_mgr_disconnect(dispc_channel, dssdev);
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005093}
5094
5095static const struct omapdss_dsi_ops dsi_ops = {
5096 .connect = dsi_connect,
5097 .disconnect = dsi_disconnect,
5098
5099 .bus_lock = dsi_bus_lock,
5100 .bus_unlock = dsi_bus_unlock,
5101
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005102 .enable = dsi_display_enable,
5103 .disable = dsi_display_disable,
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005104
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005105 .enable_hs = dsi_vc_enable_hs,
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005106
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005107 .configure_pins = dsi_configure_pins,
5108 .set_config = dsi_set_config,
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005109
5110 .enable_video_output = dsi_enable_video_output,
5111 .disable_video_output = dsi_disable_video_output,
5112
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005113 .update = dsi_update,
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005114
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005115 .enable_te = dsi_enable_te,
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005116
Tomi Valkeinen5cfc1c32013-05-15 11:24:30 +03005117 .request_vc = dsi_request_vc,
5118 .set_vc_id = dsi_set_vc_id,
5119 .release_vc = dsi_release_vc,
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005120
5121 .dcs_write = dsi_vc_dcs_write,
5122 .dcs_write_nosync = dsi_vc_dcs_write_nosync,
5123 .dcs_read = dsi_vc_dcs_read,
5124
5125 .gen_write = dsi_vc_generic_write,
5126 .gen_write_nosync = dsi_vc_generic_write_nosync,
5127 .gen_read = dsi_vc_generic_read,
5128
5129 .bta_sync = dsi_vc_send_bta_sync,
5130
5131 .set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size,
5132};
5133
Tomi Valkeinenee4a24e2013-04-26 13:47:06 +03005134static void dsi_init_output(struct platform_device *dsidev)
Archit Taneja81b87f52012-09-26 16:30:49 +05305135{
5136 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03005137 struct omap_dss_device *out = &dsi->output;
Archit Taneja81b87f52012-09-26 16:30:49 +05305138
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03005139 out->dev = &dsidev->dev;
Archit Taneja81b87f52012-09-26 16:30:49 +05305140 out->id = dsi->module_id == 0 ?
5141 OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
5142
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03005143 out->output_type = OMAP_DISPLAY_TYPE_DSI;
Tomi Valkeinen7286a082013-02-18 13:06:01 +02005144 out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
Laurent Pinchart742e6932017-08-05 01:43:57 +03005145 out->dispc_channel = dsi_get_channel(dsi);
Tomi Valkeinendeb16df2013-05-24 13:20:27 +03005146 out->ops.dsi = &dsi_ops;
Tomi Valkeinenb7328e12013-05-03 11:42:18 +03005147 out->owner = THIS_MODULE;
Archit Taneja81b87f52012-09-26 16:30:49 +05305148
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +03005149 omapdss_register_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +05305150}
5151
Tomi Valkeinend1890a682013-04-26 13:47:41 +03005152static void dsi_uninit_output(struct platform_device *dsidev)
Archit Taneja81b87f52012-09-26 16:30:49 +05305153{
5154 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
Tomi Valkeinen1f68d9c2013-04-19 15:09:34 +03005155 struct omap_dss_device *out = &dsi->output;
Archit Taneja81b87f52012-09-26 16:30:49 +05305156
Tomi Valkeinen5d47dbc2013-04-24 13:32:51 +03005157 omapdss_unregister_output(out);
Archit Taneja81b87f52012-09-26 16:30:49 +05305158}
5159
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005160static int dsi_probe_of(struct platform_device *pdev)
5161{
5162 struct device_node *node = pdev->dev.of_node;
5163 struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5164 struct property *prop;
5165 u32 lane_arr[10];
5166 int len, num_pins;
5167 int r, i;
5168 struct device_node *ep;
5169 struct omap_dsi_pin_config pin_cfg;
5170
Rob Herring09bffa62017-03-22 08:26:08 -05005171 ep = of_graph_get_endpoint_by_regs(node, 0, 0);
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005172 if (!ep)
5173 return 0;
5174
5175 prop = of_find_property(ep, "lanes", &len);
5176 if (prop == NULL) {
5177 dev_err(&pdev->dev, "failed to find lane data\n");
5178 r = -EINVAL;
5179 goto err;
5180 }
5181
5182 num_pins = len / sizeof(u32);
5183
5184 if (num_pins < 4 || num_pins % 2 != 0 ||
5185 num_pins > dsi->num_lanes_supported * 2) {
5186 dev_err(&pdev->dev, "bad number of lanes\n");
5187 r = -EINVAL;
5188 goto err;
5189 }
5190
5191 r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins);
5192 if (r) {
5193 dev_err(&pdev->dev, "failed to read lane data\n");
5194 goto err;
5195 }
5196
5197 pin_cfg.num_pins = num_pins;
5198 for (i = 0; i < num_pins; ++i)
5199 pin_cfg.pins[i] = (int)lane_arr[i];
5200
5201 r = dsi_configure_pins(&dsi->output, &pin_cfg);
5202 if (r) {
5203 dev_err(&pdev->dev, "failed to configure pins");
5204 goto err;
5205 }
5206
5207 of_node_put(ep);
5208
5209 return 0;
5210
5211err:
5212 of_node_put(ep);
5213 return r;
5214}
5215
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005216static const struct dss_pll_ops dsi_pll_ops = {
5217 .enable = dsi_pll_enable,
5218 .disable = dsi_pll_disable,
5219 .set_config = dss_pll_write_config_type_a,
5220};
5221
5222static const struct dss_pll_hw dss_omap3_dsi_pll_hw = {
Tomi Valkeinen06ede3d2016-05-18 10:48:44 +03005223 .type = DSS_PLL_TYPE_A,
5224
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005225 .n_max = (1 << 7) - 1,
5226 .m_max = (1 << 11) - 1,
5227 .mX_max = (1 << 4) - 1,
5228 .fint_min = 750000,
5229 .fint_max = 2100000,
5230 .clkdco_low = 1000000000,
5231 .clkdco_max = 1800000000,
5232
5233 .n_msb = 7,
5234 .n_lsb = 1,
5235 .m_msb = 18,
5236 .m_lsb = 8,
5237
5238 .mX_msb[0] = 22,
5239 .mX_lsb[0] = 19,
5240 .mX_msb[1] = 26,
5241 .mX_lsb[1] = 23,
5242
5243 .has_stopmode = true,
5244 .has_freqsel = true,
5245 .has_selfreqdco = false,
5246 .has_refsel = false,
5247};
5248
5249static const struct dss_pll_hw dss_omap4_dsi_pll_hw = {
Tomi Valkeinen06ede3d2016-05-18 10:48:44 +03005250 .type = DSS_PLL_TYPE_A,
5251
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005252 .n_max = (1 << 8) - 1,
5253 .m_max = (1 << 12) - 1,
5254 .mX_max = (1 << 5) - 1,
5255 .fint_min = 500000,
5256 .fint_max = 2500000,
5257 .clkdco_low = 1000000000,
5258 .clkdco_max = 1800000000,
5259
5260 .n_msb = 8,
5261 .n_lsb = 1,
5262 .m_msb = 20,
5263 .m_lsb = 9,
5264
5265 .mX_msb[0] = 25,
5266 .mX_lsb[0] = 21,
5267 .mX_msb[1] = 30,
5268 .mX_lsb[1] = 26,
5269
5270 .has_stopmode = true,
5271 .has_freqsel = false,
5272 .has_selfreqdco = false,
5273 .has_refsel = false,
5274};
5275
5276static const struct dss_pll_hw dss_omap5_dsi_pll_hw = {
Tomi Valkeinen06ede3d2016-05-18 10:48:44 +03005277 .type = DSS_PLL_TYPE_A,
5278
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005279 .n_max = (1 << 8) - 1,
5280 .m_max = (1 << 12) - 1,
5281 .mX_max = (1 << 5) - 1,
5282 .fint_min = 150000,
5283 .fint_max = 52000000,
5284 .clkdco_low = 1000000000,
5285 .clkdco_max = 1800000000,
5286
5287 .n_msb = 8,
5288 .n_lsb = 1,
5289 .m_msb = 20,
5290 .m_lsb = 9,
5291
5292 .mX_msb[0] = 25,
5293 .mX_lsb[0] = 21,
5294 .mX_msb[1] = 30,
5295 .mX_lsb[1] = 26,
5296
5297 .has_stopmode = true,
5298 .has_freqsel = false,
5299 .has_selfreqdco = true,
5300 .has_refsel = true,
5301};
5302
5303static int dsi_init_pll_data(struct platform_device *dsidev)
5304{
5305 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5306 struct dss_pll *pll = &dsi->pll;
5307 struct clk *clk;
5308 int r;
5309
5310 clk = devm_clk_get(&dsidev->dev, "sys_clk");
5311 if (IS_ERR(clk)) {
5312 DSSERR("can't get sys_clk\n");
5313 return PTR_ERR(clk);
5314 }
5315
5316 pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1";
Tomi Valkeinen64e22ff2015-01-02 10:05:33 +02005317 pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005318 pll->clkin = clk;
5319 pll->base = dsi->pll_base;
Laurent Pinchart742e6932017-08-05 01:43:57 +03005320 pll->hw = dsi->data->pll_hw;
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005321 pll->ops = &dsi_pll_ops;
5322
5323 r = dss_pll_register(pll);
5324 if (r)
5325 return r;
5326
5327 return 0;
5328}
5329
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03005330/* DSI1 HW IP initialisation */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03005331static const struct dsi_of_data dsi_of_data_omap34xx = {
5332 .model = DSI_MODEL_OMAP3,
5333 .pll_hw = &dss_omap3_dsi_pll_hw,
5334 .modules = (const struct dsi_module_id_data[]) {
5335 { .address = 0x4804fc00, .id = 0, },
5336 { },
5337 },
5338 .quirks = DSI_QUIRK_REVERSE_TXCLKESC,
5339};
5340
5341static const struct dsi_of_data dsi_of_data_omap36xx = {
5342 .model = DSI_MODEL_OMAP3,
5343 .pll_hw = &dss_omap3_dsi_pll_hw,
5344 .modules = (const struct dsi_module_id_data[]) {
5345 { .address = 0x4804fc00, .id = 0, },
5346 { },
5347 },
5348 .quirks = DSI_QUIRK_PLL_PWR_BUG,
5349};
5350
5351static const struct dsi_of_data dsi_of_data_omap4 = {
5352 .model = DSI_MODEL_OMAP4,
5353 .pll_hw = &dss_omap4_dsi_pll_hw,
5354 .modules = (const struct dsi_module_id_data[]) {
5355 { .address = 0x58004000, .id = 0, },
5356 { .address = 0x58005000, .id = 1, },
5357 { },
5358 },
5359 .quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH
5360 | DSI_QUIRK_GNQ,
5361};
5362
5363static const struct dsi_of_data dsi_of_data_omap5 = {
5364 .model = DSI_MODEL_OMAP5,
5365 .pll_hw = &dss_omap5_dsi_pll_hw,
5366 .modules = (const struct dsi_module_id_data[]) {
5367 { .address = 0x58004000, .id = 0, },
5368 { .address = 0x58009000, .id = 1, },
5369 { },
5370 },
5371 .quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH
5372 | DSI_QUIRK_GNQ | DSI_QUIRK_PHY_DCC,
5373};
5374
5375static const struct of_device_id dsi_of_match[] = {
5376 { .compatible = "ti,omap3-dsi", .data = &dsi_of_data_omap36xx, },
5377 { .compatible = "ti,omap4-dsi", .data = &dsi_of_data_omap4, },
5378 { .compatible = "ti,omap5-dsi", .data = &dsi_of_data_omap5, },
5379 {},
5380};
5381
5382static const struct soc_device_attribute dsi_soc_devices[] = {
5383 { .machine = "OMAP3[45]*", .data = &dsi_of_data_omap34xx },
5384 { .machine = "AM35*", .data = &dsi_of_data_omap34xx },
5385 { /* sentinel */ }
5386};
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005387static int dsi_bind(struct device *dev, struct device *master, void *data)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005388{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005389 struct platform_device *dsidev = to_platform_device(dev);
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03005390 const struct soc_device_attribute *soc;
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005391 const struct dsi_module_id_data *d;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005392 u32 rev;
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02005393 int r, i;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305394 struct dsi_data *dsi;
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005395 struct resource *dsi_mem;
Tomi Valkeinen68104462013-12-17 13:53:28 +02005396 struct resource *res;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005397
Julia Lawall6e2a14d2012-01-24 14:00:45 +01005398 dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL);
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005399 if (!dsi)
5400 return -ENOMEM;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305401
5402 dsi->pdev = dsidev;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305403 dev_set_drvdata(&dsidev->dev, dsi);
Archit Tanejaa72b64b2011-05-12 17:26:26 +05305404
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305405 spin_lock_init(&dsi->irq_lock);
5406 spin_lock_init(&dsi->errors_lock);
5407 dsi->errors = 0;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005408
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02005409#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305410 spin_lock_init(&dsi->irq_stats_lock);
5411 dsi->irq_stats.last_reset = jiffies;
Tomi Valkeinendfc0fd82009-12-17 14:35:21 +02005412#endif
5413
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305414 mutex_init(&dsi->lock);
5415 sema_init(&dsi->bus_lock, 1);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005416
Tejun Heo203b42f2012-08-21 13:18:23 -07005417 INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work,
5418 dsi_framedone_timeout_work_callback);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305419
5420#ifdef DSI_CATCH_MISSING_TE
5421 init_timer(&dsi->te_timer);
5422 dsi->te_timer.function = dsi_te_timeout;
5423 dsi->te_timer.data = 0;
5424#endif
Tomi Valkeinen68104462013-12-17 13:53:28 +02005425
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005426 dsi_mem = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto");
5427 dsi->proto_base = devm_ioremap_resource(&dsidev->dev, dsi_mem);
Laurent Pinchartb22622f2017-05-07 00:29:09 +03005428 if (IS_ERR(dsi->proto_base))
5429 return PTR_ERR(dsi->proto_base);
Tomi Valkeinen68104462013-12-17 13:53:28 +02005430
5431 res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "phy");
Laurent Pinchartb22622f2017-05-07 00:29:09 +03005432 dsi->phy_base = devm_ioremap_resource(&dsidev->dev, res);
5433 if (IS_ERR(dsi->phy_base))
5434 return PTR_ERR(dsi->phy_base);
Tomi Valkeinen68104462013-12-17 13:53:28 +02005435
5436 res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "pll");
Laurent Pinchartb22622f2017-05-07 00:29:09 +03005437 dsi->pll_base = devm_ioremap_resource(&dsidev->dev, res);
5438 if (IS_ERR(dsi->pll_base))
5439 return PTR_ERR(dsi->pll_base);
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005440
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305441 dsi->irq = platform_get_irq(dsi->pdev, 0);
5442 if (dsi->irq < 0) {
5443 DSSERR("platform_get_irq failed\n");
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005444 return -ENODEV;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305445 }
archit tanejaaffe3602011-02-23 08:41:03 +00005446
Julia Lawall6e2a14d2012-01-24 14:00:45 +01005447 r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
5448 IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
archit tanejaaffe3602011-02-23 08:41:03 +00005449 if (r < 0) {
5450 DSSERR("request_irq failed\n");
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005451 return r;
archit tanejaaffe3602011-02-23 08:41:03 +00005452 }
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005453
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03005454 soc = soc_device_match(dsi_soc_devices);
5455 if (soc)
5456 dsi->data = soc->data;
5457 else
5458 dsi->data = of_match_node(dsi_of_match, dev->of_node)->data;
5459
Laurent Pinchart742e6932017-08-05 01:43:57 +03005460 d = dsi->data->modules;
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005461 while (d->address != 0 && d->address != dsi_mem->start)
5462 d++;
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005463
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005464 if (d->address == 0) {
5465 DSSERR("unsupported DSI module\n");
5466 return -ENODEV;
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005467 }
5468
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005469 dsi->module_id = d->id;
5470
Laurent Pinchart9e1305d2017-08-05 01:43:53 +03005471 if (dsi->data->model == DSI_MODEL_OMAP4) {
5472 struct device_node *np;
5473
5474 /*
5475 * The OMAP4 display DT bindings don't reference the padconf
5476 * syscon. Our only option to retrieve it is to find it by name.
5477 */
5478 np = of_find_node_by_name(NULL, "omap4_padconf_global");
5479 if (!np)
5480 return -ENODEV;
5481
5482 dsi->syscon = syscon_node_to_regmap(np);
5483 of_node_put(np);
5484 }
5485
Archit Taneja5ee3c142011-03-02 12:35:53 +05305486 /* DSI VCs initialization */
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305487 for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
Archit Tanejad6049142011-08-22 11:58:08 +05305488 dsi->vc[i].source = DSI_VC_SOURCE_L4;
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305489 dsi->vc[i].dssdev = NULL;
5490 dsi->vc[i].vc_id = 0;
Archit Taneja5ee3c142011-03-02 12:35:53 +05305491 }
5492
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005493 r = dsi_get_clocks(dsidev);
5494 if (r)
5495 return r;
5496
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005497 dsi_init_pll_data(dsidev);
5498
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005499 pm_runtime_enable(&dsidev->dev);
5500
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005501 r = dsi_runtime_get(dsidev);
5502 if (r)
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005503 goto err_runtime_get;
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005504
Archit Tanejaa72b64b2011-05-12 17:26:26 +05305505 rev = dsi_read_reg(dsidev, DSI_REVISION);
5506 dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005507 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
5508
Tomi Valkeinend9820852011-10-12 15:05:59 +03005509 /* DSI on OMAP3 doesn't have register DSI_GNQ, set number
5510 * of data to 3 by default */
Laurent Pinchart44d8ca12017-08-05 01:44:10 +03005511 if (dsi->data->quirks & DSI_QUIRK_GNQ)
Tomi Valkeinend9820852011-10-12 15:05:59 +03005512 /* NB_DATA_LANES */
5513 dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9);
5514 else
5515 dsi->num_lanes_supported = 3;
Archit Taneja75d72472011-05-16 15:17:08 +05305516
Tomi Valkeinen99322572013-03-05 10:37:02 +02005517 dsi->line_buffer_size = dsi_get_line_buf_size(dsidev);
5518
Archit Taneja81b87f52012-09-26 16:30:49 +05305519 dsi_init_output(dsidev);
5520
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005521 r = dsi_probe_of(dsidev);
5522 if (r) {
5523 DSSERR("Invalid DSI DT data\n");
5524 goto err_probe_of;
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005525 }
5526
Laurent Pinchart1dff2122017-05-07 00:42:26 +03005527 r = of_platform_populate(dsidev->dev.of_node, NULL, NULL, &dsidev->dev);
5528 if (r)
5529 DSSERR("Failed to populate DSI child devices: %d\n", r);
5530
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005531 dsi_runtime_put(dsidev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005532
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02005533 if (dsi->module_id == 0)
Tomi Valkeinene40402c2012-03-02 18:01:07 +02005534 dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs);
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02005535 else if (dsi->module_id == 1)
Tomi Valkeinene40402c2012-03-02 18:01:07 +02005536 dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs);
5537
5538#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02005539 if (dsi->module_id == 0)
Tomi Valkeinene40402c2012-03-02 18:01:07 +02005540 dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs);
Tomi Valkeinen11ee9602012-03-09 16:07:39 +02005541 else if (dsi->module_id == 1)
Tomi Valkeinene40402c2012-03-02 18:01:07 +02005542 dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs);
5543#endif
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005544
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005545 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005546
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005547err_probe_of:
5548 dsi_uninit_output(dsidev);
5549 dsi_runtime_put(dsidev);
5550
Tomi Valkeinencd3b3442012-01-25 13:31:04 +02005551err_runtime_get:
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005552 pm_runtime_disable(&dsidev->dev);
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005553 return r;
5554}
5555
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005556static void dsi_unbind(struct device *dev, struct device *master, void *data)
Tomi Valkeinen3de7a1d2009-10-28 11:59:56 +02005557{
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005558 struct platform_device *dsidev = to_platform_device(dev);
Archit Tanejaf1da39d2011-05-12 17:26:27 +05305559 struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
5560
Tomi Valkeinene4e42b82014-07-31 16:15:39 +03005561 of_platform_depopulate(&dsidev->dev);
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005562
Tomi Valkeinenb98482e2011-05-16 13:52:51 +03005563 WARN_ON(dsi->scp_clk_refcount > 0);
5564
Tomi Valkeinen2daea7a2014-10-22 14:49:14 +03005565 dss_pll_unregister(&dsi->pll);
5566
Archit Taneja81b87f52012-09-26 16:30:49 +05305567 dsi_uninit_output(dsidev);
5568
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005569 pm_runtime_disable(&dsidev->dev);
5570
Tomi Valkeinenb2541c42013-05-03 13:42:24 +03005571 if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
5572 regulator_disable(dsi->vdds_dsi_reg);
5573 dsi->vdds_dsi_enabled = false;
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005574 }
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005575}
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005576
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005577static const struct component_ops dsi_component_ops = {
5578 .bind = dsi_bind,
5579 .unbind = dsi_unbind,
5580};
5581
5582static int dsi_probe(struct platform_device *pdev)
5583{
5584 return component_add(&pdev->dev, &dsi_component_ops);
5585}
5586
5587static int dsi_remove(struct platform_device *pdev)
5588{
5589 component_del(&pdev->dev, &dsi_component_ops);
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005590 return 0;
5591}
5592
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005593static int dsi_runtime_suspend(struct device *dev)
5594{
Tomi Valkeinen0925afc2014-04-11 13:49:55 +03005595 struct platform_device *pdev = to_platform_device(dev);
5596 struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5597
5598 dsi->is_enabled = false;
5599 /* ensure the irq handler sees the is_enabled value */
5600 smp_wmb();
5601 /* wait for current handler to finish before turning the DSI off */
5602 synchronize_irq(dsi->irq);
5603
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005604 dispc_runtime_put();
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005605
5606 return 0;
5607}
5608
5609static int dsi_runtime_resume(struct device *dev)
5610{
Tomi Valkeinen0925afc2014-04-11 13:49:55 +03005611 struct platform_device *pdev = to_platform_device(dev);
5612 struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005613 int r;
5614
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005615 r = dispc_runtime_get();
5616 if (r)
Tomi Valkeinen852f0832012-02-17 17:58:04 +02005617 return r;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005618
Tomi Valkeinen0925afc2014-04-11 13:49:55 +03005619 dsi->is_enabled = true;
5620 /* ensure the irq handler sees the is_enabled value */
5621 smp_wmb();
5622
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005623 return 0;
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005624}
5625
5626static const struct dev_pm_ops dsi_pm_ops = {
5627 .runtime_suspend = dsi_runtime_suspend,
5628 .runtime_resume = dsi_runtime_resume,
5629};
5630
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03005631static struct platform_driver omap_dsihw_driver = {
Tomi Valkeinen736e60d2015-06-04 15:22:23 +03005632 .probe = dsi_probe,
5633 .remove = dsi_remove,
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005634 .driver = {
Tomi Valkeinen7c68dd92011-08-03 14:00:57 +03005635 .name = "omapdss_dsi",
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03005636 .pm = &dsi_pm_ops,
Tomi Valkeinen6274a612012-08-21 15:35:42 +03005637 .of_match_table = dsi_of_match,
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +03005638 .suppress_bind_attrs = true,
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005639 },
5640};
5641
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +02005642int __init dsi_init_platform_driver(void)
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005643{
Tomi Valkeinenee4a24e2013-04-26 13:47:06 +03005644 return platform_driver_register(&omap_dsihw_driver);
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005645}
5646
Tomi Valkeinenede92692015-06-04 14:12:16 +03005647void dsi_uninit_platform_driver(void)
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005648{
Tomi Valkeinen04c742c2012-02-23 15:32:37 +02005649 platform_driver_unregister(&omap_dsihw_driver);
Senthilvadivu Guruswamyc8aac012011-01-24 06:22:02 +00005650}