/* * 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 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)) { ALOGE("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) { ALOGE("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)) { ALOGE("failed to create nouveau bo from name %u", handle->name); free(nb); return NULL; } } else { int width, height, pitch; width = handle->width; height = handle->height; gralloc_drm_align_geometry(handle->format, &width, &height); nb->bo = alloc_bo(info, width, height, cpp, handle->usage, &pitch); if (!nb->bo) { ALOGE("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)) { ALOGE("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_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: ALOGE("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) { ALOGE("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 */ ALOGW("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.alloc = nouveau_alloc; info->base.free = nouveau_free; info->base.map = nouveau_map; info->base.unmap = nouveau_unmap; return &info->base; }