linux/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c
<<
>>
Prefs
   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}
 658