diff options
author | Chia-I Wu <olvaffe@gmail.com> | 2011-06-12 16:21:30 +0800 |
---|---|---|
committer | Chia-I Wu <olvaffe@gmail.com> | 2011-06-12 16:21:30 +0800 |
commit | 2ec32d4f949f04d0006fff50065c904626c2e581 (patch) | |
tree | 7971ee3431e97f7fe7c0bc6ae17948866e10de1e | |
download | external_drm_gralloc-2ec32d4f949f04d0006fff50065c904626c2e581.zip external_drm_gralloc-2ec32d4f949f04d0006fff50065c904626c2e581.tar.gz external_drm_gralloc-2ec32d4f949f04d0006fff50065c904626c2e581.tar.bz2 |
initial commit
-rw-r--r-- | Android.mk | 77 | ||||
-rw-r--r-- | dri/intel_chipset.h | 172 | ||||
-rw-r--r-- | gralloc.c | 364 | ||||
-rw-r--r-- | gralloc_drm.c | 304 | ||||
-rw-r--r-- | gralloc_drm.h | 84 | ||||
-rw-r--r-- | gralloc_drm_handle.h | 63 | ||||
-rw-r--r-- | gralloc_drm_intel.c | 585 | ||||
-rw-r--r-- | gralloc_drm_kms.c | 499 | ||||
-rw-r--r-- | gralloc_drm_priv.h | 115 | ||||
-rw-r--r-- | gralloc_drm_radeon.c | 366 |
10 files changed, 2629 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..8aec666 --- /dev/null +++ b/Android.mk @@ -0,0 +1,77 @@ +# Copyright (C) 2010 Chia-I Wu <olvaffe@gmail.com> +# Copyright (C) 2010-2011 LunarG 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. + +# Android.mk for drm_gralloc + +ifeq ($(strip $(BOARD_USES_DRM)),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +DRM_USES_INTEL := $(findstring true, \ + $(BOARD_USES_I915C) \ + $(BOARD_USES_I965C) \ + $(BOARD_USES_I915G) \ + $(BOARD_USES_I965G)) + +DRM_USES_RADEON := $(findstring true, \ + $(BOARD_USES_R300G) \ + $(BOARD_USES_R600G)) + +LOCAL_SRC_FILES := \ + gralloc.c \ + gralloc_drm.c \ + gralloc_drm_kms.c + +LOCAL_C_INCLUDES := \ + external/drm \ + external/drm/include/drm + +LOCAL_SHARED_LIBRARIES := \ + libdrm \ + liblog \ + libcutils \ + +# for glFlush/glFinish +LOCAL_SHARED_LIBRARIES += \ + libGLESv1_CM + +ifeq ($(strip $(DRM_USES_INTEL)),true) +LOCAL_SRC_FILES += gralloc_drm_intel.c +LOCAL_C_INCLUDES += external/drm/intel +LOCAL_CFLAGS += -DENABLE_INTEL +LOCAL_SHARED_LIBRARIES += libdrm_intel +endif # DRM_USES_INTEL + +ifeq ($(strip $(DRM_USES_RADEON)),true) +LOCAL_SRC_FILES += gralloc_drm_radeon.c +LOCAL_C_INCLUDES += external/drm/radeon +LOCAL_CFLAGS += -DENABLE_RADEON +LOCAL_SHARED_LIBRARIES += libdrm_radeon +endif # DRM_USES_RADEON + +LOCAL_MODULE := gralloc.$(TARGET_PRODUCT) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw + +include $(BUILD_SHARED_LIBRARY) + +endif # BOARD_USES_DRM diff --git a/dri/intel_chipset.h b/dri/intel_chipset.h new file mode 100644 index 0000000..2e9fb2d --- /dev/null +++ b/dri/intel_chipset.h @@ -0,0 +1,172 @@ + /* + * Copyright © 2007 Intel Corporation + * + * 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 (including the next + * paragraph) 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#define PCI_CHIP_I810 0x7121 +#define PCI_CHIP_I810_DC100 0x7123 +#define PCI_CHIP_I810_E 0x7125 +#define PCI_CHIP_I815 0x1132 + +#define PCI_CHIP_I830_M 0x3577 +#define PCI_CHIP_845_G 0x2562 +#define PCI_CHIP_I855_GM 0x3582 +#define PCI_CHIP_I865_G 0x2572 + +#define PCI_CHIP_I915_G 0x2582 +#define PCI_CHIP_E7221_G 0x258A +#define PCI_CHIP_I915_GM 0x2592 +#define PCI_CHIP_I945_G 0x2772 +#define PCI_CHIP_I945_GM 0x27A2 +#define PCI_CHIP_I945_GME 0x27AE + +#define PCI_CHIP_Q35_G 0x29B2 +#define PCI_CHIP_G33_G 0x29C2 +#define PCI_CHIP_Q33_G 0x29D2 + +#define PCI_CHIP_IGD_GM 0xA011 +#define PCI_CHIP_IGD_G 0xA001 + +#define IS_IGDGM(devid) (devid == PCI_CHIP_IGD_GM) +#define IS_IGDG(devid) (devid == PCI_CHIP_IGD_G) +#define IS_IGD(devid) (IS_IGDG(devid) || IS_IGDGM(devid)) + +#define PCI_CHIP_I965_G 0x29A2 +#define PCI_CHIP_I965_Q 0x2992 +#define PCI_CHIP_I965_G_1 0x2982 +#define PCI_CHIP_I946_GZ 0x2972 +#define PCI_CHIP_I965_GM 0x2A02 +#define PCI_CHIP_I965_GME 0x2A12 + +#define PCI_CHIP_GM45_GM 0x2A42 + +#define PCI_CHIP_IGD_E_G 0x2E02 +#define PCI_CHIP_Q45_G 0x2E12 +#define PCI_CHIP_G45_G 0x2E22 +#define PCI_CHIP_G41_G 0x2E32 +#define PCI_CHIP_B43_G 0x2E42 +#define PCI_CHIP_B43_G1 0x2E92 + +#define PCI_CHIP_ILD_G 0x0042 +#define PCI_CHIP_ILM_G 0x0046 + +#define PCI_CHIP_SANDYBRIDGE_GT1 0x0102 /* Desktop */ +#define PCI_CHIP_SANDYBRIDGE_GT2 0x0112 +#define PCI_CHIP_SANDYBRIDGE_GT2_PLUS 0x0122 +#define PCI_CHIP_SANDYBRIDGE_M_GT1 0x0106 /* Mobile */ +#define PCI_CHIP_SANDYBRIDGE_M_GT2 0x0116 +#define PCI_CHIP_SANDYBRIDGE_M_GT2_PLUS 0x0126 +#define PCI_CHIP_SANDYBRIDGE_S 0x010A /* Server */ + +#define PCI_CHIP_IVYBRIDGE_GT1 0x0152 /* Desktop */ +#define PCI_CHIP_IVYBRIDGE_GT2 0x0162 +#define PCI_CHIP_IVYBRIDGE_M_GT1 0x0156 /* Mobile */ +#define PCI_CHIP_IVYBRIDGE_M_GT2 0x0166 +#define PCI_CHIP_IVYBRIDGE_S_GT1 0x015a /* Server */ + +#define IS_MOBILE(devid) (devid == PCI_CHIP_I855_GM || \ + devid == PCI_CHIP_I915_GM || \ + devid == PCI_CHIP_I945_GM || \ + devid == PCI_CHIP_I945_GME || \ + devid == PCI_CHIP_I965_GM || \ + devid == PCI_CHIP_I965_GME || \ + devid == PCI_CHIP_GM45_GM || \ + IS_IGD(devid) || \ + devid == PCI_CHIP_ILM_G) + +#define IS_G45(devid) (devid == PCI_CHIP_IGD_E_G || \ + devid == PCI_CHIP_Q45_G || \ + devid == PCI_CHIP_G45_G || \ + devid == PCI_CHIP_G41_G || \ + devid == PCI_CHIP_B43_G || \ + devid == PCI_CHIP_B43_G1) +#define IS_GM45(devid) (devid == PCI_CHIP_GM45_GM) +#define IS_G4X(devid) (IS_G45(devid) || IS_GM45(devid)) + +#define IS_ILD(devid) (devid == PCI_CHIP_ILD_G) +#define IS_ILM(devid) (devid == PCI_CHIP_ILM_G) +#define IS_GEN5(devid) (IS_ILD(devid) || IS_ILM(devid)) + +#define IS_915(devid) (devid == PCI_CHIP_I915_G || \ + devid == PCI_CHIP_E7221_G || \ + devid == PCI_CHIP_I915_GM) + +#define IS_945(devid) (devid == PCI_CHIP_I945_G || \ + devid == PCI_CHIP_I945_GM || \ + devid == PCI_CHIP_I945_GME || \ + devid == PCI_CHIP_G33_G || \ + devid == PCI_CHIP_Q33_G || \ + devid == PCI_CHIP_Q35_G || IS_IGD(devid)) + +#define IS_GEN4(devid) (devid == PCI_CHIP_I965_G || \ + devid == PCI_CHIP_I965_Q || \ + devid == PCI_CHIP_I965_G_1 || \ + devid == PCI_CHIP_I965_GM || \ + devid == PCI_CHIP_I965_GME || \ + devid == PCI_CHIP_I946_GZ || \ + IS_G4X(devid)) + +/* Compat macro for intel_decode.c */ +#define IS_IRONLAKE(devid) IS_GEN5(devid) + +#define IS_SNB_GT1(devid) (devid == PCI_CHIP_SANDYBRIDGE_GT1 || \ + devid == PCI_CHIP_SANDYBRIDGE_M_GT1 || \ + devid == PCI_CHIP_SANDYBRIDGE_S) + +#define IS_SNB_GT2(devid) (devid == PCI_CHIP_SANDYBRIDGE_GT2 || \ + devid == PCI_CHIP_SANDYBRIDGE_GT2_PLUS || \ + devid == PCI_CHIP_SANDYBRIDGE_M_GT2 || \ + devid == PCI_CHIP_SANDYBRIDGE_M_GT2_PLUS) + +#define IS_GEN6(devid) (IS_SNB_GT1(devid) || IS_SNB_GT2(devid)) + +#define IS_IVB_GT1(devid) (devid == PCI_CHIP_IVYBRIDGE_GT1 || \ + devid == PCI_CHIP_IVYBRIDGE_M_GT1 || \ + devid == PCI_CHIP_IVYBRIDGE_S_GT1) + +#define IS_IVB_GT2(devid) (devid == PCI_CHIP_IVYBRIDGE_GT2 || \ + devid == PCI_CHIP_IVYBRIDGE_M_GT2) + +#define IS_IVYBRIDGE(devid) (IS_IVB_GT1(devid) || IS_IVB_GT2(devid)) + +#define IS_GEN7(devid) IS_IVYBRIDGE(devid) + +#define IS_965(devid) (IS_GEN4(devid) || \ + IS_G4X(devid) || \ + IS_GEN5(devid) || \ + IS_GEN6(devid) || \ + IS_GEN7(devid)) + +#define IS_9XX(devid) (IS_915(devid) || \ + IS_945(devid) || \ + IS_965(devid)) + +#define IS_GEN3(devid) (IS_915(devid) || \ + IS_945(devid)) + +#define IS_GEN2(devid) (devid == PCI_CHIP_I830_M || \ + devid == PCI_CHIP_845_G || \ + devid == PCI_CHIP_I855_GM || \ + devid == PCI_CHIP_I865_G) diff --git a/gralloc.c b/gralloc.c new file mode 100644 index 0000000..850a39e --- /dev/null +++ b/gralloc.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2010-2011 LunarG 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. + */ + +#define LOG_TAG "GRALLOC-MOD" + +#include <cutils/log.h> +#include <stdlib.h> +#include <stdarg.h> +#include <pthread.h> +#include <errno.h> + +#include "gralloc_drm.h" +#include "gralloc_drm_handle.h" + +struct drm_module_t { + gralloc_module_t base; + + pthread_mutex_t mutex; + struct gralloc_drm_t *drm; +}; + +/* + * Initialize the DRM device object, optionally with KMS. + */ +static int drm_init(struct drm_module_t *dmod, int kms) +{ + int err = 0; + + pthread_mutex_lock(&dmod->mutex); + if (!dmod->drm) { + dmod->drm = gralloc_drm_create(); + if (!dmod->drm) + err = -EINVAL; + } + if (!err && kms) + err = gralloc_drm_init_kms(dmod->drm); + pthread_mutex_unlock(&dmod->mutex); + + return err; +} + +static int drm_mod_perform(const struct gralloc_module_t *mod, int op, ...) +{ + struct drm_module_t *dmod = (struct drm_module_t *) mod; + va_list args; + int err; + + err = drm_init(dmod, 0); + if (err) + return err; + + va_start(args, op); + switch (op) { + case GRALLOC_MODULE_PERFORM_GET_DRM_FD: + { + int *fd = va_arg(args, int *); + *fd = gralloc_drm_get_fd(dmod->drm); + err = 0; + } + break; + /* should we remove this and next ops, and make it transparent? */ + case GRALLOC_MODULE_PERFORM_GET_DRM_MAGIC: + { + int32_t *magic = va_arg(args, int32_t *); + err = gralloc_drm_get_magic(dmod->drm, magic); + } + break; + case GRALLOC_MODULE_PERFORM_AUTH_DRM_MAGIC: + { + int32_t magic = va_arg(args, int32_t); + err = gralloc_drm_auth_magic(dmod->drm, magic); + } + break; + case GRALLOC_MODULE_PERFORM_ENTER_VT: + { + err = gralloc_drm_set_master(dmod->drm); + } + break; + case GRALLOC_MODULE_PERFORM_LEAVE_VT: + { + gralloc_drm_drop_master(dmod->drm); + err = 0; + } + break; + default: + err = -EINVAL; + break; + } + va_end(args); + + return err; +} + +static int drm_mod_register_buffer(const gralloc_module_t *mod, + buffer_handle_t handle) +{ + return (gralloc_drm_handle(handle)) ? 0 : -EINVAL; +} + +static int drm_mod_unregister_buffer(const gralloc_module_t *mod, + buffer_handle_t handle) +{ + return (gralloc_drm_handle(handle)) ? 0 : -EINVAL; +} + +static int drm_mod_lock(const gralloc_module_t *mod, buffer_handle_t handle, + int usage, int x, int y, int w, int h, void **ptr) +{ + struct drm_module_t *dmod = (struct drm_module_t *) mod; + struct gralloc_drm_bo_t *bo; + int err; + + err = drm_init(dmod, 0); + if (err) + return err; + + bo = gralloc_drm_bo_validate(dmod->drm, handle); + if (!bo) + return -EINVAL; + + return gralloc_drm_bo_map(bo, x, y, w, h, 1, ptr); +} + +static int drm_mod_unlock(const gralloc_module_t *mod, buffer_handle_t handle) +{ + struct drm_module_t *dmod = (struct drm_module_t *) mod; + struct gralloc_drm_bo_t *bo; + + bo = gralloc_drm_bo_validate(dmod->drm, handle); + if (!bo) + return -EINVAL; + + gralloc_drm_bo_unmap(bo); + + return 0; +} + +static int drm_mod_close_gpu0(struct hw_device_t *dev) +{ + struct alloc_device_t *alloc = (struct alloc_device_t *) dev; + + free(alloc); + + return 0; +} + +static int drm_mod_free_gpu0(alloc_device_t *dev, buffer_handle_t handle) +{ + struct drm_module_t *dmod = (struct drm_module_t *) dev->common.module; + struct gralloc_drm_bo_t *bo; + + bo = gralloc_drm_bo_validate(dmod->drm, handle); + if (!bo) + return -EINVAL; + + if (gralloc_drm_bo_need_fb(bo)) + gralloc_drm_bo_rm_fb(bo); + gralloc_drm_bo_destroy(bo); + + return 0; +} + +static int drm_mod_alloc_gpu0(alloc_device_t *dev, + int w, int h, int format, int usage, + buffer_handle_t *handle, int *stride) +{ + struct drm_module_t *dmod = (struct drm_module_t *) dev->common.module; + struct gralloc_drm_bo_t *bo; + int size, bpp, err; + + bpp = gralloc_drm_get_bpp(format); + if (!bpp) + return -EINVAL; + + bo = gralloc_drm_bo_create(dmod->drm, w, h, format, usage); + if (!bo) + return -ENOMEM; + + if (gralloc_drm_bo_need_fb(bo)) { + err = gralloc_drm_bo_add_fb(bo); + if (err) { + LOGE("failed to add fb"); + gralloc_drm_bo_destroy(bo); + return err; + } + } + + *handle = gralloc_drm_bo_get_handle(bo, stride); + /* in pixels */ + *stride /= bpp; + + return 0; +} + +static int drm_mod_open_gpu0(struct drm_module_t *dmod, hw_device_t **dev) +{ + struct alloc_device_t *alloc; + int err; + + err = drm_init(dmod, 0); + if (err) + return err; + + alloc = calloc(1, sizeof(*alloc)); + if (!alloc) + return -EINVAL; + + alloc->common.tag = HARDWARE_DEVICE_TAG; + alloc->common.version = 0; + alloc->common.module = &dmod->base.common; + alloc->common.close = drm_mod_close_gpu0; + + alloc->alloc = drm_mod_alloc_gpu0; + alloc->free = drm_mod_free_gpu0; + + *dev = &alloc->common; + + return 0; +} + +static int drm_mod_close_fb0(struct hw_device_t *dev) +{ + struct framebuffer_device_t *fb = (struct framebuffer_device_t *) dev; + + free(fb); + + return 0; +} + +static int drm_mod_set_swap_interval_fb0(struct framebuffer_device_t *fb, + int interval) +{ + if (interval < fb->minSwapInterval || interval > fb->maxSwapInterval) + return -EINVAL; + return 0; +} + +static int drm_mod_post_fb0(struct framebuffer_device_t *fb, + buffer_handle_t handle) +{ + struct drm_module_t *dmod = (struct drm_module_t *) fb->common.module; + struct gralloc_drm_bo_t *bo; + + bo = gralloc_drm_bo_validate(dmod->drm, handle); + if (!bo) + return -EINVAL; + + return gralloc_drm_bo_post(bo); +} + +#include <GLES/gl.h> +static int drm_mod_composition_complete_fb0(struct framebuffer_device_t *fb) +{ + struct drm_module_t *dmod = (struct drm_module_t *) fb->common.module; + + if (gralloc_drm_is_kms_pipelined(dmod->drm)) + glFlush(); + else + glFinish(); + + return 0; +} + +static int drm_mod_open_fb0(struct drm_module_t *dmod, struct hw_device_t **dev) +{ + struct framebuffer_device_t *fb; + int err; + + err = drm_init(dmod, 1); + if (err) + return err; + + fb = calloc(1, sizeof(*fb)); + if (!fb) + return -ENOMEM; + + fb->common.tag = HARDWARE_DEVICE_TAG; + fb->common.version = 0; + fb->common.module = &dmod->base.common; + fb->common.close = drm_mod_close_fb0; + + fb->setSwapInterval = drm_mod_set_swap_interval_fb0; + fb->post = drm_mod_post_fb0; + fb->compositionComplete = drm_mod_composition_complete_fb0; + + gralloc_drm_get_kms_info(dmod->drm, fb); + + *dev = &fb->common; + + LOGI("mode.hdisplay %d\n" + "mode.vdisplay %d\n" + "mode.vrefresh %f\n" + "format 0x%x\n" + "xdpi %f\n" + "ydpi %f\n", + fb->width, + fb->height, + fb->fps, + fb->format, + fb->xdpi, fb->ydpi); + + return 0; +} + +static int drm_mod_open(const struct hw_module_t *mod, + const char *name, struct hw_device_t **dev) +{ + struct drm_module_t *dmod = (struct drm_module_t *) mod; + int err; + + if (strcmp(name, GRALLOC_HARDWARE_GPU0) == 0) + err = drm_mod_open_gpu0(dmod, dev); + else if (strcmp(name, GRALLOC_HARDWARE_FB0) == 0) + err = drm_mod_open_fb0(dmod, dev); + else + err = -EINVAL; + + return err; +} + +static struct hw_module_methods_t drm_mod_methods = { + .open = drm_mod_open +}; + +struct drm_module_t HAL_MODULE_INFO_SYM = { + .base = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = GRALLOC_HARDWARE_MODULE_ID, + .name = "DRM Memory Allocator", + .author = "Chia-I Wu", + .methods = &drm_mod_methods + }, + .registerBuffer = drm_mod_register_buffer, + .unregisterBuffer = drm_mod_unregister_buffer, + .lock = drm_mod_lock, + .unlock = drm_mod_unlock, + .perform = drm_mod_perform + }, + .mutex = PTHREAD_MUTEX_INITIALIZER, + .drm = NULL +}; diff --git a/gralloc_drm.c b/gralloc_drm.c new file mode 100644 index 0000000..63e504b --- /dev/null +++ b/gralloc_drm.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2010-2011 LunarG 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. + */ + +#define LOG_TAG "GRALLOC-DRM" + +#include <cutils/log.h> +#include <cutils/atomic.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "gralloc_drm.h" +#include "gralloc_drm_priv.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define GRALLOC_DRM_DEVICE "/dev/dri/card0" + +static int32_t gralloc_drm_pid = 0; + +/* + * Return the pid of the process. + */ +static int gralloc_drm_get_pid(void) +{ + if (unlikely(!gralloc_drm_pid)) + android_atomic_write((int32_t) getpid(), &gralloc_drm_pid); + + return gralloc_drm_pid; +} + +/* + * Create the driver for a DRM fd. + */ +static struct gralloc_drm_drv_t * +init_drv_from_fd(int fd) +{ + struct gralloc_drm_drv_t *drv = NULL; + drmVersionPtr version; + + /* get the kernel module name */ + version = drmGetVersion(fd); + if (!version) { + LOGE("invalid DRM fd"); + return NULL; + } + + if (version->name) { +#ifdef ENABLE_INTEL + if (!drv && !strcmp(version->name, "i915")) + drv = gralloc_drm_drv_create_for_intel(fd); +#endif +#ifdef ENABLE_RADEON + if (!drv && !strcmp(version->name, "radeon")) + drv = gralloc_drm_drv_create_for_radeon(fd); +#endif +#ifdef ENABLE_VMWGFX + if (!drv && !strcmp(version->name, "vmwgfx")) + drv = gralloc_drm_drv_create_for_vmwgfx(fd); +#endif + } + + if (!drv) { + LOGE("unsupported driver: %s", (version->name) ? + version->name : "NULL"); + } + + drmFreeVersion(version); + + return drv; +} + +/* + * Create a DRM device object. + */ +struct gralloc_drm_t *gralloc_drm_create(void) +{ + struct gralloc_drm_t *drm; + int err; + + drm = calloc(1, sizeof(*drm)); + if (!drm) + return NULL; + + drm->fd = open(GRALLOC_DRM_DEVICE, O_RDWR); + if (drm->fd < 0) { + LOGE("failed to open %s", GRALLOC_DRM_DEVICE); + return NULL; + } + + drm->drv = init_drv_from_fd(drm->fd); + if (!drm->drv) { + close(drm->fd); + free(drm); + return NULL; + } + + return drm; +} + +/* + * Destroy a DRM device object. + */ +void gralloc_drm_destroy(struct gralloc_drm_t *drm) +{ + if (drm->drv) + drm->drv->destroy(drm->drv); + close(drm->fd); + free(drm); +} + +/* + * Get the file descriptor of a DRM device object. + */ +int gralloc_drm_get_fd(struct gralloc_drm_t *drm) +{ + return drm->fd; +} + +/* + * Get the magic for authentication. + */ +int gralloc_drm_get_magic(struct gralloc_drm_t *drm, int32_t *magic) +{ + return drmGetMagic(drm->fd, (drm_magic_t *) magic); +} + +/* + * Authenticate a magic. + */ +int gralloc_drm_auth_magic(struct gralloc_drm_t *drm, int32_t magic) +{ + return drmAuthMagic(drm->fd, (drm_magic_t) magic); +} + +/* + * Set as the master of a DRM device. + */ +int gralloc_drm_set_master(struct gralloc_drm_t *drm) +{ + LOGD("set master"); + drmSetMaster(drm->fd); + drm->first_post = 1; + + return 0; +} + +/* + * Drop from the master of a DRM device. + */ +void gralloc_drm_drop_master(struct gralloc_drm_t *drm) +{ + drmDropMaster(drm->fd); +} + +/* + * Create a buffer handle. + */ +static struct gralloc_drm_handle_t *create_bo_handle(int width, + int height, int format, int usage) +{ + struct gralloc_drm_handle_t *handle; + + handle = calloc(1, sizeof(*handle)); + if (!handle) + return NULL; + + handle->base.version = sizeof(handle->base); + handle->base.numInts = GRALLOC_DRM_HANDLE_NUM_INTS; + handle->base.numFds = GRALLOC_DRM_HANDLE_NUM_FDS; + + handle->magic = GRALLOC_DRM_HANDLE_MAGIC; + handle->width = width; + handle->height = height; + handle->format = format; + handle->usage = usage; + + return handle; +} + +/* + * Create a bo. + */ +struct gralloc_drm_bo_t *gralloc_drm_bo_create(struct gralloc_drm_t *drm, + int width, int height, int format, int usage) +{ + struct gralloc_drm_bo_t *bo; + struct gralloc_drm_handle_t *handle; + + handle = create_bo_handle(width, height, format, usage); + if (!handle) + return NULL; + + bo = drm->drv->alloc(drm->drv, handle); + if (!bo) { + free(handle); + return NULL; + } + + bo->drm = drm; + bo->imported = 0; + bo->handle = handle; + + handle->data_owner = gralloc_drm_get_pid(); + handle->data = (int) bo; + + return bo; +} + +/* + * Validate a buffer handle and return the associated bo. + */ +struct gralloc_drm_bo_t *gralloc_drm_bo_validate(struct gralloc_drm_t *drm, + buffer_handle_t _handle) +{ + struct gralloc_drm_handle_t *handle = gralloc_drm_handle(_handle); + + /* the buffer handle is passed to a new process */ + if (handle && unlikely(handle->data_owner != gralloc_drm_pid)) { + struct gralloc_drm_bo_t *bo; + + /* create the struct gralloc_drm_bo_t locally */ + bo = drm->drv->alloc(drm->drv, handle); + if (bo) { + bo->drm = drm; + bo->imported = 1; + bo->handle = handle; + } + + handle->data_owner = gralloc_drm_get_pid(); + handle->data = (int) bo; + } + + return (struct gralloc_drm_bo_t *) handle->data; +} + +/* + * Destroy a bo. + */ +void gralloc_drm_bo_destroy(struct gralloc_drm_bo_t *bo) +{ + struct gralloc_drm_handle_t *handle = bo->handle; + int imported = bo->imported; + + bo->drm->drv->free(bo->drm->drv, bo); + if (imported) { + handle->data_owner = 0; + handle->data = 0; + } + else { + free(handle); + } +} + +/* + * Map a bo for CPU access. + */ +int gralloc_drm_bo_map(struct gralloc_drm_bo_t *bo, + int x, int y, int w, int h, + int enable_write, void **addr) +{ + return bo->drm->drv->map(bo->drm->drv, bo, + x, y, w, h, enable_write, addr); +} + +/* + * Unmap a bo. + */ +void gralloc_drm_bo_unmap(struct gralloc_drm_bo_t *bo) +{ + bo->drm->drv->unmap(bo->drm->drv, bo); +} + +/* + * Get the buffer handle and stride of a bo. + */ +buffer_handle_t gralloc_drm_bo_get_handle(struct gralloc_drm_bo_t *bo, int *stride) +{ + if (stride) + *stride = bo->handle->stride; + return &bo->handle->base; +} diff --git a/gralloc_drm.h b/gralloc_drm.h new file mode 100644 index 0000000..67f3598 --- /dev/null +++ b/gralloc_drm.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2010-2011 LunarG 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. + */ + +#ifndef _GRALLOC_DRM_H_ +#define _GRALLOC_DRM_H_ + +#include <hardware/gralloc.h> + +struct gralloc_drm_t; +struct gralloc_drm_bo_t; + +struct gralloc_drm_t *gralloc_drm_create(void); +void gralloc_drm_destroy(struct gralloc_drm_t *drm); + +int gralloc_drm_get_fd(struct gralloc_drm_t *drm); +int gralloc_drm_get_magic(struct gralloc_drm_t *drm, int32_t *magic); +int gralloc_drm_auth_magic(struct gralloc_drm_t *drm, int32_t magic); +int gralloc_drm_set_master(struct gralloc_drm_t *drm); +void gralloc_drm_drop_master(struct gralloc_drm_t *drm); + +int gralloc_drm_init_kms(struct gralloc_drm_t *drm); +void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm, struct framebuffer_device_t *fb); +int gralloc_drm_is_kms_pipelined(struct gralloc_drm_t *drm); + +static inline int gralloc_drm_get_bpp(int format) +{ + int bpp; + + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + bpp = 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + bpp = 3; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + default: + bpp = 0; + break; + } + + return bpp; +} + +struct gralloc_drm_bo_t *gralloc_drm_bo_create(struct gralloc_drm_t *drm, int width, int height, int format, int usage); +struct gralloc_drm_bo_t *gralloc_drm_bo_validate(struct gralloc_drm_t *drm, buffer_handle_t handle); +void gralloc_drm_bo_destroy(struct gralloc_drm_bo_t *bo); + +int gralloc_drm_bo_map(struct gralloc_drm_bo_t *bo, int x, int y, int w, int h, int enable_write, void **addr); +void gralloc_drm_bo_unmap(struct gralloc_drm_bo_t *bo); +buffer_handle_t gralloc_drm_bo_get_handle(struct gralloc_drm_bo_t *bo, int *stride); + +int gralloc_drm_bo_need_fb(const struct gralloc_drm_bo_t *bo); +int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo); +void gralloc_drm_bo_rm_fb(struct gralloc_drm_bo_t *bo); +int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo); + +#endif /* _GRALLOC_DRM_H_ */ diff --git a/gralloc_drm_handle.h b/gralloc_drm_handle.h new file mode 100644 index 0000000..343ace0 --- /dev/null +++ b/gralloc_drm_handle.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2010-2011 LunarG 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. + */ + +#ifndef _GRALLOC_DRM_HANDLE_H_ +#define _GRALLOC_DRM_HANDLE_H_ + +#include <cutils/native_handle.h> + +struct gralloc_drm_handle_t { + native_handle_t base; + +#define GRALLOC_DRM_HANDLE_MAGIC 0x12345678 +#define GRALLOC_DRM_HANDLE_NUM_INTS 9 +#define GRALLOC_DRM_HANDLE_NUM_FDS 0 + int magic; + + int width; + int height; + int format; + int usage; + + int name; /* the name of the bo */ + int stride; /* the stride in bytes */ + + int data_owner; /* owner of data (for validation) */ + int data; /* pointer to struct gralloc_drm_bo_t */ +}; + +static inline struct gralloc_drm_handle_t *gralloc_drm_handle(buffer_handle_t _handle) +{ + struct gralloc_drm_handle_t *handle = + (struct gralloc_drm_handle_t *) _handle; + + if (handle->base.version != sizeof(handle->base) || + handle->base.numInts != GRALLOC_DRM_HANDLE_NUM_INTS || + handle->base.numFds != GRALLOC_DRM_HANDLE_NUM_FDS || + handle->magic != GRALLOC_DRM_HANDLE_MAGIC) + handle = NULL; + + return handle; +} + +#endif /* _GRALLOC_DRM_HANDLE_H_ */ diff --git a/gralloc_drm_intel.c b/gralloc_drm_intel.c new file mode 100644 index 0000000..e5ddcfc --- /dev/null +++ b/gralloc_drm_intel.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * 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 <jbarnes@virtuousgeek.org> + * + * 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 <cutils/log.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <drm.h> +#include <intel_bufmgr.h> +#include <i915_drm.h> + +#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) | 6) +#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) + +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; +}; + +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_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) { + LOGE("failed to subdata batch"); + goto fail; + } + ret = drm_intel_bo_exec(info->batch_ibo, size, NULL, 0, 0); + if (ret) { + LOGE("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_copy(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *dst, + struct gralloc_drm_bo_t *src, + short x1, short y1, short x2, short 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; + + if (dst->handle->width != src->handle->width || + dst->handle->height != src->handle->height || + dst->handle->stride != src->handle->stride || + dst->handle->format != src->handle->format) { + LOGE("copy between incompatible buffers"); + return; + } + + if (x1 < 0) + x1 = 0; + if (y1 < 0) + y1 = 0; + if (x2 > dst->handle->width) + x2 = dst->handle->width; + if (y2 > dst->handle->height) + y2 = dst->handle->height; + + if (x2 <= x1 || y2 <= y1) + return; + + 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; + + 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: + LOGE("copy with unsupported format"); + 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; + } + } + + if (batch_reserve(info, 8)) + return; + + batch_dword(info, cmd); + batch_dword(info, br13 | dst_pitch); + batch_dword(info, (y1 << 16) | x1); + batch_dword(info, (y2 << 16) | x2); + batch_reloc(info, dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); + batch_dword(info, (y1 << 16) | x1); + batch_dword(info, src_pitch); + 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) { + LOGE("unrecognized format 0x%x", handle->format); + return NULL; + } + + 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 = (handle->width + 63) & ~63; + aligned_height = handle->height; + 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 = (handle->width + 3) & ~3; + aligned_height = (handle->height + 1) & ~1; + } + else { + name = "gralloc-buffer"; + aligned_width = handle->width; + aligned_height = handle->height; + } + + 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) { + LOGE("failed to create ibo from name %u", + handle->name); + free(ib); + return NULL; + } + + if (drm_intel_bo_get_tiling(ib->ibo, &ib->tiling, &dummy)) { + LOGE("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) { + LOGE("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)) { + LOGE("failed to flink ibo"); + drm_intel_bo_unreference(ib->ibo); + free(ib); + return NULL; + } + } + + if (handle->usage & GRALLOC_USAGE_HW_FB) + 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 "dri/intel_chipset.h" /* for IS_965() */ +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; + + drm->mode_dirty_fb = 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; + + if (IS_965(id)) { + 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->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) { + LOGE("failed to allocate driver info"); + return NULL; + } + + info->fd = fd; + info->bufmgr = drm_intel_bufmgr_gem_init(info->fd, 16 * 1024); + if (!info->bufmgr) { + LOGE("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.copy = intel_copy; + + return &info->base; +} diff --git a/gralloc_drm_kms.c b/gralloc_drm_kms.c new file mode 100644 index 0000000..c64d4ad --- /dev/null +++ b/gralloc_drm_kms.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2010-2011 LunarG 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. + */ + +#define LOG_TAG "GRALLOC-KMS" + +#include <cutils/log.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include "gralloc_drm.h" +#include "gralloc_drm_priv.h" + +/* + * Return true if a bo needs fb. + */ +int gralloc_drm_bo_need_fb(const struct gralloc_drm_bo_t *bo) +{ + return ((bo->handle->usage & GRALLOC_USAGE_HW_FB) && + bo->drm->swap_mode != DRM_SWAP_COPY); +} + +/* + * Add a fb object for a bo. + */ +int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo) +{ + uint8_t bpp; + + if (bo->fb_id) + return 0; + + bpp = gralloc_drm_get_bpp(bo->handle->format) * 8; + + return drmModeAddFB(bo->drm->fd, + bo->handle->width, bo->handle->height, bpp, bpp, + bo->handle->stride, bo->fb_handle, + (uint32_t *) &bo->fb_id); +} + +/* + * Remove a fb object for a bo. + */ +void gralloc_drm_bo_rm_fb(struct gralloc_drm_bo_t *bo) +{ + if (bo->fb_id) { + drmModeRmFB(bo->drm->fd, bo->fb_id); + bo->fb_id = 0; + } +} + +/* + * Program CRTC. + */ +static int drm_kms_set_crtc(struct gralloc_drm_t *drm, int fb_id) +{ + int ret; + + ret = drmModeSetCrtc(drm->fd, drm->crtc_id, fb_id, + 0, 0, &drm->connector_id, 1, &drm->mode); + if (ret) { + LOGE("failed to set crtc"); + return ret; + } + +#ifdef DRM_MODE_FEATURE_DIRTYFB + if (drm->mode_dirty_fb) + ret = drmModeDirtyFB(drm->fd, fb_id, &drm->clip, 1); +#endif + + return ret; +} + +/* + * Callback for a page flip event. + */ +static void page_flip_handler(int fd, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + struct gralloc_drm_t *drm = (struct gralloc_drm_t *) user_data; + + /* ack the last scheduled flip */ + drm->current_front = drm->next_front; + drm->next_front = NULL; +} + +/* + * Schedule a page flip. + */ +static int drm_kms_page_flip(struct gralloc_drm_t *drm, + struct gralloc_drm_bo_t *bo) +{ + int ret; + + /* there is another flip pending */ + while (drm->next_front) { + drm->waiting_flip = 1; + drmHandleEvent(drm->fd, &drm->evctx); + drm->waiting_flip = 0; + if (drm->next_front) { + /* record an error and break */ + LOGE("drmHandleEvent returned without flipping"); + drm->current_front = drm->next_front; + drm->next_front = NULL; + } + } + + if (!bo) + return 0; + + ret = drmModePageFlip(drm->fd, drm->crtc_id, bo->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, (void *) drm); + if (ret) + LOGE("failed to perform page flip"); + else + drm->next_front = bo; + + return ret; +} + +/* + * Wait for the next post. + */ +static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip) +{ + unsigned int current, target; + drmVBlank vbl; + int ret; + + flip = !!flip; + + memset(&vbl, 0, sizeof(vbl)); + vbl.request.type = DRM_VBLANK_RELATIVE; + if (drm->vblank_secondary) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = 0; + + /* get the current vblank */ + ret = drmWaitVBlank(drm->fd, &vbl); + if (ret) { + LOGW("failed to get vblank"); + return; + } + + current = vbl.reply.sequence; + if (drm->first_post) + target = current; + else + target = drm->last_swap + drm->swap_interval - flip; + + /* wait for vblank */ + if (current < target || !flip) { + memset(&vbl, 0, sizeof(vbl)); + vbl.request.type = DRM_VBLANK_ABSOLUTE; + if (drm->vblank_secondary) + vbl.request.type |= DRM_VBLANK_SECONDARY; + if (!flip) { + vbl.request.type |= DRM_VBLANK_NEXTONMISS; + if (target < current) + target = current; + } + + vbl.request.sequence = target; + + ret = drmWaitVBlank(drm->fd, &vbl); + if (ret) { + LOGW("failed to wait vblank"); + return; + } + } + + drm->last_swap = vbl.reply.sequence + flip; +} + +/* + * Post a bo. This is not thread-safe. + */ +int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo) +{ + struct gralloc_drm_t *drm = bo->drm; + int ret; + + if (!bo->fb_id && drm->swap_mode != DRM_SWAP_COPY) { + LOGE("unable to post bo %p without fb", bo); + return -EINVAL; + } + + /* TODO spawn a thread to avoid waiting and race */ + + if (drm->first_post) { + if (drm->swap_mode == DRM_SWAP_COPY) { + struct gralloc_drm_bo_t *dst; + + dst = (drm->next_front) ? + drm->next_front : + drm->current_front; + drm->drv->copy(drm->drv, dst, bo, 0, 0, + bo->handle->width, + bo->handle->height); + bo = dst; + } + + drm_kms_wait_for_post(drm, 0); + ret = drm_kms_set_crtc(drm, bo->fb_id); + if (!ret) { + drm->first_post = 0; + drm->current_front = bo; + if (drm->next_front == bo) + drm->next_front = NULL; + } + + return ret; + } + + switch (drm->swap_mode) { + case DRM_SWAP_FLIP: + if (drm->swap_interval > 1) + drm_kms_wait_for_post(drm, 1); + ret = drm_kms_page_flip(drm, bo); + if (drm->next_front) { + /* + * wait if the driver says so or the current front + * will be written by CPU + */ + if (drm->mode_sync_flip || + (drm->current_front->handle->usage & + GRALLOC_USAGE_SW_WRITE_MASK)) + drm_kms_page_flip(drm, NULL); + } + break; + case DRM_SWAP_COPY: + drm_kms_wait_for_post(drm, 0); + drm->drv->copy(drm->drv, drm->current_front, + bo, 0, 0, + bo->handle->width, + bo->handle->height); + ret = 0; + break; + case DRM_SWAP_SETCRTC: + drm_kms_wait_for_post(drm, 0); + ret = drm_kms_set_crtc(drm, bo->fb_id); + drm->current_front = bo; + break; + default: + /* no-op */ + ret = 0; + break; + } + + return ret; +} + +static struct gralloc_drm_t *drm_singleton; + +static void on_signal(int sig) +{ + struct sigaction act; + + /* wait the pending flip */ + if (drm_singleton && drm_singleton->next_front) { + /* there is race, but this function is hacky enough to ignore that */ + if (drm_singleton->waiting_flip) + usleep(100 * 1000); /* 100ms */ + else + drm_kms_page_flip(drm_singleton, NULL); + } + + exit(-1); +} + +static void drm_kms_init_features(struct gralloc_drm_t *drm) +{ + const char *swap_mode; + + /* call to the driver here, after KMS has been initialized */ + drm->drv->init_kms_features(drm->drv, drm); + + if (drm->swap_mode == DRM_SWAP_FLIP) { + struct sigaction act; + + memset(&drm->evctx, 0, sizeof(drm->evctx)); + drm->evctx.version = DRM_EVENT_CONTEXT_VERSION; + drm->evctx.page_flip_handler = page_flip_handler; + + /* + * XXX GPU tends to freeze if the program is terminiated with a + * flip pending. What is the right way to handle the + * situation? + */ + sigemptyset(&act.sa_mask); + act.sa_handler = on_signal; + act.sa_flags = 0; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + drm_singleton = drm; + } + else if (drm->swap_mode == DRM_SWAP_COPY) { + struct gralloc_drm_bo_t *front; + int stride; + + /* create the real front buffer */ + front = gralloc_drm_bo_create(drm, + drm->mode.hdisplay, + drm->mode.vdisplay, + drm->format, + GRALLOC_USAGE_HW_FB); + if (front && gralloc_drm_bo_add_fb(front)) { + gralloc_drm_bo_destroy(front); + front = NULL; + } + + /* abuse next_front */ + if (front) + drm->next_front = front; + else + drm->swap_mode = DRM_SWAP_SETCRTC; + } + + switch (drm->swap_mode) { + case DRM_SWAP_FLIP: + swap_mode = "flip"; + break; + case DRM_SWAP_COPY: + swap_mode = "copy"; + break; + case DRM_SWAP_SETCRTC: + swap_mode = "set-crtc"; + break; + default: + swap_mode = "no-op"; + break; + } + + LOGD("will use %s for fb posting", swap_mode); +} + +/* + * Initialize KMS with a connector. + */ +static int drm_kms_init_with_connector(struct gralloc_drm_t *drm, + drmModeConnectorPtr connector) +{ + drmModeEncoderPtr encoder; + drmModeModeInfoPtr mode; + int i; + + if (!connector->count_modes) + return -EINVAL; + + encoder = drmModeGetEncoder(drm->fd, connector->encoders[0]); + if (!encoder) + return -EINVAL; + + for (i = 0; i < drm->resources->count_crtcs; i++) { + if (encoder->possible_crtcs & (1 << i)) + break; + } + drmModeFreeEncoder(encoder); + if (i == drm->resources->count_crtcs) + return -EINVAL; + + drm->crtc_id = drm->resources->crtcs[i]; + drm->connector_id = connector->connector_id; + + /* find the first preferred mode */ + mode = NULL; + for (i = 0; i < connector->count_modes; i++) { + drmModeModeInfoPtr m = &connector->modes[i]; + if (m->type & DRM_MODE_TYPE_PREFERRED) { + mode = m; + break; + } + } + /* no preference; use the first */ + if (!mode) + mode = &connector->modes[0]; + + drm->mode = *mode; + + if (connector->mmWidth && connector->mmHeight) { + drm->xdpi = (drm->mode.hdisplay * 25.4 / connector->mmWidth); + drm->ydpi = (drm->mode.vdisplay * 25.4 / connector->mmHeight); + } + else { + drm->xdpi = 75; + drm->ydpi = 75; + } + + /* select between 32/16 bits */ +#if 1 + drm->format = HAL_PIXEL_FORMAT_BGRA_8888; +#else + drm->format = HAL_PIXEL_FORMAT_RGB_565; +#endif + +#ifdef DRM_MODE_FEATURE_DIRTYFB + drm->clip.x1 = 0; + drm->clip.y1 = 0; + drm->clip.x2 = drm->mode.hdisplay; + drm->clip.y2 = drm->mode.vdisplay; +#endif + + return 0; +} + +/* + * Initialize KMS. + */ +int gralloc_drm_init_kms(struct gralloc_drm_t *drm) +{ + int i, ret; + + if (drm->resources) + return 0; + + drm->resources = drmModeGetResources(drm->fd); + if (!drm->resources) { + LOGE("failed to get modeset resources"); + return -EINVAL; + } + + /* find the crtc/connector/mode to use */ + for (i = 0; i < drm->resources->count_connectors; i++) { + drmModeConnectorPtr connector; + + connector = drmModeGetConnector(drm->fd, + drm->resources->connectors[i]); + if (connector) { + if (connector->connection == DRM_MODE_CONNECTED) { + if (!drm_kms_init_with_connector(drm, + connector)) + break; + } + + drmModeFreeConnector(connector); + } + } + if (i == drm->resources->count_connectors) { + LOGE("failed to find a valid crtc/connector/mode combination"); + drmModeFreeResources(drm->resources); + drm->resources = NULL; + + return -EINVAL; + } + + drm_kms_init_features(drm); + drm->first_post = 1; + + return 0; +} + +/* + * Initialize a framebuffer device with KMS info. + */ +void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm, + struct framebuffer_device_t *fb) +{ + *((uint32_t *) &fb->flags) = 0x0; + *((uint32_t *) &fb->width) = drm->mode.hdisplay; + *((uint32_t *) &fb->height) = drm->mode.vdisplay; + *((int *) &fb->stride) = drm->mode.hdisplay; + *((float *) &fb->fps) = drm->mode.vrefresh; + + *((int *) &fb->format) = drm->format; + *((float *) &fb->xdpi) = drm->xdpi; + *((float *) &fb->ydpi) = drm->ydpi; + *((int *) &fb->minSwapInterval) = drm->swap_interval; + *((int *) &fb->maxSwapInterval) = drm->swap_interval; +} + +/* + * Return true if fb posting is pipelined. + */ +int gralloc_drm_is_kms_pipelined(struct gralloc_drm_t *drm) +{ + return (drm->swap_mode != DRM_SWAP_SETCRTC); +} diff --git a/gralloc_drm_priv.h b/gralloc_drm_priv.h new file mode 100644 index 0000000..6891faa --- /dev/null +++ b/gralloc_drm_priv.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2010-2011 LunarG 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. + */ + +#ifndef _GRALLOC_DRM_PRIV_H_ +#define _GRALLOC_DRM_PRIV_H_ + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "gralloc_drm_handle.h" + +/* how a bo is posted */ +enum drm_swap_mode { + DRM_SWAP_NOOP, + DRM_SWAP_FLIP, + DRM_SWAP_COPY, + DRM_SWAP_SETCRTC, +}; + +struct gralloc_drm_t { + /* initialized by gralloc_drm_create */ + int fd; + struct gralloc_drm_drv_t *drv; + + /* initialized by gralloc_drm_init_kms */ + drmModeResPtr resources; + uint32_t crtc_id; + uint32_t connector_id; + drmModeModeInfo mode; + int xdpi, ydpi; + int format; +#ifdef DRM_MODE_FEATURE_DIRTYFB + drmModeClip clip; +#endif + drmEventContext evctx; + + /* initialized by drv->init_kms_features */ + enum drm_swap_mode swap_mode; + int swap_interval; + int mode_dirty_fb; + int mode_sync_flip; /* page flip should block */ + int vblank_secondary; + + int first_post; + struct gralloc_drm_bo_t *current_front, *next_front; + int waiting_flip; + unsigned int last_swap; +}; + +struct gralloc_drm_drv_t { + /* destroy the driver */ + void (*destroy)(struct gralloc_drm_drv_t *drv); + + /* initialize KMS features */ + void (*init_kms_features)(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_t *drm); + + /* allocate or import a bo */ + struct gralloc_drm_bo_t *(*alloc)(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_handle_t *handle); + + /* free a bo */ + void (*free)(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo); + + /* map a bo for CPU access */ + int (*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); + + /* unmap a bo */ + void (*unmap)(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo); + + /* copy between two bo's, used for DRM_SWAP_COPY */ + void (*copy)(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *dst, + struct gralloc_drm_bo_t *src, + short x1, short y1, short x2, short y2); +}; + +struct gralloc_drm_bo_t { + struct gralloc_drm_t *drm; + struct gralloc_drm_handle_t *handle; + + int imported; /* the handle is from a remote proces when true */ + int fb_handle; /* the GEM handle of the bo */ + int fb_id; /* the fb id */ +}; + +struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_intel(int fd); +struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_radeon(int fd); +struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_pipe(int fd); + +#endif /* _GRALLOC_DRM_PRIV_H_ */ diff --git a/gralloc_drm_radeon.c b/gralloc_drm_radeon.c new file mode 100644 index 0000000..59fb782 --- /dev/null +++ b/gralloc_drm_radeon.c @@ -0,0 +1,366 @@ +/* + * 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" + +#define RADEON_GPU_PAGE_SIZE 4096 + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define ALIGN(val, align) (((val) + (align) - 1) & ~((align) - 1)) + +struct radeon_info { + struct gralloc_drm_drv_t base; + + int fd; + struct radeon_bo_manager *bufmgr; + + uint32_t tile_config; + int num_channels; + int num_banks; + int group_bytes; + /* r6xx+ tile config */ + int have_tiling_info; + int allow_color_tiling; +}; + +struct radeon_buffer { + struct gralloc_drm_bo_t base; + + struct radeon_bo *rbo; +}; + +static int eg_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; + + 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; + } + + info->num_banks = (info->tile_config & 0xf0) >> 4; + + switch ((info->tile_config & 0xf00) >> 8) { + case 0: + info->group_bytes = 256; + break; + case 1: + info->group_bytes = 512; + break; + default: + return -EINVAL; + break; + } + + info->have_tiling_info = 1; + info->allow_color_tiling = 0; + + return 0; +} + +/* returns pitch alignment in pixels */ +static int eg_get_pitch_align(struct radeon_info *info, int bpe, uint32_t tiling) +{ + int pitch_align = 1; + + 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; + } + + return pitch_align; +} + +/* returns height alignment in pixels */ +static int eg_get_height_align(struct radeon_info *info, uint32_t tiling) +{ + int height_align = 1; + + if (tiling & RADEON_TILING_MACRO) + height_align = info->num_channels * 8; + else if (tiling & RADEON_TILING_MICRO) + height_align = 8; + else + height_align = 8; + + return height_align; +} + +/* returns base alignment in bytes */ +static int eg_get_base_align(struct radeon_info *info, + int bpe, uint32_t tiling) +{ + int pixel_align = eg_get_pitch_align(info, bpe, tiling); + int height_align = eg_get_height_align(info, tiling); + int base_align = RADEON_GPU_PAGE_SIZE; + + 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 drm_gem_get_tiling(const struct gralloc_drm_handle_t *handle) +{ + return 0; +} + +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; + uint32_t tiling, domain; + int cpp; + + tiling = drm_gem_get_tiling(handle); + domain = RADEON_GEM_DOMAIN_VRAM; + cpp = gralloc_drm_get_bpp(handle->format); + if (!cpp) { + LOGE("unrecognized format 0x%x", handle->format); + return NULL; + } + + rbuf = calloc(1, sizeof(*rbuf)); + if (!rbuf) + return NULL; + + + if (handle->name) { + rbuf->rbo = radeon_bo_open(info->bufmgr, handle->name, + 0, 0, domain, 0); + if (!rbuf->rbo) { + LOGE("failed to create rbo from name %u", + handle->name); + free(rbuf); + return NULL; + } + } + else { + int aligned_width, aligned_height; + int pitch, size, base_align; + + if (handle->usage & (GRALLOC_USAGE_HW_FB | + GRALLOC_USAGE_HW_TEXTURE)) { + aligned_width = ALIGN(handle->width, + eg_get_pitch_align(info, cpp, tiling)); + aligned_height = ALIGN(handle->height, + eg_get_height_align(info, tiling)); + } + else { + aligned_width = handle->width; + aligned_height = handle->height; + } + + 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 = eg_get_base_align(info, cpp, tiling); + + rbuf->rbo = radeon_bo_open(info->bufmgr, 0, + size, base_align, domain, 0); + if (!rbuf->rbo) { + LOGE("failed to allocate rbo %dx%dx%d", + handle->width, handle->height, cpp); + free(rbuf); + return NULL; + } + + if (tiling) + radeon_bo_set_tiling(rbuf->rbo, tiling, pitch); + + if (radeon_gem_get_kernel_name(rbuf->rbo, + (uint32_t *) &handle->name)) { + LOGE("failed to flink rbo"); + radeon_bo_unref(rbuf->rbo); + free(rbuf); + return NULL; + } + + handle->stride = pitch; + } + + 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_init_kms_features(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_t *drm) +{ + drm->mode_dirty_fb = 0; + drm->swap_mode = DRM_SWAP_FLIP; + drm->mode_sync_flip = 1; + drm->swap_interval = 1; + drm->vblank_secondary = 0; +} + +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); +} + +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; + info->bufmgr = radeon_bo_manager_gem_ctor(info->fd); + if (!info->bufmgr) { + LOGE("failed to create buffer manager"); + free(info); + return NULL; + } + + if (eg_init_tile_config(info)) { + radeon_bo_manager_gem_dtor(info->bufmgr); + free(info); + return NULL; + } + + info->base.destroy = drm_gem_radeon_destroy; + info->base.init_kms_features = drm_gem_radeon_init_kms_features; + 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; +} |