drm/rockchip: fix iommu page fault when use boot logo
authorMark Yao <mark.yao@rock-chips.com>
Thu, 8 Dec 2016 09:00:30 +0000 (17:00 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Fri, 9 Dec 2016 02:48:17 +0000 (10:48 +0800)
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>
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_fb.c

index 148e6d4de154c1c2216dffc61421940c3665d1bb..5b4f1daa45e9704697fe0e99f8c30fcfd5ce4a53 100644 (file)
@@ -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 *
index 31c90c8e9737e12e3a6840673550d316b2b7a8fa..72db38bfe84c56930960d43dd4f40d38f786ef89 100644 (file)
@@ -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;
index 3f5f8c42fda4793e86dc37be299b573da36c5a87..deeeab3afe8f74758a5fa10a8629f4b58f7af89f 100644 (file)
@@ -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");