OSDN Git Service

Merge remote-tracking branch 'x86/kitkat-x86' into lollipop-x86
[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.h>
36
37 #include "gralloc_drm.h"
38 #include "gralloc_drm_priv.h"
39
40 #define NV_ARCH_03  0x03
41 #define NV_ARCH_04  0x04
42 #define NV_ARCH_10  0x10
43 #define NV_ARCH_20  0x20
44 #define NV_ARCH_30  0x30
45 #define NV_ARCH_40  0x40
46 #define NV_TESLA    0x50
47 #define NV_FERMI    0xc0
48 #define NV_KEPLER   0xe0
49 #define NV_MAXWELL  0x110
50
51 #define NV50_TILE_HEIGHT(m) (4 << ((m) >> 4))
52 #define NVC0_TILE_HEIGHT(m) (8 << ((m) >> 4))
53
54
55 // Comment out the following to switch between the "sw_indicator disables all
56 // tiling" and "sw_indicator zeroes the tile|surf_flags (object tiling?)".
57 // Does the latter even make sense ... ? Going through the kernel on the
58 // topic is slightly annoying :\
59
60 #undef SW_INDICATOR_FULLY_DISABLES_TILING
61
62 struct nouveau_info {
63         struct gralloc_drm_drv_t base;
64
65         int fd;
66         uint32_t arch;
67         struct nouveau_device *dev;
68         struct nouveau_client *client;
69         struct nouveau_object *channel;
70         struct nouveau_pushbuf *pushbuf;
71 };
72
73 struct nouveau_buffer {
74         struct gralloc_drm_bo_t base;
75
76         struct nouveau_bo *bo;
77 };
78
79 static struct nouveau_bo *alloc_bo(struct nouveau_info *info,
80                 int width, int height, int cpp, int usage, int *pitch)
81 {
82         struct nouveau_bo *bo = NULL;
83         union nouveau_bo_config cfg = {};
84         int flags;
85         int tiled, scanout, sw_indicator;
86         unsigned int align;
87
88         flags = NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM;
89
90         scanout = !!(usage & GRALLOC_USAGE_HW_FB);
91
92         tiled = !(usage & (GRALLOC_USAGE_SW_READ_OFTEN |
93                            GRALLOC_USAGE_SW_WRITE_OFTEN));
94
95         sw_indicator = (usage & (GRALLOC_USAGE_SW_READ_OFTEN |
96                                  GRALLOC_USAGE_SW_WRITE_OFTEN));
97
98         if (info->arch >= NV_TESLA) {
99                 tiled = 1;
100                 align = 64;
101         }
102         else {
103                 if (scanout)
104                         tiled = 1;
105                 align = 64;
106         }
107
108         *pitch = ALIGN(width * cpp, align);
109
110         if (tiled) {
111                 if (info->arch >= NV_FERMI) {
112                         if (height > 64)
113                                 cfg.nvc0.tile_mode = 0x0040;
114                         else if (height > 32)
115                                 cfg.nvc0.tile_mode = 0x0030;
116                         else if (height > 16)
117                                 cfg.nvc0.tile_mode = 0x0020;
118                         else if (height > 8)
119                                 cfg.nvc0.tile_mode = 0x0010;
120                         else
121                                 cfg.nvc0.tile_mode = 0x0000;
122
123 #ifndef SW_INDICATOR_FULLY_DISABLES_TILING
124                         if (sw_indicator)
125                                 cfg.nvc0.memtype = 0x0000;
126                         else
127 #endif
128                                 cfg.nvc0.memtype = 0x00fe;
129
130                         align = NVC0_TILE_HEIGHT(cfg.nvc0.tile_mode);
131                         height = ALIGN(height, align);
132                 }
133                 else if (info->arch >= NV_TESLA) {
134                         if (height > 32)
135                                 cfg.nv50.tile_mode = 0x0040;
136                         else if (height > 16)
137                                 cfg.nv50.tile_mode = 0x0030;
138                         else if (height >  8)
139                                 cfg.nv50.tile_mode = 0x0020;
140                         else if (height >  4)
141                                 cfg.nv50.tile_mode = 0x0010;
142                         else
143                                 cfg.nv50.tile_mode = 0x0000;
144
145 #ifndef SW_INDICATOR_FULLY_DISABLES_TILING
146                         if (sw_indicator)
147                                 cfg.nv50.memtype = 0x0000;
148                         else
149 #endif
150                                 cfg.nv50.memtype = (scanout && cpp != 2) ?
151                                                 0x007a : 0x0070;
152
153                         align = NV50_TILE_HEIGHT(cfg.nv50.tile_mode);
154                         height = ALIGN(height, align);
155                 }
156                 else {
157                         align = *pitch / 4;
158
159                         /* round down to the previous power of two */
160                         align >>= 1;
161                         align |= align >> 1;
162                         align |= align >> 2;
163                         align |= align >> 4;
164                         align |= align >> 8;
165                         align |= align >> 16;
166                         align++;
167
168                         align = MAX((info->dev->chipset >= NV_ARCH_40) ?
169                                         1024 : 256, align);
170
171                         /* adjust pitch */
172                         *pitch = ALIGN(*pitch, align);
173                         cfg.nv04.surf_pitch = *pitch;
174                 }
175         }
176
177         if (info->arch < NV_TESLA) {
178 #ifndef SW_INDICATOR_FULLY_DISABLES_TILING
179                 if (sw_indicator)
180                         cfg.nv04.surf_flags = 0x0000;
181                 else
182 #endif
183                 if (cpp == 4)
184                         cfg.nv04.surf_flags |= NV04_BO_32BPP;
185                 else if (cpp == 2)
186                         cfg.nv04.surf_flags |= NV04_BO_16BPP;
187         }
188
189         if (scanout)
190                 flags |= NOUVEAU_BO_CONTIG;
191
192 #ifdef SW_INDICATOR_FULLY_DISABLES_TILING
193         if (nouveau_bo_new(info->dev, flags, 0, *pitch * height, NULL, &bo)) {
194 #else
195         if (nouveau_bo_new(info->dev, flags, 0, *pitch * height, &cfg, &bo)) {
196 #endif
197                 ALOGE("failed to allocate bo (flags 0x%x, size %d)",
198                                 flags, *pitch * height);
199                 bo = NULL;
200         }
201
202         return bo;
203 }
204
205 static struct gralloc_drm_bo_t *nouveau_alloc(struct gralloc_drm_drv_t *drv,
206                 struct gralloc_drm_handle_t *handle)
207 {
208         struct nouveau_info *info = (struct nouveau_info *) drv;
209         struct nouveau_buffer *nb;
210         int cpp;
211
212         cpp = gralloc_drm_get_bpp(handle->format);
213         if (!cpp) {
214                 ALOGE("unrecognized format 0x%x", handle->format);
215                 return NULL;
216         }
217
218         nb = calloc(1, sizeof(*nb));
219         if (!nb)
220                 return NULL;
221
222         if (handle->name) {
223                 if (nouveau_bo_name_ref(info->dev, handle->name, &nb->bo)) {
224                         ALOGE("failed to create nouveau bo from name %u",
225                                         handle->name);
226                         free(nb);
227                         return NULL;
228                 }
229         }
230         else {
231                 int width, height, pitch;
232
233                 width = handle->width;
234                 height = handle->height;
235                 gralloc_drm_align_geometry(handle->format, &width, &height);
236
237                 nb->bo = alloc_bo(info, width, height, cpp,
238                                   handle->usage, &pitch);
239                 if (!nb->bo) {
240                         ALOGE("failed to allocate nouveau bo %dx%dx%d",
241                                         handle->width, handle->height, cpp);
242                         free(nb);
243                         return NULL;
244                 }
245
246                 if (nouveau_bo_name_get(nb->bo,
247                                         (uint32_t *) &handle->name)) {
248                         ALOGE("failed to flink nouveau bo");
249                         nouveau_bo_ref(NULL, &nb->bo);
250                         free(nb);
251                         return NULL;
252                 }
253
254                 handle->stride = pitch;
255         }
256
257         if (handle->usage & GRALLOC_USAGE_HW_FB)
258                 nb->base.fb_handle = nb->bo->handle;
259
260         nb->base.handle = handle;
261
262         return &nb->base;
263 }
264
265 static void nouveau_free(struct gralloc_drm_drv_t *drv,
266                 struct gralloc_drm_bo_t *bo)
267 {
268         struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
269         nouveau_bo_ref(NULL, &nb->bo);
270         free(nb);
271 }
272
273 static int nouveau_map(struct gralloc_drm_drv_t *drv,
274                 struct gralloc_drm_bo_t *bo, int x, int y, int w, int h,
275                 int enable_write, void **addr)
276 {
277         struct nouveau_info *info = (struct nouveau_info *) drv;
278         struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
279         uint32_t flags;
280         int err;
281
282         flags = NOUVEAU_BO_RD;
283         if (enable_write)
284                 flags |= NOUVEAU_BO_WR;
285
286         /* TODO if tiled, allocate a linear copy of bo in GART and map it */
287         err = nouveau_bo_map(nb->bo, flags, info->client);
288         if (!err)
289                 *addr = nb->bo->map;
290
291         return err;
292 }
293
294 static void nouveau_unmap(struct gralloc_drm_drv_t *drv,
295                 struct gralloc_drm_bo_t *bo)
296 {
297         /* The bo is implicitly unmapped at nouveau_bo_ref(NULL, bo) */
298 }
299
300 static void nouveau_init_kms_features(struct gralloc_drm_drv_t *drv,
301                 struct gralloc_drm_t *drm)
302 {
303         struct nouveau_info *info = (struct nouveau_info *) drv;
304
305         switch (drm->primary.fb_format) {
306         case HAL_PIXEL_FORMAT_BGRA_8888:
307         case HAL_PIXEL_FORMAT_RGB_565:
308                 break;
309         default:
310                 drm->primary.fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
311                 break;
312         }
313
314         drm->mode_quirk_vmwgfx = 0;
315         drm->swap_mode = DRM_SWAP_FLIP;
316         drm->mode_sync_flip = 1;
317         drm->swap_interval = 1;
318         drm->vblank_secondary = 0;
319 }
320
321 static void nouveau_destroy(struct gralloc_drm_drv_t *drv)
322 {
323         struct nouveau_info *info = (struct nouveau_info *) drv;
324
325         nouveau_pushbuf_del(&info->pushbuf);
326         nouveau_object_del(&info->channel);
327         nouveau_client_del(&info->client);
328         nouveau_device_del(&info->dev);
329         free(info);
330 }
331
332 static int nouveau_init(struct nouveau_info *info)
333 {
334         int err = 0;
335
336         switch (info->dev->chipset & ~0xf) {
337         case 0x00:
338                 info->arch = NV_ARCH_04;
339                 break;
340         case 0x10:
341                 info->arch = NV_ARCH_10;
342                 break;
343         case 0x20:
344                 info->arch = NV_ARCH_20;
345                 break;
346         case 0x30:
347                 info->arch = NV_ARCH_30;
348                 break;
349         case 0x40:
350         case 0x60:
351                 info->arch = NV_ARCH_40;
352                 break;
353         case 0x50:
354         case 0x80:
355         case 0x90:
356         case 0xa0:
357                 info->arch = NV_TESLA;
358                 break;
359         case 0xc0:
360         case 0xd0:
361                 info->arch = NV_FERMI;
362                 break;
363         case 0xe0:
364         case 0xf0:
365                 info->arch = NV_KEPLER;
366                 break;
367         case 0x110:
368                 info->arch = NV_MAXWELL;
369                 break;
370         default:
371                 ALOGE("unknown nouveau chipset 0x%x", info->dev->chipset);
372                 err = -EINVAL;
373                 break;
374         }
375
376         if (info->dev->drm_version < 0x01000000 && info->dev->chipset >= 0xc0) {
377                 ALOGE("nouveau kernel module is too old 0x%x",
378                       info->dev->drm_version);
379                 err = -EINVAL;
380         }
381
382         return err;
383 }
384
385 struct gralloc_drm_drv_t *
386 gralloc_drm_drv_create_for_nouveau(int fd)
387 {
388         struct nouveau_info *info;
389         struct nv04_fifo nv04_data = { .vram = 0xbeef0201, .gart = 0xbeef0202 };
390         struct nvc0_fifo nvc0_data = { };
391         int size, err;
392         void *data;
393
394         info = calloc(1, sizeof(*info));
395         if (!info)
396                 return NULL;
397
398         info->fd = fd;
399         err = nouveau_device_wrap(fd, 0, &info->dev);
400         if (err) {
401                 ALOGE("failed to wrap existing nouveau device");
402                 free(info);
403                 return NULL;
404         }
405
406         err = nouveau_init(info);
407         if (err) {
408                 free(info);
409                 return NULL;
410         }
411
412         err = nouveau_client_new(info->dev, &info->client);
413         if (err) {
414                 ALOGW("failed to create nouveau client: %d", err);
415                 nouveau_device_del(&info->dev);
416                 free(info);
417                 return NULL;
418         }
419
420         if (info->dev->chipset < 0xc0) {
421                 data = &nv04_data;
422                 size = sizeof(nv04_data);
423         }
424         else {
425                 data = &nvc0_data;
426                 size = sizeof(nvc0_data);
427         }
428
429         err = nouveau_object_new(&info->dev->object, 0,
430                         NOUVEAU_FIFO_CHANNEL_CLASS, data, size,
431                         &info->channel);
432
433         if (err) {
434                 ALOGE("failed to create nouveau channel: %d", err);
435                 nouveau_client_del(&info->client);
436                 nouveau_device_del(&info->dev);
437                 free(info);
438                 return NULL;
439         }
440
441         err = nouveau_pushbuf_new(info->client, info->channel,
442                         4, 32 * 1024, true, &info->pushbuf);
443         if (err) {
444                 ALOGE("failed to allocate DMA push buffer: %d", err);
445                 nouveau_object_del(&info->channel);
446                 nouveau_client_del(&info->client);
447                 nouveau_device_del(&info->dev);
448                 free(info);
449                 return NULL;
450         }
451
452         info->base.destroy = nouveau_destroy;
453         info->base.init_kms_features = nouveau_init_kms_features;
454         info->base.alloc = nouveau_alloc;
455         info->base.free = nouveau_free;
456         info->base.map = nouveau_map;
457         info->base.unmap = nouveau_unmap;
458
459         return &info->base;
460 }