diff options
author | Chia-I Wu <olvaffe@gmail.com> | 2011-06-12 18:43:33 +0800 |
---|---|---|
committer | Chia-I Wu <olvaffe@gmail.com> | 2011-06-13 09:30:28 +0800 |
commit | 64345b477bf3db20e0351d52c6d34a43cbb66c11 (patch) | |
tree | e68c727d5c8fb756bf8f2402250fc40bff1a8cec | |
parent | 2ec32d4f949f04d0006fff50065c904626c2e581 (diff) | |
download | external_drm_gralloc-64345b477bf3db20e0351d52c6d34a43cbb66c11.zip external_drm_gralloc-64345b477bf3db20e0351d52c6d34a43cbb66c11.tar.gz external_drm_gralloc-64345b477bf3db20e0351d52c6d34a43cbb66c11.tar.bz2 |
add nouveau support
-rw-r--r-- | Android.mk | 10 | ||||
-rw-r--r-- | gralloc_drm.c | 4 | ||||
-rw-r--r-- | gralloc_drm_nouveau.c | 372 | ||||
-rw-r--r-- | gralloc_drm_priv.h | 1 |
4 files changed, 387 insertions, 0 deletions
@@ -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 diff --git a/gralloc_drm.c b/gralloc_drm.c index 63e504b..a88e9ea 100644 --- a/gralloc_drm.c +++ b/gralloc_drm.c @@ -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 index 0000000..d5fce78 --- /dev/null +++ b/gralloc_drm_nouveau.c @@ -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; +} diff --git a/gralloc_drm_priv.h b/gralloc_drm_priv.h index 6891faa..d623f8b 100644 --- a/gralloc_drm_priv.h +++ b/gralloc_drm_priv.h @@ -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_ */ |