summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeoff Mendal <mendal@google.com>2015-01-20 13:59:44 -0800
committerGeoff Mendal <mendal@google.com>2015-01-20 13:59:44 -0800
commit96d72b6dffd58043770b3d7d9e2e18171be4b650 (patch)
tree20b8afc9bd585d8e3f0b1bca9a5207a10501d2b7
parent69f4e60d17eb1e1f83fc71e9c061c6be4de791c7 (diff)
parent68a74eb31f139ad285a49451cf2ca8271c54f9bb (diff)
downloaddrm_gralloc-96d72b6dffd58043770b3d7d9e2e18171be4b650.tar.gz
Merge branch 'lollipop-x86' of http://git.android-x86.org/platform/hardware/drm_gralloc
Bug: 18959563
-rw-r--r--Android.mk175
-rw-r--r--gralloc.c361
-rw-r--r--gralloc_drm.c437
-rw-r--r--gralloc_drm.h150
-rw-r--r--gralloc_drm_formats.h39
-rw-r--r--gralloc_drm_handle.h75
-rw-r--r--gralloc_drm_intel.c676
-rw-r--r--gralloc_drm_kms.c1258
-rw-r--r--gralloc_drm_nouveau.c384
-rw-r--r--gralloc_drm_pipe.c563
-rw-r--r--gralloc_drm_priv.h207
-rw-r--r--gralloc_drm_radeon.c554
-rw-r--r--pci_ids/pci_id_driver_map.h84
-rw-r--r--radeon/radeon.h117
-rw-r--r--radeon/radeon_chipinfo_gen.h645
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 },
+};