Merge tag 'drm-misc-fixes-2017-11-20' of git://anongit.freedesktop.org/drm/drm-misc...
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / nouveau / nouveau_sgdma.c
1 #include <linux/pagemap.h>
2 #include <linux/slab.h>
3
4 #include "nouveau_drv.h"
5 #include "nouveau_mem.h"
6 #include "nouveau_ttm.h"
7
8 struct nouveau_sgdma_be {
9         /* this has to be the first field so populate/unpopulated in
10          * nouve_bo.c works properly, otherwise have to move them here
11          */
12         struct ttm_dma_tt ttm;
13         struct nouveau_mem *mem;
14 };
15
16 static void
17 nouveau_sgdma_destroy(struct ttm_tt *ttm)
18 {
19         struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
20
21         if (ttm) {
22                 ttm_dma_tt_fini(&nvbe->ttm);
23                 kfree(nvbe);
24         }
25 }
26
27 static int
28 nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *reg)
29 {
30         struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
31         struct nouveau_mem *mem = nouveau_mem(reg);
32         int ret;
33
34         ret = nouveau_mem_host(reg, &nvbe->ttm);
35         if (ret)
36                 return ret;
37
38         ret = nouveau_mem_map(mem, &mem->cli->vmm.vmm, &mem->vma[0]);
39         if (ret) {
40                 nouveau_mem_fini(mem);
41                 return ret;
42         }
43
44         nvbe->mem = mem;
45         return 0;
46 }
47
48 static int
49 nv04_sgdma_unbind(struct ttm_tt *ttm)
50 {
51         struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
52         nouveau_mem_fini(nvbe->mem);
53         return 0;
54 }
55
56 static struct ttm_backend_func nv04_sgdma_backend = {
57         .bind                   = nv04_sgdma_bind,
58         .unbind                 = nv04_sgdma_unbind,
59         .destroy                = nouveau_sgdma_destroy
60 };
61
62 static int
63 nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *reg)
64 {
65         struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
66         struct nouveau_mem *mem = nouveau_mem(reg);
67         int ret;
68
69         ret = nouveau_mem_host(reg, &nvbe->ttm);
70         if (ret)
71                 return ret;
72
73         nvbe->mem = mem;
74         return 0;
75 }
76
77 static struct ttm_backend_func nv50_sgdma_backend = {
78         .bind                   = nv50_sgdma_bind,
79         .unbind                 = nv04_sgdma_unbind,
80         .destroy                = nouveau_sgdma_destroy
81 };
82
83 struct ttm_tt *
84 nouveau_sgdma_create_ttm(struct ttm_bo_device *bdev,
85                          unsigned long size, uint32_t page_flags,
86                          struct page *dummy_read_page)
87 {
88         struct nouveau_drm *drm = nouveau_bdev(bdev);
89         struct nouveau_sgdma_be *nvbe;
90
91         nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);
92         if (!nvbe)
93                 return NULL;
94
95         if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA)
96                 nvbe->ttm.ttm.func = &nv04_sgdma_backend;
97         else
98                 nvbe->ttm.ttm.func = &nv50_sgdma_backend;
99
100         if (ttm_dma_tt_init(&nvbe->ttm, bdev, size, page_flags, dummy_read_page))
101                 /*
102                  * A failing ttm_dma_tt_init() will call ttm_tt_destroy()
103                  * and thus our nouveau_sgdma_destroy() hook, so we don't need
104                  * to free nvbe here.
105                  */
106                 return NULL;
107         return &nvbe->ttm.ttm;
108 }