diff options
author | Dave Airlie <airlied@redhat.com> | 2017-10-14 09:39:56 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-10-14 09:39:56 +1000 |
commit | 787e1b74b77d4aa910f40dd5f08edb5da27200bd (patch) | |
tree | 0fff8873b0881e82fe679a6c1e75dbd7d7127c4b /drivers | |
parent | 972805c137ea1888a8e754b61f7ecdafaad765ed (diff) | |
parent | 8272170f7da34e2554bed10ab76582094ea6816d (diff) | |
download | linux-787e1b74b77d4aa910f40dd5f08edb5da27200bd.tar.gz linux-787e1b74b77d4aa910f40dd5f08edb5da27200bd.tar.bz2 linux-787e1b74b77d4aa910f40dd5f08edb5da27200bd.zip |
Merge branch 'etnaviv/next' of https://git.pengutronix.de/git/lst/linux into drm-next
Most notable addition this time is the support for the GPU performance
counters by Christian. This has been in the making for some time and it
has matured a lot. Since this is adding UAPI, the corresponding WIP
userspace can be found at [1] mesa/libdrm repos. I expect that
Christian sends out the final userspace patches for this once you have
pulled the kernel bits.
Philipp optimized the probe path, so etnaviv gets out of the way for
systems that want to boot real quick.
I've done mostly cleanups, disentangling etnaviv from the IOMMU API,
with some MMUv1 optimizations on the way.
* 'etnaviv/next' of https://git.pengutronix.de/git/lst/linux: (36 commits)
drm/etnaviv: remove unnecessary clock stabilization delay
drm/etnaviv: reduce reset delay
drm/etnaviv: remove unused function etnaviv_gem_new
drm/etnaviv: remove stale comment
drm/etnaviv: submit supports performance monitor requests
drm/etnaviv: enable debug registers on demand
drm/etnaviv: need to disable clock gating when doing profiling
drm/etnaviv: add MC perf domain
drm/etnaviv: add TX perf domain
drm/etnaviv: add RA perf domain
drm/etnaviv: add SE perf domain
drm/etnaviv: add PA perf domain
drm/etnaviv: add SH perf domain
drm/etnaviv: add PE perf domain
drm/etnaviv: add HI perf domain
drm/etnaviv: use 'sync points' for performance monitor requests
drm/etnaviv: clear alloced event
drm/etnaviv: add 'sync point' support
drm/etnaviv: add performance monitor request processing
drm/etnaviv: copy pmrs from userspace
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/etnaviv/Kconfig | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_buffer.c | 36 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_drv.c | 39 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_drv.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_gem.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 69 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 217 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_iommu.c | 197 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_iommu.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c | 120 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 106 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_mmu.h | 36 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_perfmon.c | 495 | ||||
-rw-r--r-- | drivers/gpu/drm/etnaviv/etnaviv_perfmon.h | 49 |
18 files changed, 1106 insertions, 329 deletions
diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index 38b477b5fbf9..a29b8f59eb15 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -7,8 +7,6 @@ config DRM_ETNAVIV select SHMEM select SYNC_FILE select TMPFS - select IOMMU_API - select IOMMU_SUPPORT select WANT_DEV_COREDUMP select CMA if HAVE_DMA_CONTIGUOUS select DMA_CMA if HAVE_DMA_CONTIGUOUS diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 4f76c992043f..15c3bfa89a79 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -10,6 +10,7 @@ etnaviv-y := \ etnaviv_gpu.o \ etnaviv_iommu_v2.o \ etnaviv_iommu.o \ - etnaviv_mmu.o + etnaviv_mmu.o \ + etnaviv_perfmon.o obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c index ed9588f36bc9..9e7098e3207f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c @@ -250,6 +250,42 @@ void etnaviv_buffer_end(struct etnaviv_gpu *gpu) } } +/* Append a 'sync point' to the ring buffer. */ +void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event) +{ + struct etnaviv_cmdbuf *buffer = gpu->buffer; + unsigned int waitlink_offset = buffer->user_size - 16; + u32 dwords, target; + + /* + * We need at most 3 dwords in the return target: + * 1 event + 1 end + 1 wait + 1 link. + */ + dwords = 4; + target = etnaviv_buffer_reserve(gpu, buffer, dwords); + + /* Signal sync point event */ + CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | + VIVS_GL_EVENT_FROM_PE); + + /* Stop the FE to 'pause' the GPU */ + CMD_END(buffer); + + /* Append waitlink */ + CMD_WAIT(buffer); + CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) + + buffer->user_size - 4); + + /* + * Kick off the 'sync point' command by replacing the previous + * WAIT with a link to the address in the ring buffer. + */ + etnaviv_buffer_replace_wait(buffer, waitlink_offset, + VIV_FE_LINK_HEADER_OP_LINK | + VIV_FE_LINK_HEADER_PREFETCH(dwords), + target); +} + /* Append a command buffer to the ring buffer. */ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_cmdbuf *cmdbuf) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c index 633e0f07cbac..66ac79558bbd 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c @@ -19,6 +19,7 @@ #include "etnaviv_cmdbuf.h" #include "etnaviv_gpu.h" #include "etnaviv_mmu.h" +#include "etnaviv_perfmon.h" #define SUBALLOC_SIZE SZ_256K #define SUBALLOC_GRANULE SZ_4K @@ -87,9 +88,10 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) struct etnaviv_cmdbuf * etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size, - size_t nr_bos) + size_t nr_bos, size_t nr_pmrs) { struct etnaviv_cmdbuf *cmdbuf; + struct etnaviv_perfmon_request *pmrs; size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]), sizeof(*cmdbuf)); int granule_offs, order, ret; @@ -98,6 +100,12 @@ etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size, if (!cmdbuf) return NULL; + sz = sizeof(*pmrs) * nr_pmrs; + pmrs = kzalloc(sz, GFP_KERNEL); + if (!pmrs) + goto out_free_cmdbuf; + + cmdbuf->pmrs = pmrs; cmdbuf->suballoc = suballoc; cmdbuf->size = size; @@ -124,6 +132,10 @@ retry: cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset; return cmdbuf; + +out_free_cmdbuf: + kfree(cmdbuf); + return NULL; } void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) @@ -139,6 +151,7 @@ void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) suballoc->free_space = 1; mutex_unlock(&suballoc->lock); wake_up_all(&suballoc->free_event); + kfree(cmdbuf->pmrs); kfree(cmdbuf); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h index 80d78076c679..b6348b9f2a9d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h @@ -21,6 +21,7 @@ struct etnaviv_gpu; struct etnaviv_cmdbuf_suballoc; +struct etnaviv_perfmon_request; struct etnaviv_cmdbuf { /* suballocator this cmdbuf is allocated from */ @@ -38,6 +39,9 @@ struct etnaviv_cmdbuf { u32 exec_state; /* per GPU in-flight list */ struct list_head node; + /* perfmon requests */ + unsigned int nr_pmrs; + struct etnaviv_perfmon_request *pmrs; /* BOs attached to this command buffer */ unsigned int nr_bos; struct etnaviv_vram_mapping *bo_map[0]; @@ -49,7 +53,7 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc); struct etnaviv_cmdbuf * etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size, - size_t nr_bos); + size_t nr_bos, size_t nr_pmrs); void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf); u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 2cb4773823c2..3fadb8d45e51 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -23,6 +23,7 @@ #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" +#include "etnaviv_perfmon.h" #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING static bool reglog; @@ -451,6 +452,40 @@ static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data, return ret; } +static int etnaviv_ioctl_pm_query_dom(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct etnaviv_drm_private *priv = dev->dev_private; + struct drm_etnaviv_pm_domain *args = data; + struct etnaviv_gpu *gpu; + + if (args->pipe >= ETNA_MAX_PIPES) + return -EINVAL; + + gpu = priv->gpu[args->pipe]; + if (!gpu) + return -ENXIO; + + return etnaviv_pm_query_dom(gpu, args); +} + +static int etnaviv_ioctl_pm_query_sig(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct etnaviv_drm_private *priv = dev->dev_private; + struct drm_etnaviv_pm_signal *args = data; + struct etnaviv_gpu *gpu; + + if (args->pipe >= ETNA_MAX_PIPES) + return -EINVAL; + + gpu = priv->gpu[args->pipe]; + if (!gpu) + return -ENXIO; + + return etnaviv_pm_query_sig(gpu, args); +} + static const struct drm_ioctl_desc etnaviv_ioctls[] = { #define ETNA_IOCTL(n, func, flags) \ DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags) @@ -463,6 +498,8 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = { ETNA_IOCTL(WAIT_FENCE, wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), ETNA_IOCTL(GEM_USERPTR, gem_userptr, DRM_AUTH|DRM_RENDER_ALLOW), ETNA_IOCTL(GEM_WAIT, gem_wait, DRM_AUTH|DRM_RENDER_ALLOW), + ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_AUTH|DRM_RENDER_ALLOW), + ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { @@ -513,7 +550,7 @@ static struct drm_driver etnaviv_drm_driver = { .desc = "etnaviv DRM", .date = "20151214", .major = 1, - .minor = 1, + .minor = 2, }; /* diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 058389f93b69..d249acb6da08 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -26,7 +26,6 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/list.h> -#include <linux/iommu.h> #include <linux/types.h> #include <linux/sizes.h> @@ -92,15 +91,12 @@ 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); u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr); void etnaviv_buffer_end(struct etnaviv_gpu *gpu); +void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event); void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_cmdbuf *cmdbuf); void etnaviv_validate_init(void); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 57881167ccd2..5884ab623e0a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -704,25 +704,6 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, 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_put_unlocked(obj); - return ERR_PTR(ret); - } - - return obj; -} - int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, struct reservation_object *robj, const struct etnaviv_gem_ops *ops, struct etnaviv_gem_object **res) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 46dfe0737f43..ff911541a190 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -21,6 +21,7 @@ #include "etnaviv_drv.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" +#include "etnaviv_perfmon.h" /* * Cmdstream submission: @@ -283,6 +284,54 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, return 0; } +static int submit_perfmon_validate(struct etnaviv_gem_submit *submit, + struct etnaviv_cmdbuf *cmdbuf, + const struct drm_etnaviv_gem_submit_pmr *pmrs, + u32 nr_pms) +{ + u32 i; + + for (i = 0; i < nr_pms; i++) { + const struct drm_etnaviv_gem_submit_pmr *r = pmrs + i; + struct etnaviv_gem_submit_bo *bo; + int ret; + + ret = submit_bo(submit, r->read_idx, &bo); + if (ret) + return ret; + + /* at offset 0 a sequence number gets stored used for userspace sync */ + if (r->read_offset == 0) { + DRM_ERROR("perfmon request: offset is 0"); + return -EINVAL; + } + + if (r->read_offset >= bo->obj->base.size - sizeof(u32)) { + DRM_ERROR("perfmon request: offset %u outside object", i); + return -EINVAL; + } + + if (r->flags & ~(ETNA_PM_PROCESS_PRE | ETNA_PM_PROCESS_POST)) { + DRM_ERROR("perfmon request: flags are not valid"); + return -EINVAL; + } + + if (etnaviv_pm_req_validate(r, cmdbuf->exec_state)) { + DRM_ERROR("perfmon request: domain or signal not valid"); + return -EINVAL; + } + + cmdbuf->pmrs[i].flags = r->flags; + cmdbuf->pmrs[i].domain = r->domain; + cmdbuf->pmrs[i].signal = r->signal; + cmdbuf->pmrs[i].sequence = r->sequence; + cmdbuf->pmrs[i].offset = r->read_offset; + cmdbuf->pmrs[i].bo_vma = etnaviv_gem_vmap(&bo->obj->base); + } + + return 0; +} + static void submit_cleanup(struct etnaviv_gem_submit *submit) { unsigned i; @@ -306,6 +355,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, struct etnaviv_drm_private *priv = dev->dev_private; struct drm_etnaviv_gem_submit *args = data; struct drm_etnaviv_gem_submit_reloc *relocs; + struct drm_etnaviv_gem_submit_pmr *pmrs; struct drm_etnaviv_gem_submit_bo *bos; struct etnaviv_gem_submit *submit; struct etnaviv_cmdbuf *cmdbuf; @@ -347,11 +397,12 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, */ bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL); relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL); + pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL); stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL); cmdbuf = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, ALIGN(args->stream_size, 8) + 8, - args->nr_bos); - if (!bos || !relocs || !stream || !cmdbuf) { + args->nr_bos, args->nr_pmrs); + if (!bos || !relocs || !pmrs || !stream || !cmdbuf) { ret = -ENOMEM; goto err_submit_cmds; } @@ -373,6 +424,14 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, goto err_submit_cmds; } + ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs), + args->nr_pmrs * sizeof(*pmrs)); + if (ret) { + ret = -EFAULT; + goto err_submit_cmds; + } + cmdbuf->nr_pmrs = args->nr_pmrs; + ret = copy_from_user(stream, u64_to_user_ptr(args->stream), args->stream_size); if (ret) { @@ -441,6 +500,10 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, if (ret) goto out; + ret = submit_perfmon_validate(submit, cmdbuf, pmrs, args->nr_pmrs); + if (ret) + goto out; + memcpy(cmdbuf->vaddr, stream, args->stream_size); cmdbuf->user_size = ALIGN(args->stream_size, 8); @@ -496,6 +559,8 @@ err_submit_cmds: kvfree(bos); if (relocs) kvfree(relocs); + if (pmrs) + kvfree(pmrs); return ret; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index fc9a6a83dfc7..8197e1d6ed11 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -25,6 +25,7 @@ #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" +#include "etnaviv_perfmon.h" #include "common.xml.h" #include "state.xml.h" #include "state_hi.xml.h" @@ -420,9 +421,10 @@ static void etnaviv_gpu_update_clock(struct etnaviv_gpu *gpu) gpu->base_rate_shader >> gpu->freq_scale); } else { unsigned int fscale = 1 << (6 - gpu->freq_scale); - u32 clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | - VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale); + u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); + clock &= ~VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK; + clock |= VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale); etnaviv_gpu_load_clock(gpu, clock); } } @@ -433,24 +435,14 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) 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)) { /* enable clock */ - etnaviv_gpu_update_clock(gpu); - - control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); - - /* Wait for stable clock. Vivante's code waited for 1ms */ - usleep_range(1000, 10000); + unsigned int fscale = 1 << (6 - gpu->freq_scale); + control = VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale); + etnaviv_gpu_load_clock(gpu, control); /* isolate the GPU. */ control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; @@ -461,7 +453,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); /* wait for reset. */ - msleep(1); + usleep_range(10, 20); /* reset soft reset bit. */ control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET; @@ -490,6 +482,10 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) continue; } + /* disable debug registers, as they are not normally needed */ + control |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS; + gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); + failed = false; break; } @@ -721,7 +717,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) } /* Create buffer: */ - gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0); + gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0, 0); if (!gpu->buffer) { ret = -ENOMEM; dev_err(gpu->dev, "could not create command buffer\n"); @@ -739,10 +735,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) /* Setup event management */ spin_lock_init(&gpu->event_spinlock); init_completion(&gpu->event_free); - for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { - gpu->event[i].used = false; + bitmap_zero(gpu->event_bitmap, ETNA_NR_EVENTS); + for (i = 0; i < ARRAY_SIZE(gpu->event); i++) complete(&gpu->event_free); - } /* Now program the hardware */ mutex_lock(&gpu->lock); @@ -926,7 +921,7 @@ static void recover_worker(struct work_struct *work) struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, recover_work); unsigned long flags; - unsigned int i; + unsigned int i = 0; dev_err(gpu->dev, "hangcheck recover!\n"); @@ -945,14 +940,12 @@ static void recover_worker(struct work_struct *work) /* 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; + for_each_set_bit_from(i, gpu->event_bitmap, ETNA_NR_EVENTS) { dma_fence_signal(gpu->event[i].fence); gpu->event[i].fence = NULL; - gpu->event[i].used = false; complete(&gpu->event_free); } + bitmap_zero(gpu->event_bitmap, ETNA_NR_EVENTS); spin_unlock_irqrestore(&gpu->event_spinlock, flags); gpu->completed_fence = gpu->active_fence; @@ -1140,30 +1133,45 @@ int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj, * event management: */ -static unsigned int event_alloc(struct etnaviv_gpu *gpu) +static int event_alloc(struct etnaviv_gpu *gpu, unsigned nr_events, + unsigned int *events) { - unsigned long ret, flags; - unsigned int i, event = ~0U; + unsigned long flags, timeout = msecs_to_jiffies(10 * 10000); + unsigned i, acquired = 0; - ret = wait_for_completion_timeout(&gpu->event_free, - msecs_to_jiffies(10 * 10000)); - if (!ret) - dev_err(gpu->dev, "wait_for_completion_timeout failed"); + for (i = 0; i < nr_events; i++) { + unsigned long ret; - spin_lock_irqsave(&gpu->event_spinlock, flags); + ret = wait_for_completion_timeout(&gpu->event_free, timeout); - /* 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; + if (!ret) { + dev_err(gpu->dev, "wait_for_completion_timeout failed"); + goto out; } + + acquired++; + timeout = ret; + } + + spin_lock_irqsave(&gpu->event_spinlock, flags); + + for (i = 0; i < nr_events; i++) { + int event = find_first_zero_bit(gpu->event_bitmap, ETNA_NR_EVENTS); + + events[i] = event; + memset(&gpu->event[event], 0, sizeof(struct etnaviv_event)); + set_bit(event, gpu->event_bitmap); } spin_unlock_irqrestore(&gpu->event_spinlock, flags); - return event; + return 0; + +out: + for (i = 0; i < acquired; i++) + complete(&gpu->event_free); + + return -EBUSY; } static void event_free(struct etnaviv_gpu *gpu, unsigned int event) @@ -1172,12 +1180,12 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event) spin_lock_irqsave(&gpu->event_spinlock, flags); - if (gpu->event[event].used == false) { + if (!test_bit(event, gpu->event_bitmap)) { 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; + clear_bit(event, gpu->event_bitmap); spin_unlock_irqrestore(&gpu->event_spinlock, flags); complete(&gpu->event_free); @@ -1311,12 +1319,71 @@ void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu) pm_runtime_put_autosuspend(gpu->dev); } +static void sync_point_perfmon_sample(struct etnaviv_gpu *gpu, + struct etnaviv_event *event, unsigned int flags) +{ + const struct etnaviv_cmdbuf *cmdbuf = event->cmdbuf; + unsigned int i; + + for (i = 0; i < cmdbuf->nr_pmrs; i++) { + const struct etnaviv_perfmon_request *pmr = cmdbuf->pmrs + i; + + if (pmr->flags == flags) + etnaviv_perfmon_process(gpu, pmr); + } +} + +static void sync_point_perfmon_sample_pre(struct etnaviv_gpu *gpu, + struct etnaviv_event *event) +{ + u32 val; + + /* disable clock gating */ + val = gpu_read(gpu, VIVS_PM_POWER_CONTROLS); + val &= ~VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING; + gpu_write(gpu, VIVS_PM_POWER_CONTROLS, val); + + /* enable debug register */ + val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); + val &= ~VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS; + gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val); + + sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_PRE); +} + +static void sync_point_perfmon_sample_post(struct etnaviv_gpu *gpu, + struct etnaviv_event *event) +{ + const struct etnaviv_cmdbuf *cmdbuf = event->cmdbuf; + unsigned int i; + u32 val; + + sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_POST); + + for (i = 0; i < cmdbuf->nr_pmrs; i++) { + const struct etnaviv_perfmon_request *pmr = cmdbuf->pmrs + i; + + *pmr->bo_vma = pmr->sequence; + } + + /* disable debug register */ + val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); + val |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS; + gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val); + + /* enable clock gating */ + val = gpu_read(gpu, VIVS_PM_POWER_CONTROLS); + val |= VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING; + gpu_write(gpu, VIVS_PM_POWER_CONTROLS, val); +} + + /* 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_cmdbuf *cmdbuf) { struct dma_fence *fence; - unsigned int event, i; + unsigned int i, nr_events = 1, event[3]; int ret; ret = etnaviv_gpu_pm_get_sync(gpu); @@ -1332,10 +1399,19 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, * */ - event = event_alloc(gpu); - if (unlikely(event == ~0U)) { - DRM_ERROR("no free event\n"); - ret = -EBUSY; + /* + * if there are performance monitor requests we need to have + * - a sync point to re-configure gpu and process ETNA_PM_PROCESS_PRE + * requests. + * - a sync point to re-configure gpu, process ETNA_PM_PROCESS_POST requests + * and update the sequence number for userspace. + */ + if (cmdbuf->nr_pmrs) + nr_events = 3; + + ret = event_alloc(gpu, nr_events, event); + if (ret) { + DRM_ERROR("no free events\n"); goto out_pm_put; } @@ -1343,12 +1419,14 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, fence = etnaviv_gpu_fence_alloc(gpu); if (!fence) { - event_free(gpu, event); + for (i = 0; i < nr_events; i++) + event_free(gpu, event[i]); + ret = -ENOMEM; goto out_unlock; } - gpu->event[event].fence = fence; + gpu->event[event[0]].fence = fence; submit->fence = dma_fence_get(fence); gpu->active_fence = submit->fence->seqno; @@ -1358,7 +1436,19 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, gpu->lastctx = cmdbuf->ctx; } - etnaviv_buffer_queue(gpu, event, cmdbuf); + if (cmdbuf->nr_pmrs) { + gpu->event[event[1]].sync_point = &sync_point_perfmon_sample_pre; + gpu->event[event[1]].cmdbuf = cmdbuf; + etnaviv_sync_point_queue(gpu, event[1]); + } + + etnaviv_buffer_queue(gpu, event[0], cmdbuf); + + if (cmdbuf->nr_pmrs) { + gpu->event[event[2]].sync_point = &sync_point_perfmon_sample_post; + gpu->event[event[2]].cmdbuf = cmdbuf; + etnaviv_sync_point_queue(gpu, event[2]); + } cmdbuf->fence = fence; list_add_tail(&cmdbuf->node, &gpu->active_cmd_list); @@ -1394,6 +1484,24 @@ out_pm_put: return ret; } +static void etnaviv_process_sync_point(struct etnaviv_gpu *gpu, + struct etnaviv_event *event) +{ + u32 addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); + + event->sync_point(gpu, event); + etnaviv_gpu_start_fe(gpu, addr + 2, 2); +} + +static void sync_point_worker(struct work_struct *work) +{ + struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, + sync_point_work); + + etnaviv_process_sync_point(gpu, &gpu->event[gpu->sync_point_event]); + event_free(gpu, gpu->sync_point_event); +} + /* * Init/Cleanup: */ @@ -1440,7 +1548,15 @@ static irqreturn_t irq_handler(int irq, void *data) dev_dbg(gpu->dev, "event %u\n", event); + if (gpu->event[event].sync_point) { + gpu->sync_point_event = event; + etnaviv_queue_work(gpu->drm, &gpu->sync_point_work); + } + fence = gpu->event[event].fence; + if (!fence) + continue; + gpu->event[event].fence = NULL; dma_fence_signal(fence); @@ -1645,6 +1761,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master, INIT_LIST_HEAD(&gpu->active_cmd_list); INIT_WORK(&gpu->retire_work, retire_worker); + INIT_WORK(&gpu->sync_point_work, sync_point_worker); INIT_WORK(&gpu->recover_work, recover_worker); init_waitqueue_head(&gpu->fence_event); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index 689cb8f3680c..4f10f147297a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -88,13 +88,17 @@ struct etnaviv_chip_identity { }; struct etnaviv_event { - bool used; struct dma_fence *fence; + struct etnaviv_cmdbuf *cmdbuf; + + void (*sync_point)(struct etnaviv_gpu *gpu, struct etnaviv_event *event); }; struct etnaviv_cmdbuf_suballoc; struct etnaviv_cmdbuf; +#define ETNA_NR_EVENTS 30 + struct etnaviv_gpu { struct drm_device *drm; struct thermal_cooling_device *cooling; @@ -112,7 +116,8 @@ struct etnaviv_gpu { u32 memory_base; /* event management: */ - struct etnaviv_event event[30]; + DECLARE_BITMAP(event_bitmap, ETNA_NR_EVENTS); + struct etnaviv_event event[ETNA_NR_EVENTS]; struct completion event_free; spinlock_t event_spinlock; @@ -133,6 +138,10 @@ struct etnaviv_gpu { /* worker for handling active-list retiring: */ struct work_struct retire_work; + /* worker for handling |