diff --git a/drivers/gpu/drm/rockchip/ebc-dev/Kconfig b/drivers/gpu/drm/rockchip/ebc-dev/Kconfig index 2cc01ea3e43a..1b2734d60e02 100644 --- a/drivers/gpu/drm/rockchip/ebc-dev/Kconfig +++ b/drivers/gpu/drm/rockchip/ebc-dev/Kconfig @@ -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. diff --git a/drivers/gpu/drm/rockchip/ebc-dev/Makefile b/drivers/gpu/drm/rockchip/ebc-dev/Makefile index aee4fdbbf89c..ec5e347ba025 100644 --- a/drivers/gpu/drm/rockchip/ebc-dev/Makefile +++ b/drivers/gpu/drm/rockchip/ebc-dev/Makefile @@ -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/ diff --git a/drivers/gpu/drm/rockchip/ebc-dev/ebc_dma_buf.c b/drivers/gpu/drm/rockchip/ebc-dev/ebc_dma_buf.c new file mode 100644 index 000000000000..5d55f821aa84 --- /dev/null +++ b/drivers/gpu/drm/rockchip/ebc-dev/ebc_dma_buf.c @@ -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); diff --git a/drivers/gpu/drm/rockchip/ebc-dev/ebc_dma_buf.h b/drivers/gpu/drm/rockchip/ebc-dev/ebc_dma_buf.h new file mode 100644 index 000000000000..cd253940755f --- /dev/null +++ b/drivers/gpu/drm/rockchip/ebc-dev/ebc_dma_buf.h @@ -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 + +/** + * 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 */