# Base git commit: 64291f7db5bd
# (Linux 4.2)
# 
# Author:    Russell King (Tue 22 Apr 11:15:03 BST 2014)
# Committer: Russell King (Wed 21 Oct 20:56:56 BST 2015)
# 
#   ARM: dt: cubox: add LCD controller and TDA998x configuration
# 
#   Signed-off-by: Russell King
# 
#   d12dfa9157dd4dd124fe8f661f2e924b4a90fb25
#    arch/arm/boot/dts/dove-cubox.dts | 25 +++++++++++++++++++++++++
#    1 file changed, 25 insertions(+)
# 
# Author:    Russell King (Sat 14 Jun 17:10:07 BST 2014)
# Committer: Russell King (Wed 21 Oct 20:56:56 BST 2015)
# 
#   ARM: dove: add DT VMeta support
# 
#   Add DT support for the VMeta video decode unit on Marvell Dove platforms
# 
#   Signed-off-by: Russell King
# 
#   f85592ee112ffb085e349979e9701a3a0196f3e4
#    arch/arm/boot/dts/dove.dtsi | 7 +++++++
#    1 file changed, 7 insertions(+)
# 
# Author:    Russell King (Wed 21 Oct 20:56:55 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:56:55 BST 2015)
# 
#   Merge branch 'drm-head' into cubox-ml-4.2
# 
# Author:    Russell King (Wed 21 Oct 20:56:43 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:56:43 BST 2015)
# 
#   Merge branches 'asoc-4.2', 'bmm-4.1', 'vmeta' and 'pmu' into cubox-ml-4.2
# 
# Author:    Russell King (Tue 22 Apr 00:42:14 BST 2014)
# Committer: Russell King (Wed 21 Oct 20:55:51 BST 2015)
# 
#   drm/i2c: tda998x: add temporary audio configuration
# 
#   Signed-off-by: Russell King
# 
#   b8374f4585428ad3617f287781e0e3d13dcb5736
#    drivers/gpu/drm/i2c/tda998x_drv.c | 4 ++++
#    1 file changed, 4 insertions(+)
# 
# Author:    Russell King (Fri 16 Aug 14:12:24 BST 2013)
# Committer: Russell King (Wed 21 Oct 20:55:50 BST 2015)
# 
#   drm/i2c: tda998x: add debug
# 
#   Signed-off-by: Russell King
# 
#   cbc3d14476ca09da84c0ac0f14264199f052e4c0
#    drivers/gpu/drm/i2c/tda998x_drv.c | 39 +++++++++++++++++++++++++++++++++++++++
#    1 file changed, 39 insertions(+)
# 
# Author:    Russell King (Sun 27 Oct 15:27:27 GMT 2013)
# Committer: Russell King (Wed 21 Oct 20:55:49 BST 2015)
# 
#   drm: ensure fbdev helper arrays are appropriately dimensioned
# 
#   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
# 
#   a7a2a029e8b3e50aafb2b194648ad921fd9dc288
#    drivers/gpu/drm/drm_fb_helper.c | 19 +++++++++----------
#    1 file changed, 9 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Sat 29 Jun 14:46:54 BST 2013)
# Committer: Russell King (Wed 21 Oct 20:55:49 BST 2015)
# 
#   drm/armada: start of MMP2/MMP3 support
# 
#   Signed-off-by: Russell King
# 
#   bb50e53dcc9e6418695ed856a5d99df646bcf88c
#    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
# 
# Author:    Russell King (Wed 21 Oct 20:55:46 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:55:46 BST 2015)
# 
#   Merge remote-tracking branches 'rmk/drm-armada-devel', 'rmk/drm-etnaviv-devel' and 'rmk/drm-tda998x-devel2' into drm-head
# 
# Author:    Russell King (Wed 21 Oct 17:37:05 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:44 BST 2015)
# 
#   staging: etnaviv: validate gem_cpu_prep() op argument
# 
#   Signed-off-by: Russell King
# 
#   09ec13b67ffa34d356c52ee050a497be9e3e9199
#    drivers/staging/etnaviv/etnaviv_drv.c | 3 +++
#    1 file changed, 3 insertions(+)
# 
# Author:    Russell King (Wed 21 Oct 17:25:30 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:44 BST 2015)
# 
#   staging: etnaviv: add validation of userptr pointer
# 
#   Signed-off-by: Russell King
# 
#   ac9d32b9e273e1261538e4cba5b9669f7a6c1162
#    drivers/staging/etnaviv/etnaviv_drv.c | 3 ++-
#    1 file changed, 2 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Wed 21 Oct 16:57:29 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:44 BST 2015)
# 
#   staging: etnaviv: validate gem_new() flags argument
# 
#   Signed-off-by: Russell King
# 
#   083fd0fef42aeb8529205cf4bd9f6f2d396ea602
#    drivers/staging/etnaviv/etnaviv_drv.c | 4 ++++
#    1 file changed, 4 insertions(+)
# 
# Author:    Russell King (Wed 21 Oct 16:49:19 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:43 BST 2015)
# 
#   staging: etnaviv: validate gem_submit->exec_state
# 
#   Validate the exec_state ioctl argument.
# 
#   Signed-off-by: Russell King
# 
#   31687903f0836a0a1f743d7eb8d4ae02f6fbf035
#    drivers/staging/etnaviv/etnaviv_gem_submit.c | 7 +++++++
#    1 file changed, 7 insertions(+)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:57:54 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:43 BST 2015)
# 
#   staging: etnaviv: implement simple hang recovery
# 
#   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
#   Signed-off-by: Russell King
# 
#   f7b7f2feededbc0d1525e7bdb7dde02d7577caec
#    drivers/staging/etnaviv/etnaviv_gpu.c | 30 +++++++++++++++++++++++++++++-
#    1 file changed, 29 insertions(+), 1 deletion(-)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:58:00 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:43 BST 2015)
# 
#   staging: etnaviv: remove CMDSTREAM GEM allocation from UAPI
# 
#   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
#   Signed-off-by: Russell King
# 
#   baa71cefd62ecaff5efe6546b364484a3207d512
#    drivers/staging/etnaviv/etnaviv_gem.c | 127 +++++++---------------------------
#    include/uapi/drm/etnaviv_drm.h        |   1 -
#    2 files changed, 26 insertions(+), 102 deletions(-)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:57:56 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:43 BST 2015)
# 
#   staging: etnaviv: implement cache maintenance on cpu_(prep|fini)
# 
#   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
#   Signed-off-by: Russell King
# 
#   5356648df7cec9c71709771277862e9ef6b0ddae
#    drivers/staging/etnaviv/etnaviv_gem.c | 41 +++++++++++++++++++++++++++++++++--
#    drivers/staging/etnaviv/etnaviv_gem.h |  3 +++
#    2 files changed, 42 insertions(+), 2 deletions(-)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:57:55 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:43 BST 2015)
# 
#   staging: etnaviv: map all buffers to the GPU
# 
#   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.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   642abef02db7de4d3d77f57711e41fae5f5d9e87
#    drivers/staging/etnaviv/etnaviv_gem.c | 6 ++----
#    1 file changed, 2 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Wed 21 Oct 14:17:07 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:42 BST 2015)
# 
#   staging: etnaviv: update driver date
# 
#   Signed-off-by: Russell King
# 
#   ec63ba2d5cd2eb1f5ef50d3402617abc31d59027
#    drivers/staging/etnaviv/etnaviv_drv.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:57:59 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:42 BST 2015)
# 
#   staging: etnaviv: don't use GEM buffer for internal ring buffer
# 
#   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
#   Signed-off-by: Russell King
# 
#   89300181dd2e57fea83daf28995dda1473417fbd
#    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(-)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:57:58 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:42 BST 2015)
# 
#   staging: etnaviv: rewrite submit interface to use copy from user
# 
#   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
#   Signed-off-by: Russell King
# 
#   7d68e433b2210e5778d610e79eaaa73e3d658b08
#    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(-)
# 
# Author:    Lucas Stach (Fri 25 Sep 12:57:57 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:42 BST 2015)
# 
#   staging: etnaviv: remove submit type
# 
#   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
#   Signed-off-by: Russell King
# 
#   e8c83cc46c07d54300973cb3bccc35960a7cb34a
#    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(-)
# 
# Author:    Christian Gmeiner (Fri 25 Sep 12:57:53 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:42 BST 2015)
# 
#   staging: etnaviv: change etnaviv_buffer_init() to return prefetch
# 
#   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
#   Signed-off-by: Russell King
# 
#   f066e064358a12edada94e6b8c10a2d710a3bfde
#    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(-)
# 
# Author:    Christian Gmeiner (Fri 25 Sep 12:57:52 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:41 BST 2015)
# 
#   staging: etnaviv: debugfs: add possibility to dump kernel buffer
# 
#   This is very useful for debugging issues regarding command
#   buffer processing.
# 
#   Signed-off-by: Christian Gmeiner
#   Signed-off-by: Russell King
# 
#   7080b79fc0fb13b93a19f9c265987588a5436f98
#    drivers/staging/etnaviv/etnaviv_drv.c | 39 +++++++++++++++++++++++++++++++++++
#    1 file changed, 39 insertions(+)
# 
# Author:    Christian Gmeiner (Sun 21 Jun 12:22:30 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:41 BST 2015)
# 
#   staging: etnaviv: fix error: 'etnaviv_gpu_hw_resume' defined but not used
# 
#   Signed-off-by: Christian Gmeiner
#   Signed-off-by: Russell King
# 
#   69f32c702866e16bd5329cf461c36a220cf4671c
#    drivers/staging/etnaviv/etnaviv_gpu.c | 2 ++
#    1 file changed, 2 insertions(+)
# 
# Author:    Christian Gmeiner (Sun 21 Jun 12:22:29 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:41 BST 2015)
# 
#   staging: etnaviv: fix 'ret' may be used uninitialized in this function
# 
#   Signed-off-by: Christian Gmeiner
#   Signed-off-by: Russell King
# 
#   b0cd09ff38634ee7cf6dccbc9cb4061494f4aa7d
#    drivers/staging/etnaviv/etnaviv_gem.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Mon 15 Jun 10:20:32 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:41 BST 2015)
# 
#   staging: etnaviv: avoid pinning pages in CMA
# 
#   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
# 
#   f25ddbaffd68abed58ba3a516ff4b4d7b235d996
#    drivers/staging/etnaviv/etnaviv_gem.c | 13 +++++++++++++
#    1 file changed, 13 insertions(+)
# 
# Author:    Russell King (Sun 31 May 09:29:51 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:41 BST 2015)
# 
#   staging: etnaviv: add support for GEM_WAIT ioctl
# 
#   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
# 
#   25f03dabb2fe80910b35e308e2f4677cf490a52e
#    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(-)
# 
# Author:    Russell King (Tue 26 May 11:57:07 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:40 BST 2015)
# 
#   staging: etnaviv: move mapping teardown into etnaviv_gem_free_object()
# 
#   Move the mapping teardown directly into etnaviv_gem_free_object().
# 
#   Signed-off-by: Russell King
# 
#   62da28054bd60c207e25eb692c3dfd74859daf1e
#    drivers/staging/etnaviv/etnaviv_gem.c | 17 ++++-------------
#    1 file changed, 4 insertions(+), 13 deletions(-)
# 
# Author:    Russell King (Tue 26 May 16:17:48 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:40 BST 2015)
# 
#   staging: etnaviv: remove cmd buffer offset validation in submit_reloc()
# 
#   We've already validated the command buffer offset, there's no need to
#   repeat it elsewhere.
# 
#   Signed-off-by: Russell King
# 
#   6cb42fdb8dfdc0a658d95314c7414df94352cf4e
#    drivers/staging/etnaviv/etnaviv_gem_submit.c | 5 -----
#    1 file changed, 5 deletions(-)
# 
# Author:    Russell King (Tue 26 May 13:47:35 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:40 BST 2015)
# 
#   staging: etnaviv: copy submit command and bos in one go
# 
#   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
# 
#   c5c9954711101bed21033e0812f78fdd3ac6929c
#    drivers/staging/etnaviv/etnaviv_gem_submit.c | 98 ++++++++++++++++------------
#    1 file changed, 57 insertions(+), 41 deletions(-)
# 
# Author:    Russell King (Tue 26 May 09:44:41 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:40 BST 2015)
# 
#   staging: etnaviv: no point looking up the mapping for cmdstream bos
# 
#   Signed-off-by: Russell King
# 
#   44b2e86875df81371d6ed8d1b27654c9e05e6d10
#    drivers/staging/etnaviv/etnaviv_gem.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Fri 22 May 10:57:18 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:39 BST 2015)
# 
#   staging: etnaviv: clean up etnaviv_iommu_unmap_gem() signature
# 
#   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
# 
#   32a6ce8f489a01c568a4b4065ff99bf5fe95d430
#    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(-)
# 
# Author:    Russell King (Thu 21 May 13:50:05 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:39 BST 2015)
# 
#   staging: etnaviv: fix oops caused by scanning for free blocks
# 
#   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
# 
#   c86892eb3b74d726818c008ba9693f87c808bbb5
#    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(-)
# 
# Author:    Russell King (Thu 21 May 13:24:26 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:39 BST 2015)
# 
#   staging: etnaviv: no need to initialise a list_head
# 
#   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
# 
#   6aad1ba2ca0439d81c9bba051a5ecbd9cf23b375
#    drivers/staging/etnaviv/etnaviv_mmu.c | 1 -
#    1 file changed, 1 deletion(-)
# 
# Author:    Russell King (Sun 17 May 17:26:14 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:39 BST 2015)
# 
#   staging: etnaviv: use standard kernel types rather than stdint.h types
# 
#   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
# 
#   0d39d3ddcfa97ff70d865e016e67abebc6de0e6b
#    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(-)
# 
# Author:    Russell King (Sun 17 May 16:07:44 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:39 BST 2015)
# 
#   staging: etnaviv: provide etnaviv_queue_work()
# 
#   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
# 
#   a60678ab9bb2c2214cb320037ff018af5b3fe040
#    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(-)
# 
# Author:    Russell King (Sun 17 May 15:48:36 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:38 BST 2015)
# 
#   staging: etnaviv: switch to per-GPU fence completion implementation
# 
#   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
# 
#   5c7d46e9ef01e2451d8ed3da2f5160181f066ff4
#    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(-)
# 
# Author:    Russell King (Sun 17 May 14:36:39 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:38 BST 2015)
# 
#   staging: etnaviv: make context a per-GPU thing
# 
#   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
# 
#   32c601e17fee5bff3131880253051da3e37d2cfa
#    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(-)
# 
# Author:    Russell King (Sun 17 May 00:02:15 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:38 BST 2015)
# 
#   staging: etnaviv: allow etnaviv_ioctl_gem_info() locking to be interruptible
# 
#   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
# 
#   3f07749aa687deb7dce546c4659fbf87261cf4f3
#    drivers/staging/etnaviv/etnaviv_drv.c | 7 ++++++-
#    drivers/staging/etnaviv/etnaviv_gem.c | 7 ++-----
#    2 files changed, 8 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Sat 16 May 23:58:24 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:38 BST 2015)
# 
#   staging: etnaviv: give etnaviv_gem_mmap_offset() a sane behaviour
# 
#   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
# 
#   24868f6fdbf4c7461001865ff2b42579772f8620
#    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(-)
# 
# Author:    Russell King (Sat 16 May 23:35:52 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:38 BST 2015)
# 
#   staging: etnaviv: etnaviv_gem_fault: reduce struct_mutex exposure
# 
#   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
# 
#   c72d47b70e31015e711483b5de743ee35f06fa4c
#    drivers/staging/etnaviv/etnaviv_gem.c | 12 +++++++-----
#    1 file changed, 7 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Sat 16 May 23:32:55 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:37 BST 2015)
# 
#   staging: etnaviv: use vm_insert_page() rather than vm_insert_mixed()
# 
#   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
# 
#   763f66727adb2a44782089238f0acd3b4535a5f5
#    drivers/staging/etnaviv/etnaviv_gem.c | 9 ++++-----
#    1 file changed, 4 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Sat 16 May 22:56:15 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:37 BST 2015)
# 
#   staging: etnaviv: fix missing error cleanups in etnaviv_load()
# 
#   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
# 
#   4f7df58806ef55d32edbb0da43e14d87abf93aa3
#    drivers/staging/etnaviv/etnaviv_drv.c | 14 +++++++++++++-
#    1 file changed, 13 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Fri 15 May 18:13:27 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:37 BST 2015)
# 
#   ARM: imx6: add power domains to Vivante GPU nodes
# 
#   v4.1 requires the power domains to be specified for Vivante GPU nodes,
#   otherwise they fail to be detected.
# 
#   Signed-off-by: Russell King
# 
#   9e3ff33dfa5d249b32e52affd9fd671f60d4cb39
#    arch/arm/boot/dts/imx6qdl.dtsi | 2 ++
#    1 file changed, 2 insertions(+)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:53 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:37 BST 2015)
# 
#   ARM: imx6: add Vivante GPU nodes
# 
#   This adds the device nodes for 2D, 3D and VG GPU cores.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   45240e5e1eb3c013082e443c44e147d1edf1dbac
#    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(+)
# 
# Author:    Russell King (Fri 15 May 18:16:07 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:36 BST 2015)
# 
#   staging: etnaviv: add Dove GPU subsystem compatible
# 
#   Signed-off-by: Russell King
# 
#   5a9711a01f6f4744bd59cfa2c15acb93fef25a24
#    drivers/staging/etnaviv/etnaviv_drv.c | 1 +
#    1 file changed, 1 insertion(+)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:52 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:36 BST 2015)
# 
#   staging: etnaviv: some final trivial changes to the module
# 
#   - correct license to the proper GPLv2
#   - add correct author names
#   - remove double MODULE_DEVICE_TABLE
#   - update driver date
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   4185614be52975c4bf1609418eadef5ad4f2f7b9
#    drivers/staging/etnaviv/etnaviv_drv.c | 13 +++++++------
#    1 file changed, 7 insertions(+), 6 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:51 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:35 BST 2015)
# 
#   staging: etnaviv: add proper license header to all files
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   8835a0e00f8c19973ed0d9d2ada47cb3238bdcf9
#    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(-)
# 
# Author:    Christian Gmeiner (Thu  2 Apr 16:30:50 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:35 BST 2015)
# 
#   staging: etnaviv: quiten down kernel log output
# 
#   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
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   62cf5ba4aa645155a6f2a10139373dfafde47d1f
#    drivers/staging/etnaviv/etnaviv_gpu.c | 64 ++++++++++++++++++-----------------
#    1 file changed, 33 insertions(+), 31 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:49 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:35 BST 2015)
# 
#   staging: etnaviv: rename last remaining bits from msm to etnaviv
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   6a42ef0a5cb4b0d417ac622c685a9b124ba1b837
#    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(-)
# 
# Author:    Russell King (Tue 10 Mar 16:30:09 GMT 2015)
# Committer: Russell King (Wed 21 Oct 20:48:35 BST 2015)
# 
#   staging: etnaviv: rename last remaining msm_* symbols
# 
#   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
# 
#   936161803f50b461d673816dcdbac9ab4b010f18
#    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(-)
# 
# Author:    Russell King (Tue 10 Mar 15:18:39 GMT 2015)
# Committer: Russell King (Wed 21 Oct 20:48:34 BST 2015)
# 
#   staging: etnaviv: remove dumb buffer support
# 
#   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
# 
#   9e7ee403fddba07dce09292000ffe8d267fab7f3
#    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(-)
# 
# Author:    Russell King (Fri 15 May 19:22:45 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:34 BST 2015)
# 
#   staging: etnaviv: clean up public API (part 2)
# 
#   This fixes the preceding patch for updates I have in my tree.
# 
#   Signed-off-by: Russell King
# 
#   9bba79343b627d24ac00ea4a363736633c1530e5
#    drivers/staging/etnaviv/etnaviv_gem_submit.c | 7 -------
#    1 file changed, 7 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:46 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:34 BST 2015)
# 
#   staging: etnaviv: clean up public API
# 
#   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
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   097ab9675ca937de1e3f26025ac07016d0aa8a2c
#    include/uapi/drm/etnaviv_drm.h | 22 ++++------------------
#    1 file changed, 4 insertions(+), 18 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:44 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:34 BST 2015)
# 
#   staging: etnaviv: separate GPU pipes from execution state
# 
#   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
#   Signed-off-by: Russell King
# 
#   1bc96a349cf4c77ca7f88ca92fb920231eb44ff7
#    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(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:43 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:34 BST 2015)
# 
#   staging: etnaviv: don't override platform provided IRQ flags
# 
#   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
#   Signed-off-by: Russell King
# 
#   657f7bdb421e5e32bbe413bd7c4fcc67d253dee7
#    drivers/staging/etnaviv/etnaviv_gpu.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:42 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:33 BST 2015)
# 
#   staging: etnaviv: use more natural devicetree abstraction
# 
#   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
#   Signed-off-by: Russell King
# 
#   eb7adc71b555338c9b159b9ba97d0d6008eb565d
#    drivers/staging/etnaviv/etnaviv_drv.c | 32 ++++++++++++++++++--------------
#    1 file changed, 18 insertions(+), 14 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:41 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:33 BST 2015)
# 
#   staging: etnaviv: add flag to force buffer through MMU
# 
#   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
#   Signed-off-by: Russell King
# 
#   a3c9d395a5c02c68b1255414d650892bb41fadeb
#    drivers/staging/etnaviv/etnaviv_mmu.c | 2 +-
#    include/uapi/drm/etnaviv_drm.h        | 2 ++
#    2 files changed, 3 insertions(+), 1 deletion(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:40 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:33 BST 2015)
# 
#   staging: etnaviv: flush MMU when switching context
# 
#   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
#   Signed-off-by: Russell King
# 
#   0ee910ebb4571e884cebbd6e2606c38cfdf87861
#    drivers/staging/etnaviv/etnaviv_gpu.c | 3 +++
#    1 file changed, 3 insertions(+)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:39 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:33 BST 2015)
# 
#   staging: etnaviv: use GPU device to construct MMU
# 
#   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
#   Signed-off-by: Russell King
# 
#   81566ab6207fb9f0ffd74cb1d3502726271959e6
#    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(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:38 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:32 BST 2015)
# 
#   staging: etnaviv: don't pretend to have a single MMU
# 
#   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
#   Signed-off-by: Russell King
# 
#   4d71b9bb6fad1ecc8d035336e15143e38b34d317
#    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(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:37 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:32 BST 2015)
# 
#   staging: etnaviv: allow to map buffer object into multiple address spaces
# 
#   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
#   Signed-off-by: Russell King
# 
#   3fd798f6cd42717b31687af478357242bf2da358
#    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(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:36 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:32 BST 2015)
# 
#   staging: etnaviv: plug in fence waiting in cpu_prepare
# 
#   Allows userspace to properly synchronize with the GPU when accessing
#   buffers.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   af48b7eb10b925b673792da576a0a18ddf49e99d
#    drivers/staging/etnaviv/etnaviv_gem.c | 11 +++++------
#    1 file changed, 5 insertions(+), 6 deletions(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:34 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:32 BST 2015)
# 
#   staging: etnaviv: convert to_etnaviv_bo() to real function
# 
#   This provides a bit more type safety.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   ca49e0b937e06190dd97c7736c4b2fe274ed2d59
#    drivers/staging/etnaviv/etnaviv_gem.h | 7 ++++++-
#    1 file changed, 6 insertions(+), 1 deletion(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:33 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:32 BST 2015)
# 
#   staging: etnaviv: properly flush all TLBs on MMUv1
# 
#   Avoids memory corruptions seen due to stale TLB entries.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   6a267cf97b9186373969a20030b9c641392b6ce7
#    drivers/staging/etnaviv/etnaviv_buffer.c | 5 ++++-
#    1 file changed, 4 insertions(+), 1 deletion(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:31 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:31 BST 2015)
# 
#   staging: etnaviv: reconfigure bus mapping on GC2000
# 
#   This is taken from the Vivante kernel driver and seems to improve
#   stability and performance.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   e29c971478d208917dd76f1a58146cafdf38c17f
#    drivers/staging/etnaviv/etnaviv_gpu.c | 10 ++++++++++
#    1 file changed, 10 insertions(+)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:30 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:31 BST 2015)
# 
#   staging: etnaviv: correct instruction count for GC2000 and GC880
# 
#   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
#   Signed-off-by: Russell King
# 
#   6a360eb918eca62848a6d4a1f054bef65324124b
#    drivers/staging/etnaviv/etnaviv_gpu.c | 7 ++++++-
#    1 file changed, 6 insertions(+), 1 deletion(-)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:30:28 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:31 BST 2015)
# 
#   staging: etnaviv: allow to draw up to 256 rectangles in one draw call
# 
#   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
#   Signed-off-by: Russell King
# 
#   34d28fbb950945676320bd4e5a490493c3326656
#    drivers/staging/etnaviv/etnaviv_cmd_parser.c | 2 ++
#    1 file changed, 2 insertions(+)
# 
# Author:    Lucas Stach (Thu  2 Apr 16:29:06 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:31 BST 2015)
# 
#   staging: etnaviv: import new headers
# 
#   Avoids leaking developer directories into the generated headers and
#   adds some defines needed for proper MMU flush.
# 
#   Signed-off-by: Lucas Stach
#   Signed-off-by: Russell King
# 
#   b96bfd53b11cff62fe2b352696dd5ffc67976659
#    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(-)
# 
# Author:    Russell King (Sat  7 Feb 10:39:45 GMT 2015)
# Committer: Russell King (Wed 21 Oct 20:48:31 BST 2015)
# 
#   staging: etnaviv: avoid holding struct_mutex over dma_alloc_coherent()
# 
#   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
# 
#   8cd62a6d6026df9914c0cca45e2a9e9a5cdd5f4d
#    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(-)
# 
# Author:    Russell King (Mon 12 Jan 19:26:31 GMT 2015)
# Committer: Russell King (Wed 21 Oct 20:48:30 BST 2015)
# 
#   staging: etnaviv: quiten down some further debugging messages
# 
#   Signed-off-by: Russell King
# 
#   bd7d330e2decbc18534bd9e7cd38807c7fc4d818
#    drivers/staging/etnaviv/etnaviv_gpu.c | 6 +++---
#    1 file changed, 3 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Sun 11 Jan 14:59:31 GMT 2015)
# Committer: Russell King (Wed 21 Oct 20:48:29 BST 2015)
# 
#   staging: etnaviv: remove "add child" kernel message
# 
#   Signed-off-by: Russell King
# 
#   7e7abc641e7a95c22afecbfe1b27d1b965d0020f
#    drivers/staging/etnaviv/etnaviv_drv.c | 5 +----
#    1 file changed, 1 insertion(+), 4 deletions(-)
# 
# Author:    Russell King (Wed  5 Nov 10:08:10 GMT 2014)
# Committer: Russell King (Wed 21 Oct 20:48:28 BST 2015)
# 
#   staging: etnaviv: add support for offset physical memory
# 
#   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
# 
#   284db6f2172274723c836908c530f970da2d97d6
#    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(-)
# 
# Author:    Russell King (Thu 30 Oct 16:54:21 GMT 2014)
# Committer: Russell King (Wed 21 Oct 20:48:28 BST 2015)
# 
#   staging: etnaviv: add runtime PM support
# 
#   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
# 
#   8813bcce5dbfb9d71a2324d528853ab4bb94f425
#    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(-)
# 
# Author:    Russell King (Sat 23 May 11:52:42 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:28 BST 2015)
# 
#   staging: etnaviv: provide a helper to load the GPU clock field
# 
#   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
# 
#   d7d7f91a67efa007d2a15b5d172dbc8f3b48407b
#    drivers/staging/etnaviv/etnaviv_gpu.c | 15 +++++++++------
#    1 file changed, 9 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Fri 31 Oct 00:31:39 GMT 2014)
# Committer: Russell King (Wed 21 Oct 20:48:28 BST 2015)
# 
#   staging: etnaviv: add support to shutdown and restore the front end
# 
#   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
# 
#   d66b9079910c689cfcf0acb7d1721a513c948bb4
#    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(-)
# 
# Author:    Russell King (Wed  6 May 10:24:15 BST 2015)
# Committer: Russell King (Wed 21 Oct 20:48:27 BST 2015)
# 
#   staging: etnaviv: restructure iommu handling
# 
#   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
# 
#   64970e65c6cb57037469691f51126d701e78c14a
#    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(-)
# 
# Author:    Russell King (Fri  7 Aug 13:34:26 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:29 BST 2015)
# 
#   drm/armada: move frame wait wakeup into plane work
# 
#   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
# 
#   7cb410cdbdc9b71e2d4f356f2e4cea0b925abb21
#    drivers/gpu/drm/armada/armada_crtc.c | 4 ++--
#    1 file changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Fri  7 Aug 09:33:05 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:28 BST 2015)
# 
#   drm/armada: convert overlay plane vbl worker to a armada plane worker
# 
#   Convert the overlay plane to use the generic armada plane worker
#   infrastructure which is shared with the primary plane.
# 
#   Signed-off-by: Russell King
# 
#   4a8506d2d68724b6d326621118874c07095c6645
#    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(-)
# 
# Author:    Russell King (Thu  6 Aug 16:37:18 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:28 BST 2015)
# 
#   drm/armada: move CRTC flip work to primary plane work
# 
#   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
# 
#   4b5dda82c20c2eee500520010c0558789592d62f
#    drivers/gpu/drm/armada/armada_crtc.c | 102 ++++++++++++++++++++---------------
#    drivers/gpu/drm/armada/armada_crtc.h |  15 ++++--
#    2 files changed, 70 insertions(+), 47 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:25 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:28 BST 2015)
# 
#   drm/armada: move frame wait into armada_frame
# 
#   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
# 
#   5740d27fa5594344ed4d2c18d7ae7bea69002004
#    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(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:25 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:28 BST 2015)
# 
#   drm/armada: move the locking for armada_drm_vbl_event_remove()
# 
#   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
# 
#   6908cf755af74b38d67195ee6607976a55f53d95
#    drivers/gpu/drm/armada/armada_crtc.c    | 2 ++
#    drivers/gpu/drm/armada/armada_overlay.c | 2 --
#    2 files changed, 2 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:25 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:27 BST 2015)
# 
#   drm/armada: move the update of dplane->ctrl0 out of spinlock
# 
#   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
# 
#   5c8752c6506abf29950d32366f826899dc87dde7
#    drivers/gpu/drm/armada/armada_overlay.c | 3 ++-
#    1 file changed, 2 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:25 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:27 BST 2015)
# 
#   drm/armada: move write to dma_ctrl0 to armada_drm_crtc_plane_disable()
# 
#   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
# 
#   9099ea19ca8ad21ab7f2a7abc06e270adeb8b02f
#    drivers/gpu/drm/armada/armada_crtc.c    | 12 ++++++++++--
#    drivers/gpu/drm/armada/armada_overlay.c |  1 -
#    2 files changed, 10 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:25 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:27 BST 2015)
# 
#   drm/armada: provide a common helper to disable a plane
# 
#   Provide a common helper to disable either the overlay or the primary
#   plane.
# 
#   Signed-off-by: Russell King
# 
#   583268035825fc5ef0bbc467631fea0358831cbe
#    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(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:24 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:27 BST 2015)
# 
#   drm/armada: allocate primary plane ourselves
# 
#   Allocate our own primary plane as an armada_plane.
# 
#   Signed-off-by: Russell King
# 
#   de32301b86030b20a51151a12d81fff6429cad0c
#    drivers/gpu/drm/armada/armada_crtc.c | 25 ++++++++++++++++++++-----
#    1 file changed, 20 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:24 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:27 BST 2015)
# 
#   drm/armada: add primary plane creation
# 
#   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
# 
#   1c914cecb5bc4b097df07b799d39abac842ce193
#    drivers/gpu/drm/armada/armada_crtc.c | 34 +++++++++++++++++++++++++++++++++-
#    1 file changed, 33 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:24 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:33:26 BST 2015)
# 
#   drm/armada: introduce generic armada_plane struct
# 
#   Introduce a generic armada_plane struct which will eventually be used
#   for both the primary and overlay planes.
# 
#   Signed-off-by: Russell King
# 
#   561f60bc511f6ec054b566205b5c40ab9558a0ff
#    drivers/gpu/drm/armada/armada_crtc.h    |  5 +++++
#    drivers/gpu/drm/armada/armada_overlay.c | 19 ++++++++++---------
#    2 files changed, 15 insertions(+), 9 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:24 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:32:52 BST 2015)
# 
#   drm/armada: update armada overlay to use drm_universal_plane_init()
# 
#   Use the new drm_universal_plane_init() rather than the legacy
#   drm_plane_init().
# 
#   Signed-off-by: Russell King
# 
#   d563c24514166d01b87cc96f92fe93b635d24c6e
#    drivers/gpu/drm/armada/armada_overlay.c | 8 +++++---
#    1 file changed, 5 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:24 BST 2015)
# Committer: Russell King (Thu  1 Oct 14:32:52 BST 2015)
# 
#   drm/armada: use xchg() to atomically update dplane->old_fb
# 
#   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
# 
#   66377efa3fd468283a092f17692e81c2344b03ed
#    drivers/gpu/drm/armada/armada_overlay.c | 12 ++----------
#    1 file changed, 2 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Fri 14 Aug 11:28:53 BST 2015)
# Committer: Russell King (Tue 29 Sep 19:26:09 BST 2015)
# 
#   drm/i2c: tda998x: clean up after struct tda998x_priv2 removal
# 
#   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
# 
#   9525c4dd923f8ffe38818f86cee523a5f7b19617
#    drivers/gpu/drm/i2c/tda998x_drv.c | 80 +++++++++++----------------------------
#    1 file changed, 22 insertions(+), 58 deletions(-)
# 
# Author:    Russell King (Fri 14 Aug 11:22:50 BST 2015)
# Committer: Russell King (Tue 29 Sep 19:26:05 BST 2015)
# 
#   drm/i2c: tda998x: kill struct tda998x_priv2
# 
#   Kill the redundant tda998x_priv2 structure now that its only member is
#   the struct tda998x_priv.
# 
#   Signed-off-by: Russell King
# 
#   a3584f60f4898c9479931cdca1dc19f758af45fb
#    drivers/gpu/drm/i2c/tda998x_drv.c | 80 +++++++++++++++++++--------------------
#    1 file changed, 38 insertions(+), 42 deletions(-)
# 
# Author:    Russell King (Fri 14 Aug 11:18:28 BST 2015)
# Committer: Russell King (Tue 29 Sep 19:26:00 BST 2015)
# 
#   drm/i2c: tda998x: move connector into struct tda998x_priv
# 
#   Move the DRM connector structure into struct tda998x_priv from the old
#   struct tda998x_priv2.
# 
#   Signed-off-by: Russell King
# 
#   eed64b5963f1496be62ba41f536346b2186727a0
#    drivers/gpu/drm/i2c/tda998x_drv.c | 22 +++++++++++-----------
#    1 file changed, 11 insertions(+), 11 deletions(-)
# 
# Author:    Russell King (Fri 14 Aug 11:17:12 BST 2015)
# Committer: Russell King (Tue 29 Sep 19:25:55 BST 2015)
# 
#   drm/i2c: tda998x: remove encoder pointer
# 
#   Remove the encoder pointer from struct tda998x_priv, moving the encoder
#   itself from struct tda998x_priv2 here.
# 
#   Signed-off-by: Russell King
# 
#   78e401f9891a8ecdb075c0ca46f9f43ce0ed0c4e
#    drivers/gpu/drm/i2c/tda998x_drv.c | 25 ++++++++++++-------------
#    1 file changed, 12 insertions(+), 13 deletions(-)
# 
# Author:    Russell King (Fri 14 Aug 11:13:50 BST 2015)
# Committer: Russell King (Tue 29 Sep 19:25:50 BST 2015)
# 
#   drm/i2c: tda998x: remove DRM slave encoder support
# 
#   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
# 
#   3d58e31888318ebd157ba0680fb24cdcd1bc3d6f
#    drivers/gpu/drm/i2c/tda998x_drv.c | 146 +++-----------------------------------
#    1 file changed, 8 insertions(+), 138 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:24 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:50 BST 2015)
# 
#   drm/armada: factor out retirement of old fb
# 
#   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
# 
#   fecfdb2db8b5fc2e6cb731a714889de5e43c2380
#    drivers/gpu/drm/armada/armada_overlay.c | 37 +++++++++++++++------------------
#    1 file changed, 17 insertions(+), 20 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:11:23 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:50 BST 2015)
# 
#   drm/armada: rename overlay identifiers
# 
#   Include an _ovl infix into the overlay identifiers to separate them from
#   the primary plane.
# 
#   Signed-off-by: Russell King
# 
#   28a2aebed6374c4af5224114e4b4273a3aae649a
#    drivers/gpu/drm/armada/armada_overlay.c | 55 ++++++++++++++++++---------------
#    1 file changed, 30 insertions(+), 25 deletions(-)
# 
# Author:    Russell King (Wed 15 Jul 18:09:38 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:50 BST 2015)
# 
#   drm/armada: redo locking and atomics for armada_drm_crtc_complete_frame_work()
# 
#   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
# 
#   709ffd82fc6ff760dc3a7f71bdf26d78a8e3caf0
#    drivers/gpu/drm/armada/armada_crtc.c | 53 ++++++++++++++++--------------------
#    1 file changed, 23 insertions(+), 30 deletions(-)
# 
# Author:    Russell King (Mon 29 Jun 18:01:38 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:50 BST 2015)
# 
#   drm/armada: disable CRTC clock during DPMS
# 
#   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
# 
#   e0ac5e9b4b14ab4be7fbba48d666fc619342fd88
#    drivers/gpu/drm/armada/armada_crtc.c | 11 +++++++++++
#    1 file changed, 11 insertions(+)
# 
# Author:    Russell King (Mon 29 Jun 17:52:42 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:50 BST 2015)
# 
#   drm/armada: use drm_plane_force_disable() to disable the overlay plane
# 
#   Use drm_plane_force_disable() to disable the overlay plane on a mode_set
#   rather than coding this ourselves.
# 
#   Signed-off-by: Russell King
# 
#   f8e140698234dae3a4ea7b971e7bf63a3e0c987a
#    drivers/gpu/drm/armada/armada_crtc.c | 12 +++---------
#    1 file changed, 3 insertions(+), 9 deletions(-)
# 
# Author:    Russell King (Mon 29 Jun 17:52:16 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:49 BST 2015)
# 
#   drm/armada: move vbl code into armada_crtc
# 
#   Our vblank event code belongs in armada_crtc.c rather than the core of
#   the driver.  Move it there.
# 
#   Signed-off-by: Russell King
# 
#   7c8f7e1abc75b853adf60d8ee0a589e058bcdb6b
#    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(-)
# 
# Author:    Russell King (Sat  6 Jun 21:46:53 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:26:49 BST 2015)
# 
#   drm/armada: remove non-component support
# 
#   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
# 
#   0fb2970b4b6bfc26817a731b8bc29a9bf9177c20
#    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
# 
# Author:    Russell King (Thu  6 Aug 10:52:05 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:19:49 BST 2015)
# 
#   drm/i2c: tda998x: use more HDMI helpers
# 
#   Signed-off-by: Russell King
# 
#   96795df15c89ce30261a31289740b4621bcec0fe
#    drivers/gpu/drm/i2c/tda998x_drv.c | 71 +++++++++++++++++----------------------
#    1 file changed, 31 insertions(+), 40 deletions(-)
# 
# Author:    Russell King (Sat  6 Jun 21:41:10 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:19:49 BST 2015)
# 
#   drm/i2c: tda998x: handle all outstanding interrupts
# 
#   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
# 
#   ec5d3e83d332ac5dfde8e0a1f57393fc91d55030
#    drivers/gpu/drm/i2c/tda998x_drv.c | 16 +++++++++++-----
#    1 file changed, 11 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Sat  6 Jun 21:41:10 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:19:48 BST 2015)
# 
#   drm/i2c: tda998x: convert to u8/u16/u32 types
# 
#   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
# 
#   e66e03abf80f701da60ae085cbb913e67ce6741d
#    drivers/gpu/drm/i2c/tda998x_drv.c | 74 +++++++++++++++++++--------------------
#    1 file changed, 37 insertions(+), 37 deletions(-)
# 
# Author:    Russell King (Sat  6 Jun 21:41:09 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:19:48 BST 2015)
# 
#   drm/i2c: tda998x: re-implement "Fix EDID read timeout on HDMI connect"
# 
#   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
# 
#   0fc6f44d9683c61678da4b0eebc89e8fa624de39
#    drivers/gpu/drm/i2c/tda998x_drv.c | 80 +++++++++++++++++++++++++++++++++------
#    1 file changed, 68 insertions(+), 12 deletions(-)
# 
# Author:    Russell King (Sat  6 Jun 21:41:09 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:19:48 BST 2015)
# 
#   drm/i2c: tda998x: report whether we actually handled the IRQ
# 
#   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
# 
#   f84a97d4804a09240372dc7b195f9d6162152228
#    drivers/gpu/drm/i2c/tda998x_drv.c | 5 ++++-
#    1 file changed, 4 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Sat  6 Jun 21:41:09 BST 2015)
# Committer: Russell King (Tue 15 Sep 16:19:48 BST 2015)
# 
#   drm/i2c: tda998x: remove useless NULL checks
# 
#   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
# 
#   3f3d0d00734de56a0c5996f4e4433046c745592e
#    drivers/gpu/drm/i2c/tda998x_drv.c | 4 +---
#    1 file changed, 1 insertion(+), 3 deletions(-)
# 
# Author:    Russell King (Thu 30 Oct 17:25:23 GMT 2014)
# Committer: Russell King (Fri 11 Sep 10:45:26 BST 2015)
# 
#   staging: etnaviv: separate out etnaviv gpu hardware initialisation
# 
#   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
# 
#   e8a5ebd2fea11cbae8a7bce4b2f725fadabda9e2
#    drivers/staging/etnaviv/etnaviv_gpu.c | 53 ++++++++++++++++++++---------------
#    1 file changed, 31 insertions(+), 22 deletions(-)
# 
# Author:    Russell King (Thu 30 Oct 17:01:06 GMT 2014)
# Committer: Russell King (Fri 11 Sep 10:45:25 BST 2015)
# 
#   staging: etnaviv: move PM calls into bind/unbind callbacks
# 
#   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
# 
#   8c78e7aa6e15b2c34b7eb12081888b8b9b151f87
#    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(-)
# 
# Author:    Russell King (Thu 30 Oct 15:53:14 GMT 2014)
# Committer: Russell King (Fri 11 Sep 10:45:25 BST 2015)
# 
#   staging: etnaviv: move mutex around component_{un,}bind_all()
# 
#   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
# 
#   44b823a5fa3e565de1fbe8a58c626284553ea709
#    drivers/staging/etnaviv/etnaviv_drv.c | 11 ++++++-----
#    1 file changed, 6 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Fri 31 Oct 20:14:18 GMT 2014)
# Committer: Russell King (Fri 11 Sep 10:45:25 BST 2015)
# 
#   staging: etnaviv: NULL out stale pointers at unbind time
# 
#   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
# 
#   faf3729e2f11db88d675da595ec5101cde94fa9b
#    drivers/staging/etnaviv/etnaviv_gpu.c | 10 ++++++++--
#    1 file changed, 8 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Thu 30 Oct 17:06:02 GMT 2014)
# Committer: Russell King (Fri 11 Sep 10:45:25 BST 2015)
# 
#   staging: etnaviv: remove powerrail support
# 
#   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
# 
#   630c85854a69f2de323d5b95364a0d75a463b700
#    drivers/staging/etnaviv/etnaviv_gpu.c | 47 -----------------------------------
#    drivers/staging/etnaviv/etnaviv_gpu.h |  3 ---
#    2 files changed, 50 deletions(-)
# 
# Author:    Russell King (Fri 31 Oct 15:18:15 GMT 2014)
# Committer: Russell King (Fri 11 Sep 10:45:24 BST 2015)
# 
#   staging: etnaviv: fix event allocation failure path
# 
#   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
# 
#   fe64a18da019fc0c6311d902cf37bc641b1832c3
#    drivers/staging/etnaviv/etnaviv_gpu.c | 14 +++++---------
#    1 file changed, 5 insertions(+), 9 deletions(-)
# 
# Author:    Russell King (Sun 27 Apr 13:20:04 BST 2014)
# Committer: Russell King (Fri  4 Sep 19:32:36 BST 2015)
# 
#   ARM: dove: convert legacy dove to PMU support
# 
#   Since Dove has non-DT support, convert the legacy support to use the new
#   PMU driver.
# 
#   Signed-off-by: Russell King
# 
#   802a2053eccc014801963923dffc0c19a2f1f93b
#    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(-)
# 
# Author:    Russell King (Sun 15 Jun 12:27:19 BST 2014)
# Committer: Russell King (Fri  4 Sep 19:30:05 BST 2015)
# 
#   ARM: dt: dove: add GPU power domain description
# 
#   Add the description of the GPU power domain to the PMU DT entry.
# 
#   Signed-off-by: Russell King
# 
#   653411e2827d74ae2a2cc6b6b43bf6013f057a8d
#    arch/arm/boot/dts/dove.dtsi | 7 +++++++
#    1 file changed, 7 insertions(+)
# 
# Author:    Russell King (Sat 14 Feb 13:22:17 GMT 2015)
# Committer: Russell King (Fri  4 Sep 19:30:04 BST 2015)
# 
#   ARM: dt: dove: add video decoder power domain description
# 
#   Add the description of the video decoder power domain to the PMU DT
#   entry.
# 
#   Signed-off-by: Russell King
# 
#   49db200cfeffa9f2f6697e71c147082c1ff41efa
#    arch/arm/boot/dts/dove.dtsi | 6 ++++++
#    1 file changed, 6 insertions(+)
# 
# Author:    Russell King (Sat 14 Feb 13:14:22 GMT 2015)
# Committer: Russell King (Fri  4 Sep 19:30:04 BST 2015)
# 
#   ARM: dt: dove: wire up RTC interrupt
# 
#   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
# 
#   bf0ab494fdc707144963431779531b295fee05f9
#    arch/arm/boot/dts/dove.dtsi | 1 +
#    1 file changed, 1 insertion(+)
# 
# Author:    Russell King (Sun 20 Apr 11:25:43 BST 2014)
# Committer: Russell King (Fri  4 Sep 19:30:04 BST 2015)
# 
#   ARM: dove: create a proper PMU driver for power domains, PMU IRQs and resets
# 
#   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.
# 
#   Signed-off-by: Russell King
# 
#   98d69117961b19a2f707ee99fff6c6eba9d35f56
#    arch/arm/mach-mvebu/Kconfig  |   1 +
#    arch/arm/mach-mvebu/dove.c   |   2 +
#    drivers/soc/Makefile         |   1 +
#    drivers/soc/dove/Makefile    |   1 +
#    drivers/soc/dove/pmu.c       | 412 +++++++++++++++++++++++++++++++++++++++++++
#    include/linux/soc/dove/pmu.h |   6 +
#    6 files changed, 423 insertions(+)
#    create mode 100644 drivers/soc/dove/Makefile
#    create mode 100644 drivers/soc/dove/pmu.c
#    create mode 100644 include/linux/soc/dove/pmu.h
# 
# Author:    Russell King (Sun  7 Jun 09:04:37 BST 2015)
# Committer: Russell King (Fri  4 Sep 19:30:03 BST 2015)
# 
#   ARM: dt: Add PMU node, making PMU child devices childs of this node
# 
#   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
# 
#   8d17ddd66145924961f05a333f59d7a5aa8900f7
#    arch/arm/boot/dts/dove.dtsi | 590 +++++++++++++++++++++++---------------------
#    1 file changed, 304 insertions(+), 286 deletions(-)
# 
# Author:    Russell King (Tue  3 Mar 11:23:40 GMT 2015)
# Committer: Russell King (Fri  4 Sep 19:30:03 BST 2015)
# 
#   dt-bindings: add Marvell PMU documentation
# 
#   Add the required DT binding documentation for the Marvell PMU driver.
# 
#   Signed-off-by: Russell King
# 
#   a68eb2d3f6cf77af968377b5816107f09dac46ec
#    Documentation/devicetree/bindings/soc/dove/pmu.txt | 56 ++++++++++++++++++++++
#    1 file changed, 56 insertions(+)
#    create mode 100644 Documentation/devicetree/bindings/soc/dove/pmu.txt
# 
# Author:    Russell King (Sun 21 Oct 17:38:26 BST 2012)
# Committer: Russell King (Fri  4 Sep 19:25:34 BST 2015)
# 
#   VMeta: add miscdevice VMeta driver
# 
#   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
# 
#   d9efad1f901ce44ffca1ba05e08f6f97aa3fcb8c
#    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
# 
# Author:    Russell King (Sat 14 Jun 23:26:12 BST 2014)
# Committer: Russell King (Fri  4 Sep 18:15:05 BST 2015)
# 
#   ASoC: kirkwood: cobble together audio support when DT is enabled
# 
#   Signed-off-by: Russell King
# 
#   8a6e06adad5e462867910de2e127a7981e5dc483
#    sound/soc/kirkwood/kirkwood-spdif.c | 24 ++++++++++++++++++++++++
#    1 file changed, 24 insertions(+)
# 
# Author:    Russell King (Sun 27 Oct 13:17:04 GMT 2013)
# Committer: Russell King (Fri  4 Sep 18:15:05 BST 2015)
# 
#   ASoC: kirkwood: add DPCM support
# 
#   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
# 
#   642e681b9509f6eab6e420c08d06f8ec4a55e691
#    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(-)
# 
# Author:    Russell King (Fri 26 Jun 15:39:55 BST 2015)
# Committer: Russell King (Fri  4 Sep 18:15:05 BST 2015)
# 
#   ASoC: kirkwood: add spdif driver
# 
#   Add support for SPDIF for Dove Cubox.  Based on a commit by Rabeeh
#   Khoury in the solid-run GIT, original author Sebastian Hesselbrath
#   (sebastian.hesselbrath@googlemail.com).  Subsequent modifications
#   and cleanups by rmk.
# 
#   Signed-off-by: Russell King
# 
#   1021f7f8f39397a18fc1d4b864cbe32b5ba92aa3
#    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
# 
# Author:    Russell King (Sat 14 Jun 16:04:35 BST 2014)
# Committer: Russell King (Fri  4 Sep 18:15:05 BST 2015)
# 
#   ASoC: kirkwood: add SPDIF controls
# 
#   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
# 
#   200db230cbc08e70c835333999270edd0dc105bd
#    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(-)
# 
# Author:    Russell King (Sat 27 Jun 12:23:37 BST 2015)
# Committer: Russell King (Fri  4 Sep 18:15:04 BST 2015)
# 
#   ASoC: kirkwood: shut down external clock when not required
# 
#   Shut down the external clock when the I2S block is not in use.
# 
#   Signed-off-by: Russell King
# 
#   004431a6ac088f9fa2e982f7a088cc3661431e69
#    sound/soc/kirkwood/kirkwood-i2s.c | 29 +++++++++++++++++++++++------
#    sound/soc/kirkwood/kirkwood.h     |  1 +
#    2 files changed, 24 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Thu 23 Oct 15:50:25 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:09 BST 2015)
# 
#   staging: etnaviv: fix busy reporting
# 
#   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
# 
#   a95bf30a05de104277385a2dd41694d1947c69ec
#    drivers/staging/etnaviv/etnaviv_gpu.c | 15 +++++++++++++++
#    drivers/staging/etnaviv/etnaviv_gpu.h |  1 +
#    2 files changed, 16 insertions(+)
# 
# Author:    Russell King (Thu 23 Oct 15:49:26 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:09 BST 2015)
# 
#   staging: etnaviv: allow get_param without auth
# 
#   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.
# 
#   Signed-off-by: Russell King
# 
#   6d7ea3686dcd19c1825ad207b9c288cff436cff3
#    drivers/staging/etnaviv/etnaviv_drv.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Tue 21 Oct 16:46:45 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:09 BST 2015)
# 
#   staging: etnaviv: validate user supplied command stream
# 
#   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
# 
#   effd67ea397a3d14d6d038bba81b1bfbacab5914
#    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
# 
# Author:    Russell King (Mon 20 Oct 10:50:09 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:08 BST 2015)
# 
#   staging: etnaviv: "better" DMA API usage
# 
#   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
# 
#   2e151c14a9553c76ead994465ead0c85f2f9913f
#    drivers/staging/etnaviv/etnaviv_gem.c | 14 ++++++++------
#    1 file changed, 8 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Sun 19 Oct 23:10:20 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:08 BST 2015)
# 
#   staging: etnaviv: clean up etnaviv mmu scatterlist code
# 
#   Signed-off-by: Russell King
# 
#   3ea73cb98fb6b875522f83a25d65ffe58f84d754
#    drivers/staging/etnaviv/etnaviv_mmu.c | 11 ++++-------
#    1 file changed, 4 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Tue 21 Oct 17:00:08 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:08 BST 2015)
# 
#   staging: etnaviv: remove shifting and bitwise or-ing of GPU addresses
# 
#   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
# 
#   0c0b8dc5981fc94456e2364acfe888b852690f84
#    drivers/staging/etnaviv/etnaviv_gem_submit.c | 16 ++++++++--------
#    1 file changed, 8 insertions(+), 8 deletions(-)
# 
# Author:    Russell King (Tue 21 Oct 16:53:53 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:08 BST 2015)
# 
#   staging: etnaviv: remove presumption of BO addresses
# 
#   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
# 
#   7452a03cae84cea9f40a05dd46e08a6e50757b36
#    drivers/staging/etnaviv/etnaviv_gem.h        |  1 -
#    drivers/staging/etnaviv/etnaviv_gem_submit.c | 36 +++++-----------------------
#    2 files changed, 6 insertions(+), 31 deletions(-)
# 
# Author:    Russell King (Fri 22 May 10:37:29 BST 2015)
# Committer: Russell King (Fri  4 Sep 12:43:07 BST 2015)
# 
#   staging: etnaviv: increase page table size to maximum
# 
#   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
# 
#   3d839bf0f4d002030b54001d6454572728622786
#    drivers/staging/etnaviv/etnaviv_iommu.c | 12 +++++-------
#    1 file changed, 5 insertions(+), 7 deletions(-)
# 
# Author:    Russell King (Tue 21 Oct 16:44:52 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:07 BST 2015)
# 
#   staging: etnaviv: iommu: add a poisoned bad page
# 
#   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
# 
#   eb951aa5c226e36d8de49cc5435ddd3928a6dbe8
#    drivers/staging/etnaviv/etnaviv_iommu.c | 29 +++++++++++++++++++++++++++--
#    1 file changed, 27 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Sun 19 Oct 22:43:16 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:07 BST 2015)
# 
#   staging: etnaviv: move scatterlist map/unmap
# 
#   Signed-off-by: Russell King
# 
#   940dbdc18eaa8a8d98d431b552736565b81a91f1
#    drivers/staging/etnaviv/etnaviv_gem.c | 89 ++++++++++++++++++++---------------
#    1 file changed, 52 insertions(+), 37 deletions(-)
# 
# Author:    Russell King (Sun 19 Oct 19:56:46 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:07 BST 2015)
# 
#   staging: etnaviv: fix fence wrapping for gem objects
# 
#   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
# 
#   a20b1dc42fd8bbc07b5c3cb8733eb814f0f263c9
#    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(-)
# 
# Author:    Russell King (Sun 19 Oct 19:26:24 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:06 BST 2015)
# 
#   staging: etnaviv: use definitions for constants
# 
#   Use the etnaviv definitions for feature constants, rather than BIT()s.
# 
#   Signed-off-by: Russell King
# 
#   4055b32ff30b49d2952bff5b0eeec267fbd6b9be
#    drivers/staging/etnaviv/etnaviv_gpu.c | 5 +++--
#    1 file changed, 3 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Sun 19 Oct 09:20:08 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:06 BST 2015)
# 
#   staging: etnaviv: dump mmu allocations
# 
#   Signed-off-by: Russell King
# 
#   c64cd9dd930c213e7f0d339886e7c9299dfa6ba8
#    drivers/staging/etnaviv/etnaviv_drv.c | 19 +++++++++++++++++++
#    1 file changed, 19 insertions(+)
# 
# Author:    Russell King (Sat 27 Sep 17:33:16 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:05 BST 2015)
# 
#   staging: etnaviv: allow non-DT use
# 
#   Signed-off-by: Russell King
# 
#   70b187a1332e7dfd853aeecc355775a798f94d92
#    drivers/staging/etnaviv/etnaviv_drv.c | 50 ++++++++++++++++++++---------------
#    drivers/staging/etnaviv/etnaviv_gpu.c | 18 ++++++++++---
#    2 files changed, 42 insertions(+), 26 deletions(-)
# 
# Author:    Russell King (Sat 18 Oct 17:24:12 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:05 BST 2015)
# 
#   staging: etnaviv: add workarounds for GC320 on iMX6
# 
#   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
# 
#   6a4f63c2ea1851f2d3845f8a445d5addd14f34e9
#    drivers/staging/etnaviv/etnaviv_gpu.c | 24 ++++++++++++++++++++++++
#    1 file changed, 24 insertions(+)
# 
# Author:    Russell King (Sat 18 Oct 17:00:12 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:05 BST 2015)
# 
#   staging: etnaviv: ensure GPU reset times out
# 
#   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
# 
#   7188ce458b616479b79d352dfc675e4f6e2c42b2
#    drivers/staging/etnaviv/etnaviv_gpu.c | 32 +++++++++++++++++++++++++++-----
#    1 file changed, 27 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Sat 18 Oct 16:49:57 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:04 BST 2015)
# 
#   staging: etnaviv: ensure that we retire all pending events
# 
#   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
# 
#   5b4693e7731b6f788bddaecadb17d91b51fe8e18
#    drivers/staging/etnaviv/etnaviv_gpu.c | 28 +++++++++++++++++++++++-----
#    1 file changed, 23 insertions(+), 5 deletions(-)
# 
# Author:    Russell King (Sat 18 Oct 16:45:47 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:04 BST 2015)
# 
#   staging: etnaviv: stop the hangcheck timer mis-firing
# 
#   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
# 
#   3d38a79a984b2f377ea93487ba47c1ed4a4f2f5c
#    drivers/staging/etnaviv/etnaviv_gpu.c | 19 +++++++++++++++----
#    drivers/staging/etnaviv/etnaviv_gpu.h |  1 +
#    2 files changed, 16 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Fri 17 Oct 14:04:05 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:03 BST 2015)
# 
#   staging: etnaviv: move hangcheck disable to separate function
# 
#   Provide a function to safely take down the hangcheck timer and
#   workqueue.
# 
#   Signed-off-by: Russell King
# 
#   3ea0dee38698f0567fa2317691c20d4e37b81405
#    drivers/staging/etnaviv/etnaviv_gpu.c | 10 +++++++---
#    1 file changed, 7 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Fri 17 Oct 13:37:33 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:03 BST 2015)
# 
#   staging: etnaviv: safely take down hangcheck
# 
#   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
# 
#   9cff43a4c58e036ad1315d7d42ae9da970de9d37
#    drivers/staging/etnaviv/etnaviv_gpu.c | 6 ++++--
#    1 file changed, 4 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Fri 17 Oct 12:23:17 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:03 BST 2015)
# 
#   staging: etnaviv: clean up printk()s etc
# 
#   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
# 
#   21545132784d6b9330cb9a50b39dda5fadfd0f0a
#    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(-)
# 
# Author:    Russell King (Fri 17 Oct 12:15:57 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:02 BST 2015)
# 
#   staging: etnaviv: call the DRM device 'drm'
# 
#   Call the DRM device 'drm' in the etnaviv_gpu structure, so that we can
#   add a struct device pointer.
# 
#   Signed-off-by: Russell King
# 
#   beeedfeaf92373d65859415899c959bc0c7ac40d
#    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(-)
# 
# Author:    Russell King (Fri 17 Oct 11:45:39 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:02 BST 2015)
# 
#   staging: etnaviv: add userptr mapping support
# 
#   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
# 
#   b20cef9c4563a5008ad716742e09db33c46cbd79
#    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(-)
# 
# Author:    Russell King (Fri 17 Oct 11:50:51 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:01 BST 2015)
# 
#   staging: etnaviv: move scatterlist creation to etnaviv_gem_get_pages()
# 
#   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
# 
#   81b04c62a4671ebd91a6c83d9869be9994946b24
#    drivers/staging/etnaviv/etnaviv_gem.c | 47 +++++++++++++++++++++--------------
#    1 file changed, 28 insertions(+), 19 deletions(-)
# 
# Author:    Russell King (Fri 17 Oct 11:25:20 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:01 BST 2015)
# 
#   staging: etnaviv: implement MMU reaping
# 
#   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
# 
#   43346702c106add9bb5a5c74cf8a704255f90ae9
#    drivers/staging/etnaviv/etnaviv_mmu.c | 61 ++++++++++++++++++++++++++++++++++-
#    1 file changed, 60 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Fri 17 Oct 11:10:31 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:00 BST 2015)
# 
#   staging: etnaviv: fix etnaviv_iommu_map_gem() return paths
# 
#   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
# 
#   636eaa8b2f927c0bb7224b26dccbdd5f9addff17
#    drivers/staging/etnaviv/etnaviv_mmu.c | 28 +++++++++++++++++-----------
#    1 file changed, 17 insertions(+), 11 deletions(-)
# 
# Author:    Russell King (Fri 17 Oct 10:00:27 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:00 BST 2015)
# 
#   staging: etnaviv: implement round-robin GPU MMU allocation
# 
#   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
# 
#   e4924caade5ff07fe8cf0e244c61bc47daf07f9e
#    drivers/staging/etnaviv/etnaviv_mmu.c | 23 +++++++++++++++++++++--
#    drivers/staging/etnaviv/etnaviv_mmu.h |  1 +
#    2 files changed, 22 insertions(+), 2 deletions(-)
# 
# Author:    Russell King (Sun 28 Sep 23:11:29 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:00 BST 2015)
# 
#   staging: etnaviv: hack: bypass iommu with contiguous buffers
# 
#   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
# 
#   95694c5fdce5b716af5f55b2f4a249127c32fc69
#    drivers/staging/etnaviv/etnaviv_mmu.c | 16 +++++++++++++++-
#    1 file changed, 15 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Thu 16 Oct 22:58:11 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:43:00 BST 2015)
# 
#   staging: etnaviv: move MMU setup and teardown code to etnaviv_mmu.c
# 
#   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
# 
#   7cacd61d7390b1e2b15d9398b4ad6f962cf2477e
#    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(-)
# 
# Author:    Russell King (Thu 16 Oct 17:54:19 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:59 BST 2015)
# 
#   staging: etnaviv: mmuv1: ensure we unmap all entries
# 
#   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
# 
#   38da1e4bd41b8e59b3ce41a99f007d1db997f7d0
#    drivers/staging/etnaviv/etnaviv_iommu.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Thu 16 Oct 17:34:44 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:59 BST 2015)
# 
#   staging: etnaviv: publish and use mmu geometry
# 
#   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
# 
#   8763717fbd9018b2c84eddce0e82e740b4eb79d8
#    drivers/staging/etnaviv/etnaviv_iommu.c | 2 ++
#    drivers/staging/etnaviv/etnaviv_mmu.c   | 4 +++-
#    2 files changed, 5 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Thu 16 Oct 17:28:43 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:59 BST 2015)
# 
#   staging: etnaviv: move GPU memory management into MMU
# 
#   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
# 
#   0756fc06c65137ebf2d7c4e0759be4d1b8f51f28
#    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(-)
# 
# Author:    Russell King (Thu 16 Oct 14:55:49 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:59 BST 2015)
# 
#   staging: etnaviv: add support to insert a MMU flush into GPU stream
# 
#   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
# 
#   3ea1d2b36ded35ed478c83689085a05fd5c9df86
#    drivers/staging/etnaviv/etnaviv_buffer.c | 56 ++++++++++++++++++++++++++------
#    drivers/staging/etnaviv/etnaviv_mmu.h    |  2 ++
#    2 files changed, 48 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Thu 16 Oct 13:28:21 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:58 BST 2015)
# 
#   staging: etnaviv: fix DMA API usage
# 
#   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
# 
#   9521df1aba4734c7ec05a0e0552692ec8fa0c17e
#    drivers/staging/etnaviv/etnaviv_gem.c | 18 +++++++++++++++---
#    1 file changed, 15 insertions(+), 3 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 15:52:19 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:58 BST 2015)
# 
#   staging: etnaviv: add gem get_pages() method
# 
#   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
# 
#   4c7ce5ab4c9876acaa2fd61d198714a88bfec5eb
#    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(-)
# 
# Author:    Russell King (Tue 14 Oct 15:24:35 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:58 BST 2015)
# 
#   staging: etnaviv: clean up etnaviv_gem_{get,put}_pages()
# 
#   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
# 
#   7210c135321b483376a21964057577aed02311fb
#    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(-)
# 
# Author:    Russell King (Tue 14 Oct 10:27:12 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:58 BST 2015)
# 
#   staging: etnaviv: convert get_pages()/put_pages() to take etnaviv_obj
# 
#   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
# 
#   20150deadd4436f26f91fc1a585b6852a283f2f4
#    drivers/staging/etnaviv/etnaviv_gem.c | 33 ++++++++++++++++-----------------
#    1 file changed, 16 insertions(+), 17 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 12:39:22 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:57 BST 2015)
# 
#   staging: etnaviv: clean up prime import
# 
#   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
# 
#   e397764b581e42d2d1ea0f5baf17e0f47388863f
#    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(-)
# 
# Author:    Russell King (Tue 14 Oct 12:29:45 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:57 BST 2015)
# 
#   staging: etnaviv: move msm_gem_import() etc to etnaviv_gem_prime.c
# 
#   Move the prime import code out into etnaviv_gem_prime.c, which keeps
#   all this functionality together.
# 
#   Signed-off-by: Russell King
# 
#   e92cdc5ed36de6ccf0a4995714a4a19aa27440d9
#    drivers/staging/etnaviv/etnaviv_gem.c       | 54 -----------------------------
#    drivers/staging/etnaviv/etnaviv_gem_prime.c | 54 +++++++++++++++++++++++++++++
#    2 files changed, 54 insertions(+), 54 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 12:26:41 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:57 BST 2015)
# 
#   staging: etnaviv: provide etnaviv_gem_new_private()
# 
#   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
# 
#   d0ce7f0341e03de48969dfdfbd0d09eaef7f1fe4
#    drivers/staging/etnaviv/etnaviv_gem.c | 32 ++++++++++++++++++++++----------
#    drivers/staging/etnaviv/etnaviv_gem.h |  3 +++
#    2 files changed, 25 insertions(+), 10 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 11:20:39 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:57 BST 2015)
# 
#   staging: etnaviv: clean up etnaviv_gem_free_object()
# 
#   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
# 
#   f8a105983dcb095f85044ff4acea3645c36e2656
#    drivers/staging/etnaviv/etnaviv_gem.c | 19 +++----------------
#    1 file changed, 3 insertions(+), 16 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 11:07:18 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:56 BST 2015)
# 
#   staging: etnaviv: ensure cleanup of reservation object
# 
#   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.
# 
#   Signed-off-by: Russell King
# 
#   244e1a5ed5947071e54a6a931467c908771a8254
#    drivers/staging/etnaviv/etnaviv_gem.c | 6 ++----
#    1 file changed, 2 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 11:05:02 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:56 BST 2015)
# 
#   staging: etnaviv: move drm_gem_object_release()
# 
#   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
# 
#   8d87dd35c6ef794ba001b78c0ae7a68f1f8e3f70
#    drivers/staging/etnaviv/etnaviv_gem.c | 6 ++----
#    1 file changed, 2 insertions(+), 4 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 11:18:20 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:56 BST 2015)
# 
#   staging: etnaviv: convert cmdbuf release to use etnaviv_gem_ops
# 
#   Convert the command buffer release handling to use the etnaviv_gem_ops
#   release method.
# 
#   Signed-off-by: Russell King
# 
#   8ff086e8a5e330697b89bd1d1bd2681b7ee51e3e
#    drivers/staging/etnaviv/etnaviv_gem.c | 12 +++++++++++-
#    1 file changed, 11 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Tue 14 Oct 11:15:16 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:55 BST 2015)
# 
#   staging: etnaviv: convert shmem release to use etnaviv_gem_ops
# 
#   Convert the shmem object release to use the etnaviv_gem_ops release
#   method.
# 
#   Signed-off-by: Russell King
# 
#   38c5df3ade56ccd06df7200e59e759af8e96065a
#    drivers/staging/etnaviv/etnaviv_gem.c | 25 ++++++++++++++++---------
#    1 file changed, 16 insertions(+), 9 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 10:37:12 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:55 BST 2015)
# 
#   staging: etnaviv: convert prime import to use etnaviv_gem_ops
# 
#   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
# 
#   7094a11a35e8a6548bdadac53fe989b4b7f0d401
#    drivers/staging/etnaviv/etnaviv_gem.c | 32 ++++++++++++++++++++------------
#    1 file changed, 20 insertions(+), 12 deletions(-)
# 
# Author:    Russell King (Tue 14 Oct 10:32:37 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:55 BST 2015)
# 
#   staging: etnaviv: add gem operations structure to etnaviv objects
# 
#   Signed-off-by: Russell King
# 
#   edd7f36c9cbeaeb6810aacec5b035271c7220825
#    drivers/staging/etnaviv/etnaviv_gem.c | 4 +++-
#    drivers/staging/etnaviv/etnaviv_gem.h | 7 +++++++
#    2 files changed, 10 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Tue 14 Oct 16:03:38 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:55 BST 2015)
# 
#   staging: etnaviv: fix get_pages() failure path
# 
#   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
# 
#   ba3fe5dc65eaa1edf19e041a43846793965c5c75
#    drivers/staging/etnaviv/etnaviv_gem.c | 1 +
#    1 file changed, 1 insertion(+)
# 
# Author:    Russell King (Mon 29 Sep 11:01:18 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:54 BST 2015)
# 
#   staging: etnaviv: fix checkpatch warnings
# 
#   Fix many checkpatch warnings.
# 
#   Signed-off-by: Russell King
# 
#   c95db0a0170a6feb2c9d333042eb3d9cfedb0023
#    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(-)
# 
# Author:    Russell King (Mon 29 Sep 10:17:36 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:54 BST 2015)
# 
#   staging: etnaviv: fix checkpatch errors
# 
#   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
# 
#   358cdea337a292b8f9c1eb71d213ec412d2131aa
#    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(-)
# 
# Author:    Russell King (Sun 28 Sep 19:03:51 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:54 BST 2015)
# 
#   staging: etnaviv: ensure that ring buffer wraps
# 
#   Ensure that the ring buffer wraps when we fill the buffer.
# 
#   Signed-off-by: Russell King
# 
#   cacefbca51895ba39b4fa66aefe437855a0c7fd4
#    drivers/staging/etnaviv/etnaviv_buffer.c | 8 +++++++-
#    1 file changed, 7 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Sun 28 Sep 18:03:39 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:54 BST 2015)
# 
#   staging: etnaviv: package up events into etnaviv_event struct
# 
#   Combine the event data into an array of etnaviv_event structures,
#   rather than individual arrays.
# 
#   Signed-off-by: Russell King
# 
#   7ae65668d06ad289073b0d025173d98a91f383f7
#    drivers/staging/etnaviv/etnaviv_gpu.c | 18 +++++++++---------
#    drivers/staging/etnaviv/etnaviv_gpu.h |  8 ++++++--
#    2 files changed, 15 insertions(+), 11 deletions(-)
# 
# Author:    Russell King (Sun 28 Sep 16:46:39 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:53 BST 2015)
# 
#   staging: etnaviv: fix multiple command buffer submission in etnaviv_buffer_queue()
# 
#   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
# 
#   e6fe9c86ede12b7e3a6ac453c6aa8148461706d8
#    drivers/staging/etnaviv/etnaviv_buffer.c | 45 +++++++++++++++++---------------
#    1 file changed, 24 insertions(+), 21 deletions(-)
# 
# Author:    Russell King (Sun 28 Sep 15:48:49 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:53 BST 2015)
# 
#   staging: etnaviv: quieten down submission debugging
# 
#   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
# 
#   626fb1d5e77627093b2a1aca49764279fb3fe557
#    drivers/staging/etnaviv/etnaviv_buffer.c | 37 +++++++++++++++++---------------
#    1 file changed, 20 insertions(+), 17 deletions(-)
# 
# Author:    Russell King (Sun 28 Sep 15:45:42 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:53 BST 2015)
# 
#   staging: etnaviv: add an offset for buffer dumping
# 
#   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
# 
#   a7ee9565deccabcd26ad5fdec1e8e8c374e80066
#    drivers/staging/etnaviv/etnaviv_buffer.c | 13 +++++++------
#    1 file changed, 7 insertions(+), 6 deletions(-)
# 
# Author:    Russell King (Sat 27 Sep 23:32:25 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:53 BST 2015)
# 
#   staging: etnaviv: respect the submission command offset
# 
#   Signed-off-by: Russell King
# 
#   8fa7d4ee42864a8f59bfa6d02584c3dab692a61e
#    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(-)
# 
# Author:    Christian Gmeiner (Sat 27 Sep 17:01:30 BST 2014)
# Committer: Russell King (Fri  4 Sep 12:42:53 BST 2015)
# 
#   staging: etnaviv: add drm driver
# 
#   This is a consolidation by Russell King of Christian's drm work.
# 
#   Signed-off-by: Christian Gmeiner
#   Signed-off-by: Russell King
# 
#   3b2b886ccfa7284c7b8042654d321ce1a37d731e
#    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
# 
# Author:    Lucas Stach (Thu  2 Apr 16:29:04 BST 2015)
# Committer: Russell King (Fri  4 Sep 12:37:32 BST 2015)
# 
#   staging: etnaviv: add devicetree bindings
# 
#   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
#   Signed-off-by: Russell King
# 
#   44f6d5666ee3211e1761fb4ee81dbca3ed140491
#    .../bindings/drm/etnaviv/etnaviv-drm.txt           | 44 ++++++++++++++++++++++
#    1 file changed, 44 insertions(+)
#    create mode 100644 Documentation/devicetree/bindings/drm/etnaviv/etnaviv-drm.txt
# 
# Author:    Philipp Zabel (Thu  2 Apr 16:29:03 BST 2015)
# Committer: Russell King (Fri  4 Sep 12:37:32 BST 2015)
# 
#   of: Add vendor prefix for Vivante Corporation
# 
#   Trivial patch to add Vivante Corporation to the list of
#   devicetree vendor prefixes.
# 
#   Signed-off-by: Philipp Zabel
#   Signed-off-by: Russell King
# 
#   9e4093ec938e998a2cc7cfbb3e6c37df7e919100
#    Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
#    1 file changed, 1 insertion(+)
# 
# Author:    Russell King (Mon 22 Jun 12:33:02 BST 2015)
# Committer: Russell King (Mon 22 Jun 16:48:05 BST 2015)
# 
#   misc: bmm: update for v4.1 dma_buf API changes
# 
#   Signed-off-by: Russell King
# 
#   d21c44980b87c3418acd3a2ec846ffb43a174bc8
#    drivers/misc/bmm_dmabuf.c | 9 ++++++++-
#    1 file changed, 8 insertions(+), 1 deletion(-)
# 
# Author:    Russell King (Mon 22 Jun 16:46:58 BST 2015)
# Committer: Russell King (Mon 22 Jun 16:46:58 BST 2015)
#   mergetag object b953c0d234bc72e8489d3bf51a276c5c4ec85345
#    type commit
#    tag v4.1
#    tagger Linus Torvalds 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
# 
# Author:    Russell King (Fri 17 Oct 17:24:02 BST 2014)
# Committer: Russell King (Fri 17 Oct 17:24:02 BST 2014)
# 
#   misc: bmm: update for v3.17 dma_buf API changes
# 
#   Add a NULL resv argument to the dma_buf_export() to avoid build errors
#   with v3.17 kernels.
# 
#   Signed-off-by: Russell King
# 
#   0f4c7f99be9bbe579ed2f9b0a0f8344ed034478f
#    drivers/misc/bmm_dmabuf.c | 2 +-
#    1 file changed, 1 insertion(+), 1 deletion(-)
# 
# Author:    Russell King (Fri 17 Oct 17:23:12 BST 2014)
# Committer: Russell King (Fri 17 Oct 17:23:12 BST 2014)
#   mergetag object bfe01a5ba2490f299e1d2d5508cbbbadd897bbe9
#    type commit
#    tag v3.17
#    tagger Linus Torvalds 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
# 
# Author:    Russell King (Sun  8 Dec 23:47:08 GMT 2013)
# Committer: Russell King (Mon  9 Dec 00:22:41 GMT 2013)
# 
#   misc: add bmm_dmabuf allocator
# 
#   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
# 
#   9e7a22d3e2d7c9215ba0327a75867d1738b3f12a
#    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/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";
+};
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/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>;
+			};
+		};
+	};
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.
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/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 {
diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
index 38b1f7e6004e..918672af6738 100644
--- a/arch/arm/boot/dts/dove.dtsi
+++ b/arch/arm/boot/dts/dove.dtsi
@@ -419,293 +419,325 @@
 				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 {
+					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>;
+					};
+				};
+
+				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>;
+					interrupts = <5>;
+				};
 			};
 
 			gconf: global-config@e802c {
@@ -735,6 +767,13 @@
 				interrupts = <47>;
 				status = "disabled";
 			};
