drm/komeda: Add irq handling
authorjames qian wang (Arm Technology China) <james.qian.wang@arm.com>
Tue, 22 Jan 2019 09:24:25 +0000 (09:24 +0000)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Mon, 1 Apr 2019 17:08:26 +0000 (18:08 +0100)
1. Added irq_handler/irq_enable/irq_disable to komeda_dev_func, then the
   Komeda-CORE can control the HW irq via these chip function.
2. Install irq and register irq_handler to system by DRM, so once the IRQ
   coming, the handling sequence is:

   komeda_kms_irq_handler(int irq, void *data)
        /* step 1. call into the CHIP to recognize event */
mdev->funcs->irq_handler(mdev, &evts);

/* step 2. notify the crtc to handle the events */
for (i = 0; i < kms->n_crtcs; i++)
komeda_crtc_handle_event(&kms->crtcs[i], &evts);

v2:
- Move get IRQ number into this change.
- Enable irq before drm_dev_register.

Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
drivers/gpu/drm/arm/display/komeda/komeda_dev.c
drivers/gpu/drm/arm/display/komeda/komeda_dev.h
drivers/gpu/drm/arm/display/komeda/komeda_kms.c
drivers/gpu/drm/arm/display/komeda/komeda_kms.h

index 4e2dfb3d4d6215a214003a4ea1d4524dafc45b16..72631d673f85926f88b15dcf79437922eb6cd2f8 100644 (file)
@@ -9,6 +9,240 @@
 #include "d71_dev.h"
 #include "malidp_io.h"
 
