#include <linux/of_graph.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/pm_runtime.h>
+#include <linux/debugfs.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
        return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
 }
 
+#ifdef CONFIG_DEBUG_FS
+
+static void malidp_error_stats_init(struct malidp_error_stats *error_stats)
+{
+       error_stats->num_errors = 0;
+       error_stats->last_error_status = 0;
+       error_stats->last_error_vblank = -1;
+}
+
+void malidp_error(struct malidp_drm *malidp,
+                 struct malidp_error_stats *error_stats, u32 status,
+                 u64 vblank)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&malidp->errors_lock, irqflags);
+       error_stats->last_error_status = status;
+       error_stats->last_error_vblank = vblank;
+       error_stats->num_errors++;
+       spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
+}
+
+void malidp_error_stats_dump(const char *prefix,
+                            struct malidp_error_stats error_stats,
+                            struct seq_file *m)
+{
+       seq_printf(m, "[%s] num_errors : %d\n", prefix,
+                  error_stats.num_errors);
+       seq_printf(m, "[%s] last_error_status  : 0x%08x\n", prefix,
+                  error_stats.last_error_status);
+       seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix,
+                  error_stats.last_error_vblank);
+}
+
+static int malidp_show_stats(struct seq_file *m, void *arg)
+{
+       struct drm_device *drm = m->private;
+       struct malidp_drm *malidp = drm->dev_private;
+       unsigned long irqflags;
+       struct malidp_error_stats de_errors, se_errors;
+
+       spin_lock_irqsave(&malidp->errors_lock, irqflags);
+       de_errors = malidp->de_errors;
+       se_errors = malidp->se_errors;
+       spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
+       malidp_error_stats_dump("DE", de_errors, m);
+       malidp_error_stats_dump("SE", se_errors, m);
+       return 0;
+}
+
+static int malidp_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, malidp_show_stats, inode->i_private);
+}
+
+static ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf,
+                                   size_t len, loff_t *offp)
+{
+       struct seq_file *m = file->private_data;
+       struct drm_device *drm = m->private;
+       struct malidp_drm *malidp = drm->dev_private;
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&malidp->errors_lock, irqflags);
+       malidp_error_stats_init(&malidp->de_errors);
+       malidp_error_stats_init(&malidp->se_errors);
+       spin_unlock_irqrestore(&malidp->errors_lock, irqflags);
+       return len;
+}
+
+static const struct file_operations malidp_debugfs_fops = {
+       .owner = THIS_MODULE,
+       .open = malidp_debugfs_open,
+       .read = seq_read,
+       .write = malidp_debugfs_write,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int malidp_debugfs_init(struct drm_minor *minor)
+{
+       struct malidp_drm *malidp = minor->dev->dev_private;
+       struct dentry *dentry = NULL;
+
+       malidp_error_stats_init(&malidp->de_errors);
+       malidp_error_stats_init(&malidp->se_errors);
+       spin_lock_init(&malidp->errors_lock);
+       dentry = debugfs_create_file("debug",
+                                    S_IRUGO | S_IWUSR,
+                                    minor->debugfs_root, minor->dev,
+                                    &malidp_debugfs_fops);
+       if (!dentry) {
+               DRM_ERROR("Cannot create debug file\n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+#endif //CONFIG_DEBUG_FS
+
 static struct drm_driver malidp_driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
                           DRIVER_PRIME,
        .gem_prime_vmap = drm_gem_cma_prime_vmap,
        .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
        .gem_prime_mmap = drm_gem_cma_prime_mmap,
+#ifdef CONFIG_DEBUG_FS
+       .debugfs_init = malidp_debugfs_init,
+#endif
        .fops = &fops,
        .name = "mali-dp",
        .desc = "ARM Mali Display Processor driver",
 
 #include <drm/drm_encoder.h>
 #include <linux/mutex.h>
 #include <linux/wait.h>
+#include <linux/spinlock.h>
 #include <drm/drmP.h>
 #include "malidp_hw.h"
 
 #define MALIDP_CONFIG_VALID_DONE       1
 #define MALIDP_CONFIG_START            0xd0
 
+struct malidp_error_stats {
+       s32 num_errors;
+       u32 last_error_status;
+       s64 last_error_vblank;
+};
+
 struct malidp_drm {
        struct malidp_hw_device *dev;
        struct drm_crtc crtc;
        struct drm_pending_vblank_event *event;
        atomic_t config_valid;
        u32 core_id;
+#ifdef CONFIG_DEBUG_FS
+       struct malidp_error_stats de_errors;
+       struct malidp_error_stats se_errors;
+       /* Protects errors stats */
+       spinlock_t errors_lock;
+#endif
 };
 
 #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc)
 int malidp_de_planes_init(struct drm_device *drm);
 int malidp_crtc_init(struct drm_device *drm);
 
+#ifdef CONFIG_DEBUG_FS
+void malidp_error(struct malidp_drm *malidp,
+                 struct malidp_error_stats *error_stats, u32 status,
+                 u64 vblank);
+#endif
+
 /* often used combination of rotational bits */
 #define MALIDP_ROTATED_MASK    (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)
 
 
                                            MALIDP500_DE_IRQ_VSYNC |
                                            MALIDP500_DE_IRQ_GLOBAL,
                                .vsync_irq = MALIDP500_DE_IRQ_VSYNC,
+                               .err_mask = MALIDP_DE_IRQ_UNDERRUN |
+                                           MALIDP500_DE_IRQ_AXI_ERR |
+                                           MALIDP500_DE_IRQ_SATURATION,
                        },
                        .se_irq_map = {
                                .irq_mask = MALIDP500_SE_IRQ_CONF_MODE |
                                            MALIDP500_SE_IRQ_CONF_VALID |
                                            MALIDP500_SE_IRQ_GLOBAL,
                                .vsync_irq = MALIDP500_SE_IRQ_CONF_VALID,
+                               .err_mask = MALIDP500_SE_IRQ_INIT_BUSY |
+                                           MALIDP500_SE_IRQ_AXI_ERROR |
+                                           MALIDP500_SE_IRQ_OVERRUN,
                        },
                        .dc_irq_map = {
                                .irq_mask = MALIDP500_DE_IRQ_CONF_VALID,
                                .irq_mask = MALIDP_DE_IRQ_UNDERRUN |
                                            MALIDP550_DE_IRQ_VSYNC,
                                .vsync_irq = MALIDP550_DE_IRQ_VSYNC,
+                               .err_mask = MALIDP_DE_IRQ_UNDERRUN |
+                                           MALIDP550_DE_IRQ_SATURATION |
+                                           MALIDP550_DE_IRQ_AXI_ERR,
                        },
                        .se_irq_map = {
-                               .irq_mask = MALIDP550_SE_IRQ_EOW |
-                                           MALIDP550_SE_IRQ_AXI_ERR,
+                               .irq_mask = MALIDP550_SE_IRQ_EOW,
                                .vsync_irq = MALIDP550_SE_IRQ_EOW,
+                               .err_mask  = MALIDP550_SE_IRQ_AXI_ERR |
+                                            MALIDP550_SE_IRQ_OVR |
+                                            MALIDP550_SE_IRQ_IBSY,
                        },
                        .dc_irq_map = {
                                .irq_mask = MALIDP550_DC_IRQ_CONF_VALID |
                                            MALIDP650_DE_IRQ_DRIFT |
                                            MALIDP550_DE_IRQ_VSYNC,
                                .vsync_irq = MALIDP550_DE_IRQ_VSYNC,
+                               .err_mask = MALIDP_DE_IRQ_UNDERRUN |
+                                           MALIDP650_DE_IRQ_DRIFT |
+                                           MALIDP550_DE_IRQ_SATURATION |
+                                           MALIDP550_DE_IRQ_AXI_ERR |
+                                           MALIDP650_DE_IRQ_ACEV1 |
+                                           MALIDP650_DE_IRQ_ACEV2 |
+                                           MALIDP650_DE_IRQ_ACEG |
+                                           MALIDP650_DE_IRQ_AXIEP,
                        },
                        .se_irq_map = {
-                               .irq_mask = MALIDP550_SE_IRQ_EOW |
-                                           MALIDP550_SE_IRQ_AXI_ERR,
+                               .irq_mask = MALIDP550_SE_IRQ_EOW,
                                .vsync_irq = MALIDP550_SE_IRQ_EOW,
+                               .err_mask = MALIDP550_SE_IRQ_AXI_ERR |
+                                           MALIDP550_SE_IRQ_OVR |
+                                           MALIDP550_SE_IRQ_IBSY,
                        },
                        .dc_irq_map = {
                                .irq_mask = MALIDP550_DC_IRQ_CONF_VALID |
                return ret;
 
        mask = malidp_hw_read(hwdev, MALIDP_REG_MASKIRQ);
-       status &= mask;
+       /* keep the status of the enabled interrupts, plus the error bits */
+       status &= (mask | de->err_mask);
        if ((status & de->vsync_irq) && malidp->crtc.enabled)
                drm_crtc_handle_vblank(&malidp->crtc);
 
+#ifdef CONFIG_DEBUG_FS
+       if (status & de->err_mask) {
+               malidp_error(malidp, &malidp->de_errors, status,
+                            drm_crtc_vblank_count(&malidp->crtc));
+       }
+#endif
        malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, status);
 
        return (ret == IRQ_NONE) ? IRQ_HANDLED : ret;
                return IRQ_NONE;
 
        status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS);
-       if (!(status & se->irq_mask))
+       if (!(status & (se->irq_mask | se->err_mask)))
                return IRQ_NONE;
 
+#ifdef CONFIG_DEBUG_FS
+       if (status & se->err_mask)
+               malidp_error(malidp, &malidp->se_errors, status,
+                            drm_crtc_vblank_count(&malidp->crtc));
+#endif
        mask = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_MASKIRQ);
-       status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS);
        status &= mask;
 
        if (status & se->vsync_irq) {
 
 struct malidp_irq_map {
        u32 irq_mask;           /* mask of IRQs that can be enabled in the block */
        u32 vsync_irq;          /* IRQ bit used for signaling during VSYNC */
+       u32 err_mask;           /* mask of bits that represent errors */
 };
 
 struct malidp_layer {
 
 #define MALIDP550_DE_IRQ_AXI_ERR               (1 << 16)
 #define MALIDP550_SE_IRQ_EOW                   (1 << 0)
 #define MALIDP550_SE_IRQ_AXI_ERR               (1 << 16)
+#define MALIDP550_SE_IRQ_OVR                   (1 << 17)
+#define MALIDP550_SE_IRQ_IBSY                  (1 << 18)
 #define MALIDP550_DC_IRQ_CONF_VALID            (1 << 0)
 #define MALIDP550_DC_IRQ_CONF_MODE             (1 << 4)
 #define MALIDP550_DC_IRQ_CONF_ACTIVE           (1 << 16)
 #define MALIDP550_DC_IRQ_SE                    (1 << 24)
 
 #define MALIDP650_DE_IRQ_DRIFT                 (1 << 4)
+#define MALIDP650_DE_IRQ_ACEV1                 (1 << 17)
+#define MALIDP650_DE_IRQ_ACEV2                 (1 << 18)
+#define MALIDP650_DE_IRQ_ACEG                  (1 << 19)
+#define MALIDP650_DE_IRQ_AXIEP                 (1 << 28)
 
 /* bit masks that are common between products */
 #define   MALIDP_CFG_VALID             (1 << 0)