OSDN Git Service

add nouveau support
authorChia-I Wu <olvaffe@gmail.com>
Sun, 12 Jun 2011 10:43:33 +0000 (18:43 +0800)
committerChia-I Wu <olvaffe@gmail.com>
Mon, 13 Jun 2011 01:30:28 +0000 (09:30 +0800)
Android.mk
gralloc_drm.c
gralloc_drm_nouveau.c [new file with mode: 0644]
gralloc_drm_priv.h

index 8aec666..5bd4c45 100644 (file)
@@ -36,6 +36,9 @@ DRM_USES_RADEON := $(findstring true, \
        $(BOARD_USES_R300G) \
        $(BOARD_USES_R600G))
 
+DRM_USES_NOUVEAU := $(findstring true, \
+       $(BOARD_USES_NOUVEAU))
+
 LOCAL_SRC_FILES := \
        gralloc.c \
        gralloc_drm.c \
@@ -68,6 +71,13 @@ LOCAL_CFLAGS += -DENABLE_RADEON
 LOCAL_SHARED_LIBRARIES += libdrm_radeon
 endif # DRM_USES_RADEON
 
+ifeq ($(strip $(DRM_USES_NOUVEAU)),true)
+LOCAL_SRC_FILES += gralloc_drm_nouveau.c
+LOCAL_C_INCLUDES += external/drm/nouveau
+LOCAL_CFLAGS += -DENABLE_NOUVEAU
+LOCAL_SHARED_LIBRARIES += libdrm_nouveau
+endif # DRM_USES_NOUVEAU
+
 LOCAL_MODULE := gralloc.$(TARGET_PRODUCT)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
index 63e504b..a88e9ea 100644 (file)
@@ -76,6 +76,10 @@ init_drv_from_fd(int fd)
                if (!drv && !strcmp(version->name, "radeon"))
                        drv = gralloc_drm_drv_create_for_radeon(fd);
 #endif
+#ifdef ENABLE_NOUVEAU
+               if (!drv && !strcmp(version->name, "nouveau"))
+                       drv = gralloc_drm_drv_create_for_nouveau(fd);
+#endif
 #ifdef ENABLE_VMWGFX
                if (!drv && !strcmp(version->name, "vmwgfx"))
                        drv = gralloc_drm_drv_create_for_vmwgfx(fd);
