162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments 462306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define DSS_SUBSYS_NAME "APPLY" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/spinlock.h> 1362306a36Sopenharmony_ci#include <linux/jiffies.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/seq_file.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <video/omapfb_dss.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "dss.h" 2162306a36Sopenharmony_ci#include "dss_features.h" 2262306a36Sopenharmony_ci#include "dispc-compat.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ 2562306a36Sopenharmony_ci DISPC_IRQ_OCP_ERR | \ 2662306a36Sopenharmony_ci DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ 2762306a36Sopenharmony_ci DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ 2862306a36Sopenharmony_ci DISPC_IRQ_SYNC_LOST | \ 2962306a36Sopenharmony_ci DISPC_IRQ_SYNC_LOST_DIGIT) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define DISPC_MAX_NR_ISRS 8 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct omap_dispc_isr_data { 3462306a36Sopenharmony_ci omap_dispc_isr_t isr; 3562306a36Sopenharmony_ci void *arg; 3662306a36Sopenharmony_ci u32 mask; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct dispc_irq_stats { 4062306a36Sopenharmony_ci unsigned long last_reset; 4162306a36Sopenharmony_ci unsigned irq_count; 4262306a36Sopenharmony_ci unsigned irqs[32]; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic struct { 4662306a36Sopenharmony_ci spinlock_t irq_lock; 4762306a36Sopenharmony_ci u32 irq_error_mask; 4862306a36Sopenharmony_ci struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; 4962306a36Sopenharmony_ci u32 error_irqs; 5062306a36Sopenharmony_ci struct work_struct error_work; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 5362306a36Sopenharmony_ci spinlock_t irq_stats_lock; 5462306a36Sopenharmony_ci struct dispc_irq_stats irq_stats; 5562306a36Sopenharmony_ci#endif 5662306a36Sopenharmony_ci} dispc_compat; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 6062306a36Sopenharmony_cistatic void dispc_dump_irqs(struct seq_file *s) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci unsigned long flags; 6362306a36Sopenharmony_ci struct dispc_irq_stats stats; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci stats = dispc_compat.irq_stats; 6862306a36Sopenharmony_ci memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats)); 6962306a36Sopenharmony_ci dispc_compat.irq_stats.last_reset = jiffies; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci seq_printf(s, "period %u ms\n", 7462306a36Sopenharmony_ci jiffies_to_msecs(jiffies - stats.last_reset)); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci seq_printf(s, "irqs %d\n", stats.irq_count); 7762306a36Sopenharmony_ci#define PIS(x) \ 7862306a36Sopenharmony_ci seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci PIS(FRAMEDONE); 8162306a36Sopenharmony_ci PIS(VSYNC); 8262306a36Sopenharmony_ci PIS(EVSYNC_EVEN); 8362306a36Sopenharmony_ci PIS(EVSYNC_ODD); 8462306a36Sopenharmony_ci PIS(ACBIAS_COUNT_STAT); 8562306a36Sopenharmony_ci PIS(PROG_LINE_NUM); 8662306a36Sopenharmony_ci PIS(GFX_FIFO_UNDERFLOW); 8762306a36Sopenharmony_ci PIS(GFX_END_WIN); 8862306a36Sopenharmony_ci PIS(PAL_GAMMA_MASK); 8962306a36Sopenharmony_ci PIS(OCP_ERR); 9062306a36Sopenharmony_ci PIS(VID1_FIFO_UNDERFLOW); 9162306a36Sopenharmony_ci PIS(VID1_END_WIN); 9262306a36Sopenharmony_ci PIS(VID2_FIFO_UNDERFLOW); 9362306a36Sopenharmony_ci PIS(VID2_END_WIN); 9462306a36Sopenharmony_ci if (dss_feat_get_num_ovls() > 3) { 9562306a36Sopenharmony_ci PIS(VID3_FIFO_UNDERFLOW); 9662306a36Sopenharmony_ci PIS(VID3_END_WIN); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci PIS(SYNC_LOST); 9962306a36Sopenharmony_ci PIS(SYNC_LOST_DIGIT); 10062306a36Sopenharmony_ci PIS(WAKEUP); 10162306a36Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD2)) { 10262306a36Sopenharmony_ci PIS(FRAMEDONE2); 10362306a36Sopenharmony_ci PIS(VSYNC2); 10462306a36Sopenharmony_ci PIS(ACBIAS_COUNT_STAT2); 10562306a36Sopenharmony_ci PIS(SYNC_LOST2); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD3)) { 10862306a36Sopenharmony_ci PIS(FRAMEDONE3); 10962306a36Sopenharmony_ci PIS(VSYNC3); 11062306a36Sopenharmony_ci PIS(ACBIAS_COUNT_STAT3); 11162306a36Sopenharmony_ci PIS(SYNC_LOST3); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci#undef PIS 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci#endif 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* dispc.irq_lock has to be locked by the caller */ 11862306a36Sopenharmony_cistatic void _omap_dispc_set_irqs(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u32 mask; 12162306a36Sopenharmony_ci int i; 12262306a36Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mask = dispc_compat.irq_error_mask; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 12762306a36Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (isr_data->isr == NULL) 13062306a36Sopenharmony_ci continue; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mask |= isr_data->mask; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci dispc_write_irqenable(mask); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int i; 14162306a36Sopenharmony_ci int ret; 14262306a36Sopenharmony_ci unsigned long flags; 14362306a36Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (isr == NULL) 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* check for duplicate entry */ 15162306a36Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 15262306a36Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 15362306a36Sopenharmony_ci if (isr_data->isr == isr && isr_data->arg == arg && 15462306a36Sopenharmony_ci isr_data->mask == mask) { 15562306a36Sopenharmony_ci ret = -EINVAL; 15662306a36Sopenharmony_ci goto err; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci isr_data = NULL; 16162306a36Sopenharmony_ci ret = -EBUSY; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 16462306a36Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (isr_data->isr != NULL) 16762306a36Sopenharmony_ci continue; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci isr_data->isr = isr; 17062306a36Sopenharmony_ci isr_data->arg = arg; 17162306a36Sopenharmony_ci isr_data->mask = mask; 17262306a36Sopenharmony_ci ret = 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (ret) 17862306a36Sopenharmony_ci goto err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci _omap_dispc_set_irqs(); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_cierr: 18662306a36Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dispc_register_isr); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciint omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int i; 19562306a36Sopenharmony_ci unsigned long flags; 19662306a36Sopenharmony_ci int ret = -EINVAL; 19762306a36Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 20262306a36Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 20362306a36Sopenharmony_ci if (isr_data->isr != isr || isr_data->arg != arg || 20462306a36Sopenharmony_ci isr_data->mask != mask) 20562306a36Sopenharmony_ci continue; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* found the correct isr */ 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci isr_data->isr = NULL; 21062306a36Sopenharmony_ci isr_data->arg = NULL; 21162306a36Sopenharmony_ci isr_data->mask = 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = 0; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ret == 0) 21862306a36Sopenharmony_ci _omap_dispc_set_irqs(); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return ret; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ciEXPORT_SYMBOL(omap_dispc_unregister_isr); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void print_irq_status(u32 status) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci if ((status & dispc_compat.irq_error_mask) == 0) 22962306a36Sopenharmony_ci return; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : "" 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n", 23462306a36Sopenharmony_ci status, 23562306a36Sopenharmony_ci PIS(OCP_ERR), 23662306a36Sopenharmony_ci PIS(GFX_FIFO_UNDERFLOW), 23762306a36Sopenharmony_ci PIS(VID1_FIFO_UNDERFLOW), 23862306a36Sopenharmony_ci PIS(VID2_FIFO_UNDERFLOW), 23962306a36Sopenharmony_ci dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "", 24062306a36Sopenharmony_ci PIS(SYNC_LOST), 24162306a36Sopenharmony_ci PIS(SYNC_LOST_DIGIT), 24262306a36Sopenharmony_ci dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "", 24362306a36Sopenharmony_ci dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : ""); 24462306a36Sopenharmony_ci#undef PIS 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* Called from dss.c. Note that we don't touch clocks here, 24862306a36Sopenharmony_ci * but we presume they are on because we got an IRQ. However, 24962306a36Sopenharmony_ci * an irq handler may turn the clocks off, so we may not have 25062306a36Sopenharmony_ci * clock later in the function. */ 25162306a36Sopenharmony_cistatic irqreturn_t omap_dispc_irq_handler(int irq, void *arg) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci u32 irqstatus, irqenable; 25562306a36Sopenharmony_ci u32 handledirqs = 0; 25662306a36Sopenharmony_ci u32 unhandled_errors; 25762306a36Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 25862306a36Sopenharmony_ci struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci spin_lock(&dispc_compat.irq_lock); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci irqstatus = dispc_read_irqstatus(); 26362306a36Sopenharmony_ci irqenable = dispc_read_irqenable(); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* IRQ is not for us */ 26662306a36Sopenharmony_ci if (!(irqstatus & irqenable)) { 26762306a36Sopenharmony_ci spin_unlock(&dispc_compat.irq_lock); 26862306a36Sopenharmony_ci return IRQ_NONE; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 27262306a36Sopenharmony_ci spin_lock(&dispc_compat.irq_stats_lock); 27362306a36Sopenharmony_ci dispc_compat.irq_stats.irq_count++; 27462306a36Sopenharmony_ci dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs); 27562306a36Sopenharmony_ci spin_unlock(&dispc_compat.irq_stats_lock); 27662306a36Sopenharmony_ci#endif 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci print_irq_status(irqstatus); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Ack the interrupt. Do it here before clocks are possibly turned 28162306a36Sopenharmony_ci * off */ 28262306a36Sopenharmony_ci dispc_clear_irqstatus(irqstatus); 28362306a36Sopenharmony_ci /* flush posted write */ 28462306a36Sopenharmony_ci dispc_read_irqstatus(); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* make a copy and unlock, so that isrs can unregister 28762306a36Sopenharmony_ci * themselves */ 28862306a36Sopenharmony_ci memcpy(registered_isr, dispc_compat.registered_isr, 28962306a36Sopenharmony_ci sizeof(registered_isr)); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci spin_unlock(&dispc_compat.irq_lock); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 29462306a36Sopenharmony_ci isr_data = ®istered_isr[i]; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (!isr_data->isr) 29762306a36Sopenharmony_ci continue; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (isr_data->mask & irqstatus) { 30062306a36Sopenharmony_ci isr_data->isr(isr_data->arg, irqstatus); 30162306a36Sopenharmony_ci handledirqs |= isr_data->mask; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci spin_lock(&dispc_compat.irq_lock); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (unhandled_errors) { 31062306a36Sopenharmony_ci dispc_compat.error_irqs |= unhandled_errors; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci dispc_compat.irq_error_mask &= ~unhandled_errors; 31362306a36Sopenharmony_ci _omap_dispc_set_irqs(); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci schedule_work(&dispc_compat.error_work); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci spin_unlock(&dispc_compat.irq_lock); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return IRQ_HANDLED; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void dispc_error_worker(struct work_struct *work) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci int i; 32662306a36Sopenharmony_ci u32 errors; 32762306a36Sopenharmony_ci unsigned long flags; 32862306a36Sopenharmony_ci static const unsigned fifo_underflow_bits[] = { 32962306a36Sopenharmony_ci DISPC_IRQ_GFX_FIFO_UNDERFLOW, 33062306a36Sopenharmony_ci DISPC_IRQ_VID1_FIFO_UNDERFLOW, 33162306a36Sopenharmony_ci DISPC_IRQ_VID2_FIFO_UNDERFLOW, 33262306a36Sopenharmony_ci DISPC_IRQ_VID3_FIFO_UNDERFLOW, 33362306a36Sopenharmony_ci }; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 33662306a36Sopenharmony_ci errors = dispc_compat.error_irqs; 33762306a36Sopenharmony_ci dispc_compat.error_irqs = 0; 33862306a36Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci dispc_runtime_get(); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < omap_dss_get_num_overlays(); ++i) { 34362306a36Sopenharmony_ci struct omap_overlay *ovl; 34462306a36Sopenharmony_ci unsigned bit; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ovl = omap_dss_get_overlay(i); 34762306a36Sopenharmony_ci bit = fifo_underflow_bits[i]; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (bit & errors) { 35062306a36Sopenharmony_ci DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n", 35162306a36Sopenharmony_ci ovl->name); 35262306a36Sopenharmony_ci ovl->disable(ovl); 35362306a36Sopenharmony_ci msleep(50); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { 35862306a36Sopenharmony_ci struct omap_overlay_manager *mgr; 35962306a36Sopenharmony_ci unsigned bit; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci mgr = omap_dss_get_overlay_manager(i); 36262306a36Sopenharmony_ci bit = dispc_mgr_get_sync_lost_irq(i); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (bit & errors) { 36562306a36Sopenharmony_ci int j; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci DSSERR("SYNC_LOST on channel %s, restarting the output " 36862306a36Sopenharmony_ci "with video overlays disabled\n", 36962306a36Sopenharmony_ci mgr->name); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci dss_mgr_disable(mgr); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for (j = 0; j < omap_dss_get_num_overlays(); ++j) { 37462306a36Sopenharmony_ci struct omap_overlay *ovl; 37562306a36Sopenharmony_ci ovl = omap_dss_get_overlay(j); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (ovl->id != OMAP_DSS_GFX && 37862306a36Sopenharmony_ci ovl->manager == mgr) 37962306a36Sopenharmony_ci ovl->disable(ovl); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci dss_mgr_enable(mgr); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (errors & DISPC_IRQ_OCP_ERR) { 38762306a36Sopenharmony_ci DSSERR("OCP_ERR\n"); 38862306a36Sopenharmony_ci for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { 38962306a36Sopenharmony_ci struct omap_overlay_manager *mgr; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci mgr = omap_dss_get_overlay_manager(i); 39262306a36Sopenharmony_ci dss_mgr_disable(mgr); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 39762306a36Sopenharmony_ci dispc_compat.irq_error_mask |= errors; 39862306a36Sopenharmony_ci _omap_dispc_set_irqs(); 39962306a36Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci dispc_runtime_put(); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciint dss_dispc_initialize_irq(void) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci int r; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 40962306a36Sopenharmony_ci spin_lock_init(&dispc_compat.irq_stats_lock); 41062306a36Sopenharmony_ci dispc_compat.irq_stats.last_reset = jiffies; 41162306a36Sopenharmony_ci dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); 41262306a36Sopenharmony_ci#endif 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci spin_lock_init(&dispc_compat.irq_lock); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci memset(dispc_compat.registered_isr, 0, 41762306a36Sopenharmony_ci sizeof(dispc_compat.registered_isr)); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR; 42062306a36Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD2)) 42162306a36Sopenharmony_ci dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; 42262306a36Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD3)) 42362306a36Sopenharmony_ci dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3; 42462306a36Sopenharmony_ci if (dss_feat_get_num_ovls() > 3) 42562306a36Sopenharmony_ci dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * there's SYNC_LOST_DIGIT waiting after enabling the DSS, 42962306a36Sopenharmony_ci * so clear it 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci dispc_clear_irqstatus(dispc_read_irqstatus()); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci INIT_WORK(&dispc_compat.error_work, dispc_error_worker); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci _omap_dispc_set_irqs(); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat); 43862306a36Sopenharmony_ci if (r) { 43962306a36Sopenharmony_ci DSSERR("dispc_request_irq failed\n"); 44062306a36Sopenharmony_ci return r; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_civoid dss_dispc_uninitialize_irq(void) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci dispc_free_irq(&dispc_compat); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void dispc_mgr_disable_isr(void *data, u32 mask) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct completion *compl = data; 45462306a36Sopenharmony_ci complete(compl); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void dispc_mgr_enable_lcd_out(enum omap_channel channel) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci dispc_mgr_enable(channel, true); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void dispc_mgr_disable_lcd_out(enum omap_channel channel) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(framedone_compl); 46562306a36Sopenharmony_ci int r; 46662306a36Sopenharmony_ci u32 irq; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!dispc_mgr_is_enabled(channel)) 46962306a36Sopenharmony_ci return; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * When we disable LCD output, we need to wait for FRAMEDONE to know 47362306a36Sopenharmony_ci * that DISPC has finished with the LCD output. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci irq = dispc_mgr_get_framedone_irq(channel); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, 47962306a36Sopenharmony_ci irq); 48062306a36Sopenharmony_ci if (r) 48162306a36Sopenharmony_ci DSSERR("failed to register FRAMEDONE isr\n"); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci dispc_mgr_enable(channel, false); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* if we couldn't register for framedone, just sleep and exit */ 48662306a36Sopenharmony_ci if (r) { 48762306a36Sopenharmony_ci msleep(100); 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!wait_for_completion_timeout(&framedone_compl, 49262306a36Sopenharmony_ci msecs_to_jiffies(100))) 49362306a36Sopenharmony_ci DSSERR("timeout waiting for FRAME DONE\n"); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, 49662306a36Sopenharmony_ci irq); 49762306a36Sopenharmony_ci if (r) 49862306a36Sopenharmony_ci DSSERR("failed to unregister FRAMEDONE isr\n"); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void dispc_digit_out_enable_isr(void *data, u32 mask) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct completion *compl = data; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* ignore any sync lost interrupts */ 50662306a36Sopenharmony_ci if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD)) 50762306a36Sopenharmony_ci complete(compl); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void dispc_mgr_enable_digit_out(void) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(vsync_compl); 51362306a36Sopenharmony_ci int r; 51462306a36Sopenharmony_ci u32 irq_mask; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT)) 51762306a36Sopenharmony_ci return; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* 52062306a36Sopenharmony_ci * Digit output produces some sync lost interrupts during the first 52162306a36Sopenharmony_ci * frame when enabling. Those need to be ignored, so we register for the 52262306a36Sopenharmony_ci * sync lost irq to prevent the error handler from triggering. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) | 52662306a36Sopenharmony_ci dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl, 52962306a36Sopenharmony_ci irq_mask); 53062306a36Sopenharmony_ci if (r) { 53162306a36Sopenharmony_ci DSSERR("failed to register %x isr\n", irq_mask); 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* wait for the first evsync */ 53862306a36Sopenharmony_ci if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100))) 53962306a36Sopenharmony_ci DSSERR("timeout waiting for digit out to start\n"); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl, 54262306a36Sopenharmony_ci irq_mask); 54362306a36Sopenharmony_ci if (r) 54462306a36Sopenharmony_ci DSSERR("failed to unregister %x isr\n", irq_mask); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void dispc_mgr_disable_digit_out(void) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(framedone_compl); 55062306a36Sopenharmony_ci int r, i; 55162306a36Sopenharmony_ci u32 irq_mask; 55262306a36Sopenharmony_ci int num_irqs; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT)) 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* 55862306a36Sopenharmony_ci * When we disable the digit output, we need to wait for FRAMEDONE to 55962306a36Sopenharmony_ci * know that DISPC has finished with the output. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT); 56362306a36Sopenharmony_ci num_irqs = 1; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (!irq_mask) { 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * omap 2/3 don't have framedone irq for TV, so we need to use 56862306a36Sopenharmony_ci * vsyncs for this. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT); 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * We need to wait for both even and odd vsyncs. Note that this 57462306a36Sopenharmony_ci * is not totally reliable, as we could get a vsync interrupt 57562306a36Sopenharmony_ci * before we disable the output, which leads to timeout in the 57662306a36Sopenharmony_ci * wait_for_completion. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci num_irqs = 2; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, 58262306a36Sopenharmony_ci irq_mask); 58362306a36Sopenharmony_ci if (r) 58462306a36Sopenharmony_ci DSSERR("failed to register %x isr\n", irq_mask); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* if we couldn't register the irq, just sleep and exit */ 58962306a36Sopenharmony_ci if (r) { 59062306a36Sopenharmony_ci msleep(100); 59162306a36Sopenharmony_ci return; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci for (i = 0; i < num_irqs; ++i) { 59562306a36Sopenharmony_ci if (!wait_for_completion_timeout(&framedone_compl, 59662306a36Sopenharmony_ci msecs_to_jiffies(100))) 59762306a36Sopenharmony_ci DSSERR("timeout waiting for digit out to stop\n"); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, 60162306a36Sopenharmony_ci irq_mask); 60262306a36Sopenharmony_ci if (r) 60362306a36Sopenharmony_ci DSSERR("failed to unregister %x isr\n", irq_mask); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_civoid dispc_mgr_enable_sync(enum omap_channel channel) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci if (dss_mgr_is_lcd(channel)) 60962306a36Sopenharmony_ci dispc_mgr_enable_lcd_out(channel); 61062306a36Sopenharmony_ci else if (channel == OMAP_DSS_CHANNEL_DIGIT) 61162306a36Sopenharmony_ci dispc_mgr_enable_digit_out(); 61262306a36Sopenharmony_ci else 61362306a36Sopenharmony_ci WARN_ON(1); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_civoid dispc_mgr_disable_sync(enum omap_channel channel) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci if (dss_mgr_is_lcd(channel)) 61962306a36Sopenharmony_ci dispc_mgr_disable_lcd_out(channel); 62062306a36Sopenharmony_ci else if (channel == OMAP_DSS_CHANNEL_DIGIT) 62162306a36Sopenharmony_ci dispc_mgr_disable_digit_out(); 62262306a36Sopenharmony_ci else 62362306a36Sopenharmony_ci WARN_ON(1); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic inline void dispc_irq_wait_handler(void *data, u32 mask) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci complete((struct completion *)data); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ciint omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, 63262306a36Sopenharmony_ci unsigned long timeout) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci int r; 63662306a36Sopenharmony_ci long time_left; 63762306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(completion); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, 64062306a36Sopenharmony_ci irqmask); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (r) 64362306a36Sopenharmony_ci return r; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci time_left = wait_for_completion_interruptible_timeout(&completion, 64662306a36Sopenharmony_ci timeout); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (time_left == 0) 65162306a36Sopenharmony_ci return -ETIMEDOUT; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (time_left == -ERESTARTSYS) 65462306a36Sopenharmony_ci return -ERESTARTSYS; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci} 658