xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2012 Texas Instruments
4*4882a593Smuzhiyun  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #define DSS_SUBSYS_NAME "APPLY"
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/spinlock.h>
13*4882a593Smuzhiyun #include <linux/jiffies.h>
14*4882a593Smuzhiyun #include <linux/delay.h>
15*4882a593Smuzhiyun #include <linux/interrupt.h>
16*4882a593Smuzhiyun #include <linux/seq_file.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <video/omapfb_dss.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "dss.h"
21*4882a593Smuzhiyun #include "dss_features.h"
22*4882a593Smuzhiyun #include "dispc-compat.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define DISPC_IRQ_MASK_ERROR            (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
25*4882a593Smuzhiyun 					 DISPC_IRQ_OCP_ERR | \
26*4882a593Smuzhiyun 					 DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
27*4882a593Smuzhiyun 					 DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
28*4882a593Smuzhiyun 					 DISPC_IRQ_SYNC_LOST | \
29*4882a593Smuzhiyun 					 DISPC_IRQ_SYNC_LOST_DIGIT)
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define DISPC_MAX_NR_ISRS		8
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun struct omap_dispc_isr_data {
34*4882a593Smuzhiyun 	omap_dispc_isr_t	isr;
35*4882a593Smuzhiyun 	void			*arg;
36*4882a593Smuzhiyun 	u32			mask;
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun struct dispc_irq_stats {
40*4882a593Smuzhiyun 	unsigned long last_reset;
41*4882a593Smuzhiyun 	unsigned irq_count;
42*4882a593Smuzhiyun 	unsigned irqs[32];
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun static struct {
46*4882a593Smuzhiyun 	spinlock_t irq_lock;
47*4882a593Smuzhiyun 	u32 irq_error_mask;
48*4882a593Smuzhiyun 	struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
49*4882a593Smuzhiyun 	u32 error_irqs;
50*4882a593Smuzhiyun 	struct work_struct error_work;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
53*4882a593Smuzhiyun 	spinlock_t irq_stats_lock;
54*4882a593Smuzhiyun 	struct dispc_irq_stats irq_stats;
55*4882a593Smuzhiyun #endif
56*4882a593Smuzhiyun } dispc_compat;
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
dispc_dump_irqs(struct seq_file * s)60*4882a593Smuzhiyun static void dispc_dump_irqs(struct seq_file *s)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	unsigned long flags;
63*4882a593Smuzhiyun 	struct dispc_irq_stats stats;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	stats = dispc_compat.irq_stats;
68*4882a593Smuzhiyun 	memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
69*4882a593Smuzhiyun 	dispc_compat.irq_stats.last_reset = jiffies;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	seq_printf(s, "period %u ms\n",
74*4882a593Smuzhiyun 			jiffies_to_msecs(jiffies - stats.last_reset));
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	seq_printf(s, "irqs %d\n", stats.irq_count);
77*4882a593Smuzhiyun #define PIS(x) \
78*4882a593Smuzhiyun 	seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	PIS(FRAMEDONE);
81*4882a593Smuzhiyun 	PIS(VSYNC);
82*4882a593Smuzhiyun 	PIS(EVSYNC_EVEN);
83*4882a593Smuzhiyun 	PIS(EVSYNC_ODD);
84*4882a593Smuzhiyun 	PIS(ACBIAS_COUNT_STAT);
85*4882a593Smuzhiyun 	PIS(PROG_LINE_NUM);
86*4882a593Smuzhiyun 	PIS(GFX_FIFO_UNDERFLOW);
87*4882a593Smuzhiyun 	PIS(GFX_END_WIN);
88*4882a593Smuzhiyun 	PIS(PAL_GAMMA_MASK);
89*4882a593Smuzhiyun 	PIS(OCP_ERR);
90*4882a593Smuzhiyun 	PIS(VID1_FIFO_UNDERFLOW);
91*4882a593Smuzhiyun 	PIS(VID1_END_WIN);
92*4882a593Smuzhiyun 	PIS(VID2_FIFO_UNDERFLOW);
93*4882a593Smuzhiyun 	PIS(VID2_END_WIN);
94*4882a593Smuzhiyun 	if (dss_feat_get_num_ovls() > 3) {
95*4882a593Smuzhiyun 		PIS(VID3_FIFO_UNDERFLOW);
96*4882a593Smuzhiyun 		PIS(VID3_END_WIN);
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 	PIS(SYNC_LOST);
99*4882a593Smuzhiyun 	PIS(SYNC_LOST_DIGIT);
100*4882a593Smuzhiyun 	PIS(WAKEUP);
101*4882a593Smuzhiyun 	if (dss_has_feature(FEAT_MGR_LCD2)) {
102*4882a593Smuzhiyun 		PIS(FRAMEDONE2);
103*4882a593Smuzhiyun 		PIS(VSYNC2);
104*4882a593Smuzhiyun 		PIS(ACBIAS_COUNT_STAT2);
105*4882a593Smuzhiyun 		PIS(SYNC_LOST2);
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 	if (dss_has_feature(FEAT_MGR_LCD3)) {
108*4882a593Smuzhiyun 		PIS(FRAMEDONE3);
109*4882a593Smuzhiyun 		PIS(VSYNC3);
110*4882a593Smuzhiyun 		PIS(ACBIAS_COUNT_STAT3);
111*4882a593Smuzhiyun 		PIS(SYNC_LOST3);
112*4882a593Smuzhiyun 	}
113*4882a593Smuzhiyun #undef PIS
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun #endif
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun /* dispc.irq_lock has to be locked by the caller */
_omap_dispc_set_irqs(void)118*4882a593Smuzhiyun static void _omap_dispc_set_irqs(void)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	u32 mask;
121*4882a593Smuzhiyun 	int i;
122*4882a593Smuzhiyun 	struct omap_dispc_isr_data *isr_data;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	mask = dispc_compat.irq_error_mask;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
127*4882a593Smuzhiyun 		isr_data = &dispc_compat.registered_isr[i];
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 		if (isr_data->isr == NULL)
130*4882a593Smuzhiyun 			continue;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 		mask |= isr_data->mask;
133*4882a593Smuzhiyun 	}
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	dispc_write_irqenable(mask);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
omap_dispc_register_isr(omap_dispc_isr_t isr,void * arg,u32 mask)138*4882a593Smuzhiyun int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	int i;
141*4882a593Smuzhiyun 	int ret;
142*4882a593Smuzhiyun 	unsigned long flags;
143*4882a593Smuzhiyun 	struct omap_dispc_isr_data *isr_data;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	if (isr == NULL)
146*4882a593Smuzhiyun 		return -EINVAL;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	spin_lock_irqsave(&dispc_compat.irq_lock, flags);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/* check for duplicate entry */
151*4882a593Smuzhiyun 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
152*4882a593Smuzhiyun 		isr_data = &dispc_compat.registered_isr[i];
153*4882a593Smuzhiyun 		if (isr_data->isr == isr && isr_data->arg == arg &&
154*4882a593Smuzhiyun 				isr_data->mask == mask) {
155*4882a593Smuzhiyun 			ret = -EINVAL;
156*4882a593Smuzhiyun 			goto err;
157*4882a593Smuzhiyun 		}
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	isr_data = NULL;
161*4882a593Smuzhiyun 	ret = -EBUSY;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
164*4882a593Smuzhiyun 		isr_data = &dispc_compat.registered_isr[i];
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 		if (isr_data->isr != NULL)
167*4882a593Smuzhiyun 			continue;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 		isr_data->isr = isr;
170*4882a593Smuzhiyun 		isr_data->arg = arg;
171*4882a593Smuzhiyun 		isr_data->mask = mask;
172*4882a593Smuzhiyun 		ret = 0;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 		break;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	if (ret)
178*4882a593Smuzhiyun 		goto err;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	_omap_dispc_set_irqs();
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	return 0;
185*4882a593Smuzhiyun err:
186*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	return ret;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun EXPORT_SYMBOL(omap_dispc_register_isr);
191*4882a593Smuzhiyun 
omap_dispc_unregister_isr(omap_dispc_isr_t isr,void * arg,u32 mask)192*4882a593Smuzhiyun int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	int i;
195*4882a593Smuzhiyun 	unsigned long flags;
196*4882a593Smuzhiyun 	int ret = -EINVAL;
197*4882a593Smuzhiyun 	struct omap_dispc_isr_data *isr_data;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	spin_lock_irqsave(&dispc_compat.irq_lock, flags);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
202*4882a593Smuzhiyun 		isr_data = &dispc_compat.registered_isr[i];
203*4882a593Smuzhiyun 		if (isr_data->isr != isr || isr_data->arg != arg ||
204*4882a593Smuzhiyun 				isr_data->mask != mask)
205*4882a593Smuzhiyun 			continue;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 		/* found the correct isr */
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 		isr_data->isr = NULL;
210*4882a593Smuzhiyun 		isr_data->arg = NULL;
211*4882a593Smuzhiyun 		isr_data->mask = 0;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 		ret = 0;
214*4882a593Smuzhiyun 		break;
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	if (ret == 0)
218*4882a593Smuzhiyun 		_omap_dispc_set_irqs();
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	return ret;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun EXPORT_SYMBOL(omap_dispc_unregister_isr);
225*4882a593Smuzhiyun 
print_irq_status(u32 status)226*4882a593Smuzhiyun static void print_irq_status(u32 status)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun 	if ((status & dispc_compat.irq_error_mask) == 0)
229*4882a593Smuzhiyun 		return;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun #define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
234*4882a593Smuzhiyun 		status,
235*4882a593Smuzhiyun 		PIS(OCP_ERR),
236*4882a593Smuzhiyun 		PIS(GFX_FIFO_UNDERFLOW),
237*4882a593Smuzhiyun 		PIS(VID1_FIFO_UNDERFLOW),
238*4882a593Smuzhiyun 		PIS(VID2_FIFO_UNDERFLOW),
239*4882a593Smuzhiyun 		dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
240*4882a593Smuzhiyun 		PIS(SYNC_LOST),
241*4882a593Smuzhiyun 		PIS(SYNC_LOST_DIGIT),
242*4882a593Smuzhiyun 		dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
243*4882a593Smuzhiyun 		dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
244*4882a593Smuzhiyun #undef PIS
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun /* Called from dss.c. Note that we don't touch clocks here,
248*4882a593Smuzhiyun  * but we presume they are on because we got an IRQ. However,
249*4882a593Smuzhiyun  * an irq handler may turn the clocks off, so we may not have
250*4882a593Smuzhiyun  * clock later in the function. */
omap_dispc_irq_handler(int irq,void * arg)251*4882a593Smuzhiyun static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	int i;
254*4882a593Smuzhiyun 	u32 irqstatus, irqenable;
255*4882a593Smuzhiyun 	u32 handledirqs = 0;
256*4882a593Smuzhiyun 	u32 unhandled_errors;
257*4882a593Smuzhiyun 	struct omap_dispc_isr_data *isr_data;
258*4882a593Smuzhiyun 	struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	spin_lock(&dispc_compat.irq_lock);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	irqstatus = dispc_read_irqstatus();
263*4882a593Smuzhiyun 	irqenable = dispc_read_irqenable();
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	/* IRQ is not for us */
266*4882a593Smuzhiyun 	if (!(irqstatus & irqenable)) {
267*4882a593Smuzhiyun 		spin_unlock(&dispc_compat.irq_lock);
268*4882a593Smuzhiyun 		return IRQ_NONE;
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
272*4882a593Smuzhiyun 	spin_lock(&dispc_compat.irq_stats_lock);
273*4882a593Smuzhiyun 	dispc_compat.irq_stats.irq_count++;
274*4882a593Smuzhiyun 	dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
275*4882a593Smuzhiyun 	spin_unlock(&dispc_compat.irq_stats_lock);
276*4882a593Smuzhiyun #endif
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	print_irq_status(irqstatus);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	/* Ack the interrupt. Do it here before clocks are possibly turned
281*4882a593Smuzhiyun 	 * off */
282*4882a593Smuzhiyun 	dispc_clear_irqstatus(irqstatus);
283*4882a593Smuzhiyun 	/* flush posted write */
284*4882a593Smuzhiyun 	dispc_read_irqstatus();
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	/* make a copy and unlock, so that isrs can unregister
287*4882a593Smuzhiyun 	 * themselves */
288*4882a593Smuzhiyun 	memcpy(registered_isr, dispc_compat.registered_isr,
289*4882a593Smuzhiyun 			sizeof(registered_isr));
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	spin_unlock(&dispc_compat.irq_lock);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
294*4882a593Smuzhiyun 		isr_data = &registered_isr[i];
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 		if (!isr_data->isr)
297*4882a593Smuzhiyun 			continue;
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 		if (isr_data->mask & irqstatus) {
300*4882a593Smuzhiyun 			isr_data->isr(isr_data->arg, irqstatus);
301*4882a593Smuzhiyun 			handledirqs |= isr_data->mask;
302*4882a593Smuzhiyun 		}
303*4882a593Smuzhiyun 	}
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	spin_lock(&dispc_compat.irq_lock);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	if (unhandled_errors) {
310*4882a593Smuzhiyun 		dispc_compat.error_irqs |= unhandled_errors;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 		dispc_compat.irq_error_mask &= ~unhandled_errors;
313*4882a593Smuzhiyun 		_omap_dispc_set_irqs();
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 		schedule_work(&dispc_compat.error_work);
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	spin_unlock(&dispc_compat.irq_lock);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	return IRQ_HANDLED;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
dispc_error_worker(struct work_struct * work)323*4882a593Smuzhiyun static void dispc_error_worker(struct work_struct *work)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	int i;
326*4882a593Smuzhiyun 	u32 errors;
327*4882a593Smuzhiyun 	unsigned long flags;
328*4882a593Smuzhiyun 	static const unsigned fifo_underflow_bits[] = {
329*4882a593Smuzhiyun 		DISPC_IRQ_GFX_FIFO_UNDERFLOW,
330*4882a593Smuzhiyun 		DISPC_IRQ_VID1_FIFO_UNDERFLOW,
331*4882a593Smuzhiyun 		DISPC_IRQ_VID2_FIFO_UNDERFLOW,
332*4882a593Smuzhiyun 		DISPC_IRQ_VID3_FIFO_UNDERFLOW,
333*4882a593Smuzhiyun 	};
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	spin_lock_irqsave(&dispc_compat.irq_lock, flags);
336*4882a593Smuzhiyun 	errors = dispc_compat.error_irqs;
337*4882a593Smuzhiyun 	dispc_compat.error_irqs = 0;
338*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	dispc_runtime_get();
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
343*4882a593Smuzhiyun 		struct omap_overlay *ovl;
344*4882a593Smuzhiyun 		unsigned bit;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 		ovl = omap_dss_get_overlay(i);
347*4882a593Smuzhiyun 		bit = fifo_underflow_bits[i];
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 		if (bit & errors) {
350*4882a593Smuzhiyun 			DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
351*4882a593Smuzhiyun 					ovl->name);
352*4882a593Smuzhiyun 			ovl->disable(ovl);
353*4882a593Smuzhiyun 			msleep(50);
354*4882a593Smuzhiyun 		}
355*4882a593Smuzhiyun 	}
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
358*4882a593Smuzhiyun 		struct omap_overlay_manager *mgr;
359*4882a593Smuzhiyun 		unsigned bit;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 		mgr = omap_dss_get_overlay_manager(i);
362*4882a593Smuzhiyun 		bit = dispc_mgr_get_sync_lost_irq(i);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 		if (bit & errors) {
365*4882a593Smuzhiyun 			int j;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 			DSSERR("SYNC_LOST on channel %s, restarting the output "
368*4882a593Smuzhiyun 					"with video overlays disabled\n",
369*4882a593Smuzhiyun 					mgr->name);
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 			dss_mgr_disable(mgr);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 			for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
374*4882a593Smuzhiyun 				struct omap_overlay *ovl;
375*4882a593Smuzhiyun 				ovl = omap_dss_get_overlay(j);
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 				if (ovl->id != OMAP_DSS_GFX &&
378*4882a593Smuzhiyun 						ovl->manager == mgr)
379*4882a593Smuzhiyun 					ovl->disable(ovl);
380*4882a593Smuzhiyun 			}
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 			dss_mgr_enable(mgr);
383*4882a593Smuzhiyun 		}
384*4882a593Smuzhiyun 	}
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	if (errors & DISPC_IRQ_OCP_ERR) {
387*4882a593Smuzhiyun 		DSSERR("OCP_ERR\n");
388*4882a593Smuzhiyun 		for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
389*4882a593Smuzhiyun 			struct omap_overlay_manager *mgr;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 			mgr = omap_dss_get_overlay_manager(i);
392*4882a593Smuzhiyun 			dss_mgr_disable(mgr);
393*4882a593Smuzhiyun 		}
394*4882a593Smuzhiyun 	}
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	spin_lock_irqsave(&dispc_compat.irq_lock, flags);
397*4882a593Smuzhiyun 	dispc_compat.irq_error_mask |= errors;
398*4882a593Smuzhiyun 	_omap_dispc_set_irqs();
399*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	dispc_runtime_put();
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun 
dss_dispc_initialize_irq(void)404*4882a593Smuzhiyun int dss_dispc_initialize_irq(void)
405*4882a593Smuzhiyun {
406*4882a593Smuzhiyun 	int r;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
409*4882a593Smuzhiyun 	spin_lock_init(&dispc_compat.irq_stats_lock);
410*4882a593Smuzhiyun 	dispc_compat.irq_stats.last_reset = jiffies;
411*4882a593Smuzhiyun 	dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
412*4882a593Smuzhiyun #endif
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	spin_lock_init(&dispc_compat.irq_lock);
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	memset(dispc_compat.registered_isr, 0,
417*4882a593Smuzhiyun 			sizeof(dispc_compat.registered_isr));
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
420*4882a593Smuzhiyun 	if (dss_has_feature(FEAT_MGR_LCD2))
421*4882a593Smuzhiyun 		dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
422*4882a593Smuzhiyun 	if (dss_has_feature(FEAT_MGR_LCD3))
423*4882a593Smuzhiyun 		dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
424*4882a593Smuzhiyun 	if (dss_feat_get_num_ovls() > 3)
425*4882a593Smuzhiyun 		dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	/*
428*4882a593Smuzhiyun 	 * there's SYNC_LOST_DIGIT waiting after enabling the DSS,
429*4882a593Smuzhiyun 	 * so clear it
430*4882a593Smuzhiyun 	 */
431*4882a593Smuzhiyun 	dispc_clear_irqstatus(dispc_read_irqstatus());
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	_omap_dispc_set_irqs();
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
438*4882a593Smuzhiyun 	if (r) {
439*4882a593Smuzhiyun 		DSSERR("dispc_request_irq failed\n");
440*4882a593Smuzhiyun 		return r;
441*4882a593Smuzhiyun 	}
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	return 0;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun 
dss_dispc_uninitialize_irq(void)446*4882a593Smuzhiyun void dss_dispc_uninitialize_irq(void)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun 	dispc_free_irq(&dispc_compat);
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun 
dispc_mgr_disable_isr(void * data,u32 mask)451*4882a593Smuzhiyun static void dispc_mgr_disable_isr(void *data, u32 mask)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun 	struct completion *compl = data;
454*4882a593Smuzhiyun 	complete(compl);
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun 
dispc_mgr_enable_lcd_out(enum omap_channel channel)457*4882a593Smuzhiyun static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun 	dispc_mgr_enable(channel, true);
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun 
dispc_mgr_disable_lcd_out(enum omap_channel channel)462*4882a593Smuzhiyun static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun 	DECLARE_COMPLETION_ONSTACK(framedone_compl);
465*4882a593Smuzhiyun 	int r;
466*4882a593Smuzhiyun 	u32 irq;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	if (!dispc_mgr_is_enabled(channel))
469*4882a593Smuzhiyun 		return;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	/*
472*4882a593Smuzhiyun 	 * When we disable LCD output, we need to wait for FRAMEDONE to know
473*4882a593Smuzhiyun 	 * that DISPC has finished with the LCD output.
474*4882a593Smuzhiyun 	 */
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	irq = dispc_mgr_get_framedone_irq(channel);
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
479*4882a593Smuzhiyun 			irq);
480*4882a593Smuzhiyun 	if (r)
481*4882a593Smuzhiyun 		DSSERR("failed to register FRAMEDONE isr\n");
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	dispc_mgr_enable(channel, false);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	/* if we couldn't register for framedone, just sleep and exit */
486*4882a593Smuzhiyun 	if (r) {
487*4882a593Smuzhiyun 		msleep(100);
488*4882a593Smuzhiyun 		return;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&framedone_compl,
492*4882a593Smuzhiyun 				msecs_to_jiffies(100)))
493*4882a593Smuzhiyun 		DSSERR("timeout waiting for FRAME DONE\n");
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
496*4882a593Smuzhiyun 			irq);
497*4882a593Smuzhiyun 	if (r)
498*4882a593Smuzhiyun 		DSSERR("failed to unregister FRAMEDONE isr\n");
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun 
dispc_digit_out_enable_isr(void * data,u32 mask)501*4882a593Smuzhiyun static void dispc_digit_out_enable_isr(void *data, u32 mask)
502*4882a593Smuzhiyun {
503*4882a593Smuzhiyun 	struct completion *compl = data;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	/* ignore any sync lost interrupts */
506*4882a593Smuzhiyun 	if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
507*4882a593Smuzhiyun 		complete(compl);
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun 
dispc_mgr_enable_digit_out(void)510*4882a593Smuzhiyun static void dispc_mgr_enable_digit_out(void)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun 	DECLARE_COMPLETION_ONSTACK(vsync_compl);
513*4882a593Smuzhiyun 	int r;
514*4882a593Smuzhiyun 	u32 irq_mask;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
517*4882a593Smuzhiyun 		return;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	/*
520*4882a593Smuzhiyun 	 * Digit output produces some sync lost interrupts during the first
521*4882a593Smuzhiyun 	 * frame when enabling. Those need to be ignored, so we register for the
522*4882a593Smuzhiyun 	 * sync lost irq to prevent the error handler from triggering.
523*4882a593Smuzhiyun 	 */
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
526*4882a593Smuzhiyun 		dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
529*4882a593Smuzhiyun 			irq_mask);
530*4882a593Smuzhiyun 	if (r) {
531*4882a593Smuzhiyun 		DSSERR("failed to register %x isr\n", irq_mask);
532*4882a593Smuzhiyun 		return;
533*4882a593Smuzhiyun 	}
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 	dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	/* wait for the first evsync */
538*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
539*4882a593Smuzhiyun 		DSSERR("timeout waiting for digit out to start\n");
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
542*4882a593Smuzhiyun 			irq_mask);
543*4882a593Smuzhiyun 	if (r)
544*4882a593Smuzhiyun 		DSSERR("failed to unregister %x isr\n", irq_mask);
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
dispc_mgr_disable_digit_out(void)547*4882a593Smuzhiyun static void dispc_mgr_disable_digit_out(void)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun 	DECLARE_COMPLETION_ONSTACK(framedone_compl);
550*4882a593Smuzhiyun 	int r, i;
551*4882a593Smuzhiyun 	u32 irq_mask;
552*4882a593Smuzhiyun 	int num_irqs;
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
555*4882a593Smuzhiyun 		return;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	/*
558*4882a593Smuzhiyun 	 * When we disable the digit output, we need to wait for FRAMEDONE to
559*4882a593Smuzhiyun 	 * know that DISPC has finished with the output.
560*4882a593Smuzhiyun 	 */
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
563*4882a593Smuzhiyun 	num_irqs = 1;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	if (!irq_mask) {
566*4882a593Smuzhiyun 		/*
567*4882a593Smuzhiyun 		 * omap 2/3 don't have framedone irq for TV, so we need to use
568*4882a593Smuzhiyun 		 * vsyncs for this.
569*4882a593Smuzhiyun 		 */
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 		irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
572*4882a593Smuzhiyun 		/*
573*4882a593Smuzhiyun 		 * We need to wait for both even and odd vsyncs. Note that this
574*4882a593Smuzhiyun 		 * is not totally reliable, as we could get a vsync interrupt
575*4882a593Smuzhiyun 		 * before we disable the output, which leads to timeout in the
576*4882a593Smuzhiyun 		 * wait_for_completion.
577*4882a593Smuzhiyun 		 */
578*4882a593Smuzhiyun 		num_irqs = 2;
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
582*4882a593Smuzhiyun 			irq_mask);
583*4882a593Smuzhiyun 	if (r)
584*4882a593Smuzhiyun 		DSSERR("failed to register %x isr\n", irq_mask);
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	/* if we couldn't register the irq, just sleep and exit */
589*4882a593Smuzhiyun 	if (r) {
590*4882a593Smuzhiyun 		msleep(100);
591*4882a593Smuzhiyun 		return;
592*4882a593Smuzhiyun 	}
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	for (i = 0; i < num_irqs; ++i) {
595*4882a593Smuzhiyun 		if (!wait_for_completion_timeout(&framedone_compl,
596*4882a593Smuzhiyun 					msecs_to_jiffies(100)))
597*4882a593Smuzhiyun 			DSSERR("timeout waiting for digit out to stop\n");
598*4882a593Smuzhiyun 	}
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
601*4882a593Smuzhiyun 			irq_mask);
602*4882a593Smuzhiyun 	if (r)
603*4882a593Smuzhiyun 		DSSERR("failed to unregister %x isr\n", irq_mask);
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
dispc_mgr_enable_sync(enum omap_channel channel)606*4882a593Smuzhiyun void dispc_mgr_enable_sync(enum omap_channel channel)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	if (dss_mgr_is_lcd(channel))
609*4882a593Smuzhiyun 		dispc_mgr_enable_lcd_out(channel);
610*4882a593Smuzhiyun 	else if (channel == OMAP_DSS_CHANNEL_DIGIT)
611*4882a593Smuzhiyun 		dispc_mgr_enable_digit_out();
612*4882a593Smuzhiyun 	else
613*4882a593Smuzhiyun 		WARN_ON(1);
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun 
dispc_mgr_disable_sync(enum omap_channel channel)616*4882a593Smuzhiyun void dispc_mgr_disable_sync(enum omap_channel channel)
617*4882a593Smuzhiyun {
618*4882a593Smuzhiyun 	if (dss_mgr_is_lcd(channel))
619*4882a593Smuzhiyun 		dispc_mgr_disable_lcd_out(channel);
620*4882a593Smuzhiyun 	else if (channel == OMAP_DSS_CHANNEL_DIGIT)
621*4882a593Smuzhiyun 		dispc_mgr_disable_digit_out();
622*4882a593Smuzhiyun 	else
623*4882a593Smuzhiyun 		WARN_ON(1);
624*4882a593Smuzhiyun }
625*4882a593Smuzhiyun 
dispc_irq_wait_handler(void * data,u32 mask)626*4882a593Smuzhiyun static inline void dispc_irq_wait_handler(void *data, u32 mask)
627*4882a593Smuzhiyun {
628*4882a593Smuzhiyun 	complete((struct completion *)data);
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun 
omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,unsigned long timeout)631*4882a593Smuzhiyun int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
632*4882a593Smuzhiyun 		unsigned long timeout)
633*4882a593Smuzhiyun {
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	int r;
636*4882a593Smuzhiyun 	long time_left;
637*4882a593Smuzhiyun 	DECLARE_COMPLETION_ONSTACK(completion);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
640*4882a593Smuzhiyun 			irqmask);
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	if (r)
643*4882a593Smuzhiyun 		return r;
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	time_left = wait_for_completion_interruptible_timeout(&completion,
646*4882a593Smuzhiyun 			timeout);
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	if (time_left == 0)
651*4882a593Smuzhiyun 		return -ETIMEDOUT;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	if (time_left == -ERESTARTSYS)
654*4882a593Smuzhiyun 		return -ERESTARTSYS;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	return 0;
657*4882a593Smuzhiyun }
658