brintos

brintos / linux-shallow public Read only

0
0
Text · 14.7 KiB · cc2ad78 Raw
658 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 Texas Instruments4 * 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		832 33struct omap_dispc_isr_data {34	omap_dispc_isr_t	isr;35	void			*arg;36	u32			mask;37};38 39struct dispc_irq_stats {40	unsigned long last_reset;41	unsigned irq_count;42	unsigned irqs[32];43};44 45static 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_STATS53	spinlock_t irq_stats_lock;54	struct dispc_irq_stats irq_stats;55#endif56} dispc_compat;57 58 59#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS60static 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 PIS114}115#endif116 117/* dispc.irq_lock has to be locked by the caller */118static 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 138int 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;185err:186	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);187 188	return ret;189}190EXPORT_SYMBOL(omap_dispc_register_isr);191 192int 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}224EXPORT_SYMBOL(omap_dispc_unregister_isr);225 226static 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 PIS245}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 have250 * clock later in the function. */251static 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_STATS272	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#endif277 278	print_irq_status(irqstatus);279 280	/* Ack the interrupt. Do it here before clocks are possibly turned281	 * 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 unregister287	 * 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 323static 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 404int dss_dispc_initialize_irq(void)405{406	int r;407 408#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS409	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#endif413 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 it430	 */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 446void dss_dispc_uninitialize_irq(void)447{448	dispc_free_irq(&dispc_compat);449}450 451static void dispc_mgr_disable_isr(void *data, u32 mask)452{453	struct completion *compl = data;454	complete(compl);455}456 457static void dispc_mgr_enable_lcd_out(enum omap_channel channel)458{459	dispc_mgr_enable(channel, true);460}461 462static 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 know473	 * 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 501static 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 510static 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 first521	 * frame when enabling. Those need to be ignored, so we register for the522	 * 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 547static 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 to559	 * 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 use568		 * 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 this574		 * is not totally reliable, as we could get a vsync interrupt575		 * before we disable the output, which leads to timeout in the576		 * 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 606void 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	else613		WARN_ON(1);614}615 616void 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	else623		WARN_ON(1);624}625 626static inline void dispc_irq_wait_handler(void *data, u32 mask)627{628	complete((struct completion *)data);629}630 631int 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