etnaviv_gpu.o \
        etnaviv_iommu_v2.o \
        etnaviv_iommu.o \
-       etnaviv_mmu.o
+       etnaviv_mmu.o \
+       etnaviv_perfmon.o
 
 obj-$(CONFIG_DRM_ETNAVIV)      += etnaviv.o
 
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_mmu.h"
+#include "etnaviv_perfmon.h"
 
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
 static bool reglog;
        return ret;
 }
 
+static int etnaviv_ioctl_pm_query_dom(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct etnaviv_drm_private *priv = dev->dev_private;
+       struct drm_etnaviv_pm_domain *args = data;
+       struct etnaviv_gpu *gpu;
+
+       if (args->pipe >= ETNA_MAX_PIPES)
+               return -EINVAL;
+
+       gpu = priv->gpu[args->pipe];
+       if (!gpu)
+               return -ENXIO;
+
+       return etnaviv_pm_query_dom(gpu, args);
+}
+
+static int etnaviv_ioctl_pm_query_sig(struct drm_device *dev, void *data,
+       struct drm_file *file)
+{
+       struct etnaviv_drm_private *priv = dev->dev_private;
+       struct drm_etnaviv_pm_signal *args = data;
+       struct etnaviv_gpu *gpu;
+
+       if (args->pipe >= ETNA_MAX_PIPES)
+               return -EINVAL;
+
+       gpu = priv->gpu[args->pipe];
+       if (!gpu)
+               return -ENXIO;
+
+       return etnaviv_pm_query_sig(gpu, args);
+}
+
 static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 #define ETNA_IOCTL(n, func, flags) \
        DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
        ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_AUTH|DRM_RENDER_ALLOW),
        ETNA_IOCTL(GEM_USERPTR,  gem_userptr,  DRM_AUTH|DRM_RENDER_ALLOW),
        ETNA_IOCTL(GEM_WAIT,     gem_wait,     DRM_AUTH|DRM_RENDER_ALLOW),
+       ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_AUTH|DRM_RENDER_ALLOW),
+       ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
 static const struct vm_operations_struct vm_ops = {
 
--- /dev/null
+/*
+ * Copyright (C) 2017 Etnaviv Project
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "etnaviv_gpu.h"
+
+struct etnaviv_pm_domain;
+
+struct etnaviv_pm_signal {
+       char name[64];
+       u32 data;
+
+       u32 (*sample)(struct etnaviv_gpu *gpu,
+                     const struct etnaviv_pm_domain *domain,
+                     const struct etnaviv_pm_signal *signal);
+};
+
+struct etnaviv_pm_domain {
+       char name[64];
+       u8 nr_signals;
+       const struct etnaviv_pm_signal *signal;
+};
+
+struct etnaviv_pm_domain_meta {
+       const struct etnaviv_pm_domain *domains;
+       u32 nr_domains;
+};
+
+static const struct etnaviv_pm_domain doms_3d[] = {
+};
+
+static const struct etnaviv_pm_domain doms_2d[] = {
+};
+
+static const struct etnaviv_pm_domain doms_vg[] = {
+};
+
+static const struct etnaviv_pm_domain_meta doms_meta[] = {
+       {
+               .nr_domains = ARRAY_SIZE(doms_3d),
+               .domains = &doms_3d[0]
+       },
+       {
+               .nr_domains = ARRAY_SIZE(doms_2d),
+               .domains = &doms_2d[0]
+       },
+       {
+               .nr_domains = ARRAY_SIZE(doms_vg),
+               .domains = &doms_vg[0]
+       }
+};
+
+int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
+       struct drm_etnaviv_pm_domain *domain)
+{
+       const struct etnaviv_pm_domain_meta *meta = &doms_meta[domain->pipe];
+       const struct etnaviv_pm_domain *dom;
+
+       if (domain->iter >= meta->nr_domains)
+               return -EINVAL;
+
+       dom = meta->domains + domain->iter;
+
+       domain->id = domain->iter;
+       domain->nr_signals = dom->nr_signals;
+       strncpy(domain->name, dom->name, sizeof(domain->name));
+
+       domain->iter++;
+       if (domain->iter == meta->nr_domains)
+               domain->iter = 0xff;
+
+       return 0;
+}
+
+int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
+       struct drm_etnaviv_pm_signal *signal)
+{
+       const struct etnaviv_pm_domain_meta *meta = &doms_meta[signal->pipe];
+       const struct etnaviv_pm_domain *dom;
+       const struct etnaviv_pm_signal *sig;
+
+       if (signal->domain >= meta->nr_domains)
+               return -EINVAL;
+
+       dom = meta->domains + signal->domain;
+
+       if (signal->iter > dom->nr_signals)
+               return -EINVAL;
+
+       sig = &dom->signal[signal->iter];
+
+       signal->id = signal->iter;
+       strncpy(signal->name, sig->name, sizeof(signal->name));
+
+       signal->iter++;
+       if (signal->iter == dom->nr_signals)
+               signal->iter = 0xffff;
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright (C) 2017 Etnaviv Project
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ETNAVIV_PERFMON_H__
+#define __ETNAVIV_PERFMON_H__
+
+struct etnaviv_gpu;
+struct drm_etnaviv_pm_domain;
+struct drm_etnaviv_pm_signal;
+
+int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
+       struct drm_etnaviv_pm_domain *domain);
+
+int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
+       struct drm_etnaviv_pm_signal *signal);
+
+#endif /* __ETNAVIV_PERFMON_H__ */
 
        struct drm_etnaviv_timespec timeout;    /* in */
 };
 
