Subject: Re: [PATCH] misc: add bmm_dmabuf allocator

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

From: Russell King <[email-masked]>

Add a dma_buf allocator to allow userspace to allocate and map dma_bufs,
and share them with other subsystems for their use.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/misc/Kconfig      |   8 +
 drivers/misc/Makefile     |   1 +
 drivers/misc/bmm_dmabuf.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/bmm_dmabuf.h |  52 +++++++
 4 files changed, 425 insertions(+)
 create mode 100644 drivers/misc/bmm_dmabuf.c
 create mode 100644 drivers/misc/bmm_dmabuf.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8dacd4c9ee87..58abc5686ee7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -450,6 +450,14 @@ config ARM_CHARLCD
 	  line and the Linux version on the second line, but that's
 	  still useful.
 
+config BMM_DMABUF
+	tristate "CMA backed dma_buf allocator"
+	depends on ARCH_DOVE
+	help
+	  This is a simple allocator of dma_bufs for userspace.  This
+	  allows userspace to allocate and map dma_bufs, and pass them
+	  into other subsystems.
+
 config BMP085
 	bool
 	depends on SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c235d5b68311..89c15f81e59d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_INTEL_MID_PTI)	+= pti.o
 obj-$(CONFIG_ATMEL_PWM)		+= atmel_pwm.o
 obj-$(CONFIG_ATMEL_SSC)		+= atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o
+obj-$(CONFIG_BMM_DMABUF)	+= bmm_dmabuf.o
 obj-$(CONFIG_BMP085)		+= bmp085.o
 obj-$(CONFIG_BMP085_I2C)	+= bmp085-i2c.o
 obj-$(CONFIG_BMP085_SPI)	+= bmp085-spi.o
diff --git a/drivers/misc/bmm_dmabuf.c b/drivers/misc/bmm_dmabuf.c
new file mode 100644
index 000000000000..abe204783dcb
--- /dev/null
+++ b/drivers/misc/bmm_dmabuf.c
@@ -0,0 +1,364 @@
+/*
+ *  bmm_dmabuf.c
+ *
+ *  CMA-backed dma_buf allocator
+ *
+ *  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.
+ */
+#undef DEBUG
+
+#include <linux/debugfs.h>
+#include <linux/dma-buf.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <asm/page.h>
+
+#define BMM_MINOR		94
+
+#include "bmm_dmabuf.h"
+
+struct bmm_block {
+	struct dma_attrs attrs;
+	void *cpu;
+	size_t size;
+	dma_addr_t paddr;
+
+	struct list_head list;
+	struct dma_buf *dmabuf;
+};
+
+static LIST_HEAD(bmm_used_blocks);
+static DEFINE_MUTEX(bmm_mutex);	/* mutex for block list */
+
+static void bmm_insert_ordered(struct bmm_block *new, struct list_head *head)
+{
+	struct bmm_block *p;
+
+	if (list_empty(head)) {
+		list_add(&new->list, head);
+		return;
+	}
+
+	list_for_each_entry(p, head, list)
+		if (p->paddr > new->paddr)
+			break;
+
+	list_add_tail(&new->list, &p->list);
+}
+
+static struct bmm_block *__bmm_malloc(size_t size, unsigned long attr,
+	unsigned align)
+{
+	struct bmm_block *new;
+
+	size = ALIGN(size, align);
+	if (size == 0 || size > TASK_SIZE)
+		return NULL;
+
+	new = kmalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	init_dma_attrs(&new->attrs);
+
+	dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &new->attrs);
+	dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &new->attrs);
+	if (attr & BMM_ATTR_WRITECOMBINE)
+		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &new->attrs);
+
+	new->size = size;
+	new->cpu = dma_alloc_attrs(NULL, size, &new->paddr,
+				   GFP_KERNEL | __GFP_HIGHMEM,
+				   &new->attrs);
+	if (!new->cpu) {
+		kfree(new);
+		return NULL;
+	}
+
+	mutex_lock(&bmm_mutex);
+	bmm_insert_ordered(new, &bmm_used_blocks);
+	mutex_unlock(&bmm_mutex);
+
+	return new;
+}
+
+static void __bmm_free(struct bmm_block *bmm)
+{
+	mutex_lock(&bmm_mutex);
+	list_del(&bmm->list);
+	mutex_unlock(&bmm_mutex);
+
+	dma_free_attrs(NULL, bmm->size, bmm->cpu, bmm->paddr, &bmm->attrs);
+	kfree(bmm);
+}
+
+static struct sg_table *
+bmm_dmabuf_map(struct dma_buf_attachment *attach, enum dma_data_direction dir)
+{
+	struct bmm_block *bmm = attach->dmabuf->priv;
+	struct sg_table *sgt;
+
+	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+	if (sgt) {
+		if (sg_alloc_table(sgt, 1, GFP_KERNEL))
+			goto err_free_sgt;
+
+		sg_dma_address(sgt->sgl) = bmm->paddr;
+		sg_dma_len(sgt->sgl) = bmm->size;
+	}
+	return sgt;
+
+ err_free_sgt:
+	kfree(sgt);
+	return NULL;
+}
+
+static void
+bmm_dmabuf_unmap(struct dma_buf_attachment *attach, struct sg_table *sgt,
+	enum dma_data_direction dir)
+{
+	sg_free_table(sgt);
+	kfree(sgt);
+}
+
+static void bmm_dmabuf_release(struct dma_buf *buf)
+{
+	struct bmm_block *bmm = buf->priv;
+
+	bmm->dmabuf = NULL;
+	__bmm_free(bmm);
+}
+
+static void *bmm_no_kmap(struct dma_buf *buf, unsigned long arg)
+{
+	return NULL;
+}
+
+static void bmm_no_kunmap(struct dma_buf *buf, unsigned long arg, void *addr)
+{
+}
+
+static int bmm_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+	struct bmm_block *bmm = buf->priv;
+
+	return dma_mmap_attrs(NULL, vma, bmm->cpu, bmm->paddr, bmm->size, &bmm->attrs);
+}
+
+static const struct dma_buf_ops bmm_dmabuf_ops = {
+	.map_dma_buf	= bmm_dmabuf_map,
+	.unmap_dma_buf	= bmm_dmabuf_unmap,
+	.release	= bmm_dmabuf_release,
+	.kmap_atomic	= bmm_no_kmap,
+	.kunmap_atomic	= bmm_no_kunmap,
+	.kmap		= bmm_no_kmap,
+	.kunmap		= bmm_no_kunmap,
+	.mmap		= bmm_dmabuf_mmap,
+};
+
+static int bmm_dmabuf_alloc(struct bmm_fd_alloc *alloc)
+{
+	struct bmm_block *bmm;
+	struct dma_buf *buf;
+	int ret;
+
+	if (alloc->align > PAGE_SIZE)
+		return -EINVAL;
+
+	bmm = __bmm_malloc(alloc->size, alloc->attr, PAGE_SIZE);
+	if (!bmm)
+		return -ENOMEM;
+
+	buf = dma_buf_export(bmm, &bmm_dmabuf_ops, bmm->size, O_RDWR);
+	if (IS_ERR(buf)) {
+		__bmm_free(bmm);
+		return PTR_ERR(buf);
+	}
+
+	bmm->dmabuf = buf;
+
+	/* Consume the refcount on the bmm */
+	ret = dma_buf_fd(buf, O_CLOEXEC);
+	if (ret >= 0)
+		alloc->fd = ret;
+
+	return ret < 0 ? ret : 0;
+}
+
+static struct dma_buf *bmm_block_get(int fd)
+{
+	struct dma_buf *buf = dma_buf_get(fd);
+
+	if (!IS_ERR(buf) && buf->ops != &bmm_dmabuf_ops) {
+		dma_buf_put(buf);
+		buf = ERR_PTR(-EINVAL);
+	}
+	return buf;
+}
+
+static int bmm_dmabuf_flush(struct bmm_fd_flush *flush)
+{
+	struct dma_buf *buf = bmm_block_get(flush->fd);
+	struct bmm_block *bmm;
+
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	bmm = buf->priv;
+
+	dma_buf_put(buf);
+
+	return -EINVAL;
+}
+
+union ioctl_arg {
+	struct bmm_fd_alloc alloc;
+	struct bmm_fd_flush flush;
+};
+
+static long bmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	void __user *u_io = (void __user *)arg;
+	union ioctl_arg io;
+	int ret;
+
+	if (_IOC_SIZE(cmd) > sizeof(io))
+		return -EINVAL;
+
+	memset(&io, 0, sizeof(io));
+	if (cmd & IOC_IN && copy_from_user(&io, u_io, _IOC_SIZE(cmd))) {
+		pr_debug("bmm_ioctl() error in copy_from_user()\n");
+		return -EFAULT;
+	}
+
+	pr_debug("bmm_ioctl(cmd=0x%08x, arg=0x%08lx)\n", cmd, arg);
+
+	switch (cmd) {
+	case BMM_FD_ALLOC:
+		ret = bmm_dmabuf_alloc(&io.alloc);
+		break;
+
+	case BMM_FD_FLUSH:
+		ret = bmm_dmabuf_flush(&io.flush);
+		break;
+
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+
+	if (ret == 0 && cmd & IOC_OUT) {
+		if (copy_to_user(u_io, &io, _IOC_SIZE(cmd))) {
+			pr_debug("bmm_ioctl() error in copy_to_user()\n");
+			return -EFAULT;
+		}
+	}
+
+	return ret;
+}
+
+static const struct file_operations bmm_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= bmm_ioctl,
+};
+
+static struct miscdevice bmm_misc = {
+	.minor	= BMM_MINOR,
+	.name	= "bmm",
+	.fops	= &bmm_fops,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *bmm_dentry;
+
+static int bmm_debugfs_show(struct seq_file *m, void *data)
+{
+	struct bmm_block *bmm;
+
+	list_for_each_entry(bmm, &bmm_used_blocks, list) {
+		seq_printf(m, "0x%08lx-0x%08lx used %p %p\n",
+			   (unsigned long)bmm->paddr,
+			   (unsigned long)bmm->paddr + bmm->size - 1,
+			   bmm->dmabuf, bmm->dmabuf ? bmm->dmabuf->file : NULL);
+	}
+	return 0;
+}
+
+static int bmm_debugfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, bmm_debugfs_show, NULL);
+}
+
+static const struct file_operations bmm_debugfs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= bmm_debugfs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void bmm_debugfs_init(void)
+{
+	bmm_dentry = debugfs_create_file("bmm", S_IRUSR, NULL, NULL,
+		&bmm_debugfs_fops);
+}
+
+static void bmm_debugfs_exit(void)
+{
+	debugfs_remove(bmm_dentry);
+}
+#else
+#define bmm_debugfs_init()
+#define bmm_debugfs_exit()
+#endif
+
+static int bmm_probe(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver bmm_driver = {
+	.driver = {
+		.name	= "bmm",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= bmm_probe,
+};
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "bmm");
+
+static int __init bmm_init(void)
+{
+	int ret;
+
+	bmm_debugfs_init();
+
+	ret = platform_driver_register(&bmm_driver);
+	if (ret)
+		return ret;
+
+	ret = misc_register(&bmm_misc);
+	if (ret)
+		platform_driver_unregister(&bmm_driver);
+
+	return ret;
+}
+
+static void __exit bmm_exit(void)
+{
+	bmm_debugfs_exit();
+	misc_deregister(&bmm_misc);
+	platform_driver_unregister(&bmm_driver);
+}
+module_init(bmm_init);
+module_exit(bmm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CMA-backed dma_buf Management Module");
diff --git a/drivers/misc/bmm_dmabuf.h b/drivers/misc/bmm_dmabuf.h
new file mode 100644
index 000000000000..632982b0c9ba
--- /dev/null
+++ b/drivers/misc/bmm_dmabuf.h
@@ -0,0 +1,52 @@
+/*
+ * bmm_dmabuf.h
+ *
+ * CMA backed dma_buf management
+ *
+ * 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.
+ *
+ * Portions (C) Copyright 2007 Marvell International Ltd.
+ * All Rights Reserved
+ */
+
+#ifndef _BMM_DMABUF_H
+#define _BMM_DMABUF_H
+
+#include <linux/dma-mapping.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* ioctl commands */
+struct bmm_fd_alloc {
+	__u64 size;
+	__u64 align;
+	__u32 attr;
+	__s32 fd;
+};
+
+struct bmm_fd_flush {
+	__u64 size;
+	__u64 offset;
+	__u64 ptr;
+	__s32 fd;
+	__u32 direction;
+};
+
+#define BMEM_IOCTL_MAGIC 'G'
+#define BMM_FD_ALLOC		_IOWR(BMEM_IOCTL_MAGIC, 64, struct bmm_fd_alloc)
+#define BMM_FD_FLUSH		_IOW(BMEM_IOCTL_MAGIC, 65, struct bmm_fd_flush)
+
+/* ioctl arguments: memory attributes */
+#define BMM_ATTR_DEFAULT	(0)		/* cacheable bufferable */
+#define BMM_ATTR_WRITECOMBINE	(1 << 0)	/* non-cacheable & bufferable */
+#define BMM_ATTR_NONCACHED	(1 << 1)	/* non-cacheable & non-bufferable */
+
+/* ioctl arguments: cache flush direction */
+#define BMM_DMA_BIDIRECTIONAL	0
+#define BMM_DMA_TO_DEVICE	1
+#define BMM_DMA_FROM_DEVICE	2
+#define BMM_DMA_NONE		3
+
+#endif

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

From: Russell King <[email-masked]>
 tag v3.17
 tagger Linus Torvalds <[email-masked]> 1412537000 -0700
 
 Linux 3.17
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJUMZqoAAoJEHm+PkMAQRiGFC4H/i0b9vxCwe6VCXonpaDW03fI
 JKE7v/zwDfhDngKSYfBWRSf3jXwfSHLvAvCgIvqTw5qBW3XSWF8xB7kJpWptQxIi
 M6ePfaETt2mPYhEWWWxJK8boykiOXObDrFJVhfjHGsjbvmKiLPMaGYwXTSwZJ32V
 fQDaA9Piugjc9wEY0d+6cjqUUEwlb4+GFz4Wv2oJgbpzxwgJS/XjQYk+3PrcdAXz
 lmwPXQ+6ntJaducVu3JM2YYvaJLzTw+T+MPsWiTvaE4ILmuiw492VNY5XdyQQtb2
 DSActOKCF2hIwnG+DMg63XV5FH81HqczwORDygBuxko0cURxupxMnaLPkVRpksk=
 =+PRQ
 -----END PGP SIGNATURE-----

Merge tag 'v3.17' into bmm-3.17

Linux 3.17
---
Patch suppressed due to merge commit

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

Add a NULL resv argument to the dma_buf_export() to avoid build errors
with v3.17 kernels.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/misc/bmm_dmabuf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/misc/bmm_dmabuf.c b/drivers/misc/bmm_dmabuf.c
index abe204783dcb..fe416da203ea 100644
--- a/drivers/misc/bmm_dmabuf.c
+++ b/drivers/misc/bmm_dmabuf.c
@@ -177,7 +177,7 @@ static int bmm_dmabuf_alloc(struct bmm_fd_alloc *alloc)
 	if (!bmm)
 		return -ENOMEM;
 
-	buf = dma_buf_export(bmm, &bmm_dmabuf_ops, bmm->size, O_RDWR);
+	buf = dma_buf_export(bmm, &bmm_dmabuf_ops, bmm->size, O_RDWR, NULL);
 	if (IS_ERR(buf)) {
 		__bmm_free(bmm);
 		return PTR_ERR(buf);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

From: Russell King <[email-masked]>
 tag v4.1
 tagger Linus Torvalds <[email-masked]> 1434949560 -0700
 
 Linux 4.1
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJVh5e4AAoJEHm+PkMAQRiG8FsIAKSGI/JmypCL1uj3I0mVbF1g
 Ih6RkGChRb7wNjbk35BSl5ZRTChH9L4T3ETjFWfIDWPn3lEjdPrLEb5Ldd48W2Nm
 db2Kk/+f4mjEZXrqESia9NrxhQvyvy6RLBuJ5fGzIE3uRmi9NZMqeiRU5uHnoOyR
 gHV2ziv6TpVYaiIUPdIPnX7y+g9obSD3qMorIQO/wqRWx7JxRxYYUW5FfTzghA7z
 D+PwUSpc1qvloHkxw2szNhTWcI6pD+of+qbFnd+wrMbWuE3k7q6lHGz5vd3ynF/w
 nd82utXhKagCWOujqWp3PsLUzx2Ja3w+nVBOpIlkT/vcXoY9CJ49oJ1tW6pFiUk=
 =SEx0
 -----END PGP SIGNATURE-----

Merge tag 'v4.1' into bmm-4.1

Linux 4.1
---

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/misc/bmm_dmabuf.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/bmm_dmabuf.c b/drivers/misc/bmm_dmabuf.c
index fe416da203ea..b441bdfe7027 100644
--- a/drivers/misc/bmm_dmabuf.c
+++ b/drivers/misc/bmm_dmabuf.c
@@ -166,6 +166,7 @@ static const struct dma_buf_ops bmm_dmabuf_ops = {
 
 static int bmm_dmabuf_alloc(struct bmm_fd_alloc *alloc)
 {
+	DEFINE_DMA_BUF_EXPORT_INFO(export);
 	struct bmm_block *bmm;
 	struct dma_buf *buf;
 	int ret;
@@ -177,7 +178,13 @@ static int bmm_dmabuf_alloc(struct bmm_fd_alloc *alloc)
 	if (!bmm)
 		return -ENOMEM;
 
-	buf = dma_buf_export(bmm, &bmm_dmabuf_ops, bmm->size, O_RDWR, NULL);
+	export.ops = &bmm_dmabuf_ops;
+	export.size = bmm->size;
+	export.flags = O_RDWR;
+	export.resv = NULL;
+	export.priv = bmm;
+
+	buf = dma_buf_export(&export);
 	if (IS_ERR(buf)) {
 		__bmm_free(bmm);
 		return PTR_ERR(buf);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

From: Philipp Zabel <[email-masked]>

Trivial patch to add Vivante Corporation to the list of
devicetree vendor prefixes.

Signed-off-by: Philipp Zabel <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index d444757c4d9e..688c7d190bfc 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -216,6 +216,7 @@ v3	V3 Semiconductor
 variscite	Variscite Ltd.
 via	VIA Technologies, Inc.
 virtio	Virtual I/O Device Specification, developed by the OASIS consortium
+vivante	Vivante Corporation
 voipac	Voipac Technologies s.r.o.
 wexler	Wexler
 winbond Winbond Electronics corp.

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:06 +0100

From: Lucas Stach <[email-masked]>

Etnaviv follows the same priciple as imx-drm to have a virtual
master device node to bind all the individual GPU cores together
into one DRM device.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 .../bindings/drm/etnaviv/etnaviv-drm.txt           | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/etnaviv/etnaviv-drm.txt

diff --git a/Documentation/devicetree/bindings/drm/etnaviv/etnaviv-drm.txt b/Documentation/devicetree/bindings/drm/etnaviv/etnaviv-drm.txt
new file mode 100644
index 000000000000..e27082bdba0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/etnaviv/etnaviv-drm.txt
@@ -0,0 +1,44 @@
+Etnaviv DRM master device
+================================
+
+The Etnaviv DRM master device is a virtual device needed to list all
+Vivante GPU cores that comprise the GPU subsystem.
+
+Required properties:
+- compatible: Should be "fsl,imx-gpu-subsystem"
+- cores: Should contain a list of phandles pointing to Vivante GPU devices
+
+example:
+
+gpu-subsystem {
+	compatible = "fsl,imx-gpu-subsystem";
+	cores = <&gpu_2d>, <&gpu_3d>;
+};
+
+
+Vivante GPU core devices
+====================
+
+Required properties:
+- compatible: Should be "vivante,gc"
+- reg: should be register base and length as documented in the
+  datasheet
+- interrupts: Should contain the cores interrupt line
+- clocks: should contain one clock for entry in clock-names
+  see Documentation/devicetree/bindings/clock/clock-bindings.txt
+- clock-names:
+   - "bus":    AXI/register clock
+   - "core":   GPU core clock
+   - "shader": Shader clock (only required if GPU has feature PIPE_3D)
+
+example:
+
+gpu_3d: gpu@00130000 {
+	compatible = "vivante,gc";
+	reg = <0x00130000 0x4000>;
+	interrupts = <0 9 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&clks IMX6QDL_CLK_GPU3D_AXI>,
+	         <&clks IMX6QDL_CLK_GPU3D_CORE>,
+	         <&clks IMX6QDL_CLK_GPU3D_SHADER>;
+	clock-names = "bus", "core", "shader";
+};

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

From: Christian Gmeiner <[email-masked]>

This is a consolidation by Russell King of Christian's drm work.

Signed-off-by: Christian Gmeiner <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/Kconfig                      |    2 +
 drivers/staging/Makefile                     |    1 +
 drivers/staging/etnaviv/Kconfig              |   20 +
 drivers/staging/etnaviv/Makefile             |   17 +
 drivers/staging/etnaviv/cmdstream.xml.h      |  218 ++++++
 drivers/staging/etnaviv/common.xml.h         |  253 +++++++
 drivers/staging/etnaviv/etnaviv_buffer.c     |  184 +++++
 drivers/staging/etnaviv/etnaviv_drv.c        |  621 ++++++++++++++++
 drivers/staging/etnaviv/etnaviv_drv.h        |  154 ++++
 drivers/staging/etnaviv/etnaviv_gem.c        |  707 ++++++++++++++++++
 drivers/staging/etnaviv/etnaviv_gem.h        |  101 +++
 drivers/staging/etnaviv/etnaviv_gem_prime.c  |   56 ++
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  422 +++++++++++
 drivers/staging/etnaviv/etnaviv_gpu.c        | 1009 ++++++++++++++++++++++++++
 drivers/staging/etnaviv/etnaviv_gpu.h        |  155 ++++
 drivers/staging/etnaviv/etnaviv_iommu.c      |  187 +++++
 drivers/staging/etnaviv/etnaviv_iommu.h      |   26 +
 drivers/staging/etnaviv/etnaviv_iommu_v2.c   |   33 +
 drivers/staging/etnaviv/etnaviv_iommu_v2.h   |   25 +
 drivers/staging/etnaviv/etnaviv_mmu.c        |  114 +++
 drivers/staging/etnaviv/etnaviv_mmu.h        |   45 ++
 drivers/staging/etnaviv/state.xml.h          |  348 +++++++++
 drivers/staging/etnaviv/state_hi.xml.h       |  405 +++++++++++
 include/uapi/drm/etnaviv_drm.h               |  225 ++++++
 24 files changed, 5328 insertions(+)
 create mode 100644 drivers/staging/etnaviv/Kconfig
 create mode 100644 drivers/staging/etnaviv/Makefile
 create mode 100644 drivers/staging/etnaviv/cmdstream.xml.h
 create mode 100644 drivers/staging/etnaviv/common.xml.h
 create mode 100644 drivers/staging/etnaviv/etnaviv_buffer.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_drv.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_drv.h
 create mode 100644 drivers/staging/etnaviv/etnaviv_gem.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_gem.h
 create mode 100644 drivers/staging/etnaviv/etnaviv_gem_prime.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_gem_submit.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_gpu.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_gpu.h
 create mode 100644 drivers/staging/etnaviv/etnaviv_iommu.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_iommu.h
 create mode 100644 drivers/staging/etnaviv/etnaviv_iommu_v2.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_iommu_v2.h
 create mode 100644 drivers/staging/etnaviv/etnaviv_mmu.c
 create mode 100644 drivers/staging/etnaviv/etnaviv_mmu.h
 create mode 100644 drivers/staging/etnaviv/state.xml.h
 create mode 100644 drivers/staging/etnaviv/state_hi.xml.h
 create mode 100644 include/uapi/drm/etnaviv_drm.h

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 7f6cae5beb90..5446fe4859ce 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -112,4 +112,6 @@ source "drivers/staging/fsl-mc/Kconfig"
 
 source "drivers/staging/wilc1000/Kconfig"
 
+source "drivers/staging/etnaviv/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 347f6477aa3e..9fd3c06b6bfd 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -48,3 +48,4 @@ obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD)	+= clocking-wizard/
 obj-$(CONFIG_FB_TFT)		+= fbtft/
 obj-$(CONFIG_FSL_MC_BUS)	+= fsl-mc/
 obj-$(CONFIG_WILC1000)		+= wilc1000/
+obj-$(CONFIG_DRM_ETNAVIV)	+= etnaviv/
diff --git a/drivers/staging/etnaviv/Kconfig b/drivers/staging/etnaviv/Kconfig
new file mode 100644
index 000000000000..6f034eda914c
--- /dev/null
+++ b/drivers/staging/etnaviv/Kconfig
@@ -0,0 +1,20 @@
+
+config DRM_ETNAVIV
+	tristate "etnaviv DRM"
+	depends on DRM
+	select SHMEM
+	select TMPFS
+	select IOMMU_API
+	select IOMMU_SUPPORT
+	default y
+	help
+	  DRM driver for Vivante GPUs.
+
+config DRM_ETNAVIV_REGISTER_LOGGING
+	bool "etnaviv DRM register logging"
+	depends on DRM_ETNAVIV
+	default n
+	help
+	  Compile in support for logging register reads/writes in a format
+	  that can be parsed by envytools demsm tool.  If enabled, register
+	  logging can be switched on via etnaviv.reglog=y module param.
diff --git a/drivers/staging/etnaviv/Makefile b/drivers/staging/etnaviv/Makefile
new file mode 100644
index 000000000000..ef0cffabdcce
--- /dev/null
+++ b/drivers/staging/etnaviv/Makefile
@@ -0,0 +1,17 @@
+ccflags-y := -Iinclude/drm -Idrivers/staging/vivante
+ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
+	ccflags-y += -Werror
+endif
+
+etnaviv-y := \
+	etnaviv_drv.o \
+	etnaviv_gem.o \
+	etnaviv_gem_prime.o \
+	etnaviv_gem_submit.o \
+	etnaviv_gpu.o \
+	etnaviv_iommu.o \
+	etnaviv_iommu_v2.o \
+	etnaviv_mmu.o \
+	etnaviv_buffer.o
+
+obj-$(CONFIG_DRM_ETNAVIV)	+= etnaviv.o
diff --git a/drivers/staging/etnaviv/cmdstream.xml.h b/drivers/staging/etnaviv/cmdstream.xml.h
new file mode 100644
index 000000000000..844f82977e3e
--- /dev/null
+++ b/drivers/staging/etnaviv/cmdstream.xml.h
@@ -0,0 +1,218 @@
+#ifndef CMDSTREAM_XML
+#define CMDSTREAM_XML
+
+/* Autogenerated file, DO NOT EDIT manually!
+
+This file was generated by the rules-ng-ng headergen tool in this git repository:
+http://0x04.net/cgit/index.cgi/rules-ng-ng
+git clone git://0x04.net/rules-ng-ng
+
+The rules-ng-ng source files this header was generated from are:
+- /home/orion/projects/etna_viv/rnndb/cmdstream.xml (  12589 bytes, from 2013-09-01 10:53:22)
+- /home/orion/projects/etna_viv/rnndb/common.xml    (  18379 bytes, from 2014-01-27 15:58:05)
+
+Copyright (C) 2013
+*/
+
+
+#define FE_OPCODE_LOAD_STATE					0x00000001
+#define FE_OPCODE_END						0x00000002
+#define FE_OPCODE_NOP						0x00000003
+#define FE_OPCODE_DRAW_2D					0x00000004
+#define FE_OPCODE_DRAW_PRIMITIVES				0x00000005
+#define FE_OPCODE_DRAW_INDEXED_PRIMITIVES			0x00000006
+#define FE_OPCODE_WAIT						0x00000007
+#define FE_OPCODE_LINK						0x00000008
+#define FE_OPCODE_STALL						0x00000009
+#define FE_OPCODE_CALL						0x0000000a
+#define FE_OPCODE_RETURN					0x0000000b
+#define FE_OPCODE_CHIP_SELECT					0x0000000d
+#define PRIMITIVE_TYPE_POINTS					0x00000001
+#define PRIMITIVE_TYPE_LINES					0x00000002
+#define PRIMITIVE_TYPE_LINE_STRIP				0x00000003
+#define PRIMITIVE_TYPE_TRIANGLES				0x00000004
+#define PRIMITIVE_TYPE_TRIANGLE_STRIP				0x00000005
+#define PRIMITIVE_TYPE_TRIANGLE_FAN				0x00000006
+#define PRIMITIVE_TYPE_LINE_LOOP				0x00000007
+#define PRIMITIVE_TYPE_QUADS					0x00000008
+#define VIV_FE_LOAD_STATE					0x00000000
+
+#define VIV_FE_LOAD_STATE_HEADER				0x00000000
+#define VIV_FE_LOAD_STATE_HEADER_OP__MASK			0xf8000000
+#define VIV_FE_LOAD_STATE_HEADER_OP__SHIFT			27
+#define VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE			0x08000000
+#define VIV_FE_LOAD_STATE_HEADER_FIXP				0x04000000
+#define VIV_FE_LOAD_STATE_HEADER_COUNT__MASK			0x03ff0000
+#define VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT			16
+#define VIV_FE_LOAD_STATE_HEADER_COUNT(x)			(((x) << VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT) & VIV_FE_LOAD_STATE_HEADER_COUNT__MASK)
+#define VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK			0x0000ffff
+#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT			0
+#define VIV_FE_LOAD_STATE_HEADER_OFFSET(x)			(((x) << VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT) & VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK)
+#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR			2
+
+#define VIV_FE_END						0x00000000
+
+#define VIV_FE_END_HEADER					0x00000000
+#define VIV_FE_END_HEADER_EVENT_ID__MASK			0x0000001f
+#define VIV_FE_END_HEADER_EVENT_ID__SHIFT			0
+#define VIV_FE_END_HEADER_EVENT_ID(x)				(((x) << VIV_FE_END_HEADER_EVENT_ID__SHIFT) & VIV_FE_END_HEADER_EVENT_ID__MASK)
+#define VIV_FE_END_HEADER_EVENT_ENABLE				0x00000100
+#define VIV_FE_END_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_END_HEADER_OP__SHIFT				27
+#define VIV_FE_END_HEADER_OP_END				0x10000000
+
+#define VIV_FE_NOP						0x00000000
+
+#define VIV_FE_NOP_HEADER					0x00000000
+#define VIV_FE_NOP_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_NOP_HEADER_OP__SHIFT				27
+#define VIV_FE_NOP_HEADER_OP_NOP				0x18000000
+
+#define VIV_FE_DRAW_2D						0x00000000
+
+#define VIV_FE_DRAW_2D_HEADER					0x00000000
+#define VIV_FE_DRAW_2D_HEADER_COUNT__MASK			0x0000ff00
+#define VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT			8
+#define VIV_FE_DRAW_2D_HEADER_COUNT(x)				(((x) << VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_COUNT__MASK)
+#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK			0x07ff0000
+#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT			16
+#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT(x)			(((x) << VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK)
+#define VIV_FE_DRAW_2D_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_DRAW_2D_HEADER_OP__SHIFT				27
+#define VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D			0x20000000
+
+#define VIV_FE_DRAW_2D_TOP_LEFT					0x00000008
+#define VIV_FE_DRAW_2D_TOP_LEFT_X__MASK				0x0000ffff
+#define VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT			0
+#define VIV_FE_DRAW_2D_TOP_LEFT_X(x)				(((x) << VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_X__MASK)
+#define VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK				0xffff0000
+#define VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT			16
+#define VIV_FE_DRAW_2D_TOP_LEFT_Y(x)				(((x) << VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK)
+
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT				0x0000000c
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK			0x0000ffff
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT			0
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(x)			(((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK)
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK			0xffff0000
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT			16
+#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(x)			(((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK)
+
+#define VIV_FE_DRAW_PRIMITIVES					0x00000000
+
+#define VIV_FE_DRAW_PRIMITIVES_HEADER				0x00000000
+#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__MASK			0xf8000000
+#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__SHIFT			27
+#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP_DRAW_PRIMITIVES	0x28000000
+
+#define VIV_FE_DRAW_PRIMITIVES_COMMAND				0x00000004
+#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK		0x000000ff
+#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT		0
+#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE(x)			(((x) << VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK)
+
+#define VIV_FE_DRAW_PRIMITIVES_START				0x00000008
+
+#define VIV_FE_DRAW_PRIMITIVES_COUNT				0x0000000c
+
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES				0x00000000
+
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER			0x00000000
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__MASK		0xf8000000
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__SHIFT		27
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP_DRAW_INDEXED_PRIMITIVES	0x30000000
+
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND			0x00000004
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK	0x000000ff
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT	0
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE(x)		(((x) << VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK)
+
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_START			0x00000008
+
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COUNT			0x0000000c
+
+#define VIV_FE_DRAW_INDEXED_PRIMITIVES_OFFSET			0x00000010
+
+#define VIV_FE_WAIT						0x00000000
+
+#define VIV_FE_WAIT_HEADER					0x00000000
+#define VIV_FE_WAIT_HEADER_DELAY__MASK				0x0000ffff
+#define VIV_FE_WAIT_HEADER_DELAY__SHIFT				0
+#define VIV_FE_WAIT_HEADER_DELAY(x)				(((x) << VIV_FE_WAIT_HEADER_DELAY__SHIFT) & VIV_FE_WAIT_HEADER_DELAY__MASK)
+#define VIV_FE_WAIT_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_WAIT_HEADER_OP__SHIFT				27
+#define VIV_FE_WAIT_HEADER_OP_WAIT				0x38000000
+
+#define VIV_FE_LINK						0x00000000
+
+#define VIV_FE_LINK_HEADER					0x00000000
+#define VIV_FE_LINK_HEADER_PREFETCH__MASK			0x0000ffff
+#define VIV_FE_LINK_HEADER_PREFETCH__SHIFT			0
+#define VIV_FE_LINK_HEADER_PREFETCH(x)				(((x) << VIV_FE_LINK_HEADER_PREFETCH__SHIFT) & VIV_FE_LINK_HEADER_PREFETCH__MASK)
+#define VIV_FE_LINK_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_LINK_HEADER_OP__SHIFT				27
+#define VIV_FE_LINK_HEADER_OP_LINK				0x40000000
+
+#define VIV_FE_LINK_ADDRESS					0x00000004
+
+#define VIV_FE_STALL						0x00000000
+
+#define VIV_FE_STALL_HEADER					0x00000000
+#define VIV_FE_STALL_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_STALL_HEADER_OP__SHIFT				27
+#define VIV_FE_STALL_HEADER_OP_STALL				0x48000000
+
+#define VIV_FE_STALL_TOKEN					0x00000004
+#define VIV_FE_STALL_TOKEN_FROM__MASK				0x0000001f
+#define VIV_FE_STALL_TOKEN_FROM__SHIFT				0
+#define VIV_FE_STALL_TOKEN_FROM(x)				(((x) << VIV_FE_STALL_TOKEN_FROM__SHIFT) & VIV_FE_STALL_TOKEN_FROM__MASK)
+#define VIV_FE_STALL_TOKEN_TO__MASK				0x00001f00
+#define VIV_FE_STALL_TOKEN_TO__SHIFT				8
+#define VIV_FE_STALL_TOKEN_TO(x)				(((x) << VIV_FE_STALL_TOKEN_TO__SHIFT) & VIV_FE_STALL_TOKEN_TO__MASK)
+
+#define VIV_FE_CALL						0x00000000
+
+#define VIV_FE_CALL_HEADER					0x00000000
+#define VIV_FE_CALL_HEADER_PREFETCH__MASK			0x0000ffff
+#define VIV_FE_CALL_HEADER_PREFETCH__SHIFT			0
+#define VIV_FE_CALL_HEADER_PREFETCH(x)				(((x) << VIV_FE_CALL_HEADER_PREFETCH__SHIFT) & VIV_FE_CALL_HEADER_PREFETCH__MASK)
+#define VIV_FE_CALL_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_CALL_HEADER_OP__SHIFT				27
+#define VIV_FE_CALL_HEADER_OP_CALL				0x50000000
+
+#define VIV_FE_CALL_ADDRESS					0x00000004
+
+#define VIV_FE_CALL_RETURN_PREFETCH				0x00000008
+
+#define VIV_FE_CALL_RETURN_ADDRESS				0x0000000c
+
+#define VIV_FE_RETURN						0x00000000
+
+#define VIV_FE_RETURN_HEADER					0x00000000
+#define VIV_FE_RETURN_HEADER_OP__MASK				0xf8000000
+#define VIV_FE_RETURN_HEADER_OP__SHIFT				27
+#define VIV_FE_RETURN_HEADER_OP_RETURN				0x58000000
+
+#define VIV_FE_CHIP_SELECT					0x00000000
+
+#define VIV_FE_CHIP_SELECT_HEADER				0x00000000
+#define VIV_FE_CHIP_SELECT_HEADER_OP__MASK			0xf8000000
+#define VIV_FE_CHIP_SELECT_HEADER_OP__SHIFT			27
+#define VIV_FE_CHIP_SELECT_HEADER_OP_CHIP_SELECT		0x68000000
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP15			0x00008000
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP14			0x00004000
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP13			0x00002000
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP12			0x00001000
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP11			0x00000800
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP10			0x00000400
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP9			0x00000200
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP8			0x00000100
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP7			0x00000080
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP6			0x00000040
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP5			0x00000020
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP4			0x00000010
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP3			0x00000008
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP2			0x00000004
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP1			0x00000002
+#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP0			0x00000001
+
+
+#endif /* CMDSTREAM_XML */
diff --git a/drivers/staging/etnaviv/common.xml.h b/drivers/staging/etnaviv/common.xml.h
new file mode 100644
index 000000000000..36fa0e4cf56b
--- /dev/null
+++ b/drivers/staging/etnaviv/common.xml.h
@@ -0,0 +1,253 @@
+#ifndef COMMON_XML
+#define COMMON_XML
+
+/* Autogenerated file, DO NOT EDIT manually!
+
+This file was generated by the rules-ng-ng headergen tool in this git repository:
+http://0x04.net/cgit/index.cgi/rules-ng-ng
+git clone git://0x04.net/rules-ng-ng
+
+The rules-ng-ng source files this header was generated from are:
+- /home/orion/projects/etna_viv/rnndb/state.xml    (  18526 bytes, from 2013-09-11 16:52:32)
+- /home/orion/projects/etna_viv/rnndb/common.xml   (  18379 bytes, from 2014-01-27 15:58:05)
+- /home/orion/projects/etna_viv/rnndb/state_hi.xml (  22236 bytes, from 2014-01-27 15:56:46)
+- /home/orion/projects/etna_viv/rnndb/state_2d.xml (  51191 bytes, from 2013-10-04 06:36:55)
+- /home/orion/projects/etna_viv/rnndb/state_3d.xml (  54570 bytes, from 2013-10-12 15:25:03)
+- /home/orion/projects/etna_viv/rnndb/state_vg.xml (   5942 bytes, from 2013-09-01 10:53:22)
+
+Copyright (C) 2014
+*/
+
+
+#define PIPE_ID_PIPE_3D						0x00000000
+#define PIPE_ID_PIPE_2D						0x00000001
+#define SYNC_RECIPIENT_FE					0x00000001
+#define SYNC_RECIPIENT_RA					0x00000005
+#define SYNC_RECIPIENT_PE					0x00000007
+#define SYNC_RECIPIENT_DE					0x0000000b
+#define SYNC_RECIPIENT_VG					0x0000000f
+#define SYNC_RECIPIENT_TESSELATOR				0x00000010
+#define SYNC_RECIPIENT_VG2					0x00000011
+#define SYNC_RECIPIENT_TESSELATOR2				0x00000012
+#define SYNC_RECIPIENT_VG3					0x00000013
+#define SYNC_RECIPIENT_TESSELATOR3				0x00000014
+#define ENDIAN_MODE_NO_SWAP					0x00000000
+#define ENDIAN_MODE_SWAP_16					0x00000001
+#define ENDIAN_MODE_SWAP_32					0x00000002
+#define chipModel_GC300						0x00000300
+#define chipModel_GC320						0x00000320
+#define chipModel_GC350						0x00000350
+#define chipModel_GC355						0x00000355
+#define chipModel_GC400						0x00000400
+#define chipModel_GC410						0x00000410
+#define chipModel_GC420						0x00000420
+#define chipModel_GC450						0x00000450
+#define chipModel_GC500						0x00000500
+#define chipModel_GC530						0x00000530
+#define chipModel_GC600						0x00000600
+#define chipModel_GC700						0x00000700
+#define chipModel_GC800						0x00000800
+#define chipModel_GC860						0x00000860
+#define chipModel_GC880						0x00000880
+#define chipModel_GC1000					0x00001000
+#define chipModel_GC2000					0x00002000
+#define chipModel_GC2100					0x00002100
+#define chipModel_GC4000					0x00004000
+#define RGBA_BITS_R						0x00000001
+#define RGBA_BITS_G						0x00000002
+#define RGBA_BITS_B						0x00000004
+#define RGBA_BITS_A						0x00000008
+#define chipFeatures_FAST_CLEAR					0x00000001
+#define chipFeatures_SPECIAL_ANTI_ALIASING			0x00000002
+#define chipFeatures_PIPE_3D					0x00000004
+#define chipFeatures_DXT_TEXTURE_COMPRESSION			0x00000008
+#define chipFeatures_DEBUG_MODE					0x00000010
+#define chipFeatures_Z_COMPRESSION				0x00000020
+#define chipFeatures_YUV420_SCALER				0x00000040
+#define chipFeatures_MSAA					0x00000080
+#define chipFeatures_DC						0x00000100
+#define chipFeatures_PIPE_2D					0x00000200
+#define chipFeatures_ETC1_TEXTURE_COMPRESSION			0x00000400
+#define chipFeatures_FAST_SCALER				0x00000800
+#define chipFeatures_HIGH_DYNAMIC_RANGE				0x00001000
+#define chipFeatures_YUV420_TILER				0x00002000
+#define chipFeatures_MODULE_CG					0x00004000
+#define chipFeatures_MIN_AREA					0x00008000
+#define chipFeatures_NO_EARLY_Z					0x00010000
+#define chipFeatures_NO_422_TEXTURE				0x00020000
+#define chipFeatures_BUFFER_INTERLEAVING			0x00040000
+#define chipFeatures_BYTE_WRITE_2D				0x00080000
+#define chipFeatures_NO_SCALER					0x00100000
+#define chipFeatures_YUY2_AVERAGING				0x00200000
+#define chipFeatures_HALF_PE_CACHE				0x00400000
+#define chipFeatures_HALF_TX_CACHE				0x00800000
+#define chipFeatures_YUY2_RENDER_TARGET				0x01000000
+#define chipFeatures_MEM32					0x02000000
+#define chipFeatures_PIPE_VG					0x04000000
+#define chipFeatures_VGTS					0x08000000
+#define chipFeatures_FE20					0x10000000
+#define chipFeatures_BYTE_WRITE_3D				0x20000000
+#define chipFeatures_RS_YUV_TARGET				0x40000000
+#define chipFeatures_32_BIT_INDICES				0x80000000
+#define chipMinorFeatures0_FLIP_Y				0x00000001
+#define chipMinorFeatures0_DUAL_RETURN_BUS			0x00000002
+#define chipMinorFeatures0_ENDIANNESS_CONFIG			0x00000004
+#define chipMinorFeatures0_TEXTURE_8K				0x00000008
+#define chipMinorFeatures0_CORRECT_TEXTURE_CONVERTER		0x00000010
+#define chipMinorFeatures0_SPECIAL_MSAA_LOD			0x00000020
+#define chipMinorFeatures0_FAST_CLEAR_FLUSH			0x00000040
+#define chipMinorFeatures0_2DPE20				0x00000080
+#define chipMinorFeatures0_CORRECT_AUTO_DISABLE			0x00000100
+#define chipMinorFeatures0_RENDERTARGET_8K			0x00000200
+#define chipMinorFeatures0_2BITPERTILE				0x00000400
+#define chipMinorFeatures0_SEPARATE_TILE_STATUS_WHEN_INTERLEAVED	0x00000800
+#define chipMinorFeatures0_SUPER_TILED				0x00001000
+#define chipMinorFeatures0_VG_20				0x00002000
+#define chipMinorFeatures0_TS_EXTENDED_COMMANDS			0x00004000
+#define chipMinorFeatures0_COMPRESSION_FIFO_FIXED		0x00008000
+#define chipMinorFeatures0_HAS_SIGN_FLOOR_CEIL			0x00010000
+#define chipMinorFeatures0_VG_FILTER				0x00020000
+#define chipMinorFeatures0_VG_21				0x00040000
+#define chipMinorFeatures0_SHADER_HAS_W				0x00080000
+#define chipMinorFeatures0_HAS_SQRT_TRIG			0x00100000
+#define chipMinorFeatures0_MORE_MINOR_FEATURES			0x00200000
+#define chipMinorFeatures0_MC20					0x00400000
+#define chipMinorFeatures0_MSAA_SIDEBAND			0x00800000
+#define chipMinorFeatures0_BUG_FIXES0				0x01000000
+#define chipMinorFeatures0_VAA					0x02000000
+#define chipMinorFeatures0_BYPASS_IN_MSAA			0x04000000
+#define chipMinorFeatures0_HZ					0x08000000
+#define chipMinorFeatures0_NEW_TEXTURE				0x10000000
+#define chipMinorFeatures0_2D_A8_TARGET				0x20000000
+#define chipMinorFeatures0_CORRECT_STENCIL			0x40000000
+#define chipMinorFeatures0_ENHANCE_VR				0x80000000
+#define chipMinorFeatures1_RSUV_SWIZZLE				0x00000001
+#define chipMinorFeatures1_V2_COMPRESSION			0x00000002
+#define chipMinorFeatures1_VG_DOUBLE_BUFFER			0x00000004
+#define chipMinorFeatures1_EXTRA_EVENT_STATES			0x00000008
+#define chipMinorFeatures1_NO_STRIPING_NEEDED			0x00000010
+#define chipMinorFeatures1_TEXTURE_STRIDE			0x00000020
+#define chipMinorFeatures1_BUG_FIXES3				0x00000040
+#define chipMinorFeatures1_AUTO_DISABLE				0x00000080
+#define chipMinorFeatures1_AUTO_RESTART_TS			0x00000100
+#define chipMinorFeatures1_DISABLE_PE_GATING			0x00000200
+#define chipMinorFeatures1_L2_WINDOWING				0x00000400
+#define chipMinorFeatures1_HALF_FLOAT				0x00000800
+#define chipMinorFeatures1_PIXEL_DITHER				0x00001000
+#define chipMinorFeatures1_TWO_STENCIL_REFERENCE		0x00002000
+#define chipMinorFeatures1_EXTENDED_PIXEL_FORMAT		0x00004000
+#define chipMinorFeatures1_CORRECT_MIN_MAX_DEPTH		0x00008000
+#define chipMinorFeatures1_2D_DITHER				0x00010000
+#define chipMinorFeatures1_BUG_FIXES5				0x00020000
+#define chipMinorFeatures1_NEW_2D				0x00040000
+#define chipMinorFeatures1_NEW_FP				0x00080000
+#define chipMinorFeatures1_TEXTURE_HALIGN			0x00100000
+#define chipMinorFeatures1_NON_POWER_OF_TWO			0x00200000
+#define chipMinorFeatures1_LINEAR_TEXTURE_SUPPORT		0x00400000
+#define chipMinorFeatures1_HALTI0				0x00800000
+#define chipMinorFeatures1_CORRECT_OVERFLOW_VG			0x01000000
+#define chipMinorFeatures1_NEGATIVE_LOG_FIX			0x02000000
+#define chipMinorFeatures1_RESOLVE_OFFSET			0x04000000
+#define chipMinorFeatures1_OK_TO_GATE_AXI_CLOCK			0x08000000
+#define chipMinorFeatures1_MMU_VERSION				0x10000000
+#define chipMinorFeatures1_WIDE_LINE				0x20000000
+#define chipMinorFeatures1_BUG_FIXES6				0x40000000
+#define chipMinorFeatures1_FC_FLUSH_STALL			0x80000000
+#define chipMinorFeatures2_LINE_LOOP				0x00000001
+#define chipMinorFeatures2_LOGIC_OP				0x00000002
+#define chipMinorFeatures2_UNK2					0x00000004
+#define chipMinorFeatures2_SUPERTILED_TEXTURE			0x00000008
+#define chipMinorFeatures2_UNK4					0x00000010
+#define chipMinorFeatures2_RECT_PRIMITIVE			0x00000020
+#define chipMinorFeatures2_COMPOSITION				0x00000040
+#define chipMinorFeatures2_CORRECT_AUTO_DISABLE_COUNT		0x00000080
+#define chipMinorFeatures2_UNK8					0x00000100
+#define chipMinorFeatures2_UNK9					0x00000200
+#define chipMinorFeatures2_UNK10				0x00000400
+#define chipMinorFeatures2_SAMPLERBASE_16			0x00000800
+#define chipMinorFeatures2_UNK12				0x00001000
+#define chipMinorFeatures2_UNK13				0x00002000
+#define chipMinorFeatures2_UNK14				0x00004000
+#define chipMinorFeatures2_EXTRA_TEXTURE_STATE			0x00008000
+#define chipMinorFeatures2_FULL_DIRECTFB			0x00010000
+#define chipMinorFeatures2_2D_TILING				0x00020000
+#define chipMinorFeatures2_THREAD_WALKER_IN_PS			0x00040000
+#define chipMinorFeatures2_TILE_FILLER				0x00080000
+#define chipMinorFeatures2_UNK20				0x00100000
+#define chipMinorFeatures2_2D_MULTI_SOURCE_BLIT			0x00200000
+#define chipMinorFeatures2_UNK22				0x00400000
+#define chipMinorFeatures2_UNK23				0x00800000
+#define chipMinorFeatures2_UNK24				0x01000000
+#define chipMinorFeatures2_MIXED_STREAMS			0x02000000
+#define chipMinorFeatures2_2D_420_L2CACHE			0x04000000
+#define chipMinorFeatures2_UNK27				0x08000000
+#define chipMinorFeatures2_2D_NO_INDEX8_BRUSH			0x10000000
+#define chipMinorFeatures2_TEXTURE_TILED_READ			0x20000000
+#define chipMinorFeatures2_UNK30				0x40000000
+#define chipMinorFeatures2_UNK31				0x80000000
+#define chipMinorFeatures3_ROTATION_STALL_FIX			0x00000001
+#define chipMinorFeatures3_UNK1					0x00000002
+#define chipMinorFeatures3_2D_MULTI_SOURCE_BLT_EX		0x00000004
+#define chipMinorFeatures3_UNK3					0x00000008
+#define chipMinorFeatures3_UNK4					0x00000010
+#define chipMinorFeatures3_UNK5					0x00000020
+#define chipMinorFeatures3_UNK6					0x00000040
+#define chipMinorFeatures3_UNK7					0x00000080
+#define chipMinorFeatures3_UNK8					0x00000100
+#define chipMinorFeatures3_UNK9					0x00000200
+#define chipMinorFeatures3_BUG_FIXES10				0x00000400
+#define chipMinorFeatures3_UNK11				0x00000800
+#define chipMinorFeatures3_BUG_FIXES11				0x00001000
+#define chipMinorFeatures3_UNK13				0x00002000
+#define chipMinorFeatures3_UNK14				0x00004000
+#define chipMinorFeatures3_UNK15				0x00008000
+#define chipMinorFeatures3_UNK16				0x00010000
+#define chipMinorFeatures3_UNK17				0x00020000
+#define chipMinorFeatures3_UNK18				0x00040000
+#define chipMinorFeatures3_UNK19				0x00080000
+#define chipMinorFeatures3_UNK20				0x00100000
+#define chipMinorFeatures3_UNK21				0x00200000
+#define chipMinorFeatures3_UNK22				0x00400000
+#define chipMinorFeatures3_UNK23				0x00800000
+#define chipMinorFeatures3_UNK24				0x01000000
+#define chipMinorFeatures3_UNK25				0x02000000
+#define chipMinorFeatures3_UNK26				0x04000000
+#define chipMinorFeatures3_UNK27				0x08000000
+#define chipMinorFeatures3_UNK28				0x10000000
+#define chipMinorFeatures3_UNK29				0x20000000
+#define chipMinorFeatures3_UNK30				0x40000000
+#define chipMinorFeatures3_UNK31				0x80000000
+#define chipMinorFeatures4_UNK0					0x00000001
+#define chipMinorFeatures4_UNK1					0x00000002
+#define chipMinorFeatures4_UNK2					0x00000004
+#define chipMinorFeatures4_UNK3					0x00000008
+#define chipMinorFeatures4_UNK4					0x00000010
+#define chipMinorFeatures4_UNK5					0x00000020
+#define chipMinorFeatures4_UNK6					0x00000040
+#define chipMinorFeatures4_UNK7					0x00000080
+#define chipMinorFeatures4_UNK8					0x00000100
+#define chipMinorFeatures4_UNK9					0x00000200
+#define chipMinorFeatures4_UNK10				0x00000400
+#define chipMinorFeatures4_UNK11				0x00000800
+#define chipMinorFeatures4_UNK12				0x00001000
+#define chipMinorFeatures4_UNK13				0x00002000
+#define chipMinorFeatures4_UNK14				0x00004000
+#define chipMinorFeatures4_UNK15				0x00008000
+#define chipMinorFeatures4_UNK16				0x00010000
+#define chipMinorFeatures4_UNK17				0x00020000
+#define chipMinorFeatures4_UNK18				0x00040000
+#define chipMinorFeatures4_UNK19				0x00080000
+#define chipMinorFeatures4_UNK20				0x00100000
+#define chipMinorFeatures4_UNK21				0x00200000
+#define chipMinorFeatures4_UNK22				0x00400000
+#define chipMinorFeatures4_UNK23				0x00800000
+#define chipMinorFeatures4_UNK24				0x01000000
+#define chipMinorFeatures4_UNK25				0x02000000
+#define chipMinorFeatures4_UNK26				0x04000000
+#define chipMinorFeatures4_UNK27				0x08000000
+#define chipMinorFeatures4_UNK28				0x10000000
+#define chipMinorFeatures4_UNK29				0x20000000
+#define chipMinorFeatures4_UNK30				0x40000000
+#define chipMinorFeatures4_UNK31				0x80000000
+
+#endif /* COMMON_XML */
diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
new file mode 100644
index 000000000000..6aabf8ce37d6
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 2014 Etnaviv Project
+ * Author: Christian Gmeiner <[email-masked]>
+ *
+ * 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"
+#include "etnaviv_gem.h"
+
+#include "common.xml.h"
+#include "state.xml.h"
+#include "cmdstream.xml.h"
+
+/*
+ * Command Buffer helper:
+ */
+
+
+static inline void OUT(struct etnaviv_gem_object *buffer, uint32_t data)
+{
+	u32 *vaddr = (u32 *)buffer->vaddr;
+	BUG_ON(buffer->offset * sizeof(*vaddr) >= buffer->base.size);
+
+	vaddr[buffer->offset++] = data;
+}
+
+static inline void CMD_LOAD_STATE(struct etnaviv_gem_object *buffer, u32 reg, u32 value)
+{
+	buffer->offset = ALIGN(buffer->offset, 2);
+
+	/* write a register via cmd stream */
+	OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | VIV_FE_LOAD_STATE_HEADER_COUNT(1) |
+			VIV_FE_LOAD_STATE_HEADER_OFFSET(reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR));
+	OUT(buffer, value);
+}
+
+static inline void CMD_END(struct etnaviv_gem_object *buffer)
+{
+	buffer->offset = ALIGN(buffer->offset, 2);
+
+	OUT(buffer, VIV_FE_END_HEADER_OP_END);
+}
+
+static inline void CMD_WAIT(struct etnaviv_gem_object *buffer)
+{
+	buffer->offset = ALIGN(buffer->offset, 2);
+
+	OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | 200);
+}
+
+static inline void CMD_LINK(struct etnaviv_gem_object *buffer, u16 prefetch, u32 address)
+{
+	buffer->offset = ALIGN(buffer->offset, 2);
+
+	OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(prefetch));
+	OUT(buffer, address);
+}
+
+static inline void CMD_STALL(struct etnaviv_gem_object *buffer, u32 from, u32 to)
+{
+	buffer->offset = ALIGN(buffer->offset, 2);
+
+	OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL);
+	OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to));
+}
+
+static void cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
+{
+	u32 flush;
+	u32 stall;
+
+	if (pipe == ETNA_PIPE_2D)
+		flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR;
+	else
+		flush = VIVS_GL_FLUSH_CACHE_TEXTURE;
+
+	stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) |
+			VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE);
+
+	CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush);
+	CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall);
+
+	CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
+
+	CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, VIVS_GL_PIPE_SELECT_PIPE(pipe));
+}
+
+static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *obj, u32 len)
+{
+	u32 size = obj->base.size;
+	u32 *ptr = obj->vaddr;
+
+	dev_info(gpu->dev->dev, "virt %p phys 0x%llx free 0x%08x\n",
+			obj->vaddr, (u64)obj->paddr, size - len * 4);
+
+	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
+			ptr, len * 4, 0);
+}
+
+u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
+{
+	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
+
+	/* initialize buffer */
+	buffer->offset = 0;
+	buffer->is_ring_buffer = true;
+
+	cmd_select_pipe(buffer, gpu->pipe);
+
+	CMD_WAIT(buffer);
+	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+
+	return buffer->offset;
+}
+
+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_gem_submit *submit)
+{
+	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
+	struct etnaviv_gem_object *cmd;
+	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
+	u32 back;
+	u32 i;
+
+	etnaviv_buffer_dump(gpu, buffer, 0x50);
+
+	/* save offset back into main buffer */
+	back = buffer->offset;
+
+	/* trigger event */
+	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | VIVS_GL_EVENT_FROM_PE);
+
+	/* append WAIT/LINK to main buffer */
+	CMD_WAIT(buffer);
+	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+
+	/* update offset for every cmd stream */
+	for (i = 0; i < submit->nr_cmds; i++)
+		submit->cmd[i].obj->offset = submit->cmd[i].size;
+
+	/* TODO: inter-connect all cmd buffers */
+
+	/* jump back from last cmd to main buffer */
+	cmd = submit->cmd[submit->nr_cmds - 1].obj;
+	CMD_LINK(cmd, 4, buffer->paddr + (back * 4));
+
+	printk(KERN_ERR "stream link @ 0x%llx\n", (u64)cmd->paddr + ((cmd->offset - 1) * 4));
+	printk(KERN_ERR "stream link @ %p\n", cmd->vaddr + ((cmd->offset - 1) * 4));
+
+	for (i = 0; i < submit->nr_cmds; i++) {
+		struct etnaviv_gem_object *obj = submit->cmd[i].obj;
+
+		/* TODO: remove later */
+		if (unlikely(drm_debug & DRM_UT_CORE))
+			etnaviv_buffer_dump(gpu, obj, submit->cmd[i].size);
+	}
+
+	/* change ll to NOP */
+	printk(KERN_ERR "link op: %p\n", lw);
+	printk(KERN_ERR "link addr: %p\n", lw + 1);
+	printk(KERN_ERR "addr: 0x%llx\n", (u64)submit->cmd[0].obj->paddr);
+	printk(KERN_ERR "back: 0x%llx\n", (u64)buffer->paddr + (back * 4));
+	printk(KERN_ERR "event: %d\n", event);
+
+	/* Change WAIT into a LINK command; write the address first. */
+	i = VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(submit->cmd[0].size * 2);
+	*(lw + 1) = submit->cmd[0].obj->paddr;
+	mb();
+	*(lw)= i;
+	mb();
+
+	etnaviv_buffer_dump(gpu, buffer, 0x50);
+}
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
new file mode 100644
index 000000000000..718bc17412d3
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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 <linux/component.h>
+#include <linux/of_platform.h>
+
+#include "etnaviv_drv.h"
+#include "etnaviv_gpu.h"
+
+void etnaviv_register_mmu(struct drm_device *dev, struct etnaviv_iommu *mmu)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	priv->mmu = mmu;
+}
+
+#ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
+static bool reglog = false;
+MODULE_PARM_DESC(reglog, "Enable register read/write logging");
+module_param(reglog, bool, 0600);
+#else
+#define reglog 0
+#endif
+
+void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name,
+		const char *dbgname)
+{
+	struct resource *res;
+	void __iomem *ptr;
+
+	if (name)
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	else
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	ptr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ptr)) {
+		dev_err(&pdev->dev, "failed to ioremap %s: %ld\n", name,
+			PTR_ERR(ptr));
+		return ptr;
+	}
+
+	if (reglog)
+		dev_printk(KERN_DEBUG, &pdev->dev, "IO:region %s 0x%p %08zx\n",
+			   dbgname, ptr, (size_t)resource_size(res));
+
+	return ptr;
+}
+
+void etnaviv_writel(u32 data, void __iomem *addr)
+{
+	if (reglog)
+		printk(KERN_DEBUG "IO:W %p %08x\n", addr, data);
+	writel(data, addr);
+}
+
+u32 etnaviv_readl(const void __iomem *addr)
+{
+	u32 val = readl(addr);
+	if (reglog)
+		printk(KERN_DEBUG "IO:R %p %08x\n", addr, val);
+	return val;
+}
+
+/*
+ * DRM operations:
+ */
+
+static int etnaviv_unload(struct drm_device *dev)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	unsigned int i;
+
+	flush_workqueue(priv->wq);
+	destroy_workqueue(priv->wq);
+
+	mutex_lock(&dev->struct_mutex);
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		struct etnaviv_gpu *g = priv->gpu[i];
+		if (g)
+			etnaviv_gpu_pm_suspend(g);
+	}
+	mutex_unlock(&dev->struct_mutex);
+
+	component_unbind_all(dev->dev, dev);
+
+	dev->dev_private = NULL;
+
+	kfree(priv);
+
+	return 0;
+}
+
+
+static void load_gpu(struct drm_device *dev)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	unsigned int i;
+
+	mutex_lock(&dev->struct_mutex);
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		struct etnaviv_gpu *g = priv->gpu[i];
+		if (g) {
+			int ret;
+			etnaviv_gpu_pm_resume(g);
+			ret = etnaviv_gpu_init(g);
+			if (ret) {
+				dev_err(dev->dev, "%s hw init failed: %d\n", g->name, ret);
+				priv->gpu[i] = NULL;
+			}
+		}
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+}
+
+static int etnaviv_load(struct drm_device *dev, unsigned long flags)
+{
+	struct platform_device *pdev = dev->platformdev;
+	struct etnaviv_drm_private *priv;
+	int err;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(dev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	dev->dev_private = priv;
+
+	priv->wq = alloc_ordered_workqueue("etnaviv", 0);
+	init_waitqueue_head(&priv->fence_event);
+
+	INIT_LIST_HEAD(&priv->inactive_list);
+
+	platform_set_drvdata(pdev, dev);
+
+	err = component_bind_all(dev->dev, dev);
+	if (err < 0)
+		return err;
+
+	load_gpu(dev);
+
+	return 0;
+}
+
+static int etnaviv_open(struct drm_device *dev, struct drm_file *file)
+{
+	struct etnaviv_file_private *ctx;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	file->driver_priv = ctx;
+
+	return 0;
+}
+
+static void etnaviv_preclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_file_private *ctx = file->driver_priv;
+
+	mutex_lock(&dev->struct_mutex);
+	if (ctx == priv->lastctx)
+		priv->lastctx = NULL;
+	mutex_unlock(&dev->struct_mutex);
+
+	kfree(ctx);
+}
+
+/*
+ * DRM debugfs:
+ */
+
+#ifdef CONFIG_DEBUG_FS
+static int etnaviv_gpu_show(struct drm_device *dev, struct seq_file *m)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gpu *gpu;
+	unsigned int i;
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		gpu = priv->gpu[i];
+		if (gpu) {
+			seq_printf(m, "%s Status:\n", gpu->name);
+			etnaviv_gpu_debugfs(gpu, m);
+		}
+	}
+
+	return 0;
+}
+
+static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gpu *gpu;
+	unsigned int i;
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		gpu = priv->gpu[i];
+		if (gpu) {
+			seq_printf(m, "Active Objects (%s):\n", gpu->name);
+			etnaviv_gem_describe_objects(&gpu->active_list, m);
+		}
+	}
+
+	seq_puts(m, "Inactive Objects:\n");
+	etnaviv_gem_describe_objects(&priv->inactive_list, m);
+
+	return 0;
+}
+
+static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m)
+{
+	return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm);
+}
+
+static int show_locked(struct seq_file *m, void *arg)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	int (*show)(struct drm_device *dev, struct seq_file *m) =
+			node->info_ent->data;
+	int ret;
+
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret)
+		return ret;
+
+	ret = show(dev, m);
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+static struct drm_info_list etnaviv_debugfs_list[] = {
+		{"gpu", show_locked, 0, etnaviv_gpu_show},
+		{"gem", show_locked, 0, etnaviv_gem_show},
+		{ "mm", show_locked, 0, etnaviv_mm_show },
+};
+
+static int etnaviv_debugfs_init(struct drm_minor *minor)
+{
+	struct drm_device *dev = minor->dev;
+	int ret;
+
+	ret = drm_debugfs_create_files(etnaviv_debugfs_list,
+			ARRAY_SIZE(etnaviv_debugfs_list),
+			minor->debugfs_root, minor);
+
+	if (ret) {
+		dev_err(dev->dev, "could not install etnaviv_debugfs_list\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void etnaviv_debugfs_cleanup(struct drm_minor *minor)
+{
+	drm_debugfs_remove_files(etnaviv_debugfs_list,
+			ARRAY_SIZE(etnaviv_debugfs_list), minor);
+}
+#endif
+
+/*
+ * Fences:
+ */
+int etnaviv_wait_fence_interruptable(struct drm_device *dev,
+		struct etnaviv_gpu *gpu, uint32_t fence,
+		struct timespec *timeout)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	int ret;
+
+	if (fence_after(fence, gpu->submitted_fence)) {
+		DRM_ERROR("waiting on invalid fence: %u (of %u)\n",
+				fence, gpu->submitted_fence);
+		return -EINVAL;
+	}
+
+	if (!timeout) {
+		/* no-wait: */
+		ret = fence_completed(dev, fence) ? 0 : -EBUSY;
+	} else {
+		unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
+		unsigned long start_jiffies = jiffies;
+		unsigned long remaining_jiffies;
+
+		if (time_after(start_jiffies, timeout_jiffies))
+			remaining_jiffies = 0;
+		else
+			remaining_jiffies = timeout_jiffies - start_jiffies;
+
+		ret = wait_event_interruptible_timeout(priv->fence_event,
+				fence_completed(dev, fence),
+				remaining_jiffies);
+
+		if (ret == 0) {
+			DBG("timeout waiting for fence: %u (completed: %u)",
+					fence, priv->completed_fence);
+			ret = -ETIMEDOUT;
+		} else if (ret != -ERESTARTSYS) {
+			ret = 0;
+		}
+	}
+
+	return ret;
+}
+
+/* called from workqueue */
+void etnaviv_update_fence(struct drm_device *dev, uint32_t fence)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+
+	mutex_lock(&dev->struct_mutex);
+	if (fence_after(fence, priv->completed_fence))
+		priv->completed_fence = fence;
+	mutex_unlock(&dev->struct_mutex);
+
+	wake_up_all(&priv->fence_event);
+}
+
+/*
+ * DRM ioctls:
+ */
+
+static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct drm_etnaviv_param *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_gpu_get_param(gpu, args->param, &args->value);
+}
+
+static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct drm_etnaviv_gem_new *args = data;
+	return etnaviv_gem_new_handle(dev, file, args->size,
+			args->flags, &args->handle);
+}
+
+#define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec })
+
+static int etnaviv_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct drm_etnaviv_gem_cpu_prep *args = data;
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = drm_gem_object_lookup(dev, file, args->handle);
+	if (!obj)
+		return -ENOENT;
+
+	ret = etnaviv_gem_cpu_prep(obj, args->op, &TS(args->timeout));
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+static int etnaviv_ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct drm_etnaviv_gem_cpu_fini *args = data;
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = drm_gem_object_lookup(dev, file, args->handle);
+	if (!obj)
+		return -ENOENT;
+
+	ret = etnaviv_gem_cpu_fini(obj);
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct drm_etnaviv_gem_info *args = data;
+	struct drm_gem_object *obj;
+	int ret = 0;
+
+	if (args->pad)
+		return -EINVAL;
+
+	obj = drm_gem_object_lookup(dev, file, args->handle);
+	if (!obj)
+		return -ENOENT;
+
+	args->offset = etnaviv_gem_mmap_offset(obj);
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct drm_etnaviv_wait_fence *args = data;
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gpu *gpu;
+
+	if (args->pipe >= ETNA_MAX_PIPES)
+		return -EINVAL;
+
+	gpu = priv->gpu[args->pipe];
+	if (!gpu)
+		return -ENXIO;
+
+	return etnaviv_wait_fence_interruptable(dev, gpu,
+		args->fence, &TS(args->timeout));
+}
+
+static const struct drm_ioctl_desc etnaviv_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ETNAVIV_GET_PARAM,    etnaviv_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_NEW,      etnaviv_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_INFO,     etnaviv_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_CPU_PREP, etnaviv_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_CPU_FINI, etnaviv_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_SUBMIT,   etnaviv_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ETNAVIV_WAIT_FENCE,   etnaviv_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+};
+
+static const struct vm_operations_struct vm_ops = {
+	.fault = etnaviv_gem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static const struct file_operations fops = {
+	.owner              = THIS_MODULE,
+	.open               = drm_open,
+	.release            = drm_release,
+	.unlocked_ioctl     = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl       = drm_compat_ioctl,
+#endif
+	.poll               = drm_poll,
+	.read               = drm_read,
+	.llseek             = no_llseek,
+	.mmap               = etnaviv_gem_mmap,
+};
+
+static struct drm_driver etnaviv_drm_driver = {
+	.driver_features    = DRIVER_HAVE_IRQ |
+				DRIVER_GEM |
+				DRIVER_PRIME |
+				DRIVER_RENDER,
+	.load               = etnaviv_load,
+	.unload             = etnaviv_unload,
+	.open               = etnaviv_open,
+	.preclose           = etnaviv_preclose,
+	.set_busid          = drm_platform_set_busid,
+	.gem_free_object    = etnaviv_gem_free_object,
+	.gem_vm_ops         = &vm_ops,
+	.dumb_create        = msm_gem_dumb_create,
+	.dumb_map_offset    = msm_gem_dumb_map_offset,
+	.dumb_destroy       = drm_gem_dumb_destroy,
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_export   = drm_gem_prime_export,
+	.gem_prime_import   = drm_gem_prime_import,
+	.gem_prime_pin      = msm_gem_prime_pin,
+	.gem_prime_unpin    = msm_gem_prime_unpin,
+	.gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table,
+	.gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
+	.gem_prime_vmap     = etnaviv_gem_prime_vmap,
+	.gem_prime_vunmap   = msm_gem_prime_vunmap,
+#ifdef CONFIG_DEBUG_FS
+	.debugfs_init       = etnaviv_debugfs_init,
+	.debugfs_cleanup    = etnaviv_debugfs_cleanup,
+#endif
+	.ioctls             = etnaviv_ioctls,
+	.num_ioctls         = DRM_ETNAVIV_NUM_IOCTLS,
+	.fops               = &fops,
+	.name               = "etnaviv",
+	.desc               = "etnaviv DRM",
+	.date               = "20130625",
+	.major              = 1,
+	.minor              = 0,
+};
+
+/*
+ * Platform driver:
+ */
+
+static int etnaviv_compare(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	return dev->of_node == np;
+}
+
+static int etnaviv_add_components(struct device *master, struct master *m)
+{
+	struct device_node *child_np;
+	int ret = 0;
+
+	for_each_available_child_of_node(master->of_node, child_np) {
+		DRM_INFO("add child %s\n", child_np->name);
+
+		ret = component_master_add_child(m, etnaviv_compare, child_np);
+		if (ret) {
+			of_node_put(child_np);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int etnaviv_bind(struct device *dev)
+{
+	return drm_platform_init(&etnaviv_drm_driver, to_platform_device(dev));
+}
+
+static void etnaviv_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops etnaviv_master_ops = {
+	.add_components = etnaviv_add_components,
+	.bind = etnaviv_bind,
+	.unbind = etnaviv_unbind,
+};
+
+static int etnaviv_pdev_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+
+	of_platform_populate(node, NULL, NULL, dev);
+
+	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+	return component_master_add(&pdev->dev, &etnaviv_master_ops);
+}
+
+static int etnaviv_pdev_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &etnaviv_master_ops);
+
+	return 0;
+}
+
+static const struct of_device_id dt_match[] = {
+	{ .compatible = "vivante,gccore" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
+static struct platform_driver etnaviv_platform_driver = {
+	.probe      = etnaviv_pdev_probe,
+	.remove     = etnaviv_pdev_remove,
+	.driver     = {
+		.owner  = THIS_MODULE,
+		.name   = "vivante",
+		.of_match_table = dt_match,
+	},
+};
+
+static int __init etnaviv_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&etnaviv_gpu_driver);
+	if (ret != 0)
+		return ret;
+
+	ret = platform_driver_register(&etnaviv_platform_driver);
+	if (ret != 0)
+		platform_driver_unregister(&etnaviv_gpu_driver);
+
+	return ret;
+}
+module_init(etnaviv_init);
+
+static void __exit etnaviv_exit(void)
+{
+	platform_driver_unregister(&etnaviv_gpu_driver);
+	platform_driver_unregister(&etnaviv_platform_driver);
+}
+module_exit(etnaviv_exit);
+
+MODULE_AUTHOR("Rob Clark <[email-masked]");
+MODULE_DESCRIPTION("etnaviv DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
new file mode 100644
index 000000000000..bb256acc88cc
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_DRV_H__
+#define __ETNAVIV_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/iommu.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem.h>
+#include <drm/etnaviv_drm.h>
+
+struct etnaviv_gpu;
+struct etnaviv_mmu;
+struct etnaviv_gem_submit;
+
+struct etnaviv_file_private {
+	/* currently we don't do anything useful with this.. but when
+	 * per-context address spaces are supported we'd keep track of
+	 * the context's page-tables here.
+	 */
+	int dummy;
+};
+
+struct etnaviv_drm_private {
+	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
+	struct etnaviv_file_private *lastctx;
+
+	uint32_t next_fence, completed_fence;
+	wait_queue_head_t fence_event;
+
+	/* list of GEM objects: */
+	struct list_head inactive_list;
+
+	struct workqueue_struct *wq;
+
+	/* registered MMUs: */
+	struct etnaviv_iommu *mmu;
+};
+
+void etnaviv_register_mmu(struct drm_device *dev, struct etnaviv_iommu *mmu);
+
+int etnaviv_wait_fence_interruptable(struct drm_device *dev,
+		struct etnaviv_gpu *gpu, uint32_t fence,
+		struct timespec *timeout);
+void etnaviv_update_fence(struct drm_device *dev, uint32_t fence);
+
+int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
+		struct drm_file *file);
+
+int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj);
+int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+		uint32_t *iova);
+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, int id, uint32_t *iova);
+struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj);
+void msm_gem_put_pages(struct drm_gem_object *obj);
+void etnaviv_gem_put_iova(struct drm_gem_object *obj);
+int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+		struct drm_mode_create_dumb *args);
+int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+		uint32_t handle, uint64_t *offset);
+struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj);
+void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
+void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
+		struct dma_buf_attachment *attach, struct sg_table *sg);
+int msm_gem_prime_pin(struct drm_gem_object *obj);
+void msm_gem_prime_unpin(struct drm_gem_object *obj);
+void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj);
+void *etnaviv_gem_vaddr(struct drm_gem_object *obj);
+dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj);
+void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
+		struct etnaviv_gpu *gpu, bool write, uint32_t fence);
+void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj);
+int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
+		struct timespec *timeout);
+int etnaviv_gem_cpu_fini(struct drm_gem_object *obj);
+void etnaviv_gem_free_object(struct drm_gem_object *obj);
+int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
+		uint32_t size, uint32_t flags, uint32_t *handle);
+struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
+		uint32_t size, uint32_t flags);
+struct drm_gem_object *msm_gem_import(struct drm_device *dev,
+		struct dma_buf_attachment *attach, struct sg_table *sgt);
+u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_gem_submit *submit);
+
+#ifdef CONFIG_DEBUG_FS
+void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m);
+#endif
+
+void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name,
+		const char *dbgname);
+void etnaviv_writel(u32 data, void __iomem *addr);
+u32 etnaviv_readl(const void __iomem *addr);
+
+#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+
+/* returns true if fence a comes after fence b */
+static inline bool fence_after(uint32_t a, uint32_t b)
+{
+	return (int32_t)(a - b) > 0;
+}
+
+static inline bool fence_after_eq(uint32_t a, uint32_t b)
+{
+	return (int32_t)(a - b) >= 0;
+}
+
+static inline bool fence_completed(struct drm_device *dev, uint32_t fence)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+
+	return fence_after_eq(priv->completed_fence, fence);
+}
+
+static inline int align_pitch(int width, int bpp)
+{
+	int bytespp = (bpp + 7) / 8;
+	/* adreno needs pitch aligned to 32 pixels: */
+	return bytespp * ALIGN(width, 32);
+}
+
+#endif /* __ETNAVIV_DRV_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
new file mode 100644
index 000000000000..01ac6e29769b
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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 <linux/spinlock.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include "etnaviv_drv.h"
+#include "etnaviv_gem.h"
+#include "etnaviv_gpu.h"
+#include "etnaviv_mmu.h"
+
+/* called with dev->struct_mutex held */
+static struct page **get_pages(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	if (!etnaviv_obj->pages) {
+		struct drm_device *dev = obj->dev;
+		struct page **p;
+		int npages = obj->size >> PAGE_SHIFT;
+
+		p = drm_gem_get_pages(obj);
+
+		if (IS_ERR(p)) {
+			dev_err(dev->dev, "could not get pages: %ld\n",
+					PTR_ERR(p));
+			return p;
+		}
+
+		etnaviv_obj->sgt = drm_prime_pages_to_sg(p, npages);
+		if (IS_ERR(etnaviv_obj->sgt)) {
+			dev_err(dev->dev, "failed to allocate sgt\n");
+			return ERR_CAST(etnaviv_obj->sgt);
+		}
+
+		etnaviv_obj->pages = p;
+
+		/* For non-cached buffers, ensure the new pages are clean
+		 * because display controller, GPU, etc. are not coherent:
+		 */
+		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
+			dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
+					etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+	}
+
+	return etnaviv_obj->pages;
+}
+
+static void put_pages(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	if (etnaviv_obj->pages) {
+		/* For non-cached buffers, ensure the new pages are clean
+		 * because display controller, GPU, etc. are not coherent:
+		 */
+		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
+			dma_unmap_sg(obj->dev->dev, etnaviv_obj->sgt->sgl,
+					etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+		sg_free_table(etnaviv_obj->sgt);
+		kfree(etnaviv_obj->sgt);
+
+		drm_gem_put_pages(obj, etnaviv_obj->pages, true, false);
+
+		etnaviv_obj->pages = NULL;
+	}
+}
+
+struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	struct page **p;
+	mutex_lock(&dev->struct_mutex);
+	p = get_pages(obj);
+	mutex_unlock(&dev->struct_mutex);
+	return p;
+}
+
+void msm_gem_put_pages(struct drm_gem_object *obj)
+{
+	/* when we start tracking the pin count, then do something here */
+}
+
+static int etnaviv_gem_mmap_cmd(struct drm_gem_object *obj,
+	struct vm_area_struct *vma)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	int ret;
+
+	/*
+	 * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
+	 * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
+	 * the whole buffer.
+	 */
+	vma->vm_flags &= ~VM_PFNMAP;
+	vma->vm_pgoff = 0;
+
+	ret = dma_mmap_coherent(obj->dev->dev, vma,
+				etnaviv_obj->vaddr, etnaviv_obj->paddr,
+				vma->vm_end - vma->vm_start);
+
+	return ret;
+}
+
+static int etnaviv_gem_mmap_obj(struct drm_gem_object *obj,
+		struct vm_area_struct *vma)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	vma->vm_flags &= ~VM_PFNMAP;
+	vma->vm_flags |= VM_MIXEDMAP;
+
+	if (etnaviv_obj->flags & ETNA_BO_WC) {
+		vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+	} else if (etnaviv_obj->flags & ETNA_BO_UNCACHED) {
+		vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
+	} else {
+		/*
+		 * Shunt off cached objs to shmem file so they have their own
+		 * address_space (so unmap_mapping_range does what we want,
+		 * in particular in the case of mmap'd dmabufs)
+		 */
+		fput(vma->vm_file);
+		get_file(obj->filp);
+		vma->vm_pgoff = 0;
+		vma->vm_file  = obj->filp;
+
+		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+	}
+
+	return 0;
+}
+
+int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct etnaviv_gem_object *obj;
+	int ret;
+
+	ret = drm_gem_mmap(filp, vma);
+	if (ret) {
+		DBG("mmap failed: %d", ret);
+		return ret;
+	}
+
+	obj = to_etnaviv_bo(vma->vm_private_data);
+	if (obj->flags & ETNA_BO_CMDSTREAM)
+		ret = etnaviv_gem_mmap_cmd(vma->vm_private_data, vma);
+	else
+		ret = etnaviv_gem_mmap_obj(vma->vm_private_data, vma);
+
+	return ret;
+}
+
+int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct drm_device *dev = obj->dev;
+	struct page **pages;
+	unsigned long pfn;
+	pgoff_t pgoff;
+	int ret;
+
+	/* Make sure we don't parallel update on a fault, nor move or remove
+	 * something from beneath our feet
+	 */
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret)
+		goto out;
+
+	/* make sure we have pages attached now */
+	pages = get_pages(obj);
+	if (IS_ERR(pages)) {
+		ret = PTR_ERR(pages);
+		goto out_unlock;
+	}
+
+	/* We don't use vmf->pgoff since that has the fake offset: */
+	pgoff = ((unsigned long)vmf->virtual_address -
+			vma->vm_start) >> PAGE_SHIFT;
+
+	pfn = page_to_pfn(pages[pgoff]);
+
+	VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
+			pfn, pfn << PAGE_SHIFT);
+
+	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+out:
+	switch (ret) {
+	case -EAGAIN:
+	case 0:
+	case -ERESTARTSYS:
+	case -EINTR:
+	case -EBUSY:
+		/*
+		 * EBUSY is ok: this just means that another thread
+		 * already did the job.
+		 */
+		return VM_FAULT_NOPAGE;
+	case -ENOMEM:
+		return VM_FAULT_OOM;
+	default:
+		return VM_FAULT_SIGBUS;
+	}
+}
+
+/** get mmap offset */
+static uint64_t mmap_offset(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	int ret;
+
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+	/* Make it mmapable */
+	ret = drm_gem_create_mmap_offset(obj);
+
+	if (ret) {
+		dev_err(dev->dev, "could not allocate mmap offset\n");
+		return 0;
+	}
+
+	return drm_vma_node_offset_addr(&obj->vma_node);
+}
+
+uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj)
+{
+	uint64_t offset;
+	mutex_lock(&obj->dev->struct_mutex);
+	offset = mmap_offset(obj);
+	mutex_unlock(&obj->dev->struct_mutex);
+	return offset;
+}
+
+/* should be called under struct_mutex.. although it can be called
+ * from atomic context without struct_mutex to acquire an extra
+ * iova ref if you know one is already held.
+ *
+ * That means when I do eventually need to add support for unpinning
+ * the refcnt counter needs to be atomic_t.
+ */
+int etnaviv_gem_get_iova_locked(struct etnaviv_gpu * gpu, struct drm_gem_object *obj,
+		uint32_t *iova)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	int ret = 0;
+
+	if (!etnaviv_obj->iova  && !(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
+		struct etnaviv_drm_private *priv = obj->dev->dev_private;
+		struct etnaviv_iommu *mmu = priv->mmu;
+		struct page **pages = get_pages(obj);
+		uint32_t offset;
+		struct drm_mm_node *node = NULL;
+
+		if (IS_ERR(pages))
+			return PTR_ERR(pages);
+
+		node = kzalloc(sizeof(*node), GFP_KERNEL);
+		if (!node)
+			return -ENOMEM;
+
+		ret = drm_mm_insert_node(&gpu->mm, node, obj->size, 0,
+				DRM_MM_SEARCH_DEFAULT);
+
+		if (!ret) {
+			offset = node->start;
+			etnaviv_obj->iova = offset;
+			etnaviv_obj->gpu_vram_node = node;
+
+			ret = etnaviv_iommu_map(mmu, offset, etnaviv_obj->sgt,
+					obj->size, IOMMU_READ | IOMMU_WRITE);
+		} else
+			kfree(node);
+	}
+
+	if (!ret)
+		*iova = etnaviv_obj->iova;
+
+	return ret;
+}
+
+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, int id, uint32_t *iova)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	int ret;
+
+	/* this is safe right now because we don't unmap until the
+	 * bo is deleted:
+	 */
+	if (etnaviv_obj->iova) {
+		*iova = etnaviv_obj->iova;
+		return 0;
+	}
+
+	mutex_lock(&obj->dev->struct_mutex);
+	ret = etnaviv_gem_get_iova_locked(gpu, obj, iova);
+	mutex_unlock(&obj->dev->struct_mutex);
+	return ret;
+}
+
+void etnaviv_gem_put_iova(struct drm_gem_object *obj)
+{
+	// XXX TODO ..
+	// NOTE: probably don't need a _locked() version.. we wouldn't
+	// normally unmap here, but instead just mark that it could be
+	// unmapped (if the iova refcnt drops to zero), but then later
+	// if another _get_iova_locked() fails we can start unmapping
+	// things that are no longer needed..
+}
+
+int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+		struct drm_mode_create_dumb *args)
+{
+	args->pitch = align_pitch(args->width, args->bpp);
+	args->size  = PAGE_ALIGN(args->pitch * args->height);
+	/* TODO: re-check flags */
+	return etnaviv_gem_new_handle(dev, file, args->size,
+			ETNA_BO_WC, &args->handle);
+}
+
+int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+		uint32_t handle, uint64_t *offset)
+{
+	struct drm_gem_object *obj;
+	int ret = 0;
+
+	/* GEM does all our handle to object mapping */
+	obj = drm_gem_object_lookup(dev, file, handle);
+	if (obj == NULL) {
+		ret = -ENOENT;
+		goto fail;
+	}
+
+	*offset = etnaviv_gem_mmap_offset(obj);
+
+	drm_gem_object_unreference_unlocked(obj);
+
+fail:
+	return ret;
+}
+
+void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
+	if (!etnaviv_obj->vaddr) {
+		struct page **pages = get_pages(obj);
+		if (IS_ERR(pages))
+			return ERR_CAST(pages);
+		etnaviv_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
+				VM_MAP, pgprot_writecombine(PAGE_KERNEL));
+	}
+	return etnaviv_obj->vaddr;
+}
+
+void *etnaviv_gem_vaddr(struct drm_gem_object *obj)
+{
+	void *ret;
+	mutex_lock(&obj->dev->struct_mutex);
+	ret = etnaviv_gem_vaddr_locked(obj);
+	mutex_unlock(&obj->dev->struct_mutex);
+	return ret;
+}
+
+dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
+
+	return etnaviv_obj->paddr;
+}
+
+void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
+		struct etnaviv_gpu *gpu, bool write, uint32_t fence)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	etnaviv_obj->gpu = gpu;
+	if (write)
+		etnaviv_obj->write_fence = fence;
+	else
+		etnaviv_obj->read_fence = fence;
+	list_del_init(&etnaviv_obj->mm_list);
+	list_add_tail(&etnaviv_obj->mm_list, &gpu->active_list);
+}
+
+void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+	etnaviv_obj->gpu = NULL;
+	etnaviv_obj->read_fence = 0;
+	etnaviv_obj->write_fence = 0;
+	list_del_init(&etnaviv_obj->mm_list);
+	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
+}
+
+int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
+		struct timespec *timeout)
+{
+/*
+	struct drm_device *dev = obj->dev;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+*/
+	int ret = 0;
+	/* TODO */
+#if 0
+	if (is_active(etnaviv_obj)) {
+		uint32_t fence = 0;
+
+		if (op & ETNA_PREP_READ)
+			fence = etnaviv_obj->write_fence;
+		if (op & ETNA_PREP_WRITE)
+			fence = max(fence, etnaviv_obj->read_fence);
+		if (op & ETNA_PREP_NOSYNC)
+			timeout = NULL;
+
+		ret = etnaviv_wait_fence_interruptable(dev, etnaviv_obj->gpu,
+			       fence, timeout);
+	}
+
+	/* TODO cache maintenance */
+#endif
+	return ret;
+}
+
+int etnaviv_gem_cpu_fini(struct drm_gem_object *obj)
+{
+	/* TODO cache maintenance */
+	return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
+{
+	struct drm_device *dev = obj->dev;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	uint64_t off = drm_vma_node_start(&obj->vma_node);
+
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+	seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %zd\n",
+			etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I',
+			etnaviv_obj->read_fence, etnaviv_obj->write_fence,
+			obj->name, obj->refcount.refcount.counter,
+			off, etnaviv_obj->vaddr, obj->size);
+}
+
+void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m)
+{
+	struct etnaviv_gem_object *etnaviv_obj;
+	int count = 0;
+	size_t size = 0;
+
+	list_for_each_entry(etnaviv_obj, list, mm_list) {
+		struct drm_gem_object *obj = &etnaviv_obj->base;
+		seq_puts(m, "   ");
+		etnaviv_gem_describe(obj, m);
+		count++;
+		size += obj->size;
+	}
+
+	seq_printf(m, "Total %d objects, %zu bytes\n", count, size);
+}
+#endif
+
+static void etnaviv_free_cmd(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	drm_gem_free_mmap_offset(obj);
+
+	dma_free_coherent(obj->dev->dev, obj->size,
+		etnaviv_obj->vaddr, etnaviv_obj->paddr);
+
+	drm_gem_object_release(obj);
+}
+
+static void etnaviv_free_obj(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct etnaviv_drm_private *priv = obj->dev->dev_private;
+	struct etnaviv_iommu *mmu = priv->mmu;
+
+	if (mmu && etnaviv_obj->iova) {
+		uint32_t offset = etnaviv_obj->gpu_vram_node->start;
+		etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt, obj->size);
+		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
+		kfree(etnaviv_obj->gpu_vram_node);
+	}
+
+	drm_gem_free_mmap_offset(obj);
+
+	if (obj->import_attach) {
+		if (etnaviv_obj->vaddr)
+			dma_buf_vunmap(obj->import_attach->dmabuf, etnaviv_obj->vaddr);
+
+		/* Don't drop the pages for imported dmabuf, as they are not
+		 * ours, just free the array we allocated:
+		 */
+		if (etnaviv_obj->pages)
+			drm_free_large(etnaviv_obj->pages);
+
+		drm_prime_gem_destroy(obj, etnaviv_obj->sgt);
+	} else {
+		if (etnaviv_obj->vaddr)
+			vunmap(etnaviv_obj->vaddr);
+		put_pages(obj);
+	}
+
+	if (etnaviv_obj->resv == &etnaviv_obj->_resv)
+		reservation_object_fini(etnaviv_obj->resv);
+
+	drm_gem_object_release(obj);
+}
+
+void etnaviv_gem_free_object(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+	/* object should not be on active list: */
+	WARN_ON(is_active(etnaviv_obj));
+
+	list_del(&etnaviv_obj->mm_list);
+
+	if (etnaviv_obj->flags & ETNA_BO_CMDSTREAM)
+		etnaviv_free_cmd(obj);
+	else
+		etnaviv_free_obj(obj);
+
+	kfree(etnaviv_obj);
+}
+
+/* convenience method to construct a GEM buffer object, and userspace handle */
+int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
+		uint32_t size, uint32_t flags, uint32_t *handle)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret)
+		return ret;
+
+	obj = etnaviv_gem_new(dev, size, flags);
+
+	mutex_unlock(&dev->struct_mutex);
+
+	if (IS_ERR(obj))
+		return PTR_ERR(obj);
+
+	ret = drm_gem_handle_create(file, obj, handle);
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+static int etnaviv_gem_new_impl(struct drm_device *dev,
+		uint32_t size, uint32_t flags,
+		struct drm_gem_object **obj)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gem_object *etnaviv_obj;
+	unsigned sz = sizeof(*etnaviv_obj);
+	bool valid = true;
+
+	/* validate flags */
+	if (flags & ETNA_BO_CMDSTREAM) {
+		if ((flags & ETNA_BO_CACHE_MASK) != 0)
+			valid = false;
+	} else {
+		switch (flags & ETNA_BO_CACHE_MASK) {
+		case ETNA_BO_UNCACHED:
+		case ETNA_BO_CACHED:
+		case ETNA_BO_WC:
+			break;
+		default:
+			valid = false;
+		}
+	}
+
+	if (!valid) {
+		dev_err(dev->dev, "invalid cache flag: %x (cmd: %d)\n",
+				(flags & ETNA_BO_CACHE_MASK),
+				(flags & ETNA_BO_CMDSTREAM));
+		return -EINVAL;
+	}
+
+	etnaviv_obj = kzalloc(sz, GFP_KERNEL);
+	if (!etnaviv_obj)
+		return -ENOMEM;
+
+	if (flags & ETNA_BO_CMDSTREAM) {
+		etnaviv_obj->vaddr = dma_alloc_coherent(dev->dev, size,
+				&etnaviv_obj->paddr, GFP_KERNEL);
+
+		if (!etnaviv_obj->vaddr) {
+			kfree(etnaviv_obj);
+			return -ENOMEM;
+		}
+	}
+
+	etnaviv_obj->flags = flags;
+
+	etnaviv_obj->resv = &etnaviv_obj->_resv;
+	reservation_object_init(etnaviv_obj->resv);
+
+	INIT_LIST_HEAD(&etnaviv_obj->submit_entry);
+	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
+
+	*obj = &etnaviv_obj->base;
+
+	return 0;
+}
+
+struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
+		uint32_t size, uint32_t flags)
+{
+	struct drm_gem_object *obj = NULL;
+	int ret;
+
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+	size = PAGE_ALIGN(size);
+
+	ret = etnaviv_gem_new_impl(dev, size, flags, &obj);
+	if (ret)
+		goto fail;
+
+	ret = 0;
+	if (flags & ETNA_BO_CMDSTREAM)
+		drm_gem_private_object_init(dev, obj, size);
+	else
+		ret = drm_gem_object_init(dev, obj, size);
+
+	if (ret)
+		goto fail;
+
+	return obj;
+
+fail:
+	if (obj)
+		drm_gem_object_unreference(obj);
+
+	return ERR_PTR(ret);
+}
+
+struct drm_gem_object *msm_gem_import(struct drm_device *dev,
+		struct dma_buf_attachment *attach, struct sg_table *sgt)
+{
+	struct etnaviv_gem_object *etnaviv_obj;
+	struct drm_gem_object *obj;
+	int ret, npages;
+	size_t size = PAGE_ALIGN(attach->dmabuf->size);
+
+	ret = etnaviv_gem_new_impl(dev, size, ETNA_BO_WC, &obj);
+	if (ret)
+		goto fail;
+
+	drm_gem_private_object_init(dev, obj, size);
+
+	npages = size / PAGE_SIZE;
+
+	etnaviv_obj = to_etnaviv_bo(obj);
+	etnaviv_obj->sgt = sgt;
+	etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
+	if (!etnaviv_obj->pages) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages, NULL, npages);
+	if (ret)
+		goto fail;
+
+	return obj;
+
+fail:
+	if (obj)
+		drm_gem_object_unreference_unlocked(obj);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
new file mode 100644
index 000000000000..5ce38d9f7a19
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_GEM_H__
+#define __ETNAVIV_GEM_H__
+
+#include <linux/reservation.h>
+#include "etnaviv_drv.h"
+
+struct etnaviv_gem_object {
+	struct drm_gem_object base;
+
+	uint32_t flags;
+
+	/* And object is either:
+	 *  inactive - on priv->inactive_list
+	 *  active   - on one one of the gpu's active_list..  well, at
+	 *     least for now we don't have (I don't think) hw sync between
+	 *     2d and 3d one devices which have both, meaning we need to
+	 *     block on submit if a bo is already on other ring
+	 *
+	 */
+	struct list_head mm_list;
+	struct etnaviv_gpu *gpu;     /* non-null if active */
+	uint32_t read_fence, write_fence;
+
+	/* Transiently in the process of submit ioctl, objects associated
+	 * with the submit are on submit->bo_list.. this only lasts for
+	 * the duration of the ioctl, so one bo can never be on multiple
+	 * submit lists.
+	 */
+	struct list_head submit_entry;
+
+	struct page **pages;
+	struct sg_table *sgt;
+	void *vaddr;
+	uint32_t iova;
+
+	/* for ETNA_BO_CMDSTREAM */
+	dma_addr_t paddr;
+
+	/* normally (resv == &_resv) except for imported bo's */
+	struct reservation_object *resv;
+	struct reservation_object _resv;
+
+	struct drm_mm_node *gpu_vram_node;
+
+	/* for buffer manipulation during submit */
+	bool is_ring_buffer;
+	u32 offset;
+};
+#define to_etnaviv_bo(x) container_of(x, struct etnaviv_gem_object, base)
+
+static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
+{
+	return etnaviv_obj->gpu != NULL;
+}
+
+#define MAX_CMDS 4
+
+/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
+ * associated with the cmdstream submission for synchronization (and
+ * make it easier to unwind when things go wrong, etc).  This only
+ * lasts for the duration of the submit-ioctl.
+ */
+struct etnaviv_gem_submit {
+	struct drm_device *dev;
+	struct etnaviv_gpu *gpu;
+	struct list_head bo_list;
+	struct ww_acquire_ctx ticket;
+	uint32_t fence;
+	bool valid;
+	unsigned int nr_cmds;
+	unsigned int nr_bos;
+	struct {
+		uint32_t type;
+		uint32_t size;  /* in dwords */
+		struct etnaviv_gem_object *obj;
+	} cmd[MAX_CMDS];
+	struct {
+		uint32_t flags;
+		struct etnaviv_gem_object *obj;
+		uint32_t iova;
+	} bos[0];
+};
+
+#endif /* __ETNAVIV_GEM_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
new file mode 100644
index 000000000000..e1a8352ff59d
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_drv.h"
+#include "etnaviv_gem.h"
+
+
+struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj= to_etnaviv_bo(obj);
+	BUG_ON(!etnaviv_obj->sgt);  /* should have already pinned! */
+	return etnaviv_obj->sgt;
+}
+
+void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj)
+{
+	return etnaviv_gem_vaddr(obj);
+}
+
+void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+	/* TODO msm_gem_vunmap() */
+}
+
+struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
+		struct dma_buf_attachment *attach, struct sg_table *sg)
+{
+	return msm_gem_import(dev, attach, sg);
+}
+
+int msm_gem_prime_pin(struct drm_gem_object *obj)
+{
+	if (!obj->import_attach)
+		etnaviv_gem_get_pages(obj);
+	return 0;
+}
+
+void msm_gem_prime_unpin(struct drm_gem_object *obj)
+{
+	if (!obj->import_attach)
+		msm_gem_put_pages(obj);
+}
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
new file mode 100644
index 000000000000..78c56adfcffc
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_drv.h"
+#include "etnaviv_gpu.h"
+#include "etnaviv_gem.h"
+
+/*
+ * Cmdstream submission:
+ */
+
+#define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
+/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
+#define BO_VALID    0x8000
+#define BO_LOCKED   0x4000
+#define BO_PINNED   0x2000
+
+static inline void __user *to_user_ptr(u64 address)
+{
+	return (void __user *)(uintptr_t)address;
+}
+
+static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
+		struct etnaviv_gpu *gpu, int nr)
+{
+	struct etnaviv_gem_submit *submit;
+	int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0]));
+
+	submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
+	if (submit) {
+		submit->dev = dev;
+		submit->gpu = gpu;
+
+		/* initially, until copy_from_user() and bo lookup succeeds: */
+		submit->nr_bos = 0;
+		submit->nr_cmds = 0;
+
+		INIT_LIST_HEAD(&submit->bo_list);
+		ww_acquire_init(&submit->ticket, &reservation_ww_class);
+	}
+
+	return submit;
+}
+
+static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
+		struct drm_etnaviv_gem_submit *args, struct drm_file *file)
+{
+	unsigned i;
+	int ret = 0;
+
+	spin_lock(&file->table_lock);
+
+	for (i = 0; i < args->nr_bos; i++) {
+		struct drm_etnaviv_gem_submit_bo submit_bo;
+		struct drm_gem_object *obj;
+		struct etnaviv_gem_object *etnaviv_obj;
+		void __user *userptr =
+			to_user_ptr(args->bos + (i * sizeof(submit_bo)));
+
+		ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
+		if (ret) {
+			ret = -EFAULT;
+			goto out_unlock;
+		}
+
+		if (submit_bo.flags & BO_INVALID_FLAGS) {
+			DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		submit->bos[i].flags = submit_bo.flags;
+		/* in validate_objects() we figure out if this is true: */
+		submit->bos[i].iova  = submit_bo.presumed;
+
+		/* normally use drm_gem_object_lookup(), but for bulk lookup
+		 * all under single table_lock just hit object_idr directly:
+		 */
+		obj = idr_find(&file->object_idr, submit_bo.handle);
+		if (!obj) {
+			DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		etnaviv_obj = to_etnaviv_bo(obj);
+
+		if (!list_empty(&etnaviv_obj->submit_entry)) {
+			DRM_ERROR("handle %u at index %u already on submit list\n",
+					submit_bo.handle, i);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		drm_gem_object_reference(obj);
+
+		submit->bos[i].obj = etnaviv_obj;
+
+		list_add_tail(&etnaviv_obj->submit_entry, &submit->bo_list);
+	}
+
+out_unlock:
+	submit->nr_bos = i;
+	spin_unlock(&file->table_lock);
+
+	return ret;
+}
+
+static void submit_unlock_unpin_bo(struct etnaviv_gem_submit *submit, int i)
+{
+	struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+
+	if (submit->bos[i].flags & BO_PINNED)
+		etnaviv_gem_put_iova(&etnaviv_obj->base);
+
+	if (submit->bos[i].flags & BO_LOCKED)
+		ww_mutex_unlock(&etnaviv_obj->resv->lock);
+
+	if (!(submit->bos[i].flags & BO_VALID))
+		submit->bos[i].iova = 0;
+
+	submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
+}
+
+/* This is where we make sure all the bo's are reserved and pin'd: */
+static int submit_validate_objects(struct etnaviv_gem_submit *submit)
+{
+	int contended, slow_locked = -1, i, ret = 0;
+
+retry:
+	submit->valid = true;
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+		uint32_t iova;
+
+		if (slow_locked == i)
+			slow_locked = -1;
+
+		contended = i;
+
+		if (!(submit->bos[i].flags & BO_LOCKED)) {
+			ret = ww_mutex_lock_interruptible(&etnaviv_obj->resv->lock,
+					&submit->ticket);
+			if (ret)
+				goto fail;
+			submit->bos[i].flags |= BO_LOCKED;
+		}
+
+
+		/* if locking succeeded, pin bo: */
+		ret = etnaviv_gem_get_iova_locked(submit->gpu, &etnaviv_obj->base, &iova);
+
+		/* this would break the logic in the fail path.. there is no
+		 * reason for this to happen, but just to be on the safe side
+		 * let's notice if this starts happening in the future:
+		 */
+		WARN_ON(ret == -EDEADLK);
+
+		if (ret)
+			goto fail;
+
+		submit->bos[i].flags |= BO_PINNED;
+
+		if (iova == submit->bos[i].iova) {
+			submit->bos[i].flags |= BO_VALID;
+		} else {
+			submit->bos[i].iova = iova;
+			submit->bos[i].flags &= ~BO_VALID;
+			submit->valid = false;
+		}
+	}
+
+	ww_acquire_done(&submit->ticket);
+
+	return 0;
+
+fail:
+	for (; i >= 0; i--)
+		submit_unlock_unpin_bo(submit, i);
+
+	if (slow_locked > 0)
+		submit_unlock_unpin_bo(submit, slow_locked);
+
+	if (ret == -EDEADLK) {
+		struct etnaviv_gem_object *etnaviv_obj = submit->bos[contended].obj;
+		/* we lost out in a seqno race, lock and retry.. */
+		ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock,
+				&submit->ticket);
+		if (!ret) {
+			submit->bos[contended].flags |= BO_LOCKED;
+			slow_locked = contended;
+			goto retry;
+		}
+	}
+
+	return ret;
+}
+
+static int submit_bo(struct etnaviv_gem_submit *submit, uint32_t idx,
+		struct etnaviv_gem_object **obj, uint32_t *iova, bool *valid)
+{
+	if (idx >= submit->nr_bos) {
+		DRM_ERROR("invalid buffer index: %u (out of %u)\n",
+				idx, submit->nr_bos);
+		return -EINVAL;
+	}
+
+	if (obj)
+		*obj = submit->bos[idx].obj;
+	if (iova)
+		*iova = submit->bos[idx].iova;
+	if (valid)
+		*valid = !!(submit->bos[idx].flags & BO_VALID);
+
+	return 0;
+}
+
+/* process the reloc's and patch up the cmdstream as needed: */
+static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_object *obj,
+		uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
+{
+	uint32_t i, last_offset = 0;
+	uint32_t *ptr = obj->vaddr;
+	int ret;
+
+	if (offset % 4) {
+		DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < nr_relocs; i++) {
+		struct drm_etnaviv_gem_submit_reloc submit_reloc;
+		struct etnaviv_gem_object *bobj;
+		void __user *userptr =
+			to_user_ptr(relocs + (i * sizeof(submit_reloc)));
+		uint32_t iova, off;
+		bool valid;
+
+		ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
+		if (ret)
+			return -EFAULT;
+
+		if (submit_reloc.submit_offset % 4) {
+			DRM_ERROR("non-aligned reloc offset: %u\n",
+					submit_reloc.submit_offset);
+			return -EINVAL;
+		}
+
+		/* offset in dwords: */
+		off = submit_reloc.submit_offset / 4;
+
+		if ((off >= (obj->base.size / 4)) ||
+				(off < last_offset)) {
+			DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
+			return -EINVAL;
+		}
+
+		ret = submit_bo(submit, submit_reloc.reloc_idx, &bobj,
+				&iova, &valid);
+		if (ret)
+			return ret;
+
+		if (valid)
+			continue;
+
+		if (submit_reloc.reloc_offset >=
+		    bobj->base.size - sizeof(*ptr)) {
+			DRM_ERROR("relocation %u outside object", i);
+			return -EINVAL;
+		}
+
+		iova += submit_reloc.reloc_offset;
+
+		if (submit_reloc.shift < 0)
+			iova >>= -submit_reloc.shift;
+		else
+			iova <<= submit_reloc.shift;
+
+		ptr[off] = iova | submit_reloc.or;
+
+		last_offset = off;
+	}
+
+	return 0;
+}
+
+static void submit_cleanup(struct etnaviv_gem_submit *submit, bool fail)
+{
+	unsigned i;
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+		submit_unlock_unpin_bo(submit, i);
+		list_del_init(&etnaviv_obj->submit_entry);
+		drm_gem_object_unreference(&etnaviv_obj->base);
+	}
+
+	ww_acquire_fini(&submit->ticket);
+	kfree(submit);
+}
+
+int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
+		struct drm_file *file)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct drm_etnaviv_gem_submit *args = data;
+	struct etnaviv_file_private *ctx = file->driver_priv;
+	struct etnaviv_gem_submit *submit;
+	struct etnaviv_gpu *gpu;
+	unsigned i;
+	int ret;
+
+	if (args->pipe >= ETNA_MAX_PIPES)
+		return -EINVAL;
+
+	gpu = priv->gpu[args->pipe];
+	if (!gpu)
+		return -ENXIO;
+
+	if (args->nr_cmds > MAX_CMDS)
+		return -EINVAL;
+
+	mutex_lock(&dev->struct_mutex);
+
+	submit = submit_create(dev, gpu, args->nr_bos);
+	if (!submit) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = submit_lookup_objects(submit, args, file);
+	if (ret)
+		goto out;
+
+	ret = submit_validate_objects(submit);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < args->nr_cmds; i++) {
+		struct drm_etnaviv_gem_submit_cmd submit_cmd;
+		void __user *userptr =
+			to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
+		struct etnaviv_gem_object *etnaviv_obj;
+		unsigned max_size;
+
+		ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
+		if (ret) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		ret = submit_bo(submit, submit_cmd.submit_idx,
+				&etnaviv_obj, NULL, NULL);
+		if (ret)
+			goto out;
+
+		if (!(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
+			DRM_ERROR("cmdstream bo has flag ETNA_BO_CMDSTREAM not set\n");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		if (submit_cmd.size % 4) {
+			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
+					submit_cmd.size);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/*
+		 * We must have space to add a LINK command at the end of
+		 * the command buffer.
+		 */
+		max_size = etnaviv_obj->base.size - 8;
+
+		if (submit_cmd.size > max_size ||
+		    submit_cmd.submit_offset > max_size - submit_cmd.size) {
+			DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		submit->cmd[i].type = submit_cmd.type;
+		submit->cmd[i].size = submit_cmd.size / 4;
+		submit->cmd[i].obj = etnaviv_obj;
+
+		if (submit->valid)
+			continue;
+
+		ret = submit_reloc(submit, etnaviv_obj, submit_cmd.submit_offset,
+				submit_cmd.nr_relocs, submit_cmd.relocs);
+		if (ret)
+			goto out;
+	}
+
+	submit->nr_cmds = i;
+
+	ret = etnaviv_gpu_submit(gpu, submit, ctx);
+
+	args->fence = submit->fence;
+
+out:
+	if (submit)
+		submit_cleanup(submit, !!ret);
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
new file mode 100644
index 000000000000..c6396216eb9a
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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 <linux/component.h>
+#include <linux/of_device.h>
+#include "etnaviv_gpu.h"
+#include "etnaviv_gem.h"
+#include "etnaviv_mmu.h"
+#include "etnaviv_iommu.h"
+#include "etnaviv_iommu_v2.h"
+#include "common.xml.h"
+#include "state.xml.h"
+#include "state_hi.xml.h"
+#include "cmdstream.xml.h"
+
+
+/*
+ * Driver functions:
+ */
+
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param, uint64_t *value)
+{
+	switch (param) {
+	case ETNAVIV_PARAM_GPU_MODEL:
+		*value = gpu->identity.model;
+		break;
+
+	case ETNAVIV_PARAM_GPU_REVISION:
+		*value = gpu->identity.revision;
+		break;
+
+	case ETNAVIV_PARAM_GPU_FEATURES_0:
+		*value = gpu->identity.features;
+		break;
+
+	case ETNAVIV_PARAM_GPU_FEATURES_1:
+		*value = gpu->identity.minor_features0;
+		break;
+
+	case ETNAVIV_PARAM_GPU_FEATURES_2:
+		*value = gpu->identity.minor_features1;
+		break;
+
+	case ETNAVIV_PARAM_GPU_FEATURES_3:
+		*value = gpu->identity.minor_features2;
+		break;
+
+	case ETNAVIV_PARAM_GPU_FEATURES_4:
+		*value = gpu->identity.minor_features3;
+		break;
+
+	case ETNAVIV_PARAM_GPU_STREAM_COUNT:
+		*value = gpu->identity.stream_count;
+		break;
+
+	case ETNAVIV_PARAM_GPU_REGISTER_MAX:
+		*value = gpu->identity.register_max;
+		break;
+
+	case ETNAVIV_PARAM_GPU_THREAD_COUNT:
+		*value = gpu->identity.thread_count;
+		break;
+
+	case ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE:
+		*value = gpu->identity.vertex_cache_size;
+		break;
+
+	case ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT:
+		*value = gpu->identity.shader_core_count;
+		break;
+
+	case ETNAVIV_PARAM_GPU_PIXEL_PIPES:
+		*value = gpu->identity.pixel_pipes;
+		break;
+
+	case ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE:
+		*value = gpu->identity.vertex_output_buffer_size;
+		break;
+
+	case ETNAVIV_PARAM_GPU_BUFFER_SIZE:
+		*value = gpu->identity.buffer_size;
+		break;
+
+	case ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT:
+		*value = gpu->identity.instruction_count;
+		break;
+
+	case ETNAVIV_PARAM_GPU_NUM_CONSTANTS:
+		*value = gpu->identity.num_constants;
+		break;
+
+	default:
+		DBG("%s: invalid param: %u", gpu->name, param);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
+{
+	if (gpu->identity.minor_features0 & chipMinorFeatures0_MORE_MINOR_FEATURES) {
+		u32 specs[2];
+
+		specs[0] = gpu_read(gpu, VIVS_HI_CHIP_SPECS);
+		specs[1] = gpu_read(gpu, VIVS_HI_CHIP_SPECS_2);
+
+		gpu->identity.stream_count = (specs[0] & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK)
+				>> VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT;
+		gpu->identity.register_max = (specs[0] & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK)
+				>> VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT;
+		gpu->identity.thread_count = (specs[0] & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK)
+				>> VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT;
+		gpu->identity.vertex_cache_size = (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK)
+				>> VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT;
+		gpu->identity.shader_core_count = (specs[0] & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK)
+				>> VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT;
+		gpu->identity.pixel_pipes = (specs[0] & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK)
+				>> VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT;
+		gpu->identity.vertex_output_buffer_size = (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK)
+				>> VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT;
+
+		gpu->identity.buffer_size = (specs[1] & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK)
+				>> VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT;
+		gpu->identity.instruction_count = (specs[1] & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK)
+				>> VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT;
+		gpu->identity.num_constants = (specs[1] & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK)
+				>> VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT;
+
+		gpu->identity.register_max = 1 << gpu->identity.register_max;
+		gpu->identity.thread_count = 1 << gpu->identity.thread_count;
+		gpu->identity.vertex_output_buffer_size = 1 << gpu->identity.vertex_output_buffer_size;
+	} else {
+		dev_err(gpu->dev->dev, "TODO: determine GPU specs based on model\n");
+	}
+
+	switch (gpu->identity.instruction_count) {
+	case 0:
+		gpu->identity.instruction_count = 256;
+		break;
+
+	case 1:
+		gpu->identity.instruction_count = 1024;
+		break;
+
+	case 2:
+		gpu->identity.instruction_count = 2048;
+		break;
+
+	default:
+		gpu->identity.instruction_count = 256;
+		break;
+	}
+
+	dev_info(gpu->dev->dev, "stream_count:  %x\n", gpu->identity.stream_count);
+	dev_info(gpu->dev->dev, "register_max: %x\n", gpu->identity.register_max);
+	dev_info(gpu->dev->dev, "thread_count: %x\n", gpu->identity.thread_count);
+	dev_info(gpu->dev->dev, "vertex_cache_size: %x\n", gpu->identity.vertex_cache_size);
+	dev_info(gpu->dev->dev, "shader_core_count: %x\n", gpu->identity.shader_core_count);
+	dev_info(gpu->dev->dev, "pixel_pipes: %x\n", gpu->identity.pixel_pipes);
+	dev_info(gpu->dev->dev, "vertex_output_buffer_size: %x\n", gpu->identity.vertex_output_buffer_size);
+	dev_info(gpu->dev->dev, "buffer_size: %x\n", gpu->identity.buffer_size);
+	dev_info(gpu->dev->dev, "instruction_count: %x\n", gpu->identity.instruction_count);
+	dev_info(gpu->dev->dev, "num_constants: %x\n", gpu->identity.num_constants);
+}
+
+static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
+{
+	u32 chipIdentity;
+
+	chipIdentity = gpu_read(gpu, VIVS_HI_CHIP_IDENTITY);
+
+	/* Special case for older graphic cores. */
+	if (VIVS_HI_CHIP_IDENTITY_FAMILY(chipIdentity) ==  0x01) {
+		gpu->identity.model    = 0x500; /* gc500 */
+		gpu->identity.revision = VIVS_HI_CHIP_IDENTITY_REVISION(chipIdentity);
+	} else {
+
+		gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL);
+		gpu->identity.revision = gpu_read(gpu, VIVS_HI_CHIP_REV);
+
+		/* !!!! HACK ALERT !!!! */
+		/* Because people change device IDs without letting software know
+		** about it - here is the hack to make it all look the same.  Only
+		** for GC400 family.  Next time - TELL ME!!! */
+		if (((gpu->identity.model & 0xFF00) == 0x0400)
+		&& (gpu->identity.model != 0x0420)) {
+			gpu->identity.model = gpu->identity.model & 0x0400;
+		}
+
+		/* An other special case */
+		if ((gpu->identity.model    == 0x300)
+		&&  (gpu->identity.revision == 0x2201)) {
+			u32 chipDate = gpu_read(gpu, VIVS_HI_CHIP_DATE);
+			u32 chipTime = gpu_read(gpu, VIVS_HI_CHIP_TIME);
+
+			if ((chipDate == 0x20080814) && (chipTime == 0x12051100)) {
+				/* This IP has an ECO; put the correct revision in it. */
+				gpu->identity.revision = 0x1051;
+			}
+		}
+	}
+
+	dev_info(gpu->dev->dev, "model: %x - revision %x\n",
+		 gpu->identity.model, gpu->identity.revision);
+
+	gpu->identity.features = gpu_read(gpu, VIVS_HI_CHIP_FEATURE);
+
+	/* Disable fast clear on GC700. */
+	if (gpu->identity.model == 0x700)
+		gpu->identity.features &= ~BIT(0);
+
+	if (((gpu->identity.model == 0x500) && (gpu->identity.revision < 2))
+	||  ((gpu->identity.model == 0x300) && (gpu->identity.revision < 0x2000))) {
+
+		/* GC500 rev 1.x and GC300 rev < 2.0 doesn't have these registers. */
+		gpu->identity.minor_features0 = 0;
+		gpu->identity.minor_features1 = 0;
+		gpu->identity.minor_features2 = 0;
+		gpu->identity.minor_features3 = 0;
+	} else
+		gpu->identity.minor_features0 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_0);
+
+	if (gpu->identity.minor_features0 & BIT(21)) {
+		gpu->identity.minor_features1 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_1);
+		gpu->identity.minor_features2 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_2);
+		gpu->identity.minor_features3 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3);
+	}
+
+	dev_info(gpu->dev->dev, "minor_features:  %x\n", gpu->identity.minor_features0);
+	dev_info(gpu->dev->dev, "minor_features1: %x\n", gpu->identity.minor_features1);
+	dev_info(gpu->dev->dev, "minor_features2: %x\n", gpu->identity.minor_features2);
+	dev_info(gpu->dev->dev, "minor_features3: %x\n", gpu->identity.minor_features3);
+
+	etnaviv_hw_specs(gpu);
+}
+
+static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
+{
+	u32 control, idle;
+
+	/* TODO
+	 *
+	 * - clock gating
+	 * - puls eater
+	 * - what about VG?
+	 */
+
+	while (true) {
+		control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
+			  VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
+
+		/* enable clock */
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control |
+			  VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD);
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
+		/* Wait for stable clock.  Vivante's code waited for 1ms */
+		usleep_range(1000, 10000);
+
+		/* isolate the GPU. */
+		control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU;
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
+		/* set soft reset. */
+		control |= VIVS_HI_CLOCK_CONTROL_SOFT_RESET;
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
+		/* wait for reset. */
+		msleep(1);
+
+		/* reset soft reset bit. */
+		control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET;
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
+		/* reset GPU isolation. */
+		control &= ~VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU;
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
+		/* read idle register. */
+		idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
+
+		/* try reseting again if FE it not idle */
+		if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) {
+			dev_dbg(gpu->dev->dev, "%s: FE is not idle\n", gpu->name);
+			continue;
+		}
+
+		/* read reset register. */
+		control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
+
+		/* is the GPU idle? */
+		if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0)
+		|| ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) {
+			dev_dbg(gpu->dev->dev, "%s: GPU is not idle\n", gpu->name);
+			continue;
+		}
+
+		break;
+	}
+
+	/* We rely on the GPU running, so program the clock */
+	control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
+		  VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
+
+	/* enable clock */
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control |
+		  VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD);
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+}
+
+int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
+{
+	int ret, i;
+	u32 words; /* 32 bit words */
+	struct iommu_domain *iommu;
+	enum etnaviv_iommu_version version;
+	bool mmuv2;
+
+	etnaviv_hw_identify(gpu);
+	etnaviv_hw_reset(gpu);
+
+	/* set base addresses */
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, 0x0);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, 0x0);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, 0x0);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, 0x0);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, 0x0);
+
+	/* Setup IOMMU.. eventually we will (I think) do this once per context
+	 * and have separate page tables per context.  For now, to keep things
+	 * simple and to get something working, just use a single address space:
+	 */
+	mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION;
+	dev_dbg(gpu->dev->dev, "mmuv2: %d\n", mmuv2);
+
+	if (!mmuv2) {
+		iommu = etnaviv_iommu_domain_alloc(gpu);
+		version = ETNAVIV_IOMMU_V1;
+	} else {
+		iommu = etnaviv_iommu_v2_domain_alloc(gpu);
+		version = ETNAVIV_IOMMU_V2;
+	}
+
+	if (!iommu) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* TODO: we will leak here memory - fix it! */
+
+	gpu->mmu = etnaviv_iommu_new(gpu->dev, iommu, version);
+	if (!gpu->mmu) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	etnaviv_register_mmu(gpu->dev, gpu->mmu);
+
+	/* Create buffer: */
+	gpu->buffer = etnaviv_gem_new(gpu->dev, PAGE_SIZE, ETNA_BO_CMDSTREAM);
+	if (IS_ERR(gpu->buffer)) {
+		ret = PTR_ERR(gpu->buffer);
+		gpu->buffer = NULL;
+		dev_err(gpu->dev->dev, "could not create buffer: %d\n", ret);
+		goto fail;
+	}
+
+	/* Setup event management */
+	spin_lock_init(&gpu->event_spinlock);
+	init_completion(&gpu->event_free);
+	for (i = 0; i < ARRAY_SIZE(gpu->event_used); i++) {
+		gpu->event_used[i] = false;
+		complete(&gpu->event_free);
+	}
+
+	/* Start command processor */
+	words = etnaviv_buffer_init(gpu);
+
+	/* convert number of 32 bit words to number of 64 bit words */
+	words = ALIGN(words, 2) / 2;
+
+	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
+	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, etnaviv_gem_paddr_locked(gpu->buffer));
+	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL, VIVS_FE_COMMAND_CONTROL_ENABLE | VIVS_FE_COMMAND_CONTROL_PREFETCH(words));
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+struct dma_debug {
+	u32 address[2];
+	u32 state[2];
+};
+
+static void verify_dma(struct etnaviv_gpu *gpu, struct dma_debug *debug)
+{
+	u32 i;
+
+	debug->address[0] = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+	debug->state[0]   = gpu_read(gpu, VIVS_FE_DMA_DEBUG_STATE);
+
+	for (i = 0; i < 500; i++) {
+		debug->address[1] = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+		debug->state[1]   = gpu_read(gpu, VIVS_FE_DMA_DEBUG_STATE);
+
+		if (debug->address[0] != debug->address[1])
+			break;
+
+		if (debug->state[0] != debug->state[1])
+			break;
+	}
+}
+
+void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
+{
+	struct dma_debug debug;
+	u32 dma_lo = gpu_read(gpu, VIVS_FE_DMA_LOW);
+	u32 dma_hi = gpu_read(gpu, VIVS_FE_DMA_HIGH);
+	u32 axi = gpu_read(gpu, VIVS_HI_AXI_STATUS);
+	u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
+
+	verify_dma(gpu, &debug);
+
+	seq_printf(m, "\taxi: 0x%08x\n", axi);
+	seq_printf(m, "\tidle: 0x%08x\n", idle);
+	if ((idle & VIVS_HI_IDLE_STATE_FE) == 0)
+		seq_puts(m, "\t FE is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_DE) == 0)
+		seq_puts(m, "\t DE is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_PE) == 0)
+		seq_puts(m, "\t PE is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_SH) == 0)
+		seq_puts(m, "\t SH is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_PA) == 0)
+		seq_puts(m, "\t PA is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_SE) == 0)
+		seq_puts(m, "\t SE is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_RA) == 0)
+		seq_puts(m, "\t RA is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_TX) == 0)
+		seq_puts(m, "\t TX is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_VG) == 0)
+		seq_puts(m, "\t VG is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_IM) == 0)
+		seq_puts(m, "\t IM is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_FP) == 0)
+		seq_puts(m, "\t FP is not idle\n");
+	if ((idle & VIVS_HI_IDLE_STATE_TS) == 0)
+		seq_puts(m, "\t TS is not idle\n");
+	if (idle & VIVS_HI_IDLE_STATE_AXI_LP)
+		seq_puts(m, "\t AXI low power mode\n");
+
+	if (gpu->identity.features & chipFeatures_DEBUG_MODE) {
+		u32 read0 = gpu_read(gpu, VIVS_MC_DEBUG_READ0);
+		u32 read1 = gpu_read(gpu, VIVS_MC_DEBUG_READ1);
+		u32 write = gpu_read(gpu, VIVS_MC_DEBUG_WRITE);
+
+		seq_puts(m, "\tMC\n");
+		seq_printf(m, "\t read0: 0x%08x\n", read0);
+		seq_printf(m, "\t read1: 0x%08x\n", read1);
+		seq_printf(m, "\t write: 0x%08x\n", write);
+	}
+
+	seq_puts(m, "\tDMA ");
+
+	if ((debug.address[0] == debug.address[1]) && (debug.state[0] == debug.state[1])) {
+		seq_puts(m, "seems to be stuck\n");
+	} else {
+		if (debug.address[0] == debug.address[1])
+			seq_puts(m, "adress is constant\n");
+		else
+			seq_puts(m, "is runing\n");
+	}
+
+	seq_printf(m, "\t address 0: 0x%08x\n", debug.address[0]);
+	seq_printf(m, "\t address 1: 0x%08x\n", debug.address[1]);
+	seq_printf(m, "\t state 0: 0x%08x\n", debug.state[0]);
+	seq_printf(m, "\t state 1: 0x%08x\n", debug.state[1]);
+	seq_printf(m, "\t last fetch 64 bit word: 0x%08x 0x%08x\n",
+		   dma_lo, dma_hi);
+}
+#endif
+
+/*
+ * Power Management:
+ */
+
+static int enable_pwrrail(struct etnaviv_gpu *gpu)
+{
+#if 0
+	struct drm_device *dev = gpu->dev;
+	int ret = 0;
+
+	if (gpu->gpu_reg) {
+		ret = regulator_enable(gpu->gpu_reg);
+		if (ret) {
+			dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (gpu->gpu_cx) {
+		ret = regulator_enable(gpu->gpu_cx);
+		if (ret) {
+			dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
+			return ret;
+		}
+	}
+#endif
+	return 0;
+}
+
+static int disable_pwrrail(struct etnaviv_gpu *gpu)
+{
+#if 0
+	if (gpu->gpu_cx)
+		regulator_disable(gpu->gpu_cx);
+	if (gpu->gpu_reg)
+		regulator_disable(gpu->gpu_reg);
+#endif
+	return 0;
+}
+
+static int enable_clk(struct etnaviv_gpu *gpu)
+{
+	if (gpu->clk_core)
+		clk_prepare_enable(gpu->clk_core);
+	if (gpu->clk_shader)
+		clk_prepare_enable(gpu->clk_shader);
+
+	return 0;
+}
+
+static int disable_clk(struct etnaviv_gpu *gpu)
+{
+	if (gpu->clk_core)
+		clk_disable_unprepare(gpu->clk_core);
+	if (gpu->clk_shader)
+		clk_disable_unprepare(gpu->clk_shader);
+
+	return 0;
+}
+
+static int enable_axi(struct etnaviv_gpu *gpu)
+{
+	if (gpu->clk_bus)
+		clk_prepare_enable(gpu->clk_bus);
+
+	return 0;
+}
+
+static int disable_axi(struct etnaviv_gpu *gpu)
+{
+	if (gpu->clk_bus)
+		clk_disable_unprepare(gpu->clk_bus);
+
+	return 0;
+}
+
+int etnaviv_gpu_pm_resume(struct etnaviv_gpu *gpu)
+{
+	int ret;
+
+	DBG("%s", gpu->name);
+
+	ret = enable_pwrrail(gpu);
+	if (ret)
+		return ret;
+
+	ret = enable_clk(gpu);
+	if (ret)
+		return ret;
+
+	ret = enable_axi(gpu);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu)
+{
+	int ret;
+
+	DBG("%s", gpu->name);
+
+	ret = disable_axi(gpu);
+	if (ret)
+		return ret;
+
+	ret = disable_clk(gpu);
+	if (ret)
+		return ret;
+
+	ret = disable_pwrrail(gpu);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * Hangcheck detection for locked gpu:
+ */
+static void recover_worker(struct work_struct *work)
+{
+	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, recover_work);
+	struct drm_device *dev = gpu->dev;
+
+	dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
+
+	mutex_lock(&dev->struct_mutex);
+	/* TODO gpu->funcs->recover(gpu); */
+	mutex_unlock(&dev->struct_mutex);
+
+	etnaviv_gpu_retire(gpu);
+}
+
+static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
+{
+	DBG("%s", gpu->name);
+	mod_timer(&gpu->hangcheck_timer,
+			round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES));
+}
+
+static void hangcheck_handler(unsigned long data)
+{
+	struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data;
+	struct drm_device *dev = gpu->dev;
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	uint32_t fence = gpu->retired_fence;
+
+	if (fence != gpu->hangcheck_fence) {
+		/* some progress has been made.. ya! */
+		gpu->hangcheck_fence = fence;
+	} else if (fence_after(gpu->submitted_fence, fence)) {
+		/* no progress and not done.. hung! */
+		gpu->hangcheck_fence = fence;
+		dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n",
+				gpu->name);
+		dev_err(dev->dev, "%s:     completed fence: %u\n",
+				gpu->name, fence);
+		dev_err(dev->dev, "%s:     submitted fence: %u\n",
+				gpu->name, gpu->submitted_fence);
+		queue_work(priv->wq, &gpu->recover_work);
+	}
+
+	/* if still more pending work, reset the hangcheck timer: */
+	if (fence_after(gpu->submitted_fence, gpu->hangcheck_fence))
+		hangcheck_timer_reset(gpu);
+}
+
+/*
+ * event management:
+ */
+
+static unsigned int event_alloc(struct etnaviv_gpu *gpu)
+{
+	unsigned long ret, flags;
+	unsigned int i, event = ~0U;
+
+	ret = wait_for_completion_timeout(&gpu->event_free, msecs_to_jiffies(10 * 10000));
+	if (!ret)
+		dev_err(gpu->dev->dev, "wait_for_completion_timeout failed");
+
+	spin_lock_irqsave(&gpu->event_spinlock, flags);
+
+	/* find first free event */
+	for (i = 0; i < ARRAY_SIZE(gpu->event_used); i++) {
+		if (gpu->event_used[i] == false) {
+			gpu->event_used[i] = true;
+			event = i;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&gpu->event_spinlock, flags);
+
+	return event;
+}
+
+static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpu->event_spinlock, flags);
+
+	if (gpu->event_used[event] == false) {
+		dev_warn(gpu->dev->dev, "event %u is already marked as free", event);
+		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
+	} else {
+		gpu->event_used[event] = false;
+		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
+
+		complete(&gpu->event_free);
+	}
+}
+
+/*
+ * Cmdstream submission/retirement:
+ */
+
+static void retire_worker(struct work_struct *work)
+{
+	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, retire_work);
+	struct drm_device *dev = gpu->dev;
+	uint32_t fence = gpu->retired_fence;
+
+	etnaviv_update_fence(gpu->dev, fence);
+
+	mutex_lock(&dev->struct_mutex);
+
+	while (!list_empty(&gpu->active_list)) {
+		struct etnaviv_gem_object *obj;
+
+		obj = list_first_entry(&gpu->active_list,
+				struct etnaviv_gem_object, mm_list);
+
+		if ((obj->read_fence <= fence) &&
+				(obj->write_fence <= fence)) {
+			/* move to inactive: */
+			etnaviv_gem_move_to_inactive(&obj->base);
+			etnaviv_gem_put_iova(&obj->base);
+			drm_gem_object_unreference(&obj->base);
+		} else {
+			break;
+		}
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+}
+
+/* call from irq handler to schedule work to retire bo's */
+void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
+{
+	struct etnaviv_drm_private *priv = gpu->dev->dev_private;
+	queue_work(priv->wq, &gpu->retire_work);
+}
+
+/* add bo's to gpu's ring, and kick gpu: */
+int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submit,
+		struct etnaviv_file_private *ctx)
+{
+	struct drm_device *dev = gpu->dev;
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	int ret = 0;
+	unsigned int event, i;
+
+	submit->fence = ++priv->next_fence;
+
+	gpu->submitted_fence = submit->fence;
+
+	/*
+	 * TODO
+	 *
+	 * - flush
+	 * - data endian
+	 * - prefetch
+	 *
+	 */
+
+	event = event_alloc(gpu);
+	if (unlikely(event == ~0U)) {
+		DRM_ERROR("no free event\n");
+		ret = -EBUSY;
+		goto fail;
+	}
+
+	gpu->event_to_fence[event] = submit->fence;
+
+	etnaviv_buffer_queue(gpu, event, submit);
+
+	priv->lastctx = ctx;
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+
+		/* can't happen yet.. but when we add 2d support we'll have
+		 * to deal w/ cross-ring synchronization:
+		 */
+		WARN_ON(is_active(etnaviv_obj) && (etnaviv_obj->gpu != gpu));
+
+		if (!is_active(etnaviv_obj)) {
+			uint32_t iova;
+
+			/* ring takes a reference to the bo and iova: */
+			drm_gem_object_reference(&etnaviv_obj->base);
+			etnaviv_gem_get_iova_locked(gpu, &etnaviv_obj->base, &iova);
+		}
+
+		if (submit->bos[i].flags & ETNA_SUBMIT_BO_READ)
+			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu, false, submit->fence);
+
+		if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
+			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu, true, submit->fence);
+	}
+	hangcheck_timer_reset(gpu);
+
+fail:
+	return ret;
+}
+
+/*
+ * Init/Cleanup:
+ */
+static irqreturn_t irq_handler(int irq, void *data)
+{
+	struct etnaviv_gpu *gpu = data;
+	irqreturn_t ret = IRQ_NONE;
+
+	u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE);
+
+	if (intr != 0) {
+		dev_dbg(gpu->dev->dev, "intr 0x%08x\n", intr);
+
+		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR)
+			dev_err(gpu->dev->dev, "AXI bus error\n");
+		else {
+			uint8_t event = __fls(intr);
+			dev_dbg(gpu->dev->dev, "event %u\n", event);
+			gpu->retired_fence = gpu->event_to_fence[event];
+			event_free(gpu, event);
+			etnaviv_gpu_retire(gpu);
+		}
+
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static int etnaviv_gpu_bind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct drm_device *drm = data;
+	struct etnaviv_drm_private *priv = drm->dev_private;
+	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+	int idx = gpu->pipe;
+
+	dev_info(dev, "pre gpu[idx]: %p\n", priv->gpu[idx]);
+
+	if (priv->gpu[idx] == NULL) {
+		dev_info(dev, "adding core @idx %d\n", idx);
+		priv->gpu[idx] = gpu;
+	} else {
+		dev_err(dev, "failed to add core @idx %d\n", idx);
+		goto fail;
+	}
+
+	dev_info(dev, "post gpu[idx]: %p\n", priv->gpu[idx]);
+
+	gpu->dev = drm;
+
+	INIT_LIST_HEAD(&gpu->active_list);
+	INIT_WORK(&gpu->retire_work, retire_worker);
+	INIT_WORK(&gpu->recover_work, recover_worker);
+
+	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
+			(unsigned long)gpu);
+	return 0;
+fail:
+	return -1;
+}
+
+static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+
+	del_timer(&gpu->hangcheck_timer);
+
+	DBG("%s", gpu->name);
+
+	WARN_ON(!list_empty(&gpu->active_list));
+
+	if (gpu->buffer)
+		drm_gem_object_unreference_unlocked(gpu->buffer);
+
+	if (gpu->mmu)
+		etnaviv_iommu_destroy(gpu->mmu);
+
+	drm_mm_takedown(&gpu->mm);
+}
+
+static const struct component_ops gpu_ops = {
+	.bind = etnaviv_gpu_bind,
+	.unbind = etnaviv_gpu_unbind,
+};
+
+static const struct of_device_id etnaviv_gpu_match[] = {
+	{
+		.compatible = "vivante,vivante-gpu-2d",
+		.data = (void *)ETNA_PIPE_2D
+	},
+	{
+		.compatible = "vivante,vivante-gpu-3d",
+		.data = (void *)ETNA_PIPE_3D
+	},
+	{
+		.compatible = "vivante,vivante-gpu-vg",
+		.data = (void *)ETNA_PIPE_VG
+	},
+	{ }
+};
+
+static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device *dev = &pdev->dev;
+	struct etnaviv_gpu *gpu;
+	int err = 0;
+
+	gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL);
+	if (!gpu)
+		return -ENOMEM;
+
+	match = of_match_device(etnaviv_gpu_match, &pdev->dev);
+	if (!match)
+		return -EINVAL;
+
+	gpu->name = pdev->name;
+
+	/* Map registers: */
+	gpu->mmio = etnaviv_ioremap(pdev, NULL, gpu->name);
+	if (IS_ERR(gpu->mmio))
+		return PTR_ERR(gpu->mmio);
+
+	/* Get Interrupt: */
+	gpu->irq = platform_get_irq(pdev, 0);
+	if (gpu->irq < 0) {
+		err = gpu->irq;
+		dev_err(dev, "failed to get irq: %d\n", err);
+		goto fail;
+	}
+
+	err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
+			IRQF_TRIGGER_HIGH, gpu->name, gpu);
+	if (err) {
+		dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
+		goto fail;
+	}
+
+	/* Get Clocks: */
+	gpu->clk_bus = devm_clk_get(&pdev->dev, "bus");
+	DBG("clk_bus: %p", gpu->clk_bus);
+	if (IS_ERR(gpu->clk_bus))
+		gpu->clk_bus = NULL;
+
+	gpu->clk_core = devm_clk_get(&pdev->dev, "core");
+	DBG("clk_core: %p", gpu->clk_core);
+	if (IS_ERR(gpu->clk_core))
+		gpu->clk_core = NULL;
+
+	gpu->clk_shader = devm_clk_get(&pdev->dev, "shader");
+	DBG("clk_shader: %p", gpu->clk_shader);
+	if (IS_ERR(gpu->clk_shader))
+		gpu->clk_shader = NULL;
+
+	gpu->pipe = (long)match->data;
+
+	/* TODO: figure out max mapped size */
+	drm_mm_init(&gpu->mm, 0x80000000, SZ_1G);
+
+	dev_set_drvdata(dev, gpu);
+
+	err = component_add(&pdev->dev, &gpu_ops);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to register component: %d\n", err);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return err;
+}
+
+static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &gpu_ops);
+	return 0;
+}
+
+struct platform_driver etnaviv_gpu_driver = {
+	.driver = {
+		.name = "etnaviv-gpu",
+		.owner = THIS_MODULE,
+		.of_match_table = etnaviv_gpu_match,
+	},
+	.probe = etnaviv_gpu_platform_probe,
+	.remove = etnaviv_gpu_platform_remove,
+};
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
new file mode 100644
index 000000000000..f918542c0a82
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_GPU_H__
+#define __ETNAVIV_GPU_H__
+
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include "etnaviv_drv.h"
+
+struct etnaviv_gem_submit;
+
+struct etnaviv_chip_identity {
+	/* Chip model. */
+	uint32_t model;
+
+	/* Revision value.*/
+	uint32_t revision;
+
+	/* Supported feature fields. */
+	uint32_t features;
+
+	/* Supported minor feature fields. */
+	uint32_t minor_features0;
+
+	/* Supported minor feature 1 fields. */
+	uint32_t minor_features1;
+
+	/* Supported minor feature 2 fields. */
+	uint32_t minor_features2;
+
+	/* Supported minor feature 3 fields. */
+	uint32_t minor_features3;
+
+	/* Number of streams supported. */
+	uint32_t stream_count;
+
+	/* Total number of temporary registers per thread. */
+	uint32_t register_max;
+
+	/* Maximum number of threads. */
+	uint32_t thread_count;
+
+	/* Number of shader cores. */
+	uint32_t shader_core_count;
+
+	/* Size of the vertex cache. */
+	uint32_t vertex_cache_size;
+
+	/* Number of entries in the vertex output buffer. */
+	uint32_t vertex_output_buffer_size;
+
+	/* Number of pixel pipes. */
+	uint32_t pixel_pipes;
+
+	/* Number of instructions. */
+	uint32_t instruction_count;
+
+	/* Number of constants. */
+	uint32_t num_constants;
+
+	/* Buffer size */
+	uint32_t buffer_size;
+};
+
+struct etnaviv_gpu {
+	const char *name;
+	struct drm_device *dev;
+	struct etnaviv_chip_identity identity;
+	long pipe;
+
+	/* 'ring'-buffer: */
+	struct drm_gem_object *buffer;
+
+	/* event management: */
+	bool event_used[30];
+	uint32_t event_to_fence[30];
+	struct completion event_free;
+	struct spinlock event_spinlock;
+
+	/* list of GEM active objects: */
+	struct list_head active_list;
+
+	uint32_t submitted_fence;
+	uint32_t retired_fence;
+
+	/* worker for handling active-list retiring: */
+	struct work_struct retire_work;
+
+	void __iomem *mmio;
+	int irq;
+
+	struct etnaviv_iommu *mmu;
+
+	/* memory manager for GPU address area */
+	struct drm_mm mm;
+
+	/* Power Control: */
+#if 0
+	struct regulator *gpu_reg, *gpu_cx;
+#endif
+	struct clk *clk_bus;
+	struct clk *clk_core;
+	struct clk *clk_shader;
+
+	/* Hang Detction: */
+#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
+#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
+	struct timer_list hangcheck_timer;
+	uint32_t hangcheck_fence;
+	struct work_struct recover_work;
+};
+
+static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data)
+{
+	etnaviv_writel(data, gpu->mmio + reg);
+}
+
+static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg)
+{
+	return etnaviv_readl(gpu->mmio + reg);
+}
+
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param, uint64_t *value);
+
+int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
+int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu);
+int etnaviv_gpu_pm_resume(struct etnaviv_gpu *gpu);
+
+#ifdef CONFIG_DEBUG_FS
+void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
+#endif
+
+void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
+int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submit,
+		struct etnaviv_file_private *ctx);
+
+extern struct platform_driver etnaviv_gpu_driver;
+
+#endif /* __ETNAVIV_GPU_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
new file mode 100644
index 000000000000..5841a08f627f
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 Christian Gmeiner <[email-masked]>
+  *
+ * 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 <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+
+#include "etnaviv_gpu.h"
+#include "etnaviv_iommu.h"
+#include "state_hi.xml.h"
+
+#define PT_SIZE		SZ_256K
+#define PT_ENTRIES	(PT_SIZE / sizeof(uint32_t))
+
+#define GPU_MEM_START	0x80000000
+
+struct etnaviv_iommu_domain_pgtable {
+	uint32_t *pgtable;
+	dma_addr_t paddr;
+};
+
+struct etnaviv_iommu_domain {
+	struct etnaviv_iommu_domain_pgtable pgtable;
+	spinlock_t map_lock;
+};
+
+static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable,
+			 size_t size)
+{
+	pgtable->pgtable = dma_alloc_coherent(NULL, size, &pgtable->paddr, GFP_KERNEL);
+	if (!pgtable->pgtable)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable,
+			 size_t size)
+{
+	dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr);
+}
+
+static uint32_t pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable,
+			   unsigned long iova)
+{
+	/* calcuate index into page table */
+	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
+	phys_addr_t paddr;
+
+	paddr = pgtable->pgtable[index];
+
+	return paddr;
+}
+
+static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
+			  unsigned long iova, phys_addr_t paddr)
+{
+	/* calcuate index into page table */
+	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
+
+	pgtable->pgtable[index] = paddr;
+}
+
+static int etnaviv_iommu_domain_init(struct iommu_domain *domain)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain;
+	int ret;
+
+	etnaviv_domain = kmalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
+	if (!etnaviv_domain)
+		return -ENOMEM;
+
+	ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE);
+	if (ret < 0) {
+		kfree(etnaviv_domain);
+		return ret;
+	}
+
+	spin_lock_init(&etnaviv_domain->map_lock);
+	domain->priv = etnaviv_domain;
+	return 0;
+}
+
+static void etnaviv_iommu_domain_destroy(struct iommu_domain *domain)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+
+	pgtable_free(&etnaviv_domain->pgtable, PT_SIZE);
+
+	kfree(etnaviv_domain);
+	domain->priv = NULL;
+}
+
+static int etnaviv_iommu_map(struct iommu_domain *domain, unsigned long iova,
+	   phys_addr_t paddr, size_t size, int prot)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+
+	if (size != SZ_4K)
+		return -EINVAL;
+
+	spin_lock(&etnaviv_domain->map_lock);
+	pgtable_write(&etnaviv_domain->pgtable, iova, paddr);
+	spin_unlock(&etnaviv_domain->map_lock);
+
+	return 0;
+}
+
+static size_t etnaviv_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+	     size_t size)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+
+	if (size != SZ_4K)
+		return -EINVAL;
+
+	spin_lock(&etnaviv_domain->map_lock);
+	pgtable_write(&etnaviv_domain->pgtable, iova, ~0);
+	spin_unlock(&etnaviv_domain->map_lock);
+
+	return 0;
+}
+
+static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,
+	dma_addr_t iova)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+
+	return pgtable_read(&etnaviv_domain->pgtable, iova);
+}
+
+static struct iommu_ops etnaviv_iommu_ops = {
+		.domain_init = etnaviv_iommu_domain_init,
+		.domain_destroy = etnaviv_iommu_domain_destroy,
+		.map = etnaviv_iommu_map,
+		.unmap = etnaviv_iommu_unmap,
+		.iova_to_phys = etnaviv_iommu_iova_to_phys,
+		.pgsize_bitmap = SZ_4K,
+};
+
+struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
+{
+	struct iommu_domain *domain;
+	struct etnaviv_iommu_domain *etnaviv_domain;
+	int ret;
+
+	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+	if (!domain)
+		return NULL;
+
+	domain->ops = &etnaviv_iommu_ops;
+
+	ret = domain->ops->domain_init(domain);
+	if (ret)
+		goto out_free;
+
+	/* set page table address in MC */
+	etnaviv_domain = domain->priv;
+
+	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
+	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
+	gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
+	gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
+	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
+
+	return domain;
+
+out_free:
+	kfree(domain);
+	return NULL;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_iommu.h b/drivers/staging/etnaviv/etnaviv_iommu.h
new file mode 100644
index 000000000000..c0c359d4f166
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_iommu.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 Christian Gmeiner <[email-masked]>
+  *
+ * 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_IOMMU_H__
+#define __ETNAVIV_IOMMU_H__
+
+#include <linux/iommu.h>
+struct etnaviv_gpu;
+
+struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu);
+struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
+
+#endif /* __ETNAVIV_IOMMU_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_iommu_v2.c b/drivers/staging/etnaviv/etnaviv_iommu_v2.c
new file mode 100644
index 000000000000..fbb4aed3dc80
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_iommu_v2.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Christian Gmeiner <[email-masked]>
+  *
+ * 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 <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+
+#include "etnaviv_gpu.h"
+#include "etnaviv_iommu.h"
+#include "state_hi.xml.h"
+
+
+struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu)
+{
+	/* TODO */
+	return NULL;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_iommu_v2.h b/drivers/staging/etnaviv/etnaviv_iommu_v2.h
new file mode 100644
index 000000000000..603ea41c5389
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_iommu_v2.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 Christian Gmeiner <[email-masked]>
+  *
+ * 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_IOMMU_V2_H__
+#define __ETNAVIV_IOMMU_V2_H__
+
+#include <linux/iommu.h>
+struct etnaviv_gpu;
+
+struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
+
+#endif /* __ETNAVIV_IOMMU_V2_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
new file mode 100644
index 000000000000..29e0056ca73a
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_drv.h"
+#include "etnaviv_mmu.h"
+
+static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
+		unsigned long iova, int flags, void *arg)
+{
+	DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
+	return 0;
+}
+
+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
+		struct sg_table *sgt, unsigned len, int prot)
+{
+	struct iommu_domain *domain = iommu->domain;
+	struct scatterlist *sg;
+	unsigned int da = iova;
+	unsigned int i, j;
+	int ret;
+
+	if (!domain || !sgt)
+		return -EINVAL;
+
+	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+		u32 pa = sg_phys(sg) - sg->offset;
+		size_t bytes = sg->length + sg->offset;
+
+		VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
+
+		ret = iommu_map(domain, da, pa, bytes, prot);
+		if (ret)
+			goto fail;
+
+		da += bytes;
+	}
+
+	return 0;
+
+fail:
+	da = iova;
+
+	for_each_sg(sgt->sgl, sg, i, j) {
+		size_t bytes = sg->length + sg->offset;
+		iommu_unmap(domain, da, bytes);
+		da += bytes;
+	}
+	return ret;
+}
+
+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
+		struct sg_table *sgt, unsigned len)
+{
+	struct iommu_domain *domain = iommu->domain;
+	struct scatterlist *sg;
+	unsigned int da = iova;
+	int i;
+
+	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+		size_t bytes = sg->length + sg->offset;
+		size_t unmapped;
+
+		unmapped = iommu_unmap(domain, da, bytes);
+		if (unmapped < bytes)
+			return unmapped;
+
+		VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
+
+		BUG_ON(!PAGE_ALIGNED(bytes));
+
+		da += bytes;
+	}
+
+	return 0;
+}
+
+void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
+{
+	iommu_domain_free(mmu->domain);
+	kfree(mmu);
+}
+
+struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
+	struct iommu_domain *domain, enum etnaviv_iommu_version version)
+{
+	struct etnaviv_iommu *mmu;
+
+	mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
+	if (!mmu)
+		return ERR_PTR(-ENOMEM);
+
+	mmu->domain = domain;
+	mmu->dev = dev;
+	mmu->version = version;
+
+	iommu_set_fault_handler(domain, etnaviv_fault_handler, dev);
+
+	return mmu;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
new file mode 100644
index 000000000000..c6f72f69ea13
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_MMU_H__
+#define __ETNAVIV_MMU_H__
+
+#include <linux/iommu.h>
+
+enum etnaviv_iommu_version {
+	ETNAVIV_IOMMU_V1 = 0,
+	ETNAVIV_IOMMU_V2,
+};
+
+struct etnaviv_iommu {
+	struct drm_device *dev;
+	struct iommu_domain *domain;
+
+	enum etnaviv_iommu_version version;
+};
+
+int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names, int cnt);
+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova, struct sg_table *sgt,
+		unsigned len, int prot);
+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova, struct sg_table *sgt,
+		unsigned len);
+void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
+
+struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
+	struct iommu_domain *domain, enum etnaviv_iommu_version version);
+
+#endif /* __ETNAVIV_MMU_H__ */
diff --git a/drivers/staging/etnaviv/state.xml.h b/drivers/staging/etnaviv/state.xml.h
new file mode 100644
index 000000000000..e7b36df1e4e3
--- /dev/null
+++ b/drivers/staging/etnaviv/state.xml.h
@@ -0,0 +1,348 @@
+#ifndef STATE_XML
+#define STATE_XML
+
+/* Autogenerated file, DO NOT EDIT manually!
+
+This file was generated by the rules-ng-ng headergen tool in this git repository:
+http://0x04.net/cgit/index.cgi/rules-ng-ng
+git clone git://0x04.net/rules-ng-ng
+
+The rules-ng-ng source files this header was generated from are:
+- /home/orion/projects/etna_viv/rnndb/state.xml    (  18526 bytes, from 2013-09-11 16:52:32)
+- /home/orion/projects/etna_viv/rnndb/common.xml   (  18379 bytes, from 2014-01-27 15:58:05)
+- /home/orion/projects/etna_viv/rnndb/state_hi.xml (  22236 bytes, from 2014-01-27 15:56:46)
+- /home/orion/projects/etna_viv/rnndb/state_2d.xml (  51191 bytes, from 2013-10-04 06:36:55)
+- /home/orion/projects/etna_viv/rnndb/state_3d.xml (  54570 bytes, from 2013-10-12 15:25:03)
+- /home/orion/projects/etna_viv/rnndb/state_vg.xml (   5942 bytes, from 2013-09-01 10:53:22)
+
+Copyright (C) 2013
+*/
+
+
+#define VARYING_COMPONENT_USE_UNUSED				0x00000000
+#define VARYING_COMPONENT_USE_USED				0x00000001
+#define VARYING_COMPONENT_USE_POINTCOORD_X			0x00000002
+#define VARYING_COMPONENT_USE_POINTCOORD_Y			0x00000003
+#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__MASK		0x000000ff
+#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__SHIFT		0
+#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE(x)		(((x) << FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__SHIFT) & FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__MASK)
+#define VIVS_FE							0x00000000
+
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG(i0)		       (0x00000600 + 0x4*(i0))
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG__ESIZE			0x00000004
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG__LEN			0x00000010
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE__MASK		0x0000000f
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE__SHIFT		0
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_BYTE			0x00000000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_BYTE	0x00000001
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_SHORT		0x00000002
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_SHORT	0x00000003
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_INT			0x00000004
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_INT		0x00000005
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_FLOAT		0x00000008
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_HALF_FLOAT		0x00000009
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_FIXED		0x0000000b
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_INT_10_10_10_2	0x0000000c
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_INT_10_10_10_2	0x0000000d
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__MASK		0x00000030
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__SHIFT		4
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN(x)			(((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__MASK)
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NONCONSECUTIVE		0x00000080
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__MASK		0x00000700
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__SHIFT		8
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM(x)			(((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__MASK)
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__MASK			0x00003000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__SHIFT		12
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM(x)			(((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__MASK)
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE__MASK		0x0000c000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE__SHIFT		14
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE_OFF		0x00000000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE_ON		0x00008000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START__MASK		0x00ff0000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START__SHIFT		16
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START(x)			(((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_START__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_START__MASK)
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END__MASK			0xff000000
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END__SHIFT		24
+#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END(x)			(((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_END__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_END__MASK)
+
+#define VIVS_FE_CMD_STREAM_BASE_ADDR				0x00000640
+
+#define VIVS_FE_INDEX_STREAM_BASE_ADDR				0x00000644
+
+#define VIVS_FE_INDEX_STREAM_CONTROL				0x00000648
+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE__MASK			0x00000003
+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE__SHIFT		0
+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_CHAR		0x00000000
+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_SHORT	0x00000001
+#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_INT		0x00000002
+
+#define VIVS_FE_VERTEX_STREAM_BASE_ADDR				0x0000064c
+
+#define VIVS_FE_VERTEX_STREAM_CONTROL				0x00000650
+
+#define VIVS_FE_COMMAND_ADDRESS					0x00000654
+
+#define VIVS_FE_COMMAND_CONTROL					0x00000658
+#define VIVS_FE_COMMAND_CONTROL_PREFETCH__MASK			0x0000ffff
+#define VIVS_FE_COMMAND_CONTROL_PREFETCH__SHIFT			0
+#define VIVS_FE_COMMAND_CONTROL_PREFETCH(x)			(((x) << VIVS_FE_COMMAND_CONTROL_PREFETCH__SHIFT) & VIVS_FE_COMMAND_CONTROL_PREFETCH__MASK)
+#define VIVS_FE_COMMAND_CONTROL_ENABLE				0x00010000
+
+#define VIVS_FE_DMA_STATUS					0x0000065c
+
+#define VIVS_FE_DMA_DEBUG_STATE					0x00000660
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE__MASK			0x0000001f
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE__SHIFT		0
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_IDLE			0x00000000
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DEC			0x00000001
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_ADR0			0x00000002
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LOAD0			0x00000003
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_ADR1			0x00000004
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LOAD1			0x00000005
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DADR			0x00000006
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DCMD			0x00000007
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DCNTL		0x00000008
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DIDXCNTL		0x00000009
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_INITREQDMA		0x0000000a
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DRAWIDX		0x0000000b
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DRAW			0x0000000c
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DRECT0		0x0000000d
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DRECT1		0x0000000e
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DDATA0		0x0000000f
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DDATA1		0x00000010
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_WAITFIFO		0x00000011
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_WAIT			0x00000012
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LINK			0x00000013
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_END			0x00000014
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_STALL			0x00000015
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE__MASK		0x00000300
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE__SHIFT		8
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_IDLE		0x00000000
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_START		0x00000100
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_REQ		0x00000200
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_END		0x00000300
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE__MASK		0x00000c00
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE__SHIFT		10
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_IDLE		0x00000000
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_RAMVALID	0x00000400
+#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_VALID		0x00000800
+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE__MASK		0x00003000
+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE__SHIFT		12
+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_IDLE		0x00000000
+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_WAITIDX		0x00001000
+#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_CAL		0x00002000
+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE__MASK			0x0000c000
+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE__SHIFT		14
+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_IDLE			0x00000000
+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_LDADR			0x00004000
+#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_IDXCALC		0x00008000
+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE__MASK		0x00030000
+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE__SHIFT		16
+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_IDLE		0x00000000
+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_CKCACHE		0x00010000
+#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_MISS		0x00020000
+
+#define VIVS_FE_DMA_ADDRESS					0x00000664
+
+#define VIVS_FE_DMA_LOW						0x00000668
+
+#define VIVS_FE_DMA_HIGH					0x0000066c
+
+#define VIVS_FE_AUTO_FLUSH					0x00000670
+
+#define VIVS_FE_UNK00678					0x00000678
+
+#define VIVS_FE_UNK0067C					0x0000067c
+
+#define VIVS_FE_VERTEX_STREAMS(i0)			       (0x00000000 + 0x4*(i0))
+#define VIVS_FE_VERTEX_STREAMS__ESIZE				0x00000004
+#define VIVS_FE_VERTEX_STREAMS__LEN				0x00000008
+
+#define VIVS_FE_VERTEX_STREAMS_BASE_ADDR(i0)		       (0x00000680 + 0x4*(i0))
+
+#define VIVS_FE_VERTEX_STREAMS_CONTROL(i0)		       (0x000006a0 + 0x4*(i0))
+
+#define VIVS_FE_UNK00700(i0)				       (0x00000700 + 0x4*(i0))
+#define VIVS_FE_UNK00700__ESIZE					0x00000004
+#define VIVS_FE_UNK00700__LEN					0x00000010
+
+#define VIVS_FE_UNK00740(i0)				       (0x00000740 + 0x4*(i0))
+#define VIVS_FE_UNK00740__ESIZE					0x00000004
+#define VIVS_FE_UNK00740__LEN					0x00000010
+
+#define VIVS_FE_UNK00780(i0)				       (0x00000780 + 0x4*(i0))
+#define VIVS_FE_UNK00780__ESIZE					0x00000004
+#define VIVS_FE_UNK00780__LEN					0x00000010
+
+#define VIVS_GL							0x00000000
+
+#define VIVS_GL_PIPE_SELECT					0x00003800
+#define VIVS_GL_PIPE_SELECT_PIPE__MASK				0x00000001
+#define VIVS_GL_PIPE_SELECT_PIPE__SHIFT				0
+#define VIVS_GL_PIPE_SELECT_PIPE(x)				(((x) << VIVS_GL_PIPE_SELECT_PIPE__SHIFT) & VIVS_GL_PIPE_SELECT_PIPE__MASK)
+
+#define VIVS_GL_EVENT						0x00003804
+#define VIVS_GL_EVENT_EVENT_ID__MASK				0x0000001f
+#define VIVS_GL_EVENT_EVENT_ID__SHIFT				0
+#define VIVS_GL_EVENT_EVENT_ID(x)				(((x) << VIVS_GL_EVENT_EVENT_ID__SHIFT) & VIVS_GL_EVENT_EVENT_ID__MASK)
+#define VIVS_GL_EVENT_FROM_FE					0x00000020
+#define VIVS_GL_EVENT_FROM_PE					0x00000040
+#define VIVS_GL_EVENT_SOURCE__MASK				0x00001f00
+#define VIVS_GL_EVENT_SOURCE__SHIFT				8
+#define VIVS_GL_EVENT_SOURCE(x)					(((x) << VIVS_GL_EVENT_SOURCE__SHIFT) & VIVS_GL_EVENT_SOURCE__MASK)
+
+#define VIVS_GL_SEMAPHORE_TOKEN					0x00003808
+#define VIVS_GL_SEMAPHORE_TOKEN_FROM__MASK			0x0000001f
+#define VIVS_GL_SEMAPHORE_TOKEN_FROM__SHIFT			0
+#define VIVS_GL_SEMAPHORE_TOKEN_FROM(x)				(((x) << VIVS_GL_SEMAPHORE_TOKEN_FROM__SHIFT) & VIVS_GL_SEMAPHORE_TOKEN_FROM__MASK)
+#define VIVS_GL_SEMAPHORE_TOKEN_TO__MASK			0x00001f00
+#define VIVS_GL_SEMAPHORE_TOKEN_TO__SHIFT			8
+#define VIVS_GL_SEMAPHORE_TOKEN_TO(x)				(((x) << VIVS_GL_SEMAPHORE_TOKEN_TO__SHIFT) & VIVS_GL_SEMAPHORE_TOKEN_TO__MASK)
+
+#define VIVS_GL_FLUSH_CACHE					0x0000380c
+#define VIVS_GL_FLUSH_CACHE_DEPTH				0x00000001
+#define VIVS_GL_FLUSH_CACHE_COLOR				0x00000002
+#define VIVS_GL_FLUSH_CACHE_TEXTURE				0x00000004
+#define VIVS_GL_FLUSH_CACHE_PE2D				0x00000008
+#define VIVS_GL_FLUSH_CACHE_TEXTUREVS				0x00000010
+#define VIVS_GL_FLUSH_CACHE_SHADER_L1				0x00000020
+#define VIVS_GL_FLUSH_CACHE_SHADER_L2				0x00000040
+
+#define VIVS_GL_FLUSH_MMU					0x00003810
+#define VIVS_GL_FLUSH_MMU_FLUSH_FEMMU				0x00000001
+#define VIVS_GL_FLUSH_MMU_FLUSH_PEMMU				0x00000002
+
+#define VIVS_GL_VERTEX_ELEMENT_CONFIG				0x00003814
+
+#define VIVS_GL_MULTI_SAMPLE_CONFIG				0x00003818
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES__MASK		0x00000003
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES__SHIFT		0
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_NONE		0x00000000
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_2X		0x00000001
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_4X		0x00000002
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_MASK		0x00000008
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__MASK		0x000000f0
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__SHIFT		4
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES(x)		(((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__MASK)
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES_MASK		0x00000100
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__MASK			0x00007000
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__SHIFT		12
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12(x)			(((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__MASK)
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12_MASK			0x00008000
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__MASK			0x00030000
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__SHIFT		16
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16(x)			(((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__MASK)
+#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16_MASK			0x00080000
+
+#define VIVS_GL_VARYING_TOTAL_COMPONENTS			0x0000381c
+#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__MASK		0x000000ff
+#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__SHIFT		0
+#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM(x)			(((x) << VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__SHIFT) & VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__MASK)
+
+#define VIVS_GL_VARYING_NUM_COMPONENTS				0x00003820
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__MASK		0x00000007
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__SHIFT		0
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__MASK		0x00000070
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__SHIFT		4
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__MASK		0x00000700
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__SHIFT		8
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__MASK		0x00007000
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__SHIFT		12
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__MASK		0x00070000
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__SHIFT		16
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__MASK		0x00700000
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__SHIFT		20
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__MASK		0x07000000
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__SHIFT		24
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__MASK)
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__MASK		0x70000000
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__SHIFT		28
+#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7(x)			(((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__MASK)
+
+#define VIVS_GL_VARYING_COMPONENT_USE(i0)		       (0x00003828 + 0x4*(i0))
+#define VIVS_GL_VARYING_COMPONENT_USE__ESIZE			0x00000004
+#define VIVS_GL_VARYING_COMPONENT_USE__LEN			0x00000002
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP0__MASK		0x00000003
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP0__SHIFT		0
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP0(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP0__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP0__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP1__MASK		0x0000000c
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP1__SHIFT		2
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP1(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP1__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP1__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP2__MASK		0x00000030
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP2__SHIFT		4
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP2(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP2__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP2__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP3__MASK		0x000000c0
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP3__SHIFT		6
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP3(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP3__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP3__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP4__MASK		0x00000300
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP4__SHIFT		8
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP4(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP4__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP4__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP5__MASK		0x00000c00
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP5__SHIFT		10
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP5(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP5__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP5__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP6__MASK		0x00003000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP6__SHIFT		12
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP6(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP6__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP6__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP7__MASK		0x0000c000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP7__SHIFT		14
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP7(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP7__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP7__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP8__MASK		0x00030000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP8__SHIFT		16
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP8(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP8__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP8__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP9__MASK		0x000c0000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP9__SHIFT		18
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP9(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP9__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP9__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP10__MASK		0x00300000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP10__SHIFT		20
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP10(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP10__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP10__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP11__MASK		0x00c00000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP11__SHIFT		22
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP11(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP11__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP11__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP12__MASK		0x03000000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP12__SHIFT		24
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP12(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP12__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP12__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP13__MASK		0x0c000000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP13__SHIFT		26
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP13(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP13__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP13__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP14__MASK		0x30000000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP14__SHIFT		28
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP14(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP14__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP14__MASK)
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP15__MASK		0xc0000000
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP15__SHIFT		30
+#define VIVS_GL_VARYING_COMPONENT_USE_COMP15(x)			(((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP15__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP15__MASK)
+
+#define VIVS_GL_UNK03834					0x00003834
+
+#define VIVS_GL_UNK03838					0x00003838
+
+#define VIVS_GL_API_MODE					0x0000384c
+#define VIVS_GL_API_MODE_OPENGL					0x00000000
+#define VIVS_GL_API_MODE_OPENVG					0x00000001
+#define VIVS_GL_API_MODE_OPENCL					0x00000002
+
+#define VIVS_GL_CONTEXT_POINTER					0x00003850
+
+#define VIVS_GL_UNK03A00					0x00003a00
+
+#define VIVS_GL_STALL_TOKEN					0x00003c00
+#define VIVS_GL_STALL_TOKEN_FROM__MASK				0x0000001f
+#define VIVS_GL_STALL_TOKEN_FROM__SHIFT				0
+#define VIVS_GL_STALL_TOKEN_FROM(x)				(((x) << VIVS_GL_STALL_TOKEN_FROM__SHIFT) & VIVS_GL_STALL_TOKEN_FROM__MASK)
+#define VIVS_GL_STALL_TOKEN_TO__MASK				0x00001f00
+#define VIVS_GL_STALL_TOKEN_TO__SHIFT				8
+#define VIVS_GL_STALL_TOKEN_TO(x)				(((x) << VIVS_GL_STALL_TOKEN_TO__SHIFT) & VIVS_GL_STALL_TOKEN_TO__MASK)
+#define VIVS_GL_STALL_TOKEN_FLIP0				0x40000000
+#define VIVS_GL_STALL_TOKEN_FLIP1				0x80000000
+
+#define VIVS_DUMMY						0x00000000
+
+#define VIVS_DUMMY_DUMMY					0x0003fffc
+
+
+#endif /* STATE_XML */
diff --git a/drivers/staging/etnaviv/state_hi.xml.h b/drivers/staging/etnaviv/state_hi.xml.h
new file mode 100644
index 000000000000..9799d7473e5e
--- /dev/null
+++ b/drivers/staging/etnaviv/state_hi.xml.h
@@ -0,0 +1,405 @@
+#ifndef STATE_HI_XML
+#define STATE_HI_XML
+
+/* Autogenerated file, DO NOT EDIT manually!
+
+This file was generated by the rules-ng-ng headergen tool in this git repository:
+http://0x04.net/cgit/index.cgi/rules-ng-ng
+git clone git://0x04.net/rules-ng-ng
+
+The rules-ng-ng source files this header was generated from are:
+- /home/christian/projects/etna_viv/rnndb/state.xml    (  18526 bytes, from 2014-09-06 05:57:57)
+- /home/christian/projects/etna_viv/rnndb/common.xml   (  18379 bytes, from 2014-09-06 05:57:57)
+- /home/christian/projects/etna_viv/rnndb/state_hi.xml (  23176 bytes, from 2014-09-06 06:07:47)
+- /home/christian/projects/etna_viv/rnndb/state_2d.xml (  51191 bytes, from 2014-09-06 05:57:57)
+- /home/christian/projects/etna_viv/rnndb/state_3d.xml (  54570 bytes, from 2014-09-06 05:57:57)
+- /home/christian/projects/etna_viv/rnndb/state_vg.xml (   5942 bytes, from 2014-09-06 05:57:57)
+
+Copyright (C) 2014
+*/
+
+
+#define MMU_EXCEPTION_SLAVE_NOT_PRESENT				0x00000001
+#define MMU_EXCEPTION_PAGE_NOT_PRESENT				0x00000002
+#define MMU_EXCEPTION_WRITE_VIOLATION				0x00000003
+#define VIVS_HI							0x00000000
+
+#define VIVS_HI_CLOCK_CONTROL					0x00000000
+#define VIVS_HI_CLOCK_CONTROL_CLK3D_DIS				0x00000001
+#define VIVS_HI_CLOCK_CONTROL_CLK2D_DIS				0x00000002
+#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK			0x000001fc
+#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__SHIFT			2
+#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(x)			(((x) << VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__SHIFT) & VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK)
+#define VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD			0x00000200
+#define VIVS_HI_CLOCK_CONTROL_DISABLE_RAM_CLK_GATING		0x00000400
+#define VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS		0x00000800
+#define VIVS_HI_CLOCK_CONTROL_SOFT_RESET			0x00001000
+#define VIVS_HI_CLOCK_CONTROL_IDLE_3D				0x00010000
+#define VIVS_HI_CLOCK_CONTROL_IDLE_2D				0x00020000
+#define VIVS_HI_CLOCK_CONTROL_IDLE_VG				0x00040000
+#define VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU			0x00080000
+#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK		0x00f00000
+#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__SHIFT		20
+#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(x)		(((x) << VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__SHIFT) & VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK)
+
+#define VIVS_HI_IDLE_STATE					0x00000004
+#define VIVS_HI_IDLE_STATE_FE					0x00000001
+#define VIVS_HI_IDLE_STATE_DE					0x00000002
+#define VIVS_HI_IDLE_STATE_PE					0x00000004
+#define VIVS_HI_IDLE_STATE_SH					0x00000008
+#define VIVS_HI_IDLE_STATE_PA					0x00000010
+#define VIVS_HI_IDLE_STATE_SE					0x00000020
+#define VIVS_HI_IDLE_STATE_RA					0x00000040
+#define VIVS_HI_IDLE_STATE_TX					0x00000080
+#define VIVS_HI_IDLE_STATE_VG					0x00000100
+#define VIVS_HI_IDLE_STATE_IM					0x00000200
+#define VIVS_HI_IDLE_STATE_FP					0x00000400
+#define VIVS_HI_IDLE_STATE_TS					0x00000800
+#define VIVS_HI_IDLE_STATE_AXI_LP				0x80000000
+
+#define VIVS_HI_AXI_CONFIG					0x00000008
+#define VIVS_HI_AXI_CONFIG_AWID__MASK				0x0000000f
+#define VIVS_HI_AXI_CONFIG_AWID__SHIFT				0
+#define VIVS_HI_AXI_CONFIG_AWID(x)				(((x) << VIVS_HI_AXI_CONFIG_AWID__SHIFT) & VIVS_HI_AXI_CONFIG_AWID__MASK)
+#define VIVS_HI_AXI_CONFIG_ARID__MASK				0x000000f0
+#define VIVS_HI_AXI_CONFIG_ARID__SHIFT				4
+#define VIVS_HI_AXI_CONFIG_ARID(x)				(((x) << VIVS_HI_AXI_CONFIG_ARID__SHIFT) & VIVS_HI_AXI_CONFIG_ARID__MASK)
+#define VIVS_HI_AXI_CONFIG_AWCACHE__MASK			0x00000f00
+#define VIVS_HI_AXI_CONFIG_AWCACHE__SHIFT			8
+#define VIVS_HI_AXI_CONFIG_AWCACHE(x)				(((x) << VIVS_HI_AXI_CONFIG_AWCACHE__SHIFT) & VIVS_HI_AXI_CONFIG_AWCACHE__MASK)
+#define VIVS_HI_AXI_CONFIG_ARCACHE__MASK			0x0000f000
+#define VIVS_HI_AXI_CONFIG_ARCACHE__SHIFT			12
+#define VIVS_HI_AXI_CONFIG_ARCACHE(x)				(((x) << VIVS_HI_AXI_CONFIG_ARCACHE__SHIFT) & VIVS_HI_AXI_CONFIG_ARCACHE__MASK)
+
+#define VIVS_HI_AXI_STATUS					0x0000000c
+#define VIVS_HI_AXI_STATUS_WR_ERR_ID__MASK			0x0000000f
+#define VIVS_HI_AXI_STATUS_WR_ERR_ID__SHIFT			0
+#define VIVS_HI_AXI_STATUS_WR_ERR_ID(x)				(((x) << VIVS_HI_AXI_STATUS_WR_ERR_ID__SHIFT) & VIVS_HI_AXI_STATUS_WR_ERR_ID__MASK)
+#define VIVS_HI_AXI_STATUS_RD_ERR_ID__MASK			0x000000f0
+#define VIVS_HI_AXI_STATUS_RD_ERR_ID__SHIFT			4
+#define VIVS_HI_AXI_STATUS_RD_ERR_ID(x)				(((x) << VIVS_HI_AXI_STATUS_RD_ERR_ID__SHIFT) & VIVS_HI_AXI_STATUS_RD_ERR_ID__MASK)
+#define VIVS_HI_AXI_STATUS_DET_WR_ERR				0x00000100
+#define VIVS_HI_AXI_STATUS_DET_RD_ERR				0x00000200
+
+#define VIVS_HI_INTR_ACKNOWLEDGE				0x00000010
+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK			0x7fffffff
+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT		0
+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC(x)			(((x) << VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT) & VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK)
+#define VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR			0x80000000
+
+#define VIVS_HI_INTR_ENBL					0x00000014
+#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__MASK			0xffffffff
+#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__SHIFT			0
+#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC(x)			(((x) << VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__SHIFT) & VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__MASK)
+
+#define VIVS_HI_CHIP_IDENTITY					0x00000018
+#define VIVS_HI_CHIP_IDENTITY_FAMILY__MASK			0xff000000
+#define VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT			24
+#define VIVS_HI_CHIP_IDENTITY_FAMILY(x)				(((x) << VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT) & VIVS_HI_CHIP_IDENTITY_FAMILY__MASK)
+#define VIVS_HI_CHIP_IDENTITY_PRODUCT__MASK			0x00ff0000
+#define VIVS_HI_CHIP_IDENTITY_PRODUCT__SHIFT			16
+#define VIVS_HI_CHIP_IDENTITY_PRODUCT(x)			(((x) << VIVS_HI_CHIP_IDENTITY_PRODUCT__SHIFT) & VIVS_HI_CHIP_IDENTITY_PRODUCT__MASK)
+#define VIVS_HI_CHIP_IDENTITY_REVISION__MASK			0x0000f000
+#define VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT			12
+#define VIVS_HI_CHIP_IDENTITY_REVISION(x)			(((x) << VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT) & VIVS_HI_CHIP_IDENTITY_REVISION__MASK)
+
+#define VIVS_HI_CHIP_FEATURE					0x0000001c
+
+#define VIVS_HI_CHIP_MODEL					0x00000020
+
+#define VIVS_HI_CHIP_REV					0x00000024
+
+#define VIVS_HI_CHIP_DATE					0x00000028
+
+#define VIVS_HI_CHIP_TIME					0x0000002c
+
+#define VIVS_HI_CHIP_MINOR_FEATURE_0				0x00000034
+
+#define VIVS_HI_CACHE_CONTROL					0x00000038
+
+#define VIVS_HI_MEMORY_COUNTER_RESET				0x0000003c
+
+#define VIVS_HI_PROFILE_READ_BYTES8				0x00000040
+
+#define VIVS_HI_PROFILE_WRITE_BYTES8				0x00000044
+
+#define VIVS_HI_CHIP_SPECS					0x00000048
+#define VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK			0x0000000f
+#define VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT			0
+#define VIVS_HI_CHIP_SPECS_STREAM_COUNT(x)			(((x) << VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK)
+#define VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK			0x000000f0
+#define VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT			4
+#define VIVS_HI_CHIP_SPECS_REGISTER_MAX(x)			(((x) << VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT) & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK)
+#define VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK			0x00000f00
+#define VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT			8
+#define VIVS_HI_CHIP_SPECS_THREAD_COUNT(x)			(((x) << VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK)
+#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK		0x0001f000
+#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT		12
+#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE(x)			(((x) << VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK)
+#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK		0x01f00000
+#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT		20
+#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT(x)			(((x) << VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK)
+#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK			0x0e000000
+#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT			25
+#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES(x)			(((x) << VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT) & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK)
+#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK	0xf0000000
+#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT	28
+#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE(x)		(((x) << VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK)
+
+#define VIVS_HI_PROFILE_WRITE_BURSTS				0x0000004c
+
+#define VIVS_HI_PROFILE_WRITE_REQUESTS				0x00000050
+
+#define VIVS_HI_PROFILE_READ_BURSTS				0x00000058
+
+#define VIVS_HI_PROFILE_READ_REQUESTS				0x0000005c
+
+#define VIVS_HI_PROFILE_READ_LASTS				0x00000060
+
+#define VIVS_HI_GP_OUT0						0x00000064
+
+#define VIVS_HI_GP_OUT1						0x00000068
+
+#define VIVS_HI_GP_OUT2						0x0000006c
+
+#define VIVS_HI_AXI_CONTROL					0x00000070
+#define VIVS_HI_AXI_CONTROL_WR_FULL_BURST_MODE			0x00000001
+
+#define VIVS_HI_CHIP_MINOR_FEATURE_1				0x00000074
+
+#define VIVS_HI_PROFILE_TOTAL_CYCLES				0x00000078
+
+#define VIVS_HI_PROFILE_IDLE_CYCLES				0x0000007c
+
+#define VIVS_HI_CHIP_SPECS_2					0x00000080
+#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK			0x000000ff
+#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT			0
+#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE(x)			(((x) << VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK)
+#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK		0x0000ff00
+#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT		8
+#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT(x)		(((x) << VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK)
+#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK		0xffff0000
+#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT		16
+#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS(x)			(((x) << VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT) & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK)
+
+#define VIVS_HI_CHIP_MINOR_FEATURE_2				0x00000084
+
+#define VIVS_HI_CHIP_MINOR_FEATURE_3				0x00000088
+
+#define VIVS_HI_CHIP_MINOR_FEATURE_4				0x00000094
+
+#define VIVS_PM							0x00000000
+
+#define VIVS_PM_POWER_CONTROLS					0x00000100
+#define VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING	0x00000001
+#define VIVS_PM_POWER_CONTROLS_DISABLE_STALL_MODULE_CLOCK_GATING	0x00000002
+#define VIVS_PM_POWER_CONTROLS_DISABLE_STARVE_MODULE_CLOCK_GATING	0x00000004
+#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__MASK		0x000000f0
+#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__SHIFT		4
+#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER(x)		(((x) << VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__SHIFT) & VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__MASK)
+#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__MASK		0xffff0000
+#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__SHIFT		16
+#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER(x)		(((x) << VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__SHIFT) & VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__MASK)
+
+#define VIVS_PM_MODULE_CONTROLS					0x00000104
+#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_FE	0x00000001
+#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_DE	0x00000002
+#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_PE	0x00000004
+
+#define VIVS_PM_MODULE_STATUS					0x00000108
+#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_FE		0x00000001
+#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_DE		0x00000002
+#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_PE		0x00000004
+
+#define VIVS_PM_PULSE_EATER					0x0000010c
+
+#define VIVS_MMUv2						0x00000000
+
+#define VIVS_MMUv2_SAFE_ADDRESS					0x00000180
+
+#define VIVS_MMUv2_CONFIGURATION				0x00000184
+#define VIVS_MMUv2_CONFIGURATION_MODE__MASK			0x00000001
+#define VIVS_MMUv2_CONFIGURATION_MODE__SHIFT			0
+#define VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K			0x00000000
+#define VIVS_MMUv2_CONFIGURATION_MODE_MODE1_K			0x00000001
+#define VIVS_MMUv2_CONFIGURATION_MODE_MASK			0x00000008
+#define VIVS_MMUv2_CONFIGURATION_FLUSH__MASK			0x00000010
+#define VIVS_MMUv2_CONFIGURATION_FLUSH__SHIFT			4
+#define VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH			0x00000010
+#define VIVS_MMUv2_CONFIGURATION_FLUSH_MASK			0x00000080
+#define VIVS_MMUv2_CONFIGURATION_ADDRESS_MASK			0x00000100
+#define VIVS_MMUv2_CONFIGURATION_ADDRESS__MASK			0xfffffc00
+#define VIVS_MMUv2_CONFIGURATION_ADDRESS__SHIFT			10
+#define VIVS_MMUv2_CONFIGURATION_ADDRESS(x)			(((x) << VIVS_MMUv2_CONFIGURATION_ADDRESS__SHIFT) & VIVS_MMUv2_CONFIGURATION_ADDRESS__MASK)
+
+#define VIVS_MMUv2_STATUS					0x00000188
+#define VIVS_MMUv2_STATUS_EXCEPTION0__MASK			0x00000003
+#define VIVS_MMUv2_STATUS_EXCEPTION0__SHIFT			0
+#define VIVS_MMUv2_STATUS_EXCEPTION0(x)				(((x) << VIVS_MMUv2_STATUS_EXCEPTION0__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION0__MASK)
+#define VIVS_MMUv2_STATUS_EXCEPTION1__MASK			0x00000030
+#define VIVS_MMUv2_STATUS_EXCEPTION1__SHIFT			4
+#define VIVS_MMUv2_STATUS_EXCEPTION1(x)				(((x) << VIVS_MMUv2_STATUS_EXCEPTION1__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION1__MASK)
+#define VIVS_MMUv2_STATUS_EXCEPTION2__MASK			0x00000300
+#define VIVS_MMUv2_STATUS_EXCEPTION2__SHIFT			8
+#define VIVS_MMUv2_STATUS_EXCEPTION2(x)				(((x) << VIVS_MMUv2_STATUS_EXCEPTION2__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION2__MASK)
+#define VIVS_MMUv2_STATUS_EXCEPTION3__MASK			0x00003000
+#define VIVS_MMUv2_STATUS_EXCEPTION3__SHIFT			12
+#define VIVS_MMUv2_STATUS_EXCEPTION3(x)				(((x) << VIVS_MMUv2_STATUS_EXCEPTION3__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION3__MASK)
+
+#define VIVS_MMUv2_CONTROL					0x0000018c
+#define VIVS_MMUv2_CONTROL_ENABLE				0x00000001
+
+#define VIVS_MMUv2_EXCEPTION_ADDR(i0)			       (0x00000190 + 0x4*(i0))
+#define VIVS_MMUv2_EXCEPTION_ADDR__ESIZE			0x00000004
+#define VIVS_MMUv2_EXCEPTION_ADDR__LEN				0x00000004
+
+#define VIVS_MC							0x00000000
+
+#define VIVS_MC_MMU_FE_PAGE_TABLE				0x00000400
+
+#define VIVS_MC_MMU_TX_PAGE_TABLE				0x00000404
+
+#define VIVS_MC_MMU_PE_PAGE_TABLE				0x00000408
+
+#define VIVS_MC_MMU_PEZ_PAGE_TABLE				0x0000040c
+
+#define VIVS_MC_MMU_RA_PAGE_TABLE				0x00000410
+
+#define VIVS_MC_DEBUG_MEMORY					0x00000414
+#define VIVS_MC_DEBUG_MEMORY_SPECIAL_PATCH_GC320		0x00000008
+#define VIVS_MC_DEBUG_MEMORY_FAST_CLEAR_BYPASS			0x00100000
+#define VIVS_MC_DEBUG_MEMORY_COMPRESSION_BYPASS			0x00200000
+
+#define VIVS_MC_MEMORY_BASE_ADDR_RA				0x00000418
+
+#define VIVS_MC_MEMORY_BASE_ADDR_FE				0x0000041c
+
+#define VIVS_MC_MEMORY_BASE_ADDR_TX				0x00000420
+
+#define VIVS_MC_MEMORY_BASE_ADDR_PEZ				0x00000424
+
+#define VIVS_MC_MEMORY_BASE_ADDR_PE				0x00000428
+
+#define VIVS_MC_MEMORY_TIMING_CONTROL				0x0000042c
+
+#define VIVS_MC_MEMORY_FLUSH					0x00000430
+
+#define VIVS_MC_PROFILE_CYCLE_COUNTER				0x00000438
+
+#define VIVS_MC_DEBUG_READ0					0x0000043c
+
+#define VIVS_MC_DEBUG_READ1					0x00000440
+
+#define VIVS_MC_DEBUG_WRITE					0x00000444
+
+#define VIVS_MC_PROFILE_RA_READ					0x00000448
+
+#define VIVS_MC_PROFILE_TX_READ					0x0000044c
+
+#define VIVS_MC_PROFILE_FE_READ					0x00000450
+
+#define VIVS_MC_PROFILE_PE_READ					0x00000454
+
+#define VIVS_MC_PROFILE_DE_READ					0x00000458
+
+#define VIVS_MC_PROFILE_SH_READ					0x0000045c
+
+#define VIVS_MC_PROFILE_PA_READ					0x00000460
+
+#define VIVS_MC_PROFILE_SE_READ					0x00000464
+
+#define VIVS_MC_PROFILE_MC_READ					0x00000468
+
+#define VIVS_MC_PROFILE_HI_READ					0x0000046c
+
+#define VIVS_MC_PROFILE_CONFIG0					0x00000470
+#define VIVS_MC_PROFILE_CONFIG0_FE__MASK			0x0000000f
+#define VIVS_MC_PROFILE_CONFIG0_FE__SHIFT			0
+#define VIVS_MC_PROFILE_CONFIG0_FE_RESET			0x0000000f
+#define VIVS_MC_PROFILE_CONFIG0_DE__MASK			0x00000f00
+#define VIVS_MC_PROFILE_CONFIG0_DE__SHIFT			8
+#define VIVS_MC_PROFILE_CONFIG0_DE_RESET			0x00000f00
+#define VIVS_MC_PROFILE_CONFIG0_PE__MASK			0x000f0000
+#define VIVS_MC_PROFILE_CONFIG0_PE__SHIFT			16
+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE	0x00000000
+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE	0x00010000
+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE	0x00020000
+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE	0x00030000
+#define VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D		0x000b0000
+#define VIVS_MC_PROFILE_CONFIG0_PE_RESET			0x000f0000
+#define VIVS_MC_PROFILE_CONFIG0_SH__MASK			0x0f000000
+#define VIVS_MC_PROFILE_CONFIG0_SH__SHIFT			24
+#define VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES		0x04000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER		0x07000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER	0x08000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER		0x09000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER	0x0a000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER	0x0b000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER	0x0c000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER	0x0d000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER	0x0e000000
+#define VIVS_MC_PROFILE_CONFIG0_SH_RESET			0x0f000000
+
+#define VIVS_MC_PROFILE_CONFIG1					0x00000474
+#define VIVS_MC_PROFILE_CONFIG1_PA__MASK			0x0000000f
+#define VIVS_MC_PROFILE_CONFIG1_PA__SHIFT			0
+#define VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER		0x00000003
+#define VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER		0x00000004
+#define VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER		0x00000005
+#define VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER	0x00000006
+#define VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER	0x00000007
+#define VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER		0x00000008
+#define VIVS_MC_PROFILE_CONFIG1_PA_RESET			0x0000000f
+#define VIVS_MC_PROFILE_CONFIG1_SE__MASK			0x00000f00
+#define VIVS_MC_PROFILE_CONFIG1_SE__SHIFT			8
+#define VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT	0x00000000
+#define VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT		0x00000100
+#define VIVS_MC_PROFILE_CONFIG1_SE_RESET			0x00000f00
+#define VIVS_MC_PROFILE_CONFIG1_RA__MASK			0x000f0000
+#define VIVS_MC_PROFILE_CONFIG1_RA__SHIFT			16
+#define VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT		0x00000000
+#define VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT		0x00010000
+#define VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z	0x00020000
+#define VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT	0x00030000
+#define VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER	0x00090000
+#define VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER	0x000a0000
+#define VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT		0x000b0000
+#define VIVS_MC_PROFILE_CONFIG1_RA_RESET			0x000f0000
+#define VIVS_MC_PROFILE_CONFIG1_TX__MASK			0x0f000000
+#define VIVS_MC_PROFILE_CONFIG1_TX__SHIFT			24
+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS	0x00000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS	0x01000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS	0x02000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS	0x03000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_UNKNOWN			0x04000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT		0x05000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT		0x06000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT		0x07000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT	0x08000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT	0x09000000
+#define VIVS_MC_PROFILE_CONFIG1_TX_RESET			0x0f000000
+
+#define VIVS_MC_PROFILE_CONFIG2					0x00000478
+#define VIVS_MC_PROFILE_CONFIG2_MC__MASK			0x0000000f
+#define VIVS_MC_PROFILE_CONFIG2_MC__SHIFT			0
+#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE	0x00000001
+#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP	0x00000002
+#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE	0x00000003
+#define VIVS_MC_PROFILE_CONFIG2_MC_RESET			0x0000000f
+#define VIVS_MC_PROFILE_CONFIG2_HI__MASK			0x00000f00
+#define VIVS_MC_PROFILE_CONFIG2_HI__SHIFT			8
+#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED	0x00000000
+#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED	0x00000100
+#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED	0x00000200
+#define VIVS_MC_PROFILE_CONFIG2_HI_RESET			0x00000f00
+
+#define VIVS_MC_PROFILE_CONFIG3					0x0000047c
+
+#define VIVS_MC_BUS_CONFIG					0x00000480
+
+#define VIVS_MC_START_COMPOSITION				0x00000554
+
+#define VIVS_MC_128B_MERGE					0x00000558
+
+
+#endif /* STATE_HI_XML */
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
new file mode 100644
index 000000000000..a9f020ed71ea
--- /dev/null
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email-masked]>
+ *
+ * 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_DRM_H__
+#define __ETNAVIV_DRM_H__
+
+#include <stddef.h>
+#include <drm/drm.h>
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints:
+ *  1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit
+ *     user/kernel compatibility
+ *  2) Keep fields aligned to their size
+ *  3) Because of how drm_ioctl() works, we can add new fields at
+ *     the end of an ioctl if some care is taken: drm_ioctl() will
+ *     zero out the new fields at the tail of the ioctl, so a zero
+ *     value should have a backwards compatible meaning.  And for
+ *     output params, userspace won't see the newly added output
+ *     fields.. so that has to be somehow ok.
+ */
+
+#define ETNA_PIPE_3D      0x00
+#define ETNA_PIPE_2D      0x01
+#define ETNA_PIPE_VG      0x02
+
+#define ETNA_MAX_PIPES    3
+
+/* timeouts are specified in clock-monotonic absolute times (to simplify
+ * restarting interrupted ioctls).  The following struct is logically the
+ * same as 'struct timespec' but 32/64b ABI safe.
+ */
+struct drm_etnaviv_timespec {
+	int64_t tv_sec;          /* seconds */
+	int64_t tv_nsec;         /* nanoseconds */
+};
+
+#define ETNAVIV_PARAM_GPU_MODEL                     0x01
+#define ETNAVIV_PARAM_GPU_REVISION                  0x02
+#define ETNAVIV_PARAM_GPU_FEATURES_0                0x03
+#define ETNAVIV_PARAM_GPU_FEATURES_1                0x04
+#define ETNAVIV_PARAM_GPU_FEATURES_2                0x05
+#define ETNAVIV_PARAM_GPU_FEATURES_3                0x06
+#define ETNAVIV_PARAM_GPU_FEATURES_4                0x07
+
+#define ETNAVIV_PARAM_GPU_STREAM_COUNT              0x10
+#define ETNAVIV_PARAM_GPU_REGISTER_MAX              0x11
+#define ETNAVIV_PARAM_GPU_THREAD_COUNT              0x12
+#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE         0x13
+#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT         0x14
+#define ETNAVIV_PARAM_GPU_PIXEL_PIPES               0x15
+#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16
+#define ETNAVIV_PARAM_GPU_BUFFER_SIZE               0x17
+#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT         0x18
+#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS             0x19
+
+//#define MSM_PARAM_GMEM_SIZE  0x02
+
+struct drm_etnaviv_param {
+	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t param;          /* in, ETNAVIV_PARAM_x */
+	uint64_t value;          /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define ETNA_BO_CMDSTREAM    0x00000001
+#define ETNA_BO_CACHE_MASK   0x000f0000
+/* cache modes */
+#define ETNA_BO_CACHED       0x00010000
+#define ETNA_BO_WC           0x00020000
+#define ETNA_BO_UNCACHED     0x00040000
+
+struct drm_etnaviv_gem_new {
+	uint64_t size;           /* in */
+	uint32_t flags;          /* in, mask of ETNA_BO_x */
+	uint32_t handle;         /* out */
+};
+
+struct drm_etnaviv_gem_info {
+	uint32_t handle;         /* in */
+	uint32_t pad;
+	uint64_t offset;         /* out, offset to pass to mmap() */
+};
+
+#define ETNA_PREP_READ        0x01
+#define ETNA_PREP_WRITE       0x02
+#define ETNA_PREP_NOSYNC      0x04
+
+struct drm_etnaviv_gem_cpu_prep {
+	uint32_t handle;         /* in */
+	uint32_t op;             /* in, mask of ETNA_PREP_x */
+	struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+struct drm_etnaviv_gem_cpu_fini {
+	uint32_t handle;         /* in */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ *
+ *   ((relocbuf->gpuaddr + reloc_offset) << shift) | or
+ *
+ * When we have GPU's w/ >32bit ptrs, it should be possible to deal
+ * with this by emit'ing two reloc entries with appropriate shift
+ * values.  Or a new ETNA_SUBMIT_CMD_x type would also be an option.
+ *
+ * NOTE that reloc's must be sorted by order of increasing submit_offset,
+ * otherwise EINVAL.
+ */
+struct drm_etnaviv_gem_submit_reloc {
+	uint32_t submit_offset;  /* in, offset from submit_bo */
+	uint32_t or;             /* in, value OR'd with result */
+	int32_t  shift;          /* in, amount of left shift (can be negative) */
+	uint32_t reloc_idx;      /* in, index of reloc_bo buffer */
+	uint64_t reloc_offset;   /* in, offset from start of reloc_bo */
+};
+
+/* submit-types:
+ *   BUF - this cmd buffer is executed normally.
+ *   IB_TARGET_BUF - this cmd buffer is an IB target.  Reloc's are
+ *      processed normally, but the kernel does not setup an IB to
+ *      this buffer in the first-level ringbuffer
+ *   CTX_RESTORE_BUF - only executed if there has been a GPU context
+ *      switch since the last SUBMIT ioctl
+ */
+#define ETNA_SUBMIT_CMD_BUF             0x0001
+#define ETNA_SUBMIT_CMD_IB_TARGET_BUF   0x0002
+#define ETNA_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003
+struct drm_etnaviv_gem_submit_cmd {
+	uint32_t type;           /* in, one of ETNA_SUBMIT_CMD_x */
+	uint32_t submit_idx;     /* in, index of submit_bo cmdstream buffer */
+	uint32_t submit_offset;  /* in, offset into submit_bo */
+	uint32_t size;           /* in, cmdstream size */
+	uint32_t pad;
+	uint32_t nr_relocs;      /* in, number of submit_reloc's */
+	uint64_t relocs;         /* in, ptr to array of submit_reloc's */
+};
+
+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
+ * cmdstream buffer(s) themselves or reloc entries) has one (and only
+ * one) entry in the submit->bos[] table.
+ *
+ * As a optimization, the current buffer (gpu virtual address) can be
+ * passed back through the 'presumed' field.  If on a subsequent reloc,
+ * userspace passes back a 'presumed' address that is still valid,
+ * then patching the cmdstream for this entry is skipped.  This can
+ * avoid kernel needing to map/access the cmdstream bo in the common
+ * case.
+ */
+#define ETNA_SUBMIT_BO_READ             0x0001
+#define ETNA_SUBMIT_BO_WRITE            0x0002
+struct drm_etnaviv_gem_submit_bo {
+	uint32_t flags;          /* in, mask of ETNA_SUBMIT_BO_x */
+	uint32_t handle;         /* in, GEM handle */
+	uint64_t presumed;       /* in/out, presumed buffer address */
+};
+
+/* Each cmdstream submit consists of a table of buffers involved, and
+ * one or more cmdstream buffers.  This allows for conditional execution
+ * (context-restore), and IB buffers needed for per tile/bin draw cmds.
+ */
+struct drm_etnaviv_gem_submit {
+	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t fence;          /* out */
+	uint32_t nr_bos;         /* in, number of submit_bo's */
+	uint32_t nr_cmds;        /* in, number of submit_cmd's */
+	uint64_t bos;            /* in, ptr to array of submit_bo's */
+	uint64_t cmds;           /* in, ptr to array of submit_cmd's */
+};
+
+/* The normal way to synchronize with the GPU is just to CPU_PREP on
+ * a buffer if you need to access it from the CPU (other cmdstream
+ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all
+ * handle the required synchronization under the hood).  This ioctl
+ * mainly just exists as a way to implement the gallium pipe_fence
+ * APIs without requiring a dummy bo to synchronize on.
+ */
+struct drm_etnaviv_wait_fence {
+	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t fence;          /* in */
+	struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+#define DRM_ETNAVIV_GET_PARAM          0x00
+/* placeholder:
+#define DRM_MSM_SET_PARAM              0x01
+ */
+#define DRM_ETNAVIV_GEM_NEW            0x02
+#define DRM_ETNAVIV_GEM_INFO           0x03
+#define DRM_ETNAVIV_GEM_CPU_PREP       0x04
+#define DRM_ETNAVIV_GEM_CPU_FINI       0x05
+#define DRM_ETNAVIV_GEM_SUBMIT         0x06
+#define DRM_ETNAVIV_WAIT_FENCE         0x07
+#define DRM_ETNAVIV_NUM_IOCTLS         0x08
+
+#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_GEM_INFO     DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
+#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT   DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
+#define DRM_IOCTL_ETNAVIV_WAIT_FENCE   DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
+
+#endif /* __ETNAVIV_DRM_H__ */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     | 10 ++++++++--
 drivers/staging/etnaviv/etnaviv_gem.h        |  1 +
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  8 ++++++++
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 6aabf8ce37d6..622852ae73e4 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -147,7 +147,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 
 	/* update offset for every cmd stream */
 	for (i = 0; i < submit->nr_cmds; i++)
-		submit->cmd[i].obj->offset = submit->cmd[i].size;
+		submit->cmd[i].obj->offset = submit->cmd[i].offset +
+					     submit->cmd[i].size;
 
 	/* TODO: inter-connect all cmd buffers */
 
@@ -155,6 +156,11 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	cmd = submit->cmd[submit->nr_cmds - 1].obj;
 	CMD_LINK(cmd, 4, buffer->paddr + (back * 4));
 
+	/* update the size */
+	for (i = 0; i < submit->nr_cmds; i++)
+		submit->cmd[i].size = submit->cmd[i].obj->offset -
+				      submit->cmd[i].offset;
+
 	printk(KERN_ERR "stream link @ 0x%llx\n", (u64)cmd->paddr + ((cmd->offset - 1) * 4));
 	printk(KERN_ERR "stream link @ %p\n", cmd->vaddr + ((cmd->offset - 1) * 4));
 
@@ -175,7 +181,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 
 	/* Change WAIT into a LINK command; write the address first. */
 	i = VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(submit->cmd[0].size * 2);
-	*(lw + 1) = submit->cmd[0].obj->paddr;
+	*(lw + 1) = submit->cmd[0].obj->paddr + submit->cmd[0].offset * 4;
 	mb();
 	*(lw)= i;
 	mb();
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 5ce38d9f7a19..5dffce39d59e 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -88,6 +88,7 @@ struct etnaviv_gem_submit {
 	unsigned int nr_bos;
 	struct {
 		uint32_t type;
+		uint32_t offset; /* in dwords */
 		uint32_t size;  /* in dwords */
 		struct etnaviv_gem_object *obj;
 	} cmd[MAX_CMDS];
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 78c56adfcffc..7eb02a121cff 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -382,6 +382,13 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 			goto out;
 		}
 
+		if (submit_cmd.submit_offset % 8) {
+			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
+					submit_cmd.size);
+			ret = -EINVAL;
+			goto out;
+		}
+
 		/*
 		 * We must have space to add a LINK command at the end of
 		 * the command buffer.
@@ -396,6 +403,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		}
 
 		submit->cmd[i].type = submit_cmd.type;
+		submit->cmd[i].offset = submit_cmd.submit_offset / 4;
 		submit->cmd[i].size = submit_cmd.size / 4;
 		submit->cmd[i].obj = etnaviv_obj;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

We don't always want to dump the start of the buffer.  Pass a byte
offset from the beginning of the buffer to be dumped.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 622852ae73e4..9b444a9d6519 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -97,13 +97,13 @@ static void cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
 }
 
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
-	struct etnaviv_gem_object *obj, u32 len)
+	struct etnaviv_gem_object *obj, u32 off, u32 len)
 {
 	u32 size = obj->base.size;
-	u32 *ptr = obj->vaddr;
+	u32 *ptr = obj->vaddr + off;
 
 	dev_info(gpu->dev->dev, "virt %p phys 0x%llx free 0x%08x\n",
-			obj->vaddr, (u64)obj->paddr, size - len * 4);
+			ptr, (u64)obj->paddr + off, size - len * 4 - off);
 
 	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
 			ptr, len * 4, 0);
@@ -133,7 +133,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	u32 back;
 	u32 i;
 
-	etnaviv_buffer_dump(gpu, buffer, 0x50);
+	etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 
 	/* save offset back into main buffer */
 	back = buffer->offset;
@@ -169,7 +169,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 
 		/* TODO: remove later */
 		if (unlikely(drm_debug & DRM_UT_CORE))
-			etnaviv_buffer_dump(gpu, obj, submit->cmd[i].size);
+			etnaviv_buffer_dump(gpu, obj, submit->cmd[i].offset * 4,
+					    submit->cmd[i].size);
 	}
 
 	/* change ll to NOP */
@@ -186,5 +187,5 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	*(lw)= i;
 	mb();
 
-	etnaviv_buffer_dump(gpu, buffer, 0x50);
+	etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

The submission debug was always being printed, and printed at error
level.  Contain this debug within DRM_UT_DRIVER, and reduce it down
to info level.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 37 +++++++++++++++++---------------
 1 file changed, 20 insertions(+), 17 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 9b444a9d6519..facf9c7af245 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -133,7 +133,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	u32 back;
 	u32 i;
 
-	etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
+	if (drm_debug & DRM_UT_DRIVER)
+		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 
 	/* save offset back into main buffer */
 	back = buffer->offset;
@@ -161,24 +162,25 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 		submit->cmd[i].size = submit->cmd[i].obj->offset -
 				      submit->cmd[i].offset;
 
-	printk(KERN_ERR "stream link @ 0x%llx\n", (u64)cmd->paddr + ((cmd->offset - 1) * 4));
-	printk(KERN_ERR "stream link @ %p\n", cmd->vaddr + ((cmd->offset - 1) * 4));
+	if (drm_debug & DRM_UT_DRIVER) {
+		pr_info("stream link @ 0x%llx\n",
+			(u64)cmd->paddr + ((cmd->offset - 1) * 4));
+		pr_info("stream link @ %p\n",
+			cmd->vaddr + ((cmd->offset - 1) * 4));
 
-	for (i = 0; i < submit->nr_cmds; i++) {
-		struct etnaviv_gem_object *obj = submit->cmd[i].obj;
+		for (i = 0; i < submit->nr_cmds; i++) {
+			struct etnaviv_gem_object *obj = submit->cmd[i].obj;
 
-		/* TODO: remove later */
-		if (unlikely(drm_debug & DRM_UT_CORE))
-			etnaviv_buffer_dump(gpu, obj, submit->cmd[i].offset * 4,
-					    submit->cmd[i].size);
-	}
+			etnaviv_buffer_dump(gpu, obj, submit->cmd[i].offset,
+					submit->cmd[i].size);
+		}
 
-	/* change ll to NOP */
-	printk(KERN_ERR "link op: %p\n", lw);
-	printk(KERN_ERR "link addr: %p\n", lw + 1);
-	printk(KERN_ERR "addr: 0x%llx\n", (u64)submit->cmd[0].obj->paddr);
-	printk(KERN_ERR "back: 0x%llx\n", (u64)buffer->paddr + (back * 4));
-	printk(KERN_ERR "event: %d\n", event);
+		pr_info("link op: %p\n", lw);
+		pr_info("link addr: %p\n", lw + 1);
+		pr_info("addr: 0x%llx\n", (u64)submit->cmd[0].obj->paddr);
+		pr_info("back: 0x%llx\n", (u64)buffer->paddr + (back * 4));
+		pr_info("event: %d\n", event);
+	}
 
 	/* Change WAIT into a LINK command; write the address first. */
 	i = VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(submit->cmd[0].size * 2);
@@ -187,5 +189,6 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	*(lw)= i;
 	mb();
 
-	etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
+	if (drm_debug & DRM_UT_DRIVER)
+		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

etnaviv_buffer_queue() could not handle multiple command buffers.  We
can handle this trivially by adapting the existing code - we record
where we want to link to, and walk the submitted command buffers in
reverse order, appending a LINK command to the previous target.

This also means that we conveniently end up with the address and size
to link to when changing the previous WAIT command.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 45 +++++++++++++++++---------------
 1 file changed, 24 insertions(+), 21 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index facf9c7af245..87c5bd14f4bf 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -130,7 +130,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
 	struct etnaviv_gem_object *cmd;
 	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
-	u32 back;
+	u32 back, link_target, link_size;
 	u32 i;
 
 	if (drm_debug & DRM_UT_DRIVER)
@@ -138,6 +138,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 
 	/* save offset back into main buffer */
 	back = buffer->offset;
+	link_target = buffer->paddr + buffer->offset * 4;
+	link_size = 6;
 
 	/* trigger event */
 	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | VIVS_GL_EVENT_FROM_PE);
@@ -147,27 +149,28 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
 
 	/* update offset for every cmd stream */
-	for (i = 0; i < submit->nr_cmds; i++)
-		submit->cmd[i].obj->offset = submit->cmd[i].offset +
-					     submit->cmd[i].size;
+	for (i = submit->nr_cmds; i--; ) {
+		cmd = submit->cmd[i].obj;
 
-	/* TODO: inter-connect all cmd buffers */
+		cmd->offset = submit->cmd[i].offset + submit->cmd[i].size;
 
-	/* jump back from last cmd to main buffer */
-	cmd = submit->cmd[submit->nr_cmds - 1].obj;
-	CMD_LINK(cmd, 4, buffer->paddr + (back * 4));
+		if (drm_debug & DRM_UT_DRIVER)
+			pr_info("stream link from buffer %u to 0x%llx @ 0x%llx %p\n",
+				i, (u64)link_target,
+				(u64)cmd->paddr + cmd->offset * 4,
+				cmd->vaddr + cmd->offset * 4);
 
-	/* update the size */
-	for (i = 0; i < submit->nr_cmds; i++)
-		submit->cmd[i].size = submit->cmd[i].obj->offset -
-				      submit->cmd[i].offset;
+		/* jump back from last cmd to main buffer */
+		CMD_LINK(cmd, link_size, link_target);
 
-	if (drm_debug & DRM_UT_DRIVER) {
-		pr_info("stream link @ 0x%llx\n",
-			(u64)cmd->paddr + ((cmd->offset - 1) * 4));
-		pr_info("stream link @ %p\n",
-			cmd->vaddr + ((cmd->offset - 1) * 4));
+		/* update the size */
+		submit->cmd[i].size = cmd->offset - submit->cmd[i].offset;
+
+		link_target = cmd->paddr + submit->cmd[i].offset * 4;
+		link_size = submit->cmd[i].size * 2;
+	}
 
+	if (drm_debug & DRM_UT_DRIVER) {
 		for (i = 0; i < submit->nr_cmds; i++) {
 			struct etnaviv_gem_object *obj = submit->cmd[i].obj;
 
@@ -177,16 +180,16 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 
 		pr_info("link op: %p\n", lw);
 		pr_info("link addr: %p\n", lw + 1);
-		pr_info("addr: 0x%llx\n", (u64)submit->cmd[0].obj->paddr);
+		pr_info("addr: 0x%llx\n", (u64)link_target);
 		pr_info("back: 0x%llx\n", (u64)buffer->paddr + (back * 4));
 		pr_info("event: %d\n", event);
 	}
 
 	/* Change WAIT into a LINK command; write the address first. */
-	i = VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(submit->cmd[0].size * 2);
-	*(lw + 1) = submit->cmd[0].obj->paddr + submit->cmd[0].offset * 4;
+	*(lw + 1) = link_target;
 	mb();
-	*(lw)= i;
+	*(lw) = VIV_FE_LINK_HEADER_OP_LINK |
+		VIV_FE_LINK_HEADER_PREFETCH(link_size);
 	mb();
 
 	if (drm_debug & DRM_UT_DRIVER)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Combine the event data into an array of etnaviv_event structures,
rather than individual arrays.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 18 +++++++++---------
 drivers/staging/etnaviv/etnaviv_gpu.h |  8 ++++++--
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index c6396216eb9a..a7857da6bc42 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -382,8 +382,8 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	/* Setup event management */
 	spin_lock_init(&gpu->event_spinlock);
 	init_completion(&gpu->event_free);
-	for (i = 0; i < ARRAY_SIZE(gpu->event_used); i++) {
-		gpu->event_used[i] = false;
+	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
+		gpu->event[i].used = false;
 		complete(&gpu->event_free);
 	}
 
@@ -683,9 +683,9 @@ static unsigned int event_alloc(struct etnaviv_gpu *gpu)
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 
 	/* find first free event */
-	for (i = 0; i < ARRAY_SIZE(gpu->event_used); i++) {
-		if (gpu->event_used[i] == false) {
-			gpu->event_used[i] = true;
+	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
+		if (gpu->event[i].used == false) {
+			gpu->event[i].used = true;
 			event = i;
 			break;
 		}
@@ -702,11 +702,11 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 
-	if (gpu->event_used[event] == false) {
+	if (gpu->event[event].used == false) {
 		dev_warn(gpu->dev->dev, "event %u is already marked as free", event);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	} else {
-		gpu->event_used[event] = false;
+		gpu->event[event].used = false;
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 
 		complete(&gpu->event_free);
@@ -783,7 +783,7 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submi
 		goto fail;
 	}
 
-	gpu->event_to_fence[event] = submit->fence;
+	gpu->event[event].fence = submit->fence;
 
 	etnaviv_buffer_queue(gpu, event, submit);
 
@@ -835,7 +835,7 @@ static irqreturn_t irq_handler(int irq, void *data)
 		else {
 			uint8_t event = __fls(intr);
 			dev_dbg(gpu->dev->dev, "event %u\n", event);
-			gpu->retired_fence = gpu->event_to_fence[event];
+			gpu->retired_fence = gpu->event[event].fence;
 			event_free(gpu, event);
 			etnaviv_gpu_retire(gpu);
 		}
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index f918542c0a82..81c5d70e7547 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -78,6 +78,11 @@ struct etnaviv_chip_identity {
 	uint32_t buffer_size;
 };
 
+struct etnaviv_event {
+	bool used;
+	uint32_t fence;
+};
+
 struct etnaviv_gpu {
 	const char *name;
 	struct drm_device *dev;
@@ -88,8 +93,7 @@ struct etnaviv_gpu {
 	struct drm_gem_object *buffer;
 
 	/* event management: */
-	bool event_used[30];
-	uint32_t event_to_fence[30];
+	struct etnaviv_event event[30];
 	struct completion event_free;
 	struct spinlock event_spinlock;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Ensure that the ring buffer wraps when we fill the buffer.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 87c5bd14f4bf..08ff5213856a 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -30,7 +30,7 @@
 static inline void OUT(struct etnaviv_gem_object *buffer, uint32_t data)
 {
 	u32 *vaddr = (u32 *)buffer->vaddr;
-	BUG_ON(buffer->offset * sizeof(*vaddr) >= buffer->base.size);
+	BUG_ON(buffer->offset >= buffer->base.size / sizeof(*vaddr));
 
 	vaddr[buffer->offset++] = data;
 }
@@ -136,6 +136,12 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	if (drm_debug & DRM_UT_DRIVER)
 		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 
+	/*
+	 * if we are going to completely overflow the buffer, we need to wrap.
+	 */
+	if (buffer->offset + 6 > buffer->base.size / sizeof(uint32_t))
+		buffer->offset = 0;
+
 	/* save offset back into main buffer */
 	back = buffer->offset;
 	link_target = buffer->paddr + buffer->offset * 4;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

ERROR: do not initialise statics to 0 or NULL
+static bool reglog = false;

ERROR: "foo * bar" should be "foo *bar"
+int etnaviv_gem_get_iova_locked(struct etnaviv_gpu * gpu, struct drm_gem_object *obj,

ERROR: do not use C99 // comments
+       // XXX TODO ..
...
ERROR: spaces required around that '=' (ctx:VxW)
+       struct etnaviv_gem_object *etnaviv_obj= to_etnaviv_bo(obj);

ERROR: do not use C99 // comments
+//#define MSM_PARAM_GMEM_SIZE  0x02

ERROR: Macros with complex values should be enclosed in parenthesis
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
...

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c       | 18 ++++++++++--------
 drivers/staging/etnaviv/etnaviv_gem_prime.c |  2 +-
 include/uapi/drm/etnaviv_drm.h              |  8 ++++----
 3 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 01ac6e29769b..2701ba9beb23 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -256,8 +256,8 @@ uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj)
  * That means when I do eventually need to add support for unpinning
  * the refcnt counter needs to be atomic_t.
  */
-int etnaviv_gem_get_iova_locked(struct etnaviv_gpu * gpu, struct drm_gem_object *obj,
-		uint32_t *iova)
+int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
+	struct drm_gem_object *obj, uint32_t *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	int ret = 0;
@@ -317,12 +317,14 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, in
 
 void etnaviv_gem_put_iova(struct drm_gem_object *obj)
 {
-	// XXX TODO ..
-	// NOTE: probably don't need a _locked() version.. we wouldn't
-	// normally unmap here, but instead just mark that it could be
-	// unmapped (if the iova refcnt drops to zero), but then later
-	// if another _get_iova_locked() fails we can start unmapping
-	// things that are no longer needed..
+	/*
+	 * XXX TODO ..
+	 * NOTE: probably don't need a _locked() version.. we wouldn't
+	 * normally unmap here, but instead just mark that it could be
+	 * unmapped (if the iova refcnt drops to zero), but then later
+	 * if another _get_iova_locked() fails we can start unmapping
+	 * things that are no longer needed..
+	 */
 }
 
 int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index e1a8352ff59d..fcd642636ce1 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -21,7 +21,7 @@
 
 struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
 {
-	struct etnaviv_gem_object *etnaviv_obj= to_etnaviv_bo(obj);
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	BUG_ON(!etnaviv_obj->sgt);  /* should have already pinned! */
 	return etnaviv_obj->sgt;
 }
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index a9f020ed71ea..76596fc46150 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -68,7 +68,7 @@ struct drm_etnaviv_timespec {
 #define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT         0x18
 #define ETNAVIV_PARAM_GPU_NUM_CONSTANTS             0x19
 
-//#define MSM_PARAM_GMEM_SIZE  0x02
+/* #define MSM_PARAM_GMEM_SIZE  0x02 */
 
 struct drm_etnaviv_param {
 	uint32_t pipe;           /* in, ETNA_PIPE_x */
@@ -217,9 +217,9 @@ struct drm_etnaviv_wait_fence {
 #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_GEM_INFO     DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info)
-#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
-#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
 #define DRM_IOCTL_ETNAVIV_GEM_SUBMIT   DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
-#define DRM_IOCTL_ETNAVIV_WAIT_FENCE   DRM_IOW (DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
+#define DRM_IOCTL_ETNAVIV_WAIT_FENCE   DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
 
 #endif /* __ETNAVIV_DRM_H__ */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Fix many checkpatch warnings.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     |  31 +++--
 drivers/staging/etnaviv/etnaviv_drv.c        |  34 +++--
 drivers/staging/etnaviv/etnaviv_drv.h        |  11 +-
 drivers/staging/etnaviv/etnaviv_gem.c        |  42 ++++--
 drivers/staging/etnaviv/etnaviv_gem_prime.c  |   2 +
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  25 ++--
 drivers/staging/etnaviv/etnaviv_gpu.c        | 185 +++++++++++++++++----------
 drivers/staging/etnaviv/etnaviv_gpu.h        |   9 +-
 drivers/staging/etnaviv/etnaviv_iommu.c      |  16 ++-
 drivers/staging/etnaviv/etnaviv_mmu.c        |   1 +
 drivers/staging/etnaviv/etnaviv_mmu.h        |  11 +-
 include/uapi/drm/etnaviv_drm.h               |   2 +-
 12 files changed, 247 insertions(+), 122 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 08ff5213856a..65158b558ce9 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -30,18 +30,23 @@
 static inline void OUT(struct etnaviv_gem_object *buffer, uint32_t data)
 {
 	u32 *vaddr = (u32 *)buffer->vaddr;
+
 	BUG_ON(buffer->offset >= buffer->base.size / sizeof(*vaddr));

-static inline void CMD_LOAD_STATE(struct etnaviv_gem_object *buffer, u32 reg, u32 value)
+static inline void CMD_LOAD_STATE(struct etnaviv_gem_object *buffer,
+	u32 reg, u32 value)
 {
+	u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR;
+
 	buffer->offset = ALIGN(buffer->offset, 2);
 
 	/* write a register via cmd stream */
-	OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | VIV_FE_LOAD_STATE_HEADER_COUNT(1) |
-			VIV_FE_LOAD_STATE_HEADER_OFFSET(reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR));
+	OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE |
+		    VIV_FE_LOAD_STATE_HEADER_COUNT(1) |
+		    VIV_FE_LOAD_STATE_HEADER_OFFSET(index));
 	OUT(buffer, value);
 }
 
@@ -59,15 +64,18 @@ static inline void CMD_WAIT(struct etnaviv_gem_object *buffer)
 	OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | 200);
 }
 
-static inline void CMD_LINK(struct etnaviv_gem_object *buffer, u16 prefetch, u32 address)
+static inline void CMD_LINK(struct etnaviv_gem_object *buffer,
+	u16 prefetch, u32 address)
 {
 	buffer->offset = ALIGN(buffer->offset, 2);
 
-	OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK | VIV_FE_LINK_HEADER_PREFETCH(prefetch));
+	OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK |
+		    VIV_FE_LINK_HEADER_PREFETCH(prefetch));
 	OUT(buffer, address);
 }
 
-static inline void CMD_STALL(struct etnaviv_gem_object *buffer, u32 from, u32 to)
+static inline void CMD_STALL(struct etnaviv_gem_object *buffer,
+	u32 from, u32 to)
 {
 	buffer->offset = ALIGN(buffer->offset, 2);
 
@@ -86,14 +94,15 @@ static void cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
 		flush = VIVS_GL_FLUSH_CACHE_TEXTURE;
 
 	stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) |
-			VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE);
+		VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE);
 
 	CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush);
 	CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall);
 
 	CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
 
-	CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, VIVS_GL_PIPE_SELECT_PIPE(pipe));
+	CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
+		       VIVS_GL_PIPE_SELECT_PIPE(pipe));
 }
 
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
@@ -125,7 +134,8 @@ u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 	return buffer->offset;
 }
 
-void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_gem_submit *submit)
+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
+	struct etnaviv_gem_submit *submit)
 {
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
 	struct etnaviv_gem_object *cmd;
@@ -148,7 +158,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct et
 	link_size = 6;
 
 	/* trigger event */
-	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | VIVS_GL_EVENT_FROM_PE);
+	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
+		       VIVS_GL_EVENT_FROM_PE);
 
 	/* append WAIT/LINK to main buffer */
 	CMD_WAIT(buffer);
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 718bc17412d3..3981029eab93 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -24,11 +24,12 @@
 void etnaviv_register_mmu(struct drm_device *dev, struct etnaviv_iommu *mmu)
 {
 	struct etnaviv_drm_private *priv = dev->dev_private;
+
 	priv->mmu = mmu;
 }
 
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
-static bool reglog = false;
+static bool reglog;
 MODULE_PARM_DESC(reglog, "Enable register read/write logging");
 module_param(reglog, bool, 0600);
 #else
@@ -64,14 +65,17 @@ void etnaviv_writel(u32 data, void __iomem *addr)
 {
 	if (reglog)
 		printk(KERN_DEBUG "IO:W %p %08x\n", addr, data);
+
 	writel(data, addr);
 }
 
 u32 etnaviv_readl(const void __iomem *addr)
 {
 	u32 val = readl(addr);
+
 	if (reglog)
 		printk(KERN_DEBUG "IO:R %p %08x\n", addr, val);
+
 	return val;
 }
 
@@ -90,6 +94,7 @@ static int etnaviv_unload(struct drm_device *dev)
 	mutex_lock(&dev->struct_mutex);
 	for (i = 0; i < ETNA_MAX_PIPES; i++) {
 		struct etnaviv_gpu *g = priv->gpu[i];
+
 		if (g)
 			etnaviv_gpu_pm_suspend(g);
 	}
@@ -114,12 +119,15 @@ static void load_gpu(struct drm_device *dev)
 
 	for (i = 0; i < ETNA_MAX_PIPES; i++) {
 		struct etnaviv_gpu *g = priv->gpu[i];
+
 		if (g) {
 			int ret;
+
 			etnaviv_gpu_pm_resume(g);
 			ret = etnaviv_gpu_init(g);
 			if (ret) {
-				dev_err(dev->dev, "%s hw init failed: %d\n", g->name, ret);
+				dev_err(dev->dev, "%s hw init failed: %d\n",
+					g->name, ret);
 				priv->gpu[i] = NULL;
 			}
 		}
@@ -363,11 +371,15 @@ static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data,
 		struct drm_file *file)
 {
 	struct drm_etnaviv_gem_new *args = data;
+
 	return etnaviv_gem_new_handle(dev, file, args->size,
 			args->flags, &args->handle);
 }
 
-#define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec })
+#define TS(t) ((struct timespec){ \
+	.tv_sec = (t).tv_sec, \
+	.tv_nsec = (t).tv_nsec \
+})
 
 static int etnaviv_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
 		struct drm_file *file)
@@ -445,13 +457,15 @@ static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data,
 }
 
 static const struct drm_ioctl_desc etnaviv_ioctls[] = {
-	DRM_IOCTL_DEF_DRV(ETNAVIV_GET_PARAM,    etnaviv_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_NEW,      etnaviv_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_INFO,     etnaviv_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_CPU_PREP, etnaviv_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_CPU_FINI, etnaviv_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-	DRM_IOCTL_DEF_DRV(ETNAVIV_GEM_SUBMIT,   etnaviv_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-	DRM_IOCTL_DEF_DRV(ETNAVIV_WAIT_FENCE,   etnaviv_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+#define ETNA_IOCTL(n, func, flags) \
+	DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
+	ETNA_IOCTL(GET_PARAM,    get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_NEW,      gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_INFO,     gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_CPU_PREP, gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_CPU_FINI, gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_SUBMIT,   gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
 static const struct vm_operations_struct vm_ops = {
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index bb256acc88cc..a61419f7d50d 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -78,9 +78,10 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj);
-int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
-		uint32_t *iova);
-int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, int id, uint32_t *iova);
+int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
+	struct drm_gem_object *obj, uint32_t *iova);
+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+	int id, uint32_t *iova);
 struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj);
 void msm_gem_put_pages(struct drm_gem_object *obj);
 void etnaviv_gem_put_iova(struct drm_gem_object *obj);
@@ -112,7 +113,8 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 struct drm_gem_object *msm_gem_import(struct drm_device *dev,
 		struct dma_buf_attachment *attach, struct sg_table *sgt);
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
-void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_gem_submit *submit);
+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
+	struct etnaviv_gem_submit *submit);
 
 #ifdef CONFIG_DEBUG_FS
 void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m);
@@ -147,6 +149,7 @@ static inline bool fence_completed(struct drm_device *dev, uint32_t fence)
 static inline int align_pitch(int width, int bpp)
 {
 	int bytespp = (bpp + 7) / 8;
+
 	/* adreno needs pitch aligned to 32 pixels: */
 	return bytespp * ALIGN(width, 32);
 }
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 2701ba9beb23..4ec097102887 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -55,7 +55,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
 		 */
 		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
 			dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
-					etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+				   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
 	}
 
 	return etnaviv_obj->pages;
@@ -71,7 +71,8 @@ static void put_pages(struct drm_gem_object *obj)
 		 */
 		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
 			dma_unmap_sg(obj->dev->dev, etnaviv_obj->sgt->sgl,
-					etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+				     etnaviv_obj->sgt->nents,
+				     DMA_BIDIRECTIONAL);
 		sg_free_table(etnaviv_obj->sgt);
 		kfree(etnaviv_obj->sgt);
 
@@ -85,9 +86,11 @@ struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct page **p;
+
 	mutex_lock(&dev->struct_mutex);
 	p = get_pages(obj);
 	mutex_unlock(&dev->struct_mutex);
+
 	return p;
 }
 
@@ -121,14 +124,17 @@ static int etnaviv_gem_mmap_obj(struct drm_gem_object *obj,
 		struct vm_area_struct *vma)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	pgprot_t vm_page_prot;
 
 	vma->vm_flags &= ~VM_PFNMAP;
 	vma->vm_flags |= VM_MIXEDMAP;
 
+	vm_page_prot = vm_get_page_prot(vma->vm_flags);
+
 	if (etnaviv_obj->flags & ETNA_BO_WC) {
-		vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+		vma->vm_page_prot = pgprot_writecombine(vm_page_prot);
 	} else if (etnaviv_obj->flags & ETNA_BO_UNCACHED) {
-		vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
+		vma->vm_page_prot = pgprot_noncached(vm_page_prot);
 	} else {
 		/*
 		 * Shunt off cached objs to shmem file so they have their own
@@ -140,7 +146,7 @@ static int etnaviv_gem_mmap_obj(struct drm_gem_object *obj,
 		vma->vm_pgoff = 0;
 		vma->vm_file  = obj->filp;
 
-		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+		vma->vm_page_prot = vm_page_prot;
 	}
 
 	return 0;
@@ -243,9 +249,11 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
 uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj)
 {
 	uint64_t offset;
+
 	mutex_lock(&obj->dev->struct_mutex);
 	offset = mmap_offset(obj);
 	mutex_unlock(&obj->dev->struct_mutex);
+
 	return offset;
 }
 
@@ -296,7 +304,8 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	return ret;
 }
 
-int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, int id, uint32_t *iova)
+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+	int id, uint32_t *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	int ret;
@@ -312,6 +321,7 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, in
 	mutex_lock(&obj->dev->struct_mutex);
 	ret = etnaviv_gem_get_iova_locked(gpu, obj, iova);
 	mutex_unlock(&obj->dev->struct_mutex);
+
 	return ret;
 }
 
@@ -361,29 +371,37 @@ int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
 void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
 	WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
+
 	if (!etnaviv_obj->vaddr) {
 		struct page **pages = get_pages(obj);
+
 		if (IS_ERR(pages))
 			return ERR_CAST(pages);
+
 		etnaviv_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
 				VM_MAP, pgprot_writecombine(PAGE_KERNEL));
 	}
+
 	return etnaviv_obj->vaddr;
 }
 
 void *etnaviv_gem_vaddr(struct drm_gem_object *obj)
 {
 	void *ret;
+
 	mutex_lock(&obj->dev->struct_mutex);
 	ret = etnaviv_gem_vaddr_locked(obj);
 	mutex_unlock(&obj->dev->struct_mutex);
+
 	return ret;
 }
 
 dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
 	WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
 
 	return etnaviv_obj->paddr;
@@ -393,11 +411,14 @@ void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
 		struct etnaviv_gpu *gpu, bool write, uint32_t fence)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
 	etnaviv_obj->gpu = gpu;
+
 	if (write)
 		etnaviv_obj->write_fence = fence;
 	else
 		etnaviv_obj->read_fence = fence;
+
 	list_del_init(&etnaviv_obj->mm_list);
 	list_add_tail(&etnaviv_obj->mm_list, &gpu->active_list);
 }
@@ -460,6 +481,7 @@ static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 	uint64_t off = drm_vma_node_start(&obj->vma_node);
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
 	seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %zd\n",
 			etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I',
 			etnaviv_obj->read_fence, etnaviv_obj->write_fence,
@@ -475,6 +497,7 @@ void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m)
 
 	list_for_each_entry(etnaviv_obj, list, mm_list) {
 		struct drm_gem_object *obj = &etnaviv_obj->base;
+
 		seq_puts(m, "   ");
 		etnaviv_gem_describe(obj, m);
 		count++;
@@ -505,6 +528,7 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 
 	if (mmu && etnaviv_obj->iova) {
 		uint32_t offset = etnaviv_obj->gpu_vram_node->start;
+
 		etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt, obj->size);
 		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
 		kfree(etnaviv_obj->gpu_vram_node);
@@ -514,7 +538,8 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 
 	if (obj->import_attach) {
 		if (etnaviv_obj->vaddr)
-			dma_buf_vunmap(obj->import_attach->dmabuf, etnaviv_obj->vaddr);
+			dma_buf_vunmap(obj->import_attach->dmabuf,
+				       etnaviv_obj->vaddr);
 
 		/* Don't drop the pages for imported dmabuf, as they are not
 		 * ours, just free the array we allocated:
@@ -695,7 +720,8 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
 		goto fail;
 	}
 
-	ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages, NULL, npages);
+	ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages,
+					       NULL, npages);
 	if (ret)
 		goto fail;
 
index fcd642636ce1..2f53ffc59158 100644
@@ -22,7 +22,9 @@
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 7eb02a121cff..af3718465ea1 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -92,7 +92,8 @@ static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
 		 */
 		obj = idr_find(&file->object_idr, submit_bo.handle);
 		if (!obj) {
-			DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);
+			DRM_ERROR("invalid handle %u at index %u\n",
+				  submit_bo.handle, i);
 			ret = -EINVAL;
 			goto out_unlock;
 		}
@@ -101,7 +102,7 @@ static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
 
 		if (!list_empty(&etnaviv_obj->submit_entry)) {
 			DRM_ERROR("handle %u at index %u already on submit list\n",
-					submit_bo.handle, i);
+				  submit_bo.handle, i);
 			ret = -EINVAL;
 			goto out_unlock;
 		}
@@ -163,7 +164,8 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)

 		/* if locking succeeded, pin bo: */
-		ret = etnaviv_gem_get_iova_locked(submit->gpu, &etnaviv_obj->base, &iova);
+		ret = etnaviv_gem_get_iova_locked(submit->gpu,
+						  &etnaviv_obj->base, &iova);
 
 		/* this would break the logic in the fail path.. there is no
 		 * reason for this to happen, but just to be on the safe side
@@ -197,7 +199,10 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)
 		submit_unlock_unpin_bo(submit, slow_locked);
 
 	if (ret == -EDEADLK) {
-		struct etnaviv_gem_object *etnaviv_obj = submit->bos[contended].obj;
+		struct etnaviv_gem_object *etnaviv_obj;
+
+		etnaviv_obj = submit->bos[contended].obj;
+
 		/* we lost out in a seqno race, lock and retry.. */
 		ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock,
 				&submit->ticket);
@@ -251,7 +256,8 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 		uint32_t iova, off;
 		bool valid;
 
-		ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc));
+		ret = copy_from_user(&submit_reloc, userptr,
+				     sizeof(submit_reloc));
 		if (ret)
 			return -EFAULT;
 
@@ -305,6 +311,7 @@ static void submit_cleanup(struct etnaviv_gem_submit *submit, bool fail)
 
 	for (i = 0; i < submit->nr_bos; i++) {
 		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+
 		submit_unlock_unpin_bo(submit, i);
 		list_del_init(&etnaviv_obj->submit_entry);
 		drm_gem_object_unreference(&etnaviv_obj->base);
@@ -397,7 +404,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 
 		if (submit_cmd.size > max_size ||
 		    submit_cmd.submit_offset > max_size - submit_cmd.size) {
-			DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
+			DRM_ERROR("invalid cmdstream size: %u\n",
+				  submit_cmd.size);
 			ret = -EINVAL;
 			goto out;
 		}
@@ -410,8 +418,9 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		if (submit->valid)
 			continue;
 
-		ret = submit_reloc(submit, etnaviv_obj, submit_cmd.submit_offset,
-				submit_cmd.nr_relocs, submit_cmd.relocs);
+		ret = submit_reloc(submit, etnaviv_obj,
+				   submit_cmd.submit_offset,
+				   submit_cmd.nr_relocs, submit_cmd.relocs);
 		if (ret)
 			goto out;
 	}
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index a7857da6bc42..0ec5a238f939 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -32,7 +32,8 @@
  * Driver functions:
  */
 
-int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param, uint64_t *value)
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
+	uint64_t *value)
 {
 	switch (param) {
 	case ETNAVIV_PARAM_GPU_MODEL:
@@ -113,37 +114,49 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param, uint64_t *val
 
 static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 {
-	if (gpu->identity.minor_features0 & chipMinorFeatures0_MORE_MINOR_FEATURES) {
+	if (gpu->identity.minor_features0 &
+	    chipMinorFeatures0_MORE_MINOR_FEATURES) {
 		u32 specs[2];
 
 		specs[0] = gpu_read(gpu, VIVS_HI_CHIP_SPECS);
 		specs[1] = gpu_read(gpu, VIVS_HI_CHIP_SPECS_2);
 
-		gpu->identity.stream_count = (specs[0] & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK)
+		gpu->identity.stream_count =
+			(specs[0] & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK)
 				>> VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT;
-		gpu->identity.register_max = (specs[0] & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK)
+		gpu->identity.register_max =
+			(specs[0] & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK)
 				>> VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT;
-		gpu->identity.thread_count = (specs[0] & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK)
+		gpu->identity.thread_count =
+			(specs[0] & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK)
 				>> VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT;
-		gpu->identity.vertex_cache_size = (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK)
+		gpu->identity.vertex_cache_size =
+			(specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK)
 				>> VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT;
-		gpu->identity.shader_core_count = (specs[0] & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK)
+		gpu->identity.shader_core_count =
+			(specs[0] & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK)
 				>> VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT;
-		gpu->identity.pixel_pipes = (specs[0] & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK)
+		gpu->identity.pixel_pipes =
+			(specs[0] & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK)
 				>> VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT;
-		gpu->identity.vertex_output_buffer_size = (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK)
+		gpu->identity.vertex_output_buffer_size =
+			(specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK)
 				>> VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT;
 
-		gpu->identity.buffer_size = (specs[1] & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK)
+		gpu->identity.buffer_size =
+			(specs[1] & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK)
 				>> VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT;
-		gpu->identity.instruction_count = (specs[1] & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK)
+		gpu->identity.instruction_count =
+			(specs[1] & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK)
 				>> VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT;
-		gpu->identity.num_constants = (specs[1] & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK)
+		gpu->identity.num_constants =
+			(specs[1] & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK)
 				>> VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT;
 
 		gpu->identity.register_max = 1 << gpu->identity.register_max;
 		gpu->identity.thread_count = 1 << gpu->identity.thread_count;
-		gpu->identity.vertex_output_buffer_size = 1 << gpu->identity.vertex_output_buffer_size;
+		gpu->identity.vertex_output_buffer_size =
+			1 << gpu->identity.vertex_output_buffer_size;
 	} else {
 		dev_err(gpu->dev->dev, "TODO: determine GPU specs based on model\n");
 	}
@@ -166,16 +179,26 @@ static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 		break;
 	}
 
-	dev_info(gpu->dev->dev, "stream_count:  %x\n", gpu->identity.stream_count);
-	dev_info(gpu->dev->dev, "register_max: %x\n", gpu->identity.register_max);
-	dev_info(gpu->dev->dev, "thread_count: %x\n", gpu->identity.thread_count);
-	dev_info(gpu->dev->dev, "vertex_cache_size: %x\n", gpu->identity.vertex_cache_size);
-	dev_info(gpu->dev->dev, "shader_core_count: %x\n", gpu->identity.shader_core_count);
-	dev_info(gpu->dev->dev, "pixel_pipes: %x\n", gpu->identity.pixel_pipes);
-	dev_info(gpu->dev->dev, "vertex_output_buffer_size: %x\n", gpu->identity.vertex_output_buffer_size);
-	dev_info(gpu->dev->dev, "buffer_size: %x\n", gpu->identity.buffer_size);
-	dev_info(gpu->dev->dev, "instruction_count: %x\n", gpu->identity.instruction_count);
-	dev_info(gpu->dev->dev, "num_constants: %x\n", gpu->identity.num_constants);
+	dev_info(gpu->dev->dev, "stream_count:  %x\n",
+		 gpu->identity.stream_count);
+	dev_info(gpu->dev->dev, "register_max: %x\n",
+		 gpu->identity.register_max);
+	dev_info(gpu->dev->dev, "thread_count: %x\n",
+		 gpu->identity.thread_count);
+	dev_info(gpu->dev->dev, "vertex_cache_size: %x\n",
+		 gpu->identity.vertex_cache_size);
+	dev_info(gpu->dev->dev, "shader_core_count: %x\n",
+		 gpu->identity.shader_core_count);
+	dev_info(gpu->dev->dev, "pixel_pipes: %x\n",
+		 gpu->identity.pixel_pipes);
+	dev_info(gpu->dev->dev, "vertex_output_buffer_size: %x\n",
+		 gpu->identity.vertex_output_buffer_size);
+	dev_info(gpu->dev->dev, "buffer_size: %x\n",
+		 gpu->identity.buffer_size);
+	dev_info(gpu->dev->dev, "instruction_count: %x\n",
+		 gpu->identity.instruction_count);
+	dev_info(gpu->dev->dev, "num_constants: %x\n",
+		 gpu->identity.num_constants);
 }
 
 static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
@@ -193,23 +216,28 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 		gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL);
 		gpu->identity.revision = gpu_read(gpu, VIVS_HI_CHIP_REV);
 
-		/* !!!! HACK ALERT !!!! */
-		/* Because people change device IDs without letting software know
-		** about it - here is the hack to make it all look the same.  Only
-		** for GC400 family.  Next time - TELL ME!!! */
-		if (((gpu->identity.model & 0xFF00) == 0x0400)
-		&& (gpu->identity.model != 0x0420)) {
+		/*
+		 * !!!! HACK ALERT !!!!
+		 * Because people change device IDs without letting software
+		 * know about it - here is the hack to make it all look the
+		 * same.  Only for GC400 family.
+		 */
+		if ((gpu->identity.model & 0xff00) == 0x0400 &&
+		    gpu->identity.model != 0x0420) {
 			gpu->identity.model = gpu->identity.model & 0x0400;
 		}
 
-		/* An other special case */
-		if ((gpu->identity.model    == 0x300)
-		&&  (gpu->identity.revision == 0x2201)) {
+		/* Another special case */
+		if (gpu->identity.model == 0x300 &&
+		    gpu->identity.revision == 0x2201) {
 			u32 chipDate = gpu_read(gpu, VIVS_HI_CHIP_DATE);
 			u32 chipTime = gpu_read(gpu, VIVS_HI_CHIP_TIME);
 
-			if ((chipDate == 0x20080814) && (chipTime == 0x12051100)) {
-				/* This IP has an ECO; put the correct revision in it. */
+			if (chipDate == 0x20080814 && chipTime == 0x12051100) {
+				/*
+				 * This IP has an ECO; put the correct
+				 * revision in it.
+				 */
 				gpu->identity.revision = 0x1051;
 			}
 		}
@@ -224,27 +252,38 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 	if (gpu->identity.model == 0x700)
 		gpu->identity.features &= ~BIT(0);
 
-	if (((gpu->identity.model == 0x500) && (gpu->identity.revision < 2))
-	||  ((gpu->identity.model == 0x300) && (gpu->identity.revision < 0x2000))) {
+	if ((gpu->identity.model == 0x500 && gpu->identity.revision < 2) ||
+	    (gpu->identity.model == 0x300 && gpu->identity.revision < 0x2000)) {
 
-		/* GC500 rev 1.x and GC300 rev < 2.0 doesn't have these registers. */
+		/*
+		 * GC500 rev 1.x and GC300 rev < 2.0 doesn't have these
+		 * registers.
+		 */
 		gpu->identity.minor_features0 = 0;
 		gpu->identity.minor_features1 = 0;
 		gpu->identity.minor_features2 = 0;
 		gpu->identity.minor_features3 = 0;
 	} else
-		gpu->identity.minor_features0 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_0);
+		gpu->identity.minor_features0 =
+				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_0);
 
 	if (gpu->identity.minor_features0 & BIT(21)) {
-		gpu->identity.minor_features1 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_1);
-		gpu->identity.minor_features2 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_2);
-		gpu->identity.minor_features3 = gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3);
+		gpu->identity.minor_features1 =
+				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_1);
+		gpu->identity.minor_features2 =
+				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_2);
+		gpu->identity.minor_features3 =
+				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3);
 	}
 
-	dev_info(gpu->dev->dev, "minor_features:  %x\n", gpu->identity.minor_features0);
-	dev_info(gpu->dev->dev, "minor_features1: %x\n", gpu->identity.minor_features1);
-	dev_info(gpu->dev->dev, "minor_features2: %x\n", gpu->identity.minor_features2);
-	dev_info(gpu->dev->dev, "minor_features3: %x\n", gpu->identity.minor_features3);
+	dev_info(gpu->dev->dev, "minor_features:  %x\n",
+		 gpu->identity.minor_features0);
+	dev_info(gpu->dev->dev, "minor_features1: %x\n",
+		 gpu->identity.minor_features1);
+	dev_info(gpu->dev->dev, "minor_features2: %x\n",
+		 gpu->identity.minor_features2);
+	dev_info(gpu->dev->dev, "minor_features3: %x\n",
+		 gpu->identity.minor_features3);
 
 	etnaviv_hw_specs(gpu);
 }
@@ -296,7 +335,8 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 
 		/* try reseting again if FE it not idle */
 		if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) {
-			dev_dbg(gpu->dev->dev, "%s: FE is not idle\n", gpu->name);
+			dev_dbg(gpu->dev->dev, "%s: FE is not idle\n",
+				gpu->name);
 			continue;
 		}
 
@@ -306,7 +346,8 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 		/* is the GPU idle? */
 		if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0)
 		|| ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) {
-			dev_dbg(gpu->dev->dev, "%s: GPU is not idle\n", gpu->name);
+			dev_dbg(gpu->dev->dev, "%s: GPU is not idle\n",
+				gpu->name);
 			continue;
 		}
 
@@ -394,8 +435,11 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	words = ALIGN(words, 2) / 2;
 
 	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
-	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, etnaviv_gem_paddr_locked(gpu->buffer));
-	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL, VIVS_FE_COMMAND_CONTROL_ENABLE | VIVS_FE_COMMAND_CONTROL_PREFETCH(words));
+	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
+		  etnaviv_gem_paddr_locked(gpu->buffer));
+	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
+		  VIVS_FE_COMMAND_CONTROL_ENABLE |
+		  VIVS_FE_COMMAND_CONTROL_PREFETCH(words));
 
 	return 0;
 
@@ -480,13 +524,13 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 
 	seq_puts(m, "\tDMA ");
 
-	if ((debug.address[0] == debug.address[1]) && (debug.state[0] == debug.state[1])) {
+	if (debug.address[0] == debug.address[1] &&
+	    debug.state[0] == debug.state[1]) {
 		seq_puts(m, "seems to be stuck\n");
+	} else if (debug.address[0] == debug.address[1]) {
+		seq_puts(m, "adress is constant\n");
 	} else {
-		if (debug.address[0] == debug.address[1])
-			seq_puts(m, "adress is constant\n");
-		else
-			seq_puts(m, "is runing\n");
+		seq_puts(m, "is runing\n");
 	}
 
 	seq_printf(m, "\t address 0: 0x%08x\n", debug.address[0]);
@@ -511,7 +555,8 @@ static int enable_pwrrail(struct etnaviv_gpu *gpu)
 	if (gpu->gpu_reg) {
 		ret = regulator_enable(gpu->gpu_reg);
 		if (ret) {
-			dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
+			dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n",
+				ret);
 			return ret;
 		}
 	}
@@ -519,7 +564,8 @@ static int enable_pwrrail(struct etnaviv_gpu *gpu)
 	if (gpu->gpu_cx) {
 		ret = regulator_enable(gpu->gpu_cx);
 		if (ret) {
-			dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
+			dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n",
+				ret);
 			return ret;
 		}
 	}
@@ -621,7 +667,8 @@ int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu)
  */
 static void recover_worker(struct work_struct *work)
 {
-	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, recover_work);
+	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
+					       recover_work);
 	struct drm_device *dev = gpu->dev;
 
 	dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
@@ -676,7 +723,8 @@ static unsigned int event_alloc(struct etnaviv_gpu *gpu)
 	unsigned long ret, flags;
 	unsigned int i, event = ~0U;
 
-	ret = wait_for_completion_timeout(&gpu->event_free, msecs_to_jiffies(10 * 10000));
+	ret = wait_for_completion_timeout(&gpu->event_free,
+					  msecs_to_jiffies(10 * 10000));
 	if (!ret)
 		dev_err(gpu->dev->dev, "wait_for_completion_timeout failed");
 
@@ -703,7 +751,8 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 
 	if (gpu->event[event].used == false) {
-		dev_warn(gpu->dev->dev, "event %u is already marked as free", event);
+		dev_warn(gpu->dev->dev, "event %u is already marked as free",
+			 event);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	} else {
 		gpu->event[event].used = false;
@@ -719,7 +768,8 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 
 static void retire_worker(struct work_struct *work)
 {
-	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, retire_work);
+	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
+					       retire_work);
 	struct drm_device *dev = gpu->dev;
 	uint32_t fence = gpu->retired_fence;
 
@@ -751,12 +801,13 @@ static void retire_worker(struct work_struct *work)
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
 {
 	struct etnaviv_drm_private *priv = gpu->dev->dev_private;
+
 	queue_work(priv->wq, &gpu->retire_work);
 }
 
 /* add bo's to gpu's ring, and kick gpu: */
-int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submit,
-		struct etnaviv_file_private *ctx)
+int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx)
 {
 	struct drm_device *dev = gpu->dev;
 	struct etnaviv_drm_private *priv = dev->dev_private;
@@ -802,14 +853,17 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submi
 
 			/* ring takes a reference to the bo and iova: */
 			drm_gem_object_reference(&etnaviv_obj->base);
-			etnaviv_gem_get_iova_locked(gpu, &etnaviv_obj->base, &iova);
+			etnaviv_gem_get_iova_locked(gpu, &etnaviv_obj->base,
+						    &iova);
 		}
 
 		if (submit->bos[i].flags & ETNA_SUBMIT_BO_READ)
-			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu, false, submit->fence);
+			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu,
+						   false, submit->fence);
 
 		if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
-			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu, true, submit->fence);
+			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu,
+						   true, submit->fence);
 	}
 	hangcheck_timer_reset(gpu);
 
@@ -834,6 +888,7 @@ static irqreturn_t irq_handler(int irq, void *data)
 			dev_err(gpu->dev->dev, "AXI bus error\n");
 		else {
 			uint8_t event = __fls(intr);
+
 			dev_dbg(gpu->dev->dev, "event %u\n", event);
 			gpu->retired_fence = gpu->event[event].fence;
 			event_free(gpu, event);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 81c5d70e7547..fca315a9a8d9 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -95,7 +95,7 @@ struct etnaviv_gpu {
 	/* event management: */
 	struct etnaviv_event event[30];
 	struct completion event_free;
-	struct spinlock event_spinlock;
+	spinlock_t event_spinlock;
 
 	/* list of GEM active objects: */
 	struct list_head active_list;
@@ -140,7 +140,8 @@ static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg)
 	return etnaviv_readl(gpu->mmio + reg);
 }
 
-int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param, uint64_t *value);
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
+	uint64_t *value);
 
 int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
 int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu);
@@ -151,8 +152,8 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
 #endif
 
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
-int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submit,
-		struct etnaviv_file_private *ctx);
+int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
 
 extern struct platform_driver etnaviv_gpu_driver;
 
diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index 5841a08f627f..6aa91bcf1893 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -122,8 +122,8 @@ static int etnaviv_iommu_map(struct iommu_domain *domain, unsigned long iova,
 	return 0;
 }
 
-static size_t etnaviv_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
-	     size_t size)
+static size_t etnaviv_iommu_unmap(struct iommu_domain *domain,
+	unsigned long iova, size_t size)
 {
 	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
 
@@ -158,6 +158,7 @@ struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
 {
 	struct iommu_domain *domain;
 	struct etnaviv_iommu_domain *etnaviv_domain;
+	uint32_t pgtable;
 	int ret;
 
 	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
@@ -172,12 +173,13 @@ struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
 
 	/* set page table address in MC */
 	etnaviv_domain = domain->priv;
+	pgtable = (uint32_t)etnaviv_domain->pgtable.paddr;
 
-	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
-	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
-	gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
-	gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
-	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, (uint32_t)etnaviv_domain->pgtable.paddr);
+	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
+	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
+	gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable);
+	gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable);
+	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
 
 	return domain;
 
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 29e0056ca73a..49cc0961fb77 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -57,6 +57,7 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 
 	for_each_sg(sgt->sgl, sg, i, j) {
 		size_t bytes = sg->length + sg->offset;
+
 		iommu_unmap(domain, da, bytes);
 		da += bytes;
 	}
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index c6f72f69ea13..5b62805f2809 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -32,11 +32,12 @@ struct etnaviv_iommu {
 	enum etnaviv_iommu_version version;
 };
 
-int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names, int cnt);
-int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova, struct sg_table *sgt,
-		unsigned len, int prot);
-int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova, struct sg_table *sgt,
-		unsigned len);
+int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
+	int cnt);
+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
+	struct sg_table *sgt, unsigned len, int prot);
+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
+	struct sg_table *sgt, unsigned len);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
 
 struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 76596fc46150..9654021017fd 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -131,7 +131,7 @@ struct drm_etnaviv_gem_cpu_fini {
 struct drm_etnaviv_gem_submit_reloc {
 	uint32_t submit_offset;  /* in, offset from submit_bo */
 	uint32_t or;             /* in, value OR'd with result */
-	int32_t  shift;          /* in, amount of left shift (can be negative) */
+	int32_t  shift;          /* in, amount of left shift (can be -ve) */
 	uint32_t reloc_idx;      /* in, index of reloc_bo buffer */
 	uint64_t reloc_offset;   /* in, offset from start of reloc_bo */
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

get_pages() tries to convert a page array into a scatterlist.  If this
fails, we bail out without freeing the page array.  Add a call to
drm_gem_put_pages() to drop the reference gained in drm_gem_get_pages(),
indicating that we didn't access the pages.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 4ec097102887..f9f1d1b34a92 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -45,6 +45,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
 		etnaviv_obj->sgt = drm_prime_pages_to_sg(p, npages);
 		if (IS_ERR(etnaviv_obj->sgt)) {
 			dev_err(dev->dev, "failed to allocate sgt\n");
+			drm_gem_put_pages(obj, p, false, false);
 			return ERR_CAST(etnaviv_obj->sgt);
 		}

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 4 +++-
 drivers/staging/etnaviv/etnaviv_gem.h | 7 +++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index f9f1d1b34a92..e697f0900e7f 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -537,7 +537,9 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 
 	drm_gem_free_mmap_offset(obj);
 
-	if (obj->import_attach) {
+	if (etnaviv_obj->ops) {
+		etnaviv_obj->ops->release(etnaviv_obj);
+	} else if (obj->import_attach) {
 		if (etnaviv_obj->vaddr)
 			dma_buf_vunmap(obj->import_attach->dmabuf,
 				       etnaviv_obj->vaddr);
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 5dffce39d59e..f45488117721 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -21,8 +21,11 @@
 #include <linux/reservation.h>
 #include "etnaviv_drv.h"
 
+struct etnaviv_gem_ops;
+
 struct etnaviv_gem_object {
 	struct drm_gem_object base;
+	const struct etnaviv_gem_ops *ops;
 
 	uint32_t flags;
 
@@ -65,6 +68,10 @@ struct etnaviv_gem_object {
 };
 #define to_etnaviv_bo(x) container_of(x, struct etnaviv_gem_object, base)
 
+struct etnaviv_gem_ops {
+	void (*release)(struct etnaviv_gem_object *);
+};
+
 static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
 {
 	return etnaviv_obj->gpu != NULL;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Convert the prime import code to use etnaviv_gem_ops release method
to clean up the object.  This removes the prime specific code from
the generic object cleanup path.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index e697f0900e7f..dabaca4fc5cc 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -539,18 +539,6 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 
 	if (etnaviv_obj->ops) {
 		etnaviv_obj->ops->release(etnaviv_obj);
-	} else if (obj->import_attach) {
-		if (etnaviv_obj->vaddr)
-			dma_buf_vunmap(obj->import_attach->dmabuf,
-				       etnaviv_obj->vaddr);
-
-		/* Don't drop the pages for imported dmabuf, as they are not
-		 * ours, just free the array we allocated:
-		 */
-		if (etnaviv_obj->pages)
-			drm_free_large(etnaviv_obj->pages);
-
-		drm_prime_gem_destroy(obj, etnaviv_obj->sgt);
 	} else {
 		if (etnaviv_obj->vaddr)
 			vunmap(etnaviv_obj->vaddr);
@@ -699,6 +687,25 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 	return ERR_PTR(ret);
 }
 
+static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->vaddr)
+		dma_buf_vunmap(etnaviv_obj->base.import_attach->dmabuf,
+			       etnaviv_obj->vaddr);
+
+	/* Don't drop the pages for imported dmabuf, as they are not
+	 * ours, just free the array we allocated:
+	 */
+	if (etnaviv_obj->pages)
+		drm_free_large(etnaviv_obj->pages);
+
+	drm_prime_gem_destroy(&etnaviv_obj->base, etnaviv_obj->sgt);
+}
+
+static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = {
+	.release = etnaviv_gem_prime_release,
+};
+
 struct drm_gem_object *msm_gem_import(struct drm_device *dev,
 		struct dma_buf_attachment *attach, struct sg_table *sgt)
 {
@@ -716,6 +723,7 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
 	npages = size / PAGE_SIZE;
 
 	etnaviv_obj = to_etnaviv_bo(obj);
+	etnaviv_obj->ops = &etnaviv_gem_prime_ops;
 	etnaviv_obj->sgt = sgt;
 	etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
 	if (!etnaviv_obj->pages) {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Convert the shmem object release to use the etnaviv_gem_ops release
method.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index dabaca4fc5cc..82d73bc19a2d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -537,13 +537,7 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)

-	if (etnaviv_obj->ops) {
-		etnaviv_obj->ops->release(etnaviv_obj);
-	} else {
-		if (etnaviv_obj->vaddr)
-			vunmap(etnaviv_obj->vaddr);
-		put_pages(obj);
-	}
+	etnaviv_obj->ops->release(etnaviv_obj);
 
 	if (etnaviv_obj->resv == &etnaviv_obj->_resv)
 		reservation_object_fini(etnaviv_obj->resv);
@@ -551,6 +545,17 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 	drm_gem_object_release(obj);
 }
 
+static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->vaddr)
+		vunmap(etnaviv_obj->vaddr);
+	put_pages(&etnaviv_obj->base);
+}
+
+static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = {
+	.release = etnaviv_gem_shmem_release,
+};
+
 void etnaviv_gem_free_object(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
@@ -670,10 +675,12 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 		goto fail;
 
 	ret = 0;
-	if (flags & ETNA_BO_CMDSTREAM)
+	if (flags & ETNA_BO_CMDSTREAM) {
 		drm_gem_private_object_init(dev, obj, size);
-	else
+	} else {
+		to_etnaviv_bo(obj)->ops = &etnaviv_gem_shmem_ops;
 		ret = drm_gem_object_init(dev, obj, size);
+	}
 
 	if (ret)
 		goto fail;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

Convert the command buffer release handling to use the etnaviv_gem_ops
release method.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 82d73bc19a2d..cbc39d7f2add 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -515,12 +515,21 @@ static void etnaviv_free_cmd(struct drm_gem_object *obj)

-	dma_free_coherent(obj->dev->dev, obj->size,
+	etnaviv_obj->ops->release(etnaviv_obj);
+}
+
+static void etnaviv_gem_cmd_release(struct etnaviv_gem_object *etnaviv_obj)
+{
+	dma_free_coherent(etnaviv_obj->base.dev->dev, etnaviv_obj->base.size,
 		etnaviv_obj->vaddr, etnaviv_obj->paddr);

+static const struct etnaviv_gem_ops etnaviv_gem_cmd_ops = {
+	.release = etnaviv_gem_cmd_release,
+};
+
 static void etnaviv_free_obj(struct drm_gem_object *obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
@@ -676,6 +685,7 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 
 	ret = 0;
 	if (flags & ETNA_BO_CMDSTREAM) {
+		to_etnaviv_bo(obj)->ops = &etnaviv_gem_cmd_ops;
 		drm_gem_private_object_init(dev, obj, size);
 	} else {
 		to_etnaviv_bo(obj)->ops = &etnaviv_gem_shmem_ops;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:07 +0100

We always call drm_gem_object_release() from both etnaviv_free_cmd()
and etnaviv_free_obj().  Move this to the parent function so it is
done at one place only.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index cbc39d7f2add..42c5cc3e7e3d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -522,8 +522,6 @@ static void etnaviv_gem_cmd_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	dma_free_coherent(etnaviv_obj->base.dev->dev, etnaviv_obj->base.size,
 		etnaviv_obj->vaddr, etnaviv_obj->paddr);
-
-	drm_gem_object_release(obj);
 }
 
 static const struct etnaviv_gem_ops etnaviv_gem_cmd_ops = {
@@ -550,8 +548,6 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 
 	if (etnaviv_obj->resv == &etnaviv_obj->_resv)
 		reservation_object_fini(etnaviv_obj->resv);
-
-	drm_gem_object_release(obj);
 }
 
 static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
@@ -582,6 +578,8 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
 	else
 		etnaviv_free_obj(obj);
 
 	kfree(etnaviv_obj);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

The embedded reservation object is always initialised whenever we create
an etnaviv buffer object, but it is not always cleaned up.  Arrange this
to be clearer: always initialise the embedded reservation object
directly, and always clean the embedded reservation object up when
removing a buffer object.

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 42c5cc3e7e3d..27f849df4339 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -545,9 +545,6 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 	drm_gem_free_mmap_offset(obj);
 
 	etnaviv_obj->ops->release(etnaviv_obj);
-
-	if (etnaviv_obj->resv == &etnaviv_obj->_resv)
-		reservation_object_fini(etnaviv_obj->resv);
 }
 
 static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
@@ -578,6 +575,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
 	else
 		etnaviv_free_obj(obj);
 
+	reservation_object_fini(&etnaviv_obj->_resv);
 	drm_gem_object_release(obj);
 
 	kfree(etnaviv_obj);
@@ -657,7 +655,7 @@ static int etnaviv_gem_new_impl(struct drm_device *dev,
 	etnaviv_obj->flags = flags;
 
 	etnaviv_obj->resv = &etnaviv_obj->_resv;
-	reservation_object_init(etnaviv_obj->resv);
+	reservation_object_init(&etnaviv_obj->_resv);
 
 	INIT_LIST_HEAD(&etnaviv_obj->submit_entry);
 	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

As the tail of etnaviv_free_obj() is identical to etnaviv_free_cmd(),
we can eliminate etnaviv_free_obj() entirely by moving it into
etnaviv_gem_free_object().

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 19 +++----------------
 1 file changed, 3 insertions(+), 16 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 27f849df4339..b29379334b6e 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -509,15 +509,6 @@ void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m)
 }
 #endif
 
-static void etnaviv_free_cmd(struct drm_gem_object *obj)
-{
-	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-
-	drm_gem_free_mmap_offset(obj);
-
-	etnaviv_obj->ops->release(etnaviv_obj);
-}
-
 static void etnaviv_gem_cmd_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	dma_free_coherent(etnaviv_obj->base.dev->dev, etnaviv_obj->base.size,
@@ -541,10 +532,6 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
 		kfree(etnaviv_obj->gpu_vram_node);
 	}
-
-	drm_gem_free_mmap_offset(obj);
-
-	etnaviv_obj->ops->release(etnaviv_obj);
 }
 
 static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
@@ -570,11 +557,11 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
 
 	list_del(&etnaviv_obj->mm_list);
 
-	if (etnaviv_obj->flags & ETNA_BO_CMDSTREAM)
-		etnaviv_free_cmd(obj);
-	else
+	if (!(etnaviv_obj->flags & ETNA_BO_CMDSTREAM))
 		etnaviv_free_obj(obj);
 
+	drm_gem_free_mmap_offset(obj);
+	etnaviv_obj->ops->release(etnaviv_obj);
 	reservation_object_fini(&etnaviv_obj->_resv);
 	drm_gem_object_release(obj);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

etnaviv_gem_new_private() creates a private non-shmem gem object which
can be used to create these kinds of objects.  Fix up msm_gem_import()
to use it.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 32 ++++++++++++++++++++++----------
 drivers/staging/etnaviv/etnaviv_gem.h |  3 +++
 2 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index b29379334b6e..083a0a6f0ed7 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -687,6 +687,23 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 	return ERR_PTR(ret);
 }
 
+int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
+	struct etnaviv_gem_object **res)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	ret = etnaviv_gem_new_impl(dev, size, flags, &obj);
+	if (ret)
+		return ret;
+
+	drm_gem_private_object_init(dev, obj, size);
+
+	*res = to_etnaviv_bo(obj);
+
+	return 0;
+}
+
 static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	if (etnaviv_obj->vaddr)
@@ -710,19 +727,15 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
 		struct dma_buf_attachment *attach, struct sg_table *sgt)
 {
 	struct etnaviv_gem_object *etnaviv_obj;
-	struct drm_gem_object *obj;
 	int ret, npages;
 	size_t size = PAGE_ALIGN(attach->dmabuf->size);
 
-	ret = etnaviv_gem_new_impl(dev, size, ETNA_BO_WC, &obj);
-	if (ret)
-		goto fail;
-
-	drm_gem_private_object_init(dev, obj, size);
+	ret = etnaviv_gem_new_private(dev, size, ETNA_BO_WC, &etnaviv_obj);
+	if (ret < 0)
+		return ERR_PTR(ret);

-	etnaviv_obj = to_etnaviv_bo(obj);
 	etnaviv_obj->ops = &etnaviv_gem_prime_ops;
 	etnaviv_obj->sgt = sgt;
 	etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
@@ -736,11 +749,10 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
 	if (ret)
 		goto fail;
 
-	return obj;
+	return &etnaviv_obj->base;
 
 fail:
-	if (obj)
-		drm_gem_object_unreference_unlocked(obj);
+	drm_gem_object_unreference_unlocked(&etnaviv_obj->base);
 
 	return ERR_PTR(ret);
 }
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index f45488117721..367d4ab1c689 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -106,4 +106,7 @@ struct etnaviv_gem_submit {
 	} bos[0];
 };
 
+int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
+	struct etnaviv_gem_object **res);
+
 #endif /* __ETNAVIV_GEM_H__ */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Move the prime import code out into etnaviv_gem_prime.c, which keeps
all this functionality together.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c       | 54 -----------------------------
 drivers/staging/etnaviv/etnaviv_gem_prime.c | 54 +++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 54 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 083a0a6f0ed7..32d7946cbb98 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -17,7 +17,6 @@
 
 #include <linux/spinlock.h>
 #include <linux/shmem_fs.h>
-#include <linux/dma-buf.h>
 
 #include "etnaviv_drv.h"
 #include "etnaviv_gem.h"
@@ -703,56 +702,3 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 
 	return 0;
 }
-
-static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj)
-{
-	if (etnaviv_obj->vaddr)
-		dma_buf_vunmap(etnaviv_obj->base.import_attach->dmabuf,
-			       etnaviv_obj->vaddr);
-
-	/* Don't drop the pages for imported dmabuf, as they are not
-	 * ours, just free the array we allocated:
-	 */
-	if (etnaviv_obj->pages)
-		drm_free_large(etnaviv_obj->pages);
-
-	drm_prime_gem_destroy(&etnaviv_obj->base, etnaviv_obj->sgt);
-}
-
-static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = {
-	.release = etnaviv_gem_prime_release,
-};
-
-struct drm_gem_object *msm_gem_import(struct drm_device *dev,
-		struct dma_buf_attachment *attach, struct sg_table *sgt)
-{
-	struct etnaviv_gem_object *etnaviv_obj;
-	int ret, npages;
-	size_t size = PAGE_ALIGN(attach->dmabuf->size);
-
-	ret = etnaviv_gem_new_private(dev, size, ETNA_BO_WC, &etnaviv_obj);
-	if (ret < 0)
-		return ERR_PTR(ret);
-
-	npages = size / PAGE_SIZE;
-
-	etnaviv_obj->ops = &etnaviv_gem_prime_ops;
-	etnaviv_obj->sgt = sgt;
-	etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
-	if (!etnaviv_obj->pages) {
-		ret = -ENOMEM;
-		goto fail;
-	}
-
-	ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages,
-					       NULL, npages);
-	if (ret)
-		goto fail;
-
-	return &etnaviv_obj->base;
-
-fail:
-	drm_gem_object_unreference_unlocked(&etnaviv_obj->base);
-
-	return ERR_PTR(ret);
-}
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index 2f53ffc59158..62b990854a74 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -15,6 +15,7 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "etnaviv_gem.h"
 
@@ -56,3 +57,56 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj)
 	if (!obj->import_attach)
 		msm_gem_put_pages(obj);
 }
+

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Clean up the etnaviv prime import handling by combining msm_gem_import()
and msm_gem_prime_import_sg_table(), and then giving it an etnaviv_
prefix.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c       | 2 +-
 drivers/staging/etnaviv/etnaviv_drv.h       | 6 ++----
 drivers/staging/etnaviv/etnaviv_gem_prime.c | 8 +-------
 3 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 3981029eab93..9fe5a49593d3 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -510,7 +510,7 @@ static struct drm_driver etnaviv_drm_driver = {
 	.gem_prime_pin      = msm_gem_prime_pin,
 	.gem_prime_unpin    = msm_gem_prime_unpin,
 	.gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table,
-	.gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
+	.gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table,
 	.gem_prime_vmap     = etnaviv_gem_prime_vmap,
 	.gem_prime_vunmap   = msm_gem_prime_vunmap,
 #ifdef CONFIG_DEBUG_FS
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index a61419f7d50d..4720510503b4 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -92,8 +92,8 @@ int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
 struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj);
 void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
 void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
-struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
-		struct dma_buf_attachment *attach, struct sg_table *sg);
+struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
+	struct dma_buf_attachment *attach, struct sg_table *sg);
 int msm_gem_prime_pin(struct drm_gem_object *obj);
 void msm_gem_prime_unpin(struct drm_gem_object *obj);
 void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj);
@@ -110,8 +110,6 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 		uint32_t size, uint32_t flags, uint32_t *handle);
 struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 		uint32_t size, uint32_t flags);
-struct drm_gem_object *msm_gem_import(struct drm_device *dev,
-		struct dma_buf_attachment *attach, struct sg_table *sgt);
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index 62b990854a74..5eabcef5161a 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -39,12 +39,6 @@ void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 	/* TODO msm_gem_vunmap() */
 }
 
-struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
-		struct dma_buf_attachment *attach, struct sg_table *sg)
-{
-	return msm_gem_import(dev, attach, sg);
-}
-
 int msm_gem_prime_pin(struct drm_gem_object *obj)
 {
 	if (!obj->import_attach)
@@ -77,7 +71,7 @@ static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = {
 	.release = etnaviv_gem_prime_release,
 };
 
-struct drm_gem_object *msm_gem_import(struct drm_device *dev,
+struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
 	struct dma_buf_attachment *attach, struct sg_table *sgt)
 {
 	struct etnaviv_gem_object *etnaviv_obj;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Convert the internal get_pages()/put_pages() functions to take an
etnaviv_obj rather tha converting between drm_gem_object and our
private one.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 33 ++++++++++++++++-----------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 32d7946cbb98..f7c1ac5ad865 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -24,16 +24,14 @@
 #include "etnaviv_mmu.h"
 
 /* called with dev->struct_mutex held */
-static struct page **get_pages(struct drm_gem_object *obj)
+static struct page **get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-
 	if (!etnaviv_obj->pages) {
-		struct drm_device *dev = obj->dev;
+		struct drm_device *dev = etnaviv_obj->base.dev;
 		struct page **p;
-		int npages = obj->size >> PAGE_SHIFT;
+		int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
 
-		p = drm_gem_get_pages(obj);
+		p = drm_gem_get_pages(&etnaviv_obj->base);
 
 		if (IS_ERR(p)) {
 			dev_err(dev->dev, "could not get pages: %ld\n",
@@ -44,7 +42,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
 		etnaviv_obj->sgt = drm_prime_pages_to_sg(p, npages);
 		if (IS_ERR(etnaviv_obj->sgt)) {
 			dev_err(dev->dev, "failed to allocate sgt\n");
-			drm_gem_put_pages(obj, p, false, false);
+			drm_gem_put_pages(&etnaviv_obj->base, p, false, false);
 			return ERR_CAST(etnaviv_obj->sgt);
 		}
 
@@ -61,22 +59,23 @@ static struct page **get_pages(struct drm_gem_object *obj)
 	return etnaviv_obj->pages;
 }
 
-static void put_pages(struct drm_gem_object *obj)
+static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-
 	if (etnaviv_obj->pages) {
+		struct drm_device *dev = etnaviv_obj->base.dev;
+
 		/* For non-cached buffers, ensure the new pages are clean
 		 * because display controller, GPU, etc. are not coherent:
 		 */
 		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
-			dma_unmap_sg(obj->dev->dev, etnaviv_obj->sgt->sgl,
+			dma_unmap_sg(dev->dev, etnaviv_obj->sgt->sgl,
 				     etnaviv_obj->sgt->nents,
 				     DMA_BIDIRECTIONAL);
 		sg_free_table(etnaviv_obj->sgt);
 		kfree(etnaviv_obj->sgt);
 
-		drm_gem_put_pages(obj, etnaviv_obj->pages, true, false);
+		drm_gem_put_pages(&etnaviv_obj->base, etnaviv_obj->pages,
+				  true, false);
 
 		etnaviv_obj->pages = NULL;
 	}
@@ -88,7 +87,7 @@ struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj)
 	struct page **p;
 
 	mutex_lock(&dev->struct_mutex);
-	p = get_pages(obj);
+	p = get_pages(to_etnaviv_bo(obj));
 	mutex_unlock(&dev->struct_mutex);
 
 	return p;
@@ -189,7 +188,7 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 		goto out;
 
 	/* make sure we have pages attached now */
-	pages = get_pages(obj);
+	pages = get_pages(to_etnaviv_bo(obj));
 	if (IS_ERR(pages)) {
 		ret = PTR_ERR(pages);
 		goto out_unlock;
@@ -273,7 +272,7 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	if (!etnaviv_obj->iova  && !(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
 		struct etnaviv_drm_private *priv = obj->dev->dev_private;
 		struct etnaviv_iommu *mmu = priv->mmu;
-		struct page **pages = get_pages(obj);
+		struct page **pages = get_pages(etnaviv_obj);
 		uint32_t offset;
 		struct drm_mm_node *node = NULL;
 
@@ -375,7 +374,7 @@ void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj)
 	WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
 
 	if (!etnaviv_obj->vaddr) {
-		struct page **pages = get_pages(obj);
+		struct page **pages = get_pages(etnaviv_obj);
 
 		if (IS_ERR(pages))
 			return ERR_CAST(pages);
@@ -537,7 +536,7 @@ static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	if (etnaviv_obj->vaddr)
 		vunmap(etnaviv_obj->vaddr);
-	put_pages(&etnaviv_obj->base);
+	put_pages(etnaviv_obj);
 }
 
 static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Move the locking into etnaviv_gem_prime.c and pass an etnaviv_gem_object
rather than drm_gem_object.  As this becomes an internal gem function,
move the prototype into etnaviv_gem.h.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.h       |  2 --
 drivers/staging/etnaviv/etnaviv_gem.c       | 13 +++----------
 drivers/staging/etnaviv/etnaviv_gem.h       |  2 ++
 drivers/staging/etnaviv/etnaviv_gem_prime.c | 18 ++++++++++++++----
 4 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 4720510503b4..5104ad47c7a4 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -82,8 +82,6 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	struct drm_gem_object *obj, uint32_t *iova);
 int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
 	int id, uint32_t *iova);
-struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj);
-void msm_gem_put_pages(struct drm_gem_object *obj);
 void etnaviv_gem_put_iova(struct drm_gem_object *obj);
 int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
 		struct drm_mode_create_dumb *args);
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index f7c1ac5ad865..afbad72d5504 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -81,19 +81,12 @@ static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 	}
 }
 
-struct page **etnaviv_gem_get_pages(struct drm_gem_object *obj)
+struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	struct drm_device *dev = obj->dev;
-	struct page **p;
-
-	mutex_lock(&dev->struct_mutex);
-	p = get_pages(to_etnaviv_bo(obj));
-	mutex_unlock(&dev->struct_mutex);
-
-	return p;
+	return get_pages(etnaviv_obj);
 }
 
-void msm_gem_put_pages(struct drm_gem_object *obj)
+void etnaviv_gem_put_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
 	/* when we start tracking the pin count, then do something here */
 }
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 367d4ab1c689..b3ab8b70585a 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -108,5 +108,7 @@ struct etnaviv_gem_submit {
 
 int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 	struct etnaviv_gem_object **res);
+struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj);
+void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj);
 
 #endif /* __ETNAVIV_GEM_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index 5eabcef5161a..89903f6b49f5 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -41,15 +41,25 @@ void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 
 int msm_gem_prime_pin(struct drm_gem_object *obj)
 {
-	if (!obj->import_attach)
-		etnaviv_gem_get_pages(obj);
+	if (!obj->import_attach) {
+		struct drm_device *dev = obj->dev;
+
+		mutex_lock(&dev->struct_mutex);
+		etnaviv_gem_get_pages(to_etnaviv_bo(obj));
+		mutex_unlock(&dev->struct_mutex);
+	}
 	return 0;
 }
 
 void msm_gem_prime_unpin(struct drm_gem_object *obj)
 {
-	if (!obj->import_attach)
-		msm_gem_put_pages(obj);
+	if (!obj->import_attach) {
+		struct drm_device *dev = obj->dev;
+
+		mutex_lock(&dev->struct_mutex);
+		etnaviv_gem_put_pages(to_etnaviv_bo(obj));
+		mutex_unlock(&dev->struct_mutex);
+	}
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Provide a get_pages() method for gem objects, which allows our objects
to provide their own method to obtain the struct page array and
scatterlist.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c       | 67 ++++++++++++++++-------------
 drivers/staging/etnaviv/etnaviv_gem.h       |  1 +
 drivers/staging/etnaviv/etnaviv_gem_prime.c |  1 +
 3 files changed, 38 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index afbad72d5504..86468679e5f6 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -24,39 +24,35 @@
 #include "etnaviv_mmu.h"
 
 /* called with dev->struct_mutex held */
-static struct page **get_pages(struct etnaviv_gem_object *etnaviv_obj)
+static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	if (!etnaviv_obj->pages) {
-		struct drm_device *dev = etnaviv_obj->base.dev;
-		struct page **p;
-		int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
-
-		p = drm_gem_get_pages(&etnaviv_obj->base);
+	struct drm_device *dev = etnaviv_obj->base.dev;
+	struct page **p;
+	int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
 
-		if (IS_ERR(p)) {
-			dev_err(dev->dev, "could not get pages: %ld\n",
-					PTR_ERR(p));
-			return p;
-		}
+	p = drm_gem_get_pages(&etnaviv_obj->base);
+	if (IS_ERR(p)) {
+		dev_err(dev->dev, "could not get pages: %ld\n", PTR_ERR(p));
+		return PTR_ERR(p);
+	}
 
-		etnaviv_obj->sgt = drm_prime_pages_to_sg(p, npages);
-		if (IS_ERR(etnaviv_obj->sgt)) {
-			dev_err(dev->dev, "failed to allocate sgt\n");
-			drm_gem_put_pages(&etnaviv_obj->base, p, false, false);
-			return ERR_CAST(etnaviv_obj->sgt);
-		}
+	etnaviv_obj->sgt = drm_prime_pages_to_sg(p, npages);
+	if (IS_ERR(etnaviv_obj->sgt)) {
+		dev_err(dev->dev, "failed to allocate sgt\n");
+		drm_gem_put_pages(&etnaviv_obj->base, p, false, false);
+		return PTR_ERR(p);
+	}
 
-		etnaviv_obj->pages = p;
+	etnaviv_obj->pages = p;
 
-		/* For non-cached buffers, ensure the new pages are clean
-		 * because display controller, GPU, etc. are not coherent:
-		 */
-		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
-			dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
-				   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
-	}
+	/* For non-cached buffers, ensure the new pages are clean
+	 * because display controller, GPU, etc. are not coherent:
+	 */
+	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
+		dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
+			   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
 
-	return etnaviv_obj->pages;
+	return 0;
 }
 
 static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
@@ -83,7 +79,15 @@ static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 
 struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	return get_pages(etnaviv_obj);
+	int ret;
+
+	if (!etnaviv_obj->pages) {
+		ret = etnaviv_obj->ops->get_pages(etnaviv_obj);
+		if (ret < 0)
+			return ERR_PTR(ret);
+	}
+
+	return etnaviv_obj->pages;
 }
 
 void etnaviv_gem_put_pages(struct etnaviv_gem_object *etnaviv_obj)
@@ -181,7 +185,7 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 		goto out;
 
 	/* make sure we have pages attached now */
-	pages = get_pages(to_etnaviv_bo(obj));
+	pages = etnaviv_gem_get_pages(to_etnaviv_bo(obj));
 	if (IS_ERR(pages)) {
 		ret = PTR_ERR(pages);
 		goto out_unlock;
@@ -265,7 +269,7 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	if (!etnaviv_obj->iova  && !(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
 		struct etnaviv_drm_private *priv = obj->dev->dev_private;
 		struct etnaviv_iommu *mmu = priv->mmu;
-		struct page **pages = get_pages(etnaviv_obj);
+		struct page **pages = etnaviv_gem_get_pages(etnaviv_obj);
 		uint32_t offset;
 		struct drm_mm_node *node = NULL;
 
@@ -367,7 +371,7 @@ void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj)
 	WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
 
 	if (!etnaviv_obj->vaddr) {
-		struct page **pages = get_pages(etnaviv_obj);
+		struct page **pages = etnaviv_gem_get_pages(etnaviv_obj);
 
 		if (IS_ERR(pages))
 			return ERR_CAST(pages);
@@ -533,6 +537,7 @@ static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
 }
 
 static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = {
+	.get_pages = etnaviv_gem_shmem_get_pages,
 	.release = etnaviv_gem_shmem_release,
 };
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index b3ab8b70585a..ae77cd6d8f82 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -69,6 +69,7 @@ struct etnaviv_gem_object {
 #define to_etnaviv_bo(x) container_of(x, struct etnaviv_gem_object, base)
 
 struct etnaviv_gem_ops {
+	int (*get_pages)(struct etnaviv_gem_object *);
 	void (*release)(struct etnaviv_gem_object *);
 };
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index 89903f6b49f5..aad5a96f9fba 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -78,6 +78,7 @@ static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj)
 }
 
 static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = {
+	/* .get_pages should never be called */
 	.release = etnaviv_gem_prime_release,
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

We test for write-combine and non-cacheable mappings before calling the
DMA API.  This is werid, because non-cacheable mappings are DMA coherent
by definition, whereas cacheable mappings need cache maintanence
provided by the DMA API.

This seems to be a typo: ETNA_BO_CACHED should be used rather than
ETNA_BO_UNCACHED.

Moreover, add a comment to the dma_unmap_sg() site so to remind people
about the data-corrupting implications of this call if it is abused (as
can happen with the etnaviv DRM code structure as it currently stands
with long-term mapping of the buffer.)

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 86468679e5f6..20edaa6531eb 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -48,7 +48,7 @@ static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 	/* For non-cached buffers, ensure the new pages are clean
 	 * because display controller, GPU, etc. are not coherent:
 	 */
-	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
+	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
 		dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
 			   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
 
@@ -60,10 +60,22 @@ static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 	if (etnaviv_obj->pages) {
 		struct drm_device *dev = etnaviv_obj->base.dev;
 
-		/* For non-cached buffers, ensure the new pages are clean
+		/*
+		 * For non-cached buffers, ensure the new pages are clean
 		 * because display controller, GPU, etc. are not coherent:
+		 *
+		 * WARNING: The DMA API does not support concurrent CPU
+		 * and device access to the memory area.  With BIDIRECTIONAL,
+		 * we will clean the cache lines which overlap the region,
+		 * and invalidate all cache lines (partially) contained in
+		 * the region.
+		 *
+		 * If you have dirty data in the overlapping cache lines,
+		 * that will corrupt the GPU-written data.  If you have
+		 * written into the remainder of the region, this can
+		 * discard those writes.
 		 */
-		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_UNCACHED))
+		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
 			dma_unmap_sg(dev->dev, etnaviv_obj->sgt->sgl,
 				     etnaviv_obj->sgt->nents,
 				     DMA_BIDIRECTIONAL);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Add a flag to indicate that the GPU MMU needs to be flushed before
executing the next set of command buffers.  This is necessary to
ensure that the GPU sees updated page table entries which may have
been modified by GEM.

It is expected that userspace will have flushed the caches at the
end of the previous command buffers, so there will be no cache
writebacks pending.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 56 ++++++++++++++++++++++++++------
 drivers/staging/etnaviv/etnaviv_mmu.h    |  2 ++
 2 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 65158b558ce9..74738d9a2845 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -17,6 +17,7 @@
 
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 
 #include "common.xml.h"
 #include "state.xml.h"
@@ -140,30 +141,38 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
 	struct etnaviv_gem_object *cmd;
 	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
-	u32 back, link_target, link_size;
+	u32 back, link_target, link_size, reserve_size;
 	u32 i;

+	reserve_size = 6;
+
+	/*
+	 * If we need to flush the MMU prior to submitting this buffer, we
+	 * will need to append a mmu flush load state, followed by a new
+	 * link to this buffer - a total of four additional words.
+	 */
+	if (gpu->mmu->need_flush)
+		reserve_size += 4;
+
 	/*
 	 * if we are going to completely overflow the buffer, we need to wrap.
 	 */
-	if (buffer->offset + 6 > buffer->base.size / sizeof(uint32_t))
+	if (buffer->offset + reserve_size >
+	    buffer->base.size / sizeof(uint32_t))
 		buffer->offset = 0;
 
 	/* save offset back into main buffer */
-	back = buffer->offset;
+	back = buffer->offset + reserve_size - 6;
 	link_target = buffer->paddr + buffer->offset * 4;
 	link_size = 6;
 
-	/* trigger event */
-	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
-		       VIVS_GL_EVENT_FROM_PE);
-
-	/* append WAIT/LINK to main buffer */
-	CMD_WAIT(buffer);
-	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+	if (gpu->mmu->need_flush) {
+		/* Skip over the MMU flush and LINK instructions */
+		link_target += 4 * sizeof(uint32_t);
+	}
 
 	/* update offset for every cmd stream */
 	for (i = submit->nr_cmds; i--; ) {
@@ -202,6 +211,33 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		pr_info("event: %d\n", event);
 	}
 
+	if (gpu->mmu->need_flush) {
+		uint32_t new_target = buffer->paddr + buffer->offset *
+					sizeof(uint32_t);
+
+		/* Add the MMU flush */
+		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
+			       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
+			       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU);
+
+		/* And the link to the first buffer */
+		CMD_LINK(buffer, link_size, link_target);
+
+		/* Update the link target to point to the flush */
+		link_target = new_target;
+		link_size = 4;
+
+		gpu->mmu->need_flush = false;
+	}
+
+	/* trigger event */
+	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
+		       VIVS_GL_EVENT_FROM_PE);
+
+	/* append WAIT/LINK to main buffer */
+	CMD_WAIT(buffer);
+	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+
 	/* Change WAIT into a LINK command; write the address first. */
 	*(lw + 1) = link_target;
 	mb();
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 5b62805f2809..f1a5d0581904 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -30,6 +30,8 @@ struct etnaviv_iommu {
 	struct iommu_domain *domain;
 
+	bool need_flush;
 };
 
 int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

The GPU memory management (managed by a drm_mm object) is used to
track which areas of the MMU address space are in-use.  Therefore,
this should be tied to the MMU object, rather than the GPU object.

This means we could (as comments suggest) have multiple MMU objects,
one for each context, and switch between them.  Each would need to
be managed by its own drm_mm object.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 2 +-
 drivers/staging/etnaviv/etnaviv_gpu.c | 4 ----
 drivers/staging/etnaviv/etnaviv_gpu.h | 3 ---
 drivers/staging/etnaviv/etnaviv_mmu.c | 3 +++
 drivers/staging/etnaviv/etnaviv_mmu.h | 2 ++
 5 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 20edaa6531eb..c232980d307b 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -292,7 +292,7 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 		if (!node)
 			return -ENOMEM;
 
-		ret = drm_mm_insert_node(&gpu->mm, node, obj->size, 0,
+		ret = drm_mm_insert_node(&mmu->mm, node, obj->size, 0,
 				DRM_MM_SEARCH_DEFAULT);
 
 		if (!ret) {
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 0ec5a238f939..65607ee97507 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -950,8 +950,6 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
 
 	if (gpu->mmu)
 		etnaviv_iommu_destroy(gpu->mmu);
-
-	drm_mm_takedown(&gpu->mm);
 }
 
 static const struct component_ops gpu_ops = {
@@ -1031,8 +1029,6 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	gpu->pipe = (long)match->data;
 
 	/* TODO: figure out max mapped size */
-	drm_mm_init(&gpu->mm, 0x80000000, SZ_1G);
-
 	dev_set_drvdata(dev, gpu);
 
 	err = component_add(&pdev->dev, &gpu_ops);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index fca315a9a8d9..3a9b198d37f5 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -111,9 +111,6 @@ struct etnaviv_gpu {
 
 	struct etnaviv_iommu *mmu;
 
-	/* memory manager for GPU address area */
-	struct drm_mm mm;
-
 	/* Power Control: */
 #if 0
 	struct regulator *gpu_reg, *gpu_cx;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 49cc0961fb77..12edc05fd1ad 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -92,6 +92,7 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 
 void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
 {
+	drm_mm_takedown(&mmu->mm);
 	iommu_domain_free(mmu->domain);
 	kfree(mmu);
 }
@@ -109,6 +110,8 @@ struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
 	mmu->dev = dev;
 	mmu->version = version;
 
+	drm_mm_init(&mmu->mm, 0x80000000, SZ_1G);
+
 	iommu_set_fault_handler(domain, etnaviv_fault_handler, dev);
 
 	return mmu;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index f1a5d0581904..93aa4c6f7c24 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -31,6 +31,8 @@ struct etnaviv_iommu {

 	bool need_flush;
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

We model the GPU MMU using the iommu layer, which supports exporting
the iommu domain geometry.  Use this feature to publish the size of
the MMU window, and initialise the MMU drm_mm object according to
the available MMU window size.

As we only allocate a MMU page table which covers 256MB, yet we
initialised the drm_mm object to cover 1GB, this fixes an overflow
of the MMU page table array.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_iommu.c | 2 ++
 drivers/staging/etnaviv/etnaviv_mmu.c   | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index 6aa91bcf1893..772d5aa8c858 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -166,6 +166,8 @@ struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
 		return NULL;
 
 	domain->ops = &etnaviv_iommu_ops;
+	domain->geometry.aperture_start = GPU_MEM_START;
+	domain->geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1;
 
 	ret = domain->ops->domain_init(domain);
 	if (ret)
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 12edc05fd1ad..9b4bd4da8156 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -110,7 +110,9 @@ struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
 	mmu->dev = dev;
 	mmu->version = version;
 
-	drm_mm_init(&mmu->mm, 0x80000000, SZ_1G);
+	drm_mm_init(&mmu->mm, domain->geometry.aperture_start,
+		    domain->geometry.aperture_end -
+		      domain->geometry.aperture_start + 1);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Ensure that we unmap all MMU entries when unmapping a region.  We
fail to do this because we assume that the return value from the
unmap method should be zero.  It should be the size of entry which
has been unmapped.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_iommu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index 772d5aa8c858..8788ffbb51fe 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -134,7 +134,7 @@ static size_t etnaviv_iommu_unmap(struct iommu_domain *domain,
 	pgtable_write(&etnaviv_domain->pgtable, iova, ~0);
 	spin_unlock(&etnaviv_domain->map_lock);
 
-	return 0;
+	return SZ_4K;
 }
 
 static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Move the code which sets up and tears down the MMU mappings (iow,
allocates a node in the drm_mm, and then calls the iommu to setup
the actual mapping, and the reverse) into etnaviv_mmu.c

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 33 ++++-----------------------
 drivers/staging/etnaviv/etnaviv_mmu.c | 43 +++++++++++++++++++++++++++++++++++
 drivers/staging/etnaviv/etnaviv_mmu.h |  6 +++++
 3 files changed, 53 insertions(+), 29 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index c232980d307b..ecfa896497de 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -278,32 +278,12 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	int ret = 0;
 
-	if (!etnaviv_obj->iova  && !(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
-		struct etnaviv_drm_private *priv = obj->dev->dev_private;
-		struct etnaviv_iommu *mmu = priv->mmu;
+	if (!etnaviv_obj->iova && !(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
 		struct page **pages = etnaviv_gem_get_pages(etnaviv_obj);
-		uint32_t offset;
-		struct drm_mm_node *node = NULL;
-
 		if (IS_ERR(pages))
 			return PTR_ERR(pages);
 
-		node = kzalloc(sizeof(*node), GFP_KERNEL);
-		if (!node)
-			return -ENOMEM;
-
-		ret = drm_mm_insert_node(&mmu->mm, node, obj->size, 0,
-				DRM_MM_SEARCH_DEFAULT);
-
-		if (!ret) {
-			offset = node->start;
-			etnaviv_obj->iova = offset;
-			etnaviv_obj->gpu_vram_node = node;
-
-			ret = etnaviv_iommu_map(mmu, offset, etnaviv_obj->sgt,
-					obj->size, IOMMU_READ | IOMMU_WRITE);
-		} else
-			kfree(node);
+		ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj);
 	}
 
 	if (!ret)
@@ -532,13 +512,8 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 	struct etnaviv_drm_private *priv = obj->dev->dev_private;
 	struct etnaviv_iommu *mmu = priv->mmu;
 
-	if (mmu && etnaviv_obj->iova) {
-		uint32_t offset = etnaviv_obj->gpu_vram_node->start;
-
-		etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt, obj->size);
-		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
-		kfree(etnaviv_obj->gpu_vram_node);
-	}
+	if (mmu)
+		etnaviv_iommu_unmap_gem(mmu, etnaviv_obj);
 }
 
index 9b4bd4da8156..588bcc1ec21e 100644
@@ -16,6 +16,7 @@
  */
 
 #include "etnaviv_drv.h"
 
 static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
@@ -90,6 +91,48 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 	return 0;
 }
 
+int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
+	struct etnaviv_gem_object *etnaviv_obj)
+{
+	struct sg_table *sgt = etnaviv_obj->sgt;
+	uint32_t offset;
+	struct drm_mm_node *node = NULL;
+	int ret;
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	ret = drm_mm_insert_node(&mmu->mm, node, etnaviv_obj->base.size, 0,
+				 DRM_MM_SEARCH_DEFAULT);
+
+	if (!ret) {
+		offset = node->start;
+		etnaviv_obj->iova = offset;
+		etnaviv_obj->gpu_vram_node = node;
+
+		ret = etnaviv_iommu_map(mmu, offset, sgt,
+					etnaviv_obj->base.size,
+					IOMMU_READ | IOMMU_WRITE);
+	} else
+		kfree(node);
+
+	return ret;
+}
+
+void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
+	struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->iova) {
+		uint32_t offset = etnaviv_obj->gpu_vram_node->start;
+
+		etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt,
+				    etnaviv_obj->base.size);
+		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
+		kfree(etnaviv_obj->gpu_vram_node);
+	}
+}
+
 void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
 {
 	drm_mm_takedown(&mmu->mm);
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 93aa4c6f7c24..56aef1efe417 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -36,12 +36,18 @@ struct etnaviv_iommu {
 	bool need_flush;
 };
 
+struct etnaviv_gem_object;
+
 int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
 	int cnt);
 int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 	struct sg_table *sgt, unsigned len, int prot);
 int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 	struct sg_table *sgt, unsigned len);
+int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
+	struct etnaviv_gem_object *etnaviv_obj);
+void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
+	struct etnaviv_gem_object *etnaviv_obj);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

Bypass the iommu when we are dealing with single-entry scatterlists.

The etnaviv iommu code needs to be more inteligent: as it currently
stands, it is unusable as it always allocates from the bottom upwards.
This causes entries to be re-used without the MMU TLB being flushed,
but in order to flush the MMU TLB, we have to insert a command into
the GPU command stream.  Doing this for every allocation/free is
really sub-optimal.

To get things working as it currently stands, bypass this so that the
armada DRM scanout buffer can at least be used with etnaviv DRM.  This
at least gets us /some/ usable acceleration on Dove.

To fix this properly, the MMU handing needs to be re-evaluated and
probably rewritten.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 588bcc1ec21e..7f9b3902a17c 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -99,6 +99,20 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	struct drm_mm_node *node = NULL;
 	int ret;
 
+	/* v1 MMU can optimize single entry (contiguous) scatterlists */
+	if (sgt->nents == 1) {
+		uint32_t iova;
+
+		iova = sg_dma_address(sgt->sgl);
+		if (!iova)
+			iova = sg_phys(sgt->sgl) - sgt->sgl->offset;
+
+		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
+			etnaviv_obj->iova = iova;
+			return 0;
+		}
+	}
+
 	node = kzalloc(sizeof(*node), GFP_KERNEL);
 	if (!node)
 		return -ENOMEM;
@@ -123,7 +137,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj)
 {
-	if (etnaviv_obj->iova) {
+	if (etnaviv_obj->gpu_vram_node) {
 		uint32_t offset = etnaviv_obj->gpu_vram_node->start;
 
 		etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt,

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:08 +0100

In order to avoid flushing the GPU MMU every time we unmap and remap,
allocate MMU addresses in a round-robin fashion.  When we have to wrap
back to the beginning, indicate that the MMU needs to be flushed.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 23 +++++++++++++++++++++--
 drivers/staging/etnaviv/etnaviv_mmu.h |  1 +
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 7f9b3902a17c..0c04a1ba103e 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -117,10 +117,29 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	if (!node)
 		return -ENOMEM;
 
-	ret = drm_mm_insert_node(&mmu->mm, node, etnaviv_obj->base.size, 0,
-				 DRM_MM_SEARCH_DEFAULT);
+	while (1) {
+		ret = drm_mm_insert_node_in_range(&mmu->mm, node,
+			etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL,
+			DRM_MM_SEARCH_DEFAULT);
+
+		if (ret != -ENOSPC)
+			break;
+
+		/*
+		 * If we did not search from the start of the MMU region,
+		 * try again in case there are free slots.
+		 */
+		if (mmu->last_iova) {
+			mmu->last_iova = 0;
+			mmu->need_flush = true;
+			continue;
+		}
+
+		break;
+	}
 
 	if (!ret) {
+		mmu->last_iova = node->start + etnaviv_obj->base.size;
 		offset = node->start;
 		etnaviv_obj->iova = offset;
 		etnaviv_obj->gpu_vram_node = node;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 56aef1efe417..3bf6d3f139e0 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -33,6 +33,7 @@ struct etnaviv_iommu {
 
 	/* memory manager for GPU address area */
 	struct drm_mm mm;
+	uint32_t last_iova;
 	bool need_flush;
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

If etnaviv_iommu_map() fails, we returned an error, but we didn't clean
up the allocated drm_mm node.  Simplify the return path and add the
necessary failure clean up.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 0c04a1ba103e..36ffb55fe203 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -95,8 +95,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj)
 {
 	struct sg_table *sgt = etnaviv_obj->sgt;
-	uint32_t offset;
-	struct drm_mm_node *node = NULL;
+	struct drm_mm_node *node;
 	int ret;
 
 	/* v1 MMU can optimize single entry (contiguous) scatterlists */
@@ -138,18 +137,25 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		break;
 	}
 
-	if (!ret) {
-		mmu->last_iova = node->start + etnaviv_obj->base.size;
-		offset = node->start;
-		etnaviv_obj->iova = offset;
-		etnaviv_obj->gpu_vram_node = node;
+	if (ret < 0) {
+		kfree(node);
+		return ret;
+	}
+
+	mmu->last_iova = node->start + etnaviv_obj->base.size;
+	etnaviv_obj->iova = node->start;
+	etnaviv_obj->gpu_vram_node = node;
+	ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size,
+				IOMMU_READ | IOMMU_WRITE);
 
-		ret = etnaviv_iommu_map(mmu, offset, sgt,
-					etnaviv_obj->base.size,
-					IOMMU_READ | IOMMU_WRITE);
-	} else
+	if (ret < 0) {
+		drm_mm_remove_node(node);
 		kfree(node);
 
+		etnaviv_obj->iova = 0;
+		etnaviv_obj->gpu_vram_node = NULL;
+	}
+
 	return ret;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

We can easily exhaust the MMU space since we leave mappings in place
until the underlying buffers are freed.

Solve this by reaping inactive MMU entries using the drm_mm scanning
facility to select candidate(s), which will then have their MMU
mappings released.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 61 ++++++++++++++++++++++++++++++++++-
 1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 36ffb55fe203..fc3b010816c2 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -94,6 +94,7 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj)
 {
+	struct etnaviv_drm_private *priv = etnaviv_obj->base.dev->dev_private;
 	struct sg_table *sgt = etnaviv_obj->sgt;
 	struct drm_mm_node *node;
 	int ret;
@@ -117,6 +118,10 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		return -ENOMEM;
 
 	while (1) {
+		struct etnaviv_gem_object *o, *n;
+		struct list_head list;
+		bool found;
+
 		ret = drm_mm_insert_node_in_range(&mmu->mm, node,
 			etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL,
 			DRM_MM_SEARCH_DEFAULT);
@@ -134,7 +139,58 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 			continue;
 		}
 
-		break;
+		/* Try to retire some entries */
+		drm_mm_init_scan(&mmu->mm, etnaviv_obj->base.size, 0, 0);
+
+		found = 0;
+		INIT_LIST_HEAD(&list);
+		list_for_each_entry(o, &priv->inactive_list, mm_list) {
+			if (!o->gpu_vram_node ||
+			    o->gpu_vram_node->mm != &mmu->mm)
+				continue;
+
+			/*
+			 * If it's on the submit list, then it is part of
+			 * a submission, and we want to keep its entry.
+			 */
+			if (!list_empty(&o->submit_entry))
+				continue;
+
+			list_add(&o->submit_entry, &list);
+			if (drm_mm_scan_add_block(o->gpu_vram_node)) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found) {
+			/* Nothing found, clean up and fail */
+			list_for_each_entry_safe(o, n, &list, submit_entry)
+				BUG_ON(drm_mm_scan_remove_block(o->gpu_vram_node));
+			break;
+		}
+
+		/*
+		 * drm_mm does not allow any other operations while
+		 * scanning, so we have to remove all blocks first.
+		 * If drm_mm_scan_remove_block() returns false, we
+		 * can leave the block pinned.
+		 */
+		list_for_each_entry_safe(o, n, &list, submit_entry)
+			if (!drm_mm_scan_remove_block(o->gpu_vram_node))
+				list_del_init(&o->submit_entry);
+
+		list_for_each_entry_safe(o, n, &list, submit_entry) {
+			list_del_init(&o->submit_entry);
+			etnaviv_iommu_unmap_gem(mmu, o);
+		}
+
+		/*
+		 * We removed enough mappings so that the new allocation will
+		 * succeed.  Ensure that the MMU will be flushed and retry
+		 * the allocation one more time.
+		 */
+		mmu->need_flush = true;
 	}
 
 	if (ret < 0) {
@@ -169,6 +225,9 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 				    etnaviv_obj->base.size);
 		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
 		kfree(etnaviv_obj->gpu_vram_node);
+
+		etnaviv_obj->gpu_vram_node = NULL;
+		etnaviv_obj->iova = 0;
 	}
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Move the scatterlist creation from etnaviv_gem_shmem_get_pages() into
etnaviv_gem_get_pages() as we always want a scatterlist internally for
the IOMMU code.  It makes little sense to have each get_pages() method
re-implement this code.

However, we still allow a get_pages() method to override this by doing
their own initialisation of the etnaviv_obj->sgt pointer.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 47 +++++++++++++++++++++--------------
 1 file changed, 28 insertions(+), 19 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index ecfa896497de..550b8e71615d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -27,37 +27,21 @@
 static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
 	struct drm_device *dev = etnaviv_obj->base.dev;
-	struct page **p;
-	int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
+	struct page **p = drm_gem_get_pages(&etnaviv_obj->base);
 
-	p = drm_gem_get_pages(&etnaviv_obj->base);
 	if (IS_ERR(p)) {
 		dev_err(dev->dev, "could not get pages: %ld\n", PTR_ERR(p));
 		return PTR_ERR(p);
 	}
 
-		return PTR_ERR(p);
-	}
-
 	etnaviv_obj->pages = p;
 
-	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
 
 static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	if (etnaviv_obj->pages) {
+	if (etnaviv_obj->sgt) {
 		struct drm_device *dev = etnaviv_obj->base.dev;
 
 		/*
@@ -81,7 +65,9 @@ static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 				     DMA_BIDIRECTIONAL);
 		sg_free_table(etnaviv_obj->sgt);
 		kfree(etnaviv_obj->sgt);
-
+		etnaviv_obj->sgt = NULL;
+	}
+	if (etnaviv_obj->pages) {
 		drm_gem_put_pages(&etnaviv_obj->base, etnaviv_obj->pages,
 				  true, false);
 
@@ -99,6 +85,29 @@ struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 			return ERR_PTR(ret);
 	}
 
+	if (!etnaviv_obj->sgt) {
+		struct drm_device *dev = etnaviv_obj->base.dev;
+		int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
+		struct sg_table *sgt;
+
+		sgt = drm_prime_pages_to_sg(etnaviv_obj->pages, npages);
+		if (IS_ERR(sgt)) {
+			dev_err(dev->dev, "failed to allocate sgt: %ld\n",
+				PTR_ERR(sgt));
+			return ERR_CAST(sgt);
+		}
+
+		etnaviv_obj->sgt = sgt;
+
+		/*
+		 * For non-cached buffers, ensure the new pages are clean
+		 * because display controller, GPU, etc. are not coherent.
+		 */
+		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
+			dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
+				   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+	}
+
 	return etnaviv_obj->pages;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Add support for mapping userspace memory to the GPU.  This is useful for
cases where we have some malloc()'d memory which we wish to pass to the
GPU or shmem memory via Xv, and wish to avoid the overhead of an
additional memcpy(), especially as is is expensive to memcpy()ing a 1080p
frame.

This is mostly taken from the 3.17 i915 userptr implementation, except
we solve the held-mm problem in (imho) a nicer way, and we also avoid
excessive spinning with -EAGAIN waiting for the queued work to run.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c        |  30 +++++
 drivers/staging/etnaviv/etnaviv_drv.h        |   2 +
 drivers/staging/etnaviv/etnaviv_gem.c        | 195 +++++++++++++++++++++++++++
 drivers/staging/etnaviv/etnaviv_gem.h        |   9 ++
 drivers/staging/etnaviv/etnaviv_gem_submit.c |   9 ++
 include/uapi/drm/etnaviv_drm.h               |  13 +-
 6 files changed, 257 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 9fe5a49593d3..7236b29b17b6 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -456,6 +456,35 @@ static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data,
 		args->fence, &TS(args->timeout));
 }
 
+static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct drm_etnaviv_gem_userptr *args = data;
+	int access;
+
+	if (args->flags & ~(ETNA_USERPTR_READ|ETNA_USERPTR_WRITE) ||
+	    args->flags == 0)
+		return -EINVAL;
+
+	if (offset_in_page(args->user_ptr | args->user_size) ||
+	    (uintptr_t)args->user_ptr != args->user_ptr ||
+	    (uint32_t)args->user_size != args->user_size)
+		return -EINVAL;
+
+	if (args->flags & ETNA_USERPTR_WRITE)
+		access = VERIFY_WRITE;
+	else
+		access = VERIFY_READ;
+
+	if (!access_ok(access, (void __user *)(unsigned long)args->user_ptr,
+		       args->user_size))
+		return -EFAULT;
+
+	return etnaviv_gem_new_userptr(dev, file, args->user_ptr,
+				       args->user_size, args->flags,
+				       &args->handle);
+}
+
 static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 #define ETNA_IOCTL(n, func, flags) \
 	DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
@@ -466,6 +495,7 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 	ETNA_IOCTL(GEM_CPU_FINI, gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_SUBMIT,   gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_USERPTR,  gem_userptr,  DRM_UNLOCKED|DRM_RENDER_ALLOW),
 };
 
 static const struct vm_operations_struct vm_ops = {
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 5104ad47c7a4..ac536014a6fe 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -108,6 +108,8 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 		uint32_t size, uint32_t flags, uint32_t *handle);
 struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 		uint32_t size, uint32_t flags);
+int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
+	uintptr_t ptr, uint32_t size, uint32_t flags, uint32_t *handle);
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 550b8e71615d..bf1493d66cbf 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -695,3 +695,198 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 
 	return 0;
 }
+
+struct get_pages_work {
+	struct work_struct work;
+	struct mm_struct *mm;
+	struct task_struct *task;
+	struct etnaviv_gem_object *etnaviv_obj;
+};
+
+static struct page **etnaviv_gem_userptr_do_get_pages(
+	struct etnaviv_gem_object *etnaviv_obj, struct mm_struct *mm, struct task_struct *task)
+{
+	int ret, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT;
+	struct page **pvec;
+	uintptr_t ptr;
+
+	pvec = drm_malloc_ab(npages, sizeof(struct page *));
+	if (!pvec)
+		return ERR_PTR(-ENOMEM);
+
+	pinned = 0;
+	ptr = etnaviv_obj->userptr.ptr;
+
+	down_read(&mm->mmap_sem);
+	while (pinned < npages) {
+		ret = get_user_pages(task, mm, ptr, npages - pinned,
+				     !etnaviv_obj->userptr.ro, 0,
+				     pvec + pinned, NULL);
+		if (ret < 0)
+			break;
+
+		ptr += ret * PAGE_SIZE;
+		pinned += ret;
+	}
+	up_read(&mm->mmap_sem);
+
+	if (ret < 0) {
+		release_pages(pvec, pinned, 0);
+		drm_free_large(pvec);
+		return ERR_PTR(ret);
+	}
+
+	return pvec;
+}
+
+static void __etnaviv_gem_userptr_get_pages(struct work_struct *_work)
+{
+	struct get_pages_work *work = container_of(_work, typeof(*work), work);
+	struct etnaviv_gem_object *etnaviv_obj = work->etnaviv_obj;
+	struct drm_device *dev = etnaviv_obj->base.dev;
+	struct page **pvec;
+
+	pvec = etnaviv_gem_userptr_do_get_pages(etnaviv_obj, work->mm, work->task);
+
+	mutex_lock(&dev->struct_mutex);
+	if (IS_ERR(pvec)) {
+		etnaviv_obj->userptr.work = ERR_CAST(pvec);
+	} else {
+		etnaviv_obj->userptr.work = NULL;
+		etnaviv_obj->pages = pvec;
+	}
+
+	drm_gem_object_unreference(&etnaviv_obj->base);
+	mutex_unlock(&dev->struct_mutex);
+
+	mmput(work->mm);
+	put_task_struct(work->task);
+	kfree(work);
+}
+
+static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj)
+{
+	struct etnaviv_drm_private *priv;
+	struct page **pvec = NULL;
+	struct get_pages_work *work;
+	struct mm_struct *mm;
+	int ret, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT;
+
+	if (etnaviv_obj->userptr.work) {
+		if (IS_ERR(etnaviv_obj->userptr.work)) {
+			ret = PTR_ERR(etnaviv_obj->userptr.work);
+			etnaviv_obj->userptr.work = NULL;
+		} else {
+			ret = -EAGAIN;
+		}
+		return ret;
+	}
+
+	mm = get_task_mm(etnaviv_obj->userptr.task);
+	pinned = 0;
+	if (mm == current->mm) {
+		pvec = drm_malloc_ab(npages, sizeof(struct page *));
+		if (!pvec) {
+			mmput(mm);
+			return -ENOMEM;
+		}
+
+		pinned = __get_user_pages_fast(etnaviv_obj->userptr.ptr, npages,
+					       !etnaviv_obj->userptr.ro, pvec);
+		if (pinned < 0) {
+			drm_free_large(pvec);
+			mmput(mm);
+			return pinned;
+		}
+
+		if (pinned == npages) {
+			etnaviv_obj->pages = pvec;
+			mmput(mm);
+			return 0;
+		}
+	}
+
+	release_pages(pvec, pinned, 0);
+	drm_free_large(pvec);
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (!work) {
+		mmput(mm);
+		return -ENOMEM;
+	}
+
+	get_task_struct(current);
+	drm_gem_object_reference(&etnaviv_obj->base);
+
+	work->mm = mm;
+	work->task = current;
+	work->etnaviv_obj = etnaviv_obj;
+
+	etnaviv_obj->userptr.work = &work->work;
+	INIT_WORK(&work->work, __etnaviv_gem_userptr_get_pages);
+
+	priv = etnaviv_obj->base.dev->dev_private;
+	queue_work(priv->wq, &work->work);
+
+	return -EAGAIN;
+}
+
+static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->sgt) {
+		/*
+		 * For non-cached buffers, ensure the new pages are clean
+		 * because display controller, GPU, etc. are not coherent:
+		 */
+		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
+			dma_unmap_sg(etnaviv_obj->base.dev->dev,
+				     etnaviv_obj->sgt->sgl,
+				     etnaviv_obj->sgt->nents,
+				     DMA_BIDIRECTIONAL);
+		sg_free_table(etnaviv_obj->sgt);
+		kfree(etnaviv_obj->sgt);
+	}
+	if (etnaviv_obj->pages) {
+		int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
+
+		release_pages(etnaviv_obj->pages, npages, 0);
+		drm_free_large(etnaviv_obj->pages);
+	}
+	put_task_struct(etnaviv_obj->userptr.task);
+}
+
+static const struct etnaviv_gem_ops etnaviv_gem_userptr_ops = {
+	.get_pages = etnaviv_gem_userptr_get_pages,
+	.release = etnaviv_gem_userptr_release,
+};
+
+int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
+	uintptr_t ptr, uint32_t size, uint32_t flags, uint32_t *handle)
+{
+	struct etnaviv_gem_object *etnaviv_obj;
+	int ret;
+
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret)
+		return ret;
+
+	ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, &etnaviv_obj);
+	if (ret == 0) {
+		etnaviv_obj->ops = &etnaviv_gem_userptr_ops;
+		etnaviv_obj->userptr.ptr = ptr;
+		etnaviv_obj->userptr.task = current;
+		etnaviv_obj->userptr.ro = !(flags & ETNA_USERPTR_WRITE);
+		get_task_struct(current);
+	}
+	mutex_unlock(&dev->struct_mutex);
+
+	if (ret)
+		return ret;
+
+	ret = drm_gem_handle_create(file, &etnaviv_obj->base, handle);
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference_unlocked(&etnaviv_obj->base);
+
+	return ret;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index ae77cd6d8f82..ba47f5b80472 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -23,6 +23,13 @@
 
 struct etnaviv_gem_ops;
 
+struct etnaviv_gem_userptr {
+	uintptr_t ptr;
+	struct task_struct *task;
+	struct work_struct *work;
+	bool ro;
+};
+
 struct etnaviv_gem_object {
 	struct drm_gem_object base;
 	const struct etnaviv_gem_ops *ops;
@@ -65,6 +72,8 @@ struct etnaviv_gem_object {
 	/* for buffer manipulation during submit */
 	bool is_ring_buffer;
 	u32 offset;
+
+	struct etnaviv_gem_userptr userptr;
 };
 #define to_etnaviv_bo(x) container_of(x, struct etnaviv_gem_object, base)
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index af3718465ea1..bbe2171b8eb4 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -435,5 +435,14 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (submit)
 		submit_cleanup(submit, !!ret);
 	mutex_unlock(&dev->struct_mutex);
+
+	/*
+	 * If we're returning -EAGAIN, it could be due to the userptr code
+	 * wanting to run its workqueue outside of the struct_mutex.
+	 * Flush our workqueue to ensure that it is run in a timely manner.
+	 */
+	if (ret == -EAGAIN)
+		flush_workqueue(priv->wq);
+
 	return ret;
 }
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 9654021017fd..a4c109ffbea4 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -202,6 +202,15 @@ struct drm_etnaviv_wait_fence {
 	struct drm_etnaviv_timespec timeout;   /* in */
 };
 
+#define ETNA_USERPTR_READ	0x01
+#define ETNA_USERPTR_WRITE	0x02
+struct drm_etnaviv_gem_userptr {
+	uint64_t user_ptr;	/* in, page aligned user pointer */
+	uint64_t user_size;	/* in, page aligned user size */
+	uint32_t flags;		/* in, flags */
+	uint32_t handle;	/* out, non-zero handle */
+};
+
 #define DRM_ETNAVIV_GET_PARAM          0x00
 /* placeholder:
 #define DRM_MSM_SET_PARAM              0x01
@@ -212,7 +221,8 @@ struct drm_etnaviv_wait_fence {
 #define DRM_ETNAVIV_GEM_CPU_FINI       0x05
 #define DRM_ETNAVIV_GEM_SUBMIT         0x06
 #define DRM_ETNAVIV_WAIT_FENCE         0x07
-#define DRM_ETNAVIV_NUM_IOCTLS         0x08
+#define DRM_ETNAVIV_GEM_USERPTR        0x08
+#define DRM_ETNAVIV_NUM_IOCTLS         0x09
 
 #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)
@@ -221,5 +231,6 @@ struct drm_etnaviv_wait_fence {
 #define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
 #define DRM_IOCTL_ETNAVIV_GEM_SUBMIT   DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
 #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)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Call the DRM device 'drm' in the etnaviv_gpu structure, so that we can
add a struct device pointer.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c |  2 +-
 drivers/staging/etnaviv/etnaviv_gpu.c    | 72 ++++++++++++++++----------------
 drivers/staging/etnaviv/etnaviv_gpu.h    |  2 +-
 3 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 74738d9a2845..dfc2413074bf 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -112,7 +112,7 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
 	u32 size = obj->base.size;
 	u32 *ptr = obj->vaddr + off;
 
-	dev_info(gpu->dev->dev, "virt %p phys 0x%llx free 0x%08x\n",
+	dev_info(gpu->drm->dev, "virt %p phys 0x%llx free 0x%08x\n",
 			ptr, (u64)obj->paddr + off, size - len * 4 - off);
 
 	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 65607ee97507..b50c2c59bc56 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -158,7 +158,7 @@ static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 		gpu->identity.vertex_output_buffer_size =
 			1 << gpu->identity.vertex_output_buffer_size;
 	} else {
-		dev_err(gpu->dev->dev, "TODO: determine GPU specs based on model\n");
+		dev_err(gpu->drm->dev, "TODO: determine GPU specs based on model\n");
 	}
 
 	switch (gpu->identity.instruction_count) {
@@ -179,25 +179,25 @@ static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 		break;
 	}
 
-	dev_info(gpu->dev->dev, "stream_count:  %x\n",
+	dev_info(gpu->drm->dev, "stream_count:  %x\n",
 		 gpu->identity.stream_count);
-	dev_info(gpu->dev->dev, "register_max: %x\n",
+	dev_info(gpu->drm->dev, "register_max: %x\n",
 		 gpu->identity.register_max);
-	dev_info(gpu->dev->dev, "thread_count: %x\n",
+	dev_info(gpu->drm->dev, "thread_count: %x\n",
 		 gpu->identity.thread_count);
-	dev_info(gpu->dev->dev, "vertex_cache_size: %x\n",
+	dev_info(gpu->drm->dev, "vertex_cache_size: %x\n",
 		 gpu->identity.vertex_cache_size);
-	dev_info(gpu->dev->dev, "shader_core_count: %x\n",
+	dev_info(gpu->drm->dev, "shader_core_count: %x\n",
 		 gpu->identity.shader_core_count);
-	dev_info(gpu->dev->dev, "pixel_pipes: %x\n",
+	dev_info(gpu->drm->dev, "pixel_pipes: %x\n",
 		 gpu->identity.pixel_pipes);
-	dev_info(gpu->dev->dev, "vertex_output_buffer_size: %x\n",
+	dev_info(gpu->drm->dev, "vertex_output_buffer_size: %x\n",
 		 gpu->identity.vertex_output_buffer_size);
-	dev_info(gpu->dev->dev, "buffer_size: %x\n",
+	dev_info(gpu->drm->dev, "buffer_size: %x\n",
 		 gpu->identity.buffer_size);
-	dev_info(gpu->dev->dev, "instruction_count: %x\n",
+	dev_info(gpu->drm->dev, "instruction_count: %x\n",
 		 gpu->identity.instruction_count);
-	dev_info(gpu->dev->dev, "num_constants: %x\n",
+	dev_info(gpu->drm->dev, "num_constants: %x\n",
 		 gpu->identity.num_constants);
 }
 
@@ -243,7 +243,7 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 		}
 	}
 
-	dev_info(gpu->dev->dev, "model: %x - revision %x\n",
+	dev_info(gpu->drm->dev, "model: %x - revision %x\n",
 		 gpu->identity.model, gpu->identity.revision);
 
 	gpu->identity.features = gpu_read(gpu, VIVS_HI_CHIP_FEATURE);
@@ -276,13 +276,13 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3);
 	}
 
-	dev_info(gpu->dev->dev, "minor_features:  %x\n",
+	dev_info(gpu->drm->dev, "minor_features:  %x\n",
 		 gpu->identity.minor_features0);
-	dev_info(gpu->dev->dev, "minor_features1: %x\n",
+	dev_info(gpu->drm->dev, "minor_features1: %x\n",
 		 gpu->identity.minor_features1);
-	dev_info(gpu->dev->dev, "minor_features2: %x\n",
+	dev_info(gpu->drm->dev, "minor_features2: %x\n",
 		 gpu->identity.minor_features2);
-	dev_info(gpu->dev->dev, "minor_features3: %x\n",
+	dev_info(gpu->drm->dev, "minor_features3: %x\n",
 		 gpu->identity.minor_features3);
 
 	etnaviv_hw_specs(gpu);
@@ -335,7 +335,7 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 
 		/* try reseting again if FE it not idle */
 		if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) {
-			dev_dbg(gpu->dev->dev, "%s: FE is not idle\n",
+			dev_dbg(gpu->drm->dev, "%s: FE is not idle\n",
 				gpu->name);
 			continue;
 		}
@@ -346,7 +346,7 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 		/* is the GPU idle? */
 		if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0)
 		|| ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) {
-			dev_dbg(gpu->dev->dev, "%s: GPU is not idle\n",
+			dev_dbg(gpu->drm->dev, "%s: GPU is not idle\n",
 				gpu->name);
 			continue;
 		}
@@ -387,7 +387,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	 * simple and to get something working, just use a single address space:
 	 */
 	mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION;
-	dev_dbg(gpu->dev->dev, "mmuv2: %d\n", mmuv2);
+	dev_dbg(gpu->drm->dev, "mmuv2: %d\n", mmuv2);
 
 	if (!mmuv2) {
 		iommu = etnaviv_iommu_domain_alloc(gpu);
@@ -404,19 +404,19 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 
 	/* TODO: we will leak here memory - fix it! */
 
-	gpu->mmu = etnaviv_iommu_new(gpu->dev, iommu, version);
+	gpu->mmu = etnaviv_iommu_new(gpu->drm, iommu, version);
 	if (!gpu->mmu) {
 		ret = -ENOMEM;
 		goto fail;
 	}
-	etnaviv_register_mmu(gpu->dev, gpu->mmu);
+	etnaviv_register_mmu(gpu->drm, gpu->mmu);
 
 	/* Create buffer: */
-	gpu->buffer = etnaviv_gem_new(gpu->dev, PAGE_SIZE, ETNA_BO_CMDSTREAM);
+	gpu->buffer = etnaviv_gem_new(gpu->drm, PAGE_SIZE, ETNA_BO_CMDSTREAM);
 	if (IS_ERR(gpu->buffer)) {
 		ret = PTR_ERR(gpu->buffer);
 		gpu->buffer = NULL;
-		dev_err(gpu->dev->dev, "could not create buffer: %d\n", ret);
+		dev_err(gpu->drm->dev, "could not create buffer: %d\n", ret);
 		goto fail;
 	}
 
@@ -549,7 +549,7 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 static int enable_pwrrail(struct etnaviv_gpu *gpu)
 {
 #if 0
-	struct drm_device *dev = gpu->dev;
+	struct drm_device *dev = gpu->drm;
 	int ret = 0;
 
 	if (gpu->gpu_reg) {
@@ -669,7 +669,7 @@ static void recover_worker(struct work_struct *work)
 {
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       recover_work);
-	struct drm_device *dev = gpu->dev;
+	struct drm_device *dev = gpu->drm;

@@ -690,7 +690,7 @@ static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
 static void hangcheck_handler(unsigned long data)
 {
 	struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data;
-	struct drm_device *dev = gpu->dev;
+	struct drm_device *dev = gpu->drm;
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	uint32_t fence = gpu->retired_fence;
 
@@ -726,7 +726,7 @@ static unsigned int event_alloc(struct etnaviv_gpu *gpu)
 	ret = wait_for_completion_timeout(&gpu->event_free,
 					  msecs_to_jiffies(10 * 10000));
 	if (!ret)
-		dev_err(gpu->dev->dev, "wait_for_completion_timeout failed");
+		dev_err(gpu->drm->dev, "wait_for_completion_timeout failed");

@@ -751,7 +751,7 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 
 	if (gpu->event[event].used == false) {
-		dev_warn(gpu->dev->dev, "event %u is already marked as free",
+		dev_warn(gpu->drm->dev, "event %u is already marked as free",
 			 event);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	} else {
@@ -770,10 +770,10 @@ static void retire_worker(struct work_struct *work)
 {
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       retire_work);
-	struct drm_device *dev = gpu->dev;
+	struct drm_device *dev = gpu->drm;
 	uint32_t fence = gpu->retired_fence;
 
-	etnaviv_update_fence(gpu->dev, fence);
+	etnaviv_update_fence(gpu->drm, fence);

@@ -800,7 +800,7 @@ static void retire_worker(struct work_struct *work)
 /* call from irq handler to schedule work to retire bo's */
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
 {
-	struct etnaviv_drm_private *priv = gpu->dev->dev_private;
+	struct etnaviv_drm_private *priv = gpu->drm->dev_private;
 
 	queue_work(priv->wq, &gpu->retire_work);
 }
@@ -809,7 +809,7 @@ void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx)
 {
-	struct drm_device *dev = gpu->dev;
+	struct drm_device *dev = gpu->drm;
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	int ret = 0;
 	unsigned int event, i;
@@ -882,14 +882,14 @@ static irqreturn_t irq_handler(int irq, void *data)
 	u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE);
 
 	if (intr != 0) {
-		dev_dbg(gpu->dev->dev, "intr 0x%08x\n", intr);
+		dev_dbg(gpu->drm->dev, "intr 0x%08x\n", intr);
 
 		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR)
-			dev_err(gpu->dev->dev, "AXI bus error\n");
+			dev_err(gpu->drm->dev, "AXI bus error\n");
 		else {
 			uint8_t event = __fls(intr);
 
-			dev_dbg(gpu->dev->dev, "event %u\n", event);
+			dev_dbg(gpu->drm->dev, "event %u\n", event);
 			gpu->retired_fence = gpu->event[event].fence;
 			event_free(gpu, event);
 			etnaviv_gpu_retire(gpu);
@@ -921,7 +921,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 
 	dev_info(dev, "post gpu[idx]: %p\n", priv->gpu[idx]);
 
-	gpu->dev = drm;
+	gpu->drm = drm;
 
 	INIT_LIST_HEAD(&gpu->active_list);
 	INIT_WORK(&gpu->retire_work, retire_worker);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 3a9b198d37f5..9d91adf6a55d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -85,7 +85,7 @@ struct etnaviv_event {
 
 struct etnaviv_gpu {
 	const char *name;
-	struct drm_device *dev;
+	struct drm_device *drm;
 	struct etnaviv_chip_identity identity;
 	long pipe;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Report messages against the component device rather than the subsystem
device, so that the component responsible for the message is identified
rather than having to be separately formatted in the string.  This
ensures that many of the debug messages are properly attributed to their
appropriate component, which is especially important when we have
multiple GPU cores in a SoC.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c |  2 +-
 drivers/staging/etnaviv/etnaviv_drv.c    |  8 ++--
 drivers/staging/etnaviv/etnaviv_gpu.c    | 80 +++++++++++++++-----------------
 drivers/staging/etnaviv/etnaviv_gpu.h    |  2 +-
 4 files changed, 44 insertions(+), 48 deletions(-)

index dfc2413074bf..d83daff2bf26 100644
 
-	dev_info(gpu->drm->dev, "virt %p phys 0x%llx free 0x%08x\n",
+	dev_info(gpu->dev, "virt %p phys 0x%llx free 0x%08x\n",
 			ptr, (u64)obj->paddr + off, size - len * 4 - off);
 
 	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 7236b29b17b6..c9d5756aed6a 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -126,8 +126,7 @@ static void load_gpu(struct drm_device *dev)
 			etnaviv_gpu_pm_resume(g);
 			ret = etnaviv_gpu_init(g);
 			if (ret) {
-				dev_err(dev->dev, "%s hw init failed: %d\n",
-					g->name, ret);
+				dev_err(g->dev, "hw init failed: %d\n", ret);
 				priv->gpu[i] = NULL;
 			}
 		}
@@ -206,7 +205,7 @@ static int etnaviv_gpu_show(struct drm_device *dev, struct seq_file *m)
 	for (i = 0; i < ETNA_MAX_PIPES; i++) {
 		gpu = priv->gpu[i];
 		if (gpu) {
-			seq_printf(m, "%s Status:\n", gpu->name);
+			seq_printf(m, "%s Status:\n", dev_name(gpu->dev));
 			etnaviv_gpu_debugfs(gpu, m);
 		}
 	}
@@ -223,7 +222,8 @@ static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m)
 	for (i = 0; i < ETNA_MAX_PIPES; i++) {
 		gpu = priv->gpu[i];
 		if (gpu) {
-			seq_printf(m, "Active Objects (%s):\n", gpu->name);
+			seq_printf(m, "Active Objects (%s):\n",
+				   dev_name(gpu->dev));
 			etnaviv_gem_describe_objects(&gpu->active_list, m);
 		}
 	}
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index b50c2c59bc56..d2bf2e7c5bd6 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -105,7 +105,7 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
 		break;
 
 	default:
-		DBG("%s: invalid param: %u", gpu->name, param);
+		DBG("%s: invalid param: %u", dev_name(gpu->dev), param);
 		return -EINVAL;
 	}
 
@@ -158,7 +158,7 @@ static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 		gpu->identity.vertex_output_buffer_size =
 			1 << gpu->identity.vertex_output_buffer_size;
 	} else {
-		dev_err(gpu->drm->dev, "TODO: determine GPU specs based on model\n");
+		dev_err(gpu->dev, "TODO: determine GPU specs based on model\n");
 	}

-	dev_info(gpu->drm->dev, "stream_count:  %x\n",
+	dev_info(gpu->dev, "stream_count:  %x\n",
 		 gpu->identity.stream_count);
-	dev_info(gpu->drm->dev, "register_max: %x\n",
+	dev_info(gpu->dev, "register_max: %x\n",
 		 gpu->identity.register_max);
-	dev_info(gpu->drm->dev, "thread_count: %x\n",
+	dev_info(gpu->dev, "thread_count: %x\n",
 		 gpu->identity.thread_count);
-	dev_info(gpu->drm->dev, "vertex_cache_size: %x\n",
+	dev_info(gpu->dev, "vertex_cache_size: %x\n",
 		 gpu->identity.vertex_cache_size);
-	dev_info(gpu->drm->dev, "shader_core_count: %x\n",
+	dev_info(gpu->dev, "shader_core_count: %x\n",
 		 gpu->identity.shader_core_count);
-	dev_info(gpu->drm->dev, "pixel_pipes: %x\n",
+	dev_info(gpu->dev, "pixel_pipes: %x\n",
 		 gpu->identity.pixel_pipes);
-	dev_info(gpu->drm->dev, "vertex_output_buffer_size: %x\n",
+	dev_info(gpu->dev, "vertex_output_buffer_size: %x\n",
 		 gpu->identity.vertex_output_buffer_size);
-	dev_info(gpu->drm->dev, "buffer_size: %x\n",
+	dev_info(gpu->dev, "buffer_size: %x\n",
 		 gpu->identity.buffer_size);
-	dev_info(gpu->drm->dev, "instruction_count: %x\n",
+	dev_info(gpu->dev, "instruction_count: %x\n",
 		 gpu->identity.instruction_count);
-	dev_info(gpu->drm->dev, "num_constants: %x\n",
+	dev_info(gpu->dev, "num_constants: %x\n",
 		 gpu->identity.num_constants);
 }

-	dev_info(gpu->drm->dev, "model: %x - revision %x\n",
+	dev_info(gpu->dev, "model: %x - revision %x\n",
 		 gpu->identity.model, gpu->identity.revision);

-	dev_info(gpu->drm->dev, "minor_features:  %x\n",
+	dev_info(gpu->dev, "minor_features:  %x\n",
 		 gpu->identity.minor_features0);
-	dev_info(gpu->drm->dev, "minor_features1: %x\n",
+	dev_info(gpu->dev, "minor_features1: %x\n",
 		 gpu->identity.minor_features1);
-	dev_info(gpu->drm->dev, "minor_features2: %x\n",
+	dev_info(gpu->dev, "minor_features2: %x\n",
 		 gpu->identity.minor_features2);
-	dev_info(gpu->drm->dev, "minor_features3: %x\n",
+	dev_info(gpu->dev, "minor_features3: %x\n",
 		 gpu->identity.minor_features3);
 
 	etnaviv_hw_specs(gpu);
@@ -335,8 +335,7 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 
 		/* try reseting again if FE it not idle */
 		if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) {
-			dev_dbg(gpu->drm->dev, "%s: FE is not idle\n",
-				gpu->name);
+			dev_dbg(gpu->dev, "FE is not idle\n");
 			continue;
 		}
 
@@ -346,8 +345,7 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 		/* is the GPU idle? */
 		if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0)
 		|| ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) {
-			dev_dbg(gpu->drm->dev, "%s: GPU is not idle\n",
-				gpu->name);
+			dev_dbg(gpu->dev, "GPU is not idle\n");
 			continue;
 		}
 
@@ -387,7 +385,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	 * simple and to get something working, just use a single address space:
 	 */
 	mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION;
-	dev_dbg(gpu->drm->dev, "mmuv2: %d\n", mmuv2);
+	dev_dbg(gpu->dev, "mmuv2: %d\n", mmuv2);
 
 	if (!mmuv2) {
 		iommu = etnaviv_iommu_domain_alloc(gpu);
@@ -416,7 +414,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	if (IS_ERR(gpu->buffer)) {
 		ret = PTR_ERR(gpu->buffer);
 		gpu->buffer = NULL;
-		dev_err(gpu->drm->dev, "could not create buffer: %d\n", ret);
+		dev_err(gpu->dev, "could not create buffer: %d\n", ret);
 		goto fail;
 	}
 
@@ -624,7 +622,7 @@ int etnaviv_gpu_pm_resume(struct etnaviv_gpu *gpu)
 {
 	int ret;
 
-	DBG("%s", gpu->name);
+	DBG("%s", dev_name(gpu->dev));
 
 	ret = enable_pwrrail(gpu);
 	if (ret)
@@ -645,7 +643,7 @@ int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu)
 {
 	int ret;
 
-	DBG("%s", gpu->name);
+	DBG("%s", dev_name(gpu->dev));
 
 	ret = disable_axi(gpu);
 	if (ret)
@@ -671,7 +669,7 @@ static void recover_worker(struct work_struct *work)
 					       recover_work);
 	struct drm_device *dev = gpu->drm;
 
-	dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
+	dev_err(gpu->dev, "hangcheck recover!\n");
 
 	mutex_lock(&dev->struct_mutex);
 	/* TODO gpu->funcs->recover(gpu); */
@@ -682,7 +680,7 @@ static void recover_worker(struct work_struct *work)
 
 static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
 {
-	DBG("%s", gpu->name);
+	DBG("%s", dev_name(gpu->dev));
 	mod_timer(&gpu->hangcheck_timer,
 			round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES));
 }
@@ -700,12 +698,10 @@ static void hangcheck_handler(unsigned long data)
 	} else if (fence_after(gpu->submitted_fence, fence)) {
 		/* no progress and not done.. hung! */
 		gpu->hangcheck_fence = fence;
-		dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n",
-				gpu->name);
-		dev_err(dev->dev, "%s:     completed fence: %u\n",
-				gpu->name, fence);
-		dev_err(dev->dev, "%s:     submitted fence: %u\n",
-				gpu->name, gpu->submitted_fence);
+		dev_err(gpu->dev, "hangcheck detected gpu lockup!\n");
+		dev_err(gpu->dev, "     completed fence: %u\n", fence);
+		dev_err(gpu->dev, "     submitted fence: %u\n",
+			gpu->submitted_fence);
 		queue_work(priv->wq, &gpu->recover_work);
 	}
 
@@ -726,7 +722,7 @@ static unsigned int event_alloc(struct etnaviv_gpu *gpu)
 	ret = wait_for_completion_timeout(&gpu->event_free,
 					  msecs_to_jiffies(10 * 10000));
 	if (!ret)
-		dev_err(gpu->drm->dev, "wait_for_completion_timeout failed");
+		dev_err(gpu->dev, "wait_for_completion_timeout failed");

@@ -751,7 +747,7 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 
 	if (gpu->event[event].used == false) {
-		dev_warn(gpu->drm->dev, "event %u is already marked as free",
+		dev_warn(gpu->dev, "event %u is already marked as free",
 			 event);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	} else {
@@ -882,14 +878,14 @@ static irqreturn_t irq_handler(int irq, void *data)
 	u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE);
 
 	if (intr != 0) {
-		dev_dbg(gpu->drm->dev, "intr 0x%08x\n", intr);
+		dev_dbg(gpu->dev, "intr 0x%08x\n", intr);
 
 		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR)
-			dev_err(gpu->drm->dev, "AXI bus error\n");
+			dev_err(gpu->dev, "AXI bus error\n");
 		else {
 			uint8_t event = __fls(intr);
 
-			dev_dbg(gpu->drm->dev, "event %u\n", event);
+			dev_dbg(gpu->dev, "event %u\n", event);
 			gpu->retired_fence = gpu->event[event].fence;
 			event_free(gpu, event);
 			etnaviv_gpu_retire(gpu);
@@ -941,7 +937,7 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
 
 	del_timer(&gpu->hangcheck_timer);
 
-	DBG("%s", gpu->name);
+	DBG("%s", dev_name(gpu->dev));
 
 	WARN_ON(!list_empty(&gpu->active_list));
 
@@ -988,10 +984,10 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	if (!match)
 		return -EINVAL;
 
-	gpu->name = pdev->name;
+	gpu->dev = &pdev->dev;
 
 	/* Map registers: */
-	gpu->mmio = etnaviv_ioremap(pdev, NULL, gpu->name);
+	gpu->mmio = etnaviv_ioremap(pdev, NULL, dev_name(gpu->dev));
 	if (IS_ERR(gpu->mmio))
 		return PTR_ERR(gpu->mmio);
 
@@ -1004,7 +1000,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	}
 
 	err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
-			IRQF_TRIGGER_HIGH, gpu->name, gpu);
+			IRQF_TRIGGER_HIGH, dev_name(gpu->dev), gpu);
 	if (err) {
 		dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
 		goto fail;
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 9d91adf6a55d..8b88b18bd481 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -84,8 +84,8 @@ struct etnaviv_event {
 };
 
 struct etnaviv_gpu {
-	const char *name;
 	struct drm_device *drm;
+	struct device *dev;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

We need to synchronously take down the hangcheck timer, and then cancel
the recovery work when we're unbinding the GPU to avoid these timers and
workers running after we clean up.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index d2bf2e7c5bd6..ac44394c144d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -935,10 +935,12 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
 {
 	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
 
-	del_timer(&gpu->hangcheck_timer);
-
 	DBG("%s", dev_name(gpu->dev));
 
+	/* Safely take down hangcheck */
+	del_timer_sync(&gpu->hangcheck_timer);
+	cancel_work_sync(&gpu->recover_work);
+
 	WARN_ON(!list_empty(&gpu->active_list));
 
 	if (gpu->buffer)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Provide a function to safely take down the hangcheck timer and
workqueue.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index ac44394c144d..02440905f690 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -710,6 +710,12 @@ static void hangcheck_handler(unsigned long data)
 		hangcheck_timer_reset(gpu);
 }
 
+static void hangcheck_disable(struct etnaviv_gpu *gpu)
+{
+	del_timer_sync(&gpu->hangcheck_timer);
+	cancel_work_sync(&gpu->recover_work);
+}
+
 /*
  * event management:
  */
@@ -937,9 +943,7 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,

-	/* Safely take down hangcheck */
-	del_timer_sync(&gpu->hangcheck_timer);
-	cancel_work_sync(&gpu->recover_work);
+	hangcheck_disable(gpu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

If we queue up a large command buffer (32K) containing about 164 1080p
blit operations, it can take the GPU several seconds to complete before
raising the next event.  Our existing hangcheck code decides after a
second that the GPU is stuck, and provokes a retirement of the events.

This can lead to errors as the GPU isn't stuck - we could end up
overwriting the buffers which the GPU is currently executing.

Resolve this by also checking the current DMA address register, and
monitoring it for progress.  We have to be careful here, because if
we get stuck in a WAIT LINK, the DMA address will change by 16 bytes
(inclusive) while the GPU spins in the loop - even though we may not
have received the last event from the GPU (eg, because the PE is
busy.)

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 19 +++++++++++++++----
 drivers/staging/etnaviv/etnaviv_gpu.h |  1 +
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 02440905f690..c44a5e5e5392 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -691,13 +691,24 @@ static void hangcheck_handler(unsigned long data)
 	struct drm_device *dev = gpu->drm;
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	uint32_t fence = gpu->retired_fence;
+	bool progress = false;
 
 	if (fence != gpu->hangcheck_fence) {
-		/* some progress has been made.. ya! */
-		gpu->hangcheck_fence = fence;
-	} else if (fence_after(gpu->submitted_fence, fence)) {
-		/* no progress and not done.. hung! */
 		gpu->hangcheck_fence = fence;
+		progress = true;
+	}
+
+	if (!progress) {
+		uint32_t dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+		int change = dma_addr - gpu->hangcheck_dma_addr;
+
+		if (change < 0 || change > 16) {
+			gpu->hangcheck_dma_addr = dma_addr;
+			progress = true;
+		}
+	}
+
+	if (!progress && fence_after(gpu->submitted_fence, fence)) {
 		dev_err(gpu->dev, "hangcheck detected gpu lockup!\n");
 		dev_err(gpu->dev, "     completed fence: %u\n", fence);
 		dev_err(gpu->dev, "     submitted fence: %u\n",
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 8b88b18bd481..6ae60254dea8 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -124,6 +124,7 @@ struct etnaviv_gpu {
 #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
 	struct timer_list hangcheck_timer;
 	uint32_t hangcheck_fence;
+	uint32_t hangcheck_dma_addr;
 	struct work_struct recover_work;
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

If we queue up multiple buffers, each with their own event, where the
first buffer takes a while to execute, but subsequent buffers do not,
we can end up receiving multiple events simultaneously.  (eg, 0, 1, 2).

In this case, we only look at event 2, which updates the last fence,
and then free event 2, leaving events 0 and 1 still allocated.  If this
is allowed to continue, eventually we consume all events, and we have
no further way to progress.

However, we have to bear in mind that we could end up with events in
other orders.  For example, we could have three buffers committed at
different times:

- buffer 0 is committed, getting event 0.
- buffer 1 is committed, getting event 1.
- buffer 0 completes, signalling event 0.
- we process event 0, and freeing it.
- buffer 2 is committed, is small, getting event 0.
- buffer 1 completes, signalling event 1.
- buffer 2 completes, signalling event 0 as well.
- we process both event 0 and event 1.  We must note that the fence from
  event 0 completed, and must not overwrite it with the fence from event 1.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index c44a5e5e5392..d4f15b83ef44 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -895,19 +895,37 @@ static irqreturn_t irq_handler(int irq, void *data)
 	u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE);
 
 	if (intr != 0) {
+		int event;
+
 		dev_dbg(gpu->dev, "intr 0x%08x\n", intr);
 
-		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR)
+		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR) {
 			dev_err(gpu->dev, "AXI bus error\n");
-		else {
-			uint8_t event = __fls(intr);
+			intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR;
+		}
+
+		while ((event = ffs(intr)) != 0) {
+			event -= 1;
+
+			intr &= ~(1 << event);
 
 			dev_dbg(gpu->dev, "event %u\n", event);
-			gpu->retired_fence = gpu->event[event].fence;
+			/*
+			 * Events can be processed out of order.  Eg,
+			 * - allocate and queue event 0
+			 * - allocate event 1
+			 * - event 0 completes, we process it
+			 * - allocate and queue event 0
+			 * - event 1 and event 0 complete
+			 * we can end up processing event 0 first, then 1.
+			 */
+			if (fence_after(gpu->event[event].fence, gpu->retired_fence))
+				gpu->retired_fence = gpu->event[event].fence;
 			event_free(gpu, event);
-			etnaviv_gpu_retire(gpu);
 		}
 
 		ret = IRQ_HANDLED;
 	}

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Rather than waiting indefinitely for the GPU to reset, bound this and
if it doesn't appear to be successful, bail out and report why we
failed.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index d4f15b83ef44..3841c80254a1 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -288,9 +288,11 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 	etnaviv_hw_specs(gpu);
 }
 
-static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
+static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 {
 	u32 control, idle;
+	unsigned long timeout;
+	bool failed = true;
 
 	/* TODO
 	 *
@@ -299,7 +301,10 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 	 * - what about VG?
 	 */
 
-	while (true) {
+	/* We hope that the GPU resets in under one second */
+	timeout = jiffies + msecs_to_jiffies(1000);
+
+	while (time_is_after_jiffies(timeout)) {
 		control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
 			  VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
 
@@ -343,15 +348,28 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 		control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
 
 		/* is the GPU idle? */
-		if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0)
-		|| ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) {
+		if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0) ||
+		    ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) {
 			dev_dbg(gpu->dev, "GPU is not idle\n");
 			continue;
 		}
 
+		failed = false;
 		break;
 	}
 
+	if (failed) {
+		idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
+		control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
+
+		dev_err(gpu->dev, "GPU failed to reset: FE %sidle, 3D %sidle, 2D %sidle\n",
+			idle & VIVS_HI_IDLE_STATE_FE ? "" : "not ",
+			control & VIVS_HI_CLOCK_CONTROL_IDLE_3D ? "" : "not ",
+			control & VIVS_HI_CLOCK_CONTROL_IDLE_2D ? "" : "not ");
+
+		return -EBUSY;
+	}
+
 	/* We rely on the GPU running, so program the clock */
 	control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
 		  VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
@@ -360,6 +378,8 @@ static void etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control |
 		  VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD);
 	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
+	return 0;
 }
 
 int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
@@ -371,7 +391,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	bool mmuv2;
 
 	etnaviv_hw_identify(gpu);
-	etnaviv_hw_reset(gpu);
+	ret = etnaviv_hw_reset(gpu);
+	if (ret)
+		return ret;
 
 	/* set base addresses */
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, 0x0);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Add the workarounds found in the GALCORE code (and found to be required)
for the GC320 2D core found on iMX6 to etnaviv.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 3841c80254a1..337e997f2362 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -395,6 +395,30 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	if (ret)
 		return ret;
 
+	if (gpu->identity.model == chipModel_GC320 &&
+	    gpu_read(gpu, VIVS_HI_CHIP_TIME) != 0x2062400 &&
+	    (gpu->identity.revision == 0x5007 ||
+	     gpu->identity.revision == 0x5220)) {
+		u32 mc_memory_debug;
+
+		mc_memory_debug = gpu_read(gpu, VIVS_MC_DEBUG_MEMORY) & ~0xff;
+
+		if (gpu->identity.revision == 0x5007)
+			mc_memory_debug |= 0x0c;
+		else
+			mc_memory_debug |= 0x08;
+
+		gpu_write(gpu, VIVS_MC_DEBUG_MEMORY, mc_memory_debug);
+	}
+
+	/*
+	 * Update GPU AXI cache atttribute to "cacheable, no allocate".
+	 * This is necessary to prevent the iMX6 SoC locking up.
+	 */
+	gpu_write(gpu, VIVS_HI_AXI_CONFIG,
+		  VIVS_HI_AXI_CONFIG_AWCACHE(2) |
+		  VIVS_HI_AXI_CONFIG_ARCACHE(2));
+
 	/* set base addresses */
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, 0x0);
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, 0x0);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 50 ++++++++++++++++++++---------------
 drivers/staging/etnaviv/etnaviv_gpu.c | 18 ++++++++++---
 2 files changed, 42 insertions(+), 26 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index c9d5756aed6a..9d47b1d480ee 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -568,24 +568,6 @@ static int etnaviv_compare(struct device *dev, void *data)
 	return dev->of_node == np;
 }
 
-static int etnaviv_add_components(struct device *master, struct master *m)
-{
-	struct device_node *child_np;
-	int ret = 0;
-
-	for_each_available_child_of_node(master->of_node, child_np) {
-		DRM_INFO("add child %s\n", child_np->name);
-
-		ret = component_master_add_child(m, etnaviv_compare, child_np);
-		if (ret) {
-			of_node_put(child_np);
-			break;
-		}
-	}
-
-	return ret;
-}
-
 static int etnaviv_bind(struct device *dev)
 {
 	return drm_platform_init(&etnaviv_drm_driver, to_platform_device(dev));
@@ -597,21 +579,43 @@ static void etnaviv_unbind(struct device *dev)
 }
 
 static const struct component_master_ops etnaviv_master_ops = {
-	.add_components = etnaviv_add_components,
 	.bind = etnaviv_bind,
 	.unbind = etnaviv_unbind,
 };
 
+static int compare_str(struct device *dev, void *data)
+{
+	return !strcmp(dev_name(dev), data);
+}
+
 static int etnaviv_pdev_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct device_node *node = dev->of_node;
-
-	of_platform_populate(node, NULL, NULL, dev);
+	struct component_match *match = NULL;
 
 	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
 
-	return component_master_add(&pdev->dev, &etnaviv_master_ops);
+	if (node) {
+		struct device_node *child_np;
+
+		of_platform_populate(node, NULL, NULL, dev);
+
+		for_each_available_child_of_node(node, child_np) {
+			DRM_INFO("add child %s\n", child_np->name);
+
+			component_match_add(dev, &match, etnaviv_compare,
+					    child_np);
+		}
+	} else if (dev->platform_data) {
+		char **names = dev->platform_data;
+		unsigned i;
+
+		for (i = 0; names[i]; i++)
+			component_match_add(dev, &match, compare_str, names[i]);
+	}
+
+	return component_master_add_with_match(dev, &etnaviv_master_ops, match);
 }
 
 static int etnaviv_pdev_remove(struct platform_device *pdev)
@@ -663,3 +667,5 @@ module_exit(etnaviv_exit);
 MODULE_AUTHOR("Rob Clark <[email-masked]");
 MODULE_DESCRIPTION("etnaviv DRM Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vivante");
+MODULE_DEVICE_TABLE(of, dt_match);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 337e997f2362..ee86a71bedd4 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -27,6 +27,10 @@
 #include "state_hi.xml.h"
 #include "cmdstream.xml.h"
 
+static const struct platform_device_id gpu_ids[] = {
+	{ .name = "etnaviv-gpu,2d", .driver_data = ETNA_PIPE_2D, },
+	{ },
+};
 
 /*
  * Driver functions:
@@ -1061,9 +1065,16 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	if (!gpu)
 		return -ENOMEM;
 
-	match = of_match_device(etnaviv_gpu_match, &pdev->dev);
-	if (!match)
+	if (pdev->dev.of_node) {
+		match = of_match_device(etnaviv_gpu_match, &pdev->dev);
+		if (!match)
+			return -EINVAL;
+		gpu->pipe = (long)match->data;
+	} else if (pdev->id_entry) {
+		gpu->pipe = pdev->id_entry->driver_data;
+	} else {
 		return -EINVAL;
+	}
 
 	gpu->dev = &pdev->dev;
 
@@ -1103,8 +1114,6 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	if (IS_ERR(gpu->clk_shader))
 		gpu->clk_shader = NULL;
 
-	gpu->pipe = (long)match->data;
-
 	/* TODO: figure out max mapped size */
 	dev_set_drvdata(dev, gpu);
 
@@ -1134,4 +1143,5 @@ struct platform_driver etnaviv_gpu_driver = {
 	},
 	.probe = etnaviv_gpu_platform_probe,
 	.remove = etnaviv_gpu_platform_remove,
+	.id_table = gpu_ids,
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 9d47b1d480ee..8fba9826d019 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -20,6 +20,7 @@
 
 #include "etnaviv_drv.h"
 #include "etnaviv_gpu.h"
 
 void etnaviv_register_mmu(struct drm_device *dev, struct etnaviv_iommu *mmu)
 {
@@ -239,6 +240,23 @@ static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m)
 	return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm);
 }
 
+static int etnaviv_mmu_show(struct drm_device *dev, struct seq_file *m)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gpu *gpu;
+	unsigned int i;
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		gpu = priv->gpu[i];
+		if (gpu) {
+			seq_printf(m, "Active Objects (%s):\n",
+				   dev_name(gpu->dev));
+			drm_mm_dump_table(m, &gpu->mmu->mm);
+		}
+	}
+	return 0;
+}
+
 static int show_locked(struct seq_file *m, void *arg)
 {
 	struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -262,6 +280,7 @@ static struct drm_info_list etnaviv_debugfs_list[] = {
 		{"gpu", show_locked, 0, etnaviv_gpu_show},
 		{"gem", show_locked, 0, etnaviv_gem_show},
 		{ "mm", show_locked, 0, etnaviv_mm_show },
+		{"mmu", show_locked, 0, etnaviv_mmu_show},
 };
 
 static int etnaviv_debugfs_init(struct drm_minor *minor)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Use the etnaviv definitions for feature constants, rather than BIT()s.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index ee86a71bedd4..f3647f0a72cb 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -254,7 +254,7 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 
 	/* Disable fast clear on GC700. */
 	if (gpu->identity.model == 0x700)
-		gpu->identity.features &= ~BIT(0);
+		gpu->identity.features &= ~chipFeatures_FAST_CLEAR;
 
 	if ((gpu->identity.model == 0x500 && gpu->identity.revision < 2) ||
 	    (gpu->identity.model == 0x300 && gpu->identity.revision < 0x2000)) {
@@ -271,7 +271,8 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 		gpu->identity.minor_features0 =
 				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_0);
 
-	if (gpu->identity.minor_features0 & BIT(21)) {
+	if (gpu->identity.minor_features0 &
+	    chipMinorFeatures0_MORE_MINOR_FEATURES) {
 		gpu->identity.minor_features1 =
 				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_1);
 		gpu->identity.minor_features2 =

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Gem objects compared fences using simple <= tests.  This is
problematical when the fences wrap past (uint32_t)~0.  Resolve this
by using our helpers.

However, this is complicated by the use of '0' to indicate that the
fence has not been set (eg, because we are not writing to an object.)
Carry the access flags into the object and use them when determining
of the object can be retired.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.h |  2 +-
 drivers/staging/etnaviv/etnaviv_gem.c | 11 +++++++----
 drivers/staging/etnaviv/etnaviv_gem.h |  1 +
 drivers/staging/etnaviv/etnaviv_gpu.c | 16 ++++++++--------
 4 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index ac536014a6fe..d6026b39e62f 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -98,7 +98,7 @@ void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj);
 void *etnaviv_gem_vaddr(struct drm_gem_object *obj);
 dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj);
 void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
-		struct etnaviv_gpu *gpu, bool write, uint32_t fence);
+		struct etnaviv_gpu *gpu, uint32_t access, uint32_t fence);
 void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj);
 int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 		struct timespec *timeout);
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index bf1493d66cbf..71c4acbb100a 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -405,16 +405,18 @@ dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj)
 }
 
 void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
-		struct etnaviv_gpu *gpu, bool write, uint32_t fence)
+	struct etnaviv_gpu *gpu, uint32_t access, uint32_t fence)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 
 	etnaviv_obj->gpu = gpu;
 
-	if (write)
-		etnaviv_obj->write_fence = fence;
-	else
+	if (access & ETNA_SUBMIT_BO_READ)
 		etnaviv_obj->read_fence = fence;
+	if (access & ETNA_SUBMIT_BO_WRITE)
+		etnaviv_obj->write_fence = fence;
+
+	etnaviv_obj->access |= access;
 
 	list_del_init(&etnaviv_obj->mm_list);
 	list_add_tail(&etnaviv_obj->mm_list, &gpu->active_list);
@@ -431,6 +433,7 @@ void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj)
 	etnaviv_obj->gpu = NULL;
 	etnaviv_obj->read_fence = 0;
 	etnaviv_obj->write_fence = 0;
+	etnaviv_obj->access = 0;
 	list_del_init(&etnaviv_obj->mm_list);
 	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
 }
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index ba47f5b80472..21575571ca3d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -46,6 +46,7 @@ struct etnaviv_gem_object {
 	 */
 	struct list_head mm_list;
 	struct etnaviv_gpu *gpu;     /* non-null if active */
+	uint32_t access;
 	uint32_t read_fence, write_fence;
 
 	/* Transiently in the process of submit ioctl, objects associated
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index f3647f0a72cb..9ae96752e8e0 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -847,8 +847,10 @@ static void retire_worker(struct work_struct *work)
 		obj = list_first_entry(&gpu->active_list,
 				struct etnaviv_gem_object, mm_list);
 
-		if ((obj->read_fence <= fence) &&
-				(obj->write_fence <= fence)) {
+		if ((!(obj->access & ETNA_SUBMIT_BO_READ) ||
+		     fence_after_eq(fence, obj->read_fence)) &&
+		    (!(obj->access & ETNA_SUBMIT_BO_WRITE) ||
+		     fence_after_eq(fence, obj->write_fence))) {
 			/* move to inactive: */
 			etnaviv_gem_move_to_inactive(&obj->base);
 			etnaviv_gem_put_iova(&obj->base);
@@ -921,13 +923,11 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 						    &iova);
 		}
 
-		if (submit->bos[i].flags & ETNA_SUBMIT_BO_READ)
+		if (submit->bos[i].flags & (ETNA_SUBMIT_BO_READ |
+					    ETNA_SUBMIT_BO_WRITE))
 			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu,
-						   false, submit->fence);
-
-		if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
-			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu,
-						   true, submit->fence);
+						   submit->bos[i].flags,
+						   submit->fence);
 	}
 	hangcheck_timer_reset(gpu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:09 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 89 ++++++++++++++++++++---------------
 1 file changed, 52 insertions(+), 37 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 71c4acbb100a..d4085702b0d4 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -23,6 +23,55 @@
 #include "etnaviv_gpu.h"
 #include "etnaviv_mmu.h"
 
+static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj)
+{
+	struct drm_device *dev = etnaviv_obj->base.dev;
+
+	/*
+	 * For non-cached buffers, ensure the new pages are clean
+	 * because display controller, GPU, etc. are not coherent.
+	 */
+	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED)) {
+		dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
+			   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+	} else {
+		struct scatterlist *sg;
+		unsigned int i;
+
+		for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+			sg_dma_address(sg) = sg_phys(sg);
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+			sg_dma_len(sg) = sg->length;
+#endif
+		}
+	}
+}
+
+static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj)
+{
+	struct drm_device *dev = etnaviv_obj->base.dev;
+
+	/*
+	 * For non-cached buffers, ensure the new pages are clean
+	 * because display controller, GPU, etc. are not coherent:
+	 *
+	 * WARNING: The DMA API does not support concurrent CPU
+	 * and device access to the memory area.  With BIDIRECTIONAL,
+	 * we will clean the cache lines which overlap the region,
+	 * and invalidate all cache lines (partially) contained in
+	 * the region.
+	 *
+	 * If you have dirty data in the overlapping cache lines,
+	 * that will corrupt the GPU-written data.  If you have
+	 * written into the remainder of the region, this can
+	 * discard those writes.
+	 */
+	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
+		dma_unmap_sg(dev->dev, etnaviv_obj->sgt->sgl,
+			     etnaviv_obj->sgt->nents,
+			     DMA_BIDIRECTIONAL);
+}
+
 /* called with dev->struct_mutex held */
 static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
@@ -42,27 +91,7 @@ static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
 	if (etnaviv_obj->sgt) {
-		struct drm_device *dev = etnaviv_obj->base.dev;
-
-		/*
-		 * For non-cached buffers, ensure the new pages are clean
-		 * because display controller, GPU, etc. are not coherent:
-		 *
-		 * WARNING: The DMA API does not support concurrent CPU
-		 * and device access to the memory area.  With BIDIRECTIONAL,
-		 * we will clean the cache lines which overlap the region,
-		 * and invalidate all cache lines (partially) contained in
-		 * the region.
-		 *
-		 * If you have dirty data in the overlapping cache lines,
-		 * that will corrupt the GPU-written data.  If you have
-		 * written into the remainder of the region, this can
-		 * discard those writes.
-		 */
-		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
-			dma_unmap_sg(dev->dev, etnaviv_obj->sgt->sgl,
-				     etnaviv_obj->sgt->nents,
-				     DMA_BIDIRECTIONAL);
+		etnaviv_gem_scatterlist_unmap(etnaviv_obj);
 		sg_free_table(etnaviv_obj->sgt);
 		kfree(etnaviv_obj->sgt);
 		etnaviv_obj->sgt = NULL;
@@ -99,13 +128,7 @@ struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 
 		etnaviv_obj->sgt = sgt;
 
-		/*
-		 * For non-cached buffers, ensure the new pages are clean
-		 * because display controller, GPU, etc. are not coherent.
-		 */
-		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
-			dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
-				   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+		etnaviv_gem_scatter_map(etnaviv_obj);
 	}
 
 	return etnaviv_obj->pages;
@@ -837,15 +860,7 @@ static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	if (etnaviv_obj->sgt) {
-		/*
-		 * For non-cached buffers, ensure the new pages are clean
-		 * because display controller, GPU, etc. are not coherent:
-		 */
-		if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
-			dma_unmap_sg(etnaviv_obj->base.dev->dev,
-				     etnaviv_obj->sgt->sgl,
-				     etnaviv_obj->sgt->nents,
-				     DMA_BIDIRECTIONAL);
+		etnaviv_gem_scatterlist_unmap(etnaviv_obj);
 		sg_free_table(etnaviv_obj->sgt);
 		kfree(etnaviv_obj->sgt);
 	}

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add a poisoned bad page to map unused MMU entries to.  This gives us a
certain amount of protection against bad command streams causing us to
hit regions of memory we really shouldn't be touching.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_iommu.c | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index 8788ffbb51fe..3ec0293941db 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -36,6 +36,8 @@ struct etnaviv_iommu_domain_pgtable {
 };
 
 struct etnaviv_iommu_domain {
+	void *bad_page_cpu;
+	dma_addr_t bad_page_dma;
 	struct etnaviv_iommu_domain_pgtable pgtable;
 	spinlock_t map_lock;
 };
@@ -80,18 +82,38 @@ static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
 static int etnaviv_iommu_domain_init(struct iommu_domain *domain)
 {
 	struct etnaviv_iommu_domain *etnaviv_domain;
-	int ret;
+	uint32_t iova, *p;
+	int ret, i;
 
 	etnaviv_domain = kmalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
 	if (!etnaviv_domain)
 		return -ENOMEM;
 
+	etnaviv_domain->bad_page_cpu = dma_alloc_coherent(NULL, SZ_4K,
+						  &etnaviv_domain->bad_page_dma,
+						  GFP_KERNEL);
+	if (!etnaviv_domain->bad_page_cpu) {
+		kfree(etnaviv_domain);
+		return -ENOMEM;
+	}
+	p = etnaviv_domain->bad_page_cpu;
+	for (i = 0; i < SZ_4K / 4; i++)
+		*p++ = 0xdead55aa;
+
 	ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE);
 	if (ret < 0) {
+		dma_free_coherent(NULL, SZ_4K, etnaviv_domain->bad_page_cpu,
+				  etnaviv_domain->bad_page_dma);
 		kfree(etnaviv_domain);
 		return ret;
 	}
 
+	for (iova = domain->geometry.aperture_start;
+	     iova < domain->geometry.aperture_end; iova += SZ_4K) {
+		pgtable_write(&etnaviv_domain->pgtable, iova,
+			      etnaviv_domain->bad_page_dma);
+	}
+
 	spin_lock_init(&etnaviv_domain->map_lock);
 	domain->priv = etnaviv_domain;
 	return 0;
@@ -103,6 +125,8 @@ static void etnaviv_iommu_domain_destroy(struct iommu_domain *domain)
 
 	pgtable_free(&etnaviv_domain->pgtable, PT_SIZE);
 
+	dma_free_coherent(NULL, SZ_4K, etnaviv_domain->bad_page_cpu,
+			  etnaviv_domain->bad_page_dma);
 	kfree(etnaviv_domain);
 	domain->priv = NULL;
 }
@@ -131,7 +155,8 @@ static size_t etnaviv_iommu_unmap(struct iommu_domain *domain,
 		return -EINVAL;
 
 	spin_lock(&etnaviv_domain->map_lock);
-	pgtable_write(&etnaviv_domain->pgtable, iova, ~0);
+	pgtable_write(&etnaviv_domain->pgtable, iova,
+		      etnaviv_domain->bad_page_dma);
 	spin_unlock(&etnaviv_domain->map_lock);
 
 	return SZ_4K;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Running the cairo trace "firefox-36-20090609" results in the Xorg server
trying to queue up 319M of buffer objects in a single commit (due to the
number of drawing operations that will fit into a 32K command buffer.)
This causes the commit ioctl to return -ENOSPC as the MMU is unable to
map all these buffer objects simultaneously.

There are three workarounds possible:
- Increase the GPU MMU table size
- Decrease the command buffer size
- Implement reconstruction of the command buffer on -ENOSPC return

All three should be implemented, but the first is only applicable to the
kernel.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_iommu.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index 3ec0293941db..f12a74d10581 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -25,7 +25,7 @@
 #include "etnaviv_iommu.h"
 #include "state_hi.xml.h"
 
-#define PT_SIZE		SZ_256K
+#define PT_SIZE		SZ_2M
 #define PT_ENTRIES	(PT_SIZE / sizeof(uint32_t))
 
 #define GPU_MEM_START	0x80000000
@@ -82,7 +82,7 @@ static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
 static int etnaviv_iommu_domain_init(struct iommu_domain *domain)
 {
 	struct etnaviv_iommu_domain *etnaviv_domain;
-	uint32_t iova, *p;
+	uint32_t *p;
 	int ret, i;
 
 	etnaviv_domain = kmalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
@@ -108,11 +108,9 @@ static int etnaviv_iommu_domain_init(struct iommu_domain *domain)
 		return ret;
 	}
 
-	for (iova = domain->geometry.aperture_start;
-	     iova < domain->geometry.aperture_end; iova += SZ_4K) {
-		pgtable_write(&etnaviv_domain->pgtable, iova,
-			      etnaviv_domain->bad_page_dma);
-	}
+	for (i = 0; i < PT_ENTRIES; i++)
+		etnaviv_domain->pgtable.pgtable[i] =
+			etnaviv_domain->bad_page_dma;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

We never pass the GPU addresses of BOs to userspace, so userspace can
never specify the correct address.  Hence, this code serves no useful
purpose, and can be removed.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.h        |  1 -
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 36 +++++-----------------------
 2 files changed, 6 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 21575571ca3d..0bf432515ced 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -101,7 +101,6 @@ struct etnaviv_gem_submit {
 	struct list_head bo_list;
 	struct ww_acquire_ctx ticket;
 	uint32_t fence;
-	bool valid;
 	unsigned int nr_cmds;
 	unsigned int nr_bos;
 	struct {
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index bbe2171b8eb4..c32fb4424eea 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -25,7 +25,6 @@
 
 #define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
 /* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
-#define BO_VALID    0x8000
 #define BO_LOCKED   0x4000
 #define BO_PINNED   0x2000
 
@@ -84,8 +83,6 @@ static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
 		}
 
 		submit->bos[i].flags = submit_bo.flags;
-		/* in validate_objects() we figure out if this is true: */
-		submit->bos[i].iova  = submit_bo.presumed;
 
 		/* normally use drm_gem_object_lookup(), but for bulk lookup
 		 * all under single table_lock just hit object_idr directly:
@@ -131,9 +128,7 @@ static void submit_unlock_unpin_bo(struct etnaviv_gem_submit *submit, int i)
 	if (submit->bos[i].flags & BO_LOCKED)
 		ww_mutex_unlock(&etnaviv_obj->resv->lock);
 
-	if (!(submit->bos[i].flags & BO_VALID))
-		submit->bos[i].iova = 0;
-
+	submit->bos[i].iova = 0;
 	submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
 }
 
@@ -143,8 +138,6 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)
 	int contended, slow_locked = -1, i, ret = 0;
 
 retry:
-	submit->valid = true;
-
 	for (i = 0; i < submit->nr_bos; i++) {
 		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
 		uint32_t iova;
@@ -177,14 +170,7 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)
 			goto fail;
 
 		submit->bos[i].flags |= BO_PINNED;
-
-		if (iova == submit->bos[i].iova) {
-			submit->bos[i].flags |= BO_VALID;
-		} else {
-			submit->bos[i].iova = iova;
-			submit->bos[i].flags &= ~BO_VALID;
-			submit->valid = false;
-		}
+		submit->bos[i].iova = iova;
 	}
 
 	ww_acquire_done(&submit->ticket);
@@ -217,7 +203,7 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)
 }
 
 static int submit_bo(struct etnaviv_gem_submit *submit, uint32_t idx,
-		struct etnaviv_gem_object **obj, uint32_t *iova, bool *valid)
+		struct etnaviv_gem_object **obj, uint32_t *iova)
 {
 	if (idx >= submit->nr_bos) {
 		DRM_ERROR("invalid buffer index: %u (out of %u)\n",
@@ -229,8 +215,6 @@ static int submit_bo(struct etnaviv_gem_submit *submit, uint32_t idx,
 		*obj = submit->bos[idx].obj;
 	if (iova)
 		*iova = submit->bos[idx].iova;
-	if (valid)
-		*valid = !!(submit->bos[idx].flags & BO_VALID);
 
 	return 0;
 }
@@ -254,7 +238,6 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 		void __user *userptr =
 			to_user_ptr(relocs + (i * sizeof(submit_reloc)));
 		uint32_t iova, off;
-		bool valid;
 
 		ret = copy_from_user(&submit_reloc, userptr,
 				     sizeof(submit_reloc));
@@ -276,14 +259,10 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 			return -EINVAL;
 		}
 
-		ret = submit_bo(submit, submit_reloc.reloc_idx, &bobj,
-				&iova, &valid);
+		ret = submit_bo(submit, submit_reloc.reloc_idx, &bobj, &iova);
 		if (ret)
 			return ret;
 
-		if (valid)
-			continue;
-
 		if (submit_reloc.reloc_offset >=
 		    bobj->base.size - sizeof(*ptr)) {
 			DRM_ERROR("relocation %u outside object", i);
@@ -371,8 +350,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 			goto out;
 		}
 
-		ret = submit_bo(submit, submit_cmd.submit_idx,
-				&etnaviv_obj, NULL, NULL);
+		ret = submit_bo(submit, submit_cmd.submit_idx, &etnaviv_obj,
+				NULL);
 		if (ret)
 			goto out;
 
@@ -415,9 +394,6 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		submit->cmd[i].size = submit_cmd.size / 4;
 		submit->cmd[i].obj = etnaviv_obj;
 
-		if (submit->valid)
-			continue;
-
 		ret = submit_reloc(submit, etnaviv_obj,
 				   submit_cmd.submit_offset,
 				   submit_cmd.nr_relocs, submit_cmd.relocs);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Remove support for shifting, or bitwise or-ing of constants with GPU
addresses.  The Vivante GPU does not need this support, so we can
remove these additional operations.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index c32fb4424eea..cdc0bb5677d1 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -259,6 +259,13 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 			return -EINVAL;
 		}
 
+		/* Vivante hardware has no need for shifts or bitwise or-ing */
+		if (submit_reloc.shift || submit_reloc.or) {
+			DRM_ERROR("invalid shift or bitwise or at reloc %u\n",
+				  i);
+			return -EINVAL;
+		}
+
 		ret = submit_bo(submit, submit_reloc.reloc_idx, &bobj, &iova);
 		if (ret)
 			return ret;
@@ -269,14 +276,7 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 			return -EINVAL;
 		}
 
-		iova += submit_reloc.reloc_offset;
-
-		if (submit_reloc.shift < 0)
-			iova >>= -submit_reloc.shift;
-		else
-			iova <<= submit_reloc.shift;
-
-		ptr[off] = iova | submit_reloc.or;
+		ptr[off] = iova + submit_reloc.reloc_offset;
 
 		last_offset = off;
 	}

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index fc3b010816c2..2855791440eb 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -39,8 +39,8 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 		return -EINVAL;
 
 	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
-		u32 pa = sg_phys(sg) - sg->offset;
-		size_t bytes = sg->length + sg->offset;
+		u32 pa = sg_dma_address(sg) - sg->offset;
+		size_t bytes = sg_dma_len(sg) + sg->offset;
 
 		VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
 
@@ -57,7 +57,7 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 	da = iova;
 
 	for_each_sg(sgt->sgl, sg, i, j) {
-		size_t bytes = sg->length + sg->offset;
+		size_t bytes = sg_dma_len(sg) + sg->offset;
 
 		iommu_unmap(domain, da, bytes);
 		da += bytes;
@@ -74,7 +74,7 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 	int i;
 
 	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
-		size_t bytes = sg->length + sg->offset;
+		size_t bytes = sg_dma_len(sg) + sg->offset;
 		size_t unmapped;
 
 		unmapped = iommu_unmap(domain, da, bytes);
@@ -104,9 +104,6 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		uint32_t iova;
 
 		iova = sg_dma_address(sgt->sgl);
-		if (!iova)
-			iova = sg_phys(sgt->sgl) - sgt->sgl->offset;
-
 		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
 			etnaviv_obj->iova = iova;
 			return 0;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

The DMA API usage by etnaviv is really not up to scratch.  It does not
respect the buffer ownership rules, which are vitally necessary when
using DMA_BIDIRECTIONAL, as mapping /only/ cleans the cache lines,
causing dirty data to be written back to RAM and an unmap /only/
invalidates them, causing any data in the cache to be discarded.  Given
the length of time which these objects remain mapped, we can't hold
them in the mapped state while hoping that no other CPU accesses to the
buffer occur.

This has lead to visible pixmap corruption in the X server.  Work around
this by mapping and then immediately unmapping the buffers.  This causes
data in the buffers to be written back and then purged from the caches.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index d4085702b0d4..e237100539cb 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -26,14 +26,15 @@
 static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj)
 {
 	struct drm_device *dev = etnaviv_obj->base.dev;
+	struct sg_table *sgt = etnaviv_obj->sgt;
 
 	/*
 	 * For non-cached buffers, ensure the new pages are clean
 	 * because display controller, GPU, etc. are not coherent.
 	 */
 	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED)) {
-		dma_map_sg(dev->dev, etnaviv_obj->sgt->sgl,
-			   etnaviv_obj->sgt->nents, DMA_BIDIRECTIONAL);
+		dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+		dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
 	} else {
 		struct scatterlist *sg;
 		unsigned int i;
@@ -50,6 +51,7 @@ static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj)
 static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj)
 {
 	struct drm_device *dev = etnaviv_obj->base.dev;
+	struct sg_table *sgt = etnaviv_obj->sgt;
 
 	/*
 	 * For non-cached buffers, ensure the new pages are clean
@@ -66,10 +68,10 @@ static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj
 	 * written into the remainder of the region, this can
 	 * discard those writes.
 	 */
-	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED))
-		dma_unmap_sg(dev->dev, etnaviv_obj->sgt->sgl,
-			     etnaviv_obj->sgt->nents,
-			     DMA_BIDIRECTIONAL);
+	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED)) {
+		dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+		dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+	}
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Parse the submitted command buffer for allowable GPU commands, and
validate that all commands fit wholely within the submitted buffer.
We allow the following commands:

- load state
- any of the draw commands
- stall
- nop

which denies attempts to link, call, return, etc from the supplied
command stream.  This, at least, ensures that the GPU should reach the
end of the submitted command set and return to our buffer.

Future validation of the load state commands will ensure that we prevent
userspace providing physical addresses via the GPU command stream, with
a future possibility of also validating that the boundaries of the
drawing commands lie wholely within the requested buffers.  For the
time being, this functionality is disabled.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/Makefile             |   1 +
 drivers/staging/etnaviv/etnaviv_cmd_parser.c | 101 +++++++++++++++++++++++++++
 drivers/staging/etnaviv/etnaviv_drv.h        |   3 +
 drivers/staging/etnaviv/etnaviv_gem_submit.c |   7 ++
 4 files changed, 112 insertions(+)
 create mode 100644 drivers/staging/etnaviv/etnaviv_cmd_parser.c

diff --git a/drivers/staging/etnaviv/Makefile b/drivers/staging/etnaviv/Makefile
index ef0cffabdcce..2b71c31b6501 100644
--- a/drivers/staging/etnaviv/Makefile
+++ b/drivers/staging/etnaviv/Makefile
@@ -4,6 +4,7 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
 endif
 
 etnaviv-y := \
+	etnaviv_cmd_parser.o \
 	etnaviv_drv.o \
 	etnaviv_gem.o \
 	etnaviv_gem_prime.o \
diff --git a/drivers/staging/etnaviv/etnaviv_cmd_parser.c b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
new file mode 100644
index 000000000000..fbbea57c7b5c
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
@@ -0,0 +1,101 @@
+#include <linux/kernel.h>
+
+#include "etnaviv_gem.h"
+#include "etnaviv_gpu.h"
+
+#include "cmdstream.xml.h"
+
+#define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT)
+
+static bool etnaviv_validate_load_state(struct etnaviv_gpu *gpu, u32 *buf,
+	unsigned int state, unsigned int num)
+{
+	return true;
+	if (0x1200 - state < num * 4)
+		return false;
+	if (0x1228 - state < num * 4)
+		return false;
+	if (0x1238 - state < num * 4)
+		return false;
+	if (0x1284 - state < num * 4)
+		return false;
+	if (0x128c - state < num * 4)
+		return false;
+	if (0x1304 - state < num * 4)
+		return false;
+	if (0x1310 - state < num * 4)
+		return false;
+	if (0x1318 - state < num * 4)
+		return false;
+	if (0x1280c - state < num * 4 + 0x0c)
+		return false;
+	if (0x128ac - state < num * 4 + 0x0c)
+		return false;
+	if (0x128cc - state < num * 4 + 0x0c)
+		return false;
+	if (0x1297c - state < num * 4 + 0x0c)
+		return false;
+	return true;
+}
+
+static uint8_t cmd_length[32] = {
+	[FE_OPCODE_DRAW_PRIMITIVES] = 4,
+	[FE_OPCODE_DRAW_INDEXED_PRIMITIVES] = 6,
+	[FE_OPCODE_NOP] = 2,
+	[FE_OPCODE_STALL] = 2,
+};
+
+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *obj, unsigned int offset, unsigned int size)
+{
+	u32 *start = obj->vaddr + offset * 4;
+	u32 *buf = start;
+	u32 *end = buf + size;
+
+	while (buf < end) {
+		u32 cmd = *buf;
+		unsigned int len, n, off;
+		unsigned int op = cmd >> 27;
+
+		switch (op) {
+		case FE_OPCODE_LOAD_STATE:
+			n = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_COUNT);
+			len = ALIGN(1 + n, 2);
+			if (buf + len > end)
+				break;
+
+			off = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_OFFSET);
+			if (!etnaviv_validate_load_state(gpu, buf + 1,
+							 off * 4, n)) {
+				dev_warn(gpu->dev, "%s: load state covers restricted state (0x%x-0x%x) at offset %tu\n",
+					 __func__, off * 4, (off + n) * 4, buf - start);
+				return false;
+			}
+			break;
+
+		case FE_OPCODE_DRAW_2D:
+			n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT);
+			len = 2 + n * 2;
+			break;
+
+		default:
+			len = cmd_length[op];
+			if (len == 0) {
+				dev_err(gpu->dev, "%s: op %u not permitted at offset %tu\n",
+					__func__, op, buf - start);
+				return false;
+			}
+			break;
+		}
+
+		buf += len;
+	}
+
+	if (buf > end) {
+		dev_err(gpu->dev, "%s: commands overflow end of buffer: %tu > %u\n",
+			__func__, buf - start, size);
+		return false;
+	}
+
+	return true;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index d6026b39e62f..840d002ca68a 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -39,6 +39,7 @@
 
 struct etnaviv_gpu;
 struct etnaviv_mmu;
+struct etnaviv_gem_object;
 struct etnaviv_gem_submit;
 
 struct etnaviv_file_private {
@@ -113,6 +114,8 @@ int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *obj, unsigned int offset, unsigned int size);
 
 #ifdef CONFIG_DEBUG_FS
 void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m);
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index cdc0bb5677d1..495f7a8e9296 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -394,6 +394,13 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		submit->cmd[i].size = submit_cmd.size / 4;
 		submit->cmd[i].obj = etnaviv_obj;
 
+		if (!etnaviv_cmd_validate_one(gpu, etnaviv_obj,
+					      submit->cmd[i].offset,
+					      submit->cmd[i].size)) {
+			ret = -EINVAL;
+			goto out;
+		}
+
 		ret = submit_reloc(submit, etnaviv_obj,
 				   submit_cmd.submit_offset,
 				   submit_cmd.nr_relocs, submit_cmd.relocs);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

There is no need to restrict access to the get_param ioctl; this is
used to obtain information about the device and has no side effects.

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 8fba9826d019..80b3c667be71 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -507,7 +507,7 @@ static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
 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(GET_PARAM,    get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GET_PARAM,    get_param,    DRM_UNLOCKED|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_NEW,      gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_INFO,     gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_CPU_PREP, gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

GC600 does not set busy bits in its idle register for modules which are
not present.  Add handling to ensure that we don't misinterpret these
zero bits as indicating that these modules are busy.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 15 +++++++++++++++
 drivers/staging/etnaviv/etnaviv_gpu.h |  1 +
 2 files changed, 16 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 9ae96752e8e0..85cd7f18950e 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -290,6 +290,20 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 	dev_info(gpu->dev, "minor_features3: %x\n",
 		 gpu->identity.minor_features3);
 
+	/* GC600 idle register reports zero bits where modules aren't present */
+	if (gpu->identity.model == chipModel_GC600) {
+		gpu->idle_mask = VIVS_HI_IDLE_STATE_TX |
+				 VIVS_HI_IDLE_STATE_RA |
+				 VIVS_HI_IDLE_STATE_SE |
+				 VIVS_HI_IDLE_STATE_PA |
+				 VIVS_HI_IDLE_STATE_SH |
+				 VIVS_HI_IDLE_STATE_PE |
+				 VIVS_HI_IDLE_STATE_DE |
+				 VIVS_HI_IDLE_STATE_FE;
+	} else {
+		gpu->idle_mask = ~VIVS_HI_IDLE_STATE_AXI_LP;
+	}
+
 	etnaviv_hw_specs(gpu);
 }
 
@@ -533,6 +547,7 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 
 	seq_printf(m, "\taxi: 0x%08x\n", axi);
 	seq_printf(m, "\tidle: 0x%08x\n", idle);
+	idle |= ~gpu->idle_mask & ~VIVS_HI_IDLE_STATE_AXI_LP;
 	if ((idle & VIVS_HI_IDLE_STATE_FE) == 0)
 		seq_puts(m, "\t FE is not idle\n");
 	if ((idle & VIVS_HI_IDLE_STATE_DE) == 0)
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 6ae60254dea8..6dedc41f7e59 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -100,6 +100,7 @@ struct etnaviv_gpu {
 	/* list of GEM active objects: */
 	struct list_head active_list;
 
+	uint32_t idle_mask;
 	uint32_t submitted_fence;
 	uint32_t retired_fence;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Shut down the external clock when the I2S block is not in use.

Signed-off-by: Russell King <[email-masked]>
---
 sound/soc/kirkwood/kirkwood-i2s.c | 29 +++++++++++++++++++++++------
 sound/soc/kirkwood/kirkwood.h     |  1 +
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 3a36d60e1785..4cc5dbb4d9d6 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -107,7 +107,7 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai,
 {
 	uint32_t clks_ctrl;
 
-	if (IS_ERR(priv->extclk)) {
+	if (!priv->extclk_enabled) {
 		/* use internal dco for the supported rates
 		 * defined in kirkwood_i2s_dai */
 		dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
@@ -151,6 +151,14 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
 		i2s_reg = KIRKWOOD_I2S_RECCTL;
 	}
 
+	if (!IS_ERR(priv->extclk) && !priv->extclk_enabled) {
+		int err = clk_prepare_enable(priv->extclk);
+		if (err)
+			return err;
+
+		priv->extclk_enabled = true;
+	}
+
 	kirkwood_set_rate(dai, priv, params_rate(params));
 
 	i2s_value = readl(priv->io+i2s_reg);
@@ -222,6 +230,19 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int kirkwood_i2s_hw_free(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
+
+	if (priv->extclk_enabled) {
+		clk_disable_unprepare(priv->extclk);
+		priv->extclk_enabled = false;
+	}
+
+	return 0;
+}
+
 static unsigned kirkwood_i2s_play_mute(unsigned ctl)
 {
 	if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
@@ -435,6 +456,7 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
 	.startup	= kirkwood_i2s_startup,
 	.trigger	= kirkwood_i2s_trigger,
 	.hw_params      = kirkwood_i2s_hw_params,
+	.hw_free	= kirkwood_i2s_hw_free,
 	.set_fmt        = kirkwood_i2s_set_fmt,
 };
 
@@ -584,7 +606,6 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 			priv->extclk = ERR_PTR(-EINVAL);
 		} else {
 			dev_info(&pdev->dev, "found external clock\n");
-			clk_prepare_enable(priv->extclk);
 			soc_dai = kirkwood_i2s_dai_extclk;
 		}
 	}
@@ -621,8 +642,6 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
  err_platform:
 	snd_soc_unregister_component(&pdev->dev);
  err_component:
-	if (!IS_ERR(priv->extclk))
-		clk_disable_unprepare(priv->extclk);
 	clk_disable_unprepare(priv->clk);
 
 	return err;
@@ -635,8 +654,6 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
 	snd_soc_unregister_platform(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
 
-	if (!IS_ERR(priv->extclk))
-		clk_disable_unprepare(priv->extclk);
 	clk_disable_unprepare(priv->clk);
 
 	return 0;
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
index 90e32a781424..0ad9a6ea1be9 100644
--- a/sound/soc/kirkwood/kirkwood.h
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -141,6 +141,7 @@ struct kirkwood_dma_data {
 	struct snd_pcm_substream *substream_rec;
 	int irq;
 	int burst;
+	bool extclk_enabled;
 };
 
 extern struct snd_soc_platform_driver kirkwood_soc_platform;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add the IEC958 SPDIF controls, which allow the SPDIF parameters to be
appropriately adjusted by user applications, which is especially
important when userspace wishes to output non-audio streams via SPDIF.

We implement all 24 bytes of AES channel status information here, and
add the IEC958 Playback Switch, which switches us between generating
our own IEC958 channel status, and using the user-provided version.

It is necessary to have correct IEC958 channel status for some HDMI
AV receivers to process and reproduce the audio - incorrect channel
status data causes them to ignore the stream.

Signed-off-by: Russell King <[email-masked]>
---
 sound/soc/kirkwood/Kconfig        |   1 +
 sound/soc/kirkwood/kirkwood-i2s.c | 223 +++++++++++++++++++++++++++++++++++++-
 sound/soc/kirkwood/kirkwood.h     |  15 +++
 3 files changed, 238 insertions(+), 1 deletion(-)

diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index 132bb83f8e99..e0fa37280d3c 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,6 +1,7 @@
 config SND_KIRKWOOD_SOC
 	tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
 	depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
+	select SND_PCM_IEC958
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the Kirkwood I2S interface. You will also need to select the
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 4cc5dbb4d9d6..28f3614a2684 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -9,7 +9,7 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  */
-
+#include <linux/bug.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
@@ -19,6 +19,7 @@
 #include <linux/delay.h>
 #include <linux/clk.h>
 #include <sound/pcm.h>
+#include <sound/pcm_iec958.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <linux/platform_data/asoc-kirkwood.h>
@@ -37,6 +38,195 @@
 	(SNDRV_PCM_FMTBIT_S16_LE | \
 	 SNDRV_PCM_FMTBIT_S24_LE)
 
+/*
+ * Write the channel status data into the Kirkwood I2S interface registers,
+ * adding the SPDIF consumer channel number as we go.
+ */
+static void kirkwood_iec958_load(struct kirkwood_dma_data *priv, const u8 *cs)
+{
+	unsigned int i, channel, val;
+	u8 temp_cs[24];
+
+	memcpy(temp_cs, cs, sizeof(temp_cs));
+
+	for (channel = 0; channel < 2; channel ++) {
+		void __iomem *reg = priv->io;
+		u8 *p;
+
+		reg += channel ? KIRKWOOD_SPDIF_STATUS0_R :
+				 KIRKWOOD_SPDIF_STATUS0_L;
+
+		/*
+		 * Insert the channel number
+		 */
+		if (!(temp_cs[0] & IEC958_AES0_PROFESSIONAL)) {
+			temp_cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+			temp_cs[2] |= channel << 4;
+		}
+
+		for (i = 0, p = temp_cs; i < 6 * 4; i += 4, p += 4) {
+			val = p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
+
+			writel_relaxed(val, reg + i);
+		}
+	}
+}
+
+/*
+ * Configure the SPDIF play control register according to the non-audio status
+ * of the stream.  Dove documentation indicates that both the non-PCM and
+ * validity bits must be set for non-audio streams.
+ */
+static void kirkwood_iec958_config(struct kirkwood_dma_data *priv, const u8 *cs)
+{
+	const u32 mask = KIRKWOOD_SPDIF_NON_PCM | KIRKWOOD_SPDIF_REG_VALIDITY;
+	u32 oval, val;
+
+	oval = readl_relaxed(priv->io + KIRKWOOD_SPDIF_PLAYCTL);
+
+	if (cs[0] & IEC958_AES0_NONAUDIO)
+		val = oval | mask;
+	else
+		val = oval & ~mask;
+
+	if (oval != val)
+		writel_relaxed(val, priv->io + KIRKWOOD_SPDIF_PLAYCTL);
+
+	kirkwood_iec958_load(priv, cs);
+}
+
+static int kirkwood_iec958_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+
+	return 0;
+}
+
+static int kirkwood_iec958_mask_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	memset(ucontrol->value.iec958.status, 0,
+	       sizeof(ucontrol->value.iec958.status));
+	memset(ucontrol->value.iec958.status, 0xff, 6 * 4);
+	return 0;
+}
+
+static int kirkwood_iec958_dflt_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
+
+	mutex_lock(&priv->iec958_mutex);
+	memcpy(ucontrol->value.iec958.status, priv->iec958_usr_status,
+	       ARRAY_SIZE(ucontrol->value.iec958.status));
+	mutex_unlock(&priv->iec958_mutex);
+
+	return 0;
+}
+
+static int kirkwood_iec958_dflt_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
+	bool changed;
+
+	BUILD_BUG_ON(ARRAY_SIZE(ucontrol->value.iec958.status) !=
+		     ARRAY_SIZE(priv->iec958_usr_status));
+
+	mutex_lock(&priv->iec958_mutex);
+	changed = !!memcmp(ucontrol->value.iec958.status,
+			   priv->iec958_usr_status,
+			   ARRAY_SIZE(priv->iec958_usr_status));
+
+	memcpy(priv->iec958_usr_status, ucontrol->value.iec958.status,
+	       ARRAY_SIZE(priv->iec958_usr_status));
+
+	if (priv->iec958_switch)
+		kirkwood_iec958_config(priv, priv->iec958_usr_status);
+	mutex_unlock(&priv->iec958_mutex);
+
+	return changed;
+}
+
+/*
+ * We use the IEC958 Playback Switch control to adjust the channel status
+ * data we apply to the output stream; this control does not mute the
+ * SPDIF output (which may be for HDMI.)
+ */
+static int kirkwood_iec958_switch_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
+
+	ucontrol->value.integer.value[0] = priv->iec958_switch;
+
+	return 0;
+}
+
+static int kirkwood_iec958_switch_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
+	bool oldval, newval;
+
+	newval = !!ucontrol->value.integer.value[0];
+
+	mutex_lock(&priv->iec958_mutex);
+	oldval = priv->iec958_switch;
+	priv->iec958_switch = newval;
+
+	if (oldval != newval) {
+		const u8 *iec958_status;
+
+		if (newval)
+			iec958_status = priv->iec958_usr_status;
+		else
+			iec958_status = priv->iec958_pcm_status;
+
+		kirkwood_iec958_config(priv, iec958_status);
+	}
+	mutex_unlock(&priv->iec958_mutex);
+
+	return newval != oldval;
+}
+
+static const struct snd_kcontrol_new kirkwood_i2s_iec958_controls[] = {
+	{
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+		.access	= SNDRV_CTL_ELEM_ACCESS_READ,
+		.info	= kirkwood_iec958_info,
+		.get	= kirkwood_iec958_mask_get,
+	}, {
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
+		.access	= SNDRV_CTL_ELEM_ACCESS_READ,
+		.info	= kirkwood_iec958_info,
+		.get	= kirkwood_iec958_mask_get,
+	}, {
+		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
+		.name	= SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+		.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info	= kirkwood_iec958_info,
+		.get	= kirkwood_iec958_dflt_get,
+		.put	= kirkwood_iec958_dflt_put,
+	}, {
+		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
+		.name	= SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+		.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info	= snd_ctl_boolean_mono_info,
+		.get	= kirkwood_iec958_switch_get,
+		.put	= kirkwood_iec958_switch_put,
+	},
+};
+
 static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
@@ -243,6 +433,25 @@ static int kirkwood_i2s_hw_free(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int kirkwood_i2s_prepare(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mutex_lock(&priv->iec958_mutex);
+		snd_pcm_create_iec958_consumer(substream->runtime,
+					       priv->iec958_pcm_status,
+				       ARRAY_SIZE(priv->iec958_pcm_status));
+
+		if (!priv->iec958_switch)
+			kirkwood_iec958_config(priv, priv->iec958_pcm_status);
+		mutex_unlock(&priv->iec958_mutex);
+	}
+
+	return 0;
+}
+
 static unsigned kirkwood_i2s_play_mute(unsigned ctl)
 {
 	if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
@@ -421,6 +630,15 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
 {
 	unsigned long value;
 	unsigned int reg_data;
+	int ret;
+
+	ret = snd_soc_add_dai_controls(dai, kirkwood_i2s_iec958_controls,
+				ARRAY_SIZE(kirkwood_i2s_iec958_controls));
+	if (ret) {
+		dev_err(dai->dev,
+			"unable to add soc card controls: %d\n", ret);
+		return ret;
+	}
 
 	/* put system in a "safe" state : */
 	/* disable audio interrupts */
@@ -457,6 +675,7 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
 	.trigger	= kirkwood_i2s_trigger,
 	.hw_params      = kirkwood_i2s_hw_params,
 	.hw_free	= kirkwood_i2s_hw_free,
+	.prepare	= kirkwood_i2s_prepare,
 	.set_fmt        = kirkwood_i2s_set_fmt,
 };
 
@@ -566,6 +785,8 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 	}
 	dev_set_drvdata(&pdev->dev, priv);
 
+	mutex_init(&priv->iec958_mutex);
+
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	priv->io = devm_ioremap_resource(&pdev->dev, mem);
 	if (IS_ERR(priv->io))
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
index 0ad9a6ea1be9..88ca12fbaf07 100644
--- a/sound/soc/kirkwood/kirkwood.h
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -108,6 +108,17 @@
 #define KIRKWOOD_PLAY_BYTE_INT_COUNT		0x1314
 #define KIRKWOOD_BYTE_INT_COUNT_MASK		0xffffff
 
+#define KIRKWOOD_SPDIF_PLAYCTL			0x2204
+#define KIRKWOOD_SPDIF_NON_PCM			(1<<17)
+#define KIRKWOOD_SPDIF_REG_VALIDITY		(1<<16)
+#define KIRKWOOD_SPDIF_FORCE_PARERR		(1<<4)
+#define KIRKWOOD_SPDIF_MEM_USER_EN		(1<<2)
+#define KIRKWOOD_SPDIF_MEM_VALIDITY_EN		(1<<1)
+#define KIRKWOOD_SPDIF_BLOCK_START_MODE		(1<<0)
+
+#define KIRKWOOD_SPDIF_STATUS0_L		0x2280
+#define KIRKWOOD_SPDIF_STATUS0_R		0x22a0
+
 #define KIRKWOOD_I2S_PLAYCTL			0x2508
 #define KIRKWOOD_I2S_RECCTL			0x2408
 #define KIRKWOOD_I2S_CTL_JUST_MASK		(0xf<<26)
@@ -142,6 +153,10 @@ struct kirkwood_dma_data {
 	int irq;
 	int burst;
 	bool extclk_enabled;
+	bool iec958_switch;
+	struct mutex iec958_mutex;
+	u8 iec958_usr_status[24];
+	u8 iec958_pcm_status[24];
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add support for SPDIF for Dove Cubox.  Based on a commit by Rabeeh
Khoury in the solid-run GIT, original author Sebastian Hesselbrath
([email-masked]).  Subsequent modifications
and cleanups by rmk.

Signed-off-by: Russell King <[email-masked]>
---
 sound/soc/kirkwood/Kconfig          |   8 +++
 sound/soc/kirkwood/Makefile         |   2 +
 sound/soc/kirkwood/kirkwood-spdif.c | 107 ++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+)
 create mode 100644 sound/soc/kirkwood/kirkwood-spdif.c

diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index e0fa37280d3c..f9ea2c4e8b50 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -7,6 +7,14 @@ config SND_KIRKWOOD_SOC
 	  the Kirkwood I2S interface. You will also need to select the
 	  audio interfaces to support below.
 
+config SND_KIRKWOOD_SOC_SPDIF
+	tristate "SoC Audio support for Kirkwood generic S/PDIF"
+	depends on SND_KIRKWOOD_SOC
+	select SND_SOC_SPDIF
+	help
+	  Say Y if you want to add support for SoC audio on
+	  generic S/PDIF.
+
 config SND_KIRKWOOD_SOC_ARMADA370_DB
 	tristate "SoC Audio support for Armada 370 DB"
 	depends on SND_KIRKWOOD_SOC && (ARCH_MVEBU || COMPILE_TEST) && I2C
diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile
index c36b03d8006c..aa0175ec15d3 100644
--- a/sound/soc/kirkwood/Makefile
+++ b/sound/soc/kirkwood/Makefile
@@ -3,5 +3,7 @@ snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
 obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
 
 snd-soc-armada-370-db-objs := armada-370-db.o
+snd-soc-kirkwood-spdif-objs := kirkwood-spdif.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC_ARMADA370_DB) += snd-soc-armada-370-db.o
+obj-$(CONFIG_SND_KIRKWOOD_SOC_SPDIF) += snd-soc-kirkwood-spdif.o
diff --git a/sound/soc/kirkwood/kirkwood-spdif.c b/sound/soc/kirkwood/kirkwood-spdif.c
new file mode 100644
index 000000000000..af9d5dd74d8d
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-spdif.c
@@ -0,0 +1,107 @@
+/*
+ * kirkwood-spdif.c
+ *
+ * (c) 2012 Sebastian Hesselbarth <[email-masked]>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_link kirkwood_spdif_dai0[] = {
+	{
+		.name = "S/PDIF0",
+		.stream_name = "S/PDIF0 PCM Playback",
+		.platform_name = "mvebu-audio.0",
+		.cpu_dai_name = "mvebu-audio.0",
+		.codec_dai_name = "dit-hifi",
+		.codec_name = "spdif-dit",
+	},
+};
+
+static struct snd_soc_dai_link kirkwood_spdif_dai1[] = {
+	{
+		.name = "S/PDIF1",
+		.stream_name = "IEC958 Playback",
+		.platform_name = "mvebu-audio.1",
+		.cpu_dai_name = "mvebu-audio.1",
+		.codec_dai_name = "dit-hifi",
+		.codec_name = "spdif-dit",
+	},
+};
+
+static int kirkwood_spdif_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: pdev = %p, pdev->id = %d",
+		__func__, pdev, pdev->id);
+
+	if (pdev->id < 0 || pdev->id > 1)
+		return -EINVAL;
+
+	card = kzalloc(sizeof(struct snd_soc_card), GFP_KERNEL);
+	if (card == NULL) {
+		dev_err(&pdev->dev, "unable to allocate soc card\n");
+		ret = -ENOMEM;
+		goto kirkwood_spdif_err_card_alloc;
+	}
+
+	card->name = "Kirkwood SPDIF";
+	card->owner = THIS_MODULE;
+	if (pdev->id == 0)
+		card->dai_link = kirkwood_spdif_dai0;
+	else
+		card->dai_link = kirkwood_spdif_dai1;
+	card->num_links = 1;
+	card->dev = &pdev->dev;
+
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register card\n");
+		goto kirkwood_spdif_err_card_alloc;
+	}
+	return 0;
+
+kirkwood_spdif_err_card_alloc:
+	kfree(card);
+	return ret;
+}
+
+static int kirkwood_spdif_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+	kfree(card);
+
+	return 0;
+}
+
+static struct platform_driver kirkwood_spdif_driver = {
+	.driver		= {
+		.name	= "kirkwood-spdif-audio",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= kirkwood_spdif_probe,
+	.remove		= kirkwood_spdif_remove,
+};
+module_platform_driver(kirkwood_spdif_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <[email-masked]>");
+MODULE_DESCRIPTION("ALSA SoC kirkwood SPDIF audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kirkwood-spdif-audio");

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add DPCM support to kirkwood-i2s to support the I2S and SPDIF streams.
This consists of:
- a single front end DAI called "kirkwood-fe" with "dma-tx" and "dma-rx"
  streams.
- one backend DAI called "kirkwood-i2s" for I2S with streams named
  "i2s-tx" and "i2s-rx"
- one backend DAI called "kirkwood-spdif" for SPDIF with a single stream
  named "spdif-tx".

DAPM widgets are used to connect the backend i2s-tx/spdif-tx streams to
the dma-tx frontend stream, and similarly for the capture side.  SPDIF
capture is not supported by this patch.

We avoid the requirement that streams must not be started independently
by keeping a separate mask of which streams are enabled - and this mask
is only used in the playback trigger when we start playback.

Signed-off-by: Russell King <[email-masked]>
---
 sound/soc/kirkwood/kirkwood-i2s.c   | 252 ++++++++++++++++++++++++------------
 sound/soc/kirkwood/kirkwood-spdif.c |  26 +++-
 sound/soc/kirkwood/kirkwood.h       |  20 +++
 3 files changed, 209 insertions(+), 89 deletions(-)

index 28f3614a2684..4e663e86c733 100644
@@ -38,6 +38,18 @@
 
+/* Workaround ASoC not respecting backend restrictions */
+#define KIRKWOOD_FE_FORMATS (KIRKWOOD_I2S_FORMATS & KIRKWOOD_SPDIF_FORMATS)
+
+enum {
+	KW_DAI_FE,
+	KW_DAI_BE_I2S,
+	KW_DAI_BE_SPDIF,
+
+	KW_DAI_BE_SPDIF_PLAYBACK = BIT(0),
+	KW_DAI_BE_SPDIF_CAPTURE = BIT(1),
+};
+
 /*
  * Write the channel status data into the Kirkwood I2S interface registers,
  * adding the SPDIF consumer channel number as we go.
@@ -491,13 +503,12 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
+		if (priv->ctl_play_mask == ~KIRKWOOD_PLAYCTL_ENABLE_MASK)
+			return -EINVAL;
+
 		/* configure */
-		ctl = priv->ctl_play;
-		if (dai->id == 0)
-			ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;	/* i2s */
-		else
-			ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN;	/* spdif */
-		ctl = kirkwood_i2s_play_mute(ctl);
+		ctl = kirkwood_i2s_play_mute(priv->ctl_play &
+					     priv->ctl_play_mask);
 		value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
 		writel(value, priv->io + KIRKWOOD_PLAYCTL);
 
@@ -559,13 +570,11 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		/* configure */
-		ctl = priv->ctl_rec;
-		if (dai->id == 0)
-			ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN;	/* i2s */
-		else
-			ctl &= ~KIRKWOOD_RECCTL_I2S_EN;		/* spdif */
+		if (priv->ctl_rec_mask == ~KIRKWOOD_RECCTL_ENABLE_MASK)
+			return -EINVAL;
 
+		/* configure */
+		ctl = priv->ctl_rec & priv->ctl_rec_mask;
 		value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
 		writel(value, priv->io + KIRKWOOD_RECCTL);
 
@@ -626,18 +635,23 @@ static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 	return 0;
 }
 
-static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
+static int kirkwood_fe_probe(struct snd_soc_dai *dai)
 {
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
 	unsigned long value;
 	unsigned int reg_data;
-	int ret;
 
-	ret = snd_soc_add_dai_controls(dai, kirkwood_i2s_iec958_controls,
+	if (priv->have_spdif) {
+		int ret;
+
+		ret = snd_soc_add_dai_controls(dai,
+				kirkwood_i2s_iec958_controls,
 				ARRAY_SIZE(kirkwood_i2s_iec958_controls));
-	if (ret) {
-		dev_err(dai->dev,
-			"unable to add soc card controls: %d\n", ret);
-		return ret;
+		if (ret) {
+			dev_err(dai->dev,
+				"unable to add soc card controls: %d\n", ret);
+			return ret;
+		}
 	}
 
 	/* put system in a "safe" state : */
@@ -670,7 +684,7 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
 
 }
 
-static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
+static const struct snd_soc_dai_ops kirkwood_dai_fe_ops = {
 	.startup	= kirkwood_i2s_startup,
 	.trigger	= kirkwood_i2s_trigger,
 	.hw_params      = kirkwood_i2s_hw_params,
@@ -679,90 +693,123 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
 	.set_fmt        = kirkwood_i2s_set_fmt,
 };
 
-static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
-    {
-	.name = "i2s",
-	.id = 0,
-	.playback = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-				SNDRV_PCM_RATE_96000,
-		.formats = KIRKWOOD_I2S_FORMATS,
-	},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-				SNDRV_PCM_RATE_96000,
-		.formats = KIRKWOOD_I2S_FORMATS,
-	},
-	.ops = &kirkwood_i2s_dai_ops,
-    },
-    {
-	.name = "spdif",
-	.id = 1,
+static int kirkwood_i2s_be_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
+
+	switch (dai->id) {
+	case KW_DAI_BE_I2S:
+		priv->ctl_play_mask |= KIRKWOOD_PLAYCTL_I2S_EN;
+		priv->ctl_rec_mask |= KIRKWOOD_RECCTL_I2S_EN;
+		break;
+
+	case KW_DAI_BE_SPDIF:
+		priv->ctl_play_mask |= KIRKWOOD_PLAYCTL_SPDIF_EN;
+		priv->ctl_rec_mask |= KIRKWOOD_RECCTL_SPDIF_EN;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void kirkwood_i2s_be_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
+
+	switch (dai->id) {
+	case KW_DAI_BE_I2S:
+		priv->ctl_play_mask &= ~KIRKWOOD_PLAYCTL_I2S_EN;
+		priv->ctl_rec_mask &= ~KIRKWOOD_RECCTL_I2S_EN;
+		break;
+
+	case KW_DAI_BE_SPDIF:
+		priv->ctl_play_mask &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;
+		priv->ctl_rec_mask &= ~KIRKWOOD_RECCTL_SPDIF_EN;
+		break;
+	}
+}
+
+static const struct snd_soc_dai_ops kirkwood_i2s_be_dai_ops = {
+	.startup	= kirkwood_i2s_be_startup,
+	.shutdown	= kirkwood_i2s_be_shutdown,
+};
+
+
+static const struct snd_soc_dai_driver kirkwood_dai_fe = {
+	.name = "kirkwood-fe",
+	.id = KW_DAI_FE,
+	.probe = kirkwood_fe_probe,
 	.playback = {
+		.stream_name = "dma-tx",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
 				SNDRV_PCM_RATE_96000,
-		.formats = KIRKWOOD_SPDIF_FORMATS,
+		.formats = KIRKWOOD_FE_FORMATS,
 	},
 	.capture = {
+		.stream_name = "dma-rx",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
 				SNDRV_PCM_RATE_96000,
-		.formats = KIRKWOOD_SPDIF_FORMATS,
+		.formats = KIRKWOOD_FE_FORMATS,
 	},
-	.ops = &kirkwood_i2s_dai_ops,
-    },
+	.ops = &kirkwood_dai_fe_ops,
 };
 
-static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
-    {
-	.name = "i2s",
-	.id = 0,
+static const struct snd_soc_dai_driver kirkwood_dai_fe_extclk = {
+	.name = "kirkwood-fe",
+	.id = KW_DAI_FE,
+	.probe = kirkwood_fe_probe,
 	.playback = {
+		.stream_name = "dma-tx",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
-		.rate_min = 5512,
-		.rate_max = 192000,
-		.formats = KIRKWOOD_I2S_FORMATS,
+		.formats = KIRKWOOD_FE_FORMATS,
 	},
 	.capture = {
+		.stream_name = "dma-rx",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
-		.rate_min = 5512,
-		.rate_max = 192000,
-		.formats = KIRKWOOD_I2S_FORMATS,
-	},
-	.ops = &kirkwood_i2s_dai_ops,
-    },
-    {
-	.name = "spdif",
-	.id = 1,
-	.playback = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_CONTINUOUS,
-		.rate_min = 5512,
-		.rate_max = 192000,
-		.formats = KIRKWOOD_SPDIF_FORMATS,
+		.formats = KIRKWOOD_FE_FORMATS,
 	},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_CONTINUOUS,
-		.rate_min = 5512,
-		.rate_max = 192000,
-		.formats = KIRKWOOD_SPDIF_FORMATS,
+	.ops = &kirkwood_dai_fe_ops,
+};
+
+static const struct snd_soc_dai_driver kirkwood_dai_be[] = {
+	{
+		.name = "kirkwood-i2s",
+		.id = KW_DAI_BE_I2S,
+		.ops = &kirkwood_i2s_be_dai_ops,
+		.playback = {
+			.stream_name = "i2s-tx",
+			.formats = KIRKWOOD_I2S_FORMATS,
+		},
+		.capture = {
+			.stream_name = "i2s-rx",
+			.formats = KIRKWOOD_I2S_FORMATS,
+		},
+	}, {
+		.name = "kirkwood-spdif",
+		.id = KW_DAI_BE_SPDIF,
+		.ops = &kirkwood_i2s_be_dai_ops,
+		.playback = {
+			.stream_name = "spdif-tx",
+			.formats = KIRKWOOD_SPDIF_FORMATS,
+		},
+		.capture = {
+			.stream_name = "spdif-rx",
+			.formats = KIRKWOOD_SPDIF_FORMATS,
+		},
 	},
-	.ops = &kirkwood_i2s_dai_ops,
-    },
 };
 
 static const struct snd_soc_component_driver kirkwood_i2s_component = {
@@ -772,10 +819,12 @@ static const struct snd_soc_component_driver kirkwood_i2s_component = {
 static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 {
 	struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
-	struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
+	const struct snd_soc_dai_driver *soc_dai = &kirkwood_dai_fe;
+	struct snd_soc_dai_driver *dai;
 	struct kirkwood_dma_data *priv;
 	struct resource *mem;
 	struct device_node *np = pdev->dev.of_node;
+	unsigned i;
 	int err;
 
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -798,6 +847,13 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	/*
+	 * We currently have no way to determine whether SPDIF playback
+	 * or capture is currently supported; take the middle ground
+	 * for the time being until DT/platform data passes this detail.
+	 */
+	priv->have_spdif = KW_DAI_BE_SPDIF_PLAYBACK;
+
 	if (np) {
 		priv->burst = 128;		/* might be 32 or 128 */
 	} else if (data) {
@@ -827,13 +883,15 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 			priv->extclk = ERR_PTR(-EINVAL);
 		} else {
 			dev_info(&pdev->dev, "found external clock\n");
-			soc_dai = kirkwood_i2s_dai_extclk;
+			soc_dai = &kirkwood_dai_fe_extclk;
 		}
 	}
 
 	/* Some sensible defaults - this reflects the powerup values */
 	priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
 	priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
+	priv->ctl_play_mask = ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
+	priv->ctl_rec_mask = ~KIRKWOOD_RECCTL_ENABLE_MASK;
 
 	/* Select the burst size */
 	if (priv->burst == 32) {
@@ -844,8 +902,35 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
 	}
 
+	dai = priv->dai_driver;
+	memcpy(dai, soc_dai, sizeof(*dai));
+
+	/* Copy the frontend channels and rates to the backends */
+	for (i = 1; i < ARRAY_SIZE(priv->dai_driver); i++) {
+		memcpy(&dai[i], &kirkwood_dai_be[i - 1], sizeof(*dai));
+		dai[i].playback.channels_min = dai[0].playback.channels_min;
+		dai[i].playback.channels_max = dai[0].playback.channels_max;
+		dai[i].playback.rates        = dai[0].playback.rates;
+		dai[i].playback.rate_min     = dai[0].playback.rate_min;
+		dai[i].playback.rate_max     = dai[0].playback.rate_max;
+		dai[i].capture.channels_min  = dai[0].capture.channels_min;
+		dai[i].capture.channels_max  = dai[0].capture.channels_max;
+		dai[i].capture.rates         = dai[0].capture.rates;
+		dai[i].capture.rate_min      = dai[0].capture.rate_min;
+		dai[i].capture.rate_max      = dai[0].capture.rate_max;
+	}
+
+	/*
+	 * Kill the SPDIF stream information according to
+	 * the capabilities we have on this device.
+	 */
+	if (!(priv->have_spdif & KW_DAI_BE_SPDIF_PLAYBACK))
+		memset(&dai[2].playback, 0, sizeof(dai[2].playback));
+	if (!(priv->have_spdif & KW_DAI_BE_SPDIF_CAPTURE))
+		memset(&dai[2].capture, 0, sizeof(dai[2].capture));
+
 	err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component,
-					 soc_dai, 2);
+					 dai, 2 + !!priv->have_spdif);
 	if (err) {
 		dev_err(&pdev->dev, "snd_soc_register_component failed\n");
 		goto err_component;
@@ -857,9 +942,8 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 		goto err_platform;
 	}
 
-	kirkwood_i2s_init(priv);
-
 	return 0;
+
  err_platform:
 	snd_soc_unregister_component(&pdev->dev);
  err_component:
diff --git a/sound/soc/kirkwood/kirkwood-spdif.c b/sound/soc/kirkwood/kirkwood-spdif.c
index af9d5dd74d8d..ef9fa26d071f 100644
--- a/sound/soc/kirkwood/kirkwood-spdif.c
+++ b/sound/soc/kirkwood/kirkwood-spdif.c
@@ -20,25 +20,39 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 
+#include "kirkwood.h"
+
+static const struct snd_soc_dapm_route routes[] = {
+	{ "spdif-tx", NULL, "dma-tx" },
+};
+
 static struct snd_soc_dai_link kirkwood_spdif_dai0[] = {
+	KIRKWOOD_FE_DAI_LINK(".0", 1, 0),
 	{
 		.name = "S/PDIF0",
 		.stream_name = "S/PDIF0 PCM Playback",
-		.platform_name = "mvebu-audio.0",
-		.cpu_dai_name = "mvebu-audio.0",
+		.cpu_name = "mvebu-audio.0",
+		.platform_name = "snd-soc-dummy",
+		.cpu_dai_name = "kirkwood-spdif",
 		.codec_dai_name = "dit-hifi",
 		.codec_name = "spdif-dit",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
 	},
 };
 
 static struct snd_soc_dai_link kirkwood_spdif_dai1[] = {
+	KIRKWOOD_FE_DAI_LINK(".1", 1, 0),
 	{
 		.name = "S/PDIF1",
 		.stream_name = "IEC958 Playback",
-		.platform_name = "mvebu-audio.1",
-		.cpu_dai_name = "mvebu-audio.1",
+		.cpu_name = "mvebu-audio.1",
+		.platform_name = "snd-soc-dummy",
+		.cpu_dai_name = "kirkwood-spdif",
 		.codec_dai_name = "dit-hifi",
 		.codec_name = "spdif-dit",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
 	},
 };
 
@@ -66,7 +80,9 @@ static int kirkwood_spdif_probe(struct platform_device *pdev)
 		card->dai_link = kirkwood_spdif_dai0;
 	else
 		card->dai_link = kirkwood_spdif_dai1;
-	card->num_links = 1;
+	card->num_links = 2;
+	card->dapm_routes = routes;
+	card->num_dapm_routes = ARRAY_SIZE(routes);
 	card->dev = &pdev->dev;
 
 	ret = snd_soc_register_card(card);
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
index 88ca12fbaf07..69cbb4b627b8 100644
--- a/sound/soc/kirkwood/kirkwood.h
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -142,12 +142,19 @@
 #define KIRKWOOD_SND_MAX_BUFFER_BYTES		(KIRKWOOD_SND_MAX_PERIOD_BYTES \
 						 * KIRKWOOD_SND_MAX_PERIODS)
 
+#define KIRKWOOD_NUM_DAIS 3
+
 struct kirkwood_dma_data {
 	void __iomem *io;
 	struct clk *clk;
 	struct clk *extclk;
+	unsigned have_spdif;
+	uint32_t ctl_play_mask;
 	uint32_t ctl_play;
+	uint32_t ctl_rec_mask;
 	uint32_t ctl_rec;
+	struct snd_soc_dai *active_dai;
+	struct snd_soc_dai_driver dai_driver[KIRKWOOD_NUM_DAIS];
 	struct snd_pcm_substream *substream_play;
 	struct snd_pcm_substream *substream_rec;
 	int irq;
@@ -161,4 +168,17 @@ struct kirkwood_dma_data {

+#define KIRKWOOD_FE_DAI_LINK(id, play, capt) {	\
+	.name = "Kirkwood-FE",			\
+	.stream_name = "FE PCM Playback",	\
+	.cpu_name = "mvebu-audio" id,		\
+	.cpu_dai_name = "kirkwood-fe",		\
+	.codec_name = "snd-soc-dummy",		\
+	.codec_dai_name = "snd-soc-dummy-dai",	\
+	.platform_name = "mvebu-audio" id,	\
+	.dynamic = 1,				\
+	.dpcm_capture = capt,			\
+	.dpcm_playback = play,			\
+}
+
 #endif

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Signed-off-by: Russell King <[email-masked]>
---
 sound/soc/kirkwood/kirkwood-spdif.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/sound/soc/kirkwood/kirkwood-spdif.c b/sound/soc/kirkwood/kirkwood-spdif.c
index ef9fa26d071f..8cf82ca07fe0 100644
--- a/sound/soc/kirkwood/kirkwood-spdif.c
+++ b/sound/soc/kirkwood/kirkwood-spdif.c
@@ -56,6 +56,28 @@ static struct snd_soc_dai_link kirkwood_spdif_dai1[] = {
 	},
 };
 
+static void kirkwood_spdif_of(struct snd_soc_card *card, int id)
+{
+	struct device_node *node;
+	int i;
+
+	for (node = NULL; (node = of_find_compatible_node(node, NULL, "marvell,dove-audio")) != NULL; ) {
+		if (!of_device_is_available(node))
+			continue;
+		break;
+	}
+
+	if (node) {
+		for (i = 0; i < card->num_links; i++) {
+			card->dai_link[i].cpu_name = NULL;
+			card->dai_link[i].cpu_of_node = node;
+		}
+		card->dai_link[0].platform_name = NULL;
+		card->dai_link[0].platform_of_node = node;
+	}
+	of_node_put(node);
+}
+
 static int kirkwood_spdif_probe(struct platform_device *pdev)
 {
 	struct snd_soc_card *card;
@@ -85,6 +107,8 @@ static int kirkwood_spdif_probe(struct platform_device *pdev)
 	card->num_dapm_routes = ARRAY_SIZE(routes);
 	card->dev = &pdev->dev;
 
+	kirkwood_spdif_of(card, pdev->id);
+
 	ret = snd_soc_register_card(card);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to register card\n");

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add a miscdevice based VMeta driver.  Changes from the UIO VMeta driver
include:
- Rather than globally tracking the power and clock status (meaning that
  each user can potentially interfere with each others request to have
  these enabled) track the status on a per-user basis.  Rely on the clk
  API counts to ensure that the clocks are running when any one user
  requires that the clock/power be running (iow, each user may at most
  own one count in the clk API.)
- axi_clk is removed; there are no enable/disable methods associated
  with this clock, so the clock manipulation does nothing useful.
- Whenever a process quits without cleaning up (eg, due to SIGINT) the
  UIO driver spammed the kernel log.  This is not necessary as we can
  clean up after the process.  This allows us to make better use of the
  bitmap operations.
- Add a DT compatible property to the driver, so that DT implementations
  can make use of this driver.

Signed-off-by: Russell King <[email-masked]>
---
 Documentation/devicetree/bindings/misc/vmeta.txt |    8 +
 drivers/misc/Kconfig                             |    7 +
 drivers/misc/Makefile                            |    1 +
 drivers/misc/vmeta.c                             | 1086 ++++++++++++++++++++++
 include/uapi/linux/vmeta.h                       |  108 +++
 5 files changed, 1210 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/vmeta.txt
 create mode 100644 drivers/misc/vmeta.c
 create mode 100644 include/uapi/linux/vmeta.h

diff --git a/Documentation/devicetree/bindings/misc/vmeta.txt b/Documentation/devicetree/bindings/misc/vmeta.txt
new file mode 100644
index 000000000000..96f9a017e012
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/vmeta.txt
@@ -0,0 +1,8 @@
+* Marvell Dove VMeta driver.
+
+Required properties:
+- compatible: "marvell,vmeta"
+- reg: should contain the VMetea registers location and length
+- interrupts: should contain one or two interrupts.
+	- first interrupt (required) is the main VMeta interrupt
+	- second interrupt (optional) is the bus error interrupt
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 42c38525904b..936614c2a553 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -506,6 +506,13 @@ config SRAM
 	  the genalloc API. It is supposed to be used for small on-chip SRAM
 	  areas found on many SoCs.
 
+config MARVELL_VMETA
+	tristate "Marvell Vmeta driver"
+	depends on ARCH_DOVE
+	help
+	  Enable support for the Marvell Vmeta multi-format video decoder
+	  engine found in Dove SoCs
+
 config VEXPRESS_SYSCFG
 	bool "Versatile Express System Configuration driver"
 	depends on VEXPRESS_CONFIG
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d056fb7186fe..50fe395a596e 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -46,6 +46,7 @@ obj-y				+= ti-st/
 obj-y				+= lis3lv02d/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
+obj-$(CONFIG_MARVELL_VMETA)	+= vmeta.o
 obj-$(CONFIG_INTEL_MEI)		+= mei/
 obj-$(CONFIG_VMWARE_VMCI)	+= vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o
diff --git a/drivers/misc/vmeta.c b/drivers/misc/vmeta.c
new file mode 100644
index 000000000000..13e59b8afe9b
--- /dev/null
+++ b/drivers/misc/vmeta.c
@@ -0,0 +1,1086 @@
+/*
+ * drivers/misc/vmeta.c
+ *
+ * Marvell multi-format video decoder engine driver.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Based on an earlier version by Peter Liao.
+ * Taken from Rabeeh's Cubox kernel and cleaned up by Russell King.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/file.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/mman.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/shmem_fs.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/uaccess.h>
+#include <linux/vmeta.h>
+
+#define VDEC_HW_CONTEXT_SIZE	SZ_512K
+#define VDEC_OBJ_SIZE		SZ_64K
+
+#define VMETA_NAME		"ap510-vmeta"
+#define VMETA_REVISION		105
+
+struct vmeta_memory {
+	void		*cpu;
+	dma_addr_t	dma;
+	size_t		size;
+};
+
+/* private */
+struct vmeta_instance {
+	struct device		*dev;
+	struct mutex		mutex;
+	unsigned long		use;
+
+	struct miscdevice	misc;
+
+	phys_addr_t		reg_phys;
+	size_t			reg_size;
+	struct vmeta_memory	memory;
+
+	/* shared s/w context */
+	struct file		*sw_context;
+
+	/* irq */
+	int			irq_bus;
+	int			irq_func;
+	struct timer_list	irq_poll_timer;
+	spinlock_t		irq_lock;
+	unsigned long		irq_flag;
+
+	/* clock / power */
+	struct clk		*clk;
+
+	/* pm */
+	wait_queue_head_t	suspend_wait;
+	struct completion	suspend_complete;
+	bool			suspend;
+	struct notifier_block	suspend_notifier;
+
+	/* id and access arbitration */
+	DECLARE_BITMAP(id_used, MAX_VMETA_INSTANCE);
+	DECLARE_BITMAP(id_registered, MAX_VMETA_INSTANCE);
+	wait_queue_head_t	id_wait;
+	unsigned		id_active;
+	enum _VMETA_LOCK_FLAG	id_lock;
+	int			id_refcnt;
+
+	/* io support */
+	atomic_t		events;
+	wait_queue_head_t	event_wait;
+	struct fasync_struct	*event_queue;
+};
+
+struct vmeta_user {
+	struct vmeta_instance	*vi;
+	unsigned long		events;
+	DECLARE_BITMAP(id, MAX_VMETA_INSTANCE);
+	bool			clock_on;
+	bool			power_on;
+	struct mutex		buf_mutex;
+	struct idr		buf_idr;
+};
+
+struct vmeta_buffer {
+	struct dma_buf		*buf;
+	struct dma_buf_attachment *attach;
+	struct sg_table		*sgt;
+};
+
+static int vmeta_dmabuf_release_one(int id, void *p, void *data)
+{
+	struct vmeta_buffer *vb = p;
+
+	dma_buf_unmap_attachment(vb->attach, vb->sgt, DMA_BIDIRECTIONAL);
+	dma_buf_detach(vb->buf, vb->attach);
+	dma_buf_put(vb->buf);
+	kfree(vb);
+
+	return 0;
+}
+
+static int vmeta_dmabuf_import(struct vmeta_user *vu,
+	struct vmeta_dmabuf_import *import)
+{
+	struct vmeta_buffer *vb;
+	int ret;
+
+	vb = kmalloc(sizeof(*vb), GFP_KERNEL);
+	if (!vb)
+		return -ENOMEM;
+
+	vb->buf = dma_buf_get(import->fd);
+	if (IS_ERR(vb->buf)) {
+		ret = PTR_ERR(vb->buf);
+		goto free;
+	}
+
+	vb->attach = dma_buf_attach(vb->buf, vu->vi->dev);
+	if (IS_ERR(vb->attach)) {
+		ret = PTR_ERR(vb->attach);
+		goto put;
+	}
+
+	vb->sgt = dma_buf_map_attachment(vb->attach, DMA_BIDIRECTIONAL);
+	if (IS_ERR(vb->sgt)) {
+		ret = PTR_ERR(vb->sgt);
+		goto detach;
+	}
+
+	if (vb->sgt->nents != 1) {
+		ret = -EINVAL;
+		goto detach;
+	}
+
+	mutex_lock(&vu->buf_mutex);
+	ret = idr_alloc(&vu->buf_idr, vb, 1, 0, GFP_KERNEL);
+	mutex_unlock(&vu->buf_mutex);
+
+	if (ret < 0)
+		goto unmap;
+
+	import->phys = sg_dma_address(vb->sgt->sgl);
+	import->size = sg_dma_len(vb->sgt->sgl);
+	import->id = ret;
+
+	return 0;
+
+ unmap:
+	dma_buf_unmap_attachment(vb->attach, vb->sgt, DMA_BIDIRECTIONAL);
+ detach:
+	dma_buf_detach(vb->buf, vb->attach);
+ put:
+	dma_buf_put(vb->buf);
+ free:
+	kfree(vb);
+	return ret;
+}
+
+static int vmeta_dmabuf_release(struct vmeta_user *vu, int id)
+{
+	struct vmeta_buffer *vb;
+
+	mutex_lock(&vu->buf_mutex);
+	vb = idr_find(&vu->buf_idr, id);
+	if (vb)
+		idr_remove(&vu->buf_idr, id);
+	mutex_unlock(&vu->buf_mutex);
+	if (!vb)
+		return -EINVAL;
+
+	dma_buf_unmap_attachment(vb->attach, vb->sgt, DMA_BIDIRECTIONAL);
+	dma_buf_detach(vb->buf, vb->attach);
+	dma_buf_put(vb->buf);
+	kfree(vb);
+
+	return 0;
+}
+
+static void vmeta_event(struct vmeta_instance *vi)
+{
+	atomic_inc(&vi->events);
+	wake_up_interruptible(&vi->event_wait);
+	kill_fasync(&vi->event_queue, SIGIO, POLL_IN);
+}
+
+static int vmeta_pm_event(struct notifier_block *notifier, unsigned long val,
+			  void *v)
+{
+	struct vmeta_instance *vi = container_of(notifier, struct vmeta_instance, suspend_notifier);
+	int ret;
+
+	/*
+	 * Hibernation and suspend events
+	 * PM_HIBERNATION_PREPARE 0x0001 Going to hibernate
+	 * PM_POST_HIBERNATION    0x0002 Hibernation finished
+	 * PM_SUSPEND_PREPARE     0x0003 Going to suspend the system
+	 * PM_POST_SUSPEND        0x0004 Suspend finished
+	 * PM_RESTORE_PREPARE     0x0005 Going to restore a saved image
+	 * PM_POST_RESTORE        0x0006 Restore failed
+	 */
+	vmeta_event(vi);
+
+	switch (val) {
+	case PM_SUSPEND_PREPARE:
+	case PM_HIBERNATION_PREPARE:
+		if (vi->use < 1)
+			break;
+
+		vi->suspend = true;
+		ret = wait_for_completion_killable_timeout(&vi->suspend_complete, HZ);
+		if (ret <= 0) {
+			/* Timed out or killed */
+			vi->suspend = false;
+			return NOTIFY_BAD;
+		}
+		break;
+	case PM_POST_SUSPEND:
+	case PM_POST_HIBERNATION:
+		vi->suspend = false;
+		wake_up_interruptible(&vi->suspend_wait);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int __vmeta_power_on(struct vmeta_instance *vi)
+{
+	pm_runtime_get_sync(vi->dev);
+
+	return 0;
+}
+
+static int __vmeta_power_off(struct vmeta_instance *vi)
+{
+	pm_runtime_mark_last_busy(vi->dev);
+	pm_runtime_put_autosuspend(vi->dev);
+
+	return 0;
+}
+
+static int __vmeta_clk_on(struct vmeta_instance *vi)
+{
+	clk_prepare_enable(vi->clk);
+
+	return 0;
+}
+
+static int __vmeta_clk_off(struct vmeta_instance *vi)
+{
+	clk_disable_unprepare(vi->clk);
+
+	return 0;
+}
+
+static int vmeta_clk_switch(struct vmeta_instance *vi, unsigned long clk_flag)
+{
+	int ret = 0;
+	unsigned long clk_rate = 0;
+
+	if (IS_ERR(vi->clk)) {
+		dev_err(vi->dev, "vmeta_clk_switch error\n");
+		return -1;
+	}
+
+	mutex_lock(&vi->mutex);
+	if (clk_flag == 0)
+		clk_rate = 400000000; /* 400MHz */
+	else if (clk_flag == 1)
+		clk_rate = 500000000; /* 500MHz */
+	else if (clk_flag == 2)
+		clk_rate = 667000000; /* 667MHz */
+	ret = clk_set_rate(vi->clk, clk_rate);
+	mutex_unlock(&vi->mutex);
+	return ret;
+}
+
+static void vmeta_irq_poll_timer_handler(unsigned long data)
+{
+	struct vmeta_instance *vi = (struct vmeta_instance *)data;
+
+	if (vi->irq_flag == 0)
+		vmeta_event(vi);
+
+	mod_timer(&vi->irq_poll_timer, jiffies + HZ/100); /* 10ms timer */
+}
+
+static irqreturn_t vmeta_bus_irq_handler(int irq, void *dev_id)
+{
+	struct vmeta_instance *vi = dev_id;
+
+	dev_err(vi->dev, "bus error detected\n");
+	vmeta_event(vi);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vmeta_func_irq_handler(int irq, void *dev_id)
+{
+	struct vmeta_instance *vi = dev_id;
+	unsigned long flags;
+
+	/*
+	 * Just disable the interrupt in the interrupt controller, and
+	 * remember the state so we can allow user space to enable it later.
+	 */
+	spin_lock_irqsave(&vi->irq_lock, flags);
+	if (!vi->irq_flag) {
+		vi->irq_flag = 1;
+		disable_irq_nosync(irq);
+	}
+	spin_unlock_irqrestore(&vi->irq_lock, flags);
+
+	vmeta_event(vi);
+
+	return IRQ_HANDLED;
+}
+
+static int vmeta_irqcontrol(struct vmeta_instance *vi, s32 irq_on)
+{
+	unsigned long flags;
+
+	/*
+	 * Allow user space to enable and disable the interrupt
+	 * in the interrupt controller, but keep track of the
+	 * state to prevent per-irq depth damage.
+	 *
+	 * Serialize this operation to support multiple tasks.
+	 */
+	spin_lock_irqsave(&vi->irq_lock, flags);
+	if (irq_on) {
+		if (vi->irq_flag) {
+			vi->irq_flag = 0;
+			if (vi->irq_func > 0)
+				enable_irq(vi->irq_func);
+		}
+	} else {
+		if (!vi->irq_flag) {
+			vi->irq_flag = 1;
+			if (vi->irq_func > 0)
+				disable_irq_nosync(vi->irq_func);
+		}
+	}
+	spin_unlock_irqrestore(&vi->irq_lock, flags);
+
+	if (vi->irq_func > 0)
+		synchronize_irq(vi->irq_func);
+
+	return 0;
+}
+
+
+static ssize_t vmeta_write(struct file *file, const char __user *p,
+	size_t sz, loff_t *off)
+{
+	struct vmeta_user *vu = file->private_data;
+	int v;
+
+	if (sz != sizeof(int) || vu->vi->irq_func <= 0)
+		return -EINVAL;
+
+	if (get_user(v, (int __user *)p))
+		return -EFAULT;
+
+	/* Reset IRQ event counter */
+	vu->events = atomic_read(&vu->vi->events);
+
+	return vmeta_irqcontrol(vu->vi, v);
+}
+
+static unsigned vmeta_poll(struct file *file, struct poll_table_struct *pt)
+{
+	struct vmeta_user *vu = file->private_data;
+
+	poll_wait(file, &vu->vi->event_wait, pt);
+	if (vu->events != atomic_read(&vu->vi->events))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int vmeta_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct vmeta_user *vu = file->private_data;
+	phys_addr_t phys;
+	size_t size;
+
+	switch (vma->vm_pgoff) {
+	case 0:
+		phys = vu->vi->reg_phys;
+		size = vu->vi->reg_size;
+		break;
+
+	case 1:
+		phys = vu->vi->memory.dma;
+		size = vu->vi->memory.size;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* Don't allow mapping a region larger than we have */
+	if (vma->vm_end - vma->vm_start > size)
+		return -EINVAL;
+
+	vma->vm_flags |= VM_IO | VM_DONTEXPAND;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	return remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot);
+}
+
+static int vmeta_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *misc = file->private_data;
+	struct vmeta_instance *vi = container_of(misc, struct vmeta_instance, misc);
+	struct vmeta_user *vu;
+	int ret;
+
+	vu = kmalloc(sizeof(*vu), GFP_KERNEL);
+	if (!vu)
+		return -ENOMEM;
+
+	vu->vi = vi;
+	vu->events = atomic_read(&vi->events);
+	bitmap_zero(vu->id, MAX_VMETA_INSTANCE);
+	mutex_init(&vu->buf_mutex);
+	idr_init(&vu->buf_idr);
+
+	mutex_lock(&vi->mutex);
+	ret = __vmeta_clk_on(vi);
+	if (ret == 0)
+		ret = __vmeta_power_on(vi);
+	if (ret != 0)
+		__vmeta_clk_off(vi);
+	if (ret == 0) {
+		vu->clock_on = true;
+		vu->power_on = true;
+	}
+	mutex_unlock(&vi->mutex);
+
+	file->private_data = vu;
+
+	return 0;
+}
+
+static int vmeta_release(struct inode *inode, struct file *file)
+{
+	struct vmeta_user *vu = file->private_data;
+	struct vmeta_instance *vi = vu->vi;
+	DECLARE_BITMAP(ids, MAX_VMETA_INSTANCE);
+	int id;
+
+	mutex_lock(&vi->mutex);
+	for (id = 0; id < MAX_VMETA_INSTANCE; id++) {
+		if (test_bit(id, vu->id)) {
+			if (vi->id_active == id) {
+				/* in case, current instance have been locked */
+				vi->id_active = MAX_VMETA_INSTANCE;
+				if (vi->id_lock == VMETA_LOCK_ON) {
+					dev_err(vi->dev, "instance id(%d) holds the lock and exit normally\n",
+						id);
+					vi->id_lock = VMETA_LOCK_FORCE_INIT;
+				}
+			}
+		}
+	}
+
+	/* Clear the registered and used bitmaps of IDs used by this process */
+	bitmap_and(ids, vi->id_registered, vu->id, MAX_VMETA_INSTANCE);
+	vi->id_refcnt -= bitmap_weight(ids, MAX_VMETA_INSTANCE);
+	bitmap_andnot(vi->id_registered, vi->id_registered, vu->id, MAX_VMETA_INSTANCE);
+	bitmap_andnot(vi->id_used, vi->id_used, vu->id, MAX_VMETA_INSTANCE);
+
+	wake_up_interruptible(&vi->id_wait);
+
+	if (vu->power_on)
+		__vmeta_power_off(vi);
+	if (vu->clock_on)
+		__vmeta_clk_off(vi);
+
+	mutex_unlock(&vi->mutex);
+
+	idr_for_each(&vu->buf_idr, vmeta_dmabuf_release_one, vu);
+	idr_destroy(&vu->buf_idr);
+
+	kfree(vu);
+
+	return 0;
+}
+
+static int vmeta_fasync(int fd, struct file *file, int on)
+{
+	struct vmeta_user *vu = file->private_data;
+	return fasync_helper(fd, file, on, &vu->vi->event_queue);
+}
+
+
+static int vmeta_get_user_id(struct vmeta_user *vu, unsigned *arg)
+{
+	struct vmeta_instance *vi = vu->vi;
+	int id;
+
+	mutex_lock(&vi->mutex);
+	id = find_first_zero_bit(vi->id_used, MAX_VMETA_INSTANCE);
+	if (id < MAX_VMETA_INSTANCE)
+		__set_bit(id, vi->id_used);
+	mutex_unlock(&vi->mutex);
+
+	__set_bit(id, vu->id);
+	*arg = id;
+
+	return 0;
+}
+
+static int vmeta_free_user_id(struct vmeta_user *vu, unsigned id)
+{
+	struct vmeta_instance *vi = vu->vi;
+	int ret;
+
+	if (id >= MAX_VMETA_INSTANCE)
+		return -EINVAL;
+
+	mutex_lock(&vi->mutex);
+	if (!test_bit(id, vi->id_used)) {
+		ret = -EACCES;
+	} else {
+		if (test_bit(id, vi->id_registered))
+			vi->id_refcnt--;
+		__clear_bit(id, vi->id_registered);
+		__clear_bit(id, vi->id_used);
+		ret = 0;
+	}
+	mutex_unlock(&vi->mutex);
+
+	if (ret == 0)
+		__clear_bit(id, vu->id);
+
+	return ret;
+}
+
+static int vmeta_register_user_id(struct vmeta_user *vu, unsigned id)
+{
+	struct vmeta_instance *vi = vu->vi;
+	int ret;
+
+	if (id >= MAX_VMETA_INSTANCE)
+		return -EINVAL;
+
+	mutex_lock(&vi->mutex);
+	if (!test_bit(id, vi->id_used)) {
+		ret = -EACCES;
+	} else if (test_bit(id, vi->id_registered)) {
+		ret = -EBUSY;
+	} else {
+		__set_bit(id, vi->id_registered);
+		vi->id_refcnt++;
+		ret = 0;
+	}
+	mutex_unlock(&vi->mutex);
+
+	return ret;
+}
+
+static int vmeta_unregister_user_id(struct vmeta_user *vu, unsigned id)
+{
+	struct vmeta_instance *vi = vu->vi;
+	int ret;
+
+	if (id >= MAX_VMETA_INSTANCE)
+		return -EINVAL;
+
+	mutex_lock(&vi->mutex);
+	if (!test_bit(id, vi->id_used)) {
+		ret = -EACCES;
+	} else if (!test_bit(id, vi->id_registered)) {
+		ret = -EIO;
+	} else {
+		__clear_bit(id, vi->id_registered);
+		vi->id_refcnt--;
+		ret = 0;
+	}
+	mutex_unlock(&vi->mutex);
+
+	return ret;
+}
+
+static int vmeta_lock_user_id(struct vmeta_user *vu, struct vmeta_lock *lock)
+	__releases(vi->mutex) __acquires(vi->mutex)
+{
+	struct vmeta_instance *vi = vu->vi;
+	int ret;
+
+	if (lock->user_id >= MAX_VMETA_INSTANCE)
+		return -EINVAL;
+
+	if (!test_bit(lock->user_id, vi->id_used)) {
+		ret = -EACCES;
+	} else do {
+		if (vi->id_active == lock->user_id) {
+			ret = -EDEADLOCK;
+			break;
+		} else if (vi->id_active == MAX_VMETA_INSTANCE) {
+			vi->id_active = lock->user_id;
+			if (vi->id_lock == VMETA_LOCK_FORCE_INIT)
+				ret = -EUCLEAN;
+			else
+				ret = 0;
+			vi->id_lock = VMETA_LOCK_ON;
+			break;
+		} else {
+			mutex_unlock(&vi->mutex);
+			ret = wait_event_interruptible_timeout(vi->id_wait,
+					vi->id_active == MAX_VMETA_INSTANCE,
+					msecs_to_jiffies(lock->timeout));
+			mutex_lock(&vi->mutex);
+			if (ret == -ERESTARTSYS)
+				break;
+			if (ret == 0) {
+				ret = -ETIME;
+				break;
+			}
+			lock->timeout = 0;
+		}
+	} while (1);
+
+	return ret;
+}
+
+static int vmeta_unlock_user_id(struct vmeta_user *vu, unsigned id)
+{
+	struct vmeta_instance *vi = vu->vi;
+	int ret = 0;
+
+	if (id >= MAX_VMETA_INSTANCE)
+		return -EINVAL;
+
+	if (!test_bit(id, vi->id_used)) {
+		ret = -EACCES;
+	} else if (vi->id_active == id) {
+		vi->id_active = MAX_VMETA_INSTANCE;
+		vi->id_lock = VMETA_LOCK_OFF;
+		wake_up_interruptible(&vi->id_wait);
+	}
+
+	return ret;
+}
+
+static int vmeta_force_ini(struct vmeta_instance *vi)
+{
+	bitmap_zero(vi->id_used, MAX_VMETA_INSTANCE);
+	bitmap_zero(vi->id_registered, MAX_VMETA_INSTANCE);
+	vi->id_active = MAX_VMETA_INSTANCE;
+	vi->id_lock = VMETA_LOCK_OFF;
+	vi->id_refcnt = 0;
+	wake_up_interruptible(&vi->id_wait);
+
+	return 0;
+}
+
+union vmeta_args {
+	int sint;
+	unsigned uint;
+	struct vmeta_lock lock;
+	struct vmeta_info info;
+	struct vmeta_mmap mmap;
+	struct vmeta_dmabuf_import dmabuf_import;
+	int32_t dmabuf_release;
+};
+
+static long vmeta_unlocked_ioctl(struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	struct vmeta_user *vu = file->private_data;
+	struct vmeta_instance *vi = vu->vi;
+	union vmeta_args args;
+	unsigned size;
+	int ret = 0;
+
+	size = _IOC_SIZE(cmd);
+	if (size > sizeof(args))
+		return -EINVAL;
+	if (_IOC_DIR(cmd) & _IOC_WRITE && size &&
+	    copy_from_user(&args, (void __user *)arg, size))
+		return -EFAULT;
+
+	switch (cmd) {
+	case VMETA_CMD_POWER_ON:
+		mutex_lock(&vi->mutex);
+		if (!vu->power_on) {
+			ret = __vmeta_power_on(vi);
+			if (ret == 0)
+				vu->power_on = true;
+		} else {
+			ret = 0;
+		}
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_POWER_OFF:
+		mutex_lock(&vi->mutex);
+		if (vu->power_on) {
+			ret = __vmeta_power_off(vi);
+			if (ret == 0)
+				vu->power_on = false;
+		} else {
+			ret = 0;
+		}
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_CLK_ON:
+		mutex_lock(&vi->mutex);
+		if (!vu->clock_on) {
+			ret = __vmeta_clk_on(vi);
+			if (ret == 0)
+				vu->clock_on = true;
+		} else {
+			ret = 0;
+		}
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_CLK_OFF:
+		mutex_lock(&vi->mutex);
+		if (vu->clock_on) {
+			ret = __vmeta_clk_off(vi);
+			if (ret == 0)
+				vu->clock_on = false;
+		} else {
+			ret = 0;
+		}
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_CLK_SWITCH:
+		ret = vmeta_clk_switch(vi, arg);
+		break;
+	case VMETA_CMD_SUSPEND_CHECK:
+		args.sint = vi->suspend;
+		ret = 0;
+		break;
+	case VMETA_CMD_SUSPEND_READY:
+		complete(&vi->suspend_complete);
+		ret = wait_event_interruptible(vi->suspend_wait, !vi->suspend);
+		break;
+	case VMETA_CMD_SUSPEND_SET:
+		vi->suspend = true;
+		break;
+	case VMETA_CMD_SUSPEND_UNSET:
+		vi->suspend = false;
+		break;
+	case VMETA_CMD_GET_USER_ID:
+		ret = vmeta_get_user_id(vu, &args.uint);
+		break;
+	case VMETA_CMD_FREE_USER_ID:
+		ret = vmeta_free_user_id(vu, arg);
+		break;
+	case VMETA_CMD_REGISTER_USER_ID:
+		ret = vmeta_register_user_id(vu, arg);
+		break;
+	case VMETA_CMD_UNREGISTER_USER_ID:
+		ret = vmeta_unregister_user_id(vu, arg);
+		break;
+	case VMETA_CMD_LOCK_USER_ID:
+		mutex_lock(&vi->mutex);
+		ret = vmeta_lock_user_id(vu, &args.lock);
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_UNLOCK_USER_ID:
+		mutex_lock(&vi->mutex);
+		ret = vmeta_unlock_user_id(vu, arg);
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_FORCE_INI:
+		mutex_lock(&vi->mutex);
+		ret = vmeta_force_ini(vi);
+		mutex_unlock(&vi->mutex);
+		if (ret == 0)
+			bitmap_zero(vu->id, MAX_VMETA_INSTANCE);
+		break;
+	case VMETA_CMD_GET_USER_NUM:
+		args.uint = vi->id_refcnt;
+		ret = 0;
+		break;
+	case VMETA_CMD_GET_INFO:
+		args.info.revision = VMETA_REVISION;
+		args.info.map[0].phys = vi->reg_phys;
+		args.info.map[0].size = vi->reg_size;
+		args.info.map[1].phys = vi->memory.dma;
+		args.info.map[1].size = vi->memory.size;
+		ret = 0;
+		break;
+	case VMETA_CMD_MAP_SW_CONTEXT:
+		args.mmap.addr = vm_mmap(vi->sw_context, 0, args.mmap.size,
+					 PROT_READ | PROT_WRITE, MAP_SHARED,
+					 0);
+		ret = IS_ERR_VALUE(args.mmap.addr) ? args.mmap.addr : 0;
+		break;
+	case VMETA_CMD_DMABUF_IMPORT:
+		ret = vmeta_dmabuf_import(vu, &args.dmabuf_import);
+		break;
+	case VMETA_CMD_DMABUF_RELEASE:
+		ret = vmeta_dmabuf_release(vu, arg);
+		break;
+	case VMETA_CMD_PM_LOCK_USER_ID:
+		mutex_lock(&vi->mutex);
+		ret = vmeta_lock_user_id(vu, &args.lock);
+		if (ret == 0 || ret == -EUCLEAN) {
+			if (vu->power_on || __vmeta_power_on(vi) == 0) {
+				if (vu->clock_on || __vmeta_clk_on(vi) == 0) {
+					vu->clock_on = true;
+					vu->power_on = true;
+				} else {
+					__vmeta_power_off(vi);
+					vu->power_on = false;
+				}
+			}
+		}
+		mutex_unlock(&vi->mutex);
+		break;
+	case VMETA_CMD_PM_UNLOCK_USER_ID:
+		mutex_lock(&vi->mutex);
+		ret = vmeta_unlock_user_id(vu, arg);
+		if (ret == 0) {
+			if (vu->clock_on)
+				__vmeta_clk_off(vi);
+			if (vu->power_on)
+				__vmeta_power_off(vi);
+			vu->clock_on = false;
+			vu->power_on = false;
+		}
+		mutex_unlock(&vi->mutex);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret >= 0 && _IOC_DIR(cmd) & _IOC_READ &&
+           copy_to_user((void __user *)arg, &args, size))
+		ret = -EFAULT;
+
+	return ret;
+}
+
+static const struct file_operations vmeta_fops = {
+	.owner		= THIS_MODULE,
+	.write		= vmeta_write,
+	.poll		= vmeta_poll,
+	.unlocked_ioctl	= vmeta_unlocked_ioctl,
+	.mmap		= vmeta_mmap,
+	.open		= vmeta_open,
+	.release	= vmeta_release,
+	.fasync		= vmeta_fasync,
+};
+
+static int vmeta_alloc_dma(struct vmeta_instance *vi, size_t size)
+{
+	dma_addr_t dma;
+	void *cpu;
+
+	cpu = dma_alloc_coherent(vi->dev, size, &dma, GFP_KERNEL);
+	if (!cpu)
+		return -ENOMEM;
+
+	vi->memory.cpu = cpu;
+	vi->memory.dma = dma;
+	vi->memory.size = size;
+
+	dev_dbg(vi->dev, "internal addr[%p],addr[0x%08llx] size[%zu]\n",
+		cpu, (unsigned long long)dma, size);
+
+	return 0;
+}
+
+static int vmeta_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct vmeta_instance *vi;
+	int ret, irq_func, irq_bus;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "no memory resources given\n");
+		return -ENODEV;
+	}
+
+	irq_func = platform_get_irq(pdev, 0);
+	if (irq_func < 0) {
+		dev_err(&pdev->dev, "no function irq resources given\n");
+		return -ENODEV;
+	}
+
+	irq_bus = platform_get_irq(pdev, 1);
+	if (irq_bus < 0 && (irq_bus != -ENXIO && irq_bus != -EINVAL)) {
+		dev_err(&pdev->dev, "no bus error irq resources given\n");
+		return -ENODEV;
+	}
+
+	vi = devm_kzalloc(&pdev->dev, sizeof(*vi), GFP_KERNEL);
+	if (vi == NULL) {
+		dev_err(&pdev->dev, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	vi->sw_context = ERR_PTR(-EINVAL);
+
+	vi->clk = devm_clk_get(&pdev->dev, "vmeta_clk");
+	if (IS_ERR(vi->clk)) {
+		ret = PTR_ERR(vi->clk);
+		dev_err(&pdev->dev, "cannot get vmeta clock: %d\n", ret);
+		goto out_free;
+	}
+
+	/* Set vmeta default clock to 500MHz */
+	clk_set_rate(vi->clk, 500000000);
+
+	if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+			dev_name(&pdev->dev))) {
+		dev_err(&pdev->dev, "can't request register region %pR\n", res);
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	platform_set_drvdata(pdev, vi);
+
+	vi->dev = &pdev->dev;
+	mutex_init(&vi->mutex);
+	vi->use = 0;
+
+	vi->misc.minor = MISC_DYNAMIC_MINOR;
+	vi->misc.name = "vmeta";
+	vi->misc.fops = &vmeta_fops;
+	vi->misc.parent = &pdev->dev;
+
+	vi->reg_phys = res->start;
+	vi->reg_size = resource_size(res);
+
+	vi->irq_bus = irq_bus;
+	vi->irq_func = irq_func;
+	init_timer(&vi->irq_poll_timer);
+	spin_lock_init(&vi->irq_lock);
+	vi->irq_flag = 0; /* interrupt is enabled to begin with */
+
+	ret = vmeta_alloc_dma(vi, VDEC_HW_CONTEXT_SIZE);
+	if (ret)
+		goto out_free;
+
+	vi->sw_context = shmem_file_setup("vmeta sw context", VDEC_OBJ_SIZE, VM_NORESERVE);
+	if (IS_ERR(vi->sw_context)) {
+		ret = PTR_ERR(vi->sw_context);
+		goto out_free;
+	}
+
+	/* init a wait queue for pm event sync */
+	init_waitqueue_head(&vi->suspend_wait);
+	init_completion(&vi->suspend_complete);
+	vi->suspend_notifier.notifier_call = vmeta_pm_event;
+	vi->suspend_notifier.priority = -5;
+
+	init_waitqueue_head(&vi->id_wait);
+	vmeta_force_ini(vi);
+
+	init_waitqueue_head(&vi->event_wait);
+
+	pm_runtime_use_autosuspend(vi->dev);
+	pm_runtime_set_autosuspend_delay(vi->dev, 100);
+	pm_runtime_enable(vi->dev);
+
+	ret = misc_register(&vi->misc);
+	if (ret)
+		goto out_runtime;
+
+	if (vi->irq_func > 0) {
+		ret = request_irq(vi->irq_func, vmeta_func_irq_handler, 0,
+				  dev_name(&pdev->dev), vi);
+		if (ret) {
+			dev_err(&pdev->dev, "can't request func irq\n");
+			goto out_func_irq;
+		}
+	} else {
+		vi->irq_poll_timer.data = (unsigned long)vi;
+		vi->irq_poll_timer.function = vmeta_irq_poll_timer_handler;
+		mod_timer(&vi->irq_poll_timer, jiffies + HZ/100);
+	}
+
+	if (vi->irq_bus > 0) {
+		ret = request_irq(vi->irq_bus, vmeta_bus_irq_handler, 0,
+				  dev_name(&pdev->dev), vi);
+		if (ret) {
+			dev_err(&pdev->dev, "can't request bus irq\n");
+			goto out_bus_irq;
+		}
+	}
+
+	/* register a pm notifier */
+	register_pm_notifier(&vi->suspend_notifier);
+
+	return 0;
+
+out_bus_irq:
+	if (vi->irq_func > 0)
+		free_irq(vi->irq_func, vi);
+	del_timer_sync(&vi->irq_poll_timer);
+out_func_irq:
+	misc_deregister(&vi->misc);
+out_runtime:
+	pm_runtime_disable(vi->dev);
+out_free:
+	if (vi->memory.size)
+		dma_free_coherent(&pdev->dev, vi->memory.size,
+				  vi->memory.cpu, vi->memory.dma);
+
+	if (!IS_ERR(vi->sw_context))
+		fput(vi->sw_context);
+
+	return ret;
+}
+
+static int vmeta_remove(struct platform_device *pdev)
+{
+	struct vmeta_instance *vi = platform_get_drvdata(pdev);
+
+	if (vi->irq_bus > 0)
+		free_irq(vi->irq_bus, vi);
+	if (vi->irq_func > 0)
+		free_irq(vi->irq_func, vi);
+
+	del_timer_sync(&vi->irq_poll_timer);
+	misc_deregister(&vi->misc);
+
+	/* unregister pm notifier */
+	unregister_pm_notifier(&vi->suspend_notifier);
+
+	if (!IS_ERR(vi->sw_context))
+		fput(vi->sw_context);
+
+	if (vi->memory.size)
+		dma_free_coherent(&pdev->dev, vi->memory.size,
+				  vi->memory.cpu, vi->memory.dma);
+
+	pm_runtime_disable(vi->dev);
+
+	return 0;
+}
+
+static struct of_device_id vmeta_of_match[] = {
+	{
+		.compatible = "marvell,vmeta",
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, vmeta_of_match);
+
+static struct platform_driver vmeta_driver = {
+	.probe		= vmeta_probe,
+	.remove		= vmeta_remove,
+	.driver = {
+		.name	= VMETA_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = vmeta_of_match,
+	},
+};
+
+module_platform_driver(vmeta_driver);
+
+MODULE_DESCRIPTION("UIO driver for Marvell multi-format video codec engine");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:"VMETA_NAME);
diff --git a/include/uapi/linux/vmeta.h b/include/uapi/linux/vmeta.h
new file mode 100644
index 000000000000..b20d578d4932
--- /dev/null
+++ b/include/uapi/linux/vmeta.h
@@ -0,0 +1,108 @@
+/*
+ * This software program is licensed subject to the GNU General Public License
+ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
+
+ * (C) Copyright 2010 Marvell International Ltd.
+ * All Rights Reserved
+ */
+
+#ifndef _UAPI_LINUX_VMETA_H
+#define _UAPI_LINUX_VMETA_H
+
+typedef unsigned int vmeta_instance_status;
+
+struct _id_instance {
+	vmeta_instance_status status;
+	int height;
+	int width;
+	int frame_rate;
+	pid_t pid;
+	unsigned int pt; /* pthread_t */
+};
+
+#define MAX_VMETA_INSTANCE 32
+
+enum _VMETA_LOCK_FLAG {
+	VMETA_LOCK_OFF = 0,
+	VMETA_LOCK_ON,
+	VMETA_LOCK_FORCE_INIT
+};
+
+/* This struct should be aligned with user space API */
+struct _kernel_share {
+	int ref_count;
+	enum _VMETA_LOCK_FLAG lock_flag;
+	int active_user_id;
+	struct _id_instance user_id_list[MAX_VMETA_INSTANCE];
+};
+
+#ifndef __KERNEL__
+/*
+ * Needed for userspace
+ */
+typedef enum _VMETA_LOCK_FLAG VMETA_LOCK_FLAG;
+typedef struct _id_instance id_instance;
+typedef struct _kernel_share kernel_share;
+#endif
+
+struct vmeta_lock {
+	unsigned long timeout;
+	unsigned int user_id;
+};
+
+struct vmeta_info {
+	uint32_t	revision;
+	struct {
+		uint32_t	phys;
+		uint32_t	size;
+	} map[2];
+};
+
+struct vmeta_mmap {
+	uint64_t	addr;
+	uint32_t	size;
+};
+
+struct vmeta_dmabuf_import {
+	uint64_t	phys;
+	uint32_t	size;
+	int32_t		fd;
+	int32_t		id;
+};
+
+#define VMETA_STATUS_BIT_USED		0
+#define VMETA_STATUS_BIT_REGISTED	1
+#define VMETA_STATUS_USED		BIT(0)
+#define VMETA_STATUS_REGISTERED		BIT(1)
+
+#define IOP_MAGIC	('v')
+
+#define VMETA_CMD_POWER_ON		_IO(IOP_MAGIC, 0)
+#define VMETA_CMD_POWER_OFF		_IO(IOP_MAGIC, 1)
+#define VMETA_CMD_CLK_ON		_IO(IOP_MAGIC, 2)
+#define VMETA_CMD_CLK_OFF		_IO(IOP_MAGIC, 3)
+#define VMETA_CMD_CLK_SWITCH		_IO(IOP_MAGIC, 4)
+#define VMETA_CMD_LOCK			_IO(IOP_MAGIC, 5)
+#define VMETA_CMD_UNLOCK		_IO(IOP_MAGIC, 6)
+#define VMETA_CMD_PRIV_LOCK		_IO(IOP_MAGIC, 7)
+#define VMETA_CMD_PRIV_UNLOCK		_IO(IOP_MAGIC, 8)
+#define VMETA_CMD_SUSPEND_CHECK		_IOR(IOP_MAGIC, 9, int)
+#define VMETA_CMD_SUSPEND_READY		_IO(IOP_MAGIC, 10)
+#define VMETA_CMD_SUSPEND_SET		_IO(IOP_MAGIC, 11)
+#define VMETA_CMD_SUSPEND_UNSET		_IO(IOP_MAGIC, 12)
+#define VMETA_CMD_GET_USER_ID		_IOR(IOP_MAGIC, 13, unsigned)
+#define VMETA_CMD_FREE_USER_ID		_IO(IOP_MAGIC, 14)
+#define VMETA_CMD_REGISTER_USER_ID	_IO(IOP_MAGIC, 15)
+#define VMETA_CMD_UNREGISTER_USER_ID	_IO(IOP_MAGIC, 16)
+#define VMETA_CMD_LOCK_USER_ID		_IOW(IOP_MAGIC, 17, struct vmeta_lock)
+#define VMETA_CMD_UNLOCK_USER_ID	_IO(IOP_MAGIC, 18)
+#define VMETA_CMD_FORCE_INI		_IO(IOP_MAGIC, 19)
+#define VMETA_CMD_GET_USER_NUM		_IOR(IOP_MAGIC, 20, unsigned)
+#define VMETA_CMD_GET_INFO		_IOR(IOP_MAGIC, 21, struct vmeta_info)
+#define VMETA_CMD_MAP_SW_CONTEXT	_IOWR(IOP_MAGIC, 22, struct vmeta_mmap)
+#define VMETA_CMD_DMABUF_IMPORT		_IOWR(IOP_MAGIC, 23, struct vmeta_dmabuf_import)
+#define VMETA_CMD_DMABUF_RELEASE	_IO(IOP_MAGIC, 24)
+#define VMETA_CMD_PM_LOCK_USER_ID	_IOW(IOP_MAGIC, 25, struct vmeta_lock)
+#define VMETA_CMD_PM_UNLOCK_USER_ID	_IO(IOP_MAGIC, 26)
+
+#endif /* __UIO_VMETA_H */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add the required DT binding documentation for the Marvell PMU driver.

Signed-off-by: Russell King <[email-masked]>
---
 Documentation/devicetree/bindings/soc/dove/pmu.txt | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/dove/pmu.txt

diff --git a/Documentation/devicetree/bindings/soc/dove/pmu.txt b/Documentation/devicetree/bindings/soc/dove/pmu.txt
new file mode 100644
index 000000000000..edd40b796b74
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/dove/pmu.txt
@@ -0,0 +1,56 @@
+Device Tree bindings for Marvell PMU
+
+Required properties:
+ - compatible: value should be "marvell,dove-pmu".
+    May also include "simple-bus" if there are child devices, in which
+    case the ranges node is required.
+ - reg: two base addresses and sizes of the PM controller and PMU.
+ - interrupts: single interrupt number for the PMU interrupt
+ - interrupt-controller: must be specified as the PMU itself is an
+    interrupt controller.
+ - #interrupt-cells: must be 1.
+ - #reset-cells: must be 1.
+ - domains: sub-node containing domain descriptions
+
+Optional properties:
+ - ranges: defines the address mapping for child devices, as per the
+   standard property of this name.  Required when compatible includes
+   "simple-bus".
+
+Power domain descriptions are listed as child nodes of the "domains"
+sub-node.  Each domain has the following properties:
+
+Required properties:
+ - #power-domain-cells: must be 0.
+
+Optional properties:
+ - marvell,pmu_pwr_mask: specifies the mask value for PMU power register
+ - marvell,pmu_iso_mask: specifies the mask value for PMU isolation register
+ - resets: points to the reset manager (PMU node) and reset index.
+
+Example:
+
+	pmu: power-management@d0000 {
+		compatible = "marvell,dove-pmu";
+		reg = <0xd0000 0x8000>, <0xd8000 0x8000>;
+		interrupts = <33>;
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		#reset-cells = <1>;
+
+		domains {
+			vpu_domain: vpu-domain {
+				#power-domain-cells = <0>;
+				marvell,pmu_pwr_mask = <0x00000008>;
+				marvell,pmu_iso_mask = <0x00000001>;
+				resets = <&pmu 16>;
+			};
+
+			gpu_domain: gpu-domain {
+				#power-domain-cells = <0>;
+				marvell,pmu_pwr_mask = <0x00000004>;
+				marvell,pmu_iso_mask = <0x00000002>;
+				resets = <&pmu 18>;
+			};
+		};
+	};

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add the PMU node, and move the child devices of the PMU node beneath
this new node, giving it a "simple-bus" so that the OF platform
device creator will create these child devices.  No functional change
from this is expected.

The PMU provides multiple features, including an interrupt, reset,
power and isolation controller.

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/dove.dtsi | 590 +++++++++++++++++++++++---------------------
 1 file changed, 304 insertions(+), 286 deletions(-)

diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 38b1f7e6004e..9fb2dc77176e 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -419,293 +419,311 @@
 				status = "disabled";
 			};
 
-			thermal: thermal-diode@d001c {
-				compatible = "marvell,dove-thermal";
-				reg = <0xd001c 0x0c>, <0xd005c 0x08>;
-			};
-
-			gate_clk: clock-gating-ctrl@d0038 {
-				compatible = "marvell,dove-gating-clock";
-				reg = <0xd0038 0x4>;
-				clocks = <&core_clk 0>;
-				#clock-cells = <1>;
-			};
-
-			pinctrl: pin-ctrl@d0200 {
-				compatible = "marvell,dove-pinctrl";
-				reg = <0xd0200 0x14>,
-				      <0xd0440 0x04>;
-				clocks = <&gate_clk 22>;
-
-				pmx_gpio_0: pmx-gpio-0 {
-					marvell,pins = "mpp0";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_1: pmx-gpio-1 {
-					marvell,pins = "mpp1";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_2: pmx-gpio-2 {
-					marvell,pins = "mpp2";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_3: pmx-gpio-3 {
-					marvell,pins = "mpp3";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_4: pmx-gpio-4 {
-					marvell,pins = "mpp4";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_5: pmx-gpio-5 {
-					marvell,pins = "mpp5";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_6: pmx-gpio-6 {
-					marvell,pins = "mpp6";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_7: pmx-gpio-7 {
-					marvell,pins = "mpp7";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_8: pmx-gpio-8 {
-					marvell,pins = "mpp8";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_9: pmx-gpio-9 {
-					marvell,pins = "mpp9";
-					marvell,function = "gpio";
-				};
-
-				pmx_pcie1_clkreq: pmx-pcie1-clkreq {
-					marvell,pins = "mpp9";
-					marvell,function = "pex1";
-				};
-
-				pmx_gpio_10: pmx-gpio-10 {
-					marvell,pins = "mpp10";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_11: pmx-gpio-11 {
-					marvell,pins = "mpp11";
-					marvell,function = "gpio";
-				};
-
-				pmx_pcie0_clkreq: pmx-pcie0-clkreq {
-					marvell,pins = "mpp11";
-					marvell,function = "pex0";
-				};
-
-				pmx_gpio_12: pmx-gpio-12 {
-					marvell,pins = "mpp12";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_13: pmx-gpio-13 {
-					marvell,pins = "mpp13";
-					marvell,function = "gpio";
-				};
-
-				pmx_audio1_extclk: pmx-audio1-extclk {
-					marvell,pins = "mpp13";
-					marvell,function = "audio1";
-				};
-
-				pmx_gpio_14: pmx-gpio-14 {
-					marvell,pins = "mpp14";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_15: pmx-gpio-15 {
-					marvell,pins = "mpp15";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_16: pmx-gpio-16 {
-					marvell,pins = "mpp16";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_17: pmx-gpio-17 {
-					marvell,pins = "mpp17";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_18: pmx-gpio-18 {
-					marvell,pins = "mpp18";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_19: pmx-gpio-19 {
-					marvell,pins = "mpp19";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_20: pmx-gpio-20 {
-					marvell,pins = "mpp20";
-					marvell,function = "gpio";
-				};
-
-				pmx_gpio_21: pmx-gpio-21 {
-					marvell,pins = "mpp21";
-					marvell,function = "gpio";
-				};
-
-				pmx_camera: pmx-camera {
-					marvell,pins = "mpp_camera";
-					marvell,function = "camera";
-				};
-
-				pmx_camera_gpio: pmx-camera-gpio {
-					marvell,pins = "mpp_camera";
-					marvell,function = "gpio";
-				};
-
-				pmx_sdio0: pmx-sdio0 {
-					marvell,pins = "mpp_sdio0";
-					marvell,function = "sdio0";
-				};
-
-				pmx_sdio0_gpio: pmx-sdio0-gpio {
-					marvell,pins = "mpp_sdio0";
-					marvell,function = "gpio";
-				};
-
-				pmx_sdio1: pmx-sdio1 {
-					marvell,pins = "mpp_sdio1";
-					marvell,function = "sdio1";
-				};
-
-				pmx_sdio1_gpio: pmx-sdio1-gpio {
-					marvell,pins = "mpp_sdio1";
-					marvell,function = "gpio";
-				};
-
-				pmx_audio1_gpio: pmx-audio1-gpio {
-					marvell,pins = "mpp_audio1";
-					marvell,function = "gpio";
-				};
-
-				pmx_audio1_i2s1_spdifo: pmx-audio1-i2s1-spdifo {
-					marvell,pins = "mpp_audio1";
-					marvell,function = "i2s1/spdifo";
-				};
-
-				pmx_spi0: pmx-spi0 {
-					marvell,pins = "mpp_spi0";
-					marvell,function = "spi0";
-				};
-
-				pmx_spi0_gpio: pmx-spi0-gpio {
-					marvell,pins = "mpp_spi0";
-					marvell,function = "gpio";
-				};
-
-				pmx_spi1_4_7: pmx-spi1-4-7 {
-					marvell,pins = "mpp4", "mpp5",
-						"mpp6", "mpp7";
-					marvell,function = "spi1";
-				};
-
-				pmx_spi1_20_23: pmx-spi1-20-23 {
-					marvell,pins = "mpp20", "mpp21",
-						"mpp22", "mpp23";
-					marvell,function = "spi1";
-				};
-
-				pmx_uart1: pmx-uart1 {
-					marvell,pins = "mpp_uart1";
-					marvell,function = "uart1";
-				};
-
-				pmx_uart1_gpio: pmx-uart1-gpio {
-					marvell,pins = "mpp_uart1";
-					marvell,function = "gpio";
-				};
-
-				pmx_nand: pmx-nand {
-					marvell,pins = "mpp_nand";
-					marvell,function = "nand";
-				};
-
-				pmx_nand_gpo: pmx-nand-gpo {
-					marvell,pins = "mpp_nand";
-					marvell,function = "gpo";
-				};
-
-				pmx_i2c1: pmx-i2c1 {
-					marvell,pins = "mpp17", "mpp19";
-					marvell,function = "twsi";
-				};
-
-				pmx_i2c2: pmx-i2c2 {
-					marvell,pins = "mpp_audio1";
-					marvell,function = "twsi";
-				};
-
-				pmx_ssp_i2c2: pmx-ssp-i2c2 {
-					marvell,pins = "mpp_audio1";
-					marvell,function = "ssp/twsi";
-				};
-
-				pmx_i2cmux_0: pmx-i2cmux-0 {
-					marvell,pins = "twsi";
-					marvell,function = "twsi-opt1";
-				};
-
-				pmx_i2cmux_1: pmx-i2cmux-1 {
-					marvell,pins = "twsi";
-					marvell,function = "twsi-opt2";
-				};
-
-				pmx_i2cmux_2: pmx-i2cmux-2 {
-					marvell,pins = "twsi";
-					marvell,function = "twsi-opt3";
-				};
-			};
-
-			core_clk: core-clocks@d0214 {
-				compatible = "marvell,dove-core-clock";
-				reg = <0xd0214 0x4>;
-				#clock-cells = <1>;
-			};
-
-			gpio0: gpio-ctrl@d0400 {
-				compatible = "marvell,orion-gpio";
-				#gpio-cells = <2>;
-				gpio-controller;
-				reg = <0xd0400 0x20>;
-				ngpios = <32>;
-				interrupt-controller;
-				#interrupt-cells = <2>;
-				interrupts = <12>, <13>, <14>, <60>;
-			};
-
-			gpio1: gpio-ctrl@d0420 {
-				compatible = "marvell,orion-gpio";
-				#gpio-cells = <2>;
-				gpio-controller;
-				reg = <0xd0420 0x20>;
-				ngpios = <32>;
+			pmu: power-management@d0000 {
+				compatible = "marvell,dove-pmu", "simple-bus";
+				reg = <0xd0000 0x8000>, <0xd8000 0x8000>;
+				ranges = <0x00000000 0x000d0000 0x8000
+					  0x00008000 0x000d8000 0x8000>;
+				interrupts = <33>;
 				interrupt-controller;
-				#interrupt-cells = <2>;
-				interrupts = <61>;
-			};
-
-			rtc: real-time-clock@d8500 {
-				compatible = "marvell,orion-rtc";
-				reg = <0xd8500 0x20>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				#interrupt-cells = <1>;
+				#reset-cells = <1>;
+
+				domains {
+				};
+
+				thermal: thermal-diode@001c {
+					compatible = "marvell,dove-thermal";
+					reg = <0x001c 0x0c>, <0x005c 0x08>;
+				};
+
+				gate_clk: clock-gating-ctrl@0038 {
+					compatible = "marvell,dove-gating-clock";
+					reg = <0x0038 0x4>;
+					clocks = <&core_clk 0>;
+					#clock-cells = <1>;
+				};
+
+				pinctrl: pin-ctrl@0200 {
+					compatible = "marvell,dove-pinctrl";
+					reg = <0x0200 0x14>,
+					      <0x0440 0x04>;
+					clocks = <&gate_clk 22>;
+
+					pmx_gpio_0: pmx-gpio-0 {
+						marvell,pins = "mpp0";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_1: pmx-gpio-1 {
+						marvell,pins = "mpp1";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_2: pmx-gpio-2 {
+						marvell,pins = "mpp2";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_3: pmx-gpio-3 {
+						marvell,pins = "mpp3";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_4: pmx-gpio-4 {
+						marvell,pins = "mpp4";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_5: pmx-gpio-5 {
+						marvell,pins = "mpp5";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_6: pmx-gpio-6 {
+						marvell,pins = "mpp6";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_7: pmx-gpio-7 {
+						marvell,pins = "mpp7";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_8: pmx-gpio-8 {
+						marvell,pins = "mpp8";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_9: pmx-gpio-9 {
+						marvell,pins = "mpp9";
+						marvell,function = "gpio";
+					};
+
+					pmx_pcie1_clkreq: pmx-pcie1-clkreq {
+						marvell,pins = "mpp9";
+						marvell,function = "pex1";
+					};
+
+					pmx_gpio_10: pmx-gpio-10 {
+						marvell,pins = "mpp10";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_11: pmx-gpio-11 {
+						marvell,pins = "mpp11";
+						marvell,function = "gpio";
+					};
+
+					pmx_pcie0_clkreq: pmx-pcie0-clkreq {
+						marvell,pins = "mpp11";
+						marvell,function = "pex0";
+					};
+
+					pmx_gpio_12: pmx-gpio-12 {
+						marvell,pins = "mpp12";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_13: pmx-gpio-13 {
+						marvell,pins = "mpp13";
+						marvell,function = "gpio";
+					};
+
+					pmx_audio1_extclk: pmx-audio1-extclk {
+						marvell,pins = "mpp13";
+						marvell,function = "audio1";
+					};
+
+					pmx_gpio_14: pmx-gpio-14 {
+						marvell,pins = "mpp14";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_15: pmx-gpio-15 {
+						marvell,pins = "mpp15";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_16: pmx-gpio-16 {
+						marvell,pins = "mpp16";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_17: pmx-gpio-17 {
+						marvell,pins = "mpp17";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_18: pmx-gpio-18 {
+						marvell,pins = "mpp18";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_19: pmx-gpio-19 {
+						marvell,pins = "mpp19";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_20: pmx-gpio-20 {
+						marvell,pins = "mpp20";
+						marvell,function = "gpio";
+					};
+
+					pmx_gpio_21: pmx-gpio-21 {
+						marvell,pins = "mpp21";
+						marvell,function = "gpio";
+					};
+
+					pmx_camera: pmx-camera {
+						marvell,pins = "mpp_camera";
+						marvell,function = "camera";
+					};
+
+					pmx_camera_gpio: pmx-camera-gpio {
+						marvell,pins = "mpp_camera";
+						marvell,function = "gpio";
+					};
+
+					pmx_sdio0: pmx-sdio0 {
+						marvell,pins = "mpp_sdio0";
+						marvell,function = "sdio0";
+					};
+
+					pmx_sdio0_gpio: pmx-sdio0-gpio {
+						marvell,pins = "mpp_sdio0";
+						marvell,function = "gpio";
+					};
+
+					pmx_sdio1: pmx-sdio1 {
+						marvell,pins = "mpp_sdio1";
+						marvell,function = "sdio1";
+					};
+
+					pmx_sdio1_gpio: pmx-sdio1-gpio {
+						marvell,pins = "mpp_sdio1";
+						marvell,function = "gpio";
+					};
+
+					pmx_audio1_gpio: pmx-audio1-gpio {
+						marvell,pins = "mpp_audio1";
+						marvell,function = "gpio";
+					};
+
+					pmx_audio1_i2s1_spdifo: pmx-audio1-i2s1-spdifo {
+						marvell,pins = "mpp_audio1";
+						marvell,function = "i2s1/spdifo";
+					};
+
+					pmx_spi0: pmx-spi0 {
+						marvell,pins = "mpp_spi0";
+						marvell,function = "spi0";
+					};
+
+					pmx_spi0_gpio: pmx-spi0-gpio {
+						marvell,pins = "mpp_spi0";
+						marvell,function = "gpio";
+					};
+
+					pmx_spi1_4_7: pmx-spi1-4-7 {
+						marvell,pins = "mpp4", "mpp5",
+							"mpp6", "mpp7";
+						marvell,function = "spi1";
+					};
+
+					pmx_spi1_20_23: pmx-spi1-20-23 {
+						marvell,pins = "mpp20", "mpp21",
+							"mpp22", "mpp23";
+						marvell,function = "spi1";
+					};
+
+					pmx_uart1: pmx-uart1 {
+						marvell,pins = "mpp_uart1";
+						marvell,function = "uart1";
+					};
+
+					pmx_uart1_gpio: pmx-uart1-gpio {
+						marvell,pins = "mpp_uart1";
+						marvell,function = "gpio";
+					};
+
+					pmx_nand: pmx-nand {
+						marvell,pins = "mpp_nand";
+						marvell,function = "nand";
+					};
+
+					pmx_nand_gpo: pmx-nand-gpo {
+						marvell,pins = "mpp_nand";
+						marvell,function = "gpo";
+					};
+
+					pmx_i2c1: pmx-i2c1 {
+						marvell,pins = "mpp17", "mpp19";
+						marvell,function = "twsi";
+					};
+
+					pmx_i2c2: pmx-i2c2 {
+						marvell,pins = "mpp_audio1";
+						marvell,function = "twsi";
+					};
+
+					pmx_ssp_i2c2: pmx-ssp-i2c2 {
+						marvell,pins = "mpp_audio1";
+						marvell,function = "ssp/twsi";
+					};
+
+					pmx_i2cmux_0: pmx-i2cmux-0 {
+						marvell,pins = "twsi";
+						marvell,function = "twsi-opt1";
+					};
+
+					pmx_i2cmux_1: pmx-i2cmux-1 {
+						marvell,pins = "twsi";
+						marvell,function = "twsi-opt2";
+					};
+
+					pmx_i2cmux_2: pmx-i2cmux-2 {
+						marvell,pins = "twsi";
+						marvell,function = "twsi-opt3";
+					};
+				};
+
+				core_clk: core-clocks@0214 {
+					compatible = "marvell,dove-core-clock";
+					reg = <0x0214 0x4>;
+					#clock-cells = <1>;
+				};
+
+				gpio0: gpio-ctrl@0400 {
+					compatible = "marvell,orion-gpio";
+					#gpio-cells = <2>;
+					gpio-controller;
+					reg = <0x0400 0x20>;
+					ngpios = <32>;
+					interrupt-controller;
+					#interrupt-cells = <2>;
+					interrupt-parent = <&intc>;
+					interrupts = <12>, <13>, <14>, <60>;
+				};
+
+				gpio1: gpio-ctrl@0420 {
+					compatible = "marvell,orion-gpio";
+					#gpio-cells = <2>;
+					gpio-controller;
+					reg = <0x0420 0x20>;
+					ngpios = <32>;
+					interrupt-controller;
+					#interrupt-cells = <2>;
+					interrupt-parent = <&intc>;
+					interrupts = <61>;
+				};
+
+				rtc: real-time-clock@8500 {
+					compatible = "marvell,orion-rtc";
+					reg = <0x8500 0x20>;
+				};
 			};
 
 			gconf: global-config@e802c {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

The PMU device contains an interrupt controller, power control and
resets.  The interrupt controller is a little sub-standard in that
there is no race free way to clear down pending interrupts, so we try
to avoid problems by reducing the window as much as possible, and
clearing as infrequently as possible.

The interrupt support is implemented using an IRQ domain, and the

The power domains and reset support is closely related - there is a
defined sequence for powering down a domain which is tightly coupled
with asserting the reset.  Hence, it makes sense to group these two
together, and in order to avoid any locking contention disrupting this
sequence, we avoid the use of syscon or regmap.

This patch adds the core PMU driver: power domains must be defined in
the DT file in order to make use of them.  The reset controller can
be referenced in the standard way for reset controllers.

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Now that we have a PMU driver, we can wire up the RTC interrupt in the
DT description for Dove.

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/dove.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 9fb2dc77176e..fad121c95487 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -723,6 +723,7 @@
 				rtc: real-time-clock@8500 {
 					compatible = "marvell,orion-rtc";
 					reg = <0x8500 0x20>;
+					interrupts = <5>;
 				};
 			};

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add the description of the video decoder power domain to the PMU DT
entry.

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/dove.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index fad121c95487..aa33e13064db 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -432,6 +432,12 @@
 				#reset-cells = <1>;
 
 				domains {
 
 				thermal: thermal-diode@001c {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:10 +0100

Add the description of the GPU power domain to the PMU DT entry.

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/dove.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index aa33e13064db..179121630ad7 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -438,6 +438,13 @@
 						marvell,pmu_iso_mask = <0x00000001>;
 						resets = <&pmu 16>;
 					};
+
+					gpu_domain: gpu-domain {
+						#power-domain-cells = <0>;
+						marvell,pmu_pwr_mask = <0x00000004>;
+						marvell,pmu_iso_mask = <0x00000002>;
+						resets = <&pmu 18>;
+					};
 				};

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Since Dove has non-DT support, convert the legacy support to use the new
PMU driver.

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/Kconfig                     |   1 +
 arch/arm/mach-dove/common.c          |  25 +++++++++
 arch/arm/mach-dove/include/mach/pm.h |  17 ------
 arch/arm/mach-dove/irq.c             |  87 ------------------------------
 drivers/soc/Makefile                 |   1 +
 drivers/soc/dove/pmu.c               | 100 +++++++++++++++++++++++++++++++++++
 include/linux/soc/dove/pmu.h         |  18 +++++++
 7 files changed, 145 insertions(+), 104 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1c5021002fe4..faf6e4fd31df 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -512,6 +512,7 @@ config ARCH_DOVE
 	select PINCTRL
 	select PINCTRL_DOVE
 	select PLAT_ORION_LEGACY
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for the Marvell Dove SoC 88AP510
 
diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c
index 0d1a89298ece..6f3887217674 100644
--- a/arch/arm/mach-dove/common.c
+++ b/arch/arm/mach-dove/common.c
@@ -16,6 +16,7 @@
 #include <linux/platform_data/dma-mv_xor.h>
 #include <linux/platform_data/usb-ehci-orion.h>
 #include <linux/platform_device.h>
+#include <linux/soc/dove/pmu.h>
 #include <asm/hardware/cache-tauros2.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -375,6 +376,29 @@ void __init dove_setup_cpu_wins(void)
 				    DOVE_SCRATCHPAD_SIZE);
 }
 
+static const struct dove_pmu_domain_initdata pmu_domains[] __initconst = {
+	{
+		.pwr_mask = PMU_PWR_VPU_PWR_DWN_MASK,
+		.rst_mask = PMU_SW_RST_VIDEO_MASK,
+		.iso_mask = PMU_ISO_VIDEO_MASK,
+		.name = "vpu-domain",
+	}, {
+		.pwr_mask = PMU_PWR_GPU_PWR_DWN_MASK,
+		.rst_mask = PMU_SW_RST_GPU_MASK,
+		.iso_mask = PMU_ISO_GPU_MASK,
+		.name = "gpu-domain",
+	}, {
+		/* sentinel */
+	},
+};
+
+static const struct dove_pmu_initdata pmu_data __initconst = {
+	.pmc_base = DOVE_PMU_VIRT_BASE,
+	.pmu_base = DOVE_PMU_VIRT_BASE + 0x8000,
+	.irq = IRQ_DOVE_PMU,
+	.domains = pmu_domains,
+};
+
 void __init dove_init(void)
 {
 	pr_info("Dove 88AP510 SoC, TCLK = %d MHz.\n",
@@ -389,6 +413,7 @@ void __init dove_init(void)
 	dove_clk_init();
 
 	/* internal devices that every board has */
+	dove_init_pmu_legacy(&pmu_data);
 	dove_rtc_init();
 	dove_xor0_init();
 	dove_xor1_init();
diff --git a/arch/arm/mach-dove/include/mach/pm.h b/arch/arm/mach-dove/include/mach/pm.h
index b47f75038686..625a89c15c1f 100644
--- a/arch/arm/mach-dove/include/mach/pm.h
+++ b/arch/arm/mach-dove/include/mach/pm.h
@@ -51,22 +51,5 @@
 #define  CLOCK_GATING_GIGA_PHY_MASK	(1 << CLOCK_GATING_BIT_GIGA_PHY)
 
 #define PMU_INTERRUPT_CAUSE	(DOVE_PMU_VIRT_BASE + 0x50)
-#define PMU_INTERRUPT_MASK	(DOVE_PMU_VIRT_BASE + 0x54)
-
-static inline int pmu_to_irq(int pin)
-{
-	if (pin < NR_PMU_IRQS)
-		return pin + IRQ_DOVE_PMU_START;
-
-	return -EINVAL;
-}
-
-static inline int irq_to_pmu(int irq)
-{
-	if (IRQ_DOVE_PMU_START <= irq && irq < NR_IRQS)
-		return irq - IRQ_DOVE_PMU_START;
-
-	return -EINVAL;
-}
 
 #endif
diff --git a/arch/arm/mach-dove/irq.c b/arch/arm/mach-dove/irq.c
index df0223f76fa9..7e5a3248b82b 100644
--- a/arch/arm/mach-dove/irq.c
+++ b/arch/arm/mach-dove/irq.c
@@ -7,86 +7,14 @@
  * License version 2.  This program is licensed "as is" without any
  * warranty of any kind, whether express or implied.
  */
-
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/irq.h>
-#include <linux/gpio.h>
 #include <linux/io.h>
-#include <asm/mach/arch.h>
 #include <plat/irq.h>
-#include <asm/mach/irq.h>
-#include <mach/pm.h>
 #include <mach/bridge-regs.h>
 #include <plat/orion-gpio.h>
 #include "common.h"
 
-static void pmu_irq_mask(struct irq_data *d)
-{
-	int pin = irq_to_pmu(d->irq);
-	u32 u;
-
-	u = readl(PMU_INTERRUPT_MASK);
-	u &= ~(1 << (pin & 31));
-	writel(u, PMU_INTERRUPT_MASK);
-}
-
-static void pmu_irq_unmask(struct irq_data *d)
-{
-	int pin = irq_to_pmu(d->irq);
-	u32 u;
-
-	u = readl(PMU_INTERRUPT_MASK);
-	u |= 1 << (pin & 31);
-	writel(u, PMU_INTERRUPT_MASK);
-}
-
-static void pmu_irq_ack(struct irq_data *d)
-{
-	int pin = irq_to_pmu(d->irq);
-	u32 u;
-
-	/*
-	 * The PMU mask register is not RW0C: it is RW.  This means that
-	 * the bits take whatever value is written to them; if you write
-	 * a '1', you will set the interrupt.
-	 *
-	 * Unfortunately this means there is NO race free way to clear
-	 * these interrupts.
-	 *
-	 * So, let's structure the code so that the window is as small as
-	 * possible.
-	 */
-	u = ~(1 << (pin & 31));
-	u &= readl_relaxed(PMU_INTERRUPT_CAUSE);
-	writel_relaxed(u, PMU_INTERRUPT_CAUSE);
-}
-
-static struct irq_chip pmu_irq_chip = {
-	.name		= "pmu_irq",
-	.irq_mask	= pmu_irq_mask,
-	.irq_unmask	= pmu_irq_unmask,
-	.irq_ack	= pmu_irq_ack,
-};
-
-static void pmu_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
-	unsigned long cause = readl(PMU_INTERRUPT_CAUSE);
-
-	cause &= readl(PMU_INTERRUPT_MASK);
-	if (cause == 0) {
-		do_bad_IRQ(irq, desc);
-		return;
-	}
-
-	for (irq = 0; irq < NR_PMU_IRQS; irq++) {
-		if (!(cause & (1 << irq)))
-			continue;
-		irq = pmu_to_irq(irq);
-		generic_handle_irq(irq);
-	}
-}
-
 static int __initdata gpio0_irqs[4] = {
 	IRQ_DOVE_GPIO_0_7,
 	IRQ_DOVE_GPIO_8_15,
@@ -142,8 +70,6 @@ __exception_irq_entry dove_legacy_handle_irq(struct pt_regs *regs)
 
 void __init dove_init_irq(void)
 {
-	int i;
-
 	orion_irq_init(1, IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF);
 	orion_irq_init(33, IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF);
 
@@ -162,17 +88,4 @@ void __init dove_init_irq(void)
 
 	orion_gpio_init(NULL, 64, 8, DOVE_GPIO2_VIRT_BASE, 0,
 			IRQ_DOVE_GPIO_START + 64, gpio2_irqs);
-
-	/*
-	 * Mask and clear PMU interrupts
-	 */
-	writel(0, PMU_INTERRUPT_MASK);
-	writel(0, PMU_INTERRUPT_CAUSE);
-
-	for (i = IRQ_DOVE_PMU_START; i < NR_IRQS; i++) {
-		irq_set_chip_and_handler(i, &pmu_irq_chip, handle_level_irq);
-		irq_set_status_flags(i, IRQ_LEVEL);
-		set_irq_flags(i, IRQF_VALID);
-	}
-	irq_set_chained_handler(IRQ_DOVE_PMU, pmu_irq_handler);
 }
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 0b12d777d3c4..c99192f30679 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -2,6 +2,7 @@
 # Makefile for the Linux Kernel SOC specific device drivers.
 #
 
+obj-$(CONFIG_ARCH_DOVE)		+= dove/
 obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-$(CONFIG_ARCH_MEDIATEK)	+= mediatek/
 obj-$(CONFIG_ARCH_QCOM)		+= qcom/
diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c
index 6792aae9e2e5..7072fdbe8f8b 100644
--- a/drivers/soc/dove/pmu.c
+++ b/drivers/soc/dove/pmu.c
@@ -305,6 +305,106 @@ static int __init dove_init_pmu_irq(struct pmu_data *pmu, int irq)
 	return 0;
 }
 
+static void pmu_add_genpd_name(const char *name, struct device *dev)
+{
+	while (1) {
+		if (pm_genpd_name_add_device(name, dev) != -EAGAIN)
+			break;
+		cond_resched();
+	}
+}
+
+static void pmu_remove_genpd(struct device *dev)
+{
+	struct generic_pm_domain *genpd = pm_genpd_lookup_dev(dev);
+
+	if (!genpd)
+		return;
+
+	while (1) {
+		if (pm_genpd_remove_device(genpd, dev) != -EAGAIN)
+			break;
+		cond_resched();
+	}
+}
+
+static int pmu_platform_call(struct notifier_block *nb,
+	unsigned long event, void *data)
+{
+	struct device *dev = data;
+	const char *name = NULL;
+
+	if (dev->of_node)
+		return NOTIFY_OK;
+
+	if (strcmp(dev_name(dev), "ap510-vmeta.0") == 0 ||
+	    strcmp(dev_name(dev), "ap510-vmeta") == 0)
+		name = "vpu-domain";
+	else if (strcmp(dev_name(dev), "galcore.0") == 0)
+		name = "gpu-domain";
+
+	switch (event) {
+	case BUS_NOTIFY_ADD_DEVICE:
+		if (name)
+			pmu_add_genpd_name(name, dev);
+		break;
+
+	case BUS_NOTIFY_DEL_DEVICE:
+		pmu_remove_genpd(dev);
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block platform_nb = {
+	.notifier_call = pmu_platform_call,
+};
+
+int __init dove_init_pmu_legacy(const struct dove_pmu_initdata *initdata)
+{
+	const struct dove_pmu_domain_initdata *domain_initdata;
+	struct pmu_data *pmu;
+	int ret;
+
+	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+	if (!pmu)
+		return -ENOMEM;
+
+	spin_lock_init(&pmu->lock);
+	pmu->pmc_base = initdata->pmc_base;
+	pmu->pmu_base = initdata->pmu_base;
+
+	pmu_reset_init(pmu);
+	for (domain_initdata = initdata->domains; domain_initdata->name;
+	     domain_initdata++) {
+		struct pmu_domain *domain;
+
+		domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+		if (domain) {
+			domain->pmu = pmu;
+			domain->pwr_mask = domain_initdata->pwr_mask;
+			domain->rst_mask = domain_initdata->rst_mask;
+			domain->iso_mask = domain_initdata->iso_mask;
+			domain->base.name = domain_initdata->name;
+
+			__pmu_domain_register(domain, NULL);
+		}
+	}
+	pm_genpd_poweroff_unused();
+
+	ret = dove_init_pmu_irq(pmu, initdata->irq);
+	if (ret)
+		pr_err("dove_init_pmu_irq() failed: %d\n", ret);
+
+	if (pmu->irq_domain)
+		irq_domain_associate_many(pmu->irq_domain, IRQ_DOVE_PMU_START,
+					  0, NR_PMU_IRQS);
+
+	bus_register_notifier(&platform_bus_type, &platform_nb);
+
+	return 0;
+}
+
 /*
  * pmu: power-manager@d0000 {
  *	compatible = "marvell,dove-pmu";
diff --git a/include/linux/soc/dove/pmu.h b/include/linux/soc/dove/pmu.h
index 9c99f84bcc0e..431dfac595e7 100644
--- a/include/linux/soc/dove/pmu.h
+++ b/include/linux/soc/dove/pmu.h
@@ -1,6 +1,24 @@
 #ifndef LINUX_SOC_DOVE_PMU_H
 #define LINUX_SOC_DOVE_PMU_H
 
+#include <linux/types.h>
+
+struct dove_pmu_domain_initdata {
+	u32 pwr_mask;
+	u32 rst_mask;
+	u32 iso_mask;
+	const char *name;
+};
+
+struct dove_pmu_initdata {
+	void __iomem *pmc_base;
+	void __iomem *pmu_base;
+	int irq;
+	const struct dove_pmu_domain_initdata *domains;
+};
+
+int dove_init_pmu_legacy(const struct dove_pmu_initdata *);
+
 int dove_init_pmu(void);
 
 #endif

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

If we fail to allocate an event, we leave the submitted fence number
incremented.  This can cause an already running hangcheck timer to
believe that we should be waiting for further events when no event has
actually been queued.

Resolve this by moving the fence allocation (which can never fail)
after the event allocation.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 85cd7f18950e..4ccd66848241 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -892,13 +892,8 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 {
 	struct drm_device *dev = gpu->drm;
 	struct etnaviv_drm_private *priv = dev->dev_private;
-	int ret = 0;
 	unsigned int event, i;
 
-	submit->fence = ++priv->next_fence;
-
-	gpu->submitted_fence = submit->fence;
-
 	/*
 	 * TODO
 	 *
@@ -911,10 +906,12 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	event = event_alloc(gpu);
 	if (unlikely(event == ~0U)) {
 		DRM_ERROR("no free event\n");
-		ret = -EBUSY;
-		goto fail;
+		return -EBUSY;
 	}
 
+	submit->fence = ++priv->next_fence;
+
+	gpu->submitted_fence = submit->fence;
 	gpu->event[event].fence = submit->fence;
 
 	etnaviv_buffer_queue(gpu, event, submit);
@@ -946,8 +943,7 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	}
 	hangcheck_timer_reset(gpu);
 
-fail:
-	return ret;
+	return 0;
 }
 
 /*

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Remove the etnaviv specific power rail support, which is mostly disabled
anyway.  This really wants to be using the power domain support instead,
which allows the SoC specifics to be abstracted from the driver, rather
than a home-cooked version of it.

For example, on Dove, it is necessary to go through a specific isolation
and reset sequence when removing and restoring the power to the GPU,
which is something we don't want to have to place into the generic GPU
driver.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 47 -----------------------------------
 drivers/staging/etnaviv/etnaviv_gpu.h |  3 ---
 2 files changed, 50 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 4ccd66848241..e0f1eb595660 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -609,45 +609,6 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 /*
  * Power Management:
  */
-
-static int enable_pwrrail(struct etnaviv_gpu *gpu)
-{
-#if 0
-	struct drm_device *dev = gpu->drm;
-	int ret = 0;
-
-	if (gpu->gpu_reg) {
-		ret = regulator_enable(gpu->gpu_reg);
-		if (ret) {
-			dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n",
-				ret);
-			return ret;
-		}
-	}
-
-	if (gpu->gpu_cx) {
-		ret = regulator_enable(gpu->gpu_cx);
-		if (ret) {
-			dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n",
-				ret);
-			return ret;
-		}
-	}
-#endif
-	return 0;
-}
-
-static int disable_pwrrail(struct etnaviv_gpu *gpu)
-{
-#if 0
-	if (gpu->gpu_cx)
-		regulator_disable(gpu->gpu_cx);
-	if (gpu->gpu_reg)
-		regulator_disable(gpu->gpu_reg);
-#endif
-	return 0;
-}
-
 static int enable_clk(struct etnaviv_gpu *gpu)
 {
 	if (gpu->clk_core)
@@ -690,10 +651,6 @@ int etnaviv_gpu_pm_resume(struct etnaviv_gpu *gpu)

-	ret = enable_pwrrail(gpu);
-	if (ret)
-		return ret;
-
 	ret = enable_clk(gpu);
 	if (ret)
 		return ret;
@@ -719,10 +676,6 @@ int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu)
 	if (ret)
 		return ret;
 
-	ret = disable_pwrrail(gpu);
-	if (ret)
-		return ret;
-
 	return 0;
 }
 
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 6dedc41f7e59..020712089b7d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -113,9 +113,6 @@ struct etnaviv_gpu {
 	struct etnaviv_iommu *mmu;
 
 	/* Power Control: */
-#if 0
-	struct regulator *gpu_reg, *gpu_cx;
-#endif
 	struct clk *clk_bus;
 	struct clk *clk_core;
 	struct clk *clk_shader;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

The etnaviv_gpu structure can have a longer lifetime than the GPU
command buffer, MMU and drm_device structures.  When these other
structures are freed (via the unbind method) we may be tempted to
access these via other functions after they've been freed.  Leaving
pointers in them invites undetected use-after-free events.  This
has happened while trying to develop runtime PM for the GPU.

Ensure that these bugs are obvious by NULLing out the pointers at
the end of their lifetime.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index e0f1eb595660..d3625ee7a859 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -991,11 +991,17 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,

-	if (gpu->buffer)
+	if (gpu->buffer) {
 		drm_gem_object_unreference_unlocked(gpu->buffer);
+		gpu->buffer = NULL;
+	}
 
-	if (gpu->mmu)
+	if (gpu->mmu) {
+		gpu->mmu = NULL;
+	}
+
+	gpu->drm = NULL;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Hold the mutex while calling component_{un,}bind_all() so that
components can perform initialisation in their bind and unbind
callbacks from the component helper.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 80b3c667be71..158cb3834a7b 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -99,10 +99,11 @@ static int etnaviv_unload(struct drm_device *dev)
 		if (g)
 			etnaviv_gpu_pm_suspend(g);
 	}
-	mutex_unlock(&dev->struct_mutex);
 
 	component_unbind_all(dev->dev, dev);
 
 	dev->dev_private = NULL;
 
 	kfree(priv);
@@ -116,8 +117,6 @@ static void load_gpu(struct drm_device *dev)
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	unsigned int i;

@@ -132,8 +131,6 @@ static void load_gpu(struct drm_device *dev)
 			}
 		}
 	}
-
-	mutex_unlock(&dev->struct_mutex);
 }
 
 static int etnaviv_load(struct drm_device *dev, unsigned long flags)
@@ -157,12 +154,16 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)
 
 	platform_set_drvdata(pdev, dev);
 
+	mutex_lock(&dev->struct_mutex);
+
 	err = component_bind_all(dev->dev, dev);
 	if (err < 0)
 		return err;
 
 	load_gpu(dev);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Power management of each GPU core should be a matter for the driver of
each GPU core itself.  Move the basic power management calls out of
etnaviv_drv.c and into etnaviv_gpu.c so that we can add runtime PM.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 10 -----
 drivers/staging/etnaviv/etnaviv_gpu.c | 73 +++++++++++++++++++----------------
 drivers/staging/etnaviv/etnaviv_gpu.h |  2 -
 3 files changed, 39 insertions(+), 46 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 158cb3834a7b..1dc8d46ad66b 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -87,21 +87,12 @@ u32 etnaviv_readl(const void __iomem *addr)
 static int etnaviv_unload(struct drm_device *dev)
 {
 	struct etnaviv_drm_private *priv = dev->dev_private;
-	unsigned int i;
 
 	flush_workqueue(priv->wq);
 	destroy_workqueue(priv->wq);
 
 	mutex_lock(&dev->struct_mutex);
-	for (i = 0; i < ETNA_MAX_PIPES; i++) {
-		struct etnaviv_gpu *g = priv->gpu[i];
-
-		if (g)
-			etnaviv_gpu_pm_suspend(g);
-	}
-
 	component_unbind_all(dev->dev, dev);
-
 	mutex_unlock(&dev->struct_mutex);
 
 	dev->dev_private = NULL;
@@ -123,7 +114,6 @@ static void load_gpu(struct drm_device *dev)
 		if (g) {
 			int ret;
 
-			etnaviv_gpu_pm_resume(g);
 			ret = etnaviv_gpu_init(g);
 			if (ret) {
 				dev_err(g->dev, "hw init failed: %d\n", ret);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index d3625ee7a859..126d833982e7 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -645,40 +645,6 @@ static int disable_axi(struct etnaviv_gpu *gpu)
 	return 0;
 }
 
-int etnaviv_gpu_pm_resume(struct etnaviv_gpu *gpu)
-{
-	int ret;
-
-	DBG("%s", dev_name(gpu->dev));
-
-	ret = enable_clk(gpu);
-	if (ret)
-		return ret;
-
-	ret = enable_axi(gpu);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
-int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu)
-{
-	int ret;
-
-	DBG("%s", dev_name(gpu->dev));
-
-	ret = disable_axi(gpu);
-	if (ret)
-		return ret;
-
-	ret = disable_clk(gpu);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
 /*
  * Hangcheck detection for locked gpu:
  */
@@ -947,6 +913,38 @@ static irqreturn_t irq_handler(int irq, void *data)
 	return ret;
 }
 
+static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu)
+{
+	int ret;
+
+	ret = enable_clk(gpu);
+	if (ret)
+		return ret;
+
+	ret = enable_axi(gpu);
+	if (ret) {
+		disable_clk(gpu);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu)
+{
+	int ret;
+
+	ret = disable_axi(gpu);
+	if (ret)
+		return ret;
+
+	ret = disable_clk(gpu);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	void *data)
 {
@@ -954,6 +952,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	struct etnaviv_drm_private *priv = drm->dev_private;
 	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
 	int idx = gpu->pipe;
+	int ret;
 
 	dev_info(dev, "pre gpu[idx]: %p\n", priv->gpu[idx]);
 
@@ -967,6 +966,10 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,

+	ret = etnaviv_gpu_clk_enable(gpu);
+	if (ret < 0)
+		return ret;
+
 	gpu->drm = drm;
 
 	INIT_LIST_HEAD(&gpu->active_list);
@@ -991,6 +994,8 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,

+	etnaviv_gpu_clk_disable(gpu);
+
 	if (gpu->buffer) {
 		drm_gem_object_unreference_unlocked(gpu->buffer);
 		gpu->buffer = NULL;
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 020712089b7d..9035d9c78475 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -140,8 +140,6 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
 	uint64_t *value);
 
 int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
-int etnaviv_gpu_pm_suspend(struct etnaviv_gpu *gpu);
-int etnaviv_gpu_pm_resume(struct etnaviv_gpu *gpu);
 
 #ifdef CONFIG_DEBUG_FS
 void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

We need to reprogram various registers when coming out of runtime PM,
many of which are those which are setup by the main initialisation.
Abstract this code out and arrange for the runtime PM resume method
to call it.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 53 ++++++++++++++++++++---------------
 1 file changed, 31 insertions(+), 22 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 126d833982e7..3b1ec50050e6 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -401,18 +401,9 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 	return 0;
 }
 
-int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
+static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 {
-	int ret, i;
 	u32 words; /* 32 bit words */
-	struct iommu_domain *iommu;
-	enum etnaviv_iommu_version version;
-	bool mmuv2;
-
-	etnaviv_hw_identify(gpu);
-	ret = etnaviv_hw_reset(gpu);
-	if (ret)
-		return ret;
 
 	if (gpu->identity.model == chipModel_GC320 &&
 	    gpu_read(gpu, VIVS_HI_CHIP_TIME) != 0x2062400 &&
@@ -445,6 +436,34 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, 0x0);
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, 0x0);
 
+	/* FIXME: we need to program the GPU table pointer(s) here */
+
 	/* Setup IOMMU.. eventually we will (I think) do this once per context
 	 * and have separate page tables per context.  For now, to keep things
@@ -491,18 +510,8 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 
-	/* Start command processor */
-	words = etnaviv_buffer_init(gpu);
-
-	/* convert number of 32 bit words to number of 64 bit words */
-	words = ALIGN(words, 2) / 2;
-
-	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
-	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
-		  etnaviv_gem_paddr_locked(gpu->buffer));
-	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
-		  VIVS_FE_COMMAND_CONTROL_ENABLE |
-		  VIVS_FE_COMMAND_CONTROL_PREFETCH(words));
+	/* Now program the hardware */
+	etnaviv_gpu_hw_init(gpu);
 
 	return 0;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

There is no way 'priv' can be NULL in tda998x_irq_thread() - this can
only happen if request_threaded_irq() was passed a NULL priv pointer,
and we would have crashed long before then if that was the case.

We also always ensure that priv->encoder is correctly setup, which
must have been initialised prior to the interrupt being claimed, so we
can remove this check as well.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 424228be79ae..d8e97085f866 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -558,7 +558,7 @@ static void tda998x_hpd(struct work_struct *work)
 	struct tda998x_priv *priv =
 			container_of(dwork, struct tda998x_priv, dwork);
 
-	if (priv->encoder && priv->encoder->dev)
+	if (priv->encoder->dev)
 		drm_kms_helper_hotplug_event(priv->encoder->dev);
 }
 
@@ -570,8 +570,6 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 	struct tda998x_priv *priv = data;
 	u8 sta, cec, lvl, flag0, flag1, flag2;
 
-	if (!priv)
-		return IRQ_HANDLED;
 	sta = cec_read(priv, REG_CEC_INTSTATUS);
 	cec = cec_read(priv, REG_CEC_RXSHPDINT);
 	lvl = cec_read(priv, REG_CEC_RXSHPDLEV);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Rather than always reporting that the interrupt was handled, we should
report whether we did handle the interrupt.  Arrange to report IRQ_NONE
for cases where we found nothing to do.

This allows us to (eventually) recover from stuck-IRQ problems, rather
than causing the kernel to solidly lock up.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index d8e97085f866..ad3ce3479edf 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -569,6 +569,7 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 {
 	struct tda998x_priv *priv = data;
 	u8 sta, cec, lvl, flag0, flag1, flag2;
+	bool handled = false;
 
 	sta = cec_read(priv, REG_CEC_INTSTATUS);
 	cec = cec_read(priv, REG_CEC_RXSHPDINT);
@@ -582,10 +583,12 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 	if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
 		priv->wq_edid_wait = 0;
 		wake_up(&priv->wq_edid);
+		handled = true;
 	} else if (cec != 0) {			/* HPD change */
 		schedule_delayed_work(&priv->dwork, HZ/10);
+		handled = true;
 	}
-	return IRQ_HANDLED;
+	return IRQ_RETVAL(handled);
 }
 
 static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Commit 6833d26ef823 ("drm: tda998x: Fix EDID read timeout on HDMI
connect") used a weak scheme to try and delay reading EDID on a HDMI
connect event.  It is weak because delaying the notification of a
hotplug event does not stop userspace from trying to read the EDID
within the 100ms delay.

The solution provided here solves this issue:
* When a HDMI connection event is detected, mark a blocking flag for
  EDID reads, and start a timer for the delay.
* If an EDID read is attempted, and the blocking flag is set, wait
  for the blocking flag to clear.
* When the timer expires, clear the blocking flag and wake any thread
  waiting for the EDID read.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 80 +++++++++++++++++++++++++++++++++------
 1 file changed, 68 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index ad3ce3479edf..a53696fd8938 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -34,7 +34,6 @@ struct tda998x_priv {
 	struct i2c_client *cec;
 	struct i2c_client *hdmi;
 	struct mutex mutex;
-	struct delayed_work dwork;
 	uint16_t rev;
 	uint8_t current_page;
 	int dpms;
@@ -47,6 +46,11 @@ struct tda998x_priv {
 	wait_queue_head_t wq_edid;
 	volatile int wq_edid_wait;
 	struct drm_encoder *encoder;
+
+	struct work_struct detect_work;
+	struct timer_list edid_delay_timer;
+	wait_queue_head_t edid_delay_waitq;
+	bool edid_delay_active;
 };
 
 #define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
@@ -551,15 +555,50 @@ tda998x_reset(struct tda998x_priv *priv)
 	reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24);
 }
 
-/* handle HDMI connect/disconnect */
-static void tda998x_hpd(struct work_struct *work)
+/*
+ * The TDA998x has a problem when trying to read the EDID close to a
+ * HPD assertion: it needs a delay of 100ms to avoid timing out while
+ * trying to read EDID data.
+ *
+ * However, tda998x_encoder_get_modes() may be called at any moment
+ * after tda998x_encoder_detect() indicates that we are connected, so
+ * we need to delay probing modes in tda998x_encoder_get_modes() after
+ * we have seen a HPD inactive->active transition.  This code implements
+ * that delay.
+ */
+static void tda998x_edid_delay_done(unsigned long data)
+{
+	struct tda998x_priv *priv = (struct tda998x_priv *)data;
+
+	priv->edid_delay_active = false;
+	wake_up(&priv->edid_delay_waitq);
+	schedule_work(&priv->detect_work);
+}
+
+static void tda998x_edid_delay_start(struct tda998x_priv *priv)
+{
+	priv->edid_delay_active = true;
+	mod_timer(&priv->edid_delay_timer, jiffies + HZ/10);
+}
+
+static int tda998x_edid_delay_wait(struct tda998x_priv *priv)
+{
+	return wait_event_killable(priv->edid_delay_waitq, !priv->edid_delay_active);
+}
+
+/*
+ * We need to run the KMS hotplug event helper outside of our threaded
+ * interrupt routine as this can call back into our get_modes method,
+ * which will want to make use of interrupts.
+ */
+static void tda998x_detect_work(struct work_struct *work)
 {
-	struct delayed_work *dwork = to_delayed_work(work);
 	struct tda998x_priv *priv =
-			container_of(dwork, struct tda998x_priv, dwork);
+		container_of(work, struct tda998x_priv, detect_work);
+	struct drm_device *dev = priv->encoder->dev;
 
-	if (priv->encoder->dev)
-		drm_kms_helper_hotplug_event(priv->encoder->dev);
+	if (dev)
+		drm_kms_helper_hotplug_event(dev);
 }
 
 /*
@@ -585,7 +624,11 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 		wake_up(&priv->wq_edid);
 		handled = true;
 	} else if (cec != 0) {			/* HPD change */
-		schedule_delayed_work(&priv->dwork, HZ/10);
+		if (lvl & CEC_RXSHPDLEV_HPD)
+			tda998x_edid_delay_start(priv);
+		else
+			schedule_work(&priv->detect_work);
+
 		handled = true;
 	}
 	return IRQ_RETVAL(handled);
@@ -1103,6 +1146,14 @@ tda998x_encoder_get_modes(struct tda998x_priv *priv,
 	struct edid *edid;
 	int n;
 
+	/*
+	 * If we get killed while waiting for the HPD timeout, return
+	 * no modes found: we are not in a restartable path, so we
+	 * can't handle signals gracefully.
+	 */
+	if (tda998x_edid_delay_wait(priv))
+		return 0;
+
 	if (priv->rev == TDA19988)
 		reg_clear(priv, REG_TX4, TX4_PD_RAM);
 
@@ -1149,10 +1200,12 @@ static void tda998x_destroy(struct tda998x_priv *priv)
 	/* disable all IRQs and free the IRQ handler */
 	cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
 	reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
-	if (priv->hdmi->irq) {
+
+	if (priv->hdmi->irq)
 		free_irq(priv->hdmi->irq, priv);
-		cancel_delayed_work_sync(&priv->dwork);
-	}
+
+	del_timer_sync(&priv->edid_delay_timer);
+	cancel_work_sync(&priv->detect_work);
 
 	i2c_unregister_device(priv->cec);
 }
@@ -1253,6 +1306,10 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	priv->dpms = DRM_MODE_DPMS_OFF;
 
 	mutex_init(&priv->mutex);	/* protect the page access */
+	init_waitqueue_head(&priv->edid_delay_waitq);
+	setup_timer(&priv->edid_delay_timer, tda998x_edid_delay_done,
+		    (unsigned long)priv);
+	INIT_WORK(&priv->detect_work, tda998x_detect_work);
 
 	/* wake up the device: */
 	cec_write(priv, REG_CEC_ENAMODS,
@@ -1311,7 +1368,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 
 		/* init read EDID waitqueue and HDP work */
 		init_waitqueue_head(&priv->wq_edid);
-		INIT_DELAYED_WORK(&priv->dwork, tda998x_hpd);
 
 		/* clear pending interrupts */
 		reg_read(priv, REG_INT_FLAGS_0);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

C99 types are against the style of the Linux kernel.  Convert to using
Linus-friendly types.  See https://lwn.net/Articles/113367/ for more
information.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 74 +++++++++++++++++++--------------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index a53696fd8938..6d6aaadc0d2f 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -34,8 +34,8 @@ struct tda998x_priv {
 	struct i2c_client *cec;
 	struct i2c_client *hdmi;
 	struct mutex mutex;
-	uint16_t rev;
-	uint8_t current_page;
+	u16 rev;
+	u8 current_page;
 	int dpms;
 	bool is_hdmi_sink;
 	u8 vip_cntrl_0;
@@ -349,10 +349,10 @@ struct tda998x_priv {
 #define TDA19988                  0x0301
 
 static void
-cec_write(struct tda998x_priv *priv, uint16_t addr, uint8_t val)
+cec_write(struct tda998x_priv *priv, u16 addr, u8 val)
 {
 	struct i2c_client *client = priv->cec;
-	uint8_t buf[] = {addr, val};
+	u8 buf[] = {addr, val};
 	int ret;
 
 	ret = i2c_master_send(client, buf, sizeof(buf));
@@ -360,11 +360,11 @@ cec_write(struct tda998x_priv *priv, uint16_t addr, uint8_t val)
 		dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
 }
 
-static uint8_t
-cec_read(struct tda998x_priv *priv, uint8_t addr)
+static u8
+cec_read(struct tda998x_priv *priv, u8 addr)
 {
 	struct i2c_client *client = priv->cec;
-	uint8_t val;
+	u8 val;
 	int ret;
 
 	ret = i2c_master_send(client, &addr, sizeof(addr));
@@ -383,11 +383,11 @@ cec_read(struct tda998x_priv *priv, uint8_t addr)
 }
 
 static int
-set_page(struct tda998x_priv *priv, uint16_t reg)
+set_page(struct tda998x_priv *priv, u16 reg)
 {
 	if (REG2PAGE(reg) != priv->current_page) {
 		struct i2c_client *client = priv->hdmi;
-		uint8_t buf[] = {
+		u8 buf[] = {
 				REG_CURPAGE, REG2PAGE(reg)
 		};
 		int ret = i2c_master_send(client, buf, sizeof(buf));
@@ -403,10 +403,10 @@ set_page(struct tda998x_priv *priv, uint16_t reg)
 }
 
 static int
-reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt)
+reg_read_range(struct tda998x_priv *priv, u16 reg, char *buf, int cnt)
 {
 	struct i2c_client *client = priv->hdmi;
-	uint8_t addr = REG2ADDR(reg);
+	u8 addr = REG2ADDR(reg);
 	int ret;
 
 	mutex_lock(&priv->mutex);
@@ -432,10 +432,10 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt)
 }
 
 static void
-reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt)
+reg_write_range(struct tda998x_priv *priv, u16 reg, u8 *p, int cnt)
 {
 	struct i2c_client *client = priv->hdmi;
-	uint8_t buf[cnt+1];
+	u8 buf[cnt+1];
 	int ret;
 
 	buf[0] = REG2ADDR(reg);
@@ -454,9 +454,9 @@ reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt)
 }
 
 static int
-reg_read(struct tda998x_priv *priv, uint16_t reg)
+reg_read(struct tda998x_priv *priv, u16 reg)
 {
-	uint8_t val = 0;
+	u8 val = 0;
 	int ret;
 
 	ret = reg_read_range(priv, reg, &val, sizeof(val));
@@ -466,10 +466,10 @@ reg_read(struct tda998x_priv *priv, uint16_t reg)
 }
 
 static void
-reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
+reg_write(struct tda998x_priv *priv, u16 reg, u8 val)
 {
 	struct i2c_client *client = priv->hdmi;
-	uint8_t buf[] = {REG2ADDR(reg), val};
+	u8 buf[] = {REG2ADDR(reg), val};
 	int ret;
 
 	mutex_lock(&priv->mutex);
@@ -485,10 +485,10 @@ reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
 }
 
 static void
-reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val)
+reg_write16(struct tda998x_priv *priv, u16 reg, u16 val)
 {
 	struct i2c_client *client = priv->hdmi;
-	uint8_t buf[] = {REG2ADDR(reg), val >> 8, val};
+	u8 buf[] = {REG2ADDR(reg), val >> 8, val};
 	int ret;
 
 	mutex_lock(&priv->mutex);
@@ -504,7 +504,7 @@ reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val)
 }
 
 static void
-reg_set(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
+reg_set(struct tda998x_priv *priv, u16 reg, u8 val)
 {
 	int old_val;
 
@@ -514,7 +514,7 @@ reg_set(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
 }
 
 static void
-reg_clear(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
+reg_clear(struct tda998x_priv *priv, u16 reg, u8 val)
 {
 	int old_val;
 
@@ -634,7 +634,7 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 	return IRQ_RETVAL(handled);
 }
 
-static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
+static u8 tda998x_cksum(u8 *buf, size_t bytes)
 {
 	int sum = 0;
 
@@ -647,8 +647,8 @@ static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
 #define PB(x) (HB(2) + 1 + (x))
 
 static void
-tda998x_write_if(struct tda998x_priv *priv, uint8_t bit, uint16_t addr,
-		 uint8_t *buf, size_t size)
+tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr,
+		 u8 *buf, size_t size)
 {
 	reg_clear(priv, REG_DIP_IF_FLAGS, bit);
 	reg_write_range(priv, addr, buf, size);
@@ -711,8 +711,8 @@ static void
 tda998x_configure_audio(struct tda998x_priv *priv,
 		struct drm_display_mode *mode, struct tda998x_encoder_params *p)
 {
-	uint8_t buf[6], clksel_aip, clksel_fs, cts_n, adiv;
-	uint32_t n;
+	u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
+	u32 n;
 
 	/* Enable audio ports */
 	reg_write(priv, REG_ENA_AP, p->audio_cfg);
@@ -888,14 +888,14 @@ tda998x_encoder_mode_set(struct tda998x_priv *priv,
 			 struct drm_display_mode *mode,
 			 struct drm_display_mode *adjusted_mode)
 {
-	uint16_t ref_pix, ref_line, n_pix, n_line;
-	uint16_t hs_pix_s, hs_pix_e;
-	uint16_t vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
-	uint16_t vs2_pix_s, vs2_pix_e, vs2_line_s, vs2_line_e;
-	uint16_t vwin1_line_s, vwin1_line_e;
-	uint16_t vwin2_line_s, vwin2_line_e;
-	uint16_t de_pix_s, de_pix_e;
-	uint8_t reg, div, rep;
+	u16 ref_pix, ref_line, n_pix, n_line;
+	u16 hs_pix_s, hs_pix_e;
+	u16 vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
+	u16 vs2_pix_s, vs2_pix_e, vs2_line_s, vs2_line_e;
+	u16 vwin1_line_s, vwin1_line_e;
+	u16 vwin2_line_s, vwin2_line_e;
+	u16 de_pix_s, de_pix_e;
+	u8 reg, div, rep;
 
 	/*
 	 * Internally TDA998x is using ITU-R BT.656 style sync but
@@ -1077,7 +1077,7 @@ tda998x_encoder_mode_set(struct tda998x_priv *priv,
 static enum drm_connector_status
 tda998x_encoder_detect(struct tda998x_priv *priv)
 {
-	uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV);
+	u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
 
 	return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
 			connector_status_disconnected;
@@ -1086,7 +1086,7 @@ tda998x_encoder_detect(struct tda998x_priv *priv)
 static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
 {
 	struct tda998x_priv *priv = data;
-	uint8_t offset, segptr;
+	u8 offset, segptr;
 	int ret, i;
 
 	offset = (blk & 1) ? 128 : 0;
@@ -1558,7 +1558,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	struct i2c_client *client = to_i2c_client(dev);
 	struct drm_device *drm = data;
 	struct tda998x_priv2 *priv;
-	uint32_t crtcs = 0;
+	u32 crtcs = 0;
 	int ret;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

As reading the interrupt registers clears the outstanding interrupts, we
must process all received interrupts to avoid dropping any.  Rearrange
the code to achieve this, and properly check for a HPD interrupt from
the CEC_RXSHPDINT register.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 6d6aaadc0d2f..1285fb354813 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -330,6 +330,8 @@ struct tda998x_priv {
 # define CEC_FRO_IM_CLK_CTRL_FRO_DIV   (1 << 0)
 #define REG_CEC_RXSHPDINTENA	  0xfc		      /* read/write */
 #define REG_CEC_RXSHPDINT	  0xfd		      /* read */
+# define CEC_RXSHPDINT_RXSENS     BIT(0)
+# define CEC_RXSHPDINT_HPD        BIT(1)
 #define REG_CEC_RXSHPDLEV         0xfe                /* read */
 # define CEC_RXSHPDLEV_RXSENS     (1 << 0)
 # define CEC_RXSHPDLEV_HPD        (1 << 1)
@@ -619,11 +621,8 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 	DRM_DEBUG_DRIVER(
 		"tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n",
 		sta, cec, lvl, flag0, flag1, flag2);
-	if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
-		priv->wq_edid_wait = 0;
-		wake_up(&priv->wq_edid);
-		handled = true;
-	} else if (cec != 0) {			/* HPD change */
+
+	if (cec & CEC_RXSHPDINT_HPD) {
 		if (lvl & CEC_RXSHPDLEV_HPD)
 			tda998x_edid_delay_start(priv);
 		else
@@ -631,6 +630,13 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 
 		handled = true;
 	}
+
+	if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
+		priv->wq_edid_wait = 0;
+		wake_up(&priv->wq_edid);
+		handled = true;
+	}
+
 	return IRQ_RETVAL(handled);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 71 +++++++++++++++++----------------------
 1 file changed, 31 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 1285fb354813..1c6fc245514f 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -640,66 +640,57 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 	return IRQ_RETVAL(handled);
 }
 
-static u8 tda998x_cksum(u8 *buf, size_t bytes)
-{
-	int sum = 0;
-
-	while (bytes--)
-		sum -= *buf++;
-	return sum;
-}
-
-#define HB(x) (x)
-#define PB(x) (HB(2) + 1 + (x))
-
 static void
 tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr,
-		 u8 *buf, size_t size)
+		 union hdmi_infoframe *frame)
 {
+	u8 buf[32];
+	ssize_t len;
+
+	len = hdmi_infoframe_pack(frame, buf, sizeof(buf));
+	if (len < 0) {
+		dev_err(&priv->hdmi->dev,
+			"hdmi_infoframe_pack() type=0x%02x failed: %zd\n",
+			frame->any.type, len);
+		return;
+	}
+
 	reg_clear(priv, REG_DIP_IF_FLAGS, bit);
-	reg_write_range(priv, addr, buf, size);
+	reg_write_range(priv, addr, buf, len);
 	reg_set(priv, REG_DIP_IF_FLAGS, bit);
 }
 
 static void
 tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p)
 {
-	u8 buf[PB(HDMI_AUDIO_INFOFRAME_SIZE) + 1];
+	union hdmi_infoframe frame;
+
+	hdmi_audio_infoframe_init(&frame.audio);
 
-	memset(buf, 0, sizeof(buf));
-	buf[HB(0)] = HDMI_INFOFRAME_TYPE_AUDIO;
-	buf[HB(1)] = 0x01;
-	buf[HB(2)] = HDMI_AUDIO_INFOFRAME_SIZE;
-	buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */
-	buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */
-	buf[PB(4)] = p->audio_frame[4];
-	buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */
+	frame.audio.channels = p->audio_frame[1] & 0x07;
+	frame.audio.channel_allocation = p->audio_frame[4];
+	frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3;
+	frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7;
 
-	buf[PB(0)] = tda998x_cksum(buf, sizeof(buf));
+	/*
+	 * L-PCM and IEC61937 compressed audio shall always set sample
+	 * frequency to "refer to stream".  For others, see the HDMI
+	 * specification.
+	 */
+	frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2;
 
-	tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf,
-			 sizeof(buf));
+	tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame);
 }
 
 static void
 tda998x_write_avi(struct tda998x_priv *priv, struct drm_display_mode *mode)
 {
-	struct hdmi_avi_infoframe frame;
-	u8 buf[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
-	ssize_t len;
-
-	drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+	union hdmi_infoframe frame;
 
-	frame.quantization_range = HDMI_QUANTIZATION_RANGE_FULL;
-
-	len = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf));
-	if (len < 0) {
-		dev_err(&priv->hdmi->dev,
-			"hdmi_avi_infoframe_pack() failed: %zd\n", len);
-		return;
-	}
+	drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
+	frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_FULL;
 
-	tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf, len);
+	tda998x_write_if(priv, DIP_IF_FLAGS_IF2, REG_IF2_HB0, &frame);
 }
 
 static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Now that the transition of TDA998x to the component helpers is complete,
remove the non-componentised support from the Armada DRM driver.  All
outputs are expected to use the component helpers from now on.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/Kconfig         |   9 ---
 drivers/gpu/drm/armada/Makefile        |   3 +-
 drivers/gpu/drm/armada/armada_crtc.c   |   2 +-
 drivers/gpu/drm/armada/armada_crtc.h   |   4 -
 drivers/gpu/drm/armada/armada_drv.c    | 125 ++++-------------------------
 drivers/gpu/drm/armada/armada_output.c | 142 ---------------------------------
 drivers/gpu/drm/armada/armada_output.h |  33 --------
 drivers/gpu/drm/armada/armada_slave.c  | 139 --------------------------------
 drivers/gpu/drm/armada/armada_slave.h  |  26 ------
 9 files changed, 19 insertions(+), 464 deletions(-)
 delete mode 100644 drivers/gpu/drm/armada/armada_output.c
 delete mode 100644 drivers/gpu/drm/armada/armada_output.h
 delete mode 100644 drivers/gpu/drm/armada/armada_slave.c
 delete mode 100644 drivers/gpu/drm/armada/armada_slave.h

diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
index 50ae88ad4d76..eb773e9af313 100644
--- a/drivers/gpu/drm/armada/Kconfig
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -14,12 +14,3 @@ config DRM_ARMADA
 	  This driver provides no built-in acceleration; acceleration is
 	  performed by other IP found on the SoC.  This driver provides
 	  kernel mode setting and buffer management to userspace.
-
-config DRM_ARMADA_TDA1998X
-	bool "Support TDA1998X HDMI output"
-	depends on DRM_ARMADA != n
-	depends on I2C && DRM_I2C_NXP_TDA998X = y
-	default y
-	help
-	  Support the TDA1998x HDMI output device found on the Solid-Run
-	  CuBox.
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
index d6f43e06150a..ffd673615772 100644
--- a/drivers/gpu/drm/armada/Makefile
+++ b/drivers/gpu/drm/armada/Makefile
@@ -1,6 +1,5 @@
 armada-y	:= armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
-		   armada_gem.o armada_output.o armada_overlay.o \
-		   armada_slave.o
+		   armada_gem.o armada_overlay.o
 armada-y	+= armada_510.o
 armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
 
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 01ffe9bffe38..c7374a3ec67a 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -1044,7 +1044,7 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
 	return 0;
 }
 
-int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
+static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	struct resource *res, int irq, const struct armada_variant *variant,
 	struct device_node *port)
 {
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 98102a5a9af5..a13469f5d72b 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -75,10 +75,6 @@ struct armada_crtc {
 };
 #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
 
-struct device_node;
-int armada_drm_crtc_create(struct drm_device *, struct device *,
-	struct resource *, int, const struct armada_variant *,
-	struct device_node *);
 void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
 void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
 void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 225034b74cda..b373cf9b2f65 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -18,47 +18,6 @@
 #include <drm/armada_drm.h>
 #include "armada_ioctlP.h"
 
-#ifdef CONFIG_DRM_ARMADA_TDA1998X
-#include <drm/i2c/tda998x.h>
-#include "armada_slave.h"
-
-static struct tda998x_encoder_params params = {
-	/* With 0x24, there is no translation between vp_out and int_vp
-	FB	LCD out	Pins	VIP	Int Vp
-	R:23:16	R:7:0	VPC7:0	7:0	7:0[R]
-	G:15:8	G:15:8	VPB7:0	23:16	23:16[G]
-	B:7:0	B:23:16	VPA7:0	15:8	15:8[B]
-	*/
-	.swap_a = 2,
-	.swap_b = 3,
-	.swap_c = 4,
-	.swap_d = 5,
-	.swap_e = 0,
-	.swap_f = 1,
-	.audio_cfg = BIT(2),
-	.audio_frame[1] = 1,
-	.audio_format = AFMT_SPDIF,
-	.audio_sample_rate = 44100,
-};
-
-static const struct armada_drm_slave_config tda19988_config = {
-	.i2c_adapter_id = 0,
-	.crtcs = 1 << 0, /* Only LCD0 at the moment */
-	.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
-	.interlace_allowed = true,
-	.info = {
-		.type = "tda998x",
-		.addr = 0x70,
-		.platform_data = &params,
-	},
-};
-#endif
-
-static bool is_componentized(struct device *dev)
-{
-	return dev->of_node || dev->platform_data;
-}
-
 static void armada_drm_unref_work(struct work_struct *work)
 {
 	struct armada_private *priv =
@@ -91,16 +50,11 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
 
 static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 {
-	const struct platform_device_id *id;
-	const struct armada_variant *variant;
 	struct armada_private *priv;
-	struct resource *res[ARRAY_SIZE(priv->dcrtc)];
 	struct resource *mem = NULL;
-	int ret, n, i;
+	int ret, n;
 
-	memset(res, 0, sizeof(res));
-
-	for (n = i = 0; ; n++) {
+	for (n = 0; ; n++) {
 		struct resource *r = platform_get_resource(dev->platformdev,
 							   IORESOURCE_MEM, n);
 		if (!r)
@@ -109,8 +63,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 		/* Resources above 64K are graphics memory */
 		if (resource_size(r) > SZ_64K)
 			mem = r;
-		else if (i < ARRAY_SIZE(priv->dcrtc))
-			res[i++] = r;
 		else
 			return -EINVAL;
 	}
@@ -131,13 +83,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 	platform_set_drvdata(dev->platformdev, dev);
 	dev->dev_private = priv;
 
-	/* Get the implementation specific driver data. */
-	id = platform_get_device_id(dev->platformdev);
-	if (!id)
-		return -ENXIO;
-
-	variant = (const struct armada_variant *)id->driver_data;
-
 	INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
 	INIT_KFIFO(priv->fb_unref);
 
@@ -157,34 +102,9 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 	dev->mode_config.funcs = &armada_drm_mode_config_funcs;
 	drm_mm_init(&priv->linear, mem->start, resource_size(mem));
 
-	/* Create all LCD controllers */
-	for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
-		int irq;
-
-		if (!res[n])
-			break;
-
-		irq = platform_get_irq(dev->platformdev, n);
-		if (irq < 0)
-			goto err_kms;
-
-		ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq,
-					     variant, NULL);
-		if (ret)
-			goto err_kms;
-	}
-
-	if (is_componentized(dev->dev)) {
-		ret = component_bind_all(dev->dev, dev);
-		if (ret)
-			goto err_kms;
-	} else {
-#ifdef CONFIG_DRM_ARMADA_TDA1998X
-		ret = armada_drm_connector_slave_create(dev, &tda19988_config);
-		if (ret)
-			goto err_kms;
-#endif
-	}
+	ret = component_bind_all(dev->dev, dev);
+	if (ret)
+		goto err_kms;
 
 	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
 	if (ret)
@@ -202,8 +122,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 	return 0;
 
  err_comp:
-	if (is_componentized(dev->dev))
-		component_unbind_all(dev->dev, dev);
+	component_unbind_all(dev->dev, dev);
  err_kms:
 	drm_mode_config_cleanup(dev);
 	drm_mm_takedown(&priv->linear);
@@ -219,8 +138,7 @@ static int armada_drm_unload(struct drm_device *dev)
 	drm_kms_helper_poll_fini(dev);
 	armada_fbdev_fini(dev);
 
-	if (is_componentized(dev->dev))
-		component_unbind_all(dev->dev, dev);
+	component_unbind_all(dev->dev, dev);
 
 	drm_mode_config_cleanup(dev);
 	drm_mm_takedown(&priv->linear);
@@ -435,37 +353,28 @@ static const struct component_master_ops armada_master_ops = {
 
 static int armada_drm_probe(struct platform_device *pdev)
 {
-	if (is_componentized(&pdev->dev)) {
-		struct component_match *match = NULL;
-		int ret;
-
-		ret = armada_drm_find_components(&pdev->dev, &match);
-		if (ret < 0)
-			return ret;
-
-		return component_master_add_with_match(&pdev->dev,
-				&armada_master_ops, match);
-	} else {
-		return drm_platform_init(&armada_drm_driver, pdev);
-	}
+	struct component_match *match = NULL;
+	int ret;
+
+	ret = armada_drm_find_components(&pdev->dev, &match);
+	if (ret < 0)
+		return ret;
+
+	return component_master_add_with_match(&pdev->dev, &armada_master_ops,
+					       match);
 }
 
 static int armada_drm_remove(struct platform_device *pdev)
 {
-	if (is_componentized(&pdev->dev))
-		component_master_del(&pdev->dev, &armada_master_ops);
-	else
-		drm_put_dev(platform_get_drvdata(pdev));
+	component_master_del(&pdev->dev, &armada_master_ops);
 	return 0;
 }
 
 static const struct platform_device_id armada_drm_platform_ids[] = {
 	{
 		.name		= "armada-drm",
-		.driver_data	= (unsigned long)&armada510_ops,
 	}, {
 		.name		= "armada-510-drm",
-		.driver_data	= (unsigned long)&armada510_ops,
 	},
 	{ },
 };
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
deleted file mode 100644
index 5a9823178291..000000000000
--- a/drivers/gpu/drm/armada/armada_output.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2012 Russell King
- *
- * 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.
- */
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_encoder_slave.h>
-#include "armada_output.h"
-#include "armada_drm.h"
-
-struct armada_connector {
-	struct drm_connector conn;
-	const struct armada_output_type *type;
-};
-
-#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
-
-struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
-{
-	struct drm_encoder *enc = conn->encoder;
-
-	return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
-}
-
-static enum drm_connector_status armada_drm_connector_detect(
-	struct drm_connector *conn, bool force)
-{
-	struct armada_connector *dconn = drm_to_armada_conn(conn);
-	enum drm_connector_status status = connector_status_disconnected;
-
-	if (dconn->type->detect) {
-		status = dconn->type->detect(conn, force);
-	} else {
-		struct drm_encoder *enc = armada_drm_connector_encoder(conn);
-
-		if (enc)
-			status = encoder_helper_funcs(enc)->detect(enc, conn);
-	}
-
-	return status;
-}
-
-static void armada_drm_connector_destroy(struct drm_connector *conn)
-{
-	struct armada_connector *dconn = drm_to_armada_conn(conn);
-
-	drm_connector_unregister(conn);
-	drm_connector_cleanup(conn);
-	kfree(dconn);
-}
-
-static int armada_drm_connector_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
-{
-	struct armada_connector *dconn = drm_to_armada_conn(conn);
-
-	if (!dconn->type->set_property)
-		return -EINVAL;
-
-	return dconn->type->set_property(conn, property, value);
-}
-
-static const struct drm_connector_funcs armada_drm_conn_funcs = {
-	.dpms		= drm_helper_connector_dpms,
-	.fill_modes	= drm_helper_probe_single_connector_modes,
-	.detect		= armada_drm_connector_detect,
-	.destroy	= armada_drm_connector_destroy,
-	.set_property	= armada_drm_connector_set_property,
-};
-
-/* Shouldn't this be a generic helper function? */
-int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
-	struct drm_display_mode *mode)
-{
-	struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
-	int valid = MODE_BAD;
-
-	if (encoder) {
-		struct drm_encoder_slave *slave = to_encoder_slave(encoder);
-
-		valid = slave->slave_funcs->mode_valid(encoder, mode);
-	}
-	return valid;
-}
-
-int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
-{
-	struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
-	int rc = -EINVAL;
-
-	if (encoder) {
-		struct drm_encoder_slave *slave = to_encoder_slave(encoder);
-
-		rc = slave->slave_funcs->set_property(encoder, conn, property,
-						      value);
-	}
-	return rc;
-}
-
-int armada_output_create(struct drm_device *dev,
-	const struct armada_output_type *type, const void *data)
-{
-	struct armada_connector *dconn;
-	int ret;
-
-	dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
-	if (!dconn)
-		return -ENOMEM;
-
-	dconn->type = type;
-
-	ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
-				 type->connector_type);
-	if (ret) {
-		DRM_ERROR("unable to init connector\n");
-		goto err_destroy_dconn;
-	}
-
-	ret = type->create(&dconn->conn, data);
-	if (ret)
-		goto err_conn;
-
-	ret = drm_connector_register(&dconn->conn);
-	if (ret)
-		goto err_sysfs;
-
-	return 0;
-
- err_sysfs:
-	if (dconn->conn.encoder)
-		dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
- err_conn:
-	drm_connector_cleanup(&dconn->conn);
- err_destroy_dconn:
-	kfree(dconn);
-	return ret;
-}
diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h
deleted file mode 100644
index f448785753e8..000000000000
--- a/drivers/gpu/drm/armada/armada_output.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2012 Russell King
- *
- * 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.
- */
-#ifndef ARMADA_CONNETOR_H
-#define ARMADA_CONNETOR_H
-
-#define encoder_helper_funcs(encoder) \
-	((const struct drm_encoder_helper_funcs *)encoder->helper_private)
-
-struct armada_output_type {
-	int connector_type;
-	enum drm_connector_status (*detect)(struct drm_connector *, bool);
-	int (*create)(struct drm_connector *, const void *);
-	int (*set_property)(struct drm_connector *, struct drm_property *,
-			    uint64_t);
-};
-
-struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
-
-int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
-	struct drm_display_mode *mode);
-
-int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value);
-
-int armada_output_create(struct drm_device *dev,
-	const struct armada_output_type *type, const void *data);
-
-#endif
diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c
deleted file mode 100644
index 00d0facb42f3..000000000000
--- a/drivers/gpu/drm/armada/armada_slave.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2012 Russell King
- *  Rewritten from the dovefb driver, and Armada510 manuals.
- *
- * 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.
- */
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_encoder_slave.h>
-#include "armada_drm.h"
-#include "armada_output.h"
-#include "armada_slave.h"
-
-static int armada_drm_slave_get_modes(struct drm_connector *conn)
-{
-	struct drm_encoder *enc = armada_drm_connector_encoder(conn);
-	int count = 0;
-
-	if (enc) {
-		struct drm_encoder_slave *slave = to_encoder_slave(enc);
-
-		count = slave->slave_funcs->get_modes(enc, conn);
-	}
-
-	return count;
-}
-
-static void armada_drm_slave_destroy(struct drm_encoder *enc)
-{
-	struct drm_encoder_slave *slave = to_encoder_slave(enc);
-	struct i2c_client *client = drm_i2c_encoder_get_client(enc);
-
-	if (slave->slave_funcs)
-		slave->slave_funcs->destroy(enc);
-	if (client)
-		i2c_put_adapter(client->adapter);
-
-	drm_encoder_cleanup(&slave->base);
-	kfree(slave);
-}
-
-static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
-	.destroy	= armada_drm_slave_destroy,
-};
-
-static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
-	.get_modes	= armada_drm_slave_get_modes,
-	.mode_valid	= armada_drm_slave_encoder_mode_valid,
-	.best_encoder	= armada_drm_connector_encoder,
-};
-
-static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
-	.dpms = drm_i2c_encoder_dpms,
-	.save = drm_i2c_encoder_save,
-	.restore = drm_i2c_encoder_restore,
-	.mode_fixup = drm_i2c_encoder_mode_fixup,
-	.prepare = drm_i2c_encoder_prepare,
-	.commit = drm_i2c_encoder_commit,
-	.mode_set = drm_i2c_encoder_mode_set,
-	.detect = drm_i2c_encoder_detect,
-};
-
-static int
-armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
-{
-	const struct armada_drm_slave_config *config = data;
-	struct drm_encoder_slave *slave;
-	struct i2c_adapter *adap;
-	int ret;
-
-	conn->interlace_allowed = config->interlace_allowed;
-	conn->doublescan_allowed = config->doublescan_allowed;
-	conn->polled = config->polled;
-
-	drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
-
-	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
-	if (!slave)
-		return -ENOMEM;
-
-	slave->base.possible_crtcs = config->crtcs;
-
-	adap = i2c_get_adapter(config->i2c_adapter_id);
-	if (!adap) {
-		kfree(slave);
-		return -EPROBE_DEFER;
-	}
-
-	ret = drm_encoder_init(conn->dev, &slave->base,
-			       &armada_drm_slave_encoder_funcs,
-			       DRM_MODE_ENCODER_TMDS);
-	if (ret) {
-		DRM_ERROR("unable to init encoder\n");
-		i2c_put_adapter(adap);
-		kfree(slave);
-		return ret;
-	}
-
-	ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
-	i2c_put_adapter(adap);
-	if (ret) {
-		DRM_ERROR("unable to init encoder slave\n");
-		armada_drm_slave_destroy(&slave->base);
-		return ret;
-	}
-
-	drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
-
-	ret = slave->slave_funcs->create_resources(&slave->base, conn);
-	if (ret) {
-		armada_drm_slave_destroy(&slave->base);
-		return ret;
-	}
-
-	ret = drm_mode_connector_attach_encoder(conn, &slave->base);
-	if (ret) {
-		armada_drm_slave_destroy(&slave->base);
-		return ret;
-	}
-
-	conn->encoder = &slave->base;
-
-	return ret;
-}
-
-static const struct armada_output_type armada_drm_conn_slave = {
-	.connector_type	= DRM_MODE_CONNECTOR_HDMIA,
-	.create		= armada_drm_conn_slave_create,
-	.set_property	= armada_drm_slave_encoder_set_property,
-};
-
-int armada_drm_connector_slave_create(struct drm_device *dev,
-	const struct armada_drm_slave_config *config)
-{
-	return armada_output_create(dev, &armada_drm_conn_slave, config);
-}
diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h
deleted file mode 100644
index bf2374c96fc1..000000000000
--- a/drivers/gpu/drm/armada/armada_slave.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2012 Russell King
- *
- * 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.
- */
-#ifndef ARMADA_SLAVE_H
-#define ARMADA_SLAVE_H
-
-#include <linux/i2c.h>
-#include <drm/drmP.h>
-
-struct armada_drm_slave_config {
-	int i2c_adapter_id;
-	uint32_t crtcs;
-	uint8_t polled;
-	bool interlace_allowed;
-	bool doublescan_allowed;
-	struct i2c_board_info info;
-};
-
-int armada_drm_connector_slave_create(struct drm_device *dev,
-	const struct armada_drm_slave_config *);
-
-#endif

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Our vblank event code belongs in armada_crtc.c rather than the core of
the driver.  Move it there.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 46 ++++++++++++++++++++++++++++++------
 drivers/gpu/drm/armada/armada_crtc.h | 17 +++++++++++++
 drivers/gpu/drm/armada/armada_drm.h  | 16 -------------
 drivers/gpu/drm/armada/armada_drv.c  | 23 ------------------
 4 files changed, 56 insertions(+), 46 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index c7374a3ec67a..418594b19d1d 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -173,6 +173,44 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
 	return i;
 }
 
+void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
+	struct armada_vbl_event *evt)
+{
+	unsigned long flags;
+	bool not_on_list;
+
+	WARN_ON(drm_vblank_get(dcrtc->crtc.dev, dcrtc->num));
+
+	spin_lock_irqsave(&dcrtc->irq_lock, flags);
+	not_on_list = list_empty(&evt->node);
+	if (not_on_list)
+		list_add_tail(&evt->node, &dcrtc->vbl_list);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+
+	if (!not_on_list)
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+}
+
+void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
+	struct armada_vbl_event *evt)
+{
+	if (!list_empty(&evt->node)) {
+		list_del_init(&evt->node);
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+	}
+}
+
+static void armada_drm_vbl_event_run(struct armada_crtc *dcrtc)
+{
+	struct armada_vbl_event *e, *n;
+
+	list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
+		list_del_init(&e->node);
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+		e->fn(dcrtc, e->data);
+	}
+}
+
 static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
 	struct armada_frame_work *work)
 {
@@ -356,7 +394,6 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
 
 static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 {
-	struct armada_vbl_event *e, *n;
 	void __iomem *base = dcrtc->base;
 
 	if (stat & DMA_FF_UNDERFLOW)
@@ -368,12 +405,7 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 		drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
 
 	spin_lock(&dcrtc->irq_lock);
-
-	list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
-		list_del_init(&e->node);
-		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
-		e->fn(dcrtc, e->data);
-	}
+	armada_drm_vbl_event_run(dcrtc);
 
 	if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
 		int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index a13469f5d72b..a86243ef4a51 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -75,6 +75,23 @@ struct armada_crtc {
 };
 #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
 
+struct armada_vbl_event {
+	struct list_head	node;
+	void			*data;
+	void			(*fn)(struct armada_crtc *, void *);
+};
+
+void armada_drm_vbl_event_add(struct armada_crtc *,
+	struct armada_vbl_event *);
+void armada_drm_vbl_event_remove(struct armada_crtc *,
+	struct armada_vbl_event *);
+#define armada_drm_vbl_event_init(_e, _f, _d) do {	\
+	struct armada_vbl_event *__e = _e;		\
+	INIT_LIST_HEAD(&__e->node);			\
+	__e->data = _d;					\
+	__e->fn = _f;					\
+} while (0)
+
 void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
 void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
 void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index 5f6aef0dca59..4df6f2af2b21 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -37,22 +37,6 @@ static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp)
 	return ALIGN(pitch, 128);
 }
 
-struct armada_vbl_event {
-	struct list_head	node;
-	void			*data;
-	void			(*fn)(struct armada_crtc *, void *);
-};
-void armada_drm_vbl_event_add(struct armada_crtc *,
-	struct armada_vbl_event *);
-void armada_drm_vbl_event_remove(struct armada_crtc *,
-	struct armada_vbl_event *);
-#define armada_drm_vbl_event_init(_e, _f, _d) do {	\
-	struct armada_vbl_event *__e = _e;		\
-	INIT_LIST_HEAD(&__e->node);			\
-	__e->data = _d;					\
-	__e->fn = _f;					\
-} while (0)
-
 
 struct armada_private;
 
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index b373cf9b2f65..3f1396e673dd 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -148,29 +148,6 @@ static int armada_drm_unload(struct drm_device *dev)
 	return 0;
 }
 
-void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
-	struct armada_vbl_event *evt)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&dcrtc->irq_lock, flags);
-	if (list_empty(&evt->node)) {
-		list_add_tail(&evt->node, &dcrtc->vbl_list);
-
-		drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
-	}
-	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
-}
-
-void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
-	struct armada_vbl_event *evt)
-{
-	if (!list_empty(&evt->node)) {
-		list_del_init(&evt->node);
-		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
-	}
-}
-
 /* These are called under the vbl_lock. */
 static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
 {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

Use drm_plane_force_disable() to disable the overlay plane on a mode_set
rather than coding this ourselves.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 418594b19d1d..bbf5ff785cd2 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -348,17 +348,11 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc)
 	/*
 	 * If we have an overlay plane associated with this CRTC, disable
 	 * it before the modeset to avoid its coordinates being outside
-	 * the new mode parameters.  DRM doesn't provide help with this.
+	 * the new mode parameters.
 	 */
 	plane = dcrtc->plane;
-	if (plane) {
-		struct drm_framebuffer *fb = plane->fb;
-
-		plane->funcs->disable_plane(plane);
-		plane->fb = NULL;
-		plane->crtc = NULL;
-		drm_framebuffer_unreference(fb);
-	}
+	if (plane)
+		drm_plane_force_disable(plane);
 }
 
 /* The mode_config.mutex will be held for this call */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

When the CRTC is in low power mode, it isn't running, and so there's
no point keeping the CRTC clock enabled.  Disable the CRTC clock during
DPMS.

We need to re-enable it in the mode_set callback to ensure that the
variant's compute_clock() continues to see its clock in the expected
state (enabled).

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index bbf5ff785cd2..8c43ecc19c15 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -325,7 +325,11 @@ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
 
 	if (dcrtc->dpms != dpms) {
 		dcrtc->dpms = dpms;
+		if (!IS_ERR(dcrtc->clk) && !dpms_blanked(dpms))
+			WARN_ON(clk_prepare_enable(dcrtc->clk));
 		armada_drm_crtc_update(dcrtc);
+		if (!IS_ERR(dcrtc->clk) && dpms_blanked(dpms))
+			clk_disable_unprepare(dcrtc->clk);
 		if (dpms_blanked(dpms))
 			armada_drm_vblank_off(dcrtc);
 		else
@@ -563,6 +567,13 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 		writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL);
 	}
 
+	/*
+	 * If we are blanked, we would have disabled the clock.  Re-enable
+	 * it so that compute_clock() does the right thing.
+	 */
+	if (!IS_ERR(dcrtc->clk) && dpms_blanked(dcrtc->dpms))
+		WARN_ON(clk_prepare_enable(dcrtc->clk));
+
 	/* Now compute the divider for real */
 	dcrtc->variant->compute_clock(dcrtc, adj, &sclk);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:11 +0100

We can do better with armada_drm_crtc_complete_frame_work() - we can
avoid taking the event lock unless a call to drm_send_vblank_event()
is required, and using cmpxchg() and xchg(), we can eliminate the
locking around dcrtc->frame_work entirely.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 53 ++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 8c43ecc19c15..5d627646601e 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -215,7 +215,6 @@ static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
 	struct armada_frame_work *work)
 {
 	struct drm_device *dev = dcrtc->crtc.dev;
-	unsigned long flags;
 	int ret;
 
 	ret = drm_vblank_get(dev, dcrtc->num);
@@ -224,30 +223,29 @@ static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
 		return ret;
 	}
 
-	spin_lock_irqsave(&dev->event_lock, flags);
-	if (!dcrtc->frame_work)
-		dcrtc->frame_work = work;
-	else
-		ret = -EBUSY;
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-
-	if (ret)
+	if (cmpxchg(&dcrtc->frame_work, NULL, work)) {
 		drm_vblank_put(dev, dcrtc->num);
+		ret = -EBUSY;
+	}
 
 	return ret;
 }
 
-static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
+static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc,
+	struct armada_frame_work *work)
 {
 	struct drm_device *dev = dcrtc->crtc.dev;
-	struct armada_frame_work *work = dcrtc->frame_work;
-
-	dcrtc->frame_work = NULL;
+	unsigned long flags;
 
+	spin_lock_irqsave(&dcrtc->irq_lock, flags);
 	armada_drm_crtc_update_regs(dcrtc, work->regs);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
 
-	if (work->event)
+	if (work->event) {
+		spin_lock_irqsave(&dev->event_lock, flags);
 		drm_send_vblank_event(dev, dcrtc->num, work->event);
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
 
 	drm_vblank_put(dev, dcrtc->num);
 
@@ -293,7 +291,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
 
 static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
 {
-	struct drm_device *dev = dcrtc->crtc.dev;
+	struct armada_frame_work *work;
 
 	/*
 	 * Tell the DRM core that vblank IRQs aren't going to happen for
@@ -302,10 +300,9 @@ static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
 	drm_crtc_vblank_off(&dcrtc->crtc);
 
 	/* Handle any pending flip event. */
-	spin_lock_irq(&dev->event_lock);
-	if (dcrtc->frame_work)
-		armada_drm_crtc_complete_frame_work(dcrtc);
-	spin_unlock_irq(&dev->event_lock);
+	work = xchg(&dcrtc->frame_work, NULL);
+	if (work)
+		armada_drm_crtc_complete_frame_work(dcrtc, work);
 }
 
 void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
@@ -434,12 +431,10 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 	spin_unlock(&dcrtc->irq_lock);
 
 	if (stat & GRA_FRAME_IRQ) {
-		struct drm_device *dev = dcrtc->crtc.dev;
+		struct armada_frame_work *work = xchg(&dcrtc->frame_work, NULL);
 
-		spin_lock(&dev->event_lock);
-		if (dcrtc->frame_work)
-			armada_drm_crtc_complete_frame_work(dcrtc);
-		spin_unlock(&dev->event_lock);
+		if (work)
+			armada_drm_crtc_complete_frame_work(dcrtc, work);
 
 		wake_up(&dcrtc->frame_wait);
 	}
@@ -957,8 +952,6 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_frame_work *work;
-	struct drm_device *dev = crtc->dev;
-	unsigned long flags;
 	unsigned i;
 	int ret;
 
@@ -1004,10 +997,10 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 	 * interrupt, so complete it now.
 	 */
 	if (dpms_blanked(dcrtc->dpms)) {
-		spin_lock_irqsave(&dev->event_lock, flags);
-		if (dcrtc->frame_work)
-			armada_drm_crtc_complete_frame_work(dcrtc);
-		spin_unlock_irqrestore(&dev->event_lock, flags);
+		struct armada_frame_work *work = xchg(&dcrtc->frame_work, NULL);
+
+		if (work)
+			armada_drm_crtc_complete_frame_work(dcrtc, work);
 	}
 
 	return 0;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Include an _ovl infix into the overlay identifiers to separate them from
the primary plane.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_overlay.c | 55 ++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index e939faba7fcc..9393a15183e2 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -16,7 +16,7 @@
 #include <drm/armada_drm.h>
 #include "armada_ioctlP.h"
 
-struct armada_plane_properties {
+struct armada_ovl_plane_properties {
 	uint32_t colorkey_yr;
 	uint32_t colorkey_ug;
 	uint32_t colorkey_vb;
@@ -29,7 +29,7 @@ struct armada_plane_properties {
 	uint32_t colorkey_mode;
 };
 
-struct armada_plane {
+struct armada_ovl_plane {
 	struct drm_plane base;
 	spinlock_t lock;
 	struct drm_framebuffer *old_fb;
@@ -42,13 +42,13 @@ struct armada_plane {
 		struct armada_regs regs[13];
 		wait_queue_head_t wait;
 	} vbl;
-	struct armada_plane_properties prop;
+	struct armada_ovl_plane_properties prop;
 };
-#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
+#define drm_to_armada_ovl_plane(p) container_of(p, struct armada_ovl_plane, base)

 static void
-armada_ovl_update_attr(struct armada_plane_properties *prop,
+armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
 	struct armada_crtc *dcrtc)
 {
 	writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
@@ -72,9 +72,9 @@ armada_ovl_update_attr(struct armada_plane_properties *prop,
 }
 
 /* === Plane support === */
-static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
+static void armada_ovl_plane_vbl(struct armada_crtc *dcrtc, void *data)
 {
-	struct armada_plane *dplane = data;
+	struct armada_ovl_plane *dplane = data;
 	struct drm_framebuffer *fb;
 
 	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
@@ -91,12 +91,12 @@ static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
 }
 
 static int
-armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 	struct drm_framebuffer *fb,
 	int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
 	uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
 {
-	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct drm_rect src = {
 		.x1 = src_x,
@@ -267,9 +267,9 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 	return 0;
 }
 
-static int armada_plane_disable(struct drm_plane *plane)
+static int armada_ovl_plane_disable(struct drm_plane *plane)
 {
-	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 	struct drm_framebuffer *fb;
 	struct armada_crtc *dcrtc;
 
@@ -302,20 +302,20 @@ static int armada_plane_disable(struct drm_plane *plane)
 	return 0;
 }
 
-static void armada_plane_destroy(struct drm_plane *plane)
+static void armada_ovl_plane_destroy(struct drm_plane *plane)
 {
-	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 
 	drm_plane_cleanup(plane);
 
 	kfree(dplane);
 }
 
-static int armada_plane_set_property(struct drm_plane *plane,
+static int armada_ovl_plane_set_property(struct drm_plane *plane,
 	struct drm_property *property, uint64_t val)
 {
 	struct armada_private *priv = plane->dev->dev_private;
-	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
 	bool update_attr = false;
 
 	if (property == priv->colorkey_prop) {
@@ -379,14 +379,14 @@ static int armada_plane_set_property(struct drm_plane *plane,
 	return 0;
 }
 
-static const struct drm_plane_funcs armada_plane_funcs = {
-	.update_plane	= armada_plane_update,
-	.disable_plane	= armada_plane_disable,
-	.destroy	= armada_plane_destroy,
-	.set_property	= armada_plane_set_property,
+static const struct drm_plane_funcs armada_ovl_plane_funcs = {
+	.update_plane	= armada_ovl_plane_update,
+	.disable_plane	= armada_ovl_plane_disable,
+	.destroy	= armada_ovl_plane_destroy,
+	.set_property	= armada_ovl_plane_set_property,
 };
 
-static const uint32_t armada_formats[] = {
+static const uint32_t armada_ovl_formats[] = {
 	DRM_FORMAT_UYVY,
 	DRM_FORMAT_YUYV,
 	DRM_FORMAT_YUV420,
@@ -456,7 +456,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 {
 	struct armada_private *priv = dev->dev_private;
 	struct drm_mode_object *mobj;
-	struct armada_plane *dplane;
+	struct armada_ovl_plane *dplane;
 	int ret;
 
 	ret = armada_overlay_create_properties(dev);
@@ -469,11 +469,16 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 
 	spin_lock_init(&dplane->lock);
 	init_waitqueue_head(&dplane->vbl.wait);
-	armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
+	armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
 				  dplane);
 
-	drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
-		       armada_formats, ARRAY_SIZE(armada_formats), false);
+	drm_plane_init(dev, &dplane->base, crtcs, &armada_ovl_plane_funcs,
+		       armada_ovl_formats, ARRAY_SIZE(armada_ovl_formats),
+		       false);
+	if (ret) {
+		kfree(dplane);
+		return ret;
+	}
 
 	dplane->prop.colorkey_yr = 0xfefefe00;
 	dplane->prop.colorkey_ug = 0x01010100;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

We have two identical places in the overlay code which retire the drm
framebuffer.  Factor these out into a common function.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_overlay.c | 37 +++++++++++++++------------------
 1 file changed, 17 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 9393a15183e2..093c2d4f2b79 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -71,21 +71,27 @@ armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
 	spin_unlock_irq(&dcrtc->irq_lock);
 }
 
+static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
+	struct drm_framebuffer *fb)
+{
+	struct drm_framebuffer *old_fb;
+
+	spin_lock(&dplane->lock);
+	old_fb = dplane->old_fb;
+	dplane->old_fb = fb;
+	spin_unlock(&dplane->lock);
+
+	if (old_fb)
+		armada_drm_queue_unref_work(dplane->base.dev, old_fb);
+}
+
 /* === Plane support === */
 static void armada_ovl_plane_vbl(struct armada_crtc *dcrtc, void *data)
 {
 	struct armada_ovl_plane *dplane = data;
-	struct drm_framebuffer *fb;
 
 	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
-
-	spin_lock(&dplane->lock);
-	fb = dplane->old_fb;
-	dplane->old_fb = NULL;
-	spin_unlock(&dplane->lock);
-
-	if (fb)
-		armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
+	armada_ovl_retire_fb(dplane, NULL);
 
 	wake_up(&dplane->vbl.wait);
 }
@@ -175,17 +181,8 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 		 */
 		drm_framebuffer_reference(fb);
 
-		if (plane->fb) {
-			struct drm_framebuffer *older_fb;
-
-			spin_lock_irq(&dplane->lock);
-			older_fb = dplane->old_fb;
-			dplane->old_fb = plane->fb;
-			spin_unlock_irq(&dplane->lock);
-			if (older_fb)
-				armada_drm_queue_unref_work(dcrtc->crtc.dev,
-							    older_fb);
-		}
+		if (plane->fb)
+			armada_ovl_retire_fb(dplane, plane->fb);
 
 		src_y = src.y1 >> 16;
 		src_x = src.x1 >> 16;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Remove the DRM slave encoder compatibility from the TDA998x driver.  We
now use the component helpers to manage the binding of DRM sub-drivers.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 146 +++-----------------------------------
 1 file changed, 8 insertions(+), 138 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 1c6fc245514f..883025d35f87 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -23,7 +23,6 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_encoder_slave.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/i2c/tda998x.h>
@@ -53,8 +52,6 @@ struct tda998x_priv {
 	bool edid_delay_active;
 };
 
-#define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
-
 /* The TDA9988 series of devices use a paged register scheme.. to simplify
  * things we encode the page # in upper bits of the register #.  To read/
  * write a given register, we need to make sure CURPAGE register is set
@@ -1182,16 +1179,6 @@ static void tda998x_encoder_set_polling(struct tda998x_priv *priv,
 			DRM_CONNECTOR_POLL_DISCONNECT;
 }
 
-static int
-tda998x_encoder_set_property(struct drm_encoder *encoder,
-			    struct drm_connector *connector,
-			    struct drm_property *property,
-			    uint64_t val)
-{
-	DBG("");
-	return 0;
-}
-
 static void tda998x_destroy(struct tda998x_priv *priv)
 {
 	/* disable all IRQs and free the IRQ handler */
@@ -1207,78 +1194,6 @@ static void tda998x_destroy(struct tda998x_priv *priv)
 	i2c_unregister_device(priv->cec);
 }
 
-/* Slave encoder support */
-
-static void
-tda998x_encoder_slave_set_config(struct drm_encoder *encoder, void *params)
-{
-	tda998x_encoder_set_config(to_tda998x_priv(encoder), params);
-}
-
-static void tda998x_encoder_slave_destroy(struct drm_encoder *encoder)
-{
-	struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
-	tda998x_destroy(priv);
-	drm_i2c_encoder_destroy(encoder);
-	kfree(priv);
-}
-
-static void tda998x_encoder_slave_dpms(struct drm_encoder *encoder, int mode)
-{
-	tda998x_encoder_dpms(to_tda998x_priv(encoder), mode);
-}
-
-static int tda998x_encoder_slave_mode_valid(struct drm_encoder *encoder,
-					    struct drm_display_mode *mode)
-{
-	return tda998x_encoder_mode_valid(to_tda998x_priv(encoder), mode);
-}
-
-static void
-tda998x_encoder_slave_mode_set(struct drm_encoder *encoder,
-			       struct drm_display_mode *mode,
-			       struct drm_display_mode *adjusted_mode)
-{
-	tda998x_encoder_mode_set(to_tda998x_priv(encoder), mode, adjusted_mode);
-}
-
-static enum drm_connector_status
-tda998x_encoder_slave_detect(struct drm_encoder *encoder,
-			     struct drm_connector *connector)
-{
-	return tda998x_encoder_detect(to_tda998x_priv(encoder));
-}
-
-static int tda998x_encoder_slave_get_modes(struct drm_encoder *encoder,
-					   struct drm_connector *connector)
-{
-	return tda998x_encoder_get_modes(to_tda998x_priv(encoder), connector);
-}
-
-static int
-tda998x_encoder_slave_create_resources(struct drm_encoder *encoder,
-				       struct drm_connector *connector)
-{
-	tda998x_encoder_set_polling(to_tda998x_priv(encoder), connector);
-	return 0;
-}
-
-static struct drm_encoder_slave_funcs tda998x_encoder_slave_funcs = {
-	.set_config = tda998x_encoder_slave_set_config,
-	.destroy = tda998x_encoder_slave_destroy,
-	.dpms = tda998x_encoder_slave_dpms,
-	.save = tda998x_encoder_save,
-	.restore = tda998x_encoder_restore,
-	.mode_fixup = tda998x_encoder_mode_fixup,
-	.mode_valid = tda998x_encoder_slave_mode_valid,
-	.mode_set = tda998x_encoder_slave_mode_set,
-	.detect = tda998x_encoder_slave_detect,
-	.get_modes = tda998x_encoder_slave_get_modes,
-	.create_resources = tda998x_encoder_slave_create_resources,
-	.set_property = tda998x_encoder_set_property,
-};
-
 /* I2C driver functions */
 
 static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
@@ -1413,31 +1328,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	return -ENXIO;
 }
 
-static int tda998x_encoder_init(struct i2c_client *client,
-				struct drm_device *dev,
-				struct drm_encoder_slave *encoder_slave)
-{
-	struct tda998x_priv *priv;
-	int ret;
-
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	priv->encoder = &encoder_slave->base;
-
-	ret = tda998x_create(client, priv);
-	if (ret) {
-		kfree(priv);
-		return ret;
-	}
-
-	encoder_slave->slave_priv = priv;
-	encoder_slave->slave_funcs = &tda998x_encoder_slave_funcs;
-
-	return 0;
-}
-
 struct tda998x_priv2 {
 	struct tda998x_priv base;
 	struct drm_encoder encoder;
@@ -1659,38 +1549,18 @@ static struct i2c_device_id tda998x_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, tda998x_ids);
 
-static struct drm_i2c_encoder_driver tda998x_driver = {
-	.i2c_driver = {
-		.probe = tda998x_probe,
-		.remove = tda998x_remove,
-		.driver = {
-			.name = "tda998x",
-			.of_match_table = of_match_ptr(tda998x_dt_ids),
-		},
-		.id_table = tda998x_ids,
+static struct i2c_driver tda998x_driver = {
+	.probe = tda998x_probe,
+	.remove = tda998x_remove,
+	.driver = {
+		.name = "tda998x",
+		.of_match_table = of_match_ptr(tda998x_dt_ids),
 	},
-	.encoder_init = tda998x_encoder_init,
+	.id_table = tda998x_ids,
 };
 
-/* Module initialization */
-
-static int __init
-tda998x_init(void)
-{
-	DBG("");
-	return drm_i2c_encoder_register(THIS_MODULE, &tda998x_driver);
-}
-
-static void __exit
-tda998x_exit(void)
-{
-	DBG("");
-	drm_i2c_encoder_unregister(&tda998x_driver);
-}
+module_i2c_driver(tda998x_driver);
 
 MODULE_AUTHOR("Rob Clark <[email-masked]");
 MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder");
 MODULE_LICENSE("GPL");
-
-module_init(tda998x_init);
-module_exit(tda998x_exit);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Remove the encoder pointer from struct tda998x_priv, moving the encoder
itself from struct tda998x_priv2 here.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 883025d35f87..e30a2a8c2a3c 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -44,12 +44,13 @@ struct tda998x_priv {
 
 	wait_queue_head_t wq_edid;
 	volatile int wq_edid_wait;
-	struct drm_encoder *encoder;
 
 	struct work_struct detect_work;
 	struct timer_list edid_delay_timer;
 	wait_queue_head_t edid_delay_waitq;
 	bool edid_delay_active;
+
+	struct drm_encoder encoder;
 };
 
 /* The TDA9988 series of devices use a paged register scheme.. to simplify
@@ -594,7 +595,7 @@ static void tda998x_detect_work(struct work_struct *work)
 {
 	struct tda998x_priv *priv =
 		container_of(work, struct tda998x_priv, detect_work);
-	struct drm_device *dev = priv->encoder->dev;
+	struct drm_device *dev = priv->encoder.dev;
 
 	if (dev)
 		drm_kms_helper_hotplug_event(dev);
@@ -1330,7 +1331,6 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 
 struct tda998x_priv2 {
 	struct tda998x_priv base;
-	struct drm_encoder encoder;
 	struct drm_connector connector;
 };
 
@@ -1338,7 +1338,7 @@ struct tda998x_priv2 {
 	container_of(x, struct tda998x_priv2, connector);
 
 #define enc_to_tda998x_priv2(x) \
-	container_of(x, struct tda998x_priv2, encoder);
+	container_of(x, struct tda998x_priv2, base.encoder);
 
 static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode)
 {
@@ -1408,7 +1408,7 @@ tda998x_connector_best_encoder(struct drm_connector *connector)
 {
 	struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
 
-	return &priv->encoder;
+	return &priv->base.encoder;
 }
 
 static
@@ -1463,9 +1463,8 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 		crtcs = 1 << 0;
 	}
 
-	priv->base.encoder = &priv->encoder;
 	priv->connector.interlace_allowed = 1;
-	priv->encoder.possible_crtcs = crtcs;
+	priv->base.encoder.possible_crtcs = crtcs;
 
 	ret = tda998x_create(client, &priv->base);
 	if (ret)
@@ -1476,8 +1475,8 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 
 	tda998x_encoder_set_polling(&priv->base, &priv->connector);
 
-	drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
-	ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
+	drm_encoder_helper_add(&priv->base.encoder, &tda998x_encoder_helper_funcs);
+	ret = drm_encoder_init(drm, &priv->base.encoder, &tda998x_encoder_funcs,
 			       DRM_MODE_ENCODER_TMDS);
 	if (ret)
 		goto err_encoder;
@@ -1494,15 +1493,15 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		goto err_sysfs;
 
-	priv->connector.encoder = &priv->encoder;
-	drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
+	priv->connector.encoder = &priv->base.encoder;
+	drm_mode_connector_attach_encoder(&priv->connector, &priv->base.encoder);
 
 	return 0;
 
 err_sysfs:
 	drm_connector_cleanup(&priv->connector);
 err_connector:
-	drm_encoder_cleanup(&priv->encoder);
+	drm_encoder_cleanup(&priv->base.encoder);
 err_encoder:
 	tda998x_destroy(&priv->base);
 	return ret;
@@ -1514,7 +1513,7 @@ static void tda998x_unbind(struct device *dev, struct device *master,
 	struct tda998x_priv2 *priv = dev_get_drvdata(dev);
 
 	drm_connector_cleanup(&priv->connector);
-	drm_encoder_cleanup(&priv->encoder);
+	drm_encoder_cleanup(&priv->base.encoder);
 	tda998x_destroy(&priv->base);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Move the DRM connector structure into struct tda998x_priv from the old
struct tda998x_priv2.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index e30a2a8c2a3c..a2a463cec244 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -51,6 +51,7 @@ struct tda998x_priv {
 	bool edid_delay_active;
 
 	struct drm_encoder encoder;
+	struct drm_connector connector;
 };
 
 /* The TDA9988 series of devices use a paged register scheme.. to simplify
@@ -1331,11 +1332,10 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 
 struct tda998x_priv2 {
 	struct tda998x_priv base;
-	struct drm_connector connector;
 };
 
 #define conn_to_tda998x_priv2(x) \
-	container_of(x, struct tda998x_priv2, connector);
+	container_of(x, struct tda998x_priv2, base.connector);
 
 #define enc_to_tda998x_priv2(x) \
 	container_of(x, struct tda998x_priv2, base.encoder);
@@ -1463,7 +1463,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 		crtcs = 1 << 0;
 	}
 
-	priv->connector.interlace_allowed = 1;
+	priv->base.connector.interlace_allowed = 1;
 	priv->base.encoder.possible_crtcs = crtcs;
 
 	ret = tda998x_create(client, &priv->base);
@@ -1473,7 +1473,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	if (!dev->of_node && params)
 		tda998x_encoder_set_config(&priv->base, params);
 
-	tda998x_encoder_set_polling(&priv->base, &priv->connector);
+	tda998x_encoder_set_polling(&priv->base, &priv->base.connector);
 
 	drm_encoder_helper_add(&priv->base.encoder, &tda998x_encoder_helper_funcs);
 	ret = drm_encoder_init(drm, &priv->base.encoder, &tda998x_encoder_funcs,
@@ -1481,25 +1481,25 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		goto err_encoder;
 
-	drm_connector_helper_add(&priv->connector,
+	drm_connector_helper_add(&priv->base.connector,
 				 &tda998x_connector_helper_funcs);
-	ret = drm_connector_init(drm, &priv->connector,
+	ret = drm_connector_init(drm, &priv->base.connector,
 				 &tda998x_connector_funcs,
 				 DRM_MODE_CONNECTOR_HDMIA);
 	if (ret)
 		goto err_connector;
 
-	ret = drm_connector_register(&priv->connector);
+	ret = drm_connector_register(&priv->base.connector);
 	if (ret)
 		goto err_sysfs;
 
-	priv->connector.encoder = &priv->base.encoder;
-	drm_mode_connector_attach_encoder(&priv->connector, &priv->base.encoder);
+	priv->base.connector.encoder = &priv->base.encoder;
+	drm_mode_connector_attach_encoder(&priv->base.connector, &priv->base.encoder);
 
 	return 0;
 
 err_sysfs:
-	drm_connector_cleanup(&priv->connector);
+	drm_connector_cleanup(&priv->base.connector);
 err_connector:
 	drm_encoder_cleanup(&priv->base.encoder);
 err_encoder:
@@ -1512,7 +1512,7 @@ static void tda998x_unbind(struct device *dev, struct device *master,
 {
 	struct tda998x_priv2 *priv = dev_get_drvdata(dev);
 
-	drm_connector_cleanup(&priv->connector);
+	drm_connector_cleanup(&priv->base.connector);
 	drm_encoder_cleanup(&priv->base.encoder);
 	tda998x_destroy(&priv->base);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Kill the redundant tda998x_priv2 structure now that its only member is
the struct tda998x_priv.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 80 +++++++++++++++++++--------------------
 1 file changed, 38 insertions(+), 42 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index a2a463cec244..8bca9155ee18 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -1330,21 +1330,17 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	return -ENXIO;
 }
 
-struct tda998x_priv2 {
-	struct tda998x_priv base;
-};
-
-#define conn_to_tda998x_priv2(x) \
-	container_of(x, struct tda998x_priv2, base.connector);
+#define conn_to_tda998x_priv(x) \
+	container_of(x, struct tda998x_priv, connector);
 
-#define enc_to_tda998x_priv2(x) \
-	container_of(x, struct tda998x_priv2, base.encoder);
+#define enc_to_tda998x_priv(x) \
+	container_of(x, struct tda998x_priv, encoder);
 
 static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode)
 {
-	struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
+	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
 
-	tda998x_encoder_dpms(&priv->base, mode);
+	tda998x_encoder_dpms(priv, mode);
 }
 
 static void tda998x_encoder_prepare(struct drm_encoder *encoder)
@@ -1361,9 +1357,9 @@ static void tda998x_encoder2_mode_set(struct drm_encoder *encoder,
 				      struct drm_display_mode *mode,
 				      struct drm_display_mode *adjusted_mode)
 {
-	struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
+	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
 
-	tda998x_encoder_mode_set(&priv->base, mode, adjusted_mode);
+	tda998x_encoder_mode_set(priv, mode, adjusted_mode);
 }
 
 static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = {
@@ -1378,9 +1374,9 @@ static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = {
 
 static void tda998x_encoder_destroy(struct drm_encoder *encoder)
 {
-	struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
+	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
 
-	tda998x_destroy(&priv->base);
+	tda998x_destroy(priv);
 	drm_encoder_cleanup(encoder);
 }
 
@@ -1390,25 +1386,25 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = {
 
 static int tda998x_connector_get_modes(struct drm_connector *connector)
 {
-	struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
 
-	return tda998x_encoder_get_modes(&priv->base, connector);
+	return tda998x_encoder_get_modes(priv, connector);
 }
 
 static int tda998x_connector_mode_valid(struct drm_connector *connector,
 					struct drm_display_mode *mode)
 {
-	struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
 
-	return tda998x_encoder_mode_valid(&priv->base, mode);
+	return tda998x_encoder_mode_valid(priv, mode);
 }
 
 static struct drm_encoder *
 tda998x_connector_best_encoder(struct drm_connector *connector)
 {
-	struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
 
-	return &priv->base.encoder;
+	return &priv->encoder;
 }
 
 static
@@ -1421,9 +1417,9 @@ const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
 static enum drm_connector_status
 tda998x_connector_detect(struct drm_connector *connector, bool force)
 {
-	struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
 
-	return tda998x_encoder_detect(&priv->base);
+	return tda998x_encoder_detect(priv);
 }
 
 static void tda998x_connector_destroy(struct drm_connector *connector)
@@ -1444,7 +1440,7 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	struct tda998x_encoder_params *params = dev->platform_data;
 	struct i2c_client *client = to_i2c_client(dev);
 	struct drm_device *drm = data;
-	struct tda998x_priv2 *priv;
+	struct tda998x_priv *priv;
 	u32 crtcs = 0;
 	int ret;
 
@@ -1463,58 +1459,58 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 		crtcs = 1 << 0;
 	}
 
-	priv->base.connector.interlace_allowed = 1;
-	priv->base.encoder.possible_crtcs = crtcs;
+	priv->connector.interlace_allowed = 1;
+	priv->encoder.possible_crtcs = crtcs;
 
-	ret = tda998x_create(client, &priv->base);
+	ret = tda998x_create(client, priv);
 	if (ret)
 		return ret;
 
 	if (!dev->of_node && params)
-		tda998x_encoder_set_config(&priv->base, params);
+		tda998x_encoder_set_config(priv, params);
 
-	tda998x_encoder_set_polling(&priv->base, &priv->base.connector);
+	tda998x_encoder_set_polling(priv, &priv->connector);
 
-	drm_encoder_helper_add(&priv->base.encoder, &tda998x_encoder_helper_funcs);
-	ret = drm_encoder_init(drm, &priv->base.encoder, &tda998x_encoder_funcs,
+	drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
+	ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
 			       DRM_MODE_ENCODER_TMDS);
 	if (ret)
 		goto err_encoder;
 
-	drm_connector_helper_add(&priv->base.connector,
+	drm_connector_helper_add(&priv->connector,
 				 &tda998x_connector_helper_funcs);
-	ret = drm_connector_init(drm, &priv->base.connector,
+	ret = drm_connector_init(drm, &priv->connector,
 				 &tda998x_connector_funcs,
 				 DRM_MODE_CONNECTOR_HDMIA);
 	if (ret)
 		goto err_connector;
 
-	ret = drm_connector_register(&priv->base.connector);
+	ret = drm_connector_register(&priv->connector);
 	if (ret)
 		goto err_sysfs;
 
-	priv->base.connector.encoder = &priv->base.encoder;
-	drm_mode_connector_attach_encoder(&priv->base.connector, &priv->base.encoder);
+	priv->connector.encoder = &priv->encoder;
+	drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
 
 	return 0;
 
 err_sysfs:
-	drm_connector_cleanup(&priv->base.connector);
+	drm_connector_cleanup(&priv->connector);
 err_connector:
-	drm_encoder_cleanup(&priv->base.encoder);
+	drm_encoder_cleanup(&priv->encoder);
 err_encoder:
-	tda998x_destroy(&priv->base);
+	tda998x_destroy(priv);
 	return ret;
 }
 
 static void tda998x_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct tda998x_priv2 *priv = dev_get_drvdata(dev);
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
 
-	drm_connector_cleanup(&priv->base.connector);
-	drm_encoder_cleanup(&priv->base.encoder);
-	tda998x_destroy(&priv->base);
+	drm_connector_cleanup(&priv->connector);
+	drm_encoder_cleanup(&priv->encoder);
+	tda998x_destroy(priv);
 }
 
 static const struct component_ops tda998x_ops = {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

We can now kill a number of glue functions which were sitting between
the common tda998x code and the drm encoder/connector methods.  This
results in slightly cleaner code.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 80 +++++++++++----------------------------
 1 file changed, 22 insertions(+), 58 deletions(-)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 8bca9155ee18..896b6aaf8c4d 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -54,6 +54,12 @@ struct tda998x_priv {
 	struct drm_connector connector;
 };
 
+#define conn_to_tda998x_priv(x) \
+	container_of(x, struct tda998x_priv, connector)
+
+#define enc_to_tda998x_priv(x) \
+	container_of(x, struct tda998x_priv, encoder)
+
 /* The TDA9988 series of devices use a paged register scheme.. to simplify
  * things we encode the page # in upper bits of the register #.  To read/
  * write a given register, we need to make sure CURPAGE register is set
@@ -562,7 +568,7 @@ tda998x_reset(struct tda998x_priv *priv)
  * trying to read EDID data.
  *
  * However, tda998x_encoder_get_modes() may be called at any moment
- * after tda998x_encoder_detect() indicates that we are connected, so
+ * after tda998x_connector_detect() indicates that we are connected, so
  * we need to delay probing modes in tda998x_encoder_get_modes() after
  * we have seen a HPD inactive->active transition.  This code implements
  * that delay.
@@ -816,8 +822,10 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv,
 	priv->params = *p;
 }
 
-static void tda998x_encoder_dpms(struct tda998x_priv *priv, int mode)
+static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
+	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
+
 	/* we only care about on or off: */
 	if (mode != DRM_MODE_DPMS_ON)
 		mode = DRM_MODE_DPMS_OFF;
@@ -867,8 +875,8 @@ tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
 	return true;
 }
 
-static int tda998x_encoder_mode_valid(struct tda998x_priv *priv,
-				      struct drm_display_mode *mode)
+static int tda998x_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
 {
 	if (mode->clock > 150000)
 		return MODE_CLOCK_HIGH;
@@ -880,10 +888,11 @@ static int tda998x_encoder_mode_valid(struct tda998x_priv *priv,
 }
 
 static void
-tda998x_encoder_mode_set(struct tda998x_priv *priv,
+tda998x_encoder_mode_set(struct drm_encoder *encoder,
 			 struct drm_display_mode *mode,
 			 struct drm_display_mode *adjusted_mode)
 {
+	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
 	u16 ref_pix, ref_line, n_pix, n_line;
 	u16 hs_pix_s, hs_pix_e;
 	u16 vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
@@ -1071,8 +1080,9 @@ tda998x_encoder_mode_set(struct tda998x_priv *priv,
 }
 
 static enum drm_connector_status
-tda998x_encoder_detect(struct tda998x_priv *priv)
+tda998x_connector_detect(struct drm_connector *connector, bool force)
 {
+	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
 	u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
 
 	return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
@@ -1135,10 +1145,9 @@ static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
 	return 0;
 }
 
-static int
-tda998x_encoder_get_modes(struct tda998x_priv *priv,
-			  struct drm_connector *connector)
+static int tda998x_connector_get_modes(struct drm_connector *connector)
 {
+	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
 	struct edid *edid;
 	int n;
 
@@ -1330,46 +1339,24 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 	return -ENXIO;
 }
 
-#define conn_to_tda998x_priv(x) \
-	container_of(x, struct tda998x_priv, connector);
-
-#define enc_to_tda998x_priv(x) \
-	container_of(x, struct tda998x_priv, encoder);
-
-static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode)
-{
-	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
-
-	tda998x_encoder_dpms(priv, mode);
-}
-
 static void tda998x_encoder_prepare(struct drm_encoder *encoder)
 {
-	tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_OFF);
+	tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
 }
 
 static void tda998x_encoder_commit(struct drm_encoder *encoder)
 {
-	tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_ON);
-}
-
-static void tda998x_encoder2_mode_set(struct drm_encoder *encoder,
-				      struct drm_display_mode *mode,
-				      struct drm_display_mode *adjusted_mode)
-{
-	struct tda998x_priv *priv = enc_to_tda998x_priv(encoder);
-
-	tda998x_encoder_mode_set(priv, mode, adjusted_mode);
+	tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
 }
 
 static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = {
-	.dpms = tda998x_encoder2_dpms,
+	.dpms = tda998x_encoder_dpms,
 	.save = tda998x_encoder_save,
 	.restore = tda998x_encoder_restore,
 	.mode_fixup = tda998x_encoder_mode_fixup,
 	.prepare = tda998x_encoder_prepare,
 	.commit = tda998x_encoder_commit,
-	.mode_set = tda998x_encoder2_mode_set,
+	.mode_set = tda998x_encoder_mode_set,
 };
 
 static void tda998x_encoder_destroy(struct drm_encoder *encoder)
@@ -1384,21 +1371,6 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = {
 	.destroy = tda998x_encoder_destroy,
 };
 
-static int tda998x_connector_get_modes(struct drm_connector *connector)
-{
-	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
-
-	return tda998x_encoder_get_modes(priv, connector);
-}
-
-static int tda998x_connector_mode_valid(struct drm_connector *connector,
-					struct drm_display_mode *mode)
-{
-	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
-
-	return tda998x_encoder_mode_valid(priv, mode);
-}
-
 static struct drm_encoder *
 tda998x_connector_best_encoder(struct drm_connector *connector)
 {
@@ -1414,14 +1386,6 @@ const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
 	.best_encoder = tda998x_connector_best_encoder,
 };
 
-static enum drm_connector_status
-tda998x_connector_detect(struct drm_connector *connector, bool force)
-{
-	struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
-
-	return tda998x_encoder_detect(priv);
-}
-
 static void tda998x_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Rather than using a spinlock, use xchg() to atomically update
dplane->old_fb.  This allows us to eliminate dplane->lock.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_overlay.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 093c2d4f2b79..5e2dcecbf6ef 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -31,7 +31,6 @@ struct armada_ovl_plane_properties {
 
 struct armada_ovl_plane {
 	struct drm_plane base;
-	spinlock_t lock;
 	struct drm_framebuffer *old_fb;
 	uint32_t src_hw;
 	uint32_t dst_hw;
@@ -76,10 +75,7 @@ static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
 {
 	struct drm_framebuffer *old_fb;
 
-	spin_lock(&dplane->lock);
-	old_fb = dplane->old_fb;
-	dplane->old_fb = fb;
-	spin_unlock(&dplane->lock);
+	old_fb = xchg(&dplane->old_fb, fb);
 
 	if (old_fb)
 		armada_drm_queue_unref_work(dplane->base.dev, old_fb);
@@ -289,10 +285,7 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 	if (plane->fb)
 		drm_framebuffer_unreference(plane->fb);
 
-	spin_lock_irq(&dplane->lock);
-	fb = dplane->old_fb;
-	dplane->old_fb = NULL;
-	spin_unlock_irq(&dplane->lock);
+	fb = xchg(&dplane->old_fb, NULL);
 	if (fb)
 		drm_framebuffer_unreference(fb);
 
@@ -464,7 +457,6 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 	if (!dplane)
 		return -ENOMEM;
 
-	spin_lock_init(&dplane->lock);
 	init_waitqueue_head(&dplane->vbl.wait);
 	armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
 				  dplane);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Use the new drm_universal_plane_init() rather than the legacy
drm_plane_init().

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_overlay.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 5e2dcecbf6ef..8f2b21693c76 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -461,9 +461,11 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 	armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
 				  dplane);
 
-	drm_plane_init(dev, &dplane->base, crtcs, &armada_ovl_plane_funcs,
-		       armada_ovl_formats, ARRAY_SIZE(armada_ovl_formats),
-		       false);
+	ret = drm_universal_plane_init(dev, &dplane->base, crtcs,
+				       &armada_ovl_plane_funcs,
+				       armada_ovl_formats,
+				       ARRAY_SIZE(armada_ovl_formats),
+				       DRM_PLANE_TYPE_OVERLAY);
 	if (ret) {
 		kfree(dplane);
 		return ret;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Introduce a generic armada_plane struct which will eventually be used
for both the primary and overlay planes.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.h    |  5 +++++
 drivers/gpu/drm/armada/armada_overlay.c | 19 ++++++++++---------
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index a86243ef4a51..549b5f538266 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -34,6 +34,11 @@ struct armada_regs {
 struct armada_frame_work;
 struct armada_variant;
 
+struct armada_plane {
+	struct drm_plane	base;
+};
+#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
+
 struct armada_crtc {
 	struct drm_crtc		crtc;
 	const struct armada_variant *variant;
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 8f2b21693c76..e5a5b73a08cb 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -30,7 +30,7 @@ struct armada_ovl_plane_properties {
 };
 
 struct armada_ovl_plane {
-	struct drm_plane base;
+	struct armada_plane base;
 	struct drm_framebuffer *old_fb;
 	uint32_t src_hw;
 	uint32_t dst_hw;
@@ -43,7 +43,8 @@ struct armada_ovl_plane {
 	} vbl;
 	struct armada_ovl_plane_properties prop;
 };
-#define drm_to_armada_ovl_plane(p) container_of(p, struct armada_ovl_plane, base)
+#define drm_to_armada_ovl_plane(p) \
+	container_of(p, struct armada_ovl_plane, base.base)

 static void
@@ -78,7 +79,7 @@ static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
 	old_fb = xchg(&dplane->old_fb, fb);
 
 	if (old_fb)
-		armada_drm_queue_unref_work(dplane->base.dev, old_fb);
+		armada_drm_queue_unref_work(dplane->base.base.dev, old_fb);
 }
 
 /* === Plane support === */
@@ -266,10 +267,10 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 	struct drm_framebuffer *fb;
 	struct armada_crtc *dcrtc;
 
-	if (!dplane->base.crtc)
+	if (!dplane->base.base.crtc)
 		return 0;
 
-	dcrtc = drm_to_armada_crtc(dplane->base.crtc);
+	dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
 	dcrtc->plane = NULL;
 
 	spin_lock_irq(&dcrtc->irq_lock);
@@ -362,9 +363,9 @@ static int armada_ovl_plane_set_property(struct drm_plane *plane,
 		update_attr = true;
 	}
 
-	if (update_attr && dplane->base.crtc)
+	if (update_attr && dplane->base.base.crtc)
 		armada_ovl_update_attr(&dplane->prop,
-				       drm_to_armada_crtc(dplane->base.crtc));
+				       drm_to_armada_crtc(dplane->base.base.crtc));
 
 	return 0;
 }
@@ -461,7 +462,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 	armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
 				  dplane);
 
-	ret = drm_universal_plane_init(dev, &dplane->base, crtcs,
+	ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
 				       &armada_ovl_plane_funcs,
 				       armada_ovl_formats,
 				       ARRAY_SIZE(armada_ovl_formats),
@@ -479,7 +480,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 	dplane->prop.contrast = 0x4000;
 	dplane->prop.saturation = 0x4000;
 
-	mobj = &dplane->base.base;
+	mobj = &dplane->base.base.base;
 	drm_object_attach_property(mobj, priv->colorkey_prop,
 				   0x0101fe);
 	drm_object_attach_property(mobj, priv->colorkey_min_prop,

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Use drm_primary_helper_create_plane() to create our primary plane, and
register the CRTC with drm_crtc_init_with_planes().  This enables the
primary plane to be initialised with the supported format information.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 5d627646601e..b96b77b61337 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -33,6 +33,23 @@ enum csc_mode {
 	CSC_RGB_STUDIO = 2,
 };
 
+static const uint32_t armada_primary_formats[] = {
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+};
+
 /*
  * A note about interlacing.  Let's consider HDMI 1920x1080i.
  * The timing parameters we have from X are:
@@ -1080,6 +1097,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 {
 	struct armada_private *priv = drm->dev_private;
 	struct armada_crtc *dcrtc;
+	struct drm_plane *primary;
 	void __iomem *base;
 	int ret;
 
@@ -1148,7 +1166,17 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	priv->dcrtc[dcrtc->num] = dcrtc;
 
 	dcrtc->crtc.port = port;
-	drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
+
+	primary = drm_primary_helper_create_plane(drm, armada_primary_formats,
+					ARRAY_SIZE(armada_primary_formats));
+	if (!primary)
+		return -ENOMEM;
+
+	ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, primary, NULL,
+					&armada_crtc_funcs);
+	if (ret)
+		goto err_crtc_init;
+
 	drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
 
 	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
@@ -1157,6 +1185,10 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 				   dcrtc->csc_rgb_mode);
 
 	return armada_overlay_plane_create(drm, 1 << dcrtc->num);
+
+err_crtc_init:
+	primary->funcs->destroy(primary);
+	return ret;
 }
 
 static int

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Allocate our own primary plane as an armada_plane.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index b96b77b61337..f146fcf6b274 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -1059,6 +1059,12 @@ static struct drm_crtc_funcs armada_crtc_funcs = {
 	.set_property	= armada_drm_crtc_set_property,
 };
 
+static const struct drm_plane_funcs armada_primary_plane_funcs = {
+	.update_plane	= drm_primary_helper_update,
+	.disable_plane	= drm_primary_helper_disable,
+	.destroy	= drm_primary_helper_destroy,
+};
+
 static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
 	{ CSC_AUTO,        "Auto" },
 	{ CSC_YUV_CCIR601, "CCIR601" },
@@ -1097,7 +1103,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 {
 	struct armada_private *priv = drm->dev_private;
 	struct armada_crtc *dcrtc;
-	struct drm_plane *primary;
+	struct armada_plane *primary;
 	void __iomem *base;
 	int ret;
 
@@ -1167,12 +1173,21 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 
 	dcrtc->crtc.port = port;
 
-	primary = drm_primary_helper_create_plane(drm, armada_primary_formats,
-					ARRAY_SIZE(armada_primary_formats));
+	primary = kzalloc(sizeof(*primary), GFP_KERNEL);
 	if (!primary)
 		return -ENOMEM;
 
-	ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, primary, NULL,
+	ret = drm_universal_plane_init(drm, &primary->base, 0,
+				       &armada_primary_plane_funcs,
+				       armada_primary_formats,
+				       ARRAY_SIZE(armada_primary_formats),
+				       DRM_PLANE_TYPE_PRIMARY);
+	if (ret) {
+		kfree(primary);
+		return ret;
+	}
+
+	ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL,
 					&armada_crtc_funcs);
 	if (ret)
 		goto err_crtc_init;
@@ -1187,7 +1202,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	return armada_overlay_plane_create(drm, 1 << dcrtc->num);
 
 err_crtc_init:
-	primary->funcs->destroy(primary);
+	primary->base.funcs->destroy(&primary->base);
 	return ret;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Provide a common helper to disable either the overlay or the primary
plane.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c    | 33 +++++++++++++++++++++++++++------
 drivers/gpu/drm/armada/armada_crtc.h    |  3 +++
 drivers/gpu/drm/armada/armada_overlay.c |  7 +------
 3 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index f146fcf6b274..007fc5d3eb54 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -700,18 +700,39 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 	return 0;
 }
 
+void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
+	struct drm_plane *plane)
+{
+	u32 sram_para1;
+
+	/*
+	 * Drop our reference on any framebuffer attached to this plane.
+	 * We don't need to NULL this out as drm_plane_force_disable(),
+	 * and __setplane_internal() will do so for an overlay plane, and
+	 * __drm_helper_disable_unused_functions() will do so for the
+	 * primary plane.
+	 */
+	if (plane->fb)
+		drm_framebuffer_unreference(plane->fb);
+
+	/* Power down the Y/U/V FIFOs */
+	sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
+
+	/* Power down most RAMs and FIFOs if this is the primary plane */
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+		sram_para1 |= CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+			      CFG_PDWN32x32 | CFG_PDWN64x66;
+
+	armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1);
+}
+
 /* The mode_config.mutex will be held for this call */
 static void armada_drm_crtc_disable(struct drm_crtc *crtc)
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 
 	armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-	armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true);
-
-	/* Power down most RAMs and FIFOs */
-	writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
-		       CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
-		       CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
+	armada_drm_crtc_plane_disable(dcrtc, crtc->primary);
 }
 
 static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 549b5f538266..500ce0f43f64 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -103,6 +103,9 @@ void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
 
+void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
+	struct drm_plane *plane);
+
 extern struct platform_driver armada_lcd_platform_driver;
 
 #endif
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index e5a5b73a08cb..1032f9b3d5f1 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -279,12 +279,7 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 	dplane->ctrl0 = 0;
 	spin_unlock_irq(&dcrtc->irq_lock);
 
-	/* Power down the Y/U/V FIFOs */
-	armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
-		       dcrtc->base + LCD_SPU_SRAM_PARA1);
-
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
+	armada_drm_crtc_plane_disable(dcrtc, plane);
 
 	fb = xchg(&dplane->old_fb, NULL);
 	if (fb)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Move the write to clear the DMA enable bit, and augment it with clearing
the graphics enable bit for the primary plane.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c    | 12 ++++++++++--
 drivers/gpu/drm/armada/armada_overlay.c |  1 -
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 007fc5d3eb54..89decc5bdcd4 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -703,7 +703,7 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
 	struct drm_plane *plane)
 {
-	u32 sram_para1;
+	u32 sram_para1, dma_ctrl0_mask;
 
 	/*
 	 * Drop our reference on any framebuffer attached to this plane.
@@ -719,9 +719,17 @@ void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
 	sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
 
 	/* Power down most RAMs and FIFOs if this is the primary plane */
-	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
 		sram_para1 |= CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
 			      CFG_PDWN32x32 | CFG_PDWN64x66;
+		dma_ctrl0_mask = CFG_GRA_ENA;
+	} else {
+		dma_ctrl0_mask = CFG_DMA_ENA;
+	}
+
+	spin_lock_irq(&dcrtc->irq_lock);
+	armada_updatel(0, dma_ctrl0_mask, dcrtc->base + LCD_SPU_DMA_CTRL0);
+	spin_unlock_irq(&dcrtc->irq_lock);
 
 	armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1);
 }
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 1032f9b3d5f1..9686d79335a0 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -275,7 +275,6 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 
 	spin_lock_irq(&dcrtc->irq_lock);
 	armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
-	armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
 	dplane->ctrl0 = 0;
 	spin_unlock_irq(&dcrtc->irq_lock);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

It is not necessary to write dplane->ctrl0 under the CRTC spinlock, as
this is only accessed under process context where the DRM locks will
protect us instead.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_overlay.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 9686d79335a0..e7e020d4372a 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -275,9 +275,10 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 
-	dplane->ctrl0 = 0;
 
+	dplane->ctrl0 = 0;
+
 	armada_drm_crtc_plane_disable(dcrtc, plane);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Move the locking for armada_drm_vbl_event_remove() into itself, which
makes this function symmetrical with armada_drm_vbl_event_add().

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c    | 2 ++
 drivers/gpu/drm/armada/armada_overlay.c | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 89decc5bdcd4..e3e6f81593c0 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -211,10 +211,12 @@ void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
 void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
 	struct armada_vbl_event *evt)
 {
+	spin_lock_irq(&dcrtc->irq_lock);
 	if (!list_empty(&evt->node)) {
 		list_del_init(&evt->node);
 		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
 	}
+	spin_unlock_irq(&dcrtc->irq_lock);
 }
 
 static void armada_drm_vbl_event_run(struct armada_crtc *dcrtc)
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index e7e020d4372a..6ec42eb85981 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -273,9 +273,7 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 	dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
 	dcrtc->plane = NULL;
 
-	spin_lock_irq(&dcrtc->irq_lock);
 	armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
-	spin_unlock_irq(&dcrtc->irq_lock);
 
 	dplane->ctrl0 = 0;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Both the CRTC and overlay frames have their own wait queues.  It would
make more sense if these were part of the plane - the primary plane for
the CRTC and overlay plane for the overlay.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c    | 22 ++++++++++++++++++----
 drivers/gpu/drm/armada/armada_crtc.h    |  4 +++-
 drivers/gpu/drm/armada/armada_overlay.c | 12 ++++++++----
 3 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index e3e6f81593c0..46d932bc7678 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -455,7 +455,7 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 		if (work)
 			armada_drm_crtc_complete_frame_work(dcrtc, work);
 
-		wake_up(&dcrtc->frame_wait);
+		wake_up(&drm_to_armada_plane(dcrtc->crtc.primary)->frame_wait);
 	}
 }
 
@@ -571,7 +571,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 		adj->crtc_vtotal, tm, bm);
 
 	/* Wait for pending flips to complete */
-	wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+	wait_event(drm_to_armada_plane(dcrtc->crtc.primary)->frame_wait,
+		   !dcrtc->frame_work);
 
 	drm_crtc_vblank_off(crtc);
 
@@ -688,7 +689,8 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 	armada_reg_queue_end(regs, i);
 
 	/* Wait for pending flips to complete */
-	wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+	wait_event(drm_to_armada_plane(dcrtc->crtc.primary)->frame_wait,
+		   !dcrtc->frame_work);
 
 	/* Take a reference to the new fb as we're using it */
 	drm_framebuffer_reference(crtc->primary->fb);
@@ -1096,6 +1098,13 @@ static const struct drm_plane_funcs armada_primary_plane_funcs = {
 	.destroy	= drm_primary_helper_destroy,
 };
 
+int armada_drm_plane_init(struct armada_plane *plane)
+{
+	init_waitqueue_head(&plane->frame_wait);
+
+	return 0;
+}
+
 static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
 	{ CSC_AUTO,        "Auto" },
 	{ CSC_YUV_CCIR601, "CCIR601" },
@@ -1166,7 +1175,6 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	spin_lock_init(&dcrtc->irq_lock);
 	dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
 	INIT_LIST_HEAD(&dcrtc->vbl_list);
-	init_waitqueue_head(&dcrtc->frame_wait);
 
 	/* Initialize some registers which we don't otherwise set */
 	writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
@@ -1208,6 +1216,12 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	if (!primary)
 		return -ENOMEM;
 
+	ret = armada_drm_plane_init(primary);
+	if (ret) {
+		kfree(primary);
+		return ret;
+	}
+
 	ret = drm_universal_plane_init(drm, &primary->base, 0,
 				       &armada_primary_plane_funcs,
 				       armada_primary_formats,
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 500ce0f43f64..3ec5101e13f7 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -36,9 +36,12 @@ struct armada_variant;
 
 struct armada_plane {
 	struct drm_plane	base;
+	wait_queue_head_t	frame_wait;
 };
 #define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
 
+int armada_drm_plane_init(struct armada_plane *plane);
+
 struct armada_crtc {
 	struct drm_crtc		crtc;
 	const struct armada_variant *variant;
@@ -71,7 +74,6 @@ struct armada_crtc {
 	uint32_t		dumb_ctrl;
 	uint32_t		spu_iopad_ctrl;
 
-	wait_queue_head_t	frame_wait;
 	struct armada_frame_work *frame_work;
 
 	spinlock_t		irq_lock;
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 6ec42eb85981..9a5bab765085 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -39,7 +39,6 @@ struct armada_ovl_plane {
 	struct {
 		struct armada_vbl_event update;
 		struct armada_regs regs[13];
-		wait_queue_head_t wait;
 	} vbl;
 	struct armada_ovl_plane_properties prop;
 };
@@ -90,7 +89,7 @@ static void armada_ovl_plane_vbl(struct armada_crtc *dcrtc, void *data)
 	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
 	armada_ovl_retire_fb(dplane, NULL);
 
-	wake_up(&dplane->vbl.wait);
+	wake_up(&dplane->base.frame_wait);
 }
 
 static int
@@ -163,7 +162,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 			       dcrtc->base + LCD_SPU_SRAM_PARA1);
 	}
 
-	wait_event_timeout(dplane->vbl.wait,
+	wait_event_timeout(dplane->base.frame_wait,
 			   list_empty(&dplane->vbl.update.node),
 			   HZ/25);
 
@@ -451,7 +450,12 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 	if (!dplane)
 		return -ENOMEM;
 
-	init_waitqueue_head(&dplane->vbl.wait);
+	ret = armada_drm_plane_init(&dplane->base);
+	if (ret) {
+		kfree(dplane);
+		return ret;
+	}
+
 	armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
 				  dplane);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:12 +0100

Add a plane work implementation, and move the CRTC framebuffer flip
work to it for the primary plane.  The idea is to have a common
plane work implementation for both the primary and overlay planes.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 102 ++++++++++++++++++++---------------
 drivers/gpu/drm/armada/armada_crtc.h |  15 ++++--
 2 files changed, 70 insertions(+), 47 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 46d932bc7678..0c1a1524f5d5 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -20,6 +20,7 @@
 #include "armada_hw.h"
 
 struct armada_frame_work {
+	struct armada_plane_work work;
 	struct drm_pending_vblank_event *event;
 	struct armada_regs regs[4];
 	struct drm_framebuffer *old_fb;
@@ -190,6 +191,41 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
 	return i;
 }
 
+static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
+	struct armada_plane *plane)
+{
+	struct armada_plane_work *work = xchg(&plane->work, NULL);
+
+	/* Handle any pending frame work. */
+	if (work) {
+		work->fn(dcrtc, plane, work);
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+	}
+}
+
+int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
+	struct armada_plane *plane, struct armada_plane_work *work)
+{
+	int ret;
+
+	ret = drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
+	if (ret) {
+		DRM_ERROR("failed to acquire vblank counter\n");
+		return ret;
+	}
+
+	ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0;
+	if (ret)
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+
+	return ret;
+}
+
+int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout)
+{
+	return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
+}
+
 void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
 	struct armada_vbl_event *evt)
 {
@@ -233,44 +269,31 @@ static void armada_drm_vbl_event_run(struct armada_crtc *dcrtc)
 static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
 	struct armada_frame_work *work)
 {
-	struct drm_device *dev = dcrtc->crtc.dev;
-	int ret;
+	struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
 
-	ret = drm_vblank_get(dev, dcrtc->num);
-	if (ret) {
-		DRM_ERROR("failed to acquire vblank counter\n");
-		return ret;
-	}
-
-	if (cmpxchg(&dcrtc->frame_work, NULL, work)) {
-		drm_vblank_put(dev, dcrtc->num);
-		ret = -EBUSY;
-	}
-
-	return ret;
+	return armada_drm_plane_work_queue(dcrtc, plane, &work->work);
 }
 
 static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc,
-	struct armada_frame_work *work)
+	struct armada_plane *plane, struct armada_plane_work *work)
 {
+	struct armada_frame_work *fwork = container_of(work, struct armada_frame_work, work);
 	struct drm_device *dev = dcrtc->crtc.dev;
 	unsigned long flags;
 
 	spin_lock_irqsave(&dcrtc->irq_lock, flags);
-	armada_drm_crtc_update_regs(dcrtc, work->regs);
+	armada_drm_crtc_update_regs(dcrtc, fwork->regs);
 	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
 
-	if (work->event) {
+	if (fwork->event) {
 		spin_lock_irqsave(&dev->event_lock, flags);
-		drm_send_vblank_event(dev, dcrtc->num, work->event);
+		drm_send_vblank_event(dev, dcrtc->num, fwork->event);
 		spin_unlock_irqrestore(&dev->event_lock, flags);
 	}
 
-	drm_vblank_put(dev, dcrtc->num);
-
 	/* Finally, queue the process-half of the cleanup. */
-	__armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
-	kfree(work);
+	__armada_drm_queue_unref_work(dcrtc->crtc.dev, fwork->old_fb);
+	kfree(fwork);
 }
 
 static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
@@ -290,6 +313,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
 	work = kmalloc(sizeof(*work), GFP_KERNEL);
 	if (work) {
 		int i = 0;
+		work->work.fn = armada_drm_crtc_complete_frame_work;
 		work->event = NULL;
 		work->old_fb = fb;
 		armada_reg_queue_end(work->regs, i);
@@ -310,18 +334,14 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
 
 static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
 {
-	struct armada_frame_work *work;
+	struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
 
 	/*
 	 * Tell the DRM core that vblank IRQs aren't going to happen for
 	 * a while.  This cleans up any pending vblank events for us.
 	 */
 	drm_crtc_vblank_off(&dcrtc->crtc);
-
-	/* Handle any pending flip event. */
-	work = xchg(&dcrtc->frame_work, NULL);
-	if (work)
-		armada_drm_crtc_complete_frame_work(dcrtc, work);
+	armada_drm_plane_work_run(dcrtc, plane);
 }
 
 void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
@@ -450,12 +470,9 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 	spin_unlock(&dcrtc->irq_lock);
 
 	if (stat & GRA_FRAME_IRQ) {
-		struct armada_frame_work *work = xchg(&dcrtc->frame_work, NULL);
-
-		if (work)
-			armada_drm_crtc_complete_frame_work(dcrtc, work);
-
-		wake_up(&drm_to_armada_plane(dcrtc->crtc.primary)->frame_wait);
+		struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
+		armada_drm_plane_work_run(dcrtc, plane);
+		wake_up(&plane->frame_wait);
 	}
 }
 
@@ -571,8 +588,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 		adj->crtc_vtotal, tm, bm);
 
 	/* Wait for pending flips to complete */
-	wait_event(drm_to_armada_plane(dcrtc->crtc.primary)->frame_wait,
-		   !dcrtc->frame_work);
+	armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
+				   MAX_SCHEDULE_TIMEOUT);

@@ -689,8 +706,8 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 	armada_reg_queue_end(regs, i);
 
 	/* Wait for pending flips to complete */
-	wait_event(drm_to_armada_plane(dcrtc->crtc.primary)->frame_wait,
-		   !dcrtc->frame_work);
+	armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
+				   MAX_SCHEDULE_TIMEOUT);
 
 	/* Take a reference to the new fb as we're using it */
 	drm_framebuffer_reference(crtc->primary->fb);
@@ -1013,6 +1030,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 	if (!work)
 		return -ENOMEM;
 
+	work->work.fn = armada_drm_crtc_complete_frame_work;
 	work->event = event;
 	work->old_fb = dcrtc->crtc.primary->fb;
 
@@ -1046,12 +1064,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 	 * Finally, if the display is blanked, we won't receive an
 	 * interrupt, so complete it now.
 	 */
-	if (dpms_blanked(dcrtc->dpms)) {
-		struct armada_frame_work *work = xchg(&dcrtc->frame_work, NULL);
-
-		if (work)
-			armada_drm_crtc_complete_frame_work(dcrtc, work);
-	}
+	if (dpms_blanked(dcrtc->dpms))
+		armada_drm_plane_work_run(dcrtc, drm_to_armada_plane(dcrtc->crtc.primary));
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 3ec5101e13f7..aaad5ab78673 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -31,16 +31,27 @@ struct armada_regs {
 #define armada_reg_queue_end(_r, _i)		\
 	armada_reg_queue_mod(_r, _i, 0, 0, ~0)
 
-struct armada_frame_work;
+struct armada_crtc;
+struct armada_plane;
 struct armada_variant;
 
+struct armada_plane_work {
+	void			(*fn)(struct armada_crtc *,
+				      struct armada_plane *,
+				      struct armada_plane_work *);
+};
+
 struct armada_plane {
 	struct drm_plane	base;
 	wait_queue_head_t	frame_wait;
+	struct armada_plane_work *work;
 };
 #define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
 
 int armada_drm_plane_init(struct armada_plane *plane);
+int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
+	struct armada_plane *plane, struct armada_plane_work *work);
+int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout);
 
 struct armada_crtc {
 	struct drm_crtc		crtc;
@@ -74,8 +85,6 @@ struct armada_crtc {
 	uint32_t		dumb_ctrl;
 	uint32_t		spu_iopad_ctrl;
 
-	struct armada_frame_work *frame_work;
-
 	spinlock_t		irq_lock;
 	uint32_t		irq_ena;
 	struct list_head	vbl_list;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Convert the overlay plane to use the generic armada plane worker
infrastructure which is shared with the primary plane.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c    | 48 +++++++++------------------------
 drivers/gpu/drm/armada/armada_crtc.h    | 20 ++------------
 drivers/gpu/drm/armada/armada_overlay.c | 27 +++++++++----------
 3 files changed, 26 insertions(+), 69 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 0c1a1524f5d5..418dbfad4271 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -226,44 +226,15 @@ int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout)
 	return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
 }
 
-void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
-	struct armada_vbl_event *evt)
+struct armada_plane_work *armada_drm_plane_work_cancel(
+	struct armada_crtc *dcrtc, struct armada_plane *plane)
 {
-	unsigned long flags;
-	bool not_on_list;
-
-	WARN_ON(drm_vblank_get(dcrtc->crtc.dev, dcrtc->num));
-
-	spin_lock_irqsave(&dcrtc->irq_lock, flags);
-	not_on_list = list_empty(&evt->node);
-	if (not_on_list)
-		list_add_tail(&evt->node, &dcrtc->vbl_list);
-	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+	struct armada_plane_work *work = xchg(&plane->work, NULL);
 
-	if (!not_on_list)
+	if (work)
 
-static void armada_drm_vbl_event_run(struct armada_crtc *dcrtc)
+	return work;
 }
 
 static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
@@ -429,6 +400,7 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
 static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 {
 	void __iomem *base = dcrtc->base;
+	struct drm_plane *ovl_plane;
 
 	if (stat & DMA_FF_UNDERFLOW)
 		DRM_ERROR("video underflow on crtc %u\n", dcrtc->num);
@@ -439,7 +411,12 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 		drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
 
 	spin_lock(&dcrtc->irq_lock);
-	armada_drm_vbl_event_run(dcrtc);
+	ovl_plane = dcrtc->plane;
+	if (ovl_plane) {
+		struct armada_plane *plane = drm_to_armada_plane(ovl_plane);
+		armada_drm_plane_work_run(dcrtc, plane);
+		wake_up(&plane->frame_wait);
+	}
 
 	if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
 		int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
@@ -1188,7 +1165,6 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
 	spin_lock_init(&dcrtc->irq_lock);
 	dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
-	INIT_LIST_HEAD(&dcrtc->vbl_list);
 
 	/* Initialize some registers which we don't otherwise set */
 	writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index aaad5ab78673..04fdd22d483b 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -52,6 +52,8 @@ int armada_drm_plane_init(struct armada_plane *plane);
 int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
 	struct armada_plane *plane, struct armada_plane_work *work);
 int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout);
+struct armada_plane_work *armada_drm_plane_work_cancel(
+	struct armada_crtc *dcrtc, struct armada_plane *plane);
 
 struct armada_crtc {
 	struct drm_crtc		crtc;
@@ -87,27 +89,9 @@ struct armada_crtc {
 
 	spinlock_t		irq_lock;
 	uint32_t		irq_ena;
-	struct list_head	vbl_list;
 };
 #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
 
index 9a5bab765085..5c22b380f8f3 100644
@@ -37,7 +37,7 @@ struct armada_ovl_plane {
 	uint32_t dst_yx;
 	uint32_t ctrl0;
-		struct armada_vbl_event update;
@@ -82,14 +82,13 @@ static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
 }
 
 /* === Plane support === */
-static void armada_ovl_plane_vbl(struct armada_crtc *dcrtc, void *data)
+static void armada_ovl_plane_work(struct armada_crtc *dcrtc,
+	struct armada_plane *plane, struct armada_plane_work *work)
 {
-	struct armada_ovl_plane *dplane = data;
+	struct armada_ovl_plane *dplane = container_of(plane, struct armada_ovl_plane, base);
 
 	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
 	armada_ovl_retire_fb(dplane, NULL);
-
-	wake_up(&dplane->base.frame_wait);
 }
 
 static int
@@ -162,9 +161,8 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 			       dcrtc->base + LCD_SPU_SRAM_PARA1);
 	}
 
-	wait_event_timeout(dplane->base.frame_wait,
-			   list_empty(&dplane->vbl.update.node),
-			   HZ/25);
+	if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
+		armada_drm_plane_work_cancel(dcrtc, &dplane->base);
 
 	if (plane->fb != fb) {
 		struct armada_gem_object *obj = drm_fb_obj(fb);
@@ -255,7 +253,8 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 	}
 	if (idx) {
 		armada_reg_queue_end(dplane->vbl.regs, idx);
-		armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
+		armada_drm_plane_work_queue(dcrtc, &dplane->base,
+					    &dplane->vbl.work);
 	}
 	return 0;
 }
@@ -270,14 +269,13 @@ static int armada_ovl_plane_disable(struct drm_plane *plane)
 		return 0;
 
-	dcrtc->plane = NULL;
 
-	armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
+	armada_drm_plane_work_cancel(dcrtc, &dplane->base);
+	armada_drm_crtc_plane_disable(dcrtc, plane);
 
+	dcrtc->plane = NULL;
 	dplane->ctrl0 = 0;
 
-	armada_drm_crtc_plane_disable(dcrtc, plane);
-
 	fb = xchg(&dplane->old_fb, NULL);
 	if (fb)
 		drm_framebuffer_unreference(fb);
@@ -456,8 +454,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
 		return ret;
 	}
 
-	armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
-				  dplane);
+	dplane->vbl.work.fn = armada_ovl_plane_work;
 
 	ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
 				       &armada_ovl_plane_funcs,

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Move the wakeup for the frame wait into the armada plane work, to
ensure that it is woken up every time we run a work.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/armada_crtc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 418dbfad4271..cebcab560626 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -201,6 +201,8 @@ static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
 		work->fn(dcrtc, plane, work);
 		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
 	}
+
+	wake_up(&plane->frame_wait);
 }
 
 int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
@@ -415,7 +417,6 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 	if (ovl_plane) {
 		struct armada_plane *plane = drm_to_armada_plane(ovl_plane);
 		armada_drm_plane_work_run(dcrtc, plane);
-		wake_up(&plane->frame_wait);
 	}
 
 	if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
@@ -449,7 +450,6 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 	if (stat & GRA_FRAME_IRQ) {
 		struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
 		armada_drm_plane_work_run(dcrtc, plane);
-		wake_up(&plane->frame_wait);
 	}
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Restructure the IOMMU handling to allow Etnaviv DRM to work on v4.1
kernels, as well as previous kernel versions.  This also allows us
to implement runtime PM properly for Dove, where the GPU is powered
down and loses the MMU table pointer.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c   |  10 +++-
 drivers/staging/etnaviv/etnaviv_iommu.c | 101 +++++++++++++++++---------------
 drivers/staging/etnaviv/etnaviv_iommu.h |   2 +
 3 files changed, 66 insertions(+), 47 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 3b1ec50050e6..2fbb96f23c0d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -436,7 +436,8 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, 0x0);
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, 0x0);
 
-	/* FIXME: we need to program the GPU table pointer(s) here */
+	/* setup the MMU page table pointers */
+	etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain);
 
 	/* Start command processor */
 	words = etnaviv_buffer_init(gpu);
@@ -460,6 +461,13 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	bool mmuv2;
 
 	etnaviv_hw_identify(gpu);
+
+	if (gpu->identity.model == 0) {
+		dev_err(gpu->dev, "Unknown GPU model\n");
+		pm_runtime_put_autosuspend(gpu->dev);
+		return -ENXIO;
+	}
+
 	ret = etnaviv_hw_reset(gpu);
 	if (ret)
 		return ret;
diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index f12a74d10581..e500305534b4 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 Christian Gmeiner <[email-masked]>
-  *
+ *
  * 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.
@@ -36,12 +36,19 @@ struct etnaviv_iommu_domain_pgtable {
 };
 
 struct etnaviv_iommu_domain {
+	struct iommu_domain domain;
+	struct device *dev;
 	void *bad_page_cpu;
 	dma_addr_t bad_page_dma;
 	struct etnaviv_iommu_domain_pgtable pgtable;
 	spinlock_t map_lock;
 };
 
+static struct etnaviv_iommu_domain *to_etnaviv_domain(struct iommu_domain *domain)
+{
+	return container_of(domain, struct etnaviv_iommu_domain, domain);
+}
+
 static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable,
 			 size_t size)
 {
@@ -79,32 +86,27 @@ static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
 	pgtable->pgtable[index] = paddr;
 }
 
-static int etnaviv_iommu_domain_init(struct iommu_domain *domain)
+static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain)
 {
-	struct etnaviv_iommu_domain *etnaviv_domain;
 	uint32_t *p;
 	int ret, i;
 
-	etnaviv_domain = kmalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
-	if (!etnaviv_domain)
-		return -ENOMEM;
-
-	etnaviv_domain->bad_page_cpu = dma_alloc_coherent(NULL, SZ_4K,
+	etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
+						  SZ_4K,
 						  &etnaviv_domain->bad_page_dma,
 						  GFP_KERNEL);
-	if (!etnaviv_domain->bad_page_cpu) {
-		kfree(etnaviv_domain);
+	if (!etnaviv_domain->bad_page_cpu)
 		return -ENOMEM;
-	}
+
 	p = etnaviv_domain->bad_page_cpu;
 	for (i = 0; i < SZ_4K / 4; i++)
 		*p++ = 0xdead55aa;
 
 	ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE);
 	if (ret < 0) {
-		dma_free_coherent(NULL, SZ_4K, etnaviv_domain->bad_page_cpu,
+		dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+				  etnaviv_domain->bad_page_cpu,
 				  etnaviv_domain->bad_page_dma);
-		kfree(etnaviv_domain);
 		return ret;
 	}
 
@@ -113,26 +115,27 @@ static int etnaviv_iommu_domain_init(struct iommu_domain *domain)
 			etnaviv_domain->bad_page_dma;
 
 	spin_lock_init(&etnaviv_domain->map_lock);
-	domain->priv = etnaviv_domain;
+
 	return 0;
 }
 
-static void etnaviv_iommu_domain_destroy(struct iommu_domain *domain)
+static void etnaviv_domain_free(struct iommu_domain *domain)
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);

-	dma_free_coherent(NULL, SZ_4K, etnaviv_domain->bad_page_cpu,
+	dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+			  etnaviv_domain->bad_page_cpu,
 			  etnaviv_domain->bad_page_dma);
+
 	kfree(etnaviv_domain);
-	domain->priv = NULL;
 }
 
 static int etnaviv_iommu_map(struct iommu_domain *domain, unsigned long iova,
 	   phys_addr_t paddr, size_t size, int prot)
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
 
 	if (size != SZ_4K)
 		return -EINVAL;
@@ -147,7 +150,7 @@ static int etnaviv_iommu_map(struct iommu_domain *domain, unsigned long iova,
 static size_t etnaviv_iommu_unmap(struct iommu_domain *domain,
 	unsigned long iova, size_t size)
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
 
 	if (size != SZ_4K)
 		return -EINVAL;
@@ -163,41 +166,26 @@ static size_t etnaviv_iommu_unmap(struct iommu_domain *domain,
 static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,
 	dma_addr_t iova)
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = domain->priv;
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
 
 	return pgtable_read(&etnaviv_domain->pgtable, iova);
 }
 
 static struct iommu_ops etnaviv_iommu_ops = {
-		.domain_init = etnaviv_iommu_domain_init,
-		.domain_destroy = etnaviv_iommu_domain_destroy,
-		.map = etnaviv_iommu_map,
-		.unmap = etnaviv_iommu_unmap,
-		.iova_to_phys = etnaviv_iommu_iova_to_phys,
-		.pgsize_bitmap = SZ_4K,
+	.domain_free = etnaviv_domain_free,
+	.map = etnaviv_iommu_map,
+	.unmap = etnaviv_iommu_unmap,
+	.iova_to_phys = etnaviv_iommu_iova_to_phys,
+	.pgsize_bitmap = SZ_4K,
 };
 
-struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
+void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
+	struct iommu_domain *domain)
 {
-	struct iommu_domain *domain;
-	struct etnaviv_iommu_domain *etnaviv_domain;
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
 	uint32_t pgtable;
-	int ret;
-
-	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
-	if (!domain)
-		return NULL;
-
-	domain->ops = &etnaviv_iommu_ops;
-	domain->geometry.aperture_start = GPU_MEM_START;
-	domain->geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1;
-
-	ret = domain->ops->domain_init(domain);
-	if (ret)
-		goto out_free;
 
 	/* set page table address in MC */
-	etnaviv_domain = domain->priv;
 	pgtable = (uint32_t)etnaviv_domain->pgtable.paddr;
 
 	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
@@ -205,10 +193,31 @@ struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
 	gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
+}
+
+struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain;
+	int ret;
 
-	return domain;
+	etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
+	if (!etnaviv_domain)
+		return NULL;
+
+	etnaviv_domain->dev = gpu->dev;
+
+	etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
+	etnaviv_domain->domain.ops = &etnaviv_iommu_ops;
+	etnaviv_domain->domain.geometry.aperture_start = GPU_MEM_START;
+	etnaviv_domain->domain.geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1;
+
+	ret = __etnaviv_iommu_init(etnaviv_domain);
+	if (ret)
+		goto out_free;
+
+	return &etnaviv_domain->domain;
 
 out_free:
-	kfree(domain);
+	kfree(etnaviv_domain);
 	return NULL;
 }
diff --git a/drivers/staging/etnaviv/etnaviv_iommu.h b/drivers/staging/etnaviv/etnaviv_iommu.h
index c0c359d4f166..cf45503f6b6f 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.h
+++ b/drivers/staging/etnaviv/etnaviv_iommu.h
@@ -21,6 +21,8 @@
 struct etnaviv_gpu;
 
 struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu);
+void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
+	struct iommu_domain *domain);
 struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
 
 #endif /* __ETNAVIV_IOMMU_H__ */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Add support to append an END command to the GPU command stream so we
cleanly shutdown the GPU on driver removal and suspend.  This properly
quiesces the GPU, allowing it to come back up on SoCs such as iMX6.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 11 ++++++++++
 drivers/staging/etnaviv/etnaviv_drv.h    |  1 +
 drivers/staging/etnaviv/etnaviv_gpu.c    | 36 +++++++++++++++++++++++++++++++-
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index d83daff2bf26..4c9e20765a5d 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -135,6 +135,17 @@ u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 	return buffer->offset;
 }
 
+void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
+{
+	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
+
+	/* Replace the last WAIT with an END */
+	buffer->offset -= 4;
+
+	CMD_END(buffer);
+	mb();
+}
+
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit)
 {
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 840d002ca68a..68fcf2c491e6 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -112,6 +112,7 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 	uintptr_t ptr, uint32_t size, uint32_t flags, uint32_t *handle);
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
+void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
 bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 2fbb96f23c0d..8b661d41f55d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -962,6 +962,40 @@ static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu)
 	return 0;
 }
 
+static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
+{
+	if (gpu->buffer) {
+		unsigned long timeout;
+
+		/* Replace the last WAIT with END */
+		etnaviv_buffer_end(gpu);
+
+		/*
+		 * We know that only the FE is busy here, this should
+		 * happen quickly (as the WAIT is only 200 cycles).  If
+		 * we fail, just warn and continue.
+		 */
+		timeout = jiffies + msecs_to_jiffies(100);
+		do {
+			u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
+
+			if ((idle & gpu->idle_mask) == gpu->idle_mask)
+				break;
+
+			if (time_is_before_jiffies(timeout)) {
+				dev_warn(gpu->dev,
+					 "timed out waiting for idle: idle=0x%x\n",
+					 idle);
+				break;
+			}
+
+			udelay(5);
+		} while (1);
+	}
+
+	return etnaviv_gpu_clk_disable(gpu);
+}
+
 static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	void *data)
 {
@@ -1011,7 +1045,7 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,

-	etnaviv_gpu_clk_disable(gpu);
+	etnaviv_gpu_hw_suspend(gpu);
 
 	if (gpu->buffer) {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

The GPU requires a double-write to set the clock divisor.  Rather than
open-coding this knowledge in a couple of places, provide a helper to
do this instead.  This avoids spreading this knowledge around the
driver.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 8b661d41f55d..7199d7963f13 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -307,6 +307,13 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 	etnaviv_hw_specs(gpu);
 }
 
+static void etnaviv_gpu_load_clock(struct etnaviv_gpu *gpu, u32 clock)
+{
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock |
+		  VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD);
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
+}
+
 static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 {
 	u32 control, idle;
@@ -328,9 +335,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 			  VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
 
 		/* enable clock */
-		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control |
-			  VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD);
-		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+		etnaviv_gpu_load_clock(gpu, control);
 
 		/* Wait for stable clock.  Vivante's code waited for 1ms */
 		usleep_range(1000, 10000);
@@ -394,9 +399,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 		  VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
 
 	/* enable clock */
-	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control |
-		  VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD);
-	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+	etnaviv_gpu_load_clock(gpu, control);
 
 	return 0;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Add runtime PM support to etnaviv.  We need to be careful about locking
here, otherwise we can end up with large lock dependency loops, which
are avoided by ensuring that we always "get" the runtime PM state
outside of the DRM struct_mutex.

This needs careful handling in the command stream submission path, where
we need to wake the GPU early to avoid this problem.

We generally manage the runtime PM state based on the GPU events: when
we allocate an event upon command submission, we take a reference on the
runtime PM state (it will already have been resumed at submission time.)

When we receive an interrupt, and free an event, we "put" the runtime
state.  When all events are eventually freed, the runtime PM state
will then indicate that it can attempt to suspend the device.

The suspend callback will check that the GPU modules (except for the
front end) are idle before suspending.  This way we ensure that the GPU
is properly idle, and we will retry the suspend later if not.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c        |  43 ++++---
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  23 ++++
 drivers/staging/etnaviv/etnaviv_gpu.c        | 166 ++++++++++++++++++++++++++-
 drivers/staging/etnaviv/etnaviv_gpu.h        |   4 +-
 4 files changed, 211 insertions(+), 25 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 1dc8d46ad66b..c5e606a183d8 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -188,23 +188,6 @@ static void etnaviv_preclose(struct drm_device *dev, struct drm_file *file)
  */
 
 #ifdef CONFIG_DEBUG_FS
-static int etnaviv_gpu_show(struct drm_device *dev, struct seq_file *m)
-{
-	struct etnaviv_drm_private *priv = dev->dev_private;
-	struct etnaviv_gpu *gpu;
-	unsigned int i;
-
-	for (i = 0; i < ETNA_MAX_PIPES; i++) {
-		gpu = priv->gpu[i];
-		if (gpu) {
-			seq_printf(m, "%s Status:\n", dev_name(gpu->dev));
-			etnaviv_gpu_debugfs(gpu, m);
-		}
-	}
-
-	return 0;
-}
-
 static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m)
 {
 	struct etnaviv_drm_private *priv = dev->dev_private;
@@ -267,8 +250,32 @@ static int show_locked(struct seq_file *m, void *arg)
 	return ret;
 }
 
+static int show_each_gpu(struct seq_file *m, void *arg)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gpu *gpu;
+	int (*show)(struct etnaviv_gpu *gpu, struct seq_file *m) =
+			node->info_ent->data;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		gpu = priv->gpu[i];
+		if (!gpu)
+			continue;
+
+		ret = show(gpu, m);
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}
+
 static struct drm_info_list etnaviv_debugfs_list[] = {
-		{"gpu", show_locked, 0, etnaviv_gpu_show},
+		{"gpu", show_each_gpu, 0, etnaviv_gpu_debugfs},
 		{"gem", show_locked, 0, etnaviv_gem_show},
 		{ "mm", show_locked, 0, etnaviv_mm_show },
 		{"mmu", show_locked, 0, etnaviv_mmu_show},
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 495f7a8e9296..ee24fdc5eafa 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -321,6 +321,27 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (args->nr_cmds > MAX_CMDS)
 		return -EINVAL;
 
+	/*
+	 * Avoid big circular locking dependency loops:
+	 * - reading debugfs results in mmap_sem depending on i_mutex_key#3
+	 *   (iterate_dir -> filldir64)
+	 * - struct_mutex depends on mmap_sem
+	 *   (vm_mmap_pgoff -> drm_gem_mmap)
+	 * then if we try to do a get_sync() under struct_mutex,
+	 * - genpd->lock depends on struct_mutex
+	 *   (etnaviv_ioctl_gem_submit -> pm_genpd_runtime_resume)
+	 * - (regulator) rdev->mutex depends on genpd->lock
+	 *   (pm_genpd_poweron -> regulator_enable)
+	 * - i_mutex_key#3 depends on rdev->mutex
+	 *   (create_regulator -> debugfs::start_creating)
+	 * and lockdep rightfully explodes.
+	 *
+	 * Avoid this by getting runtime PM outside of the struct_mutex lock.
+	 */
+	ret = etnaviv_gpu_pm_get_sync(gpu);
+	if (ret < 0)
+		return ret;
+
 	mutex_lock(&dev->struct_mutex);
 
 	submit = submit_create(dev, gpu, args->nr_bos);
@@ -419,6 +440,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		submit_cleanup(submit, !!ret);
 	mutex_unlock(&dev->struct_mutex);
 
+	etnaviv_gpu_pm_put(gpu);
+
 	/*
 	 * If we're returning -EAGAIN, it could be due to the userptr code
 	 * wanting to run its workqueue outside of the struct_mutex.
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 7199d7963f13..42b9280b19ad 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -463,6 +463,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	enum etnaviv_iommu_version version;
 	bool mmuv2;
 
+	ret = pm_runtime_get_sync(gpu->dev);
+	if (ret < 0)
+		return ret;
+
 	etnaviv_hw_identify(gpu);
 
 	if (gpu->identity.model == 0) {
@@ -473,7 +477,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)

 	/* Setup IOMMU.. eventually we will (I think) do this once per context
 	 * and have separate page tables per context.  For now, to keep things
@@ -524,9 +528,15 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	/* Now program the hardware */
 	etnaviv_gpu_hw_init(gpu);
 
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
+
 	return 0;
 
 fail:
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
+
 	return ret;
 }
 
@@ -555,13 +565,26 @@ static void verify_dma(struct etnaviv_gpu *gpu, struct dma_debug *debug)
 	}
 }
 
-void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
+int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 {
 	struct dma_debug debug;
-	u32 dma_lo = gpu_read(gpu, VIVS_FE_DMA_LOW);
-	u32 dma_hi = gpu_read(gpu, VIVS_FE_DMA_HIGH);
-	u32 axi = gpu_read(gpu, VIVS_HI_AXI_STATUS);
-	u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
+	u32 dma_lo, dma_hi, axi, idle;
+	int ret;
+
+	seq_printf(m, "%s Status:\n", dev_name(gpu->dev));
+
+	ret = pm_runtime_get_sync(gpu->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = mutex_lock_interruptible(&gpu->drm->struct_mutex);
+	if (ret < 0)
+		goto err_rpm;
+
+	dma_lo = gpu_read(gpu, VIVS_FE_DMA_LOW);
+	dma_hi = gpu_read(gpu, VIVS_FE_DMA_HIGH);
+	axi = gpu_read(gpu, VIVS_HI_AXI_STATUS);
+	idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
 
 	verify_dma(gpu, &debug);
 
@@ -623,6 +646,16 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 	seq_printf(m, "\t state 1: 0x%08x\n", debug.state[1]);
 	seq_printf(m, "\t last fetch 64 bit word: 0x%08x 0x%08x\n",
 		   dma_lo, dma_hi);
+
+	ret = 0;
+
+	mutex_unlock(&gpu->drm->struct_mutex);
+
+err_rpm:
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
+
+	return ret;
 }
 #endif
 
@@ -825,6 +858,17 @@ void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
 	queue_work(priv->wq, &gpu->retire_work);
 }
 
+int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu)
+{
+	return pm_runtime_get_sync(gpu->dev);
+}
+
+void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu)
+{
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
+}
+
 /* add bo's to gpu's ring, and kick gpu: */
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx)
@@ -832,6 +876,11 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct drm_device *dev = gpu->drm;
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	unsigned int event, i;
+	int ret;
+
+	ret = pm_runtime_get_sync(gpu->dev);
+	if (ret < 0)
+		return ret;
 
 	/*
 	 * TODO
@@ -845,6 +894,7 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 		return -EBUSY;
 	}
 
@@ -898,6 +948,8 @@ static irqreturn_t irq_handler(int irq, void *data)
 	if (intr != 0) {
 		int event;
 
+		pm_runtime_mark_last_busy(gpu->dev);
+
 		dev_dbg(gpu->dev, "intr 0x%08x\n", intr);
 
 		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR) {
@@ -923,6 +975,15 @@ static irqreturn_t irq_handler(int irq, void *data)
 			if (fence_after(gpu->event[event].fence, gpu->retired_fence))
 				gpu->retired_fence = gpu->event[event].fence;
 			event_free(gpu, event);
+
+			/*
+			 * We need to balance the runtime PM count caused by
+			 * each submission.  Upon submission, we increment
+			 * the runtime PM counter, and allocate one event.
+			 * So here, we put the runtime PM count for each
+			 * completed event.
+			 */
+			pm_runtime_put_autosuspend(gpu->dev);
 		}
 
 		etnaviv_gpu_retire(gpu);
@@ -999,6 +1060,29 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
 	return etnaviv_gpu_clk_disable(gpu);
 }
 
+static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
+{
+	struct drm_device *drm = gpu->drm;
+	uint32_t clock;
+	int ret;
+
+	ret = mutex_lock_killable(&drm->struct_mutex);
+	if (ret)
+		return ret;
+
+	clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
+		VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40);
+
+	etnaviv_gpu_load_clock(gpu, clock);
+	etnaviv_gpu_hw_init(gpu);
+
+	gpu->switch_context = true;
+
+	mutex_unlock(&drm->struct_mutex);
+
+	return 0;
+}
+
 static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	void *data)
 {
@@ -1020,7 +1104,11 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,

+#ifdef CONFIG_PM
+	ret = pm_runtime_get_sync(gpu->dev);
+#else
 	ret = etnaviv_gpu_clk_enable(gpu);
+#endif
 	if (ret < 0)
 		return ret;
 
@@ -1032,6 +1120,10 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 
 	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
 			(unsigned long)gpu);
+
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
+
 	return 0;
 fail:
 	return -1;
@@ -1048,7 +1140,12 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,

+#ifdef CONFIG_PM
+	pm_runtime_get_sync(gpu->dev);
+	pm_runtime_put_sync_suspend(gpu->dev);
+#else
 	etnaviv_gpu_hw_suspend(gpu);
+#endif
 
 	if (gpu->buffer) {
 		drm_gem_object_unreference_unlocked(gpu->buffer);
@@ -1147,6 +1244,15 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	/* TODO: figure out max mapped size */
 	dev_set_drvdata(dev, gpu);
 
+	/*
+	 * We treat the device as initially suspended.  The runtime PM
+	 * autosuspend delay is rather arbitary: no measurements have
+	 * yet been performed to determine an appropriate value.
+	 */
+	pm_runtime_use_autosuspend(gpu->dev);
+	pm_runtime_set_autosuspend_delay(gpu->dev, 200);
+	pm_runtime_enable(gpu->dev);
+
 	err = component_add(&pdev->dev, &gpu_ops);
 	if (err < 0) {
 		dev_err(&pdev->dev, "failed to register component: %d\n", err);
@@ -1162,13 +1268,61 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
 {
 	component_del(&pdev->dev, &gpu_ops);
+	pm_runtime_disable(&pdev->dev);
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int etnaviv_gpu_rpm_suspend(struct device *dev)
+{
+	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+	u32 idle, mask;
+
+	/* If we have outstanding fences, we're not idle */
+	if (gpu->retired_fence != gpu->submitted_fence)
+		return -EBUSY;
+
+	/* Check whether the hardware (except FE) is idle */
+	mask = gpu->idle_mask & ~VIVS_HI_IDLE_STATE_FE;
+	idle = gpu_read(gpu, VIVS_HI_IDLE_STATE) & mask;
+	if (idle != mask)
+		return -EBUSY;
+
+	return etnaviv_gpu_hw_suspend(gpu);
+}
+
+static int etnaviv_gpu_rpm_resume(struct device *dev)
+{
+	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+	int ret;
+
+	ret = etnaviv_gpu_clk_enable(gpu);
+	if (ret)
+		return ret;
+
+	/* Re-initialise the basic hardware state */
+	if (gpu->drm && gpu->buffer) {
+		ret = etnaviv_gpu_hw_resume(gpu);
+		if (ret) {
+			etnaviv_gpu_clk_disable(gpu);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops etnaviv_gpu_pm_ops = {
+	SET_RUNTIME_PM_OPS(etnaviv_gpu_rpm_suspend, etnaviv_gpu_rpm_resume,
+			   NULL)
+};
+
 struct platform_driver etnaviv_gpu_driver = {
 	.driver = {
 		.name = "etnaviv-gpu",
 		.owner = THIS_MODULE,
+		.pm = &etnaviv_gpu_pm_ops,
 		.of_match_table = etnaviv_gpu_match,
 	},
 	.probe = etnaviv_gpu_platform_probe,
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 9035d9c78475..d37b29fc87cc 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -142,12 +142,14 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
 int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
 
 #ifdef CONFIG_DEBUG_FS
-void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
+int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
 #endif
 
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
+int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
+void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

On iMX6, memory starts at 256MB physical, and if we have 2GB of memory,
this causes it to extend beyond 0x80000000.  All memory is available for
DMA coherent allocations, which can result in the command buffers being
allocated from addresses above 0x80000000.

However, the Vivante GPU requires that command buffers are in the linear
space (first 2GB of GPU addresses.)  Trying to program an address with
bit 31 set results in it being ignored.

The Vivante GPU has a solution to this: they provide a set of memory
base address registers, which are added to the GPU linear space address
before appearing on the bus.  If we program these to the base address of
physical memory, we can use all 2GB of RAM.

There are other reasons to do this: firstly, other SoCs may start their
physical memory at other bus addresses, which may further reduce the
range of physical addresses for command buffers.  Secondly, it prevents
the GPU being able to access addresses below RAM, which in the iMX6
case are hardware registers.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 29 +++++++++++++++++------------
 drivers/staging/etnaviv/etnaviv_gem.c    |  3 ++-
 drivers/staging/etnaviv/etnaviv_gpu.c    | 20 ++++++++++++++------
 drivers/staging/etnaviv/etnaviv_gpu.h    |  3 +++
 drivers/staging/etnaviv/etnaviv_mmu.c    |  4 ++--
 drivers/staging/etnaviv/etnaviv_mmu.h    |  2 +-
 6 files changed, 39 insertions(+), 22 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 4c9e20765a5d..919e2650a9ae 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -106,14 +106,19 @@ static void cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
 		       VIVS_GL_PIPE_SELECT_PIPE(pipe));
 }
 
+static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_gem_object *obj)
+{
+	return obj->paddr - gpu->memory_base;
+}
+
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_object *obj, u32 off, u32 len)
 {
 	u32 size = obj->base.size;
 	u32 *ptr = obj->vaddr + off;
 
-	dev_info(gpu->dev, "virt %p phys 0x%llx free 0x%08x\n",
-			ptr, (u64)obj->paddr + off, size - len * 4 - off);
+	dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n",
+			ptr, gpu_va(gpu, obj) + off, size - len * 4 - off);
 
 	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
 			ptr, len * 4, 0);
@@ -130,7 +135,7 @@ u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 	cmd_select_pipe(buffer, gpu->pipe);
 
 	CMD_WAIT(buffer);
-	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
 
 	return buffer->offset;
 }
@@ -177,7 +182,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
 	/* save offset back into main buffer */
 	back = buffer->offset + reserve_size - 6;
-	link_target = buffer->paddr + buffer->offset * 4;
+	link_target = gpu_va(gpu, buffer) + buffer->offset * 4;
 	link_size = 6;
 
 	if (gpu->mmu->need_flush) {
@@ -192,9 +197,9 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		cmd->offset = submit->cmd[i].offset + submit->cmd[i].size;
 
 		if (drm_debug & DRM_UT_DRIVER)
-			pr_info("stream link from buffer %u to 0x%llx @ 0x%llx %p\n",
-				i, (u64)link_target,
-				(u64)cmd->paddr + cmd->offset * 4,
+			pr_info("stream link from buffer %u to 0x%08x @ 0x%08x %p\n",
+				i, link_target,
+				gpu_va(gpu, cmd) + cmd->offset * 4,
 				cmd->vaddr + cmd->offset * 4);
 
 		/* jump back from last cmd to main buffer */
@@ -203,7 +208,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		/* update the size */
 		submit->cmd[i].size = cmd->offset - submit->cmd[i].offset;
 
-		link_target = cmd->paddr + submit->cmd[i].offset * 4;
+		link_target = gpu_va(gpu, cmd) + submit->cmd[i].offset * 4;
 		link_size = submit->cmd[i].size * 2;
 	}
 
@@ -217,13 +222,13 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
 		pr_info("link op: %p\n", lw);
 		pr_info("link addr: %p\n", lw + 1);
-		pr_info("addr: 0x%llx\n", (u64)link_target);
-		pr_info("back: 0x%llx\n", (u64)buffer->paddr + (back * 4));
+		pr_info("addr: 0x%08x\n", link_target);
+		pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + (back * 4));
 		pr_info("event: %d\n", event);
 	}
 
 	if (gpu->mmu->need_flush) {
-		uint32_t new_target = buffer->paddr + buffer->offset *
+		uint32_t new_target = gpu_va(gpu, buffer) + buffer->offset *
 					sizeof(uint32_t);
 
 		/* Add the MMU flush */
@@ -247,7 +252,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
 	/* append WAIT/LINK to main buffer */
 	CMD_WAIT(buffer);
-	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
 
 	/* Change WAIT into a LINK command; write the address first. */
 	*(lw + 1) = link_target;
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index e237100539cb..a4df6b4f60c6 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -317,7 +317,8 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 		if (IS_ERR(pages))
 			return PTR_ERR(pages);
 
-		ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj);
+		ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj,
+				gpu->memory_base);
 	}
 
 	if (!ret)
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 42b9280b19ad..69f4e45e1f77 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -433,11 +433,11 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 		  VIVS_HI_AXI_CONFIG_ARCACHE(2));
 
 	/* set base addresses */
-	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, 0x0);
-	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, 0x0);
-	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, 0x0);
-	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, 0x0);
-	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, 0x0);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base);
+	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
 
 	/* setup the MMU page table pointers */
 	etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain);
@@ -450,7 +450,7 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 
 	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
 	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
-		  etnaviv_gem_paddr_locked(gpu->buffer));
+		  etnaviv_gem_paddr_locked(gpu->buffer) - gpu->memory_base);
 	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
 		  VIVS_FE_COMMAND_CONTROL_ENABLE |
 		  VIVS_FE_COMMAND_CONTROL_PREFETCH(words));
@@ -1205,6 +1205,14 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 
 	gpu->dev = &pdev->dev;
 
+	/*
+	 * Set the GPU base address to the start of physical memory.  This
+	 * ensures that if we have up to 2GB, the v1 MMU can address the
+	 * highest memory.  This is important as command buffers may be
+	 * allocated outside of this limit.
+	 */
+	gpu->memory_base = PHYS_OFFSET;
+
 	/* Map registers: */
 	gpu->mmio = etnaviv_ioremap(pdev, NULL, dev_name(gpu->dev));
 	if (IS_ERR(gpu->mmio))
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index d37b29fc87cc..5f5147c0c0b4 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -92,6 +92,9 @@ struct etnaviv_gpu {
 	/* 'ring'-buffer: */
 	struct drm_gem_object *buffer;
 
+	/* bus base address of memory  */
+	uint32_t memory_base;
+
 	/* event management: */
 	struct etnaviv_event event[30];
 	struct completion event_free;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 2855791440eb..b7088c47c04f 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -92,7 +92,7 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 }
 
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj)
+	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base)
 {
 	struct etnaviv_drm_private *priv = etnaviv_obj->base.dev->dev_private;
 	struct sg_table *sgt = etnaviv_obj->sgt;
@@ -103,7 +103,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	if (sgt->nents == 1) {
 		uint32_t iova;
 
-		iova = sg_dma_address(sgt->sgl);
+		iova = sg_dma_address(sgt->sgl) - memory_base;
 		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
 			etnaviv_obj->iova = iova;
 			return 0;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 3bf6d3f139e0..c9a484e86b7f 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -46,7 +46,7 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 	struct sg_table *sgt, unsigned len);
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj);
+	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base);
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index c5e606a183d8..aae6221f5a30 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -618,12 +618,9 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
 
 		of_platform_populate(node, NULL, NULL, dev);
 
-		for_each_available_child_of_node(node, child_np) {
-			DRM_INFO("add child %s\n", child_np->name);
-
+		for_each_available_child_of_node(node, child_np)
 			component_match_add(dev, &match, etnaviv_compare,
 					    child_np);
-		}
 	} else if (dev->platform_data) {
 		char **names = dev->platform_data;
 		unsigned i;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 69f4e45e1f77..e628ab4adbff 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -1092,17 +1092,17 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	int idx = gpu->pipe;
 	int ret;
 
-	dev_info(dev, "pre gpu[idx]: %p\n", priv->gpu[idx]);
+	dev_dbg(dev, "pre gpu[idx]: %p\n", priv->gpu[idx]);
 
 	if (priv->gpu[idx] == NULL) {
-		dev_info(dev, "adding core @idx %d\n", idx);
+		dev_dbg(dev, "adding core @idx %d\n", idx);
 		priv->gpu[idx] = gpu;
 	} else {
 		dev_err(dev, "failed to add core @idx %d\n", idx);
 		goto fail;
 	}
 
-	dev_info(dev, "post gpu[idx]: %p\n", priv->gpu[idx]);
+	dev_dbg(dev, "post gpu[idx]: %p\n", priv->gpu[idx]);
 
 #ifdef CONFIG_PM
 	ret = pm_runtime_get_sync(gpu->dev);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Holding the DRM struct_mutex over a call to dma_alloc_coherent() is
asking for trouble; the DRM struct_mutex is held inside several other
system-wide locks, and when CMA is enabled, this causes the CMA lock
to be nested inside struct_mutex.

In conjunction with other system locks, this eventually causes a AB-BA
lock ordering bug, which becomes most apparent when using CPU hotplug:

[ INFO: possible circular locking dependency detected ]
3.19.0-rc6+ #1497 Not tainted
-------------------------------------------------------
bash/2154 is trying to acquire lock:
 (console_lock){+.+.+.}, at: [<c006b75c>] console_cpu_notify+0x28/0x34

but task is already holding lock:
 (cpu_hotplug.lock#2){+.+.+.}, at: [<c00270f0>] cpu_hotplug_begin+0x64/0xb8

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #5 (cpu_hotplug.lock#2){+.+.+.}:
       [<c06ecaac>] mutex_lock_nested+0x5c/0x3d8
       [<c00dad28>] lru_add_drain_all+0x1c/0x18c
       [<c010fbe8>] migrate_prep+0x10/0x18
       [<c00d5d74>] alloc_contig_range+0xd0/0x2cc
       [<c0111448>] cma_alloc+0xe0/0x1ac
       [<c0397268>] dma_alloc_from_contiguous+0x3c/0x44
       [<c001a7bc>] __alloc_from_contiguous+0x3c/0xe8
       [<c09a3b48>] atomic_pool_init+0x6c/0x15c
       [<c0008a5c>] do_one_initcall+0x88/0x1d8
       [<c099ee44>] kernel_init_freeable+0x110/0x1dc
       [<c06e3104>] kernel_init+0x10/0xec
       [<c000ecd0>] ret_from_fork+0x14/0x24

-> #4 (lock){+.+...}:
       [<c06ecaac>] mutex_lock_nested+0x5c/0x3d8
       [<c00dad28>] lru_add_drain_all+0x1c/0x18c
       [<c010fbe8>] migrate_prep+0x10/0x18
       [<c00d5d74>] alloc_contig_range+0xd0/0x2cc
       [<c0111448>] cma_alloc+0xe0/0x1ac
       [<c0397268>] dma_alloc_from_contiguous+0x3c/0x44
       [<c001a7bc>] __alloc_from_contiguous+0x3c/0xe8
       [<c09a3b48>] atomic_pool_init+0x6c/0x15c
       [<c0008a5c>] do_one_initcall+0x88/0x1d8
       [<c099ee44>] kernel_init_freeable+0x110/0x1dc
       [<c06e3104>] kernel_init+0x10/0xec
       [<c000ecd0>] ret_from_fork+0x14/0x24

-> #3 (cma_mutex){+.+.+.}:
       [<c06ecaac>] mutex_lock_nested+0x5c/0x3d8
       [<c0111438>] cma_alloc+0xd0/0x1ac
       [<c0397268>] dma_alloc_from_contiguous+0x3c/0x44
       [<c001a7bc>] __alloc_from_contiguous+0x3c/0xe8
       [<c001abbc>] __dma_alloc+0x15c/0x28c
       [<c001ae38>] arm_dma_alloc+0xa0/0xa8
       [<c0508a24>] etnaviv_iommu_domain_init+0x54/0x138
       [<c0508b54>] etnaviv_iommu_domain_alloc+0x4c/0xd8
       [<c0507c8c>] etnaviv_gpu_init+0x380/0x620
       [<c0503ff8>] etnaviv_load+0xc0/0x128
       [<c0369244>] drm_dev_register+0xac/0x10c
       [<c036ad1c>] drm_platform_init+0x48/0xd4
       [<c050382c>] etnaviv_bind+0x18/0x20
       [<c038d138>] try_to_bring_up_master+0x140/0x17c
       [<c038d2f0>] component_master_add_with_match+0x84/0xe0
       [<c0504114>] etnaviv_pdev_probe+0xb4/0x104
       [<c0392ed8>] platform_drv_probe+0x50/0xac
       [<c03916f0>] driver_probe_device+0x114/0x234
       [<c03918ac>] __driver_attach+0x9c/0xa0
       [<c038fd3c>] bus_for_each_dev+0x5c/0x90
       [<c03911e0>] driver_attach+0x24/0x28
       [<c0390e58>] bus_add_driver+0xe0/0x1d8
       [<c03920ac>] driver_register+0x80/0xfc
       [<c0392d60>] __platform_driver_register+0x50/0x64
       [<c09d8b08>] etnaviv_init+0x2c/0x4c
       [<c0008a5c>] do_one_initcall+0x88/0x1d8
       [<c099ee44>] kernel_init_freeable+0x110/0x1dc
       [<c06e3104>] kernel_init+0x10/0xec
       [<c000ecd0>] ret_from_fork+0x14/0x24

-> #2 (&dev->struct_mutex){+.+.+.}:
       [<c06ecaac>] mutex_lock_nested+0x5c/0x3d8
       [<c0364508>] drm_gem_mmap+0x3c/0xd4
       [<c037ef88>] drm_gem_cma_mmap+0x14/0x2c
       [<c00fd020>] mmap_region+0x3d0/0x6a4
       [<c00fd5d8>] do_mmap_pgoff+0x2e4/0x374
       [<c00e6c18>] vm_mmap_pgoff+0x6c/0x9c
       [<c00fbb98>] SyS_mmap_pgoff+0x94/0xb8
       [<c000ec00>] ret_fast_syscall+0x0/0x4c

-> #1 (&mm->mmap_sem){++++++}:
       [<c00f4a68>] might_fault+0x64/0x98
       [<c033dd3c>] con_set_unimap+0x160/0x25c
       [<c03381c8>] vt_ioctl+0x126c/0x1328
       [<c032bfdc>] tty_ioctl+0x498/0xc5c
       [<c012493c>] do_vfs_ioctl+0x84/0x66c
       [<c0124f60>] SyS_ioctl+0x3c/0x60
       [<c000ec00>] ret_fast_syscall+0x0/0x4c

-> #0 (console_lock){+.+.+.}:
       [<c00627f0>] lock_acquire+0xb0/0x124
       [<c00697cc>] console_lock+0x44/0x6c
       [<c006b75c>] console_cpu_notify+0x28/0x34
       [<c00431b0>] notifier_call_chain+0x4c/0x8c
       [<c00432cc>] __raw_notifier_call_chain+0x1c/0x24
       [<c0026d74>] __cpu_notify+0x34/0x50
       [<c0026da8>] cpu_notify+0x18/0x1c
       [<c0026eec>] cpu_notify_nofail+0x10/0x1c
       [<c06e3484>] _cpu_down+0x100/0x248
       [<c06e35f8>] cpu_down+0x2c/0x48
       [<c03939a8>] cpu_subsys_offline+0x14/0x18
       [<c038f6d0>] device_offline+0x90/0xc0
       [<c038f7e0>] online_store+0x4c/0x74
       [<c038d46c>] dev_attr_store+0x20/0x2c
       [<c017c0f8>] sysfs_kf_write+0x54/0x58
       [<c017b430>] kernfs_fop_write+0xfc/0x1ac
       [<c0114274>] vfs_write+0xac/0x1b4
       [<c01145b0>] SyS_write+0x44/0x90
       [<c000ec00>] ret_fast_syscall+0x0/0x4c

other info that might help us debug this:

Chain exists of:
  console_lock --> lock --> cpu_hotplug.lock#2

 Possible unsafe locking scenario:
       CPU0                    CPU1
       ----                    ----
  lock(cpu_hotplug.lock#2);
                               lock(lock);
                               lock(cpu_hotplug.lock#2);
  lock(console_lock);

 *** DEADLOCK ***

8 locks held by bash/2154:
 #0:  (sb_writers#5){.+.+.+}, at: [<c0114354>] vfs_write+0x18c/0x1b4
 #1:  (&of->mutex){+.+.+.}, at: [<c017b3bc>] kernfs_fop_write+0x88/0x1ac
 #2:  (s_active#40){.+.+.+}, at: [<c017b3c4>] kernfs_fop_write+0x90/0x1ac
 #3:  (device_hotplug_lock){+.+.+.}, at: [<c038e4d8>] lock_device_hotplug_sysfs+0x14/0x54
 #4:  (&dev->mutex){......}, at: [<c038f684>] device_offline+0x44/0xc0
 #5:  (cpu_add_remove_lock){+.+.+.}, at: [<c0026de0>] cpu_maps_update_begin+0x18/0x20
 #6:  (cpu_hotplug.lock){++++++}, at: [<c002708c>] cpu_hotplug_begin+0x0/0xb8
 #7:  (cpu_hotplug.lock#2){+.+.+.}, at: [<c00270f0>] cpu_hotplug_begin+0x64/0xb8
stack backtrace:
CPU: 0 PID: 2154 Comm: bash Not tainted 3.19.0-rc6+ #1497
Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
Backtrace:
[<c0012280>] (dump_backtrace) from [<c0012418>] (show_stack+0x18/0x1c)
[<c0012400>] (show_stack) from [<c06e8eac>] (dump_stack+0x7c/0x98)
[<c06e8e30>] (dump_stack) from [<c06e6520>] (print_circular_bug+0x28c/0x2d8)
[<c06e6294>] (print_circular_bug) from [<c00621a8>] (__lock_acquire+0x1acc/0x1bb0)
[<c00606dc>] (__lock_acquire) from [<c00627f0>] (lock_acquire+0xb0/0x124)
[<c0062740>] (lock_acquire) from [<c00697cc>] (console_lock+0x44/0x6c)
[<c0069788>] (console_lock) from [<c006b75c>] (console_cpu_notify+0x28/0x34)
[<c006b734>] (console_cpu_notify) from [<c00431b0>] (notifier_call_chain+0x4c/0x8c)
[<c0043164>] (notifier_call_chain) from [<c00432cc>] (__raw_notifier_call_chain+0x1c/0x24)
[<c00432b0>] (__raw_notifier_call_chain) from [<c0026d74>] (__cpu_notify+0x34/0x50)
[<c0026d40>] (__cpu_notify) from [<c0026da8>] (cpu_notify+0x18/0x1c)
[<c0026d90>] (cpu_notify) from [<c0026eec>] (cpu_notify_nofail+0x10/0x1c)
[<c0026edc>] (cpu_notify_nofail) from [<c06e3484>] (_cpu_down+0x100/0x248)
[<c06e3384>] (_cpu_down) from [<c06e35f8>] (cpu_down+0x2c/0x48)
[<c06e35cc>] (cpu_down) from [<c03939a8>] (cpu_subsys_offline+0x14/0x18)
[<c0393994>] (cpu_subsys_offline) from [<c038f6d0>] (device_offline+0x90/0xc0)
[<c038f640>] (device_offline) from [<c038f7e0>] (online_store+0x4c/0x74)
[<c038f794>] (online_store) from [<c038d46c>] (dev_attr_store+0x20/0x2c)
[<c038d44c>] (dev_attr_store) from [<c017c0f8>] (sysfs_kf_write+0x54/0x58)
[<c017c0a4>] (sysfs_kf_write) from [<c017b430>] (kernfs_fop_write+0xfc/0x1ac)
[<c017b334>] (kernfs_fop_write) from [<c0114274>] (vfs_write+0xac/0x1b4)
[<c01141c8>] (vfs_write) from [<c01145b0>] (SyS_write+0x44/0x90)
[<c011456c>] (SyS_write) from [<c000ec00>] (ret_fast_syscall+0x0/0x4c)

The locking ordering for each of the chain backtraces are:

5: cpu_hotplug.lock (in lru_add_drain_all, get_online_cpus)
   lock (in lru_add_drain_all)
   cma_mutex (in cma_alloc)
4: lock (in lru_add_drain_all),
   cma_mutex (in cma_alloc)
3: cma_mutex (in cma_alloc)
   drm dev->struct_mutex (in etnaviv_load)
2: drm dev->struct_mutex (in drm_gem_mmap)
   mm->mmap_sem (in vm_mmap_pgoff)
1: mm->mmap_sem (in might_fault)
   console_lock (in con_set_unimap, console_lock)
0: console_lock (in console_cpu_notify, console_lock)
   cpu_hotplug.lock (in _cpu_down, cpu_hotplug_begin)

Hence the dependency chain of:
  cpu_hotplug.lock -> console_lock -> mmap_sem -> struct_mutex ->
    cma_mutex -> cpu_hotplug.lock *deadlock*

The operation which etnadrm needs to lock is not the allocations, but
the addition of the etnaviv_obj to the inactive list (to prevent the
list becoming corrupted.)  Move this to a separate operation which is
performed once all the setup of the object is complete, and move the
locking such that the allocation and setup is unlocked.

This is overall more efficient, as we permit multiple expensive
operations to occur in parallel (memory allocation) while only locking
what we need.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c       |  6 --
 drivers/staging/etnaviv/etnaviv_gem.c       | 96 +++++++++++++++++++----------
 drivers/staging/etnaviv/etnaviv_gem.h       |  1 +
 drivers/staging/etnaviv/etnaviv_gem_prime.c |  4 ++
 drivers/staging/etnaviv/etnaviv_gpu.c       |  2 +
 5 files changed, 70 insertions(+), 39 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index aae6221f5a30..5e3b97fdc6cf 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -91,9 +91,7 @@ static int etnaviv_unload(struct drm_device *dev)
 	flush_workqueue(priv->wq);
 	destroy_workqueue(priv->wq);

 	dev->dev_private = NULL;
 
@@ -144,16 +142,12 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)

 
 	load_gpu(dev);

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index a4df6b4f60c6..3d3e096dcedf 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -589,37 +589,26 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
 	kfree(etnaviv_obj);
 }
 
-/* convenience method to construct a GEM buffer object, and userspace handle */
-int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
-		uint32_t size, uint32_t flags, uint32_t *handle)
+int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj)
 {
-	struct drm_gem_object *obj;
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	int ret;
 
-	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	ret = mutex_lock_killable(&dev->struct_mutex);
 	if (ret)
 		return ret;
 
-	obj = etnaviv_gem_new(dev, size, flags);
-
+	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
 	mutex_unlock(&dev->struct_mutex);
 
-	if (IS_ERR(obj))
-		return PTR_ERR(obj);
-
-	ret = drm_gem_handle_create(file, obj, handle);
-
-	/* drop reference from allocate - handle holds it now */
-	drm_gem_object_unreference_unlocked(obj);
-
-	return ret;
+	return 0;
 }
 
 static int etnaviv_gem_new_impl(struct drm_device *dev,
 		uint32_t size, uint32_t flags,
 		struct drm_gem_object **obj)
 {
-	struct etnaviv_drm_private *priv = dev->dev_private;
 	struct etnaviv_gem_object *etnaviv_obj;
 	unsigned sz = sizeof(*etnaviv_obj);
 	bool valid = true;
@@ -666,21 +655,19 @@ static int etnaviv_gem_new_impl(struct drm_device *dev,
 	reservation_object_init(&etnaviv_obj->_resv);
 
 	INIT_LIST_HEAD(&etnaviv_obj->submit_entry);
-	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
+	INIT_LIST_HEAD(&etnaviv_obj->mm_list);
 
 	*obj = &etnaviv_obj->base;
 
 	return 0;
 }
 
-struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
+static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev,
 		uint32_t size, uint32_t flags)
 {
 	struct drm_gem_object *obj = NULL;
 	int ret;
 
-	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-
 	size = PAGE_ALIGN(size);
 
 	ret = etnaviv_gem_new_impl(dev, size, flags, &obj);
@@ -703,11 +690,55 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 
 fail:
 	if (obj)
-		drm_gem_object_unreference(obj);
+		drm_gem_object_unreference_unlocked(obj);
 
 	return ERR_PTR(ret);
 }
 
+/* convenience method to construct a GEM buffer object, and userspace handle */
+int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
+		uint32_t size, uint32_t flags, uint32_t *handle)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = __etnaviv_gem_new(dev, size, flags);
+	if (IS_ERR(obj))
+		return PTR_ERR(obj);
+
+	ret = etnaviv_gem_obj_add(dev, obj);
+	if (ret < 0) {
+		drm_gem_object_unreference_unlocked(obj);
+		return ret;
+	}
+
+	ret = drm_gem_handle_create(file, obj, handle);
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
+		uint32_t size, uint32_t flags)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = __etnaviv_gem_new(dev, size, flags);
+	if (IS_ERR(obj))
+		return obj;
+
+	ret = etnaviv_gem_obj_add(dev, obj);
+	if (ret < 0) {
+		drm_gem_object_unreference_unlocked(obj);
+		return ERR_PTR(ret);
+	}
+
+	return obj;
+}
+
 int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 	struct etnaviv_gem_object **res)
 {
@@ -887,22 +918,21 @@ int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 	struct etnaviv_gem_object *etnaviv_obj;
 	int ret;
 
-	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, &etnaviv_obj);
 	if (ret)
 		return ret;
 
-	ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, &etnaviv_obj);
-	if (ret == 0) {
-		etnaviv_obj->ops = &etnaviv_gem_userptr_ops;
-		etnaviv_obj->userptr.ptr = ptr;
-		etnaviv_obj->userptr.task = current;
-		etnaviv_obj->userptr.ro = !(flags & ETNA_USERPTR_WRITE);
-		get_task_struct(current);
-	}
-	mutex_unlock(&dev->struct_mutex);
+	etnaviv_obj->ops = &etnaviv_gem_userptr_ops;
+	etnaviv_obj->userptr.ptr = ptr;
+	etnaviv_obj->userptr.task = current;
+	etnaviv_obj->userptr.ro = !(flags & ETNA_USERPTR_WRITE);
+	get_task_struct(current);
 
-	if (ret)
+	ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base);
+	if (ret) {
+		drm_gem_object_unreference_unlocked(&etnaviv_obj->base);
 		return ret;
+	}
 
 	ret = drm_gem_handle_create(file, &etnaviv_obj->base, handle);
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 0bf432515ced..c7d0e8b79355 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -118,6 +118,7 @@ struct etnaviv_gem_submit {
 
 int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 	struct etnaviv_gem_object **res);
+int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj);
 struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj);
 void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj);
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index aad5a96f9fba..cca569be98bf 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -108,6 +108,10 @@ struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
 	if (ret)
 		goto fail;
 
+	ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base);
+	if (ret)
+		goto fail;
+
 	return &etnaviv_obj->base;
 
 fail:
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index e628ab4adbff..cdd03ace15d9 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -526,7 +526,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	}
 
 	/* Now program the hardware */
+	mutex_lock(&gpu->drm->struct_mutex);
 	etnaviv_gpu_hw_init(gpu);
+	mutex_unlock(&gpu->drm->struct_mutex);
 
 	pm_runtime_mark_last_busy(gpu->dev);
 	pm_runtime_put_autosuspend(gpu->dev);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Avoids leaking developer directories into the generated headers and
adds some defines needed for proper MMU flush.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/cmdstream.xml.h |  6 +++---
 drivers/staging/etnaviv/common.xml.h    | 10 +++-------
 drivers/staging/etnaviv/state.xml.h     | 21 ++++++++++++---------
 drivers/staging/etnaviv/state_hi.xml.h  | 18 ++++++++++--------
 4 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/drivers/staging/etnaviv/cmdstream.xml.h b/drivers/staging/etnaviv/cmdstream.xml.h
index 844f82977e3e..8c44ba9a694e 100644
--- a/drivers/staging/etnaviv/cmdstream.xml.h
+++ b/drivers/staging/etnaviv/cmdstream.xml.h
@@ -8,10 +8,10 @@ http://0x04.net/cgit/index.cgi/rules-ng-ng
 git clone git://0x04.net/rules-ng-ng
 
 The rules-ng-ng source files this header was generated from are:
-- /home/orion/projects/etna_viv/rnndb/cmdstream.xml (  12589 bytes, from 2013-09-01 10:53:22)
-- /home/orion/projects/etna_viv/rnndb/common.xml    (  18379 bytes, from 2014-01-27 15:58:05)
+- cmdstream.xml (  12589 bytes, from 2014-02-17 14:57:56)
+- common.xml    (  18437 bytes, from 2015-03-25 11:27:41)
 
-Copyright (C) 2013
+Copyright (C) 2014
 */

diff --git a/drivers/staging/etnaviv/common.xml.h b/drivers/staging/etnaviv/common.xml.h
index 36fa0e4cf56b..9e585d51fb78 100644
--- a/drivers/staging/etnaviv/common.xml.h
+++ b/drivers/staging/etnaviv/common.xml.h
@@ -8,14 +8,10 @@ http://0x04.net/cgit/index.cgi/rules-ng-ng
 git clone git://0x04.net/rules-ng-ng
 
 The rules-ng-ng source files this header was generated from are:
-- /home/orion/projects/etna_viv/rnndb/state.xml    (  18526 bytes, from 2013-09-11 16:52:32)
-- /home/orion/projects/etna_viv/rnndb/common.xml   (  18379 bytes, from 2014-01-27 15:58:05)
-- /home/orion/projects/etna_viv/rnndb/state_hi.xml (  22236 bytes, from 2014-01-27 15:56:46)
-- /home/orion/projects/etna_viv/rnndb/state_2d.xml (  51191 bytes, from 2013-10-04 06:36:55)
-- /home/orion/projects/etna_viv/rnndb/state_3d.xml (  54570 bytes, from 2013-10-12 15:25:03)
-- /home/orion/projects/etna_viv/rnndb/state_vg.xml (   5942 bytes, from 2013-09-01 10:53:22)
+- state_vg.xml (   5973 bytes, from 2015-03-25 11:26:01)
+- common.xml   (  18437 bytes, from 2015-03-25 11:27:41)
 
-Copyright (C) 2014
+Copyright (C) 2015
 */

diff --git a/drivers/staging/etnaviv/state.xml.h b/drivers/staging/etnaviv/state.xml.h
index e7b36df1e4e3..368218304566 100644
--- a/drivers/staging/etnaviv/state.xml.h
+++ b/drivers/staging/etnaviv/state.xml.h
@@ -8,14 +8,14 @@ http://0x04.net/cgit/index.cgi/rules-ng-ng
 git clone git://0x04.net/rules-ng-ng
 
 The rules-ng-ng source files this header was generated from are:
-- /home/orion/projects/etna_viv/rnndb/state.xml    (  18526 bytes, from 2013-09-11 16:52:32)
-- /home/orion/projects/etna_viv/rnndb/common.xml   (  18379 bytes, from 2014-01-27 15:58:05)
-- /home/orion/projects/etna_viv/rnndb/state_hi.xml (  22236 bytes, from 2014-01-27 15:56:46)
-- /home/orion/projects/etna_viv/rnndb/state_2d.xml (  51191 bytes, from 2013-10-04 06:36:55)
-- /home/orion/projects/etna_viv/rnndb/state_3d.xml (  54570 bytes, from 2013-10-12 15:25:03)
-- /home/orion/projects/etna_viv/rnndb/state_vg.xml (   5942 bytes, from 2013-09-01 10:53:22)
-
-Copyright (C) 2013
+- state.xml    (  18882 bytes, from 2015-03-25 11:42:32)
+- common.xml   (  18437 bytes, from 2015-03-25 11:27:41)
+- state_hi.xml (  23420 bytes, from 2015-03-25 11:47:21)
+- state_2d.xml (  51549 bytes, from 2015-03-25 11:25:06)
+- state_3d.xml (  54600 bytes, from 2015-03-25 11:25:19)
+- state_vg.xml (   5973 bytes, from 2015-03-25 11:26:01)
+
+Copyright (C) 2015
 */

@@ -210,7 +210,10 @@ Copyright (C) 2013
 
 #define VIVS_GL_FLUSH_MMU					0x00003810
 #define VIVS_GL_FLUSH_MMU_FLUSH_FEMMU				0x00000001
-#define VIVS_GL_FLUSH_MMU_FLUSH_PEMMU				0x00000002
+#define VIVS_GL_FLUSH_MMU_FLUSH_UNK1				0x00000002
+#define VIVS_GL_FLUSH_MMU_FLUSH_UNK2				0x00000004
+#define VIVS_GL_FLUSH_MMU_FLUSH_PEMMU				0x00000008
+#define VIVS_GL_FLUSH_MMU_FLUSH_UNK4				0x00000010
 
 #define VIVS_GL_VERTEX_ELEMENT_CONFIG				0x00003814
 
diff --git a/drivers/staging/etnaviv/state_hi.xml.h b/drivers/staging/etnaviv/state_hi.xml.h
index 9799d7473e5e..0064f2640396 100644
--- a/drivers/staging/etnaviv/state_hi.xml.h
+++ b/drivers/staging/etnaviv/state_hi.xml.h
@@ -8,14 +8,10 @@ http://0x04.net/cgit/index.cgi/rules-ng-ng
 git clone git://0x04.net/rules-ng-ng
 
 The rules-ng-ng source files this header was generated from are:
-- /home/christian/projects/etna_viv/rnndb/state.xml    (  18526 bytes, from 2014-09-06 05:57:57)
-- /home/christian/projects/etna_viv/rnndb/common.xml   (  18379 bytes, from 2014-09-06 05:57:57)
-- /home/christian/projects/etna_viv/rnndb/state_hi.xml (  23176 bytes, from 2014-09-06 06:07:47)
-- /home/christian/projects/etna_viv/rnndb/state_2d.xml (  51191 bytes, from 2014-09-06 05:57:57)
-- /home/christian/projects/etna_viv/rnndb/state_3d.xml (  54570 bytes, from 2014-09-06 05:57:57)
-- /home/christian/projects/etna_viv/rnndb/state_vg.xml (   5942 bytes, from 2014-09-06 05:57:57)
-
-Copyright (C) 2014
+- state_hi.xml (  23420 bytes, from 2015-03-25 11:47:21)
+- common.xml   (  18437 bytes, from 2015-03-25 11:27:41)
+
+Copyright (C) 2015
 */

@@ -396,6 +392,12 @@ Copyright (C) 2014
 #define VIVS_MC_PROFILE_CONFIG3					0x0000047c
 
 #define VIVS_MC_BUS_CONFIG					0x00000480
+#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK			0x0000000f
+#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__SHIFT			0
+#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG(x)			(((x) << VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__SHIFT) & VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK)
+#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK			0x000000f0
+#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__SHIFT			4
+#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG(x)			(((x) << VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__SHIFT) & VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK)
 
 #define VIVS_MC_START_COMPOSITION				0x00000554

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

The hardware interprets a value of 0 as the maximium number of rectangles
which is 256. Allow this in the command parser.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_cmd_parser.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_cmd_parser.c b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
index fbbea57c7b5c..03552c5118bf 100644
--- a/drivers/staging/etnaviv/etnaviv_cmd_parser.c
+++ b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
@@ -75,6 +75,8 @@ bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
 
 		case FE_OPCODE_DRAW_2D:
 			n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT);
+			if (n == 0)
+				n = 256;
 			len = 2 + n * 2;
 			break;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

These two GPUs seems to be in the transition state between two
generations and report a wrong instuction count. Fix it up in the
kernel like the Vivante driver does.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index cdd03ace15d9..bfe561a016d3 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -167,7 +167,12 @@ static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 
 	switch (gpu->identity.instruction_count) {
 	case 0:
-		gpu->identity.instruction_count = 256;
+		if ((gpu->identity.model == 0x2000 &&
+		     gpu->identity.revision == 0x5108) ||
+		    gpu->identity.model == 0x880)
+			gpu->identity.instruction_count = 512;
+		else
+			gpu->identity.instruction_count = 256;
 		break;
 
 	case 1:

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

This is taken from the Vivante kernel driver and seems to improve
stability and performance.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index bfe561a016d3..9bee4e8f296a 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -437,6 +437,16 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 		  VIVS_HI_AXI_CONFIG_AWCACHE(2) |
 		  VIVS_HI_AXI_CONFIG_ARCACHE(2));
 
+	/* GC2000 rev 5108 needs a special bus config */
+	if (gpu->identity.model == 0x2000 && gpu->identity.revision == 0x5108) {
+		u32 bus_config = gpu_read(gpu, VIVS_MC_BUS_CONFIG);
+		bus_config &= ~(VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK |
+				VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK);
+		bus_config |= VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG(1) |
+			      VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG(0);
+		gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config);
+	}
+
 	/* set base addresses */
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base);
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Avoids memory corruptions seen due to stale TLB entries.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 919e2650a9ae..b73975bdc69a 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -234,7 +234,10 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		/* Add the MMU flush */
 		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
 			       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
-			       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU);
+			       VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
+			       VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
+			       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
+			       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
 
 		/* And the link to the first buffer */
 		CMD_LINK(buffer, link_size, link_target);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

This provides a bit more type safety.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index c7d0e8b79355..e109e8c1ff31 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -76,7 +76,12 @@ struct etnaviv_gem_object {
 
 	struct etnaviv_gem_userptr userptr;
 };
-#define to_etnaviv_bo(x) container_of(x, struct etnaviv_gem_object, base)
+
+static inline
+struct etnaviv_gem_object *to_etnaviv_bo(struct drm_gem_object *obj)
+{
+	return container_of(obj, struct etnaviv_gem_object, base);
+}
 
 struct etnaviv_gem_ops {
 	int (*get_pages)(struct etnaviv_gem_object *);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Allows userspace to properly synchronize with the GPU when accessing
buffers.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 3d3e096dcedf..49296375d024 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -467,13 +467,12 @@ void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj)
 int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 		struct timespec *timeout)
 {
-/*
+
 	struct drm_device *dev = obj->dev;
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-*/
+
 	int ret = 0;
-	/* TODO */
-#if 0
+
 	if (is_active(etnaviv_obj)) {
 		uint32_t fence = 0;
 
@@ -485,11 +484,11 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 			timeout = NULL;
 
 		ret = etnaviv_wait_fence_interruptable(dev, etnaviv_obj->gpu,
-			       fence, timeout);
+						       fence, timeout);
 	}
 
 	/* TODO cache maintenance */
-#endif
+
 	return ret;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

As single buffer object may be mapped into different address spaces at the
same time. For now we only have two different address spaces for the 3D and
2D pipe, but this may change as soon as we implement per-process page tables.

Allow this by having each buffer object manage a list of all it's
mappings into the respective address spaces.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 44 +++++++++++++++++-----
 drivers/staging/etnaviv/etnaviv_gem.h | 13 ++++++-
 drivers/staging/etnaviv/etnaviv_mmu.c | 71 +++++++++++++++++++++--------------
 drivers/staging/etnaviv/etnaviv_mmu.h |  8 +++-
 4 files changed, 93 insertions(+), 43 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 49296375d024..0ea9fba5d657 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -310,19 +310,25 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	struct drm_gem_object *obj, uint32_t *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct etnaviv_vram_mapping *mapping =
+			etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu);
 	int ret = 0;
 
-	if (!etnaviv_obj->iova && !(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
+	if (etnaviv_obj->flags & ETNA_BO_CMDSTREAM) {
+		*iova = etnaviv_obj->paddr;
+		return 0;
+	}
+
+	if (!mapping) {
 		struct page **pages = etnaviv_gem_get_pages(etnaviv_obj);
 		if (IS_ERR(pages))
 			return PTR_ERR(pages);
-
 		ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj,
-				gpu->memory_base);
+				gpu->memory_base, &mapping);
 	}
 
 	if (!ret)
-		*iova = etnaviv_obj->iova;
+		*iova = mapping->iova;
 
 	return ret;
 }
@@ -331,13 +337,15 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
 	int id, uint32_t *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct etnaviv_vram_mapping *mapping =
+			etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu);
 	int ret;
 
 	/* this is safe right now because we don't unmap until the
 	 * bo is deleted:
 	 */
-	if (etnaviv_obj->iova) {
-		*iova = etnaviv_obj->iova;
+	if (mapping) {
+		*iova = mapping->iova;
 		return 0;
 	}
 
@@ -546,11 +554,12 @@ static const struct etnaviv_gem_ops etnaviv_gem_cmd_ops = {
 static void etnaviv_free_obj(struct drm_gem_object *obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-	struct etnaviv_drm_private *priv = obj->dev->dev_private;
-	struct etnaviv_iommu *mmu = priv->mmu;
+	struct etnaviv_vram_mapping *mapping, *tmp;
 
-	if (mmu)
-		etnaviv_iommu_unmap_gem(mmu, etnaviv_obj);
+	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
+				 obj_head) {
+		etnaviv_iommu_unmap_gem(mapping->mmu, etnaviv_obj, mapping);
+	}
 }
 
 static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
@@ -655,6 +664,7 @@ static int etnaviv_gem_new_impl(struct drm_device *dev,
 
 	INIT_LIST_HEAD(&etnaviv_obj->submit_entry);
 	INIT_LIST_HEAD(&etnaviv_obj->mm_list);
+	INIT_LIST_HEAD(&etnaviv_obj->vram_list);

@@ -755,6 +765,20 @@ int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 	return 0;
 }
 
+struct etnaviv_vram_mapping *
+etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj,
+			     struct etnaviv_iommu *mmu)
+{
+	struct etnaviv_vram_mapping *mapping;
+
+	list_for_each_entry(mapping, &obj->vram_list, obj_head) {
+		if (mapping->mmu == mmu)
+			return mapping;
+	}
+
+	return NULL;
+}
+
 struct get_pages_work {
 	struct work_struct work;
 	struct mm_struct *mm;
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index e109e8c1ff31..93720eccacaa 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -30,6 +30,13 @@ struct etnaviv_gem_userptr {
 	bool ro;
 };
 
+struct etnaviv_vram_mapping {
+	struct list_head obj_head;
+	struct etnaviv_iommu *mmu;
+	struct drm_mm_node vram_node;
+	uint32_t iova;
+};
+
 struct etnaviv_gem_object {
 	struct drm_gem_object base;
 	const struct etnaviv_gem_ops *ops;
@@ -59,7 +66,6 @@ struct etnaviv_gem_object {
 	struct page **pages;
 	struct sg_table *sgt;
 	void *vaddr;
-	uint32_t iova;
 
 	/* for ETNA_BO_CMDSTREAM */
 	dma_addr_t paddr;
@@ -68,7 +74,7 @@ struct etnaviv_gem_object {
 	struct reservation_object *resv;
 	struct reservation_object _resv;
 
-	struct drm_mm_node *gpu_vram_node;
+	struct list_head vram_list;
 
 	/* for buffer manipulation during submit */
 	bool is_ring_buffer;
@@ -121,6 +127,9 @@ struct etnaviv_gem_submit {
 	} bos[0];
 };
 
+struct etnaviv_vram_mapping *
+etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj,
+			     struct etnaviv_iommu *mmu);
 int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
 	struct etnaviv_gem_object **res);
 int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj);
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index b7088c47c04f..6aa6857c4682 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -92,28 +92,38 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 }
 
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base)
+	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base,
+	struct etnaviv_vram_mapping **out_mapping)
 {
 	struct etnaviv_drm_private *priv = etnaviv_obj->base.dev->dev_private;
 	struct sg_table *sgt = etnaviv_obj->sgt;
+	struct etnaviv_vram_mapping *mapping, *free = NULL;
 	struct drm_mm_node *node;
 	int ret;
 
+	mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+	if (!mapping)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&mapping->obj_head);
+	mapping->mmu = mmu;
+
 	/* v1 MMU can optimize single entry (contiguous) scatterlists */
 	if (sgt->nents == 1) {
 		uint32_t iova;
 
 		iova = sg_dma_address(sgt->sgl) - memory_base;
 		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
-			etnaviv_obj->iova = iova;
+			mapping->iova = iova;
+			list_add_tail(&mapping->obj_head,
+				      &etnaviv_obj->vram_list);
+			if (out_mapping)
+				*out_mapping = mapping;
 			return 0;
 		}
 	}
 
-	node = kzalloc(sizeof(*node), GFP_KERNEL);
-	if (!node)
-		return -ENOMEM;
-
+	node = &mapping->vram_node;
 	while (1) {
 		struct etnaviv_gem_object *o, *n;
 		struct list_head list;
@@ -142,8 +152,8 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		found = 0;
 		INIT_LIST_HEAD(&list);
 		list_for_each_entry(o, &priv->inactive_list, mm_list) {
-			if (!o->gpu_vram_node ||
-			    o->gpu_vram_node->mm != &mmu->mm)
+			free = etnaviv_gem_get_vram_mapping(o, mmu);
+			if (!free)
 				continue;
 
 			/*
@@ -154,7 +164,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 				continue;
 
 			list_add(&o->submit_entry, &list);
-			if (drm_mm_scan_add_block(o->gpu_vram_node)) {
+			if (drm_mm_scan_add_block(&free->vram_node)) {
 				found = true;
 				break;
 			}
@@ -163,7 +173,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		if (!found) {
 			/* Nothing found, clean up and fail */
 			list_for_each_entry_safe(o, n, &list, submit_entry)
-				BUG_ON(drm_mm_scan_remove_block(o->gpu_vram_node));
+				BUG_ON(drm_mm_scan_remove_block(&free->vram_node));
 			break;
 		}
 
@@ -174,12 +184,12 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		 * can leave the block pinned.
 		 */
 		list_for_each_entry_safe(o, n, &list, submit_entry)
-			if (!drm_mm_scan_remove_block(o->gpu_vram_node))
+			if (!drm_mm_scan_remove_block(&free->vram_node))
 				list_del_init(&o->submit_entry);
 
 		list_for_each_entry_safe(o, n, &list, submit_entry) {
 			list_del_init(&o->submit_entry);
-			etnaviv_iommu_unmap_gem(mmu, o);
+			etnaviv_iommu_unmap_gem(mmu, o, free);
 		}
 
 		/*
@@ -191,40 +201,43 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	}
 
 	if (ret < 0) {
-		kfree(node);
+		kfree(mapping);
 		return ret;
 	}
 
 	mmu->last_iova = node->start + etnaviv_obj->base.size;
-	etnaviv_obj->iova = node->start;
-	etnaviv_obj->gpu_vram_node = node;
+	mapping->iova = node->start;
 	ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size,
 				IOMMU_READ | IOMMU_WRITE);
 
 	if (ret < 0) {
 		drm_mm_remove_node(node);
-		kfree(node);
-
-		etnaviv_obj->iova = 0;
-		etnaviv_obj->gpu_vram_node = NULL;
+		kfree(mapping);
+		return ret;
 	}
 
+	list_add_tail(&mapping->obj_head, &etnaviv_obj->vram_list);
+	if (out_mapping)
+		*out_mapping = mapping;
+
 	return ret;
 }
 
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj)
+			     struct etnaviv_gem_object *etnaviv_obj,
+			     struct etnaviv_vram_mapping *mapping)
 {
-	if (etnaviv_obj->gpu_vram_node) {
-		uint32_t offset = etnaviv_obj->gpu_vram_node->start;
+	if (mapping) {
+		uint32_t offset = mapping->vram_node.start;
 
-		etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt,
-				    etnaviv_obj->base.size);
-		drm_mm_remove_node(etnaviv_obj->gpu_vram_node);
-		kfree(etnaviv_obj->gpu_vram_node);
-
-		etnaviv_obj->gpu_vram_node = NULL;
-		etnaviv_obj->iova = 0;
+		if (mapping->iova >= 0x80000000) {
+			etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt,
+					    etnaviv_obj->base.size);
+			drm_mm_remove_node(&mapping->vram_node);
+		}
+		list_del(&mapping->obj_head);
+		kfree(mapping);
+		mapping = NULL;
 	}
 }
 
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index c9a484e86b7f..f3fae34f6b72 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -25,6 +25,8 @@ enum etnaviv_iommu_version {
 	ETNAVIV_IOMMU_V2,
 };
 
+struct etnaviv_vram_mapping;
+
 struct etnaviv_iommu {
 	struct drm_device *dev;
 	struct iommu_domain *domain;
@@ -46,9 +48,11 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 	struct sg_table *sgt, unsigned len);
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base);
+	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base,
+	struct etnaviv_vram_mapping **mapping);
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj);
+	struct etnaviv_gem_object *etnaviv_obj,
+	struct etnaviv_vram_mapping *mapping);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:13 +0100

Each pipe has it's own MMU, so there is no point in pretending
to have a single one at the DRM driver level. All MMU management has
to happen on a per-pipe level.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 7 -------
 drivers/staging/etnaviv/etnaviv_drv.h | 5 -----
 drivers/staging/etnaviv/etnaviv_gpu.c | 1 -
 3 files changed, 13 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 5e3b97fdc6cf..70b0268ab6a1 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -22,13 +22,6 @@
 #include "etnaviv_gpu.h"
 #include "etnaviv_mmu.h"
 
-void etnaviv_register_mmu(struct drm_device *dev, struct etnaviv_iommu *mmu)
-{
-	struct etnaviv_drm_private *priv = dev->dev_private;
-
-	priv->mmu = mmu;
-}
-
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
 static bool reglog;
 MODULE_PARM_DESC(reglog, "Enable register read/write logging");
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 68fcf2c491e6..3f53d24db2e2 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -61,13 +61,8 @@ struct etnaviv_drm_private {
 	struct list_head inactive_list;
 
 	struct workqueue_struct *wq;
-
-	/* registered MMUs: */
-	struct etnaviv_iommu *mmu;
 };
 
-void etnaviv_register_mmu(struct drm_device *dev, struct etnaviv_iommu *mmu);
-
 int etnaviv_wait_fence_interruptable(struct drm_device *dev,
 		struct etnaviv_gpu *gpu, uint32_t fence,
 		struct timespec *timeout);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 9bee4e8f296a..472254c4bf20 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -521,7 +521,6 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 		ret = -ENOMEM;
 		goto fail;
 	}
-	etnaviv_register_mmu(gpu->drm, gpu->mmu);
 
 	/* Create buffer: */
 	gpu->buffer = etnaviv_gem_new(gpu->drm, PAGE_SIZE, ETNA_BO_CMDSTREAM);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

The MMU is per GPU (pipe), rather than per DRM device, so it makes
a lot more sense to use the GPU device instead of the DRM device for
the MMU to hang off from.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 2 +-
 drivers/staging/etnaviv/etnaviv_mmu.c | 2 +-
 drivers/staging/etnaviv/etnaviv_mmu.h | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 472254c4bf20..1bfac0c4c705 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -516,7 +516,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)

-	gpu->mmu = etnaviv_iommu_new(gpu->drm, iommu, version);
+	gpu->mmu = etnaviv_iommu_new(gpu->dev, iommu, version);
 	if (!gpu->mmu) {
 		ret = -ENOMEM;
 		goto fail;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 6aa6857c4682..f4f15fceb072 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -248,7 +248,7 @@ void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
 	kfree(mmu);
 }
 
-struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
+struct etnaviv_iommu *etnaviv_iommu_new(struct device *dev,
 	struct iommu_domain *domain, enum etnaviv_iommu_version version)
 {
 	struct etnaviv_iommu *mmu;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index f3fae34f6b72..a8e48b2a61dc 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -28,7 +28,7 @@ enum etnaviv_iommu_version {
 struct etnaviv_vram_mapping;
 
 struct etnaviv_iommu {
 
 	enum etnaviv_iommu_version version;
@@ -55,7 +55,7 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_vram_mapping *mapping);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
 
-struct etnaviv_iommu *etnaviv_iommu_new(struct drm_device *dev,
+struct etnaviv_iommu *etnaviv_iommu_new(struct device *dev,
 	struct iommu_domain *domain, enum etnaviv_iommu_version version);
 
 #endif /* __ETNAVIV_MMU_H__ */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

The MMU needs to be flushed when changing the render context to get
rid of stale TLB entries left behind by the last context.

While we do not support context switching between different processes yet
this commit fixes memory corruptions seen when executing different 3D
applications one after another.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 1bfac0c4c705..a7a691f7036c 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -919,6 +919,9 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	gpu->submitted_fence = submit->fence;
 	gpu->event[event].fence = submit->fence;
 
+	if (priv->lastctx != ctx)
+		gpu->mmu->need_flush = true;
+
 	etnaviv_buffer_queue(gpu, event, submit);
 
 	priv->lastctx = ctx;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

At least the GC2000 I'm testing with seems to have a bug that all vertex
streams have to be mapped either through the MMU or without it. Mixing
between both mapping types in a single draw command results in corrupted
vertex data.

As we can not quarantee that a buffer may be mappable without the MMU
all vertex buffers need to go through the MMU. As the userspace knows
which buffers may be used as vertex buffers at allocation time this
adds a flagfor userspace to specify in this situation.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 2 +-
 include/uapi/drm/etnaviv_drm.h        | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index f4f15fceb072..6da6b8e0c6e5 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -109,7 +109,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	mapping->mmu = mmu;
 
 	/* v1 MMU can optimize single entry (contiguous) scatterlists */
-	if (sgt->nents == 1) {
+	if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) {
 		uint32_t iova;
 
 		iova = sg_dma_address(sgt->sgl) - memory_base;
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index a4c109ffbea4..52c6989ad93f 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -86,6 +86,8 @@ struct drm_etnaviv_param {
 #define ETNA_BO_CACHED       0x00010000
 #define ETNA_BO_WC           0x00020000
 #define ETNA_BO_UNCACHED     0x00040000
+/* map flags */
+#define ETNA_BO_FORCE_MMU    0x00100000
 
 struct drm_etnaviv_gem_new {
 	uint64_t size;           /* in */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

The GPU cores are possibly scattered in the SoC address space, so the
current abstraction of having a parent node for the master device and
the cores as child nodes doesn't fit too well.

Instead take the same approach as with imx-drm to have a logical master
node that refers to the other components by a phandle, so those can be
placed under their real parent buses in the DT.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 32 ++++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 70b0268ab6a1..dbb80686fbb2 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -564,14 +564,6 @@ static struct drm_driver etnaviv_drm_driver = {
 /*
  * Platform driver:
  */
-
-static int etnaviv_compare(struct device *dev, void *data)
-{
-	struct device_node *np = data;
-
-	return dev->of_node == np;
-}
-
 static int etnaviv_bind(struct device *dev)
 {
 	return drm_platform_init(&etnaviv_drm_driver, to_platform_device(dev));
@@ -587,6 +579,13 @@ static const struct component_master_ops etnaviv_master_ops = {
 	.unbind = etnaviv_unbind,
 };
 
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	return dev->of_node == np;
+}
+
 static int compare_str(struct device *dev, void *data)
 {
 	return !strcmp(dev_name(dev), data);
@@ -601,13 +600,18 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
 	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
 
 	if (node) {
-		struct device_node *child_np;
+		struct device_node *core_node;
+		int i;
 
-		of_platform_populate(node, NULL, NULL, dev);
+		for (i = 0; ; i++) {
+			core_node = of_parse_phandle(node, "cores", i);
+			if (!core_node)
+				break;
 
-		for_each_available_child_of_node(node, child_np)
-			component_match_add(dev, &match, etnaviv_compare,
-					    child_np);
+			component_match_add(&pdev->dev, &match, compare_of,
+					    core_node);
+			of_node_put(core_node);
+		}
 	} else if (dev->platform_data) {
 		char **names = dev->platform_data;
 		unsigned i;
@@ -627,7 +631,7 @@ static int etnaviv_pdev_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id dt_match[] = {
-	{ .compatible = "vivante,gccore" },
+	{ .compatible = "fsl,imx-gpu-subsystem" },
 	{}
 };
 MODULE_DEVICE_TABLE(of, dt_match);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

The platform should specify the appropriate IRQ flags and it's
a really bad idea to override them in individual drivers.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index a7a691f7036c..333c200733c1 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -1245,8 +1245,8 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 		goto fail;
 	}
 
-	err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
-			IRQF_TRIGGER_HIGH, dev_name(gpu->dev), gpu);
+	err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0,
+			       dev_name(gpu->dev), gpu);
 	if (err) {
 		dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
 		goto fail;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

While this isn't the case on i.MX6 a single GPU pipe can have
multiple rendering backend states, which can be selected by the
pipe switch command, so there is no strict mapping between the
user "pipes" and the PIPE_2D/PIPE_3D execution states.

We need to respect this in the public userspace API. The exported
pipes are the GC cores which may be able to support one or more
execution states. The information which execution states are supported
on a given pipe is available to userspace through the features0 param.

Userspace is responsible to choose a matching pipe when deciding which
pipe to use for a specific task.

The submit ioctl now takes one more parameter for userspace to specify
which execution state it expects a pipe to be in when starting execution
of the command buffers. This allows the kernel to insert pipe switch
commands only when really needed while maintaining separation between
processes by making sure that no process can leave the pipe behind in
an unexpected state, potentially hanging the next one to use the GPU.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     | 59 +++++++++++++++++-----------
 drivers/staging/etnaviv/etnaviv_drv.c        |  1 +
 drivers/staging/etnaviv/etnaviv_drv.h        |  1 +
 drivers/staging/etnaviv/etnaviv_gem.h        |  1 +
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  1 +
 drivers/staging/etnaviv/etnaviv_gpu.c        | 48 ++++------------------
 drivers/staging/etnaviv/etnaviv_gpu.h        |  2 +-
 include/uapi/drm/etnaviv_drm.h               | 19 ++++-----
 8 files changed, 58 insertions(+), 74 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index b73975bdc69a..a662288a1298 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -84,7 +84,7 @@ static inline void CMD_STALL(struct etnaviv_gem_object *buffer,
 	OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to));
 }
 
-static void cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
+static void etnaviv_cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
 {
 	u32 flush;
 	u32 stall;
@@ -132,8 +132,6 @@ u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 	buffer->offset = 0;
 	buffer->is_ring_buffer = true;
 
-	cmd_select_pipe(buffer, gpu->pipe);
-
 	CMD_WAIT(buffer);
 	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
 
@@ -157,21 +155,29 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
 	struct etnaviv_gem_object *cmd;
 	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
-	u32 back, link_target, link_size, reserve_size;
+	u32 back, link_target, link_size, reserve_size, extra_size = 0;
 	u32 i;

-	reserve_size = 6;
-
 	/*
 	 * If we need to flush the MMU prior to submitting this buffer, we
 	 * will need to append a mmu flush load state, followed by a new
 	 * link to this buffer - a total of four additional words.
 	 */
-	if (gpu->mmu->need_flush)
-		reserve_size += 4;
+	if (gpu->mmu->need_flush || gpu->switch_context) {
+		/* link command */
+		extra_size += 2;
+		/* flush command */
+		if (gpu->mmu->need_flush)
+			extra_size += 2;
+		/* pipe switch commands */
+		if (gpu->switch_context)
+			extra_size += 8;
+	}
+
+	reserve_size = 6 + extra_size;
 
 	/*
 	 * if we are going to completely overflow the buffer, we need to wrap.
@@ -185,10 +191,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	link_target = gpu_va(gpu, buffer) + buffer->offset * 4;
 	link_size = 6;
 
-	if (gpu->mmu->need_flush) {
-		/* Skip over the MMU flush and LINK instructions */
-		link_target += 4 * sizeof(uint32_t);
-	}
+	/* Skip over any extra instructions */
+	link_target += extra_size * sizeof(uint32_t);
 
 	/* update offset for every cmd stream */
 	for (i = submit->nr_cmds; i--; ) {
@@ -227,26 +231,33 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		pr_info("event: %d\n", event);
 	}
 
-	if (gpu->mmu->need_flush) {
+	if (gpu->mmu->need_flush || gpu->switch_context) {
 		uint32_t new_target = gpu_va(gpu, buffer) + buffer->offset *
 					sizeof(uint32_t);
 
-		/* Add the MMU flush */
-		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
-			       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
-			       VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
-			       VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
-			       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
-			       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
+		if (gpu->mmu->need_flush) {
+			/* Add the MMU flush */
+			CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
+				       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
+				       VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
+				       VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
+				       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
+				       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
+
+			gpu->mmu->need_flush = false;
+		}
+
+		if (gpu->switch_context) {
+			etnaviv_cmd_select_pipe(buffer, submit->exec_state);
+			gpu->switch_context = false;
+		}

-		/* Update the link target to point to the flush */
+		/* Update the link target to point to above instructions */
 		link_target = new_target;
-		link_size = 4;
-
-		gpu->mmu->need_flush = false;
+		link_size = extra_size;
 	}
 
 	/* trigger event */
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index dbb80686fbb2..5bc2f733226c 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -132,6 +132,7 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)
 	init_waitqueue_head(&priv->fence_event);
 
 	INIT_LIST_HEAD(&priv->inactive_list);
+	priv->num_gpus = 0;

diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 3f53d24db2e2..29846b3edca0 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -51,6 +51,7 @@ struct etnaviv_file_private {
 };
 
 struct etnaviv_drm_private {
+	int num_gpus;
 	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
 	struct etnaviv_file_private *lastctx;
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 93720eccacaa..c801b9a161e6 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -109,6 +109,7 @@ static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
 struct etnaviv_gem_submit {
 	struct drm_device *dev;
 	struct etnaviv_gpu *gpu;
+	uint32_t exec_state;
 	struct list_head bo_list;
 	struct ww_acquire_ctx ticket;
 	uint32_t fence;
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index ee24fdc5eafa..301983aa317d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -349,6 +349,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		ret = -ENOMEM;
 		goto out;
 	}
+	submit->exec_state = args->exec_state;
 
 	ret = submit_lookup_objects(submit, args, file);
 	if (ret)
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 333c200733c1..d02605db2d73 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -28,7 +28,7 @@
 #include "cmdstream.xml.h"
 
 static const struct platform_device_id gpu_ids[] = {
-	{ .name = "etnaviv-gpu,2d", .driver_data = ETNA_PIPE_2D, },
+	{ .name = "etnaviv-gpu,2d" },
 	{ },
 };
 
@@ -919,8 +919,10 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	gpu->submitted_fence = submit->fence;
 	gpu->event[event].fence = submit->fence;
 
-	if (priv->lastctx != ctx)
+	if (priv->lastctx != ctx) {
 		gpu->mmu->need_flush = true;
+		gpu->switch_context = true;
+	}

@@ -1108,21 +1110,8 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	struct drm_device *drm = data;
 	struct etnaviv_drm_private *priv = drm->dev_private;
 	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
-	int idx = gpu->pipe;
 	int ret;
 
-	dev_dbg(dev, "pre gpu[idx]: %p\n", priv->gpu[idx]);
-
-	if (priv->gpu[idx] == NULL) {
-		dev_dbg(dev, "adding core @idx %d\n", idx);
-		priv->gpu[idx] = gpu;
-	} else {
-		dev_err(dev, "failed to add core @idx %d\n", idx);
-		goto fail;
-	}
-
-	dev_dbg(dev, "post gpu[idx]: %p\n", priv->gpu[idx]);
-
 #ifdef CONFIG_PM
 	ret = pm_runtime_get_sync(gpu->dev);
 #else
@@ -1140,12 +1129,12 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
 			(unsigned long)gpu);
 
+	priv->gpu[priv->num_gpus++] = gpu;
+
 	pm_runtime_mark_last_busy(gpu->dev);
 	pm_runtime_put_autosuspend(gpu->dev);
 
 	return 0;
-fail:
-	return -1;
 }
 
 static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
@@ -1186,23 +1175,13 @@ static const struct component_ops gpu_ops = {
 
 static const struct of_device_id etnaviv_gpu_match[] = {
 	{
-		.compatible = "vivante,vivante-gpu-2d",
-		.data = (void *)ETNA_PIPE_2D
+		.compatible = "vivante,gc"
 	},
-	{
-		.compatible = "vivante,vivante-gpu-3d",
-		.data = (void *)ETNA_PIPE_3D
-	},
-	{
-		.compatible = "vivante,vivante-gpu-vg",
-		.data = (void *)ETNA_PIPE_VG
-	},
-	{ }
+	{ /* sentinel */ }
 };
 
 static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 {
-	const struct of_device_id *match;
 	struct device *dev = &pdev->dev;
 	struct etnaviv_gpu *gpu;
 	int err = 0;
@@ -1211,17 +1190,6 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	if (!gpu)
 		return -ENOMEM;
 
-	if (pdev->dev.of_node) {
-		match = of_match_device(etnaviv_gpu_match, &pdev->dev);
-		if (!match)
-			return -EINVAL;
-		gpu->pipe = (long)match->data;
-	} else if (pdev->id_entry) {
-		gpu->pipe = pdev->id_entry->driver_data;
-	} else {
-		return -EINVAL;
-	}
-
 	gpu->dev = &pdev->dev;
 
 	/*
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 5f5147c0c0b4..786156701e14 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -87,7 +87,7 @@ struct etnaviv_gpu {
 	struct drm_device *drm;
 	struct device *dev;
 	struct etnaviv_chip_identity identity;
-	long pipe;
+	bool switch_context;
 
 	/* 'ring'-buffer: */
 	struct drm_gem_object *buffer;
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 52c6989ad93f..dfd51fcd56d6 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -34,12 +34,6 @@
  *     fields.. so that has to be somehow ok.
  */
 
-#define ETNA_PIPE_3D      0x00
-#define ETNA_PIPE_2D      0x01
-#define ETNA_PIPE_VG      0x02
-
-#define ETNA_MAX_PIPES    3
-
 /* timeouts are specified in clock-monotonic absolute times (to simplify
  * restarting interrupted ioctls).  The following struct is logically the
  * same as 'struct timespec' but 32/64b ABI safe.
@@ -70,8 +64,10 @@ struct drm_etnaviv_timespec {
 
 /* #define MSM_PARAM_GMEM_SIZE  0x02 */
 
+#define ETNA_MAX_PIPES 4
+
 struct drm_etnaviv_param {
-	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t pipe;           /* in */
 	uint32_t param;          /* in, ETNAVIV_PARAM_x */
 	uint64_t value;          /* out (get_param) or in (set_param) */
 };
@@ -182,11 +178,16 @@ struct drm_etnaviv_gem_submit_bo {
  * one or more cmdstream buffers.  This allows for conditional execution
  * (context-restore), and IB buffers needed for per tile/bin draw cmds.
  */
+#define ETNA_PIPE_3D      0x00
+#define ETNA_PIPE_2D      0x01
+#define ETNA_PIPE_VG      0x02
 struct drm_etnaviv_gem_submit {
-	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t pipe;           /* in */
+	uint32_t exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
 	uint32_t fence;          /* out */
 	uint32_t nr_bos;         /* in, number of submit_bo's */
 	uint32_t nr_cmds;        /* in, number of submit_cmd's */
+	uint32_t pad;
 	uint64_t bos;            /* in, ptr to array of submit_bo's */
 	uint64_t cmds;           /* in, ptr to array of submit_cmd's */
 };
@@ -199,7 +200,7 @@ struct drm_etnaviv_gem_submit {
  * APIs without requiring a dummy bo to synchronize on.
  */
 struct drm_etnaviv_wait_fence {
-	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t pipe;           /* in */
 	uint32_t fence;          /* in */
 	struct drm_etnaviv_timespec timeout;   /* in */
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

Drop the last remaining MSM bits and things we don't need for
Vivante GPUs. Those include shifting and or-ing of reloc addresses
and IB buffers.

Signed-off-by: Russell King <[email-masked]>
Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 include/uapi/drm/etnaviv_drm.h | 22 ++++------------------
 1 file changed, 4 insertions(+), 18 deletions(-)

diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index dfd51fcd56d6..c6ce72ae4dbe 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
@@ -62,8 +61,6 @@ struct drm_etnaviv_timespec {
 #define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT         0x18
 #define ETNAVIV_PARAM_GPU_NUM_CONSTANTS             0x19
 
-/* #define MSM_PARAM_GMEM_SIZE  0x02 */
-
 #define ETNA_MAX_PIPES 4
 
 struct drm_etnaviv_param {
@@ -116,35 +113,24 @@ struct drm_etnaviv_gem_cpu_fini {
  */
 
 /* The value written into the cmdstream is logically:
- *
- *   ((relocbuf->gpuaddr + reloc_offset) << shift) | or
- *
- * When we have GPU's w/ >32bit ptrs, it should be possible to deal
- * with this by emit'ing two reloc entries with appropriate shift
- * values.  Or a new ETNA_SUBMIT_CMD_x type would also be an option.
+ * relocbuf->gpuaddr + reloc_offset
  *
  * NOTE that reloc's must be sorted by order of increasing submit_offset,
  * otherwise EINVAL.
  */
 struct drm_etnaviv_gem_submit_reloc {
 	uint32_t submit_offset;  /* in, offset from submit_bo */
-	uint32_t or;             /* in, value OR'd with result */
-	int32_t  shift;          /* in, amount of left shift (can be -ve) */
 	uint32_t reloc_idx;      /* in, index of reloc_bo buffer */
 	uint64_t reloc_offset;   /* in, offset from start of reloc_bo */
 };
 
 /* submit-types:
  *   BUF - this cmd buffer is executed normally.
- *   IB_TARGET_BUF - this cmd buffer is an IB target.  Reloc's are
- *      processed normally, but the kernel does not setup an IB to
- *      this buffer in the first-level ringbuffer
  *   CTX_RESTORE_BUF - only executed if there has been a GPU context
  *      switch since the last SUBMIT ioctl
  */
 #define ETNA_SUBMIT_CMD_BUF             0x0001
-#define ETNA_SUBMIT_CMD_IB_TARGET_BUF   0x0002
-#define ETNA_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003
+#define ETNA_SUBMIT_CMD_CTX_RESTORE_BUF 0x0002
 struct drm_etnaviv_gem_submit_cmd {
 	uint32_t type;           /* in, one of ETNA_SUBMIT_CMD_x */
 	uint32_t submit_idx;     /* in, index of submit_bo cmdstream buffer */
@@ -216,7 +202,7 @@ struct drm_etnaviv_gem_userptr {
 
 #define DRM_ETNAVIV_GET_PARAM          0x00
 /* placeholder:
-#define DRM_MSM_SET_PARAM              0x01
+#define DRM_ETNAVIV_SET_PARAM          0x01
  */
 #define DRM_ETNAVIV_GEM_NEW            0x02
 #define DRM_ETNAVIV_GEM_INFO           0x03

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

This fixes the preceding patch for updates I have in my tree.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 301983aa317d..746cb55e24e7 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -259,13 +259,6 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 			return -EINVAL;
 		}
 
-		/* Vivante hardware has no need for shifts or bitwise or-ing */
-		if (submit_reloc.shift || submit_reloc.or) {
-			DRM_ERROR("invalid shift or bitwise or at reloc %u\n",
-				  i);
-			return -EINVAL;
-		}
-
 		ret = submit_bo(submit, submit_reloc.reloc_idx, &bobj, &iova);
 		if (ret)
 			return ret;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

etnaviv does not make use of dumb contiguous buffers; these are more
for KMS drivers than for render drivers.  These functions also clash
with similarly named functions in the MSM driver.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c |  3 ---
 drivers/staging/etnaviv/etnaviv_drv.h | 12 ------------
 drivers/staging/etnaviv/etnaviv_gem.c | 31 -------------------------------
 3 files changed, 46 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 5bc2f733226c..471b43291fc0 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -535,9 +535,6 @@ static struct drm_driver etnaviv_drm_driver = {
 	.set_busid          = drm_platform_set_busid,
 	.gem_free_object    = etnaviv_gem_free_object,
 	.gem_vm_ops         = &vm_ops,
-	.dumb_create        = msm_gem_dumb_create,
-	.dumb_map_offset    = msm_gem_dumb_map_offset,
-	.dumb_destroy       = drm_gem_dumb_destroy,
 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 	.gem_prime_export   = drm_gem_prime_export,
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 29846b3edca0..d26ad84615e5 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -80,10 +80,6 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
 	int id, uint32_t *iova);
 void etnaviv_gem_put_iova(struct drm_gem_object *obj);
-int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
-		struct drm_mode_create_dumb *args);
-int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
-		uint32_t handle, uint64_t *offset);
 struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj);
 void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
 void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
@@ -144,12 +140,4 @@ static inline bool fence_completed(struct drm_device *dev, uint32_t fence)
 	return fence_after_eq(priv->completed_fence, fence);
 }
 
-static inline int align_pitch(int width, int bpp)
-{
-	int bytespp = (bpp + 7) / 8;
-
-	/* adreno needs pitch aligned to 32 pixels: */
-	return bytespp * ALIGN(width, 32);
-}
-
 #endif /* __ETNAVIV_DRV_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 0ea9fba5d657..22407846320b 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -368,37 +368,6 @@ void etnaviv_gem_put_iova(struct drm_gem_object *obj)
 	 */
 }
 
-int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
-		struct drm_mode_create_dumb *args)
-{
-	args->pitch = align_pitch(args->width, args->bpp);
-	args->size  = PAGE_ALIGN(args->pitch * args->height);
-	/* TODO: re-check flags */
-	return etnaviv_gem_new_handle(dev, file, args->size,
-			ETNA_BO_WC, &args->handle);
-}
-
-int msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
-		uint32_t handle, uint64_t *offset)
-{
-	struct drm_gem_object *obj;
-	int ret = 0;
-
-	/* GEM does all our handle to object mapping */
-	obj = drm_gem_object_lookup(dev, file, handle);
-	if (obj == NULL) {
-		ret = -ENOENT;
-		goto fail;
-	}
-
-	*offset = etnaviv_gem_mmap_offset(obj);
-
-	drm_gem_object_unreference_unlocked(obj);
-
-fail:
-	return ret;
-}
-
 void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

Rename the last three msm_* symbols with an etnaviv_ prefix.  This
resolves some build errors when MSM and Etnaviv DRM drivers are both
built into the same kernel.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c       | 6 +++---
 drivers/staging/etnaviv/etnaviv_drv.h       | 6 +++---
 drivers/staging/etnaviv/etnaviv_gem_prime.c | 6 +++---
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 471b43291fc0..7e6edbadd2b9 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -539,12 +539,12 @@ static struct drm_driver etnaviv_drm_driver = {
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 	.gem_prime_export   = drm_gem_prime_export,
 	.gem_prime_import   = drm_gem_prime_import,
-	.gem_prime_pin      = msm_gem_prime_pin,
-	.gem_prime_unpin    = msm_gem_prime_unpin,
+	.gem_prime_pin      = etnaviv_gem_prime_pin,
+	.gem_prime_unpin    = etnaviv_gem_prime_unpin,
 	.gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table,
 	.gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table,
 	.gem_prime_vmap     = etnaviv_gem_prime_vmap,
-	.gem_prime_vunmap   = msm_gem_prime_vunmap,
+	.gem_prime_vunmap   = etnaviv_gem_prime_vunmap,
 #ifdef CONFIG_DEBUG_FS
 	.debugfs_init       = etnaviv_debugfs_init,
 	.debugfs_cleanup    = etnaviv_debugfs_cleanup,
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index d26ad84615e5..cde898f33c53 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -82,11 +82,11 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
 void etnaviv_gem_put_iova(struct drm_gem_object *obj);
 struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj);
 void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
-void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
 	struct dma_buf_attachment *attach, struct sg_table *sg);
-int msm_gem_prime_pin(struct drm_gem_object *obj);
-void msm_gem_prime_unpin(struct drm_gem_object *obj);
+int etnaviv_gem_prime_pin(struct drm_gem_object *obj);
+void etnaviv_gem_prime_unpin(struct drm_gem_object *obj);
 void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj);
 void *etnaviv_gem_vaddr(struct drm_gem_object *obj);
 dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj);
diff --git a/drivers/staging/etnaviv/etnaviv_gem_prime.c b/drivers/staging/etnaviv/etnaviv_gem_prime.c
index cca569be98bf..58c13ae7c345 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -34,12 +34,12 @@ void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj)
 	return etnaviv_gem_vaddr(obj);
 }
 
-void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 {
 	/* TODO msm_gem_vunmap() */
 }
 
-int msm_gem_prime_pin(struct drm_gem_object *obj)
+int etnaviv_gem_prime_pin(struct drm_gem_object *obj)
 {
 	if (!obj->import_attach) {
 		struct drm_device *dev = obj->dev;
@@ -51,7 +51,7 @@ int msm_gem_prime_pin(struct drm_gem_object *obj)
 	return 0;
 }
 
-void msm_gem_prime_unpin(struct drm_gem_object *obj)
+void etnaviv_gem_prime_unpin(struct drm_gem_object *obj)
 {
 	if (!obj->import_attach) {
 		struct drm_device *dev = obj->dev;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 2 +-
 drivers/staging/etnaviv/etnaviv_gpu.c        | 2 +-
 drivers/staging/etnaviv/etnaviv_gpu.h        | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 746cb55e24e7..6e55da0a4826 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -24,7 +24,7 @@
  */
 
 #define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
-/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
+/* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */
 #define BO_LOCKED   0x4000
 #define BO_PINNED   0x2000
 
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index d02605db2d73..22bc4a4b4ac3 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -736,7 +736,7 @@ static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
 {
 	DBG("%s", dev_name(gpu->dev));
 	mod_timer(&gpu->hangcheck_timer,
-			round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES));
+		  round_jiffies_up(jiffies + DRM_ETNAVIV_HANGCHECK_JIFFIES));
 }
 
 static void hangcheck_handler(unsigned long data)
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 786156701e14..11584259650e 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -121,8 +121,8 @@ struct etnaviv_gpu {
 	struct clk *clk_shader;
 
 	/* Hang Detction: */
-#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
-#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
+#define DRM_ETNAVIV_HANGCHECK_PERIOD 500 /* in ms */
+#define DRM_ETNAVIV_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_ETNAVIV_HANGCHECK_PERIOD)
 	struct timer_list hangcheck_timer;
 	uint32_t hangcheck_fence;
 	uint32_t hangcheck_dma_addr;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

There is no need to spam the kernel logs with the GPU specs and features
at startup. If someone wants to know about this stuff debugfs should be
the right place to look at.

Also use better format specifiers to make it easier for humans to read.

Signed-off-by: Christian Gmeiner <[email-masked]>
Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 64 ++++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 22bc4a4b4ac3..87255b999cdf 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -187,27 +187,6 @@ static void etnaviv_hw_specs(struct etnaviv_gpu *gpu)
 		gpu->identity.instruction_count = 256;
 		break;
 	}
-
-	dev_info(gpu->dev, "stream_count:  %x\n",
-		 gpu->identity.stream_count);
-	dev_info(gpu->dev, "register_max: %x\n",
-		 gpu->identity.register_max);
-	dev_info(gpu->dev, "thread_count: %x\n",
-		 gpu->identity.thread_count);
-	dev_info(gpu->dev, "vertex_cache_size: %x\n",
-		 gpu->identity.vertex_cache_size);
-	dev_info(gpu->dev, "shader_core_count: %x\n",
-		 gpu->identity.shader_core_count);
-	dev_info(gpu->dev, "pixel_pipes: %x\n",
-		 gpu->identity.pixel_pipes);
-	dev_info(gpu->dev, "vertex_output_buffer_size: %x\n",
-		 gpu->identity.vertex_output_buffer_size);
-	dev_info(gpu->dev, "buffer_size: %x\n",
-		 gpu->identity.buffer_size);
-	dev_info(gpu->dev, "instruction_count: %x\n",
-		 gpu->identity.instruction_count);
-	dev_info(gpu->dev, "num_constants: %x\n",
-		 gpu->identity.num_constants);
 }
 
 static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
@@ -252,7 +231,7 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 		}
 	}
 
-	dev_info(gpu->dev, "model: %x - revision %x\n",
+	dev_info(gpu->dev, "model: GC%x, revision: %x\n",
 		 gpu->identity.model, gpu->identity.revision);
 
 	gpu->identity.features = gpu_read(gpu, VIVS_HI_CHIP_FEATURE);
@@ -286,15 +265,6 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
 				gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3);
 	}
 
-	dev_info(gpu->dev, "minor_features:  %x\n",
-		 gpu->identity.minor_features0);
-	dev_info(gpu->dev, "minor_features1: %x\n",
-		 gpu->identity.minor_features1);
-	dev_info(gpu->dev, "minor_features2: %x\n",
-		 gpu->identity.minor_features2);
-	dev_info(gpu->dev, "minor_features3: %x\n",
-		 gpu->identity.minor_features3);
-
 	/* GC600 idle register reports zero bits where modules aren't present */
 	if (gpu->identity.model == chipModel_GC600) {
 		gpu->idle_mask = VIVS_HI_IDLE_STATE_TX |
@@ -604,6 +574,38 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 
 	verify_dma(gpu, &debug);
 
+	seq_puts(m, "\tfeatures\n");
+	seq_printf(m, "\t minor_features0: 0x%08x\n",
+		   gpu->identity.minor_features0);
+	seq_printf(m, "\t minor_features1: 0x%08x\n",
+		   gpu->identity.minor_features1);
+	seq_printf(m, "\t minor_features2: 0x%08x\n",
+		   gpu->identity.minor_features2);
+	seq_printf(m, "\t minor_features3: 0x%08x\n",
+		   gpu->identity.minor_features3);
+
+	seq_puts(m, "\tspecs\n");
+	seq_printf(m, "\t stream_count:  %d\n",
+			gpu->identity.stream_count);
+	seq_printf(m, "\t register_max: %d\n",
+			gpu->identity.register_max);
+	seq_printf(m, "\t thread_count: %d\n",
+			gpu->identity.thread_count);
+	seq_printf(m, "\t vertex_cache_size: %d\n",
+			gpu->identity.vertex_cache_size);
+	seq_printf(m, "\t shader_core_count: %d\n",
+			gpu->identity.shader_core_count);
+	seq_printf(m, "\t pixel_pipes: %d\n",
+			gpu->identity.pixel_pipes);
+	seq_printf(m, "\t vertex_output_buffer_size: %d\n",
+			gpu->identity.vertex_output_buffer_size);
+	seq_printf(m, "\t buffer_size: %d\n",
+			gpu->identity.buffer_size);
+	seq_printf(m, "\t instruction_count: %d\n",
+			gpu->identity.instruction_count);
+	seq_printf(m, "\t num_constants: %d\n",
+			gpu->identity.num_constants);
+
 	seq_printf(m, "\taxi: 0x%08x\n", axi);
 	seq_printf(m, "\tidle: 0x%08x\n", idle);
 	idle |= ~gpu->idle_mask & ~VIVS_HI_IDLE_STATE_AXI_LP;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     |  2 +-
 drivers/staging/etnaviv/etnaviv_cmd_parser.c | 16 ++++++++++++++++
 drivers/staging/etnaviv/etnaviv_drv.c        |  3 +--
 drivers/staging/etnaviv/etnaviv_drv.h        |  3 +--
 drivers/staging/etnaviv/etnaviv_gem.c        |  3 +--
 drivers/staging/etnaviv/etnaviv_gem.h        |  3 +--
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  3 +--
 drivers/staging/etnaviv/etnaviv_gpu.c        |  3 +--
 drivers/staging/etnaviv/etnaviv_gpu.h        |  3 +--
 drivers/staging/etnaviv/etnaviv_mmu.c        |  3 +--
 drivers/staging/etnaviv/etnaviv_mmu.h        |  3 +--
 11 files changed, 26 insertions(+), 19 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index a662288a1298..b88fa49f2525 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 2014 Etnaviv Project
+ * Copyright (C) 2014 Etnaviv Project
  * Author: Christian Gmeiner <[email-masked]>
  *
  * This program is free software; you can redistribute it and/or modify it
diff --git a/drivers/staging/etnaviv/etnaviv_cmd_parser.c b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
index 03552c5118bf..7ae6ddb4306e 100644
--- a/drivers/staging/etnaviv/etnaviv_cmd_parser.c
+++ b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * 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 <linux/kernel.h>
 
 #include "etnaviv_gem.h"
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 7e6edbadd2b9..6e5117a4bded 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index cde898f33c53..9abb7f8c2dc0 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 22407846320b..0b823a9566d3 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index c801b9a161e6..397b30f39708 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 6e55da0a4826..647d16fee98b 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 87255b999cdf..1b1b1a659715 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 11584259650e..a97aac860cfd 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 6da6b8e0c6e5..ec01c8a560f4 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index a8e48b2a61dc..94dbab736fc1 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2013 Red Hat
- * Author: Rob Clark <[email-masked]>
+ * Copyright (C) 2015 Etnaviv Project
  *
  * 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

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

- correct license to the proper GPLv2
- add correct author names
- remove double MODULE_DEVICE_TABLE
- update driver date

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 6e5117a4bded..4beca62c91f3 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -553,7 +553,7 @@ static struct drm_driver etnaviv_drm_driver = {
 	.fops               = &fops,
 	.name               = "etnaviv",
 	.desc               = "etnaviv DRM",
-	.date               = "20130625",
+	.date               = "20150302",
 	.major              = 1,
 	.minor              = 0,
 };
@@ -638,7 +638,7 @@ static struct platform_driver etnaviv_platform_driver = {
 	.remove     = etnaviv_pdev_remove,
 	.driver     = {
 		.owner  = THIS_MODULE,
-		.name   = "vivante",
+		.name   = "etnaviv",
 		.of_match_table = dt_match,
 	},
 };
@@ -666,8 +666,9 @@ static void __exit etnaviv_exit(void)
 }
 module_exit(etnaviv_exit);
 
-MODULE_AUTHOR("Rob Clark <[email-masked]");
+MODULE_AUTHOR("Christian Gmeiner <[email-masked]>");
+MODULE_AUTHOR("Russell King <[email-masked]>");
+MODULE_AUTHOR("Lucas Stach <[email-masked]>");
 MODULE_DESCRIPTION("etnaviv DRM Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:vivante");
-MODULE_DEVICE_TABLE(of, dt_match);
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:etnaviv");

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 4beca62c91f3..d3ef7b15593b 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -629,6 +629,7 @@ static int etnaviv_pdev_remove(struct platform_device *pdev)
 
 static const struct of_device_id dt_match[] = {
 	{ .compatible = "fsl,imx-gpu-subsystem" },
+	{ .compatible = "marvell,dove-gpu-subsystem" },
 	{}
 };
 MODULE_DEVICE_TABLE(of, dt_match);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

This adds the device nodes for 2D, 3D and VG GPU cores.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/imx6dl.dtsi  |  5 +++++
 arch/arm/boot/dts/imx6q.dtsi   | 14 ++++++++++++++
 arch/arm/boot/dts/imx6qdl.dtsi | 20 ++++++++++++++++++++
 3 files changed, 39 insertions(+)

diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 4b0ec0703825..51c517a5cafd 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -104,6 +104,11 @@
 		compatible = "fsl,imx-display-subsystem";
 		ports = <&ipu1_di0>, <&ipu1_di1>;
 	};
+
+	gpu-subsystem {
+		compatible = "fsl,imx-gpu-subsystem";
+		cores = <&gpu_2d>, <&gpu_3d>;
+	};
 };
 
 &gpt {
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 399103b8e2c9..2fd0faa1fd72 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -153,6 +153,15 @@
 			status = "disabled";
 		};
 
+		gpu_vg: gpu@02204000 {
+			compatible = "vivante,gc";
+			reg = <0x02204000 0x4000>;
+			interrupts = <0 11 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clks IMX6QDL_CLK_OPENVG_AXI>,
+				 <&clks IMX6QDL_CLK_GPU2D_CORE>;
+			clock-names = "bus", "core";
+		};
+
 		ipu2: ipu@02800000 {
 			#address-cells = <1>;
 			#size-cells = <0>;
@@ -225,6 +234,11 @@
 		compatible = "fsl,imx-display-subsystem";
 		ports = <&ipu1_di0>, <&ipu1_di1>, <&ipu2_di0>, <&ipu2_di1>;
 	};
+
+	gpu-subsystem {
+		compatible = "fsl,imx-gpu-subsystem";
+		cores = <&gpu_2d>, <&gpu_3d>, <&gpu_vg>;
+	};
 };
 
 &hdmi {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index b57033e8c633..15504e823cc6 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -119,6 +119,26 @@
 			status = "disabled";
 		};
 
+
+		gpu_2d: gpu@00134000 {
+			compatible = "vivante,gc";
+			reg = <0x00134000 0x4000>;
+			interrupts = <0 10 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clks IMX6QDL_CLK_GPU2D_AXI>,
+				 <&clks IMX6QDL_CLK_GPU2D_CORE>;
+			clock-names = "bus", "core";
+		};
+
+		gpu_3d: gpu@00130000 {
+			compatible = "vivante,gc";
+			reg = <0x00130000 0x4000>;
+			interrupts = <0 9 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clks IMX6QDL_CLK_GPU3D_AXI>,
+				 <&clks IMX6QDL_CLK_GPU3D_CORE>,
+				 <&clks IMX6QDL_CLK_GPU3D_SHADER>;
+			clock-names = "bus", "core", "shader";
+		};
+
 		hdmi: hdmi@0120000 {
 			#address-cells = <1>;
 			#size-cells = <0>;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:14 +0100

v4.1 requires the power domains to be specified for Vivante GPU nodes,
otherwise they fail to be detected.

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/imx6qdl.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 15504e823cc6..9b24bfe191a8 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -127,6 +127,7 @@
 			clocks = <&clks IMX6QDL_CLK_GPU2D_AXI>,
 				 <&clks IMX6QDL_CLK_GPU2D_CORE>;
 			clock-names = "bus", "core";
+			power-domains = <&gpc 1>;
 		};
 
 		gpu_3d: gpu@00130000 {
@@ -137,6 +138,7 @@
 				 <&clks IMX6QDL_CLK_GPU3D_CORE>,
 				 <&clks IMX6QDL_CLK_GPU3D_SHADER>;
 			clock-names = "bus", "core", "shader";
+			power-domains = <&gpc 1>;
 		};
 
 		hdmi: hdmi@0120000 {

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

The only failure which etnaviv_load() implemented was for kzalloc().
Other failures (alloc_ordered_workqueue(), component_bind_all()) were
not implemented.  Add these.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index d3ef7b15593b..9d2ba044d8b9 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -128,6 +128,11 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)
 	dev->dev_private = priv;
 
 	priv->wq = alloc_ordered_workqueue("etnaviv", 0);
+	if (!priv->wq) {
+		err = -ENOMEM;
+		goto err_wq;
+	}
+
 	init_waitqueue_head(&priv->fence_event);
 
 	INIT_LIST_HEAD(&priv->inactive_list);
@@ -137,11 +142,18 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)
 
-		return err;
+		goto err_bind;
 
 	load_gpu(dev);
 
 	return 0;
+
+err_bind:
+err_wq:
+	kfree(priv);
+	return err;
 }
 
 static int etnaviv_open(struct drm_device *dev, struct drm_file *file)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

We're only ever going to insert using a pointer to a struct page.  Use
vm_insert_page() rather than converting back and forth between a
struct page and a PFN.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 0b823a9566d3..69360c899b78 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -217,8 +217,7 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	struct drm_gem_object *obj = vma->vm_private_data;
 	struct drm_device *dev = obj->dev;
-	struct page **pages;
-	unsigned long pfn;
+	struct page **pages, *page;
 	pgoff_t pgoff;
 	int ret;
 
@@ -240,12 +239,12 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	pgoff = ((unsigned long)vmf->virtual_address -
 			vma->vm_start) >> PAGE_SHIFT;
 
-	pfn = page_to_pfn(pages[pgoff]);
+	page = pages[pgoff];
 
 	VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
-			pfn, pfn << PAGE_SHIFT);
+	     page_to_pfn(page), page_to_pfn(page) << PAGE_SHIFT);
 
-	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+	ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
 
 out_unlock:

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Reduce the exposure of struct_mutex to an absolute minimum.  We only
need to take this lock over the call to etnaviv_gem_get_pages() as
vm_insert_page() already does a properly synchronised update of the
PTE (using the pte table lock.)

Reducing the code covered by this lock allows for more parallel
execution in SMP environments.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 69360c899b78..522e5a6c3612 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -221,8 +221,10 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	pgoff_t pgoff;
 	int ret;
 
-	/* Make sure we don't parallel update on a fault, nor move or remove
-	 * something from beneath our feet
+	/*
+	 * Make sure we don't parallel update on a fault, nor move or remove
+	 * something from beneath our feet.  Note that vm_insert_page() is
+	 * specifically coded to take care of this, so we don't have to.
 	 */
 	ret = mutex_lock_interruptible(&dev->struct_mutex);
 	if (ret)
@@ -230,9 +232,11 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
 	/* make sure we have pages attached now */
 	pages = etnaviv_gem_get_pages(to_etnaviv_bo(obj));
+	mutex_unlock(&dev->struct_mutex);
+
 	if (IS_ERR(pages)) {
 		ret = PTR_ERR(pages);
-		goto out_unlock;
+		goto out;
 	}
 
 	/* We don't use vmf->pgoff since that has the fake offset: */
@@ -246,8 +250,6 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
 	ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
 
-out_unlock:
 out:
 	switch (ret) {
 	case -EAGAIN:

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

etnaviv_gem_mmap_offset() returned zero if drm_gem_create_mmap_offset()
failed.  This is incorrect behaviour as etnaviv_ioctl_gem_info() does
not detect this condition, so it returns success.

Fix this by re-architecting etnaviv_gem_mmap_offset().  Merge
mmap_offset() into this function, and change its prototype to accept a
pointer to the "offset" return value, thereby allowing it to return a
standard negative errno code.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c |  4 ++--
 drivers/staging/etnaviv/etnaviv_drv.h |  2 +-
 drivers/staging/etnaviv/etnaviv_gem.c | 26 +++++++-------------------
 3 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 9d2ba044d8b9..e149b0735d69 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -438,7 +438,7 @@ static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data,
 {
 	struct drm_etnaviv_gem_info *args = data;
 	struct drm_gem_object *obj;
-	int ret = 0;
+	int ret;
 
 	if (args->pad)
 		return -EINVAL;
@@ -447,7 +447,7 @@ static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data,
 	if (!obj)
 		return -ENOENT;
 
-	args->offset = etnaviv_gem_mmap_offset(obj);
+	ret = etnaviv_gem_mmap_offset(obj, &args->offset);
 
 	drm_gem_object_unreference_unlocked(obj);
 
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 9abb7f8c2dc0..37992342bbf0 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -73,7 +73,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 
 int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
-uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj);
+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, uint64_t *offset);
 int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	struct drm_gem_object *obj, uint32_t *iova);
 int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 522e5a6c3612..38dee80d996c 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -270,33 +270,21 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 }
 
 /** get mmap offset */
-static uint64_t mmap_offset(struct drm_gem_object *obj)
+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, uint64_t *offset)
 {
 	struct drm_device *dev = obj->dev;
 	int ret;
 
-	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-
+	mutex_lock(&dev->struct_mutex);
 	/* Make it mmapable */
 	ret = drm_gem_create_mmap_offset(obj);
-
-	if (ret) {
+	if (ret)
 		dev_err(dev->dev, "could not allocate mmap offset\n");
-		return 0;
-	}
-
-	return drm_vma_node_offset_addr(&obj->vma_node);
-}
-
-uint64_t etnaviv_gem_mmap_offset(struct drm_gem_object *obj)
-{
-	uint64_t offset;
-
-	mutex_lock(&obj->dev->struct_mutex);
-	offset = mmap_offset(obj);
-	mutex_unlock(&obj->dev->struct_mutex);
+	else
+		*offset = drm_vma_node_offset_addr(&obj->vma_node);
+	mutex_unlock(&dev->struct_mutex);
 
-	return offset;
+	return ret;
 }
 
 /* should be called under struct_mutex.. although it can be called

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Move the locking out of etnaviv_gem_mmap_offset() into its solitary
caller, and take the mutex using mutex_lock_interruptible().  This
allows etnaviv_ioctl_gem_info() to handle signals while trying to
obtain this lock.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 7 ++++++-
 drivers/staging/etnaviv/etnaviv_gem.c | 7 ++-----
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index e149b0735d69..02205551b699 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -447,7 +447,12 @@ static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data,
 	if (!obj)
 		return -ENOENT;
 
-	ret = etnaviv_gem_mmap_offset(obj, &args->offset);
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret == 0) {
+		ret = etnaviv_gem_mmap_offset(obj, &args->offset);
+
+		mutex_unlock(&dev->struct_mutex);
+	}

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 38dee80d996c..b1984a4ac0c5 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -269,20 +269,17 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 	}
 }
 
-/** get mmap offset */
+/* get mmap offset - must be called under struct_mutex */
 int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, uint64_t *offset)
 {
-	struct drm_device *dev = obj->dev;
 	int ret;
 
-	mutex_lock(&dev->struct_mutex);
 	/* Make it mmapable */
 	ret = drm_gem_create_mmap_offset(obj);
 	if (ret)
-		dev_err(dev->dev, "could not allocate mmap offset\n");
+		dev_err(obj->dev->dev, "could not allocate mmap offset\n");
 	else
 		*offset = drm_vma_node_offset_addr(&obj->vma_node);
-	mutex_unlock(&dev->struct_mutex);
 
 	return ret;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Tracking the current context on a system wide level is incorrect: if the
context for one GPU core changes, the context does not change on other
GPU cores.  Make the context tracking a per-GPU thing.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 9 +++++++--
 drivers/staging/etnaviv/etnaviv_drv.h | 1 -
 drivers/staging/etnaviv/etnaviv_gpu.c | 5 ++---
 drivers/staging/etnaviv/etnaviv_gpu.h | 1 +
 4 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 02205551b699..39edc3f3a486 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -173,10 +173,15 @@ static void etnaviv_preclose(struct drm_device *dev, struct drm_file *file)
 {
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	struct etnaviv_file_private *ctx = file->driver_priv;
+	unsigned int i;
 
 	mutex_lock(&dev->struct_mutex);
-	if (ctx == priv->lastctx)
-		priv->lastctx = NULL;
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		struct etnaviv_gpu *gpu = priv->gpu[i];
+
+		if (gpu && gpu->lastctx == ctx)
+			gpu->lastctx = NULL;
+	}
 	mutex_unlock(&dev->struct_mutex);
 
 	kfree(ctx);
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 37992342bbf0..f7aff4b2562c 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -52,7 +52,6 @@ struct etnaviv_file_private {
 struct etnaviv_drm_private {
 	int num_gpus;
 	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
-	struct etnaviv_file_private *lastctx;
 
 	uint32_t next_fence, completed_fence;
 	wait_queue_head_t fence_event;
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 1b1b1a659715..415a8455e3b4 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -920,15 +920,14 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	gpu->submitted_fence = submit->fence;
 	gpu->event[event].fence = submit->fence;
 
-	if (priv->lastctx != ctx) {
+	if (gpu->lastctx != ctx) {
 		gpu->mmu->need_flush = true;
 		gpu->switch_context = true;
+		gpu->lastctx = ctx;
 	}

-	priv->lastctx = ctx;
-
 
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index a97aac860cfd..5f53cc53880d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -86,6 +86,7 @@ struct etnaviv_gpu {
 	struct drm_device *drm;
 	struct device *dev;
 	struct etnaviv_chip_identity identity;
+	struct etnaviv_file_private *lastctx;
 	bool switch_context;
 
 	/* 'ring'-buffer: */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Having a global fence completion implementation in the presence of
mutliple GPU cores which can complete out of order is buggy.  Consider
the case of two processes submitting work for two GPU cores:

	GPU A				GPU B
	priv->completed_fence = 0
	gpu->submitted_fence = 0	gpu->submitted_fence = 0
	gpu->retired_fence = 0		gpu->retired_fence = 0

	process A submits work,
	allocated fence 1,
	gpu->submitted_fence = 1,
	priv->next_fence = 2
					process B submits work,
					allocated fence 2,
					gpu->submitted_fence = 2,
					priv->next_fence = 3

					GPU B finishes,
					gpu->retired_fence = 2
					runs work queue,
					priv->completed_fence = 2

At this point, GPU A's buffers have fence 1, and when tested by
etnaviv_wait_fence_interruptable(), it compares fence 1 to the global
priv->completed_fence, and decides that GPU A has also finished its
work.

This is plainly incorrect at this point.

Solve this by moving etnaviv_wait_fence_interruptable() into the GPU
code, and arrange for it to decide whether the fence has completed
based upon the GPUs submitted and retired fence markers.

This allows us to get rid of the global priv->completed_fence, and
change the global fence wait queue to a per-GPU fence wait queue, thus
avoiding disturbing sleepers on other (busy) GPUs.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 64 ++---------------------------------
 drivers/staging/etnaviv/etnaviv_drv.h | 15 +-------
 drivers/staging/etnaviv/etnaviv_gem.c |  7 ++--
 drivers/staging/etnaviv/etnaviv_gpu.c | 44 ++++++++++++++++++++++--
 drivers/staging/etnaviv/etnaviv_gpu.h | 10 ++++++
 5 files changed, 57 insertions(+), 83 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 39edc3f3a486..fcddb7a6b979 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -133,8 +133,6 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)
 		goto err_wq;
 	}
 
-	init_waitqueue_head(&priv->fence_event);
-
 	INIT_LIST_HEAD(&priv->inactive_list);
 	priv->num_gpus = 0;
 
@@ -310,64 +308,6 @@ static void etnaviv_debugfs_cleanup(struct drm_minor *minor)
 #endif
 
 /*
- * Fences:
- */
-int etnaviv_wait_fence_interruptable(struct drm_device *dev,
-		struct etnaviv_gpu *gpu, uint32_t fence,
-		struct timespec *timeout)
-{
-	struct etnaviv_drm_private *priv = dev->dev_private;
-	int ret;
-
-	if (fence_after(fence, gpu->submitted_fence)) {
-		DRM_ERROR("waiting on invalid fence: %u (of %u)\n",
-				fence, gpu->submitted_fence);
-		return -EINVAL;
-	}
-
-	if (!timeout) {
-		/* no-wait: */
-		ret = fence_completed(dev, fence) ? 0 : -EBUSY;
-	} else {
-		unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
-		unsigned long start_jiffies = jiffies;
-		unsigned long remaining_jiffies;
-
-		if (time_after(start_jiffies, timeout_jiffies))
-			remaining_jiffies = 0;
-		else
-			remaining_jiffies = timeout_jiffies - start_jiffies;
-
-		ret = wait_event_interruptible_timeout(priv->fence_event,
-				fence_completed(dev, fence),
-				remaining_jiffies);
-
-		if (ret == 0) {
-			DBG("timeout waiting for fence: %u (completed: %u)",
-					fence, priv->completed_fence);
-			ret = -ETIMEDOUT;
-		} else if (ret != -ERESTARTSYS) {
-			ret = 0;
-		}
-	}
-
-	return ret;
-}
-
-/* called from workqueue */
-void etnaviv_update_fence(struct drm_device *dev, uint32_t fence)
-{
-	struct etnaviv_drm_private *priv = dev->dev_private;
-
-	mutex_lock(&dev->struct_mutex);
-	if (fence_after(fence, priv->completed_fence))
-		priv->completed_fence = fence;
-	mutex_unlock(&dev->struct_mutex);
-
-	wake_up_all(&priv->fence_event);
-}
-
-/*
  * DRM ioctls:
  */
 
@@ -478,8 +418,8 @@ static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data,
 	if (!gpu)
 		return -ENXIO;
 
-	return etnaviv_wait_fence_interruptable(dev, gpu,
-		args->fence, &TS(args->timeout));
+	return etnaviv_gpu_wait_fence_interruptible(gpu, args->fence,
+						    &TS(args->timeout));
 }
 
 static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index f7aff4b2562c..cfe5fe493d38 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -53,8 +53,7 @@ struct etnaviv_drm_private {
 	int num_gpus;
 	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
 
-	uint32_t next_fence, completed_fence;
-	wait_queue_head_t fence_event;
+	uint32_t next_fence;
 
 	/* list of GEM objects: */
 	struct list_head inactive_list;
@@ -62,11 +61,6 @@ struct etnaviv_drm_private {
 	struct workqueue_struct *wq;
 };
 
-int etnaviv_wait_fence_interruptable(struct drm_device *dev,
-		struct etnaviv_gpu *gpu, uint32_t fence,
-		struct timespec *timeout);
-void etnaviv_update_fence(struct drm_device *dev, uint32_t fence);
-
 int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		struct drm_file *file);
 
@@ -131,11 +125,4 @@ static inline bool fence_after_eq(uint32_t a, uint32_t b)
 	return (int32_t)(a - b) >= 0;
 }
 
-static inline bool fence_completed(struct drm_device *dev, uint32_t fence)
-{
-	struct etnaviv_drm_private *priv = dev->dev_private;
-
-	return fence_after_eq(priv->completed_fence, fence);
-}
-
 #endif /* __ETNAVIV_DRV_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index b1984a4ac0c5..aaa0f5eaeabd 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -429,13 +429,11 @@ void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj)
 int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 		struct timespec *timeout)
 {
-
-	struct drm_device *dev = obj->dev;
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-
 	int ret = 0;
 
 	if (is_active(etnaviv_obj)) {
+		struct etnaviv_gpu *gpu = etnaviv_obj->gpu;
 		uint32_t fence = 0;
 
 		if (op & ETNA_PREP_READ)
@@ -445,8 +443,7 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 		if (op & ETNA_PREP_NOSYNC)
 			timeout = NULL;
 
-		ret = etnaviv_wait_fence_interruptable(dev, etnaviv_obj->gpu,
-						       fence, timeout);
+		ret = etnaviv_gpu_wait_fence_interruptible(gpu, fence, timeout);
 	}
 
 	/* TODO cache maintenance */
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 415a8455e3b4..9c493cfa7174 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -841,8 +841,6 @@ static void retire_worker(struct work_struct *work)
 	struct drm_device *dev = gpu->drm;
 	uint32_t fence = gpu->retired_fence;
 
-	etnaviv_update_fence(gpu->drm, fence);
-
 	mutex_lock(&dev->struct_mutex);
 
 	while (!list_empty(&gpu->active_list)) {
@@ -865,6 +863,8 @@ static void retire_worker(struct work_struct *work)
 	}
 
 	mutex_unlock(&dev->struct_mutex);
+
+	wake_up_all(&gpu->fence_event);
 }
 
 /* call from irq handler to schedule work to retire bo's */
@@ -875,6 +875,45 @@ void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
 	queue_work(priv->wq, &gpu->retire_work);
 }
 
+int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
+	uint32_t fence, struct timespec *timeout)
+{
+	int ret;
+
+	if (fence_after(fence, gpu->submitted_fence)) {
+		DRM_ERROR("waiting on invalid fence: %u (of %u)\n",
+				fence, gpu->submitted_fence);
+		return -EINVAL;
+	}
+
+	if (!timeout) {
+		/* No timeout was requested: just test for completion */
+		ret = fence_completed(gpu, fence) ? 0 : -EBUSY;
+	} else {
+		unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
+		unsigned long start_jiffies = jiffies;
+		unsigned long remaining_jiffies;
+
+		if (time_after(start_jiffies, timeout_jiffies))
+			remaining_jiffies = 0;
+		else
+			remaining_jiffies = timeout_jiffies - start_jiffies;
+
+		ret = wait_event_interruptible_timeout(gpu->fence_event,
+						fence_completed(gpu, fence),
+						remaining_jiffies);
+		if (ret == 0) {
+			DBG("timeout waiting for fence: %u (completed: %u)",
+					fence, gpu->retired_fence);
+			ret = -ETIMEDOUT;
+		} else if (ret != -ERESTARTSYS) {
+			ret = 0;
+		}
+	}
+
+	return ret;
+}
+
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu)
 {
 	return pm_runtime_get_sync(gpu->dev);
@@ -1125,6 +1164,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	INIT_LIST_HEAD(&gpu->active_list);
 	INIT_WORK(&gpu->retire_work, retire_worker);
 	INIT_WORK(&gpu->recover_work, recover_worker);
+	init_waitqueue_head(&gpu->fence_event);
 
 	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
 			(unsigned long)gpu);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 5f53cc53880d..6981d17f6252 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -104,8 +104,11 @@ struct etnaviv_gpu {
 	struct list_head active_list;
 
 	uint32_t idle_mask;
+
+	/* Fencing support */
 
 	/* worker for handling active-list retiring: */
 	struct work_struct retire_work;
@@ -139,6 +142,11 @@ static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg)
 	return etnaviv_readl(gpu->mmio + reg);
 }
 
+static inline bool fence_completed(struct etnaviv_gpu *gpu, uint32_t fence)
+{
+	return fence_after_eq(gpu->retired_fence, fence);
+}
+
 int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
 	uint64_t *value);
 
@@ -149,6 +157,8 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
 #endif
 
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
+int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
+	uint32_t fence, struct timespec *timeout);
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Provide a helper to queue a work_struct onto our private workqueue.
This avoids repetitions of:

	struct drm_device *dev = ...
	struct etnaviv_private *priv = dev->dev_private;

	queue_work(priv->wq, w);

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.h |  7 +++++++
 drivers/staging/etnaviv/etnaviv_gem.c |  4 +---
 drivers/staging/etnaviv/etnaviv_gpu.c | 18 +++++-------------
 3 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index cfe5fe493d38..ae379dd1deab 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -61,6 +61,13 @@ struct etnaviv_drm_private {
 	struct workqueue_struct *wq;
 };
 
+static void etnaviv_queue_work(struct drm_device *dev, struct work_struct *w)
+{
+	struct etnaviv_drm_private *priv = drm->dev_private;
+
+	queue_work(priv->wq, w);
+}
+
 int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		struct drm_file *file);
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index aaa0f5eaeabd..6e114a390101 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -800,7 +800,6 @@ static void __etnaviv_gem_userptr_get_pages(struct work_struct *_work)
 
 static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 {
-	struct etnaviv_drm_private *priv;
 	struct page **pvec = NULL;
 	struct get_pages_work *work;
 	struct mm_struct *mm;
@@ -859,8 +858,7 @@ static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj)
 	etnaviv_obj->userptr.work = &work->work;
 	INIT_WORK(&work->work, __etnaviv_gem_userptr_get_pages);
 
-	priv = etnaviv_obj->base.dev->dev_private;
-	queue_work(priv->wq, &work->work);
+	etnaviv_queue_work(etnaviv_obj->base.dev, &work->work);
 
 	return -EAGAIN;
 }
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 9c493cfa7174..b2d59d641ed9 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -730,7 +730,8 @@ static void recover_worker(struct work_struct *work)
 	/* TODO gpu->funcs->recover(gpu); */
 	mutex_unlock(&dev->struct_mutex);
 
-	etnaviv_gpu_retire(gpu);
+	/* Retire the buffer objects in a work */
+	etnaviv_queue_work(gpu->dev, &gpu->retire_work);
 }
 
@@ -743,8 +744,6 @@ static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
 	bool progress = false;
 
@@ -768,7 +767,7 @@ static void hangcheck_handler(unsigned long data)
 		dev_err(gpu->dev, "     completed fence: %u\n", fence);
 		dev_err(gpu->dev, "     submitted fence: %u\n",
 			gpu->submitted_fence);
-		queue_work(priv->wq, &gpu->recover_work);
+		etnaviv_queue_work(gpu->dev, &gpu->recover_work);
 	}
 
 	/* if still more pending work, reset the hangcheck timer: */
@@ -867,14 +866,6 @@ static void retire_worker(struct work_struct *work)
 	wake_up_all(&gpu->fence_event);
 }
 
-/* call from irq handler to schedule work to retire bo's */
-void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
-{
-	struct etnaviv_drm_private *priv = gpu->drm->dev_private;
-
-	queue_work(priv->wq, &gpu->retire_work);
-}
-
 int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
 	uint32_t fence, struct timespec *timeout)
 {
@@ -1046,7 +1037,8 @@ static irqreturn_t irq_handler(int irq, void *data)
 			pm_runtime_put_autosuspend(gpu->dev);
 		}
 
-		etnaviv_gpu_retire(gpu);
+		/* Retire the buffer objects in a work */
+		etnaviv_queue_work(gpu->dev, &gpu->retire_work);
 
 		ret = IRQ_HANDLED;
 	}

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

The Linux kernel dislikes the use of stdint.h types.  In order to
prepare this code for merging, we need to convert to the preferred
types, which are u8/u16/u32/u64 for unsigned ints of the specified
size, and s8/s16/s32/s64 for signed ints of the same.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     | 10 ++--
 drivers/staging/etnaviv/etnaviv_drv.c        |  2 +-
 drivers/staging/etnaviv/etnaviv_drv.h        | 31 +++++-----
 drivers/staging/etnaviv/etnaviv_gem.c        | 28 ++++-----
 drivers/staging/etnaviv/etnaviv_gem.h        | 24 ++++----
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 14 ++---
 drivers/staging/etnaviv/etnaviv_gpu.c        | 21 ++++---
 drivers/staging/etnaviv/etnaviv_gpu.h        | 55 +++++++++---------
 drivers/staging/etnaviv/etnaviv_iommu.c      | 12 ++--
 drivers/staging/etnaviv/etnaviv_mmu.c        | 10 ++--
 drivers/staging/etnaviv/etnaviv_mmu.h        |  8 +--
 include/uapi/drm/etnaviv_drm.h               | 85 ++++++++++++++--------------
 12 files changed, 149 insertions(+), 151 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index b88fa49f2525..70c5c6fcc8ea 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -28,7 +28,7 @@
  */

-static inline void OUT(struct etnaviv_gem_object *buffer, uint32_t data)
+static inline void OUT(struct etnaviv_gem_object *buffer, u32 data)
 {
 	u32 *vaddr = (u32 *)buffer->vaddr;
 
@@ -183,7 +183,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	 * if we are going to completely overflow the buffer, we need to wrap.
 	 */
 	if (buffer->offset + reserve_size >
-	    buffer->base.size / sizeof(uint32_t))
+	    buffer->base.size / sizeof(u32))
 		buffer->offset = 0;
 
 	/* save offset back into main buffer */
@@ -192,7 +192,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	link_size = 6;
 
 	/* Skip over any extra instructions */
-	link_target += extra_size * sizeof(uint32_t);
+	link_target += extra_size * sizeof(u32);
 
 	/* update offset for every cmd stream */
 	for (i = submit->nr_cmds; i--; ) {
@@ -232,8 +232,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	}
 
 	if (gpu->mmu->need_flush || gpu->switch_context) {
-		uint32_t new_target = gpu_va(gpu, buffer) + buffer->offset *
-					sizeof(uint32_t);
+		u32 new_target = gpu_va(gpu, buffer) + buffer->offset *
+					sizeof(u32);
 
 		if (gpu->mmu->need_flush) {
 			/* Add the MMU flush */
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index fcddb7a6b979..d938b0377e20 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -434,7 +434,7 @@ static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
 
 	if (offset_in_page(args->user_ptr | args->user_size) ||
 	    (uintptr_t)args->user_ptr != args->user_ptr ||
-	    (uint32_t)args->user_size != args->user_size)
+	    (u32)args->user_size != args->user_size)
 		return -EINVAL;
 
 	if (args->flags & ETNA_USERPTR_WRITE)
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index ae379dd1deab..d7fc08066fe9 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -53,7 +53,7 @@ struct etnaviv_drm_private {
 	int num_gpus;
 	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
 
-	uint32_t next_fence;
+	u32 next_fence;
 
 	/* list of GEM objects: */
 	struct list_head inactive_list;
@@ -61,9 +61,10 @@ struct etnaviv_drm_private {
 	struct workqueue_struct *wq;
 };
 
-static void etnaviv_queue_work(struct drm_device *dev, struct work_struct *w)
+static inline void etnaviv_queue_work(struct drm_device *dev,
+	struct work_struct *w)
 {
-	struct etnaviv_drm_private *priv = drm->dev_private;
+	struct etnaviv_drm_private *priv = dev->dev_private;
 
 	queue_work(priv->wq, w);
 }
@@ -73,11 +74,11 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 
 int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
-int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, uint64_t *offset);
+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset);
 int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
-	struct drm_gem_object *obj, uint32_t *iova);
+	struct drm_gem_object *obj, u32 *iova);
 int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
-	int id, uint32_t *iova);
+	int id, u32 *iova);
 void etnaviv_gem_put_iova(struct drm_gem_object *obj);
 struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj);
 void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
@@ -90,18 +91,18 @@ void *etnaviv_gem_vaddr_locked(struct drm_gem_object *obj);
 void *etnaviv_gem_vaddr(struct drm_gem_object *obj);
 dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj);
 void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
-		struct etnaviv_gpu *gpu, uint32_t access, uint32_t fence);
+		struct etnaviv_gpu *gpu, u32 access, u32 fence);
 void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj);
-int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
+int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
 		struct timespec *timeout);
 int etnaviv_gem_cpu_fini(struct drm_gem_object *obj);
 void etnaviv_gem_free_object(struct drm_gem_object *obj);
 int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
-		uint32_t size, uint32_t flags, uint32_t *handle);
+		u32 size, u32 flags, u32 *handle);
 struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
-		uint32_t size, uint32_t flags);
+		u32 size, u32 flags);
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
-	uintptr_t ptr, uint32_t size, uint32_t flags, uint32_t *handle);
+	uintptr_t ptr, u32 size, u32 flags, u32 *handle);
 u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
@@ -122,14 +123,14 @@ u32 etnaviv_readl(const void __iomem *addr);
 #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
 /* returns true if fence a comes after fence b */
-static inline bool fence_after(uint32_t a, uint32_t b)
+static inline bool fence_after(u32 a, u32 b)
 {
-	return (int32_t)(a - b) > 0;
+	return (s32)(a - b) > 0;
 }
 
-static inline bool fence_after_eq(uint32_t a, uint32_t b)
+static inline bool fence_after_eq(u32 a, u32 b)
 {
-	return (int32_t)(a - b) >= 0;
+	return (s32)(a - b) >= 0;
 }
 
 #endif /* __ETNAVIV_DRV_H__ */
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 6e114a390101..23e655de7925 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -270,7 +270,7 @@ int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 }
 
 /* get mmap offset - must be called under struct_mutex */
-int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, uint64_t *offset)
+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset)
 {
 	int ret;
 
@@ -292,7 +292,7 @@ int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, uint64_t *offset)
  * the refcnt counter needs to be atomic_t.
  */
 int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
-	struct drm_gem_object *obj, uint32_t *iova)
+	struct drm_gem_object *obj, u32 *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	struct etnaviv_vram_mapping *mapping =
@@ -319,7 +319,7 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 }
 
 int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
-	int id, uint32_t *iova)
+	int id, u32 *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 	struct etnaviv_vram_mapping *mapping =
@@ -393,7 +393,7 @@ dma_addr_t etnaviv_gem_paddr_locked(struct drm_gem_object *obj)
 }
 
 void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
-	struct etnaviv_gpu *gpu, uint32_t access, uint32_t fence)
+	struct etnaviv_gpu *gpu, u32 access, u32 fence)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
 
@@ -426,7 +426,7 @@ void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj)
 	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
 }
 
-int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
+int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
 		struct timespec *timeout)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
@@ -434,7 +434,7 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 
 	if (is_active(etnaviv_obj)) {
 		struct etnaviv_gpu *gpu = etnaviv_obj->gpu;
-		uint32_t fence = 0;
+		u32 fence = 0;
 
 		if (op & ETNA_PREP_READ)
 			fence = etnaviv_obj->write_fence;
@@ -462,11 +462,11 @@ static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
 	struct drm_device *dev = obj->dev;
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-	uint64_t off = drm_vma_node_start(&obj->vma_node);
+	unsigned long off = drm_vma_node_start(&obj->vma_node);

-	seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %zd\n",
+	seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08lx %p %zd\n",
 			etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I',
 			etnaviv_obj->read_fence, etnaviv_obj->write_fence,
 			obj->name, obj->refcount.refcount.counter,
@@ -565,7 +565,7 @@ int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj)
 }
 
 static int etnaviv_gem_new_impl(struct drm_device *dev,
-		uint32_t size, uint32_t flags,
+		u32 size, u32 flags,
 		struct drm_gem_object **obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj;
@@ -623,7 +623,7 @@ static int etnaviv_gem_new_impl(struct drm_device *dev,
 }
 
 static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev,
-		uint32_t size, uint32_t flags)
+		u32 size, u32 flags)
 {
 	struct drm_gem_object *obj = NULL;
 	int ret;
@@ -657,7 +657,7 @@ static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev,
 
 /* convenience method to construct a GEM buffer object, and userspace handle */
 int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
-		uint32_t size, uint32_t flags, uint32_t *handle)
+		u32 size, u32 flags, u32 *handle)
 {
 	struct drm_gem_object *obj;
 	int ret;
@@ -681,7 +681,7 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 }
 
 struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
-		uint32_t size, uint32_t flags)
+		u32 size, u32 flags)
 {
 	struct drm_gem_object *obj;
 	int ret;
@@ -699,7 +699,7 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 	return obj;
 }
 
-int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
+int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
 	struct etnaviv_gem_object **res)
 {
 	struct drm_gem_object *obj;
@@ -885,7 +885,7 @@ static const struct etnaviv_gem_ops etnaviv_gem_userptr_ops = {
 };
 
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
-	uintptr_t ptr, uint32_t size, uint32_t flags, uint32_t *handle)
+	uintptr_t ptr, u32 size, u32 flags, u32 *handle)
 {
 	struct etnaviv_gem_object *etnaviv_obj;
 	int ret;
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 397b30f39708..08f0e6464cc7 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -33,14 +33,14 @@ struct etnaviv_vram_mapping {
 	struct list_head obj_head;
 	struct etnaviv_iommu *mmu;
 	struct drm_mm_node vram_node;
-	uint32_t iova;
+	u32 iova;
 };

-	uint32_t flags;
+	u32 flags;
 
 	/* And object is either:
 	 *  inactive - on priv->inactive_list
@@ -52,8 +52,8 @@ struct etnaviv_gem_object {
 	 */
 	struct list_head mm_list;
 	struct etnaviv_gpu *gpu;     /* non-null if active */
-	uint32_t access;
-	uint32_t read_fence, write_fence;
+	u32 access;
+	u32 read_fence, write_fence;
 
 	/* Transiently in the process of submit ioctl, objects associated
 	 * with the submit are on submit->bo_list.. this only lasts for
@@ -108,29 +108,29 @@ static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
 struct etnaviv_gem_submit {
 	struct drm_device *dev;
 	struct etnaviv_gpu *gpu;
-	uint32_t exec_state;
+	u32 exec_state;
 	struct list_head bo_list;
 	struct ww_acquire_ctx ticket;
-	uint32_t fence;
+	u32 fence;
 	unsigned int nr_cmds;
 	unsigned int nr_bos;
 	struct {
-		uint32_t type;
-		uint32_t offset; /* in dwords */
-		uint32_t size;  /* in dwords */
+		u32 type;
+		u32 offset; /* in dwords */
+		u32 size;  /* in dwords */
 		struct etnaviv_gem_object *obj;
 	} cmd[MAX_CMDS];
 	struct {
-		uint32_t flags;
+		u32 flags;
 		struct etnaviv_gem_object *obj;
-		uint32_t iova;
+		u32 iova;
 	} bos[0];
 };
 
 struct etnaviv_vram_mapping *
 etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj,
 			     struct etnaviv_iommu *mmu);
-int etnaviv_gem_new_private(struct drm_device *dev, size_t size, uint32_t flags,
+int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
 	struct etnaviv_gem_object **res);
 int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj);
 struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj);
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 647d16fee98b..e2c94f476810 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -139,7 +139,7 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)
 retry:
 	for (i = 0; i < submit->nr_bos; i++) {
 		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
-		uint32_t iova;
+		u32 iova;
 
 		if (slow_locked == i)
 			slow_locked = -1;
@@ -201,8 +201,8 @@ static int submit_validate_objects(struct etnaviv_gem_submit *submit)
 	return ret;
 }
 
-static int submit_bo(struct etnaviv_gem_submit *submit, uint32_t idx,
-		struct etnaviv_gem_object **obj, uint32_t *iova)
+static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx,
+		struct etnaviv_gem_object **obj, u32 *iova)
 {
 	if (idx >= submit->nr_bos) {
 		DRM_ERROR("invalid buffer index: %u (out of %u)\n",
@@ -220,10 +220,10 @@ static int submit_bo(struct etnaviv_gem_submit *submit, uint32_t idx,
 
 /* process the reloc's and patch up the cmdstream as needed: */
 static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_object *obj,
-		uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
+		u32 offset, u32 nr_relocs, u64 relocs)
 {
-	uint32_t i, last_offset = 0;
-	uint32_t *ptr = obj->vaddr;
+	u32 i, last_offset = 0;
+	u32 *ptr = obj->vaddr;
 	int ret;
 
 	if (offset % 4) {
@@ -236,7 +236,7 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 		struct etnaviv_gem_object *bobj;
 		void __user *userptr =
 			to_user_ptr(relocs + (i * sizeof(submit_reloc)));
-		uint32_t iova, off;
+		u32 iova, off;
 
 		ret = copy_from_user(&submit_reloc, userptr,
 				     sizeof(submit_reloc));
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index b2d59d641ed9..c2a1dd483723 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -35,8 +35,7 @@ static const struct platform_device_id gpu_ids[] = {
  * Driver functions:
  */
 
-int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
-	uint64_t *value)
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value)
 {
 	switch (param) {
 	case ETNAVIV_PARAM_GPU_MODEL:
@@ -731,7 +730,7 @@ static void recover_worker(struct work_struct *work)
 	mutex_unlock(&dev->struct_mutex);
 
 	/* Retire the buffer objects in a work */
-	etnaviv_queue_work(gpu->dev, &gpu->retire_work);
+	etnaviv_queue_work(gpu->drm, &gpu->retire_work);
 }
 
 static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
@@ -744,7 +743,7 @@ static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
 static void hangcheck_handler(unsigned long data)
 {
 	struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data;
-	uint32_t fence = gpu->retired_fence;
+	u32 fence = gpu->retired_fence;
 	bool progress = false;
 
 	if (fence != gpu->hangcheck_fence) {
@@ -753,7 +752,7 @@ static void hangcheck_handler(unsigned long data)
 	}
 
 	if (!progress) {
-		uint32_t dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+		u32 dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
 		int change = dma_addr - gpu->hangcheck_dma_addr;
 
 		if (change < 0 || change > 16) {
@@ -767,7 +766,7 @@ static void hangcheck_handler(unsigned long data)
 		dev_err(gpu->dev, "     completed fence: %u\n", fence);
 		dev_err(gpu->dev, "     submitted fence: %u\n",
 			gpu->submitted_fence);
-		etnaviv_queue_work(gpu->dev, &gpu->recover_work);
+		etnaviv_queue_work(gpu->drm, &gpu->recover_work);
 	}
 
 	/* if still more pending work, reset the hangcheck timer: */
@@ -838,7 +837,7 @@ static void retire_worker(struct work_struct *work)
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       retire_work);
 	struct drm_device *dev = gpu->drm;
-	uint32_t fence = gpu->retired_fence;
+	u32 fence = gpu->retired_fence;

@@ -867,7 +866,7 @@ static void retire_worker(struct work_struct *work)
 }
 
 int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
-	uint32_t fence, struct timespec *timeout)
+	u32 fence, struct timespec *timeout)
 {
 	int ret;
 
@@ -967,7 +966,7 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 		WARN_ON(is_active(etnaviv_obj) && (etnaviv_obj->gpu != gpu));
 
 		if (!is_active(etnaviv_obj)) {
-			uint32_t iova;
+			u32 iova;
 
 			/* ring takes a reference to the bo and iova: */
 			drm_gem_object_reference(&etnaviv_obj->base);
@@ -1038,7 +1037,7 @@ static irqreturn_t irq_handler(int irq, void *data)
 		}
 
 		/* Retire the buffer objects in a work */
-		etnaviv_queue_work(gpu->dev, &gpu->retire_work);
+		etnaviv_queue_work(gpu->drm, &gpu->retire_work);
 
 		ret = IRQ_HANDLED;
 	}
@@ -1115,7 +1114,7 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
 static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
 {
 	struct drm_device *drm = gpu->drm;
-	uint32_t clock;
+	u32 clock;
 	int ret;
 
 	ret = mutex_lock_killable(&drm->struct_mutex);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 6981d17f6252..b88340302571 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -26,60 +26,60 @@ struct etnaviv_gem_submit;
 
 struct etnaviv_chip_identity {
 	/* Chip model. */
-	uint32_t model;
+	u32 model;
 
 	/* Revision value.*/
-	uint32_t revision;
+	u32 revision;
 
 	/* Supported feature fields. */
-	uint32_t features;
+	u32 features;
 
 	/* Supported minor feature fields. */
-	uint32_t minor_features0;
+	u32 minor_features0;
 
 	/* Supported minor feature 1 fields. */
-	uint32_t minor_features1;
+	u32 minor_features1;
 
 	/* Supported minor feature 2 fields. */
-	uint32_t minor_features2;
+	u32 minor_features2;
 
 	/* Supported minor feature 3 fields. */
-	uint32_t minor_features3;
+	u32 minor_features3;
 
 	/* Number of streams supported. */
-	uint32_t stream_count;
+	u32 stream_count;
 
 	/* Total number of temporary registers per thread. */
-	uint32_t register_max;
+	u32 register_max;
 
 	/* Maximum number of threads. */
-	uint32_t thread_count;
+	u32 thread_count;
 
 	/* Number of shader cores. */
-	uint32_t shader_core_count;
+	u32 shader_core_count;
 
 	/* Size of the vertex cache. */
-	uint32_t vertex_cache_size;
+	u32 vertex_cache_size;
 
 	/* Number of entries in the vertex output buffer. */
-	uint32_t vertex_output_buffer_size;
+	u32 vertex_output_buffer_size;
 
 	/* Number of pixel pipes. */
-	uint32_t pixel_pipes;
+	u32 pixel_pipes;
 
 	/* Number of instructions. */
-	uint32_t instruction_count;
+	u32 instruction_count;
 
 	/* Number of constants. */
-	uint32_t num_constants;
+	u32 num_constants;
 
 	/* Buffer size */
-	uint32_t buffer_size;
+	u32 buffer_size;
 };
 
 struct etnaviv_event {
 	bool used;
-	uint32_t fence;
+	u32 fence;
 };
 
 struct etnaviv_gpu {
@@ -93,7 +93,7 @@ struct etnaviv_gpu {
 	struct drm_gem_object *buffer;
 
 	/* bus base address of memory  */
-	uint32_t memory_base;
+	u32 memory_base;
 
 	/* event management: */
 	struct etnaviv_event event[30];
@@ -103,11 +103,11 @@ struct etnaviv_gpu {
 	/* list of GEM active objects: */
 	struct list_head active_list;
 
-	uint32_t idle_mask;
+	u32 idle_mask;
 
 	/* Fencing support */
-	uint32_t submitted_fence;
-	uint32_t retired_fence;
+	u32 submitted_fence;
+	u32 retired_fence;
 	wait_queue_head_t fence_event;
 
 	/* worker for handling active-list retiring: */
@@ -127,8 +127,8 @@ struct etnaviv_gpu {
 #define DRM_ETNAVIV_HANGCHECK_PERIOD 500 /* in ms */
 #define DRM_ETNAVIV_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_ETNAVIV_HANGCHECK_PERIOD)
 	struct timer_list hangcheck_timer;
-	uint32_t hangcheck_fence;
-	uint32_t hangcheck_dma_addr;
+	u32 hangcheck_fence;
+	u32 hangcheck_dma_addr;
 	struct work_struct recover_work;
 };
 
@@ -142,13 +142,12 @@ static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg)
 	return etnaviv_readl(gpu->mmio + reg);
 }
 
-static inline bool fence_completed(struct etnaviv_gpu *gpu, uint32_t fence)
+static inline bool fence_completed(struct etnaviv_gpu *gpu, u32 fence)
 {
 	return fence_after_eq(gpu->retired_fence, fence);
 }
 
-int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, uint32_t param,
-	uint64_t *value);
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value);

@@ -158,7 +157,7 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
 
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
 int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
-	uint32_t fence, struct timespec *timeout);
+	u32 fence, struct timespec *timeout);
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
diff --git a/drivers/staging/etnaviv/etnaviv_iommu.c b/drivers/staging/etnaviv/etnaviv_iommu.c
index e500305534b4..bf7b38ecd502 100644
--- a/drivers/staging/etnaviv/etnaviv_iommu.c
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -26,12 +26,12 @@
 #include "state_hi.xml.h"
 
 #define PT_SIZE		SZ_2M
-#define PT_ENTRIES	(PT_SIZE / sizeof(uint32_t))
+#define PT_ENTRIES	(PT_SIZE / sizeof(u32))

 struct etnaviv_iommu_domain_pgtable {
-	uint32_t *pgtable;
+	u32 *pgtable;
 	dma_addr_t paddr;
 };
 
@@ -65,7 +65,7 @@ static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable,
 	dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr);
 }
 
-static uint32_t pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable,
+static u32 pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable,
 			   unsigned long iova)
 {
 	/* calcuate index into page table */
@@ -88,7 +88,7 @@ static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
 
 static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain)
 {
-	uint32_t *p;
+	u32 *p;
 	int ret, i;
 
 	etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
@@ -183,10 +183,10 @@ void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
 	struct iommu_domain *domain)
 {
 	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
-	uint32_t pgtable;
+	u32 pgtable;
 
 	/* set page table address in MC */
-	pgtable = (uint32_t)etnaviv_domain->pgtable.paddr;
+	pgtable = (u32)etnaviv_domain->pgtable.paddr;
 
 	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index ec01c8a560f4..754bcbc0c19a 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -25,7 +25,7 @@ static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
 	return 0;
 }
 
-int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
 		struct sg_table *sgt, unsigned len, int prot)
 {
 	struct iommu_domain *domain = iommu->domain;
@@ -64,7 +64,7 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
 	return ret;
 }
 
-int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
 		struct sg_table *sgt, unsigned len)
 {
 	struct iommu_domain *domain = iommu->domain;
@@ -91,7 +91,7 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
 }
 
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base,
+	struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
 	struct etnaviv_vram_mapping **out_mapping)
 {
 	struct etnaviv_drm_private *priv = etnaviv_obj->base.dev->dev_private;
@@ -109,7 +109,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 
 	/* v1 MMU can optimize single entry (contiguous) scatterlists */
 	if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) {
-		uint32_t iova;
+		u32 iova;
 
 		iova = sg_dma_address(sgt->sgl) - memory_base;
 		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
@@ -227,7 +227,7 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 			     struct etnaviv_vram_mapping *mapping)
 {
 	if (mapping) {
-		uint32_t offset = mapping->vram_node.start;
+		u32 offset = mapping->vram_node.start;
 
 		if (mapping->iova >= 0x80000000) {
 			etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt,
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 94dbab736fc1..1d619e91f457 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -34,7 +34,7 @@ struct etnaviv_iommu {
 
-	uint32_t last_iova;
+	u32 last_iova;
 	bool need_flush;
 };
 
@@ -42,12 +42,12 @@ struct etnaviv_gem_object;
 
 int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
 	int cnt);
-int etnaviv_iommu_map(struct etnaviv_iommu *iommu, uint32_t iova,
+int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
 	struct sg_table *sgt, unsigned len, int prot);
-int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, uint32_t iova,
+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
 	struct sg_table *sgt, unsigned len);
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj, uint32_t memory_base,
+	struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
 	struct etnaviv_vram_mapping **mapping);
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj,
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index c6ce72ae4dbe..0aca6b189e63 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -17,12 +17,11 @@
 #ifndef __ETNAVIV_DRM_H__
 #define __ETNAVIV_DRM_H__
 
-#include <stddef.h>
 #include <drm/drm.h>
 
 /* Please note that modifications to all structs defined here are
  * subject to backwards-compatibility constraints:
- *  1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit
+ *  1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
  *     user/kernel compatibility
  *  2) Keep fields aligned to their size
  *  3) Because of how drm_ioctl() works, we can add new fields at
@@ -38,8 +37,8 @@
  * same as 'struct timespec' but 32/64b ABI safe.
  */
 struct drm_etnaviv_timespec {
-	int64_t tv_sec;          /* seconds */
-	int64_t tv_nsec;         /* nanoseconds */
+	__s64 tv_sec;          /* seconds */
+	__s64 tv_nsec;         /* nanoseconds */
 };
 
 #define ETNAVIV_PARAM_GPU_MODEL                     0x01
@@ -64,9 +63,9 @@ struct drm_etnaviv_timespec {
 #define ETNA_MAX_PIPES 4
 
 struct drm_etnaviv_param {
-	uint32_t pipe;           /* in */
-	uint32_t param;          /* in, ETNAVIV_PARAM_x */
-	uint64_t value;          /* out (get_param) or in (set_param) */
+	__u32 pipe;           /* in */
+	__u32 param;          /* in, ETNAVIV_PARAM_x */
+	__u64 value;          /* out (get_param) or in (set_param) */
 };
 
 /*
@@ -83,15 +82,15 @@ struct drm_etnaviv_param {
 #define ETNA_BO_FORCE_MMU    0x00100000
 
 struct drm_etnaviv_gem_new {
-	uint64_t size;           /* in */
-	uint32_t flags;          /* in, mask of ETNA_BO_x */
-	uint32_t handle;         /* out */
+	__u64 size;           /* in */
+	__u32 flags;          /* in, mask of ETNA_BO_x */
+	__u32 handle;         /* out */
 };
 
 struct drm_etnaviv_gem_info {
-	uint32_t handle;         /* in */
-	uint32_t pad;
-	uint64_t offset;         /* out, offset to pass to mmap() */
+	__u32 handle;         /* in */
+	__u32 pad;
+	__u64 offset;         /* out, offset to pass to mmap() */
 };
 
 #define ETNA_PREP_READ        0x01
@@ -99,13 +98,13 @@ struct drm_etnaviv_gem_info {
 #define ETNA_PREP_NOSYNC      0x04
 
 struct drm_etnaviv_gem_cpu_prep {
-	uint32_t handle;         /* in */
-	uint32_t op;             /* in, mask of ETNA_PREP_x */
+	__u32 handle;         /* in */
+	__u32 op;             /* in, mask of ETNA_PREP_x */
 	struct drm_etnaviv_timespec timeout;   /* in */
 };
 
 struct drm_etnaviv_gem_cpu_fini {
-	uint32_t handle;         /* in */
+	__u32 handle;         /* in */
 };
 
 /*
@@ -119,9 +118,9 @@ struct drm_etnaviv_gem_cpu_fini {
  * otherwise EINVAL.
  */
 struct drm_etnaviv_gem_submit_reloc {
-	uint32_t submit_offset;  /* in, offset from submit_bo */
-	uint32_t reloc_idx;      /* in, index of reloc_bo buffer */
-	uint64_t reloc_offset;   /* in, offset from start of reloc_bo */
+	__u32 submit_offset;  /* in, offset from submit_bo */
+	__u32 reloc_idx;      /* in, index of reloc_bo buffer */
+	__u64 reloc_offset;   /* in, offset from start of reloc_bo */
 };
 
 /* submit-types:
@@ -132,13 +131,13 @@ struct drm_etnaviv_gem_submit_reloc {
 #define ETNA_SUBMIT_CMD_BUF             0x0001
 #define ETNA_SUBMIT_CMD_CTX_RESTORE_BUF 0x0002
 struct drm_etnaviv_gem_submit_cmd {
-	uint32_t type;           /* in, one of ETNA_SUBMIT_CMD_x */
-	uint32_t submit_idx;     /* in, index of submit_bo cmdstream buffer */
-	uint32_t submit_offset;  /* in, offset into submit_bo */
-	uint32_t size;           /* in, cmdstream size */
-	uint32_t pad;
-	uint32_t nr_relocs;      /* in, number of submit_reloc's */
-	uint64_t relocs;         /* in, ptr to array of submit_reloc's */
+	__u32 type;           /* in, one of ETNA_SUBMIT_CMD_x */
+	__u32 submit_idx;     /* in, index of submit_bo cmdstream buffer */
+	__u32 submit_offset;  /* in, offset into submit_bo */
+	__u32 size;           /* in, cmdstream size */
+	__u32 pad;
+	__u32 nr_relocs;      /* in, number of submit_reloc's */
+	__u64 relocs;         /* in, ptr to array of submit_reloc's */
 };
 
 /* Each buffer referenced elsewhere in the cmdstream submit (ie. the
@@ -155,9 +154,9 @@ struct drm_etnaviv_gem_submit_cmd {
 #define ETNA_SUBMIT_BO_READ             0x0001
 #define ETNA_SUBMIT_BO_WRITE            0x0002
 struct drm_etnaviv_gem_submit_bo {
-	uint32_t flags;          /* in, mask of ETNA_SUBMIT_BO_x */
-	uint32_t handle;         /* in, GEM handle */
-	uint64_t presumed;       /* in/out, presumed buffer address */
+	__u32 flags;          /* in, mask of ETNA_SUBMIT_BO_x */
+	__u32 handle;         /* in, GEM handle */
+	__u64 presumed;       /* in/out, presumed buffer address */
 };
 
 /* Each cmdstream submit consists of a table of buffers involved, and
@@ -168,14 +167,14 @@ struct drm_etnaviv_gem_submit_bo {
 #define ETNA_PIPE_2D      0x01
 #define ETNA_PIPE_VG      0x02
 struct drm_etnaviv_gem_submit {
-	uint32_t pipe;           /* in */
-	uint32_t exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
-	uint32_t fence;          /* out */
-	uint32_t nr_bos;         /* in, number of submit_bo's */
-	uint32_t nr_cmds;        /* in, number of submit_cmd's */
-	uint32_t pad;
-	uint64_t bos;            /* in, ptr to array of submit_bo's */
-	uint64_t cmds;           /* in, ptr to array of submit_cmd's */
+	__u32 pipe;           /* in */
+	__u32 exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
+	__u32 fence;          /* out */
+	__u32 nr_bos;         /* in, number of submit_bo's */
+	__u32 nr_cmds;        /* in, number of submit_cmd's */
+	__u32 pad;
+	__u64 bos;            /* in, ptr to array of submit_bo's */
+	__u64 cmds;           /* in, ptr to array of submit_cmd's */
 };
 
 /* The normal way to synchronize with the GPU is just to CPU_PREP on
@@ -186,18 +185,18 @@ struct drm_etnaviv_gem_submit {
  * APIs without requiring a dummy bo to synchronize on.
  */
 struct drm_etnaviv_wait_fence {
-	uint32_t pipe;           /* in */
-	uint32_t fence;          /* in */
+	__u32 pipe;           /* in */
+	__u32 fence;          /* in */
 	struct drm_etnaviv_timespec timeout;   /* in */
 };
 
 #define ETNA_USERPTR_READ	0x01
 #define ETNA_USERPTR_WRITE	0x02
 struct drm_etnaviv_gem_userptr {
-	uint64_t user_ptr;	/* in, page aligned user pointer */
-	uint64_t user_size;	/* in, page aligned user size */
-	uint32_t flags;		/* in, flags */
-	uint32_t handle;	/* out, non-zero handle */
+	__u64 user_ptr;	/* in, page aligned user pointer */
+	__u64 user_size;	/* in, page aligned user size */
+	__u32 flags;		/* in, flags */
+	__u32 handle;	/* out, non-zero handle */
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

There's no need to initialise a list_head which is only going to be
added with list_add() to an existing list.  Remove this redundant code.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_mmu.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index 754bcbc0c19a..f40d4ec5ade7 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -104,7 +104,6 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	if (!mapping)
 		return -ENOMEM;
 
-	INIT_LIST_HEAD(&mapping->obj_head);
 	mapping->mmu = mmu;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

The drm_mm scanning for free MMU blocks walks the list of inactive
objects, looking up their vram node.  Unfortunately, the code assumes
that if it has a vram node, it is registered into drm_mm.

However, there are two cases when a vram node is created - whenever a
MMU entry has to be allocated (when it is registered into the drm_mm)
and when a direct mapping is registered (which isn't.)  The direct
mapping case has vram_node.mm NULL, which causes an oops in
drm_mm_scan_add_block().

The list of objects on the scanning list was also broken - we would end
up calling drm_mm_remove_block() multiple times for the same 'free'
vram node, instead of walking the list of scanned blocks.  Fix this by
having a separate list for scanned blocks.

Fixes: ("staging: etnaviv: allow to map buffer object into multiple address spaces")
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c |  4 ++--
 drivers/staging/etnaviv/etnaviv_gem.h |  5 ++++-
 drivers/staging/etnaviv/etnaviv_mmu.c | 36 +++++++++++++++++++++--------------
 3 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 23e655de7925..256ec0775133 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -508,7 +508,7 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 	struct etnaviv_vram_mapping *mapping, *tmp;
 
 	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
-				 obj_head) {
+				 obj_node) {
 		etnaviv_iommu_unmap_gem(mapping->mmu, etnaviv_obj, mapping);
 	}
 }
@@ -722,7 +722,7 @@ etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj,
 {
 	struct etnaviv_vram_mapping *mapping;
 
-	list_for_each_entry(mapping, &obj->vram_list, obj_head) {
+	list_for_each_entry(mapping, &obj->vram_list, obj_node) {
 		if (mapping->mmu == mmu)
 			return mapping;
 	}
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 08f0e6464cc7..f3136d86d73e 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -21,6 +21,7 @@
 #include "etnaviv_drv.h"
 
 struct etnaviv_gem_ops;
 
 struct etnaviv_gem_userptr {
 	uintptr_t ptr;
@@ -30,7 +31,9 @@ struct etnaviv_gem_userptr {
 };
 
 struct etnaviv_vram_mapping {
-	struct list_head obj_head;
+	struct list_head obj_node;
+	struct list_head scan_node;
+	struct etnaviv_gem_object *object;
 	struct etnaviv_iommu *mmu;
 	struct drm_mm_node vram_node;
 	u32 iova;
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index f40d4ec5ade7..b327d37f5111 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -104,6 +104,8 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	if (!mapping)
 		return -ENOMEM;
 
+	INIT_LIST_HEAD(&mapping->scan_node);
+	mapping->object = etnaviv_obj;
 	mapping->mmu = mmu;
 
 	/* v1 MMU can optimize single entry (contiguous) scatterlists */
@@ -113,7 +115,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		iova = sg_dma_address(sgt->sgl) - memory_base;
 		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
 			mapping->iova = iova;
-			list_add_tail(&mapping->obj_head,
+			list_add_tail(&mapping->obj_node,
 				      &etnaviv_obj->vram_list);
 			if (out_mapping)
 				*out_mapping = mapping;
@@ -123,7 +125,8 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 
 	node = &mapping->vram_node;
 	while (1) {
-		struct etnaviv_gem_object *o, *n;
+		struct etnaviv_gem_object *o;
+		struct etnaviv_vram_mapping *m, *n;
 		struct list_head list;
 		bool found;
 
@@ -155,13 +158,19 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 				continue;
 
 			/*
+			 * If this vram node has not been used, skip this.
+			 */
+			if (!free->vram_node.mm)
+				continue;
+
+			/*
 			 * If it's on the submit list, then it is part of
 			 * a submission, and we want to keep its entry.
 			 */
 			if (!list_empty(&o->submit_entry))
 				continue;
 
-			list_add(&o->submit_entry, &list);
+			list_add(&free->scan_node, &list);
 			if (drm_mm_scan_add_block(&free->vram_node)) {
 				found = true;
 				break;
@@ -170,8 +179,8 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 
 		if (!found) {
 			/* Nothing found, clean up and fail */
-			list_for_each_entry_safe(o, n, &list, submit_entry)
-				BUG_ON(drm_mm_scan_remove_block(&free->vram_node));
+			list_for_each_entry_safe(m, n, &list, scan_node)
+				BUG_ON(drm_mm_scan_remove_block(&m->vram_node));
 			break;
 		}
 
@@ -181,13 +190,13 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		 * If drm_mm_scan_remove_block() returns false, we
 		 * can leave the block pinned.
 		 */
-		list_for_each_entry_safe(o, n, &list, submit_entry)
-			if (!drm_mm_scan_remove_block(&free->vram_node))
-				list_del_init(&o->submit_entry);
+		list_for_each_entry_safe(m, n, &list, scan_node)
+			if (!drm_mm_scan_remove_block(&m->vram_node))
+				list_del_init(&m->scan_node);
 
-		list_for_each_entry_safe(o, n, &list, submit_entry) {
-			list_del_init(&o->submit_entry);
-			etnaviv_iommu_unmap_gem(mmu, o, free);
+		list_for_each_entry_safe(m, n, &list, scan_node) {
+			list_del_init(&m->scan_node);
+			etnaviv_iommu_unmap_gem(mmu, m->object, m);
 		}
 
 		/*
@@ -214,7 +223,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 		return ret;
 	}
 
-	list_add_tail(&mapping->obj_head, &etnaviv_obj->vram_list);
+	list_add_tail(&mapping->obj_node, &etnaviv_obj->vram_list);
 	if (out_mapping)
 		*out_mapping = mapping;
 
@@ -233,9 +242,8 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 					    etnaviv_obj->base.size);
 			drm_mm_remove_node(&mapping->vram_node);
 		}
-		list_del(&mapping->obj_head);
+		list_del(&mapping->obj_node);
 		kfree(mapping);
-		mapping = NULL;
 	}
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

The mapping structure contains everything we need; avoid passing extra
data in rather than using the data we have stored in the structure.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c |  2 +-
 drivers/staging/etnaviv/etnaviv_mmu.c | 31 ++++++++++++++++++-------------
 drivers/staging/etnaviv/etnaviv_mmu.h |  4 +---
 3 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 256ec0775133..3a68295eaee4 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -509,7 +509,7 @@ static void etnaviv_free_obj(struct drm_gem_object *obj)
 
 	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
 				 obj_node) {
-		etnaviv_iommu_unmap_gem(mapping->mmu, etnaviv_obj, mapping);
+		etnaviv_iommu_unmap_gem(mapping);
 	}
 }
 
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.c b/drivers/staging/etnaviv/etnaviv_mmu.c
index b327d37f5111..ca317f633970 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.c
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -196,7 +196,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 
 		list_for_each_entry_safe(m, n, &list, scan_node) {
 			list_del_init(&m->scan_node);
-			etnaviv_iommu_unmap_gem(mmu, m->object, m);
+			etnaviv_iommu_unmap_gem(m);
 		}
 
 		/*
@@ -230,21 +230,26 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	return ret;
 }
 
-void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
-			     struct etnaviv_gem_object *etnaviv_obj,
-			     struct etnaviv_vram_mapping *mapping)
+void etnaviv_iommu_unmap_gem(struct etnaviv_vram_mapping *mapping)
 {
-	if (mapping) {
-		u32 offset = mapping->vram_node.start;
+	struct etnaviv_iommu *mmu;
+	struct etnaviv_gem_object *etnaviv_obj;
 
-		if (mapping->iova >= 0x80000000) {
-			etnaviv_iommu_unmap(mmu, offset, etnaviv_obj->sgt,
-					    etnaviv_obj->base.size);
-			drm_mm_remove_node(&mapping->vram_node);
-		}
-		list_del(&mapping->obj_node);
-		kfree(mapping);
+	if (!mapping)
+		return;
+
+	mmu = mapping->mmu;
+
+	/* If the vram node is on the mm, unmap and remove the node */
+	if (mapping->vram_node.mm == &mmu->mm) {
+		etnaviv_obj = mapping->object;
+		etnaviv_iommu_unmap(mmu, mapping->vram_node.start,
+				    etnaviv_obj->sgt, etnaviv_obj->base.size);
+		drm_mm_remove_node(&mapping->vram_node);
 	}
+
+	list_del(&mapping->obj_node);
+	kfree(mapping);
 }
 
 void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 1d619e91f457..444ef296d2b4 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -49,9 +49,7 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
 	struct etnaviv_vram_mapping **mapping);
-void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
-	struct etnaviv_gem_object *etnaviv_obj,
-	struct etnaviv_vram_mapping *mapping);
+void etnaviv_iommu_unmap_gem(struct etnaviv_vram_mapping *mapping);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
 
 struct etnaviv_iommu *etnaviv_iommu_new(struct device *dev,

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 3a68295eaee4..02c5e46dca75 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -295,8 +295,7 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	struct drm_gem_object *obj, u32 *iova)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-	struct etnaviv_vram_mapping *mapping =
-			etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu);
+	struct etnaviv_vram_mapping *mapping;
 	int ret = 0;
 
 	if (etnaviv_obj->flags & ETNA_BO_CMDSTREAM) {
@@ -304,6 +303,7 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 		return 0;
 	}
 
+	mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu);
 	if (!mapping) {
 		struct page **pages = etnaviv_gem_get_pages(etnaviv_obj);
 		if (IS_ERR(pages))

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

On GPU submission, copy the submit command and bo structures in one go
and outside of dev->struct_mutex.  There are two reasons:

- This avoids the overhead from calling and checking copy_from_user()
  multiple times.
- Holding dev->struct_mutex over a page fault should be avoided as this
  will block all users of the GPU while page IO is being performed.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 98 ++++++++++++++++------------
 1 file changed, 57 insertions(+), 41 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index e2c94f476810..f94b6e8fb4fb 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -55,41 +55,34 @@ static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
 }
 
 static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
-		struct drm_etnaviv_gem_submit *args, struct drm_file *file)
+	struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos,
+	unsigned nr_bos)
 {
+	struct drm_etnaviv_gem_submit_bo *bo;
 	unsigned i;
 	int ret = 0;
 
 	spin_lock(&file->table_lock);
 
-	for (i = 0; i < args->nr_bos; i++) {
-		struct drm_etnaviv_gem_submit_bo submit_bo;
+	for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) {
 		struct drm_gem_object *obj;
 		struct etnaviv_gem_object *etnaviv_obj;
-		void __user *userptr =
-			to_user_ptr(args->bos + (i * sizeof(submit_bo)));
-
-		ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
-		if (ret) {
-			ret = -EFAULT;
-			goto out_unlock;
-		}
 
-		if (submit_bo.flags & BO_INVALID_FLAGS) {
-			DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
+		if (bo->flags & BO_INVALID_FLAGS) {
+			DRM_ERROR("invalid flags: %x\n", bo->flags);
 			ret = -EINVAL;
 			goto out_unlock;
 		}
 
-		submit->bos[i].flags = submit_bo.flags;
+		submit->bos[i].flags = bo->flags;
 
 		/* normally use drm_gem_object_lookup(), but for bulk lookup
 		 * all under single table_lock just hit object_idr directly:
 		 */
-		obj = idr_find(&file->object_idr, submit_bo.handle);
+		obj = idr_find(&file->object_idr, bo->handle);
 		if (!obj) {
 			DRM_ERROR("invalid handle %u at index %u\n",
-				  submit_bo.handle, i);
+				  bo->handle, i);
 			ret = -EINVAL;
 			goto out_unlock;
 		}
@@ -98,7 +91,7 @@ static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
 
 		if (!list_empty(&etnaviv_obj->submit_entry)) {
 			DRM_ERROR("handle %u at index %u already on submit list\n",
-				  submit_bo.handle, i);
+				  bo->handle, i);
 			ret = -EINVAL;
 			goto out_unlock;
 		}
@@ -298,6 +291,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	struct drm_etnaviv_gem_submit *args = data;
 	struct etnaviv_file_private *ctx = file->driver_priv;
+	struct drm_etnaviv_gem_submit_cmd *cmds;
+	struct drm_etnaviv_gem_submit_bo *bos;
 	struct etnaviv_gem_submit *submit;
 	struct etnaviv_gpu *gpu;
 	unsigned i;
@@ -314,6 +309,29 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		return -EINVAL;
 
 	/*
+	 * Copy the command submission and bo array to kernel space in
+	 * one go, and do this outside of the dev->struct_mutex lock.
+	 */
+	cmds = drm_malloc_ab(args->nr_cmds, sizeof(*cmds));
+	bos = drm_malloc_ab(args->nr_bos, sizeof(*bos));
+	if (!cmds || !bos)
+		return -ENOMEM;
+
+	ret = copy_from_user(cmds, to_user_ptr(args->cmds),
+			     args->nr_cmds * sizeof(*cmds));
+	if (ret) {
+		ret = -EFAULT;
+		goto err_submit_cmds;
+	}
+
+	ret = copy_from_user(bos, to_user_ptr(args->bos),
+			     args->nr_bos * sizeof(*bos));
+	if (ret) {
+		ret = -EFAULT;
+		goto err_submit_cmds;
+	}
+
+	/*
 	 * Avoid big circular locking dependency loops:
 	 * - reading debugfs results in mmap_sem depending on i_mutex_key#3
 	 *   (iterate_dir -> filldir64)
@@ -332,7 +350,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	 */
 	ret = etnaviv_gpu_pm_get_sync(gpu);
 	if (ret < 0)
-		return ret;
+		goto err_submit_cmds;

@@ -343,7 +361,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	}
 	submit->exec_state = args->exec_state;
 
-	ret = submit_lookup_objects(submit, args, file);
+	ret = submit_lookup_objects(submit, file, bos, args->nr_bos);
 	if (ret)
 		goto out;
 
@@ -352,19 +370,11 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		goto out;
 
 	for (i = 0; i < args->nr_cmds; i++) {
-		struct drm_etnaviv_gem_submit_cmd submit_cmd;
-		void __user *userptr =
-			to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
+		struct drm_etnaviv_gem_submit_cmd *submit_cmd = cmds + i;
 		struct etnaviv_gem_object *etnaviv_obj;
 		unsigned max_size;
 
-		ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
-		if (ret) {
-			ret = -EFAULT;
-			goto out;
-		}
-
-		ret = submit_bo(submit, submit_cmd.submit_idx, &etnaviv_obj,
+		ret = submit_bo(submit, submit_cmd->submit_idx, &etnaviv_obj,
 				NULL);
 		if (ret)
 			goto out;
@@ -375,16 +385,16 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 			goto out;
 		}
 
-		if (submit_cmd.size % 4) {
+		if (submit_cmd->size % 4) {
 			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
-					submit_cmd.size);
+					submit_cmd->size);
 			ret = -EINVAL;
 			goto out;
 		}
 
-		if (submit_cmd.submit_offset % 8) {
+		if (submit_cmd->submit_offset % 8) {
 			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
-					submit_cmd.size);
+					submit_cmd->size);
 			ret = -EINVAL;
 			goto out;
 		}
@@ -395,17 +405,17 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		 */
 		max_size = etnaviv_obj->base.size - 8;
 
-		if (submit_cmd.size > max_size ||
-		    submit_cmd.submit_offset > max_size - submit_cmd.size) {
+		if (submit_cmd->size > max_size ||
+		    submit_cmd->submit_offset > max_size - submit_cmd->size) {
 			DRM_ERROR("invalid cmdstream size: %u\n",
-				  submit_cmd.size);
+				  submit_cmd->size);
 			ret = -EINVAL;
 			goto out;
 		}
 
-		submit->cmd[i].type = submit_cmd.type;
-		submit->cmd[i].offset = submit_cmd.submit_offset / 4;
-		submit->cmd[i].size = submit_cmd.size / 4;
+		submit->cmd[i].type = submit_cmd->type;
+		submit->cmd[i].offset = submit_cmd->submit_offset / 4;
+		submit->cmd[i].size = submit_cmd->size / 4;
 		submit->cmd[i].obj = etnaviv_obj;
 
 		if (!etnaviv_cmd_validate_one(gpu, etnaviv_obj,
@@ -416,8 +426,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		}
 
 		ret = submit_reloc(submit, etnaviv_obj,
-				   submit_cmd.submit_offset,
-				   submit_cmd.nr_relocs, submit_cmd.relocs);
+				   submit_cmd->submit_offset,
+				   submit_cmd->nr_relocs, submit_cmd->relocs);
 		if (ret)
 			goto out;
 	}
@@ -443,5 +453,11 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (ret == -EAGAIN)
 		flush_workqueue(priv->wq);
 
+ err_submit_cmds:
+	if (bos)
+		drm_free_large(bos);
+	if (cmds)
+		drm_free_large(cmds);
+
 	return ret;
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

We've already validated the command buffer offset, there's no need to
repeat it elsewhere.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index f94b6e8fb4fb..a0bc61946036 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -219,11 +219,6 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 	u32 *ptr = obj->vaddr;
 	int ret;
 
-	if (offset % 4) {
-		DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
-		return -EINVAL;
-	}
-
 	for (i = 0; i < nr_relocs; i++) {
 		struct drm_etnaviv_gem_submit_reloc submit_reloc;
 		struct etnaviv_gem_object *bobj;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Move the mapping teardown directly into etnaviv_gem_free_object().

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 02c5e46dca75..1c003afc9ab1 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -502,17 +502,6 @@ static const struct etnaviv_gem_ops etnaviv_gem_cmd_ops = {
 	.release = etnaviv_gem_cmd_release,
 };
 
-static void etnaviv_free_obj(struct drm_gem_object *obj)
-{
-	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-	struct etnaviv_vram_mapping *mapping, *tmp;
-
-	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
-				 obj_node) {
-		etnaviv_iommu_unmap_gem(mapping);
-	}
-}
-
 static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	if (etnaviv_obj->vaddr)
@@ -529,6 +518,7 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct etnaviv_vram_mapping *mapping, *tmp;

@@ -537,8 +527,9 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)

-	if (!(etnaviv_obj->flags & ETNA_BO_CMDSTREAM))
-		etnaviv_free_obj(obj);
+	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
+				 obj_node)
+		etnaviv_iommu_unmap_gem(mapping);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:15 +0100

Add support for the new GEM_WAIT ioctl, which waits for the kernel to
finish with the GEM object (all render complete, and has been retired).
This is different from the WAIT_FENCE ioctl, which just waits for
rendering to complete.

This is an important distinction, as when a gem object is retired and
freed, the kernel will invalidate the cache for this object, which
effectively changes the data userspace sees.  If userspace merely
waits for rendering to complete, there is a race condition where a
userptr BO can be free()d and re-used, and the kernel's invalidation
then corrupts the malloc() free lists.

GEM_WAIT is not fully race free: if the BO is re-submitted via a
different thread, we can't report this (indeed, this could happen
after the call has returned.)  It is up to userspace to ensure that
such behaviour does not occur.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 29 +++++++++++++
 drivers/staging/etnaviv/etnaviv_gem.c |  8 ++++
 drivers/staging/etnaviv/etnaviv_gem.h |  2 +
 drivers/staging/etnaviv/etnaviv_gpu.c | 76 +++++++++++++++++++++++++++--------
 drivers/staging/etnaviv/etnaviv_gpu.h |  8 ++++
 include/uapi/drm/etnaviv_drm.h        | 10 ++++-
 6 files changed, 116 insertions(+), 17 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index d938b0377e20..8af536f59a58 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -19,6 +19,7 @@
 
 #include "etnaviv_drv.h"
 #include "etnaviv_gpu.h"
 
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
@@ -451,6 +452,33 @@ static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
 				       &args->handle);
 }
 
+static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct drm_etnaviv_gem_wait *args = data;
+	struct drm_gem_object *obj;
+	struct etnaviv_gpu *gpu;
+	int ret;
+
+	if (args->pipe >= ETNA_MAX_PIPES)
+		return -EINVAL;
+
+	gpu = priv->gpu[args->pipe];
+	if (!gpu)
+		return -ENXIO;
+
+	obj = drm_gem_object_lookup(dev, file, args->handle);
+	if (!obj)
+		return -ENOENT;
+
+	ret = etnaviv_gem_wait_bo(gpu, obj, &TS(args->timeout));
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
 static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 #define ETNA_IOCTL(n, func, flags) \
 	DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
@@ -462,6 +490,7 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 	ETNA_IOCTL(GEM_SUBMIT,   gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_USERPTR,  gem_userptr,  DRM_UNLOCKED|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(GEM_WAIT,     gem_wait,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
 static const struct vm_operations_struct vm_ops = {
diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 1c003afc9ab1..2518f897fdd8 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -457,6 +457,14 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj)
 	return 0;
 }
 
+int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+	struct timespec *timeout)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	return etnaviv_gpu_wait_obj_inactive(gpu, etnaviv_obj, timeout);
+}
+
 #ifdef CONFIG_DEBUG_FS
 static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 {
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index f3136d86d73e..4d5455a3fe39 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -130,6 +130,8 @@ struct etnaviv_gem_submit {
 	} bos[0];
 };
 
+int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+	struct timespec *timeout);
 struct etnaviv_vram_mapping *
 etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj,
 			     struct etnaviv_iommu *mmu);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index c2a1dd483723..96b55f05d08b 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -743,7 +743,7 @@ static void hangcheck_timer_reset(struct etnaviv_gpu *gpu)
 static void hangcheck_handler(unsigned long data)
 {
 	struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data;
-	u32 fence = gpu->retired_fence;
+	u32 fence = gpu->completed_fence;
 	bool progress = false;
 
 	if (fence != gpu->hangcheck_fence) {
@@ -837,7 +837,7 @@ static void retire_worker(struct work_struct *work)
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       retire_work);
 	struct drm_device *dev = gpu->drm;
-	u32 fence = gpu->retired_fence;
+	u32 fence = gpu->completed_fence;

@@ -860,11 +860,27 @@ static void retire_worker(struct work_struct *work)
 		}
 	}
 
+	gpu->retired_fence = fence;
+
 	mutex_unlock(&dev->struct_mutex);

+static unsigned long etnaviv_timeout_to_jiffies(struct timespec *timeout)
+{
+	unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
+	unsigned long start_jiffies = jiffies;
+	unsigned long remaining_jiffies;
+
+	if (time_after(start_jiffies, timeout_jiffies))
+		remaining_jiffies = 0;
+	else
+		remaining_jiffies = timeout_jiffies - start_jiffies;
+
+	return remaining_jiffies;
+}
+
 int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
 	u32 fence, struct timespec *timeout)
 {
@@ -880,21 +896,15 @@ int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
 		/* No timeout was requested: just test for completion */
 		ret = fence_completed(gpu, fence) ? 0 : -EBUSY;
 	} else {
-		unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
-		unsigned long start_jiffies = jiffies;
-		unsigned long remaining_jiffies;
-
-		if (time_after(start_jiffies, timeout_jiffies))
-			remaining_jiffies = 0;
-		else
-			remaining_jiffies = timeout_jiffies - start_jiffies;
+		unsigned long remaining = etnaviv_timeout_to_jiffies(timeout);
 
 		ret = wait_event_interruptible_timeout(gpu->fence_event,
 						fence_completed(gpu, fence),
-						remaining_jiffies);
+						remaining);
 		if (ret == 0) {
-			DBG("timeout waiting for fence: %u (completed: %u)",
-					fence, gpu->retired_fence);
+			DBG("timeout waiting for fence: %u (retired: %u completed: %u)",
+				fence, gpu->retired_fence,
+				gpu->completed_fence);
 			ret = -ETIMEDOUT;
 		} else if (ret != -ERESTARTSYS) {
 			ret = 0;
@@ -904,6 +914,39 @@ int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
 	return ret;
 }
 
+/*
+ * Wait for an object to become inactive.  This, on it's own, is not race
+ * free: the object is moved by the retire worker off the active list, and
+ * then the iova is put.  Moreover, the object could be re-submitted just
+ * after we notice that it's become inactive.
+ *
+ * Although the retirement happens under the struct_mutex, we don't want
+ * to hold that lock in this function.  Instead, the caller is responsible
+ * for ensuring that the retire worker has finished (which will happen, eg,
+ * when we unreference the object, an action which takes the struct_mutex.)
+ */
+int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout)
+{
+	unsigned long remaining;
+	long ret;
+
+	if (!timeout)
+		return !is_active(etnaviv_obj) ? 0 : -EBUSY;
+
+	remaining = etnaviv_timeout_to_jiffies(timeout);
+
+	ret = wait_event_interruptible_timeout(gpu->fence_event,
+					       !is_active(etnaviv_obj),
+					       remaining);
+	if (ret > 0)
+		return 0;
+	else if (ret == -ERESTARTSYS)
+		return -ERESTARTSYS;
+	else
+		return -ETIMEDOUT;
+}
+
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu)
 {
 	return pm_runtime_get_sync(gpu->dev);
@@ -1022,8 +1065,9 @@ static irqreturn_t irq_handler(int irq, void *data)
 			 * - event 1 and event 0 complete
 			 * we can end up processing event 0 first, then 1.
 			 */
-			if (fence_after(gpu->event[event].fence, gpu->retired_fence))
-				gpu->retired_fence = gpu->event[event].fence;
+			if (fence_after(gpu->event[event].fence,
+					gpu->completed_fence))
+				gpu->completed_fence = gpu->event[event].fence;
 			event_free(gpu, event);
 
 			/*
@@ -1305,7 +1349,7 @@ static int etnaviv_gpu_rpm_suspend(struct device *dev)
 	u32 idle, mask;
 
 	/* If we have outstanding fences, we're not idle */
-	if (gpu->retired_fence != gpu->submitted_fence)
+	if (gpu->completed_fence != gpu->submitted_fence)
 		return -EBUSY;
 
 	/* Check whether the hardware (except FE) is idle */
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index b88340302571..374f37f3665f 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -107,6 +107,7 @@ struct etnaviv_gpu {
 
 	/* Fencing support */
 	u32 submitted_fence;
+	u32 completed_fence;
 	u32 retired_fence;
 
@@ -144,6 +145,11 @@ static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg)
 
 static inline bool fence_completed(struct etnaviv_gpu *gpu, u32 fence)
 {
+	return fence_after_eq(gpu->completed_fence, fence);
+}
+
+static inline bool fence_retired(struct etnaviv_gpu *gpu, u32 fence)
+{
 	return fence_after_eq(gpu->retired_fence, fence);
 }
 
@@ -158,6 +164,8 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
 void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
 int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu,
 	u32 fence, struct timespec *timeout);
+int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu,
+	struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout);
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 0aca6b189e63..7cc749c30970 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -199,6 +199,12 @@ struct drm_etnaviv_gem_userptr {
 	__u32 handle;	/* out, non-zero handle */
 };
 
+struct drm_etnaviv_gem_wait {
+	__u32 pipe;				/* in */
+	__u32 handle;				/* in, bo to be waited for */
+	struct drm_etnaviv_timespec timeout;	/* in */
+};
+
 #define DRM_ETNAVIV_GET_PARAM          0x00
 /* placeholder:
 #define DRM_ETNAVIV_SET_PARAM          0x01
@@ -210,7 +216,8 @@ struct drm_etnaviv_gem_userptr {
 #define DRM_ETNAVIV_GEM_SUBMIT         0x06
 #define DRM_ETNAVIV_WAIT_FENCE         0x07
 #define DRM_ETNAVIV_GEM_USERPTR        0x08
-#define DRM_ETNAVIV_NUM_IOCTLS         0x09
+#define DRM_ETNAVIV_GEM_WAIT           0x09
+#define DRM_ETNAVIV_NUM_IOCTLS         0x0a
 
 #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)
@@ -220,5 +227,6 @@ struct drm_etnaviv_gem_userptr {
 #define DRM_IOCTL_ETNAVIV_GEM_SUBMIT   DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
 #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)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

The CMA memory area is shared between CMA and other users.  In order to
allow CMA to work, non-CMA allocations in this area must be for movable
pages, so that a CMA allocation can reclaim its memory from other users
when required.

Page cache backed allocations, such as pages allocated via shmem, are by
default marked as "movable" allocations, which means that they can come
from the CMA region.

However, in etnaviv's case, we allocate shmem pages, and then we pin
these pages for the life of the buffer.  This prevents the pages being
reclaimed, and causes CMA failures.

new_inode() in fs/inode.c even says:

 * Allocates a new inode for given superblock. The default gfp_mask
 * for allocations related to inode->i_mapping is GFP_HIGHUSER_MOVABLE.
 * If HIGHMEM pages are unsuitable or it is known that pages allocated
 * for the page cache are not reclaimable or migratable,
 * mapping_set_gfp_mask() must be called with suitable flags on the
 * newly created inode's mapping

Etnaviv shmem pages are not reclaimable nor migratable once they're
pinned, so etnaviv is not conforming with this requirement.  Change the
gfp_mask to GFP_HIGHUSER, so we can still allocate from highmem, but not
from the CMA area.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 2518f897fdd8..5c2462d6130d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -640,6 +640,19 @@ static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev,
 	} else {
 		to_etnaviv_bo(obj)->ops = &etnaviv_gem_shmem_ops;
 		ret = drm_gem_object_init(dev, obj, size);
+		if (ret == 0) {
+			struct address_space *mapping;
+
+			/*
+			 * Our buffers are kept pinned, so allocating them
+			 * from the MOVABLE zone is a really bad idea, and
+			 * conflicts with CMA.  See coments above new_inode()
+			 * why this is required _and_ expected if you're
+			 * going to pin these pages.
+			 */
+			mapping = file_inode(obj->filp)->i_mapping;
+			mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
+		}
 	}
 
 	if (ret)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 5c2462d6130d..3e08f0694021 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -752,7 +752,7 @@ struct get_pages_work {
 static struct page **etnaviv_gem_userptr_do_get_pages(
 	struct etnaviv_gem_object *etnaviv_obj, struct mm_struct *mm, struct task_struct *task)
 {
-	int ret, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT;
+	int ret = 0, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT;
 	struct page **pvec;
 	uintptr_t ptr;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Signed-off-by: Christian Gmeiner <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 96b55f05d08b..f4bbfb673ea8 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -1155,6 +1155,7 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
 	return etnaviv_gpu_clk_disable(gpu);
 }
 
+#ifdef CONFIG_PM
 static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
 {
 	struct drm_device *drm = gpu->drm;
@@ -1177,6 +1178,7 @@ static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
 
 	return 0;
 }
+#endif

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

This is very useful for debugging issues regarding command
buffer processing.

Signed-off-by: Christian Gmeiner <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 39 +++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

index 8af536f59a58..80206c8b4bc3 100644
 
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
 static bool reglog;
@@ -234,6 +235,43 @@ static int etnaviv_mmu_show(struct drm_device *dev, struct seq_file *m)
 	return 0;
 }
 
+static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m)
+{
+	struct etnaviv_gem_object *obj = to_etnaviv_bo(gpu->buffer);
+	u32 size = obj->base.size;
+	u32 *ptr = obj->vaddr;
+	u32 i;
+
+	seq_printf(m, "virt %p - phys 0x%llx - free 0x%08x\n",
+			obj->vaddr, (u64)obj->paddr, size - (obj->offset * 4));
+
+	for (i = 0; i < size / 4; i++) {
+		if (i && !(i % 4))
+			seq_puts(m, "\n");
+		if (i % 4 == 0)
+			seq_printf(m, "\t0x%p: ", ptr + i);
+		seq_printf(m, "%08x ", *(ptr + i));
+	}
+	seq_puts(m, "\n");
+}
+
+static int etnaviv_ring_show(struct drm_device *dev, struct seq_file *m)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct etnaviv_gpu *gpu;
+	unsigned int i;
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		gpu = priv->gpu[i];
+		if (gpu) {
+			seq_printf(m, "Ring Buffer (%s): ",
+				   dev_name(gpu->dev));
+			etnaviv_buffer_dump(gpu, m);
+		}
+	}
+	return 0;
+}
+
 static int show_locked(struct seq_file *m, void *arg)
 {
 	struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -282,6 +320,7 @@ static struct drm_info_list etnaviv_debugfs_list[] = {
 		{"gem", show_locked, 0, etnaviv_gem_show},
 		{ "mm", show_locked, 0, etnaviv_mm_show },
 		{"mmu", show_locked, 0, etnaviv_mmu_show},
+		{"ring", show_locked, 0, etnaviv_ring_show},
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

etnaviv_buffer_init() creates a very simple command buffer
to be able to start the FE. FE fetches 'prefetch' number of 64 bit
words via DMA and starts to execute the read buffer.

This is a very simple code cleanup and pushes the whole buffer
logic (alignment, cmd buffer commands, etc.) into etnaviv_buffer.c

Signed-off-by: Christian Gmeiner <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 4 ++--
 drivers/staging/etnaviv/etnaviv_drv.h    | 2 +-
 drivers/staging/etnaviv/etnaviv_gpu.c    | 9 +++------
 3 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 70c5c6fcc8ea..225206640eb9 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -124,7 +124,7 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
 			ptr, len * 4, 0);
 }
 
-u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
+u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 {
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
 
@@ -135,7 +135,7 @@ u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 	CMD_WAIT(buffer);
 	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
 
-	return buffer->offset;
+	return buffer->offset / 2;
 }
 
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index d7fc08066fe9..9cf59646fad9 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -103,7 +103,7 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 		u32 size, u32 flags);
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 	uintptr_t ptr, u32 size, u32 flags, u32 *handle);
-u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
+u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index f4bbfb673ea8..9291d7ad4bbf 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -379,7 +379,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 
 static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 {
-	u32 words; /* 32 bit words */
+	u16 prefetch;
 
 	if (gpu->identity.model == chipModel_GC320 &&
 	    gpu_read(gpu, VIVS_HI_CHIP_TIME) != 0x2062400 &&
@@ -426,17 +426,14 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 	etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain);
 
+	prefetch = etnaviv_buffer_init(gpu);
 
 	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
 	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
 		  etnaviv_gem_paddr_locked(gpu->buffer) - gpu->memory_base);
 	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
 		  VIVS_FE_COMMAND_CONTROL_ENABLE |
-		  VIVS_FE_COMMAND_CONTROL_PREFETCH(words));
+		  VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

There is no point in having a context restore buffer, as the need for tracking
GPU hardware state in userspace and the fact that we need to submit all states
that reference memory buffers anyway to ensure proper patching of the
relocations, eat away from the potential benefit.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.h        | 1 -
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 1 -
 include/uapi/drm/etnaviv_drm.h               | 8 --------
 3 files changed, 10 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 4d5455a3fe39..23e093425bb2 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -118,7 +118,6 @@ struct etnaviv_gem_submit {
 	unsigned int nr_cmds;
 	unsigned int nr_bos;
 	struct {
-		u32 type;
 		u32 offset; /* in dwords */
 		u32 size;  /* in dwords */
 		struct etnaviv_gem_object *obj;
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index a0bc61946036..73c1378e96a1 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -408,7 +408,6 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 			goto out;
 		}
 
-		submit->cmd[i].type = submit_cmd->type;
 		submit->cmd[i].offset = submit_cmd->submit_offset / 4;
 		submit->cmd[i].size = submit_cmd->size / 4;
 		submit->cmd[i].obj = etnaviv_obj;
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 7cc749c30970..1515fa4f6986 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -123,15 +123,7 @@ struct drm_etnaviv_gem_submit_reloc {
 	__u64 reloc_offset;   /* in, offset from start of reloc_bo */
 };
 
-/* submit-types:
- *   BUF - this cmd buffer is executed normally.
- *   CTX_RESTORE_BUF - only executed if there has been a GPU context
- *      switch since the last SUBMIT ioctl
- */
-#define ETNA_SUBMIT_CMD_BUF             0x0001
-#define ETNA_SUBMIT_CMD_CTX_RESTORE_BUF 0x0002
 struct drm_etnaviv_gem_submit_cmd {
-	__u32 type;           /* in, one of ETNA_SUBMIT_CMD_x */
 	__u32 submit_idx;     /* in, index of submit_bo cmdstream buffer */
 	__u32 submit_offset;  /* in, offset into submit_bo */
 	__u32 size;           /* in, cmdstream size */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

This rewrites the submit interface to copy the command stream from user
memory. This mitigates a potential attack vector of the old interface
where userspace could submit a command buffer that would be validated by
the kernel, but is still mapped into userspace. This could be exploited
by changing the command stream after validation but before the actual
GPU execution.

A nice side effect is that validation and reloc patching can now operate
on cached memory and only the final result is copied to a writecombined
command buffer which should make those operations a bit more efficient.

A simplification to the interface is the removal of the ability to push
multiple command buffers per submit. As we don't use it for context
restore buffers and the fact that userspace doesn't need to work with
a fixed command buffer size anymore, with the potential risk to overflow
its size in the middle of an atomic stream section, there is no need
for this complication anymore.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     |  48 +++++------
 drivers/staging/etnaviv/etnaviv_cmd_parser.c |   6 +-
 drivers/staging/etnaviv/etnaviv_drv.h        |   4 +-
 drivers/staging/etnaviv/etnaviv_gem.h        |   7 +-
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 122 ++++++++++-----------------
 drivers/staging/etnaviv/etnaviv_gpu.c        |  38 +++++++++
 drivers/staging/etnaviv/etnaviv_gpu.h        |  20 +++++
 include/uapi/drm/etnaviv_drm.h               |  18 ++--
 8 files changed, 136 insertions(+), 127 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 225206640eb9..883beb36cdb9 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -111,6 +111,11 @@ static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_gem_object *obj)
 	return obj->paddr - gpu->memory_base;
 }
 
+static u32 gpu_va_raw(struct etnaviv_gpu *gpu, u32 paddr)
+{
+	return paddr - gpu->memory_base;
+}
+
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_object *obj, u32 off, u32 len)
 {
@@ -153,10 +158,9 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit)
 {
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
-	struct etnaviv_gem_object *cmd;
 	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
+	u32 *usercmd = submit->cmdbuf->vaddr;
 	u32 back, link_target, link_size, reserve_size, extra_size = 0;
-	u32 i;
 
 	if (drm_debug & DRM_UT_DRIVER)
 		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
@@ -194,35 +198,24 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	/* Skip over any extra instructions */
 	link_target += extra_size * sizeof(u32);
 
-	/* update offset for every cmd stream */
-	for (i = submit->nr_cmds; i--; ) {
-		cmd = submit->cmd[i].obj;
+	if (drm_debug & DRM_UT_DRIVER)
+		pr_info("stream link to 0x%08x @ 0x%08x %p\n",
+			link_target, gpu_va_raw(gpu, submit->cmdbuf->paddr),
+			submit->cmdbuf->vaddr);
 
-		cmd->offset = submit->cmd[i].offset + submit->cmd[i].size;
+	/* jump back from cmd to main buffer */
+	usercmd[submit->cmdbuf->user_size/4] = VIV_FE_LINK_HEADER_OP_LINK |
+				    VIV_FE_LINK_HEADER_PREFETCH(link_size);
+	usercmd[submit->cmdbuf->user_size/4 + 1] = link_target;
 
-		if (drm_debug & DRM_UT_DRIVER)
-			pr_info("stream link from buffer %u to 0x%08x @ 0x%08x %p\n",
-				i, link_target,
-				gpu_va(gpu, cmd) + cmd->offset * 4,
-				cmd->vaddr + cmd->offset * 4);
+	link_target = gpu_va_raw(gpu, submit->cmdbuf->paddr);
+	link_size = submit->cmdbuf->size / 8;
 
-		/* jump back from last cmd to main buffer */
-		CMD_LINK(cmd, link_size, link_target);
 
-		/* update the size */
-		submit->cmd[i].size = cmd->offset - submit->cmd[i].offset;
-
-		link_target = gpu_va(gpu, cmd) + submit->cmd[i].offset * 4;
-		link_size = submit->cmd[i].size * 2;
-	}
 
 	if (drm_debug & DRM_UT_DRIVER) {
-		for (i = 0; i < submit->nr_cmds; i++) {
-			struct etnaviv_gem_object *obj = submit->cmd[i].obj;
-
-			etnaviv_buffer_dump(gpu, obj, submit->cmd[i].offset,
-					submit->cmd[i].size);
-		}
+		print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
+			       submit->cmdbuf->vaddr, submit->cmdbuf->size, 0);
 
 		pr_info("link op: %p\n", lw);
 		pr_info("link addr: %p\n", lw + 1);
@@ -260,6 +253,11 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		link_size = extra_size;
 	}
 
+	/* take ownership of cmdbuffer*/
+	submit->cmdbuf->fence = submit->fence;
+	list_add_tail(&submit->cmdbuf->gpu_active_list, &gpu->active_cmd_list);
+	submit->cmdbuf = NULL;
+
 	/* trigger event */
 	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
 		       VIVS_GL_EVENT_FROM_PE);
diff --git a/drivers/staging/etnaviv/etnaviv_cmd_parser.c b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
index 7ae6ddb4306e..5175d6eb3bdc 100644
--- a/drivers/staging/etnaviv/etnaviv_cmd_parser.c
+++ b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
@@ -61,10 +61,10 @@ static uint8_t cmd_length[32] = {
 	[FE_OPCODE_STALL] = 2,
 };
 
-bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
-	struct etnaviv_gem_object *obj, unsigned int offset, unsigned int size)
+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, void *stream,
+			      unsigned int size)
 {
-	u32 *start = obj->vaddr + offset * 4;
+	u32 *start = stream;
 	u32 *buf = start;
 	u32 *end = buf + size;
 
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index 9cf59646fad9..719e33174e83 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -99,6 +99,8 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj);
 void etnaviv_gem_free_object(struct drm_gem_object *obj);
 int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 		u32 size, u32 flags, u32 *handle);
+struct drm_gem_object *etnaviv_gem_new_locked(struct drm_device *dev,
+		u32 size, u32 flags);
 struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 		u32 size, u32 flags);
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
@@ -108,7 +110,7 @@ void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit);
 bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
-	struct etnaviv_gem_object *obj, unsigned int offset, unsigned int size);
+	void *stream, unsigned int size);
 
 #ifdef CONFIG_DEBUG_FS
 void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m);
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 23e093425bb2..1f8876268507 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -115,13 +115,8 @@ struct etnaviv_gem_submit {
 	struct list_head bo_list;
 	struct ww_acquire_ctx ticket;
 	u32 fence;
-	unsigned int nr_cmds;
 	unsigned int nr_bos;
-	struct {
-		u32 offset; /* in dwords */
-		u32 size;  /* in dwords */
-		struct etnaviv_gem_object *obj;
-	} cmd[MAX_CMDS];
+	struct etnaviv_cmdbuf *cmdbuf;
 	struct {
 		u32 flags;
 		struct etnaviv_gem_object *obj;
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 73c1378e96a1..f886a3c66d30 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -45,7 +45,7 @@ static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
 
 		/* initially, until copy_from_user() and bo lookup succeeds: */
 		submit->nr_bos = 0;
-		submit->nr_cmds = 0;
+		submit->cmdbuf = NULL;
 
 		INIT_LIST_HEAD(&submit->bo_list);
 		ww_acquire_init(&submit->ticket, &reservation_ww_class);
@@ -212,11 +212,11 @@ static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx,
 }
 
 /* process the reloc's and patch up the cmdstream as needed: */
-static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_object *obj,
-		u32 offset, u32 nr_relocs, u64 relocs)
+static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
+		u32 size, u32 nr_relocs, u64 relocs)
 {
 	u32 i, last_offset = 0;
-	u32 *ptr = obj->vaddr;
+	u32 *ptr = stream;
 	int ret;
 
 	for (i = 0; i < nr_relocs; i++) {
@@ -240,7 +240,7 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, struct etnaviv_gem_ob
 		/* offset in dwords: */
 		off = submit_reloc.submit_offset / 4;
 
-		if ((off >= (obj->base.size / 4)) ||
+		if ((off >= size ) ||
 				(off < last_offset)) {
 			DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
 			return -EINVAL;
@@ -276,6 +276,9 @@ static void submit_cleanup(struct etnaviv_gem_submit *submit, bool fail)
 		drm_gem_object_unreference(&etnaviv_obj->base);
 	}
 
+	if (submit->cmdbuf)
+		etnaviv_gpu_cmdbuf_free(submit->cmdbuf);
+
 	ww_acquire_fini(&submit->ticket);
 	kfree(submit);
 }
@@ -286,11 +289,11 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	struct drm_etnaviv_gem_submit *args = data;
 	struct etnaviv_file_private *ctx = file->driver_priv;
-	struct drm_etnaviv_gem_submit_cmd *cmds;
 	struct drm_etnaviv_gem_submit_bo *bos;
 	struct etnaviv_gem_submit *submit;
+	struct etnaviv_cmdbuf *cmdbuf;
 	struct etnaviv_gpu *gpu;
-	unsigned i;
+	void *stream;
 	int ret;
 
 	if (args->pipe >= ETNA_MAX_PIPES)
@@ -300,27 +303,33 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (!gpu)
 		return -ENXIO;
 
-	if (args->nr_cmds > MAX_CMDS)
+	if (args->stream_size % 4) {
+		DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
+			  args->stream_size);
 		return -EINVAL;
+	}
 
 	/*
 	 * Copy the command submission and bo array to kernel space in
 	 * one go, and do this outside of the dev->struct_mutex lock.
 	 */
-	cmds = drm_malloc_ab(args->nr_cmds, sizeof(*cmds));
 	bos = drm_malloc_ab(args->nr_bos, sizeof(*bos));
-	if (!cmds || !bos)
-		return -ENOMEM;
+	stream = drm_malloc_ab(1, args->stream_size);
+	cmdbuf = etnaviv_gpu_cmdbuf_new(gpu, ALIGN(args->stream_size, 8) + 8);
+	if (!bos || !stream || !cmdbuf) {
+		ret = -ENOMEM;
+		goto err_submit_cmds;
+	}
 
-	ret = copy_from_user(cmds, to_user_ptr(args->cmds),
-			     args->nr_cmds * sizeof(*cmds));
+	ret = copy_from_user(bos, to_user_ptr(args->bos),
+			     args->nr_bos * sizeof(*bos));
 	if (ret) {
 		ret = -EFAULT;
 		goto err_submit_cmds;
 	}
 
-	ret = copy_from_user(bos, to_user_ptr(args->bos),
-			     args->nr_bos * sizeof(*bos));
+	ret = copy_from_user(stream, to_user_ptr(args->stream),
+			     args->stream_size);
 	if (ret) {
 		ret = -EFAULT;
 		goto err_submit_cmds;
@@ -364,69 +373,21 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (ret)
 		goto out;
 
-	for (i = 0; i < args->nr_cmds; i++) {
-		struct drm_etnaviv_gem_submit_cmd *submit_cmd = cmds + i;
-		struct etnaviv_gem_object *etnaviv_obj;
-		unsigned max_size;
-
-		ret = submit_bo(submit, submit_cmd->submit_idx, &etnaviv_obj,
-				NULL);
-		if (ret)
-			goto out;
-
-		if (!(etnaviv_obj->flags & ETNA_BO_CMDSTREAM)) {
-			DRM_ERROR("cmdstream bo has flag ETNA_BO_CMDSTREAM not set\n");
-			ret = -EINVAL;
-			goto out;
-		}
-
-		if (submit_cmd->size % 4) {
-			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
-					submit_cmd->size);
-			ret = -EINVAL;
-			goto out;
-		}
-
-		if (submit_cmd->submit_offset % 8) {
-			DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
-					submit_cmd->size);
-			ret = -EINVAL;
-			goto out;
-		}
-
-		/*
-		 * We must have space to add a LINK command at the end of
-		 * the command buffer.
-		 */
-		max_size = etnaviv_obj->base.size - 8;
-
-		if (submit_cmd->size > max_size ||
-		    submit_cmd->submit_offset > max_size - submit_cmd->size) {
-			DRM_ERROR("invalid cmdstream size: %u\n",
-				  submit_cmd->size);
-			ret = -EINVAL;
-			goto out;
-		}
-
-		submit->cmd[i].offset = submit_cmd->submit_offset / 4;
-		submit->cmd[i].size = submit_cmd->size / 4;
-		submit->cmd[i].obj = etnaviv_obj;
-
-		if (!etnaviv_cmd_validate_one(gpu, etnaviv_obj,
-					      submit->cmd[i].offset,
-					      submit->cmd[i].size)) {
-			ret = -EINVAL;
-			goto out;
-		}
-
-		ret = submit_reloc(submit, etnaviv_obj,
-				   submit_cmd->submit_offset,
-				   submit_cmd->nr_relocs, submit_cmd->relocs);
-		if (ret)
-			goto out;
+	if (!etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4)) {
+		ret = -EINVAL;
+		goto out;
 	}
 
-	submit->nr_cmds = i;
+	ret = submit_reloc(submit, stream, args->stream_size / 4,
+			   args->nr_relocs, args->relocs);
+	if (ret)
+		goto out;
+
+	memcpy(cmdbuf->vaddr, stream, args->stream_size);
+	cmdbuf->user_size = ALIGN(args->stream_size, 8);
+	/* transfer ownership of cmdbuf to submit */
+	submit->cmdbuf = cmdbuf;
+	cmdbuf = NULL;
 
 	ret = etnaviv_gpu_submit(gpu, submit, ctx);
 
@@ -447,11 +408,14 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (ret == -EAGAIN)
 		flush_workqueue(priv->wq);
 
- err_submit_cmds:
+err_submit_cmds:
+	/* if we still own the cmdbuf */
+	if (cmdbuf)
+		etnaviv_gpu_cmdbuf_free(cmdbuf);
+	if (stream)
+		drm_free_large(stream);
 	if (bos)
 		drm_free_large(bos);
-	if (cmds)
-		drm_free_large(cmds);
 
 	return ret;
 }
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 9291d7ad4bbf..32ff98311598 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -829,12 +829,41 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
  * Cmdstream submission/retirement:
  */
 
+struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size)
+{
+	struct etnaviv_cmdbuf *cmdbuf;
+
+	cmdbuf = kzalloc(sizeof(*cmdbuf), GFP_KERNEL);
+	if (!cmdbuf)
+		return NULL;
+
+	cmdbuf->vaddr = dma_alloc_writecombine(gpu->dev, size, &cmdbuf->paddr,
+					       GFP_KERNEL);
+	if (!cmdbuf->vaddr) {
+		kfree(cmdbuf);
+		return NULL;
+	}
+
+	cmdbuf->gpu = gpu;
+	cmdbuf->size = size;
+
+	return cmdbuf;
+}
+
+void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
+{
+	dma_free_writecombine(cmdbuf->gpu->dev, cmdbuf->size,
+			      cmdbuf->vaddr, cmdbuf->paddr);
+	kfree(cmdbuf);
+}
+
 static void retire_worker(struct work_struct *work)
 {
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       retire_work);
 	struct drm_device *dev = gpu->drm;
 	u32 fence = gpu->completed_fence;
+	struct etnaviv_cmdbuf *cmdbuf, *tmp;

@@ -857,6 +886,14 @@ static void retire_worker(struct work_struct *work)
 		}
 	}
 
+	list_for_each_entry_safe(cmdbuf, tmp, &gpu->active_cmd_list,
+				 gpu_active_list) {
+		if (fence_after_eq(fence, cmdbuf->fence)) {
+			etnaviv_gpu_cmdbuf_free(cmdbuf);
+			list_del(&cmdbuf->gpu_active_list);
+		}
+	}
+
 	gpu->retired_fence = fence;
 
 	mutex_unlock(&dev->struct_mutex);
@@ -1196,6 +1233,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	gpu->drm = drm;
 
 	INIT_LIST_HEAD(&gpu->active_list);
+	INIT_LIST_HEAD(&gpu->active_cmd_list);
 	INIT_WORK(&gpu->retire_work, retire_worker);
 	INIT_WORK(&gpu->recover_work, recover_worker);
 	init_waitqueue_head(&gpu->fence_event);
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 374f37f3665f..ee1c8504e4c8 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -103,6 +103,9 @@ struct etnaviv_gpu {
 	/* list of GEM active objects: */
 	struct list_head active_list;
 
+	/* list of currently in-flight command buffers */
+	struct list_head active_cmd_list;
+
 	u32 idle_mask;
 
 	/* Fencing support */
@@ -133,6 +136,20 @@ struct etnaviv_gpu {
 	struct work_struct recover_work;
 };
 
+struct etnaviv_cmdbuf {
+	/* device this cmdbuf is allocated for */
+	struct etnaviv_gpu *gpu;
+	/* cmdbuf properties */
+	void *vaddr;
+	dma_addr_t paddr;
+	u32 size;
+	u32 user_size;
+	/* fence after which this buffer is to be disposed */
+	u32 fence;
+	/* per GPU in-flight list */
+	struct list_head gpu_active_list;
+};
+
 static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data)
 {
 	etnaviv_writel(data, gpu->mmio + reg);
@@ -168,6 +185,9 @@ int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout);
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
+struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu,
+					      u32 size);
+void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf);
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
 void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu);
 
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 1515fa4f6986..689caf93c85d 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -123,15 +123,6 @@ struct drm_etnaviv_gem_submit_reloc {
 	__u64 reloc_offset;   /* in, offset from start of reloc_bo */
 };
 
-struct drm_etnaviv_gem_submit_cmd {
-	__u32 submit_idx;     /* in, index of submit_bo cmdstream buffer */
-	__u32 submit_offset;  /* in, offset into submit_bo */
-	__u32 size;           /* in, cmdstream size */
-	__u32 pad;
-	__u32 nr_relocs;      /* in, number of submit_reloc's */
-	__u64 relocs;         /* in, ptr to array of submit_reloc's */
-};
-
 /* Each buffer referenced elsewhere in the cmdstream submit (ie. the
  * cmdstream buffer(s) themselves or reloc entries) has one (and only
  * one) entry in the submit->bos[] table.
@@ -159,14 +150,15 @@ struct drm_etnaviv_gem_submit_bo {
 #define ETNA_PIPE_2D      0x01
 #define ETNA_PIPE_VG      0x02
 struct drm_etnaviv_gem_submit {
+	__u32 fence;          /* out */
 	__u32 pipe;           /* in */
 	__u32 exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
-	__u32 fence;          /* out */
 	__u32 nr_bos;         /* in, number of submit_bo's */
-	__u32 nr_cmds;        /* in, number of submit_cmd's */
-	__u32 pad;
+	__u32 nr_relocs;      /* in, number of submit_reloc's */
+	__u32 stream_size;    /* in, cmdstream size */
 	__u64 bos;            /* in, ptr to array of submit_bo's */
-	__u64 cmds;           /* in, ptr to array of submit_cmd's */
+	__u64 relocs;         /* in, ptr to array of submit_reloc's */
+	__u64 stream;         /* in, ptr to cmdstream */
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Instead of using a GEM buffer for the kernel internal ring buffer
use the newly introduced cmdbuf object. This removes the last remaining
user of the CMDSTREAM GEM flag.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 90 ++++++++++++++------------------
 drivers/staging/etnaviv/etnaviv_drv.c    |  8 +--
 drivers/staging/etnaviv/etnaviv_gpu.c    | 13 +++--
 drivers/staging/etnaviv/etnaviv_gpu.h    |  4 +-
 4 files changed, 53 insertions(+), 62 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 883beb36cdb9..0196d3374cd7 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -28,21 +28,22 @@
  */

-static inline void OUT(struct etnaviv_gem_object *buffer, u32 data)
+static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data)
 {
 	u32 *vaddr = (u32 *)buffer->vaddr;
 
-	BUG_ON(buffer->offset >= buffer->base.size / sizeof(*vaddr));
+	BUG_ON(buffer->user_size >= buffer->size);
 
-	vaddr[buffer->offset++] = data;
+	vaddr[buffer->user_size / 4] = data;
+	buffer->user_size += 4;
 }
 
-static inline void CMD_LOAD_STATE(struct etnaviv_gem_object *buffer,
+static inline void CMD_LOAD_STATE(struct etnaviv_cmdbuf *buffer,
 	u32 reg, u32 value)
 {
 	u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR;
 
-	buffer->offset = ALIGN(buffer->offset, 2);
+	buffer->user_size = ALIGN(buffer->user_size, 8);
 
 	/* write a register via cmd stream */
 	OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE |
@@ -51,40 +52,40 @@ static inline void CMD_LOAD_STATE(struct etnaviv_gem_object *buffer,
 	OUT(buffer, value);
 }
 
-static inline void CMD_END(struct etnaviv_gem_object *buffer)
+static inline void CMD_END(struct etnaviv_cmdbuf *buffer)
 {
-	buffer->offset = ALIGN(buffer->offset, 2);
+	buffer->user_size = ALIGN(buffer->user_size, 8);
 
 	OUT(buffer, VIV_FE_END_HEADER_OP_END);
 }
 
-static inline void CMD_WAIT(struct etnaviv_gem_object *buffer)
+static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer)
 {
-	buffer->offset = ALIGN(buffer->offset, 2);
+	buffer->user_size = ALIGN(buffer->user_size, 8);

-static inline void CMD_LINK(struct etnaviv_gem_object *buffer,
+static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer,
 	u16 prefetch, u32 address)
 {
-	buffer->offset = ALIGN(buffer->offset, 2);
+	buffer->user_size = ALIGN(buffer->user_size, 8);
 
 	OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK |
 		    VIV_FE_LINK_HEADER_PREFETCH(prefetch));
 	OUT(buffer, address);
 }
 
-static inline void CMD_STALL(struct etnaviv_gem_object *buffer,
+static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer,
 	u32 from, u32 to)
 {
-	buffer->offset = ALIGN(buffer->offset, 2);
+	buffer->user_size = ALIGN(buffer->user_size, 8);
 
 	OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL);
 	OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to));
 }
 
-static void etnaviv_cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
+static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe)
 {
 	u32 flush;
 	u32 stall;
@@ -106,24 +107,19 @@ static void etnaviv_cmd_select_pipe(struct etnaviv_gem_object *buffer, u8 pipe)
 		       VIVS_GL_PIPE_SELECT_PIPE(pipe));
 }
 
-static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_gem_object *obj)
+static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf)
 {
-	return obj->paddr - gpu->memory_base;
-}
-
-static u32 gpu_va_raw(struct etnaviv_gpu *gpu, u32 paddr)
-{
-	return paddr - gpu->memory_base;
+	return buf->paddr - gpu->memory_base;
 }
 
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
-	struct etnaviv_gem_object *obj, u32 off, u32 len)
+	struct etnaviv_cmdbuf *buf, u32 off, u32 len)
 {
-	u32 size = obj->base.size;
-	u32 *ptr = obj->vaddr + off;
+	u32 size = buf->size;
+	u32 *ptr = buf->vaddr + off;
 
 	dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n",
-			ptr, gpu_va(gpu, obj) + off, size - len * 4 - off);
+			ptr, gpu_va(gpu, buf) + off, size - len * 4 - off);
 
 	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
 			ptr, len * 4, 0);
@@ -131,24 +127,23 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
 
 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 {
-	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
+	struct etnaviv_cmdbuf *buffer = gpu->buffer;
 
 	/* initialize buffer */
-	buffer->offset = 0;
-	buffer->is_ring_buffer = true;
+	buffer->user_size = 0;
 
 	CMD_WAIT(buffer);
-	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
+	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4);
 
-	return buffer->offset / 2;
+	return buffer->user_size / 8;
 }
 
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
 {
-	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
+	struct etnaviv_cmdbuf *buffer = gpu->buffer;
 
 	/* Replace the last WAIT with an END */
-	buffer->offset -= 4;
+	buffer->user_size -= 16;
 
 	CMD_END(buffer);
 	mb();
@@ -157,9 +152,8 @@ void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_submit *submit)
 {
-	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
-	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
-	u32 *usercmd = submit->cmdbuf->vaddr;
+	struct etnaviv_cmdbuf *buffer = gpu->buffer;
+	u32 *lw = buffer->vaddr + buffer->user_size - 16;
 	u32 back, link_target, link_size, reserve_size, extra_size = 0;
 
 	if (drm_debug & DRM_UT_DRIVER)
@@ -181,18 +175,17 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 			extra_size += 8;
 	}
 
-	reserve_size = 6 + extra_size;
+	reserve_size = (6 + extra_size) * 4;
 
 	/*
 	 * if we are going to completely overflow the buffer, we need to wrap.
 	 */
-	if (buffer->offset + reserve_size >
-	    buffer->base.size / sizeof(u32))
-		buffer->offset = 0;
+	if (buffer->user_size + reserve_size > buffer->size)
+		buffer->user_size = 0;
 
 	/* save offset back into main buffer */
-	back = buffer->offset + reserve_size - 6;
-	link_target = gpu_va(gpu, buffer) + buffer->offset * 4;
+	back = buffer->user_size + reserve_size - 6 * 4;
+	link_target = gpu_va(gpu, buffer) + buffer->user_size;
 	link_size = 6;
 
 	/* Skip over any extra instructions */
@@ -200,15 +193,13 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
 	if (drm_debug & DRM_UT_DRIVER)
 		pr_info("stream link to 0x%08x @ 0x%08x %p\n",
-			link_target, gpu_va_raw(gpu, submit->cmdbuf->paddr),
+			link_target, gpu_va(gpu, submit->cmdbuf),
 			submit->cmdbuf->vaddr);
 
 	/* jump back from cmd to main buffer */
-	usercmd[submit->cmdbuf->user_size/4] = VIV_FE_LINK_HEADER_OP_LINK |
-				    VIV_FE_LINK_HEADER_PREFETCH(link_size);
-	usercmd[submit->cmdbuf->user_size/4 + 1] = link_target;
+	CMD_LINK(submit->cmdbuf, link_size, link_target);
 
-	link_target = gpu_va_raw(gpu, submit->cmdbuf->paddr);
+	link_target = gpu_va(gpu, submit->cmdbuf);
 	link_size = submit->cmdbuf->size / 8;

@@ -220,13 +211,12 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		pr_info("link op: %p\n", lw);
 		pr_info("link addr: %p\n", lw + 1);
 		pr_info("addr: 0x%08x\n", link_target);
-		pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + (back * 4));
+		pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + back);
 		pr_info("event: %d\n", event);
 	}
 
 	if (gpu->mmu->need_flush || gpu->switch_context) {
-		u32 new_target = gpu_va(gpu, buffer) + buffer->offset *
-					sizeof(u32);
+		u32 new_target = gpu_va(gpu, buffer) + buffer->user_size;
 
 		if (gpu->mmu->need_flush) {
 			/* Add the MMU flush */
@@ -264,7 +254,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
 	/* append WAIT/LINK to main buffer */
 	CMD_WAIT(buffer);
-	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
+	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + (buffer->user_size - 4));
 
 	/* Change WAIT into a LINK command; write the address first. */
 	*(lw + 1) = link_target;
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 80206c8b4bc3..27832c06920b 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -237,13 +237,13 @@ static int etnaviv_mmu_show(struct drm_device *dev, struct seq_file *m)
 
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m)
 {
-	struct etnaviv_gem_object *obj = to_etnaviv_bo(gpu->buffer);
-	u32 size = obj->base.size;
-	u32 *ptr = obj->vaddr;
+	struct etnaviv_cmdbuf *buf = gpu->buffer;
+	u32 size = buf->size;
+	u32 *ptr = buf->vaddr;
 	u32 i;
 
 	seq_printf(m, "virt %p - phys 0x%llx - free 0x%08x\n",
-			obj->vaddr, (u64)obj->paddr, size - (obj->offset * 4));
+			buf->vaddr, (u64)buf->paddr, size - buf->user_size);
 
 	for (i = 0; i < size / 4; i++) {
 		if (i && !(i % 4))
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 32ff98311598..818e3d4486fc 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -430,7 +430,7 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 
 	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
 	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
-		  etnaviv_gem_paddr_locked(gpu->buffer) - gpu->memory_base);
+		  gpu->buffer->paddr - gpu->memory_base);
 	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
 		  VIVS_FE_COMMAND_CONTROL_ENABLE |
 		  VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
@@ -488,11 +488,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	}
 
 	/* Create buffer: */
-	gpu->buffer = etnaviv_gem_new(gpu->drm, PAGE_SIZE, ETNA_BO_CMDSTREAM);
-	if (IS_ERR(gpu->buffer)) {
-		ret = PTR_ERR(gpu->buffer);
-		gpu->buffer = NULL;
-		dev_err(gpu->dev, "could not create buffer: %d\n", ret);
+	gpu->buffer = etnaviv_gpu_cmdbuf_new(gpu, PAGE_SIZE);
+	if (!gpu->buffer) {
+		ret = -ENOMEM;
+		dev_err(gpu->dev, "could not create command buffer\n");
 		goto fail;
 	}
 
@@ -1268,7 +1267,7 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
 #endif
 
 	if (gpu->buffer) {
-		drm_gem_object_unreference_unlocked(gpu->buffer);
+		etnaviv_gpu_cmdbuf_free(gpu->buffer);
 		gpu->buffer = NULL;
 	}
 
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index ee1c8504e4c8..3be5b481d8d1 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -82,6 +82,8 @@ struct etnaviv_event {
 	u32 fence;
 };
 
+struct etnaviv_cmdbuf;
+
 struct etnaviv_gpu {
 	struct drm_device *drm;
 	struct device *dev;
@@ -90,7 +92,7 @@ struct etnaviv_gpu {
 	bool switch_context;
 
 	/* 'ring'-buffer: */
-	struct drm_gem_object *buffer;
+	struct etnaviv_cmdbuf *buffer;
 
 	u32 memory_base;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 27832c06920b..4bdafc59effd 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -583,7 +583,7 @@ static struct drm_driver etnaviv_drm_driver = {
 	.fops               = &fops,
 	.name               = "etnaviv",
 	.desc               = "etnaviv DRM",
-	.date               = "20150302",
+	.date               = "20150925",
 	.major              = 1,
 	.minor              = 0,
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

This redefines how we do cache handling in the following way:

All buffer are pushed into the GPU domain initially, this will only
be done when populating the buffers backing store, but for simplicity
userspace can assume that the buffer is owned by the GPU as soon as it
constructs a GEM handle. The memory is only implicitly pulled back into
the CPU domain when destroying the handle.

Uncached and writecombined buffers can stay in the GPU domain for their
entire lifetime, as any modification to them will be seen by the GPU
either immediately or latest when the write buffers get flushed when
linking a new submit into the GPUs command stream.

If any modifications needs to be done to a cached buffer they must be
pulled into the CPU domain before and pushed to the GPU afterwards in
an explicit manner.

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 3e08f0694021..5ee43861095d 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -31,9 +31,8 @@ static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj)
 	 * For non-cached buffers, ensure the new pages are clean
 	 * because display controller, GPU, etc. are not coherent.
 	 */
-	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED)) {
+	if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) {
 		dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
-		dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
 	} else {
 		struct scatterlist *sg;
 		unsigned int i;
@@ -67,8 +66,7 @@ static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj
 	 * written into the remainder of the region, this can
 	 * discard those writes.
 	 */
-	if (etnaviv_obj->flags & (ETNA_BO_WC|ETNA_BO_CACHED)) {
-		dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+	if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) {
 		dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
 	}
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

This makes sure that we are satifying the cache handling rules outlined
in the previous commit. Cached buffers are pulled into the CPU domain
before access and pushed to the GPU again when the CPU is done.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 41 +++++++++++++++++++++++++++++++++--
 drivers/staging/etnaviv/etnaviv_gem.h |  3 +++
 2 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index 5ee43861095d..c9300f1e03d5 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -424,10 +424,21 @@ void etnaviv_gem_move_to_inactive(struct drm_gem_object *obj)
 	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
 }
 
+static inline enum dma_data_direction etnaviv_op_to_dma_dir(u32 op)
+{
+	if (op & ETNA_PREP_READ)
+		return DMA_FROM_DEVICE;
+	else if (op & ETNA_PREP_WRITE)
+		return DMA_TO_DEVICE;
+	else
+		return DMA_BIDIRECTIONAL;
+}
+
 int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
 		struct timespec *timeout)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct drm_device *dev = obj->dev;
 	int ret = 0;
 
 	if (is_active(etnaviv_obj)) {
@@ -444,14 +455,40 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
 		ret = etnaviv_gpu_wait_fence_interruptible(gpu, fence, timeout);
 	}
 
-	/* TODO cache maintenance */
+	if (etnaviv_obj->flags & ETNA_BO_CACHED) {
+		if (!etnaviv_obj->sgt) {
+			void * ret;
+
+			mutex_lock(&dev->struct_mutex);
+			ret = etnaviv_gem_get_pages(etnaviv_obj);
+			mutex_unlock(&dev->struct_mutex);
+			if (IS_ERR(ret))
+				return PTR_ERR(ret);
+		}
+
+		dma_sync_sg_for_cpu(dev->dev, etnaviv_obj->sgt->sgl,
+				    etnaviv_obj->sgt->nents,
+				    etnaviv_op_to_dma_dir(op));
+		etnaviv_obj->last_cpu_prep_op = op;
+	}
 
 	return ret;
 }
 
 int etnaviv_gem_cpu_fini(struct drm_gem_object *obj)
 {
-	/* TODO cache maintenance */
+	struct drm_device *dev = obj->dev;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	if (etnaviv_obj->flags & ETNA_BO_CACHED) {
+		/* fini without a prep is almost certainly a userspace error */
+		WARN_ON(etnaviv_obj->last_cpu_prep_op == 0);
+		dma_sync_sg_for_device(dev->dev, etnaviv_obj->sgt->sgl,
+			etnaviv_obj->sgt->nents,
+			etnaviv_op_to_dma_dir(etnaviv_obj->last_cpu_prep_op));
+		etnaviv_obj->last_cpu_prep_op = 0;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index 1f8876268507..c991d12e7aed 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -82,6 +82,9 @@ struct etnaviv_gem_object {
 	bool is_ring_buffer;
 	u32 offset;
 
+	/* cache maintenance */
+	uint32_t last_cpu_prep_op;
+
 	struct etnaviv_gem_userptr userptr;
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Neither userspace nor the kernel internal functions use the CMDSTREAM
GEM type anymore. Remove it from the public API and clean up all related
functions.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem.c | 127 +++++++---------------------------
 include/uapi/drm/etnaviv_drm.h        |   1 -
 2 files changed, 26 insertions(+), 102 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gem.c b/drivers/staging/etnaviv/etnaviv_gem.c
index c9300f1e03d5..1381c952c52f 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.c
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -31,19 +31,8 @@ static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj)
 	 * For non-cached buffers, ensure the new pages are clean
 	 * because display controller, GPU, etc. are not coherent.
 	 */
-	if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) {
+	if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK)
 		dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
-	} else {
-		struct scatterlist *sg;
-		unsigned int i;
-
-		for_each_sg(sgt->sgl, sg, sgt->nents, i) {
-			sg_dma_address(sg) = sg_phys(sg);
-#ifdef CONFIG_NEED_SG_DMA_LENGTH
-			sg_dma_len(sg) = sg->length;
-#endif
-		}
-	}
 }
 
 static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj)
@@ -66,9 +55,8 @@ static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj
 	 * written into the remainder of the region, this can
 	 * discard those writes.
 	 */
-	if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) {
+	if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK)
 		dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
-	}
 }
 
 /* called with dev->struct_mutex held */
@@ -138,27 +126,6 @@ void etnaviv_gem_put_pages(struct etnaviv_gem_object *etnaviv_obj)
 	/* when we start tracking the pin count, then do something here */
 }
 
-static int etnaviv_gem_mmap_cmd(struct drm_gem_object *obj,
-	struct vm_area_struct *vma)
-{
-	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
-	int ret;
-
-	/*
-	 * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
-	 * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
-	 * the whole buffer.
-	 */
-	vma->vm_flags &= ~VM_PFNMAP;
-	vma->vm_pgoff = 0;
-
-	ret = dma_mmap_coherent(obj->dev->dev, vma,
-				etnaviv_obj->vaddr, etnaviv_obj->paddr,
-				vma->vm_end - vma->vm_start);
-
-	return ret;
-}
-
 static int etnaviv_gem_mmap_obj(struct drm_gem_object *obj,
 		struct vm_area_struct *vma)
 {
@@ -203,12 +170,7 @@ int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 	}
 
 	obj = to_etnaviv_bo(vma->vm_private_data);
-	if (obj->flags & ETNA_BO_CMDSTREAM)
-		ret = etnaviv_gem_mmap_cmd(vma->vm_private_data, vma);
-	else
-		ret = etnaviv_gem_mmap_obj(vma->vm_private_data, vma);
-
-	return ret;
+	return etnaviv_gem_mmap_obj(vma->vm_private_data, vma);
 }
 
 int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -296,11 +258,6 @@ int etnaviv_gem_get_iova_locked(struct etnaviv_gpu *gpu,
 	struct etnaviv_vram_mapping *mapping;
 	int ret = 0;
 
-	if (etnaviv_obj->flags & ETNA_BO_CMDSTREAM) {
-		*iova = etnaviv_obj->paddr;
-		return 0;
-	}
-
 	mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu);
 	if (!mapping) {
 		struct page **pages = etnaviv_gem_get_pages(etnaviv_obj);
@@ -535,16 +492,6 @@ void etnaviv_gem_describe_objects(struct list_head *list, struct seq_file *m)
 }
 #endif
 
-static void etnaviv_gem_cmd_release(struct etnaviv_gem_object *etnaviv_obj)
-{
-	dma_free_coherent(etnaviv_obj->base.dev->dev, etnaviv_obj->base.size,
-		etnaviv_obj->vaddr, etnaviv_obj->paddr);
-}
-
-static const struct etnaviv_gem_ops etnaviv_gem_cmd_ops = {
-	.release = etnaviv_gem_cmd_release,
-};
-
 static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
 {
 	if (etnaviv_obj->vaddr)
@@ -607,24 +554,18 @@ static int etnaviv_gem_new_impl(struct drm_device *dev,
 	bool valid = true;
 
 	/* validate flags */
-	if (flags & ETNA_BO_CMDSTREAM) {
-		if ((flags & ETNA_BO_CACHE_MASK) != 0)
-			valid = false;
-	} else {
-		switch (flags & ETNA_BO_CACHE_MASK) {
-		case ETNA_BO_UNCACHED:
-		case ETNA_BO_CACHED:
-		case ETNA_BO_WC:
-			break;
-		default:
-			valid = false;
-		}
+	switch (flags & ETNA_BO_CACHE_MASK) {
+	case ETNA_BO_UNCACHED:
+	case ETNA_BO_CACHED:
+	case ETNA_BO_WC:
+		break;
+	default:
+		valid = false;
 	}
 
 	if (!valid) {
-		dev_err(dev->dev, "invalid cache flag: %x (cmd: %d)\n",
-				(flags & ETNA_BO_CACHE_MASK),
-				(flags & ETNA_BO_CMDSTREAM));
+		dev_err(dev->dev, "invalid cache flag: %x\n",
+			(flags & ETNA_BO_CACHE_MASK));
 		return -EINVAL;
 	}
 
@@ -632,16 +573,6 @@ static int etnaviv_gem_new_impl(struct drm_device *dev,
 	if (!etnaviv_obj)
 		return -ENOMEM;
 
-	if (flags & ETNA_BO_CMDSTREAM) {
-		etnaviv_obj->vaddr = dma_alloc_coherent(dev->dev, size,
-				&etnaviv_obj->paddr, GFP_KERNEL);
-
-		if (!etnaviv_obj->vaddr) {
-			kfree(etnaviv_obj);
-			return -ENOMEM;
-		}
-	}
-
 	etnaviv_obj->flags = flags;
 
 	etnaviv_obj->resv = &etnaviv_obj->_resv;
@@ -668,26 +599,20 @@ static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev,
 	if (ret)
 		goto fail;
 
-	ret = 0;
-	if (flags & ETNA_BO_CMDSTREAM) {
-		to_etnaviv_bo(obj)->ops = &etnaviv_gem_cmd_ops;
-		drm_gem_private_object_init(dev, obj, size);
-	} else {
-		to_etnaviv_bo(obj)->ops = &etnaviv_gem_shmem_ops;
-		ret = drm_gem_object_init(dev, obj, size);
-		if (ret == 0) {
-			struct address_space *mapping;
-
-			/*
-			 * Our buffers are kept pinned, so allocating them
-			 * from the MOVABLE zone is a really bad idea, and
-			 * conflicts with CMA.  See coments above new_inode()
-			 * why this is required _and_ expected if you're
-			 * going to pin these pages.
-			 */
-			mapping = file_inode(obj->filp)->i_mapping;
-			mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
-		}
+	to_etnaviv_bo(obj)->ops = &etnaviv_gem_shmem_ops;
+	ret = drm_gem_object_init(dev, obj, size);
+	if (ret == 0) {
+		struct address_space *mapping;
+
+		/*
+		 * Our buffers are kept pinned, so allocating them
+		 * from the MOVABLE zone is a really bad idea, and
+		 * conflicts with CMA.  See coments above new_inode()
+		 * why this is required _and_ expected if you're
+		 * going to pin these pages.
+		 */
+		mapping = file_inode(obj->filp)->i_mapping;
+		mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
 	}
 
 	if (ret)
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 689caf93c85d..5f1206b3f9ba 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -72,7 +72,6 @@ struct drm_etnaviv_param {
  * GEM buffers:
  */
 
-#define ETNA_BO_CMDSTREAM    0x00000001
 #define ETNA_BO_CACHE_MASK   0x000f0000
 /* cache modes */
 #define ETNA_BO_CACHED       0x00010000

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Not bullet proof yet, as this possibly shoots down more submits than
necessary. However it allows for fairly rapid turnarounds during
userspace development.

Signed-off-by: Lucas Stach <[email-masked]>
Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index 818e3d4486fc..8f54fa8dd59d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -718,12 +718,40 @@ static void recover_worker(struct work_struct *work)
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       recover_work);
 	struct drm_device *dev = gpu->drm;
+	unsigned long flags;
+	unsigned int i;
 
 	dev_err(gpu->dev, "hangcheck recover!\n");
 
+	if (pm_runtime_get_sync(gpu->dev) < 0)
+		return;
+
 	mutex_lock(&dev->struct_mutex);
-	/* TODO gpu->funcs->recover(gpu); */
+
+	etnaviv_hw_reset(gpu);
+
+	/* complete all events, the GPU won't do it after the reset */
+	spin_lock_irqsave(&gpu->event_spinlock, flags);
+	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
+		if (!gpu->event[i].used)
+			continue;
+		gpu->event[i].used = false;
+		complete(&gpu->event_free);
+		/*
+		 * Decrement the PM count for each stuck event. This is safe
+		 * even in atomic context as we use ASYNC RPM here.
+		 */
+		pm_runtime_put_autosuspend(gpu->dev);
+	}
+	spin_unlock_irqrestore(&gpu->event_spinlock, flags);
+	gpu->completed_fence = gpu->submitted_fence;
+
+	etnaviv_gpu_hw_init(gpu);
+	gpu->switch_context = true;
+
 	mutex_unlock(&dev->struct_mutex);
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
 
 	/* Retire the buffer objects in a work */
 	etnaviv_queue_work(gpu->drm, &gpu->retire_work);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Validate the exec_state ioctl argument.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_gem_submit.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index f886a3c66d30..6c5eeb47e5c4 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -309,6 +309,13 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		return -EINVAL;
 	}
 
+	if (args->exec_state != ETNA_PIPE_3D &&
+	    args->exec_state != ETNA_PIPE_2D &&
+	    args->exec_state != ETNA_PIPE_VG) {
+		DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state);
+		return -EINVAL;
+	}
+
 	/*
 	 * Copy the command submission and bo array to kernel space in
 	 * one go, and do this outside of the dev->struct_mutex lock.

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 4bdafc59effd..9aefa6099f7c 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -373,6 +373,10 @@ static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data,
 {
 	struct drm_etnaviv_gem_new *args = data;
 
+	if (args->flags & ~(ETNA_BO_CACHED | ETNA_BO_WC | ETNA_BO_UNCACHED |
+			    ETNA_BO_FORCE_MMU))
+		return -EINVAL;
+
 	return etnaviv_gem_new_handle(dev, file, args->size,
 			args->flags, &args->handle);
 }

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 9aefa6099f7c..65908df203f9 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -478,7 +478,8 @@ static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
 
 	if (offset_in_page(args->user_ptr | args->user_size) ||
 	    (uintptr_t)args->user_ptr != args->user_ptr ||
-	    (u32)args->user_size != args->user_size)
+	    (u32)args->user_size != args->user_size ||
+	    args->user_ptr & ~PAGE_MASK)
 		return -EINVAL;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:16 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/staging/etnaviv/etnaviv_drv.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index 65908df203f9..153d33eea147 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -393,6 +393,9 @@ static int etnaviv_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
 	struct drm_gem_object *obj;
 	int ret;
 
+	if (args->op & ~(ETNA_PREP_READ | ETNA_PREP_WRITE | ETNA_PREP_NOSYNC))
+		return -EINVAL;
+
 	obj = drm_gem_object_lookup(dev, file, args->handle);
 	if (!obj)
 		return -ENOENT;

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/armada/Makefile      |  1 +
 drivers/gpu/drm/armada/armada_610.c  | 49 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/armada/armada_crtc.c |  7 ++++++
 drivers/gpu/drm/armada/armada_drm.h  |  1 +
 drivers/gpu/drm/armada/armada_hw.h   |  8 ++++++
 5 files changed, 66 insertions(+)
 create mode 100644 drivers/gpu/drm/armada/armada_610.c

diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
index ffd673615772..1b52ae594006 100644
--- a/drivers/gpu/drm/armada/Makefile
+++ b/drivers/gpu/drm/armada/Makefile
@@ -1,6 +1,7 @@
 armada-y	:= armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
 		   armada_gem.o armada_overlay.o
 armada-y	+= armada_510.o
+armada-y	+= armada_610.o
 armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
 
 obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_610.c b/drivers/gpu/drm/armada/armada_610.c
new file mode 100644
index 000000000000..1fbb7295c77f
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_610.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 Russell King
+ *
+ * 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.
+ *
+ * Armada 610/MMP2/MMP3 variant support
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "armada_crtc.h"
+#include "armada_drm.h"
+#include "armada_hw.h"
+
+static int mmp23_init(struct armada_crtc *dcrtc, struct device *dev)
+{
+	dcrtc->extclk[0] = devm_clk_get(dev, NULL);
+
+	if (IS_ERR(dcrtc->extclk[0]) && PTR_ERR(dcrtc->extclk[0]) == -ENOENT)
+		dcrtc->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+
+	return PTR_RET(dcrtc->extclk[0]);
+}
+
+/*
+ * This gets called with sclk = NULL to test whether the mode is
+ * supportable, and again with sclk != NULL to set the clocks up for
+ * that.  The former can return an error, but the latter is expected
+ * not to.
+ */
+static int mmp23_crtc_compute_clock(struct armada_crtc *dcrtc,
+	const struct drm_display_mode *mode, uint32_t *sclk)
+{
+	/*
+	 * on MMP3 bits 31:29 select the clock, OLPC wants 0x1 here, LCD clock 1
+	 * on MMP2 bits 31:30 select the clock, OLPC wants 0x1 here, LCD clock 1
+	 */
+	*sclk = 0x20001100; // FIXME hardcoded mmp3 value
+
+	return 0;
+}
+
+const struct armada_variant mmp23_ops = {
+	.init = mmp23_init,
+	.compute_clock = mmp23_crtc_compute_clock,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index cebcab560626..00ada2eaaac9 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -1161,6 +1161,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 	dcrtc->clk = ERR_PTR(-EINVAL);
 	dcrtc->csc_yuv_mode = CSC_AUTO;
 	dcrtc->csc_rgb_mode = CSC_AUTO;
+	/* FIXME: MMP2/MMP3: OLPC panel is RGB666 */
 	dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
 	dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
 	spin_lock_init(&dcrtc->irq_lock);
@@ -1325,6 +1326,12 @@ static const struct platform_device_id armada_lcd_platform_ids[] = {
 		.name		= "armada-lcd",
 		.driver_data	= (unsigned long)&armada510_ops,
 	}, {
+		.name		= "armada-610-lcd", /* aka MMP2 */
+		.driver_data	= (unsigned long)&mmp23_ops,
+	}, {
+		.name		= "mmp3-lcd",
+		.driver_data	= (unsigned long)&mmp23_ops,
+	}, {
 		.name		= "armada-510-lcd",
 		.driver_data	= (unsigned long)&armada510_ops,
 	},
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index 4df6f2af2b21..679c5f52b51b 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -51,6 +51,7 @@ struct armada_variant {
 
 /* Variant ops */
 extern const struct armada_variant armada510_ops;
+extern const struct armada_variant mmp23_ops;
 
 struct armada_private {
 	struct work_struct	fb_unref_work;
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
index 27319a8335e2..1688716a15e7 100644
--- a/drivers/gpu/drm/armada/armada_hw.h
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -74,6 +74,7 @@ enum {
 	LCD_SPU_IOPAD_CONTROL		= 0x01bc,
 	LCD_SPU_IRQ_ENA			= 0x01c0,
 	LCD_SPU_IRQ_ISR			= 0x01c4,
+	LCD_MISC_CNTL			= 0x01c8,	/* Armada 168 */
 };
 
 /* For LCD_SPU_ADV_REG */
@@ -211,6 +212,13 @@ enum {
 	SCLK_16X_PLL		= 0x8 << 28,
 	SCLK_16X_FRAC_DIV_MASK	= 0xfff << 16,
 	SCLK_16X_INT_DIV_MASK	= 0xffff << 0,
+
+	/* PXA910 / MMP2 (Armada 610) / MMP3 / PXA988 */
+	SCLK_MMP_SRC_SEL	= 1 << 31,
+	SCLK_MMP_DISABLE	= 1 << 28,
+	SCLK_MMP_FRAC_DIV_MASK	= 0xfff << 16,
+	SCLK_MMP_DSI_DIV_MASK	= 0xf << 8,
+	SCLK_MMP_INT_DIV_MASK	= 0xff << 0,
 };
 
 /* For LCD_SPU_DUMB_CTRL */

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

If the number of connectors changes, then it is possible for the fbdev
helper to overrun/underrun some arrays which it allocates.  It allocates
these arrays based on mode_config.num_connector but then walks lists
using fb_helper->connector_count to limit the array index.  This can
lead to writes off the end of the arrays.

Fix this by allocating the arrays using fb_helper->connector_count.

A similar thing exists for some of the CRTC arrays.  For these, use
fb_helper->crtc_count.

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/drm_fb_helper.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index cac422916c7a..ba43fa2b6dfd 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1549,7 +1549,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 			  int n, int width, int height)
 {
 	int c, o;
-	struct drm_device *dev = fb_helper->dev;
 	struct drm_connector *connector;
 	const struct drm_connector_helper_funcs *connector_funcs;
 	struct drm_encoder *encoder;
@@ -1568,7 +1567,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 	if (modes[n] == NULL)
 		return best_score;
 
-	crtcs = kzalloc(dev->mode_config.num_connector *
+	crtcs = kcalloc(fb_helper->crtc_count,
 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
 	if (!crtcs)
 		return best_score;
@@ -1614,7 +1613,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 		if (score > best_score) {
 			best_score = score;
 			memcpy(best_crtcs, crtcs,
-			       dev->mode_config.num_connector *
+			       fb_helper->crtc_count *
 			       sizeof(struct drm_fb_helper_crtc *));
 		}
 	}
@@ -1639,13 +1638,13 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 	width = dev->mode_config.max_width;
 	height = dev->mode_config.max_height;
 
-	crtcs = kcalloc(dev->mode_config.num_connector,
+	crtcs = kcalloc(fb_helper->connector_count,
 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
-	modes = kcalloc(dev->mode_config.num_connector,
+	modes = kcalloc(fb_helper->connector_count,
 			sizeof(struct drm_display_mode *), GFP_KERNEL);
-	offsets = kcalloc(dev->mode_config.num_connector,
+	offsets = kcalloc(fb_helper->connector_count,
 			  sizeof(struct drm_fb_offset), GFP_KERNEL);
-	enabled = kcalloc(dev->mode_config.num_connector,
+	enabled = kcalloc(fb_helper->connector_count,
 			  sizeof(bool), GFP_KERNEL);
 	if (!crtcs || !modes || !enabled || !offsets) {
 		DRM_ERROR("Memory allocation failed\n");
@@ -1659,9 +1658,9 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 	      fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
 					       offsets,
 					       enabled, width, height))) {
-		memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
-		memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
-		memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
+		memset(modes, 0, fb_helper->connector_count * sizeof(modes[0]));
+		memset(crtcs, 0, fb_helper->connector_count * sizeof(crtcs[0]));
+		memset(offsets, 0, fb_helper->connector_count * sizeof(offsets[0]));
 
 		if (!drm_target_cloned(fb_helper, modes, offsets,
 				       enabled, width, height) &&

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 896b6aaf8c4d..8b0508b4ca98 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -1399,6 +1399,40 @@ static const struct drm_connector_funcs tda998x_connector_funcs = {
 	.destroy = tda998x_connector_destroy,
 };
 
+static ssize_t i2c_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+	unsigned int page, addr;
+	unsigned char val;
+
+	sscanf(buf, "%x %x", &page, &addr);
+
+	val = reg_read(priv, REG(page, addr));
+
+	printk("i2c read %02x @ page:%02x address:%02x\n", val, page, addr);
+	return size;
+}
+
+static ssize_t i2c_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct tda998x_priv *priv = dev_get_drvdata(dev);
+	unsigned int page, addr, mask, val;
+	unsigned char rval;
+
+	sscanf(buf, "%x %x %x %x", &page, &addr, &mask, &val);
+
+	rval = reg_read(priv, REG(page, addr));
+	rval &= ~mask;
+	rval |= val & mask;
+	reg_write(priv, REG(page, addr), rval);
+
+	printk("i2c write %02x @ page:%02x address:%02x\n", rval, page, addr);
+	return size;
+}
+
+static DEVICE_ATTR(i2c_read, S_IWUSR, NULL, i2c_read_store);
+static DEVICE_ATTR(i2c_write, S_IWUSR, NULL, i2c_write_store);
+
 static int tda998x_bind(struct device *dev, struct device *master, void *data)
 {
 	struct tda998x_encoder_params *params = dev->platform_data;
@@ -1430,6 +1464,11 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
+/* debug */
+	device_create_file(&client->dev, &dev_attr_i2c_read);
+	device_create_file(&client->dev, &dev_attr_i2c_write);
+/* debug end */
+
 	if (!dev->of_node && params)
 		tda998x_encoder_set_config(priv, params);

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

Signed-off-by: Russell King <[email-masked]>
---
 drivers/gpu/drm/i2c/tda998x_drv.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 8b0508b4ca98..49ca9eda4957 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -1459,6 +1459,10 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 
 	priv->connector.interlace_allowed = 1;
 	priv->encoder.possible_crtcs = crtcs;
+	priv->base.params.audio_cfg = BIT(2);
+	priv->base.params.audio_frame[1] = 1;
+	priv->base.params.audio_format = AFMT_SPDIF;
+	priv->base.params.audio_sample_rate = 44100;
 
 	ret = tda998x_create(client, priv);
 	if (ret)

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

Add DT support for the VMeta video decode unit on Marvell Dove platforms

diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 179121630ad7..918672af6738 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -767,6 +767,13 @@
 				interrupts = <47>;
 				status = "disabled";
 			};
+
+			vmeta: video-decoder@c00000 {
+				compatible = "marvell,vmeta";
+				reg = <0xc00000 0x280000>;
+				interrupts = <51>;
+				power-domains = <&vpu_domain>;
+			};
 		};
 	};
 };

---

From: Russell King <[email-masked]>
Date: Fri, 23 Oct 2015 14:56:17 +0100

Signed-off-by: Russell King <[email-masked]>
---
 arch/arm/boot/dts/dove-cubox.dts | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/arch/arm/boot/dts/dove-cubox.dts b/arch/arm/boot/dts/dove-cubox.dts
index e6fa251e17b9..857f5a7f20f2 100644
--- a/arch/arm/boot/dts/dove-cubox.dts
+++ b/arch/arm/boot/dts/dove-cubox.dts
@@ -108,6 +108,31 @@
 			silabs,pll-master;
 		};
 	};
+
+	tda998x: hdmi-encoder {
+		compatible = "nxp,tda998x";
+		reg = <0x70>;
+		video-ports = <0x234501>;
+		interrupt-parent = <&gpio0>;
+		interrupts = <27 2>;
+
+		port {
+			tda998x_video: endpoint {
+				remote-endpoint = <&lcd0_rgb>;
+			};
+		};
+	};
+};
+
+&lcd0 {
+	status = "okay";
+	clocks = <&si5351 0>;
+	clock-names = "ext_ref_clk1";
+	port {
+		lcd0_rgb: endpoint {
+			remote-endpoint = <&tda998x_video>;
+		};
+	};
 };
 
 &sdio0 {