OSDN Git Service

gralloc: cleanup, use ALIGN macro
[android-x86/external-drm_gralloc.git] / gralloc_drm_nouveau.c
1 /*
2  * Copyright (C) 2011 Chia-I Wu <olvaffe@gmail.com>
3  * Copyright (C) 2011 LunarG Inc.
4  *
5  * Based on xf86-video-nouveau, which has
6  *
7  * Copyright © 2007 Red Hat, Inc.
8  * Copyright © 2008 Maarten Maathuis
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  */
28
29 #define LOG_TAG "GRALLOC-NOUVEAU"
30
31 #include <cutils/log.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <drm.h>
35 #include <nouveau_drmif.h>
36 #include <nouveau_channel.h>
37 #include <nouveau_bo.h>
38
39 #include "gralloc_drm.h"
40 #include "gralloc_drm_priv.h"
41
42 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
43
44 #define NVC0_TILE_HEIGHT(m) (8 << ((m) >> 4))
45
46 enum {
47         NvDmaFB = 0xd8000001,
48         NvDmaTT = 0xd8000002,
49 };
50
51 struct nouveau_info {
52         struct gralloc_drm_drv_t base;
53
54         int fd;
55         struct nouveau_device *dev;
56         struct nouveau_channel *chan;
57         int arch;
58         int tiled_scanout;
59 };
60
61 struct nouveau_buffer {
62         struct gralloc_drm_bo_t base;
63
64         struct nouveau_bo *bo;
65 };
66
67 static struct nouveau_bo *alloc_bo(struct nouveau_info *info,
68                 int width, int height, int cpp, int usage, int *pitch)
69 {
70         struct nouveau_bo *bo = NULL;
71         int flags, tile_mode, tile_flags;
72         int tiled, scanout;
73         unsigned int align;
74
75         flags = NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM;
76         tile_mode = 0;
77         tile_flags = 0;
78
79         scanout = !!(usage & GRALLOC_USAGE_HW_FB);
80
81         tiled = !(usage & (GRALLOC_USAGE_SW_READ_OFTEN |
82                            GRALLOC_USAGE_SW_WRITE_OFTEN));
83         if (!info->chan)
84                 tiled = 0;
85         else if (scanout && info->tiled_scanout)
86                 tiled = 1;
87
88         /* calculate pitch align */
89         align = 64;
90         if (info->arch >= 0x50) {
91                 if (scanout && !info->tiled_scanout)
92                         align = 256;
93                 else
94                         tiled = 1;
95         }
96
97         *pitch = ALIGN(width * cpp, align);
98
99         if (tiled) {
100                 if (info->arch >= 0xc0) {
101                         if (height > 64)
102                                 tile_mode = 0x40;
103                         else if (height > 32)
104                                 tile_mode = 0x30;
105                         else if (height > 16)
106                                 tile_mode = 0x20;
107                         else if (height > 8)
108                                 tile_mode = 0x10;
109                         else
110                                 tile_mode = 0x00;
111
112                         tile_flags = 0xfe00;
113
114                         align = NVC0_TILE_HEIGHT(tile_mode);
115                         height = ALIGN(height, align);
116                 }
117                 else if (info->arch >= 0x50) {
118                         if (height > 32)
119                                 tile_mode = 4;
120                         else if (height > 16)
121                                 tile_mode = 3;
122                         else if (height > 8)
123                                 tile_mode = 2;
124                         else if (height > 4)
125                                 tile_mode = 1;
126                         else
127                                 tile_mode = 0;
128
129                         tile_flags = (scanout && cpp != 2) ? 0x7a00 : 0x7000;
130
131                         align = 1 << (tile_mode + 2);
132                         height = ALIGN(height, align);
133                 }
134                 else {
135                         align = *pitch / 4;
136
137                         /* round down to the previous power of two */
138                         align >>= 1;
139                         align |= align >> 1;
140                         align |= align >> 2;
141                         align |= align >> 4;
142                         align |= align >> 8;
143                         align |= align >> 16;
144                         align++;
145
146                         align = MAX((info->dev->chipset >= 0x40) ? 1024 : 256,
147                                         align);
148
149                         /* adjust pitch */
150                         *pitch = ALIGN(*pitch, align);
151
152                         tile_mode = *pitch;
153                 }
154         }
155
156         if (cpp == 4)
157                 tile_flags |= NOUVEAU_BO_TILE_32BPP;
158         else if (cpp == 2)
159                 tile_flags |= NOUVEAU_BO_TILE_16BPP;
160
161         if (scanout)
162                 tile_flags |= NOUVEAU_BO_TILE_SCANOUT;
163
164         if (nouveau_bo_new_tile(info->dev, flags, 0, *pitch * height,
165                                 tile_mode, tile_flags, &bo)) {
166                 ALOGE("failed to allocate bo (flags 0x%x, size %d, tile_mode 0x%x, tile_flags 0x%x)",
167                                 flags, *pitch * height, tile_mode, tile_flags);
168                 bo = NULL;
169         }
170
171         return bo;
172 }
173
174 static struct gralloc_drm_bo_t *
175 nouveau_alloc(struct gralloc_drm_drv_t *drv, struct gralloc_drm_handle_t *handle)
176 {
177         struct nouveau_info *info = (struct nouveau_info *) drv;
178         struct nouveau_buffer *nb;
179         int cpp;
180
181         cpp = gralloc_drm_get_bpp(handle->format);
182         if (!cpp) {
183                 ALOGE("unrecognized format 0x%x", handle->format);
184                 return NULL;
185         }
186
187         nb = calloc(1, sizeof(*nb));
188         if (!nb)
189                 return NULL;
190
191         if (handle->name) {
192                 if (nouveau_bo_handle_ref(info->dev, handle->name, &nb->bo)) {
193                         ALOGE("failed to create nouveau bo from name %u",
194                                         handle->name);
195                         free(nb);
196                         return NULL;
197                 }
198         }
199         else {
200                 int width, height, pitch;
201
202                 width = handle->width;
203                 height = handle->height;
204                 gralloc_drm_align_geometry(handle->format, &width, &height);
205
206                 nb->bo = alloc_bo(info, width, height,
207                                 cpp, handle->usage, &pitch);
208                 if (!nb->bo) {
209                         ALOGE("failed to allocate nouveau bo %dx%dx%d",
210                                         handle->width, handle->height, cpp);
211                         free(nb);
212                         return NULL;
213                 }
214
215                 if (nouveau_bo_handle_get(nb->bo,
216                                         (uint32_t *) &handle->name)) {
217                         ALOGE("failed to flink nouveau bo");
218                         nouveau_bo_ref(NULL, &nb->bo);
219                         free(nb);
220                         return NULL;
221                 }
222
223                 handle->stride = pitch;
224         }
225
226         if (handle->usage & GRALLOC_USAGE_HW_FB)
227                 nb->base.fb_handle = nb->bo->handle;
228
229         nb->base.handle = handle;
230
231         return &nb->base;
232 }
233
234 static void nouveau_free(struct gralloc_drm_drv_t *drv,
235                 struct gralloc_drm_bo_t *bo)
236 {
237         struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
238         nouveau_bo_ref(NULL, &nb->bo);
239         free(nb);
240 }
241
242 static int nouveau_map(struct gralloc_drm_drv_t *drv,
243                 struct gralloc_drm_bo_t *bo, int x, int y, int w, int h,
244                 int enable_write, void **addr)
245 {
246         struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
247         uint32_t flags;
248         int err;
249
250         flags = NOUVEAU_BO_RD;
251         if (enable_write)
252                 flags |= NOUVEAU_BO_WR;
253
254         /* TODO if tiled, allocate a linear copy of bo in GART and map it */
255         err = nouveau_bo_map(nb->bo, flags);
256         if (!err)
257                 *addr = nb->bo->map;
258
259         return err;
260 }
261
262 static void nouveau_unmap(struct gralloc_drm_drv_t *drv,
263                 struct gralloc_drm_bo_t *bo)
264 {
265         struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
266         /* TODO if tiled, unmap the linear bo and copy back */
267         nouveau_bo_unmap(nb->bo);
268 }
269
270 static void nouveau_init_kms_features(struct gralloc_drm_drv_t *drv,
271                 struct gralloc_drm_t *drm)
272 {
273         struct nouveau_info *info = (struct nouveau_info *) drv;
274
275         switch (drm->primary.fb_format) {
276         case HAL_PIXEL_FORMAT_BGRA_8888:
277         case HAL_PIXEL_FORMAT_RGB_565:
278                 break;
279         default:
280                 drm->primary.fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
281                 break;
282         }
283
284         drm->mode_quirk_vmwgfx = 0;
285         drm->swap_mode = (info->chan) ? DRM_SWAP_FLIP : DRM_SWAP_SETCRTC;
286         drm->mode_sync_flip = 1;
287         drm->swap_interval = 1;
288         drm->vblank_secondary = 0;
289 }
290
291 static void nouveau_destroy(struct gralloc_drm_drv_t *drv)
292 {
293         struct nouveau_info *info = (struct nouveau_info *) drv;
294
295         if (info->chan)
296                 nouveau_channel_free(&info->chan);
297         nouveau_device_close(&info->dev);
298         free(info);
299 }
300
301 static int nouveau_init(struct nouveau_info *info)
302 {
303         int err = 0;
304
305         switch (info->dev->chipset & 0xf0) {
306         case 0x00:
307                 info->arch = 0x04;
308                 break;
309         case 0x10:
310                 info->arch = 0x10;
311                 break;
312         case 0x20:
313                 info->arch = 0x20;
314                 break;
315         case 0x30:
316                 info->arch = 0x30;
317                 break;
318         case 0x40:
319         case 0x60:
320                 info->arch = 0x40;
321                 break;
322         case 0x50:
323         case 0x80:
324         case 0x90:
325         case 0xa0:
326                 info->arch = 0x50;
327                 break;
328         case 0xc0:
329                 info->arch = 0xc0;
330                 break;
331         default:
332                 ALOGE("unknown nouveau chipset 0x%x", info->dev->chipset);
333                 err = -EINVAL;
334                 break;
335         }
336
337         info->tiled_scanout = (info->chan != NULL);
338
339         return err;
340 }
341
342 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_nouveau(int fd)
343 {
344         struct nouveau_info *info;
345         int err;
346
347         info = calloc(1, sizeof(*info));
348         if (!info)
349                 return NULL;
350
351         info->fd = fd;
352         err = nouveau_device_open_existing(&info->dev, 0, info->fd, 0);
353         if (err) {
354                 ALOGE("failed to create nouveau device");
355                 free(info);
356                 return NULL;
357         }
358
359         err = nouveau_channel_alloc(info->dev, NvDmaFB, NvDmaTT,
360                         24 * 1024, &info->chan);
361         if (err) {
362                 /* make it non-fatal temporarily as it may require firmwares */
363                 ALOGW("failed to create nouveau channel");
364                 info->chan = NULL;
365         }
366
367         err = nouveau_init(info);
368         if (err) {
369                 if (info->chan)
370                         nouveau_channel_free(&info->chan);
371                 nouveau_device_close(&info->dev);
372                 free(info);
373                 return NULL;
374         }
375
376         info->base.destroy = nouveau_destroy;
377         info->base.init_kms_features = nouveau_init_kms_features;
378         info->base.alloc = nouveau_alloc;
379         info->base.free = nouveau_free;
380         info->base.map = nouveau_map;
381         info->base.unmap = nouveau_unmap;
382
383         return &info->base;
384 }