On Fri, Nov 14, 2014 at 5:42 PM, Hai Li <hali@xxxxxxxxxxxxxx> wrote: > 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. > So, I think the point of this is to better deal w/ different hw variants which do or do-not have hdmi, edp, dsi, etc.. But, just playing devil's advocate here, it seems like it would be simpler to instead just do something like: if (priv->hdmi && (intr & MDP5_HW_INTR_STATUS_INTR_HDMI)) hdmi_irq(0, priv->hdmi); if (priv->edp && (intr & MDP5_HW_INTR_STATUS_INTR_EDP)) edp_irq(0, priv->edp); ... etc ... It is a little less elegant. But it is also less lines of code. I guess there will be plenty of necessary complexity as we add support for all mdp5 features. So avoiding some unnecessary complexity might be a good thing in the long run. If we really did want to make it more dynamic, we could always extend 'struct mdp_irq' to take both an irq mask and an initiator id, I suppose. I'm not sure if that is overkill. AFAICT we really only have 5 different subsystems to dispatch to (mdp5 itself, and dsi0/dsi1/hdmi/edp), so simply adding some if-not-null checks in mdp5_irq() seems pretty reasonable to me. BR, -R > Signed-off-by: Hai Li <hali@xxxxxxxxxxxxxx> > --- > 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@xxxxxxxxx> > * > @@ -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 > -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html