blob: c51cd0bfdddb470052ab0cc3024051d0972fbb7b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Support for using dma-bufs.
*
* Copyright (C) 2022 Google LLC
*/
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <gcip/gcip-config.h>
#include <gcip/gcip-iommu-reserve.h>
#include <gcip/gcip-iommu.h>
#include "gxp-dma.h"
#include "gxp-dmabuf.h"
#include <trace/events/gxp.h>
/* Mapping destructor for gxp_mapping_put() to call */
static void destroy_dmabuf_mapping(struct gxp_mapping *mapping)
{
dma_addr_t device_address = mapping->gcip_mapping->device_address;
size_t size = mapping->gcip_mapping->size;
trace_gxp_mapping_destroy_start(device_address, size);
gcip_iommu_mapping_unmap(mapping->gcip_mapping);
kfree(mapping);
trace_gxp_mapping_destroy_end(device_address, size);
}
struct gxp_mapping *gxp_dmabuf_map(struct gxp_dev *gxp, struct gcip_iommu_reserve_manager *mgr,
struct gcip_iommu_domain *domain, int fd, u32 flags,
dma_addr_t iova_hint)
{
struct gxp_mapping *mapping;
struct gcip_iommu_mapping *gcip_mapping;
struct dma_buf *dmabuf;
u64 gcip_map_flags;
int ret;
trace_gxp_dmabuf_mapping_create_start(fd);
dmabuf = dma_buf_get(fd);
if (IS_ERR(dmabuf))
return ERR_CAST(dmabuf);
/* Skip CPU cache syncs while mapping this dmabuf. */
gcip_map_flags = gxp_dma_encode_gcip_map_flags(flags, 0) |
GCIP_MAP_FLAGS_DMA_ATTR_TO_FLAGS(DMA_ATTR_SKIP_CPU_SYNC);
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
ret = -ENOMEM;
goto err_dma_buf_put;
}
if (!iova_hint)
gcip_mapping = gcip_iommu_domain_map_dma_buf(domain, dmabuf, gcip_map_flags);
else
gcip_mapping = gcip_iommu_reserve_map_dma_buf(mgr, dmabuf, gcip_map_flags,
iova_hint, mapping);
if (IS_ERR(gcip_mapping)) {
ret = PTR_ERR(gcip_mapping);
dev_err(gxp->dev, "Failed to map dma-buf (ret=%d)\n", ret);
goto err_free_mapping;
}
dma_buf_put(dmabuf);
/* dma-buf mappings are indicated by a host_address of 0 */
mapping->host_address = 0;
mapping->gcip_mapping = gcip_mapping;
mapping->destructor = destroy_dmabuf_mapping;
mapping->gxp = gxp;
refcount_set(&mapping->refcount, 1);
trace_gxp_dmabuf_mapping_create_end(gcip_mapping->device_address, gcip_mapping->size);
return mapping;
err_free_mapping:
kfree(mapping);
err_dma_buf_put:
dma_buf_put(dmabuf);
return ERR_PTR(ret);
}
MODULE_IMPORT_NS(DMA_BUF);