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