cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dispc-compat.c (15026B)


      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
     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_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
     60static 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 */
    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 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. */
    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_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
    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_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
    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 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
    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 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
    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 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
    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	else
    613		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	else
    623		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}