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