+
+			vmeta: video-decoder@c00000 {
+				compatible = "marvell,vmeta";
+				reg = <0xc00000 0x280000>;
+				interrupts = <51>;
+				power-domains = <&vpu_domain>;
+			};
 		};
 	};
 };
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..9b24bfe191a8 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -119,6 +119,28 @@
 			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";
+			power-domains = <&gpc 1>;
+		};
+
+		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";
+			power-domains = <&gpc 1>;
+		};
+
 		hdmi: hdmi@0120000 {
 			#address-cells = <1>;
 			#size-cells = <0>;
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/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 97473168d6b6..c86a5a0aefac 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -96,6 +96,7 @@ config MACH_DOVE
 	select MACH_MVEBU_ANY
 	select ORION_IRQCHIP
 	select ORION_TIMER
+	select PM_GENERIC_DOMAINS if PM
 	select PINCTRL_DOVE
 	help
 	  Say 'Y' here if you want your kernel to support the
diff --git a/arch/arm/mach-mvebu/dove.c b/arch/arm/mach-mvebu/dove.c
index 5a1741500a30..1aebb82e3d7b 100644
--- a/arch/arm/mach-mvebu/dove.c
+++ b/arch/arm/mach-mvebu/dove.c
@@ -12,6 +12,7 @@
 #include <linux/mbus.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/soc/dove/pmu.h>
 #include <asm/hardware/cache-tauros2.h>
 #include <asm/mach/arch.h>
 #include "common.h"
@@ -24,6 +25,7 @@ static void __init dove_init(void)
 	tauros2_init(0);
 #endif
 	BUG_ON(mvebu_mbus_dt_init(false));
+	dove_init_pmu();
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
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..1b52ae594006 100644
--- a/drivers/gpu/drm/armada/Makefile
+++ b/drivers/gpu/drm/armada/Makefile
@@ -1,7 +1,7 @@
 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-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 01ffe9bffe38..00ada2eaaac9 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;
@@ -33,6 +34,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:
@@ -173,49 +191,82 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
 	return i;
 }
 
