You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
8.0 KiB
363 lines
8.0 KiB
/*
|
|
* 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 <log/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;
|
|
}
|