]> git.kernelconcepts.de Git - mv-sheeva.git/blob - drivers/gpu/drm/nouveau/nouveau_notifier.c
Merge remote branch 'nouveau/for-airlied' of ../drm-nouveau-next into drm-core-next
[mv-sheeva.git] / drivers / gpu / drm / nouveau / nouveau_notifier.c
1 /*
2  * Copyright (C) 2007 Ben Skeggs.
3  *
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial
16  * portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27
28 #include "drmP.h"
29 #include "drm.h"
30 #include "nouveau_drv.h"
31 #include "nouveau_ramht.h"
32
33 int
34 nouveau_notifier_init_channel(struct nouveau_channel *chan)
35 {
36         struct drm_device *dev = chan->dev;
37         struct nouveau_bo *ntfy = NULL;
38         uint32_t flags;
39         int ret;
40
41         if (nouveau_vram_notify)
42                 flags = TTM_PL_FLAG_VRAM;
43         else
44                 flags = TTM_PL_FLAG_TT;
45
46         ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags,
47                               0, 0x0000, false, true, &ntfy);
48         if (ret)
49                 return ret;
50
51         ret = nouveau_bo_pin(ntfy, flags);
52         if (ret)
53                 goto out_err;
54
55         ret = nouveau_bo_map(ntfy);
56         if (ret)
57                 goto out_err;
58
59         ret = drm_mm_init(&chan->notifier_heap, 0, ntfy->bo.mem.size);
60         if (ret)
61                 goto out_err;
62
63         chan->notifier_bo = ntfy;
64 out_err:
65         if (ret)
66                 drm_gem_object_unreference_unlocked(ntfy->gem);
67
68         return ret;
69 }
70
71 void
72 nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
73 {
74         struct drm_device *dev = chan->dev;
75
76         if (!chan->notifier_bo)
77                 return;
78
79         nouveau_bo_unmap(chan->notifier_bo);
80         mutex_lock(&dev->struct_mutex);
81         nouveau_bo_unpin(chan->notifier_bo);
82         mutex_unlock(&dev->struct_mutex);
83         drm_gem_object_handle_unreference_unlocked(chan->notifier_bo->gem);
84         drm_gem_object_unreference_unlocked(chan->notifier_bo->gem);
85         drm_mm_takedown(&chan->notifier_heap);
86 }
87
88 static void
89 nouveau_notifier_gpuobj_dtor(struct drm_device *dev,
90                              struct nouveau_gpuobj *gpuobj)
91 {
92         NV_DEBUG(dev, "\n");
93
94         if (gpuobj->priv)
95                 drm_mm_put_block(gpuobj->priv);
96 }
97
98 int
99 nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
100                        int size, uint32_t *b_offset)
101 {
102         struct drm_device *dev = chan->dev;
103         struct drm_nouveau_private *dev_priv = dev->dev_private;
104         struct nouveau_gpuobj *nobj = NULL;
105         struct drm_mm_node *mem;
106         uint32_t offset;
107         int target, ret;
108
109         mem = drm_mm_search_free(&chan->notifier_heap, size, 0, 0);
110         if (mem)
111                 mem = drm_mm_get_block(mem, size, 0);
112         if (!mem) {
113                 NV_ERROR(dev, "Channel %d notifier block full\n", chan->id);
114                 return -ENOMEM;
115         }
116
117         offset = chan->notifier_bo->bo.mem.start << PAGE_SHIFT;
118         if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) {
119                 target = NV_DMA_TARGET_VIDMEM;
120         } else
121         if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_TT) {
122                 if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA &&
123                     dev_priv->card_type < NV_50) {
124                         ret = nouveau_sgdma_get_page(dev, offset, &offset);
125                         if (ret)
126                                 return ret;
127                         target = NV_DMA_TARGET_PCI;
128                 } else {
129                         target = NV_DMA_TARGET_AGP;
130                         if (dev_priv->card_type >= NV_50)
131                                 offset += dev_priv->vm_gart_base;
132                 }
133         } else {
134                 NV_ERROR(dev, "Bad DMA target, mem_type %d!\n",
135                          chan->notifier_bo->bo.mem.mem_type);
136                 return -EINVAL;
137         }
138         offset += mem->start;
139
140         ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset,
141                                      mem->size, NV_DMA_ACCESS_RW, target,
142                                      &nobj);
143         if (ret) {
144                 drm_mm_put_block(mem);
145                 NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret);
146                 return ret;
147         }
148         nobj->dtor = nouveau_notifier_gpuobj_dtor;
149         nobj->priv = mem;
150
151         ret = nouveau_ramht_insert(chan, handle, nobj);
152         nouveau_gpuobj_ref(NULL, &nobj);
153         if (ret) {
154                 drm_mm_put_block(mem);
155                 NV_ERROR(dev, "Error adding notifier to ramht: %d\n", ret);
156                 return ret;
157         }
158
159         *b_offset = mem->start;
160         return 0;
161 }
162
163 int
164 nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset)
165 {
166         if (!nobj || nobj->dtor != nouveau_notifier_gpuobj_dtor)
167                 return -EINVAL;
168
169         if (poffset) {
170                 struct drm_mm_node *mem = nobj->priv;
171
172                 if (*poffset >= mem->size)
173                         return false;
174
175                 *poffset += mem->start;
176         }
177
178         return 0;
179 }
180
181 int
182 nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
183                              struct drm_file *file_priv)
184 {
185         struct drm_nouveau_notifierobj_alloc *na = data;
186         struct nouveau_channel *chan;
187         int ret;
188
189         NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan);
190
191         ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset);
192         if (ret)
193                 return ret;
194
195         return 0;
196 }