-static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
-	struct armada_frame_work *work)
+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);
+	}
+
+	wake_up(&plane->frame_wait);
+}
+
+int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
+	struct armada_plane *plane, struct armada_plane_work *work)
 {
-	struct drm_device *dev = dcrtc->crtc.dev;
-	unsigned long flags;
 	int ret;
 
-	ret = drm_vblank_get(dev, dcrtc->num);
+	ret = drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
 	if (ret) {
 		DRM_ERROR("failed to acquire vblank counter\n");
 		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);
-
+	ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0;
 	if (ret)
-		drm_vblank_put(dev, dcrtc->num);
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
 
 	return ret;
 }
 
-static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
+int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout)
 {
-	struct drm_device *dev = dcrtc->crtc.dev;
-	struct armada_frame_work *work = dcrtc->frame_work;
+	return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
+}
+
+struct armada_plane_work *armada_drm_plane_work_cancel(
+	struct armada_crtc *dcrtc, struct armada_plane *plane)
+{
+	struct armada_plane_work *work = xchg(&plane->work, NULL);
+
+	if (work)
+		drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+
+	return work;
+}
 
-	dcrtc->frame_work = NULL;
+static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
+	struct armada_frame_work *work)
+{
+	struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
 
-	armada_drm_crtc_update_regs(dcrtc, work->regs);
+	return armada_drm_plane_work_queue(dcrtc, plane, &work->work);
+}
+
+static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc,
+	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;
 
