# 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: [] console_cpu_notify+0x28/0x34 # # but task is already holding lock: # (cpu_hotplug.lock#2){+.+.+.}, at: [] 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){+.+.+.}: # [] mutex_lock_nested+0x5c/0x3d8 # [] lru_add_drain_all+0x1c/0x18c # [] migrate_prep+0x10/0x18 # [] alloc_contig_range+0xd0/0x2cc # [] cma_alloc+0xe0/0x1ac # [] dma_alloc_from_contiguous+0x3c/0x44 # [] __alloc_from_contiguous+0x3c/0xe8 # [] atomic_pool_init+0x6c/0x15c # [] do_one_initcall+0x88/0x1d8 # [] kernel_init_freeable+0x110/0x1dc # [] kernel_init+0x10/0xec # [] ret_from_fork+0x14/0x24 # # -> #4 (lock){+.+...}: # [] mutex_lock_nested+0x5c/0x3d8 # [] lru_add_drain_all+0x1c/0x18c # [] migrate_prep+0x10/0x18 # [] alloc_contig_range+0xd0/0x2cc # [] cma_alloc+0xe0/0x1ac # [] dma_alloc_from_contiguous+0x3c/0x44 # [] __alloc_from_contiguous+0x3c/0xe8 # [] atomic_pool_init+0x6c/0x15c # [] do_one_initcall+0x88/0x1d8 # [] kernel_init_freeable+0x110/0x1dc # [] kernel_init+0x10/0xec # [] ret_from_fork+0x14/0x24 # # -> #3 (cma_mutex){+.+.+.}: # [] mutex_lock_nested+0x5c/0x3d8 # [] cma_alloc+0xd0/0x1ac # [] dma_alloc_from_contiguous+0x3c/0x44 # [] __alloc_from_contiguous+0x3c/0xe8 # [] __dma_alloc+0x15c/0x28c # [] arm_dma_alloc+0xa0/0xa8 # [] etnaviv_iommu_domain_init+0x54/0x138 # [] etnaviv_iommu_domain_alloc+0x4c/0xd8 # [] etnaviv_gpu_init+0x380/0x620 # [] etnaviv_load+0xc0/0x128 # [] drm_dev_register+0xac/0x10c # [] drm_platform_init+0x48/0xd4 # [] etnaviv_bind+0x18/0x20 # [] try_to_bring_up_master+0x140/0x17c # [] component_master_add_with_match+0x84/0xe0 # [] etnaviv_pdev_probe+0xb4/0x104 # [] platform_drv_probe+0x50/0xac # [] driver_probe_device+0x114/0x234 # [] __driver_attach+0x9c/0xa0 # [] bus_for_each_dev+0x5c/0x90 # [] driver_attach+0x24/0x28 # [] bus_add_driver+0xe0/0x1d8 # [] driver_register+0x80/0xfc # [] __platform_driver_register+0x50/0x64 # [] etnaviv_init+0x2c/0x4c # [] do_one_initcall+0x88/0x1d8 # [] kernel_init_freeable+0x110/0x1dc # [] kernel_init+0x10/0xec # [] ret_from_fork+0x14/0x24 # # -> #2 (&dev->struct_mutex){+.+.+.}: # [] mutex_lock_nested+0x5c/0x3d8 # [] drm_gem_mmap+0x3c/0xd4 # [] drm_gem_cma_mmap+0x14/0x2c # [] mmap_region+0x3d0/0x6a4 # [] do_mmap_pgoff+0x2e4/0x374 # [] vm_mmap_pgoff+0x6c/0x9c # [] SyS_mmap_pgoff+0x94/0xb8 # [] ret_fast_syscall+0x0/0x4c # # -> #1 (&mm->mmap_sem){++++++}: # [] might_fault+0x64/0x98 # [] con_set_unimap+0x160/0x25c # [] vt_ioctl+0x126c/0x1328 # [] tty_ioctl+0x498/0xc5c # [] do_vfs_ioctl+0x84/0x66c # [] SyS_ioctl+0x3c/0x60 # [] ret_fast_syscall+0x0/0x4c # # -> #0 (console_lock){+.+.+.}: # [] lock_acquire+0xb0/0x124 # [] console_lock+0x44/0x6c # [] console_cpu_notify+0x28/0x34 # [] notifier_call_chain+0x4c/0x8c # [] __raw_notifier_call_chain+0x1c/0x24 # [] __cpu_notify+0x34/0x50 # [] cpu_notify+0x18/0x1c # [] cpu_notify_nofail+0x10/0x1c # [] _cpu_down+0x100/0x248 # [] cpu_down+0x2c/0x48 # [] cpu_subsys_offline+0x14/0x18 # [] device_offline+0x90/0xc0 # [] online_store+0x4c/0x74 # [] dev_attr_store+0x20/0x2c # [] sysfs_kf_write+0x54/0x58 # [] kernfs_fop_write+0xfc/0x1ac # [] vfs_write+0xac/0x1b4 # [] SyS_write+0x44/0x90 # [] 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: [] vfs_write+0x18c/0x1b4 # #1: (&of->mutex){+.+.+.}, at: [] kernfs_fop_write+0x88/0x1ac # #2: (s_active#40){.+.+.+}, at: [] kernfs_fop_write+0x90/0x1ac # #3: (device_hotplug_lock){+.+.+.}, at: [] lock_device_hotplug_sysfs+0x14/0x54 # #4: (&dev->mutex){......}, at: [] device_offline+0x44/0xc0 # #5: (cpu_add_remove_lock){+.+.+.}, at: [] cpu_maps_update_begin+0x18/0x20 # #6: (cpu_hotplug.lock){++++++}, at: [] cpu_hotplug_begin+0x0/0xb8 # #7: (cpu_hotplug.lock#2){+.+.+.}, at: [] 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: # [] (dump_backtrace) from [] (show_stack+0x18/0x1c) # [] (show_stack) from [] (dump_stack+0x7c/0x98) # [] (dump_stack) from [] (print_circular_bug+0x28c/0x2d8) # [] (print_circular_bug) from [] (__lock_acquire+0x1acc/0x1bb0) # [] (__lock_acquire) from [] (lock_acquire+0xb0/0x124) # [] (lock_acquire) from [] (console_lock+0x44/0x6c) # [] (console_lock) from [] (console_cpu_notify+0x28/0x34) # [] (console_cpu_notify) from [] (notifier_call_chain+0x4c/0x8c) # [] (notifier_call_chain) from [] (__raw_notifier_call_chain+0x1c/0x24) # [] (__raw_notifier_call_chain) from [] (__cpu_notify+0x34/0x50) # [] (__cpu_notify) from [] (cpu_notify+0x18/0x1c) # [] (cpu_notify) from [] (cpu_notify_nofail+0x10/0x1c) # [] (cpu_notify_nofail) from [] (_cpu_down+0x100/0x248) # [] (_cpu_down) from [] (cpu_down+0x2c/0x48) # [] (cpu_down) from [] (cpu_subsys_offline+0x14/0x18) # [] (cpu_subsys_offline) from [] (device_offline+0x90/0xc0) # [] (device_offline) from [] (online_store+0x4c/0x74) # [] (online_store) from [] (dev_attr_store+0x20/0x2c) # [] (dev_attr_store) from [] (sysfs_kf_write+0x54/0x58) # [] (sysfs_kf_write) from [] (kernfs_fop_write+0xfc/0x1ac) # [] (kernfs_fop_write) from [] (vfs_write+0xac/0x1b4) # [] (vfs_write) from [] (SyS_write+0x44/0x90) # [] (SyS_write) from [] (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 #include #include +#include #include #include #include @@ -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 #include #include -#include #include -#include #include -#include -#include #include #include #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 #include #include +#include #include #include #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 +#include +#include +#include +#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 #include "armada_ioctlP.h" -#ifdef CONFIG_DRM_ARMADA_TDA1998X -#include -#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 = ¶ms, - }, -}; -#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 -#include -#include -#include -#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 #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 -#include -#include -#include -#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 -#include - -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 #include -#include #include #include #include @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 . + */ + +#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 . + */ + +#include + +#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 . + */ + +#include +#include + +#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 "); +MODULE_AUTHOR("Russell King "); +MODULE_AUTHOR("Lucas Stach "); +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 . + */ + +#ifndef __ETNAVIV_DRV_H__ +#define __ETNAVIV_DRV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 . + */ + +#include +#include + +#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 . + */ + +#ifndef __ETNAVIV_GEM_H__ +#define __ETNAVIV_GEM_H__ + +#include +#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 + * + * 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 . + */ + +#include +#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 . + */ + +#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 . + */ + +#include +#include +#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 . + */ + +#ifndef __ETNAVIV_GPU_H__ +#define __ETNAVIV_GPU_H__ + +#include +#include + +#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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 . + */ + +#ifndef __ETNAVIV_IOMMU_H__ +#define __ETNAVIV_IOMMU_H__ + +#include +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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 . + */ + +#ifndef __ETNAVIV_IOMMU_V2_H__ +#define __ETNAVIV_IOMMU_V2_H__ + +#include +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 . + */ + +#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 . + */ + +#ifndef __ETNAVIV_MMU_H__ +#define __ETNAVIV_MMU_H__ + +#include + +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 + +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 . + */ + +#ifndef __ETNAVIV_DRM_H__ +#define __ETNAVIV_DRM_H__ + +#include + +/* 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 #include #include #include @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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