| // SPDX-License-Identifier: MIT |
| /* |
| * Copyright © 2024 Intel Corporation |
| */ |
| |
| #include <linux/cleanup.h> |
| #include <drm/drm_managed.h> |
| |
| #include "xe_assert.h" |
| #include "xe_bo.h" |
| #include "xe_gt_printk.h" |
| #include "xe_guc.h" |
| #include "xe_guc_buf.h" |
| #include "xe_sa.h" |
| |
| static struct xe_guc *cache_to_guc(struct xe_guc_buf_cache *cache) |
| { |
| return container_of(cache, struct xe_guc, buf); |
| } |
| |
| static struct xe_gt *cache_to_gt(struct xe_guc_buf_cache *cache) |
| { |
| return guc_to_gt(cache_to_guc(cache)); |
| } |
| |
| /** |
| * xe_guc_buf_cache_init() - Initialize the GuC Buffer Cache. |
| * @cache: the &xe_guc_buf_cache to initialize |
| * |
| * The Buffer Cache allows to obtain a reusable buffer that can be used to pass |
| * indirect H2G data to GuC without a need to create a ad-hoc allocation. |
| * |
| * Return: 0 on success or a negative error code on failure. |
| */ |
| int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache) |
| { |
| struct xe_gt *gt = cache_to_gt(cache); |
| struct xe_sa_manager *sam; |
| |
| sam = __xe_sa_bo_manager_init(gt_to_tile(gt), SZ_8K, 0, sizeof(u32)); |
| if (IS_ERR(sam)) |
| return PTR_ERR(sam); |
| cache->sam = sam; |
| |
| xe_gt_dbg(gt, "reusable buffer with %u dwords at %#x for %ps\n", |
| xe_guc_buf_cache_dwords(cache), xe_bo_ggtt_addr(sam->bo), |
| __builtin_return_address(0)); |
| return 0; |
| } |
| |
| /** |
| * xe_guc_buf_cache_dwords() - Number of dwords the GuC Buffer Cache supports. |
| * @cache: the &xe_guc_buf_cache to query |
| * |
| * Return: a size of the largest reusable buffer (in dwords) |
| */ |
| u32 xe_guc_buf_cache_dwords(struct xe_guc_buf_cache *cache) |
| { |
| return cache->sam ? cache->sam->base.size / sizeof(u32) : 0; |
| } |
| |
| /** |
| * xe_guc_buf_reserve() - Reserve a new sub-allocation. |
| * @cache: the &xe_guc_buf_cache where reserve sub-allocation |
| * @dwords: the requested size of the buffer in dwords |
| * |
| * Use xe_guc_buf_is_valid() to check if returned buffer reference is valid. |
| * Must use xe_guc_buf_release() to release a sub-allocation. |
| * |
| * Return: a &xe_guc_buf of new sub-allocation. |
| */ |
| struct xe_guc_buf xe_guc_buf_reserve(struct xe_guc_buf_cache *cache, u32 dwords) |
| { |
| struct drm_suballoc *sa; |
| |
| if (cache->sam) |
| sa = __xe_sa_bo_new(cache->sam, dwords * sizeof(u32), GFP_ATOMIC); |
| else |
| sa = ERR_PTR(-EOPNOTSUPP); |
| |
| return (struct xe_guc_buf){ .sa = sa }; |
| } |
| |
| /** |
| * xe_guc_buf_from_data() - Reserve a new sub-allocation using data. |
| * @cache: the &xe_guc_buf_cache where reserve sub-allocation |
| * @data: the data to flush the sub-allocation |
| * @size: the size of the data |
| * |
| * Similar to xe_guc_buf_reserve() but flushes @data to the GPU memory. |
| * |
| * Return: a &xe_guc_buf of new sub-allocation. |
| */ |
| struct xe_guc_buf xe_guc_buf_from_data(struct xe_guc_buf_cache *cache, |
| const void *data, size_t size) |
| { |
| struct drm_suballoc *sa; |
| |
| sa = __xe_sa_bo_new(cache->sam, size, GFP_ATOMIC); |
| if (!IS_ERR(sa)) |
| memcpy(xe_sa_bo_cpu_addr(sa), data, size); |
| |
| return (struct xe_guc_buf){ .sa = sa }; |
| } |
| |
| /** |
| * xe_guc_buf_release() - Release a sub-allocation. |
| * @buf: the &xe_guc_buf to release |
| * |
| * Releases a sub-allocation reserved by the xe_guc_buf_reserve(). |
| */ |
| void xe_guc_buf_release(const struct xe_guc_buf buf) |
| { |
| if (xe_guc_buf_is_valid(buf)) |
| xe_sa_bo_free(buf.sa, NULL); |
| } |
| |
| /** |
| * xe_guc_buf_flush() - Copy the data from the sub-allocation to the GPU memory. |
| * @buf: the &xe_guc_buf to flush |
| * |
| * Return: a GPU address of the sub-allocation. |
| */ |
| u64 xe_guc_buf_flush(const struct xe_guc_buf buf) |
| { |
| xe_sa_bo_flush_write(buf.sa); |
| return xe_sa_bo_gpu_addr(buf.sa); |
| } |
| |
| /** |
| * xe_guc_buf_cpu_ptr() - Obtain a CPU pointer to the sub-allocation. |
| * @buf: the &xe_guc_buf to query |
| * |
| * Return: a CPU pointer of the sub-allocation. |
| */ |
| void *xe_guc_buf_cpu_ptr(const struct xe_guc_buf buf) |
| { |
| return xe_sa_bo_cpu_addr(buf.sa); |
| } |
| |
| /** |
| * xe_guc_buf_gpu_addr() - Obtain a GPU address of the sub-allocation. |
| * @buf: the &xe_guc_buf to query |
| * |
| * Return: a GPU address of the sub-allocation. |
| */ |
| u64 xe_guc_buf_gpu_addr(const struct xe_guc_buf buf) |
| { |
| return xe_sa_bo_gpu_addr(buf.sa); |
| } |
| |
| /** |
| * xe_guc_cache_gpu_addr_from_ptr() - Lookup a GPU address using the pointer. |
| * @cache: the &xe_guc_buf_cache with sub-allocations |
| * @ptr: the CPU pointer of the sub-allocation |
| * @size: the size of the data |
| * |
| * Return: a GPU address on success or 0 if the pointer was unrelated. |
| */ |
| u64 xe_guc_cache_gpu_addr_from_ptr(struct xe_guc_buf_cache *cache, const void *ptr, u32 size) |
| { |
| ptrdiff_t offset = ptr - cache->sam->cpu_ptr; |
| |
| if (offset < 0 || offset + size > cache->sam->base.size) |
| return 0; |
| |
| return cache->sam->gpu_addr + offset; |
| } |
| |
| #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST) |
| #include "tests/xe_guc_buf_kunit.c" |
| #endif |