-	if (work->event)
-		drm_send_vblank_event(dev, dcrtc->num, work->event);
+	spin_lock_irqsave(&dcrtc->irq_lock, flags);
+	armada_drm_crtc_update_regs(dcrtc, fwork->regs);
+	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
 
-	drm_vblank_put(dev, dcrtc->num);
+	if (fwork->event) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+		drm_send_vblank_event(dev, dcrtc->num, fwork->event);
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
 
 	/* 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,
@@ -235,6 +286,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);
@@ -255,19 +307,14 @@ 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_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. */
-	spin_lock_irq(&dev->event_lock);
-	if (dcrtc->frame_work)
-		armada_drm_crtc_complete_frame_work(dcrtc);
-	spin_unlock_irq(&dev->event_lock);
+	armada_drm_plane_work_run(dcrtc, plane);
 }
 
 void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
@@ -287,7 +334,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
@@ -310,17 +361,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 */
@@ -356,8 +401,8 @@ 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;
+	struct drm_plane *ovl_plane;
 
 	if (stat & DMA_FF_UNDERFLOW)
 		DRM_ERROR("video underflow on crtc %u\n", dcrtc->num);
@@ -368,11 +413,10 @@ 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);
+	ovl_plane = dcrtc->plane;
+	if (ovl_plane) {
+		struct armada_plane *plane = drm_to_armada_plane(ovl_plane);
+		armada_drm_plane_work_run(dcrtc, plane);
 	}
 
 	if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
