All the sub-systems in mdss share the same irq. This change provides
the sub-systems with the interfaces to register/unregister their own
irq handlers.

With this change, struct mdp5_kms does not have to keep the hdmi or
edp context.

Signed-off-by: Hai Li <hali at codeaurora.org>
---
 drivers/gpu/drm/msm/hdmi/hdmi.c         |  12 +++-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c | 107 ++++++++++++++++++++++++++++++--
 drivers/gpu/drm/msm/msm_drv.h           |  19 +++++-
 3 files changed, 130 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 9d00dcb..aaf5e2b 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -39,7 +39,7 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
                        power_on ? "Enable" : "Disable", ctrl);
 }

-irqreturn_t hdmi_irq(int irq, void *dev_id)
+static irqreturn_t hdmi_irq(int irq, void *dev_id)
 {
        struct hdmi *hdmi = dev_id;

@@ -59,6 +59,9 @@ void hdmi_destroy(struct kref *kref)
        struct hdmi *hdmi = container_of(kref, struct hdmi, refcount);
        struct hdmi_phy *phy = hdmi->phy;

+       if (hdmi->config->shared_irq)
+               msm_shared_irq_unregister(MSM_SUBSYS_HDMI);
+
        if (phy)
                phy->funcs->destroy(phy);

@@ -221,6 +224,13 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct 
drm_encoder *encoder)
                                        hdmi->irq, ret);
                        goto fail;
                }
+       } else {
+               ret = msm_shared_irq_register(MSM_SUBSYS_HDMI, hdmi_irq, hdmi);
+               if (ret < 0) {
+                       dev_err(dev->dev, "failed to register shared IRQ: %d\n",
+                                       ret);
+                       goto fail;
+               }
        }

        encoder->bridge = hdmi->bridge;
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
index f2b985b..2973c1c 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark at gmail.com>
  *
@@ -19,6 +20,75 @@
 #include "msm_drv.h"
 #include "mdp5_kms.h"

+struct msm_subsys_shared_irq {
+       u32 mask;
+       u32 count;
+
+       /* Filled by sub system */
+       irqreturn_t (*handler)(int irq, void *dev_id);
+       void *data;
+};
+
+static struct msm_subsys_shared_irq msm_shared_irqs[MSM_SUBSYS_COUNT] = {
+       [MSM_SUBSYS_MDP] = {.mask = MDP5_HW_INTR_STATUS_INTR_MDP,
+                               .count = 0},
+       [MSM_SUBSYS_DSI_0] = {.mask = MDP5_HW_INTR_STATUS_INTR_DSI0,
+                               .count = 0},
+       [MSM_SUBSYS_DSI_1] = {.mask = MDP5_HW_INTR_STATUS_INTR_DSI1,
+                               .count = 0},
+       [MSM_SUBSYS_HDMI] = {.mask = MDP5_HW_INTR_STATUS_INTR_HDMI,
+                               .count = 0},
+       [MSM_SUBSYS_EDP] = {.mask = MDP5_HW_INTR_STATUS_INTR_EDP,
+                               .count = 0},
+};
+
+static irqreturn_t mdp5_irq_mdp(int irq, void *dev_id);
+
+int msm_shared_irq_register(enum msm_sub_system sys_id,
+       irqreturn_t (*handler)(int irq, void *dev_id), void *data)
+{
+       if (sys_id >= MSM_SUBSYS_COUNT) {
+               DRM_ERROR("Invalid sys_id %d", sys_id);
+               return -EINVAL;
+       }
+
+       if (msm_shared_irqs[sys_id].handler != NULL) {
+               DRM_ERROR("sys %d irq already registered", sys_id);
+               return -EBUSY;
+       }
+
+       msm_shared_irqs[sys_id].data = data;
+       msm_shared_irqs[sys_id].handler = handler;
+
+       return 0;
+}
+
+/*
+ * This function should be called after the interrupt
+ * on the sub-system is disabled.
+ */
+int msm_shared_irq_unregister(enum msm_sub_system sys_id)
+{
+       if (sys_id >= MSM_SUBSYS_COUNT) {
+               DRM_ERROR("Invalid sys_id %d", sys_id);
+               return -EINVAL;
+       }
+
+       msm_shared_irqs[sys_id].handler = NULL;
+       msm_shared_irqs[sys_id].data = NULL;
+
+       /*
+        * Make sure irq_handler and data is invalid.
+        * Then we only need to wait until the last pending interrupt is done.
+        */
+       wmb();
+
+       while (msm_shared_irqs[sys_id].count & 0x1)
+               usleep_range(100, 1000);
+
+       return 0;
+}
+
 void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask)
 {
        mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_INTR_EN, irqmask);
