drm/rockchip: ebc: Add support for ebc dma-buf exporter

In previous versions, the ebc driver share ebc framebuffer with
user space using mmap. But it cannot be shared across multiple
subsystems (e.g., GPU, RGA) or user processes.

This patch adds support for ebc dma-buf, which exports contiguous
ebc framebuffer as a dma-buf, and other modules can process the
ebc framebuffer using dma-buf related operations.

Signed-off-by: Chaoyi Chen <chaoyi.chen@rock-chips.com>
Change-Id: I22ccc68c311490deca57fd1ab0726d53e7e02c6e
This commit is contained in:
Chaoyi Chen
2024-05-08 17:31:50 +08:00
committed by Tao Huang
parent d2cbeecf81
commit d08e17a736
4 changed files with 170 additions and 1 deletions

View File

@@ -2,6 +2,7 @@
menuconfig ROCKCHIP_EBC_DEV
bool "Rockchip eBook Device Driver"
select DMA_SHARED_BUFFER
help
Rockchip eBook Device Dirver could help to driver the electronic ink screen.

View File

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ROCKCHIP_EBC_DEV) += rkebc.o ebc_public.o
obj-$(CONFIG_ROCKCHIP_EBC_DEV) += rkebc.o ebc_public.o ebc_dma_buf.o
obj-$(CONFIG_ROCKCHIP_EBC_DEV) += epdlut/
obj-$(CONFIG_ROCKCHIP_EBC_DEV) += bufmanage/
obj-$(CONFIG_ROCKCHIP_EBC_DEV) += pmic/

View File

@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
*/
#include "ebc_dma_buf.h"
struct ebc_dmabuf {
phys_addr_t phy_addr;
size_t size;
int npages;
struct page *pages[];
};
static inline struct ebc_dmabuf *to_ebc_dmabuf(struct dma_buf *buf)
{
return buf->priv;
}
static struct sg_table *ebc_map_dma_buf(struct dma_buf_attachment *attachment,
enum dma_data_direction dir)
{
struct ebc_dmabuf *ebcbuf = to_ebc_dmabuf(attachment->dmabuf);
struct sg_table *st;
int ret;
st = kzalloc(sizeof(*st), GFP_KERNEL);
if (!st)
return ERR_PTR(-ENOMEM);
ret = sg_alloc_table_from_pages(st, ebcbuf->pages, ebcbuf->npages, 0,
ebcbuf->npages << PAGE_SHIFT, GFP_KERNEL);
if (ret)
goto err_free_st;
ret = dma_map_sgtable(attachment->dev, st, dir, 0);
if (ret)
goto err_free_table;
return st;
err_free_table:
sg_free_table(st);
err_free_st:
kfree(st);
return ERR_PTR(ret);
}
static void ebc_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *st,
enum dma_data_direction dir)
{
dma_unmap_sgtable(attachment->dev, st, dir, 0);
sg_free_table(st);
kfree(st);
}
static int ebc_dmabuf_vmap(struct dma_buf *dma_buf, struct iosys_map *map)
{
struct ebc_dmabuf *ebcbuf = to_ebc_dmabuf(dma_buf);
void *vaddr;
vaddr = vmap(ebcbuf->pages, ebcbuf->npages, VM_MAP, pgprot_writecombine(PAGE_KERNEL));
if (!vaddr)
return -ENOMEM;
iosys_map_set_vaddr(map, vaddr);
return 0;
}
static void ebc_dmabuf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map)
{
vunmap(map->vaddr);
iosys_map_clear(map);
}
static int ebc_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
{
struct ebc_dmabuf *ebcbuf = to_ebc_dmabuf(dma_buf);
unsigned long pfn;
int ret;
pfn = ((unsigned long)(ebcbuf->phy_addr) >> PAGE_SHIFT);
vm_flags_set(vma, VM_IO | VM_DONTEXPAND | VM_DONTDUMP);
ret = remap_pfn_range(vma, vma->vm_start, pfn, vma->vm_end - vma->vm_start,
vma->vm_page_prot);
if (ret)
return -EAGAIN;
return 0;
}
static void ebc_dmabuf_release(struct dma_buf *dma_buf)
{
struct ebc_dmabuf *ebcbuf = to_ebc_dmabuf(dma_buf);
kfree(ebcbuf);
}
static const struct dma_buf_ops ebc_dmabuf_ops = {
.map_dma_buf = ebc_map_dma_buf,
.unmap_dma_buf = ebc_unmap_dma_buf,
.release = ebc_dmabuf_release,
.mmap = ebc_dmabuf_mmap,
.vmap = ebc_dmabuf_vmap,
.vunmap = ebc_dmabuf_vunmap,
};
struct dma_buf *ebc_get_dma_buf(phys_addr_t phy_addr, size_t size)
{
struct ebc_dmabuf *ebcbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct dma_buf *dmabuf;
int i, npages;
if (size % PAGE_SIZE)
return ERR_PTR(-EINVAL);
npages = size / PAGE_SIZE;
ebcbuf = kmalloc(sizeof(*ebcbuf) + npages * sizeof(struct page *), GFP_KERNEL);
if (!ebcbuf)
return ERR_PTR(-ENOMEM);
for (i = 0; i < npages; i++)
ebcbuf->pages[i] = phys_to_page(phy_addr + i * PAGE_SIZE);
ebcbuf->phy_addr = phy_addr;
ebcbuf->size = size;
ebcbuf->npages = npages;
exp_info.ops = &ebc_dmabuf_ops;
exp_info.size = npages * PAGE_SIZE;
exp_info.flags = O_RDWR;
exp_info.priv = ebcbuf;
dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(dmabuf))
goto err;
return dmabuf;
err:
kfree(ebcbuf);
return ERR_PTR(-ENOMEM);
}
EXPORT_SYMBOL(ebc_get_dma_buf);

View File

@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
*/
#ifndef EBC_DMA_BUF_H
#define EBC_DMA_BUF_H
#include <linux/dma-buf.h>
/**
* ebc_get_dma_buf - Convert ebc buffer to dma_buf
* @phy_addr: Physical address of the ebc buffer
* @size: Size of the ebc buffer
*
* The caller must ensure that the physical address and size of the ebc buffer
* are aligned to PAGE_SIZE.
*/
struct dma_buf *ebc_get_dma_buf(phys_addr_t phy_addr, size_t size);
#endif /* EBC_DMA_BUF_H */