@@ -404,14 +448,8 @@ 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;
-
-		spin_lock(&dev->event_lock);
-		if (dcrtc->frame_work)
-			armada_drm_crtc_complete_frame_work(dcrtc);
-		spin_unlock(&dev->event_lock);
-
-		wake_up(&dcrtc->frame_wait);
+		struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
+		armada_drm_plane_work_run(dcrtc, plane);
 	}
 }
 
@@ -527,7 +565,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);
+	armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
+				   MAX_SCHEDULE_TIMEOUT);
 
 	drm_crtc_vblank_off(crtc);
 
@@ -537,6 +576,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);
 
@@ -637,7 +683,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);
+	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);
@@ -651,18 +698,47 @@ 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, dma_ctrl0_mask;
+
+	/*
+	 * 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;
+		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);
+}
+
 /* 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 = {
@@ -920,8 +996,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;
 
@@ -933,6 +1007,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;
 
@@ -966,12 +1041,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)) {
-		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);
-	}
+	if (dpms_blanked(dcrtc->dpms))
+		armada_drm_plane_work_run(dcrtc, drm_to_armada_plane(dcrtc->crtc.primary));
 
 	return 0;
 }
@@ -1012,6 +1083,19 @@ 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,
+};
+
+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" },
@@ -1044,12 +1128,13 @@ 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)
 {
 	struct armada_private *priv = drm->dev_private;
 	struct armada_crtc *dcrtc;
+	struct armada_plane *primary;
 	void __iomem *base;
 	int ret;
 
@@ -1076,12 +1161,11 @@ 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);
 	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);
@@ -1118,7 +1202,32 @@ 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 = kzalloc(sizeof(*primary), GFP_KERNEL);
+	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,
+				       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;
+
 	drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
 
 	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
@@ -1127,6 +1236,10 @@ 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->base.funcs->destroy(&primary->base);
+	return ret;
 }
 
 static int
@@ -1213,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_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 98102a5a9af5..04fdd22d483b 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -31,9 +31,30 @@ 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_plane_work *armada_drm_plane_work_cancel(
+	struct armada_crtc *dcrtc, struct armada_plane *plane);
+
 struct armada_crtc {
 	struct drm_crtc		crtc;
 	const struct armada_variant *variant;
@@ -66,25 +87,20 @@ 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;
 	uint32_t		irq_ena;
-	struct list_head	vbl_list;
 };
 #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);
 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_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index 5f6aef0dca59..679c5f52b51b 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;
 
@@ -67,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_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 225034b74cda..3f1396e673dd 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;
-
-	memset(res, 0, sizeof(res));
+	int ret, n;
 
-	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);
@@ -230,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)
 {
@@ -435,37 +330,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_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 */
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_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index e939faba7fcc..5c22b380f8f3 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,26 +29,25 @@ struct armada_plane_properties {
 	uint32_t colorkey_mode;
 };
 
-struct armada_plane {
-	struct drm_plane base;
-	spinlock_t lock;
+struct armada_ovl_plane {
+	struct armada_plane base;
 	struct drm_framebuffer *old_fb;
 	uint32_t src_hw;
 	uint32_t dst_hw;
 	uint32_t dst_yx;
 	uint32_t ctrl0;
 	struct {
-		struct armada_vbl_event update;
+		struct armada_plane_work work;
 		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.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);
@@ -71,32 +70,34 @@ armada_ovl_update_attr(struct armada_plane_properties *prop,
 	spin_unlock_irq(&dcrtc->irq_lock);
 }
 
-/* === Plane support === */
-static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
+static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
+	struct drm_framebuffer *fb)
 {
-	struct armada_plane *dplane = data;
-	struct drm_framebuffer *fb;
+	struct drm_framebuffer *old_fb;
 
-	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
+	old_fb = xchg(&dplane->old_fb, fb);
 
-	spin_lock(&dplane->lock);
-	fb = dplane->old_fb;
-	dplane->old_fb = NULL;
-	spin_unlock(&dplane->lock);
+	if (old_fb)
+		armada_drm_queue_unref_work(dplane->base.base.dev, old_fb);
+}
 
-	if (fb)
-		armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
+/* === Plane support === */
+static void armada_ovl_plane_work(struct armada_crtc *dcrtc,
+	struct armada_plane *plane, struct armada_plane_work *work)
+{
+	struct armada_ovl_plane *dplane = container_of(plane, struct armada_ovl_plane, base);
 
-	wake_up(&dplane->vbl.wait);
+	armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
+	armada_ovl_retire_fb(dplane, NULL);
 }
 
 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,
@@ -160,9 +161,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 			       dcrtc->base + LCD_SPU_SRAM_PARA1);
 	}
 
-	wait_event_timeout(dplane->vbl.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);
@@ -175,17 +175,8 @@ armada_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;
@@ -262,60 +253,50 @@ armada_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;
 }
 
-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;
 
-	if (!dplane->base.crtc)
+	if (!dplane->base.base.crtc)
 		return 0;
 
-	dcrtc = drm_to_armada_crtc(dplane->base.crtc);
-	dcrtc->plane = NULL;
-
-	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);
+	dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
 
-	/* Power down the Y/U/V FIFOs */
-	armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
-		       dcrtc->base + LCD_SPU_SRAM_PARA1);
+	armada_drm_plane_work_cancel(dcrtc, &dplane->base);
+	armada_drm_crtc_plane_disable(dcrtc, plane);
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
+	dcrtc->plane = NULL;
+	dplane->ctrl0 = 0;
 
-	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);
 
 	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) {
@@ -372,21 +353,21 @@ static int armada_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;
 }
 
-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 +437,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);
@@ -467,13 +448,23 @@ 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_plane_vbl,
-				  dplane);
+	ret = armada_drm_plane_init(&dplane->base);
+	if (ret) {
+		kfree(dplane);
+		return ret;
+	}
+
+	dplane->vbl.work.fn = armada_ovl_plane_work;
 
-	drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
-		       armada_formats, ARRAY_SIZE(armada_formats), false);
+	ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
+				       &armada_ovl_plane_funcs,
+				       armada_ovl_formats,
+				       ARRAY_SIZE(armada_ovl_formats),
+				       DRM_PLANE_TYPE_OVERLAY);
+	if (ret) {
+		kfree(dplane);
+		return ret;
+	}
 
 	dplane->prop.colorkey_yr = 0xfefefe00;
 	dplane->prop.colorkey_ug = 0x01010100;
@@ -483,7 +474,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,
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
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) &&
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 424228be79ae..49ca9eda4957 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>
@@ -34,9 +33,8 @@ 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;
+	u16 rev;
+	u8 current_page;
 	int dpms;
 	bool is_hdmi_sink;
 	u8 vip_cntrl_0;
@@ -46,10 +44,21 @@ 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;
+	struct drm_connector connector;
 };
 
-#define to_tda998x_priv(x)  ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
+#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/
@@ -326,6 +335,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)
@@ -345,10 +356,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));
@@ -356,11 +367,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));
@@ -379,11 +390,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));
@@ -399,10 +410,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);
@@ -428,10 +439,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);
@@ -450,9 +461,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));
@@ -462,10 +473,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);
@@ -481,10 +492,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);
@@ -500,7 +511,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;
 
@@ -510,7 +521,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;
 
@@ -551,15 +562,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_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.
+ */
+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 && priv->encoder->dev)
-		drm_kms_helper_hotplug_event(priv->encoder->dev);
+	if (dev)
+		drm_kms_helper_hotplug_event(dev);
 }
 
 /*
@@ -569,9 +615,8 @@ 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;
 
-	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);
@@ -581,75 +626,76 @@ 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 (cec & CEC_RXSHPDINT_HPD) {
+		if (lvl & CEC_RXSHPDLEV_HPD)
+			tda998x_edid_delay_start(priv);
+		else
+			schedule_work(&priv->detect_work);
+
+		handled = true;
+	}
+
 	if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
 		priv->wq_edid_wait = 0;
 		wake_up(&priv->wq_edid);
-	} else if (cec != 0) {			/* HPD change */
-		schedule_delayed_work(&priv->dwork, HZ/10);
+		handled = true;
 	}
-	return IRQ_HANDLED;
-}
 
-static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
-{
-	int sum = 0;
-
-	while (bytes--)
-		sum -= *buf++;
-	return sum;
+	return IRQ_RETVAL(handled);
 }
 
-#define HB(x) (x)
-#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,
+		 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;
+	union hdmi_infoframe frame;
 
-	drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
-
-	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)
@@ -667,8 +713,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);
@@ -776,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;
@@ -827,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;
@@ -840,18 +888,19 @@ 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)
 {
-	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;
+	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;
+	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
@@ -1031,9 +1080,10 @@ 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)
 {
-	uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV);
+	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 :
 			connector_status_disconnected;
@@ -1042,7 +1092,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;
@@ -1095,13 +1145,20 @@ 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;
 
+	/*
+	 * 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);
 
@@ -1133,101 +1190,21 @@ 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 */
 	cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
 	reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
-	if (priv->hdmi->irq) {
-		free_irq(priv->hdmi->irq, priv);
-		cancel_delayed_work_sync(&priv->dwork);
-	}
-
-	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));
-}
+	if (priv->hdmi->irq)
+		free_irq(priv->hdmi->irq, priv);
 
-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);
-}
+	del_timer_sync(&priv->edid_delay_timer);
+	cancel_work_sync(&priv->detect_work);
 
-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;
+	i2c_unregister_device(priv->cec);
 }
 
-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)
@@ -1252,6 +1229,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,
@@ -1310,7 +1291,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);
@@ -1359,84 +1339,31 @@ 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;
-	struct drm_connector connector;
-};
-
-#define conn_to_tda998x_priv2(x) \
-	container_of(x, struct tda998x_priv2, connector);
-
-#define enc_to_tda998x_priv2(x) \
-	container_of(x, struct tda998x_priv2, encoder);
-
-static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode)
-{
-	struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
-
-	tda998x_encoder_dpms(&priv->base, 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_priv2 *priv = enc_to_tda998x_priv2(encoder);
-
-	tda998x_encoder_mode_set(&priv->base, 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)
 {
-	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);
 }
 
@@ -1444,25 +1371,10 @@ 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_priv2 *priv = conn_to_tda998x_priv2(connector);
-
-	return tda998x_encoder_get_modes(&priv->base, 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);
-
-	return tda998x_encoder_mode_valid(&priv->base, 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->encoder;
 }
@@ -1474,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_priv2 *priv = conn_to_tda998x_priv2(connector);
-
-	return tda998x_encoder_detect(&priv->base);
-}
-
 static void tda998x_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
@@ -1495,13 +1399,47 @@ 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;
 	struct i2c_client *client = to_i2c_client(dev);
 	struct drm_device *drm = data;
-	struct tda998x_priv2 *priv;
-	uint32_t crtcs = 0;
+	struct tda998x_priv *priv;
+	u32 crtcs = 0;
 	int ret;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -1519,18 +1457,26 @@ 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.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->base);
+	ret = tda998x_create(client, priv);
 	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->base, params);
+		tda998x_encoder_set_config(priv, params);
 
-	tda998x_encoder_set_polling(&priv->base, &priv->connector);
+	tda998x_encoder_set_polling(priv, &priv->connector);
 
 	drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
 	ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
@@ -1560,18 +1506,18 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
 err_connector:
 	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->connector);
 	drm_encoder_cleanup(&priv->encoder);
-	tda998x_destroy(&priv->base);
+	tda998x_destroy(priv);
 }
 
 static const struct component_ops tda998x_ops = {
@@ -1605,38 +1551,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 <robdclark@gmail.com");
 MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder");
 MODULE_LICENSE("GPL");
-
-module_init(tda998x_init);
-module_exit(tda998x_exit);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 42c38525904b..4238c238ba37 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -428,6 +428,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
@@ -506,6 +514,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..9297a7911ad2 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_AD525X_DPOT_SPI)	+= ad525x_dpot-spi.o
 obj-$(CONFIG_INTEL_MID_PTI)	+= pti.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
