summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am74
-rw-r--r--src/amdgpu_bo_helper.c326
-rw-r--r--src/amdgpu_bo_helper.h103
-rw-r--r--src/amdgpu_chipinfo_gen.h76
-rw-r--r--src/amdgpu_chipset_gen.h77
-rw-r--r--src/amdgpu_dri2.c1583
-rw-r--r--src/amdgpu_dri2.h96
-rw-r--r--src/amdgpu_drv.h244
-rw-r--r--src/amdgpu_glamor.c349
-rw-r--r--src/amdgpu_glamor.h46
-rw-r--r--src/amdgpu_kms.c1100
-rw-r--r--src/amdgpu_misc.c70
-rw-r--r--src/amdgpu_pci_chipset_gen.h77
-rw-r--r--src/amdgpu_pci_device_match_gen.h77
-rw-r--r--src/amdgpu_pixmap.c115
-rw-r--r--src/amdgpu_pixmap.h110
-rw-r--r--src/amdgpu_probe.c374
-rw-r--r--src/amdgpu_probe.h97
-rw-r--r--src/amdgpu_version.h61
-rw-r--r--src/amdgpu_video.c177
-rw-r--r--src/amdgpu_video.h12
-rw-r--r--src/amdpciids.h39
-rw-r--r--src/ati_pciids_gen.h73
-rw-r--r--src/compat-api.h94
-rw-r--r--src/drmmode_display.c1855
-rw-r--r--src/drmmode_display.h125
-rw-r--r--src/pcidb/ati_pciids.csv74
-rwxr-xr-xsrc/pcidb/parse_pci_ids.pl70
-rw-r--r--src/simple_list.h201
29 files changed, 7775 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..817da2d
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,74 @@
+# Copyright 2005 Adam Jackson.
+# Copyright 2005 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
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, 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
+# ADAM JACKSON 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.
+
+
+# this is obnoxious:
+# -module lets us name the module exactly how we want
+# -avoid-version prevents gratuitous .0.0.0 version numbers on the end
+# _ladir passes a dummy rpath to libtool so the thing will actually link
+# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
+
+amdgpu_drv_la_LIBADD = $(PCIACCESS_LIBS) $(LIBDRM_AMDGPU_LIBS)
+
+AMDGPU_KMS_SRCS=amdgpu_dri2.c amdgpu_kms.c drmmode_display.c amdgpu_bo_helper.c
+
+AM_CFLAGS = \
+ @LIBDRM_AMDGPU_CFLAGS@ \
+ @XORG_CFLAGS@ \
+ @LIBUDEV_CFLAGS@
+
+if LIBUDEV
+amdgpu_drv_la_LIBADD += $(LIBUDEV_LIBS)
+endif
+
+amdgpu_drv_la_LTLIBRARIES = amdgpu_drv.la
+amdgpu_drv_la_LDFLAGS = -module -avoid-version
+amdgpu_drv_ladir = @moduledir@/drivers
+amdgpu_drv_la_SOURCES = \
+ amdgpu_video.c \
+ amdgpu_misc.c amdgpu_probe.c \
+ $(AMDGPU_KMS_SRCS)
+
+AM_CFLAGS += @LIBGLAMOR_CFLAGS@
+amdgpu_drv_la_LIBADD += @LIBGLAMOR_LIBS@
+amdgpu_drv_la_SOURCES += \
+ amdgpu_glamor.c \
+ amdgpu_pixmap.c
+
+EXTRA_DIST = \
+ compat-api.h \
+ amdgpu_bo_helper.h \
+ amdgpu_glamor.h \
+ amdgpu_drv.h \
+ amdgpu_probe.h \
+ amdgpu_version.h \
+ amdgpu_video.h \
+ simple_list.h \
+ amdpciids.h \
+ ati_pciids_gen.h \
+ amdgpu_chipinfo_gen.h \
+ amdgpu_chipset_gen.h \
+ amdgpu_pci_chipset_gen.h \
+ amdgpu_pci_device_match_gen.h \
+ pcidb/ati_pciids.csv \
+ pcidb/parse_pci_ids.pl \
+ amdgpu_dri2.h \
+ drmmode_display.h
diff --git a/src/amdgpu_bo_helper.c b/src/amdgpu_bo_helper.c
new file mode 100644
index 0000000..10f5856
--- /dev/null
+++ b/src/amdgpu_bo_helper.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/mman.h>
+#include <gbm.h>
+#include "amdgpu_drv.h"
+#include "amdgpu_bo_helper.h"
+#include "amdgpu_pixmap.h"
+
+/* Calculate appropriate pitch for a pixmap and allocate a BO that can hold it.
+ */
+struct amdgpu_buffer *amdgpu_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width,
+ int height, int depth, int usage_hint,
+ int bitsPerPixel, int *new_pitch)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ struct amdgpu_buffer *pixmap_buffer;
+
+ if (info->gbm) {
+ uint32_t bo_use = GBM_BO_USE_RENDERING;
+ uint32_t gbm_format;
+
+ switch (depth) {
+#ifdef GBM_FORMAT_R8
+ case 8:
+ gbm_format = GBM_FORMAT_R8;
+ break;
+#endif
+ case 16:
+ gbm_format = GBM_FORMAT_RGB565;
+ break;
+ case 32:
+ gbm_format = GBM_FORMAT_ARGB8888;
+ break;
+ case 24:
+ if (bitsPerPixel == 32) {
+ gbm_format = GBM_FORMAT_XRGB8888;
+ break;
+ }
+ /* fall through */
+ default:
+ ErrorF("%s: Unsupported depth/bpp %d/%d\n", __func__,
+ depth, bitsPerPixel);
+ return NULL;
+ }
+
+ pixmap_buffer = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
+ if (!pixmap_buffer) {
+ return NULL;
+ }
+ pixmap_buffer->ref_count = 1;
+
+ if ( bitsPerPixel == pScrn->bitsPerPixel)
+ bo_use |= GBM_BO_USE_SCANOUT;
+
+#ifdef CREATE_PIXMAP_USAGE_SHARED
+ if (usage_hint == CREATE_PIXMAP_USAGE_SHARED) {
+ /* XXX: Need to tell GBM to disable tiling in this case */
+ }
+#endif
+
+ if (usage_hint & AMDGPU_CREATE_PIXMAP_LINEAR) {
+ bo_use |= GBM_BO_USE_LINEAR;
+ }
+
+ pixmap_buffer->bo.gbm = gbm_bo_create(info->gbm, width, height,
+ gbm_format,
+ bo_use);
+ if (!pixmap_buffer->bo.gbm) {
+ free(pixmap_buffer);
+ return NULL;
+ }
+
+ pixmap_buffer->flags |= AMDGPU_BO_FLAGS_GBM;
+
+ if (new_pitch)
+ *new_pitch = gbm_bo_get_stride(pixmap_buffer->bo.gbm);
+ } else {
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ unsigned cpp = (bitsPerPixel + 7) / 8;
+ unsigned pitch = cpp *
+ AMDGPU_ALIGN(width, drmmode_get_pitch_align(pScrn, cpp));
+
+ pixmap_buffer = amdgpu_bo_open(pAMDGPUEnt->pDev, pitch * height,
+ 4096, AMDGPU_GEM_DOMAIN_VRAM);
+
+ if (new_pitch)
+ *new_pitch = pitch;
+ }
+
+ return pixmap_buffer;
+}
+
+int amdgpu_bo_map(ScrnInfoPtr pScrn, struct amdgpu_buffer *bo)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ int ret = 0;
+
+ if (info->use_glamor)
+ return 0;
+
+ if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
+ uint32_t handle, stride, height;
+ union drm_amdgpu_gem_mmap args;
+ int fd;
+ void *ptr;
+
+ handle = gbm_bo_get_handle(bo->bo.gbm).u32;
+ stride = gbm_bo_get_stride(bo->bo.gbm);
+ height = gbm_bo_get_height(bo->bo.gbm);
+ fd = info->dri2.drm_fd;
+
+ memset(&args, 0, sizeof(union drm_amdgpu_gem_mmap));
+ args.in.handle = handle;
+
+ ret = drmCommandWriteRead(fd, DRM_AMDGPU_GEM_MMAP,
+ &args, sizeof(args));
+ if (ret) {
+ ErrorF("Failed to get the mmap offset\n");
+ return ret;
+ }
+
+ ptr = mmap(NULL, stride * height,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, args.out.addr_ptr);
+
+ if (ptr == NULL) {
+ ErrorF("Failed to mmap the bo\n");
+ return -1;
+ }
+
+ bo->cpu_ptr = ptr;
+ } else
+ ret = amdgpu_bo_cpu_map(bo->bo.amdgpu, &bo->cpu_ptr);
+
+ return ret;
+}
+
+void amdgpu_bo_unmap(struct amdgpu_buffer *bo)
+{
+ if (bo->cpu_ptr == NULL)
+ return;
+
+ if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
+ uint32_t stride, height;
+ stride = gbm_bo_get_stride(bo->bo.gbm);
+ height = gbm_bo_get_height(bo->bo.gbm);
+ munmap(bo->cpu_ptr, stride * height);
+ } else
+ amdgpu_bo_cpu_unmap(bo->bo.amdgpu);
+}
+
+struct amdgpu_buffer *amdgpu_bo_open(amdgpu_device_handle pDev,
+ uint32_t alloc_size,
+ uint32_t phys_alignment,
+ uint32_t domains)
+{
+ struct amdgpu_bo_alloc_request alloc_request;
+ struct amdgpu_bo_alloc_result buffer;
+ struct amdgpu_buffer *bo = NULL;
+
+ memset(&alloc_request, 0, sizeof(struct amdgpu_bo_alloc_request));
+ memset(&buffer, 0, sizeof(struct amdgpu_bo_alloc_result));
+
+ bo = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
+ if (bo == NULL) {
+ return NULL;
+ }
+
+ alloc_request.alloc_size = alloc_size;
+ alloc_request.phys_alignment = phys_alignment;
+ alloc_request.preferred_heap = domains;
+
+ if (amdgpu_bo_alloc(pDev, &alloc_request, &buffer)) {
+ free(bo);
+ return NULL;
+ }
+
+ bo->bo.amdgpu = buffer.buf_handle;
+ bo->ref_count = 1;
+
+ return bo;
+}
+
+void amdgpu_bo_ref(struct amdgpu_buffer *buffer)
+{
+ buffer->ref_count++;
+}
+
+void amdgpu_bo_unref(struct amdgpu_buffer **buffer)
+{
+ struct amdgpu_buffer *buf = *buffer;
+
+ buf->ref_count--;
+ if (buf->ref_count) {
+ return;
+ }
+
+ amdgpu_bo_unmap(buf);
+
+ if (buf->flags & AMDGPU_BO_FLAGS_GBM) {
+ gbm_bo_destroy(buf->bo.gbm);
+ } else {
+ amdgpu_bo_free(buf->bo.amdgpu);
+ }
+ free(buf);
+ *buffer = NULL;
+}
+
+int amdgpu_query_bo_size(amdgpu_bo_handle buf_handle, uint32_t *size)
+{
+ struct amdgpu_bo_info buffer_info;
+ memset(&buffer_info, 0, sizeof(struct amdgpu_bo_info));
+ int ret;
+
+ ret = amdgpu_bo_query_info(buf_handle, &buffer_info);
+ if (ret)
+ *size = 0;
+ else
+ *size = (uint32_t)(buffer_info.alloc_size);
+
+ return ret;
+}
+
+int amdgpu_query_heap_size(amdgpu_device_handle pDev,
+ uint32_t heap,
+ uint64_t *heap_size,
+ uint64_t *max_allocation)
+{
+ struct amdgpu_heap_info heap_info;
+ memset(&heap_info, 0, sizeof(struct amdgpu_heap_info));
+ int ret;
+
+ ret = amdgpu_query_heap_info(pDev, heap, 0, &heap_info);
+ if (ret) {
+ *heap_size = 0;
+ *max_allocation = 0;
+ } else {
+ *heap_size = heap_info.heap_size;
+ *max_allocation = heap_info.max_allocation;
+ }
+
+ return ret;
+}
+
+struct amdgpu_buffer *amdgpu_gem_bo_open_prime(amdgpu_device_handle pDev,
+ int fd_handle,
+ uint32_t size)
+{
+ struct amdgpu_buffer *bo = NULL;
+ struct amdgpu_bo_import_result buffer = {0};
+
+ bo = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
+ if (bo == NULL) {
+ return NULL;
+ }
+
+ if (amdgpu_bo_import(pDev, amdgpu_bo_handle_type_dma_buf_fd,
+ (uint32_t)fd_handle, &buffer)) {
+ free(bo);
+ return FALSE;
+ }
+ bo->bo.amdgpu = buffer.buf_handle;
+ bo->ref_count = 1;
+
+ return bo;
+}
+
+#ifdef AMDGPU_PIXMAP_SHARING
+
+Bool amdgpu_share_pixmap_backing(struct amdgpu_buffer *bo, void **handle_p)
+{
+ int handle;
+
+ amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_dma_buf_fd,
+ (uint32_t *)&handle);
+
+ *handle_p = (void *)(long)handle;
+ return TRUE;
+}
+
+Bool amdgpu_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ struct amdgpu_buffer *pixmap_buffer = NULL;
+ int ihandle = (int)(long)fd_handle;
+ uint32_t size = ppix->devKind * ppix->drawable.height;
+
+ pixmap_buffer = amdgpu_gem_bo_open_prime(pAMDGPUEnt->pDev, ihandle, size);
+ if (!pixmap_buffer) {
+ return FALSE;
+ }
+
+ amdgpu_set_pixmap_bo(ppix, pixmap_buffer);
+
+ close(ihandle);
+ /* we have a reference from the alloc and one from set pixmap bo,
+ drop one */
+ amdgpu_bo_unref(&pixmap_buffer);
+ return TRUE;
+}
+
+#endif /* AMDGPU_PIXMAP_SHARING */
diff --git a/src/amdgpu_bo_helper.h b/src/amdgpu_bo_helper.h
new file mode 100644
index 0000000..8270d39
--- /dev/null
+++ b/src/amdgpu_bo_helper.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 AMDGPU_BO_HELPER_H
+#define AMDGPU_BO_HELPER_H 1
+
+#include "amdgpu_drv.h"
+
+extern struct amdgpu_buffer *amdgpu_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width,
+ int height, int depth, int usage_hint,
+ int bitsPerPixel, int *new_pitch);
+
+extern int amdgpu_bo_map(ScrnInfoPtr pScrn, struct amdgpu_buffer *bo);
+
+extern void amdgpu_bo_unmap(struct amdgpu_buffer *bo);
+
+extern Bool amdgpu_share_pixmap_backing(struct amdgpu_buffer *bo, void **handle_p);
+
+extern Bool
+amdgpu_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle);
+
+/* helper function to allocate memory to be used for GPU operations
+ *
+ * \param pDev - \c [in] device handle
+ * \param alloc_size - \c [in] allocation size
+ * \param phys_alignment - \c [in] requested alignment. 0 means no alignment requirement
+ * \param domains - \c [in] GEM domains
+ *
+ * \return pointer to amdgpu_buffer on success
+ * NULL on failure
+*/
+extern struct amdgpu_buffer *amdgpu_bo_open(amdgpu_device_handle pDev,
+ uint32_t alloc_size,
+ uint32_t phys_alignment,
+ uint32_t domains);
+
+/* helper function to add the ref_count of a amdgpu_buffer
+ * \param buffer - \c [in] amdgpu_buffer
+*/
+extern void amdgpu_bo_ref(struct amdgpu_buffer *buffer);
+
+/* helper function to dec the ref_count of a amdgpu_buffer
+ * \param buffer - \c [in] amdgpu_buffer
+*/
+extern void amdgpu_bo_unref(struct amdgpu_buffer **buffer);
+
+/* helper function to query the buffer size
+ * \param buf_handle - \c [in] amdgpu bo handle
+ * \param size - \c [out] pointer to buffer size
+ *
+ * \return 0 on success
+ >0 - AMD specific error code \n
+ <0 - Negative POSIX error code
+*/
+int amdgpu_query_bo_size(amdgpu_bo_handle buf_handle, uint32_t *size);
+
+/* helper function to query the heap information
+ * \param pDev - \c [in] amdgpu device handle
+ * \param heap - \c [in] heap type
+ * \param heap_size - \c [out] theoretical max available memory
+ * \param max_allcoation - \c [out] theoretical possible max. size of buffer
+ *
+ * \return 0 on success
+ >0 - AMD specific error code \n
+ <0 - Negative POSIX error code
+*/
+int amdgpu_query_heap_size(amdgpu_device_handle pDev,
+ uint32_t heap,
+ uint64_t *heap_size,
+ uint64_t *max_allocation);
+
+/* helper function to convert a DMA buf handle to a KMS handle
+ * \param pDev - \c [in] amdgpu device handle
+ * \param fd_handle - \c [in] dma-buf fd handle
+ * \size size - \c [in] buffer size
+ *
+ * \return pointer to amdgpu_buffer on success
+ NULL on failure
+*/
+struct amdgpu_buffer *amdgpu_gem_bo_open_prime(amdgpu_device_handle pDev,
+ int fd_handle,
+ uint32_t size);
+
+#endif /* AMDGPU_BO_HELPER_H */
diff --git a/src/amdgpu_chipinfo_gen.h b/src/amdgpu_chipinfo_gen.h
new file mode 100644
index 0000000..ca28a98
--- /dev/null
+++ b/src/amdgpu_chipinfo_gen.h
@@ -0,0 +1,76 @@
+/* This file is autogenerated please do not edit */
+static AMDGPUCardInfo AMDGPUCards[] = {
+ { 0x6640, CHIP_FAMILY_BONAIRE },
+ { 0x6641, CHIP_FAMILY_BONAIRE },
+ { 0x6649, CHIP_FAMILY_BONAIRE },
+ { 0x6650, CHIP_FAMILY_BONAIRE },
+ { 0x6651, CHIP_FAMILY_BONAIRE },
+ { 0x6658, CHIP_FAMILY_BONAIRE },
+ { 0x665C, CHIP_FAMILY_BONAIRE },
+ { 0x665D, CHIP_FAMILY_BONAIRE },
+ { 0x9830, CHIP_FAMILY_KABINI },
+ { 0x9831, CHIP_FAMILY_KABINI },
+ { 0x9832, CHIP_FAMILY_KABINI },
+ { 0x9833, CHIP_FAMILY_KABINI },
+ { 0x9834, CHIP_FAMILY_KABINI },
+ { 0x9835, CHIP_FAMILY_KABINI },
+ { 0x9836, CHIP_FAMILY_KABINI },
+ { 0x9837, CHIP_FAMILY_KABINI },
+ { 0x9838, CHIP_FAMILY_KABINI },
+ { 0x9839, CHIP_FAMILY_KABINI },
+ { 0x983A, CHIP_FAMILY_KABINI },
+ { 0x983B, CHIP_FAMILY_KABINI },
+ { 0x983C, CHIP_FAMILY_KABINI },
+ { 0x983D, CHIP_FAMILY_KABINI },
+ { 0x983E, CHIP_FAMILY_KABINI },
+ { 0x983F, CHIP_FAMILY_KABINI },
+ { 0x1304, CHIP_FAMILY_KAVERI },
+ { 0x1305, CHIP_FAMILY_KAVERI },
+ { 0x1306, CHIP_FAMILY_KAVERI },
+ { 0x1307, CHIP_FAMILY_KAVERI },
+ { 0x1309, CHIP_FAMILY_KAVERI },
+ { 0x130A, CHIP_FAMILY_KAVERI },
+ { 0x130B, CHIP_FAMILY_KAVERI },
+ { 0x130C, CHIP_FAMILY_KAVERI },
+ { 0x130D, CHIP_FAMILY_KAVERI },
+ { 0x130E, CHIP_FAMILY_KAVERI },
+ { 0x130F, CHIP_FAMILY_KAVERI },
+ { 0x1310, CHIP_FAMILY_KAVERI },
+ { 0x1311, CHIP_FAMILY_KAVERI },
+ { 0x1312, CHIP_FAMILY_KAVERI },
+ { 0x1313, CHIP_FAMILY_KAVERI },
+ { 0x1315, CHIP_FAMILY_KAVERI },
+ { 0x1316, CHIP_FAMILY_KAVERI },
+ { 0x1317, CHIP_FAMILY_KAVERI },
+ { 0x131B, CHIP_FAMILY_KAVERI },
+ { 0x131C, CHIP_FAMILY_KAVERI },
+ { 0x131D, CHIP_FAMILY_KAVERI },
+ { 0x67A0, CHIP_FAMILY_HAWAII },
+ { 0x67A1, CHIP_FAMILY_HAWAII },
+ { 0x67A2, CHIP_FAMILY_HAWAII },
+ { 0x67A8, CHIP_FAMILY_HAWAII },
+ { 0x67A9, CHIP_FAMILY_HAWAII },
+ { 0x67AA, CHIP_FAMILY_HAWAII },
+ { 0x67B0, CHIP_FAMILY_HAWAII },
+ { 0x67B1, CHIP_FAMILY_HAWAII },
+ { 0x67B8, CHIP_FAMILY_HAWAII },
+ { 0x67B9, CHIP_FAMILY_HAWAII },
+ { 0x67BA, CHIP_FAMILY_HAWAII },
+ { 0x67BE, CHIP_FAMILY_HAWAII },
+ { 0x6900, CHIP_FAMILY_TOPAZ },
+ { 0x6901, CHIP_FAMILY_TOPAZ },
+ { 0x6902, CHIP_FAMILY_TOPAZ },
+ { 0x6903, CHIP_FAMILY_TOPAZ },
+ { 0x6907, CHIP_FAMILY_TOPAZ },
+ { 0x6920, CHIP_FAMILY_TONGA },
+ { 0x6921, CHIP_FAMILY_TONGA },
+ { 0x6938, CHIP_FAMILY_TONGA },
+ { 0x6939, CHIP_FAMILY_TONGA },
+ { 0x692B, CHIP_FAMILY_TONGA },
+ { 0x692F, CHIP_FAMILY_TONGA },
+ { 0x9870, CHIP_FAMILY_CARRIZO },
+ { 0x9874, CHIP_FAMILY_CARRIZO },
+ { 0x9875, CHIP_FAMILY_CARRIZO },
+ { 0x9876, CHIP_FAMILY_CARRIZO },
+ { 0x9877, CHIP_FAMILY_CARRIZO },
+};
diff --git a/src/amdgpu_chipset_gen.h b/src/amdgpu_chipset_gen.h
new file mode 100644
index 0000000..258ee22
--- /dev/null
+++ b/src/amdgpu_chipset_gen.h
@@ -0,0 +1,77 @@
+/* This file is autogenerated please do not edit */
+SymTabRec AMDGPUChipsets[] = {
+ { PCI_CHIP_BONAIRE_6640, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_6641, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_6649, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_6650, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_6651, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_6658, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_665C, "BONAIRE" },
+ { PCI_CHIP_BONAIRE_665D, "BONAIRE" },
+ { PCI_CHIP_KABINI_9830, "KABINI" },
+ { PCI_CHIP_KABINI_9831, "KABINI" },
+ { PCI_CHIP_KABINI_9832, "KABINI" },
+ { PCI_CHIP_KABINI_9833, "KABINI" },
+ { PCI_CHIP_KABINI_9834, "KABINI" },
+ { PCI_CHIP_KABINI_9835, "KABINI" },
+ { PCI_CHIP_KABINI_9836, "KABINI" },
+ { PCI_CHIP_KABINI_9837, "KABINI" },
+ { PCI_CHIP_KABINI_9838, "KABINI" },
+ { PCI_CHIP_KABINI_9839, "KABINI" },
+ { PCI_CHIP_KABINI_983A, "KABINI" },
+ { PCI_CHIP_KABINI_983B, "KABINI" },
+ { PCI_CHIP_KABINI_983C, "KABINI" },
+ { PCI_CHIP_KABINI_983D, "KABINI" },
+ { PCI_CHIP_KABINI_983E, "KABINI" },
+ { PCI_CHIP_KABINI_983F, "KABINI" },
+ { PCI_CHIP_KAVERI_1304, "KAVERI" },
+ { PCI_CHIP_KAVERI_1305, "KAVERI" },
+ { PCI_CHIP_KAVERI_1306, "KAVERI" },
+ { PCI_CHIP_KAVERI_1307, "KAVERI" },
+ { PCI_CHIP_KAVERI_1309, "KAVERI" },
+ { PCI_CHIP_KAVERI_130A, "KAVERI" },
+ { PCI_CHIP_KAVERI_130B, "KAVERI" },
+ { PCI_CHIP_KAVERI_130C, "KAVERI" },
+ { PCI_CHIP_KAVERI_130D, "KAVERI" },
+ { PCI_CHIP_KAVERI_130E, "KAVERI" },
+ { PCI_CHIP_KAVERI_130F, "KAVERI" },
+ { PCI_CHIP_KAVERI_1310, "KAVERI" },
+ { PCI_CHIP_KAVERI_1311, "KAVERI" },
+ { PCI_CHIP_KAVERI_1312, "KAVERI" },
+ { PCI_CHIP_KAVERI_1313, "KAVERI" },
+ { PCI_CHIP_KAVERI_1315, "KAVERI" },
+ { PCI_CHIP_KAVERI_1316, "KAVERI" },
+ { PCI_CHIP_KAVERI_1317, "KAVERI" },
+ { PCI_CHIP_KAVERI_131B, "KAVERI" },
+ { PCI_CHIP_KAVERI_131C, "KAVERI" },
+ { PCI_CHIP_KAVERI_131D, "KAVERI" },
+ { PCI_CHIP_HAWAII_67A0, "HAWAII" },
+ { PCI_CHIP_HAWAII_67A1, "HAWAII" },
+ { PCI_CHIP_HAWAII_67A2, "HAWAII" },
+ { PCI_CHIP_HAWAII_67A8, "HAWAII" },
+ { PCI_CHIP_HAWAII_67A9, "HAWAII" },
+ { PCI_CHIP_HAWAII_67AA, "HAWAII" },
+ { PCI_CHIP_HAWAII_67B0, "HAWAII" },
+ { PCI_CHIP_HAWAII_67B1, "HAWAII" },
+ { PCI_CHIP_HAWAII_67B8, "HAWAII" },
+ { PCI_CHIP_HAWAII_67B9, "HAWAII" },
+ { PCI_CHIP_HAWAII_67BA, "HAWAII" },
+ { PCI_CHIP_HAWAII_67BE, "HAWAII" },
+ { PCI_CHIP_TOPAZ_6900, "TOPAZ" },
+ { PCI_CHIP_TOPAZ_6901, "TOPAZ" },
+ { PCI_CHIP_TOPAZ_6902, "TOPAZ" },
+ { PCI_CHIP_TOPAZ_6903, "TOPAZ" },
+ { PCI_CHIP_TOPAZ_6907, "TOPAZ" },
+ { PCI_CHIP_TONGA_6920, "TONGA" },
+ { PCI_CHIP_TONGA_6921, "TONGA" },
+ { PCI_CHIP_TONGA_6938, "TONGA" },
+ { PCI_CHIP_TONGA_6939, "TONGA" },
+ { PCI_CHIP_TONGA_692B, "TONGA" },
+ { PCI_CHIP_TONGA_692F, "TONGA" },
+ { PCI_CHIP_CARRIZO_9870, "CARRIZO" },
+ { PCI_CHIP_CARRIZO_9874, "CARRIZO" },
+ { PCI_CHIP_CARRIZO_9875, "CARRIZO" },
+ { PCI_CHIP_CARRIZO_9876, "CARRIZO" },
+ { PCI_CHIP_CARRIZO_9877, "CARRIZO" },
+ { -1, NULL }
+};
diff --git a/src/amdgpu_dri2.c b/src/amdgpu_dri2.c
new file mode 100644
index 0000000..26906d3
--- /dev/null
+++ b/src/amdgpu_dri2.c
@@ -0,0 +1,1583 @@
+/*
+ * Copyright 2008 Kristian Høgsberg
+ * Copyright 2008 Jérôme Glisse
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amdgpu_drv.h"
+#include "amdgpu_dri2.h"
+#include "amdgpu_video.h"
+#include "amdgpu_pixmap.h"
+
+#ifdef DRI2
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <gbm.h>
+
+#include "amdgpu_version.h"
+
+#if HAVE_LIST_H
+#include "list.h"
+#if !HAVE_XORG_LIST
+#define xorg_list list
+#define xorg_list_init list_init
+#define xorg_list_add list_add
+#define xorg_list_del list_del
+#define xorg_list_for_each_entry list_for_each_entry
+#endif
+#endif
+
+#if DRI2INFOREC_VERSION >= 4 && HAVE_LIST_H
+#define USE_DRI2_SCHEDULING
+#endif
+
+#if DRI2INFOREC_VERSION >= 9
+#define USE_DRI2_PRIME
+#endif
+
+#define FALLBACK_SWAP_DELAY 16
+
+#include <glamor.h>
+
+typedef DRI2BufferPtr BufferPtr;
+
+struct dri2_buffer_priv {
+ PixmapPtr pixmap;
+ unsigned int attachment;
+ unsigned int refcnt;
+};
+
+static PixmapPtr get_drawable_pixmap(DrawablePtr drawable)
+{
+ if (drawable->type == DRAWABLE_PIXMAP)
+ return (PixmapPtr) drawable;
+ else
+ return (*drawable->pScreen->
+ GetWindowPixmap) ((WindowPtr) drawable);
+}
+
+static PixmapPtr fixup_glamor(DrawablePtr drawable, PixmapPtr pixmap)
+{
+ PixmapPtr old = get_drawable_pixmap(drawable);
+ ScreenPtr screen = drawable->pScreen;
+ struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap);
+ GCPtr gc;
+
+ /* With a glamor pixmap, 2D pixmaps are created in texture
+ * and without a static BO attached to it. To support DRI,
+ * we need to create a new textured-drm pixmap and
+ * need to copy the original content to this new textured-drm
+ * pixmap, and then convert the old pixmap to a coherent
+ * textured-drm pixmap which has a valid BO attached to it
+ * and also has a valid texture, thus both glamor and DRI2
+ * can access it.
+ *
+ */
+
+ /* Copy the current contents of the pixmap to the bo. */
+ gc = GetScratchGC(drawable->depth, screen);
+ if (gc) {
+ ValidateGC(&pixmap->drawable, gc);
+ gc->ops->CopyArea(&old->drawable, &pixmap->drawable,
+ gc,
+ 0, 0,
+ old->drawable.width,
+ old->drawable.height, 0, 0);
+ FreeScratchGC(gc);
+ }
+
+ amdgpu_set_pixmap_private(pixmap, NULL);
+
+ /* And redirect the pixmap to the new bo (for 3D). */
+ glamor_egl_exchange_buffers(old, pixmap);
+ amdgpu_set_pixmap_private(old, priv);
+ screen->DestroyPixmap(pixmap);
+ old->refcnt++;
+
+ screen->ModifyPixmapHeader(old,
+ old->drawable.width,
+ old->drawable.height,
+ 0, 0, priv->stride, NULL);
+
+ return old;
+}
+
+static BufferPtr
+amdgpu_dri2_create_buffer2(ScreenPtr pScreen,
+ DrawablePtr drawable,
+ unsigned int attachment, unsigned int format)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ BufferPtr buffers;
+ struct dri2_buffer_priv *privates;
+ PixmapPtr pixmap, depth_pixmap;
+ struct amdgpu_buffer *bo = NULL;
+ unsigned front_width;
+ unsigned aligned_width = drawable->width;
+ unsigned height = drawable->height;
+ Bool is_glamor_pixmap = FALSE;
+ int depth;
+ int cpp;
+
+ if (format) {
+ depth = format;
+
+ switch (depth) {
+ case 15:
+ cpp = 2;
+ break;
+ case 24:
+ cpp = 4;
+ break;
+ default:
+ cpp = depth / 8;
+ }
+ } else {
+ depth = drawable->depth;
+ cpp = drawable->bitsPerPixel / 8;
+ }
+
+ pixmap = pScreen->GetScreenPixmap(pScreen);
+ front_width = pixmap->drawable.width;
+
+ pixmap = depth_pixmap = NULL;
+
+ if (attachment == DRI2BufferFrontLeft) {
+ pixmap = get_drawable_pixmap(drawable);
+ if (pScreen != pixmap->drawable.pScreen)
+ pixmap = NULL;
+ else if (info->use_glamor && !amdgpu_get_pixmap_bo(pixmap)) {
+ is_glamor_pixmap = TRUE;
+ aligned_width = pixmap->drawable.width;
+ height = pixmap->drawable.height;
+ pixmap = NULL;
+ } else
+ pixmap->refcnt++;
+ } else if (attachment == DRI2BufferStencil && depth_pixmap) {
+ pixmap = depth_pixmap;
+ pixmap->refcnt++;
+ }
+
+ if (!pixmap && (is_glamor_pixmap || attachment != DRI2BufferFrontLeft)) {
+ if (aligned_width == front_width)
+ aligned_width = pScrn->virtualX;
+
+ pixmap = (*pScreen->CreatePixmap) (pScreen,
+ aligned_width,
+ height,
+ depth,
+ AMDGPU_CREATE_PIXMAP_DRI2);
+ }
+
+ buffers = calloc(1, sizeof *buffers);
+ if (buffers == NULL)
+ goto error;
+
+ if (attachment == DRI2BufferDepth) {
+ depth_pixmap = pixmap;
+ }
+
+ if (pixmap) {
+ struct drm_gem_flink flink;
+ union gbm_bo_handle bo_handle;
+
+ if (is_glamor_pixmap)
+ pixmap = fixup_glamor(drawable, pixmap);
+ bo = amdgpu_get_pixmap_bo(pixmap);
+ if (!bo) {
+ goto error;
+ }
+
+ if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
+ bo_handle = gbm_bo_get_handle(bo->bo.gbm);
+ flink.handle = bo_handle.u32;
+ if (ioctl(info->dri2.drm_fd, DRM_IOCTL_GEM_FLINK, &flink) < 0)
+ goto error;
+ buffers->name = flink.name;
+ } else {
+ amdgpu_bo_export(bo->bo.amdgpu,
+ amdgpu_bo_handle_type_gem_flink_name,
+ &buffers->name);
+ }
+ }
+
+ privates = calloc(1, sizeof(struct dri2_buffer_priv));
+ if (privates == NULL)
+ goto error;
+
+ buffers->attachment = attachment;
+ if (pixmap) {
+ buffers->pitch = pixmap->devKind;
+ buffers->cpp = cpp;
+ }
+ buffers->driverPrivate = privates;
+ buffers->format = format;
+ buffers->flags = 0; /* not tiled */
+ privates->pixmap = pixmap;
+ privates->attachment = attachment;
+ privates->refcnt = 1;
+
+ return buffers;
+
+error:
+ free(buffers);
+ if (pixmap)
+ (*pScreen->DestroyPixmap) (pixmap);
+ return NULL;
+}
+
+DRI2BufferPtr
+amdgpu_dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment,
+ unsigned int format)
+{
+ return amdgpu_dri2_create_buffer2(pDraw->pScreen, pDraw,
+ attachment, format);
+}
+
+static void
+amdgpu_dri2_destroy_buffer2(ScreenPtr pScreen,
+ DrawablePtr drawable, BufferPtr buffers)
+{
+ if (buffers) {
+ struct dri2_buffer_priv *private = buffers->driverPrivate;
+
+ /* Trying to free an already freed buffer is unlikely to end well */
+ if (private->refcnt == 0) {
+ ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
+
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "Attempted to destroy previously destroyed buffer.\
+ This is a programming error\n");
+ return;
+ }
+
+ private->refcnt--;
+ if (private->refcnt == 0) {
+ if (private->pixmap)
+ (*pScreen->DestroyPixmap) (private->pixmap);
+
+ free(buffers->driverPrivate);
+ free(buffers);
+ }
+ }
+}
+
+void amdgpu_dri2_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buf)
+{
+ amdgpu_dri2_destroy_buffer2(pDraw->pScreen, pDraw, buf);
+}
+
+static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable)
+{
+ if (drawable->type == DRAWABLE_PIXMAP)
+ return (PixmapPtr) drawable;
+ else {
+ struct _Window *pWin = (struct _Window *)drawable;
+ return drawable->pScreen->GetWindowPixmap(pWin);
+ }
+}
+
+static void
+amdgpu_dri2_copy_region2(ScreenPtr pScreen,
+ DrawablePtr drawable,
+ RegionPtr region,
+ BufferPtr dest_buffer, BufferPtr src_buffer)
+{
+ struct dri2_buffer_priv *src_private = src_buffer->driverPrivate;
+ struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate;
+ DrawablePtr src_drawable;
+ DrawablePtr dst_drawable;
+ RegionPtr copy_clip;
+ GCPtr gc;
+ Bool translate = FALSE;
+ int off_x = 0, off_y = 0;
+
+ src_drawable = &src_private->pixmap->drawable;
+ dst_drawable = &dst_private->pixmap->drawable;
+
+ if (src_private->attachment == DRI2BufferFrontLeft) {
+ src_drawable = drawable;
+ }
+ if (dst_private->attachment == DRI2BufferFrontLeft) {
+#ifdef USE_DRI2_PRIME
+ if (drawable->pScreen != pScreen) {
+ dst_drawable = DRI2UpdatePrime(drawable, dest_buffer);
+ if (!dst_drawable)
+ return;
+ if (dst_drawable != drawable)
+ translate = TRUE;
+ } else
+#endif
+ dst_drawable = drawable;
+ }
+
+ if (translate && drawable->type == DRAWABLE_WINDOW) {
+ PixmapPtr pPix = GetDrawablePixmap(drawable);
+
+ off_x = drawable->x - pPix->screen_x;
+ off_y = drawable->y - pPix->screen_y;
+ }
+ gc = GetScratchGC(dst_drawable->depth, pScreen);
+ copy_clip = REGION_CREATE(pScreen, NULL, 0);
+ REGION_COPY(pScreen, copy_clip, region);
+
+ if (translate) {
+ REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y);
+ }
+
+ (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0);
+ ValidateGC(dst_drawable, gc);
+
+ /* If this is a full buffer swap or frontbuffer flush, throttle on the
+ * previous one
+ */
+ if (dst_private->attachment == DRI2BufferFrontLeft) {
+ if (REGION_NUM_RECTS(region) == 1) {
+ BoxPtr extents = REGION_EXTENTS(pScreen, region);
+
+ if (extents->x1 == 0 && extents->y1 == 0 &&
+ extents->x2 == drawable->width &&
+ extents->y2 == drawable->height) {
+ char pixel[4];
+
+ /* XXX: This is a pretty big hammer... */
+ pScreen->GetImage(drawable, 0, 0, 1, 1,
+ ZPixmap, ~0, pixel);
+ }
+ }
+ }
+
+ (*gc->ops->CopyArea) (src_drawable, dst_drawable, gc,
+ 0, 0, drawable->width, drawable->height, off_x,
+ off_y);
+
+ FreeScratchGC(gc);
+}
+
+void
+amdgpu_dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
+ DRI2BufferPtr pDstBuffer, DRI2BufferPtr pSrcBuffer)
+{
+ return amdgpu_dri2_copy_region2(pDraw->pScreen, pDraw, pRegion,
+ pDstBuffer, pSrcBuffer);
+}
+
+#ifdef USE_DRI2_SCHEDULING
+
+enum DRI2FrameEventType {
+ DRI2_SWAP,
+ DRI2_FLIP,
+ DRI2_WAITMSC,
+};
+
+typedef struct _DRI2FrameEvent {
+ XID drawable_id;
+ ClientPtr client;
+ enum DRI2FrameEventType type;
+ int frame;
+ xf86CrtcPtr crtc;
+
+ /* for swaps & flips only */
+ DRI2SwapEventPtr event_complete;
+ void *event_data;
+ DRI2BufferPtr front;
+ DRI2BufferPtr back;
+
+ Bool valid;
+
+ struct xorg_list link;
+} DRI2FrameEventRec, *DRI2FrameEventPtr;
+
+typedef struct _DRI2ClientEvents {
+ struct xorg_list reference_list;
+} DRI2ClientEventsRec, *DRI2ClientEventsPtr;
+
+#if HAS_DEVPRIVATEKEYREC
+
+static int DRI2InfoCnt;
+
+static DevPrivateKeyRec DRI2ClientEventsPrivateKeyRec;
+#define DRI2ClientEventsPrivateKey (&DRI2ClientEventsPrivateKeyRec)
+
+#else
+
+static int DRI2ClientEventsPrivateKeyIndex;
+DevPrivateKey DRI2ClientEventsPrivateKey = &DRI2ClientEventsPrivateKeyIndex;
+
+#endif /* HAS_DEVPRIVATEKEYREC */
+
+#define GetDRI2ClientEvents(pClient) ((DRI2ClientEventsPtr) \
+ dixLookupPrivate(&(pClient)->devPrivates, DRI2ClientEventsPrivateKey))
+
+static int ListAddDRI2ClientEvents(ClientPtr client, struct xorg_list *entry)
+{
+ DRI2ClientEventsPtr pClientPriv;
+ pClientPriv = GetDRI2ClientEvents(client);
+
+ if (!pClientPriv) {
+ return BadAlloc;
+ }
+
+ xorg_list_add(entry, &pClientPriv->reference_list);
+ return 0;
+}
+
+static void ListDelDRI2ClientEvents(ClientPtr client, struct xorg_list *entry)
+{
+ DRI2ClientEventsPtr pClientPriv;
+ pClientPriv = GetDRI2ClientEvents(client);
+
+ if (!pClientPriv) {
+ return;
+ }
+ xorg_list_del(entry);
+}
+
+static void amdgpu_dri2_ref_buffer(BufferPtr buffer)
+{
+ struct dri2_buffer_priv *private = buffer->driverPrivate;
+ private->refcnt++;
+}
+
+static void amdgpu_dri2_unref_buffer(BufferPtr buffer)
+{
+ if (buffer) {
+ struct dri2_buffer_priv *private = buffer->driverPrivate;
+ amdgpu_dri2_destroy_buffer(&(private->pixmap->drawable),
+ buffer);
+ }
+}
+
+static void
+amdgpu_dri2_client_state_changed(CallbackListPtr * ClientStateCallback,
+ pointer data, pointer calldata)
+{
+ DRI2ClientEventsPtr pClientEventsPriv;
+ DRI2FrameEventPtr ref;
+ NewClientInfoRec *clientinfo = calldata;
+ ClientPtr pClient = clientinfo->client;
+ pClientEventsPriv = GetDRI2ClientEvents(pClient);
+
+ switch (pClient->clientState) {
+ case ClientStateInitial:
+ xorg_list_init(&pClientEventsPriv->reference_list);
+ break;
+ case ClientStateRunning:
+ break;
+
+ case ClientStateRetained:
+ case ClientStateGone:
+ if (pClientEventsPriv) {
+ xorg_list_for_each_entry(ref,
+ &pClientEventsPriv->
+ reference_list, link) {
+ ref->valid = FALSE;
+ amdgpu_dri2_unref_buffer(ref->front);
+ amdgpu_dri2_unref_buffer(ref->back);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static
+xf86CrtcPtr amdgpu_dri2_drawable_crtc(DrawablePtr pDraw, Bool consider_disabled)
+{
+ ScreenPtr pScreen = pDraw->pScreen;
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ xf86CrtcPtr crtc;
+
+ crtc = amdgpu_pick_best_crtc(pScrn, consider_disabled,
+ pDraw->x,
+ pDraw->x + pDraw->width,
+ pDraw->y, pDraw->y + pDraw->height);
+
+ /* Make sure the CRTC is valid and this is the real front buffer */
+ if (crtc != NULL && !crtc->rotatedData)
+ return crtc;
+ else
+ return NULL;
+}
+
+static Bool
+amdgpu_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client,
+ DrawablePtr draw, DRI2BufferPtr front,
+ DRI2BufferPtr back, DRI2SwapEventPtr func,
+ void *data, unsigned int target_msc)
+{
+ struct dri2_buffer_priv *back_priv;
+ struct amdgpu_buffer *bo = NULL;
+ DRI2FrameEventPtr flip_info;
+ /* Main crtc for this drawable shall finally deliver pageflip event. */
+ xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, FALSE);
+ int ref_crtc_hw_id = crtc ? drmmode_get_crtc_id(crtc) : -1;
+
+ flip_info = calloc(1, sizeof(DRI2FrameEventRec));
+ if (!flip_info)
+ return FALSE;
+
+ flip_info->drawable_id = draw->id;
+ flip_info->client = client;
+ flip_info->type = DRI2_SWAP;
+ flip_info->event_complete = func;
+ flip_info->event_data = data;
+ flip_info->frame = target_msc;
+ flip_info->crtc = crtc;
+
+ xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info);
+
+ /* Page flip the full screen buffer */
+ back_priv = back->driverPrivate;
+ bo = amdgpu_get_pixmap_bo(back_priv->pixmap);
+
+ return amdgpu_do_pageflip(scrn, bo, flip_info, ref_crtc_hw_id);
+}
+
+static Bool update_front(DrawablePtr draw, DRI2BufferPtr front)
+{
+ ScreenPtr screen = draw->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ PixmapPtr pixmap;
+ struct dri2_buffer_priv *priv = front->driverPrivate;
+ struct amdgpu_buffer *bo = NULL;
+ union gbm_bo_handle bo_handle;
+ struct drm_gem_flink flink;
+
+ pixmap = get_drawable_pixmap(draw);
+ pixmap->refcnt++;
+
+ bo = amdgpu_get_pixmap_bo(pixmap);
+ if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
+ bo_handle = gbm_bo_get_handle(bo->bo.gbm);
+ flink.handle = bo_handle.u32;
+ if (ioctl(info->dri2.drm_fd, DRM_IOCTL_GEM_FLINK, &flink) < 0)
+ return FALSE;
+ front->name = flink.name;
+ } else {
+ amdgpu_bo_export(bo->bo.amdgpu,
+ amdgpu_bo_handle_type_gem_flink_name,
+ &front->name);
+ }
+ (*draw->pScreen->DestroyPixmap) (priv->pixmap);
+ front->pitch = pixmap->devKind;
+ front->cpp = pixmap->drawable.bitsPerPixel / 8;
+ priv->pixmap = pixmap;
+
+ return TRUE;
+}
+
+static Bool
+can_exchange(ScrnInfoPtr pScrn, DrawablePtr draw,
+ DRI2BufferPtr front, DRI2BufferPtr back)
+{
+ struct dri2_buffer_priv *front_priv = front->driverPrivate;
+ struct dri2_buffer_priv *back_priv = back->driverPrivate;
+ PixmapPtr front_pixmap;
+ PixmapPtr back_pixmap = back_priv->pixmap;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ for (i = 0; i < xf86_config->num_crtc; i++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[i];
+ if (crtc->enabled && crtc->rotatedData)
+ return FALSE;
+ }
+
+ if (!update_front(draw, front))
+ return FALSE;
+
+ front_pixmap = front_priv->pixmap;
+
+ if (front_pixmap->drawable.width != back_pixmap->drawable.width)
+ return FALSE;
+
+ if (front_pixmap->drawable.height != back_pixmap->drawable.height)
+ return FALSE;
+
+ if (front_pixmap->drawable.bitsPerPixel !=
+ back_pixmap->drawable.bitsPerPixel)
+ return FALSE;
+
+ if (front_pixmap->devKind != back_pixmap->devKind)
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool
+can_flip(ScrnInfoPtr pScrn, DrawablePtr draw,
+ DRI2BufferPtr front, DRI2BufferPtr back)
+{
+ return draw->type == DRAWABLE_WINDOW &&
+ AMDGPUPTR(pScrn)->allowPageFlip &&
+ pScrn->vtSema &&
+ DRI2CanFlip(draw) && can_exchange(pScrn, draw, front, back);
+}
+
+static void
+amdgpu_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front,
+ DRI2BufferPtr back)
+{
+ struct dri2_buffer_priv *front_priv = front->driverPrivate;
+ struct dri2_buffer_priv *back_priv = back->driverPrivate;
+ struct amdgpu_buffer *front_bo = NULL;
+ struct amdgpu_buffer *back_bo = NULL;
+ ScreenPtr screen;
+ AMDGPUInfoPtr info;
+ RegionRec region;
+ int tmp;
+
+ region.extents.x1 = region.extents.y1 = 0;
+ region.extents.x2 = front_priv->pixmap->drawable.width;
+ region.extents.y2 = front_priv->pixmap->drawable.width;
+ region.data = NULL;
+ DamageRegionAppend(&front_priv->pixmap->drawable, &region);
+
+ /* Swap BO names so DRI works */
+ tmp = front->name;
+ front->name = back->name;
+ back->name = tmp;
+
+ /* Swap pixmap bos */
+ front_bo = amdgpu_get_pixmap_bo(front_priv->pixmap);
+ back_bo = amdgpu_get_pixmap_bo(back_priv->pixmap);
+ amdgpu_set_pixmap_bo(front_priv->pixmap, back_bo);
+ amdgpu_set_pixmap_bo(back_priv->pixmap, front_bo);
+
+ /* Do we need to update the Screen? */
+ screen = draw->pScreen;
+ info = AMDGPUPTR(xf86ScreenToScrn(screen));
+ if (front_bo == info->front_buffer) {
+ amdgpu_bo_ref(back_bo);
+ amdgpu_bo_unref(&info->front_buffer);
+ info->front_buffer = back_bo;
+ amdgpu_set_pixmap_bo(screen->GetScreenPixmap(screen), back_bo);
+ }
+
+ amdgpu_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
+
+ DamageRegionProcessPending(&front_priv->pixmap->drawable);
+}
+
+void amdgpu_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data)
+{
+ DRI2FrameEventPtr event = event_data;
+ DrawablePtr drawable;
+ ScreenPtr screen;
+ ScrnInfoPtr scrn;
+ int status;
+ int swap_type;
+ BoxRec box;
+ RegionRec region;
+
+ if (!event->valid)
+ goto cleanup;
+
+ status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
+ M_ANY, DixWriteAccess);
+ if (status != Success)
+ goto cleanup;
+ if (!event->crtc)
+ goto cleanup;
+ frame += amdgpu_get_interpolated_vblanks(event->crtc);
+
+ screen = drawable->pScreen;
+ scrn = xf86ScreenToScrn(screen);
+
+ switch (event->type) {
+ case DRI2_FLIP:
+ if (can_flip(scrn, drawable, event->front, event->back) &&
+ amdgpu_dri2_schedule_flip(scrn,
+ event->client,
+ drawable,
+ event->front,
+ event->back,
+ event->event_complete,
+ event->event_data,
+ event->frame)) {
+ amdgpu_dri2_exchange_buffers(drawable, event->front,
+ event->back);
+ break;
+ }
+ /* else fall through to exchange/blit */
+ case DRI2_SWAP:
+ if (DRI2CanExchange(drawable) &&
+ can_exchange(scrn, drawable, event->front, event->back)) {
+ amdgpu_dri2_exchange_buffers(drawable, event->front,
+ event->back);
+ swap_type = DRI2_EXCHANGE_COMPLETE;
+ } else {
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = drawable->width;
+ box.y2 = drawable->height;
+ REGION_INIT(pScreen, &region, &box, 0);
+ amdgpu_dri2_copy_region(drawable, &region, event->front,
+ event->back);
+ swap_type = DRI2_BLIT_COMPLETE;
+ }
+
+ DRI2SwapComplete(event->client, drawable, frame, tv_sec,
+ tv_usec, swap_type, event->event_complete,
+ event->event_data);
+
+ break;
+ case DRI2_WAITMSC:
+ DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec,
+ tv_usec);
+ break;
+ default:
+ /* Unknown type */
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "%s: unknown vblank event received\n", __func__);
+ break;
+ }
+
+cleanup:
+ if (event->valid) {
+ amdgpu_dri2_unref_buffer(event->front);
+ amdgpu_dri2_unref_buffer(event->back);
+ ListDelDRI2ClientEvents(event->client, &event->link);
+ }
+ free(event);
+}
+
+drmVBlankSeqType amdgpu_populate_vbl_request_type(xf86CrtcPtr crtc)
+{
+ drmVBlankSeqType type = 0;
+ int crtc_id = drmmode_get_crtc_id(crtc);
+
+ if (crtc_id == 1)
+ type |= DRM_VBLANK_SECONDARY;
+ else if (crtc_id > 1)
+#ifdef DRM_VBLANK_HIGH_CRTC_SHIFT
+ type |= (crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+ DRM_VBLANK_HIGH_CRTC_MASK;
+#else
+ ErrorF("amdgpu driver bug: %s called for CRTC %d > 1, but "
+ "DRM_VBLANK_HIGH_CRTC_MASK not defined at build time\n",
+ __func__, crtc_id);
+#endif
+
+ return type;
+}
+
+/*
+ * This function should be called on a disabled CRTC only (i.e., CRTC
+ * in DPMS-off state). It will calculate the delay necessary to reach
+ * target_msc from present time if the CRTC were running.
+ */
+static
+CARD32 amdgpu_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 * target_msc,
+ CARD64 divisor, CARD64 remainder)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ int nominal_frame_rate = drmmode_crtc->dpms_last_fps;
+ CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust;
+ uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq;
+ int interpolated_vblanks = drmmode_crtc->interpolated_vblanks;
+ int target_seq;
+ CARD64 now, target_time, delta_t;
+ int64_t d, delta_seq;
+ int ret;
+ CARD32 d_ms;
+
+ if (!last_vblank_ust) {
+ *target_msc = 0;
+ return FALLBACK_SWAP_DELAY;
+ }
+ ret = drmmode_get_current_ust(info->dri2.drm_fd, &now);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "%s cannot get current time\n", __func__);
+ *target_msc = 0;
+ return FALLBACK_SWAP_DELAY;
+ }
+ target_seq = (int)*target_msc - interpolated_vblanks;
+ delta_seq = (int64_t) target_seq - (int64_t) last_vblank_seq;
+ delta_seq *= 1000000;
+ target_time = last_vblank_ust;
+ target_time += delta_seq / nominal_frame_rate;
+ d = target_time - now;
+ if (d < 0) {
+ /* we missed the event, adjust target_msc, do the divisor magic */
+ CARD64 current_msc;
+ current_msc = last_vblank_seq + interpolated_vblanks;
+ delta_t = now - last_vblank_ust;
+ delta_seq = delta_t * nominal_frame_rate;
+ current_msc += delta_seq / 1000000;
+ current_msc &= 0xffffffff;
+ if (divisor == 0) {
+ *target_msc = current_msc;
+ d = 0;
+ } else {
+ *target_msc =
+ current_msc - (current_msc % divisor) + remainder;
+ if ((current_msc % divisor) >= remainder)
+ *target_msc += divisor;
+ *target_msc &= 0xffffffff;
+ target_seq = (int)*target_msc - interpolated_vblanks;
+ delta_seq =
+ (int64_t) target_seq - (int64_t) last_vblank_seq;
+ delta_seq *= 1000000;
+ target_time = last_vblank_ust;
+ target_time += delta_seq / nominal_frame_rate;
+ d = target_time - now;
+ }
+ }
+ /*
+ * convert delay to milliseconds and add margin to prevent the client
+ * from coming back early (due to timer granularity and rounding
+ * errors) and getting the same MSC it just got
+ */
+ d_ms = (CARD32) d / 1000;
+ if ((CARD32) d - d_ms * 1000 > 0)
+ d_ms += 2;
+ else
+ d_ms++;
+ return d_ms;
+}
+
+/*
+ * Get current frame count and frame count timestamp, based on drawable's
+ * crtc.
+ */
+static int amdgpu_dri2_get_msc(DrawablePtr draw, CARD64 * ust, CARD64 * msc)
+{
+ ScreenPtr screen = draw->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ drmVBlank vbl;
+ int ret;
+ xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE);
+
+ /* Drawable not displayed, make up a value */
+ if (crtc == NULL) {
+ *ust = 0;
+ *msc = 0;
+ return TRUE;
+ }
+ if (amdgpu_crtc_is_enabled(crtc)) {
+ /* CRTC is running, read vblank counter and timestamp */
+ vbl.request.type = DRM_VBLANK_RELATIVE;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+ vbl.request.sequence = 0;
+
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "get vblank counter failed: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+
+ *ust =
+ ((CARD64) vbl.reply.tval_sec * 1000000) +
+ vbl.reply.tval_usec;
+ *msc =
+ vbl.reply.sequence + amdgpu_get_interpolated_vblanks(crtc);
+ *msc &= 0xffffffff;
+ } else {
+ /* CRTC is not running, extrapolate MSC and timestamp */
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ CARD64 now, delta_t, delta_seq;
+
+ if (!drmmode_crtc->dpms_last_ust)
+ return FALSE;
+ ret = drmmode_get_current_ust(info->dri2.drm_fd, &now);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "%s cannot get current time\n", __func__);
+ return FALSE;
+ }
+ delta_t = now - drmmode_crtc->dpms_last_ust;
+ delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
+ delta_seq /= 1000000;
+ *ust = drmmode_crtc->dpms_last_ust;
+ delta_t = delta_seq * 1000000;
+ delta_t /= drmmode_crtc->dpms_last_fps;
+ *ust += delta_t;
+ *msc = drmmode_crtc->dpms_last_seq;
+ *msc += drmmode_crtc->interpolated_vblanks;
+ *msc += delta_seq;
+ *msc &= 0xffffffff;
+ }
+ return TRUE;
+}
+
+static
+CARD32 amdgpu_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data)
+{
+ DRI2FrameEventPtr event_info = (DRI2FrameEventPtr) data;
+ DrawablePtr drawable;
+ ScreenPtr screen;
+ ScrnInfoPtr scrn;
+ AMDGPUInfoPtr info;
+ int status;
+ CARD64 drm_now;
+ int ret;
+ unsigned int tv_sec, tv_usec;
+ CARD64 delta_t, delta_seq, frame;
+ drmmode_crtc_private_ptr drmmode_crtc;
+ TimerFree(timer);
+
+ /*
+ * This is emulated event, so its time is current time, which we
+ * have to get in DRM-compatible form (which is a bit messy given
+ * the information that we have at this point). Can't use now argument
+ * because DRM event time may come from monotonic clock, while
+ * DIX timer facility uses real-time clock.
+ */
+ if (!event_info->crtc) {
+ ErrorF("%s no crtc\n", __func__);
+ amdgpu_dri2_frame_event_handler(0, 0, 0, data);
+ return 0;
+ }
+ status =
+ dixLookupDrawable(&drawable, event_info->drawable_id, serverClient,
+ M_ANY, DixWriteAccess);
+ if (status != Success) {
+ ErrorF("%s cannot lookup drawable\n", __func__);
+ amdgpu_dri2_frame_event_handler(0, 0, 0, data);
+ return 0;
+ }
+ screen = drawable->pScreen;
+ scrn = xf86ScreenToScrn(screen);
+ info = AMDGPUPTR(scrn);
+ ret = drmmode_get_current_ust(info->dri2.drm_fd, &drm_now);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "%s cannot get current time\n", __func__);
+ amdgpu_dri2_frame_event_handler(0, 0, 0, data);
+ return 0;
+ }
+ tv_sec = (unsigned int)(drm_now / 1000000);
+ tv_usec = (unsigned int)(drm_now - (CARD64) tv_sec * 1000000);
+ /*
+ * calculate the frame number from current time
+ * that would come from CRTC if it were running
+ */
+ drmmode_crtc = event_info->crtc->driver_private;
+ delta_t = drm_now - (CARD64) drmmode_crtc->dpms_last_ust;
+ delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
+ delta_seq /= 1000000;
+ frame = (CARD64) drmmode_crtc->dpms_last_seq + delta_seq;
+ frame &= 0xffffffff;
+ amdgpu_dri2_frame_event_handler((unsigned int)frame, tv_sec, tv_usec,
+ data);
+ return 0;
+}
+
+static
+void amdgpu_dri2_schedule_event(CARD32 delay, pointer arg)
+{
+ OsTimerPtr timer;
+
+ timer = TimerSet(NULL, 0, delay, amdgpu_dri2_deferred_event, arg);
+ if (delay == 0) {
+ CARD32 now = GetTimeInMillis();
+ amdgpu_dri2_deferred_event(timer, now, arg);
+ }
+}
+
+/*
+ * Request a DRM event when the requested conditions will be satisfied.
+ *
+ * We need to handle the event and ask the server to wake up the client when
+ * we receive it.
+ */
+static int amdgpu_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
+ CARD64 target_msc, CARD64 divisor,
+ CARD64 remainder)
+{
+ ScreenPtr screen = draw->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ DRI2FrameEventPtr wait_info = NULL;
+ xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE);
+ drmVBlank vbl;
+ int ret;
+ CARD64 current_msc;
+
+ /* Truncate to match kernel interfaces; means occasional overflow
+ * misses, but that's generally not a big deal */
+ target_msc &= 0xffffffff;
+ divisor &= 0xffffffff;
+ remainder &= 0xffffffff;
+
+ /* Drawable not visible, return immediately */
+ if (crtc == NULL)
+ goto out_complete;
+
+ wait_info = calloc(1, sizeof(DRI2FrameEventRec));
+ if (!wait_info)
+ goto out_complete;
+
+ wait_info->drawable_id = draw->id;
+ wait_info->client = client;
+ wait_info->type = DRI2_WAITMSC;
+ wait_info->valid = TRUE;
+ wait_info->crtc = crtc;
+
+ if (ListAddDRI2ClientEvents(client, &wait_info->link)) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "add events to client private failed.\n");
+ free(wait_info);
+ wait_info = NULL;
+ goto out_complete;
+ }
+
+ /*
+ * CRTC is in DPMS off state, calculate wait time from current time,
+ * target_msc and last vblank time/sequence when CRTC was turned off
+ */
+ if (!amdgpu_crtc_is_enabled(crtc)) {
+ CARD32 delay;
+ delay = amdgpu_dri2_extrapolate_msc_delay(crtc, &target_msc,
+ divisor, remainder);
+ wait_info->frame = target_msc;
+ amdgpu_dri2_schedule_event(delay, wait_info);
+ DRI2BlockClient(client, draw);
+ return TRUE;
+ }
+
+ /* Get current count */
+ vbl.request.type = DRM_VBLANK_RELATIVE;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+ vbl.request.sequence = 0;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "get vblank counter failed: %s\n", strerror(errno));
+ goto out_complete;
+ }
+
+ current_msc =
+ vbl.reply.sequence + amdgpu_get_interpolated_vblanks(crtc);
+ current_msc &= 0xffffffff;
+
+ /*
+ * If divisor is zero, or current_msc is smaller than target_msc,
+ * we just need to make sure target_msc passes before waking up the
+ * client.
+ */
+ if (divisor == 0 || current_msc < target_msc) {
+ /* If target_msc already reached or passed, set it to
+ * current_msc to ensure we return a reasonable value back
+ * to the caller. This keeps the client from continually
+ * sending us MSC targets from the past by forcibly updating
+ * their count on this call.
+ */
+ if (current_msc >= target_msc)
+ target_msc = current_msc;
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+ vbl.request.sequence = target_msc;
+ vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc);
+ vbl.request.signal = (unsigned long)wait_info;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "get vblank counter failed: %s\n",
+ strerror(errno));
+ goto out_complete;
+ }
+
+ wait_info->frame = vbl.reply.sequence;
+ wait_info->frame += amdgpu_get_interpolated_vblanks(crtc);
+ DRI2BlockClient(client, draw);
+ return TRUE;
+ }
+
+ /*
+ * If we get here, target_msc has already passed or we don't have one,
+ * so we queue an event that will satisfy the divisor/remainder equation.
+ */
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+
+ vbl.request.sequence = current_msc - (current_msc % divisor) +
+ remainder;
+
+ /*
+ * If calculated remainder is larger than requested remainder,
+ * it means we've passed the last point where
+ * seq % divisor == remainder, so we need to wait for the next time
+ * that will happen.
+ */
+ if ((current_msc % divisor) >= remainder)
+ vbl.request.sequence += divisor;
+ vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc);
+
+ vbl.request.signal = (unsigned long)wait_info;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "get vblank counter failed: %s\n", strerror(errno));
+ goto out_complete;
+ }
+
+ wait_info->frame = vbl.reply.sequence;
+ wait_info->frame += amdgpu_get_interpolated_vblanks(crtc);
+ DRI2BlockClient(client, draw);
+
+ return TRUE;
+
+out_complete:
+ if (wait_info) {
+ ListDelDRI2ClientEvents(wait_info->client, &wait_info->link);
+ free(wait_info);
+ }
+ DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
+ return TRUE;
+}
+
+void amdgpu_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data)
+{
+ DRI2FrameEventPtr flip = event_data;
+ DrawablePtr drawable;
+ ScreenPtr screen;
+ ScrnInfoPtr scrn;
+ int status;
+ PixmapPtr pixmap;
+
+ status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient,
+ M_ANY, DixWriteAccess);
+ if (status != Success) {
+ free(flip);
+ return;
+ }
+ if (!flip->crtc) {
+ free(flip);
+ return;
+ }
+ frame += amdgpu_get_interpolated_vblanks(flip->crtc);
+
+ screen = drawable->pScreen;
+ scrn = xf86ScreenToScrn(screen);
+
+ pixmap = screen->GetScreenPixmap(screen);
+ xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n",
+ __func__, __LINE__, flip, pixmap->drawable.width,
+ pixmap->devKind, pixmap->devKind / 4);
+
+ /* We assume our flips arrive in order, so we don't check the frame */
+ switch (flip->type) {
+ case DRI2_SWAP:
+ /* Check for too small vblank count of pageflip completion, taking wraparound
+ * into account. This usually means some defective kms pageflip completion,
+ * causing wrong (msc, ust) return values and possible visual corruption.
+ */
+ if ((frame < flip->frame) && (flip->frame - frame < 5)) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "%s: Pageflip completion event has impossible msc %d < target_msc %d\n",
+ __func__, frame, flip->frame);
+ /* All-Zero values signal failure of (msc, ust) timestamping to client. */
+ frame = tv_sec = tv_usec = 0;
+ }
+
+ DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec,
+ DRI2_FLIP_COMPLETE, flip->event_complete,
+ flip->event_data);
+ break;
+ default:
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "%s: unknown vblank event received\n", __func__);
+ /* Unknown type */
+ break;
+ }
+
+ free(flip);
+}
+
+/*
+ * ScheduleSwap is responsible for requesting a DRM vblank event for the
+ * appropriate frame.
+ *
+ * In the case of a blit (e.g. for a windowed swap) or buffer exchange,
+ * the vblank requested can simply be the last queued swap frame + the swap
+ * interval for the drawable.
+ *
+ * In the case of a page flip, we request an event for the last queued swap
+ * frame + swap interval - 1, since we'll need to queue the flip for the frame
+ * immediately following the received event.
+ *
+ * The client will be blocked if it tries to perform further GL commands
+ * after queueing a swap, though in the Intel case after queueing a flip, the
+ * client is free to queue more commands; they'll block in the kernel if
+ * they access buffers busy with the flip.
+ *
+ * When the swap is complete, the driver should call into the server so it
+ * can send any swap complete events that have been requested.
+ */
+static int amdgpu_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
+ DRI2BufferPtr front, DRI2BufferPtr back,
+ CARD64 * target_msc, CARD64 divisor,
+ CARD64 remainder, DRI2SwapEventPtr func,
+ void *data)
+{
+ ScreenPtr screen = draw->pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE);
+ drmVBlank vbl;
+ int ret, flip = 0;
+ DRI2FrameEventPtr swap_info = NULL;
+ enum DRI2FrameEventType swap_type = DRI2_SWAP;
+ CARD64 current_msc;
+ BoxRec box;
+ RegionRec region;
+
+ /* Truncate to match kernel interfaces; means occasional overflow
+ * misses, but that's generally not a big deal */
+ *target_msc &= 0xffffffff;
+ divisor &= 0xffffffff;
+ remainder &= 0xffffffff;
+
+ /* amdgpu_dri2_frame_event_handler will get called some unknown time in the
+ * future with these buffers. Take a reference to ensure that they won't
+ * get destroyed before then.
+ */
+ amdgpu_dri2_ref_buffer(front);
+ amdgpu_dri2_ref_buffer(back);
+
+ /* either off-screen or CRTC not usable... just complete the swap */
+ if (crtc == NULL)
+ goto blit_fallback;
+
+ swap_info = calloc(1, sizeof(DRI2FrameEventRec));
+ if (!swap_info)
+ goto blit_fallback;
+
+ swap_info->drawable_id = draw->id;
+ swap_info->client = client;
+ swap_info->event_complete = func;
+ swap_info->event_data = data;
+ swap_info->front = front;
+ swap_info->back = back;
+ swap_info->valid = TRUE;
+ swap_info->crtc = crtc;
+ if (ListAddDRI2ClientEvents(client, &swap_info->link)) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "add events to client private failed.\n");
+ free(swap_info);
+ swap_info = NULL;
+ goto blit_fallback;
+ }
+
+ /*
+ * CRTC is in DPMS off state, fallback to blit, but calculate
+ * wait time from current time, target_msc and last vblank
+ * time/sequence when CRTC was turned off
+ */
+ if (!amdgpu_crtc_is_enabled(crtc)) {
+ CARD32 delay;
+ delay = amdgpu_dri2_extrapolate_msc_delay(crtc, target_msc,
+ divisor, remainder);
+ swap_info->frame = *target_msc;
+ amdgpu_dri2_schedule_event(delay, swap_info);
+ return TRUE;
+ }
+
+ /* Get current count */
+ vbl.request.type = DRM_VBLANK_RELATIVE;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+ vbl.request.sequence = 0;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "first get vblank counter failed: %s\n",
+ strerror(errno));
+ *target_msc = 0;
+ amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info);
+ return TRUE;
+ }
+
+ current_msc =
+ vbl.reply.sequence + amdgpu_get_interpolated_vblanks(crtc);
+ current_msc &= 0xffffffff;
+
+ /* Flips need to be submitted one frame before */
+ if (can_flip(scrn, draw, front, back)) {
+ swap_type = DRI2_FLIP;
+ flip = 1;
+ }
+
+ swap_info->type = swap_type;
+
+ /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.
+ * Do it early, so handling of different timing constraints
+ * for divisor, remainder and msc vs. target_msc works.
+ */
+ if (*target_msc > 0)
+ *target_msc -= flip;
+
+ /*
+ * If divisor is zero, or current_msc is smaller than target_msc
+ * we just need to make sure target_msc passes before initiating
+ * the swap.
+ */
+ if (divisor == 0 || current_msc < *target_msc) {
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+ /* If non-pageflipping, but blitting/exchanging, we need to use
+ * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
+ * on.
+ */
+ if (flip == 0)
+ vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+
+ /* If target_msc already reached or passed, set it to
+ * current_msc to ensure we return a reasonable value back
+ * to the caller. This makes swap_interval logic more robust.
+ */
+ if (current_msc >= *target_msc)
+ *target_msc = current_msc;
+
+ vbl.request.sequence = *target_msc;
+ vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc);
+ vbl.request.signal = (unsigned long)swap_info;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "divisor 0 get vblank counter failed: %s\n",
+ strerror(errno));
+ *target_msc = 0;
+ amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY,
+ swap_info);
+ return TRUE;
+ }
+
+ *target_msc = vbl.reply.sequence + flip;
+ *target_msc += amdgpu_get_interpolated_vblanks(crtc);
+ swap_info->frame = *target_msc;
+
+ return TRUE;
+ }
+
+ /*
+ * If we get here, target_msc has already passed or we don't have one,
+ * and we need to queue an event that will satisfy the divisor/remainder
+ * equation.
+ */
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+ if (flip == 0)
+ vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+
+ vbl.request.sequence = current_msc - (current_msc % divisor) +
+ remainder;
+
+ /*
+ * If the calculated deadline vbl.request.sequence is smaller than
+ * or equal to current_msc, it means we've passed the last point
+ * when effective onset frame seq could satisfy
+ * seq % divisor == remainder, so we need to wait for the next time
+ * this will happen.
+
+ * This comparison takes the 1 frame swap delay in pageflipping mode
+ * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
+ * if we are blitting/exchanging instead of flipping.
+ */
+ if (vbl.request.sequence <= current_msc)
+ vbl.request.sequence += divisor;
+ vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc);
+
+ /* Account for 1 frame extra pageflip delay if flip > 0 */
+ vbl.request.sequence -= flip;
+
+ vbl.request.signal = (unsigned long)swap_info;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "final get vblank counter failed: %s\n",
+ strerror(errno));
+ *target_msc = 0;
+ amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info);
+ return TRUE;
+ }
+
+ /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
+ *target_msc = vbl.reply.sequence + flip;
+ *target_msc += amdgpu_get_interpolated_vblanks(crtc);
+ swap_info->frame = *target_msc;
+
+ return TRUE;
+
+blit_fallback:
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = draw->width;
+ box.y2 = draw->height;
+ REGION_INIT(pScreen, &region, &box, 0);
+
+ amdgpu_dri2_copy_region(draw, &region, front, back);
+
+ DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
+ if (swap_info) {
+ ListDelDRI2ClientEvents(swap_info->client, &swap_info->link);
+ free(swap_info);
+ }
+
+ amdgpu_dri2_unref_buffer(front);
+ amdgpu_dri2_unref_buffer(back);
+
+ *target_msc = 0; /* offscreen, so zero out target vblank count */
+ return TRUE;
+}
+
+#endif /* USE_DRI2_SCHEDULING */
+
+Bool amdgpu_dri2_screen_init(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ DRI2InfoRec dri2_info = { 0 };
+#ifdef USE_DRI2_SCHEDULING
+ const char *driverNames[2];
+ Bool scheduling_works = TRUE;
+#endif
+
+ if (!info->dri2.available)
+ return FALSE;
+
+ info->dri2.device_name = drmGetDeviceNameFromFd(info->dri2.drm_fd);
+
+ dri2_info.driverName = SI_DRIVER_NAME;
+ dri2_info.fd = info->dri2.drm_fd;
+ dri2_info.deviceName = info->dri2.device_name;
+ dri2_info.version = DRI2INFOREC_VERSION;
+ dri2_info.CreateBuffer = amdgpu_dri2_create_buffer;
+ dri2_info.DestroyBuffer = amdgpu_dri2_destroy_buffer;
+ dri2_info.CopyRegion = amdgpu_dri2_copy_region;
+
+#ifdef USE_DRI2_SCHEDULING
+ if (info->drmmode.mode_res->count_crtcs > 2) {
+#ifdef DRM_CAP_VBLANK_HIGH_CRTC
+ uint64_t cap_value;
+
+ if (drmGetCap
+ (info->dri2.drm_fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "You need a newer kernel "
+ "for VBLANKs on CRTC > 1\n");
+ scheduling_works = FALSE;
+ } else if (!cap_value) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Your kernel does not "
+ "handle VBLANKs on CRTC > 1\n");
+ scheduling_works = FALSE;
+ }
+#else
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "You need to rebuild against a "
+ "newer libdrm to handle VBLANKs on CRTC > 1\n");
+ scheduling_works = FALSE;
+#endif
+ }
+
+ if (scheduling_works) {
+ dri2_info.version = 4;
+ dri2_info.ScheduleSwap = amdgpu_dri2_schedule_swap;
+ dri2_info.GetMSC = amdgpu_dri2_get_msc;
+ dri2_info.ScheduleWaitMSC = amdgpu_dri2_schedule_wait_msc;
+ dri2_info.numDrivers = AMDGPU_ARRAY_SIZE(driverNames);
+ dri2_info.driverNames = driverNames;
+ driverNames[0] = driverNames[1] = dri2_info.driverName;
+
+ if (DRI2InfoCnt == 0) {
+#if HAS_DIXREGISTERPRIVATEKEY
+ if (!dixRegisterPrivateKey(DRI2ClientEventsPrivateKey,
+ PRIVATE_CLIENT,
+ sizeof(DRI2ClientEventsRec)))
+ {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "DRI2 registering "
+ "private key to client failed\n");
+ return FALSE;
+ }
+#else
+ if (!dixRequestPrivate(DRI2ClientEventsPrivateKey,
+ sizeof(DRI2ClientEventsRec))) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "DRI2 requesting "
+ "private key to client failed\n");
+ return FALSE;
+ }
+#endif
+
+ AddCallback(&ClientStateCallback,
+ amdgpu_dri2_client_state_changed, 0);
+ }
+
+ DRI2InfoCnt++;
+ }
+#endif
+
+#if DRI2INFOREC_VERSION >= 9
+ dri2_info.version = 9;
+ dri2_info.CreateBuffer2 = amdgpu_dri2_create_buffer2;
+ dri2_info.DestroyBuffer2 = amdgpu_dri2_destroy_buffer2;
+ dri2_info.CopyRegion2 = amdgpu_dri2_copy_region2;
+#endif
+
+ info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info);
+ return info->dri2.enabled;
+}
+
+void amdgpu_dri2_close_screen(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+#ifdef USE_DRI2_SCHEDULING
+ if (--DRI2InfoCnt == 0)
+ DeleteCallback(&ClientStateCallback,
+ amdgpu_dri2_client_state_changed, 0);
+#endif
+
+ DRI2CloseScreen(pScreen);
+ drmFree(info->dri2.device_name);
+}
+
+#endif /* DRI2 */
diff --git a/src/amdgpu_dri2.h b/src/amdgpu_dri2.h
new file mode 100644
index 0000000..3ca9dd2
--- /dev/null
+++ b/src/amdgpu_dri2.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 Jerome Glisse
+ *
+ * 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.
+ */
+#ifndef AMDGPU_DRI2_H
+#define AMDGPU_DRI2_H
+
+#include <xorg-server.h>
+
+struct amdgpu_dri2 {
+ drmVersionPtr pKernelDRMVersion;
+ int drm_fd;
+ Bool available;
+ Bool enabled;
+ char *device_name;
+};
+
+#ifdef DRI2
+
+#include "dri2.h"
+Bool amdgpu_dri2_screen_init(ScreenPtr pScreen);
+void amdgpu_dri2_close_screen(ScreenPtr pScreen);
+
+int drmmode_get_crtc_id(xf86CrtcPtr crtc);
+void amdgpu_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data);
+void amdgpu_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data);
+
+#else
+
+static inline Bool amdgpu_dri2_screen_init(ScreenPtr pScreen)
+{
+ return FALSE;
+}
+
+static inline void amdgpu_dri2_close_screen(ScreenPtr pScreen)
+{
+}
+
+static inline void
+amdgpu_dri2_dummy_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data,
+ const char *name)
+{
+ static Bool warned;
+
+ if (!warned) {
+ ErrorF("%s called but DRI2 disabled at build time\n", name);
+ warned = TRUE;
+ }
+
+ free(event_data);
+}
+
+static inline void
+amdgpu_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data)
+{
+ amdgpu_dri2_dummy_event_handler(frame, tv_sec, tv_usec, event_data,
+ __func__);
+}
+
+static inline void
+amdgpu_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data)
+{
+ amdgpu_dri2_dummy_event_handler(frame, tv_sec, tv_usec, event_data,
+ __func__);
+}
+
+#endif
+
+#endif /* AMDGPU_DRI2_H */
diff --git a/src/amdgpu_drv.h b/src/amdgpu_drv.h
new file mode 100644
index 0000000..bb9ba14
--- /dev/null
+++ b/src/amdgpu_drv.h
@@ -0,0 +1,244 @@
+/*
+ * 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 _AMDGPU_DRV_H_
+#define _AMDGPU_DRV_H_
+
+#include <stdlib.h> /* For abs() */
+#include <unistd.h> /* For usleep() */
+#include <sys/time.h> /* For gettimeofday() */
+
+#include "config.h"
+
+#include "xf86str.h"
+#include "compiler.h"
+
+/* PCI support */
+#include "xf86Pci.h"
+
+#include "fb.h"
+
+#include "amdgpu_glamor.h"
+
+/* Cursor Support */
+#include "xf86Cursor.h"
+
+/* DDC support */
+#include "xf86DDC.h"
+
+/* Xv support */
+#include "xf86xv.h"
+
+#include "amdgpu_probe.h"
+
+/* DRI support */
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+
+#ifdef DAMAGE
+#include "damage.h"
+#include "globals.h"
+#endif
+
+#include "xf86Crtc.h"
+#include "X11/Xatom.h"
+
+#include "amdgpu_dri2.h"
+#include "drmmode_display.h"
+#include "amdgpu_bo_helper.h"
+
+/* Render support */
+#ifdef RENDER
+#include "picturestr.h"
+#endif
+
+#include "compat-api.h"
+
+#include "simple_list.h"
+#include "amdpciids.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a)>(b)?(b):(a))
+#endif
+
+#if HAVE_BYTESWAP_H
+#include <byteswap.h>
+#elif defined(USE_SYS_ENDIAN_H)
+#include <sys/endian.h>
+#else
+#define bswap_16(value) \
+ ((((value) & 0xff) << 8) | ((value) >> 8))
+
+#define bswap_32(value) \
+ (((uint32_t)bswap_16((uint16_t)((value) & 0xffff)) << 16) | \
+ (uint32_t)bswap_16((uint16_t)((value) >> 16)))
+
+#define bswap_64(value) \
+ (((uint64_t)bswap_32((uint32_t)((value) & 0xffffffff)) \
+ << 32) | \
+ (uint64_t)bswap_32((uint32_t)((value) >> 32)))
+#endif
+
+#if X_BYTE_ORDER == X_BIG_ENDIAN
+#define le32_to_cpu(x) bswap_32(x)
+#define le16_to_cpu(x) bswap_16(x)
+#define cpu_to_le32(x) bswap_32(x)
+#define cpu_to_le16(x) bswap_16(x)
+#else
+#define le32_to_cpu(x) (x)
+#define le16_to_cpu(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_le16(x) (x)
+#endif
+
+/* Provide substitutes for gcc's __FUNCTION__ on other compilers */
+#if !defined(__GNUC__) && !defined(__FUNCTION__)
+#define __FUNCTION__ __func__ /* C99 */
+#endif
+
+typedef enum {
+ OPTION_NOACCEL,
+ OPTION_SW_CURSOR,
+ OPTION_PAGE_FLIP,
+#ifdef RENDER
+ OPTION_SUBPIXEL_ORDER,
+#endif
+ OPTION_ZAPHOD_HEADS,
+ OPTION_ACCEL_METHOD
+} AMDGPUOpts;
+
+#define AMDGPU_VSYNC_TIMEOUT 20000 /* Maximum wait for VSYNC (in usecs) */
+
+/* Buffer are aligned on 4096 byte boundaries */
+#define AMDGPU_GPU_PAGE_SIZE 4096
+#define AMDGPU_BUFFER_ALIGN (AMDGPU_GPU_PAGE_SIZE - 1)
+
+#define xFixedToFloat(f) (((float) (f)) / 65536)
+
+#define AMDGPU_LOGLEVEL_DEBUG 4
+
+/* Other macros */
+#define AMDGPU_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define AMDGPU_ALIGN(x,bytes) (((x) + ((bytes) - 1)) & ~((bytes) - 1))
+#define AMDGPUPTR(pScrn) ((AMDGPUInfoPtr)(pScrn)->driverPrivate)
+
+#define CURSOR_WIDTH 64
+#define CURSOR_HEIGHT 64
+
+#define CURSOR_WIDTH_CIK 128
+#define CURSOR_HEIGHT_CIK 128
+
+#define AMDGPU_BO_FLAGS_GBM 0x1
+
+struct amdgpu_buffer {
+ union {
+ struct gbm_bo *gbm;
+ amdgpu_bo_handle amdgpu;
+ } bo;
+ void *cpu_ptr;
+ uint32_t ref_count;
+ uint32_t flags;
+};
+
+typedef struct {
+ EntityInfoPtr pEnt;
+ pciVideoPtr PciInfo;
+ int Chipset;
+ AMDGPUChipFamily ChipFamily;
+ struct gbm_device *gbm;
+
+ Bool(*CloseScreen) (CLOSE_SCREEN_ARGS_DECL);
+
+ void (*BlockHandler) (BLOCKHANDLER_ARGS_DECL);
+
+ int pix24bpp; /* Depth of pixmap for 24bpp fb */
+ Bool dac6bits; /* Use 6 bit DAC? */
+
+ int pixel_bytes;
+
+ Bool directRenderingEnabled;
+ struct amdgpu_dri2 dri2;
+
+ /* accel */
+ Bool use_glamor;
+
+ /* general */
+ OptionInfoPtr Options;
+
+ DisplayModePtr currentMode;
+
+ CreateScreenResourcesProcPtr CreateScreenResources;
+
+ Bool IsSecondary;
+ Bool IsPrimary;
+
+ Bool shadow_fb;
+ void *fb_shadow;
+ struct amdgpu_buffer *front_buffer;
+ struct amdgpu_buffer *cursor_buffer[32];
+
+ uint64_t vram_size;
+ uint64_t gart_size;
+ drmmode_rec drmmode;
+ Bool drmmode_inited;
+ /* r6xx+ tile config */
+ Bool have_tiling_info;
+ int group_bytes;
+
+ /* kms pageflipping */
+ Bool allowPageFlip;
+
+ /* cursor size */
+ int cursor_w;
+ int cursor_h;
+} AMDGPUInfoRec, *AMDGPUInfoPtr;
+
+
+/* amdgpu_video.c */
+extern void AMDGPUInitVideo(ScreenPtr pScreen);
+extern void AMDGPUResetVideo(ScrnInfoPtr pScrn);
+extern xf86CrtcPtr amdgpu_pick_best_crtc(ScrnInfoPtr pScrn,
+ Bool consider_disabled,
+ int x1, int x2, int y1, int y2);
+
+extern AMDGPUEntPtr AMDGPUEntPriv(ScrnInfoPtr pScrn);
+
+drmVBlankSeqType amdgpu_populate_vbl_request_type(xf86CrtcPtr crtc);
+
+#endif /* _AMDGPU_DRV_H_ */
diff --git a/src/amdgpu_glamor.c b/src/amdgpu_glamor.c
new file mode 100644
index 0000000..d0e2c44
--- /dev/null
+++ b/src/amdgpu_glamor.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright © 2011 Intel Corporation.
+ * 2012 Advanced Micro Devices, 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 (including
+ * the next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xf86.h>
+#define GLAMOR_FOR_XORG 1
+#include <glamor.h>
+
+#include "amdgpu_bo_helper.h"
+#include "amdgpu_pixmap.h"
+
+#include <gbm.h>
+
+#if HAS_DEVPRIVATEKEYREC
+DevPrivateKeyRec amdgpu_pixmap_index;
+#else
+int amdgpu_pixmap_index;
+#endif
+
+void amdgpu_glamor_exchange_buffers(PixmapPtr src, PixmapPtr dst)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(dst->drawable.pScreen));
+
+ if (!info->use_glamor)
+ return;
+ glamor_egl_exchange_buffers(src, dst);
+}
+
+Bool amdgpu_glamor_create_screen_resources(ScreenPtr screen)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ union gbm_bo_handle bo_handle;
+
+ if (!info->use_glamor)
+ return TRUE;
+
+ if (!glamor_glyphs_init(screen))
+ return FALSE;
+
+ bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm);
+ if (!glamor_egl_create_textured_screen_ext(screen,
+ bo_handle.u32,
+ scrn->displayWidth *
+ info->pixel_bytes, NULL)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+Bool amdgpu_glamor_pre_init(ScrnInfoPtr scrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ pointer glamor_module;
+ CARD32 version;
+
+ if (!info->dri2.available)
+ return FALSE;
+
+ if (scrn->depth < 24) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "glamor requires depth >= 24, disabling.\n");
+ return FALSE;
+ }
+#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,0,0,0)
+ if (!xf86LoaderCheckSymbol("glamor_egl_init")) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "glamor requires Load \"glamoregl\" in "
+ "Section \"Module\", disabling.\n");
+ return FALSE;
+ }
+#endif
+
+ /* Load glamor module */
+ if ((glamor_module = xf86LoadSubModule(scrn, GLAMOR_EGL_MODULE_NAME))) {
+ version = xf86GetModuleVersion(glamor_module);
+ if (version < MODULE_VERSION_NUMERIC(0, 3, 1)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Incompatible glamor version, required >= 0.3.0.\n");
+ return FALSE;
+ } else {
+ if (glamor_egl_init(scrn, info->dri2.drm_fd)) {
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "glamor detected, initialising EGL layer.\n");
+ } else {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "glamor detected, failed to initialize EGL.\n");
+ return FALSE;
+ }
+ }
+ } else {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR, "glamor not available\n");
+ return FALSE;
+ }
+
+ info->use_glamor = TRUE;
+
+ return TRUE;
+}
+
+Bool amdgpu_glamor_create_textured_pixmap(PixmapPtr pixmap)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ struct amdgpu_pixmap *priv;
+ union gbm_bo_handle bo_handle;
+
+ if ((info->use_glamor) == 0)
+ return TRUE;
+
+ priv = amdgpu_get_pixmap_private(pixmap);
+ if (!priv->stride) {
+ priv->stride = pixmap->devKind;
+ }
+
+ bo_handle = gbm_bo_get_handle(priv->bo->bo.gbm);
+ if (glamor_egl_create_textured_pixmap(pixmap, bo_handle.u32,
+ priv->stride)) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+Bool amdgpu_glamor_pixmap_is_offscreen(PixmapPtr pixmap)
+{
+ struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap);
+ return priv && priv->bo;
+}
+
+#ifndef CREATE_PIXMAP_USAGE_SHARED
+#define CREATE_PIXMAP_USAGE_SHARED AMDGPU_CREATE_PIXMAP_DRI2
+#endif
+
+#define AMDGPU_CREATE_PIXMAP_SHARED(usage) \
+ ((usage) & AMDGPU_CREATE_PIXMAP_DRI2 || (usage) == CREATE_PIXMAP_USAGE_SHARED)
+
+static PixmapPtr
+amdgpu_glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth,
+ unsigned usage)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct amdgpu_pixmap *priv;
+ PixmapPtr pixmap, new_pixmap = NULL;
+
+ if (!AMDGPU_CREATE_PIXMAP_SHARED(usage)) {
+ pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
+ if (pixmap)
+ return pixmap;
+ }
+
+ if (w > 32767 || h > 32767)
+ return NullPixmap;
+
+ if (depth == 1)
+ return fbCreatePixmap(screen, w, h, depth, usage);
+
+ if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32)
+ return fbCreatePixmap(screen, w, h, depth, usage);
+
+ pixmap = fbCreatePixmap(screen, 0, 0, depth, usage);
+ if (pixmap == NullPixmap)
+ return pixmap;
+
+ if (w && h) {
+ priv = calloc(1, sizeof(struct amdgpu_pixmap));
+ if (priv == NULL)
+ goto fallback_pixmap;
+
+ priv->bo = amdgpu_alloc_pixmap_bo(scrn, w, h, depth, usage,
+ pixmap->drawable.bitsPerPixel,
+ &priv->stride);
+ if (!priv->bo)
+ goto fallback_priv;
+
+ amdgpu_set_pixmap_private(pixmap, priv);
+
+ screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, priv->stride,
+ NULL);
+
+ if (!amdgpu_glamor_create_textured_pixmap(pixmap))
+ goto fallback_glamor;
+ }
+
+ return pixmap;
+
+fallback_glamor:
+ if (AMDGPU_CREATE_PIXMAP_SHARED(usage)) {
+ /* XXX need further work to handle the DRI2 failure case.
+ * Glamor don't know how to handle a BO only pixmap. Put
+ * a warning indicator here.
+ */
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "Failed to create textured DRI2/PRIME pixmap.");
+ return pixmap;
+ }
+ /* Create textured pixmap failed means glamor failed to
+ * create a texture from current BO for some reasons. We turn
+ * to create a new glamor pixmap and clean up current one.
+ * One thing need to be noted, this new pixmap doesn't
+ * has a priv and bo attached to it. It's glamor's responsbility
+ * to take care of it. Glamor will mark this new pixmap as a
+ * texture only pixmap and will never fallback to DDX layer
+ * afterwards.
+ */
+ new_pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
+ amdgpu_bo_unref(&priv->bo);
+fallback_priv:
+ free(priv);
+fallback_pixmap:
+ fbDestroyPixmap(pixmap);
+ if (new_pixmap)
+ return new_pixmap;
+ else
+ return fbCreatePixmap(screen, w, h, depth, usage);
+}
+
+static Bool amdgpu_glamor_destroy_pixmap(PixmapPtr pixmap)
+{
+ if (pixmap->refcnt == 1) {
+ glamor_egl_destroy_textured_pixmap(pixmap);
+ amdgpu_set_pixmap_bo(pixmap, NULL);
+ }
+ fbDestroyPixmap(pixmap);
+ return TRUE;
+}
+
+#ifdef AMDGPU_PIXMAP_SHARING
+
+static Bool
+amdgpu_glamor_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave,
+ void **handle_p)
+{
+ struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap);
+
+ if (!priv)
+ return FALSE;
+
+ return amdgpu_share_pixmap_backing(priv->bo, handle_p);
+}
+
+static Bool
+amdgpu_glamor_set_shared_pixmap_backing(PixmapPtr pixmap, void *handle)
+{
+ ScreenPtr screen = pixmap->drawable.pScreen;
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+ struct amdgpu_pixmap *priv;
+
+ if (!amdgpu_set_shared_pixmap_backing(pixmap, handle))
+ return FALSE;
+
+ priv = amdgpu_get_pixmap_private(pixmap);
+ priv->stride = pixmap->devKind;
+
+ if (!amdgpu_glamor_create_textured_pixmap(pixmap)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to get PRIME drawable for glamor pixmap.\n");
+ return FALSE;
+ }
+
+ screen->ModifyPixmapHeader(pixmap,
+ pixmap->drawable.width,
+ pixmap->drawable.height,
+ 0, 0, priv->stride, NULL);
+
+ return TRUE;
+}
+
+#endif /* AMDGPU_PIXMAP_SHARING */
+
+Bool amdgpu_glamor_init(ScreenPtr screen)
+{
+ ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+
+ if (!glamor_init
+ (screen,
+ GLAMOR_INVERTED_Y_AXIS | GLAMOR_USE_EGL_SCREEN | GLAMOR_USE_SCREEN
+#ifdef GLAMOR_NO_DRI3
+ | GLAMOR_NO_DRI3
+#endif
+ | GLAMOR_USE_PICTURE_SCREEN)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to initialize glamor.\n");
+ return FALSE;
+ }
+
+ if (!glamor_egl_init_textured_pixmap(screen)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to initialize textured pixmap of screen for glamor.\n");
+ return FALSE;
+ }
+#if HAS_DIXREGISTERPRIVATEKEY
+ if (!dixRegisterPrivateKey(&amdgpu_pixmap_index, PRIVATE_PIXMAP, 0))
+#else
+ if (!dixRequestPrivate(&amdgpu_pixmap_index, 0))
+#endif
+ return FALSE;
+
+ screen->CreatePixmap = amdgpu_glamor_create_pixmap;
+ screen->DestroyPixmap = amdgpu_glamor_destroy_pixmap;
+#ifdef AMDGPU_PIXMAP_SHARING
+ screen->SharePixmapBacking = amdgpu_glamor_share_pixmap_backing;
+ screen->SetSharedPixmapBacking =
+ amdgpu_glamor_set_shared_pixmap_backing;
+#endif
+
+ xf86DrvMsg(scrn->scrnIndex, X_INFO, "Use GLAMOR acceleration.\n");
+ return TRUE;
+}
+
+void amdgpu_glamor_flush(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ if (info->use_glamor)
+ glamor_block_handler(pScrn->pScreen);
+}
+
+XF86VideoAdaptorPtr amdgpu_glamor_xv_init(ScreenPtr pScreen, int num_adapt)
+{
+ return glamor_xv_init(pScreen, num_adapt);
+}
diff --git a/src/amdgpu_glamor.h b/src/amdgpu_glamor.h
new file mode 100644
index 0000000..bc4c628
--- /dev/null
+++ b/src/amdgpu_glamor.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2011 Intel Corporation.
+ * 2012 Advanced Micro Devices, 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 (including
+ * the next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef AMDGPU_GLAMOR_H
+#define AMDGPU_GLAMOR_H
+
+#include "xf86xv.h"
+
+Bool amdgpu_glamor_pre_init(ScrnInfoPtr scrn);
+Bool amdgpu_glamor_init(ScreenPtr screen);
+Bool amdgpu_glamor_create_screen_resources(ScreenPtr screen);
+void amdgpu_glamor_free_screen(int scrnIndex, int flags);
+
+void amdgpu_glamor_flush(ScrnInfoPtr pScrn);
+
+Bool amdgpu_glamor_create_textured_pixmap(PixmapPtr pixmap);
+void amdgpu_glamor_exchange_buffers(PixmapPtr src, PixmapPtr dst);
+
+Bool amdgpu_glamor_pixmap_is_offscreen(PixmapPtr pixmap);
+
+XF86VideoAdaptorPtr amdgpu_glamor_xv_init(ScreenPtr pScreen, int num_adapt);
+
+#endif /* AMDGPU_GLAMOR_H */
diff --git a/src/amdgpu_kms.c b/src/amdgpu_kms.c
new file mode 100644
index 0000000..4612000
--- /dev/null
+++ b/src/amdgpu_kms.c
@@ -0,0 +1,1100 @@
+/*
+ * 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 (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <sys/ioctl.h>
+/* Driver data structures */
+#include "amdgpu_drv.h"
+#include "amdgpu_probe.h"
+#include "micmap.h"
+
+#include "amdgpu_version.h"
+#include "shadow.h"
+
+#include "amdpciids.h"
+
+/* DPMS */
+#ifdef HAVE_XEXTPROTO_71
+#include <X11/extensions/dpmsconst.h>
+#else
+#define DPMS_SERVER
+#include <X11/extensions/dpms.h>
+#endif
+
+#include "amdgpu_chipinfo_gen.h"
+#include "amdgpu_bo_helper.h"
+#include "amdgpu_pixmap.h"
+
+#include <gbm.h>
+
+extern SymTabRec AMDGPUChipsets[];
+static Bool amdgpu_setup_kernel_mem(ScreenPtr pScreen);
+
+const OptionInfoRec AMDGPUOptions_KMS[] = {
+ {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_PAGE_FLIP, "EnablePageFlip", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_SUBPIXEL_ORDER, "SubPixelOrder", OPTV_ANYSTR, {0}, FALSE},
+ {OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE},
+ {OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE},
+ {-1, NULL, OPTV_NONE, {0}, FALSE}
+};
+
+const OptionInfoRec *AMDGPUOptionsWeak(void)
+{
+ return AMDGPUOptions_KMS;
+}
+
+extern _X_EXPORT int gAMDGPUEntityIndex;
+
+static int getAMDGPUEntityIndex(void)
+{
+ return gAMDGPUEntityIndex;
+}
+
+AMDGPUEntPtr AMDGPUEntPriv(ScrnInfoPtr pScrn)
+{
+ DevUnion *pPriv;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ pPriv = xf86GetEntityPrivate(info->pEnt->index, getAMDGPUEntityIndex());
+ return pPriv->ptr;
+}
+
+/* Allocate our private AMDGPUInfoRec */
+static Bool AMDGPUGetRec(ScrnInfoPtr pScrn)
+{
+ if (pScrn->driverPrivate)
+ return TRUE;
+
+ pScrn->driverPrivate = xnfcalloc(sizeof(AMDGPUInfoRec), 1);
+ return TRUE;
+}
+
+/* Free our private AMDGPUInfoRec */
+static void AMDGPUFreeRec(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info;
+
+ if (!pScrn || !pScrn->driverPrivate)
+ return;
+
+ info = AMDGPUPTR(pScrn);
+
+ if (info->dri2.drm_fd > 0) {
+ DevUnion *pPriv;
+ AMDGPUEntPtr pAMDGPUEnt;
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ getAMDGPUEntityIndex());
+
+ pAMDGPUEnt = pPriv->ptr;
+ pAMDGPUEnt->fd_ref--;
+ if (!pAMDGPUEnt->fd_ref) {
+ amdgpu_device_deinitialize(pAMDGPUEnt->pDev);
+ drmClose(pAMDGPUEnt->fd);
+ pAMDGPUEnt->fd = 0;
+ }
+ }
+
+ free(pScrn->driverPrivate);
+ pScrn->driverPrivate = NULL;
+}
+
+static void *amdgpuShadowWindow(ScreenPtr screen, CARD32 row, CARD32 offset,
+ int mode, CARD32 * size, void *closure)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ int stride;
+
+ stride = (pScrn->displayWidth * pScrn->bitsPerPixel) / 8;
+ *size = stride;
+
+ return ((uint8_t *) info->front_buffer->cpu_ptr + row * stride + offset);
+}
+
+static Bool AMDGPUCreateScreenResources_KMS(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ PixmapPtr pixmap;
+
+ pScreen->CreateScreenResources = info->CreateScreenResources;
+ if (!(*pScreen->CreateScreenResources) (pScreen))
+ return FALSE;
+ pScreen->CreateScreenResources = AMDGPUCreateScreenResources_KMS;
+
+ if (!drmmode_set_desired_modes(pScrn, &info->drmmode))
+ return FALSE;
+
+ drmmode_uevent_init(pScrn, &info->drmmode);
+
+ if (info->shadow_fb) {
+ pixmap = pScreen->GetScreenPixmap(pScreen);
+
+ if (!shadowAdd(pScreen, pixmap, shadowUpdatePackedWeak(),
+ amdgpuShadowWindow, 0, NULL))
+ return FALSE;
+ }
+
+ if (info->dri2.enabled || info->use_glamor) {
+ if (info->front_buffer) {
+ PixmapPtr pPix = pScreen->GetScreenPixmap(pScreen);
+ amdgpu_set_pixmap_bo(pPix, info->front_buffer);
+ }
+ }
+
+ if (info->use_glamor)
+ amdgpu_glamor_create_screen_resources(pScreen);
+
+ return TRUE;
+}
+
+#ifdef AMDGPU_PIXMAP_SHARING
+static void redisplay_dirty(ScreenPtr screen, PixmapDirtyUpdatePtr dirty)
+{
+ RegionRec pixregion;
+
+ PixmapRegionInit(&pixregion, dirty->slave_dst);
+ DamageRegionAppend(&dirty->slave_dst->drawable, &pixregion);
+ PixmapSyncDirtyHelper(dirty, &pixregion);
+
+ DamageRegionProcessPending(&dirty->slave_dst->drawable);
+ RegionUninit(&pixregion);
+}
+
+static void amdgpu_dirty_update(ScreenPtr screen)
+{
+ RegionPtr region;
+ PixmapDirtyUpdatePtr ent;
+
+ if (xorg_list_is_empty(&screen->pixmap_dirty_list))
+ return;
+
+ xorg_list_for_each_entry(ent, &screen->pixmap_dirty_list, ent) {
+ region = DamageRegion(ent->damage);
+ if (RegionNotEmpty(region)) {
+ redisplay_dirty(screen, ent);
+ DamageEmpty(ent->damage);
+ }
+ }
+}
+#endif
+
+static void AMDGPUBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
+{
+ SCREEN_PTR(arg);
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ pScreen->BlockHandler = info->BlockHandler;
+ (*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
+ pScreen->BlockHandler = AMDGPUBlockHandler_KMS;
+
+ if (info->use_glamor)
+ amdgpu_glamor_flush(pScrn);
+
+#ifdef AMDGPU_PIXMAP_SHARING
+ amdgpu_dirty_update(pScreen);
+#endif
+}
+
+static void
+amdgpu_flush_callback(CallbackListPtr * list,
+ pointer user_data, pointer call_data)
+{
+ ScrnInfoPtr pScrn = user_data;
+
+ if (pScrn->vtSema) {
+ amdgpu_glamor_flush(pScrn);
+ }
+}
+
+static Bool AMDGPUIsAccelWorking(ScrnInfoPtr pScrn)
+{
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ uint32_t accel_working;
+
+ if (amdgpu_query_info(pAMDGPUEnt->pDev, AMDGPU_INFO_ACCEL_WORKING,
+ sizeof(accel_working), &accel_working) != 0)
+ return FALSE;
+
+ return accel_working;
+}
+
+/* This is called by AMDGPUPreInit to set up the default visual */
+static Bool AMDGPUPreInitVisual(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support32bppFb))
+ return FALSE;
+
+ switch (pScrn->depth) {
+ case 8:
+ case 15:
+ case 16:
+ case 24:
+ break;
+
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Given depth (%d) is not supported by %s driver\n",
+ pScrn->depth, AMDGPU_DRIVER_NAME);
+ return FALSE;
+ }
+
+ xf86PrintDepthBpp(pScrn);
+
+ info->pix24bpp = xf86GetBppFromDepth(pScrn, pScrn->depth);
+ info->pixel_bytes = pScrn->bitsPerPixel / 8;
+
+ if (info->pix24bpp == 24) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Amdgpu does NOT support 24bpp\n");
+ return FALSE;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Pixel depth = %d bits stored in %d byte%s (%d bpp pixmaps)\n",
+ pScrn->depth,
+ info->pixel_bytes,
+ info->pixel_bytes > 1 ? "s" : "", info->pix24bpp);
+
+ if (!xf86SetDefaultVisual(pScrn, -1))
+ return FALSE;
+
+ if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Default visual (%s) is not supported at depth %d\n",
+ xf86GetVisualName(pScrn->defaultVisual),
+ pScrn->depth);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* This is called by AMDGPUPreInit to handle all color weight issues */
+static Bool AMDGPUPreInitWeight(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ /* Save flag for 6 bit DAC to use for
+ setting CRTC registers. Otherwise use
+ an 8 bit DAC, even if xf86SetWeight sets
+ pScrn->rgbBits to some value other than
+ 8. */
+ info->dac6bits = FALSE;
+
+ if (pScrn->depth > 8) {
+ rgb defaultWeight = { 0, 0, 0 };
+
+ if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
+ return FALSE;
+ } else {
+ pScrn->rgbBits = 8;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using %d bits per RGB (%d bit DAC)\n",
+ pScrn->rgbBits, info->dac6bits ? 6 : 8);
+
+ return TRUE;
+}
+
+static Bool AMDGPUPreInitAccel_KMS(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ const char *accel_method;
+
+ if (!xf86ReturnOptValBool(info->Options, OPTION_NOACCEL,
+ info->ChipFamily == CHIP_FAMILY_HAWAII) &&
+ AMDGPUIsAccelWorking(pScrn)) {
+ Bool use_glamor = TRUE;
+
+ accel_method = xf86GetOptValString(info->Options, OPTION_ACCEL_METHOD);
+ if ((accel_method && !strcmp(accel_method, "none")))
+ use_glamor = FALSE;
+
+#ifdef DRI2
+ info->dri2.available = ! !xf86LoadSubModule(pScrn, "dri2");
+#endif
+
+ if (info->dri2.available)
+ info->gbm = gbm_create_device(info->dri2.drm_fd);
+ if (info->gbm == NULL)
+ info->dri2.available = FALSE;
+
+ if (use_glamor &&
+ amdgpu_glamor_pre_init(pScrn))
+ return TRUE;
+
+ if (info->dri2.available)
+ return TRUE;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "GPU accel disabled or not working, using shadowfb for KMS\n");
+ info->shadow_fb = TRUE;
+ if (!xf86LoadSubModule(pScrn, "shadow"))
+ info->shadow_fb = FALSE;
+
+ return TRUE;
+}
+
+static Bool AMDGPUPreInitChipType_KMS(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ int i;
+
+ info->Chipset = PCI_DEV_DEVICE_ID(info->PciInfo);
+ pScrn->chipset =
+ (char *)xf86TokenToString(AMDGPUChipsets, info->Chipset);
+ if (!pScrn->chipset) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "ChipID 0x%04x is not recognized\n", info->Chipset);
+ return FALSE;
+ }
+
+ if (info->Chipset < 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Chipset \"%s\" is not recognized\n",
+ pScrn->chipset);
+ return FALSE;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
+ "Chipset: \"%s\" (ChipID = 0x%04x)\n",
+ pScrn->chipset, info->Chipset);
+
+ for (i = 0; i < sizeof(AMDGPUCards) / sizeof(AMDGPUCardInfo); i++) {
+ if (info->Chipset == AMDGPUCards[i].pci_device_id) {
+ AMDGPUCardInfo *card = &AMDGPUCards[i];
+ info->ChipFamily = card->chip_family;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static void amdgpu_reference_drm_fd(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+
+ info->dri2.drm_fd = pAMDGPUEnt->fd;
+ pAMDGPUEnt->fd_ref++;
+ info->drmmode.fd = info->dri2.drm_fd;
+}
+
+static Bool amdgpu_get_tile_config(ScrnInfoPtr pScrn)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ struct amdgpu_gpu_info gpu_info;
+
+ memset(&gpu_info, 0, sizeof(gpu_info));
+ amdgpu_query_gpu_info(pAMDGPUEnt->pDev, &gpu_info);
+
+ switch ((gpu_info.gb_addr_cfg & 0x70) >> 4) {
+ case 0:
+ info->group_bytes = 256;
+ break;
+ case 1:
+ info->group_bytes = 512;
+ break;
+ default:
+ return FALSE;
+ }
+
+ info->have_tiling_info = TRUE;
+ return TRUE;
+}
+
+static void AMDGPUSetupCapabilities(ScrnInfoPtr pScrn)
+{
+#ifdef AMDGPU_PIXMAP_SHARING
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ uint64_t value;
+ int ret;
+
+ pScrn->capabilities = 0;
+ ret = drmGetCap(info->dri2.drm_fd, DRM_CAP_PRIME, &value);
+ if (ret == 0) {
+ if (value & DRM_PRIME_CAP_EXPORT)
+ pScrn->capabilities |=
+ RR_Capability_SourceOutput |
+ RR_Capability_SinkOffload;
+ if (value & DRM_PRIME_CAP_IMPORT)
+ pScrn->capabilities |=
+ RR_Capability_SourceOffload |
+ RR_Capability_SinkOutput;
+ }
+#endif
+}
+
+Bool AMDGPUPreInit_KMS(ScrnInfoPtr pScrn, int flags)
+{
+ AMDGPUInfoPtr info;
+ AMDGPUEntPtr pAMDGPUEnt;
+ DevUnion *pPriv;
+ Gamma zeros = { 0.0, 0.0, 0.0 };
+ int cpp;
+ uint64_t heap_size = 0;
+ uint64_t max_allocation = 0;
+
+ if (flags & PROBE_DETECT)
+ return TRUE;
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPUPreInit_KMS\n");
+ if (pScrn->numEntities != 1)
+ return FALSE;
+ if (!AMDGPUGetRec(pScrn))
+ return FALSE;
+
+ info = AMDGPUPTR(pScrn);
+ info->IsSecondary = FALSE;
+ info->IsPrimary = FALSE;
+ info->pEnt =
+ xf86GetEntityInfo(pScrn->entityList[pScrn->numEntities - 1]);
+ if (info->pEnt->location.type != BUS_PCI
+#ifdef XSERVER_PLATFORM_BUS
+ && info->pEnt->location.type != BUS_PLATFORM
+#endif
+ )
+ goto fail;
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ getAMDGPUEntityIndex());
+ pAMDGPUEnt = pPriv->ptr;
+
+ if (xf86IsEntityShared(pScrn->entityList[0])) {
+ if (xf86IsPrimInitDone(pScrn->entityList[0])) {
+ info->IsSecondary = TRUE;
+ pAMDGPUEnt->pSecondaryScrn = pScrn;
+ } else {
+ info->IsPrimary = TRUE;
+ xf86SetPrimInitDone(pScrn->entityList[0]);
+ pAMDGPUEnt->pPrimaryScrn = pScrn;
+ pAMDGPUEnt->HasSecondary = FALSE;
+ }
+ }
+
+ info->PciInfo = xf86GetPciInfoForEntity(info->pEnt->index);
+ pScrn->monitor = pScrn->confScreen->monitor;
+
+ if (!AMDGPUPreInitVisual(pScrn))
+ goto fail;
+
+ xf86CollectOptions(pScrn, NULL);
+ if (!(info->Options = malloc(sizeof(AMDGPUOptions_KMS))))
+ goto fail;
+
+ memcpy(info->Options, AMDGPUOptions_KMS, sizeof(AMDGPUOptions_KMS));
+ xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, info->Options);
+
+ if (!AMDGPUPreInitWeight(pScrn))
+ goto fail;
+
+ if (!AMDGPUPreInitChipType_KMS(pScrn))
+ goto fail;
+
+ amdgpu_reference_drm_fd(pScrn);
+
+ info->dri2.available = FALSE;
+ info->dri2.enabled = FALSE;
+ info->dri2.pKernelDRMVersion = drmGetVersion(info->dri2.drm_fd);
+ if (info->dri2.pKernelDRMVersion == NULL) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "AMDGPUDRIGetVersion failed to get the DRM version\n");
+ goto fail;
+ }
+
+ if (!AMDGPUPreInitAccel_KMS(pScrn))
+ goto fail;
+
+ AMDGPUSetupCapabilities(pScrn);
+
+ /* don't enable tiling if accel is not enabled */
+ if (info->use_glamor) {
+ /* set default group bytes, overridden by kernel info below */
+ info->group_bytes = 256;
+ info->have_tiling_info = FALSE;
+ amdgpu_get_tile_config(pScrn);
+ }
+
+ info->allowPageFlip = xf86ReturnOptValBool(info->Options,
+ OPTION_PAGE_FLIP,
+ TRUE);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "KMS Pageflipping: %sabled\n",
+ info->allowPageFlip ? "en" : "dis");
+
+ if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) ==
+ FALSE) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Kernel modesetting setup failed\n");
+ goto fail;
+ }
+
+ if (info->drmmode.mode_res->count_crtcs == 1)
+ pAMDGPUEnt->HasCRTC2 = FALSE;
+ else
+ pAMDGPUEnt->HasCRTC2 = TRUE;
+
+ info->cursor_w = CURSOR_WIDTH_CIK;
+ info->cursor_h = CURSOR_HEIGHT_CIK;
+
+ amdgpu_query_heap_size(pAMDGPUEnt->pDev, AMDGPU_GEM_DOMAIN_GTT,
+ &heap_size, &max_allocation);
+ info->gart_size = heap_size;
+ amdgpu_query_heap_size(pAMDGPUEnt->pDev, AMDGPU_GEM_DOMAIN_VRAM,
+ &heap_size, &max_allocation);
+ info->vram_size = max_allocation;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "mem size init: gart size :%llx vram size: s:%llx visible:%llx\n",
+ (unsigned long long)info->gart_size,
+ (unsigned long long)heap_size,
+ (unsigned long long)max_allocation);
+
+ cpp = pScrn->bitsPerPixel / 8;
+ pScrn->displayWidth =
+ AMDGPU_ALIGN(pScrn->virtualX, drmmode_get_pitch_align(pScrn, cpp));
+
+ /* Set display resolution */
+ xf86SetDpi(pScrn, 0, 0);
+
+ /* Get ScreenInit function */
+ if (!xf86LoadSubModule(pScrn, "fb"))
+ return FALSE;
+
+ if (!xf86SetGamma(pScrn, zeros))
+ return FALSE;
+
+ if (!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) {
+ if (!xf86LoadSubModule(pScrn, "ramdac"))
+ return FALSE;
+ }
+
+ if (pScrn->modes == NULL
+#ifdef XSERVER_PLATFORM_BUS
+ && !pScrn->is_gpu
+#endif
+ ) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
+ goto fail;
+ }
+
+ return TRUE;
+fail:
+ AMDGPUFreeRec(pScrn);
+ return FALSE;
+
+}
+
+static Bool AMDGPUCursorInit_KMS(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ return xf86_cursors_init(pScreen, info->cursor_w, info->cursor_h,
+ (HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
+ HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
+ HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
+ HARDWARE_CURSOR_UPDATE_UNHIDDEN |
+ HARDWARE_CURSOR_ARGB));
+}
+
+void AMDGPUBlank(ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ xf86OutputPtr output;
+ xf86CrtcPtr crtc;
+ int o, c;
+
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ crtc = xf86_config->crtc[c];
+ for (o = 0; o < xf86_config->num_output; o++) {
+ output = xf86_config->output[o];
+ if (output->crtc != crtc)
+ continue;
+
+ output->funcs->dpms(output, DPMSModeOff);
+ }
+ crtc->funcs->dpms(crtc, DPMSModeOff);
+ }
+}
+
+void AMDGPUUnblank(ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ xf86OutputPtr output;
+ xf86CrtcPtr crtc;
+ int o, c;
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ crtc = xf86_config->crtc[c];
+ if (!crtc->enabled)
+ continue;
+ crtc->funcs->dpms(crtc, DPMSModeOn);
+ for (o = 0; o < xf86_config->num_output; o++) {
+ output = xf86_config->output[o];
+ if (output->crtc != crtc)
+ continue;
+ output->funcs->dpms(output, DPMSModeOn);
+ }
+ }
+}
+
+static Bool AMDGPUSaveScreen_KMS(ScreenPtr pScreen, int mode)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ Bool unblank;
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPUSaveScreen(%d)\n", mode);
+
+ unblank = xf86IsUnblank(mode);
+ if (unblank)
+ SetTimeSinceLastInputEvent();
+
+ if ((pScrn != NULL) && pScrn->vtSema) {
+ if (unblank)
+ AMDGPUUnblank(pScrn);
+ else
+ AMDGPUBlank(pScrn);
+ }
+ return TRUE;
+}
+
+/* Called at the end of each server generation. Restore the original
+ * text mode, unmap video memory, and unwrap and call the saved
+ * CloseScreen function.
+ */
+static Bool AMDGPUCloseScreen_KMS(CLOSE_SCREEN_ARGS_DECL)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPUCloseScreen\n");
+
+ drmmode_uevent_fini(pScrn, &info->drmmode);
+
+ DeleteCallback(&FlushCallback, amdgpu_flush_callback, pScrn);
+
+ drmDropMaster(info->dri2.drm_fd);
+
+ drmmode_fini(pScrn, &info->drmmode);
+ if (info->dri2.enabled) {
+ amdgpu_dri2_close_screen(pScreen);
+ }
+ pScrn->vtSema = FALSE;
+ xf86ClearPrimInitDone(info->pEnt->index);
+ pScreen->BlockHandler = info->BlockHandler;
+ pScreen->CloseScreen = info->CloseScreen;
+ return (*pScreen->CloseScreen) (CLOSE_SCREEN_ARGS);
+}
+
+void AMDGPUFreeScreen_KMS(FREE_SCREEN_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPUFreeScreen\n");
+
+ /* when server quits at PreInit, we don't need do this anymore */
+ if (!info)
+ return;
+
+ AMDGPUFreeRec(pScrn);
+}
+
+Bool AMDGPUScreenInit_KMS(SCREEN_INIT_ARGS_DECL)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ int subPixelOrder = SubPixelUnknown;
+ char *s;
+ void *front_ptr;
+ int ret;
+
+ pScrn->fbOffset = 0;
+
+ miClearVisualTypes();
+ if (!miSetVisualTypes(pScrn->depth,
+ miGetDefaultVisualMask(pScrn->depth),
+ pScrn->rgbBits, pScrn->defaultVisual))
+ return FALSE;
+ miSetPixmapDepths();
+
+ ret = drmSetMaster(info->dri2.drm_fd);
+ if (ret) {
+ ErrorF("Unable to retrieve master\n");
+ return FALSE;
+ }
+ info->directRenderingEnabled = FALSE;
+ if (info->shadow_fb == FALSE)
+ info->directRenderingEnabled = amdgpu_dri2_screen_init(pScreen);
+
+ if (!amdgpu_setup_kernel_mem(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "amdgpu_setup_kernel_mem failed\n");
+ return FALSE;
+ }
+ front_ptr = info->front_buffer->cpu_ptr;
+
+ if (info->shadow_fb) {
+ info->fb_shadow = calloc(1,
+ pScrn->displayWidth * pScrn->virtualY *
+ ((pScrn->bitsPerPixel + 7) >> 3));
+ if (info->fb_shadow == NULL) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow framebuffer\n");
+ info->shadow_fb = FALSE;
+ } else {
+ if (!fbScreenInit(pScreen, info->fb_shadow,
+ pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi,
+ pScrn->displayWidth,
+ pScrn->bitsPerPixel))
+ return FALSE;
+ }
+ }
+
+ if (info->shadow_fb == FALSE) {
+ /* Init fb layer */
+ if (!fbScreenInit(pScreen, front_ptr,
+ pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth,
+ pScrn->bitsPerPixel))
+ return FALSE;
+ }
+
+ xf86SetBlackWhitePixels(pScreen);
+
+ if (pScrn->bitsPerPixel > 8) {
+ VisualPtr visual;
+
+ visual = pScreen->visuals + pScreen->numVisuals;
+ while (--visual >= pScreen->visuals) {
+ if ((visual->class | DynamicClass) == DirectColor) {
+ visual->offsetRed = pScrn->offset.red;
+ visual->offsetGreen = pScrn->offset.green;
+ visual->offsetBlue = pScrn->offset.blue;
+ visual->redMask = pScrn->mask.red;
+ visual->greenMask = pScrn->mask.green;
+ visual->blueMask = pScrn->mask.blue;
+ }
+ }
+ }
+
+ /* Must be after RGB order fixed */
+ fbPictureInit(pScreen, 0, 0);
+
+#ifdef RENDER
+ if ((s = xf86GetOptValString(info->Options, OPTION_SUBPIXEL_ORDER))) {
+ if (strcmp(s, "RGB") == 0)
+ subPixelOrder = SubPixelHorizontalRGB;
+ else if (strcmp(s, "BGR") == 0)
+ subPixelOrder = SubPixelHorizontalBGR;
+ else if (strcmp(s, "NONE") == 0)
+ subPixelOrder = SubPixelNone;
+ PictureSetSubpixelOrder(pScreen, subPixelOrder);
+ }
+#endif
+
+ pScrn->vtSema = TRUE;
+ xf86SetBackingStore(pScreen);
+
+ if (info->directRenderingEnabled) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Direct rendering enabled\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Direct rendering disabled\n");
+ }
+
+ if (info->use_glamor && info->directRenderingEnabled) {
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "Initializing Acceleration\n");
+ if (amdgpu_glamor_init(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Acceleration enabled\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Acceleration initialization failed\n");
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "2D and 3D acceleration disabled\n");
+ info->use_glamor = FALSE;
+ }
+ } else if (info->directRenderingEnabled) {
+ if (!amdgpu_pixmap_init(pScreen))
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "3D acceleration disabled\n");
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "2D acceleration disabled\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "2D and 3D cceleration disabled\n");
+ }
+
+ /* Init DPMS */
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "Initializing DPMS\n");
+ xf86DPMSInit(pScreen, xf86DPMSSet, 0);
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "Initializing Cursor\n");
+
+ /* Set Silken Mouse */
+ xf86SetSilkenMouse(pScreen);
+
+ /* Cursor setup */
+ miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
+
+ if (!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) {
+ if (AMDGPUCursorInit_KMS(pScreen)) {
+ }
+ }
+
+ /* DGA setup */
+#ifdef XFreeXDGA
+ /* DGA is dangerous on kms as the base and framebuffer location may change:
+ * http://lists.freedesktop.org/archives/xorg-devel/2009-September/002113.html
+ */
+ /* xf86DiDGAInit(pScreen, info->LinearAddr + pScrn->fbOffset); */
+#endif
+ if (info->shadow_fb == FALSE) {
+ /* Init Xv */
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "Initializing Xv\n");
+ AMDGPUInitVideo(pScreen);
+ }
+
+ if (info->shadow_fb == TRUE) {
+ if (!shadowSetup(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Shadowfb initialization failed\n");
+ return FALSE;
+ }
+ }
+ pScrn->pScreen = pScreen;
+
+ /* Provide SaveScreen & wrap BlockHandler and CloseScreen */
+ /* Wrap CloseScreen */
+ info->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = AMDGPUCloseScreen_KMS;
+ pScreen->SaveScreen = AMDGPUSaveScreen_KMS;
+ info->BlockHandler = pScreen->BlockHandler;
+ pScreen->BlockHandler = AMDGPUBlockHandler_KMS;
+
+ if (!AddCallback(&FlushCallback, amdgpu_flush_callback, pScrn))
+ return FALSE;
+
+ info->CreateScreenResources = pScreen->CreateScreenResources;
+ pScreen->CreateScreenResources = AMDGPUCreateScreenResources_KMS;
+
+#ifdef AMDGPU_PIXMAP_SHARING
+ pScreen->StartPixmapTracking = PixmapStartDirtyTracking;
+ pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
+#endif
+
+ if (!xf86CrtcScreenInit(pScreen))
+ return FALSE;
+
+ /* Wrap pointer motion to flip touch screen around */
+// info->PointerMoved = pScrn->PointerMoved;
+// pScrn->PointerMoved = AMDGPUPointerMoved;
+
+ if (!drmmode_setup_colormap(pScreen, pScrn))
+ return FALSE;
+
+ /* Note unused options */
+ if (serverGeneration == 1)
+ xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
+
+ drmmode_init(pScrn, &info->drmmode);
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPUScreenInit finished\n");
+
+ return TRUE;
+}
+
+Bool AMDGPUEnterVT_KMS(VT_FUNC_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ int ret;
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPUEnterVT_KMS\n");
+
+ ret = drmSetMaster(info->dri2.drm_fd);
+ if (ret)
+ ErrorF("Unable to retrieve master\n");
+
+ pScrn->vtSema = TRUE;
+
+ if (!drmmode_set_desired_modes(pScrn, &info->drmmode))
+ return FALSE;
+
+ return TRUE;
+}
+
+void AMDGPULeaveVT_KMS(VT_FUNC_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "AMDGPULeaveVT_KMS\n");
+
+ drmDropMaster(info->dri2.drm_fd);
+
+ xf86RotateFreeShadow(pScrn);
+
+ xf86_hide_cursors(pScrn);
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "Ok, leaving now...\n");
+}
+
+Bool AMDGPUSwitchMode_KMS(SWITCH_MODE_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ Bool ret;
+ ret = xf86SetSingleMode(pScrn, mode, RR_Rotate_0);
+ return ret;
+
+}
+
+void AMDGPUAdjustFrame_KMS(ADJUST_FRAME_ARGS_DECL)
+{
+ SCRN_INFO_PTR(arg);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ drmmode_adjust_frame(pScrn, &info->drmmode, x, y);
+ return;
+}
+
+static Bool amdgpu_setup_kernel_mem(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int cpp = info->pixel_bytes;
+ int cursor_size;
+ int c;
+
+ cursor_size = info->cursor_w * info->cursor_h * 4;
+ cursor_size = AMDGPU_ALIGN(cursor_size, AMDGPU_GPU_PAGE_SIZE);
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ /* cursor objects */
+ if (info->cursor_buffer[c] == NULL) {
+ if (info->gbm) {
+ info->cursor_buffer[c] = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
+ if (!info->cursor_buffer[c]) {
+ return FALSE;
+ }
+ info->cursor_buffer[c]->ref_count = 1;
+
+ info->cursor_buffer[c]->bo.gbm = gbm_bo_create(info->gbm,
+ info->cursor_w,
+ info->cursor_h,
+ GBM_FORMAT_ARGB8888,
+ GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+ if (!info->cursor_buffer[c]->bo.gbm) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to allocate cursor buffer memory\n");
+ free(info->cursor_buffer[c]);
+ return FALSE;
+ }
+ } else {
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ info->cursor_buffer[c] = amdgpu_bo_open(pAMDGPUEnt->pDev,
+ cursor_size,
+ 0,
+ AMDGPU_GEM_DOMAIN_VRAM);
+ if (!(info->cursor_buffer[c])) {
+ ErrorF("Failed to allocate cursor buffer memory\n");
+ return FALSE;
+ }
+
+ if (amdgpu_bo_cpu_map(info->cursor_buffer[c]->bo.amdgpu,
+ &info->cursor_buffer[c]->cpu_ptr)) {
+ ErrorF("Failed to map cursor buffer memory\n");
+ }
+ }
+
+ drmmode_set_cursor(pScrn, &info->drmmode, c,
+ info->cursor_buffer[c]);
+ }
+ }
+
+ if (info->front_buffer == NULL) {
+ int pitch;
+ int hint = info->use_glamor ? 0 : AMDGPU_CREATE_PIXMAP_LINEAR;
+
+ info->front_buffer =
+ amdgpu_alloc_pixmap_bo(pScrn, pScrn->virtualX,
+ pScrn->virtualY, pScrn->depth,
+ hint, pScrn->bitsPerPixel,
+ &pitch);
+ if (!(info->front_buffer)) {
+ ErrorF("Failed to allocate front buffer memory\n");
+ return FALSE;
+ }
+
+ if (amdgpu_bo_map(pScrn, info->front_buffer)) {
+ ErrorF("Failed to map front buffer memory\n");
+ return FALSE;
+ }
+
+ pScrn->displayWidth = pitch / cpp;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Front buffer pitch: %d bytes\n",
+ pScrn->displayWidth * cpp);
+ return TRUE;
+}
+
+/* Used to disallow modes that are not supported by the hardware */
+ModeStatus AMDGPUValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode,
+ Bool verbose, int flag)
+{
+ /* There are problems with double scan mode at high clocks
+ * They're likely related PLL and display buffer settings.
+ * Disable these modes for now.
+ */
+ if (mode->Flags & V_DBLSCAN) {
+ if ((mode->CrtcHDisplay >= 1024) || (mode->CrtcVDisplay >= 768))
+ return MODE_CLOCK_RANGE;
+ }
+ return MODE_OK;
+}
diff --git a/src/amdgpu_misc.c b/src/amdgpu_misc.c
new file mode 100644
index 0000000..560a877
--- /dev/null
+++ b/src/amdgpu_misc.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of Marc Aurele La France not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Marc Aurele La France makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as-is" without express or implied warranty.
+ *
+ * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amdgpu_probe.h"
+#include "amdgpu_version.h"
+
+#include "xf86.h"
+
+/* Module loader interface for subsidiary driver module */
+
+static XF86ModuleVersionInfo AMDGPUVersionRec = {
+ AMDGPU_DRIVER_NAME,
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XORG_VERSION_CURRENT,
+ AMDGPU_VERSION_MAJOR, AMDGPU_VERSION_MINOR, AMDGPU_VERSION_PATCH,
+ ABI_CLASS_VIDEODRV,
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_VIDEODRV,
+ {0, 0, 0, 0}
+};
+
+/*
+ * AMDGPUSetup --
+ *
+ * This function is called every time the module is loaded.
+ */
+static pointer
+AMDGPUSetup(pointer Module, pointer Options, int *ErrorMajor, int *ErrorMinor)
+{
+ static Bool Inited = FALSE;
+
+ if (!Inited) {
+ Inited = TRUE;
+ xf86AddDriver(&AMDGPU, Module, HaveDriverFuncs);
+ }
+
+ return (pointer) TRUE;
+}
+
+/* The following record must be called amdgpuModuleData */
+_X_EXPORT XF86ModuleData amdgpuModuleData = {
+ &AMDGPUVersionRec,
+ AMDGPUSetup,
+ NULL
+};
diff --git a/src/amdgpu_pci_chipset_gen.h b/src/amdgpu_pci_chipset_gen.h
new file mode 100644
index 0000000..257a121
--- /dev/null
+++ b/src/amdgpu_pci_chipset_gen.h
@@ -0,0 +1,77 @@
+/* This file is autogenerated please do not edit */
+static PciChipsets AMDGPUPciChipsets[] = {
+ { PCI_CHIP_BONAIRE_6640, PCI_CHIP_BONAIRE_6640, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_6641, PCI_CHIP_BONAIRE_6641, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_6649, PCI_CHIP_BONAIRE_6649, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_6650, PCI_CHIP_BONAIRE_6650, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_6651, PCI_CHIP_BONAIRE_6651, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_6658, PCI_CHIP_BONAIRE_6658, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_665C, PCI_CHIP_BONAIRE_665C, RES_SHARED_VGA },
+ { PCI_CHIP_BONAIRE_665D, PCI_CHIP_BONAIRE_665D, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9830, PCI_CHIP_KABINI_9830, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9831, PCI_CHIP_KABINI_9831, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9832, PCI_CHIP_KABINI_9832, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9833, PCI_CHIP_KABINI_9833, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9834, PCI_CHIP_KABINI_9834, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9835, PCI_CHIP_KABINI_9835, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9836, PCI_CHIP_KABINI_9836, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9837, PCI_CHIP_KABINI_9837, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9838, PCI_CHIP_KABINI_9838, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_9839, PCI_CHIP_KABINI_9839, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_983A, PCI_CHIP_KABINI_983A, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_983B, PCI_CHIP_KABINI_983B, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_983C, PCI_CHIP_KABINI_983C, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_983D, PCI_CHIP_KABINI_983D, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_983E, PCI_CHIP_KABINI_983E, RES_SHARED_VGA },
+ { PCI_CHIP_KABINI_983F, PCI_CHIP_KABINI_983F, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1304, PCI_CHIP_KAVERI_1304, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1305, PCI_CHIP_KAVERI_1305, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1306, PCI_CHIP_KAVERI_1306, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1307, PCI_CHIP_KAVERI_1307, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1309, PCI_CHIP_KAVERI_1309, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_130A, PCI_CHIP_KAVERI_130A, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_130B, PCI_CHIP_KAVERI_130B, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_130C, PCI_CHIP_KAVERI_130C, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_130D, PCI_CHIP_KAVERI_130D, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_130E, PCI_CHIP_KAVERI_130E, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_130F, PCI_CHIP_KAVERI_130F, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1310, PCI_CHIP_KAVERI_1310, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1311, PCI_CHIP_KAVERI_1311, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1312, PCI_CHIP_KAVERI_1312, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1313, PCI_CHIP_KAVERI_1313, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1315, PCI_CHIP_KAVERI_1315, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1316, PCI_CHIP_KAVERI_1316, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_1317, PCI_CHIP_KAVERI_1317, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_131B, PCI_CHIP_KAVERI_131B, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_131C, PCI_CHIP_KAVERI_131C, RES_SHARED_VGA },
+ { PCI_CHIP_KAVERI_131D, PCI_CHIP_KAVERI_131D, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67A0, PCI_CHIP_HAWAII_67A0, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67A1, PCI_CHIP_HAWAII_67A1, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67A2, PCI_CHIP_HAWAII_67A2, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67A8, PCI_CHIP_HAWAII_67A8, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67A9, PCI_CHIP_HAWAII_67A9, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67AA, PCI_CHIP_HAWAII_67AA, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67B0, PCI_CHIP_HAWAII_67B0, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67B1, PCI_CHIP_HAWAII_67B1, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67B8, PCI_CHIP_HAWAII_67B8, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67B9, PCI_CHIP_HAWAII_67B9, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67BA, PCI_CHIP_HAWAII_67BA, RES_SHARED_VGA },
+ { PCI_CHIP_HAWAII_67BE, PCI_CHIP_HAWAII_67BE, RES_SHARED_VGA },
+ { PCI_CHIP_TOPAZ_6900, PCI_CHIP_TOPAZ_6900, RES_SHARED_VGA },
+ { PCI_CHIP_TOPAZ_6901, PCI_CHIP_TOPAZ_6901, RES_SHARED_VGA },
+ { PCI_CHIP_TOPAZ_6902, PCI_CHIP_TOPAZ_6902, RES_SHARED_VGA },
+ { PCI_CHIP_TOPAZ_6903, PCI_CHIP_TOPAZ_6903, RES_SHARED_VGA },
+ { PCI_CHIP_TOPAZ_6907, PCI_CHIP_TOPAZ_6907, RES_SHARED_VGA },
+ { PCI_CHIP_TONGA_6920, PCI_CHIP_TONGA_6920, RES_SHARED_VGA },
+ { PCI_CHIP_TONGA_6921, PCI_CHIP_TONGA_6921, RES_SHARED_VGA },
+ { PCI_CHIP_TONGA_6938, PCI_CHIP_TONGA_6938, RES_SHARED_VGA },
+ { PCI_CHIP_TONGA_6939, PCI_CHIP_TONGA_6939, RES_SHARED_VGA },
+ { PCI_CHIP_TONGA_692B, PCI_CHIP_TONGA_692B, RES_SHARED_VGA },
+ { PCI_CHIP_TONGA_692F, PCI_CHIP_TONGA_692F, RES_SHARED_VGA },
+ { PCI_CHIP_CARRIZO_9870, PCI_CHIP_CARRIZO_9870, RES_SHARED_VGA },
+ { PCI_CHIP_CARRIZO_9874, PCI_CHIP_CARRIZO_9874, RES_SHARED_VGA },
+ { PCI_CHIP_CARRIZO_9875, PCI_CHIP_CARRIZO_9875, RES_SHARED_VGA },
+ { PCI_CHIP_CARRIZO_9876, PCI_CHIP_CARRIZO_9876, RES_SHARED_VGA },
+ { PCI_CHIP_CARRIZO_9877, PCI_CHIP_CARRIZO_9877, RES_SHARED_VGA },
+ { -1, -1, RES_UNDEFINED }
+};
diff --git a/src/amdgpu_pci_device_match_gen.h b/src/amdgpu_pci_device_match_gen.h
new file mode 100644
index 0000000..1e9255f
--- /dev/null
+++ b/src/amdgpu_pci_device_match_gen.h
@@ -0,0 +1,77 @@
+/* This file is autogenerated please do not edit */
+static const struct pci_id_match amdgpu_device_match[] = {
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6640, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6641, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6649, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6650, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6651, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6658, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_665C, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_665D, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9830, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9831, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9832, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9833, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9834, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9835, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9836, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9837, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9838, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9839, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983A, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983B, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983C, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983D, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983E, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983F, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1304, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1305, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1306, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1307, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1309, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130A, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130B, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130C, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130D, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130E, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130F, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1310, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1311, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1312, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1313, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1315, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1316, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1317, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_131B, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_131C, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_131D, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A0, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A1, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A2, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A8, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A9, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67AA, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B0, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B1, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B8, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B9, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67BA, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67BE, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6900, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6901, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6902, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6903, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6907, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6920, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6921, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6938, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6939, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TONGA_692B, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_TONGA_692F, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9870, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9874, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9875, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9876, 0 ),
+ ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9877, 0 ),
+ { 0, 0, 0 }
+};
diff --git a/src/amdgpu_pixmap.c b/src/amdgpu_pixmap.c
new file mode 100644
index 0000000..657ad33
--- /dev/null
+++ b/src/amdgpu_pixmap.c
@@ -0,0 +1,115 @@
+/* Copyright © 2014 Advanced Micro Devices, 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 (including
+ * the next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xf86.h>
+#include "amdgpu_pixmap.h"
+#include "amdgpu_bo_helper.h"
+
+static PixmapPtr
+amdgpu_pixmap_create(ScreenPtr screen, int w, int h, int depth, unsigned usage)
+{
+ ScrnInfoPtr scrn;
+ struct amdgpu_pixmap *priv;
+ PixmapPtr pixmap;
+ AMDGPUInfoPtr info;
+
+ /* only DRI2 pixmap is suppported */
+ if (!(usage & AMDGPU_CREATE_PIXMAP_DRI2))
+ return fbCreatePixmap(screen, w, h, depth, usage);
+
+ if (w > 32767 || h > 32767)
+ return NullPixmap;
+
+ if (depth == 1)
+ return fbCreatePixmap(screen, w, h, depth, usage);
+
+ pixmap = fbCreatePixmap(screen, 0, 0, depth, usage);
+ if (pixmap == NullPixmap)
+ return pixmap;
+
+ if (w && h) {
+ priv = calloc(1, sizeof(struct amdgpu_pixmap));
+ if (priv == NULL)
+ goto fallback_pixmap;
+
+ scrn = xf86ScreenToScrn(screen);
+ info = AMDGPUPTR(scrn);
+ if (!info->use_glamor)
+ usage |= AMDGPU_CREATE_PIXMAP_LINEAR;
+ priv->bo = amdgpu_alloc_pixmap_bo(scrn, w, h, depth, usage,
+ pixmap->drawable.bitsPerPixel,
+ &priv->stride);
+ if (!priv->bo)
+ goto fallback_priv;
+
+ amdgpu_set_pixmap_private(pixmap, priv);
+
+ if (amdgpu_bo_map(scrn, priv->bo)) {
+ ErrorF("Failed to mmap the bo\n");
+ goto fallback_bo;
+ }
+
+ screen->ModifyPixmapHeader(pixmap, w, h,
+ 0, 0, priv->stride,
+ priv->bo->cpu_ptr);
+ }
+
+ return pixmap;
+
+fallback_bo:
+ amdgpu_bo_unref(&priv->bo);
+fallback_priv:
+ free(priv);
+fallback_pixmap:
+ fbDestroyPixmap(pixmap);
+ return fbCreatePixmap(screen, w, h, depth, usage);
+}
+
+static Bool amdgpu_pixmap_destroy(PixmapPtr pixmap)
+{
+ if (pixmap->refcnt == 1) {
+ amdgpu_set_pixmap_bo(pixmap, NULL);
+ }
+ fbDestroyPixmap(pixmap);
+ return TRUE;
+}
+
+/* This should only be called when glamor is disabled */
+Bool amdgpu_pixmap_init(ScreenPtr screen)
+{
+#if HAS_DIXREGISTERPRIVATEKEY
+ if (!dixRegisterPrivateKey(&amdgpu_pixmap_index, PRIVATE_PIXMAP, 0))
+#else
+ if (!dixRequestPrivate(&amdgpu_pixmap_index, 0))
+#endif
+ return FALSE;
+
+ screen->CreatePixmap = amdgpu_pixmap_create;
+ screen->DestroyPixmap = amdgpu_pixmap_destroy;
+ return TRUE;
+}
diff --git a/src/amdgpu_pixmap.h b/src/amdgpu_pixmap.h
new file mode 100644
index 0000000..e37466c
--- /dev/null
+++ b/src/amdgpu_pixmap.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, 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 (including
+ * the next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef AMDGPU_PIXMAP_H
+#define AMDGPU_PIXMAP_H
+
+#include "amdgpu_drv.h"
+
+struct amdgpu_pixmap {
+ struct amdgpu_buffer *bo;
+ int stride;
+};
+
+#if HAS_DEVPRIVATEKEYREC
+extern DevPrivateKeyRec amdgpu_pixmap_index;
+#else
+extern int amdgpu_pixmap_index;
+#endif
+
+static inline struct amdgpu_pixmap *amdgpu_get_pixmap_private(PixmapPtr pixmap)
+{
+#if HAS_DEVPRIVATEKEYREC
+ return dixGetPrivate(&pixmap->devPrivates, &amdgpu_pixmap_index);
+#else
+ return dixLookupPrivate(&pixmap->devPrivates, &amdgpu_pixmap_index);
+#endif
+}
+
+static inline void amdgpu_set_pixmap_private(PixmapPtr pixmap,
+ struct amdgpu_pixmap *priv)
+{
+ dixSetPrivate(&pixmap->devPrivates, &amdgpu_pixmap_index, priv);
+}
+
+#if XF86_CRTC_VERSION >= 5
+#define AMDGPU_PIXMAP_SHARING 1
+#endif
+
+static inline void amdgpu_set_pixmap_bo(PixmapPtr pPix, struct amdgpu_buffer *bo)
+{
+ struct amdgpu_pixmap *priv;
+
+ priv = amdgpu_get_pixmap_private(pPix);
+ if (priv == NULL && bo == NULL)
+ return;
+
+ if (priv) {
+ if (priv->bo == bo)
+ return;
+
+ if (priv->bo) {
+ amdgpu_bo_unref(&priv->bo);
+ }
+
+ if (!bo) {
+ free(priv);
+ priv = NULL;
+ }
+ }
+
+ if (bo) {
+ if (!priv) {
+ priv = calloc(1, sizeof(struct amdgpu_pixmap));
+ if (!priv)
+ goto out;
+ }
+ amdgpu_bo_ref(bo);
+ priv->bo = bo;
+ }
+out:
+ amdgpu_set_pixmap_private(pPix, priv);
+}
+
+static inline struct amdgpu_buffer *amdgpu_get_pixmap_bo(PixmapPtr pPix)
+{
+ struct amdgpu_pixmap *priv;
+ priv = amdgpu_get_pixmap_private(pPix);
+ return priv ? priv->bo : NULL;
+}
+
+enum {
+ AMDGPU_CREATE_PIXMAP_DRI2 = 0x08000000,
+ AMDGPU_CREATE_PIXMAP_LINEAR = 0x04000000
+};
+
+extern Bool amdgpu_pixmap_init(ScreenPtr screen);
+
+#endif /* AMDGPU_PIXMAP_H */
diff --git a/src/amdgpu_probe.c b/src/amdgpu_probe.c
new file mode 100644
index 0000000..9edaf4f
--- /dev/null
+++ b/src/amdgpu_probe.c
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Authors:
+ * Kevin E. Martin <martin@xfree86.org>
+ * Rickard E. Faith <faith@valinux.com>
+ * KMS support - Dave Airlie <airlied@redhat.com>
+ */
+
+#include "amdgpu_probe.h"
+#include "amdgpu_version.h"
+#include "amdpciids.h"
+
+#include "xf86.h"
+
+#include "xf86drmMode.h"
+#include "dri.h"
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <xf86_OSproc.h>
+#endif
+
+#ifdef XSERVER_PLATFORM_BUS
+#include <xf86platformBus.h>
+#endif
+
+#include "amdgpu_chipset_gen.h"
+
+#include "amdgpu_pci_chipset_gen.h"
+
+#include "amdgpu_pci_device_match_gen.h"
+
+_X_EXPORT int gAMDGPUEntityIndex = -1;
+
+/* Return the options for supported chipset 'n'; NULL otherwise */
+static const OptionInfoRec *AMDGPUAvailableOptions(int chipid, int busid)
+{
+ return AMDGPUOptionsWeak();
+}
+
+/* Return the string name for supported chipset 'n'; NULL otherwise. */
+static void AMDGPUIdentify(int flags)
+{
+ xf86PrintChipsets(AMDGPU_NAME,
+ "Driver for AMD Radeon chipsets", AMDGPUChipsets);
+}
+
+static Bool amdgpu_kernel_mode_enabled(ScrnInfoPtr pScrn,
+ struct pci_device *pci_dev)
+{
+ char *busIdString;
+ int ret;
+
+ if (!xf86LoaderCheckSymbol("DRICreatePCIBusID")) {
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
+ "[KMS] No DRICreatePCIBusID symbol, no kernel modesetting.\n");
+ return FALSE;
+ }
+
+ busIdString = DRICreatePCIBusID(pci_dev);
+ ret = drmCheckModesettingSupported(busIdString);
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (ret) {
+ if (xf86LoadKernelModule("amdgpukms"))
+ ret = drmCheckModesettingSupported(busIdString);
+ }
+#endif
+ free(busIdString);
+ if (ret) {
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
+ "[KMS] drm report modesetting isn't supported.\n");
+ return FALSE;
+ }
+
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
+ "[KMS] Kernel modesetting enabled.\n");
+ return TRUE;
+}
+
+static int amdgpu_kernel_open_fd(ScrnInfoPtr pScrn, struct pci_device *dev)
+{
+ char *busid;
+ drmSetVersion sv;
+ int err;
+ int fd;
+
+#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,9,99,901,0)
+ XNFasprintf(&busid, "pci:%04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->dev, dev->func);
+#else
+ busid = XNFprintf("pci:%04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->dev, dev->func);
+#endif
+
+ fd = drmOpen(NULL, busid);
+ free(busid);
+ if (fd == -1) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] Failed to open DRM device for %s: %s\n",
+ busid, strerror(errno));
+ return fd;
+ }
+
+ /* Check that what we opened was a master or a master-capable FD,
+ * by setting the version of the interface we'll use to talk to it.
+ * (see DRIOpenDRMMaster() in DRI1)
+ */
+ sv.drm_di_major = 1;
+ sv.drm_di_minor = 1;
+ sv.drm_dd_major = -1;
+ sv.drm_dd_minor = -1;
+ err = drmSetInterfaceVersion(fd, &sv);
+ if (err != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set drm interface version.\n");
+ drmClose(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static Bool amdgpu_get_scrninfo(int entity_num, void *pci_dev)
+{
+ ScrnInfoPtr pScrn = NULL;
+ EntityInfoPtr pEnt;
+ DevUnion *pPriv;
+ AMDGPUEntPtr pAMDGPUEnt;
+
+ pScrn = xf86ConfigPciEntity(pScrn, 0, entity_num, AMDGPUPciChipsets,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (!pScrn)
+ return FALSE;
+
+ if (pci_dev) {
+ if (!amdgpu_kernel_mode_enabled(pScrn, pci_dev)) {
+ return FALSE;
+ }
+ }
+
+ pScrn->driverVersion = AMDGPU_VERSION_CURRENT;
+ pScrn->driverName = AMDGPU_DRIVER_NAME;
+ pScrn->name = AMDGPU_NAME;
+ pScrn->Probe = NULL;
+
+ pScrn->PreInit = AMDGPUPreInit_KMS;
+ pScrn->ScreenInit = AMDGPUScreenInit_KMS;
+ pScrn->SwitchMode = AMDGPUSwitchMode_KMS;
+ pScrn->AdjustFrame = AMDGPUAdjustFrame_KMS;
+ pScrn->EnterVT = AMDGPUEnterVT_KMS;
+ pScrn->LeaveVT = AMDGPULeaveVT_KMS;
+ pScrn->FreeScreen = AMDGPUFreeScreen_KMS;
+ pScrn->ValidMode = AMDGPUValidMode;
+
+ pEnt = xf86GetEntityInfo(entity_num);
+
+ /* Create a AMDGPUEntity for all chips, even with old single head
+ * Radeon, need to use pAMDGPUEnt for new monitor detection routines.
+ */
+ xf86SetEntitySharable(entity_num);
+
+ if (gAMDGPUEntityIndex == -1)
+ gAMDGPUEntityIndex = xf86AllocateEntityPrivateIndex();
+
+ pPriv = xf86GetEntityPrivate(pEnt->index, gAMDGPUEntityIndex);
+
+ if (!pPriv->ptr) {
+ uint32_t major_version;
+ uint32_t minor_version;
+
+ pPriv->ptr = xnfcalloc(sizeof(AMDGPUEntRec), 1);
+ pAMDGPUEnt = pPriv->ptr;
+ pAMDGPUEnt->HasSecondary = FALSE;
+
+ pAMDGPUEnt->fd = amdgpu_kernel_open_fd(pScrn, pci_dev);
+ if (pAMDGPUEnt->fd < 0)
+ goto error_fd;
+
+ pAMDGPUEnt->fd_ref = 1;
+
+ if (amdgpu_device_initialize(pAMDGPUEnt->fd,
+ &major_version,
+ &minor_version,
+ &pAMDGPUEnt->pDev)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "amdgpu_device_initialize failed\n");
+ goto error_amdgpu;
+ }
+ } else {
+ pAMDGPUEnt = pPriv->ptr;
+ pAMDGPUEnt->HasSecondary = TRUE;
+ }
+
+ xf86SetEntityInstanceForScreen(pScrn, pEnt->index,
+ xf86GetNumEntityInstances(pEnt->
+ index)
+ - 1);
+ free(pEnt);
+
+ return TRUE;
+
+error_amdgpu:
+ drmClose(pAMDGPUEnt->fd);
+ pAMDGPUEnt->fd = 0;
+error_fd:
+ free(pPriv->ptr);
+ return FALSE;
+}
+
+static Bool
+amdgpu_pci_probe(DriverPtr pDriver,
+ int entity_num, struct pci_device *device, intptr_t match_data)
+{
+ return amdgpu_get_scrninfo(entity_num, (void *)device);
+}
+
+static Bool AMDGPUDriverFunc(ScrnInfoPtr scrn, xorgDriverFuncOp op, void *data)
+{
+ xorgHWFlags *flag;
+
+ switch (op) {
+ case GET_REQUIRED_HW_INTERFACES:
+ flag = (CARD32 *) data;
+ (*flag) = 0;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+#ifdef XSERVER_PLATFORM_BUS
+static Bool
+amdgpu_platform_probe(DriverPtr pDriver,
+ int entity_num, int flags,
+ struct xf86_platform_device *dev, intptr_t match_data)
+{
+ ScrnInfoPtr pScrn;
+ int scr_flags = 0;
+ EntityInfoPtr pEnt;
+ DevUnion *pPriv;
+ AMDGPUEntPtr pAMDGPUEnt;
+
+ if (!dev->pdev)
+ return FALSE;
+
+ if (flags & PLATFORM_PROBE_GPU_SCREEN)
+ scr_flags = XF86_ALLOCATE_GPU_SCREEN;
+
+ pScrn = xf86AllocateScreen(pDriver, scr_flags);
+ if (xf86IsEntitySharable(entity_num))
+ xf86SetEntityShared(entity_num);
+ xf86AddEntityToScreen(pScrn, entity_num);
+
+ if (!amdgpu_kernel_mode_enabled(pScrn, dev->pdev))
+ return FALSE;
+
+ pScrn->driverVersion = AMDGPU_VERSION_CURRENT;
+ pScrn->driverName = AMDGPU_DRIVER_NAME;
+ pScrn->name = AMDGPU_NAME;
+ pScrn->Probe = NULL;
+ pScrn->PreInit = AMDGPUPreInit_KMS;
+ pScrn->ScreenInit = AMDGPUScreenInit_KMS;
+ pScrn->SwitchMode = AMDGPUSwitchMode_KMS;
+ pScrn->AdjustFrame = AMDGPUAdjustFrame_KMS;
+ pScrn->EnterVT = AMDGPUEnterVT_KMS;
+ pScrn->LeaveVT = AMDGPULeaveVT_KMS;
+ pScrn->FreeScreen = AMDGPUFreeScreen_KMS;
+ pScrn->ValidMode = AMDGPUValidMode;
+
+ pEnt = xf86GetEntityInfo(entity_num);
+
+ /* Create a AMDGPUEntity for all chips, even with old single head
+ * Radeon, need to use pAMDGPUEnt for new monitor detection routines.
+ */
+ xf86SetEntitySharable(entity_num);
+
+ if (gAMDGPUEntityIndex == -1)
+ gAMDGPUEntityIndex = xf86AllocateEntityPrivateIndex();
+
+ pPriv = xf86GetEntityPrivate(pEnt->index, gAMDGPUEntityIndex);
+
+ if (!pPriv->ptr) {
+ uint32_t major_version;
+ uint32_t minor_version;
+
+ pPriv->ptr = xnfcalloc(sizeof(AMDGPUEntRec), 1);
+ pAMDGPUEnt = pPriv->ptr;
+ pAMDGPUEnt->HasSecondary = FALSE;
+ pAMDGPUEnt->fd = amdgpu_kernel_open_fd(pScrn, dev->pdev);
+ if (pAMDGPUEnt->fd < 0)
+ goto error_fd;
+
+ pAMDGPUEnt->fd_ref = 1;
+
+ if (amdgpu_device_initialize(pAMDGPUEnt->fd,
+ &major_version,
+ &minor_version,
+ &pAMDGPUEnt->pDev)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "amdgpu_device_initialize failed\n");
+ goto error_amdgpu;
+ }
+ } else {
+ pAMDGPUEnt = pPriv->ptr;
+ pAMDGPUEnt->HasSecondary = TRUE;
+ }
+
+ xf86SetEntityInstanceForScreen(pScrn, pEnt->index,
+ xf86GetNumEntityInstances(pEnt->
+ index)
+ - 1);
+ free(pEnt);
+
+ return TRUE;
+
+error_amdgpu:
+ drmClose(pAMDGPUEnt->fd);
+ pAMDGPUEnt->fd = 0;
+error_fd:
+ free(pPriv->ptr);
+ return FALSE;
+}
+#endif
+
+_X_EXPORT DriverRec AMDGPU = {
+ AMDGPU_VERSION_CURRENT,
+ AMDGPU_DRIVER_NAME,
+ AMDGPUIdentify,
+ NULL,
+ AMDGPUAvailableOptions,
+ NULL,
+ 0,
+ AMDGPUDriverFunc,
+ amdgpu_device_match,
+ amdgpu_pci_probe,
+#ifdef XSERVER_PLATFORM_BUS
+ amdgpu_platform_probe
+#endif
+};
diff --git a/src/amdgpu_probe.h b/src/amdgpu_probe.h
new file mode 100644
index 0000000..1f760ac
--- /dev/null
+++ b/src/amdgpu_probe.h
@@ -0,0 +1,97 @@
+/*
+ * 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>
+ *
+ * Modified by Marc Aurele La France <tsi@xfree86.org> for ATI driver merge.
+ */
+
+#ifndef _AMDGPU_PROBE_H_
+#define _AMDGPU_PROBE_H_ 1
+
+#include <stdint.h>
+#include "xf86str.h"
+#include "xf86DDC.h"
+#include "randrstr.h"
+
+#include "xf86Crtc.h"
+
+#include <amdgpu.h>
+
+#include "compat-api.h"
+
+extern DriverRec AMDGPU;
+
+typedef enum {
+ CHIP_FAMILY_UNKNOW,
+ CHIP_FAMILY_LEGACY,
+ CHIP_FAMILY_AMDGPU,
+ CHIP_FAMILY_BONAIRE,
+ CHIP_FAMILY_KAVERI,
+ CHIP_FAMILY_KABINI,
+ CHIP_FAMILY_HAWAII,
+ CHIP_FAMILY_TOPAZ,
+ CHIP_FAMILY_TONGA,
+ CHIP_FAMILY_CARRIZO,
+ CHIP_FAMILY_LAST
+} AMDGPUChipFamily;
+
+typedef struct {
+ uint32_t pci_device_id;
+ AMDGPUChipFamily chip_family;
+} AMDGPUCardInfo;
+
+typedef struct {
+ Bool HasSecondary;
+ Bool HasCRTC2; /* All cards except original Radeon */
+
+ ScrnInfoPtr pSecondaryScrn;
+ ScrnInfoPtr pPrimaryScrn;
+ amdgpu_device_handle pDev;
+
+ int fd; /* for sharing across zaphod heads */
+ int fd_ref;
+ unsigned long fd_wakeup_registered; /* server generation for which fd has been registered for wakeup handling */
+ int fd_wakeup_ref;
+} AMDGPUEntRec, *AMDGPUEntPtr;
+
+extern const OptionInfoRec *AMDGPUOptionsWeak(void);
+
+extern Bool AMDGPUPreInit_KMS(ScrnInfoPtr, int);
+extern Bool AMDGPUScreenInit_KMS(SCREEN_INIT_ARGS_DECL);
+extern Bool AMDGPUSwitchMode_KMS(SWITCH_MODE_ARGS_DECL);
+extern void AMDGPUAdjustFrame_KMS(ADJUST_FRAME_ARGS_DECL);
+extern Bool AMDGPUEnterVT_KMS(VT_FUNC_ARGS_DECL);
+extern void AMDGPULeaveVT_KMS(VT_FUNC_ARGS_DECL);
+extern void AMDGPUFreeScreen_KMS(FREE_SCREEN_ARGS_DECL);
+
+extern ModeStatus AMDGPUValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode,
+ Bool verbose, int flag);
+#endif /* _AMDGPU_PROBE_H_ */
diff --git a/src/amdgpu_version.h b/src/amdgpu_version.h
new file mode 100644
index 0000000..8b98ecd
--- /dev/null
+++ b/src/amdgpu_version.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of Marc Aurele La France not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Marc Aurele La France makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as-is" without express or implied warranty.
+ *
+ * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _AMDGPU_VERSION_H_
+#define _AMDGPU_VERSION_H_ 1
+
+#undef AMDGPU_NAME
+#undef AMDGPU_DRIVER_NAME
+#undef R200_DRIVER_NAME
+#undef AMDGPU_VERSION_MAJOR
+#undef AMDGPU_VERSION_MINOR
+#undef AMDGPU_VERSION_PATCH
+#undef AMDGPU_VERSION_CURRENT
+#undef AMDGPU_VERSION_EVALUATE
+#undef AMDGPU_VERSION_STRINGIFY
+#undef AMDGPU_VERSION_NAME
+
+#define AMDGPU_NAME "AMDGPU"
+#define AMDGPU_DRIVER_NAME "amdgpu"
+#define SI_DRIVER_NAME "radeonsi"
+
+#define AMDGPU_VERSION_MAJOR PACKAGE_VERSION_MAJOR
+#define AMDGPU_VERSION_MINOR PACKAGE_VERSION_MINOR
+#define AMDGPU_VERSION_PATCH PACKAGE_VERSION_PATCHLEVEL
+
+#ifndef AMDGPU_VERSION_EXTRA
+#define AMDGPU_VERSION_EXTRA ""
+#endif
+
+#define AMDGPU_VERSION_CURRENT \
+ ((AMDGPU_VERSION_MAJOR << 20) | \
+ (AMDGPU_VERSION_MINOR << 10) | \
+ (AMDGPU_VERSION_PATCH))
+
+#define AMDGPU_VERSION_EVALUATE(__x) #__x
+#define AMDGPU_VERSION_STRINGIFY(_x) AMDGPU_VERSION_EVALUATE(_x)
+#define AMDGPU_VERSION_NAME \
+ AMDGPU_VERSION_STRINGIFY(AMDGPU_VERSION_MAJOR) "." \
+ AMDGPU_VERSION_STRINGIFY(AMDGPU_VERSION_MINOR) "." \
+ AMDGPU_VERSION_STRINGIFY(AMDGPU_VERSION_PATCH) AMDGPU_VERSION_EXTRA
+
+#endif /* _AMDGPU_VERSION_H_ */
diff --git a/src/amdgpu_video.c b/src/amdgpu_video.c
new file mode 100644
index 0000000..32f5709
--- /dev/null
+++ b/src/amdgpu_video.c
@@ -0,0 +1,177 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "amdgpu_drv.h"
+#include "amdgpu_probe.h"
+#include "amdgpu_video.h"
+#include "amdgpu_pixmap.h"
+
+#include "xf86.h"
+#include "dixstruct.h"
+
+/* DPMS */
+#ifdef HAVE_XEXTPROTO_71
+#include <X11/extensions/dpmsconst.h>
+#else
+#define DPMS_SERVER
+#include <X11/extensions/dpms.h>
+#endif
+
+#include <X11/extensions/Xv.h>
+#include "fourcc.h"
+
+#define OFF_DELAY 250 /* milliseconds */
+#define FREE_DELAY 15000
+
+#define OFF_TIMER 0x01
+#define FREE_TIMER 0x02
+#define CLIENT_VIDEO_ON 0x04
+
+static void amdgpu_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
+{
+ dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
+ dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
+ dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
+ dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
+
+ if (dest->x1 >= dest->x2 || dest->y1 >= dest->y2)
+ dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
+}
+
+static void amdgpu_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
+{
+ if (crtc->enabled) {
+ crtc_box->x1 = crtc->x;
+ crtc_box->x2 =
+ crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
+ crtc_box->y1 = crtc->y;
+ crtc_box->y2 =
+ crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
+ } else
+ crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
+}
+
+static int amdgpu_box_area(BoxPtr box)
+{
+ return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1);
+}
+
+Bool amdgpu_crtc_is_enabled(xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ return drmmode_crtc->dpms_mode == DPMSModeOn;
+}
+
+uint32_t amdgpu_get_interpolated_vblanks(xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ return drmmode_crtc->interpolated_vblanks;
+}
+
+xf86CrtcPtr
+amdgpu_pick_best_crtc(ScrnInfoPtr pScrn, Bool consider_disabled,
+ int x1, int x2, int y1, int y2)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int coverage, best_coverage, c;
+ BoxRec box, crtc_box, cover_box;
+ RROutputPtr primary_output = NULL;
+ xf86CrtcPtr best_crtc = NULL, primary_crtc = NULL;
+
+ if (!pScrn->vtSema)
+ return NULL;
+
+ box.x1 = x1;
+ box.x2 = x2;
+ box.y1 = y1;
+ box.y2 = y2;
+ best_coverage = 0;
+
+ /* Prefer the CRTC of the primary output */
+#ifdef HAS_DIXREGISTERPRIVATEKEY
+ if (dixPrivateKeyRegistered(rrPrivKey))
+#endif
+ {
+ primary_output = RRFirstOutput(pScrn->pScreen);
+ }
+ if (primary_output && primary_output->crtc)
+ primary_crtc = primary_output->crtc->devPrivate;
+
+ /* first consider only enabled CRTCs */
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
+
+ if (!amdgpu_crtc_is_enabled(crtc))
+ continue;
+
+ amdgpu_crtc_box(crtc, &crtc_box);
+ amdgpu_box_intersect(&cover_box, &crtc_box, &box);
+ coverage = amdgpu_box_area(&cover_box);
+ if (coverage > best_coverage ||
+ (coverage == best_coverage && crtc == primary_crtc)) {
+ best_crtc = crtc;
+ best_coverage = coverage;
+ }
+ }
+ if (best_crtc || !consider_disabled)
+ return best_crtc;
+
+ /* if we found nothing, repeat the search including disabled CRTCs */
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
+
+ amdgpu_crtc_box(crtc, &crtc_box);
+ amdgpu_box_intersect(&cover_box, &crtc_box, &box);
+ coverage = amdgpu_box_area(&cover_box);
+ if (coverage > best_coverage ||
+ (coverage == best_coverage && crtc == primary_crtc)) {
+ best_crtc = crtc;
+ best_coverage = coverage;
+ }
+ }
+ return best_crtc;
+}
+
+void AMDGPUInitVideo(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
+ XF86VideoAdaptorPtr texturedAdaptor = NULL;
+ int num_adaptors;
+
+ num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);
+ newAdaptors =
+ malloc((num_adaptors + 2) * sizeof(XF86VideoAdaptorPtr *));
+ if (newAdaptors == NULL)
+ return;
+
+ memcpy(newAdaptors, adaptors,
+ num_adaptors * sizeof(XF86VideoAdaptorPtr));
+ adaptors = newAdaptors;
+
+ if (info->use_glamor) {
+ texturedAdaptor = amdgpu_glamor_xv_init(pScreen, 16);
+ if (texturedAdaptor != NULL) {
+ adaptors[num_adaptors++] = texturedAdaptor;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Set up textured video (glamor)\n");
+ } else
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to set up textured video (glamor)\n");
+ }
+
+ if (num_adaptors)
+ xf86XVScreenInit(pScreen, adaptors, num_adaptors);
+
+ if (newAdaptors)
+ free(newAdaptors);
+
+}
diff --git a/src/amdgpu_video.h b/src/amdgpu_video.h
new file mode 100644
index 0000000..2915e3a
--- /dev/null
+++ b/src/amdgpu_video.h
@@ -0,0 +1,12 @@
+#ifndef __AMDGPU_VIDEO_H__
+#define __AMDGPU_VIDEO_H__
+
+#include "xf86i2c.h"
+#include "i2c_def.h"
+
+#include "xf86Crtc.h"
+
+Bool amdgpu_crtc_is_enabled(xf86CrtcPtr crtc);
+uint32_t amdgpu_get_interpolated_vblanks(xf86CrtcPtr crtc);
+
+#endif /* __AMDGPU_VIDEO_H__ */
diff --git a/src/amdpciids.h b/src/amdpciids.h
new file mode 100644
index 0000000..33ec6da
--- /dev/null
+++ b/src/amdpciids.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 _AMDPCIIDS_H
+#define _AMDPCIIDS_H
+
+#define PCI_VENDOR_ATI 0x1002
+
+#include "ati_pciids_gen.h"
+
+typedef struct pci_device *pciVideoPtr;
+
+#define PCI_DEV_DEVICE_ID(_pcidev) ((_pcidev)->device_id)
+
+#define ATI_DEVICE_MATCH(d, i) \
+ { PCI_VENDOR_ATI, (d), PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0, (i) }
+
+#endif /* AMDPCIIDS_H */
diff --git a/src/ati_pciids_gen.h b/src/ati_pciids_gen.h
new file mode 100644
index 0000000..8c58736
--- /dev/null
+++ b/src/ati_pciids_gen.h
@@ -0,0 +1,73 @@
+#define PCI_CHIP_BONAIRE_6640 0x6640
+#define PCI_CHIP_BONAIRE_6641 0x6641
+#define PCI_CHIP_BONAIRE_6649 0x6649
+#define PCI_CHIP_BONAIRE_6650 0x6650
+#define PCI_CHIP_BONAIRE_6651 0x6651
+#define PCI_CHIP_BONAIRE_6658 0x6658
+#define PCI_CHIP_BONAIRE_665C 0x665C
+#define PCI_CHIP_BONAIRE_665D 0x665D
+#define PCI_CHIP_KABINI_9830 0x9830
+#define PCI_CHIP_KABINI_9831 0x9831
+#define PCI_CHIP_KABINI_9832 0x9832
+#define PCI_CHIP_KABINI_9833 0x9833
+#define PCI_CHIP_KABINI_9834 0x9834
+#define PCI_CHIP_KABINI_9835 0x9835
+#define PCI_CHIP_KABINI_9836 0x9836
+#define PCI_CHIP_KABINI_9837 0x9837
+#define PCI_CHIP_KABINI_9838 0x9838
+#define PCI_CHIP_KABINI_9839 0x9839
+#define PCI_CHIP_KABINI_983A 0x983A
+#define PCI_CHIP_KABINI_983B 0x983B
+#define PCI_CHIP_KABINI_983C 0x983C
+#define PCI_CHIP_KABINI_983D 0x983D
+#define PCI_CHIP_KABINI_983E 0x983E
+#define PCI_CHIP_KABINI_983F 0x983F
+#define PCI_CHIP_KAVERI_1304 0x1304
+#define PCI_CHIP_KAVERI_1305 0x1305
+#define PCI_CHIP_KAVERI_1306 0x1306
+#define PCI_CHIP_KAVERI_1307 0x1307
+#define PCI_CHIP_KAVERI_1309 0x1309
+#define PCI_CHIP_KAVERI_130A 0x130A
+#define PCI_CHIP_KAVERI_130B 0x130B
+#define PCI_CHIP_KAVERI_130C 0x130C
+#define PCI_CHIP_KAVERI_130D 0x130D
+#define PCI_CHIP_KAVERI_130E 0x130E
+#define PCI_CHIP_KAVERI_130F 0x130F
+#define PCI_CHIP_KAVERI_1310 0x1310
+#define PCI_CHIP_KAVERI_1311 0x1311
+#define PCI_CHIP_KAVERI_1312 0x1312
+#define PCI_CHIP_KAVERI_1313 0x1313
+#define PCI_CHIP_KAVERI_1315 0x1315
+#define PCI_CHIP_KAVERI_1316 0x1316
+#define PCI_CHIP_KAVERI_1317 0x1317
+#define PCI_CHIP_KAVERI_131B 0x131B
+#define PCI_CHIP_KAVERI_131C 0x131C
+#define PCI_CHIP_KAVERI_131D 0x131D
+#define PCI_CHIP_HAWAII_67A0 0x67A0
+#define PCI_CHIP_HAWAII_67A1 0x67A1
+#define PCI_CHIP_HAWAII_67A2 0x67A2
+#define PCI_CHIP_HAWAII_67A8 0x67A8
+#define PCI_CHIP_HAWAII_67A9 0x67A9
+#define PCI_CHIP_HAWAII_67AA 0x67AA
+#define PCI_CHIP_HAWAII_67B0 0x67B0
+#define PCI_CHIP_HAWAII_67B1 0x67B1
+#define PCI_CHIP_HAWAII_67B8 0x67B8
+#define PCI_CHIP_HAWAII_67B9 0x67B9
+#define PCI_CHIP_HAWAII_67BA 0x67BA
+#define PCI_CHIP_HAWAII_67BE 0x67BE
+#define PCI_CHIP_TOPAZ_6900 0x6900
+#define PCI_CHIP_TOPAZ_6901 0x6901
+#define PCI_CHIP_TOPAZ_6902 0x6902
+#define PCI_CHIP_TOPAZ_6903 0x6903
+#define PCI_CHIP_TOPAZ_6907 0x6907
+#define PCI_CHIP_TONGA_6920 0x6920
+#define PCI_CHIP_TONGA_6921 0x6921
+#define PCI_CHIP_TONGA_6938 0x6938
+#define PCI_CHIP_TONGA_6939 0x6939
+#define PCI_CHIP_TONGA_692B 0x692B
+#define PCI_CHIP_TONGA_692F 0x692F
+#define PCI_CHIP_CARRIZO_9870 0x9870
+#define PCI_CHIP_CARRIZO_9874 0x9874
+#define PCI_CHIP_CARRIZO_9875 0x9875
+#define PCI_CHIP_CARRIZO_9876 0x9876
+#define PCI_CHIP_CARRIZO_9877 0x9877
diff --git a/src/compat-api.h b/src/compat-api.h
new file mode 100644
index 0000000..b299f83
--- /dev/null
+++ b/src/compat-api.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 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 (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Dave Airlie <airlied@redhat.com>
+ */
+
+/* this file provides API compat between server post 1.13 and pre it,
+ it should be reused inside as many drivers as possible */
+#ifndef COMPAT_API_H
+#define COMPAT_API_H
+
+#ifndef GLYPH_HAS_GLYPH_PICTURE_ACCESSOR
+#define GetGlyphPicture(g, s) GlyphPicture((g))[(s)->myNum]
+#define SetGlyphPicture(g, s, p) GlyphPicture((g))[(s)->myNum] = p
+#endif
+
+#ifndef XF86_HAS_SCRN_CONV
+#define xf86ScreenToScrn(s) xf86Screens[(s)->myNum]
+#define xf86ScrnToScreen(s) screenInfo.screens[(s)->scrnIndex]
+#endif
+
+#ifndef XF86_SCRN_INTERFACE
+
+#define SCRN_ARG_TYPE int
+#define SCRN_INFO_PTR(arg1) ScrnInfoPtr pScrn = xf86Screens[(arg1)]
+
+#define SCREEN_ARG_TYPE int
+#define SCREEN_PTR(arg1) ScreenPtr pScreen = screenInfo.screens[(arg1)]
+
+#define SCREEN_INIT_ARGS_DECL int i, ScreenPtr pScreen, int argc, char **argv
+
+#define BLOCKHANDLER_ARGS_DECL int arg, pointer blockData, pointer pTimeout, pointer pReadmask
+#define BLOCKHANDLER_ARGS arg, blockData, pTimeout, pReadmask
+
+#define CLOSE_SCREEN_ARGS_DECL int scrnIndex, ScreenPtr pScreen
+#define CLOSE_SCREEN_ARGS scrnIndex, pScreen
+
+#define ADJUST_FRAME_ARGS_DECL int arg, int x, int y, int flags
+
+#define SWITCH_MODE_ARGS_DECL int arg, DisplayModePtr mode, int flags
+
+#define FREE_SCREEN_ARGS_DECL int arg, int flags
+
+#define VT_FUNC_ARGS_DECL int arg, int flags
+#define VT_FUNC_ARGS(flags) pScrn->scrnIndex, (flags)
+
+#define XF86_ENABLEDISABLEFB_ARG(x) ((x)->scrnIndex)
+#else
+#define SCRN_ARG_TYPE ScrnInfoPtr
+#define SCRN_INFO_PTR(arg1) ScrnInfoPtr pScrn = (arg1)
+
+#define SCREEN_ARG_TYPE ScreenPtr
+#define SCREEN_PTR(arg1) ScreenPtr pScreen = (arg1)
+
+#define SCREEN_INIT_ARGS_DECL ScreenPtr pScreen, int argc, char **argv
+
+#define BLOCKHANDLER_ARGS_DECL ScreenPtr arg, pointer pTimeout, pointer pReadmask
+#define BLOCKHANDLER_ARGS arg, pTimeout, pReadmask
+
+#define CLOSE_SCREEN_ARGS_DECL ScreenPtr pScreen
+#define CLOSE_SCREEN_ARGS pScreen
+
+#define ADJUST_FRAME_ARGS_DECL ScrnInfoPtr arg, int x, int y
+#define SWITCH_MODE_ARGS_DECL ScrnInfoPtr arg, DisplayModePtr mode
+
+#define FREE_SCREEN_ARGS_DECL ScrnInfoPtr arg
+
+#define VT_FUNC_ARGS_DECL ScrnInfoPtr arg
+#define VT_FUNC_ARGS(flags) pScrn
+
+#define XF86_ENABLEDISABLEFB_ARG(x) (x)
+
+#endif
+
+#endif
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
new file mode 100644
index 0000000..59a3e5a
--- /dev/null
+++ b/src/drmmode_display.c
@@ -0,0 +1,1855 @@
+/*
+ * Copyright © 2007 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 (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include "micmap.h"
+#include "xf86cmap.h"
+#include "sarea.h"
+
+#include "drmmode_display.h"
+#include "amdgpu_bo_helper.h"
+#include "amdgpu_pixmap.h"
+
+/* DPMS */
+#ifdef HAVE_XEXTPROTO_71
+#include <X11/extensions/dpmsconst.h>
+#else
+#define DPMS_SERVER
+#include <X11/extensions/dpms.h>
+#endif
+
+#include <gbm.h>
+
+#define DEFAULT_NOMINAL_FRAME_RATE 60
+
+static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
+
+static Bool
+AMDGPUZaphodStringMatches(ScrnInfoPtr pScrn, const char *s, char *output_name)
+{
+ int i = 0;
+ char s1[20];
+
+ do {
+ switch (*s) {
+ case ',':
+ s1[i] = '\0';
+ i = 0;
+ if (strcmp(s1, output_name) == 0)
+ return TRUE;
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+ default:
+ s1[i] = *s;
+ i++;
+ break;
+ }
+ } while (*s++);
+
+ s1[i] = '\0';
+ if (strcmp(s1, output_name) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static PixmapPtr drmmode_create_bo_pixmap(ScrnInfoPtr pScrn,
+ int width, int height,
+ int depth, int bpp,
+ int pitch,
+ struct amdgpu_buffer *bo)
+{
+ ScreenPtr pScreen = pScrn->pScreen;
+ PixmapPtr pixmap;
+
+ pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0);
+ if (!pixmap)
+ return NULL;
+
+ if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height,
+ depth, bpp, pitch, NULL)) {
+ return NULL;
+ }
+
+ amdgpu_set_pixmap_bo(pixmap, bo);
+
+ if (!amdgpu_glamor_create_textured_pixmap(pixmap)) {
+ pScreen->DestroyPixmap(pixmap);
+ return NULL;
+ }
+
+ return pixmap;
+}
+
+static void drmmode_destroy_bo_pixmap(PixmapPtr pixmap)
+{
+ ScreenPtr pScreen = pixmap->drawable.pScreen;
+
+ (*pScreen->DestroyPixmap) (pixmap);
+}
+
+static void
+drmmode_ConvertFromKMode(ScrnInfoPtr scrn,
+ drmModeModeInfo * kmode, DisplayModePtr mode)
+{
+ memset(mode, 0, sizeof(DisplayModeRec));
+ mode->status = MODE_OK;
+
+ mode->Clock = kmode->clock;
+
+ mode->HDisplay = kmode->hdisplay;
+ mode->HSyncStart = kmode->hsync_start;
+ mode->HSyncEnd = kmode->hsync_end;
+ mode->HTotal = kmode->htotal;
+ mode->HSkew = kmode->hskew;
+
+ mode->VDisplay = kmode->vdisplay;
+ mode->VSyncStart = kmode->vsync_start;
+ mode->VSyncEnd = kmode->vsync_end;
+ mode->VTotal = kmode->vtotal;
+ mode->VScan = kmode->vscan;
+
+ mode->Flags = kmode->flags; //& FLAG_BITS;
+ mode->name = strdup(kmode->name);
+
+ if (kmode->type & DRM_MODE_TYPE_DRIVER)
+ mode->type = M_T_DRIVER;
+ if (kmode->type & DRM_MODE_TYPE_PREFERRED)
+ mode->type |= M_T_PREFERRED;
+ xf86SetModeCrtc(mode, scrn->adjustFlags);
+}
+
+static void
+drmmode_ConvertToKMode(ScrnInfoPtr scrn,
+ drmModeModeInfo * kmode, DisplayModePtr mode)
+{
+ memset(kmode, 0, sizeof(*kmode));
+
+ kmode->clock = mode->Clock;
+ kmode->hdisplay = mode->HDisplay;
+ kmode->hsync_start = mode->HSyncStart;
+ kmode->hsync_end = mode->HSyncEnd;
+ kmode->htotal = mode->HTotal;
+ kmode->hskew = mode->HSkew;
+
+ kmode->vdisplay = mode->VDisplay;
+ kmode->vsync_start = mode->VSyncStart;
+ kmode->vsync_end = mode->VSyncEnd;
+ kmode->vtotal = mode->VTotal;
+ kmode->vscan = mode->VScan;
+
+ kmode->flags = mode->Flags; //& FLAG_BITS;
+ if (mode->name)
+ strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
+ kmode->name[DRM_DISPLAY_MODE_LEN - 1] = 0;
+
+}
+
+/*
+ * Retrieves present time in microseconds that is compatible
+ * with units used by vblank timestamps. Depending on the kernel
+ * version and DRM kernel module configuration, the vblank
+ * timestamp can either be in real time or monotonic time
+ */
+int drmmode_get_current_ust(int drm_fd, CARD64 * ust)
+{
+ uint64_t cap_value;
+ int ret;
+ struct timespec now;
+
+ ret = drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap_value);
+ if (ret || !cap_value)
+ /* old kernel or drm_timestamp_monotonic turned off */
+ ret = clock_gettime(CLOCK_REALTIME, &now);
+ else
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret)
+ return ret;
+ *ust = ((CARD64) now.tv_sec * 1000000) + ((CARD64) now.tv_nsec / 1000);
+ return 0;
+}
+
+static void drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ ScrnInfoPtr scrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ CARD64 ust;
+ int ret;
+
+ if (drmmode_crtc->dpms_mode == DPMSModeOn && mode != DPMSModeOn) {
+ drmVBlank vbl;
+
+ /*
+ * On->Off transition: record the last vblank time,
+ * sequence number and frame period.
+ */
+ vbl.request.type = DRM_VBLANK_RELATIVE;
+ vbl.request.type |= amdgpu_populate_vbl_request_type(crtc);
+ vbl.request.sequence = 0;
+ ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
+ if (ret)
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "%s cannot get last vblank counter\n",
+ __func__);
+ else {
+ CARD64 seq = (CARD64) vbl.reply.sequence;
+ CARD64 nominal_frame_rate, pix_in_frame;
+
+ ust = ((CARD64) vbl.reply.tval_sec * 1000000) +
+ vbl.reply.tval_usec;
+ drmmode_crtc->dpms_last_ust = ust;
+ drmmode_crtc->dpms_last_seq = seq;
+ nominal_frame_rate = crtc->mode.Clock;
+ nominal_frame_rate *= 1000;
+ pix_in_frame = crtc->mode.HTotal * crtc->mode.VTotal;
+ if (nominal_frame_rate == 0 || pix_in_frame == 0)
+ nominal_frame_rate = DEFAULT_NOMINAL_FRAME_RATE;
+ else
+ nominal_frame_rate /= pix_in_frame;
+ drmmode_crtc->dpms_last_fps = nominal_frame_rate;
+ }
+ } else if (drmmode_crtc->dpms_mode != DPMSModeOn && mode == DPMSModeOn) {
+ /*
+ * Off->On transition: calculate and accumulate the
+ * number of interpolated vblanks while we were in Off state
+ */
+ ret = drmmode_get_current_ust(info->dri2.drm_fd, &ust);
+ if (ret)
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "%s cannot get current time\n", __func__);
+ else if (drmmode_crtc->dpms_last_ust) {
+ CARD64 time_elapsed, delta_seq;
+ time_elapsed = ust - drmmode_crtc->dpms_last_ust;
+ delta_seq = time_elapsed * drmmode_crtc->dpms_last_fps;
+ delta_seq /= 1000000;
+ drmmode_crtc->interpolated_vblanks += delta_seq;
+
+ }
+ }
+ drmmode_crtc->dpms_mode = mode;
+}
+
+/* TODO: currently this function only clear the front buffer to zero */
+/* Moving forward, we might to look into making the copy with glamor instead */
+void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ uint32_t size = pScrn->displayWidth * info->pixel_bytes * pScrn->virtualY;
+
+ /* memset the bo */
+ if (info->gbm) {
+ void *cpu_ptr = malloc(size);
+
+ if (cpu_ptr) {
+ memset(cpu_ptr, 0x00, size);
+ gbm_bo_write(info->front_buffer->bo.gbm, cpu_ptr, size);
+ free(cpu_ptr);
+ }
+ } else {
+ memset(info->front_buffer->cpu_ptr, 0x00, size);
+ }
+}
+
+static Bool
+drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
+ Rotation rotation, int x, int y)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ int saved_x, saved_y;
+ Rotation saved_rotation;
+ DisplayModeRec saved_mode;
+ uint32_t *output_ids;
+ int output_count = 0;
+ Bool ret = TRUE;
+ int i;
+ int fb_id;
+ drmModeModeInfo kmode;
+ union gbm_bo_handle bo_handle;
+
+ if (drmmode->fb_id == 0) {
+ if (info->gbm) {
+ bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm);
+ } else {
+ if (amdgpu_bo_export(info->front_buffer->bo.amdgpu,
+ amdgpu_bo_handle_type_kms,
+ &bo_handle.u32)) {
+ ErrorF("failed to get BO handle for FB\n");
+ return FALSE;
+ }
+ }
+
+ ret = drmModeAddFB(drmmode->fd,
+ pScrn->virtualX,
+ pScrn->virtualY,
+ pScrn->depth, pScrn->bitsPerPixel,
+ pScrn->displayWidth * info->pixel_bytes,
+ bo_handle.u32, &drmmode->fb_id);
+ if (ret < 0) {
+ ErrorF("failed to add fb\n");
+ return FALSE;
+ }
+ }
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+ saved_rotation = crtc->rotation;
+
+ if (mode) {
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+ crtc->rotation = rotation;
+ crtc->transformPresent = FALSE;
+ }
+
+ output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
+ if (!output_ids) {
+ ret = FALSE;
+ goto done;
+ }
+
+ if (mode) {
+ for (i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+ drmmode_output_private_ptr drmmode_output;
+
+ if (output->crtc != crtc)
+ continue;
+
+ drmmode_output = output->driver_private;
+ output_ids[output_count] =
+ drmmode_output->mode_output->connector_id;
+ output_count++;
+ }
+
+ if (!xf86CrtcRotate(crtc)) {
+ goto done;
+ }
+ crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
+ crtc->gamma_blue, crtc->gamma_size);
+
+ drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
+
+ fb_id = drmmode->fb_id;
+#ifdef AMDGPU_PIXMAP_SHARING
+ if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) {
+ x = drmmode_crtc->scanout_pixmap_x;
+ y = 0;
+ } else
+#endif
+ if (drmmode_crtc->rotate_fb_id) {
+ fb_id = drmmode_crtc->rotate_fb_id;
+ x = y = 0;
+ }
+ ret =
+ drmModeSetCrtc(drmmode->fd,
+ drmmode_crtc->mode_crtc->crtc_id, fb_id, x,
+ y, output_ids, output_count, &kmode);
+ if (ret)
+ xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
+ "failed to set mode: %s", strerror(-ret));
+ else
+ ret = TRUE;
+
+ if (crtc->scrn->pScreen)
+ xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
+ /* go through all the outputs and force DPMS them back on? */
+ for (i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+
+ if (output->crtc != crtc)
+ continue;
+
+ output->funcs->dpms(output, DPMSModeOn);
+ }
+ }
+
+ if (pScrn->pScreen &&
+ !xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE))
+ xf86_reload_cursors(pScrn->pScreen);
+
+done:
+ if (!ret) {
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ crtc->rotation = saved_rotation;
+ crtc->mode = saved_mode;
+ }
+#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
+ else
+ crtc->active = TRUE;
+#endif
+
+ return ret;
+}
+
+static void drmmode_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
+{
+
+}
+
+static void drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+ drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
+}
+
+static void drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ int i;
+ uint32_t cursor_size = info->cursor_w * info->cursor_h;
+
+ if (info->gbm) {
+ uint32_t ptr[cursor_size];
+
+ for (i = 0; i < cursor_size; i++)
+ ptr[i] = cpu_to_le32(image[i]);
+
+ gbm_bo_write(drmmode_crtc->cursor_buffer->bo.gbm, ptr, cursor_size * 4);
+ } else {
+ /* cursor should be mapped already */
+ uint32_t *ptr = (uint32_t *) (drmmode_crtc->cursor_buffer->cpu_ptr);
+
+ for (i = 0; i < cursor_size; i++)
+ ptr[i] = cpu_to_le32(image[i]);
+ }
+}
+
+static void drmmode_hide_cursor(xf86CrtcPtr crtc)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+ drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0,
+ info->cursor_w, info->cursor_h);
+
+}
+
+static void drmmode_show_cursor(xf86CrtcPtr crtc)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ union gbm_bo_handle bo_handle;
+
+ if (info->gbm) {
+ bo_handle = gbm_bo_get_handle(drmmode_crtc->cursor_buffer->bo.gbm);
+ } else {
+ if (amdgpu_bo_export(drmmode_crtc->cursor_buffer->bo.amdgpu,
+ amdgpu_bo_handle_type_kms,
+ &bo_handle.u32)) {
+ ErrorF("failed to get BO handle for cursor\n");
+ return;
+ }
+ }
+
+ drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, bo_handle.u32,
+ info->cursor_w, info->cursor_h);
+}
+
+static void *drmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width,
+ int height)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ struct amdgpu_buffer *rotate_buffer = NULL;
+ int ret;
+ int pitch;
+ union gbm_bo_handle bo_handle;
+
+ /* rotation requires acceleration */
+ if (info->shadow_fb) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Rotation requires acceleration!\n");
+ return NULL;
+ }
+
+ rotate_buffer = amdgpu_alloc_pixmap_bo(pScrn, width, height,
+ pScrn->depth, 0,
+ pScrn->bitsPerPixel, &pitch);
+ if (!rotate_buffer) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to allocate rotation buffer memory\n");
+ return NULL;
+ }
+
+ bo_handle = gbm_bo_get_handle(rotate_buffer->bo.gbm);
+ ret = drmModeAddFB(drmmode->fd, width, height, crtc->scrn->depth,
+ crtc->scrn->bitsPerPixel, pitch,
+ bo_handle.u32, &drmmode_crtc->rotate_fb_id);
+ if (ret) {
+ ErrorF("failed to add rotate fb\n");
+ }
+
+ drmmode_crtc->rotate_buffer = rotate_buffer;
+ return rotate_buffer;
+}
+
+static PixmapPtr
+drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ unsigned long rotate_pitch;
+ PixmapPtr rotate_pixmap;
+
+ if (!data)
+ data = drmmode_crtc_shadow_allocate(crtc, width, height);
+
+ rotate_pitch = gbm_bo_get_stride(drmmode_crtc->rotate_buffer->bo.gbm);
+
+ rotate_pixmap = drmmode_create_bo_pixmap(pScrn,
+ width, height,
+ pScrn->depth,
+ pScrn->bitsPerPixel,
+ rotate_pitch,
+ drmmode_crtc->rotate_buffer);
+ if (rotate_pixmap == NULL) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Couldn't allocate shadow pixmap for rotated CRTC\n");
+ }
+ return rotate_pixmap;
+
+}
+
+static void
+drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap,
+ void *data)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+ if (rotate_pixmap)
+ drmmode_destroy_bo_pixmap(rotate_pixmap);
+
+ if (data) {
+ drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
+ drmmode_crtc->rotate_fb_id = 0;
+ amdgpu_bo_unref(&drmmode_crtc->rotate_buffer);
+ drmmode_crtc->rotate_buffer = NULL;
+ }
+
+}
+
+static void
+drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
+ uint16_t * blue, int size)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+ drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
+ size, red, green, blue);
+}
+
+#ifdef AMDGPU_PIXMAP_SHARING
+static Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
+{
+ ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
+ PixmapPtr screenpix = screen->GetScreenPixmap(screen);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ int c, total_width = 0, max_height = 0, this_x = 0;
+
+ if (!ppix) {
+ if (crtc->randr_crtc->scanout_pixmap)
+ PixmapStopDirtyTracking(crtc->
+ randr_crtc->scanout_pixmap,
+ screenpix);
+ drmmode_crtc->scanout_pixmap_x = 0;
+ return TRUE;
+ }
+
+ /* iterate over all the attached crtcs -
+ work out bounding box */
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr iter = xf86_config->crtc[c];
+ if (!iter->enabled && iter != crtc)
+ continue;
+ if (iter == crtc) {
+ this_x = total_width;
+ total_width += ppix->drawable.width;
+ if (max_height < ppix->drawable.height)
+ max_height = ppix->drawable.height;
+ } else {
+ total_width += iter->mode.HDisplay;
+ if (max_height < iter->mode.VDisplay)
+ max_height = iter->mode.VDisplay;
+ }
+#ifndef HAS_DIRTYTRACKING2
+ if (iter != crtc) {
+ ErrorF
+ ("Cannot do multiple crtcs without X server dirty tracking 2 interface\n");
+ return FALSE;
+ }
+#endif
+ }
+
+ if (total_width != screenpix->drawable.width ||
+ max_height != screenpix->drawable.height) {
+ Bool ret;
+ ret =
+ drmmode_xf86crtc_resize(crtc->scrn, total_width,
+ max_height);
+ if (ret == FALSE)
+ return FALSE;
+
+ screenpix = screen->GetScreenPixmap(screen);
+ screen->width = screenpix->drawable.width = total_width;
+ screen->height = screenpix->drawable.height = max_height;
+ }
+ drmmode_crtc->scanout_pixmap_x = this_x;
+#ifdef HAS_DIRTYTRACKING2
+ PixmapStartDirtyTracking2(ppix, screenpix, 0, 0, this_x, 0);
+#else
+ PixmapStartDirtyTracking(ppix, screenpix, 0, 0);
+#endif
+ return TRUE;
+}
+#endif
+
+static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
+ .dpms = drmmode_crtc_dpms,
+ .set_mode_major = drmmode_set_mode_major,
+ .set_cursor_colors = drmmode_set_cursor_colors,
+ .set_cursor_position = drmmode_set_cursor_position,
+ .show_cursor = drmmode_show_cursor,
+ .hide_cursor = drmmode_hide_cursor,
+ .load_cursor_argb = drmmode_load_cursor_argb,
+
+ .gamma_set = drmmode_crtc_gamma_set,
+ .shadow_create = drmmode_crtc_shadow_create,
+ .shadow_allocate = drmmode_crtc_shadow_allocate,
+ .shadow_destroy = drmmode_crtc_shadow_destroy,
+ .destroy = NULL, /* XXX */
+#ifdef AMDGPU_PIXMAP_SHARING
+ .set_scanout_pixmap = drmmode_set_scanout_pixmap,
+#endif
+};
+
+int drmmode_get_crtc_id(xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ return drmmode_crtc->hw_id;
+}
+
+void drmmode_crtc_hw_id(xf86CrtcPtr crtc)
+{
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ ScrnInfoPtr pScrn = crtc->scrn;
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ struct drm_amdgpu_info ginfo;
+ int r;
+
+ memset(&ginfo, 0, sizeof(ginfo));
+ ginfo.return_pointer = (uintptr_t)&drmmode_crtc->hw_id;
+ ginfo.return_size = sizeof(drmmode_crtc->hw_id);
+ ginfo.query = AMDGPU_INFO_CRTC_FROM_ID;
+ ginfo.mode_crtc.id = drmmode_crtc->mode_crtc->crtc_id;
+ r = drmCommandWriteRead(info->dri2.drm_fd, DRM_AMDGPU_INFO, &ginfo,
+ sizeof(ginfo));
+ if (r)
+ drmmode_crtc->hw_id = -1;
+}
+
+static void drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
+{
+ xf86CrtcPtr crtc;
+ drmmode_crtc_private_ptr drmmode_crtc;
+
+ crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
+ if (crtc == NULL)
+ return;
+
+ drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
+ drmmode_crtc->mode_crtc =
+ drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]);
+ drmmode_crtc->drmmode = drmmode;
+ crtc->driver_private = drmmode_crtc;
+ drmmode_crtc_hw_id(crtc);
+
+ return;
+}
+
+static xf86OutputStatus drmmode_output_detect(xf86OutputPtr output)
+{
+ /* go to the hw and retrieve a new output struct */
+ drmmode_output_private_ptr drmmode_output = output->driver_private;
+ drmmode_ptr drmmode = drmmode_output->drmmode;
+ xf86OutputStatus status;
+ drmModeFreeConnector(drmmode_output->mode_output);
+
+ drmmode_output->mode_output =
+ drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
+ if (!drmmode_output->mode_output)
+ return XF86OutputStatusDisconnected;
+
+ switch (drmmode_output->mode_output->connection) {
+ case DRM_MODE_CONNECTED:
+ status = XF86OutputStatusConnected;
+ break;
+ case DRM_MODE_DISCONNECTED:
+ status = XF86OutputStatusDisconnected;
+ break;
+ default:
+ case DRM_MODE_UNKNOWNCONNECTION:
+ status = XF86OutputStatusUnknown;
+ break;
+ }
+ return status;
+}
+
+static Bool
+drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
+{
+ return MODE_OK;
+}
+
+static DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output)
+{
+ drmmode_output_private_ptr drmmode_output = output->driver_private;
+ drmModeConnectorPtr koutput = drmmode_output->mode_output;
+ drmmode_ptr drmmode = drmmode_output->drmmode;
+ int i;
+ DisplayModePtr Modes = NULL, Mode;
+ drmModePropertyPtr props;
+ xf86MonPtr mon = NULL;
+
+ if (!koutput)
+ return NULL;
+
+ /* look for an EDID property */
+ for (i = 0; i < koutput->count_props; i++) {
+ props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
+ if (props && (props->flags & DRM_MODE_PROP_BLOB)) {
+ if (!strcmp(props->name, "EDID")) {
+ if (drmmode_output->edid_blob)
+ drmModeFreePropertyBlob
+ (drmmode_output->edid_blob);
+ drmmode_output->edid_blob =
+ drmModeGetPropertyBlob(drmmode->fd,
+ koutput->prop_values
+ [i]);
+ }
+ }
+ if (props)
+ drmModeFreeProperty(props);
+ }
+
+ if (drmmode_output->edid_blob) {
+ mon = xf86InterpretEDID(output->scrn->scrnIndex,
+ drmmode_output->edid_blob->data);
+ if (mon && drmmode_output->edid_blob->length > 128)
+ mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
+ }
+ xf86OutputSetEDID(output, mon);
+
+ /* modes should already be available */
+ for (i = 0; i < koutput->count_modes; i++) {
+ Mode = xnfalloc(sizeof(DisplayModeRec));
+
+ drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
+ Mode);
+ Modes = xf86ModesAdd(Modes, Mode);
+
+ }
+ return Modes;
+}
+
+static void drmmode_output_destroy(xf86OutputPtr output)
+{
+ drmmode_output_private_ptr drmmode_output = output->driver_private;
+ int i;
+
+ if (drmmode_output->edid_blob)
+ drmModeFreePropertyBlob(drmmode_output->edid_blob);
+ for (i = 0; i < drmmode_output->num_props; i++) {
+ drmModeFreeProperty(drmmode_output->props[i].mode_prop);
+ free(drmmode_output->props[i].atoms);
+ }
+ for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
+ drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
+ free(drmmode_output->mode_encoders);
+ }
+ free(drmmode_output->props);
+ drmModeFreeConnector(drmmode_output->mode_output);
+ free(drmmode_output);
+ output->driver_private = NULL;
+}
+
+static void drmmode_output_dpms(xf86OutputPtr output, int mode)
+{
+ drmmode_output_private_ptr drmmode_output = output->driver_private;
+ drmModeConnectorPtr koutput = drmmode_output->mode_output;
+ drmmode_ptr drmmode = drmmode_output->drmmode;
+
+ drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
+ drmmode_output->dpms_enum_id, mode);
+ return;
+}
+
+static Bool drmmode_property_ignore(drmModePropertyPtr prop)
+{
+ if (!prop)
+ return TRUE;
+ /* ignore blob prop */
+ if (prop->flags & DRM_MODE_PROP_BLOB)
+ return TRUE;
+ /* ignore standard property */
+ if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS"))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void drmmode_output_create_resources(xf86OutputPtr output)
+{
+ drmmode_output_private_ptr drmmode_output = output->driver_private;
+ drmModeConnectorPtr mode_output = drmmode_output->mode_output;
+ drmmode_ptr drmmode = drmmode_output->drmmode;
+ drmModePropertyPtr drmmode_prop;
+ int i, j, err;
+
+ drmmode_output->props =
+ calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
+ if (!drmmode_output->props)
+ return;
+
+ drmmode_output->num_props = 0;
+ for (i = 0, j = 0; i < mode_output->count_props; i++) {
+ drmmode_prop =
+ drmModeGetProperty(drmmode->fd, mode_output->props[i]);
+ if (drmmode_property_ignore(drmmode_prop)) {
+ drmModeFreeProperty(drmmode_prop);
+ continue;
+ }
+ drmmode_output->props[j].mode_prop = drmmode_prop;
+ drmmode_output->props[j].value = mode_output->prop_values[i];
+ drmmode_output->num_props++;
+ j++;
+ }
+
+ for (i = 0; i < drmmode_output->num_props; i++) {
+ drmmode_prop_ptr p = &drmmode_output->props[i];
+ drmmode_prop = p->mode_prop;
+
+ if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
+ INT32 range[2];
+ INT32 value = p->value;
+
+ p->num_atoms = 1;
+ p->atoms = calloc(p->num_atoms, sizeof(Atom));
+ if (!p->atoms)
+ continue;
+ p->atoms[0] =
+ MakeAtom(drmmode_prop->name,
+ strlen(drmmode_prop->name), TRUE);
+ range[0] = drmmode_prop->values[0];
+ range[1] = drmmode_prop->values[1];
+ err =
+ RRConfigureOutputProperty(output->randr_output,
+ p->atoms[0], FALSE, TRUE,
+ drmmode_prop->flags &
+ DRM_MODE_PROP_IMMUTABLE ?
+ TRUE : FALSE, 2, range);
+ if (err != 0) {
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRConfigureOutputProperty error, %d\n",
+ err);
+ }
+ err =
+ RRChangeOutputProperty(output->randr_output,
+ p->atoms[0], XA_INTEGER, 32,
+ PropModeReplace, 1, &value,
+ FALSE, TRUE);
+ if (err != 0) {
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRChangeOutputProperty error, %d\n",
+ err);
+ }
+ } else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
+ p->num_atoms = drmmode_prop->count_enums + 1;
+ p->atoms = calloc(p->num_atoms, sizeof(Atom));
+ if (!p->atoms)
+ continue;
+ p->atoms[0] =
+ MakeAtom(drmmode_prop->name,
+ strlen(drmmode_prop->name), TRUE);
+ for (j = 1; j <= drmmode_prop->count_enums; j++) {
+ struct drm_mode_property_enum *e =
+ &drmmode_prop->enums[j - 1];
+ p->atoms[j] =
+ MakeAtom(e->name, strlen(e->name), TRUE);
+ }
+ err =
+ RRConfigureOutputProperty(output->randr_output,
+ p->atoms[0], FALSE, FALSE,
+ drmmode_prop->flags &
+ DRM_MODE_PROP_IMMUTABLE ?
+ TRUE : FALSE,
+ p->num_atoms - 1,
+ (INT32 *) & p->atoms[1]);
+ if (err != 0) {
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRConfigureOutputProperty error, %d\n",
+ err);
+ }
+ for (j = 0; j < drmmode_prop->count_enums; j++)
+ if (drmmode_prop->enums[j].value == p->value)
+ break;
+ /* there's always a matching value */
+ err =
+ RRChangeOutputProperty(output->randr_output,
+ p->atoms[0], XA_ATOM, 32,
+ PropModeReplace, 1,
+ &p->atoms[j + 1], FALSE,
+ TRUE);
+ if (err != 0) {
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRChangeOutputProperty error, %d\n",
+ err);
+ }
+ }
+ }
+}
+
+static Bool
+drmmode_output_set_property(xf86OutputPtr output, Atom property,
+ RRPropertyValuePtr value)
+{
+ drmmode_output_private_ptr drmmode_output = output->driver_private;
+ drmmode_ptr drmmode = drmmode_output->drmmode;
+ int i;
+
+ for (i = 0; i < drmmode_output->num_props; i++) {
+ drmmode_prop_ptr p = &drmmode_output->props[i];
+
+ if (p->atoms[0] != property)
+ continue;
+
+ if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
+ uint32_t val;
+
+ if (value->type != XA_INTEGER || value->format != 32 ||
+ value->size != 1)
+ return FALSE;
+ val = *(uint32_t *) value->data;
+
+ drmModeConnectorSetProperty(drmmode->fd,
+ drmmode_output->output_id,
+ p->mode_prop->prop_id,
+ (uint64_t) val);
+ return TRUE;
+ } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
+ Atom atom;
+ const char *name;
+ int j;
+
+ if (value->type != XA_ATOM || value->format != 32
+ || value->size != 1)
+ return FALSE;
+ memcpy(&atom, value->data, 4);
+ name = NameForAtom(atom);
+
+ /* search for matching name string, then set its value down */
+ for (j = 0; j < p->mode_prop->count_enums; j++) {
+ if (!strcmp(p->mode_prop->enums[j].name, name)) {
+ drmModeConnectorSetProperty(drmmode->fd,
+ drmmode_output->output_id,
+ p->mode_prop->prop_id,
+ p->mode_prop->enums
+ [j].value);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static Bool drmmode_output_get_property(xf86OutputPtr output, Atom property)
+{
+ return TRUE;
+}
+
+static const xf86OutputFuncsRec drmmode_output_funcs = {
+ .dpms = drmmode_output_dpms,
+ .create_resources = drmmode_output_create_resources,
+ .set_property = drmmode_output_set_property,
+ .get_property = drmmode_output_get_property,
+#if 0
+
+ .save = drmmode_crt_save,
+ .restore = drmmode_crt_restore,
+ .mode_fixup = drmmode_crt_mode_fixup,
+ .prepare = drmmode_output_prepare,
+ .mode_set = drmmode_crt_mode_set,
+ .commit = drmmode_output_commit,
+#endif
+ .detect = drmmode_output_detect,
+ .mode_valid = drmmode_output_mode_valid,
+
+ .get_modes = drmmode_output_get_modes,
+ .destroy = drmmode_output_destroy
+};
+
+static int subpixel_conv_table[7] = { 0, SubPixelUnknown,
+ SubPixelHorizontalRGB,
+ SubPixelHorizontalBGR,
+ SubPixelVerticalRGB,
+ SubPixelVerticalBGR,
+ SubPixelNone
+};
+
+const char *output_names[] = { "None",
+ "VGA",
+ "DVI-I",
+ "DVI-D",
+ "DVI-A",
+ "Composite",
+ "S-video",
+ "LVDS",
+ "CTV",
+ "DIN",
+ "DisplayPort",
+ "HDMI-A",
+ "HDMI-B",
+ "TV",
+ "eDP",
+ "Virtual",
+ "DSI",
+};
+
+#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
+
+static void
+drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+ xf86OutputPtr output;
+ drmModeConnectorPtr koutput;
+ drmModeEncoderPtr *kencoders = NULL;
+ drmmode_output_private_ptr drmmode_output;
+ drmModePropertyPtr props;
+ char name[32];
+ int i;
+ const char *s;
+
+ koutput =
+ drmModeGetConnector(drmmode->fd,
+ drmmode->mode_res->connectors[num]);
+ if (!koutput)
+ return;
+
+ kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
+ if (!kencoders) {
+ goto out_free_encoders;
+ }
+
+ for (i = 0; i < koutput->count_encoders; i++) {
+ kencoders[i] =
+ drmModeGetEncoder(drmmode->fd, koutput->encoders[i]);
+ if (!kencoders[i]) {
+ goto out_free_encoders;
+ }
+ }
+
+ if (koutput->connector_type >= NUM_OUTPUT_NAMES)
+ snprintf(name, 32, "Unknown%d-%d", koutput->connector_type,
+ koutput->connector_type_id - 1);
+#ifdef AMDGPU_PIXMAP_SHARING
+ else if (pScrn->is_gpu)
+ snprintf(name, 32, "%s-%d-%d",
+ output_names[koutput->connector_type],
+ pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1,
+ koutput->connector_type_id - 1);
+#endif
+ else
+ snprintf(name, 32, "%s-%d",
+ output_names[koutput->connector_type],
+ koutput->connector_type_id - 1);
+
+ if (xf86IsEntityShared(pScrn->entityList[0])) {
+ if ((s =
+ xf86GetOptValString(info->Options, OPTION_ZAPHOD_HEADS))) {
+ if (!AMDGPUZaphodStringMatches(pScrn, s, name))
+ goto out_free_encoders;
+ } else {
+ if (info->IsPrimary && (num != 0))
+ goto out_free_encoders;
+ else if (info->IsSecondary && (num != 1))
+ goto out_free_encoders;
+ }
+ }
+
+ output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name);
+ if (!output) {
+ goto out_free_encoders;
+ }
+
+ drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
+ if (!drmmode_output) {
+ xf86OutputDestroy(output);
+ goto out_free_encoders;
+ }
+
+ drmmode_output->output_id = drmmode->mode_res->connectors[num];
+ drmmode_output->mode_output = koutput;
+ drmmode_output->mode_encoders = kencoders;
+ drmmode_output->drmmode = drmmode;
+ output->mm_width = koutput->mmWidth;
+ output->mm_height = koutput->mmHeight;
+
+ output->subpixel_order = subpixel_conv_table[koutput->subpixel];
+ output->interlaceAllowed = TRUE;
+ output->doubleScanAllowed = TRUE;
+ output->driver_private = drmmode_output;
+
+ output->possible_crtcs = 0xffffffff;
+ for (i = 0; i < koutput->count_encoders; i++) {
+ output->possible_crtcs &= kencoders[i]->possible_crtcs;
+ }
+ /* work out the possible clones later */
+ output->possible_clones = 0;
+
+ for (i = 0; i < koutput->count_props; i++) {
+ props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
+ if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
+ if (!strcmp(props->name, "DPMS")) {
+ drmmode_output->dpms_enum_id =
+ koutput->props[i];
+ drmModeFreeProperty(props);
+ break;
+ }
+ drmModeFreeProperty(props);
+ }
+ }
+
+ return;
+out_free_encoders:
+ if (kencoders) {
+ for (i = 0; i < koutput->count_encoders; i++)
+ drmModeFreeEncoder(kencoders[i]);
+ free(kencoders);
+ }
+ drmModeFreeConnector(koutput);
+
+}
+
+uint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
+{
+ drmmode_output_private_ptr drmmode_output =
+ output->driver_private, clone_drmout;
+ int i;
+ xf86OutputPtr clone_output;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ int index_mask = 0;
+
+ if (drmmode_output->enc_clone_mask == 0)
+ return index_mask;
+
+ for (i = 0; i < xf86_config->num_output; i++) {
+ clone_output = xf86_config->output[i];
+ clone_drmout = clone_output->driver_private;
+ if (output == clone_output)
+ continue;
+
+ if (clone_drmout->enc_mask == 0)
+ continue;
+ if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask)
+ index_mask |= (1 << i);
+ }
+ return index_mask;
+}
+
+static void drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
+{
+ int i, j;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+
+ for (i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+ drmmode_output_private_ptr drmmode_output;
+
+ drmmode_output = output->driver_private;
+ drmmode_output->enc_clone_mask = 0xff;
+ /* and all the possible encoder clones for this output together */
+ for (j = 0; j < drmmode_output->mode_output->count_encoders;
+ j++) {
+ int k;
+ for (k = 0; k < drmmode->mode_res->count_encoders; k++) {
+ if (drmmode->mode_res->encoders[k] ==
+ drmmode_output->
+ mode_encoders[j]->encoder_id)
+ drmmode_output->enc_mask |= (1 << k);
+ }
+
+ drmmode_output->enc_clone_mask &=
+ drmmode_output->mode_encoders[j]->possible_clones;
+ }
+ }
+
+ for (i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+ output->possible_clones = find_clones(scrn, output);
+ }
+}
+
+/* returns pitch alignment in pixels */
+int drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+
+ if (info->have_tiling_info)
+ /* linear aligned requirements */
+ return 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.
+ */
+ return 512;
+}
+
+static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ drmmode_crtc_private_ptr
+ drmmode_crtc = xf86_config->crtc[0]->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ struct amdgpu_buffer *old_front = NULL;
+ Bool ret;
+ ScreenPtr screen = xf86ScrnToScreen(scrn);
+ uint32_t old_fb_id;
+ int i, pitch, old_width, old_height, old_pitch;
+ int cpp = info->pixel_bytes;
+ PixmapPtr ppix = screen->GetScreenPixmap(screen);
+ void *fb_shadow;
+ int hint = info->use_glamor ? 0 : AMDGPU_CREATE_PIXMAP_LINEAR;
+
+ if (scrn->virtualX == width && scrn->virtualY == height)
+ return TRUE;
+
+ xf86DrvMsg(scrn->scrnIndex, X_INFO,
+ "Allocate new frame buffer %dx%d\n", width, height);
+
+ old_width = scrn->virtualX;
+ old_height = scrn->virtualY;
+ old_pitch = scrn->displayWidth;
+ old_fb_id = drmmode->fb_id;
+ old_front = info->front_buffer;
+
+ scrn->virtualX = width;
+ scrn->virtualY = height;
+
+ info->front_buffer =
+ amdgpu_alloc_pixmap_bo(scrn, scrn->virtualX, scrn->virtualY,
+ scrn->depth, hint, scrn->bitsPerPixel,
+ &pitch);
+ if (!info->front_buffer) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to allocate front buffer memory\n");
+ goto fail;
+ }
+
+ if (amdgpu_bo_map(scrn, info->front_buffer)) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to map front buffer memory\n");
+ goto fail;
+ }
+
+ xf86DrvMsg(scrn->scrnIndex, X_INFO, " => pitch %d bytes\n", pitch);
+ scrn->displayWidth = pitch / cpp;
+
+ if (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM) {
+ union gbm_bo_handle bo_handle;
+
+ bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm);
+ ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth,
+ scrn->bitsPerPixel, pitch,
+ bo_handle.u32, &drmmode->fb_id);
+ if (ret) {
+ goto fail;
+ }
+
+ amdgpu_set_pixmap_bo(ppix, info->front_buffer);
+ screen->ModifyPixmapHeader(ppix,
+ width, height, -1, -1, pitch, info->front_buffer->cpu_ptr);
+ } else {
+ uint32_t bo_handle;
+
+ if (amdgpu_bo_export(info->front_buffer->bo.amdgpu,
+ amdgpu_bo_handle_type_kms,
+ &bo_handle)) {
+ goto fail;
+ }
+ ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth,
+ scrn->bitsPerPixel, pitch,
+ bo_handle, &drmmode->fb_id);
+ if (ret) {
+ goto fail;
+ }
+
+ fb_shadow = calloc(1, pitch * scrn->virtualY);
+ if (fb_shadow == NULL)
+ goto fail;
+ free(info->fb_shadow);
+ info->fb_shadow = fb_shadow;
+ screen->ModifyPixmapHeader(ppix,
+ width, height, -1, -1, pitch,
+ info->fb_shadow);
+ }
+#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,9,99,1,0)
+ scrn->pixmapPrivate.ptr = ppix->devPrivate.ptr;
+#endif
+
+ for (i = 0; i < xf86_config->num_crtc; i++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[i];
+
+ if (!crtc->enabled)
+ continue;
+
+ drmmode_set_mode_major(crtc, &crtc->mode,
+ crtc->rotation, crtc->x, crtc->y);
+ }
+
+ if (info->use_glamor)
+ amdgpu_glamor_create_screen_resources(scrn->pScreen);
+
+ if (old_fb_id)
+ drmModeRmFB(drmmode->fd, old_fb_id);
+ if (old_front) {
+ amdgpu_bo_unref(&old_front);
+ }
+
+ return TRUE;
+
+fail:
+ if (info->front_buffer) {
+ amdgpu_bo_unref(&info->front_buffer);
+ }
+ info->front_buffer = old_front;
+ scrn->virtualX = old_width;
+ scrn->virtualY = old_height;
+ scrn->displayWidth = old_pitch;
+ drmmode->fb_id = old_fb_id;
+
+ return FALSE;
+}
+
+static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
+ drmmode_xf86crtc_resize
+};
+
+static void
+drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data)
+{
+ amdgpu_dri2_frame_event_handler(frame, tv_sec, tv_usec, event_data);
+}
+
+static void
+drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
+ unsigned int tv_usec, void *event_data)
+{
+ drmmode_flipevtcarrier_ptr flipcarrier = event_data;
+ drmmode_flipdata_ptr flipdata = flipcarrier->flipdata;
+ drmmode_ptr drmmode = flipdata->drmmode;
+
+ /* Is this the event whose info shall be delivered to higher level? */
+ if (flipcarrier->dispatch_me) {
+ /* Yes: Cache msc, ust for later delivery. */
+ flipdata->fe_frame = frame;
+ flipdata->fe_tv_sec = tv_sec;
+ flipdata->fe_tv_usec = tv_usec;
+ }
+ free(flipcarrier);
+
+ /* Last crtc completed flip? */
+ flipdata->flip_count--;
+ if (flipdata->flip_count > 0)
+ return;
+
+ /* Release framebuffer */
+ drmModeRmFB(drmmode->fd, flipdata->old_fb_id);
+
+ if (flipdata->event_data == NULL)
+ return;
+
+ /* Deliver cached msc, ust from reference crtc to flip event handler */
+ amdgpu_dri2_flip_event_handler(flipdata->fe_frame, flipdata->fe_tv_sec,
+ flipdata->fe_tv_usec,
+ flipdata->event_data);
+
+ free(flipdata);
+}
+
+static void drm_wakeup_handler(pointer data, int err, pointer p)
+{
+ drmmode_ptr drmmode = data;
+ fd_set *read_mask = p;
+
+ if (err >= 0 && FD_ISSET(drmmode->fd, read_mask)) {
+ drmHandleEvent(drmmode->fd, &drmmode->event_context);
+ }
+}
+
+Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
+{
+ int i;
+
+ xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
+
+ drmmode->scrn = pScrn;
+ drmmode->cpp = cpp;
+ drmmode->mode_res = drmModeGetResources(drmmode->fd);
+ if (!drmmode->mode_res)
+ return FALSE;
+
+ xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width,
+ drmmode->mode_res->max_height);
+ for (i = 0; i < drmmode->mode_res->count_crtcs; i++)
+ if (!xf86IsEntityShared(pScrn->entityList[0])
+ || pScrn->confScreen->device->screen == i)
+ drmmode_crtc_init(pScrn, drmmode, i);
+
+ for (i = 0; i < drmmode->mode_res->count_connectors; i++)
+ drmmode_output_init(pScrn, drmmode, i);
+
+ /* workout clones */
+ drmmode_clones_init(pScrn, drmmode);
+
+#ifdef AMDGPU_PIXMAP_SHARING
+ xf86ProviderSetup(pScrn, NULL, "amdgpu");
+#endif
+
+ xf86InitialConfiguration(pScrn, TRUE);
+
+ drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+ drmmode->event_context.vblank_handler = drmmode_vblank_handler;
+ drmmode->event_context.page_flip_handler = drmmode_flip_handler;
+
+ return TRUE;
+}
+
+void drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
+{
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ info->drmmode_inited = TRUE;
+ if (pAMDGPUEnt->fd_wakeup_registered != serverGeneration) {
+ AddGeneralSocket(drmmode->fd);
+ RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
+ drm_wakeup_handler, drmmode);
+ pAMDGPUEnt->fd_wakeup_registered = serverGeneration;
+ pAMDGPUEnt->fd_wakeup_ref = 1;
+ } else
+ pAMDGPUEnt->fd_wakeup_ref++;
+}
+
+void drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
+{
+ AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
+ AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
+
+ if (!info->drmmode_inited)
+ return;
+
+ if (pAMDGPUEnt->fd_wakeup_registered == serverGeneration &&
+ !--pAMDGPUEnt->fd_wakeup_ref) {
+ RemoveGeneralSocket(drmmode->fd);
+ RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
+ drm_wakeup_handler, drmmode);
+ }
+}
+
+void drmmode_set_cursor(ScrnInfoPtr scrn, drmmode_ptr drmmode, int id,
+ struct amdgpu_buffer *bo)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ xf86CrtcPtr crtc = xf86_config->crtc[id];
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+ drmmode_crtc->cursor_buffer = bo;
+}
+
+void drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ xf86OutputPtr output = config->output[config->compat_output];
+ xf86CrtcPtr crtc = output->crtc;
+
+ if (crtc && crtc->enabled) {
+ drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
+ }
+}
+
+Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int c;
+
+ drmmode_copy_fb(pScrn, drmmode);
+
+ for (c = 0; c < config->num_crtc; c++) {
+ xf86CrtcPtr crtc = config->crtc[c];
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+ xf86OutputPtr output = NULL;
+ int o;
+
+ /* Skip disabled CRTCs */
+ if (!crtc->enabled) {
+ drmModeSetCrtc(drmmode->fd,
+ drmmode_crtc->mode_crtc->crtc_id, 0, 0,
+ 0, NULL, 0, NULL);
+ continue;
+ }
+
+ if (config->output[config->compat_output]->crtc == crtc)
+ output = config->output[config->compat_output];
+ else {
+ for (o = 0; o < config->num_output; o++)
+ if (config->output[o]->crtc == crtc) {
+ output = config->output[o];
+ break;
+ }
+ }
+ /* paranoia */
+ if (!output)
+ continue;
+
+ /* Mark that we'll need to re-set the mode for sure */
+ memset(&crtc->mode, 0, sizeof(crtc->mode));
+ if (!crtc->desiredMode.CrtcHDisplay) {
+ DisplayModePtr mode = xf86OutputFindClosestMode(output,
+ pScrn->
+ currentMode);
+
+ if (!mode)
+ return FALSE;
+ crtc->desiredMode = *mode;
+ crtc->desiredRotation = RR_Rotate_0;
+ crtc->desiredX = 0;
+ crtc->desiredY = 0;
+ }
+
+ if (!crtc->funcs->set_mode_major(crtc, &crtc->desiredMode,
+ crtc->desiredRotation,
+ crtc->desiredX,
+ crtc->desiredY))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void drmmode_load_palette(ScrnInfoPtr pScrn, int numColors,
+ int *indices, LOCO * colors, VisualPtr pVisual)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ uint16_t lut_r[256], lut_g[256], lut_b[256];
+ int index, j, i;
+ int c;
+
+ for (c = 0; c < xf86_config->num_crtc; c++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[c];
+ drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+ for (i = 0; i < 256; i++) {
+ lut_r[i] = drmmode_crtc->lut_r[i] << 6;
+ lut_g[i] = drmmode_crtc->lut_g[i] << 6;
+ lut_b[i] = drmmode_crtc->lut_b[i] << 6;
+ }
+
+ switch (pScrn->depth) {
+ case 15:
+ for (i = 0; i < numColors; i++) {
+ index = indices[i];
+ for (j = 0; j < 8; j++) {
+ lut_r[index * 8 + j] =
+ colors[index].red << 6;
+ lut_g[index * 8 + j] =
+ colors[index].green << 6;
+ lut_b[index * 8 + j] =
+ colors[index].blue << 6;
+ }
+ }
+ break;
+ case 16:
+ for (i = 0; i < numColors; i++) {
+ index = indices[i];
+
+ if (i <= 31) {
+ for (j = 0; j < 8; j++) {
+ lut_r[index * 8 + j] =
+ colors[index].red << 6;
+ lut_b[index * 8 + j] =
+ colors[index].blue << 6;
+ }
+ }
+
+ for (j = 0; j < 4; j++) {
+ lut_g[index * 4 + j] =
+ colors[index].green << 6;
+ }
+ }
+ break;
+ default:
+ for (i = 0; i < numColors; i++) {
+ index = indices[i];
+ lut_r[index] = colors[index].red << 6;
+ lut_g[index] = colors[index].green << 6;
+ lut_b[index] = colors[index].blue << 6;
+ }
+ break;
+ }
+
+ /* Make the change through RandR */
+ if (crtc->randr_crtc)
+ RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
+ else
+ crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256);
+ }
+}
+
+Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+
+ if (xf86_config->num_crtc) {
+ xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
+ "Initializing kms color map\n");
+ if (!miCreateDefColormap(pScreen))
+ return FALSE;
+ /* all amdgpus support 10 bit CLUTs */
+ if (!xf86HandleColormaps(pScreen, 256, 10,
+ drmmode_load_palette, NULL,
+ CMAP_PALETTED_TRUECOLOR
+#if 0 /* This option messes up text mode! (eich@suse.de) */
+ | CMAP_LOAD_EVEN_IF_OFFSCREEN
+#endif
+ | CMAP_RELOAD_ON_MODE_SWITCH))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#ifdef HAVE_LIBUDEV
+static void drmmode_handle_uevents(int fd, void *closure)
+{
+ drmmode_ptr drmmode = closure;
+ ScrnInfoPtr scrn = drmmode->scrn;
+ struct udev_device *dev;
+ dev = udev_monitor_receive_device(drmmode->uevent_monitor);
+ if (!dev)
+ return;
+
+ RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
+ udev_device_unref(dev);
+}
+#endif
+
+void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
+{
+#ifdef HAVE_LIBUDEV
+ struct udev *u;
+ struct udev_monitor *mon;
+
+ u = udev_new();
+ if (!u)
+ return;
+ mon = udev_monitor_new_from_netlink(u, "udev");
+ if (!mon) {
+ udev_unref(u);
+ return;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype(mon,
+ "drm",
+ "drm_minor") < 0 ||
+ udev_monitor_enable_receiving(mon) < 0) {
+ udev_monitor_unref(mon);
+ udev_unref(u);
+ return;
+ }
+
+ drmmode->uevent_handler =
+ xf86AddGeneralHandler(udev_monitor_get_fd(mon),
+ drmmode_handle_uevents, drmmode);
+
+ drmmode->uevent_monitor = mon;
+#endif
+}
+
+void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
+{
+#ifdef HAVE_LIBUDEV
+ if (drmmode->uevent_handler) {
+ struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
+ xf86RemoveGeneralHandler(drmmode->uevent_handler);
+
+ udev_monitor_unref(drmmode->uevent_monitor);
+ udev_unref(u);
+ }
+#endif
+}
+
+Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, struct amdgpu_buffer *new_front,
+ void *data, int ref_crtc_hw_id)
+{
+ AMDGPUInfoPtr info = AMDGPUPTR(scrn);
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+ drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ unsigned int pitch;
+ int i, old_fb_id;
+ int height, emitted = 0;
+ drmmode_flipdata_ptr flipdata;
+ drmmode_flipevtcarrier_ptr flipcarrier;
+ union gbm_bo_handle bo_handle;
+ uint32_t handle;
+
+ if (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM) {
+ pitch = gbm_bo_get_stride(info->front_buffer->bo.gbm);
+ height = gbm_bo_get_height(info->front_buffer->bo.gbm);
+ bo_handle = gbm_bo_get_handle(new_front->bo.gbm);
+ handle = bo_handle.u32;
+ } else {
+ pitch = scrn->displayWidth;
+ height = scrn->virtualY;
+ if (amdgpu_bo_export(new_front->bo.amdgpu,
+ amdgpu_bo_handle_type_kms,
+ &handle))
+ goto error_out;
+ }
+
+ /*
+ * Create a new handle for the back buffer
+ */
+ old_fb_id = drmmode->fb_id;
+
+ if (drmModeAddFB(drmmode->fd, scrn->virtualX, height,
+ scrn->depth, scrn->bitsPerPixel, pitch,
+ handle, &drmmode->fb_id)) {
+ goto error_out;
+ }
+ flipdata = calloc(1, sizeof(drmmode_flipdata_rec));
+ if (!flipdata) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "flip queue: data alloc failed.\n");
+ goto error_undo;
+ }
+ /*
+ * Queue flips on all enabled CRTCs
+ * Note that if/when we get per-CRTC buffers, we'll have to update this.
+ * Right now it assumes a single shared fb across all CRTCs, with the
+ * kernel fixing up the offset of each CRTC as necessary.
+ *
+ * Also, flips queued on disabled or incorrectly configured displays
+ * may never complete; this is a configuration error.
+ */
+
+ flipdata->event_data = data;
+ flipdata->drmmode = drmmode;
+
+ for (i = 0; i < config->num_crtc; i++) {
+ if (!config->crtc[i]->enabled)
+ continue;
+
+ flipdata->flip_count++;
+ drmmode_crtc = config->crtc[i]->driver_private;
+
+ flipcarrier = calloc(1, sizeof(drmmode_flipevtcarrier_rec));
+ if (!flipcarrier) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "flip queue: carrier alloc failed.\n");
+ if (emitted == 0)
+ free(flipdata);
+ goto error_undo;
+ }
+
+ /* Only the reference crtc will finally deliver its page flip
+ * completion event. All other crtc's events will be discarded.
+ */
+ flipcarrier->dispatch_me =
+ (drmmode_crtc->hw_id == ref_crtc_hw_id);
+ flipcarrier->flipdata = flipdata;
+
+ if (drmModePageFlip
+ (drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
+ drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, flipcarrier)) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "flip queue failed: %s\n", strerror(errno));
+ free(flipcarrier);
+ if (emitted == 0)
+ free(flipdata);
+ goto error_undo;
+ }
+ emitted++;
+ }
+
+ flipdata->old_fb_id = old_fb_id;
+ return TRUE;
+
+error_undo:
+ drmModeRmFB(drmmode->fd, drmmode->fb_id);
+ drmmode->fb_id = old_fb_id;
+
+error_out:
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
+ strerror(errno));
+ return FALSE;
+}
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
new file mode 100644
index 0000000..c2ec683
--- /dev/null
+++ b/src/drmmode_display.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2007 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 (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ *
+ */
+#ifndef DRMMODE_DISPLAY_H
+#define DRMMODE_DISPLAY_H
+
+#include "xf86drmMode.h"
+#ifdef HAVE_LIBUDEV
+#include "libudev.h"
+#endif
+
+#include "amdgpu_probe.h"
+#include "amdgpu.h"
+
+#ifndef DRM_CAP_TIMESTAMP_MONOTONIC
+#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
+#endif
+
+typedef struct {
+ int fd;
+ unsigned fb_id;
+ drmModeResPtr mode_res;
+ drmModeFBPtr mode_fb;
+ int cpp;
+ ScrnInfoPtr scrn;
+#ifdef HAVE_LIBUDEV
+ struct udev_monitor *uevent_monitor;
+ InputHandlerProc uevent_handler;
+#endif
+ drmEventContext event_context;
+} drmmode_rec, *drmmode_ptr;
+
+typedef struct {
+ drmmode_ptr drmmode;
+ unsigned old_fb_id;
+ int flip_count;
+ void *event_data;
+ unsigned int fe_frame;
+ unsigned int fe_tv_sec;
+ unsigned int fe_tv_usec;
+} drmmode_flipdata_rec, *drmmode_flipdata_ptr;
+
+typedef struct {
+ drmmode_flipdata_ptr flipdata;
+ Bool dispatch_me;
+} drmmode_flipevtcarrier_rec, *drmmode_flipevtcarrier_ptr;
+
+typedef struct {
+ drmmode_ptr drmmode;
+ drmModeCrtcPtr mode_crtc;
+ int hw_id;
+ struct amdgpu_buffer *cursor_buffer;
+ struct amdgpu_buffer *rotate_buffer;
+ unsigned rotate_fb_id;
+ int dpms_mode;
+ CARD64 dpms_last_ust;
+ uint32_t dpms_last_seq;
+ int dpms_last_fps;
+ uint32_t interpolated_vblanks;
+ uint16_t lut_r[256], lut_g[256], lut_b[256];
+ int scanout_pixmap_x;
+} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
+
+typedef struct {
+ drmModePropertyPtr mode_prop;
+ uint64_t value;
+ int num_atoms; /* if range prop, num_atoms == 1; if enum prop, num_atoms == num_enums + 1 */
+ Atom *atoms;
+} drmmode_prop_rec, *drmmode_prop_ptr;
+
+typedef struct {
+ drmmode_ptr drmmode;
+ int output_id;
+ drmModeConnectorPtr mode_output;
+ drmModeEncoderPtr *mode_encoders;
+ drmModePropertyBlobPtr edid_blob;
+ int dpms_enum_id;
+ int num_props;
+ drmmode_prop_ptr props;
+ int enc_mask;
+ int enc_clone_mask;
+} drmmode_output_private_rec, *drmmode_output_private_ptr;
+
+extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp);
+extern void drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
+extern void drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
+extern void drmmode_set_cursor(ScrnInfoPtr scrn, drmmode_ptr drmmode, int id,
+ struct amdgpu_buffer *bo);
+void drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y);
+extern Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
+extern void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
+extern Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn);
+
+extern void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode);
+extern void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode);
+
+extern int drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe);
+Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, struct amdgpu_buffer *new_front,
+ void *data, int ref_crtc_hw_id);
+int drmmode_get_current_ust(int drm_fd, CARD64 * ust);
+
+#endif
diff --git a/src/pcidb/ati_pciids.csv b/src/pcidb/ati_pciids.csv
new file mode 100644
index 0000000..bac9b8f
--- /dev/null
+++ b/src/pcidb/ati_pciids.csv
@@ -0,0 +1,74 @@
+"#pciid","define","family","name"
+"0x6640","BONAIRE_6640","BONAIRE","BONAIRE"
+"0x6641","BONAIRE_6641","BONAIRE","BONAIRE"
+"0x6649","BONAIRE_6649","BONAIRE","BONAIRE"
+"0x6650","BONAIRE_6650","BONAIRE","BONAIRE"
+"0x6651","BONAIRE_6651","BONAIRE","BONAIRE"
+"0x6658","BONAIRE_6658","BONAIRE","BONAIRE"
+"0x665C","BONAIRE_665C","BONAIRE","BONAIRE"
+"0x665D","BONAIRE_665D","BONAIRE","BONAIRE"
+"0x9830","KABINI_9830","KABINI","KABINI"
+"0x9831","KABINI_9831","KABINI","KABINI"
+"0x9832","KABINI_9832","KABINI","KABINI"
+"0x9833","KABINI_9833","KABINI","KABINI"
+"0x9834","KABINI_9834","KABINI","KABINI"
+"0x9835","KABINI_9835","KABINI","KABINI"
+"0x9836","KABINI_9836","KABINI","KABINI"
+"0x9837","KABINI_9837","KABINI","KABINI"
+"0x9838","KABINI_9838","KABINI","KABINI"
+"0x9839","KABINI_9839","KABINI","KABINI"
+"0x983A","KABINI_983A","KABINI","KABINI"
+"0x983B","KABINI_983B","KABINI","KABINI"
+"0x983C","KABINI_983C","KABINI","KABINI"
+"0x983D","KABINI_983D","KABINI","KABINI"
+"0x983E","KABINI_983E","KABINI","KABINI"
+"0x983F","KABINI_983F","KABINI","KABINI"
+"0x1304","KAVERI_1304","KAVERI","KAVERI"
+"0x1305","KAVERI_1305","KAVERI","KAVERI"
+"0x1306","KAVERI_1306","KAVERI","KAVERI"
+"0x1307","KAVERI_1307","KAVERI","KAVERI"
+"0x1309","KAVERI_1309","KAVERI","KAVERI"
+"0x130A","KAVERI_130A","KAVERI","KAVERI"
+"0x130B","KAVERI_130B","KAVERI","KAVERI"
+"0x130C","KAVERI_130C","KAVERI","KAVERI"
+"0x130D","KAVERI_130D","KAVERI","KAVERI"
+"0x130E","KAVERI_130E","KAVERI","KAVERI"
+"0x130F","KAVERI_130F","KAVERI","KAVERI"
+"0x1310","KAVERI_1310","KAVERI","KAVERI"
+"0x1311","KAVERI_1311","KAVERI","KAVERI"
+"0x1312","KAVERI_1312","KAVERI","KAVERI"
+"0x1313","KAVERI_1313","KAVERI","KAVERI"
+"0x1315","KAVERI_1315","KAVERI","KAVERI"
+"0x1316","KAVERI_1316","KAVERI","KAVERI"
+"0x1317","KAVERI_1317","KAVERI","KAVERI"
+"0x131B","KAVERI_131B","KAVERI","KAVERI"
+"0x131C","KAVERI_131C","KAVERI","KAVERI"
+"0x131D","KAVERI_131D","KAVERI","KAVERI"
+"0x67A0","HAWAII_67A0","HAWAII","HAWAII"
+"0x67A1","HAWAII_67A1","HAWAII","HAWAII"
+"0x67A2","HAWAII_67A2","HAWAII","HAWAII"
+"0x67A8","HAWAII_67A8","HAWAII","HAWAII"
+"0x67A9","HAWAII_67A9","HAWAII","HAWAII"
+"0x67AA","HAWAII_67AA","HAWAII","HAWAII"
+"0x67B0","HAWAII_67B0","HAWAII","HAWAII"
+"0x67B1","HAWAII_67B1","HAWAII","HAWAII"
+"0x67B8","HAWAII_67B8","HAWAII","HAWAII"
+"0x67B9","HAWAII_67B9","HAWAII","HAWAII"
+"0x67BA","HAWAII_67BA","HAWAII","HAWAII"
+"0x67BE","HAWAII_67BE","HAWAII","HAWAII"
+"0x6900","TOPAZ_6900","TOPAZ","TOPAZ"
+"0x6901","TOPAZ_6901","TOPAZ","TOPAZ"
+"0x6902","TOPAZ_6902","TOPAZ","TOPAZ"
+"0x6903","TOPAZ_6903","TOPAZ","TOPAZ"
+"0x6907","TOPAZ_6907","TOPAZ","TOPAZ"
+"0x6920","TONGA_6920","TONGA","TONGA"
+"0x6921","TONGA_6921","TONGA","TONGA"
+"0x6938","TONGA_6938","TONGA","TONGA"
+"0x6939","TONGA_6939","TONGA","TONGA"
+"0x692B","TONGA_692B","TONGA","TONGA"
+"0x692F","TONGA_692F","TONGA","TONGA"
+"0x9870","CARRIZO_9870","CARRIZO","CARRIZO"
+"0x9874","CARRIZO_9874","CARRIZO","CARRIZO"
+"0x9875","CARRIZO_9875","CARRIZO","CARRIZO"
+"0x9876","CARRIZO_9876","CARRIZO","CARRIZO"
+"0x9877","CARRIZO_9877","CARRIZO","CARRIZO"
diff --git a/src/pcidb/parse_pci_ids.pl b/src/pcidb/parse_pci_ids.pl
new file mode 100755
index 0000000..06a9e1e
--- /dev/null
+++ b/src/pcidb/parse_pci_ids.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+#
+# Copyright 2007 Red Hat Inc.
+# This crappy script written by Dave Airlie to avoid hassle of adding
+# ids in every place.
+#
+use strict;
+use warnings;
+use Text::CSV_XS;
+
+my $file = $ARGV[0];
+
+my $atioutfile = 'ati_pciids_gen.h';
+my $amdgpupcichipsetfile = 'amdgpu_pci_chipset_gen.h';
+my $amdgpupcidevicematchfile = 'amdgpu_pci_device_match_gen.h';
+my $amdgpuchipsetfile = 'amdgpu_chipset_gen.h';
+my $amdgpuchipinfofile = 'amdgpu_chipinfo_gen.h';
+
+my $csv = Text::CSV_XS->new();
+
+open (CSV, "<", $file) or die $!;
+
+open (ATIOUT, ">", $atioutfile) or die;
+open (PCICHIPSET, ">", $amdgpupcichipsetfile) or die;
+open (PCIDEVICEMATCH, ">", $amdgpupcidevicematchfile) or die;
+open (AMDGPUCHIPSET, ">", $amdgpuchipsetfile) or die;
+open (AMDGPUCHIPINFO, ">", $amdgpuchipinfofile) or die;
+
+print AMDGPUCHIPSET "/* This file is autogenerated please do not edit */\n";
+print AMDGPUCHIPSET "SymTabRec AMDGPUChipsets[] = {\n";
+print PCICHIPSET "/* This file is autogenerated please do not edit */\n";
+print PCICHIPSET "static PciChipsets AMDGPUPciChipsets[] = {\n";
+print PCIDEVICEMATCH "/* This file is autogenerated please do not edit */\n";
+print PCIDEVICEMATCH "static const struct pci_id_match amdgpu_device_match[] = {\n";
+print AMDGPUCHIPINFO "/* This file is autogenerated please do not edit */\n";
+print AMDGPUCHIPINFO "static AMDGPUCardInfo AMDGPUCards[] = {\n";
+while (<CSV>) {
+ if ($csv->parse($_)) {
+ my @columns = $csv->fields();
+
+ if ((substr($columns[0], 0, 1) ne "#")) {
+
+ print ATIOUT "#define PCI_CHIP_$columns[1] $columns[0]\n";
+
+ if (($columns[2] ne "R128") && ($columns[2] ne "MACH64") && ($columns[2] ne "MACH32")) {
+ print PCICHIPSET " { PCI_CHIP_$columns[1], PCI_CHIP_$columns[1], RES_SHARED_VGA },\n";
+
+ print PCIDEVICEMATCH " ATI_DEVICE_MATCH( PCI_CHIP_$columns[1], 0 ),\n";
+
+ print AMDGPUCHIPSET " { PCI_CHIP_$columns[1], \"$columns[3]\" },\n";
+
+ print AMDGPUCHIPINFO " { $columns[0], CHIP_FAMILY_$columns[2] },\n";
+ }
+ }
+ } else {
+ my $err = $csv->error_input;
+ print "Failed to parse line: $err";
+ }
+}
+
+print AMDGPUCHIPINFO "};\n";
+print AMDGPUCHIPSET " { -1, NULL }\n};\n";
+print PCICHIPSET " { -1, -1, RES_UNDEFINED }\n};\n";
+print PCIDEVICEMATCH " { 0, 0, 0 }\n};\n";
+close CSV;
+close ATIOUT;
+close PCICHIPSET;
+close PCIDEVICEMATCH;
+close AMDGPUCHIPSET;
+close AMDGPUCHIPINFO;
diff --git a/src/simple_list.h b/src/simple_list.h
new file mode 100644
index 0000000..94a8af4
--- /dev/null
+++ b/src/simple_list.h
@@ -0,0 +1,201 @@
+/**
+ * \file simple_list.h
+ * Simple macros for type-safe, intrusive lists.
+ *
+ * Intended to work with a list sentinal which is created as an empty
+ * list. Insert & delete are O(1).
+ *
+ * \author
+ * (C) 1997, Keith Whitwell
+ */
+
+/*
+ * Mesa 3-D graphics library
+ * Version: 3.5
+ *
+ * Copyright (C) 1999-2001 Brian Paul 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
+ * 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
+ * BRIAN PAUL 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 _SIMPLE_LIST_H
+#define _SIMPLE_LIST_H
+
+struct simple_node {
+ struct simple_node *next;
+ struct simple_node *prev;
+};
+
+/**
+ * Remove an element from list.
+ *
+ * \param elem element to remove.
+ */
+#define remove_from_list(elem) \
+do { \
+ (elem)->next->prev = (elem)->prev; \
+ (elem)->prev->next = (elem)->next; \
+} while (0)
+
+/**
+ * Insert an element to the list head.
+ *
+ * \param list list.
+ * \param elem element to insert.
+ */
+#define insert_at_head(list, elem) \
+do { \
+ (elem)->prev = list; \
+ (elem)->next = (list)->next; \
+ (list)->next->prev = elem; \
+ (list)->next = elem; \
+} while(0)
+
+/**
+ * Insert an element to the list tail.
+ *
+ * \param list list.
+ * \param elem element to insert.
+ */
+#define insert_at_tail(list, elem) \
+do { \
+ (elem)->next = list; \
+ (elem)->prev = (list)->prev; \
+ (list)->prev->next = elem; \
+ (list)->prev = elem; \
+} while(0)
+
+/**
+ * Move an element to the list head.
+ *
+ * \param list list.
+ * \param elem element to move.
+ */
+#define move_to_head(list, elem) \
+do { \
+ remove_from_list(elem); \
+ insert_at_head(list, elem); \
+} while (0)
+
+/**
+ * Move an element to the list tail.
+ *
+ * \param list list.
+ * \param elem element to move.
+ */
+#define move_to_tail(list, elem) \
+do { \
+ remove_from_list(elem); \
+ insert_at_tail(list, elem); \
+} while (0)
+
+/**
+ * Make a empty list empty.
+ *
+ * \param sentinal list (sentinal element).
+ */
+#define make_empty_list(sentinal) \
+do { \
+ (sentinal)->next = sentinal; \
+ (sentinal)->prev = sentinal; \
+} while (0)
+
+/**
+ * Get list first element.
+ *
+ * \param list list.
+ *
+ * \return pointer to first element.
+ */
+#define first_elem(list) ((list)->next)
+
+/**
+ * Get list last element.
+ *
+ * \param list list.
+ *
+ * \return pointer to last element.
+ */
+#define last_elem(list) ((list)->prev)
+
+/**
+ * Get next element.
+ *
+ * \param elem element.
+ *
+ * \return pointer to next element.
+ */
+#define next_elem(elem) ((elem)->next)
+
+/**
+ * Get previous element.
+ *
+ * \param elem element.
+ *
+ * \return pointer to previous element.
+ */
+#define prev_elem(elem) ((elem)->prev)
+
+/**
+ * Test whether element is at end of the list.
+ *
+ * \param list list.
+ * \param elem element.
+ *
+ * \return non-zero if element is at end of list, or zero otherwise.
+ */
+#define at_end(list, elem) ((elem) == (list))
+
+/**
+ * Test if a list is empty.
+ *
+ * \param list list.
+ *
+ * \return non-zero if list empty, or zero otherwise.
+ */
+#define is_empty_list(list) ((list)->next == (list))
+
+/**
+ * Walk through the elements of a list.
+ *
+ * \param ptr pointer to the current element.
+ * \param list list.
+ *
+ * \note It should be followed by a { } block or a single statement, as in a \c
+ * for loop.
+ */
+#define foreach(ptr, list) \
+ for( ptr=(list)->next ; ptr!=list ; ptr=(ptr)->next )
+
+/**
+ * Walk through the elements of a list.
+ *
+ * Same as #foreach but lets you unlink the current value during a list
+ * traversal. Useful for freeing a list, element by element.
+ *
+ * \param ptr pointer to the current element.
+ * \param t temporary pointer.
+ * \param list list.
+ *
+ * \note It should be followed by a { } block or a single statement, as in a \c
+ * for loop.
+ */
+#define foreach_s(ptr, t, list) \
+ for(ptr=(list)->next,t=(ptr)->next; list != ptr; ptr=t, t=(t)->next)
+
+#endif