diff options
author | Geoff Mendal <mendal@google.com> | 2015-01-20 13:59:44 -0800 |
---|---|---|
committer | Geoff Mendal <mendal@google.com> | 2015-01-20 13:59:44 -0800 |
commit | 96d72b6dffd58043770b3d7d9e2e18171be4b650 (patch) | |
tree | 20b8afc9bd585d8e3f0b1bca9a5207a10501d2b7 | |
parent | 69f4e60d17eb1e1f83fc71e9c061c6be4de791c7 (diff) | |
parent | 68a74eb31f139ad285a49451cf2ca8271c54f9bb (diff) | |
download | drm_gralloc-96d72b6dffd58043770b3d7d9e2e18171be4b650.tar.gz |
Merge branch 'lollipop-x86' of http://git.android-x86.org/platform/hardware/drm_gralloc
Bug: 18959563
-rw-r--r-- | Android.mk | 175 | ||||
-rw-r--r-- | gralloc.c | 361 | ||||
-rw-r--r-- | gralloc_drm.c | 437 | ||||
-rw-r--r-- | gralloc_drm.h | 150 | ||||
-rw-r--r-- | gralloc_drm_formats.h | 39 | ||||
-rw-r--r-- | gralloc_drm_handle.h | 75 | ||||
-rw-r--r-- | gralloc_drm_intel.c | 676 | ||||
-rw-r--r-- | gralloc_drm_kms.c | 1258 | ||||
-rw-r--r-- | gralloc_drm_nouveau.c | 384 | ||||
-rw-r--r-- | gralloc_drm_pipe.c | 563 | ||||
-rw-r--r-- | gralloc_drm_priv.h | 207 | ||||
-rw-r--r-- | gralloc_drm_radeon.c | 554 | ||||
-rw-r--r-- | pci_ids/pci_id_driver_map.h | 84 | ||||
-rw-r--r-- | radeon/radeon.h | 117 | ||||
-rw-r--r-- | radeon/radeon_chipinfo_gen.h | 645 |
15 files changed, 5725 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..cda4321 --- /dev/null +++ b/Android.mk @@ -0,0 +1,175 @@ +# 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 + +DRM_GPU_DRIVERS := $(strip $(filter-out swrast, $(BOARD_GPU_DRIVERS))) + +intel_drivers := i915 i965 i915g ilo +radeon_drivers := r300g r600g +nouveau_drivers := nouveau +vmwgfx_drivers := vmwgfx + +valid_drivers := \ + prebuilt \ + $(intel_drivers) \ + $(radeon_drivers) \ + $(nouveau_drivers) \ + $(vmwgfx_drivers) + +# warn about invalid drivers +invalid_drivers := $(filter-out $(valid_drivers), $(DRM_GPU_DRIVERS)) +ifneq ($(invalid_drivers),) +$(warning invalid GPU drivers: $(invalid_drivers)) +# tidy up +DRM_GPU_DRIVERS := $(filter-out $(invalid_drivers), $(DRM_GPU_DRIVERS)) +endif + +ifneq ($(filter $(vmwgfx_drivers), $(DRM_GPU_DRIVERS)),) +DRM_USES_PIPE := true +else +DRM_USES_PIPE := false +endif + +ifneq ($(strip $(DRM_GPU_DRIVERS)),) + +LOCAL_PATH := $(call my-dir) + + +# Use the PREBUILT libraries +ifeq ($(strip $(DRM_GPU_DRIVERS)),prebuilt) + +include $(CLEAR_VARS) +LOCAL_MODULE := libgralloc_drm +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := ../../$(BOARD_GPU_DRIVER_BINARY) +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := gralloc.$(TARGET_PRODUCT) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_SRC_FILES := ../../$(BOARD_GPU_DRIVER_BINARY) +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX) +include $(BUILD_PREBUILT) + +# Use the sources +else + +include $(CLEAR_VARS) +LOCAL_MODULE := libgralloc_drm +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + gralloc_drm.c \ + gralloc_drm_kms.c + +LOCAL_C_INCLUDES := \ + external/drm \ + external/drm/include/drm + +LOCAL_SHARED_LIBRARIES := \ + libdrm \ + liblog \ + libcutils \ + libhardware_legacy \ + +ifneq ($(filter $(intel_drivers), $(DRM_GPU_DRIVERS)),) +LOCAL_SRC_FILES += gralloc_drm_intel.c +LOCAL_C_INCLUDES += external/drm/intel +LOCAL_CFLAGS += -DENABLE_INTEL +LOCAL_SHARED_LIBRARIES += libdrm_intel +endif + +ifneq ($(filter $(radeon_drivers), $(DRM_GPU_DRIVERS)),) +LOCAL_SRC_FILES += gralloc_drm_radeon.c +LOCAL_C_INCLUDES += external/drm/radeon +LOCAL_CFLAGS += -DENABLE_RADEON +LOCAL_SHARED_LIBRARIES += libdrm_radeon +endif + +ifneq ($(filter $(nouveau_drivers), $(DRM_GPU_DRIVERS)),) +LOCAL_SRC_FILES += gralloc_drm_nouveau.c +LOCAL_C_INCLUDES += external/drm/nouveau +LOCAL_CFLAGS += -DENABLE_NOUVEAU +LOCAL_SHARED_LIBRARIES += libdrm_nouveau +endif + +ifeq ($(strip $(DRM_USES_PIPE)),true) +LOCAL_SRC_FILES += gralloc_drm_pipe.c +LOCAL_CFLAGS += -DENABLE_PIPE +LOCAL_C_INCLUDES += \ + external/mesa/include \ + external/mesa/src/gallium/include \ + external/mesa/src/gallium/winsys \ + external/mesa/src/gallium/drivers \ + external/mesa/src/gallium/auxiliary + +ifneq ($(filter r600g, $(DRM_GPU_DRIVERS)),) +LOCAL_CFLAGS += -DENABLE_PIPE_R600 +LOCAL_SHARED_LIBRARIES += libstlport +LOCAL_STATIC_LIBRARIES += \ + libmesa_pipe_r600 \ + libmesa_pipe_radeon \ + libmesa_winsys_radeon +endif +ifneq ($(filter vmwgfx, $(DRM_GPU_DRIVERS)),) +LOCAL_CFLAGS += -DENABLE_PIPE_VMWGFX +LOCAL_STATIC_LIBRARIES += \ + libmesa_pipe_svga \ + libmesa_winsys_svga +LOCAL_C_INCLUDES += \ + external/mesa/src/gallium/drivers/svga/include +endif + +LOCAL_STATIC_LIBRARIES += \ + libmesa_gallium +LOCAL_SHARED_LIBRARIES += libdl +endif # DRM_USES_PIPE +include $(BUILD_SHARED_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + gralloc.c \ + +LOCAL_C_INCLUDES := \ + external/drm \ + external/drm/include/drm \ + +LOCAL_SHARED_LIBRARIES := \ + libgralloc_drm \ + liblog \ + +# for glFlush/glFinish +LOCAL_SHARED_LIBRARIES += \ + libGLESv1_CM + +LOCAL_MODULE := gralloc.drm +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_RELATIVE_PATH := hw +include $(BUILD_SHARED_LIBRARY) + +endif # DRM_GPU_DRIVERS=prebuilt +endif # DRM_GPU_DRIVERS diff --git a/gralloc.c b/gralloc.c new file mode 100644 index 0000000..92866ca --- /dev/null +++ b/gralloc.c @@ -0,0 +1,361 @@ +/* + * 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_priv.h" + +/* + * 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) +{ + struct drm_module_t *dmod = (struct drm_module_t *) mod; + int err; + + err = drm_init(dmod, 0); + if (err) + return err; + + return gralloc_drm_handle_register(handle, dmod->drm); +} + +static int drm_mod_unregister_buffer(const gralloc_module_t *mod, + buffer_handle_t handle) +{ + return gralloc_drm_handle_unregister(handle); +} + +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 gralloc_drm_bo_t *bo; + int err; + + bo = gralloc_drm_bo_from_handle(handle); + if (!bo) + return -EINVAL; + + return gralloc_drm_bo_lock(bo, usage, x, y, w, h, 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_from_handle(handle); + if (!bo) + return -EINVAL; + + gralloc_drm_bo_unlock(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_from_handle(handle); + if (!bo) + return -EINVAL; + + gralloc_drm_bo_decref(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) { + ALOGE("failed to add fb"); + gralloc_drm_bo_decref(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_from_handle(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; + + ALOGI("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 + }, + .hwc_reserve_plane = gralloc_drm_reserve_plane, + .hwc_disable_planes = gralloc_drm_disable_planes, + .hwc_set_plane_handle = gralloc_drm_set_plane_handle, + + .mutex = PTHREAD_MUTEX_INITIALIZER, + .drm = NULL +}; diff --git a/gralloc_drm.c b/gralloc_drm.c new file mode 100644 index 0000000..31ac988 --- /dev/null +++ b/gralloc_drm.c @@ -0,0 +1,437 @@ +/* + * 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) { + ALOGE("invalid DRM fd"); + return NULL; + } + + if (version->name) { +#ifdef ENABLE_PIPE + drv = gralloc_drm_drv_create_for_pipe(fd, version->name); +#endif + +#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_NOUVEAU + if (!drv && !strcmp(version->name, "nouveau")) + drv = gralloc_drm_drv_create_for_nouveau(fd); +#endif + } + + if (!drv) { + ALOGE("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) { + ALOGE("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) +{ + ALOGD("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); +} + +/* + * Validate a buffer handle and return the associated bo. + */ +static struct gralloc_drm_bo_t *validate_handle(buffer_handle_t _handle, + struct gralloc_drm_t *drm) +{ + struct gralloc_drm_handle_t *handle = gralloc_drm_handle(_handle); + + if (!handle) + return NULL; + + /* the buffer handle is passed to a new process */ + if (unlikely(handle->data_owner != gralloc_drm_pid)) { + struct gralloc_drm_bo_t *bo; + + /* check only */ + if (!drm) + return NULL; + + /* create the struct gralloc_drm_bo_t locally */ + if (handle->name) + bo = drm->drv->alloc(drm->drv, handle); + else /* an invalid handle */ + bo = NULL; + if (bo) { + bo->drm = drm; + bo->imported = 1; + bo->handle = handle; + bo->refcount = 1; + } + + handle->data_owner = gralloc_drm_get_pid(); + handle->data = bo; + } + + return handle->data; +} + +/* + * Register a buffer handle. + */ +int gralloc_drm_handle_register(buffer_handle_t handle, struct gralloc_drm_t *drm) +{ + return (validate_handle(handle, drm)) ? 0 : -EINVAL; +} + +/* + * Unregister a buffer handle. It is no-op for handles created locally. + */ +int gralloc_drm_handle_unregister(buffer_handle_t handle) +{ + struct gralloc_drm_bo_t *bo; + + bo = validate_handle(handle, NULL); + if (!bo) + return -EINVAL; + + if (bo->imported) + gralloc_drm_bo_decref(bo); + + return 0; +} + +/* + * 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; + handle->plane_mask = 0; + + 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; + + handle->plane_mask = planes_for_format(drm, format); + + bo = drm->drv->alloc(drm->drv, handle); + if (!bo) { + free(handle); + return NULL; + } + + bo->drm = drm; + bo->imported = 0; + bo->handle = handle; + bo->fb_id = 0; + bo->refcount = 1; + + handle->data_owner = gralloc_drm_get_pid(); + handle->data = bo; + + return bo; +} + +/* + * Destroy a bo. + */ +static void gralloc_drm_bo_destroy(struct gralloc_drm_bo_t *bo) +{ + struct gralloc_drm_handle_t *handle = bo->handle; + int imported = bo->imported; + + /* gralloc still has a reference */ + if (bo->refcount) + return; + + gralloc_drm_bo_rm_fb(bo); + + bo->drm->drv->free(bo->drm->drv, bo); + if (imported) { + handle->data_owner = 0; + handle->data = 0; + } + else { + free(handle); + } +} + +/* + * Decrease refcount, if no refs anymore then destroy. + */ +void gralloc_drm_bo_decref(struct gralloc_drm_bo_t *bo) +{ + if (!--bo->refcount) + gralloc_drm_bo_destroy(bo); +} + +/* + * Return the bo of a registered handle. + */ +struct gralloc_drm_bo_t *gralloc_drm_bo_from_handle(buffer_handle_t handle) +{ + return validate_handle(handle, NULL); +} + +/* + * 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; +} + +int gralloc_drm_get_gem_handle(buffer_handle_t _handle) +{ + struct gralloc_drm_handle_t *handle = gralloc_drm_handle(_handle); + return (handle) ? handle->name : 0; +} + +/* + * Query YUV component offsets for a buffer handle + */ +void gralloc_drm_resolve_format(buffer_handle_t _handle, + uint32_t *pitches, uint32_t *offsets, uint32_t *handles) +{ + struct gralloc_drm_handle_t *handle = gralloc_drm_handle(_handle); + struct gralloc_drm_bo_t *bo = handle->data; + struct gralloc_drm_t *drm = bo->drm; + + /* if handle exists and driver implements resolve_format */ + if (handle && drm->drv->resolve_format) + drm->drv->resolve_format(drm->drv, bo, + pitches, offsets, handles); +} + +/* + * Lock a bo. XXX thread-safety? + */ +int gralloc_drm_bo_lock(struct gralloc_drm_bo_t *bo, + int usage, int x, int y, int w, int h, + void **addr) +{ + if ((bo->handle->usage & usage) != usage) { + /* make FB special for testing software renderer with */ + + if (!(bo->handle->usage & GRALLOC_USAGE_HW_FB) + && !(bo->handle->usage & GRALLOC_USAGE_HW_TEXTURE)) { + ALOGE("bo.usage:x%X/usage:x%X is not GRALLOC_USAGE_HW_FB or GRALLOC_USAGE_HW_TEXTURE" + ,bo->handle->usage,usage); + return -EINVAL; + } + } + + /* allow multiple locks with compatible usages */ + if (bo->lock_count && (bo->locked_for & usage) != usage) + return -EINVAL; + + usage |= bo->locked_for; + + if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | + GRALLOC_USAGE_SW_READ_MASK)) { + /* the driver is supposed to wait for the bo */ + int write = !!(usage & GRALLOC_USAGE_SW_WRITE_MASK); + int err = bo->drm->drv->map(bo->drm->drv, bo, + x, y, w, h, write, addr); + if (err) + return err; + } + else { + /* kernel handles the synchronization here */ + } + + bo->lock_count++; + bo->locked_for |= usage; + + return 0; +} + +/* + * Unlock a bo. + */ +void gralloc_drm_bo_unlock(struct gralloc_drm_bo_t *bo) +{ + int mapped = bo->locked_for & + (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_SW_READ_MASK); + + if (!bo->lock_count) + return; + + if (mapped) + bo->drm->drv->unmap(bo->drm->drv, bo); + + bo->lock_count--; + if (!bo->lock_count) + bo->locked_for = 0; +} diff --git a/gralloc_drm.h b/gralloc_drm.h new file mode 100644 index 0000000..c32aaf6 --- /dev/null +++ b/gralloc_drm.h @@ -0,0 +1,150 @@ +/* + * 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> +#include <system/graphics.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALIGN(val, align) (((val) + (align) - 1) & ~((align) - 1)) + +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_fini_kms(struct gralloc_drm_t *drm); +int gralloc_drm_is_kms_initialized(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_YCbCr_422_I: + bpp = 2; + break; + /* planar; only Y is considered */ + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_DRM_NV12: + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + bpp = 1; + break; + default: + bpp = 0; + break; + } + + return bpp; +} + +static inline void gralloc_drm_align_geometry(int format, int *width, int *height) +{ + int align_w = 1, align_h = 1, extra_height_div = 0; + + switch (format) { + case HAL_PIXEL_FORMAT_DRM_NV12: + case HAL_PIXEL_FORMAT_YV12: + align_w = 32; + align_h = 2; + extra_height_div = 2; + break; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + align_w = 2; + extra_height_div = 1; + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + align_w = 2; + align_h = 2; + extra_height_div = 2; + break; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + align_w = 2; + break; + } + + *width = ALIGN(*width, align_w); + *height = ALIGN(*height, align_h); + + if (extra_height_div) + *height += *height / extra_height_div; +} + +int gralloc_drm_handle_register(buffer_handle_t handle, struct gralloc_drm_t *drm); +int gralloc_drm_handle_unregister(buffer_handle_t handle); + +struct gralloc_drm_bo_t *gralloc_drm_bo_create(struct gralloc_drm_t *drm, int width, int height, int format, int usage); +void gralloc_drm_bo_decref(struct gralloc_drm_bo_t *bo); + +struct gralloc_drm_bo_t *gralloc_drm_bo_from_handle(buffer_handle_t handle); +buffer_handle_t gralloc_drm_bo_get_handle(struct gralloc_drm_bo_t *bo, int *stride); +int gralloc_drm_get_gem_handle(buffer_handle_t handle); +void gralloc_drm_resolve_format(buffer_handle_t _handle, uint32_t *pitches, uint32_t *offsets, uint32_t *handles); +unsigned int planes_for_format(struct gralloc_drm_t *drm, int hal_format); + +int gralloc_drm_bo_lock(struct gralloc_drm_bo_t *bo, int x, int y, int w, int h, int enable_write, void **addr); +void gralloc_drm_bo_unlock(struct gralloc_drm_bo_t *bo); + +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); + +int gralloc_drm_reserve_plane(struct gralloc_drm_t *drm, + buffer_handle_t handle, uint32_t id, + uint32_t dst_x, uint32_t dst_y, uint32_t dst_w, uint32_t dst_h, + uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); +void gralloc_drm_disable_planes(struct gralloc_drm_t *mod); +int gralloc_drm_set_plane_handle(struct gralloc_drm_t *drm, + uint32_t id, buffer_handle_t handle); + +#ifdef __cplusplus +} +#endif +#endif /* _GRALLOC_DRM_H_ */ diff --git a/gralloc_drm_formats.h b/gralloc_drm_formats.h new file mode 100644 index 0000000..b8d0622 --- /dev/null +++ b/gralloc_drm_formats.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Android graphics.h defines the formats and leaves 0x100 - 0x1FF + * range available for HAL implementation specific formats. + */ + +#ifndef GRALLOC_DRM_FORMATS_H +#define GRALLOC_DRM_FORMATS_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + + HAL_PIXEL_FORMAT_DRM_NV12 = 0x102, +}; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/gralloc_drm_handle.h b/gralloc_drm_handle.h new file mode 100644 index 0000000..7fc4746 --- /dev/null +++ b/gralloc_drm_handle.h @@ -0,0 +1,75 @@ +/* + * 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> +#include <system/graphics.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct gralloc_drm_bo_t; + +struct gralloc_drm_handle_t { + native_handle_t base; + +#define GRALLOC_DRM_HANDLE_MAGIC 0x12345678 +#define GRALLOC_DRM_HANDLE_NUM_INTS 10 +#define GRALLOC_DRM_HANDLE_NUM_FDS 0 + int magic; + + int width; + int height; + int format; + int usage; + + unsigned int plane_mask; /* planes that support handle */ + + int name; /* the name of the bo */ + int stride; /* the stride in bytes */ + + int data_owner; /* owner of data (for validation) */ + struct gralloc_drm_bo_t *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 && (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; +} + +#ifdef __cplusplus +} +#endif +#endif /* _GRALLOC_DRM_HANDLE_H_ */ diff --git a/gralloc_drm_intel.c b/gralloc_drm_intel.c new file mode 100644 index 0000000..3a020b4 --- /dev/null +++ b/gralloc_drm_intel.c @@ -0,0 +1,676 @@ +/* + * 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; + 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_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; + } + } + + if (batch_reserve(info, 8)) + return; + + batch_dword(info, cmd); + batch_dword(info, br13 | (uint16_t)dst_pitch); + batch_dword(info, (dst_y1 << 16) | dst_x1); + batch_dword(info, (dst_y2 << 16) | dst_x2); + 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); + 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_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; +} diff --git a/gralloc_drm_kms.c b/gralloc_drm_kms.c new file mode 100644 index 0000000..cb02a13 --- /dev/null +++ b/gralloc_drm_kms.c @@ -0,0 +1,1258 @@ +/* + * 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/properties.h> +#include <cutils/log.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <poll.h> +#include <math.h> +#include "gralloc_drm.h" +#include "gralloc_drm_priv.h" +#include <hardware_legacy/uevent.h> + +#include <drm_fourcc.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); +} + +static unsigned int drm_format_from_hal(int hal_format) +{ + switch(hal_format) { + case HAL_PIXEL_FORMAT_RGB_888: + case HAL_PIXEL_FORMAT_BGRA_8888: + return DRM_FORMAT_XRGB8888; + case HAL_PIXEL_FORMAT_RGBX_8888: + return DRM_FORMAT_XBGR8888; + case HAL_PIXEL_FORMAT_RGBA_8888: + return DRM_FORMAT_RGBA8888; + case HAL_PIXEL_FORMAT_RGB_565: + return DRM_FORMAT_RGB565; + case HAL_PIXEL_FORMAT_YV12: + return DRM_FORMAT_YUV420; + case HAL_PIXEL_FORMAT_DRM_NV12: + return DRM_FORMAT_NV12; + default: + return 0; + } +} + +/* + * Modify pitches, offsets and handles according to + * the format and return corresponding drm format value + */ +static int resolve_drm_format(struct gralloc_drm_bo_t *bo, + uint32_t *pitches, uint32_t *offsets, uint32_t *handles) +{ + struct gralloc_drm_t *drm = bo->drm; + + pitches[0] = bo->handle->stride; + handles[0] = bo->fb_handle; + + /* driver takes care of HW specific padding, alignment etc. */ + if (drm->drv->resolve_format) + drm->drv->resolve_format(drm->drv, bo, + pitches, offsets, handles); + + return drm_format_from_hal(bo->handle->format); +} + +/* + * Returns planes that are supported for a particular format + */ +unsigned int planes_for_format(struct gralloc_drm_t *drm, + int hal_format) +{ + unsigned int i, j, mask = 0; + unsigned int drm_format = drm_format_from_hal(hal_format); + struct gralloc_drm_plane_t *plane = drm->planes; + + /* no planes available */ + if (!plane) + return 0; + + /* iterate through planes, mark those that match format */ + for (i=0; i<drm->plane_resources->count_planes; i++, plane++) + for (j=0; j<plane->drm_plane->count_formats; j++) + if (plane->drm_plane->formats[j] == drm_format) + mask |= (1U << plane->drm_plane->plane_id); + + return mask; +} + +/* + * Add a fb object for a bo. + */ +int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo) +{ + uint32_t pitches[4] = { 0, 0, 0, 0 }; + uint32_t offsets[4] = { 0, 0, 0, 0 }; + uint32_t handles[4] = { 0, 0, 0, 0 }; + + if (bo->fb_id) + return 0; + + int drm_format = resolve_drm_format(bo, pitches, offsets, handles); + + if (drm_format == 0) { + ALOGE("error resolving drm format"); + return -EINVAL; + } + + return drmModeAddFB2(bo->drm->fd, + bo->handle->width, bo->handle->height, + drm_format, handles, pitches, offsets, + (uint32_t *) &bo->fb_id, 0); +} + +/* + * 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, + struct gralloc_drm_output *output, int fb_id) +{ + int ret; + + ret = drmModeSetCrtc(drm->fd, output->crtc_id, fb_id, + 0, 0, &output->connector_id, 1, &output->mode); + if (ret) { + ALOGE("failed to set crtc (%s) (crtc_id %d, fb_id %d, conn %d, mode %dx%d)", + strerror(errno), output->crtc_id, fb_id, output->connector_id, + output->mode.hdisplay, output->mode.vdisplay); + return ret; + } + + if (drm->mode_quirk_vmwgfx) + ret = drmModeDirtyFB(drm->fd, fb_id, &drm->clip, 1); + + 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; +} + +/* + * Set a plane. + */ +static int gralloc_drm_bo_setplane(struct gralloc_drm_t *drm, + struct gralloc_drm_plane_t *plane) +{ + struct gralloc_drm_bo_t *bo = NULL; + int err; + + if (plane->handle) + bo = gralloc_drm_bo_from_handle(plane->handle); + + // create a framebuffer if does not exist + if (bo && bo->fb_id == 0) { + err = gralloc_drm_bo_add_fb(bo); + if (err) { + ALOGE("%s: could not create drm fb, (%s)", + __func__, strerror(-err)); + return err; + } + } + + err = drmModeSetPlane(drm->fd, + plane->drm_plane->plane_id, + drm->primary.crtc_id, + bo ? bo->fb_id : 0, + 0, // flags + plane->dst_x, + plane->dst_y, + plane->dst_w, + plane->dst_h, + plane->src_x << 16, + plane->src_y << 16, + plane->src_w << 16, + plane->src_h << 16); + + if (err) { + /* clear plane_mask so that this buffer won't be tried again */ + struct gralloc_drm_handle_t *drm_handle = + (struct gralloc_drm_handle_t *) plane->handle; + drm_handle->plane_mask = 0; + + ALOGE("drmModeSetPlane : error (%s) (plane %d crtc %d fb %d)", + strerror(-err), + plane->drm_plane->plane_id, + drm->primary.crtc_id, + bo ? bo->fb_id : 0); + } + + if (plane->prev) + gralloc_drm_bo_decref(plane->prev); + + if (bo) + bo->refcount++; + + plane->prev = bo; + + return err; +} + +/* + * Returns if a particular plane is supported with the implementation + */ +static unsigned is_plane_supported(const struct gralloc_drm_t *drm, + const struct gralloc_drm_plane_t *plane) +{ + /* Planes are only supported on primary pipe for now */ + return plane->drm_plane->possible_crtcs & (1 << drm->primary.pipe); +} + +/* + * Sets all the active planes to be displayed. + */ +static void gralloc_drm_set_planes(struct gralloc_drm_t *drm) +{ + struct gralloc_drm_plane_t *plane = drm->planes; + unsigned int i; + for (i = 0; i < drm->plane_resources->count_planes; + i++, plane++) { + /* plane is not in use at all */ + if (!plane->active && !plane->handle) + continue; + + /* plane is active, safety check if it is supported */ + if (!is_plane_supported(drm, plane)) + ALOGE("%s: plane %d is not supported", + __func__, plane->drm_plane->plane_id); + + /* + * Disable overlay if it is not active + * or if there is error during setplane + */ + if (!plane->active) + plane->handle = 0; + + if (gralloc_drm_bo_setplane(drm, plane)) + plane->active = 0; + } +} + +/* + * Interface for HWC, used to reserve a plane for a layer. + */ +int gralloc_drm_reserve_plane(struct gralloc_drm_t *drm, + buffer_handle_t handle, + uint32_t id, + uint32_t dst_x, + uint32_t dst_y, + uint32_t dst_w, + uint32_t dst_h, + uint32_t src_x, + uint32_t src_y, + uint32_t src_w, + uint32_t src_h) +{ + int j; + struct gralloc_drm_handle_t *drm_handle = + gralloc_drm_handle(handle); + int plane_count = drm->plane_resources->count_planes; + struct gralloc_drm_plane_t *plane = drm->planes; + + /* no supported planes for this handle */ + if (!drm_handle->plane_mask) { + ALOGE("%s: buffer %p cannot be shown on a plane\n", + __func__, drm_handle); + return -EINVAL; + } + + for (j = 0; j < plane_count; j++, plane++) { + + /* + * handle may be suitable to be shown on a plane, in + * addition we need to check that this particular plane + * is supported by the current implementation + */ + if (!is_plane_supported(drm, plane)) + continue; + + /* if plane is available and can support this buffer */ + if (!plane->active && + drm_handle->plane_mask & + (1U << plane->drm_plane->plane_id)) { + + plane->dst_x = dst_x; + plane->dst_y = dst_y; + plane->dst_w = dst_w; + plane->dst_h = dst_h; + plane->src_x = src_x; + plane->src_y = src_y; + plane->src_w = src_w; + plane->src_h = src_h; + plane->handle = handle; + plane->id = id; + plane->active = 1; + + return 0; + } + } + + /* no free planes available */ + return -EBUSY; +} + +/* + * Interface for HWC, used to disable all the overlays. Plane id + * is also set to 0 as it should be mappable to a particular layer only + * if it has been reserved with 'reserve_plane'. + */ +void gralloc_drm_disable_planes(struct gralloc_drm_t *drm) +{ + struct gralloc_drm_plane_t *plane = drm->planes; + unsigned int i; + + for (i = 0; i < drm->plane_resources->count_planes; i++, plane++) { + plane->active = 0; + plane->id = 0; + } +} + +/* + * Interface for HWC, used to change handle of a reserved plane. + */ +int gralloc_drm_set_plane_handle(struct gralloc_drm_t *drm, + uint32_t id, buffer_handle_t handle) +{ + struct gralloc_drm_plane_t *plane = drm->planes; + unsigned i; + + for (i = 0; i < drm->plane_resources->count_planes; i++, plane++) + if (plane->active && plane->id == id) { + plane->handle = handle; + return 0; + } + + return -EINVAL; +} + +/* + * 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 */ + ALOGE("drmHandleEvent returned without flipping"); + drm->current_front = drm->next_front; + drm->next_front = NULL; + } + } + + if (!bo) + return 0; + + pthread_mutex_lock(&drm->hdmi_mutex); + if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED && drm->hdmi.bo) { + + int dst_x1 = 0, dst_y1 = 0; + + if (drm->hdmi.bo->handle->width > bo->handle->width) + dst_x1 = (drm->hdmi.bo->handle->width - bo->handle->width) / 2; + if (drm->hdmi.bo->handle->height > bo->handle->height) + dst_y1 = (drm->hdmi.bo->handle->height - bo->handle->height) / 2; + + drm->drv->blit(drm->drv, drm->hdmi.bo, bo, + dst_x1, dst_y1, + dst_x1 + bo->handle->width, + dst_y1 + bo->handle->height, + 0, 0, bo->handle->width, bo->handle->height); + + ret = drmModePageFlip(drm->fd, drm->hdmi.crtc_id, drm->hdmi.bo->fb_id, 0, NULL); + if (ret && errno != EBUSY) + ALOGE("failed to perform page flip for hdmi (%s) (crtc %d fb %d))", + strerror(errno), drm->hdmi.crtc_id, drm->hdmi.bo->fb_id); + } + pthread_mutex_unlock(&drm->hdmi_mutex); + + /* set planes to be displayed */ + gralloc_drm_set_planes(drm); + + ret = drmModePageFlip(drm->fd, drm->primary.crtc_id, bo->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, (void *) drm); + if (ret) { + ALOGE("failed to perform page flip for primary (%s) (crtc %d fb %d))", + strerror(errno), drm->primary.crtc_id, bo->fb_id); + /* try to set mode for next frame */ + if (errno != EBUSY) + drm->first_post = 1; + } + 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; + + if (drm->mode_quirk_vmwgfx) + return; + + 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) { + ALOGW("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) { + ALOGW("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) { + ALOGE("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->blit(drm->drv, dst, bo, 0, 0, + bo->handle->width, + bo->handle->height, + 0, 0, + bo->handle->width, + bo->handle->height); + bo = dst; + } + + ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id); + if (!ret) { + drm->first_post = 0; + drm->current_front = bo; + if (drm->next_front == bo) + drm->next_front = NULL; + } + + pthread_mutex_lock(&drm->hdmi_mutex); + if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED && drm->hdmi.bo) + drm_kms_set_crtc(drm, &drm->hdmi, drm->hdmi.bo->fb_id); + pthread_mutex_unlock(&drm->hdmi_mutex); + + 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->blit(drm->drv, drm->current_front, + bo, 0, 0, + bo->handle->width, + bo->handle->height, + 0, 0, + bo->handle->width, + bo->handle->height); + if (drm->mode_quirk_vmwgfx) + ret = drmModeDirtyFB(drm->fd, drm->current_front->fb_id, &drm->clip, 1); + ret = 0; + break; + case DRM_SWAP_SETCRTC: + drm_kms_wait_for_post(drm, 0); + ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id); + + pthread_mutex_lock(&drm->hdmi_mutex); + if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED && drm->hdmi.bo) + drm_kms_set_crtc(drm, &drm->hdmi, drm->hdmi.bo->fb_id); + pthread_mutex_unlock(&drm->hdmi_mutex); + + 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 gralloc_drm_t *drm = drm_singleton; + + /* wait the pending flip */ + if (drm && drm->swap_mode == DRM_SWAP_FLIP && drm->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->primary.mode.hdisplay, + drm->primary.mode.vdisplay, + drm->primary.fb_format, + GRALLOC_USAGE_HW_FB); + if (front && gralloc_drm_bo_add_fb(front)) { + gralloc_drm_bo_decref(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; + } + + ALOGD("will use %s for fb posting", swap_mode); +} + +#define MARGIN_PERCENT 1.8 /* % of active vertical image*/ +#define CELL_GRAN 8.0 /* assumed character cell granularity*/ +#define MIN_PORCH 1 /* minimum front porch */ +#define V_SYNC_RQD 3 /* width of vsync in lines */ +#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */ +#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */ +#define M 600.0 /* blanking formula gradient */ +#define C 40.0 /* blanking formula offset */ +#define K 128.0 /* blanking formula scaling factor */ +#define J 20.0 /* blanking formula scaling factor */ +/* C' and M' are part of the Blanking Duty Cycle computation */ +#define C_PRIME (((C - J) * K / 256.0) + J) +#define M_PRIME (K / 256.0 * M) + +static drmModeModeInfoPtr generate_mode(int h_pixels, int v_lines, float freq) +{ + float h_pixels_rnd; + float v_lines_rnd; + float v_field_rate_rqd; + float top_margin; + float bottom_margin; + float interlace; + float h_period_est; + float vsync_plus_bp; + float v_back_porch; + float total_v_lines; + float v_field_rate_est; + float h_period; + float v_field_rate; + float v_frame_rate; + float left_margin; + float right_margin; + float total_active_pixels; + float ideal_duty_cycle; + float h_blank; + float total_pixels; + float pixel_freq; + float h_freq; + + float h_sync; + float h_front_porch; + float v_odd_front_porch_lines; + int interlaced = 0; + int margins = 0; + + drmModeModeInfoPtr m = malloc(sizeof(drmModeModeInfo)); + + h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN; + v_lines_rnd = interlaced ? rint((float) v_lines) / 2.0 : rint((float) v_lines); + v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq); + top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0); + bottom_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0); + interlace = interlaced ? 0.5 : 0.0; + h_period_est = (((1.0 / v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP / 1000000.0)) / (v_lines_rnd + (2 * top_margin) + MIN_PORCH + interlace) * 1000000.0); + vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP / h_period_est); + v_back_porch = vsync_plus_bp - V_SYNC_RQD; + total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp + interlace + MIN_PORCH; + v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0; + h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est); + v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0; + v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate; + left_margin = margins ? rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : 0.0; + right_margin = margins ? rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : 0.0; + total_active_pixels = h_pixels_rnd + left_margin + right_margin; + ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0); + h_blank = rint(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN); + total_pixels = total_active_pixels + h_blank; + pixel_freq = total_pixels / h_period; + h_freq = 1000.0 / h_period; + h_sync = rint(H_SYNC_PERCENT / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN; + h_front_porch = (h_blank / 2.0) - h_sync; + v_odd_front_porch_lines = MIN_PORCH + interlace; + + m->clock = ceil(pixel_freq) * 1000; + m->hdisplay = (int) (h_pixels_rnd); + m->hsync_start = (int) (h_pixels_rnd + h_front_porch); + m->hsync_end = (int) (h_pixels_rnd + h_front_porch + h_sync); + m->htotal = (int) (total_pixels); + m->hskew = 0; + m->vdisplay = (int) (v_lines_rnd); + m->vsync_start = (int) (v_lines_rnd + v_odd_front_porch_lines); + m->vsync_end = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD); + m->vtotal = (int) (total_v_lines); + m->vscan = 0; + m->vrefresh = freq; + m->flags = 10; + m->type = 64; + + return (m); +} + +static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp) +{ + char value[PROPERTY_VALUE_MAX]; + drmModeModeInfoPtr mode; + int dist, i; + int xres = 0, yres = 0, rate = 0; + int forcemode = 0; + + if (property_get("debug.drm.mode", value, NULL)) { + char *p = value, *end; + + /* parse <xres>x<yres>[@<bpp>] */ + if (sscanf(value, "%dx%d@%d", &xres, &yres, bpp) != 3) { + *bpp = 0; + if (sscanf(value, "%dx%d", &xres, &yres) != 2) + xres = yres = 0; + } + + if ((xres && yres) || *bpp) { + ALOGI("will find the closest match for %dx%d@%d", + xres, yres, *bpp); + } + } else if (property_get("debug.drm.mode.force", value, NULL)) { + char *p = value, *end; + *bpp = 0; + + /* parse <xres>x<yres>[@<refreshrate>] */ + if (sscanf(value, "%dx%d@%d", &xres, &yres, &rate) != 3) { + rate = 60; + if (sscanf(value, "%dx%d", &xres, &yres) != 2) + xres = yres = 0; + } + + if (xres && yres && rate) { + ALOGI("will use %dx%d@%dHz", xres, yres, rate); + forcemode = 1; + } + } else { + *bpp = 0; + } + + dist = INT_MAX; + + if (forcemode) + mode = generate_mode(xres, yres, rate); + else { + mode = NULL; + for (i = 0; i < connector->count_modes; i++) { + drmModeModeInfoPtr m = &connector->modes[i]; + int tmp; + + if (xres && yres) { + tmp = (m->hdisplay - xres) * (m->hdisplay - xres) + + (m->vdisplay - yres) * (m->vdisplay - yres); + } + else { + /* use the first preferred mode */ + tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist; + } + + if (tmp < dist) { + mode = m; + dist = tmp; + if (!dist) + break; + } + } + } + + /* fallback to the first mode */ + if (!mode) + mode = &connector->modes[0]; + + ALOGI("Established mode:"); + ALOGI("clock: %d, hdisplay: %d, hsync_start: %d, hsync_end: %d, htotal: %d, hskew: %d", mode->clock, mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, mode->hskew); + ALOGI("vdisplay: %d, vsync_start: %d, vsync_end: %d, vtotal: %d, vscan: %d, vrefresh: %d", mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, mode->vscan, mode->vrefresh); + ALOGI("flags: %d, type: %d, name %s", mode->flags, mode->type, mode->name); + + *bpp /= 8; + + return mode; +} + +/* + * Initialize KMS with a connector. + */ +static int drm_kms_init_with_connector(struct gralloc_drm_t *drm, + struct gralloc_drm_output *output, drmModeConnectorPtr connector) +{ + drmModeEncoderPtr encoder; + drmModeModeInfoPtr mode; + static int used_crtcs = 0; + int bpp, i; + + if (!connector->count_modes) + return -EINVAL; + + encoder = drmModeGetEncoder(drm->fd, connector->encoders[0]); + if (!encoder) + return -EINVAL; + + /* find first possible crtc which is not used yet */ + for (i = 0; i < drm->resources->count_crtcs; i++) { + if (encoder->possible_crtcs & (1 << i) && + (used_crtcs & (1 << i)) != (1 << i)) + break; + } + + used_crtcs |= (1 << i); + + drmModeFreeEncoder(encoder); + if (i == drm->resources->count_crtcs) + return -EINVAL; + + output->bo = NULL; + output->crtc_id = drm->resources->crtcs[i]; + output->connector_id = connector->connector_id; + output->pipe = i; + + /* print connector info */ + if (connector->count_modes > 1) { + ALOGI("there are %d modes on connector 0x%x, type %d", + connector->count_modes, + connector->connector_id, + connector->connector_type); + for (i = 0; i < connector->count_modes; i++) + ALOGI(" %s", connector->modes[i].name); + } + else { + ALOGI("there is one mode on connector 0x%d: %s", + connector->connector_id, + connector->modes[0].name); + } + + mode = find_mode(connector, &bpp); + + ALOGI("the best mode is %s", mode->name); + + output->mode = *mode; + switch (bpp) { + case 2: + output->fb_format = HAL_PIXEL_FORMAT_RGB_565; + break; + case 4: + default: + output->fb_format = HAL_PIXEL_FORMAT_BGRA_8888; + break; + } + + if (connector->mmWidth && connector->mmHeight) { + output->xdpi = (output->mode.hdisplay * 25.4 / connector->mmWidth); + output->ydpi = (output->mode.vdisplay * 25.4 / connector->mmHeight); + } + else { + output->xdpi = 75; + output->ydpi = 75; + } + +#ifdef DRM_MODE_FEATURE_DIRTYFB + drm->clip.x1 = 0; + drm->clip.y1 = 0; + drm->clip.x2 = output->mode.hdisplay; + drm->clip.y2 = output->mode.vdisplay; +#endif + + return 0; +} + + +/* + * Fetch a connector of particular type + */ +static drmModeConnectorPtr fetch_connector(struct gralloc_drm_t *drm, + uint32_t type) +{ + int i; + + if (!drm->resources) + return NULL; + + for (i = 0; i < drm->resources->count_connectors; i++) { + drmModeConnectorPtr connector = + connector = drmModeGetConnector(drm->fd, + drm->resources->connectors[i]); + if (connector) { + if (connector->connector_type == type && + connector->connection == DRM_MODE_CONNECTED) + return connector; + drmModeFreeConnector(connector); + } + } + return NULL; +} + + +/* + * Initializes hdmi output with a connector and allocates + * a private framebuffer for it. This is called on startup if + * hdmi cable is connected and also on hotplug events. + */ +static void init_hdmi_output(struct gralloc_drm_t *drm, + drmModeConnectorPtr connector) +{ + drm_kms_init_with_connector(drm, &drm->hdmi, connector); + + ALOGD("%s, allocate private buffer for hdmi [%dx%d]", + __func__, drm->hdmi.mode.hdisplay, drm->hdmi.mode.vdisplay); + + drm->hdmi.bo = gralloc_drm_bo_create(drm, + drm->hdmi.mode.hdisplay, drm->hdmi.mode.vdisplay, + drm->hdmi.fb_format, + GRALLOC_USAGE_SW_WRITE_OFTEN|GRALLOC_USAGE_HW_RENDER); + + gralloc_drm_bo_add_fb(drm->hdmi.bo); + + drm->hdmi_mode = HDMI_CLONED; + drm->hdmi.active = 1; +} + + +/* + * Thread that listens to uevents and checks if hdmi state changes + */ +static void *hdmi_observer(void *data) +{ + static char uevent_desc[4096]; + drmModeConnectorPtr hdmi; + struct gralloc_drm_t *drm = + (struct gralloc_drm_t *) data; + + uevent_init(); + + memset(uevent_desc, 0, sizeof(uevent_desc)); + + while(1) { + + /* this polls */ + int len = uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2); + + if(len && strstr(uevent_desc, "devices/virtual/switch/hdmi")) { + + /* check what changed */ + const char *prop = uevent_desc + strlen(uevent_desc) + 1; + + while (*prop) { + + const char *state = strstr(prop, "SWITCH_STATE="); + if (state) { + unsigned int value = 0; + state += strlen("SWITCH_STATE="); + value = atoi(state); + + pthread_mutex_lock(&drm->hdmi_mutex); + + if (value) { + hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA); + if (hdmi) { + + ALOGD("init hdmi on hotplug event"); + init_hdmi_output(drm, hdmi); + + /* will trigger modeset */ + drm->first_post = 1; + + drmModeFreeConnector(hdmi); + + pthread_mutex_unlock(&drm->hdmi_mutex); + } + break; + } else { + drm->hdmi.active = 0; + + ALOGD("destroy hdmi private buffer"); + gralloc_drm_bo_decref(drm->hdmi.bo); + drm->hdmi.bo = NULL; + + pthread_mutex_unlock(&drm->hdmi_mutex); + break; + } + + pthread_mutex_unlock(&drm->hdmi_mutex); + } + + /* next property/value pair */ + prop += strlen(prop) + 1; + if (prop - uevent_desc >= len) + break; + } + } + } + + pthread_exit(NULL); + return 0; +} + + +/* + * Initialize KMS. + */ +int gralloc_drm_init_kms(struct gralloc_drm_t *drm) +{ + drmModeConnectorPtr lvds, hdmi; + int i, ret; + + if (drm->resources) + return 0; + + drm->resources = drmModeGetResources(drm->fd); + if (!drm->resources) { + ALOGE("failed to get modeset resources"); + return -EINVAL; + } + + drm->plane_resources = drmModeGetPlaneResources(drm->fd); + if (!drm->plane_resources) { + ALOGD("no planes found from drm resources"); + } else { + unsigned int i, j; + + ALOGD("supported drm planes and formats"); + /* fill a helper structure for hwcomposer */ + drm->planes = calloc(drm->plane_resources->count_planes, + sizeof(struct gralloc_drm_plane_t)); + + for (i = 0; i < drm->plane_resources->count_planes; i++) { + drm->planes[i].drm_plane = drmModeGetPlane(drm->fd, + drm->plane_resources->planes[i]); + + ALOGD("plane id %d", drm->planes[i].drm_plane->plane_id); + for (j = 0; j < drm->planes[i].drm_plane->count_formats; j++) + ALOGD(" format %c%c%c%c", + (drm->planes[i].drm_plane->formats[j]), + (drm->planes[i].drm_plane->formats[j])>>8, + (drm->planes[i].drm_plane->formats[j])>>16, + (drm->planes[i].drm_plane->formats[j])>>24); + } + } + + /* find the crtc/connector/mode to use */ + lvds = fetch_connector(drm, DRM_MODE_CONNECTOR_LVDS); + if (lvds) { + drm_kms_init_with_connector(drm, &drm->primary, lvds); + drmModeFreeConnector(lvds); + drm->primary.active = 1; + } + + /* if still no connector, find first connected connector and try it */ + if (!drm->primary.active) { + + 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, + &drm->primary, connector)) + break; + } + + drmModeFreeConnector(connector); + } + } + if (i == drm->resources->count_connectors) { + ALOGE("failed to find a valid crtc/connector/mode combination"); + drmModeFreeResources(drm->resources); + drm->resources = NULL; + + return -EINVAL; + } + } + + + /* check if hdmi is connected already */ + hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA); + if (hdmi) { + + if (hdmi->connector_id == drm->primary.connector_id) { + /* special case: our primary connector is hdmi */ + ALOGD("hdmi is the primary connector"); + goto skip_hdmi_modes; + } + + ALOGD("init hdmi on startup"); + init_hdmi_output(drm, hdmi); + + drmModeFreeConnector(hdmi); + } + +goto skip_hdmi_modes; + /* launch hdmi observer thread */ + pthread_mutex_init(&drm->hdmi_mutex, NULL); + pthread_create(&drm->hdmi_hotplug_thread, NULL, hdmi_observer, drm); + +skip_hdmi_modes: + + drm_kms_init_features(drm); + drm->first_post = 1; + + return 0; +} + +void gralloc_drm_fini_kms(struct gralloc_drm_t *drm) +{ + switch (drm->swap_mode) { + case DRM_SWAP_FLIP: + drm_kms_page_flip(drm, NULL); + break; + case DRM_SWAP_COPY: + { + struct gralloc_drm_bo_t **bo = (drm->current_front) ? + &drm->current_front : &drm->next_front; + + if (*bo) + gralloc_drm_bo_decref(*bo); + *bo = NULL; + } + break; + default: + break; + } + + /* restore crtc? */ + + if (drm->resources) { + drmModeFreeResources(drm->resources); + drm->resources = NULL; + } + + if (drm->planes) { + unsigned int i; + for (i = 0; i < drm->plane_resources->count_planes; i++) + drmModeFreePlane(drm->planes[i].drm_plane); + free(drm->planes); + drm->planes = NULL; + } + + if (drm->plane_resources) { + drmModeFreePlaneResources(drm->plane_resources); + drm->plane_resources = NULL; + } + + /* destroy private buffer of hdmi output */ + if (drm->hdmi.bo) + gralloc_drm_bo_decref(drm->hdmi.bo); + + drm_singleton = NULL; +} + +int gralloc_drm_is_kms_initialized(struct gralloc_drm_t *drm) +{ + return (drm->resources != NULL); +} + +/* + * 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->primary.mode.hdisplay; + *((uint32_t *) &fb->height) = drm->primary.mode.vdisplay; + *((int *) &fb->stride) = drm->primary.mode.hdisplay; + *((float *) &fb->fps) = drm->primary.mode.vrefresh; + + *((int *) &fb->format) = drm->primary.fb_format; + *((float *) &fb->xdpi) = drm->primary.xdpi; + *((float *) &fb->ydpi) = drm->primary.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_nouveau.c b/gralloc_drm_nouveau.c new file mode 100644 index 0000000..576966e --- /dev/null +++ b/gralloc_drm_nouveau.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2011 Chia-I Wu <olvaffe@gmail.com> + * Copyright (C) 2011 LunarG Inc. + * + * Based on xf86-video-nouveau, which has + * + * Copyright © 2007 Red Hat, Inc. + * Copyright © 2008 Maarten Maathuis + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define LOG_TAG "GRALLOC-NOUVEAU" + +#include <cutils/log.h> +#include <stdlib.h> +#include <errno.h> +#include <drm.h> +#include <nouveau_drmif.h> +#include <nouveau_channel.h> +#include <nouveau_bo.h> + +#include "gralloc_drm.h" +#include "gralloc_drm_priv.h" + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define NVC0_TILE_HEIGHT(m) (8 << ((m) >> 4)) + +enum { + NvDmaFB = 0xd8000001, + NvDmaTT = 0xd8000002, +}; + +struct nouveau_info { + struct gralloc_drm_drv_t base; + + int fd; + struct nouveau_device *dev; + struct nouveau_channel *chan; + int arch; + int tiled_scanout; +}; + +struct nouveau_buffer { + struct gralloc_drm_bo_t base; + + struct nouveau_bo *bo; +}; + +static struct nouveau_bo *alloc_bo(struct nouveau_info *info, + int width, int height, int cpp, int usage, int *pitch) +{ + struct nouveau_bo *bo = NULL; + int flags, tile_mode, tile_flags; + int tiled, scanout; + unsigned int align; + + flags = NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM; + tile_mode = 0; + tile_flags = 0; + + scanout = !!(usage & GRALLOC_USAGE_HW_FB); + + tiled = !(usage & (GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN)); + if (!info->chan) + tiled = 0; + else if (scanout && info->tiled_scanout) + tiled = 1; + + /* calculate pitch align */ + align = 64; + if (info->arch >= 0x50) { + if (scanout && !info->tiled_scanout) + align = 256; + else + tiled = 1; + } + + *pitch = ALIGN(width * cpp, align); + + if (tiled) { + if (info->arch >= 0xc0) { + if (height > 64) + tile_mode = 0x40; + else if (height > 32) + tile_mode = 0x30; + else if (height > 16) + tile_mode = 0x20; + else if (height > 8) + tile_mode = 0x10; + else + tile_mode = 0x00; + + tile_flags = 0xfe00; + + align = NVC0_TILE_HEIGHT(tile_mode); + height = ALIGN(height, align); + } + else if (info->arch >= 0x50) { + if (height > 32) + tile_mode = 4; + else if (height > 16) + tile_mode = 3; + else if (height > 8) + tile_mode = 2; + else if (height > 4) + tile_mode = 1; + else + tile_mode = 0; + + tile_flags = (scanout && cpp != 2) ? 0x7a00 : 0x7000; + + align = 1 << (tile_mode + 2); + height = ALIGN(height, align); + } + else { + align = *pitch / 4; + + /* round down to the previous power of two */ + align >>= 1; + align |= align >> 1; + align |= align >> 2; + align |= align >> 4; + align |= align >> 8; + align |= align >> 16; + align++; + + align = MAX((info->dev->chipset >= 0x40) ? 1024 : 256, + align); + + /* adjust pitch */ + *pitch = ALIGN(*pitch, align); + + tile_mode = *pitch; + } + } + + if (cpp == 4) + tile_flags |= NOUVEAU_BO_TILE_32BPP; + else if (cpp == 2) + tile_flags |= NOUVEAU_BO_TILE_16BPP; + + if (scanout) + tile_flags |= NOUVEAU_BO_TILE_SCANOUT; + + if (nouveau_bo_new_tile(info->dev, flags, 0, *pitch * height, + tile_mode, tile_flags, &bo)) { + ALOGE("failed to allocate bo (flags 0x%x, size %d, tile_mode 0x%x, tile_flags 0x%x)", + flags, *pitch * height, tile_mode, tile_flags); + bo = NULL; + } + + return bo; +} + +static struct gralloc_drm_bo_t * +nouveau_alloc(struct gralloc_drm_drv_t *drv, struct gralloc_drm_handle_t *handle) +{ + struct nouveau_info *info = (struct nouveau_info *) drv; + struct nouveau_buffer *nb; + int cpp; + + cpp = gralloc_drm_get_bpp(handle->format); + if (!cpp) { + ALOGE("unrecognized format 0x%x", handle->format); + return NULL; + } + + nb = calloc(1, sizeof(*nb)); + if (!nb) + return NULL; + + if (handle->name) { + if (nouveau_bo_handle_ref(info->dev, handle->name, &nb->bo)) { + ALOGE("failed to create nouveau bo from name %u", + handle->name); + free(nb); + return NULL; + } + } + else { + int width, height, pitch; + + width = handle->width; + height = handle->height; + gralloc_drm_align_geometry(handle->format, &width, &height); + + nb->bo = alloc_bo(info, width, height, + cpp, handle->usage, &pitch); + if (!nb->bo) { + ALOGE("failed to allocate nouveau bo %dx%dx%d", + handle->width, handle->height, cpp); + free(nb); + return NULL; + } + + if (nouveau_bo_handle_get(nb->bo, + (uint32_t *) &handle->name)) { + ALOGE("failed to flink nouveau bo"); + nouveau_bo_ref(NULL, &nb->bo); + free(nb); + return NULL; + } + + handle->stride = pitch; + } + + if (handle->usage & GRALLOC_USAGE_HW_FB) + nb->base.fb_handle = nb->bo->handle; + + nb->base.handle = handle; + + return &nb->base; +} + +static void nouveau_free(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo) +{ + struct nouveau_buffer *nb = (struct nouveau_buffer *) bo; + nouveau_bo_ref(NULL, &nb->bo); + free(nb); +} + +static int nouveau_map(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo, int x, int y, int w, int h, + int enable_write, void **addr) +{ + struct nouveau_buffer *nb = (struct nouveau_buffer *) bo; + uint32_t flags; + int err; + + flags = NOUVEAU_BO_RD; + if (enable_write) + flags |= NOUVEAU_BO_WR; + + /* TODO if tiled, allocate a linear copy of bo in GART and map it */ + err = nouveau_bo_map(nb->bo, flags); + if (!err) + *addr = nb->bo->map; + + return err; +} + +static void nouveau_unmap(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo) +{ + struct nouveau_buffer *nb = (struct nouveau_buffer *) bo; + /* TODO if tiled, unmap the linear bo and copy back */ + nouveau_bo_unmap(nb->bo); +} + +static void nouveau_init_kms_features(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_t *drm) +{ + struct nouveau_info *info = (struct nouveau_info *) drv; + + 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; + drm->swap_mode = (info->chan) ? DRM_SWAP_FLIP : DRM_SWAP_SETCRTC; + drm->mode_sync_flip = 1; + drm->swap_interval = 1; + drm->vblank_secondary = 0; +} + +static void nouveau_destroy(struct gralloc_drm_drv_t *drv) +{ + struct nouveau_info *info = (struct nouveau_info *) drv; + + if (info->chan) + nouveau_channel_free(&info->chan); + nouveau_device_close(&info->dev); + free(info); +} + +static int nouveau_init(struct nouveau_info *info) +{ + int err = 0; + + switch (info->dev->chipset & 0xf0) { + case 0x00: + info->arch = 0x04; + break; + case 0x10: + info->arch = 0x10; + break; + case 0x20: + info->arch = 0x20; + break; + case 0x30: + info->arch = 0x30; + break; + case 0x40: + case 0x60: + info->arch = 0x40; + break; + case 0x50: + case 0x80: + case 0x90: + case 0xa0: + info->arch = 0x50; + break; + case 0xc0: + info->arch = 0xc0; + break; + default: + ALOGE("unknown nouveau chipset 0x%x", info->dev->chipset); + err = -EINVAL; + break; + } + + info->tiled_scanout = (info->chan != NULL); + + return err; +} + +struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_nouveau(int fd) +{ + struct nouveau_info *info; + int err; + + info = calloc(1, sizeof(*info)); + if (!info) + return NULL; + + info->fd = fd; + err = nouveau_device_open_existing(&info->dev, 0, info->fd, 0); + if (err) { + ALOGE("failed to create nouveau device"); + free(info); + return NULL; + } + + err = nouveau_channel_alloc(info->dev, NvDmaFB, NvDmaTT, + 24 * 1024, &info->chan); + if (err) { + /* make it non-fatal temporarily as it may require firmwares */ + ALOGW("failed to create nouveau channel"); + info->chan = NULL; + } + + err = nouveau_init(info); + if (err) { + if (info->chan) + nouveau_channel_free(&info->chan); + nouveau_device_close(&info->dev); + free(info); + return NULL; + } + + info->base.destroy = nouveau_destroy; + info->base.init_kms_features = nouveau_init_kms_features; + info->base.alloc = nouveau_alloc; + info->base.free = nouveau_free; + info->base.map = nouveau_map; + info->base.unmap = nouveau_unmap; + + return &info->base; +} diff --git a/gralloc_drm_pipe.c b/gralloc_drm_pipe.c new file mode 100644 index 0000000..ec3e19d --- /dev/null +++ b/gralloc_drm_pipe.c @@ -0,0 +1,563 @@ +/* + * 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-PIPE" + +#include <cutils/log.h> +#include <errno.h> + +#include <pipe/p_screen.h> +#include <pipe/p_context.h> +#include <state_tracker/drm_driver.h> +#include <util/u_inlines.h> +#include <util/u_memory.h> + +#include "gralloc_drm.h" +#include "gralloc_drm_priv.h" + +struct pipe_manager { + struct gralloc_drm_drv_t base; + + int fd; + char driver[16]; + pthread_mutex_t mutex; + struct pipe_screen *screen; + struct pipe_context *context; +}; + +struct pipe_buffer { + struct gralloc_drm_bo_t base; + + struct pipe_resource *resource; + struct winsys_handle winsys; + + struct pipe_transfer *transfer; +}; + +static enum pipe_format get_pipe_format(int format) +{ + enum pipe_format fmt; + + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + fmt = PIPE_FORMAT_R8G8B8A8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGBX_8888: + fmt = PIPE_FORMAT_R8G8B8X8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGB_888: + fmt = PIPE_FORMAT_R8G8B8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGB_565: + fmt = PIPE_FORMAT_B5G6R5_UNORM; + break; + case HAL_PIXEL_FORMAT_BGRA_8888: + fmt = PIPE_FORMAT_B8G8R8A8_UNORM; + break; + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_DRM_NV12: + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + default: + fmt = PIPE_FORMAT_NONE; + break; + } + + return fmt; +} + +static unsigned get_pipe_bind(int usage) +{ + unsigned bind = PIPE_BIND_SHARED; + + if (usage & GRALLOC_USAGE_SW_READ_MASK) + bind |= PIPE_BIND_TRANSFER_READ; + if (usage & GRALLOC_USAGE_SW_WRITE_MASK) + bind |= PIPE_BIND_TRANSFER_WRITE; + + if (usage & GRALLOC_USAGE_HW_TEXTURE) + bind |= PIPE_BIND_SAMPLER_VIEW; + if (usage & GRALLOC_USAGE_HW_RENDER) + bind |= PIPE_BIND_RENDER_TARGET; + if (usage & GRALLOC_USAGE_HW_FB) { + bind |= PIPE_BIND_RENDER_TARGET; + bind |= PIPE_BIND_SCANOUT; + } + + return bind; +} + +static struct pipe_buffer *get_pipe_buffer_locked(struct pipe_manager *pm, + const struct gralloc_drm_handle_t *handle) +{ + struct pipe_buffer *buf; + struct pipe_resource templ; + + memset(&templ, 0, sizeof(templ)); + templ.format = get_pipe_format(handle->format); + templ.bind = get_pipe_bind(handle->usage); + templ.target = PIPE_TEXTURE_2D; + + if (templ.format == PIPE_FORMAT_NONE || + !pm->screen->is_format_supported(pm->screen, templ.format, + templ.target, 0, templ.bind)) { + ALOGE("unsupported format 0x%x", handle->format); + return NULL; + } + + buf = CALLOC(1, sizeof(*buf)); + if (!buf) { + ALOGE("failed to allocate pipe buffer"); + return NULL; + } + + templ.width0 = handle->width; + templ.height0 = handle->height; + templ.depth0 = 1; + templ.array_size = 1; + + if (handle->name) { + buf->winsys.type = DRM_API_HANDLE_TYPE_SHARED; + buf->winsys.handle = handle->name; + buf->winsys.stride = handle->stride; + + buf->resource = pm->screen->resource_from_handle(pm->screen, + &templ, &buf->winsys); + if (!buf->resource) + goto fail; + } + else { + buf->resource = + pm->screen->resource_create(pm->screen, &templ); + if (!buf->resource) + goto fail; + + buf->winsys.type = DRM_API_HANDLE_TYPE_SHARED; + if (!pm->screen->resource_get_handle(pm->screen, + buf->resource, &buf->winsys)) + goto fail; + } + + /* need the gem handle for fb */ + if (handle->usage & GRALLOC_USAGE_HW_FB) { + struct winsys_handle tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = DRM_API_HANDLE_TYPE_KMS; + if (!pm->screen->resource_get_handle(pm->screen, + buf->resource, &tmp)) + goto fail; + + buf->base.fb_handle = tmp.handle; + } + + return buf; + +fail: + ALOGE("failed to allocate pipe buffer"); + if (buf->resource) + pipe_resource_reference(&buf->resource, NULL); + FREE(buf); + + return NULL; +} + +static struct gralloc_drm_bo_t *pipe_alloc(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_handle_t *handle) +{ + struct pipe_manager *pm = (struct pipe_manager *) drv; + struct pipe_buffer *buf; + + pthread_mutex_lock(&pm->mutex); + buf = get_pipe_buffer_locked(pm, handle); + pthread_mutex_unlock(&pm->mutex); + + if (buf) { + handle->name = (int) buf->winsys.handle; + handle->stride = (int) buf->winsys.stride; + + buf->base.handle = handle; + } + + return &buf->base; +} + +static void pipe_free(struct gralloc_drm_drv_t *drv, struct gralloc_drm_bo_t *bo) +{ + struct pipe_manager *pm = (struct pipe_manager *) drv; + struct pipe_buffer *buf = (struct pipe_buffer *) bo; + + pthread_mutex_lock(&pm->mutex); + + if (buf->transfer) + pipe_transfer_unmap(pm->context, buf->transfer); + pipe_resource_reference(&buf->resource, NULL); + + pthread_mutex_unlock(&pm->mutex); + + FREE(buf); +} + +static int pipe_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 pipe_manager *pm = (struct pipe_manager *) drv; + struct pipe_buffer *buf = (struct pipe_buffer *) bo; + int err = 0; + + pthread_mutex_lock(&pm->mutex); + + /* need a context to get transfer */ + if (!pm->context) { + pm->context = pm->screen->context_create(pm->screen, NULL); + if (!pm->context) { + ALOGE("failed to create pipe context"); + err = -ENOMEM; + } + } + + if (!err) { + enum pipe_transfer_usage usage; + + usage = PIPE_TRANSFER_READ; + if (enable_write) + usage |= PIPE_TRANSFER_WRITE; + + assert(!buf->transfer); + + /* + * ignore x, y, w and h so that returned addr points at the + * start of the buffer + */ + *addr = pipe_transfer_map(pm->context, buf->resource, + 0, 0, usage, 0, 0, + buf->resource->width0, buf->resource->height0, + &buf->transfer); + if (*addr == NULL) + err = -ENOMEM; + } + + pthread_mutex_unlock(&pm->mutex); + + return err; +} + +static void pipe_unmap(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo) +{ + struct pipe_manager *pm = (struct pipe_manager *) drv; + struct pipe_buffer *buf = (struct pipe_buffer *) bo; + + pthread_mutex_lock(&pm->mutex); + + assert(buf && buf->transfer); + + pipe_transfer_unmap(pm->context, buf->transfer); + buf->transfer = NULL; + + pm->context->flush(pm->context, NULL, 0); + + pthread_mutex_unlock(&pm->mutex); +} + +static void pipe_blit(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *dst_bo, + struct gralloc_drm_bo_t *src_bo, + 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 pipe_manager *pm = (struct pipe_manager *) drv; + struct pipe_buffer *dst = (struct pipe_buffer *) dst_bo; + struct pipe_buffer *src = (struct pipe_buffer *) src_bo; + struct pipe_box src_box; + + if (dst_bo->handle->width != src_bo->handle->width || + dst_bo->handle->height != src_bo->handle->height || + dst_bo->handle->stride != src_bo->handle->stride || + dst_bo->handle->format != src_bo->handle->format) { + ALOGE("copy between incompatible buffers"); + return; + } + + if (dst_x2 > dst_bo->handle->width) + dst_x2 = dst_bo->handle->width; + if (dst_y2 > dst_bo->handle->height) + dst_y2 = dst_bo->handle->height; + + if (src_x2 <= src_x1 || src_y2 <= src_y1) + return; + + u_box_2d(src_x1, src_y1, src_x2 - src_x1, src_y2 - src_y1, &src_box); + + pthread_mutex_lock(&pm->mutex); + + /* need a context for copying */ + if (!pm->context) { + pm->context = pm->screen->context_create(pm->screen, NULL); + if (!pm->context) { + ALOGE("failed to create pipe context"); + pthread_mutex_unlock(&pm->mutex); + return; + } + } + + pm->context->resource_copy_region(pm->context, + dst->resource, 0, dst_x1, dst_y1, 0, + src->resource, 0, &src_box); + pm->context->flush(pm->context, NULL, 0); + + pthread_mutex_unlock(&pm->mutex); +} + +static void pipe_init_kms_features(struct gralloc_drm_drv_t *drv, struct gralloc_drm_t *drm) +{ + struct pipe_manager *pm = (struct pipe_manager *) drv; + + 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; + } + + if (strcmp(pm->driver, "vmwgfx") == 0) { + drm->mode_quirk_vmwgfx = 1; + drm->swap_mode = DRM_SWAP_COPY; + } + else { + drm->mode_quirk_vmwgfx = 0; + drm->swap_mode = DRM_SWAP_FLIP; + } + drm->mode_sync_flip = 1; + drm->swap_interval = 1; + drm->vblank_secondary = 0; +} + +static void pipe_destroy(struct gralloc_drm_drv_t *drv) +{ + struct pipe_manager *pm = (struct pipe_manager *) drv; + + if (pm->context) + pm->context->destroy(pm->context); + pm->screen->destroy(pm->screen); + FREE(pm); +} + +/* for nouveau */ +#include "nouveau/drm/nouveau_drm_public.h" +/* for r300 */ +#include "radeon/drm/radeon_drm_public.h" +#include "r300/r300_public.h" +/* for r600 */ +#include "radeon/drm/radeon_winsys.h" +#include "r600/r600_public.h" +/* for vmwgfx */ +#include "svga/drm/svga_drm_public.h" +#include "svga/svga_winsys.h" +#include "svga/svga_public.h" +/* for debug */ +#include "target-helpers/inline_debug_helper.h" + +static int pipe_init_screen(struct pipe_manager *pm) +{ + struct pipe_screen *screen = NULL; + +#ifdef ENABLE_PIPE_NOUVEAU + if (strcmp(pm->driver, "nouveau") == 0) + screen = nouveau_drm_screen_create(pm->fd); +#endif +#ifdef ENABLE_PIPE_R300 + if (strcmp(pm->driver, "r300") == 0) { + struct radeon_winsys *sws = radeon_drm_winsys_create(pm->fd); + + if (sws) { + screen = r300_screen_create(sws); + if (!screen) + sws->destroy(sws); + } + } +#endif +#ifdef ENABLE_PIPE_R600 + if (strcmp(pm->driver, "r600") == 0) { + struct radeon_winsys *sws = radeon_drm_winsys_create(pm->fd); + + if (sws) { + screen = r600_screen_create(sws); + if (!screen) + sws->destroy(sws); + } + } +#endif +#ifdef ENABLE_PIPE_VMWGFX + if (strcmp(pm->driver, "vmwgfx") == 0) { + struct svga_winsys_screen *sws = + svga_drm_winsys_screen_create(pm->fd); + + if (sws) { + screen = svga_screen_create(sws); + if (!screen) + sws->destroy(sws); + } + } +#endif + + if (!screen) { + ALOGW("failed to create screen for %s", pm->driver); + return -EINVAL; + } + + pm->screen = debug_screen_wrap(screen); + + return 0; +} + +#include <xf86drm.h> +#include <i915_drm.h> +#include <radeon_drm.h> +static int pipe_get_pci_id(struct pipe_manager *pm, + const char *name, int *vendor, int *device) +{ + int err = -EINVAL; + + if (strcmp(name, "i915") == 0) { + struct drm_i915_getparam gp; + + *vendor = 0x8086; + + memset(&gp, 0, sizeof(gp)); + gp.param = I915_PARAM_CHIPSET_ID; + gp.value = device; + err = drmCommandWriteRead(pm->fd, DRM_I915_GETPARAM, &gp, sizeof(gp)); + } + else if (strcmp(name, "radeon") == 0) { + struct drm_radeon_info info; + + *vendor = 0x1002; + + memset(&info, 0, sizeof(info)); + info.request = RADEON_INFO_DEVICE_ID; + info.value = (long) device; + err = drmCommandWriteRead(pm->fd, DRM_RADEON_INFO, &info, sizeof(info)); + } + else if (strcmp(name, "nouveau") == 0) { + *vendor = 0x10de; + *device = 0; + err = 0; + } + else if (strcmp(name, "vmwgfx") == 0) { + *vendor = 0x15ad; + /* assume SVGA II */ + *device = 0x0405; + err = 0; + } + else { + err = -EINVAL; + } + + return err; +} + +#define DRIVER_MAP_GALLIUM_ONLY +#include "pci_ids/pci_id_driver_map.h" +static int pipe_find_driver(struct pipe_manager *pm, const char *name) +{ + int vendor, device; + int err; + const char *driver; + + err = pipe_get_pci_id(pm, name, &vendor, &device); + if (!err) { + int idx; + + /* look up in the driver map */ + for (idx = 0; driver_map[idx].driver; idx++) { + int i; + + if (vendor != driver_map[idx].vendor_id) + continue; + + if (driver_map[idx].num_chips_ids == -1) + break; + + for (i = 0; i < driver_map[idx].num_chips_ids; i++) { + if (driver_map[idx].chip_ids[i] == device) + break; + } + if (i < driver_map[idx].num_chips_ids) + break; + } + + driver = driver_map[idx].driver; + err = (driver) ? 0 : -ENODEV; + } + else { + if (strcmp(name, "vmwgfx") == 0) { + driver = "vmwgfx"; + err = 0; + } + } + + if (!err) + strncpy(pm->driver, driver, sizeof(pm->driver) - 1); + + return err; +} + +struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_pipe(int fd, const char *name) +{ + struct pipe_manager *pm; + + pm = CALLOC(1, sizeof(*pm)); + if (!pm) { + ALOGE("failed to allocate pipe manager for %s", name); + return NULL; + } + + pm->fd = fd; + pthread_mutex_init(&pm->mutex, NULL); + + if (pipe_find_driver(pm, name)) { + FREE(pm); + return NULL; + } + + if (pipe_init_screen(pm)) { + FREE(pm); + return NULL; + } + + pm->base.destroy = pipe_destroy; + pm->base.init_kms_features = pipe_init_kms_features; + pm->base.alloc = pipe_alloc; + pm->base.free = pipe_free; + pm->base.map = pipe_map; + pm->base.unmap = pipe_unmap; + pm->base.blit = pipe_blit; + + return &pm->base; +} diff --git a/gralloc_drm_priv.h b/gralloc_drm_priv.h new file mode 100644 index 0000000..1a36cc1 --- /dev/null +++ b/gralloc_drm_priv.h @@ -0,0 +1,207 @@ +/* + * 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 <pthread.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "gralloc_drm_handle.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* how a bo is posted */ +enum drm_swap_mode { + DRM_SWAP_NOOP, + DRM_SWAP_FLIP, + DRM_SWAP_COPY, + DRM_SWAP_SETCRTC, +}; + +enum hdmi_output_mode { + HDMI_CLONED, + HDMI_EXTENDED, +}; + +struct gralloc_drm_plane_t { + drmModePlane *drm_plane; + + /* plane has been set to display a layer */ + uint32_t active; + + /* handle to display */ + buffer_handle_t handle; + + /* identifier set by hwc */ + uint32_t id; + + /* position, crop and scale */ + uint32_t src_x; + uint32_t src_y; + uint32_t src_w; + uint32_t src_h; + uint32_t dst_x; + uint32_t dst_y; + uint32_t dst_w; + uint32_t dst_h; + + /* previous buffer, for refcounting */ + struct gralloc_drm_bo_t *prev; +}; + +struct gralloc_drm_output +{ + uint32_t crtc_id; + uint32_t connector_id; + uint32_t pipe; + drmModeModeInfo mode; + int xdpi, ydpi; + int fb_format; + int bpp; + uint32_t active; + + /* 'private fb' for this output */ + struct gralloc_drm_bo_t *bo; +}; + +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; + struct gralloc_drm_output primary; + struct gralloc_drm_output hdmi; + enum hdmi_output_mode hdmi_mode; + + /* hdmi hotplug */ + pthread_mutex_t hdmi_mutex; + pthread_t hdmi_hotplug_thread; + +#ifdef DRM_MODE_FEATURE_DIRTYFB + drmModeClip clip; +#endif + + /* initialized by drv->init_kms_features */ + enum drm_swap_mode swap_mode; + int swap_interval; + int mode_quirk_vmwgfx; + int mode_sync_flip; /* page flip should block */ + int vblank_secondary; + + drmEventContext evctx; + + int first_post; + struct gralloc_drm_bo_t *current_front, *next_front; + int waiting_flip; + unsigned int last_swap; + + /* plane support */ + drmModePlaneResPtr plane_resources; + struct gralloc_drm_plane_t *planes; +}; + +struct drm_module_t { + gralloc_module_t base; + + /* HWC plane API */ + int (*hwc_reserve_plane) (struct gralloc_drm_t *mod, + buffer_handle_t handle, uint32_t id, + uint32_t dst_x, uint32_t dst_y, uint32_t dst_w, uint32_t dst_h, + uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); + void (*hwc_disable_planes) (struct gralloc_drm_t *mod); + int (*hwc_set_plane_handle) (struct gralloc_drm_t *mod, + uint32_t id, buffer_handle_t handle); + + pthread_mutex_t mutex; + struct gralloc_drm_t *drm; +}; + +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); + + /* blit between two bo's, used for DRM_SWAP_COPY and general blitting */ + void (*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); + + /* query component offsets, strides and handles for a format */ + void (*resolve_format)(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_bo_t *bo, + uint32_t *pitches, uint32_t *offsets, uint32_t *handles); +}; + +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 */ + + int lock_count; + int locked_for; + + unsigned int refcount; +}; + +struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_pipe(int fd, const char *name); +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_nouveau(int fd); + +#ifdef __cplusplus +} +#endif +#endif /* _GRALLOC_DRM_PRIV_H_ */ diff --git a/gralloc_drm_radeon.c b/gralloc_drm_radeon.c new file mode 100644 index 0000000..be2ea9b --- /dev/null +++ b/gralloc_drm_radeon.c @@ -0,0 +1,554 @@ +/* + * 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_init_kms_features(struct gralloc_drm_drv_t *drv, + struct gralloc_drm_t *drm) +{ + 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; + 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); +} + +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.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; +} diff --git a/pci_ids/pci_id_driver_map.h b/pci_ids/pci_id_driver_map.h new file mode 100644 index 0000000..c3eec14 --- /dev/null +++ b/pci_ids/pci_id_driver_map.h @@ -0,0 +1,84 @@ +#ifndef _PCI_ID_DRIVER_MAP_H_ +#define _PCI_ID_DRIVER_MAP_H_ + +#include <stddef.h> + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#if !defined(DRIVER_MAP_DRI2_ONLY) && !defined(DRIVER_MAP_GALLIUM_ONLY) +static const int i810_chip_ids[] = { +#define CHIPSET(chip, desc, misc) chip, +#include "pci_ids/i810_pci_ids.h" +#undef CHIPSET +}; +#endif + +static const int i915_chip_ids[] = { +#define CHIPSET(chip, desc, misc) chip, +#include "pci_ids/i915_pci_ids.h" +#undef CHIPSET +}; + +static const int i965_chip_ids[] = { +#define CHIPSET(chip, desc, misc) chip, +#include "pci_ids/i965_pci_ids.h" +#undef CHIPSET +}; + +#ifndef DRIVER_MAP_GALLIUM_ONLY +static const int r100_chip_ids[] = { +#define CHIPSET(chip, name, family) chip, +#include "pci_ids/radeon_pci_ids.h" +#undef CHIPSET +}; + +static const int r200_chip_ids[] = { +#define CHIPSET(chip, name, family) chip, +#include "pci_ids/r200_pci_ids.h" +#undef CHIPSET +}; +#endif + +static const int r300_chip_ids[] = { +#define CHIPSET(chip, name, family) chip, +#include "pci_ids/r300_pci_ids.h" +#undef CHIPSET +}; + +static const int r600_chip_ids[] = { +#define CHIPSET(chip, name, family) chip, +#include "pci_ids/r600_pci_ids.h" +#undef CHIPSET +}; + +static const int vmwgfx_chip_ids[] = { +#define CHIPSET(chip, name, family) chip, +#include "pci_ids/vmwgfx_pci_ids.h" +#undef CHIPSET +}; + +static const struct { + int vendor_id; + const char *driver; + const int *chip_ids; + int num_chips_ids; +} driver_map[] = { +#if !defined(DRIVER_MAP_DRI2_ONLY) && !defined(DRIVER_MAP_GALLIUM_ONLY) + { 0x8086, "i810", i810_chip_ids, ARRAY_SIZE(i810_chip_ids) }, +#endif + { 0x8086, "i915", i915_chip_ids, ARRAY_SIZE(i915_chip_ids) }, + { 0x8086, "i965", i965_chip_ids, ARRAY_SIZE(i965_chip_ids) }, +#ifndef DRIVER_MAP_GALLIUM_ONLY + { 0x1002, "radeon", r100_chip_ids, ARRAY_SIZE(r100_chip_ids) }, + { 0x1002, "r200", r200_chip_ids, ARRAY_SIZE(r200_chip_ids) }, +#endif + { 0x1002, "r300", r300_chip_ids, ARRAY_SIZE(r300_chip_ids) }, + { 0x1002, "r600", r600_chip_ids, ARRAY_SIZE(r600_chip_ids) }, + { 0x10de, "nouveau", NULL, -1 }, + { 0x15ad, "vmwgfx", vmwgfx_chip_ids, ARRAY_SIZE(vmwgfx_chip_ids) }, + { 0x0000, NULL, NULL, 0 }, +}; + +#endif /* _PCI_ID_DRIVER_MAP_H_ */ diff --git a/radeon/radeon.h b/radeon/radeon.h new file mode 100644 index 0000000..a4c818e --- /dev/null +++ b/radeon/radeon.h @@ -0,0 +1,117 @@ +/* A subset of radeon.h from xf86-video-ati */ + +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * 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 on 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 + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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: + * Kevin E. Martin <martin@xfree86.org> + * Rickard E. Faith <faith@valinux.com> + * Alan Hourihane <alanh@fairlite.demon.co.uk> + * + */ + +#ifndef _RADEON_H_ +#define _RADEON_H_ + +typedef enum { + CHIP_FAMILY_UNKNOW, + CHIP_FAMILY_LEGACY, + CHIP_FAMILY_RADEON, + CHIP_FAMILY_RV100, + CHIP_FAMILY_RS100, /* U1 (IGP320M) or A3 (IGP320)*/ + CHIP_FAMILY_RV200, + CHIP_FAMILY_RS200, /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350), RS250 (IGP 7000) */ + CHIP_FAMILY_R200, + CHIP_FAMILY_RV250, + CHIP_FAMILY_RS300, /* RS300/RS350 */ + CHIP_FAMILY_RV280, + CHIP_FAMILY_R300, + CHIP_FAMILY_R350, + CHIP_FAMILY_RV350, + CHIP_FAMILY_RV380, /* RV370/RV380/M22/M24 */ + CHIP_FAMILY_R420, /* R420/R423/M18 */ + CHIP_FAMILY_RV410, /* RV410, M26 */ + CHIP_FAMILY_RS400, /* xpress 200, 200m (RS400) Intel */ + CHIP_FAMILY_RS480, /* xpress 200, 200m (RS410/480/482/485) AMD */ + CHIP_FAMILY_RV515, /* rv515 */ + CHIP_FAMILY_R520, /* r520 */ + CHIP_FAMILY_RV530, /* rv530 */ + CHIP_FAMILY_R580, /* r580 */ + CHIP_FAMILY_RV560, /* rv560 */ + CHIP_FAMILY_RV570, /* rv570 */ + CHIP_FAMILY_RS600, + CHIP_FAMILY_RS690, + CHIP_FAMILY_RS740, + CHIP_FAMILY_R600, /* r600 */ + CHIP_FAMILY_RV610, + CHIP_FAMILY_RV630, + CHIP_FAMILY_RV670, + CHIP_FAMILY_RV620, + CHIP_FAMILY_RV635, + CHIP_FAMILY_RS780, + CHIP_FAMILY_RS880, + CHIP_FAMILY_RV770, /* r700 */ + CHIP_FAMILY_RV730, + CHIP_FAMILY_RV710, + CHIP_FAMILY_RV740, + CHIP_FAMILY_CEDAR, /* evergreen */ + CHIP_FAMILY_REDWOOD, + CHIP_FAMILY_JUNIPER, + CHIP_FAMILY_CYPRESS, + CHIP_FAMILY_HEMLOCK, + CHIP_FAMILY_PALM, + CHIP_FAMILY_SUMO, + CHIP_FAMILY_SUMO2, + CHIP_FAMILY_BARTS, + CHIP_FAMILY_TURKS, + CHIP_FAMILY_CAICOS, + CHIP_FAMILY_CAYMAN, + CHIP_FAMILY_ARUBA, + CHIP_FAMILY_TAHITI, + CHIP_FAMILY_PITCAIRN, + CHIP_FAMILY_VERDE, + CHIP_FAMILY_OLAND, + CHIP_FAMILY_HAINAN, + CHIP_FAMILY_BONAIRE, + CHIP_FAMILY_KAVERI, + CHIP_FAMILY_KABINI, + CHIP_FAMILY_LAST +} RADEONChipFamily; + +typedef struct { + uint32_t pci_device_id; + RADEONChipFamily chip_family; + int mobility; + int igp; + int nocrtc2; + int nointtvout; + int singledac; +} RADEONCardInfo; + +#endif /* _RADEON_H_ */ diff --git a/radeon/radeon_chipinfo_gen.h b/radeon/radeon_chipinfo_gen.h new file mode 100644 index 0000000..221d93e --- /dev/null +++ b/radeon/radeon_chipinfo_gen.h @@ -0,0 +1,645 @@ +/* This file is autogenerated please do not edit */ +static RADEONCardInfo RADEONCards[] = { + { 0x3150, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x3151, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x3152, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x3154, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x3155, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x3E50, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x3E54, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x4136, CHIP_FAMILY_RS100, 0, 1, 0, 0, 1 }, + { 0x4137, CHIP_FAMILY_RS200, 0, 1, 0, 0, 1 }, + { 0x4144, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4145, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4146, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4147, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4148, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x4149, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x414A, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x414B, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x4150, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4151, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4152, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4153, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4154, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4155, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4156, CHIP_FAMILY_RV350, 0, 0, 0, 0, 0 }, + { 0x4237, CHIP_FAMILY_RS200, 0, 1, 0, 0, 1 }, + { 0x4242, CHIP_FAMILY_R200, 0, 0, 0, 1, 0 }, + { 0x4336, CHIP_FAMILY_RS100, 1, 1, 0, 0, 1 }, + { 0x4337, CHIP_FAMILY_RS200, 1, 1, 0, 0, 1 }, + { 0x4437, CHIP_FAMILY_RS200, 1, 1, 0, 0, 1 }, + { 0x4966, CHIP_FAMILY_RV250, 0, 0, 0, 0, 0 }, + { 0x4967, CHIP_FAMILY_RV250, 0, 0, 0, 0, 0 }, + { 0x4A48, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A49, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A4A, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A4B, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A4C, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A4D, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A4E, CHIP_FAMILY_R420, 1, 0, 0, 0, 0 }, + { 0x4A4F, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A50, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4A54, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4B48, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4B49, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4B4A, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4B4B, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4B4C, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x4C57, CHIP_FAMILY_RV200, 1, 0, 0, 0, 0 }, + { 0x4C58, CHIP_FAMILY_RV200, 1, 0, 0, 0, 0 }, + { 0x4C59, CHIP_FAMILY_RV100, 1, 0, 0, 0, 0 }, + { 0x4C5A, CHIP_FAMILY_RV100, 1, 0, 0, 0, 0 }, + { 0x4C64, CHIP_FAMILY_RV250, 1, 0, 0, 0, 0 }, + { 0x4C66, CHIP_FAMILY_RV250, 1, 0, 0, 0, 0 }, + { 0x4C67, CHIP_FAMILY_RV250, 1, 0, 0, 0, 0 }, + { 0x4C6E, CHIP_FAMILY_RV280, 1, 0, 0, 0, 0 }, + { 0x4E44, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4E45, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4E46, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4E47, CHIP_FAMILY_R300, 0, 0, 0, 0, 0 }, + { 0x4E48, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x4E49, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x4E4A, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x4E4B, CHIP_FAMILY_R350, 0, 0, 0, 0, 0 }, + { 0x4E50, CHIP_FAMILY_RV350, 1, 0, 0, 0, 0 }, + { 0x4E51, CHIP_FAMILY_RV350, 1, 0, 0, 0, 0 }, + { 0x4E52, CHIP_FAMILY_RV350, 1, 0, 0, 0, 0 }, + { 0x4E53, CHIP_FAMILY_RV350, 1, 0, 0, 0, 0 }, + { 0x4E54, CHIP_FAMILY_RV350, 1, 0, 0, 0, 0 }, + { 0x4E56, CHIP_FAMILY_RV350, 1, 0, 0, 0, 0 }, + { 0x5144, CHIP_FAMILY_RADEON, 0, 0, 1, 1, 0 }, + { 0x5145, CHIP_FAMILY_RADEON, 0, 0, 1, 1, 0 }, + { 0x5146, CHIP_FAMILY_RADEON, 0, 0, 1, 1, 0 }, + { 0x5147, CHIP_FAMILY_RADEON, 0, 0, 1, 1, 0 }, + { 0x5148, CHIP_FAMILY_R200, 0, 0, 0, 1, 0 }, + { 0x514C, CHIP_FAMILY_R200, 0, 0, 0, 1, 0 }, + { 0x514D, CHIP_FAMILY_R200, 0, 0, 0, 1, 0 }, + { 0x5157, CHIP_FAMILY_RV200, 0, 0, 0, 0, 0 }, + { 0x5158, CHIP_FAMILY_RV200, 0, 0, 0, 0, 0 }, + { 0x5159, CHIP_FAMILY_RV100, 0, 0, 0, 0, 0 }, + { 0x515A, CHIP_FAMILY_RV100, 0, 0, 0, 0, 0 }, + { 0x515E, CHIP_FAMILY_RV100, 0, 0, 1, 0, 0 }, + { 0x5460, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x5462, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x5464, CHIP_FAMILY_RV380, 1, 0, 0, 0, 0 }, + { 0x5548, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5549, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x554A, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x554B, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x554C, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x554D, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x554E, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x554F, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5550, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5551, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5552, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5554, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x564A, CHIP_FAMILY_RV410, 1, 0, 0, 0, 0 }, + { 0x564B, CHIP_FAMILY_RV410, 1, 0, 0, 0, 0 }, + { 0x564F, CHIP_FAMILY_RV410, 1, 0, 0, 0, 0 }, + { 0x5652, CHIP_FAMILY_RV410, 1, 0, 0, 0, 0 }, + { 0x5653, CHIP_FAMILY_RV410, 1, 0, 0, 0, 0 }, + { 0x5657, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x5834, CHIP_FAMILY_RS300, 0, 1, 0, 0, 1 }, + { 0x5835, CHIP_FAMILY_RS300, 1, 1, 0, 0, 1 }, + { 0x5954, CHIP_FAMILY_RS480, 0, 1, 0, 0, 1 }, + { 0x5955, CHIP_FAMILY_RS480, 1, 1, 0, 0, 1 }, + { 0x5960, CHIP_FAMILY_RV280, 0, 0, 0, 0, 0 }, + { 0x5961, CHIP_FAMILY_RV280, 0, 0, 0, 0, 0 }, + { 0x5962, CHIP_FAMILY_RV280, 0, 0, 0, 0, 0 }, + { 0x5964, CHIP_FAMILY_RV280, 0, 0, 0, 0, 0 }, + { 0x5965, CHIP_FAMILY_RV280, 0, 0, 0, 0, 0 }, + { 0x5969, CHIP_FAMILY_RV100, 0, 0, 1, 0, 0 }, + { 0x5974, CHIP_FAMILY_RS480, 1, 1, 0, 0, 1 }, + { 0x5975, CHIP_FAMILY_RS480, 1, 1, 0, 0, 1 }, + { 0x5A41, CHIP_FAMILY_RS400, 0, 1, 0, 0, 1 }, + { 0x5A42, CHIP_FAMILY_RS400, 1, 1, 0, 0, 1 }, + { 0x5A61, CHIP_FAMILY_RS400, 0, 1, 0, 0, 1 }, + { 0x5A62, CHIP_FAMILY_RS400, 1, 1, 0, 0, 1 }, + { 0x5B60, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x5B62, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x5B63, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x5B64, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x5B65, CHIP_FAMILY_RV380, 0, 0, 0, 0, 0 }, + { 0x5C61, CHIP_FAMILY_RV280, 1, 0, 0, 0, 0 }, + { 0x5C63, CHIP_FAMILY_RV280, 1, 0, 0, 0, 0 }, + { 0x5D48, CHIP_FAMILY_R420, 1, 0, 0, 0, 0 }, + { 0x5D49, CHIP_FAMILY_R420, 1, 0, 0, 0, 0 }, + { 0x5D4A, CHIP_FAMILY_R420, 1, 0, 0, 0, 0 }, + { 0x5D4C, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5D4D, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5D4E, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5D4F, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5D50, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5D52, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5D57, CHIP_FAMILY_R420, 0, 0, 0, 0, 0 }, + { 0x5E48, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x5E4A, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x5E4B, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x5E4C, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x5E4D, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x5E4F, CHIP_FAMILY_RV410, 0, 0, 0, 0, 0 }, + { 0x7100, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x7101, CHIP_FAMILY_R520, 1, 0, 0, 0, 0 }, + { 0x7102, CHIP_FAMILY_R520, 1, 0, 0, 0, 0 }, + { 0x7103, CHIP_FAMILY_R520, 1, 0, 0, 0, 0 }, + { 0x7104, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x7105, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x7106, CHIP_FAMILY_R520, 1, 0, 0, 0, 0 }, + { 0x7108, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x7109, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x710A, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x710B, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x710C, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x710E, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x710F, CHIP_FAMILY_R520, 0, 0, 0, 0, 0 }, + { 0x7140, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7141, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7142, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7143, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7144, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x7145, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x7146, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7147, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7149, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x714A, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x714B, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x714C, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x714D, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x714E, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x714F, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7151, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7152, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7153, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x715E, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x715F, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7180, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7181, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7183, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7186, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x7187, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7188, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x718A, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x718B, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x718C, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x718D, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x718F, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7193, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7196, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x719B, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x719F, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x71C0, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71C1, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71C2, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71C3, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71C4, CHIP_FAMILY_RV530, 1, 0, 0, 0, 0 }, + { 0x71C5, CHIP_FAMILY_RV530, 1, 0, 0, 0, 0 }, + { 0x71C6, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71C7, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71CD, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71CE, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71D2, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71D4, CHIP_FAMILY_RV530, 1, 0, 0, 0, 0 }, + { 0x71D5, CHIP_FAMILY_RV530, 1, 0, 0, 0, 0 }, + { 0x71D6, CHIP_FAMILY_RV530, 1, 0, 0, 0, 0 }, + { 0x71DA, CHIP_FAMILY_RV530, 0, 0, 0, 0, 0 }, + { 0x71DE, CHIP_FAMILY_RV530, 1, 0, 0, 0, 0 }, + { 0x7200, CHIP_FAMILY_RV515, 0, 0, 0, 0, 0 }, + { 0x7210, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x7211, CHIP_FAMILY_RV515, 1, 0, 0, 0, 0 }, + { 0x7240, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7243, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7244, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7245, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7246, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7247, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7248, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7249, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x724A, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x724B, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x724C, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x724D, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x724E, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x724F, CHIP_FAMILY_R580, 0, 0, 0, 0, 0 }, + { 0x7280, CHIP_FAMILY_RV570, 0, 0, 0, 0, 0 }, + { 0x7281, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7283, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7284, CHIP_FAMILY_R580, 1, 0, 0, 0, 0 }, + { 0x7287, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7288, CHIP_FAMILY_RV570, 0, 0, 0, 0, 0 }, + { 0x7289, CHIP_FAMILY_RV570, 0, 0, 0, 0, 0 }, + { 0x728B, CHIP_FAMILY_RV570, 0, 0, 0, 0, 0 }, + { 0x728C, CHIP_FAMILY_RV570, 0, 0, 0, 0, 0 }, + { 0x7290, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7291, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7293, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7297, CHIP_FAMILY_RV560, 0, 0, 0, 0, 0 }, + { 0x7834, CHIP_FAMILY_RS300, 0, 1, 0, 0, 1 }, + { 0x7835, CHIP_FAMILY_RS300, 1, 1, 0, 0, 1 }, + { 0x791E, CHIP_FAMILY_RS690, 0, 1, 0, 0, 1 }, + { 0x791F, CHIP_FAMILY_RS690, 0, 1, 0, 0, 1 }, + { 0x793F, CHIP_FAMILY_RS600, 0, 1, 0, 0, 1 }, + { 0x7941, CHIP_FAMILY_RS600, 0, 1, 0, 0, 1 }, + { 0x7942, CHIP_FAMILY_RS600, 0, 1, 0, 0, 1 }, + { 0x796C, CHIP_FAMILY_RS740, 0, 1, 0, 0, 1 }, + { 0x796D, CHIP_FAMILY_RS740, 0, 1, 0, 0, 1 }, + { 0x796E, CHIP_FAMILY_RS740, 0, 1, 0, 0, 1 }, + { 0x796F, CHIP_FAMILY_RS740, 0, 1, 0, 0, 1 }, + { 0x9400, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x9401, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x9402, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x9403, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x9405, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x940A, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x940B, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x940F, CHIP_FAMILY_R600, 0, 0, 0, 0, 0 }, + { 0x9440, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9441, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9442, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9443, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9444, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9446, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x944A, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x944B, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x944C, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x944E, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9450, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9452, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9456, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x945A, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x945B, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x945E, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x9460, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x9462, CHIP_FAMILY_RV770, 0, 0, 0, 0, 0 }, + { 0x946A, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x946B, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x947A, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x947B, CHIP_FAMILY_RV770, 1, 0, 0, 0, 0 }, + { 0x9480, CHIP_FAMILY_RV730, 1, 0, 0, 0, 0 }, + { 0x9487, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x9488, CHIP_FAMILY_RV730, 1, 0, 0, 0, 0 }, + { 0x9489, CHIP_FAMILY_RV730, 1, 0, 0, 0, 0 }, + { 0x948A, CHIP_FAMILY_RV730, 1, 0, 0, 0, 0 }, + { 0x948F, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x9490, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x9491, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x9495, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x9498, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x949C, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x949E, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x949F, CHIP_FAMILY_RV730, 0, 0, 0, 0, 0 }, + { 0x94A0, CHIP_FAMILY_RV740, 1, 0, 0, 0, 0 }, + { 0x94A1, CHIP_FAMILY_RV740, 1, 0, 0, 0, 0 }, + { 0x94A3, CHIP_FAMILY_RV740, 1, 0, 0, 0, 0 }, + { 0x94B1, CHIP_FAMILY_RV740, 0, 0, 0, 0, 0 }, + { 0x94B3, CHIP_FAMILY_RV740, 0, 0, 0, 0, 0 }, + { 0x94B4, CHIP_FAMILY_RV740, 0, 0, 0, 0, 0 }, + { 0x94B5, CHIP_FAMILY_RV740, 0, 0, 0, 0, 0 }, + { 0x94B9, CHIP_FAMILY_RV740, 1, 0, 0, 0, 0 }, + { 0x94C0, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C1, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C3, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C4, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C5, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C6, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C7, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94C8, CHIP_FAMILY_RV610, 1, 0, 0, 0, 0 }, + { 0x94C9, CHIP_FAMILY_RV610, 1, 0, 0, 0, 0 }, + { 0x94CB, CHIP_FAMILY_RV610, 1, 0, 0, 0, 0 }, + { 0x94CC, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x94CD, CHIP_FAMILY_RV610, 0, 0, 0, 0, 0 }, + { 0x9500, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9501, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9504, CHIP_FAMILY_RV670, 1, 0, 0, 0, 0 }, + { 0x9505, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9506, CHIP_FAMILY_RV670, 1, 0, 0, 0, 0 }, + { 0x9507, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9508, CHIP_FAMILY_RV670, 1, 0, 0, 0, 0 }, + { 0x9509, CHIP_FAMILY_RV670, 1, 0, 0, 0, 0 }, + { 0x950F, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9511, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9515, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9517, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9519, CHIP_FAMILY_RV670, 0, 0, 0, 0, 0 }, + { 0x9540, CHIP_FAMILY_RV710, 0, 0, 0, 0, 0 }, + { 0x9541, CHIP_FAMILY_RV710, 0, 0, 0, 0, 0 }, + { 0x9542, CHIP_FAMILY_RV710, 0, 0, 0, 0, 0 }, + { 0x954E, CHIP_FAMILY_RV710, 0, 0, 0, 0, 0 }, + { 0x954F, CHIP_FAMILY_RV710, 0, 0, 0, 0, 0 }, + { 0x9552, CHIP_FAMILY_RV710, 1, 0, 0, 0, 0 }, + { 0x9553, CHIP_FAMILY_RV710, 1, 0, 0, 0, 0 }, + { 0x9555, CHIP_FAMILY_RV710, 1, 0, 0, 0, 0 }, + { 0x9557, CHIP_FAMILY_RV710, 1, 0, 0, 0, 0 }, + { 0x955F, CHIP_FAMILY_RV710, 1, 0, 0, 0, 0 }, + { 0x9580, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x9581, CHIP_FAMILY_RV630, 1, 0, 0, 0, 0 }, + { 0x9583, CHIP_FAMILY_RV630, 1, 0, 0, 0, 0 }, + { 0x9586, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x9587, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x9588, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x9589, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x958A, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x958B, CHIP_FAMILY_RV630, 1, 0, 0, 0, 0 }, + { 0x958C, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x958D, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x958E, CHIP_FAMILY_RV630, 0, 0, 0, 0, 0 }, + { 0x958F, CHIP_FAMILY_RV630, 1, 0, 0, 0, 0 }, + { 0x95C0, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95C2, CHIP_FAMILY_RV620, 1, 0, 0, 0, 0 }, + { 0x95C4, CHIP_FAMILY_RV620, 1, 0, 0, 0, 0 }, + { 0x95C5, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95C6, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95C7, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95C9, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95CC, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95CD, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95CE, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x95CF, CHIP_FAMILY_RV620, 0, 0, 0, 0, 0 }, + { 0x9590, CHIP_FAMILY_RV635, 0, 0, 0, 0, 0 }, + { 0x9596, CHIP_FAMILY_RV635, 0, 0, 0, 0, 0 }, + { 0x9597, CHIP_FAMILY_RV635, 0, 0, 0, 0, 0 }, + { 0x9598, CHIP_FAMILY_RV635, 0, 0, 0, 0, 0 }, + { 0x9599, CHIP_FAMILY_RV635, 0, 0, 0, 0, 0 }, + { 0x9591, CHIP_FAMILY_RV635, 1, 0, 0, 0, 0 }, + { 0x9593, CHIP_FAMILY_RV635, 1, 0, 0, 0, 0 }, + { 0x9595, CHIP_FAMILY_RV635, 1, 0, 0, 0, 0 }, + { 0x959B, CHIP_FAMILY_RV635, 1, 0, 0, 0, 0 }, + { 0x9610, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9611, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9612, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9613, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9614, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9615, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9616, CHIP_FAMILY_RS780, 0, 1, 0, 0, 1 }, + { 0x9640, CHIP_FAMILY_SUMO, 0, 1, 0, 0, 1 }, + { 0x9641, CHIP_FAMILY_SUMO, 1, 1, 0, 0, 1 }, + { 0x9642, CHIP_FAMILY_SUMO2, 0, 1, 0, 0, 1 }, + { 0x9643, CHIP_FAMILY_SUMO2, 1, 1, 0, 0, 1 }, + { 0x9644, CHIP_FAMILY_SUMO2, 0, 1, 0, 0, 1 }, + { 0x9645, CHIP_FAMILY_SUMO2, 1, 1, 0, 0, 1 }, + { 0x9647, CHIP_FAMILY_SUMO, 1, 1, 0, 0, 1 }, + { 0x9648, CHIP_FAMILY_SUMO, 1, 1, 0, 0, 1 }, + { 0x9649, CHIP_FAMILY_SUMO, 1, 1, 0, 0, 1 }, + { 0x964A, CHIP_FAMILY_SUMO, 0, 1, 0, 0, 1 }, + { 0x964B, CHIP_FAMILY_SUMO, 0, 1, 0, 0, 1 }, + { 0x964C, CHIP_FAMILY_SUMO, 0, 1, 0, 0, 1 }, + { 0x964E, CHIP_FAMILY_SUMO, 1, 1, 0, 0, 1 }, + { 0x964F, CHIP_FAMILY_SUMO, 1, 1, 0, 0, 1 }, + { 0x9710, CHIP_FAMILY_RS880, 0, 1, 0, 0, 1 }, + { 0x9711, CHIP_FAMILY_RS880, 0, 1, 0, 0, 1 }, + { 0x9712, CHIP_FAMILY_RS880, 1, 1, 0, 0, 1 }, + { 0x9713, CHIP_FAMILY_RS880, 1, 1, 0, 0, 1 }, + { 0x9714, CHIP_FAMILY_RS880, 0, 1, 0, 0, 1 }, + { 0x9715, CHIP_FAMILY_RS880, 0, 1, 0, 0, 1 }, + { 0x9802, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9803, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9804, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9805, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9806, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9807, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9808, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x9809, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x980A, CHIP_FAMILY_PALM, 0, 1, 0, 0, 1 }, + { 0x6880, CHIP_FAMILY_CYPRESS, 1, 0, 0, 0, 0 }, + { 0x6888, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x6889, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x688A, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x688C, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x688D, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x6898, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x6899, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x689B, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x689E, CHIP_FAMILY_CYPRESS, 0, 0, 0, 0, 0 }, + { 0x689C, CHIP_FAMILY_HEMLOCK, 0, 0, 0, 0, 0 }, + { 0x689D, CHIP_FAMILY_HEMLOCK, 0, 0, 0, 0, 0 }, + { 0x68A0, CHIP_FAMILY_JUNIPER, 1, 0, 0, 0, 0 }, + { 0x68A1, CHIP_FAMILY_JUNIPER, 1, 0, 0, 0, 0 }, + { 0x68A8, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68A9, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68B0, CHIP_FAMILY_JUNIPER, 1, 0, 0, 0, 0 }, + { 0x68B8, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68B9, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68BA, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68BE, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68BF, CHIP_FAMILY_JUNIPER, 0, 0, 0, 0, 0 }, + { 0x68C0, CHIP_FAMILY_REDWOOD, 1, 0, 0, 0, 0 }, + { 0x68C1, CHIP_FAMILY_REDWOOD, 1, 0, 0, 0, 0 }, + { 0x68C7, CHIP_FAMILY_REDWOOD, 1, 0, 0, 0, 0 }, + { 0x68C8, CHIP_FAMILY_REDWOOD, 0, 0, 0, 0, 0 }, + { 0x68C9, CHIP_FAMILY_REDWOOD, 0, 0, 0, 0, 0 }, + { 0x68D8, CHIP_FAMILY_REDWOOD, 0, 0, 0, 0, 0 }, + { 0x68D9, CHIP_FAMILY_REDWOOD, 0, 0, 0, 0, 0 }, + { 0x68DA, CHIP_FAMILY_REDWOOD, 0, 0, 0, 0, 0 }, + { 0x68DE, CHIP_FAMILY_REDWOOD, 0, 0, 0, 0, 0 }, + { 0x68E0, CHIP_FAMILY_CEDAR, 1, 0, 0, 0, 0 }, + { 0x68E1, CHIP_FAMILY_CEDAR, 1, 0, 0, 0, 0 }, + { 0x68E4, CHIP_FAMILY_CEDAR, 1, 0, 0, 0, 0 }, + { 0x68E5, CHIP_FAMILY_CEDAR, 1, 0, 0, 0, 0 }, + { 0x68E8, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68E9, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68F1, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68F2, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68F8, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68F9, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68FA, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x68FE, CHIP_FAMILY_CEDAR, 0, 0, 0, 0, 0 }, + { 0x6700, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6701, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6702, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6703, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6704, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6705, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6706, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6707, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6708, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6709, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6718, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6719, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x671C, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x671D, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x671F, CHIP_FAMILY_CAYMAN, 0, 0, 0, 0, 0 }, + { 0x6720, CHIP_FAMILY_BARTS, 1, 0, 0, 0, 0 }, + { 0x6721, CHIP_FAMILY_BARTS, 1, 0, 0, 0, 0 }, + { 0x6722, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6723, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6724, CHIP_FAMILY_BARTS, 1, 0, 0, 0, 0 }, + { 0x6725, CHIP_FAMILY_BARTS, 1, 0, 0, 0, 0 }, + { 0x6726, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6727, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6728, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6729, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6738, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6739, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x673E, CHIP_FAMILY_BARTS, 0, 0, 0, 0, 0 }, + { 0x6740, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6741, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6742, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6743, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6744, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6745, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6746, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6747, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6748, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6749, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x674A, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6750, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6751, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6758, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6759, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x675B, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x675D, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x675F, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6840, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6841, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6842, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6843, CHIP_FAMILY_TURKS, 1, 0, 0, 0, 0 }, + { 0x6849, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6850, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6858, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6859, CHIP_FAMILY_TURKS, 0, 0, 0, 0, 0 }, + { 0x6760, CHIP_FAMILY_CAICOS, 1, 0, 0, 0, 0 }, + { 0x6761, CHIP_FAMILY_CAICOS, 1, 0, 0, 0, 0 }, + { 0x6762, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6763, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6764, CHIP_FAMILY_CAICOS, 1, 0, 0, 0, 0 }, + { 0x6765, CHIP_FAMILY_CAICOS, 1, 0, 0, 0, 0 }, + { 0x6766, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6767, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6768, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6770, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6771, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6772, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6778, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x6779, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x677B, CHIP_FAMILY_CAICOS, 0, 0, 0, 0, 0 }, + { 0x9900, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9901, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9903, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9904, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9905, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9906, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9907, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9908, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9909, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x990A, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x990B, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x990C, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x990D, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x990E, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x990F, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9910, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9913, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9917, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9918, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9919, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9990, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9991, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9992, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9993, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9994, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9995, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9996, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9997, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x9998, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x9999, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x999A, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x999B, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x999C, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x999D, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x99A0, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x99A2, CHIP_FAMILY_ARUBA, 1, 1, 0, 0, 0 }, + { 0x99A4, CHIP_FAMILY_ARUBA, 0, 1, 0, 0, 0 }, + { 0x6780, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6784, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6788, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x678A, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6790, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6791, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6792, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6798, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6799, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x679A, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x679B, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x679E, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x679F, CHIP_FAMILY_TAHITI, 0, 0, 0, 0, 0 }, + { 0x6800, CHIP_FAMILY_PITCAIRN, 1, 0, 0, 0, 0 }, + { 0x6801, CHIP_FAMILY_PITCAIRN, 1, 0, 0, 0, 0 }, + { 0x6802, CHIP_FAMILY_PITCAIRN, 1, 0, 0, 0, 0 }, + { 0x6806, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6808, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6809, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6810, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6811, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6816, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6817, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6818, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6819, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x684C, CHIP_FAMILY_PITCAIRN, 0, 0, 0, 0, 0 }, + { 0x6820, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6821, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6822, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6823, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6824, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6825, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6826, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6827, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6828, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x6829, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x682A, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x682B, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x682D, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x682F, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6830, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6831, CHIP_FAMILY_VERDE, 1, 0, 0, 0, 0 }, + { 0x6835, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x6837, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x6838, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x6839, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x683B, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x683D, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x683F, CHIP_FAMILY_VERDE, 0, 0, 0, 0, 0 }, + { 0x6600, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6601, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6602, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6603, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6606, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6607, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6610, CHIP_FAMILY_OLAND, 0, 0, 0, 0, 0 }, + { 0x6611, CHIP_FAMILY_OLAND, 0, 0, 0, 0, 0 }, + { 0x6613, CHIP_FAMILY_OLAND, 0, 0, 0, 0, 0 }, + { 0x6620, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6621, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6623, CHIP_FAMILY_OLAND, 1, 0, 0, 0, 0 }, + { 0x6631, CHIP_FAMILY_OLAND, 0, 0, 0, 0, 0 }, + { 0x6660, CHIP_FAMILY_HAINAN, 1, 0, 0, 0, 0 }, + { 0x6663, CHIP_FAMILY_HAINAN, 1, 0, 0, 0, 0 }, + { 0x6664, CHIP_FAMILY_HAINAN, 1, 0, 0, 0, 0 }, + { 0x6665, CHIP_FAMILY_HAINAN, 1, 0, 0, 0, 0 }, + { 0x6667, CHIP_FAMILY_HAINAN, 1, 0, 0, 0, 0 }, + { 0x666F, CHIP_FAMILY_HAINAN, 1, 0, 0, 0, 0 }, + { 0x6640, CHIP_FAMILY_BONAIRE, 1, 0, 0, 0, 0 }, + { 0x6641, CHIP_FAMILY_BONAIRE, 1, 0, 0, 0, 0 }, + { 0x6649, CHIP_FAMILY_BONAIRE, 0, 0, 0, 0, 0 }, + { 0x6650, CHIP_FAMILY_BONAIRE, 0, 0, 0, 0, 0 }, + { 0x6651, CHIP_FAMILY_BONAIRE, 0, 0, 0, 0, 0 }, + { 0x6658, CHIP_FAMILY_BONAIRE, 0, 0, 0, 0, 0 }, + { 0x665C, CHIP_FAMILY_BONAIRE, 0, 0, 0, 0, 0 }, + { 0x665D, CHIP_FAMILY_BONAIRE, 0, 0, 0, 0, 0 }, + { 0x9830, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x9831, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x9832, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x9833, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x9834, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x9835, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x9836, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x9837, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x9838, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x9839, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x983A, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x983B, CHIP_FAMILY_KABINI, 1, 1, 0, 0, 1 }, + { 0x983C, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x983D, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x983E, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, + { 0x983F, CHIP_FAMILY_KABINI, 0, 1, 0, 0, 1 }, +}; |