| // SPDX-License-Identifier: GPL-2.0-only |
| |
| /* Copyright (c) 2007-2018, 2020, The Linux Foundation. All rights reserved. */ |
| |
| #define pr_fmt(fmt) "%s: " fmt, __func__ |
| |
| #include <linux/interrupt.h> |
| #include "mdss_mdp.h" |
| |
| struct mdss_hw *mdss_irq_handlers[MDSS_MAX_HW_BLK]; |
| static DEFINE_SPINLOCK(mdss_lock); |
| |
| int mdss_register_irq(struct mdss_hw *hw) |
| { |
| unsigned long irq_flags; |
| u32 ndx_bit; |
| bool err = false; |
| |
| if (!hw || hw->hw_ndx >= MDSS_MAX_HW_BLK) |
| return -EINVAL; |
| |
| ndx_bit = BIT(hw->hw_ndx); |
| |
| spin_lock_irqsave(&mdss_lock, irq_flags); |
| if (!mdss_irq_handlers[hw->hw_ndx]) |
| mdss_irq_handlers[hw->hw_ndx] = hw; |
| else |
| err = true; |
| spin_unlock_irqrestore(&mdss_lock, irq_flags); |
| |
| if (err) |
| pr_err("panel %d's irq at %pK is already registered\n", |
| hw->hw_ndx, hw->irq_handler); |
| return 0; |
| } |
| |
| void mdss_enable_irq(struct mdss_hw *hw) |
| { |
| unsigned long irq_flags; |
| u32 ndx_bit; |
| |
| if (hw->hw_ndx >= MDSS_MAX_HW_BLK) |
| return; |
| |
| if (!mdss_irq_handlers[hw->hw_ndx]) { |
| pr_err("failed. First register the irq then enable it.\n"); |
| return; |
| } |
| |
| ndx_bit = BIT(hw->hw_ndx); |
| |
| pr_debug("Enable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx, |
| hw->irq_info->irq_ena, hw->irq_info->irq_mask); |
| |
| spin_lock_irqsave(&mdss_lock, irq_flags); |
| if (hw->irq_info->irq_mask & ndx_bit) { |
| pr_debug("MDSS HW ndx=%d is already set, mask=%x\n", |
| hw->hw_ndx, hw->irq_info->irq_mask); |
| } else { |
| hw->irq_info->irq_mask |= ndx_bit; |
| if (!hw->irq_info->irq_ena) { |
| hw->irq_info->irq_ena = true; |
| enable_irq(hw->irq_info->irq); |
| } |
| } |
| spin_unlock_irqrestore(&mdss_lock, irq_flags); |
| } |
| |
| void mdss_disable_irq(struct mdss_hw *hw) |
| { |
| unsigned long irq_flags; |
| u32 ndx_bit; |
| bool err = false; |
| |
| if (hw->hw_ndx >= MDSS_MAX_HW_BLK) |
| return; |
| |
| ndx_bit = BIT(hw->hw_ndx); |
| |
| pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx, |
| hw->irq_info->irq_ena, hw->irq_info->irq_mask); |
| |
| spin_lock_irqsave(&mdss_lock, irq_flags); |
| if (!(hw->irq_info->irq_mask & ndx_bit)) { |
| err = true; |
| } else { |
| hw->irq_info->irq_mask &= ~ndx_bit; |
| if (hw->irq_info->irq_mask == 0) { |
| hw->irq_info->irq_ena = false; |
| disable_irq_nosync(hw->irq_info->irq); |
| } |
| } |
| spin_unlock_irqrestore(&mdss_lock, irq_flags); |
| |
| if (err) |
| pr_warn("MDSS HW ndx=%d is NOT set\n", hw->hw_ndx); |
| } |
| |
| /* called from interrupt context */ |
| void mdss_disable_irq_nosync(struct mdss_hw *hw) |
| { |
| u32 ndx_bit; |
| bool err = false; |
| |
| if (hw->hw_ndx >= MDSS_MAX_HW_BLK) |
| return; |
| |
| ndx_bit = BIT(hw->hw_ndx); |
| |
| pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx, |
| hw->irq_info->irq_ena, hw->irq_info->irq_mask); |
| |
| spin_lock(&mdss_lock); |
| if (!(hw->irq_info->irq_mask & ndx_bit)) { |
| err = true; |
| } else { |
| hw->irq_info->irq_mask &= ~ndx_bit; |
| if (hw->irq_info->irq_mask == 0) { |
| hw->irq_info->irq_ena = false; |
| disable_irq_nosync(hw->irq_info->irq); |
| } |
| } |
| spin_unlock(&mdss_lock); |
| |
| if (err) |
| pr_warn("MDSS HW ndx=%d is NOT set\n", hw->hw_ndx); |
| } |
| |
| int mdss_irq_dispatch(u32 hw_ndx, int irq, void *ptr) |
| { |
| struct mdss_hw *hw; |
| int rc = -ENODEV; |
| |
| spin_lock(&mdss_lock); |
| hw = mdss_irq_handlers[hw_ndx]; |
| spin_unlock(&mdss_lock); |
| |
| if (hw) |
| rc = hw->irq_handler(irq, hw->ptr); |
| |
| return rc; |
| } |
| |
| void mdss_enable_irq_wake(struct mdss_hw *hw) |
| { |
| unsigned long irq_flags; |
| u32 ndx_bit; |
| |
| if (hw->hw_ndx >= MDSS_MAX_HW_BLK) |
| return; |
| |
| if (!mdss_irq_handlers[hw->hw_ndx]) { |
| pr_err("failed. First register the irq then enable it.\n"); |
| return; |
| } |
| |
| ndx_bit = BIT(hw->hw_ndx); |
| |
| pr_debug("Enable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx, |
| hw->irq_info->irq_wake_ena, |
| hw->irq_info->irq_wake_mask); |
| |
| spin_lock_irqsave(&mdss_lock, irq_flags); |
| if (hw->irq_info->irq_wake_mask & ndx_bit) { |
| pr_debug("MDSS HW ndx=%d is already set, mask=%x\n", |
| hw->hw_ndx, hw->irq_info->irq_wake_mask); |
| } else { |
| hw->irq_info->irq_wake_mask |= ndx_bit; |
| if (!hw->irq_info->irq_wake_ena) { |
| hw->irq_info->irq_wake_ena = true; |
| enable_irq_wake(hw->irq_info->irq); |
| } |
| } |
| spin_unlock_irqrestore(&mdss_lock, irq_flags); |
| } |
| |
| void mdss_disable_irq_wake(struct mdss_hw *hw) |
| { |
| unsigned long irq_flags; |
| u32 ndx_bit; |
| bool err = false; |
| |
| if (hw->hw_ndx >= MDSS_MAX_HW_BLK) |
| return; |
| |
| ndx_bit = BIT(hw->hw_ndx); |
| |
| pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx, |
| hw->irq_info->irq_wake_ena, |
| hw->irq_info->irq_wake_mask); |
| |
| spin_lock_irqsave(&mdss_lock, irq_flags); |
| if (!(hw->irq_info->irq_wake_mask & ndx_bit)) { |
| err = true; |
| } else { |
| hw->irq_info->irq_wake_mask &= ~ndx_bit; |
| if (hw->irq_info->irq_wake_ena) { |
| hw->irq_info->irq_wake_ena = false; |
| disable_irq_wake(hw->irq_info->irq); |
| } |
| } |
| spin_unlock_irqrestore(&mdss_lock, irq_flags); |
| |
| if (err) |
| pr_warn("MDSS HW ndx=%d is NOT set\n", hw->hw_ndx); |
| } |
| |
| struct mdss_util_intf mdss_util = { |
| .register_irq = mdss_register_irq, |
| .enable_irq = mdss_enable_irq, |
| .disable_irq = mdss_disable_irq, |
| .enable_wake_irq = mdss_enable_irq_wake, |
| .disable_wake_irq = mdss_disable_irq_wake, |
| .disable_irq_nosync = mdss_disable_irq_nosync, |
| .irq_dispatch = mdss_irq_dispatch, |
| .get_iommu_domain = NULL, |
| .iommu_attached = NULL, |
| .iommu_ctrl = NULL, |
| .bus_bandwidth_ctrl = NULL, |
| .bus_scale_set_quota = NULL, |
| .panel_intf_type = NULL, |
| .panel_intf_status = NULL, |
| .mdp_probe_done = false |
| }; |
| |
| struct mdss_util_intf *mdss_get_util_intf() |
| { |
| return &mdss_util; |
| } |
| EXPORT_SYMBOL(mdss_get_util_intf); |
| |
| /* This routine should only be called from interrupt context */ |
| bool mdss_get_irq_enable_state(struct mdss_hw *hw) |
| { |
| bool is_irq_enabled; |
| |
| spin_lock(&mdss_lock); |
| is_irq_enabled = hw->irq_info->irq_ena; |
| spin_unlock(&mdss_lock); |
| |
| return is_irq_enabled; |
| } |