mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
drm/rockchip: fix iommu page fault when use boot logo
Since (cacb6f5 FROMLIST: drm/rockchip: Use common IOMMU API to attach
devices), rockchip drm use common IOMMU API, the boot logo buffer
mapping need change to new api.
Change-Id: Ifa2c886e05d2de65de53a868458c56859519a0f2
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
This commit is contained in:
@@ -111,7 +111,6 @@ static int init_loader_memory(struct drm_device *drm_dev)
|
||||
unsigned long nr_pages;
|
||||
struct page **pages;
|
||||
struct sg_table *sgt;
|
||||
DEFINE_DMA_ATTRS(attrs);
|
||||
phys_addr_t start, size;
|
||||
struct resource res;
|
||||
int i, ret;
|
||||
@@ -144,15 +143,35 @@ static int init_loader_memory(struct drm_device *drm_dev)
|
||||
}
|
||||
sgt = drm_prime_pages_to_sg(pages, nr_pages);
|
||||
if (IS_ERR(sgt)) {
|
||||
kfree(pages);
|
||||
return PTR_ERR(sgt);
|
||||
ret = PTR_ERR(sgt);
|
||||
goto err_free_pages;
|
||||
}
|
||||
|
||||
if (private->domain) {
|
||||
int prot = IOMMU_READ | IOMMU_WRITE;
|
||||
|
||||
memset(&logo->mm, 0, sizeof(logo->mm));
|
||||
ret = drm_mm_insert_node_generic(&private->mm, &logo->mm,
|
||||
size, PAGE_SIZE,
|
||||
0, 0, 0);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("out of I/O virtual memory: %d\n", ret);
|
||||
goto err_free_pages;
|
||||
}
|
||||
|
||||
logo->dma_addr = logo->mm.start;
|
||||
|
||||
if (iommu_map_sg(private->domain, logo->dma_addr, sgt->sgl,
|
||||
sgt->nents, prot) < size) {
|
||||
DRM_ERROR("failed to map buffer");
|
||||
ret = -ENOMEM;
|
||||
goto err_remove_node;
|
||||
}
|
||||
} else {
|
||||
dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
|
||||
logo->dma_addr = sg_dma_address(sgt->sgl);
|
||||
}
|
||||
|
||||
dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
|
||||
dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
|
||||
dma_map_sg_attrs(drm_dev->dev, sgt->sgl, sgt->nents,
|
||||
DMA_TO_DEVICE, &attrs);
|
||||
logo->dma_addr = sg_dma_address(sgt->sgl);
|
||||
logo->sgt = sgt;
|
||||
logo->start = res.start;
|
||||
logo->size = size;
|
||||
@@ -160,6 +179,13 @@ static int init_loader_memory(struct drm_device *drm_dev)
|
||||
private->logo = logo;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_node:
|
||||
drm_mm_remove_node(&logo->mm);
|
||||
err_free_pages:
|
||||
kfree(pages);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct drm_framebuffer *
|
||||
|
||||
@@ -90,6 +90,7 @@ struct rockchip_drm_file_private {
|
||||
|
||||
struct rockchip_logo {
|
||||
struct sg_table *sgt;
|
||||
struct drm_mm_node mm;
|
||||
dma_addr_t dma_addr;
|
||||
phys_addr_t start;
|
||||
phys_addr_t size;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/iommu.h>
|
||||
|
||||
#include "rockchip_drm_drv.h"
|
||||
#include "rockchip_drm_gem.h"
|
||||
@@ -46,6 +47,7 @@ dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb,
|
||||
static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
|
||||
struct rockchip_drm_private *private = fb->dev->dev_private;
|
||||
struct drm_gem_object *obj;
|
||||
int i;
|
||||
|
||||
@@ -63,8 +65,14 @@ static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
|
||||
void *start = phys_to_virt(logo->start);
|
||||
void *end = phys_to_virt(logo->size);
|
||||
|
||||
dma_unmap_sg(fb->dev->dev, logo->sgt->sgl,
|
||||
logo->sgt->nents, DMA_TO_DEVICE);
|
||||
if (private && private->domain) {
|
||||
iommu_unmap(private->domain, logo->dma_addr,
|
||||
logo->size);
|
||||
drm_mm_remove_node(&logo->mm);
|
||||
} else {
|
||||
dma_unmap_sg(fb->dev->dev, logo->sgt->sgl,
|
||||
logo->sgt->nents, DMA_TO_DEVICE);
|
||||
}
|
||||
sg_free_table(logo->sgt);
|
||||
memblock_free(logo->start, logo->size);
|
||||
free_reserved_area(start, end, -1, "drm_logo");
|
||||
|
||||
Reference in New Issue
Block a user