@@ -47,6 +117,9 @@ int mdp5_irq_postinstall(struct msm_kms *kms)
                        MDP5_IRQ_INTF2_UNDER_RUN |
                        MDP5_IRQ_INTF3_UNDER_RUN;

+       /* Register mdp irq to mdss */
+       msm_shared_irq_register(MSM_SUBSYS_MDP, mdp5_irq_mdp, mdp_kms);
+
        mdp_irq_register(mdp_kms, error_handler);

        return 0;
@@ -56,10 +129,15 @@ void mdp5_irq_uninstall(struct msm_kms *kms)
 {
        struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
        mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000);
+
+       /* Make sure interrupt is disabled before remove irq. */
+       wmb();
+       msm_shared_irq_unregister(MSM_SUBSYS_MDP);
 }

-static void mdp5_irq_mdp(struct mdp_kms *mdp_kms)
+static irqreturn_t mdp5_irq_mdp(int irq, void *dev_id)
 {
+       struct mdp_kms *mdp_kms = (struct mdp_kms *)dev_id;
        struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms);
        struct drm_device *dev = mdp5_kms->dev;
        struct msm_drm_private *priv = dev->dev_private;
@@ -76,23 +154,40 @@ static void mdp5_irq_mdp(struct mdp_kms *mdp_kms)
        for (id = 0; id < priv->num_crtcs; id++)
                if (status & mdp5_crtc_vblank(priv->crtcs[id]))
                        drm_handle_vblank(dev, id);
+
+       return IRQ_HANDLED;
 }

 irqreturn_t mdp5_irq(struct msm_kms *kms)
 {
        struct mdp_kms *mdp_kms = to_mdp_kms(kms);
        struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms);
+       struct msm_subsys_shared_irq *irq;
        uint32_t intr;
+       int i;

        intr = mdp5_read(mdp5_kms, REG_MDP5_HW_INTR_STATUS);

        VERB("intr=%08x", intr);

-       if (intr & MDP5_HW_INTR_STATUS_INTR_MDP)
-               mdp5_irq_mdp(mdp_kms);
-
-       if (intr & MDP5_HW_INTR_STATUS_INTR_HDMI)
-               hdmi_irq(0, mdp5_kms->hdmi);
+       for (i = 0; i < MSM_SUBSYS_COUNT; i++) {
+               irq = &msm_shared_irqs[i];
+               if (intr & irq->mask) {
+                       irq->count++;
+
+                       /*
+                        * These 2 wmb() ensure count is odd number
+                        * during handler is running.
+                        */
+                       wmb();
+                       if ((irq->handler != NULL) && (irq->data != NULL))
+                               irq->handler(0, irq->data);
+
+                       /* Make sure count increments after handler is done */
+                       wmb();
+                       irq->count++;
+               }
+       }

        return IRQ_HANDLED;
 }
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 67f9d0a..718ac55 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -127,6 +127,16 @@ struct msm_drm_private {
        } vram;
 };

+/* For mdp5 only */
+enum msm_sub_system {
+       MSM_SUBSYS_MDP = 0,
+       MSM_SUBSYS_DSI_0,
+       MSM_SUBSYS_DSI_1,
+       MSM_SUBSYS_HDMI,
+       MSM_SUBSYS_EDP,
+       MSM_SUBSYS_COUNT
+};
+
 struct msm_format {
        uint32_t pixel_format;
 };
@@ -145,6 +155,14 @@ void __msm_fence_worker(struct work_struct *work);
                (_cb)->func = _func;                         \
        } while (0)

+/*
+ * For mdp5 only, callers should call these 2 functions
+ * only if the irqs are shared with others.
+ */
+int msm_shared_irq_register(enum msm_sub_system sys_id,
+       irqreturn_t (*handler)(int irq, void *dev_id), void *data);
+int msm_shared_irq_unregister(enum msm_sub_system sys_id);
+
 int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);

 int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
@@ -203,7 +221,6 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device 
*dev);

 struct hdmi;
 struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder);
-irqreturn_t hdmi_irq(int irq, void *dev_id);
 void __init hdmi_register(void);
 void __exit hdmi_unregister(void);

-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

Reply via email to