diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c new file mode 100644 index 000000000..111a301ce --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c @@ -0,0 +1,178 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <linux/dma-mapping.h> + +#include "amdgpu.h" +#include "amdgpu_ih.h" + +/** + * amdgpu_ih_ring_init - initialize the IH state + * + * @adev: amdgpu_device pointer + * @ih: ih ring to initialize + * @ring_size: ring size to allocate + * @use_bus_addr: true when we can use dma_alloc_coherent + * + * Initializes the IH state and allocates a buffer + * for the IH ring buffer. + * Returns 0 for success, errors for failure. + */ +int amdgpu_ih_ring_init(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih, + unsigned ring_size, bool use_bus_addr) +{ + u32 rb_bufsz; + int r; + + /* Align ring size */ + rb_bufsz = order_base_2(ring_size / 4); + ring_size = (1 << rb_bufsz) * 4; + ih->ring_size = ring_size; + ih->ptr_mask = ih->ring_size - 1; + ih->rptr = 0; + ih->use_bus_addr = use_bus_addr; + + if (use_bus_addr) { + dma_addr_t dma_addr; + + if (ih->ring) + return 0; + + /* add 8 bytes for the rptr/wptr shadows and + * add them to the end of the ring allocation. + */ + ih->ring = dma_alloc_coherent(adev->dev, ih->ring_size + 8, + &dma_addr, GFP_KERNEL); + if (ih->ring == NULL) + return -ENOMEM; + + ih->gpu_addr = dma_addr; + ih->wptr_addr = dma_addr + ih->ring_size; + ih->wptr_cpu = &ih->ring[ih->ring_size / 4]; + ih->rptr_addr = dma_addr + ih->ring_size + 4; + ih->rptr_cpu = &ih->ring[(ih->ring_size / 4) + 1]; + } else { + unsigned wptr_offs, rptr_offs; + + r = amdgpu_device_wb_get(adev, &wptr_offs); + if (r) + return r; + + r = amdgpu_device_wb_get(adev, &rptr_offs); + if (r) { + amdgpu_device_wb_free(adev, wptr_offs); + return r; + } + + r = amdgpu_bo_create_kernel(adev, ih->ring_size, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_GTT, + &ih->ring_obj, &ih->gpu_addr, + (void **)&ih->ring); + if (r) { + amdgpu_device_wb_free(adev, rptr_offs); + amdgpu_device_wb_free(adev, wptr_offs); + return r; + } + + ih->wptr_addr = adev->wb.gpu_addr + wptr_offs * 4; + ih->wptr_cpu = &adev->wb.wb[wptr_offs]; + ih->rptr_addr = adev->wb.gpu_addr + rptr_offs * 4; + ih->rptr_cpu = &adev->wb.wb[rptr_offs]; + } + return 0; +} + +/** + * amdgpu_ih_ring_fini - tear down the IH state + * + * @adev: amdgpu_device pointer + * @ih: ih ring to tear down + * + * Tears down the IH state and frees buffer + * used for the IH ring buffer. + */ +void amdgpu_ih_ring_fini(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih) +{ + if (ih->use_bus_addr) { + if (!ih->ring) + return; + + /* add 8 bytes for the rptr/wptr shadows and + * add them to the end of the ring allocation. + */ + dma_free_coherent(adev->dev, ih->ring_size + 8, + (void *)ih->ring, ih->gpu_addr); + ih->ring = NULL; + } else { + amdgpu_bo_free_kernel(&ih->ring_obj, &ih->gpu_addr, + (void **)&ih->ring); + amdgpu_device_wb_free(adev, (ih->wptr_addr - ih->gpu_addr) / 4); + amdgpu_device_wb_free(adev, (ih->rptr_addr - ih->gpu_addr) / 4); + } +} + +/** + * amdgpu_ih_process - interrupt handler + * + * @adev: amdgpu_device pointer + * @ih: ih ring to process + * + * Interrupt hander (VI), walk the IH ring. + * Returns irq process return code. + */ +int amdgpu_ih_process(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih) +{ + unsigned int count = AMDGPU_IH_MAX_NUM_IVS; + u32 wptr; + + if (!ih->enabled || adev->shutdown) + return IRQ_NONE; + + wptr = amdgpu_ih_get_wptr(adev, ih); + +restart_ih: + /* is somebody else already processing irqs? */ + if (atomic_xchg(&ih->lock, 1)) + return IRQ_NONE; + + DRM_DEBUG("%s: rptr %d, wptr %d\n", __func__, ih->rptr, wptr); + + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + + while (ih->rptr != wptr && --count) { + amdgpu_irq_dispatch(adev, ih); + ih->rptr &= ih->ptr_mask; + } + + amdgpu_ih_set_rptr(adev, ih); + atomic_set(&ih->lock, 0); + + /* make sure wptr hasn't changed while processing */ + wptr = amdgpu_ih_get_wptr(adev, ih); + if (wptr != ih->rptr) + goto restart_ih; + + return IRQ_HANDLED; +} + |