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 = ®istered_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