+/*
+ * Performance Monitor (PM):
+ */
+
+struct drm_etnaviv_pm_domain {
+       __u32 pipe;       /* in */
+       __u8  iter;       /* in/out, select pm domain at index iter */
+       __u8  id;         /* out, id of domain */
+       __u16 nr_signals; /* out, how many signals does this domain provide */
+       char  name[64];   /* out, name of domain */
+};
+
+struct drm_etnaviv_pm_signal {
+       __u32 pipe;       /* in */
+       __u8  domain;     /* in, pm domain index */
+       __u8  pad;
+       __u16 iter;       /* in/out, select pm source at index iter */
+       __u16 id;         /* out, id of signal */
+       char  name[64];   /* out, name of domain */
+};
+
 #define DRM_ETNAVIV_GET_PARAM          0x00
 /* placeholder:
 #define DRM_ETNAVIV_SET_PARAM          0x01
 #define DRM_ETNAVIV_WAIT_FENCE         0x07
 #define DRM_ETNAVIV_GEM_USERPTR        0x08
 #define DRM_ETNAVIV_GEM_WAIT           0x09
-#define DRM_ETNAVIV_NUM_IOCTLS         0x0a
+#define DRM_ETNAVIV_PM_QUERY_DOM       0x0a
+#define DRM_ETNAVIV_PM_QUERY_SIG       0x0b
+#define DRM_ETNAVIV_NUM_IOCTLS         0x0c
 
 #define DRM_IOCTL_ETNAVIV_GET_PARAM    DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param)
 #define DRM_IOCTL_ETNAVIV_GEM_NEW      DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new)
 #define DRM_IOCTL_ETNAVIV_WAIT_FENCE   DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
 #define DRM_IOCTL_ETNAVIV_GEM_USERPTR  DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr)
 #define DRM_IOCTL_ETNAVIV_GEM_WAIT     DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait)
+#define DRM_IOCTL_ETNAVIV_PM_QUERY_DOM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_DOM, struct drm_etnaviv_pm_domain)
+#define DRM_IOCTL_ETNAVIV_PM_QUERY_SIG DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_SIG, struct drm_etnaviv_pm_signal)
 
 #if defined(__cplusplus)
 }