diff --git a/gralloc_drm_nouveau.c b/gralloc_drm_nouveau.c
new file mode 100644 (file)
index 0000000..d5fce78
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2011 Chia-I Wu <olvaffe@gmail.com>
+ * Copyright (C) 2011 LunarG Inc.
+ *
+ * Based on xf86-video-nouveau, which has
+ *
+ * Copyright © 2007 Red Hat, Inc.
+ * Copyright © 2008 Maarten Maathuis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define LOG_TAG "GRALLOC-NOUVEAU"
+
+#include <cutils/log.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <drm.h>
+#include <nouveau_drmif.h>
+#include <nouveau_channel.h>
+#include <nouveau_bo.h>
+
+#include "gralloc_drm.h"
+#include "gralloc_drm_priv.h"
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define ALIGN(val, align) (((val) + (align) - 1) & ~((align) - 1))
+
+#define NVC0_TILE_HEIGHT(m) (8 << ((m) >> 4))
+
+enum {
+       NvDmaFB = 0xd8000001,
+       NvDmaTT = 0xd8000002,
+};
+
+struct nouveau_info {
+       struct gralloc_drm_drv_t base;
+
+       int fd;
+       struct nouveau_device *dev;
+       struct nouveau_channel *chan;
+       int arch;
+       int tiled_scanout;
+};
+
+struct nouveau_buffer {
+       struct gralloc_drm_bo_t base;
+
+       struct nouveau_bo *bo;
+};
+
+static struct nouveau_bo *alloc_bo(struct nouveau_info *info,
+               int width, int height, int cpp, int usage, int *pitch)
+{
+       struct nouveau_bo *bo = NULL;
+       int flags, tile_mode, tile_flags;
+       int tiled, scanout;
+       unsigned int align;
+
+       flags = NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM;
+       tile_mode = 0;
+       tile_flags = 0;
+
+       scanout = !!(usage & GRALLOC_USAGE_HW_FB);
+
+       tiled = !(usage & (GRALLOC_USAGE_SW_READ_OFTEN |
+                          GRALLOC_USAGE_SW_WRITE_OFTEN));
+       if (!info->chan)
+               tiled = 0;
+       else if (scanout && info->tiled_scanout)
+               tiled = 1;
+
+       /* calculate pitch align */
+       align = 64;
+       if (info->arch >= 0x50) {
+               if (scanout && !info->tiled_scanout)
+                       align = 256;
+               else
+                       tiled = 1;
+       }
+
+       *pitch = ALIGN(width * cpp, align);
+
+       if (tiled) {
+               if (info->arch >= 0xc0) {
+                       if (height > 64)
+                               tile_mode = 0x40;
+                       else if (height > 32)
+                               tile_mode = 0x30;
+                       else if (height > 16)
+                               tile_mode = 0x20;
+                       else if (height > 8)
+                               tile_mode = 0x10;
+                       else
+                               tile_mode = 0x00;
+
+                       tile_flags = 0xfe00;
+
+                       align = NVC0_TILE_HEIGHT(tile_mode);
+                       height = ALIGN(height, align);
+               }
+               else if (info->arch >= 0x50) {
+                       if (height > 32)
+                               tile_mode = 4;
+                       else if (height > 16)
+                               tile_mode = 3;
+                       else if (height > 8)
+                               tile_mode = 2;
+                       else if (height > 4)
+                               tile_mode = 1;
+                       else
+                               tile_mode = 0;
+
+                       tile_flags = (scanout && cpp != 2) ? 0x7a00 : 0x7000;
+
+                       align = 1 << (tile_mode + 2);
+                       height = ALIGN(height, align);
+               }
+               else {
+                       align = *pitch / 4;
+
+                       /* round down to the previous power of two */
+                       align >>= 1;
+                       align |= align >> 1;
+                       align |= align >> 2;
+                       align |= align >> 4;
+                       align |= align >> 8;
+                       align |= align >> 16;
+                       align++;
+
+                       align = MAX((info->dev->chipset >= 0x40) ? 1024 : 256,
+                                       align);
+
+                       /* adjust pitch */
+                       *pitch = ALIGN(*pitch, align);
+
+                       tile_mode = *pitch;
+               }
+       }
+
+       if (cpp == 4)
+               tile_flags |= NOUVEAU_BO_TILE_32BPP;
+       else if (cpp == 2)
+               tile_flags |= NOUVEAU_BO_TILE_16BPP;
+
+       if (scanout)
+               tile_flags |= NOUVEAU_BO_TILE_SCANOUT;
+
+       if (nouveau_bo_new_tile(info->dev, flags, 0, *pitch * height,
+                               tile_mode, tile_flags, &bo)) {
+               LOGE("failed to allocate bo (flags 0x%x, size %d, tile_mode 0x%x, tile_flags 0x%x)",
+                               flags, *pitch * height, tile_mode, tile_flags);
+               bo = NULL;
+       }
+
+       return bo;
+}
+
+static struct gralloc_drm_bo_t *
+nouveau_alloc(struct gralloc_drm_drv_t *drv, struct gralloc_drm_handle_t *handle)
+{
+       struct nouveau_info *info = (struct nouveau_info *) drv;
+       struct nouveau_buffer *nb;
+       int cpp;
+
+       cpp = gralloc_drm_get_bpp(handle->format);
+       if (!cpp) {
+               LOGE("unrecognized format 0x%x", handle->format);
+               return NULL;
+       }
+
+       nb = calloc(1, sizeof(*nb));
+       if (!nb)
+               return NULL;
+
+       if (handle->name) {
+               if (nouveau_bo_handle_ref(info->dev, handle->name, &nb->bo)) {
+                       LOGE("failed to create nouveau bo from name %u",
+                                       handle->name);
+                       free(nb);
+                       return NULL;
+               }
+       }
+       else {
+               int pitch;
+
+               nb->bo = alloc_bo(info, handle->width, handle->height,
+                               cpp, handle->usage, &pitch);
+               if (!nb->bo) {
+                       LOGE("failed to allocate nouveau bo %dx%dx%d",
+                                       handle->width, handle->height, cpp);
+                       free(nb);
+                       return NULL;
+               }
+
+               if (nouveau_bo_handle_get(nb->bo,
+                                       (uint32_t *) &handle->name)) {
+                       LOGE("failed to flink nouveau bo");
+                       nouveau_bo_ref(NULL, &nb->bo);
+                       free(nb);
+                       return NULL;
+               }
+
+               handle->stride = pitch;
+       }
+
+       if (handle->usage & GRALLOC_USAGE_HW_FB)
+               nb->base.fb_handle = nb->bo->handle;
+
+       nb->base.handle = handle;
+
+       return &nb->base;
+}
+
+static void nouveau_free(struct gralloc_drm_drv_t *drv,
+               struct gralloc_drm_bo_t *bo)
+{
+       struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
+       nouveau_bo_ref(NULL, &nb->bo);
+       free(nb);
+}
+
+static int nouveau_map(struct gralloc_drm_drv_t *drv,
+               struct gralloc_drm_bo_t *bo, int x, int y, int w, int h,
+               int enable_write, void **addr)
+{
+       struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
+       uint32_t flags;
+       int err;
+
+       flags = NOUVEAU_BO_RD;
+       if (enable_write)
+               flags |= NOUVEAU_BO_WR;
+
+       /* TODO if tiled, allocate a linear copy of bo in GART and map it */
+       err = nouveau_bo_map(nb->bo, flags);
+       if (!err)
+               *addr = nb->bo->map;
+
+       return err;
+}
+
+static void nouveau_unmap(struct gralloc_drm_drv_t *drv,
+               struct gralloc_drm_bo_t *bo)
+{
+       struct nouveau_buffer *nb = (struct nouveau_buffer *) bo;
+       /* TODO if tiled, unmap the linear bo and copy back */
+       nouveau_bo_unmap(nb->bo);
+}
+
+static void nouveau_init_kms_features(struct gralloc_drm_drv_t *drv,
+               struct gralloc_drm_t *drm)
+{
+       struct nouveau_info *info = (struct nouveau_info *) drv;
+
+       drm->mode_dirty_fb = 0;
+       drm->swap_mode = (info->chan) ? DRM_SWAP_FLIP : DRM_SWAP_SETCRTC;
+       drm->mode_sync_flip = 1;
+       drm->swap_interval = 1;
+       drm->vblank_secondary = 0;
+}
+
+static void nouveau_destroy(struct gralloc_drm_drv_t *drv)
+{
+       struct nouveau_info *info = (struct nouveau_info *) drv;
+
+       if (info->chan)
+               nouveau_channel_free(&info->chan);
+       nouveau_device_close(&info->dev);
+       free(info);
+}
+
+static int nouveau_init(struct nouveau_info *info)
+{
+       int err = 0;
+
+       switch (info->dev->chipset & 0xf0) {
+       case 0x00:
+               info->arch = 0x04;
+               break;
+       case 0x10:
+               info->arch = 0x10;
+               break;
+       case 0x20:
+               info->arch = 0x20;
+               break;
+       case 0x30:
+               info->arch = 0x30;
+               break;
+       case 0x40:
+       case 0x60:
+               info->arch = 0x40;
+               break;
+       case 0x50:
+       case 0x80:
+       case 0x90:
+       case 0xa0:
+               info->arch = 0x50;
+               break;
+       case 0xc0:
+               info->arch = 0xc0;
+               break;
+       default:
+               LOGE("unknown nouveau chipset 0x%x", info->dev->chipset);
+               err = -EINVAL;
+               break;
+       }
+
+       info->tiled_scanout = (info->chan != NULL);
+
+       return err;
+}
+
+struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_nouveau(int fd)
+{
+       struct nouveau_info *info;
+       int err;
+
+       info = calloc(1, sizeof(*info));
+       if (!info)
+               return NULL;
+
+       info->fd = fd;
+       err = nouveau_device_open_existing(&info->dev, 0, info->fd, 0);
+       if (err) {
+               LOGE("failed to create nouveau device");
+               free(info);
+               return NULL;
+       }
+
+       err = nouveau_channel_alloc(info->dev, NvDmaFB, NvDmaTT,
+                       24 * 1024, &info->chan);
+       if (err) {
+               /* make it non-fatal temporarily as it may require firmwares */
+               LOGW("failed to create nouveau channel");
+               info->chan = NULL;
+       }
+
+       err = nouveau_init(info);
+       if (err) {
+               if (info->chan)
+                       nouveau_channel_free(&info->chan);
+               nouveau_device_close(&info->dev);
+               free(info);
+               return NULL;
+       }
+
+       info->base.destroy = nouveau_destroy;
+       info->base.init_kms_features = nouveau_init_kms_features;
+       info->base.alloc = nouveau_alloc;
+       info->base.free = nouveau_free;
+       info->base.map = nouveau_map;
+       info->base.unmap = nouveau_unmap;
+
+       return &info->base;
+}
index 6891faa..d623f8b 100644 (file)
@@ -110,6 +110,7 @@ struct gralloc_drm_bo_t {
 
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_intel(int fd);
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_radeon(int fd);
+struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_nouveau(int fd);
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_pipe(int fd);
 
 #endif /* _GRALLOC_DRM_PRIV_H_ */