mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
dma-buf: system_heap: add support system-uncached type
Change-Id: Ie9f81536ef710b244e79c8d32abd71c2601df4c5 Signed-off-by: Simon Xue <xxm@rock-chips.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
static struct dma_heap *sys_heap;
|
||||
static struct dma_heap *sys_uncached_heap;
|
||||
|
||||
struct system_heap_buffer {
|
||||
struct dma_heap *heap;
|
||||
@@ -31,6 +32,8 @@ struct system_heap_buffer {
|
||||
struct sg_table sg_table;
|
||||
int vmap_cnt;
|
||||
void *vaddr;
|
||||
|
||||
bool uncached;
|
||||
};
|
||||
|
||||
struct dma_heap_attachment {
|
||||
@@ -38,6 +41,8 @@ struct dma_heap_attachment {
|
||||
struct sg_table *table;
|
||||
struct list_head list;
|
||||
bool mapped;
|
||||
|
||||
bool uncached;
|
||||
};
|
||||
|
||||
#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO)
|
||||
@@ -100,7 +105,7 @@ static int system_heap_attach(struct dma_buf *dmabuf,
|
||||
a->dev = attachment->dev;
|
||||
INIT_LIST_HEAD(&a->list);
|
||||
a->mapped = false;
|
||||
|
||||
a->uncached = buffer->uncached;
|
||||
attachment->priv = a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
@@ -130,9 +135,13 @@ static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attac
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
struct sg_table *table = a->table;
|
||||
int attr = attachment->dma_map_attrs;
|
||||
int ret;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, 0);
|
||||
if (a->uncached)
|
||||
attr |= DMA_ATTR_SKIP_CPU_SYNC;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, attr);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@@ -145,9 +154,12 @@ static void system_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
int attr = attachment->dma_map_attrs;
|
||||
|
||||
if (a->uncached)
|
||||
attr |= DMA_ATTR_SKIP_CPU_SYNC;
|
||||
a->mapped = false;
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, 0);
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, attr);
|
||||
}
|
||||
|
||||
static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
@@ -161,10 +173,12 @@ static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
if (buffer->vmap_cnt)
|
||||
invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_cpu(a->dev, a->table, direction);
|
||||
if (!buffer->uncached) {
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_cpu(a->dev, a->table, direction);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
@@ -182,10 +196,12 @@ static int system_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
|
||||
if (buffer->vmap_cnt)
|
||||
flush_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_device(a->dev, a->table, direction);
|
||||
if (!buffer->uncached) {
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_device(a->dev, a->table, direction);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
@@ -252,6 +268,11 @@ system_heap_dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf,
|
||||
if (buffer->vmap_cnt)
|
||||
invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
if (buffer->uncached) {
|
||||
mutex_unlock(&buffer->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = system_heap_sgl_sync_range(dma_heap_get_dev(heap), table,
|
||||
offset, len, direction, true);
|
||||
mutex_unlock(&buffer->lock);
|
||||
@@ -274,6 +295,11 @@ system_heap_dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf,
|
||||
if (buffer->vmap_cnt)
|
||||
flush_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
if (buffer->uncached) {
|
||||
mutex_unlock(&buffer->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = system_heap_sgl_sync_range(dma_heap_get_dev(heap), table,
|
||||
offset, len, direction, false);
|
||||
mutex_unlock(&buffer->lock);
|
||||
@@ -289,6 +315,9 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
struct sg_page_iter piter;
|
||||
int ret;
|
||||
|
||||
if (buffer->uncached)
|
||||
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||
|
||||
for_each_sgtable_page(table, &piter, vma->vm_pgoff) {
|
||||
struct page *page = sg_page_iter_page(&piter);
|
||||
|
||||
@@ -310,17 +339,21 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
|
||||
struct page **pages = vmalloc(sizeof(struct page *) * npages);
|
||||
struct page **tmp = pages;
|
||||
struct sg_page_iter piter;
|
||||
pgprot_t pgprot = PAGE_KERNEL;
|
||||
void *vaddr;
|
||||
|
||||
if (!pages)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (buffer->uncached)
|
||||
pgprot = pgprot_writecombine(PAGE_KERNEL);
|
||||
|
||||
for_each_sgtable_page(table, &piter, 0) {
|
||||
WARN_ON(tmp - pages >= npages);
|
||||
*tmp++ = sg_page_iter_page(&piter);
|
||||
}
|
||||
|
||||
vaddr = vmap(pages, npages, VM_MAP, PAGE_KERNEL);
|
||||
vaddr = vmap(pages, npages, VM_MAP, pgprot);
|
||||
vfree(pages);
|
||||
|
||||
if (!vaddr)
|
||||
@@ -424,10 +457,11 @@ static struct page *alloc_largest_available(unsigned long size,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags)
|
||||
static struct dma_buf *system_heap_do_allocate(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags,
|
||||
bool uncached)
|
||||
{
|
||||
struct system_heap_buffer *buffer;
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
@@ -448,6 +482,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
mutex_init(&buffer->lock);
|
||||
buffer->heap = heap;
|
||||
buffer->len = len;
|
||||
buffer->uncached = uncached;
|
||||
|
||||
INIT_LIST_HEAD(&pages);
|
||||
i = 0;
|
||||
@@ -493,6 +528,18 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
ret = PTR_ERR(dmabuf);
|
||||
goto free_pages;
|
||||
}
|
||||
|
||||
/*
|
||||
* For uncached buffers, we need to initially flush cpu cache, since
|
||||
* the __GFP_ZERO on the allocation means the zeroing was done by the
|
||||
* cpu and thus it is likely cached. Map (and implicitly flush) and
|
||||
* unmap it now so we don't get corruption later on.
|
||||
*/
|
||||
if (buffer->uncached) {
|
||||
dma_map_sgtable(dma_heap_get_dev(heap), table, DMA_BIDIRECTIONAL, 0);
|
||||
dma_unmap_sgtable(dma_heap_get_dev(heap), table, DMA_BIDIRECTIONAL, 0);
|
||||
}
|
||||
|
||||
return dmabuf;
|
||||
|
||||
free_pages:
|
||||
@@ -510,10 +557,39 @@ free_buffer:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags)
|
||||
{
|
||||
return system_heap_do_allocate(heap, len, fd_flags, heap_flags, false);
|
||||
}
|
||||
static const struct dma_heap_ops system_heap_ops = {
|
||||
.allocate = system_heap_allocate,
|
||||
};
|
||||
|
||||
static struct dma_buf *system_uncached_heap_allocate(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags)
|
||||
{
|
||||
return system_heap_do_allocate(heap, len, fd_flags, heap_flags, true);
|
||||
}
|
||||
|
||||
/* Dummy function to be used until we can call coerce_mask_and_coherent */
|
||||
static struct dma_buf *system_uncached_heap_not_initialized(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags)
|
||||
{
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
static struct dma_heap_ops system_uncached_heap_ops = {
|
||||
/* After system_heap_create is complete, we will swap this */
|
||||
.allocate = system_uncached_heap_not_initialized,
|
||||
};
|
||||
|
||||
static int system_heap_create(void)
|
||||
{
|
||||
struct dma_heap_export_info exp_info;
|
||||
@@ -526,6 +602,18 @@ static int system_heap_create(void)
|
||||
if (IS_ERR(sys_heap))
|
||||
return PTR_ERR(sys_heap);
|
||||
|
||||
exp_info.name = "system-uncached";
|
||||
exp_info.ops = &system_uncached_heap_ops;
|
||||
exp_info.priv = NULL;
|
||||
|
||||
sys_uncached_heap = dma_heap_add(&exp_info);
|
||||
if (IS_ERR(sys_uncached_heap))
|
||||
return PTR_ERR(sys_uncached_heap);
|
||||
|
||||
dma_coerce_mask_and_coherent(dma_heap_get_dev(sys_uncached_heap), DMA_BIT_MASK(64));
|
||||
mb(); /* make sure we only set allocate after dma_mask is set */
|
||||
system_uncached_heap_ops.allocate = system_uncached_heap_allocate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(system_heap_create);
|
||||
|
||||
@@ -580,6 +580,8 @@ struct dma_buf_attach_ops {
|
||||
* @importer_ops: importer operations for this attachment, if provided
|
||||
* dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
|
||||
* @importer_priv: importer specific attachment data.
|
||||
* @dma_map_attrs: DMA attributes to be used when the exporter maps the buffer
|
||||
* through dma_buf_map_attachment.
|
||||
*
|
||||
* This structure holds the attachment information between the dma_buf buffer
|
||||
* and its user device(s). The list contains one attachment struct per device
|
||||
@@ -600,6 +602,7 @@ struct dma_buf_attachment {
|
||||
const struct dma_buf_attach_ops *importer_ops;
|
||||
void *importer_priv;
|
||||
void *priv;
|
||||
unsigned long dma_map_attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user