drm/rockchip: support add fb from dev resource
authorMark Yao <mark.yao@rock-chips.com>
Mon, 1 Aug 2016 09:34:23 +0000 (17:34 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Wed, 3 Aug 2016 07:56:15 +0000 (15:56 +0800)
Change-Id: I980af965d83de25c433ba5424bab2ad063534bcb
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
drivers/gpu/drm/rockchip/rockchip_drm_fb.c
drivers/gpu/drm/rockchip/rockchip_drm_fb.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c

index f1631e3..0406702 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <linux/memblock.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 
 struct rockchip_drm_fb {
        struct drm_framebuffer fb;
+       dma_addr_t dma_addr[ROCKCHIP_MAX_FB_BUFFER];
        struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+       struct sg_table *sgt;
+       phys_addr_t start;
+       phys_addr_t size;
 };
 
-struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
-                                              unsigned int plane)
+dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb,
+                                   unsigned int plane, struct device *dev)
 {
        struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
 
-       if (plane >= ROCKCHIP_MAX_FB_BUFFER)
-               return NULL;
+       if (WARN_ON(plane >= ROCKCHIP_MAX_FB_BUFFER))
+               return 0;
 
-       return rk_fb->obj[plane];
+       return rk_fb->dma_addr[plane];
 }
 
 static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
 {
        struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+       struct drm_device *dev = fb->dev;
        struct drm_gem_object *obj;
        int i;
 
@@ -52,6 +58,17 @@ static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
                        drm_gem_object_unreference_unlocked(obj);
        }
 
+       if (rockchip_fb->sgt) {
+               void *start = phys_to_virt(rockchip_fb->start);
+               void *end = phys_to_virt(rockchip_fb->size);
+
+               dma_unmap_sg(dev->dev, rockchip_fb->sgt->sgl,
+                            rockchip_fb->sgt->nents, DMA_TO_DEVICE);
+               sg_free_table(rockchip_fb->sgt);
+               memblock_free(rockchip_fb->start, rockchip_fb->size);
+               free_reserved_area(start, end, -1, "drm_fb");
+       }
+
        drm_framebuffer_cleanup(fb);
        kfree(rockchip_fb);
 }
@@ -71,12 +88,14 @@ static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
        .create_handle  = rockchip_drm_fb_create_handle,
 };
 
-static struct rockchip_drm_fb *
+struct drm_framebuffer *
 rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
-                 struct drm_gem_object **obj, unsigned int num_planes)
+                 struct drm_gem_object **obj, struct resource *res,
+                 unsigned int num_planes)
 {
        struct rockchip_drm_fb *rockchip_fb;
-       int ret;
+       struct rockchip_gem_object *rk_obj;
+       int ret = 0;
        int i;
 
        rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
@@ -85,26 +104,77 @@ rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
 
        drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
 
-       for (i = 0; i < num_planes; i++)
-               rockchip_fb->obj[i] = obj[i];
-
        ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
                                   &rockchip_drm_fb_funcs);
        if (ret) {
                dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
                        ret);
-               kfree(rockchip_fb);
-               return ERR_PTR(ret);
+               goto err_free_fb;
        }
 
-       return rockchip_fb;
+       if (obj) {
+               for (i = 0; i < num_planes; i++)
+                       rockchip_fb->obj[i] = obj[i];
+
+               for (i = 0; i < num_planes; i++) {
+                       rk_obj = to_rockchip_obj(obj[i]);
+                       rockchip_fb->dma_addr[i] = rk_obj->dma_addr;
+               }
+       } else if (res) {
+               unsigned long nr_pages;
+               struct page **pages;
+               struct sg_table *sgt;
+               DEFINE_DMA_ATTRS(attrs);
+               phys_addr_t start = res->start;
+               phys_addr_t size = res->end - res->start;
+
+               nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+               pages = kmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
+               if (!pages) {
+                       ret = -ENOMEM;
+                       goto err_deinit_drm_fb;
+               }
+               i = 0;
+               while (i < nr_pages) {
+                       pages[i] = phys_to_page(start);
+                       start += PAGE_SIZE;
+                       i++;
+               }
+               sgt = drm_prime_pages_to_sg(pages, nr_pages);
+               if (IS_ERR(sgt)) {
+                       kfree(pages);
+                       ret = PTR_ERR(sgt);
+                       goto err_deinit_drm_fb;
+               }
+
+               dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+               dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+               dma_map_sg_attrs(dev->dev, sgt->sgl, sgt->nents,
+                                DMA_TO_DEVICE, &attrs);
+               rockchip_fb->dma_addr[0] = sg_dma_address(sgt->sgl);
+               rockchip_fb->sgt = sgt;
+               rockchip_fb->start = res->start;
+               rockchip_fb->size = size;
+       } else {
+               ret = -EINVAL;
+               dev_err(dev->dev, "Failed to find available buffer\n");
+               goto err_deinit_drm_fb;
+       }
+
+       return &rockchip_fb->fb;
+
+err_deinit_drm_fb:
+       drm_framebuffer_cleanup(&rockchip_fb->fb);
+err_free_fb:
+       kfree(rockchip_fb);
+       return ERR_PTR(ret);
 }
 
 static struct drm_framebuffer *
 rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
                        struct drm_mode_fb_cmd2 *mode_cmd)
 {
-       struct rockchip_drm_fb *rockchip_fb;
+       struct drm_framebuffer *fb;
        struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
        struct drm_gem_object *obj;
        unsigned int hsub;
@@ -143,13 +213,13 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
                objs[i] = obj;
        }
 
