/* * Copyright (C) 2010-2011 Chia-I Wu * Copyright (C) 2010-2011 LunarG Inc. * * drm_gem_intel_copy is based on xorg-driver-intel, which has * * Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. * All Rights Reserved. * Copyright (c) 2005 Jesse Barnes * * 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-I915" #include #include #include #include #include #include #include #include "gralloc_drm.h" #include "gralloc_drm_priv.h" #define MI_NOOP (0) #define MI_BATCH_BUFFER_END (0x0a << 23) #define MI_FLUSH (0x04 << 23) #define MI_FLUSH_DW (0x26 << 23) #define MI_WRITE_DIRTY_STATE (1 << 4) #define MI_INVALIDATE_MAP_CACHE (1 << 0) #define XY_SRC_COPY_BLT_CMD ((2 << 29) | (0x53 << 22)) #define XY_SRC_COPY_BLT_WRITE_ALPHA (1 << 21) #define XY_SRC_COPY_BLT_WRITE_RGB (1 << 20) #define XY_SRC_COPY_BLT_SRC_TILED (1 << 15) #define XY_SRC_COPY_BLT_DST_TILED (1 << 11) #define DEBUG_BLT 0 struct intel_info { struct gralloc_drm_drv_t base; int fd; drm_intel_bufmgr *bufmgr; int gen; drm_intel_bo *batch_ibo; uint32_t *batch, *cur; int capacity, size; int exec_blt; }; struct intel_buffer { struct gralloc_drm_bo_t base; drm_intel_bo *ibo; uint32_t tiling; }; static int batch_next(struct intel_info *info) { info->cur = info->batch; if (info->batch_ibo) drm_intel_bo_unreference(info->batch_ibo); info->batch_ibo = drm_intel_bo_alloc(info->bufmgr, "gralloc-batchbuffer", info->size, 4096); return (info->batch_ibo) ? 0 : -ENOMEM; } static int batch_count(struct intel_info *info) { return info->cur - info->batch; } static void batch_dword(struct intel_info *info, uint32_t dword) { *info->cur++ = dword; } static int batch_reloc(struct intel_info *info, struct gralloc_drm_bo_t *bo, uint32_t read_domains, uint32_t write_domain) { struct intel_buffer *target = (struct intel_buffer *) bo; uint32_t offset = (info->cur - info->batch) * sizeof(info->batch[0]); int ret; ret = drm_intel_bo_emit_reloc(info->batch_ibo, offset, target->ibo, 0, read_domains, write_domain); if (!ret) batch_dword(info, target->ibo->offset); return ret; } static int batch_reloc64(struct intel_info *info, struct gralloc_drm_bo_t *bo, uint32_t read_domains, uint32_t write_domain) { struct intel_buffer *target = (struct intel_buffer *) bo; uint32_t offset = (info->cur - info->batch) * sizeof(info->batch[0]); int ret; ret = drm_intel_bo_emit_reloc(info->batch_ibo, offset, target->ibo, 0, read_domains, write_domain); if (!ret) { batch_dword(info, target->ibo->offset64); batch_dword(info, target->ibo->offset64 >> 32); } return ret; } static int batch_flush(struct intel_info *info) { int size, ret; batch_dword(info, MI_BATCH_BUFFER_END); size = batch_count(info); if (size & 1) { batch_dword(info, MI_NOOP); size = batch_count(info); } size *= sizeof(info->batch[0]); ret = drm_intel_bo_subdata(info->batch_ibo, 0, size, info->batch); if (ret) { ALOGE("failed to subdata batch"); goto fail; } ret = drm_intel_bo_mrb_exec(info->batch_ibo, size, NULL, 0, 0, info->exec_blt); if (ret) { ALOGE("failed to exec batch"); goto fail; } return batch_next(info); fail: info->cur = info->batch; return ret; } static int batch_reserve(struct intel_info *info, int count) { int ret = 0; if (batch_count(info) + count > info->capacity) ret = batch_flush(info); return ret; } static void batch_destroy(struct intel_info *info) { if (info->batch_ibo) { drm_intel_bo_unreference(info->batch_ibo); info->batch_ibo = NULL; } if (info->batch) { free(info->batch); info->batch = NULL; } } static int batch_init(struct intel_info *info) { int ret; info->capacity = 512; info->size = (info->capacity + 16) * sizeof(info->batch[0]); info->batch = malloc(info->size); if (!info->batch) return -ENOMEM; ret = batch_next(info); if (ret) { free(info->batch); info->batch = NULL; } return ret; } static void intel_resolve_format(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *bo, uint32_t *pitches, uint32_t *offsets, uint32_t *handles) { /* * TODO - should take account hw specific padding, alignment * for camera, video decoder etc. */ struct intel_buffer *ib = (struct intel_buffer *) bo; memset(pitches, 0, 4 * sizeof(uint32_t)); memset(offsets, 0, 4 * sizeof(uint32_t)); memset(handles, 0, 4 * sizeof(uint32_t)); pitches[0] = ib->base.handle->stride; handles[0] = ib->base.fb_handle; switch(ib->base.handle->format) { case HAL_PIXEL_FORMAT_YV12: // U and V stride are half of Y plane pitches[2] = pitches[0]/2; pitches[1] = pitches[0]/2; // like I420 but U and V are in reverse order offsets[2] = offsets[0] + pitches[0] * ib->base.handle->height; offsets[1] = offsets[2] + pitches[2] * ib->base.handle->height/2; handles[1] = handles[2] = handles[0]; break; case HAL_PIXEL_FORMAT_DRM_NV12: // U and V are interleaved in 2nd plane pitches[1] = pitches[0]; offsets[1] = offsets[0] + pitches[0] * ib->base.handle->height; handles[1] = handles[0]; break; } } static void intel_blit(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *dst, struct gralloc_drm_bo_t *src, uint16_t dst_x1, uint16_t dst_y1, uint16_t dst_x2, uint16_t dst_y2, uint16_t src_x1, uint16_t src_y1, uint16_t src_x2, uint16_t src_y2) { struct intel_info *info = (struct intel_info *) drv; struct intel_buffer *dst_ib = (struct intel_buffer *) dst; struct intel_buffer *src_ib = (struct intel_buffer *) src; drm_intel_bo *bo_table[3]; uint32_t cmd, br13, dst_pitch, src_pitch; /* * XY_SRC_COPY_BLT_CMD does not support scaling, * rectangle dimensions much match */ if (src_x2 - src_x1 != dst_x2 - dst_x1 || src_y2 - src_y1 != dst_y2 - dst_y1) { ALOGE("%s, src and dst rect must match", __func__); return; } if (dst->handle->format != src->handle->format) { ALOGE("%s, src and dst format must match", __func__); return; } /* nothing to blit */ if (src_x2 <= src_x1 || src_y2 <= src_y1) return; /* clamp x2, y2 to surface size */ if (src_x2 > src->handle->width) src_x2 = src->handle->width; if (src_y2 > src->handle->height) src_y2 = src->handle->height; if (dst_x2 > dst->handle->width) dst_x2 = dst->handle->width; if (dst_y2 > dst->handle->height) dst_y2 = dst->handle->height; bo_table[0] = info->batch_ibo; bo_table[1] = src_ib->ibo; bo_table[2] = dst_ib->ibo; if (drm_intel_bufmgr_check_aperture_space(bo_table, 3)) { if (batch_flush(info)) return; assert(!drm_intel_bufmgr_check_aperture_space(bo_table, 3)); } cmd = XY_SRC_COPY_BLT_CMD; br13 = 0xcc << 16; /* ROP_S/GXcopy */ dst_pitch = dst->handle->stride; src_pitch = src->handle->stride; /* Blit pitch must be dword-aligned. Otherwise, the hardware appears to * drop the low bits. */ if (src_pitch % 4 != 0 || dst_pitch % 4 != 0) { ALOGE("%s, src and dst pitch must be dword aligned", __func__); return; } switch (gralloc_drm_get_bpp(dst->handle->format)) { case 1: break; case 2: br13 |= (1 << 24); break; case 4: br13 |= (1 << 24) | (1 << 25); cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; break; default: ALOGE("%s, copy with unsupported format", __func__); return; } if (info->gen >= 40) { if (dst_ib->tiling != I915_TILING_NONE) { assert(dst_pitch % 512 == 0); dst_pitch >>= 2; cmd |= XY_SRC_COPY_BLT_DST_TILED; } if (src_ib->tiling != I915_TILING_NONE) { assert(src_pitch % 512 == 0); src_pitch >>= 2; cmd |= XY_SRC_COPY_BLT_SRC_TILED; } } unsigned length = (info->gen >= 80) ? 10 : 8; if (batch_reserve(info, length)) return; ALOGD_IF(DEBUG_BLT, "running batch commands, gen=%d tiling: [%d, %d]. dst=[%d, %d, %d, %d], " "src=[%d, %d, %d, %d], pitch=[%d, %d]", info->gen, dst_ib->tiling, src_ib->tiling, dst_x1, dst_y1, dst_x2, dst_y2, src_x1, src_y1, src_x2, src_y2, dst_pitch, src_pitch); batch_dword(info, cmd | (length - 2)); batch_dword(info, br13 | (uint16_t)dst_pitch); batch_dword(info, (dst_y1 << 16) | dst_x1); batch_dword(info, (dst_y2 << 16) | dst_x2); if (info->gen >= 80) { batch_reloc64(info, dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); } else { batch_reloc(info, dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); } batch_dword(info, (src_y1 << 16) | src_x1); batch_dword(info, (uint16_t)src_pitch); if (info->gen >= 80) { batch_reloc64(info, src, I915_GEM_DOMAIN_RENDER, 0); } else { batch_reloc(info, src, I915_GEM_DOMAIN_RENDER, 0); } if (info->gen >= 60) { batch_reserve(info, 4); batch_dword(info, MI_FLUSH_DW | 2); batch_dword(info, 0); batch_dword(info, 0); batch_dword(info, 0); } else { int flags = (info->gen >= 40) ? 0 : MI_WRITE_DIRTY_STATE | MI_INVALIDATE_MAP_CACHE; batch_reserve(info, 1); batch_dword(info, MI_FLUSH | flags); } batch_flush(info); } static drm_intel_bo *alloc_ibo(struct intel_info *info, const struct gralloc_drm_handle_t *handle, uint32_t *tiling, unsigned long *stride) { drm_intel_bo *ibo; const char *name; int aligned_width, aligned_height, bpp; unsigned long flags; flags = 0; bpp = gralloc_drm_get_bpp(handle->format); if (!bpp) { ALOGE("unrecognized format 0x%x", handle->format); return NULL; } 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) { unsigned long max_stride; max_stride = 32 * 1024; if (info->gen < 50) max_stride /= 2; if (info->gen < 40) max_stride /= 2; name = "gralloc-fb"; aligned_width = ALIGN(aligned_width, 64); flags = BO_ALLOC_FOR_RENDER; *tiling = I915_TILING_X; *stride = aligned_width * bpp; if (*stride > max_stride) { *tiling = I915_TILING_NONE; max_stride = 32 * 1024; if (*stride > max_stride) return NULL; } while (1) { ibo = drm_intel_bo_alloc_tiled(info->bufmgr, name, aligned_width, aligned_height, bpp, tiling, stride, flags); if (!ibo || *stride > max_stride) { if (ibo) { drm_intel_bo_unreference(ibo); ibo = NULL; } if (*tiling != I915_TILING_NONE) { /* retry */ *tiling = I915_TILING_NONE; max_stride = 32 * 1024; continue; } } if (ibo) drm_intel_bo_disable_reuse(ibo); break; } } else { if (handle->usage & (GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)) *tiling = I915_TILING_NONE; else if ((handle->usage & GRALLOC_USAGE_HW_RENDER) || ((handle->usage & GRALLOC_USAGE_HW_TEXTURE) && handle->width >= 64)) *tiling = I915_TILING_X; else *tiling = I915_TILING_NONE; if (handle->usage & GRALLOC_USAGE_HW_TEXTURE) { name = "gralloc-texture"; /* see 2D texture layout of DRI drivers */ aligned_width = ALIGN(aligned_width, 4); aligned_height = ALIGN(aligned_height, 2); } else { name = "gralloc-buffer"; } if (handle->usage & GRALLOC_USAGE_HW_RENDER) flags = BO_ALLOC_FOR_RENDER; ibo = drm_intel_bo_alloc_tiled(info->bufmgr, name, aligned_width, aligned_height, bpp, tiling, stride, flags); } return ibo; } static struct gralloc_drm_bo_t *intel_alloc(struct gralloc_drm_drv_t *drv, struct gralloc_drm_handle_t *handle) { struct intel_info *info = (struct intel_info *) drv; struct intel_buffer *ib; ib = calloc(1, sizeof(*ib)); if (!ib) return NULL; if (handle->name) { uint32_t dummy; ib->ibo = drm_intel_bo_gem_create_from_name(info->bufmgr, "gralloc-r", handle->name); if (!ib->ibo) { ALOGE("failed to create ibo from name %u", handle->name); free(ib); return NULL; } if (drm_intel_bo_get_tiling(ib->ibo, &ib->tiling, &dummy)) { ALOGE("failed to get ibo tiling"); drm_intel_bo_unreference(ib->ibo); free(ib); return NULL; } } else { unsigned long stride; ib->ibo = alloc_ibo(info, handle, &ib->tiling, &stride); if (!ib->ibo) { ALOGE("failed to allocate ibo %dx%d (format %d)", handle->width, handle->height, handle->format); free(ib); return NULL; } handle->stride = stride; if (drm_intel_bo_flink(ib->ibo, (uint32_t *) &handle->name)) { ALOGE("failed to flink ibo"); drm_intel_bo_unreference(ib->ibo); free(ib); return NULL; } } ib->base.fb_handle = ib->ibo->handle; ib->base.handle = handle; return &ib->base; } static void intel_free(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *bo) { struct intel_buffer *ib = (struct intel_buffer *) bo; drm_intel_bo_unreference(ib->ibo); free(ib); } static int intel_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 intel_buffer *ib = (struct intel_buffer *) bo; int err; if (ib->tiling != I915_TILING_NONE || (ib->base.handle->usage & GRALLOC_USAGE_HW_FB)) err = drm_intel_gem_bo_map_gtt(ib->ibo); else err = drm_intel_bo_map(ib->ibo, enable_write); if (!err) *addr = ib->ibo->virtual; return err; } static void intel_unmap(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *bo) { struct intel_buffer *ib = (struct intel_buffer *) bo; if (ib->tiling != I915_TILING_NONE || (ib->base.handle->usage & GRALLOC_USAGE_HW_FB)) drm_intel_gem_bo_unmap_gtt(ib->ibo); else drm_intel_bo_unmap(ib->ibo); } #include "intel_chipset.h" /* for platform detection macros */ static void intel_init_kms_features(struct gralloc_drm_drv_t *drv, struct gralloc_drm_t *drm) { struct intel_info *info = (struct intel_info *) drv; struct drm_i915_getparam gp; int pageflipping, id, has_blt; switch (drm->primary->fb_format) { case HAL_PIXEL_FORMAT_BGRA_8888: case HAL_PIXEL_FORMAT_RGB_565: break; default: drm->primary->fb_format = HAL_PIXEL_FORMAT_BGRA_8888; break; } drm->mode_quirk_vmwgfx = 0; /* why? */ drm->mode_sync_flip = 1; memset(&gp, 0, sizeof(gp)); gp.param = I915_PARAM_HAS_PAGEFLIPPING; gp.value = &pageflipping; if (drmCommandWriteRead(drm->fd, DRM_I915_GETPARAM, &gp, sizeof(gp))) pageflipping = 0; memset(&gp, 0, sizeof(gp)); gp.param = I915_PARAM_CHIPSET_ID; gp.value = &id; if (drmCommandWriteRead(drm->fd, DRM_I915_GETPARAM, &gp, sizeof(gp))) id = 0; memset(&gp, 0, sizeof(gp)); gp.param = I915_PARAM_HAS_BLT; gp.value = &has_blt; if (drmCommandWriteRead(drm->fd, DRM_I915_GETPARAM, &gp, sizeof(gp))) has_blt = 0; info->exec_blt = has_blt ? I915_EXEC_BLT : 0; /* GEN4, G4X, GEN5, GEN6, GEN7 */ if ((IS_9XX(id) || IS_G4X(id)) && !IS_GEN3(id)) { if (IS_GEN9(id)) info->gen = 90; else if (IS_GEN8(id)) info->gen = 80; else if (IS_GEN7(id)) info->gen = 70; else if (IS_GEN6(id)) info->gen = 60; else if (IS_GEN5(id)) info->gen = 50; else info->gen = 40; } else { info->gen = 30; } if (pageflipping && info->gen > 30) drm->swap_mode = DRM_SWAP_FLIP; else if (info->batch && info->gen == 30) drm->swap_mode = DRM_SWAP_COPY; else drm->swap_mode = DRM_SWAP_SETCRTC; if (drm->resources) { int pipe; pipe = drm_intel_get_pipe_from_crtc_id(info->bufmgr, drm->primary->crtc_id); drm->swap_interval = (pipe >= 0) ? 1 : 0; drm->vblank_secondary = (pipe > 0); } else { drm->swap_interval = 0; } } static void intel_destroy(struct gralloc_drm_drv_t *drv) { struct intel_info *info = (struct intel_info *) drv; batch_destroy(info); drm_intel_bufmgr_destroy(info->bufmgr); free(info); } struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_intel(int fd) { struct intel_info *info; info = calloc(1, sizeof(*info)); if (!info) { ALOGE("failed to allocate driver info"); return NULL; } info->fd = fd; info->bufmgr = drm_intel_bufmgr_gem_init(info->fd, 16 * 1024); if (!info->bufmgr) { ALOGE("failed to create buffer manager"); free(info); return NULL; } batch_init(info); info->base.destroy = intel_destroy; info->base.init_kms_features = intel_init_kms_features; info->base.alloc = intel_alloc; info->base.free = intel_free; info->base.map = intel_map; info->base.unmap = intel_unmap; info->base.blit = intel_blit; info->base.resolve_format = intel_resolve_format; return &info->base; }