This is a snapshot of mdss driver from msm-4.14 as of commit '15e898828d1e (fbdev: msm: Avoid UAF in mdss_dsi_cmd_write)'. Change-Id: I16fe01fd45855d5c268c210ec61f09c6fa625761 Signed-off-by: Nirmal Abraham <nabrah@codeaurora.org> Signed-off-by: Althaf Neelanchirayil <aneelanc@codeaurora.org>
243 lines
5.4 KiB
C
243 lines
5.4 KiB
C
// 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;
|
|
}
|