+static u64 get_lpu_event(struct d71_pipeline *d71_pipeline)
+{
+       u32 __iomem *reg = d71_pipeline->lpu_addr;
+       u32 status, raw_status;
+       u64 evts = 0ULL;
+
+       raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS);
+       if (raw_status & LPU_IRQ_IBSY)
+               evts |= KOMEDA_EVENT_IBSY;
+       if (raw_status & LPU_IRQ_EOW)
+               evts |= KOMEDA_EVENT_EOW;
+
+       if (raw_status & (LPU_IRQ_ERR | LPU_IRQ_IBSY)) {
+               u32 restore = 0, tbu_status;
+               /* Check error of LPU status */
+               status = malidp_read32(reg, BLK_STATUS);
+               if (status & LPU_STATUS_AXIE) {
+                       restore |= LPU_STATUS_AXIE;
+                       evts |= KOMEDA_ERR_AXIE;
+               }
+               if (status & LPU_STATUS_ACE0) {
+                       restore |= LPU_STATUS_ACE0;
+                       evts |= KOMEDA_ERR_ACE0;
+               }
+               if (status & LPU_STATUS_ACE1) {
+                       restore |= LPU_STATUS_ACE1;
+                       evts |= KOMEDA_ERR_ACE1;
+               }
+               if (status & LPU_STATUS_ACE2) {
+                       restore |= LPU_STATUS_ACE2;
+                       evts |= KOMEDA_ERR_ACE2;
+               }
+               if (status & LPU_STATUS_ACE3) {
+                       restore |= LPU_STATUS_ACE3;
+                       evts |= KOMEDA_ERR_ACE3;
+               }
+               if (restore != 0)
+                       malidp_write32_mask(reg, BLK_STATUS, restore, 0);
+
+               restore = 0;
+               /* Check errors of TBU status */
+               tbu_status = malidp_read32(reg, LPU_TBU_STATUS);
+               if (tbu_status & LPU_TBU_STATUS_TCF) {
+                       restore |= LPU_TBU_STATUS_TCF;
+                       evts |= KOMEDA_ERR_TCF;
+               }
+               if (tbu_status & LPU_TBU_STATUS_TTNG) {
+                       restore |= LPU_TBU_STATUS_TTNG;
+                       evts |= KOMEDA_ERR_TTNG;
+               }
+               if (tbu_status & LPU_TBU_STATUS_TITR) {
+                       restore |= LPU_TBU_STATUS_TITR;
+                       evts |= KOMEDA_ERR_TITR;
+               }
+               if (tbu_status & LPU_TBU_STATUS_TEMR) {
+                       restore |= LPU_TBU_STATUS_TEMR;
+                       evts |= KOMEDA_ERR_TEMR;
+               }
+               if (tbu_status & LPU_TBU_STATUS_TTF) {
+                       restore |= LPU_TBU_STATUS_TTF;
+                       evts |= KOMEDA_ERR_TTF;
+               }
+               if (restore != 0)
+                       malidp_write32_mask(reg, LPU_TBU_STATUS, restore, 0);
+       }
+
+       malidp_write32(reg, BLK_IRQ_CLEAR, raw_status);
+       return evts;
+}
+
+static u64 get_cu_event(struct d71_pipeline *d71_pipeline)
+{
+       u32 __iomem *reg = d71_pipeline->cu_addr;
+       u32 status, raw_status;
+       u64 evts = 0ULL;
+
+       raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS);
+       if (raw_status & CU_IRQ_OVR)
+               evts |= KOMEDA_EVENT_OVR;
+
+       if (raw_status & (CU_IRQ_ERR | CU_IRQ_OVR)) {
+               status = malidp_read32(reg, BLK_STATUS) & 0x7FFFFFFF;
+               if (status & CU_STATUS_CPE)
+                       evts |= KOMEDA_ERR_CPE;
+               if (status & CU_STATUS_ZME)
+                       evts |= KOMEDA_ERR_ZME;
+               if (status & CU_STATUS_CFGE)
+                       evts |= KOMEDA_ERR_CFGE;
+               if (status)
+                       malidp_write32_mask(reg, BLK_STATUS, status, 0);
+       }
+
+       malidp_write32(reg, BLK_IRQ_CLEAR, raw_status);
+
+       return evts;
+}
+
+static u64 get_dou_event(struct d71_pipeline *d71_pipeline)
+{
+       u32 __iomem *reg = d71_pipeline->dou_addr;
+       u32 status, raw_status;
+       u64 evts = 0ULL;
+
+       raw_status = malidp_read32(reg, BLK_IRQ_RAW_STATUS);
+       if (raw_status & DOU_IRQ_PL0)
+               evts |= KOMEDA_EVENT_VSYNC;
+       if (raw_status & DOU_IRQ_UND)
+               evts |= KOMEDA_EVENT_URUN;
+
+       if (raw_status & (DOU_IRQ_ERR | DOU_IRQ_UND)) {
+               u32 restore  = 0;
+
+               status = malidp_read32(reg, BLK_STATUS);
+               if (status & DOU_STATUS_DRIFTTO) {
+                       restore |= DOU_STATUS_DRIFTTO;
+                       evts |= KOMEDA_ERR_DRIFTTO;
+               }
+               if (status & DOU_STATUS_FRAMETO) {
+                       restore |= DOU_STATUS_FRAMETO;
+                       evts |= KOMEDA_ERR_FRAMETO;
+               }
+               if (status & DOU_STATUS_TETO) {
+                       restore |= DOU_STATUS_TETO;
+                       evts |= KOMEDA_ERR_TETO;
+               }
+               if (status & DOU_STATUS_CSCE) {
+                       restore |= DOU_STATUS_CSCE;
+                       evts |= KOMEDA_ERR_CSCE;
+               }
+
+               if (restore != 0)
+                       malidp_write32_mask(reg, BLK_STATUS, restore, 0);
+       }
+
+       malidp_write32(reg, BLK_IRQ_CLEAR, raw_status);
+       return evts;
+}
+
+static u64 get_pipeline_event(struct d71_pipeline *d71_pipeline, u32 gcu_status)
+{
+       u32 evts = 0ULL;
+
+       if (gcu_status & (GLB_IRQ_STATUS_LPU0 | GLB_IRQ_STATUS_LPU1))
+               evts |= get_lpu_event(d71_pipeline);
+
+       if (gcu_status & (GLB_IRQ_STATUS_CU0 | GLB_IRQ_STATUS_CU1))
+               evts |= get_cu_event(d71_pipeline);
+
+       if (gcu_status & (GLB_IRQ_STATUS_DOU0 | GLB_IRQ_STATUS_DOU1))
+               evts |= get_dou_event(d71_pipeline);
+
+       return evts;
+}
+
+static irqreturn_t
+d71_irq_handler(struct komeda_dev *mdev, struct komeda_events *evts)
+{
+       struct d71_dev *d71 = mdev->chip_data;
+       u32 status, gcu_status, raw_status;
+
+       gcu_status = malidp_read32(d71->gcu_addr, GLB_IRQ_STATUS);
+
+       if (gcu_status & GLB_IRQ_STATUS_GCU) {
+               raw_status = malidp_read32(d71->gcu_addr, BLK_IRQ_RAW_STATUS);
+               if (raw_status & GCU_IRQ_CVAL0)
+                       evts->pipes[0] |= KOMEDA_EVENT_FLIP;
+               if (raw_status & GCU_IRQ_CVAL1)
+                       evts->pipes[1] |= KOMEDA_EVENT_FLIP;
+               if (raw_status & GCU_IRQ_ERR) {
+                       status = malidp_read32(d71->gcu_addr, BLK_STATUS);
+                       if (status & GCU_STATUS_MERR) {
+                               evts->global |= KOMEDA_ERR_MERR;
+                               malidp_write32_mask(d71->gcu_addr, BLK_STATUS,
+                                                   GCU_STATUS_MERR, 0);
+                       }
+               }
+
+               malidp_write32(d71->gcu_addr, BLK_IRQ_CLEAR, raw_status);
+       }
+
+       if (gcu_status & GLB_IRQ_STATUS_PIPE0)
+               evts->pipes[0] |= get_pipeline_event(d71->pipes[0], gcu_status);
+
+       if (gcu_status & GLB_IRQ_STATUS_PIPE1)
+               evts->pipes[1] |= get_pipeline_event(d71->pipes[1], gcu_status);
+
+       return gcu_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+#define ENABLED_GCU_IRQS       (GCU_IRQ_CVAL0 | GCU_IRQ_CVAL1 | \
+                                GCU_IRQ_MODE | GCU_IRQ_ERR)
+#define ENABLED_LPU_IRQS       (LPU_IRQ_IBSY | LPU_IRQ_ERR | LPU_IRQ_EOW)
+#define ENABLED_CU_IRQS                (CU_IRQ_OVR | CU_IRQ_ERR)
+#define ENABLED_DOU_IRQS       (DOU_IRQ_UND | DOU_IRQ_ERR)
+
+static int d71_enable_irq(struct komeda_dev *mdev)
+{
+       struct d71_dev *d71 = mdev->chip_data;
+       struct d71_pipeline *pipe;
+       u32 i;
+
+       malidp_write32_mask(d71->gcu_addr, BLK_IRQ_MASK,
+                           ENABLED_GCU_IRQS, ENABLED_GCU_IRQS);
+       for (i = 0; i < d71->num_pipelines; i++) {
+               pipe = d71->pipes[i];
+               malidp_write32_mask(pipe->cu_addr,  BLK_IRQ_MASK,
+                                   ENABLED_CU_IRQS, ENABLED_CU_IRQS);
+               malidp_write32_mask(pipe->lpu_addr, BLK_IRQ_MASK,
+                                   ENABLED_LPU_IRQS, ENABLED_LPU_IRQS);
+               malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK,
+                                   ENABLED_DOU_IRQS, ENABLED_DOU_IRQS);
+       }
+       return 0;
+}
+
+static int d71_disable_irq(struct komeda_dev *mdev)
+{
+       struct d71_dev *d71 = mdev->chip_data;
+       struct d71_pipeline *pipe;
+       u32 i;
+
+       malidp_write32_mask(d71->gcu_addr, BLK_IRQ_MASK, ENABLED_GCU_IRQS, 0);
+       for (i = 0; i < d71->num_pipelines; i++) {
+               pipe = d71->pipes[i];
+               malidp_write32_mask(pipe->cu_addr,  BLK_IRQ_MASK,
+                                   ENABLED_CU_IRQS, 0);
+               malidp_write32_mask(pipe->lpu_addr, BLK_IRQ_MASK,
+                                   ENABLED_LPU_IRQS, 0);
+               malidp_write32_mask(pipe->dou_addr, BLK_IRQ_MASK,
+                                   ENABLED_DOU_IRQS, 0);
+       }
+       return 0;
+}
+
 static int d71_reset(struct d71_dev *d71)
 {
        u32 __iomem *gcu = d71->gcu_addr;
@@ -222,6 +456,9 @@ static struct komeda_dev_funcs d71_chip_funcs = {
        .init_format_table = d71_init_fmt_tbl,
        .enum_resources = d71_enum_resources,
        .cleanup        = d71_cleanup,
+       .irq_handler    = d71_irq_handler,
+       .enable_irq     = d71_enable_irq,
+       .disable_irq    = d71_disable_irq,
 };
 
 struct komeda_dev_funcs *
index 3ca5718aa0c29c9abe409f26ef4d0bbd687ec98b..f88a14927be91460d4b81cf69b5fd08882de5ebc 100644 (file)
 #include "komeda_dev.h"
 #include "komeda_kms.h"
 
+void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
+                             struct komeda_events *evts)
+{
+       struct drm_crtc *crtc = &kcrtc->base;
+       u32 events = evts->pipes[kcrtc->master->id];
+
+       if (events & KOMEDA_EVENT_VSYNC)
+               drm_crtc_handle_vblank(crtc);
+
+       /* will handle it together with the write back support */
+       if (events & KOMEDA_EVENT_EOW)
+               DRM_DEBUG("EOW.\n");
+
+       /* will handle it with crtc->flush */
+       if (events & KOMEDA_EVENT_FLIP)
+               DRM_DEBUG("FLIP Done.\n");
+}
+
 struct drm_crtc_helper_funcs komeda_crtc_helper_funcs = {
 };
 
