OSDN Git Service

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