mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
Determining whether the header file iosys-map.h is present in the kernel is currently determine by kernel version. However, for Linux v5.15, iosys-map.h has been backported in order to support simple-framebuffer for early display. Therefore, we cannot rely on the kernel version to indicate whether iosys-map is present. This is also true for 3rd party Linux kernels that backport changes as well. Fix this by adding a compile time flag, that will be set accordingly by the conftest script if this header is present. Bug 4119327 Bug 4228080 Change-Id: Ibd814285b2a07932ede2fbe9e6dc8fd03039d0c3 Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2971954 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
711 lines
16 KiB
C
711 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2017-2023 NVIDIA Corporation. All rights reserved.
|
|
|
|
/**
|
|
* @file drivers/media/platform/tegra/camera/fusa-capture/capture-common.c
|
|
*
|
|
* @brief VI/ISP channel common operations for the T186/T194 Camera RTCPU
|
|
* platform.
|
|
*/
|
|
|
|
#include <nvidia/conftest.h>
|
|
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/nospec.h>
|
|
#include <linux/nvhost.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/hashtable.h>
|
|
#include <linux/atomic.h>
|
|
#include <media/mc_common.h>
|
|
|
|
#include <media/fusa-capture/capture-common.h>
|
|
|
|
|
|
/**
|
|
* @brief Capture buffer management table.
|
|
*/
|
|
struct capture_buffer_table {
|
|
struct device *dev; /**< Originating device (VI or ISP) */
|
|
struct kmem_cache *cache; /**< SLAB allocator cache */
|
|
rwlock_t hlock; /**< Reader/writer lock on table contents */
|
|
DECLARE_HASHTABLE(hhead, 4U); /**< Buffer hashtable head */
|
|
};
|
|
|
|
/**
|
|
* @brief Capture surface NvRm and IOVA addresses handle.
|
|
*/
|
|
union capture_surface {
|
|
uint64_t raw; /**< Pinned VI or ISP IOVA address */
|
|
struct {
|
|
uint32_t offset; /**< NvRm handle (upper 32 bits) */
|
|
uint32_t hmem;
|
|
/**<
|
|
* Offset of surface or pushbuffer address in descriptor
|
|
* (lower 32 bits) [byte]
|
|
*/
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @brief Capture buffer mapping (pinned).
|
|
*/
|
|
struct capture_mapping {
|
|
struct hlist_node hnode; /**< Hash table node struct */
|
|
atomic_t refcnt; /**< Capture mapping reference count */
|
|
struct dma_buf *buf; /** Capture mapping dma_buf */
|
|
struct dma_buf_attachment *atch;
|
|
/**< dma_buf attachment (VI or ISP device) */
|
|
struct sg_table *sgt; /**< Scatterlist to dma_buf attachment */
|
|
unsigned int flag; /**< Bitmask access flag */
|
|
};
|
|
|
|
/**
|
|
* @brief Determine whether all the bits of @a other are set in @a self.
|
|
*
|
|
* @param[in] self Bitmask flag to be compared
|
|
* @param[in] other Bitmask value(s) to compare
|
|
*
|
|
* @retval true compatible
|
|
* @retval false not compatible
|
|
*/
|
|
static inline bool flag_compatible(
|
|
unsigned int self,
|
|
unsigned int other)
|
|
{
|
|
return (self & other) == other;
|
|
}
|
|
|
|
/**
|
|
* @brief Determine whether BUFFER_RDWR is set in @a flag.
|
|
*
|
|
* @param[in] flag Bitmask flag to be compared
|
|
*
|
|
* @retval true BUFFER_RDWR set
|
|
* @retval false BUFFER_RDWR not set
|
|
*/
|
|
static inline unsigned int flag_access_mode(
|
|
unsigned int flag)
|
|
{
|
|
return flag & BUFFER_RDWR;
|
|
}
|
|
|
|
/**
|
|
* @brief Map capture common buffer access flag to a Linux dma_data_direction.
|
|
*
|
|
* @param[in] flag Bitmask access flag of capture common buffer
|
|
*
|
|
* @returns @ref dma_data_direction mapping
|
|
*/
|
|
static inline enum dma_data_direction flag_dma_direction(
|
|
unsigned int flag)
|
|
{
|
|
static const enum dma_data_direction dir[4U] = {
|
|
[0U] = DMA_BIDIRECTIONAL,
|
|
[BUFFER_READ] = DMA_TO_DEVICE,
|
|
[BUFFER_WRITE] = DMA_FROM_DEVICE,
|
|
[BUFFER_RDWR] = DMA_BIDIRECTIONAL,
|
|
};
|
|
|
|
return dir[flag_access_mode(flag)];
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve the scatterlist IOVA address of the capture surface mapping.
|
|
*
|
|
* @param[in] pin The capture_mapping of the buffer
|
|
*
|
|
* @returns Physical address of scatterlist mapping
|
|
*/
|
|
static inline dma_addr_t mapping_iova(
|
|
const struct capture_mapping *pin,
|
|
uint64_t mem_offset)
|
|
{
|
|
dma_addr_t iova = 0;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
|
|
iova = (sg_dma_address(pin->sgt->sgl) != 0) ? sg_dma_address(pin->sgt->sgl) :
|
|
sg_phys(pin->sgt->sgl);
|
|
iova += mem_offset;
|
|
#else
|
|
struct scatterlist *sg;
|
|
uint64_t mem_offset_adjusted = mem_offset;
|
|
int i;
|
|
|
|
/* Traverse the scatterlist and adjust the offset
|
|
* as per the page block. This is needed in case
|
|
* where memory spans across multiple pages and
|
|
* is non-contiguous
|
|
*/
|
|
for_each_sgtable_dma_sg(pin->sgt, sg, i) {
|
|
if (mem_offset_adjusted < sg_dma_len(sg)) {
|
|
iova = (sg_dma_address(sg) == 0) ? sg_phys(sg) : sg_dma_address(sg);
|
|
iova += mem_offset_adjusted;
|
|
if (iova < mem_offset_adjusted) {
|
|
/** It means iova has wrapped */
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
mem_offset_adjusted -= sg_dma_len(sg);
|
|
}
|
|
#endif
|
|
|
|
return iova;
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve the dma_buf pointer of a capture surface mapping.
|
|
*
|
|
* @param[in] pin The capture_mapping of the buffer
|
|
*
|
|
* @returns Pointer to the capture_mapping @ref dma_buf
|
|
*/
|
|
static inline struct dma_buf *mapping_buf(
|
|
const struct capture_mapping *pin)
|
|
{
|
|
return pin->buf;
|
|
}
|
|
|
|
/**
|
|
* @brief Determine whether BUFFER_ADD is set in the capture surface mapping's
|
|
* access flag.
|
|
*
|
|
* @param[in] pin The capture_mapping of the buffer
|
|
*
|
|
* @retval true BUFFER_ADD set
|
|
* @retval false BUFFER_ADD not set
|
|
*/
|
|
static inline bool mapping_preserved(
|
|
const struct capture_mapping *pin)
|
|
{
|
|
return (bool)(pin->flag & BUFFER_ADD);
|
|
}
|
|
|
|
/**
|
|
* @brief Set or unset the BUFFER_ADD bit in the capture surface mapping's
|
|
* access flag, and correspondingly increment or decrement the mapping's refcnt.
|
|
*
|
|
* @param[in] pin The capture_mapping of the buffer
|
|
* @param[in] val The capture_mapping of the buffer
|
|
*
|
|
* @retval true BUFFER_ADD set
|
|
* @retval false BUFFER_ADD not set
|
|
*/
|
|
static inline void set_mapping_preservation(
|
|
struct capture_mapping *pin,
|
|
bool val)
|
|
{
|
|
if (val) {
|
|
pin->flag |= BUFFER_ADD;
|
|
atomic_inc(&pin->refcnt);
|
|
} else {
|
|
pin->flag &= (~BUFFER_ADD);
|
|
atomic_dec(&pin->refcnt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Iteratively search a capture buffer management table to find the entry
|
|
* with @a buf, and @a flag bits set in the capture mapping.
|
|
*
|
|
* On success, the capture mapping is incremented by one if it is non-zero.
|
|
*
|
|
* @param[in] tab The capture buffer management table
|
|
* @param[in] buf The mapping dma_buf pointer to match
|
|
* @param[in] flag The mapping bitmask access flag to compare
|
|
*
|
|
* @returns @ref capture_mapping pointer (success), NULL (failure)
|
|
*/
|
|
static struct capture_mapping *find_mapping(
|
|
struct capture_buffer_table *tab,
|
|
struct dma_buf *buf,
|
|
unsigned int flag)
|
|
{
|
|
struct capture_mapping *pin;
|
|
bool success;
|
|
|
|
read_lock(&tab->hlock);
|
|
|
|
hash_for_each_possible(tab->hhead, pin, hnode, (unsigned long)buf) {
|
|
if (
|
|
(pin->buf == buf) &&
|
|
flag_compatible(pin->flag, flag)
|
|
) {
|
|
success = atomic_inc_not_zero(&pin->refcnt);
|
|
if (success) {
|
|
read_unlock(&tab->hlock);
|
|
return pin;
|
|
}
|
|
}
|
|
}
|
|
|
|
read_unlock(&tab->hlock);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Add an NvRm buffer to the buffer management table and initialize its
|
|
* refcnt to 1.
|
|
*
|
|
* @param[in] tab The capture buffer management table
|
|
* @param[in] fd The NvRm handle
|
|
* @param[in] flag The mapping bitmask access flag to set
|
|
*
|
|
* @returns @ref capture_mapping pointer (success), PTR_ERR (failure)
|
|
*/
|
|
static struct capture_mapping *get_mapping(
|
|
struct capture_buffer_table *tab,
|
|
uint32_t fd,
|
|
unsigned int flag)
|
|
{
|
|
struct capture_mapping *pin;
|
|
struct dma_buf *buf;
|
|
void *err;
|
|
|
|
if (unlikely(tab == NULL)) {
|
|
pr_err("%s: invalid buffer table\n", __func__);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
buf = dma_buf_get((int)fd);
|
|
if (IS_ERR(buf)) {
|
|
dev_err(tab->dev, "%s:%d: invalid memfd %u; errno %ld \n",
|
|
__func__, __LINE__, fd, PTR_ERR(buf));
|
|
return ERR_CAST(buf);
|
|
}
|
|
|
|
pin = find_mapping(tab, buf, flag);
|
|
if (pin != NULL) {
|
|
dma_buf_put(buf);
|
|
return pin;
|
|
}
|
|
|
|
pin = kmem_cache_alloc(tab->cache, GFP_KERNEL);
|
|
if (unlikely(pin == NULL)) {
|
|
err = ERR_PTR(-ENOMEM);
|
|
goto err0;
|
|
}
|
|
|
|
pin->atch = dma_buf_attach(buf, tab->dev);
|
|
if (unlikely(IS_ERR(pin->atch))) {
|
|
err = pin->atch;
|
|
goto err1;
|
|
}
|
|
|
|
pin->sgt = dma_buf_map_attachment(pin->atch, flag_dma_direction(flag));
|
|
if (unlikely(IS_ERR(pin->sgt))) {
|
|
err = pin->sgt;
|
|
goto err2;
|
|
}
|
|
|
|
pin->flag = flag;
|
|
pin->buf = buf;
|
|
atomic_set(&pin->refcnt, 1U);
|
|
INIT_HLIST_NODE(&pin->hnode);
|
|
|
|
write_lock(&tab->hlock);
|
|
hash_add(tab->hhead, &pin->hnode, (unsigned long)pin->buf);
|
|
write_unlock(&tab->hlock);
|
|
|
|
return pin;
|
|
err2:
|
|
dma_buf_detach(buf, pin->atch);
|
|
err1:
|
|
kmem_cache_free(tab->cache, pin);
|
|
err0:
|
|
dma_buf_put(buf);
|
|
dev_err(tab->dev, "%s:%d: memfd %u, flag %u; errno %ld \n",
|
|
__func__, __LINE__,fd, flag, PTR_ERR(buf));
|
|
return err;
|
|
}
|
|
|
|
struct capture_buffer_table *create_buffer_table(
|
|
struct device *dev)
|
|
{
|
|
struct capture_buffer_table *tab;
|
|
|
|
tab = kmalloc(sizeof(*tab), GFP_KERNEL);
|
|
|
|
if (likely(tab != NULL)) {
|
|
tab->cache = KMEM_CACHE(capture_mapping, 0U);
|
|
|
|
if (likely(tab->cache != NULL)) {
|
|
tab->dev = dev;
|
|
hash_init(tab->hhead);
|
|
rwlock_init(&tab->hlock);
|
|
} else {
|
|
kfree(tab);
|
|
tab = NULL;
|
|
}
|
|
}
|
|
|
|
return tab;
|
|
}
|
|
EXPORT_SYMBOL_GPL(create_buffer_table);
|
|
|
|
void destroy_buffer_table(
|
|
struct capture_buffer_table *tab)
|
|
{
|
|
size_t bkt;
|
|
struct hlist_node *next;
|
|
struct capture_mapping *pin;
|
|
|
|
if (unlikely(tab == NULL))
|
|
return;
|
|
|
|
|
|
hash_for_each_safe(tab->hhead, bkt, next, pin, hnode) {
|
|
write_lock(&tab->hlock);
|
|
hash_del(&pin->hnode);
|
|
write_unlock(&tab->hlock);
|
|
dma_buf_unmap_attachment(
|
|
pin->atch, pin->sgt, flag_dma_direction(pin->flag));
|
|
dma_buf_detach(pin->buf, pin->atch);
|
|
dma_buf_put(pin->buf);
|
|
kmem_cache_free(tab->cache, pin);
|
|
}
|
|
|
|
|
|
kmem_cache_destroy(tab->cache);
|
|
kfree(tab);
|
|
}
|
|
EXPORT_SYMBOL_GPL(destroy_buffer_table);
|
|
|
|
static DEFINE_MUTEX(req_lock);
|
|
|
|
int capture_buffer_request(
|
|
struct capture_buffer_table *tab,
|
|
uint32_t memfd,
|
|
uint32_t flag)
|
|
{
|
|
struct capture_mapping *pin;
|
|
struct dma_buf *buf;
|
|
bool add = (bool)(flag & BUFFER_ADD);
|
|
int err = 0;
|
|
|
|
if (unlikely(tab == NULL)) {
|
|
pr_err("%s: invalid buffer table\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&req_lock);
|
|
|
|
if (add) {
|
|
pin = get_mapping(tab, memfd, flag_access_mode(flag));
|
|
if (IS_ERR(pin)) {
|
|
err = PTR_ERR_OR_ZERO(pin);
|
|
dev_err(tab->dev, "%s:%d: memfd %u, flag %u; errno %d",
|
|
__func__, __LINE__, memfd, flag, err);
|
|
goto end;
|
|
}
|
|
|
|
if (mapping_preserved(pin)) {
|
|
err = -EEXIST;
|
|
dev_err(tab->dev, "%s:%d: memfd %u exists; errno %d",
|
|
__func__, __LINE__, memfd, err);
|
|
put_mapping(tab, pin);
|
|
goto end;
|
|
}
|
|
} else {
|
|
buf = dma_buf_get((int)memfd);
|
|
if (IS_ERR(buf)) {
|
|
err = PTR_ERR_OR_ZERO(buf);
|
|
dev_err(tab->dev, "%s:%d: invalid memfd %u; errno %d",
|
|
__func__, __LINE__, memfd, err);
|
|
goto end;
|
|
}
|
|
|
|
pin = find_mapping(tab, buf, BUFFER_ADD);
|
|
if (pin == NULL) {
|
|
err = -ENOENT;
|
|
dev_err(tab->dev, "%s:%d: memfd %u not exists; errno %d",
|
|
__func__, __LINE__, memfd, err);
|
|
dma_buf_put(buf);
|
|
goto end;
|
|
}
|
|
dma_buf_put(buf);
|
|
}
|
|
|
|
set_mapping_preservation(pin, add);
|
|
put_mapping(tab, pin);
|
|
|
|
end:
|
|
mutex_unlock(&req_lock);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_buffer_request);
|
|
|
|
int capture_buffer_add(
|
|
struct capture_buffer_table *t,
|
|
uint32_t fd)
|
|
{
|
|
return capture_buffer_request(t, fd, BUFFER_ADD | BUFFER_RDWR);
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_buffer_add);
|
|
|
|
void put_mapping(
|
|
struct capture_buffer_table *t,
|
|
struct capture_mapping *pin)
|
|
{
|
|
bool zero;
|
|
|
|
zero = atomic_dec_and_test(&pin->refcnt);
|
|
if (zero) {
|
|
if (unlikely(mapping_preserved(pin))) {
|
|
dev_err(t->dev, "%s:%d: unexpected put for a preserved mapping",
|
|
__func__, __LINE__);
|
|
atomic_inc(&pin->refcnt);
|
|
return;
|
|
}
|
|
|
|
write_lock(&t->hlock);
|
|
hash_del(&pin->hnode);
|
|
write_unlock(&t->hlock);
|
|
|
|
dma_buf_unmap_attachment(
|
|
pin->atch, pin->sgt, flag_dma_direction(pin->flag));
|
|
dma_buf_detach(pin->buf, pin->atch);
|
|
dma_buf_put(pin->buf);
|
|
kmem_cache_free(t->cache, pin);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(put_mapping);
|
|
|
|
int capture_common_pin_and_get_iova(struct capture_buffer_table *buf_ctx,
|
|
uint32_t mem_handle, uint64_t mem_offset,
|
|
uint64_t *meminfo_base_address, uint64_t *meminfo_size,
|
|
struct capture_common_unpins *unpins)
|
|
{
|
|
struct capture_mapping *map;
|
|
struct dma_buf *buf;
|
|
uint64_t size;
|
|
uint64_t iova;
|
|
|
|
/* NULL is a valid unput indicating unused data field */
|
|
if (!mem_handle) {
|
|
return 0;
|
|
}
|
|
|
|
if (unpins->num_unpins >= MAX_PIN_BUFFER_PER_REQUEST) {
|
|
pr_err("%s: too many buffers per request\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
map = get_mapping(buf_ctx, mem_handle, BUFFER_RDWR);
|
|
|
|
if (IS_ERR(map)) {
|
|
pr_err("%s: cannot get mapping\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
buf = mapping_buf(map);
|
|
size = buf->size;
|
|
if (mem_offset >= size) {
|
|
pr_err("%s: offset is out of bounds\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iova = mapping_iova(map, mem_offset);
|
|
if (iova == 0) {
|
|
pr_err("%s: Invalid iova\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*meminfo_base_address = iova;
|
|
*meminfo_size = size - mem_offset;
|
|
|
|
unpins->data[unpins->num_unpins] = map;
|
|
unpins->num_unpins++;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_common_pin_and_get_iova);
|
|
|
|
int capture_common_setup_progress_status_notifier(
|
|
struct capture_common_status_notifier *status_notifier,
|
|
uint32_t mem,
|
|
uint32_t buffer_size,
|
|
uint32_t mem_offset)
|
|
{
|
|
struct dma_buf *dmabuf;
|
|
#if defined(NV_LINUX_IOSYS_MAP_H_PRESENT)
|
|
struct iosys_map map = {0};
|
|
#else
|
|
struct dma_buf_map map = {0};
|
|
#endif
|
|
void *va;
|
|
int err = 0;
|
|
|
|
/* take reference for the userctx */
|
|
dmabuf = dma_buf_get(mem);
|
|
if (IS_ERR(dmabuf))
|
|
return PTR_ERR(dmabuf);
|
|
|
|
if (buffer_size > U32_MAX - mem_offset) {
|
|
pr_err("%s: buffer_size or mem_offset too large\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((buffer_size + mem_offset) > dmabuf->size) {
|
|
dma_buf_put(dmabuf);
|
|
pr_err("%s: invalid offset\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* map handle and clear error notifier struct */
|
|
err = dma_buf_vmap(dmabuf, &map);
|
|
va = err ? NULL : map.vaddr;
|
|
if (!va) {
|
|
dma_buf_put(dmabuf);
|
|
pr_err("%s: Cannot map notifier handle\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(va, 0, buffer_size);
|
|
|
|
status_notifier->buf = dmabuf;
|
|
status_notifier->va = va;
|
|
status_notifier->offset = mem_offset;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_common_setup_progress_status_notifier);
|
|
|
|
int capture_common_release_progress_status_notifier(
|
|
struct capture_common_status_notifier *progress_status_notifier)
|
|
{
|
|
struct dma_buf *dmabuf = progress_status_notifier->buf;
|
|
void *va = progress_status_notifier->va;
|
|
#if defined(NV_LINUX_IOSYS_MAP_H_PRESENT)
|
|
struct iosys_map map = IOSYS_MAP_INIT_VADDR(va);
|
|
#else
|
|
struct dma_buf_map map = DMA_BUF_MAP_INIT_VADDR(va);
|
|
#endif
|
|
|
|
if (dmabuf != NULL) {
|
|
if (va != NULL)
|
|
dma_buf_vunmap(dmabuf, &map);
|
|
|
|
dma_buf_put(dmabuf);
|
|
}
|
|
|
|
progress_status_notifier->buf = NULL;
|
|
progress_status_notifier->va = NULL;
|
|
progress_status_notifier->offset = 0;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_common_release_progress_status_notifier);
|
|
|
|
int capture_common_set_progress_status(
|
|
struct capture_common_status_notifier *progress_status_notifier,
|
|
uint32_t buffer_slot,
|
|
uint32_t buffer_depth,
|
|
uint8_t new_val)
|
|
{
|
|
uint32_t *status_notifier = (uint32_t *) (progress_status_notifier->va +
|
|
progress_status_notifier->offset);
|
|
|
|
if (buffer_slot >= buffer_depth) {
|
|
pr_err("%s: Invalid offset!", __func__);
|
|
return -EINVAL;
|
|
}
|
|
buffer_slot = array_index_nospec(buffer_slot, buffer_depth);
|
|
|
|
/*
|
|
* Since UMD and KMD can both write to the shared progress status
|
|
* notifier buffer, insert memory barrier here to ensure that any
|
|
* other store operations to the buffer would be done before the
|
|
* write below.
|
|
*/
|
|
wmb();
|
|
|
|
status_notifier[buffer_slot] = new_val;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_common_set_progress_status);
|
|
|
|
int capture_common_pin_memory(
|
|
struct device *dev,
|
|
uint32_t mem,
|
|
struct capture_common_buf *unpin_data)
|
|
{
|
|
struct dma_buf *buf;
|
|
#if defined(NV_LINUX_IOSYS_MAP_H_PRESENT)
|
|
struct iosys_map map = {0};
|
|
#else
|
|
struct dma_buf_map map = {0};
|
|
#endif
|
|
struct dma_buf_attachment *attach;
|
|
struct sg_table *sgt;
|
|
int err = 0;
|
|
|
|
buf = dma_buf_get(mem);
|
|
if (IS_ERR(buf)) {
|
|
err = PTR_ERR(buf);
|
|
goto fail;
|
|
}
|
|
|
|
attach = dma_buf_attach(buf, dev);
|
|
if (IS_ERR(attach)) {
|
|
err = PTR_ERR(attach);
|
|
goto fail;
|
|
}
|
|
|
|
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
|
if (IS_ERR(sgt)) {
|
|
err = PTR_ERR(sgt);
|
|
goto fail;
|
|
}
|
|
|
|
if (sg_dma_address(sgt->sgl) == 0)
|
|
sg_dma_address(sgt->sgl) = sg_phys(sgt->sgl);
|
|
|
|
err = dma_buf_vmap(buf, &map);
|
|
unpin_data->va = err ? NULL : map.vaddr;
|
|
if (unpin_data->va == NULL) {
|
|
pr_err("%s: failed to map pinned memory\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
unpin_data->iova = sg_dma_address(sgt->sgl);
|
|
unpin_data->buf = buf;
|
|
unpin_data->attach = attach;
|
|
unpin_data->sgt = sgt;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
capture_common_unpin_memory(unpin_data);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_common_pin_memory);
|
|
|
|
void capture_common_unpin_memory(
|
|
struct capture_common_buf *unpin_data)
|
|
{
|
|
#if defined(NV_LINUX_IOSYS_MAP_H_PRESENT)
|
|
struct iosys_map map = IOSYS_MAP_INIT_VADDR(unpin_data->va);
|
|
#else
|
|
struct dma_buf_map map = DMA_BUF_MAP_INIT_VADDR(unpin_data->va);
|
|
#endif
|
|
|
|
if (unpin_data->va)
|
|
dma_buf_vunmap(unpin_data->buf, &map);
|
|
|
|
if (unpin_data->sgt != NULL)
|
|
dma_buf_unmap_attachment(unpin_data->attach, unpin_data->sgt,
|
|
DMA_BIDIRECTIONAL);
|
|
if (unpin_data->attach != NULL)
|
|
dma_buf_detach(unpin_data->buf, unpin_data->attach);
|
|
if (unpin_data->buf != NULL)
|
|
dma_buf_put(unpin_data->buf);
|
|
|
|
unpin_data->sgt = NULL;
|
|
unpin_data->attach = NULL;
|
|
unpin_data->buf = NULL;
|
|
unpin_data->iova = 0;
|
|
unpin_data->va = NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(capture_common_unpin_memory);
|