index 780ca86c9db9d21594d297ae085207deba8b5884..cf65bf5331d9f8ff4d349def7a092aac52a9f366 100644 (file)
@@ -53,6 +53,7 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
 
 static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct device_node *child, *np = dev->of_node;
        struct clk *clk;
        int ret;
@@ -62,6 +63,11 @@ static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
                return PTR_ERR(clk);
 
        mdev->mclk = clk;
+       mdev->irq  = platform_get_irq(pdev, 0);
+       if (mdev->irq < 0) {
+               DRM_ERROR("could not get IRQ number.\n");
+               return mdev->irq;
+       }
 
        for_each_available_child_of_node(np, child) {
                if (of_node_cmp(child->name, "pipeline") == 0) {
index 0f77dead6a2375030e078b07bc7b0711e03975dd..681fe022bd2209b81db37d708bebf1d895d338fe 100644 (file)
 #include "malidp_product.h"
 #include "komeda_format_caps.h"
 
+#define KOMEDA_EVENT_VSYNC             BIT_ULL(0)
+#define KOMEDA_EVENT_FLIP              BIT_ULL(1)
+#define KOMEDA_EVENT_URUN              BIT_ULL(2)
+#define KOMEDA_EVENT_IBSY              BIT_ULL(3)
+#define KOMEDA_EVENT_OVR               BIT_ULL(4)
+#define KOMEDA_EVENT_EOW               BIT_ULL(5)
+#define KOMEDA_EVENT_MODE              BIT_ULL(6)
+
+#define KOMEDA_ERR_TETO                        BIT_ULL(14)
+#define KOMEDA_ERR_TEMR                        BIT_ULL(15)
+#define KOMEDA_ERR_TITR                        BIT_ULL(16)
+#define KOMEDA_ERR_CPE                 BIT_ULL(17)
+#define KOMEDA_ERR_CFGE                        BIT_ULL(18)
+#define KOMEDA_ERR_AXIE                        BIT_ULL(19)
+#define KOMEDA_ERR_ACE0                        BIT_ULL(20)
+#define KOMEDA_ERR_ACE1                        BIT_ULL(21)
+#define KOMEDA_ERR_ACE2                        BIT_ULL(22)
+#define KOMEDA_ERR_ACE3                        BIT_ULL(23)
+#define KOMEDA_ERR_DRIFTTO             BIT_ULL(24)
+#define KOMEDA_ERR_FRAMETO             BIT_ULL(25)
+#define KOMEDA_ERR_CSCE                        BIT_ULL(26)
+#define KOMEDA_ERR_ZME                 BIT_ULL(27)
+#define KOMEDA_ERR_MERR                        BIT_ULL(28)
+#define KOMEDA_ERR_TCF                 BIT_ULL(29)
+#define KOMEDA_ERR_TTNG                        BIT_ULL(30)
+#define KOMEDA_ERR_TTF                 BIT_ULL(31)
+
 /* malidp device id */
 enum {
        MALI_D71 = 0,
@@ -39,6 +66,11 @@ struct komeda_product_data {
 
 struct komeda_dev;
 
+struct komeda_events {
+       u64 global;
+       u64 pipes[KOMEDA_MAX_PIPELINES];
+};
+
 /**
  * struct komeda_dev_funcs
  *
@@ -60,6 +92,17 @@ struct komeda_dev_funcs {
        int (*enum_resources)(struct komeda_dev *mdev);
        /** @cleanup: call to chip to cleanup komeda_dev->chip data */
        void (*cleanup)(struct komeda_dev *mdev);
+       /**
+        * @irq_handler:
+        *
+        * for CORE to get the HW event from the CHIP when interrupt happened.
+        */
+       irqreturn_t (*irq_handler)(struct komeda_dev *mdev,
+                                  struct komeda_events *events);
+       /** @enable_irq: enable irq */
+       int (*enable_irq)(struct komeda_dev *mdev);
+       /** @disable_irq: disable irq */
+       int (*disable_irq)(struct komeda_dev *mdev);
 };
 
 /**
@@ -81,6 +124,9 @@ struct komeda_dev {
        /** @mck: HW main engine clk */
        struct clk *mclk;
 
+       /** @irq: irq number */
+       int irq;
+
        int n_pipelines;
        struct komeda_pipeline *pipelines[KOMEDA_MAX_PIPELINES];
 
index 47a58ab20434e63b575820bd864045326784f5dc..b214edbfbbc6774e5ed60f1baa2c49b01b4e5dd1 100644 (file)
@@ -13,6 +13,7 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_irq.h>
 #include <drm/drm_vblank.h>
 
 #include "komeda_dev.h"
@@ -33,10 +34,31 @@ static int komeda_gem_cma_dumb_create(struct drm_file *file,
        return drm_gem_cma_dumb_create_internal(file, dev, args);
 }
 
+static irqreturn_t komeda_kms_irq_handler(int irq, void *data)
+{
+       struct drm_device *drm = data;
+       struct komeda_dev *mdev = drm->dev_private;
+       struct komeda_kms_dev *kms = to_kdev(drm);
+       struct komeda_events evts;
+       irqreturn_t status;
+       u32 i;
+
+       /* Call into the CHIP to recognize events */
+       memset(&evts, 0, sizeof(evts));
+       status = mdev->funcs->irq_handler(mdev, &evts);
+
+       /* Notify the crtc to handle the events */
+       for (i = 0; i < kms->n_crtcs; i++)
+               komeda_crtc_handle_event(&kms->crtcs[i], &evts);
+
+       return status;
+}
+
 static struct drm_driver komeda_kms_driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
-                          DRIVER_PRIME,
+                          DRIVER_PRIME | DRIVER_HAVE_IRQ,
        .lastclose                      = drm_fb_helper_lastclose,
+       .irq_handler                    = komeda_kms_irq_handler,
        .gem_free_object_unlocked       = drm_gem_cma_free_object,
        .gem_vm_ops                     = &drm_gem_cma_vm_ops,
        .dumb_create                    = komeda_gem_cma_dumb_create,
@@ -144,12 +166,22 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
 
        drm_mode_config_reset(drm);
 
-       err = drm_dev_register(drm, 0);
+       err = drm_irq_install(drm, mdev->irq);
        if (err)
                goto cleanup_mode_config;
 
+       err = mdev->funcs->enable_irq(mdev);
+       if (err)
+               goto uninstall_irq;
+
+       err = drm_dev_register(drm, 0);
+       if (err)
+               goto uninstall_irq;
+
        return kms;
 
+uninstall_irq:
+       drm_irq_uninstall(drm);
 cleanup_mode_config:
        drm_mode_config_cleanup(drm);
 free_kms:
@@ -162,7 +194,9 @@ void komeda_kms_detach(struct komeda_kms_dev *kms)
        struct drm_device *drm = &kms->base;
        struct komeda_dev *mdev = drm->dev_private;
 
+       mdev->funcs->disable_irq(mdev);
        drm_dev_unregister(drm);
+       drm_irq_uninstall(drm);
        component_unbind_all(mdev->dev, drm);
        komeda_kms_cleanup_private_objs(mdev);
        drm_mode_config_cleanup(drm);
index 1e0c5ff650c0144ab106f4ba589bf51a404a1c6d..15ac8b85506ce475c25db5b92744ff9a8acee3e5 100644 (file)
@@ -110,6 +110,9 @@ int komeda_kms_add_private_objs(struct komeda_kms_dev *kms,
                                struct komeda_dev *mdev);
 void komeda_kms_cleanup_private_objs(struct komeda_dev *mdev);
 
+void komeda_crtc_handle_event(struct komeda_crtc   *kcrtc,
+                             struct komeda_events *evts);
+
 struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev);
 void komeda_kms_detach(struct komeda_kms_dev *kms);