-       rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
-       if (IS_ERR(rockchip_fb)) {
-               ret = PTR_ERR(rockchip_fb);
+       fb = rockchip_fb_alloc(dev, mode_cmd, objs, NULL, i);
+       if (IS_ERR(fb)) {
+               ret = PTR_ERR(fb);
                goto err_gem_object_unreference;
        }
 
-       return &rockchip_fb->fb;
+       return fb;
 
 err_gem_object_unreference:
        for (i--; i >= 0; i--)
@@ -317,13 +387,13 @@ rockchip_drm_framebuffer_init(struct drm_device *dev,
                              struct drm_mode_fb_cmd2 *mode_cmd,
                              struct drm_gem_object *obj)
 {
-       struct rockchip_drm_fb *rockchip_fb;
+       struct drm_framebuffer *fb;
 
-       rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
-       if (IS_ERR(rockchip_fb))
+       fb = rockchip_fb_alloc(dev, mode_cmd, &obj, NULL, 1);
+       if (IS_ERR(fb))
                return NULL;
 
-       return &rockchip_fb->fb;
+       return fb;
 }
 
 void rockchip_drm_mode_config_init(struct drm_device *dev)
index 09574d4..df6111e 100644 (file)
@@ -23,6 +23,11 @@ void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
 
 void rockchip_drm_mode_config_init(struct drm_device *dev);
 
-struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
-                                              unsigned int plane);
+struct drm_framebuffer *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+                 struct drm_gem_object **obj, struct resource *res,
+                 unsigned int num_planes);
+
+dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb,
+                                   unsigned int plane, struct device *dev);
 #endif /* _ROCKCHIP_DRM_FB_H */
index 1013131..aeb5b6c 100644 (file)
@@ -760,8 +760,6 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        uint32_t act_info, dsp_info, dsp_st;
        struct drm_rect *src = &vop_plane_state->src;
        struct drm_rect *dest = &vop_plane_state->dest;
-       struct drm_gem_object *obj, *uv_obj;
-       struct rockchip_gem_object *rk_obj, *rk_uv_obj;
        unsigned long offset;
        dma_addr_t dma_addr;
        int ymirror, xmirror;
@@ -779,9 +777,6 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                return;
        }
 
-       obj = rockchip_fb_get_gem_obj(fb, 0);
-       rk_obj = to_rockchip_obj(obj);
-
        actual_w = drm_rect_width(src) >> 16;
        actual_h = drm_rect_height(src) >> 16;
        act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
@@ -798,7 +793,9 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
        else
                offset += (src->y1 >> 16) * fb->pitches[0];
-       vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+
+       dma_addr = rockchip_fb_get_dma_addr(fb, 0, vop->dev);
+       vop_plane_state->yrgb_mst = dma_addr + offset + fb->offsets[0];
 
        ymirror = !!(state->rotation & BIT(DRM_REFLECT_Y));
        xmirror = !!(state->rotation & BIT(DRM_REFLECT_X));
@@ -815,13 +812,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
                int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
                int bpp = drm_format_plane_cpp(fb->pixel_format, 1);
 
-               uv_obj = rockchip_fb_get_gem_obj(fb, 1);
-               rk_uv_obj = to_rockchip_obj(uv_obj);
-
                offset = (src->x1 >> 16) * bpp / hsub;
                offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
 
-               dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
+               dma_addr = rockchip_fb_get_dma_addr(fb, 1, vop->dev);
+               dma_addr += offset + fb->offsets[1];
                VOP_WIN_SET(vop, win, uv_vir, fb->pitches[1] >> 2);
                VOP_WIN_SET(vop, win, uv_mst, dma_addr);
        }