/* * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> * Copyright (C) 2010-2011 LunarG Inc. * * Based on xf86-video-ati, which has * * Copyright © 2009 Red Hat, Inc. * * 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. */ /* XXX This driver assumes evergreen. */ #define LOG_TAG "GRALLOC-RADEON" #include <cutils/log.h> #include <stdlib.h> #include <errno.h> #include <drm.h> #include <radeon_drm.h> #include <radeon_bo_gem.h> #include <radeon_bo.h> #include "gralloc_drm.h" #include "gralloc_drm_priv.h" #include "radeon/radeon.h" #include "radeon/radeon_chipinfo_gen.h" #define RADEON_GPU_PAGE_SIZE 4096 #define MAX(a, b) (((a) > (b)) ? (a) : (b)) struct radeon_info { struct gralloc_drm_drv_t base; int fd; struct radeon_bo_manager *bufmgr; uint32_t chipset; RADEONChipFamily chip_family; int is_mobility; int is_igp; uint32_t tile_config; int num_channels; int num_banks; int group_bytes; /* r6xx+ tile config */ int have_tiling_info; int allow_color_tiling; int vram_size; int gart_size; }; struct radeon_buffer { struct gralloc_drm_bo_t base; struct radeon_bo *rbo; }; /* returns pitch alignment in pixels */ static int radeon_get_pitch_align(struct radeon_info *info, int bpe, uint32_t tiling) { int pitch_align = 1; if (info->chip_family >= CHIP_FAMILY_R600) { if (tiling & RADEON_TILING_MACRO) { /* general surface requirements */ pitch_align = (((info->group_bytes / 8) / bpe) * info->num_banks) * 8; /* further restrictions for scanout */ pitch_align = MAX(info->num_banks * 8, pitch_align); } else if (tiling & RADEON_TILING_MICRO) { /* general surface requirements */ pitch_align = MAX(8, (info->group_bytes / (8 * bpe))); /* further restrictions for scanout */ pitch_align = MAX(info->group_bytes / bpe, pitch_align); } else { if (info->have_tiling_info) /* linear aligned requirements */ pitch_align = MAX(64, info->group_bytes / bpe); else /* default to 512 elements if we don't know the real * group size otherwise the kernel may reject the CS * if the group sizes don't match as the pitch won't * be aligned properly. */ pitch_align = 512; } } else { /* general surface requirements */ if (tiling) pitch_align = 256 / bpe; else pitch_align = 64; } return pitch_align; } /* returns height alignment in pixels */ static int radeon_get_height_align(struct radeon_info *info, uint32_t tiling) { int height_align = 1; if (info->chip_family >= CHIP_FAMILY_R600) { if (tiling & RADEON_TILING_MACRO) height_align = info->num_channels * 8; else if (tiling & RADEON_TILING_MICRO) height_align = 8; else height_align = 8; } else { if (tiling) height_align = 16; else height_align = 1; } return height_align; } /* returns base alignment in bytes */ static int radeon_get_base_align(struct radeon_info *info, int bpe, uint32_t tiling) { int pixel_align = radeon_get_pitch_align(info, bpe, tiling); int height_align = radeon_get_height_align(info, tiling); int base_align = RADEON_GPU_PAGE_SIZE; if (info->chip_family >= CHIP_FAMILY_R600) { if (tiling & RADEON_TILING_MACRO) base_align = MAX(info->num_banks * info->num_channels * 8 * 8 * bpe, pixel_align * bpe * height_align); else { if (info->have_tiling_info) base_align = info->group_bytes; else /* default to 512 if we don't know the real * group size otherwise the kernel may reject the CS * if the group sizes don't match as the base won't * be aligned properly. */ base_align = 512; } } return base_align; } static uint32_t radeon_get_tiling(struct radeon_info *info, const struct gralloc_drm_handle_t *handle) { int sw = (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_SW_READ_MASK); if ((handle->usage & sw) && !info->allow_color_tiling) return 0; if (info->chip_family >= CHIP_FAMILY_R600) return RADEON_TILING_MICRO; else return RADEON_TILING_MACRO; } static struct radeon_bo *radeon_alloc(struct radeon_info *info, struct gralloc_drm_handle_t *handle) { struct radeon_bo *rbo; int aligned_width, aligned_height; int pitch, size, base_align; uint32_t tiling, domain; int cpp; cpp = gralloc_drm_get_bpp(handle->format); if (!cpp) { ALOGE("unrecognized format 0x%x", handle->format); return NULL; } tiling = radeon_get_tiling(info, handle); domain = RADEON_GEM_DOMAIN_VRAM; aligned_width = handle->width; aligned_height = handle->height; gralloc_drm_align_geometry(handle->format, &aligned_width, &aligned_height); if (handle->usage & (GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_TEXTURE)) { aligned_width = ALIGN(aligned_width, radeon_get_pitch_align(info, cpp, tiling)); aligned_height = ALIGN(aligned_height, radeon_get_height_align(info, tiling)); } if (!(handle->usage & (GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER)) && (handle->usage & GRALLOC_USAGE_SW_READ_OFTEN)) domain = RADEON_GEM_DOMAIN_GTT; pitch = aligned_width * cpp; size = ALIGN(aligned_height * pitch, RADEON_GPU_PAGE_SIZE); base_align = radeon_get_base_align(info, cpp, tiling); rbo = radeon_bo_open(info->bufmgr, 0, size, base_align, domain, 0); if (!rbo) { ALOGE("failed to allocate rbo %dx%dx%d", handle->width, handle->height, cpp); return NULL; } if (tiling) radeon_bo_set_tiling(rbo, tiling, pitch); if (radeon_gem_get_kernel_name(rbo, (uint32_t *) &handle->name)) { ALOGE("failed to flink rbo"); radeon_bo_unref(rbo); return NULL; } handle->stride = pitch; return rbo; } static void radeon_zero(struct radeon_info *info, struct radeon_bo *rbo) { /* should use HW clear... */ if (!radeon_bo_map(rbo, 1)) { memset(rbo->ptr, 0, rbo->size); radeon_bo_unmap(rbo); } } static struct gralloc_drm_bo_t * drm_gem_radeon_alloc(struct gralloc_drm_drv_t *drv, struct gralloc_drm_handle_t *handle) { struct radeon_info *info = (struct radeon_info *) drv; struct radeon_buffer *rbuf; rbuf = calloc(1, sizeof(*rbuf)); if (!rbuf) return NULL; if (handle->name) { rbuf->rbo = radeon_bo_open(info->bufmgr, handle->name, 0, 0, 0, 0); if (!rbuf->rbo) { ALOGE("failed to create rbo from name %u", handle->name); free(rbuf); return NULL; } } else { rbuf->rbo = radeon_alloc(info, handle); if (!rbuf->rbo) { free(rbuf); return NULL; } /* Android expects the buffer to be zeroed */ radeon_zero(info, rbuf->rbo); } if (handle->usage & GRALLOC_USAGE_HW_FB) rbuf->base.fb_handle = rbuf->rbo->handle; rbuf->base.handle = handle; return &rbuf->base; } static void drm_gem_radeon_free(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *bo) { struct radeon_buffer *rbuf = (struct radeon_buffer *) bo; radeon_bo_unref(rbuf->rbo); } static int drm_gem_radeon_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 radeon_buffer *rbuf = (struct radeon_buffer *) bo; int err; err = radeon_bo_map(rbuf->rbo, enable_write); if (!err) *addr = rbuf->rbo->ptr; return err; } static void drm_gem_radeon_unmap(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *bo) { struct radeon_buffer *rbuf = (struct radeon_buffer *) bo; radeon_bo_unmap(rbuf->rbo); } static void drm_gem_radeon_destroy(struct gralloc_drm_drv_t *drv) { struct radeon_info *info = (struct radeon_info *) drv; radeon_bo_manager_gem_dtor(info->bufmgr); free(info); } static int radeon_init_tile_config(struct radeon_info *info) { struct drm_radeon_info ginfo; uint32_t val; int ret; memset(&ginfo, 0, sizeof(ginfo)); ginfo.request = RADEON_INFO_TILING_CONFIG; ginfo.value = (long) &val; ret = drmCommandWriteRead(info->fd, DRM_RADEON_INFO, &ginfo, sizeof(ginfo)); if (ret) return ret; info->tile_config = val; if (info->chip_family >= CHIP_FAMILY_CEDAR) { switch (info->tile_config & 0xf) { case 0: info->num_channels = 1; break; case 1: info->num_channels = 2; break; case 2: info->num_channels = 4; break; case 3: info->num_channels = 8; break; default: return -EINVAL; break; } switch ((info->tile_config & 0xf0) >> 4) { case 0: info->num_banks = 4; break; case 1: info->num_banks = 8; break; case 2: info->num_banks = 16; break; default: return -EINVAL; break; } switch ((info->tile_config & 0xf00) >> 8) { case 0: info->group_bytes = 256; break; case 1: info->group_bytes = 512; break; default: return -EINVAL; break; } } else { switch ((info->tile_config & 0xe) >> 1) { case 0: info->num_channels = 1; break; case 1: info->num_channels = 2; break; case 2: info->num_channels = 4; break; case 3: info->num_channels = 8; break; default: return -EINVAL; break; } switch ((info->tile_config & 0x30) >> 4) { case 0: info->num_banks = 4; break; case 1: info->num_banks = 8; break; default: return -EINVAL; break; } switch ((info->tile_config & 0xc0) >> 6) { case 0: info->group_bytes = 256; break; case 1: info->group_bytes = 512; break; default: return -EINVAL; break; } } info->have_tiling_info = 1; return 0; } static int radeon_probe(struct radeon_info *info) { struct drm_radeon_info kinfo; struct drm_radeon_gem_info mminfo; unsigned int i; int err; memset(&kinfo, 0, sizeof(kinfo)); kinfo.request = RADEON_INFO_DEVICE_ID; kinfo.value = (long) &info->chipset; err = drmCommandWriteRead(info->fd, DRM_RADEON_INFO, &kinfo, sizeof(kinfo)); if (err) { ALOGE("failed to get device id"); return err; } for (i = 0; i < sizeof(RADEONCards) / sizeof(RADEONCards[0]); i++) { const RADEONCardInfo *card = &RADEONCards[i]; if (info->chipset == card->pci_device_id) { info->chip_family = card->chip_family; info->is_mobility = card->mobility; info->is_igp = card->igp; break; } } if (info->chip_family == CHIP_FAMILY_UNKNOW) { ALOGE("unknown device id 0x%04x", info->chipset); return -EINVAL; } if (info->chip_family >= CHIP_FAMILY_R600) { err = radeon_init_tile_config(info); if (err) { ALOGE("failed to get tiling config"); return err; } } else { /* No tiling config for family older than 06xx */ info->have_tiling_info = 0; } /* CPU cannot handle tiled buffers (need scratch buffers) */ info->allow_color_tiling = 0; memset(&mminfo, 0, sizeof(mminfo)); err = drmCommandWriteRead(info->fd, DRM_RADEON_GEM_INFO, &mminfo, sizeof(mminfo)); if (err) { ALOGE("failed to get gem info"); return err; } info->vram_size = mminfo.vram_visible; info->gart_size = mminfo.gart_size; ALOGI("detected chipset 0x%04x family 0x%02x (vram size %dMiB, gart size %dMiB)", info->chipset, info->chip_family, info->vram_size / 1024 / 1024, info->gart_size / 1024 / 1024); return 0; } struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_radeon(int fd) { struct radeon_info *info; info = calloc(1, sizeof(*info)); if (!info) return NULL; info->fd = fd; if (radeon_probe(info)) { free(info); return NULL; } info->bufmgr = radeon_bo_manager_gem_ctor(info->fd); if (!info->bufmgr) { ALOGE("failed to create buffer manager"); free(info); return NULL; } info->base.destroy = drm_gem_radeon_destroy; info->base.alloc = drm_gem_radeon_alloc; info->base.free = drm_gem_radeon_free; info->base.map = drm_gem_radeon_map; info->base.unmap = drm_gem_radeon_unmap; return &info->base; }