summaryrefslogtreecommitdiffstats
path: root/gralloc_drm_nouveau.c
diff options
context:
space:
mode:
authorChia-I Wu <olvaffe@gmail.com>2011-06-12 18:43:33 +0800
committerChia-I Wu <olvaffe@gmail.com>2011-06-13 09:30:28 +0800
commit64345b477bf3db20e0351d52c6d34a43cbb66c11 (patch)
treee68c727d5c8fb756bf8f2402250fc40bff1a8cec /gralloc_drm_nouveau.c
parent2ec32d4f949f04d0006fff50065c904626c2e581 (diff)
downloadexternal_drm_gralloc-64345b477bf3db20e0351d52c6d34a43cbb66c11.zip
external_drm_gralloc-64345b477bf3db20e0351d52c6d34a43cbb66c11.tar.gz
external_drm_gralloc-64345b477bf3db20e0351d52c6d34a43cbb66c11.tar.bz2
add nouveau support
Diffstat (limited to 'gralloc_drm_nouveau.c')
-rw-r--r--gralloc_drm_nouveau.c372
1 files changed, 372 insertions, 0 deletions
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;
+}