xref: /openbmc/linux/drivers/gpu/drm/omapdrm/dss/dsi.c (revision c2807ecb)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29960aa7cSTomi Valkeinen /*
39960aa7cSTomi Valkeinen  * Copyright (C) 2009 Nokia Corporation
46505d75cSTomi Valkeinen  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
59960aa7cSTomi Valkeinen  */
69960aa7cSTomi Valkeinen 
79960aa7cSTomi Valkeinen #define DSS_SUBSYS_NAME "DSI"
89960aa7cSTomi Valkeinen 
99960aa7cSTomi Valkeinen #include <linux/kernel.h>
109e1305d0SLaurent Pinchart #include <linux/mfd/syscon.h>
119e1305d0SLaurent Pinchart #include <linux/regmap.h>
129960aa7cSTomi Valkeinen #include <linux/io.h>
139960aa7cSTomi Valkeinen #include <linux/clk.h>
149960aa7cSTomi Valkeinen #include <linux/device.h>
159960aa7cSTomi Valkeinen #include <linux/err.h>
169960aa7cSTomi Valkeinen #include <linux/interrupt.h>
174c1b935fSSebastian Reichel #include <linux/irq.h>
189960aa7cSTomi Valkeinen #include <linux/delay.h>
194c1b935fSSebastian Reichel #include <linux/gpio/consumer.h>
209960aa7cSTomi Valkeinen #include <linux/mutex.h>
219960aa7cSTomi Valkeinen #include <linux/module.h>
229960aa7cSTomi Valkeinen #include <linux/semaphore.h>
239960aa7cSTomi Valkeinen #include <linux/seq_file.h>
249960aa7cSTomi Valkeinen #include <linux/platform_device.h>
259960aa7cSTomi Valkeinen #include <linux/regulator/consumer.h>
269960aa7cSTomi Valkeinen #include <linux/wait.h>
279960aa7cSTomi Valkeinen #include <linux/workqueue.h>
289960aa7cSTomi Valkeinen #include <linux/sched.h>
299960aa7cSTomi Valkeinen #include <linux/slab.h>
309960aa7cSTomi Valkeinen #include <linux/debugfs.h>
319960aa7cSTomi Valkeinen #include <linux/pm_runtime.h>
329960aa7cSTomi Valkeinen #include <linux/of.h>
3309bffa6eSRob Herring #include <linux/of_graph.h>
349960aa7cSTomi Valkeinen #include <linux/of_platform.h>
359960aa7cSTomi Valkeinen #include <linux/component.h>
3644d8ca10SLaurent Pinchart #include <linux/sys_soc.h>
379960aa7cSTomi Valkeinen 
38af1110cbSSebastian Reichel #include <drm/drm_bridge.h>
39d0103cebSSebastian Reichel #include <drm/drm_mipi_dsi.h>
401cac9ba2SSebastian Reichel #include <drm/drm_panel.h>
419960aa7cSTomi Valkeinen #include <video/mipi_display.h>
429960aa7cSTomi Valkeinen 
4332043da7SPeter Ujfalusi #include "omapdss.h"
449960aa7cSTomi Valkeinen #include "dss.h"
459960aa7cSTomi Valkeinen 
469960aa7cSTomi Valkeinen #define DSI_CATCH_MISSING_TE
479960aa7cSTomi Valkeinen 
487aa52346STomi Valkeinen #include "dsi.h"
499960aa7cSTomi Valkeinen 
507093d6cdSLaurent Pinchart #define REG_GET(dsi, idx, start, end) \
517093d6cdSLaurent Pinchart 	FLD_GET(dsi_read_reg(dsi, idx), start, end)
529960aa7cSTomi Valkeinen 
537093d6cdSLaurent Pinchart #define REG_FLD_MOD(dsi, idx, val, start, end) \
547093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, idx, FLD_MOD(dsi_read_reg(dsi, idx), val, start, end))
559960aa7cSTomi Valkeinen 
56a4a29d1dSTomi Valkeinen static int dsi_init_dispc(struct dsi_data *dsi);
57a4a29d1dSTomi Valkeinen static void dsi_uninit_dispc(struct dsi_data *dsi);
589960aa7cSTomi Valkeinen 
595e430754STomi Valkeinen static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel);
609960aa7cSTomi Valkeinen 
615e430754STomi Valkeinen static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc,
622a4703c2SSebastian Reichel 				       const struct mipi_dsi_msg *msg);
632a4703c2SSebastian Reichel 
649960aa7cSTomi Valkeinen #ifdef DSI_PERF_MEASURE
659960aa7cSTomi Valkeinen static bool dsi_perf;
669960aa7cSTomi Valkeinen module_param(dsi_perf, bool, 0644);
679960aa7cSTomi Valkeinen #endif
689960aa7cSTomi Valkeinen 
694029b16bSTomi Valkeinen /* Note: for some reason video mode seems to work only if VC_VIDEO is 0 */
704029b16bSTomi Valkeinen #define VC_VIDEO	0
714029b16bSTomi Valkeinen #define VC_CMD		1
72d843314eSTomi Valkeinen 
73af1110cbSSebastian Reichel #define drm_bridge_to_dsi(bridge) \
74af1110cbSSebastian Reichel 	container_of(bridge, struct dsi_data, bridge)
75af1110cbSSebastian Reichel 
to_dsi_data(struct omap_dss_device * dssdev)76c068408eSLaurent Pinchart static inline struct dsi_data *to_dsi_data(struct omap_dss_device *dssdev)
779960aa7cSTomi Valkeinen {
78c068408eSLaurent Pinchart 	return dev_get_drvdata(dssdev->dev);
799960aa7cSTomi Valkeinen }
809960aa7cSTomi Valkeinen 
host_to_omap(struct mipi_dsi_host * host)819cd87829SSebastian Reichel static inline struct dsi_data *host_to_omap(struct mipi_dsi_host *host)
829cd87829SSebastian Reichel {
839cd87829SSebastian Reichel 	return container_of(host, struct dsi_data, host);
849cd87829SSebastian Reichel }
859cd87829SSebastian Reichel 
dsi_write_reg(struct dsi_data * dsi,const struct dsi_reg idx,u32 val)867093d6cdSLaurent Pinchart static inline void dsi_write_reg(struct dsi_data *dsi,
879960aa7cSTomi Valkeinen 				 const struct dsi_reg idx, u32 val)
889960aa7cSTomi Valkeinen {
899960aa7cSTomi Valkeinen 	void __iomem *base;
909960aa7cSTomi Valkeinen 
919960aa7cSTomi Valkeinen 	switch(idx.module) {
929960aa7cSTomi Valkeinen 		case DSI_PROTO: base = dsi->proto_base; break;
939960aa7cSTomi Valkeinen 		case DSI_PHY: base = dsi->phy_base; break;
949960aa7cSTomi Valkeinen 		case DSI_PLL: base = dsi->pll_base; break;
959960aa7cSTomi Valkeinen 		default: return;
969960aa7cSTomi Valkeinen 	}
979960aa7cSTomi Valkeinen 
989960aa7cSTomi Valkeinen 	__raw_writel(val, base + idx.idx);
999960aa7cSTomi Valkeinen }
1009960aa7cSTomi Valkeinen 
dsi_read_reg(struct dsi_data * dsi,const struct dsi_reg idx)1017093d6cdSLaurent Pinchart static inline u32 dsi_read_reg(struct dsi_data *dsi, const struct dsi_reg idx)
1029960aa7cSTomi Valkeinen {
1039960aa7cSTomi Valkeinen 	void __iomem *base;
1049960aa7cSTomi Valkeinen 
1059960aa7cSTomi Valkeinen 	switch(idx.module) {
1069960aa7cSTomi Valkeinen 		case DSI_PROTO: base = dsi->proto_base; break;
1079960aa7cSTomi Valkeinen 		case DSI_PHY: base = dsi->phy_base; break;
1089960aa7cSTomi Valkeinen 		case DSI_PLL: base = dsi->pll_base; break;
1099960aa7cSTomi Valkeinen 		default: return 0;
1109960aa7cSTomi Valkeinen 	}
1119960aa7cSTomi Valkeinen 
1129960aa7cSTomi Valkeinen 	return __raw_readl(base + idx.idx);
1139960aa7cSTomi Valkeinen }
1149960aa7cSTomi Valkeinen 
dsi_bus_lock(struct dsi_data * dsi)1150c93a61dSSebastian Reichel static void dsi_bus_lock(struct dsi_data *dsi)
1169960aa7cSTomi Valkeinen {
1179960aa7cSTomi Valkeinen 	down(&dsi->bus_lock);
1189960aa7cSTomi Valkeinen }
1199960aa7cSTomi Valkeinen 
dsi_bus_unlock(struct dsi_data * dsi)1200c93a61dSSebastian Reichel static void dsi_bus_unlock(struct dsi_data *dsi)
1219960aa7cSTomi Valkeinen {
1229960aa7cSTomi Valkeinen 	up(&dsi->bus_lock);
1239960aa7cSTomi Valkeinen }
1249960aa7cSTomi Valkeinen 
dsi_bus_is_locked(struct dsi_data * dsi)1257093d6cdSLaurent Pinchart static bool dsi_bus_is_locked(struct dsi_data *dsi)
1269960aa7cSTomi Valkeinen {
1279960aa7cSTomi Valkeinen 	return dsi->bus_lock.count == 0;
1289960aa7cSTomi Valkeinen }
1299960aa7cSTomi Valkeinen 
dsi_completion_handler(void * data,u32 mask)1309960aa7cSTomi Valkeinen static void dsi_completion_handler(void *data, u32 mask)
1319960aa7cSTomi Valkeinen {
1329960aa7cSTomi Valkeinen 	complete((struct completion *)data);
1339960aa7cSTomi Valkeinen }
1349960aa7cSTomi Valkeinen 
wait_for_bit_change(struct dsi_data * dsi,const struct dsi_reg idx,int bitnum,int value)1357093d6cdSLaurent Pinchart static inline bool wait_for_bit_change(struct dsi_data *dsi,
1367093d6cdSLaurent Pinchart 				       const struct dsi_reg idx,
1377093d6cdSLaurent Pinchart 				       int bitnum, int value)
1389960aa7cSTomi Valkeinen {
1399960aa7cSTomi Valkeinen 	unsigned long timeout;
1409960aa7cSTomi Valkeinen 	ktime_t wait;
1419960aa7cSTomi Valkeinen 	int t;
1429960aa7cSTomi Valkeinen 
1439960aa7cSTomi Valkeinen 	/* first busyloop to see if the bit changes right away */
1449960aa7cSTomi Valkeinen 	t = 100;
1459960aa7cSTomi Valkeinen 	while (t-- > 0) {
1467093d6cdSLaurent Pinchart 		if (REG_GET(dsi, idx, bitnum, bitnum) == value)
147bafa89fcSLaurent Pinchart 			return true;
1489960aa7cSTomi Valkeinen 	}
1499960aa7cSTomi Valkeinen 
1509960aa7cSTomi Valkeinen 	/* then loop for 500ms, sleeping for 1ms in between */
1519960aa7cSTomi Valkeinen 	timeout = jiffies + msecs_to_jiffies(500);
1529960aa7cSTomi Valkeinen 	while (time_before(jiffies, timeout)) {
1537093d6cdSLaurent Pinchart 		if (REG_GET(dsi, idx, bitnum, bitnum) == value)
154bafa89fcSLaurent Pinchart 			return true;
1559960aa7cSTomi Valkeinen 
1569960aa7cSTomi Valkeinen 		wait = ns_to_ktime(1000 * 1000);
1579960aa7cSTomi Valkeinen 		set_current_state(TASK_UNINTERRUPTIBLE);
1589960aa7cSTomi Valkeinen 		schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
1599960aa7cSTomi Valkeinen 	}
1609960aa7cSTomi Valkeinen 
161bafa89fcSLaurent Pinchart 	return false;
1629960aa7cSTomi Valkeinen }
1639960aa7cSTomi Valkeinen 
1649960aa7cSTomi Valkeinen #ifdef DSI_PERF_MEASURE
dsi_perf_mark_setup(struct dsi_data * dsi)1657093d6cdSLaurent Pinchart static void dsi_perf_mark_setup(struct dsi_data *dsi)
1669960aa7cSTomi Valkeinen {
1679960aa7cSTomi Valkeinen 	dsi->perf_setup_time = ktime_get();
1689960aa7cSTomi Valkeinen }
1699960aa7cSTomi Valkeinen 
dsi_perf_mark_start(struct dsi_data * dsi)1707093d6cdSLaurent Pinchart static void dsi_perf_mark_start(struct dsi_data *dsi)
1719960aa7cSTomi Valkeinen {
1729960aa7cSTomi Valkeinen 	dsi->perf_start_time = ktime_get();
1739960aa7cSTomi Valkeinen }
1749960aa7cSTomi Valkeinen 
dsi_perf_show(struct dsi_data * dsi,const char * name)1757093d6cdSLaurent Pinchart static void dsi_perf_show(struct dsi_data *dsi, const char *name)
1769960aa7cSTomi Valkeinen {
1779960aa7cSTomi Valkeinen 	ktime_t t, setup_time, trans_time;
1789960aa7cSTomi Valkeinen 	u32 total_bytes;
1799960aa7cSTomi Valkeinen 	u32 setup_us, trans_us, total_us;
1809960aa7cSTomi Valkeinen 
1819960aa7cSTomi Valkeinen 	if (!dsi_perf)
1829960aa7cSTomi Valkeinen 		return;
1839960aa7cSTomi Valkeinen 
1849960aa7cSTomi Valkeinen 	t = ktime_get();
1859960aa7cSTomi Valkeinen 
1869960aa7cSTomi Valkeinen 	setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time);
1879960aa7cSTomi Valkeinen 	setup_us = (u32)ktime_to_us(setup_time);
1889960aa7cSTomi Valkeinen 	if (setup_us == 0)
1899960aa7cSTomi Valkeinen 		setup_us = 1;
1909960aa7cSTomi Valkeinen 
1919960aa7cSTomi Valkeinen 	trans_time = ktime_sub(t, dsi->perf_start_time);
1929960aa7cSTomi Valkeinen 	trans_us = (u32)ktime_to_us(trans_time);
1939960aa7cSTomi Valkeinen 	if (trans_us == 0)
1949960aa7cSTomi Valkeinen 		trans_us = 1;
1959960aa7cSTomi Valkeinen 
1969960aa7cSTomi Valkeinen 	total_us = setup_us + trans_us;
1979960aa7cSTomi Valkeinen 
1989960aa7cSTomi Valkeinen 	total_bytes = dsi->update_bytes;
1999960aa7cSTomi Valkeinen 
2008dfe162aSJoe Perches 	pr_info("DSI(%s): %u us + %u us = %u us (%uHz), %u bytes, %u kbytes/sec\n",
2019960aa7cSTomi Valkeinen 		name,
2029960aa7cSTomi Valkeinen 		setup_us,
2039960aa7cSTomi Valkeinen 		trans_us,
2049960aa7cSTomi Valkeinen 		total_us,
2059960aa7cSTomi Valkeinen 		1000 * 1000 / total_us,
2069960aa7cSTomi Valkeinen 		total_bytes,
2079960aa7cSTomi Valkeinen 		total_bytes * 1000 / total_us);
2089960aa7cSTomi Valkeinen }
2099960aa7cSTomi Valkeinen #else
dsi_perf_mark_setup(struct dsi_data * dsi)2107093d6cdSLaurent Pinchart static inline void dsi_perf_mark_setup(struct dsi_data *dsi)
2119960aa7cSTomi Valkeinen {
2129960aa7cSTomi Valkeinen }
2139960aa7cSTomi Valkeinen 
dsi_perf_mark_start(struct dsi_data * dsi)2147093d6cdSLaurent Pinchart static inline void dsi_perf_mark_start(struct dsi_data *dsi)
2159960aa7cSTomi Valkeinen {
2169960aa7cSTomi Valkeinen }
2179960aa7cSTomi Valkeinen 
dsi_perf_show(struct dsi_data * dsi,const char * name)2187093d6cdSLaurent Pinchart static inline void dsi_perf_show(struct dsi_data *dsi, const char *name)
2199960aa7cSTomi Valkeinen {
2209960aa7cSTomi Valkeinen }
2219960aa7cSTomi Valkeinen #endif
2229960aa7cSTomi Valkeinen 
2239960aa7cSTomi Valkeinen static int verbose_irq;
2249960aa7cSTomi Valkeinen 
print_irq_status(u32 status)2259960aa7cSTomi Valkeinen static void print_irq_status(u32 status)
2269960aa7cSTomi Valkeinen {
2279960aa7cSTomi Valkeinen 	if (status == 0)
2289960aa7cSTomi Valkeinen 		return;
2299960aa7cSTomi Valkeinen 
2309960aa7cSTomi Valkeinen 	if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0)
2319960aa7cSTomi Valkeinen 		return;
2329960aa7cSTomi Valkeinen 
2339960aa7cSTomi Valkeinen #define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : ""
2349960aa7cSTomi Valkeinen 
2359960aa7cSTomi Valkeinen 	pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
2369960aa7cSTomi Valkeinen 		status,
2379960aa7cSTomi Valkeinen 		verbose_irq ? PIS(VC0) : "",
2389960aa7cSTomi Valkeinen 		verbose_irq ? PIS(VC1) : "",
2399960aa7cSTomi Valkeinen 		verbose_irq ? PIS(VC2) : "",
2409960aa7cSTomi Valkeinen 		verbose_irq ? PIS(VC3) : "",
2419960aa7cSTomi Valkeinen 		PIS(WAKEUP),
2429960aa7cSTomi Valkeinen 		PIS(RESYNC),
2439960aa7cSTomi Valkeinen 		PIS(PLL_LOCK),
2449960aa7cSTomi Valkeinen 		PIS(PLL_UNLOCK),
2459960aa7cSTomi Valkeinen 		PIS(PLL_RECALL),
2469960aa7cSTomi Valkeinen 		PIS(COMPLEXIO_ERR),
2479960aa7cSTomi Valkeinen 		PIS(HS_TX_TIMEOUT),
2489960aa7cSTomi Valkeinen 		PIS(LP_RX_TIMEOUT),
2499960aa7cSTomi Valkeinen 		PIS(TE_TRIGGER),
2509960aa7cSTomi Valkeinen 		PIS(ACK_TRIGGER),
2519960aa7cSTomi Valkeinen 		PIS(SYNC_LOST),
2529960aa7cSTomi Valkeinen 		PIS(LDO_POWER_GOOD),
2539960aa7cSTomi Valkeinen 		PIS(TA_TIMEOUT));
2549960aa7cSTomi Valkeinen #undef PIS
2559960aa7cSTomi Valkeinen }
2569960aa7cSTomi Valkeinen 
print_irq_status_vc(int vc,u32 status)257d8171145STomi Valkeinen static void print_irq_status_vc(int vc, u32 status)
2589960aa7cSTomi Valkeinen {
2599960aa7cSTomi Valkeinen 	if (status == 0)
2609960aa7cSTomi Valkeinen 		return;
2619960aa7cSTomi Valkeinen 
2629960aa7cSTomi Valkeinen 	if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
2639960aa7cSTomi Valkeinen 		return;
2649960aa7cSTomi Valkeinen 
2659960aa7cSTomi Valkeinen #define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : ""
2669960aa7cSTomi Valkeinen 
2679960aa7cSTomi Valkeinen 	pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n",
268d8171145STomi Valkeinen 		vc,
2699960aa7cSTomi Valkeinen 		status,
2709960aa7cSTomi Valkeinen 		PIS(CS),
2719960aa7cSTomi Valkeinen 		PIS(ECC_CORR),
2729960aa7cSTomi Valkeinen 		PIS(ECC_NO_CORR),
2739960aa7cSTomi Valkeinen 		verbose_irq ? PIS(PACKET_SENT) : "",
2749960aa7cSTomi Valkeinen 		PIS(BTA),
2759960aa7cSTomi Valkeinen 		PIS(FIFO_TX_OVF),
2769960aa7cSTomi Valkeinen 		PIS(FIFO_RX_OVF),
2779960aa7cSTomi Valkeinen 		PIS(FIFO_TX_UDF),
2789960aa7cSTomi Valkeinen 		PIS(PP_BUSY_CHANGE));
2799960aa7cSTomi Valkeinen #undef PIS
2809960aa7cSTomi Valkeinen }
2819960aa7cSTomi Valkeinen 
print_irq_status_cio(u32 status)2829960aa7cSTomi Valkeinen static void print_irq_status_cio(u32 status)
2839960aa7cSTomi Valkeinen {
2849960aa7cSTomi Valkeinen 	if (status == 0)
2859960aa7cSTomi Valkeinen 		return;
2869960aa7cSTomi Valkeinen 
2879960aa7cSTomi Valkeinen #define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : ""
2889960aa7cSTomi Valkeinen 
2899960aa7cSTomi Valkeinen 	pr_debug("DSI CIO IRQ 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
2909960aa7cSTomi Valkeinen 		status,
2919960aa7cSTomi Valkeinen 		PIS(ERRSYNCESC1),
2929960aa7cSTomi Valkeinen 		PIS(ERRSYNCESC2),
2939960aa7cSTomi Valkeinen 		PIS(ERRSYNCESC3),
2949960aa7cSTomi Valkeinen 		PIS(ERRESC1),
2959960aa7cSTomi Valkeinen 		PIS(ERRESC2),
2969960aa7cSTomi Valkeinen 		PIS(ERRESC3),
2979960aa7cSTomi Valkeinen 		PIS(ERRCONTROL1),
2989960aa7cSTomi Valkeinen 		PIS(ERRCONTROL2),
2999960aa7cSTomi Valkeinen 		PIS(ERRCONTROL3),
3009960aa7cSTomi Valkeinen 		PIS(STATEULPS1),
3019960aa7cSTomi Valkeinen 		PIS(STATEULPS2),
3029960aa7cSTomi Valkeinen 		PIS(STATEULPS3),
3039960aa7cSTomi Valkeinen 		PIS(ERRCONTENTIONLP0_1),
3049960aa7cSTomi Valkeinen 		PIS(ERRCONTENTIONLP1_1),
3059960aa7cSTomi Valkeinen 		PIS(ERRCONTENTIONLP0_2),
3069960aa7cSTomi Valkeinen 		PIS(ERRCONTENTIONLP1_2),
3079960aa7cSTomi Valkeinen 		PIS(ERRCONTENTIONLP0_3),
3089960aa7cSTomi Valkeinen 		PIS(ERRCONTENTIONLP1_3),
3099960aa7cSTomi Valkeinen 		PIS(ULPSACTIVENOT_ALL0),
3109960aa7cSTomi Valkeinen 		PIS(ULPSACTIVENOT_ALL1));
3119960aa7cSTomi Valkeinen #undef PIS
3129960aa7cSTomi Valkeinen }
3139960aa7cSTomi Valkeinen 
3149960aa7cSTomi Valkeinen #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
dsi_collect_irq_stats(struct dsi_data * dsi,u32 irqstatus,u32 * vcstatus,u32 ciostatus)3157093d6cdSLaurent Pinchart static void dsi_collect_irq_stats(struct dsi_data *dsi, u32 irqstatus,
3169960aa7cSTomi Valkeinen 				  u32 *vcstatus, u32 ciostatus)
3179960aa7cSTomi Valkeinen {
3189960aa7cSTomi Valkeinen 	int i;
3199960aa7cSTomi Valkeinen 
3209960aa7cSTomi Valkeinen 	spin_lock(&dsi->irq_stats_lock);
3219960aa7cSTomi Valkeinen 
3229960aa7cSTomi Valkeinen 	dsi->irq_stats.irq_count++;
3239960aa7cSTomi Valkeinen 	dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs);
3249960aa7cSTomi Valkeinen 
3259960aa7cSTomi Valkeinen 	for (i = 0; i < 4; ++i)
3269960aa7cSTomi Valkeinen 		dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]);
3279960aa7cSTomi Valkeinen 
3289960aa7cSTomi Valkeinen 	dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs);
3299960aa7cSTomi Valkeinen 
3309960aa7cSTomi Valkeinen 	spin_unlock(&dsi->irq_stats_lock);
3319960aa7cSTomi Valkeinen }
3329960aa7cSTomi Valkeinen #else
3337093d6cdSLaurent Pinchart #define dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus)
3349960aa7cSTomi Valkeinen #endif
3359960aa7cSTomi Valkeinen 
3369960aa7cSTomi Valkeinen static int debug_irq;
3379960aa7cSTomi Valkeinen 
dsi_handle_irq_errors(struct dsi_data * dsi,u32 irqstatus,u32 * vcstatus,u32 ciostatus)3387093d6cdSLaurent Pinchart static void dsi_handle_irq_errors(struct dsi_data *dsi, u32 irqstatus,
3399960aa7cSTomi Valkeinen 				  u32 *vcstatus, u32 ciostatus)
3409960aa7cSTomi Valkeinen {
3419960aa7cSTomi Valkeinen 	int i;
3429960aa7cSTomi Valkeinen 
3439960aa7cSTomi Valkeinen 	if (irqstatus & DSI_IRQ_ERROR_MASK) {
3449960aa7cSTomi Valkeinen 		DSSERR("DSI error, irqstatus %x\n", irqstatus);
3459960aa7cSTomi Valkeinen 		print_irq_status(irqstatus);
3469960aa7cSTomi Valkeinen 		spin_lock(&dsi->errors_lock);
3479960aa7cSTomi Valkeinen 		dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK;
3489960aa7cSTomi Valkeinen 		spin_unlock(&dsi->errors_lock);
3499960aa7cSTomi Valkeinen 	} else if (debug_irq) {
3509960aa7cSTomi Valkeinen 		print_irq_status(irqstatus);
3519960aa7cSTomi Valkeinen 	}
3529960aa7cSTomi Valkeinen 
3539960aa7cSTomi Valkeinen 	for (i = 0; i < 4; ++i) {
3549960aa7cSTomi Valkeinen 		if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) {
3559960aa7cSTomi Valkeinen 			DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
3569960aa7cSTomi Valkeinen 				       i, vcstatus[i]);
3579960aa7cSTomi Valkeinen 			print_irq_status_vc(i, vcstatus[i]);
3589960aa7cSTomi Valkeinen 		} else if (debug_irq) {
3599960aa7cSTomi Valkeinen 			print_irq_status_vc(i, vcstatus[i]);
3609960aa7cSTomi Valkeinen 		}
3619960aa7cSTomi Valkeinen 	}
3629960aa7cSTomi Valkeinen 
3639960aa7cSTomi Valkeinen 	if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
3649960aa7cSTomi Valkeinen 		DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
3659960aa7cSTomi Valkeinen 		print_irq_status_cio(ciostatus);
3669960aa7cSTomi Valkeinen 	} else if (debug_irq) {
3679960aa7cSTomi Valkeinen 		print_irq_status_cio(ciostatus);
3689960aa7cSTomi Valkeinen 	}
3699960aa7cSTomi Valkeinen }
3709960aa7cSTomi Valkeinen 
dsi_call_isrs(struct dsi_isr_data * isr_array,unsigned int isr_array_size,u32 irqstatus)3719960aa7cSTomi Valkeinen static void dsi_call_isrs(struct dsi_isr_data *isr_array,
372d11e5c82SLaurent Pinchart 		unsigned int isr_array_size, u32 irqstatus)
3739960aa7cSTomi Valkeinen {
3749960aa7cSTomi Valkeinen 	struct dsi_isr_data *isr_data;
3759960aa7cSTomi Valkeinen 	int i;
3769960aa7cSTomi Valkeinen 
3779960aa7cSTomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
3789960aa7cSTomi Valkeinen 		isr_data = &isr_array[i];
3799960aa7cSTomi Valkeinen 		if (isr_data->isr && isr_data->mask & irqstatus)
3809960aa7cSTomi Valkeinen 			isr_data->isr(isr_data->arg, irqstatus);
3819960aa7cSTomi Valkeinen 	}
3829960aa7cSTomi Valkeinen }
3839960aa7cSTomi Valkeinen 
dsi_handle_isrs(struct dsi_isr_tables * isr_tables,u32 irqstatus,u32 * vcstatus,u32 ciostatus)3849960aa7cSTomi Valkeinen static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
3859960aa7cSTomi Valkeinen 		u32 irqstatus, u32 *vcstatus, u32 ciostatus)
3869960aa7cSTomi Valkeinen {
3879960aa7cSTomi Valkeinen 	int i;
3889960aa7cSTomi Valkeinen 
3899960aa7cSTomi Valkeinen 	dsi_call_isrs(isr_tables->isr_table,
3909960aa7cSTomi Valkeinen 			ARRAY_SIZE(isr_tables->isr_table),
3919960aa7cSTomi Valkeinen 			irqstatus);
3929960aa7cSTomi Valkeinen 
3939960aa7cSTomi Valkeinen 	for (i = 0; i < 4; ++i) {
3949960aa7cSTomi Valkeinen 		if (vcstatus[i] == 0)
3959960aa7cSTomi Valkeinen 			continue;
3969960aa7cSTomi Valkeinen 		dsi_call_isrs(isr_tables->isr_table_vc[i],
3979960aa7cSTomi Valkeinen 				ARRAY_SIZE(isr_tables->isr_table_vc[i]),
3989960aa7cSTomi Valkeinen 				vcstatus[i]);
3999960aa7cSTomi Valkeinen 	}
4009960aa7cSTomi Valkeinen 
4019960aa7cSTomi Valkeinen 	if (ciostatus != 0)
4029960aa7cSTomi Valkeinen 		dsi_call_isrs(isr_tables->isr_table_cio,
4039960aa7cSTomi Valkeinen 				ARRAY_SIZE(isr_tables->isr_table_cio),
4049960aa7cSTomi Valkeinen 				ciostatus);
4059960aa7cSTomi Valkeinen }
4069960aa7cSTomi Valkeinen 
omap_dsi_irq_handler(int irq,void * arg)4079960aa7cSTomi Valkeinen static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
4089960aa7cSTomi Valkeinen {
4097093d6cdSLaurent Pinchart 	struct dsi_data *dsi = arg;
4109960aa7cSTomi Valkeinen 	u32 irqstatus, vcstatus[4], ciostatus;
4119960aa7cSTomi Valkeinen 	int i;
4129960aa7cSTomi Valkeinen 
4139960aa7cSTomi Valkeinen 	if (!dsi->is_enabled)
4149960aa7cSTomi Valkeinen 		return IRQ_NONE;
4159960aa7cSTomi Valkeinen 
4169960aa7cSTomi Valkeinen 	spin_lock(&dsi->irq_lock);
4179960aa7cSTomi Valkeinen 
4187093d6cdSLaurent Pinchart 	irqstatus = dsi_read_reg(dsi, DSI_IRQSTATUS);
4199960aa7cSTomi Valkeinen 
4209960aa7cSTomi Valkeinen 	/* IRQ is not for us */
4219960aa7cSTomi Valkeinen 	if (!irqstatus) {
4229960aa7cSTomi Valkeinen 		spin_unlock(&dsi->irq_lock);
4239960aa7cSTomi Valkeinen 		return IRQ_NONE;
4249960aa7cSTomi Valkeinen 	}
4259960aa7cSTomi Valkeinen 
4267093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
4279960aa7cSTomi Valkeinen 	/* flush posted write */
4287093d6cdSLaurent Pinchart 	dsi_read_reg(dsi, DSI_IRQSTATUS);
4299960aa7cSTomi Valkeinen 
4309960aa7cSTomi Valkeinen 	for (i = 0; i < 4; ++i) {
4319960aa7cSTomi Valkeinen 		if ((irqstatus & (1 << i)) == 0) {
4329960aa7cSTomi Valkeinen 			vcstatus[i] = 0;
4339960aa7cSTomi Valkeinen 			continue;
4349960aa7cSTomi Valkeinen 		}
4359960aa7cSTomi Valkeinen 
4367093d6cdSLaurent Pinchart 		vcstatus[i] = dsi_read_reg(dsi, DSI_VC_IRQSTATUS(i));
4379960aa7cSTomi Valkeinen 
4387093d6cdSLaurent Pinchart 		dsi_write_reg(dsi, DSI_VC_IRQSTATUS(i), vcstatus[i]);
4399960aa7cSTomi Valkeinen 		/* flush posted write */
4407093d6cdSLaurent Pinchart 		dsi_read_reg(dsi, DSI_VC_IRQSTATUS(i));
4419960aa7cSTomi Valkeinen 	}
4429960aa7cSTomi Valkeinen 
4439960aa7cSTomi Valkeinen 	if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
4447093d6cdSLaurent Pinchart 		ciostatus = dsi_read_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS);
4459960aa7cSTomi Valkeinen 
4467093d6cdSLaurent Pinchart 		dsi_write_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
4479960aa7cSTomi Valkeinen 		/* flush posted write */
4487093d6cdSLaurent Pinchart 		dsi_read_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS);
4499960aa7cSTomi Valkeinen 	} else {
4509960aa7cSTomi Valkeinen 		ciostatus = 0;
4519960aa7cSTomi Valkeinen 	}
4529960aa7cSTomi Valkeinen 
4539960aa7cSTomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
4549960aa7cSTomi Valkeinen 	if (irqstatus & DSI_IRQ_TE_TRIGGER)
4559960aa7cSTomi Valkeinen 		del_timer(&dsi->te_timer);
4569960aa7cSTomi Valkeinen #endif
4579960aa7cSTomi Valkeinen 
4589960aa7cSTomi Valkeinen 	/* make a copy and unlock, so that isrs can unregister
4599960aa7cSTomi Valkeinen 	 * themselves */
4609960aa7cSTomi Valkeinen 	memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
4619960aa7cSTomi Valkeinen 		sizeof(dsi->isr_tables));
4629960aa7cSTomi Valkeinen 
4639960aa7cSTomi Valkeinen 	spin_unlock(&dsi->irq_lock);
4649960aa7cSTomi Valkeinen 
4659960aa7cSTomi Valkeinen 	dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
4669960aa7cSTomi Valkeinen 
4677093d6cdSLaurent Pinchart 	dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus);
4689960aa7cSTomi Valkeinen 
4697093d6cdSLaurent Pinchart 	dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus);
4709960aa7cSTomi Valkeinen 
4719960aa7cSTomi Valkeinen 	return IRQ_HANDLED;
4729960aa7cSTomi Valkeinen }
4739960aa7cSTomi Valkeinen 
4749960aa7cSTomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_configure_irqs(struct dsi_data * dsi,struct dsi_isr_data * isr_array,unsigned int isr_array_size,u32 default_mask,const struct dsi_reg enable_reg,const struct dsi_reg status_reg)4757093d6cdSLaurent Pinchart static void _omap_dsi_configure_irqs(struct dsi_data *dsi,
4769960aa7cSTomi Valkeinen 				     struct dsi_isr_data *isr_array,
4777093d6cdSLaurent Pinchart 				     unsigned int isr_array_size,
4787093d6cdSLaurent Pinchart 				     u32 default_mask,
4799960aa7cSTomi Valkeinen 				     const struct dsi_reg enable_reg,
4809960aa7cSTomi Valkeinen 				     const struct dsi_reg status_reg)
4819960aa7cSTomi Valkeinen {
4829960aa7cSTomi Valkeinen 	struct dsi_isr_data *isr_data;
4839960aa7cSTomi Valkeinen 	u32 mask;
4849960aa7cSTomi Valkeinen 	u32 old_mask;
4859960aa7cSTomi Valkeinen 	int i;
4869960aa7cSTomi Valkeinen 
4879960aa7cSTomi Valkeinen 	mask = default_mask;
4889960aa7cSTomi Valkeinen 
4899960aa7cSTomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
4909960aa7cSTomi Valkeinen 		isr_data = &isr_array[i];
4919960aa7cSTomi Valkeinen 
4929960aa7cSTomi Valkeinen 		if (isr_data->isr == NULL)
4939960aa7cSTomi Valkeinen 			continue;
4949960aa7cSTomi Valkeinen 
4959960aa7cSTomi Valkeinen 		mask |= isr_data->mask;
4969960aa7cSTomi Valkeinen 	}
4979960aa7cSTomi Valkeinen 
4987093d6cdSLaurent Pinchart 	old_mask = dsi_read_reg(dsi, enable_reg);
4999960aa7cSTomi Valkeinen 	/* clear the irqstatus for newly enabled irqs */
5007093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, status_reg, (mask ^ old_mask) & mask);
5017093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, enable_reg, mask);
5029960aa7cSTomi Valkeinen 
5039960aa7cSTomi Valkeinen 	/* flush posted writes */
5047093d6cdSLaurent Pinchart 	dsi_read_reg(dsi, enable_reg);
5057093d6cdSLaurent Pinchart 	dsi_read_reg(dsi, status_reg);
5069960aa7cSTomi Valkeinen }
5079960aa7cSTomi Valkeinen 
5089960aa7cSTomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_set_irqs(struct dsi_data * dsi)5097093d6cdSLaurent Pinchart static void _omap_dsi_set_irqs(struct dsi_data *dsi)
5109960aa7cSTomi Valkeinen {
5119960aa7cSTomi Valkeinen 	u32 mask = DSI_IRQ_ERROR_MASK;
5129960aa7cSTomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
5139960aa7cSTomi Valkeinen 	mask |= DSI_IRQ_TE_TRIGGER;
5149960aa7cSTomi Valkeinen #endif
5157093d6cdSLaurent Pinchart 	_omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table,
5169960aa7cSTomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table), mask,
5179960aa7cSTomi Valkeinen 			DSI_IRQENABLE, DSI_IRQSTATUS);
5189960aa7cSTomi Valkeinen }
5199960aa7cSTomi Valkeinen 
5209960aa7cSTomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_set_irqs_vc(struct dsi_data * dsi,int vc)5217093d6cdSLaurent Pinchart static void _omap_dsi_set_irqs_vc(struct dsi_data *dsi, int vc)
5229960aa7cSTomi Valkeinen {
5237093d6cdSLaurent Pinchart 	_omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table_vc[vc],
5249960aa7cSTomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]),
5259960aa7cSTomi Valkeinen 			DSI_VC_IRQ_ERROR_MASK,
5269960aa7cSTomi Valkeinen 			DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
5279960aa7cSTomi Valkeinen }
5289960aa7cSTomi Valkeinen 
5299960aa7cSTomi Valkeinen /* dsi->irq_lock has to be locked by the caller */
_omap_dsi_set_irqs_cio(struct dsi_data * dsi)5307093d6cdSLaurent Pinchart static void _omap_dsi_set_irqs_cio(struct dsi_data *dsi)
5319960aa7cSTomi Valkeinen {
5327093d6cdSLaurent Pinchart 	_omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table_cio,
5339960aa7cSTomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_cio),
5349960aa7cSTomi Valkeinen 			DSI_CIO_IRQ_ERROR_MASK,
5359960aa7cSTomi Valkeinen 			DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
5369960aa7cSTomi Valkeinen }
5379960aa7cSTomi Valkeinen 
_dsi_initialize_irq(struct dsi_data * dsi)5387093d6cdSLaurent Pinchart static void _dsi_initialize_irq(struct dsi_data *dsi)
5399960aa7cSTomi Valkeinen {
5409960aa7cSTomi Valkeinen 	unsigned long flags;
5419960aa7cSTomi Valkeinen 	int vc;
5429960aa7cSTomi Valkeinen 
5439960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
5449960aa7cSTomi Valkeinen 
5459960aa7cSTomi Valkeinen 	memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables));
5469960aa7cSTomi Valkeinen 
5477093d6cdSLaurent Pinchart 	_omap_dsi_set_irqs(dsi);
5489960aa7cSTomi Valkeinen 	for (vc = 0; vc < 4; ++vc)
5497093d6cdSLaurent Pinchart 		_omap_dsi_set_irqs_vc(dsi, vc);
5507093d6cdSLaurent Pinchart 	_omap_dsi_set_irqs_cio(dsi);
5519960aa7cSTomi Valkeinen 
5529960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
5539960aa7cSTomi Valkeinen }
5549960aa7cSTomi Valkeinen 
_dsi_register_isr(omap_dsi_isr_t isr,void * arg,u32 mask,struct dsi_isr_data * isr_array,unsigned int isr_array_size)5559960aa7cSTomi Valkeinen static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
556d11e5c82SLaurent Pinchart 		struct dsi_isr_data *isr_array, unsigned int isr_array_size)
5579960aa7cSTomi Valkeinen {
5589960aa7cSTomi Valkeinen 	struct dsi_isr_data *isr_data;
5599960aa7cSTomi Valkeinen 	int free_idx;
5609960aa7cSTomi Valkeinen 	int i;
5619960aa7cSTomi Valkeinen 
5629960aa7cSTomi Valkeinen 	BUG_ON(isr == NULL);
5639960aa7cSTomi Valkeinen 
5649960aa7cSTomi Valkeinen 	/* check for duplicate entry and find a free slot */
5659960aa7cSTomi Valkeinen 	free_idx = -1;
5669960aa7cSTomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
5679960aa7cSTomi Valkeinen 		isr_data = &isr_array[i];
5689960aa7cSTomi Valkeinen 
5699960aa7cSTomi Valkeinen 		if (isr_data->isr == isr && isr_data->arg == arg &&
5709960aa7cSTomi Valkeinen 				isr_data->mask == mask) {
5719960aa7cSTomi Valkeinen 			return -EINVAL;
5729960aa7cSTomi Valkeinen 		}
5739960aa7cSTomi Valkeinen 
5749960aa7cSTomi Valkeinen 		if (isr_data->isr == NULL && free_idx == -1)
5759960aa7cSTomi Valkeinen 			free_idx = i;
5769960aa7cSTomi Valkeinen 	}
5779960aa7cSTomi Valkeinen 
5789960aa7cSTomi Valkeinen 	if (free_idx == -1)
5799960aa7cSTomi Valkeinen 		return -EBUSY;
5809960aa7cSTomi Valkeinen 
5819960aa7cSTomi Valkeinen 	isr_data = &isr_array[free_idx];
5829960aa7cSTomi Valkeinen 	isr_data->isr = isr;
5839960aa7cSTomi Valkeinen 	isr_data->arg = arg;
5849960aa7cSTomi Valkeinen 	isr_data->mask = mask;
5859960aa7cSTomi Valkeinen 
5869960aa7cSTomi Valkeinen 	return 0;
5879960aa7cSTomi Valkeinen }
5889960aa7cSTomi Valkeinen 
_dsi_unregister_isr(omap_dsi_isr_t isr,void * arg,u32 mask,struct dsi_isr_data * isr_array,unsigned int isr_array_size)5899960aa7cSTomi Valkeinen static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
590d11e5c82SLaurent Pinchart 		struct dsi_isr_data *isr_array, unsigned int isr_array_size)
5919960aa7cSTomi Valkeinen {
5929960aa7cSTomi Valkeinen 	struct dsi_isr_data *isr_data;
5939960aa7cSTomi Valkeinen 	int i;
5949960aa7cSTomi Valkeinen 
5959960aa7cSTomi Valkeinen 	for (i = 0; i < isr_array_size; i++) {
5969960aa7cSTomi Valkeinen 		isr_data = &isr_array[i];
5979960aa7cSTomi Valkeinen 		if (isr_data->isr != isr || isr_data->arg != arg ||
5989960aa7cSTomi Valkeinen 				isr_data->mask != mask)
5999960aa7cSTomi Valkeinen 			continue;
6009960aa7cSTomi Valkeinen 
6019960aa7cSTomi Valkeinen 		isr_data->isr = NULL;
6029960aa7cSTomi Valkeinen 		isr_data->arg = NULL;
6039960aa7cSTomi Valkeinen 		isr_data->mask = 0;
6049960aa7cSTomi Valkeinen 
6059960aa7cSTomi Valkeinen 		return 0;
6069960aa7cSTomi Valkeinen 	}
6079960aa7cSTomi Valkeinen 
6089960aa7cSTomi Valkeinen 	return -EINVAL;
6099960aa7cSTomi Valkeinen }
6109960aa7cSTomi Valkeinen 
dsi_register_isr(struct dsi_data * dsi,omap_dsi_isr_t isr,void * arg,u32 mask)6117093d6cdSLaurent Pinchart static int dsi_register_isr(struct dsi_data *dsi, omap_dsi_isr_t isr,
6129960aa7cSTomi Valkeinen 			    void *arg, u32 mask)
6139960aa7cSTomi Valkeinen {
6149960aa7cSTomi Valkeinen 	unsigned long flags;
6159960aa7cSTomi Valkeinen 	int r;
6169960aa7cSTomi Valkeinen 
6179960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
6189960aa7cSTomi Valkeinen 
6199960aa7cSTomi Valkeinen 	r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table,
6209960aa7cSTomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table));
6219960aa7cSTomi Valkeinen 
6229960aa7cSTomi Valkeinen 	if (r == 0)
6237093d6cdSLaurent Pinchart 		_omap_dsi_set_irqs(dsi);
6249960aa7cSTomi Valkeinen 
6259960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
6269960aa7cSTomi Valkeinen 
6279960aa7cSTomi Valkeinen 	return r;
6289960aa7cSTomi Valkeinen }
6299960aa7cSTomi Valkeinen 
dsi_unregister_isr(struct dsi_data * dsi,omap_dsi_isr_t isr,void * arg,u32 mask)6307093d6cdSLaurent Pinchart static int dsi_unregister_isr(struct dsi_data *dsi, omap_dsi_isr_t isr,
6317093d6cdSLaurent Pinchart 			      void *arg, u32 mask)
6329960aa7cSTomi Valkeinen {
6339960aa7cSTomi Valkeinen 	unsigned long flags;
6349960aa7cSTomi Valkeinen 	int r;
6359960aa7cSTomi Valkeinen 
6369960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
6379960aa7cSTomi Valkeinen 
6389960aa7cSTomi Valkeinen 	r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table,
6399960aa7cSTomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table));
6409960aa7cSTomi Valkeinen 
6419960aa7cSTomi Valkeinen 	if (r == 0)
6427093d6cdSLaurent Pinchart 		_omap_dsi_set_irqs(dsi);
6439960aa7cSTomi Valkeinen 
6449960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
6459960aa7cSTomi Valkeinen 
6469960aa7cSTomi Valkeinen 	return r;
6479960aa7cSTomi Valkeinen }
6489960aa7cSTomi Valkeinen 
dsi_register_isr_vc(struct dsi_data * dsi,int vc,omap_dsi_isr_t isr,void * arg,u32 mask)649d8171145STomi Valkeinen static int dsi_register_isr_vc(struct dsi_data *dsi, int vc,
6509960aa7cSTomi Valkeinen 			       omap_dsi_isr_t isr, void *arg, u32 mask)
6519960aa7cSTomi Valkeinen {
6529960aa7cSTomi Valkeinen 	unsigned long flags;
6539960aa7cSTomi Valkeinen 	int r;
6549960aa7cSTomi Valkeinen 
6559960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
6569960aa7cSTomi Valkeinen 
6579960aa7cSTomi Valkeinen 	r = _dsi_register_isr(isr, arg, mask,
658d8171145STomi Valkeinen 			dsi->isr_tables.isr_table_vc[vc],
659d8171145STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]));
6609960aa7cSTomi Valkeinen 
6619960aa7cSTomi Valkeinen 	if (r == 0)
662d8171145STomi Valkeinen 		_omap_dsi_set_irqs_vc(dsi, vc);
6639960aa7cSTomi Valkeinen 
6649960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
6659960aa7cSTomi Valkeinen 
6669960aa7cSTomi Valkeinen 	return r;
6679960aa7cSTomi Valkeinen }
6689960aa7cSTomi Valkeinen 
dsi_unregister_isr_vc(struct dsi_data * dsi,int vc,omap_dsi_isr_t isr,void * arg,u32 mask)669d8171145STomi Valkeinen static int dsi_unregister_isr_vc(struct dsi_data *dsi, int vc,
6709960aa7cSTomi Valkeinen 				 omap_dsi_isr_t isr, void *arg, u32 mask)
6719960aa7cSTomi Valkeinen {
6729960aa7cSTomi Valkeinen 	unsigned long flags;
6739960aa7cSTomi Valkeinen 	int r;
6749960aa7cSTomi Valkeinen 
6759960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->irq_lock, flags);
6769960aa7cSTomi Valkeinen 
6779960aa7cSTomi Valkeinen 	r = _dsi_unregister_isr(isr, arg, mask,
678d8171145STomi Valkeinen 			dsi->isr_tables.isr_table_vc[vc],
679d8171145STomi Valkeinen 			ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]));
6809960aa7cSTomi Valkeinen 
6819960aa7cSTomi Valkeinen 	if (r == 0)
682d8171145STomi Valkeinen 		_omap_dsi_set_irqs_vc(dsi, vc);
6839960aa7cSTomi Valkeinen 
6849960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_lock, flags);
6859960aa7cSTomi Valkeinen 
6869960aa7cSTomi Valkeinen 	return r;
6879960aa7cSTomi Valkeinen }
6889960aa7cSTomi Valkeinen 
dsi_get_errors(struct dsi_data * dsi)6897093d6cdSLaurent Pinchart static u32 dsi_get_errors(struct dsi_data *dsi)
6909960aa7cSTomi Valkeinen {
6919960aa7cSTomi Valkeinen 	unsigned long flags;
6929960aa7cSTomi Valkeinen 	u32 e;
6937093d6cdSLaurent Pinchart 
6949960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->errors_lock, flags);
6959960aa7cSTomi Valkeinen 	e = dsi->errors;
6969960aa7cSTomi Valkeinen 	dsi->errors = 0;
6979960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->errors_lock, flags);
6989960aa7cSTomi Valkeinen 	return e;
6999960aa7cSTomi Valkeinen }
7009960aa7cSTomi Valkeinen 
dsi_runtime_get(struct dsi_data * dsi)7017093d6cdSLaurent Pinchart static int dsi_runtime_get(struct dsi_data *dsi)
7029960aa7cSTomi Valkeinen {
7039960aa7cSTomi Valkeinen 	int r;
7049960aa7cSTomi Valkeinen 
7059960aa7cSTomi Valkeinen 	DSSDBG("dsi_runtime_get\n");
7069960aa7cSTomi Valkeinen 
7074600ea9cSLaurent Pinchart 	r = pm_runtime_get_sync(dsi->dev);
708a5d704d3SDinghao Liu 	if (WARN_ON(r < 0)) {
709a5d704d3SDinghao Liu 		pm_runtime_put_noidle(dsi->dev);
710a5d704d3SDinghao Liu 		return r;
711a5d704d3SDinghao Liu 	}
712a5d704d3SDinghao Liu 	return 0;
7139960aa7cSTomi Valkeinen }
7149960aa7cSTomi Valkeinen 
dsi_runtime_put(struct dsi_data * dsi)7157093d6cdSLaurent Pinchart static void dsi_runtime_put(struct dsi_data *dsi)
7169960aa7cSTomi Valkeinen {
7179960aa7cSTomi Valkeinen 	int r;
7189960aa7cSTomi Valkeinen 
7199960aa7cSTomi Valkeinen 	DSSDBG("dsi_runtime_put\n");
7209960aa7cSTomi Valkeinen 
7214600ea9cSLaurent Pinchart 	r = pm_runtime_put_sync(dsi->dev);
7229960aa7cSTomi Valkeinen 	WARN_ON(r < 0 && r != -ENOSYS);
7239960aa7cSTomi Valkeinen }
7249960aa7cSTomi Valkeinen 
_dsi_print_reset_status(struct dsi_data * dsi)7257093d6cdSLaurent Pinchart static void _dsi_print_reset_status(struct dsi_data *dsi)
7269960aa7cSTomi Valkeinen {
7279960aa7cSTomi Valkeinen 	int b0, b1, b2;
7289960aa7cSTomi Valkeinen 
7299960aa7cSTomi Valkeinen 	/* A dummy read using the SCP interface to any DSIPHY register is
7309960aa7cSTomi Valkeinen 	 * required after DSIPHY reset to complete the reset of the DSI complex
7319960aa7cSTomi Valkeinen 	 * I/O. */
732ae36fcccSLee Jones 	dsi_read_reg(dsi, DSI_DSIPHY_CFG5);
7339960aa7cSTomi Valkeinen 
73444d8ca10SLaurent Pinchart 	if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC) {
7359960aa7cSTomi Valkeinen 		b0 = 28;
7369960aa7cSTomi Valkeinen 		b1 = 27;
7379960aa7cSTomi Valkeinen 		b2 = 26;
7389960aa7cSTomi Valkeinen 	} else {
7399960aa7cSTomi Valkeinen 		b0 = 24;
7409960aa7cSTomi Valkeinen 		b1 = 25;
7419960aa7cSTomi Valkeinen 		b2 = 26;
7429960aa7cSTomi Valkeinen 	}
7439960aa7cSTomi Valkeinen 
7449960aa7cSTomi Valkeinen #define DSI_FLD_GET(fld, start, end)\
7457093d6cdSLaurent Pinchart 	FLD_GET(dsi_read_reg(dsi, DSI_##fld), start, end)
7469960aa7cSTomi Valkeinen 
7479960aa7cSTomi Valkeinen 	pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n",
7489960aa7cSTomi Valkeinen 		DSI_FLD_GET(PLL_STATUS, 0, 0),
7499960aa7cSTomi Valkeinen 		DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29),
7509960aa7cSTomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, b0, b0),
7519960aa7cSTomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, b1, b1),
7529960aa7cSTomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, b2, b2),
7539960aa7cSTomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, 29, 29),
7549960aa7cSTomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, 30, 30),
7559960aa7cSTomi Valkeinen 		DSI_FLD_GET(DSIPHY_CFG5, 31, 31));
7569960aa7cSTomi Valkeinen 
7579960aa7cSTomi Valkeinen #undef DSI_FLD_GET
7589960aa7cSTomi Valkeinen }
7599960aa7cSTomi Valkeinen 
dsi_if_enable(struct dsi_data * dsi,bool enable)7607093d6cdSLaurent Pinchart static inline int dsi_if_enable(struct dsi_data *dsi, bool enable)
7619960aa7cSTomi Valkeinen {
7629960aa7cSTomi Valkeinen 	DSSDBG("dsi_if_enable(%d)\n", enable);
7639960aa7cSTomi Valkeinen 
7649960aa7cSTomi Valkeinen 	enable = enable ? 1 : 0;
7657093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CTRL, enable, 0, 0); /* IF_EN */
7669960aa7cSTomi Valkeinen 
7677093d6cdSLaurent Pinchart 	if (!wait_for_bit_change(dsi, DSI_CTRL, 0, enable)) {
7689960aa7cSTomi Valkeinen 		DSSERR("Failed to set dsi_if_enable to %d\n", enable);
7699960aa7cSTomi Valkeinen 		return -EIO;
7709960aa7cSTomi Valkeinen 	}
7719960aa7cSTomi Valkeinen 
7729960aa7cSTomi Valkeinen 	return 0;
7739960aa7cSTomi Valkeinen }
7749960aa7cSTomi Valkeinen 
dsi_get_pll_hsdiv_dispc_rate(struct dsi_data * dsi)7757093d6cdSLaurent Pinchart static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct dsi_data *dsi)
7769960aa7cSTomi Valkeinen {
7779960aa7cSTomi Valkeinen 	return dsi->pll.cinfo.clkout[HSDIV_DISPC];
7789960aa7cSTomi Valkeinen }
7799960aa7cSTomi Valkeinen 
dsi_get_pll_hsdiv_dsi_rate(struct dsi_data * dsi)7807093d6cdSLaurent Pinchart static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct dsi_data *dsi)
7819960aa7cSTomi Valkeinen {
7829960aa7cSTomi Valkeinen 	return dsi->pll.cinfo.clkout[HSDIV_DSI];
7839960aa7cSTomi Valkeinen }
7849960aa7cSTomi Valkeinen 
dsi_get_txbyteclkhs(struct dsi_data * dsi)7857093d6cdSLaurent Pinchart static unsigned long dsi_get_txbyteclkhs(struct dsi_data *dsi)
7869960aa7cSTomi Valkeinen {
7879960aa7cSTomi Valkeinen 	return dsi->pll.cinfo.clkdco / 16;
7889960aa7cSTomi Valkeinen }
7899960aa7cSTomi Valkeinen 
dsi_fclk_rate(struct dsi_data * dsi)7907093d6cdSLaurent Pinchart static unsigned long dsi_fclk_rate(struct dsi_data *dsi)
7919960aa7cSTomi Valkeinen {
7929960aa7cSTomi Valkeinen 	unsigned long r;
7933cc62aadSLaurent Pinchart 	enum dss_clk_source source;
7949960aa7cSTomi Valkeinen 
7953cc62aadSLaurent Pinchart 	source = dss_get_dsi_clk_source(dsi->dss, dsi->module_id);
7963cc62aadSLaurent Pinchart 	if (source == DSS_CLK_SRC_FCK) {
7979960aa7cSTomi Valkeinen 		/* DSI FCLK source is DSS_CLK_FCK */
7989960aa7cSTomi Valkeinen 		r = clk_get_rate(dsi->dss_clk);
7999960aa7cSTomi Valkeinen 	} else {
8009960aa7cSTomi Valkeinen 		/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
8017093d6cdSLaurent Pinchart 		r = dsi_get_pll_hsdiv_dsi_rate(dsi);
8029960aa7cSTomi Valkeinen 	}
8039960aa7cSTomi Valkeinen 
8049960aa7cSTomi Valkeinen 	return r;
8059960aa7cSTomi Valkeinen }
8069960aa7cSTomi Valkeinen 
dsi_lp_clock_calc(unsigned long dsi_fclk,unsigned long lp_clk_min,unsigned long lp_clk_max,struct dsi_lp_clock_info * lp_cinfo)8079960aa7cSTomi Valkeinen static int dsi_lp_clock_calc(unsigned long dsi_fclk,
8089960aa7cSTomi Valkeinen 		unsigned long lp_clk_min, unsigned long lp_clk_max,
8099960aa7cSTomi Valkeinen 		struct dsi_lp_clock_info *lp_cinfo)
8109960aa7cSTomi Valkeinen {
811d11e5c82SLaurent Pinchart 	unsigned int lp_clk_div;
8129960aa7cSTomi Valkeinen 	unsigned long lp_clk;
8139960aa7cSTomi Valkeinen 
8149960aa7cSTomi Valkeinen 	lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2);
8159960aa7cSTomi Valkeinen 	lp_clk = dsi_fclk / 2 / lp_clk_div;
8169960aa7cSTomi Valkeinen 
8179960aa7cSTomi Valkeinen 	if (lp_clk < lp_clk_min || lp_clk > lp_clk_max)
8189960aa7cSTomi Valkeinen 		return -EINVAL;
8199960aa7cSTomi Valkeinen 
8209960aa7cSTomi Valkeinen 	lp_cinfo->lp_clk_div = lp_clk_div;
8219960aa7cSTomi Valkeinen 	lp_cinfo->lp_clk = lp_clk;
8229960aa7cSTomi Valkeinen 
8239960aa7cSTomi Valkeinen 	return 0;
8249960aa7cSTomi Valkeinen }
8259960aa7cSTomi Valkeinen 
dsi_set_lp_clk_divisor(struct dsi_data * dsi)8267093d6cdSLaurent Pinchart static int dsi_set_lp_clk_divisor(struct dsi_data *dsi)
8279960aa7cSTomi Valkeinen {
8289960aa7cSTomi Valkeinen 	unsigned long dsi_fclk;
829d11e5c82SLaurent Pinchart 	unsigned int lp_clk_div;
8309960aa7cSTomi Valkeinen 	unsigned long lp_clk;
831d11e5c82SLaurent Pinchart 	unsigned int lpdiv_max = dsi->data->max_pll_lpdiv;
8329960aa7cSTomi Valkeinen 
8339960aa7cSTomi Valkeinen 
8349960aa7cSTomi Valkeinen 	lp_clk_div = dsi->user_lp_cinfo.lp_clk_div;
8359960aa7cSTomi Valkeinen 
8369960aa7cSTomi Valkeinen 	if (lp_clk_div == 0 || lp_clk_div > lpdiv_max)
8379960aa7cSTomi Valkeinen 		return -EINVAL;
8389960aa7cSTomi Valkeinen 
8397093d6cdSLaurent Pinchart 	dsi_fclk = dsi_fclk_rate(dsi);
8409960aa7cSTomi Valkeinen 
8419960aa7cSTomi Valkeinen 	lp_clk = dsi_fclk / 2 / lp_clk_div;
8429960aa7cSTomi Valkeinen 
8439960aa7cSTomi Valkeinen 	DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
8449960aa7cSTomi Valkeinen 	dsi->current_lp_cinfo.lp_clk = lp_clk;
8459960aa7cSTomi Valkeinen 	dsi->current_lp_cinfo.lp_clk_div = lp_clk_div;
8469960aa7cSTomi Valkeinen 
8479960aa7cSTomi Valkeinen 	/* LP_CLK_DIVISOR */
8487093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL, lp_clk_div, 12, 0);
8499960aa7cSTomi Valkeinen 
8509960aa7cSTomi Valkeinen 	/* LP_RX_SYNCHRO_ENABLE */
8517093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21);
8529960aa7cSTomi Valkeinen 
8539960aa7cSTomi Valkeinen 	return 0;
8549960aa7cSTomi Valkeinen }
8559960aa7cSTomi Valkeinen 
dsi_enable_scp_clk(struct dsi_data * dsi)8567093d6cdSLaurent Pinchart static void dsi_enable_scp_clk(struct dsi_data *dsi)
8579960aa7cSTomi Valkeinen {
8589960aa7cSTomi Valkeinen 	if (dsi->scp_clk_refcount++ == 0)
8597093d6cdSLaurent Pinchart 		REG_FLD_MOD(dsi, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */
8609960aa7cSTomi Valkeinen }
8619960aa7cSTomi Valkeinen 
dsi_disable_scp_clk(struct dsi_data * dsi)8627093d6cdSLaurent Pinchart static void dsi_disable_scp_clk(struct dsi_data *dsi)
8639960aa7cSTomi Valkeinen {
8649960aa7cSTomi Valkeinen 	WARN_ON(dsi->scp_clk_refcount == 0);
8659960aa7cSTomi Valkeinen 	if (--dsi->scp_clk_refcount == 0)
8667093d6cdSLaurent Pinchart 		REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */
8679960aa7cSTomi Valkeinen }
8689960aa7cSTomi Valkeinen 
8699960aa7cSTomi Valkeinen enum dsi_pll_power_state {
8709960aa7cSTomi Valkeinen 	DSI_PLL_POWER_OFF	= 0x0,
8719960aa7cSTomi Valkeinen 	DSI_PLL_POWER_ON_HSCLK	= 0x1,
8729960aa7cSTomi Valkeinen 	DSI_PLL_POWER_ON_ALL	= 0x2,
8739960aa7cSTomi Valkeinen 	DSI_PLL_POWER_ON_DIV	= 0x3,
8749960aa7cSTomi Valkeinen };
8759960aa7cSTomi Valkeinen 
dsi_pll_power(struct dsi_data * dsi,enum dsi_pll_power_state state)8767093d6cdSLaurent Pinchart static int dsi_pll_power(struct dsi_data *dsi, enum dsi_pll_power_state state)
8779960aa7cSTomi Valkeinen {
8789960aa7cSTomi Valkeinen 	int t = 0;
8799960aa7cSTomi Valkeinen 
8809960aa7cSTomi Valkeinen 	/* DSI-PLL power command 0x3 is not working */
88144d8ca10SLaurent Pinchart 	if ((dsi->data->quirks & DSI_QUIRK_PLL_PWR_BUG) &&
8829960aa7cSTomi Valkeinen 	    state == DSI_PLL_POWER_ON_DIV)
8839960aa7cSTomi Valkeinen 		state = DSI_PLL_POWER_ON_ALL;
8849960aa7cSTomi Valkeinen 
8859960aa7cSTomi Valkeinen 	/* PLL_PWR_CMD */
8867093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL, state, 31, 30);
8879960aa7cSTomi Valkeinen 
8889960aa7cSTomi Valkeinen 	/* PLL_PWR_STATUS */
8897093d6cdSLaurent Pinchart 	while (FLD_GET(dsi_read_reg(dsi, DSI_CLK_CTRL), 29, 28) != state) {
8909960aa7cSTomi Valkeinen 		if (++t > 1000) {
8919960aa7cSTomi Valkeinen 			DSSERR("Failed to set DSI PLL power mode to %d\n",
8929960aa7cSTomi Valkeinen 					state);
8939960aa7cSTomi Valkeinen 			return -ENODEV;
8949960aa7cSTomi Valkeinen 		}
8959960aa7cSTomi Valkeinen 		udelay(1);
8969960aa7cSTomi Valkeinen 	}
8979960aa7cSTomi Valkeinen 
8989960aa7cSTomi Valkeinen 	return 0;
8999960aa7cSTomi Valkeinen }
9009960aa7cSTomi Valkeinen 
9019960aa7cSTomi Valkeinen 
dsi_pll_calc_dsi_fck(struct dsi_data * dsi,struct dss_pll_clock_info * cinfo)902fe9964cbSLaurent Pinchart static void dsi_pll_calc_dsi_fck(struct dsi_data *dsi,
903fe9964cbSLaurent Pinchart 				 struct dss_pll_clock_info *cinfo)
9049960aa7cSTomi Valkeinen {
9059960aa7cSTomi Valkeinen 	unsigned long max_dsi_fck;
9069960aa7cSTomi Valkeinen 
907fe9964cbSLaurent Pinchart 	max_dsi_fck = dsi->data->max_fck_freq;
9089960aa7cSTomi Valkeinen 
9099960aa7cSTomi Valkeinen 	cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck);
9109960aa7cSTomi Valkeinen 	cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI];
9119960aa7cSTomi Valkeinen }
9129960aa7cSTomi Valkeinen 
dsi_pll_enable(struct dss_pll * pll)9139960aa7cSTomi Valkeinen static int dsi_pll_enable(struct dss_pll *pll)
9149960aa7cSTomi Valkeinen {
9159960aa7cSTomi Valkeinen 	struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
9169960aa7cSTomi Valkeinen 	int r = 0;
9179960aa7cSTomi Valkeinen 
9189960aa7cSTomi Valkeinen 	DSSDBG("PLL init\n");
9199960aa7cSTomi Valkeinen 
9207093d6cdSLaurent Pinchart 	r = dsi_runtime_get(dsi);
9219960aa7cSTomi Valkeinen 	if (r)
9229960aa7cSTomi Valkeinen 		return r;
9239960aa7cSTomi Valkeinen 
9249960aa7cSTomi Valkeinen 	/*
9259960aa7cSTomi Valkeinen 	 * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
9269960aa7cSTomi Valkeinen 	 */
9277093d6cdSLaurent Pinchart 	dsi_enable_scp_clk(dsi);
9289960aa7cSTomi Valkeinen 
9299960aa7cSTomi Valkeinen 	r = regulator_enable(dsi->vdds_dsi_reg);
9309960aa7cSTomi Valkeinen 	if (r)
9319960aa7cSTomi Valkeinen 		goto err0;
9329960aa7cSTomi Valkeinen 
9339960aa7cSTomi Valkeinen 	/* XXX PLL does not come out of reset without this... */
9348a7eda76SLaurent Pinchart 	dispc_pck_free_enable(dsi->dss->dispc, 1);
9359960aa7cSTomi Valkeinen 
9367093d6cdSLaurent Pinchart 	if (!wait_for_bit_change(dsi, DSI_PLL_STATUS, 0, 1)) {
9379960aa7cSTomi Valkeinen 		DSSERR("PLL not coming out of reset.\n");
9389960aa7cSTomi Valkeinen 		r = -ENODEV;
9398a7eda76SLaurent Pinchart 		dispc_pck_free_enable(dsi->dss->dispc, 0);
9409960aa7cSTomi Valkeinen 		goto err1;
9419960aa7cSTomi Valkeinen 	}
9429960aa7cSTomi Valkeinen 
9439960aa7cSTomi Valkeinen 	/* XXX ... but if left on, we get problems when planes do not
9449960aa7cSTomi Valkeinen 	 * fill the whole display. No idea about this */
9458a7eda76SLaurent Pinchart 	dispc_pck_free_enable(dsi->dss->dispc, 0);
9469960aa7cSTomi Valkeinen 
9477093d6cdSLaurent Pinchart 	r = dsi_pll_power(dsi, DSI_PLL_POWER_ON_ALL);
9489960aa7cSTomi Valkeinen 
9499960aa7cSTomi Valkeinen 	if (r)
9509960aa7cSTomi Valkeinen 		goto err1;
9519960aa7cSTomi Valkeinen 
9529960aa7cSTomi Valkeinen 	DSSDBG("PLL init done\n");
9539960aa7cSTomi Valkeinen 
9549960aa7cSTomi Valkeinen 	return 0;
9559960aa7cSTomi Valkeinen err1:
9569960aa7cSTomi Valkeinen 	regulator_disable(dsi->vdds_dsi_reg);
9579960aa7cSTomi Valkeinen err0:
9587093d6cdSLaurent Pinchart 	dsi_disable_scp_clk(dsi);
9597093d6cdSLaurent Pinchart 	dsi_runtime_put(dsi);
9609960aa7cSTomi Valkeinen 	return r;
9619960aa7cSTomi Valkeinen }
9629960aa7cSTomi Valkeinen 
dsi_pll_disable(struct dss_pll * pll)9639960aa7cSTomi Valkeinen static void dsi_pll_disable(struct dss_pll *pll)
9649960aa7cSTomi Valkeinen {
9659960aa7cSTomi Valkeinen 	struct dsi_data *dsi = container_of(pll, struct dsi_data, pll);
9669960aa7cSTomi Valkeinen 
967fe4ed1b4STony Lindgren 	dsi_pll_power(dsi, DSI_PLL_POWER_OFF);
968fe4ed1b4STony Lindgren 
969fe4ed1b4STony Lindgren 	regulator_disable(dsi->vdds_dsi_reg);
970fe4ed1b4STony Lindgren 
971fe4ed1b4STony Lindgren 	dsi_disable_scp_clk(dsi);
972fe4ed1b4STony Lindgren 	dsi_runtime_put(dsi);
973fe4ed1b4STony Lindgren 
974fe4ed1b4STony Lindgren 	DSSDBG("PLL disable done\n");
9759960aa7cSTomi Valkeinen }
9769960aa7cSTomi Valkeinen 
dsi_dump_dsi_clocks(struct seq_file * s,void * p)9773ce75d67SLaurent Pinchart static int dsi_dump_dsi_clocks(struct seq_file *s, void *p)
9789960aa7cSTomi Valkeinen {
9794df04ac9STomi Valkeinen 	struct dsi_data *dsi = s->private;
9809960aa7cSTomi Valkeinen 	struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo;
981dc0352d1STomi Valkeinen 	enum dss_clk_source dispc_clk_src, dsi_clk_src;
9829960aa7cSTomi Valkeinen 	int dsi_module = dsi->module_id;
9839960aa7cSTomi Valkeinen 	struct dss_pll *pll = &dsi->pll;
9849960aa7cSTomi Valkeinen 
9853cc62aadSLaurent Pinchart 	dispc_clk_src = dss_get_dispc_clk_source(dsi->dss);
9863cc62aadSLaurent Pinchart 	dsi_clk_src = dss_get_dsi_clk_source(dsi->dss, dsi_module);
9879960aa7cSTomi Valkeinen 
9887093d6cdSLaurent Pinchart 	if (dsi_runtime_get(dsi))
9893ce75d67SLaurent Pinchart 		return 0;
9909960aa7cSTomi Valkeinen 
9919960aa7cSTomi Valkeinen 	seq_printf(s,	"- DSI%d PLL -\n", dsi_module + 1);
9929960aa7cSTomi Valkeinen 
9939960aa7cSTomi Valkeinen 	seq_printf(s,	"dsi pll clkin\t%lu\n", clk_get_rate(pll->clkin));
9949960aa7cSTomi Valkeinen 
9959960aa7cSTomi Valkeinen 	seq_printf(s,	"Fint\t\t%-16lun %u\n", cinfo->fint, cinfo->n);
9969960aa7cSTomi Valkeinen 
9979960aa7cSTomi Valkeinen 	seq_printf(s,	"CLKIN4DDR\t%-16lum %u\n",
9989960aa7cSTomi Valkeinen 			cinfo->clkdco, cinfo->m);
9999960aa7cSTomi Valkeinen 
10009960aa7cSTomi Valkeinen 	seq_printf(s,	"DSI_PLL_HSDIV_DISPC (%s)\t%-16lum_dispc %u\t(%s)\n",
1001407bd564STomi Valkeinen 			dss_get_clk_source_name(dsi_module == 0 ?
10023b63ca75STomi Valkeinen 				DSS_CLK_SRC_PLL1_1 :
10033b63ca75STomi Valkeinen 				DSS_CLK_SRC_PLL2_1),
10049960aa7cSTomi Valkeinen 			cinfo->clkout[HSDIV_DISPC],
10059960aa7cSTomi Valkeinen 			cinfo->mX[HSDIV_DISPC],
10063b63ca75STomi Valkeinen 			dispc_clk_src == DSS_CLK_SRC_FCK ?
10079960aa7cSTomi Valkeinen 			"off" : "on");
10089960aa7cSTomi Valkeinen 
10099960aa7cSTomi Valkeinen 	seq_printf(s,	"DSI_PLL_HSDIV_DSI (%s)\t%-16lum_dsi %u\t(%s)\n",
1010407bd564STomi Valkeinen 			dss_get_clk_source_name(dsi_module == 0 ?
10113b63ca75STomi Valkeinen 				DSS_CLK_SRC_PLL1_2 :
10123b63ca75STomi Valkeinen 				DSS_CLK_SRC_PLL2_2),
10139960aa7cSTomi Valkeinen 			cinfo->clkout[HSDIV_DSI],
10149960aa7cSTomi Valkeinen 			cinfo->mX[HSDIV_DSI],
10153b63ca75STomi Valkeinen 			dsi_clk_src == DSS_CLK_SRC_FCK ?
10169960aa7cSTomi Valkeinen 			"off" : "on");
10179960aa7cSTomi Valkeinen 
10189960aa7cSTomi Valkeinen 	seq_printf(s,	"- DSI%d -\n", dsi_module + 1);
10199960aa7cSTomi Valkeinen 
1020557a1544STomi Valkeinen 	seq_printf(s,	"dsi fclk source = %s\n",
1021407bd564STomi Valkeinen 			dss_get_clk_source_name(dsi_clk_src));
10229960aa7cSTomi Valkeinen 
10237093d6cdSLaurent Pinchart 	seq_printf(s,	"DSI_FCLK\t%lu\n", dsi_fclk_rate(dsi));
10249960aa7cSTomi Valkeinen 
10259960aa7cSTomi Valkeinen 	seq_printf(s,	"DDR_CLK\t\t%lu\n",
10269960aa7cSTomi Valkeinen 			cinfo->clkdco / 4);
10279960aa7cSTomi Valkeinen 
10287093d6cdSLaurent Pinchart 	seq_printf(s,	"TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsi));
10299960aa7cSTomi Valkeinen 
10309960aa7cSTomi Valkeinen 	seq_printf(s,	"LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk);
10319960aa7cSTomi Valkeinen 
10327093d6cdSLaurent Pinchart 	dsi_runtime_put(dsi);
10339960aa7cSTomi Valkeinen 
10343ce75d67SLaurent Pinchart 	return 0;
10359960aa7cSTomi Valkeinen }
10369960aa7cSTomi Valkeinen 
10379960aa7cSTomi Valkeinen #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
dsi_dump_dsi_irqs(struct seq_file * s,void * p)1038f3ed97f9SLaurent Pinchart static int dsi_dump_dsi_irqs(struct seq_file *s, void *p)
10399960aa7cSTomi Valkeinen {
10404df04ac9STomi Valkeinen 	struct dsi_data *dsi = s->private;
10419960aa7cSTomi Valkeinen 	unsigned long flags;
1042cfca7897STomi Valkeinen 	struct dsi_irq_stats *stats;
1043cfca7897STomi Valkeinen 
1044cfca7897STomi Valkeinen 	stats = kmalloc(sizeof(*stats), GFP_KERNEL);
1045cfca7897STomi Valkeinen 	if (!stats)
1046cfca7897STomi Valkeinen 		return -ENOMEM;
10479960aa7cSTomi Valkeinen 
10489960aa7cSTomi Valkeinen 	spin_lock_irqsave(&dsi->irq_stats_lock, flags);
10499960aa7cSTomi Valkeinen 
1050cfca7897STomi Valkeinen 	*stats = dsi->irq_stats;
10519960aa7cSTomi Valkeinen 	memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
10529960aa7cSTomi Valkeinen 	dsi->irq_stats.last_reset = jiffies;
10539960aa7cSTomi Valkeinen 
10549960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
10559960aa7cSTomi Valkeinen 
10569960aa7cSTomi Valkeinen 	seq_printf(s, "period %u ms\n",
1057cfca7897STomi Valkeinen 			jiffies_to_msecs(jiffies - stats->last_reset));
10589960aa7cSTomi Valkeinen 
1059cfca7897STomi Valkeinen 	seq_printf(s, "irqs %d\n", stats->irq_count);
10609960aa7cSTomi Valkeinen #define PIS(x) \
1061cfca7897STomi Valkeinen 	seq_printf(s, "%-20s %10d\n", #x, stats->dsi_irqs[ffs(DSI_IRQ_##x)-1]);
10629960aa7cSTomi Valkeinen 
10639960aa7cSTomi Valkeinen 	seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1);
10649960aa7cSTomi Valkeinen 	PIS(VC0);
10659960aa7cSTomi Valkeinen 	PIS(VC1);
10669960aa7cSTomi Valkeinen 	PIS(VC2);
10679960aa7cSTomi Valkeinen 	PIS(VC3);
10689960aa7cSTomi Valkeinen 	PIS(WAKEUP);
10699960aa7cSTomi Valkeinen 	PIS(RESYNC);
10709960aa7cSTomi Valkeinen 	PIS(PLL_LOCK);
10719960aa7cSTomi Valkeinen 	PIS(PLL_UNLOCK);
10729960aa7cSTomi Valkeinen 	PIS(PLL_RECALL);
10739960aa7cSTomi Valkeinen 	PIS(COMPLEXIO_ERR);
10749960aa7cSTomi Valkeinen 	PIS(HS_TX_TIMEOUT);
10759960aa7cSTomi Valkeinen 	PIS(LP_RX_TIMEOUT);
10769960aa7cSTomi Valkeinen 	PIS(TE_TRIGGER);
10779960aa7cSTomi Valkeinen 	PIS(ACK_TRIGGER);
10789960aa7cSTomi Valkeinen 	PIS(SYNC_LOST);
10799960aa7cSTomi Valkeinen 	PIS(LDO_POWER_GOOD);
10809960aa7cSTomi Valkeinen 	PIS(TA_TIMEOUT);
10819960aa7cSTomi Valkeinen #undef PIS
10829960aa7cSTomi Valkeinen 
10839960aa7cSTomi Valkeinen #define PIS(x) \
10849960aa7cSTomi Valkeinen 	seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
1085cfca7897STomi Valkeinen 			stats->vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
1086cfca7897STomi Valkeinen 			stats->vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
1087cfca7897STomi Valkeinen 			stats->vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
1088cfca7897STomi Valkeinen 			stats->vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
10899960aa7cSTomi Valkeinen 
10909960aa7cSTomi Valkeinen 	seq_printf(s, "-- VC interrupts --\n");
10919960aa7cSTomi Valkeinen 	PIS(CS);
10929960aa7cSTomi Valkeinen 	PIS(ECC_CORR);
10939960aa7cSTomi Valkeinen 	PIS(PACKET_SENT);
10949960aa7cSTomi Valkeinen 	PIS(FIFO_TX_OVF);
10959960aa7cSTomi Valkeinen 	PIS(FIFO_RX_OVF);
10969960aa7cSTomi Valkeinen 	PIS(BTA);
10979960aa7cSTomi Valkeinen 	PIS(ECC_NO_CORR);
10989960aa7cSTomi Valkeinen 	PIS(FIFO_TX_UDF);
10999960aa7cSTomi Valkeinen 	PIS(PP_BUSY_CHANGE);
11009960aa7cSTomi Valkeinen #undef PIS
11019960aa7cSTomi Valkeinen 
11029960aa7cSTomi Valkeinen #define PIS(x) \
11039960aa7cSTomi Valkeinen 	seq_printf(s, "%-20s %10d\n", #x, \
1104cfca7897STomi Valkeinen 			stats->cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
11059960aa7cSTomi Valkeinen 
11069960aa7cSTomi Valkeinen 	seq_printf(s, "-- CIO interrupts --\n");
11079960aa7cSTomi Valkeinen 	PIS(ERRSYNCESC1);
11089960aa7cSTomi Valkeinen 	PIS(ERRSYNCESC2);
11099960aa7cSTomi Valkeinen 	PIS(ERRSYNCESC3);
11109960aa7cSTomi Valkeinen 	PIS(ERRESC1);
11119960aa7cSTomi Valkeinen 	PIS(ERRESC2);
11129960aa7cSTomi Valkeinen 	PIS(ERRESC3);
11139960aa7cSTomi Valkeinen 	PIS(ERRCONTROL1);
11149960aa7cSTomi Valkeinen 	PIS(ERRCONTROL2);
11159960aa7cSTomi Valkeinen 	PIS(ERRCONTROL3);
11169960aa7cSTomi Valkeinen 	PIS(STATEULPS1);
11179960aa7cSTomi Valkeinen 	PIS(STATEULPS2);
11189960aa7cSTomi Valkeinen 	PIS(STATEULPS3);
11199960aa7cSTomi Valkeinen 	PIS(ERRCONTENTIONLP0_1);
11209960aa7cSTomi Valkeinen 	PIS(ERRCONTENTIONLP1_1);
11219960aa7cSTomi Valkeinen 	PIS(ERRCONTENTIONLP0_2);
11229960aa7cSTomi Valkeinen 	PIS(ERRCONTENTIONLP1_2);
11239960aa7cSTomi Valkeinen 	PIS(ERRCONTENTIONLP0_3);
11249960aa7cSTomi Valkeinen 	PIS(ERRCONTENTIONLP1_3);
11259960aa7cSTomi Valkeinen 	PIS(ULPSACTIVENOT_ALL0);
11269960aa7cSTomi Valkeinen 	PIS(ULPSACTIVENOT_ALL1);
11279960aa7cSTomi Valkeinen #undef PIS
11289960aa7cSTomi Valkeinen 
1129cfca7897STomi Valkeinen 	kfree(stats);
1130cfca7897STomi Valkeinen 
1131f33656e1SLaurent Pinchart 	return 0;
11329960aa7cSTomi Valkeinen }
11339960aa7cSTomi Valkeinen #endif
11349960aa7cSTomi Valkeinen 
dsi_dump_dsi_regs(struct seq_file * s,void * p)1135f3ed97f9SLaurent Pinchart static int dsi_dump_dsi_regs(struct seq_file *s, void *p)
11369960aa7cSTomi Valkeinen {
11374df04ac9STomi Valkeinen 	struct dsi_data *dsi = s->private;
11389960aa7cSTomi Valkeinen 
11397093d6cdSLaurent Pinchart 	if (dsi_runtime_get(dsi))
1140f3ed97f9SLaurent Pinchart 		return 0;
11417093d6cdSLaurent Pinchart 	dsi_enable_scp_clk(dsi);
11429960aa7cSTomi Valkeinen 
1143f3ed97f9SLaurent Pinchart #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsi, r))
11449960aa7cSTomi Valkeinen 	DUMPREG(DSI_REVISION);
11459960aa7cSTomi Valkeinen 	DUMPREG(DSI_SYSCONFIG);
11469960aa7cSTomi Valkeinen 	DUMPREG(DSI_SYSSTATUS);
11479960aa7cSTomi Valkeinen 	DUMPREG(DSI_IRQSTATUS);
11489960aa7cSTomi Valkeinen 	DUMPREG(DSI_IRQENABLE);
11499960aa7cSTomi Valkeinen 	DUMPREG(DSI_CTRL);
11509960aa7cSTomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_CFG1);
11519960aa7cSTomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
11529960aa7cSTomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
11539960aa7cSTomi Valkeinen 	DUMPREG(DSI_CLK_CTRL);
11549960aa7cSTomi Valkeinen 	DUMPREG(DSI_TIMING1);
11559960aa7cSTomi Valkeinen 	DUMPREG(DSI_TIMING2);
11569960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING1);
11579960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING2);
11589960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING3);
11599960aa7cSTomi Valkeinen 	DUMPREG(DSI_CLK_TIMING);
11609960aa7cSTomi Valkeinen 	DUMPREG(DSI_TX_FIFO_VC_SIZE);
11619960aa7cSTomi Valkeinen 	DUMPREG(DSI_RX_FIFO_VC_SIZE);
11629960aa7cSTomi Valkeinen 	DUMPREG(DSI_COMPLEXIO_CFG2);
11639960aa7cSTomi Valkeinen 	DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
11649960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING4);
11659960aa7cSTomi Valkeinen 	DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
11669960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING5);
11679960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING6);
11689960aa7cSTomi Valkeinen 	DUMPREG(DSI_VM_TIMING7);
11699960aa7cSTomi Valkeinen 	DUMPREG(DSI_STOPCLK_TIMING);
11709960aa7cSTomi Valkeinen 
11719960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_CTRL(0));
11729960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_TE(0));
11739960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
11749960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
11759960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
11769960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(0));
11779960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(0));
11789960aa7cSTomi Valkeinen 
11799960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_CTRL(1));
11809960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_TE(1));
11819960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
11829960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
11839960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
11849960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(1));
11859960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(1));
11869960aa7cSTomi Valkeinen 
11879960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_CTRL(2));
11889960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_TE(2));
11899960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
11909960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
11919960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
11929960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(2));
11939960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(2));
11949960aa7cSTomi Valkeinen 
11959960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_CTRL(3));
11969960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_TE(3));
11979960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
11989960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
11999960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
12009960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQSTATUS(3));
12019960aa7cSTomi Valkeinen 	DUMPREG(DSI_VC_IRQENABLE(3));
12029960aa7cSTomi Valkeinen 
12039960aa7cSTomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG0);
12049960aa7cSTomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG1);
12059960aa7cSTomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG2);
12069960aa7cSTomi Valkeinen 	DUMPREG(DSI_DSIPHY_CFG5);
12079960aa7cSTomi Valkeinen 
12089960aa7cSTomi Valkeinen 	DUMPREG(DSI_PLL_CONTROL);
12099960aa7cSTomi Valkeinen 	DUMPREG(DSI_PLL_STATUS);
12109960aa7cSTomi Valkeinen 	DUMPREG(DSI_PLL_GO);
12119960aa7cSTomi Valkeinen 	DUMPREG(DSI_PLL_CONFIGURATION1);
12129960aa7cSTomi Valkeinen 	DUMPREG(DSI_PLL_CONFIGURATION2);
1213f3ed97f9SLaurent Pinchart #undef DUMPREG
12149960aa7cSTomi Valkeinen 
12157093d6cdSLaurent Pinchart 	dsi_disable_scp_clk(dsi);
12167093d6cdSLaurent Pinchart 	dsi_runtime_put(dsi);
12179960aa7cSTomi Valkeinen 
1218f33656e1SLaurent Pinchart 	return 0;
12199960aa7cSTomi Valkeinen }
12209960aa7cSTomi Valkeinen 
12219960aa7cSTomi Valkeinen enum dsi_cio_power_state {
12229960aa7cSTomi Valkeinen 	DSI_COMPLEXIO_POWER_OFF		= 0x0,
12239960aa7cSTomi Valkeinen 	DSI_COMPLEXIO_POWER_ON		= 0x1,
12249960aa7cSTomi Valkeinen 	DSI_COMPLEXIO_POWER_ULPS	= 0x2,
12259960aa7cSTomi Valkeinen };
12269960aa7cSTomi Valkeinen 
dsi_cio_power(struct dsi_data * dsi,enum dsi_cio_power_state state)12277093d6cdSLaurent Pinchart static int dsi_cio_power(struct dsi_data *dsi, enum dsi_cio_power_state state)
12289960aa7cSTomi Valkeinen {
12299960aa7cSTomi Valkeinen 	int t = 0;
12309960aa7cSTomi Valkeinen 
12319960aa7cSTomi Valkeinen 	/* PWR_CMD */
12327093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_COMPLEXIO_CFG1, state, 28, 27);
12339960aa7cSTomi Valkeinen 
12349960aa7cSTomi Valkeinen 	/* PWR_STATUS */
12357093d6cdSLaurent Pinchart 	while (FLD_GET(dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1),
12369960aa7cSTomi Valkeinen 			26, 25) != state) {
12379960aa7cSTomi Valkeinen 		if (++t > 1000) {
12389960aa7cSTomi Valkeinen 			DSSERR("failed to set complexio power state to "
12399960aa7cSTomi Valkeinen 					"%d\n", state);
12409960aa7cSTomi Valkeinen 			return -ENODEV;
12419960aa7cSTomi Valkeinen 		}
12429960aa7cSTomi Valkeinen 		udelay(1);
12439960aa7cSTomi Valkeinen 	}
12449960aa7cSTomi Valkeinen 
12459960aa7cSTomi Valkeinen 	return 0;
12469960aa7cSTomi Valkeinen }
12479960aa7cSTomi Valkeinen 
dsi_get_line_buf_size(struct dsi_data * dsi)12487093d6cdSLaurent Pinchart static unsigned int dsi_get_line_buf_size(struct dsi_data *dsi)
12499960aa7cSTomi Valkeinen {
12509960aa7cSTomi Valkeinen 	int val;
12519960aa7cSTomi Valkeinen 
12529960aa7cSTomi Valkeinen 	/* line buffer on OMAP3 is 1024 x 24bits */
12539960aa7cSTomi Valkeinen 	/* XXX: for some reason using full buffer size causes
12549960aa7cSTomi Valkeinen 	 * considerable TX slowdown with update sizes that fill the
12559960aa7cSTomi Valkeinen 	 * whole buffer */
125644d8ca10SLaurent Pinchart 	if (!(dsi->data->quirks & DSI_QUIRK_GNQ))
12579960aa7cSTomi Valkeinen 		return 1023 * 3;
12589960aa7cSTomi Valkeinen 
12597093d6cdSLaurent Pinchart 	val = REG_GET(dsi, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */
12609960aa7cSTomi Valkeinen 
12619960aa7cSTomi Valkeinen 	switch (val) {
12629960aa7cSTomi Valkeinen 	case 1:
12639960aa7cSTomi Valkeinen 		return 512 * 3;		/* 512x24 bits */
12649960aa7cSTomi Valkeinen 	case 2:
12659960aa7cSTomi Valkeinen 		return 682 * 3;		/* 682x24 bits */
12669960aa7cSTomi Valkeinen 	case 3:
12679960aa7cSTomi Valkeinen 		return 853 * 3;		/* 853x24 bits */
12689960aa7cSTomi Valkeinen 	case 4:
12699960aa7cSTomi Valkeinen 		return 1024 * 3;	/* 1024x24 bits */
12709960aa7cSTomi Valkeinen 	case 5:
12719960aa7cSTomi Valkeinen 		return 1194 * 3;	/* 1194x24 bits */
12729960aa7cSTomi Valkeinen 	case 6:
12739960aa7cSTomi Valkeinen 		return 1365 * 3;	/* 1365x24 bits */
12749960aa7cSTomi Valkeinen 	case 7:
12759960aa7cSTomi Valkeinen 		return 1920 * 3;	/* 1920x24 bits */
12769960aa7cSTomi Valkeinen 	default:
12779960aa7cSTomi Valkeinen 		BUG();
12789960aa7cSTomi Valkeinen 		return 0;
12799960aa7cSTomi Valkeinen 	}
12809960aa7cSTomi Valkeinen }
12819960aa7cSTomi Valkeinen 
dsi_set_lane_config(struct dsi_data * dsi)12827093d6cdSLaurent Pinchart static int dsi_set_lane_config(struct dsi_data *dsi)
12839960aa7cSTomi Valkeinen {
12849960aa7cSTomi Valkeinen 	static const u8 offsets[] = { 0, 4, 8, 12, 16 };
12859960aa7cSTomi Valkeinen 	static const enum dsi_lane_function functions[] = {
12869960aa7cSTomi Valkeinen 		DSI_LANE_CLK,
12879960aa7cSTomi Valkeinen 		DSI_LANE_DATA1,
12889960aa7cSTomi Valkeinen 		DSI_LANE_DATA2,
12899960aa7cSTomi Valkeinen 		DSI_LANE_DATA3,
12909960aa7cSTomi Valkeinen 		DSI_LANE_DATA4,
12919960aa7cSTomi Valkeinen 	};
12929960aa7cSTomi Valkeinen 	u32 r;
12939960aa7cSTomi Valkeinen 	int i;
12949960aa7cSTomi Valkeinen 
12957093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1);
12969960aa7cSTomi Valkeinen 
12979960aa7cSTomi Valkeinen 	for (i = 0; i < dsi->num_lanes_used; ++i) {
1298d11e5c82SLaurent Pinchart 		unsigned int offset = offsets[i];
1299d11e5c82SLaurent Pinchart 		unsigned int polarity, lane_number;
1300d11e5c82SLaurent Pinchart 		unsigned int t;
13019960aa7cSTomi Valkeinen 
13029960aa7cSTomi Valkeinen 		for (t = 0; t < dsi->num_lanes_supported; ++t)
13039960aa7cSTomi Valkeinen 			if (dsi->lanes[t].function == functions[i])
13049960aa7cSTomi Valkeinen 				break;
13059960aa7cSTomi Valkeinen 
13069960aa7cSTomi Valkeinen 		if (t == dsi->num_lanes_supported)
13079960aa7cSTomi Valkeinen 			return -EINVAL;
13089960aa7cSTomi Valkeinen 
13099960aa7cSTomi Valkeinen 		lane_number = t;
13109960aa7cSTomi Valkeinen 		polarity = dsi->lanes[t].polarity;
13119960aa7cSTomi Valkeinen 
13129960aa7cSTomi Valkeinen 		r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
13139960aa7cSTomi Valkeinen 		r = FLD_MOD(r, polarity, offset + 3, offset + 3);
13149960aa7cSTomi Valkeinen 	}
13159960aa7cSTomi Valkeinen 
13169960aa7cSTomi Valkeinen 	/* clear the unused lanes */
13179960aa7cSTomi Valkeinen 	for (; i < dsi->num_lanes_supported; ++i) {
1318d11e5c82SLaurent Pinchart 		unsigned int offset = offsets[i];
13199960aa7cSTomi Valkeinen 
13209960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 0, offset + 2, offset);
13219960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 0, offset + 3, offset + 3);
13229960aa7cSTomi Valkeinen 	}
13239960aa7cSTomi Valkeinen 
13247093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_COMPLEXIO_CFG1, r);
13259960aa7cSTomi Valkeinen 
13269960aa7cSTomi Valkeinen 	return 0;
13279960aa7cSTomi Valkeinen }
13289960aa7cSTomi Valkeinen 
ns2ddr(struct dsi_data * dsi,unsigned int ns)13297093d6cdSLaurent Pinchart static inline unsigned int ns2ddr(struct dsi_data *dsi, unsigned int ns)
13309960aa7cSTomi Valkeinen {
13319960aa7cSTomi Valkeinen 	/* convert time in ns to ddr ticks, rounding up */
13329960aa7cSTomi Valkeinen 	unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
13337093d6cdSLaurent Pinchart 
13349960aa7cSTomi Valkeinen 	return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
13359960aa7cSTomi Valkeinen }
13369960aa7cSTomi Valkeinen 
ddr2ns(struct dsi_data * dsi,unsigned int ddr)13377093d6cdSLaurent Pinchart static inline unsigned int ddr2ns(struct dsi_data *dsi, unsigned int ddr)
13389960aa7cSTomi Valkeinen {
13399960aa7cSTomi Valkeinen 	unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4;
13407093d6cdSLaurent Pinchart 
13419960aa7cSTomi Valkeinen 	return ddr * 1000 * 1000 / (ddr_clk / 1000);
13429960aa7cSTomi Valkeinen }
13439960aa7cSTomi Valkeinen 
dsi_cio_timings(struct dsi_data * dsi)13447093d6cdSLaurent Pinchart static void dsi_cio_timings(struct dsi_data *dsi)
13459960aa7cSTomi Valkeinen {
13469960aa7cSTomi Valkeinen 	u32 r;
13479960aa7cSTomi Valkeinen 	u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
13489960aa7cSTomi Valkeinen 	u32 tlpx_half, tclk_trail, tclk_zero;
13499960aa7cSTomi Valkeinen 	u32 tclk_prepare;
13509960aa7cSTomi Valkeinen 
13519960aa7cSTomi Valkeinen 	/* calculate timings */
13529960aa7cSTomi Valkeinen 
13539960aa7cSTomi Valkeinen 	/* 1 * DDR_CLK = 2 * UI */
13549960aa7cSTomi Valkeinen 
13559960aa7cSTomi Valkeinen 	/* min 40ns + 4*UI	max 85ns + 6*UI */
13567093d6cdSLaurent Pinchart 	ths_prepare = ns2ddr(dsi, 70) + 2;
13579960aa7cSTomi Valkeinen 
13589960aa7cSTomi Valkeinen 	/* min 145ns + 10*UI */
13597093d6cdSLaurent Pinchart 	ths_prepare_ths_zero = ns2ddr(dsi, 175) + 2;
13609960aa7cSTomi Valkeinen 
13619960aa7cSTomi Valkeinen 	/* min max(8*UI, 60ns+4*UI) */
13627093d6cdSLaurent Pinchart 	ths_trail = ns2ddr(dsi, 60) + 5;
13639960aa7cSTomi Valkeinen 
13649960aa7cSTomi Valkeinen 	/* min 100ns */
13657093d6cdSLaurent Pinchart 	ths_exit = ns2ddr(dsi, 145);
13669960aa7cSTomi Valkeinen 
13679960aa7cSTomi Valkeinen 	/* tlpx min 50n */
13687093d6cdSLaurent Pinchart 	tlpx_half = ns2ddr(dsi, 25);
13699960aa7cSTomi Valkeinen 
13709960aa7cSTomi Valkeinen 	/* min 60ns */
13717093d6cdSLaurent Pinchart 	tclk_trail = ns2ddr(dsi, 60) + 2;
13729960aa7cSTomi Valkeinen 
13739960aa7cSTomi Valkeinen 	/* min 38ns, max 95ns */
13747093d6cdSLaurent Pinchart 	tclk_prepare = ns2ddr(dsi, 65);
13759960aa7cSTomi Valkeinen 
13769960aa7cSTomi Valkeinen 	/* min tclk-prepare + tclk-zero = 300ns */
13777093d6cdSLaurent Pinchart 	tclk_zero = ns2ddr(dsi, 260);
13789960aa7cSTomi Valkeinen 
13799960aa7cSTomi Valkeinen 	DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
13807093d6cdSLaurent Pinchart 		ths_prepare, ddr2ns(dsi, ths_prepare),
13817093d6cdSLaurent Pinchart 		ths_prepare_ths_zero, ddr2ns(dsi, ths_prepare_ths_zero));
13829960aa7cSTomi Valkeinen 	DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
13837093d6cdSLaurent Pinchart 			ths_trail, ddr2ns(dsi, ths_trail),
13847093d6cdSLaurent Pinchart 			ths_exit, ddr2ns(dsi, ths_exit));
13859960aa7cSTomi Valkeinen 
13869960aa7cSTomi Valkeinen 	DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
13879960aa7cSTomi Valkeinen 			"tclk_zero %u (%uns)\n",
13887093d6cdSLaurent Pinchart 			tlpx_half, ddr2ns(dsi, tlpx_half),
13897093d6cdSLaurent Pinchart 			tclk_trail, ddr2ns(dsi, tclk_trail),
13907093d6cdSLaurent Pinchart 			tclk_zero, ddr2ns(dsi, tclk_zero));
13919960aa7cSTomi Valkeinen 	DSSDBG("tclk_prepare %u (%uns)\n",
13927093d6cdSLaurent Pinchart 			tclk_prepare, ddr2ns(dsi, tclk_prepare));
13939960aa7cSTomi Valkeinen 
13949960aa7cSTomi Valkeinen 	/* program timings */
13959960aa7cSTomi Valkeinen 
13967093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0);
13979960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ths_prepare, 31, 24);
13989960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
13999960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ths_trail, 15, 8);
14009960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ths_exit, 7, 0);
14017093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_DSIPHY_CFG0, r);
14029960aa7cSTomi Valkeinen 
14037093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
14049960aa7cSTomi Valkeinen 	r = FLD_MOD(r, tlpx_half, 20, 16);
14059960aa7cSTomi Valkeinen 	r = FLD_MOD(r, tclk_trail, 15, 8);
14069960aa7cSTomi Valkeinen 	r = FLD_MOD(r, tclk_zero, 7, 0);
14079960aa7cSTomi Valkeinen 
140844d8ca10SLaurent Pinchart 	if (dsi->data->quirks & DSI_QUIRK_PHY_DCC) {
14099960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 0, 21, 21);	/* DCCEN = disable */
14109960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 1, 22, 22);	/* CLKINP_DIVBY2EN = enable */
14119960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 1, 23, 23);	/* CLKINP_SEL = enable */
14129960aa7cSTomi Valkeinen 	}
14139960aa7cSTomi Valkeinen 
14147093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_DSIPHY_CFG1, r);
14159960aa7cSTomi Valkeinen 
14167093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2);
14179960aa7cSTomi Valkeinen 	r = FLD_MOD(r, tclk_prepare, 7, 0);
14187093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_DSIPHY_CFG2, r);
14199960aa7cSTomi Valkeinen }
14209960aa7cSTomi Valkeinen 
dsi_cio_wait_tx_clk_esc_reset(struct dsi_data * dsi)14217093d6cdSLaurent Pinchart static int dsi_cio_wait_tx_clk_esc_reset(struct dsi_data *dsi)
14229960aa7cSTomi Valkeinen {
14239960aa7cSTomi Valkeinen 	int t, i;
14249960aa7cSTomi Valkeinen 	bool in_use[DSI_MAX_NR_LANES];
14259960aa7cSTomi Valkeinen 	static const u8 offsets_old[] = { 28, 27, 26 };
14269960aa7cSTomi Valkeinen 	static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
14279960aa7cSTomi Valkeinen 	const u8 *offsets;
14289960aa7cSTomi Valkeinen 
142944d8ca10SLaurent Pinchart 	if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC)
14309960aa7cSTomi Valkeinen 		offsets = offsets_old;
14319960aa7cSTomi Valkeinen 	else
14329960aa7cSTomi Valkeinen 		offsets = offsets_new;
14339960aa7cSTomi Valkeinen 
14349960aa7cSTomi Valkeinen 	for (i = 0; i < dsi->num_lanes_supported; ++i)
14359960aa7cSTomi Valkeinen 		in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
14369960aa7cSTomi Valkeinen 
14379960aa7cSTomi Valkeinen 	t = 100000;
14389960aa7cSTomi Valkeinen 	while (true) {
14399960aa7cSTomi Valkeinen 		u32 l;
14409960aa7cSTomi Valkeinen 		int ok;
14419960aa7cSTomi Valkeinen 
14427093d6cdSLaurent Pinchart 		l = dsi_read_reg(dsi, DSI_DSIPHY_CFG5);
14439960aa7cSTomi Valkeinen 
14449960aa7cSTomi Valkeinen 		ok = 0;
14459960aa7cSTomi Valkeinen 		for (i = 0; i < dsi->num_lanes_supported; ++i) {
14469960aa7cSTomi Valkeinen 			if (!in_use[i] || (l & (1 << offsets[i])))
14479960aa7cSTomi Valkeinen 				ok++;
14489960aa7cSTomi Valkeinen 		}
14499960aa7cSTomi Valkeinen 
14509960aa7cSTomi Valkeinen 		if (ok == dsi->num_lanes_supported)
14519960aa7cSTomi Valkeinen 			break;
14529960aa7cSTomi Valkeinen 
14539960aa7cSTomi Valkeinen 		if (--t == 0) {
14549960aa7cSTomi Valkeinen 			for (i = 0; i < dsi->num_lanes_supported; ++i) {
14559960aa7cSTomi Valkeinen 				if (!in_use[i] || (l & (1 << offsets[i])))
14569960aa7cSTomi Valkeinen 					continue;
14579960aa7cSTomi Valkeinen 
14589960aa7cSTomi Valkeinen 				DSSERR("CIO TXCLKESC%d domain not coming " \
14599960aa7cSTomi Valkeinen 						"out of reset\n", i);
14609960aa7cSTomi Valkeinen 			}
14619960aa7cSTomi Valkeinen 			return -EIO;
14629960aa7cSTomi Valkeinen 		}
14639960aa7cSTomi Valkeinen 	}
14649960aa7cSTomi Valkeinen 
14659960aa7cSTomi Valkeinen 	return 0;
14669960aa7cSTomi Valkeinen }
14679960aa7cSTomi Valkeinen 
14689960aa7cSTomi Valkeinen /* return bitmask of enabled lanes, lane0 being the lsb */
dsi_get_lane_mask(struct dsi_data * dsi)14697093d6cdSLaurent Pinchart static unsigned int dsi_get_lane_mask(struct dsi_data *dsi)
14709960aa7cSTomi Valkeinen {
1471d11e5c82SLaurent Pinchart 	unsigned int mask = 0;
14729960aa7cSTomi Valkeinen 	int i;
14739960aa7cSTomi Valkeinen 
14749960aa7cSTomi Valkeinen 	for (i = 0; i < dsi->num_lanes_supported; ++i) {
14759960aa7cSTomi Valkeinen 		if (dsi->lanes[i].function != DSI_LANE_UNUSED)
14769960aa7cSTomi Valkeinen 			mask |= 1 << i;
14779960aa7cSTomi Valkeinen 	}
14789960aa7cSTomi Valkeinen 
14799960aa7cSTomi Valkeinen 	return mask;
14809960aa7cSTomi Valkeinen }
14819960aa7cSTomi Valkeinen 
14829e1305d0SLaurent Pinchart /* OMAP4 CONTROL_DSIPHY */
14839e1305d0SLaurent Pinchart #define OMAP4_DSIPHY_SYSCON_OFFSET			0x78
14849e1305d0SLaurent Pinchart 
14859e1305d0SLaurent Pinchart #define OMAP4_DSI2_LANEENABLE_SHIFT			29
14869e1305d0SLaurent Pinchart #define OMAP4_DSI2_LANEENABLE_MASK			(0x7 << 29)
14879e1305d0SLaurent Pinchart #define OMAP4_DSI1_LANEENABLE_SHIFT			24
14889e1305d0SLaurent Pinchart #define OMAP4_DSI1_LANEENABLE_MASK			(0x1f << 24)
14899e1305d0SLaurent Pinchart #define OMAP4_DSI1_PIPD_SHIFT				19
14909e1305d0SLaurent Pinchart #define OMAP4_DSI1_PIPD_MASK				(0x1f << 19)
14919e1305d0SLaurent Pinchart #define OMAP4_DSI2_PIPD_SHIFT				14
14929e1305d0SLaurent Pinchart #define OMAP4_DSI2_PIPD_MASK				(0x1f << 14)
14939e1305d0SLaurent Pinchart 
dsi_omap4_mux_pads(struct dsi_data * dsi,unsigned int lanes)14949e1305d0SLaurent Pinchart static int dsi_omap4_mux_pads(struct dsi_data *dsi, unsigned int lanes)
14959e1305d0SLaurent Pinchart {
14969e1305d0SLaurent Pinchart 	u32 enable_mask, enable_shift;
14979e1305d0SLaurent Pinchart 	u32 pipd_mask, pipd_shift;
14989e1305d0SLaurent Pinchart 
14999e1305d0SLaurent Pinchart 	if (dsi->module_id == 0) {
15009e1305d0SLaurent Pinchart 		enable_mask = OMAP4_DSI1_LANEENABLE_MASK;
15019e1305d0SLaurent Pinchart 		enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT;
15029e1305d0SLaurent Pinchart 		pipd_mask = OMAP4_DSI1_PIPD_MASK;
15039e1305d0SLaurent Pinchart 		pipd_shift = OMAP4_DSI1_PIPD_SHIFT;
15049e1305d0SLaurent Pinchart 	} else if (dsi->module_id == 1) {
15059e1305d0SLaurent Pinchart 		enable_mask = OMAP4_DSI2_LANEENABLE_MASK;
15069e1305d0SLaurent Pinchart 		enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT;
15079e1305d0SLaurent Pinchart 		pipd_mask = OMAP4_DSI2_PIPD_MASK;
15089e1305d0SLaurent Pinchart 		pipd_shift = OMAP4_DSI2_PIPD_SHIFT;
15099e1305d0SLaurent Pinchart 	} else {
15109e1305d0SLaurent Pinchart 		return -ENODEV;
15119e1305d0SLaurent Pinchart 	}
15129e1305d0SLaurent Pinchart 
15135cdc8dbbSTomi Valkeinen 	return regmap_update_bits(dsi->syscon, OMAP4_DSIPHY_SYSCON_OFFSET,
15145cdc8dbbSTomi Valkeinen 		enable_mask | pipd_mask,
15155cdc8dbbSTomi Valkeinen 		(lanes << enable_shift) | (lanes << pipd_shift));
15169e1305d0SLaurent Pinchart }
15179e1305d0SLaurent Pinchart 
1518eeb45f85STomi Valkeinen /* OMAP5 CONTROL_DSIPHY */
1519eeb45f85STomi Valkeinen 
1520eeb45f85STomi Valkeinen #define OMAP5_DSIPHY_SYSCON_OFFSET	0x74
1521eeb45f85STomi Valkeinen 
1522eeb45f85STomi Valkeinen #define OMAP5_DSI1_LANEENABLE_SHIFT	24
1523eeb45f85STomi Valkeinen #define OMAP5_DSI2_LANEENABLE_SHIFT	19
1524eeb45f85STomi Valkeinen #define OMAP5_DSI_LANEENABLE_MASK	0x1f
1525eeb45f85STomi Valkeinen 
dsi_omap5_mux_pads(struct dsi_data * dsi,unsigned int lanes)1526eeb45f85STomi Valkeinen static int dsi_omap5_mux_pads(struct dsi_data *dsi, unsigned int lanes)
1527eeb45f85STomi Valkeinen {
1528eeb45f85STomi Valkeinen 	u32 enable_shift;
1529eeb45f85STomi Valkeinen 
1530eeb45f85STomi Valkeinen 	if (dsi->module_id == 0)
1531eeb45f85STomi Valkeinen 		enable_shift = OMAP5_DSI1_LANEENABLE_SHIFT;
1532eeb45f85STomi Valkeinen 	else if (dsi->module_id == 1)
1533eeb45f85STomi Valkeinen 		enable_shift = OMAP5_DSI2_LANEENABLE_SHIFT;
1534eeb45f85STomi Valkeinen 	else
1535eeb45f85STomi Valkeinen 		return -ENODEV;
1536eeb45f85STomi Valkeinen 
1537eeb45f85STomi Valkeinen 	return regmap_update_bits(dsi->syscon, OMAP5_DSIPHY_SYSCON_OFFSET,
1538eeb45f85STomi Valkeinen 		OMAP5_DSI_LANEENABLE_MASK << enable_shift,
1539eeb45f85STomi Valkeinen 		lanes << enable_shift);
1540eeb45f85STomi Valkeinen }
1541eeb45f85STomi Valkeinen 
dsi_enable_pads(struct dsi_data * dsi,unsigned int lane_mask)15429e1305d0SLaurent Pinchart static int dsi_enable_pads(struct dsi_data *dsi, unsigned int lane_mask)
15439e1305d0SLaurent Pinchart {
1544eeb45f85STomi Valkeinen 	if (dsi->data->model == DSI_MODEL_OMAP4)
15459e1305d0SLaurent Pinchart 		return dsi_omap4_mux_pads(dsi, lane_mask);
1546eeb45f85STomi Valkeinen 	if (dsi->data->model == DSI_MODEL_OMAP5)
1547eeb45f85STomi Valkeinen 		return dsi_omap5_mux_pads(dsi, lane_mask);
1548eeb45f85STomi Valkeinen 	return 0;
15499e1305d0SLaurent Pinchart }
15509e1305d0SLaurent Pinchart 
dsi_disable_pads(struct dsi_data * dsi)15519e1305d0SLaurent Pinchart static void dsi_disable_pads(struct dsi_data *dsi)
15529e1305d0SLaurent Pinchart {
1553eeb45f85STomi Valkeinen 	if (dsi->data->model == DSI_MODEL_OMAP4)
15549e1305d0SLaurent Pinchart 		dsi_omap4_mux_pads(dsi, 0);
1555eeb45f85STomi Valkeinen 	else if (dsi->data->model == DSI_MODEL_OMAP5)
1556eeb45f85STomi Valkeinen 		dsi_omap5_mux_pads(dsi, 0);
15579e1305d0SLaurent Pinchart }
15589e1305d0SLaurent Pinchart 
dsi_cio_init(struct dsi_data * dsi)15597093d6cdSLaurent Pinchart static int dsi_cio_init(struct dsi_data *dsi)
15609960aa7cSTomi Valkeinen {
15619960aa7cSTomi Valkeinen 	int r;
15629960aa7cSTomi Valkeinen 	u32 l;
15639960aa7cSTomi Valkeinen 
15649960aa7cSTomi Valkeinen 	DSSDBG("DSI CIO init starts");
15659960aa7cSTomi Valkeinen 
15667093d6cdSLaurent Pinchart 	r = dsi_enable_pads(dsi, dsi_get_lane_mask(dsi));
15679960aa7cSTomi Valkeinen 	if (r)
15689960aa7cSTomi Valkeinen 		return r;
15699960aa7cSTomi Valkeinen 
15707093d6cdSLaurent Pinchart 	dsi_enable_scp_clk(dsi);
15719960aa7cSTomi Valkeinen 
15729960aa7cSTomi Valkeinen 	/* A dummy read using the SCP interface to any DSIPHY register is
15739960aa7cSTomi Valkeinen 	 * required after DSIPHY reset to complete the reset of the DSI complex
15749960aa7cSTomi Valkeinen 	 * I/O. */
15757093d6cdSLaurent Pinchart 	dsi_read_reg(dsi, DSI_DSIPHY_CFG5);
15769960aa7cSTomi Valkeinen 
15777093d6cdSLaurent Pinchart 	if (!wait_for_bit_change(dsi, DSI_DSIPHY_CFG5, 30, 1)) {
15789960aa7cSTomi Valkeinen 		DSSERR("CIO SCP Clock domain not coming out of reset.\n");
15799960aa7cSTomi Valkeinen 		r = -EIO;
15809960aa7cSTomi Valkeinen 		goto err_scp_clk_dom;
15819960aa7cSTomi Valkeinen 	}
15829960aa7cSTomi Valkeinen 
15837093d6cdSLaurent Pinchart 	r = dsi_set_lane_config(dsi);
15849960aa7cSTomi Valkeinen 	if (r)
15859960aa7cSTomi Valkeinen 		goto err_scp_clk_dom;
15869960aa7cSTomi Valkeinen 
15879960aa7cSTomi Valkeinen 	/* set TX STOP MODE timer to maximum for this operation */
15887093d6cdSLaurent Pinchart 	l = dsi_read_reg(dsi, DSI_TIMING1);
15899960aa7cSTomi Valkeinen 	l = FLD_MOD(l, 1, 15, 15);	/* FORCE_TX_STOP_MODE_IO */
15909960aa7cSTomi Valkeinen 	l = FLD_MOD(l, 1, 14, 14);	/* STOP_STATE_X16_IO */
15919960aa7cSTomi Valkeinen 	l = FLD_MOD(l, 1, 13, 13);	/* STOP_STATE_X4_IO */
15929960aa7cSTomi Valkeinen 	l = FLD_MOD(l, 0x1fff, 12, 0);	/* STOP_STATE_COUNTER_IO */
15937093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TIMING1, l);
15949960aa7cSTomi Valkeinen 
15957093d6cdSLaurent Pinchart 	r = dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_ON);
15969960aa7cSTomi Valkeinen 	if (r)
15979960aa7cSTomi Valkeinen 		goto err_cio_pwr;
15989960aa7cSTomi Valkeinen 
15997093d6cdSLaurent Pinchart 	if (!wait_for_bit_change(dsi, DSI_COMPLEXIO_CFG1, 29, 1)) {
16009960aa7cSTomi Valkeinen 		DSSERR("CIO PWR clock domain not coming out of reset.\n");
16019960aa7cSTomi Valkeinen 		r = -ENODEV;
16029960aa7cSTomi Valkeinen 		goto err_cio_pwr_dom;
16039960aa7cSTomi Valkeinen 	}
16049960aa7cSTomi Valkeinen 
16057093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, true);
16067093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, false);
16077093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
16089960aa7cSTomi Valkeinen 
16097093d6cdSLaurent Pinchart 	r = dsi_cio_wait_tx_clk_esc_reset(dsi);
16109960aa7cSTomi Valkeinen 	if (r)
16119960aa7cSTomi Valkeinen 		goto err_tx_clk_esc_rst;
16129960aa7cSTomi Valkeinen 
16139960aa7cSTomi Valkeinen 	/* FORCE_TX_STOP_MODE_IO */
16147093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_TIMING1, 0, 15, 15);
16159960aa7cSTomi Valkeinen 
16167093d6cdSLaurent Pinchart 	dsi_cio_timings(dsi);
16179960aa7cSTomi Valkeinen 
16189960aa7cSTomi Valkeinen 	/* DDR_CLK_ALWAYS_ON */
16197093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL,
16209a521118STomi Valkeinen 		    !(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS),
16219a521118STomi Valkeinen 		    13, 13);
16229960aa7cSTomi Valkeinen 
16239960aa7cSTomi Valkeinen 	DSSDBG("CIO init done\n");
16249960aa7cSTomi Valkeinen 
16259960aa7cSTomi Valkeinen 	return 0;
16269960aa7cSTomi Valkeinen 
16279960aa7cSTomi Valkeinen err_tx_clk_esc_rst:
16287093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */
16299960aa7cSTomi Valkeinen err_cio_pwr_dom:
16307093d6cdSLaurent Pinchart 	dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_OFF);
16319960aa7cSTomi Valkeinen err_cio_pwr:
16329960aa7cSTomi Valkeinen err_scp_clk_dom:
16337093d6cdSLaurent Pinchart 	dsi_disable_scp_clk(dsi);
16349e1305d0SLaurent Pinchart 	dsi_disable_pads(dsi);
16359960aa7cSTomi Valkeinen 	return r;
16369960aa7cSTomi Valkeinen }
16379960aa7cSTomi Valkeinen 
dsi_cio_uninit(struct dsi_data * dsi)16387093d6cdSLaurent Pinchart static void dsi_cio_uninit(struct dsi_data *dsi)
16399960aa7cSTomi Valkeinen {
16409960aa7cSTomi Valkeinen 	/* DDR_CLK_ALWAYS_ON */
16417093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 13, 13);
16429960aa7cSTomi Valkeinen 
16437093d6cdSLaurent Pinchart 	dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_OFF);
16447093d6cdSLaurent Pinchart 	dsi_disable_scp_clk(dsi);
16459e1305d0SLaurent Pinchart 	dsi_disable_pads(dsi);
16469960aa7cSTomi Valkeinen }
16479960aa7cSTomi Valkeinen 
dsi_config_tx_fifo(struct dsi_data * dsi,enum fifo_size size1,enum fifo_size size2,enum fifo_size size3,enum fifo_size size4)16487093d6cdSLaurent Pinchart static void dsi_config_tx_fifo(struct dsi_data *dsi,
16499960aa7cSTomi Valkeinen 			       enum fifo_size size1, enum fifo_size size2,
16509960aa7cSTomi Valkeinen 			       enum fifo_size size3, enum fifo_size size4)
16519960aa7cSTomi Valkeinen {
16529960aa7cSTomi Valkeinen 	u32 r = 0;
16539960aa7cSTomi Valkeinen 	int add = 0;
16549960aa7cSTomi Valkeinen 	int i;
16559960aa7cSTomi Valkeinen 
16569960aa7cSTomi Valkeinen 	dsi->vc[0].tx_fifo_size = size1;
16579960aa7cSTomi Valkeinen 	dsi->vc[1].tx_fifo_size = size2;
16589960aa7cSTomi Valkeinen 	dsi->vc[2].tx_fifo_size = size3;
16599960aa7cSTomi Valkeinen 	dsi->vc[3].tx_fifo_size = size4;
16609960aa7cSTomi Valkeinen 
16619960aa7cSTomi Valkeinen 	for (i = 0; i < 4; i++) {
16629960aa7cSTomi Valkeinen 		u8 v;
16639960aa7cSTomi Valkeinen 		int size = dsi->vc[i].tx_fifo_size;
16649960aa7cSTomi Valkeinen 
16659960aa7cSTomi Valkeinen 		if (add + size > 4) {
16669960aa7cSTomi Valkeinen 			DSSERR("Illegal FIFO configuration\n");
16679960aa7cSTomi Valkeinen 			BUG();
16689960aa7cSTomi Valkeinen 			return;
16699960aa7cSTomi Valkeinen 		}
16709960aa7cSTomi Valkeinen 
16719960aa7cSTomi Valkeinen 		v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
16729960aa7cSTomi Valkeinen 		r |= v << (8 * i);
16739960aa7cSTomi Valkeinen 		/*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
16749960aa7cSTomi Valkeinen 		add += size;
16759960aa7cSTomi Valkeinen 	}
16769960aa7cSTomi Valkeinen 
16777093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TX_FIFO_VC_SIZE, r);
16789960aa7cSTomi Valkeinen }
16799960aa7cSTomi Valkeinen 
dsi_config_rx_fifo(struct dsi_data * dsi,enum fifo_size size1,enum fifo_size size2,enum fifo_size size3,enum fifo_size size4)16807093d6cdSLaurent Pinchart static void dsi_config_rx_fifo(struct dsi_data *dsi,
16819960aa7cSTomi Valkeinen 		enum fifo_size size1, enum fifo_size size2,
16829960aa7cSTomi Valkeinen 		enum fifo_size size3, enum fifo_size size4)
16839960aa7cSTomi Valkeinen {
16849960aa7cSTomi Valkeinen 	u32 r = 0;
16859960aa7cSTomi Valkeinen 	int add = 0;
16869960aa7cSTomi Valkeinen 	int i;
16879960aa7cSTomi Valkeinen 
16889960aa7cSTomi Valkeinen 	dsi->vc[0].rx_fifo_size = size1;
16899960aa7cSTomi Valkeinen 	dsi->vc[1].rx_fifo_size = size2;
16909960aa7cSTomi Valkeinen 	dsi->vc[2].rx_fifo_size = size3;
16919960aa7cSTomi Valkeinen 	dsi->vc[3].rx_fifo_size = size4;
16929960aa7cSTomi Valkeinen 
16939960aa7cSTomi Valkeinen 	for (i = 0; i < 4; i++) {
16949960aa7cSTomi Valkeinen 		u8 v;
16959960aa7cSTomi Valkeinen 		int size = dsi->vc[i].rx_fifo_size;
16969960aa7cSTomi Valkeinen 
16979960aa7cSTomi Valkeinen 		if (add + size > 4) {
16989960aa7cSTomi Valkeinen 			DSSERR("Illegal FIFO configuration\n");
16999960aa7cSTomi Valkeinen 			BUG();
17009960aa7cSTomi Valkeinen 			return;
17019960aa7cSTomi Valkeinen 		}
17029960aa7cSTomi Valkeinen 
17039960aa7cSTomi Valkeinen 		v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
17049960aa7cSTomi Valkeinen 		r |= v << (8 * i);
17059960aa7cSTomi Valkeinen 		/*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
17069960aa7cSTomi Valkeinen 		add += size;
17079960aa7cSTomi Valkeinen 	}
17089960aa7cSTomi Valkeinen 
17097093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_RX_FIFO_VC_SIZE, r);
17109960aa7cSTomi Valkeinen }
17119960aa7cSTomi Valkeinen 
dsi_force_tx_stop_mode_io(struct dsi_data * dsi)17127093d6cdSLaurent Pinchart static int dsi_force_tx_stop_mode_io(struct dsi_data *dsi)
17139960aa7cSTomi Valkeinen {
17149960aa7cSTomi Valkeinen 	u32 r;
17159960aa7cSTomi Valkeinen 
17167093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_TIMING1);
17179960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);	/* FORCE_TX_STOP_MODE_IO */
17187093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TIMING1, r);
17199960aa7cSTomi Valkeinen 
17207093d6cdSLaurent Pinchart 	if (!wait_for_bit_change(dsi, DSI_TIMING1, 15, 0)) {
17219960aa7cSTomi Valkeinen 		DSSERR("TX_STOP bit not going down\n");
17229960aa7cSTomi Valkeinen 		return -EIO;
17239960aa7cSTomi Valkeinen 	}
17249960aa7cSTomi Valkeinen 
17259960aa7cSTomi Valkeinen 	return 0;
17269960aa7cSTomi Valkeinen }
17279960aa7cSTomi Valkeinen 
dsi_vc_is_enabled(struct dsi_data * dsi,int vc)1728d8171145STomi Valkeinen static bool dsi_vc_is_enabled(struct dsi_data *dsi, int vc)
17299960aa7cSTomi Valkeinen {
1730d8171145STomi Valkeinen 	return REG_GET(dsi, DSI_VC_CTRL(vc), 0, 0);
17319960aa7cSTomi Valkeinen }
17329960aa7cSTomi Valkeinen 
dsi_packet_sent_handler_vp(void * data,u32 mask)17339960aa7cSTomi Valkeinen static void dsi_packet_sent_handler_vp(void *data, u32 mask)
17349960aa7cSTomi Valkeinen {
17359960aa7cSTomi Valkeinen 	struct dsi_packet_sent_handler_data *vp_data =
17369960aa7cSTomi Valkeinen 		(struct dsi_packet_sent_handler_data *) data;
17377093d6cdSLaurent Pinchart 	struct dsi_data *dsi = vp_data->dsi;
1738d8171145STomi Valkeinen 	const int vc = dsi->update_vc;
17399960aa7cSTomi Valkeinen 	u8 bit = dsi->te_enabled ? 30 : 31;
17409960aa7cSTomi Valkeinen 
1741d8171145STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit) == 0)
17429960aa7cSTomi Valkeinen 		complete(vp_data->completion);
17439960aa7cSTomi Valkeinen }
17449960aa7cSTomi Valkeinen 
dsi_sync_vc_vp(struct dsi_data * dsi,int vc)1745d8171145STomi Valkeinen static int dsi_sync_vc_vp(struct dsi_data *dsi, int vc)
17469960aa7cSTomi Valkeinen {
17479960aa7cSTomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
17489960aa7cSTomi Valkeinen 	struct dsi_packet_sent_handler_data vp_data = {
17497093d6cdSLaurent Pinchart 		.dsi = dsi,
17509960aa7cSTomi Valkeinen 		.completion = &completion
17519960aa7cSTomi Valkeinen 	};
17529960aa7cSTomi Valkeinen 	int r = 0;
17539960aa7cSTomi Valkeinen 	u8 bit;
17549960aa7cSTomi Valkeinen 
17559960aa7cSTomi Valkeinen 	bit = dsi->te_enabled ? 30 : 31;
17569960aa7cSTomi Valkeinen 
1757d8171145STomi Valkeinen 	r = dsi_register_isr_vc(dsi, vc, dsi_packet_sent_handler_vp,
17589960aa7cSTomi Valkeinen 		&vp_data, DSI_VC_IRQ_PACKET_SENT);
17599960aa7cSTomi Valkeinen 	if (r)
17609960aa7cSTomi Valkeinen 		goto err0;
17619960aa7cSTomi Valkeinen 
17629960aa7cSTomi Valkeinen 	/* Wait for completion only if TE_EN/TE_START is still set */
1763d8171145STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit)) {
17649960aa7cSTomi Valkeinen 		if (wait_for_completion_timeout(&completion,
17659960aa7cSTomi Valkeinen 				msecs_to_jiffies(10)) == 0) {
17669960aa7cSTomi Valkeinen 			DSSERR("Failed to complete previous frame transfer\n");
17679960aa7cSTomi Valkeinen 			r = -EIO;
17689960aa7cSTomi Valkeinen 			goto err1;
17699960aa7cSTomi Valkeinen 		}
17709960aa7cSTomi Valkeinen 	}
17719960aa7cSTomi Valkeinen 
1772d8171145STomi Valkeinen 	dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_vp,
17739960aa7cSTomi Valkeinen 		&vp_data, DSI_VC_IRQ_PACKET_SENT);
17749960aa7cSTomi Valkeinen 
17759960aa7cSTomi Valkeinen 	return 0;
17769960aa7cSTomi Valkeinen err1:
1777d8171145STomi Valkeinen 	dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_vp,
17789960aa7cSTomi Valkeinen 		&vp_data, DSI_VC_IRQ_PACKET_SENT);
17799960aa7cSTomi Valkeinen err0:
17809960aa7cSTomi Valkeinen 	return r;
17819960aa7cSTomi Valkeinen }
17829960aa7cSTomi Valkeinen 
dsi_packet_sent_handler_l4(void * data,u32 mask)17839960aa7cSTomi Valkeinen static void dsi_packet_sent_handler_l4(void *data, u32 mask)
17849960aa7cSTomi Valkeinen {
17859960aa7cSTomi Valkeinen 	struct dsi_packet_sent_handler_data *l4_data =
17869960aa7cSTomi Valkeinen 		(struct dsi_packet_sent_handler_data *) data;
17877093d6cdSLaurent Pinchart 	struct dsi_data *dsi = l4_data->dsi;
1788d8171145STomi Valkeinen 	const int vc = dsi->update_vc;
17899960aa7cSTomi Valkeinen 
1790d8171145STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5) == 0)
17919960aa7cSTomi Valkeinen 		complete(l4_data->completion);
17929960aa7cSTomi Valkeinen }
17939960aa7cSTomi Valkeinen 
dsi_sync_vc_l4(struct dsi_data * dsi,int vc)1794d8171145STomi Valkeinen static int dsi_sync_vc_l4(struct dsi_data *dsi, int vc)
17959960aa7cSTomi Valkeinen {
17969960aa7cSTomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
17979960aa7cSTomi Valkeinen 	struct dsi_packet_sent_handler_data l4_data = {
17987093d6cdSLaurent Pinchart 		.dsi = dsi,
17999960aa7cSTomi Valkeinen 		.completion = &completion
18009960aa7cSTomi Valkeinen 	};
18019960aa7cSTomi Valkeinen 	int r = 0;
18029960aa7cSTomi Valkeinen 
1803d8171145STomi Valkeinen 	r = dsi_register_isr_vc(dsi, vc, dsi_packet_sent_handler_l4,
18049960aa7cSTomi Valkeinen 		&l4_data, DSI_VC_IRQ_PACKET_SENT);
18059960aa7cSTomi Valkeinen 	if (r)
18069960aa7cSTomi Valkeinen 		goto err0;
18079960aa7cSTomi Valkeinen 
18089960aa7cSTomi Valkeinen 	/* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */
1809d8171145STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5)) {
18109960aa7cSTomi Valkeinen 		if (wait_for_completion_timeout(&completion,
18119960aa7cSTomi Valkeinen 				msecs_to_jiffies(10)) == 0) {
18129960aa7cSTomi Valkeinen 			DSSERR("Failed to complete previous l4 transfer\n");
18139960aa7cSTomi Valkeinen 			r = -EIO;
18149960aa7cSTomi Valkeinen 			goto err1;
18159960aa7cSTomi Valkeinen 		}
18169960aa7cSTomi Valkeinen 	}
18179960aa7cSTomi Valkeinen 
1818d8171145STomi Valkeinen 	dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_l4,
18199960aa7cSTomi Valkeinen 		&l4_data, DSI_VC_IRQ_PACKET_SENT);
18209960aa7cSTomi Valkeinen 
18219960aa7cSTomi Valkeinen 	return 0;
18229960aa7cSTomi Valkeinen err1:
1823d8171145STomi Valkeinen 	dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_l4,
18249960aa7cSTomi Valkeinen 		&l4_data, DSI_VC_IRQ_PACKET_SENT);
18259960aa7cSTomi Valkeinen err0:
18269960aa7cSTomi Valkeinen 	return r;
18279960aa7cSTomi Valkeinen }
18289960aa7cSTomi Valkeinen 
dsi_sync_vc(struct dsi_data * dsi,int vc)1829d8171145STomi Valkeinen static int dsi_sync_vc(struct dsi_data *dsi, int vc)
18309960aa7cSTomi Valkeinen {
18317093d6cdSLaurent Pinchart 	WARN_ON(!dsi_bus_is_locked(dsi));
18329960aa7cSTomi Valkeinen 
18339960aa7cSTomi Valkeinen 	WARN_ON(in_interrupt());
18349960aa7cSTomi Valkeinen 
1835d8171145STomi Valkeinen 	if (!dsi_vc_is_enabled(dsi, vc))
18369960aa7cSTomi Valkeinen 		return 0;
18379960aa7cSTomi Valkeinen 
1838d8171145STomi Valkeinen 	switch (dsi->vc[vc].source) {
18399960aa7cSTomi Valkeinen 	case DSI_VC_SOURCE_VP:
1840d8171145STomi Valkeinen 		return dsi_sync_vc_vp(dsi, vc);
18419960aa7cSTomi Valkeinen 	case DSI_VC_SOURCE_L4:
1842d8171145STomi Valkeinen 		return dsi_sync_vc_l4(dsi, vc);
18439960aa7cSTomi Valkeinen 	default:
18449960aa7cSTomi Valkeinen 		BUG();
18459960aa7cSTomi Valkeinen 		return -EINVAL;
18469960aa7cSTomi Valkeinen 	}
18479960aa7cSTomi Valkeinen }
18489960aa7cSTomi Valkeinen 
dsi_vc_enable(struct dsi_data * dsi,int vc,bool enable)1849d8171145STomi Valkeinen static int dsi_vc_enable(struct dsi_data *dsi, int vc, bool enable)
18509960aa7cSTomi Valkeinen {
1851d8171145STomi Valkeinen 	DSSDBG("dsi_vc_enable vc %d, enable %d\n",
1852d8171145STomi Valkeinen 			vc, enable);
18539960aa7cSTomi Valkeinen 
18549960aa7cSTomi Valkeinen 	enable = enable ? 1 : 0;
18559960aa7cSTomi Valkeinen 
1856d8171145STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 0, 0);
18579960aa7cSTomi Valkeinen 
1858d8171145STomi Valkeinen 	if (!wait_for_bit_change(dsi, DSI_VC_CTRL(vc), 0, enable)) {
18599960aa7cSTomi Valkeinen 		DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
18609960aa7cSTomi Valkeinen 		return -EIO;
18619960aa7cSTomi Valkeinen 	}
18629960aa7cSTomi Valkeinen 
18639960aa7cSTomi Valkeinen 	return 0;
18649960aa7cSTomi Valkeinen }
18659960aa7cSTomi Valkeinen 
dsi_vc_initial_config(struct dsi_data * dsi,int vc)1866d8171145STomi Valkeinen static void dsi_vc_initial_config(struct dsi_data *dsi, int vc)
18679960aa7cSTomi Valkeinen {
18689960aa7cSTomi Valkeinen 	u32 r;
18699960aa7cSTomi Valkeinen 
1870d8171145STomi Valkeinen 	DSSDBG("Initial config of VC %d", vc);
18719960aa7cSTomi Valkeinen 
1872d8171145STomi Valkeinen 	r = dsi_read_reg(dsi, DSI_VC_CTRL(vc));
18739960aa7cSTomi Valkeinen 
18749960aa7cSTomi Valkeinen 	if (FLD_GET(r, 15, 15)) /* VC_BUSY */
18759960aa7cSTomi Valkeinen 		DSSERR("VC(%d) busy when trying to configure it!\n",
1876d8171145STomi Valkeinen 				vc);
18779960aa7cSTomi Valkeinen 
18789960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
18799960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN  */
18809960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
18819960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
18829960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
18839960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
18849960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
188544d8ca10SLaurent Pinchart 	if (dsi->data->quirks & DSI_QUIRK_VC_OCP_WIDTH)
18869960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 3, 11, 10);	/* OCP_WIDTH = 32 bit */
18879960aa7cSTomi Valkeinen 
18889960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
18899960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
18909960aa7cSTomi Valkeinen 
1891d8171145STomi Valkeinen 	dsi_write_reg(dsi, DSI_VC_CTRL(vc), r);
18929960aa7cSTomi Valkeinen 
1893d8171145STomi Valkeinen 	dsi->vc[vc].source = DSI_VC_SOURCE_L4;
18949960aa7cSTomi Valkeinen }
18959960aa7cSTomi Valkeinen 
dsi_vc_enable_hs(struct omap_dss_device * dssdev,int vc,bool enable)1896d8171145STomi Valkeinen static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int vc,
18979960aa7cSTomi Valkeinen 		bool enable)
18989960aa7cSTomi Valkeinen {
1899c068408eSLaurent Pinchart 	struct dsi_data *dsi = to_dsi_data(dssdev);
19009960aa7cSTomi Valkeinen 
1901d8171145STomi Valkeinen 	DSSDBG("dsi_vc_enable_hs(%d, %d)\n", vc, enable);
19029960aa7cSTomi Valkeinen 
190383d74642STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_CTRL(vc), 9, 9) == enable)
190483d74642STomi Valkeinen 		return;
190583d74642STomi Valkeinen 
19067093d6cdSLaurent Pinchart 	WARN_ON(!dsi_bus_is_locked(dsi));
19079960aa7cSTomi Valkeinen 
1908d8171145STomi Valkeinen 	dsi_vc_enable(dsi, vc, 0);
19097093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, 0);
19109960aa7cSTomi Valkeinen 
1911d8171145STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 9, 9);
19129960aa7cSTomi Valkeinen 
1913d8171145STomi Valkeinen 	dsi_vc_enable(dsi, vc, 1);
19147093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, 1);
19159960aa7cSTomi Valkeinen 
19167093d6cdSLaurent Pinchart 	dsi_force_tx_stop_mode_io(dsi);
19179960aa7cSTomi Valkeinen }
19189960aa7cSTomi Valkeinen 
dsi_vc_flush_long_data(struct dsi_data * dsi,int vc)1919d8171145STomi Valkeinen static void dsi_vc_flush_long_data(struct dsi_data *dsi, int vc)
19209960aa7cSTomi Valkeinen {
1921d8171145STomi Valkeinen 	while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
19229960aa7cSTomi Valkeinen 		u32 val;
1923d8171145STomi Valkeinen 		val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc));
19249960aa7cSTomi Valkeinen 		DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
19259960aa7cSTomi Valkeinen 				(val >> 0) & 0xff,
19269960aa7cSTomi Valkeinen 				(val >> 8) & 0xff,
19279960aa7cSTomi Valkeinen 				(val >> 16) & 0xff,
19289960aa7cSTomi Valkeinen 				(val >> 24) & 0xff);
19299960aa7cSTomi Valkeinen 	}
19309960aa7cSTomi Valkeinen }
19319960aa7cSTomi Valkeinen 
dsi_show_rx_ack_with_err(u16 err)19329960aa7cSTomi Valkeinen static void dsi_show_rx_ack_with_err(u16 err)
19339960aa7cSTomi Valkeinen {
19349960aa7cSTomi Valkeinen 	DSSERR("\tACK with ERROR (%#x):\n", err);
19359960aa7cSTomi Valkeinen 	if (err & (1 << 0))
19369960aa7cSTomi Valkeinen 		DSSERR("\t\tSoT Error\n");
19379960aa7cSTomi Valkeinen 	if (err & (1 << 1))
19389960aa7cSTomi Valkeinen 		DSSERR("\t\tSoT Sync Error\n");
19399960aa7cSTomi Valkeinen 	if (err & (1 << 2))
19409960aa7cSTomi Valkeinen 		DSSERR("\t\tEoT Sync Error\n");
19419960aa7cSTomi Valkeinen 	if (err & (1 << 3))
19429960aa7cSTomi Valkeinen 		DSSERR("\t\tEscape Mode Entry Command Error\n");
19439960aa7cSTomi Valkeinen 	if (err & (1 << 4))
19449960aa7cSTomi Valkeinen 		DSSERR("\t\tLP Transmit Sync Error\n");
19459960aa7cSTomi Valkeinen 	if (err & (1 << 5))
19469960aa7cSTomi Valkeinen 		DSSERR("\t\tHS Receive Timeout Error\n");
19479960aa7cSTomi Valkeinen 	if (err & (1 << 6))
19489960aa7cSTomi Valkeinen 		DSSERR("\t\tFalse Control Error\n");
19499960aa7cSTomi Valkeinen 	if (err & (1 << 7))
19509960aa7cSTomi Valkeinen 		DSSERR("\t\t(reserved7)\n");
19519960aa7cSTomi Valkeinen 	if (err & (1 << 8))
19529960aa7cSTomi Valkeinen 		DSSERR("\t\tECC Error, single-bit (corrected)\n");
19539960aa7cSTomi Valkeinen 	if (err & (1 << 9))
19549960aa7cSTomi Valkeinen 		DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
19559960aa7cSTomi Valkeinen 	if (err & (1 << 10))
19569960aa7cSTomi Valkeinen 		DSSERR("\t\tChecksum Error\n");
19579960aa7cSTomi Valkeinen 	if (err & (1 << 11))
19589960aa7cSTomi Valkeinen 		DSSERR("\t\tData type not recognized\n");
19599960aa7cSTomi Valkeinen 	if (err & (1 << 12))
19609960aa7cSTomi Valkeinen 		DSSERR("\t\tInvalid VC ID\n");
19619960aa7cSTomi Valkeinen 	if (err & (1 << 13))
19629960aa7cSTomi Valkeinen 		DSSERR("\t\tInvalid Transmission Length\n");
19639960aa7cSTomi Valkeinen 	if (err & (1 << 14))
19649960aa7cSTomi Valkeinen 		DSSERR("\t\t(reserved14)\n");
19659960aa7cSTomi Valkeinen 	if (err & (1 << 15))
19669960aa7cSTomi Valkeinen 		DSSERR("\t\tDSI Protocol Violation\n");
19679960aa7cSTomi Valkeinen }
19689960aa7cSTomi Valkeinen 
dsi_vc_flush_receive_data(struct dsi_data * dsi,int vc)1969d8171145STomi Valkeinen static u16 dsi_vc_flush_receive_data(struct dsi_data *dsi, int vc)
19709960aa7cSTomi Valkeinen {
19719960aa7cSTomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
1972d8171145STomi Valkeinen 	while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
19739960aa7cSTomi Valkeinen 		u32 val;
19749960aa7cSTomi Valkeinen 		u8 dt;
1975d8171145STomi Valkeinen 		val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc));
19769960aa7cSTomi Valkeinen 		DSSERR("\trawval %#08x\n", val);
19779960aa7cSTomi Valkeinen 		dt = FLD_GET(val, 5, 0);
19789960aa7cSTomi Valkeinen 		if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
19799960aa7cSTomi Valkeinen 			u16 err = FLD_GET(val, 23, 8);
19809960aa7cSTomi Valkeinen 			dsi_show_rx_ack_with_err(err);
19819960aa7cSTomi Valkeinen 		} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
19829960aa7cSTomi Valkeinen 			DSSERR("\tDCS short response, 1 byte: %#x\n",
19839960aa7cSTomi Valkeinen 					FLD_GET(val, 23, 8));
19849960aa7cSTomi Valkeinen 		} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
19859960aa7cSTomi Valkeinen 			DSSERR("\tDCS short response, 2 byte: %#x\n",
19869960aa7cSTomi Valkeinen 					FLD_GET(val, 23, 8));
19879960aa7cSTomi Valkeinen 		} else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
19889960aa7cSTomi Valkeinen 			DSSERR("\tDCS long response, len %d\n",
19899960aa7cSTomi Valkeinen 					FLD_GET(val, 23, 8));
1990d8171145STomi Valkeinen 			dsi_vc_flush_long_data(dsi, vc);
19919960aa7cSTomi Valkeinen 		} else {
19929960aa7cSTomi Valkeinen 			DSSERR("\tunknown datatype 0x%02x\n", dt);
19939960aa7cSTomi Valkeinen 		}
19949960aa7cSTomi Valkeinen 	}
19959960aa7cSTomi Valkeinen 	return 0;
19969960aa7cSTomi Valkeinen }
19979960aa7cSTomi Valkeinen 
dsi_vc_send_bta(struct dsi_data * dsi,int vc)1998d8171145STomi Valkeinen static int dsi_vc_send_bta(struct dsi_data *dsi, int vc)
19999960aa7cSTomi Valkeinen {
20009960aa7cSTomi Valkeinen 	if (dsi->debug_write || dsi->debug_read)
2001d8171145STomi Valkeinen 		DSSDBG("dsi_vc_send_bta %d\n", vc);
20029960aa7cSTomi Valkeinen 
20037093d6cdSLaurent Pinchart 	WARN_ON(!dsi_bus_is_locked(dsi));
20049960aa7cSTomi Valkeinen 
20059960aa7cSTomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2006d8171145STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
20079960aa7cSTomi Valkeinen 		DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
2008d8171145STomi Valkeinen 		dsi_vc_flush_receive_data(dsi, vc);
20099960aa7cSTomi Valkeinen 	}
20109960aa7cSTomi Valkeinen 
2011d8171145STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 1, 6, 6); /* BTA_EN */
20129960aa7cSTomi Valkeinen 
20139960aa7cSTomi Valkeinen 	/* flush posted write */
2014d8171145STomi Valkeinen 	dsi_read_reg(dsi, DSI_VC_CTRL(vc));
20159960aa7cSTomi Valkeinen 
20169960aa7cSTomi Valkeinen 	return 0;
20179960aa7cSTomi Valkeinen }
20189960aa7cSTomi Valkeinen 
dsi_vc_send_bta_sync(struct omap_dss_device * dssdev,int vc)2019d8171145STomi Valkeinen static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int vc)
20209960aa7cSTomi Valkeinen {
2021c068408eSLaurent Pinchart 	struct dsi_data *dsi = to_dsi_data(dssdev);
20229960aa7cSTomi Valkeinen 	DECLARE_COMPLETION_ONSTACK(completion);
20239960aa7cSTomi Valkeinen 	int r = 0;
20249960aa7cSTomi Valkeinen 	u32 err;
20259960aa7cSTomi Valkeinen 
2026d8171145STomi Valkeinen 	r = dsi_register_isr_vc(dsi, vc, dsi_completion_handler,
20279960aa7cSTomi Valkeinen 			&completion, DSI_VC_IRQ_BTA);
20289960aa7cSTomi Valkeinen 	if (r)
20299960aa7cSTomi Valkeinen 		goto err0;
20309960aa7cSTomi Valkeinen 
20317093d6cdSLaurent Pinchart 	r = dsi_register_isr(dsi, dsi_completion_handler, &completion,
20329960aa7cSTomi Valkeinen 			DSI_IRQ_ERROR_MASK);
20339960aa7cSTomi Valkeinen 	if (r)
20349960aa7cSTomi Valkeinen 		goto err1;
20359960aa7cSTomi Valkeinen 
2036d8171145STomi Valkeinen 	r = dsi_vc_send_bta(dsi, vc);
20379960aa7cSTomi Valkeinen 	if (r)
20389960aa7cSTomi Valkeinen 		goto err2;
20399960aa7cSTomi Valkeinen 
20409960aa7cSTomi Valkeinen 	if (wait_for_completion_timeout(&completion,
20419960aa7cSTomi Valkeinen 				msecs_to_jiffies(500)) == 0) {
20429960aa7cSTomi Valkeinen 		DSSERR("Failed to receive BTA\n");
20439960aa7cSTomi Valkeinen 		r = -EIO;
20449960aa7cSTomi Valkeinen 		goto err2;
20459960aa7cSTomi Valkeinen 	}
20469960aa7cSTomi Valkeinen 
20477093d6cdSLaurent Pinchart 	err = dsi_get_errors(dsi);
20489960aa7cSTomi Valkeinen 	if (err) {
20499960aa7cSTomi Valkeinen 		DSSERR("Error while sending BTA: %x\n", err);
20509960aa7cSTomi Valkeinen 		r = -EIO;
20519960aa7cSTomi Valkeinen 		goto err2;
20529960aa7cSTomi Valkeinen 	}
20539960aa7cSTomi Valkeinen err2:
20547093d6cdSLaurent Pinchart 	dsi_unregister_isr(dsi, dsi_completion_handler, &completion,
20559960aa7cSTomi Valkeinen 			DSI_IRQ_ERROR_MASK);
20569960aa7cSTomi Valkeinen err1:
2057d8171145STomi Valkeinen 	dsi_unregister_isr_vc(dsi, vc, dsi_completion_handler,
20589960aa7cSTomi Valkeinen 			&completion, DSI_VC_IRQ_BTA);
20599960aa7cSTomi Valkeinen err0:
20609960aa7cSTomi Valkeinen 	return r;
20619960aa7cSTomi Valkeinen }
20629960aa7cSTomi Valkeinen 
dsi_vc_write_long_header(struct dsi_data * dsi,int vc,int channel,u8 data_type,u16 len,u8 ecc)2063d8171145STomi Valkeinen static inline void dsi_vc_write_long_header(struct dsi_data *dsi, int vc,
20645e430754STomi Valkeinen 					    int channel, u8 data_type, u16 len,
20655e430754STomi Valkeinen 					    u8 ecc)
20669960aa7cSTomi Valkeinen {
20679960aa7cSTomi Valkeinen 	u32 val;
20689960aa7cSTomi Valkeinen 	u8 data_id;
20699960aa7cSTomi Valkeinen 
20707093d6cdSLaurent Pinchart 	WARN_ON(!dsi_bus_is_locked(dsi));
20719960aa7cSTomi Valkeinen 
2072d58eb31fSTomi Valkeinen 	data_id = data_type | channel << 6;
20739960aa7cSTomi Valkeinen 
20749960aa7cSTomi Valkeinen 	val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
20759960aa7cSTomi Valkeinen 		FLD_VAL(ecc, 31, 24);
20769960aa7cSTomi Valkeinen 
2077d8171145STomi Valkeinen 	dsi_write_reg(dsi, DSI_VC_LONG_PACKET_HEADER(vc), val);
20789960aa7cSTomi Valkeinen }
20799960aa7cSTomi Valkeinen 
dsi_vc_write_long_payload(struct dsi_data * dsi,int vc,u8 b1,u8 b2,u8 b3,u8 b4)2080d8171145STomi Valkeinen static inline void dsi_vc_write_long_payload(struct dsi_data *dsi, int vc,
20817093d6cdSLaurent Pinchart 					     u8 b1, u8 b2, u8 b3, u8 b4)
20829960aa7cSTomi Valkeinen {
20839960aa7cSTomi Valkeinen 	u32 val;
20849960aa7cSTomi Valkeinen 
20859960aa7cSTomi Valkeinen 	val = b4 << 24 | b3 << 16 | b2 << 8  | b1 << 0;
20869960aa7cSTomi Valkeinen 
20879960aa7cSTomi Valkeinen /*	DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
20889960aa7cSTomi Valkeinen 			b1, b2, b3, b4, val); */
20899960aa7cSTomi Valkeinen 
2090d8171145STomi Valkeinen 	dsi_write_reg(dsi, DSI_VC_LONG_PACKET_PAYLOAD(vc), val);
20919960aa7cSTomi Valkeinen }
20929960aa7cSTomi Valkeinen 
dsi_vc_send_long(struct dsi_data * dsi,int vc,const struct mipi_dsi_msg * msg)20935e430754STomi Valkeinen static int dsi_vc_send_long(struct dsi_data *dsi, int vc,
20941ed62538SSebastian Reichel 			    const struct mipi_dsi_msg *msg)
20959960aa7cSTomi Valkeinen {
20969960aa7cSTomi Valkeinen 	/*u32 val; */
20979960aa7cSTomi Valkeinen 	int i;
2098ee0c365cSSebastian Reichel 	const u8 *p;
20999960aa7cSTomi Valkeinen 	int r = 0;
21009960aa7cSTomi Valkeinen 	u8 b1, b2, b3, b4;
21019960aa7cSTomi Valkeinen 
21029960aa7cSTomi Valkeinen 	if (dsi->debug_write)
2103d6a4bf45SLaurent Pinchart 		DSSDBG("dsi_vc_send_long, %zu bytes\n", msg->tx_len);
21049960aa7cSTomi Valkeinen 
21059960aa7cSTomi Valkeinen 	/* len + header */
2106d58eb31fSTomi Valkeinen 	if (dsi->vc[vc].tx_fifo_size * 32 * 4 < msg->tx_len + 4) {
21079960aa7cSTomi Valkeinen 		DSSERR("unable to send long packet: packet too long.\n");
21089960aa7cSTomi Valkeinen 		return -EINVAL;
21099960aa7cSTomi Valkeinen 	}
21109960aa7cSTomi Valkeinen 
21115e430754STomi Valkeinen 	dsi_vc_write_long_header(dsi, vc, msg->channel, msg->type, msg->tx_len, 0);
21129960aa7cSTomi Valkeinen 
21131ed62538SSebastian Reichel 	p = msg->tx_buf;
21141ed62538SSebastian Reichel 	for (i = 0; i < msg->tx_len >> 2; i++) {
21159960aa7cSTomi Valkeinen 		if (dsi->debug_write)
21169960aa7cSTomi Valkeinen 			DSSDBG("\tsending full packet %d\n", i);
21179960aa7cSTomi Valkeinen 
21189960aa7cSTomi Valkeinen 		b1 = *p++;
21199960aa7cSTomi Valkeinen 		b2 = *p++;
21209960aa7cSTomi Valkeinen 		b3 = *p++;
21219960aa7cSTomi Valkeinen 		b4 = *p++;
21229960aa7cSTomi Valkeinen 
2123d58eb31fSTomi Valkeinen 		dsi_vc_write_long_payload(dsi, vc, b1, b2, b3, b4);
21249960aa7cSTomi Valkeinen 	}
21259960aa7cSTomi Valkeinen 
21261ed62538SSebastian Reichel 	i = msg->tx_len % 4;
21279960aa7cSTomi Valkeinen 	if (i) {
21289960aa7cSTomi Valkeinen 		b1 = 0; b2 = 0; b3 = 0;
21299960aa7cSTomi Valkeinen 
21309960aa7cSTomi Valkeinen 		if (dsi->debug_write)
21319960aa7cSTomi Valkeinen 			DSSDBG("\tsending remainder bytes %d\n", i);
21329960aa7cSTomi Valkeinen 
21339960aa7cSTomi Valkeinen 		switch (i) {
21349960aa7cSTomi Valkeinen 		case 3:
21359960aa7cSTomi Valkeinen 			b1 = *p++;
21369960aa7cSTomi Valkeinen 			b2 = *p++;
21379960aa7cSTomi Valkeinen 			b3 = *p++;
21389960aa7cSTomi Valkeinen 			break;
21399960aa7cSTomi Valkeinen 		case 2:
21409960aa7cSTomi Valkeinen 			b1 = *p++;
21419960aa7cSTomi Valkeinen 			b2 = *p++;
21429960aa7cSTomi Valkeinen 			break;
21439960aa7cSTomi Valkeinen 		case 1:
21449960aa7cSTomi Valkeinen 			b1 = *p++;
21459960aa7cSTomi Valkeinen 			break;
21469960aa7cSTomi Valkeinen 		}
21479960aa7cSTomi Valkeinen 
2148d58eb31fSTomi Valkeinen 		dsi_vc_write_long_payload(dsi, vc, b1, b2, b3, 0);
21499960aa7cSTomi Valkeinen 	}
21509960aa7cSTomi Valkeinen 
21519960aa7cSTomi Valkeinen 	return r;
21529960aa7cSTomi Valkeinen }
21539960aa7cSTomi Valkeinen 
dsi_vc_send_short(struct dsi_data * dsi,int vc,const struct mipi_dsi_msg * msg)21545e430754STomi Valkeinen static int dsi_vc_send_short(struct dsi_data *dsi, int vc,
21551ed62538SSebastian Reichel 			     const struct mipi_dsi_msg *msg)
21569960aa7cSTomi Valkeinen {
21571ed62538SSebastian Reichel 	struct mipi_dsi_packet pkt;
215869091154SJunlin Yang 	int ret;
21599960aa7cSTomi Valkeinen 	u32 r;
21601ed62538SSebastian Reichel 
216169091154SJunlin Yang 	ret = mipi_dsi_create_packet(&pkt, msg);
216269091154SJunlin Yang 	if (ret < 0)
216369091154SJunlin Yang 		return ret;
21649960aa7cSTomi Valkeinen 
21657093d6cdSLaurent Pinchart 	WARN_ON(!dsi_bus_is_locked(dsi));
21669960aa7cSTomi Valkeinen 
21679960aa7cSTomi Valkeinen 	if (dsi->debug_write)
2168d58eb31fSTomi Valkeinen 		DSSDBG("dsi_vc_send_short(vc%d, dt %#x, b1 %#x, b2 %#x)\n",
2169d58eb31fSTomi Valkeinen 		       vc, msg->type, pkt.header[1], pkt.header[2]);
21709960aa7cSTomi Valkeinen 
2171d58eb31fSTomi Valkeinen 	if (FLD_GET(dsi_read_reg(dsi, DSI_VC_CTRL(vc)), 16, 16)) {
21729960aa7cSTomi Valkeinen 		DSSERR("ERROR FIFO FULL, aborting transfer\n");
21739960aa7cSTomi Valkeinen 		return -EINVAL;
21749960aa7cSTomi Valkeinen 	}
21759960aa7cSTomi Valkeinen 
21761ed62538SSebastian Reichel 	r = pkt.header[3] << 24 | pkt.header[2] << 16 | pkt.header[1] << 8 |
21771ed62538SSebastian Reichel 	    pkt.header[0];
21789960aa7cSTomi Valkeinen 
2179d58eb31fSTomi Valkeinen 	dsi_write_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc), r);
21809960aa7cSTomi Valkeinen 
21819960aa7cSTomi Valkeinen 	return 0;
21829960aa7cSTomi Valkeinen }
21839960aa7cSTomi Valkeinen 
dsi_vc_send_null(struct dsi_data * dsi,int vc,int channel)21845e430754STomi Valkeinen static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel)
21859960aa7cSTomi Valkeinen {
21861ed62538SSebastian Reichel 	const struct mipi_dsi_msg msg = {
2187d58eb31fSTomi Valkeinen 		.channel = channel,
21881ed62538SSebastian Reichel 		.type = MIPI_DSI_NULL_PACKET,
21891ed62538SSebastian Reichel 	};
21901ed62538SSebastian Reichel 
21915e430754STomi Valkeinen 	return dsi_vc_send_long(dsi, vc, &msg);
21929960aa7cSTomi Valkeinen }
21939960aa7cSTomi Valkeinen 
dsi_vc_write_common(struct omap_dss_device * dssdev,int vc,const struct mipi_dsi_msg * msg)21945e430754STomi Valkeinen static int dsi_vc_write_common(struct omap_dss_device *dssdev, int vc,
2195e7096538SSebastian Reichel 			       const struct mipi_dsi_msg *msg)
21969960aa7cSTomi Valkeinen {
2197e7096538SSebastian Reichel 	struct dsi_data *dsi = to_dsi_data(dssdev);
21989960aa7cSTomi Valkeinen 	int r;
21999960aa7cSTomi Valkeinen 
22001ed62538SSebastian Reichel 	if (mipi_dsi_packet_format_is_short(msg->type))
22015e430754STomi Valkeinen 		r = dsi_vc_send_short(dsi, vc, msg);
22021ed62538SSebastian Reichel 	else
22035e430754STomi Valkeinen 		r = dsi_vc_send_long(dsi, vc, msg);
22049960aa7cSTomi Valkeinen 
2205e7096538SSebastian Reichel 	if (r < 0)
2206e7096538SSebastian Reichel 		return r;
2207e7096538SSebastian Reichel 
2208e7096538SSebastian Reichel 	/*
2209e7096538SSebastian Reichel 	 * TODO: we do not always have to do the BTA sync, for example
2210e7096538SSebastian Reichel 	 * we can improve performance by setting the update window
2211e7096538SSebastian Reichel 	 * information without sending BTA sync between the commands.
2212e7096538SSebastian Reichel 	 * In that case we can return early.
2213e7096538SSebastian Reichel 	 */
2214e7096538SSebastian Reichel 
2215d58eb31fSTomi Valkeinen 	r = dsi_vc_send_bta_sync(dssdev, vc);
2216e7096538SSebastian Reichel 	if (r) {
2217e7096538SSebastian Reichel 		DSSERR("bta sync failed\n");
22189960aa7cSTomi Valkeinen 		return r;
22199960aa7cSTomi Valkeinen 	}
22209960aa7cSTomi Valkeinen 
22219960aa7cSTomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2222d58eb31fSTomi Valkeinen 	if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) {
22239960aa7cSTomi Valkeinen 		DSSERR("rx fifo not empty after write, dumping data:\n");
2224d58eb31fSTomi Valkeinen 		dsi_vc_flush_receive_data(dsi, vc);
2225e7096538SSebastian Reichel 		return -EIO;
22269960aa7cSTomi Valkeinen 	}
22279960aa7cSTomi Valkeinen 
22289960aa7cSTomi Valkeinen 	return 0;
22299960aa7cSTomi Valkeinen }
22309960aa7cSTomi Valkeinen 
dsi_vc_read_rx_fifo(struct dsi_data * dsi,int vc,u8 * buf,int buflen,enum dss_dsi_content_type type)2231d8171145STomi Valkeinen static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf,
22327093d6cdSLaurent Pinchart 			       int buflen, enum dss_dsi_content_type type)
22339960aa7cSTomi Valkeinen {
22349960aa7cSTomi Valkeinen 	u32 val;
22359960aa7cSTomi Valkeinen 	u8 dt;
22369960aa7cSTomi Valkeinen 	int r;
22379960aa7cSTomi Valkeinen 
22389960aa7cSTomi Valkeinen 	/* RX_FIFO_NOT_EMPTY */
2239d8171145STomi Valkeinen 	if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20) == 0) {
22409960aa7cSTomi Valkeinen 		DSSERR("RX fifo empty when trying to read.\n");
22419960aa7cSTomi Valkeinen 		r = -EIO;
22429960aa7cSTomi Valkeinen 		goto err;
22439960aa7cSTomi Valkeinen 	}
22449960aa7cSTomi Valkeinen 
2245d8171145STomi Valkeinen 	val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc));
22469960aa7cSTomi Valkeinen 	if (dsi->debug_read)
22479960aa7cSTomi Valkeinen 		DSSDBG("\theader: %08x\n", val);
22489960aa7cSTomi Valkeinen 	dt = FLD_GET(val, 5, 0);
22499960aa7cSTomi Valkeinen 	if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
22509960aa7cSTomi Valkeinen 		u16 err = FLD_GET(val, 23, 8);
22519960aa7cSTomi Valkeinen 		dsi_show_rx_ack_with_err(err);
22529960aa7cSTomi Valkeinen 		r = -EIO;
22539960aa7cSTomi Valkeinen 		goto err;
22549960aa7cSTomi Valkeinen 
22559960aa7cSTomi Valkeinen 	} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
22569960aa7cSTomi Valkeinen 			MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
22579960aa7cSTomi Valkeinen 			MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
22589960aa7cSTomi Valkeinen 		u8 data = FLD_GET(val, 15, 8);
22599960aa7cSTomi Valkeinen 		if (dsi->debug_read)
22609960aa7cSTomi Valkeinen 			DSSDBG("\t%s short response, 1 byte: %02x\n",
22619960aa7cSTomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
22629960aa7cSTomi Valkeinen 				"DCS", data);
22639960aa7cSTomi Valkeinen 
22649960aa7cSTomi Valkeinen 		if (buflen < 1) {
22659960aa7cSTomi Valkeinen 			r = -EIO;
22669960aa7cSTomi Valkeinen 			goto err;
22679960aa7cSTomi Valkeinen 		}
22689960aa7cSTomi Valkeinen 
22699960aa7cSTomi Valkeinen 		buf[0] = data;
22709960aa7cSTomi Valkeinen 
22719960aa7cSTomi Valkeinen 		return 1;
22729960aa7cSTomi Valkeinen 	} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
22739960aa7cSTomi Valkeinen 			MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
22749960aa7cSTomi Valkeinen 			MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
22759960aa7cSTomi Valkeinen 		u16 data = FLD_GET(val, 23, 8);
22769960aa7cSTomi Valkeinen 		if (dsi->debug_read)
22779960aa7cSTomi Valkeinen 			DSSDBG("\t%s short response, 2 byte: %04x\n",
22789960aa7cSTomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
22799960aa7cSTomi Valkeinen 				"DCS", data);
22809960aa7cSTomi Valkeinen 
22819960aa7cSTomi Valkeinen 		if (buflen < 2) {
22829960aa7cSTomi Valkeinen 			r = -EIO;
22839960aa7cSTomi Valkeinen 			goto err;
22849960aa7cSTomi Valkeinen 		}
22859960aa7cSTomi Valkeinen 
22869960aa7cSTomi Valkeinen 		buf[0] = data & 0xff;
22879960aa7cSTomi Valkeinen 		buf[1] = (data >> 8) & 0xff;
22889960aa7cSTomi Valkeinen 
22899960aa7cSTomi Valkeinen 		return 2;
22909960aa7cSTomi Valkeinen 	} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
22919960aa7cSTomi Valkeinen 			MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
22929960aa7cSTomi Valkeinen 			MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
22939960aa7cSTomi Valkeinen 		int w;
22949960aa7cSTomi Valkeinen 		int len = FLD_GET(val, 23, 8);
22959960aa7cSTomi Valkeinen 		if (dsi->debug_read)
22969960aa7cSTomi Valkeinen 			DSSDBG("\t%s long response, len %d\n",
22979960aa7cSTomi Valkeinen 				type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
22989960aa7cSTomi Valkeinen 				"DCS", len);
22999960aa7cSTomi Valkeinen 
23009960aa7cSTomi Valkeinen 		if (len > buflen) {
23019960aa7cSTomi Valkeinen 			r = -EIO;
23029960aa7cSTomi Valkeinen 			goto err;
23039960aa7cSTomi Valkeinen 		}
23049960aa7cSTomi Valkeinen 
23059960aa7cSTomi Valkeinen 		/* two byte checksum ends the packet, not included in len */
23069960aa7cSTomi Valkeinen 		for (w = 0; w < len + 2;) {
23079960aa7cSTomi Valkeinen 			int b;
23087093d6cdSLaurent Pinchart 			val = dsi_read_reg(dsi,
2309d8171145STomi Valkeinen 				DSI_VC_SHORT_PACKET_HEADER(vc));
23109960aa7cSTomi Valkeinen 			if (dsi->debug_read)
23119960aa7cSTomi Valkeinen 				DSSDBG("\t\t%02x %02x %02x %02x\n",
23129960aa7cSTomi Valkeinen 						(val >> 0) & 0xff,
23139960aa7cSTomi Valkeinen 						(val >> 8) & 0xff,
23149960aa7cSTomi Valkeinen 						(val >> 16) & 0xff,
23159960aa7cSTomi Valkeinen 						(val >> 24) & 0xff);
23169960aa7cSTomi Valkeinen 
23179960aa7cSTomi Valkeinen 			for (b = 0; b < 4; ++b) {
23189960aa7cSTomi Valkeinen 				if (w < len)
23199960aa7cSTomi Valkeinen 					buf[w] = (val >> (b * 8)) & 0xff;
23209960aa7cSTomi Valkeinen 				/* we discard the 2 byte checksum */
23219960aa7cSTomi Valkeinen 				++w;
23229960aa7cSTomi Valkeinen 			}
23239960aa7cSTomi Valkeinen 		}
23249960aa7cSTomi Valkeinen 
23259960aa7cSTomi Valkeinen 		return len;
23269960aa7cSTomi Valkeinen 	} else {
23279960aa7cSTomi Valkeinen 		DSSERR("\tunknown datatype 0x%02x\n", dt);
23289960aa7cSTomi Valkeinen 		r = -EIO;
23299960aa7cSTomi Valkeinen 		goto err;
23309960aa7cSTomi Valkeinen 	}
23319960aa7cSTomi Valkeinen 
23329960aa7cSTomi Valkeinen err:
2333d8171145STomi Valkeinen 	DSSERR("dsi_vc_read_rx_fifo(vc %d type %s) failed\n", vc,
23349960aa7cSTomi Valkeinen 		type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
23359960aa7cSTomi Valkeinen 
23369960aa7cSTomi Valkeinen 	return r;
23379960aa7cSTomi Valkeinen }
23389960aa7cSTomi Valkeinen 
dsi_vc_dcs_read(struct omap_dss_device * dssdev,int vc,const struct mipi_dsi_msg * msg)23395e430754STomi Valkeinen static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int vc,
2340952545a2SSebastian Reichel 			   const struct mipi_dsi_msg *msg)
23419960aa7cSTomi Valkeinen {
2342c068408eSLaurent Pinchart 	struct dsi_data *dsi = to_dsi_data(dssdev);
23431ed62538SSebastian Reichel 	u8 cmd = ((u8 *)msg->tx_buf)[0];
23449960aa7cSTomi Valkeinen 	int r;
23459960aa7cSTomi Valkeinen 
2346952545a2SSebastian Reichel 	if (dsi->debug_read)
2347d58eb31fSTomi Valkeinen 		DSSDBG("%s(vc %d, cmd %x)\n", __func__, vc, cmd);
2348952545a2SSebastian Reichel 
23495e430754STomi Valkeinen 	r = dsi_vc_send_short(dsi, vc, msg);
23509960aa7cSTomi Valkeinen 	if (r)
23519960aa7cSTomi Valkeinen 		goto err;
23529960aa7cSTomi Valkeinen 
2353d58eb31fSTomi Valkeinen 	r = dsi_vc_send_bta_sync(dssdev, vc);
23549960aa7cSTomi Valkeinen 	if (r)
23559960aa7cSTomi Valkeinen 		goto err;
23569960aa7cSTomi Valkeinen 
2357d58eb31fSTomi Valkeinen 	r = dsi_vc_read_rx_fifo(dsi, vc, msg->rx_buf, msg->rx_len,
23589960aa7cSTomi Valkeinen 		DSS_DSI_CONTENT_DCS);
23599960aa7cSTomi Valkeinen 	if (r < 0)
23609960aa7cSTomi Valkeinen 		goto err;
23619960aa7cSTomi Valkeinen 
2362952545a2SSebastian Reichel 	if (r != msg->rx_len) {
23639960aa7cSTomi Valkeinen 		r = -EIO;
23649960aa7cSTomi Valkeinen 		goto err;
23659960aa7cSTomi Valkeinen 	}
23669960aa7cSTomi Valkeinen 
23679960aa7cSTomi Valkeinen 	return 0;
23689960aa7cSTomi Valkeinen err:
2369d58eb31fSTomi Valkeinen 	DSSERR("%s(vc %d, cmd 0x%02x) failed\n", __func__,  vc, cmd);
23709960aa7cSTomi Valkeinen 	return r;
23719960aa7cSTomi Valkeinen }
23729960aa7cSTomi Valkeinen 
dsi_vc_generic_read(struct omap_dss_device * dssdev,int vc,const struct mipi_dsi_msg * msg)23735e430754STomi Valkeinen static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int vc,
2374952545a2SSebastian Reichel 			       const struct mipi_dsi_msg *msg)
23759960aa7cSTomi Valkeinen {
2376c068408eSLaurent Pinchart 	struct dsi_data *dsi = to_dsi_data(dssdev);
23779960aa7cSTomi Valkeinen 	int r;
23789960aa7cSTomi Valkeinen 
23795e430754STomi Valkeinen 	r = dsi_vc_send_short(dsi, vc, msg);
2380952545a2SSebastian Reichel 	if (r)
2381952545a2SSebastian Reichel 		goto err;
2382952545a2SSebastian Reichel 
2383d58eb31fSTomi Valkeinen 	r = dsi_vc_send_bta_sync(dssdev, vc);
2384952545a2SSebastian Reichel 	if (r)
2385952545a2SSebastian Reichel 		goto err;
2386952545a2SSebastian Reichel 
2387d58eb31fSTomi Valkeinen 	r = dsi_vc_read_rx_fifo(dsi, vc, msg->rx_buf, msg->rx_len,
23889960aa7cSTomi Valkeinen 		DSS_DSI_CONTENT_GENERIC);
23899960aa7cSTomi Valkeinen 	if (r < 0)
2390952545a2SSebastian Reichel 		goto err;
23919960aa7cSTomi Valkeinen 
2392952545a2SSebastian Reichel 	if (r != msg->rx_len) {
23939960aa7cSTomi Valkeinen 		r = -EIO;
2394952545a2SSebastian Reichel 		goto err;
23959960aa7cSTomi Valkeinen 	}
23969960aa7cSTomi Valkeinen 
23979960aa7cSTomi Valkeinen 	return 0;
2398952545a2SSebastian Reichel err:
2399d6a4bf45SLaurent Pinchart 	DSSERR("%s(vc %d, reqlen %zu) failed\n", __func__,  vc, msg->tx_len);
2400952545a2SSebastian Reichel 	return r;
24019960aa7cSTomi Valkeinen }
24029960aa7cSTomi Valkeinen 
dsi_set_lp_rx_timeout(struct dsi_data * dsi,unsigned int ticks,bool x4,bool x16)24037093d6cdSLaurent Pinchart static void dsi_set_lp_rx_timeout(struct dsi_data *dsi, unsigned int ticks,
24047093d6cdSLaurent Pinchart 				  bool x4, bool x16)
24059960aa7cSTomi Valkeinen {
24069960aa7cSTomi Valkeinen 	unsigned long fck;
24079960aa7cSTomi Valkeinen 	unsigned long total_ticks;
24089960aa7cSTomi Valkeinen 	u32 r;
24099960aa7cSTomi Valkeinen 
24109960aa7cSTomi Valkeinen 	BUG_ON(ticks > 0x1fff);
24119960aa7cSTomi Valkeinen 
24129960aa7cSTomi Valkeinen 	/* ticks in DSI_FCK */
24137093d6cdSLaurent Pinchart 	fck = dsi_fclk_rate(dsi);
24149960aa7cSTomi Valkeinen 
24157093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_TIMING2);
24169960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);	/* LP_RX_TO */
24179960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 14, 14);	/* LP_RX_TO_X16 */
24189960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x4 ? 1 : 0, 13, 13);	/* LP_RX_TO_X4 */
24199960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ticks, 12, 0);	/* LP_RX_COUNTER */
24207093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TIMING2, r);
24219960aa7cSTomi Valkeinen 
24229960aa7cSTomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
24239960aa7cSTomi Valkeinen 
24249960aa7cSTomi Valkeinen 	DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
24259960aa7cSTomi Valkeinen 			total_ticks,
24269960aa7cSTomi Valkeinen 			ticks, x4 ? " x4" : "", x16 ? " x16" : "",
24279960aa7cSTomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
24289960aa7cSTomi Valkeinen }
24299960aa7cSTomi Valkeinen 
dsi_set_ta_timeout(struct dsi_data * dsi,unsigned int ticks,bool x8,bool x16)24307093d6cdSLaurent Pinchart static void dsi_set_ta_timeout(struct dsi_data *dsi, unsigned int ticks,
24317093d6cdSLaurent Pinchart 			       bool x8, bool x16)
24329960aa7cSTomi Valkeinen {
24339960aa7cSTomi Valkeinen 	unsigned long fck;
24349960aa7cSTomi Valkeinen 	unsigned long total_ticks;
24359960aa7cSTomi Valkeinen 	u32 r;
24369960aa7cSTomi Valkeinen 
24379960aa7cSTomi Valkeinen 	BUG_ON(ticks > 0x1fff);
24389960aa7cSTomi Valkeinen 
24399960aa7cSTomi Valkeinen 	/* ticks in DSI_FCK */
24407093d6cdSLaurent Pinchart 	fck = dsi_fclk_rate(dsi);
24419960aa7cSTomi Valkeinen 
24427093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_TIMING1);
24439960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 31, 31);	/* TA_TO */
24449960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 30, 30);	/* TA_TO_X16 */
24459960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x8 ? 1 : 0, 29, 29);	/* TA_TO_X8 */
24469960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ticks, 28, 16);	/* TA_TO_COUNTER */
24477093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TIMING1, r);
24489960aa7cSTomi Valkeinen 
24499960aa7cSTomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
24509960aa7cSTomi Valkeinen 
24519960aa7cSTomi Valkeinen 	DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
24529960aa7cSTomi Valkeinen 			total_ticks,
24539960aa7cSTomi Valkeinen 			ticks, x8 ? " x8" : "", x16 ? " x16" : "",
24549960aa7cSTomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
24559960aa7cSTomi Valkeinen }
24569960aa7cSTomi Valkeinen 
dsi_set_stop_state_counter(struct dsi_data * dsi,unsigned int ticks,bool x4,bool x16)24577093d6cdSLaurent Pinchart static void dsi_set_stop_state_counter(struct dsi_data *dsi, unsigned int ticks,
24587093d6cdSLaurent Pinchart 				       bool x4, bool x16)
24599960aa7cSTomi Valkeinen {
24609960aa7cSTomi Valkeinen 	unsigned long fck;
24619960aa7cSTomi Valkeinen 	unsigned long total_ticks;
24629960aa7cSTomi Valkeinen 	u32 r;
24639960aa7cSTomi Valkeinen 
24649960aa7cSTomi Valkeinen 	BUG_ON(ticks > 0x1fff);
24659960aa7cSTomi Valkeinen 
24669960aa7cSTomi Valkeinen 	/* ticks in DSI_FCK */
24677093d6cdSLaurent Pinchart 	fck = dsi_fclk_rate(dsi);
24689960aa7cSTomi Valkeinen 
24697093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_TIMING1);
24709960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);	/* FORCE_TX_STOP_MODE_IO */
24719960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 14, 14);	/* STOP_STATE_X16_IO */
24729960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x4 ? 1 : 0, 13, 13);	/* STOP_STATE_X4_IO */
24739960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ticks, 12, 0);	/* STOP_STATE_COUNTER_IO */
24747093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TIMING1, r);
24759960aa7cSTomi Valkeinen 
24769960aa7cSTomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
24779960aa7cSTomi Valkeinen 
24789960aa7cSTomi Valkeinen 	DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
24799960aa7cSTomi Valkeinen 			total_ticks,
24809960aa7cSTomi Valkeinen 			ticks, x4 ? " x4" : "", x16 ? " x16" : "",
24819960aa7cSTomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
24829960aa7cSTomi Valkeinen }
24839960aa7cSTomi Valkeinen 
dsi_set_hs_tx_timeout(struct dsi_data * dsi,unsigned int ticks,bool x4,bool x16)24847093d6cdSLaurent Pinchart static void dsi_set_hs_tx_timeout(struct dsi_data *dsi, unsigned int ticks,
24857093d6cdSLaurent Pinchart 				  bool x4, bool x16)
24869960aa7cSTomi Valkeinen {
24879960aa7cSTomi Valkeinen 	unsigned long fck;
24889960aa7cSTomi Valkeinen 	unsigned long total_ticks;
24899960aa7cSTomi Valkeinen 	u32 r;
24909960aa7cSTomi Valkeinen 
24919960aa7cSTomi Valkeinen 	BUG_ON(ticks > 0x1fff);
24929960aa7cSTomi Valkeinen 
24939960aa7cSTomi Valkeinen 	/* ticks in TxByteClkHS */
24947093d6cdSLaurent Pinchart 	fck = dsi_get_txbyteclkhs(dsi);
24959960aa7cSTomi Valkeinen 
24967093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_TIMING2);
24979960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 31, 31);	/* HS_TX_TO */
24989960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x16 ? 1 : 0, 30, 30);	/* HS_TX_TO_X16 */
24999960aa7cSTomi Valkeinen 	r = FLD_MOD(r, x4 ? 1 : 0, 29, 29);	/* HS_TX_TO_X8 (4 really) */
25009960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ticks, 28, 16);	/* HS_TX_TO_COUNTER */
25017093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_TIMING2, r);
25029960aa7cSTomi Valkeinen 
25039960aa7cSTomi Valkeinen 	total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
25049960aa7cSTomi Valkeinen 
25059960aa7cSTomi Valkeinen 	DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
25069960aa7cSTomi Valkeinen 			total_ticks,
25079960aa7cSTomi Valkeinen 			ticks, x4 ? " x4" : "", x16 ? " x16" : "",
25089960aa7cSTomi Valkeinen 			(total_ticks * 1000) / (fck / 1000 / 1000));
25099960aa7cSTomi Valkeinen }
25109960aa7cSTomi Valkeinen 
dsi_config_vp_num_line_buffers(struct dsi_data * dsi)25117093d6cdSLaurent Pinchart static void dsi_config_vp_num_line_buffers(struct dsi_data *dsi)
25129960aa7cSTomi Valkeinen {
25139960aa7cSTomi Valkeinen 	int num_line_buffers;
25149960aa7cSTomi Valkeinen 
25159960aa7cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
2516d0103cebSSebastian Reichel 		int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
25177d39e59bSLaurent Pinchart 		const struct videomode *vm = &dsi->vm;
25189960aa7cSTomi Valkeinen 		/*
25199960aa7cSTomi Valkeinen 		 * Don't use line buffers if width is greater than the video
25209960aa7cSTomi Valkeinen 		 * port's line buffer size
25219960aa7cSTomi Valkeinen 		 */
2522da11bbbbSPeter Ujfalusi 		if (dsi->line_buffer_size <= vm->hactive * bpp / 8)
25239960aa7cSTomi Valkeinen 			num_line_buffers = 0;
25249960aa7cSTomi Valkeinen 		else
25259960aa7cSTomi Valkeinen 			num_line_buffers = 2;
25269960aa7cSTomi Valkeinen 	} else {
25279960aa7cSTomi Valkeinen 		/* Use maximum number of line buffers in command mode */
25289960aa7cSTomi Valkeinen 		num_line_buffers = 2;
25299960aa7cSTomi Valkeinen 	}
25309960aa7cSTomi Valkeinen 
25319960aa7cSTomi Valkeinen 	/* LINE_BUFFER */
25327093d6cdSLaurent Pinchart 	REG_FLD_MOD(dsi, DSI_CTRL, num_line_buffers, 13, 12);
25339960aa7cSTomi Valkeinen }
25349960aa7cSTomi Valkeinen 
dsi_config_vp_sync_events(struct dsi_data * dsi)25357093d6cdSLaurent Pinchart static void dsi_config_vp_sync_events(struct dsi_data *dsi)
25369960aa7cSTomi Valkeinen {
25379960aa7cSTomi Valkeinen 	bool sync_end;
25389960aa7cSTomi Valkeinen 	u32 r;
25399960aa7cSTomi Valkeinen 
25409960aa7cSTomi Valkeinen 	if (dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE)
25419960aa7cSTomi Valkeinen 		sync_end = true;
25429960aa7cSTomi Valkeinen 	else
25439960aa7cSTomi Valkeinen 		sync_end = false;
25449960aa7cSTomi Valkeinen 
25457093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CTRL);
25469960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 9, 9);		/* VP_DE_POL */
25479960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 10, 10);		/* VP_HSYNC_POL */
25489960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 11, 11);		/* VP_VSYNC_POL */
25499960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 15, 15);		/* VP_VSYNC_START */
25509960aa7cSTomi Valkeinen 	r = FLD_MOD(r, sync_end, 16, 16);	/* VP_VSYNC_END */
25519960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 17, 17);		/* VP_HSYNC_START */
25529960aa7cSTomi Valkeinen 	r = FLD_MOD(r, sync_end, 18, 18);	/* VP_HSYNC_END */
25537093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_CTRL, r);
25549960aa7cSTomi Valkeinen }
25559960aa7cSTomi Valkeinen 
dsi_config_blanking_modes(struct dsi_data * dsi)25567093d6cdSLaurent Pinchart static void dsi_config_blanking_modes(struct dsi_data *dsi)
25579960aa7cSTomi Valkeinen {
25589960aa7cSTomi Valkeinen 	int blanking_mode = dsi->vm_timings.blanking_mode;
25599960aa7cSTomi Valkeinen 	int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode;
25609960aa7cSTomi Valkeinen 	int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode;
25619960aa7cSTomi Valkeinen 	int hsa_blanking_mode = dsi->vm_timings.hsa_blanking_mode;
25629960aa7cSTomi Valkeinen 	u32 r;
25639960aa7cSTomi Valkeinen 
25649960aa7cSTomi Valkeinen 	/*
25659960aa7cSTomi Valkeinen 	 * 0 = TX FIFO packets sent or LPS in corresponding blanking periods
25669960aa7cSTomi Valkeinen 	 * 1 = Long blanking packets are sent in corresponding blanking periods
25679960aa7cSTomi Valkeinen 	 */
25687093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CTRL);
25699960aa7cSTomi Valkeinen 	r = FLD_MOD(r, blanking_mode, 20, 20);		/* BLANKING_MODE */
25709960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hfp_blanking_mode, 21, 21);	/* HFP_BLANKING */
25719960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hbp_blanking_mode, 22, 22);	/* HBP_BLANKING */
25729960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hsa_blanking_mode, 23, 23);	/* HSA_BLANKING */
25737093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_CTRL, r);
25749960aa7cSTomi Valkeinen }
25759960aa7cSTomi Valkeinen 
25769960aa7cSTomi Valkeinen /*
25779960aa7cSTomi Valkeinen  * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3
25789960aa7cSTomi Valkeinen  * results in maximum transition time for data and clock lanes to enter and
25799960aa7cSTomi Valkeinen  * exit HS mode. Hence, this is the scenario where the least amount of command
25809960aa7cSTomi Valkeinen  * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS
25819960aa7cSTomi Valkeinen  * clock cycles that can be used to interleave command mode data in HS so that
25829960aa7cSTomi Valkeinen  * all scenarios are satisfied.
25839960aa7cSTomi Valkeinen  */
dsi_compute_interleave_hs(int blank,bool ddr_alwon,int enter_hs,int exit_hs,int exiths_clk,int ddr_pre,int ddr_post)25849960aa7cSTomi Valkeinen static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs,
25859960aa7cSTomi Valkeinen 		int exit_hs, int exiths_clk, int ddr_pre, int ddr_post)
25869960aa7cSTomi Valkeinen {
25879960aa7cSTomi Valkeinen 	int transition;
25889960aa7cSTomi Valkeinen 
25899960aa7cSTomi Valkeinen 	/*
25909960aa7cSTomi Valkeinen 	 * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition
25919960aa7cSTomi Valkeinen 	 * time of data lanes only, if it isn't set, we need to consider HS
25929960aa7cSTomi Valkeinen 	 * transition time of both data and clock lanes. HS transition time
25939960aa7cSTomi Valkeinen 	 * of Scenario 3 is considered.
25949960aa7cSTomi Valkeinen 	 */
25959960aa7cSTomi Valkeinen 	if (ddr_alwon) {
25969960aa7cSTomi Valkeinen 		transition = enter_hs + exit_hs + max(enter_hs, 2) + 1;
25979960aa7cSTomi Valkeinen 	} else {
25989960aa7cSTomi Valkeinen 		int trans1, trans2;
25999960aa7cSTomi Valkeinen 		trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1;
26009960aa7cSTomi Valkeinen 		trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre +
26019960aa7cSTomi Valkeinen 				enter_hs + 1;
26029960aa7cSTomi Valkeinen 		transition = max(trans1, trans2);
26039960aa7cSTomi Valkeinen 	}
26049960aa7cSTomi Valkeinen 
26059960aa7cSTomi Valkeinen 	return blank > transition ? blank - transition : 0;
26069960aa7cSTomi Valkeinen }
26079960aa7cSTomi Valkeinen 
26089960aa7cSTomi Valkeinen /*
26099960aa7cSTomi Valkeinen  * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1
26109960aa7cSTomi Valkeinen  * results in maximum transition time for data lanes to enter and exit LP mode.
26119960aa7cSTomi Valkeinen  * Hence, this is the scenario where the least amount of command mode data can
26129960aa7cSTomi Valkeinen  * be interleaved. We program the minimum amount of bytes that can be
26139960aa7cSTomi Valkeinen  * interleaved in LP so that all scenarios are satisfied.
26149960aa7cSTomi Valkeinen  */
dsi_compute_interleave_lp(int blank,int enter_hs,int exit_hs,int lp_clk_div,int tdsi_fclk)26159960aa7cSTomi Valkeinen static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs,
26169960aa7cSTomi Valkeinen 		int lp_clk_div, int tdsi_fclk)
26179960aa7cSTomi Valkeinen {
26189960aa7cSTomi Valkeinen 	int trans_lp;	/* time required for a LP transition, in TXBYTECLKHS */
26199960aa7cSTomi Valkeinen 	int tlp_avail;	/* time left for interleaving commands, in CLKIN4DDR */
26209960aa7cSTomi Valkeinen 	int ttxclkesc;	/* period of LP transmit escape clock, in CLKIN4DDR */
26219960aa7cSTomi Valkeinen 	int thsbyte_clk = 16;	/* Period of TXBYTECLKHS clock, in CLKIN4DDR */
26229960aa7cSTomi Valkeinen 	int lp_inter;	/* cmd mode data that can be interleaved, in bytes */
26239960aa7cSTomi Valkeinen 
26249960aa7cSTomi Valkeinen 	/* maximum LP transition time according to Scenario 1 */
26259960aa7cSTomi Valkeinen 	trans_lp = exit_hs + max(enter_hs, 2) + 1;
26269960aa7cSTomi Valkeinen 
26279960aa7cSTomi Valkeinen 	/* CLKIN4DDR = 16 * TXBYTECLKHS */
26289960aa7cSTomi Valkeinen 	tlp_avail = thsbyte_clk * (blank - trans_lp);
26299960aa7cSTomi Valkeinen 
26309960aa7cSTomi Valkeinen 	ttxclkesc = tdsi_fclk * lp_clk_div;
26319960aa7cSTomi Valkeinen 
26329960aa7cSTomi Valkeinen 	lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc -
26339960aa7cSTomi Valkeinen 			26) / 16;
26349960aa7cSTomi Valkeinen 
26359960aa7cSTomi Valkeinen 	return max(lp_inter, 0);
26369960aa7cSTomi Valkeinen }
26379960aa7cSTomi Valkeinen 
dsi_config_cmd_mode_interleaving(struct dsi_data * dsi)26387093d6cdSLaurent Pinchart static void dsi_config_cmd_mode_interleaving(struct dsi_data *dsi)
26399960aa7cSTomi Valkeinen {
26409960aa7cSTomi Valkeinen 	int blanking_mode;
26419960aa7cSTomi Valkeinen 	int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode;
26429960aa7cSTomi Valkeinen 	int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div;
26439960aa7cSTomi Valkeinen 	int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat;
26449960aa7cSTomi Valkeinen 	int tclk_trail, ths_exit, exiths_clk;
26459960aa7cSTomi Valkeinen 	bool ddr_alwon;
26467d39e59bSLaurent Pinchart 	const struct videomode *vm = &dsi->vm;
2647d0103cebSSebastian Reichel 	int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
26489960aa7cSTomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
26499960aa7cSTomi Valkeinen 	int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1;
26509960aa7cSTomi Valkeinen 	int hsa_interleave_hs = 0, hsa_interleave_lp = 0;
26519960aa7cSTomi Valkeinen 	int hfp_interleave_hs = 0, hfp_interleave_lp = 0;
26529960aa7cSTomi Valkeinen 	int hbp_interleave_hs = 0, hbp_interleave_lp = 0;
26539960aa7cSTomi Valkeinen 	int bl_interleave_hs = 0, bl_interleave_lp = 0;
26549960aa7cSTomi Valkeinen 	u32 r;
26559960aa7cSTomi Valkeinen 
26567093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CTRL);
26579960aa7cSTomi Valkeinen 	blanking_mode = FLD_GET(r, 20, 20);
26589960aa7cSTomi Valkeinen 	hfp_blanking_mode = FLD_GET(r, 21, 21);
26599960aa7cSTomi Valkeinen 	hbp_blanking_mode = FLD_GET(r, 22, 22);
26609960aa7cSTomi Valkeinen 	hsa_blanking_mode = FLD_GET(r, 23, 23);
26619960aa7cSTomi Valkeinen 
26627093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_VM_TIMING1);
26639960aa7cSTomi Valkeinen 	hbp = FLD_GET(r, 11, 0);
26649960aa7cSTomi Valkeinen 	hfp = FLD_GET(r, 23, 12);
26659960aa7cSTomi Valkeinen 	hsa = FLD_GET(r, 31, 24);
26669960aa7cSTomi Valkeinen 
26677093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CLK_TIMING);
26689960aa7cSTomi Valkeinen 	ddr_clk_post = FLD_GET(r, 7, 0);
26699960aa7cSTomi Valkeinen 	ddr_clk_pre = FLD_GET(r, 15, 8);
26709960aa7cSTomi Valkeinen 
26717093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_VM_TIMING7);
26729960aa7cSTomi Valkeinen 	exit_hs_mode_lat = FLD_GET(r, 15, 0);
26739960aa7cSTomi Valkeinen 	enter_hs_mode_lat = FLD_GET(r, 31, 16);
26749960aa7cSTomi Valkeinen 
26757093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CLK_CTRL);
26769960aa7cSTomi Valkeinen 	lp_clk_div = FLD_GET(r, 12, 0);
26779960aa7cSTomi Valkeinen 	ddr_alwon = FLD_GET(r, 13, 13);
26789960aa7cSTomi Valkeinen 
26797093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0);
26809960aa7cSTomi Valkeinen 	ths_exit = FLD_GET(r, 7, 0);
26819960aa7cSTomi Valkeinen 
26827093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
26839960aa7cSTomi Valkeinen 	tclk_trail = FLD_GET(r, 15, 8);
26849960aa7cSTomi Valkeinen 
26859960aa7cSTomi Valkeinen 	exiths_clk = ths_exit + tclk_trail;
26869960aa7cSTomi Valkeinen 
2687da11bbbbSPeter Ujfalusi 	width_bytes = DIV_ROUND_UP(vm->hactive * bpp, 8);
26889960aa7cSTomi Valkeinen 	bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl);
26899960aa7cSTomi Valkeinen 
26909960aa7cSTomi Valkeinen 	if (!hsa_blanking_mode) {
26919960aa7cSTomi Valkeinen 		hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon,
26929960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
26939960aa7cSTomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
26949960aa7cSTomi Valkeinen 		hsa_interleave_lp = dsi_compute_interleave_lp(hsa,
26959960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
26969960aa7cSTomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
26979960aa7cSTomi Valkeinen 	}
26989960aa7cSTomi Valkeinen 
26999960aa7cSTomi Valkeinen 	if (!hfp_blanking_mode) {
27009960aa7cSTomi Valkeinen 		hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon,
27019960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
27029960aa7cSTomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
27039960aa7cSTomi Valkeinen 		hfp_interleave_lp = dsi_compute_interleave_lp(hfp,
27049960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
27059960aa7cSTomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
27069960aa7cSTomi Valkeinen 	}
27079960aa7cSTomi Valkeinen 
27089960aa7cSTomi Valkeinen 	if (!hbp_blanking_mode) {
27099960aa7cSTomi Valkeinen 		hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon,
27109960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
27119960aa7cSTomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
27129960aa7cSTomi Valkeinen 
27139960aa7cSTomi Valkeinen 		hbp_interleave_lp = dsi_compute_interleave_lp(hbp,
27149960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
27159960aa7cSTomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
27169960aa7cSTomi Valkeinen 	}
27179960aa7cSTomi Valkeinen 
27189960aa7cSTomi Valkeinen 	if (!blanking_mode) {
27199960aa7cSTomi Valkeinen 		bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon,
27209960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
27219960aa7cSTomi Valkeinen 					exiths_clk, ddr_clk_pre, ddr_clk_post);
27229960aa7cSTomi Valkeinen 
27239960aa7cSTomi Valkeinen 		bl_interleave_lp = dsi_compute_interleave_lp(bllp,
27249960aa7cSTomi Valkeinen 					enter_hs_mode_lat, exit_hs_mode_lat,
27259960aa7cSTomi Valkeinen 					lp_clk_div, dsi_fclk_hsdiv);
27269960aa7cSTomi Valkeinen 	}
27279960aa7cSTomi Valkeinen 
27289960aa7cSTomi Valkeinen 	DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n",
27299960aa7cSTomi Valkeinen 		hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs,
27309960aa7cSTomi Valkeinen 		bl_interleave_hs);
27319960aa7cSTomi Valkeinen 
27329960aa7cSTomi Valkeinen 	DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n",
27339960aa7cSTomi Valkeinen 		hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp,
27349960aa7cSTomi Valkeinen 		bl_interleave_lp);
27359960aa7cSTomi Valkeinen 
27367093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_VM_TIMING4);
27379960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hsa_interleave_hs, 23, 16);
27389960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hfp_interleave_hs, 15, 8);
27399960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hbp_interleave_hs, 7, 0);
27407093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_VM_TIMING4, r);
27419960aa7cSTomi Valkeinen 
27427093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_VM_TIMING5);
27439960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hsa_interleave_lp, 23, 16);
27449960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hfp_interleave_lp, 15, 8);
27459960aa7cSTomi Valkeinen 	r = FLD_MOD(r, hbp_interleave_lp, 7, 0);
27467093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_VM_TIMING5, r);
27479960aa7cSTomi Valkeinen 
27487093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_VM_TIMING6);
27499960aa7cSTomi Valkeinen 	r = FLD_MOD(r, bl_interleave_hs, 31, 15);
27509960aa7cSTomi Valkeinen 	r = FLD_MOD(r, bl_interleave_lp, 16, 0);
27517093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_VM_TIMING6, r);
27529960aa7cSTomi Valkeinen }
27539960aa7cSTomi Valkeinen 
dsi_proto_config(struct dsi_data * dsi)27547093d6cdSLaurent Pinchart static int dsi_proto_config(struct dsi_data *dsi)
27559960aa7cSTomi Valkeinen {
27569960aa7cSTomi Valkeinen 	u32 r;
27579960aa7cSTomi Valkeinen 	int buswidth = 0;
27589960aa7cSTomi Valkeinen 
27597093d6cdSLaurent Pinchart 	dsi_config_tx_fifo(dsi, DSI_FIFO_SIZE_32,
27609960aa7cSTomi Valkeinen 			DSI_FIFO_SIZE_32,
27619960aa7cSTomi Valkeinen 			DSI_FIFO_SIZE_32,
27629960aa7cSTomi Valkeinen 			DSI_FIFO_SIZE_32);
27639960aa7cSTomi Valkeinen 
27647093d6cdSLaurent Pinchart 	dsi_config_rx_fifo(dsi, DSI_FIFO_SIZE_32,
27659960aa7cSTomi Valkeinen 			DSI_FIFO_SIZE_32,
27669960aa7cSTomi Valkeinen 			DSI_FIFO_SIZE_32,
27679960aa7cSTomi Valkeinen 			DSI_FIFO_SIZE_32);
27689960aa7cSTomi Valkeinen 
27699960aa7cSTomi Valkeinen 	/* XXX what values for the timeouts? */
27707093d6cdSLaurent Pinchart 	dsi_set_stop_state_counter(dsi, 0x1000, false, false);
27717093d6cdSLaurent Pinchart 	dsi_set_ta_timeout(dsi, 0x1fff, true, true);
27727093d6cdSLaurent Pinchart 	dsi_set_lp_rx_timeout(dsi, 0x1fff, true, true);
27737093d6cdSLaurent Pinchart 	dsi_set_hs_tx_timeout(dsi, 0x1fff, true, true);
27749960aa7cSTomi Valkeinen 
2775d0103cebSSebastian Reichel 	switch (mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt)) {
27769960aa7cSTomi Valkeinen 	case 16:
27779960aa7cSTomi Valkeinen 		buswidth = 0;
27789960aa7cSTomi Valkeinen 		break;
27799960aa7cSTomi Valkeinen 	case 18:
27809960aa7cSTomi Valkeinen 		buswidth = 1;
27819960aa7cSTomi Valkeinen 		break;
27829960aa7cSTomi Valkeinen 	case 24:
27839960aa7cSTomi Valkeinen 		buswidth = 2;
27849960aa7cSTomi Valkeinen 		break;
27859960aa7cSTomi Valkeinen 	default:
27869960aa7cSTomi Valkeinen 		BUG();
27879960aa7cSTomi Valkeinen 		return -EINVAL;
27889960aa7cSTomi Valkeinen 	}
27899960aa7cSTomi Valkeinen 
27907093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CTRL);
27919960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 1, 1);	/* CS_RX_EN */
27929960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 2, 2);	/* ECC_RX_EN */
27939960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 3, 3);	/* TX_FIFO_ARBITRATION */
27949960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 4, 4);	/* VP_CLK_RATIO, always 1, see errata*/
27959960aa7cSTomi Valkeinen 	r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
27969960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 0, 8, 8);	/* VP_CLK_POL */
27979960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 14, 14);	/* TRIGGER_RESET_MODE */
27989960aa7cSTomi Valkeinen 	r = FLD_MOD(r, 1, 19, 19);	/* EOT_ENABLE */
279944d8ca10SLaurent Pinchart 	if (!(dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC)) {
28009960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 1, 24, 24);	/* DCS_CMD_ENABLE */
28019960aa7cSTomi Valkeinen 		/* DCS_CMD_CODE, 1=start, 0=continue */
28029960aa7cSTomi Valkeinen 		r = FLD_MOD(r, 0, 25, 25);
28039960aa7cSTomi Valkeinen 	}
28049960aa7cSTomi Valkeinen 
28057093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_CTRL, r);
28069960aa7cSTomi Valkeinen 
28077093d6cdSLaurent Pinchart 	dsi_config_vp_num_line_buffers(dsi);
28089960aa7cSTomi Valkeinen 
28099960aa7cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
28107093d6cdSLaurent Pinchart 		dsi_config_vp_sync_events(dsi);
28117093d6cdSLaurent Pinchart 		dsi_config_blanking_modes(dsi);
28127093d6cdSLaurent Pinchart 		dsi_config_cmd_mode_interleaving(dsi);
28139960aa7cSTomi Valkeinen 	}
28149960aa7cSTomi Valkeinen 
28157093d6cdSLaurent Pinchart 	dsi_vc_initial_config(dsi, 0);
28167093d6cdSLaurent Pinchart 	dsi_vc_initial_config(dsi, 1);
28177093d6cdSLaurent Pinchart 	dsi_vc_initial_config(dsi, 2);
28187093d6cdSLaurent Pinchart 	dsi_vc_initial_config(dsi, 3);
28199960aa7cSTomi Valkeinen 
28209960aa7cSTomi Valkeinen 	return 0;
28219960aa7cSTomi Valkeinen }
28229960aa7cSTomi Valkeinen 
dsi_proto_timings(struct dsi_data * dsi)28237093d6cdSLaurent Pinchart static void dsi_proto_timings(struct dsi_data *dsi)
28249960aa7cSTomi Valkeinen {
282583e3b8a9Szhengbin 	unsigned int tlpx, tclk_zero, tclk_prepare;
2826d11e5c82SLaurent Pinchart 	unsigned int tclk_pre, tclk_post;
2827d11e5c82SLaurent Pinchart 	unsigned int ths_prepare, ths_prepare_ths_zero, ths_zero;
2828d11e5c82SLaurent Pinchart 	unsigned int ths_trail, ths_exit;
2829d11e5c82SLaurent Pinchart 	unsigned int ddr_clk_pre, ddr_clk_post;
2830d11e5c82SLaurent Pinchart 	unsigned int enter_hs_mode_lat, exit_hs_mode_lat;
2831d11e5c82SLaurent Pinchart 	unsigned int ths_eot;
28329960aa7cSTomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
28339960aa7cSTomi Valkeinen 	u32 r;
28349960aa7cSTomi Valkeinen 
28357093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0);
28369960aa7cSTomi Valkeinen 	ths_prepare = FLD_GET(r, 31, 24);
28379960aa7cSTomi Valkeinen 	ths_prepare_ths_zero = FLD_GET(r, 23, 16);
28389960aa7cSTomi Valkeinen 	ths_zero = ths_prepare_ths_zero - ths_prepare;
28399960aa7cSTomi Valkeinen 	ths_trail = FLD_GET(r, 15, 8);
28409960aa7cSTomi Valkeinen 	ths_exit = FLD_GET(r, 7, 0);
28419960aa7cSTomi Valkeinen 
28427093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1);
28439960aa7cSTomi Valkeinen 	tlpx = FLD_GET(r, 20, 16) * 2;
28449960aa7cSTomi Valkeinen 	tclk_zero = FLD_GET(r, 7, 0);
28459960aa7cSTomi Valkeinen 
28467093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2);
28479960aa7cSTomi Valkeinen 	tclk_prepare = FLD_GET(r, 7, 0);
28489960aa7cSTomi Valkeinen 
28499960aa7cSTomi Valkeinen 	/* min 8*UI */
28509960aa7cSTomi Valkeinen 	tclk_pre = 20;
28519960aa7cSTomi Valkeinen 	/* min 60ns + 52*UI */
28527093d6cdSLaurent Pinchart 	tclk_post = ns2ddr(dsi, 60) + 26;
28539960aa7cSTomi Valkeinen 
28549960aa7cSTomi Valkeinen 	ths_eot = DIV_ROUND_UP(4, ndl);
28559960aa7cSTomi Valkeinen 
28569960aa7cSTomi Valkeinen 	ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
28579960aa7cSTomi Valkeinen 			4);
28589960aa7cSTomi Valkeinen 	ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
28599960aa7cSTomi Valkeinen 
28609960aa7cSTomi Valkeinen 	BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
28619960aa7cSTomi Valkeinen 	BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
28629960aa7cSTomi Valkeinen 
28637093d6cdSLaurent Pinchart 	r = dsi_read_reg(dsi, DSI_CLK_TIMING);
28649960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ddr_clk_pre, 15, 8);
28659960aa7cSTomi Valkeinen 	r = FLD_MOD(r, ddr_clk_post, 7, 0);
28667093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_CLK_TIMING, r);
28679960aa7cSTomi Valkeinen 
28689960aa7cSTomi Valkeinen 	DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
28699960aa7cSTomi Valkeinen 			ddr_clk_pre,
28709960aa7cSTomi Valkeinen 			ddr_clk_post);
28719960aa7cSTomi Valkeinen 
28729960aa7cSTomi Valkeinen 	enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
28739960aa7cSTomi Valkeinen 		DIV_ROUND_UP(ths_prepare, 4) +
28749960aa7cSTomi Valkeinen 		DIV_ROUND_UP(ths_zero + 3, 4);
28759960aa7cSTomi Valkeinen 
28769960aa7cSTomi Valkeinen 	exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
28779960aa7cSTomi Valkeinen 
28789960aa7cSTomi Valkeinen 	r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
28799960aa7cSTomi Valkeinen 		FLD_VAL(exit_hs_mode_lat, 15, 0);
28807093d6cdSLaurent Pinchart 	dsi_write_reg(dsi, DSI_VM_TIMING7, r);
28819960aa7cSTomi Valkeinen 
28829960aa7cSTomi Valkeinen 	DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
28839960aa7cSTomi Valkeinen 			enter_hs_mode_lat, exit_hs_mode_lat);
28849960aa7cSTomi Valkeinen 
28859960aa7cSTomi Valkeinen 	 if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
28869960aa7cSTomi Valkeinen 		/* TODO: Implement a video mode check_timings function */
28879960aa7cSTomi Valkeinen 		int hsa = dsi->vm_timings.hsa;
28889960aa7cSTomi Valkeinen 		int hfp = dsi->vm_timings.hfp;
28899960aa7cSTomi Valkeinen 		int hbp = dsi->vm_timings.hbp;
28909960aa7cSTomi Valkeinen 		int vsa = dsi->vm_timings.vsa;
28919960aa7cSTomi Valkeinen 		int vfp = dsi->vm_timings.vfp;
28929960aa7cSTomi Valkeinen 		int vbp = dsi->vm_timings.vbp;
28939960aa7cSTomi Valkeinen 		int window_sync = dsi->vm_timings.window_sync;
28949960aa7cSTomi Valkeinen 		bool hsync_end;
28957d39e59bSLaurent Pinchart 		const struct videomode *vm = &dsi->vm;
2896d0103cebSSebastian Reichel 		int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
28979960aa7cSTomi Valkeinen 		int tl, t_he, width_bytes;
28989960aa7cSTomi Valkeinen 
28999960aa7cSTomi Valkeinen 		hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE;
29009960aa7cSTomi Valkeinen 		t_he = hsync_end ?
29019960aa7cSTomi Valkeinen 			((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
29029960aa7cSTomi Valkeinen 
2903da11bbbbSPeter Ujfalusi 		width_bytes = DIV_ROUND_UP(vm->hactive * bpp, 8);
29049960aa7cSTomi Valkeinen 
29059960aa7cSTomi Valkeinen 		/* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
29069960aa7cSTomi Valkeinen 		tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
29079960aa7cSTomi Valkeinen 			DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
29089960aa7cSTomi Valkeinen 
29099960aa7cSTomi Valkeinen 		DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
29109960aa7cSTomi Valkeinen 			hfp, hsync_end ? hsa : 0, tl);
29119960aa7cSTomi Valkeinen 		DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
2912da11bbbbSPeter Ujfalusi 			vsa, vm->vactive);
29139960aa7cSTomi Valkeinen 
29147093d6cdSLaurent Pinchart 		r = dsi_read_reg(dsi, DSI_VM_TIMING1);
29159960aa7cSTomi Valkeinen 		r = FLD_MOD(r, hbp, 11, 0);	/* HBP */
29169960aa7cSTomi Valkeinen 		r = FLD_MOD(r, hfp, 23, 12);	/* HFP */
29179960aa7cSTomi Valkeinen 		r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24);	/* HSA */
29187093d6cdSLaurent Pinchart 		dsi_write_reg(dsi, DSI_VM_TIMING1, r);
29199960aa7cSTomi Valkeinen 
29207093d6cdSLaurent Pinchart 		r = dsi_read_reg(dsi, DSI_VM_TIMING2);
29219960aa7cSTomi Valkeinen 		r = FLD_MOD(r, vbp, 7, 0);	/* VBP */
29229960aa7cSTomi Valkeinen 		r = FLD_MOD(r, vfp, 15, 8);	/* VFP */
29239960aa7cSTomi Valkeinen 		r = FLD_MOD(r, vsa, 23, 16);	/* VSA */
29249960aa7cSTomi Valkeinen 		r = FLD_MOD(r, window_sync, 27, 24);	/* WINDOW_SYNC */
29257093d6cdSLaurent Pinchart 		dsi_write_reg(dsi, DSI_VM_TIMING2, r);
29269960aa7cSTomi Valkeinen 
29277093d6cdSLaurent Pinchart 		r = dsi_read_reg(dsi, DSI_VM_TIMING3);
2928da11bbbbSPeter Ujfalusi 		r = FLD_MOD(r, vm->vactive, 14, 0);	/* VACT */
29299960aa7cSTomi Valkeinen 		r = FLD_MOD(r, tl, 31, 16);		/* TL */
29307093d6cdSLaurent Pinchart 		dsi_write_reg(dsi, DSI_VM_TIMING3, r);
29319960aa7cSTomi Valkeinen 	}
29329960aa7cSTomi Valkeinen }
29339960aa7cSTomi Valkeinen 
dsi_configure_pins(struct dsi_data * dsi,int num_pins,const u32 * pins)2934578739e8SSebastian Reichel static int dsi_configure_pins(struct dsi_data *dsi,
2935578739e8SSebastian Reichel 		int num_pins, const u32 *pins)
29369960aa7cSTomi Valkeinen {
29379960aa7cSTomi Valkeinen 	struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
29389960aa7cSTomi Valkeinen 	int num_lanes;
29399960aa7cSTomi Valkeinen 	int i;
29409960aa7cSTomi Valkeinen 
29419960aa7cSTomi Valkeinen 	static const enum dsi_lane_function functions[] = {
29429960aa7cSTomi Valkeinen 		DSI_LANE_CLK,
29439960aa7cSTomi Valkeinen 		DSI_LANE_DATA1,
29449960aa7cSTomi Valkeinen 		DSI_LANE_DATA2,
29459960aa7cSTomi Valkeinen 		DSI_LANE_DATA3,
29469960aa7cSTomi Valkeinen 		DSI_LANE_DATA4,
29479960aa7cSTomi Valkeinen 	};
29489960aa7cSTomi Valkeinen 
29499960aa7cSTomi Valkeinen 	if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2
29509960aa7cSTomi Valkeinen 			|| num_pins % 2 != 0)
29519960aa7cSTomi Valkeinen 		return -EINVAL;
29529960aa7cSTomi Valkeinen 
29539960aa7cSTomi Valkeinen 	for (i = 0; i < DSI_MAX_NR_LANES; ++i)
29549960aa7cSTomi Valkeinen 		lanes[i].function = DSI_LANE_UNUSED;
29559960aa7cSTomi Valkeinen 
29569960aa7cSTomi Valkeinen 	num_lanes = 0;
29579960aa7cSTomi Valkeinen 
29589960aa7cSTomi Valkeinen 	for (i = 0; i < num_pins; i += 2) {
29599960aa7cSTomi Valkeinen 		u8 lane, pol;
2960578739e8SSebastian Reichel 		u32 dx, dy;
29619960aa7cSTomi Valkeinen 
29629960aa7cSTomi Valkeinen 		dx = pins[i];
29639960aa7cSTomi Valkeinen 		dy = pins[i + 1];
29649960aa7cSTomi Valkeinen 
2965578739e8SSebastian Reichel 		if (dx >= dsi->num_lanes_supported * 2)
29669960aa7cSTomi Valkeinen 			return -EINVAL;
29679960aa7cSTomi Valkeinen 
2968578739e8SSebastian Reichel 		if (dy >= dsi->num_lanes_supported * 2)
29699960aa7cSTomi Valkeinen 			return -EINVAL;
29709960aa7cSTomi Valkeinen 
29719960aa7cSTomi Valkeinen 		if (dx & 1) {
29729960aa7cSTomi Valkeinen 			if (dy != dx - 1)
29739960aa7cSTomi Valkeinen 				return -EINVAL;
29749960aa7cSTomi Valkeinen 			pol = 1;
29759960aa7cSTomi Valkeinen 		} else {
29769960aa7cSTomi Valkeinen 			if (dy != dx + 1)
29779960aa7cSTomi Valkeinen 				return -EINVAL;
29789960aa7cSTomi Valkeinen 			pol = 0;
29799960aa7cSTomi Valkeinen 		}
29809960aa7cSTomi Valkeinen 
29819960aa7cSTomi Valkeinen 		lane = dx / 2;
29829960aa7cSTomi Valkeinen 
29839960aa7cSTomi Valkeinen 		lanes[lane].function = functions[i / 2];
29849960aa7cSTomi Valkeinen 		lanes[lane].polarity = pol;
29859960aa7cSTomi Valkeinen 		num_lanes++;
29869960aa7cSTomi Valkeinen 	}
29879960aa7cSTomi Valkeinen 
29889960aa7cSTomi Valkeinen 	memcpy(dsi->lanes, lanes, sizeof(dsi->lanes));
29899960aa7cSTomi Valkeinen 	dsi->num_lanes_used = num_lanes;
29909960aa7cSTomi Valkeinen 
29919960aa7cSTomi Valkeinen 	return 0;
29929960aa7cSTomi Valkeinen }
29939960aa7cSTomi Valkeinen 
dsi_enable_video_mode(struct dsi_data * dsi,int vc)2994b6c1048cSTomi Valkeinen static int dsi_enable_video_mode(struct dsi_data *dsi, int vc)
29959960aa7cSTomi Valkeinen {
2996d0103cebSSebastian Reichel 	int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
29979960aa7cSTomi Valkeinen 	u8 data_type;
29989960aa7cSTomi Valkeinen 	u16 word_count;
29999960aa7cSTomi Valkeinen 
30009960aa7cSTomi Valkeinen 	switch (dsi->pix_fmt) {
3001d0103cebSSebastian Reichel 	case MIPI_DSI_FMT_RGB888:
30029960aa7cSTomi Valkeinen 		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
30039960aa7cSTomi Valkeinen 		break;
3004d0103cebSSebastian Reichel 	case MIPI_DSI_FMT_RGB666:
30059960aa7cSTomi Valkeinen 		data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
30069960aa7cSTomi Valkeinen 		break;
3007d0103cebSSebastian Reichel 	case MIPI_DSI_FMT_RGB666_PACKED:
30089960aa7cSTomi Valkeinen 		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
30099960aa7cSTomi Valkeinen 		break;
3010d0103cebSSebastian Reichel 	case MIPI_DSI_FMT_RGB565:
30119960aa7cSTomi Valkeinen 		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
30129960aa7cSTomi Valkeinen 		break;
30139960aa7cSTomi Valkeinen 	default:
3014b6c1048cSTomi Valkeinen 		return -EINVAL;
30159960aa7cSTomi Valkeinen 	}
30169960aa7cSTomi Valkeinen 
30177093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, false);
3018d8171145STomi Valkeinen 	dsi_vc_enable(dsi, vc, false);
30199960aa7cSTomi Valkeinen 
30209960aa7cSTomi Valkeinen 	/* MODE, 1 = video mode */
3021d8171145STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 1, 4, 4);
30229960aa7cSTomi Valkeinen 
3023da11bbbbSPeter Ujfalusi 	word_count = DIV_ROUND_UP(dsi->vm.hactive * bpp, 8);
30249960aa7cSTomi Valkeinen 
30255e430754STomi Valkeinen 	dsi_vc_write_long_header(dsi, vc, dsi->dsidev->channel, data_type,
30269960aa7cSTomi Valkeinen 			word_count, 0);
30279960aa7cSTomi Valkeinen 
3028d8171145STomi Valkeinen 	dsi_vc_enable(dsi, vc, true);
30297093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, true);
3030b6c1048cSTomi Valkeinen 
3031b6c1048cSTomi Valkeinen 	return 0;
3032b6c1048cSTomi Valkeinen }
3033b6c1048cSTomi Valkeinen 
dsi_disable_video_mode(struct dsi_data * dsi,int vc)3034b6c1048cSTomi Valkeinen static void dsi_disable_video_mode(struct dsi_data *dsi, int vc)
3035b6c1048cSTomi Valkeinen {
3036b6c1048cSTomi Valkeinen 	dsi_if_enable(dsi, false);
3037b6c1048cSTomi Valkeinen 	dsi_vc_enable(dsi, vc, false);
3038b6c1048cSTomi Valkeinen 
3039b6c1048cSTomi Valkeinen 	/* MODE, 0 = command mode */
3040b6c1048cSTomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 0, 4, 4);
3041b6c1048cSTomi Valkeinen 
3042b6c1048cSTomi Valkeinen 	dsi_vc_enable(dsi, vc, true);
3043b6c1048cSTomi Valkeinen 	dsi_if_enable(dsi, true);
3044b6c1048cSTomi Valkeinen }
3045b6c1048cSTomi Valkeinen 
dsi_enable_video_output(struct omap_dss_device * dssdev,int vc)3046b6c1048cSTomi Valkeinen static void dsi_enable_video_output(struct omap_dss_device *dssdev, int vc)
3047b6c1048cSTomi Valkeinen {
3048b6c1048cSTomi Valkeinen 	struct dsi_data *dsi = to_dsi_data(dssdev);
3049b6c1048cSTomi Valkeinen 	int r;
3050b6c1048cSTomi Valkeinen 
3051b6c1048cSTomi Valkeinen 	r = dsi_init_dispc(dsi);
3052b6c1048cSTomi Valkeinen 	if (r) {
3053b6c1048cSTomi Valkeinen 		dev_err(dsi->dev, "failed to init dispc!\n");
3054b6c1048cSTomi Valkeinen 		return;
3055b6c1048cSTomi Valkeinen 	}
3056b6c1048cSTomi Valkeinen 
3057b6c1048cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
3058b6c1048cSTomi Valkeinen 		r = dsi_enable_video_mode(dsi, vc);
3059b6c1048cSTomi Valkeinen 		if (r)
3060b6c1048cSTomi Valkeinen 			goto err_video_mode;
30619960aa7cSTomi Valkeinen 	}
30629960aa7cSTomi Valkeinen 
306328d79f3eSLaurent Pinchart 	r = dss_mgr_enable(&dsi->output);
30649960aa7cSTomi Valkeinen 	if (r)
30659960aa7cSTomi Valkeinen 		goto err_mgr_enable;
30669960aa7cSTomi Valkeinen 
30671cac9ba2SSebastian Reichel 	return;
30689960aa7cSTomi Valkeinen 
30699960aa7cSTomi Valkeinen err_mgr_enable:
30709960aa7cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
30717093d6cdSLaurent Pinchart 		dsi_if_enable(dsi, false);
3072d8171145STomi Valkeinen 		dsi_vc_enable(dsi, vc, false);
30739960aa7cSTomi Valkeinen 	}
3074b6c1048cSTomi Valkeinen err_video_mode:
3075a4a29d1dSTomi Valkeinen 	dsi_uninit_dispc(dsi);
30761cac9ba2SSebastian Reichel 	dev_err(dsi->dev, "failed to enable DSI encoder!\n");
30771cac9ba2SSebastian Reichel 	return;
30789960aa7cSTomi Valkeinen }
30799960aa7cSTomi Valkeinen 
dsi_disable_video_output(struct omap_dss_device * dssdev,int vc)3080d8171145STomi Valkeinen static void dsi_disable_video_output(struct omap_dss_device *dssdev, int vc)
30819960aa7cSTomi Valkeinen {
3082c068408eSLaurent Pinchart 	struct dsi_data *dsi = to_dsi_data(dssdev);
30839960aa7cSTomi Valkeinen 
3084b6c1048cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE)
3085b6c1048cSTomi Valkeinen 		dsi_disable_video_mode(dsi, vc);
30869960aa7cSTomi Valkeinen 
308728d79f3eSLaurent Pinchart 	dss_mgr_disable(&dsi->output);
30889960aa7cSTomi Valkeinen 
3089a4a29d1dSTomi Valkeinen 	dsi_uninit_dispc(dsi);
30909960aa7cSTomi Valkeinen }
30919960aa7cSTomi Valkeinen 
dsi_update_screen_dispc(struct dsi_data * dsi)30927093d6cdSLaurent Pinchart static void dsi_update_screen_dispc(struct dsi_data *dsi)
30939960aa7cSTomi Valkeinen {
3094d11e5c82SLaurent Pinchart 	unsigned int bytespp;
3095d11e5c82SLaurent Pinchart 	unsigned int bytespl;
3096d11e5c82SLaurent Pinchart 	unsigned int bytespf;
3097d11e5c82SLaurent Pinchart 	unsigned int total_len;
3098d11e5c82SLaurent Pinchart 	unsigned int packet_payload;
3099d11e5c82SLaurent Pinchart 	unsigned int packet_len;
31009960aa7cSTomi Valkeinen 	u32 l;
31019960aa7cSTomi Valkeinen 	int r;
3102d8171145STomi Valkeinen 	const unsigned vc = dsi->update_vc;
3103d11e5c82SLaurent Pinchart 	const unsigned int line_buf_size = dsi->line_buffer_size;
3104da11bbbbSPeter Ujfalusi 	u16 w = dsi->vm.hactive;
3105da11bbbbSPeter Ujfalusi 	u16 h = dsi->vm.vactive;
31069960aa7cSTomi Valkeinen 
31079960aa7cSTomi Valkeinen 	DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
31089960aa7cSTomi Valkeinen 
3109d0103cebSSebastian Reichel 	bytespp	= mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8;
31109960aa7cSTomi Valkeinen 	bytespl = w * bytespp;
31119960aa7cSTomi Valkeinen 	bytespf = bytespl * h;
31129960aa7cSTomi Valkeinen 
31139960aa7cSTomi Valkeinen 	/* NOTE: packet_payload has to be equal to N * bytespl, where N is
31149960aa7cSTomi Valkeinen 	 * number of lines in a packet.  See errata about VP_CLK_RATIO */
31159960aa7cSTomi Valkeinen 
31169960aa7cSTomi Valkeinen 	if (bytespf < line_buf_size)
31179960aa7cSTomi Valkeinen 		packet_payload = bytespf;
31189960aa7cSTomi Valkeinen 	else
31199960aa7cSTomi Valkeinen 		packet_payload = (line_buf_size) / bytespl * bytespl;
31209960aa7cSTomi Valkeinen 
31219960aa7cSTomi Valkeinen 	packet_len = packet_payload + 1;	/* 1 byte for DCS cmd */
31229960aa7cSTomi Valkeinen 	total_len = (bytespf / packet_payload) * packet_len;
31239960aa7cSTomi Valkeinen 
31249960aa7cSTomi Valkeinen 	if (bytespf % packet_payload)
31259960aa7cSTomi Valkeinen 		total_len += (bytespf % packet_payload) + 1;
31269960aa7cSTomi Valkeinen 
31279960aa7cSTomi Valkeinen 	l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
3128d8171145STomi Valkeinen 	dsi_write_reg(dsi, DSI_VC_TE(vc), l);
31299960aa7cSTomi Valkeinen 
31305e430754STomi Valkeinen 	dsi_vc_write_long_header(dsi, vc, dsi->dsidev->channel, MIPI_DSI_DCS_LONG_WRITE,
31319960aa7cSTomi Valkeinen 		packet_len, 0);
31329960aa7cSTomi Valkeinen 
31339960aa7cSTomi Valkeinen 	if (dsi->te_enabled)
31349960aa7cSTomi Valkeinen 		l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
31359960aa7cSTomi Valkeinen 	else
31369960aa7cSTomi Valkeinen 		l = FLD_MOD(l, 1, 31, 31); /* TE_START */
3137d8171145STomi Valkeinen 	dsi_write_reg(dsi, DSI_VC_TE(vc), l);
31389960aa7cSTomi Valkeinen 
31399960aa7cSTomi Valkeinen 	/* We put SIDLEMODE to no-idle for the duration of the transfer,
31409960aa7cSTomi Valkeinen 	 * because DSS interrupts are not capable of waking up the CPU and the
31419960aa7cSTomi Valkeinen 	 * framedone interrupt could be delayed for quite a long time. I think
31429960aa7cSTomi Valkeinen 	 * the same goes for any DSS interrupts, but for some reason I have not
31439960aa7cSTomi Valkeinen 	 * seen the problem anywhere else than here.
31449960aa7cSTomi Valkeinen 	 */
31458a7eda76SLaurent Pinchart 	dispc_disable_sidle(dsi->dss->dispc);
31469960aa7cSTomi Valkeinen 
31477093d6cdSLaurent Pinchart 	dsi_perf_mark_start(dsi);
31489960aa7cSTomi Valkeinen 
31499960aa7cSTomi Valkeinen 	r = schedule_delayed_work(&dsi->framedone_timeout_work,
31509960aa7cSTomi Valkeinen 		msecs_to_jiffies(250));
31519960aa7cSTomi Valkeinen 	BUG_ON(r == 0);
31529960aa7cSTomi Valkeinen 
315328d79f3eSLaurent Pinchart 	dss_mgr_start_update(&dsi->output);
31549960aa7cSTomi Valkeinen 
31559960aa7cSTomi Valkeinen 	if (dsi->te_enabled) {
31569960aa7cSTomi Valkeinen 		/* disable LP_RX_TO, so that we can receive TE.  Time to wait
31579960aa7cSTomi Valkeinen 		 * for TE is longer than the timer allows */
31587093d6cdSLaurent Pinchart 		REG_FLD_MOD(dsi, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
31599960aa7cSTomi Valkeinen 
3160d8171145STomi Valkeinen 		dsi_vc_send_bta(dsi, vc);
31619960aa7cSTomi Valkeinen 
31629960aa7cSTomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
31639960aa7cSTomi Valkeinen 		mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
31649960aa7cSTomi Valkeinen #endif
31659960aa7cSTomi Valkeinen 	}
31669960aa7cSTomi Valkeinen }
31679960aa7cSTomi Valkeinen 
31689960aa7cSTomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
dsi_te_timeout(struct timer_list * unused)3169e99e88a9SKees Cook static void dsi_te_timeout(struct timer_list *unused)
31709960aa7cSTomi Valkeinen {
31719960aa7cSTomi Valkeinen 	DSSERR("TE not received for 250ms!\n");
31729960aa7cSTomi Valkeinen }
31739960aa7cSTomi Valkeinen #endif
31749960aa7cSTomi Valkeinen 
dsi_handle_framedone(struct dsi_data * dsi,int error)31757093d6cdSLaurent Pinchart static void dsi_handle_framedone(struct dsi_data *dsi, int error)
31769960aa7cSTomi Valkeinen {
31779960aa7cSTomi Valkeinen 	/* SIDLEMODE back to smart-idle */
31788a7eda76SLaurent Pinchart 	dispc_enable_sidle(dsi->dss->dispc);
31799960aa7cSTomi Valkeinen 
31809960aa7cSTomi Valkeinen 	if (dsi->te_enabled) {
31819960aa7cSTomi Valkeinen 		/* enable LP_RX_TO again after the TE */
31827093d6cdSLaurent Pinchart 		REG_FLD_MOD(dsi, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
31839960aa7cSTomi Valkeinen 	}
31849960aa7cSTomi Valkeinen 
31850c93a61dSSebastian Reichel 	dsi_bus_unlock(dsi);
31860c93a61dSSebastian Reichel 
31879960aa7cSTomi Valkeinen 	if (!error)
31887093d6cdSLaurent Pinchart 		dsi_perf_show(dsi, "DISPC");
31899960aa7cSTomi Valkeinen }
31909960aa7cSTomi Valkeinen 
dsi_framedone_timeout_work_callback(struct work_struct * work)31919960aa7cSTomi Valkeinen static void dsi_framedone_timeout_work_callback(struct work_struct *work)
31929960aa7cSTomi Valkeinen {
31939960aa7cSTomi Valkeinen 	struct dsi_data *dsi = container_of(work, struct dsi_data,
31949960aa7cSTomi Valkeinen 			framedone_timeout_work.work);
31959960aa7cSTomi Valkeinen 	/* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
31969960aa7cSTomi Valkeinen 	 * 250ms which would conflict with this timeout work. What should be
31979960aa7cSTomi Valkeinen 	 * done is first cancel the transfer on the HW, and then cancel the
31989960aa7cSTomi Valkeinen 	 * possibly scheduled framedone work. However, cancelling the transfer
31999960aa7cSTomi Valkeinen 	 * on the HW is buggy, and would probably require resetting the whole
32009960aa7cSTomi Valkeinen 	 * DSI */
32019960aa7cSTomi Valkeinen 
32029960aa7cSTomi Valkeinen 	DSSERR("Framedone not received for 250ms!\n");
32039960aa7cSTomi Valkeinen 
32047093d6cdSLaurent Pinchart 	dsi_handle_framedone(dsi, -ETIMEDOUT);
32059960aa7cSTomi Valkeinen }
32069960aa7cSTomi Valkeinen 
dsi_framedone_irq_callback(void * data)32079960aa7cSTomi Valkeinen static void dsi_framedone_irq_callback(void *data)
32089960aa7cSTomi Valkeinen {
32097093d6cdSLaurent Pinchart 	struct dsi_data *dsi = data;
32109960aa7cSTomi Valkeinen 
32119960aa7cSTomi Valkeinen 	/* Note: We get FRAMEDONE when DISPC has finished sending pixels and
32129960aa7cSTomi Valkeinen 	 * turns itself off. However, DSI still has the pixels in its buffers,
32139960aa7cSTomi Valkeinen 	 * and is sending the data.
32149960aa7cSTomi Valkeinen 	 */
32159960aa7cSTomi Valkeinen 
32169960aa7cSTomi Valkeinen 	cancel_delayed_work(&dsi->framedone_timeout_work);
32179960aa7cSTomi Valkeinen 
32182a4703c2SSebastian Reichel 	DSSDBG("Framedone received!\n");
32192a4703c2SSebastian Reichel 
32207093d6cdSLaurent Pinchart 	dsi_handle_framedone(dsi, 0);
32219960aa7cSTomi Valkeinen }
32229960aa7cSTomi Valkeinen 
_dsi_update(struct dsi_data * dsi)32234c1b935fSSebastian Reichel static int _dsi_update(struct dsi_data *dsi)
32249960aa7cSTomi Valkeinen {
32257093d6cdSLaurent Pinchart 	dsi_perf_mark_setup(dsi);
32269960aa7cSTomi Valkeinen 
32279960aa7cSTomi Valkeinen #ifdef DSI_PERF_MEASURE
3228ae36fcccSLee Jones 	dsi->update_bytes = dsi->vm.hactive * dsi->vm.vactive *
3229d0103cebSSebastian Reichel 		mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8;
32309960aa7cSTomi Valkeinen #endif
32317093d6cdSLaurent Pinchart 	dsi_update_screen_dispc(dsi);
32329960aa7cSTomi Valkeinen 
32339960aa7cSTomi Valkeinen 	return 0;
32349960aa7cSTomi Valkeinen }
32359960aa7cSTomi Valkeinen 
_dsi_send_nop(struct dsi_data * dsi,int vc,int channel)32365e430754STomi Valkeinen static int _dsi_send_nop(struct dsi_data *dsi, int vc, int channel)
32372a4703c2SSebastian Reichel {
32388350ac8eSTomi Valkeinen 	const u8 payload[] = { MIPI_DCS_NOP };
32398350ac8eSTomi Valkeinen 	const struct mipi_dsi_msg msg = {
32408350ac8eSTomi Valkeinen 		.channel = channel,
32418350ac8eSTomi Valkeinen 		.type = MIPI_DSI_DCS_SHORT_WRITE,
32428350ac8eSTomi Valkeinen 		.tx_len = 1,
32438350ac8eSTomi Valkeinen 		.tx_buf = payload,
32448350ac8eSTomi Valkeinen 	};
32452a4703c2SSebastian Reichel 
32462a4703c2SSebastian Reichel 	WARN_ON(!dsi_bus_is_locked(dsi));
32472a4703c2SSebastian Reichel 
32485e430754STomi Valkeinen 	return _omap_dsi_host_transfer(dsi, vc, &msg);
32492a4703c2SSebastian Reichel }
32502a4703c2SSebastian Reichel 
dsi_update_channel(struct omap_dss_device * dssdev,int vc)3251d8171145STomi Valkeinen static int dsi_update_channel(struct omap_dss_device *dssdev, int vc)
32524c1b935fSSebastian Reichel {
32534c1b935fSSebastian Reichel 	struct dsi_data *dsi = to_dsi_data(dssdev);
32542a4703c2SSebastian Reichel 	int r;
32552a4703c2SSebastian Reichel 
32560c93a61dSSebastian Reichel 	dsi_bus_lock(dsi);
32572a4703c2SSebastian Reichel 
32581cac9ba2SSebastian Reichel 	if (!dsi->video_enabled) {
32591cac9ba2SSebastian Reichel 		r = -EIO;
32601cac9ba2SSebastian Reichel 		goto err;
32611cac9ba2SSebastian Reichel 	}
32621cac9ba2SSebastian Reichel 
32632a4703c2SSebastian Reichel 	if (dsi->vm.hactive == 0 || dsi->vm.vactive == 0) {
32642a4703c2SSebastian Reichel 		r = -EINVAL;
32652a4703c2SSebastian Reichel 		goto err;
32662a4703c2SSebastian Reichel 	}
32672a4703c2SSebastian Reichel 
3268d8171145STomi Valkeinen 	DSSDBG("dsi_update_channel: %d", vc);
32692a4703c2SSebastian Reichel 
32708350ac8eSTomi Valkeinen 	/*
32718350ac8eSTomi Valkeinen 	 * Send NOP between the frames. If we don't send something here, the
32728350ac8eSTomi Valkeinen 	 * updates stop working. This is probably related to DSI spec stating
32738350ac8eSTomi Valkeinen 	 * that the DSI host should transition to LP at least once per frame.
32748350ac8eSTomi Valkeinen 	 */
32757b93de98STomi Valkeinen 	r = _dsi_send_nop(dsi, VC_CMD, dsi->dsidev->channel);
32762a4703c2SSebastian Reichel 	if (r < 0) {
32778350ac8eSTomi Valkeinen 		DSSWARN("failed to send nop between frames: %d\n", r);
32782a4703c2SSebastian Reichel 		goto err;
32792a4703c2SSebastian Reichel 	}
32802a4703c2SSebastian Reichel 
3281d8171145STomi Valkeinen 	dsi->update_vc = vc;
32824c1b935fSSebastian Reichel 
32834c1b935fSSebastian Reichel 	if (dsi->te_enabled && dsi->te_gpio) {
32844c1b935fSSebastian Reichel 		schedule_delayed_work(&dsi->te_timeout_work,
32854c1b935fSSebastian Reichel 				      msecs_to_jiffies(250));
32864c1b935fSSebastian Reichel 		atomic_set(&dsi->do_ext_te_update, 1);
32874c1b935fSSebastian Reichel 	} else {
32884c1b935fSSebastian Reichel 		_dsi_update(dsi);
32894c1b935fSSebastian Reichel 	}
32904c1b935fSSebastian Reichel 
32914c1b935fSSebastian Reichel 	return 0;
32922a4703c2SSebastian Reichel 
32932a4703c2SSebastian Reichel err:
32942a4703c2SSebastian Reichel 	dsi_bus_unlock(dsi);
32952a4703c2SSebastian Reichel 	return r;
32962a4703c2SSebastian Reichel }
32972a4703c2SSebastian Reichel 
dsi_update_all(struct omap_dss_device * dssdev)32982a4703c2SSebastian Reichel static int dsi_update_all(struct omap_dss_device *dssdev)
32992a4703c2SSebastian Reichel {
33004029b16bSTomi Valkeinen 	return dsi_update_channel(dssdev, VC_VIDEO);
33014c1b935fSSebastian Reichel }
33024c1b935fSSebastian Reichel 
33039960aa7cSTomi Valkeinen /* Display funcs */
33049960aa7cSTomi Valkeinen 
dsi_configure_dispc_clocks(struct dsi_data * dsi)33057093d6cdSLaurent Pinchart static int dsi_configure_dispc_clocks(struct dsi_data *dsi)
33069960aa7cSTomi Valkeinen {
33079960aa7cSTomi Valkeinen 	struct dispc_clock_info dispc_cinfo;
33089960aa7cSTomi Valkeinen 	int r;
33099960aa7cSTomi Valkeinen 	unsigned long fck;
33109960aa7cSTomi Valkeinen 
33117093d6cdSLaurent Pinchart 	fck = dsi_get_pll_hsdiv_dispc_rate(dsi);
33129960aa7cSTomi Valkeinen 
33139960aa7cSTomi Valkeinen 	dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div;
33149960aa7cSTomi Valkeinen 	dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div;
33159960aa7cSTomi Valkeinen 
33168a7eda76SLaurent Pinchart 	r = dispc_calc_clock_rates(dsi->dss->dispc, fck, &dispc_cinfo);
33179960aa7cSTomi Valkeinen 	if (r) {
33189960aa7cSTomi Valkeinen 		DSSERR("Failed to calc dispc clocks\n");
33199960aa7cSTomi Valkeinen 		return r;
33209960aa7cSTomi Valkeinen 	}
33219960aa7cSTomi Valkeinen 
33229960aa7cSTomi Valkeinen 	dsi->mgr_config.clock_info = dispc_cinfo;
33239960aa7cSTomi Valkeinen 
33249960aa7cSTomi Valkeinen 	return 0;
33259960aa7cSTomi Valkeinen }
33269960aa7cSTomi Valkeinen 
dsi_init_dispc(struct dsi_data * dsi)3327a4a29d1dSTomi Valkeinen static int dsi_init_dispc(struct dsi_data *dsi)
33289960aa7cSTomi Valkeinen {
3329f408600fSTomi Valkeinen 	enum omap_channel dispc_channel = dsi->output.dispc_channel;
33309960aa7cSTomi Valkeinen 	int r;
33319960aa7cSTomi Valkeinen 
3332f408600fSTomi Valkeinen 	dss_select_lcd_clk_source(dsi->dss, dispc_channel, dsi->module_id == 0 ?
33333b63ca75STomi Valkeinen 			DSS_CLK_SRC_PLL1_1 :
33343b63ca75STomi Valkeinen 			DSS_CLK_SRC_PLL2_1);
33359960aa7cSTomi Valkeinen 
33369960aa7cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
333728d79f3eSLaurent Pinchart 		r = dss_mgr_register_framedone_handler(&dsi->output,
33387093d6cdSLaurent Pinchart 				dsi_framedone_irq_callback, dsi);
33399960aa7cSTomi Valkeinen 		if (r) {
33409960aa7cSTomi Valkeinen 			DSSERR("can't register FRAMEDONE handler\n");
33419960aa7cSTomi Valkeinen 			goto err;
33429960aa7cSTomi Valkeinen 		}
33439960aa7cSTomi Valkeinen 
33449960aa7cSTomi Valkeinen 		dsi->mgr_config.stallmode = true;
33459960aa7cSTomi Valkeinen 		dsi->mgr_config.fifohandcheck = true;
33469960aa7cSTomi Valkeinen 	} else {
33479960aa7cSTomi Valkeinen 		dsi->mgr_config.stallmode = false;
33489960aa7cSTomi Valkeinen 		dsi->mgr_config.fifohandcheck = false;
33499960aa7cSTomi Valkeinen 	}
33509960aa7cSTomi Valkeinen 
33517093d6cdSLaurent Pinchart 	r = dsi_configure_dispc_clocks(dsi);
33529960aa7cSTomi Valkeinen 	if (r)
33539960aa7cSTomi Valkeinen 		goto err1;
33549960aa7cSTomi Valkeinen 
33559960aa7cSTomi Valkeinen 	dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
33569960aa7cSTomi Valkeinen 	dsi->mgr_config.video_port_width =
3357d0103cebSSebastian Reichel 			mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt);
33589960aa7cSTomi Valkeinen 	dsi->mgr_config.lcden_sig_polarity = 0;
33599960aa7cSTomi Valkeinen 
336028d79f3eSLaurent Pinchart 	dss_mgr_set_lcd_config(&dsi->output, &dsi->mgr_config);
33619960aa7cSTomi Valkeinen 
33629960aa7cSTomi Valkeinen 	return 0;
33639960aa7cSTomi Valkeinen err1:
33649960aa7cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
336528d79f3eSLaurent Pinchart 		dss_mgr_unregister_framedone_handler(&dsi->output,
33667093d6cdSLaurent Pinchart 				dsi_framedone_irq_callback, dsi);
33679960aa7cSTomi Valkeinen err:
3368f408600fSTomi Valkeinen 	dss_select_lcd_clk_source(dsi->dss, dispc_channel, DSS_CLK_SRC_FCK);
33699960aa7cSTomi Valkeinen 	return r;
33709960aa7cSTomi Valkeinen }
33719960aa7cSTomi Valkeinen 
dsi_uninit_dispc(struct dsi_data * dsi)3372a4a29d1dSTomi Valkeinen static void dsi_uninit_dispc(struct dsi_data *dsi)
33739960aa7cSTomi Valkeinen {
3374f408600fSTomi Valkeinen 	enum omap_channel dispc_channel = dsi->output.dispc_channel;
3375f81b0fd4SLaurent Pinchart 
33769960aa7cSTomi Valkeinen 	if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
337728d79f3eSLaurent Pinchart 		dss_mgr_unregister_framedone_handler(&dsi->output,
33787093d6cdSLaurent Pinchart 				dsi_framedone_irq_callback, dsi);
33799960aa7cSTomi Valkeinen 
3380f408600fSTomi Valkeinen 	dss_select_lcd_clk_source(dsi->dss, dispc_channel, DSS_CLK_SRC_FCK);
33819960aa7cSTomi Valkeinen }
33829960aa7cSTomi Valkeinen 
dsi_configure_dsi_clocks(struct dsi_data * dsi)33837093d6cdSLaurent Pinchart static int dsi_configure_dsi_clocks(struct dsi_data *dsi)
33849960aa7cSTomi Valkeinen {
33859960aa7cSTomi Valkeinen 	struct dss_pll_clock_info cinfo;
33869960aa7cSTomi Valkeinen 	int r;
33879960aa7cSTomi Valkeinen 
33889960aa7cSTomi Valkeinen 	cinfo = dsi->user_dsi_cinfo;
33899960aa7cSTomi Valkeinen 
33909960aa7cSTomi Valkeinen 	r = dss_pll_set_config(&dsi->pll, &cinfo);
33919960aa7cSTomi Valkeinen 	if (r) {
33929960aa7cSTomi Valkeinen 		DSSERR("Failed to set dsi clocks\n");
33939960aa7cSTomi Valkeinen 		return r;
33949960aa7cSTomi Valkeinen 	}
33959960aa7cSTomi Valkeinen 
33969960aa7cSTomi Valkeinen 	return 0;
33979960aa7cSTomi Valkeinen }
33989960aa7cSTomi Valkeinen 
dsi_setup_dsi_vcs(struct dsi_data * dsi)33997b93de98STomi Valkeinen static void dsi_setup_dsi_vcs(struct dsi_data *dsi)
34007b93de98STomi Valkeinen {
34017b93de98STomi Valkeinen 	/* Setup VC_CMD for LP and cpu transfers */
34027b93de98STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_CMD), 0, 9, 9); /* LP */
34037b93de98STomi Valkeinen 
34047b93de98STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_CMD), 0, 1, 1); /* SOURCE_L4 */
34057b93de98STomi Valkeinen 	dsi->vc[VC_CMD].source = DSI_VC_SOURCE_L4;
34067b93de98STomi Valkeinen 
34077b93de98STomi Valkeinen 	/* Setup VC_VIDEO for HS and dispc transfers */
34087b93de98STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 9, 9); /* HS */
34097b93de98STomi Valkeinen 
34107b93de98STomi Valkeinen 	REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 1, 1); /* SOURCE_VP */
34117b93de98STomi Valkeinen 	dsi->vc[VC_VIDEO].source = DSI_VC_SOURCE_VP;
34127b93de98STomi Valkeinen 
341392bb0eabSTomi Valkeinen 	if ((dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC) &&
341492bb0eabSTomi Valkeinen 	    !(dsi->dsidev->mode_flags & MIPI_DSI_MODE_VIDEO))
34157b93de98STomi Valkeinen 		REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 30, 30); /* DCS_CMD_ENABLE */
34167b93de98STomi Valkeinen 
34177b93de98STomi Valkeinen 	dsi_vc_enable(dsi, VC_CMD, 1);
34187b93de98STomi Valkeinen 	dsi_vc_enable(dsi, VC_VIDEO, 1);
34197b93de98STomi Valkeinen 
34207b93de98STomi Valkeinen 	dsi_if_enable(dsi, 1);
34217b93de98STomi Valkeinen 
34227b93de98STomi Valkeinen 	dsi_force_tx_stop_mode_io(dsi);
34237b93de98STomi Valkeinen 
34247b93de98STomi Valkeinen 	/* start the DDR clock by sending a NULL packet */
34259a521118STomi Valkeinen 	if (!(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
34267b93de98STomi Valkeinen 		dsi_vc_send_null(dsi, VC_CMD, dsi->dsidev->channel);
34277b93de98STomi Valkeinen }
34287b93de98STomi Valkeinen 
dsi_init_dsi(struct dsi_data * dsi)3429a4a29d1dSTomi Valkeinen static int dsi_init_dsi(struct dsi_data *dsi)
34309960aa7cSTomi Valkeinen {
34319960aa7cSTomi Valkeinen 	int r;
34329960aa7cSTomi Valkeinen 
34339960aa7cSTomi Valkeinen 	r = dss_pll_enable(&dsi->pll);
34349960aa7cSTomi Valkeinen 	if (r)
3435fe4ed1b4STony Lindgren 		return r;
34369960aa7cSTomi Valkeinen 
34377093d6cdSLaurent Pinchart 	r = dsi_configure_dsi_clocks(dsi);
34389960aa7cSTomi Valkeinen 	if (r)
3439fe4ed1b4STony Lindgren 		goto err0;
34409960aa7cSTomi Valkeinen 
34418aea8e6aSLaurent Pinchart 	dss_select_dsi_clk_source(dsi->dss, dsi->module_id,
34428aea8e6aSLaurent Pinchart 				  dsi->module_id == 0 ?
34438aea8e6aSLaurent Pinchart 				  DSS_CLK_SRC_PLL1_2 : DSS_CLK_SRC_PLL2_2);
34449960aa7cSTomi Valkeinen 
34459960aa7cSTomi Valkeinen 	DSSDBG("PLL OK\n");
34469960aa7cSTomi Valkeinen 
3447fe4ed1b4STony Lindgren 	if (!dsi->vdds_dsi_enabled) {
3448fe4ed1b4STony Lindgren 		r = regulator_enable(dsi->vdds_dsi_reg);
3449fe4ed1b4STony Lindgren 		if (r)
3450fe4ed1b4STony Lindgren 			goto err1;
3451fe4ed1b4STony Lindgren 
3452fe4ed1b4STony Lindgren 		dsi->vdds_dsi_enabled = true;
3453fe4ed1b4STony Lindgren 	}
3454fe4ed1b4STony Lindgren 
34557093d6cdSLaurent Pinchart 	r = dsi_cio_init(dsi);
34569960aa7cSTomi Valkeinen 	if (r)
34579960aa7cSTomi Valkeinen 		goto err2;
34589960aa7cSTomi Valkeinen 
34597093d6cdSLaurent Pinchart 	_dsi_print_reset_status(dsi);
34609960aa7cSTomi Valkeinen 
34617093d6cdSLaurent Pinchart 	dsi_proto_timings(dsi);
34627093d6cdSLaurent Pinchart 	dsi_set_lp_clk_divisor(dsi);
34639960aa7cSTomi Valkeinen 
34649960aa7cSTomi Valkeinen 	if (1)
34657093d6cdSLaurent Pinchart 		_dsi_print_reset_status(dsi);
34669960aa7cSTomi Valkeinen 
34677093d6cdSLaurent Pinchart 	r = dsi_proto_config(dsi);
34689960aa7cSTomi Valkeinen 	if (r)
34699960aa7cSTomi Valkeinen 		goto err3;
34709960aa7cSTomi Valkeinen 
34717b93de98STomi Valkeinen 	dsi_setup_dsi_vcs(dsi);
34729960aa7cSTomi Valkeinen 
34739960aa7cSTomi Valkeinen 	return 0;
34749960aa7cSTomi Valkeinen err3:
34757093d6cdSLaurent Pinchart 	dsi_cio_uninit(dsi);
34769960aa7cSTomi Valkeinen err2:
3477fe4ed1b4STony Lindgren 	regulator_disable(dsi->vdds_dsi_reg);
3478fe4ed1b4STony Lindgren 	dsi->vdds_dsi_enabled = false;
34799960aa7cSTomi Valkeinen err1:
3480fe4ed1b4STony Lindgren 	dss_select_dsi_clk_source(dsi->dss, dsi->module_id, DSS_CLK_SRC_FCK);
34819960aa7cSTomi Valkeinen err0:
3482fe4ed1b4STony Lindgren 	dss_pll_disable(&dsi->pll);
3483fe4ed1b4STony Lindgren 
34849960aa7cSTomi Valkeinen 	return r;
34859960aa7cSTomi Valkeinen }
34869960aa7cSTomi Valkeinen 
dsi_uninit_dsi(struct dsi_data * dsi)3487c8320789STomi Valkeinen static void dsi_uninit_dsi(struct dsi_data *dsi)
34889960aa7cSTomi Valkeinen {
34899960aa7cSTomi Valkeinen 	/* disable interface */
34907093d6cdSLaurent Pinchart 	dsi_if_enable(dsi, 0);
34917093d6cdSLaurent Pinchart 	dsi_vc_enable(dsi, 0, 0);
34927093d6cdSLaurent Pinchart 	dsi_vc_enable(dsi, 1, 0);
34937093d6cdSLaurent Pinchart 	dsi_vc_enable(dsi, 2, 0);
34947093d6cdSLaurent Pinchart 	dsi_vc_enable(dsi, 3, 0);
34959960aa7cSTomi Valkeinen 
34968aea8e6aSLaurent Pinchart 	dss_select_dsi_clk_source(dsi->dss, dsi->module_id, DSS_CLK_SRC_FCK);
34977093d6cdSLaurent Pinchart 	dsi_cio_uninit(dsi);
3498fe4ed1b4STony Lindgren 	dss_pll_disable(&dsi->pll);
3499fe4ed1b4STony Lindgren 
3500fe4ed1b4STony Lindgren 	regulator_disable(dsi->vdds_dsi_reg);
3501fe4ed1b4STony Lindgren 	dsi->vdds_dsi_enabled = false;
3502fe4ed1b4STony Lindgren }
35039960aa7cSTomi Valkeinen 
dsi_enable(struct dsi_data * dsi)3504a4a29d1dSTomi Valkeinen static void dsi_enable(struct dsi_data *dsi)
35059960aa7cSTomi Valkeinen {
350619b4200dSLaurent Pinchart 	int r;
35079960aa7cSTomi Valkeinen 
35089f0eb51eSSebastian Reichel 	WARN_ON(!dsi_bus_is_locked(dsi));
35099960aa7cSTomi Valkeinen 
3510dfd2edccSTomi Valkeinen 	if (WARN_ON(dsi->iface_enabled))
3511dfd2edccSTomi Valkeinen 		return;
3512dfd2edccSTomi Valkeinen 
35139960aa7cSTomi Valkeinen 	mutex_lock(&dsi->lock);
35149960aa7cSTomi Valkeinen 
35157093d6cdSLaurent Pinchart 	r = dsi_runtime_get(dsi);
35169960aa7cSTomi Valkeinen 	if (r)
35179960aa7cSTomi Valkeinen 		goto err_get_dsi;
35189960aa7cSTomi Valkeinen 
35197093d6cdSLaurent Pinchart 	_dsi_initialize_irq(dsi);
35209960aa7cSTomi Valkeinen 
3521a4a29d1dSTomi Valkeinen 	r = dsi_init_dsi(dsi);
35229960aa7cSTomi Valkeinen 	if (r)
35239960aa7cSTomi Valkeinen 		goto err_init_dsi;
35249960aa7cSTomi Valkeinen 
3525dfd2edccSTomi Valkeinen 	dsi->iface_enabled = true;
3526dfd2edccSTomi Valkeinen 
35279960aa7cSTomi Valkeinen 	mutex_unlock(&dsi->lock);
35289960aa7cSTomi Valkeinen 
352919b4200dSLaurent Pinchart 	return;
35309960aa7cSTomi Valkeinen 
35319960aa7cSTomi Valkeinen err_init_dsi:
35327093d6cdSLaurent Pinchart 	dsi_runtime_put(dsi);
35339960aa7cSTomi Valkeinen err_get_dsi:
35349960aa7cSTomi Valkeinen 	mutex_unlock(&dsi->lock);
3535a4a29d1dSTomi Valkeinen 	DSSDBG("dsi_enable FAILED\n");
35369960aa7cSTomi Valkeinen }
35379960aa7cSTomi Valkeinen 
dsi_disable(struct dsi_data * dsi)3538c8320789STomi Valkeinen static void dsi_disable(struct dsi_data *dsi)
35399f0eb51eSSebastian Reichel {
35409f0eb51eSSebastian Reichel 	WARN_ON(!dsi_bus_is_locked(dsi));
35419960aa7cSTomi Valkeinen 
3542dfd2edccSTomi Valkeinen 	if (WARN_ON(!dsi->iface_enabled))
3543dfd2edccSTomi Valkeinen 		return;
3544dfd2edccSTomi Valkeinen 
35459960aa7cSTomi Valkeinen 	mutex_lock(&dsi->lock);
35469960aa7cSTomi Valkeinen 
35477093d6cdSLaurent Pinchart 	dsi_sync_vc(dsi, 0);
35487093d6cdSLaurent Pinchart 	dsi_sync_vc(dsi, 1);
35497093d6cdSLaurent Pinchart 	dsi_sync_vc(dsi, 2);
35507093d6cdSLaurent Pinchart 	dsi_sync_vc(dsi, 3);
35519960aa7cSTomi Valkeinen 
3552c8320789STomi Valkeinen 	dsi_uninit_dsi(dsi);
35539960aa7cSTomi Valkeinen 
35547093d6cdSLaurent Pinchart 	dsi_runtime_put(dsi);
35559960aa7cSTomi Valkeinen 
3556dfd2edccSTomi Valkeinen 	dsi->iface_enabled = false;
3557dfd2edccSTomi Valkeinen 
35589960aa7cSTomi Valkeinen 	mutex_unlock(&dsi->lock);
35599f0eb51eSSebastian Reichel }
35609f0eb51eSSebastian Reichel 
dsi_enable_te(struct dsi_data * dsi,bool enable)3561e9c7a0d7SSebastian Reichel static int dsi_enable_te(struct dsi_data *dsi, bool enable)
35629960aa7cSTomi Valkeinen {
35639960aa7cSTomi Valkeinen 	dsi->te_enabled = enable;
35644c1b935fSSebastian Reichel 
35654c1b935fSSebastian Reichel 	if (dsi->te_gpio) {
35664c1b935fSSebastian Reichel 		if (enable)
35674c1b935fSSebastian Reichel 			enable_irq(dsi->te_irq);
35684c1b935fSSebastian Reichel 		else
35694c1b935fSSebastian Reichel 			disable_irq(dsi->te_irq);
35704c1b935fSSebastian Reichel 	}
35714c1b935fSSebastian Reichel 
35729960aa7cSTomi Valkeinen 	return 0;
35739960aa7cSTomi Valkeinen }
35749960aa7cSTomi Valkeinen 
35759960aa7cSTomi Valkeinen #ifdef PRINT_VERBOSE_VM_TIMINGS
print_dsi_vm(const char * str,const struct omap_dss_dsi_videomode_timings * t)35769960aa7cSTomi Valkeinen static void print_dsi_vm(const char *str,
35779960aa7cSTomi Valkeinen 		const struct omap_dss_dsi_videomode_timings *t)
35789960aa7cSTomi Valkeinen {
35799960aa7cSTomi Valkeinen 	unsigned long byteclk = t->hsclk / 4;
35809960aa7cSTomi Valkeinen 	int bl, wc, pps, tot;
35819960aa7cSTomi Valkeinen 
35829960aa7cSTomi Valkeinen 	wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
35839960aa7cSTomi Valkeinen 	pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
35847e6d80ddSH. Nikolaus Schaller 	bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
35859960aa7cSTomi Valkeinen 	tot = bl + pps;
35869960aa7cSTomi Valkeinen 
35879960aa7cSTomi Valkeinen #define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
35889960aa7cSTomi Valkeinen 
35899960aa7cSTomi Valkeinen 	pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, "
35909960aa7cSTomi Valkeinen 			"%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
35919960aa7cSTomi Valkeinen 			str,
35929960aa7cSTomi Valkeinen 			byteclk,
35937e6d80ddSH. Nikolaus Schaller 			t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
35949960aa7cSTomi Valkeinen 			bl, pps, tot,
35959960aa7cSTomi Valkeinen 			TO_DSI_T(t->hss),
35969960aa7cSTomi Valkeinen 			TO_DSI_T(t->hsa),
35979960aa7cSTomi Valkeinen 			TO_DSI_T(t->hse),
35989960aa7cSTomi Valkeinen 			TO_DSI_T(t->hbp),
35999960aa7cSTomi Valkeinen 			TO_DSI_T(pps),
36007e6d80ddSH. Nikolaus Schaller 			TO_DSI_T(t->hfp),
36019960aa7cSTomi Valkeinen 
36029960aa7cSTomi Valkeinen 			TO_DSI_T(bl),
36039960aa7cSTomi Valkeinen 			TO_DSI_T(pps),
36049960aa7cSTomi Valkeinen 
36059960aa7cSTomi Valkeinen 			TO_DSI_T(tot));
36069960aa7cSTomi Valkeinen #undef TO_DSI_T
36079960aa7cSTomi Valkeinen }
36089960aa7cSTomi Valkeinen 
print_dispc_vm(const char * str,const struct videomode * vm)3609da11bbbbSPeter Ujfalusi static void print_dispc_vm(const char *str, const struct videomode *vm)
36109960aa7cSTomi Valkeinen {
3611da11bbbbSPeter Ujfalusi 	unsigned long pck = vm->pixelclock;
36129960aa7cSTomi Valkeinen 	int hact, bl, tot;
36139960aa7cSTomi Valkeinen 
3614da11bbbbSPeter Ujfalusi 	hact = vm->hactive;
36157e6d80ddSH. Nikolaus Schaller 	bl = vm->hsync_len + vm->hback_porch + vm->hfront_porch;
36169960aa7cSTomi Valkeinen 	tot = hact + bl;
36179960aa7cSTomi Valkeinen 
36189960aa7cSTomi Valkeinen #define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
36199960aa7cSTomi Valkeinen 
36209960aa7cSTomi Valkeinen 	pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, "
36219960aa7cSTomi Valkeinen 			"%u/%u/%u/%u = %u + %u = %u\n",
36229960aa7cSTomi Valkeinen 			str,
36239960aa7cSTomi Valkeinen 			pck,
36247e6d80ddSH. Nikolaus Schaller 			vm->hsync_len, vm->hback_porch, hact, vm->hfront_porch,
36259960aa7cSTomi Valkeinen 			bl, hact, tot,
3626da11bbbbSPeter Ujfalusi 			TO_DISPC_T(vm->hsync_len),
36277e6d80ddSH. Nikolaus Schaller 			TO_DISPC_T(vm->hback_porch),
36289960aa7cSTomi Valkeinen 			TO_DISPC_T(hact),
3629da11bbbbSPeter Ujfalusi 			TO_DISPC_T(vm->hfront_porch),
36309960aa7cSTomi Valkeinen 			TO_DISPC_T(bl),
36319960aa7cSTomi Valkeinen 			TO_DISPC_T(hact),
36329960aa7cSTomi Valkeinen 			TO_DISPC_T(tot));
36339960aa7cSTomi Valkeinen #undef TO_DISPC_T
36349960aa7cSTomi Valkeinen }
36359960aa7cSTomi Valkeinen 
36369960aa7cSTomi Valkeinen /* note: this is not quite accurate */
print_dsi_dispc_vm(const char * str,const struct omap_dss_dsi_videomode_timings * t)36379960aa7cSTomi Valkeinen static void print_dsi_dispc_vm(const char *str,
36389960aa7cSTomi Valkeinen 		const struct omap_dss_dsi_videomode_timings *t)
36399960aa7cSTomi Valkeinen {
36404520ff28SPeter Ujfalusi 	struct videomode vm = { 0 };
36419960aa7cSTomi Valkeinen 	unsigned long byteclk = t->hsclk / 4;
36429960aa7cSTomi Valkeinen 	unsigned long pck;
36439960aa7cSTomi Valkeinen 	u64 dsi_tput;
36449960aa7cSTomi Valkeinen 	int dsi_hact, dsi_htot;
36459960aa7cSTomi Valkeinen 
36469960aa7cSTomi Valkeinen 	dsi_tput = (u64)byteclk * t->ndl * 8;
36479960aa7cSTomi Valkeinen 	pck = (u32)div64_u64(dsi_tput, t->bitspp);
36489960aa7cSTomi Valkeinen 	dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
36497e6d80ddSH. Nikolaus Schaller 	dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
36509960aa7cSTomi Valkeinen 
36519960aa7cSTomi Valkeinen 	vm.pixelclock = pck;
36524dc2250dSPeter Ujfalusi 	vm.hsync_len = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
36537e6d80ddSH. Nikolaus Schaller 	vm.hback_porch = div64_u64((u64)t->hbp * pck, byteclk);
36547e6d80ddSH. Nikolaus Schaller 	vm.hfront_porch = div64_u64((u64)t->hfp * pck, byteclk);
365581899060SPeter Ujfalusi 	vm.hactive = t->hact;
36569960aa7cSTomi Valkeinen 
36579960aa7cSTomi Valkeinen 	print_dispc_vm(str, &vm);
36589960aa7cSTomi Valkeinen }
36599960aa7cSTomi Valkeinen #endif /* PRINT_VERBOSE_VM_TIMINGS */
36609960aa7cSTomi Valkeinen 
dsi_cm_calc_dispc_cb(int lckd,int pckd,unsigned long lck,unsigned long pck,void * data)36619960aa7cSTomi Valkeinen static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
36629960aa7cSTomi Valkeinen 		unsigned long pck, void *data)
36639960aa7cSTomi Valkeinen {
36649960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
3665da11bbbbSPeter Ujfalusi 	struct videomode *vm = &ctx->vm;
36669960aa7cSTomi Valkeinen 
36679960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.lck_div = lckd;
36689960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.pck_div = pckd;
36699960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.lck = lck;
36709960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.pck = pck;
36719960aa7cSTomi Valkeinen 
3672da11bbbbSPeter Ujfalusi 	*vm = *ctx->config->vm;
3673da11bbbbSPeter Ujfalusi 	vm->pixelclock = pck;
3674da11bbbbSPeter Ujfalusi 	vm->hactive = ctx->config->vm->hactive;
3675da11bbbbSPeter Ujfalusi 	vm->vactive = ctx->config->vm->vactive;
3676da11bbbbSPeter Ujfalusi 	vm->hsync_len = vm->hfront_porch = vm->hback_porch = vm->vsync_len = 1;
3677da11bbbbSPeter Ujfalusi 	vm->vfront_porch = vm->vback_porch = 0;
36789960aa7cSTomi Valkeinen 
36799960aa7cSTomi Valkeinen 	return true;
36809960aa7cSTomi Valkeinen }
36819960aa7cSTomi Valkeinen 
dsi_cm_calc_hsdiv_cb(int m_dispc,unsigned long dispc,void * data)36829960aa7cSTomi Valkeinen static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
36839960aa7cSTomi Valkeinen 		void *data)
36849960aa7cSTomi Valkeinen {
36859960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
36869960aa7cSTomi Valkeinen 
36879960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
36889960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
36899960aa7cSTomi Valkeinen 
36908a7eda76SLaurent Pinchart 	return dispc_div_calc(ctx->dsi->dss->dispc, dispc,
36918a7eda76SLaurent Pinchart 			      ctx->req_pck_min, ctx->req_pck_max,
36929960aa7cSTomi Valkeinen 			      dsi_cm_calc_dispc_cb, ctx);
36939960aa7cSTomi Valkeinen }
36949960aa7cSTomi Valkeinen 
dsi_cm_calc_pll_cb(int n,int m,unsigned long fint,unsigned long clkdco,void * data)36959960aa7cSTomi Valkeinen static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint,
36969960aa7cSTomi Valkeinen 		unsigned long clkdco, void *data)
36979960aa7cSTomi Valkeinen {
36989960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
36997093d6cdSLaurent Pinchart 	struct dsi_data *dsi = ctx->dsi;
37009960aa7cSTomi Valkeinen 
37019960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.n = n;
37029960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.m = m;
37039960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.fint = fint;
37049960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.clkdco = clkdco;
37059960aa7cSTomi Valkeinen 
3706cd0715ffSTomi Valkeinen 	return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min,
3707fe9964cbSLaurent Pinchart 			dsi->data->max_fck_freq,
37089960aa7cSTomi Valkeinen 			dsi_cm_calc_hsdiv_cb, ctx);
37099960aa7cSTomi Valkeinen }
37109960aa7cSTomi Valkeinen 
dsi_cm_calc(struct dsi_data * dsi,const struct omap_dss_dsi_config * cfg,struct dsi_clk_calc_ctx * ctx)37119960aa7cSTomi Valkeinen static bool dsi_cm_calc(struct dsi_data *dsi,
37129960aa7cSTomi Valkeinen 		const struct omap_dss_dsi_config *cfg,
37139960aa7cSTomi Valkeinen 		struct dsi_clk_calc_ctx *ctx)
37149960aa7cSTomi Valkeinen {
37159960aa7cSTomi Valkeinen 	unsigned long clkin;
37169960aa7cSTomi Valkeinen 	int bitspp, ndl;
37179960aa7cSTomi Valkeinen 	unsigned long pll_min, pll_max;
37189960aa7cSTomi Valkeinen 	unsigned long pck, txbyteclk;
37199960aa7cSTomi Valkeinen 
37209960aa7cSTomi Valkeinen 	clkin = clk_get_rate(dsi->pll.clkin);
3721d0103cebSSebastian Reichel 	bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format);
37229960aa7cSTomi Valkeinen 	ndl = dsi->num_lanes_used - 1;
37239960aa7cSTomi Valkeinen 
37249960aa7cSTomi Valkeinen 	/*
37259960aa7cSTomi Valkeinen 	 * Here we should calculate minimum txbyteclk to be able to send the
37269960aa7cSTomi Valkeinen 	 * frame in time, and also to handle TE. That's not very simple, though,
37279960aa7cSTomi Valkeinen 	 * especially as we go to LP between each pixel packet due to HW
37289960aa7cSTomi Valkeinen 	 * "feature". So let's just estimate very roughly and multiply by 1.5.
37299960aa7cSTomi Valkeinen 	 */
3730da11bbbbSPeter Ujfalusi 	pck = cfg->vm->pixelclock;
37319960aa7cSTomi Valkeinen 	pck = pck * 3 / 2;
37329960aa7cSTomi Valkeinen 	txbyteclk = pck * bitspp / 8 / ndl;
37339960aa7cSTomi Valkeinen 
37349960aa7cSTomi Valkeinen 	memset(ctx, 0, sizeof(*ctx));
37357093d6cdSLaurent Pinchart 	ctx->dsi = dsi;
37369960aa7cSTomi Valkeinen 	ctx->pll = &dsi->pll;
37379960aa7cSTomi Valkeinen 	ctx->config = cfg;
37389960aa7cSTomi Valkeinen 	ctx->req_pck_min = pck;
37399960aa7cSTomi Valkeinen 	ctx->req_pck_nom = pck;
37409960aa7cSTomi Valkeinen 	ctx->req_pck_max = pck * 3 / 2;
37419960aa7cSTomi Valkeinen 
37429960aa7cSTomi Valkeinen 	pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4);
37439960aa7cSTomi Valkeinen 	pll_max = cfg->hs_clk_max * 4;
37449960aa7cSTomi Valkeinen 
3745cd0715ffSTomi Valkeinen 	return dss_pll_calc_a(ctx->pll, clkin,
37469960aa7cSTomi Valkeinen 			pll_min, pll_max,
37479960aa7cSTomi Valkeinen 			dsi_cm_calc_pll_cb, ctx);
37489960aa7cSTomi Valkeinen }
37499960aa7cSTomi Valkeinen 
dsi_vm_calc_blanking(struct dsi_clk_calc_ctx * ctx)37509960aa7cSTomi Valkeinen static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
37519960aa7cSTomi Valkeinen {
37527093d6cdSLaurent Pinchart 	struct dsi_data *dsi = ctx->dsi;
37539960aa7cSTomi Valkeinen 	const struct omap_dss_dsi_config *cfg = ctx->config;
3754d0103cebSSebastian Reichel 	int bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format);
37559960aa7cSTomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
37569960aa7cSTomi Valkeinen 	unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4;
37579960aa7cSTomi Valkeinen 	unsigned long byteclk = hsclk / 4;
37589960aa7cSTomi Valkeinen 
37599960aa7cSTomi Valkeinen 	unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max;
37609960aa7cSTomi Valkeinen 	int xres;
37619960aa7cSTomi Valkeinen 	int panel_htot, panel_hbl; /* pixels */
37629960aa7cSTomi Valkeinen 	int dispc_htot, dispc_hbl; /* pixels */
37639960aa7cSTomi Valkeinen 	int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */
37649960aa7cSTomi Valkeinen 	int hfp, hsa, hbp;
37654520ff28SPeter Ujfalusi 	const struct videomode *req_vm;
37664520ff28SPeter Ujfalusi 	struct videomode *dispc_vm;
37679960aa7cSTomi Valkeinen 	struct omap_dss_dsi_videomode_timings *dsi_vm;
37689960aa7cSTomi Valkeinen 	u64 dsi_tput, dispc_tput;
37699960aa7cSTomi Valkeinen 
37709960aa7cSTomi Valkeinen 	dsi_tput = (u64)byteclk * ndl * 8;
37719960aa7cSTomi Valkeinen 
3772da11bbbbSPeter Ujfalusi 	req_vm = cfg->vm;
37739960aa7cSTomi Valkeinen 	req_pck_min = ctx->req_pck_min;
37749960aa7cSTomi Valkeinen 	req_pck_max = ctx->req_pck_max;
37759960aa7cSTomi Valkeinen 	req_pck_nom = ctx->req_pck_nom;
37769960aa7cSTomi Valkeinen 
37779960aa7cSTomi Valkeinen 	dispc_pck = ctx->dispc_cinfo.pck;
37789960aa7cSTomi Valkeinen 	dispc_tput = (u64)dispc_pck * bitspp;
37799960aa7cSTomi Valkeinen 
378081899060SPeter Ujfalusi 	xres = req_vm->hactive;
37819960aa7cSTomi Valkeinen 
3782a85f4a80SPeter Ujfalusi 	panel_hbl = req_vm->hfront_porch + req_vm->hback_porch +
3783a85f4a80SPeter Ujfalusi 		    req_vm->hsync_len;
37849960aa7cSTomi Valkeinen 	panel_htot = xres + panel_hbl;
37859960aa7cSTomi Valkeinen 
37869960aa7cSTomi Valkeinen 	dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl);
37879960aa7cSTomi Valkeinen 
37889960aa7cSTomi Valkeinen 	/*
37899960aa7cSTomi Valkeinen 	 * When there are no line buffers, DISPC and DSI must have the
37909960aa7cSTomi Valkeinen 	 * same tput. Otherwise DISPC tput needs to be higher than DSI's.
37919960aa7cSTomi Valkeinen 	 */
37929960aa7cSTomi Valkeinen 	if (dsi->line_buffer_size < xres * bitspp / 8) {
37939960aa7cSTomi Valkeinen 		if (dispc_tput != dsi_tput)
37949960aa7cSTomi Valkeinen 			return false;
37959960aa7cSTomi Valkeinen 	} else {
37969960aa7cSTomi Valkeinen 		if (dispc_tput < dsi_tput)
37979960aa7cSTomi Valkeinen 			return false;
37989960aa7cSTomi Valkeinen 	}
37999960aa7cSTomi Valkeinen 
38009960aa7cSTomi Valkeinen 	/* DSI tput must be over the min requirement */
38019960aa7cSTomi Valkeinen 	if (dsi_tput < (u64)bitspp * req_pck_min)
38029960aa7cSTomi Valkeinen 		return false;
38039960aa7cSTomi Valkeinen 
38049960aa7cSTomi Valkeinen 	/* When non-burst mode, DSI tput must be below max requirement. */
38059960aa7cSTomi Valkeinen 	if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) {
38069960aa7cSTomi Valkeinen 		if (dsi_tput > (u64)bitspp * req_pck_max)
38079960aa7cSTomi Valkeinen 			return false;
38089960aa7cSTomi Valkeinen 	}
38099960aa7cSTomi Valkeinen 
38109960aa7cSTomi Valkeinen 	hss = DIV_ROUND_UP(4, ndl);
38119960aa7cSTomi Valkeinen 
38129960aa7cSTomi Valkeinen 	if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
38134dc2250dSPeter Ujfalusi 		if (ndl == 3 && req_vm->hsync_len == 0)
38149960aa7cSTomi Valkeinen 			hse = 1;
38159960aa7cSTomi Valkeinen 		else
38169960aa7cSTomi Valkeinen 			hse = DIV_ROUND_UP(4, ndl);
38179960aa7cSTomi Valkeinen 	} else {
38189960aa7cSTomi Valkeinen 		hse = 0;
38199960aa7cSTomi Valkeinen 	}
38209960aa7cSTomi Valkeinen 
38219960aa7cSTomi Valkeinen 	/* DSI htot to match the panel's nominal pck */
38229960aa7cSTomi Valkeinen 	dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom);
38239960aa7cSTomi Valkeinen 
38249960aa7cSTomi Valkeinen 	/* fail if there would be no time for blanking */
38259960aa7cSTomi Valkeinen 	if (dsi_htot < hss + hse + dsi_hact)
38269960aa7cSTomi Valkeinen 		return false;
38279960aa7cSTomi Valkeinen 
38289960aa7cSTomi Valkeinen 	/* total DSI blanking needed to achieve panel's TL */
38299960aa7cSTomi Valkeinen 	dsi_hbl = dsi_htot - dsi_hact;
38309960aa7cSTomi Valkeinen 
38319960aa7cSTomi Valkeinen 	/* DISPC htot to match the DSI TL */
38329960aa7cSTomi Valkeinen 	dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk);
38339960aa7cSTomi Valkeinen 
38349960aa7cSTomi Valkeinen 	/* verify that the DSI and DISPC TLs are the same */
38359960aa7cSTomi Valkeinen 	if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk)
38369960aa7cSTomi Valkeinen 		return false;
38379960aa7cSTomi Valkeinen 
38389960aa7cSTomi Valkeinen 	dispc_hbl = dispc_htot - xres;
38399960aa7cSTomi Valkeinen 
38409960aa7cSTomi Valkeinen 	/* setup DSI videomode */
38419960aa7cSTomi Valkeinen 
38429960aa7cSTomi Valkeinen 	dsi_vm = &ctx->dsi_vm;
38439960aa7cSTomi Valkeinen 	memset(dsi_vm, 0, sizeof(*dsi_vm));
38449960aa7cSTomi Valkeinen 
38459960aa7cSTomi Valkeinen 	dsi_vm->hsclk = hsclk;
38469960aa7cSTomi Valkeinen 
38479960aa7cSTomi Valkeinen 	dsi_vm->ndl = ndl;
38489960aa7cSTomi Valkeinen 	dsi_vm->bitspp = bitspp;
38499960aa7cSTomi Valkeinen 
38509960aa7cSTomi Valkeinen 	if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) {
38519960aa7cSTomi Valkeinen 		hsa = 0;
38524dc2250dSPeter Ujfalusi 	} else if (ndl == 3 && req_vm->hsync_len == 0) {
38539960aa7cSTomi Valkeinen 		hsa = 0;
38549960aa7cSTomi Valkeinen 	} else {
38554dc2250dSPeter Ujfalusi 		hsa = div64_u64((u64)req_vm->hsync_len * byteclk, req_pck_nom);
38569960aa7cSTomi Valkeinen 		hsa = max(hsa - hse, 1);
38579960aa7cSTomi Valkeinen 	}
38589960aa7cSTomi Valkeinen 
3859a85f4a80SPeter Ujfalusi 	hbp = div64_u64((u64)req_vm->hback_porch * byteclk, req_pck_nom);
38609960aa7cSTomi Valkeinen 	hbp = max(hbp, 1);
38619960aa7cSTomi Valkeinen 
38629960aa7cSTomi Valkeinen 	hfp = dsi_hbl - (hss + hsa + hse + hbp);
38639960aa7cSTomi Valkeinen 	if (hfp < 1) {
38649960aa7cSTomi Valkeinen 		int t;
38659960aa7cSTomi Valkeinen 		/* we need to take cycles from hbp */
38669960aa7cSTomi Valkeinen 
38679960aa7cSTomi Valkeinen 		t = 1 - hfp;
38689960aa7cSTomi Valkeinen 		hbp = max(hbp - t, 1);
38699960aa7cSTomi Valkeinen 		hfp = dsi_hbl - (hss + hsa + hse + hbp);
38709960aa7cSTomi Valkeinen 
38719960aa7cSTomi Valkeinen 		if (hfp < 1 && hsa > 0) {
38729960aa7cSTomi Valkeinen 			/* we need to take cycles from hsa */
38739960aa7cSTomi Valkeinen 			t = 1 - hfp;
38749960aa7cSTomi Valkeinen 			hsa = max(hsa - t, 1);
38759960aa7cSTomi Valkeinen 			hfp = dsi_hbl - (hss + hsa + hse + hbp);
38769960aa7cSTomi Valkeinen 		}
38779960aa7cSTomi Valkeinen 	}
38789960aa7cSTomi Valkeinen 
38799960aa7cSTomi Valkeinen 	if (hfp < 1)
38809960aa7cSTomi Valkeinen 		return false;
38819960aa7cSTomi Valkeinen 
38829960aa7cSTomi Valkeinen 	dsi_vm->hss = hss;
38839960aa7cSTomi Valkeinen 	dsi_vm->hsa = hsa;
38849960aa7cSTomi Valkeinen 	dsi_vm->hse = hse;
38859960aa7cSTomi Valkeinen 	dsi_vm->hbp = hbp;
38869960aa7cSTomi Valkeinen 	dsi_vm->hact = xres;
38879960aa7cSTomi Valkeinen 	dsi_vm->hfp = hfp;
38889960aa7cSTomi Valkeinen 
3889d5bcf0aaSPeter Ujfalusi 	dsi_vm->vsa = req_vm->vsync_len;
3890458540c6SPeter Ujfalusi 	dsi_vm->vbp = req_vm->vback_porch;
3891fb7f3c43SPeter Ujfalusi 	dsi_vm->vact = req_vm->vactive;
38920996c68eSPeter Ujfalusi 	dsi_vm->vfp = req_vm->vfront_porch;
38939960aa7cSTomi Valkeinen 
38949960aa7cSTomi Valkeinen 	dsi_vm->trans_mode = cfg->trans_mode;
38959960aa7cSTomi Valkeinen 
38969960aa7cSTomi Valkeinen 	dsi_vm->blanking_mode = 0;
38979960aa7cSTomi Valkeinen 	dsi_vm->hsa_blanking_mode = 1;
38989960aa7cSTomi Valkeinen 	dsi_vm->hfp_blanking_mode = 1;
38999960aa7cSTomi Valkeinen 	dsi_vm->hbp_blanking_mode = 1;
39009960aa7cSTomi Valkeinen 
39019960aa7cSTomi Valkeinen 	dsi_vm->window_sync = 4;
39029960aa7cSTomi Valkeinen 
39039960aa7cSTomi Valkeinen 	/* setup DISPC videomode */
39049960aa7cSTomi Valkeinen 
3905da11bbbbSPeter Ujfalusi 	dispc_vm = &ctx->vm;
39069960aa7cSTomi Valkeinen 	*dispc_vm = *req_vm;
39079960aa7cSTomi Valkeinen 	dispc_vm->pixelclock = dispc_pck;
39089960aa7cSTomi Valkeinen 
39099960aa7cSTomi Valkeinen 	if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
39104dc2250dSPeter Ujfalusi 		hsa = div64_u64((u64)req_vm->hsync_len * dispc_pck,
39119960aa7cSTomi Valkeinen 				req_pck_nom);
39129960aa7cSTomi Valkeinen 		hsa = max(hsa, 1);
39139960aa7cSTomi Valkeinen 	} else {
39149960aa7cSTomi Valkeinen 		hsa = 1;
39159960aa7cSTomi Valkeinen 	}
39169960aa7cSTomi Valkeinen 
3917a85f4a80SPeter Ujfalusi 	hbp = div64_u64((u64)req_vm->hback_porch * dispc_pck, req_pck_nom);
39189960aa7cSTomi Valkeinen 	hbp = max(hbp, 1);
39199960aa7cSTomi Valkeinen 
39209960aa7cSTomi Valkeinen 	hfp = dispc_hbl - hsa - hbp;
39219960aa7cSTomi Valkeinen 	if (hfp < 1) {
39229960aa7cSTomi Valkeinen 		int t;
39239960aa7cSTomi Valkeinen 		/* we need to take cycles from hbp */
39249960aa7cSTomi Valkeinen 
39259960aa7cSTomi Valkeinen 		t = 1 - hfp;
39269960aa7cSTomi Valkeinen 		hbp = max(hbp - t, 1);
39279960aa7cSTomi Valkeinen 		hfp = dispc_hbl - hsa - hbp;
39289960aa7cSTomi Valkeinen 
39299960aa7cSTomi Valkeinen 		if (hfp < 1) {
39309960aa7cSTomi Valkeinen 			/* we need to take cycles from hsa */
39319960aa7cSTomi Valkeinen 			t = 1 - hfp;
39329960aa7cSTomi Valkeinen 			hsa = max(hsa - t, 1);
39339960aa7cSTomi Valkeinen 			hfp = dispc_hbl - hsa - hbp;
39349960aa7cSTomi Valkeinen 		}
39359960aa7cSTomi Valkeinen 	}
39369960aa7cSTomi Valkeinen 
39379960aa7cSTomi Valkeinen 	if (hfp < 1)
39389960aa7cSTomi Valkeinen 		return false;
39399960aa7cSTomi Valkeinen 
39400a30e150SPeter Ujfalusi 	dispc_vm->hfront_porch = hfp;
39414dc2250dSPeter Ujfalusi 	dispc_vm->hsync_len = hsa;
3942a85f4a80SPeter Ujfalusi 	dispc_vm->hback_porch = hbp;
39439960aa7cSTomi Valkeinen 
39449960aa7cSTomi Valkeinen 	return true;
39459960aa7cSTomi Valkeinen }
39469960aa7cSTomi Valkeinen 
39479960aa7cSTomi Valkeinen 
dsi_vm_calc_dispc_cb(int lckd,int pckd,unsigned long lck,unsigned long pck,void * data)39489960aa7cSTomi Valkeinen static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
39499960aa7cSTomi Valkeinen 		unsigned long pck, void *data)
39509960aa7cSTomi Valkeinen {
39519960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
39529960aa7cSTomi Valkeinen 
39539960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.lck_div = lckd;
39549960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.pck_div = pckd;
39559960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.lck = lck;
39569960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.pck = pck;
39579960aa7cSTomi Valkeinen 
39589960aa7cSTomi Valkeinen 	if (dsi_vm_calc_blanking(ctx) == false)
39599960aa7cSTomi Valkeinen 		return false;
39609960aa7cSTomi Valkeinen 
39619960aa7cSTomi Valkeinen #ifdef PRINT_VERBOSE_VM_TIMINGS
3962da11bbbbSPeter Ujfalusi 	print_dispc_vm("dispc", &ctx->vm);
39639960aa7cSTomi Valkeinen 	print_dsi_vm("dsi  ", &ctx->dsi_vm);
3964da11bbbbSPeter Ujfalusi 	print_dispc_vm("req  ", ctx->config->vm);
39659960aa7cSTomi Valkeinen 	print_dsi_dispc_vm("act  ", &ctx->dsi_vm);
39669960aa7cSTomi Valkeinen #endif
39679960aa7cSTomi Valkeinen 
39689960aa7cSTomi Valkeinen 	return true;
39699960aa7cSTomi Valkeinen }
39709960aa7cSTomi Valkeinen 
dsi_vm_calc_hsdiv_cb(int m_dispc,unsigned long dispc,void * data)39719960aa7cSTomi Valkeinen static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
39729960aa7cSTomi Valkeinen 		void *data)
39739960aa7cSTomi Valkeinen {
39749960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
39759960aa7cSTomi Valkeinen 	unsigned long pck_max;
39769960aa7cSTomi Valkeinen 
39779960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
39789960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
39799960aa7cSTomi Valkeinen 
39809960aa7cSTomi Valkeinen 	/*
39819960aa7cSTomi Valkeinen 	 * In burst mode we can let the dispc pck be arbitrarily high, but it
39829960aa7cSTomi Valkeinen 	 * limits our scaling abilities. So for now, don't aim too high.
39839960aa7cSTomi Valkeinen 	 */
39849960aa7cSTomi Valkeinen 
39859960aa7cSTomi Valkeinen 	if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE)
39869960aa7cSTomi Valkeinen 		pck_max = ctx->req_pck_max + 10000000;
39879960aa7cSTomi Valkeinen 	else
39889960aa7cSTomi Valkeinen 		pck_max = ctx->req_pck_max;
39899960aa7cSTomi Valkeinen 
39908a7eda76SLaurent Pinchart 	return dispc_div_calc(ctx->dsi->dss->dispc, dispc,
39918a7eda76SLaurent Pinchart 			      ctx->req_pck_min, pck_max,
39929960aa7cSTomi Valkeinen 			      dsi_vm_calc_dispc_cb, ctx);
39939960aa7cSTomi Valkeinen }
39949960aa7cSTomi Valkeinen 
dsi_vm_calc_pll_cb(int n,int m,unsigned long fint,unsigned long clkdco,void * data)39959960aa7cSTomi Valkeinen static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint,
39969960aa7cSTomi Valkeinen 		unsigned long clkdco, void *data)
39979960aa7cSTomi Valkeinen {
39989960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx *ctx = data;
39997093d6cdSLaurent Pinchart 	struct dsi_data *dsi = ctx->dsi;
40009960aa7cSTomi Valkeinen 
40019960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.n = n;
40029960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.m = m;
40039960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.fint = fint;
40049960aa7cSTomi Valkeinen 	ctx->dsi_cinfo.clkdco = clkdco;
40059960aa7cSTomi Valkeinen 
4006cd0715ffSTomi Valkeinen 	return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min,
4007fe9964cbSLaurent Pinchart 			dsi->data->max_fck_freq,
40089960aa7cSTomi Valkeinen 			dsi_vm_calc_hsdiv_cb, ctx);
40099960aa7cSTomi Valkeinen }
40109960aa7cSTomi Valkeinen 
dsi_vm_calc(struct dsi_data * dsi,const struct omap_dss_dsi_config * cfg,struct dsi_clk_calc_ctx * ctx)40119960aa7cSTomi Valkeinen static bool dsi_vm_calc(struct dsi_data *dsi,
40129960aa7cSTomi Valkeinen 		const struct omap_dss_dsi_config *cfg,
40139960aa7cSTomi Valkeinen 		struct dsi_clk_calc_ctx *ctx)
40149960aa7cSTomi Valkeinen {
4015da11bbbbSPeter Ujfalusi 	const struct videomode *vm = cfg->vm;
40169960aa7cSTomi Valkeinen 	unsigned long clkin;
40179960aa7cSTomi Valkeinen 	unsigned long pll_min;
40189960aa7cSTomi Valkeinen 	unsigned long pll_max;
40199960aa7cSTomi Valkeinen 	int ndl = dsi->num_lanes_used - 1;
4020d0103cebSSebastian Reichel 	int bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format);
40219960aa7cSTomi Valkeinen 	unsigned long byteclk_min;
40229960aa7cSTomi Valkeinen 
40239960aa7cSTomi Valkeinen 	clkin = clk_get_rate(dsi->pll.clkin);
40249960aa7cSTomi Valkeinen 
40259960aa7cSTomi Valkeinen 	memset(ctx, 0, sizeof(*ctx));
40267093d6cdSLaurent Pinchart 	ctx->dsi = dsi;
40279960aa7cSTomi Valkeinen 	ctx->pll = &dsi->pll;
40289960aa7cSTomi Valkeinen 	ctx->config = cfg;
40299960aa7cSTomi Valkeinen 
40309960aa7cSTomi Valkeinen 	/* these limits should come from the panel driver */
4031da11bbbbSPeter Ujfalusi 	ctx->req_pck_min = vm->pixelclock - 1000;
4032da11bbbbSPeter Ujfalusi 	ctx->req_pck_nom = vm->pixelclock;
4033da11bbbbSPeter Ujfalusi 	ctx->req_pck_max = vm->pixelclock + 1000;
40349960aa7cSTomi Valkeinen 
40359960aa7cSTomi Valkeinen 	byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
40369960aa7cSTomi Valkeinen 	pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
40379960aa7cSTomi Valkeinen 
40389960aa7cSTomi Valkeinen 	if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) {
40399960aa7cSTomi Valkeinen 		pll_max = cfg->hs_clk_max * 4;
40409960aa7cSTomi Valkeinen 	} else {
40419960aa7cSTomi Valkeinen 		unsigned long byteclk_max;
40429960aa7cSTomi Valkeinen 		byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp,
40439960aa7cSTomi Valkeinen 				ndl * 8);
40449960aa7cSTomi Valkeinen 
40459960aa7cSTomi Valkeinen 		pll_max = byteclk_max * 4 * 4;
40469960aa7cSTomi Valkeinen 	}
40479960aa7cSTomi Valkeinen 
4048cd0715ffSTomi Valkeinen 	return dss_pll_calc_a(ctx->pll, clkin,
40499960aa7cSTomi Valkeinen 			pll_min, pll_max,
40509960aa7cSTomi Valkeinen 			dsi_vm_calc_pll_cb, ctx);
40519960aa7cSTomi Valkeinen }
40529960aa7cSTomi Valkeinen 
dsi_is_video_mode(struct omap_dss_device * dssdev)4053e4869b04SSebastian Reichel static bool dsi_is_video_mode(struct omap_dss_device *dssdev)
4054e4869b04SSebastian Reichel {
4055e4869b04SSebastian Reichel 	struct dsi_data *dsi = to_dsi_data(dssdev);
4056e4869b04SSebastian Reichel 
4057e4869b04SSebastian Reichel 	return dsi->mode == OMAP_DSS_DSI_VIDEO_MODE;
4058e4869b04SSebastian Reichel }
4059e4869b04SSebastian Reichel 
__dsi_calc_config(struct dsi_data * dsi,const struct drm_display_mode * mode,struct dsi_clk_calc_ctx * ctx)406060782431SSebastian Reichel static int __dsi_calc_config(struct dsi_data *dsi,
406160782431SSebastian Reichel 		const struct drm_display_mode *mode,
406260782431SSebastian Reichel 		struct dsi_clk_calc_ctx *ctx)
406360782431SSebastian Reichel {
406460782431SSebastian Reichel 	struct omap_dss_dsi_config cfg = dsi->config;
406560782431SSebastian Reichel 	struct videomode vm;
406660782431SSebastian Reichel 	bool ok;
406760782431SSebastian Reichel 	int r;
406860782431SSebastian Reichel 
406960782431SSebastian Reichel 	drm_display_mode_to_videomode(mode, &vm);
407060782431SSebastian Reichel 
407160782431SSebastian Reichel 	cfg.vm = &vm;
407260782431SSebastian Reichel 	cfg.mode = dsi->mode;
407360782431SSebastian Reichel 	cfg.pixel_format = dsi->pix_fmt;
407460782431SSebastian Reichel 
407560782431SSebastian Reichel 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE)
407660782431SSebastian Reichel 		ok = dsi_vm_calc(dsi, &cfg, ctx);
407760782431SSebastian Reichel 	else
407860782431SSebastian Reichel 		ok = dsi_cm_calc(dsi, &cfg, ctx);
407960782431SSebastian Reichel 
408060782431SSebastian Reichel 	if (!ok)
408160782431SSebastian Reichel 		return -EINVAL;
408260782431SSebastian Reichel 
408360782431SSebastian Reichel 	dsi_pll_calc_dsi_fck(dsi, &ctx->dsi_cinfo);
408460782431SSebastian Reichel 
408560782431SSebastian Reichel 	r = dsi_lp_clock_calc(ctx->dsi_cinfo.clkout[HSDIV_DSI],
408660782431SSebastian Reichel 		cfg.lp_clk_min, cfg.lp_clk_max, &ctx->lp_cinfo);
408760782431SSebastian Reichel 	if (r)
408860782431SSebastian Reichel 		return r;
408960782431SSebastian Reichel 
409060782431SSebastian Reichel 	return 0;
409160782431SSebastian Reichel }
409260782431SSebastian Reichel 
dsi_set_config(struct omap_dss_device * dssdev,const struct drm_display_mode * mode)40939960aa7cSTomi Valkeinen static int dsi_set_config(struct omap_dss_device *dssdev,
40941cac9ba2SSebastian Reichel 		const struct drm_display_mode *mode)
40959960aa7cSTomi Valkeinen {
4096c068408eSLaurent Pinchart 	struct dsi_data *dsi = to_dsi_data(dssdev);
40979960aa7cSTomi Valkeinen 	struct dsi_clk_calc_ctx ctx;
40989960aa7cSTomi Valkeinen 	int r;
40999960aa7cSTomi Valkeinen 
41009960aa7cSTomi Valkeinen 	mutex_lock(&dsi->lock);
41019960aa7cSTomi Valkeinen 
410260782431SSebastian Reichel 	r = __dsi_calc_config(dsi, mode, &ctx);
41039960aa7cSTomi Valkeinen 	if (r) {
410460782431SSebastian Reichel 		DSSERR("failed to find suitable DSI clock settings\n");
41059960aa7cSTomi Valkeinen 		goto err;
41069960aa7cSTomi Valkeinen 	}
41079960aa7cSTomi Valkeinen 
410860782431SSebastian Reichel 	dsi->user_lp_cinfo = ctx.lp_cinfo;
41099960aa7cSTomi Valkeinen 	dsi->user_dsi_cinfo = ctx.dsi_cinfo;
41109960aa7cSTomi Valkeinen 	dsi->user_dispc_cinfo = ctx.dispc_cinfo;
41119960aa7cSTomi Valkeinen 
4112da11bbbbSPeter Ujfalusi 	dsi->vm = ctx.vm;
41137d39e59bSLaurent Pinchart 
41147d39e59bSLaurent Pinchart 	/*
41157d39e59bSLaurent Pinchart 	 * override interlace, logic level and edge related parameters in
41167d39e59bSLaurent Pinchart 	 * videomode with default values
41177d39e59bSLaurent Pinchart 	 */
41187d39e59bSLaurent Pinchart 	dsi->vm.flags &= ~DISPLAY_FLAGS_INTERLACED;
41197d39e59bSLaurent Pinchart 	dsi->vm.flags &= ~DISPLAY_FLAGS_HSYNC_LOW;
41207d39e59bSLaurent Pinchart 	dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
41217d39e59bSLaurent Pinchart 	dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW;
41227d39e59bSLaurent Pinchart 	dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH;
41236297388eSTomi Valkeinen 	/*
41246297388eSTomi Valkeinen 	 * HACK: These flags should be handled through the omap_dss_device bus
41256297388eSTomi Valkeinen 	 * flags, but this will only be possible when the DSI encoder will be
41266297388eSTomi Valkeinen 	 * converted to the omapdrm-managed encoder model.
41276297388eSTomi Valkeinen 	 */
41286297388eSTomi Valkeinen 	dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE;
41296297388eSTomi Valkeinen 	dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
41306297388eSTomi Valkeinen 	dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW;
41316297388eSTomi Valkeinen 	dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH;
41326297388eSTomi Valkeinen 	dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE;
41336297388eSTomi Valkeinen 	dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
41347d39e59bSLaurent Pinchart 
4135d8dbe791SLaurent Pinchart 	dss_mgr_set_timings(&dsi->output, &dsi->vm);
4136d8dbe791SLaurent Pinchart 
41379960aa7cSTomi Valkeinen 	dsi->vm_timings = ctx.dsi_vm;
41389960aa7cSTomi Valkeinen 
41399960aa7cSTomi Valkeinen 	mutex_unlock(&dsi->lock);
41409960aa7cSTomi Valkeinen 
41419960aa7cSTomi Valkeinen 	return 0;
41429960aa7cSTomi Valkeinen err:
41439960aa7cSTomi Valkeinen 	mutex_unlock(&dsi->lock);
41449960aa7cSTomi Valkeinen 
41459960aa7cSTomi Valkeinen 	return r;
41469960aa7cSTomi Valkeinen }
41479960aa7cSTomi Valkeinen 
41489960aa7cSTomi Valkeinen /*
4149f408600fSTomi Valkeinen  * Return a hardcoded dispc channel for the DSI output. This should work for
41509960aa7cSTomi Valkeinen  * current use cases, but this can be later expanded to either resolve
41519960aa7cSTomi Valkeinen  * the channel in some more dynamic manner, or get the channel as a user
41529960aa7cSTomi Valkeinen  * parameter.
41539960aa7cSTomi Valkeinen  */
dsi_get_dispc_channel(struct dsi_data * dsi)4154f408600fSTomi Valkeinen static enum omap_channel dsi_get_dispc_channel(struct dsi_data *dsi)
41559960aa7cSTomi Valkeinen {
4156742e693bSLaurent Pinchart 	switch (dsi->data->model) {
4157742e693bSLaurent Pinchart 	case DSI_MODEL_OMAP3:
41589960aa7cSTomi Valkeinen 		return OMAP_DSS_CHANNEL_LCD;
41599960aa7cSTomi Valkeinen 
4160742e693bSLaurent Pinchart 	case DSI_MODEL_OMAP4:
4161742e693bSLaurent Pinchart 		switch (dsi->module_id) {
41629960aa7cSTomi Valkeinen 		case 0:
41639960aa7cSTomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
41649960aa7cSTomi Valkeinen 		case 1:
41659960aa7cSTomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD2;
41669960aa7cSTomi Valkeinen 		default:
41679960aa7cSTomi Valkeinen 			DSSWARN("unsupported module id\n");
41689960aa7cSTomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
41699960aa7cSTomi Valkeinen 		}
41709960aa7cSTomi Valkeinen 
4171742e693bSLaurent Pinchart 	case DSI_MODEL_OMAP5:
4172742e693bSLaurent Pinchart 		switch (dsi->module_id) {
41739960aa7cSTomi Valkeinen 		case 0:
41749960aa7cSTomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
41759960aa7cSTomi Valkeinen 		case 1:
41769960aa7cSTomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD3;
41779960aa7cSTomi Valkeinen 		default:
41789960aa7cSTomi Valkeinen 			DSSWARN("unsupported module id\n");
41799960aa7cSTomi Valkeinen 			return OMAP_DSS_CHANNEL_LCD;
41809960aa7cSTomi Valkeinen 		}
41819960aa7cSTomi Valkeinen 
41829960aa7cSTomi Valkeinen 	default:
41839960aa7cSTomi Valkeinen 		DSSWARN("unsupported DSS version\n");
41849960aa7cSTomi Valkeinen 		return OMAP_DSS_CHANNEL_LCD;
41859960aa7cSTomi Valkeinen 	}
41869960aa7cSTomi Valkeinen }
41879960aa7cSTomi Valkeinen 
_omap_dsi_host_transfer(struct dsi_data * dsi,int vc,const struct mipi_dsi_msg * msg)41885e430754STomi Valkeinen static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc,
41899e8d3b92SSebastian Reichel 				       const struct mipi_dsi_msg *msg)
41909e8d3b92SSebastian Reichel {
41919cd87829SSebastian Reichel 	struct omap_dss_device *dssdev = &dsi->output;
4192e9c7a0d7SSebastian Reichel 	int r;
41939cd87829SSebastian Reichel 
419483d74642STomi Valkeinen 	dsi_vc_enable_hs(dssdev, vc, !(msg->flags & MIPI_DSI_MSG_USE_LPM));
41953220034bSSebastian Reichel 
41969e8d3b92SSebastian Reichel 	switch (msg->type) {
41979e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
41989e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
41999e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
42009e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_LONG_WRITE:
42019e8d3b92SSebastian Reichel 	case MIPI_DSI_DCS_SHORT_WRITE:
42029e8d3b92SSebastian Reichel 	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
42039e8d3b92SSebastian Reichel 	case MIPI_DSI_DCS_LONG_WRITE:
4204e7096538SSebastian Reichel 	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
4205e7096538SSebastian Reichel 	case MIPI_DSI_NULL_PACKET:
42065e430754STomi Valkeinen 		r = dsi_vc_write_common(dssdev, vc, msg);
4207e9c7a0d7SSebastian Reichel 		break;
42089e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
42099e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
42109e8d3b92SSebastian Reichel 	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
42115e430754STomi Valkeinen 		r = dsi_vc_generic_read(dssdev, vc, msg);
4212e9c7a0d7SSebastian Reichel 		break;
42139e8d3b92SSebastian Reichel 	case MIPI_DSI_DCS_READ:
42145e430754STomi Valkeinen 		r = dsi_vc_dcs_read(dssdev, vc, msg);
4215e9c7a0d7SSebastian Reichel 		break;
4216e9c7a0d7SSebastian Reichel 	default:
4217e9c7a0d7SSebastian Reichel 		r = -EINVAL;
4218e9c7a0d7SSebastian Reichel 		break;
42199e8d3b92SSebastian Reichel 	}
42209e8d3b92SSebastian Reichel 
4221e9c7a0d7SSebastian Reichel 	if (r < 0)
4222e9c7a0d7SSebastian Reichel 		return r;
4223e9c7a0d7SSebastian Reichel 
4224e9c7a0d7SSebastian Reichel 	if (msg->type == MIPI_DSI_DCS_SHORT_WRITE ||
4225e9c7a0d7SSebastian Reichel 	    msg->type == MIPI_DSI_DCS_SHORT_WRITE_PARAM) {
4226e9c7a0d7SSebastian Reichel 		u8 cmd = ((u8 *)msg->tx_buf)[0];
4227e9c7a0d7SSebastian Reichel 
4228e9c7a0d7SSebastian Reichel 		if (cmd == MIPI_DCS_SET_TEAR_OFF)
4229e9c7a0d7SSebastian Reichel 			dsi_enable_te(dsi, false);
4230e9c7a0d7SSebastian Reichel 		else if (cmd == MIPI_DCS_SET_TEAR_ON)
4231e9c7a0d7SSebastian Reichel 			dsi_enable_te(dsi, true);
4232e9c7a0d7SSebastian Reichel 	}
4233e9c7a0d7SSebastian Reichel 
4234e9c7a0d7SSebastian Reichel 	return 0;
42359e8d3b92SSebastian Reichel }
42369960aa7cSTomi Valkeinen 
omap_dsi_host_transfer(struct mipi_dsi_host * host,const struct mipi_dsi_msg * msg)42370c93a61dSSebastian Reichel static ssize_t omap_dsi_host_transfer(struct mipi_dsi_host *host,
42380c93a61dSSebastian Reichel 				      const struct mipi_dsi_msg *msg)
42390c93a61dSSebastian Reichel {
42400c93a61dSSebastian Reichel 	struct dsi_data *dsi = host_to_omap(host);
42410c93a61dSSebastian Reichel 	int r;
42424029b16bSTomi Valkeinen 	int vc = VC_CMD;
42430c93a61dSSebastian Reichel 
42440c93a61dSSebastian Reichel 	dsi_bus_lock(dsi);
42451cac9ba2SSebastian Reichel 
4246dfd2edccSTomi Valkeinen 	if (!dsi->iface_enabled) {
4247dfd2edccSTomi Valkeinen 		dsi_enable(dsi);
4248dfd2edccSTomi Valkeinen 		schedule_delayed_work(&dsi->dsi_disable_work, msecs_to_jiffies(2000));
4249dfd2edccSTomi Valkeinen 	}
4250dfd2edccSTomi Valkeinen 
42515e430754STomi Valkeinen 	r = _omap_dsi_host_transfer(dsi, vc, msg);
42521cac9ba2SSebastian Reichel 
42530c93a61dSSebastian Reichel 	dsi_bus_unlock(dsi);
42540c93a61dSSebastian Reichel 
42550c93a61dSSebastian Reichel 	return r;
42560c93a61dSSebastian Reichel }
42570c93a61dSSebastian Reichel 
dsi_get_clocks(struct dsi_data * dsi)42587093d6cdSLaurent Pinchart static int dsi_get_clocks(struct dsi_data *dsi)
42599960aa7cSTomi Valkeinen {
42609960aa7cSTomi Valkeinen 	struct clk *clk;
42619960aa7cSTomi Valkeinen 
42624600ea9cSLaurent Pinchart 	clk = devm_clk_get(dsi->dev, "fck");
42639960aa7cSTomi Valkeinen 	if (IS_ERR(clk)) {
42649960aa7cSTomi Valkeinen 		DSSERR("can't get fck\n");
42659960aa7cSTomi Valkeinen 		return PTR_ERR(clk);
42669960aa7cSTomi Valkeinen 	}
42679960aa7cSTomi Valkeinen 
42689960aa7cSTomi Valkeinen 	dsi->dss_clk = clk;
42699960aa7cSTomi Valkeinen 
42709960aa7cSTomi Valkeinen 	return 0;
42719960aa7cSTomi Valkeinen }
42729960aa7cSTomi Valkeinen 
427394d73329SSebastian Reichel static const struct omapdss_dsi_ops dsi_ops = {
42742a4703c2SSebastian Reichel 	.update = dsi_update_all,
4275e4869b04SSebastian Reichel 	.is_video_mode = dsi_is_video_mode,
42769960aa7cSTomi Valkeinen };
42779960aa7cSTomi Valkeinen 
omap_dsi_te_irq_handler(int irq,void * dev_id)42784c1b935fSSebastian Reichel static irqreturn_t omap_dsi_te_irq_handler(int irq, void *dev_id)
42794c1b935fSSebastian Reichel {
42804c1b935fSSebastian Reichel 	struct dsi_data *dsi = (struct dsi_data *)dev_id;
42814c1b935fSSebastian Reichel 	int old;
42824c1b935fSSebastian Reichel 
42834c1b935fSSebastian Reichel 	old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0);
42844c1b935fSSebastian Reichel 	if (old) {
42854c1b935fSSebastian Reichel 		cancel_delayed_work(&dsi->te_timeout_work);
42864c1b935fSSebastian Reichel 		_dsi_update(dsi);
42874c1b935fSSebastian Reichel 	}
42884c1b935fSSebastian Reichel 
42894c1b935fSSebastian Reichel 	return IRQ_HANDLED;
42904c1b935fSSebastian Reichel }
42914c1b935fSSebastian Reichel 
omap_dsi_te_timeout_work_callback(struct work_struct * work)42924c1b935fSSebastian Reichel static void omap_dsi_te_timeout_work_callback(struct work_struct *work)
42934c1b935fSSebastian Reichel {
42944c1b935fSSebastian Reichel 	struct dsi_data *dsi =
42954c1b935fSSebastian Reichel 		container_of(work, struct dsi_data, te_timeout_work.work);
42964c1b935fSSebastian Reichel 	int old;
42974c1b935fSSebastian Reichel 
42984c1b935fSSebastian Reichel 	old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0);
42994c1b935fSSebastian Reichel 	if (old) {
43004c1b935fSSebastian Reichel 		dev_err(dsi->dev, "TE not received for 250ms!\n");
43014c1b935fSSebastian Reichel 		_dsi_update(dsi);
43024c1b935fSSebastian Reichel 	}
43034c1b935fSSebastian Reichel }
43044c1b935fSSebastian Reichel 
omap_dsi_register_te_irq(struct dsi_data * dsi,struct mipi_dsi_device * client)43054c1b935fSSebastian Reichel static int omap_dsi_register_te_irq(struct dsi_data *dsi,
43064c1b935fSSebastian Reichel 				    struct mipi_dsi_device *client)
43074c1b935fSSebastian Reichel {
43084c1b935fSSebastian Reichel 	int err;
43094c1b935fSSebastian Reichel 	int te_irq;
43104c1b935fSSebastian Reichel 
43114c1b935fSSebastian Reichel 	dsi->te_gpio = gpiod_get(&client->dev, "te-gpios", GPIOD_IN);
43124c1b935fSSebastian Reichel 	if (IS_ERR(dsi->te_gpio)) {
43134c1b935fSSebastian Reichel 		err = PTR_ERR(dsi->te_gpio);
43144c1b935fSSebastian Reichel 
43154c1b935fSSebastian Reichel 		if (err == -ENOENT) {
43164c1b935fSSebastian Reichel 			dsi->te_gpio = NULL;
43174c1b935fSSebastian Reichel 			return 0;
43184c1b935fSSebastian Reichel 		}
43194c1b935fSSebastian Reichel 
43204c1b935fSSebastian Reichel 		dev_err(dsi->dev, "Could not get TE gpio: %d\n", err);
43214c1b935fSSebastian Reichel 		return err;
43224c1b935fSSebastian Reichel 	}
43234c1b935fSSebastian Reichel 
43244c1b935fSSebastian Reichel 	te_irq = gpiod_to_irq(dsi->te_gpio);
43254c1b935fSSebastian Reichel 	if (te_irq < 0) {
43264c1b935fSSebastian Reichel 		gpiod_put(dsi->te_gpio);
43274c1b935fSSebastian Reichel 		dsi->te_gpio = NULL;
43284c1b935fSSebastian Reichel 		return -EINVAL;
43294c1b935fSSebastian Reichel 	}
43304c1b935fSSebastian Reichel 
43314c1b935fSSebastian Reichel 	dsi->te_irq = te_irq;
43324c1b935fSSebastian Reichel 
43334c1b935fSSebastian Reichel 	irq_set_status_flags(te_irq, IRQ_NOAUTOEN);
43344c1b935fSSebastian Reichel 
43354c1b935fSSebastian Reichel 	err = request_threaded_irq(te_irq, NULL, omap_dsi_te_irq_handler,
43360cafc8d8SYang Li 				   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
43370cafc8d8SYang Li 				   "TE", dsi);
43384c1b935fSSebastian Reichel 	if (err) {
43394c1b935fSSebastian Reichel 		dev_err(dsi->dev, "request irq failed with %d\n", err);
43404c1b935fSSebastian Reichel 		gpiod_put(dsi->te_gpio);
43414c1b935fSSebastian Reichel 		dsi->te_gpio = NULL;
43424c1b935fSSebastian Reichel 		return err;
43434c1b935fSSebastian Reichel 	}
43444c1b935fSSebastian Reichel 
43454c1b935fSSebastian Reichel 	INIT_DEFERRABLE_WORK(&dsi->te_timeout_work,
43464c1b935fSSebastian Reichel 			     omap_dsi_te_timeout_work_callback);
43474c1b935fSSebastian Reichel 
43484c1b935fSSebastian Reichel 	dev_dbg(dsi->dev, "Using GPIO TE\n");
43494c1b935fSSebastian Reichel 
43504c1b935fSSebastian Reichel 	return 0;
43514c1b935fSSebastian Reichel }
43524c1b935fSSebastian Reichel 
omap_dsi_unregister_te_irq(struct dsi_data * dsi)43534c1b935fSSebastian Reichel static void omap_dsi_unregister_te_irq(struct dsi_data *dsi)
43544c1b935fSSebastian Reichel {
43554c1b935fSSebastian Reichel 	if (dsi->te_gpio) {
43564c1b935fSSebastian Reichel 		free_irq(dsi->te_irq, dsi);
43574c1b935fSSebastian Reichel 		cancel_delayed_work(&dsi->te_timeout_work);
43584c1b935fSSebastian Reichel 		gpiod_put(dsi->te_gpio);
43594c1b935fSSebastian Reichel 		dsi->te_gpio = NULL;
43604c1b935fSSebastian Reichel 	}
43614c1b935fSSebastian Reichel }
43624c1b935fSSebastian Reichel 
omap_dsi_host_attach(struct mipi_dsi_host * host,struct mipi_dsi_device * client)43639cd87829SSebastian Reichel static int omap_dsi_host_attach(struct mipi_dsi_host *host,
4364a5f2dcdeSSebastian Reichel 				struct mipi_dsi_device *client)
43659cd87829SSebastian Reichel {
4366a5f2dcdeSSebastian Reichel 	struct dsi_data *dsi = host_to_omap(host);
43674c1b935fSSebastian Reichel 	int r;
4368a5f2dcdeSSebastian Reichel 
4369d843314eSTomi Valkeinen 	if (dsi->dsidev) {
4370d843314eSTomi Valkeinen 		DSSERR("dsi client already attached\n");
4371a5f2dcdeSSebastian Reichel 		return -EBUSY;
4372a5f2dcdeSSebastian Reichel 	}
4373a5f2dcdeSSebastian Reichel 
437468ca91d7SSebastian Reichel 	if (mipi_dsi_pixel_format_to_bpp(client->format) < 0) {
437568ca91d7SSebastian Reichel 		DSSERR("invalid pixel format\n");
437668ca91d7SSebastian Reichel 		return -EINVAL;
437768ca91d7SSebastian Reichel 	}
437868ca91d7SSebastian Reichel 
43794c1b935fSSebastian Reichel 	atomic_set(&dsi->do_ext_te_update, 0);
438068ca91d7SSebastian Reichel 
43814c1b935fSSebastian Reichel 	if (client->mode_flags & MIPI_DSI_MODE_VIDEO) {
438268ca91d7SSebastian Reichel 		dsi->mode = OMAP_DSS_DSI_VIDEO_MODE;
43834c1b935fSSebastian Reichel 	} else {
43844c1b935fSSebastian Reichel 		r = omap_dsi_register_te_irq(dsi, client);
43854c1b935fSSebastian Reichel 		if (r)
43864c1b935fSSebastian Reichel 			return r;
43874c1b935fSSebastian Reichel 
438868ca91d7SSebastian Reichel 		dsi->mode = OMAP_DSS_DSI_CMD_MODE;
43894c1b935fSSebastian Reichel 	}
43904c1b935fSSebastian Reichel 
4391d843314eSTomi Valkeinen 	dsi->dsidev = client;
43924c1b935fSSebastian Reichel 	dsi->pix_fmt = client->format;
439368ca91d7SSebastian Reichel 
43941cac9ba2SSebastian Reichel 	dsi->config.hs_clk_min = 150000000; // TODO: get from client?
43951cac9ba2SSebastian Reichel 	dsi->config.hs_clk_max = client->hs_rate;
43961cac9ba2SSebastian Reichel 	dsi->config.lp_clk_min = 7000000; // TODO: get from client?
43971cac9ba2SSebastian Reichel 	dsi->config.lp_clk_max = client->lp_rate;
43981cac9ba2SSebastian Reichel 
4399443dae09STomi Valkeinen 	if (client->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
4400443dae09STomi Valkeinen 		dsi->config.trans_mode = OMAP_DSS_DSI_BURST_MODE;
4401443dae09STomi Valkeinen 	else if (client->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
4402443dae09STomi Valkeinen 		dsi->config.trans_mode = OMAP_DSS_DSI_PULSE_MODE;
4403443dae09STomi Valkeinen 	else
4404443dae09STomi Valkeinen 		dsi->config.trans_mode = OMAP_DSS_DSI_EVENT_MODE;
4405443dae09STomi Valkeinen 
44069cd87829SSebastian Reichel 	return 0;
44079cd87829SSebastian Reichel }
44089cd87829SSebastian Reichel 
omap_dsi_host_detach(struct mipi_dsi_host * host,struct mipi_dsi_device * client)44099cd87829SSebastian Reichel static int omap_dsi_host_detach(struct mipi_dsi_host *host,
4410a5f2dcdeSSebastian Reichel 				struct mipi_dsi_device *client)
44119cd87829SSebastian Reichel {
4412a5f2dcdeSSebastian Reichel 	struct dsi_data *dsi = host_to_omap(host);
4413a5f2dcdeSSebastian Reichel 
4414d843314eSTomi Valkeinen 	if (WARN_ON(dsi->dsidev != client))
4415a5f2dcdeSSebastian Reichel 		return -EINVAL;
4416a5f2dcdeSSebastian Reichel 
4417dfd2edccSTomi Valkeinen 	cancel_delayed_work_sync(&dsi->dsi_disable_work);
4418dfd2edccSTomi Valkeinen 
4419dfd2edccSTomi Valkeinen 	dsi_bus_lock(dsi);
4420dfd2edccSTomi Valkeinen 
4421dfd2edccSTomi Valkeinen 	if (dsi->iface_enabled)
4422dfd2edccSTomi Valkeinen 		dsi_disable(dsi);
4423dfd2edccSTomi Valkeinen 
4424dfd2edccSTomi Valkeinen 	dsi_bus_unlock(dsi);
4425dfd2edccSTomi Valkeinen 
44264c1b935fSSebastian Reichel 	omap_dsi_unregister_te_irq(dsi);
4427d843314eSTomi Valkeinen 	dsi->dsidev = NULL;
44289cd87829SSebastian Reichel 	return 0;
44299cd87829SSebastian Reichel }
44309cd87829SSebastian Reichel 
44319cd87829SSebastian Reichel static const struct mipi_dsi_host_ops omap_dsi_host_ops = {
44329cd87829SSebastian Reichel 	.attach = omap_dsi_host_attach,
44339cd87829SSebastian Reichel 	.detach = omap_dsi_host_detach,
44349cd87829SSebastian Reichel 	.transfer = omap_dsi_host_transfer,
44359cd87829SSebastian Reichel };
44369cd87829SSebastian Reichel 
4437edb715dfSLaurent Pinchart /* -----------------------------------------------------------------------------
4438edb715dfSLaurent Pinchart  * PLL
4439edb715dfSLaurent Pinchart  */
44409960aa7cSTomi Valkeinen 
44419960aa7cSTomi Valkeinen static const struct dss_pll_ops dsi_pll_ops = {
44429960aa7cSTomi Valkeinen 	.enable = dsi_pll_enable,
44439960aa7cSTomi Valkeinen 	.disable = dsi_pll_disable,
44449960aa7cSTomi Valkeinen 	.set_config = dss_pll_write_config_type_a,
44459960aa7cSTomi Valkeinen };
44469960aa7cSTomi Valkeinen 
44479960aa7cSTomi Valkeinen static const struct dss_pll_hw dss_omap3_dsi_pll_hw = {
444806ede3ddSTomi Valkeinen 	.type = DSS_PLL_TYPE_A,
444906ede3ddSTomi Valkeinen 
44509960aa7cSTomi Valkeinen 	.n_max = (1 << 7) - 1,
44519960aa7cSTomi Valkeinen 	.m_max = (1 << 11) - 1,
44529960aa7cSTomi Valkeinen 	.mX_max = (1 << 4) - 1,
44539960aa7cSTomi Valkeinen 	.fint_min = 750000,
44549960aa7cSTomi Valkeinen 	.fint_max = 2100000,
44559960aa7cSTomi Valkeinen 	.clkdco_low = 1000000000,
44569960aa7cSTomi Valkeinen 	.clkdco_max = 1800000000,
44579960aa7cSTomi Valkeinen 
44589960aa7cSTomi Valkeinen 	.n_msb = 7,
44599960aa7cSTomi Valkeinen 	.n_lsb = 1,
44609960aa7cSTomi Valkeinen 	.m_msb = 18,
44619960aa7cSTomi Valkeinen 	.m_lsb = 8,
44629960aa7cSTomi Valkeinen 
44639960aa7cSTomi Valkeinen 	.mX_msb[0] = 22,
44649960aa7cSTomi Valkeinen 	.mX_lsb[0] = 19,
44659960aa7cSTomi Valkeinen 	.mX_msb[1] = 26,
44669960aa7cSTomi Valkeinen 	.mX_lsb[1] = 23,
44679960aa7cSTomi Valkeinen 
44689960aa7cSTomi Valkeinen 	.has_stopmode = true,
44699960aa7cSTomi Valkeinen 	.has_freqsel = true,
44709960aa7cSTomi Valkeinen 	.has_selfreqdco = false,
44719960aa7cSTomi Valkeinen 	.has_refsel = false,
44729960aa7cSTomi Valkeinen };
44739960aa7cSTomi Valkeinen 
44749960aa7cSTomi Valkeinen static const struct dss_pll_hw dss_omap4_dsi_pll_hw = {
447506ede3ddSTomi Valkeinen 	.type = DSS_PLL_TYPE_A,
447606ede3ddSTomi Valkeinen 
44779960aa7cSTomi Valkeinen 	.n_max = (1 << 8) - 1,
44789960aa7cSTomi Valkeinen 	.m_max = (1 << 12) - 1,
44799960aa7cSTomi Valkeinen 	.mX_max = (1 << 5) - 1,
44809960aa7cSTomi Valkeinen 	.fint_min = 500000,
44819960aa7cSTomi Valkeinen 	.fint_max = 2500000,
44829960aa7cSTomi Valkeinen 	.clkdco_low = 1000000000,
44839960aa7cSTomi Valkeinen 	.clkdco_max = 1800000000,
44849960aa7cSTomi Valkeinen 
44859960aa7cSTomi Valkeinen 	.n_msb = 8,
44869960aa7cSTomi Valkeinen 	.n_lsb = 1,
44879960aa7cSTomi Valkeinen 	.m_msb = 20,
44889960aa7cSTomi Valkeinen 	.m_lsb = 9,
44899960aa7cSTomi Valkeinen 
44909960aa7cSTomi Valkeinen 	.mX_msb[0] = 25,
44919960aa7cSTomi Valkeinen 	.mX_lsb[0] = 21,
44929960aa7cSTomi Valkeinen 	.mX_msb[1] = 30,
44939960aa7cSTomi Valkeinen 	.mX_lsb[1] = 26,
44949960aa7cSTomi Valkeinen 
44959960aa7cSTomi Valkeinen 	.has_stopmode = true,
44969960aa7cSTomi Valkeinen 	.has_freqsel = false,
44979960aa7cSTomi Valkeinen 	.has_selfreqdco = false,
44989960aa7cSTomi Valkeinen 	.has_refsel = false,
44999960aa7cSTomi Valkeinen };
45009960aa7cSTomi Valkeinen 
45019960aa7cSTomi Valkeinen static const struct dss_pll_hw dss_omap5_dsi_pll_hw = {
450206ede3ddSTomi Valkeinen 	.type = DSS_PLL_TYPE_A,
450306ede3ddSTomi Valkeinen 
45049960aa7cSTomi Valkeinen 	.n_max = (1 << 8) - 1,
45059960aa7cSTomi Valkeinen 	.m_max = (1 << 12) - 1,
45069960aa7cSTomi Valkeinen 	.mX_max = (1 << 5) - 1,
45079960aa7cSTomi Valkeinen 	.fint_min = 150000,
45089960aa7cSTomi Valkeinen 	.fint_max = 52000000,
45099960aa7cSTomi Valkeinen 	.clkdco_low = 1000000000,
45109960aa7cSTomi Valkeinen 	.clkdco_max = 1800000000,
45119960aa7cSTomi Valkeinen 
45129960aa7cSTomi Valkeinen 	.n_msb = 8,
45139960aa7cSTomi Valkeinen 	.n_lsb = 1,
45149960aa7cSTomi Valkeinen 	.m_msb = 20,
45159960aa7cSTomi Valkeinen 	.m_lsb = 9,
45169960aa7cSTomi Valkeinen 
45179960aa7cSTomi Valkeinen 	.mX_msb[0] = 25,
45189960aa7cSTomi Valkeinen 	.mX_lsb[0] = 21,
45199960aa7cSTomi Valkeinen 	.mX_msb[1] = 30,
45209960aa7cSTomi Valkeinen 	.mX_lsb[1] = 26,
45219960aa7cSTomi Valkeinen 
45229960aa7cSTomi Valkeinen 	.has_stopmode = true,
45239960aa7cSTomi Valkeinen 	.has_freqsel = false,
45249960aa7cSTomi Valkeinen 	.has_selfreqdco = true,
45259960aa7cSTomi Valkeinen 	.has_refsel = true,
45269960aa7cSTomi Valkeinen };
45279960aa7cSTomi Valkeinen 
dsi_init_pll_data(struct dss_device * dss,struct dsi_data * dsi)45287093d6cdSLaurent Pinchart static int dsi_init_pll_data(struct dss_device *dss, struct dsi_data *dsi)
45299960aa7cSTomi Valkeinen {
45309960aa7cSTomi Valkeinen 	struct dss_pll *pll = &dsi->pll;
45319960aa7cSTomi Valkeinen 	struct clk *clk;
45329960aa7cSTomi Valkeinen 	int r;
45339960aa7cSTomi Valkeinen 
45344600ea9cSLaurent Pinchart 	clk = devm_clk_get(dsi->dev, "sys_clk");
45359960aa7cSTomi Valkeinen 	if (IS_ERR(clk)) {
45369960aa7cSTomi Valkeinen 		DSSERR("can't get sys_clk\n");
45379960aa7cSTomi Valkeinen 		return PTR_ERR(clk);
45389960aa7cSTomi Valkeinen 	}
45399960aa7cSTomi Valkeinen 
45409960aa7cSTomi Valkeinen 	pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1";
45419960aa7cSTomi Valkeinen 	pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2;
45429960aa7cSTomi Valkeinen 	pll->clkin = clk;
45439960aa7cSTomi Valkeinen 	pll->base = dsi->pll_base;
4544742e693bSLaurent Pinchart 	pll->hw = dsi->data->pll_hw;
45459960aa7cSTomi Valkeinen 	pll->ops = &dsi_pll_ops;
45469960aa7cSTomi Valkeinen 
4547798957aeSLaurent Pinchart 	r = dss_pll_register(dss, pll);
45489960aa7cSTomi Valkeinen 	if (r)
45499960aa7cSTomi Valkeinen 		return r;
45509960aa7cSTomi Valkeinen 
45519960aa7cSTomi Valkeinen 	return 0;
45529960aa7cSTomi Valkeinen }
45539960aa7cSTomi Valkeinen 
4554edb715dfSLaurent Pinchart /* -----------------------------------------------------------------------------
4555edb715dfSLaurent Pinchart  * Component Bind & Unbind
4556edb715dfSLaurent Pinchart  */
4557edb715dfSLaurent Pinchart 
dsi_bind(struct device * dev,struct device * master,void * data)4558edb715dfSLaurent Pinchart static int dsi_bind(struct device *dev, struct device *master, void *data)
4559edb715dfSLaurent Pinchart {
4560edb715dfSLaurent Pinchart 	struct dss_device *dss = dss_get_device(master);
4561edb715dfSLaurent Pinchart 	struct dsi_data *dsi = dev_get_drvdata(dev);
4562edb715dfSLaurent Pinchart 	char name[10];
4563edb715dfSLaurent Pinchart 	u32 rev;
4564edb715dfSLaurent Pinchart 	int r;
4565edb715dfSLaurent Pinchart 
4566edb715dfSLaurent Pinchart 	dsi->dss = dss;
4567edb715dfSLaurent Pinchart 
4568edb715dfSLaurent Pinchart 	dsi_init_pll_data(dss, dsi);
4569edb715dfSLaurent Pinchart 
4570edb715dfSLaurent Pinchart 	r = dsi_runtime_get(dsi);
4571edb715dfSLaurent Pinchart 	if (r)
4572edb715dfSLaurent Pinchart 		return r;
4573edb715dfSLaurent Pinchart 
4574edb715dfSLaurent Pinchart 	rev = dsi_read_reg(dsi, DSI_REVISION);
4575edb715dfSLaurent Pinchart 	dev_dbg(dev, "OMAP DSI rev %d.%d\n",
4576edb715dfSLaurent Pinchart 	       FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
4577edb715dfSLaurent Pinchart 
4578edb715dfSLaurent Pinchart 	dsi->line_buffer_size = dsi_get_line_buf_size(dsi);
4579edb715dfSLaurent Pinchart 
4580edb715dfSLaurent Pinchart 	dsi_runtime_put(dsi);
4581edb715dfSLaurent Pinchart 
4582edb715dfSLaurent Pinchart 	snprintf(name, sizeof(name), "dsi%u_regs", dsi->module_id + 1);
4583edb715dfSLaurent Pinchart 	dsi->debugfs.regs = dss_debugfs_create_file(dss, name,
45844df04ac9STomi Valkeinen 						    dsi_dump_dsi_regs, dsi);
4585edb715dfSLaurent Pinchart #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
4586edb715dfSLaurent Pinchart 	snprintf(name, sizeof(name), "dsi%u_irqs", dsi->module_id + 1);
4587edb715dfSLaurent Pinchart 	dsi->debugfs.irqs = dss_debugfs_create_file(dss, name,
45884df04ac9STomi Valkeinen 						    dsi_dump_dsi_irqs, dsi);
4589edb715dfSLaurent Pinchart #endif
4590edb715dfSLaurent Pinchart 	snprintf(name, sizeof(name), "dsi%u_clks", dsi->module_id + 1);
4591edb715dfSLaurent Pinchart 	dsi->debugfs.clks = dss_debugfs_create_file(dss, name,
45924df04ac9STomi Valkeinen 						    dsi_dump_dsi_clocks, dsi);
4593edb715dfSLaurent Pinchart 
4594edb715dfSLaurent Pinchart 	return 0;
4595edb715dfSLaurent Pinchart }
4596edb715dfSLaurent Pinchart 
dsi_unbind(struct device * dev,struct device * master,void * data)4597edb715dfSLaurent Pinchart static void dsi_unbind(struct device *dev, struct device *master, void *data)
4598edb715dfSLaurent Pinchart {
4599edb715dfSLaurent Pinchart 	struct dsi_data *dsi = dev_get_drvdata(dev);
4600edb715dfSLaurent Pinchart 
4601edb715dfSLaurent Pinchart 	dss_debugfs_remove_file(dsi->debugfs.clks);
4602edb715dfSLaurent Pinchart 	dss_debugfs_remove_file(dsi->debugfs.irqs);
4603edb715dfSLaurent Pinchart 	dss_debugfs_remove_file(dsi->debugfs.regs);
4604edb715dfSLaurent Pinchart 
4605edb715dfSLaurent Pinchart 	WARN_ON(dsi->scp_clk_refcount > 0);
4606edb715dfSLaurent Pinchart 
4607edb715dfSLaurent Pinchart 	dss_pll_unregister(&dsi->pll);
4608edb715dfSLaurent Pinchart }
4609edb715dfSLaurent Pinchart 
4610edb715dfSLaurent Pinchart static const struct component_ops dsi_component_ops = {
4611edb715dfSLaurent Pinchart 	.bind	= dsi_bind,
4612edb715dfSLaurent Pinchart 	.unbind	= dsi_unbind,
4613edb715dfSLaurent Pinchart };
4614edb715dfSLaurent Pinchart 
4615edb715dfSLaurent Pinchart /* -----------------------------------------------------------------------------
4616af1110cbSSebastian Reichel  * DRM Bridge Operations
4617af1110cbSSebastian Reichel  */
4618af1110cbSSebastian Reichel 
dsi_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)4619af1110cbSSebastian Reichel static int dsi_bridge_attach(struct drm_bridge *bridge,
4620af1110cbSSebastian Reichel 			     enum drm_bridge_attach_flags flags)
4621af1110cbSSebastian Reichel {
4622af1110cbSSebastian Reichel 	struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
4623af1110cbSSebastian Reichel 
4624af1110cbSSebastian Reichel 	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
4625af1110cbSSebastian Reichel 		return -EINVAL;
4626af1110cbSSebastian Reichel 
4627af1110cbSSebastian Reichel 	return drm_bridge_attach(bridge->encoder, dsi->output.next_bridge,
4628af1110cbSSebastian Reichel 				 bridge, flags);
4629af1110cbSSebastian Reichel }
4630af1110cbSSebastian Reichel 
4631af1110cbSSebastian Reichel static enum drm_mode_status
dsi_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)4632af1110cbSSebastian Reichel dsi_bridge_mode_valid(struct drm_bridge *bridge,
4633af1110cbSSebastian Reichel 		      const struct drm_display_info *info,
4634af1110cbSSebastian Reichel 		      const struct drm_display_mode *mode)
4635af1110cbSSebastian Reichel {
4636af1110cbSSebastian Reichel 	struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
4637af1110cbSSebastian Reichel 	struct dsi_clk_calc_ctx ctx;
4638af1110cbSSebastian Reichel 	int r;
4639af1110cbSSebastian Reichel 
4640af1110cbSSebastian Reichel 	mutex_lock(&dsi->lock);
4641af1110cbSSebastian Reichel 	r = __dsi_calc_config(dsi, mode, &ctx);
4642af1110cbSSebastian Reichel 	mutex_unlock(&dsi->lock);
4643af1110cbSSebastian Reichel 
4644af1110cbSSebastian Reichel 	return r ? MODE_CLOCK_RANGE : MODE_OK;
4645af1110cbSSebastian Reichel }
4646af1110cbSSebastian Reichel 
dsi_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)4647af1110cbSSebastian Reichel static void dsi_bridge_mode_set(struct drm_bridge *bridge,
4648af1110cbSSebastian Reichel 				const struct drm_display_mode *mode,
4649af1110cbSSebastian Reichel 				const struct drm_display_mode *adjusted_mode)
4650af1110cbSSebastian Reichel {
4651af1110cbSSebastian Reichel 	struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
4652af1110cbSSebastian Reichel 
4653af1110cbSSebastian Reichel 	dsi_set_config(&dsi->output, adjusted_mode);
4654af1110cbSSebastian Reichel }
4655af1110cbSSebastian Reichel 
dsi_bridge_enable(struct drm_bridge * bridge)4656af1110cbSSebastian Reichel static void dsi_bridge_enable(struct drm_bridge *bridge)
4657af1110cbSSebastian Reichel {
4658af1110cbSSebastian Reichel 	struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
46595d676b6fSTomi Valkeinen 	struct omap_dss_device *dssdev = &dsi->output;
4660af1110cbSSebastian Reichel 
4661dfd2edccSTomi Valkeinen 	cancel_delayed_work_sync(&dsi->dsi_disable_work);
4662dfd2edccSTomi Valkeinen 
46635d676b6fSTomi Valkeinen 	dsi_bus_lock(dsi);
46645d676b6fSTomi Valkeinen 
4665dfd2edccSTomi Valkeinen 	if (!dsi->iface_enabled)
4666a4a29d1dSTomi Valkeinen 		dsi_enable(dsi);
46675d676b6fSTomi Valkeinen 
46685d676b6fSTomi Valkeinen 	dsi_enable_video_output(dssdev, VC_VIDEO);
46695d676b6fSTomi Valkeinen 
46705d676b6fSTomi Valkeinen 	dsi->video_enabled = true;
46715d676b6fSTomi Valkeinen 
46725d676b6fSTomi Valkeinen 	dsi_bus_unlock(dsi);
4673af1110cbSSebastian Reichel }
4674af1110cbSSebastian Reichel 
dsi_bridge_disable(struct drm_bridge * bridge)4675af1110cbSSebastian Reichel static void dsi_bridge_disable(struct drm_bridge *bridge)
4676af1110cbSSebastian Reichel {
4677af1110cbSSebastian Reichel 	struct dsi_data *dsi = drm_bridge_to_dsi(bridge);
46785d676b6fSTomi Valkeinen 	struct omap_dss_device *dssdev = &dsi->output;
4679af1110cbSSebastian Reichel 
4680dfd2edccSTomi Valkeinen 	cancel_delayed_work_sync(&dsi->dsi_disable_work);
4681dfd2edccSTomi Valkeinen 
46825d676b6fSTomi Valkeinen 	dsi_bus_lock(dsi);
46835d676b6fSTomi Valkeinen 
46845d676b6fSTomi Valkeinen 	dsi->video_enabled = false;
46855d676b6fSTomi Valkeinen 
46865d676b6fSTomi Valkeinen 	dsi_disable_video_output(dssdev, VC_VIDEO);
46875d676b6fSTomi Valkeinen 
4688c8320789STomi Valkeinen 	dsi_disable(dsi);
46895d676b6fSTomi Valkeinen 
46905d676b6fSTomi Valkeinen 	dsi_bus_unlock(dsi);
4691af1110cbSSebastian Reichel }
4692af1110cbSSebastian Reichel 
4693af1110cbSSebastian Reichel static const struct drm_bridge_funcs dsi_bridge_funcs = {
4694af1110cbSSebastian Reichel 	.attach = dsi_bridge_attach,
4695af1110cbSSebastian Reichel 	.mode_valid = dsi_bridge_mode_valid,
4696af1110cbSSebastian Reichel 	.mode_set = dsi_bridge_mode_set,
4697af1110cbSSebastian Reichel 	.enable = dsi_bridge_enable,
4698af1110cbSSebastian Reichel 	.disable = dsi_bridge_disable,
4699af1110cbSSebastian Reichel };
4700af1110cbSSebastian Reichel 
dsi_bridge_init(struct dsi_data * dsi)4701af1110cbSSebastian Reichel static void dsi_bridge_init(struct dsi_data *dsi)
4702af1110cbSSebastian Reichel {
4703af1110cbSSebastian Reichel 	dsi->bridge.funcs = &dsi_bridge_funcs;
4704af1110cbSSebastian Reichel 	dsi->bridge.of_node = dsi->host.dev->of_node;
4705af1110cbSSebastian Reichel 	dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
4706af1110cbSSebastian Reichel 
4707af1110cbSSebastian Reichel 	drm_bridge_add(&dsi->bridge);
4708af1110cbSSebastian Reichel }
4709af1110cbSSebastian Reichel 
dsi_bridge_cleanup(struct dsi_data * dsi)4710af1110cbSSebastian Reichel static void dsi_bridge_cleanup(struct dsi_data *dsi)
4711af1110cbSSebastian Reichel {
4712af1110cbSSebastian Reichel 	drm_bridge_remove(&dsi->bridge);
4713af1110cbSSebastian Reichel }
4714af1110cbSSebastian Reichel 
4715af1110cbSSebastian Reichel /* -----------------------------------------------------------------------------
4716edb715dfSLaurent Pinchart  * Probe & Remove, Suspend & Resume
4717edb715dfSLaurent Pinchart  */
4718edb715dfSLaurent Pinchart 
dsi_init_output(struct dsi_data * dsi)471927d62452SLaurent Pinchart static int dsi_init_output(struct dsi_data *dsi)
4720edb715dfSLaurent Pinchart {
4721edb715dfSLaurent Pinchart 	struct omap_dss_device *out = &dsi->output;
472271316556SLaurent Pinchart 	int r;
4723edb715dfSLaurent Pinchart 
4724af1110cbSSebastian Reichel 	dsi_bridge_init(dsi);
4725af1110cbSSebastian Reichel 
4726edb715dfSLaurent Pinchart 	out->dev = dsi->dev;
4727edb715dfSLaurent Pinchart 	out->id = dsi->module_id == 0 ?
4728edb715dfSLaurent Pinchart 			OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
4729edb715dfSLaurent Pinchart 
47300dbfc396SLaurent Pinchart 	out->type = OMAP_DISPLAY_TYPE_DSI;
4731edb715dfSLaurent Pinchart 	out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
4732f408600fSTomi Valkeinen 	out->dispc_channel = dsi_get_dispc_channel(dsi);
473394d73329SSebastian Reichel 	out->dsi_ops = &dsi_ops;
4734c83fefd7SLaurent Pinchart 	out->of_port = 0;
473588bc4178SLaurent Pinchart 	out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE
4736b4935e3aSLaurent Pinchart 		       | DRM_BUS_FLAG_DE_HIGH
473788bc4178SLaurent Pinchart 		       | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
4738edb715dfSLaurent Pinchart 
4739af1110cbSSebastian Reichel 	r = omapdss_device_init_output(out, &dsi->bridge);
4740af1110cbSSebastian Reichel 	if (r < 0) {
4741af1110cbSSebastian Reichel 		dsi_bridge_cleanup(dsi);
474271316556SLaurent Pinchart 		return r;
4743af1110cbSSebastian Reichel 	}
474471316556SLaurent Pinchart 
4745edb715dfSLaurent Pinchart 	omapdss_device_register(out);
474627d62452SLaurent Pinchart 
474727d62452SLaurent Pinchart 	return 0;
4748edb715dfSLaurent Pinchart }
4749edb715dfSLaurent Pinchart 
dsi_uninit_output(struct dsi_data * dsi)4750edb715dfSLaurent Pinchart static void dsi_uninit_output(struct dsi_data *dsi)
4751edb715dfSLaurent Pinchart {
4752edb715dfSLaurent Pinchart 	struct omap_dss_device *out = &dsi->output;
4753edb715dfSLaurent Pinchart 
4754edb715dfSLaurent Pinchart 	omapdss_device_unregister(out);
4755d17eb453SLaurent Pinchart 	omapdss_device_cleanup_output(out);
4756af1110cbSSebastian Reichel 	dsi_bridge_cleanup(dsi);
4757edb715dfSLaurent Pinchart }
4758edb715dfSLaurent Pinchart 
dsi_probe_of(struct dsi_data * dsi)4759edb715dfSLaurent Pinchart static int dsi_probe_of(struct dsi_data *dsi)
4760edb715dfSLaurent Pinchart {
4761edb715dfSLaurent Pinchart 	struct device_node *node = dsi->dev->of_node;
4762edb715dfSLaurent Pinchart 	struct property *prop;
4763edb715dfSLaurent Pinchart 	u32 lane_arr[10];
4764edb715dfSLaurent Pinchart 	int len, num_pins;
4765578739e8SSebastian Reichel 	int r;
4766edb715dfSLaurent Pinchart 	struct device_node *ep;
4767edb715dfSLaurent Pinchart 
4768edb715dfSLaurent Pinchart 	ep = of_graph_get_endpoint_by_regs(node, 0, 0);
4769edb715dfSLaurent Pinchart 	if (!ep)
4770edb715dfSLaurent Pinchart 		return 0;
4771edb715dfSLaurent Pinchart 
4772edb715dfSLaurent Pinchart 	prop = of_find_property(ep, "lanes", &len);
4773edb715dfSLaurent Pinchart 	if (prop == NULL) {
4774edb715dfSLaurent Pinchart 		dev_err(dsi->dev, "failed to find lane data\n");
4775edb715dfSLaurent Pinchart 		r = -EINVAL;
4776edb715dfSLaurent Pinchart 		goto err;
4777edb715dfSLaurent Pinchart 	}
4778edb715dfSLaurent Pinchart 
4779edb715dfSLaurent Pinchart 	num_pins = len / sizeof(u32);
4780edb715dfSLaurent Pinchart 
4781edb715dfSLaurent Pinchart 	if (num_pins < 4 || num_pins % 2 != 0 ||
4782edb715dfSLaurent Pinchart 		num_pins > dsi->num_lanes_supported * 2) {
4783edb715dfSLaurent Pinchart 		dev_err(dsi->dev, "bad number of lanes\n");
4784edb715dfSLaurent Pinchart 		r = -EINVAL;
4785edb715dfSLaurent Pinchart 		goto err;
4786edb715dfSLaurent Pinchart 	}
4787edb715dfSLaurent Pinchart 
4788edb715dfSLaurent Pinchart 	r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins);
4789edb715dfSLaurent Pinchart 	if (r) {
4790edb715dfSLaurent Pinchart 		dev_err(dsi->dev, "failed to read lane data\n");
4791edb715dfSLaurent Pinchart 		goto err;
4792edb715dfSLaurent Pinchart 	}
4793edb715dfSLaurent Pinchart 
4794578739e8SSebastian Reichel 	r = dsi_configure_pins(dsi, num_pins, lane_arr);
4795edb715dfSLaurent Pinchart 	if (r) {
4796edb715dfSLaurent Pinchart 		dev_err(dsi->dev, "failed to configure pins");
4797edb715dfSLaurent Pinchart 		goto err;
4798edb715dfSLaurent Pinchart 	}
4799edb715dfSLaurent Pinchart 
4800edb715dfSLaurent Pinchart 	of_node_put(ep);
4801edb715dfSLaurent Pinchart 
4802edb715dfSLaurent Pinchart 	return 0;
4803edb715dfSLaurent Pinchart 
4804edb715dfSLaurent Pinchart err:
4805edb715dfSLaurent Pinchart 	of_node_put(ep);
4806edb715dfSLaurent Pinchart 	return r;
4807edb715dfSLaurent Pinchart }
4808edb715dfSLaurent Pinchart 
480944d8ca10SLaurent Pinchart static const struct dsi_of_data dsi_of_data_omap34xx = {
481044d8ca10SLaurent Pinchart 	.model = DSI_MODEL_OMAP3,
481144d8ca10SLaurent Pinchart 	.pll_hw = &dss_omap3_dsi_pll_hw,
481244d8ca10SLaurent Pinchart 	.modules = (const struct dsi_module_id_data[]) {
481344d8ca10SLaurent Pinchart 		{ .address = 0x4804fc00, .id = 0, },
481444d8ca10SLaurent Pinchart 		{ },
481544d8ca10SLaurent Pinchart 	},
4816fe9964cbSLaurent Pinchart 	.max_fck_freq = 173000000,
4817fe9964cbSLaurent Pinchart 	.max_pll_lpdiv = (1 << 13) - 1,
481844d8ca10SLaurent Pinchart 	.quirks = DSI_QUIRK_REVERSE_TXCLKESC,
481944d8ca10SLaurent Pinchart };
482044d8ca10SLaurent Pinchart 
482144d8ca10SLaurent Pinchart static const struct dsi_of_data dsi_of_data_omap36xx = {
482244d8ca10SLaurent Pinchart 	.model = DSI_MODEL_OMAP3,
482344d8ca10SLaurent Pinchart 	.pll_hw = &dss_omap3_dsi_pll_hw,
482444d8ca10SLaurent Pinchart 	.modules = (const struct dsi_module_id_data[]) {
482544d8ca10SLaurent Pinchart 		{ .address = 0x4804fc00, .id = 0, },
482644d8ca10SLaurent Pinchart 		{ },
482744d8ca10SLaurent Pinchart 	},
4828fe9964cbSLaurent Pinchart 	.max_fck_freq = 173000000,
4829fe9964cbSLaurent Pinchart 	.max_pll_lpdiv = (1 << 13) - 1,
483044d8ca10SLaurent Pinchart 	.quirks = DSI_QUIRK_PLL_PWR_BUG,
483144d8ca10SLaurent Pinchart };
483244d8ca10SLaurent Pinchart 
483344d8ca10SLaurent Pinchart static const struct dsi_of_data dsi_of_data_omap4 = {
483444d8ca10SLaurent Pinchart 	.model = DSI_MODEL_OMAP4,
483544d8ca10SLaurent Pinchart 	.pll_hw = &dss_omap4_dsi_pll_hw,
483644d8ca10SLaurent Pinchart 	.modules = (const struct dsi_module_id_data[]) {
483744d8ca10SLaurent Pinchart 		{ .address = 0x58004000, .id = 0, },
483844d8ca10SLaurent Pinchart 		{ .address = 0x58005000, .id = 1, },
483944d8ca10SLaurent Pinchart 		{ },
484044d8ca10SLaurent Pinchart 	},
4841fe9964cbSLaurent Pinchart 	.max_fck_freq = 170000000,
4842fe9964cbSLaurent Pinchart 	.max_pll_lpdiv = (1 << 13) - 1,
484344d8ca10SLaurent Pinchart 	.quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH
484444d8ca10SLaurent Pinchart 		| DSI_QUIRK_GNQ,
484544d8ca10SLaurent Pinchart };
484644d8ca10SLaurent Pinchart 
484744d8ca10SLaurent Pinchart static const struct dsi_of_data dsi_of_data_omap5 = {
484844d8ca10SLaurent Pinchart 	.model = DSI_MODEL_OMAP5,
484944d8ca10SLaurent Pinchart 	.pll_hw = &dss_omap5_dsi_pll_hw,
485044d8ca10SLaurent Pinchart 	.modules = (const struct dsi_module_id_data[]) {
485144d8ca10SLaurent Pinchart 		{ .address = 0x58004000, .id = 0, },
485244d8ca10SLaurent Pinchart 		{ .address = 0x58009000, .id = 1, },
485344d8ca10SLaurent Pinchart 		{ },
485444d8ca10SLaurent Pinchart 	},
4855fe9964cbSLaurent Pinchart 	.max_fck_freq = 209250000,
4856fe9964cbSLaurent Pinchart 	.max_pll_lpdiv = (1 << 13) - 1,
485744d8ca10SLaurent Pinchart 	.quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH
485844d8ca10SLaurent Pinchart 		| DSI_QUIRK_GNQ | DSI_QUIRK_PHY_DCC,
485944d8ca10SLaurent Pinchart };
486044d8ca10SLaurent Pinchart 
486144d8ca10SLaurent Pinchart static const struct of_device_id dsi_of_match[] = {
486244d8ca10SLaurent Pinchart 	{ .compatible = "ti,omap3-dsi", .data = &dsi_of_data_omap36xx, },
486344d8ca10SLaurent Pinchart 	{ .compatible = "ti,omap4-dsi", .data = &dsi_of_data_omap4, },
486444d8ca10SLaurent Pinchart 	{ .compatible = "ti,omap5-dsi", .data = &dsi_of_data_omap5, },
486544d8ca10SLaurent Pinchart 	{},
486644d8ca10SLaurent Pinchart };
486744d8ca10SLaurent Pinchart 
486844d8ca10SLaurent Pinchart static const struct soc_device_attribute dsi_soc_devices[] = {
486944d8ca10SLaurent Pinchart 	{ .machine = "OMAP3[45]*",	.data = &dsi_of_data_omap34xx },
487044d8ca10SLaurent Pinchart 	{ .machine = "AM35*",		.data = &dsi_of_data_omap34xx },
487144d8ca10SLaurent Pinchart 	{ /* sentinel */ }
487244d8ca10SLaurent Pinchart };
4873c7963f5fSLaurent Pinchart 
omap_dsi_disable_work_callback(struct work_struct * work)4874dfd2edccSTomi Valkeinen static void omap_dsi_disable_work_callback(struct work_struct *work)
4875dfd2edccSTomi Valkeinen {
4876dfd2edccSTomi Valkeinen 	struct dsi_data *dsi = container_of(work, struct dsi_data, dsi_disable_work.work);
4877dfd2edccSTomi Valkeinen 
4878dfd2edccSTomi Valkeinen 	dsi_bus_lock(dsi);
4879dfd2edccSTomi Valkeinen 
4880dfd2edccSTomi Valkeinen 	if (dsi->iface_enabled && !dsi->video_enabled)
4881dfd2edccSTomi Valkeinen 		dsi_disable(dsi);
4882dfd2edccSTomi Valkeinen 
4883dfd2edccSTomi Valkeinen 	dsi_bus_unlock(dsi);
4884dfd2edccSTomi Valkeinen }
4885dfd2edccSTomi Valkeinen 
dsi_probe(struct platform_device * pdev)4886edb715dfSLaurent Pinchart static int dsi_probe(struct platform_device *pdev)
48879960aa7cSTomi Valkeinen {
488844d8ca10SLaurent Pinchart 	const struct soc_device_attribute *soc;
48891dff212cSLaurent Pinchart 	const struct dsi_module_id_data *d;
4890edb715dfSLaurent Pinchart 	struct device *dev = &pdev->dev;
48919960aa7cSTomi Valkeinen 	struct dsi_data *dsi;
48929960aa7cSTomi Valkeinen 	struct resource *dsi_mem;
4893edb715dfSLaurent Pinchart 	unsigned int i;
4894edb715dfSLaurent Pinchart 	int r;
48959960aa7cSTomi Valkeinen 
4896c7963f5fSLaurent Pinchart 	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
48979960aa7cSTomi Valkeinen 	if (!dsi)
48989960aa7cSTomi Valkeinen 		return -ENOMEM;
48999960aa7cSTomi Valkeinen 
49004600ea9cSLaurent Pinchart 	dsi->dev = dev;
4901c7963f5fSLaurent Pinchart 	dev_set_drvdata(dev, dsi);
49029960aa7cSTomi Valkeinen 
49039960aa7cSTomi Valkeinen 	spin_lock_init(&dsi->irq_lock);
49049960aa7cSTomi Valkeinen 	spin_lock_init(&dsi->errors_lock);
49059960aa7cSTomi Valkeinen 	dsi->errors = 0;
49069960aa7cSTomi Valkeinen 
49079960aa7cSTomi Valkeinen #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
49089960aa7cSTomi Valkeinen 	spin_lock_init(&dsi->irq_stats_lock);
49099960aa7cSTomi Valkeinen 	dsi->irq_stats.last_reset = jiffies;
49109960aa7cSTomi Valkeinen #endif
49119960aa7cSTomi Valkeinen 
49129960aa7cSTomi Valkeinen 	mutex_init(&dsi->lock);
49139960aa7cSTomi Valkeinen 	sema_init(&dsi->bus_lock, 1);
49149960aa7cSTomi Valkeinen 
49159960aa7cSTomi Valkeinen 	INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work,
49169960aa7cSTomi Valkeinen 			     dsi_framedone_timeout_work_callback);
49179960aa7cSTomi Valkeinen 
4918dfd2edccSTomi Valkeinen 	INIT_DEFERRABLE_WORK(&dsi->dsi_disable_work, omap_dsi_disable_work_callback);
4919dfd2edccSTomi Valkeinen 
49209960aa7cSTomi Valkeinen #ifdef DSI_CATCH_MISSING_TE
4921e99e88a9SKees Cook 	timer_setup(&dsi->te_timer, dsi_te_timeout, 0);
49229960aa7cSTomi Valkeinen #endif
49239960aa7cSTomi Valkeinen 
4924c7963f5fSLaurent Pinchart 	dsi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "proto");
4925c7963f5fSLaurent Pinchart 	dsi->proto_base = devm_ioremap_resource(dev, dsi_mem);
4926b22622f0SLaurent Pinchart 	if (IS_ERR(dsi->proto_base))
4927b22622f0SLaurent Pinchart 		return PTR_ERR(dsi->proto_base);
49289960aa7cSTomi Valkeinen 
4929ed8414abSCai Huoqing 	dsi->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy");
4930b22622f0SLaurent Pinchart 	if (IS_ERR(dsi->phy_base))
4931b22622f0SLaurent Pinchart 		return PTR_ERR(dsi->phy_base);
49329960aa7cSTomi Valkeinen 
4933ed8414abSCai Huoqing 	dsi->pll_base = devm_platform_ioremap_resource_byname(pdev, "pll");
4934b22622f0SLaurent Pinchart 	if (IS_ERR(dsi->pll_base))
4935b22622f0SLaurent Pinchart 		return PTR_ERR(dsi->pll_base);
49369960aa7cSTomi Valkeinen 
4937c7963f5fSLaurent Pinchart 	dsi->irq = platform_get_irq(pdev, 0);
49389960aa7cSTomi Valkeinen 	if (dsi->irq < 0) {
49399960aa7cSTomi Valkeinen 		DSSERR("platform_get_irq failed\n");
49409960aa7cSTomi Valkeinen 		return -ENODEV;
49419960aa7cSTomi Valkeinen 	}
49429960aa7cSTomi Valkeinen 
4943c7963f5fSLaurent Pinchart 	r = devm_request_irq(dev, dsi->irq, omap_dsi_irq_handler,
4944c7963f5fSLaurent Pinchart 			     IRQF_SHARED, dev_name(dev), dsi);
49459960aa7cSTomi Valkeinen 	if (r < 0) {
49469960aa7cSTomi Valkeinen 		DSSERR("request_irq failed\n");
49479960aa7cSTomi Valkeinen 		return r;
49489960aa7cSTomi Valkeinen 	}
49499960aa7cSTomi Valkeinen 
49508a36357aSLaurent Pinchart 	dsi->vdds_dsi_reg = devm_regulator_get(dev, "vdd");
49518a36357aSLaurent Pinchart 	if (IS_ERR(dsi->vdds_dsi_reg)) {
49528a36357aSLaurent Pinchart 		if (PTR_ERR(dsi->vdds_dsi_reg) != -EPROBE_DEFER)
49538a36357aSLaurent Pinchart 			DSSERR("can't get DSI VDD regulator\n");
49548a36357aSLaurent Pinchart 		return PTR_ERR(dsi->vdds_dsi_reg);
49558a36357aSLaurent Pinchart 	}
49568a36357aSLaurent Pinchart 
495744d8ca10SLaurent Pinchart 	soc = soc_device_match(dsi_soc_devices);
495844d8ca10SLaurent Pinchart 	if (soc)
495944d8ca10SLaurent Pinchart 		dsi->data = soc->data;
496044d8ca10SLaurent Pinchart 	else
496144d8ca10SLaurent Pinchart 		dsi->data = of_match_node(dsi_of_match, dev->of_node)->data;
496244d8ca10SLaurent Pinchart 
4963742e693bSLaurent Pinchart 	d = dsi->data->modules;
49649960aa7cSTomi Valkeinen 	while (d->address != 0 && d->address != dsi_mem->start)
49659960aa7cSTomi Valkeinen 		d++;
49669960aa7cSTomi Valkeinen 
49679960aa7cSTomi Valkeinen 	if (d->address == 0) {
49689960aa7cSTomi Valkeinen 		DSSERR("unsupported DSI module\n");
49699960aa7cSTomi Valkeinen 		return -ENODEV;
49709960aa7cSTomi Valkeinen 	}
49719960aa7cSTomi Valkeinen 
49729960aa7cSTomi Valkeinen 	dsi->module_id = d->id;
49739960aa7cSTomi Valkeinen 
4974eeb45f85STomi Valkeinen 	if (dsi->data->model == DSI_MODEL_OMAP4 ||
4975eeb45f85STomi Valkeinen 	    dsi->data->model == DSI_MODEL_OMAP5) {
49769e1305d0SLaurent Pinchart 		struct device_node *np;
49779e1305d0SLaurent Pinchart 
49789e1305d0SLaurent Pinchart 		/*
4979eeb45f85STomi Valkeinen 		 * The OMAP4/5 display DT bindings don't reference the padconf
49809e1305d0SLaurent Pinchart 		 * syscon. Our only option to retrieve it is to find it by name.
49819e1305d0SLaurent Pinchart 		 */
4982eeb45f85STomi Valkeinen 		np = of_find_node_by_name(NULL,
4983eeb45f85STomi Valkeinen 			dsi->data->model == DSI_MODEL_OMAP4 ?
4984eeb45f85STomi Valkeinen 			"omap4_padconf_global" : "omap5_padconf_global");
49859e1305d0SLaurent Pinchart 		if (!np)
49869e1305d0SLaurent Pinchart 			return -ENODEV;
49879e1305d0SLaurent Pinchart 
49889e1305d0SLaurent Pinchart 		dsi->syscon = syscon_node_to_regmap(np);
49899e1305d0SLaurent Pinchart 		of_node_put(np);
49909e1305d0SLaurent Pinchart 	}
49919e1305d0SLaurent Pinchart 
49929960aa7cSTomi Valkeinen 	/* DSI VCs initialization */
4993d843314eSTomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(dsi->vc); i++)
49949960aa7cSTomi Valkeinen 		dsi->vc[i].source = DSI_VC_SOURCE_L4;
49959960aa7cSTomi Valkeinen 
49967093d6cdSLaurent Pinchart 	r = dsi_get_clocks(dsi);
49979960aa7cSTomi Valkeinen 	if (r)
49989960aa7cSTomi Valkeinen 		return r;
49999960aa7cSTomi Valkeinen 
5000c7963f5fSLaurent Pinchart 	pm_runtime_enable(dev);
50019960aa7cSTomi Valkeinen 
50029960aa7cSTomi Valkeinen 	/* DSI on OMAP3 doesn't have register DSI_GNQ, set number
50039960aa7cSTomi Valkeinen 	 * of data to 3 by default */
5004350c03e8SLaurent Pinchart 	if (dsi->data->quirks & DSI_QUIRK_GNQ) {
5005350c03e8SLaurent Pinchart 		dsi_runtime_get(dsi);
50069960aa7cSTomi Valkeinen 		/* NB_DATA_LANES */
50077093d6cdSLaurent Pinchart 		dsi->num_lanes_supported = 1 + REG_GET(dsi, DSI_GNQ, 11, 9);
5008350c03e8SLaurent Pinchart 		dsi_runtime_put(dsi);
5009350c03e8SLaurent Pinchart 	} else {
50109960aa7cSTomi Valkeinen 		dsi->num_lanes_supported = 3;
5011350c03e8SLaurent Pinchart 	}
50129960aa7cSTomi Valkeinen 
50139cd87829SSebastian Reichel 	dsi->host.ops = &omap_dsi_host_ops;
50149cd87829SSebastian Reichel 	dsi->host.dev = &pdev->dev;
50159cd87829SSebastian Reichel 
5016578739e8SSebastian Reichel 	r = dsi_probe_of(dsi);
5017578739e8SSebastian Reichel 	if (r) {
5018578739e8SSebastian Reichel 		DSSERR("Invalid DSI DT data\n");
5019578739e8SSebastian Reichel 		goto err_pm_disable;
5020578739e8SSebastian Reichel 	}
5021578739e8SSebastian Reichel 
50229cd87829SSebastian Reichel 	r = mipi_dsi_host_register(&dsi->host);
50239cd87829SSebastian Reichel 	if (r < 0) {
50249cd87829SSebastian Reichel 		dev_err(&pdev->dev, "failed to register DSI host: %d\n", r);
50250a02d495SSebastian Reichel 		goto err_pm_disable;
50260a02d495SSebastian Reichel 	}
50270a02d495SSebastian Reichel 
502827d62452SLaurent Pinchart 	r = dsi_init_output(dsi);
502927d62452SLaurent Pinchart 	if (r)
50309cd87829SSebastian Reichel 		goto err_dsi_host_unregister;
50319960aa7cSTomi Valkeinen 
5032edb715dfSLaurent Pinchart 	r = component_add(&pdev->dev, &dsi_component_ops);
5033edb715dfSLaurent Pinchart 	if (r)
50340a02d495SSebastian Reichel 		goto err_uninit_output;
50359960aa7cSTomi Valkeinen 
50369960aa7cSTomi Valkeinen 	return 0;
50379960aa7cSTomi Valkeinen 
503866aacfe2SLaurent Pinchart err_uninit_output:
50397093d6cdSLaurent Pinchart 	dsi_uninit_output(dsi);
50409cd87829SSebastian Reichel err_dsi_host_unregister:
50419cd87829SSebastian Reichel 	mipi_dsi_host_unregister(&dsi->host);
504227d62452SLaurent Pinchart err_pm_disable:
5043c7963f5fSLaurent Pinchart 	pm_runtime_disable(dev);
50449960aa7cSTomi Valkeinen 	return r;
50459960aa7cSTomi Valkeinen }
50469960aa7cSTomi Valkeinen 
dsi_remove(struct platform_device * pdev)5047*c2807ecbSUwe Kleine-König static void dsi_remove(struct platform_device *pdev)
50489960aa7cSTomi Valkeinen {
5049edb715dfSLaurent Pinchart 	struct dsi_data *dsi = platform_get_drvdata(pdev);
50509960aa7cSTomi Valkeinen 
5051edb715dfSLaurent Pinchart 	component_del(&pdev->dev, &dsi_component_ops);
50529960aa7cSTomi Valkeinen 
50537093d6cdSLaurent Pinchart 	dsi_uninit_output(dsi);
50549960aa7cSTomi Valkeinen 
50559cd87829SSebastian Reichel 	mipi_dsi_host_unregister(&dsi->host);
50560940c527STomi Valkeinen 
5057edb715dfSLaurent Pinchart 	pm_runtime_disable(&pdev->dev);
50589960aa7cSTomi Valkeinen 
50599960aa7cSTomi Valkeinen 	if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
50609960aa7cSTomi Valkeinen 		regulator_disable(dsi->vdds_dsi_reg);
50619960aa7cSTomi Valkeinen 		dsi->vdds_dsi_enabled = false;
50629960aa7cSTomi Valkeinen 	}
50639960aa7cSTomi Valkeinen }
50649960aa7cSTomi Valkeinen 
dsi_runtime_suspend(struct device * dev)5065d6c75c29SArnd Bergmann static __maybe_unused int dsi_runtime_suspend(struct device *dev)
50669960aa7cSTomi Valkeinen {
5067c068408eSLaurent Pinchart 	struct dsi_data *dsi = dev_get_drvdata(dev);
50689960aa7cSTomi Valkeinen 
50699960aa7cSTomi Valkeinen 	dsi->is_enabled = false;
50709960aa7cSTomi Valkeinen 	/* ensure the irq handler sees the is_enabled value */
50719960aa7cSTomi Valkeinen 	smp_wmb();
50729960aa7cSTomi Valkeinen 	/* wait for current handler to finish before turning the DSI off */
50739960aa7cSTomi Valkeinen 	synchronize_irq(dsi->irq);
50749960aa7cSTomi Valkeinen 
50759960aa7cSTomi Valkeinen 	return 0;
50769960aa7cSTomi Valkeinen }
50779960aa7cSTomi Valkeinen 
dsi_runtime_resume(struct device * dev)5078d6c75c29SArnd Bergmann static __maybe_unused int dsi_runtime_resume(struct device *dev)
50799960aa7cSTomi Valkeinen {
5080c068408eSLaurent Pinchart 	struct dsi_data *dsi = dev_get_drvdata(dev);
50819960aa7cSTomi Valkeinen 
50829960aa7cSTomi Valkeinen 	dsi->is_enabled = true;
50839960aa7cSTomi Valkeinen 	/* ensure the irq handler sees the is_enabled value */
50849960aa7cSTomi Valkeinen 	smp_wmb();
50859960aa7cSTomi Valkeinen 
50869960aa7cSTomi Valkeinen 	return 0;
50879960aa7cSTomi Valkeinen }
50889960aa7cSTomi Valkeinen 
50899960aa7cSTomi Valkeinen static const struct dev_pm_ops dsi_pm_ops = {
5090b92f7ea5SCai Huoqing 	SET_RUNTIME_PM_OPS(dsi_runtime_suspend, dsi_runtime_resume, NULL)
5091ecfdedd7STomi Valkeinen 	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
50929960aa7cSTomi Valkeinen };
50939960aa7cSTomi Valkeinen 
5094d66c36a3SAndrew F. Davis struct platform_driver omap_dsihw_driver = {
50959960aa7cSTomi Valkeinen 	.probe		= dsi_probe,
5096*c2807ecbSUwe Kleine-König 	.remove_new	= dsi_remove,
50979960aa7cSTomi Valkeinen 	.driver         = {
50989960aa7cSTomi Valkeinen 		.name   = "omapdss_dsi",
50999960aa7cSTomi Valkeinen 		.pm	= &dsi_pm_ops,
51009960aa7cSTomi Valkeinen 		.of_match_table = dsi_of_match,
51019960aa7cSTomi Valkeinen 		.suppress_bind_attrs = true,
51029960aa7cSTomi Valkeinen 	},
51039960aa7cSTomi Valkeinen };
5104