File Index
Files and directories
Sign in
FILE

dispc-compat.c

File preview with syntax highlighting.

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