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 = ®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_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}