2 * Copyright (C) 2011 Chia-I Wu <olvaffe@gmail.com>
3 * Copyright (C) 2011 LunarG Inc.
5 * Based on xf86-video-nouveau, which has
7 * Copyright © 2007 Red Hat, Inc.
8 * Copyright © 2008 Maarten Maathuis
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:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
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.
29 #define LOG_TAG "GRALLOC-NOUVEAU"
31 #include <cutils/log.h>
37 #include "gralloc_drm.h"
38 #include "gralloc_drm_priv.h"
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
48 #define NV_KEPLER 0xe0
49 #define NV_MAXWELL 0x110
50 #define NV_PASCAL 0x130
52 #define NV50_TILE_HEIGHT(m) (4 << ((m) >> 4))
53 #define NVC0_TILE_HEIGHT(m) (8 << ((m) >> 4))
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 :\
61 #undef SW_INDICATOR_FULLY_DISABLES_TILING
64 struct gralloc_drm_drv_t base;
68 struct nouveau_device *dev;
69 struct nouveau_client *client;
70 struct nouveau_object *channel;
71 struct nouveau_pushbuf *pushbuf;
74 struct nouveau_buffer {
75 struct gralloc_drm_bo_t base;
77 struct nouveau_bo *bo;
80 static struct nouveau_bo *alloc_bo(struct nouveau_info *info,
81 int width, int height, int cpp, int usage, int *pitch)
83 struct nouveau_bo *bo = NULL;
84 union nouveau_bo_config cfg = {};
86 int tiled, scanout, sw_indicator;
89 flags = NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM;
91 scanout = !!(usage & GRALLOC_USAGE_HW_FB);
93 tiled = !(usage & (GRALLOC_USAGE_SW_READ_OFTEN |
94 GRALLOC_USAGE_SW_WRITE_OFTEN));
96 sw_indicator = (usage & (GRALLOC_USAGE_SW_READ_OFTEN |
97 GRALLOC_USAGE_SW_WRITE_OFTEN));
99 if (info->arch >= NV_TESLA) {
109 *pitch = ALIGN(width * cpp, align);
112 if (info->arch >= NV_FERMI) {
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;
120 cfg.nvc0.tile_mode = 0x0010;
122 cfg.nvc0.tile_mode = 0x0000;
124 #ifndef SW_INDICATOR_FULLY_DISABLES_TILING
126 cfg.nvc0.memtype = 0x0000;
129 cfg.nvc0.memtype = 0x00fe;
131 align = NVC0_TILE_HEIGHT(cfg.nvc0.tile_mode);
132 height = ALIGN(height, align);
134 else if (info->arch >= NV_TESLA) {
136 cfg.nv50.tile_mode = 0x0040;
137 else if (height > 16)
138 cfg.nv50.tile_mode = 0x0030;
140 cfg.nv50.tile_mode = 0x0020;
142 cfg.nv50.tile_mode = 0x0010;
144 cfg.nv50.tile_mode = 0x0000;
146 #ifndef SW_INDICATOR_FULLY_DISABLES_TILING
148 cfg.nv50.memtype = 0x0000;
151 cfg.nv50.memtype = (scanout && cpp != 2) ?
154 align = NV50_TILE_HEIGHT(cfg.nv50.tile_mode);
155 height = ALIGN(height, align);
160 /* round down to the previous power of two */
166 align |= align >> 16;
169 align = MAX((info->dev->chipset >= NV_ARCH_40) ?
173 *pitch = ALIGN(*pitch, align);
174 cfg.nv04.surf_pitch = *pitch;
178 if (info->arch < NV_TESLA) {
179 #ifndef SW_INDICATOR_FULLY_DISABLES_TILING
181 cfg.nv04.surf_flags = 0x0000;
185 cfg.nv04.surf_flags |= NV04_BO_32BPP;
187 cfg.nv04.surf_flags |= NV04_BO_16BPP;
191 flags |= NOUVEAU_BO_CONTIG;
193 #ifdef SW_INDICATOR_FULLY_DISABLES_TILING
194 if (nouveau_bo_new(info->dev, flags, 0, *pitch * height, NULL, &bo)) {
196 if (nouveau_bo_new(info->dev, flags, 0, *pitch * height, &cfg, &bo)) {
198 ALOGE("failed to allocate bo (flags 0x%x, size %d)",
199 flags, *pitch * height);
206 static struct gralloc_drm_bo_t *nouveau_alloc(struct gralloc_drm_drv_t *drv,
207 struct gralloc_drm_handle_t *handle)
209 struct nouveau_info *info = (struct nouveau_info *) drv;
210 struct nouveau_buffer *nb;
213 cpp = gralloc_drm_get_bpp(handle->format);
215 ALOGE("unrecognized format 0x%x", handle->format);
219 nb = calloc(1, sizeof(*nb));
224 if (nouveau_bo_name_ref(info->dev, handle->name, &nb->bo)) {
225 ALOGE("failed to create nouveau bo from name %u",
232 int width, height, pitch;
234 width = handle->width;
235 height = handle->height;
236 gralloc_drm_align_geometry(handle->format, &width, &height);
238 nb->bo = alloc_bo(info, width, height, cpp,
239 handle->usage, &pitch);
241 ALOGE("failed to allocate nouveau bo %dx%dx%d",
242 handle->width, handle->height, cpp);
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);
255 handle->stride = pitch;
258 if (handle->usage & GRALLOC_USAGE_HW_FB)
259 nb->base.fb_handle = nb->bo->handle;
261 nb->base.handle = handle;
266 static void nouveau_free(struct gralloc_drm_drv_t *drv,
267 struct gralloc_drm_bo_t *bo)
269 struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
270 nouveau_bo_ref(NULL, &nb->bo);
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)
278 struct nouveau_info *info = (struct nouveau_info *) drv;
279 struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
283 flags = NOUVEAU_BO_RD;
285 flags |= NOUVEAU_BO_WR;
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);
295 static void nouveau_unmap(struct gralloc_drm_drv_t *drv,
296 struct gralloc_drm_bo_t *bo)
298 /* The bo is implicitly unmapped at nouveau_bo_ref(NULL, bo) */
301 static void nouveau_init_kms_features(struct gralloc_drm_drv_t *drv,
302 struct gralloc_drm_t *drm)
304 struct nouveau_info *info = (struct nouveau_info *) drv;
306 switch (drm->primary->fb_format) {
307 case HAL_PIXEL_FORMAT_BGRA_8888:
308 case HAL_PIXEL_FORMAT_RGB_565:
311 drm->primary->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
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;
322 static void nouveau_destroy(struct gralloc_drm_drv_t *drv)
324 struct nouveau_info *info = (struct nouveau_info *) drv;
326 nouveau_pushbuf_del(&info->pushbuf);
327 nouveau_object_del(&info->channel);
328 nouveau_client_del(&info->client);
329 nouveau_device_del(&info->dev);
333 static int nouveau_init(struct nouveau_info *info)
337 switch (info->dev->chipset & ~0xf) {
339 info->arch = NV_ARCH_04;
342 info->arch = NV_ARCH_10;
345 info->arch = NV_ARCH_20;
348 info->arch = NV_ARCH_30;
352 info->arch = NV_ARCH_40;
358 info->arch = NV_TESLA;
362 info->arch = NV_FERMI;
367 info->arch = NV_KEPLER;
371 info->arch = NV_MAXWELL;
374 info->arch = NV_PASCAL;
377 ALOGE("unknown nouveau chipset 0x%x", info->dev->chipset);
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);
391 struct gralloc_drm_drv_t *
392 gralloc_drm_drv_create_for_nouveau(int fd)
394 struct nouveau_info *info;
395 struct nv04_fifo nv04_data = { .vram = 0xbeef0201, .gart = 0xbeef0202 };
396 struct nvc0_fifo nvc0_data = { };
400 info = calloc(1, sizeof(*info));
405 err = nouveau_device_wrap(fd, 0, &info->dev);
407 ALOGE("failed to wrap existing nouveau device");
412 err = nouveau_init(info);
418 err = nouveau_client_new(info->dev, &info->client);
420 ALOGW("failed to create nouveau client: %d", err);
421 nouveau_device_del(&info->dev);
426 if (info->dev->chipset < 0xc0) {
428 size = sizeof(nv04_data);
432 size = sizeof(nvc0_data);
435 err = nouveau_object_new(&info->dev->object, 0,
436 NOUVEAU_FIFO_CHANNEL_CLASS, data, size,
440 ALOGE("failed to create nouveau channel: %d", err);
441 nouveau_client_del(&info->client);
442 nouveau_device_del(&info->dev);
447 err = nouveau_pushbuf_new(info->client, info->channel,
448 4, 32 * 1024, true, &info->pushbuf);
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);
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;