summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2020-09-01 18:19:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2020-09-01 18:19:47 +0000
commitab9b5f058286a5485150343219d243f270ecb27d (patch)
treecac29d03d9750d2259c19c0d8c623cd2e7d71b23
parentInitial commit. (diff)
downloadlibdrm-ab9b5f058286a5485150343219d243f270ecb27d.tar.xz
libdrm-ab9b5f058286a5485150343219d243f270ecb27d.zip
Adding upstream version 2.4.102.upstream/2.4.102upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.editorconfig23
-rw-r--r--Android.common.mk22
-rw-r--r--Android.mk74
-rw-r--r--CONTRIBUTING.rst105
-rw-r--r--CleanSpec.mk4
-rw-r--r--Makefile.sources45
-rw-r--r--README.rst33
-rw-r--r--RELEASING40
-rw-r--r--amdgpu/.editorconfig13
-rw-r--r--amdgpu/Android.mk19
-rw-r--r--amdgpu/Makefile.sources14
-rw-r--r--amdgpu/amdgpu-symbols.txt74
-rw-r--r--amdgpu/amdgpu.h1830
-rw-r--r--amdgpu/amdgpu_asic_id.c161
-rw-r--r--amdgpu/amdgpu_bo.c799
-rw-r--r--amdgpu/amdgpu_cs.c950
-rw-r--r--amdgpu/amdgpu_device.c308
-rw-r--r--amdgpu/amdgpu_gpu_info.c333
-rw-r--r--amdgpu/amdgpu_internal.h176
-rw-r--r--amdgpu/amdgpu_vamgr.c263
-rw-r--r--amdgpu/amdgpu_vm.c50
-rw-r--r--amdgpu/handle_table.c72
-rw-r--r--amdgpu/handle_table.h41
-rw-r--r--amdgpu/libdrm_amdgpu.pc.in11
-rw-r--r--amdgpu/meson.build68
-rw-r--r--android/gralloc_handle.h111
-rw-r--r--core-symbols.txt197
-rw-r--r--data/Android.mk10
-rw-r--r--data/amdgpu.ids220
-rw-r--r--data/meson.build27
-rw-r--r--etnaviv/Android.mk14
-rw-r--r--etnaviv/Makefile.sources13
-rw-r--r--etnaviv/etnaviv-symbols.txt36
-rw-r--r--etnaviv/etnaviv_bo.c349
-rw-r--r--etnaviv/etnaviv_bo_cache.c204
-rw-r--r--etnaviv/etnaviv_cmd_stream.c279
-rw-r--r--etnaviv/etnaviv_device.c115
-rw-r--r--etnaviv/etnaviv_drm.h289
-rw-r--r--etnaviv/etnaviv_drmif.h214
-rw-r--r--etnaviv/etnaviv_gpu.c155
-rw-r--r--etnaviv/etnaviv_perfmon.c185
-rw-r--r--etnaviv/etnaviv_pipe.c79
-rw-r--r--etnaviv/etnaviv_priv.h208
-rw-r--r--etnaviv/libdrm_etnaviv.pc.in11
-rw-r--r--etnaviv/meson.build63
-rw-r--r--exynos/exynos-symbols.txt23
-rw-r--r--exynos/exynos_drm.c448
-rw-r--r--exynos/exynos_drm.h172
-rw-r--r--exynos/exynos_drmif.h120
-rw-r--r--exynos/exynos_fimg2d.c1010
-rw-r--r--exynos/exynos_fimg2d.h333
-rw-r--r--exynos/fimg2d_reg.h127
-rw-r--r--exynos/libdrm_exynos.pc.in11
-rw-r--r--exynos/meson.build57
-rw-r--r--freedreno/Android.mk14
-rw-r--r--freedreno/Makefile.sources25
-rw-r--r--freedreno/freedreno-symbols.txt45
-rw-r--r--freedreno/freedreno_bo.c365
-rw-r--r--freedreno/freedreno_bo_cache.c220
-rw-r--r--freedreno/freedreno_device.c145
-rw-r--r--freedreno/freedreno_drmif.h140
-rw-r--r--freedreno/freedreno_pipe.c102
-rw-r--r--freedreno/freedreno_priv.h258
-rw-r--r--freedreno/freedreno_ringbuffer.c182
-rw-r--r--freedreno/freedreno_ringbuffer.h142
-rw-r--r--freedreno/kgsl/README26
-rw-r--r--freedreno/kgsl/kgsl_bo.c311
-rw-r--r--freedreno/kgsl/kgsl_device.c63
-rw-r--r--freedreno/kgsl/kgsl_drm.h192
-rw-r--r--freedreno/kgsl/kgsl_pipe.c282
-rw-r--r--freedreno/kgsl/kgsl_priv.h120
-rw-r--r--freedreno/kgsl/kgsl_ringbuffer.c235
-rw-r--r--freedreno/kgsl/msm_kgsl.h519
-rw-r--r--freedreno/libdrm_freedreno.pc.in11
-rw-r--r--freedreno/meson.build80
-rw-r--r--freedreno/msm/msm_bo.c170
-rw-r--r--freedreno/msm/msm_device.c63
-rw-r--r--freedreno/msm/msm_pipe.c212
-rw-r--r--freedreno/msm/msm_priv.h141
-rw-r--r--freedreno/msm/msm_ringbuffer.c723
-rw-r--r--include/drm/README128
-rw-r--r--include/drm/amdgpu_drm.h1067
-rw-r--r--include/drm/drm.h1045
-rw-r--r--include/drm/drm_fourcc.h763
-rw-r--r--include/drm/drm_mode.h914
-rw-r--r--include/drm/drm_sarea.h92
-rw-r--r--include/drm/i915_drm.h1915
-rw-r--r--include/drm/mach64_drm.h256
-rw-r--r--include/drm/mga_drm.h427
-rw-r--r--include/drm/msm_drm.h308
-rw-r--r--include/drm/nouveau_drm.h256
-rw-r--r--include/drm/qxl_drm.h158
-rw-r--r--include/drm/r128_drm.h336
-rw-r--r--include/drm/radeon_drm.h1079
-rw-r--r--include/drm/savage_drm.h220
-rw-r--r--include/drm/sis_drm.h77
-rw-r--r--include/drm/tegra_drm.h681
-rw-r--r--include/drm/vc4_drm.h442
-rw-r--r--include/drm/via_drm.h283
-rw-r--r--include/drm/virtgpu_drm.h182
-rw-r--r--include/drm/vmwgfx_drm.h1128
-rw-r--r--intel/.gitignore1
-rw-r--r--intel/Android.mk38
-rw-r--r--intel/Makefile.sources17
-rw-r--r--intel/i915_pciids.h608
-rw-r--r--intel/intel-symbols.txt83
-rw-r--r--intel/intel_aub.h153
-rw-r--r--intel/intel_bufmgr.c382
-rw-r--r--intel/intel_bufmgr.h341
-rw-r--r--intel/intel_bufmgr_fake.c1626
-rw-r--r--intel/intel_bufmgr_gem.c3822
-rw-r--r--intel/intel_bufmgr_priv.h325
-rw-r--r--intel/intel_chipset.c87
-rw-r--r--intel/intel_chipset.h350
-rw-r--r--intel/intel_debug.h44
-rw-r--r--intel/intel_decode.c3980
-rw-r--r--intel/libdrm_intel.pc.in11
-rw-r--r--intel/meson.build104
-rw-r--r--intel/mm.c260
-rw-r--r--intel/mm.h80
-rw-r--r--intel/test_decode.c194
-rw-r--r--intel/tests/.gitignore1
-rw-r--r--intel/tests/gen4-3d.batchbin0 -> 1952 bytes
-rw-r--r--intel/tests/gen4-3d.batch-ref.txt488
l---------intel/tests/gen4-3d.batch.sh1
-rw-r--r--intel/tests/gen5-3d.batchbin0 -> 2048 bytes
-rw-r--r--intel/tests/gen5-3d.batch-ref.txt512
l---------intel/tests/gen5-3d.batch.sh1
-rw-r--r--intel/tests/gen6-3d.batchbin0 -> 3960 bytes
-rw-r--r--intel/tests/gen6-3d.batch-ref.txt990
l---------intel/tests/gen6-3d.batch.sh1
-rw-r--r--intel/tests/gen7-2d-copy.batchbin0 -> 56 bytes
-rw-r--r--intel/tests/gen7-2d-copy.batch-ref.txt14
l---------intel/tests/gen7-2d-copy.batch.sh1
-rw-r--r--intel/tests/gen7-3d.batchbin0 -> 848 bytes
-rw-r--r--intel/tests/gen7-3d.batch-ref.txt212
l---------intel/tests/gen7-3d.batch.sh1
-rw-r--r--intel/tests/gm45-3d.batchbin0 -> 1952 bytes
-rw-r--r--intel/tests/gm45-3d.batch-ref.txt488
l---------intel/tests/gm45-3d.batch.sh1
-rwxr-xr-xintel/tests/test-batch.sh20
-rw-r--r--intel/uthash.h1074
-rw-r--r--libdrm.pc.in10
-rw-r--r--libdrm_lists.h118
-rw-r--r--libdrm_macros.h87
-rw-r--r--libkms/Android.mk51
-rw-r--r--libkms/Makefile.sources23
-rw-r--r--libkms/api.c139
-rw-r--r--libkms/dumb.c216
-rw-r--r--libkms/exynos.c220
-rw-r--r--libkms/intel.c236
-rw-r--r--libkms/internal.h80
-rw-r--r--libkms/kms-symbols.txt8
-rw-r--r--libkms/libkms.h82
-rw-r--r--libkms/libkms.pc.in11
-rw-r--r--libkms/linux.c147
-rw-r--r--libkms/meson.build78
-rw-r--r--libkms/nouveau.c218
-rw-r--r--libkms/radeon.c239
-rw-r--r--libkms/vmwgfx.c204
-rw-r--r--libsync.h148
-rw-r--r--man/drm-kms.xml342
-rw-r--r--man/drm-memory.xml430
-rw-r--r--man/drm.xml137
-rw-r--r--man/drmAvailable.xml75
-rw-r--r--man/drmHandleEvent.xml102
-rw-r--r--man/drmModeGetResources.xml139
-rw-r--r--man/meson.build67
-rw-r--r--meson.build416
-rw-r--r--meson_options.txt143
-rw-r--r--nouveau/Android.mk14
-rw-r--r--nouveau/Makefile.sources9
-rw-r--r--nouveau/abi16.c359
-rw-r--r--nouveau/bufctx.c160
-rw-r--r--nouveau/libdrm_nouveau.pc.in11
-rw-r--r--nouveau/meson.build62
-rw-r--r--nouveau/nouveau-symbols.txt41
-rw-r--r--nouveau/nouveau.c874
-rw-r--r--nouveau/nouveau.h276
-rw-r--r--nouveau/nvif/cl0080.h45
-rw-r--r--nouveau/nvif/cl9097.h44
-rw-r--r--nouveau/nvif/class.h141
-rw-r--r--nouveau/nvif/if0002.h38
-rw-r--r--nouveau/nvif/if0003.h33
-rw-r--r--nouveau/nvif/ioctl.h132
-rw-r--r--nouveau/nvif/unpack.h28
-rw-r--r--nouveau/private.h124
-rw-r--r--nouveau/pushbuf.c777
-rw-r--r--omap/Android.mk13
-rw-r--r--omap/libdrm_omap.pc.in11
-rw-r--r--omap/meson.build57
-rw-r--r--omap/omap-symbols.txt18
-rw-r--r--omap/omap_drm.c474
-rw-r--r--omap/omap_drm.h135
-rw-r--r--omap/omap_drmif.h65
-rw-r--r--radeon/Android.mk14
-rw-r--r--radeon/Makefile.sources21
-rw-r--r--radeon/bof.c477
-rw-r--r--radeon/bof.h90
-rw-r--r--radeon/libdrm_radeon.pc.in11
-rw-r--r--radeon/meson.build67
-rw-r--r--radeon/r600_pci_ids.h487
-rw-r--r--radeon/radeon-symbols.txt44
-rw-r--r--radeon/radeon_bo.c142
-rw-r--r--radeon/radeon_bo.h74
-rw-r--r--radeon/radeon_bo_gem.c404
-rw-r--r--radeon/radeon_bo_gem.h48
-rw-r--r--radeon/radeon_bo_int.h45
-rw-r--r--radeon/radeon_cs.c95
-rw-r--r--radeon/radeon_cs.h141
-rw-r--r--radeon/radeon_cs_gem.c556
-rw-r--r--radeon/radeon_cs_gem.h41
-rw-r--r--radeon/radeon_cs_int.h67
-rw-r--r--radeon/radeon_cs_space.c248
-rw-r--r--radeon/radeon_surface.c2566
-rw-r--r--radeon/radeon_surface.h149
-rw-r--r--symbols-check.py130
-rw-r--r--tegra/.gitignore1
-rw-r--r--tegra/libdrm_tegra.pc.in11
-rw-r--r--tegra/meson.build56
-rw-r--r--tegra/private.h51
-rw-r--r--tegra/tegra-symbols.txt13
-rw-r--r--tegra/tegra.c328
-rw-r--r--tegra/tegra.h60
-rw-r--r--tests/Android.mk1
l---------tests/amdgpu/.editorconfig1
-rw-r--r--tests/amdgpu/amdgpu_test.c684
-rw-r--r--tests/amdgpu/amdgpu_test.h421
-rw-r--r--tests/amdgpu/basic_tests.c3685
-rw-r--r--tests/amdgpu/bo_tests.c317
-rw-r--r--tests/amdgpu/cs_tests.c468
-rw-r--r--tests/amdgpu/deadlock_tests.c546
-rw-r--r--tests/amdgpu/decode_messages.h854
-rw-r--r--tests/amdgpu/frame.h1949
-rw-r--r--tests/amdgpu/meson.build34
-rw-r--r--tests/amdgpu/ras_tests.c1003
-rw-r--r--tests/amdgpu/syncobj_tests.c298
-rw-r--r--tests/amdgpu/uvd_enc_tests.c491
-rw-r--r--tests/amdgpu/uve_ib.h527
-rw-r--r--tests/amdgpu/vce_ib.h335
-rw-r--r--tests/amdgpu/vce_tests.c763
-rw-r--r--tests/amdgpu/vcn_tests.c439
-rw-r--r--tests/amdgpu/vm_tests.c255
-rw-r--r--tests/drmdevice.c164
-rw-r--r--tests/drmsl.c184
-rw-r--r--tests/etnaviv/cmdstream.xml.h242
-rw-r--r--tests/etnaviv/etnaviv_2d_test.c236
-rw-r--r--tests/etnaviv/etnaviv_bo_cache_test.c117
-rw-r--r--tests/etnaviv/etnaviv_cmd_stream_test.c123
-rw-r--r--tests/etnaviv/meson.build45
-rw-r--r--tests/etnaviv/state.xml.h375
-rw-r--r--tests/etnaviv/state_2d.xml.h1497
-rw-r--r--tests/etnaviv/write_bmp.c149
-rw-r--r--tests/etnaviv/write_bmp.h34
-rw-r--r--tests/exynos/exynos_fimg2d_event.c333
-rw-r--r--tests/exynos/exynos_fimg2d_perf.c327
-rw-r--r--tests/exynos/exynos_fimg2d_test.c904
-rw-r--r--tests/exynos/meson.build54
-rw-r--r--tests/hash.c217
-rw-r--r--tests/kms/kms-steal-crtc.c161
-rw-r--r--tests/kms/kms-universal-planes.c357
-rw-r--r--tests/kms/libkms-test-crtc.c43
-rw-r--r--tests/kms/libkms-test-device.c217
-rw-r--r--tests/kms/libkms-test-framebuffer.c153
-rw-r--r--tests/kms/libkms-test-plane.c137
-rw-r--r--tests/kms/libkms-test-screen.c90
-rw-r--r--tests/kms/libkms-test.h120
-rw-r--r--tests/kms/meson.build49
-rw-r--r--tests/kmstest/main.c109
-rw-r--r--tests/kmstest/meson.build30
-rw-r--r--tests/meson.build78
-rw-r--r--tests/modeprint/meson.build29
-rw-r--r--tests/modeprint/modeprint.c418
-rw-r--r--tests/modetest/Android.mk14
-rw-r--r--tests/modetest/Makefile.sources6
-rw-r--r--tests/modetest/buffers.c355
-rw-r--r--tests/modetest/buffers.h40
-rw-r--r--tests/modetest/cursor.c206
-rw-r--r--tests/modetest/cursor.h33
-rw-r--r--tests/modetest/meson.build29
-rw-r--r--tests/modetest/modetest.c2300
-rw-r--r--tests/nouveau/.gitignore1
-rw-r--r--tests/nouveau/meson.build30
-rw-r--r--tests/nouveau/threaded.c156
-rw-r--r--tests/proptest/Android.mk14
-rw-r--r--tests/proptest/Makefile.sources2
-rw-r--r--tests/proptest/meson.build28
-rw-r--r--tests/proptest/proptest.c325
-rw-r--r--tests/radeon/meson.build27
-rw-r--r--tests/radeon/radeon_ttm.c77
-rw-r--r--tests/radeon/rbo.c171
-rw-r--r--tests/radeon/rbo.h50
-rw-r--r--tests/tegra/.gitignore1
-rw-r--r--tests/tegra/meson.build27
-rw-r--r--tests/tegra/openclose.c67
-rw-r--r--tests/ttmtest/AUTHORS1
-rw-r--r--tests/ttmtest/ChangeLog23
-rw-r--r--tests/ttmtest/Makefile.am1
-rw-r--r--tests/ttmtest/NEWS0
-rw-r--r--tests/ttmtest/README0
-rw-r--r--tests/ttmtest/configure.ac33
-rwxr-xr-xtests/ttmtest/reconf2
-rw-r--r--tests/ttmtest/src/Makefile.am8
-rw-r--r--tests/ttmtest/src/ttmtest.c430
-rw-r--r--tests/ttmtest/src/xf86dri.c603
-rw-r--r--tests/ttmtest/src/xf86dri.h116
-rw-r--r--tests/ttmtest/src/xf86dristr.h390
-rw-r--r--tests/util/Android.mk38
-rw-r--r--tests/util/Makefile.sources8
-rw-r--r--tests/util/common.h33
-rw-r--r--tests/util/format.c123
-rw-r--r--tests/util/format.h65
-rw-r--r--tests/util/kms.c185
-rw-r--r--tests/util/kms.h35
-rw-r--r--tests/util/meson.build28
-rw-r--r--tests/util/pattern.c1283
-rw-r--r--tests/util/pattern.h46
-rw-r--r--tests/vbltest/meson.build28
-rw-r--r--tests/vbltest/vbltest.c186
-rw-r--r--util_double_list.h143
-rw-r--r--util_math.h34
-rw-r--r--vc4/Makefile.sources3
-rw-r--r--vc4/libdrm_vc4.pc.in9
-rw-r--r--vc4/meson.build28
-rw-r--r--vc4/vc4_packet.h397
-rw-r--r--vc4/vc4_qpu_defines.h274
-rw-r--r--xf86atomic.h114
-rw-r--r--xf86drm.c4562
-rw-r--r--xf86drm.h933
-rw-r--r--xf86drmHash.c242
-rw-r--r--xf86drmHash.h47
-rw-r--r--xf86drmMode.c1624
-rw-r--r--xf86drmMode.h569
-rw-r--r--xf86drmRandom.c138
-rw-r--r--xf86drmRandom.h35
-rw-r--r--xf86drmSL.c319
336 files changed, 95164 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..29b4f39
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,23 @@
+# To use this config with your editor, follow the instructions at:
+# http://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+
+[*.{c,h}]
+indent_style = space
+indent_size = 4
+
+[{Makefile.*,*.mk}]
+indent_style = tab
+
+[*.m4]
+indent_style = space
+indent_size = 2
+
+[{meson.build,meson_options.txt}]
+indent_style = space
+indent_size = 2
diff --git a/Android.common.mk b/Android.common.mk
new file mode 100644
index 0000000..37c2b23
--- /dev/null
+++ b/Android.common.mk
@@ -0,0 +1,22 @@
+# XXX: Consider moving these to config.h analogous to autoconf.
+LOCAL_CFLAGS += \
+ -DMAJOR_IN_SYSMACROS=1 \
+ -DHAVE_ALLOCA_H=0 \
+ -DHAVE_SYS_SELECT_H=0 \
+ -DHAVE_SYS_SYSCTL_H=0 \
+ -DHAVE_VISIBILITY=1 \
+ -fvisibility=hidden \
+ -DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1
+
+LOCAL_CFLAGS += \
+ -Wno-error \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -Wno-pointer-arith \
+ -Wno-enum-conversion
+
+# Quiet down the build system and remove any .h files from the sources
+LOCAL_SRC_FILES := $(patsubst %.h, , $(LOCAL_SRC_FILES))
+LOCAL_EXPORT_C_INCLUDE_DIRS += $(LOCAL_PATH)
+
+LOCAL_PROPRIETARY_MODULE := true
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..0ab6f0f
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,74 @@
+#
+# Copyright © 2011-2012 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#
+
+LIBDRM_ANDROID_MAJOR_VERSION := $(word 1, $(subst ., , $(PLATFORM_VERSION)))
+ifneq ($(filter 2 4, $(LIBDRM_ANDROID_MAJOR_VERSION)),)
+$(error "Android 4.4 and earlier not supported")
+endif
+
+LIBDRM_COMMON_MK := $(call my-dir)/Android.common.mk
+
+LOCAL_PATH := $(call my-dir)
+LIBDRM_TOP := $(LOCAL_PATH)
+
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_{,H,INCLUDE_H,INCLUDE_ANDROID_H,INCLUDE_VMWGFX_H}_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+#static library for the device (recovery)
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_FILES)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/include/drm \
+ $(LOCAL_PATH)/android
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include/drm
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_STATIC_LIBRARY)
+
+# Shared library for the device
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_FILES)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/include/drm \
+ $(LOCAL_PATH)/android
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include/drm
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 0000000..96f1e4f
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,105 @@
+Contributing to libdrm
+======================
+
+Submitting Patches
+------------------
+
+Patches should be sent to dri-devel@lists.freedesktop.org, using git
+send-email. For patches only touching driver specific code one of the driver
+mailing lists (like amd-gfx@lists.freedesktop.org) is also appropriate. See git
+documentation for help:
+
+http://git-scm.com/documentation
+
+Since dri-devel is a very busy mailing list please use --subject-prefix="PATCH
+libdrm" to make it easier to find libdrm patches. This is best done by running
+
+ git config --local format.subjectprefix "PATCH libdrm"
+
+The first line of a commit message should contain a prefix indicating what part
+is affected by the patch followed by one sentence that describes the change. For
+examples:
+
+ amdgpu: Use uint32_t i in amdgpu_find_bo_by_cpu_mapping
+
+The body of the commit message should describe what the patch changes and why,
+and also note any particular side effects. For a recommended reading on
+writing commit messages, see:
+
+http://who-t.blogspot.de/2009/12/on-commit-messages.html
+
+Your patches should also include a Signed-off-by line with your name and email
+address. If you're not the patch's original author, you should also gather
+S-o-b's by them (and/or whomever gave the patch to you.) The significance of
+this is that it certifies that you created the patch, that it was created under
+an appropriate open source license, or provided to you under those terms. This
+lets us indicate a chain of responsibility for the copyright status of the code.
+For more details:
+
+https://developercertificate.org/
+
+We won't reject patches that lack S-o-b, but it is strongly recommended.
+
+Review and Merging
+------------------
+
+Patches should have at least one positive review (Reviewed-by: tag) or
+indication of approval (Acked-by: tag) before merging. For any code shared
+between drivers this is mandatory.
+
+Please note that kernel/userspace API header files have special rules, see
+include/drm/README.
+
+Coding style in the project loosely follows the CodingStyle of the linux kernel:
+
+https://www.kernel.org/doc/html/latest/process/coding-style.html?highlight=coding%20style
+
+Commit Rights
+-------------
+
+Commit rights will be granted to anyone who requests them and fulfills the
+below criteria:
+
+- Submitted a few (5-10 as a rule of thumb) non-trivial (not just simple
+ spelling fixes and whitespace adjustment) patches that have been merged
+ already. Since libdrm is just a glue library between the kernel and userspace
+ drivers, merged patches to those components also count towards the commit
+ criteria.
+
+- Are actively participating on discussions about their work (on the mailing
+ list or IRC). This should not be interpreted as a requirement to review other
+ peoples patches but just make sure that patch submission isn't one-way
+ communication. Cross-review is still highly encouraged.
+
+- Will be regularly contributing further patches. This includes regular
+ contributors to other parts of the open source graphics stack who only
+ do the oddball rare patch within libdrm itself.
+
+- Agrees to use their commit rights in accordance with the documented merge
+ criteria, tools, and processes.
+
+To apply for commit rights ("Developer" role in gitlab) send a mail to
+dri-devel@lists.freedesktop.org and please ping the maintainers if your request
+is stuck.
+
+Committers are encouraged to request their commit rights get removed when they
+no longer contribute to the project. Commit rights will be reinstated when they
+come back to the project.
+
+Maintainers and committers should encourage contributors to request commit
+rights, as especially junior contributors tend to underestimate their skills.
+
+Code of Conduct
+---------------
+
+Please be aware the fd.o Code of Conduct also applies to libdrm:
+
+https://www.freedesktop.org/wiki/CodeOfConduct/
+
+See the gitlab project owners for contact details of the libdrm maintainers.
+
+Abuse of commit rights, like engaging in commit fights or willfully pushing
+patches that violate the documented merge criteria, will also be handled through
+the Code of Conduct enforcement process.
+
+Happy hacking!
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..28a11db
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,4 @@
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/include/libdrm)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/include/freedreno)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdrm_*intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libdrm_*intermediates)
diff --git a/Makefile.sources b/Makefile.sources
new file mode 100644
index 0000000..55290fe
--- /dev/null
+++ b/Makefile.sources
@@ -0,0 +1,45 @@
+LIBDRM_FILES := \
+ xf86drm.c \
+ xf86drmHash.c \
+ xf86drmHash.h \
+ xf86drmRandom.c \
+ xf86drmRandom.h \
+ xf86drmSL.c \
+ xf86drmMode.c \
+ xf86atomic.h \
+ libdrm_macros.h \
+ libdrm_lists.h \
+ util_double_list.h \
+ util_math.h
+
+LIBDRM_H_FILES := \
+ libsync.h \
+ xf86drm.h \
+ xf86drmMode.h
+
+LIBDRM_INCLUDE_H_FILES := \
+ include/drm/drm.h \
+ include/drm/drm_fourcc.h \
+ include/drm/drm_mode.h \
+ include/drm/drm_sarea.h \
+ include/drm/i915_drm.h \
+ include/drm/mach64_drm.h \
+ include/drm/mga_drm.h \
+ include/drm/msm_drm.h \
+ include/drm/nouveau_drm.h \
+ include/drm/qxl_drm.h \
+ include/drm/r128_drm.h \
+ include/drm/radeon_drm.h \
+ include/drm/amdgpu_drm.h \
+ include/drm/savage_drm.h \
+ include/drm/sis_drm.h \
+ include/drm/tegra_drm.h \
+ include/drm/vc4_drm.h \
+ include/drm/via_drm.h \
+ include/drm/virtgpu_drm.h
+
+LIBDRM_INCLUDE_ANDROID_H_FILES := \
+ android/gralloc_handle.h
+
+LIBDRM_INCLUDE_VMWGFX_H_FILES := \
+ include/drm/vmwgfx_drm.h
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..da995d0
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,33 @@
+libdrm - userspace library for drm
+----------------------------------
+
+This is libdrm, a userspace library for accessing the DRM, direct rendering
+manager, on Linux, BSD and other operating systems that support the ioctl
+interface.
+The library provides wrapper functions for the ioctls to avoid exposing the
+kernel interface directly, and for chipsets with drm memory manager, support
+for tracking relocations and buffers.
+New functionality in the kernel DRM drivers typically requires a new libdrm,
+but a new libdrm will always work with an older kernel.
+
+libdrm is a low-level library, typically used by graphics drivers such as
+the Mesa drivers, the X drivers, libva and similar projects.
+
+
+Compiling
+---------
+
+To set up meson:
+
+ meson builddir/
+
+By default this will install into /usr/local, you can change your prefix
+with --prefix=/usr (or `meson configure builddir/ -Dprefix=/usr` after
+the initial meson setup).
+
+Then use ninja to build and install:
+
+ ninja -C builddir/ install
+
+If you are installing into a system location you will need to run install
+separately, and as root.
diff --git a/RELEASING b/RELEASING
new file mode 100644
index 0000000..903c176
--- /dev/null
+++ b/RELEASING
@@ -0,0 +1,40 @@
+The release criteria for libdrm is essentially "if you need a release,
+make one". There is no designated release engineer or maintainer.
+Anybody is free to make a release if there's a certain feature or bug
+fix they need in a released version of libdrm.
+
+When new ioctl definitions are merged into drm-next, we will add
+support to libdrm, at which point we typically create a new release.
+However, this is up to whoever is driving the feature in question.
+
+Follow these steps to release a new version of libdrm:
+
+ 1) Bump the version number in meson.build. We seem to have settled for
+ 2.4.x as the versioning scheme for libdrm, so just bump the micro
+ version.
+
+ 2) Run `ninja -C builddir/ dist` to generate the tarballs.
+ Make sure that the version number of the tarball name in
+ builddir/meson-dist/ matches the number you bumped to. Move that
+ tarball to the libdrm repo root for the release script to pick up.
+
+ 3) Push the updated master branch with the bumped version number:
+
+ git push origin master
+
+ assuming the remote for the upstream libdrm repo is called origin.
+
+ 4) Use the release.sh script from the xorg/util/modular repo to
+ upload the tarballs to the freedesktop.org download area and
+ create an announce email template. The script takes one argument:
+ the path to the libdrm checkout. So, if a checkout of modular is
+ at the same level than the libdrm repo:
+
+ ./modular/release.sh libdrm
+
+ This copies the two tarballs to freedesktop.org and creates
+ libdrm-2.4.16.announce which has a detailed summary of the
+ changes, links to the tarballs, MD5 and SHA1 sums and pre-filled
+ out email headers. Fill out the blank between the email headers
+ and the list of changes with a brief message of what changed or
+ what prompted this release. Send out the email and you're done!
diff --git a/amdgpu/.editorconfig b/amdgpu/.editorconfig
new file mode 100644
index 0000000..426273f
--- /dev/null
+++ b/amdgpu/.editorconfig
@@ -0,0 +1,13 @@
+# To use this config with your editor, follow the instructions at:
+# http://editorconfig.org
+
+[*]
+charset = utf-8
+indent_style = tab
+indent_size = 8
+tab_width = 8
+insert_final_newline = true
+
+[meson.build]
+indent_style = space
+indent_size = 2
diff --git a/amdgpu/Android.mk b/amdgpu/Android.mk
new file mode 100644
index 0000000..1f028d0
--- /dev/null
+++ b/amdgpu/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_AMDGPU_FILES, LIBDRM_AMDGPU_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_amdgpu
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_AMDGPU_FILES)
+
+LOCAL_CFLAGS := \
+ -DAMDGPU_ASIC_ID_TABLE=\"/vendor/etc/hwdata/amdgpu.ids\"
+
+LOCAL_REQUIRED_MODULES := amdgpu.ids
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/amdgpu/Makefile.sources b/amdgpu/Makefile.sources
new file mode 100644
index 0000000..d6df324
--- /dev/null
+++ b/amdgpu/Makefile.sources
@@ -0,0 +1,14 @@
+LIBDRM_AMDGPU_FILES := \
+ amdgpu_asic_id.c \
+ amdgpu_bo.c \
+ amdgpu_cs.c \
+ amdgpu_device.c \
+ amdgpu_gpu_info.c \
+ amdgpu_internal.h \
+ amdgpu_vamgr.c \
+ amdgpu_vm.c \
+ handle_table.c \
+ handle_table.h
+
+LIBDRM_AMDGPU_H_FILES := \
+ amdgpu.h
diff --git a/amdgpu/amdgpu-symbols.txt b/amdgpu/amdgpu-symbols.txt
new file mode 100644
index 0000000..e3bafaa
--- /dev/null
+++ b/amdgpu/amdgpu-symbols.txt
@@ -0,0 +1,74 @@
+amdgpu_bo_alloc
+amdgpu_bo_cpu_map
+amdgpu_bo_cpu_unmap
+amdgpu_bo_export
+amdgpu_bo_free
+amdgpu_bo_import
+amdgpu_bo_inc_ref
+amdgpu_bo_list_create_raw
+amdgpu_bo_list_destroy_raw
+amdgpu_bo_list_create
+amdgpu_bo_list_destroy
+amdgpu_bo_list_update
+amdgpu_bo_query_info
+amdgpu_bo_set_metadata
+amdgpu_bo_va_op
+amdgpu_bo_va_op_raw
+amdgpu_bo_wait_for_idle
+amdgpu_create_bo_from_user_mem
+amdgpu_cs_chunk_fence_info_to_data
+amdgpu_cs_chunk_fence_to_dep
+amdgpu_cs_create_semaphore
+amdgpu_cs_create_syncobj
+amdgpu_cs_create_syncobj2
+amdgpu_cs_ctx_create
+amdgpu_cs_ctx_create2
+amdgpu_cs_ctx_free
+amdgpu_cs_ctx_override_priority
+amdgpu_cs_destroy_semaphore
+amdgpu_cs_destroy_syncobj
+amdgpu_cs_export_syncobj
+amdgpu_cs_fence_to_handle
+amdgpu_cs_import_syncobj
+amdgpu_cs_query_fence_status
+amdgpu_cs_query_reset_state
+amdgpu_cs_query_reset_state2
+amdgpu_query_sw_info
+amdgpu_cs_signal_semaphore
+amdgpu_cs_submit
+amdgpu_cs_submit_raw
+amdgpu_cs_submit_raw2
+amdgpu_cs_syncobj_export_sync_file
+amdgpu_cs_syncobj_export_sync_file2
+amdgpu_cs_syncobj_import_sync_file
+amdgpu_cs_syncobj_import_sync_file2
+amdgpu_cs_syncobj_query
+amdgpu_cs_syncobj_query2
+amdgpu_cs_syncobj_reset
+amdgpu_cs_syncobj_signal
+amdgpu_cs_syncobj_timeline_signal
+amdgpu_cs_syncobj_timeline_wait
+amdgpu_cs_syncobj_transfer
+amdgpu_cs_syncobj_wait
+amdgpu_cs_wait_fences
+amdgpu_cs_wait_semaphore
+amdgpu_device_deinitialize
+amdgpu_device_initialize
+amdgpu_find_bo_by_cpu_mapping
+amdgpu_get_marketing_name
+amdgpu_query_buffer_size_alignment
+amdgpu_query_crtc_from_id
+amdgpu_query_firmware_version
+amdgpu_query_gds_info
+amdgpu_query_gpu_info
+amdgpu_query_heap_info
+amdgpu_query_hw_ip_count
+amdgpu_query_hw_ip_info
+amdgpu_query_info
+amdgpu_query_sensor_info
+amdgpu_read_mm_registers
+amdgpu_va_range_alloc
+amdgpu_va_range_free
+amdgpu_va_range_query
+amdgpu_vm_reserve_vmid
+amdgpu_vm_unreserve_vmid
diff --git a/amdgpu/amdgpu.h b/amdgpu/amdgpu.h
new file mode 100644
index 0000000..188179c
--- /dev/null
+++ b/amdgpu/amdgpu.h
@@ -0,0 +1,1830 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * \file amdgpu.h
+ *
+ * Declare public libdrm_amdgpu API
+ *
+ * This file define API exposed by libdrm_amdgpu library.
+ * User wanted to use libdrm_amdgpu functionality must include
+ * this file.
+ *
+ */
+#ifndef _AMDGPU_H_
+#define _AMDGPU_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct drm_amdgpu_info_hw_ip;
+struct drm_amdgpu_bo_list_entry;
+
+/*--------------------------------------------------------------------------*/
+/* --------------------------- Defines ------------------------------------ */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Define max. number of Command Buffers (IB) which could be sent to the single
+ * hardware IP to accommodate CE/DE requirements
+ *
+ * \sa amdgpu_cs_ib_info
+*/
+#define AMDGPU_CS_MAX_IBS_PER_SUBMIT 4
+
+/**
+ * Special timeout value meaning that the timeout is infinite.
+ */
+#define AMDGPU_TIMEOUT_INFINITE 0xffffffffffffffffull
+
+/**
+ * Used in amdgpu_cs_query_fence_status(), meaning that the given timeout
+ * is absolute.
+ */
+#define AMDGPU_QUERY_FENCE_TIMEOUT_IS_ABSOLUTE (1 << 0)
+
+/*--------------------------------------------------------------------------*/
+/* ----------------------------- Enums ------------------------------------ */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Enum describing possible handle types
+ *
+ * \sa amdgpu_bo_import, amdgpu_bo_export
+ *
+*/
+enum amdgpu_bo_handle_type {
+ /** GEM flink name (needs DRM authentication, used by DRI2) */
+ amdgpu_bo_handle_type_gem_flink_name = 0,
+
+ /** KMS handle which is used by all driver ioctls */
+ amdgpu_bo_handle_type_kms = 1,
+
+ /** DMA-buf fd handle */
+ amdgpu_bo_handle_type_dma_buf_fd = 2,
+
+ /** Deprecated in favour of and same behaviour as
+ * amdgpu_bo_handle_type_kms, use that instead of this
+ */
+ amdgpu_bo_handle_type_kms_noimport = 3,
+};
+
+/** Define known types of GPU VM VA ranges */
+enum amdgpu_gpu_va_range
+{
+ /** Allocate from "normal"/general range */
+ amdgpu_gpu_va_range_general = 0
+};
+
+enum amdgpu_sw_info {
+ amdgpu_sw_info_address32_hi = 0,
+};
+
+/*--------------------------------------------------------------------------*/
+/* -------------------------- Datatypes ----------------------------------- */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Define opaque pointer to context associated with fd.
+ * This context will be returned as the result of
+ * "initialize" function and should be pass as the first
+ * parameter to any API call
+ */
+typedef struct amdgpu_device *amdgpu_device_handle;
+
+/**
+ * Define GPU Context type as pointer to opaque structure
+ * Example of GPU Context is the "rendering" context associated
+ * with OpenGL context (glCreateContext)
+ */
+typedef struct amdgpu_context *amdgpu_context_handle;
+
+/**
+ * Define handle for amdgpu resources: buffer, GDS, etc.
+ */
+typedef struct amdgpu_bo *amdgpu_bo_handle;
+
+/**
+ * Define handle for list of BOs
+ */
+typedef struct amdgpu_bo_list *amdgpu_bo_list_handle;
+
+/**
+ * Define handle to be used to work with VA allocated ranges
+ */
+typedef struct amdgpu_va *amdgpu_va_handle;
+
+/**
+ * Define handle for semaphore
+ */
+typedef struct amdgpu_semaphore *amdgpu_semaphore_handle;
+
+/*--------------------------------------------------------------------------*/
+/* -------------------------- Structures ---------------------------------- */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Structure describing memory allocation request
+ *
+ * \sa amdgpu_bo_alloc()
+ *
+*/
+struct amdgpu_bo_alloc_request {
+ /** Allocation request. It must be aligned correctly. */
+ uint64_t alloc_size;
+
+ /**
+ * It may be required to have some specific alignment requirements
+ * for physical back-up storage (e.g. for displayable surface).
+ * If 0 there is no special alignment requirement
+ */
+ uint64_t phys_alignment;
+
+ /**
+ * UMD should specify where to allocate memory and how it
+ * will be accessed by the CPU.
+ */
+ uint32_t preferred_heap;
+
+ /** Additional flags passed on allocation */
+ uint64_t flags;
+};
+
+/**
+ * Special UMD specific information associated with buffer.
+ *
+ * It may be need to pass some buffer charactersitic as part
+ * of buffer sharing. Such information are defined UMD and
+ * opaque for libdrm_amdgpu as well for kernel driver.
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_query_info,
+ * amdgpu_bo_import(), amdgpu_bo_export
+ *
+*/
+struct amdgpu_bo_metadata {
+ /** Special flag associated with surface */
+ uint64_t flags;
+
+ /**
+ * ASIC-specific tiling information (also used by DCE).
+ * The encoding is defined by the AMDGPU_TILING_* definitions.
+ */
+ uint64_t tiling_info;
+
+ /** Size of metadata associated with the buffer, in bytes. */
+ uint32_t size_metadata;
+
+ /** UMD specific metadata. Opaque for kernel */
+ uint32_t umd_metadata[64];
+};
+
+/**
+ * Structure describing allocated buffer. Client may need
+ * to query such information as part of 'sharing' buffers mechanism
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_query_info(),
+ * amdgpu_bo_import(), amdgpu_bo_export()
+*/
+struct amdgpu_bo_info {
+ /** Allocated memory size */
+ uint64_t alloc_size;
+
+ /**
+ * It may be required to have some specific alignment requirements
+ * for physical back-up storage.
+ */
+ uint64_t phys_alignment;
+
+ /** Heap where to allocate memory. */
+ uint32_t preferred_heap;
+
+ /** Additional allocation flags. */
+ uint64_t alloc_flags;
+
+ /** Metadata associated with buffer if any. */
+ struct amdgpu_bo_metadata metadata;
+};
+
+/**
+ * Structure with information about "imported" buffer
+ *
+ * \sa amdgpu_bo_import()
+ *
+ */
+struct amdgpu_bo_import_result {
+ /** Handle of memory/buffer to use */
+ amdgpu_bo_handle buf_handle;
+
+ /** Buffer size */
+ uint64_t alloc_size;
+};
+
+/**
+ *
+ * Structure to describe GDS partitioning information.
+ * \note OA and GWS resources are asscoiated with GDS partition
+ *
+ * \sa amdgpu_gpu_resource_query_gds_info
+ *
+*/
+struct amdgpu_gds_resource_info {
+ uint32_t gds_gfx_partition_size;
+ uint32_t compute_partition_size;
+ uint32_t gds_total_size;
+ uint32_t gws_per_gfx_partition;
+ uint32_t gws_per_compute_partition;
+ uint32_t oa_per_gfx_partition;
+ uint32_t oa_per_compute_partition;
+};
+
+/**
+ * Structure describing CS fence
+ *
+ * \sa amdgpu_cs_query_fence_status(), amdgpu_cs_request, amdgpu_cs_submit()
+ *
+*/
+struct amdgpu_cs_fence {
+
+ /** In which context IB was sent to execution */
+ amdgpu_context_handle context;
+
+ /** To which HW IP type the fence belongs */
+ uint32_t ip_type;
+
+ /** IP instance index if there are several IPs of the same type. */
+ uint32_t ip_instance;
+
+ /** Ring index of the HW IP */
+ uint32_t ring;
+
+ /** Specify fence for which we need to check submission status.*/
+ uint64_t fence;
+};
+
+/**
+ * Structure describing IB
+ *
+ * \sa amdgpu_cs_request, amdgpu_cs_submit()
+ *
+*/
+struct amdgpu_cs_ib_info {
+ /** Special flags */
+ uint64_t flags;
+
+ /** Virtual MC address of the command buffer */
+ uint64_t ib_mc_address;
+
+ /**
+ * Size of Command Buffer to be submitted.
+ * - The size is in units of dwords (4 bytes).
+ * - Could be 0
+ */
+ uint32_t size;
+};
+
+/**
+ * Structure describing fence information
+ *
+ * \sa amdgpu_cs_request, amdgpu_cs_query_fence,
+ * amdgpu_cs_submit(), amdgpu_cs_query_fence_status()
+*/
+struct amdgpu_cs_fence_info {
+ /** buffer object for the fence */
+ amdgpu_bo_handle handle;
+
+ /** fence offset in the unit of sizeof(uint64_t) */
+ uint64_t offset;
+};
+
+/**
+ * Structure describing submission request
+ *
+ * \note We could have several IBs as packet. e.g. CE, CE, DE case for gfx
+ *
+ * \sa amdgpu_cs_submit()
+*/
+struct amdgpu_cs_request {
+ /** Specify flags with additional information */
+ uint64_t flags;
+
+ /** Specify HW IP block type to which to send the IB. */
+ unsigned ip_type;
+
+ /** IP instance index if there are several IPs of the same type. */
+ unsigned ip_instance;
+
+ /**
+ * Specify ring index of the IP. We could have several rings
+ * in the same IP. E.g. 0 for SDMA0 and 1 for SDMA1.
+ */
+ uint32_t ring;
+
+ /**
+ * List handle with resources used by this request.
+ */
+ amdgpu_bo_list_handle resources;
+
+ /**
+ * Number of dependencies this Command submission needs to
+ * wait for before starting execution.
+ */
+ uint32_t number_of_dependencies;
+
+ /**
+ * Array of dependencies which need to be met before
+ * execution can start.
+ */
+ struct amdgpu_cs_fence *dependencies;
+
+ /** Number of IBs to submit in the field ibs. */
+ uint32_t number_of_ibs;
+
+ /**
+ * IBs to submit. Those IBs will be submit together as single entity
+ */
+ struct amdgpu_cs_ib_info *ibs;
+
+ /**
+ * The returned sequence number for the command submission
+ */
+ uint64_t seq_no;
+
+ /**
+ * The fence information
+ */
+ struct amdgpu_cs_fence_info fence_info;
+};
+
+/**
+ * Structure which provide information about GPU VM MC Address space
+ * alignments requirements
+ *
+ * \sa amdgpu_query_buffer_size_alignment
+ */
+struct amdgpu_buffer_size_alignments {
+ /** Size alignment requirement for allocation in
+ * local memory */
+ uint64_t size_local;
+
+ /**
+ * Size alignment requirement for allocation in remote memory
+ */
+ uint64_t size_remote;
+};
+
+/**
+ * Structure which provide information about heap
+ *
+ * \sa amdgpu_query_heap_info()
+ *
+ */
+struct amdgpu_heap_info {
+ /** Theoretical max. available memory in the given heap */
+ uint64_t heap_size;
+
+ /**
+ * Number of bytes allocated in the heap. This includes all processes
+ * and private allocations in the kernel. It changes when new buffers
+ * are allocated, freed, and moved. It cannot be larger than
+ * heap_size.
+ */
+ uint64_t heap_usage;
+
+ /**
+ * Theoretical possible max. size of buffer which
+ * could be allocated in the given heap
+ */
+ uint64_t max_allocation;
+};
+
+/**
+ * Describe GPU h/w info needed for UMD correct initialization
+ *
+ * \sa amdgpu_query_gpu_info()
+*/
+struct amdgpu_gpu_info {
+ /** Asic id */
+ uint32_t asic_id;
+ /** Chip revision */
+ uint32_t chip_rev;
+ /** Chip external revision */
+ uint32_t chip_external_rev;
+ /** Family ID */
+ uint32_t family_id;
+ /** Special flags */
+ uint64_t ids_flags;
+ /** max engine clock*/
+ uint64_t max_engine_clk;
+ /** max memory clock */
+ uint64_t max_memory_clk;
+ /** number of shader engines */
+ uint32_t num_shader_engines;
+ /** number of shader arrays per engine */
+ uint32_t num_shader_arrays_per_engine;
+ /** Number of available good shader pipes */
+ uint32_t avail_quad_shader_pipes;
+ /** Max. number of shader pipes.(including good and bad pipes */
+ uint32_t max_quad_shader_pipes;
+ /** Number of parameter cache entries per shader quad pipe */
+ uint32_t cache_entries_per_quad_pipe;
+ /** Number of available graphics context */
+ uint32_t num_hw_gfx_contexts;
+ /** Number of render backend pipes */
+ uint32_t rb_pipes;
+ /** Enabled render backend pipe mask */
+ uint32_t enabled_rb_pipes_mask;
+ /** Frequency of GPU Counter */
+ uint32_t gpu_counter_freq;
+ /** CC_RB_BACKEND_DISABLE.BACKEND_DISABLE per SE */
+ uint32_t backend_disable[4];
+ /** Value of MC_ARB_RAMCFG register*/
+ uint32_t mc_arb_ramcfg;
+ /** Value of GB_ADDR_CONFIG */
+ uint32_t gb_addr_cfg;
+ /** Values of the GB_TILE_MODE0..31 registers */
+ uint32_t gb_tile_mode[32];
+ /** Values of GB_MACROTILE_MODE0..15 registers */
+ uint32_t gb_macro_tile_mode[16];
+ /** Value of PA_SC_RASTER_CONFIG register per SE */
+ uint32_t pa_sc_raster_cfg[4];
+ /** Value of PA_SC_RASTER_CONFIG_1 register per SE */
+ uint32_t pa_sc_raster_cfg1[4];
+ /* CU info */
+ uint32_t cu_active_number;
+ uint32_t cu_ao_mask;
+ uint32_t cu_bitmap[4][4];
+ /* video memory type info*/
+ uint32_t vram_type;
+ /* video memory bit width*/
+ uint32_t vram_bit_width;
+ /** constant engine ram size*/
+ uint32_t ce_ram_size;
+ /* vce harvesting instance */
+ uint32_t vce_harvest_config;
+ /* PCI revision ID */
+ uint32_t pci_rev_id;
+};
+
+
+/*--------------------------------------------------------------------------*/
+/*------------------------- Functions --------------------------------------*/
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Initialization / Cleanup
+ *
+*/
+
+/**
+ *
+ * \param fd - \c [in] File descriptor for AMD GPU device
+ * received previously as the result of
+ * e.g. drmOpen() call.
+ * For legacy fd type, the DRI2/DRI3
+ * authentication should be done before
+ * calling this function.
+ * \param major_version - \c [out] Major version of library. It is assumed
+ * that adding new functionality will cause
+ * increase in major version
+ * \param minor_version - \c [out] Minor version of library
+ * \param device_handle - \c [out] Pointer to opaque context which should
+ * be passed as the first parameter on each
+ * API call
+ *
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ *
+ * \sa amdgpu_device_deinitialize()
+*/
+int amdgpu_device_initialize(int fd,
+ uint32_t *major_version,
+ uint32_t *minor_version,
+ amdgpu_device_handle *device_handle);
+
+/**
+ *
+ * When access to such library does not needed any more the special
+ * function must be call giving opportunity to clean up any
+ * resources if needed.
+ *
+ * \param device_handle - \c [in] Context associated with file
+ * descriptor for AMD GPU device
+ * received previously as the
+ * result e.g. of drmOpen() call.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_device_initialize()
+ *
+*/
+int amdgpu_device_deinitialize(amdgpu_device_handle device_handle);
+
+/*
+ * Memory Management
+ *
+*/
+
+/**
+ * Allocate memory to be used by UMD for GPU related operations
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param alloc_buffer - \c [in] Pointer to the structure describing an
+ * allocation request
+ * \param buf_handle - \c [out] Allocated buffer handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_free()
+*/
+int amdgpu_bo_alloc(amdgpu_device_handle dev,
+ struct amdgpu_bo_alloc_request *alloc_buffer,
+ amdgpu_bo_handle *buf_handle);
+
+/**
+ * Associate opaque data with buffer to be queried by another UMD
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param buf_handle - \c [in] Buffer handle
+ * \param info - \c [in] Metadata to associated with buffer
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+*/
+int amdgpu_bo_set_metadata(amdgpu_bo_handle buf_handle,
+ struct amdgpu_bo_metadata *info);
+
+/**
+ * Query buffer information including metadata previusly associated with
+ * buffer.
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param buf_handle - \c [in] Buffer handle
+ * \param info - \c [out] Structure describing buffer
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_alloc()
+*/
+int amdgpu_bo_query_info(amdgpu_bo_handle buf_handle,
+ struct amdgpu_bo_info *info);
+
+/**
+ * Allow others to get access to buffer
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param buf_handle - \c [in] Buffer handle
+ * \param type - \c [in] Type of handle requested
+ * \param shared_handle - \c [out] Special "shared" handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_import()
+ *
+*/
+int amdgpu_bo_export(amdgpu_bo_handle buf_handle,
+ enum amdgpu_bo_handle_type type,
+ uint32_t *shared_handle);
+
+/**
+ * Request access to "shared" buffer
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param type - \c [in] Type of handle requested
+ * \param shared_handle - \c [in] Shared handle received as result "import"
+ * operation
+ * \param output - \c [out] Pointer to structure with information
+ * about imported buffer
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \note Buffer must be "imported" only using new "fd" (different from
+ * one used by "exporter").
+ *
+ * \sa amdgpu_bo_export()
+ *
+*/
+int amdgpu_bo_import(amdgpu_device_handle dev,
+ enum amdgpu_bo_handle_type type,
+ uint32_t shared_handle,
+ struct amdgpu_bo_import_result *output);
+
+/**
+ * Request GPU access to user allocated memory e.g. via "malloc"
+ *
+ * \param dev - [in] Device handle. See #amdgpu_device_initialize()
+ * \param cpu - [in] CPU address of user allocated memory which we
+ * want to map to GPU address space (make GPU accessible)
+ * (This address must be correctly aligned).
+ * \param size - [in] Size of allocation (must be correctly aligned)
+ * \param buf_handle - [out] Buffer handle for the userptr memory
+ * resource on submission and be used in other operations.
+ *
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \note
+ * This call doesn't guarantee that such memory will be persistently
+ * "locked" / make non-pageable. The purpose of this call is to provide
+ * opportunity for GPU get access to this resource during submission.
+ *
+ * The maximum amount of memory which could be mapped in this call depends
+ * if overcommit is disabled or not. If overcommit is disabled than the max.
+ * amount of memory to be pinned will be limited by left "free" size in total
+ * amount of memory which could be locked simultaneously ("GART" size).
+ *
+ * Supported (theoretical) max. size of mapping is restricted only by
+ * "GART" size.
+ *
+ * It is responsibility of caller to correctly specify access rights
+ * on VA assignment.
+*/
+int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev,
+ void *cpu, uint64_t size,
+ amdgpu_bo_handle *buf_handle);
+
+/**
+ * Validate if the user memory comes from BO
+ *
+ * \param dev - [in] Device handle. See #amdgpu_device_initialize()
+ * \param cpu - [in] CPU address of user allocated memory which we
+ * want to map to GPU address space (make GPU accessible)
+ * (This address must be correctly aligned).
+ * \param size - [in] Size of allocation (must be correctly aligned)
+ * \param buf_handle - [out] Buffer handle for the userptr memory
+ * if the user memory is not from BO, the buf_handle will be NULL.
+ * \param offset_in_bo - [out] offset in this BO for this user memory
+ *
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_find_bo_by_cpu_mapping(amdgpu_device_handle dev,
+ void *cpu,
+ uint64_t size,
+ amdgpu_bo_handle *buf_handle,
+ uint64_t *offset_in_bo);
+
+/**
+ * Free previously allocated memory
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param buf_handle - \c [in] Buffer handle to free
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \note In the case of memory shared between different applications all
+ * resources will be “physically” freed only all such applications
+ * will be terminated
+ * \note If is UMD responsibility to ‘free’ buffer only when there is no
+ * more GPU access
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_alloc()
+ *
+*/
+int amdgpu_bo_free(amdgpu_bo_handle buf_handle);
+
+/**
+ * Increase the reference count of a buffer object
+ *
+ * \param bo - \c [in] Buffer object handle to increase the reference count
+ *
+ * \sa amdgpu_bo_alloc(), amdgpu_bo_free()
+ *
+*/
+void amdgpu_bo_inc_ref(amdgpu_bo_handle bo);
+
+/**
+ * Request CPU access to GPU accessible memory
+ *
+ * \param buf_handle - \c [in] Buffer handle
+ * \param cpu - \c [out] CPU address to be used for access
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_cpu_unmap()
+ *
+*/
+int amdgpu_bo_cpu_map(amdgpu_bo_handle buf_handle, void **cpu);
+
+/**
+ * Release CPU access to GPU memory
+ *
+ * \param buf_handle - \c [in] Buffer handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_cpu_map()
+ *
+*/
+int amdgpu_bo_cpu_unmap(amdgpu_bo_handle buf_handle);
+
+/**
+ * Wait until a buffer is not used by the device.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param buf_handle - \c [in] Buffer handle.
+ * \param timeout_ns - Timeout in nanoseconds.
+ * \param buffer_busy - 0 if buffer is idle, all GPU access was completed
+ * and no GPU access is scheduled.
+ * 1 GPU access is in fly or scheduled
+ *
+ * \return 0 - on success
+ * <0 - Negative POSIX Error code
+ */
+int amdgpu_bo_wait_for_idle(amdgpu_bo_handle buf_handle,
+ uint64_t timeout_ns,
+ bool *buffer_busy);
+
+/**
+ * Creates a BO list handle for command submission.
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param number_of_buffers - \c [in] Number of BOs in the list
+ * \param buffers - \c [in] List of BO handles
+ * \param result - \c [out] Created BO list handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_destroy_raw(), amdgpu_cs_submit_raw2()
+*/
+int amdgpu_bo_list_create_raw(amdgpu_device_handle dev,
+ uint32_t number_of_buffers,
+ struct drm_amdgpu_bo_list_entry *buffers,
+ uint32_t *result);
+
+/**
+ * Destroys a BO list handle.
+ *
+ * \param bo_list - \c [in] BO list handle.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_create_raw(), amdgpu_cs_submit_raw2()
+*/
+int amdgpu_bo_list_destroy_raw(amdgpu_device_handle dev, uint32_t bo_list);
+
+/**
+ * Creates a BO list handle for command submission.
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param number_of_resources - \c [in] Number of BOs in the list
+ * \param resources - \c [in] List of BO handles
+ * \param resource_prios - \c [in] Optional priority for each handle
+ * \param result - \c [out] Created BO list handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_destroy()
+*/
+int amdgpu_bo_list_create(amdgpu_device_handle dev,
+ uint32_t number_of_resources,
+ amdgpu_bo_handle *resources,
+ uint8_t *resource_prios,
+ amdgpu_bo_list_handle *result);
+
+/**
+ * Destroys a BO list handle.
+ *
+ * \param handle - \c [in] BO list handle.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_create()
+*/
+int amdgpu_bo_list_destroy(amdgpu_bo_list_handle handle);
+
+/**
+ * Update resources for existing BO list
+ *
+ * \param handle - \c [in] BO list handle
+ * \param number_of_resources - \c [in] Number of BOs in the list
+ * \param resources - \c [in] List of BO handles
+ * \param resource_prios - \c [in] Optional priority for each handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_update()
+*/
+int amdgpu_bo_list_update(amdgpu_bo_list_handle handle,
+ uint32_t number_of_resources,
+ amdgpu_bo_handle *resources,
+ uint8_t *resource_prios);
+
+/*
+ * GPU Execution context
+ *
+*/
+
+/**
+ * Create GPU execution Context
+ *
+ * For the purpose of GPU Scheduler and GPU Robustness extensions it is
+ * necessary to have information/identify rendering/compute contexts.
+ * It also may be needed to associate some specific requirements with such
+ * contexts. Kernel driver will guarantee that submission from the same
+ * context will always be executed in order (first come, first serve).
+ *
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param priority - \c [in] Context creation flags. See AMDGPU_CTX_PRIORITY_*
+ * \param context - \c [out] GPU Context handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_free()
+ *
+*/
+int amdgpu_cs_ctx_create2(amdgpu_device_handle dev,
+ uint32_t priority,
+ amdgpu_context_handle *context);
+/**
+ * Create GPU execution Context
+ *
+ * Refer to amdgpu_cs_ctx_create2 for full documentation. This call
+ * is missing the priority parameter.
+ *
+ * \sa amdgpu_cs_ctx_create2()
+ *
+*/
+int amdgpu_cs_ctx_create(amdgpu_device_handle dev,
+ amdgpu_context_handle *context);
+
+/**
+ *
+ * Destroy GPU execution context when not needed any more
+ *
+ * \param context - \c [in] GPU Context handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_ctx_free(amdgpu_context_handle context);
+
+/**
+ * Override the submission priority for the given context using a master fd.
+ *
+ * \param dev - \c [in] device handle
+ * \param context - \c [in] context handle for context id
+ * \param master_fd - \c [in] The master fd to authorize the override.
+ * \param priority - \c [in] The priority to assign to the context.
+ *
+ * \return 0 on success or a a negative Posix error code on failure.
+ */
+int amdgpu_cs_ctx_override_priority(amdgpu_device_handle dev,
+ amdgpu_context_handle context,
+ int master_fd,
+ unsigned priority);
+
+/**
+ * Query reset state for the specific GPU Context
+ *
+ * \param context - \c [in] GPU Context handle
+ * \param state - \c [out] One of AMDGPU_CTX_*_RESET
+ * \param hangs - \c [out] Number of hangs caused by the context.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_query_reset_state(amdgpu_context_handle context,
+ uint32_t *state, uint32_t *hangs);
+
+/**
+ * Query reset state for the specific GPU Context.
+ *
+ * \param context - \c [in] GPU Context handle
+ * \param flags - \c [out] A combination of AMDGPU_CTX_QUERY2_FLAGS_*
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_query_reset_state2(amdgpu_context_handle context,
+ uint64_t *flags);
+
+/*
+ * Command Buffers Management
+ *
+*/
+
+/**
+ * Send request to submit command buffers to hardware.
+ *
+ * Kernel driver could use GPU Scheduler to make decision when physically
+ * sent this request to the hardware. Accordingly this request could be put
+ * in queue and sent for execution later. The only guarantee is that request
+ * from the same GPU context to the same ip:ip_instance:ring will be executed in
+ * order.
+ *
+ * The caller can specify the user fence buffer/location with the fence_info in the
+ * cs_request.The sequence number is returned via the 'seq_no' parameter
+ * in ibs_request structure.
+ *
+ *
+ * \param dev - \c [in] Device handle.
+ * See #amdgpu_device_initialize()
+ * \param context - \c [in] GPU Context
+ * \param flags - \c [in] Global submission flags
+ * \param ibs_request - \c [in/out] Pointer to submission requests.
+ * We could submit to the several
+ * engines/rings simulteniously as
+ * 'atomic' operation
+ * \param number_of_requests - \c [in] Number of submission requests
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \note It is required to pass correct resource list with buffer handles
+ * which will be accessible by command buffers from submission
+ * This will allow kernel driver to correctly implement "paging".
+ * Failure to do so will have unpredictable results.
+ *
+ * \sa amdgpu_command_buffer_alloc(), amdgpu_command_buffer_free(),
+ * amdgpu_cs_query_fence_status()
+ *
+*/
+int amdgpu_cs_submit(amdgpu_context_handle context,
+ uint64_t flags,
+ struct amdgpu_cs_request *ibs_request,
+ uint32_t number_of_requests);
+
+/**
+ * Query status of Command Buffer Submission
+ *
+ * \param fence - \c [in] Structure describing fence to query
+ * \param timeout_ns - \c [in] Timeout value to wait
+ * \param flags - \c [in] Flags for the query
+ * \param expired - \c [out] If fence expired or not.\n
+ * 0 – if fence is not expired\n
+ * !0 - otherwise
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \note If UMD wants only to check operation status and returned immediately
+ * then timeout value as 0 must be passed. In this case success will be
+ * returned in the case if submission was completed or timeout error
+ * code.
+ *
+ * \sa amdgpu_cs_submit()
+*/
+int amdgpu_cs_query_fence_status(struct amdgpu_cs_fence *fence,
+ uint64_t timeout_ns,
+ uint64_t flags,
+ uint32_t *expired);
+
+/**
+ * Wait for multiple fences
+ *
+ * \param fences - \c [in] The fence array to wait
+ * \param fence_count - \c [in] The fence count
+ * \param wait_all - \c [in] If true, wait all fences to be signaled,
+ * otherwise, wait at least one fence
+ * \param timeout_ns - \c [in] The timeout to wait, in nanoseconds
+ * \param status - \c [out] '1' for signaled, '0' for timeout
+ * \param first - \c [out] the index of the first signaled fence from @fences
+ *
+ * \return 0 on success
+ * <0 - Negative POSIX Error code
+ *
+ * \note Currently it supports only one amdgpu_device. All fences come from
+ * the same amdgpu_device with the same fd.
+*/
+int amdgpu_cs_wait_fences(struct amdgpu_cs_fence *fences,
+ uint32_t fence_count,
+ bool wait_all,
+ uint64_t timeout_ns,
+ uint32_t *status, uint32_t *first);
+
+/*
+ * Query / Info API
+ *
+*/
+
+/**
+ * Query allocation size alignments
+ *
+ * UMD should query information about GPU VM MC size alignments requirements
+ * to be able correctly choose required allocation size and implement
+ * internal optimization if needed.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param info - \c [out] Pointer to structure to get size alignment
+ * requirements
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev,
+ struct amdgpu_buffer_size_alignments
+ *info);
+
+/**
+ * Query firmware versions
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param fw_type - \c [in] AMDGPU_INFO_FW_*
+ * \param ip_instance - \c [in] Index of the IP block of the same type.
+ * \param index - \c [in] Index of the engine. (for SDMA and MEC)
+ * \param version - \c [out] Pointer to to the "version" return value
+ * \param feature - \c [out] Pointer to to the "feature" return value
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_firmware_version(amdgpu_device_handle dev, unsigned fw_type,
+ unsigned ip_instance, unsigned index,
+ uint32_t *version, uint32_t *feature);
+
+/**
+ * Query the number of HW IP instances of a certain type.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param type - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param count - \c [out] Pointer to structure to get information
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+*/
+int amdgpu_query_hw_ip_count(amdgpu_device_handle dev, unsigned type,
+ uint32_t *count);
+
+/**
+ * Query engine information
+ *
+ * This query allows UMD to query information different engines and their
+ * capabilities.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param type - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param ip_instance - \c [in] Index of the IP block of the same type.
+ * \param info - \c [out] Pointer to structure to get information
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+*/
+int amdgpu_query_hw_ip_info(amdgpu_device_handle dev, unsigned type,
+ unsigned ip_instance,
+ struct drm_amdgpu_info_hw_ip *info);
+
+/**
+ * Query heap information
+ *
+ * This query allows UMD to query potentially available memory resources and
+ * adjust their logic if necessary.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param heap - \c [in] Heap type
+ * \param info - \c [in] Pointer to structure to get needed information
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_heap_info(amdgpu_device_handle dev, uint32_t heap,
+ uint32_t flags, struct amdgpu_heap_info *info);
+
+/**
+ * Get the CRTC ID from the mode object ID
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param id - \c [in] Mode object ID
+ * \param result - \c [in] Pointer to the CRTC ID
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_crtc_from_id(amdgpu_device_handle dev, unsigned id,
+ int32_t *result);
+
+/**
+ * Query GPU H/w Info
+ *
+ * Query hardware specific information
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param heap - \c [in] Heap type
+ * \param info - \c [in] Pointer to structure to get needed information
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_gpu_info(amdgpu_device_handle dev,
+ struct amdgpu_gpu_info *info);
+
+/**
+ * Query hardware or driver information.
+ *
+ * The return size is query-specific and depends on the "info_id" parameter.
+ * No more than "size" bytes is returned.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param info_id - \c [in] AMDGPU_INFO_*
+ * \param size - \c [in] Size of the returned value.
+ * \param value - \c [out] Pointer to the return value.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX error code
+ *
+*/
+int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id,
+ unsigned size, void *value);
+
+/**
+ * Query hardware or driver information.
+ *
+ * The return size is query-specific and depends on the "info_id" parameter.
+ * No more than "size" bytes is returned.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param info - \c [in] amdgpu_sw_info_*
+ * \param value - \c [out] Pointer to the return value.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX error code
+ *
+*/
+int amdgpu_query_sw_info(amdgpu_device_handle dev, enum amdgpu_sw_info info,
+ void *value);
+
+/**
+ * Query information about GDS
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param gds_info - \c [out] Pointer to structure to get GDS information
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_gds_info(amdgpu_device_handle dev,
+ struct amdgpu_gds_resource_info *gds_info);
+
+/**
+ * Query information about sensor.
+ *
+ * The return size is query-specific and depends on the "sensor_type"
+ * parameter. No more than "size" bytes is returned.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param sensor_type - \c [in] AMDGPU_INFO_SENSOR_*
+ * \param size - \c [in] Size of the returned value.
+ * \param value - \c [out] Pointer to the return value.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_sensor_info(amdgpu_device_handle dev, unsigned sensor_type,
+ unsigned size, void *value);
+
+/**
+ * Read a set of consecutive memory-mapped registers.
+ * Not all registers are allowed to be read by userspace.
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize(
+ * \param dword_offset - \c [in] Register offset in dwords
+ * \param count - \c [in] The number of registers to read starting
+ * from the offset
+ * \param instance - \c [in] GRBM_GFX_INDEX selector. It may have other
+ * uses. Set it to 0xffffffff if unsure.
+ * \param flags - \c [in] Flags with additional information.
+ * \param values - \c [out] The pointer to return values.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX error code
+ *
+*/
+int amdgpu_read_mm_registers(amdgpu_device_handle dev, unsigned dword_offset,
+ unsigned count, uint32_t instance, uint32_t flags,
+ uint32_t *values);
+
+/**
+ * Flag to request VA address range in the 32bit address space
+*/
+#define AMDGPU_VA_RANGE_32_BIT 0x1
+#define AMDGPU_VA_RANGE_HIGH 0x2
+
+/**
+ * Allocate virtual address range
+ *
+ * \param dev - [in] Device handle. See #amdgpu_device_initialize()
+ * \param va_range_type - \c [in] Type of MC va range from which to allocate
+ * \param size - \c [in] Size of range. Size must be correctly* aligned.
+ * It is client responsibility to correctly aligned size based on the future
+ * usage of allocated range.
+ * \param va_base_alignment - \c [in] Overwrite base address alignment
+ * requirement for GPU VM MC virtual
+ * address assignment. Must be multiple of size alignments received as
+ * 'amdgpu_buffer_size_alignments'.
+ * If 0 use the default one.
+ * \param va_base_required - \c [in] Specified required va base address.
+ * If 0 then library choose available one.
+ * If !0 value will be passed and those value already "in use" then
+ * corresponding error status will be returned.
+ * \param va_base_allocated - \c [out] On return: Allocated VA base to be used
+ * by client.
+ * \param va_range_handle - \c [out] On return: Handle assigned to allocation
+ * \param flags - \c [in] flags for special VA range
+ *
+ * \return 0 on success\n
+ * >0 - AMD specific error code\n
+ * <0 - Negative POSIX Error code
+ *
+ * \notes \n
+ * It is client responsibility to correctly handle VA assignments and usage.
+ * Neither kernel driver nor libdrm_amdpgu are able to prevent and
+ * detect wrong va assignment.
+ *
+ * It is client responsibility to correctly handle multi-GPU cases and to pass
+ * the corresponding arrays of all devices handles where corresponding VA will
+ * be used.
+ *
+*/
+int amdgpu_va_range_alloc(amdgpu_device_handle dev,
+ enum amdgpu_gpu_va_range va_range_type,
+ uint64_t size,
+ uint64_t va_base_alignment,
+ uint64_t va_base_required,
+ uint64_t *va_base_allocated,
+ amdgpu_va_handle *va_range_handle,
+ uint64_t flags);
+
+/**
+ * Free previously allocated virtual address range
+ *
+ *
+ * \param va_range_handle - \c [in] Handle assigned to VA allocation
+ *
+ * \return 0 on success\n
+ * >0 - AMD specific error code\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_va_range_free(amdgpu_va_handle va_range_handle);
+
+/**
+* Query virtual address range
+*
+* UMD can query GPU VM range supported by each device
+* to initialize its own VAM accordingly.
+*
+* \param dev - [in] Device handle. See #amdgpu_device_initialize()
+* \param type - \c [in] Type of virtual address range
+* \param offset - \c [out] Start offset of virtual address range
+* \param size - \c [out] Size of virtual address range
+*
+* \return 0 on success\n
+* <0 - Negative POSIX Error code
+*
+*/
+
+int amdgpu_va_range_query(amdgpu_device_handle dev,
+ enum amdgpu_gpu_va_range type,
+ uint64_t *start,
+ uint64_t *end);
+
+/**
+ * VA mapping/unmapping for the buffer object
+ *
+ * \param bo - \c [in] BO handle
+ * \param offset - \c [in] Start offset to map
+ * \param size - \c [in] Size to map
+ * \param addr - \c [in] Start virtual address.
+ * \param flags - \c [in] Supported flags for mapping/unmapping
+ * \param ops - \c [in] AMDGPU_VA_OP_MAP or AMDGPU_VA_OP_UNMAP
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+
+int amdgpu_bo_va_op(amdgpu_bo_handle bo,
+ uint64_t offset,
+ uint64_t size,
+ uint64_t addr,
+ uint64_t flags,
+ uint32_t ops);
+
+/**
+ * VA mapping/unmapping for a buffer object or PRT region.
+ *
+ * This is not a simple drop-in extension for amdgpu_bo_va_op; instead, all
+ * parameters are treated "raw", i.e. size is not automatically aligned, and
+ * all flags must be specified explicitly.
+ *
+ * \param dev - \c [in] device handle
+ * \param bo - \c [in] BO handle (may be NULL)
+ * \param offset - \c [in] Start offset to map
+ * \param size - \c [in] Size to map
+ * \param addr - \c [in] Start virtual address.
+ * \param flags - \c [in] Supported flags for mapping/unmapping
+ * \param ops - \c [in] AMDGPU_VA_OP_MAP or AMDGPU_VA_OP_UNMAP
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+
+int amdgpu_bo_va_op_raw(amdgpu_device_handle dev,
+ amdgpu_bo_handle bo,
+ uint64_t offset,
+ uint64_t size,
+ uint64_t addr,
+ uint64_t flags,
+ uint32_t ops);
+
+/**
+ * create semaphore
+ *
+ * \param sem - \c [out] semaphore handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_create_semaphore(amdgpu_semaphore_handle *sem);
+
+/**
+ * signal semaphore
+ *
+ * \param context - \c [in] GPU Context
+ * \param ip_type - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param ip_instance - \c [in] Index of the IP block of the same type
+ * \param ring - \c [in] Specify ring index of the IP
+ * \param sem - \c [in] semaphore handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_signal_semaphore(amdgpu_context_handle ctx,
+ uint32_t ip_type,
+ uint32_t ip_instance,
+ uint32_t ring,
+ amdgpu_semaphore_handle sem);
+
+/**
+ * wait semaphore
+ *
+ * \param context - \c [in] GPU Context
+ * \param ip_type - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param ip_instance - \c [in] Index of the IP block of the same type
+ * \param ring - \c [in] Specify ring index of the IP
+ * \param sem - \c [in] semaphore handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_wait_semaphore(amdgpu_context_handle ctx,
+ uint32_t ip_type,
+ uint32_t ip_instance,
+ uint32_t ring,
+ amdgpu_semaphore_handle sem);
+
+/**
+ * destroy semaphore
+ *
+ * \param sem - \c [in] semaphore handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_destroy_semaphore(amdgpu_semaphore_handle sem);
+
+/**
+ * Get the ASIC marketing name
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ *
+ * \return the constant string of the marketing name
+ * "NULL" means the ASIC is not found
+*/
+const char *amdgpu_get_marketing_name(amdgpu_device_handle dev);
+
+/**
+ * Create kernel sync object
+ *
+ * \param dev - \c [in] device handle
+ * \param flags - \c [in] flags that affect creation
+ * \param syncobj - \c [out] sync object handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_create_syncobj2(amdgpu_device_handle dev,
+ uint32_t flags,
+ uint32_t *syncobj);
+
+/**
+ * Create kernel sync object
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [out] sync object handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_create_syncobj(amdgpu_device_handle dev,
+ uint32_t *syncobj);
+/**
+ * Destroy kernel sync object
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [in] sync object handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_destroy_syncobj(amdgpu_device_handle dev,
+ uint32_t syncobj);
+
+/**
+ * Reset kernel sync objects to unsignalled state.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobjs - \c [in] array of sync object handles
+ * \param syncobj_count - \c [in] number of handles in syncobjs
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_syncobj_reset(amdgpu_device_handle dev,
+ const uint32_t *syncobjs, uint32_t syncobj_count);
+
+/**
+ * Signal kernel sync objects.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobjs - \c [in] array of sync object handles
+ * \param syncobj_count - \c [in] number of handles in syncobjs
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_syncobj_signal(amdgpu_device_handle dev,
+ const uint32_t *syncobjs, uint32_t syncobj_count);
+
+/**
+ * Signal kernel timeline sync objects.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobjs - \c [in] array of sync object handles
+ * \param points - \c [in] array of timeline points
+ * \param syncobj_count - \c [in] number of handles in syncobjs
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_syncobj_timeline_signal(amdgpu_device_handle dev,
+ const uint32_t *syncobjs,
+ uint64_t *points,
+ uint32_t syncobj_count);
+
+/**
+ * Wait for one or all sync objects to signal.
+ *
+ * \param dev - \c [in] self-explanatory
+ * \param handles - \c [in] array of sync object handles
+ * \param num_handles - \c [in] self-explanatory
+ * \param timeout_nsec - \c [in] self-explanatory
+ * \param flags - \c [in] a bitmask of DRM_SYNCOBJ_WAIT_FLAGS_*
+ * \param first_signaled - \c [in] self-explanatory
+ *
+ * \return 0 on success\n
+ * -ETIME - Timeout
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_wait(amdgpu_device_handle dev,
+ uint32_t *handles, unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled);
+
+/**
+ * Wait for one or all sync objects on their points to signal.
+ *
+ * \param dev - \c [in] self-explanatory
+ * \param handles - \c [in] array of sync object handles
+ * \param points - \c [in] array of sync points to wait
+ * \param num_handles - \c [in] self-explanatory
+ * \param timeout_nsec - \c [in] self-explanatory
+ * \param flags - \c [in] a bitmask of DRM_SYNCOBJ_WAIT_FLAGS_*
+ * \param first_signaled - \c [in] self-explanatory
+ *
+ * \return 0 on success\n
+ * -ETIME - Timeout
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_timeline_wait(amdgpu_device_handle dev,
+ uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled);
+/**
+ * Query sync objects payloads.
+ *
+ * \param dev - \c [in] self-explanatory
+ * \param handles - \c [in] array of sync object handles
+ * \param points - \c [out] array of sync points returned, which presents
+ * syncobj payload.
+ * \param num_handles - \c [in] self-explanatory
+ *
+ * \return 0 on success\n
+ * -ETIME - Timeout
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_query(amdgpu_device_handle dev,
+ uint32_t *handles, uint64_t *points,
+ unsigned num_handles);
+/**
+ * Query sync objects last signaled or submitted point.
+ *
+ * \param dev - \c [in] self-explanatory
+ * \param handles - \c [in] array of sync object handles
+ * \param points - \c [out] array of sync points returned, which presents
+ * syncobj payload.
+ * \param num_handles - \c [in] self-explanatory
+ * \param flags - \c [in] a bitmask of DRM_SYNCOBJ_QUERY_FLAGS_*
+ *
+ * \return 0 on success\n
+ * -ETIME - Timeout
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_query2(amdgpu_device_handle dev,
+ uint32_t *handles, uint64_t *points,
+ unsigned num_handles, uint32_t flags);
+
+/**
+ * Export kernel sync object to shareable fd.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [in] sync object handle
+ * \param shared_fd - \c [out] shared file descriptor.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_export_syncobj(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ int *shared_fd);
+/**
+ * Import kernel sync object from shareable fd.
+ *
+ * \param dev - \c [in] device handle
+ * \param shared_fd - \c [in] shared file descriptor.
+ * \param syncobj - \c [out] sync object handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_import_syncobj(amdgpu_device_handle dev,
+ int shared_fd,
+ uint32_t *syncobj);
+
+/**
+ * Export kernel sync object to a sync_file.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [in] sync object handle
+ * \param sync_file_fd - \c [out] sync_file file descriptor.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_export_sync_file(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ int *sync_file_fd);
+
+/**
+ * Import kernel sync object from a sync_file.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [in] sync object handle
+ * \param sync_file_fd - \c [in] sync_file file descriptor.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_import_sync_file(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ int sync_file_fd);
+/**
+ * Export kernel timeline sync object to a sync_file.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [in] sync object handle
+ * \param point - \c [in] timeline point
+ * \param flags - \c [in] flags
+ * \param sync_file_fd - \c [out] sync_file file descriptor.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_export_sync_file2(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ uint64_t point,
+ uint32_t flags,
+ int *sync_file_fd);
+
+/**
+ * Import kernel timeline sync object from a sync_file.
+ *
+ * \param dev - \c [in] device handle
+ * \param syncobj - \c [in] sync object handle
+ * \param point - \c [in] timeline point
+ * \param sync_file_fd - \c [in] sync_file file descriptor.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_import_sync_file2(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ uint64_t point,
+ int sync_file_fd);
+
+/**
+ * transfer between syncbojs.
+ *
+ * \param dev - \c [in] device handle
+ * \param dst_handle - \c [in] sync object handle
+ * \param dst_point - \c [in] timeline point, 0 presents dst is binary
+ * \param src_handle - \c [in] sync object handle
+ * \param src_point - \c [in] timeline point, 0 presents src is binary
+ * \param flags - \c [in] flags
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_transfer(amdgpu_device_handle dev,
+ uint32_t dst_handle,
+ uint64_t dst_point,
+ uint32_t src_handle,
+ uint64_t src_point,
+ uint32_t flags);
+
+/**
+ * Export an amdgpu fence as a handle (syncobj or fd).
+ *
+ * \param what AMDGPU_FENCE_TO_HANDLE_GET_{SYNCOBJ, FD}
+ * \param out_handle returned handle
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ */
+int amdgpu_cs_fence_to_handle(amdgpu_device_handle dev,
+ struct amdgpu_cs_fence *fence,
+ uint32_t what,
+ uint32_t *out_handle);
+
+/**
+ * Submit raw command submission to kernel
+ *
+ * \param dev - \c [in] device handle
+ * \param context - \c [in] context handle for context id
+ * \param bo_list_handle - \c [in] request bo list handle (0 for none)
+ * \param num_chunks - \c [in] number of CS chunks to submit
+ * \param chunks - \c [in] array of CS chunks
+ * \param seq_no - \c [out] output sequence number for submission.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ */
+struct drm_amdgpu_cs_chunk;
+struct drm_amdgpu_cs_chunk_dep;
+struct drm_amdgpu_cs_chunk_data;
+
+int amdgpu_cs_submit_raw(amdgpu_device_handle dev,
+ amdgpu_context_handle context,
+ amdgpu_bo_list_handle bo_list_handle,
+ int num_chunks,
+ struct drm_amdgpu_cs_chunk *chunks,
+ uint64_t *seq_no);
+
+/**
+ * Submit raw command submission to the kernel with a raw BO list handle.
+ *
+ * \param dev - \c [in] device handle
+ * \param context - \c [in] context handle for context id
+ * \param bo_list_handle - \c [in] raw bo list handle (0 for none)
+ * \param num_chunks - \c [in] number of CS chunks to submit
+ * \param chunks - \c [in] array of CS chunks
+ * \param seq_no - \c [out] output sequence number for submission.
+ *
+ * \return 0 on success\n
+ * <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_create_raw(), amdgpu_bo_list_destroy_raw()
+ */
+int amdgpu_cs_submit_raw2(amdgpu_device_handle dev,
+ amdgpu_context_handle context,
+ uint32_t bo_list_handle,
+ int num_chunks,
+ struct drm_amdgpu_cs_chunk *chunks,
+ uint64_t *seq_no);
+
+void amdgpu_cs_chunk_fence_to_dep(struct amdgpu_cs_fence *fence,
+ struct drm_amdgpu_cs_chunk_dep *dep);
+void amdgpu_cs_chunk_fence_info_to_data(struct amdgpu_cs_fence_info *fence_info,
+ struct drm_amdgpu_cs_chunk_data *data);
+
+/**
+ * Reserve VMID
+ * \param context - \c [in] GPU Context
+ * \param flags - \c [in] TBD
+ *
+ * \return 0 on success otherwise POSIX Error code
+*/
+int amdgpu_vm_reserve_vmid(amdgpu_device_handle dev, uint32_t flags);
+
+/**
+ * Free reserved VMID
+ * \param context - \c [in] GPU Context
+ * \param flags - \c [in] TBD
+ *
+ * \return 0 on success otherwise POSIX Error code
+*/
+int amdgpu_vm_unreserve_vmid(amdgpu_device_handle dev, uint32_t flags);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* #ifdef _AMDGPU_H_ */
diff --git a/amdgpu/amdgpu_asic_id.c b/amdgpu/amdgpu_asic_id.c
new file mode 100644
index 0000000..a5007ff
--- /dev/null
+++ b/amdgpu/amdgpu_asic_id.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2017 Advanced Micro Devices, Inc.
+ * 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
+ * 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.
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+
+static int parse_one_line(struct amdgpu_device *dev, const char *line)
+{
+ char *buf, *saveptr;
+ char *s_did;
+ uint32_t did;
+ char *s_rid;
+ uint32_t rid;
+ char *s_name;
+ char *endptr;
+ int r = -EINVAL;
+
+ /* ignore empty line and commented line */
+ if (strlen(line) == 0 || line[0] == '#')
+ return -EAGAIN;
+
+ buf = strdup(line);
+ if (!buf)
+ return -ENOMEM;
+
+ /* device id */
+ s_did = strtok_r(buf, ",", &saveptr);
+ if (!s_did)
+ goto out;
+
+ did = strtol(s_did, &endptr, 16);
+ if (*endptr)
+ goto out;
+
+ if (did != dev->info.asic_id) {
+ r = -EAGAIN;
+ goto out;
+ }
+
+ /* revision id */
+ s_rid = strtok_r(NULL, ",", &saveptr);
+ if (!s_rid)
+ goto out;
+
+ rid = strtol(s_rid, &endptr, 16);
+ if (*endptr)
+ goto out;
+
+ if (rid != dev->info.pci_rev_id) {
+ r = -EAGAIN;
+ goto out;
+ }
+
+ /* marketing name */
+ s_name = strtok_r(NULL, ",", &saveptr);
+ if (!s_name)
+ goto out;
+
+ /* trim leading whitespaces or tabs */
+ while (isblank(*s_name))
+ s_name++;
+ if (strlen(s_name) == 0)
+ goto out;
+
+ dev->marketing_name = strdup(s_name);
+ if (dev->marketing_name)
+ r = 0;
+ else
+ r = -ENOMEM;
+
+out:
+ free(buf);
+
+ return r;
+}
+
+void amdgpu_parse_asic_ids(struct amdgpu_device *dev)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t n;
+ int line_num = 1;
+ int r = 0;
+
+ fp = fopen(AMDGPU_ASIC_ID_TABLE, "r");
+ if (!fp) {
+ fprintf(stderr, "%s: %s\n", AMDGPU_ASIC_ID_TABLE,
+ strerror(errno));
+ return;
+ }
+
+ /* 1st valid line is file version */
+ while ((n = getline(&line, &len, fp)) != -1) {
+ /* trim trailing newline */
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+
+ /* ignore empty line and commented line */
+ if (strlen(line) == 0 || line[0] == '#') {
+ line_num++;
+ continue;
+ }
+
+ drmMsg("%s version: %s\n", AMDGPU_ASIC_ID_TABLE, line);
+ break;
+ }
+
+ while ((n = getline(&line, &len, fp)) != -1) {
+ /* trim trailing newline */
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+
+ r = parse_one_line(dev, line);
+ if (r != -EAGAIN)
+ break;
+
+ line_num++;
+ }
+
+ if (r == -EINVAL) {
+ fprintf(stderr, "Invalid format: %s: line %d: %s\n",
+ AMDGPU_ASIC_ID_TABLE, line_num, line);
+ } else if (r && r != -EAGAIN) {
+ fprintf(stderr, "%s: Cannot parse ASIC IDs: %s\n",
+ __func__, strerror(-r));
+ }
+
+ free(line);
+ fclose(fp);
+}
diff --git a/amdgpu/amdgpu_bo.c b/amdgpu/amdgpu_bo.c
new file mode 100644
index 0000000..5bdb8fe
--- /dev/null
+++ b/amdgpu/amdgpu_bo.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, Inc.
+ * 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
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_math.h"
+
+static int amdgpu_close_kms_handle(int fd, uint32_t handle)
+{
+ struct drm_gem_close args = {};
+
+ args.handle = handle;
+ return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &args);
+}
+
+static int amdgpu_bo_create(amdgpu_device_handle dev,
+ uint64_t size,
+ uint32_t handle,
+ amdgpu_bo_handle *buf_handle)
+{
+ struct amdgpu_bo *bo;
+ int r;
+
+ bo = calloc(1, sizeof(struct amdgpu_bo));
+ if (!bo)
+ return -ENOMEM;
+
+ r = handle_table_insert(&dev->bo_handles, handle, bo);
+ if (r) {
+ free(bo);
+ return r;
+ }
+
+ atomic_set(&bo->refcount, 1);
+ bo->dev = dev;
+ bo->alloc_size = size;
+ bo->handle = handle;
+ pthread_mutex_init(&bo->cpu_access_mutex, NULL);
+
+ *buf_handle = bo;
+ return 0;
+}
+
+drm_public int amdgpu_bo_alloc(amdgpu_device_handle dev,
+ struct amdgpu_bo_alloc_request *alloc_buffer,
+ amdgpu_bo_handle *buf_handle)
+{
+ union drm_amdgpu_gem_create args;
+ int r;
+
+ memset(&args, 0, sizeof(args));
+ args.in.bo_size = alloc_buffer->alloc_size;
+ args.in.alignment = alloc_buffer->phys_alignment;
+
+ /* Set the placement. */
+ args.in.domains = alloc_buffer->preferred_heap;
+ args.in.domain_flags = alloc_buffer->flags;
+
+ /* Allocate the buffer with the preferred heap. */
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_CREATE,
+ &args, sizeof(args));
+ if (r)
+ goto out;
+
+ pthread_mutex_lock(&dev->bo_table_mutex);
+ r = amdgpu_bo_create(dev, alloc_buffer->alloc_size, args.out.handle,
+ buf_handle);
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+ if (r) {
+ amdgpu_close_kms_handle(dev->fd, args.out.handle);
+ }
+
+out:
+ return r;
+}
+
+drm_public int amdgpu_bo_set_metadata(amdgpu_bo_handle bo,
+ struct amdgpu_bo_metadata *info)
+{
+ struct drm_amdgpu_gem_metadata args = {};
+
+ args.handle = bo->handle;
+ args.op = AMDGPU_GEM_METADATA_OP_SET_METADATA;
+ args.data.flags = info->flags;
+ args.data.tiling_info = info->tiling_info;
+
+ if (info->size_metadata > sizeof(args.data.data))
+ return -EINVAL;
+
+ if (info->size_metadata) {
+ args.data.data_size_bytes = info->size_metadata;
+ memcpy(args.data.data, info->umd_metadata, info->size_metadata);
+ }
+
+ return drmCommandWriteRead(bo->dev->fd,
+ DRM_AMDGPU_GEM_METADATA,
+ &args, sizeof(args));
+}
+
+drm_public int amdgpu_bo_query_info(amdgpu_bo_handle bo,
+ struct amdgpu_bo_info *info)
+{
+ struct drm_amdgpu_gem_metadata metadata = {};
+ struct drm_amdgpu_gem_create_in bo_info = {};
+ struct drm_amdgpu_gem_op gem_op = {};
+ int r;
+
+ /* Validate the BO passed in */
+ if (!bo->handle)
+ return -EINVAL;
+
+ /* Query metadata. */
+ metadata.handle = bo->handle;
+ metadata.op = AMDGPU_GEM_METADATA_OP_GET_METADATA;
+
+ r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_METADATA,
+ &metadata, sizeof(metadata));
+ if (r)
+ return r;
+
+ if (metadata.data.data_size_bytes >
+ sizeof(info->metadata.umd_metadata))
+ return -EINVAL;
+
+ /* Query buffer info. */
+ gem_op.handle = bo->handle;
+ gem_op.op = AMDGPU_GEM_OP_GET_GEM_CREATE_INFO;
+ gem_op.value = (uintptr_t)&bo_info;
+
+ r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_OP,
+ &gem_op, sizeof(gem_op));
+ if (r)
+ return r;
+
+ memset(info, 0, sizeof(*info));
+ info->alloc_size = bo_info.bo_size;
+ info->phys_alignment = bo_info.alignment;
+ info->preferred_heap = bo_info.domains;
+ info->alloc_flags = bo_info.domain_flags;
+ info->metadata.flags = metadata.data.flags;
+ info->metadata.tiling_info = metadata.data.tiling_info;
+
+ info->metadata.size_metadata = metadata.data.data_size_bytes;
+ if (metadata.data.data_size_bytes > 0)
+ memcpy(info->metadata.umd_metadata, metadata.data.data,
+ metadata.data.data_size_bytes);
+
+ return 0;
+}
+
+static int amdgpu_bo_export_flink(amdgpu_bo_handle bo)
+{
+ struct drm_gem_flink flink;
+ int fd, dma_fd;
+ uint32_t handle;
+ int r;
+
+ fd = bo->dev->fd;
+ handle = bo->handle;
+ if (bo->flink_name)
+ return 0;
+
+
+ if (bo->dev->flink_fd != bo->dev->fd) {
+ r = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+ &dma_fd);
+ if (!r) {
+ r = drmPrimeFDToHandle(bo->dev->flink_fd, dma_fd, &handle);
+ close(dma_fd);
+ }
+ if (r)
+ return r;
+ fd = bo->dev->flink_fd;
+ }
+ memset(&flink, 0, sizeof(flink));
+ flink.handle = handle;
+
+ r = drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink);
+ if (r)
+ return r;
+
+ bo->flink_name = flink.name;
+
+ if (bo->dev->flink_fd != bo->dev->fd)
+ amdgpu_close_kms_handle(bo->dev->flink_fd, handle);
+
+ pthread_mutex_lock(&bo->dev->bo_table_mutex);
+ r = handle_table_insert(&bo->dev->bo_flink_names, bo->flink_name, bo);
+ pthread_mutex_unlock(&bo->dev->bo_table_mutex);
+
+ return r;
+}
+
+drm_public int amdgpu_bo_export(amdgpu_bo_handle bo,
+ enum amdgpu_bo_handle_type type,
+ uint32_t *shared_handle)
+{
+ int r;
+
+ switch (type) {
+ case amdgpu_bo_handle_type_gem_flink_name:
+ r = amdgpu_bo_export_flink(bo);
+ if (r)
+ return r;
+
+ *shared_handle = bo->flink_name;
+ return 0;
+
+ case amdgpu_bo_handle_type_kms:
+ case amdgpu_bo_handle_type_kms_noimport:
+ *shared_handle = bo->handle;
+ return 0;
+
+ case amdgpu_bo_handle_type_dma_buf_fd:
+ return drmPrimeHandleToFD(bo->dev->fd, bo->handle,
+ DRM_CLOEXEC | DRM_RDWR,
+ (int*)shared_handle);
+ }
+ return -EINVAL;
+}
+
+drm_public int amdgpu_bo_import(amdgpu_device_handle dev,
+ enum amdgpu_bo_handle_type type,
+ uint32_t shared_handle,
+ struct amdgpu_bo_import_result *output)
+{
+ struct drm_gem_open open_arg = {};
+ struct amdgpu_bo *bo = NULL;
+ uint32_t handle = 0, flink_name = 0;
+ uint64_t alloc_size = 0;
+ int r = 0;
+ int dma_fd;
+ uint64_t dma_buf_size = 0;
+
+ /* We must maintain a list of pairs <handle, bo>, so that we always
+ * return the same amdgpu_bo instance for the same handle. */
+ pthread_mutex_lock(&dev->bo_table_mutex);
+
+ /* Convert a DMA buf handle to a KMS handle now. */
+ if (type == amdgpu_bo_handle_type_dma_buf_fd) {
+ off_t size;
+
+ /* Get a KMS handle. */
+ r = drmPrimeFDToHandle(dev->fd, shared_handle, &handle);
+ if (r)
+ goto unlock;
+
+ /* Query the buffer size. */
+ size = lseek(shared_handle, 0, SEEK_END);
+ if (size == (off_t)-1) {
+ r = -errno;
+ goto free_bo_handle;
+ }
+ lseek(shared_handle, 0, SEEK_SET);
+
+ dma_buf_size = size;
+ shared_handle = handle;
+ }
+
+ /* If we have already created a buffer with this handle, find it. */
+ switch (type) {
+ case amdgpu_bo_handle_type_gem_flink_name:
+ bo = handle_table_lookup(&dev->bo_flink_names, shared_handle);
+ break;
+
+ case amdgpu_bo_handle_type_dma_buf_fd:
+ bo = handle_table_lookup(&dev->bo_handles, shared_handle);
+ break;
+
+ case amdgpu_bo_handle_type_kms:
+ case amdgpu_bo_handle_type_kms_noimport:
+ /* Importing a KMS handle in not allowed. */
+ r = -EPERM;
+ goto unlock;
+
+ default:
+ r = -EINVAL;
+ goto unlock;
+ }
+
+ if (bo) {
+ /* The buffer already exists, just bump the refcount. */
+ atomic_inc(&bo->refcount);
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+
+ output->buf_handle = bo;
+ output->alloc_size = bo->alloc_size;
+ return 0;
+ }
+
+ /* Open the handle. */
+ switch (type) {
+ case amdgpu_bo_handle_type_gem_flink_name:
+ open_arg.name = shared_handle;
+ r = drmIoctl(dev->flink_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
+ if (r)
+ goto unlock;
+
+ flink_name = shared_handle;
+ handle = open_arg.handle;
+ alloc_size = open_arg.size;
+ if (dev->flink_fd != dev->fd) {
+ r = drmPrimeHandleToFD(dev->flink_fd, handle,
+ DRM_CLOEXEC, &dma_fd);
+ if (r)
+ goto free_bo_handle;
+ r = drmPrimeFDToHandle(dev->fd, dma_fd, &handle);
+ close(dma_fd);
+ if (r)
+ goto free_bo_handle;
+ r = amdgpu_close_kms_handle(dev->flink_fd,
+ open_arg.handle);
+ if (r)
+ goto free_bo_handle;
+ }
+ open_arg.handle = 0;
+ break;
+
+ case amdgpu_bo_handle_type_dma_buf_fd:
+ handle = shared_handle;
+ alloc_size = dma_buf_size;
+ break;
+
+ case amdgpu_bo_handle_type_kms:
+ case amdgpu_bo_handle_type_kms_noimport:
+ assert(0); /* unreachable */
+ }
+
+ /* Initialize it. */
+ r = amdgpu_bo_create(dev, alloc_size, handle, &bo);
+ if (r)
+ goto free_bo_handle;
+
+ if (flink_name) {
+ bo->flink_name = flink_name;
+ r = handle_table_insert(&dev->bo_flink_names, flink_name,
+ bo);
+ if (r)
+ goto free_bo_handle;
+
+ }
+
+ output->buf_handle = bo;
+ output->alloc_size = bo->alloc_size;
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+ return 0;
+
+free_bo_handle:
+ if (flink_name && open_arg.handle)
+ amdgpu_close_kms_handle(dev->flink_fd, open_arg.handle);
+
+ if (bo)
+ amdgpu_bo_free(bo);
+ else
+ amdgpu_close_kms_handle(dev->fd, handle);
+unlock:
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+ return r;
+}
+
+drm_public int amdgpu_bo_free(amdgpu_bo_handle buf_handle)
+{
+ struct amdgpu_device *dev;
+ struct amdgpu_bo *bo = buf_handle;
+
+ assert(bo != NULL);
+ dev = bo->dev;
+ pthread_mutex_lock(&dev->bo_table_mutex);
+
+ if (update_references(&bo->refcount, NULL)) {
+ /* Remove the buffer from the hash tables. */
+ handle_table_remove(&dev->bo_handles, bo->handle);
+
+ if (bo->flink_name)
+ handle_table_remove(&dev->bo_flink_names,
+ bo->flink_name);
+
+ /* Release CPU access. */
+ if (bo->cpu_map_count > 0) {
+ bo->cpu_map_count = 1;
+ amdgpu_bo_cpu_unmap(bo);
+ }
+
+ amdgpu_close_kms_handle(dev->fd, bo->handle);
+ pthread_mutex_destroy(&bo->cpu_access_mutex);
+ free(bo);
+ }
+
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+
+ return 0;
+}
+
+drm_public void amdgpu_bo_inc_ref(amdgpu_bo_handle bo)
+{
+ atomic_inc(&bo->refcount);
+}
+
+drm_public int amdgpu_bo_cpu_map(amdgpu_bo_handle bo, void **cpu)
+{
+ union drm_amdgpu_gem_mmap args;
+ void *ptr;
+ int r;
+
+ pthread_mutex_lock(&bo->cpu_access_mutex);
+
+ if (bo->cpu_ptr) {
+ /* already mapped */
+ assert(bo->cpu_map_count > 0);
+ bo->cpu_map_count++;
+ *cpu = bo->cpu_ptr;
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+ return 0;
+ }
+
+ assert(bo->cpu_map_count == 0);
+
+ memset(&args, 0, sizeof(args));
+
+ /* Query the buffer address (args.addr_ptr).
+ * The kernel driver ignores the offset and size parameters. */
+ args.in.handle = bo->handle;
+
+ r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_MMAP, &args,
+ sizeof(args));
+ if (r) {
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+ return r;
+ }
+
+ /* Map the buffer. */
+ ptr = drm_mmap(NULL, bo->alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ bo->dev->fd, args.out.addr_ptr);
+ if (ptr == MAP_FAILED) {
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+ return -errno;
+ }
+
+ bo->cpu_ptr = ptr;
+ bo->cpu_map_count = 1;
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+
+ *cpu = ptr;
+ return 0;
+}
+
+drm_public int amdgpu_bo_cpu_unmap(amdgpu_bo_handle bo)
+{
+ int r;
+
+ pthread_mutex_lock(&bo->cpu_access_mutex);
+ assert(bo->cpu_map_count >= 0);
+
+ if (bo->cpu_map_count == 0) {
+ /* not mapped */
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+ return -EINVAL;
+ }
+
+ bo->cpu_map_count--;
+ if (bo->cpu_map_count > 0) {
+ /* mapped multiple times */
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+ return 0;
+ }
+
+ r = drm_munmap(bo->cpu_ptr, bo->alloc_size) == 0 ? 0 : -errno;
+ bo->cpu_ptr = NULL;
+ pthread_mutex_unlock(&bo->cpu_access_mutex);
+ return r;
+}
+
+drm_public int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev,
+ struct amdgpu_buffer_size_alignments *info)
+{
+ info->size_local = dev->dev_info.pte_fragment_size;
+ info->size_remote = dev->dev_info.gart_page_size;
+ return 0;
+}
+
+drm_public int amdgpu_bo_wait_for_idle(amdgpu_bo_handle bo,
+ uint64_t timeout_ns,
+ bool *busy)
+{
+ union drm_amdgpu_gem_wait_idle args;
+ int r;
+
+ memset(&args, 0, sizeof(args));
+ args.in.handle = bo->handle;
+ args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns);
+
+ r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_WAIT_IDLE,
+ &args, sizeof(args));
+
+ if (r == 0) {
+ *busy = args.out.status;
+ return 0;
+ } else {
+ fprintf(stderr, "amdgpu: GEM_WAIT_IDLE failed with %i\n", r);
+ return r;
+ }
+}
+
+drm_public int amdgpu_find_bo_by_cpu_mapping(amdgpu_device_handle dev,
+ void *cpu,
+ uint64_t size,
+ amdgpu_bo_handle *buf_handle,
+ uint64_t *offset_in_bo)
+{
+ struct amdgpu_bo *bo;
+ uint32_t i;
+ int r = 0;
+
+ if (cpu == NULL || size == 0)
+ return -EINVAL;
+
+ /*
+ * Workaround for a buggy application which tries to import previously
+ * exposed CPU pointers. If we find a real world use case we should
+ * improve that by asking the kernel for the right handle.
+ */
+ pthread_mutex_lock(&dev->bo_table_mutex);
+ for (i = 0; i < dev->bo_handles.max_key; i++) {
+ bo = handle_table_lookup(&dev->bo_handles, i);
+ if (!bo || !bo->cpu_ptr || size > bo->alloc_size)
+ continue;
+ if (cpu >= bo->cpu_ptr &&
+ cpu < (void*)((uintptr_t)bo->cpu_ptr + bo->alloc_size))
+ break;
+ }
+
+ if (i < dev->bo_handles.max_key) {
+ atomic_inc(&bo->refcount);
+ *buf_handle = bo;
+ *offset_in_bo = (uintptr_t)cpu - (uintptr_t)bo->cpu_ptr;
+ } else {
+ *buf_handle = NULL;
+ *offset_in_bo = 0;
+ r = -ENXIO;
+ }
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+
+ return r;
+}
+
+drm_public int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev,
+ void *cpu,
+ uint64_t size,
+ amdgpu_bo_handle *buf_handle)
+{
+ int r;
+ struct drm_amdgpu_gem_userptr args;
+
+ args.addr = (uintptr_t)cpu;
+ args.flags = AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_REGISTER |
+ AMDGPU_GEM_USERPTR_VALIDATE;
+ args.size = size;
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_USERPTR,
+ &args, sizeof(args));
+ if (r)
+ goto out;
+
+ pthread_mutex_lock(&dev->bo_table_mutex);
+ r = amdgpu_bo_create(dev, size, args.handle, buf_handle);
+ pthread_mutex_unlock(&dev->bo_table_mutex);
+ if (r) {
+ amdgpu_close_kms_handle(dev->fd, args.handle);
+ }
+
+out:
+ return r;
+}
+
+drm_public int amdgpu_bo_list_create_raw(amdgpu_device_handle dev,
+ uint32_t number_of_buffers,
+ struct drm_amdgpu_bo_list_entry *buffers,
+ uint32_t *result)
+{
+ union drm_amdgpu_bo_list args;
+ int r;
+
+ memset(&args, 0, sizeof(args));
+ args.in.operation = AMDGPU_BO_LIST_OP_CREATE;
+ args.in.bo_number = number_of_buffers;
+ args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
+ args.in.bo_info_ptr = (uint64_t)(uintptr_t)buffers;
+
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
+ &args, sizeof(args));
+ if (!r)
+ *result = args.out.list_handle;
+ return r;
+}
+
+drm_public int amdgpu_bo_list_destroy_raw(amdgpu_device_handle dev,
+ uint32_t bo_list)
+{
+ union drm_amdgpu_bo_list args;
+
+ memset(&args, 0, sizeof(args));
+ args.in.operation = AMDGPU_BO_LIST_OP_DESTROY;
+ args.in.list_handle = bo_list;
+
+ return drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
+ &args, sizeof(args));
+}
+
+drm_public int amdgpu_bo_list_create(amdgpu_device_handle dev,
+ uint32_t number_of_resources,
+ amdgpu_bo_handle *resources,
+ uint8_t *resource_prios,
+ amdgpu_bo_list_handle *result)
+{
+ struct drm_amdgpu_bo_list_entry *list;
+ union drm_amdgpu_bo_list args;
+ unsigned i;
+ int r;
+
+ if (!number_of_resources)
+ return -EINVAL;
+
+ /* overflow check for multiplication */
+ if (number_of_resources > UINT32_MAX / sizeof(struct drm_amdgpu_bo_list_entry))
+ return -EINVAL;
+
+ list = malloc(number_of_resources * sizeof(struct drm_amdgpu_bo_list_entry));
+ if (!list)
+ return -ENOMEM;
+
+ *result = malloc(sizeof(struct amdgpu_bo_list));
+ if (!*result) {
+ free(list);
+ return -ENOMEM;
+ }
+
+ memset(&args, 0, sizeof(args));
+ args.in.operation = AMDGPU_BO_LIST_OP_CREATE;
+ args.in.bo_number = number_of_resources;
+ args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
+ args.in.bo_info_ptr = (uint64_t)(uintptr_t)list;
+
+ for (i = 0; i < number_of_resources; i++) {
+ list[i].bo_handle = resources[i]->handle;
+ if (resource_prios)
+ list[i].bo_priority = resource_prios[i];
+ else
+ list[i].bo_priority = 0;
+ }
+
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
+ &args, sizeof(args));
+ free(list);
+ if (r) {
+ free(*result);
+ return r;
+ }
+
+ (*result)->dev = dev;
+ (*result)->handle = args.out.list_handle;
+ return 0;
+}
+
+drm_public int amdgpu_bo_list_destroy(amdgpu_bo_list_handle list)
+{
+ union drm_amdgpu_bo_list args;
+ int r;
+
+ memset(&args, 0, sizeof(args));
+ args.in.operation = AMDGPU_BO_LIST_OP_DESTROY;
+ args.in.list_handle = list->handle;
+
+ r = drmCommandWriteRead(list->dev->fd, DRM_AMDGPU_BO_LIST,
+ &args, sizeof(args));
+
+ if (!r)
+ free(list);
+
+ return r;
+}
+
+drm_public int amdgpu_bo_list_update(amdgpu_bo_list_handle handle,
+ uint32_t number_of_resources,
+ amdgpu_bo_handle *resources,
+ uint8_t *resource_prios)
+{
+ struct drm_amdgpu_bo_list_entry *list;
+ union drm_amdgpu_bo_list args;
+ unsigned i;
+ int r;
+
+ if (!number_of_resources)
+ return -EINVAL;
+
+ /* overflow check for multiplication */
+ if (number_of_resources > UINT32_MAX / sizeof(struct drm_amdgpu_bo_list_entry))
+ return -EINVAL;
+
+ list = malloc(number_of_resources * sizeof(struct drm_amdgpu_bo_list_entry));
+ if (!list)
+ return -ENOMEM;
+
+ args.in.operation = AMDGPU_BO_LIST_OP_UPDATE;
+ args.in.list_handle = handle->handle;
+ args.in.bo_number = number_of_resources;
+ args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
+ args.in.bo_info_ptr = (uintptr_t)list;
+
+ for (i = 0; i < number_of_resources; i++) {
+ list[i].bo_handle = resources[i]->handle;
+ if (resource_prios)
+ list[i].bo_priority = resource_prios[i];
+ else
+ list[i].bo_priority = 0;
+ }
+
+ r = drmCommandWriteRead(handle->dev->fd, DRM_AMDGPU_BO_LIST,
+ &args, sizeof(args));
+ free(list);
+ return r;
+}
+
+drm_public int amdgpu_bo_va_op(amdgpu_bo_handle bo,
+ uint64_t offset,
+ uint64_t size,
+ uint64_t addr,
+ uint64_t flags,
+ uint32_t ops)
+{
+ amdgpu_device_handle dev = bo->dev;
+
+ size = ALIGN(size, getpagesize());
+
+ return amdgpu_bo_va_op_raw(dev, bo, offset, size, addr,
+ AMDGPU_VM_PAGE_READABLE |
+ AMDGPU_VM_PAGE_WRITEABLE |
+ AMDGPU_VM_PAGE_EXECUTABLE, ops);
+}
+
+drm_public int amdgpu_bo_va_op_raw(amdgpu_device_handle dev,
+ amdgpu_bo_handle bo,
+ uint64_t offset,
+ uint64_t size,
+ uint64_t addr,
+ uint64_t flags,
+ uint32_t ops)
+{
+ struct drm_amdgpu_gem_va va;
+ int r;
+
+ if (ops != AMDGPU_VA_OP_MAP && ops != AMDGPU_VA_OP_UNMAP &&
+ ops != AMDGPU_VA_OP_REPLACE && ops != AMDGPU_VA_OP_CLEAR)
+ return -EINVAL;
+
+ memset(&va, 0, sizeof(va));
+ va.handle = bo ? bo->handle : 0;
+ va.operation = ops;
+ va.flags = flags;
+ va.va_address = addr;
+ va.offset_in_bo = offset;
+ va.map_size = size;
+
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va));
+
+ return r;
+}
diff --git a/amdgpu/amdgpu_cs.c b/amdgpu/amdgpu_cs.c
new file mode 100644
index 0000000..fad484b
--- /dev/null
+++ b/amdgpu/amdgpu_cs.c
@@ -0,0 +1,950 @@
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/ioctl.h>
+#if HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+
+static int amdgpu_cs_unreference_sem(amdgpu_semaphore_handle sem);
+static int amdgpu_cs_reset_sem(amdgpu_semaphore_handle sem);
+
+/**
+ * Create command submission context
+ *
+ * \param dev - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param priority - \c [in] Context creation flags. See AMDGPU_CTX_PRIORITY_*
+ * \param context - \c [out] GPU Context handle
+ *
+ * \return 0 on success otherwise POSIX Error code
+*/
+drm_public int amdgpu_cs_ctx_create2(amdgpu_device_handle dev,
+ uint32_t priority,
+ amdgpu_context_handle *context)
+{
+ struct amdgpu_context *gpu_context;
+ union drm_amdgpu_ctx args;
+ int i, j, k;
+ int r;
+
+ if (!dev || !context)
+ return -EINVAL;
+
+ gpu_context = calloc(1, sizeof(struct amdgpu_context));
+ if (!gpu_context)
+ return -ENOMEM;
+
+ gpu_context->dev = dev;
+
+ r = pthread_mutex_init(&gpu_context->sequence_mutex, NULL);
+ if (r)
+ goto error;
+
+ /* Create the context */
+ memset(&args, 0, sizeof(args));
+ args.in.op = AMDGPU_CTX_OP_ALLOC_CTX;
+ args.in.priority = priority;
+
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CTX, &args, sizeof(args));
+ if (r)
+ goto error;
+
+ gpu_context->id = args.out.alloc.ctx_id;
+ for (i = 0; i < AMDGPU_HW_IP_NUM; i++)
+ for (j = 0; j < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; j++)
+ for (k = 0; k < AMDGPU_CS_MAX_RINGS; k++)
+ list_inithead(&gpu_context->sem_list[i][j][k]);
+ *context = (amdgpu_context_handle)gpu_context;
+
+ return 0;
+
+error:
+ pthread_mutex_destroy(&gpu_context->sequence_mutex);
+ free(gpu_context);
+ return r;
+}
+
+drm_public int amdgpu_cs_ctx_create(amdgpu_device_handle dev,
+ amdgpu_context_handle *context)
+{
+ return amdgpu_cs_ctx_create2(dev, AMDGPU_CTX_PRIORITY_NORMAL, context);
+}
+
+/**
+ * Release command submission context
+ *
+ * \param dev - \c [in] amdgpu device handle
+ * \param context - \c [in] amdgpu context handle
+ *
+ * \return 0 on success otherwise POSIX Error code
+*/
+drm_public int amdgpu_cs_ctx_free(amdgpu_context_handle context)
+{
+ union drm_amdgpu_ctx args;
+ int i, j, k;
+ int r;
+
+ if (!context)
+ return -EINVAL;
+
+ pthread_mutex_destroy(&context->sequence_mutex);
+
+ /* now deal with kernel side */
+ memset(&args, 0, sizeof(args));
+ args.in.op = AMDGPU_CTX_OP_FREE_CTX;
+ args.in.ctx_id = context->id;
+ r = drmCommandWriteRead(context->dev->fd, DRM_AMDGPU_CTX,
+ &args, sizeof(args));
+ for (i = 0; i < AMDGPU_HW_IP_NUM; i++) {
+ for (j = 0; j < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; j++) {
+ for (k = 0; k < AMDGPU_CS_MAX_RINGS; k++) {
+ amdgpu_semaphore_handle sem;
+ LIST_FOR_EACH_ENTRY(sem, &context->sem_list[i][j][k], list) {
+ list_del(&sem->list);
+ amdgpu_cs_reset_sem(sem);
+ amdgpu_cs_unreference_sem(sem);
+ }
+ }
+ }
+ }
+ free(context);
+
+ return r;
+}
+
+drm_public int amdgpu_cs_ctx_override_priority(amdgpu_device_handle dev,
+ amdgpu_context_handle context,
+ int master_fd,
+ unsigned priority)
+{
+ union drm_amdgpu_sched args;
+ int r;
+
+ if (!dev || !context || master_fd < 0)
+ return -EINVAL;
+
+ memset(&args, 0, sizeof(args));
+
+ args.in.op = AMDGPU_SCHED_OP_CONTEXT_PRIORITY_OVERRIDE;
+ args.in.fd = dev->fd;
+ args.in.priority = priority;
+ args.in.ctx_id = context->id;
+
+ r = drmCommandWrite(master_fd, DRM_AMDGPU_SCHED, &args, sizeof(args));
+ if (r)
+ return r;
+
+ return 0;
+}
+
+drm_public int amdgpu_cs_query_reset_state(amdgpu_context_handle context,
+ uint32_t *state, uint32_t *hangs)
+{
+ union drm_amdgpu_ctx args;
+ int r;
+
+ if (!context)
+ return -EINVAL;
+
+ memset(&args, 0, sizeof(args));
+ args.in.op = AMDGPU_CTX_OP_QUERY_STATE;
+ args.in.ctx_id = context->id;
+ r = drmCommandWriteRead(context->dev->fd, DRM_AMDGPU_CTX,
+ &args, sizeof(args));
+ if (!r) {
+ *state = args.out.state.reset_status;
+ *hangs = args.out.state.hangs;
+ }
+ return r;
+}
+
+drm_public int amdgpu_cs_query_reset_state2(amdgpu_context_handle context,
+ uint64_t *flags)
+{
+ union drm_amdgpu_ctx args;
+ int r;
+
+ if (!context)
+ return -EINVAL;
+
+ memset(&args, 0, sizeof(args));
+ args.in.op = AMDGPU_CTX_OP_QUERY_STATE2;
+ args.in.ctx_id = context->id;
+ r = drmCommandWriteRead(context->dev->fd, DRM_AMDGPU_CTX,
+ &args, sizeof(args));
+ if (!r)
+ *flags = args.out.state.flags;
+ return r;
+}
+
+/**
+ * Submit command to kernel DRM
+ * \param dev - \c [in] Device handle
+ * \param context - \c [in] GPU Context
+ * \param ibs_request - \c [in] Pointer to submission requests
+ * \param fence - \c [out] return fence for this submission
+ *
+ * \return 0 on success otherwise POSIX Error code
+ * \sa amdgpu_cs_submit()
+*/
+static int amdgpu_cs_submit_one(amdgpu_context_handle context,
+ struct amdgpu_cs_request *ibs_request)
+{
+ struct drm_amdgpu_cs_chunk *chunks;
+ struct drm_amdgpu_cs_chunk_data *chunk_data;
+ struct drm_amdgpu_cs_chunk_dep *dependencies = NULL;
+ struct drm_amdgpu_cs_chunk_dep *sem_dependencies = NULL;
+ amdgpu_device_handle dev = context->dev;
+ struct list_head *sem_list;
+ amdgpu_semaphore_handle sem, tmp;
+ uint32_t i, size, num_chunks, bo_list_handle = 0, sem_count = 0;
+ uint64_t seq_no;
+ bool user_fence;
+ int r = 0;
+
+ if (ibs_request->ip_type >= AMDGPU_HW_IP_NUM)
+ return -EINVAL;
+ if (ibs_request->ring >= AMDGPU_CS_MAX_RINGS)
+ return -EINVAL;
+ if (ibs_request->number_of_ibs == 0) {
+ ibs_request->seq_no = AMDGPU_NULL_SUBMIT_SEQ;
+ return 0;
+ }
+ user_fence = (ibs_request->fence_info.handle != NULL);
+
+ size = ibs_request->number_of_ibs + (user_fence ? 2 : 1) + 1;
+
+ chunks = alloca(sizeof(struct drm_amdgpu_cs_chunk) * size);
+
+ size = ibs_request->number_of_ibs + (user_fence ? 1 : 0);
+
+ chunk_data = alloca(sizeof(struct drm_amdgpu_cs_chunk_data) * size);
+
+ if (ibs_request->resources)
+ bo_list_handle = ibs_request->resources->handle;
+ num_chunks = ibs_request->number_of_ibs;
+ /* IB chunks */
+ for (i = 0; i < ibs_request->number_of_ibs; i++) {
+ struct amdgpu_cs_ib_info *ib;
+ chunks[i].chunk_id = AMDGPU_CHUNK_ID_IB;
+ chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_ib) / 4;
+ chunks[i].chunk_data = (uint64_t)(uintptr_t)&chunk_data[i];
+
+ ib = &ibs_request->ibs[i];
+
+ chunk_data[i].ib_data._pad = 0;
+ chunk_data[i].ib_data.va_start = ib->ib_mc_address;
+ chunk_data[i].ib_data.ib_bytes = ib->size * 4;
+ chunk_data[i].ib_data.ip_type = ibs_request->ip_type;
+ chunk_data[i].ib_data.ip_instance = ibs_request->ip_instance;
+ chunk_data[i].ib_data.ring = ibs_request->ring;
+ chunk_data[i].ib_data.flags = ib->flags;
+ }
+
+ pthread_mutex_lock(&context->sequence_mutex);
+
+ if (user_fence) {
+ i = num_chunks++;
+
+ /* fence chunk */
+ chunks[i].chunk_id = AMDGPU_CHUNK_ID_FENCE;
+ chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_fence) / 4;
+ chunks[i].chunk_data = (uint64_t)(uintptr_t)&chunk_data[i];
+
+ /* fence bo handle */
+ chunk_data[i].fence_data.handle = ibs_request->fence_info.handle->handle;
+ /* offset */
+ chunk_data[i].fence_data.offset =
+ ibs_request->fence_info.offset * sizeof(uint64_t);
+ }
+
+ if (ibs_request->number_of_dependencies) {
+ dependencies = alloca(sizeof(struct drm_amdgpu_cs_chunk_dep) *
+ ibs_request->number_of_dependencies);
+ if (!dependencies) {
+ r = -ENOMEM;
+ goto error_unlock;
+ }
+
+ for (i = 0; i < ibs_request->number_of_dependencies; ++i) {
+ struct amdgpu_cs_fence *info = &ibs_request->dependencies[i];
+ struct drm_amdgpu_cs_chunk_dep *dep = &dependencies[i];
+ dep->ip_type = info->ip_type;
+ dep->ip_instance = info->ip_instance;
+ dep->ring = info->ring;
+ dep->ctx_id = info->context->id;
+ dep->handle = info->fence;
+ }
+
+ i = num_chunks++;
+
+ /* dependencies chunk */
+ chunks[i].chunk_id = AMDGPU_CHUNK_ID_DEPENDENCIES;
+ chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_dep) / 4
+ * ibs_request->number_of_dependencies;
+ chunks[i].chunk_data = (uint64_t)(uintptr_t)dependencies;
+ }
+
+ sem_list = &context->sem_list[ibs_request->ip_type][ibs_request->ip_instance][ibs_request->ring];
+ LIST_FOR_EACH_ENTRY(sem, sem_list, list)
+ sem_count++;
+ if (sem_count) {
+ sem_dependencies = alloca(sizeof(struct drm_amdgpu_cs_chunk_dep) * sem_count);
+ if (!sem_dependencies) {
+ r = -ENOMEM;
+ goto error_unlock;
+ }
+ sem_count = 0;
+ LIST_FOR_EACH_ENTRY_SAFE(sem, tmp, sem_list, list) {
+ struct amdgpu_cs_fence *info = &sem->signal_fence;
+ struct drm_amdgpu_cs_chunk_dep *dep = &sem_dependencies[sem_count++];
+ dep->ip_type = info->ip_type;
+ dep->ip_instance = info->ip_instance;
+ dep->ring = info->ring;
+ dep->ctx_id = info->context->id;
+ dep->handle = info->fence;
+
+ list_del(&sem->list);
+ amdgpu_cs_reset_sem(sem);
+ amdgpu_cs_unreference_sem(sem);
+ }
+ i = num_chunks++;
+
+ /* dependencies chunk */
+ chunks[i].chunk_id = AMDGPU_CHUNK_ID_DEPENDENCIES;
+ chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_dep) / 4 * sem_count;
+ chunks[i].chunk_data = (uint64_t)(uintptr_t)sem_dependencies;
+ }
+
+ r = amdgpu_cs_submit_raw2(dev, context, bo_list_handle, num_chunks,
+ chunks, &seq_no);
+ if (r)
+ goto error_unlock;
+
+ ibs_request->seq_no = seq_no;
+ context->last_seq[ibs_request->ip_type][ibs_request->ip_instance][ibs_request->ring] = ibs_request->seq_no;
+error_unlock:
+ pthread_mutex_unlock(&context->sequence_mutex);
+ return r;
+}
+
+drm_public int amdgpu_cs_submit(amdgpu_context_handle context,
+ uint64_t flags,
+ struct amdgpu_cs_request *ibs_request,
+ uint32_t number_of_requests)
+{
+ uint32_t i;
+ int r;
+
+ if (!context || !ibs_request)
+ return -EINVAL;
+
+ r = 0;
+ for (i = 0; i < number_of_requests; i++) {
+ r = amdgpu_cs_submit_one(context, ibs_request);
+ if (r)
+ break;
+ ibs_request++;
+ }
+
+ return r;
+}
+
+/**
+ * Calculate absolute timeout.
+ *
+ * \param timeout - \c [in] timeout in nanoseconds.
+ *
+ * \return absolute timeout in nanoseconds
+*/
+drm_private uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout)
+{
+ int r;
+
+ if (timeout != AMDGPU_TIMEOUT_INFINITE) {
+ struct timespec current;
+ uint64_t current_ns;
+ r = clock_gettime(CLOCK_MONOTONIC, &current);
+ if (r) {
+ fprintf(stderr, "clock_gettime() returned error (%d)!", errno);
+ return AMDGPU_TIMEOUT_INFINITE;
+ }
+
+ current_ns = ((uint64_t)current.tv_sec) * 1000000000ull;
+ current_ns += current.tv_nsec;
+ timeout += current_ns;
+ if (timeout < current_ns)
+ timeout = AMDGPU_TIMEOUT_INFINITE;
+ }
+ return timeout;
+}
+
+static int amdgpu_ioctl_wait_cs(amdgpu_context_handle context,
+ unsigned ip,
+ unsigned ip_instance,
+ uint32_t ring,
+ uint64_t handle,
+ uint64_t timeout_ns,
+ uint64_t flags,
+ bool *busy)
+{
+ amdgpu_device_handle dev = context->dev;
+ union drm_amdgpu_wait_cs args;
+ int r;
+
+ memset(&args, 0, sizeof(args));
+ args.in.handle = handle;
+ args.in.ip_type = ip;
+ args.in.ip_instance = ip_instance;
+ args.in.ring = ring;
+ args.in.ctx_id = context->id;
+
+ if (flags & AMDGPU_QUERY_FENCE_TIMEOUT_IS_ABSOLUTE)
+ args.in.timeout = timeout_ns;
+ else
+ args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns);
+
+ r = drmIoctl(dev->fd, DRM_IOCTL_AMDGPU_WAIT_CS, &args);
+ if (r)
+ return -errno;
+
+ *busy = args.out.status;
+ return 0;
+}
+
+drm_public int amdgpu_cs_query_fence_status(struct amdgpu_cs_fence *fence,
+ uint64_t timeout_ns,
+ uint64_t flags,
+ uint32_t *expired)
+{
+ bool busy = true;
+ int r;
+
+ if (!fence || !expired || !fence->context)
+ return -EINVAL;
+ if (fence->ip_type >= AMDGPU_HW_IP_NUM)
+ return -EINVAL;
+ if (fence->ring >= AMDGPU_CS_MAX_RINGS)
+ return -EINVAL;
+ if (fence->fence == AMDGPU_NULL_SUBMIT_SEQ) {
+ *expired = true;
+ return 0;
+ }
+
+ *expired = false;
+
+ r = amdgpu_ioctl_wait_cs(fence->context, fence->ip_type,
+ fence->ip_instance, fence->ring,
+ fence->fence, timeout_ns, flags, &busy);
+
+ if (!r && !busy)
+ *expired = true;
+
+ return r;
+}
+
+static int amdgpu_ioctl_wait_fences(struct amdgpu_cs_fence *fences,
+ uint32_t fence_count,
+ bool wait_all,
+ uint64_t timeout_ns,
+ uint32_t *status,
+ uint32_t *first)
+{
+ struct drm_amdgpu_fence *drm_fences;
+ amdgpu_device_handle dev = fences[0].context->dev;
+ union drm_amdgpu_wait_fences args;
+ int r;
+ uint32_t i;
+
+ drm_fences = alloca(sizeof(struct drm_amdgpu_fence) * fence_count);
+ for (i = 0; i < fence_count; i++) {
+ drm_fences[i].ctx_id = fences[i].context->id;
+ drm_fences[i].ip_type = fences[i].ip_type;
+ drm_fences[i].ip_instance = fences[i].ip_instance;
+ drm_fences[i].ring = fences[i].ring;
+ drm_fences[i].seq_no = fences[i].fence;
+ }
+
+ memset(&args, 0, sizeof(args));
+ args.in.fences = (uint64_t)(uintptr_t)drm_fences;
+ args.in.fence_count = fence_count;
+ args.in.wait_all = wait_all;
+ args.in.timeout_ns = amdgpu_cs_calculate_timeout(timeout_ns);
+
+ r = drmIoctl(dev->fd, DRM_IOCTL_AMDGPU_WAIT_FENCES, &args);
+ if (r)
+ return -errno;
+
+ *status = args.out.status;
+
+ if (first)
+ *first = args.out.first_signaled;
+
+ return 0;
+}
+
+drm_public int amdgpu_cs_wait_fences(struct amdgpu_cs_fence *fences,
+ uint32_t fence_count,
+ bool wait_all,
+ uint64_t timeout_ns,
+ uint32_t *status,
+ uint32_t *first)
+{
+ uint32_t i;
+
+ /* Sanity check */
+ if (!fences || !status || !fence_count)
+ return -EINVAL;
+
+ for (i = 0; i < fence_count; i++) {
+ if (NULL == fences[i].context)
+ return -EINVAL;
+ if (fences[i].ip_type >= AMDGPU_HW_IP_NUM)
+ return -EINVAL;
+ if (fences[i].ring >= AMDGPU_CS_MAX_RINGS)
+ return -EINVAL;
+ }
+
+ *status = 0;
+
+ return amdgpu_ioctl_wait_fences(fences, fence_count, wait_all,
+ timeout_ns, status, first);
+}
+
+drm_public int amdgpu_cs_create_semaphore(amdgpu_semaphore_handle *sem)
+{
+ struct amdgpu_semaphore *gpu_semaphore;
+
+ if (!sem)
+ return -EINVAL;
+
+ gpu_semaphore = calloc(1, sizeof(struct amdgpu_semaphore));
+ if (!gpu_semaphore)
+ return -ENOMEM;
+
+ atomic_set(&gpu_semaphore->refcount, 1);
+ *sem = gpu_semaphore;
+
+ return 0;
+}
+
+drm_public int amdgpu_cs_signal_semaphore(amdgpu_context_handle ctx,
+ uint32_t ip_type,
+ uint32_t ip_instance,
+ uint32_t ring,
+ amdgpu_semaphore_handle sem)
+{
+ if (!ctx || !sem)
+ return -EINVAL;
+ if (ip_type >= AMDGPU_HW_IP_NUM)
+ return -EINVAL;
+ if (ring >= AMDGPU_CS_MAX_RINGS)
+ return -EINVAL;
+ /* sem has been signaled */
+ if (sem->signal_fence.context)
+ return -EINVAL;
+ pthread_mutex_lock(&ctx->sequence_mutex);
+ sem->signal_fence.context = ctx;
+ sem->signal_fence.ip_type = ip_type;
+ sem->signal_fence.ip_instance = ip_instance;
+ sem->signal_fence.ring = ring;
+ sem->signal_fence.fence = ctx->last_seq[ip_type][ip_instance][ring];
+ update_references(NULL, &sem->refcount);
+ pthread_mutex_unlock(&ctx->sequence_mutex);
+ return 0;
+}
+
+drm_public int amdgpu_cs_wait_semaphore(amdgpu_context_handle ctx,
+ uint32_t ip_type,
+ uint32_t ip_instance,
+ uint32_t ring,
+ amdgpu_semaphore_handle sem)
+{
+ if (!ctx || !sem)
+ return -EINVAL;
+ if (ip_type >= AMDGPU_HW_IP_NUM)
+ return -EINVAL;
+ if (ring >= AMDGPU_CS_MAX_RINGS)
+ return -EINVAL;
+ /* must signal first */
+ if (!sem->signal_fence.context)
+ return -EINVAL;
+
+ pthread_mutex_lock(&ctx->sequence_mutex);
+ list_add(&sem->list, &ctx->sem_list[ip_type][ip_instance][ring]);
+ pthread_mutex_unlock(&ctx->sequence_mutex);
+ return 0;
+}
+
+static int amdgpu_cs_reset_sem(amdgpu_semaphore_handle sem)
+{
+ if (!sem || !sem->signal_fence.context)
+ return -EINVAL;
+
+ sem->signal_fence.context = NULL;
+ sem->signal_fence.ip_type = 0;
+ sem->signal_fence.ip_instance = 0;
+ sem->signal_fence.ring = 0;
+ sem->signal_fence.fence = 0;
+
+ return 0;
+}
+
+static int amdgpu_cs_unreference_sem(amdgpu_semaphore_handle sem)
+{
+ if (!sem)
+ return -EINVAL;
+
+ if (update_references(&sem->refcount, NULL))
+ free(sem);
+ return 0;
+}
+
+drm_public int amdgpu_cs_destroy_semaphore(amdgpu_semaphore_handle sem)
+{
+ return amdgpu_cs_unreference_sem(sem);
+}
+
+drm_public int amdgpu_cs_create_syncobj2(amdgpu_device_handle dev,
+ uint32_t flags,
+ uint32_t *handle)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjCreate(dev->fd, flags, handle);
+}
+
+drm_public int amdgpu_cs_create_syncobj(amdgpu_device_handle dev,
+ uint32_t *handle)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjCreate(dev->fd, 0, handle);
+}
+
+drm_public int amdgpu_cs_destroy_syncobj(amdgpu_device_handle dev,
+ uint32_t handle)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjDestroy(dev->fd, handle);
+}
+
+drm_public int amdgpu_cs_syncobj_reset(amdgpu_device_handle dev,
+ const uint32_t *syncobjs,
+ uint32_t syncobj_count)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjReset(dev->fd, syncobjs, syncobj_count);
+}
+
+drm_public int amdgpu_cs_syncobj_signal(amdgpu_device_handle dev,
+ const uint32_t *syncobjs,
+ uint32_t syncobj_count)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjSignal(dev->fd, syncobjs, syncobj_count);
+}
+
+drm_public int amdgpu_cs_syncobj_timeline_signal(amdgpu_device_handle dev,
+ const uint32_t *syncobjs,
+ uint64_t *points,
+ uint32_t syncobj_count)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjTimelineSignal(dev->fd, syncobjs,
+ points, syncobj_count);
+}
+
+drm_public int amdgpu_cs_syncobj_wait(amdgpu_device_handle dev,
+ uint32_t *handles, unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjWait(dev->fd, handles, num_handles, timeout_nsec,
+ flags, first_signaled);
+}
+
+drm_public int amdgpu_cs_syncobj_timeline_wait(amdgpu_device_handle dev,
+ uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjTimelineWait(dev->fd, handles, points, num_handles,
+ timeout_nsec, flags, first_signaled);
+}
+
+drm_public int amdgpu_cs_syncobj_query(amdgpu_device_handle dev,
+ uint32_t *handles, uint64_t *points,
+ unsigned num_handles)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjQuery(dev->fd, handles, points, num_handles);
+}
+
+drm_public int amdgpu_cs_syncobj_query2(amdgpu_device_handle dev,
+ uint32_t *handles, uint64_t *points,
+ unsigned num_handles, uint32_t flags)
+{
+ if (!dev)
+ return -EINVAL;
+
+ return drmSyncobjQuery2(dev->fd, handles, points, num_handles, flags);
+}
+
+drm_public int amdgpu_cs_export_syncobj(amdgpu_device_handle dev,
+ uint32_t handle,
+ int *shared_fd)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjHandleToFD(dev->fd, handle, shared_fd);
+}
+
+drm_public int amdgpu_cs_import_syncobj(amdgpu_device_handle dev,
+ int shared_fd,
+ uint32_t *handle)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjFDToHandle(dev->fd, shared_fd, handle);
+}
+
+drm_public int amdgpu_cs_syncobj_export_sync_file(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ int *sync_file_fd)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjExportSyncFile(dev->fd, syncobj, sync_file_fd);
+}
+
+drm_public int amdgpu_cs_syncobj_import_sync_file(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ int sync_file_fd)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjImportSyncFile(dev->fd, syncobj, sync_file_fd);
+}
+
+drm_public int amdgpu_cs_syncobj_export_sync_file2(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ uint64_t point,
+ uint32_t flags,
+ int *sync_file_fd)
+{
+ uint32_t binary_handle;
+ int ret;
+
+ if (NULL == dev)
+ return -EINVAL;
+
+ if (!point)
+ return drmSyncobjExportSyncFile(dev->fd, syncobj, sync_file_fd);
+
+ ret = drmSyncobjCreate(dev->fd, 0, &binary_handle);
+ if (ret)
+ return ret;
+
+ ret = drmSyncobjTransfer(dev->fd, binary_handle, 0,
+ syncobj, point, flags);
+ if (ret)
+ goto out;
+ ret = drmSyncobjExportSyncFile(dev->fd, binary_handle, sync_file_fd);
+out:
+ drmSyncobjDestroy(dev->fd, binary_handle);
+ return ret;
+}
+
+drm_public int amdgpu_cs_syncobj_import_sync_file2(amdgpu_device_handle dev,
+ uint32_t syncobj,
+ uint64_t point,
+ int sync_file_fd)
+{
+ uint32_t binary_handle;
+ int ret;
+
+ if (NULL == dev)
+ return -EINVAL;
+
+ if (!point)
+ return drmSyncobjImportSyncFile(dev->fd, syncobj, sync_file_fd);
+
+ ret = drmSyncobjCreate(dev->fd, 0, &binary_handle);
+ if (ret)
+ return ret;
+ ret = drmSyncobjImportSyncFile(dev->fd, binary_handle, sync_file_fd);
+ if (ret)
+ goto out;
+ ret = drmSyncobjTransfer(dev->fd, syncobj, point,
+ binary_handle, 0, 0);
+out:
+ drmSyncobjDestroy(dev->fd, binary_handle);
+ return ret;
+}
+
+drm_public int amdgpu_cs_syncobj_transfer(amdgpu_device_handle dev,
+ uint32_t dst_handle,
+ uint64_t dst_point,
+ uint32_t src_handle,
+ uint64_t src_point,
+ uint32_t flags)
+{
+ if (NULL == dev)
+ return -EINVAL;
+
+ return drmSyncobjTransfer(dev->fd,
+ dst_handle, dst_point,
+ src_handle, src_point,
+ flags);
+}
+
+drm_public int amdgpu_cs_submit_raw(amdgpu_device_handle dev,
+ amdgpu_context_handle context,
+ amdgpu_bo_list_handle bo_list_handle,
+ int num_chunks,
+ struct drm_amdgpu_cs_chunk *chunks,
+ uint64_t *seq_no)
+{
+ union drm_amdgpu_cs cs;
+ uint64_t *chunk_array;
+ int i, r;
+ if (num_chunks == 0)
+ return -EINVAL;
+
+ memset(&cs, 0, sizeof(cs));
+ chunk_array = alloca(sizeof(uint64_t) * num_chunks);
+ for (i = 0; i < num_chunks; i++)
+ chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+ cs.in.chunks = (uint64_t)(uintptr_t)chunk_array;
+ cs.in.ctx_id = context->id;
+ cs.in.bo_list_handle = bo_list_handle ? bo_list_handle->handle : 0;
+ cs.in.num_chunks = num_chunks;
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CS,
+ &cs, sizeof(cs));
+ if (r)
+ return r;
+
+ if (seq_no)
+ *seq_no = cs.out.handle;
+ return 0;
+}
+
+drm_public int amdgpu_cs_submit_raw2(amdgpu_device_handle dev,
+ amdgpu_context_handle context,
+ uint32_t bo_list_handle,
+ int num_chunks,
+ struct drm_amdgpu_cs_chunk *chunks,
+ uint64_t *seq_no)
+{
+ union drm_amdgpu_cs cs;
+ uint64_t *chunk_array;
+ int i, r;
+
+ memset(&cs, 0, sizeof(cs));
+ chunk_array = alloca(sizeof(uint64_t) * num_chunks);
+ for (i = 0; i < num_chunks; i++)
+ chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+ cs.in.chunks = (uint64_t)(uintptr_t)chunk_array;
+ cs.in.ctx_id = context->id;
+ cs.in.bo_list_handle = bo_list_handle;
+ cs.in.num_chunks = num_chunks;
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CS,
+ &cs, sizeof(cs));
+ if (!r && seq_no)
+ *seq_no = cs.out.handle;
+ return r;
+}
+
+drm_public void amdgpu_cs_chunk_fence_info_to_data(struct amdgpu_cs_fence_info *fence_info,
+ struct drm_amdgpu_cs_chunk_data *data)
+{
+ data->fence_data.handle = fence_info->handle->handle;
+ data->fence_data.offset = fence_info->offset * sizeof(uint64_t);
+}
+
+drm_public void amdgpu_cs_chunk_fence_to_dep(struct amdgpu_cs_fence *fence,
+ struct drm_amdgpu_cs_chunk_dep *dep)
+{
+ dep->ip_type = fence->ip_type;
+ dep->ip_instance = fence->ip_instance;
+ dep->ring = fence->ring;
+ dep->ctx_id = fence->context->id;
+ dep->handle = fence->fence;
+}
+
+drm_public int amdgpu_cs_fence_to_handle(amdgpu_device_handle dev,
+ struct amdgpu_cs_fence *fence,
+ uint32_t what,
+ uint32_t *out_handle)
+{
+ union drm_amdgpu_fence_to_handle fth;
+ int r;
+
+ memset(&fth, 0, sizeof(fth));
+ fth.in.fence.ctx_id = fence->context->id;
+ fth.in.fence.ip_type = fence->ip_type;
+ fth.in.fence.ip_instance = fence->ip_instance;
+ fth.in.fence.ring = fence->ring;
+ fth.in.fence.seq_no = fence->fence;
+ fth.in.what = what;
+
+ r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_FENCE_TO_HANDLE,
+ &fth, sizeof(fth));
+ if (r == 0)
+ *out_handle = fth.out.handle;
+ return r;
+}
diff --git a/amdgpu/amdgpu_device.c b/amdgpu/amdgpu_device.c
new file mode 100644
index 0000000..76b4e5e
--- /dev/null
+++ b/amdgpu/amdgpu_device.c
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * \file amdgpu_device.c
+ *
+ * Implementation of functions for AMD GPU device
+ *
+ */
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_math.h"
+
+#define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x)))
+
+static pthread_mutex_t dev_mutex = PTHREAD_MUTEX_INITIALIZER;
+static amdgpu_device_handle dev_list;
+
+static int fd_compare(int fd1, int fd2)
+{
+ char *name1 = drmGetPrimaryDeviceNameFromFd(fd1);
+ char *name2 = drmGetPrimaryDeviceNameFromFd(fd2);
+ int result;
+
+ if (name1 == NULL || name2 == NULL) {
+ free(name1);
+ free(name2);
+ return 0;
+ }
+
+ result = strcmp(name1, name2);
+ free(name1);
+ free(name2);
+
+ return result;
+}
+
+/**
+* Get the authenticated form fd,
+*
+* \param fd - \c [in] File descriptor for AMD GPU device
+* \param auth - \c [out] Pointer to output the fd is authenticated or not
+* A render node fd, output auth = 0
+* A legacy fd, get the authenticated for compatibility root
+*
+* \return 0 on success\n
+* >0 - AMD specific error code\n
+* <0 - Negative POSIX Error code
+*/
+static int amdgpu_get_auth(int fd, int *auth)
+{
+ int r = 0;
+ drm_client_t client = {};
+
+ if (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER)
+ *auth = 0;
+ else {
+ client.idx = 0;
+ r = drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client);
+ if (!r)
+ *auth = client.auth;
+ }
+ return r;
+}
+
+static void amdgpu_device_free_internal(amdgpu_device_handle dev)
+{
+ amdgpu_device_handle *node = &dev_list;
+
+ pthread_mutex_lock(&dev_mutex);
+ while (*node != dev && (*node)->next)
+ node = &(*node)->next;
+ *node = (*node)->next;
+ pthread_mutex_unlock(&dev_mutex);
+
+ close(dev->fd);
+ if ((dev->flink_fd >= 0) && (dev->fd != dev->flink_fd))
+ close(dev->flink_fd);
+
+ amdgpu_vamgr_deinit(&dev->vamgr_32);
+ amdgpu_vamgr_deinit(&dev->vamgr);
+ amdgpu_vamgr_deinit(&dev->vamgr_high_32);
+ amdgpu_vamgr_deinit(&dev->vamgr_high);
+ handle_table_fini(&dev->bo_handles);
+ handle_table_fini(&dev->bo_flink_names);
+ pthread_mutex_destroy(&dev->bo_table_mutex);
+ free(dev->marketing_name);
+ free(dev);
+}
+
+/**
+ * Assignment between two amdgpu_device pointers with reference counting.
+ *
+ * Usage:
+ * struct amdgpu_device *dst = ... , *src = ...;
+ *
+ * dst = src;
+ * // No reference counting. Only use this when you need to move
+ * // a reference from one pointer to another.
+ *
+ * amdgpu_device_reference(&dst, src);
+ * // Reference counters are updated. dst is decremented and src is
+ * // incremented. dst is freed if its reference counter is 0.
+ */
+static void amdgpu_device_reference(struct amdgpu_device **dst,
+ struct amdgpu_device *src)
+{
+ if (update_references(&(*dst)->refcount, &src->refcount))
+ amdgpu_device_free_internal(*dst);
+ *dst = src;
+}
+
+drm_public int amdgpu_device_initialize(int fd,
+ uint32_t *major_version,
+ uint32_t *minor_version,
+ amdgpu_device_handle *device_handle)
+{
+ struct amdgpu_device *dev;
+ drmVersionPtr version;
+ int r;
+ int flag_auth = 0;
+ int flag_authexist=0;
+ uint32_t accel_working = 0;
+ uint64_t start, max;
+
+ *device_handle = NULL;
+
+ pthread_mutex_lock(&dev_mutex);
+ r = amdgpu_get_auth(fd, &flag_auth);
+ if (r) {
+ fprintf(stderr, "%s: amdgpu_get_auth (1) failed (%i)\n",
+ __func__, r);
+ pthread_mutex_unlock(&dev_mutex);
+ return r;
+ }
+
+ for (dev = dev_list; dev; dev = dev->next)
+ if (fd_compare(dev->fd, fd) == 0)
+ break;
+
+ if (dev) {
+ r = amdgpu_get_auth(dev->fd, &flag_authexist);
+ if (r) {
+ fprintf(stderr, "%s: amdgpu_get_auth (2) failed (%i)\n",
+ __func__, r);
+ pthread_mutex_unlock(&dev_mutex);
+ return r;
+ }
+ if ((flag_auth) && (!flag_authexist)) {
+ dev->flink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ }
+ *major_version = dev->major_version;
+ *minor_version = dev->minor_version;
+ amdgpu_device_reference(device_handle, dev);
+ pthread_mutex_unlock(&dev_mutex);
+ return 0;
+ }
+
+ dev = calloc(1, sizeof(struct amdgpu_device));
+ if (!dev) {
+ fprintf(stderr, "%s: calloc failed\n", __func__);
+ pthread_mutex_unlock(&dev_mutex);
+ return -ENOMEM;
+ }
+
+ dev->fd = -1;
+ dev->flink_fd = -1;
+
+ atomic_set(&dev->refcount, 1);
+
+ version = drmGetVersion(fd);
+ if (version->version_major != 3) {
+ fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is "
+ "only compatible with 3.x.x.\n",
+ __func__,
+ version->version_major,
+ version->version_minor,
+ version->version_patchlevel);
+ drmFreeVersion(version);
+ r = -EBADF;
+ goto cleanup;
+ }
+
+ dev->fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ dev->flink_fd = dev->fd;
+ dev->major_version = version->version_major;
+ dev->minor_version = version->version_minor;
+ drmFreeVersion(version);
+
+ pthread_mutex_init(&dev->bo_table_mutex, NULL);
+
+ /* Check if acceleration is working. */
+ r = amdgpu_query_info(dev, AMDGPU_INFO_ACCEL_WORKING, 4, &accel_working);
+ if (r) {
+ fprintf(stderr, "%s: amdgpu_query_info(ACCEL_WORKING) failed (%i)\n",
+ __func__, r);
+ goto cleanup;
+ }
+ if (!accel_working) {
+ fprintf(stderr, "%s: AMDGPU_INFO_ACCEL_WORKING = 0\n", __func__);
+ r = -EBADF;
+ goto cleanup;
+ }
+
+ r = amdgpu_query_gpu_info_init(dev);
+ if (r) {
+ fprintf(stderr, "%s: amdgpu_query_gpu_info_init failed\n", __func__);
+ goto cleanup;
+ }
+
+ start = dev->dev_info.virtual_address_offset;
+ max = MIN2(dev->dev_info.virtual_address_max, 0x100000000ULL);
+ amdgpu_vamgr_init(&dev->vamgr_32, start, max,
+ dev->dev_info.virtual_address_alignment);
+
+ start = max;
+ max = MAX2(dev->dev_info.virtual_address_max, 0x100000000ULL);
+ amdgpu_vamgr_init(&dev->vamgr, start, max,
+ dev->dev_info.virtual_address_alignment);
+
+ start = dev->dev_info.high_va_offset;
+ max = MIN2(dev->dev_info.high_va_max, (start & ~0xffffffffULL) +
+ 0x100000000ULL);
+ amdgpu_vamgr_init(&dev->vamgr_high_32, start, max,
+ dev->dev_info.virtual_address_alignment);
+
+ start = max;
+ max = MAX2(dev->dev_info.high_va_max, (start & ~0xffffffffULL) +
+ 0x100000000ULL);
+ amdgpu_vamgr_init(&dev->vamgr_high, start, max,
+ dev->dev_info.virtual_address_alignment);
+
+ amdgpu_parse_asic_ids(dev);
+
+ *major_version = dev->major_version;
+ *minor_version = dev->minor_version;
+ *device_handle = dev;
+ dev->next = dev_list;
+ dev_list = dev;
+ pthread_mutex_unlock(&dev_mutex);
+
+ return 0;
+
+cleanup:
+ if (dev->fd >= 0)
+ close(dev->fd);
+ free(dev);
+ pthread_mutex_unlock(&dev_mutex);
+ return r;
+}
+
+drm_public int amdgpu_device_deinitialize(amdgpu_device_handle dev)
+{
+ amdgpu_device_reference(&dev, NULL);
+ return 0;
+}
+
+drm_public const char *amdgpu_get_marketing_name(amdgpu_device_handle dev)
+{
+ return dev->marketing_name;
+}
+
+drm_public int amdgpu_query_sw_info(amdgpu_device_handle dev,
+ enum amdgpu_sw_info info,
+ void *value)
+{
+ uint32_t *val32 = (uint32_t*)value;
+
+ switch (info) {
+ case amdgpu_sw_info_address32_hi:
+ if (dev->vamgr_high_32.va_max)
+ *val32 = (dev->vamgr_high_32.va_max - 1) >> 32;
+ else
+ *val32 = (dev->vamgr_32.va_max - 1) >> 32;
+ return 0;
+ }
+ return -EINVAL;
+}
diff --git a/amdgpu/amdgpu_gpu_info.c b/amdgpu/amdgpu_gpu_info.c
new file mode 100644
index 0000000..777087f
--- /dev/null
+++ b/amdgpu/amdgpu_gpu_info.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, Inc.
+ * 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
+ * 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "amdgpu.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "xf86drm.h"
+
+drm_public int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id,
+ unsigned size, void *value)
+{
+ struct drm_amdgpu_info request;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)value;
+ request.return_size = size;
+ request.query = info_id;
+
+ return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+}
+
+drm_public int amdgpu_query_crtc_from_id(amdgpu_device_handle dev, unsigned id,
+ int32_t *result)
+{
+ struct drm_amdgpu_info request;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)result;
+ request.return_size = sizeof(*result);
+ request.query = AMDGPU_INFO_CRTC_FROM_ID;
+ request.mode_crtc.id = id;
+
+ return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+}
+
+drm_public int amdgpu_read_mm_registers(amdgpu_device_handle dev,
+ unsigned dword_offset, unsigned count, uint32_t instance,
+ uint32_t flags, uint32_t *values)
+{
+ struct drm_amdgpu_info request;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)values;
+ request.return_size = count * sizeof(uint32_t);
+ request.query = AMDGPU_INFO_READ_MMR_REG;
+ request.read_mmr_reg.dword_offset = dword_offset;
+ request.read_mmr_reg.count = count;
+ request.read_mmr_reg.instance = instance;
+ request.read_mmr_reg.flags = flags;
+
+ return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+}
+
+drm_public int amdgpu_query_hw_ip_count(amdgpu_device_handle dev,
+ unsigned type,
+ uint32_t *count)
+{
+ struct drm_amdgpu_info request;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)count;
+ request.return_size = sizeof(*count);
+ request.query = AMDGPU_INFO_HW_IP_COUNT;
+ request.query_hw_ip.type = type;
+
+ return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+}
+
+drm_public int amdgpu_query_hw_ip_info(amdgpu_device_handle dev, unsigned type,
+ unsigned ip_instance,
+ struct drm_amdgpu_info_hw_ip *info)
+{
+ struct drm_amdgpu_info request;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)info;
+ request.return_size = sizeof(*info);
+ request.query = AMDGPU_INFO_HW_IP_INFO;
+ request.query_hw_ip.type = type;
+ request.query_hw_ip.ip_instance = ip_instance;
+
+ return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+}
+
+drm_public int amdgpu_query_firmware_version(amdgpu_device_handle dev,
+ unsigned fw_type, unsigned ip_instance, unsigned index,
+ uint32_t *version, uint32_t *feature)
+{
+ struct drm_amdgpu_info request;
+ struct drm_amdgpu_info_firmware firmware = {};
+ int r;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)&firmware;
+ request.return_size = sizeof(firmware);
+ request.query = AMDGPU_INFO_FW_VERSION;
+ request.query_fw.fw_type = fw_type;
+ request.query_fw.ip_instance = ip_instance;
+ request.query_fw.index = index;
+
+ r = drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+ if (r)
+ return r;
+
+ *version = firmware.ver;
+ *feature = firmware.feature;
+ return 0;
+}
+
+drm_private int amdgpu_query_gpu_info_init(amdgpu_device_handle dev)
+{
+ int r, i;
+
+ r = amdgpu_query_info(dev, AMDGPU_INFO_DEV_INFO, sizeof(dev->dev_info),
+ &dev->dev_info);
+ if (r)
+ return r;
+
+ dev->info.asic_id = dev->dev_info.device_id;
+ dev->info.chip_rev = dev->dev_info.chip_rev;
+ dev->info.chip_external_rev = dev->dev_info.external_rev;
+ dev->info.family_id = dev->dev_info.family;
+ dev->info.max_engine_clk = dev->dev_info.max_engine_clock;
+ dev->info.max_memory_clk = dev->dev_info.max_memory_clock;
+ dev->info.gpu_counter_freq = dev->dev_info.gpu_counter_freq;
+ dev->info.enabled_rb_pipes_mask = dev->dev_info.enabled_rb_pipes_mask;
+ dev->info.rb_pipes = dev->dev_info.num_rb_pipes;
+ dev->info.ids_flags = dev->dev_info.ids_flags;
+ dev->info.num_hw_gfx_contexts = dev->dev_info.num_hw_gfx_contexts;
+ dev->info.num_shader_engines = dev->dev_info.num_shader_engines;
+ dev->info.num_shader_arrays_per_engine =
+ dev->dev_info.num_shader_arrays_per_engine;
+ dev->info.vram_type = dev->dev_info.vram_type;
+ dev->info.vram_bit_width = dev->dev_info.vram_bit_width;
+ dev->info.ce_ram_size = dev->dev_info.ce_ram_size;
+ dev->info.vce_harvest_config = dev->dev_info.vce_harvest_config;
+ dev->info.pci_rev_id = dev->dev_info.pci_rev;
+
+ if (dev->info.family_id < AMDGPU_FAMILY_AI) {
+ for (i = 0; i < (int)dev->info.num_shader_engines; i++) {
+ unsigned instance = (i << AMDGPU_INFO_MMR_SE_INDEX_SHIFT) |
+ (AMDGPU_INFO_MMR_SH_INDEX_MASK <<
+ AMDGPU_INFO_MMR_SH_INDEX_SHIFT);
+
+ r = amdgpu_read_mm_registers(dev, 0x263d, 1, instance, 0,
+ &dev->info.backend_disable[i]);
+ if (r)
+ return r;
+ /* extract bitfield CC_RB_BACKEND_DISABLE.BACKEND_DISABLE */
+ dev->info.backend_disable[i] =
+ (dev->info.backend_disable[i] >> 16) & 0xff;
+
+ r = amdgpu_read_mm_registers(dev, 0xa0d4, 1, instance, 0,
+ &dev->info.pa_sc_raster_cfg[i]);
+ if (r)
+ return r;
+
+ if (dev->info.family_id >= AMDGPU_FAMILY_CI) {
+ r = amdgpu_read_mm_registers(dev, 0xa0d5, 1, instance, 0,
+ &dev->info.pa_sc_raster_cfg1[i]);
+ if (r)
+ return r;
+ }
+ }
+ }
+
+ r = amdgpu_read_mm_registers(dev, 0x263e, 1, 0xffffffff, 0,
+ &dev->info.gb_addr_cfg);
+ if (r)
+ return r;
+
+ if (dev->info.family_id < AMDGPU_FAMILY_AI) {
+ r = amdgpu_read_mm_registers(dev, 0x2644, 32, 0xffffffff, 0,
+ dev->info.gb_tile_mode);
+ if (r)
+ return r;
+
+ if (dev->info.family_id >= AMDGPU_FAMILY_CI) {
+ r = amdgpu_read_mm_registers(dev, 0x2664, 16, 0xffffffff, 0,
+ dev->info.gb_macro_tile_mode);
+ if (r)
+ return r;
+ }
+
+ r = amdgpu_read_mm_registers(dev, 0x9d8, 1, 0xffffffff, 0,
+ &dev->info.mc_arb_ramcfg);
+ if (r)
+ return r;
+ }
+
+ dev->info.cu_active_number = dev->dev_info.cu_active_number;
+ dev->info.cu_ao_mask = dev->dev_info.cu_ao_mask;
+ memcpy(&dev->info.cu_bitmap[0][0], &dev->dev_info.cu_bitmap[0][0], sizeof(dev->info.cu_bitmap));
+
+ /* TODO: info->max_quad_shader_pipes is not set */
+ /* TODO: info->avail_quad_shader_pipes is not set */
+ /* TODO: info->cache_entries_per_quad_pipe is not set */
+ return 0;
+}
+
+drm_public int amdgpu_query_gpu_info(amdgpu_device_handle dev,
+ struct amdgpu_gpu_info *info)
+{
+ if (!dev || !info)
+ return -EINVAL;
+
+ /* Get ASIC info*/
+ *info = dev->info;
+
+ return 0;
+}
+
+drm_public int amdgpu_query_heap_info(amdgpu_device_handle dev,
+ uint32_t heap,
+ uint32_t flags,
+ struct amdgpu_heap_info *info)
+{
+ struct drm_amdgpu_info_vram_gtt vram_gtt_info = {};
+ int r;
+
+ r = amdgpu_query_info(dev, AMDGPU_INFO_VRAM_GTT,
+ sizeof(vram_gtt_info), &vram_gtt_info);
+ if (r)
+ return r;
+
+ /* Get heap information */
+ switch (heap) {
+ case AMDGPU_GEM_DOMAIN_VRAM:
+ /* query visible only vram heap */
+ if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
+ info->heap_size = vram_gtt_info.vram_cpu_accessible_size;
+ else /* query total vram heap */
+ info->heap_size = vram_gtt_info.vram_size;
+
+ info->max_allocation = vram_gtt_info.vram_cpu_accessible_size;
+
+ if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
+ r = amdgpu_query_info(dev, AMDGPU_INFO_VIS_VRAM_USAGE,
+ sizeof(info->heap_usage),
+ &info->heap_usage);
+ else
+ r = amdgpu_query_info(dev, AMDGPU_INFO_VRAM_USAGE,
+ sizeof(info->heap_usage),
+ &info->heap_usage);
+ if (r)
+ return r;
+ break;
+ case AMDGPU_GEM_DOMAIN_GTT:
+ info->heap_size = vram_gtt_info.gtt_size;
+ info->max_allocation = vram_gtt_info.vram_cpu_accessible_size;
+
+ r = amdgpu_query_info(dev, AMDGPU_INFO_GTT_USAGE,
+ sizeof(info->heap_usage),
+ &info->heap_usage);
+ if (r)
+ return r;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+drm_public int amdgpu_query_gds_info(amdgpu_device_handle dev,
+ struct amdgpu_gds_resource_info *gds_info)
+{
+ struct drm_amdgpu_info_gds gds_config = {};
+ int r;
+
+ if (!gds_info)
+ return -EINVAL;
+
+ r = amdgpu_query_info(dev, AMDGPU_INFO_GDS_CONFIG,
+ sizeof(gds_config), &gds_config);
+ if (r)
+ return r;
+
+ gds_info->gds_gfx_partition_size = gds_config.gds_gfx_partition_size;
+ gds_info->compute_partition_size = gds_config.compute_partition_size;
+ gds_info->gds_total_size = gds_config.gds_total_size;
+ gds_info->gws_per_gfx_partition = gds_config.gws_per_gfx_partition;
+ gds_info->gws_per_compute_partition = gds_config.gws_per_compute_partition;
+ gds_info->oa_per_gfx_partition = gds_config.oa_per_gfx_partition;
+ gds_info->oa_per_compute_partition = gds_config.oa_per_compute_partition;
+
+ return 0;
+}
+
+drm_public int amdgpu_query_sensor_info(amdgpu_device_handle dev, unsigned sensor_type,
+ unsigned size, void *value)
+{
+ struct drm_amdgpu_info request;
+
+ memset(&request, 0, sizeof(request));
+ request.return_pointer = (uintptr_t)value;
+ request.return_size = size;
+ request.query = AMDGPU_INFO_SENSOR;
+ request.sensor_info.type = sensor_type;
+
+ return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+ sizeof(struct drm_amdgpu_info));
+}
diff --git a/amdgpu/amdgpu_internal.h b/amdgpu/amdgpu_internal.h
new file mode 100644
index 0000000..37a7c9d
--- /dev/null
+++ b/amdgpu/amdgpu_internal.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, Inc.
+ * 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
+ * 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_INTERNAL_H_
+#define _AMDGPU_INTERNAL_H_
+
+#include <assert.h>
+#include <pthread.h>
+
+#include "libdrm_macros.h"
+#include "xf86atomic.h"
+#include "amdgpu.h"
+#include "util_double_list.h"
+#include "handle_table.h"
+
+#define AMDGPU_CS_MAX_RINGS 8
+/* do not use below macro if b is not power of 2 aligned value */
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define ROUND_UP(x, y) ((((x)-1) | __round_mask(x, y))+1)
+#define ROUND_DOWN(x, y) ((x) & ~__round_mask(x, y))
+
+#define AMDGPU_INVALID_VA_ADDRESS 0xffffffffffffffff
+#define AMDGPU_NULL_SUBMIT_SEQ 0
+
+struct amdgpu_bo_va_hole {
+ struct list_head list;
+ uint64_t offset;
+ uint64_t size;
+};
+
+struct amdgpu_bo_va_mgr {
+ uint64_t va_max;
+ struct list_head va_holes;
+ pthread_mutex_t bo_va_mutex;
+ uint32_t va_alignment;
+};
+
+struct amdgpu_va {
+ amdgpu_device_handle dev;
+ uint64_t address;
+ uint64_t size;
+ enum amdgpu_gpu_va_range range;
+ struct amdgpu_bo_va_mgr *vamgr;
+};
+
+struct amdgpu_device {
+ atomic_t refcount;
+ struct amdgpu_device *next;
+ int fd;
+ int flink_fd;
+ unsigned major_version;
+ unsigned minor_version;
+
+ char *marketing_name;
+ /** List of buffer handles. Protected by bo_table_mutex. */
+ struct handle_table bo_handles;
+ /** List of buffer GEM flink names. Protected by bo_table_mutex. */
+ struct handle_table bo_flink_names;
+ /** This protects all hash tables. */
+ pthread_mutex_t bo_table_mutex;
+ struct drm_amdgpu_info_device dev_info;
+ struct amdgpu_gpu_info info;
+ /** The VA manager for the lower virtual address space */
+ struct amdgpu_bo_va_mgr vamgr;
+ /** The VA manager for the 32bit address space */
+ struct amdgpu_bo_va_mgr vamgr_32;
+ /** The VA manager for the high virtual address space */
+ struct amdgpu_bo_va_mgr vamgr_high;
+ /** The VA manager for the 32bit high address space */
+ struct amdgpu_bo_va_mgr vamgr_high_32;
+};
+
+struct amdgpu_bo {
+ atomic_t refcount;
+ struct amdgpu_device *dev;
+
+ uint64_t alloc_size;
+
+ uint32_t handle;
+ uint32_t flink_name;
+
+ pthread_mutex_t cpu_access_mutex;
+ void *cpu_ptr;
+ int64_t cpu_map_count;
+};
+
+struct amdgpu_bo_list {
+ struct amdgpu_device *dev;
+
+ uint32_t handle;
+};
+
+struct amdgpu_context {
+ struct amdgpu_device *dev;
+ /** Mutex for accessing fences and to maintain command submissions
+ in good sequence. */
+ pthread_mutex_t sequence_mutex;
+ /* context id*/
+ uint32_t id;
+ uint64_t last_seq[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+ struct list_head sem_list[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+};
+
+/**
+ * Structure describing sw semaphore based on scheduler
+ *
+ */
+struct amdgpu_semaphore {
+ atomic_t refcount;
+ struct list_head list;
+ struct amdgpu_cs_fence signal_fence;
+};
+
+/**
+ * Functions.
+ */
+
+drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start,
+ uint64_t max, uint64_t alignment);
+
+drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr);
+
+drm_private void amdgpu_parse_asic_ids(struct amdgpu_device *dev);
+
+drm_private int amdgpu_query_gpu_info_init(amdgpu_device_handle dev);
+
+drm_private uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout);
+
+/**
+ * Inline functions.
+ */
+
+/**
+ * Increment src and decrement dst as if we were updating references
+ * for an assignment between 2 pointers of some objects.
+ *
+ * \return true if dst is 0
+ */
+static inline bool update_references(atomic_t *dst, atomic_t *src)
+{
+ if (dst != src) {
+ /* bump src first */
+ if (src) {
+ assert(atomic_read(src) > 0);
+ atomic_inc(src);
+ }
+ if (dst) {
+ assert(atomic_read(dst) > 0);
+ return atomic_dec_and_test(dst);
+ }
+ }
+ return false;
+}
+
+#endif
diff --git a/amdgpu/amdgpu_vamgr.c b/amdgpu/amdgpu_vamgr.c
new file mode 100644
index 0000000..d25d421
--- /dev/null
+++ b/amdgpu/amdgpu_vamgr.c
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "amdgpu.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_math.h"
+
+drm_public int amdgpu_va_range_query(amdgpu_device_handle dev,
+ enum amdgpu_gpu_va_range type,
+ uint64_t *start, uint64_t *end)
+{
+ if (type != amdgpu_gpu_va_range_general)
+ return -EINVAL;
+
+ *start = dev->dev_info.virtual_address_offset;
+ *end = dev->dev_info.virtual_address_max;
+ return 0;
+}
+
+drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start,
+ uint64_t max, uint64_t alignment)
+{
+ struct amdgpu_bo_va_hole *n;
+
+ mgr->va_max = max;
+ mgr->va_alignment = alignment;
+
+ list_inithead(&mgr->va_holes);
+ pthread_mutex_init(&mgr->bo_va_mutex, NULL);
+ pthread_mutex_lock(&mgr->bo_va_mutex);
+ n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
+ n->size = mgr->va_max - start;
+ n->offset = start;
+ list_add(&n->list, &mgr->va_holes);
+ pthread_mutex_unlock(&mgr->bo_va_mutex);
+}
+
+drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr)
+{
+ struct amdgpu_bo_va_hole *hole, *tmp;
+ LIST_FOR_EACH_ENTRY_SAFE(hole, tmp, &mgr->va_holes, list) {
+ list_del(&hole->list);
+ free(hole);
+ }
+ pthread_mutex_destroy(&mgr->bo_va_mutex);
+}
+
+static drm_private uint64_t
+amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size,
+ uint64_t alignment, uint64_t base_required)
+{
+ struct amdgpu_bo_va_hole *hole, *n;
+ uint64_t offset = 0, waste = 0;
+
+
+ alignment = MAX2(alignment, mgr->va_alignment);
+ size = ALIGN(size, mgr->va_alignment);
+
+ if (base_required % alignment)
+ return AMDGPU_INVALID_VA_ADDRESS;
+
+ pthread_mutex_lock(&mgr->bo_va_mutex);
+ LIST_FOR_EACH_ENTRY_SAFE_REV(hole, n, &mgr->va_holes, list) {
+ if (base_required) {
+ if (hole->offset > base_required ||
+ (hole->offset + hole->size) < (base_required + size))
+ continue;
+ waste = base_required - hole->offset;
+ offset = base_required;
+ } else {
+ offset = hole->offset;
+ waste = offset % alignment;
+ waste = waste ? alignment - waste : 0;
+ offset += waste;
+ if (offset >= (hole->offset + hole->size)) {
+ continue;
+ }
+ }
+ if (!waste && hole->size == size) {
+ offset = hole->offset;
+ list_del(&hole->list);
+ free(hole);
+ pthread_mutex_unlock(&mgr->bo_va_mutex);
+ return offset;
+ }
+ if ((hole->size - waste) > size) {
+ if (waste) {
+ n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
+ n->size = waste;
+ n->offset = hole->offset;
+ list_add(&n->list, &hole->list);
+ }
+ hole->size -= (size + waste);
+ hole->offset += size + waste;
+ pthread_mutex_unlock(&mgr->bo_va_mutex);
+ return offset;
+ }
+ if ((hole->size - waste) == size) {
+ hole->size = waste;
+ pthread_mutex_unlock(&mgr->bo_va_mutex);
+ return offset;
+ }
+ }
+
+ pthread_mutex_unlock(&mgr->bo_va_mutex);
+ return AMDGPU_INVALID_VA_ADDRESS;
+}
+
+static drm_private void
+amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, uint64_t size)
+{
+ struct amdgpu_bo_va_hole *hole, *next;
+
+ if (va == AMDGPU_INVALID_VA_ADDRESS)
+ return;
+
+ size = ALIGN(size, mgr->va_alignment);
+
+ pthread_mutex_lock(&mgr->bo_va_mutex);
+ hole = container_of(&mgr->va_holes, hole, list);
+ LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) {
+ if (next->offset < va)
+ break;
+ hole = next;
+ }
+
+ if (&hole->list != &mgr->va_holes) {
+ /* Grow upper hole if it's adjacent */
+ if (hole->offset == (va + size)) {
+ hole->offset = va;
+ hole->size += size;
+ /* Merge lower hole if it's adjacent */
+ if (next != hole &&
+ &next->list != &mgr->va_holes &&
+ (next->offset + next->size) == va) {
+ next->size += hole->size;
+ list_del(&hole->list);
+ free(hole);
+ }
+ goto out;
+ }
+ }
+
+ /* Grow lower hole if it's adjacent */
+ if (next != hole && &next->list != &mgr->va_holes &&
+ (next->offset + next->size) == va) {
+ next->size += size;
+ goto out;
+ }
+
+ /* FIXME on allocation failure we just lose virtual address space
+ * maybe print a warning
+ */
+ next = calloc(1, sizeof(struct amdgpu_bo_va_hole));
+ if (next) {
+ next->size = size;
+ next->offset = va;
+ list_add(&next->list, &hole->list);
+ }
+
+out:
+ pthread_mutex_unlock(&mgr->bo_va_mutex);
+}
+
+drm_public int amdgpu_va_range_alloc(amdgpu_device_handle dev,
+ enum amdgpu_gpu_va_range va_range_type,
+ uint64_t size,
+ uint64_t va_base_alignment,
+ uint64_t va_base_required,
+ uint64_t *va_base_allocated,
+ amdgpu_va_handle *va_range_handle,
+ uint64_t flags)
+{
+ struct amdgpu_bo_va_mgr *vamgr;
+
+ /* Clear the flag when the high VA manager is not initialized */
+ if (flags & AMDGPU_VA_RANGE_HIGH && !dev->vamgr_high_32.va_max)
+ flags &= ~AMDGPU_VA_RANGE_HIGH;
+
+ if (flags & AMDGPU_VA_RANGE_HIGH) {
+ if (flags & AMDGPU_VA_RANGE_32_BIT)
+ vamgr = &dev->vamgr_high_32;
+ else
+ vamgr = &dev->vamgr_high;
+ } else {
+ if (flags & AMDGPU_VA_RANGE_32_BIT)
+ vamgr = &dev->vamgr_32;
+ else
+ vamgr = &dev->vamgr;
+ }
+
+ va_base_alignment = MAX2(va_base_alignment, vamgr->va_alignment);
+ size = ALIGN(size, vamgr->va_alignment);
+
+ *va_base_allocated = amdgpu_vamgr_find_va(vamgr, size,
+ va_base_alignment, va_base_required);
+
+ if (!(flags & AMDGPU_VA_RANGE_32_BIT) &&
+ (*va_base_allocated == AMDGPU_INVALID_VA_ADDRESS)) {
+ /* fallback to 32bit address */
+ if (flags & AMDGPU_VA_RANGE_HIGH)
+ vamgr = &dev->vamgr_high_32;
+ else
+ vamgr = &dev->vamgr_32;
+ *va_base_allocated = amdgpu_vamgr_find_va(vamgr, size,
+ va_base_alignment, va_base_required);
+ }
+
+ if (*va_base_allocated != AMDGPU_INVALID_VA_ADDRESS) {
+ struct amdgpu_va* va;
+ va = calloc(1, sizeof(struct amdgpu_va));
+ if(!va){
+ amdgpu_vamgr_free_va(vamgr, *va_base_allocated, size);
+ return -ENOMEM;
+ }
+ va->dev = dev;
+ va->address = *va_base_allocated;
+ va->size = size;
+ va->range = va_range_type;
+ va->vamgr = vamgr;
+ *va_range_handle = va;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+drm_public int amdgpu_va_range_free(amdgpu_va_handle va_range_handle)
+{
+ if(!va_range_handle || !va_range_handle->address)
+ return 0;
+
+ amdgpu_vamgr_free_va(va_range_handle->vamgr,
+ va_range_handle->address,
+ va_range_handle->size);
+ free(va_range_handle);
+ return 0;
+}
diff --git a/amdgpu/amdgpu_vm.c b/amdgpu/amdgpu_vm.c
new file mode 100644
index 0000000..7e6e28f
--- /dev/null
+++ b/amdgpu/amdgpu_vm.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 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.
+ *
+ */
+
+#include "amdgpu.h"
+#include "amdgpu_drm.h"
+#include "xf86drm.h"
+#include "amdgpu_internal.h"
+
+drm_public int amdgpu_vm_reserve_vmid(amdgpu_device_handle dev, uint32_t flags)
+{
+ union drm_amdgpu_vm vm;
+
+ vm.in.op = AMDGPU_VM_OP_RESERVE_VMID;
+ vm.in.flags = flags;
+
+ return drmCommandWriteRead(dev->fd, DRM_AMDGPU_VM,
+ &vm, sizeof(vm));
+}
+
+drm_public int amdgpu_vm_unreserve_vmid(amdgpu_device_handle dev,
+ uint32_t flags)
+{
+ union drm_amdgpu_vm vm;
+
+ vm.in.op = AMDGPU_VM_OP_UNRESERVE_VMID;
+ vm.in.flags = flags;
+
+ return drmCommandWriteRead(dev->fd, DRM_AMDGPU_VM,
+ &vm, sizeof(vm));
+}
diff --git a/amdgpu/handle_table.c b/amdgpu/handle_table.c
new file mode 100644
index 0000000..4fdd29d
--- /dev/null
+++ b/amdgpu/handle_table.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "handle_table.h"
+#include "util_math.h"
+
+drm_private int handle_table_insert(struct handle_table *table, uint32_t key,
+ void *value)
+{
+ if (key >= table->max_key) {
+ uint32_t alignment = sysconf(_SC_PAGESIZE) / sizeof(void*);
+ uint32_t max_key = ALIGN(key + 1, alignment);
+ void **values;
+
+ values = realloc(table->values, max_key * sizeof(void *));
+ if (!values)
+ return -ENOMEM;
+
+ memset(values + table->max_key, 0, (max_key - table->max_key) *
+ sizeof(void *));
+
+ table->max_key = max_key;
+ table->values = values;
+ }
+ table->values[key] = value;
+ return 0;
+}
+
+drm_private void handle_table_remove(struct handle_table *table, uint32_t key)
+{
+ if (key < table->max_key)
+ table->values[key] = NULL;
+}
+
+drm_private void *handle_table_lookup(struct handle_table *table, uint32_t key)
+{
+ if (key < table->max_key)
+ return table->values[key];
+ else
+ return NULL;
+}
+
+drm_private void handle_table_fini(struct handle_table *table)
+{
+ free(table->values);
+ table->max_key = 0;
+ table->values = NULL;
+}
diff --git a/amdgpu/handle_table.h b/amdgpu/handle_table.h
new file mode 100644
index 0000000..461193f
--- /dev/null
+++ b/amdgpu/handle_table.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 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 _HANDLE_TABLE_H_
+#define _HANDLE_TABLE_H_
+
+#include <stdint.h>
+#include "libdrm_macros.h"
+
+struct handle_table {
+ uint32_t max_key;
+ void **values;
+};
+
+drm_private int handle_table_insert(struct handle_table *table, uint32_t key,
+ void *value);
+drm_private void handle_table_remove(struct handle_table *table, uint32_t key);
+drm_private void *handle_table_lookup(struct handle_table *table, uint32_t key);
+drm_private void handle_table_fini(struct handle_table *table);
+
+#endif /* _HANDLE_TABLE_H_ */
diff --git a/amdgpu/libdrm_amdgpu.pc.in b/amdgpu/libdrm_amdgpu.pc.in
new file mode 100644
index 0000000..f1c552a
--- /dev/null
+++ b/amdgpu/libdrm_amdgpu.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_amdgpu
+Description: Userspace interface to kernel DRM services for amdgpu
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_amdgpu
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
diff --git a/amdgpu/meson.build b/amdgpu/meson.build
new file mode 100644
index 0000000..a1781f5
--- /dev/null
+++ b/amdgpu/meson.build
@@ -0,0 +1,68 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+
+datadir_amdgpu = join_paths(get_option('prefix'), get_option('datadir'), 'libdrm')
+
+libdrm_amdgpu = shared_library(
+ 'drm_amdgpu',
+ [
+ files(
+ 'amdgpu_asic_id.c', 'amdgpu_bo.c', 'amdgpu_cs.c', 'amdgpu_device.c',
+ 'amdgpu_gpu_info.c', 'amdgpu_vamgr.c', 'amdgpu_vm.c', 'handle_table.c',
+ ),
+ config_file,
+ ],
+ c_args : [
+ libdrm_c_args,
+ '-DAMDGPU_ASIC_ID_TABLE="@0@"'.format(join_paths(datadir_amdgpu, 'amdgpu.ids')),
+ ],
+ include_directories : [inc_root, inc_drm],
+ link_with : libdrm,
+ dependencies : [dep_pthread_stubs, dep_atomic_ops],
+ version : '1.0.0',
+ install : true,
+)
+
+install_headers('amdgpu.h', subdir : 'libdrm')
+
+pkg.generate(
+ name : 'libdrm_amdgpu',
+ libraries : libdrm_amdgpu,
+ subdirs : ['.', 'libdrm'],
+ version : meson.project_version(),
+ requires_private : 'libdrm',
+ description : 'Userspace interface to kernel DRM services for amdgpu',
+)
+
+ext_libdrm_amdgpu = declare_dependency(
+ link_with : [libdrm, libdrm_amdgpu],
+ include_directories : [inc_drm, include_directories('.')],
+)
+
+test(
+ 'amdgpu-symbols-check',
+ symbols_check,
+ args : [
+ '--lib', libdrm_amdgpu,
+ '--symbols-file', files('amdgpu-symbols.txt'),
+ '--nm', prog_nm.path(),
+ ],
+)
diff --git a/android/gralloc_handle.h b/android/gralloc_handle.h
new file mode 100644
index 0000000..d3d975e
--- /dev/null
+++ b/android/gralloc_handle.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com>
+ * Copyright (C) 2010-2011 LunarG Inc.
+ * Copyright (C) 2016 Linaro, Ltd., Rob Herring <robh@kernel.org>
+ * Copyright (C) 2018 Collabora, Robert Foss <robert.foss@collabora.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __ANDROID_GRALLOC_HANDLE_H__
+#define __ANDROID_GRALLOC_HANDLE_H__
+
+#include <cutils/native_handle.h>
+#include <stdint.h>
+
+/* support users of drm_gralloc/gbm_gralloc */
+#define gralloc_gbm_handle_t gralloc_handle_t
+#define gralloc_drm_handle_t gralloc_handle_t
+
+struct gralloc_handle_t {
+ native_handle_t base;
+
+ /* dma-buf file descriptor
+ * Must be located first since, native_handle_t is allocated
+ * using native_handle_create(), which allocates space for
+ * sizeof(native_handle_t) + sizeof(int) * (numFds + numInts)
+ * numFds = GRALLOC_HANDLE_NUM_FDS
+ * numInts = GRALLOC_HANDLE_NUM_INTS
+ * Where numFds represents the number of FDs and
+ * numInts represents the space needed for the
+ * remainder of this struct.
+ * And the FDs are expected to be found first following
+ * native_handle_t.
+ */
+ int prime_fd;
+
+ /* api variables */
+ uint32_t magic; /* differentiate between allocator impls */
+ uint32_t version; /* api version */
+
+ uint32_t width; /* width of buffer in pixels */
+ uint32_t height; /* height of buffer in pixels */
+ uint32_t format; /* pixel format (Android) */
+ uint32_t usage; /* android libhardware usage flags */
+
+ uint32_t stride; /* the stride in bytes */
+ int data_owner; /* owner of data (for validation) */
+ uint64_t modifier __attribute__((aligned(8))); /* buffer modifiers */
+
+ union {
+ void *data; /* pointer to struct gralloc_gbm_bo_t */
+ uint64_t reserved;
+ } __attribute__((aligned(8)));
+};
+
+#define GRALLOC_HANDLE_VERSION 4
+#define GRALLOC_HANDLE_MAGIC 0x60585350
+#define GRALLOC_HANDLE_NUM_FDS 1
+#define GRALLOC_HANDLE_NUM_INTS ( \
+ ((sizeof(struct gralloc_handle_t) - sizeof(native_handle_t))/sizeof(int)) \
+ - GRALLOC_HANDLE_NUM_FDS)
+
+static inline struct gralloc_handle_t *gralloc_handle(buffer_handle_t handle)
+{
+ return (struct gralloc_handle_t *)handle;
+}
+
+/**
+ * Create a buffer handle.
+ */
+static inline native_handle_t *gralloc_handle_create(int32_t width,
+ int32_t height,
+ int32_t hal_format,
+ int32_t usage)
+{
+ struct gralloc_handle_t *handle;
+ native_handle_t *nhandle = native_handle_create(GRALLOC_HANDLE_NUM_FDS,
+ GRALLOC_HANDLE_NUM_INTS);
+
+ if (!nhandle)
+ return NULL;
+
+ handle = gralloc_handle(nhandle);
+ handle->magic = GRALLOC_HANDLE_MAGIC;
+ handle->version = GRALLOC_HANDLE_VERSION;
+ handle->width = width;
+ handle->height = height;
+ handle->format = hal_format;
+ handle->usage = usage;
+ handle->prime_fd = -1;
+
+ return nhandle;
+}
+
+#endif
diff --git a/core-symbols.txt b/core-symbols.txt
new file mode 100644
index 0000000..1ff4eca
--- /dev/null
+++ b/core-symbols.txt
@@ -0,0 +1,197 @@
+drmAddBufs
+drmAddContextPrivateMapping
+drmAddContextTag
+drmAddMap
+drmAgpAcquire
+drmAgpAlloc
+drmAgpBase
+drmAgpBind
+drmAgpDeviceId
+drmAgpEnable
+drmAgpFree
+drmAgpGetMode
+drmAgpMemoryAvail
+drmAgpMemoryUsed
+drmAgpRelease
+drmAgpSize
+drmAgpUnbind
+drmAgpVendorId
+drmAgpVersionMajor
+drmAgpVersionMinor
+drmAuthMagic
+drmAvailable
+drmCheckModesettingSupported
+drmClose
+drmCloseOnce
+drmCommandNone
+drmCommandRead
+drmCommandWrite
+drmCommandWriteRead
+drmCreateContext
+drmCreateDrawable
+drmCrtcGetSequence
+drmCrtcQueueSequence
+drmCtlInstHandler
+drmCtlUninstHandler
+drmDelContextTag
+drmDestroyContext
+drmDestroyDrawable
+drmDevicesEqual
+drmDMA
+drmDropMaster
+drmError
+drmFinish
+drmFree
+drmFreeBufs
+drmFreeBusid
+drmFreeDevice
+drmFreeDevices
+drmFreeReservedContextList
+drmFreeVersion
+drmGetBufInfo
+drmGetBusid
+drmGetCap
+drmGetClient
+drmGetContextFlags
+drmGetContextPrivateMapping
+drmGetContextTag
+drmGetDevice
+drmGetDevice2
+drmGetDeviceNameFromFd
+drmGetDeviceNameFromFd2
+drmGetDevices
+drmGetDevices2
+drmGetEntry
+drmGetHashTable
+drmGetInterruptFromBusID
+drmGetLibVersion
+drmGetLock
+drmGetMagic
+drmGetMap
+drmGetNodeTypeFromFd
+drmGetPrimaryDeviceNameFromFd
+drmGetRenderDeviceNameFromFd
+drmGetReservedContextList
+drmGetStats
+drmGetVersion
+drmHandleEvent
+drmHashCreate
+drmHashDelete
+drmHashDestroy
+drmHashFirst
+drmHashInsert
+drmHashLookup
+drmHashNext
+drmIoctl
+drmIsMaster
+drmMalloc
+drmMap
+drmMapBufs
+drmMarkBufs
+drmModeAddFB
+drmModeAddFB2
+drmModeAddFB2WithModifiers
+drmModeAtomicAddProperty
+drmModeAtomicAlloc
+drmModeAtomicCommit
+drmModeAtomicDuplicate
+drmModeAtomicFree
+drmModeAtomicGetCursor
+drmModeAtomicMerge
+drmModeAtomicSetCursor
+drmModeAttachMode
+drmModeConnectorSetProperty
+drmModeCreateLease
+drmModeCreatePropertyBlob
+drmModeCrtcGetGamma
+drmModeCrtcSetGamma
+drmModeDestroyPropertyBlob
+drmModeDetachMode
+drmModeDirtyFB
+drmModeFreeConnector
+drmModeFreeCrtc
+drmModeFreeEncoder
+drmModeFreeFB
+drmModeFreeFB2
+drmModeFreeModeInfo
+drmModeFreeObjectProperties
+drmModeFreePlane
+drmModeFreePlaneResources
+drmModeFreeProperty
+drmModeFreePropertyBlob
+drmModeFreeResources
+drmModeGetConnector
+drmModeGetConnectorCurrent
+drmModeGetCrtc
+drmModeGetEncoder
+drmModeGetFB
+drmModeGetFB2
+drmModeGetLease
+drmModeGetPlane
+drmModeGetPlaneResources
+drmModeGetProperty
+drmModeGetPropertyBlob
+drmModeGetResources
+drmModeListLessees
+drmModeMoveCursor
+drmModeObjectGetProperties
+drmModeObjectSetProperty
+drmModePageFlip
+drmModePageFlipTarget
+drmModeRevokeLease
+drmModeRmFB
+drmModeSetCrtc
+drmModeSetCursor
+drmModeSetCursor2
+drmModeSetPlane
+drmMsg
+drmOpen
+drmOpenControl
+drmOpenOnce
+drmOpenOnceWithType
+drmOpenRender
+drmOpenWithType
+drmPrimeFDToHandle
+drmPrimeHandleToFD
+drmRandom
+drmRandomCreate
+drmRandomDestroy
+drmRandomDouble
+drmRmMap
+drmScatterGatherAlloc
+drmScatterGatherFree
+drmSetBusid
+drmSetClientCap
+drmSetContextFlags
+drmSetInterfaceVersion
+drmSetMaster
+drmSetServerInfo
+drmSLCreate
+drmSLDelete
+drmSLDestroy
+drmSLDump
+drmSLFirst
+drmSLInsert
+drmSLLookup
+drmSLLookupNeighbors
+drmSLNext
+drmSwitchToContext
+drmSyncobjCreate
+drmSyncobjDestroy
+drmSyncobjExportSyncFile
+drmSyncobjFDToHandle
+drmSyncobjHandleToFD
+drmSyncobjImportSyncFile
+drmSyncobjQuery
+drmSyncobjQuery2
+drmSyncobjReset
+drmSyncobjSignal
+drmSyncobjTimelineSignal
+drmSyncobjTimelineWait
+drmSyncobjTransfer
+drmSyncobjWait
+drmUnlock
+drmUnmap
+drmUnmapBufs
+drmUpdateDrawableInfo
+drmWaitVBlank
diff --git a/data/Android.mk b/data/Android.mk
new file mode 100644
index 0000000..62013f0
--- /dev/null
+++ b/data/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := amdgpu.ids
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := hwdata
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/amdgpu.ids b/data/amdgpu.ids
new file mode 100644
index 0000000..6fc6858
--- /dev/null
+++ b/data/amdgpu.ids
@@ -0,0 +1,220 @@
+# List of AMDGPU IDs
+#
+# Syntax:
+# device_id, revision_id, product_name <-- single tab after comma
+
+1.0.0
+15DD, 81, AMD Ryzen Embedded V1807B with Radeon Vega Gfx
+15DD, 82, AMD Ryzen Embedded V1756B with Radeon Vega Gfx
+15DD, 83, AMD Ryzen Embedded V1605B with Radeon Vega Gfx
+15DD, 85, AMD Ryzen Embedded V1202B with Radeon Vega Gfx
+6600, 0, AMD Radeon HD 8600/8700M
+6600, 81, AMD Radeon (TM) R7 M370
+6601, 0, AMD Radeon (TM) HD 8500M/8700M
+6604, 0, AMD Radeon R7 M265 Series
+6604, 81, AMD Radeon (TM) R7 M350
+6605, 0, AMD Radeon R7 M260 Series
+6605, 81, AMD Radeon (TM) R7 M340
+6606, 0, AMD Radeon HD 8790M
+6607, 0, AMD Radeon (TM) HD8530M
+6608, 0, AMD FirePro W2100
+6610, 0, AMD Radeon HD 8600 Series
+6610, 81, AMD Radeon (TM) R7 350
+6610, 83, AMD Radeon (TM) R5 340
+6611, 0, AMD Radeon HD 8500 Series
+6613, 0, AMD Radeon HD 8500 series
+6617, C7, AMD Radeon R7 240 Series
+6640, 0, AMD Radeon HD 8950
+6640, 80, AMD Radeon (TM) R9 M380
+6646, 0, AMD Radeon R9 M280X
+6646, 80, AMD Radeon (TM) R9 M470X
+6647, 0, AMD Radeon R9 M270X
+6647, 80, AMD Radeon (TM) R9 M380
+6649, 0, AMD FirePro W5100
+6658, 0, AMD Radeon R7 200 Series
+665C, 0, AMD Radeon HD 7700 Series
+665D, 0, AMD Radeon R7 200 Series
+665F, 81, AMD Radeon (TM) R7 300 Series
+6660, 0, AMD Radeon HD 8600M Series
+6660, 81, AMD Radeon (TM) R5 M335
+6660, 83, AMD Radeon (TM) R5 M330
+6663, 0, AMD Radeon HD 8500M Series
+6663, 83, AMD Radeon (TM) R5 M320
+6664, 0, AMD Radeon R5 M200 Series
+6665, 0, AMD Radeon R5 M200 Series
+6665, 83, AMD Radeon (TM) R5 M320
+6667, 0, AMD Radeon R5 M200 Series
+666F, 0, AMD Radeon HD 8500M
+66AF, C1, AMD Radeon VII
+6780, 0, ATI FirePro V (FireGL V) Graphics Adapter
+678A, 0, ATI FirePro V (FireGL V) Graphics Adapter
+6798, 0, AMD Radeon HD 7900 Series
+679A, 0, AMD Radeon HD 7900 Series
+679B, 0, AMD Radeon HD 7900 Series
+679E, 0, AMD Radeon HD 7800 Series
+67A0, 0, AMD Radeon FirePro W9100
+67A1, 0, AMD Radeon FirePro W8100
+67B0, 0, AMD Radeon R9 200 Series
+67B0, 80, AMD Radeon (TM) R9 390 Series
+67B1, 0, AMD Radeon R9 200 Series
+67B1, 80, AMD Radeon (TM) R9 390 Series
+67B9, 0, AMD Radeon R9 200 Series
+67DF, C1, Radeon RX 580 Series
+67DF, C2, Radeon RX 570 Series
+67DF, C3, Radeon RX 580 Series
+67DF, C4, AMD Radeon (TM) RX 480 Graphics
+67DF, C5, AMD Radeon (TM) RX 470 Graphics
+67DF, C6, Radeon RX 570 Series
+67DF, C7, AMD Radeon (TM) RX 480 Graphics
+67DF, CF, AMD Radeon (TM) RX 470 Graphics
+67DF, D7, Radeon(TM) RX 470 Graphics
+67DF, E0, Radeon RX 470 Series
+67DF, E1, Radeon RX 590 Series
+67DF, E3, Radeon RX Series
+67DF, E7, Radeon RX 580 Series
+67DF, EF, Radeon RX 570 Series
+67DF, F7, Radeon RX P30PH
+67C2, 01, AMD Radeon (TM) Pro V7350x2
+67C2, 02, AMD Radeon (TM) Pro V7300X
+67C4, 00, AMD Radeon (TM) Pro WX 7100 Graphics
+67C7, 00, AMD Radeon (TM) Pro WX 5100 Graphics
+67C0, 00, AMD Radeon (TM) Pro WX 7100 Graphics
+67D0, 01, AMD Radeon (TM) Pro V7350x2
+67D0, 02, AMD Radeon (TM) Pro V7300X
+67E0, 00, AMD Radeon (TM) Pro WX Series
+67E3, 00, AMD Radeon (TM) Pro WX 4100
+67E8, 00, AMD Radeon (TM) Pro WX Series
+67E8, 01, AMD Radeon (TM) Pro WX Series
+67E8, 80, AMD Radeon (TM) E9260 Graphics
+67EB, 00, AMD Radeon (TM) Pro V5300X
+67EF, C0, AMD Radeon (TM) RX Graphics
+67EF, C1, AMD Radeon (TM) RX 460 Graphics
+67EF, C3, Radeon RX Series
+67EF, C5, AMD Radeon (TM) RX 460 Graphics
+67EF, C7, AMD Radeon (TM) RX Graphics
+67EF, CF, AMD Radeon (TM) RX 460 Graphics
+67EF, E2, RX 560X
+67EF, E0, Radeon RX 560 Series
+67EF, E1, Radeon RX Series
+67EF, E3, Radeon RX Series
+67EF, E5, Radeon RX 560 Series
+67EF, EF, AMD Radeon (TM) RX Graphics
+67EF, FF, Radeon(TM) RX 460 Graphics
+67FF, C0, AMD Radeon (TM) RX Graphics
+67FF, C1, AMD Radeon (TM) RX Graphics
+67FF, CF, Radeon RX 560 Series
+67FF, EF, Radeon RX 560 Series
+67FF, FF, Radeon RX 550 Series
+6800, 0, AMD Radeon HD 7970M
+6801, 0, AMD Radeon(TM) HD8970M
+6808, 0, ATI FirePro V(FireGL V) Graphics Adapter
+6809, 0, ATI FirePro V(FireGL V) Graphics Adapter
+6810, 0, AMD Radeon(TM) HD 8800 Series
+6810, 81, AMD Radeon (TM) R7 370 Series
+6811, 0, AMD Radeon(TM) HD8800 Series
+6811, 81, AMD Radeon (TM) R7 300 Series
+6818, 0, AMD Radeon HD 7800 Series
+6819, 0, AMD Radeon HD 7800 Series
+6820, 0, AMD Radeon HD 8800M Series
+6820, 81, AMD Radeon (TM) R9 M375
+6820, 83, AMD Radeon (TM) R9 M375X
+6821, 0, AMD Radeon HD 8800M Series
+6821, 87, AMD Radeon (TM) R7 M380
+6821, 83, AMD Radeon R9 (TM) M370X
+6822, 0, AMD Radeon E8860
+6823, 0, AMD Radeon HD 8800M Series
+6825, 0, AMD Radeon HD 7800M Series
+6827, 0, AMD Radeon HD 7800M Series
+6828, 0, ATI FirePro V(FireGL V) Graphics Adapter
+682B, 0, AMD Radeon HD 8800M Series
+682B, 87, AMD Radeon (TM) R9 M360
+682C, 0, AMD FirePro W4100
+682D, 0, AMD Radeon HD 7700M Series
+682F, 0, AMD Radeon HD 7700M Series
+6835, 0, AMD Radeon R7 Series / HD 9000 Series
+6837, 0, AMD Radeon HD7700 Series
+683D, 0, AMD Radeon HD 7700 Series
+683F, 0, AMD Radeon HD 7700 Series
+6860, 00, Radeon Instinct MI25
+6860, 01, Radeon Instinct MI25
+6860, 02, Radeon Instinct MI25
+6860, 03, Radeon Pro V340
+6860, 04, Radeon Instinct MI25x2
+6860, 07, Radeon (TM) Pro V320
+6861, 00, Radeon Pro WX 9100
+6862, 00, Radeon Pro SSG
+6863, 00, Radeon Vega Frontier Edition
+6864, 03, Radeon Pro V340
+6864, 04, Instinct MI25x2
+6868, 00, Radeon (TM) PRO WX 8200
+686C, 00, Radeon Instinct MI25 MxGPU
+686C, 01, Radeon Instinct MI25 MxGPU
+686C, 02, Radeon Instinct MI25 MxGPU
+686C, 03, Radeon Pro V340 MxGPU
+686C, 04, Radeon Instinct MI25x2 MxGPU
+686C, 05, Radeon Pro V340L MxGPU
+686C, 06, Radeon Instinct MI25 MxGPU
+687F, C0, Radeon RX Vega
+687F, C1, Radeon RX Vega
+687F, C3, Radeon RX Vega
+6900, 0, AMD Radeon R7 M260
+6900, 81, AMD Radeon (TM) R7 M360
+6900, 83, AMD Radeon (TM) R7 M340
+6901, 0, AMD Radeon R5 M255
+6907, 0, AMD Radeon R5 M255
+6907, 87, AMD Radeon (TM) R5 M315
+6920, 0, AMD RADEON R9 M395X
+6920, 1, AMD RADEON R9 M390X
+6921, 0, AMD Radeon R9 M295X
+6929, 0, AMD FirePro S7150
+692B, 0, AMD FirePro W7100
+6938, 0, AMD Radeon R9 200 Series
+6938, F0, AMD Radeon R9 200 Series
+6938, F1, AMD Radeon (TM) R9 380 Series
+6939, F0, AMD Radeon R9 200 Series
+6939, 0, AMD Radeon R9 200 Series
+6939, F1, AMD Radeon (TM) R9 380 Series
+6980, 00, Radeon Pro WX3100
+6981, 00, AMD Radeon (TM) Pro WX 3200 Series
+6981, 01, AMD Radeon (TM) Pro WX 3200 Series
+6981, 10, AMD Radeon (TM) Pro WX 3200 Series
+6985, 00, AMD Radeon Pro WX3100
+6987, 80, AMD Embedded Radeon E9171
+6987, C0, Radeon 550X Series
+6987, C3, Radeon 540X Series
+6995, 00, AMD Radeon Pro WX2100
+6997, 00, Radeon Pro WX2100
+699F, 81, AMD Embedded Radeon E9170 Series
+699F, C0, Radeon 500 Series
+699F, C1, Radeon 540 Series
+699F, C3, Radeon 500 Series
+699F, C7, Radeon RX550/550 Series
+7300, C1, AMD FirePro (TM) S9300 x2
+7300, C8, AMD Radeon (TM) R9 Fury Series
+7300, C9, Radeon (TM) Pro Duo
+7300, CB, AMD Radeon (TM) R9 Fury Series
+7300, CA, AMD Radeon (TM) R9 Fury Series
+7312, 00, AMD Radeon Pro W5700
+731F, C0, AMD Radeon RX 5700 XT 50th Anniversary
+731F, C1, AMD Radeon RX 5700 XT
+731F, C2, AMD Radeon RX 5600M
+731F, C3, AMD Radeon RX 5700M
+731F, C4, AMD Radeon RX 5700
+731F, C5, AMD Radeon RX 5700 XT
+731F, CA, AMD Radeon RX 5600 XT
+731F, CB, AMD Radeon RX 5600 OEM
+7340, C1, Radeon RX 5500M
+7340, C5, Radeon RX 5500 XT
+7340, C7, Radeon RX 5500
+7341, 00, AMD Radeon Pro W5500
+7347, 00, AMD Radeon Pro W5500M
+9874, C4, AMD Radeon R7 Graphics
+9874, C5, AMD Radeon R6 Graphics
+9874, C6, AMD Radeon R6 Graphics
+9874, C7, AMD Radeon R5 Graphics
+9874, C8, AMD Radeon R7 Graphics
+9874, 81, AMD Radeon R6 Graphics
+9874, 87, AMD Radeon R5 Graphics
+9874, 85, AMD Radeon R6 Graphics
+9874, 84, AMD Radeon R7 Graphics
+6FDF, EF, AMD Radeon RX 580 2048SP
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..9c26b66
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,27 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+if with_amdgpu
+ install_data(
+ 'amdgpu.ids',
+ install_mode : 'rw-r--r--',
+ install_dir : datadir_amdgpu,
+ )
+endif
diff --git a/etnaviv/Android.mk b/etnaviv/Android.mk
new file mode 100644
index 0000000..390f9a9
--- /dev/null
+++ b/etnaviv/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_ETNAVIV_FILES, LIBDRM_ETNAVIV_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_etnaviv
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_ETNAVIV_FILES)
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/etnaviv/Makefile.sources b/etnaviv/Makefile.sources
new file mode 100644
index 0000000..0eb7378
--- /dev/null
+++ b/etnaviv/Makefile.sources
@@ -0,0 +1,13 @@
+LIBDRM_ETNAVIV_FILES := \
+ etnaviv_device.c \
+ etnaviv_gpu.c \
+ etnaviv_bo.c \
+ etnaviv_bo_cache.c \
+ etnaviv_perfmon.c \
+ etnaviv_pipe.c \
+ etnaviv_cmd_stream.c \
+ etnaviv_drm.h \
+ etnaviv_priv.h
+
+LIBDRM_ETNAVIV_H_FILES := \
+ etnaviv_drmif.h
diff --git a/etnaviv/etnaviv-symbols.txt b/etnaviv/etnaviv-symbols.txt
new file mode 100644
index 0000000..f48cece
--- /dev/null
+++ b/etnaviv/etnaviv-symbols.txt
@@ -0,0 +1,36 @@
+etna_device_new
+etna_device_new_dup
+etna_device_ref
+etna_device_del
+etna_device_fd
+etna_gpu_new
+etna_gpu_del
+etna_gpu_get_param
+etna_pipe_new
+etna_pipe_del
+etna_pipe_wait
+etna_pipe_wait_ns
+etna_bo_new
+etna_bo_from_name
+etna_bo_from_dmabuf
+etna_bo_ref
+etna_bo_del
+etna_bo_get_name
+etna_bo_handle
+etna_bo_dmabuf
+etna_bo_size
+etna_bo_map
+etna_bo_cpu_prep
+etna_bo_cpu_fini
+etna_cmd_stream_new
+etna_cmd_stream_del
+etna_cmd_stream_timestamp
+etna_cmd_stream_flush
+etna_cmd_stream_flush2
+etna_cmd_stream_finish
+etna_cmd_stream_perf
+etna_cmd_stream_reloc
+etna_perfmon_create
+etna_perfmon_del
+etna_perfmon_get_dom_by_name
+etna_perfmon_get_sig_by_name
diff --git a/etnaviv/etnaviv_bo.c b/etnaviv/etnaviv_bo.c
new file mode 100644
index 0000000..43ce6b4
--- /dev/null
+++ b/etnaviv/etnaviv_bo.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private void bo_del(struct etna_bo *bo);
+
+/* set buffer name, and add to table, call w/ table_lock held: */
+static void set_name(struct etna_bo *bo, uint32_t name)
+{
+ bo->name = name;
+ /* add ourself into the name table: */
+ drmHashInsert(bo->dev->name_table, name, bo);
+}
+
+/* Called under table_lock */
+drm_private void bo_del(struct etna_bo *bo)
+{
+ if (bo->map)
+ drm_munmap(bo->map, bo->size);
+
+ if (bo->name)
+ drmHashDelete(bo->dev->name_table, bo->name);
+
+ if (bo->handle) {
+ struct drm_gem_close req = {
+ .handle = bo->handle,
+ };
+
+ drmHashDelete(bo->dev->handle_table, bo->handle);
+ drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+ }
+
+ free(bo);
+}
+
+/* lookup a buffer from it's handle, call w/ table_lock held: */
+static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
+{
+ struct etna_bo *bo = NULL;
+
+ if (!drmHashLookup(tbl, handle, (void **)&bo)) {
+ /* found, incr refcnt and return: */
+ bo = etna_bo_ref(bo);
+
+ /* don't break the bucket if this bo was found in one */
+ list_delinit(&bo->list);
+ }
+
+ return bo;
+}
+
+/* allocate a new buffer object, call w/ table_lock held */
+static struct etna_bo *bo_from_handle(struct etna_device *dev,
+ uint32_t size, uint32_t handle, uint32_t flags)
+{
+ struct etna_bo *bo = calloc(sizeof(*bo), 1);
+
+ if (!bo) {
+ struct drm_gem_close req = {
+ .handle = handle,
+ };
+
+ drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+
+ return NULL;
+ }
+
+ bo->dev = etna_device_ref(dev);
+ bo->size = size;
+ bo->handle = handle;
+ bo->flags = flags;
+ atomic_set(&bo->refcnt, 1);
+ list_inithead(&bo->list);
+ /* add ourselves to the handle table: */
+ drmHashInsert(dev->handle_table, handle, bo);
+
+ return bo;
+}
+
+/* allocate a new (un-tiled) buffer object */
+drm_public struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
+ uint32_t flags)
+{
+ struct etna_bo *bo;
+ int ret;
+ struct drm_etnaviv_gem_new req = {
+ .flags = flags,
+ };
+
+ bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
+ if (bo)
+ return bo;
+
+ req.size = size;
+ ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
+ &req, sizeof(req));
+ if (ret)
+ return NULL;
+
+ pthread_mutex_lock(&table_lock);
+ bo = bo_from_handle(dev, size, req.handle, flags);
+ bo->reuse = 1;
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+drm_public struct etna_bo *etna_bo_ref(struct etna_bo *bo)
+{
+ atomic_inc(&bo->refcnt);
+
+ return bo;
+}
+
+/* get buffer info */
+static int get_buffer_info(struct etna_bo *bo)
+{
+ int ret;
+ struct drm_etnaviv_gem_info req = {
+ .handle = bo->handle,
+ };
+
+ ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
+ &req, sizeof(req));
+ if (ret) {
+ return ret;
+ }
+
+ /* really all we need for now is mmap offset */
+ bo->offset = req.offset;
+
+ return 0;
+}
+
+/* import a buffer object from DRI2 name */
+drm_public struct etna_bo *etna_bo_from_name(struct etna_device *dev,
+ uint32_t name)
+{
+ struct etna_bo *bo;
+ struct drm_gem_open req = {
+ .name = name,
+ };
+
+ pthread_mutex_lock(&table_lock);
+
+ /* check name table first, to see if bo is already open: */
+ bo = lookup_bo(dev->name_table, name);
+ if (bo)
+ goto out_unlock;
+
+ if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+ ERROR_MSG("gem-open failed: %s", strerror(errno));
+ goto out_unlock;
+ }
+
+ bo = lookup_bo(dev->handle_table, req.handle);
+ if (bo)
+ goto out_unlock;
+
+ bo = bo_from_handle(dev, req.size, req.handle, 0);
+ if (bo)
+ set_name(bo, name);
+
+out_unlock:
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+/* import a buffer from dmabuf fd, does not take ownership of the
+ * fd so caller should close() the fd when it is otherwise done
+ * with it (even if it is still using the 'struct etna_bo *')
+ */
+drm_public struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
+{
+ struct etna_bo *bo;
+ int ret, size;
+ uint32_t handle;
+
+ /* take the lock before calling drmPrimeFDToHandle to avoid
+ * racing against etna_bo_del, which might invalidate the
+ * returned handle.
+ */
+ pthread_mutex_lock(&table_lock);
+
+ ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
+ if (ret) {
+ pthread_mutex_unlock(&table_lock);
+ return NULL;
+ }
+
+ bo = lookup_bo(dev->handle_table, handle);
+ if (bo)
+ goto out_unlock;
+
+ /* lseek() to get bo size */
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_CUR);
+
+ bo = bo_from_handle(dev, size, handle, 0);
+
+out_unlock:
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+/* destroy a buffer object */
+drm_public void etna_bo_del(struct etna_bo *bo)
+{
+ struct etna_device *dev = bo->dev;
+
+ if (!bo)
+ return;
+
+ if (!atomic_dec_and_test(&bo->refcnt))
+ return;
+
+ pthread_mutex_lock(&table_lock);
+
+ if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
+ goto out;
+
+ bo_del(bo);
+ etna_device_del_locked(dev);
+out:
+ pthread_mutex_unlock(&table_lock);
+}
+
+/* get the global flink/DRI2 buffer name */
+drm_public int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
+{
+ if (!bo->name) {
+ struct drm_gem_flink req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
+ if (ret) {
+ return ret;
+ }
+
+ pthread_mutex_lock(&table_lock);
+ set_name(bo, req.name);
+ pthread_mutex_unlock(&table_lock);
+ bo->reuse = 0;
+ }
+
+ *name = bo->name;
+
+ return 0;
+}
+
+drm_public uint32_t etna_bo_handle(struct etna_bo *bo)
+{
+ return bo->handle;
+}
+
+/* caller owns the dmabuf fd that is returned and is responsible
+ * to close() it when done
+ */
+drm_public int etna_bo_dmabuf(struct etna_bo *bo)
+{
+ int ret, prime_fd;
+
+ ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+ &prime_fd);
+ if (ret) {
+ ERROR_MSG("failed to get dmabuf fd: %d", ret);
+ return ret;
+ }
+
+ bo->reuse = 0;
+
+ return prime_fd;
+}
+
+drm_public uint32_t etna_bo_size(struct etna_bo *bo)
+{
+ return bo->size;
+}
+
+drm_public void *etna_bo_map(struct etna_bo *bo)
+{
+ if (!bo->map) {
+ if (!bo->offset) {
+ get_buffer_info(bo);
+ }
+
+ bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, bo->dev->fd, bo->offset);
+ if (bo->map == MAP_FAILED) {
+ ERROR_MSG("mmap failed: %s", strerror(errno));
+ bo->map = NULL;
+ }
+ }
+
+ return bo->map;
+}
+
+drm_public int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
+{
+ struct drm_etnaviv_gem_cpu_prep req = {
+ .handle = bo->handle,
+ .op = op,
+ };
+
+ get_abs_timeout(&req.timeout, 5000000000);
+
+ return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
+ &req, sizeof(req));
+}
+
+drm_public void etna_bo_cpu_fini(struct etna_bo *bo)
+{
+ struct drm_etnaviv_gem_cpu_fini req = {
+ .handle = bo->handle,
+ };
+
+ drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
+ &req, sizeof(req));
+}
diff --git a/etnaviv/etnaviv_bo_cache.c b/etnaviv/etnaviv_bo_cache.c
new file mode 100644
index 0000000..c81de26
--- /dev/null
+++ b/etnaviv/etnaviv_bo_cache.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2016 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private void bo_del(struct etna_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void add_bucket(struct etna_bo_cache *cache, int size)
+{
+ unsigned i = cache->num_buckets;
+
+ assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+ list_inithead(&cache->cache_bucket[i].list);
+ cache->cache_bucket[i].size = size;
+ cache->num_buckets++;
+}
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache)
+{
+ unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+ /* OK, so power of two buckets was too wasteful of memory.
+ * Give 3 other sizes between each power of two, to hopefully
+ * cover things accurately enough. (The alternative is
+ * probably to just go for exact matching of sizes, and assume
+ * that for things like composited window resize the tiled
+ * width/height alignment and rounding of sizes to pages will
+ * get us useful cache hit rates anyway)
+ */
+ add_bucket(cache, 4096);
+ add_bucket(cache, 4096 * 2);
+ add_bucket(cache, 4096 * 3);
+
+ /* Initialize the linked lists for BO reuse cache. */
+ for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+ add_bucket(cache, size);
+ add_bucket(cache, size + size * 1 / 4);
+ add_bucket(cache, size + size * 2 / 4);
+ add_bucket(cache, size + size * 3 / 4);
+ }
+}
+
+/* Frees older cached buffers. Called under table_lock */
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time)
+{
+ unsigned i;
+
+ if (cache->time == time)
+ return;
+
+ for (i = 0; i < cache->num_buckets; i++) {
+ struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+ struct etna_bo *bo;
+
+ while (!LIST_IS_EMPTY(&bucket->list)) {
+ bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
+
+ /* keep things in cache for at least 1 second: */
+ if (time && ((time - bo->free_time) <= 1))
+ break;
+
+ list_del(&bo->list);
+ bo_del(bo);
+ }
+ }
+
+ cache->time = time;
+}
+
+static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size)
+{
+ unsigned i;
+
+ /* hmm, this is what intel does, but I suppose we could calculate our
+ * way to the correct bucket size rather than looping..
+ */
+ for (i = 0; i < cache->num_buckets; i++) {
+ struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+ if (bucket->size >= size) {
+ return bucket;
+ }
+ }
+
+ return NULL;
+}
+
+static int is_idle(struct etna_bo *bo)
+{
+ return etna_bo_cpu_prep(bo,
+ DRM_ETNA_PREP_READ |
+ DRM_ETNA_PREP_WRITE |
+ DRM_ETNA_PREP_NOSYNC) == 0;
+}
+
+static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags)
+{
+ struct etna_bo *bo = NULL, *tmp;
+
+ pthread_mutex_lock(&table_lock);
+
+ if (LIST_IS_EMPTY(&bucket->list))
+ goto out_unlock;
+
+ LIST_FOR_EACH_ENTRY_SAFE(bo, tmp, &bucket->list, list) {
+ /* skip BOs with different flags */
+ if (bo->flags != flags)
+ continue;
+
+ /* check if the first BO with matching flags is idle */
+ if (is_idle(bo)) {
+ list_delinit(&bo->list);
+ goto out_unlock;
+ }
+
+ /* If the oldest BO is still busy, don't try younger ones */
+ break;
+ }
+
+ /* There was no matching buffer found */
+ bo = NULL;
+
+out_unlock:
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+/* allocate a new (un-tiled) buffer object
+ *
+ * NOTE: size is potentially rounded up to bucket size
+ */
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size,
+ uint32_t flags)
+{
+ struct etna_bo *bo;
+ struct etna_bo_bucket *bucket;
+
+ *size = ALIGN(*size, 4096);
+ bucket = get_bucket(cache, *size);
+
+ /* see if we can be green and recycle: */
+ if (bucket) {
+ *size = bucket->size;
+ bo = find_in_bucket(bucket, flags);
+ if (bo) {
+ atomic_set(&bo->refcnt, 1);
+ etna_device_ref(bo->dev);
+ return bo;
+ }
+ }
+
+ return NULL;
+}
+
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo)
+{
+ struct etna_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+ /* see if we can be green and recycle: */
+ if (bucket) {
+ struct timespec time;
+
+ clock_gettime(CLOCK_MONOTONIC, &time);
+
+ bo->free_time = time.tv_sec;
+ list_addtail(&bo->list, &bucket->list);
+ etna_bo_cache_cleanup(cache, time.tv_sec);
+
+ /* bo's in the bucket cache don't have a ref and
+ * don't hold a ref to the dev:
+ */
+ etna_device_del_locked(bo->dev);
+
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/etnaviv/etnaviv_cmd_stream.c b/etnaviv/etnaviv_cmd_stream.c
new file mode 100644
index 0000000..261777b
--- /dev/null
+++ b/etnaviv/etnaviv_cmd_stream.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include <assert.h>
+
+#include "etnaviv_drmif.h"
+#include "etnaviv_priv.h"
+
+static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
+{
+ if ((nr + 1) > *max) {
+ if ((*max * 2) < (nr + 1))
+ *max = nr + 5;
+ else
+ *max = *max * 2;
+ ptr = realloc(ptr, *max * sz);
+ }
+
+ return ptr;
+}
+
+#define APPEND(x, name) ({ \
+ (x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
+ (x)->nr_ ## name ++; \
+})
+
+static inline struct etna_cmd_stream_priv *
+etna_cmd_stream_priv(struct etna_cmd_stream *stream)
+{
+ return (struct etna_cmd_stream_priv *)stream;
+}
+
+drm_public struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe,
+ uint32_t size,
+ void (*reset_notify)(struct etna_cmd_stream *stream, void *priv),
+ void *priv)
+{
+ struct etna_cmd_stream_priv *stream = NULL;
+
+ if (size == 0) {
+ ERROR_MSG("invalid size of 0");
+ goto fail;
+ }
+
+ stream = calloc(1, sizeof(*stream));
+ if (!stream) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ /* allocate even number of 32-bit words */
+ size = ALIGN(size, 2);
+
+ stream->base.buffer = malloc(size * sizeof(uint32_t));
+ if (!stream->base.buffer) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ stream->base.size = size;
+ stream->pipe = pipe;
+ stream->reset_notify = reset_notify;
+ stream->reset_notify_priv = priv;
+
+ return &stream->base;
+
+fail:
+ if (stream)
+ etna_cmd_stream_del(&stream->base);
+
+ return NULL;
+}
+
+drm_public void etna_cmd_stream_del(struct etna_cmd_stream *stream)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+ free(stream->buffer);
+ free(priv->submit.relocs);
+ free(priv->submit.pmrs);
+ free(priv);
+}
+
+static void reset_buffer(struct etna_cmd_stream *stream)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+ stream->offset = 0;
+ priv->submit.nr_bos = 0;
+ priv->submit.nr_relocs = 0;
+ priv->submit.nr_pmrs = 0;
+ priv->nr_bos = 0;
+
+ if (priv->reset_notify)
+ priv->reset_notify(stream, priv->reset_notify_priv);
+}
+
+drm_public uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream)
+{
+ return etna_cmd_stream_priv(stream)->last_timestamp;
+}
+
+static uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+ uint32_t idx;
+
+ idx = APPEND(&priv->submit, bos);
+ idx = APPEND(priv, bos);
+
+ priv->submit.bos[idx].flags = 0;
+ priv->submit.bos[idx].handle = bo->handle;
+
+ priv->bos[idx] = etna_bo_ref(bo);
+
+ return idx;
+}
+
+/* add (if needed) bo, return idx: */
+static uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo,
+ uint32_t flags)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+ uint32_t idx;
+
+ pthread_mutex_lock(&idx_lock);
+
+ if (bo->current_stream == stream) {
+ idx = bo->idx;
+ } else {
+ /* slow-path: */
+ for (idx = 0; idx < priv->nr_bos; idx++)
+ if (priv->bos[idx] == bo)
+ break;
+ if (idx == priv->nr_bos) {
+ /* not found */
+ idx = append_bo(stream, bo);
+ }
+ bo->current_stream = stream;
+ bo->idx = idx;
+ }
+ pthread_mutex_unlock(&idx_lock);
+
+ if (flags & ETNA_RELOC_READ)
+ priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ;
+ if (flags & ETNA_RELOC_WRITE)
+ priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE;
+
+ return idx;
+}
+
+static void flush(struct etna_cmd_stream *stream, int in_fence_fd,
+ int *out_fence_fd)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+ int ret, id = priv->pipe->id;
+ struct etna_gpu *gpu = priv->pipe->gpu;
+
+ struct drm_etnaviv_gem_submit req = {
+ .pipe = gpu->core,
+ .exec_state = id,
+ .bos = VOID2U64(priv->submit.bos),
+ .nr_bos = priv->submit.nr_bos,
+ .relocs = VOID2U64(priv->submit.relocs),
+ .nr_relocs = priv->submit.nr_relocs,
+ .pmrs = VOID2U64(priv->submit.pmrs),
+ .nr_pmrs = priv->submit.nr_pmrs,
+ .stream = VOID2U64(stream->buffer),
+ .stream_size = stream->offset * 4, /* in bytes */
+ };
+
+ if (in_fence_fd != -1) {
+ req.flags |= ETNA_SUBMIT_FENCE_FD_IN | ETNA_SUBMIT_NO_IMPLICIT;
+ req.fence_fd = in_fence_fd;
+ }
+
+ if (out_fence_fd)
+ req.flags |= ETNA_SUBMIT_FENCE_FD_OUT;
+
+ ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT,
+ &req, sizeof(req));
+
+ if (ret)
+ ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
+ else
+ priv->last_timestamp = req.fence;
+
+ for (uint32_t i = 0; i < priv->nr_bos; i++) {
+ struct etna_bo *bo = priv->bos[i];
+
+ bo->current_stream = NULL;
+ etna_bo_del(bo);
+ }
+
+ if (out_fence_fd)
+ *out_fence_fd = req.fence_fd;
+}
+
+drm_public void etna_cmd_stream_flush(struct etna_cmd_stream *stream)
+{
+ flush(stream, -1, NULL);
+ reset_buffer(stream);
+}
+
+drm_public void etna_cmd_stream_flush2(struct etna_cmd_stream *stream,
+ int in_fence_fd,
+ int *out_fence_fd)
+{
+ flush(stream, in_fence_fd, out_fence_fd);
+ reset_buffer(stream);
+}
+
+drm_public void etna_cmd_stream_finish(struct etna_cmd_stream *stream)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+ flush(stream, -1, NULL);
+ etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000);
+ reset_buffer(stream);
+}
+
+drm_public void etna_cmd_stream_reloc(struct etna_cmd_stream *stream,
+ const struct etna_reloc *r)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+ struct drm_etnaviv_gem_submit_reloc *reloc;
+ uint32_t idx = APPEND(&priv->submit, relocs);
+ uint32_t addr = 0;
+
+ reloc = &priv->submit.relocs[idx];
+
+ reloc->reloc_idx = bo2idx(stream, r->bo, r->flags);
+ reloc->reloc_offset = r->offset;
+ reloc->submit_offset = stream->offset * 4; /* in bytes */
+ reloc->flags = 0;
+
+ etna_cmd_stream_emit(stream, addr);
+}
+
+drm_public void etna_cmd_stream_perf(struct etna_cmd_stream *stream, const struct etna_perf *p)
+{
+ struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+ struct drm_etnaviv_gem_submit_pmr *pmr;
+ uint32_t idx = APPEND(&priv->submit, pmrs);
+
+ pmr = &priv->submit.pmrs[idx];
+
+ pmr->flags = p->flags;
+ pmr->sequence = p->sequence;
+ pmr->read_offset = p->offset;
+ pmr->read_idx = bo2idx(stream, p->bo, ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE);
+ pmr->domain = p->signal->domain->id;
+ pmr->signal = p->signal->signal;
+}
diff --git a/etnaviv/etnaviv_device.c b/etnaviv/etnaviv_device.c
new file mode 100644
index 0000000..699df25
--- /dev/null
+++ b/etnaviv/etnaviv_device.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include <stdlib.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <xf86drm.h>
+#include <xf86atomic.h>
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+
+drm_public struct etna_device *etna_device_new(int fd)
+{
+ struct etna_device *dev = calloc(sizeof(*dev), 1);
+
+ if (!dev)
+ return NULL;
+
+ atomic_set(&dev->refcnt, 1);
+ dev->fd = fd;
+ dev->handle_table = drmHashCreate();
+ dev->name_table = drmHashCreate();
+ etna_bo_cache_init(&dev->bo_cache);
+
+ return dev;
+}
+
+/* like etna_device_new() but creates it's own private dup() of the fd
+ * which is close()d when the device is finalized. */
+drm_public struct etna_device *etna_device_new_dup(int fd)
+{
+ int dup_fd = dup(fd);
+ struct etna_device *dev = etna_device_new(dup_fd);
+
+ if (dev)
+ dev->closefd = 1;
+ else
+ close(dup_fd);
+
+ return dev;
+}
+
+drm_public struct etna_device *etna_device_ref(struct etna_device *dev)
+{
+ atomic_inc(&dev->refcnt);
+
+ return dev;
+}
+
+static void etna_device_del_impl(struct etna_device *dev)
+{
+ etna_bo_cache_cleanup(&dev->bo_cache, 0);
+ drmHashDestroy(dev->handle_table);
+ drmHashDestroy(dev->name_table);
+
+ if (dev->closefd)
+ close(dev->fd);
+
+ free(dev);
+}
+
+drm_private void etna_device_del_locked(struct etna_device *dev)
+{
+ if (!atomic_dec_and_test(&dev->refcnt))
+ return;
+
+ etna_device_del_impl(dev);
+}
+
+drm_public void etna_device_del(struct etna_device *dev)
+{
+ if (!atomic_dec_and_test(&dev->refcnt))
+ return;
+
+ pthread_mutex_lock(&table_lock);
+ etna_device_del_impl(dev);
+ pthread_mutex_unlock(&table_lock);
+}
+
+drm_public int etna_device_fd(struct etna_device *dev)
+{
+ return dev->fd;
+}
diff --git a/etnaviv/etnaviv_drm.h b/etnaviv/etnaviv_drm.h
new file mode 100644
index 0000000..0d5c49d
--- /dev/null
+++ b/etnaviv/etnaviv_drm.h
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ETNAVIV_DRM_H__
+#define __ETNAVIV_DRM_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints:
+ * 1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
+ * user/kernel compatibility
+ * 2) Keep fields aligned to their size
+ * 3) Because of how drm_ioctl() works, we can add new fields at
+ * the end of an ioctl if some care is taken: drm_ioctl() will
+ * zero out the new fields at the tail of the ioctl, so a zero
+ * value should have a backwards compatible meaning. And for
+ * output params, userspace won't see the newly added output
+ * fields.. so that has to be somehow ok.
+ */
+
+/* timeouts are specified in clock-monotonic absolute times (to simplify
+ * restarting interrupted ioctls). The following struct is logically the
+ * same as 'struct timespec' but 32/64b ABI safe.
+ */
+struct drm_etnaviv_timespec {
+ __s64 tv_sec; /* seconds */
+ __s64 tv_nsec; /* nanoseconds */
+};
+
+#define ETNAVIV_PARAM_GPU_MODEL 0x01
+#define ETNAVIV_PARAM_GPU_REVISION 0x02
+#define ETNAVIV_PARAM_GPU_FEATURES_0 0x03
+#define ETNAVIV_PARAM_GPU_FEATURES_1 0x04
+#define ETNAVIV_PARAM_GPU_FEATURES_2 0x05
+#define ETNAVIV_PARAM_GPU_FEATURES_3 0x06
+#define ETNAVIV_PARAM_GPU_FEATURES_4 0x07
+#define ETNAVIV_PARAM_GPU_FEATURES_5 0x08
+#define ETNAVIV_PARAM_GPU_FEATURES_6 0x09
+#define ETNAVIV_PARAM_GPU_FEATURES_7 0x0a
+#define ETNAVIV_PARAM_GPU_FEATURES_8 0x0b
+#define ETNAVIV_PARAM_GPU_FEATURES_9 0x0c
+#define ETNAVIV_PARAM_GPU_FEATURES_10 0x0d
+#define ETNAVIV_PARAM_GPU_FEATURES_11 0x0e
+#define ETNAVIV_PARAM_GPU_FEATURES_12 0x0f
+
+#define ETNAVIV_PARAM_GPU_STREAM_COUNT 0x10
+#define ETNAVIV_PARAM_GPU_REGISTER_MAX 0x11
+#define ETNAVIV_PARAM_GPU_THREAD_COUNT 0x12
+#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE 0x13
+#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT 0x14
+#define ETNAVIV_PARAM_GPU_PIXEL_PIPES 0x15
+#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16
+#define ETNAVIV_PARAM_GPU_BUFFER_SIZE 0x17
+#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18
+#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19
+#define ETNAVIV_PARAM_GPU_NUM_VARYINGS 0x1a
+
+#define ETNA_MAX_PIPES 4
+
+struct drm_etnaviv_param {
+ __u32 pipe; /* in */
+ __u32 param; /* in, ETNAVIV_PARAM_x */
+ __u64 value; /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define ETNA_BO_CACHE_MASK 0x000f0000
+/* cache modes */
+#define ETNA_BO_CACHED 0x00010000
+#define ETNA_BO_WC 0x00020000
+#define ETNA_BO_UNCACHED 0x00040000
+/* map flags */
+#define ETNA_BO_FORCE_MMU 0x00100000
+
+struct drm_etnaviv_gem_new {
+ __u64 size; /* in */
+ __u32 flags; /* in, mask of ETNA_BO_x */
+ __u32 handle; /* out */
+};
+
+struct drm_etnaviv_gem_info {
+ __u32 handle; /* in */
+ __u32 pad;
+ __u64 offset; /* out, offset to pass to mmap() */
+};
+
+#define ETNA_PREP_READ 0x01
+#define ETNA_PREP_WRITE 0x02
+#define ETNA_PREP_NOSYNC 0x04
+
+struct drm_etnaviv_gem_cpu_prep {
+ __u32 handle; /* in */
+ __u32 op; /* in, mask of ETNA_PREP_x */
+ struct drm_etnaviv_timespec timeout; /* in */
+};
+
+struct drm_etnaviv_gem_cpu_fini {
+ __u32 handle; /* in */
+ __u32 flags; /* in, placeholder for now, no defined values */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ * relocbuf->gpuaddr + reloc_offset
+ *
+ * NOTE that reloc's must be sorted by order of increasing submit_offset,
+ * otherwise EINVAL.
+ */
+struct drm_etnaviv_gem_submit_reloc {
+ __u32 submit_offset; /* in, offset from submit_bo */
+ __u32 reloc_idx; /* in, index of reloc_bo buffer */
+ __u64 reloc_offset; /* in, offset from start of reloc_bo */
+ __u32 flags; /* in, placeholder for now, no defined values */
+};
+
+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
+ * cmdstream buffer(s) themselves or reloc entries) has one (and only
+ * one) entry in the submit->bos[] table.
+ *
+ * As a optimization, the current buffer (gpu virtual address) can be
+ * passed back through the 'presumed' field. If on a subsequent reloc,
+ * userspace passes back a 'presumed' address that is still valid,
+ * then patching the cmdstream for this entry is skipped. This can
+ * avoid kernel needing to map/access the cmdstream bo in the common
+ * case.
+ */
+#define ETNA_SUBMIT_BO_READ 0x0001
+#define ETNA_SUBMIT_BO_WRITE 0x0002
+struct drm_etnaviv_gem_submit_bo {
+ __u32 flags; /* in, mask of ETNA_SUBMIT_BO_x */
+ __u32 handle; /* in, GEM handle */
+ __u64 presumed; /* in/out, presumed buffer address */
+};
+
+/* performance monitor request (pmr) */
+#define ETNA_PM_PROCESS_PRE 0x0001
+#define ETNA_PM_PROCESS_POST 0x0002
+struct drm_etnaviv_gem_submit_pmr {
+ __u32 flags; /* in, when to process request (ETNA_PM_PROCESS_x) */
+ __u8 domain; /* in, pm domain */
+ __u8 pad;
+ __u16 signal; /* in, pm signal */
+ __u32 sequence; /* in, sequence number */
+ __u32 read_offset; /* in, offset from read_bo */
+ __u32 read_idx; /* in, index of read_bo buffer */
+};
+
+/* Each cmdstream submit consists of a table of buffers involved, and
+ * one or more cmdstream buffers. This allows for conditional execution
+ * (context-restore), and IB buffers needed for per tile/bin draw cmds.
+ */
+#define ETNA_SUBMIT_NO_IMPLICIT 0x0001
+#define ETNA_SUBMIT_FENCE_FD_IN 0x0002
+#define ETNA_SUBMIT_FENCE_FD_OUT 0x0004
+#define ETNA_SUBMIT_FLAGS (ETNA_SUBMIT_NO_IMPLICIT | \
+ ETNA_SUBMIT_FENCE_FD_IN | \
+ ETNA_SUBMIT_FENCE_FD_OUT)
+#define ETNA_PIPE_3D 0x00
+#define ETNA_PIPE_2D 0x01
+#define ETNA_PIPE_VG 0x02
+struct drm_etnaviv_gem_submit {
+ __u32 fence; /* out */
+ __u32 pipe; /* in */
+ __u32 exec_state; /* in, initial execution state (ETNA_PIPE_x) */
+ __u32 nr_bos; /* in, number of submit_bo's */
+ __u32 nr_relocs; /* in, number of submit_reloc's */
+ __u32 stream_size; /* in, cmdstream size */
+ __u64 bos; /* in, ptr to array of submit_bo's */
+ __u64 relocs; /* in, ptr to array of submit_reloc's */
+ __u64 stream; /* in, ptr to cmdstream */
+ __u32 flags; /* in, mask of ETNA_SUBMIT_x */
+ __s32 fence_fd; /* in/out, fence fd (see ETNA_SUBMIT_FENCE_FD_x) */
+ __u64 pmrs; /* in, ptr to array of submit_pmr's */
+ __u32 nr_pmrs; /* in, number of submit_pmr's */
+ __u32 pad;
+};
+
+/* The normal way to synchronize with the GPU is just to CPU_PREP on
+ * a buffer if you need to access it from the CPU (other cmdstream
+ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all
+ * handle the required synchronization under the hood). This ioctl
+ * mainly just exists as a way to implement the gallium pipe_fence
+ * APIs without requiring a dummy bo to synchronize on.
+ */
+#define ETNA_WAIT_NONBLOCK 0x01
+struct drm_etnaviv_wait_fence {
+ __u32 pipe; /* in */
+ __u32 fence; /* in */
+ __u32 flags; /* in, mask of ETNA_WAIT_x */
+ __u32 pad;
+ struct drm_etnaviv_timespec timeout; /* in */
+};
+
+#define ETNA_USERPTR_READ 0x01
+#define ETNA_USERPTR_WRITE 0x02
+struct drm_etnaviv_gem_userptr {
+ __u64 user_ptr; /* in, page aligned user pointer */
+ __u64 user_size; /* in, page aligned user size */
+ __u32 flags; /* in, flags */
+ __u32 handle; /* out, non-zero handle */
+};
+
+struct drm_etnaviv_gem_wait {
+ __u32 pipe; /* in */
+ __u32 handle; /* in, bo to be waited for */
+ __u32 flags; /* in, mask of ETNA_WAIT_x */
+ __u32 pad;
+ struct drm_etnaviv_timespec timeout; /* in */
+};
+
+/*
+ * Performance Monitor (PM):
+ */
+
+struct drm_etnaviv_pm_domain {
+ __u32 pipe; /* in */
+ __u8 iter; /* in/out, select pm domain at index iter */
+ __u8 id; /* out, id of domain */
+ __u16 nr_signals; /* out, how many signals does this domain provide */
+ char name[64]; /* out, name of domain */
+};
+
+struct drm_etnaviv_pm_signal {
+ __u32 pipe; /* in */
+ __u8 domain; /* in, pm domain index */
+ __u8 pad;
+ __u16 iter; /* in/out, select pm source at index iter */
+ __u16 id; /* out, id of signal */
+ char name[64]; /* out, name of domain */
+};
+
+#define DRM_ETNAVIV_GET_PARAM 0x00
+/* placeholder:
+#define DRM_ETNAVIV_SET_PARAM 0x01
+ */
+#define DRM_ETNAVIV_GEM_NEW 0x02
+#define DRM_ETNAVIV_GEM_INFO 0x03
+#define DRM_ETNAVIV_GEM_CPU_PREP 0x04
+#define DRM_ETNAVIV_GEM_CPU_FINI 0x05
+#define DRM_ETNAVIV_GEM_SUBMIT 0x06
+#define DRM_ETNAVIV_WAIT_FENCE 0x07
+#define DRM_ETNAVIV_GEM_USERPTR 0x08
+#define DRM_ETNAVIV_GEM_WAIT 0x09
+#define DRM_ETNAVIV_PM_QUERY_DOM 0x0a
+#define DRM_ETNAVIV_PM_QUERY_SIG 0x0b
+#define DRM_ETNAVIV_NUM_IOCTLS 0x0c
+
+#define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param)
+#define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new)
+#define DRM_IOCTL_ETNAVIV_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
+#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
+#define DRM_IOCTL_ETNAVIV_WAIT_FENCE DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
+#define DRM_IOCTL_ETNAVIV_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr)
+#define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait)
+#define DRM_IOCTL_ETNAVIV_PM_QUERY_DOM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_DOM, struct drm_etnaviv_pm_domain)
+#define DRM_IOCTL_ETNAVIV_PM_QUERY_SIG DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_SIG, struct drm_etnaviv_pm_signal)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ETNAVIV_DRM_H__ */
diff --git a/etnaviv/etnaviv_drmif.h b/etnaviv/etnaviv_drmif.h
new file mode 100644
index 0000000..80aedc1
--- /dev/null
+++ b/etnaviv/etnaviv_drmif.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef ETNAVIV_DRMIF_H_
+#define ETNAVIV_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+
+struct etna_bo;
+struct etna_pipe;
+struct etna_gpu;
+struct etna_device;
+struct etna_cmd_stream;
+struct etna_perfmon;
+struct etna_perfmon_domain;
+struct etna_perfmon_signal;
+
+enum etna_pipe_id {
+ ETNA_PIPE_3D = 0,
+ ETNA_PIPE_2D = 1,
+ ETNA_PIPE_VG = 2,
+ ETNA_PIPE_MAX
+};
+
+enum etna_param_id {
+ ETNA_GPU_MODEL = 0x1,
+ ETNA_GPU_REVISION = 0x2,
+ ETNA_GPU_FEATURES_0 = 0x3,
+ ETNA_GPU_FEATURES_1 = 0x4,
+ ETNA_GPU_FEATURES_2 = 0x5,
+ ETNA_GPU_FEATURES_3 = 0x6,
+ ETNA_GPU_FEATURES_4 = 0x7,
+ ETNA_GPU_FEATURES_5 = 0x8,
+ ETNA_GPU_FEATURES_6 = 0x9,
+
+ ETNA_GPU_STREAM_COUNT = 0x10,
+ ETNA_GPU_REGISTER_MAX = 0x11,
+ ETNA_GPU_THREAD_COUNT = 0x12,
+ ETNA_GPU_VERTEX_CACHE_SIZE = 0x13,
+ ETNA_GPU_SHADER_CORE_COUNT = 0x14,
+ ETNA_GPU_PIXEL_PIPES = 0x15,
+ ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE = 0x16,
+ ETNA_GPU_BUFFER_SIZE = 0x17,
+ ETNA_GPU_INSTRUCTION_COUNT = 0x18,
+ ETNA_GPU_NUM_CONSTANTS = 0x19,
+ ETNA_GPU_NUM_VARYINGS = 0x1a
+};
+
+/* bo flags: */
+#define DRM_ETNA_GEM_CACHE_CACHED 0x00010000
+#define DRM_ETNA_GEM_CACHE_WC 0x00020000
+#define DRM_ETNA_GEM_CACHE_UNCACHED 0x00040000
+#define DRM_ETNA_GEM_CACHE_MASK 0x000f0000
+/* map flags */
+#define DRM_ETNA_GEM_FORCE_MMU 0x00100000
+
+/* bo access flags: (keep aligned to ETNA_PREP_x) */
+#define DRM_ETNA_PREP_READ 0x01
+#define DRM_ETNA_PREP_WRITE 0x02
+#define DRM_ETNA_PREP_NOSYNC 0x04
+
+/* device functions:
+ */
+
+struct etna_device *etna_device_new(int fd);
+struct etna_device *etna_device_new_dup(int fd);
+struct etna_device *etna_device_ref(struct etna_device *dev);
+void etna_device_del(struct etna_device *dev);
+int etna_device_fd(struct etna_device *dev);
+
+/* gpu functions:
+ */
+
+struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core);
+void etna_gpu_del(struct etna_gpu *gpu);
+int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param,
+ uint64_t *value);
+
+
+/* pipe functions:
+ */
+
+struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id);
+void etna_pipe_del(struct etna_pipe *pipe);
+int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms);
+int etna_pipe_wait_ns(struct etna_pipe *pipe, uint32_t timestamp, uint64_t ns);
+
+
+/* buffer-object functions:
+ */
+
+struct etna_bo *etna_bo_new(struct etna_device *dev,
+ uint32_t size, uint32_t flags);
+struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name);
+struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd);
+struct etna_bo *etna_bo_ref(struct etna_bo *bo);
+void etna_bo_del(struct etna_bo *bo);
+int etna_bo_get_name(struct etna_bo *bo, uint32_t *name);
+uint32_t etna_bo_handle(struct etna_bo *bo);
+int etna_bo_dmabuf(struct etna_bo *bo);
+uint32_t etna_bo_size(struct etna_bo *bo);
+void * etna_bo_map(struct etna_bo *bo);
+int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op);
+void etna_bo_cpu_fini(struct etna_bo *bo);
+
+
+/* cmd stream functions:
+ */
+
+struct etna_cmd_stream {
+ uint32_t *buffer;
+ uint32_t offset; /* in 32-bit words */
+ uint32_t size; /* in 32-bit words */
+};
+
+struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size,
+ void (*reset_notify)(struct etna_cmd_stream *stream, void *priv),
+ void *priv);
+void etna_cmd_stream_del(struct etna_cmd_stream *stream);
+uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream);
+void etna_cmd_stream_flush(struct etna_cmd_stream *stream);
+void etna_cmd_stream_flush2(struct etna_cmd_stream *stream, int in_fence_fd,
+ int *out_fence_fd);
+void etna_cmd_stream_finish(struct etna_cmd_stream *stream);
+
+static inline uint32_t etna_cmd_stream_avail(struct etna_cmd_stream *stream)
+{
+ static const uint32_t END_CLEARANCE = 2; /* LINK op code */
+
+ return stream->size - stream->offset - END_CLEARANCE;
+}
+
+static inline void etna_cmd_stream_reserve(struct etna_cmd_stream *stream, size_t n)
+{
+ if (etna_cmd_stream_avail(stream) < n)
+ etna_cmd_stream_flush(stream);
+}
+
+static inline void etna_cmd_stream_emit(struct etna_cmd_stream *stream, uint32_t data)
+{
+ stream->buffer[stream->offset++] = data;
+}
+
+static inline uint32_t etna_cmd_stream_get(struct etna_cmd_stream *stream, uint32_t offset)
+{
+ return stream->buffer[offset];
+}
+
+static inline void etna_cmd_stream_set(struct etna_cmd_stream *stream, uint32_t offset,
+ uint32_t data)
+{
+ stream->buffer[offset] = data;
+}
+
+static inline uint32_t etna_cmd_stream_offset(struct etna_cmd_stream *stream)
+{
+ return stream->offset;
+}
+
+struct etna_reloc {
+ struct etna_bo *bo;
+#define ETNA_RELOC_READ 0x0001
+#define ETNA_RELOC_WRITE 0x0002
+ uint32_t flags;
+ uint32_t offset;
+};
+
+void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r);
+
+/* performance monitoring functions:
+ */
+
+struct etna_perfmon *etna_perfmon_create(struct etna_pipe *pipe);
+void etna_perfmon_del(struct etna_perfmon *perfmon);
+struct etna_perfmon_domain *etna_perfmon_get_dom_by_name(struct etna_perfmon *pm, const char *name);
+struct etna_perfmon_signal *etna_perfmon_get_sig_by_name(struct etna_perfmon_domain *dom, const char *name);
+
+struct etna_perf {
+#define ETNA_PM_PROCESS_PRE 0x0001
+#define ETNA_PM_PROCESS_POST 0x0002
+ uint32_t flags;
+ uint32_t sequence;
+ struct etna_perfmon_signal *signal;
+ struct etna_bo *bo;
+ uint32_t offset;
+};
+
+void etna_cmd_stream_perf(struct etna_cmd_stream *stream, const struct etna_perf *p);
+
+#endif /* ETNAVIV_DRMIF_H_ */
diff --git a/etnaviv/etnaviv_gpu.c b/etnaviv/etnaviv_gpu.c
new file mode 100644
index 0000000..dc4c126
--- /dev/null
+++ b/etnaviv/etnaviv_gpu.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+static uint64_t get_param(struct etna_device *dev, uint32_t core, uint32_t param)
+{
+ struct drm_etnaviv_param req = {
+ .pipe = core,
+ .param = param,
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GET_PARAM, &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("get-param (%x) failed! %d (%s)", param, ret, strerror(errno));
+ return 0;
+ }
+
+ return req.value;
+}
+
+drm_public struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core)
+{
+ struct etna_gpu *gpu;
+
+ gpu = calloc(1, sizeof(*gpu));
+ if (!gpu) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ gpu->dev = dev;
+ gpu->core = core;
+
+ gpu->model = get_param(dev, core, ETNAVIV_PARAM_GPU_MODEL);
+ gpu->revision = get_param(dev, core, ETNAVIV_PARAM_GPU_REVISION);
+
+ if (!gpu->model)
+ goto fail;
+
+ INFO_MSG(" GPU model: 0x%x (rev %x)", gpu->model, gpu->revision);
+
+ return gpu;
+fail:
+ if (gpu)
+ etna_gpu_del(gpu);
+
+ return NULL;
+}
+
+drm_public void etna_gpu_del(struct etna_gpu *gpu)
+{
+ free(gpu);
+}
+
+drm_public int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param,
+ uint64_t *value)
+{
+ struct etna_device *dev = gpu->dev;
+ unsigned int core = gpu->core;
+
+ switch(param) {
+ case ETNA_GPU_MODEL:
+ *value = gpu->model;
+ return 0;
+ case ETNA_GPU_REVISION:
+ *value = gpu->revision;
+ return 0;
+ case ETNA_GPU_FEATURES_0:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_0);
+ return 0;
+ case ETNA_GPU_FEATURES_1:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_1);
+ return 0;
+ case ETNA_GPU_FEATURES_2:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_2);
+ return 0;
+ case ETNA_GPU_FEATURES_3:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_3);
+ return 0;
+ case ETNA_GPU_FEATURES_4:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_4);
+ return 0;
+ case ETNA_GPU_FEATURES_5:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_5);
+ return 0;
+ case ETNA_GPU_FEATURES_6:
+ *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_6);
+ return 0;
+ case ETNA_GPU_STREAM_COUNT:
+ *value = get_param(dev, core, ETNA_GPU_STREAM_COUNT);
+ return 0;
+ case ETNA_GPU_REGISTER_MAX:
+ *value = get_param(dev, core, ETNA_GPU_REGISTER_MAX);
+ return 0;
+ case ETNA_GPU_THREAD_COUNT:
+ *value = get_param(dev, core, ETNA_GPU_THREAD_COUNT);
+ return 0;
+ case ETNA_GPU_VERTEX_CACHE_SIZE:
+ *value = get_param(dev, core, ETNA_GPU_VERTEX_CACHE_SIZE);
+ return 0;
+ case ETNA_GPU_SHADER_CORE_COUNT:
+ *value = get_param(dev, core, ETNA_GPU_SHADER_CORE_COUNT);
+ return 0;
+ case ETNA_GPU_PIXEL_PIPES:
+ *value = get_param(dev, core, ETNA_GPU_PIXEL_PIPES);
+ return 0;
+ case ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE:
+ *value = get_param(dev, core, ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE);
+ return 0;
+ case ETNA_GPU_BUFFER_SIZE:
+ *value = get_param(dev, core, ETNA_GPU_BUFFER_SIZE);
+ return 0;
+ case ETNA_GPU_INSTRUCTION_COUNT:
+ *value = get_param(dev, core, ETNA_GPU_INSTRUCTION_COUNT);
+ return 0;
+ case ETNA_GPU_NUM_CONSTANTS:
+ *value = get_param(dev, core, ETNA_GPU_NUM_CONSTANTS);
+ return 0;
+ case ETNA_GPU_NUM_VARYINGS:
+ *value = get_param(dev, core, ETNA_GPU_NUM_VARYINGS);
+ return 0;
+
+ default:
+ ERROR_MSG("invalid param id: %d", param);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/etnaviv/etnaviv_perfmon.c b/etnaviv/etnaviv_perfmon.c
new file mode 100644
index 0000000..f6576b8
--- /dev/null
+++ b/etnaviv/etnaviv_perfmon.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 Etnaviv Project
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "etnaviv_priv.h"
+
+static int etna_perfmon_query_signals(struct etna_perfmon *pm, struct etna_perfmon_domain *dom)
+{
+ struct etna_device *dev = pm->pipe->gpu->dev;
+ struct drm_etnaviv_pm_signal req = {
+ .pipe = pm->pipe->id,
+ .domain = dom->id
+ };
+
+ do {
+ struct etna_perfmon_signal *sig;
+ int ret;
+
+ ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_PM_QUERY_SIG, &req, sizeof(req));
+ if (ret)
+ break;
+
+ sig = calloc(1, sizeof(*sig));
+ if (!sig)
+ return -ENOMEM;
+
+ INFO_MSG("perfmon signal:");
+ INFO_MSG("id = %d", req.id);
+ INFO_MSG("name = %s", req.name);
+
+ sig->domain = dom;
+ sig->signal = req.id;
+ strncpy(sig->name, req.name, sizeof(sig->name));
+ list_addtail(&sig->head, &dom->signals);
+ } while (req.iter != 0xffff);
+
+ return 0;
+}
+
+static int etna_perfmon_query_domains(struct etna_perfmon *pm)
+{
+ struct etna_device *dev = pm->pipe->gpu->dev;
+ struct drm_etnaviv_pm_domain req = {
+ .pipe = pm->pipe->id
+ };
+
+ do {
+ struct etna_perfmon_domain *dom;
+ int ret;
+
+ ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_PM_QUERY_DOM, &req, sizeof(req));
+ if (ret)
+ break;
+
+ dom = calloc(1, sizeof(*dom));
+ if (!dom)
+ return -ENOMEM;
+
+ list_inithead(&dom->signals);
+ dom->id = req.id;
+ strncpy(dom->name, req.name, sizeof(dom->name));
+ list_addtail(&dom->head, &pm->domains);
+
+ INFO_MSG("perfmon domain:");
+ INFO_MSG("id = %d", req.id);
+ INFO_MSG("name = %s", req.name);
+ INFO_MSG("nr_signals = %d", req.nr_signals);
+
+ /* Query all available signals for this domain. */
+ if (req.nr_signals > 0) {
+ ret = etna_perfmon_query_signals(pm, dom);
+ if (ret)
+ return ret;
+ }
+ } while (req.iter != 0xff);
+
+ return 0;
+}
+
+static void etna_perfmon_free_signals(struct etna_perfmon_domain *dom)
+{
+ struct etna_perfmon_signal *sig, *next;
+
+ LIST_FOR_EACH_ENTRY_SAFE(sig, next, &dom->signals, head) {
+ list_del(&sig->head);
+ free(sig);
+ }
+}
+
+static void etna_perfmon_free_domains(struct etna_perfmon *pm)
+{
+ struct etna_perfmon_domain *dom, *next;
+
+ LIST_FOR_EACH_ENTRY_SAFE(dom, next, &pm->domains, head) {
+ etna_perfmon_free_signals(dom);
+ list_del(&dom->head);
+ free(dom);
+ }
+}
+
+drm_public struct etna_perfmon *etna_perfmon_create(struct etna_pipe *pipe)
+{
+ struct etna_perfmon *pm;
+ int ret;
+
+ pm = calloc(1, sizeof(*pm));
+ if (!pm) {
+ ERROR_MSG("allocation failed");
+ return NULL;
+ }
+
+ list_inithead(&pm->domains);
+ pm->pipe = pipe;
+
+ /* query all available domains and sources for this device */
+ ret = etna_perfmon_query_domains(pm);
+ if (ret)
+ goto fail;
+
+ return pm;
+
+fail:
+ etna_perfmon_del(pm);
+ return NULL;
+}
+
+drm_public void etna_perfmon_del(struct etna_perfmon *pm)
+{
+ if (!pm)
+ return;
+
+ etna_perfmon_free_domains(pm);
+ free(pm);
+}
+
+drm_public struct etna_perfmon_domain *etna_perfmon_get_dom_by_name(struct etna_perfmon *pm, const char *name)
+{
+ struct etna_perfmon_domain *dom;
+
+ if (pm) {
+ LIST_FOR_EACH_ENTRY(dom, &pm->domains, head) {
+ if (!strcmp(dom->name, name))
+ return dom;
+ }
+ }
+
+ return NULL;
+}
+
+drm_public struct etna_perfmon_signal *etna_perfmon_get_sig_by_name(struct etna_perfmon_domain *dom, const char *name)
+{
+ struct etna_perfmon_signal *signal;
+
+ if (dom) {
+ LIST_FOR_EACH_ENTRY(signal, &dom->signals, head) {
+ if (!strcmp(signal->name, name))
+ return signal;
+ }
+ }
+
+ return NULL;
+}
diff --git a/etnaviv/etnaviv_pipe.c b/etnaviv/etnaviv_pipe.c
new file mode 100644
index 0000000..4120a36
--- /dev/null
+++ b/etnaviv/etnaviv_pipe.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "etnaviv_priv.h"
+
+drm_public int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms)
+{
+ return etna_pipe_wait_ns(pipe, timestamp, ms * 1000000);
+}
+
+drm_public int etna_pipe_wait_ns(struct etna_pipe *pipe, uint32_t timestamp, uint64_t ns)
+{
+ struct etna_device *dev = pipe->gpu->dev;
+ int ret;
+
+ struct drm_etnaviv_wait_fence req = {
+ .pipe = pipe->gpu->core,
+ .fence = timestamp,
+ };
+
+ if (ns == 0)
+ req.flags |= ETNA_WAIT_NONBLOCK;
+
+ get_abs_timeout(&req.timeout, ns);
+
+ ret = drmCommandWrite(dev->fd, DRM_ETNAVIV_WAIT_FENCE, &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+drm_public void etna_pipe_del(struct etna_pipe *pipe)
+{
+ free(pipe);
+}
+
+drm_public struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id)
+{
+ struct etna_pipe *pipe;
+
+ pipe = calloc(1, sizeof(*pipe));
+ if (!pipe) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ pipe->id = id;
+ pipe->gpu = gpu;
+
+ return pipe;
+fail:
+ return NULL;
+}
diff --git a/etnaviv/etnaviv_priv.h b/etnaviv/etnaviv_priv.h
new file mode 100644
index 0000000..eef7f49
--- /dev/null
+++ b/etnaviv/etnaviv_priv.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef ETNAVIV_PRIV_H_
+#define ETNAVIV_PRIV_H_
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "xf86atomic.h"
+
+#include "util_double_list.h"
+
+#include "etnaviv_drmif.h"
+#include "etnaviv_drm.h"
+
+struct etna_bo_bucket {
+ uint32_t size;
+ struct list_head list;
+};
+
+struct etna_bo_cache {
+ struct etna_bo_bucket cache_bucket[14 * 4];
+ unsigned num_buckets;
+ time_t time;
+};
+
+struct etna_device {
+ int fd;
+ atomic_t refcnt;
+
+ /* tables to keep track of bo's, to avoid "evil-twin" etna_bo objects:
+ *
+ * handle_table: maps handle to etna_bo
+ * name_table: maps flink name to etna_bo
+ *
+ * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always
+ * returns a new handle. So we need to figure out if the bo is already
+ * open in the process first, before calling gem-open.
+ */
+ void *handle_table, *name_table;
+
+ struct etna_bo_cache bo_cache;
+
+ int closefd; /* call close(fd) upon destruction */
+};
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache);
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time);
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache,
+ uint32_t *size, uint32_t flags);
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo);
+
+/* for where @table_lock is already held: */
+drm_private void etna_device_del_locked(struct etna_device *dev);
+
+/* a GEM buffer object allocated from the DRM device */
+struct etna_bo {
+ struct etna_device *dev;
+ void *map; /* userspace mmap'ing (if there is one) */
+ uint32_t size;
+ uint32_t handle;
+ uint32_t flags;
+ uint32_t name; /* flink global handle (DRI2 name) */
+ uint64_t offset; /* offset to mmap() */
+ atomic_t refcnt;
+
+ /* in the common case, a bo won't be referenced by more than a single
+ * command stream. So to avoid looping over all the bo's in the
+ * reloc table to find the idx of a bo that might already be in the
+ * table, we cache the idx in the bo. But in order to detect the
+ * slow-path where bo is ref'd in multiple streams, we also must track
+ * the current_stream for which the idx is valid. See bo2idx().
+ */
+ struct etna_cmd_stream *current_stream;
+ uint32_t idx;
+
+ int reuse;
+ struct list_head list; /* bucket-list entry */
+ time_t free_time; /* time when added to bucket-list */
+};
+
+struct etna_gpu {
+ struct etna_device *dev;
+ uint32_t core;
+ uint32_t model;
+ uint32_t revision;
+};
+
+struct etna_pipe {
+ enum etna_pipe_id id;
+ struct etna_gpu *gpu;
+};
+
+struct etna_cmd_stream_priv {
+ struct etna_cmd_stream base;
+ struct etna_pipe *pipe;
+
+ uint32_t last_timestamp;
+
+ /* submit ioctl related tables: */
+ struct {
+ /* bo's table: */
+ struct drm_etnaviv_gem_submit_bo *bos;
+ uint32_t nr_bos, max_bos;
+
+ /* reloc's table: */
+ struct drm_etnaviv_gem_submit_reloc *relocs;
+ uint32_t nr_relocs, max_relocs;
+
+ /* perf's table: */
+ struct drm_etnaviv_gem_submit_pmr *pmrs;
+ uint32_t nr_pmrs, max_pmrs;
+ } submit;
+
+ /* should have matching entries in submit.bos: */
+ struct etna_bo **bos;
+ uint32_t nr_bos, max_bos;
+
+ /* notify callback if buffer reset happened */
+ void (*reset_notify)(struct etna_cmd_stream *stream, void *priv);
+ void *reset_notify_priv;
+};
+
+struct etna_perfmon {
+ struct list_head domains;
+ struct etna_pipe *pipe;
+};
+
+struct etna_perfmon_domain
+{
+ struct list_head head;
+ struct list_head signals;
+ uint8_t id;
+ char name[64];
+};
+
+struct etna_perfmon_signal
+{
+ struct list_head head;
+ struct etna_perfmon_domain *domain;
+ uint8_t signal;
+ char name[64];
+};
+
+#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define enable_debug 1 /* TODO make dynamic */
+
+#define INFO_MSG(fmt, ...) \
+ do { drmMsg("[I] "fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define DEBUG_MSG(fmt, ...) \
+ do if (enable_debug) { drmMsg("[D] "fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define WARN_MSG(fmt, ...) \
+ do { drmMsg("[W] "fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define ERROR_MSG(fmt, ...) \
+ do { drmMsg("[E] " fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+
+#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
+
+static inline void get_abs_timeout(struct drm_etnaviv_timespec *tv, uint64_t ns)
+{
+ struct timespec t;
+ uint32_t s = ns / 1000000000;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ tv->tv_sec = t.tv_sec + s;
+ tv->tv_nsec = t.tv_nsec + ns - (s * 1000000000);
+}
+
+#endif /* ETNAVIV_PRIV_H_ */
diff --git a/etnaviv/libdrm_etnaviv.pc.in b/etnaviv/libdrm_etnaviv.pc.in
new file mode 100644
index 0000000..13fed01
--- /dev/null
+++ b/etnaviv/libdrm_etnaviv.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_etnaviv
+Description: Userspace interface to etnaviv kernel DRM services
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_etnaviv
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
diff --git a/etnaviv/meson.build b/etnaviv/meson.build
new file mode 100644
index 0000000..6040cf6
--- /dev/null
+++ b/etnaviv/meson.build
@@ -0,0 +1,63 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+
+libdrm_etnaviv = shared_library(
+ 'drm_etnaviv',
+ [
+ files(
+ 'etnaviv_device.c', 'etnaviv_gpu.c', 'etnaviv_bo.c', 'etnaviv_bo_cache.c',
+ 'etnaviv_perfmon.c', 'etnaviv_pipe.c', 'etnaviv_cmd_stream.c',
+ ),
+ config_file
+ ],
+ include_directories : [inc_root, inc_drm],
+ link_with : libdrm,
+ c_args : libdrm_c_args,
+ dependencies : [dep_pthread_stubs, dep_rt, dep_atomic_ops],
+ version : '1.0.0',
+ install : true,
+)
+
+install_headers('etnaviv_drmif.h', subdir : 'libdrm')
+
+pkg.generate(
+ name : 'libdrm_etnaviv',
+ libraries : libdrm_etnaviv,
+ subdirs : ['.', 'libdrm'],
+ version : meson.project_version(),
+ requires_private : 'libdrm',
+ description : 'Userspace interface to Tegra kernel DRM services',
+)
+
+ext_libdrm_etnaviv = declare_dependency(
+ link_with : [libdrm, libdrm_etnaviv],
+ include_directories : [inc_drm, include_directories('.')],
+)
+
+test(
+ 'etnaviv-symbols-check',
+ symbols_check,
+ args : [
+ '--lib', libdrm_etnaviv,
+ '--symbols-file', files('etnaviv-symbols.txt'),
+ '--nm', prog_nm.path(),
+ ],
+)
diff --git a/exynos/exynos-symbols.txt b/exynos/exynos-symbols.txt
new file mode 100644
index 0000000..c674841
--- /dev/null
+++ b/exynos/exynos-symbols.txt
@@ -0,0 +1,23 @@
+exynos_bo_create
+exynos_bo_destroy
+exynos_bo_from_name
+exynos_bo_get_info
+exynos_bo_get_name
+exynos_bo_handle
+exynos_bo_map
+exynos_device_create
+exynos_device_destroy
+exynos_prime_fd_to_handle
+exynos_prime_handle_to_fd
+exynos_vidi_connection
+exynos_handle_event
+g2d_blend
+g2d_copy
+g2d_copy_with_scale
+g2d_exec
+g2d_config_event
+g2d_fini
+g2d_init
+g2d_move
+g2d_scale_and_blend
+g2d_solid_fill
diff --git a/exynos/exynos_drm.c b/exynos/exynos_drm.c
new file mode 100644
index 0000000..b008ad7
--- /dev/null
+++ b/exynos/exynos_drm.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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:
+ * Inki Dae <inki.dae@samsung.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <linux/stddef.h>
+
+#include <xf86drm.h>
+
+#include "libdrm_macros.h"
+#include "exynos_drm.h"
+#include "exynos_drmif.h"
+
+#define U642VOID(x) ((void *)(unsigned long)(x))
+
+/*
+ * Create exynos drm device object.
+ *
+ * @fd: file descriptor to exynos drm driver opened.
+ *
+ * if true, return the device object else NULL.
+ */
+drm_public struct exynos_device * exynos_device_create(int fd)
+{
+ struct exynos_device *dev;
+
+ dev = calloc(sizeof(*dev), 1);
+ if (!dev) {
+ fprintf(stderr, "failed to create device[%s].\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ dev->fd = fd;
+
+ return dev;
+}
+
+/*
+ * Destroy exynos drm device object
+ *
+ * @dev: exynos drm device object.
+ */
+drm_public void exynos_device_destroy(struct exynos_device *dev)
+{
+ free(dev);
+}
+
+/*
+ * Create a exynos buffer object to exynos drm device.
+ *
+ * @dev: exynos drm device object.
+ * @size: user-desired size.
+ * flags: user-desired memory type.
+ * user can set one or more types among several types to memory
+ * allocation and cache attribute types. and as default,
+ * EXYNOS_BO_NONCONTIG and EXYNOS-BO_NONCACHABLE types would
+ * be used.
+ *
+ * if true, return a exynos buffer object else NULL.
+ */
+drm_public struct exynos_bo * exynos_bo_create(struct exynos_device *dev,
+ size_t size, uint32_t flags)
+{
+ struct exynos_bo *bo;
+ struct drm_exynos_gem_create req = {
+ .size = size,
+ .flags = flags,
+ };
+
+ if (size == 0) {
+ fprintf(stderr, "invalid size.\n");
+ goto fail;
+ }
+
+ bo = calloc(sizeof(*bo), 1);
+ if (!bo) {
+ fprintf(stderr, "failed to create bo[%s].\n",
+ strerror(errno));
+ goto err_free_bo;
+ }
+
+ bo->dev = dev;
+
+ if (drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &req)){
+ fprintf(stderr, "failed to create gem object[%s].\n",
+ strerror(errno));
+ goto err_free_bo;
+ }
+
+ bo->handle = req.handle;
+ bo->size = size;
+ bo->flags = flags;
+
+ return bo;
+
+err_free_bo:
+ free(bo);
+fail:
+ return NULL;
+}
+
+/*
+ * Get information to gem region allocated.
+ *
+ * @dev: exynos drm device object.
+ * @handle: gem handle to request gem info.
+ * @size: size to gem object and returned by kernel side.
+ * @flags: gem flags to gem object and returned by kernel side.
+ *
+ * with this function call, you can get flags and size to gem handle
+ * through bo object.
+ *
+ * if true, return 0 else negative.
+ */
+drm_public int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle,
+ size_t *size, uint32_t *flags)
+{
+ int ret;
+ struct drm_exynos_gem_info req = {
+ .handle = handle,
+ };
+
+ ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_GET, &req);
+ if (ret < 0) {
+ fprintf(stderr, "failed to get gem object information[%s].\n",
+ strerror(errno));
+ return ret;
+ }
+
+ *size = req.size;
+ *flags = req.flags;
+
+ return 0;
+}
+
+/*
+ * Destroy a exynos buffer object.
+ *
+ * @bo: a exynos buffer object to be destroyed.
+ */
+drm_public void exynos_bo_destroy(struct exynos_bo *bo)
+{
+ if (!bo)
+ return;
+
+ if (bo->vaddr)
+ munmap(bo->vaddr, bo->size);
+
+ if (bo->handle) {
+ struct drm_gem_close req = {
+ .handle = bo->handle,
+ };
+
+ drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+ }
+
+ free(bo);
+}
+
+
+/*
+ * Get a exynos buffer object from a gem global object name.
+ *
+ * @dev: a exynos device object.
+ * @name: a gem global object name exported by another process.
+ *
+ * this interface is used to get a exynos buffer object from a gem
+ * global object name sent by another process for buffer sharing.
+ *
+ * if true, return a exynos buffer object else NULL.
+ *
+ */
+drm_public struct exynos_bo *
+exynos_bo_from_name(struct exynos_device *dev, uint32_t name)
+{
+ struct exynos_bo *bo;
+ struct drm_gem_open req = {
+ .name = name,
+ };
+
+ bo = calloc(sizeof(*bo), 1);
+ if (!bo) {
+ fprintf(stderr, "failed to allocate bo[%s].\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+ fprintf(stderr, "failed to open gem object[%s].\n",
+ strerror(errno));
+ goto err_free_bo;
+ }
+
+ bo->dev = dev;
+ bo->name = name;
+ bo->handle = req.handle;
+
+ return bo;
+
+err_free_bo:
+ free(bo);
+ return NULL;
+}
+
+/*
+ * Get a gem global object name from a gem object handle.
+ *
+ * @bo: a exynos buffer object including gem handle.
+ * @name: a gem global object name to be got by kernel driver.
+ *
+ * this interface is used to get a gem global object name from a gem object
+ * handle to a buffer that wants to share it with another process.
+ *
+ * if true, return 0 else negative.
+ */
+drm_public int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name)
+{
+ if (!bo->name) {
+ struct drm_gem_flink req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
+ if (ret) {
+ fprintf(stderr, "failed to get gem global name[%s].\n",
+ strerror(errno));
+ return ret;
+ }
+
+ bo->name = req.name;
+ }
+
+ *name = bo->name;
+
+ return 0;
+}
+
+drm_public uint32_t exynos_bo_handle(struct exynos_bo *bo)
+{
+ return bo->handle;
+}
+
+/*
+ * Mmap a buffer to user space.
+ *
+ * @bo: a exynos buffer object including a gem object handle to be mmapped
+ * to user space.
+ *
+ * if true, user pointer mmapped else NULL.
+ */
+drm_public void *exynos_bo_map(struct exynos_bo *bo)
+{
+ if (!bo->vaddr) {
+ struct exynos_device *dev = bo->dev;
+ struct drm_mode_map_dumb arg;
+ void *map = NULL;
+ int ret;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.handle = bo->handle;
+
+ ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
+ if (ret) {
+ fprintf(stderr, "failed to map dumb buffer[%s].\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ dev->fd, arg.offset);
+
+ if (map != MAP_FAILED)
+ bo->vaddr = map;
+ }
+
+ return bo->vaddr;
+}
+
+/*
+ * Export gem object to dmabuf as file descriptor.
+ *
+ * @dev: exynos device object
+ * @handle: gem handle to export as file descriptor of dmabuf
+ * @fd: file descriptor returned from kernel
+ *
+ * @return: 0 on success, -1 on error, and errno will be set
+ */
+drm_public int
+exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle, int *fd)
+{
+ return drmPrimeHandleToFD(dev->fd, handle, 0, fd);
+}
+
+/*
+ * Import file descriptor into gem handle.
+ *
+ * @dev: exynos device object
+ * @fd: file descriptor of dmabuf to import
+ * @handle: gem handle returned from kernel
+ *
+ * @return: 0 on success, -1 on error, and errno will be set
+ */
+drm_public int
+exynos_prime_fd_to_handle(struct exynos_device *dev, int fd, uint32_t *handle)
+{
+ return drmPrimeFDToHandle(dev->fd, fd, handle);
+}
+
+
+
+/*
+ * Request Wireless Display connection or disconnection.
+ *
+ * @dev: a exynos device object.
+ * @connect: indicate whether connectoin or disconnection request.
+ * @ext: indicate whether edid data includes extensions data or not.
+ * @edid: a pointer to edid data from Wireless Display device.
+ *
+ * this interface is used to request Virtual Display driver connection or
+ * disconnection. for this, user should get a edid data from the Wireless
+ * Display device and then send that data to kernel driver with connection
+ * request
+ *
+ * if true, return 0 else negative.
+ */
+drm_public int
+exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
+ uint32_t ext, void *edid)
+{
+ struct drm_exynos_vidi_connection req = {
+ .connection = connect,
+ .extensions = ext,
+ .edid = (uint64_t)(uintptr_t)edid,
+ };
+ int ret;
+
+ ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_VIDI_CONNECTION, &req);
+ if (ret) {
+ fprintf(stderr, "failed to request vidi connection[%s].\n",
+ strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+exynos_handle_vendor(int fd, struct drm_event *e, void *ctx)
+{
+ struct drm_exynos_g2d_event *g2d;
+ struct exynos_event_context *ectx = ctx;
+
+ switch (e->type) {
+ case DRM_EXYNOS_G2D_EVENT:
+ if (ectx->version < 1 || ectx->g2d_event_handler == NULL)
+ break;
+ g2d = (struct drm_exynos_g2d_event *)e;
+ ectx->g2d_event_handler(fd, g2d->cmdlist_no, g2d->tv_sec,
+ g2d->tv_usec, U642VOID(g2d->user_data));
+ break;
+
+ default:
+ break;
+ }
+}
+
+drm_public int
+exynos_handle_event(struct exynos_device *dev, struct exynos_event_context *ctx)
+{
+ char buffer[1024];
+ int len, i;
+ struct drm_event *e;
+ struct drm_event_vblank *vblank;
+ drmEventContextPtr evctx = &ctx->base;
+
+ /* The DRM read semantics guarantees that we always get only
+ * complete events. */
+ len = read(dev->fd, buffer, sizeof buffer);
+ if (len == 0)
+ return 0;
+ if (len < (int)sizeof *e)
+ return -1;
+
+ i = 0;
+ while (i < len) {
+ e = (struct drm_event *)(buffer + i);
+ switch (e->type) {
+ case DRM_EVENT_VBLANK:
+ if (evctx->version < 1 ||
+ evctx->vblank_handler == NULL)
+ break;
+ vblank = (struct drm_event_vblank *) e;
+ evctx->vblank_handler(dev->fd,
+ vblank->sequence,
+ vblank->tv_sec,
+ vblank->tv_usec,
+ U642VOID (vblank->user_data));
+ break;
+ case DRM_EVENT_FLIP_COMPLETE:
+ if (evctx->version < 2 ||
+ evctx->page_flip_handler == NULL)
+ break;
+ vblank = (struct drm_event_vblank *) e;
+ evctx->page_flip_handler(dev->fd,
+ vblank->sequence,
+ vblank->tv_sec,
+ vblank->tv_usec,
+ U642VOID (vblank->user_data));
+ break;
+ default:
+ exynos_handle_vendor(dev->fd, e, evctx);
+ break;
+ }
+ i += e->length;
+ }
+
+ return 0;
+}
diff --git a/exynos/exynos_drm.h b/exynos/exynos_drm.h
new file mode 100644
index 0000000..50181c4
--- /dev/null
+++ b/exynos/exynos_drm.h
@@ -0,0 +1,172 @@
+/* exynos_drm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * 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
+ * VA LINUX SYSTEMS AND/OR ITS 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 _EXYNOS_DRM_H_
+#define _EXYNOS_DRM_H_
+
+#include "drm.h"
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ * - this size value would be page-aligned internally.
+ * @flags: user request for setting memory type or cache attributes.
+ * @handle: returned a handle to created gem object.
+ * - this handle will be set by gem module of kernel side.
+ */
+struct drm_exynos_gem_create {
+ uint64_t size;
+ unsigned int flags;
+ unsigned int handle;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ * this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ * be set by driver.
+ */
+struct drm_exynos_gem_info {
+ unsigned int handle;
+ unsigned int flags;
+ uint64_t size;
+};
+
+/**
+ * A structure for user connection request of virtual display.
+ *
+ * @connection: indicate whether doing connection or not by user.
+ * @extensions: if this value is 1 then the vidi driver would need additional
+ * 128bytes edid data.
+ * @edid: the edid data pointer from user side.
+ */
+struct drm_exynos_vidi_connection {
+ unsigned int connection;
+ unsigned int extensions;
+ uint64_t edid;
+};
+
+/* memory type definitions. */
+enum e_drm_exynos_gem_mem_type {
+ /* Physically Continuous memory and used as default. */
+ EXYNOS_BO_CONTIG = 0 << 0,
+ /* Physically Non-Continuous memory. */
+ EXYNOS_BO_NONCONTIG = 1 << 0,
+ /* non-cachable mapping and used as default. */
+ EXYNOS_BO_NONCACHABLE = 0 << 1,
+ /* cachable mapping. */
+ EXYNOS_BO_CACHABLE = 1 << 1,
+ /* write-combine mapping. */
+ EXYNOS_BO_WC = 1 << 2,
+ EXYNOS_BO_MASK = EXYNOS_BO_NONCONTIG | EXYNOS_BO_CACHABLE |
+ EXYNOS_BO_WC
+};
+
+struct drm_exynos_g2d_get_ver {
+ __u32 major;
+ __u32 minor;
+};
+
+struct drm_exynos_g2d_cmd {
+ __u32 offset;
+ __u32 data;
+};
+
+enum drm_exynos_g2d_buf_type {
+ G2D_BUF_USERPTR = 1 << 31,
+};
+
+enum drm_exynos_g2d_event_type {
+ G2D_EVENT_NOT,
+ G2D_EVENT_NONSTOP,
+ G2D_EVENT_STOP, /* not yet */
+};
+
+struct drm_exynos_g2d_userptr {
+ unsigned long userptr;
+ unsigned long size;
+};
+
+struct drm_exynos_g2d_set_cmdlist {
+ __u64 cmd;
+ __u64 cmd_buf;
+ __u32 cmd_nr;
+ __u32 cmd_buf_nr;
+
+ /* for g2d event */
+ __u64 event_type;
+ __u64 user_data;
+};
+
+struct drm_exynos_g2d_exec {
+ __u64 async;
+};
+
+#define DRM_EXYNOS_GEM_CREATE 0x00
+/* Reserved 0x04 ~ 0x05 for exynos specific gem ioctl */
+#define DRM_EXYNOS_GEM_GET 0x04
+#define DRM_EXYNOS_VIDI_CONNECTION 0x07
+
+/* G2D */
+#define DRM_EXYNOS_G2D_GET_VER 0x20
+#define DRM_EXYNOS_G2D_SET_CMDLIST 0x21
+#define DRM_EXYNOS_G2D_EXEC 0x22
+
+#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
+
+#define DRM_IOCTL_EXYNOS_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_GEM_GET, struct drm_exynos_gem_info)
+
+#define DRM_IOCTL_EXYNOS_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection)
+
+#define DRM_IOCTL_EXYNOS_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_G2D_GET_VER, struct drm_exynos_g2d_get_ver)
+#define DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_G2D_SET_CMDLIST, struct drm_exynos_g2d_set_cmdlist)
+#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
+
+/* EXYNOS specific events */
+#define DRM_EXYNOS_G2D_EVENT 0x80000000
+
+struct drm_exynos_g2d_event {
+ struct drm_event base;
+ __u64 user_data;
+ __u32 tv_sec;
+ __u32 tv_usec;
+ __u32 cmdlist_no;
+ __u32 reserved;
+};
+
+#endif
diff --git a/exynos/exynos_drmif.h b/exynos/exynos_drmif.h
new file mode 100644
index 0000000..bcf8595
--- /dev/null
+++ b/exynos/exynos_drmif.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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:
+ * Inki Dae <inki.dae@samsung.com>
+ */
+
+#ifndef EXYNOS_DRMIF_H_
+#define EXYNOS_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+#include "exynos_drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct exynos_device {
+ int fd;
+};
+
+/*
+ * Exynos Buffer Object structure.
+ *
+ * @dev: exynos device object allocated.
+ * @handle: a gem handle to gem object created.
+ * @flags: indicate memory allocation and cache attribute types.
+ * @size: size to the buffer created.
+ * @vaddr: user space address to a gem buffer mmapped.
+ * @name: a gem global handle from flink request.
+ */
+struct exynos_bo {
+ struct exynos_device *dev;
+ uint32_t handle;
+ uint32_t flags;
+ size_t size;
+ void *vaddr;
+ uint32_t name;
+};
+
+#define EXYNOS_EVENT_CONTEXT_VERSION 1
+
+/*
+ * Exynos Event Context structure.
+ *
+ * @base: base context (for core events).
+ * @version: version info similar to the one in 'drmEventContext'.
+ * @g2d_event_handler: handler for G2D events.
+ */
+struct exynos_event_context {
+ drmEventContext base;
+
+ int version;
+
+ void (*g2d_event_handler)(int fd, unsigned int cmdlist_no,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *user_data);
+};
+
+/*
+ * device related functions:
+ */
+struct exynos_device * exynos_device_create(int fd);
+void exynos_device_destroy(struct exynos_device *dev);
+
+/*
+ * buffer-object related functions:
+ */
+struct exynos_bo * exynos_bo_create(struct exynos_device *dev,
+ size_t size, uint32_t flags);
+int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle,
+ size_t *size, uint32_t *flags);
+void exynos_bo_destroy(struct exynos_bo *bo);
+struct exynos_bo * exynos_bo_from_name(struct exynos_device *dev, uint32_t name);
+int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name);
+uint32_t exynos_bo_handle(struct exynos_bo *bo);
+void * exynos_bo_map(struct exynos_bo *bo);
+int exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle,
+ int *fd);
+int exynos_prime_fd_to_handle(struct exynos_device *dev, int fd,
+ uint32_t *handle);
+
+/*
+ * Virtual Display related functions:
+ */
+int exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
+ uint32_t ext, void *edid);
+
+/*
+ * event handling related functions:
+ */
+int exynos_handle_event(struct exynos_device *dev,
+ struct exynos_event_context *ctx);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* EXYNOS_DRMIF_H_ */
diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
new file mode 100644
index 0000000..ac6fa68
--- /dev/null
+++ b/exynos/exynos_fimg2d.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ *
+ * 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
+ * VA LINUX SYSTEMS AND/OR ITS 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/mman.h>
+#include <linux/stddef.h>
+
+#include <xf86drm.h>
+
+#include "libdrm_macros.h"
+#include "exynos_drm.h"
+#include "fimg2d_reg.h"
+#include "exynos_fimg2d.h"
+
+#define SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \
+ val.data.src_coeff = sc; \
+ val.data.inv_src_color_coeff = si; \
+ val.data.src_coeff_src_a = scsa; \
+ val.data.src_coeff_dst_a = scda; \
+ val.data.dst_coeff = dc; \
+ val.data.inv_dst_color_coeff = di; \
+ val.data.dst_coeff_src_a = dcsa; \
+ val.data.dst_coeff_dst_a = dcda;
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define MSG_PREFIX "exynos/fimg2d: "
+
+#define G2D_MAX_CMD_NR 64
+#define G2D_MAX_GEM_CMD_NR 64
+#define G2D_MAX_CMD_LIST_NR 64
+
+struct g2d_context {
+ int fd;
+ unsigned int major;
+ unsigned int minor;
+ struct drm_exynos_g2d_cmd cmd[G2D_MAX_CMD_NR];
+ struct drm_exynos_g2d_cmd cmd_buf[G2D_MAX_GEM_CMD_NR];
+ unsigned int cmd_nr;
+ unsigned int cmd_buf_nr;
+ unsigned int cmdlist_nr;
+ void *event_userdata;
+};
+
+enum g2d_base_addr_reg {
+ g2d_dst = 0,
+ g2d_src
+};
+
+enum e_g2d_dir_mode {
+ G2D_DIR_MODE_POSITIVE = 0,
+ G2D_DIR_MODE_NEGATIVE = 1
+};
+
+union g2d_direction_val {
+ unsigned int val[2];
+ struct {
+ /* SRC_MSK_DIRECT_REG [0:1] (source) */
+ enum e_g2d_dir_mode src_x_direction:1;
+ enum e_g2d_dir_mode src_y_direction:1;
+
+ /* SRC_MSK_DIRECT_REG [2:3] */
+ unsigned int reversed1:2;
+
+ /* SRC_MSK_DIRECT_REG [4:5] (mask) */
+ enum e_g2d_dir_mode mask_x_direction:1;
+ enum e_g2d_dir_mode mask_y_direction:1;
+
+ /* SRC_MSK_DIRECT_REG [6:31] */
+ unsigned int padding1:26;
+
+ /* DST_PAT_DIRECT_REG [0:1] (destination) */
+ enum e_g2d_dir_mode dst_x_direction:1;
+ enum e_g2d_dir_mode dst_y_direction:1;
+
+ /* DST_PAT_DIRECT_REG [2:3] */
+ unsigned int reversed2:2;
+
+ /* DST_PAT_DIRECT_REG [4:5] (pattern) */
+ enum e_g2d_dir_mode pat_x_direction:1;
+ enum e_g2d_dir_mode pat_y_direction:1;
+
+ /* DST_PAT_DIRECT_REG [6:31] */
+ unsigned int padding2:26;
+ } data;
+};
+
+static unsigned int g2d_get_scaling(unsigned int src, unsigned int dst)
+{
+ /*
+ * The G2D hw scaling factor is a normalized inverse of the scaling factor.
+ * For example: When source width is 100 and destination width is 200
+ * (scaling of 2x), then the hw factor is NC * 100 / 200.
+ * The normalization factor (NC) is 2^16 = 0x10000.
+ */
+
+ return ((src << 16) / dst);
+}
+
+static unsigned int g2d_get_blend_op(enum e_g2d_op op)
+{
+ union g2d_blend_func_val val;
+
+ val.val = 0;
+
+ /*
+ * The switch statement is missing the default branch since
+ * we assume that the caller checks the blending operation
+ * via g2d_validate_blending_op() first.
+ */
+ switch (op) {
+ case G2D_OP_CLEAR:
+ case G2D_OP_DISJOINT_CLEAR:
+ case G2D_OP_CONJOINT_CLEAR:
+ SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO,
+ 0, 0, 0);
+ break;
+ case G2D_OP_SRC:
+ case G2D_OP_DISJOINT_SRC:
+ case G2D_OP_CONJOINT_SRC:
+ SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
+ 0, 0, 0);
+ break;
+ case G2D_OP_DST:
+ case G2D_OP_DISJOINT_DST:
+ case G2D_OP_CONJOINT_DST:
+ SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE,
+ 0, 0, 0);
+ break;
+ case G2D_OP_OVER:
+ SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0,
+ G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
+ break;
+ case G2D_OP_INTERPOLATE:
+ SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0,
+ G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
+ break;
+ }
+
+ return val.val;
+}
+
+/*
+ * g2d_check_space - check if command buffers have enough space left.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @num_cmds: number of (regular) commands.
+ * @num_gem_cmds: number of GEM commands.
+ */
+static unsigned int g2d_check_space(const struct g2d_context *ctx,
+ unsigned int num_cmds, unsigned int num_gem_cmds)
+{
+ if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR ||
+ ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * g2d_validate_select_mode - validate select mode.
+ *
+ * @mode: the mode to validate
+ *
+ * Returns zero for an invalid mode and one otherwise.
+ */
+static int g2d_validate_select_mode(
+ enum e_g2d_select_mode mode)
+{
+ switch (mode) {
+ case G2D_SELECT_MODE_NORMAL:
+ case G2D_SELECT_MODE_FGCOLOR:
+ case G2D_SELECT_MODE_BGCOLOR:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * g2d_validate_blending_op - validate blending operation.
+ *
+ * @operation: the operation to validate
+ *
+ * Returns zero for an invalid mode and one otherwise.
+ */
+static int g2d_validate_blending_op(
+ enum e_g2d_op operation)
+{
+ switch (operation) {
+ case G2D_OP_CLEAR:
+ case G2D_OP_SRC:
+ case G2D_OP_DST:
+ case G2D_OP_OVER:
+ case G2D_OP_INTERPOLATE:
+ case G2D_OP_DISJOINT_CLEAR:
+ case G2D_OP_DISJOINT_SRC:
+ case G2D_OP_DISJOINT_DST:
+ case G2D_OP_CONJOINT_CLEAR:
+ case G2D_OP_CONJOINT_SRC:
+ case G2D_OP_CONJOINT_DST:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * g2d_add_cmd - set given command and value to user side command buffer.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @cmd: command data.
+ * @value: value data.
+ *
+ * The caller has to make sure that the commands buffers have enough space
+ * left to hold the command. Use g2d_check_space() to ensure this.
+ */
+static void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
+ unsigned long value)
+{
+ switch (cmd & ~(G2D_BUF_USERPTR)) {
+ case SRC_BASE_ADDR_REG:
+ case SRC_PLANE2_BASE_ADDR_REG:
+ case DST_BASE_ADDR_REG:
+ case DST_PLANE2_BASE_ADDR_REG:
+ case PAT_BASE_ADDR_REG:
+ case MASK_BASE_ADDR_REG:
+ assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR);
+
+ ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd;
+ ctx->cmd_buf[ctx->cmd_buf_nr].data = value;
+ ctx->cmd_buf_nr++;
+ break;
+ default:
+ assert(ctx->cmd_nr < G2D_MAX_CMD_NR);
+
+ ctx->cmd[ctx->cmd_nr].offset = cmd;
+ ctx->cmd[ctx->cmd_nr].data = value;
+ ctx->cmd_nr++;
+ break;
+ }
+}
+
+/*
+ * g2d_add_base_addr - helper function to set dst/src base address register.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to the dst/src g2d_image structure.
+ * @reg: the register that should be set.
+ */
+static void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img,
+ enum g2d_base_addr_reg reg)
+{
+ const unsigned long cmd = (reg == g2d_dst) ?
+ DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG;
+
+ if (img->buf_type == G2D_IMGBUF_USERPTR)
+ g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR,
+ (unsigned long)&img->user_ptr[0]);
+ else
+ g2d_add_cmd(ctx, cmd, img->bo[0]);
+}
+
+/*
+ * g2d_set_direction - setup direction register (useful for overlapping blits).
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @dir: a pointer to the g2d_direction_val structure.
+ */
+static void g2d_set_direction(struct g2d_context *ctx,
+ const union g2d_direction_val *dir)
+{
+ g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]);
+ g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]);
+}
+
+/*
+ * g2d_flush - submit all commands and values in user side command buffer
+ * to command queue aware of fimg2d dma.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ *
+ * This function should be called after all commands and values to user
+ * side command buffer are set. It submits that buffer to the kernel side driver.
+ */
+static int g2d_flush(struct g2d_context *ctx)
+{
+ int ret;
+ struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
+
+ if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
+ return 0;
+
+ if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
+ fprintf(stderr, MSG_PREFIX "command list overflow.\n");
+ return -EINVAL;
+ }
+
+ cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0];
+ cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
+ cmdlist.cmd_nr = ctx->cmd_nr;
+ cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
+
+ if (ctx->event_userdata) {
+ cmdlist.event_type = G2D_EVENT_NONSTOP;
+ cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata);
+ ctx->event_userdata = NULL;
+ } else {
+ cmdlist.event_type = G2D_EVENT_NOT;
+ cmdlist.user_data = 0;
+ }
+
+ ctx->cmd_nr = 0;
+ ctx->cmd_buf_nr = 0;
+
+ ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
+ if (ret < 0) {
+ fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n");
+ return ret;
+ }
+
+ ctx->cmdlist_nr++;
+
+ return ret;
+}
+
+/**
+ * g2d_init - create a new g2d context and get hardware version.
+ *
+ * fd: a file descriptor to an opened drm device.
+ */
+drm_public struct g2d_context *g2d_init(int fd)
+{
+ struct drm_exynos_g2d_get_ver ver;
+ struct g2d_context *ctx;
+ int ret;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ fprintf(stderr, MSG_PREFIX "failed to allocate context.\n");
+ return NULL;
+ }
+
+ ctx->fd = fd;
+
+ ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
+ if (ret < 0) {
+ fprintf(stderr, MSG_PREFIX "failed to get version.\n");
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->major = ver.major;
+ ctx->minor = ver.minor;
+
+ printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor);
+ return ctx;
+}
+
+drm_public void g2d_fini(struct g2d_context *ctx)
+{
+ free(ctx);
+}
+
+/**
+ * g2d_config_event - setup userdata configuration for a g2d event.
+ * The next invocation of a g2d call (e.g. g2d_solid_fill) is
+ * then going to flag the command buffer as 'nonstop'.
+ * Completion of the command buffer execution can then be
+ * determined by using drmHandleEvent on the DRM fd.
+ * The userdata is 'consumed' in the process.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @userdata: a pointer to the user data
+ */
+drm_public void g2d_config_event(struct g2d_context *ctx, void *userdata)
+{
+ ctx->event_userdata = userdata;
+}
+
+/**
+ * g2d_exec - start the dma to process all commands summited by g2d_flush().
+ *
+ * @ctx: a pointer to g2d_context structure.
+ */
+drm_public int g2d_exec(struct g2d_context *ctx)
+{
+ struct drm_exynos_g2d_exec exec;
+ int ret;
+
+ if (ctx->cmdlist_nr == 0)
+ return -EINVAL;
+
+ exec.async = 0;
+
+ ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
+ if (ret < 0) {
+ fprintf(stderr, MSG_PREFIX "failed to execute.\n");
+ return ret;
+ }
+
+ ctx->cmdlist_nr = 0;
+
+ return ret;
+}
+
+/**
+ * g2d_solid_fill - fill given buffer with given color data.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to g2d_image structure including image and buffer
+ * information.
+ * @x: x start position to buffer filled with given color data.
+ * @y: y start position to buffer filled with given color data.
+ * @w: width value to buffer filled with given color data.
+ * @h: height value to buffer filled with given color data.
+ */
+drm_public int
+g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
+ unsigned int x, unsigned int y, unsigned int w,
+ unsigned int h)
+{
+ union g2d_bitblt_cmd_val bitblt;
+ union g2d_point_val pt;
+
+ if (g2d_check_space(ctx, 7, 1))
+ return -ENOSPC;
+
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+ g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
+ g2d_add_base_addr(ctx, img, g2d_dst);
+ g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
+
+ if (x + w > img->width)
+ w = img->width - x;
+ if (y + h > img->height)
+ h = img->height - y;
+
+ pt.data.x = x;
+ pt.data.y = y;
+ g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+
+ pt.data.x = x + w;
+ pt.data.y = y + h;
+ g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+ g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
+
+ bitblt.val = 0;
+ bitblt.data.fast_solid_color_fill_en = 1;
+ g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
+
+ return g2d_flush(ctx);
+}
+
+/**
+ * g2d_copy - copy contents in source buffer to destination buffer.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ * information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ * information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @w: width value to source and destination buffers.
+ * @h: height value to source and destination buffers.
+ */
+drm_public int
+g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
+ unsigned int dst_x, unsigned dst_y, unsigned int w,
+ unsigned int h)
+{
+ union g2d_rop4_val rop4;
+ union g2d_point_val pt;
+ unsigned int src_w, src_h, dst_w, dst_h;
+
+ src_w = w;
+ src_h = h;
+ if (src_x + src->width > w)
+ src_w = src->width - src_x;
+ if (src_y + src->height > h)
+ src_h = src->height - src_y;
+
+ dst_w = w;
+ dst_h = w;
+ if (dst_x + dst->width > w)
+ dst_w = dst->width - dst_x;
+ if (dst_y + dst->height > h)
+ dst_h = dst->height - dst_y;
+
+ w = MIN(src_w, dst_w);
+ h = MIN(src_h, dst_h);
+
+ if (w <= 0 || h <= 0) {
+ fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+ return -EINVAL;
+ }
+
+ if (g2d_check_space(ctx, 11, 2))
+ return -ENOSPC;
+
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+ g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+ g2d_add_base_addr(ctx, dst, g2d_dst);
+ g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+ g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+ g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+ g2d_add_base_addr(ctx, src, g2d_src);
+ g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+
+ pt.data.x = src_x;
+ pt.data.y = src_y;
+ g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+ pt.data.x = src_x + w;
+ pt.data.y = src_y + h;
+ g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+ pt.data.x = dst_x;
+ pt.data.y = dst_y;
+ g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+ pt.data.x = dst_x + w;
+ pt.data.y = dst_y + h;
+ g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+ rop4.val = 0;
+ rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+ g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+ return g2d_flush(ctx);
+}
+
+/**
+ * g2d_move - copy content inside single buffer.
+ * Similar to libc's memmove() this copies a rectangular
+ * region of the provided buffer to another location, while
+ * properly handling the situation where source and
+ * destination rectangle overlap.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to g2d_image structure providing
+ * buffer information.
+ * @src_x: x position of source rectangle.
+ * @src_y: y position of source rectangle.
+ * @dst_x: x position of destination rectangle.
+ * @dst_y: y position of destination rectangle.
+ * @w: width of rectangle to move.
+ * @h: height of rectangle to move.
+ */
+drm_public int
+g2d_move(struct g2d_context *ctx, struct g2d_image *img,
+ unsigned int src_x, unsigned int src_y,
+ unsigned int dst_x, unsigned dst_y, unsigned int w,
+ unsigned int h)
+{
+ union g2d_rop4_val rop4;
+ union g2d_point_val pt;
+ union g2d_direction_val dir;
+ unsigned int src_w, src_h, dst_w, dst_h;
+
+ src_w = w;
+ src_h = h;
+ if (src_x + img->width > w)
+ src_w = img->width - src_x;
+ if (src_y + img->height > h)
+ src_h = img->height - src_y;
+
+ dst_w = w;
+ dst_h = w;
+ if (dst_x + img->width > w)
+ dst_w = img->width - dst_x;
+ if (dst_y + img->height > h)
+ dst_h = img->height - dst_y;
+
+ w = MIN(src_w, dst_w);
+ h = MIN(src_h, dst_h);
+
+ if (w == 0 || h == 0) {
+ fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+ return -EINVAL;
+ }
+
+ if (g2d_check_space(ctx, 13, 2))
+ return -ENOSPC;
+
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+ g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+ g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
+ g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode);
+
+ g2d_add_base_addr(ctx, img, g2d_dst);
+ g2d_add_base_addr(ctx, img, g2d_src);
+
+ g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
+ g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride);
+
+ dir.val[0] = dir.val[1] = 0;
+
+ if (dst_x >= src_x)
+ dir.data.src_x_direction = dir.data.dst_x_direction = 1;
+ if (dst_y >= src_y)
+ dir.data.src_y_direction = dir.data.dst_y_direction = 1;
+
+ g2d_set_direction(ctx, &dir);
+
+ pt.data.x = src_x;
+ pt.data.y = src_y;
+ g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+ pt.data.x = src_x + w;
+ pt.data.y = src_y + h;
+ g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+ pt.data.x = dst_x;
+ pt.data.y = dst_y;
+ g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+ pt.data.x = dst_x + w;
+ pt.data.y = dst_y + h;
+ g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+ rop4.val = 0;
+ rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+ g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+ return g2d_flush(ctx);
+}
+
+/**
+ * g2d_copy_with_scale - copy contents in source buffer to destination buffer
+ * scaling up or down properly.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ * information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ * information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @src_w: width value to source buffer.
+ * @src_h: height value to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @dst_w: width value to destination buffer.
+ * @dst_h: height value to destination buffer.
+ * @negative: indicate that it uses color negative to source and
+ * destination buffers.
+ */
+drm_public int
+g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x,
+ unsigned int src_y, unsigned int src_w,
+ unsigned int src_h, unsigned int dst_x,
+ unsigned int dst_y, unsigned int dst_w,
+ unsigned int dst_h, unsigned int negative)
+{
+ union g2d_rop4_val rop4;
+ union g2d_point_val pt;
+ unsigned int scale, repeat_pad;
+ unsigned int scale_x, scale_y;
+
+ /* Sanitize this parameter to facilitate space computation below. */
+ if (negative)
+ negative = 1;
+
+ if (src_w == dst_w && src_h == dst_h)
+ scale = 0;
+ else {
+ scale = 1;
+ scale_x = g2d_get_scaling(src_w, dst_w);
+ scale_y = g2d_get_scaling(src_h, dst_h);
+ }
+
+ repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0;
+
+ if (src_x + src_w > src->width)
+ src_w = src->width - src_x;
+ if (src_y + src_h > src->height)
+ src_h = src->height - src_y;
+
+ if (dst_x + dst_w > dst->width)
+ dst_w = dst->width - dst_x;
+ if (dst_y + dst_h > dst->height)
+ dst_h = dst->height - dst_y;
+
+ if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
+ fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+ return -EINVAL;
+ }
+
+ if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2))
+ return -ENOSPC;
+
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+ g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+ g2d_add_base_addr(ctx, dst, g2d_dst);
+ g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+ g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+ g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+ g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
+ if (repeat_pad)
+ g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
+
+ g2d_add_base_addr(ctx, src, g2d_src);
+ g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+
+ rop4.val = 0;
+ rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+
+ if (negative) {
+ g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
+ rop4.data.unmasked_rop3 ^= G2D_ROP3_DST;
+ }
+
+ g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+ if (scale) {
+ g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
+ g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
+ g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
+ }
+
+ pt.data.x = src_x;
+ pt.data.y = src_y;
+ g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+ pt.data.x = src_x + src_w;
+ pt.data.y = src_y + src_h;
+ g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+ pt.data.x = dst_x;
+ pt.data.y = dst_y;
+ g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+ pt.data.x = dst_x + dst_w;
+ pt.data.y = dst_y + dst_h;
+ g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+ return g2d_flush(ctx);
+}
+
+/**
+ * g2d_blend - blend image data in source and destination buffers.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ * information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ * information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @w: width value to source and destination buffer.
+ * @h: height value to source and destination buffer.
+ * @op: blend operation type.
+ */
+drm_public int
+g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x,
+ unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
+ unsigned int w, unsigned int h, enum e_g2d_op op)
+{
+ union g2d_point_val pt;
+ union g2d_bitblt_cmd_val bitblt;
+ union g2d_blend_func_val blend;
+ unsigned int gem_space;
+ unsigned int src_w, src_h, dst_w, dst_h;
+
+ src_w = w;
+ src_h = h;
+ if (src_x + w > src->width)
+ src_w = src->width - src_x;
+ if (src_y + h > src->height)
+ src_h = src->height - src_y;
+
+ dst_w = w;
+ dst_h = h;
+ if (dst_x + w > dst->width)
+ dst_w = dst->width - dst_x;
+ if (dst_y + h > dst->height)
+ dst_h = dst->height - dst_y;
+
+ w = MIN(src_w, dst_w);
+ h = MIN(src_h, dst_h);
+
+ if (w <= 0 || h <= 0) {
+ fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+ return -EINVAL;
+ }
+
+ if (!g2d_validate_select_mode(src->select_mode)) {
+ fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
+ return -EINVAL;
+ }
+
+ if (!g2d_validate_blending_op(op)) {
+ fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
+ return -EINVAL;
+ }
+
+ gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
+
+ if (g2d_check_space(ctx, 12, gem_space))
+ return -ENOSPC;
+
+ bitblt.val = 0;
+ blend.val = 0;
+
+ if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+ else
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+ g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+ g2d_add_base_addr(ctx, dst, g2d_dst);
+ g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+ g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
+ g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+ switch (src->select_mode) {
+ case G2D_SELECT_MODE_NORMAL:
+ g2d_add_base_addr(ctx, src, g2d_src);
+ g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+ break;
+ case G2D_SELECT_MODE_FGCOLOR:
+ g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
+ break;
+ case G2D_SELECT_MODE_BGCOLOR:
+ g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
+ break;
+ }
+
+ bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
+ blend.val = g2d_get_blend_op(op);
+ g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
+ g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
+
+ pt.data.x = src_x;
+ pt.data.y = src_y;
+ g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+ pt.data.x = src_x + w;
+ pt.data.y = src_y + h;
+ g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+ pt.data.x = dst_x;
+ pt.data.y = dst_y;
+ g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+ pt.data.x = dst_x + w;
+ pt.data.y = dst_y + h;
+ g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+ return g2d_flush(ctx);
+}
+
+/**
+ * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ * information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ * information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @src_w: width value to source buffer.
+ * @src_h: height value to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @dst_w: width value to destination buffer.
+ * @dst_h: height value to destination buffer.
+ * @op: blend operation type.
+ */
+drm_public int
+g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
+ unsigned int src_w, unsigned int src_h, unsigned int dst_x,
+ unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
+ enum e_g2d_op op)
+{
+ union g2d_point_val pt;
+ union g2d_bitblt_cmd_val bitblt;
+ union g2d_blend_func_val blend;
+ unsigned int scale, gem_space;
+ unsigned int scale_x, scale_y;
+
+ if (src_w == dst_w && src_h == dst_h)
+ scale = 0;
+ else {
+ scale = 1;
+ scale_x = g2d_get_scaling(src_w, dst_w);
+ scale_y = g2d_get_scaling(src_h, dst_h);
+ }
+
+ if (src_x + src_w > src->width)
+ src_w = src->width - src_x;
+ if (src_y + src_h > src->height)
+ src_h = src->height - src_y;
+
+ if (dst_x + dst_w > dst->width)
+ dst_w = dst->width - dst_x;
+ if (dst_y + dst_h > dst->height)
+ dst_h = dst->height - dst_y;
+
+ if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
+ fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+ return -EINVAL;
+ }
+
+ if (!g2d_validate_select_mode(src->select_mode)) {
+ fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
+ return -EINVAL;
+ }
+
+ if (!g2d_validate_blending_op(op)) {
+ fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
+ return -EINVAL;
+ }
+
+ gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
+
+ if (g2d_check_space(ctx, 12 + scale * 3, gem_space))
+ return -ENOSPC;
+
+ bitblt.val = 0;
+ blend.val = 0;
+
+ if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+ else
+ g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+ g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+ g2d_add_base_addr(ctx, dst, g2d_dst);
+ g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+ g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
+ g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+ switch (src->select_mode) {
+ case G2D_SELECT_MODE_NORMAL:
+ g2d_add_base_addr(ctx, src, g2d_src);
+ g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+ break;
+ case G2D_SELECT_MODE_FGCOLOR:
+ g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
+ break;
+ case G2D_SELECT_MODE_BGCOLOR:
+ g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
+ break;
+ }
+
+ if (scale) {
+ g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
+ g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
+ g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
+ }
+
+ bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
+ blend.val = g2d_get_blend_op(op);
+ g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
+ g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
+
+ pt.data.x = src_x;
+ pt.data.y = src_y;
+ g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+ pt.data.x = src_x + src_w;
+ pt.data.y = src_y + src_h;
+ g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+ pt.data.x = dst_x;
+ pt.data.y = dst_y;
+ g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+ pt.data.x = dst_x + dst_w;
+ pt.data.y = dst_y + dst_h;
+ g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+ return g2d_flush(ctx);
+}
diff --git a/exynos/exynos_fimg2d.h b/exynos/exynos_fimg2d.h
new file mode 100644
index 0000000..a4dfbe7
--- /dev/null
+++ b/exynos/exynos_fimg2d.h
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ *
+ * 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
+ * VA LINUX SYSTEMS AND/OR ITS 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 _FIMG2D_H_
+#define _FIMG2D_H_
+
+#define G2D_PLANE_MAX_NR 2
+
+enum e_g2d_color_mode {
+ /* COLOR FORMAT */
+ G2D_COLOR_FMT_XRGB8888,
+ G2D_COLOR_FMT_ARGB8888,
+ G2D_COLOR_FMT_RGB565,
+ G2D_COLOR_FMT_XRGB1555,
+ G2D_COLOR_FMT_ARGB1555,
+ G2D_COLOR_FMT_XRGB4444,
+ G2D_COLOR_FMT_ARGB4444,
+ G2D_COLOR_FMT_PRGB888,
+ G2D_COLOR_FMT_YCbCr444,
+ G2D_COLOR_FMT_YCbCr422,
+ G2D_COLOR_FMT_YCbCr420,
+ /* alpha 8bit */
+ G2D_COLOR_FMT_A8,
+ /* Luminance 8bit: gray color */
+ G2D_COLOR_FMT_L8,
+ /* alpha 1bit */
+ G2D_COLOR_FMT_A1,
+ /* alpha 4bit */
+ G2D_COLOR_FMT_A4,
+ G2D_COLOR_FMT_MASK, /* VER4.1 */
+
+ /* COLOR ORDER */
+ G2D_ORDER_AXRGB = (0 << 4), /* VER4.1 */
+ G2D_ORDER_RGBAX = (1 << 4), /* VER4.1 */
+ G2D_ORDER_AXBGR = (2 << 4), /* VER4.1 */
+ G2D_ORDER_BGRAX = (3 << 4), /* VER4.1 */
+ G2D_ORDER_MASK = (3 << 4), /* VER4.1 */
+
+ /* Number of YCbCr plane */
+ G2D_YCbCr_1PLANE = (0 << 8), /* VER4.1 */
+ G2D_YCbCr_2PLANE = (1 << 8), /* VER4.1 */
+ G2D_YCbCr_PLANE_MASK = (3 << 8), /* VER4.1 */
+
+ /* Order in YCbCr */
+ G2D_YCbCr_ORDER_CrY1CbY0 = (0 << 12), /* VER4.1 */
+ G2D_YCbCr_ORDER_CbY1CrY0 = (1 << 12), /* VER4.1 */
+ G2D_YCbCr_ORDER_Y1CrY0Cb = (2 << 12), /* VER4.1 */
+ G2D_YCbCr_ORDER_Y1CbY0Cr = (3 << 12), /* VER4.1 */
+ G2D_YCbCr_ORDER_CrCb = G2D_YCbCr_ORDER_CrY1CbY0, /* VER4.1 */
+ G2D_YCbCr_ORDER_CbCr = G2D_YCbCr_ORDER_CbY1CrY0, /* VER4.1 */
+ G2D_YCbCr_ORDER_MASK = (3 < 12), /* VER4.1 */
+
+ /* CSC */
+ G2D_CSC_601 = (0 << 16), /* VER4.1 */
+ G2D_CSC_709 = (1 << 16), /* VER4.1 */
+ G2D_CSC_MASK = (1 << 16), /* VER4.1 */
+
+ /* Valid value range of YCbCr */
+ G2D_YCbCr_RANGE_NARROW = (0 << 17), /* VER4.1 */
+ G2D_YCbCr_RANGE_WIDE = (1 << 17), /* VER4.1 */
+ G2D_YCbCr_RANGE_MASK = (1 << 17), /* VER4.1 */
+
+ G2D_COLOR_MODE_MASK = 0xFFFFFFFF,
+};
+
+enum e_g2d_select_mode {
+ G2D_SELECT_MODE_NORMAL = (0 << 0),
+ G2D_SELECT_MODE_FGCOLOR = (1 << 0),
+ G2D_SELECT_MODE_BGCOLOR = (2 << 0),
+};
+
+enum e_g2d_repeat_mode {
+ G2D_REPEAT_MODE_REPEAT,
+ G2D_REPEAT_MODE_PAD,
+ G2D_REPEAT_MODE_REFLECT,
+ G2D_REPEAT_MODE_CLAMP,
+ G2D_REPEAT_MODE_NONE,
+};
+
+enum e_g2d_scale_mode {
+ G2D_SCALE_MODE_NONE = 0,
+ G2D_SCALE_MODE_NEAREST,
+ G2D_SCALE_MODE_BILINEAR,
+ G2D_SCALE_MODE_MAX,
+};
+
+enum e_g2d_buf_type {
+ G2D_IMGBUF_COLOR,
+ G2D_IMGBUF_GEM,
+ G2D_IMGBUF_USERPTR,
+};
+
+enum e_g2d_rop3_type {
+ G2D_ROP3_DST = 0xAA,
+ G2D_ROP3_SRC = 0xCC,
+ G2D_ROP3_3RD = 0xF0,
+ G2D_ROP3_MASK = 0xFF,
+};
+
+enum e_g2d_select_alpha_src {
+ G2D_SELECT_SRC_FOR_ALPHA_BLEND, /* VER4.1 */
+ G2D_SELECT_ROP_FOR_ALPHA_BLEND, /* VER4.1 */
+};
+
+enum e_g2d_transparent_mode {
+ G2D_TRANSPARENT_MODE_OPAQUE,
+ G2D_TRANSPARENT_MODE_TRANSPARENT,
+ G2D_TRANSPARENT_MODE_BLUESCREEN,
+ G2D_TRANSPARENT_MODE_MAX,
+};
+
+enum e_g2d_color_key_mode {
+ G2D_COLORKEY_MODE_DISABLE = 0,
+ G2D_COLORKEY_MODE_SRC_RGBA = (1<<0),
+ G2D_COLORKEY_MODE_DST_RGBA = (1<<1),
+ G2D_COLORKEY_MODE_SRC_YCbCr = (1<<2), /* VER4.1 */
+ G2D_COLORKEY_MODE_DST_YCbCr = (1<<3), /* VER4.1 */
+ G2D_COLORKEY_MODE_MASK = 15,
+};
+
+enum e_g2d_alpha_blend_mode {
+ G2D_ALPHA_BLEND_MODE_DISABLE,
+ G2D_ALPHA_BLEND_MODE_ENABLE,
+ G2D_ALPHA_BLEND_MODE_FADING, /* VER3.0 */
+ G2D_ALPHA_BLEND_MODE_MAX,
+};
+
+enum e_g2d_op {
+ G2D_OP_CLEAR = 0x00,
+ G2D_OP_SRC = 0x01,
+ G2D_OP_DST = 0x02,
+ G2D_OP_OVER = 0x03,
+ G2D_OP_INTERPOLATE = 0x04,
+ G2D_OP_DISJOINT_CLEAR = 0x10,
+ G2D_OP_DISJOINT_SRC = 0x11,
+ G2D_OP_DISJOINT_DST = 0x12,
+ G2D_OP_CONJOINT_CLEAR = 0x20,
+ G2D_OP_CONJOINT_SRC = 0x21,
+ G2D_OP_CONJOINT_DST = 0x22,
+};
+
+/*
+ * The G2D_COEFF_MODE_DST_{COLOR,ALPHA} modes both use the ALPHA_REG(0x618)
+ * register. The registers fields are as follows:
+ * bits 31:8 = color value (RGB order)
+ * bits 7:0 = alpha value
+ */
+enum e_g2d_coeff_mode {
+ G2D_COEFF_MODE_ONE,
+ G2D_COEFF_MODE_ZERO,
+ G2D_COEFF_MODE_SRC_ALPHA,
+ G2D_COEFF_MODE_SRC_COLOR,
+ G2D_COEFF_MODE_DST_ALPHA,
+ G2D_COEFF_MODE_DST_COLOR,
+ /* Global Alpha : Set by ALPHA_REG(0x618) */
+ G2D_COEFF_MODE_GB_ALPHA,
+ /* Global Color and Alpha : Set by ALPHA_REG(0x618) */
+ G2D_COEFF_MODE_GB_COLOR,
+ /* (1-SRC alpha)/DST Alpha */
+ G2D_COEFF_MODE_DISJOINT_S,
+ /* (1-DST alpha)/SRC Alpha */
+ G2D_COEFF_MODE_DISJOINT_D,
+ /* SRC alpha/DST alpha */
+ G2D_COEFF_MODE_CONJOINT_S,
+ /* DST alpha/SRC alpha */
+ G2D_COEFF_MODE_CONJOINT_D,
+ /* DST alpha/SRC alpha */
+ G2D_COEFF_MODE_MASK
+};
+
+enum e_g2d_acoeff_mode {
+ G2D_ACOEFF_MODE_A, /* alpha */
+ G2D_ACOEFF_MODE_APGA, /* alpha + global alpha */
+ G2D_ACOEFF_MODE_AMGA, /* alpha * global alpha */
+ G2D_ACOEFF_MODE_MASK
+};
+
+union g2d_point_val {
+ unsigned int val;
+ struct {
+ /*
+ * Coordinate of Source Image
+ * Range: 0 ~ 8000 (Requirement: SrcLeftX < SrcRightX)
+ * In YCbCr 422 and YCbCr 420 format with even number.
+ */
+ unsigned int x:16;
+ /*
+ * Y Coordinate of Source Image
+ * Range: 0 ~ 8000 (Requirement: SrcTopY < SrcBottomY)
+ * In YCbCr 420 format with even number.
+ */
+ unsigned int y:16;
+ } data;
+};
+
+union g2d_rop4_val {
+ unsigned int val;
+ struct {
+ enum e_g2d_rop3_type unmasked_rop3:8;
+ enum e_g2d_rop3_type masked_rop3:8;
+ unsigned int reserved:16;
+ } data;
+};
+
+union g2d_bitblt_cmd_val {
+ unsigned int val;
+ struct {
+ /* [0:3] */
+ unsigned int mask_rop4_en:1;
+ unsigned int masking_en:1;
+ enum e_g2d_select_alpha_src rop4_alpha_en:1;
+ unsigned int dither_en:1;
+ /* [4:7] */
+ unsigned int resolved1:4;
+ /* [8:11] */
+ unsigned int cw_en:4;
+ /* [12:15] */
+ enum e_g2d_transparent_mode transparent_mode:4;
+ /* [16:19] */
+ enum e_g2d_color_key_mode color_key_mode:4;
+ /* [20:23] */
+ enum e_g2d_alpha_blend_mode alpha_blend_mode:4;
+ /* [24:27] */
+ unsigned int src_pre_multiply:1;
+ unsigned int pat_pre_multiply:1;
+ unsigned int dst_pre_multiply:1;
+ unsigned int dst_depre_multiply:1;
+ /* [28:31] */
+ unsigned int fast_solid_color_fill_en:1;
+ unsigned int reserved:3;
+ } data;
+};
+
+union g2d_blend_func_val {
+ unsigned int val;
+ struct {
+ /* [0:15] */
+ enum e_g2d_coeff_mode src_coeff:4;
+ enum e_g2d_acoeff_mode src_coeff_src_a:2;
+ enum e_g2d_acoeff_mode src_coeff_dst_a:2;
+ enum e_g2d_coeff_mode dst_coeff:4;
+ enum e_g2d_acoeff_mode dst_coeff_src_a:2;
+ enum e_g2d_acoeff_mode dst_coeff_dst_a:2;
+ /* [16:19] */
+ unsigned int inv_src_color_coeff:1;
+ unsigned int resoled1:1;
+ unsigned int inv_dst_color_coeff:1;
+ unsigned int resoled2:1;
+ /* [20:23] */
+ unsigned int lighten_en:1;
+ unsigned int darken_en:1;
+ unsigned int win_ce_src_over_en:2;
+ /* [24:31] */
+ unsigned int reserved:8;
+ } data;
+};
+
+struct g2d_image {
+ enum e_g2d_select_mode select_mode;
+ enum e_g2d_color_mode color_mode;
+ enum e_g2d_repeat_mode repeat_mode;
+ enum e_g2d_scale_mode scale_mode;
+ unsigned int xscale;
+ unsigned int yscale;
+ unsigned char rotate_90;
+ unsigned char x_dir;
+ unsigned char y_dir;
+ unsigned char component_alpha;
+ unsigned int width;
+ unsigned int height;
+ unsigned int stride;
+ unsigned int need_free;
+ unsigned int color;
+ enum e_g2d_buf_type buf_type;
+ unsigned int bo[G2D_PLANE_MAX_NR];
+ struct drm_exynos_g2d_userptr user_ptr[G2D_PLANE_MAX_NR];
+ void *mapped_ptr[G2D_PLANE_MAX_NR];
+};
+
+struct g2d_context;
+
+struct g2d_context *g2d_init(int fd);
+void g2d_fini(struct g2d_context *ctx);
+void g2d_config_event(struct g2d_context *ctx, void *userdata);
+int g2d_exec(struct g2d_context *ctx);
+int g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
+ unsigned int x, unsigned int y, unsigned int w,
+ unsigned int h);
+int g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x,
+ unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
+ unsigned int w, unsigned int h);
+int g2d_move(struct g2d_context *ctx, struct g2d_image *img,
+ unsigned int src_x, unsigned int src_y, unsigned int dst_x,
+ unsigned dst_y, unsigned int w, unsigned int h);
+int g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x,
+ unsigned int src_y, unsigned int src_w,
+ unsigned int src_h, unsigned int dst_x,
+ unsigned int dst_y, unsigned int dst_w,
+ unsigned int dst_h, unsigned int negative);
+int g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x,
+ unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
+ unsigned int w, unsigned int h, enum e_g2d_op op);
+int g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
+ struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
+ unsigned int src_w, unsigned int src_h, unsigned int dst_x,
+ unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
+ enum e_g2d_op op);
+#endif /* _FIMG2D_H_ */
diff --git a/exynos/fimg2d_reg.h b/exynos/fimg2d_reg.h
new file mode 100644
index 0000000..d42296d
--- /dev/null
+++ b/exynos/fimg2d_reg.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ *
+ * 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
+ * VA LINUX SYSTEMS AND/OR ITS 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 _FIMG2D_REG_H_
+#define _FIMG2D_REG_H_
+
+#define SOFT_RESET_REG (0x0000)
+#define INTEN_REG (0x0004)
+#define INTC_PEND_REG (0x000C)
+#define FIFO_STAT_REG (0x0010)
+#define AXI_MODE_REG (0x001C)
+#define DMA_SFR_BASE_ADDR_REG (0x0080)
+#define DMA_COMMAND_REG (0x0084)
+#define DMA_EXE_LIST_NUM_REG (0x0088)
+#define DMA_STATUS_REG (0x008C)
+#define DMA_HOLD_CMD_REG (0x0090)
+
+/* COMMAND REGISTER */
+#define BITBLT_START_REG (0x0100)
+#define BITBLT_COMMAND_REG (0x0104)
+#define BLEND_FUNCTION_REG (0x0108) /* VER4.1 */
+#define ROUND_MODE_REG (0x010C) /* VER4.1 */
+
+/* PARAMETER SETTING REGISTER */
+#define ROTATE_REG (0x0200)
+#define SRC_MASK_DIRECT_REG (0x0204)
+#define DST_PAT_DIRECT_REG (0x0208)
+
+/* SOURCE */
+#define SRC_SELECT_REG (0x0300)
+#define SRC_BASE_ADDR_REG (0x0304)
+#define SRC_STRIDE_REG (0x0308)
+#define SRC_COLOR_MODE_REG (0x030c)
+#define SRC_LEFT_TOP_REG (0x0310)
+#define SRC_RIGHT_BOTTOM_REG (0x0314)
+#define SRC_PLANE2_BASE_ADDR_REG (0x0318) /* VER4.1 */
+#define SRC_REPEAT_MODE_REG (0x031C)
+#define SRC_PAD_VALUE_REG (0x0320)
+#define SRC_A8_RGB_EXT_REG (0x0324)
+#define SRC_SCALE_CTRL_REG (0x0328)
+#define SRC_XSCALE_REG (0x032C)
+#define SRC_YSCALE_REG (0x0330)
+
+/* DESTINATION */
+#define DST_SELECT_REG (0x0400)
+#define DST_BASE_ADDR_REG (0x0404)
+#define DST_STRIDE_REG (0x0408)
+#define DST_COLOR_MODE_REG (0x040C)
+#define DST_LEFT_TOP_REG (0x0410)
+#define DST_RIGHT_BOTTOM_REG (0x0414)
+#define DST_PLANE2_BASE_ADDR_REG (0x0418) /* VER4.1 */
+#define DST_A8_RGB_EXT_REG (0x041C)
+
+/* PATTERN */
+#define PAT_BASE_ADDR_REG (0x0500)
+#define PAT_SIZE_REG (0x0504)
+#define PAT_COLOR_MODE_REG (0x0508)
+#define PAT_OFFSET_REG (0x050C)
+#define PAT_STRIDE_REG (0x0510)
+
+/* MASK */
+#define MASK_BASE_ADDR_REG (0x0520)
+#define MASK_STRIDE_REG (0x0524)
+#define MASK_LEFT_TOP_REG (0x0528) /* VER4.1 */
+#define MASK_RIGHT_BOTTOM_REG (0x052C) /* VER4.1 */
+#define MASK_MODE_REG (0x0530) /* VER4.1 */
+#define MASK_REPEAT_MODE_REG (0x0534)
+#define MASK_PAD_VALUE_REG (0x0538)
+#define MASK_SCALE_CTRL_REG (0x053C)
+#define MASK_XSCALE_REG (0x0540)
+#define MASK_YSCALE_REG (0x0544)
+
+/* CLIPPING WINDOW */
+#define CW_LT_REG (0x0600)
+#define CW_RB_REG (0x0604)
+
+/* ROP & ALPHA SETTING */
+#define THIRD_OPERAND_REG (0x0610)
+#define ROP4_REG (0x0614)
+#define ALPHA_REG (0x0618)
+
+/* COLOR SETTING */
+#define FG_COLOR_REG (0x0700)
+#define BG_COLOR_REG (0x0704)
+#define BS_COLOR_REG (0x0708)
+#define SF_COLOR_REG (0x070C) /* VER4.1 */
+
+/* COLOR KEY */
+#define SRC_COLORKEY_CTRL_REG (0x0710)
+#define SRC_COLORKEY_DR_MIN_REG (0x0714)
+#define SRC_COLORKEY_DR_MAX_REG (0x0718)
+#define DST_COLORKEY_CTRL_REG (0x071C)
+#define DST_COLORKEY_DR_MIN_REG (0x0720)
+#define DST_COLORKEY_DR_MAX_REG (0x0724)
+/* YCbCr src Color Key */
+#define YCbCr_SRC_COLORKEY_CTRL_REG (0x0728) /* VER4.1 */
+#define YCbCr_SRC_COLORKEY_DR_MIN_REG (0x072C) /* VER4.1 */
+#define YCbCr_SRC_COLORKEY_DR_MAX_REG (0x0730) /* VER4.1 */
+/*Y CbCr dst Color Key */
+#define YCbCr_DST_COLORKEY_CTRL_REG (0x0734) /* VER4.1 */
+#define YCbCr_DST_COLORKEY_DR_MIN_REG (0x0738) /* VER4.1 */
+#define YCbCr_DST_COLORKEY_DR_MAX_REG (0x073C) /* VER4.1 */
+
+#endif
+
diff --git a/exynos/libdrm_exynos.pc.in b/exynos/libdrm_exynos.pc.in
new file mode 100644
index 0000000..ff1c432
--- /dev/null
+++ b/exynos/libdrm_exynos.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_exynos
+Description: Userspace interface to exynos kernel DRM services
+Version: 0.7
+Libs: -L${libdir} -ldrm_exynos
+Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/exynos
+Requires.private: libdrm
diff --git a/exynos/meson.build b/exynos/meson.build
new file mode 100644
index 0000000..40d66fc
--- /dev/null
+++ b/exynos/meson.build
@@ -0,0 +1,57 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+libdrm_exynos = shared_library(
+ 'drm_exynos',
+ [files('exynos_drm.c', 'exynos_fimg2d.c'), config_file],
+ c_args : libdrm_c_args,
+ include_directories : [inc_root, inc_drm],
+ link_with : libdrm,
+ dependencies : [dep_pthread_stubs],
+ version : '1.0.0',
+ install : true,
+)
+
+install_headers('exynos_drmif.h', subdir : 'libdrm')
+install_headers('exynos_drm.h', 'exynos_fimg2d.h', subdir : 'exynos')
+
+ext_libdrm_exynos = declare_dependency(
+ link_with : [libdrm, libdrm_exynos],
+ include_directories : [inc_drm, include_directories('.')],
+)
+
+pkg.generate(
+ name : 'libdrm_exynos',
+ libraries : libdrm_exynos,
+ subdirs : ['.', 'libdrm', 'exynos'],
+ version : '0.7',
+ requires_private : 'libdrm',
+ description : 'Userspace interface to exynos kernel DRM services',
+)
+
+test(
+ 'exynos-symbols-check',
+ symbols_check,
+ args : [
+ '--lib', libdrm_exynos,
+ '--symbols-file', files('exynos-symbols.txt'),
+ '--nm', prog_nm.path(),
+ ],
+)
diff --git a/freedreno/Android.mk b/freedreno/Android.mk
new file mode 100644
index 0000000..2b582ae
--- /dev/null
+++ b/freedreno/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_FREEDRENO_FILES, LIBDRM_FREEDRENO_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_freedreno
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_FREEDRENO_FILES)
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/freedreno/Makefile.sources b/freedreno/Makefile.sources
new file mode 100644
index 0000000..ca89511
--- /dev/null
+++ b/freedreno/Makefile.sources
@@ -0,0 +1,25 @@
+LIBDRM_FREEDRENO_FILES := \
+ freedreno_device.c \
+ freedreno_pipe.c \
+ freedreno_priv.h \
+ freedreno_ringbuffer.c \
+ freedreno_bo.c \
+ freedreno_bo_cache.c \
+ msm/msm_bo.c \
+ msm/msm_device.c \
+ msm/msm_pipe.c \
+ msm/msm_priv.h \
+ msm/msm_ringbuffer.c
+
+LIBDRM_FREEDRENO_KGSL_FILES := \
+ kgsl/kgsl_bo.c \
+ kgsl/kgsl_device.c \
+ kgsl/kgsl_drm.h \
+ kgsl/kgsl_pipe.c \
+ kgsl/kgsl_priv.h \
+ kgsl/kgsl_ringbuffer.c \
+ kgsl/msm_kgsl.h
+
+LIBDRM_FREEDRENO_H_FILES := \
+ freedreno_drmif.h \
+ freedreno_ringbuffer.h
diff --git a/freedreno/freedreno-symbols.txt b/freedreno/freedreno-symbols.txt
new file mode 100644
index 0000000..471ca99
--- /dev/null
+++ b/freedreno/freedreno-symbols.txt
@@ -0,0 +1,45 @@
+fd_bo_cpu_fini
+fd_bo_cpu_prep
+fd_bo_del
+fd_bo_dmabuf
+fd_bo_from_dmabuf
+fd_bo_from_fbdev
+fd_bo_from_handle
+fd_bo_from_name
+fd_bo_get_iova
+fd_bo_get_name
+fd_bo_handle
+fd_bo_map
+fd_bo_new
+fd_bo_put_iova
+fd_bo_ref
+fd_bo_size
+fd_device_del
+fd_device_fd
+fd_device_new
+fd_device_new_dup
+fd_device_ref
+fd_device_version
+fd_pipe_del
+fd_pipe_get_param
+fd_pipe_new
+fd_pipe_new2
+fd_pipe_ref
+fd_pipe_wait
+fd_pipe_wait_timeout
+fd_ringbuffer_cmd_count
+fd_ringbuffer_del
+fd_ringbuffer_emit_reloc_ring_full
+fd_ringbuffer_flush
+fd_ringbuffer_grow
+fd_ringbuffer_new
+fd_ringbuffer_new_flags
+fd_ringbuffer_new_object
+fd_ringbuffer_ref
+fd_ringbuffer_reloc
+fd_ringbuffer_reloc2
+fd_ringbuffer_reset
+fd_ringbuffer_set_parent
+fd_ringbuffer_size
+fd_ringbuffer_timestamp
+fd_ringbuffer_flush2
diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c
new file mode 100644
index 0000000..efc5b71
--- /dev/null
+++ b/freedreno/freedreno_bo.c
@@ -0,0 +1,365 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private void bo_del(struct fd_bo *bo);
+
+/* set buffer name, and add to table, call w/ table_lock held: */
+static void set_name(struct fd_bo *bo, uint32_t name)
+{
+ bo->name = name;
+ /* add ourself into the handle table: */
+ drmHashInsert(bo->dev->name_table, name, bo);
+}
+
+/* lookup a buffer, call w/ table_lock held: */
+static struct fd_bo * lookup_bo(void *tbl, uint32_t key)
+{
+ struct fd_bo *bo = NULL;
+ if (!drmHashLookup(tbl, key, (void **)&bo)) {
+ /* found, incr refcnt and return: */
+ bo = fd_bo_ref(bo);
+
+ /* don't break the bucket if this bo was found in one */
+ list_delinit(&bo->list);
+ }
+ return bo;
+}
+
+/* allocate a new buffer object, call w/ table_lock held */
+static struct fd_bo * bo_from_handle(struct fd_device *dev,
+ uint32_t size, uint32_t handle)
+{
+ struct fd_bo *bo;
+
+ bo = dev->funcs->bo_from_handle(dev, size, handle);
+ if (!bo) {
+ struct drm_gem_close req = {
+ .handle = handle,
+ };
+ drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+ return NULL;
+ }
+ bo->dev = fd_device_ref(dev);
+ bo->size = size;
+ bo->handle = handle;
+ atomic_set(&bo->refcnt, 1);
+ list_inithead(&bo->list);
+ /* add ourself into the handle table: */
+ drmHashInsert(dev->handle_table, handle, bo);
+ return bo;
+}
+
+static struct fd_bo *
+bo_new(struct fd_device *dev, uint32_t size, uint32_t flags,
+ struct fd_bo_cache *cache)
+{
+ struct fd_bo *bo = NULL;
+ uint32_t handle;
+ int ret;
+
+ bo = fd_bo_cache_alloc(cache, &size, flags);
+ if (bo)
+ return bo;
+
+ ret = dev->funcs->bo_new_handle(dev, size, flags, &handle);
+ if (ret)
+ return NULL;
+
+ pthread_mutex_lock(&table_lock);
+ bo = bo_from_handle(dev, size, handle);
+ pthread_mutex_unlock(&table_lock);
+
+ VG_BO_ALLOC(bo);
+
+ return bo;
+}
+
+drm_public struct fd_bo *
+fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
+{
+ struct fd_bo *bo = bo_new(dev, size, flags, &dev->bo_cache);
+ if (bo)
+ bo->bo_reuse = BO_CACHE;
+ return bo;
+}
+
+/* internal function to allocate bo's that use the ringbuffer cache
+ * instead of the normal bo_cache. The purpose is, because cmdstream
+ * bo's get vmap'd on the kernel side, and that is expensive, we want
+ * to re-use cmdstream bo's for cmdstream and not unrelated purposes.
+ */
+drm_private struct fd_bo *
+fd_bo_new_ring(struct fd_device *dev, uint32_t size, uint32_t flags)
+{
+ struct fd_bo *bo = bo_new(dev, size, flags, &dev->ring_cache);
+ if (bo)
+ bo->bo_reuse = RING_CACHE;
+ return bo;
+}
+
+drm_public struct fd_bo *
+fd_bo_from_handle(struct fd_device *dev, uint32_t handle, uint32_t size)
+{
+ struct fd_bo *bo = NULL;
+
+ pthread_mutex_lock(&table_lock);
+
+ bo = lookup_bo(dev->handle_table, handle);
+ if (bo)
+ goto out_unlock;
+
+ bo = bo_from_handle(dev, size, handle);
+
+ VG_BO_ALLOC(bo);
+
+out_unlock:
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+drm_public struct fd_bo *
+fd_bo_from_dmabuf(struct fd_device *dev, int fd)
+{
+ int ret, size;
+ uint32_t handle;
+ struct fd_bo *bo;
+
+ pthread_mutex_lock(&table_lock);
+ ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
+ if (ret) {
+ pthread_mutex_unlock(&table_lock);
+ return NULL;
+ }
+
+ bo = lookup_bo(dev->handle_table, handle);
+ if (bo)
+ goto out_unlock;
+
+ /* lseek() to get bo size */
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_CUR);
+
+ bo = bo_from_handle(dev, size, handle);
+
+ VG_BO_ALLOC(bo);
+
+out_unlock:
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+drm_public struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name)
+{
+ struct drm_gem_open req = {
+ .name = name,
+ };
+ struct fd_bo *bo;
+
+ pthread_mutex_lock(&table_lock);
+
+ /* check name table first, to see if bo is already open: */
+ bo = lookup_bo(dev->name_table, name);
+ if (bo)
+ goto out_unlock;
+
+ if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+ ERROR_MSG("gem-open failed: %s", strerror(errno));
+ goto out_unlock;
+ }
+
+ bo = lookup_bo(dev->handle_table, req.handle);
+ if (bo)
+ goto out_unlock;
+
+ bo = bo_from_handle(dev, req.size, req.handle);
+ if (bo) {
+ set_name(bo, name);
+ VG_BO_ALLOC(bo);
+ }
+
+out_unlock:
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+drm_public uint64_t fd_bo_get_iova(struct fd_bo *bo)
+{
+ return bo->funcs->iova(bo);
+}
+
+drm_public void fd_bo_put_iova(struct fd_bo *bo)
+{
+ /* currently a no-op */
+}
+
+drm_public struct fd_bo * fd_bo_ref(struct fd_bo *bo)
+{
+ atomic_inc(&bo->refcnt);
+ return bo;
+}
+
+drm_public void fd_bo_del(struct fd_bo *bo)
+{
+ struct fd_device *dev = bo->dev;
+
+ if (!atomic_dec_and_test(&bo->refcnt))
+ return;
+
+ pthread_mutex_lock(&table_lock);
+
+ if ((bo->bo_reuse == BO_CACHE) && (fd_bo_cache_free(&dev->bo_cache, bo) == 0))
+ goto out;
+ if ((bo->bo_reuse == RING_CACHE) && (fd_bo_cache_free(&dev->ring_cache, bo) == 0))
+ goto out;
+
+ bo_del(bo);
+ fd_device_del_locked(dev);
+out:
+ pthread_mutex_unlock(&table_lock);
+}
+
+/* Called under table_lock */
+drm_private void bo_del(struct fd_bo *bo)
+{
+ VG_BO_FREE(bo);
+
+ if (bo->map)
+ drm_munmap(bo->map, bo->size);
+
+ /* TODO probably bo's in bucket list get removed from
+ * handle table??
+ */
+
+ if (bo->handle) {
+ struct drm_gem_close req = {
+ .handle = bo->handle,
+ };
+ drmHashDelete(bo->dev->handle_table, bo->handle);
+ if (bo->name)
+ drmHashDelete(bo->dev->name_table, bo->name);
+ drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+ }
+
+ bo->funcs->destroy(bo);
+}
+
+drm_public int fd_bo_get_name(struct fd_bo *bo, uint32_t *name)
+{
+ if (!bo->name) {
+ struct drm_gem_flink req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
+ if (ret) {
+ return ret;
+ }
+
+ pthread_mutex_lock(&table_lock);
+ set_name(bo, req.name);
+ pthread_mutex_unlock(&table_lock);
+ bo->bo_reuse = NO_CACHE;
+ }
+
+ *name = bo->name;
+
+ return 0;
+}
+
+drm_public uint32_t fd_bo_handle(struct fd_bo *bo)
+{
+ return bo->handle;
+}
+
+drm_public int fd_bo_dmabuf(struct fd_bo *bo)
+{
+ int ret, prime_fd;
+
+ ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+ &prime_fd);
+ if (ret) {
+ ERROR_MSG("failed to get dmabuf fd: %d", ret);
+ return ret;
+ }
+
+ bo->bo_reuse = NO_CACHE;
+
+ return prime_fd;
+}
+
+drm_public uint32_t fd_bo_size(struct fd_bo *bo)
+{
+ return bo->size;
+}
+
+drm_public void * fd_bo_map(struct fd_bo *bo)
+{
+ if (!bo->map) {
+ uint64_t offset;
+ int ret;
+
+ ret = bo->funcs->offset(bo, &offset);
+ if (ret) {
+ return NULL;
+ }
+
+ bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ bo->dev->fd, offset);
+ if (bo->map == MAP_FAILED) {
+ ERROR_MSG("mmap failed: %s", strerror(errno));
+ bo->map = NULL;
+ }
+ }
+ return bo->map;
+}
+
+/* a bit odd to take the pipe as an arg, but it's a, umm, quirk of kgsl.. */
+drm_public int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
+{
+ return bo->funcs->cpu_prep(bo, pipe, op);
+}
+
+drm_public void fd_bo_cpu_fini(struct fd_bo *bo)
+{
+ bo->funcs->cpu_fini(bo);
+}
+
+#if !HAVE_FREEDRENO_KGSL
+drm_public struct fd_bo * fd_bo_from_fbdev(struct fd_pipe *pipe, int fbfd, uint32_t size)
+{
+ return NULL;
+}
+#endif
diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c
new file mode 100644
index 0000000..bb0605a
--- /dev/null
+++ b/freedreno/freedreno_bo_cache.c
@@ -0,0 +1,220 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2016 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+drm_private void bo_del(struct fd_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void
+add_bucket(struct fd_bo_cache *cache, int size)
+{
+ unsigned int i = cache->num_buckets;
+
+ assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+ list_inithead(&cache->cache_bucket[i].list);
+ cache->cache_bucket[i].size = size;
+ cache->num_buckets++;
+}
+
+/**
+ * @coarse: if true, only power-of-two bucket sizes, otherwise
+ * fill in for a bit smoother size curve..
+ */
+drm_private void
+fd_bo_cache_init(struct fd_bo_cache *cache, int coarse)
+{
+ unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+ /* OK, so power of two buckets was too wasteful of memory.
+ * Give 3 other sizes between each power of two, to hopefully
+ * cover things accurately enough. (The alternative is
+ * probably to just go for exact matching of sizes, and assume
+ * that for things like composited window resize the tiled
+ * width/height alignment and rounding of sizes to pages will
+ * get us useful cache hit rates anyway)
+ */
+ add_bucket(cache, 4096);
+ add_bucket(cache, 4096 * 2);
+ if (!coarse)
+ add_bucket(cache, 4096 * 3);
+
+ /* Initialize the linked lists for BO reuse cache. */
+ for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+ add_bucket(cache, size);
+ if (!coarse) {
+ add_bucket(cache, size + size * 1 / 4);
+ add_bucket(cache, size + size * 2 / 4);
+ add_bucket(cache, size + size * 3 / 4);
+ }
+ }
+}
+
+/* Frees older cached buffers. Called under table_lock */
+drm_private void
+fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time)
+{
+ int i;
+
+ if (cache->time == time)
+ return;
+
+ for (i = 0; i < cache->num_buckets; i++) {
+ struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
+ struct fd_bo *bo;
+
+ while (!LIST_IS_EMPTY(&bucket->list)) {
+ bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
+
+ /* keep things in cache for at least 1 second: */
+ if (time && ((time - bo->free_time) <= 1))
+ break;
+
+ VG_BO_OBTAIN(bo);
+ list_del(&bo->list);
+ bo_del(bo);
+ }
+ }
+
+ cache->time = time;
+}
+
+static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size)
+{
+ int i;
+
+ /* hmm, this is what intel does, but I suppose we could calculate our
+ * way to the correct bucket size rather than looping..
+ */
+ for (i = 0; i < cache->num_buckets; i++) {
+ struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
+ if (bucket->size >= size) {
+ return bucket;
+ }
+ }
+
+ return NULL;
+}
+
+static int is_idle(struct fd_bo *bo)
+{
+ return fd_bo_cpu_prep(bo, NULL,
+ DRM_FREEDRENO_PREP_READ |
+ DRM_FREEDRENO_PREP_WRITE |
+ DRM_FREEDRENO_PREP_NOSYNC) == 0;
+}
+
+static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags)
+{
+ struct fd_bo *bo = NULL;
+
+ /* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could
+ * skip the busy check.. if it is only going to be a render target
+ * then we probably don't need to stall..
+ *
+ * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail
+ * (MRU, since likely to be in GPU cache), rather than head (LRU)..
+ */
+ pthread_mutex_lock(&table_lock);
+ if (!LIST_IS_EMPTY(&bucket->list)) {
+ bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
+ /* TODO check for compatible flags? */
+ if (is_idle(bo)) {
+ list_del(&bo->list);
+ } else {
+ bo = NULL;
+ }
+ }
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+/* NOTE: size is potentially rounded up to bucket size: */
+drm_private struct fd_bo *
+fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags)
+{
+ struct fd_bo *bo = NULL;
+ struct fd_bo_bucket *bucket;
+
+ *size = ALIGN(*size, 4096);
+ bucket = get_bucket(cache, *size);
+
+ /* see if we can be green and recycle: */
+retry:
+ if (bucket) {
+ *size = bucket->size;
+ bo = find_in_bucket(bucket, flags);
+ if (bo) {
+ VG_BO_OBTAIN(bo);
+ if (bo->funcs->madvise(bo, TRUE) <= 0) {
+ /* we've lost the backing pages, delete and try again: */
+ pthread_mutex_lock(&table_lock);
+ bo_del(bo);
+ pthread_mutex_unlock(&table_lock);
+ goto retry;
+ }
+ atomic_set(&bo->refcnt, 1);
+ fd_device_ref(bo->dev);
+ return bo;
+ }
+ }
+
+ return NULL;
+}
+
+drm_private int
+fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo)
+{
+ struct fd_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+ /* see if we can be green and recycle: */
+ if (bucket) {
+ struct timespec time;
+
+ bo->funcs->madvise(bo, FALSE);
+
+ clock_gettime(CLOCK_MONOTONIC, &time);
+
+ bo->free_time = time.tv_sec;
+ VG_BO_RELEASE(bo);
+ list_addtail(&bo->list, &bucket->list);
+ fd_bo_cache_cleanup(cache, time.tv_sec);
+
+ /* bo's in the bucket cache don't have a ref and
+ * don't hold a ref to the dev:
+ */
+ fd_device_del_locked(bo->dev);
+
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
new file mode 100644
index 0000000..ac23430
--- /dev/null
+++ b/freedreno/freedreno_device.c
@@ -0,0 +1,145 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct fd_device * kgsl_device_new(int fd);
+struct fd_device * msm_device_new(int fd);
+
+drm_public struct fd_device * fd_device_new(int fd)
+{
+ struct fd_device *dev;
+ drmVersionPtr version;
+
+ /* figure out if we are kgsl or msm drm driver: */
+ version = drmGetVersion(fd);
+ if (!version) {
+ ERROR_MSG("cannot get version: %s", strerror(errno));
+ return NULL;
+ }
+
+ if (!strcmp(version->name, "msm")) {
+ DEBUG_MSG("msm DRM device");
+ if (version->version_major != 1) {
+ ERROR_MSG("unsupported version: %u.%u.%u", version->version_major,
+ version->version_minor, version->version_patchlevel);
+ dev = NULL;
+ goto out;
+ }
+
+ dev = msm_device_new(fd);
+ dev->version = version->version_minor;
+#if HAVE_FREEDRENO_KGSL
+ } else if (!strcmp(version->name, "kgsl")) {
+ DEBUG_MSG("kgsl DRM device");
+ dev = kgsl_device_new(fd);
+#endif
+ } else {
+ ERROR_MSG("unknown device: %s", version->name);
+ dev = NULL;
+ }
+
+out:
+ drmFreeVersion(version);
+
+ if (!dev)
+ return NULL;
+
+ atomic_set(&dev->refcnt, 1);
+ dev->fd = fd;
+ dev->handle_table = drmHashCreate();
+ dev->name_table = drmHashCreate();
+ fd_bo_cache_init(&dev->bo_cache, FALSE);
+ fd_bo_cache_init(&dev->ring_cache, TRUE);
+
+ return dev;
+}
+
+/* like fd_device_new() but creates it's own private dup() of the fd
+ * which is close()d when the device is finalized.
+ */
+drm_public struct fd_device * fd_device_new_dup(int fd)
+{
+ int dup_fd = dup(fd);
+ struct fd_device *dev = fd_device_new(dup_fd);
+ if (dev)
+ dev->closefd = 1;
+ else
+ close(dup_fd);
+ return dev;
+}
+
+drm_public struct fd_device * fd_device_ref(struct fd_device *dev)
+{
+ atomic_inc(&dev->refcnt);
+ return dev;
+}
+
+static void fd_device_del_impl(struct fd_device *dev)
+{
+ int close_fd = dev->closefd ? dev->fd : -1;
+ fd_bo_cache_cleanup(&dev->bo_cache, 0);
+ drmHashDestroy(dev->handle_table);
+ drmHashDestroy(dev->name_table);
+ dev->funcs->destroy(dev);
+ if (close_fd >= 0)
+ close(close_fd);
+}
+
+drm_private void fd_device_del_locked(struct fd_device *dev)
+{
+ if (!atomic_dec_and_test(&dev->refcnt))
+ return;
+ fd_device_del_impl(dev);
+}
+
+drm_public void fd_device_del(struct fd_device *dev)
+{
+ if (!atomic_dec_and_test(&dev->refcnt))
+ return;
+ pthread_mutex_lock(&table_lock);
+ fd_device_del_impl(dev);
+ pthread_mutex_unlock(&table_lock);
+}
+
+drm_public int fd_device_fd(struct fd_device *dev)
+{
+ return dev->fd;
+}
+
+drm_public enum fd_version fd_device_version(struct fd_device *dev)
+{
+ return dev->version;
+}
diff --git a/freedreno/freedreno_drmif.h b/freedreno/freedreno_drmif.h
new file mode 100644
index 0000000..c95c21b
--- /dev/null
+++ b/freedreno/freedreno_drmif.h
@@ -0,0 +1,140 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef FREEDRENO_DRMIF_H_
+#define FREEDRENO_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+
+#if defined(__GNUC__)
+# define drm_deprecated __attribute__((__deprecated__))
+#else
+# define drm_deprecated
+#endif
+
+/* an empty marker for things that will be deprecated in the future: */
+#define will_be_deprecated
+
+struct fd_bo;
+struct fd_pipe;
+struct fd_device;
+
+enum fd_pipe_id {
+ FD_PIPE_3D = 1,
+ FD_PIPE_2D = 2,
+ /* some devices have two 2d blocks.. not really sure how to
+ * use that yet, so just ignoring the 2nd 2d pipe for now
+ */
+ FD_PIPE_MAX
+};
+
+enum fd_param_id {
+ FD_DEVICE_ID,
+ FD_GMEM_SIZE,
+ FD_GPU_ID,
+ FD_CHIP_ID,
+ FD_MAX_FREQ,
+ FD_TIMESTAMP,
+ FD_NR_RINGS, /* # of rings == # of distinct priority levels */
+};
+
+/* bo flags: */
+#define DRM_FREEDRENO_GEM_TYPE_SMI 0x00000001
+#define DRM_FREEDRENO_GEM_TYPE_KMEM 0x00000002
+#define DRM_FREEDRENO_GEM_TYPE_MEM_MASK 0x0000000f
+#define DRM_FREEDRENO_GEM_CACHE_NONE 0x00000000
+#define DRM_FREEDRENO_GEM_CACHE_WCOMBINE 0x00100000
+#define DRM_FREEDRENO_GEM_CACHE_WTHROUGH 0x00200000
+#define DRM_FREEDRENO_GEM_CACHE_WBACK 0x00400000
+#define DRM_FREEDRENO_GEM_CACHE_WBACKWA 0x00800000
+#define DRM_FREEDRENO_GEM_CACHE_MASK 0x00f00000
+#define DRM_FREEDRENO_GEM_GPUREADONLY 0x01000000
+
+/* bo access flags: (keep aligned to MSM_PREP_x) */
+#define DRM_FREEDRENO_PREP_READ 0x01
+#define DRM_FREEDRENO_PREP_WRITE 0x02
+#define DRM_FREEDRENO_PREP_NOSYNC 0x04
+
+/* device functions:
+ */
+
+struct fd_device * fd_device_new(int fd);
+struct fd_device * fd_device_new_dup(int fd);
+struct fd_device * fd_device_ref(struct fd_device *dev);
+void fd_device_del(struct fd_device *dev);
+int fd_device_fd(struct fd_device *dev);
+
+enum fd_version {
+ FD_VERSION_MADVISE = 1, /* kernel supports madvise */
+ FD_VERSION_UNLIMITED_CMDS = 1, /* submits w/ >4 cmd buffers (growable ringbuffer) */
+ FD_VERSION_FENCE_FD = 2, /* submit command supports in/out fences */
+ FD_VERSION_SUBMIT_QUEUES = 3, /* submit queues and multiple priority levels */
+ FD_VERSION_BO_IOVA = 3, /* supports fd_bo_get/put_iova() */
+};
+enum fd_version fd_device_version(struct fd_device *dev);
+
+/* pipe functions:
+ */
+
+struct fd_pipe * fd_pipe_new(struct fd_device *dev, enum fd_pipe_id id);
+struct fd_pipe * fd_pipe_new2(struct fd_device *dev, enum fd_pipe_id id, uint32_t prio);
+struct fd_pipe * fd_pipe_ref(struct fd_pipe *pipe);
+void fd_pipe_del(struct fd_pipe *pipe);
+int fd_pipe_get_param(struct fd_pipe *pipe, enum fd_param_id param,
+ uint64_t *value);
+int fd_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp);
+/* timeout in nanosec */
+int fd_pipe_wait_timeout(struct fd_pipe *pipe, uint32_t timestamp,
+ uint64_t timeout);
+
+
+/* buffer-object functions:
+ */
+
+struct fd_bo * fd_bo_new(struct fd_device *dev,
+ uint32_t size, uint32_t flags);
+struct fd_bo * fd_bo_from_fbdev(struct fd_pipe *pipe,
+ int fbfd, uint32_t size);
+struct fd_bo *fd_bo_from_handle(struct fd_device *dev,
+ uint32_t handle, uint32_t size);
+struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name);
+struct fd_bo * fd_bo_from_dmabuf(struct fd_device *dev, int fd);
+uint64_t fd_bo_get_iova(struct fd_bo *bo);
+void fd_bo_put_iova(struct fd_bo *bo);
+struct fd_bo * fd_bo_ref(struct fd_bo *bo);
+void fd_bo_del(struct fd_bo *bo);
+int fd_bo_get_name(struct fd_bo *bo, uint32_t *name);
+uint32_t fd_bo_handle(struct fd_bo *bo);
+int fd_bo_dmabuf(struct fd_bo *bo);
+uint32_t fd_bo_size(struct fd_bo *bo);
+void * fd_bo_map(struct fd_bo *bo);
+int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op);
+void fd_bo_cpu_fini(struct fd_bo *bo);
+
+#endif /* FREEDRENO_DRMIF_H_ */
diff --git a/freedreno/freedreno_pipe.c b/freedreno/freedreno_pipe.c
new file mode 100644
index 0000000..e82e644
--- /dev/null
+++ b/freedreno/freedreno_pipe.c
@@ -0,0 +1,102 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+/**
+ * priority of zero is highest priority, and higher numeric values are
+ * lower priorities
+ */
+drm_public struct fd_pipe *
+fd_pipe_new2(struct fd_device *dev, enum fd_pipe_id id, uint32_t prio)
+{
+ struct fd_pipe *pipe;
+ uint64_t val;
+
+ if (id > FD_PIPE_MAX) {
+ ERROR_MSG("invalid pipe id: %d", id);
+ return NULL;
+ }
+
+ if ((prio != 1) && (fd_device_version(dev) < FD_VERSION_SUBMIT_QUEUES)) {
+ ERROR_MSG("invalid priority!");
+ return NULL;
+ }
+
+ pipe = dev->funcs->pipe_new(dev, id, prio);
+ if (!pipe) {
+ ERROR_MSG("allocation failed");
+ return NULL;
+ }
+
+ pipe->dev = dev;
+ pipe->id = id;
+ atomic_set(&pipe->refcnt, 1);
+
+ fd_pipe_get_param(pipe, FD_GPU_ID, &val);
+ pipe->gpu_id = val;
+
+ return pipe;
+}
+
+drm_public struct fd_pipe *
+fd_pipe_new(struct fd_device *dev, enum fd_pipe_id id)
+{
+ return fd_pipe_new2(dev, id, 1);
+}
+
+drm_public struct fd_pipe * fd_pipe_ref(struct fd_pipe *pipe)
+{
+ atomic_inc(&pipe->refcnt);
+ return pipe;
+}
+
+drm_public void fd_pipe_del(struct fd_pipe *pipe)
+{
+ if (!atomic_dec_and_test(&pipe->refcnt))
+ return;
+ pipe->funcs->destroy(pipe);
+}
+
+drm_public int fd_pipe_get_param(struct fd_pipe *pipe,
+ enum fd_param_id param, uint64_t *value)
+{
+ return pipe->funcs->get_param(pipe, param, value);
+}
+
+drm_public int fd_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp)
+{
+ return fd_pipe_wait_timeout(pipe, timestamp, ~0);
+}
+
+drm_public int fd_pipe_wait_timeout(struct fd_pipe *pipe, uint32_t timestamp,
+ uint64_t timeout)
+{
+ return pipe->funcs->wait(pipe, timestamp, timeout);
+}
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
new file mode 100644
index 0000000..b8eac4b
--- /dev/null
+++ b/freedreno/freedreno_priv.h
@@ -0,0 +1,258 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef FREEDRENO_PRIV_H_
+#define FREEDRENO_PRIV_H_
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "xf86atomic.h"
+
+#include "util_double_list.h"
+#include "util_math.h"
+
+#include "freedreno_drmif.h"
+#include "freedreno_ringbuffer.h"
+#include "drm.h"
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+struct fd_device_funcs {
+ int (*bo_new_handle)(struct fd_device *dev, uint32_t size,
+ uint32_t flags, uint32_t *handle);
+ struct fd_bo * (*bo_from_handle)(struct fd_device *dev,
+ uint32_t size, uint32_t handle);
+ struct fd_pipe * (*pipe_new)(struct fd_device *dev, enum fd_pipe_id id,
+ unsigned prio);
+ void (*destroy)(struct fd_device *dev);
+};
+
+struct fd_bo_bucket {
+ uint32_t size;
+ struct list_head list;
+};
+
+struct fd_bo_cache {
+ struct fd_bo_bucket cache_bucket[14 * 4];
+ int num_buckets;
+ time_t time;
+};
+
+struct fd_device {
+ int fd;
+ enum fd_version version;
+ atomic_t refcnt;
+
+ /* tables to keep track of bo's, to avoid "evil-twin" fd_bo objects:
+ *
+ * handle_table: maps handle to fd_bo
+ * name_table: maps flink name to fd_bo
+ *
+ * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always
+ * returns a new handle. So we need to figure out if the bo is already
+ * open in the process first, before calling gem-open.
+ */
+ void *handle_table, *name_table;
+
+ const struct fd_device_funcs *funcs;
+
+ struct fd_bo_cache bo_cache;
+ struct fd_bo_cache ring_cache;
+
+ int closefd; /* call close(fd) upon destruction */
+
+ /* just for valgrind: */
+ int bo_size;
+};
+
+drm_private void fd_bo_cache_init(struct fd_bo_cache *cache, int coarse);
+drm_private void fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time);
+drm_private struct fd_bo * fd_bo_cache_alloc(struct fd_bo_cache *cache,
+ uint32_t *size, uint32_t flags);
+drm_private int fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo);
+
+/* for where @table_lock is already held: */
+drm_private void fd_device_del_locked(struct fd_device *dev);
+
+struct fd_pipe_funcs {
+ struct fd_ringbuffer * (*ringbuffer_new)(struct fd_pipe *pipe, uint32_t size,
+ enum fd_ringbuffer_flags flags);
+ int (*get_param)(struct fd_pipe *pipe, enum fd_param_id param, uint64_t *value);
+ int (*wait)(struct fd_pipe *pipe, uint32_t timestamp, uint64_t timeout);
+ void (*destroy)(struct fd_pipe *pipe);
+};
+
+struct fd_pipe {
+ struct fd_device *dev;
+ enum fd_pipe_id id;
+ uint32_t gpu_id;
+ atomic_t refcnt;
+ const struct fd_pipe_funcs *funcs;
+};
+
+struct fd_ringbuffer_funcs {
+ void * (*hostptr)(struct fd_ringbuffer *ring);
+ int (*flush)(struct fd_ringbuffer *ring, uint32_t *last_start,
+ int in_fence_fd, int *out_fence_fd);
+ void (*grow)(struct fd_ringbuffer *ring, uint32_t size);
+ void (*reset)(struct fd_ringbuffer *ring);
+ void (*emit_reloc)(struct fd_ringbuffer *ring,
+ const struct fd_reloc *reloc);
+ uint32_t (*emit_reloc_ring)(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *target, uint32_t cmd_idx);
+ uint32_t (*cmd_count)(struct fd_ringbuffer *ring);
+ void (*destroy)(struct fd_ringbuffer *ring);
+};
+
+struct fd_bo_funcs {
+ int (*offset)(struct fd_bo *bo, uint64_t *offset);
+ int (*cpu_prep)(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op);
+ void (*cpu_fini)(struct fd_bo *bo);
+ int (*madvise)(struct fd_bo *bo, int willneed);
+ uint64_t (*iova)(struct fd_bo *bo);
+ void (*destroy)(struct fd_bo *bo);
+};
+
+struct fd_bo {
+ struct fd_device *dev;
+ uint32_t size;
+ uint32_t handle;
+ uint32_t name;
+ void *map;
+ atomic_t refcnt;
+ const struct fd_bo_funcs *funcs;
+
+ enum {
+ NO_CACHE = 0,
+ BO_CACHE = 1,
+ RING_CACHE = 2,
+ } bo_reuse;
+
+ struct list_head list; /* bucket-list entry */
+ time_t free_time; /* time when added to bucket-list */
+};
+
+drm_private struct fd_bo *fd_bo_new_ring(struct fd_device *dev,
+ uint32_t size, uint32_t flags);
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define enable_debug 0 /* TODO make dynamic */
+
+#define INFO_MSG(fmt, ...) \
+ do { drmMsg("[I] "fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define DEBUG_MSG(fmt, ...) \
+ do if (enable_debug) { drmMsg("[D] "fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define WARN_MSG(fmt, ...) \
+ do { drmMsg("[W] "fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define ERROR_MSG(fmt, ...) \
+ do { drmMsg("[E] " fmt " (%s:%d)\n", \
+ ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+
+#define U642VOID(x) ((void *)(unsigned long)(x))
+#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
+
+static inline uint32_t
+offset_bytes(void *end, void *start)
+{
+ return ((char *)end) - ((char *)start);
+}
+
+#if HAVE_VALGRIND
+# include <memcheck.h>
+
+/*
+ * For tracking the backing memory (if valgrind enabled, we force a mmap
+ * for the purposes of tracking)
+ */
+static inline void VG_BO_ALLOC(struct fd_bo *bo)
+{
+ if (bo && RUNNING_ON_VALGRIND) {
+ VALGRIND_MALLOCLIKE_BLOCK(fd_bo_map(bo), bo->size, 0, 1);
+ }
+}
+
+static inline void VG_BO_FREE(struct fd_bo *bo)
+{
+ VALGRIND_FREELIKE_BLOCK(bo->map, 0);
+}
+
+/*
+ * For tracking bo structs that are in the buffer-cache, so that valgrind
+ * doesn't attribute ownership to the first one to allocate the recycled
+ * bo.
+ *
+ * Note that the list_head in fd_bo is used to track the buffers in cache
+ * so disable error reporting on the range while they are in cache so
+ * valgrind doesn't squawk about list traversal.
+ *
+ */
+static inline void VG_BO_RELEASE(struct fd_bo *bo)
+{
+ if (RUNNING_ON_VALGRIND) {
+ VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, bo->dev->bo_size);
+ VALGRIND_MAKE_MEM_NOACCESS(bo, bo->dev->bo_size);
+ VALGRIND_FREELIKE_BLOCK(bo->map, 0);
+ }
+}
+static inline void VG_BO_OBTAIN(struct fd_bo *bo)
+{
+ if (RUNNING_ON_VALGRIND) {
+ VALGRIND_MAKE_MEM_DEFINED(bo, bo->dev->bo_size);
+ VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, bo->dev->bo_size);
+ VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, 1);
+ }
+}
+#else
+static inline void VG_BO_ALLOC(struct fd_bo *bo) {}
+static inline void VG_BO_FREE(struct fd_bo *bo) {}
+static inline void VG_BO_RELEASE(struct fd_bo *bo) {}
+static inline void VG_BO_OBTAIN(struct fd_bo *bo) {}
+#endif
+
+
+#endif /* FREEDRENO_PRIV_H_ */
diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
new file mode 100644
index 0000000..8f0093a
--- /dev/null
+++ b/freedreno/freedreno_ringbuffer.c
@@ -0,0 +1,182 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include <assert.h>
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+#include "freedreno_ringbuffer.h"
+
+drm_public struct fd_ringbuffer *
+fd_ringbuffer_new_flags(struct fd_pipe *pipe, uint32_t size,
+ enum fd_ringbuffer_flags flags)
+{
+ struct fd_ringbuffer *ring;
+
+ /* we can't really support "growable" rb's in general for
+ * stateobj's since we need a single gpu addr (ie. can't
+ * do the trick of a chain of IB packets):
+ */
+ if (flags & FD_RINGBUFFER_OBJECT)
+ assert(size);
+
+ ring = pipe->funcs->ringbuffer_new(pipe, size, flags);
+ if (!ring)
+ return NULL;
+
+ ring->flags = flags;
+ ring->pipe = pipe;
+ ring->start = ring->funcs->hostptr(ring);
+ ring->end = &(ring->start[ring->size/4]);
+
+ ring->cur = ring->last_start = ring->start;
+
+ return ring;
+}
+
+drm_public struct fd_ringbuffer *
+fd_ringbuffer_new(struct fd_pipe *pipe, uint32_t size)
+{
+ return fd_ringbuffer_new_flags(pipe, size, 0);
+}
+
+drm_public struct fd_ringbuffer *
+fd_ringbuffer_new_object(struct fd_pipe *pipe, uint32_t size)
+{
+ return fd_ringbuffer_new_flags(pipe, size, FD_RINGBUFFER_OBJECT);
+}
+
+drm_public void fd_ringbuffer_del(struct fd_ringbuffer *ring)
+{
+ if (!atomic_dec_and_test(&ring->refcnt))
+ return;
+
+ fd_ringbuffer_reset(ring);
+ ring->funcs->destroy(ring);
+}
+
+drm_public struct fd_ringbuffer *
+fd_ringbuffer_ref(struct fd_ringbuffer *ring)
+{
+ STATIC_ASSERT(sizeof(ring->refcnt) <= sizeof(ring->__pad));
+ atomic_inc(&ring->refcnt);
+ return ring;
+}
+
+/* ringbuffers which are IB targets should set the toplevel rb (ie.
+ * the IB source) as it's parent before emitting reloc's, to ensure
+ * the bookkeeping works out properly.
+ */
+drm_public void fd_ringbuffer_set_parent(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *parent)
+{
+ /* state objects should not be parented! */
+ assert(!(ring->flags & FD_RINGBUFFER_OBJECT));
+ ring->parent = parent;
+}
+
+drm_public void fd_ringbuffer_reset(struct fd_ringbuffer *ring)
+{
+ uint32_t *start = ring->start;
+ if (ring->pipe->id == FD_PIPE_2D)
+ start = &ring->start[0x140];
+ ring->cur = ring->last_start = start;
+ if (ring->funcs->reset)
+ ring->funcs->reset(ring);
+}
+
+drm_public int fd_ringbuffer_flush(struct fd_ringbuffer *ring)
+{
+ return ring->funcs->flush(ring, ring->last_start, -1, NULL);
+}
+
+drm_public int fd_ringbuffer_flush2(struct fd_ringbuffer *ring, int in_fence_fd,
+ int *out_fence_fd)
+{
+ return ring->funcs->flush(ring, ring->last_start, in_fence_fd, out_fence_fd);
+}
+
+drm_public void fd_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t ndwords)
+{
+ assert(ring->funcs->grow); /* unsupported on kgsl */
+
+ /* there is an upper bound on IB size, which appears to be 0x100000 */
+ if (ring->size < 0x100000)
+ ring->size *= 2;
+
+ ring->funcs->grow(ring, ring->size);
+
+ ring->start = ring->funcs->hostptr(ring);
+ ring->end = &(ring->start[ring->size/4]);
+
+ ring->cur = ring->last_start = ring->start;
+}
+
+drm_public uint32_t fd_ringbuffer_timestamp(struct fd_ringbuffer *ring)
+{
+ return ring->last_timestamp;
+}
+
+drm_public void fd_ringbuffer_reloc(struct fd_ringbuffer *ring,
+ const struct fd_reloc *reloc)
+{
+ assert(ring->pipe->gpu_id < 500);
+ ring->funcs->emit_reloc(ring, reloc);
+}
+
+drm_public void fd_ringbuffer_reloc2(struct fd_ringbuffer *ring,
+ const struct fd_reloc *reloc)
+{
+ ring->funcs->emit_reloc(ring, reloc);
+}
+
+drm_public uint32_t fd_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
+{
+ if (!ring->funcs->cmd_count)
+ return 1;
+ return ring->funcs->cmd_count(ring);
+}
+
+drm_public uint32_t
+fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *target, uint32_t cmd_idx)
+{
+ return ring->funcs->emit_reloc_ring(ring, target, cmd_idx);
+}
+
+drm_public uint32_t
+fd_ringbuffer_size(struct fd_ringbuffer *ring)
+{
+ /* only really needed for stateobj ringbuffers, and won't really
+ * do what you expect for growable rb's.. so lets just restrict
+ * this to stateobj's for now:
+ */
+ assert(ring->flags & FD_RINGBUFFER_OBJECT);
+ return offset_bytes(ring->cur, ring->start);
+}
+
diff --git a/freedreno/freedreno_ringbuffer.h b/freedreno/freedreno_ringbuffer.h
new file mode 100644
index 0000000..bc41a31
--- /dev/null
+++ b/freedreno/freedreno_ringbuffer.h
@@ -0,0 +1,142 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef FREEDRENO_RINGBUFFER_H_
+#define FREEDRENO_RINGBUFFER_H_
+
+#include <freedreno_drmif.h>
+
+/* the ringbuffer object is not opaque so that OUT_RING() type stuff
+ * can be inlined. Note that users should not make assumptions about
+ * the size of this struct.
+ */
+
+struct fd_ringbuffer_funcs;
+
+enum fd_ringbuffer_flags {
+
+ /* Ringbuffer is a "state object", which is potentially reused
+ * many times, rather than being used in one-shot mode linked
+ * to a parent ringbuffer.
+ */
+ FD_RINGBUFFER_OBJECT = 0x1,
+
+ /* Hint that the stateobj will be used for streaming state
+ * that is used once or a few times and then discarded.
+ *
+ * For sub-allocation, non streaming stateobj's should be
+ * sub-allocated from a page size buffer, so one long lived
+ * state obj doesn't prevent other pages from being freed.
+ * (Ie. it would be no worse than allocating a page sized
+ * bo for each small non-streaming stateobj).
+ *
+ * But streaming stateobj's could be sub-allocated from a
+ * larger buffer to reduce the alloc/del overhead.
+ */
+ FD_RINGBUFFER_STREAMING = 0x2,
+};
+
+struct fd_ringbuffer {
+ int size;
+ uint32_t *cur, *end, *start, *last_start;
+ struct fd_pipe *pipe;
+ const struct fd_ringbuffer_funcs *funcs;
+ uint32_t last_timestamp;
+ struct fd_ringbuffer *parent;
+
+ /* for users of fd_ringbuffer to store their own private per-
+ * ringbuffer data
+ */
+ void *user;
+
+ enum fd_ringbuffer_flags flags;
+
+ /* This is a bit gross, but we can't use atomic_t in exported
+ * headers. OTOH, we don't need the refcnt to be publicly
+ * visible. The only reason that this struct is exported is
+ * because fd_ringbuffer_emit needs to be something that can
+ * be inlined for performance reasons.
+ */
+ union {
+#ifdef HAS_ATOMIC_OPS
+ atomic_t refcnt;
+#endif
+ uint64_t __pad;
+ };
+};
+
+struct fd_ringbuffer * fd_ringbuffer_new(struct fd_pipe *pipe,
+ uint32_t size);
+will_be_deprecated
+struct fd_ringbuffer * fd_ringbuffer_new_object(struct fd_pipe *pipe,
+ uint32_t size);
+struct fd_ringbuffer * fd_ringbuffer_new_flags(struct fd_pipe *pipe,
+ uint32_t size, enum fd_ringbuffer_flags flags);
+
+struct fd_ringbuffer *fd_ringbuffer_ref(struct fd_ringbuffer *ring);
+void fd_ringbuffer_del(struct fd_ringbuffer *ring);
+void fd_ringbuffer_set_parent(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *parent);
+will_be_deprecated
+void fd_ringbuffer_reset(struct fd_ringbuffer *ring);
+int fd_ringbuffer_flush(struct fd_ringbuffer *ring);
+/* in_fence_fd: -1 for no in-fence, else fence fd
+ * out_fence_fd: NULL for no output-fence requested, else ptr to return out-fence
+ */
+int fd_ringbuffer_flush2(struct fd_ringbuffer *ring, int in_fence_fd,
+ int *out_fence_fd);
+void fd_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t ndwords);
+uint32_t fd_ringbuffer_timestamp(struct fd_ringbuffer *ring);
+
+static inline void fd_ringbuffer_emit(struct fd_ringbuffer *ring,
+ uint32_t data)
+{
+ (*ring->cur++) = data;
+}
+
+struct fd_reloc {
+ struct fd_bo *bo;
+#define FD_RELOC_READ 0x0001
+#define FD_RELOC_WRITE 0x0002
+ uint32_t flags;
+ uint32_t offset;
+ uint32_t or;
+ int32_t shift;
+ uint32_t orhi; /* used for a5xx+ */
+};
+
+/* NOTE: relocs are 2 dwords on a5xx+ */
+
+void fd_ringbuffer_reloc2(struct fd_ringbuffer *ring, const struct fd_reloc *reloc);
+will_be_deprecated void fd_ringbuffer_reloc(struct fd_ringbuffer *ring, const struct fd_reloc *reloc);
+uint32_t fd_ringbuffer_cmd_count(struct fd_ringbuffer *ring);
+uint32_t fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *target, uint32_t cmd_idx);
+uint32_t fd_ringbuffer_size(struct fd_ringbuffer *ring);
+
+#endif /* FREEDRENO_RINGBUFFER_H_ */
diff --git a/freedreno/kgsl/README b/freedreno/kgsl/README
new file mode 100644
index 0000000..c46ba08
--- /dev/null
+++ b/freedreno/kgsl/README
@@ -0,0 +1,26 @@
+This is a historical description of what is now the kgsl backend
+in libdrm freedreno (before the upstream drm/msm driver). Note
+that the kgsl backend requires the "kgsl-drm" shim driver, which
+usually is in disrepair (QCOM does not build it for android), and
+due to random differences between different downstream android
+kernel branches it may or may not work. So YMMV.
+
+Original README:
+----------------
+
+Note that current msm kernel driver is a bit strange. It provides a
+DRM interface for GEM, which is basically sufficient to have DRI2
+working. But it does not provide KMS. And interface to 2d and 3d
+cores is via different other devices (/dev/kgsl-*). This is not
+quite how I'd write a DRM driver, but at this stage it is useful for
+xf86-video-freedreno and fdre (and eventual gallium driver) to be
+able to work on existing kernel driver from QCOM, to allow to
+capture cmdstream dumps from the binary blob drivers without having
+to reboot. So libdrm_freedreno attempts to hide most of the crazy.
+The intention is that when there is a proper kernel driver, it will
+be mostly just changes in libdrm_freedreno to adapt the gallium
+driver and xf86-video-freedreno (ignoring the fbdev->KMS changes).
+
+So don't look at freedreno as an example of how to write a libdrm
+module or a DRM driver.. it is just an attempt to paper over a non-
+standard kernel driver architecture.
diff --git a/freedreno/kgsl/kgsl_bo.c b/freedreno/kgsl/kgsl_bo.c
new file mode 100644
index 0000000..7a6af2f
--- /dev/null
+++ b/freedreno/kgsl/kgsl_bo.c
@@ -0,0 +1,311 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "kgsl_priv.h"
+
+#include <linux/fb.h>
+
+static int set_memtype(struct fd_device *dev, uint32_t handle, uint32_t flags)
+{
+ struct drm_kgsl_gem_memtype req = {
+ .handle = handle,
+ .type = flags & DRM_FREEDRENO_GEM_TYPE_MEM_MASK,
+ };
+
+ return drmCommandWrite(dev->fd, DRM_KGSL_GEM_SETMEMTYPE,
+ &req, sizeof(req));
+}
+
+static int bo_alloc(struct kgsl_bo *kgsl_bo)
+{
+ struct fd_bo *bo = &kgsl_bo->base;
+ if (!kgsl_bo->offset) {
+ struct drm_kgsl_gem_alloc req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ /* if the buffer is already backed by pages then this
+ * doesn't actually do anything (other than giving us
+ * the offset)
+ */
+ ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_ALLOC,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("alloc failed: %s", strerror(errno));
+ return ret;
+ }
+
+ kgsl_bo->offset = req.offset;
+ }
+
+ return 0;
+}
+
+static int kgsl_bo_offset(struct fd_bo *bo, uint64_t *offset)
+{
+ struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
+ int ret = bo_alloc(kgsl_bo);
+ if (ret)
+ return ret;
+ *offset = kgsl_bo->offset;
+ return 0;
+}
+
+static int kgsl_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
+{
+ uint32_t timestamp = kgsl_bo_get_timestamp(to_kgsl_bo(bo));
+
+ if (op & DRM_FREEDRENO_PREP_NOSYNC) {
+ uint32_t current;
+ int ret;
+
+ /* special case for is_idle().. we can't really handle that
+ * properly in kgsl (perhaps we need a way to just disable
+ * the bo-cache for kgsl?)
+ */
+ if (!pipe)
+ return -EBUSY;
+
+ ret = kgsl_pipe_timestamp(to_kgsl_pipe(pipe), &current);
+ if (ret)
+ return ret;
+
+ if (timestamp > current)
+ return -EBUSY;
+
+ return 0;
+ }
+
+ if (timestamp)
+ fd_pipe_wait(pipe, timestamp);
+
+ return 0;
+}
+
+static void kgsl_bo_cpu_fini(struct fd_bo *bo)
+{
+}
+
+static int kgsl_bo_madvise(struct fd_bo *bo, int willneed)
+{
+ return willneed; /* not supported by kgsl */
+}
+
+static void kgsl_bo_destroy(struct fd_bo *bo)
+{
+ struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
+ free(kgsl_bo);
+
+}
+
+static const struct fd_bo_funcs funcs = {
+ .offset = kgsl_bo_offset,
+ .cpu_prep = kgsl_bo_cpu_prep,
+ .cpu_fini = kgsl_bo_cpu_fini,
+ .madvise = kgsl_bo_madvise,
+ .destroy = kgsl_bo_destroy,
+};
+
+/* allocate a buffer handle: */
+drm_private int kgsl_bo_new_handle(struct fd_device *dev,
+ uint32_t size, uint32_t flags, uint32_t *handle)
+{
+ struct drm_kgsl_gem_create req = {
+ .size = size,
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(dev->fd, DRM_KGSL_GEM_CREATE,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ // TODO make flags match msm driver, since kgsl is legacy..
+ // translate flags in kgsl..
+
+ set_memtype(dev, req.handle, flags);
+
+ *handle = req.handle;
+
+ return 0;
+}
+
+/* allocate a new buffer object */
+drm_private struct fd_bo * kgsl_bo_from_handle(struct fd_device *dev,
+ uint32_t size, uint32_t handle)
+{
+ struct kgsl_bo *kgsl_bo;
+ struct fd_bo *bo;
+ unsigned i;
+
+ kgsl_bo = calloc(1, sizeof(*kgsl_bo));
+ if (!kgsl_bo)
+ return NULL;
+
+ bo = &kgsl_bo->base;
+ bo->funcs = &funcs;
+
+ for (i = 0; i < ARRAY_SIZE(kgsl_bo->list); i++)
+ list_inithead(&kgsl_bo->list[i]);
+
+ return bo;
+}
+
+drm_public struct fd_bo *
+fd_bo_from_fbdev(struct fd_pipe *pipe, int fbfd, uint32_t size)
+{
+ struct fd_bo *bo;
+
+ if (!is_kgsl_pipe(pipe))
+ return NULL;
+
+ bo = fd_bo_new(pipe->dev, 1, 0);
+
+ /* this is fugly, but works around a bug in the kernel..
+ * priv->memdesc.size never gets set, so getbufinfo ioctl
+ * thinks the buffer hasn't be allocate and fails
+ */
+ if (bo) {
+ void *fbmem = drm_mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fbfd, 0);
+ struct kgsl_map_user_mem req = {
+ .memtype = KGSL_USER_MEM_TYPE_ADDR,
+ .len = size,
+ .offset = 0,
+ .hostptr = (unsigned long)fbmem,
+ };
+ struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
+ int ret;
+
+ ret = ioctl(to_kgsl_pipe(pipe)->fd, IOCTL_KGSL_MAP_USER_MEM, &req);
+ if (ret) {
+ ERROR_MSG("mapping user mem failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+ kgsl_bo->gpuaddr = req.gpuaddr;
+ bo->map = fbmem;
+ }
+
+ return bo;
+fail:
+ if (bo)
+ fd_bo_del(bo);
+ return NULL;
+}
+
+drm_private uint32_t kgsl_bo_gpuaddr(struct kgsl_bo *kgsl_bo, uint32_t offset)
+{
+ struct fd_bo *bo = &kgsl_bo->base;
+ if (!kgsl_bo->gpuaddr) {
+ struct drm_kgsl_gem_bufinfo req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ ret = bo_alloc(kgsl_bo);
+ if (ret) {
+ return ret;
+ }
+
+ ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("get bufinfo failed: %s", strerror(errno));
+ return 0;
+ }
+
+ kgsl_bo->gpuaddr = req.gpuaddr[0];
+ }
+ return kgsl_bo->gpuaddr + offset;
+}
+
+/*
+ * Super-cheezy way to synchronization between mesa and ddx.. the
+ * SET_ACTIVE ioctl gives us a way to stash a 32b # w/ a GEM bo, and
+ * GET_BUFINFO gives us a way to retrieve it. We use this to stash
+ * the timestamp of the last ISSUEIBCMDS on the buffer.
+ *
+ * To avoid an obscene amount of syscalls, we:
+ * 1) Only set the timestamp for buffers w/ an flink name, ie.
+ * only buffers shared across processes. This is enough to
+ * catch the DRI2 buffers.
+ * 2) Only set the timestamp for buffers submitted to the 3d ring
+ * and only check the timestamps on buffers submitted to the
+ * 2d ring. This should be enough to handle synchronizing of
+ * presentation blit. We could do synchronization in the other
+ * direction too, but that would be problematic if we are using
+ * the 3d ring from DDX, since client side wouldn't know this.
+ *
+ * The waiting on timestamp happens before flush, and setting of
+ * timestamp happens after flush. It is transparent to the user
+ * of libdrm_freedreno as all the tracking of buffers happens via
+ * _emit_reloc()..
+ */
+
+drm_private void kgsl_bo_set_timestamp(struct kgsl_bo *kgsl_bo,
+ uint32_t timestamp)
+{
+ struct fd_bo *bo = &kgsl_bo->base;
+ if (bo->name) {
+ struct drm_kgsl_gem_active req = {
+ .handle = bo->handle,
+ .active = timestamp,
+ };
+ int ret;
+
+ ret = drmCommandWrite(bo->dev->fd, DRM_KGSL_GEM_SET_ACTIVE,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("set active failed: %s", strerror(errno));
+ }
+ }
+}
+
+drm_private uint32_t kgsl_bo_get_timestamp(struct kgsl_bo *kgsl_bo)
+{
+ struct fd_bo *bo = &kgsl_bo->base;
+ uint32_t timestamp = 0;
+ if (bo->name) {
+ struct drm_kgsl_gem_bufinfo req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("get bufinfo failed: %s", strerror(errno));
+ return 0;
+ }
+
+ timestamp = req.active;
+ }
+ return timestamp;
+}
diff --git a/freedreno/kgsl/kgsl_device.c b/freedreno/kgsl/kgsl_device.c
new file mode 100644
index 0000000..914f341
--- /dev/null
+++ b/freedreno/kgsl/kgsl_device.c
@@ -0,0 +1,63 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "kgsl_priv.h"
+
+static void kgsl_device_destroy(struct fd_device *dev)
+{
+ struct kgsl_device *kgsl_dev = to_kgsl_device(dev);
+ free(kgsl_dev);
+}
+
+static const struct fd_device_funcs funcs = {
+ .bo_new_handle = kgsl_bo_new_handle,
+ .bo_from_handle = kgsl_bo_from_handle,
+ .pipe_new = kgsl_pipe_new,
+ .destroy = kgsl_device_destroy,
+};
+
+drm_private struct fd_device * kgsl_device_new(int fd)
+{
+ struct kgsl_device *kgsl_dev;
+ struct fd_device *dev;
+
+ kgsl_dev = calloc(1, sizeof(*kgsl_dev));
+ if (!kgsl_dev)
+ return NULL;
+
+ dev = &kgsl_dev->base;
+ dev->funcs = &funcs;
+
+ dev->bo_size = sizeof(struct kgsl_bo);
+
+ return dev;
+}
diff --git a/freedreno/kgsl/kgsl_drm.h b/freedreno/kgsl/kgsl_drm.h
new file mode 100644
index 0000000..281978e
--- /dev/null
+++ b/freedreno/kgsl/kgsl_drm.h
@@ -0,0 +1,192 @@
+#ifndef _KGSL_DRM_H_
+#define _KGSL_DRM_H_
+
+#include "drm.h"
+
+#define DRM_KGSL_GEM_CREATE 0x00
+#define DRM_KGSL_GEM_PREP 0x01
+#define DRM_KGSL_GEM_SETMEMTYPE 0x02
+#define DRM_KGSL_GEM_GETMEMTYPE 0x03
+#define DRM_KGSL_GEM_MMAP 0x04
+#define DRM_KGSL_GEM_ALLOC 0x05
+#define DRM_KGSL_GEM_BIND_GPU 0x06
+#define DRM_KGSL_GEM_UNBIND_GPU 0x07
+
+#define DRM_KGSL_GEM_GET_BUFINFO 0x08
+#define DRM_KGSL_GEM_SET_BUFCOUNT 0x09
+#define DRM_KGSL_GEM_SET_ACTIVE 0x0A
+#define DRM_KGSL_GEM_LOCK_HANDLE 0x0B
+#define DRM_KGSL_GEM_UNLOCK_HANDLE 0x0C
+#define DRM_KGSL_GEM_UNLOCK_ON_TS 0x0D
+#define DRM_KGSL_GEM_CREATE_FD 0x0E
+
+#define DRM_IOCTL_KGSL_GEM_CREATE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE, struct drm_kgsl_gem_create)
+
+#define DRM_IOCTL_KGSL_GEM_PREP \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_PREP, struct drm_kgsl_gem_prep)
+
+#define DRM_IOCTL_KGSL_GEM_SETMEMTYPE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_SETMEMTYPE, \
+struct drm_kgsl_gem_memtype)
+
+#define DRM_IOCTL_KGSL_GEM_GETMEMTYPE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_GETMEMTYPE, \
+struct drm_kgsl_gem_memtype)
+
+#define DRM_IOCTL_KGSL_GEM_MMAP \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_MMAP, struct drm_kgsl_gem_mmap)
+
+#define DRM_IOCTL_KGSL_GEM_ALLOC \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_ALLOC, struct drm_kgsl_gem_alloc)
+
+#define DRM_IOCTL_KGSL_GEM_BIND_GPU \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_BIND_GPU, struct drm_kgsl_gem_bind_gpu)
+
+#define DRM_IOCTL_KGSL_GEM_UNBIND_GPU \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_UNBIND_GPU, \
+struct drm_kgsl_gem_bind_gpu)
+
+#define DRM_IOCTL_KGSL_GEM_GET_BUFINFO \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_GET_BUFINFO, \
+ struct drm_kgsl_gem_bufinfo)
+
+#define DRM_IOCTL_KGSL_GEM_SET_BUFCOUNT \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_SET_BUFCOUNT, \
+ struct drm_kgsl_gem_bufcount)
+
+#define DRM_IOCTL_KGSL_GEM_SET_ACTIVE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_SET_ACTIVE, \
+ struct drm_kgsl_gem_active)
+
+#define DRM_IOCTL_KGSL_GEM_LOCK_HANDLE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_LOCK_HANDLE, \
+struct drm_kgsl_gem_lock_handles)
+
+#define DRM_IOCTL_KGSL_GEM_UNLOCK_HANDLE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_UNLOCK_HANDLE, \
+struct drm_kgsl_gem_unlock_handles)
+
+#define DRM_IOCTL_KGSL_GEM_UNLOCK_ON_TS \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_UNLOCK_ON_TS, \
+struct drm_kgsl_gem_unlock_on_ts)
+
+#define DRM_IOCTL_KGSL_GEM_CREATE_FD \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE_FD, \
+struct drm_kgsl_gem_create_fd)
+
+/* Maximum number of sub buffers per GEM object */
+#define DRM_KGSL_GEM_MAX_BUFFERS 2
+
+/* Memory types - these define the source and caching policies
+ of the GEM memory chunk */
+
+/* Legacy definitions left for compatibility */
+
+#define DRM_KGSL_GEM_TYPE_EBI 0
+#define DRM_KGSL_GEM_TYPE_SMI 1
+#define DRM_KGSL_GEM_TYPE_KMEM 2
+#define DRM_KGSL_GEM_TYPE_KMEM_NOCACHE 3
+#define DRM_KGSL_GEM_TYPE_MEM_MASK 0xF
+
+/* Contiguous memory (PMEM) */
+#define DRM_KGSL_GEM_TYPE_PMEM 0x000100
+
+/* PMEM memory types */
+#define DRM_KGSL_GEM_PMEM_EBI 0x001000
+#define DRM_KGSL_GEM_PMEM_SMI 0x002000
+
+/* Standard paged memory */
+#define DRM_KGSL_GEM_TYPE_MEM 0x010000
+
+/* Caching controls */
+#define DRM_KGSL_GEM_CACHE_NONE 0x000000
+#define DRM_KGSL_GEM_CACHE_WCOMBINE 0x100000
+#define DRM_KGSL_GEM_CACHE_WTHROUGH 0x200000
+#define DRM_KGSL_GEM_CACHE_WBACK 0x400000
+#define DRM_KGSL_GEM_CACHE_WBACKWA 0x800000
+#define DRM_KGSL_GEM_CACHE_MASK 0xF00000
+
+/* FD based objects */
+#define DRM_KGSL_GEM_TYPE_FD_FBMEM 0x1000000
+#define DRM_KGSL_GEM_TYPE_FD_MASK 0xF000000
+
+/* Timestamp types */
+#define DRM_KGSL_GEM_TS_3D 0x00000430
+#define DRM_KGSL_GEM_TS_2D 0x00000180
+
+
+struct drm_kgsl_gem_create {
+ uint32_t size;
+ uint32_t handle;
+};
+
+struct drm_kgsl_gem_prep {
+ uint32_t handle;
+ uint32_t phys;
+ uint64_t offset;
+};
+
+struct drm_kgsl_gem_memtype {
+ uint32_t handle;
+ uint32_t type;
+};
+
+struct drm_kgsl_gem_mmap {
+ uint32_t handle;
+ uint32_t size;
+ uint32_t hostptr;
+ uint64_t offset;
+};
+
+struct drm_kgsl_gem_alloc {
+ uint32_t handle;
+ uint64_t offset;
+};
+
+struct drm_kgsl_gem_bind_gpu {
+ uint32_t handle;
+ uint32_t gpuptr;
+};
+
+struct drm_kgsl_gem_bufinfo {
+ uint32_t handle;
+ uint32_t count;
+ uint32_t active;
+ uint32_t offset[DRM_KGSL_GEM_MAX_BUFFERS];
+ uint32_t gpuaddr[DRM_KGSL_GEM_MAX_BUFFERS];
+};
+
+struct drm_kgsl_gem_bufcount {
+ uint32_t handle;
+ uint32_t bufcount;
+};
+
+struct drm_kgsl_gem_active {
+ uint32_t handle;
+ uint32_t active;
+};
+
+struct drm_kgsl_gem_lock_handles {
+ uint32_t num_handles;
+ uint32_t *handle_list;
+ uint32_t pid;
+ uint32_t lock_id; /* Returned lock id used for unlocking */
+};
+
+struct drm_kgsl_gem_unlock_handles {
+ uint32_t lock_id;
+};
+
+struct drm_kgsl_gem_unlock_on_ts {
+ uint32_t lock_id;
+ uint32_t timestamp; /* This field is a hw generated ts */
+ uint32_t type; /* Which pipe to check for ts generation */
+};
+
+struct drm_kgsl_gem_create_fd {
+ uint32_t fd;
+ uint32_t handle;
+};
+
+#endif
diff --git a/freedreno/kgsl/kgsl_pipe.c b/freedreno/kgsl/kgsl_pipe.c
new file mode 100644
index 0000000..0a8b658
--- /dev/null
+++ b/freedreno/kgsl/kgsl_pipe.c
@@ -0,0 +1,282 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "kgsl_priv.h"
+
+
+static int kgsl_pipe_get_param(struct fd_pipe *pipe,
+ enum fd_param_id param, uint64_t *value)
+{
+ struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe);
+ switch (param) {
+ case FD_DEVICE_ID:
+ *value = kgsl_pipe->devinfo.device_id;
+ return 0;
+ case FD_GPU_ID:
+ *value = kgsl_pipe->devinfo.gpu_id;
+ return 0;
+ case FD_GMEM_SIZE:
+ *value = kgsl_pipe->devinfo.gmem_sizebytes;
+ return 0;
+ case FD_CHIP_ID:
+ *value = kgsl_pipe->devinfo.chip_id;
+ return 0;
+ case FD_MAX_FREQ:
+ case FD_TIMESTAMP:
+ case FD_NR_RINGS:
+ /* unsupported on kgsl */
+ return -1;
+ default:
+ ERROR_MSG("invalid param id: %d", param);
+ return -1;
+ }
+}
+
+static int kgsl_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp,
+ uint64_t timeout)
+{
+ struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe);
+ struct kgsl_device_waittimestamp req = {
+ .timestamp = timestamp,
+ .timeout = 5000,
+ };
+ int ret;
+
+ do {
+ ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_DEVICE_WAITTIMESTAMP, &req);
+ } while ((ret == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+ if (ret)
+ ERROR_MSG("waittimestamp failed! %d (%s)", ret, strerror(errno));
+ else
+ kgsl_pipe_process_pending(kgsl_pipe, timestamp);
+ return ret;
+}
+
+drm_private int kgsl_pipe_timestamp(struct kgsl_pipe *kgsl_pipe,
+ uint32_t *timestamp)
+{
+ struct kgsl_cmdstream_readtimestamp req = {
+ .type = KGSL_TIMESTAMP_RETIRED
+ };
+ int ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_CMDSTREAM_READTIMESTAMP, &req);
+ if (ret) {
+ ERROR_MSG("readtimestamp failed! %d (%s)",
+ ret, strerror(errno));
+ return ret;
+ }
+ *timestamp = req.timestamp;
+ return 0;
+}
+
+static void kgsl_pipe_destroy(struct fd_pipe *pipe)
+{
+ struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe);
+ struct kgsl_drawctxt_destroy req = {
+ .drawctxt_id = kgsl_pipe->drawctxt_id,
+ };
+
+ if (kgsl_pipe->drawctxt_id)
+ ioctl(kgsl_pipe->fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &req);
+
+ if (kgsl_pipe->fd >= 0)
+ close(kgsl_pipe->fd);
+
+ free(kgsl_pipe);
+}
+
+static const struct fd_pipe_funcs funcs = {
+ .ringbuffer_new = kgsl_ringbuffer_new,
+ .get_param = kgsl_pipe_get_param,
+ .wait = kgsl_pipe_wait,
+ .destroy = kgsl_pipe_destroy,
+};
+
+drm_private int is_kgsl_pipe(struct fd_pipe *pipe)
+{
+ return pipe->funcs == &funcs;
+}
+
+/* add buffer to submit list when it is referenced in cmdstream: */
+drm_private void kgsl_pipe_add_submit(struct kgsl_pipe *kgsl_pipe,
+ struct kgsl_bo *kgsl_bo)
+{
+ struct fd_pipe *pipe = &kgsl_pipe->base;
+ struct fd_bo *bo = &kgsl_bo->base;
+ struct list_head *list = &kgsl_bo->list[pipe->id];
+ if (LIST_IS_EMPTY(list)) {
+ fd_bo_ref(bo);
+ } else {
+ list_del(list);
+ }
+ list_addtail(list, &kgsl_pipe->submit_list);
+}
+
+/* prepare buffers on submit list before flush: */
+drm_private void kgsl_pipe_pre_submit(struct kgsl_pipe *kgsl_pipe)
+{
+ struct fd_pipe *pipe = &kgsl_pipe->base;
+ struct kgsl_bo *kgsl_bo = NULL;
+
+ if (!kgsl_pipe->p3d)
+ kgsl_pipe->p3d = fd_pipe_new(pipe->dev, FD_PIPE_3D);
+
+ LIST_FOR_EACH_ENTRY(kgsl_bo, &kgsl_pipe->submit_list, list[pipe->id]) {
+ uint32_t timestamp = kgsl_bo_get_timestamp(kgsl_bo);
+ if (timestamp)
+ fd_pipe_wait(kgsl_pipe->p3d, timestamp);
+ }
+}
+
+/* process buffers on submit list after flush: */
+drm_private void kgsl_pipe_post_submit(struct kgsl_pipe *kgsl_pipe,
+ uint32_t timestamp)
+{
+ struct fd_pipe *pipe = &kgsl_pipe->base;
+ struct kgsl_bo *kgsl_bo = NULL, *tmp;
+
+ LIST_FOR_EACH_ENTRY_SAFE(kgsl_bo, tmp, &kgsl_pipe->submit_list, list[pipe->id]) {
+ struct list_head *list = &kgsl_bo->list[pipe->id];
+ list_del(list);
+ kgsl_bo->timestamp[pipe->id] = timestamp;
+ list_addtail(list, &kgsl_pipe->pending_list);
+
+ kgsl_bo_set_timestamp(kgsl_bo, timestamp);
+ }
+
+ if (!kgsl_pipe_timestamp(kgsl_pipe, &timestamp))
+ kgsl_pipe_process_pending(kgsl_pipe, timestamp);
+}
+
+drm_private void kgsl_pipe_process_pending(struct kgsl_pipe *kgsl_pipe,
+ uint32_t timestamp)
+{
+ struct fd_pipe *pipe = &kgsl_pipe->base;
+ struct kgsl_bo *kgsl_bo = NULL, *tmp;
+
+ LIST_FOR_EACH_ENTRY_SAFE(kgsl_bo, tmp, &kgsl_pipe->pending_list, list[pipe->id]) {
+ struct list_head *list = &kgsl_bo->list[pipe->id];
+ if (kgsl_bo->timestamp[pipe->id] > timestamp)
+ return;
+ list_delinit(list);
+ kgsl_bo->timestamp[pipe->id] = 0;
+ fd_bo_del(&kgsl_bo->base);
+ }
+}
+
+static int getprop(int fd, enum kgsl_property_type type,
+ void *value, int sizebytes)
+{
+ struct kgsl_device_getproperty req = {
+ .type = type,
+ .value = value,
+ .sizebytes = sizebytes,
+ };
+ return ioctl(fd, IOCTL_KGSL_DEVICE_GETPROPERTY, &req);
+}
+
+#define GETPROP(fd, prop, x) do { \
+ if (getprop((fd), KGSL_PROP_##prop, &(x), sizeof(x))) { \
+ ERROR_MSG("failed to get property: " #prop); \
+ goto fail; \
+ } } while (0)
+
+
+drm_private struct fd_pipe * kgsl_pipe_new(struct fd_device *dev,
+ enum fd_pipe_id id, uint32_t prio)
+{
+ static const char *paths[] = {
+ [FD_PIPE_3D] = "/dev/kgsl-3d0",
+ [FD_PIPE_2D] = "/dev/kgsl-2d0",
+ };
+ struct kgsl_drawctxt_create req = {
+ .flags = 0x2000, /* ??? */
+ };
+ struct kgsl_pipe *kgsl_pipe = NULL;
+ struct fd_pipe *pipe = NULL;
+ int ret, fd;
+
+ fd = open(paths[id], O_RDWR);
+ if (fd < 0) {
+ ERROR_MSG("could not open %s device: %d (%s)",
+ paths[id], fd, strerror(errno));
+ goto fail;
+ }
+
+ ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &req);
+ if (ret) {
+ ERROR_MSG("failed to allocate context: %d (%s)",
+ ret, strerror(errno));
+ goto fail;
+ }
+
+ kgsl_pipe = calloc(1, sizeof(*kgsl_pipe));
+ if (!kgsl_pipe) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ pipe = &kgsl_pipe->base;
+ pipe->funcs = &funcs;
+
+ kgsl_pipe->fd = fd;
+ kgsl_pipe->drawctxt_id = req.drawctxt_id;
+
+ list_inithead(&kgsl_pipe->submit_list);
+ list_inithead(&kgsl_pipe->pending_list);
+
+ GETPROP(fd, VERSION, kgsl_pipe->version);
+ GETPROP(fd, DEVICE_INFO, kgsl_pipe->devinfo);
+
+ if (kgsl_pipe->devinfo.gpu_id >= 500) {
+ ERROR_MSG("64b unsupported with kgsl");
+ goto fail;
+ }
+
+ INFO_MSG("Pipe Info:");
+ INFO_MSG(" Device: %s", paths[id]);
+ INFO_MSG(" Chip-id: %d.%d.%d.%d",
+ (kgsl_pipe->devinfo.chip_id >> 24) & 0xff,
+ (kgsl_pipe->devinfo.chip_id >> 16) & 0xff,
+ (kgsl_pipe->devinfo.chip_id >> 8) & 0xff,
+ (kgsl_pipe->devinfo.chip_id >> 0) & 0xff);
+ INFO_MSG(" Device-id: %d", kgsl_pipe->devinfo.device_id);
+ INFO_MSG(" GPU-id: %d", kgsl_pipe->devinfo.gpu_id);
+ INFO_MSG(" MMU enabled: %d", kgsl_pipe->devinfo.mmu_enabled);
+ INFO_MSG(" GMEM Base addr: 0x%08x", kgsl_pipe->devinfo.gmem_gpubaseaddr);
+ INFO_MSG(" GMEM size: 0x%08x", kgsl_pipe->devinfo.gmem_sizebytes);
+ INFO_MSG(" Driver version: %d.%d",
+ kgsl_pipe->version.drv_major, kgsl_pipe->version.drv_minor);
+ INFO_MSG(" Device version: %d.%d",
+ kgsl_pipe->version.dev_major, kgsl_pipe->version.dev_minor);
+
+ return pipe;
+fail:
+ if (pipe)
+ fd_pipe_del(pipe);
+ return NULL;
+}
diff --git a/freedreno/kgsl/kgsl_priv.h b/freedreno/kgsl/kgsl_priv.h
new file mode 100644
index 0000000..a6bf2d4
--- /dev/null
+++ b/freedreno/kgsl/kgsl_priv.h
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef KGSL_PRIV_H_
+#define KGSL_PRIV_H_
+
+#include "freedreno_priv.h"
+#include "msm_kgsl.h"
+#include "kgsl_drm.h"
+
+struct kgsl_device {
+ struct fd_device base;
+};
+
+static inline struct kgsl_device * to_kgsl_device(struct fd_device *x)
+{
+ return (struct kgsl_device *)x;
+}
+
+struct kgsl_pipe {
+ struct fd_pipe base;
+
+ int fd;
+ uint32_t drawctxt_id;
+
+ /* device properties: */
+ struct kgsl_version version;
+ struct kgsl_devinfo devinfo;
+
+ /* list of bo's that are referenced in ringbuffer but not
+ * submitted yet:
+ */
+ struct list_head submit_list;
+
+ /* list of bo's that have been submitted but timestamp has
+ * not passed yet (so still ref'd in active cmdstream)
+ */
+ struct list_head pending_list;
+
+ /* if we are the 2d pipe, and want to wait on a timestamp
+ * from 3d, we need to also internally open the 3d pipe:
+ */
+ struct fd_pipe *p3d;
+};
+
+static inline struct kgsl_pipe * to_kgsl_pipe(struct fd_pipe *x)
+{
+ return (struct kgsl_pipe *)x;
+}
+
+drm_private int is_kgsl_pipe(struct fd_pipe *pipe);
+
+struct kgsl_bo {
+ struct fd_bo base;
+ uint64_t offset;
+ uint32_t gpuaddr;
+ /* timestamp (per pipe) for bo's in a pipe's pending_list: */
+ uint32_t timestamp[FD_PIPE_MAX];
+ /* list-node for pipe's submit_list or pending_list */
+ struct list_head list[FD_PIPE_MAX];
+};
+
+static inline struct kgsl_bo * to_kgsl_bo(struct fd_bo *x)
+{
+ return (struct kgsl_bo *)x;
+}
+
+
+drm_private struct fd_device * kgsl_device_new(int fd);
+
+drm_private int kgsl_pipe_timestamp(struct kgsl_pipe *kgsl_pipe,
+ uint32_t *timestamp);
+drm_private void kgsl_pipe_add_submit(struct kgsl_pipe *pipe,
+ struct kgsl_bo *bo);
+drm_private void kgsl_pipe_pre_submit(struct kgsl_pipe *pipe);
+drm_private void kgsl_pipe_post_submit(struct kgsl_pipe *pipe,
+ uint32_t timestamp);
+drm_private void kgsl_pipe_process_pending(struct kgsl_pipe *pipe,
+ uint32_t timestamp);
+drm_private struct fd_pipe * kgsl_pipe_new(struct fd_device *dev,
+ enum fd_pipe_id id, uint32_t prio);
+
+drm_private struct fd_ringbuffer * kgsl_ringbuffer_new(struct fd_pipe *pipe,
+ uint32_t size, enum fd_ringbuffer_flags flags);
+
+drm_private int kgsl_bo_new_handle(struct fd_device *dev,
+ uint32_t size, uint32_t flags, uint32_t *handle);
+drm_private struct fd_bo * kgsl_bo_from_handle(struct fd_device *dev,
+ uint32_t size, uint32_t handle);
+
+drm_private uint32_t kgsl_bo_gpuaddr(struct kgsl_bo *bo, uint32_t offset);
+drm_private void kgsl_bo_set_timestamp(struct kgsl_bo *bo, uint32_t timestamp);
+drm_private uint32_t kgsl_bo_get_timestamp(struct kgsl_bo *bo);
+
+#endif /* KGSL_PRIV_H_ */
diff --git a/freedreno/kgsl/kgsl_ringbuffer.c b/freedreno/kgsl/kgsl_ringbuffer.c
new file mode 100644
index 0000000..9abf0ad
--- /dev/null
+++ b/freedreno/kgsl/kgsl_ringbuffer.c
@@ -0,0 +1,235 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include <assert.h>
+
+#include "xf86atomic.h"
+#include "freedreno_ringbuffer.h"
+#include "kgsl_priv.h"
+
+
+/* because kgsl tries to validate the gpuaddr on kernel side in ISSUEIBCMDS,
+ * we can't use normal gem bo's for ringbuffer.. someday the kernel part
+ * needs to be reworked into a single sane drm driver :-/
+ */
+struct kgsl_rb_bo {
+ struct kgsl_pipe *pipe;
+ void *hostptr;
+ uint32_t gpuaddr;
+ uint32_t size;
+};
+
+struct kgsl_ringbuffer {
+ struct fd_ringbuffer base;
+ struct kgsl_rb_bo *bo;
+};
+
+static inline struct kgsl_ringbuffer * to_kgsl_ringbuffer(struct fd_ringbuffer *x)
+{
+ return (struct kgsl_ringbuffer *)x;
+}
+
+static void kgsl_rb_bo_del(struct kgsl_rb_bo *bo)
+{
+ struct kgsl_sharedmem_free req = {
+ .gpuaddr = bo->gpuaddr,
+ };
+ int ret;
+
+ drm_munmap(bo->hostptr, bo->size);
+
+ ret = ioctl(bo->pipe->fd, IOCTL_KGSL_SHAREDMEM_FREE, &req);
+ if (ret) {
+ ERROR_MSG("sharedmem free failed: %s", strerror(errno));
+ }
+
+ free(bo);
+}
+
+static struct kgsl_rb_bo * kgsl_rb_bo_new(struct kgsl_pipe *pipe, uint32_t size)
+{
+ struct kgsl_rb_bo *bo;
+ struct kgsl_gpumem_alloc req = {
+ .size = ALIGN(size, 4096),
+ .flags = KGSL_MEMFLAGS_GPUREADONLY,
+ };
+ int ret;
+
+ bo = calloc(1, sizeof(*bo));
+ if (!bo) {
+ ERROR_MSG("allocation failed");
+ return NULL;
+ }
+ ret = ioctl(pipe->fd, IOCTL_KGSL_GPUMEM_ALLOC, &req);
+ if (ret) {
+ ERROR_MSG("gpumem allocation failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ bo->pipe = pipe;
+ bo->gpuaddr = req.gpuaddr;
+ bo->size = size;
+ bo->hostptr = drm_mmap(NULL, size, PROT_WRITE|PROT_READ,
+ MAP_SHARED, pipe->fd, req.gpuaddr);
+
+ return bo;
+fail:
+ if (bo)
+ kgsl_rb_bo_del(bo);
+ return NULL;
+}
+
+static void * kgsl_ringbuffer_hostptr(struct fd_ringbuffer *ring)
+{
+ struct kgsl_ringbuffer *kgsl_ring = to_kgsl_ringbuffer(ring);
+ return kgsl_ring->bo->hostptr;
+}
+
+static int kgsl_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start,
+ int in_fence_fd, int *out_fence_fd)
+{
+ struct kgsl_ringbuffer *kgsl_ring = to_kgsl_ringbuffer(ring);
+ struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(ring->pipe);
+ uint32_t offset = (uint8_t *)last_start - (uint8_t *)ring->start;
+ struct kgsl_ibdesc ibdesc = {
+ .gpuaddr = kgsl_ring->bo->gpuaddr + offset,
+ .hostptr = last_start,
+ .sizedwords = ring->cur - last_start,
+ };
+ struct kgsl_ringbuffer_issueibcmds req = {
+ .drawctxt_id = kgsl_pipe->drawctxt_id,
+ .ibdesc_addr = (unsigned long)&ibdesc,
+ .numibs = 1,
+ .flags = KGSL_CONTEXT_SUBMIT_IB_LIST,
+ };
+ int ret;
+
+ assert(in_fence_fd == -1);
+ assert(out_fence_fd == NULL);
+
+ kgsl_pipe_pre_submit(kgsl_pipe);
+
+ /* z180_cmdstream_issueibcmds() is made of fail: */
+ if (ring->pipe->id == FD_PIPE_2D) {
+ /* fix up size field in last cmd packet */
+ uint32_t last_size = (uint32_t)(ring->cur - last_start);
+ /* 5 is length of first packet, 2 for the two 7f000000's */
+ last_start[2] = last_size - (5 + 2);
+ ibdesc.gpuaddr = kgsl_ring->bo->gpuaddr;
+ ibdesc.hostptr = kgsl_ring->bo->hostptr;
+ ibdesc.sizedwords = 0x145;
+ req.timestamp = (uintptr_t)kgsl_ring->bo->hostptr;
+ }
+
+ do {
+ ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS, &req);
+ } while ((ret == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+ if (ret)
+ ERROR_MSG("issueibcmds failed! %d (%s)", ret, strerror(errno));
+
+ ring->last_timestamp = req.timestamp;
+ ring->last_start = ring->cur;
+
+ kgsl_pipe_post_submit(kgsl_pipe, req.timestamp);
+
+ return ret;
+}
+
+static void kgsl_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
+ const struct fd_reloc *r)
+{
+ struct kgsl_bo *kgsl_bo = to_kgsl_bo(r->bo);
+ uint32_t addr = kgsl_bo_gpuaddr(kgsl_bo, r->offset);
+ assert(addr);
+ if (r->shift < 0)
+ addr >>= -r->shift;
+ else
+ addr <<= r->shift;
+ (*ring->cur++) = addr | r->or;
+ kgsl_pipe_add_submit(to_kgsl_pipe(ring->pipe), kgsl_bo);
+}
+
+static uint32_t kgsl_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *target, uint32_t cmd_idx)
+{
+ struct kgsl_ringbuffer *target_ring = to_kgsl_ringbuffer(target);
+ assert(cmd_idx == 0);
+ (*ring->cur++) = target_ring->bo->gpuaddr;
+ return offset_bytes(target->cur, target->start);
+}
+
+static void kgsl_ringbuffer_destroy(struct fd_ringbuffer *ring)
+{
+ struct kgsl_ringbuffer *kgsl_ring = to_kgsl_ringbuffer(ring);
+ if (ring->last_timestamp)
+ fd_pipe_wait(ring->pipe, ring->last_timestamp);
+ if (kgsl_ring->bo)
+ kgsl_rb_bo_del(kgsl_ring->bo);
+ free(kgsl_ring);
+}
+
+static const struct fd_ringbuffer_funcs funcs = {
+ .hostptr = kgsl_ringbuffer_hostptr,
+ .flush = kgsl_ringbuffer_flush,
+ .emit_reloc = kgsl_ringbuffer_emit_reloc,
+ .emit_reloc_ring = kgsl_ringbuffer_emit_reloc_ring,
+ .destroy = kgsl_ringbuffer_destroy,
+};
+
+drm_private struct fd_ringbuffer * kgsl_ringbuffer_new(struct fd_pipe *pipe,
+ uint32_t size, enum fd_ringbuffer_flags flags)
+{
+ struct kgsl_ringbuffer *kgsl_ring;
+ struct fd_ringbuffer *ring = NULL;
+
+ assert(!flags);
+
+ kgsl_ring = calloc(1, sizeof(*kgsl_ring));
+ if (!kgsl_ring) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ ring = &kgsl_ring->base;
+ atomic_set(&ring->refcnt, 1);
+
+ ring->funcs = &funcs;
+ ring->size = size;
+
+ kgsl_ring->bo = kgsl_rb_bo_new(to_kgsl_pipe(pipe), size);
+ if (!kgsl_ring->bo) {
+ ERROR_MSG("ringbuffer allocation failed");
+ goto fail;
+ }
+
+ return ring;
+fail:
+ if (ring)
+ fd_ringbuffer_del(ring);
+ return NULL;
+}
diff --git a/freedreno/kgsl/msm_kgsl.h b/freedreno/kgsl/msm_kgsl.h
new file mode 100644
index 0000000..5b36eeb
--- /dev/null
+++ b/freedreno/kgsl/msm_kgsl.h
@@ -0,0 +1,519 @@
+#ifndef _MSM_KGSL_H
+#define _MSM_KGSL_H
+
+#define KGSL_VERSION_MAJOR 3
+#define KGSL_VERSION_MINOR 11
+
+/*context flags */
+#define KGSL_CONTEXT_SAVE_GMEM 0x00000001
+#define KGSL_CONTEXT_NO_GMEM_ALLOC 0x00000002
+#define KGSL_CONTEXT_SUBMIT_IB_LIST 0x00000004
+#define KGSL_CONTEXT_CTX_SWITCH 0x00000008
+#define KGSL_CONTEXT_PREAMBLE 0x00000010
+#define KGSL_CONTEXT_TRASH_STATE 0x00000020
+#define KGSL_CONTEXT_PER_CONTEXT_TS 0x00000040
+
+#define KGSL_CONTEXT_INVALID 0xffffffff
+
+/* Memory allocayion flags */
+#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000
+
+/* generic flag values */
+#define KGSL_FLAGS_NORMALMODE 0x00000000
+#define KGSL_FLAGS_SAFEMODE 0x00000001
+#define KGSL_FLAGS_INITIALIZED0 0x00000002
+#define KGSL_FLAGS_INITIALIZED 0x00000004
+#define KGSL_FLAGS_STARTED 0x00000008
+#define KGSL_FLAGS_ACTIVE 0x00000010
+#define KGSL_FLAGS_RESERVED0 0x00000020
+#define KGSL_FLAGS_RESERVED1 0x00000040
+#define KGSL_FLAGS_RESERVED2 0x00000080
+#define KGSL_FLAGS_SOFT_RESET 0x00000100
+#define KGSL_FLAGS_PER_CONTEXT_TIMESTAMPS 0x00000200
+
+/* Clock flags to show which clocks should be controlled by a given platform */
+#define KGSL_CLK_SRC 0x00000001
+#define KGSL_CLK_CORE 0x00000002
+#define KGSL_CLK_IFACE 0x00000004
+#define KGSL_CLK_MEM 0x00000008
+#define KGSL_CLK_MEM_IFACE 0x00000010
+#define KGSL_CLK_AXI 0x00000020
+
+/*
+ * Reset status values for context
+ */
+enum kgsl_ctx_reset_stat {
+ KGSL_CTX_STAT_NO_ERROR = 0x00000000,
+ KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT = 0x00000001,
+ KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT = 0x00000002,
+ KGSL_CTX_STAT_UNKNOWN_CONTEXT_RESET_EXT = 0x00000003
+};
+
+#define KGSL_MAX_PWRLEVELS 5
+
+#define KGSL_CONVERT_TO_MBPS(val) \
+ (val*1000*1000U)
+
+/* device id */
+enum kgsl_deviceid {
+ KGSL_DEVICE_3D0 = 0x00000000,
+ KGSL_DEVICE_2D0 = 0x00000001,
+ KGSL_DEVICE_2D1 = 0x00000002,
+ KGSL_DEVICE_MAX = 0x00000003
+};
+
+enum kgsl_user_mem_type {
+ KGSL_USER_MEM_TYPE_PMEM = 0x00000000,
+ KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001,
+ KGSL_USER_MEM_TYPE_ADDR = 0x00000002,
+ KGSL_USER_MEM_TYPE_ION = 0x00000003,
+ KGSL_USER_MEM_TYPE_MAX = 0x00000004,
+};
+
+struct kgsl_devinfo {
+
+ unsigned int device_id;
+ /* chip revision id
+ * coreid:8 majorrev:8 minorrev:8 patch:8
+ */
+ unsigned int chip_id;
+ unsigned int mmu_enabled;
+ unsigned int gmem_gpubaseaddr;
+ /*
+ * This field contains the adreno revision
+ * number 200, 205, 220, etc...
+ */
+ unsigned int gpu_id;
+ unsigned int gmem_sizebytes;
+};
+
+/* this structure defines the region of memory that can be mmap()ed from this
+ driver. The timestamp fields are volatile because they are written by the
+ GPU
+*/
+struct kgsl_devmemstore {
+ volatile unsigned int soptimestamp;
+ unsigned int sbz;
+ volatile unsigned int eoptimestamp;
+ unsigned int sbz2;
+ volatile unsigned int ts_cmp_enable;
+ unsigned int sbz3;
+ volatile unsigned int ref_wait_ts;
+ unsigned int sbz4;
+ unsigned int current_context;
+ unsigned int sbz5;
+};
+
+#define KGSL_MEMSTORE_OFFSET(ctxt_id, field) \
+ ((ctxt_id)*sizeof(struct kgsl_devmemstore) + \
+ offsetof(struct kgsl_devmemstore, field))
+
+/* timestamp id*/
+enum kgsl_timestamp_type {
+ KGSL_TIMESTAMP_CONSUMED = 0x00000001, /* start-of-pipeline timestamp */
+ KGSL_TIMESTAMP_RETIRED = 0x00000002, /* end-of-pipeline timestamp*/
+ KGSL_TIMESTAMP_QUEUED = 0x00000003,
+};
+
+/* property types - used with kgsl_device_getproperty */
+enum kgsl_property_type {
+ KGSL_PROP_DEVICE_INFO = 0x00000001,
+ KGSL_PROP_DEVICE_SHADOW = 0x00000002,
+ KGSL_PROP_DEVICE_POWER = 0x00000003,
+ KGSL_PROP_SHMEM = 0x00000004,
+ KGSL_PROP_SHMEM_APERTURES = 0x00000005,
+ KGSL_PROP_MMU_ENABLE = 0x00000006,
+ KGSL_PROP_INTERRUPT_WAITS = 0x00000007,
+ KGSL_PROP_VERSION = 0x00000008,
+ KGSL_PROP_GPU_RESET_STAT = 0x00000009,
+ KGSL_PROP_PWRCTRL = 0x0000000E,
+};
+
+struct kgsl_shadowprop {
+ unsigned int gpuaddr;
+ unsigned int size;
+ unsigned int flags; /* contains KGSL_FLAGS_ values */
+};
+
+struct kgsl_pwrlevel {
+ unsigned int gpu_freq;
+ unsigned int bus_freq;
+ unsigned int io_fraction;
+};
+
+struct kgsl_version {
+ unsigned int drv_major;
+ unsigned int drv_minor;
+ unsigned int dev_major;
+ unsigned int dev_minor;
+};
+
+#ifdef __KERNEL__
+
+#define KGSL_3D0_REG_MEMORY "kgsl_3d0_reg_memory"
+#define KGSL_3D0_IRQ "kgsl_3d0_irq"
+#define KGSL_2D0_REG_MEMORY "kgsl_2d0_reg_memory"
+#define KGSL_2D0_IRQ "kgsl_2d0_irq"
+#define KGSL_2D1_REG_MEMORY "kgsl_2d1_reg_memory"
+#define KGSL_2D1_IRQ "kgsl_2d1_irq"
+
+enum kgsl_iommu_context_id {
+ KGSL_IOMMU_CONTEXT_USER = 0,
+ KGSL_IOMMU_CONTEXT_PRIV = 1,
+};
+
+struct kgsl_iommu_ctx {
+ const char *iommu_ctx_name;
+ enum kgsl_iommu_context_id ctx_id;
+};
+
+struct kgsl_device_iommu_data {
+ const struct kgsl_iommu_ctx *iommu_ctxs;
+ int iommu_ctx_count;
+ unsigned int physstart;
+ unsigned int physend;
+};
+
+struct kgsl_device_platform_data {
+ struct kgsl_pwrlevel pwrlevel[KGSL_MAX_PWRLEVELS];
+ int init_level;
+ int num_levels;
+ int (*set_grp_async)(void);
+ unsigned int idle_timeout;
+ bool strtstp_sleepwake;
+ unsigned int nap_allowed;
+ unsigned int clk_map;
+ unsigned int idle_needed;
+ struct msm_bus_scale_pdata *bus_scale_table;
+ struct kgsl_device_iommu_data *iommu_data;
+ int iommu_count;
+ struct msm_dcvs_core_info *core_info;
+};
+
+#endif
+
+/* structure holds list of ibs */
+struct kgsl_ibdesc {
+ unsigned int gpuaddr;
+ void *hostptr;
+ unsigned int sizedwords;
+ unsigned int ctrl;
+};
+
+/* ioctls */
+#define KGSL_IOC_TYPE 0x09
+
+/* get misc info about the GPU
+ type should be a value from enum kgsl_property_type
+ value points to a structure that varies based on type
+ sizebytes is sizeof() that structure
+ for KGSL_PROP_DEVICE_INFO, use struct kgsl_devinfo
+ this structure contaings hardware versioning info.
+ for KGSL_PROP_DEVICE_SHADOW, use struct kgsl_shadowprop
+ this is used to find mmap() offset and sizes for mapping
+ struct kgsl_memstore into userspace.
+*/
+struct kgsl_device_getproperty {
+ unsigned int type;
+ void *value;
+ unsigned int sizebytes;
+};
+
+#define IOCTL_KGSL_DEVICE_GETPROPERTY \
+ _IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty)
+
+/* IOCTL_KGSL_DEVICE_READ (0x3) - removed 03/2012
+ */
+
+/* block until the GPU has executed past a given timestamp
+ * timeout is in milliseconds.
+ */
+struct kgsl_device_waittimestamp {
+ unsigned int timestamp;
+ unsigned int timeout;
+};
+
+#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP \
+ _IOW(KGSL_IOC_TYPE, 0x6, struct kgsl_device_waittimestamp)
+
+struct kgsl_device_waittimestamp_ctxtid {
+ unsigned int context_id;
+ unsigned int timestamp;
+ unsigned int timeout;
+};
+
+#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID \
+ _IOW(KGSL_IOC_TYPE, 0x7, struct kgsl_device_waittimestamp_ctxtid)
+
+/* issue indirect commands to the GPU.
+ * drawctxt_id must have been created with IOCTL_KGSL_DRAWCTXT_CREATE
+ * ibaddr and sizedwords must specify a subset of a buffer created
+ * with IOCTL_KGSL_SHAREDMEM_FROM_PMEM
+ * flags may be a mask of KGSL_CONTEXT_ values
+ * timestamp is a returned counter value which can be passed to
+ * other ioctls to determine when the commands have been executed by
+ * the GPU.
+ */
+struct kgsl_ringbuffer_issueibcmds {
+ unsigned int drawctxt_id;
+ unsigned int ibdesc_addr;
+ unsigned int numibs;
+ unsigned int timestamp; /*output param */
+ unsigned int flags;
+};
+
+#define IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS \
+ _IOWR(KGSL_IOC_TYPE, 0x10, struct kgsl_ringbuffer_issueibcmds)
+
+/* read the most recently executed timestamp value
+ * type should be a value from enum kgsl_timestamp_type
+ */
+struct kgsl_cmdstream_readtimestamp {
+ unsigned int type;
+ unsigned int timestamp; /*output param */
+};
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_OLD \
+ _IOR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp)
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP \
+ _IOWR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp)
+
+/* free memory when the GPU reaches a given timestamp.
+ * gpuaddr specify a memory region created by a
+ * IOCTL_KGSL_SHAREDMEM_FROM_PMEM call
+ * type should be a value from enum kgsl_timestamp_type
+ */
+struct kgsl_cmdstream_freememontimestamp {
+ unsigned int gpuaddr;
+ unsigned int type;
+ unsigned int timestamp;
+};
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP \
+ _IOW(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp)
+
+/* Previous versions of this header had incorrectly defined
+ IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP as a read-only ioctl instead
+ of a write only ioctl. To ensure binary compatibility, the following
+ #define will be used to intercept the incorrect ioctl
+*/
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_OLD \
+ _IOR(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp)
+
+/* create a draw context, which is used to preserve GPU state.
+ * The flags field may contain a mask KGSL_CONTEXT_* values
+ */
+struct kgsl_drawctxt_create {
+ unsigned int flags;
+ unsigned int drawctxt_id; /*output param */
+};
+
+#define IOCTL_KGSL_DRAWCTXT_CREATE \
+ _IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
+
+/* destroy a draw context */
+struct kgsl_drawctxt_destroy {
+ unsigned int drawctxt_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_DESTROY \
+ _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
+
+/* add a block of pmem, fb, ashmem or user allocated address
+ * into the GPU address space */
+struct kgsl_map_user_mem {
+ int fd;
+ unsigned int gpuaddr; /*output param */
+ unsigned int len;
+ unsigned int offset;
+ unsigned int hostptr; /*input param */
+ enum kgsl_user_mem_type memtype;
+ unsigned int reserved; /* May be required to add
+ params for another mem type */
+};
+
+#define IOCTL_KGSL_MAP_USER_MEM \
+ _IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem)
+
+struct kgsl_cmdstream_readtimestamp_ctxtid {
+ unsigned int context_id;
+ unsigned int type;
+ unsigned int timestamp; /*output param */
+};
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID \
+ _IOWR(KGSL_IOC_TYPE, 0x16, struct kgsl_cmdstream_readtimestamp_ctxtid)
+
+struct kgsl_cmdstream_freememontimestamp_ctxtid {
+ unsigned int context_id;
+ unsigned int gpuaddr;
+ unsigned int type;
+ unsigned int timestamp;
+};
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_CTXTID \
+ _IOW(KGSL_IOC_TYPE, 0x17, \
+ struct kgsl_cmdstream_freememontimestamp_ctxtid)
+
+/* add a block of pmem or fb into the GPU address space */
+struct kgsl_sharedmem_from_pmem {
+ int pmem_fd;
+ unsigned int gpuaddr; /*output param */
+ unsigned int len;
+ unsigned int offset;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FROM_PMEM \
+ _IOWR(KGSL_IOC_TYPE, 0x20, struct kgsl_sharedmem_from_pmem)
+
+/* remove memory from the GPU's address space */
+struct kgsl_sharedmem_free {
+ unsigned int gpuaddr;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FREE \
+ _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free)
+
+struct kgsl_cff_user_event {
+ unsigned char cff_opcode;
+ unsigned int op1;
+ unsigned int op2;
+ unsigned int op3;
+ unsigned int op4;
+ unsigned int op5;
+ unsigned int __pad[2];
+};
+
+#define IOCTL_KGSL_CFF_USER_EVENT \
+ _IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_cff_user_event)
+
+struct kgsl_gmem_desc {
+ unsigned int x;
+ unsigned int y;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pitch;
+};
+
+struct kgsl_buffer_desc {
+ void *hostptr;
+ unsigned int gpuaddr;
+ int size;
+ unsigned int format;
+ unsigned int pitch;
+ unsigned int enabled;
+};
+
+struct kgsl_bind_gmem_shadow {
+ unsigned int drawctxt_id;
+ struct kgsl_gmem_desc gmem_desc;
+ unsigned int shadow_x;
+ unsigned int shadow_y;
+ struct kgsl_buffer_desc shadow_buffer;
+ unsigned int buffer_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_BIND_GMEM_SHADOW \
+ _IOW(KGSL_IOC_TYPE, 0x22, struct kgsl_bind_gmem_shadow)
+
+/* add a block of memory into the GPU address space */
+struct kgsl_sharedmem_from_vmalloc {
+ unsigned int gpuaddr; /*output param */
+ unsigned int hostptr;
+ unsigned int flags;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC \
+ _IOWR(KGSL_IOC_TYPE, 0x23, struct kgsl_sharedmem_from_vmalloc)
+
+#define IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE \
+ _IOW(KGSL_IOC_TYPE, 0x24, struct kgsl_sharedmem_free)
+
+struct kgsl_drawctxt_set_bin_base_offset {
+ unsigned int drawctxt_id;
+ unsigned int offset;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET \
+ _IOW(KGSL_IOC_TYPE, 0x25, struct kgsl_drawctxt_set_bin_base_offset)
+
+enum kgsl_cmdwindow_type {
+ KGSL_CMDWINDOW_MIN = 0x00000000,
+ KGSL_CMDWINDOW_2D = 0x00000000,
+ KGSL_CMDWINDOW_3D = 0x00000001, /* legacy */
+ KGSL_CMDWINDOW_MMU = 0x00000002,
+ KGSL_CMDWINDOW_ARBITER = 0x000000FF,
+ KGSL_CMDWINDOW_MAX = 0x000000FF,
+};
+
+/* write to the command window */
+struct kgsl_cmdwindow_write {
+ enum kgsl_cmdwindow_type target;
+ unsigned int addr;
+ unsigned int data;
+};
+
+#define IOCTL_KGSL_CMDWINDOW_WRITE \
+ _IOW(KGSL_IOC_TYPE, 0x2e, struct kgsl_cmdwindow_write)
+
+struct kgsl_gpumem_alloc {
+ unsigned long gpuaddr;
+ size_t size;
+ unsigned int flags;
+};
+
+#define IOCTL_KGSL_GPUMEM_ALLOC \
+ _IOWR(KGSL_IOC_TYPE, 0x2f, struct kgsl_gpumem_alloc)
+
+struct kgsl_cff_syncmem {
+ unsigned int gpuaddr;
+ unsigned int len;
+ unsigned int __pad[2]; /* For future binary compatibility */
+};
+
+#define IOCTL_KGSL_CFF_SYNCMEM \
+ _IOW(KGSL_IOC_TYPE, 0x30, struct kgsl_cff_syncmem)
+
+/*
+ * A timestamp event allows the user space to register an action following an
+ * expired timestamp.
+ */
+
+struct kgsl_timestamp_event {
+ int type; /* Type of event (see list below) */
+ unsigned int timestamp; /* Timestamp to trigger event on */
+ unsigned int context_id; /* Context for the timestamp */
+ void *priv; /* Pointer to the event specific blob */
+ size_t len; /* Size of the event specific blob */
+};
+
+#define IOCTL_KGSL_TIMESTAMP_EVENT \
+ _IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_timestamp_event)
+
+/* A genlock timestamp event releases an existing lock on timestamp expire */
+
+#define KGSL_TIMESTAMP_EVENT_GENLOCK 1
+
+struct kgsl_timestamp_event_genlock {
+ int handle; /* Handle of the genlock lock to release */
+};
+
+/*
+ * Set a property within the kernel. Uses the same structure as
+ * IOCTL_KGSL_GETPROPERTY
+ */
+
+#define IOCTL_KGSL_SETPROPERTY \
+ _IOW(KGSL_IOC_TYPE, 0x32, struct kgsl_device_getproperty)
+
+#ifdef __KERNEL__
+#ifdef CONFIG_MSM_KGSL_DRM
+int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,
+ unsigned long *len);
+#else
+#define kgsl_gem_obj_addr(...) 0
+#endif
+#endif
+#endif /* _MSM_KGSL_H */
diff --git a/freedreno/libdrm_freedreno.pc.in b/freedreno/libdrm_freedreno.pc.in
new file mode 100644
index 0000000..b736b65
--- /dev/null
+++ b/freedreno/libdrm_freedreno.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_freedreno
+Description: Userspace interface to freedreno kernel DRM services
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_freedreno
+Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/freedreno
+Requires.private: libdrm
diff --git a/freedreno/meson.build b/freedreno/meson.build
new file mode 100644
index 0000000..63b84fc
--- /dev/null
+++ b/freedreno/meson.build
@@ -0,0 +1,80 @@
+# Copyright © 2017-2018 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+files_freedreno = files(
+ 'freedreno_device.c',
+ 'freedreno_pipe.c',
+ 'freedreno_ringbuffer.c',
+ 'freedreno_bo.c',
+ 'freedreno_bo_cache.c',
+ 'msm/msm_bo.c',
+ 'msm/msm_device.c',
+ 'msm/msm_pipe.c',
+ 'msm/msm_ringbuffer.c',
+)
+
+if with_freedreno_kgsl
+ files_freedreno += files(
+ 'kgsl/kgsl_bo.c',
+ 'kgsl/kgsl_device.c',
+ 'kgsl/kgsl_pipe.c',
+ 'kgsl/kgsl_ringbuffer.c',
+ )
+endif
+
+libdrm_freedreno = shared_library(
+ 'drm_freedreno',
+ [files_freedreno, config_file],
+ c_args : libdrm_c_args,
+ include_directories : [inc_root, inc_drm],
+ dependencies : [dep_valgrind, dep_pthread_stubs, dep_rt, dep_atomic_ops],
+ link_with : libdrm,
+ version : '1.0.0',
+ install : true,
+)
+
+ext_libdrm_freedreno = declare_dependency(
+ link_with : [libdrm, libdrm_freedreno],
+ include_directories : [inc_drm, include_directories('.')],
+)
+
+install_headers(
+ 'freedreno_drmif.h', 'freedreno_ringbuffer.h',
+ subdir : 'freedreno'
+)
+
+pkg.generate(
+ name : 'libdrm_freedreno',
+ libraries : libdrm_freedreno,
+ subdirs : ['.', 'libdrm', 'freedreno'],
+ version : meson.project_version(),
+ requires_private : 'libdrm',
+ description : 'Userspace interface to freedreno kernel DRM services',
+)
+
+test(
+ 'freedreno-symbols-check',
+ symbols_check,
+ args : [
+ '--lib', libdrm_freedreno,
+ '--symbols-file', files('freedreno-symbols.txt'),
+ '--nm', prog_nm.path(),
+ ],
+)
diff --git a/freedreno/msm/msm_bo.c b/freedreno/msm/msm_bo.c
new file mode 100644
index 0000000..8b3d0bc
--- /dev/null
+++ b/freedreno/msm/msm_bo.c
@@ -0,0 +1,170 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "msm_priv.h"
+
+static int bo_allocate(struct msm_bo *msm_bo)
+{
+ struct fd_bo *bo = &msm_bo->base;
+ if (!msm_bo->offset) {
+ struct drm_msm_gem_info req = {
+ .handle = bo->handle,
+ };
+ int ret;
+
+ /* if the buffer is already backed by pages then this
+ * doesn't actually do anything (other than giving us
+ * the offset)
+ */
+ ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_INFO,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("alloc failed: %s", strerror(errno));
+ return ret;
+ }
+
+ msm_bo->offset = req.offset;
+ }
+
+ return 0;
+}
+
+static int msm_bo_offset(struct fd_bo *bo, uint64_t *offset)
+{
+ struct msm_bo *msm_bo = to_msm_bo(bo);
+ int ret = bo_allocate(msm_bo);
+ if (ret)
+ return ret;
+ *offset = msm_bo->offset;
+ return 0;
+}
+
+static int msm_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
+{
+ struct drm_msm_gem_cpu_prep req = {
+ .handle = bo->handle,
+ .op = op,
+ };
+
+ get_abs_timeout(&req.timeout, 5000000000);
+
+ return drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_PREP, &req, sizeof(req));
+}
+
+static void msm_bo_cpu_fini(struct fd_bo *bo)
+{
+ struct drm_msm_gem_cpu_fini req = {
+ .handle = bo->handle,
+ };
+
+ drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_FINI, &req, sizeof(req));
+}
+
+static int msm_bo_madvise(struct fd_bo *bo, int willneed)
+{
+ struct drm_msm_gem_madvise req = {
+ .handle = bo->handle,
+ .madv = willneed ? MSM_MADV_WILLNEED : MSM_MADV_DONTNEED,
+ };
+ int ret;
+
+ /* older kernels do not support this: */
+ if (bo->dev->version < FD_VERSION_MADVISE)
+ return willneed;
+
+ ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_MADVISE, &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ return req.retained;
+}
+
+static uint64_t msm_bo_iova(struct fd_bo *bo)
+{
+ struct drm_msm_gem_info req = {
+ .handle = bo->handle,
+ .flags = MSM_INFO_IOVA,
+ };
+
+ drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_INFO, &req, sizeof(req));
+
+ return req.offset;
+}
+
+static void msm_bo_destroy(struct fd_bo *bo)
+{
+ struct msm_bo *msm_bo = to_msm_bo(bo);
+ free(msm_bo);
+
+}
+
+static const struct fd_bo_funcs funcs = {
+ .offset = msm_bo_offset,
+ .cpu_prep = msm_bo_cpu_prep,
+ .cpu_fini = msm_bo_cpu_fini,
+ .madvise = msm_bo_madvise,
+ .iova = msm_bo_iova,
+ .destroy = msm_bo_destroy,
+};
+
+/* allocate a buffer handle: */
+drm_private int msm_bo_new_handle(struct fd_device *dev,
+ uint32_t size, uint32_t flags, uint32_t *handle)
+{
+ struct drm_msm_gem_new req = {
+ .size = size,
+ .flags = MSM_BO_WC, // TODO figure out proper flags..
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(dev->fd, DRM_MSM_GEM_NEW,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ *handle = req.handle;
+
+ return 0;
+}
+
+/* allocate a new buffer object */
+drm_private struct fd_bo * msm_bo_from_handle(struct fd_device *dev,
+ uint32_t size, uint32_t handle)
+{
+ struct msm_bo *msm_bo;
+ struct fd_bo *bo;
+
+ msm_bo = calloc(1, sizeof(*msm_bo));
+ if (!msm_bo)
+ return NULL;
+
+ bo = &msm_bo->base;
+ bo->funcs = &funcs;
+
+ return bo;
+}
diff --git a/freedreno/msm/msm_device.c b/freedreno/msm/msm_device.c
new file mode 100644
index 0000000..58b0746
--- /dev/null
+++ b/freedreno/msm/msm_device.c
@@ -0,0 +1,63 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "msm_priv.h"
+
+static void msm_device_destroy(struct fd_device *dev)
+{
+ struct msm_device *msm_dev = to_msm_device(dev);
+ free(msm_dev);
+}
+
+static const struct fd_device_funcs funcs = {
+ .bo_new_handle = msm_bo_new_handle,
+ .bo_from_handle = msm_bo_from_handle,
+ .pipe_new = msm_pipe_new,
+ .destroy = msm_device_destroy,
+};
+
+drm_private struct fd_device * msm_device_new(int fd)
+{
+ struct msm_device *msm_dev;
+ struct fd_device *dev;
+
+ msm_dev = calloc(1, sizeof(*msm_dev));
+ if (!msm_dev)
+ return NULL;
+
+ dev = &msm_dev->base;
+ dev->funcs = &funcs;
+
+ dev->bo_size = sizeof(struct msm_bo);
+
+ return dev;
+}
diff --git a/freedreno/msm/msm_pipe.c b/freedreno/msm/msm_pipe.c
new file mode 100644
index 0000000..e070b31
--- /dev/null
+++ b/freedreno/msm/msm_pipe.c
@@ -0,0 +1,212 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include "msm_priv.h"
+
+static int query_param(struct fd_pipe *pipe, uint32_t param,
+ uint64_t *value)
+{
+ struct msm_pipe *msm_pipe = to_msm_pipe(pipe);
+ struct drm_msm_param req = {
+ .pipe = msm_pipe->pipe,
+ .param = param,
+ };
+ int ret;
+
+ ret = drmCommandWriteRead(pipe->dev->fd, DRM_MSM_GET_PARAM,
+ &req, sizeof(req));
+ if (ret)
+ return ret;
+
+ *value = req.value;
+
+ return 0;
+}
+
+static int msm_pipe_get_param(struct fd_pipe *pipe,
+ enum fd_param_id param, uint64_t *value)
+{
+ struct msm_pipe *msm_pipe = to_msm_pipe(pipe);
+ switch(param) {
+ case FD_DEVICE_ID: // XXX probably get rid of this..
+ case FD_GPU_ID:
+ *value = msm_pipe->gpu_id;
+ return 0;
+ case FD_GMEM_SIZE:
+ *value = msm_pipe->gmem;
+ return 0;
+ case FD_CHIP_ID:
+ *value = msm_pipe->chip_id;
+ return 0;
+ case FD_MAX_FREQ:
+ return query_param(pipe, MSM_PARAM_MAX_FREQ, value);
+ case FD_TIMESTAMP:
+ return query_param(pipe, MSM_PARAM_TIMESTAMP, value);
+ case FD_NR_RINGS:
+ return query_param(pipe, MSM_PARAM_NR_RINGS, value);
+ default:
+ ERROR_MSG("invalid param id: %d", param);
+ return -1;
+ }
+}
+
+static int msm_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp,
+ uint64_t timeout)
+{
+ struct fd_device *dev = pipe->dev;
+ struct drm_msm_wait_fence req = {
+ .fence = timestamp,
+ .queueid = to_msm_pipe(pipe)->queue_id,
+ };
+ int ret;
+
+ get_abs_timeout(&req.timeout, timeout);
+
+ ret = drmCommandWrite(dev->fd, DRM_MSM_WAIT_FENCE, &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int open_submitqueue(struct fd_pipe *pipe, uint32_t prio)
+{
+ struct drm_msm_submitqueue req = {
+ .flags = 0,
+ .prio = prio,
+ };
+ uint64_t nr_rings = 1;
+ int ret;
+
+ if (fd_device_version(pipe->dev) < FD_VERSION_SUBMIT_QUEUES) {
+ to_msm_pipe(pipe)->queue_id = 0;
+ return 0;
+ }
+
+ msm_pipe_get_param(pipe, FD_NR_RINGS, &nr_rings);
+
+ req.prio = MIN2(req.prio, MAX2(nr_rings, 1) - 1);
+
+ ret = drmCommandWriteRead(pipe->dev->fd, DRM_MSM_SUBMITQUEUE_NEW,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("could not create submitqueue! %d (%s)", ret, strerror(errno));
+ return ret;
+ }
+
+ to_msm_pipe(pipe)->queue_id = req.id;
+ return 0;
+}
+
+static void close_submitqueue(struct fd_pipe *pipe, uint32_t queue_id)
+{
+ if (fd_device_version(pipe->dev) < FD_VERSION_SUBMIT_QUEUES)
+ return;
+
+ drmCommandWrite(pipe->dev->fd, DRM_MSM_SUBMITQUEUE_CLOSE,
+ &queue_id, sizeof(queue_id));
+}
+
+static void msm_pipe_destroy(struct fd_pipe *pipe)
+{
+ struct msm_pipe *msm_pipe = to_msm_pipe(pipe);
+ close_submitqueue(pipe, msm_pipe->queue_id);
+
+ if (msm_pipe->suballoc_ring) {
+ fd_ringbuffer_del(msm_pipe->suballoc_ring);
+ msm_pipe->suballoc_ring = NULL;
+ }
+
+ free(msm_pipe);
+}
+
+static const struct fd_pipe_funcs funcs = {
+ .ringbuffer_new = msm_ringbuffer_new,
+ .get_param = msm_pipe_get_param,
+ .wait = msm_pipe_wait,
+ .destroy = msm_pipe_destroy,
+};
+
+static uint64_t get_param(struct fd_pipe *pipe, uint32_t param)
+{
+ uint64_t value;
+ int ret = query_param(pipe, param, &value);
+ if (ret) {
+ ERROR_MSG("get-param failed! %d (%s)", ret, strerror(errno));
+ return 0;
+ }
+ return value;
+}
+
+drm_private struct fd_pipe * msm_pipe_new(struct fd_device *dev,
+ enum fd_pipe_id id, uint32_t prio)
+{
+ static const uint32_t pipe_id[] = {
+ [FD_PIPE_3D] = MSM_PIPE_3D0,
+ [FD_PIPE_2D] = MSM_PIPE_2D0,
+ };
+ struct msm_pipe *msm_pipe = NULL;
+ struct fd_pipe *pipe = NULL;
+
+ msm_pipe = calloc(1, sizeof(*msm_pipe));
+ if (!msm_pipe) {
+ ERROR_MSG("allocation failed");
+ goto fail;
+ }
+
+ pipe = &msm_pipe->base;
+ pipe->funcs = &funcs;
+
+ /* initialize before get_param(): */
+ pipe->dev = dev;
+ msm_pipe->pipe = pipe_id[id];
+
+ /* these params should be supported since the first version of drm/msm: */
+ msm_pipe->gpu_id = get_param(pipe, MSM_PARAM_GPU_ID);
+ msm_pipe->gmem = get_param(pipe, MSM_PARAM_GMEM_SIZE);
+ msm_pipe->chip_id = get_param(pipe, MSM_PARAM_CHIP_ID);
+
+ if (! msm_pipe->gpu_id)
+ goto fail;
+
+ INFO_MSG("Pipe Info:");
+ INFO_MSG(" GPU-id: %d", msm_pipe->gpu_id);
+ INFO_MSG(" Chip-id: 0x%08x", msm_pipe->chip_id);
+ INFO_MSG(" GMEM size: 0x%08x", msm_pipe->gmem);
+
+ if (open_submitqueue(pipe, prio))
+ goto fail;
+
+ return pipe;
+fail:
+ if (pipe)
+ fd_pipe_del(pipe);
+ return NULL;
+}
diff --git a/freedreno/msm/msm_priv.h b/freedreno/msm/msm_priv.h
new file mode 100644
index 0000000..cc951fb
--- /dev/null
+++ b/freedreno/msm/msm_priv.h
@@ -0,0 +1,141 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef MSM_PRIV_H_
+#define MSM_PRIV_H_
+
+#include "freedreno_priv.h"
+
+#ifndef __user
+# define __user
+#endif
+
+#include "msm_drm.h"
+
+struct msm_device {
+ struct fd_device base;
+ struct fd_bo_cache ring_cache;
+ unsigned ring_cnt;
+};
+
+static inline struct msm_device * to_msm_device(struct fd_device *x)
+{
+ return (struct msm_device *)x;
+}
+
+drm_private struct fd_device * msm_device_new(int fd);
+
+struct msm_pipe {
+ struct fd_pipe base;
+ uint32_t pipe;
+ uint32_t gpu_id;
+ uint32_t gmem;
+ uint32_t chip_id;
+ uint32_t queue_id;
+
+ /* Allow for sub-allocation of stateobj ring buffers (ie. sharing
+ * the same underlying bo)..
+ *
+ * This takes advantage of each context having it's own fd_pipe,
+ * so we don't have to worry about access from multiple threads.
+ *
+ * We also rely on previous stateobj having been fully constructed
+ * so we can reclaim extra space at it's end.
+ */
+ struct fd_ringbuffer *suballoc_ring;
+};
+
+static inline struct msm_pipe * to_msm_pipe(struct fd_pipe *x)
+{
+ return (struct msm_pipe *)x;
+}
+
+drm_private struct fd_pipe * msm_pipe_new(struct fd_device *dev,
+ enum fd_pipe_id id, uint32_t prio);
+
+drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
+ uint32_t size, enum fd_ringbuffer_flags flags);
+
+struct msm_bo {
+ struct fd_bo base;
+ uint64_t offset;
+ uint64_t presumed;
+ /* to avoid excess hashtable lookups, cache the ring this bo was
+ * last emitted on (since that will probably also be the next ring
+ * it is emitted on)
+ */
+ unsigned current_ring_seqno;
+ uint32_t idx;
+};
+
+static inline struct msm_bo * to_msm_bo(struct fd_bo *x)
+{
+ return (struct msm_bo *)x;
+}
+
+drm_private int msm_bo_new_handle(struct fd_device *dev,
+ uint32_t size, uint32_t flags, uint32_t *handle);
+drm_private struct fd_bo * msm_bo_from_handle(struct fd_device *dev,
+ uint32_t size, uint32_t handle);
+
+static inline void get_abs_timeout(struct drm_msm_timespec *tv, uint64_t ns)
+{
+ struct timespec t;
+ uint32_t s = ns / 1000000000;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ tv->tv_sec = t.tv_sec + s;
+ tv->tv_nsec = t.tv_nsec + ns - (s * 1000000000);
+}
+
+/*
+ * Stupid/simple growable array implementation:
+ */
+
+static inline void *
+grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
+{
+ if ((nr + 1) > *max) {
+ if ((*max * 2) < (nr + 1))
+ *max = nr + 5;
+ else
+ *max = *max * 2;
+ ptr = realloc(ptr, *max * sz);
+ }
+ return ptr;
+}
+
+#define DECLARE_ARRAY(type, name) \
+ unsigned nr_ ## name, max_ ## name; \
+ type * name;
+
+#define APPEND(x, name) ({ \
+ (x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
+ (x)->nr_ ## name ++; \
+})
+
+#endif /* MSM_PRIV_H_ */
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
new file mode 100644
index 0000000..7b9df4a
--- /dev/null
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -0,0 +1,723 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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:
+ * Rob Clark <robclark@freedesktop.org>
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+
+#include "xf86atomic.h"
+#include "freedreno_ringbuffer.h"
+#include "msm_priv.h"
+
+/* represents a single cmd buffer in the submit ioctl. Each cmd buffer has
+ * a backing bo, and a reloc table.
+ */
+struct msm_cmd {
+ struct list_head list;
+
+ struct fd_ringbuffer *ring;
+ struct fd_bo *ring_bo;
+
+ /* reloc's table: */
+ DECLARE_ARRAY(struct drm_msm_gem_submit_reloc, relocs);
+
+ uint32_t size;
+
+ /* has cmd already been added to parent rb's submit.cmds table? */
+ int is_appended_to_submit;
+};
+
+struct msm_ringbuffer {
+ struct fd_ringbuffer base;
+
+ /* submit ioctl related tables:
+ * Note that bos and cmds are tracked by the parent ringbuffer, since
+ * that is global to the submit ioctl call. The reloc's table is tracked
+ * per cmd-buffer.
+ */
+ struct {
+ /* bo's table: */
+ DECLARE_ARRAY(struct drm_msm_gem_submit_bo, bos);
+
+ /* cmd's table: */
+ DECLARE_ARRAY(struct drm_msm_gem_submit_cmd, cmds);
+ } submit;
+
+ /* should have matching entries in submit.bos: */
+ /* Note, only in parent ringbuffer */
+ DECLARE_ARRAY(struct fd_bo *, bos);
+
+ /* should have matching entries in submit.cmds: */
+ DECLARE_ARRAY(struct msm_cmd *, cmds);
+
+ /* List of physical cmdstream buffers (msm_cmd) associated with this
+ * logical fd_ringbuffer.
+ *
+ * Note that this is different from msm_ringbuffer::cmds (which
+ * shadows msm_ringbuffer::submit::cmds for tracking submit ioctl
+ * related stuff, and *only* is tracked in the parent ringbuffer.
+ * And only has "completed" cmd buffers (ie. we already know the
+ * size) added via get_cmd().
+ */
+ struct list_head cmd_list;
+
+ int is_growable;
+ unsigned cmd_count;
+
+ unsigned offset; /* for sub-allocated stateobj rb's */
+
+ unsigned seqno;
+
+ /* maps fd_bo to idx: */
+ void *bo_table;
+
+ /* maps msm_cmd to drm_msm_gem_submit_cmd in parent rb. Each rb has a
+ * list of msm_cmd's which correspond to each chunk of cmdstream in
+ * a 'growable' rb. For each of those we need to create one
+ * drm_msm_gem_submit_cmd in the parent rb which collects the state
+ * for the submit ioctl. Because we can have multiple IB's to the same
+ * target rb (for example, or same stateobj emit multiple times), and
+ * because in theory we can have multiple different rb's that have a
+ * reference to a given target, we need a hashtable to track this per
+ * rb.
+ */
+ void *cmd_table;
+};
+
+static inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x)
+{
+ return (struct msm_ringbuffer *)x;
+}
+
+#define INIT_SIZE 0x1000
+
+static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static struct msm_cmd *current_cmd(struct fd_ringbuffer *ring)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ assert(!LIST_IS_EMPTY(&msm_ring->cmd_list));
+ return LIST_LAST_ENTRY(&msm_ring->cmd_list, struct msm_cmd, list);
+}
+
+static void ring_cmd_del(struct msm_cmd *cmd)
+{
+ fd_bo_del(cmd->ring_bo);
+ list_del(&cmd->list);
+ to_msm_ringbuffer(cmd->ring)->cmd_count--;
+ free(cmd->relocs);
+ free(cmd);
+}
+
+static struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size,
+ enum fd_ringbuffer_flags flags)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ struct msm_cmd *cmd = calloc(1, sizeof(*cmd));
+
+ if (!cmd)
+ return NULL;
+
+ cmd->ring = ring;
+
+ /* TODO separate suballoc buffer for small non-streaming state, using
+ * smaller page-sized backing bo's.
+ */
+ if (flags & FD_RINGBUFFER_STREAMING) {
+ struct msm_pipe *msm_pipe = to_msm_pipe(ring->pipe);
+ unsigned suballoc_offset = 0;
+ struct fd_bo *suballoc_bo = NULL;
+
+ if (msm_pipe->suballoc_ring) {
+ struct msm_ringbuffer *suballoc_ring = to_msm_ringbuffer(msm_pipe->suballoc_ring);
+
+ assert(msm_pipe->suballoc_ring->flags & FD_RINGBUFFER_OBJECT);
+ assert(suballoc_ring->cmd_count == 1);
+
+ suballoc_bo = current_cmd(msm_pipe->suballoc_ring)->ring_bo;
+
+ suballoc_offset = fd_ringbuffer_size(msm_pipe->suballoc_ring) +
+ suballoc_ring->offset;
+
+ suballoc_offset = ALIGN(suballoc_offset, 0x10);
+
+ if ((size + suballoc_offset) > suballoc_bo->size) {
+ suballoc_bo = NULL;
+ }
+ }
+
+ if (!suballoc_bo) {
+ cmd->ring_bo = fd_bo_new_ring(ring->pipe->dev, 0x8000, 0);
+ msm_ring->offset = 0;
+ } else {
+ cmd->ring_bo = fd_bo_ref(suballoc_bo);
+ msm_ring->offset = suballoc_offset;
+ }
+
+ if (msm_pipe->suballoc_ring)
+ fd_ringbuffer_del(msm_pipe->suballoc_ring);
+
+ msm_pipe->suballoc_ring = fd_ringbuffer_ref(ring);
+ } else {
+ cmd->ring_bo = fd_bo_new_ring(ring->pipe->dev, size, 0);
+ }
+ if (!cmd->ring_bo)
+ goto fail;
+
+ list_addtail(&cmd->list, &msm_ring->cmd_list);
+ msm_ring->cmd_count++;
+
+ return cmd;
+
+fail:
+ ring_cmd_del(cmd);
+ return NULL;
+}
+
+static uint32_t append_bo(struct fd_ringbuffer *ring, struct fd_bo *bo)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ uint32_t idx;
+
+ idx = APPEND(&msm_ring->submit, bos);
+ idx = APPEND(msm_ring, bos);
+
+ msm_ring->submit.bos[idx].flags = 0;
+ msm_ring->submit.bos[idx].handle = bo->handle;
+ msm_ring->submit.bos[idx].presumed = to_msm_bo(bo)->presumed;
+
+ msm_ring->bos[idx] = fd_bo_ref(bo);
+
+ return idx;
+}
+
+/* add (if needed) bo, return idx: */
+static uint32_t bo2idx(struct fd_ringbuffer *ring, struct fd_bo *bo, uint32_t flags)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ struct msm_bo *msm_bo = to_msm_bo(bo);
+ uint32_t idx;
+ pthread_mutex_lock(&idx_lock);
+ if (msm_bo->current_ring_seqno == msm_ring->seqno) {
+ idx = msm_bo->idx;
+ } else {
+ void *val;
+
+ if (!msm_ring->bo_table)
+ msm_ring->bo_table = drmHashCreate();
+
+ if (!drmHashLookup(msm_ring->bo_table, bo->handle, &val)) {
+ /* found */
+ idx = (uint32_t)(uintptr_t)val;
+ } else {
+ idx = append_bo(ring, bo);
+ val = (void *)(uintptr_t)idx;
+ drmHashInsert(msm_ring->bo_table, bo->handle, val);
+ }
+ msm_bo->current_ring_seqno = msm_ring->seqno;
+ msm_bo->idx = idx;
+ }
+ pthread_mutex_unlock(&idx_lock);
+ if (flags & FD_RELOC_READ)
+ msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_READ;
+ if (flags & FD_RELOC_WRITE)
+ msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_WRITE;
+ return idx;
+}
+
+/* Ensure that submit has corresponding entry in cmds table for the
+ * target cmdstream buffer:
+ *
+ * Returns TRUE if new cmd added (else FALSE if it was already in
+ * the cmds table)
+ */
+static int get_cmd(struct fd_ringbuffer *ring, struct msm_cmd *target_cmd,
+ uint32_t submit_offset, uint32_t size, uint32_t type)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ struct drm_msm_gem_submit_cmd *cmd;
+ uint32_t i;
+ void *val;
+
+ if (!msm_ring->cmd_table)
+ msm_ring->cmd_table = drmHashCreate();
+
+ /* figure out if we already have a cmd buf.. short-circuit hash
+ * lookup if:
+ * - target cmd has never been added to submit.cmds
+ * - target cmd is not a streaming stateobj (which unlike longer
+ * lived CSO stateobj, is not expected to be reused with multiple
+ * submits)
+ */
+ if (target_cmd->is_appended_to_submit &&
+ !(target_cmd->ring->flags & FD_RINGBUFFER_STREAMING) &&
+ !drmHashLookup(msm_ring->cmd_table, (unsigned long)target_cmd, &val)) {
+ i = VOID2U64(val);
+ cmd = &msm_ring->submit.cmds[i];
+
+ assert(cmd->submit_offset == submit_offset);
+ assert(cmd->size == size);
+ assert(cmd->type == type);
+ assert(msm_ring->submit.bos[cmd->submit_idx].handle ==
+ target_cmd->ring_bo->handle);
+
+ return FALSE;
+ }
+
+ /* create cmd buf if not: */
+ i = APPEND(&msm_ring->submit, cmds);
+ APPEND(msm_ring, cmds);
+ msm_ring->cmds[i] = target_cmd;
+ cmd = &msm_ring->submit.cmds[i];
+ cmd->type = type;
+ cmd->submit_idx = bo2idx(ring, target_cmd->ring_bo, FD_RELOC_READ);
+ cmd->submit_offset = submit_offset;
+ cmd->size = size;
+ cmd->pad = 0;
+
+ target_cmd->is_appended_to_submit = TRUE;
+
+ if (!(target_cmd->ring->flags & FD_RINGBUFFER_STREAMING)) {
+ drmHashInsert(msm_ring->cmd_table, (unsigned long)target_cmd,
+ U642VOID(i));
+ }
+
+ target_cmd->size = size;
+
+ return TRUE;
+}
+
+static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring)
+{
+ struct msm_cmd *cmd = current_cmd(ring);
+ uint8_t *base = fd_bo_map(cmd->ring_bo);
+ return base + to_msm_ringbuffer(ring)->offset;
+}
+
+static void delete_cmds(struct msm_ringbuffer *msm_ring)
+{
+ struct msm_cmd *cmd, *tmp;
+
+ LIST_FOR_EACH_ENTRY_SAFE(cmd, tmp, &msm_ring->cmd_list, list) {
+ ring_cmd_del(cmd);
+ }
+}
+
+static void flush_reset(struct fd_ringbuffer *ring)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ unsigned i;
+
+ for (i = 0; i < msm_ring->nr_bos; i++) {
+ struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]);
+ if (!msm_bo)
+ continue;
+ msm_bo->current_ring_seqno = 0;
+ fd_bo_del(&msm_bo->base);
+ }
+
+ for (i = 0; i < msm_ring->nr_cmds; i++) {
+ struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+
+ if (msm_cmd->ring == ring)
+ continue;
+
+ if (msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT)
+ fd_ringbuffer_del(msm_cmd->ring);
+ }
+
+ msm_ring->submit.nr_cmds = 0;
+ msm_ring->submit.nr_bos = 0;
+ msm_ring->nr_cmds = 0;
+ msm_ring->nr_bos = 0;
+
+ if (msm_ring->bo_table) {
+ drmHashDestroy(msm_ring->bo_table);
+ msm_ring->bo_table = NULL;
+ }
+
+ if (msm_ring->cmd_table) {
+ drmHashDestroy(msm_ring->cmd_table);
+ msm_ring->cmd_table = NULL;
+ }
+
+ if (msm_ring->is_growable) {
+ delete_cmds(msm_ring);
+ } else {
+ /* in old mode, just reset the # of relocs: */
+ current_cmd(ring)->nr_relocs = 0;
+ }
+}
+
+static void finalize_current_cmd(struct fd_ringbuffer *ring, uint32_t *last_start)
+{
+ uint32_t submit_offset, size, type;
+ struct fd_ringbuffer *parent;
+
+ if (ring->parent) {
+ parent = ring->parent;
+ type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
+ } else {
+ parent = ring;
+ type = MSM_SUBMIT_CMD_BUF;
+ }
+
+ submit_offset = offset_bytes(last_start, ring->start);
+ size = offset_bytes(ring->cur, last_start);
+
+ get_cmd(parent, current_cmd(ring), submit_offset, size, type);
+}
+
+static void dump_submit(struct msm_ringbuffer *msm_ring)
+{
+ uint32_t i, j;
+
+ for (i = 0; i < msm_ring->submit.nr_bos; i++) {
+ struct drm_msm_gem_submit_bo *bo = &msm_ring->submit.bos[i];
+ ERROR_MSG(" bos[%d]: handle=%u, flags=%x", i, bo->handle, bo->flags);
+ }
+ for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+ struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
+ struct drm_msm_gem_submit_reloc *relocs = U642VOID(cmd->relocs);
+ ERROR_MSG(" cmd[%d]: type=%u, submit_idx=%u, submit_offset=%u, size=%u",
+ i, cmd->type, cmd->submit_idx, cmd->submit_offset, cmd->size);
+ for (j = 0; j < cmd->nr_relocs; j++) {
+ struct drm_msm_gem_submit_reloc *r = &relocs[j];
+ ERROR_MSG(" reloc[%d]: submit_offset=%u, or=%08x, shift=%d, reloc_idx=%u"
+ ", reloc_offset=%"PRIu64, j, r->submit_offset, r->or, r->shift,
+ r->reloc_idx, r->reloc_offset);
+ }
+ }
+}
+
+static struct drm_msm_gem_submit_reloc *
+handle_stateobj_relocs(struct fd_ringbuffer *parent, struct fd_ringbuffer *stateobj,
+ struct drm_msm_gem_submit_reloc *orig_relocs, unsigned nr_relocs)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(stateobj);
+ struct drm_msm_gem_submit_reloc *relocs = malloc(nr_relocs * sizeof(*relocs));
+ unsigned i;
+
+ for (i = 0; i < nr_relocs; i++) {
+ unsigned idx = orig_relocs[i].reloc_idx;
+ struct fd_bo *bo = msm_ring->bos[idx];
+ unsigned flags = 0;
+
+ if (msm_ring->submit.bos[idx].flags & MSM_SUBMIT_BO_READ)
+ flags |= FD_RELOC_READ;
+ if (msm_ring->submit.bos[idx].flags & MSM_SUBMIT_BO_WRITE)
+ flags |= FD_RELOC_WRITE;
+
+ relocs[i] = orig_relocs[i];
+ relocs[i].reloc_idx = bo2idx(parent, bo, flags);
+ }
+
+ /* stateobj rb's could have reloc's to other stateobj rb's which didn't
+ * get propagated to the parent rb at _emit_reloc_ring() time (because
+ * the parent wasn't known then), so fix that up now:
+ */
+ for (i = 0; i < msm_ring->nr_cmds; i++) {
+ struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+ struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
+
+ if (msm_ring->cmds[i]->ring == stateobj)
+ continue;
+
+ assert(msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT);
+
+ if (get_cmd(parent, msm_cmd, cmd->submit_offset, cmd->size, cmd->type)) {
+ fd_ringbuffer_ref(msm_cmd->ring);
+ }
+ }
+
+ return relocs;
+}
+
+static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start,
+ int in_fence_fd, int *out_fence_fd)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+ struct msm_pipe *msm_pipe = to_msm_pipe(ring->pipe);
+ struct drm_msm_gem_submit req = {
+ .flags = msm_pipe->pipe,
+ .queueid = msm_pipe->queue_id,
+ };
+ uint32_t i;
+ int ret;
+
+ assert(!ring->parent);
+
+ if (in_fence_fd != -1) {
+ req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;
+ req.fence_fd = in_fence_fd;
+ }
+
+ if (out_fence_fd) {
+ req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
+ }
+
+ finalize_current_cmd(ring, last_start);
+
+ /* for each of the cmd's fix up their reloc's: */
+ for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+ struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+ struct drm_msm_gem_submit_reloc *relocs = msm_cmd->relocs;
+ struct drm_msm_gem_submit_cmd *cmd;
+ unsigned nr_relocs = msm_cmd->nr_relocs;
+
+ /* for reusable stateobjs, the reloc table has reloc_idx that
+ * points into it's own private bos table, rather than the global
+ * bos table used for the submit, so we need to add the stateobj's
+ * bos to the global table and construct new relocs table with
+ * corresponding reloc_idx
+ */
+ if (msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT) {
+ relocs = handle_stateobj_relocs(ring, msm_cmd->ring,
+ relocs, nr_relocs);
+ }
+
+ cmd = &msm_ring->submit.cmds[i];
+ cmd->relocs = VOID2U64(relocs);
+ cmd->nr_relocs = nr_relocs;
+ }
+
+ /* needs to be after get_cmd() as that could create bos/cmds table: */
+ req.bos = VOID2U64(msm_ring->submit.bos),
+ req.nr_bos = msm_ring->submit.nr_bos;
+ req.cmds = VOID2U64(msm_ring->submit.cmds),
+ req.nr_cmds = msm_ring->submit.nr_cmds;
+
+ DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
+
+ ret = drmCommandWriteRead(ring->pipe->dev->fd, DRM_MSM_GEM_SUBMIT,
+ &req, sizeof(req));
+ if (ret) {
+ ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
+ dump_submit(msm_ring);
+ } else if (!ret) {
+ /* update timestamp on all rings associated with submit: */
+ for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+ struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+ msm_cmd->ring->last_timestamp = req.fence;
+ }
+
+ if (out_fence_fd) {
+ *out_fence_fd = req.fence_fd;
+ }
+ }
+
+ /* free dynamically constructed stateobj relocs tables: */
+ for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+ struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
+ struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+ if (msm_cmd->ring->flags & FD_RINGBUFFER_OBJECT) {
+ free(U642VOID(cmd->relocs));
+ }
+ }
+
+ flush_reset(ring);
+
+ return ret;
+}
+
+static void msm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size)
+{
+ assert(to_msm_ringbuffer(ring)->is_growable);
+ finalize_current_cmd(ring, ring->last_start);
+ ring_cmd_new(ring, size, 0);
+}
+
+static void msm_ringbuffer_reset(struct fd_ringbuffer *ring)
+{
+ flush_reset(ring);
+}
+
+static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
+ const struct fd_reloc *r)
+{
+ struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring;
+ struct msm_bo *msm_bo = to_msm_bo(r->bo);
+ struct drm_msm_gem_submit_reloc *reloc;
+ struct msm_cmd *cmd = current_cmd(ring);
+ uint32_t idx = APPEND(cmd, relocs);
+ uint32_t addr;
+
+ reloc = &cmd->relocs[idx];
+
+ reloc->reloc_idx = bo2idx(parent, r->bo, r->flags);
+ reloc->reloc_offset = r->offset;
+ reloc->or = r->or;
+ reloc->shift = r->shift;
+ reloc->submit_offset = offset_bytes(ring->cur, ring->start) +
+ to_msm_ringbuffer(ring)->offset;
+
+ addr = msm_bo->presumed;
+ if (reloc->shift < 0)
+ addr >>= -reloc->shift;
+ else
+ addr <<= reloc->shift;
+ (*ring->cur++) = addr | r->or;
+
+ if (ring->pipe->gpu_id >= 500) {
+ struct drm_msm_gem_submit_reloc *reloc_hi;
+
+ /* NOTE: grab reloc_idx *before* APPEND() since that could
+ * realloc() meaning that 'reloc' ptr is no longer valid:
+ */
+ uint32_t reloc_idx = reloc->reloc_idx;
+
+ idx = APPEND(cmd, relocs);
+
+ reloc_hi = &cmd->relocs[idx];
+
+ reloc_hi->reloc_idx = reloc_idx;
+ reloc_hi->reloc_offset = r->offset;
+ reloc_hi->or = r->orhi;
+ reloc_hi->shift = r->shift - 32;
+ reloc_hi->submit_offset = offset_bytes(ring->cur, ring->start) +
+ to_msm_ringbuffer(ring)->offset;
+
+ addr = msm_bo->presumed >> 32;
+ if (reloc_hi->shift < 0)
+ addr >>= -reloc_hi->shift;
+ else
+ addr <<= reloc_hi->shift;
+ (*ring->cur++) = addr | r->orhi;
+ }
+}
+
+static uint32_t msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+ struct fd_ringbuffer *target, uint32_t cmd_idx)
+{
+ struct msm_cmd *cmd = NULL;
+ struct msm_ringbuffer *msm_target = to_msm_ringbuffer(target);
+ uint32_t idx = 0;
+ int added_cmd = FALSE;
+ uint32_t size;
+ uint32_t submit_offset = msm_target->offset;
+
+ LIST_FOR_EACH_ENTRY(cmd, &msm_target->cmd_list, list) {
+ if (idx == cmd_idx)
+ break;
+ idx++;
+ }
+
+ assert(cmd && (idx == cmd_idx));
+
+ if (idx < (msm_target->cmd_count - 1)) {
+ /* All but the last cmd buffer is fully "baked" (ie. already has
+ * done get_cmd() to add it to the cmds table). But in this case,
+ * the size we get is invalid (since it is calculated from the
+ * last cmd buffer):
+ */
+ size = cmd->size;
+ } else {
+ struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring;
+ size = offset_bytes(target->cur, target->start);
+ added_cmd = get_cmd(parent, cmd, submit_offset, size,
+ MSM_SUBMIT_CMD_IB_TARGET_BUF);
+ }
+
+ msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
+ .bo = cmd->ring_bo,
+ .flags = FD_RELOC_READ,
+ .offset = submit_offset,
+ });
+
+ /* Unlike traditional ringbuffers which are deleted as a set (after
+ * being flushed), mesa can't really guarantee that a stateobj isn't
+ * destroyed after emitted but before flush, so we must hold a ref:
+ */
+ if (added_cmd && (target->flags & FD_RINGBUFFER_OBJECT)) {
+ fd_ringbuffer_ref(target);
+ }
+
+ return size;
+}
+
+static uint32_t msm_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
+{
+ return to_msm_ringbuffer(ring)->cmd_count;
+}
+
+static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
+{
+ struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+
+ flush_reset(ring);
+ delete_cmds(msm_ring);
+
+ free(msm_ring->submit.cmds);
+ free(msm_ring->submit.bos);
+ free(msm_ring->bos);
+ free(msm_ring->cmds);
+ free(msm_ring);
+}
+
+static const struct fd_ringbuffer_funcs funcs = {
+ .hostptr = msm_ringbuffer_hostptr,
+ .flush = msm_ringbuffer_flush,
+ .grow = msm_ringbuffer_grow,
+ .reset = msm_ringbuffer_reset,
+ .emit_reloc = msm_ringbuffer_emit_reloc,
+ .emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,
+ .cmd_count = msm_ringbuffer_cmd_count,
+ .destroy = msm_ringbuffer_destroy,
+};
+
+drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
+ uint32_t size, enum fd_ringbuffer_flags flags)
+{
+ struct msm_ringbuffer *msm_ring;
+ struct fd_ringbuffer *ring;
+
+ msm_ring = calloc(1, sizeof(*msm_ring));
+ if (!msm_ring) {
+ ERROR_MSG("allocation failed");
+ return NULL;
+ }
+
+ if (size == 0) {
+ assert(pipe->dev->version >= FD_VERSION_UNLIMITED_CMDS);
+ size = INIT_SIZE;
+ msm_ring->is_growable = TRUE;
+ }
+
+ list_inithead(&msm_ring->cmd_list);
+ msm_ring->seqno = ++to_msm_device(pipe->dev)->ring_cnt;
+
+ ring = &msm_ring->base;
+ atomic_set(&ring->refcnt, 1);
+
+ ring->funcs = &funcs;
+ ring->size = size;
+ ring->pipe = pipe; /* needed in ring_cmd_new() */
+
+ ring_cmd_new(ring, size, flags);
+
+ return ring;
+}
diff --git a/include/drm/README b/include/drm/README
new file mode 100644
index 0000000..ea2320c
--- /dev/null
+++ b/include/drm/README
@@ -0,0 +1,128 @@
+What are these headers ?
+------------------------
+This is the canonical source of drm headers that user space should use for
+communicating with the kernel DRM subsystem.
+
+They flow from the kernel, thus any changes must be merged there first.
+Do _not_ attempt to "fix" these by deviating from the kernel ones !
+
+
+Non-linux platforms - changes/patches
+-------------------------------------
+If your platform has local changes, please send them upstream for inclusion.
+Even if your patches don't get accepted in their current form, devs will
+give you feedback on how to address things properly.
+
+git send-email --subject-prefix="PATCH libdrm" your patches to dri-devel
+mailing list.
+
+Before doing so, please consider the following:
+ - Have the [libdrm vs kernel] headers on your platform deviated ?
+Consider unifying them first.
+
+ - Have you introduced additional ABI that's not available in Linux ?
+Propose it for [Linux kernel] upstream inclusion.
+If that doesn't work out (hopefully it never does), move it to another header
+and/or keep the change(s) local ?
+
+ - Are your changes DRI1/UMS specific ?
+There is virtually no interest/power in keeping those legacy interfaces. They
+are around due to the kernel "thou shalt not break existing user space" rule.
+
+Consider porting the driver to DRI2/KMS - all (almost?) sensible hardware is
+capable of supporting those.
+
+
+Which headers go where ?
+------------------------
+A snipped from the, now removed, Makefile.am used to state:
+
+ XXX airlied says, nothing besides *_drm.h and drm*.h should be necessary.
+ however, r300 and via need their reg headers installed in order to build.
+ better solutions are welcome.
+
+Obviously the r300 and via headers are no longer around ;-)
+
+Reason behind is that the drm headers can be used as a basic communications
+channel with the respective kernel modules. If more advanced functionality is
+required one can pull the specific libdrm_$driver which is free to pull
+additional files from the kernel.
+
+For example: nouveau has nouveau/nvif/*.h while vc4 has vc4/*.h
+
+If your driver is still in prototyping/staging state, consider moving the
+$driver_drm.h into $driver and _not_ installing it. An header providing opaque
+definitions and access [via $driver_drmif.h or similar] would be better fit.
+
+
+When and which headers to update
+--------------------------------
+Ideally all files will be synced (updated) with the latest released kernel on
+each libdrm release. Sadly that's not yet possible since quite a few headers
+differ significantly - see Outdated or Broken Headers section below.
+
+That said, it's up-to the individual developers to sync with newer version
+(from drm-next) as they see fit.
+
+
+When and how to update these files
+----------------------------------
+Note: One should not do _any_ changes to the files apart from the steps below.
+
+In order to update the files do the following:
+ - Switch to a Linux kernel tree/branch which is not rebased.
+ For example: drm-next (https://cgit.freedesktop.org/drm/drm)
+ - Install the headers via `make headers_install' to a separate location.
+ - Copy the drm header[s] + git add + git commit.
+ - Note: Your commit message must include:
+ a) Brief summary on the delta. If there's any change that looks like an
+API/ABI break one _must_ explicitly state why it's safe to do so.
+ b) "Generated using make headers_install."
+ c) "Generated from $tree/branch commit $sha"
+
+
+Outdated or Broken Headers
+--------------------------
+This section contains a list of headers and the respective "issues" they might
+have relative to their kernel equivalent.
+
+Most UMS headers:
+ - Not using fixed size integers - compat ioctls are broken.
+Status: ?
+Promote to fixed size ints, which match the current (32bit) ones.
+
+nouveau_drm.h
+ - Missing macros NOUVEAU_GETPARAM*, NOUVEAU_DRM_HEADER_PATCHLEVEL, structs,
+enums
+Status: Deliberate UABI choice; nouveau hides the exact kernel ABI behind libdrm
+
+r128_drm.h
+ - Broken compat ioctls.
+
+radeon_drm.h
+ - Missing RADEON_TILING_R600_NO_SCANOUT, CIK_TILE_MODE_*, broken UMS ioctls
+ - Both kernel and libdrm: missing padding -
+drm_radeon_gem_{create,{g,s}et_tiling,set_domain} others ?
+Status: ?
+
+savage_drm.h
+ - Renamed ioctls - DRM_IOCTL_SAVAGE_{,BCI}_EVENT_EMIT, compat ioctls are broken.
+Status: ?
+
+sis_drm.h
+ - Borken ioctls + libdrm uses int vs kernel long
+Status: ?
+
+via_drm.h
+ - Borken ioctls - libdrm int vs kernel long
+Status: ?
+
+
+omap_drm.h (living in $TOP/omap)
+ - License mismatch, missing DRM_IOCTL_OMAP_GEM_NEW and related struct
+Status: ?
+
+exynos_drm.h (living in $TOP/exynos)
+ - License mismatch, now using fixed size ints (but not everywhere). Lots of
+new stuff.
+Status: ?
diff --git a/include/drm/amdgpu_drm.h b/include/drm/amdgpu_drm.h
new file mode 100644
index 0000000..4fe35d6
--- /dev/null
+++ b/include/drm/amdgpu_drm.h
@@ -0,0 +1,1067 @@
+/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * 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.
+ *
+ * Authors:
+ * Kevin E. Martin <martin@valinux.com>
+ * Gareth Hughes <gareth@valinux.com>
+ * Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#ifndef __AMDGPU_DRM_H__
+#define __AMDGPU_DRM_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DRM_AMDGPU_GEM_CREATE 0x00
+#define DRM_AMDGPU_GEM_MMAP 0x01
+#define DRM_AMDGPU_CTX 0x02
+#define DRM_AMDGPU_BO_LIST 0x03
+#define DRM_AMDGPU_CS 0x04
+#define DRM_AMDGPU_INFO 0x05
+#define DRM_AMDGPU_GEM_METADATA 0x06
+#define DRM_AMDGPU_GEM_WAIT_IDLE 0x07
+#define DRM_AMDGPU_GEM_VA 0x08
+#define DRM_AMDGPU_WAIT_CS 0x09
+#define DRM_AMDGPU_GEM_OP 0x10
+#define DRM_AMDGPU_GEM_USERPTR 0x11
+#define DRM_AMDGPU_WAIT_FENCES 0x12
+#define DRM_AMDGPU_VM 0x13
+#define DRM_AMDGPU_FENCE_TO_HANDLE 0x14
+#define DRM_AMDGPU_SCHED 0x15
+
+#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
+#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
+#define DRM_IOCTL_AMDGPU_CTX DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx)
+#define DRM_IOCTL_AMDGPU_BO_LIST DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list)
+#define DRM_IOCTL_AMDGPU_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs)
+#define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info)
+#define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata)
+#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle)
+#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, struct drm_amdgpu_gem_va)
+#define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs)
+#define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op)
+#define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr)
+#define DRM_IOCTL_AMDGPU_WAIT_FENCES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences)
+#define DRM_IOCTL_AMDGPU_VM DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm)
+#define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle)
+#define DRM_IOCTL_AMDGPU_SCHED DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_SCHED, union drm_amdgpu_sched)
+
+/**
+ * DOC: memory domains
+ *
+ * %AMDGPU_GEM_DOMAIN_CPU System memory that is not GPU accessible.
+ * Memory in this pool could be swapped out to disk if there is pressure.
+ *
+ * %AMDGPU_GEM_DOMAIN_GTT GPU accessible system memory, mapped into the
+ * GPU's virtual address space via gart. Gart memory linearizes non-contiguous
+ * pages of system memory, allows GPU access system memory in a linezrized
+ * fashion.
+ *
+ * %AMDGPU_GEM_DOMAIN_VRAM Local video memory. For APUs, it is memory
+ * carved out by the BIOS.
+ *
+ * %AMDGPU_GEM_DOMAIN_GDS Global on-chip data storage used to share data
+ * across shader threads.
+ *
+ * %AMDGPU_GEM_DOMAIN_GWS Global wave sync, used to synchronize the
+ * execution of all the waves on a device.
+ *
+ * %AMDGPU_GEM_DOMAIN_OA Ordered append, used by 3D or Compute engines
+ * for appending data.
+ */
+#define AMDGPU_GEM_DOMAIN_CPU 0x1
+#define AMDGPU_GEM_DOMAIN_GTT 0x2
+#define AMDGPU_GEM_DOMAIN_VRAM 0x4
+#define AMDGPU_GEM_DOMAIN_GDS 0x8
+#define AMDGPU_GEM_DOMAIN_GWS 0x10
+#define AMDGPU_GEM_DOMAIN_OA 0x20
+#define AMDGPU_GEM_DOMAIN_MASK (AMDGPU_GEM_DOMAIN_CPU | \
+ AMDGPU_GEM_DOMAIN_GTT | \
+ AMDGPU_GEM_DOMAIN_VRAM | \
+ AMDGPU_GEM_DOMAIN_GDS | \
+ AMDGPU_GEM_DOMAIN_GWS | \
+ AMDGPU_GEM_DOMAIN_OA)
+
+/* Flag that CPU access will be required for the case of VRAM domain */
+#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0)
+/* Flag that CPU access will not work, this VRAM domain is invisible */
+#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1)
+/* Flag that USWC attributes should be used for GTT */
+#define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2)
+/* Flag that the memory should be in VRAM and cleared */
+#define AMDGPU_GEM_CREATE_VRAM_CLEARED (1 << 3)
+/* Flag that create shadow bo(GTT) while allocating vram bo */
+#define AMDGPU_GEM_CREATE_SHADOW (1 << 4)
+/* Flag that allocating the BO should use linear VRAM */
+#define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 5)
+/* Flag that BO is always valid in this VM */
+#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 6)
+/* Flag that BO sharing will be explicitly synchronized */
+#define AMDGPU_GEM_CREATE_EXPLICIT_SYNC (1 << 7)
+/* Flag that indicates allocating MQD gart on GFX9, where the mtype
+ * for the second page onward should be set to NC.
+ */
+#define AMDGPU_GEM_CREATE_MQD_GFX9 (1 << 8)
+/* Flag that BO may contain sensitive data that must be wiped before
+ * releasing the memory
+ */
+#define AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE (1 << 9)
+
+struct drm_amdgpu_gem_create_in {
+ /** the requested memory size */
+ __u64 bo_size;
+ /** physical start_addr alignment in bytes for some HW requirements */
+ __u64 alignment;
+ /** the requested memory domains */
+ __u64 domains;
+ /** allocation flags */
+ __u64 domain_flags;
+};
+
+struct drm_amdgpu_gem_create_out {
+ /** returned GEM object handle */
+ __u32 handle;
+ __u32 _pad;
+};
+
+union drm_amdgpu_gem_create {
+ struct drm_amdgpu_gem_create_in in;
+ struct drm_amdgpu_gem_create_out out;
+};
+
+/** Opcode to create new residency list. */
+#define AMDGPU_BO_LIST_OP_CREATE 0
+/** Opcode to destroy previously created residency list */
+#define AMDGPU_BO_LIST_OP_DESTROY 1
+/** Opcode to update resource information in the list */
+#define AMDGPU_BO_LIST_OP_UPDATE 2
+
+struct drm_amdgpu_bo_list_in {
+ /** Type of operation */
+ __u32 operation;
+ /** Handle of list or 0 if we want to create one */
+ __u32 list_handle;
+ /** Number of BOs in list */
+ __u32 bo_number;
+ /** Size of each element describing BO */
+ __u32 bo_info_size;
+ /** Pointer to array describing BOs */
+ __u64 bo_info_ptr;
+};
+
+struct drm_amdgpu_bo_list_entry {
+ /** Handle of BO */
+ __u32 bo_handle;
+ /** New (if specified) BO priority to be used during migration */
+ __u32 bo_priority;
+};
+
+struct drm_amdgpu_bo_list_out {
+ /** Handle of resource list */
+ __u32 list_handle;
+ __u32 _pad;
+};
+
+union drm_amdgpu_bo_list {
+ struct drm_amdgpu_bo_list_in in;
+ struct drm_amdgpu_bo_list_out out;
+};
+
+/* context related */
+#define AMDGPU_CTX_OP_ALLOC_CTX 1
+#define AMDGPU_CTX_OP_FREE_CTX 2
+#define AMDGPU_CTX_OP_QUERY_STATE 3
+#define AMDGPU_CTX_OP_QUERY_STATE2 4
+
+/* GPU reset status */
+#define AMDGPU_CTX_NO_RESET 0
+/* this the context caused it */
+#define AMDGPU_CTX_GUILTY_RESET 1
+/* some other context caused it */
+#define AMDGPU_CTX_INNOCENT_RESET 2
+/* unknown cause */
+#define AMDGPU_CTX_UNKNOWN_RESET 3
+
+/* indicate gpu reset occured after ctx created */
+#define AMDGPU_CTX_QUERY2_FLAGS_RESET (1<<0)
+/* indicate vram lost occured after ctx created */
+#define AMDGPU_CTX_QUERY2_FLAGS_VRAMLOST (1<<1)
+/* indicate some job from this context once cause gpu hang */
+#define AMDGPU_CTX_QUERY2_FLAGS_GUILTY (1<<2)
+/* indicate some errors are detected by RAS */
+#define AMDGPU_CTX_QUERY2_FLAGS_RAS_CE (1<<3)
+#define AMDGPU_CTX_QUERY2_FLAGS_RAS_UE (1<<4)
+
+/* Context priority level */
+#define AMDGPU_CTX_PRIORITY_UNSET -2048
+#define AMDGPU_CTX_PRIORITY_VERY_LOW -1023
+#define AMDGPU_CTX_PRIORITY_LOW -512
+#define AMDGPU_CTX_PRIORITY_NORMAL 0
+/*
+ * When used in struct drm_amdgpu_ctx_in, a priority above NORMAL requires
+ * CAP_SYS_NICE or DRM_MASTER
+*/
+#define AMDGPU_CTX_PRIORITY_HIGH 512
+#define AMDGPU_CTX_PRIORITY_VERY_HIGH 1023
+
+struct drm_amdgpu_ctx_in {
+ /** AMDGPU_CTX_OP_* */
+ __u32 op;
+ /** For future use, no flags defined so far */
+ __u32 flags;
+ __u32 ctx_id;
+ /** AMDGPU_CTX_PRIORITY_* */
+ __s32 priority;
+};
+
+union drm_amdgpu_ctx_out {
+ struct {
+ __u32 ctx_id;
+ __u32 _pad;
+ } alloc;
+
+ struct {
+ /** For future use, no flags defined so far */
+ __u64 flags;
+ /** Number of resets caused by this context so far. */
+ __u32 hangs;
+ /** Reset status since the last call of the ioctl. */
+ __u32 reset_status;
+ } state;
+};
+
+union drm_amdgpu_ctx {
+ struct drm_amdgpu_ctx_in in;
+ union drm_amdgpu_ctx_out out;
+};
+
+/* vm ioctl */
+#define AMDGPU_VM_OP_RESERVE_VMID 1
+#define AMDGPU_VM_OP_UNRESERVE_VMID 2
+
+struct drm_amdgpu_vm_in {
+ /** AMDGPU_VM_OP_* */
+ __u32 op;
+ __u32 flags;
+};
+
+struct drm_amdgpu_vm_out {
+ /** For future use, no flags defined so far */
+ __u64 flags;
+};
+
+union drm_amdgpu_vm {
+ struct drm_amdgpu_vm_in in;
+ struct drm_amdgpu_vm_out out;
+};
+
+/* sched ioctl */
+#define AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE 1
+#define AMDGPU_SCHED_OP_CONTEXT_PRIORITY_OVERRIDE 2
+
+struct drm_amdgpu_sched_in {
+ /* AMDGPU_SCHED_OP_* */
+ __u32 op;
+ __u32 fd;
+ /** AMDGPU_CTX_PRIORITY_* */
+ __s32 priority;
+ __u32 ctx_id;
+};
+
+union drm_amdgpu_sched {
+ struct drm_amdgpu_sched_in in;
+};
+
+/*
+ * This is not a reliable API and you should expect it to fail for any
+ * number of reasons and have fallback path that do not use userptr to
+ * perform any operation.
+ */
+#define AMDGPU_GEM_USERPTR_READONLY (1 << 0)
+#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1)
+#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2)
+#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3)
+
+struct drm_amdgpu_gem_userptr {
+ __u64 addr;
+ __u64 size;
+ /* AMDGPU_GEM_USERPTR_* */
+ __u32 flags;
+ /* Resulting GEM handle */
+ __u32 handle;
+};
+
+/* SI-CI-VI: */
+/* same meaning as the GB_TILE_MODE and GL_MACRO_TILE_MODE fields */
+#define AMDGPU_TILING_ARRAY_MODE_SHIFT 0
+#define AMDGPU_TILING_ARRAY_MODE_MASK 0xf
+#define AMDGPU_TILING_PIPE_CONFIG_SHIFT 4
+#define AMDGPU_TILING_PIPE_CONFIG_MASK 0x1f
+#define AMDGPU_TILING_TILE_SPLIT_SHIFT 9
+#define AMDGPU_TILING_TILE_SPLIT_MASK 0x7
+#define AMDGPU_TILING_MICRO_TILE_MODE_SHIFT 12
+#define AMDGPU_TILING_MICRO_TILE_MODE_MASK 0x7
+#define AMDGPU_TILING_BANK_WIDTH_SHIFT 15
+#define AMDGPU_TILING_BANK_WIDTH_MASK 0x3
+#define AMDGPU_TILING_BANK_HEIGHT_SHIFT 17
+#define AMDGPU_TILING_BANK_HEIGHT_MASK 0x3
+#define AMDGPU_TILING_MACRO_TILE_ASPECT_SHIFT 19
+#define AMDGPU_TILING_MACRO_TILE_ASPECT_MASK 0x3
+#define AMDGPU_TILING_NUM_BANKS_SHIFT 21
+#define AMDGPU_TILING_NUM_BANKS_MASK 0x3
+
+/* GFX9 and later: */
+#define AMDGPU_TILING_SWIZZLE_MODE_SHIFT 0
+#define AMDGPU_TILING_SWIZZLE_MODE_MASK 0x1f
+#define AMDGPU_TILING_DCC_OFFSET_256B_SHIFT 5
+#define AMDGPU_TILING_DCC_OFFSET_256B_MASK 0xFFFFFF
+#define AMDGPU_TILING_DCC_PITCH_MAX_SHIFT 29
+#define AMDGPU_TILING_DCC_PITCH_MAX_MASK 0x3FFF
+#define AMDGPU_TILING_DCC_INDEPENDENT_64B_SHIFT 43
+#define AMDGPU_TILING_DCC_INDEPENDENT_64B_MASK 0x1
+
+/* Set/Get helpers for tiling flags. */
+#define AMDGPU_TILING_SET(field, value) \
+ (((__u64)(value) & AMDGPU_TILING_##field##_MASK) << AMDGPU_TILING_##field##_SHIFT)
+#define AMDGPU_TILING_GET(value, field) \
+ (((__u64)(value) >> AMDGPU_TILING_##field##_SHIFT) & AMDGPU_TILING_##field##_MASK)
+
+#define AMDGPU_GEM_METADATA_OP_SET_METADATA 1
+#define AMDGPU_GEM_METADATA_OP_GET_METADATA 2
+
+/** The same structure is shared for input/output */
+struct drm_amdgpu_gem_metadata {
+ /** GEM Object handle */
+ __u32 handle;
+ /** Do we want get or set metadata */
+ __u32 op;
+ struct {
+ /** For future use, no flags defined so far */
+ __u64 flags;
+ /** family specific tiling info */
+ __u64 tiling_info;
+ __u32 data_size_bytes;
+ __u32 data[64];
+ } data;
+};
+
+struct drm_amdgpu_gem_mmap_in {
+ /** the GEM object handle */
+ __u32 handle;
+ __u32 _pad;
+};
+
+struct drm_amdgpu_gem_mmap_out {
+ /** mmap offset from the vma offset manager */
+ __u64 addr_ptr;
+};
+
+union drm_amdgpu_gem_mmap {
+ struct drm_amdgpu_gem_mmap_in in;
+ struct drm_amdgpu_gem_mmap_out out;
+};
+
+struct drm_amdgpu_gem_wait_idle_in {
+ /** GEM object handle */
+ __u32 handle;
+ /** For future use, no flags defined so far */
+ __u32 flags;
+ /** Absolute timeout to wait */
+ __u64 timeout;
+};
+
+struct drm_amdgpu_gem_wait_idle_out {
+ /** BO status: 0 - BO is idle, 1 - BO is busy */
+ __u32 status;
+ /** Returned current memory domain */
+ __u32 domain;
+};
+
+union drm_amdgpu_gem_wait_idle {
+ struct drm_amdgpu_gem_wait_idle_in in;
+ struct drm_amdgpu_gem_wait_idle_out out;
+};
+
+struct drm_amdgpu_wait_cs_in {
+ /* Command submission handle
+ * handle equals 0 means none to wait for
+ * handle equals ~0ull means wait for the latest sequence number
+ */
+ __u64 handle;
+ /** Absolute timeout to wait */
+ __u64 timeout;
+ __u32 ip_type;
+ __u32 ip_instance;
+ __u32 ring;
+ __u32 ctx_id;
+};
+
+struct drm_amdgpu_wait_cs_out {
+ /** CS status: 0 - CS completed, 1 - CS still busy */
+ __u64 status;
+};
+
+union drm_amdgpu_wait_cs {
+ struct drm_amdgpu_wait_cs_in in;
+ struct drm_amdgpu_wait_cs_out out;
+};
+
+struct drm_amdgpu_fence {
+ __u32 ctx_id;
+ __u32 ip_type;
+ __u32 ip_instance;
+ __u32 ring;
+ __u64 seq_no;
+};
+
+struct drm_amdgpu_wait_fences_in {
+ /** This points to uint64_t * which points to fences */
+ __u64 fences;
+ __u32 fence_count;
+ __u32 wait_all;
+ __u64 timeout_ns;
+};
+
+struct drm_amdgpu_wait_fences_out {
+ __u32 status;
+ __u32 first_signaled;
+};
+
+union drm_amdgpu_wait_fences {
+ struct drm_amdgpu_wait_fences_in in;
+ struct drm_amdgpu_wait_fences_out out;
+};
+
+#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0
+#define AMDGPU_GEM_OP_SET_PLACEMENT 1
+
+/* Sets or returns a value associated with a buffer. */
+struct drm_amdgpu_gem_op {
+ /** GEM object handle */
+ __u32 handle;
+ /** AMDGPU_GEM_OP_* */
+ __u32 op;
+ /** Input or return value */
+ __u64 value;
+};
+
+#define AMDGPU_VA_OP_MAP 1
+#define AMDGPU_VA_OP_UNMAP 2
+#define AMDGPU_VA_OP_CLEAR 3
+#define AMDGPU_VA_OP_REPLACE 4
+
+/* Delay the page table update till the next CS */
+#define AMDGPU_VM_DELAY_UPDATE (1 << 0)
+
+/* Mapping flags */
+/* readable mapping */
+#define AMDGPU_VM_PAGE_READABLE (1 << 1)
+/* writable mapping */
+#define AMDGPU_VM_PAGE_WRITEABLE (1 << 2)
+/* executable mapping, new for VI */
+#define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3)
+/* partially resident texture */
+#define AMDGPU_VM_PAGE_PRT (1 << 4)
+/* MTYPE flags use bit 5 to 8 */
+#define AMDGPU_VM_MTYPE_MASK (0xf << 5)
+/* Default MTYPE. Pre-AI must use this. Recommended for newer ASICs. */
+#define AMDGPU_VM_MTYPE_DEFAULT (0 << 5)
+/* Use NC MTYPE instead of default MTYPE */
+#define AMDGPU_VM_MTYPE_NC (1 << 5)
+/* Use WC MTYPE instead of default MTYPE */
+#define AMDGPU_VM_MTYPE_WC (2 << 5)
+/* Use CC MTYPE instead of default MTYPE */
+#define AMDGPU_VM_MTYPE_CC (3 << 5)
+/* Use UC MTYPE instead of default MTYPE */
+#define AMDGPU_VM_MTYPE_UC (4 << 5)
+
+struct drm_amdgpu_gem_va {
+ /** GEM object handle */
+ __u32 handle;
+ __u32 _pad;
+ /** AMDGPU_VA_OP_* */
+ __u32 operation;
+ /** AMDGPU_VM_PAGE_* */
+ __u32 flags;
+ /** va address to assign . Must be correctly aligned.*/
+ __u64 va_address;
+ /** Specify offset inside of BO to assign. Must be correctly aligned.*/
+ __u64 offset_in_bo;
+ /** Specify mapping size. Must be correctly aligned. */
+ __u64 map_size;
+};
+
+#define AMDGPU_HW_IP_GFX 0
+#define AMDGPU_HW_IP_COMPUTE 1
+#define AMDGPU_HW_IP_DMA 2
+#define AMDGPU_HW_IP_UVD 3
+#define AMDGPU_HW_IP_VCE 4
+#define AMDGPU_HW_IP_UVD_ENC 5
+#define AMDGPU_HW_IP_VCN_DEC 6
+#define AMDGPU_HW_IP_VCN_ENC 7
+#define AMDGPU_HW_IP_VCN_JPEG 8
+#define AMDGPU_HW_IP_NUM 9
+
+#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1
+
+#define AMDGPU_CHUNK_ID_IB 0x01
+#define AMDGPU_CHUNK_ID_FENCE 0x02
+#define AMDGPU_CHUNK_ID_DEPENDENCIES 0x03
+#define AMDGPU_CHUNK_ID_SYNCOBJ_IN 0x04
+#define AMDGPU_CHUNK_ID_SYNCOBJ_OUT 0x05
+#define AMDGPU_CHUNK_ID_BO_HANDLES 0x06
+#define AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES 0x07
+#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT 0x08
+#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL 0x09
+
+struct drm_amdgpu_cs_chunk {
+ __u32 chunk_id;
+ __u32 length_dw;
+ __u64 chunk_data;
+};
+
+struct drm_amdgpu_cs_in {
+ /** Rendering context id */
+ __u32 ctx_id;
+ /** Handle of resource list associated with CS */
+ __u32 bo_list_handle;
+ __u32 num_chunks;
+ __u32 _pad;
+ /** this points to __u64 * which point to cs chunks */
+ __u64 chunks;
+};
+
+struct drm_amdgpu_cs_out {
+ __u64 handle;
+};
+
+union drm_amdgpu_cs {
+ struct drm_amdgpu_cs_in in;
+ struct drm_amdgpu_cs_out out;
+};
+
+/* Specify flags to be used for IB */
+
+/* This IB should be submitted to CE */
+#define AMDGPU_IB_FLAG_CE (1<<0)
+
+/* Preamble flag, which means the IB could be dropped if no context switch */
+#define AMDGPU_IB_FLAG_PREAMBLE (1<<1)
+
+/* Preempt flag, IB should set Pre_enb bit if PREEMPT flag detected */
+#define AMDGPU_IB_FLAG_PREEMPT (1<<2)
+
+/* The IB fence should do the L2 writeback but not invalidate any shader
+ * caches (L2/vL1/sL1/I$). */
+#define AMDGPU_IB_FLAG_TC_WB_NOT_INVALIDATE (1 << 3)
+
+/* Set GDS_COMPUTE_MAX_WAVE_ID = DEFAULT before PACKET3_INDIRECT_BUFFER.
+ * This will reset wave ID counters for the IB.
+ */
+#define AMDGPU_IB_FLAG_RESET_GDS_MAX_WAVE_ID (1 << 4)
+
+struct drm_amdgpu_cs_chunk_ib {
+ __u32 _pad;
+ /** AMDGPU_IB_FLAG_* */
+ __u32 flags;
+ /** Virtual address to begin IB execution */
+ __u64 va_start;
+ /** Size of submission */
+ __u32 ib_bytes;
+ /** HW IP to submit to */
+ __u32 ip_type;
+ /** HW IP index of the same type to submit to */
+ __u32 ip_instance;
+ /** Ring index to submit to */
+ __u32 ring;
+};
+
+struct drm_amdgpu_cs_chunk_dep {
+ __u32 ip_type;
+ __u32 ip_instance;
+ __u32 ring;
+ __u32 ctx_id;
+ __u64 handle;
+};
+
+struct drm_amdgpu_cs_chunk_fence {
+ __u32 handle;
+ __u32 offset;
+};
+
+struct drm_amdgpu_cs_chunk_sem {
+ __u32 handle;
+};
+
+struct drm_amdgpu_cs_chunk_syncobj {
+ __u32 handle;
+ __u32 flags;
+ __u64 point;
+};
+
+#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ 0
+#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD 1
+#define AMDGPU_FENCE_TO_HANDLE_GET_SYNC_FILE_FD 2
+
+union drm_amdgpu_fence_to_handle {
+ struct {
+ struct drm_amdgpu_fence fence;
+ __u32 what;
+ __u32 pad;
+ } in;
+ struct {
+ __u32 handle;
+ } out;
+};
+
+struct drm_amdgpu_cs_chunk_data {
+ union {
+ struct drm_amdgpu_cs_chunk_ib ib_data;
+ struct drm_amdgpu_cs_chunk_fence fence_data;
+ };
+};
+
+/**
+ * Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU
+ *
+ */
+#define AMDGPU_IDS_FLAGS_FUSION 0x1
+#define AMDGPU_IDS_FLAGS_PREEMPTION 0x2
+
+/* indicate if acceleration can be working */
+#define AMDGPU_INFO_ACCEL_WORKING 0x00
+/* get the crtc_id from the mode object id? */
+#define AMDGPU_INFO_CRTC_FROM_ID 0x01
+/* query hw IP info */
+#define AMDGPU_INFO_HW_IP_INFO 0x02
+/* query hw IP instance count for the specified type */
+#define AMDGPU_INFO_HW_IP_COUNT 0x03
+/* timestamp for GL_ARB_timer_query */
+#define AMDGPU_INFO_TIMESTAMP 0x05
+/* Query the firmware version */
+#define AMDGPU_INFO_FW_VERSION 0x0e
+ /* Subquery id: Query VCE firmware version */
+ #define AMDGPU_INFO_FW_VCE 0x1
+ /* Subquery id: Query UVD firmware version */
+ #define AMDGPU_INFO_FW_UVD 0x2
+ /* Subquery id: Query GMC firmware version */
+ #define AMDGPU_INFO_FW_GMC 0x03
+ /* Subquery id: Query GFX ME firmware version */
+ #define AMDGPU_INFO_FW_GFX_ME 0x04
+ /* Subquery id: Query GFX PFP firmware version */
+ #define AMDGPU_INFO_FW_GFX_PFP 0x05
+ /* Subquery id: Query GFX CE firmware version */
+ #define AMDGPU_INFO_FW_GFX_CE 0x06
+ /* Subquery id: Query GFX RLC firmware version */
+ #define AMDGPU_INFO_FW_GFX_RLC 0x07
+ /* Subquery id: Query GFX MEC firmware version */
+ #define AMDGPU_INFO_FW_GFX_MEC 0x08
+ /* Subquery id: Query SMC firmware version */
+ #define AMDGPU_INFO_FW_SMC 0x0a
+ /* Subquery id: Query SDMA firmware version */
+ #define AMDGPU_INFO_FW_SDMA 0x0b
+ /* Subquery id: Query PSP SOS firmware version */
+ #define AMDGPU_INFO_FW_SOS 0x0c
+ /* Subquery id: Query PSP ASD firmware version */
+ #define AMDGPU_INFO_FW_ASD 0x0d
+ /* Subquery id: Query VCN firmware version */
+ #define AMDGPU_INFO_FW_VCN 0x0e
+ /* Subquery id: Query GFX RLC SRLC firmware version */
+ #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_CNTL 0x0f
+ /* Subquery id: Query GFX RLC SRLG firmware version */
+ #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_GPM_MEM 0x10
+ /* Subquery id: Query GFX RLC SRLS firmware version */
+ #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_SRM_MEM 0x11
+ /* Subquery id: Query DMCU firmware version */
+ #define AMDGPU_INFO_FW_DMCU 0x12
+ #define AMDGPU_INFO_FW_TA 0x13
+/* number of bytes moved for TTM migration */
+#define AMDGPU_INFO_NUM_BYTES_MOVED 0x0f
+/* the used VRAM size */
+#define AMDGPU_INFO_VRAM_USAGE 0x10
+/* the used GTT size */
+#define AMDGPU_INFO_GTT_USAGE 0x11
+/* Information about GDS, etc. resource configuration */
+#define AMDGPU_INFO_GDS_CONFIG 0x13
+/* Query information about VRAM and GTT domains */
+#define AMDGPU_INFO_VRAM_GTT 0x14
+/* Query information about register in MMR address space*/
+#define AMDGPU_INFO_READ_MMR_REG 0x15
+/* Query information about device: rev id, family, etc. */
+#define AMDGPU_INFO_DEV_INFO 0x16
+/* visible vram usage */
+#define AMDGPU_INFO_VIS_VRAM_USAGE 0x17
+/* number of TTM buffer evictions */
+#define AMDGPU_INFO_NUM_EVICTIONS 0x18
+/* Query memory about VRAM and GTT domains */
+#define AMDGPU_INFO_MEMORY 0x19
+/* Query vce clock table */
+#define AMDGPU_INFO_VCE_CLOCK_TABLE 0x1A
+/* Query vbios related information */
+#define AMDGPU_INFO_VBIOS 0x1B
+ /* Subquery id: Query vbios size */
+ #define AMDGPU_INFO_VBIOS_SIZE 0x1
+ /* Subquery id: Query vbios image */
+ #define AMDGPU_INFO_VBIOS_IMAGE 0x2
+/* Query UVD handles */
+#define AMDGPU_INFO_NUM_HANDLES 0x1C
+/* Query sensor related information */
+#define AMDGPU_INFO_SENSOR 0x1D
+ /* Subquery id: Query GPU shader clock */
+ #define AMDGPU_INFO_SENSOR_GFX_SCLK 0x1
+ /* Subquery id: Query GPU memory clock */
+ #define AMDGPU_INFO_SENSOR_GFX_MCLK 0x2
+ /* Subquery id: Query GPU temperature */
+ #define AMDGPU_INFO_SENSOR_GPU_TEMP 0x3
+ /* Subquery id: Query GPU load */
+ #define AMDGPU_INFO_SENSOR_GPU_LOAD 0x4
+ /* Subquery id: Query average GPU power */
+ #define AMDGPU_INFO_SENSOR_GPU_AVG_POWER 0x5
+ /* Subquery id: Query northbridge voltage */
+ #define AMDGPU_INFO_SENSOR_VDDNB 0x6
+ /* Subquery id: Query graphics voltage */
+ #define AMDGPU_INFO_SENSOR_VDDGFX 0x7
+ /* Subquery id: Query GPU stable pstate shader clock */
+ #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_SCLK 0x8
+ /* Subquery id: Query GPU stable pstate memory clock */
+ #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_MCLK 0x9
+/* Number of VRAM page faults on CPU access. */
+#define AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS 0x1E
+#define AMDGPU_INFO_VRAM_LOST_COUNTER 0x1F
+/* query ras mask of enabled features*/
+#define AMDGPU_INFO_RAS_ENABLED_FEATURES 0x20
+
+/* RAS MASK: UMC (VRAM) */
+#define AMDGPU_INFO_RAS_ENABLED_UMC (1 << 0)
+/* RAS MASK: SDMA */
+#define AMDGPU_INFO_RAS_ENABLED_SDMA (1 << 1)
+/* RAS MASK: GFX */
+#define AMDGPU_INFO_RAS_ENABLED_GFX (1 << 2)
+/* RAS MASK: MMHUB */
+#define AMDGPU_INFO_RAS_ENABLED_MMHUB (1 << 3)
+/* RAS MASK: ATHUB */
+#define AMDGPU_INFO_RAS_ENABLED_ATHUB (1 << 4)
+/* RAS MASK: PCIE */
+#define AMDGPU_INFO_RAS_ENABLED_PCIE (1 << 5)
+/* RAS MASK: HDP */
+#define AMDGPU_INFO_RAS_ENABLED_HDP (1 << 6)
+/* RAS MASK: XGMI */
+#define AMDGPU_INFO_RAS_ENABLED_XGMI (1 << 7)
+/* RAS MASK: DF */
+#define AMDGPU_INFO_RAS_ENABLED_DF (1 << 8)
+/* RAS MASK: SMN */
+#define AMDGPU_INFO_RAS_ENABLED_SMN (1 << 9)
+/* RAS MASK: SEM */
+#define AMDGPU_INFO_RAS_ENABLED_SEM (1 << 10)
+/* RAS MASK: MP0 */
+#define AMDGPU_INFO_RAS_ENABLED_MP0 (1 << 11)
+/* RAS MASK: MP1 */
+#define AMDGPU_INFO_RAS_ENABLED_MP1 (1 << 12)
+/* RAS MASK: FUSE */
+#define AMDGPU_INFO_RAS_ENABLED_FUSE (1 << 13)
+
+#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0
+#define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff
+#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT 8
+#define AMDGPU_INFO_MMR_SH_INDEX_MASK 0xff
+
+struct drm_amdgpu_query_fw {
+ /** AMDGPU_INFO_FW_* */
+ __u32 fw_type;
+ /**
+ * Index of the IP if there are more IPs of
+ * the same type.
+ */
+ __u32 ip_instance;
+ /**
+ * Index of the engine. Whether this is used depends
+ * on the firmware type. (e.g. MEC, SDMA)
+ */
+ __u32 index;
+ __u32 _pad;
+};
+
+/* Input structure for the INFO ioctl */
+struct drm_amdgpu_info {
+ /* Where the return value will be stored */
+ __u64 return_pointer;
+ /* The size of the return value. Just like "size" in "snprintf",
+ * it limits how many bytes the kernel can write. */
+ __u32 return_size;
+ /* The query request id. */
+ __u32 query;
+
+ union {
+ struct {
+ __u32 id;
+ __u32 _pad;
+ } mode_crtc;
+
+ struct {
+ /** AMDGPU_HW_IP_* */
+ __u32 type;
+ /**
+ * Index of the IP if there are more IPs of the same
+ * type. Ignored by AMDGPU_INFO_HW_IP_COUNT.
+ */
+ __u32 ip_instance;
+ } query_hw_ip;
+
+ struct {
+ __u32 dword_offset;
+ /** number of registers to read */
+ __u32 count;
+ __u32 instance;
+ /** For future use, no flags defined so far */
+ __u32 flags;
+ } read_mmr_reg;
+
+ struct drm_amdgpu_query_fw query_fw;
+
+ struct {
+ __u32 type;
+ __u32 offset;
+ } vbios_info;
+
+ struct {
+ __u32 type;
+ } sensor_info;
+ };
+};
+
+struct drm_amdgpu_info_gds {
+ /** GDS GFX partition size */
+ __u32 gds_gfx_partition_size;
+ /** GDS compute partition size */
+ __u32 compute_partition_size;
+ /** total GDS memory size */
+ __u32 gds_total_size;
+ /** GWS size per GFX partition */
+ __u32 gws_per_gfx_partition;
+ /** GSW size per compute partition */
+ __u32 gws_per_compute_partition;
+ /** OA size per GFX partition */
+ __u32 oa_per_gfx_partition;
+ /** OA size per compute partition */
+ __u32 oa_per_compute_partition;
+ __u32 _pad;
+};
+
+struct drm_amdgpu_info_vram_gtt {
+ __u64 vram_size;
+ __u64 vram_cpu_accessible_size;
+ __u64 gtt_size;
+};
+
+struct drm_amdgpu_heap_info {
+ /** max. physical memory */
+ __u64 total_heap_size;
+
+ /** Theoretical max. available memory in the given heap */
+ __u64 usable_heap_size;
+
+ /**
+ * Number of bytes allocated in the heap. This includes all processes
+ * and private allocations in the kernel. It changes when new buffers
+ * are allocated, freed, and moved. It cannot be larger than
+ * heap_size.
+ */
+ __u64 heap_usage;
+
+ /**
+ * Theoretical possible max. size of buffer which
+ * could be allocated in the given heap
+ */
+ __u64 max_allocation;
+};
+
+struct drm_amdgpu_memory_info {
+ struct drm_amdgpu_heap_info vram;
+ struct drm_amdgpu_heap_info cpu_accessible_vram;
+ struct drm_amdgpu_heap_info gtt;
+};
+
+struct drm_amdgpu_info_firmware {
+ __u32 ver;
+ __u32 feature;
+};
+
+#define AMDGPU_VRAM_TYPE_UNKNOWN 0
+#define AMDGPU_VRAM_TYPE_GDDR1 1
+#define AMDGPU_VRAM_TYPE_DDR2 2
+#define AMDGPU_VRAM_TYPE_GDDR3 3
+#define AMDGPU_VRAM_TYPE_GDDR4 4
+#define AMDGPU_VRAM_TYPE_GDDR5 5
+#define AMDGPU_VRAM_TYPE_HBM 6
+#define AMDGPU_VRAM_TYPE_DDR3 7
+#define AMDGPU_VRAM_TYPE_DDR4 8
+#define AMDGPU_VRAM_TYPE_GDDR6 9
+
+struct drm_amdgpu_info_device {
+ /** PCI Device ID */
+ __u32 device_id;
+ /** Internal chip revision: A0, A1, etc.) */
+ __u32 chip_rev;
+ __u32 external_rev;
+ /** Revision id in PCI Config space */
+ __u32 pci_rev;
+ __u32 family;
+ __u32 num_shader_engines;
+ __u32 num_shader_arrays_per_engine;
+ /* in KHz */
+ __u32 gpu_counter_freq;
+ __u64 max_engine_clock;
+ __u64 max_memory_clock;
+ /* cu information */
+ __u32 cu_active_number;
+ /* NOTE: cu_ao_mask is INVALID, DON'T use it */
+ __u32 cu_ao_mask;
+ __u32 cu_bitmap[4][4];
+ /** Render backend pipe mask. One render backend is CB+DB. */
+ __u32 enabled_rb_pipes_mask;
+ __u32 num_rb_pipes;
+ __u32 num_hw_gfx_contexts;
+ __u32 _pad;
+ __u64 ids_flags;
+ /** Starting virtual address for UMDs. */
+ __u64 virtual_address_offset;
+ /** The maximum virtual address */
+ __u64 virtual_address_max;
+ /** Required alignment of virtual addresses. */
+ __u32 virtual_address_alignment;
+ /** Page table entry - fragment size */
+ __u32 pte_fragment_size;
+ __u32 gart_page_size;
+ /** constant engine ram size*/
+ __u32 ce_ram_size;
+ /** video memory type info*/
+ __u32 vram_type;
+ /** video memory bit width*/
+ __u32 vram_bit_width;
+ /* vce harvesting instance */
+ __u32 vce_harvest_config;
+ /* gfx double offchip LDS buffers */
+ __u32 gc_double_offchip_lds_buf;
+ /* NGG Primitive Buffer */
+ __u64 prim_buf_gpu_addr;
+ /* NGG Position Buffer */
+ __u64 pos_buf_gpu_addr;
+ /* NGG Control Sideband */
+ __u64 cntl_sb_buf_gpu_addr;
+ /* NGG Parameter Cache */
+ __u64 param_buf_gpu_addr;
+ __u32 prim_buf_size;
+ __u32 pos_buf_size;
+ __u32 cntl_sb_buf_size;
+ __u32 param_buf_size;
+ /* wavefront size*/
+ __u32 wave_front_size;
+ /* shader visible vgprs*/
+ __u32 num_shader_visible_vgprs;
+ /* CU per shader array*/
+ __u32 num_cu_per_sh;
+ /* number of tcc blocks*/
+ __u32 num_tcc_blocks;
+ /* gs vgt table depth*/
+ __u32 gs_vgt_table_depth;
+ /* gs primitive buffer depth*/
+ __u32 gs_prim_buffer_depth;
+ /* max gs wavefront per vgt*/
+ __u32 max_gs_waves_per_vgt;
+ __u32 _pad1;
+ /* always on cu bitmap */
+ __u32 cu_ao_bitmap[4][4];
+ /** Starting high virtual address for UMDs. */
+ __u64 high_va_offset;
+ /** The maximum high virtual address */
+ __u64 high_va_max;
+ /* gfx10 pa_sc_tile_steering_override */
+ __u32 pa_sc_tile_steering_override;
+ /* disabled TCCs */
+ __u64 tcc_disabled_mask;
+};
+
+struct drm_amdgpu_info_hw_ip {
+ /** Version of h/w IP */
+ __u32 hw_ip_version_major;
+ __u32 hw_ip_version_minor;
+ /** Capabilities */
+ __u64 capabilities_flags;
+ /** command buffer address start alignment*/
+ __u32 ib_start_alignment;
+ /** command buffer size alignment*/
+ __u32 ib_size_alignment;
+ /** Bitmask of available rings. Bit 0 means ring 0, etc. */
+ __u32 available_rings;
+ __u32 _pad;
+};
+
+struct drm_amdgpu_info_num_handles {
+ /** Max handles as supported by firmware for UVD */
+ __u32 uvd_max_handles;
+ /** Handles currently in use for UVD */
+ __u32 uvd_used_handles;
+};
+
+#define AMDGPU_VCE_CLOCK_TABLE_ENTRIES 6
+
+struct drm_amdgpu_info_vce_clock_table_entry {
+ /** System clock */
+ __u32 sclk;
+ /** Memory clock */
+ __u32 mclk;
+ /** VCE clock */
+ __u32 eclk;
+ __u32 pad;
+};
+
+struct drm_amdgpu_info_vce_clock_table {
+ struct drm_amdgpu_info_vce_clock_table_entry entries[AMDGPU_VCE_CLOCK_TABLE_ENTRIES];
+ __u32 num_valid_entries;
+ __u32 pad;
+};
+
+/*
+ * Supported GPU families
+ */
+#define AMDGPU_FAMILY_UNKNOWN 0
+#define AMDGPU_FAMILY_SI 110 /* Hainan, Oland, Verde, Pitcairn, Tahiti */
+#define AMDGPU_FAMILY_CI 120 /* Bonaire, Hawaii */
+#define AMDGPU_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */
+#define AMDGPU_FAMILY_VI 130 /* Iceland, Tonga */
+#define AMDGPU_FAMILY_CZ 135 /* Carrizo, Stoney */
+#define AMDGPU_FAMILY_AI 141 /* Vega10 */
+#define AMDGPU_FAMILY_RV 142 /* Raven */
+#define AMDGPU_FAMILY_NV 143 /* Navi10 */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/include/drm/drm.h b/include/drm/drm.h
new file mode 100644
index 0000000..c7fd2a3
--- /dev/null
+++ b/include/drm/drm.h
@@ -0,0 +1,1045 @@
+/**
+ * \file drm.h
+ * Header for the Direct Rendering Manager
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ * \par Acknowledgments:
+ * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic \c cmpxchg.
+ */
+
+/*
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, 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
+ * 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
+ * VA LINUX SYSTEMS AND/OR ITS 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 _DRM_H_
+#define _DRM_H_
+
+#if defined(__linux__)
+
+#include <linux/types.h>
+#include <asm/ioctl.h>
+typedef unsigned int drm_handle_t;
+
+#else /* One of the BSDs */
+
+#include <stdint.h>
+#include <sys/ioccom.h>
+#include <sys/types.h>
+typedef int8_t __s8;
+typedef uint8_t __u8;
+typedef int16_t __s16;
+typedef uint16_t __u16;
+typedef int32_t __s32;
+typedef uint32_t __u32;
+typedef int64_t __s64;
+typedef uint64_t __u64;
+typedef size_t __kernel_size_t;
+typedef unsigned long drm_handle_t;
+
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */
+#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */
+#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */
+#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */
+
+#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */
+#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */
+#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD)
+#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT)
+#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
+
+typedef unsigned int drm_context_t;
+typedef unsigned int drm_drawable_t;
+typedef unsigned int drm_magic_t;
+
+/**
+ * Cliprect.
+ *
+ * \warning: If you change this structure, make sure you change
+ * XF86DRIClipRectRec in the server as well
+ *
+ * \note KW: Actually it's illegal to change either for
+ * backwards-compatibility reasons.
+ */
+struct drm_clip_rect {
+ unsigned short x1;
+ unsigned short y1;
+ unsigned short x2;
+ unsigned short y2;
+};
+
+/**
+ * Drawable information.
+ */
+struct drm_drawable_info {
+ unsigned int num_rects;
+ struct drm_clip_rect *rects;
+};
+
+/**
+ * Texture region,
+ */
+struct drm_tex_region {
+ unsigned char next;
+ unsigned char prev;
+ unsigned char in_use;
+ unsigned char padding;
+ unsigned int age;
+};
+
+/**
+ * Hardware lock.
+ *
+ * The lock structure is a simple cache-line aligned integer. To avoid
+ * processor bus contention on a multiprocessor system, there should not be any
+ * other data stored in the same cache line.
+ */
+struct drm_hw_lock {
+ __volatile__ unsigned int lock; /**< lock variable */
+ char padding[60]; /**< Pad to cache line */
+};
+
+/**
+ * DRM_IOCTL_VERSION ioctl argument type.
+ *
+ * \sa drmGetVersion().
+ */
+struct drm_version {
+ int version_major; /**< Major version */
+ int version_minor; /**< Minor version */