@@ -46,6 +47,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/bmm_dmabuf.c b/drivers/misc/bmm_dmabuf.c
new file mode 100644
index 000000000000..b441bdfe7027
--- /dev/null
+++ b/drivers/misc/bmm_dmabuf.c
@@ -0,0 +1,371 @@
+/*
+ *  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)
+{
+	DEFINE_DMA_BUF_EXPORT_INFO(export);
+	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;
+
+	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);
+	}
+
+	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
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/drivers/soc/Makefile b/drivers/soc/Makefile
index 7dc7c0d8a2c1..c99192f30679 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -2,6 +2,8 @@
 # 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/
 obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
diff --git a/drivers/soc/dove/Makefile b/drivers/soc/dove/Makefile
new file mode 100644
index 000000000000..2db8e65513a3
--- /dev/null
+++ b/drivers/soc/dove/Makefile
@@ -0,0 +1 @@
+obj-y		+= pmu.o
diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c
new file mode 100644
index 000000000000..7072fdbe8f8b
--- /dev/null
+++ b/drivers/soc/dove/pmu.c
@@ -0,0 +1,512 @@
+/*
+ * Marvell Dove PMU support
+ */
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/soc/dove/pmu.h>
+#include <linux/spinlock.h>
+
+#define NR_PMU_IRQS		7
+
+#define PMC_SW_RST		0x30
+#define PMC_IRQ_CAUSE		0x50
+#define PMC_IRQ_MASK		0x54
+
+#define PMU_PWR			0x10
+#define PMU_ISO			0x58
+
+struct pmu_data {
+	spinlock_t lock;
+	struct device_node *of_node;
+	void __iomem *pmc_base;
+	void __iomem *pmu_base;
+	struct irq_chip_generic *irq_gc;
+	struct irq_domain *irq_domain;
+#ifdef CONFIG_RESET_CONTROLLER
+	struct reset_controller_dev reset;
+#endif
+};
+
+/*
+ * The PMU contains a register to reset various subsystems within the
+ * SoC.  Export this as a reset controller.
+ */
+#ifdef CONFIG_RESET_CONTROLLER
+#define rcdev_to_pmu(rcdev) container_of(rcdev, struct pmu_data, reset)
+
+static int pmu_reset_reset(struct reset_controller_dev *rc, unsigned long id)
+{
+	struct pmu_data *pmu = rcdev_to_pmu(rc);
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+	val = readl_relaxed(pmu->pmc_base + PMC_SW_RST);
+	writel_relaxed(val & ~BIT(id), pmu->pmc_base + PMC_SW_RST);
+	writel_relaxed(val | BIT(id), pmu->pmc_base + PMC_SW_RST);
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	return 0;
+}
+
+static int pmu_reset_assert(struct reset_controller_dev *rc, unsigned long id)
+{
+	struct pmu_data *pmu = rcdev_to_pmu(rc);
+	unsigned long flags;
+	u32 val = ~BIT(id);
+
+	spin_lock_irqsave(&pmu->lock, flags);
+	val &= readl_relaxed(pmu->pmc_base + PMC_SW_RST);
+	writel_relaxed(val, pmu->pmc_base + PMC_SW_RST);
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	return 0;
+}
+
+static int pmu_reset_deassert(struct reset_controller_dev *rc, unsigned long id)
+{
+	struct pmu_data *pmu = rcdev_to_pmu(rc);
+	unsigned long flags;
+	u32 val = BIT(id);
+
+	spin_lock_irqsave(&pmu->lock, flags);
+	val |= readl_relaxed(pmu->pmc_base + PMC_SW_RST);
+	writel_relaxed(val, pmu->pmc_base + PMC_SW_RST);
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	return 0;
+}
+
+static struct reset_control_ops pmu_reset_ops = {
+	.reset = pmu_reset_reset,
+	.assert = pmu_reset_assert,
+	.deassert = pmu_reset_deassert,
+};
+
+static struct reset_controller_dev pmu_reset __initdata = {
+	.ops = &pmu_reset_ops,
+	.owner = THIS_MODULE,
+	.nr_resets = 32,
+};
+
+static void __init pmu_reset_init(struct pmu_data *pmu)
+{
+	int ret;
+
+	pmu->reset = pmu_reset;
+	pmu->reset.of_node = pmu->of_node;
+
+	ret = reset_controller_register(&pmu->reset);
+	if (ret)
+		pr_err("pmu: %s failed: %d\n", "reset_controller_register", ret);
+}
+#else
+static void __init pmu_reset_init(struct pmu_data *pmu)
+{
+}
+#endif
+
+struct pmu_domain {
+	struct pmu_data *pmu;
+	u32 pwr_mask;
+	u32 rst_mask;
+	u32 iso_mask;
+	struct generic_pm_domain base;
+};
+
+#define to_pmu_domain(dom) container_of(dom, struct pmu_domain, base)
+
+/*
+ * This deals with the "old" Marvell sequence of bringing a power domain
+ * down/up, which is: apply power, release reset, disable isolators.
+ *
+ * Later devices apparantly use a different sequence: power up, disable
+ * isolators, assert repair signal, enable SRMA clock, enable AXI clock,
+ * enable module clock, deassert reset.
+ *
+ * Note: reading the assembly, it seems that the IO accessors have an
+ * unfortunate side-effect - they cause memory already read into registers
+ * for the if () to be re-read for the bit-set or bit-clear operation.
+ * The code is written to avoid this.
+ */
+static int pmu_domain_power_off(struct generic_pm_domain *domain)
+{
+	struct pmu_domain *pmu_dom = to_pmu_domain(domain);
+	struct pmu_data *pmu = pmu_dom->pmu;
+	unsigned long flags;
+	unsigned int val;
+	void __iomem *pmu_base = pmu->pmu_base;
+	void __iomem *pmc_base = pmu->pmc_base;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+
+	/* Enable isolators */
+	if (pmu_dom->iso_mask) {
+		val = ~pmu_dom->iso_mask;
+		val &= readl_relaxed(pmu_base + PMU_ISO);
+		writel_relaxed(val, pmu_base + PMU_ISO);
+	}
+
+	/* Reset unit */
+	if (pmu_dom->rst_mask) {
+		val = ~pmu_dom->rst_mask;
+		val &= readl_relaxed(pmc_base + PMC_SW_RST);
+		writel_relaxed(val, pmc_base + PMC_SW_RST);
+	}
+
+	/* Power down */
+	val = readl_relaxed(pmu_base + PMU_PWR) | pmu_dom->pwr_mask;
+	writel_relaxed(val, pmu_base + PMU_PWR);
+
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	return 0;
+}
+
+static int pmu_domain_power_on(struct generic_pm_domain *domain)
+{
+	struct pmu_domain *pmu_dom = to_pmu_domain(domain);
+	struct pmu_data *pmu = pmu_dom->pmu;
+	unsigned long flags;
+	unsigned int val;
+	void __iomem *pmu_base = pmu->pmu_base;
+	void __iomem *pmc_base = pmu->pmc_base;
+
+	spin_lock_irqsave(&pmu->lock, flags);
+
+	/* Power on */
+	val = ~pmu_dom->pwr_mask & readl_relaxed(pmu_base + PMU_PWR);
+	writel_relaxed(val, pmu_base + PMU_PWR);
+
+	/* Release reset */
+	if (pmu_dom->rst_mask) {
+		val = pmu_dom->rst_mask;
+		val |= readl_relaxed(pmc_base + PMC_SW_RST);
+		writel_relaxed(val, pmc_base + PMC_SW_RST);
+	}
+
+	/* Disable isolators */
+	if (pmu_dom->iso_mask) {
+		val = pmu_dom->iso_mask;
+		val |= readl_relaxed(pmu_base + PMU_ISO);
+		writel_relaxed(val, pmu_base + PMU_ISO);
+	}
+
+	spin_unlock_irqrestore(&pmu->lock, flags);
+
+	return 0;
+}
+
+static void __pmu_domain_register(struct pmu_domain *domain,
+	struct device_node *np)
+{
+	unsigned int val = readl_relaxed(domain->pmu->pmu_base + PMU_PWR);
+
+	domain->base.power_off = pmu_domain_power_off;
+	domain->base.power_on = pmu_domain_power_on;
+
+	pm_genpd_init(&domain->base, NULL, !(val & domain->pwr_mask));
+
+	if (np)
+		of_genpd_add_provider_simple(np, &domain->base);
+}
+
+/* PMU IRQ controller */
+static void pmu_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct pmu_data *pmu = irq_get_handler_data(irq);
+	struct irq_chip_generic *gc = pmu->irq_gc;
+	struct irq_domain *domain = pmu->irq_domain;
+	void __iomem *base = gc->reg_base;
+	u32 stat = readl_relaxed(base + PMC_IRQ_CAUSE) & gc->mask_cache;
+	u32 done = ~0;
+
+	if (stat == 0) {
+		handle_bad_irq(irq, desc);
+		return;
+	}
+
+	while (stat) {
+		u32 hwirq = fls(stat) - 1;
+
+		stat &= ~(1 << hwirq);
+		done &= ~(1 << hwirq);
+
+		generic_handle_irq(irq_find_mapping(domain, hwirq));
+	}
+
+	/*
+	 * 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.
+	 */
+	irq_gc_lock(gc);
+	done &= readl_relaxed(base + PMC_IRQ_CAUSE);
+	writel_relaxed(done, base + PMC_IRQ_CAUSE);
+	irq_gc_unlock(gc);
+}
+
+static int __init dove_init_pmu_irq(struct pmu_data *pmu, int irq)
+{
+	const char *name = "pmu_irq";
+	struct irq_chip_generic *gc;
+	struct irq_domain *domain;
+	int ret;
+
+	/* mask and clear all interrupts */
+	writel(0, pmu->pmc_base + PMC_IRQ_MASK);
+	writel(0, pmu->pmc_base + PMC_IRQ_CAUSE);
+
+	domain = irq_domain_add_linear(pmu->of_node, NR_PMU_IRQS,
+				       &irq_generic_chip_ops, NULL);
+	if (!domain) {
+		pr_err("%s: unable to add irq domain\n", name);
+		return -ENOMEM;
+	}
+
+	ret = irq_alloc_domain_generic_chips(domain, NR_PMU_IRQS, 1, name,
+					     handle_level_irq,
+					     IRQ_NOREQUEST | IRQ_NOPROBE, 0,
+					     IRQ_GC_INIT_MASK_CACHE);
+	if (ret) {
+		pr_err("%s: unable to alloc irq domain gc: %d\n", name, ret);
+		irq_domain_remove(domain);
+		return ret;
+	}
+
+	gc = irq_get_domain_generic_chip(domain, 0);
+	gc->reg_base = pmu->pmc_base;
+	gc->chip_types[0].regs.mask = PMC_IRQ_MASK;
+	gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+	gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+	pmu->irq_domain = domain;
+	pmu->irq_gc = gc;
+
+	irq_set_handler_data(irq, pmu);
+	irq_set_chained_handler(irq, pmu_irq_handler);
+
+	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";
+ *	reg = <0xd0000 0x8000> <0xd8000 0x8000>;
+ *	interrupts = <33>;
+ *	interrupt-controller;
+ *	#reset-cells = 1;
+ *	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>;
+ *	};
+ * };
+ */
+int __init dove_init_pmu(void)
+{
+	struct device_node *np_pmu, *domains_node, *np;
+	struct pmu_data *pmu;
+	int ret, parent_irq;
+
+	/* Lookup the PMU node */
+	np_pmu = of_find_compatible_node(NULL, NULL, "marvell,dove-pmu");
+	if (!np_pmu)
+		return 0;
+
+	domains_node = of_get_child_by_name(np_pmu, "domains");
+	if (!domains_node) {
+		pr_err("%s: failed to find domains sub-node\n", np_pmu->name);
+		return 0;
+	}
+
+	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
+	if (!pmu)
+		return -ENOMEM;
+
+	spin_lock_init(&pmu->lock);
+	pmu->of_node = np_pmu;
+	pmu->pmc_base = of_iomap(pmu->of_node, 0);
+	pmu->pmu_base = of_iomap(pmu->of_node, 1);
+	if (!pmu->pmc_base || !pmu->pmu_base) {
+		pr_err("%s: failed to map PMU\n", np_pmu->name);
+		iounmap(pmu->pmu_base);
+		iounmap(pmu->pmc_base);
+		kfree(pmu);
+		return -ENOMEM;
+	}
+
+	pmu_reset_init(pmu);
+
+	for_each_available_child_of_node(domains_node, np) {
+		struct of_phandle_args args;
+		struct pmu_domain *domain;
+
+		domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+		if (!domain)
+			break;
+
+		domain->pmu = pmu;
+		domain->base.name = kstrdup(np->name, GFP_KERNEL);
+		if (!domain->base.name) {
+			kfree(domain);
+			break;
+		}
+
+		of_property_read_u32(np, "marvell,pmu_pwr_mask",
+				     &domain->pwr_mask);
+		of_property_read_u32(np, "marvell,pmu_iso_mask",
+				     &domain->iso_mask);
+
+		/*
+		 * We parse the reset controller property directly here
+		 * to ensure that we can operate when the reset controller
+		 * support is not configured into the kernel.
+		 */
+		ret = of_parse_phandle_with_args(np, "resets", "#reset-cells",
+						 0, &args);
+		if (ret == 0) {
+			if (args.np == pmu->of_node)
+				domain->rst_mask = BIT(args.args[0]);
+			of_node_put(args.np);
+		}
+
+		__pmu_domain_register(domain, np);
+	}
+	pm_genpd_poweroff_unused();
+
+	/* Loss of the interrupt controller is not a fatal error. */
+	parent_irq = irq_of_parse_and_map(pmu->of_node, 0);
+	if (!parent_irq) {
+		pr_err("%s: no interrupt specified\n", np_pmu->name);
+	} else {
+		ret = dove_init_pmu_irq(pmu, parent_irq);
+		if (ret)
+			pr_err("dove_init_pmu_irq() failed: %d\n", ret);
+	}
+
+	return 0;
+}
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..2b71c31b6501
--- /dev/null
+++ b/drivers/staging/etnaviv/Makefile
@@ -0,0 +1,18 @@
+ccflags-y := -Iinclude/drm -Idrivers/staging/vivante
+ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
+	ccflags-y += -Werror
+endif
+
+etnaviv-y := \
+	etnaviv_cmd_parser.o \
+	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..8c44ba9a694e
--- /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:
+- cmdstream.xml (  12589 bytes, from 2014-02-17 14:57:56)
+- common.xml    (  18437 bytes, from 2015-03-25 11:27:41)
+
+Copyright (C) 2014
+*/
+
+
+#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..9e585d51fb78
--- /dev/null
+++ b/drivers/staging/etnaviv/common.xml.h
@@ -0,0 +1,249 @@
+#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:
+- 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) 2015
+*/
+
+
+#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..0196d3374cd7
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ * Author: Christian Gmeiner <christian.gmeiner@gmail.com>
+ *
+ * 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 "etnaviv_mmu.h"
+
+#include "common.xml.h"
+#include "state.xml.h"
+#include "cmdstream.xml.h"
+
+/*
+ * Command Buffer helper:
+ */
+
+
+static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data)
+{
+	u32 *vaddr = (u32 *)buffer->vaddr;
+
+	BUG_ON(buffer->user_size >= buffer->size);
+
+	vaddr[buffer->user_size / 4] = data;
+	buffer->user_size += 4;
+}
+
+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->user_size = ALIGN(buffer->user_size, 8);
+
+	/* 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(index));
+	OUT(buffer, value);
+}
+
+static inline void CMD_END(struct etnaviv_cmdbuf *buffer)
+{
+	buffer->user_size = ALIGN(buffer->user_size, 8);
+
+	OUT(buffer, VIV_FE_END_HEADER_OP_END);
+}
+
+static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer)
+{
+	buffer->user_size = ALIGN(buffer->user_size, 8);
+
+	OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | 200);
+}
+
+static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer,
+	u16 prefetch, u32 address)
+{
+	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_cmdbuf *buffer,
+	u32 from, u32 to)
+{
+	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_cmdbuf *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 u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf)
+{
+	return buf->paddr - gpu->memory_base;
+}
+
+static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
+	struct etnaviv_cmdbuf *buf, u32 off, u32 len)
+{
+	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, buf) + off, size - len * 4 - off);
+
+	print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
+			ptr, len * 4, 0);
+}
+
+u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
+{
+	struct etnaviv_cmdbuf *buffer = gpu->buffer;
+
+	/* initialize buffer */
+	buffer->user_size = 0;
+
+	CMD_WAIT(buffer);
+	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4);
+
+	return buffer->user_size / 8;
+}
+
+void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
+{
+	struct etnaviv_cmdbuf *buffer = gpu->buffer;
+
+	/* Replace the last WAIT with an END */
+	buffer->user_size -= 16;
+
+	CMD_END(buffer);
+	mb();
+}
+
+void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
+	struct etnaviv_gem_submit *submit)
+{
+	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)
+		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
+
+	/*
+	 * 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 || 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) * 4;
+
+	/*
+	 * if we are going to completely overflow the buffer, we need to wrap.
+	 */
+	if (buffer->user_size + reserve_size > buffer->size)
+		buffer->user_size = 0;
+
+	/* save offset back into main buffer */
+	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 */
+	link_target += extra_size * sizeof(u32);
+
+	if (drm_debug & DRM_UT_DRIVER)
+		pr_info("stream link to 0x%08x @ 0x%08x %p\n",
+			link_target, gpu_va(gpu, submit->cmdbuf),
+			submit->cmdbuf->vaddr);
+
+	/* jump back from cmd to main buffer */
+	CMD_LINK(submit->cmdbuf, link_size, link_target);
+
+	link_target = gpu_va(gpu, submit->cmdbuf);
+	link_size = submit->cmdbuf->size / 8;
+
+
+
+	if (drm_debug & DRM_UT_DRIVER) {
+		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);
+		pr_info("addr: 0x%08x\n", link_target);
+		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->user_size;
+
+		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;
+		}
+
+		/* And the link to the first buffer */
+		CMD_LINK(buffer, link_size, link_target);
+
+		/* Update the link target to point to above instructions */
+		link_target = new_target;
+		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);
+
+	/* append WAIT/LINK to main buffer */
+	CMD_WAIT(buffer);
+	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;
+	mb();
+	*(lw) = VIV_FE_LINK_HEADER_OP_LINK |
+		VIV_FE_LINK_HEADER_PREFETCH(link_size);
+	mb();
+
+	if (drm_debug & DRM_UT_DRIVER)
+		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
+}
diff --git a/drivers/staging/etnaviv/etnaviv_cmd_parser.c b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
new file mode 100644
index 000000000000..5175d6eb3bdc
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_cmd_parser.c
@@ -0,0 +1,119 @@
+/*
+ * 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"
+#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, void *stream,
+			      unsigned int size)
+{
+	u32 *start = stream;
+	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);
+			if (n == 0)
+				n = 256;
+			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.c b/drivers/staging/etnaviv/etnaviv_drv.c
new file mode 100644
index 000000000000..153d33eea147
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -0,0 +1,713 @@
+/*
+ * 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/component.h>
+#include <linux/of_platform.h>
+
+#include "etnaviv_drv.h"
+#include "etnaviv_gpu.h"
+#include "etnaviv_gem.h"
+#include "etnaviv_mmu.h"
+#include "etnaviv_gem.h"
+
+#ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
+static bool reglog;
+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;
+
+	flush_workqueue(priv->wq);
+	destroy_workqueue(priv->wq);
+
+	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;
+
+	for (i = 0; i < ETNA_MAX_PIPES; i++) {
+		struct etnaviv_gpu *g = priv->gpu[i];
+
+		if (g) {
+			int ret;
+
+			ret = etnaviv_gpu_init(g);
+			if (ret) {
+				dev_err(g->dev, "hw init failed: %d\n", ret);
+				priv->gpu[i] = NULL;
+			}
+		}
+	}
+}
+
+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);
+	if (!priv->wq) {
+		err = -ENOMEM;
+		goto err_wq;
+	}
+
+	INIT_LIST_HEAD(&priv->inactive_list);
+	priv->num_gpus = 0;
+
+	platform_set_drvdata(pdev, dev);
+
+	err = component_bind_all(dev->dev, dev);
+	if (err < 0)
+		goto err_bind;
+
+	load_gpu(dev);
+
+	return 0;
+
+err_bind:
+	flush_workqueue(priv->wq);
+	destroy_workqueue(priv->wq);
+err_wq:
+	kfree(priv);
+	return err;
+}
+
+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;
+	unsigned int i;
+
+	mutex_lock(&dev->struct_mutex);
+	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);
+}
+
+/*
+ * DRM debugfs:
+ */
+
+#ifdef CONFIG_DEBUG_FS
+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",
+				   dev_name(gpu->dev));
+			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 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 void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m)
+{
+	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",
+			buf->vaddr, (u64)buf->paddr, size - buf->user_size);
+
+	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;
+	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 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_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},
+		{"ring", show_locked, 0, etnaviv_ring_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
+
+/*
+ * 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;
+
+	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);
+}
+
+#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;
+
+	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;
+
+	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;
+
+	if (args->pad)
+		return -EINVAL;
+
+	obj = drm_gem_object_lookup(dev, file, args->handle);
+	if (!obj)
+		return -ENOENT;
+
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret == 0) {
+		ret = etnaviv_gem_mmap_offset(obj, &args->offset);
+
+		mutex_unlock(&dev->struct_mutex);
+	}
+
+	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_gpu_wait_fence_interruptible(gpu, 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 ||
+	    (u32)args->user_size != args->user_size ||
+	    args->user_ptr & ~PAGE_MASK)
+		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 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)
+	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),
+	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),
+	ETNA_IOCTL(GEM_WAIT,     gem_wait,     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,
+	.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      = 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   = etnaviv_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               = "20150925",
+	.major              = 1,
+	.minor              = 0,
+};
+
+/*
+ * Platform driver:
+ */
+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 = {
+	.bind = etnaviv_bind,
+	.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);
+}
+
+static int etnaviv_pdev_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct component_match *match = NULL;
+
+	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+	if (node) {
+		struct device_node *core_node;
+		int i;
+
+		for (i = 0; ; i++) {
+			core_node = of_parse_phandle(node, "cores", i);
+			if (!core_node)
+				break;
+
+			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;
+
+		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)
+{
+	component_master_del(&pdev->dev, &etnaviv_master_ops);
+
+	return 0;
+}
+
+static const struct of_device_id dt_match[] = {
+	{ .compatible = "fsl,imx-gpu-subsystem" },
+	{ .compatible = "marvell,dove-gpu-subsystem" },
+	{}
+};
+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   = "etnaviv",
+		.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("Christian Gmeiner <christian.gmeiner@gmail.com>");
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_AUTHOR("Lucas Stach <l.stach@pengutronix.de>");
+MODULE_DESCRIPTION("etnaviv DRM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:etnaviv");
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
new file mode 100644
index 000000000000..719e33174e83
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -0,0 +1,138 @@
+/*
+ * 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/>.
+ */
+
+#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_object;
+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 {
+	int num_gpus;
+	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
+
+	u32 next_fence;
+
+	/* list of GEM objects: */
+	struct list_head inactive_list;
+
+	struct workqueue_struct *wq;
+};
+
+static inline void etnaviv_queue_work(struct drm_device *dev,
+	struct work_struct *w)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+
+	queue_work(priv->wq, w);
+}
+
+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);
+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, u32 *iova);
+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+	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);
+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 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);
+void etnaviv_gem_move_to_active(struct drm_gem_object *obj,
+		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, 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,
+		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,
+	uintptr_t ptr, u32 size, u32 flags, u32 *handle);
+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);
+bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
+	void *stream, unsigned int size);
+
+#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(u32 a, u32 b)
+{
+	return (s32)(a - b) > 0;
+}
+
+static inline bool fence_after_eq(u32 a, u32 b)
+{
+	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
new file mode 100644
index 000000000000..1381c952c52f
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem.c
@@ -0,0 +1,887 @@
+/*
+ * 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/spinlock.h>
+#include <linux/shmem_fs.h>
+
+#include "etnaviv_drv.h"
+#include "etnaviv_gem.h"
+#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;
+	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_CACHE_MASK)
+		dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+}
+
+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
+	 * 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_CACHE_MASK)
+		dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+}
+
+/* called with dev->struct_mutex held */
+static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
+{
+	struct drm_device *dev = etnaviv_obj->base.dev;
+	struct page **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->pages = p;
+
+	return 0;
+}
+
+static void put_pages(struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->sgt) {
+		etnaviv_gem_scatterlist_unmap(etnaviv_obj);
+		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);
+
+		etnaviv_obj->pages = NULL;
+	}
+}
+
+struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
+{
+	int ret;
+
+	if (!etnaviv_obj->pages) {
+		ret = etnaviv_obj->ops->get_pages(etnaviv_obj);
+		if (ret < 0)
+			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;
+
+		etnaviv_gem_scatter_map(etnaviv_obj);
+	}
+
+	return etnaviv_obj->pages;
+}
+
+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_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_page_prot);
+	} else if (etnaviv_obj->flags & ETNA_BO_UNCACHED) {
+		vma->vm_page_prot = pgprot_noncached(vm_page_prot);
+	} 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_page_prot;
+	}
+
+	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);
+	return etnaviv_gem_mmap_obj(vma->vm_private_data, vma);
+}
+
+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, *page;
+	pgoff_t pgoff;
+	int ret;
+
+	/*
+	 * 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)
+		goto out;
+
+	/* 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;
+	}
+
+	/* We don't use vmf->pgoff since that has the fake offset: */
+	pgoff = ((unsigned long)vmf->virtual_address -
+			vma->vm_start) >> PAGE_SHIFT;
+
+	page = pages[pgoff];
+
+	VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
+	     page_to_pfn(page), page_to_pfn(page) << PAGE_SHIFT);
+
+	ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
+
+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 - must be called under struct_mutex */
+int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset)
+{
+	int ret;
+
+	/* Make it mmapable */
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret)
+		dev_err(obj->dev->dev, "could not allocate mmap offset\n");
+	else
+		*offset = drm_vma_node_offset_addr(&obj->vma_node);
+
+	return ret;
+}
+
+/* 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, u32 *iova)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct etnaviv_vram_mapping *mapping;
+	int ret = 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))
+			return PTR_ERR(pages);
+		ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj,
+				gpu->memory_base, &mapping);
+	}
+
+	if (!ret)
+		*iova = mapping->iova;
+
+	return ret;
+}
+
+int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj,
+	int id, 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);
+	int ret;
+
+	/* this is safe right now because we don't unmap until the
+	 * bo is deleted:
+	 */
+	if (mapping) {
+		*iova = mapping->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..
+	 */
+}
+
+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 = etnaviv_gem_get_pages(etnaviv_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, u32 access, u32 fence)
+{
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+
+	etnaviv_obj->gpu = gpu;
+
+	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);
+}
+
+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;
+	etnaviv_obj->access = 0;
+	list_del_init(&etnaviv_obj->mm_list);
+	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)) {
+		struct etnaviv_gpu *gpu = etnaviv_obj->gpu;
+		u32 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_gpu_wait_fence_interruptible(gpu, fence, timeout);
+	}
+
+	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)
+{
+	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;
+}
+
+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)
+{
+	struct drm_device *dev = obj->dev;
+	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	unsigned long 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) %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,
+			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_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->vaddr)
+		vunmap(etnaviv_obj->vaddr);
+	put_pages(etnaviv_obj);
+}
+
+static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = {
+	.get_pages = etnaviv_gem_shmem_get_pages,
+	.release = etnaviv_gem_shmem_release,
+};
+
+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;
+
+	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);
+
+	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
+				 obj_node)
+		etnaviv_iommu_unmap_gem(mapping);
+
+	drm_gem_free_mmap_offset(obj);
+	etnaviv_obj->ops->release(etnaviv_obj);
+	reservation_object_fini(&etnaviv_obj->_resv);
+	drm_gem_object_release(obj);
+
+	kfree(etnaviv_obj);
+}
+
+int etnaviv_gem_obj_add(struct drm_device *dev, 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_killable(&dev->struct_mutex);
+	if (ret)
+		return ret;
+
+	list_add_tail(&etnaviv_obj->mm_list, &priv->inactive_list);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+static int etnaviv_gem_new_impl(struct drm_device *dev,
+		u32 size, u32 flags,
+		struct drm_gem_object **obj)
+{
+	struct etnaviv_gem_object *etnaviv_obj;
+	unsigned sz = sizeof(*etnaviv_obj);
+	bool valid = true;
+
+	/* validate flags */
+	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\n",
+			(flags & ETNA_BO_CACHE_MASK));
+		return -EINVAL;
+	}
+
+	etnaviv_obj = kzalloc(sz, GFP_KERNEL);
+	if (!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);
+	INIT_LIST_HEAD(&etnaviv_obj->mm_list);
+	INIT_LIST_HEAD(&etnaviv_obj->vram_list);
+
+	*obj = &etnaviv_obj->base;
+
+	return 0;
+}
+
+static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev,
+		u32 size, u32 flags)
+{
+	struct drm_gem_object *obj = NULL;
+	int ret;
+
+	size = PAGE_ALIGN(size);
+
+	ret = etnaviv_gem_new_impl(dev, size, flags, &obj);
+	if (ret)
+		goto fail;
+
+	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)
+		goto fail;
+
+	return obj;
+
+fail:
+	if (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,
+		u32 size, u32 flags, u32 *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,
+		u32 size, u32 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, u32 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;
+}
+
+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_node) {
+		if (mapping->mmu == mmu)
+			return mapping;
+	}
+
+	return NULL;
+}
+
+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 = 0, 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 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);
+
+	etnaviv_queue_work(etnaviv_obj->base.dev, &work->work);
+
+	return -EAGAIN;
+}
+
+static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj)
+{
+	if (etnaviv_obj->sgt) {
+		etnaviv_gem_scatterlist_unmap(etnaviv_obj);
+		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, u32 size, u32 flags, u32 *handle)
+{
+	struct etnaviv_gem_object *etnaviv_obj;
+	int ret;
+
+	ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, &etnaviv_obj);
+	if (ret)
+		return ret;
+
+	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);
+
+	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);
+
+	/* 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
new file mode 100644
index 000000000000..c991d12e7aed
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -0,0 +1,141 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __ETNAVIV_GEM_H__
+#define __ETNAVIV_GEM_H__
+
+#include <linux/reservation.h>
+#include "etnaviv_drv.h"
+
+struct etnaviv_gem_ops;
+struct etnaviv_gem_object;
+
+struct etnaviv_gem_userptr {
+	uintptr_t ptr;
+	struct task_struct *task;
+	struct work_struct *work;
+	bool ro;
+};
+
+struct etnaviv_vram_mapping {
+	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;
+};
+
+struct etnaviv_gem_object {
+	struct drm_gem_object base;
+	const struct etnaviv_gem_ops *ops;
+
+	u32 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 */
+	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
+	 * 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;
+
+	/* 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 list_head vram_list;
+
+	/* for buffer manipulation during submit */
+	bool is_ring_buffer;
+	u32 offset;
+
+	/* cache maintenance */
+	uint32_t last_cpu_prep_op;
+
+	struct etnaviv_gem_userptr userptr;
+};
+
+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 *);
+	void (*release)(struct etnaviv_gem_object *);
+};
+
+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;
+	u32 exec_state;
+	struct list_head bo_list;
+	struct ww_acquire_ctx ticket;
+	u32 fence;
+	unsigned int nr_bos;
+	struct etnaviv_cmdbuf *cmdbuf;
+	struct {
+		u32 flags;
+		struct etnaviv_gem_object *obj;
+		u32 iova;
+	} 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);
+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);
+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
new file mode 100644
index 000000000000..58c13ae7c345
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem_prime.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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/dma-buf.h>
+#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 etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+	/* TODO msm_gem_vunmap() */
+}
+
+int etnaviv_gem_prime_pin(struct drm_gem_object *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 etnaviv_gem_prime_unpin(struct drm_gem_object *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);
+	}
+}
+
+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 = {
+	/* .get_pages should never be called */
+	.release = etnaviv_gem_prime_release,
+};
+
+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;
+	size_t size = PAGE_ALIGN(attach->dmabuf->size);
+	int ret, npages;
+
+	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;
+
+	ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base);
+	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_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
new file mode 100644
index 000000000000..6c5eeb47e5c4
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -0,0 +1,428 @@
+/*
+ * 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 "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/ ETNAVIV_SUBMIT_BO_x */
+#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->cmdbuf = NULL;
+
+		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_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, bo = submit_bos; i < nr_bos; i++, bo++) {
+		struct drm_gem_object *obj;
+		struct etnaviv_gem_object *etnaviv_obj;
+
+		if (bo->flags & BO_INVALID_FLAGS) {
+			DRM_ERROR("invalid flags: %x\n", bo->flags);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
+		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, bo->handle);
+		if (!obj) {
+			DRM_ERROR("invalid handle %u at index %u\n",
+				  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",
+				  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);
+
+	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:
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
+		u32 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;
+		submit->bos[i].iova = iova;
+	}
+
+	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;
+
+		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, u32 idx,
+		struct etnaviv_gem_object **obj, u32 *iova)
+{
+	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;
+
+	return 0;
+}
+
+/* process the reloc's and patch up the cmdstream as needed: */
+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 = stream;
+	int ret;
+
+	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)));
+		u32 iova, off;
+
+		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 >= size ) ||
+				(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);
+		if (ret)
+			return ret;
+
+		if (submit_reloc.reloc_offset >=
+		    bobj->base.size - sizeof(*ptr)) {
+			DRM_ERROR("relocation %u outside object", i);
+			return -EINVAL;
+		}
+
+		ptr[off] = iova + submit_reloc.reloc_offset;
+
+		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);
+	}
+
+	if (submit->cmdbuf)
+		etnaviv_gpu_cmdbuf_free(submit->cmdbuf);
+
+	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 drm_etnaviv_gem_submit_bo *bos;
+	struct etnaviv_gem_submit *submit;
+	struct etnaviv_cmdbuf *cmdbuf;
+	struct etnaviv_gpu *gpu;
+	void *stream;
+	int ret;
+
+	if (args->pipe >= ETNA_MAX_PIPES)
+		return -EINVAL;
+
+	gpu = priv->gpu[args->pipe];
+	if (!gpu)
+		return -ENXIO;
+
+	if (args->stream_size % 4) {
+		DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
+			  args->stream_size);
+		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.
+	 */
+	bos = drm_malloc_ab(args->nr_bos, sizeof(*bos));
+	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(bos, to_user_ptr(args->bos),
+			     args->nr_bos * sizeof(*bos));
+	if (ret) {
+		ret = -EFAULT;
+		goto err_submit_cmds;
+	}
+
+	ret = copy_from_user(stream, to_user_ptr(args->stream),
+			     args->stream_size);
+	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)
+	 * - 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)
+		goto err_submit_cmds;
+
+	mutex_lock(&dev->struct_mutex);
+
+	submit = submit_create(dev, gpu, args->nr_bos);
+	if (!submit) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	submit->exec_state = args->exec_state;
+
+	ret = submit_lookup_objects(submit, file, bos, args->nr_bos);
+	if (ret)
+		goto out;
+
+	ret = submit_validate_objects(submit);
+	if (ret)
+		goto out;
+
+	if (!etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	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);
+
+	args->fence = submit->fence;
+
+out:
+	if (submit)
+		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.
+	 * Flush our workqueue to ensure that it is run in a timely manner.
+	 */
+	if (ret == -EAGAIN)
+		flush_workqueue(priv->wq);
+
+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);
+
+	return ret;
+}
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
new file mode 100644
index 000000000000..8f54fa8dd59d
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -0,0 +1,1465 @@
+/*
+ * 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/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"
+
+static const struct platform_device_id gpu_ids[] = {
+	{ .name = "etnaviv-gpu,2d" },
+	{ },
+};
+
+/*
+ * Driver functions:
+ */
+
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *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", dev_name(gpu->dev), 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, "TODO: determine GPU specs based on model\n");
+	}
+
+	switch (gpu->identity.instruction_count) {
+	case 0:
+		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:
+		gpu->identity.instruction_count = 1024;
+		break;
+
+	case 2:
+		gpu->identity.instruction_count = 2048;
+		break;
+
+	default:
+		gpu->identity.instruction_count = 256;
+		break;
+	}
+}
+
+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.
+		 */
+		if ((gpu->identity.model & 0xff00) == 0x0400 &&
+		    gpu->identity.model != 0x0420) {
+			gpu->identity.model = gpu->identity.model & 0x0400;
+		}
+
+		/* 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.
+				 */
+				gpu->identity.revision = 0x1051;
+			}
+		}
+	}
+
+	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);
+
+	/* Disable fast clear on GC700. */
+	if (gpu->identity.model == 0x700)
+		gpu->identity.features &= ~chipFeatures_FAST_CLEAR;
+
+	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 &
+	    chipMinorFeatures0_MORE_MINOR_FEATURES) {
+		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);
+	}
+
+	/* 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);
+}
+
+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;
+	unsigned long timeout;
+	bool failed = true;
+
+	/* TODO
+	 *
+	 * - clock gating
+	 * - puls eater
+	 * - what about VG?
+	 */
+
+	/* 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);
+
+		/* enable clock */
+		etnaviv_gpu_load_clock(gpu, 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, "FE is not idle\n");
+			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, "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);
+
+	/* enable clock */
+	etnaviv_gpu_load_clock(gpu, control);
+
+	return 0;
+}
+
+static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
+{
+	u16 prefetch;
+
+	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));
+
+	/* 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);
+	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);
+
+	/* Start command processor */
+	prefetch = etnaviv_buffer_init(gpu);
+
+	gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
+	gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
+		  gpu->buffer->paddr - gpu->memory_base);
+	gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
+		  VIVS_FE_COMMAND_CONTROL_ENABLE |
+		  VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
+}
+
+int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
+{
+	int ret, i;
+	struct iommu_domain *iommu;
+	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) {
+		dev_err(gpu->dev, "Unknown GPU model\n");
+		pm_runtime_put_autosuspend(gpu->dev);
+		return -ENXIO;
+	}
+
+	ret = etnaviv_hw_reset(gpu);
+	if (ret)
+		goto fail;
+
+	/* 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, "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;
+	}
+
+	/* Create buffer: */
+	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;
+	}
+
+	/* Setup event management */
+	spin_lock_init(&gpu->event_spinlock);
+	init_completion(&gpu->event_free);
+	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
+		gpu->event[i].used = false;
+		complete(&gpu->event_free);
+	}
+
+	/* 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);
+
+	return 0;
+
+fail:
+	pm_runtime_mark_last_busy(gpu->dev);
+	pm_runtime_put_autosuspend(gpu->dev);
+
+	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;
+	}
+}
+
+int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
+{
+	struct dma_debug debug;
+	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);
+
+	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;
+	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);
+
+	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
+
+/*
+ * Power Management:
+ */
+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;
+}
+
+/*
+ * 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->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);
+
+	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);
+}
+
+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_ETNAVIV_HANGCHECK_JIFFIES));
+}
+
+static void hangcheck_handler(unsigned long data)
+{
+	struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data;
+	u32 fence = gpu->completed_fence;
+	bool progress = false;
+
+	if (fence != gpu->hangcheck_fence) {
+		gpu->hangcheck_fence = fence;
+		progress = true;
+	}
+
+	if (!progress) {
+		u32 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",
+			gpu->submitted_fence);
+		etnaviv_queue_work(gpu->drm, &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);
+}
+
+static void hangcheck_disable(struct etnaviv_gpu *gpu)
+{
+	del_timer_sync(&gpu->hangcheck_timer);
+	cancel_work_sync(&gpu->recover_work);
+}
+
+/*
+ * 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, "wait_for_completion_timeout failed");
+
+	spin_lock_irqsave(&gpu->event_spinlock, flags);
+
+	/* find first free event */
+	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
+		if (gpu->event[i].used == false) {
+			gpu->event[i].used = 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[event].used == false) {
+		dev_warn(gpu->dev, "event %u is already marked as free",
+			 event);
+		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
+	} else {
+		gpu->event[event].used = false;
+		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
+
+		complete(&gpu->event_free);
+	}
+}
+
+/*
+ * 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;
+
+	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->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);
+			drm_gem_object_unreference(&obj->base);
+		} else {
+			break;
+		}
+	}
+
+	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);
+
+	wake_up_all(&gpu->fence_event);
+}
+
+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)
+{
+	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 remaining = etnaviv_timeout_to_jiffies(timeout);
+
+		ret = wait_event_interruptible_timeout(gpu->fence_event,
+						fence_completed(gpu, fence),
+						remaining);
+		if (ret == 0) {
+			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;
+		}
+	}
+
+	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);
+}
+
+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)
+{
+	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
+	 *
+	 * - flush
+	 * - data endian
+	 * - prefetch
+	 *
+	 */
+
+	event = event_alloc(gpu);
+	if (unlikely(event == ~0U)) {
+		DRM_ERROR("no free event\n");
+		pm_runtime_put_autosuspend(gpu->dev);
+		return -EBUSY;
+	}
+
+	submit->fence = ++priv->next_fence;
+
+	gpu->submitted_fence = submit->fence;
+	gpu->event[event].fence = submit->fence;
+
+	if (gpu->lastctx != ctx) {
+		gpu->mmu->need_flush = true;
+		gpu->switch_context = true;
+		gpu->lastctx = ctx;
+	}
+
+	etnaviv_buffer_queue(gpu, event, submit);
+
+	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)) {
+			u32 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 |
+					    ETNA_SUBMIT_BO_WRITE))
+			etnaviv_gem_move_to_active(&etnaviv_obj->base, gpu,
+						   submit->bos[i].flags,
+						   submit->fence);
+	}
+	hangcheck_timer_reset(gpu);
+
+	return 0;
+}
+
+/*
+ * 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) {
+		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) {
+			dev_err(gpu->dev, "AXI bus error\n");
+			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);
+			/*
+			 * 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->completed_fence))
+				gpu->completed_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);
+		}
+
+		/* Retire the buffer objects in a work */
+		etnaviv_queue_work(gpu->drm, &gpu->retire_work);
+
+		ret = IRQ_HANDLED;
+	}
+
+	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_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);
+}
+
+#ifdef CONFIG_PM
+static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
+{
+	struct drm_device *drm = gpu->drm;
+	u32 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;
+}
+#endif
+
+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 ret;
+
+#ifdef CONFIG_PM
+	ret = pm_runtime_get_sync(gpu->dev);
+#else
+	ret = etnaviv_gpu_clk_enable(gpu);
+#endif
+	if (ret < 0)
+		return ret;
+
+	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);
+
+	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;
+}
+
+static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+
+	DBG("%s", dev_name(gpu->dev));
+
+	hangcheck_disable(gpu);
+
+	WARN_ON(!list_empty(&gpu->active_list));
+
+#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) {
+		etnaviv_gpu_cmdbuf_free(gpu->buffer);
+		gpu->buffer = NULL;
+	}
+
+	if (gpu->mmu) {
+		etnaviv_iommu_destroy(gpu->mmu);
+		gpu->mmu = NULL;
+	}
+
+	gpu->drm = NULL;
+}
+
+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,gc"
+	},
+	{ /* sentinel */ }
+};
+
+static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct etnaviv_gpu *gpu;
+	int err = 0;
+
+	gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL);
+	if (!gpu)
+		return -ENOMEM;
+
+	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))
+		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, 0,
+			       dev_name(gpu->dev), 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;
+
+	/* 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);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return err;
+}
+
+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->completed_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,
+	.remove = etnaviv_gpu_platform_remove,
+	.id_table = gpu_ids,
+};
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
new file mode 100644
index 000000000000..3be5b481d8d1
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -0,0 +1,198 @@
+/*
+ * 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/>.
+ */
+
+#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. */
+	u32 model;
+
+	/* Revision value.*/
+	u32 revision;
+
+	/* Supported feature fields. */
+	u32 features;
+
+	/* Supported minor feature fields. */
+	u32 minor_features0;
+
+	/* Supported minor feature 1 fields. */
+	u32 minor_features1;
+
+	/* Supported minor feature 2 fields. */
+	u32 minor_features2;
+
+	/* Supported minor feature 3 fields. */
+	u32 minor_features3;
+
+	/* Number of streams supported. */
+	u32 stream_count;
+
+	/* Total number of temporary registers per thread. */
+	u32 register_max;
+
+	/* Maximum number of threads. */
+	u32 thread_count;
+
+	/* Number of shader cores. */
+	u32 shader_core_count;
+
+	/* Size of the vertex cache. */
+	u32 vertex_cache_size;
+
+	/* Number of entries in the vertex output buffer. */
+	u32 vertex_output_buffer_size;
+
+	/* Number of pixel pipes. */
+	u32 pixel_pipes;
+
+	/* Number of instructions. */
+	u32 instruction_count;
+
+	/* Number of constants. */
+	u32 num_constants;
+
+	/* Buffer size */
+	u32 buffer_size;
+};
+
+struct etnaviv_event {
+	bool used;
+	u32 fence;
+};
+
+struct etnaviv_cmdbuf;
+
+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: */
+	struct etnaviv_cmdbuf *buffer;
+
+	/* bus base address of memory  */
+	u32 memory_base;
+
+	/* event management: */
+	struct etnaviv_event event[30];
+	struct completion event_free;
+	spinlock_t event_spinlock;
+
+	/* 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 */
+	u32 submitted_fence;
+	u32 completed_fence;
+	u32 retired_fence;
+	wait_queue_head_t fence_event;
+
+	/* worker for handling active-list retiring: */
+	struct work_struct retire_work;
+
+	void __iomem *mmio;
+	int irq;
+
+	struct etnaviv_iommu *mmu;
+
+	/* Power Control: */
+	struct clk *clk_bus;
+	struct clk *clk_core;
+	struct clk *clk_shader;
+
+	/* Hang Detction: */
+#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;
+	u32 hangcheck_fence;
+	u32 hangcheck_dma_addr;
+	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);
+}
+
+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, 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);
+}
+
+int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value);
+
+int etnaviv_gpu_init(struct etnaviv_gpu *gpu);
+
+#ifdef CONFIG_DEBUG_FS
+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,
+	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);
+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);
+
+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..bf7b38ecd502
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_iommu.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.com>
+ *
+ * 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_2M
+#define PT_ENTRIES	(PT_SIZE / sizeof(u32))
+
+#define GPU_MEM_START	0x80000000
+
+struct etnaviv_iommu_domain_pgtable {
+	u32 *pgtable;
+	dma_addr_t paddr;
+};
+
+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)
+{
+	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 u32 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_init(struct etnaviv_iommu_domain *etnaviv_domain)
+{
+	u32 *p;
+	int ret, i;
+
+	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)
+		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(etnaviv_domain->dev, SZ_4K,
+				  etnaviv_domain->bad_page_cpu,
+				  etnaviv_domain->bad_page_dma);
+		return ret;
+	}
+
+	for (i = 0; i < PT_ENTRIES; i++)
+		etnaviv_domain->pgtable.pgtable[i] =
+			etnaviv_domain->bad_page_dma;
+
+	spin_lock_init(&etnaviv_domain->map_lock);
+
+	return 0;
+}
+
+static void etnaviv_domain_free(struct iommu_domain *domain)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+
+	pgtable_free(&etnaviv_domain->pgtable, PT_SIZE);
+
+	dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+			  etnaviv_domain->bad_page_cpu,
+			  etnaviv_domain->bad_page_dma);
+
+	kfree(etnaviv_domain);
+}
+
+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 = to_etnaviv_domain(domain);
+
+	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 = to_etnaviv_domain(domain);
+
+	if (size != SZ_4K)
+		return -EINVAL;
+
+	spin_lock(&etnaviv_domain->map_lock);
+	pgtable_write(&etnaviv_domain->pgtable, iova,
+		      etnaviv_domain->bad_page_dma);
+	spin_unlock(&etnaviv_domain->map_lock);
+
+	return SZ_4K;
+}
+
+static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,
+	dma_addr_t iova)
+{
+	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_free = etnaviv_domain_free,
+	.map = etnaviv_iommu_map,
+	.unmap = etnaviv_iommu_unmap,
+	.iova_to_phys = etnaviv_iommu_iova_to_phys,
+	.pgsize_bitmap = SZ_4K,
+};
+
+void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
+	struct iommu_domain *domain)
+{
+	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+	u32 pgtable;
+
+	/* set page table address in MC */
+	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);
+	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;
+
+	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(etnaviv_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..cf45503f6b6f
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_iommu.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.com>
+  *
+ * 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);
+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__ */
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 <christian.gmeiner@gmail.com>
+  *
+ * 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 <christian.gmeiner@gmail.com>
+  *
+ * 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..ca317f633970
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_mmu.c
@@ -0,0 +1,282 @@
+/*
+ * 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 "etnaviv_drv.h"
+#include "etnaviv_gem.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, u32 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_dma_address(sg) - sg->offset;
+		size_t bytes = sg_dma_len(sg) + 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_dma_len(sg) + sg->offset;
+
+		iommu_unmap(domain, da, bytes);
+		da += bytes;
+	}
+	return ret;
+}
+
+int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 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_dma_len(sg) + 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;
+}
+
+int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
+	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;
+	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->scan_node);
+	mapping->object = etnaviv_obj;
+	mapping->mmu = mmu;
+
+	/* v1 MMU can optimize single entry (contiguous) scatterlists */
+	if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) {
+		u32 iova;
+
+		iova = sg_dma_address(sgt->sgl) - memory_base;
+		if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
+			mapping->iova = iova;
+			list_add_tail(&mapping->obj_node,
+				      &etnaviv_obj->vram_list);
+			if (out_mapping)
+				*out_mapping = mapping;
+			return 0;
+		}
+	}
+
+	node = &mapping->vram_node;
+	while (1) {
+		struct etnaviv_gem_object *o;
+		struct etnaviv_vram_mapping *m, *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);
+
+		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;
+		}
+
+		/* 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) {
+			free = etnaviv_gem_get_vram_mapping(o, mmu);
+			if (!free)
+				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(&free->scan_node, &list);
+			if (drm_mm_scan_add_block(&free->vram_node)) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found) {
+			/* Nothing found, clean up and fail */
+			list_for_each_entry_safe(m, n, &list, scan_node)
+				BUG_ON(drm_mm_scan_remove_block(&m->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(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(m, n, &list, scan_node) {
+			list_del_init(&m->scan_node);
+			etnaviv_iommu_unmap_gem(m);
+		}
+
+		/*
+		 * 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) {
+		kfree(mapping);
+		return ret;
+	}
+
+	mmu->last_iova = node->start + etnaviv_obj->base.size;
+	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(mapping);
+		return ret;
+	}
+
+	list_add_tail(&mapping->obj_node, &etnaviv_obj->vram_list);
+	if (out_mapping)
+		*out_mapping = mapping;
+
+	return ret;
+}
+
+void etnaviv_iommu_unmap_gem(struct etnaviv_vram_mapping *mapping)
+{
+	struct etnaviv_iommu *mmu;
+	struct etnaviv_gem_object *etnaviv_obj;
+
+	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)
+{
+	drm_mm_takedown(&mmu->mm);
+	iommu_domain_free(mmu->domain);
+	kfree(mmu);
+}
+
+struct etnaviv_iommu *etnaviv_iommu_new(struct 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;
+
+	drm_mm_init(&mmu->mm, domain->geometry.aperture_start,
+		    domain->geometry.aperture_end -
+		      domain->geometry.aperture_start + 1);
+
+	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..444ef296d2b4
--- /dev/null
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -0,0 +1,58 @@
+/*
+ * 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/>.
+ */
+
+#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_vram_mapping;
+
+struct etnaviv_iommu {
+	struct device *dev;
+	struct iommu_domain *domain;
+
+	enum etnaviv_iommu_version version;
+
+	/* memory manager for GPU address area */
+	struct drm_mm mm;
+	u32 last_iova;
+	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, u32 iova,
+	struct sg_table *sgt, unsigned len, int prot);
+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, u32 memory_base,
+	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,
+	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..368218304566
--- /dev/null
+++ b/drivers/staging/etnaviv/state.xml.h
@@ -0,0 +1,351 @@
+#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:
+- 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
+*/
+
+
+#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_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
+
+#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..0064f2640396
--- /dev/null
+++ b/drivers/staging/etnaviv/state_hi.xml.h
@@ -0,0 +1,407 @@
+#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:
+- 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
+*/
+
+
+#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_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
+
+#define VIVS_MC_128B_MERGE					0x00000558
+
+
+#endif /* STATE_HI_XML */
diff --git a/include/linux/soc/dove/pmu.h b/include/linux/soc/dove/pmu.h
new file mode 100644
index 000000000000..431dfac595e7
--- /dev/null
+++ b/include/linux/soc/dove/pmu.h
@@ -0,0 +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
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
new file mode 100644
index 000000000000..5f1206b3f9ba
--- /dev/null
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -0,0 +1,215 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __ETNAVIV_DRM_H__
+#define __ETNAVIV_DRM_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 __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
+ *     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.
+ */
+
+/* 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 {
+	__s64 tv_sec;          /* seconds */
+	__s64 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 ETNA_MAX_PIPES 4
+
+struct drm_etnaviv_param {
+	__u32 pipe;           /* in */
+	__u32 param;          /* in, ETNAVIV_PARAM_x */
+	__u64 value;          /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define ETNA_BO_CACHE_MASK   0x000f0000
+/* cache modes */
+#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 {
+	__u64 size;           /* in */
+	__u32 flags;          /* in, mask of ETNA_BO_x */
+	__u32 handle;         /* out */
+};
+
+struct drm_etnaviv_gem_info {
+	__u32 handle;         /* in */
+	__u32 pad;
+	__u64 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 {
+	__u32 handle;         /* in */
+	__u32 op;             /* in, mask of ETNA_PREP_x */
+	struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+struct drm_etnaviv_gem_cpu_fini {
+	__u32 handle;         /* in */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ * 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 {
+	__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 */
+};
+
+/* 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 {
+	__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
+ * 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 {
+	__u32 fence;          /* out */
+	__u32 pipe;           /* in */
+	__u32 exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
+	__u32 nr_bos;         /* in, number of submit_bo's */
+	__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 relocs;         /* in, ptr to array of submit_reloc's */
+	__u64 stream;         /* in, ptr to cmdstream */
+};
+
+/* 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 {
+	__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 {
+	__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 */
+};
+
+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
+ */
+#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_GEM_USERPTR        0x08
+#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)
+#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)
+#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)
+
+#endif /* __ETNAVIV_DRM_H__ */
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 */
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index 132bb83f8e99..f9ea2c4e8b50 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,11 +1,20 @@
 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
 	  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-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 3a36d60e1785..4e663e86c733 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,207 @@
 	(SNDRV_PCM_FMTBIT_S16_LE | \
 	 SNDRV_PCM_FMTBIT_S24_LE)
 
+/* 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.
+ */
+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)
 {
@@ -107,7 +309,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 +353,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 +432,38 @@ 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 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))
@@ -261,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);
 
@@ -329,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);
 
