Commit 647371a6 authored by Jacek Lawrynowicz's avatar Jacek Lawrynowicz Committed by Daniel Vetter

accel/ivpu: Add GEM buffer object management

Adds four types of GEM-based BOs for the VPU:
  - shmem
  - internal
  - prime

All types are implemented as struct ivpu_bo, based on
struct drm_gem_object. VPU address is allocated when buffer is created
except for imported prime buffers that allocate it in BO_INFO IOCTL due
to missing file_priv arg in gem_prime_import callback.
Internal buffers are pinned on creation, the rest of buffers types
can be pinned on demand (in SUBMIT IOCTL).
Buffer VPU address, allocated pages and mappings are released when the
buffer is destroyed.
Eviction mechanism is planned for future versions.

Add two new IOCTLs: BO_CREATE, BO_INFO
Signed-off-by: default avatarJacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Reviewed-by: default avatarOded Gabbay <ogabbay@kernel.org>
Reviewed-by: default avatarJeffrey Hugo <quic_jhugo@quicinc.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20230117092723.60441-4-jacek.lawrynowicz@linux.intel.com
parent 263b2ba5
......@@ -3,6 +3,7 @@
intel_vpu-y := \
ivpu_drv.o \
ivpu_gem.o \
ivpu_hw_mtl.o \
ivpu_mmu.o \
ivpu_mmu_context.o
......
......@@ -12,8 +12,10 @@
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_prime.h>
#include "ivpu_drv.h"
#include "ivpu_gem.h"
#include "ivpu_hw.h"
#include "ivpu_mmu.h"
#include "ivpu_mmu_context.h"
......@@ -49,6 +51,24 @@ struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv)
return file_priv;
}
struct ivpu_file_priv *ivpu_file_priv_get_by_ctx_id(struct ivpu_device *vdev, unsigned long id)
{
struct ivpu_file_priv *file_priv;
xa_lock_irq(&vdev->context_xa);
file_priv = xa_load(&vdev->context_xa, id);
/* file_priv may still be in context_xa during file_priv_release() */
if (file_priv && !kref_get_unless_zero(&file_priv->ref))
file_priv = NULL;
xa_unlock_irq(&vdev->context_xa);
if (file_priv)
ivpu_dbg(vdev, KREF, "file_priv get by id: ctx %u refcount %u\n",
file_priv->ctx.id, kref_read(&file_priv->ref));
return file_priv;
}
static void file_priv_release(struct kref *ref)
{
struct ivpu_file_priv *file_priv = container_of(ref, struct ivpu_file_priv, ref);
......@@ -57,7 +77,7 @@ static void file_priv_release(struct kref *ref)
ivpu_dbg(vdev, FILE, "file_priv release: ctx %u\n", file_priv->ctx.id);
ivpu_mmu_user_context_fini(vdev, &file_priv->ctx);
WARN_ON(xa_erase_irq(&vdev->context_xa, file_priv->ctx.id) != file_priv);
drm_WARN_ON(&vdev->drm, xa_erase_irq(&vdev->context_xa, file_priv->ctx.id) != file_priv);
kfree(file_priv);
}
......@@ -66,7 +86,7 @@ void ivpu_file_priv_put(struct ivpu_file_priv **link)
struct ivpu_file_priv *file_priv = *link;
struct ivpu_device *vdev = file_priv->vdev;
WARN_ON(!file_priv);
drm_WARN_ON(&vdev->drm, !file_priv);
ivpu_dbg(vdev, KREF, "file_priv put: ctx %u refcount %u\n",
file_priv->ctx.id, kref_read(&file_priv->ref));
......@@ -200,6 +220,8 @@ static void ivpu_postclose(struct drm_device *dev, struct drm_file *file)
static const struct drm_ioctl_desc ivpu_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(IVPU_GET_PARAM, ivpu_get_param_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_SET_PARAM, ivpu_set_param_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_BO_CREATE, ivpu_bo_create_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_BO_INFO, ivpu_bo_info_ioctl, 0),
};
int ivpu_shutdown(struct ivpu_device *vdev)
......@@ -227,6 +249,10 @@ static const struct drm_driver driver = {
.open = ivpu_open,
.postclose = ivpu_postclose,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = ivpu_gem_prime_import,
.gem_prime_mmap = drm_gem_prime_mmap,
.ioctls = ivpu_drm_ioctls,
.num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls),
......
......@@ -115,6 +115,7 @@ extern u8 ivpu_pll_min_ratio;
extern u8 ivpu_pll_max_ratio;
struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv);
struct ivpu_file_priv *ivpu_file_priv_get_by_ctx_id(struct ivpu_device *vdev, unsigned long id);
void ivpu_file_priv_put(struct ivpu_file_priv **link);
int ivpu_shutdown(struct ivpu_device *vdev);
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2023 Intel Corporation
*/
#ifndef __IVPU_GEM_H__
#define __IVPU_GEM_H__
#include <drm/drm_gem.h>
#include <drm/drm_mm.h>
struct dma_buf;
struct ivpu_bo_ops;
struct ivpu_file_priv;
struct ivpu_bo {
struct drm_gem_object base;
const struct ivpu_bo_ops *ops;
struct ivpu_mmu_context *ctx;
struct list_head ctx_node;
struct drm_mm_node mm_node;
struct mutex lock; /* Protects: pages, sgt, mmu_mapped */
struct sg_table *sgt;
struct page **pages;
bool mmu_mapped;
void *kvaddr;
u64 vpu_addr;
u32 handle;
u32 flags;
uintptr_t user_ptr;
};
enum ivpu_bo_type {
IVPU_BO_TYPE_SHMEM = 1,
IVPU_BO_TYPE_INTERNAL,
IVPU_BO_TYPE_PRIME,
};
struct ivpu_bo_ops {
enum ivpu_bo_type type;
const char *name;
int (*alloc_pages)(struct ivpu_bo *bo);
void (*free_pages)(struct ivpu_bo *bo);
int (*map_pages)(struct ivpu_bo *bo);
void (*unmap_pages)(struct ivpu_bo *bo);
};
int ivpu_bo_pin(struct ivpu_bo *bo);
void ivpu_bo_remove_all_bos_from_context(struct ivpu_mmu_context *ctx);
void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p);
void ivpu_bo_list_print(struct drm_device *dev);
struct ivpu_bo *
ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 flags);
void ivpu_bo_free_internal(struct ivpu_bo *bo);
struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf);
void ivpu_bo_unmap_sgt_and_remove_from_context(struct ivpu_bo *bo);
int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
static inline struct ivpu_bo *to_ivpu_bo(struct drm_gem_object *obj)
{
return container_of(obj, struct ivpu_bo, base);
}
static inline struct page *ivpu_bo_get_page(struct ivpu_bo *bo, u64 offset)
{
if (offset > bo->base.size || !bo->pages)
return NULL;
return bo->pages[offset / PAGE_SIZE];
}
static inline u32 ivpu_bo_cache_mode(struct ivpu_bo *bo)
{
return bo->flags & DRM_IVPU_BO_CACHE_MASK;
}
static inline bool ivpu_bo_is_snooped(struct ivpu_bo *bo)
{
return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED;
}
static inline pgprot_t ivpu_bo_pgprot(struct ivpu_bo *bo, pgprot_t prot)
{
if (bo->flags & DRM_IVPU_BO_WC)
return pgprot_writecombine(prot);
if (bo->flags & DRM_IVPU_BO_UNCACHED)
return pgprot_noncached(prot);
return prot;
}
static inline struct ivpu_device *ivpu_bo_to_vdev(struct ivpu_bo *bo)
{
return to_ivpu_device(bo->base.dev);
}
static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr)
{
if (vpu_addr < bo->vpu_addr)
return NULL;
if (vpu_addr >= (bo->vpu_addr + bo->base.size))
return NULL;
return bo->kvaddr + (vpu_addr - bo->vpu_addr);
}
static inline u32 cpu_to_vpu_addr(struct ivpu_bo *bo, void *cpu_addr)
{
if (cpu_addr < bo->kvaddr)
return 0;
if (cpu_addr >= (bo->kvaddr + bo->base.size))
return 0;
return bo->vpu_addr + (cpu_addr - bo->kvaddr);
}
#endif /* __IVPU_GEM_H__ */
......@@ -17,6 +17,8 @@ extern "C" {
#define DRM_IVPU_GET_PARAM 0x00
#define DRM_IVPU_SET_PARAM 0x01
#define DRM_IVPU_BO_CREATE 0x02
#define DRM_IVPU_BO_INFO 0x03
#define DRM_IOCTL_IVPU_GET_PARAM \
DRM_IOWR(DRM_COMMAND_BASE + DRM_IVPU_GET_PARAM, struct drm_ivpu_param)
......@@ -24,6 +26,12 @@ extern "C" {
#define DRM_IOCTL_IVPU_SET_PARAM \
DRM_IOW(DRM_COMMAND_BASE + DRM_IVPU_SET_PARAM, struct drm_ivpu_param)
#define DRM_IOCTL_IVPU_BO_CREATE \
DRM_IOWR(DRM_COMMAND_BASE + DRM_IVPU_BO_CREATE, struct drm_ivpu_bo_create)
#define DRM_IOCTL_IVPU_BO_INFO \
DRM_IOWR(DRM_COMMAND_BASE + DRM_IVPU_BO_INFO, struct drm_ivpu_bo_info)
/**
* DOC: contexts
*
......@@ -92,6 +100,92 @@ struct drm_ivpu_param {
__u64 value;
};
#define DRM_IVPU_BO_HIGH_MEM 0x00000001
#define DRM_IVPU_BO_MAPPABLE 0x00000002
#define DRM_IVPU_BO_CACHED 0x00000000
#define DRM_IVPU_BO_UNCACHED 0x00010000
#define DRM_IVPU_BO_WC 0x00020000
#define DRM_IVPU_BO_CACHE_MASK 0x00030000
#define DRM_IVPU_BO_FLAGS \
(DRM_IVPU_BO_HIGH_MEM | \
DRM_IVPU_BO_MAPPABLE | \
DRM_IVPU_BO_CACHE_MASK)
/**
* struct drm_ivpu_bo_create - Create BO backed by SHMEM
*
* Create GEM buffer object allocated in SHMEM memory.
*/
struct drm_ivpu_bo_create {
/** @size: The size in bytes of the allocated memory */
__u64 size;
/**
* @flags:
*
* Supported flags:
*
* %DRM_IVPU_BO_HIGH_MEM:
*
* Allocate VPU address from >4GB range.
* Buffer object with vpu address >4GB can be always accessed by the
* VPU DMA engine, but some HW generation may not be able to access
* this memory from then firmware running on the VPU management processor.
* Suitable for input, output and some scratch buffers.
*
* %DRM_IVPU_BO_MAPPABLE:
*
* Buffer object can be mapped using mmap().
*
* %DRM_IVPU_BO_CACHED:
*
* Allocated BO will be cached on host side (WB) and snooped on the VPU side.
* This is the default caching mode.
*
* %DRM_IVPU_BO_UNCACHED:
*
* Allocated BO will not be cached on host side nor snooped on the VPU side.
*
* %DRM_IVPU_BO_WC:
*
* Allocated BO will use write combining buffer for writes but reads will be
* uncached.
*/
__u32 flags;
/** @handle: Returned GEM object handle */
__u32 handle;
/** @vpu_addr: Returned VPU virtual address */
__u64 vpu_addr;
};
/**
* struct drm_ivpu_bo_info - Query buffer object info
*/
struct drm_ivpu_bo_info {
/** @handle: Handle of the queried BO */
__u32 handle;
/** @flags: Returned flags used to create the BO */
__u32 flags;
/** @vpu_addr: Returned VPU virtual address */
__u64 vpu_addr;
/**
* @mmap_offset:
*
* Returned offset to be used in mmap(). 0 in case the BO is not mappable.
*/
__u64 mmap_offset;
/** @size: Returned GEM object size, aligned to PAGE_SIZE */
__u64 size;
};
#if defined(__cplusplus)
}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment