18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments 48c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define DSS_SUBSYS_NAME "APPLY" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <video/omapfb_dss.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "dss.h" 218c2ecf20Sopenharmony_ci#include "dss_features.h" 228c2ecf20Sopenharmony_ci#include "dispc-compat.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ 258c2ecf20Sopenharmony_ci DISPC_IRQ_OCP_ERR | \ 268c2ecf20Sopenharmony_ci DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ 278c2ecf20Sopenharmony_ci DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ 288c2ecf20Sopenharmony_ci DISPC_IRQ_SYNC_LOST | \ 298c2ecf20Sopenharmony_ci DISPC_IRQ_SYNC_LOST_DIGIT) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DISPC_MAX_NR_ISRS 8 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct omap_dispc_isr_data { 348c2ecf20Sopenharmony_ci omap_dispc_isr_t isr; 358c2ecf20Sopenharmony_ci void *arg; 368c2ecf20Sopenharmony_ci u32 mask; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct dispc_irq_stats { 408c2ecf20Sopenharmony_ci unsigned long last_reset; 418c2ecf20Sopenharmony_ci unsigned irq_count; 428c2ecf20Sopenharmony_ci unsigned irqs[32]; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct { 468c2ecf20Sopenharmony_ci spinlock_t irq_lock; 478c2ecf20Sopenharmony_ci u32 irq_error_mask; 488c2ecf20Sopenharmony_ci struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; 498c2ecf20Sopenharmony_ci u32 error_irqs; 508c2ecf20Sopenharmony_ci struct work_struct error_work; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 538c2ecf20Sopenharmony_ci spinlock_t irq_stats_lock; 548c2ecf20Sopenharmony_ci struct dispc_irq_stats irq_stats; 558c2ecf20Sopenharmony_ci#endif 568c2ecf20Sopenharmony_ci} dispc_compat; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 608c2ecf20Sopenharmony_cistatic void dispc_dump_irqs(struct seq_file *s) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci unsigned long flags; 638c2ecf20Sopenharmony_ci struct dispc_irq_stats stats; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci stats = dispc_compat.irq_stats; 688c2ecf20Sopenharmony_ci memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats)); 698c2ecf20Sopenharmony_ci dispc_compat.irq_stats.last_reset = jiffies; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci seq_printf(s, "period %u ms\n", 748c2ecf20Sopenharmony_ci jiffies_to_msecs(jiffies - stats.last_reset)); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci seq_printf(s, "irqs %d\n", stats.irq_count); 778c2ecf20Sopenharmony_ci#define PIS(x) \ 788c2ecf20Sopenharmony_ci seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci PIS(FRAMEDONE); 818c2ecf20Sopenharmony_ci PIS(VSYNC); 828c2ecf20Sopenharmony_ci PIS(EVSYNC_EVEN); 838c2ecf20Sopenharmony_ci PIS(EVSYNC_ODD); 848c2ecf20Sopenharmony_ci PIS(ACBIAS_COUNT_STAT); 858c2ecf20Sopenharmony_ci PIS(PROG_LINE_NUM); 868c2ecf20Sopenharmony_ci PIS(GFX_FIFO_UNDERFLOW); 878c2ecf20Sopenharmony_ci PIS(GFX_END_WIN); 888c2ecf20Sopenharmony_ci PIS(PAL_GAMMA_MASK); 898c2ecf20Sopenharmony_ci PIS(OCP_ERR); 908c2ecf20Sopenharmony_ci PIS(VID1_FIFO_UNDERFLOW); 918c2ecf20Sopenharmony_ci PIS(VID1_END_WIN); 928c2ecf20Sopenharmony_ci PIS(VID2_FIFO_UNDERFLOW); 938c2ecf20Sopenharmony_ci PIS(VID2_END_WIN); 948c2ecf20Sopenharmony_ci if (dss_feat_get_num_ovls() > 3) { 958c2ecf20Sopenharmony_ci PIS(VID3_FIFO_UNDERFLOW); 968c2ecf20Sopenharmony_ci PIS(VID3_END_WIN); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci PIS(SYNC_LOST); 998c2ecf20Sopenharmony_ci PIS(SYNC_LOST_DIGIT); 1008c2ecf20Sopenharmony_ci PIS(WAKEUP); 1018c2ecf20Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD2)) { 1028c2ecf20Sopenharmony_ci PIS(FRAMEDONE2); 1038c2ecf20Sopenharmony_ci PIS(VSYNC2); 1048c2ecf20Sopenharmony_ci PIS(ACBIAS_COUNT_STAT2); 1058c2ecf20Sopenharmony_ci PIS(SYNC_LOST2); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD3)) { 1088c2ecf20Sopenharmony_ci PIS(FRAMEDONE3); 1098c2ecf20Sopenharmony_ci PIS(VSYNC3); 1108c2ecf20Sopenharmony_ci PIS(ACBIAS_COUNT_STAT3); 1118c2ecf20Sopenharmony_ci PIS(SYNC_LOST3); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci#undef PIS 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci#endif 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* dispc.irq_lock has to be locked by the caller */ 1188c2ecf20Sopenharmony_cistatic void _omap_dispc_set_irqs(void) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u32 mask; 1218c2ecf20Sopenharmony_ci int i; 1228c2ecf20Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mask = dispc_compat.irq_error_mask; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 1278c2ecf20Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (isr_data->isr == NULL) 1308c2ecf20Sopenharmony_ci continue; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci mask |= isr_data->mask; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci dispc_write_irqenable(mask); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciint omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int i; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci unsigned long flags; 1438c2ecf20Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (isr == NULL) 1468c2ecf20Sopenharmony_ci return -EINVAL; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* check for duplicate entry */ 1518c2ecf20Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 1528c2ecf20Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 1538c2ecf20Sopenharmony_ci if (isr_data->isr == isr && isr_data->arg == arg && 1548c2ecf20Sopenharmony_ci isr_data->mask == mask) { 1558c2ecf20Sopenharmony_ci ret = -EINVAL; 1568c2ecf20Sopenharmony_ci goto err; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci isr_data = NULL; 1618c2ecf20Sopenharmony_ci ret = -EBUSY; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 1648c2ecf20Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (isr_data->isr != NULL) 1678c2ecf20Sopenharmony_ci continue; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci isr_data->isr = isr; 1708c2ecf20Sopenharmony_ci isr_data->arg = arg; 1718c2ecf20Sopenharmony_ci isr_data->mask = mask; 1728c2ecf20Sopenharmony_ci ret = 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (ret) 1788c2ecf20Sopenharmony_ci goto err; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci _omap_dispc_set_irqs(); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_cierr: 1868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omap_dispc_register_isr); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciint omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int i; 1958c2ecf20Sopenharmony_ci unsigned long flags; 1968c2ecf20Sopenharmony_ci int ret = -EINVAL; 1978c2ecf20Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 2028c2ecf20Sopenharmony_ci isr_data = &dispc_compat.registered_isr[i]; 2038c2ecf20Sopenharmony_ci if (isr_data->isr != isr || isr_data->arg != arg || 2048c2ecf20Sopenharmony_ci isr_data->mask != mask) 2058c2ecf20Sopenharmony_ci continue; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* found the correct isr */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci isr_data->isr = NULL; 2108c2ecf20Sopenharmony_ci isr_data->arg = NULL; 2118c2ecf20Sopenharmony_ci isr_data->mask = 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ret = 0; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (ret == 0) 2188c2ecf20Sopenharmony_ci _omap_dispc_set_irqs(); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omap_dispc_unregister_isr); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void print_irq_status(u32 status) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci if ((status & dispc_compat.irq_error_mask) == 0) 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : "" 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n", 2348c2ecf20Sopenharmony_ci status, 2358c2ecf20Sopenharmony_ci PIS(OCP_ERR), 2368c2ecf20Sopenharmony_ci PIS(GFX_FIFO_UNDERFLOW), 2378c2ecf20Sopenharmony_ci PIS(VID1_FIFO_UNDERFLOW), 2388c2ecf20Sopenharmony_ci PIS(VID2_FIFO_UNDERFLOW), 2398c2ecf20Sopenharmony_ci dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "", 2408c2ecf20Sopenharmony_ci PIS(SYNC_LOST), 2418c2ecf20Sopenharmony_ci PIS(SYNC_LOST_DIGIT), 2428c2ecf20Sopenharmony_ci dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "", 2438c2ecf20Sopenharmony_ci dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : ""); 2448c2ecf20Sopenharmony_ci#undef PIS 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* Called from dss.c. Note that we don't touch clocks here, 2488c2ecf20Sopenharmony_ci * but we presume they are on because we got an IRQ. However, 2498c2ecf20Sopenharmony_ci * an irq handler may turn the clocks off, so we may not have 2508c2ecf20Sopenharmony_ci * clock later in the function. */ 2518c2ecf20Sopenharmony_cistatic irqreturn_t omap_dispc_irq_handler(int irq, void *arg) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int i; 2548c2ecf20Sopenharmony_ci u32 irqstatus, irqenable; 2558c2ecf20Sopenharmony_ci u32 handledirqs = 0; 2568c2ecf20Sopenharmony_ci u32 unhandled_errors; 2578c2ecf20Sopenharmony_ci struct omap_dispc_isr_data *isr_data; 2588c2ecf20Sopenharmony_ci struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci spin_lock(&dispc_compat.irq_lock); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci irqstatus = dispc_read_irqstatus(); 2638c2ecf20Sopenharmony_ci irqenable = dispc_read_irqenable(); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* IRQ is not for us */ 2668c2ecf20Sopenharmony_ci if (!(irqstatus & irqenable)) { 2678c2ecf20Sopenharmony_ci spin_unlock(&dispc_compat.irq_lock); 2688c2ecf20Sopenharmony_ci return IRQ_NONE; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 2728c2ecf20Sopenharmony_ci spin_lock(&dispc_compat.irq_stats_lock); 2738c2ecf20Sopenharmony_ci dispc_compat.irq_stats.irq_count++; 2748c2ecf20Sopenharmony_ci dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs); 2758c2ecf20Sopenharmony_ci spin_unlock(&dispc_compat.irq_stats_lock); 2768c2ecf20Sopenharmony_ci#endif 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci print_irq_status(irqstatus); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Ack the interrupt. Do it here before clocks are possibly turned 2818c2ecf20Sopenharmony_ci * off */ 2828c2ecf20Sopenharmony_ci dispc_clear_irqstatus(irqstatus); 2838c2ecf20Sopenharmony_ci /* flush posted write */ 2848c2ecf20Sopenharmony_ci dispc_read_irqstatus(); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* make a copy and unlock, so that isrs can unregister 2878c2ecf20Sopenharmony_ci * themselves */ 2888c2ecf20Sopenharmony_ci memcpy(registered_isr, dispc_compat.registered_isr, 2898c2ecf20Sopenharmony_ci sizeof(registered_isr)); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci spin_unlock(&dispc_compat.irq_lock); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { 2948c2ecf20Sopenharmony_ci isr_data = ®istered_isr[i]; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!isr_data->isr) 2978c2ecf20Sopenharmony_ci continue; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (isr_data->mask & irqstatus) { 3008c2ecf20Sopenharmony_ci isr_data->isr(isr_data->arg, irqstatus); 3018c2ecf20Sopenharmony_ci handledirqs |= isr_data->mask; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci spin_lock(&dispc_compat.irq_lock); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (unhandled_errors) { 3108c2ecf20Sopenharmony_ci dispc_compat.error_irqs |= unhandled_errors; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dispc_compat.irq_error_mask &= ~unhandled_errors; 3138c2ecf20Sopenharmony_ci _omap_dispc_set_irqs(); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci schedule_work(&dispc_compat.error_work); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci spin_unlock(&dispc_compat.irq_lock); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void dispc_error_worker(struct work_struct *work) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci int i; 3268c2ecf20Sopenharmony_ci u32 errors; 3278c2ecf20Sopenharmony_ci unsigned long flags; 3288c2ecf20Sopenharmony_ci static const unsigned fifo_underflow_bits[] = { 3298c2ecf20Sopenharmony_ci DISPC_IRQ_GFX_FIFO_UNDERFLOW, 3308c2ecf20Sopenharmony_ci DISPC_IRQ_VID1_FIFO_UNDERFLOW, 3318c2ecf20Sopenharmony_ci DISPC_IRQ_VID2_FIFO_UNDERFLOW, 3328c2ecf20Sopenharmony_ci DISPC_IRQ_VID3_FIFO_UNDERFLOW, 3338c2ecf20Sopenharmony_ci }; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 3368c2ecf20Sopenharmony_ci errors = dispc_compat.error_irqs; 3378c2ecf20Sopenharmony_ci dispc_compat.error_irqs = 0; 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dispc_runtime_get(); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci for (i = 0; i < omap_dss_get_num_overlays(); ++i) { 3438c2ecf20Sopenharmony_ci struct omap_overlay *ovl; 3448c2ecf20Sopenharmony_ci unsigned bit; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ovl = omap_dss_get_overlay(i); 3478c2ecf20Sopenharmony_ci bit = fifo_underflow_bits[i]; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (bit & errors) { 3508c2ecf20Sopenharmony_ci DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n", 3518c2ecf20Sopenharmony_ci ovl->name); 3528c2ecf20Sopenharmony_ci ovl->disable(ovl); 3538c2ecf20Sopenharmony_ci msleep(50); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { 3588c2ecf20Sopenharmony_ci struct omap_overlay_manager *mgr; 3598c2ecf20Sopenharmony_ci unsigned bit; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci mgr = omap_dss_get_overlay_manager(i); 3628c2ecf20Sopenharmony_ci bit = dispc_mgr_get_sync_lost_irq(i); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (bit & errors) { 3658c2ecf20Sopenharmony_ci int j; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci DSSERR("SYNC_LOST on channel %s, restarting the output " 3688c2ecf20Sopenharmony_ci "with video overlays disabled\n", 3698c2ecf20Sopenharmony_ci mgr->name); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci dss_mgr_disable(mgr); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci for (j = 0; j < omap_dss_get_num_overlays(); ++j) { 3748c2ecf20Sopenharmony_ci struct omap_overlay *ovl; 3758c2ecf20Sopenharmony_ci ovl = omap_dss_get_overlay(j); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (ovl->id != OMAP_DSS_GFX && 3788c2ecf20Sopenharmony_ci ovl->manager == mgr) 3798c2ecf20Sopenharmony_ci ovl->disable(ovl); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci dss_mgr_enable(mgr); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (errors & DISPC_IRQ_OCP_ERR) { 3878c2ecf20Sopenharmony_ci DSSERR("OCP_ERR\n"); 3888c2ecf20Sopenharmony_ci for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { 3898c2ecf20Sopenharmony_ci struct omap_overlay_manager *mgr; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci mgr = omap_dss_get_overlay_manager(i); 3928c2ecf20Sopenharmony_ci dss_mgr_disable(mgr); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci spin_lock_irqsave(&dispc_compat.irq_lock, flags); 3978c2ecf20Sopenharmony_ci dispc_compat.irq_error_mask |= errors; 3988c2ecf20Sopenharmony_ci _omap_dispc_set_irqs(); 3998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci dispc_runtime_put(); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciint dss_dispc_initialize_irq(void) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int r; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 4098c2ecf20Sopenharmony_ci spin_lock_init(&dispc_compat.irq_stats_lock); 4108c2ecf20Sopenharmony_ci dispc_compat.irq_stats.last_reset = jiffies; 4118c2ecf20Sopenharmony_ci dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); 4128c2ecf20Sopenharmony_ci#endif 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci spin_lock_init(&dispc_compat.irq_lock); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci memset(dispc_compat.registered_isr, 0, 4178c2ecf20Sopenharmony_ci sizeof(dispc_compat.registered_isr)); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR; 4208c2ecf20Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD2)) 4218c2ecf20Sopenharmony_ci dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; 4228c2ecf20Sopenharmony_ci if (dss_has_feature(FEAT_MGR_LCD3)) 4238c2ecf20Sopenharmony_ci dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3; 4248c2ecf20Sopenharmony_ci if (dss_feat_get_num_ovls() > 3) 4258c2ecf20Sopenharmony_ci dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * there's SYNC_LOST_DIGIT waiting after enabling the DSS, 4298c2ecf20Sopenharmony_ci * so clear it 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci dispc_clear_irqstatus(dispc_read_irqstatus()); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci INIT_WORK(&dispc_compat.error_work, dispc_error_worker); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci _omap_dispc_set_irqs(); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat); 4388c2ecf20Sopenharmony_ci if (r) { 4398c2ecf20Sopenharmony_ci DSSERR("dispc_request_irq failed\n"); 4408c2ecf20Sopenharmony_ci return r; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_civoid dss_dispc_uninitialize_irq(void) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci dispc_free_irq(&dispc_compat); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic void dispc_mgr_disable_isr(void *data, u32 mask) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct completion *compl = data; 4548c2ecf20Sopenharmony_ci complete(compl); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void dispc_mgr_enable_lcd_out(enum omap_channel channel) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci dispc_mgr_enable(channel, true); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void dispc_mgr_disable_lcd_out(enum omap_channel channel) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(framedone_compl); 4658c2ecf20Sopenharmony_ci int r; 4668c2ecf20Sopenharmony_ci u32 irq; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!dispc_mgr_is_enabled(channel)) 4698c2ecf20Sopenharmony_ci return; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * When we disable LCD output, we need to wait for FRAMEDONE to know 4738c2ecf20Sopenharmony_ci * that DISPC has finished with the LCD output. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci irq = dispc_mgr_get_framedone_irq(channel); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, 4798c2ecf20Sopenharmony_ci irq); 4808c2ecf20Sopenharmony_ci if (r) 4818c2ecf20Sopenharmony_ci DSSERR("failed to register FRAMEDONE isr\n"); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci dispc_mgr_enable(channel, false); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* if we couldn't register for framedone, just sleep and exit */ 4868c2ecf20Sopenharmony_ci if (r) { 4878c2ecf20Sopenharmony_ci msleep(100); 4888c2ecf20Sopenharmony_ci return; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&framedone_compl, 4928c2ecf20Sopenharmony_ci msecs_to_jiffies(100))) 4938c2ecf20Sopenharmony_ci DSSERR("timeout waiting for FRAME DONE\n"); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, 4968c2ecf20Sopenharmony_ci irq); 4978c2ecf20Sopenharmony_ci if (r) 4988c2ecf20Sopenharmony_ci DSSERR("failed to unregister FRAMEDONE isr\n"); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic void dispc_digit_out_enable_isr(void *data, u32 mask) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct completion *compl = data; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* ignore any sync lost interrupts */ 5068c2ecf20Sopenharmony_ci if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD)) 5078c2ecf20Sopenharmony_ci complete(compl); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void dispc_mgr_enable_digit_out(void) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(vsync_compl); 5138c2ecf20Sopenharmony_ci int r; 5148c2ecf20Sopenharmony_ci u32 irq_mask; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT)) 5178c2ecf20Sopenharmony_ci return; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * Digit output produces some sync lost interrupts during the first 5218c2ecf20Sopenharmony_ci * frame when enabling. Those need to be ignored, so we register for the 5228c2ecf20Sopenharmony_ci * sync lost irq to prevent the error handler from triggering. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) | 5268c2ecf20Sopenharmony_ci dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl, 5298c2ecf20Sopenharmony_ci irq_mask); 5308c2ecf20Sopenharmony_ci if (r) { 5318c2ecf20Sopenharmony_ci DSSERR("failed to register %x isr\n", irq_mask); 5328c2ecf20Sopenharmony_ci return; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* wait for the first evsync */ 5388c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100))) 5398c2ecf20Sopenharmony_ci DSSERR("timeout waiting for digit out to start\n"); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl, 5428c2ecf20Sopenharmony_ci irq_mask); 5438c2ecf20Sopenharmony_ci if (r) 5448c2ecf20Sopenharmony_ci DSSERR("failed to unregister %x isr\n", irq_mask); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void dispc_mgr_disable_digit_out(void) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(framedone_compl); 5508c2ecf20Sopenharmony_ci int r, i; 5518c2ecf20Sopenharmony_ci u32 irq_mask; 5528c2ecf20Sopenharmony_ci int num_irqs; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT)) 5558c2ecf20Sopenharmony_ci return; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* 5588c2ecf20Sopenharmony_ci * When we disable the digit output, we need to wait for FRAMEDONE to 5598c2ecf20Sopenharmony_ci * know that DISPC has finished with the output. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT); 5638c2ecf20Sopenharmony_ci num_irqs = 1; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (!irq_mask) { 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * omap 2/3 don't have framedone irq for TV, so we need to use 5688c2ecf20Sopenharmony_ci * vsyncs for this. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT); 5728c2ecf20Sopenharmony_ci /* 5738c2ecf20Sopenharmony_ci * We need to wait for both even and odd vsyncs. Note that this 5748c2ecf20Sopenharmony_ci * is not totally reliable, as we could get a vsync interrupt 5758c2ecf20Sopenharmony_ci * before we disable the output, which leads to timeout in the 5768c2ecf20Sopenharmony_ci * wait_for_completion. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci num_irqs = 2; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, 5828c2ecf20Sopenharmony_ci irq_mask); 5838c2ecf20Sopenharmony_ci if (r) 5848c2ecf20Sopenharmony_ci DSSERR("failed to register %x isr\n", irq_mask); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* if we couldn't register the irq, just sleep and exit */ 5898c2ecf20Sopenharmony_ci if (r) { 5908c2ecf20Sopenharmony_ci msleep(100); 5918c2ecf20Sopenharmony_ci return; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci for (i = 0; i < num_irqs; ++i) { 5958c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&framedone_compl, 5968c2ecf20Sopenharmony_ci msecs_to_jiffies(100))) 5978c2ecf20Sopenharmony_ci DSSERR("timeout waiting for digit out to stop\n"); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, 6018c2ecf20Sopenharmony_ci irq_mask); 6028c2ecf20Sopenharmony_ci if (r) 6038c2ecf20Sopenharmony_ci DSSERR("failed to unregister %x isr\n", irq_mask); 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_civoid dispc_mgr_enable_sync(enum omap_channel channel) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci if (dss_mgr_is_lcd(channel)) 6098c2ecf20Sopenharmony_ci dispc_mgr_enable_lcd_out(channel); 6108c2ecf20Sopenharmony_ci else if (channel == OMAP_DSS_CHANNEL_DIGIT) 6118c2ecf20Sopenharmony_ci dispc_mgr_enable_digit_out(); 6128c2ecf20Sopenharmony_ci else 6138c2ecf20Sopenharmony_ci WARN_ON(1); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_civoid dispc_mgr_disable_sync(enum omap_channel channel) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci if (dss_mgr_is_lcd(channel)) 6198c2ecf20Sopenharmony_ci dispc_mgr_disable_lcd_out(channel); 6208c2ecf20Sopenharmony_ci else if (channel == OMAP_DSS_CHANNEL_DIGIT) 6218c2ecf20Sopenharmony_ci dispc_mgr_disable_digit_out(); 6228c2ecf20Sopenharmony_ci else 6238c2ecf20Sopenharmony_ci WARN_ON(1); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic inline void dispc_irq_wait_handler(void *data, u32 mask) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci complete((struct completion *)data); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ciint omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, 6328c2ecf20Sopenharmony_ci unsigned long timeout) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci int r; 6368c2ecf20Sopenharmony_ci long time_left; 6378c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(completion); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, 6408c2ecf20Sopenharmony_ci irqmask); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (r) 6438c2ecf20Sopenharmony_ci return r; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci time_left = wait_for_completion_interruptible_timeout(&completion, 6468c2ecf20Sopenharmony_ci timeout); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (time_left == 0) 6518c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (time_left == -ERESTARTSYS) 6548c2ecf20Sopenharmony_ci return -ERESTARTSYS; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci} 658