@@ -396,11 +635,25 @@ 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;
 
+	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;
+		}
+	}
+
 	/* put system in a "safe" state : */
 	/* disable audio interrupts */
 	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
@@ -431,97 +684,132 @@ 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,
+	.hw_free	= kirkwood_i2s_hw_free,
+	.prepare	= kirkwood_i2s_prepare,
 	.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,
+		.formats = KIRKWOOD_FE_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,
-	},
-	.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 = {
@@ -531,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);
@@ -544,6 +834,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))
@@ -555,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) {
@@ -584,14 +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");
-			clk_prepare_enable(priv->extclk);
-			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) {
@@ -602,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;
@@ -615,14 +942,11 @@ 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:
-	if (!IS_ERR(priv->extclk))
-		clk_disable_unprepare(priv->extclk);
 	clk_disable_unprepare(priv->clk);
 
 	return err;
@@ -635,8 +959,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-spdif.c b/sound/soc/kirkwood/kirkwood-spdif.c
new file mode 100644
index 000000000000..8cf82ca07fe0
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-spdif.c
@@ -0,0 +1,147 @@
+/*
+ * kirkwood-spdif.c
+ *
+ * (c) 2012 Sebastian Hesselbarth <sebastian.hesselbarth@googlemail.com>
+ *
+ *  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>
+
+#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",
+		.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",
+		.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,
+	},
+};
+
+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;
+	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 = 2;
+	card->dapm_routes = routes;
+	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");
+		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 <sebastian.hesselbarth@googlemail.com>");
+MODULE_DESCRIPTION("ALSA SoC kirkwood SPDIF audio driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kirkwood-spdif-audio");
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
index 90e32a781424..69cbb4b627b8 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)
@@ -131,18 +142,43 @@
 #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;
 	int burst;
+	bool extclk_enabled;
+	bool iec958_switch;
+	struct mutex iec958_mutex;
+	u8 iec958_usr_status[24];
+	u8 iec958_pcm_status[24];
 };
 
 extern struct snd_soc_platform_driver kirkwood_soc_platform;
 
+#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
