diff options
Diffstat (limited to 'common/recipes-kernel/linux/files/0174-drm-amdgpu-add-core-driver-v4.patch')
-rw-r--r-- | common/recipes-kernel/linux/files/0174-drm-amdgpu-add-core-driver-v4.patch | 34205 |
1 files changed, 0 insertions, 34205 deletions
diff --git a/common/recipes-kernel/linux/files/0174-drm-amdgpu-add-core-driver-v4.patch b/common/recipes-kernel/linux/files/0174-drm-amdgpu-add-core-driver-v4.patch deleted file mode 100644 index f474b9d6..00000000 --- a/common/recipes-kernel/linux/files/0174-drm-amdgpu-add-core-driver-v4.patch +++ /dev/null @@ -1,34205 +0,0 @@ -From d38ceaf99ed015f2a0b9af3499791bd3a3daae21 Mon Sep 17 00:00:00 2001 -From: Alex Deucher <alexander.deucher@amd.com> -Date: Mon, 20 Apr 2015 16:55:21 -0400 -Subject: [PATCH 0174/1050] drm/amdgpu: add core driver (v4) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This adds the non-asic specific core driver code. - -v2: remove extra kconfig option -v3: implement minor fixes from Fengguang Wu -v4: fix cast in amdgpu_ucode.c - -Acked-by: Christian König <christian.koenig@amd.com> -Acked-by: Jammy Zhou <Jammy.Zhou@amd.com> -Signed-off-by: Alex Deucher <alexander.deucher@amd.com> ---- - drivers/gpu/drm/amd/amdgpu/Kconfig | 17 + - drivers/gpu/drm/amd/amdgpu/Makefile | 49 + - drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c | 768 ++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h | 445 +++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c | 105 ++ - drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c | 1598 +++++++++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h | 206 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c | 572 ++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c | 221 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 359 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c | 268 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 1907 ++++++++++++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h | 42 + - drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 825 +++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 161 ++ - drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 1971 +++++++++++++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | 832 +++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c | 955 ++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h | 85 + - drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 439 +++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h | 48 + - drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c | 245 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 432 +++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c | 1139 ++++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c | 371 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h | 72 + - drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 735 ++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 72 + - drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h | 30 + - drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c | 395 +++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h | 44 + - drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | 345 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c | 216 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h | 62 + - drivers/gpu/drm/amd/amdgpu/amdgpu_ioc32.c | 47 + - drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 456 +++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h | 92 + - drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 674 +++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c | 319 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 586 ++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 646 +++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_object.h | 196 ++ - drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c | 350 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_pll.h | 38 + - drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | 801 +++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h | 35 + - drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c | 125 ++ - drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c | 561 ++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | 419 +++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_semaphore.c | 102 ++ - drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 231 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_test.c | 552 ++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 209 +++ - drivers/gpu/drm/amd/amdgpu/amdgpu_trace_points.c | 9 + - drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 1249 +++++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c | 317 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h | 176 ++ - drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 976 ++++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h | 39 + - drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c | 727 ++++++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h | 47 + - drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 1248 +++++++++++++ - drivers/gpu/drm/amd/amdgpu/atom-bits.h | 48 + - drivers/gpu/drm/amd/amdgpu/atom-names.h | 100 ++ - drivers/gpu/drm/amd/amdgpu/atom-types.h | 42 + - drivers/gpu/drm/amd/amdgpu/atom.c | 1408 +++++++++++++++ - drivers/gpu/drm/amd/amdgpu/atom.h | 159 ++ - drivers/gpu/drm/amd/amdgpu/atombios_crtc.c | 807 +++++++++ - drivers/gpu/drm/amd/amdgpu/atombios_crtc.h | 58 + - drivers/gpu/drm/amd/amdgpu/atombios_dp.c | 774 ++++++++ - drivers/gpu/drm/amd/amdgpu/atombios_dp.h | 42 + - drivers/gpu/drm/amd/amdgpu/atombios_encoders.c | 2066 ++++++++++++++++++++++ - drivers/gpu/drm/amd/amdgpu/atombios_encoders.h | 73 + - drivers/gpu/drm/amd/amdgpu/atombios_i2c.c | 158 ++ - drivers/gpu/drm/amd/amdgpu/atombios_i2c.h | 31 + - drivers/gpu/drm/amd/amdgpu/cikd.h | 550 ++++++ - 76 files changed, 33574 insertions(+) - create mode 100644 drivers/gpu/drm/amd/amdgpu/Kconfig - create mode 100644 drivers/gpu/drm/amd/amdgpu/Makefile - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_display.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ioc32.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_object.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_object.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_pll.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_semaphore.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_test.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_trace_points.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/atom-bits.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atom-names.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atom-types.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atom.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/atom.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_crtc.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_crtc.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_dp.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_dp.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_encoders.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_encoders.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_i2c.c - create mode 100644 drivers/gpu/drm/amd/amdgpu/atombios_i2c.h - create mode 100644 drivers/gpu/drm/amd/amdgpu/cikd.h - -diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig -new file mode 100644 -index 0000000..b30fcfa ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/Kconfig -@@ -0,0 +1,17 @@ -+config DRM_AMDGPU_CIK -+ bool "Enable amdgpu support for CIK parts" -+ depends on DRM_AMDGPU -+ help -+ Choose this option if you want to enable experimental support -+ for CIK asics. -+ -+ CIK is already supported in radeon. CIK support in amdgpu -+ is for experimentation and testing. -+ -+config DRM_AMDGPU_USERPTR -+ bool "Always enable userptr write support" -+ depends on DRM_AMDGPU -+ select MMU_NOTIFIER -+ help -+ This option selects CONFIG_MMU_NOTIFIER if it isn't already -+ selected to enabled full userptr support. -diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile -new file mode 100644 -index 0000000..01276a5 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/Makefile -@@ -0,0 +1,49 @@ -+# -+# Makefile for the drm device driver. This driver provides support for the -+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -+ -+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/amd/include/asic_reg -+ -+amdgpu-y := amdgpu_drv.o -+ -+# add KMS driver -+amdgpu-y += amdgpu_device.o amdgpu_kms.o \ -+ amdgpu_atombios.o atombios_crtc.o amdgpu_connectors.o \ -+ atom.o amdgpu_fence.o amdgpu_ttm.o amdgpu_object.o amdgpu_gart.o \ -+ amdgpu_encoders.o amdgpu_display.o amdgpu_i2c.o \ -+ amdgpu_fb.o amdgpu_gem.o amdgpu_ring.o \ -+ amdgpu_cs.o amdgpu_bios.o amdgpu_benchmark.o amdgpu_test.o \ -+ amdgpu_pm.o atombios_dp.o amdgpu_afmt.o amdgpu_trace_points.o \ -+ atombios_encoders.o amdgpu_semaphore.o amdgpu_sa.o atombios_i2c.o \ -+ amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \ -+ amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o -+ -+# add IH block -+amdgpu-y += \ -+ amdgpu_irq.o \ -+ amdgpu_ih.o -+ -+# add SMC block -+amdgpu-y += \ -+ amdgpu_dpm.o -+ -+# add GFX block -+amdgpu-y += \ -+ amdgpu_gfx.o -+ -+# add UVD block -+amdgpu-y += \ -+ amdgpu_uvd.o -+ -+# add VCE block -+amdgpu-y += \ -+ amdgpu_vce.o -+ -+amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o -+amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o -+amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o -+amdgpu-$(CONFIG_MMU_NOTIFIER) += amdgpu_mn.o -+ -+obj-$(CONFIG_DRM_AMDGPU)+= amdgpu.o -+ -+CFLAGS_amdgpu_trace_points.o := -I$(src) -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c -new file mode 100644 -index 0000000..aef4a7a ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c -@@ -0,0 +1,768 @@ -+/* -+ * Copyright 2012 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#include <linux/pci.h> -+#include <linux/acpi.h> -+#include <linux/slab.h> -+#include <linux/power_supply.h> -+#include <linux/vga_switcheroo.h> -+#include <acpi/video.h> -+#include <drm/drmP.h> -+#include <drm/drm_crtc_helper.h> -+#include "amdgpu.h" -+#include "amdgpu_acpi.h" -+#include "atom.h" -+ -+#define ACPI_AC_CLASS "ac_adapter" -+ -+extern void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev); -+ -+struct atif_verify_interface { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u16 version; /* version */ -+ u32 notification_mask; /* supported notifications mask */ -+ u32 function_bits; /* supported functions bit vector */ -+} __packed; -+ -+struct atif_system_params { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u32 valid_mask; /* valid flags mask */ -+ u32 flags; /* flags */ -+ u8 command_code; /* notify command code */ -+} __packed; -+ -+struct atif_sbios_requests { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u32 pending; /* pending sbios requests */ -+ u8 panel_exp_mode; /* panel expansion mode */ -+ u8 thermal_gfx; /* thermal state: target gfx controller */ -+ u8 thermal_state; /* thermal state: state id (0: exit state, non-0: state) */ -+ u8 forced_power_gfx; /* forced power state: target gfx controller */ -+ u8 forced_power_state; /* forced power state: state id */ -+ u8 system_power_src; /* system power source */ -+ u8 backlight_level; /* panel backlight level (0-255) */ -+} __packed; -+ -+#define ATIF_NOTIFY_MASK 0x3 -+#define ATIF_NOTIFY_NONE 0 -+#define ATIF_NOTIFY_81 1 -+#define ATIF_NOTIFY_N 2 -+ -+struct atcs_verify_interface { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u16 version; /* version */ -+ u32 function_bits; /* supported functions bit vector */ -+} __packed; -+ -+#define ATCS_VALID_FLAGS_MASK 0x3 -+ -+struct atcs_pref_req_input { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u16 client_id; /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */ -+ u16 valid_flags_mask; /* valid flags mask */ -+ u16 flags; /* flags */ -+ u8 req_type; /* request type */ -+ u8 perf_req; /* performance request */ -+} __packed; -+ -+struct atcs_pref_req_output { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u8 ret_val; /* return value */ -+} __packed; -+ -+/* Call the ATIF method -+ */ -+/** -+ * amdgpu_atif_call - call an ATIF method -+ * -+ * @handle: acpi handle -+ * @function: the ATIF function to execute -+ * @params: ATIF function params -+ * -+ * Executes the requested ATIF function (all asics). -+ * Returns a pointer to the acpi output buffer. -+ */ -+static union acpi_object *amdgpu_atif_call(acpi_handle handle, int function, -+ struct acpi_buffer *params) -+{ -+ acpi_status status; -+ union acpi_object atif_arg_elements[2]; -+ struct acpi_object_list atif_arg; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ -+ atif_arg.count = 2; -+ atif_arg.pointer = &atif_arg_elements[0]; -+ -+ atif_arg_elements[0].type = ACPI_TYPE_INTEGER; -+ atif_arg_elements[0].integer.value = function; -+ -+ if (params) { -+ atif_arg_elements[1].type = ACPI_TYPE_BUFFER; -+ atif_arg_elements[1].buffer.length = params->length; -+ atif_arg_elements[1].buffer.pointer = params->pointer; -+ } else { -+ /* We need a second fake parameter */ -+ atif_arg_elements[1].type = ACPI_TYPE_INTEGER; -+ atif_arg_elements[1].integer.value = 0; -+ } -+ -+ status = acpi_evaluate_object(handle, "ATIF", &atif_arg, &buffer); -+ -+ /* Fail only if calling the method fails and ATIF is supported */ -+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { -+ DRM_DEBUG_DRIVER("failed to evaluate ATIF got %s\n", -+ acpi_format_exception(status)); -+ kfree(buffer.pointer); -+ return NULL; -+ } -+ -+ return buffer.pointer; -+} -+ -+/** -+ * amdgpu_atif_parse_notification - parse supported notifications -+ * -+ * @n: supported notifications struct -+ * @mask: supported notifications mask from ATIF -+ * -+ * Use the supported notifications mask from ATIF function -+ * ATIF_FUNCTION_VERIFY_INTERFACE to determine what notifications -+ * are supported (all asics). -+ */ -+static void amdgpu_atif_parse_notification(struct amdgpu_atif_notifications *n, u32 mask) -+{ -+ n->display_switch = mask & ATIF_DISPLAY_SWITCH_REQUEST_SUPPORTED; -+ n->expansion_mode_change = mask & ATIF_EXPANSION_MODE_CHANGE_REQUEST_SUPPORTED; -+ n->thermal_state = mask & ATIF_THERMAL_STATE_CHANGE_REQUEST_SUPPORTED; -+ n->forced_power_state = mask & ATIF_FORCED_POWER_STATE_CHANGE_REQUEST_SUPPORTED; -+ n->system_power_state = mask & ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST_SUPPORTED; -+ n->display_conf_change = mask & ATIF_DISPLAY_CONF_CHANGE_REQUEST_SUPPORTED; -+ n->px_gfx_switch = mask & ATIF_PX_GFX_SWITCH_REQUEST_SUPPORTED; -+ n->brightness_change = mask & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED; -+ n->dgpu_display_event = mask & ATIF_DGPU_DISPLAY_EVENT_SUPPORTED; -+} -+ -+/** -+ * amdgpu_atif_parse_functions - parse supported functions -+ * -+ * @f: supported functions struct -+ * @mask: supported functions mask from ATIF -+ * -+ * Use the supported functions mask from ATIF function -+ * ATIF_FUNCTION_VERIFY_INTERFACE to determine what functions -+ * are supported (all asics). -+ */ -+static void amdgpu_atif_parse_functions(struct amdgpu_atif_functions *f, u32 mask) -+{ -+ f->system_params = mask & ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED; -+ f->sbios_requests = mask & ATIF_GET_SYSTEM_BIOS_REQUESTS_SUPPORTED; -+ f->select_active_disp = mask & ATIF_SELECT_ACTIVE_DISPLAYS_SUPPORTED; -+ f->lid_state = mask & ATIF_GET_LID_STATE_SUPPORTED; -+ f->get_tv_standard = mask & ATIF_GET_TV_STANDARD_FROM_CMOS_SUPPORTED; -+ f->set_tv_standard = mask & ATIF_SET_TV_STANDARD_IN_CMOS_SUPPORTED; -+ f->get_panel_expansion_mode = mask & ATIF_GET_PANEL_EXPANSION_MODE_FROM_CMOS_SUPPORTED; -+ f->set_panel_expansion_mode = mask & ATIF_SET_PANEL_EXPANSION_MODE_IN_CMOS_SUPPORTED; -+ f->temperature_change = mask & ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED; -+ f->graphics_device_types = mask & ATIF_GET_GRAPHICS_DEVICE_TYPES_SUPPORTED; -+} -+ -+/** -+ * amdgpu_atif_verify_interface - verify ATIF -+ * -+ * @handle: acpi handle -+ * @atif: amdgpu atif struct -+ * -+ * Execute the ATIF_FUNCTION_VERIFY_INTERFACE ATIF function -+ * to initialize ATIF and determine what features are supported -+ * (all asics). -+ * returns 0 on success, error on failure. -+ */ -+static int amdgpu_atif_verify_interface(acpi_handle handle, -+ struct amdgpu_atif *atif) -+{ -+ union acpi_object *info; -+ struct atif_verify_interface output; -+ size_t size; -+ int err = 0; -+ -+ info = amdgpu_atif_call(handle, ATIF_FUNCTION_VERIFY_INTERFACE, NULL); -+ if (!info) -+ return -EIO; -+ -+ memset(&output, 0, sizeof(output)); -+ -+ size = *(u16 *) info->buffer.pointer; -+ if (size < 12) { -+ DRM_INFO("ATIF buffer is too small: %zu\n", size); -+ err = -EINVAL; -+ goto out; -+ } -+ size = min(sizeof(output), size); -+ -+ memcpy(&output, info->buffer.pointer, size); -+ -+ /* TODO: check version? */ -+ DRM_DEBUG_DRIVER("ATIF version %u\n", output.version); -+ -+ amdgpu_atif_parse_notification(&atif->notifications, output.notification_mask); -+ amdgpu_atif_parse_functions(&atif->functions, output.function_bits); -+ -+out: -+ kfree(info); -+ return err; -+} -+ -+/** -+ * amdgpu_atif_get_notification_params - determine notify configuration -+ * -+ * @handle: acpi handle -+ * @n: atif notification configuration struct -+ * -+ * Execute the ATIF_FUNCTION_GET_SYSTEM_PARAMETERS ATIF function -+ * to determine if a notifier is used and if so which one -+ * (all asics). This is either Notify(VGA, 0x81) or Notify(VGA, n) -+ * where n is specified in the result if a notifier is used. -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atif_get_notification_params(acpi_handle handle, -+ struct amdgpu_atif_notification_cfg *n) -+{ -+ union acpi_object *info; -+ struct atif_system_params params; -+ size_t size; -+ int err = 0; -+ -+ info = amdgpu_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS, NULL); -+ if (!info) { -+ err = -EIO; -+ goto out; -+ } -+ -+ size = *(u16 *) info->buffer.pointer; -+ if (size < 10) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ memset(¶ms, 0, sizeof(params)); -+ size = min(sizeof(params), size); -+ memcpy(¶ms, info->buffer.pointer, size); -+ -+ DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n", -+ params.flags, params.valid_mask); -+ params.flags = params.flags & params.valid_mask; -+ -+ if ((params.flags & ATIF_NOTIFY_MASK) == ATIF_NOTIFY_NONE) { -+ n->enabled = false; -+ n->command_code = 0; -+ } else if ((params.flags & ATIF_NOTIFY_MASK) == ATIF_NOTIFY_81) { -+ n->enabled = true; -+ n->command_code = 0x81; -+ } else { -+ if (size < 11) { -+ err = -EINVAL; -+ goto out; -+ } -+ n->enabled = true; -+ n->command_code = params.command_code; -+ } -+ -+out: -+ DRM_DEBUG_DRIVER("Notification %s, command code = %#x\n", -+ (n->enabled ? "enabled" : "disabled"), -+ n->command_code); -+ kfree(info); -+ return err; -+} -+ -+/** -+ * amdgpu_atif_get_sbios_requests - get requested sbios event -+ * -+ * @handle: acpi handle -+ * @req: atif sbios request struct -+ * -+ * Execute the ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS ATIF function -+ * to determine what requests the sbios is making to the driver -+ * (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atif_get_sbios_requests(acpi_handle handle, -+ struct atif_sbios_requests *req) -+{ -+ union acpi_object *info; -+ size_t size; -+ int count = 0; -+ -+ info = amdgpu_atif_call(handle, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS, NULL); -+ if (!info) -+ return -EIO; -+ -+ size = *(u16 *)info->buffer.pointer; -+ if (size < 0xd) { -+ count = -EINVAL; -+ goto out; -+ } -+ memset(req, 0, sizeof(*req)); -+ -+ size = min(sizeof(*req), size); -+ memcpy(req, info->buffer.pointer, size); -+ DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending); -+ -+ count = hweight32(req->pending); -+ -+out: -+ kfree(info); -+ return count; -+} -+ -+/** -+ * amdgpu_atif_handler - handle ATIF notify requests -+ * -+ * @adev: amdgpu_device pointer -+ * @event: atif sbios request struct -+ * -+ * Checks the acpi event and if it matches an atif event, -+ * handles it. -+ * Returns NOTIFY code -+ */ -+int amdgpu_atif_handler(struct amdgpu_device *adev, -+ struct acpi_bus_event *event) -+{ -+ struct amdgpu_atif *atif = &adev->atif; -+ struct atif_sbios_requests req; -+ acpi_handle handle; -+ int count; -+ -+ DRM_DEBUG_DRIVER("event, device_class = %s, type = %#x\n", -+ event->device_class, event->type); -+ -+ if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) -+ return NOTIFY_DONE; -+ -+ if (!atif->notification_cfg.enabled || -+ event->type != atif->notification_cfg.command_code) -+ /* Not our event */ -+ return NOTIFY_DONE; -+ -+ /* Check pending SBIOS requests */ -+ handle = ACPI_HANDLE(&adev->pdev->dev); -+ count = amdgpu_atif_get_sbios_requests(handle, &req); -+ -+ if (count <= 0) -+ return NOTIFY_DONE; -+ -+ DRM_DEBUG_DRIVER("ATIF: %d pending SBIOS requests\n", count); -+ -+ if (req.pending & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST) { -+ struct amdgpu_encoder *enc = atif->encoder_for_bl; -+ -+ if (enc) { -+ struct amdgpu_encoder_atom_dig *dig = enc->enc_priv; -+ -+ DRM_DEBUG_DRIVER("Changing brightness to %d\n", -+ req.backlight_level); -+ -+ amdgpu_display_backlight_set_level(adev, enc, req.backlight_level); -+ -+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) -+ backlight_force_update(dig->bl_dev, -+ BACKLIGHT_UPDATE_HOTKEY); -+#endif -+ } -+ } -+ /* TODO: check other events */ -+ -+ /* We've handled the event, stop the notifier chain. The ACPI interface -+ * overloads ACPI_VIDEO_NOTIFY_PROBE, we don't want to send that to -+ * userspace if the event was generated only to signal a SBIOS -+ * request. -+ */ -+ return NOTIFY_BAD; -+} -+ -+/* Call the ATCS method -+ */ -+/** -+ * amdgpu_atcs_call - call an ATCS method -+ * -+ * @handle: acpi handle -+ * @function: the ATCS function to execute -+ * @params: ATCS function params -+ * -+ * Executes the requested ATCS function (all asics). -+ * Returns a pointer to the acpi output buffer. -+ */ -+static union acpi_object *amdgpu_atcs_call(acpi_handle handle, int function, -+ struct acpi_buffer *params) -+{ -+ acpi_status status; -+ union acpi_object atcs_arg_elements[2]; -+ struct acpi_object_list atcs_arg; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ -+ atcs_arg.count = 2; -+ atcs_arg.pointer = &atcs_arg_elements[0]; -+ -+ atcs_arg_elements[0].type = ACPI_TYPE_INTEGER; -+ atcs_arg_elements[0].integer.value = function; -+ -+ if (params) { -+ atcs_arg_elements[1].type = ACPI_TYPE_BUFFER; -+ atcs_arg_elements[1].buffer.length = params->length; -+ atcs_arg_elements[1].buffer.pointer = params->pointer; -+ } else { -+ /* We need a second fake parameter */ -+ atcs_arg_elements[1].type = ACPI_TYPE_INTEGER; -+ atcs_arg_elements[1].integer.value = 0; -+ } -+ -+ status = acpi_evaluate_object(handle, "ATCS", &atcs_arg, &buffer); -+ -+ /* Fail only if calling the method fails and ATIF is supported */ -+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { -+ DRM_DEBUG_DRIVER("failed to evaluate ATCS got %s\n", -+ acpi_format_exception(status)); -+ kfree(buffer.pointer); -+ return NULL; -+ } -+ -+ return buffer.pointer; -+} -+ -+/** -+ * amdgpu_atcs_parse_functions - parse supported functions -+ * -+ * @f: supported functions struct -+ * @mask: supported functions mask from ATCS -+ * -+ * Use the supported functions mask from ATCS function -+ * ATCS_FUNCTION_VERIFY_INTERFACE to determine what functions -+ * are supported (all asics). -+ */ -+static void amdgpu_atcs_parse_functions(struct amdgpu_atcs_functions *f, u32 mask) -+{ -+ f->get_ext_state = mask & ATCS_GET_EXTERNAL_STATE_SUPPORTED; -+ f->pcie_perf_req = mask & ATCS_PCIE_PERFORMANCE_REQUEST_SUPPORTED; -+ f->pcie_dev_rdy = mask & ATCS_PCIE_DEVICE_READY_NOTIFICATION_SUPPORTED; -+ f->pcie_bus_width = mask & ATCS_SET_PCIE_BUS_WIDTH_SUPPORTED; -+} -+ -+/** -+ * amdgpu_atcs_verify_interface - verify ATCS -+ * -+ * @handle: acpi handle -+ * @atcs: amdgpu atcs struct -+ * -+ * Execute the ATCS_FUNCTION_VERIFY_INTERFACE ATCS function -+ * to initialize ATCS and determine what features are supported -+ * (all asics). -+ * returns 0 on success, error on failure. -+ */ -+static int amdgpu_atcs_verify_interface(acpi_handle handle, -+ struct amdgpu_atcs *atcs) -+{ -+ union acpi_object *info; -+ struct atcs_verify_interface output; -+ size_t size; -+ int err = 0; -+ -+ info = amdgpu_atcs_call(handle, ATCS_FUNCTION_VERIFY_INTERFACE, NULL); -+ if (!info) -+ return -EIO; -+ -+ memset(&output, 0, sizeof(output)); -+ -+ size = *(u16 *) info->buffer.pointer; -+ if (size < 8) { -+ DRM_INFO("ATCS buffer is too small: %zu\n", size); -+ err = -EINVAL; -+ goto out; -+ } -+ size = min(sizeof(output), size); -+ -+ memcpy(&output, info->buffer.pointer, size); -+ -+ /* TODO: check version? */ -+ DRM_DEBUG_DRIVER("ATCS version %u\n", output.version); -+ -+ amdgpu_atcs_parse_functions(&atcs->functions, output.function_bits); -+ -+out: -+ kfree(info); -+ return err; -+} -+ -+/** -+ * amdgpu_acpi_is_pcie_performance_request_supported -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Check if the ATCS pcie_perf_req and pcie_dev_rdy methods -+ * are supported (all asics). -+ * returns true if supported, false if not. -+ */ -+bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev) -+{ -+ struct amdgpu_atcs *atcs = &adev->atcs; -+ -+ if (atcs->functions.pcie_perf_req && atcs->functions.pcie_dev_rdy) -+ return true; -+ -+ return false; -+} -+ -+/** -+ * amdgpu_acpi_pcie_notify_device_ready -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Executes the PCIE_DEVICE_READY_NOTIFICATION method -+ * (all asics). -+ * returns 0 on success, error on failure. -+ */ -+int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev) -+{ -+ acpi_handle handle; -+ union acpi_object *info; -+ struct amdgpu_atcs *atcs = &adev->atcs; -+ -+ /* Get the device handle */ -+ handle = ACPI_HANDLE(&adev->pdev->dev); -+ if (!handle) -+ return -EINVAL; -+ -+ if (!atcs->functions.pcie_dev_rdy) -+ return -EINVAL; -+ -+ info = amdgpu_atcs_call(handle, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION, NULL); -+ if (!info) -+ return -EIO; -+ -+ kfree(info); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_acpi_pcie_performance_request -+ * -+ * @adev: amdgpu_device pointer -+ * @perf_req: requested perf level (pcie gen speed) -+ * @advertise: set advertise caps flag if set -+ * -+ * Executes the PCIE_PERFORMANCE_REQUEST method to -+ * change the pcie gen speed (all asics). -+ * returns 0 on success, error on failure. -+ */ -+int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev, -+ u8 perf_req, bool advertise) -+{ -+ acpi_handle handle; -+ union acpi_object *info; -+ struct amdgpu_atcs *atcs = &adev->atcs; -+ struct atcs_pref_req_input atcs_input; -+ struct atcs_pref_req_output atcs_output; -+ struct acpi_buffer params; -+ size_t size; -+ u32 retry = 3; -+ -+ /* Get the device handle */ -+ handle = ACPI_HANDLE(&adev->pdev->dev); -+ if (!handle) -+ return -EINVAL; -+ -+ if (!atcs->functions.pcie_perf_req) -+ return -EINVAL; -+ -+ atcs_input.size = sizeof(struct atcs_pref_req_input); -+ /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */ -+ atcs_input.client_id = adev->pdev->devfn | (adev->pdev->bus->number << 8); -+ atcs_input.valid_flags_mask = ATCS_VALID_FLAGS_MASK; -+ atcs_input.flags = ATCS_WAIT_FOR_COMPLETION; -+ if (advertise) -+ atcs_input.flags |= ATCS_ADVERTISE_CAPS; -+ atcs_input.req_type = ATCS_PCIE_LINK_SPEED; -+ atcs_input.perf_req = perf_req; -+ -+ params.length = sizeof(struct atcs_pref_req_input); -+ params.pointer = &atcs_input; -+ -+ while (retry--) { -+ info = amdgpu_atcs_call(handle, ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST, ¶ms); -+ if (!info) -+ return -EIO; -+ -+ memset(&atcs_output, 0, sizeof(atcs_output)); -+ -+ size = *(u16 *) info->buffer.pointer; -+ if (size < 3) { -+ DRM_INFO("ATCS buffer is too small: %zu\n", size); -+ kfree(info); -+ return -EINVAL; -+ } -+ size = min(sizeof(atcs_output), size); -+ -+ memcpy(&atcs_output, info->buffer.pointer, size); -+ -+ kfree(info); -+ -+ switch (atcs_output.ret_val) { -+ case ATCS_REQUEST_REFUSED: -+ default: -+ return -EINVAL; -+ case ATCS_REQUEST_COMPLETE: -+ return 0; -+ case ATCS_REQUEST_IN_PROGRESS: -+ udelay(10); -+ break; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_acpi_event - handle notify events -+ * -+ * @nb: notifier block -+ * @val: val -+ * @data: acpi event -+ * -+ * Calls relevant amdgpu functions in response to various -+ * acpi events. -+ * Returns NOTIFY code -+ */ -+static int amdgpu_acpi_event(struct notifier_block *nb, -+ unsigned long val, -+ void *data) -+{ -+ struct amdgpu_device *adev = container_of(nb, struct amdgpu_device, acpi_nb); -+ struct acpi_bus_event *entry = (struct acpi_bus_event *)data; -+ -+ if (strcmp(entry->device_class, ACPI_AC_CLASS) == 0) { -+ if (power_supply_is_system_supplied() > 0) -+ DRM_DEBUG_DRIVER("pm: AC\n"); -+ else -+ DRM_DEBUG_DRIVER("pm: DC\n"); -+ -+ amdgpu_pm_acpi_event_handler(adev); -+ } -+ -+ /* Check for pending SBIOS requests */ -+ return amdgpu_atif_handler(adev, entry); -+} -+ -+/* Call all ACPI methods here */ -+/** -+ * amdgpu_acpi_init - init driver acpi support -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Verifies the AMD ACPI interfaces and registers with the acpi -+ * notifier chain (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_acpi_init(struct amdgpu_device *adev) -+{ -+ acpi_handle handle; -+ struct amdgpu_atif *atif = &adev->atif; -+ struct amdgpu_atcs *atcs = &adev->atcs; -+ int ret; -+ -+ /* Get the device handle */ -+ handle = ACPI_HANDLE(&adev->pdev->dev); -+ -+ if (!adev->bios || !handle) -+ return 0; -+ -+ /* Call the ATCS method */ -+ ret = amdgpu_atcs_verify_interface(handle, atcs); -+ if (ret) { -+ DRM_DEBUG_DRIVER("Call to ATCS verify_interface failed: %d\n", ret); -+ } -+ -+ /* Call the ATIF method */ -+ ret = amdgpu_atif_verify_interface(handle, atif); -+ if (ret) { -+ DRM_DEBUG_DRIVER("Call to ATIF verify_interface failed: %d\n", ret); -+ goto out; -+ } -+ -+ if (atif->notifications.brightness_change) { -+ struct drm_encoder *tmp; -+ -+ /* Find the encoder controlling the brightness */ -+ list_for_each_entry(tmp, &adev->ddev->mode_config.encoder_list, -+ head) { -+ struct amdgpu_encoder *enc = to_amdgpu_encoder(tmp); -+ -+ if ((enc->devices & (ATOM_DEVICE_LCD_SUPPORT)) && -+ enc->enc_priv) { -+ if (adev->is_atom_bios) { -+ struct amdgpu_encoder_atom_dig *dig = enc->enc_priv; -+ if (dig->bl_dev) { -+ atif->encoder_for_bl = enc; -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (atif->functions.sbios_requests && !atif->functions.system_params) { -+ /* XXX check this workraround, if sbios request function is -+ * present we have to see how it's configured in the system -+ * params -+ */ -+ atif->functions.system_params = true; -+ } -+ -+ if (atif->functions.system_params) { -+ ret = amdgpu_atif_get_notification_params(handle, -+ &atif->notification_cfg); -+ if (ret) { -+ DRM_DEBUG_DRIVER("Call to GET_SYSTEM_PARAMS failed: %d\n", -+ ret); -+ /* Disable notification */ -+ atif->notification_cfg.enabled = false; -+ } -+ } -+ -+out: -+ adev->acpi_nb.notifier_call = amdgpu_acpi_event; -+ register_acpi_notifier(&adev->acpi_nb); -+ -+ return ret; -+} -+ -+/** -+ * amdgpu_acpi_fini - tear down driver acpi support -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Unregisters with the acpi notifier chain (all asics). -+ */ -+void amdgpu_acpi_fini(struct amdgpu_device *adev) -+{ -+ unregister_acpi_notifier(&adev->acpi_nb); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h -new file mode 100644 -index 0000000..01a29c3 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h -@@ -0,0 +1,445 @@ -+/* -+ * Copyright 2012 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef AMDGPU_ACPI_H -+#define AMDGPU_ACPI_H -+ -+struct amdgpu_device; -+struct acpi_bus_event; -+ -+int amdgpu_atif_handler(struct amdgpu_device *adev, -+ struct acpi_bus_event *event); -+ -+/* AMD hw uses four ACPI control methods: -+ * 1. ATIF -+ * ARG0: (ACPI_INTEGER) function code -+ * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes -+ * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes -+ * ATIF provides an entry point for the gfx driver to interact with the sbios. -+ * The AMD ACPI notification mechanism uses Notify (VGA, 0x81) or a custom -+ * notification. Which notification is used as indicated by the ATIF Control -+ * Method GET_SYSTEM_PARAMETERS. When the driver receives Notify (VGA, 0x81) or -+ * a custom notification it invokes ATIF Control Method GET_SYSTEM_BIOS_REQUESTS -+ * to identify pending System BIOS requests and associated parameters. For -+ * example, if one of the pending requests is DISPLAY_SWITCH_REQUEST, the driver -+ * will perform display device detection and invoke ATIF Control Method -+ * SELECT_ACTIVE_DISPLAYS. -+ * -+ * 2. ATPX -+ * ARG0: (ACPI_INTEGER) function code -+ * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes -+ * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes -+ * ATPX methods are used on PowerXpress systems to handle mux switching and -+ * discrete GPU power control. -+ * -+ * 3. ATRM -+ * ARG0: (ACPI_INTEGER) offset of vbios rom data -+ * ARG1: (ACPI_BUFFER) size of the buffer to fill (up to 4K). -+ * OUTPUT: (ACPI_BUFFER) output buffer -+ * ATRM provides an interfacess to access the discrete GPU vbios image on -+ * PowerXpress systems with multiple GPUs. -+ * -+ * 4. ATCS -+ * ARG0: (ACPI_INTEGER) function code -+ * ARG1: (ACPI_BUFFER) parameter buffer, 256 bytes -+ * OUTPUT: (ACPI_BUFFER) output buffer, 256 bytes -+ * ATCS provides an interface to AMD chipset specific functionality. -+ * -+ */ -+/* ATIF */ -+#define ATIF_FUNCTION_VERIFY_INTERFACE 0x0 -+/* ARG0: ATIF_FUNCTION_VERIFY_INTERFACE -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - version -+ * DWORD - supported notifications mask -+ * DWORD - supported functions bit vector -+ */ -+/* Notifications mask */ -+# define ATIF_DISPLAY_SWITCH_REQUEST_SUPPORTED (1 << 0) -+# define ATIF_EXPANSION_MODE_CHANGE_REQUEST_SUPPORTED (1 << 1) -+# define ATIF_THERMAL_STATE_CHANGE_REQUEST_SUPPORTED (1 << 2) -+# define ATIF_FORCED_POWER_STATE_CHANGE_REQUEST_SUPPORTED (1 << 3) -+# define ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST_SUPPORTED (1 << 4) -+# define ATIF_DISPLAY_CONF_CHANGE_REQUEST_SUPPORTED (1 << 5) -+# define ATIF_PX_GFX_SWITCH_REQUEST_SUPPORTED (1 << 6) -+# define ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED (1 << 7) -+# define ATIF_DGPU_DISPLAY_EVENT_SUPPORTED (1 << 8) -+/* supported functions vector */ -+# define ATIF_GET_SYSTEM_PARAMETERS_SUPPORTED (1 << 0) -+# define ATIF_GET_SYSTEM_BIOS_REQUESTS_SUPPORTED (1 << 1) -+# define ATIF_SELECT_ACTIVE_DISPLAYS_SUPPORTED (1 << 2) -+# define ATIF_GET_LID_STATE_SUPPORTED (1 << 3) -+# define ATIF_GET_TV_STANDARD_FROM_CMOS_SUPPORTED (1 << 4) -+# define ATIF_SET_TV_STANDARD_IN_CMOS_SUPPORTED (1 << 5) -+# define ATIF_GET_PANEL_EXPANSION_MODE_FROM_CMOS_SUPPORTED (1 << 6) -+# define ATIF_SET_PANEL_EXPANSION_MODE_IN_CMOS_SUPPORTED (1 << 7) -+# define ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED (1 << 12) -+# define ATIF_GET_GRAPHICS_DEVICE_TYPES_SUPPORTED (1 << 14) -+#define ATIF_FUNCTION_GET_SYSTEM_PARAMETERS 0x1 -+/* ARG0: ATIF_FUNCTION_GET_SYSTEM_PARAMETERS -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * DWORD - valid flags mask -+ * DWORD - flags -+ * -+ * OR -+ * -+ * WORD - structure size in bytes (includes size field) -+ * DWORD - valid flags mask -+ * DWORD - flags -+ * BYTE - notify command code -+ * -+ * flags -+ * bits 1:0: -+ * 0 - Notify(VGA, 0x81) is not used for notification -+ * 1 - Notify(VGA, 0x81) is used for notification -+ * 2 - Notify(VGA, n) is used for notification where -+ * n (0xd0-0xd9) is specified in notify command code. -+ * bit 2: -+ * 1 - lid changes not reported though int10 -+ */ -+#define ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS 0x2 -+/* ARG0: ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * DWORD - pending sbios requests -+ * BYTE - panel expansion mode -+ * BYTE - thermal state: target gfx controller -+ * BYTE - thermal state: state id (0: exit state, non-0: state) -+ * BYTE - forced power state: target gfx controller -+ * BYTE - forced power state: state id -+ * BYTE - system power source -+ * BYTE - panel backlight level (0-255) -+ */ -+/* pending sbios requests */ -+# define ATIF_DISPLAY_SWITCH_REQUEST (1 << 0) -+# define ATIF_EXPANSION_MODE_CHANGE_REQUEST (1 << 1) -+# define ATIF_THERMAL_STATE_CHANGE_REQUEST (1 << 2) -+# define ATIF_FORCED_POWER_STATE_CHANGE_REQUEST (1 << 3) -+# define ATIF_SYSTEM_POWER_SOURCE_CHANGE_REQUEST (1 << 4) -+# define ATIF_DISPLAY_CONF_CHANGE_REQUEST (1 << 5) -+# define ATIF_PX_GFX_SWITCH_REQUEST (1 << 6) -+# define ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST (1 << 7) -+# define ATIF_DGPU_DISPLAY_EVENT (1 << 8) -+/* panel expansion mode */ -+# define ATIF_PANEL_EXPANSION_DISABLE 0 -+# define ATIF_PANEL_EXPANSION_FULL 1 -+# define ATIF_PANEL_EXPANSION_ASPECT 2 -+/* target gfx controller */ -+# define ATIF_TARGET_GFX_SINGLE 0 -+# define ATIF_TARGET_GFX_PX_IGPU 1 -+# define ATIF_TARGET_GFX_PX_DGPU 2 -+/* system power source */ -+# define ATIF_POWER_SOURCE_AC 1 -+# define ATIF_POWER_SOURCE_DC 2 -+# define ATIF_POWER_SOURCE_RESTRICTED_AC_1 3 -+# define ATIF_POWER_SOURCE_RESTRICTED_AC_2 4 -+#define ATIF_FUNCTION_SELECT_ACTIVE_DISPLAYS 0x3 -+/* ARG0: ATIF_FUNCTION_SELECT_ACTIVE_DISPLAYS -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - selected displays -+ * WORD - connected displays -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - selected displays -+ */ -+# define ATIF_LCD1 (1 << 0) -+# define ATIF_CRT1 (1 << 1) -+# define ATIF_TV (1 << 2) -+# define ATIF_DFP1 (1 << 3) -+# define ATIF_CRT2 (1 << 4) -+# define ATIF_LCD2 (1 << 5) -+# define ATIF_DFP2 (1 << 7) -+# define ATIF_CV (1 << 8) -+# define ATIF_DFP3 (1 << 9) -+# define ATIF_DFP4 (1 << 10) -+# define ATIF_DFP5 (1 << 11) -+# define ATIF_DFP6 (1 << 12) -+#define ATIF_FUNCTION_GET_LID_STATE 0x4 -+/* ARG0: ATIF_FUNCTION_GET_LID_STATE -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - lid state (0: open, 1: closed) -+ * -+ * GET_LID_STATE only works at boot and resume, for general lid -+ * status, use the kernel provided status -+ */ -+#define ATIF_FUNCTION_GET_TV_STANDARD_FROM_CMOS 0x5 -+/* ARG0: ATIF_FUNCTION_GET_TV_STANDARD_FROM_CMOS -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - 0 -+ * BYTE - TV standard -+ */ -+# define ATIF_TV_STD_NTSC 0 -+# define ATIF_TV_STD_PAL 1 -+# define ATIF_TV_STD_PALM 2 -+# define ATIF_TV_STD_PAL60 3 -+# define ATIF_TV_STD_NTSCJ 4 -+# define ATIF_TV_STD_PALCN 5 -+# define ATIF_TV_STD_PALN 6 -+# define ATIF_TV_STD_SCART_RGB 9 -+#define ATIF_FUNCTION_SET_TV_STANDARD_IN_CMOS 0x6 -+/* ARG0: ATIF_FUNCTION_SET_TV_STANDARD_IN_CMOS -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - 0 -+ * BYTE - TV standard -+ * OUTPUT: none -+ */ -+#define ATIF_FUNCTION_GET_PANEL_EXPANSION_MODE_FROM_CMOS 0x7 -+/* ARG0: ATIF_FUNCTION_GET_PANEL_EXPANSION_MODE_FROM_CMOS -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - panel expansion mode -+ */ -+#define ATIF_FUNCTION_SET_PANEL_EXPANSION_MODE_IN_CMOS 0x8 -+/* ARG0: ATIF_FUNCTION_SET_PANEL_EXPANSION_MODE_IN_CMOS -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - panel expansion mode -+ * OUTPUT: none -+ */ -+#define ATIF_FUNCTION_TEMPERATURE_CHANGE_NOTIFICATION 0xD -+/* ARG0: ATIF_FUNCTION_TEMPERATURE_CHANGE_NOTIFICATION -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - gfx controller id -+ * BYTE - current temperature (degress Celsius) -+ * OUTPUT: none -+ */ -+#define ATIF_FUNCTION_GET_GRAPHICS_DEVICE_TYPES 0xF -+/* ARG0: ATIF_FUNCTION_GET_GRAPHICS_DEVICE_TYPES -+ * ARG1: none -+ * OUTPUT: -+ * WORD - number of gfx devices -+ * WORD - device structure size in bytes (excludes device size field) -+ * DWORD - flags \ -+ * WORD - bus number } repeated structure -+ * WORD - device number / -+ */ -+/* flags */ -+# define ATIF_PX_REMOVABLE_GRAPHICS_DEVICE (1 << 0) -+# define ATIF_XGP_PORT (1 << 1) -+# define ATIF_VGA_ENABLED_GRAPHICS_DEVICE (1 << 2) -+# define ATIF_XGP_PORT_IN_DOCK (1 << 3) -+ -+/* ATPX */ -+#define ATPX_FUNCTION_VERIFY_INTERFACE 0x0 -+/* ARG0: ATPX_FUNCTION_VERIFY_INTERFACE -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - version -+ * DWORD - supported functions bit vector -+ */ -+/* supported functions vector */ -+# define ATPX_GET_PX_PARAMETERS_SUPPORTED (1 << 0) -+# define ATPX_POWER_CONTROL_SUPPORTED (1 << 1) -+# define ATPX_DISPLAY_MUX_CONTROL_SUPPORTED (1 << 2) -+# define ATPX_I2C_MUX_CONTROL_SUPPORTED (1 << 3) -+# define ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED (1 << 4) -+# define ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED (1 << 5) -+# define ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED (1 << 7) -+# define ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED (1 << 8) -+#define ATPX_FUNCTION_GET_PX_PARAMETERS 0x1 -+/* ARG0: ATPX_FUNCTION_GET_PX_PARAMETERS -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * DWORD - valid flags mask -+ * DWORD - flags -+ */ -+/* flags */ -+# define ATPX_LVDS_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 0) -+# define ATPX_CRT1_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 1) -+# define ATPX_DVI1_I2C_AVAILABLE_TO_BOTH_GPUS (1 << 2) -+# define ATPX_CRT1_RGB_SIGNAL_MUXED (1 << 3) -+# define ATPX_TV_SIGNAL_MUXED (1 << 4) -+# define ATPX_DFP_SIGNAL_MUXED (1 << 5) -+# define ATPX_SEPARATE_MUX_FOR_I2C (1 << 6) -+# define ATPX_DYNAMIC_PX_SUPPORTED (1 << 7) -+# define ATPX_ACF_NOT_SUPPORTED (1 << 8) -+# define ATPX_FIXED_NOT_SUPPORTED (1 << 9) -+# define ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED (1 << 10) -+# define ATPX_DGPU_REQ_POWER_FOR_DISPLAYS (1 << 11) -+#define ATPX_FUNCTION_POWER_CONTROL 0x2 -+/* ARG0: ATPX_FUNCTION_POWER_CONTROL -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - dGPU power state (0: power off, 1: power on) -+ * OUTPUT: none -+ */ -+#define ATPX_FUNCTION_DISPLAY_MUX_CONTROL 0x3 -+/* ARG0: ATPX_FUNCTION_DISPLAY_MUX_CONTROL -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - display mux control (0: iGPU, 1: dGPU) -+ * OUTPUT: none -+ */ -+# define ATPX_INTEGRATED_GPU 0 -+# define ATPX_DISCRETE_GPU 1 -+#define ATPX_FUNCTION_I2C_MUX_CONTROL 0x4 -+/* ARG0: ATPX_FUNCTION_I2C_MUX_CONTROL -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - i2c/aux/hpd mux control (0: iGPU, 1: dGPU) -+ * OUTPUT: none -+ */ -+#define ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION 0x5 -+/* ARG0: ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - target gpu (0: iGPU, 1: dGPU) -+ * OUTPUT: none -+ */ -+#define ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION 0x6 -+/* ARG0: ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - target gpu (0: iGPU, 1: dGPU) -+ * OUTPUT: none -+ */ -+#define ATPX_FUNCTION_GET_DISPLAY_CONNECTORS_MAPPING 0x8 -+/* ARG0: ATPX_FUNCTION_GET_DISPLAY_CONNECTORS_MAPPING -+ * ARG1: none -+ * OUTPUT: -+ * WORD - number of display connectors -+ * WORD - connector structure size in bytes (excludes connector size field) -+ * BYTE - flags \ -+ * BYTE - ATIF display vector bit position } repeated -+ * BYTE - adapter id (0: iGPU, 1-n: dGPU ordered by pcie bus number) } structure -+ * WORD - connector ACPI id / -+ */ -+/* flags */ -+# define ATPX_DISPLAY_OUTPUT_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 0) -+# define ATPX_DISPLAY_HPD_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 1) -+# define ATPX_DISPLAY_I2C_SUPPORTED_BY_ADAPTER_ID_DEVICE (1 << 2) -+#define ATPX_FUNCTION_GET_DISPLAY_DETECTION_PORTS 0x9 -+/* ARG0: ATPX_FUNCTION_GET_DISPLAY_DETECTION_PORTS -+ * ARG1: none -+ * OUTPUT: -+ * WORD - number of HPD/DDC ports -+ * WORD - port structure size in bytes (excludes port size field) -+ * BYTE - ATIF display vector bit position \ -+ * BYTE - hpd id } reapeated structure -+ * BYTE - ddc id / -+ * -+ * available on A+A systems only -+ */ -+/* hpd id */ -+# define ATPX_HPD_NONE 0 -+# define ATPX_HPD1 1 -+# define ATPX_HPD2 2 -+# define ATPX_HPD3 3 -+# define ATPX_HPD4 4 -+# define ATPX_HPD5 5 -+# define ATPX_HPD6 6 -+/* ddc id */ -+# define ATPX_DDC_NONE 0 -+# define ATPX_DDC1 1 -+# define ATPX_DDC2 2 -+# define ATPX_DDC3 3 -+# define ATPX_DDC4 4 -+# define ATPX_DDC5 5 -+# define ATPX_DDC6 6 -+# define ATPX_DDC7 7 -+# define ATPX_DDC8 8 -+ -+/* ATCS */ -+#define ATCS_FUNCTION_VERIFY_INTERFACE 0x0 -+/* ARG0: ATCS_FUNCTION_VERIFY_INTERFACE -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - version -+ * DWORD - supported functions bit vector -+ */ -+/* supported functions vector */ -+# define ATCS_GET_EXTERNAL_STATE_SUPPORTED (1 << 0) -+# define ATCS_PCIE_PERFORMANCE_REQUEST_SUPPORTED (1 << 1) -+# define ATCS_PCIE_DEVICE_READY_NOTIFICATION_SUPPORTED (1 << 2) -+# define ATCS_SET_PCIE_BUS_WIDTH_SUPPORTED (1 << 3) -+#define ATCS_FUNCTION_GET_EXTERNAL_STATE 0x1 -+/* ARG0: ATCS_FUNCTION_GET_EXTERNAL_STATE -+ * ARG1: none -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * DWORD - valid flags mask -+ * DWORD - flags (0: undocked, 1: docked) -+ */ -+/* flags */ -+# define ATCS_DOCKED (1 << 0) -+#define ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST 0x2 -+/* ARG0: ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) -+ * WORD - valid flags mask -+ * WORD - flags -+ * BYTE - request type -+ * BYTE - performance request -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - return value -+ */ -+/* flags */ -+# define ATCS_ADVERTISE_CAPS (1 << 0) -+# define ATCS_WAIT_FOR_COMPLETION (1 << 1) -+/* request type */ -+# define ATCS_PCIE_LINK_SPEED 1 -+/* performance request */ -+# define ATCS_REMOVE 0 -+# define ATCS_FORCE_LOW_POWER 1 -+# define ATCS_PERF_LEVEL_1 2 /* PCIE Gen 1 */ -+# define ATCS_PERF_LEVEL_2 3 /* PCIE Gen 2 */ -+# define ATCS_PERF_LEVEL_3 4 /* PCIE Gen 3 */ -+/* return value */ -+# define ATCS_REQUEST_REFUSED 1 -+# define ATCS_REQUEST_COMPLETE 2 -+# define ATCS_REQUEST_IN_PROGRESS 3 -+#define ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION 0x3 -+/* ARG0: ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION -+ * ARG1: none -+ * OUTPUT: none -+ */ -+#define ATCS_FUNCTION_SET_PCIE_BUS_WIDTH 0x4 -+/* ARG0: ATCS_FUNCTION_SET_PCIE_BUS_WIDTH -+ * ARG1: -+ * WORD - structure size in bytes (includes size field) -+ * WORD - client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) -+ * BYTE - number of active lanes -+ * OUTPUT: -+ * WORD - structure size in bytes (includes size field) -+ * BYTE - number of active lanes -+ */ -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c -new file mode 100644 -index 0000000..857ba08 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c -@@ -0,0 +1,105 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Christian König. -+ * -+ * 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: Christian König -+ */ -+#include <linux/hdmi.h> -+#include <linux/gcd.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+static const struct amdgpu_afmt_acr amdgpu_afmt_predefined_acr[] = { -+ /* 32kHz 44.1kHz 48kHz */ -+ /* Clock N CTS N CTS N CTS */ -+ { 25175, 4096, 25175, 28224, 125875, 6144, 25175 }, /* 25,20/1.001 MHz */ -+ { 25200, 4096, 25200, 6272, 28000, 6144, 25200 }, /* 25.20 MHz */ -+ { 27000, 4096, 27000, 6272, 30000, 6144, 27000 }, /* 27.00 MHz */ -+ { 27027, 4096, 27027, 6272, 30030, 6144, 27027 }, /* 27.00*1.001 MHz */ -+ { 54000, 4096, 54000, 6272, 60000, 6144, 54000 }, /* 54.00 MHz */ -+ { 54054, 4096, 54054, 6272, 60060, 6144, 54054 }, /* 54.00*1.001 MHz */ -+ { 74176, 4096, 74176, 5733, 75335, 6144, 74176 }, /* 74.25/1.001 MHz */ -+ { 74250, 4096, 74250, 6272, 82500, 6144, 74250 }, /* 74.25 MHz */ -+ { 148352, 4096, 148352, 5733, 150670, 6144, 148352 }, /* 148.50/1.001 MHz */ -+ { 148500, 4096, 148500, 6272, 165000, 6144, 148500 }, /* 148.50 MHz */ -+}; -+ -+ -+/* -+ * calculate CTS and N values if they are not found in the table -+ */ -+static void amdgpu_afmt_calc_cts(uint32_t clock, int *CTS, int *N, int freq) -+{ -+ int n, cts; -+ unsigned long div, mul; -+ -+ /* Safe, but overly large values */ -+ n = 128 * freq; -+ cts = clock * 1000; -+ -+ /* Smallest valid fraction */ -+ div = gcd(n, cts); -+ -+ n /= div; -+ cts /= div; -+ -+ /* -+ * The optimal N is 128*freq/1000. Calculate the closest larger -+ * value that doesn't truncate any bits. -+ */ -+ mul = ((128*freq/1000) + (n-1))/n; -+ -+ n *= mul; -+ cts *= mul; -+ -+ /* Check that we are in spec (not always possible) */ -+ if (n < (128*freq/1500)) -+ printk(KERN_WARNING "Calculated ACR N value is too small. You may experience audio problems.\n"); -+ if (n > (128*freq/300)) -+ printk(KERN_WARNING "Calculated ACR N value is too large. You may experience audio problems.\n"); -+ -+ *N = n; -+ *CTS = cts; -+ -+ DRM_DEBUG("Calculated ACR timing N=%d CTS=%d for frequency %d\n", -+ *N, *CTS, freq); -+} -+ -+struct amdgpu_afmt_acr amdgpu_afmt_acr(uint32_t clock) -+{ -+ struct amdgpu_afmt_acr res; -+ u8 i; -+ -+ /* Precalculated values for common clocks */ -+ for (i = 0; i < ARRAY_SIZE(amdgpu_afmt_predefined_acr); i++) { -+ if (amdgpu_afmt_predefined_acr[i].clock == clock) -+ return amdgpu_afmt_predefined_acr[i]; -+ } -+ -+ /* And odd clocks get manually calculated */ -+ amdgpu_afmt_calc_cts(clock, &res.cts_32khz, &res.n_32khz, 32000); -+ amdgpu_afmt_calc_cts(clock, &res.cts_44_1khz, &res.n_44_1khz, 44100); -+ amdgpu_afmt_calc_cts(clock, &res.cts_48khz, &res.n_48khz, 48000); -+ -+ return res; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c -new file mode 100644 -index 0000000..6a58837 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c -@@ -0,0 +1,1598 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_atombios.h" -+#include "amdgpu_i2c.h" -+ -+#include "atom.h" -+#include "atom-bits.h" -+#include "atombios_encoders.h" -+#include "bif/bif_4_1_d.h" -+ -+static void amdgpu_atombios_lookup_i2c_gpio_quirks(struct amdgpu_device *adev, -+ ATOM_GPIO_I2C_ASSIGMENT *gpio, -+ u8 index) -+{ -+ -+} -+ -+static struct amdgpu_i2c_bus_rec amdgpu_atombios_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio) -+{ -+ struct amdgpu_i2c_bus_rec i2c; -+ -+ memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); -+ -+ i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex); -+ i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex); -+ i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex); -+ i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex); -+ i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex); -+ i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex); -+ i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex); -+ i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex); -+ i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); -+ i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); -+ i2c.en_clk_mask = (1 << gpio->ucClkEnShift); -+ i2c.en_data_mask = (1 << gpio->ucDataEnShift); -+ i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); -+ i2c.y_data_mask = (1 << gpio->ucDataY_Shift); -+ i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); -+ i2c.a_data_mask = (1 << gpio->ucDataA_Shift); -+ -+ if (gpio->sucI2cId.sbfAccess.bfHW_Capable) -+ i2c.hw_capable = true; -+ else -+ i2c.hw_capable = false; -+ -+ if (gpio->sucI2cId.ucAccess == 0xa0) -+ i2c.mm_i2c = true; -+ else -+ i2c.mm_i2c = false; -+ -+ i2c.i2c_id = gpio->sucI2cId.ucAccess; -+ -+ if (i2c.mask_clk_reg) -+ i2c.valid = true; -+ else -+ i2c.valid = false; -+ -+ return i2c; -+} -+ -+struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev, -+ uint8_t id) -+{ -+ struct atom_context *ctx = adev->mode_info.atom_context; -+ ATOM_GPIO_I2C_ASSIGMENT *gpio; -+ struct amdgpu_i2c_bus_rec i2c; -+ int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); -+ struct _ATOM_GPIO_I2C_INFO *i2c_info; -+ uint16_t data_offset, size; -+ int i, num_indices; -+ -+ memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); -+ i2c.valid = false; -+ -+ if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { -+ i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); -+ -+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / -+ sizeof(ATOM_GPIO_I2C_ASSIGMENT); -+ -+ gpio = &i2c_info->asGPIO_Info[0]; -+ for (i = 0; i < num_indices; i++) { -+ -+ amdgpu_atombios_lookup_i2c_gpio_quirks(adev, gpio, i); -+ -+ if (gpio->sucI2cId.ucAccess == id) { -+ i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); -+ break; -+ } -+ gpio = (ATOM_GPIO_I2C_ASSIGMENT *) -+ ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); -+ } -+ } -+ -+ return i2c; -+} -+ -+void amdgpu_atombios_i2c_init(struct amdgpu_device *adev) -+{ -+ struct atom_context *ctx = adev->mode_info.atom_context; -+ ATOM_GPIO_I2C_ASSIGMENT *gpio; -+ struct amdgpu_i2c_bus_rec i2c; -+ int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); -+ struct _ATOM_GPIO_I2C_INFO *i2c_info; -+ uint16_t data_offset, size; -+ int i, num_indices; -+ char stmp[32]; -+ -+ if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { -+ i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); -+ -+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / -+ sizeof(ATOM_GPIO_I2C_ASSIGMENT); -+ -+ gpio = &i2c_info->asGPIO_Info[0]; -+ for (i = 0; i < num_indices; i++) { -+ amdgpu_atombios_lookup_i2c_gpio_quirks(adev, gpio, i); -+ -+ i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); -+ -+ if (i2c.valid) { -+ sprintf(stmp, "0x%x", i2c.i2c_id); -+ adev->i2c_bus[i] = amdgpu_i2c_create(adev->ddev, &i2c, stmp); -+ } -+ gpio = (ATOM_GPIO_I2C_ASSIGMENT *) -+ ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); -+ } -+ } -+} -+ -+struct amdgpu_gpio_rec -+amdgpu_atombios_lookup_gpio(struct amdgpu_device *adev, -+ u8 id) -+{ -+ struct atom_context *ctx = adev->mode_info.atom_context; -+ struct amdgpu_gpio_rec gpio; -+ int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); -+ struct _ATOM_GPIO_PIN_LUT *gpio_info; -+ ATOM_GPIO_PIN_ASSIGNMENT *pin; -+ u16 data_offset, size; -+ int i, num_indices; -+ -+ memset(&gpio, 0, sizeof(struct amdgpu_gpio_rec)); -+ gpio.valid = false; -+ -+ if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { -+ gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset); -+ -+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / -+ sizeof(ATOM_GPIO_PIN_ASSIGNMENT); -+ -+ pin = gpio_info->asGPIO_Pin; -+ for (i = 0; i < num_indices; i++) { -+ if (id == pin->ucGPIO_ID) { -+ gpio.id = pin->ucGPIO_ID; -+ gpio.reg = le16_to_cpu(pin->usGpioPin_AIndex); -+ gpio.shift = pin->ucGpioPinBitShift; -+ gpio.mask = (1 << pin->ucGpioPinBitShift); -+ gpio.valid = true; -+ break; -+ } -+ pin = (ATOM_GPIO_PIN_ASSIGNMENT *) -+ ((u8 *)pin + sizeof(ATOM_GPIO_PIN_ASSIGNMENT)); -+ } -+ } -+ -+ return gpio; -+} -+ -+static struct amdgpu_hpd -+amdgpu_atombios_get_hpd_info_from_gpio(struct amdgpu_device *adev, -+ struct amdgpu_gpio_rec *gpio) -+{ -+ struct amdgpu_hpd hpd; -+ u32 reg; -+ -+ memset(&hpd, 0, sizeof(struct amdgpu_hpd)); -+ -+ reg = amdgpu_display_hpd_get_gpio_reg(adev); -+ -+ hpd.gpio = *gpio; -+ if (gpio->reg == reg) { -+ switch(gpio->mask) { -+ case (1 << 0): -+ hpd.hpd = AMDGPU_HPD_1; -+ break; -+ case (1 << 8): -+ hpd.hpd = AMDGPU_HPD_2; -+ break; -+ case (1 << 16): -+ hpd.hpd = AMDGPU_HPD_3; -+ break; -+ case (1 << 24): -+ hpd.hpd = AMDGPU_HPD_4; -+ break; -+ case (1 << 26): -+ hpd.hpd = AMDGPU_HPD_5; -+ break; -+ case (1 << 28): -+ hpd.hpd = AMDGPU_HPD_6; -+ break; -+ default: -+ hpd.hpd = AMDGPU_HPD_NONE; -+ break; -+ } -+ } else -+ hpd.hpd = AMDGPU_HPD_NONE; -+ return hpd; -+} -+ -+static bool amdgpu_atombios_apply_quirks(struct amdgpu_device *adev, -+ uint32_t supported_device, -+ int *connector_type, -+ struct amdgpu_i2c_bus_rec *i2c_bus, -+ uint16_t *line_mux, -+ struct amdgpu_hpd *hpd) -+{ -+ return true; -+} -+ -+static const int object_connector_convert[] = { -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_DVII, -+ DRM_MODE_CONNECTOR_DVII, -+ DRM_MODE_CONNECTOR_DVID, -+ DRM_MODE_CONNECTOR_DVID, -+ DRM_MODE_CONNECTOR_VGA, -+ DRM_MODE_CONNECTOR_Composite, -+ DRM_MODE_CONNECTOR_SVIDEO, -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_9PinDIN, -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_HDMIA, -+ DRM_MODE_CONNECTOR_HDMIB, -+ DRM_MODE_CONNECTOR_LVDS, -+ DRM_MODE_CONNECTOR_9PinDIN, -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_Unknown, -+ DRM_MODE_CONNECTOR_DisplayPort, -+ DRM_MODE_CONNECTOR_eDP, -+ DRM_MODE_CONNECTOR_Unknown -+}; -+ -+bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ struct atom_context *ctx = mode_info->atom_context; -+ int index = GetIndexIntoMasterTable(DATA, Object_Header); -+ u16 size, data_offset; -+ u8 frev, crev; -+ ATOM_CONNECTOR_OBJECT_TABLE *con_obj; -+ ATOM_ENCODER_OBJECT_TABLE *enc_obj; -+ ATOM_OBJECT_TABLE *router_obj; -+ ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; -+ ATOM_OBJECT_HEADER *obj_header; -+ int i, j, k, path_size, device_support; -+ int connector_type; -+ u16 conn_id, connector_object_id; -+ struct amdgpu_i2c_bus_rec ddc_bus; -+ struct amdgpu_router router; -+ struct amdgpu_gpio_rec gpio; -+ struct amdgpu_hpd hpd; -+ -+ if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) -+ return false; -+ -+ if (crev < 2) -+ return false; -+ -+ obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); -+ path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(obj_header->usDisplayPathTableOffset)); -+ con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(obj_header->usConnectorObjectTableOffset)); -+ enc_obj = (ATOM_ENCODER_OBJECT_TABLE *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(obj_header->usEncoderObjectTableOffset)); -+ router_obj = (ATOM_OBJECT_TABLE *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(obj_header->usRouterObjectTableOffset)); -+ device_support = le16_to_cpu(obj_header->usDeviceSupport); -+ -+ path_size = 0; -+ for (i = 0; i < path_obj->ucNumOfDispPath; i++) { -+ uint8_t *addr = (uint8_t *) path_obj->asDispPath; -+ ATOM_DISPLAY_OBJECT_PATH *path; -+ addr += path_size; -+ path = (ATOM_DISPLAY_OBJECT_PATH *) addr; -+ path_size += le16_to_cpu(path->usSize); -+ -+ if (device_support & le16_to_cpu(path->usDeviceTag)) { -+ uint8_t con_obj_id, con_obj_num, con_obj_type; -+ -+ con_obj_id = -+ (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK) -+ >> OBJECT_ID_SHIFT; -+ con_obj_num = -+ (le16_to_cpu(path->usConnObjectId) & ENUM_ID_MASK) -+ >> ENUM_ID_SHIFT; -+ con_obj_type = -+ (le16_to_cpu(path->usConnObjectId) & -+ OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; -+ -+ connector_type = -+ object_connector_convert[con_obj_id]; -+ connector_object_id = con_obj_id; -+ -+ if (connector_type == DRM_MODE_CONNECTOR_Unknown) -+ continue; -+ -+ router.ddc_valid = false; -+ router.cd_valid = false; -+ for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { -+ uint8_t grph_obj_id, grph_obj_num, grph_obj_type; -+ -+ grph_obj_id = -+ (le16_to_cpu(path->usGraphicObjIds[j]) & -+ OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; -+ grph_obj_num = -+ (le16_to_cpu(path->usGraphicObjIds[j]) & -+ ENUM_ID_MASK) >> ENUM_ID_SHIFT; -+ grph_obj_type = -+ (le16_to_cpu(path->usGraphicObjIds[j]) & -+ OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; -+ -+ if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { -+ for (k = 0; k < enc_obj->ucNumberOfObjects; k++) { -+ u16 encoder_obj = le16_to_cpu(enc_obj->asObjects[k].usObjectID); -+ if (le16_to_cpu(path->usGraphicObjIds[j]) == encoder_obj) { -+ ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(enc_obj->asObjects[k].usRecordOffset)); -+ ATOM_ENCODER_CAP_RECORD *cap_record; -+ u16 caps = 0; -+ -+ while (record->ucRecordSize > 0 && -+ record->ucRecordType > 0 && -+ record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { -+ switch (record->ucRecordType) { -+ case ATOM_ENCODER_CAP_RECORD_TYPE: -+ cap_record =(ATOM_ENCODER_CAP_RECORD *) -+ record; -+ caps = le16_to_cpu(cap_record->usEncoderCap); -+ break; -+ } -+ record = (ATOM_COMMON_RECORD_HEADER *) -+ ((char *)record + record->ucRecordSize); -+ } -+ amdgpu_display_add_encoder(adev, encoder_obj, -+ le16_to_cpu(path->usDeviceTag), -+ caps); -+ } -+ } -+ } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { -+ for (k = 0; k < router_obj->ucNumberOfObjects; k++) { -+ u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID); -+ if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { -+ ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(router_obj->asObjects[k].usRecordOffset)); -+ ATOM_I2C_RECORD *i2c_record; -+ ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; -+ ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; -+ ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path; -+ ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = -+ (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); -+ u8 *num_dst_objs = (u8 *) -+ ((u8 *)router_src_dst_table + 1 + -+ (router_src_dst_table->ucNumberOfSrc * 2)); -+ u16 *dst_objs = (u16 *)(num_dst_objs + 1); -+ int enum_id; -+ -+ router.router_id = router_obj_id; -+ for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) { -+ if (le16_to_cpu(path->usConnObjectId) == -+ le16_to_cpu(dst_objs[enum_id])) -+ break; -+ } -+ -+ while (record->ucRecordSize > 0 && -+ record->ucRecordType > 0 && -+ record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { -+ switch (record->ucRecordType) { -+ case ATOM_I2C_RECORD_TYPE: -+ i2c_record = -+ (ATOM_I2C_RECORD *) -+ record; -+ i2c_config = -+ (ATOM_I2C_ID_CONFIG_ACCESS *) -+ &i2c_record->sucI2cId; -+ router.i2c_info = -+ amdgpu_atombios_lookup_i2c_gpio(adev, -+ i2c_config-> -+ ucAccess); -+ router.i2c_addr = i2c_record->ucI2CAddr >> 1; -+ break; -+ case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: -+ ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) -+ record; -+ router.ddc_valid = true; -+ router.ddc_mux_type = ddc_path->ucMuxType; -+ router.ddc_mux_control_pin = ddc_path->ucMuxControlPin; -+ router.ddc_mux_state = ddc_path->ucMuxState[enum_id]; -+ break; -+ case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: -+ cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *) -+ record; -+ router.cd_valid = true; -+ router.cd_mux_type = cd_path->ucMuxType; -+ router.cd_mux_control_pin = cd_path->ucMuxControlPin; -+ router.cd_mux_state = cd_path->ucMuxState[enum_id]; -+ break; -+ } -+ record = (ATOM_COMMON_RECORD_HEADER *) -+ ((char *)record + record->ucRecordSize); -+ } -+ } -+ } -+ } -+ } -+ -+ /* look up gpio for ddc, hpd */ -+ ddc_bus.valid = false; -+ hpd.hpd = AMDGPU_HPD_NONE; -+ if ((le16_to_cpu(path->usDeviceTag) & -+ (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) == 0) { -+ for (j = 0; j < con_obj->ucNumberOfObjects; j++) { -+ if (le16_to_cpu(path->usConnObjectId) == -+ le16_to_cpu(con_obj->asObjects[j]. -+ usObjectID)) { -+ ATOM_COMMON_RECORD_HEADER -+ *record = -+ (ATOM_COMMON_RECORD_HEADER -+ *) -+ (ctx->bios + data_offset + -+ le16_to_cpu(con_obj-> -+ asObjects[j]. -+ usRecordOffset)); -+ ATOM_I2C_RECORD *i2c_record; -+ ATOM_HPD_INT_RECORD *hpd_record; -+ ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; -+ -+ while (record->ucRecordSize > 0 && -+ record->ucRecordType > 0 && -+ record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { -+ switch (record->ucRecordType) { -+ case ATOM_I2C_RECORD_TYPE: -+ i2c_record = -+ (ATOM_I2C_RECORD *) -+ record; -+ i2c_config = -+ (ATOM_I2C_ID_CONFIG_ACCESS *) -+ &i2c_record->sucI2cId; -+ ddc_bus = amdgpu_atombios_lookup_i2c_gpio(adev, -+ i2c_config-> -+ ucAccess); -+ break; -+ case ATOM_HPD_INT_RECORD_TYPE: -+ hpd_record = -+ (ATOM_HPD_INT_RECORD *) -+ record; -+ gpio = amdgpu_atombios_lookup_gpio(adev, -+ hpd_record->ucHPDIntGPIOID); -+ hpd = amdgpu_atombios_get_hpd_info_from_gpio(adev, &gpio); -+ hpd.plugged_state = hpd_record->ucPlugged_PinState; -+ break; -+ } -+ record = -+ (ATOM_COMMON_RECORD_HEADER -+ *) ((char *)record -+ + -+ record-> -+ ucRecordSize); -+ } -+ break; -+ } -+ } -+ } -+ -+ /* needed for aux chan transactions */ -+ ddc_bus.hpd = hpd.hpd; -+ -+ conn_id = le16_to_cpu(path->usConnObjectId); -+ -+ if (!amdgpu_atombios_apply_quirks -+ (adev, le16_to_cpu(path->usDeviceTag), &connector_type, -+ &ddc_bus, &conn_id, &hpd)) -+ continue; -+ -+ amdgpu_display_add_connector(adev, -+ conn_id, -+ le16_to_cpu(path->usDeviceTag), -+ connector_type, &ddc_bus, -+ connector_object_id, -+ &hpd, -+ &router); -+ -+ } -+ } -+ -+ amdgpu_link_encoder_connector(adev->ddev); -+ -+ return true; -+} -+ -+union firmware_info { -+ ATOM_FIRMWARE_INFO info; -+ ATOM_FIRMWARE_INFO_V1_2 info_12; -+ ATOM_FIRMWARE_INFO_V1_3 info_13; -+ ATOM_FIRMWARE_INFO_V1_4 info_14; -+ ATOM_FIRMWARE_INFO_V2_1 info_21; -+ ATOM_FIRMWARE_INFO_V2_2 info_22; -+}; -+ -+int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); -+ uint8_t frev, crev; -+ uint16_t data_offset; -+ int ret = -EINVAL; -+ -+ if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, -+ &frev, &crev, &data_offset)) { -+ int i; -+ struct amdgpu_pll *ppll = &adev->clock.ppll[0]; -+ struct amdgpu_pll *spll = &adev->clock.spll; -+ struct amdgpu_pll *mpll = &adev->clock.mpll; -+ union firmware_info *firmware_info = -+ (union firmware_info *)(mode_info->atom_context->bios + -+ data_offset); -+ /* pixel clocks */ -+ ppll->reference_freq = -+ le16_to_cpu(firmware_info->info.usReferenceClock); -+ ppll->reference_div = 0; -+ -+ if (crev < 2) -+ ppll->pll_out_min = -+ le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); -+ else -+ ppll->pll_out_min = -+ le32_to_cpu(firmware_info->info_12.ulMinPixelClockPLL_Output); -+ ppll->pll_out_max = -+ le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output); -+ -+ if (crev >= 4) { -+ ppll->lcd_pll_out_min = -+ le16_to_cpu(firmware_info->info_14.usLcdMinPixelClockPLL_Output) * 100; -+ if (ppll->lcd_pll_out_min == 0) -+ ppll->lcd_pll_out_min = ppll->pll_out_min; -+ ppll->lcd_pll_out_max = -+ le16_to_cpu(firmware_info->info_14.usLcdMaxPixelClockPLL_Output) * 100; -+ if (ppll->lcd_pll_out_max == 0) -+ ppll->lcd_pll_out_max = ppll->pll_out_max; -+ } else { -+ ppll->lcd_pll_out_min = ppll->pll_out_min; -+ ppll->lcd_pll_out_max = ppll->pll_out_max; -+ } -+ -+ if (ppll->pll_out_min == 0) -+ ppll->pll_out_min = 64800; -+ -+ ppll->pll_in_min = -+ le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Input); -+ ppll->pll_in_max = -+ le16_to_cpu(firmware_info->info.usMaxPixelClockPLL_Input); -+ -+ ppll->min_post_div = 2; -+ ppll->max_post_div = 0x7f; -+ ppll->min_frac_feedback_div = 0; -+ ppll->max_frac_feedback_div = 9; -+ ppll->min_ref_div = 2; -+ ppll->max_ref_div = 0x3ff; -+ ppll->min_feedback_div = 4; -+ ppll->max_feedback_div = 0xfff; -+ ppll->best_vco = 0; -+ -+ for (i = 1; i < AMDGPU_MAX_PPLL; i++) -+ adev->clock.ppll[i] = *ppll; -+ -+ /* system clock */ -+ spll->reference_freq = -+ le16_to_cpu(firmware_info->info_21.usCoreReferenceClock); -+ spll->reference_div = 0; -+ -+ spll->pll_out_min = -+ le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Output); -+ spll->pll_out_max = -+ le32_to_cpu(firmware_info->info.ulMaxEngineClockPLL_Output); -+ -+ /* ??? */ -+ if (spll->pll_out_min == 0) -+ spll->pll_out_min = 64800; -+ -+ spll->pll_in_min = -+ le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Input); -+ spll->pll_in_max = -+ le16_to_cpu(firmware_info->info.usMaxEngineClockPLL_Input); -+ -+ spll->min_post_div = 1; -+ spll->max_post_div = 1; -+ spll->min_ref_div = 2; -+ spll->max_ref_div = 0xff; -+ spll->min_feedback_div = 4; -+ spll->max_feedback_div = 0xff; -+ spll->best_vco = 0; -+ -+ /* memory clock */ -+ mpll->reference_freq = -+ le16_to_cpu(firmware_info->info_21.usMemoryReferenceClock); -+ mpll->reference_div = 0; -+ -+ mpll->pll_out_min = -+ le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Output); -+ mpll->pll_out_max = -+ le32_to_cpu(firmware_info->info.ulMaxMemoryClockPLL_Output); -+ -+ /* ??? */ -+ if (mpll->pll_out_min == 0) -+ mpll->pll_out_min = 64800; -+ -+ mpll->pll_in_min = -+ le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Input); -+ mpll->pll_in_max = -+ le16_to_cpu(firmware_info->info.usMaxMemoryClockPLL_Input); -+ -+ adev->clock.default_sclk = -+ le32_to_cpu(firmware_info->info.ulDefaultEngineClock); -+ adev->clock.default_mclk = -+ le32_to_cpu(firmware_info->info.ulDefaultMemoryClock); -+ -+ mpll->min_post_div = 1; -+ mpll->max_post_div = 1; -+ mpll->min_ref_div = 2; -+ mpll->max_ref_div = 0xff; -+ mpll->min_feedback_div = 4; -+ mpll->max_feedback_div = 0xff; -+ mpll->best_vco = 0; -+ -+ /* disp clock */ -+ adev->clock.default_dispclk = -+ le32_to_cpu(firmware_info->info_21.ulDefaultDispEngineClkFreq); -+ if (adev->clock.default_dispclk == 0) -+ adev->clock.default_dispclk = 54000; /* 540 Mhz */ -+ adev->clock.dp_extclk = -+ le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq); -+ adev->clock.current_dispclk = adev->clock.default_dispclk; -+ -+ adev->clock.max_pixel_clock = le16_to_cpu(firmware_info->info.usMaxPixelClock); -+ if (adev->clock.max_pixel_clock == 0) -+ adev->clock.max_pixel_clock = 40000; -+ -+ /* not technically a clock, but... */ -+ adev->mode_info.firmware_flags = -+ le16_to_cpu(firmware_info->info.usFirmwareCapability.susAccess); -+ -+ ret = 0; -+ } -+ -+ adev->pm.current_sclk = adev->clock.default_sclk; -+ adev->pm.current_mclk = adev->clock.default_mclk; -+ -+ return ret; -+} -+ -+union igp_info { -+ struct _ATOM_INTEGRATED_SYSTEM_INFO info; -+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; -+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6; -+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; -+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8; -+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_9 info_9; -+}; -+ -+static void amdgpu_atombios_get_igp_ss_overrides(struct amdgpu_device *adev, -+ struct amdgpu_atom_ss *ss, -+ int id) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); -+ u16 data_offset, size; -+ union igp_info *igp_info; -+ u8 frev, crev; -+ u16 percentage = 0, rate = 0; -+ -+ /* get any igp specific overrides */ -+ if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, &size, -+ &frev, &crev, &data_offset)) { -+ igp_info = (union igp_info *) -+ (mode_info->atom_context->bios + data_offset); -+ switch (crev) { -+ case 6: -+ switch (id) { -+ case ASIC_INTERNAL_SS_ON_TMDS: -+ percentage = le16_to_cpu(igp_info->info_6.usDVISSPercentage); -+ rate = le16_to_cpu(igp_info->info_6.usDVISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_HDMI: -+ percentage = le16_to_cpu(igp_info->info_6.usHDMISSPercentage); -+ rate = le16_to_cpu(igp_info->info_6.usHDMISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_LVDS: -+ percentage = le16_to_cpu(igp_info->info_6.usLvdsSSPercentage); -+ rate = le16_to_cpu(igp_info->info_6.usLvdsSSpreadRateIn10Hz); -+ break; -+ } -+ break; -+ case 7: -+ switch (id) { -+ case ASIC_INTERNAL_SS_ON_TMDS: -+ percentage = le16_to_cpu(igp_info->info_7.usDVISSPercentage); -+ rate = le16_to_cpu(igp_info->info_7.usDVISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_HDMI: -+ percentage = le16_to_cpu(igp_info->info_7.usHDMISSPercentage); -+ rate = le16_to_cpu(igp_info->info_7.usHDMISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_LVDS: -+ percentage = le16_to_cpu(igp_info->info_7.usLvdsSSPercentage); -+ rate = le16_to_cpu(igp_info->info_7.usLvdsSSpreadRateIn10Hz); -+ break; -+ } -+ break; -+ case 8: -+ switch (id) { -+ case ASIC_INTERNAL_SS_ON_TMDS: -+ percentage = le16_to_cpu(igp_info->info_8.usDVISSPercentage); -+ rate = le16_to_cpu(igp_info->info_8.usDVISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_HDMI: -+ percentage = le16_to_cpu(igp_info->info_8.usHDMISSPercentage); -+ rate = le16_to_cpu(igp_info->info_8.usHDMISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_LVDS: -+ percentage = le16_to_cpu(igp_info->info_8.usLvdsSSPercentage); -+ rate = le16_to_cpu(igp_info->info_8.usLvdsSSpreadRateIn10Hz); -+ break; -+ } -+ break; -+ case 9: -+ switch (id) { -+ case ASIC_INTERNAL_SS_ON_TMDS: -+ percentage = le16_to_cpu(igp_info->info_9.usDVISSPercentage); -+ rate = le16_to_cpu(igp_info->info_9.usDVISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_HDMI: -+ percentage = le16_to_cpu(igp_info->info_9.usHDMISSPercentage); -+ rate = le16_to_cpu(igp_info->info_9.usHDMISSpreadRateIn10Hz); -+ break; -+ case ASIC_INTERNAL_SS_ON_LVDS: -+ percentage = le16_to_cpu(igp_info->info_9.usLvdsSSPercentage); -+ rate = le16_to_cpu(igp_info->info_9.usLvdsSSpreadRateIn10Hz); -+ break; -+ } -+ break; -+ default: -+ DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); -+ break; -+ } -+ if (percentage) -+ ss->percentage = percentage; -+ if (rate) -+ ss->rate = rate; -+ } -+} -+ -+union asic_ss_info { -+ struct _ATOM_ASIC_INTERNAL_SS_INFO info; -+ struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2; -+ struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3; -+}; -+ -+union asic_ss_assignment { -+ struct _ATOM_ASIC_SS_ASSIGNMENT v1; -+ struct _ATOM_ASIC_SS_ASSIGNMENT_V2 v2; -+ struct _ATOM_ASIC_SS_ASSIGNMENT_V3 v3; -+}; -+ -+bool amdgpu_atombios_get_asic_ss_info(struct amdgpu_device *adev, -+ struct amdgpu_atom_ss *ss, -+ int id, u32 clock) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); -+ uint16_t data_offset, size; -+ union asic_ss_info *ss_info; -+ union asic_ss_assignment *ss_assign; -+ uint8_t frev, crev; -+ int i, num_indices; -+ -+ if (id == ASIC_INTERNAL_MEMORY_SS) { -+ if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT)) -+ return false; -+ } -+ if (id == ASIC_INTERNAL_ENGINE_SS) { -+ if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT)) -+ return false; -+ } -+ -+ memset(ss, 0, sizeof(struct amdgpu_atom_ss)); -+ if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, &size, -+ &frev, &crev, &data_offset)) { -+ -+ ss_info = -+ (union asic_ss_info *)(mode_info->atom_context->bios + data_offset); -+ -+ switch (frev) { -+ case 1: -+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / -+ sizeof(ATOM_ASIC_SS_ASSIGNMENT); -+ -+ ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info.asSpreadSpectrum[0]); -+ for (i = 0; i < num_indices; i++) { -+ if ((ss_assign->v1.ucClockIndication == id) && -+ (clock <= le32_to_cpu(ss_assign->v1.ulTargetClockRange))) { -+ ss->percentage = -+ le16_to_cpu(ss_assign->v1.usSpreadSpectrumPercentage); -+ ss->type = ss_assign->v1.ucSpreadSpectrumMode; -+ ss->rate = le16_to_cpu(ss_assign->v1.usSpreadRateInKhz); -+ ss->percentage_divider = 100; -+ return true; -+ } -+ ss_assign = (union asic_ss_assignment *) -+ ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT)); -+ } -+ break; -+ case 2: -+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / -+ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); -+ ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info_2.asSpreadSpectrum[0]); -+ for (i = 0; i < num_indices; i++) { -+ if ((ss_assign->v2.ucClockIndication == id) && -+ (clock <= le32_to_cpu(ss_assign->v2.ulTargetClockRange))) { -+ ss->percentage = -+ le16_to_cpu(ss_assign->v2.usSpreadSpectrumPercentage); -+ ss->type = ss_assign->v2.ucSpreadSpectrumMode; -+ ss->rate = le16_to_cpu(ss_assign->v2.usSpreadRateIn10Hz); -+ ss->percentage_divider = 100; -+ if ((crev == 2) && -+ ((id == ASIC_INTERNAL_ENGINE_SS) || -+ (id == ASIC_INTERNAL_MEMORY_SS))) -+ ss->rate /= 100; -+ return true; -+ } -+ ss_assign = (union asic_ss_assignment *) -+ ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2)); -+ } -+ break; -+ case 3: -+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / -+ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); -+ ss_assign = (union asic_ss_assignment *)((u8 *)&ss_info->info_3.asSpreadSpectrum[0]); -+ for (i = 0; i < num_indices; i++) { -+ if ((ss_assign->v3.ucClockIndication == id) && -+ (clock <= le32_to_cpu(ss_assign->v3.ulTargetClockRange))) { -+ ss->percentage = -+ le16_to_cpu(ss_assign->v3.usSpreadSpectrumPercentage); -+ ss->type = ss_assign->v3.ucSpreadSpectrumMode; -+ ss->rate = le16_to_cpu(ss_assign->v3.usSpreadRateIn10Hz); -+ if (ss_assign->v3.ucSpreadSpectrumMode & -+ SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK) -+ ss->percentage_divider = 1000; -+ else -+ ss->percentage_divider = 100; -+ if ((id == ASIC_INTERNAL_ENGINE_SS) || -+ (id == ASIC_INTERNAL_MEMORY_SS)) -+ ss->rate /= 100; -+ if (adev->flags & AMDGPU_IS_APU) -+ amdgpu_atombios_get_igp_ss_overrides(adev, ss, id); -+ return true; -+ } -+ ss_assign = (union asic_ss_assignment *) -+ ((u8 *)ss_assign + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3)); -+ } -+ break; -+ default: -+ DRM_ERROR("Unsupported ASIC_InternalSS_Info table: %d %d\n", frev, crev); -+ break; -+ } -+ -+ } -+ return false; -+} -+ -+union get_clock_dividers { -+ struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS v1; -+ struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 v2; -+ struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 v3; -+ struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 v4; -+ struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 v5; -+ struct _COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6 v6_in; -+ struct _COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 v6_out; -+}; -+ -+int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev, -+ u8 clock_type, -+ u32 clock, -+ bool strobe_mode, -+ struct atom_clock_dividers *dividers) -+{ -+ union get_clock_dividers args; -+ int index = GetIndexIntoMasterTable(COMMAND, ComputeMemoryEnginePLL); -+ u8 frev, crev; -+ -+ memset(&args, 0, sizeof(args)); -+ memset(dividers, 0, sizeof(struct atom_clock_dividers)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return -EINVAL; -+ -+ switch (crev) { -+ case 4: -+ /* fusion */ -+ args.v4.ulClock = cpu_to_le32(clock); /* 10 khz */ -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ dividers->post_divider = dividers->post_div = args.v4.ucPostDiv; -+ dividers->real_clock = le32_to_cpu(args.v4.ulClock); -+ break; -+ case 6: -+ /* CI */ -+ /* COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, COMPUTE_GPUCLK_INPUT_FLAG_SCLK */ -+ args.v6_in.ulClock.ulComputeClockFlag = clock_type; -+ args.v6_in.ulClock.ulClockFreq = cpu_to_le32(clock); /* 10 khz */ -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ dividers->whole_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDiv); -+ dividers->frac_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDivFrac); -+ dividers->ref_div = args.v6_out.ucPllRefDiv; -+ dividers->post_div = args.v6_out.ucPllPostDiv; -+ dividers->flags = args.v6_out.ucPllCntlFlag; -+ dividers->real_clock = le32_to_cpu(args.v6_out.ulClock.ulClock); -+ dividers->post_divider = args.v6_out.ulClock.ucPostDiv; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+int amdgpu_atombios_get_memory_pll_dividers(struct amdgpu_device *adev, -+ u32 clock, -+ bool strobe_mode, -+ struct atom_mpll_param *mpll_param) -+{ -+ COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1 args; -+ int index = GetIndexIntoMasterTable(COMMAND, ComputeMemoryClockParam); -+ u8 frev, crev; -+ -+ memset(&args, 0, sizeof(args)); -+ memset(mpll_param, 0, sizeof(struct atom_mpll_param)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return -EINVAL; -+ -+ switch (frev) { -+ case 2: -+ switch (crev) { -+ case 1: -+ /* SI */ -+ args.ulClock = cpu_to_le32(clock); /* 10 khz */ -+ args.ucInputFlag = 0; -+ if (strobe_mode) -+ args.ucInputFlag |= MPLL_INPUT_FLAG_STROBE_MODE_EN; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ mpll_param->clkfrac = le16_to_cpu(args.ulFbDiv.usFbDivFrac); -+ mpll_param->clkf = le16_to_cpu(args.ulFbDiv.usFbDiv); -+ mpll_param->post_div = args.ucPostDiv; -+ mpll_param->dll_speed = args.ucDllSpeed; -+ mpll_param->bwcntl = args.ucBWCntl; -+ mpll_param->vco_mode = -+ (args.ucPllCntlFlag & MPLL_CNTL_FLAG_VCO_MODE_MASK); -+ mpll_param->yclk_sel = -+ (args.ucPllCntlFlag & MPLL_CNTL_FLAG_BYPASS_DQ_PLL) ? 1 : 0; -+ mpll_param->qdr = -+ (args.ucPllCntlFlag & MPLL_CNTL_FLAG_QDR_ENABLE) ? 1 : 0; -+ mpll_param->half_rate = -+ (args.ucPllCntlFlag & MPLL_CNTL_FLAG_AD_HALF_RATE) ? 1 : 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+uint32_t amdgpu_atombios_get_engine_clock(struct amdgpu_device *adev) -+{ -+ GET_ENGINE_CLOCK_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, GetEngineClock); -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ return le32_to_cpu(args.ulReturnEngineClock); -+} -+ -+uint32_t amdgpu_atombios_get_memory_clock(struct amdgpu_device *adev) -+{ -+ GET_MEMORY_CLOCK_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, GetMemoryClock); -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ return le32_to_cpu(args.ulReturnMemoryClock); -+} -+ -+void amdgpu_atombios_set_engine_clock(struct amdgpu_device *adev, -+ uint32_t eng_clock) -+{ -+ SET_ENGINE_CLOCK_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, SetEngineClock); -+ -+ args.ulTargetEngineClock = cpu_to_le32(eng_clock); /* 10 khz */ -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_set_memory_clock(struct amdgpu_device *adev, -+ uint32_t mem_clock) -+{ -+ SET_MEMORY_CLOCK_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, SetMemoryClock); -+ -+ if (adev->flags & AMDGPU_IS_APU) -+ return; -+ -+ args.ulTargetMemoryClock = cpu_to_le32(mem_clock); /* 10 khz */ -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev, -+ u32 eng_clock, u32 mem_clock) -+{ -+ SET_ENGINE_CLOCK_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings); -+ u32 tmp; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ tmp = eng_clock & SET_CLOCK_FREQ_MASK; -+ tmp |= (COMPUTE_ENGINE_PLL_PARAM << 24); -+ -+ args.ulTargetEngineClock = cpu_to_le32(tmp); -+ if (mem_clock) -+ args.sReserved.ulClock = cpu_to_le32(mem_clock & SET_CLOCK_FREQ_MASK); -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+union set_voltage { -+ struct _SET_VOLTAGE_PS_ALLOCATION alloc; -+ struct _SET_VOLTAGE_PARAMETERS v1; -+ struct _SET_VOLTAGE_PARAMETERS_V2 v2; -+ struct _SET_VOLTAGE_PARAMETERS_V1_3 v3; -+}; -+ -+void amdgpu_atombios_set_voltage(struct amdgpu_device *adev, -+ u16 voltage_level, -+ u8 voltage_type) -+{ -+ union set_voltage args; -+ int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); -+ u8 frev, crev, volt_index = voltage_level; -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return; -+ -+ /* 0xff01 is a flag rather then an actual voltage */ -+ if (voltage_level == 0xff01) -+ return; -+ -+ switch (crev) { -+ case 1: -+ args.v1.ucVoltageType = voltage_type; -+ args.v1.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_ALL_SOURCE; -+ args.v1.ucVoltageIndex = volt_index; -+ break; -+ case 2: -+ args.v2.ucVoltageType = voltage_type; -+ args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE; -+ args.v2.usVoltageLevel = cpu_to_le16(voltage_level); -+ break; -+ case 3: -+ args.v3.ucVoltageType = voltage_type; -+ args.v3.ucVoltageMode = ATOM_SET_VOLTAGE; -+ args.v3.usVoltageLevel = cpu_to_le16(voltage_level); -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ return; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+int amdgpu_atombios_get_leakage_id_from_vbios(struct amdgpu_device *adev, -+ u16 *leakage_id) -+{ -+ union set_voltage args; -+ int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); -+ u8 frev, crev; -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return -EINVAL; -+ -+ switch (crev) { -+ case 3: -+ case 4: -+ args.v3.ucVoltageType = 0; -+ args.v3.ucVoltageMode = ATOM_GET_LEAKAGE_ID; -+ args.v3.usVoltageLevel = 0; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ *leakage_id = le16_to_cpu(args.v3.usVoltageLevel); -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int amdgpu_atombios_get_leakage_vddc_based_on_leakage_params(struct amdgpu_device *adev, -+ u16 *vddc, u16 *vddci, -+ u16 virtual_voltage_id, -+ u16 vbios_voltage_id) -+{ -+ int index = GetIndexIntoMasterTable(DATA, ASIC_ProfilingInfo); -+ u8 frev, crev; -+ u16 data_offset, size; -+ int i, j; -+ ATOM_ASIC_PROFILING_INFO_V2_1 *profile; -+ u16 *leakage_bin, *vddc_id_buf, *vddc_buf, *vddci_id_buf, *vddci_buf; -+ -+ *vddc = 0; -+ *vddci = 0; -+ -+ if (!amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, -+ &frev, &crev, &data_offset)) -+ return -EINVAL; -+ -+ profile = (ATOM_ASIC_PROFILING_INFO_V2_1 *) -+ (adev->mode_info.atom_context->bios + data_offset); -+ -+ switch (frev) { -+ case 1: -+ return -EINVAL; -+ case 2: -+ switch (crev) { -+ case 1: -+ if (size < sizeof(ATOM_ASIC_PROFILING_INFO_V2_1)) -+ return -EINVAL; -+ leakage_bin = (u16 *) -+ (adev->mode_info.atom_context->bios + data_offset + -+ le16_to_cpu(profile->usLeakageBinArrayOffset)); -+ vddc_id_buf = (u16 *) -+ (adev->mode_info.atom_context->bios + data_offset + -+ le16_to_cpu(profile->usElbVDDC_IdArrayOffset)); -+ vddc_buf = (u16 *) -+ (adev->mode_info.atom_context->bios + data_offset + -+ le16_to_cpu(profile->usElbVDDC_LevelArrayOffset)); -+ vddci_id_buf = (u16 *) -+ (adev->mode_info.atom_context->bios + data_offset + -+ le16_to_cpu(profile->usElbVDDCI_IdArrayOffset)); -+ vddci_buf = (u16 *) -+ (adev->mode_info.atom_context->bios + data_offset + -+ le16_to_cpu(profile->usElbVDDCI_LevelArrayOffset)); -+ -+ if (profile->ucElbVDDC_Num > 0) { -+ for (i = 0; i < profile->ucElbVDDC_Num; i++) { -+ if (vddc_id_buf[i] == virtual_voltage_id) { -+ for (j = 0; j < profile->ucLeakageBinNum; j++) { -+ if (vbios_voltage_id <= leakage_bin[j]) { -+ *vddc = vddc_buf[j * profile->ucElbVDDC_Num + i]; -+ break; -+ } -+ } -+ break; -+ } -+ } -+ } -+ if (profile->ucElbVDDCI_Num > 0) { -+ for (i = 0; i < profile->ucElbVDDCI_Num; i++) { -+ if (vddci_id_buf[i] == virtual_voltage_id) { -+ for (j = 0; j < profile->ucLeakageBinNum; j++) { -+ if (vbios_voltage_id <= leakage_bin[j]) { -+ *vddci = vddci_buf[j * profile->ucElbVDDCI_Num + i]; -+ break; -+ } -+ } -+ break; -+ } -+ } -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ return -EINVAL; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+union get_voltage_info { -+ struct _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2 in; -+ struct _GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2 evv_out; -+}; -+ -+int amdgpu_atombios_get_voltage_evv(struct amdgpu_device *adev, -+ u16 virtual_voltage_id, -+ u16 *voltage) -+{ -+ int index = GetIndexIntoMasterTable(COMMAND, GetVoltageInfo); -+ u32 entry_id; -+ u32 count = adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; -+ union get_voltage_info args; -+ -+ for (entry_id = 0; entry_id < count; entry_id++) { -+ if (adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].v == -+ virtual_voltage_id) -+ break; -+ } -+ -+ if (entry_id >= count) -+ return -EINVAL; -+ -+ args.in.ucVoltageType = VOLTAGE_TYPE_VDDC; -+ args.in.ucVoltageMode = ATOM_GET_VOLTAGE_EVV_VOLTAGE; -+ args.in.usVoltageLevel = cpu_to_le16(virtual_voltage_id); -+ args.in.ulSCLKFreq = -+ cpu_to_le32(adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].clk); -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ *voltage = le16_to_cpu(args.evv_out.usVoltageLevel); -+ -+ return 0; -+} -+ -+union voltage_object_info { -+ struct _ATOM_VOLTAGE_OBJECT_INFO v1; -+ struct _ATOM_VOLTAGE_OBJECT_INFO_V2 v2; -+ struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 v3; -+}; -+ -+union voltage_object { -+ struct _ATOM_VOLTAGE_OBJECT v1; -+ struct _ATOM_VOLTAGE_OBJECT_V2 v2; -+ union _ATOM_VOLTAGE_OBJECT_V3 v3; -+}; -+ -+ -+static ATOM_VOLTAGE_OBJECT_V3 *amdgpu_atombios_lookup_voltage_object_v3(ATOM_VOLTAGE_OBJECT_INFO_V3_1 *v3, -+ u8 voltage_type, u8 voltage_mode) -+{ -+ u32 size = le16_to_cpu(v3->sHeader.usStructureSize); -+ u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]); -+ u8 *start = (u8*)v3; -+ -+ while (offset < size) { -+ ATOM_VOLTAGE_OBJECT_V3 *vo = (ATOM_VOLTAGE_OBJECT_V3 *)(start + offset); -+ if ((vo->asGpioVoltageObj.sHeader.ucVoltageType == voltage_type) && -+ (vo->asGpioVoltageObj.sHeader.ucVoltageMode == voltage_mode)) -+ return vo; -+ offset += le16_to_cpu(vo->asGpioVoltageObj.sHeader.usSize); -+ } -+ return NULL; -+} -+ -+bool -+amdgpu_atombios_is_voltage_gpio(struct amdgpu_device *adev, -+ u8 voltage_type, u8 voltage_mode) -+{ -+ int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); -+ u8 frev, crev; -+ u16 data_offset, size; -+ union voltage_object_info *voltage_info; -+ -+ if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, -+ &frev, &crev, &data_offset)) { -+ voltage_info = (union voltage_object_info *) -+ (adev->mode_info.atom_context->bios + data_offset); -+ -+ switch (frev) { -+ case 3: -+ switch (crev) { -+ case 1: -+ if (amdgpu_atombios_lookup_voltage_object_v3(&voltage_info->v3, -+ voltage_type, voltage_mode)) -+ return true; -+ break; -+ default: -+ DRM_ERROR("unknown voltage object table\n"); -+ return false; -+ } -+ break; -+ default: -+ DRM_ERROR("unknown voltage object table\n"); -+ return false; -+ } -+ -+ } -+ return false; -+} -+ -+int amdgpu_atombios_get_voltage_table(struct amdgpu_device *adev, -+ u8 voltage_type, u8 voltage_mode, -+ struct atom_voltage_table *voltage_table) -+{ -+ int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); -+ u8 frev, crev; -+ u16 data_offset, size; -+ int i; -+ union voltage_object_info *voltage_info; -+ union voltage_object *voltage_object = NULL; -+ -+ if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, -+ &frev, &crev, &data_offset)) { -+ voltage_info = (union voltage_object_info *) -+ (adev->mode_info.atom_context->bios + data_offset); -+ -+ switch (frev) { -+ case 3: -+ switch (crev) { -+ case 1: -+ voltage_object = (union voltage_object *) -+ amdgpu_atombios_lookup_voltage_object_v3(&voltage_info->v3, -+ voltage_type, voltage_mode); -+ if (voltage_object) { -+ ATOM_GPIO_VOLTAGE_OBJECT_V3 *gpio = -+ &voltage_object->v3.asGpioVoltageObj; -+ VOLTAGE_LUT_ENTRY_V2 *lut; -+ if (gpio->ucGpioEntryNum > MAX_VOLTAGE_ENTRIES) -+ return -EINVAL; -+ lut = &gpio->asVolGpioLut[0]; -+ for (i = 0; i < gpio->ucGpioEntryNum; i++) { -+ voltage_table->entries[i].value = -+ le16_to_cpu(lut->usVoltageValue); -+ voltage_table->entries[i].smio_low = -+ le32_to_cpu(lut->ulVoltageId); -+ lut = (VOLTAGE_LUT_ENTRY_V2 *) -+ ((u8 *)lut + sizeof(VOLTAGE_LUT_ENTRY_V2)); -+ } -+ voltage_table->mask_low = le32_to_cpu(gpio->ulGpioMaskVal); -+ voltage_table->count = gpio->ucGpioEntryNum; -+ voltage_table->phase_delay = gpio->ucPhaseDelay; -+ return 0; -+ } -+ break; -+ default: -+ DRM_ERROR("unknown voltage object table\n"); -+ return -EINVAL; -+ } -+ break; -+ default: -+ DRM_ERROR("unknown voltage object table\n"); -+ return -EINVAL; -+ } -+ } -+ return -EINVAL; -+} -+ -+union vram_info { -+ struct _ATOM_VRAM_INFO_V3 v1_3; -+ struct _ATOM_VRAM_INFO_V4 v1_4; -+ struct _ATOM_VRAM_INFO_HEADER_V2_1 v2_1; -+}; -+ -+#define MEM_ID_MASK 0xff000000 -+#define MEM_ID_SHIFT 24 -+#define CLOCK_RANGE_MASK 0x00ffffff -+#define CLOCK_RANGE_SHIFT 0 -+#define LOW_NIBBLE_MASK 0xf -+#define DATA_EQU_PREV 0 -+#define DATA_FROM_TABLE 4 -+ -+int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, -+ u8 module_index, -+ struct atom_mc_reg_table *reg_table) -+{ -+ int index = GetIndexIntoMasterTable(DATA, VRAM_Info); -+ u8 frev, crev, num_entries, t_mem_id, num_ranges = 0; -+ u32 i = 0, j; -+ u16 data_offset, size; -+ union vram_info *vram_info; -+ -+ memset(reg_table, 0, sizeof(struct atom_mc_reg_table)); -+ -+ if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size, -+ &frev, &crev, &data_offset)) { -+ vram_info = (union vram_info *) -+ (adev->mode_info.atom_context->bios + data_offset); -+ switch (frev) { -+ case 1: -+ DRM_ERROR("old table version %d, %d\n", frev, crev); -+ return -EINVAL; -+ case 2: -+ switch (crev) { -+ case 1: -+ if (module_index < vram_info->v2_1.ucNumOfVRAMModule) { -+ ATOM_INIT_REG_BLOCK *reg_block = -+ (ATOM_INIT_REG_BLOCK *) -+ ((u8 *)vram_info + le16_to_cpu(vram_info->v2_1.usMemClkPatchTblOffset)); -+ ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = -+ (ATOM_MEMORY_SETTING_DATA_BLOCK *) -+ ((u8 *)reg_block + (2 * sizeof(u16)) + -+ le16_to_cpu(reg_block->usRegIndexTblSize)); -+ ATOM_INIT_REG_INDEX_FORMAT *format = ®_block->asRegIndexBuf[0]; -+ num_entries = (u8)((le16_to_cpu(reg_block->usRegIndexTblSize)) / -+ sizeof(ATOM_INIT_REG_INDEX_FORMAT)) - 1; -+ if (num_entries > VBIOS_MC_REGISTER_ARRAY_SIZE) -+ return -EINVAL; -+ while (i < num_entries) { -+ if (format->ucPreRegDataLength & ACCESS_PLACEHOLDER) -+ break; -+ reg_table->mc_reg_address[i].s1 = -+ (u16)(le16_to_cpu(format->usRegIndex)); -+ reg_table->mc_reg_address[i].pre_reg_data = -+ (u8)(format->ucPreRegDataLength); -+ i++; -+ format = (ATOM_INIT_REG_INDEX_FORMAT *) -+ ((u8 *)format + sizeof(ATOM_INIT_REG_INDEX_FORMAT)); -+ } -+ reg_table->last = i; -+ while ((le32_to_cpu(*(u32 *)reg_data) != END_OF_REG_DATA_BLOCK) && -+ (num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES)) { -+ t_mem_id = (u8)((le32_to_cpu(*(u32 *)reg_data) & MEM_ID_MASK) -+ >> MEM_ID_SHIFT); -+ if (module_index == t_mem_id) { -+ reg_table->mc_reg_table_entry[num_ranges].mclk_max = -+ (u32)((le32_to_cpu(*(u32 *)reg_data) & CLOCK_RANGE_MASK) -+ >> CLOCK_RANGE_SHIFT); -+ for (i = 0, j = 1; i < reg_table->last; i++) { -+ if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) { -+ reg_table->mc_reg_table_entry[num_ranges].mc_data[i] = -+ (u32)le32_to_cpu(*((u32 *)reg_data + j)); -+ j++; -+ } else if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_EQU_PREV) { -+ reg_table->mc_reg_table_entry[num_ranges].mc_data[i] = -+ reg_table->mc_reg_table_entry[num_ranges].mc_data[i - 1]; -+ } -+ } -+ num_ranges++; -+ } -+ reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) -+ ((u8 *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)); -+ } -+ if (le32_to_cpu(*(u32 *)reg_data) != END_OF_REG_DATA_BLOCK) -+ return -EINVAL; -+ reg_table->num_entries = num_ranges; -+ } else -+ return -EINVAL; -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ return -EINVAL; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ return -EINVAL; -+ } -+ return 0; -+ } -+ return -EINVAL; -+} -+ -+void amdgpu_atombios_scratch_regs_lock(struct amdgpu_device *adev, bool lock) -+{ -+ uint32_t bios_6_scratch; -+ -+ bios_6_scratch = RREG32(mmBIOS_SCRATCH_6); -+ -+ if (lock) { -+ bios_6_scratch |= ATOM_S6_CRITICAL_STATE; -+ bios_6_scratch &= ~ATOM_S6_ACC_MODE; -+ } else { -+ bios_6_scratch &= ~ATOM_S6_CRITICAL_STATE; -+ bios_6_scratch |= ATOM_S6_ACC_MODE; -+ } -+ -+ WREG32(mmBIOS_SCRATCH_6, bios_6_scratch); -+} -+ -+void amdgpu_atombios_scratch_regs_init(struct amdgpu_device *adev) -+{ -+ uint32_t bios_2_scratch, bios_6_scratch; -+ -+ bios_2_scratch = RREG32(mmBIOS_SCRATCH_2); -+ bios_6_scratch = RREG32(mmBIOS_SCRATCH_6); -+ -+ /* let the bios control the backlight */ -+ bios_2_scratch &= ~ATOM_S2_VRI_BRIGHT_ENABLE; -+ -+ /* tell the bios not to handle mode switching */ -+ bios_6_scratch |= ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH; -+ -+ /* clear the vbios dpms state */ -+ bios_2_scratch &= ~ATOM_S2_DEVICE_DPMS_STATE; -+ -+ WREG32(mmBIOS_SCRATCH_2, bios_2_scratch); -+ WREG32(mmBIOS_SCRATCH_6, bios_6_scratch); -+} -+ -+void amdgpu_atombios_scratch_regs_save(struct amdgpu_device *adev) -+{ -+ int i; -+ -+ for (i = 0; i < AMDGPU_BIOS_NUM_SCRATCH; i++) -+ adev->bios_scratch[i] = RREG32(mmBIOS_SCRATCH_0 + i); -+} -+ -+void amdgpu_atombios_scratch_regs_restore(struct amdgpu_device *adev) -+{ -+ int i; -+ -+ for (i = 0; i < AMDGPU_BIOS_NUM_SCRATCH; i++) -+ WREG32(mmBIOS_SCRATCH_0 + i, adev->bios_scratch[i]); -+} -+ -+/* Atom needs data in little endian format -+ * so swap as appropriate when copying data to -+ * or from atom. Note that atom operates on -+ * dw units. -+ */ -+void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) -+{ -+#ifdef __BIG_ENDIAN -+ u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */ -+ u32 *dst32, *src32; -+ int i; -+ -+ memcpy(src_tmp, src, num_bytes); -+ src32 = (u32 *)src_tmp; -+ dst32 = (u32 *)dst_tmp; -+ if (to_le) { -+ for (i = 0; i < ((num_bytes + 3) / 4); i++) -+ dst32[i] = cpu_to_le32(src32[i]); -+ memcpy(dst, dst_tmp, num_bytes); -+ } else { -+ u8 dws = num_bytes & ~3; -+ for (i = 0; i < ((num_bytes + 3) / 4); i++) -+ dst32[i] = le32_to_cpu(src32[i]); -+ memcpy(dst, dst_tmp, dws); -+ if (num_bytes % 4) { -+ for (i = 0; i < (num_bytes % 4); i++) -+ dst[dws+i] = dst_tmp[dws+i]; -+ } -+ } -+#else -+ memcpy(dst, src, num_bytes); -+#endif -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h -new file mode 100644 -index 0000000..0ebb959 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h -@@ -0,0 +1,206 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_ATOMBIOS_H__ -+#define __AMDGPU_ATOMBIOS_H__ -+ -+struct atom_clock_dividers { -+ u32 post_div; -+ union { -+ struct { -+#ifdef __BIG_ENDIAN -+ u32 reserved : 6; -+ u32 whole_fb_div : 12; -+ u32 frac_fb_div : 14; -+#else -+ u32 frac_fb_div : 14; -+ u32 whole_fb_div : 12; -+ u32 reserved : 6; -+#endif -+ }; -+ u32 fb_div; -+ }; -+ u32 ref_div; -+ bool enable_post_div; -+ bool enable_dithen; -+ u32 vco_mode; -+ u32 real_clock; -+ /* added for CI */ -+ u32 post_divider; -+ u32 flags; -+}; -+ -+struct atom_mpll_param { -+ union { -+ struct { -+#ifdef __BIG_ENDIAN -+ u32 reserved : 8; -+ u32 clkfrac : 12; -+ u32 clkf : 12; -+#else -+ u32 clkf : 12; -+ u32 clkfrac : 12; -+ u32 reserved : 8; -+#endif -+ }; -+ u32 fb_div; -+ }; -+ u32 post_div; -+ u32 bwcntl; -+ u32 dll_speed; -+ u32 vco_mode; -+ u32 yclk_sel; -+ u32 qdr; -+ u32 half_rate; -+}; -+ -+#define MEM_TYPE_GDDR5 0x50 -+#define MEM_TYPE_GDDR4 0x40 -+#define MEM_TYPE_GDDR3 0x30 -+#define MEM_TYPE_DDR2 0x20 -+#define MEM_TYPE_GDDR1 0x10 -+#define MEM_TYPE_DDR3 0xb0 -+#define MEM_TYPE_MASK 0xf0 -+ -+struct atom_memory_info { -+ u8 mem_vendor; -+ u8 mem_type; -+}; -+ -+#define MAX_AC_TIMING_ENTRIES 16 -+ -+struct atom_memory_clock_range_table -+{ -+ u8 num_entries; -+ u8 rsv[3]; -+ u32 mclk[MAX_AC_TIMING_ENTRIES]; -+}; -+ -+#define VBIOS_MC_REGISTER_ARRAY_SIZE 32 -+#define VBIOS_MAX_AC_TIMING_ENTRIES 20 -+ -+struct atom_mc_reg_entry { -+ u32 mclk_max; -+ u32 mc_data[VBIOS_MC_REGISTER_ARRAY_SIZE]; -+}; -+ -+struct atom_mc_register_address { -+ u16 s1; -+ u8 pre_reg_data; -+}; -+ -+struct atom_mc_reg_table { -+ u8 last; -+ u8 num_entries; -+ struct atom_mc_reg_entry mc_reg_table_entry[VBIOS_MAX_AC_TIMING_ENTRIES]; -+ struct atom_mc_register_address mc_reg_address[VBIOS_MC_REGISTER_ARRAY_SIZE]; -+}; -+ -+#define MAX_VOLTAGE_ENTRIES 32 -+ -+struct atom_voltage_table_entry -+{ -+ u16 value; -+ u32 smio_low; -+}; -+ -+struct atom_voltage_table -+{ -+ u32 count; -+ u32 mask_low; -+ u32 phase_delay; -+ struct atom_voltage_table_entry entries[MAX_VOLTAGE_ENTRIES]; -+}; -+ -+struct amdgpu_gpio_rec -+amdgpu_atombios_lookup_gpio(struct amdgpu_device *adev, -+ u8 id); -+ -+struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev, -+ uint8_t id); -+void amdgpu_atombios_i2c_init(struct amdgpu_device *adev); -+ -+bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev); -+ -+int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev); -+ -+bool amdgpu_atombios_get_asic_ss_info(struct amdgpu_device *adev, -+ struct amdgpu_atom_ss *ss, -+ int id, u32 clock); -+ -+int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev, -+ u8 clock_type, -+ u32 clock, -+ bool strobe_mode, -+ struct atom_clock_dividers *dividers); -+ -+int amdgpu_atombios_get_memory_pll_dividers(struct amdgpu_device *adev, -+ u32 clock, -+ bool strobe_mode, -+ struct atom_mpll_param *mpll_param); -+ -+uint32_t amdgpu_atombios_get_engine_clock(struct amdgpu_device *adev); -+uint32_t amdgpu_atombios_get_memory_clock(struct amdgpu_device *adev); -+void amdgpu_atombios_set_engine_clock(struct amdgpu_device *adev, -+ uint32_t eng_clock); -+void amdgpu_atombios_set_memory_clock(struct amdgpu_device *adev, -+ uint32_t mem_clock); -+void amdgpu_atombios_set_voltage(struct amdgpu_device *adev, -+ u16 voltage_level, -+ u8 voltage_type); -+ -+void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev, -+ u32 eng_clock, u32 mem_clock); -+ -+int amdgpu_atombios_get_leakage_id_from_vbios(struct amdgpu_device *adev, -+ u16 *leakage_id); -+ -+int amdgpu_atombios_get_leakage_vddc_based_on_leakage_params(struct amdgpu_device *adev, -+ u16 *vddc, u16 *vddci, -+ u16 virtual_voltage_id, -+ u16 vbios_voltage_id); -+ -+int amdgpu_atombios_get_voltage_evv(struct amdgpu_device *adev, -+ u16 virtual_voltage_id, -+ u16 *voltage); -+ -+bool -+amdgpu_atombios_is_voltage_gpio(struct amdgpu_device *adev, -+ u8 voltage_type, u8 voltage_mode); -+ -+int amdgpu_atombios_get_voltage_table(struct amdgpu_device *adev, -+ u8 voltage_type, u8 voltage_mode, -+ struct atom_voltage_table *voltage_table); -+ -+int amdgpu_atombios_init_mc_reg_table(struct amdgpu_device *adev, -+ u8 module_index, -+ struct atom_mc_reg_table *reg_table); -+ -+void amdgpu_atombios_scratch_regs_lock(struct amdgpu_device *adev, bool lock); -+void amdgpu_atombios_scratch_regs_init(struct amdgpu_device *adev); -+void amdgpu_atombios_scratch_regs_save(struct amdgpu_device *adev); -+void amdgpu_atombios_scratch_regs_restore(struct amdgpu_device *adev); -+ -+void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c -new file mode 100644 -index 0000000..3f7aaa4 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c -@@ -0,0 +1,572 @@ -+/* -+ * Copyright (c) 2010 Red Hat Inc. -+ * Author : Dave Airlie <airlied@redhat.com> -+ * -+ * Licensed under GPLv2 -+ * -+ * ATPX support for both Intel/ATI -+ */ -+#include <linux/vga_switcheroo.h> -+#include <linux/slab.h> -+#include <linux/acpi.h> -+#include <linux/pci.h> -+ -+#include "amdgpu_acpi.h" -+ -+struct amdgpu_atpx_functions { -+ bool px_params; -+ bool power_cntl; -+ bool disp_mux_cntl; -+ bool i2c_mux_cntl; -+ bool switch_start; -+ bool switch_end; -+ bool disp_connectors_mapping; -+ bool disp_detetion_ports; -+}; -+ -+struct amdgpu_atpx { -+ acpi_handle handle; -+ struct amdgpu_atpx_functions functions; -+}; -+ -+static struct amdgpu_atpx_priv { -+ bool atpx_detected; -+ /* handle for device - and atpx */ -+ acpi_handle dhandle; -+ acpi_handle other_handle; -+ struct amdgpu_atpx atpx; -+} amdgpu_atpx_priv; -+ -+struct atpx_verify_interface { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u16 version; /* version */ -+ u32 function_bits; /* supported functions bit vector */ -+} __packed; -+ -+struct atpx_px_params { -+ u16 size; /* structure size in bytes (includes size field) */ -+ u32 valid_flags; /* which flags are valid */ -+ u32 flags; /* flags */ -+} __packed; -+ -+struct atpx_power_control { -+ u16 size; -+ u8 dgpu_state; -+} __packed; -+ -+struct atpx_mux { -+ u16 size; -+ u16 mux; -+} __packed; -+ -+bool amdgpu_has_atpx(void) { -+ return amdgpu_atpx_priv.atpx_detected; -+} -+ -+/** -+ * amdgpu_atpx_call - call an ATPX method -+ * -+ * @handle: acpi handle -+ * @function: the ATPX function to execute -+ * @params: ATPX function params -+ * -+ * Executes the requested ATPX function (all asics). -+ * Returns a pointer to the acpi output buffer. -+ */ -+static union acpi_object *amdgpu_atpx_call(acpi_handle handle, int function, -+ struct acpi_buffer *params) -+{ -+ acpi_status status; -+ union acpi_object atpx_arg_elements[2]; -+ struct acpi_object_list atpx_arg; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ -+ atpx_arg.count = 2; -+ atpx_arg.pointer = &atpx_arg_elements[0]; -+ -+ atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; -+ atpx_arg_elements[0].integer.value = function; -+ -+ if (params) { -+ atpx_arg_elements[1].type = ACPI_TYPE_BUFFER; -+ atpx_arg_elements[1].buffer.length = params->length; -+ atpx_arg_elements[1].buffer.pointer = params->pointer; -+ } else { -+ /* We need a second fake parameter */ -+ atpx_arg_elements[1].type = ACPI_TYPE_INTEGER; -+ atpx_arg_elements[1].integer.value = 0; -+ } -+ -+ status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); -+ -+ /* Fail only if calling the method fails and ATPX is supported */ -+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { -+ printk("failed to evaluate ATPX got %s\n", -+ acpi_format_exception(status)); -+ kfree(buffer.pointer); -+ return NULL; -+ } -+ -+ return buffer.pointer; -+} -+ -+/** -+ * amdgpu_atpx_parse_functions - parse supported functions -+ * -+ * @f: supported functions struct -+ * @mask: supported functions mask from ATPX -+ * -+ * Use the supported functions mask from ATPX function -+ * ATPX_FUNCTION_VERIFY_INTERFACE to determine what functions -+ * are supported (all asics). -+ */ -+static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mask) -+{ -+ f->px_params = mask & ATPX_GET_PX_PARAMETERS_SUPPORTED; -+ f->power_cntl = mask & ATPX_POWER_CONTROL_SUPPORTED; -+ f->disp_mux_cntl = mask & ATPX_DISPLAY_MUX_CONTROL_SUPPORTED; -+ f->i2c_mux_cntl = mask & ATPX_I2C_MUX_CONTROL_SUPPORTED; -+ f->switch_start = mask & ATPX_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION_SUPPORTED; -+ f->switch_end = mask & ATPX_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION_SUPPORTED; -+ f->disp_connectors_mapping = mask & ATPX_GET_DISPLAY_CONNECTORS_MAPPING_SUPPORTED; -+ f->disp_detetion_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED; -+} -+ -+/** -+ * amdgpu_atpx_validate_functions - validate ATPX functions -+ * -+ * @atpx: amdgpu atpx struct -+ * -+ * Validate that required functions are enabled (all asics). -+ * returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) -+{ -+ /* make sure required functions are enabled */ -+ /* dGPU power control is required */ -+ atpx->functions.power_cntl = true; -+ -+ if (atpx->functions.px_params) { -+ union acpi_object *info; -+ struct atpx_px_params output; -+ size_t size; -+ u32 valid_bits; -+ -+ info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_GET_PX_PARAMETERS, NULL); -+ if (!info) -+ return -EIO; -+ -+ memset(&output, 0, sizeof(output)); -+ -+ size = *(u16 *) info->buffer.pointer; -+ if (size < 10) { -+ printk("ATPX buffer is too small: %zu\n", size); -+ kfree(info); -+ return -EINVAL; -+ } -+ size = min(sizeof(output), size); -+ -+ memcpy(&output, info->buffer.pointer, size); -+ -+ valid_bits = output.flags & output.valid_flags; -+ /* if separate mux flag is set, mux controls are required */ -+ if (valid_bits & ATPX_SEPARATE_MUX_FOR_I2C) { -+ atpx->functions.i2c_mux_cntl = true; -+ atpx->functions.disp_mux_cntl = true; -+ } -+ /* if any outputs are muxed, mux controls are required */ -+ if (valid_bits & (ATPX_CRT1_RGB_SIGNAL_MUXED | -+ ATPX_TV_SIGNAL_MUXED | -+ ATPX_DFP_SIGNAL_MUXED)) -+ atpx->functions.disp_mux_cntl = true; -+ -+ kfree(info); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_verify_interface - verify ATPX -+ * -+ * @atpx: amdgpu atpx struct -+ * -+ * Execute the ATPX_FUNCTION_VERIFY_INTERFACE ATPX function -+ * to initialize ATPX and determine what features are supported -+ * (all asics). -+ * returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx) -+{ -+ union acpi_object *info; -+ struct atpx_verify_interface output; -+ size_t size; -+ int err = 0; -+ -+ info = amdgpu_atpx_call(atpx->handle, ATPX_FUNCTION_VERIFY_INTERFACE, NULL); -+ if (!info) -+ return -EIO; -+ -+ memset(&output, 0, sizeof(output)); -+ -+ size = *(u16 *) info->buffer.pointer; -+ if (size < 8) { -+ printk("ATPX buffer is too small: %zu\n", size); -+ err = -EINVAL; -+ goto out; -+ } -+ size = min(sizeof(output), size); -+ -+ memcpy(&output, info->buffer.pointer, size); -+ -+ /* TODO: check version? */ -+ printk("ATPX version %u, functions 0x%08x\n", -+ output.version, output.function_bits); -+ -+ amdgpu_atpx_parse_functions(&atpx->functions, output.function_bits); -+ -+out: -+ kfree(info); -+ return err; -+} -+ -+/** -+ * amdgpu_atpx_set_discrete_state - power up/down discrete GPU -+ * -+ * @atpx: atpx info struct -+ * @state: discrete GPU state (0 = power down, 1 = power up) -+ * -+ * Execute the ATPX_FUNCTION_POWER_CONTROL ATPX function to -+ * power down/up the discrete GPU (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_set_discrete_state(struct amdgpu_atpx *atpx, u8 state) -+{ -+ struct acpi_buffer params; -+ union acpi_object *info; -+ struct atpx_power_control input; -+ -+ if (atpx->functions.power_cntl) { -+ input.size = 3; -+ input.dgpu_state = state; -+ params.length = input.size; -+ params.pointer = &input; -+ info = amdgpu_atpx_call(atpx->handle, -+ ATPX_FUNCTION_POWER_CONTROL, -+ ¶ms); -+ if (!info) -+ return -EIO; -+ kfree(info); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_switch_disp_mux - switch display mux -+ * -+ * @atpx: atpx info struct -+ * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) -+ * -+ * Execute the ATPX_FUNCTION_DISPLAY_MUX_CONTROL ATPX function to -+ * switch the display mux between the discrete GPU and integrated GPU -+ * (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_switch_disp_mux(struct amdgpu_atpx *atpx, u16 mux_id) -+{ -+ struct acpi_buffer params; -+ union acpi_object *info; -+ struct atpx_mux input; -+ -+ if (atpx->functions.disp_mux_cntl) { -+ input.size = 4; -+ input.mux = mux_id; -+ params.length = input.size; -+ params.pointer = &input; -+ info = amdgpu_atpx_call(atpx->handle, -+ ATPX_FUNCTION_DISPLAY_MUX_CONTROL, -+ ¶ms); -+ if (!info) -+ return -EIO; -+ kfree(info); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_switch_i2c_mux - switch i2c/hpd mux -+ * -+ * @atpx: atpx info struct -+ * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) -+ * -+ * Execute the ATPX_FUNCTION_I2C_MUX_CONTROL ATPX function to -+ * switch the i2c/hpd mux between the discrete GPU and integrated GPU -+ * (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_switch_i2c_mux(struct amdgpu_atpx *atpx, u16 mux_id) -+{ -+ struct acpi_buffer params; -+ union acpi_object *info; -+ struct atpx_mux input; -+ -+ if (atpx->functions.i2c_mux_cntl) { -+ input.size = 4; -+ input.mux = mux_id; -+ params.length = input.size; -+ params.pointer = &input; -+ info = amdgpu_atpx_call(atpx->handle, -+ ATPX_FUNCTION_I2C_MUX_CONTROL, -+ ¶ms); -+ if (!info) -+ return -EIO; -+ kfree(info); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_switch_start - notify the sbios of a GPU switch -+ * -+ * @atpx: atpx info struct -+ * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) -+ * -+ * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION ATPX -+ * function to notify the sbios that a switch between the discrete GPU and -+ * integrated GPU has begun (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_switch_start(struct amdgpu_atpx *atpx, u16 mux_id) -+{ -+ struct acpi_buffer params; -+ union acpi_object *info; -+ struct atpx_mux input; -+ -+ if (atpx->functions.switch_start) { -+ input.size = 4; -+ input.mux = mux_id; -+ params.length = input.size; -+ params.pointer = &input; -+ info = amdgpu_atpx_call(atpx->handle, -+ ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_START_NOTIFICATION, -+ ¶ms); -+ if (!info) -+ return -EIO; -+ kfree(info); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_switch_end - notify the sbios of a GPU switch -+ * -+ * @atpx: atpx info struct -+ * @mux_id: mux state (0 = integrated GPU, 1 = discrete GPU) -+ * -+ * Execute the ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION ATPX -+ * function to notify the sbios that a switch between the discrete GPU and -+ * integrated GPU has ended (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_switch_end(struct amdgpu_atpx *atpx, u16 mux_id) -+{ -+ struct acpi_buffer params; -+ union acpi_object *info; -+ struct atpx_mux input; -+ -+ if (atpx->functions.switch_end) { -+ input.size = 4; -+ input.mux = mux_id; -+ params.length = input.size; -+ params.pointer = &input; -+ info = amdgpu_atpx_call(atpx->handle, -+ ATPX_FUNCTION_GRAPHICS_DEVICE_SWITCH_END_NOTIFICATION, -+ ¶ms); -+ if (!info) -+ return -EIO; -+ kfree(info); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_switchto - switch to the requested GPU -+ * -+ * @id: GPU to switch to -+ * -+ * Execute the necessary ATPX functions to switch between the discrete GPU and -+ * integrated GPU (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_switchto(enum vga_switcheroo_client_id id) -+{ -+ u16 gpu_id; -+ -+ if (id == VGA_SWITCHEROO_IGD) -+ gpu_id = ATPX_INTEGRATED_GPU; -+ else -+ gpu_id = ATPX_DISCRETE_GPU; -+ -+ amdgpu_atpx_switch_start(&amdgpu_atpx_priv.atpx, gpu_id); -+ amdgpu_atpx_switch_disp_mux(&amdgpu_atpx_priv.atpx, gpu_id); -+ amdgpu_atpx_switch_i2c_mux(&amdgpu_atpx_priv.atpx, gpu_id); -+ amdgpu_atpx_switch_end(&amdgpu_atpx_priv.atpx, gpu_id); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_power_state - power down/up the requested GPU -+ * -+ * @id: GPU to power down/up -+ * @state: requested power state (0 = off, 1 = on) -+ * -+ * Execute the necessary ATPX function to power down/up the discrete GPU -+ * (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_power_state(enum vga_switcheroo_client_id id, -+ enum vga_switcheroo_state state) -+{ -+ /* on w500 ACPI can't change intel gpu state */ -+ if (id == VGA_SWITCHEROO_IGD) -+ return 0; -+ -+ amdgpu_atpx_set_discrete_state(&amdgpu_atpx_priv.atpx, state); -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_pci_probe_handle - look up the ATPX handle -+ * -+ * @pdev: pci device -+ * -+ * Look up the ATPX handles (all asics). -+ * Returns true if the handles are found, false if not. -+ */ -+static bool amdgpu_atpx_pci_probe_handle(struct pci_dev *pdev) -+{ -+ acpi_handle dhandle, atpx_handle; -+ acpi_status status; -+ -+ dhandle = ACPI_HANDLE(&pdev->dev); -+ if (!dhandle) -+ return false; -+ -+ status = acpi_get_handle(dhandle, "ATPX", &atpx_handle); -+ if (ACPI_FAILURE(status)) { -+ amdgpu_atpx_priv.other_handle = dhandle; -+ return false; -+ } -+ amdgpu_atpx_priv.dhandle = dhandle; -+ amdgpu_atpx_priv.atpx.handle = atpx_handle; -+ return true; -+} -+ -+/** -+ * amdgpu_atpx_init - verify the ATPX interface -+ * -+ * Verify the ATPX interface (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_atpx_init(void) -+{ -+ int r; -+ -+ /* set up the ATPX handle */ -+ r = amdgpu_atpx_verify_interface(&amdgpu_atpx_priv.atpx); -+ if (r) -+ return r; -+ -+ /* validate the atpx setup */ -+ r = amdgpu_atpx_validate(&amdgpu_atpx_priv.atpx); -+ if (r) -+ return r; -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_atpx_get_client_id - get the client id -+ * -+ * @pdev: pci device -+ * -+ * look up whether we are the integrated or discrete GPU (all asics). -+ * Returns the client id. -+ */ -+static int amdgpu_atpx_get_client_id(struct pci_dev *pdev) -+{ -+ if (amdgpu_atpx_priv.dhandle == ACPI_HANDLE(&pdev->dev)) -+ return VGA_SWITCHEROO_IGD; -+ else -+ return VGA_SWITCHEROO_DIS; -+} -+ -+static struct vga_switcheroo_handler amdgpu_atpx_handler = { -+ .switchto = amdgpu_atpx_switchto, -+ .power_state = amdgpu_atpx_power_state, -+ .init = amdgpu_atpx_init, -+ .get_client_id = amdgpu_atpx_get_client_id, -+}; -+ -+/** -+ * amdgpu_atpx_detect - detect whether we have PX -+ * -+ * Check if we have a PX system (all asics). -+ * Returns true if we have a PX system, false if not. -+ */ -+static bool amdgpu_atpx_detect(void) -+{ -+ char acpi_method_name[255] = { 0 }; -+ struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; -+ struct pci_dev *pdev = NULL; -+ bool has_atpx = false; -+ int vga_count = 0; -+ -+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { -+ vga_count++; -+ -+ has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true); -+ } -+ -+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { -+ vga_count++; -+ -+ has_atpx |= (amdgpu_atpx_pci_probe_handle(pdev) == true); -+ } -+ -+ if (has_atpx && vga_count == 2) { -+ acpi_get_name(amdgpu_atpx_priv.atpx.handle, ACPI_FULL_PATHNAME, &buffer); -+ printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n", -+ acpi_method_name); -+ amdgpu_atpx_priv.atpx_detected = true; -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * amdgpu_register_atpx_handler - register with vga_switcheroo -+ * -+ * Register the PX callbacks with vga_switcheroo (all asics). -+ */ -+void amdgpu_register_atpx_handler(void) -+{ -+ bool r; -+ -+ /* detect if we have any ATPX + 2 VGA in the system */ -+ r = amdgpu_atpx_detect(); -+ if (!r) -+ return; -+ -+ vga_switcheroo_register_handler(&amdgpu_atpx_handler); -+} -+ -+/** -+ * amdgpu_unregister_atpx_handler - unregister with vga_switcheroo -+ * -+ * Unregister the PX callbacks with vga_switcheroo (all asics). -+ */ -+void amdgpu_unregister_atpx_handler(void) -+{ -+ vga_switcheroo_unregister_handler(); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c -new file mode 100644 -index 0000000..2742b9a ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c -@@ -0,0 +1,221 @@ -+/* -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+#define AMDGPU_BENCHMARK_ITERATIONS 1024 -+#define AMDGPU_BENCHMARK_COMMON_MODES_N 17 -+ -+static int amdgpu_benchmark_do_move(struct amdgpu_device *adev, unsigned size, -+ uint64_t saddr, uint64_t daddr, int n) -+{ -+ unsigned long start_jiffies; -+ unsigned long end_jiffies; -+ struct amdgpu_fence *fence = NULL; -+ int i, r; -+ -+ start_jiffies = jiffies; -+ for (i = 0; i < n; i++) { -+ struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring; -+ r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence); -+ if (r) -+ goto exit_do_move; -+ r = amdgpu_fence_wait(fence, false); -+ if (r) -+ goto exit_do_move; -+ amdgpu_fence_unref(&fence); -+ } -+ end_jiffies = jiffies; -+ r = jiffies_to_msecs(end_jiffies - start_jiffies); -+ -+exit_do_move: -+ if (fence) -+ amdgpu_fence_unref(&fence); -+ return r; -+} -+ -+ -+static void amdgpu_benchmark_log_results(int n, unsigned size, -+ unsigned int time, -+ unsigned sdomain, unsigned ddomain, -+ char *kind) -+{ -+ unsigned int throughput = (n * (size >> 10)) / time; -+ DRM_INFO("amdgpu: %s %u bo moves of %u kB from" -+ " %d to %d in %u ms, throughput: %u Mb/s or %u MB/s\n", -+ kind, n, size >> 10, sdomain, ddomain, time, -+ throughput * 8, throughput); -+} -+ -+static void amdgpu_benchmark_move(struct amdgpu_device *adev, unsigned size, -+ unsigned sdomain, unsigned ddomain) -+{ -+ struct amdgpu_bo *dobj = NULL; -+ struct amdgpu_bo *sobj = NULL; -+ uint64_t saddr, daddr; -+ int r, n; -+ int time; -+ -+ n = AMDGPU_BENCHMARK_ITERATIONS; -+ r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, sdomain, 0, NULL, &sobj); -+ if (r) { -+ goto out_cleanup; -+ } -+ r = amdgpu_bo_reserve(sobj, false); -+ if (unlikely(r != 0)) -+ goto out_cleanup; -+ r = amdgpu_bo_pin(sobj, sdomain, &saddr); -+ amdgpu_bo_unreserve(sobj); -+ if (r) { -+ goto out_cleanup; -+ } -+ r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, ddomain, 0, NULL, &dobj); -+ if (r) { -+ goto out_cleanup; -+ } -+ r = amdgpu_bo_reserve(dobj, false); -+ if (unlikely(r != 0)) -+ goto out_cleanup; -+ r = amdgpu_bo_pin(dobj, ddomain, &daddr); -+ amdgpu_bo_unreserve(dobj); -+ if (r) { -+ goto out_cleanup; -+ } -+ -+ if (adev->mman.buffer_funcs) { -+ time = amdgpu_benchmark_do_move(adev, size, saddr, daddr, n); -+ if (time < 0) -+ goto out_cleanup; -+ if (time > 0) -+ amdgpu_benchmark_log_results(n, size, time, -+ sdomain, ddomain, "dma"); -+ } -+ -+out_cleanup: -+ if (sobj) { -+ r = amdgpu_bo_reserve(sobj, false); -+ if (likely(r == 0)) { -+ amdgpu_bo_unpin(sobj); -+ amdgpu_bo_unreserve(sobj); -+ } -+ amdgpu_bo_unref(&sobj); -+ } -+ if (dobj) { -+ r = amdgpu_bo_reserve(dobj, false); -+ if (likely(r == 0)) { -+ amdgpu_bo_unpin(dobj); -+ amdgpu_bo_unreserve(dobj); -+ } -+ amdgpu_bo_unref(&dobj); -+ } -+ -+ if (r) { -+ DRM_ERROR("Error while benchmarking BO move.\n"); -+ } -+} -+ -+void amdgpu_benchmark(struct amdgpu_device *adev, int test_number) -+{ -+ int i; -+ int common_modes[AMDGPU_BENCHMARK_COMMON_MODES_N] = { -+ 640 * 480 * 4, -+ 720 * 480 * 4, -+ 800 * 600 * 4, -+ 848 * 480 * 4, -+ 1024 * 768 * 4, -+ 1152 * 768 * 4, -+ 1280 * 720 * 4, -+ 1280 * 800 * 4, -+ 1280 * 854 * 4, -+ 1280 * 960 * 4, -+ 1280 * 1024 * 4, -+ 1440 * 900 * 4, -+ 1400 * 1050 * 4, -+ 1680 * 1050 * 4, -+ 1600 * 1200 * 4, -+ 1920 * 1080 * 4, -+ 1920 * 1200 * 4 -+ }; -+ -+ switch (test_number) { -+ case 1: -+ /* simple test, VRAM to GTT and GTT to VRAM */ -+ amdgpu_benchmark_move(adev, 1024*1024, AMDGPU_GEM_DOMAIN_GTT, -+ AMDGPU_GEM_DOMAIN_VRAM); -+ amdgpu_benchmark_move(adev, 1024*1024, AMDGPU_GEM_DOMAIN_VRAM, -+ AMDGPU_GEM_DOMAIN_GTT); -+ break; -+ case 2: -+ /* simple test, VRAM to VRAM */ -+ amdgpu_benchmark_move(adev, 1024*1024, AMDGPU_GEM_DOMAIN_VRAM, -+ AMDGPU_GEM_DOMAIN_VRAM); -+ break; -+ case 3: -+ /* GTT to VRAM, buffer size sweep, powers of 2 */ -+ for (i = 1; i <= 16384; i <<= 1) -+ amdgpu_benchmark_move(adev, i * AMDGPU_GPU_PAGE_SIZE, -+ AMDGPU_GEM_DOMAIN_GTT, -+ AMDGPU_GEM_DOMAIN_VRAM); -+ break; -+ case 4: -+ /* VRAM to GTT, buffer size sweep, powers of 2 */ -+ for (i = 1; i <= 16384; i <<= 1) -+ amdgpu_benchmark_move(adev, i * AMDGPU_GPU_PAGE_SIZE, -+ AMDGPU_GEM_DOMAIN_VRAM, -+ AMDGPU_GEM_DOMAIN_GTT); -+ break; -+ case 5: -+ /* VRAM to VRAM, buffer size sweep, powers of 2 */ -+ for (i = 1; i <= 16384; i <<= 1) -+ amdgpu_benchmark_move(adev, i * AMDGPU_GPU_PAGE_SIZE, -+ AMDGPU_GEM_DOMAIN_VRAM, -+ AMDGPU_GEM_DOMAIN_VRAM); -+ break; -+ case 6: -+ /* GTT to VRAM, buffer size sweep, common modes */ -+ for (i = 0; i < AMDGPU_BENCHMARK_COMMON_MODES_N; i++) -+ amdgpu_benchmark_move(adev, common_modes[i], -+ AMDGPU_GEM_DOMAIN_GTT, -+ AMDGPU_GEM_DOMAIN_VRAM); -+ break; -+ case 7: -+ /* VRAM to GTT, buffer size sweep, common modes */ -+ for (i = 0; i < AMDGPU_BENCHMARK_COMMON_MODES_N; i++) -+ amdgpu_benchmark_move(adev, common_modes[i], -+ AMDGPU_GEM_DOMAIN_VRAM, -+ AMDGPU_GEM_DOMAIN_GTT); -+ break; -+ case 8: -+ /* VRAM to VRAM, buffer size sweep, common modes */ -+ for (i = 0; i < AMDGPU_BENCHMARK_COMMON_MODES_N; i++) -+ amdgpu_benchmark_move(adev, common_modes[i], -+ AMDGPU_GEM_DOMAIN_VRAM, -+ AMDGPU_GEM_DOMAIN_VRAM); -+ break; -+ -+ default: -+ DRM_ERROR("Unknown benchmark\n"); -+ } -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c -new file mode 100644 -index 0000000..d7a3ab2 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c -@@ -0,0 +1,359 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include "atom.h" -+ -+#include <linux/vga_switcheroo.h> -+#include <linux/slab.h> -+#include <linux/acpi.h> -+/* -+ * BIOS. -+ */ -+ -+/* If you boot an IGP board with a discrete card as the primary, -+ * the IGP rom is not accessible via the rom bar as the IGP rom is -+ * part of the system bios. On boot, the system bios puts a -+ * copy of the igp rom at the start of vram if a discrete card is -+ * present. -+ */ -+static bool igp_read_bios_from_vram(struct amdgpu_device *adev) -+{ -+ uint8_t __iomem *bios; -+ resource_size_t vram_base; -+ resource_size_t size = 256 * 1024; /* ??? */ -+ -+ if (!(adev->flags & AMDGPU_IS_APU)) -+ if (!amdgpu_card_posted(adev)) -+ return false; -+ -+ adev->bios = NULL; -+ vram_base = pci_resource_start(adev->pdev, 0); -+ bios = ioremap(vram_base, size); -+ if (!bios) { -+ return false; -+ } -+ -+ if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { -+ iounmap(bios); -+ return false; -+ } -+ adev->bios = kmalloc(size, GFP_KERNEL); -+ if (adev->bios == NULL) { -+ iounmap(bios); -+ return false; -+ } -+ memcpy_fromio(adev->bios, bios, size); -+ iounmap(bios); -+ return true; -+} -+ -+bool amdgpu_read_bios(struct amdgpu_device *adev) -+{ -+ uint8_t __iomem *bios; -+ size_t size; -+ -+ adev->bios = NULL; -+ /* XXX: some cards may return 0 for rom size? ddx has a workaround */ -+ bios = pci_map_rom(adev->pdev, &size); -+ if (!bios) { -+ return false; -+ } -+ -+ if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { -+ pci_unmap_rom(adev->pdev, bios); -+ return false; -+ } -+ adev->bios = kmemdup(bios, size, GFP_KERNEL); -+ if (adev->bios == NULL) { -+ pci_unmap_rom(adev->pdev, bios); -+ return false; -+ } -+ pci_unmap_rom(adev->pdev, bios); -+ return true; -+} -+ -+static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) -+{ -+ uint8_t __iomem *bios; -+ size_t size; -+ -+ adev->bios = NULL; -+ -+ bios = pci_platform_rom(adev->pdev, &size); -+ if (!bios) { -+ return false; -+ } -+ -+ if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { -+ return false; -+ } -+ adev->bios = kmemdup(bios, size, GFP_KERNEL); -+ if (adev->bios == NULL) { -+ return false; -+ } -+ -+ return true; -+} -+ -+#ifdef CONFIG_ACPI -+/* ATRM is used to get the BIOS on the discrete cards in -+ * dual-gpu systems. -+ */ -+/* retrieve the ROM in 4k blocks */ -+#define ATRM_BIOS_PAGE 4096 -+/** -+ * amdgpu_atrm_call - fetch a chunk of the vbios -+ * -+ * @atrm_handle: acpi ATRM handle -+ * @bios: vbios image pointer -+ * @offset: offset of vbios image data to fetch -+ * @len: length of vbios image data to fetch -+ * -+ * Executes ATRM to fetch a chunk of the discrete -+ * vbios image on PX systems (all asics). -+ * Returns the length of the buffer fetched. -+ */ -+static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios, -+ int offset, int len) -+{ -+ acpi_status status; -+ union acpi_object atrm_arg_elements[2], *obj; -+ struct acpi_object_list atrm_arg; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; -+ -+ atrm_arg.count = 2; -+ atrm_arg.pointer = &atrm_arg_elements[0]; -+ -+ atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; -+ atrm_arg_elements[0].integer.value = offset; -+ -+ atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; -+ atrm_arg_elements[1].integer.value = len; -+ -+ status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); -+ if (ACPI_FAILURE(status)) { -+ printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); -+ return -ENODEV; -+ } -+ -+ obj = (union acpi_object *)buffer.pointer; -+ memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); -+ len = obj->buffer.length; -+ kfree(buffer.pointer); -+ return len; -+} -+ -+static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) -+{ -+ int ret; -+ int size = 256 * 1024; -+ int i; -+ struct pci_dev *pdev = NULL; -+ acpi_handle dhandle, atrm_handle; -+ acpi_status status; -+ bool found = false; -+ -+ /* ATRM is for the discrete card only */ -+ if (adev->flags & AMDGPU_IS_APU) -+ return false; -+ -+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { -+ dhandle = ACPI_HANDLE(&pdev->dev); -+ if (!dhandle) -+ continue; -+ -+ status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); -+ if (!ACPI_FAILURE(status)) { -+ found = true; -+ break; -+ } -+ } -+ -+ if (!found) { -+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { -+ dhandle = ACPI_HANDLE(&pdev->dev); -+ if (!dhandle) -+ continue; -+ -+ status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); -+ if (!ACPI_FAILURE(status)) { -+ found = true; -+ break; -+ } -+ } -+ } -+ -+ if (!found) -+ return false; -+ -+ adev->bios = kmalloc(size, GFP_KERNEL); -+ if (!adev->bios) { -+ DRM_ERROR("Unable to allocate bios\n"); -+ return false; -+ } -+ -+ for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { -+ ret = amdgpu_atrm_call(atrm_handle, -+ adev->bios, -+ (i * ATRM_BIOS_PAGE), -+ ATRM_BIOS_PAGE); -+ if (ret < ATRM_BIOS_PAGE) -+ break; -+ } -+ -+ if (i == 0 || adev->bios[0] != 0x55 || adev->bios[1] != 0xaa) { -+ kfree(adev->bios); -+ return false; -+ } -+ return true; -+} -+#else -+static inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) -+{ -+ return false; -+} -+#endif -+ -+static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) -+{ -+ if (adev->flags & AMDGPU_IS_APU) -+ return igp_read_bios_from_vram(adev); -+ else -+ return amdgpu_asic_read_disabled_bios(adev); -+} -+ -+#ifdef CONFIG_ACPI -+static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) -+{ -+ bool ret = false; -+ struct acpi_table_header *hdr; -+ acpi_size tbl_size; -+ UEFI_ACPI_VFCT *vfct; -+ GOP_VBIOS_CONTENT *vbios; -+ VFCT_IMAGE_HEADER *vhdr; -+ -+ if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size))) -+ return false; -+ if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { -+ DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); -+ goto out_unmap; -+ } -+ -+ vfct = (UEFI_ACPI_VFCT *)hdr; -+ if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { -+ DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); -+ goto out_unmap; -+ } -+ -+ vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); -+ vhdr = &vbios->VbiosHeader; -+ DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", -+ vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, -+ vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); -+ -+ if (vhdr->PCIBus != adev->pdev->bus->number || -+ vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) || -+ vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn) || -+ vhdr->VendorID != adev->pdev->vendor || -+ vhdr->DeviceID != adev->pdev->device) { -+ DRM_INFO("ACPI VFCT table is not for this card\n"); -+ goto out_unmap; -+ } -+ -+ if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { -+ DRM_ERROR("ACPI VFCT image truncated\n"); -+ goto out_unmap; -+ } -+ -+ adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); -+ ret = !!adev->bios; -+ -+out_unmap: -+ return ret; -+} -+#else -+static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) -+{ -+ return false; -+} -+#endif -+ -+bool amdgpu_get_bios(struct amdgpu_device *adev) -+{ -+ bool r; -+ uint16_t tmp; -+ -+ r = amdgpu_atrm_get_bios(adev); -+ if (r == false) -+ r = amdgpu_acpi_vfct_bios(adev); -+ if (r == false) -+ r = igp_read_bios_from_vram(adev); -+ if (r == false) -+ r = amdgpu_read_bios(adev); -+ if (r == false) { -+ r = amdgpu_read_disabled_bios(adev); -+ } -+ if (r == false) { -+ r = amdgpu_read_platform_bios(adev); -+ } -+ if (r == false || adev->bios == NULL) { -+ DRM_ERROR("Unable to locate a BIOS ROM\n"); -+ adev->bios = NULL; -+ return false; -+ } -+ if (adev->bios[0] != 0x55 || adev->bios[1] != 0xaa) { -+ printk("BIOS signature incorrect %x %x\n", adev->bios[0], adev->bios[1]); -+ goto free_bios; -+ } -+ -+ tmp = RBIOS16(0x18); -+ if (RBIOS8(tmp + 0x14) != 0x0) { -+ DRM_INFO("Not an x86 BIOS ROM, not using.\n"); -+ goto free_bios; -+ } -+ -+ adev->bios_header_start = RBIOS16(0x48); -+ if (!adev->bios_header_start) { -+ goto free_bios; -+ } -+ tmp = adev->bios_header_start + 4; -+ if (!memcmp(adev->bios + tmp, "ATOM", 4) || -+ !memcmp(adev->bios + tmp, "MOTA", 4)) { -+ adev->is_atom_bios = true; -+ } else { -+ adev->is_atom_bios = false; -+ } -+ -+ DRM_DEBUG("%sBIOS detected\n", adev->is_atom_bios ? "ATOM" : "COM"); -+ return true; -+free_bios: -+ kfree(adev->bios); -+ adev->bios = NULL; -+ return false; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c -new file mode 100644 -index 0000000..819fb86 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c -@@ -0,0 +1,268 @@ -+/* -+ * Copyright 2015 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Christian König <deathsimple@vodafone.de> -+ */ -+ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+ -+static int amdgpu_bo_list_create(struct amdgpu_fpriv *fpriv, -+ struct amdgpu_bo_list **result, -+ int *id) -+{ -+ int r; -+ -+ *result = kzalloc(sizeof(struct amdgpu_bo_list), GFP_KERNEL); -+ if (!*result) -+ return -ENOMEM; -+ -+ mutex_lock(&fpriv->bo_list_lock); -+ r = idr_alloc(&fpriv->bo_list_handles, *result, -+ 0, 0, GFP_KERNEL); -+ if (r < 0) { -+ mutex_unlock(&fpriv->bo_list_lock); -+ kfree(*result); -+ return r; -+ } -+ *id = r; -+ -+ mutex_init(&(*result)->lock); -+ (*result)->num_entries = 0; -+ (*result)->array = NULL; -+ -+ mutex_lock(&(*result)->lock); -+ mutex_unlock(&fpriv->bo_list_lock); -+ -+ return 0; -+} -+ -+static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) -+{ -+ struct amdgpu_bo_list *list; -+ -+ mutex_lock(&fpriv->bo_list_lock); -+ list = idr_find(&fpriv->bo_list_handles, id); -+ if (list) { -+ mutex_lock(&list->lock); -+ idr_remove(&fpriv->bo_list_handles, id); -+ mutex_unlock(&list->lock); -+ amdgpu_bo_list_free(list); -+ } -+ mutex_unlock(&fpriv->bo_list_lock); -+} -+ -+static int amdgpu_bo_list_set(struct amdgpu_device *adev, -+ struct drm_file *filp, -+ struct amdgpu_bo_list *list, -+ struct drm_amdgpu_bo_list_entry *info, -+ unsigned num_entries) -+{ -+ struct amdgpu_bo_list_entry *array; -+ struct amdgpu_bo *gds_obj = adev->gds.gds_gfx_bo; -+ struct amdgpu_bo *gws_obj = adev->gds.gws_gfx_bo; -+ struct amdgpu_bo *oa_obj = adev->gds.oa_gfx_bo; -+ -+ bool has_userptr = false; -+ unsigned i; -+ -+ array = drm_malloc_ab(num_entries, sizeof(struct amdgpu_bo_list_entry)); -+ if (!array) -+ return -ENOMEM; -+ memset(array, 0, num_entries * sizeof(struct amdgpu_bo_list_entry)); -+ -+ for (i = 0; i < num_entries; ++i) { -+ struct amdgpu_bo_list_entry *entry = &array[i]; -+ struct drm_gem_object *gobj; -+ -+ gobj = drm_gem_object_lookup(adev->ddev, filp, info[i].bo_handle); -+ if (!gobj) -+ goto error_free; -+ -+ entry->robj = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); -+ drm_gem_object_unreference_unlocked(gobj); -+ entry->priority = info[i].bo_priority; -+ entry->prefered_domains = entry->robj->initial_domain; -+ entry->allowed_domains = entry->prefered_domains; -+ if (entry->allowed_domains == AMDGPU_GEM_DOMAIN_VRAM) -+ entry->allowed_domains |= AMDGPU_GEM_DOMAIN_GTT; -+ if (amdgpu_ttm_tt_has_userptr(entry->robj->tbo.ttm)) { -+ has_userptr = true; -+ entry->prefered_domains = AMDGPU_GEM_DOMAIN_GTT; -+ entry->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; -+ } -+ entry->tv.bo = &entry->robj->tbo; -+ entry->tv.shared = true; -+ -+ if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GDS) -+ gds_obj = entry->robj; -+ if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GWS) -+ gws_obj = entry->robj; -+ if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_OA) -+ oa_obj = entry->robj; -+ } -+ -+ for (i = 0; i < list->num_entries; ++i) -+ amdgpu_bo_unref(&list->array[i].robj); -+ -+ drm_free_large(list->array); -+ -+ list->gds_obj = gds_obj; -+ list->gws_obj = gws_obj; -+ list->oa_obj = oa_obj; -+ list->has_userptr = has_userptr; -+ list->array = array; -+ list->num_entries = num_entries; -+ -+ return 0; -+ -+error_free: -+ drm_free_large(array); -+ return -ENOENT; -+} -+ -+struct amdgpu_bo_list * -+amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id) -+{ -+ struct amdgpu_bo_list *result; -+ -+ mutex_lock(&fpriv->bo_list_lock); -+ result = idr_find(&fpriv->bo_list_handles, id); -+ if (result) -+ mutex_lock(&result->lock); -+ mutex_unlock(&fpriv->bo_list_lock); -+ return result; -+} -+ -+void amdgpu_bo_list_put(struct amdgpu_bo_list *list) -+{ -+ mutex_unlock(&list->lock); -+} -+ -+void amdgpu_bo_list_free(struct amdgpu_bo_list *list) -+{ -+ unsigned i; -+ -+ for (i = 0; i < list->num_entries; ++i) -+ amdgpu_bo_unref(&list->array[i].robj); -+ -+ mutex_destroy(&list->lock); -+ drm_free_large(list->array); -+ kfree(list); -+} -+ -+int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry); -+ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_fpriv *fpriv = filp->driver_priv; -+ union drm_amdgpu_bo_list *args = data; -+ uint32_t handle = args->in.list_handle; -+ const void __user *uptr = (const void*)(long)args->in.bo_info_ptr; -+ -+ struct drm_amdgpu_bo_list_entry *info; -+ struct amdgpu_bo_list *list; -+ -+ int r; -+ -+ info = drm_malloc_ab(args->in.bo_number, -+ sizeof(struct drm_amdgpu_bo_list_entry)); -+ if (!info) -+ return -ENOMEM; -+ -+ /* copy the handle array from userspace to a kernel buffer */ -+ r = -EFAULT; -+ if (likely(info_size == args->in.bo_info_size)) { -+ unsigned long bytes = args->in.bo_number * -+ args->in.bo_info_size; -+ -+ if (copy_from_user(info, uptr, bytes)) -+ goto error_free; -+ -+ } else { -+ unsigned long bytes = min(args->in.bo_info_size, info_size); -+ unsigned i; -+ -+ memset(info, 0, args->in.bo_number * info_size); -+ for (i = 0; i < args->in.bo_number; ++i) { -+ if (copy_from_user(&info[i], uptr, bytes)) -+ goto error_free; -+ -+ uptr += args->in.bo_info_size; -+ } -+ } -+ -+ switch (args->in.operation) { -+ case AMDGPU_BO_LIST_OP_CREATE: -+ r = amdgpu_bo_list_create(fpriv, &list, &handle); -+ if (r) -+ goto error_free; -+ -+ r = amdgpu_bo_list_set(adev, filp, list, info, -+ args->in.bo_number); -+ amdgpu_bo_list_put(list); -+ if (r) -+ goto error_free; -+ -+ break; -+ -+ case AMDGPU_BO_LIST_OP_DESTROY: -+ amdgpu_bo_list_destroy(fpriv, handle); -+ handle = 0; -+ break; -+ -+ case AMDGPU_BO_LIST_OP_UPDATE: -+ r = -ENOENT; -+ list = amdgpu_bo_list_get(fpriv, handle); -+ if (!list) -+ goto error_free; -+ -+ r = amdgpu_bo_list_set(adev, filp, list, info, -+ args->in.bo_number); -+ amdgpu_bo_list_put(list); -+ if (r) -+ goto error_free; -+ -+ break; -+ -+ default: -+ r = -EINVAL; -+ goto error_free; -+ } -+ -+ memset(args, 0, sizeof(*args)); -+ args->out.list_handle = handle; -+ drm_free_large(info); -+ -+ return 0; -+ -+error_free: -+ drm_free_large(info); -+ return r; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c -new file mode 100644 -index 0000000..6a8d28f ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c -@@ -0,0 +1,1907 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <drm/drmP.h> -+#include <drm/drm_edid.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/drm_fb_helper.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "atom.h" -+#include "atombios_encoders.h" -+#include "atombios_dp.h" -+#include "amdgpu_connectors.h" -+#include "amdgpu_i2c.h" -+ -+#include <linux/pm_runtime.h> -+ -+void amdgpu_connector_hotplug(struct drm_connector *connector) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ -+ /* bail if the connector does not have hpd pin, e.g., -+ * VGA, TV, etc. -+ */ -+ if (amdgpu_connector->hpd.hpd == AMDGPU_HPD_NONE) -+ return; -+ -+ amdgpu_display_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd); -+ -+ /* if the connector is already off, don't turn it back on */ -+ if (connector->dpms != DRM_MODE_DPMS_ON) -+ return; -+ -+ /* just deal with DP (not eDP) here. */ -+ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { -+ struct amdgpu_connector_atom_dig *dig_connector = -+ amdgpu_connector->con_priv; -+ -+ /* if existing sink type was not DP no need to retrain */ -+ if (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) -+ return; -+ -+ /* first get sink type as it may be reset after (un)plug */ -+ dig_connector->dp_sink_type = amdgpu_atombios_dp_get_sinktype(amdgpu_connector); -+ /* don't do anything if sink is not display port, i.e., -+ * passive dp->(dvi|hdmi) adaptor -+ */ -+ if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { -+ int saved_dpms = connector->dpms; -+ /* Only turn off the display if it's physically disconnected */ -+ if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { -+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); -+ } else if (amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) { -+ /* set it to OFF so that drm_helper_connector_dpms() -+ * won't return immediately since the current state -+ * is ON at this point. -+ */ -+ connector->dpms = DRM_MODE_DPMS_OFF; -+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); -+ } -+ connector->dpms = saved_dpms; -+ } -+ } -+} -+ -+static void amdgpu_connector_property_change_mode(struct drm_encoder *encoder) -+{ -+ struct drm_crtc *crtc = encoder->crtc; -+ -+ if (crtc && crtc->enabled) { -+ drm_crtc_helper_set_mode(crtc, &crtc->mode, -+ crtc->x, crtc->y, crtc->primary->fb); -+ } -+} -+ -+int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector; -+ int bpc = 8; -+ unsigned mode_clock, max_tmds_clock; -+ -+ switch (connector->connector_type) { -+ case DRM_MODE_CONNECTOR_DVII: -+ case DRM_MODE_CONNECTOR_HDMIB: -+ if (amdgpu_connector->use_digital) { -+ if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ if (connector->display_info.bpc) -+ bpc = connector->display_info.bpc; -+ } -+ } -+ break; -+ case DRM_MODE_CONNECTOR_DVID: -+ case DRM_MODE_CONNECTOR_HDMIA: -+ if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ if (connector->display_info.bpc) -+ bpc = connector->display_info.bpc; -+ } -+ break; -+ case DRM_MODE_CONNECTOR_DisplayPort: -+ dig_connector = amdgpu_connector->con_priv; -+ if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || -+ (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) || -+ drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ if (connector->display_info.bpc) -+ bpc = connector->display_info.bpc; -+ } -+ break; -+ case DRM_MODE_CONNECTOR_eDP: -+ case DRM_MODE_CONNECTOR_LVDS: -+ if (connector->display_info.bpc) -+ bpc = connector->display_info.bpc; -+ else { -+ struct drm_connector_helper_funcs *connector_funcs = -+ connector->helper_private; -+ struct drm_encoder *encoder = connector_funcs->best_encoder(connector); -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ -+ if (dig->lcd_misc & ATOM_PANEL_MISC_V13_6BIT_PER_COLOR) -+ bpc = 6; -+ else if (dig->lcd_misc & ATOM_PANEL_MISC_V13_8BIT_PER_COLOR) -+ bpc = 8; -+ } -+ break; -+ } -+ -+ if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ /* -+ * Pre DCE-8 hw can't handle > 12 bpc, and more than 12 bpc doesn't make -+ * much sense without support for > 12 bpc framebuffers. RGB 4:4:4 at -+ * 12 bpc is always supported on hdmi deep color sinks, as this is -+ * required by the HDMI-1.3 spec. Clamp to a safe 12 bpc maximum. -+ */ -+ if (bpc > 12) { -+ DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 12 bpc.\n", -+ connector->name, bpc); -+ bpc = 12; -+ } -+ -+ /* Any defined maximum tmds clock limit we must not exceed? */ -+ if (connector->max_tmds_clock > 0) { -+ /* mode_clock is clock in kHz for mode to be modeset on this connector */ -+ mode_clock = amdgpu_connector->pixelclock_for_modeset; -+ -+ /* Maximum allowable input clock in kHz */ -+ max_tmds_clock = connector->max_tmds_clock * 1000; -+ -+ DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n", -+ connector->name, mode_clock, max_tmds_clock); -+ -+ /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */ -+ if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) { -+ if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) && -+ (mode_clock * 5/4 <= max_tmds_clock)) -+ bpc = 10; -+ else -+ bpc = 8; -+ -+ DRM_DEBUG("%s: HDMI deep color 12 bpc exceeds max tmds clock. Using %d bpc.\n", -+ connector->name, bpc); -+ } -+ -+ if ((bpc == 10) && (mode_clock * 5/4 > max_tmds_clock)) { -+ bpc = 8; -+ DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n", -+ connector->name, bpc); -+ } else if (bpc > 8) { -+ /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */ -+ DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n", -+ connector->name); -+ bpc = 8; -+ } -+ } -+ } -+ -+ if ((amdgpu_deep_color == 0) && (bpc > 8)) { -+ DRM_DEBUG("%s: Deep color disabled. Set amdgpu module param deep_color=1 to enable.\n", -+ connector->name); -+ bpc = 8; -+ } -+ -+ DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n", -+ connector->name, connector->display_info.bpc, bpc); -+ -+ return bpc; -+} -+ -+static void -+amdgpu_connector_update_scratch_regs(struct drm_connector *connector, -+ enum drm_connector_status status) -+{ -+ struct drm_encoder *best_encoder = NULL; -+ struct drm_encoder *encoder = NULL; -+ struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; -+ bool connected; -+ int i; -+ -+ best_encoder = connector_funcs->best_encoder(connector); -+ -+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -+ if (connector->encoder_ids[i] == 0) -+ break; -+ -+ encoder = drm_encoder_find(connector->dev, -+ connector->encoder_ids[i]); -+ if (!encoder) -+ continue; -+ -+ if ((encoder == best_encoder) && (status == connector_status_connected)) -+ connected = true; -+ else -+ connected = false; -+ -+ amdgpu_atombios_encoder_set_bios_scratch_regs(connector, encoder, connected); -+ -+ } -+} -+ -+static struct drm_encoder * -+amdgpu_connector_find_encoder(struct drm_connector *connector, -+ int encoder_type) -+{ -+ struct drm_encoder *encoder; -+ int i; -+ -+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -+ if (connector->encoder_ids[i] == 0) -+ break; -+ encoder = drm_encoder_find(connector->dev, -+ connector->encoder_ids[i]); -+ if (!encoder) -+ continue; -+ -+ if (encoder->encoder_type == encoder_type) -+ return encoder; -+ } -+ return NULL; -+} -+ -+struct edid *amdgpu_connector_edid(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_property_blob *edid_blob = connector->edid_blob_ptr; -+ -+ if (amdgpu_connector->edid) { -+ return amdgpu_connector->edid; -+ } else if (edid_blob) { -+ struct edid *edid = kmemdup(edid_blob->data, edid_blob->length, GFP_KERNEL); -+ if (edid) -+ amdgpu_connector->edid = edid; -+ } -+ return amdgpu_connector->edid; -+} -+ -+static struct edid * -+amdgpu_connector_get_hardcoded_edid(struct amdgpu_device *adev) -+{ -+ struct edid *edid; -+ -+ if (adev->mode_info.bios_hardcoded_edid) { -+ edid = kmalloc(adev->mode_info.bios_hardcoded_edid_size, GFP_KERNEL); -+ if (edid) { -+ memcpy((unsigned char *)edid, -+ (unsigned char *)adev->mode_info.bios_hardcoded_edid, -+ adev->mode_info.bios_hardcoded_edid_size); -+ return edid; -+ } -+ } -+ return NULL; -+} -+ -+static void amdgpu_connector_get_edid(struct drm_connector *connector) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ -+ if (amdgpu_connector->edid) -+ return; -+ -+ /* on hw with routers, select right port */ -+ if (amdgpu_connector->router.ddc_valid) -+ amdgpu_i2c_router_select_ddc_port(amdgpu_connector); -+ -+ if ((amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != -+ ENCODER_OBJECT_ID_NONE) && -+ amdgpu_connector->ddc_bus->has_aux) { -+ amdgpu_connector->edid = drm_get_edid(connector, -+ &amdgpu_connector->ddc_bus->aux.ddc); -+ } else if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || -+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { -+ struct amdgpu_connector_atom_dig *dig = amdgpu_connector->con_priv; -+ -+ if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT || -+ dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && -+ amdgpu_connector->ddc_bus->has_aux) -+ amdgpu_connector->edid = drm_get_edid(connector, -+ &amdgpu_connector->ddc_bus->aux.ddc); -+ else if (amdgpu_connector->ddc_bus) -+ amdgpu_connector->edid = drm_get_edid(connector, -+ &amdgpu_connector->ddc_bus->adapter); -+ } else if (amdgpu_connector->ddc_bus) { -+ amdgpu_connector->edid = drm_get_edid(connector, -+ &amdgpu_connector->ddc_bus->adapter); -+ } -+ -+ if (!amdgpu_connector->edid) { -+ /* some laptops provide a hardcoded edid in rom for LCDs */ -+ if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) || -+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP))) -+ amdgpu_connector->edid = amdgpu_connector_get_hardcoded_edid(adev); -+ } -+} -+ -+static void amdgpu_connector_free_edid(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ -+ if (amdgpu_connector->edid) { -+ kfree(amdgpu_connector->edid); -+ amdgpu_connector->edid = NULL; -+ } -+} -+ -+static int amdgpu_connector_ddc_get_modes(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ int ret; -+ -+ if (amdgpu_connector->edid) { -+ drm_mode_connector_update_edid_property(connector, amdgpu_connector->edid); -+ ret = drm_add_edid_modes(connector, amdgpu_connector->edid); -+ drm_edid_to_eld(connector, amdgpu_connector->edid); -+ return ret; -+ } -+ drm_mode_connector_update_edid_property(connector, NULL); -+ return 0; -+} -+ -+static struct drm_encoder * -+amdgpu_connector_best_single_encoder(struct drm_connector *connector) -+{ -+ int enc_id = connector->encoder_ids[0]; -+ -+ /* pick the encoder ids */ -+ if (enc_id) -+ return drm_encoder_find(connector->dev, enc_id); -+ return NULL; -+} -+ -+static void amdgpu_get_native_mode(struct drm_connector *connector) -+{ -+ struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); -+ struct amdgpu_encoder *amdgpu_encoder; -+ -+ if (encoder == NULL) -+ return; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (!list_empty(&connector->probed_modes)) { -+ struct drm_display_mode *preferred_mode = -+ list_first_entry(&connector->probed_modes, -+ struct drm_display_mode, head); -+ -+ amdgpu_encoder->native_mode = *preferred_mode; -+ } else { -+ amdgpu_encoder->native_mode.clock = 0; -+ } -+} -+ -+static struct drm_display_mode * -+amdgpu_connector_lcd_native_mode(struct drm_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *mode = NULL; -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ -+ if (native_mode->hdisplay != 0 && -+ native_mode->vdisplay != 0 && -+ native_mode->clock != 0) { -+ mode = drm_mode_duplicate(dev, native_mode); -+ mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; -+ drm_mode_set_name(mode); -+ -+ DRM_DEBUG_KMS("Adding native panel mode %s\n", mode->name); -+ } else if (native_mode->hdisplay != 0 && -+ native_mode->vdisplay != 0) { -+ /* mac laptops without an edid */ -+ /* Note that this is not necessarily the exact panel mode, -+ * but an approximation based on the cvt formula. For these -+ * systems we should ideally read the mode info out of the -+ * registers or add a mode table, but this works and is much -+ * simpler. -+ */ -+ mode = drm_cvt_mode(dev, native_mode->hdisplay, native_mode->vdisplay, 60, true, false, false); -+ mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; -+ DRM_DEBUG_KMS("Adding cvt approximation of native panel mode %s\n", mode->name); -+ } -+ return mode; -+} -+ -+static void amdgpu_connector_add_common_modes(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *mode = NULL; -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ int i; -+ struct mode_size { -+ int w; -+ int h; -+ } common_modes[17] = { -+ { 640, 480}, -+ { 720, 480}, -+ { 800, 600}, -+ { 848, 480}, -+ {1024, 768}, -+ {1152, 768}, -+ {1280, 720}, -+ {1280, 800}, -+ {1280, 854}, -+ {1280, 960}, -+ {1280, 1024}, -+ {1440, 900}, -+ {1400, 1050}, -+ {1680, 1050}, -+ {1600, 1200}, -+ {1920, 1080}, -+ {1920, 1200} -+ }; -+ -+ for (i = 0; i < 17; i++) { -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { -+ if (common_modes[i].w > 1024 || -+ common_modes[i].h > 768) -+ continue; -+ } -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { -+ if (common_modes[i].w > native_mode->hdisplay || -+ common_modes[i].h > native_mode->vdisplay || -+ (common_modes[i].w == native_mode->hdisplay && -+ common_modes[i].h == native_mode->vdisplay)) -+ continue; -+ } -+ if (common_modes[i].w < 320 || common_modes[i].h < 200) -+ continue; -+ -+ mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); -+ drm_mode_probed_add(connector, mode); -+ } -+} -+ -+static int amdgpu_connector_set_property(struct drm_connector *connector, -+ struct drm_property *property, -+ uint64_t val) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_encoder *encoder; -+ struct amdgpu_encoder *amdgpu_encoder; -+ -+ if (property == adev->mode_info.coherent_mode_property) { -+ struct amdgpu_encoder_atom_dig *dig; -+ bool new_coherent_mode; -+ -+ /* need to find digital encoder on connector */ -+ encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); -+ if (!encoder) -+ return 0; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (!amdgpu_encoder->enc_priv) -+ return 0; -+ -+ dig = amdgpu_encoder->enc_priv; -+ new_coherent_mode = val ? true : false; -+ if (dig->coherent_mode != new_coherent_mode) { -+ dig->coherent_mode = new_coherent_mode; -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ } -+ -+ if (property == adev->mode_info.audio_property) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ /* need to find digital encoder on connector */ -+ encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); -+ if (!encoder) -+ return 0; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (amdgpu_connector->audio != val) { -+ amdgpu_connector->audio = val; -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ } -+ -+ if (property == adev->mode_info.dither_property) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ /* need to find digital encoder on connector */ -+ encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); -+ if (!encoder) -+ return 0; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (amdgpu_connector->dither != val) { -+ amdgpu_connector->dither = val; -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ } -+ -+ if (property == adev->mode_info.underscan_property) { -+ /* need to find digital encoder on connector */ -+ encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); -+ if (!encoder) -+ return 0; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (amdgpu_encoder->underscan_type != val) { -+ amdgpu_encoder->underscan_type = val; -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ } -+ -+ if (property == adev->mode_info.underscan_hborder_property) { -+ /* need to find digital encoder on connector */ -+ encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); -+ if (!encoder) -+ return 0; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (amdgpu_encoder->underscan_hborder != val) { -+ amdgpu_encoder->underscan_hborder = val; -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ } -+ -+ if (property == adev->mode_info.underscan_vborder_property) { -+ /* need to find digital encoder on connector */ -+ encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS); -+ if (!encoder) -+ return 0; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ if (amdgpu_encoder->underscan_vborder != val) { -+ amdgpu_encoder->underscan_vborder = val; -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ } -+ -+ if (property == adev->mode_info.load_detect_property) { -+ struct amdgpu_connector *amdgpu_connector = -+ to_amdgpu_connector(connector); -+ -+ if (val == 0) -+ amdgpu_connector->dac_load_detect = false; -+ else -+ amdgpu_connector->dac_load_detect = true; -+ } -+ -+ if (property == dev->mode_config.scaling_mode_property) { -+ enum amdgpu_rmx_type rmx_type; -+ -+ if (connector->encoder) { -+ amdgpu_encoder = to_amdgpu_encoder(connector->encoder); -+ } else { -+ struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; -+ amdgpu_encoder = to_amdgpu_encoder(connector_funcs->best_encoder(connector)); -+ } -+ -+ switch (val) { -+ default: -+ case DRM_MODE_SCALE_NONE: rmx_type = RMX_OFF; break; -+ case DRM_MODE_SCALE_CENTER: rmx_type = RMX_CENTER; break; -+ case DRM_MODE_SCALE_ASPECT: rmx_type = RMX_ASPECT; break; -+ case DRM_MODE_SCALE_FULLSCREEN: rmx_type = RMX_FULL; break; -+ } -+ if (amdgpu_encoder->rmx_type == rmx_type) -+ return 0; -+ -+ if ((rmx_type != DRM_MODE_SCALE_NONE) && -+ (amdgpu_encoder->native_mode.clock == 0)) -+ return 0; -+ -+ amdgpu_encoder->rmx_type = rmx_type; -+ -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ } -+ -+ return 0; -+} -+ -+static void -+amdgpu_connector_fixup_lcd_native_mode(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ struct drm_display_mode *t, *mode; -+ -+ /* If the EDID preferred mode doesn't match the native mode, use it */ -+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { -+ if (mode->type & DRM_MODE_TYPE_PREFERRED) { -+ if (mode->hdisplay != native_mode->hdisplay || -+ mode->vdisplay != native_mode->vdisplay) -+ memcpy(native_mode, mode, sizeof(*mode)); -+ } -+ } -+ -+ /* Try to get native mode details from EDID if necessary */ -+ if (!native_mode->clock) { -+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { -+ if (mode->hdisplay == native_mode->hdisplay && -+ mode->vdisplay == native_mode->vdisplay) { -+ *native_mode = *mode; -+ drm_mode_set_crtcinfo(native_mode, CRTC_INTERLACE_HALVE_V); -+ DRM_DEBUG_KMS("Determined LVDS native mode details from EDID\n"); -+ break; -+ } -+ } -+ } -+ -+ if (!native_mode->clock) { -+ DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n"); -+ amdgpu_encoder->rmx_type = RMX_OFF; -+ } -+} -+ -+static int amdgpu_connector_lvds_get_modes(struct drm_connector *connector) -+{ -+ struct drm_encoder *encoder; -+ int ret = 0; -+ struct drm_display_mode *mode; -+ -+ amdgpu_connector_get_edid(connector); -+ ret = amdgpu_connector_ddc_get_modes(connector); -+ if (ret > 0) { -+ encoder = amdgpu_connector_best_single_encoder(connector); -+ if (encoder) { -+ amdgpu_connector_fixup_lcd_native_mode(encoder, connector); -+ /* add scaled modes */ -+ amdgpu_connector_add_common_modes(encoder, connector); -+ } -+ return ret; -+ } -+ -+ encoder = amdgpu_connector_best_single_encoder(connector); -+ if (!encoder) -+ return 0; -+ -+ /* we have no EDID modes */ -+ mode = amdgpu_connector_lcd_native_mode(encoder); -+ if (mode) { -+ ret = 1; -+ drm_mode_probed_add(connector, mode); -+ /* add the width/height from vbios tables if available */ -+ connector->display_info.width_mm = mode->width_mm; -+ connector->display_info.height_mm = mode->height_mm; -+ /* add scaled modes */ -+ amdgpu_connector_add_common_modes(encoder, connector); -+ } -+ -+ return ret; -+} -+ -+static int amdgpu_connector_lvds_mode_valid(struct drm_connector *connector, -+ struct drm_display_mode *mode) -+{ -+ struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); -+ -+ if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) -+ return MODE_PANEL; -+ -+ if (encoder) { -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ -+ /* AVIVO hardware supports downscaling modes larger than the panel -+ * to the panel size, but I'm not sure this is desirable. -+ */ -+ if ((mode->hdisplay > native_mode->hdisplay) || -+ (mode->vdisplay > native_mode->vdisplay)) -+ return MODE_PANEL; -+ -+ /* if scaling is disabled, block non-native modes */ -+ if (amdgpu_encoder->rmx_type == RMX_OFF) { -+ if ((mode->hdisplay != native_mode->hdisplay) || -+ (mode->vdisplay != native_mode->vdisplay)) -+ return MODE_PANEL; -+ } -+ } -+ -+ return MODE_OK; -+} -+ -+static enum drm_connector_status -+amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); -+ enum drm_connector_status ret = connector_status_disconnected; -+ int r; -+ -+ r = pm_runtime_get_sync(connector->dev->dev); -+ if (r < 0) -+ return connector_status_disconnected; -+ -+ if (encoder) { -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ -+ /* check if panel is valid */ -+ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) -+ ret = connector_status_connected; -+ -+ } -+ -+ /* check for edid as well */ -+ amdgpu_connector_get_edid(connector); -+ if (amdgpu_connector->edid) -+ ret = connector_status_connected; -+ /* check acpi lid status ??? */ -+ -+ amdgpu_connector_update_scratch_regs(connector, ret); -+ pm_runtime_mark_last_busy(connector->dev->dev); -+ pm_runtime_put_autosuspend(connector->dev->dev); -+ return ret; -+} -+ -+static void amdgpu_connector_destroy(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ -+ if (amdgpu_connector->ddc_bus->has_aux) -+ drm_dp_aux_unregister(&amdgpu_connector->ddc_bus->aux); -+ amdgpu_connector_free_edid(connector); -+ kfree(amdgpu_connector->con_priv); -+ drm_connector_unregister(connector); -+ drm_connector_cleanup(connector); -+ kfree(connector); -+} -+ -+static int amdgpu_connector_set_lcd_property(struct drm_connector *connector, -+ struct drm_property *property, -+ uint64_t value) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_encoder *amdgpu_encoder; -+ enum amdgpu_rmx_type rmx_type; -+ -+ DRM_DEBUG_KMS("\n"); -+ if (property != dev->mode_config.scaling_mode_property) -+ return 0; -+ -+ if (connector->encoder) -+ amdgpu_encoder = to_amdgpu_encoder(connector->encoder); -+ else { -+ struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; -+ amdgpu_encoder = to_amdgpu_encoder(connector_funcs->best_encoder(connector)); -+ } -+ -+ switch (value) { -+ case DRM_MODE_SCALE_NONE: rmx_type = RMX_OFF; break; -+ case DRM_MODE_SCALE_CENTER: rmx_type = RMX_CENTER; break; -+ case DRM_MODE_SCALE_ASPECT: rmx_type = RMX_ASPECT; break; -+ default: -+ case DRM_MODE_SCALE_FULLSCREEN: rmx_type = RMX_FULL; break; -+ } -+ if (amdgpu_encoder->rmx_type == rmx_type) -+ return 0; -+ -+ amdgpu_encoder->rmx_type = rmx_type; -+ -+ amdgpu_connector_property_change_mode(&amdgpu_encoder->base); -+ return 0; -+} -+ -+ -+static const struct drm_connector_helper_funcs amdgpu_connector_lvds_helper_funcs = { -+ .get_modes = amdgpu_connector_lvds_get_modes, -+ .mode_valid = amdgpu_connector_lvds_mode_valid, -+ .best_encoder = amdgpu_connector_best_single_encoder, -+}; -+ -+static const struct drm_connector_funcs amdgpu_connector_lvds_funcs = { -+ .dpms = drm_helper_connector_dpms, -+ .detect = amdgpu_connector_lvds_detect, -+ .fill_modes = drm_helper_probe_single_connector_modes, -+ .destroy = amdgpu_connector_destroy, -+ .set_property = amdgpu_connector_set_lcd_property, -+}; -+ -+static int amdgpu_connector_vga_get_modes(struct drm_connector *connector) -+{ -+ int ret; -+ -+ amdgpu_connector_get_edid(connector); -+ ret = amdgpu_connector_ddc_get_modes(connector); -+ -+ return ret; -+} -+ -+static int amdgpu_connector_vga_mode_valid(struct drm_connector *connector, -+ struct drm_display_mode *mode) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ /* XXX check mode bandwidth */ -+ -+ if ((mode->clock / 10) > adev->clock.max_pixel_clock) -+ return MODE_CLOCK_HIGH; -+ -+ return MODE_OK; -+} -+ -+static enum drm_connector_status -+amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_encoder *encoder; -+ struct drm_encoder_helper_funcs *encoder_funcs; -+ bool dret = false; -+ enum drm_connector_status ret = connector_status_disconnected; -+ int r; -+ -+ r = pm_runtime_get_sync(connector->dev->dev); -+ if (r < 0) -+ return connector_status_disconnected; -+ -+ encoder = amdgpu_connector_best_single_encoder(connector); -+ if (!encoder) -+ ret = connector_status_disconnected; -+ -+ if (amdgpu_connector->ddc_bus) -+ dret = amdgpu_ddc_probe(amdgpu_connector, false); -+ if (dret) { -+ amdgpu_connector->detected_by_load = false; -+ amdgpu_connector_free_edid(connector); -+ amdgpu_connector_get_edid(connector); -+ -+ if (!amdgpu_connector->edid) { -+ DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", -+ connector->name); -+ ret = connector_status_connected; -+ } else { -+ amdgpu_connector->use_digital = -+ !!(amdgpu_connector->edid->input & DRM_EDID_INPUT_DIGITAL); -+ -+ /* some oems have boards with separate digital and analog connectors -+ * with a shared ddc line (often vga + hdmi) -+ */ -+ if (amdgpu_connector->use_digital && amdgpu_connector->shared_ddc) { -+ amdgpu_connector_free_edid(connector); -+ ret = connector_status_disconnected; -+ } else { -+ ret = connector_status_connected; -+ } -+ } -+ } else { -+ -+ /* if we aren't forcing don't do destructive polling */ -+ if (!force) { -+ /* only return the previous status if we last -+ * detected a monitor via load. -+ */ -+ if (amdgpu_connector->detected_by_load) -+ ret = connector->status; -+ goto out; -+ } -+ -+ if (amdgpu_connector->dac_load_detect && encoder) { -+ encoder_funcs = encoder->helper_private; -+ ret = encoder_funcs->detect(encoder, connector); -+ if (ret != connector_status_disconnected) -+ amdgpu_connector->detected_by_load = true; -+ } -+ } -+ -+ amdgpu_connector_update_scratch_regs(connector, ret); -+ -+out: -+ pm_runtime_mark_last_busy(connector->dev->dev); -+ pm_runtime_put_autosuspend(connector->dev->dev); -+ -+ return ret; -+} -+ -+static const struct drm_connector_helper_funcs amdgpu_connector_vga_helper_funcs = { -+ .get_modes = amdgpu_connector_vga_get_modes, -+ .mode_valid = amdgpu_connector_vga_mode_valid, -+ .best_encoder = amdgpu_connector_best_single_encoder, -+}; -+ -+static const struct drm_connector_funcs amdgpu_connector_vga_funcs = { -+ .dpms = drm_helper_connector_dpms, -+ .detect = amdgpu_connector_vga_detect, -+ .fill_modes = drm_helper_probe_single_connector_modes, -+ .destroy = amdgpu_connector_destroy, -+ .set_property = amdgpu_connector_set_property, -+}; -+ -+static bool -+amdgpu_connector_check_hpd_status_unchanged(struct drm_connector *connector) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ enum drm_connector_status status; -+ -+ if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE) { -+ if (amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) -+ status = connector_status_connected; -+ else -+ status = connector_status_disconnected; -+ if (connector->status == status) -+ return true; -+ } -+ -+ return false; -+} -+ -+/* -+ * DVI is complicated -+ * Do a DDC probe, if DDC probe passes, get the full EDID so -+ * we can do analog/digital monitor detection at this point. -+ * If the monitor is an analog monitor or we got no DDC, -+ * we need to find the DAC encoder object for this connector. -+ * If we got no DDC, we do load detection on the DAC encoder object. -+ * If we got analog DDC or load detection passes on the DAC encoder -+ * we have to check if this analog encoder is shared with anyone else (TV) -+ * if its shared we have to set the other connector to disconnected. -+ */ -+static enum drm_connector_status -+amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_encoder *encoder = NULL; -+ struct drm_encoder_helper_funcs *encoder_funcs; -+ int i, r; -+ enum drm_connector_status ret = connector_status_disconnected; -+ bool dret = false, broken_edid = false; -+ -+ r = pm_runtime_get_sync(connector->dev->dev); -+ if (r < 0) -+ return connector_status_disconnected; -+ -+ if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { -+ ret = connector->status; -+ goto exit; -+ } -+ -+ if (amdgpu_connector->ddc_bus) -+ dret = amdgpu_ddc_probe(amdgpu_connector, false); -+ if (dret) { -+ amdgpu_connector->detected_by_load = false; -+ amdgpu_connector_free_edid(connector); -+ amdgpu_connector_get_edid(connector); -+ -+ if (!amdgpu_connector->edid) { -+ DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", -+ connector->name); -+ ret = connector_status_connected; -+ broken_edid = true; /* defer use_digital to later */ -+ } else { -+ amdgpu_connector->use_digital = -+ !!(amdgpu_connector->edid->input & DRM_EDID_INPUT_DIGITAL); -+ -+ /* some oems have boards with separate digital and analog connectors -+ * with a shared ddc line (often vga + hdmi) -+ */ -+ if ((!amdgpu_connector->use_digital) && amdgpu_connector->shared_ddc) { -+ amdgpu_connector_free_edid(connector); -+ ret = connector_status_disconnected; -+ } else { -+ ret = connector_status_connected; -+ } -+ -+ /* This gets complicated. We have boards with VGA + HDMI with a -+ * shared DDC line and we have boards with DVI-D + HDMI with a shared -+ * DDC line. The latter is more complex because with DVI<->HDMI adapters -+ * you don't really know what's connected to which port as both are digital. -+ */ -+ if (amdgpu_connector->shared_ddc && (ret == connector_status_connected)) { -+ struct drm_connector *list_connector; -+ struct amdgpu_connector *list_amdgpu_connector; -+ list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { -+ if (connector == list_connector) -+ continue; -+ list_amdgpu_connector = to_amdgpu_connector(list_connector); -+ if (list_amdgpu_connector->shared_ddc && -+ (list_amdgpu_connector->ddc_bus->rec.i2c_id == -+ amdgpu_connector->ddc_bus->rec.i2c_id)) { -+ /* cases where both connectors are digital */ -+ if (list_connector->connector_type != DRM_MODE_CONNECTOR_VGA) { -+ /* hpd is our only option in this case */ -+ if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { -+ amdgpu_connector_free_edid(connector); -+ ret = connector_status_disconnected; -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ if ((ret == connector_status_connected) && (amdgpu_connector->use_digital == true)) -+ goto out; -+ -+ /* DVI-D and HDMI-A are digital only */ -+ if ((connector->connector_type == DRM_MODE_CONNECTOR_DVID) || -+ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)) -+ goto out; -+ -+ /* if we aren't forcing don't do destructive polling */ -+ if (!force) { -+ /* only return the previous status if we last -+ * detected a monitor via load. -+ */ -+ if (amdgpu_connector->detected_by_load) -+ ret = connector->status; -+ goto out; -+ } -+ -+ /* find analog encoder */ -+ if (amdgpu_connector->dac_load_detect) { -+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -+ if (connector->encoder_ids[i] == 0) -+ break; -+ -+ encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); -+ if (!encoder) -+ continue; -+ -+ if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && -+ encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) -+ continue; -+ -+ encoder_funcs = encoder->helper_private; -+ if (encoder_funcs->detect) { -+ if (!broken_edid) { -+ if (ret != connector_status_connected) { -+ /* deal with analog monitors without DDC */ -+ ret = encoder_funcs->detect(encoder, connector); -+ if (ret == connector_status_connected) { -+ amdgpu_connector->use_digital = false; -+ } -+ if (ret != connector_status_disconnected) -+ amdgpu_connector->detected_by_load = true; -+ } -+ } else { -+ enum drm_connector_status lret; -+ /* assume digital unless load detected otherwise */ -+ amdgpu_connector->use_digital = true; -+ lret = encoder_funcs->detect(encoder, connector); -+ DRM_DEBUG_KMS("load_detect %x returned: %x\n",encoder->encoder_type,lret); -+ if (lret == connector_status_connected) -+ amdgpu_connector->use_digital = false; -+ } -+ break; -+ } -+ } -+ } -+ -+out: -+ /* updated in get modes as well since we need to know if it's analog or digital */ -+ amdgpu_connector_update_scratch_regs(connector, ret); -+ -+exit: -+ pm_runtime_mark_last_busy(connector->dev->dev); -+ pm_runtime_put_autosuspend(connector->dev->dev); -+ -+ return ret; -+} -+ -+/* okay need to be smart in here about which encoder to pick */ -+static struct drm_encoder * -+amdgpu_connector_dvi_encoder(struct drm_connector *connector) -+{ -+ int enc_id = connector->encoder_ids[0]; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_encoder *encoder; -+ int i; -+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -+ if (connector->encoder_ids[i] == 0) -+ break; -+ -+ encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); -+ if (!encoder) -+ continue; -+ -+ if (amdgpu_connector->use_digital == true) { -+ if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) -+ return encoder; -+ } else { -+ if (encoder->encoder_type == DRM_MODE_ENCODER_DAC || -+ encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) -+ return encoder; -+ } -+ } -+ -+ /* see if we have a default encoder TODO */ -+ -+ /* then check use digitial */ -+ /* pick the first one */ -+ if (enc_id) -+ return drm_encoder_find(connector->dev, enc_id); -+ return NULL; -+} -+ -+static void amdgpu_connector_dvi_force(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ if (connector->force == DRM_FORCE_ON) -+ amdgpu_connector->use_digital = false; -+ if (connector->force == DRM_FORCE_ON_DIGITAL) -+ amdgpu_connector->use_digital = true; -+} -+ -+static int amdgpu_connector_dvi_mode_valid(struct drm_connector *connector, -+ struct drm_display_mode *mode) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ -+ /* XXX check mode bandwidth */ -+ -+ if (amdgpu_connector->use_digital && (mode->clock > 165000)) { -+ if ((amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) || -+ (amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || -+ (amdgpu_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) { -+ return MODE_OK; -+ } else if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ /* HDMI 1.3+ supports max clock of 340 Mhz */ -+ if (mode->clock > 340000) -+ return MODE_CLOCK_HIGH; -+ else -+ return MODE_OK; -+ } else { -+ return MODE_CLOCK_HIGH; -+ } -+ } -+ -+ /* check against the max pixel clock */ -+ if ((mode->clock / 10) > adev->clock.max_pixel_clock) -+ return MODE_CLOCK_HIGH; -+ -+ return MODE_OK; -+} -+ -+static const struct drm_connector_helper_funcs amdgpu_connector_dvi_helper_funcs = { -+ .get_modes = amdgpu_connector_vga_get_modes, -+ .mode_valid = amdgpu_connector_dvi_mode_valid, -+ .best_encoder = amdgpu_connector_dvi_encoder, -+}; -+ -+static const struct drm_connector_funcs amdgpu_connector_dvi_funcs = { -+ .dpms = drm_helper_connector_dpms, -+ .detect = amdgpu_connector_dvi_detect, -+ .fill_modes = drm_helper_probe_single_connector_modes, -+ .set_property = amdgpu_connector_set_property, -+ .destroy = amdgpu_connector_destroy, -+ .force = amdgpu_connector_dvi_force, -+}; -+ -+static int amdgpu_connector_dp_get_modes(struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *amdgpu_dig_connector = amdgpu_connector->con_priv; -+ struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); -+ int ret; -+ -+ if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || -+ (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { -+ struct drm_display_mode *mode; -+ -+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { -+ if (!amdgpu_dig_connector->edp_on) -+ amdgpu_atombios_encoder_set_edp_panel_power(connector, -+ ATOM_TRANSMITTER_ACTION_POWER_ON); -+ amdgpu_connector_get_edid(connector); -+ ret = amdgpu_connector_ddc_get_modes(connector); -+ if (!amdgpu_dig_connector->edp_on) -+ amdgpu_atombios_encoder_set_edp_panel_power(connector, -+ ATOM_TRANSMITTER_ACTION_POWER_OFF); -+ } else { -+ /* need to setup ddc on the bridge */ -+ if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != -+ ENCODER_OBJECT_ID_NONE) { -+ if (encoder) -+ amdgpu_atombios_encoder_setup_ext_encoder_ddc(encoder); -+ } -+ amdgpu_connector_get_edid(connector); -+ ret = amdgpu_connector_ddc_get_modes(connector); -+ } -+ -+ if (ret > 0) { -+ if (encoder) { -+ amdgpu_connector_fixup_lcd_native_mode(encoder, connector); -+ /* add scaled modes */ -+ amdgpu_connector_add_common_modes(encoder, connector); -+ } -+ return ret; -+ } -+ -+ if (!encoder) -+ return 0; -+ -+ /* we have no EDID modes */ -+ mode = amdgpu_connector_lcd_native_mode(encoder); -+ if (mode) { -+ ret = 1; -+ drm_mode_probed_add(connector, mode); -+ /* add the width/height from vbios tables if available */ -+ connector->display_info.width_mm = mode->width_mm; -+ connector->display_info.height_mm = mode->height_mm; -+ /* add scaled modes */ -+ amdgpu_connector_add_common_modes(encoder, connector); -+ } -+ } else { -+ /* need to setup ddc on the bridge */ -+ if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != -+ ENCODER_OBJECT_ID_NONE) { -+ if (encoder) -+ amdgpu_atombios_encoder_setup_ext_encoder_ddc(encoder); -+ } -+ amdgpu_connector_get_edid(connector); -+ ret = amdgpu_connector_ddc_get_modes(connector); -+ -+ amdgpu_get_native_mode(connector); -+ } -+ -+ return ret; -+} -+ -+u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector) -+{ -+ struct drm_encoder *encoder; -+ struct amdgpu_encoder *amdgpu_encoder; -+ int i; -+ -+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -+ if (connector->encoder_ids[i] == 0) -+ break; -+ -+ encoder = drm_encoder_find(connector->dev, -+ connector->encoder_ids[i]); -+ if (!encoder) -+ continue; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_TRAVIS: -+ case ENCODER_OBJECT_ID_NUTMEG: -+ return amdgpu_encoder->encoder_id; -+ default: -+ break; -+ } -+ } -+ -+ return ENCODER_OBJECT_ID_NONE; -+} -+ -+static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector) -+{ -+ struct drm_encoder *encoder; -+ struct amdgpu_encoder *amdgpu_encoder; -+ int i; -+ bool found = false; -+ -+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -+ if (connector->encoder_ids[i] == 0) -+ break; -+ encoder = drm_encoder_find(connector->dev, -+ connector->encoder_ids[i]); -+ if (!encoder) -+ continue; -+ -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ if (amdgpu_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) -+ found = true; -+ } -+ -+ return found; -+} -+ -+bool amdgpu_connector_is_dp12_capable(struct drm_connector *connector) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if ((adev->clock.default_dispclk >= 53900) && -+ amdgpu_connector_encoder_is_hbr2(connector)) { -+ return true; -+ } -+ -+ return false; -+} -+ -+static enum drm_connector_status -+amdgpu_connector_dp_detect(struct drm_connector *connector, bool force) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ enum drm_connector_status ret = connector_status_disconnected; -+ struct amdgpu_connector_atom_dig *amdgpu_dig_connector = amdgpu_connector->con_priv; -+ struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); -+ int r; -+ -+ r = pm_runtime_get_sync(connector->dev->dev); -+ if (r < 0) -+ return connector_status_disconnected; -+ -+ if (!force && amdgpu_connector_check_hpd_status_unchanged(connector)) { -+ ret = connector->status; -+ goto out; -+ } -+ -+ amdgpu_connector_free_edid(connector); -+ -+ if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || -+ (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { -+ if (encoder) { -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ -+ /* check if panel is valid */ -+ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) -+ ret = connector_status_connected; -+ } -+ /* eDP is always DP */ -+ amdgpu_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; -+ if (!amdgpu_dig_connector->edp_on) -+ amdgpu_atombios_encoder_set_edp_panel_power(connector, -+ ATOM_TRANSMITTER_ACTION_POWER_ON); -+ if (!amdgpu_atombios_dp_get_dpcd(amdgpu_connector)) -+ ret = connector_status_connected; -+ if (!amdgpu_dig_connector->edp_on) -+ amdgpu_atombios_encoder_set_edp_panel_power(connector, -+ ATOM_TRANSMITTER_ACTION_POWER_OFF); -+ } else if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) != -+ ENCODER_OBJECT_ID_NONE) { -+ /* DP bridges are always DP */ -+ amdgpu_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; -+ /* get the DPCD from the bridge */ -+ amdgpu_atombios_dp_get_dpcd(amdgpu_connector); -+ -+ if (encoder) { -+ /* setup ddc on the bridge */ -+ amdgpu_atombios_encoder_setup_ext_encoder_ddc(encoder); -+ /* bridge chips are always aux */ -+ if (amdgpu_ddc_probe(amdgpu_connector, true)) /* try DDC */ -+ ret = connector_status_connected; -+ else if (amdgpu_connector->dac_load_detect) { /* try load detection */ -+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; -+ ret = encoder_funcs->detect(encoder, connector); -+ } -+ } -+ } else { -+ amdgpu_dig_connector->dp_sink_type = -+ amdgpu_atombios_dp_get_sinktype(amdgpu_connector); -+ if (amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { -+ ret = connector_status_connected; -+ if (amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) -+ amdgpu_atombios_dp_get_dpcd(amdgpu_connector); -+ } else { -+ if (amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { -+ if (!amdgpu_atombios_dp_get_dpcd(amdgpu_connector)) -+ ret = connector_status_connected; -+ } else { -+ /* try non-aux ddc (DP to DVI/HDMI/etc. adapter) */ -+ if (amdgpu_ddc_probe(amdgpu_connector, false)) -+ ret = connector_status_connected; -+ } -+ } -+ } -+ -+ amdgpu_connector_update_scratch_regs(connector, ret); -+out: -+ pm_runtime_mark_last_busy(connector->dev->dev); -+ pm_runtime_put_autosuspend(connector->dev->dev); -+ -+ return ret; -+} -+ -+static int amdgpu_connector_dp_mode_valid(struct drm_connector *connector, -+ struct drm_display_mode *mode) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *amdgpu_dig_connector = amdgpu_connector->con_priv; -+ -+ /* XXX check mode bandwidth */ -+ -+ if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || -+ (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { -+ struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector); -+ -+ if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) -+ return MODE_PANEL; -+ -+ if (encoder) { -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ -+ /* AVIVO hardware supports downscaling modes larger than the panel -+ * to the panel size, but I'm not sure this is desirable. -+ */ -+ if ((mode->hdisplay > native_mode->hdisplay) || -+ (mode->vdisplay > native_mode->vdisplay)) -+ return MODE_PANEL; -+ -+ /* if scaling is disabled, block non-native modes */ -+ if (amdgpu_encoder->rmx_type == RMX_OFF) { -+ if ((mode->hdisplay != native_mode->hdisplay) || -+ (mode->vdisplay != native_mode->vdisplay)) -+ return MODE_PANEL; -+ } -+ } -+ return MODE_OK; -+ } else { -+ if ((amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || -+ (amdgpu_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { -+ return amdgpu_atombios_dp_mode_valid_helper(connector, mode); -+ } else { -+ if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ /* HDMI 1.3+ supports max clock of 340 Mhz */ -+ if (mode->clock > 340000) -+ return MODE_CLOCK_HIGH; -+ } else { -+ if (mode->clock > 165000) -+ return MODE_CLOCK_HIGH; -+ } -+ } -+ } -+ -+ return MODE_OK; -+} -+ -+static const struct drm_connector_helper_funcs amdgpu_connector_dp_helper_funcs = { -+ .get_modes = amdgpu_connector_dp_get_modes, -+ .mode_valid = amdgpu_connector_dp_mode_valid, -+ .best_encoder = amdgpu_connector_dvi_encoder, -+}; -+ -+static const struct drm_connector_funcs amdgpu_connector_dp_funcs = { -+ .dpms = drm_helper_connector_dpms, -+ .detect = amdgpu_connector_dp_detect, -+ .fill_modes = drm_helper_probe_single_connector_modes, -+ .set_property = amdgpu_connector_set_property, -+ .destroy = amdgpu_connector_destroy, -+ .force = amdgpu_connector_dvi_force, -+}; -+ -+static const struct drm_connector_funcs amdgpu_connector_edp_funcs = { -+ .dpms = drm_helper_connector_dpms, -+ .detect = amdgpu_connector_dp_detect, -+ .fill_modes = drm_helper_probe_single_connector_modes, -+ .set_property = amdgpu_connector_set_lcd_property, -+ .destroy = amdgpu_connector_destroy, -+ .force = amdgpu_connector_dvi_force, -+}; -+ -+void -+amdgpu_connector_add(struct amdgpu_device *adev, -+ uint32_t connector_id, -+ uint32_t supported_device, -+ int connector_type, -+ struct amdgpu_i2c_bus_rec *i2c_bus, -+ uint16_t connector_object_id, -+ struct amdgpu_hpd *hpd, -+ struct amdgpu_router *router) -+{ -+ struct drm_device *dev = adev->ddev; -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ struct amdgpu_connector_atom_dig *amdgpu_dig_connector; -+ struct drm_encoder *encoder; -+ struct amdgpu_encoder *amdgpu_encoder; -+ uint32_t subpixel_order = SubPixelNone; -+ bool shared_ddc = false; -+ bool is_dp_bridge = false; -+ bool has_aux = false; -+ -+ if (connector_type == DRM_MODE_CONNECTOR_Unknown) -+ return; -+ -+ /* see if we already added it */ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ amdgpu_connector = to_amdgpu_connector(connector); -+ if (amdgpu_connector->connector_id == connector_id) { -+ amdgpu_connector->devices |= supported_device; -+ return; -+ } -+ if (amdgpu_connector->ddc_bus && i2c_bus->valid) { -+ if (amdgpu_connector->ddc_bus->rec.i2c_id == i2c_bus->i2c_id) { -+ amdgpu_connector->shared_ddc = true; -+ shared_ddc = true; -+ } -+ if (amdgpu_connector->router_bus && router->ddc_valid && -+ (amdgpu_connector->router.router_id == router->router_id)) { -+ amdgpu_connector->shared_ddc = false; -+ shared_ddc = false; -+ } -+ } -+ } -+ -+ /* check if it's a dp bridge */ -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ if (amdgpu_encoder->devices & supported_device) { -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_TRAVIS: -+ case ENCODER_OBJECT_ID_NUTMEG: -+ is_dp_bridge = true; -+ break; -+ default: -+ break; -+ } -+ } -+ } -+ -+ amdgpu_connector = kzalloc(sizeof(struct amdgpu_connector), GFP_KERNEL); -+ if (!amdgpu_connector) -+ return; -+ -+ connector = &amdgpu_connector->base; -+ -+ amdgpu_connector->connector_id = connector_id; -+ amdgpu_connector->devices = supported_device; -+ amdgpu_connector->shared_ddc = shared_ddc; -+ amdgpu_connector->connector_object_id = connector_object_id; -+ amdgpu_connector->hpd = *hpd; -+ -+ amdgpu_connector->router = *router; -+ if (router->ddc_valid || router->cd_valid) { -+ amdgpu_connector->router_bus = amdgpu_i2c_lookup(adev, &router->i2c_info); -+ if (!amdgpu_connector->router_bus) -+ DRM_ERROR("Failed to assign router i2c bus! Check dmesg for i2c errors.\n"); -+ } -+ -+ if (is_dp_bridge) { -+ amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); -+ if (!amdgpu_dig_connector) -+ goto failed; -+ amdgpu_connector->con_priv = amdgpu_dig_connector; -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (amdgpu_connector->ddc_bus) -+ has_aux = true; -+ else -+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ switch (connector_type) { -+ case DRM_MODE_CONNECTOR_VGA: -+ case DRM_MODE_CONNECTOR_DVIA: -+ default: -+ drm_connector_init(dev, &amdgpu_connector->base, -+ &amdgpu_connector_dp_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, -+ &amdgpu_connector_dp_helper_funcs); -+ connector->interlace_allowed = true; -+ connector->doublescan_allowed = true; -+ amdgpu_connector->dac_load_detect = true; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.load_detect_property, -+ 1); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ break; -+ case DRM_MODE_CONNECTOR_DVII: -+ case DRM_MODE_CONNECTOR_DVID: -+ case DRM_MODE_CONNECTOR_HDMIA: -+ case DRM_MODE_CONNECTOR_HDMIB: -+ case DRM_MODE_CONNECTOR_DisplayPort: -+ drm_connector_init(dev, &amdgpu_connector->base, -+ &amdgpu_connector_dp_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, -+ &amdgpu_connector_dp_helper_funcs); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_property, -+ UNDERSCAN_OFF); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_hborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_vborder_property, -+ 0); -+ -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.dither_property, -+ AMDGPU_FMT_DITHER_DISABLE); -+ -+ if (amdgpu_audio != 0) -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.audio_property, -+ AMDGPU_AUDIO_AUTO); -+ -+ subpixel_order = SubPixelHorizontalRGB; -+ connector->interlace_allowed = true; -+ if (connector_type == DRM_MODE_CONNECTOR_HDMIB) -+ connector->doublescan_allowed = true; -+ else -+ connector->doublescan_allowed = false; -+ if (connector_type == DRM_MODE_CONNECTOR_DVII) { -+ amdgpu_connector->dac_load_detect = true; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.load_detect_property, -+ 1); -+ } -+ break; -+ case DRM_MODE_CONNECTOR_LVDS: -+ case DRM_MODE_CONNECTOR_eDP: -+ drm_connector_init(dev, &amdgpu_connector->base, -+ &amdgpu_connector_edp_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, -+ &amdgpu_connector_dp_helper_funcs); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_FULLSCREEN); -+ subpixel_order = SubPixelHorizontalRGB; -+ connector->interlace_allowed = false; -+ connector->doublescan_allowed = false; -+ break; -+ } -+ } else { -+ switch (connector_type) { -+ case DRM_MODE_CONNECTOR_VGA: -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_vga_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_vga_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (!amdgpu_connector->ddc_bus) -+ DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ amdgpu_connector->dac_load_detect = true; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.load_detect_property, -+ 1); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ /* no HPD on analog connectors */ -+ amdgpu_connector->hpd.hpd = AMDGPU_HPD_NONE; -+ connector->polled = DRM_CONNECTOR_POLL_CONNECT; -+ connector->interlace_allowed = true; -+ connector->doublescan_allowed = true; -+ break; -+ case DRM_MODE_CONNECTOR_DVIA: -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_vga_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_vga_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (!amdgpu_connector->ddc_bus) -+ DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ amdgpu_connector->dac_load_detect = true; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.load_detect_property, -+ 1); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ /* no HPD on analog connectors */ -+ amdgpu_connector->hpd.hpd = AMDGPU_HPD_NONE; -+ connector->interlace_allowed = true; -+ connector->doublescan_allowed = true; -+ break; -+ case DRM_MODE_CONNECTOR_DVII: -+ case DRM_MODE_CONNECTOR_DVID: -+ amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); -+ if (!amdgpu_dig_connector) -+ goto failed; -+ amdgpu_connector->con_priv = amdgpu_dig_connector; -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_dvi_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dvi_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (!amdgpu_connector->ddc_bus) -+ DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ subpixel_order = SubPixelHorizontalRGB; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.coherent_mode_property, -+ 1); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_property, -+ UNDERSCAN_OFF); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_hborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_vborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ -+ if (amdgpu_audio != 0) { -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.audio_property, -+ AMDGPU_AUDIO_AUTO); -+ } -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.dither_property, -+ AMDGPU_FMT_DITHER_DISABLE); -+ if (connector_type == DRM_MODE_CONNECTOR_DVII) { -+ amdgpu_connector->dac_load_detect = true; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.load_detect_property, -+ 1); -+ } -+ connector->interlace_allowed = true; -+ if (connector_type == DRM_MODE_CONNECTOR_DVII) -+ connector->doublescan_allowed = true; -+ else -+ connector->doublescan_allowed = false; -+ break; -+ case DRM_MODE_CONNECTOR_HDMIA: -+ case DRM_MODE_CONNECTOR_HDMIB: -+ amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); -+ if (!amdgpu_dig_connector) -+ goto failed; -+ amdgpu_connector->con_priv = amdgpu_dig_connector; -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_dvi_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dvi_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (!amdgpu_connector->ddc_bus) -+ DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.coherent_mode_property, -+ 1); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_property, -+ UNDERSCAN_OFF); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_hborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_vborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ if (amdgpu_audio != 0) { -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.audio_property, -+ AMDGPU_AUDIO_AUTO); -+ } -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.dither_property, -+ AMDGPU_FMT_DITHER_DISABLE); -+ subpixel_order = SubPixelHorizontalRGB; -+ connector->interlace_allowed = true; -+ if (connector_type == DRM_MODE_CONNECTOR_HDMIB) -+ connector->doublescan_allowed = true; -+ else -+ connector->doublescan_allowed = false; -+ break; -+ case DRM_MODE_CONNECTOR_DisplayPort: -+ amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); -+ if (!amdgpu_dig_connector) -+ goto failed; -+ amdgpu_connector->con_priv = amdgpu_dig_connector; -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_dp_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dp_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (amdgpu_connector->ddc_bus) -+ has_aux = true; -+ else -+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ subpixel_order = SubPixelHorizontalRGB; -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.coherent_mode_property, -+ 1); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_property, -+ UNDERSCAN_OFF); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_hborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.underscan_vborder_property, -+ 0); -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_NONE); -+ if (amdgpu_audio != 0) { -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.audio_property, -+ AMDGPU_AUDIO_AUTO); -+ } -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ adev->mode_info.dither_property, -+ AMDGPU_FMT_DITHER_DISABLE); -+ connector->interlace_allowed = true; -+ /* in theory with a DP to VGA converter... */ -+ connector->doublescan_allowed = false; -+ break; -+ case DRM_MODE_CONNECTOR_eDP: -+ amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); -+ if (!amdgpu_dig_connector) -+ goto failed; -+ amdgpu_connector->con_priv = amdgpu_dig_connector; -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_edp_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_dp_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (amdgpu_connector->ddc_bus) -+ has_aux = true; -+ else -+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_FULLSCREEN); -+ subpixel_order = SubPixelHorizontalRGB; -+ connector->interlace_allowed = false; -+ connector->doublescan_allowed = false; -+ break; -+ case DRM_MODE_CONNECTOR_LVDS: -+ amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL); -+ if (!amdgpu_dig_connector) -+ goto failed; -+ amdgpu_connector->con_priv = amdgpu_dig_connector; -+ drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_lvds_funcs, connector_type); -+ drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_lvds_helper_funcs); -+ if (i2c_bus->valid) { -+ amdgpu_connector->ddc_bus = amdgpu_i2c_lookup(adev, i2c_bus); -+ if (!amdgpu_connector->ddc_bus) -+ DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); -+ } -+ drm_object_attach_property(&amdgpu_connector->base.base, -+ dev->mode_config.scaling_mode_property, -+ DRM_MODE_SCALE_FULLSCREEN); -+ subpixel_order = SubPixelHorizontalRGB; -+ connector->interlace_allowed = false; -+ connector->doublescan_allowed = false; -+ break; -+ } -+ } -+ -+ if (amdgpu_connector->hpd.hpd == AMDGPU_HPD_NONE) { -+ if (i2c_bus->valid) -+ connector->polled = DRM_CONNECTOR_POLL_CONNECT; -+ } else -+ connector->polled = DRM_CONNECTOR_POLL_HPD; -+ -+ connector->display_info.subpixel_order = subpixel_order; -+ drm_connector_register(connector); -+ -+ if (has_aux) -+ amdgpu_atombios_dp_aux_init(amdgpu_connector); -+ -+ return; -+ -+failed: -+ drm_connector_cleanup(connector); -+ kfree(connector); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h -new file mode 100644 -index 0000000..61fcef1 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h -@@ -0,0 +1,42 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_CONNECTORS_H__ -+#define __AMDGPU_CONNECTORS_H__ -+ -+struct edid *amdgpu_connector_edid(struct drm_connector *connector); -+void amdgpu_connector_hotplug(struct drm_connector *connector); -+int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector); -+u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector); -+bool amdgpu_connector_is_dp12_capable(struct drm_connector *connector); -+void -+amdgpu_connector_add(struct amdgpu_device *adev, -+ uint32_t connector_id, -+ uint32_t supported_device, -+ int connector_type, -+ struct amdgpu_i2c_bus_rec *i2c_bus, -+ uint16_t connector_object_id, -+ struct amdgpu_hpd *hpd, -+ struct amdgpu_router *router); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c -new file mode 100644 -index 0000000..70a9031 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c -@@ -0,0 +1,825 @@ -+/* -+ * Copyright 2008 Jerome Glisse. -+ * All Rights Reserved. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * 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 -+ * PRECISION INSIGHT 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. -+ * -+ * Authors: -+ * Jerome Glisse <glisse@freedesktop.org> -+ */ -+#include <linux/list_sort.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_trace.h" -+ -+#define AMDGPU_CS_MAX_PRIORITY 32u -+#define AMDGPU_CS_NUM_BUCKETS (AMDGPU_CS_MAX_PRIORITY + 1) -+ -+/* This is based on the bucket sort with O(n) time complexity. -+ * An item with priority "i" is added to bucket[i]. The lists are then -+ * concatenated in descending order. -+ */ -+struct amdgpu_cs_buckets { -+ struct list_head bucket[AMDGPU_CS_NUM_BUCKETS]; -+}; -+ -+static void amdgpu_cs_buckets_init(struct amdgpu_cs_buckets *b) -+{ -+ unsigned i; -+ -+ for (i = 0; i < AMDGPU_CS_NUM_BUCKETS; i++) -+ INIT_LIST_HEAD(&b->bucket[i]); -+} -+ -+static void amdgpu_cs_buckets_add(struct amdgpu_cs_buckets *b, -+ struct list_head *item, unsigned priority) -+{ -+ /* Since buffers which appear sooner in the relocation list are -+ * likely to be used more often than buffers which appear later -+ * in the list, the sort mustn't change the ordering of buffers -+ * with the same priority, i.e. it must be stable. -+ */ -+ list_add_tail(item, &b->bucket[min(priority, AMDGPU_CS_MAX_PRIORITY)]); -+} -+ -+static void amdgpu_cs_buckets_get_list(struct amdgpu_cs_buckets *b, -+ struct list_head *out_list) -+{ -+ unsigned i; -+ -+ /* Connect the sorted buckets in the output list. */ -+ for (i = 0; i < AMDGPU_CS_NUM_BUCKETS; i++) { -+ list_splice(&b->bucket[i], out_list); -+ } -+} -+ -+int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type, -+ u32 ip_instance, u32 ring, -+ struct amdgpu_ring **out_ring) -+{ -+ /* Right now all IPs have only one instance - multiple rings. */ -+ if (ip_instance != 0) { -+ DRM_ERROR("invalid ip instance: %d\n", ip_instance); -+ return -EINVAL; -+ } -+ -+ switch (ip_type) { -+ default: -+ DRM_ERROR("unknown ip type: %d\n", ip_type); -+ return -EINVAL; -+ case AMDGPU_HW_IP_GFX: -+ if (ring < adev->gfx.num_gfx_rings) { -+ *out_ring = &adev->gfx.gfx_ring[ring]; -+ } else { -+ DRM_ERROR("only %d gfx rings are supported now\n", -+ adev->gfx.num_gfx_rings); -+ return -EINVAL; -+ } -+ break; -+ case AMDGPU_HW_IP_COMPUTE: -+ if (ring < adev->gfx.num_compute_rings) { -+ *out_ring = &adev->gfx.compute_ring[ring]; -+ } else { -+ DRM_ERROR("only %d compute rings are supported now\n", -+ adev->gfx.num_compute_rings); -+ return -EINVAL; -+ } -+ break; -+ case AMDGPU_HW_IP_DMA: -+ if (ring < 2) { -+ *out_ring = &adev->sdma[ring].ring; -+ } else { -+ DRM_ERROR("only two SDMA rings are supported\n"); -+ return -EINVAL; -+ } -+ break; -+ case AMDGPU_HW_IP_UVD: -+ *out_ring = &adev->uvd.ring; -+ break; -+ case AMDGPU_HW_IP_VCE: -+ if (ring < 2){ -+ *out_ring = &adev->vce.ring[ring]; -+ } else { -+ DRM_ERROR("only two VCE rings are supported\n"); -+ return -EINVAL; -+ } -+ break; -+ } -+ return 0; -+} -+ -+int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) -+{ -+ union drm_amdgpu_cs *cs = data; -+ uint64_t *chunk_array_user; -+ uint64_t *chunk_array = NULL; -+ struct amdgpu_fpriv *fpriv = p->filp->driver_priv; -+ unsigned size, i; -+ int r = 0; -+ -+ if (!cs->in.num_chunks) -+ goto out; -+ -+ p->ctx_id = cs->in.ctx_id; -+ p->bo_list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle); -+ -+ /* get chunks */ -+ INIT_LIST_HEAD(&p->validated); -+ chunk_array = kcalloc(cs->in.num_chunks, sizeof(uint64_t), GFP_KERNEL); -+ if (chunk_array == NULL) { -+ r = -ENOMEM; -+ goto out; -+ } -+ -+ chunk_array_user = (uint64_t *)(unsigned long)(cs->in.chunks); -+ if (copy_from_user(chunk_array, chunk_array_user, -+ sizeof(uint64_t)*cs->in.num_chunks)) { -+ r = -EFAULT; -+ goto out; -+ } -+ -+ p->nchunks = cs->in.num_chunks; -+ p->chunks = kcalloc(p->nchunks, sizeof(struct amdgpu_cs_chunk), -+ GFP_KERNEL); -+ if (p->chunks == NULL) { -+ r = -ENOMEM; -+ goto out; -+ } -+ -+ for (i = 0; i < p->nchunks; i++) { -+ struct drm_amdgpu_cs_chunk __user **chunk_ptr = NULL; -+ struct drm_amdgpu_cs_chunk user_chunk; -+ uint32_t __user *cdata; -+ -+ chunk_ptr = (void __user *)(unsigned long)chunk_array[i]; -+ if (copy_from_user(&user_chunk, chunk_ptr, -+ sizeof(struct drm_amdgpu_cs_chunk))) { -+ r = -EFAULT; -+ goto out; -+ } -+ p->chunks[i].chunk_id = user_chunk.chunk_id; -+ p->chunks[i].length_dw = user_chunk.length_dw; -+ if (p->chunks[i].chunk_id == AMDGPU_CHUNK_ID_IB) -+ p->num_ibs++; -+ -+ size = p->chunks[i].length_dw; -+ cdata = (void __user *)(unsigned long)user_chunk.chunk_data; -+ p->chunks[i].user_ptr = cdata; -+ -+ p->chunks[i].kdata = drm_malloc_ab(size, sizeof(uint32_t)); -+ if (p->chunks[i].kdata == NULL) { -+ r = -ENOMEM; -+ goto out; -+ } -+ size *= sizeof(uint32_t); -+ if (copy_from_user(p->chunks[i].kdata, cdata, size)) { -+ r = -EFAULT; -+ goto out; -+ } -+ -+ if (p->chunks[i].chunk_id == AMDGPU_CHUNK_ID_FENCE) { -+ size = sizeof(struct drm_amdgpu_cs_chunk_fence); -+ if (p->chunks[i].length_dw * sizeof(uint32_t) >= size) { -+ uint32_t handle; -+ struct drm_gem_object *gobj; -+ struct drm_amdgpu_cs_chunk_fence *fence_data; -+ -+ fence_data = (void *)p->chunks[i].kdata; -+ handle = fence_data->handle; -+ gobj = drm_gem_object_lookup(p->adev->ddev, -+ p->filp, handle); -+ if (gobj == NULL) { -+ r = -EINVAL; -+ goto out; -+ } -+ -+ p->uf.bo = gem_to_amdgpu_bo(gobj); -+ p->uf.offset = fence_data->offset; -+ } else { -+ r = -EINVAL; -+ goto out; -+ } -+ } -+ } -+ -+ p->ibs = kcalloc(p->num_ibs, sizeof(struct amdgpu_ib), GFP_KERNEL); -+ if (!p->ibs) { -+ r = -ENOMEM; -+ goto out; -+ } -+ -+ p->ib_bos = kcalloc(p->num_ibs, sizeof(struct amdgpu_bo_list_entry), -+ GFP_KERNEL); -+ if (!p->ib_bos) -+ r = -ENOMEM; -+ -+out: -+ kfree(chunk_array); -+ return r; -+} -+ -+/* Returns how many bytes TTM can move per IB. -+ */ -+static u64 amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev) -+{ -+ u64 real_vram_size = adev->mc.real_vram_size; -+ u64 vram_usage = atomic64_read(&adev->vram_usage); -+ -+ /* This function is based on the current VRAM usage. -+ * -+ * - If all of VRAM is free, allow relocating the number of bytes that -+ * is equal to 1/4 of the size of VRAM for this IB. -+ -+ * - If more than one half of VRAM is occupied, only allow relocating -+ * 1 MB of data for this IB. -+ * -+ * - From 0 to one half of used VRAM, the threshold decreases -+ * linearly. -+ * __________________ -+ * 1/4 of -|\ | -+ * VRAM | \ | -+ * | \ | -+ * | \ | -+ * | \ | -+ * | \ | -+ * | \ | -+ * | \________|1 MB -+ * |----------------| -+ * VRAM 0 % 100 % -+ * used used -+ * -+ * Note: It's a threshold, not a limit. The threshold must be crossed -+ * for buffer relocations to stop, so any buffer of an arbitrary size -+ * can be moved as long as the threshold isn't crossed before -+ * the relocation takes place. We don't want to disable buffer -+ * relocations completely. -+ * -+ * The idea is that buffers should be placed in VRAM at creation time -+ * and TTM should only do a minimum number of relocations during -+ * command submission. In practice, you need to submit at least -+ * a dozen IBs to move all buffers to VRAM if they are in GTT. -+ * -+ * Also, things can get pretty crazy under memory pressure and actual -+ * VRAM usage can change a lot, so playing safe even at 50% does -+ * consistently increase performance. -+ */ -+ -+ u64 half_vram = real_vram_size >> 1; -+ u64 half_free_vram = vram_usage >= half_vram ? 0 : half_vram - vram_usage; -+ u64 bytes_moved_threshold = half_free_vram >> 1; -+ return max(bytes_moved_threshold, 1024*1024ull); -+} -+ -+int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p) -+{ -+ struct amdgpu_fpriv *fpriv = p->filp->driver_priv; -+ struct amdgpu_vm *vm = &fpriv->vm; -+ struct amdgpu_device *adev = p->adev; -+ struct amdgpu_bo_list_entry *lobj; -+ struct list_head duplicates; -+ struct amdgpu_bo *bo; -+ u64 bytes_moved = 0, initial_bytes_moved; -+ u64 bytes_moved_threshold = amdgpu_cs_get_threshold_for_moves(adev); -+ int r; -+ -+ INIT_LIST_HEAD(&duplicates); -+ r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true, &duplicates); -+ if (unlikely(r != 0)) { -+ return r; -+ } -+ -+ list_for_each_entry(lobj, &p->validated, tv.head) { -+ bo = lobj->robj; -+ if (!bo->pin_count) { -+ u32 domain = lobj->prefered_domains; -+ u32 current_domain = -+ amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type); -+ -+ /* Check if this buffer will be moved and don't move it -+ * if we have moved too many buffers for this IB already. -+ * -+ * Note that this allows moving at least one buffer of -+ * any size, because it doesn't take the current "bo" -+ * into account. We don't want to disallow buffer moves -+ * completely. -+ */ -+ if (current_domain != AMDGPU_GEM_DOMAIN_CPU && -+ (domain & current_domain) == 0 && /* will be moved */ -+ bytes_moved > bytes_moved_threshold) { -+ /* don't move it */ -+ domain = current_domain; -+ } -+ -+ retry: -+ amdgpu_ttm_placement_from_domain(bo, domain); -+ initial_bytes_moved = atomic64_read(&adev->num_bytes_moved); -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); -+ bytes_moved += atomic64_read(&adev->num_bytes_moved) - -+ initial_bytes_moved; -+ -+ if (unlikely(r)) { -+ if (r != -ERESTARTSYS && domain != lobj->allowed_domains) { -+ domain = lobj->allowed_domains; -+ goto retry; -+ } -+ ttm_eu_backoff_reservation(&p->ticket, &p->validated); -+ return r; -+ } -+ } -+ lobj->bo_va = amdgpu_vm_bo_find(vm, bo); -+ } -+ return 0; -+} -+ -+static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p) -+{ -+ struct amdgpu_fpriv *fpriv = p->filp->driver_priv; -+ struct amdgpu_cs_buckets buckets; -+ bool need_mmap_lock; -+ int i, r; -+ -+ if (p->bo_list == NULL) -+ return 0; -+ -+ need_mmap_lock = p->bo_list->has_userptr; -+ amdgpu_cs_buckets_init(&buckets); -+ for (i = 0; i < p->bo_list->num_entries; i++) -+ amdgpu_cs_buckets_add(&buckets, &p->bo_list->array[i].tv.head, -+ p->bo_list->array[i].priority); -+ -+ amdgpu_cs_buckets_get_list(&buckets, &p->validated); -+ p->vm_bos = amdgpu_vm_get_bos(p->adev, &fpriv->vm, -+ &p->validated); -+ -+ for (i = 0; i < p->num_ibs; i++) { -+ if (!p->ib_bos[i].robj) -+ continue; -+ -+ list_add(&p->ib_bos[i].tv.head, &p->validated); -+ } -+ -+ if (need_mmap_lock) -+ down_read(¤t->mm->mmap_sem); -+ -+ r = amdgpu_cs_list_validate(p); -+ -+ if (need_mmap_lock) -+ up_read(¤t->mm->mmap_sem); -+ -+ return r; -+} -+ -+static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) -+{ -+ struct amdgpu_bo_list_entry *e; -+ int r; -+ -+ list_for_each_entry(e, &p->validated, tv.head) { -+ struct reservation_object *resv = e->robj->tbo.resv; -+ r = amdgpu_sync_resv(p->adev, &p->ibs[0].sync, resv, p->filp); -+ -+ if (r) -+ return r; -+ } -+ return 0; -+} -+ -+static int cmp_size_smaller_first(void *priv, struct list_head *a, -+ struct list_head *b) -+{ -+ struct amdgpu_bo_list_entry *la = list_entry(a, struct amdgpu_bo_list_entry, tv.head); -+ struct amdgpu_bo_list_entry *lb = list_entry(b, struct amdgpu_bo_list_entry, tv.head); -+ -+ /* Sort A before B if A is smaller. */ -+ return (int)la->robj->tbo.num_pages - (int)lb->robj->tbo.num_pages; -+} -+ -+/** -+ * cs_parser_fini() - clean parser states -+ * @parser: parser structure holding parsing context. -+ * @error: error number -+ * -+ * If error is set than unvalidate buffer, otherwise just free memory -+ * used by parsing context. -+ **/ -+static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bool backoff) -+{ -+ unsigned i; -+ -+ if (!error) { -+ /* Sort the buffer list from the smallest to largest buffer, -+ * which affects the order of buffers in the LRU list. -+ * This assures that the smallest buffers are added first -+ * to the LRU list, so they are likely to be later evicted -+ * first, instead of large buffers whose eviction is more -+ * expensive. -+ * -+ * This slightly lowers the number of bytes moved by TTM -+ * per frame under memory pressure. -+ */ -+ list_sort(NULL, &parser->validated, cmp_size_smaller_first); -+ -+ ttm_eu_fence_buffer_objects(&parser->ticket, -+ &parser->validated, -+ &parser->ibs[parser->num_ibs-1].fence->base); -+ } else if (backoff) { -+ ttm_eu_backoff_reservation(&parser->ticket, -+ &parser->validated); -+ } -+ -+ if (parser->bo_list) -+ amdgpu_bo_list_put(parser->bo_list); -+ drm_free_large(parser->vm_bos); -+ for (i = 0; i < parser->nchunks; i++) -+ drm_free_large(parser->chunks[i].kdata); -+ kfree(parser->chunks); -+ for (i = 0; i < parser->num_ibs; i++) { -+ struct amdgpu_bo *bo = parser->ib_bos[i].robj; -+ amdgpu_ib_free(parser->adev, &parser->ibs[i]); -+ -+ if (bo) -+ drm_gem_object_unreference_unlocked(&bo->gem_base); -+ } -+ kfree(parser->ibs); -+ kfree(parser->ib_bos); -+ if (parser->uf.bo) -+ drm_gem_object_unreference_unlocked(&parser->uf.bo->gem_base); -+} -+ -+static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p, -+ struct amdgpu_vm *vm) -+{ -+ struct amdgpu_device *adev = p->adev; -+ struct amdgpu_bo_va *bo_va; -+ struct amdgpu_bo *bo; -+ int i, r; -+ -+ r = amdgpu_vm_update_page_directory(adev, vm); -+ if (r) -+ return r; -+ -+ r = amdgpu_vm_clear_freed(adev, vm); -+ if (r) -+ return r; -+ -+ if (p->bo_list) { -+ for (i = 0; i < p->bo_list->num_entries; i++) { -+ /* ignore duplicates */ -+ bo = p->bo_list->array[i].robj; -+ if (!bo) -+ continue; -+ -+ bo_va = p->bo_list->array[i].bo_va; -+ if (bo_va == NULL) -+ continue; -+ -+ r = amdgpu_vm_bo_update(adev, bo_va, &bo->tbo.mem); -+ if (r) -+ return r; -+ -+ amdgpu_sync_fence(&p->ibs[0].sync, bo_va->last_pt_update); -+ } -+ } -+ -+ for (i = 0; i < p->num_ibs; i++) { -+ bo = p->ib_bos[i].robj; -+ if (!bo) -+ continue; -+ -+ bo_va = p->ib_bos[i].bo_va; -+ if (!bo_va) -+ continue; -+ -+ r = amdgpu_vm_bo_update(adev, bo_va, &bo->tbo.mem); -+ if (r) -+ return r; -+ -+ amdgpu_sync_fence(&p->ibs[0].sync, bo_va->last_pt_update); -+ } -+ return amdgpu_vm_clear_invalids(adev, vm); -+} -+ -+static int amdgpu_cs_ib_vm_chunk(struct amdgpu_device *adev, -+ struct amdgpu_cs_parser *parser) -+{ -+ struct amdgpu_fpriv *fpriv = parser->filp->driver_priv; -+ struct amdgpu_vm *vm = &fpriv->vm; -+ struct amdgpu_ring *ring; -+ int i, r; -+ -+ if (parser->num_ibs == 0) -+ return 0; -+ -+ /* Only for UVD/VCE VM emulation */ -+ for (i = 0; i < parser->num_ibs; i++) { -+ ring = parser->ibs[i].ring; -+ if (ring->funcs->parse_cs) { -+ r = amdgpu_ring_parse_cs(ring, parser, i); -+ if (r) -+ return r; -+ } -+ } -+ -+ mutex_lock(&vm->mutex); -+ r = amdgpu_bo_vm_update_pte(parser, vm); -+ if (r) { -+ goto out; -+ } -+ amdgpu_cs_sync_rings(parser); -+ -+ r = amdgpu_ib_schedule(adev, parser->num_ibs, parser->ibs, -+ parser->filp); -+ -+out: -+ mutex_unlock(&vm->mutex); -+ return r; -+} -+ -+static int amdgpu_cs_handle_lockup(struct amdgpu_device *adev, int r) -+{ -+ if (r == -EDEADLK) { -+ r = amdgpu_gpu_reset(adev); -+ if (!r) -+ r = -EAGAIN; -+ } -+ return r; -+} -+ -+static int amdgpu_cs_ib_fill(struct amdgpu_device *adev, -+ struct amdgpu_cs_parser *parser) -+{ -+ struct amdgpu_fpriv *fpriv = parser->filp->driver_priv; -+ struct amdgpu_vm *vm = &fpriv->vm; -+ int i, j; -+ int r; -+ -+ for (i = 0, j = 0; i < parser->nchunks && j < parser->num_ibs; i++) { -+ struct amdgpu_cs_chunk *chunk; -+ struct amdgpu_ib *ib; -+ struct drm_amdgpu_cs_chunk_ib *chunk_ib; -+ struct amdgpu_bo_list_entry *ib_bo; -+ struct amdgpu_ring *ring; -+ struct drm_gem_object *gobj; -+ struct amdgpu_bo *aobj; -+ void *kptr; -+ -+ chunk = &parser->chunks[i]; -+ ib = &parser->ibs[j]; -+ chunk_ib = (struct drm_amdgpu_cs_chunk_ib *)chunk->kdata; -+ -+ if (chunk->chunk_id != AMDGPU_CHUNK_ID_IB) -+ continue; -+ -+ gobj = drm_gem_object_lookup(adev->ddev, parser->filp, chunk_ib->handle); -+ if (gobj == NULL) -+ return -ENOENT; -+ aobj = gem_to_amdgpu_bo(gobj); -+ -+ r = amdgpu_cs_get_ring(adev, chunk_ib->ip_type, -+ chunk_ib->ip_instance, chunk_ib->ring, -+ &ring); -+ if (r) { -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+ } -+ -+ if (ring->funcs->parse_cs) { -+ r = amdgpu_bo_reserve(aobj, false); -+ if (r) { -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+ } -+ -+ r = amdgpu_bo_kmap(aobj, &kptr); -+ if (r) { -+ amdgpu_bo_unreserve(aobj); -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+ } -+ -+ r = amdgpu_ib_get(ring, NULL, chunk_ib->ib_bytes, ib); -+ if (r) { -+ DRM_ERROR("Failed to get ib !\n"); -+ amdgpu_bo_unreserve(aobj); -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+ } -+ -+ memcpy(ib->ptr, kptr, chunk_ib->ib_bytes); -+ amdgpu_bo_kunmap(aobj); -+ amdgpu_bo_unreserve(aobj); -+ } else { -+ r = amdgpu_ib_get(ring, vm, 0, ib); -+ if (r) { -+ DRM_ERROR("Failed to get ib !\n"); -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+ } -+ -+ ib->gpu_addr = chunk_ib->va_start; -+ } -+ ib->length_dw = chunk_ib->ib_bytes / 4; -+ -+ if (chunk_ib->flags & AMDGPU_IB_FLAG_CE) -+ ib->is_const_ib = true; -+ if (chunk_ib->flags & AMDGPU_IB_FLAG_GDS) -+ ib->gds_needed = true; -+ if (ib->ring->current_filp != parser->filp) { -+ ib->ring->need_ctx_switch = true; -+ ib->ring->current_filp = parser->filp; -+ } -+ -+ ib_bo = &parser->ib_bos[j]; -+ ib_bo->robj = aobj; -+ ib_bo->prefered_domains = aobj->initial_domain; -+ ib_bo->allowed_domains = aobj->initial_domain; -+ ib_bo->priority = 0; -+ ib_bo->tv.bo = &aobj->tbo; -+ ib_bo->tv.shared = true; -+ j++; -+ } -+ -+ if (!parser->num_ibs) -+ return 0; -+ -+ /* add GDS resources to first IB */ -+ if (parser->bo_list) { -+ struct amdgpu_bo *gds = parser->bo_list->gds_obj; -+ struct amdgpu_bo *gws = parser->bo_list->gws_obj; -+ struct amdgpu_bo *oa = parser->bo_list->oa_obj; -+ struct amdgpu_ib *ib = &parser->ibs[0]; -+ -+ if (gds) { -+ ib->gds_base = amdgpu_bo_gpu_offset(gds); -+ ib->gds_size = amdgpu_bo_size(gds); -+ } -+ if (gws) { -+ ib->gws_base = amdgpu_bo_gpu_offset(gws); -+ ib->gws_size = amdgpu_bo_size(gws); -+ } -+ if (oa) { -+ ib->oa_base = amdgpu_bo_gpu_offset(oa); -+ ib->oa_size = amdgpu_bo_size(oa); -+ } -+ } -+ -+ /* wrap the last IB with user fence */ -+ if (parser->uf.bo) { -+ struct amdgpu_ib *ib = &parser->ibs[parser->num_ibs - 1]; -+ -+ /* UVD & VCE fw doesn't support user fences */ -+ if (ib->ring->type == AMDGPU_RING_TYPE_UVD || -+ ib->ring->type == AMDGPU_RING_TYPE_VCE) -+ return -EINVAL; -+ -+ ib->user = &parser->uf; -+ } -+ -+ return 0; -+} -+ -+int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ union drm_amdgpu_cs *cs = data; -+ struct amdgpu_cs_parser parser; -+ int r, i; -+ -+ down_read(&adev->exclusive_lock); -+ if (!adev->accel_working) { -+ up_read(&adev->exclusive_lock); -+ return -EBUSY; -+ } -+ /* initialize parser */ -+ memset(&parser, 0, sizeof(struct amdgpu_cs_parser)); -+ parser.filp = filp; -+ parser.adev = adev; -+ r = amdgpu_cs_parser_init(&parser, data); -+ if (r) { -+ DRM_ERROR("Failed to initialize parser !\n"); -+ amdgpu_cs_parser_fini(&parser, r, false); -+ up_read(&adev->exclusive_lock); -+ r = amdgpu_cs_handle_lockup(adev, r); -+ return r; -+ } -+ -+ r = amdgpu_cs_ib_fill(adev, &parser); -+ if (!r) { -+ r = amdgpu_cs_parser_relocs(&parser); -+ if (r && r != -ERESTARTSYS) -+ DRM_ERROR("Failed to parse relocation %d!\n", r); -+ } -+ -+ if (r) { -+ amdgpu_cs_parser_fini(&parser, r, false); -+ up_read(&adev->exclusive_lock); -+ r = amdgpu_cs_handle_lockup(adev, r); -+ return r; -+ } -+ -+ for (i = 0; i < parser.num_ibs; i++) -+ trace_amdgpu_cs(&parser, i); -+ -+ r = amdgpu_cs_ib_vm_chunk(adev, &parser); -+ if (r) { -+ goto out; -+ } -+ -+ cs->out.handle = parser.ibs[parser.num_ibs - 1].fence->seq; -+out: -+ amdgpu_cs_parser_fini(&parser, r, true); -+ up_read(&adev->exclusive_lock); -+ r = amdgpu_cs_handle_lockup(adev, r); -+ return r; -+} -+ -+/** -+ * amdgpu_cs_wait_ioctl - wait for a command submission to finish -+ * -+ * @dev: drm device -+ * @data: data from userspace -+ * @filp: file private -+ * -+ * Wait for the command submission identified by handle to finish. -+ */ -+int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ union drm_amdgpu_wait_cs *wait = data; -+ struct amdgpu_device *adev = dev->dev_private; -+ uint64_t seq[AMDGPU_MAX_RINGS] = {0}; -+ struct amdgpu_ring *ring = NULL; -+ unsigned long timeout = amdgpu_gem_timeout(wait->in.timeout); -+ long r; -+ -+ r = amdgpu_cs_get_ring(adev, wait->in.ip_type, wait->in.ip_instance, -+ wait->in.ring, &ring); -+ if (r) -+ return r; -+ -+ seq[ring->idx] = wait->in.handle; -+ -+ r = amdgpu_fence_wait_seq_timeout(adev, seq, true, timeout); -+ if (r < 0) -+ return r; -+ -+ memset(wait, 0, sizeof(*wait)); -+ wait->out.status = (r == 0); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_cs_find_bo_va - find bo_va for VM address -+ * -+ * @parser: command submission parser context -+ * @addr: VM address -+ * @bo: resulting BO of the mapping found -+ * -+ * Search the buffer objects in the command submission context for a certain -+ * virtual memory address. Returns allocation structure when found, NULL -+ * otherwise. -+ */ -+struct amdgpu_bo_va_mapping * -+amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser, -+ uint64_t addr, struct amdgpu_bo **bo) -+{ -+ struct amdgpu_bo_list_entry *reloc; -+ struct amdgpu_bo_va_mapping *mapping; -+ -+ addr /= AMDGPU_GPU_PAGE_SIZE; -+ -+ list_for_each_entry(reloc, &parser->validated, tv.head) { -+ if (!reloc->bo_va) -+ continue; -+ -+ list_for_each_entry(mapping, &reloc->bo_va->mappings, list) { -+ if (mapping->it.start > addr || -+ addr > mapping->it.last) -+ continue; -+ -+ *bo = reloc->bo_va->bo; -+ return mapping; -+ } -+ } -+ -+ return NULL; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c -new file mode 100644 -index 0000000..235010a ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c -@@ -0,0 +1,161 @@ -+/* -+ * Copyright 2015 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: monk liu <monk.liu@amd.com> -+ */ -+ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+ -+static void amdgpu_ctx_do_release(struct kref *ref) -+{ -+ struct amdgpu_ctx *ctx; -+ struct amdgpu_ctx_mgr *mgr; -+ -+ ctx = container_of(ref, struct amdgpu_ctx, refcount); -+ mgr = &ctx->fpriv->ctx_mgr; -+ -+ mutex_lock(&mgr->hlock); -+ idr_remove(&mgr->ctx_handles, ctx->id); -+ mutex_unlock(&mgr->hlock); -+ kfree(ctx); -+} -+ -+int amdgpu_ctx_alloc(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t *id, uint32_t flags) -+{ -+ int r; -+ struct amdgpu_ctx *ctx; -+ struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; -+ -+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; -+ -+ mutex_lock(&mgr->hlock); -+ r = idr_alloc(&mgr->ctx_handles, ctx, 0, 0, GFP_KERNEL); -+ if (r < 0) { -+ mutex_unlock(&mgr->hlock); -+ kfree(ctx); -+ return r; -+ } -+ mutex_unlock(&mgr->hlock); -+ *id = (uint32_t)r; -+ -+ memset(ctx, 0, sizeof(*ctx)); -+ ctx->id = *id; -+ ctx->fpriv = fpriv; -+ kref_init(&ctx->refcount); -+ -+ return 0; -+} -+ -+int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id) -+{ -+ int r; -+ struct amdgpu_ctx *ctx; -+ struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; -+ -+ rcu_read_lock(); -+ ctx = idr_find(&mgr->ctx_handles, id); -+ rcu_read_unlock(); -+ if (ctx) { -+ /* if no task is pending on this context, free it */ -+ r = kref_put(&ctx->refcount, amdgpu_ctx_do_release); -+ if (r == 1) -+ return 0;//context is removed successfully -+ else { -+ /* context is still in using */ -+ kref_get(&ctx->refcount); -+ return -ERESTARTSYS; -+ } -+ } -+ return -EINVAL; -+} -+ -+int amdgpu_ctx_query(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id, struct amdgpu_ctx_state *state) -+{ -+ struct amdgpu_ctx *ctx; -+ struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; -+ -+ rcu_read_lock(); -+ ctx = idr_find(&mgr->ctx_handles, id); -+ rcu_read_unlock(); -+ if (ctx) { -+ /* state should alter with CS activity */ -+ *state = ctx->state; -+ return 0; -+ } -+ return -EINVAL; -+} -+ -+void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv) -+{ -+ struct idr *idp; -+ struct amdgpu_ctx *ctx; -+ uint32_t id; -+ struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; -+ idp = &mgr->ctx_handles; -+ -+ idr_for_each_entry(idp,ctx,id) { -+ if (kref_put(&ctx->refcount, amdgpu_ctx_do_release) != 1) -+ DRM_ERROR("ctx (id=%ul) is still alive\n",ctx->id); -+ } -+ -+ mutex_destroy(&mgr->hlock); -+} -+ -+int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ int r; -+ uint32_t id; -+ uint32_t flags; -+ struct amdgpu_ctx_state state; -+ -+ union drm_amdgpu_ctx *args = data; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_fpriv *fpriv = filp->driver_priv; -+ -+ r = 0; -+ id = args->in.ctx_id; -+ flags = args->in.flags; -+ -+ switch (args->in.op) { -+ case AMDGPU_CTX_OP_ALLOC_CTX: -+ r = amdgpu_ctx_alloc(adev, fpriv, &id, flags); -+ args->out.alloc.ctx_id = id; -+ break; -+ case AMDGPU_CTX_OP_FREE_CTX: -+ r = amdgpu_ctx_free(adev, fpriv, id); -+ break; -+ case AMDGPU_CTX_OP_QUERY_STATE: -+ r = amdgpu_ctx_query(adev, fpriv, id, &state); -+ if (r == 0) { -+ args->out.state.flags = state.flags; -+ args->out.state.hangs = state.hangs; -+ } -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return r; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c -new file mode 100644 -index 0000000..cd4bb90 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c -@@ -0,0 +1,1971 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <linux/console.h> -+#include <linux/slab.h> -+#include <linux/debugfs.h> -+#include <drm/drmP.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/amdgpu_drm.h> -+#include <linux/vgaarb.h> -+#include <linux/vga_switcheroo.h> -+#include <linux/efi.h> -+#include "amdgpu.h" -+#include "amdgpu_i2c.h" -+#include "atom.h" -+#include "amdgpu_atombios.h" -+#include "bif/bif_4_1_d.h" -+ -+static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev); -+static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev); -+ -+static const char *amdgpu_asic_name[] = { -+ "BONAIRE", -+ "KAVERI", -+ "KABINI", -+ "HAWAII", -+ "MULLINS", -+ "TOPAZ", -+ "TONGA", -+ "CARRIZO", -+ "LAST", -+}; -+ -+bool amdgpu_device_is_px(struct drm_device *dev) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (adev->flags & AMDGPU_IS_PX) -+ return true; -+ return false; -+} -+ -+/* -+ * MMIO register access helper functions. -+ */ -+uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, -+ bool always_indirect) -+{ -+ if ((reg * 4) < adev->rmmio_size && !always_indirect) -+ return readl(((void __iomem *)adev->rmmio) + (reg * 4)); -+ else { -+ unsigned long flags; -+ uint32_t ret; -+ -+ spin_lock_irqsave(&adev->mmio_idx_lock, flags); -+ writel((reg * 4), ((void __iomem *)adev->rmmio) + (mmMM_INDEX * 4)); -+ ret = readl(((void __iomem *)adev->rmmio) + (mmMM_DATA * 4)); -+ spin_unlock_irqrestore(&adev->mmio_idx_lock, flags); -+ -+ return ret; -+ } -+} -+ -+void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, -+ bool always_indirect) -+{ -+ if ((reg * 4) < adev->rmmio_size && !always_indirect) -+ writel(v, ((void __iomem *)adev->rmmio) + (reg * 4)); -+ else { -+ unsigned long flags; -+ -+ spin_lock_irqsave(&adev->mmio_idx_lock, flags); -+ writel((reg * 4), ((void __iomem *)adev->rmmio) + (mmMM_INDEX * 4)); -+ writel(v, ((void __iomem *)adev->rmmio) + (mmMM_DATA * 4)); -+ spin_unlock_irqrestore(&adev->mmio_idx_lock, flags); -+ } -+} -+ -+u32 amdgpu_io_rreg(struct amdgpu_device *adev, u32 reg) -+{ -+ if ((reg * 4) < adev->rio_mem_size) -+ return ioread32(adev->rio_mem + (reg * 4)); -+ else { -+ iowrite32((reg * 4), adev->rio_mem + (mmMM_INDEX * 4)); -+ return ioread32(adev->rio_mem + (mmMM_DATA * 4)); -+ } -+} -+ -+void amdgpu_io_wreg(struct amdgpu_device *adev, u32 reg, u32 v) -+{ -+ -+ if ((reg * 4) < adev->rio_mem_size) -+ iowrite32(v, adev->rio_mem + (reg * 4)); -+ else { -+ iowrite32((reg * 4), adev->rio_mem + (mmMM_INDEX * 4)); -+ iowrite32(v, adev->rio_mem + (mmMM_DATA * 4)); -+ } -+} -+ -+/** -+ * amdgpu_mm_rdoorbell - read a doorbell dword -+ * -+ * @adev: amdgpu_device pointer -+ * @index: doorbell index -+ * -+ * Returns the value in the doorbell aperture at the -+ * requested doorbell index (CIK). -+ */ -+u32 amdgpu_mm_rdoorbell(struct amdgpu_device *adev, u32 index) -+{ -+ if (index < adev->doorbell.num_doorbells) { -+ return readl(adev->doorbell.ptr + index); -+ } else { -+ DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", index); -+ return 0; -+ } -+} -+ -+/** -+ * amdgpu_mm_wdoorbell - write a doorbell dword -+ * -+ * @adev: amdgpu_device pointer -+ * @index: doorbell index -+ * @v: value to write -+ * -+ * Writes @v to the doorbell aperture at the -+ * requested doorbell index (CIK). -+ */ -+void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v) -+{ -+ if (index < adev->doorbell.num_doorbells) { -+ writel(v, adev->doorbell.ptr + index); -+ } else { -+ DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", index); -+ } -+} -+ -+/** -+ * amdgpu_invalid_rreg - dummy reg read function -+ * -+ * @adev: amdgpu device pointer -+ * @reg: offset of register -+ * -+ * Dummy register read function. Used for register blocks -+ * that certain asics don't have (all asics). -+ * Returns the value in the register. -+ */ -+static uint32_t amdgpu_invalid_rreg(struct amdgpu_device *adev, uint32_t reg) -+{ -+ DRM_ERROR("Invalid callback to read register 0x%04X\n", reg); -+ BUG(); -+ return 0; -+} -+ -+/** -+ * amdgpu_invalid_wreg - dummy reg write function -+ * -+ * @adev: amdgpu device pointer -+ * @reg: offset of register -+ * @v: value to write to the register -+ * -+ * Dummy register read function. Used for register blocks -+ * that certain asics don't have (all asics). -+ */ -+static void amdgpu_invalid_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v) -+{ -+ DRM_ERROR("Invalid callback to write register 0x%04X with 0x%08X\n", -+ reg, v); -+ BUG(); -+} -+ -+/** -+ * amdgpu_block_invalid_rreg - dummy reg read function -+ * -+ * @adev: amdgpu device pointer -+ * @block: offset of instance -+ * @reg: offset of register -+ * -+ * Dummy register read function. Used for register blocks -+ * that certain asics don't have (all asics). -+ * Returns the value in the register. -+ */ -+static uint32_t amdgpu_block_invalid_rreg(struct amdgpu_device *adev, -+ uint32_t block, uint32_t reg) -+{ -+ DRM_ERROR("Invalid callback to read register 0x%04X in block 0x%04X\n", -+ reg, block); -+ BUG(); -+ return 0; -+} -+ -+/** -+ * amdgpu_block_invalid_wreg - dummy reg write function -+ * -+ * @adev: amdgpu device pointer -+ * @block: offset of instance -+ * @reg: offset of register -+ * @v: value to write to the register -+ * -+ * Dummy register read function. Used for register blocks -+ * that certain asics don't have (all asics). -+ */ -+static void amdgpu_block_invalid_wreg(struct amdgpu_device *adev, -+ uint32_t block, -+ uint32_t reg, uint32_t v) -+{ -+ DRM_ERROR("Invalid block callback to write register 0x%04X in block 0x%04X with 0x%08X\n", -+ reg, block, v); -+ BUG(); -+} -+ -+static int amdgpu_vram_scratch_init(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->vram_scratch.robj == NULL) { -+ r = amdgpu_bo_create(adev, AMDGPU_GPU_PAGE_SIZE, -+ PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, 0, -+ NULL, &adev->vram_scratch.robj); -+ if (r) { -+ return r; -+ } -+ } -+ -+ r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); -+ if (unlikely(r != 0)) -+ return r; -+ r = amdgpu_bo_pin(adev->vram_scratch.robj, -+ AMDGPU_GEM_DOMAIN_VRAM, &adev->vram_scratch.gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(adev->vram_scratch.robj); -+ return r; -+ } -+ r = amdgpu_bo_kmap(adev->vram_scratch.robj, -+ (void **)&adev->vram_scratch.ptr); -+ if (r) -+ amdgpu_bo_unpin(adev->vram_scratch.robj); -+ amdgpu_bo_unreserve(adev->vram_scratch.robj); -+ -+ return r; -+} -+ -+static void amdgpu_vram_scratch_fini(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->vram_scratch.robj == NULL) { -+ return; -+ } -+ r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); -+ if (likely(r == 0)) { -+ amdgpu_bo_kunmap(adev->vram_scratch.robj); -+ amdgpu_bo_unpin(adev->vram_scratch.robj); -+ amdgpu_bo_unreserve(adev->vram_scratch.robj); -+ } -+ amdgpu_bo_unref(&adev->vram_scratch.robj); -+} -+ -+/** -+ * amdgpu_program_register_sequence - program an array of registers. -+ * -+ * @adev: amdgpu_device pointer -+ * @registers: pointer to the register array -+ * @array_size: size of the register array -+ * -+ * Programs an array or registers with and and or masks. -+ * This is a helper for setting golden registers. -+ */ -+void amdgpu_program_register_sequence(struct amdgpu_device *adev, -+ const u32 *registers, -+ const u32 array_size) -+{ -+ u32 tmp, reg, and_mask, or_mask; -+ int i; -+ -+ if (array_size % 3) -+ return; -+ -+ for (i = 0; i < array_size; i +=3) { -+ reg = registers[i + 0]; -+ and_mask = registers[i + 1]; -+ or_mask = registers[i + 2]; -+ -+ if (and_mask == 0xffffffff) { -+ tmp = or_mask; -+ } else { -+ tmp = RREG32(reg); -+ tmp &= ~and_mask; -+ tmp |= or_mask; -+ } -+ WREG32(reg, tmp); -+ } -+} -+ -+void amdgpu_pci_config_reset(struct amdgpu_device *adev) -+{ -+ pci_write_config_dword(adev->pdev, 0x7c, AMDGPU_ASIC_RESET_DATA); -+} -+ -+/* -+ * GPU doorbell aperture helpers function. -+ */ -+/** -+ * amdgpu_doorbell_init - Init doorbell driver information. -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Init doorbell driver information (CIK) -+ * Returns 0 on success, error on failure. -+ */ -+static int amdgpu_doorbell_init(struct amdgpu_device *adev) -+{ -+ /* doorbell bar mapping */ -+ adev->doorbell.base = pci_resource_start(adev->pdev, 2); -+ adev->doorbell.size = pci_resource_len(adev->pdev, 2); -+ -+ adev->doorbell.num_doorbells = min_t(u32, adev->doorbell.size / sizeof(u32), -+ AMDGPU_DOORBELL_MAX_ASSIGNMENT+1); -+ if (adev->doorbell.num_doorbells == 0) -+ return -EINVAL; -+ -+ adev->doorbell.ptr = ioremap(adev->doorbell.base, adev->doorbell.num_doorbells * sizeof(u32)); -+ if (adev->doorbell.ptr == NULL) { -+ return -ENOMEM; -+ } -+ DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)adev->doorbell.base); -+ DRM_INFO("doorbell mmio size: %u\n", (unsigned)adev->doorbell.size); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_doorbell_fini - Tear down doorbell driver information. -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Tear down doorbell driver information (CIK) -+ */ -+static void amdgpu_doorbell_fini(struct amdgpu_device *adev) -+{ -+ iounmap(adev->doorbell.ptr); -+ adev->doorbell.ptr = NULL; -+} -+ -+/** -+ * amdgpu_doorbell_get_kfd_info - Report doorbell configuration required to -+ * setup amdkfd -+ * -+ * @adev: amdgpu_device pointer -+ * @aperture_base: output returning doorbell aperture base physical address -+ * @aperture_size: output returning doorbell aperture size in bytes -+ * @start_offset: output returning # of doorbell bytes reserved for amdgpu. -+ * -+ * amdgpu and amdkfd share the doorbell aperture. amdgpu sets it up, -+ * takes doorbells required for its own rings and reports the setup to amdkfd. -+ * amdgpu reserved doorbells are at the start of the doorbell aperture. -+ */ -+void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev, -+ phys_addr_t *aperture_base, -+ size_t *aperture_size, -+ size_t *start_offset) -+{ -+ /* -+ * The first num_doorbells are used by amdgpu. -+ * amdkfd takes whatever's left in the aperture. -+ */ -+ if (adev->doorbell.size > adev->doorbell.num_doorbells * sizeof(u32)) { -+ *aperture_base = adev->doorbell.base; -+ *aperture_size = adev->doorbell.size; -+ *start_offset = adev->doorbell.num_doorbells * sizeof(u32); -+ } else { -+ *aperture_base = 0; -+ *aperture_size = 0; -+ *start_offset = 0; -+ } -+} -+ -+/* -+ * amdgpu_wb_*() -+ * Writeback is the the method by which the the GPU updates special pages -+ * in memory with the status of certain GPU events (fences, ring pointers, -+ * etc.). -+ */ -+ -+/** -+ * amdgpu_wb_fini - Disable Writeback and free memory -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Disables Writeback and frees the Writeback memory (all asics). -+ * Used at driver shutdown. -+ */ -+static void amdgpu_wb_fini(struct amdgpu_device *adev) -+{ -+ if (adev->wb.wb_obj) { -+ if (!amdgpu_bo_reserve(adev->wb.wb_obj, false)) { -+ amdgpu_bo_kunmap(adev->wb.wb_obj); -+ amdgpu_bo_unpin(adev->wb.wb_obj); -+ amdgpu_bo_unreserve(adev->wb.wb_obj); -+ } -+ amdgpu_bo_unref(&adev->wb.wb_obj); -+ adev->wb.wb = NULL; -+ adev->wb.wb_obj = NULL; -+ } -+} -+ -+/** -+ * amdgpu_wb_init- Init Writeback driver info and allocate memory -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Disables Writeback and frees the Writeback memory (all asics). -+ * Used at driver startup. -+ * Returns 0 on success or an -error on failure. -+ */ -+static int amdgpu_wb_init(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->wb.wb_obj == NULL) { -+ r = amdgpu_bo_create(adev, AMDGPU_MAX_WB * 4, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_GTT, 0, NULL, &adev->wb.wb_obj); -+ if (r) { -+ dev_warn(adev->dev, "(%d) create WB bo failed\n", r); -+ return r; -+ } -+ r = amdgpu_bo_reserve(adev->wb.wb_obj, false); -+ if (unlikely(r != 0)) { -+ amdgpu_wb_fini(adev); -+ return r; -+ } -+ r = amdgpu_bo_pin(adev->wb.wb_obj, AMDGPU_GEM_DOMAIN_GTT, -+ &adev->wb.gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(adev->wb.wb_obj); -+ dev_warn(adev->dev, "(%d) pin WB bo failed\n", r); -+ amdgpu_wb_fini(adev); -+ return r; -+ } -+ r = amdgpu_bo_kmap(adev->wb.wb_obj, (void **)&adev->wb.wb); -+ amdgpu_bo_unreserve(adev->wb.wb_obj); -+ if (r) { -+ dev_warn(adev->dev, "(%d) map WB bo failed\n", r); -+ amdgpu_wb_fini(adev); -+ return r; -+ } -+ -+ adev->wb.num_wb = AMDGPU_MAX_WB; -+ memset(&adev->wb.used, 0, sizeof(adev->wb.used)); -+ -+ /* clear wb memory */ -+ memset((char *)adev->wb.wb, 0, AMDGPU_GPU_PAGE_SIZE); -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_wb_get - Allocate a wb entry -+ * -+ * @adev: amdgpu_device pointer -+ * @wb: wb index -+ * -+ * Allocate a wb slot for use by the driver (all asics). -+ * Returns 0 on success or -EINVAL on failure. -+ */ -+int amdgpu_wb_get(struct amdgpu_device *adev, u32 *wb) -+{ -+ unsigned long offset = find_first_zero_bit(adev->wb.used, adev->wb.num_wb); -+ if (offset < adev->wb.num_wb) { -+ __set_bit(offset, adev->wb.used); -+ *wb = offset; -+ return 0; -+ } else { -+ return -EINVAL; -+ } -+} -+ -+/** -+ * amdgpu_wb_free - Free a wb entry -+ * -+ * @adev: amdgpu_device pointer -+ * @wb: wb index -+ * -+ * Free a wb slot allocated for use by the driver (all asics) -+ */ -+void amdgpu_wb_free(struct amdgpu_device *adev, u32 wb) -+{ -+ if (wb < adev->wb.num_wb) -+ __clear_bit(wb, adev->wb.used); -+} -+ -+/** -+ * amdgpu_vram_location - try to find VRAM location -+ * @adev: amdgpu device structure holding all necessary informations -+ * @mc: memory controller structure holding memory informations -+ * @base: base address at which to put VRAM -+ * -+ * Function will place try to place VRAM at base address provided -+ * as parameter (which is so far either PCI aperture address or -+ * for IGP TOM base address). -+ * -+ * If there is not enough space to fit the unvisible VRAM in the 32bits -+ * address space then we limit the VRAM size to the aperture. -+ * -+ * Note: We don't explicitly enforce VRAM start to be aligned on VRAM size, -+ * this shouldn't be a problem as we are using the PCI aperture as a reference. -+ * Otherwise this would be needed for rv280, all r3xx, and all r4xx, but -+ * not IGP. -+ * -+ * Note: we use mc_vram_size as on some board we need to program the mc to -+ * cover the whole aperture even if VRAM size is inferior to aperture size -+ * Novell bug 204882 + along with lots of ubuntu ones -+ * -+ * Note: when limiting vram it's safe to overwritte real_vram_size because -+ * we are not in case where real_vram_size is inferior to mc_vram_size (ie -+ * note afected by bogus hw of Novell bug 204882 + along with lots of ubuntu -+ * ones) -+ * -+ * Note: IGP TOM addr should be the same as the aperture addr, we don't -+ * explicitly check for that thought. -+ * -+ * FIXME: when reducing VRAM size align new size on power of 2. -+ */ -+void amdgpu_vram_location(struct amdgpu_device *adev, struct amdgpu_mc *mc, u64 base) -+{ -+ uint64_t limit = (uint64_t)amdgpu_vram_limit << 20; -+ -+ mc->vram_start = base; -+ if (mc->mc_vram_size > (adev->mc.mc_mask - base + 1)) { -+ dev_warn(adev->dev, "limiting VRAM to PCI aperture size\n"); -+ mc->real_vram_size = mc->aper_size; -+ mc->mc_vram_size = mc->aper_size; -+ } -+ mc->vram_end = mc->vram_start + mc->mc_vram_size - 1; -+ if (limit && limit < mc->real_vram_size) -+ mc->real_vram_size = limit; -+ dev_info(adev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n", -+ mc->mc_vram_size >> 20, mc->vram_start, -+ mc->vram_end, mc->real_vram_size >> 20); -+} -+ -+/** -+ * amdgpu_gtt_location - try to find GTT location -+ * @adev: amdgpu device structure holding all necessary informations -+ * @mc: memory controller structure holding memory informations -+ * -+ * Function will place try to place GTT before or after VRAM. -+ * -+ * If GTT size is bigger than space left then we ajust GTT size. -+ * Thus function will never fails. -+ * -+ * FIXME: when reducing GTT size align new size on power of 2. -+ */ -+void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc) -+{ -+ u64 size_af, size_bf; -+ -+ size_af = ((adev->mc.mc_mask - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align; -+ size_bf = mc->vram_start & ~mc->gtt_base_align; -+ if (size_bf > size_af) { -+ if (mc->gtt_size > size_bf) { -+ dev_warn(adev->dev, "limiting GTT\n"); -+ mc->gtt_size = size_bf; -+ } -+ mc->gtt_start = (mc->vram_start & ~mc->gtt_base_align) - mc->gtt_size; -+ } else { -+ if (mc->gtt_size > size_af) { -+ dev_warn(adev->dev, "limiting GTT\n"); -+ mc->gtt_size = size_af; -+ } -+ mc->gtt_start = (mc->vram_end + 1 + mc->gtt_base_align) & ~mc->gtt_base_align; -+ } -+ mc->gtt_end = mc->gtt_start + mc->gtt_size - 1; -+ dev_info(adev->dev, "GTT: %lluM 0x%016llX - 0x%016llX\n", -+ mc->gtt_size >> 20, mc->gtt_start, mc->gtt_end); -+} -+ -+/* -+ * GPU helpers function. -+ */ -+/** -+ * amdgpu_card_posted - check if the hw has already been initialized -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Check if the asic has been initialized (all asics). -+ * Used at driver startup. -+ * Returns true if initialized or false if not. -+ */ -+bool amdgpu_card_posted(struct amdgpu_device *adev) -+{ -+ uint32_t reg; -+ -+ /* then check MEM_SIZE, in case the crtcs are off */ -+ reg = RREG32(mmCONFIG_MEMSIZE); -+ -+ if (reg) -+ return true; -+ -+ return false; -+ -+} -+ -+/** -+ * amdgpu_boot_test_post_card - check and possibly initialize the hw -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Check if the asic is initialized and if not, attempt to initialize -+ * it (all asics). -+ * Returns true if initialized or false if not. -+ */ -+bool amdgpu_boot_test_post_card(struct amdgpu_device *adev) -+{ -+ if (amdgpu_card_posted(adev)) -+ return true; -+ -+ if (adev->bios) { -+ DRM_INFO("GPU not posted. posting now...\n"); -+ if (adev->is_atom_bios) -+ amdgpu_atom_asic_init(adev->mode_info.atom_context); -+ return true; -+ } else { -+ dev_err(adev->dev, "Card not posted and no BIOS - ignoring\n"); -+ return false; -+ } -+} -+ -+/** -+ * amdgpu_dummy_page_init - init dummy page used by the driver -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Allocate the dummy page used by the driver (all asics). -+ * This dummy page is used by the driver as a filler for gart entries -+ * when pages are taken out of the GART -+ * Returns 0 on sucess, -ENOMEM on failure. -+ */ -+int amdgpu_dummy_page_init(struct amdgpu_device *adev) -+{ -+ if (adev->dummy_page.page) -+ return 0; -+ adev->dummy_page.page = alloc_page(GFP_DMA32 | GFP_KERNEL | __GFP_ZERO); -+ if (adev->dummy_page.page == NULL) -+ return -ENOMEM; -+ adev->dummy_page.addr = pci_map_page(adev->pdev, adev->dummy_page.page, -+ 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -+ if (pci_dma_mapping_error(adev->pdev, adev->dummy_page.addr)) { -+ dev_err(&adev->pdev->dev, "Failed to DMA MAP the dummy page\n"); -+ __free_page(adev->dummy_page.page); -+ adev->dummy_page.page = NULL; -+ return -ENOMEM; -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_dummy_page_fini - free dummy page used by the driver -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Frees the dummy page used by the driver (all asics). -+ */ -+void amdgpu_dummy_page_fini(struct amdgpu_device *adev) -+{ -+ if (adev->dummy_page.page == NULL) -+ return; -+ pci_unmap_page(adev->pdev, adev->dummy_page.addr, -+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -+ __free_page(adev->dummy_page.page); -+ adev->dummy_page.page = NULL; -+} -+ -+ -+/* ATOM accessor methods */ -+/* -+ * ATOM is an interpreted byte code stored in tables in the vbios. The -+ * driver registers callbacks to access registers and the interpreter -+ * in the driver parses the tables and executes then to program specific -+ * actions (set display modes, asic init, etc.). See amdgpu_atombios.c, -+ * atombios.h, and atom.c -+ */ -+ -+/** -+ * cail_pll_read - read PLL register -+ * -+ * @info: atom card_info pointer -+ * @reg: PLL register offset -+ * -+ * Provides a PLL register accessor for the atom interpreter (r4xx+). -+ * Returns the value of the PLL register. -+ */ -+static uint32_t cail_pll_read(struct card_info *info, uint32_t reg) -+{ -+ return 0; -+} -+ -+/** -+ * cail_pll_write - write PLL register -+ * -+ * @info: atom card_info pointer -+ * @reg: PLL register offset -+ * @val: value to write to the pll register -+ * -+ * Provides a PLL register accessor for the atom interpreter (r4xx+). -+ */ -+static void cail_pll_write(struct card_info *info, uint32_t reg, uint32_t val) -+{ -+ -+} -+ -+/** -+ * cail_mc_read - read MC (Memory Controller) register -+ * -+ * @info: atom card_info pointer -+ * @reg: MC register offset -+ * -+ * Provides an MC register accessor for the atom interpreter (r4xx+). -+ * Returns the value of the MC register. -+ */ -+static uint32_t cail_mc_read(struct card_info *info, uint32_t reg) -+{ -+ return 0; -+} -+ -+/** -+ * cail_mc_write - write MC (Memory Controller) register -+ * -+ * @info: atom card_info pointer -+ * @reg: MC register offset -+ * @val: value to write to the pll register -+ * -+ * Provides a MC register accessor for the atom interpreter (r4xx+). -+ */ -+static void cail_mc_write(struct card_info *info, uint32_t reg, uint32_t val) -+{ -+ -+} -+ -+/** -+ * cail_reg_write - write MMIO register -+ * -+ * @info: atom card_info pointer -+ * @reg: MMIO register offset -+ * @val: value to write to the pll register -+ * -+ * Provides a MMIO register accessor for the atom interpreter (r4xx+). -+ */ -+static void cail_reg_write(struct card_info *info, uint32_t reg, uint32_t val) -+{ -+ struct amdgpu_device *adev = info->dev->dev_private; -+ -+ WREG32(reg, val); -+} -+ -+/** -+ * cail_reg_read - read MMIO register -+ * -+ * @info: atom card_info pointer -+ * @reg: MMIO register offset -+ * -+ * Provides an MMIO register accessor for the atom interpreter (r4xx+). -+ * Returns the value of the MMIO register. -+ */ -+static uint32_t cail_reg_read(struct card_info *info, uint32_t reg) -+{ -+ struct amdgpu_device *adev = info->dev->dev_private; -+ uint32_t r; -+ -+ r = RREG32(reg); -+ return r; -+} -+ -+/** -+ * cail_ioreg_write - write IO register -+ * -+ * @info: atom card_info pointer -+ * @reg: IO register offset -+ * @val: value to write to the pll register -+ * -+ * Provides a IO register accessor for the atom interpreter (r4xx+). -+ */ -+static void cail_ioreg_write(struct card_info *info, uint32_t reg, uint32_t val) -+{ -+ struct amdgpu_device *adev = info->dev->dev_private; -+ -+ WREG32_IO(reg, val); -+} -+ -+/** -+ * cail_ioreg_read - read IO register -+ * -+ * @info: atom card_info pointer -+ * @reg: IO register offset -+ * -+ * Provides an IO register accessor for the atom interpreter (r4xx+). -+ * Returns the value of the IO register. -+ */ -+static uint32_t cail_ioreg_read(struct card_info *info, uint32_t reg) -+{ -+ struct amdgpu_device *adev = info->dev->dev_private; -+ uint32_t r; -+ -+ r = RREG32_IO(reg); -+ return r; -+} -+ -+/** -+ * amdgpu_atombios_fini - free the driver info and callbacks for atombios -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Frees the driver info and register access callbacks for the ATOM -+ * interpreter (r4xx+). -+ * Called at driver shutdown. -+ */ -+static void amdgpu_atombios_fini(struct amdgpu_device *adev) -+{ -+ if (adev->mode_info.atom_context) -+ kfree(adev->mode_info.atom_context->scratch); -+ kfree(adev->mode_info.atom_context); -+ adev->mode_info.atom_context = NULL; -+ kfree(adev->mode_info.atom_card_info); -+ adev->mode_info.atom_card_info = NULL; -+} -+ -+/** -+ * amdgpu_atombios_init - init the driver info and callbacks for atombios -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Initializes the driver info and register access callbacks for the -+ * ATOM interpreter (r4xx+). -+ * Returns 0 on sucess, -ENOMEM on failure. -+ * Called at driver startup. -+ */ -+static int amdgpu_atombios_init(struct amdgpu_device *adev) -+{ -+ struct card_info *atom_card_info = -+ kzalloc(sizeof(struct card_info), GFP_KERNEL); -+ -+ if (!atom_card_info) -+ return -ENOMEM; -+ -+ adev->mode_info.atom_card_info = atom_card_info; -+ atom_card_info->dev = adev->ddev; -+ atom_card_info->reg_read = cail_reg_read; -+ atom_card_info->reg_write = cail_reg_write; -+ /* needed for iio ops */ -+ if (adev->rio_mem) { -+ atom_card_info->ioreg_read = cail_ioreg_read; -+ atom_card_info->ioreg_write = cail_ioreg_write; -+ } else { -+ DRM_ERROR("Unable to find PCI I/O BAR; using MMIO for ATOM IIO\n"); -+ atom_card_info->ioreg_read = cail_reg_read; -+ atom_card_info->ioreg_write = cail_reg_write; -+ } -+ atom_card_info->mc_read = cail_mc_read; -+ atom_card_info->mc_write = cail_mc_write; -+ atom_card_info->pll_read = cail_pll_read; -+ atom_card_info->pll_write = cail_pll_write; -+ -+ adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios); -+ if (!adev->mode_info.atom_context) { -+ amdgpu_atombios_fini(adev); -+ return -ENOMEM; -+ } -+ -+ mutex_init(&adev->mode_info.atom_context->mutex); -+ amdgpu_atombios_scratch_regs_init(adev); -+ amdgpu_atom_allocate_fb_scratch(adev->mode_info.atom_context); -+ return 0; -+} -+ -+/* if we get transitioned to only one device, take VGA back */ -+/** -+ * amdgpu_vga_set_decode - enable/disable vga decode -+ * -+ * @cookie: amdgpu_device pointer -+ * @state: enable/disable vga decode -+ * -+ * Enable/disable vga decode (all asics). -+ * Returns VGA resource flags. -+ */ -+static unsigned int amdgpu_vga_set_decode(void *cookie, bool state) -+{ -+ struct amdgpu_device *adev = cookie; -+ amdgpu_asic_set_vga_state(adev, state); -+ if (state) -+ return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | -+ VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; -+ else -+ return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; -+} -+ -+/** -+ * amdgpu_check_pot_argument - check that argument is a power of two -+ * -+ * @arg: value to check -+ * -+ * Validates that a certain argument is a power of two (all asics). -+ * Returns true if argument is valid. -+ */ -+static bool amdgpu_check_pot_argument(int arg) -+{ -+ return (arg & (arg - 1)) == 0; -+} -+ -+/** -+ * amdgpu_check_arguments - validate module params -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Validates certain module parameters and updates -+ * the associated values used by the driver (all asics). -+ */ -+static void amdgpu_check_arguments(struct amdgpu_device *adev) -+{ -+ /* vramlimit must be a power of two */ -+ if (!amdgpu_check_pot_argument(amdgpu_vram_limit)) { -+ dev_warn(adev->dev, "vram limit (%d) must be a power of 2\n", -+ amdgpu_vram_limit); -+ amdgpu_vram_limit = 0; -+ } -+ -+ if (amdgpu_gart_size != -1) { -+ /* gtt size must be power of two and greater or equal to 32M */ -+ if (amdgpu_gart_size < 32) { -+ dev_warn(adev->dev, "gart size (%d) too small\n", -+ amdgpu_gart_size); -+ amdgpu_gart_size = -1; -+ } else if (!amdgpu_check_pot_argument(amdgpu_gart_size)) { -+ dev_warn(adev->dev, "gart size (%d) must be a power of 2\n", -+ amdgpu_gart_size); -+ amdgpu_gart_size = -1; -+ } -+ } -+ -+ if (!amdgpu_check_pot_argument(amdgpu_vm_size)) { -+ dev_warn(adev->dev, "VM size (%d) must be a power of 2\n", -+ amdgpu_vm_size); -+ amdgpu_vm_size = 4; -+ } -+ -+ if (amdgpu_vm_size < 1) { -+ dev_warn(adev->dev, "VM size (%d) too small, min is 1GB\n", -+ amdgpu_vm_size); -+ amdgpu_vm_size = 4; -+ } -+ -+ /* -+ * Max GPUVM size for Cayman, SI and CI are 40 bits. -+ */ -+ if (amdgpu_vm_size > 1024) { -+ dev_warn(adev->dev, "VM size (%d) too large, max is 1TB\n", -+ amdgpu_vm_size); -+ amdgpu_vm_size = 4; -+ } -+ -+ /* defines number of bits in page table versus page directory, -+ * a page is 4KB so we have 12 bits offset, minimum 9 bits in the -+ * page table and the remaining bits are in the page directory */ -+ if (amdgpu_vm_block_size == -1) { -+ -+ /* Total bits covered by PD + PTs */ -+ unsigned bits = ilog2(amdgpu_vm_size) + 18; -+ -+ /* Make sure the PD is 4K in size up to 8GB address space. -+ Above that split equal between PD and PTs */ -+ if (amdgpu_vm_size <= 8) -+ amdgpu_vm_block_size = bits - 9; -+ else -+ amdgpu_vm_block_size = (bits + 3) / 2; -+ -+ } else if (amdgpu_vm_block_size < 9) { -+ dev_warn(adev->dev, "VM page table size (%d) too small\n", -+ amdgpu_vm_block_size); -+ amdgpu_vm_block_size = 9; -+ } -+ -+ if (amdgpu_vm_block_size > 24 || -+ (amdgpu_vm_size * 1024) < (1ull << amdgpu_vm_block_size)) { -+ dev_warn(adev->dev, "VM page table size (%d) too large\n", -+ amdgpu_vm_block_size); -+ amdgpu_vm_block_size = 9; -+ } -+} -+ -+/** -+ * amdgpu_switcheroo_set_state - set switcheroo state -+ * -+ * @pdev: pci dev pointer -+ * @state: vga switcheroo state -+ * -+ * Callback for the switcheroo driver. Suspends or resumes the -+ * the asics before or after it is powered up using ACPI methods. -+ */ -+static void amdgpu_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) -+{ -+ struct drm_device *dev = pci_get_drvdata(pdev); -+ -+ if (amdgpu_device_is_px(dev) && state == VGA_SWITCHEROO_OFF) -+ return; -+ -+ if (state == VGA_SWITCHEROO_ON) { -+ unsigned d3_delay = dev->pdev->d3_delay; -+ -+ printk(KERN_INFO "amdgpu: switched on\n"); -+ /* don't suspend or resume card normally */ -+ dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; -+ -+ amdgpu_resume_kms(dev, true, true); -+ -+ dev->pdev->d3_delay = d3_delay; -+ -+ dev->switch_power_state = DRM_SWITCH_POWER_ON; -+ drm_kms_helper_poll_enable(dev); -+ } else { -+ printk(KERN_INFO "amdgpu: switched off\n"); -+ drm_kms_helper_poll_disable(dev); -+ dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; -+ amdgpu_suspend_kms(dev, true, true); -+ dev->switch_power_state = DRM_SWITCH_POWER_OFF; -+ } -+} -+ -+/** -+ * amdgpu_switcheroo_can_switch - see if switcheroo state can change -+ * -+ * @pdev: pci dev pointer -+ * -+ * Callback for the switcheroo driver. Check of the switcheroo -+ * state can be changed. -+ * Returns true if the state can be changed, false if not. -+ */ -+static bool amdgpu_switcheroo_can_switch(struct pci_dev *pdev) -+{ -+ struct drm_device *dev = pci_get_drvdata(pdev); -+ -+ /* -+ * FIXME: open_count is protected by drm_global_mutex but that would lead to -+ * locking inversion with the driver load path. And the access here is -+ * completely racy anyway. So don't bother with locking for now. -+ */ -+ return dev->open_count == 0; -+} -+ -+static const struct vga_switcheroo_client_ops amdgpu_switcheroo_ops = { -+ .set_gpu_state = amdgpu_switcheroo_set_state, -+ .reprobe = NULL, -+ .can_switch = amdgpu_switcheroo_can_switch, -+}; -+ -+int amdgpu_set_clockgating_state(struct amdgpu_device *adev, -+ enum amdgpu_ip_block_type block_type, -+ enum amdgpu_clockgating_state state) -+{ -+ int i, r = 0; -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (adev->ip_blocks[i].type == block_type) { -+ r = adev->ip_blocks[i].funcs->set_clockgating_state(adev, -+ state); -+ if (r) -+ return r; -+ } -+ } -+ return r; -+} -+ -+int amdgpu_set_powergating_state(struct amdgpu_device *adev, -+ enum amdgpu_ip_block_type block_type, -+ enum amdgpu_powergating_state state) -+{ -+ int i, r = 0; -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (adev->ip_blocks[i].type == block_type) { -+ r = adev->ip_blocks[i].funcs->set_powergating_state(adev, -+ state); -+ if (r) -+ return r; -+ } -+ } -+ return r; -+} -+ -+const struct amdgpu_ip_block_version * amdgpu_get_ip_block( -+ struct amdgpu_device *adev, -+ enum amdgpu_ip_block_type type) -+{ -+ int i; -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) -+ if (adev->ip_blocks[i].type == type) -+ return &adev->ip_blocks[i]; -+ -+ return NULL; -+} -+ -+/** -+ * amdgpu_ip_block_version_cmp -+ * -+ * @adev: amdgpu_device pointer -+ * @type: enum amdgpu_ip_block_type -+ * @major: major version -+ * @minor: minor version -+ * -+ * return 0 if equal or greater -+ * return 1 if smaller or the ip_block doesn't exist -+ */ -+int amdgpu_ip_block_version_cmp(struct amdgpu_device *adev, -+ enum amdgpu_ip_block_type type, -+ u32 major, u32 minor) -+{ -+ const struct amdgpu_ip_block_version *ip_block; -+ ip_block = amdgpu_get_ip_block(adev, type); -+ -+ if (ip_block && ((ip_block->major > major) || -+ ((ip_block->major == major) && -+ (ip_block->minor >= minor)))) -+ return 0; -+ -+ return 1; -+} -+ -+static int amdgpu_early_init(struct amdgpu_device *adev) -+{ -+ int i, r = -EINVAL; -+ -+ switch (adev->asic_type) { -+ default: -+ /* FIXME: not supported yet */ -+ return -EINVAL; -+ } -+ -+ -+ -+ if (adev->ip_blocks == NULL) { -+ DRM_ERROR("No IP blocks found!\n"); -+ return r; -+ } -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if ((amdgpu_ip_block_mask & (1 << i)) == 0) { -+ DRM_ERROR("disabled ip block: %d\n", i); -+ adev->ip_block_enabled[i] = false; -+ } else { -+ if (adev->ip_blocks[i].funcs->early_init) { -+ r = adev->ip_blocks[i].funcs->early_init(adev); -+ if (r) -+ return r; -+ } -+ adev->ip_block_enabled[i] = true; -+ } -+ } -+ -+ return 0; -+} -+ -+static int amdgpu_init(struct amdgpu_device *adev) -+{ -+ int i, r; -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ r = adev->ip_blocks[i].funcs->sw_init(adev); -+ if (r) -+ return r; -+ /* need to do gmc hw init early so we can allocate gpu mem */ -+ if (adev->ip_blocks[i].type == AMDGPU_IP_BLOCK_TYPE_GMC) { -+ r = amdgpu_vram_scratch_init(adev); -+ if (r) -+ return r; -+ r = adev->ip_blocks[i].funcs->hw_init(adev); -+ if (r) -+ return r; -+ r = amdgpu_wb_init(adev); -+ if (r) -+ return r; -+ } -+ } -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ /* gmc hw init is done early */ -+ if (adev->ip_blocks[i].type == AMDGPU_IP_BLOCK_TYPE_GMC) -+ continue; -+ r = adev->ip_blocks[i].funcs->hw_init(adev); -+ if (r) -+ return r; -+ } -+ -+ return 0; -+} -+ -+static int amdgpu_late_init(struct amdgpu_device *adev) -+{ -+ int i = 0, r; -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ /* enable clockgating to save power */ -+ r = adev->ip_blocks[i].funcs->set_clockgating_state(adev, -+ AMDGPU_CG_STATE_GATE); -+ if (r) -+ return r; -+ if (adev->ip_blocks[i].funcs->late_init) { -+ r = adev->ip_blocks[i].funcs->late_init(adev); -+ if (r) -+ return r; -+ } -+ } -+ -+ return 0; -+} -+ -+static int amdgpu_fini(struct amdgpu_device *adev) -+{ -+ int i, r; -+ -+ for (i = adev->num_ip_blocks - 1; i >= 0; i--) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ if (adev->ip_blocks[i].type == AMDGPU_IP_BLOCK_TYPE_GMC) { -+ amdgpu_wb_fini(adev); -+ amdgpu_vram_scratch_fini(adev); -+ } -+ /* ungate blocks before hw fini so that we can shutdown the blocks safely */ -+ r = adev->ip_blocks[i].funcs->set_clockgating_state(adev, -+ AMDGPU_CG_STATE_UNGATE); -+ if (r) -+ return r; -+ r = adev->ip_blocks[i].funcs->hw_fini(adev); -+ /* XXX handle errors */ -+ } -+ -+ for (i = adev->num_ip_blocks - 1; i >= 0; i--) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ r = adev->ip_blocks[i].funcs->sw_fini(adev); -+ /* XXX handle errors */ -+ adev->ip_block_enabled[i] = false; -+ } -+ -+ return 0; -+} -+ -+static int amdgpu_suspend(struct amdgpu_device *adev) -+{ -+ int i, r; -+ -+ for (i = adev->num_ip_blocks - 1; i >= 0; i--) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ /* ungate blocks so that suspend can properly shut them down */ -+ r = adev->ip_blocks[i].funcs->set_clockgating_state(adev, -+ AMDGPU_CG_STATE_UNGATE); -+ /* XXX handle errors */ -+ r = adev->ip_blocks[i].funcs->suspend(adev); -+ /* XXX handle errors */ -+ } -+ -+ return 0; -+} -+ -+static int amdgpu_resume(struct amdgpu_device *adev) -+{ -+ int i, r; -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (!adev->ip_block_enabled[i]) -+ continue; -+ r = adev->ip_blocks[i].funcs->resume(adev); -+ if (r) -+ return r; -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_device_init - initialize the driver -+ * -+ * @adev: amdgpu_device pointer -+ * @pdev: drm dev pointer -+ * @pdev: pci dev pointer -+ * @flags: driver flags -+ * -+ * Initializes the driver info and hw (all asics). -+ * Returns 0 for success or an error on failure. -+ * Called at driver startup. -+ */ -+int amdgpu_device_init(struct amdgpu_device *adev, -+ struct drm_device *ddev, -+ struct pci_dev *pdev, -+ uint32_t flags) -+{ -+ int r, i; -+ bool runtime = false; -+ -+ adev->shutdown = false; -+ adev->dev = &pdev->dev; -+ adev->ddev = ddev; -+ adev->pdev = pdev; -+ adev->flags = flags; -+ adev->asic_type = flags & AMDGPU_ASIC_MASK; -+ adev->is_atom_bios = false; -+ adev->usec_timeout = AMDGPU_MAX_USEC_TIMEOUT; -+ adev->mc.gtt_size = 512 * 1024 * 1024; -+ adev->accel_working = false; -+ adev->num_rings = 0; -+ adev->mman.buffer_funcs = NULL; -+ adev->mman.buffer_funcs_ring = NULL; -+ adev->vm_manager.vm_pte_funcs = NULL; -+ adev->vm_manager.vm_pte_funcs_ring = NULL; -+ adev->gart.gart_funcs = NULL; -+ adev->fence_context = fence_context_alloc(AMDGPU_MAX_RINGS); -+ -+ adev->smc_rreg = &amdgpu_invalid_rreg; -+ adev->smc_wreg = &amdgpu_invalid_wreg; -+ adev->pcie_rreg = &amdgpu_invalid_rreg; -+ adev->pcie_wreg = &amdgpu_invalid_wreg; -+ adev->uvd_ctx_rreg = &amdgpu_invalid_rreg; -+ adev->uvd_ctx_wreg = &amdgpu_invalid_wreg; -+ adev->didt_rreg = &amdgpu_invalid_rreg; -+ adev->didt_wreg = &amdgpu_invalid_wreg; -+ adev->audio_endpt_rreg = &amdgpu_block_invalid_rreg; -+ adev->audio_endpt_wreg = &amdgpu_block_invalid_wreg; -+ -+ DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n", -+ amdgpu_asic_name[adev->asic_type], pdev->vendor, pdev->device, -+ pdev->subsystem_vendor, pdev->subsystem_device); -+ -+ /* mutex initialization are all done here so we -+ * can recall function without having locking issues */ -+ mutex_init(&adev->ring_lock); -+ atomic_set(&adev->irq.ih.lock, 0); -+ mutex_init(&adev->gem.mutex); -+ mutex_init(&adev->pm.mutex); -+ mutex_init(&adev->gfx.gpu_clock_mutex); -+ mutex_init(&adev->srbm_mutex); -+ mutex_init(&adev->grbm_idx_mutex); -+ init_rwsem(&adev->pm.mclk_lock); -+ init_rwsem(&adev->exclusive_lock); -+ mutex_init(&adev->mn_lock); -+ hash_init(adev->mn_hash); -+ -+ amdgpu_check_arguments(adev); -+ -+ /* Registers mapping */ -+ /* TODO: block userspace mapping of io register */ -+ spin_lock_init(&adev->mmio_idx_lock); -+ spin_lock_init(&adev->smc_idx_lock); -+ spin_lock_init(&adev->pcie_idx_lock); -+ spin_lock_init(&adev->uvd_ctx_idx_lock); -+ spin_lock_init(&adev->didt_idx_lock); -+ spin_lock_init(&adev->audio_endpt_idx_lock); -+ -+ adev->rmmio_base = pci_resource_start(adev->pdev, 5); -+ adev->rmmio_size = pci_resource_len(adev->pdev, 5); -+ adev->rmmio = ioremap(adev->rmmio_base, adev->rmmio_size); -+ if (adev->rmmio == NULL) { -+ return -ENOMEM; -+ } -+ DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)adev->rmmio_base); -+ DRM_INFO("register mmio size: %u\n", (unsigned)adev->rmmio_size); -+ -+ /* doorbell bar mapping */ -+ amdgpu_doorbell_init(adev); -+ -+ /* io port mapping */ -+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { -+ if (pci_resource_flags(adev->pdev, i) & IORESOURCE_IO) { -+ adev->rio_mem_size = pci_resource_len(adev->pdev, i); -+ adev->rio_mem = pci_iomap(adev->pdev, i, adev->rio_mem_size); -+ break; -+ } -+ } -+ if (adev->rio_mem == NULL) -+ DRM_ERROR("Unable to find PCI I/O BAR\n"); -+ -+ /* early init functions */ -+ r = amdgpu_early_init(adev); -+ if (r) -+ return r; -+ -+ /* if we have > 1 VGA cards, then disable the amdgpu VGA resources */ -+ /* this will fail for cards that aren't VGA class devices, just -+ * ignore it */ -+ vga_client_register(adev->pdev, adev, NULL, amdgpu_vga_set_decode); -+ -+ if (amdgpu_runtime_pm == 1) -+ runtime = true; -+ if (amdgpu_device_is_px(ddev)) -+ runtime = true; -+ vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime); -+ if (runtime) -+ vga_switcheroo_init_domain_pm_ops(adev->dev, &adev->vga_pm_domain); -+ -+ /* Read BIOS */ -+ if (!amdgpu_get_bios(adev)) -+ return -EINVAL; -+ /* Must be an ATOMBIOS */ -+ if (!adev->is_atom_bios) { -+ dev_err(adev->dev, "Expecting atombios for GPU\n"); -+ return -EINVAL; -+ } -+ r = amdgpu_atombios_init(adev); -+ if (r) -+ return r; -+ -+ /* Post card if necessary */ -+ if (!amdgpu_card_posted(adev)) { -+ if (!adev->bios) { -+ dev_err(adev->dev, "Card not posted and no BIOS - ignoring\n"); -+ return -EINVAL; -+ } -+ DRM_INFO("GPU not posted. posting now...\n"); -+ amdgpu_atom_asic_init(adev->mode_info.atom_context); -+ } -+ -+ /* Initialize clocks */ -+ r = amdgpu_atombios_get_clock_info(adev); -+ if (r) -+ return r; -+ /* init i2c buses */ -+ amdgpu_atombios_i2c_init(adev); -+ -+ /* Fence driver */ -+ r = amdgpu_fence_driver_init(adev); -+ if (r) -+ return r; -+ -+ /* init the mode config */ -+ drm_mode_config_init(adev->ddev); -+ -+ r = amdgpu_init(adev); -+ if (r) { -+ amdgpu_fini(adev); -+ return r; -+ } -+ -+ adev->accel_working = true; -+ -+ amdgpu_fbdev_init(adev); -+ -+ r = amdgpu_ib_pool_init(adev); -+ if (r) { -+ dev_err(adev->dev, "IB initialization failed (%d).\n", r); -+ return r; -+ } -+ -+ r = amdgpu_ib_ring_tests(adev); -+ if (r) -+ DRM_ERROR("ib ring test failed (%d).\n", r); -+ -+ r = amdgpu_gem_debugfs_init(adev); -+ if (r) { -+ DRM_ERROR("registering gem debugfs failed (%d).\n", r); -+ } -+ -+ r = amdgpu_debugfs_regs_init(adev); -+ if (r) { -+ DRM_ERROR("registering register debugfs failed (%d).\n", r); -+ } -+ -+ if ((amdgpu_testing & 1)) { -+ if (adev->accel_working) -+ amdgpu_test_moves(adev); -+ else -+ DRM_INFO("amdgpu: acceleration disabled, skipping move tests\n"); -+ } -+ if ((amdgpu_testing & 2)) { -+ if (adev->accel_working) -+ amdgpu_test_syncing(adev); -+ else -+ DRM_INFO("amdgpu: acceleration disabled, skipping sync tests\n"); -+ } -+ if (amdgpu_benchmarking) { -+ if (adev->accel_working) -+ amdgpu_benchmark(adev, amdgpu_benchmarking); -+ else -+ DRM_INFO("amdgpu: acceleration disabled, skipping benchmarks\n"); -+ } -+ -+ /* enable clockgating, etc. after ib tests, etc. since some blocks require -+ * explicit gating rather than handling it automatically. -+ */ -+ r = amdgpu_late_init(adev); -+ if (r) -+ return r; -+ -+ return 0; -+} -+ -+static void amdgpu_debugfs_remove_files(struct amdgpu_device *adev); -+ -+/** -+ * amdgpu_device_fini - tear down the driver -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Tear down the driver info (all asics). -+ * Called at driver shutdown. -+ */ -+void amdgpu_device_fini(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ DRM_INFO("amdgpu: finishing device.\n"); -+ adev->shutdown = true; -+ /* evict vram memory */ -+ amdgpu_bo_evict_vram(adev); -+ amdgpu_ib_pool_fini(adev); -+ amdgpu_fence_driver_fini(adev); -+ amdgpu_fbdev_fini(adev); -+ r = amdgpu_fini(adev); -+ if (adev->ip_block_enabled) -+ kfree(adev->ip_block_enabled); -+ adev->ip_block_enabled = NULL; -+ adev->accel_working = false; -+ /* free i2c buses */ -+ amdgpu_i2c_fini(adev); -+ amdgpu_atombios_fini(adev); -+ kfree(adev->bios); -+ adev->bios = NULL; -+ vga_switcheroo_unregister_client(adev->pdev); -+ vga_client_register(adev->pdev, NULL, NULL, NULL); -+ if (adev->rio_mem) -+ pci_iounmap(adev->pdev, adev->rio_mem); -+ adev->rio_mem = NULL; -+ iounmap(adev->rmmio); -+ adev->rmmio = NULL; -+ amdgpu_doorbell_fini(adev); -+ amdgpu_debugfs_regs_cleanup(adev); -+ amdgpu_debugfs_remove_files(adev); -+} -+ -+ -+/* -+ * Suspend & resume. -+ */ -+/** -+ * amdgpu_suspend_kms - initiate device suspend -+ * -+ * @pdev: drm dev pointer -+ * @state: suspend state -+ * -+ * Puts the hw in the suspend state (all asics). -+ * Returns 0 for success or an error on failure. -+ * Called at driver suspend. -+ */ -+int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon) -+{ -+ struct amdgpu_device *adev; -+ struct drm_crtc *crtc; -+ struct drm_connector *connector; -+ int i, r; -+ bool force_completion = false; -+ -+ if (dev == NULL || dev->dev_private == NULL) { -+ return -ENODEV; -+ } -+ -+ adev = dev->dev_private; -+ -+ if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) -+ return 0; -+ -+ drm_kms_helper_poll_disable(dev); -+ -+ /* turn off display hw */ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); -+ } -+ -+ /* unpin the front buffers */ -+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -+ struct amdgpu_framebuffer *rfb = to_amdgpu_framebuffer(crtc->primary->fb); -+ struct amdgpu_bo *robj; -+ -+ if (rfb == NULL || rfb->obj == NULL) { -+ continue; -+ } -+ robj = gem_to_amdgpu_bo(rfb->obj); -+ /* don't unpin kernel fb objects */ -+ if (!amdgpu_fbdev_robj_is_fb(adev, robj)) { -+ r = amdgpu_bo_reserve(robj, false); -+ if (r == 0) { -+ amdgpu_bo_unpin(robj); -+ amdgpu_bo_unreserve(robj); -+ } -+ } -+ } -+ /* evict vram memory */ -+ amdgpu_bo_evict_vram(adev); -+ -+ /* wait for gpu to finish processing current batch */ -+ for (i = 0; i < AMDGPU_MAX_RINGS; i++) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (!ring) -+ continue; -+ -+ r = amdgpu_fence_wait_empty(ring); -+ if (r) { -+ /* delay GPU reset to resume */ -+ force_completion = true; -+ } -+ } -+ if (force_completion) { -+ amdgpu_fence_driver_force_completion(adev); -+ } -+ -+ r = amdgpu_suspend(adev); -+ -+ /* evict remaining vram memory */ -+ amdgpu_bo_evict_vram(adev); -+ -+ pci_save_state(dev->pdev); -+ if (suspend) { -+ /* Shut down the device */ -+ pci_disable_device(dev->pdev); -+ pci_set_power_state(dev->pdev, PCI_D3hot); -+ } -+ -+ if (fbcon) { -+ console_lock(); -+ amdgpu_fbdev_set_suspend(adev, 1); -+ console_unlock(); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_resume_kms - initiate device resume -+ * -+ * @pdev: drm dev pointer -+ * -+ * Bring the hw back to operating state (all asics). -+ * Returns 0 for success or an error on failure. -+ * Called at driver resume. -+ */ -+int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon) -+{ -+ struct drm_connector *connector; -+ struct amdgpu_device *adev = dev->dev_private; -+ int r; -+ -+ if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) -+ return 0; -+ -+ if (fbcon) { -+ console_lock(); -+ } -+ if (resume) { -+ pci_set_power_state(dev->pdev, PCI_D0); -+ pci_restore_state(dev->pdev); -+ if (pci_enable_device(dev->pdev)) { -+ if (fbcon) -+ console_unlock(); -+ return -1; -+ } -+ } -+ -+ /* post card */ -+ amdgpu_atom_asic_init(adev->mode_info.atom_context); -+ -+ r = amdgpu_resume(adev); -+ -+ r = amdgpu_ib_ring_tests(adev); -+ if (r) -+ DRM_ERROR("ib ring test failed (%d).\n", r); -+ -+ r = amdgpu_late_init(adev); -+ if (r) -+ return r; -+ -+ /* blat the mode back in */ -+ if (fbcon) { -+ drm_helper_resume_force_mode(dev); -+ /* turn on display hw */ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); -+ } -+ } -+ -+ drm_kms_helper_poll_enable(dev); -+ -+ if (fbcon) { -+ amdgpu_fbdev_set_suspend(adev, 0); -+ console_unlock(); -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_gpu_reset - reset the asic -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Attempt the reset the GPU if it has hung (all asics). -+ * Returns 0 for success or an error on failure. -+ */ -+int amdgpu_gpu_reset(struct amdgpu_device *adev) -+{ -+ unsigned ring_sizes[AMDGPU_MAX_RINGS]; -+ uint32_t *ring_data[AMDGPU_MAX_RINGS]; -+ -+ bool saved = false; -+ -+ int i, r; -+ int resched; -+ -+ down_write(&adev->exclusive_lock); -+ -+ if (!adev->needs_reset) { -+ up_write(&adev->exclusive_lock); -+ return 0; -+ } -+ -+ adev->needs_reset = false; -+ -+ /* block TTM */ -+ resched = ttm_bo_lock_delayed_workqueue(&adev->mman.bdev); -+ -+ r = amdgpu_suspend(adev); -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (!ring) -+ continue; -+ -+ ring_sizes[i] = amdgpu_ring_backup(ring, &ring_data[i]); -+ if (ring_sizes[i]) { -+ saved = true; -+ dev_info(adev->dev, "Saved %d dwords of commands " -+ "on ring %d.\n", ring_sizes[i], i); -+ } -+ } -+ -+retry: -+ r = amdgpu_asic_reset(adev); -+ if (!r) { -+ dev_info(adev->dev, "GPU reset succeeded, trying to resume\n"); -+ r = amdgpu_resume(adev); -+ } -+ -+ if (!r) { -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (!ring) -+ continue; -+ -+ amdgpu_ring_restore(ring, ring_sizes[i], ring_data[i]); -+ ring_sizes[i] = 0; -+ ring_data[i] = NULL; -+ } -+ -+ r = amdgpu_ib_ring_tests(adev); -+ if (r) { -+ dev_err(adev->dev, "ib ring test failed (%d).\n", r); -+ if (saved) { -+ saved = false; -+ r = amdgpu_suspend(adev); -+ goto retry; -+ } -+ } -+ } else { -+ amdgpu_fence_driver_force_completion(adev); -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ if (adev->rings[i]) -+ kfree(ring_data[i]); -+ } -+ } -+ -+ drm_helper_resume_force_mode(adev->ddev); -+ -+ ttm_bo_unlock_delayed_workqueue(&adev->mman.bdev, resched); -+ if (r) { -+ /* bad news, how to tell it to userspace ? */ -+ dev_info(adev->dev, "GPU reset failed\n"); -+ } -+ -+ up_write(&adev->exclusive_lock); -+ return r; -+} -+ -+ -+/* -+ * Debugfs -+ */ -+int amdgpu_debugfs_add_files(struct amdgpu_device *adev, -+ struct drm_info_list *files, -+ unsigned nfiles) -+{ -+ unsigned i; -+ -+ for (i = 0; i < adev->debugfs_count; i++) { -+ if (adev->debugfs[i].files == files) { -+ /* Already registered */ -+ return 0; -+ } -+ } -+ -+ i = adev->debugfs_count + 1; -+ if (i > AMDGPU_DEBUGFS_MAX_COMPONENTS) { -+ DRM_ERROR("Reached maximum number of debugfs components.\n"); -+ DRM_ERROR("Report so we increase " -+ "AMDGPU_DEBUGFS_MAX_COMPONENTS.\n"); -+ return -EINVAL; -+ } -+ adev->debugfs[adev->debugfs_count].files = files; -+ adev->debugfs[adev->debugfs_count].num_files = nfiles; -+ adev->debugfs_count = i; -+#if defined(CONFIG_DEBUG_FS) -+ drm_debugfs_create_files(files, nfiles, -+ adev->ddev->control->debugfs_root, -+ adev->ddev->control); -+ drm_debugfs_create_files(files, nfiles, -+ adev->ddev->primary->debugfs_root, -+ adev->ddev->primary); -+#endif -+ return 0; -+} -+ -+static void amdgpu_debugfs_remove_files(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ unsigned i; -+ -+ for (i = 0; i < adev->debugfs_count; i++) { -+ drm_debugfs_remove_files(adev->debugfs[i].files, -+ adev->debugfs[i].num_files, -+ adev->ddev->control); -+ drm_debugfs_remove_files(adev->debugfs[i].files, -+ adev->debugfs[i].num_files, -+ adev->ddev->primary); -+ } -+#endif -+} -+ -+#if defined(CONFIG_DEBUG_FS) -+ -+static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, -+ size_t size, loff_t *pos) -+{ -+ struct amdgpu_device *adev = f->f_inode->i_private; -+ ssize_t result = 0; -+ int r; -+ -+ if (size & 0x3 || *pos & 0x3) -+ return -EINVAL; -+ -+ while (size) { -+ uint32_t value; -+ -+ if (*pos > adev->rmmio_size) -+ return result; -+ -+ value = RREG32(*pos >> 2); -+ r = put_user(value, (uint32_t *)buf); -+ if (r) -+ return r; -+ -+ result += 4; -+ buf += 4; -+ *pos += 4; -+ size -= 4; -+ } -+ -+ return result; -+} -+ -+static ssize_t amdgpu_debugfs_regs_write(struct file *f, const char __user *buf, -+ size_t size, loff_t *pos) -+{ -+ struct amdgpu_device *adev = f->f_inode->i_private; -+ ssize_t result = 0; -+ int r; -+ -+ if (size & 0x3 || *pos & 0x3) -+ return -EINVAL; -+ -+ while (size) { -+ uint32_t value; -+ -+ if (*pos > adev->rmmio_size) -+ return result; -+ -+ r = get_user(value, (uint32_t *)buf); -+ if (r) -+ return r; -+ -+ WREG32(*pos >> 2, value); -+ -+ result += 4; -+ buf += 4; -+ *pos += 4; -+ size -= 4; -+ } -+ -+ return result; -+} -+ -+static const struct file_operations amdgpu_debugfs_regs_fops = { -+ .owner = THIS_MODULE, -+ .read = amdgpu_debugfs_regs_read, -+ .write = amdgpu_debugfs_regs_write, -+ .llseek = default_llseek -+}; -+ -+static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) -+{ -+ struct drm_minor *minor = adev->ddev->primary; -+ struct dentry *ent, *root = minor->debugfs_root; -+ -+ ent = debugfs_create_file("amdgpu_regs", S_IFREG | S_IRUGO, root, -+ adev, &amdgpu_debugfs_regs_fops); -+ if (IS_ERR(ent)) -+ return PTR_ERR(ent); -+ i_size_write(ent->d_inode, adev->rmmio_size); -+ adev->debugfs_regs = ent; -+ -+ return 0; -+} -+ -+static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev) -+{ -+ debugfs_remove(adev->debugfs_regs); -+ adev->debugfs_regs = NULL; -+} -+ -+int amdgpu_debugfs_init(struct drm_minor *minor) -+{ -+ return 0; -+} -+ -+void amdgpu_debugfs_cleanup(struct drm_minor *minor) -+{ -+} -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c -new file mode 100644 -index 0000000..f22c067 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c -@@ -0,0 +1,832 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_i2c.h" -+#include "atom.h" -+#include "amdgpu_connectors.h" -+#include <asm/div64.h> -+ -+#include <linux/pm_runtime.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/drm_edid.h> -+ -+ -+static void amdgpu_flip_work_func(struct work_struct *__work) -+{ -+ struct amdgpu_flip_work *work = -+ container_of(__work, struct amdgpu_flip_work, flip_work); -+ struct amdgpu_device *adev = work->adev; -+ struct amdgpu_crtc *amdgpuCrtc = adev->mode_info.crtcs[work->crtc_id]; -+ -+ struct drm_crtc *crtc = &amdgpuCrtc->base; -+ struct amdgpu_fence *fence; -+ unsigned long flags; -+ int r; -+ -+ down_read(&adev->exclusive_lock); -+ if (work->fence) { -+ fence = to_amdgpu_fence(work->fence); -+ if (fence) { -+ r = amdgpu_fence_wait(fence, false); -+ if (r == -EDEADLK) { -+ up_read(&adev->exclusive_lock); -+ r = amdgpu_gpu_reset(adev); -+ down_read(&adev->exclusive_lock); -+ } -+ } else -+ r = fence_wait(work->fence, false); -+ -+ if (r) -+ DRM_ERROR("failed to wait on page flip fence (%d)!\n", r); -+ -+ /* We continue with the page flip even if we failed to wait on -+ * the fence, otherwise the DRM core and userspace will be -+ * confused about which BO the CRTC is scanning out -+ */ -+ -+ fence_put(work->fence); -+ work->fence = NULL; -+ } -+ -+ /* We borrow the event spin lock for protecting flip_status */ -+ spin_lock_irqsave(&crtc->dev->event_lock, flags); -+ -+ /* set the proper interrupt */ -+ amdgpu_irq_get(adev, &adev->pageflip_irq, work->crtc_id); -+ /* do the flip (mmio) */ -+ adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base); -+ /* set the flip status */ -+ amdgpuCrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; -+ -+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags); -+ up_read(&adev->exclusive_lock); -+} -+ -+/* -+ * Handle unpin events outside the interrupt handler proper. -+ */ -+static void amdgpu_unpin_work_func(struct work_struct *__work) -+{ -+ struct amdgpu_flip_work *work = -+ container_of(__work, struct amdgpu_flip_work, unpin_work); -+ int r; -+ -+ /* unpin of the old buffer */ -+ r = amdgpu_bo_reserve(work->old_rbo, false); -+ if (likely(r == 0)) { -+ r = amdgpu_bo_unpin(work->old_rbo); -+ if (unlikely(r != 0)) { -+ DRM_ERROR("failed to unpin buffer after flip\n"); -+ } -+ amdgpu_bo_unreserve(work->old_rbo); -+ } else -+ DRM_ERROR("failed to reserve buffer after flip\n"); -+ -+ drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); -+ kfree(work); -+} -+ -+int amdgpu_crtc_page_flip(struct drm_crtc *crtc, -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t page_flip_flags) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct amdgpu_framebuffer *old_amdgpu_fb; -+ struct amdgpu_framebuffer *new_amdgpu_fb; -+ struct drm_gem_object *obj; -+ struct amdgpu_flip_work *work; -+ struct amdgpu_bo *new_rbo; -+ unsigned long flags; -+ u64 tiling_flags; -+ u64 base; -+ int r; -+ -+ work = kzalloc(sizeof *work, GFP_KERNEL); -+ if (work == NULL) -+ return -ENOMEM; -+ -+ INIT_WORK(&work->flip_work, amdgpu_flip_work_func); -+ INIT_WORK(&work->unpin_work, amdgpu_unpin_work_func); -+ -+ work->event = event; -+ work->adev = adev; -+ work->crtc_id = amdgpu_crtc->crtc_id; -+ -+ /* schedule unpin of the old buffer */ -+ old_amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb); -+ obj = old_amdgpu_fb->obj; -+ -+ /* take a reference to the old object */ -+ drm_gem_object_reference(obj); -+ work->old_rbo = gem_to_amdgpu_bo(obj); -+ -+ new_amdgpu_fb = to_amdgpu_framebuffer(fb); -+ obj = new_amdgpu_fb->obj; -+ new_rbo = gem_to_amdgpu_bo(obj); -+ -+ /* pin the new buffer */ -+ r = amdgpu_bo_reserve(new_rbo, false); -+ if (unlikely(r != 0)) { -+ DRM_ERROR("failed to reserve new rbo buffer before flip\n"); -+ goto cleanup; -+ } -+ -+ r = amdgpu_bo_pin_restricted(new_rbo, AMDGPU_GEM_DOMAIN_VRAM, 0, &base); -+ if (unlikely(r != 0)) { -+ amdgpu_bo_unreserve(new_rbo); -+ r = -EINVAL; -+ DRM_ERROR("failed to pin new rbo buffer before flip\n"); -+ goto cleanup; -+ } -+ -+ work->fence = fence_get(reservation_object_get_excl(new_rbo->tbo.resv)); -+ amdgpu_bo_get_tiling_flags(new_rbo, &tiling_flags); -+ amdgpu_bo_unreserve(new_rbo); -+ -+ work->base = base; -+ -+ r = drm_vblank_get(crtc->dev, amdgpu_crtc->crtc_id); -+ if (r) { -+ DRM_ERROR("failed to get vblank before flip\n"); -+ goto pflip_cleanup; -+ } -+ -+ /* we borrow the event spin lock for protecting flip_wrok */ -+ spin_lock_irqsave(&crtc->dev->event_lock, flags); -+ if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_NONE) { -+ DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); -+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags); -+ r = -EBUSY; -+ goto vblank_cleanup; -+ } -+ -+ amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING; -+ amdgpu_crtc->pflip_works = work; -+ -+ /* update crtc fb */ -+ crtc->primary->fb = fb; -+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags); -+ queue_work(amdgpu_crtc->pflip_queue, &work->flip_work); -+ return 0; -+ -+vblank_cleanup: -+ drm_vblank_put(crtc->dev, amdgpu_crtc->crtc_id); -+ -+pflip_cleanup: -+ if (unlikely(amdgpu_bo_reserve(new_rbo, false) != 0)) { -+ DRM_ERROR("failed to reserve new rbo in error path\n"); -+ goto cleanup; -+ } -+ if (unlikely(amdgpu_bo_unpin(new_rbo) != 0)) { -+ DRM_ERROR("failed to unpin new rbo in error path\n"); -+ } -+ amdgpu_bo_unreserve(new_rbo); -+ -+cleanup: -+ drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); -+ fence_put(work->fence); -+ kfree(work); -+ -+ return r; -+} -+ -+int amdgpu_crtc_set_config(struct drm_mode_set *set) -+{ -+ struct drm_device *dev; -+ struct amdgpu_device *adev; -+ struct drm_crtc *crtc; -+ bool active = false; -+ int ret; -+ -+ if (!set || !set->crtc) -+ return -EINVAL; -+ -+ dev = set->crtc->dev; -+ -+ ret = pm_runtime_get_sync(dev->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = drm_crtc_helper_set_config(set); -+ -+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) -+ if (crtc->enabled) -+ active = true; -+ -+ pm_runtime_mark_last_busy(dev->dev); -+ -+ adev = dev->dev_private; -+ /* if we have active crtcs and we don't have a power ref, -+ take the current one */ -+ if (active && !adev->have_disp_power_ref) { -+ adev->have_disp_power_ref = true; -+ return ret; -+ } -+ /* if we have no active crtcs, then drop the power ref -+ we got before */ -+ if (!active && adev->have_disp_power_ref) { -+ pm_runtime_put_autosuspend(dev->dev); -+ adev->have_disp_power_ref = false; -+ } -+ -+ /* drop the power reference we got coming in here */ -+ pm_runtime_put_autosuspend(dev->dev); -+ return ret; -+} -+ -+static const char *encoder_names[38] = { -+ "NONE", -+ "INTERNAL_LVDS", -+ "INTERNAL_TMDS1", -+ "INTERNAL_TMDS2", -+ "INTERNAL_DAC1", -+ "INTERNAL_DAC2", -+ "INTERNAL_SDVOA", -+ "INTERNAL_SDVOB", -+ "SI170B", -+ "CH7303", -+ "CH7301", -+ "INTERNAL_DVO1", -+ "EXTERNAL_SDVOA", -+ "EXTERNAL_SDVOB", -+ "TITFP513", -+ "INTERNAL_LVTM1", -+ "VT1623", -+ "HDMI_SI1930", -+ "HDMI_INTERNAL", -+ "INTERNAL_KLDSCP_TMDS1", -+ "INTERNAL_KLDSCP_DVO1", -+ "INTERNAL_KLDSCP_DAC1", -+ "INTERNAL_KLDSCP_DAC2", -+ "SI178", -+ "MVPU_FPGA", -+ "INTERNAL_DDI", -+ "VT1625", -+ "HDMI_SI1932", -+ "DP_AN9801", -+ "DP_DP501", -+ "INTERNAL_UNIPHY", -+ "INTERNAL_KLDSCP_LVTMA", -+ "INTERNAL_UNIPHY1", -+ "INTERNAL_UNIPHY2", -+ "NUTMEG", -+ "TRAVIS", -+ "INTERNAL_VCE", -+ "INTERNAL_UNIPHY3", -+}; -+ -+static const char *hpd_names[6] = { -+ "HPD1", -+ "HPD2", -+ "HPD3", -+ "HPD4", -+ "HPD5", -+ "HPD6", -+}; -+ -+void amdgpu_print_display_setup(struct drm_device *dev) -+{ -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ struct drm_encoder *encoder; -+ struct amdgpu_encoder *amdgpu_encoder; -+ uint32_t devices; -+ int i = 0; -+ -+ DRM_INFO("AMDGPU Display Connectors\n"); -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ amdgpu_connector = to_amdgpu_connector(connector); -+ DRM_INFO("Connector %d:\n", i); -+ DRM_INFO(" %s\n", connector->name); -+ if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE) -+ DRM_INFO(" %s\n", hpd_names[amdgpu_connector->hpd.hpd]); -+ if (amdgpu_connector->ddc_bus) { -+ DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", -+ amdgpu_connector->ddc_bus->rec.mask_clk_reg, -+ amdgpu_connector->ddc_bus->rec.mask_data_reg, -+ amdgpu_connector->ddc_bus->rec.a_clk_reg, -+ amdgpu_connector->ddc_bus->rec.a_data_reg, -+ amdgpu_connector->ddc_bus->rec.en_clk_reg, -+ amdgpu_connector->ddc_bus->rec.en_data_reg, -+ amdgpu_connector->ddc_bus->rec.y_clk_reg, -+ amdgpu_connector->ddc_bus->rec.y_data_reg); -+ if (amdgpu_connector->router.ddc_valid) -+ DRM_INFO(" DDC Router 0x%x/0x%x\n", -+ amdgpu_connector->router.ddc_mux_control_pin, -+ amdgpu_connector->router.ddc_mux_state); -+ if (amdgpu_connector->router.cd_valid) -+ DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", -+ amdgpu_connector->router.cd_mux_control_pin, -+ amdgpu_connector->router.cd_mux_state); -+ } else { -+ if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || -+ connector->connector_type == DRM_MODE_CONNECTOR_DVII || -+ connector->connector_type == DRM_MODE_CONNECTOR_DVID || -+ connector->connector_type == DRM_MODE_CONNECTOR_DVIA || -+ connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || -+ connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) -+ DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n"); -+ } -+ DRM_INFO(" Encoders:\n"); -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ devices = amdgpu_encoder->devices & amdgpu_connector->devices; -+ if (devices) { -+ if (devices & ATOM_DEVICE_CRT1_SUPPORT) -+ DRM_INFO(" CRT1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_CRT2_SUPPORT) -+ DRM_INFO(" CRT2: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_LCD1_SUPPORT) -+ DRM_INFO(" LCD1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_DFP1_SUPPORT) -+ DRM_INFO(" DFP1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_DFP2_SUPPORT) -+ DRM_INFO(" DFP2: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_DFP3_SUPPORT) -+ DRM_INFO(" DFP3: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_DFP4_SUPPORT) -+ DRM_INFO(" DFP4: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_DFP5_SUPPORT) -+ DRM_INFO(" DFP5: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_DFP6_SUPPORT) -+ DRM_INFO(" DFP6: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_TV1_SUPPORT) -+ DRM_INFO(" TV1: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ if (devices & ATOM_DEVICE_CV_SUPPORT) -+ DRM_INFO(" CV: %s\n", encoder_names[amdgpu_encoder->encoder_id]); -+ } -+ } -+ i++; -+ } -+} -+ -+/** -+ * amdgpu_ddc_probe -+ * -+ */ -+bool amdgpu_ddc_probe(struct amdgpu_connector *amdgpu_connector, -+ bool use_aux) -+{ -+ u8 out = 0x0; -+ u8 buf[8]; -+ int ret; -+ struct i2c_msg msgs[] = { -+ { -+ .addr = DDC_ADDR, -+ .flags = 0, -+ .len = 1, -+ .buf = &out, -+ }, -+ { -+ .addr = DDC_ADDR, -+ .flags = I2C_M_RD, -+ .len = 8, -+ .buf = buf, -+ } -+ }; -+ -+ /* on hw with routers, select right port */ -+ if (amdgpu_connector->router.ddc_valid) -+ amdgpu_i2c_router_select_ddc_port(amdgpu_connector); -+ -+ if (use_aux) { -+ ret = i2c_transfer(&amdgpu_connector->ddc_bus->aux.ddc, msgs, 2); -+ } else { -+ ret = i2c_transfer(&amdgpu_connector->ddc_bus->adapter, msgs, 2); -+ } -+ -+ if (ret != 2) -+ /* Couldn't find an accessible DDC on this connector */ -+ return false; -+ /* Probe also for valid EDID header -+ * EDID header starts with: -+ * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. -+ * Only the first 6 bytes must be valid as -+ * drm_edid_block_valid() can fix the last 2 bytes */ -+ if (drm_edid_header_is_valid(buf) < 6) { -+ /* Couldn't find an accessible EDID on this -+ * connector */ -+ return false; -+ } -+ return true; -+} -+ -+static void amdgpu_user_framebuffer_destroy(struct drm_framebuffer *fb) -+{ -+ struct amdgpu_framebuffer *amdgpu_fb = to_amdgpu_framebuffer(fb); -+ -+ if (amdgpu_fb->obj) { -+ drm_gem_object_unreference_unlocked(amdgpu_fb->obj); -+ } -+ drm_framebuffer_cleanup(fb); -+ kfree(amdgpu_fb); -+} -+ -+static int amdgpu_user_framebuffer_create_handle(struct drm_framebuffer *fb, -+ struct drm_file *file_priv, -+ unsigned int *handle) -+{ -+ struct amdgpu_framebuffer *amdgpu_fb = to_amdgpu_framebuffer(fb); -+ -+ return drm_gem_handle_create(file_priv, amdgpu_fb->obj, handle); -+} -+ -+static const struct drm_framebuffer_funcs amdgpu_fb_funcs = { -+ .destroy = amdgpu_user_framebuffer_destroy, -+ .create_handle = amdgpu_user_framebuffer_create_handle, -+}; -+ -+int -+amdgpu_framebuffer_init(struct drm_device *dev, -+ struct amdgpu_framebuffer *rfb, -+ struct drm_mode_fb_cmd2 *mode_cmd, -+ struct drm_gem_object *obj) -+{ -+ int ret; -+ rfb->obj = obj; -+ drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); -+ ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs); -+ if (ret) { -+ rfb->obj = NULL; -+ return ret; -+ } -+ return 0; -+} -+ -+static struct drm_framebuffer * -+amdgpu_user_framebuffer_create(struct drm_device *dev, -+ struct drm_file *file_priv, -+ struct drm_mode_fb_cmd2 *mode_cmd) -+{ -+ struct drm_gem_object *obj; -+ struct amdgpu_framebuffer *amdgpu_fb; -+ int ret; -+ -+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); -+ if (obj == NULL) { -+ dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, " -+ "can't create framebuffer\n", mode_cmd->handles[0]); -+ return ERR_PTR(-ENOENT); -+ } -+ -+ amdgpu_fb = kzalloc(sizeof(*amdgpu_fb), GFP_KERNEL); -+ if (amdgpu_fb == NULL) { -+ drm_gem_object_unreference_unlocked(obj); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ ret = amdgpu_framebuffer_init(dev, amdgpu_fb, mode_cmd, obj); -+ if (ret) { -+ kfree(amdgpu_fb); -+ drm_gem_object_unreference_unlocked(obj); -+ return ERR_PTR(ret); -+ } -+ -+ return &amdgpu_fb->base; -+} -+ -+static void amdgpu_output_poll_changed(struct drm_device *dev) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ amdgpu_fb_output_poll_changed(adev); -+} -+ -+const struct drm_mode_config_funcs amdgpu_mode_funcs = { -+ .fb_create = amdgpu_user_framebuffer_create, -+ .output_poll_changed = amdgpu_output_poll_changed -+}; -+ -+static struct drm_prop_enum_list amdgpu_underscan_enum_list[] = -+{ { UNDERSCAN_OFF, "off" }, -+ { UNDERSCAN_ON, "on" }, -+ { UNDERSCAN_AUTO, "auto" }, -+}; -+ -+static struct drm_prop_enum_list amdgpu_audio_enum_list[] = -+{ { AMDGPU_AUDIO_DISABLE, "off" }, -+ { AMDGPU_AUDIO_ENABLE, "on" }, -+ { AMDGPU_AUDIO_AUTO, "auto" }, -+}; -+ -+/* XXX support different dither options? spatial, temporal, both, etc. */ -+static struct drm_prop_enum_list amdgpu_dither_enum_list[] = -+{ { AMDGPU_FMT_DITHER_DISABLE, "off" }, -+ { AMDGPU_FMT_DITHER_ENABLE, "on" }, -+}; -+ -+int amdgpu_modeset_create_props(struct amdgpu_device *adev) -+{ -+ int sz; -+ -+ if (adev->is_atom_bios) { -+ adev->mode_info.coherent_mode_property = -+ drm_property_create_range(adev->ddev, 0 , "coherent", 0, 1); -+ if (!adev->mode_info.coherent_mode_property) -+ return -ENOMEM; -+ } -+ -+ adev->mode_info.load_detect_property = -+ drm_property_create_range(adev->ddev, 0, "load detection", 0, 1); -+ if (!adev->mode_info.load_detect_property) -+ return -ENOMEM; -+ -+ drm_mode_create_scaling_mode_property(adev->ddev); -+ -+ sz = ARRAY_SIZE(amdgpu_underscan_enum_list); -+ adev->mode_info.underscan_property = -+ drm_property_create_enum(adev->ddev, 0, -+ "underscan", -+ amdgpu_underscan_enum_list, sz); -+ -+ adev->mode_info.underscan_hborder_property = -+ drm_property_create_range(adev->ddev, 0, -+ "underscan hborder", 0, 128); -+ if (!adev->mode_info.underscan_hborder_property) -+ return -ENOMEM; -+ -+ adev->mode_info.underscan_vborder_property = -+ drm_property_create_range(adev->ddev, 0, -+ "underscan vborder", 0, 128); -+ if (!adev->mode_info.underscan_vborder_property) -+ return -ENOMEM; -+ -+ sz = ARRAY_SIZE(amdgpu_audio_enum_list); -+ adev->mode_info.audio_property = -+ drm_property_create_enum(adev->ddev, 0, -+ "audio", -+ amdgpu_audio_enum_list, sz); -+ -+ sz = ARRAY_SIZE(amdgpu_dither_enum_list); -+ adev->mode_info.dither_property = -+ drm_property_create_enum(adev->ddev, 0, -+ "dither", -+ amdgpu_dither_enum_list, sz); -+ -+ return 0; -+} -+ -+void amdgpu_update_display_priority(struct amdgpu_device *adev) -+{ -+ /* adjustment options for the display watermarks */ -+ if ((amdgpu_disp_priority == 0) || (amdgpu_disp_priority > 2)) -+ adev->mode_info.disp_priority = 0; -+ else -+ adev->mode_info.disp_priority = amdgpu_disp_priority; -+ -+} -+ -+static bool is_hdtv_mode(const struct drm_display_mode *mode) -+{ -+ /* try and guess if this is a tv or a monitor */ -+ if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ -+ (mode->vdisplay == 576) || /* 576p */ -+ (mode->vdisplay == 720) || /* 720p */ -+ (mode->vdisplay == 1080)) /* 1080p */ -+ return true; -+ else -+ return false; -+} -+ -+bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc, -+ const struct drm_display_mode *mode, -+ struct drm_display_mode *adjusted_mode) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct drm_encoder *encoder; -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct amdgpu_encoder *amdgpu_encoder; -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ u32 src_v = 1, dst_v = 1; -+ u32 src_h = 1, dst_h = 1; -+ -+ amdgpu_crtc->h_border = 0; -+ amdgpu_crtc->v_border = 0; -+ -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ if (encoder->crtc != crtc) -+ continue; -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ connector = amdgpu_get_connector_for_encoder(encoder); -+ amdgpu_connector = to_amdgpu_connector(connector); -+ -+ /* set scaling */ -+ if (amdgpu_encoder->rmx_type == RMX_OFF) -+ amdgpu_crtc->rmx_type = RMX_OFF; -+ else if (mode->hdisplay < amdgpu_encoder->native_mode.hdisplay || -+ mode->vdisplay < amdgpu_encoder->native_mode.vdisplay) -+ amdgpu_crtc->rmx_type = amdgpu_encoder->rmx_type; -+ else -+ amdgpu_crtc->rmx_type = RMX_OFF; -+ /* copy native mode */ -+ memcpy(&amdgpu_crtc->native_mode, -+ &amdgpu_encoder->native_mode, -+ sizeof(struct drm_display_mode)); -+ src_v = crtc->mode.vdisplay; -+ dst_v = amdgpu_crtc->native_mode.vdisplay; -+ src_h = crtc->mode.hdisplay; -+ dst_h = amdgpu_crtc->native_mode.hdisplay; -+ -+ /* fix up for overscan on hdmi */ -+ if ((!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && -+ ((amdgpu_encoder->underscan_type == UNDERSCAN_ON) || -+ ((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) && -+ drm_detect_hdmi_monitor(amdgpu_connector_edid(connector)) && -+ is_hdtv_mode(mode)))) { -+ if (amdgpu_encoder->underscan_hborder != 0) -+ amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder; -+ else -+ amdgpu_crtc->h_border = (mode->hdisplay >> 5) + 16; -+ if (amdgpu_encoder->underscan_vborder != 0) -+ amdgpu_crtc->v_border = amdgpu_encoder->underscan_vborder; -+ else -+ amdgpu_crtc->v_border = (mode->vdisplay >> 5) + 16; -+ amdgpu_crtc->rmx_type = RMX_FULL; -+ src_v = crtc->mode.vdisplay; -+ dst_v = crtc->mode.vdisplay - (amdgpu_crtc->v_border * 2); -+ src_h = crtc->mode.hdisplay; -+ dst_h = crtc->mode.hdisplay - (amdgpu_crtc->h_border * 2); -+ } -+ } -+ if (amdgpu_crtc->rmx_type != RMX_OFF) { -+ fixed20_12 a, b; -+ a.full = dfixed_const(src_v); -+ b.full = dfixed_const(dst_v); -+ amdgpu_crtc->vsc.full = dfixed_div(a, b); -+ a.full = dfixed_const(src_h); -+ b.full = dfixed_const(dst_h); -+ amdgpu_crtc->hsc.full = dfixed_div(a, b); -+ } else { -+ amdgpu_crtc->vsc.full = dfixed_const(1); -+ amdgpu_crtc->hsc.full = dfixed_const(1); -+ } -+ return true; -+} -+ -+/* -+ * Retrieve current video scanout position of crtc on a given gpu, and -+ * an optional accurate timestamp of when query happened. -+ * -+ * \param dev Device to query. -+ * \param crtc Crtc to query. -+ * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). -+ * \param *vpos Location where vertical scanout position should be stored. -+ * \param *hpos Location where horizontal scanout position should go. -+ * \param *stime Target location for timestamp taken immediately before -+ * scanout position query. Can be NULL to skip timestamp. -+ * \param *etime Target location for timestamp taken immediately after -+ * scanout position query. Can be NULL to skip timestamp. -+ * -+ * Returns vpos as a positive number while in active scanout area. -+ * Returns vpos as a negative number inside vblank, counting the number -+ * of scanlines to go until end of vblank, e.g., -1 means "one scanline -+ * until start of active scanout / end of vblank." -+ * -+ * \return Flags, or'ed together as follows: -+ * -+ * DRM_SCANOUTPOS_VALID = Query successful. -+ * DRM_SCANOUTPOS_INVBL = Inside vblank. -+ * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of -+ * this flag means that returned position may be offset by a constant but -+ * unknown small number of scanlines wrt. real scanout position. -+ * -+ */ -+int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, -+ int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) -+{ -+ u32 vbl = 0, position = 0; -+ int vbl_start, vbl_end, vtotal, ret = 0; -+ bool in_vbl = true; -+ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ -+ -+ /* Get optional system timestamp before query. */ -+ if (stime) -+ *stime = ktime_get(); -+ -+ if (amdgpu_display_page_flip_get_scanoutpos(adev, crtc, &vbl, &position) == 0) -+ ret |= DRM_SCANOUTPOS_VALID; -+ -+ /* Get optional system timestamp after query. */ -+ if (etime) -+ *etime = ktime_get(); -+ -+ /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ -+ -+ /* Decode into vertical and horizontal scanout position. */ -+ *vpos = position & 0x1fff; -+ *hpos = (position >> 16) & 0x1fff; -+ -+ /* Valid vblank area boundaries from gpu retrieved? */ -+ if (vbl > 0) { -+ /* Yes: Decode. */ -+ ret |= DRM_SCANOUTPOS_ACCURATE; -+ vbl_start = vbl & 0x1fff; -+ vbl_end = (vbl >> 16) & 0x1fff; -+ } -+ else { -+ /* No: Fake something reasonable which gives at least ok results. */ -+ vbl_start = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; -+ vbl_end = 0; -+ } -+ -+ /* Test scanout position against vblank region. */ -+ if ((*vpos < vbl_start) && (*vpos >= vbl_end)) -+ in_vbl = false; -+ -+ /* Check if inside vblank area and apply corrective offsets: -+ * vpos will then be >=0 in video scanout area, but negative -+ * within vblank area, counting down the number of lines until -+ * start of scanout. -+ */ -+ -+ /* Inside "upper part" of vblank area? Apply corrective offset if so: */ -+ if (in_vbl && (*vpos >= vbl_start)) { -+ vtotal = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; -+ *vpos = *vpos - vtotal; -+ } -+ -+ /* Correct for shifted end of vbl at vbl_end. */ -+ *vpos = *vpos - vbl_end; -+ -+ /* In vblank? */ -+ if (in_vbl) -+ ret |= DRM_SCANOUTPOS_IN_VBLANK; -+ -+ /* Is vpos outside nominal vblank area, but less than -+ * 1/100 of a frame height away from start of vblank? -+ * If so, assume this isn't a massively delayed vblank -+ * interrupt, but a vblank interrupt that fired a few -+ * microseconds before true start of vblank. Compensate -+ * by adding a full frame duration to the final timestamp. -+ * Happens, e.g., on ATI R500, R600. -+ * -+ * We only do this if DRM_CALLED_FROM_VBLIRQ. -+ */ -+ if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { -+ vbl_start = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; -+ vtotal = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; -+ -+ if (vbl_start - *vpos < vtotal / 100) { -+ *vpos -= vtotal; -+ -+ /* Signal this correction as "applied". */ -+ ret |= 0x8; -+ } -+ } -+ -+ return ret; -+} -+ -+int amdgpu_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc) -+{ -+ if (crtc < 0 || crtc >= adev->mode_info.num_crtc) -+ return AMDGPU_CRTC_IRQ_NONE; -+ -+ switch (crtc) { -+ case 0: -+ return AMDGPU_CRTC_IRQ_VBLANK1; -+ case 1: -+ return AMDGPU_CRTC_IRQ_VBLANK2; -+ case 2: -+ return AMDGPU_CRTC_IRQ_VBLANK3; -+ case 3: -+ return AMDGPU_CRTC_IRQ_VBLANK4; -+ case 4: -+ return AMDGPU_CRTC_IRQ_VBLANK5; -+ case 5: -+ return AMDGPU_CRTC_IRQ_VBLANK6; -+ default: -+ return AMDGPU_CRTC_IRQ_NONE; -+ } -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c -new file mode 100644 -index 0000000..7b7f4ab ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c -@@ -0,0 +1,955 @@ -+/* -+ * Copyright 2011 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: Alex Deucher -+ */ -+ -+#include "drmP.h" -+#include "amdgpu.h" -+#include "amdgpu_atombios.h" -+#include "amdgpu_i2c.h" -+#include "amdgpu_dpm.h" -+#include "atom.h" -+ -+void amdgpu_dpm_print_class_info(u32 class, u32 class2) -+{ -+ printk("\tui class: "); -+ switch (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) { -+ case ATOM_PPLIB_CLASSIFICATION_UI_NONE: -+ default: -+ printk("none\n"); -+ break; -+ case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY: -+ printk("battery\n"); -+ break; -+ case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED: -+ printk("balanced\n"); -+ break; -+ case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE: -+ printk("performance\n"); -+ break; -+ } -+ printk("\tinternal class: "); -+ if (((class & ~ATOM_PPLIB_CLASSIFICATION_UI_MASK) == 0) && -+ (class2 == 0)) -+ printk("none"); -+ else { -+ if (class & ATOM_PPLIB_CLASSIFICATION_BOOT) -+ printk("boot "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL) -+ printk("thermal "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE) -+ printk("limited_pwr "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_REST) -+ printk("rest "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_FORCED) -+ printk("forced "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) -+ printk("3d_perf "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE) -+ printk("ovrdrv "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) -+ printk("uvd "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW) -+ printk("3d_low "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_ACPI) -+ printk("acpi "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) -+ printk("uvd_hd2 "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) -+ printk("uvd_hd "); -+ if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) -+ printk("uvd_sd "); -+ if (class2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2) -+ printk("limited_pwr2 "); -+ if (class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) -+ printk("ulv "); -+ if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) -+ printk("uvd_mvc "); -+ } -+ printk("\n"); -+} -+ -+void amdgpu_dpm_print_cap_info(u32 caps) -+{ -+ printk("\tcaps: "); -+ if (caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) -+ printk("single_disp "); -+ if (caps & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK) -+ printk("video "); -+ if (caps & ATOM_PPLIB_DISALLOW_ON_DC) -+ printk("no_dc "); -+ printk("\n"); -+} -+ -+void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev, -+ struct amdgpu_ps *rps) -+{ -+ printk("\tstatus: "); -+ if (rps == adev->pm.dpm.current_ps) -+ printk("c "); -+ if (rps == adev->pm.dpm.requested_ps) -+ printk("r "); -+ if (rps == adev->pm.dpm.boot_ps) -+ printk("b "); -+ printk("\n"); -+} -+ -+u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev) -+{ -+ struct drm_device *dev = adev->ddev; -+ struct drm_crtc *crtc; -+ struct amdgpu_crtc *amdgpu_crtc; -+ u32 line_time_us, vblank_lines; -+ u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */ -+ -+ if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { -+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -+ amdgpu_crtc = to_amdgpu_crtc(crtc); -+ if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) { -+ line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) / -+ amdgpu_crtc->hw_mode.clock; -+ vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end - -+ amdgpu_crtc->hw_mode.crtc_vdisplay + -+ (amdgpu_crtc->v_border * 2); -+ vblank_time_us = vblank_lines * line_time_us; -+ break; -+ } -+ } -+ } -+ -+ return vblank_time_us; -+} -+ -+u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev) -+{ -+ struct drm_device *dev = adev->ddev; -+ struct drm_crtc *crtc; -+ struct amdgpu_crtc *amdgpu_crtc; -+ u32 vrefresh = 0; -+ -+ if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { -+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -+ amdgpu_crtc = to_amdgpu_crtc(crtc); -+ if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) { -+ vrefresh = amdgpu_crtc->hw_mode.vrefresh; -+ break; -+ } -+ } -+ } -+ -+ return vrefresh; -+} -+ -+void amdgpu_calculate_u_and_p(u32 i, u32 r_c, u32 p_b, -+ u32 *p, u32 *u) -+{ -+ u32 b_c = 0; -+ u32 i_c; -+ u32 tmp; -+ -+ i_c = (i * r_c) / 100; -+ tmp = i_c >> p_b; -+ -+ while (tmp) { -+ b_c++; -+ tmp >>= 1; -+ } -+ -+ *u = (b_c + 1) / 2; -+ *p = i_c / (1 << (2 * (*u))); -+} -+ -+int amdgpu_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th) -+{ -+ u32 k, a, ah, al; -+ u32 t1; -+ -+ if ((fl == 0) || (fh == 0) || (fl > fh)) -+ return -EINVAL; -+ -+ k = (100 * fh) / fl; -+ t1 = (t * (k - 100)); -+ a = (1000 * (100 * h + t1)) / (10000 + (t1 / 100)); -+ a = (a + 5) / 10; -+ ah = ((a * t) + 5000) / 10000; -+ al = a - ah; -+ -+ *th = t - ah; -+ *tl = t + al; -+ -+ return 0; -+} -+ -+bool amdgpu_is_uvd_state(u32 class, u32 class2) -+{ -+ if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) -+ return true; -+ if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) -+ return true; -+ if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) -+ return true; -+ if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) -+ return true; -+ if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) -+ return true; -+ return false; -+} -+ -+bool amdgpu_is_internal_thermal_sensor(enum amdgpu_int_thermal_type sensor) -+{ -+ switch (sensor) { -+ case THERMAL_TYPE_RV6XX: -+ case THERMAL_TYPE_RV770: -+ case THERMAL_TYPE_EVERGREEN: -+ case THERMAL_TYPE_SUMO: -+ case THERMAL_TYPE_NI: -+ case THERMAL_TYPE_SI: -+ case THERMAL_TYPE_CI: -+ case THERMAL_TYPE_KV: -+ return true; -+ case THERMAL_TYPE_ADT7473_WITH_INTERNAL: -+ case THERMAL_TYPE_EMC2103_WITH_INTERNAL: -+ return false; /* need special handling */ -+ case THERMAL_TYPE_NONE: -+ case THERMAL_TYPE_EXTERNAL: -+ case THERMAL_TYPE_EXTERNAL_GPIO: -+ default: -+ return false; -+ } -+} -+ -+union power_info { -+ struct _ATOM_POWERPLAY_INFO info; -+ struct _ATOM_POWERPLAY_INFO_V2 info_2; -+ struct _ATOM_POWERPLAY_INFO_V3 info_3; -+ struct _ATOM_PPLIB_POWERPLAYTABLE pplib; -+ struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; -+ struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; -+ struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4; -+ struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5; -+}; -+ -+union fan_info { -+ struct _ATOM_PPLIB_FANTABLE fan; -+ struct _ATOM_PPLIB_FANTABLE2 fan2; -+ struct _ATOM_PPLIB_FANTABLE3 fan3; -+}; -+ -+static int amdgpu_parse_clk_voltage_dep_table(struct amdgpu_clock_voltage_dependency_table *amdgpu_table, -+ ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table) -+{ -+ u32 size = atom_table->ucNumEntries * -+ sizeof(struct amdgpu_clock_voltage_dependency_entry); -+ int i; -+ ATOM_PPLIB_Clock_Voltage_Dependency_Record *entry; -+ -+ amdgpu_table->entries = kzalloc(size, GFP_KERNEL); -+ if (!amdgpu_table->entries) -+ return -ENOMEM; -+ -+ entry = &atom_table->entries[0]; -+ for (i = 0; i < atom_table->ucNumEntries; i++) { -+ amdgpu_table->entries[i].clk = le16_to_cpu(entry->usClockLow) | -+ (entry->ucClockHigh << 16); -+ amdgpu_table->entries[i].v = le16_to_cpu(entry->usVoltage); -+ entry = (ATOM_PPLIB_Clock_Voltage_Dependency_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Record)); -+ } -+ amdgpu_table->count = atom_table->ucNumEntries; -+ -+ return 0; -+} -+ -+int amdgpu_get_platform_caps(struct amdgpu_device *adev) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ union power_info *power_info; -+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); -+ u16 data_offset; -+ u8 frev, crev; -+ -+ if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, -+ &frev, &crev, &data_offset)) -+ return -EINVAL; -+ power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); -+ -+ adev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); -+ adev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); -+ adev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); -+ -+ return 0; -+} -+ -+/* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */ -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8 24 -+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V9 26 -+ -+int amdgpu_parse_extended_power_table(struct amdgpu_device *adev) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ union power_info *power_info; -+ union fan_info *fan_info; -+ ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table; -+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); -+ u16 data_offset; -+ u8 frev, crev; -+ int ret, i; -+ -+ if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, -+ &frev, &crev, &data_offset)) -+ return -EINVAL; -+ power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); -+ -+ /* fan table */ -+ if (le16_to_cpu(power_info->pplib.usTableSize) >= -+ sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { -+ if (power_info->pplib3.usFanTableOffset) { -+ fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib3.usFanTableOffset)); -+ adev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst; -+ adev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin); -+ adev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed); -+ adev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh); -+ adev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin); -+ adev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed); -+ adev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh); -+ if (fan_info->fan.ucFanTableFormat >= 2) -+ adev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax); -+ else -+ adev->pm.dpm.fan.t_max = 10900; -+ adev->pm.dpm.fan.cycle_delay = 100000; -+ if (fan_info->fan.ucFanTableFormat >= 3) { -+ adev->pm.dpm.fan.control_mode = fan_info->fan3.ucFanControlMode; -+ adev->pm.dpm.fan.default_max_fan_pwm = -+ le16_to_cpu(fan_info->fan3.usFanPWMMax); -+ adev->pm.dpm.fan.default_fan_output_sensitivity = 4836; -+ adev->pm.dpm.fan.fan_output_sensitivity = -+ le16_to_cpu(fan_info->fan3.usFanOutputSensitivity); -+ } -+ adev->pm.dpm.fan.ucode_fan_control = true; -+ } -+ } -+ -+ /* clock dependancy tables, shedding tables */ -+ if (le16_to_cpu(power_info->pplib.usTableSize) >= -+ sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) { -+ if (power_info->pplib4.usVddcDependencyOnSCLKOffset) { -+ dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset)); -+ ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk, -+ dep_table); -+ if (ret) { -+ amdgpu_free_extended_power_table(adev); -+ return ret; -+ } -+ } -+ if (power_info->pplib4.usVddciDependencyOnMCLKOffset) { -+ dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset)); -+ ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddci_dependency_on_mclk, -+ dep_table); -+ if (ret) { -+ amdgpu_free_extended_power_table(adev); -+ return ret; -+ } -+ } -+ if (power_info->pplib4.usVddcDependencyOnMCLKOffset) { -+ dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset)); -+ ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk, -+ dep_table); -+ if (ret) { -+ amdgpu_free_extended_power_table(adev); -+ return ret; -+ } -+ } -+ if (power_info->pplib4.usMvddDependencyOnMCLKOffset) { -+ dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib4.usMvddDependencyOnMCLKOffset)); -+ ret = amdgpu_parse_clk_voltage_dep_table(&adev->pm.dpm.dyn_state.mvdd_dependency_on_mclk, -+ dep_table); -+ if (ret) { -+ amdgpu_free_extended_power_table(adev); -+ return ret; -+ } -+ } -+ if (power_info->pplib4.usMaxClockVoltageOnDCOffset) { -+ ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v = -+ (ATOM_PPLIB_Clock_Voltage_Limit_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset)); -+ if (clk_v->ucNumEntries) { -+ adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk = -+ le16_to_cpu(clk_v->entries[0].usSclkLow) | -+ (clk_v->entries[0].ucSclkHigh << 16); -+ adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk = -+ le16_to_cpu(clk_v->entries[0].usMclkLow) | -+ (clk_v->entries[0].ucMclkHigh << 16); -+ adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc = -+ le16_to_cpu(clk_v->entries[0].usVddc); -+ adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci = -+ le16_to_cpu(clk_v->entries[0].usVddci); -+ } -+ } -+ if (power_info->pplib4.usVddcPhaseShedLimitsTableOffset) { -+ ATOM_PPLIB_PhaseSheddingLimits_Table *psl = -+ (ATOM_PPLIB_PhaseSheddingLimits_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib4.usVddcPhaseShedLimitsTableOffset)); -+ ATOM_PPLIB_PhaseSheddingLimits_Record *entry; -+ -+ adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries = -+ kzalloc(psl->ucNumEntries * -+ sizeof(struct amdgpu_phase_shedding_limits_entry), -+ GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ -+ entry = &psl->entries[0]; -+ for (i = 0; i < psl->ucNumEntries; i++) { -+ adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].sclk = -+ le16_to_cpu(entry->usSclkLow) | (entry->ucSclkHigh << 16); -+ adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].mclk = -+ le16_to_cpu(entry->usMclkLow) | (entry->ucMclkHigh << 16); -+ adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].voltage = -+ le16_to_cpu(entry->usVoltage); -+ entry = (ATOM_PPLIB_PhaseSheddingLimits_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_PhaseSheddingLimits_Record)); -+ } -+ adev->pm.dpm.dyn_state.phase_shedding_limits_table.count = -+ psl->ucNumEntries; -+ } -+ } -+ -+ /* cac data */ -+ if (le16_to_cpu(power_info->pplib.usTableSize) >= -+ sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) { -+ adev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit); -+ adev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit); -+ adev->pm.dpm.near_tdp_limit_adjusted = adev->pm.dpm.near_tdp_limit; -+ adev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit); -+ if (adev->pm.dpm.tdp_od_limit) -+ adev->pm.dpm.power_control = true; -+ else -+ adev->pm.dpm.power_control = false; -+ adev->pm.dpm.tdp_adjustment = 0; -+ adev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold); -+ adev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage); -+ adev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope); -+ if (power_info->pplib5.usCACLeakageTableOffset) { -+ ATOM_PPLIB_CAC_Leakage_Table *cac_table = -+ (ATOM_PPLIB_CAC_Leakage_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset)); -+ ATOM_PPLIB_CAC_Leakage_Record *entry; -+ u32 size = cac_table->ucNumEntries * sizeof(struct amdgpu_cac_leakage_table); -+ adev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.cac_leakage_table.entries) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ entry = &cac_table->entries[0]; -+ for (i = 0; i < cac_table->ucNumEntries; i++) { -+ if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) { -+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc1 = -+ le16_to_cpu(entry->usVddc1); -+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc2 = -+ le16_to_cpu(entry->usVddc2); -+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc3 = -+ le16_to_cpu(entry->usVddc3); -+ } else { -+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc = -+ le16_to_cpu(entry->usVddc); -+ adev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage = -+ le32_to_cpu(entry->ulLeakageValue); -+ } -+ entry = (ATOM_PPLIB_CAC_Leakage_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_CAC_Leakage_Record)); -+ } -+ adev->pm.dpm.dyn_state.cac_leakage_table.count = cac_table->ucNumEntries; -+ } -+ } -+ -+ /* ext tables */ -+ if (le16_to_cpu(power_info->pplib.usTableSize) >= -+ sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { -+ ATOM_PPLIB_EXTENDEDHEADER *ext_hdr = (ATOM_PPLIB_EXTENDEDHEADER *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(power_info->pplib3.usExtendendedHeaderOffset)); -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2) && -+ ext_hdr->usVCETableOffset) { -+ VCEClockInfoArray *array = (VCEClockInfoArray *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usVCETableOffset) + 1); -+ ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *limits = -+ (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usVCETableOffset) + 1 + -+ 1 + array->ucNumEntries * sizeof(VCEClockInfo)); -+ ATOM_PPLIB_VCE_State_Table *states = -+ (ATOM_PPLIB_VCE_State_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usVCETableOffset) + 1 + -+ 1 + (array->ucNumEntries * sizeof (VCEClockInfo)) + -+ 1 + (limits->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record))); -+ ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *entry; -+ ATOM_PPLIB_VCE_State_Record *state_entry; -+ VCEClockInfo *vce_clk; -+ u32 size = limits->numEntries * -+ sizeof(struct amdgpu_vce_clock_voltage_dependency_entry); -+ adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries = -+ kzalloc(size, GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.count = -+ limits->numEntries; -+ entry = &limits->entries[0]; -+ state_entry = &states->entries[0]; -+ for (i = 0; i < limits->numEntries; i++) { -+ vce_clk = (VCEClockInfo *) -+ ((u8 *)&array->entries[0] + -+ (entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo))); -+ adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].evclk = -+ le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16); -+ adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].ecclk = -+ le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16); -+ adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].v = -+ le16_to_cpu(entry->usVoltage); -+ entry = (ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record)); -+ } -+ for (i = 0; i < states->numEntries; i++) { -+ if (i >= AMDGPU_MAX_VCE_LEVELS) -+ break; -+ vce_clk = (VCEClockInfo *) -+ ((u8 *)&array->entries[0] + -+ (state_entry->ucVCEClockInfoIndex * sizeof(VCEClockInfo))); -+ adev->pm.dpm.vce_states[i].evclk = -+ le16_to_cpu(vce_clk->usEVClkLow) | (vce_clk->ucEVClkHigh << 16); -+ adev->pm.dpm.vce_states[i].ecclk = -+ le16_to_cpu(vce_clk->usECClkLow) | (vce_clk->ucECClkHigh << 16); -+ adev->pm.dpm.vce_states[i].clk_idx = -+ state_entry->ucClockInfoIndex & 0x3f; -+ adev->pm.dpm.vce_states[i].pstate = -+ (state_entry->ucClockInfoIndex & 0xc0) >> 6; -+ state_entry = (ATOM_PPLIB_VCE_State_Record *) -+ ((u8 *)state_entry + sizeof(ATOM_PPLIB_VCE_State_Record)); -+ } -+ } -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) && -+ ext_hdr->usUVDTableOffset) { -+ UVDClockInfoArray *array = (UVDClockInfoArray *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usUVDTableOffset) + 1); -+ ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *limits = -+ (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usUVDTableOffset) + 1 + -+ 1 + (array->ucNumEntries * sizeof (UVDClockInfo))); -+ ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *entry; -+ u32 size = limits->numEntries * -+ sizeof(struct amdgpu_uvd_clock_voltage_dependency_entry); -+ adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries = -+ kzalloc(size, GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count = -+ limits->numEntries; -+ entry = &limits->entries[0]; -+ for (i = 0; i < limits->numEntries; i++) { -+ UVDClockInfo *uvd_clk = (UVDClockInfo *) -+ ((u8 *)&array->entries[0] + -+ (entry->ucUVDClockInfoIndex * sizeof(UVDClockInfo))); -+ adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].vclk = -+ le16_to_cpu(uvd_clk->usVClkLow) | (uvd_clk->ucVClkHigh << 16); -+ adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].dclk = -+ le16_to_cpu(uvd_clk->usDClkLow) | (uvd_clk->ucDClkHigh << 16); -+ adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].v = -+ le16_to_cpu(entry->usVoltage); -+ entry = (ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record)); -+ } -+ } -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4) && -+ ext_hdr->usSAMUTableOffset) { -+ ATOM_PPLIB_SAMClk_Voltage_Limit_Table *limits = -+ (ATOM_PPLIB_SAMClk_Voltage_Limit_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usSAMUTableOffset) + 1); -+ ATOM_PPLIB_SAMClk_Voltage_Limit_Record *entry; -+ u32 size = limits->numEntries * -+ sizeof(struct amdgpu_clock_voltage_dependency_entry); -+ adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries = -+ kzalloc(size, GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.count = -+ limits->numEntries; -+ entry = &limits->entries[0]; -+ for (i = 0; i < limits->numEntries; i++) { -+ adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].clk = -+ le16_to_cpu(entry->usSAMClockLow) | (entry->ucSAMClockHigh << 16); -+ adev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].v = -+ le16_to_cpu(entry->usVoltage); -+ entry = (ATOM_PPLIB_SAMClk_Voltage_Limit_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_SAMClk_Voltage_Limit_Record)); -+ } -+ } -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5) && -+ ext_hdr->usPPMTableOffset) { -+ ATOM_PPLIB_PPM_Table *ppm = (ATOM_PPLIB_PPM_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usPPMTableOffset)); -+ adev->pm.dpm.dyn_state.ppm_table = -+ kzalloc(sizeof(struct amdgpu_ppm_table), GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.ppm_table) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ adev->pm.dpm.dyn_state.ppm_table->ppm_design = ppm->ucPpmDesign; -+ adev->pm.dpm.dyn_state.ppm_table->cpu_core_number = -+ le16_to_cpu(ppm->usCpuCoreNumber); -+ adev->pm.dpm.dyn_state.ppm_table->platform_tdp = -+ le32_to_cpu(ppm->ulPlatformTDP); -+ adev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdp = -+ le32_to_cpu(ppm->ulSmallACPlatformTDP); -+ adev->pm.dpm.dyn_state.ppm_table->platform_tdc = -+ le32_to_cpu(ppm->ulPlatformTDC); -+ adev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdc = -+ le32_to_cpu(ppm->ulSmallACPlatformTDC); -+ adev->pm.dpm.dyn_state.ppm_table->apu_tdp = -+ le32_to_cpu(ppm->ulApuTDP); -+ adev->pm.dpm.dyn_state.ppm_table->dgpu_tdp = -+ le32_to_cpu(ppm->ulDGpuTDP); -+ adev->pm.dpm.dyn_state.ppm_table->dgpu_ulv_power = -+ le32_to_cpu(ppm->ulDGpuUlvPower); -+ adev->pm.dpm.dyn_state.ppm_table->tj_max = -+ le32_to_cpu(ppm->ulTjmax); -+ } -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6) && -+ ext_hdr->usACPTableOffset) { -+ ATOM_PPLIB_ACPClk_Voltage_Limit_Table *limits = -+ (ATOM_PPLIB_ACPClk_Voltage_Limit_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usACPTableOffset) + 1); -+ ATOM_PPLIB_ACPClk_Voltage_Limit_Record *entry; -+ u32 size = limits->numEntries * -+ sizeof(struct amdgpu_clock_voltage_dependency_entry); -+ adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries = -+ kzalloc(size, GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.count = -+ limits->numEntries; -+ entry = &limits->entries[0]; -+ for (i = 0; i < limits->numEntries; i++) { -+ adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].clk = -+ le16_to_cpu(entry->usACPClockLow) | (entry->ucACPClockHigh << 16); -+ adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].v = -+ le16_to_cpu(entry->usVoltage); -+ entry = (ATOM_PPLIB_ACPClk_Voltage_Limit_Record *) -+ ((u8 *)entry + sizeof(ATOM_PPLIB_ACPClk_Voltage_Limit_Record)); -+ } -+ } -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7) && -+ ext_hdr->usPowerTuneTableOffset) { -+ u8 rev = *(u8 *)(mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); -+ ATOM_PowerTune_Table *pt; -+ adev->pm.dpm.dyn_state.cac_tdp_table = -+ kzalloc(sizeof(struct amdgpu_cac_tdp_table), GFP_KERNEL); -+ if (!adev->pm.dpm.dyn_state.cac_tdp_table) { -+ amdgpu_free_extended_power_table(adev); -+ return -ENOMEM; -+ } -+ if (rev > 0) { -+ ATOM_PPLIB_POWERTUNE_Table_V1 *ppt = (ATOM_PPLIB_POWERTUNE_Table_V1 *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); -+ adev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = -+ ppt->usMaximumPowerDeliveryLimit; -+ pt = &ppt->power_tune_table; -+ } else { -+ ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); -+ adev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = 255; -+ pt = &ppt->power_tune_table; -+ } -+ adev->pm.dpm.dyn_state.cac_tdp_table->tdp = le16_to_cpu(pt->usTDP); -+ adev->pm.dpm.dyn_state.cac_tdp_table->configurable_tdp = -+ le16_to_cpu(pt->usConfigurableTDP); -+ adev->pm.dpm.dyn_state.cac_tdp_table->tdc = le16_to_cpu(pt->usTDC); -+ adev->pm.dpm.dyn_state.cac_tdp_table->battery_power_limit = -+ le16_to_cpu(pt->usBatteryPowerLimit); -+ adev->pm.dpm.dyn_state.cac_tdp_table->small_power_limit = -+ le16_to_cpu(pt->usSmallPowerLimit); -+ adev->pm.dpm.dyn_state.cac_tdp_table->low_cac_leakage = -+ le16_to_cpu(pt->usLowCACLeakage); -+ adev->pm.dpm.dyn_state.cac_tdp_table->high_cac_leakage = -+ le16_to_cpu(pt->usHighCACLeakage); -+ } -+ if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8) && -+ ext_hdr->usSclkVddgfxTableOffset) { -+ dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) -+ (mode_info->atom_context->bios + data_offset + -+ le16_to_cpu(ext_hdr->usSclkVddgfxTableOffset)); -+ ret = amdgpu_parse_clk_voltage_dep_table( -+ &adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk, -+ dep_table); -+ if (ret) { -+ kfree(adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk.entries); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+void amdgpu_free_extended_power_table(struct amdgpu_device *adev) -+{ -+ struct amdgpu_dpm_dynamic_state *dyn_state = &adev->pm.dpm.dyn_state; -+ -+ kfree(dyn_state->vddc_dependency_on_sclk.entries); -+ kfree(dyn_state->vddci_dependency_on_mclk.entries); -+ kfree(dyn_state->vddc_dependency_on_mclk.entries); -+ kfree(dyn_state->mvdd_dependency_on_mclk.entries); -+ kfree(dyn_state->cac_leakage_table.entries); -+ kfree(dyn_state->phase_shedding_limits_table.entries); -+ kfree(dyn_state->ppm_table); -+ kfree(dyn_state->cac_tdp_table); -+ kfree(dyn_state->vce_clock_voltage_dependency_table.entries); -+ kfree(dyn_state->uvd_clock_voltage_dependency_table.entries); -+ kfree(dyn_state->samu_clock_voltage_dependency_table.entries); -+ kfree(dyn_state->acp_clock_voltage_dependency_table.entries); -+ kfree(dyn_state->vddgfx_dependency_on_sclk.entries); -+} -+ -+static const char *pp_lib_thermal_controller_names[] = { -+ "NONE", -+ "lm63", -+ "adm1032", -+ "adm1030", -+ "max6649", -+ "lm64", -+ "f75375", -+ "RV6xx", -+ "RV770", -+ "adt7473", -+ "NONE", -+ "External GPIO", -+ "Evergreen", -+ "emc2103", -+ "Sumo", -+ "Northern Islands", -+ "Southern Islands", -+ "lm96163", -+ "Sea Islands", -+ "Kaveri/Kabini", -+}; -+ -+void amdgpu_add_thermal_controller(struct amdgpu_device *adev) -+{ -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ ATOM_PPLIB_POWERPLAYTABLE *power_table; -+ int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); -+ ATOM_PPLIB_THERMALCONTROLLER *controller; -+ struct amdgpu_i2c_bus_rec i2c_bus; -+ u16 data_offset; -+ u8 frev, crev; -+ -+ if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, -+ &frev, &crev, &data_offset)) -+ return; -+ power_table = (ATOM_PPLIB_POWERPLAYTABLE *) -+ (mode_info->atom_context->bios + data_offset); -+ controller = &power_table->sThermalController; -+ -+ /* add the i2c bus for thermal/fan chip */ -+ if (controller->ucType > 0) { -+ if (controller->ucFanParameters & ATOM_PP_FANPARAMETERS_NOFAN) -+ adev->pm.no_fan = true; -+ adev->pm.fan_pulses_per_revolution = -+ controller->ucFanParameters & ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK; -+ if (adev->pm.fan_pulses_per_revolution) { -+ adev->pm.fan_min_rpm = controller->ucFanMinRPM; -+ adev->pm.fan_max_rpm = controller->ucFanMaxRPM; -+ } -+ if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV6xx) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_RV6XX; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_RV770) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_RV770; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EVERGREEN) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_EVERGREEN; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SUMO) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_SUMO; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_NISLANDS) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_NI; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SISLANDS) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_SI; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_CISLANDS) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_CI; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_KAVERI) { -+ DRM_INFO("Internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_KV; -+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) { -+ DRM_INFO("External GPIO thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL_GPIO; -+ } else if (controller->ucType == -+ ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL) { -+ DRM_INFO("ADT7473 with internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_ADT7473_WITH_INTERNAL; -+ } else if (controller->ucType == -+ ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL) { -+ DRM_INFO("EMC2103 with internal thermal controller %s fan control\n", -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_EMC2103_WITH_INTERNAL; -+ } else if (controller->ucType < ARRAY_SIZE(pp_lib_thermal_controller_names)) { -+ DRM_INFO("Possible %s thermal controller at 0x%02x %s fan control\n", -+ pp_lib_thermal_controller_names[controller->ucType], -+ controller->ucI2cAddress >> 1, -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ adev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL; -+ i2c_bus = amdgpu_atombios_lookup_i2c_gpio(adev, controller->ucI2cLine); -+ adev->pm.i2c_bus = amdgpu_i2c_lookup(adev, &i2c_bus); -+ if (adev->pm.i2c_bus) { -+ struct i2c_board_info info = { }; -+ const char *name = pp_lib_thermal_controller_names[controller->ucType]; -+ info.addr = controller->ucI2cAddress >> 1; -+ strlcpy(info.type, name, sizeof(info.type)); -+ i2c_new_device(&adev->pm.i2c_bus->adapter, &info); -+ } -+ } else { -+ DRM_INFO("Unknown thermal controller type %d at 0x%02x %s fan control\n", -+ controller->ucType, -+ controller->ucI2cAddress >> 1, -+ (controller->ucFanParameters & -+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); -+ } -+ } -+} -+ -+enum amdgpu_pcie_gen amdgpu_get_pcie_gen_support(struct amdgpu_device *adev, -+ u32 sys_mask, -+ enum amdgpu_pcie_gen asic_gen, -+ enum amdgpu_pcie_gen default_gen) -+{ -+ switch (asic_gen) { -+ case AMDGPU_PCIE_GEN1: -+ return AMDGPU_PCIE_GEN1; -+ case AMDGPU_PCIE_GEN2: -+ return AMDGPU_PCIE_GEN2; -+ case AMDGPU_PCIE_GEN3: -+ return AMDGPU_PCIE_GEN3; -+ default: -+ if ((sys_mask & DRM_PCIE_SPEED_80) && (default_gen == AMDGPU_PCIE_GEN3)) -+ return AMDGPU_PCIE_GEN3; -+ else if ((sys_mask & DRM_PCIE_SPEED_50) && (default_gen == AMDGPU_PCIE_GEN2)) -+ return AMDGPU_PCIE_GEN2; -+ else -+ return AMDGPU_PCIE_GEN1; -+ } -+ return AMDGPU_PCIE_GEN1; -+} -+ -+u16 amdgpu_get_pcie_lane_support(struct amdgpu_device *adev, -+ u16 asic_lanes, -+ u16 default_lanes) -+{ -+ switch (asic_lanes) { -+ case 0: -+ default: -+ return default_lanes; -+ case 1: -+ return 1; -+ case 2: -+ return 2; -+ case 4: -+ return 4; -+ case 8: -+ return 8; -+ case 12: -+ return 12; -+ case 16: -+ return 16; -+ } -+} -+ -+u8 amdgpu_encode_pci_lane_width(u32 lanes) -+{ -+ u8 encoded_lanes[] = { 0, 1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6 }; -+ -+ if (lanes > 16) -+ return 0; -+ -+ return encoded_lanes[lanes]; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h -new file mode 100644 -index 0000000..3738a96 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h -@@ -0,0 +1,85 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+#ifndef __AMDGPU_DPM_H__ -+#define __AMDGPU_DPM_H__ -+ -+#define R600_SSTU_DFLT 0 -+#define R600_SST_DFLT 0x00C8 -+ -+/* XXX are these ok? */ -+#define R600_TEMP_RANGE_MIN (90 * 1000) -+#define R600_TEMP_RANGE_MAX (120 * 1000) -+ -+#define FDO_PWM_MODE_STATIC 1 -+#define FDO_PWM_MODE_STATIC_RPM 5 -+ -+enum amdgpu_td { -+ AMDGPU_TD_AUTO, -+ AMDGPU_TD_UP, -+ AMDGPU_TD_DOWN, -+}; -+ -+enum amdgpu_display_watermark { -+ AMDGPU_DISPLAY_WATERMARK_LOW = 0, -+ AMDGPU_DISPLAY_WATERMARK_HIGH = 1, -+}; -+ -+enum amdgpu_display_gap -+{ -+ AMDGPU_PM_DISPLAY_GAP_VBLANK_OR_WM = 0, -+ AMDGPU_PM_DISPLAY_GAP_VBLANK = 1, -+ AMDGPU_PM_DISPLAY_GAP_WATERMARK = 2, -+ AMDGPU_PM_DISPLAY_GAP_IGNORE = 3, -+}; -+ -+void amdgpu_dpm_print_class_info(u32 class, u32 class2); -+void amdgpu_dpm_print_cap_info(u32 caps); -+void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev, -+ struct amdgpu_ps *rps); -+u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev); -+u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev); -+bool amdgpu_is_uvd_state(u32 class, u32 class2); -+void amdgpu_calculate_u_and_p(u32 i, u32 r_c, u32 p_b, -+ u32 *p, u32 *u); -+int amdgpu_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th); -+ -+bool amdgpu_is_internal_thermal_sensor(enum amdgpu_int_thermal_type sensor); -+ -+int amdgpu_get_platform_caps(struct amdgpu_device *adev); -+ -+int amdgpu_parse_extended_power_table(struct amdgpu_device *adev); -+void amdgpu_free_extended_power_table(struct amdgpu_device *adev); -+ -+void amdgpu_add_thermal_controller(struct amdgpu_device *adev); -+ -+enum amdgpu_pcie_gen amdgpu_get_pcie_gen_support(struct amdgpu_device *adev, -+ u32 sys_mask, -+ enum amdgpu_pcie_gen asic_gen, -+ enum amdgpu_pcie_gen default_gen); -+ -+u16 amdgpu_get_pcie_lane_support(struct amdgpu_device *adev, -+ u16 asic_lanes, -+ u16 default_lanes); -+u8 amdgpu_encode_pci_lane_width(u32 lanes); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c -new file mode 100644 -index 0000000..d1af448 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c -@@ -0,0 +1,439 @@ -+/** -+ * \file amdgpu_drv.c -+ * AMD Amdgpu driver -+ * -+ * \author Gareth Hughes <gareth@valinux.com> -+ */ -+ -+/* -+ * 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. -+ */ -+ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include <drm/drm_gem.h> -+#include "amdgpu_drv.h" -+ -+#include <drm/drm_pciids.h> -+#include <linux/console.h> -+#include <linux/module.h> -+#include <linux/pm_runtime.h> -+#include <linux/vga_switcheroo.h> -+#include "drm_crtc_helper.h" -+ -+#include "amdgpu.h" -+#include "amdgpu_irq.h" -+ -+/* -+ * KMS wrapper. -+ * - 3.0.0 - initial driver -+ */ -+#define KMS_DRIVER_MAJOR 3 -+#define KMS_DRIVER_MINOR 0 -+#define KMS_DRIVER_PATCHLEVEL 0 -+ -+int amdgpu_vram_limit = 0; -+int amdgpu_gart_size = -1; /* auto */ -+int amdgpu_benchmarking = 0; -+int amdgpu_testing = 0; -+int amdgpu_audio = -1; -+int amdgpu_disp_priority = 0; -+int amdgpu_hw_i2c = 0; -+int amdgpu_pcie_gen2 = -1; -+int amdgpu_msi = -1; -+int amdgpu_lockup_timeout = 10000; -+int amdgpu_dpm = -1; -+int amdgpu_smc_load_fw = 1; -+int amdgpu_aspm = -1; -+int amdgpu_runtime_pm = -1; -+int amdgpu_hard_reset = 0; -+unsigned amdgpu_ip_block_mask = 0xffffffff; -+int amdgpu_bapm = -1; -+int amdgpu_deep_color = 0; -+int amdgpu_vm_size = 8; -+int amdgpu_vm_block_size = -1; -+int amdgpu_exp_hw_support = 0; -+ -+MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes"); -+module_param_named(vramlimit, amdgpu_vram_limit, int, 0600); -+ -+MODULE_PARM_DESC(gartsize, "Size of PCIE/IGP gart to setup in megabytes (32, 64, etc., -1 = auto)"); -+module_param_named(gartsize, amdgpu_gart_size, int, 0600); -+ -+MODULE_PARM_DESC(benchmark, "Run benchmark"); -+module_param_named(benchmark, amdgpu_benchmarking, int, 0444); -+ -+MODULE_PARM_DESC(test, "Run tests"); -+module_param_named(test, amdgpu_testing, int, 0444); -+ -+MODULE_PARM_DESC(audio, "Audio enable (-1 = auto, 0 = disable, 1 = enable)"); -+module_param_named(audio, amdgpu_audio, int, 0444); -+ -+MODULE_PARM_DESC(disp_priority, "Display Priority (0 = auto, 1 = normal, 2 = high)"); -+module_param_named(disp_priority, amdgpu_disp_priority, int, 0444); -+ -+MODULE_PARM_DESC(hw_i2c, "hw i2c engine enable (0 = disable)"); -+module_param_named(hw_i2c, amdgpu_hw_i2c, int, 0444); -+ -+MODULE_PARM_DESC(pcie_gen2, "PCIE Gen2 mode (-1 = auto, 0 = disable, 1 = enable)"); -+module_param_named(pcie_gen2, amdgpu_pcie_gen2, int, 0444); -+ -+MODULE_PARM_DESC(msi, "MSI support (1 = enable, 0 = disable, -1 = auto)"); -+module_param_named(msi, amdgpu_msi, int, 0444); -+ -+MODULE_PARM_DESC(lockup_timeout, "GPU lockup timeout in ms (defaul 10000 = 10 seconds, 0 = disable)"); -+module_param_named(lockup_timeout, amdgpu_lockup_timeout, int, 0444); -+ -+MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)"); -+module_param_named(dpm, amdgpu_dpm, int, 0444); -+ -+MODULE_PARM_DESC(smc_load_fw, "SMC firmware loading(1 = enable, 0 = disable)"); -+module_param_named(smc_load_fw, amdgpu_smc_load_fw, int, 0444); -+ -+MODULE_PARM_DESC(aspm, "ASPM support (1 = enable, 0 = disable, -1 = auto)"); -+module_param_named(aspm, amdgpu_aspm, int, 0444); -+ -+MODULE_PARM_DESC(runpm, "PX runtime pm (1 = force enable, 0 = disable, -1 = PX only default)"); -+module_param_named(runpm, amdgpu_runtime_pm, int, 0444); -+ -+MODULE_PARM_DESC(hard_reset, "PCI config reset (1 = force enable, 0 = disable (default))"); -+module_param_named(hard_reset, amdgpu_hard_reset, int, 0444); -+ -+MODULE_PARM_DESC(ip_block_mask, "IP Block Mask (all blocks enabled (default))"); -+module_param_named(ip_block_mask, amdgpu_ip_block_mask, uint, 0444); -+ -+MODULE_PARM_DESC(bapm, "BAPM support (1 = enable, 0 = disable, -1 = auto)"); -+module_param_named(bapm, amdgpu_bapm, int, 0444); -+ -+MODULE_PARM_DESC(deep_color, "Deep Color support (1 = enable, 0 = disable (default))"); -+module_param_named(deep_color, amdgpu_deep_color, int, 0444); -+ -+MODULE_PARM_DESC(vm_size, "VM address space size in gigabytes (default 4GB)"); -+module_param_named(vm_size, amdgpu_vm_size, int, 0444); -+ -+MODULE_PARM_DESC(vm_block_size, "VM page table size in bits (default depending on vm_size)"); -+module_param_named(vm_block_size, amdgpu_vm_block_size, int, 0444); -+ -+MODULE_PARM_DESC(exp_hw_support, "experimental hw support (1 = enable, 0 = disable (default))"); -+module_param_named(exp_hw_support, amdgpu_exp_hw_support, int, 0444); -+ -+static struct pci_device_id pciidlist[] = { -+ -+ {0, 0, 0} -+}; -+ -+MODULE_DEVICE_TABLE(pci, pciidlist); -+ -+static struct drm_driver kms_driver; -+ -+static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev) -+{ -+ struct apertures_struct *ap; -+ bool primary = false; -+ -+ ap = alloc_apertures(1); -+ if (!ap) -+ return -ENOMEM; -+ -+ ap->ranges[0].base = pci_resource_start(pdev, 0); -+ ap->ranges[0].size = pci_resource_len(pdev, 0); -+ -+#ifdef CONFIG_X86 -+ primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -+#endif -+ remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary); -+ kfree(ap); -+ -+ return 0; -+} -+ -+static int amdgpu_pci_probe(struct pci_dev *pdev, -+ const struct pci_device_id *ent) -+{ -+ unsigned long flags = ent->driver_data; -+ int ret; -+ -+ if ((flags & AMDGPU_EXP_HW_SUPPORT) && !amdgpu_exp_hw_support) { -+ DRM_INFO("This hardware requires experimental hardware support.\n" -+ "See modparam exp_hw_support\n"); -+ return -ENODEV; -+ } -+ -+ /* Get rid of things like offb */ -+ ret = amdgpu_kick_out_firmware_fb(pdev); -+ if (ret) -+ return ret; -+ -+ return drm_get_pci_dev(pdev, ent, &kms_driver); -+} -+ -+static void -+amdgpu_pci_remove(struct pci_dev *pdev) -+{ -+ struct drm_device *dev = pci_get_drvdata(pdev); -+ -+ drm_put_dev(dev); -+} -+ -+static int amdgpu_pmops_suspend(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ return amdgpu_suspend_kms(drm_dev, true, true); -+} -+ -+static int amdgpu_pmops_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ return amdgpu_resume_kms(drm_dev, true, true); -+} -+ -+static int amdgpu_pmops_freeze(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ return amdgpu_suspend_kms(drm_dev, false, true); -+} -+ -+static int amdgpu_pmops_thaw(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ return amdgpu_resume_kms(drm_dev, false, true); -+} -+ -+static int amdgpu_pmops_runtime_suspend(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ int ret; -+ -+ if (!amdgpu_device_is_px(drm_dev)) { -+ pm_runtime_forbid(dev); -+ return -EBUSY; -+ } -+ -+ drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; -+ drm_kms_helper_poll_disable(drm_dev); -+ vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); -+ -+ ret = amdgpu_suspend_kms(drm_dev, false, false); -+ pci_save_state(pdev); -+ pci_disable_device(pdev); -+ pci_ignore_hotplug(pdev); -+ pci_set_power_state(pdev, PCI_D3cold); -+ drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; -+ -+ return 0; -+} -+ -+static int amdgpu_pmops_runtime_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ int ret; -+ -+ if (!amdgpu_device_is_px(drm_dev)) -+ return -EINVAL; -+ -+ drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; -+ -+ pci_set_power_state(pdev, PCI_D0); -+ pci_restore_state(pdev); -+ ret = pci_enable_device(pdev); -+ if (ret) -+ return ret; -+ pci_set_master(pdev); -+ -+ ret = amdgpu_resume_kms(drm_dev, false, false); -+ drm_kms_helper_poll_enable(drm_dev); -+ vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); -+ drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; -+ return 0; -+} -+ -+static int amdgpu_pmops_runtime_idle(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct drm_device *drm_dev = pci_get_drvdata(pdev); -+ struct drm_crtc *crtc; -+ -+ if (!amdgpu_device_is_px(drm_dev)) { -+ pm_runtime_forbid(dev); -+ return -EBUSY; -+ } -+ -+ list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { -+ if (crtc->enabled) { -+ DRM_DEBUG_DRIVER("failing to power off - crtc active\n"); -+ return -EBUSY; -+ } -+ } -+ -+ pm_runtime_mark_last_busy(dev); -+ pm_runtime_autosuspend(dev); -+ /* we don't want the main rpm_idle to call suspend - we want to autosuspend */ -+ return 1; -+} -+ -+long amdgpu_drm_ioctl(struct file *filp, -+ unsigned int cmd, unsigned long arg) -+{ -+ struct drm_file *file_priv = filp->private_data; -+ struct drm_device *dev; -+ long ret; -+ dev = file_priv->minor->dev; -+ ret = pm_runtime_get_sync(dev->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = drm_ioctl(filp, cmd, arg); -+ -+ pm_runtime_mark_last_busy(dev->dev); -+ pm_runtime_put_autosuspend(dev->dev); -+ return ret; -+} -+ -+static const struct dev_pm_ops amdgpu_pm_ops = { -+ .suspend = amdgpu_pmops_suspend, -+ .resume = amdgpu_pmops_resume, -+ .freeze = amdgpu_pmops_freeze, -+ .thaw = amdgpu_pmops_thaw, -+ .poweroff = amdgpu_pmops_freeze, -+ .restore = amdgpu_pmops_resume, -+ .runtime_suspend = amdgpu_pmops_runtime_suspend, -+ .runtime_resume = amdgpu_pmops_runtime_resume, -+ .runtime_idle = amdgpu_pmops_runtime_idle, -+}; -+ -+static const struct file_operations amdgpu_driver_kms_fops = { -+ .owner = THIS_MODULE, -+ .open = drm_open, -+ .release = drm_release, -+ .unlocked_ioctl = amdgpu_drm_ioctl, -+ .mmap = amdgpu_mmap, -+ .poll = drm_poll, -+ .read = drm_read, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = amdgpu_kms_compat_ioctl, -+#endif -+}; -+ -+static struct drm_driver kms_driver = { -+ .driver_features = -+ DRIVER_USE_AGP | -+ DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | -+ DRIVER_PRIME | DRIVER_RENDER, -+ .dev_priv_size = 0, -+ .load = amdgpu_driver_load_kms, -+ .open = amdgpu_driver_open_kms, -+ .preclose = amdgpu_driver_preclose_kms, -+ .postclose = amdgpu_driver_postclose_kms, -+ .lastclose = amdgpu_driver_lastclose_kms, -+ .set_busid = drm_pci_set_busid, -+ .unload = amdgpu_driver_unload_kms, -+ .get_vblank_counter = amdgpu_get_vblank_counter_kms, -+ .enable_vblank = amdgpu_enable_vblank_kms, -+ .disable_vblank = amdgpu_disable_vblank_kms, -+ .get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms, -+ .get_scanout_position = amdgpu_get_crtc_scanoutpos, -+#if defined(CONFIG_DEBUG_FS) -+ .debugfs_init = amdgpu_debugfs_init, -+ .debugfs_cleanup = amdgpu_debugfs_cleanup, -+#endif -+ .irq_preinstall = amdgpu_irq_preinstall, -+ .irq_postinstall = amdgpu_irq_postinstall, -+ .irq_uninstall = amdgpu_irq_uninstall, -+ .irq_handler = amdgpu_irq_handler, -+ .ioctls = amdgpu_ioctls_kms, -+ .gem_free_object = amdgpu_gem_object_free, -+ .gem_open_object = amdgpu_gem_object_open, -+ .gem_close_object = amdgpu_gem_object_close, -+ .dumb_create = amdgpu_mode_dumb_create, -+ .dumb_map_offset = amdgpu_mode_dumb_mmap, -+ .dumb_destroy = drm_gem_dumb_destroy, -+ .fops = &amdgpu_driver_kms_fops, -+ -+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, -+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle, -+ .gem_prime_export = amdgpu_gem_prime_export, -+ .gem_prime_import = drm_gem_prime_import, -+ .gem_prime_pin = amdgpu_gem_prime_pin, -+ .gem_prime_unpin = amdgpu_gem_prime_unpin, -+ .gem_prime_res_obj = amdgpu_gem_prime_res_obj, -+ .gem_prime_get_sg_table = amdgpu_gem_prime_get_sg_table, -+ .gem_prime_import_sg_table = amdgpu_gem_prime_import_sg_table, -+ .gem_prime_vmap = amdgpu_gem_prime_vmap, -+ .gem_prime_vunmap = amdgpu_gem_prime_vunmap, -+ -+ .name = DRIVER_NAME, -+ .desc = DRIVER_DESC, -+ .date = DRIVER_DATE, -+ .major = KMS_DRIVER_MAJOR, -+ .minor = KMS_DRIVER_MINOR, -+ .patchlevel = KMS_DRIVER_PATCHLEVEL, -+}; -+ -+static struct drm_driver *driver; -+static struct pci_driver *pdriver; -+ -+static struct pci_driver amdgpu_kms_pci_driver = { -+ .name = DRIVER_NAME, -+ .id_table = pciidlist, -+ .probe = amdgpu_pci_probe, -+ .remove = amdgpu_pci_remove, -+ .driver.pm = &amdgpu_pm_ops, -+}; -+ -+static int __init amdgpu_init(void) -+{ -+#ifdef CONFIG_VGA_CONSOLE -+ if (vgacon_text_force()) { -+ DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); -+ return -EINVAL; -+ } -+#endif -+ DRM_INFO("amdgpu kernel modesetting enabled.\n"); -+ driver = &kms_driver; -+ pdriver = &amdgpu_kms_pci_driver; -+ driver->driver_features |= DRIVER_MODESET; -+ driver->num_ioctls = amdgpu_max_kms_ioctl; -+ amdgpu_register_atpx_handler(); -+ -+ /* let modprobe override vga console setting */ -+ return drm_pci_init(driver, pdriver); -+} -+ -+static void __exit amdgpu_exit(void) -+{ -+ drm_pci_exit(driver, pdriver); -+ amdgpu_unregister_atpx_handler(); -+} -+ -+module_init(amdgpu_init); -+module_exit(amdgpu_exit); -+ -+MODULE_AUTHOR(DRIVER_AUTHOR); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL and additional rights"); -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h -new file mode 100644 -index 0000000..cceeb33 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h -@@ -0,0 +1,48 @@ -+/* amdgpu_drv.h -- Private header for amdgpu driver -*- linux-c -*- -+ * -+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. -+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California. -+ * All rights reserved. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * 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 -+ * PRECISION INSIGHT 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 __AMDGPU_DRV_H__ -+#define __AMDGPU_DRV_H__ -+ -+#include <linux/firmware.h> -+#include <linux/platform_device.h> -+ -+#include "amdgpu_family.h" -+ -+/* General customization: -+ */ -+ -+#define DRIVER_AUTHOR "AMD linux driver team" -+ -+#define DRIVER_NAME "amdgpu" -+#define DRIVER_DESC "AMD GPU" -+#define DRIVER_DATE "20150101" -+ -+long amdgpu_drm_ioctl(struct file *filp, -+ unsigned int cmd, unsigned long arg); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c -new file mode 100644 -index 0000000..94138ab ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_encoders.c -@@ -0,0 +1,245 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <drm/drmP.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_connectors.h" -+#include "atom.h" -+#include "atombios_encoders.h" -+ -+void -+amdgpu_link_encoder_connector(struct drm_device *dev) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ struct drm_encoder *encoder; -+ struct amdgpu_encoder *amdgpu_encoder; -+ -+ /* walk the list and link encoders to connectors */ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ amdgpu_connector = to_amdgpu_connector(connector); -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ amdgpu_encoder = to_amdgpu_encoder(encoder); -+ if (amdgpu_encoder->devices & amdgpu_connector->devices) { -+ drm_mode_connector_attach_encoder(connector, encoder); -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { -+ amdgpu_atombios_encoder_init_backlight(amdgpu_encoder, connector); -+ adev->mode_info.bl_encoder = amdgpu_encoder; -+ } -+ } -+ } -+ } -+} -+ -+void amdgpu_encoder_set_active_device(struct drm_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_connector *connector; -+ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ if (connector->encoder == encoder) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ amdgpu_encoder->active_device = amdgpu_encoder->devices & amdgpu_connector->devices; -+ DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n", -+ amdgpu_encoder->active_device, amdgpu_encoder->devices, -+ amdgpu_connector->devices, encoder->encoder_type); -+ } -+ } -+} -+ -+struct drm_connector * -+amdgpu_get_connector_for_encoder(struct drm_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ amdgpu_connector = to_amdgpu_connector(connector); -+ if (amdgpu_encoder->active_device & amdgpu_connector->devices) -+ return connector; -+ } -+ return NULL; -+} -+ -+struct drm_connector * -+amdgpu_get_connector_for_encoder_init(struct drm_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ -+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -+ amdgpu_connector = to_amdgpu_connector(connector); -+ if (amdgpu_encoder->devices & amdgpu_connector->devices) -+ return connector; -+ } -+ return NULL; -+} -+ -+struct drm_encoder *amdgpu_get_external_encoder(struct drm_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_encoder *other_encoder; -+ struct amdgpu_encoder *other_amdgpu_encoder; -+ -+ if (amdgpu_encoder->is_ext_encoder) -+ return NULL; -+ -+ list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) { -+ if (other_encoder == encoder) -+ continue; -+ other_amdgpu_encoder = to_amdgpu_encoder(other_encoder); -+ if (other_amdgpu_encoder->is_ext_encoder && -+ (amdgpu_encoder->devices & other_amdgpu_encoder->devices)) -+ return other_encoder; -+ } -+ return NULL; -+} -+ -+u16 amdgpu_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder) -+{ -+ struct drm_encoder *other_encoder = amdgpu_get_external_encoder(encoder); -+ -+ if (other_encoder) { -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(other_encoder); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_TRAVIS: -+ case ENCODER_OBJECT_ID_NUTMEG: -+ return amdgpu_encoder->encoder_id; -+ default: -+ return ENCODER_OBJECT_ID_NONE; -+ } -+ } -+ return ENCODER_OBJECT_ID_NONE; -+} -+ -+void amdgpu_panel_mode_fixup(struct drm_encoder *encoder, -+ struct drm_display_mode *adjusted_mode) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; -+ unsigned hblank = native_mode->htotal - native_mode->hdisplay; -+ unsigned vblank = native_mode->vtotal - native_mode->vdisplay; -+ unsigned hover = native_mode->hsync_start - native_mode->hdisplay; -+ unsigned vover = native_mode->vsync_start - native_mode->vdisplay; -+ unsigned hsync_width = native_mode->hsync_end - native_mode->hsync_start; -+ unsigned vsync_width = native_mode->vsync_end - native_mode->vsync_start; -+ -+ adjusted_mode->clock = native_mode->clock; -+ adjusted_mode->flags = native_mode->flags; -+ -+ adjusted_mode->hdisplay = native_mode->hdisplay; -+ adjusted_mode->vdisplay = native_mode->vdisplay; -+ -+ adjusted_mode->htotal = native_mode->hdisplay + hblank; -+ adjusted_mode->hsync_start = native_mode->hdisplay + hover; -+ adjusted_mode->hsync_end = adjusted_mode->hsync_start + hsync_width; -+ -+ adjusted_mode->vtotal = native_mode->vdisplay + vblank; -+ adjusted_mode->vsync_start = native_mode->vdisplay + vover; -+ adjusted_mode->vsync_end = adjusted_mode->vsync_start + vsync_width; -+ -+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); -+ -+ adjusted_mode->crtc_hdisplay = native_mode->hdisplay; -+ adjusted_mode->crtc_vdisplay = native_mode->vdisplay; -+ -+ adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + hblank; -+ adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + hover; -+ adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + hsync_width; -+ -+ adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + vblank; -+ adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + vover; -+ adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + vsync_width; -+ -+} -+ -+bool amdgpu_dig_monitor_is_duallink(struct drm_encoder *encoder, -+ u32 pixel_clock) -+{ -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ struct amdgpu_connector_atom_dig *dig_connector; -+ -+ connector = amdgpu_get_connector_for_encoder(encoder); -+ /* if we don't have an active device yet, just use one of -+ * the connectors tied to the encoder. -+ */ -+ if (!connector) -+ connector = amdgpu_get_connector_for_encoder_init(encoder); -+ amdgpu_connector = to_amdgpu_connector(connector); -+ -+ switch (connector->connector_type) { -+ case DRM_MODE_CONNECTOR_DVII: -+ case DRM_MODE_CONNECTOR_HDMIB: -+ if (amdgpu_connector->use_digital) { -+ /* HDMI 1.3 supports up to 340 Mhz over single link */ -+ if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ if (pixel_clock > 340000) -+ return true; -+ else -+ return false; -+ } else { -+ if (pixel_clock > 165000) -+ return true; -+ else -+ return false; -+ } -+ } else -+ return false; -+ case DRM_MODE_CONNECTOR_DVID: -+ case DRM_MODE_CONNECTOR_HDMIA: -+ case DRM_MODE_CONNECTOR_DisplayPort: -+ dig_connector = amdgpu_connector->con_priv; -+ if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || -+ (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) -+ return false; -+ else { -+ /* HDMI 1.3 supports up to 340 Mhz over single link */ -+ if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector))) { -+ if (pixel_clock > 340000) -+ return true; -+ else -+ return false; -+ } else { -+ if (pixel_clock > 165000) -+ return true; -+ else -+ return false; -+ } -+ } -+ default: -+ return false; -+ } -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c -new file mode 100644 -index 0000000..2b1735d ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c -@@ -0,0 +1,432 @@ -+/* -+ * Copyright © 2007 David Airlie -+ * -+ * 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: -+ * David Airlie -+ */ -+#include <linux/module.h> -+#include <linux/slab.h> -+#include <linux/fb.h> -+ -+#include <drm/drmP.h> -+#include <drm/drm_crtc.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+#include <drm/drm_fb_helper.h> -+ -+#include <linux/vga_switcheroo.h> -+ -+/* object hierarchy - -+ this contains a helper + a amdgpu fb -+ the helper contains a pointer to amdgpu framebuffer baseclass. -+*/ -+struct amdgpu_fbdev { -+ struct drm_fb_helper helper; -+ struct amdgpu_framebuffer rfb; -+ struct list_head fbdev_list; -+ struct amdgpu_device *adev; -+}; -+ -+static struct fb_ops amdgpufb_ops = { -+ .owner = THIS_MODULE, -+ .fb_check_var = drm_fb_helper_check_var, -+ .fb_set_par = drm_fb_helper_set_par, -+ .fb_fillrect = cfb_fillrect, -+ .fb_copyarea = cfb_copyarea, -+ .fb_imageblit = cfb_imageblit, -+ .fb_pan_display = drm_fb_helper_pan_display, -+ .fb_blank = drm_fb_helper_blank, -+ .fb_setcmap = drm_fb_helper_setcmap, -+ .fb_debug_enter = drm_fb_helper_debug_enter, -+ .fb_debug_leave = drm_fb_helper_debug_leave, -+}; -+ -+ -+int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tiled) -+{ -+ int aligned = width; -+ int pitch_mask = 0; -+ -+ switch (bpp / 8) { -+ case 1: -+ pitch_mask = 255; -+ break; -+ case 2: -+ pitch_mask = 127; -+ break; -+ case 3: -+ case 4: -+ pitch_mask = 63; -+ break; -+ } -+ -+ aligned += pitch_mask; -+ aligned &= ~pitch_mask; -+ return aligned; -+} -+ -+static void amdgpufb_destroy_pinned_object(struct drm_gem_object *gobj) -+{ -+ struct amdgpu_bo *rbo = gem_to_amdgpu_bo(gobj); -+ int ret; -+ -+ ret = amdgpu_bo_reserve(rbo, false); -+ if (likely(ret == 0)) { -+ amdgpu_bo_kunmap(rbo); -+ amdgpu_bo_unpin(rbo); -+ amdgpu_bo_unreserve(rbo); -+ } -+ drm_gem_object_unreference_unlocked(gobj); -+} -+ -+static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, -+ struct drm_mode_fb_cmd2 *mode_cmd, -+ struct drm_gem_object **gobj_p) -+{ -+ struct amdgpu_device *adev = rfbdev->adev; -+ struct drm_gem_object *gobj = NULL; -+ struct amdgpu_bo *rbo = NULL; -+ bool fb_tiled = false; /* useful for testing */ -+ u32 tiling_flags = 0; -+ int ret; -+ int aligned_size, size; -+ int height = mode_cmd->height; -+ u32 bpp, depth; -+ -+ drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); -+ -+ /* need to align pitch with crtc limits */ -+ mode_cmd->pitches[0] = amdgpu_align_pitch(adev, mode_cmd->width, bpp, -+ fb_tiled) * ((bpp + 1) / 8); -+ -+ height = ALIGN(mode_cmd->height, 8); -+ size = mode_cmd->pitches[0] * height; -+ aligned_size = ALIGN(size, PAGE_SIZE); -+ ret = amdgpu_gem_object_create(adev, aligned_size, 0, -+ AMDGPU_GEM_DOMAIN_VRAM, -+ 0, true, -+ &gobj); -+ if (ret) { -+ printk(KERN_ERR "failed to allocate framebuffer (%d)\n", -+ aligned_size); -+ return -ENOMEM; -+ } -+ rbo = gem_to_amdgpu_bo(gobj); -+ -+ if (fb_tiled) -+ tiling_flags = AMDGPU_TILING_MACRO; -+ -+#ifdef __BIG_ENDIAN -+ switch (bpp) { -+ case 32: -+ tiling_flags |= AMDGPU_TILING_SWAP_32BIT; -+ break; -+ case 16: -+ tiling_flags |= AMDGPU_TILING_SWAP_16BIT; -+ default: -+ break; -+ } -+#endif -+ -+ ret = amdgpu_bo_reserve(rbo, false); -+ if (unlikely(ret != 0)) -+ goto out_unref; -+ -+ if (tiling_flags) { -+ ret = amdgpu_bo_set_tiling_flags(rbo, -+ tiling_flags | AMDGPU_TILING_SURFACE); -+ if (ret) -+ dev_err(adev->dev, "FB failed to set tiling flags\n"); -+ } -+ -+ -+ ret = amdgpu_bo_pin_restricted(rbo, AMDGPU_GEM_DOMAIN_VRAM, 0, NULL); -+ if (ret) { -+ amdgpu_bo_unreserve(rbo); -+ goto out_unref; -+ } -+ ret = amdgpu_bo_kmap(rbo, NULL); -+ amdgpu_bo_unreserve(rbo); -+ if (ret) { -+ goto out_unref; -+ } -+ -+ *gobj_p = gobj; -+ return 0; -+out_unref: -+ amdgpufb_destroy_pinned_object(gobj); -+ *gobj_p = NULL; -+ return ret; -+} -+ -+static int amdgpufb_create(struct drm_fb_helper *helper, -+ struct drm_fb_helper_surface_size *sizes) -+{ -+ struct amdgpu_fbdev *rfbdev = (struct amdgpu_fbdev *)helper; -+ struct amdgpu_device *adev = rfbdev->adev; -+ struct fb_info *info; -+ struct drm_framebuffer *fb = NULL; -+ struct drm_mode_fb_cmd2 mode_cmd; -+ struct drm_gem_object *gobj = NULL; -+ struct amdgpu_bo *rbo = NULL; -+ struct device *device = &adev->pdev->dev; -+ int ret; -+ unsigned long tmp; -+ -+ mode_cmd.width = sizes->surface_width; -+ mode_cmd.height = sizes->surface_height; -+ -+ if (sizes->surface_bpp == 24) -+ sizes->surface_bpp = 32; -+ -+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, -+ sizes->surface_depth); -+ -+ ret = amdgpufb_create_pinned_object(rfbdev, &mode_cmd, &gobj); -+ if (ret) { -+ DRM_ERROR("failed to create fbcon object %d\n", ret); -+ return ret; -+ } -+ -+ rbo = gem_to_amdgpu_bo(gobj); -+ -+ /* okay we have an object now allocate the framebuffer */ -+ info = framebuffer_alloc(0, device); -+ if (info == NULL) { -+ ret = -ENOMEM; -+ goto out_unref; -+ } -+ -+ info->par = rfbdev; -+ -+ ret = amdgpu_framebuffer_init(adev->ddev, &rfbdev->rfb, &mode_cmd, gobj); -+ if (ret) { -+ DRM_ERROR("failed to initialize framebuffer %d\n", ret); -+ goto out_unref; -+ } -+ -+ fb = &rfbdev->rfb.base; -+ -+ /* setup helper */ -+ rfbdev->helper.fb = fb; -+ rfbdev->helper.fbdev = info; -+ -+ memset_io(rbo->kptr, 0x0, amdgpu_bo_size(rbo)); -+ -+ strcpy(info->fix.id, "amdgpudrmfb"); -+ -+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); -+ -+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; -+ info->fbops = &amdgpufb_ops; -+ -+ tmp = amdgpu_bo_gpu_offset(rbo) - adev->mc.vram_start; -+ info->fix.smem_start = adev->mc.aper_base + tmp; -+ info->fix.smem_len = amdgpu_bo_size(rbo); -+ info->screen_base = rbo->kptr; -+ info->screen_size = amdgpu_bo_size(rbo); -+ -+ drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height); -+ -+ /* setup aperture base/size for vesafb takeover */ -+ info->apertures = alloc_apertures(1); -+ if (!info->apertures) { -+ ret = -ENOMEM; -+ goto out_unref; -+ } -+ info->apertures->ranges[0].base = adev->ddev->mode_config.fb_base; -+ info->apertures->ranges[0].size = adev->mc.aper_size; -+ -+ /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ -+ -+ if (info->screen_base == NULL) { -+ ret = -ENOSPC; -+ goto out_unref; -+ } -+ -+ ret = fb_alloc_cmap(&info->cmap, 256, 0); -+ if (ret) { -+ ret = -ENOMEM; -+ goto out_unref; -+ } -+ -+ DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); -+ DRM_INFO("vram apper at 0x%lX\n", (unsigned long)adev->mc.aper_base); -+ DRM_INFO("size %lu\n", (unsigned long)amdgpu_bo_size(rbo)); -+ DRM_INFO("fb depth is %d\n", fb->depth); -+ DRM_INFO(" pitch is %d\n", fb->pitches[0]); -+ -+ vga_switcheroo_client_fb_set(adev->ddev->pdev, info); -+ return 0; -+ -+out_unref: -+ if (rbo) { -+ -+ } -+ if (fb && ret) { -+ drm_gem_object_unreference(gobj); -+ drm_framebuffer_unregister_private(fb); -+ drm_framebuffer_cleanup(fb); -+ kfree(fb); -+ } -+ return ret; -+} -+ -+void amdgpu_fb_output_poll_changed(struct amdgpu_device *adev) -+{ -+ if (adev->mode_info.rfbdev) -+ drm_fb_helper_hotplug_event(&adev->mode_info.rfbdev->helper); -+} -+ -+static int amdgpu_fbdev_destroy(struct drm_device *dev, struct amdgpu_fbdev *rfbdev) -+{ -+ struct fb_info *info; -+ struct amdgpu_framebuffer *rfb = &rfbdev->rfb; -+ -+ if (rfbdev->helper.fbdev) { -+ info = rfbdev->helper.fbdev; -+ -+ unregister_framebuffer(info); -+ if (info->cmap.len) -+ fb_dealloc_cmap(&info->cmap); -+ framebuffer_release(info); -+ } -+ -+ if (rfb->obj) { -+ amdgpufb_destroy_pinned_object(rfb->obj); -+ rfb->obj = NULL; -+ } -+ drm_fb_helper_fini(&rfbdev->helper); -+ drm_framebuffer_unregister_private(&rfb->base); -+ drm_framebuffer_cleanup(&rfb->base); -+ -+ return 0; -+} -+ -+/** Sets the color ramps on behalf of fbcon */ -+static void amdgpu_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, -+ u16 blue, int regno) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ -+ amdgpu_crtc->lut_r[regno] = red >> 6; -+ amdgpu_crtc->lut_g[regno] = green >> 6; -+ amdgpu_crtc->lut_b[regno] = blue >> 6; -+} -+ -+/** Gets the color ramps on behalf of fbcon */ -+static void amdgpu_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, -+ u16 *blue, int regno) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ -+ *red = amdgpu_crtc->lut_r[regno] << 6; -+ *green = amdgpu_crtc->lut_g[regno] << 6; -+ *blue = amdgpu_crtc->lut_b[regno] << 6; -+} -+ -+static const struct drm_fb_helper_funcs amdgpu_fb_helper_funcs = { -+ .gamma_set = amdgpu_crtc_fb_gamma_set, -+ .gamma_get = amdgpu_crtc_fb_gamma_get, -+ .fb_probe = amdgpufb_create, -+}; -+ -+int amdgpu_fbdev_init(struct amdgpu_device *adev) -+{ -+ struct amdgpu_fbdev *rfbdev; -+ int bpp_sel = 32; -+ int ret; -+ -+ /* don't init fbdev on hw without DCE */ -+ if (!adev->mode_info.mode_config_initialized) -+ return 0; -+ -+ /* select 8 bpp console on low vram cards */ -+ if (adev->mc.real_vram_size <= (32*1024*1024)) -+ bpp_sel = 8; -+ -+ rfbdev = kzalloc(sizeof(struct amdgpu_fbdev), GFP_KERNEL); -+ if (!rfbdev) -+ return -ENOMEM; -+ -+ rfbdev->adev = adev; -+ adev->mode_info.rfbdev = rfbdev; -+ -+ drm_fb_helper_prepare(adev->ddev, &rfbdev->helper, -+ &amdgpu_fb_helper_funcs); -+ -+ ret = drm_fb_helper_init(adev->ddev, &rfbdev->helper, -+ adev->mode_info.num_crtc, -+ AMDGPUFB_CONN_LIMIT); -+ if (ret) { -+ kfree(rfbdev); -+ return ret; -+ } -+ -+ drm_fb_helper_single_add_all_connectors(&rfbdev->helper); -+ -+ /* disable all the possible outputs/crtcs before entering KMS mode */ -+ drm_helper_disable_unused_functions(adev->ddev); -+ -+ drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); -+ return 0; -+} -+ -+void amdgpu_fbdev_fini(struct amdgpu_device *adev) -+{ -+ if (!adev->mode_info.rfbdev) -+ return; -+ -+ amdgpu_fbdev_destroy(adev->ddev, adev->mode_info.rfbdev); -+ kfree(adev->mode_info.rfbdev); -+ adev->mode_info.rfbdev = NULL; -+} -+ -+void amdgpu_fbdev_set_suspend(struct amdgpu_device *adev, int state) -+{ -+ if (adev->mode_info.rfbdev) -+ fb_set_suspend(adev->mode_info.rfbdev->helper.fbdev, state); -+} -+ -+int amdgpu_fbdev_total_size(struct amdgpu_device *adev) -+{ -+ struct amdgpu_bo *robj; -+ int size = 0; -+ -+ if (!adev->mode_info.rfbdev) -+ return 0; -+ -+ robj = gem_to_amdgpu_bo(adev->mode_info.rfbdev->rfb.obj); -+ size += amdgpu_bo_size(robj); -+ return size; -+} -+ -+bool amdgpu_fbdev_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj) -+{ -+ if (!adev->mode_info.rfbdev) -+ return false; -+ if (robj == gem_to_amdgpu_bo(adev->mode_info.rfbdev->rfb.obj)) -+ return true; -+ return false; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c -new file mode 100644 -index 0000000..fc63855e ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c -@@ -0,0 +1,1139 @@ -+/* -+ * Copyright 2009 Jerome Glisse. -+ * All Rights Reserved. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the -+ * "Software"), to deal in the Software without restriction, including -+ * without limitation the rights to use, copy, modify, merge, publish, -+ * distribute, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Jerome Glisse <glisse@freedesktop.org> -+ * Dave Airlie -+ */ -+#include <linux/seq_file.h> -+#include <linux/atomic.h> -+#include <linux/wait.h> -+#include <linux/kref.h> -+#include <linux/slab.h> -+#include <linux/firmware.h> -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include "amdgpu_trace.h" -+ -+/* -+ * Fences -+ * Fences mark an event in the GPUs pipeline and are used -+ * for GPU/CPU synchronization. When the fence is written, -+ * it is expected that all buffers associated with that fence -+ * are no longer in use by the associated ring on the GPU and -+ * that the the relevant GPU caches have been flushed. -+ */ -+ -+/** -+ * amdgpu_fence_write - write a fence value -+ * -+ * @ring: ring the fence is associated with -+ * @seq: sequence number to write -+ * -+ * Writes a fence value to memory (all asics). -+ */ -+static void amdgpu_fence_write(struct amdgpu_ring *ring, u32 seq) -+{ -+ struct amdgpu_fence_driver *drv = &ring->fence_drv; -+ -+ if (drv->cpu_addr) -+ *drv->cpu_addr = cpu_to_le32(seq); -+} -+ -+/** -+ * amdgpu_fence_read - read a fence value -+ * -+ * @ring: ring the fence is associated with -+ * -+ * Reads a fence value from memory (all asics). -+ * Returns the value of the fence read from memory. -+ */ -+static u32 amdgpu_fence_read(struct amdgpu_ring *ring) -+{ -+ struct amdgpu_fence_driver *drv = &ring->fence_drv; -+ u32 seq = 0; -+ -+ if (drv->cpu_addr) -+ seq = le32_to_cpu(*drv->cpu_addr); -+ else -+ seq = lower_32_bits(atomic64_read(&drv->last_seq)); -+ -+ return seq; -+} -+ -+/** -+ * amdgpu_fence_schedule_check - schedule lockup check -+ * -+ * @ring: pointer to struct amdgpu_ring -+ * -+ * Queues a delayed work item to check for lockups. -+ */ -+static void amdgpu_fence_schedule_check(struct amdgpu_ring *ring) -+{ -+ /* -+ * Do not reset the timer here with mod_delayed_work, -+ * this can livelock in an interaction with TTM delayed destroy. -+ */ -+ queue_delayed_work(system_power_efficient_wq, -+ &ring->fence_drv.lockup_work, -+ AMDGPU_FENCE_JIFFIES_TIMEOUT); -+} -+ -+/** -+ * amdgpu_fence_emit - emit a fence on the requested ring -+ * -+ * @ring: ring the fence is associated with -+ * @owner: creator of the fence -+ * @fence: amdgpu fence object -+ * -+ * Emits a fence command on the requested ring (all asics). -+ * Returns 0 on success, -ENOMEM on failure. -+ */ -+int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner, -+ struct amdgpu_fence **fence) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ -+ /* we are protected by the ring emission mutex */ -+ *fence = kmalloc(sizeof(struct amdgpu_fence), GFP_KERNEL); -+ if ((*fence) == NULL) { -+ return -ENOMEM; -+ } -+ (*fence)->seq = ++ring->fence_drv.sync_seq[ring->idx]; -+ (*fence)->ring = ring; -+ (*fence)->owner = owner; -+ fence_init(&(*fence)->base, &amdgpu_fence_ops, -+ &adev->fence_queue.lock, adev->fence_context + ring->idx, -+ (*fence)->seq); -+ amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr, (*fence)->seq, false); -+ trace_amdgpu_fence_emit(ring->adev->ddev, ring->idx, (*fence)->seq); -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_check_signaled - callback from fence_queue -+ * -+ * this function is called with fence_queue lock held, which is also used -+ * for the fence locking itself, so unlocked variants are used for -+ * fence_signal, and remove_wait_queue. -+ */ -+static int amdgpu_fence_check_signaled(wait_queue_t *wait, unsigned mode, int flags, void *key) -+{ -+ struct amdgpu_fence *fence; -+ struct amdgpu_device *adev; -+ u64 seq; -+ int ret; -+ -+ fence = container_of(wait, struct amdgpu_fence, fence_wake); -+ adev = fence->ring->adev; -+ -+ /* -+ * We cannot use amdgpu_fence_process here because we're already -+ * in the waitqueue, in a call from wake_up_all. -+ */ -+ seq = atomic64_read(&fence->ring->fence_drv.last_seq); -+ if (seq >= fence->seq) { -+ ret = fence_signal_locked(&fence->base); -+ if (!ret) -+ FENCE_TRACE(&fence->base, "signaled from irq context\n"); -+ else -+ FENCE_TRACE(&fence->base, "was already signaled\n"); -+ -+ amdgpu_irq_put(adev, fence->ring->fence_drv.irq_src, -+ fence->ring->fence_drv.irq_type); -+ __remove_wait_queue(&adev->fence_queue, &fence->fence_wake); -+ fence_put(&fence->base); -+ } else -+ FENCE_TRACE(&fence->base, "pending\n"); -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_activity - check for fence activity -+ * -+ * @ring: pointer to struct amdgpu_ring -+ * -+ * Checks the current fence value and calculates the last -+ * signalled fence value. Returns true if activity occured -+ * on the ring, and the fence_queue should be waken up. -+ */ -+static bool amdgpu_fence_activity(struct amdgpu_ring *ring) -+{ -+ uint64_t seq, last_seq, last_emitted; -+ unsigned count_loop = 0; -+ bool wake = false; -+ -+ /* Note there is a scenario here for an infinite loop but it's -+ * very unlikely to happen. For it to happen, the current polling -+ * process need to be interrupted by another process and another -+ * process needs to update the last_seq btw the atomic read and -+ * xchg of the current process. -+ * -+ * More over for this to go in infinite loop there need to be -+ * continuously new fence signaled ie radeon_fence_read needs -+ * to return a different value each time for both the currently -+ * polling process and the other process that xchg the last_seq -+ * btw atomic read and xchg of the current process. And the -+ * value the other process set as last seq must be higher than -+ * the seq value we just read. Which means that current process -+ * need to be interrupted after radeon_fence_read and before -+ * atomic xchg. -+ * -+ * To be even more safe we count the number of time we loop and -+ * we bail after 10 loop just accepting the fact that we might -+ * have temporarly set the last_seq not to the true real last -+ * seq but to an older one. -+ */ -+ last_seq = atomic64_read(&ring->fence_drv.last_seq); -+ do { -+ last_emitted = ring->fence_drv.sync_seq[ring->idx]; -+ seq = amdgpu_fence_read(ring); -+ seq |= last_seq & 0xffffffff00000000LL; -+ if (seq < last_seq) { -+ seq &= 0xffffffff; -+ seq |= last_emitted & 0xffffffff00000000LL; -+ } -+ -+ if (seq <= last_seq || seq > last_emitted) { -+ break; -+ } -+ /* If we loop over we don't want to return without -+ * checking if a fence is signaled as it means that the -+ * seq we just read is different from the previous on. -+ */ -+ wake = true; -+ last_seq = seq; -+ if ((count_loop++) > 10) { -+ /* We looped over too many time leave with the -+ * fact that we might have set an older fence -+ * seq then the current real last seq as signaled -+ * by the hw. -+ */ -+ break; -+ } -+ } while (atomic64_xchg(&ring->fence_drv.last_seq, seq) > seq); -+ -+ if (seq < last_emitted) -+ amdgpu_fence_schedule_check(ring); -+ -+ return wake; -+} -+ -+/** -+ * amdgpu_fence_check_lockup - check for hardware lockup -+ * -+ * @work: delayed work item -+ * -+ * Checks for fence activity and if there is none probe -+ * the hardware if a lockup occured. -+ */ -+static void amdgpu_fence_check_lockup(struct work_struct *work) -+{ -+ struct amdgpu_fence_driver *fence_drv; -+ struct amdgpu_ring *ring; -+ -+ fence_drv = container_of(work, struct amdgpu_fence_driver, -+ lockup_work.work); -+ ring = fence_drv->ring; -+ -+ if (!down_read_trylock(&ring->adev->exclusive_lock)) { -+ /* just reschedule the check if a reset is going on */ -+ amdgpu_fence_schedule_check(ring); -+ return; -+ } -+ -+ if (fence_drv->delayed_irq && ring->adev->ddev->irq_enabled) { -+ fence_drv->delayed_irq = false; -+ amdgpu_irq_update(ring->adev, fence_drv->irq_src, -+ fence_drv->irq_type); -+ } -+ -+ if (amdgpu_fence_activity(ring)) -+ wake_up_all(&ring->adev->fence_queue); -+ else if (amdgpu_ring_is_lockup(ring)) { -+ /* good news we believe it's a lockup */ -+ dev_warn(ring->adev->dev, "GPU lockup (current fence id " -+ "0x%016llx last fence id 0x%016llx on ring %d)\n", -+ (uint64_t)atomic64_read(&fence_drv->last_seq), -+ fence_drv->sync_seq[ring->idx], ring->idx); -+ -+ /* remember that we need an reset */ -+ ring->adev->needs_reset = true; -+ wake_up_all(&ring->adev->fence_queue); -+ } -+ up_read(&ring->adev->exclusive_lock); -+} -+ -+/** -+ * amdgpu_fence_process - process a fence -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: ring index the fence is associated with -+ * -+ * Checks the current fence value and wakes the fence queue -+ * if the sequence number has increased (all asics). -+ */ -+void amdgpu_fence_process(struct amdgpu_ring *ring) -+{ -+ uint64_t seq, last_seq, last_emitted; -+ unsigned count_loop = 0; -+ bool wake = false; -+ -+ /* Note there is a scenario here for an infinite loop but it's -+ * very unlikely to happen. For it to happen, the current polling -+ * process need to be interrupted by another process and another -+ * process needs to update the last_seq btw the atomic read and -+ * xchg of the current process. -+ * -+ * More over for this to go in infinite loop there need to be -+ * continuously new fence signaled ie amdgpu_fence_read needs -+ * to return a different value each time for both the currently -+ * polling process and the other process that xchg the last_seq -+ * btw atomic read and xchg of the current process. And the -+ * value the other process set as last seq must be higher than -+ * the seq value we just read. Which means that current process -+ * need to be interrupted after amdgpu_fence_read and before -+ * atomic xchg. -+ * -+ * To be even more safe we count the number of time we loop and -+ * we bail after 10 loop just accepting the fact that we might -+ * have temporarly set the last_seq not to the true real last -+ * seq but to an older one. -+ */ -+ last_seq = atomic64_read(&ring->fence_drv.last_seq); -+ do { -+ last_emitted = ring->fence_drv.sync_seq[ring->idx]; -+ seq = amdgpu_fence_read(ring); -+ seq |= last_seq & 0xffffffff00000000LL; -+ if (seq < last_seq) { -+ seq &= 0xffffffff; -+ seq |= last_emitted & 0xffffffff00000000LL; -+ } -+ -+ if (seq <= last_seq || seq > last_emitted) { -+ break; -+ } -+ /* If we loop over we don't want to return without -+ * checking if a fence is signaled as it means that the -+ * seq we just read is different from the previous on. -+ */ -+ wake = true; -+ last_seq = seq; -+ if ((count_loop++) > 10) { -+ /* We looped over too many time leave with the -+ * fact that we might have set an older fence -+ * seq then the current real last seq as signaled -+ * by the hw. -+ */ -+ break; -+ } -+ } while (atomic64_xchg(&ring->fence_drv.last_seq, seq) > seq); -+ -+ if (wake) -+ wake_up_all(&ring->adev->fence_queue); -+} -+ -+/** -+ * amdgpu_fence_seq_signaled - check if a fence sequence number has signaled -+ * -+ * @ring: ring the fence is associated with -+ * @seq: sequence number -+ * -+ * Check if the last signaled fence sequnce number is >= the requested -+ * sequence number (all asics). -+ * Returns true if the fence has signaled (current fence value -+ * is >= requested value) or false if it has not (current fence -+ * value is < the requested value. Helper function for -+ * amdgpu_fence_signaled(). -+ */ -+static bool amdgpu_fence_seq_signaled(struct amdgpu_ring *ring, u64 seq) -+{ -+ if (atomic64_read(&ring->fence_drv.last_seq) >= seq) -+ return true; -+ -+ /* poll new last sequence at least once */ -+ amdgpu_fence_process(ring); -+ if (atomic64_read(&ring->fence_drv.last_seq) >= seq) -+ return true; -+ -+ return false; -+} -+ -+static bool amdgpu_fence_is_signaled(struct fence *f) -+{ -+ struct amdgpu_fence *fence = to_amdgpu_fence(f); -+ struct amdgpu_ring *ring = fence->ring; -+ struct amdgpu_device *adev = ring->adev; -+ -+ if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) -+ return true; -+ -+ if (down_read_trylock(&adev->exclusive_lock)) { -+ amdgpu_fence_process(ring); -+ up_read(&adev->exclusive_lock); -+ -+ if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * amdgpu_fence_enable_signaling - enable signalling on fence -+ * @fence: fence -+ * -+ * This function is called with fence_queue lock held, and adds a callback -+ * to fence_queue that checks if this fence is signaled, and if so it -+ * signals the fence and removes itself. -+ */ -+static bool amdgpu_fence_enable_signaling(struct fence *f) -+{ -+ struct amdgpu_fence *fence = to_amdgpu_fence(f); -+ struct amdgpu_ring *ring = fence->ring; -+ struct amdgpu_device *adev = ring->adev; -+ -+ if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) -+ return false; -+ -+ if (down_read_trylock(&adev->exclusive_lock)) { -+ amdgpu_irq_get(adev, ring->fence_drv.irq_src, -+ ring->fence_drv.irq_type); -+ if (amdgpu_fence_activity(ring)) -+ wake_up_all_locked(&adev->fence_queue); -+ -+ /* did fence get signaled after we enabled the sw irq? */ -+ if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) { -+ amdgpu_irq_put(adev, ring->fence_drv.irq_src, -+ ring->fence_drv.irq_type); -+ up_read(&adev->exclusive_lock); -+ return false; -+ } -+ -+ up_read(&adev->exclusive_lock); -+ } else { -+ /* we're probably in a lockup, lets not fiddle too much */ -+ if (amdgpu_irq_get_delayed(adev, ring->fence_drv.irq_src, -+ ring->fence_drv.irq_type)) -+ ring->fence_drv.delayed_irq = true; -+ amdgpu_fence_schedule_check(ring); -+ } -+ -+ fence->fence_wake.flags = 0; -+ fence->fence_wake.private = NULL; -+ fence->fence_wake.func = amdgpu_fence_check_signaled; -+ __add_wait_queue(&adev->fence_queue, &fence->fence_wake); -+ fence_get(f); -+ FENCE_TRACE(&fence->base, "armed on ring %i!\n", ring->idx); -+ return true; -+} -+ -+/** -+ * amdgpu_fence_signaled - check if a fence has signaled -+ * -+ * @fence: amdgpu fence object -+ * -+ * Check if the requested fence has signaled (all asics). -+ * Returns true if the fence has signaled or false if it has not. -+ */ -+bool amdgpu_fence_signaled(struct amdgpu_fence *fence) -+{ -+ if (!fence) -+ return true; -+ -+ if (fence->seq == AMDGPU_FENCE_SIGNALED_SEQ) -+ return true; -+ -+ if (amdgpu_fence_seq_signaled(fence->ring, fence->seq)) { -+ fence->seq = AMDGPU_FENCE_SIGNALED_SEQ; -+ if (!fence_signal(&fence->base)) -+ FENCE_TRACE(&fence->base, "signaled from amdgpu_fence_signaled\n"); -+ return true; -+ } -+ -+ return false; -+} -+ -+/** -+ * amdgpu_fence_any_seq_signaled - check if any sequence number is signaled -+ * -+ * @adev: amdgpu device pointer -+ * @seq: sequence numbers -+ * -+ * Check if the last signaled fence sequnce number is >= the requested -+ * sequence number (all asics). -+ * Returns true if any has signaled (current value is >= requested value) -+ * or false if it has not. Helper function for amdgpu_fence_wait_seq. -+ */ -+static bool amdgpu_fence_any_seq_signaled(struct amdgpu_device *adev, u64 *seq) -+{ -+ unsigned i; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ if (!adev->rings[i] || !seq[i]) -+ continue; -+ -+ if (amdgpu_fence_seq_signaled(adev->rings[i], seq[i])) -+ return true; -+ } -+ -+ return false; -+} -+ -+/** -+ * amdgpu_fence_wait_seq_timeout - wait for a specific sequence numbers -+ * -+ * @adev: amdgpu device pointer -+ * @target_seq: sequence number(s) we want to wait for -+ * @intr: use interruptable sleep -+ * @timeout: maximum time to wait, or MAX_SCHEDULE_TIMEOUT for infinite wait -+ * -+ * Wait for the requested sequence number(s) to be written by any ring -+ * (all asics). Sequnce number array is indexed by ring id. -+ * @intr selects whether to use interruptable (true) or non-interruptable -+ * (false) sleep when waiting for the sequence number. Helper function -+ * for amdgpu_fence_wait_*(). -+ * Returns remaining time if the sequence number has passed, 0 when -+ * the wait timeout, or an error for all other cases. -+ * -EDEADLK is returned when a GPU lockup has been detected. -+ */ -+long amdgpu_fence_wait_seq_timeout(struct amdgpu_device *adev, u64 *target_seq, -+ bool intr, long timeout) -+{ -+ uint64_t last_seq[AMDGPU_MAX_RINGS]; -+ bool signaled; -+ int i, r; -+ -+ while (!amdgpu_fence_any_seq_signaled(adev, target_seq)) { -+ -+ /* Save current sequence values, used to check for GPU lockups */ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ -+ if (!ring || !target_seq[i]) -+ continue; -+ -+ last_seq[i] = atomic64_read(&ring->fence_drv.last_seq); -+ trace_amdgpu_fence_wait_begin(adev->ddev, i, target_seq[i]); -+ amdgpu_irq_get(adev, ring->fence_drv.irq_src, -+ ring->fence_drv.irq_type); -+ } -+ -+ if (intr) { -+ r = wait_event_interruptible_timeout(adev->fence_queue, ( -+ (signaled = amdgpu_fence_any_seq_signaled(adev, target_seq)) -+ || adev->needs_reset), AMDGPU_FENCE_JIFFIES_TIMEOUT); -+ } else { -+ r = wait_event_timeout(adev->fence_queue, ( -+ (signaled = amdgpu_fence_any_seq_signaled(adev, target_seq)) -+ || adev->needs_reset), AMDGPU_FENCE_JIFFIES_TIMEOUT); -+ } -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ -+ if (!ring || !target_seq[i]) -+ continue; -+ -+ amdgpu_irq_put(adev, ring->fence_drv.irq_src, -+ ring->fence_drv.irq_type); -+ trace_amdgpu_fence_wait_end(adev->ddev, i, target_seq[i]); -+ } -+ -+ if (unlikely(r < 0)) -+ return r; -+ -+ if (unlikely(!signaled)) { -+ -+ if (adev->needs_reset) -+ return -EDEADLK; -+ -+ /* we were interrupted for some reason and fence -+ * isn't signaled yet, resume waiting */ -+ if (r) -+ continue; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ -+ if (!ring || !target_seq[i]) -+ continue; -+ -+ if (last_seq[i] != atomic64_read(&ring->fence_drv.last_seq)) -+ break; -+ } -+ -+ if (i != AMDGPU_MAX_RINGS) -+ continue; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ if (!adev->rings[i] || !target_seq[i]) -+ continue; -+ -+ if (amdgpu_ring_is_lockup(adev->rings[i])) -+ break; -+ } -+ -+ if (i < AMDGPU_MAX_RINGS) { -+ /* good news we believe it's a lockup */ -+ dev_warn(adev->dev, "GPU lockup (waiting for " -+ "0x%016llx last fence id 0x%016llx on" -+ " ring %d)\n", -+ target_seq[i], last_seq[i], i); -+ -+ /* remember that we need an reset */ -+ adev->needs_reset = true; -+ wake_up_all(&adev->fence_queue); -+ return -EDEADLK; -+ } -+ -+ if (timeout < MAX_SCHEDULE_TIMEOUT) { -+ timeout -= AMDGPU_FENCE_JIFFIES_TIMEOUT; -+ if (timeout <= 0) { -+ return 0; -+ } -+ } -+ } -+ } -+ return timeout; -+} -+ -+/** -+ * amdgpu_fence_wait - wait for a fence to signal -+ * -+ * @fence: amdgpu fence object -+ * @intr: use interruptable sleep -+ * -+ * Wait for the requested fence to signal (all asics). -+ * @intr selects whether to use interruptable (true) or non-interruptable -+ * (false) sleep when waiting for the fence. -+ * Returns 0 if the fence has passed, error for all other cases. -+ */ -+int amdgpu_fence_wait(struct amdgpu_fence *fence, bool intr) -+{ -+ uint64_t seq[AMDGPU_MAX_RINGS] = {}; -+ long r; -+ -+ seq[fence->ring->idx] = fence->seq; -+ if (seq[fence->ring->idx] == AMDGPU_FENCE_SIGNALED_SEQ) -+ return 0; -+ -+ r = amdgpu_fence_wait_seq_timeout(fence->ring->adev, seq, intr, MAX_SCHEDULE_TIMEOUT); -+ if (r < 0) { -+ return r; -+ } -+ -+ fence->seq = AMDGPU_FENCE_SIGNALED_SEQ; -+ r = fence_signal(&fence->base); -+ if (!r) -+ FENCE_TRACE(&fence->base, "signaled from fence_wait\n"); -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_wait_any - wait for a fence to signal on any ring -+ * -+ * @adev: amdgpu device pointer -+ * @fences: amdgpu fence object(s) -+ * @intr: use interruptable sleep -+ * -+ * Wait for any requested fence to signal (all asics). Fence -+ * array is indexed by ring id. @intr selects whether to use -+ * interruptable (true) or non-interruptable (false) sleep when -+ * waiting for the fences. Used by the suballocator. -+ * Returns 0 if any fence has passed, error for all other cases. -+ */ -+int amdgpu_fence_wait_any(struct amdgpu_device *adev, -+ struct amdgpu_fence **fences, -+ bool intr) -+{ -+ uint64_t seq[AMDGPU_MAX_RINGS]; -+ unsigned i, num_rings = 0; -+ long r; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ seq[i] = 0; -+ -+ if (!fences[i]) { -+ continue; -+ } -+ -+ seq[i] = fences[i]->seq; -+ ++num_rings; -+ -+ /* test if something was allready signaled */ -+ if (seq[i] == AMDGPU_FENCE_SIGNALED_SEQ) -+ return 0; -+ } -+ -+ /* nothing to wait for ? */ -+ if (num_rings == 0) -+ return -ENOENT; -+ -+ r = amdgpu_fence_wait_seq_timeout(adev, seq, intr, MAX_SCHEDULE_TIMEOUT); -+ if (r < 0) { -+ return r; -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_wait_next - wait for the next fence to signal -+ * -+ * @adev: amdgpu device pointer -+ * @ring: ring index the fence is associated with -+ * -+ * Wait for the next fence on the requested ring to signal (all asics). -+ * Returns 0 if the next fence has passed, error for all other cases. -+ * Caller must hold ring lock. -+ */ -+int amdgpu_fence_wait_next(struct amdgpu_ring *ring) -+{ -+ uint64_t seq[AMDGPU_MAX_RINGS] = {}; -+ long r; -+ -+ seq[ring->idx] = atomic64_read(&ring->fence_drv.last_seq) + 1ULL; -+ if (seq[ring->idx] >= ring->fence_drv.sync_seq[ring->idx]) { -+ /* nothing to wait for, last_seq is -+ already the last emited fence */ -+ return -ENOENT; -+ } -+ r = amdgpu_fence_wait_seq_timeout(ring->adev, seq, false, MAX_SCHEDULE_TIMEOUT); -+ if (r < 0) -+ return r; -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_wait_empty - wait for all fences to signal -+ * -+ * @adev: amdgpu device pointer -+ * @ring: ring index the fence is associated with -+ * -+ * Wait for all fences on the requested ring to signal (all asics). -+ * Returns 0 if the fences have passed, error for all other cases. -+ * Caller must hold ring lock. -+ */ -+int amdgpu_fence_wait_empty(struct amdgpu_ring *ring) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ uint64_t seq[AMDGPU_MAX_RINGS] = {}; -+ long r; -+ -+ seq[ring->idx] = ring->fence_drv.sync_seq[ring->idx]; -+ if (!seq[ring->idx]) -+ return 0; -+ -+ r = amdgpu_fence_wait_seq_timeout(adev, seq, false, MAX_SCHEDULE_TIMEOUT); -+ if (r < 0) { -+ if (r == -EDEADLK) -+ return -EDEADLK; -+ -+ dev_err(adev->dev, "error waiting for ring[%d] to become idle (%ld)\n", -+ ring->idx, r); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_ref - take a ref on a fence -+ * -+ * @fence: amdgpu fence object -+ * -+ * Take a reference on a fence (all asics). -+ * Returns the fence. -+ */ -+struct amdgpu_fence *amdgpu_fence_ref(struct amdgpu_fence *fence) -+{ -+ fence_get(&fence->base); -+ return fence; -+} -+ -+/** -+ * amdgpu_fence_unref - remove a ref on a fence -+ * -+ * @fence: amdgpu fence object -+ * -+ * Remove a reference on a fence (all asics). -+ */ -+void amdgpu_fence_unref(struct amdgpu_fence **fence) -+{ -+ struct amdgpu_fence *tmp = *fence; -+ -+ *fence = NULL; -+ if (tmp) -+ fence_put(&tmp->base); -+} -+ -+/** -+ * amdgpu_fence_count_emitted - get the count of emitted fences -+ * -+ * @ring: ring the fence is associated with -+ * -+ * Get the number of fences emitted on the requested ring (all asics). -+ * Returns the number of emitted fences on the ring. Used by the -+ * dynpm code to ring track activity. -+ */ -+unsigned amdgpu_fence_count_emitted(struct amdgpu_ring *ring) -+{ -+ uint64_t emitted; -+ -+ /* We are not protected by ring lock when reading the last sequence -+ * but it's ok to report slightly wrong fence count here. -+ */ -+ amdgpu_fence_process(ring); -+ emitted = ring->fence_drv.sync_seq[ring->idx] -+ - atomic64_read(&ring->fence_drv.last_seq); -+ /* to avoid 32bits warp around */ -+ if (emitted > 0x10000000) -+ emitted = 0x10000000; -+ -+ return (unsigned)emitted; -+} -+ -+/** -+ * amdgpu_fence_need_sync - do we need a semaphore -+ * -+ * @fence: amdgpu fence object -+ * @dst_ring: which ring to check against -+ * -+ * Check if the fence needs to be synced against another ring -+ * (all asics). If so, we need to emit a semaphore. -+ * Returns true if we need to sync with another ring, false if -+ * not. -+ */ -+bool amdgpu_fence_need_sync(struct amdgpu_fence *fence, -+ struct amdgpu_ring *dst_ring) -+{ -+ struct amdgpu_fence_driver *fdrv; -+ -+ if (!fence) -+ return false; -+ -+ if (fence->ring == dst_ring) -+ return false; -+ -+ /* we are protected by the ring mutex */ -+ fdrv = &dst_ring->fence_drv; -+ if (fence->seq <= fdrv->sync_seq[fence->ring->idx]) -+ return false; -+ -+ return true; -+} -+ -+/** -+ * amdgpu_fence_note_sync - record the sync point -+ * -+ * @fence: amdgpu fence object -+ * @dst_ring: which ring to check against -+ * -+ * Note the sequence number at which point the fence will -+ * be synced with the requested ring (all asics). -+ */ -+void amdgpu_fence_note_sync(struct amdgpu_fence *fence, -+ struct amdgpu_ring *dst_ring) -+{ -+ struct amdgpu_fence_driver *dst, *src; -+ unsigned i; -+ -+ if (!fence) -+ return; -+ -+ if (fence->ring == dst_ring) -+ return; -+ -+ /* we are protected by the ring mutex */ -+ src = &fence->ring->fence_drv; -+ dst = &dst_ring->fence_drv; -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ if (i == dst_ring->idx) -+ continue; -+ -+ dst->sync_seq[i] = max(dst->sync_seq[i], src->sync_seq[i]); -+ } -+} -+ -+/** -+ * amdgpu_fence_driver_start_ring - make the fence driver -+ * ready for use on the requested ring. -+ * -+ * @ring: ring to start the fence driver on -+ * @irq_src: interrupt source to use for this ring -+ * @irq_type: interrupt type to use for this ring -+ * -+ * Make the fence driver ready for processing (all asics). -+ * Not all asics have all rings, so each asic will only -+ * start the fence driver on the rings it has. -+ * Returns 0 for success, errors for failure. -+ */ -+int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, -+ struct amdgpu_irq_src *irq_src, -+ unsigned irq_type) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ uint64_t index; -+ -+ if (ring != &adev->uvd.ring) { -+ ring->fence_drv.cpu_addr = &adev->wb.wb[ring->fence_offs]; -+ ring->fence_drv.gpu_addr = adev->wb.gpu_addr + (ring->fence_offs * 4); -+ } else { -+ /* put fence directly behind firmware */ -+ index = ALIGN(adev->uvd.fw->size, 8); -+ ring->fence_drv.cpu_addr = adev->uvd.cpu_addr + index; -+ ring->fence_drv.gpu_addr = adev->uvd.gpu_addr + index; -+ } -+ amdgpu_fence_write(ring, atomic64_read(&ring->fence_drv.last_seq)); -+ ring->fence_drv.initialized = true; -+ ring->fence_drv.irq_src = irq_src; -+ ring->fence_drv.irq_type = irq_type; -+ dev_info(adev->dev, "fence driver on ring %d use gpu addr 0x%016llx, " -+ "cpu addr 0x%p\n", ring->idx, -+ ring->fence_drv.gpu_addr, ring->fence_drv.cpu_addr); -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_driver_init_ring - init the fence driver -+ * for the requested ring. -+ * -+ * @ring: ring to init the fence driver on -+ * -+ * Init the fence driver for the requested ring (all asics). -+ * Helper function for amdgpu_fence_driver_init(). -+ */ -+void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) -+{ -+ int i; -+ -+ ring->fence_drv.cpu_addr = NULL; -+ ring->fence_drv.gpu_addr = 0; -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) -+ ring->fence_drv.sync_seq[i] = 0; -+ -+ atomic64_set(&ring->fence_drv.last_seq, 0); -+ ring->fence_drv.initialized = false; -+ -+ INIT_DELAYED_WORK(&ring->fence_drv.lockup_work, -+ amdgpu_fence_check_lockup); -+ ring->fence_drv.ring = ring; -+} -+ -+/** -+ * amdgpu_fence_driver_init - init the fence driver -+ * for all possible rings. -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Init the fence driver for all possible rings (all asics). -+ * Not all asics have all rings, so each asic will only -+ * start the fence driver on the rings it has using -+ * amdgpu_fence_driver_start_ring(). -+ * Returns 0 for success. -+ */ -+int amdgpu_fence_driver_init(struct amdgpu_device *adev) -+{ -+ init_waitqueue_head(&adev->fence_queue); -+ if (amdgpu_debugfs_fence_init(adev)) -+ dev_err(adev->dev, "fence debugfs file creation failed\n"); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_fence_driver_fini - tear down the fence driver -+ * for all possible rings. -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Tear down the fence driver for all possible rings (all asics). -+ */ -+void amdgpu_fence_driver_fini(struct amdgpu_device *adev) -+{ -+ int i, r; -+ -+ mutex_lock(&adev->ring_lock); -+ for (i = 0; i < AMDGPU_MAX_RINGS; i++) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (!ring || !ring->fence_drv.initialized) -+ continue; -+ r = amdgpu_fence_wait_empty(ring); -+ if (r) { -+ /* no need to trigger GPU reset as we are unloading */ -+ amdgpu_fence_driver_force_completion(adev); -+ } -+ wake_up_all(&adev->fence_queue); -+ ring->fence_drv.initialized = false; -+ } -+ mutex_unlock(&adev->ring_lock); -+} -+ -+/** -+ * amdgpu_fence_driver_force_completion - force all fence waiter to complete -+ * -+ * @adev: amdgpu device pointer -+ * -+ * In case of GPU reset failure make sure no process keep waiting on fence -+ * that will never complete. -+ */ -+void amdgpu_fence_driver_force_completion(struct amdgpu_device *adev) -+{ -+ int i; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; i++) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (!ring || !ring->fence_drv.initialized) -+ continue; -+ -+ amdgpu_fence_write(ring, ring->fence_drv.sync_seq[i]); -+ } -+} -+ -+ -+/* -+ * Fence debugfs -+ */ -+#if defined(CONFIG_DEBUG_FS) -+static int amdgpu_debugfs_fence_info(struct seq_file *m, void *data) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ int i, j; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (!ring || !ring->fence_drv.initialized) -+ continue; -+ -+ amdgpu_fence_process(ring); -+ -+ seq_printf(m, "--- ring %d ---\n", i); -+ seq_printf(m, "Last signaled fence 0x%016llx\n", -+ (unsigned long long)atomic64_read(&ring->fence_drv.last_seq)); -+ seq_printf(m, "Last emitted 0x%016llx\n", -+ ring->fence_drv.sync_seq[i]); -+ -+ for (j = 0; j < AMDGPU_MAX_RINGS; ++j) { -+ struct amdgpu_ring *other = adev->rings[j]; -+ if (i != j && other && other->fence_drv.initialized) -+ seq_printf(m, "Last sync to ring %d 0x%016llx\n", -+ j, ring->fence_drv.sync_seq[j]); -+ } -+ } -+ return 0; -+} -+ -+static struct drm_info_list amdgpu_debugfs_fence_list[] = { -+ {"amdgpu_fence_info", &amdgpu_debugfs_fence_info, 0, NULL}, -+}; -+#endif -+ -+int amdgpu_debugfs_fence_init(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_fence_list, 1); -+#else -+ return 0; -+#endif -+} -+ -+static const char *amdgpu_fence_get_driver_name(struct fence *fence) -+{ -+ return "amdgpu"; -+} -+ -+static const char *amdgpu_fence_get_timeline_name(struct fence *f) -+{ -+ struct amdgpu_fence *fence = to_amdgpu_fence(f); -+ return (const char *)fence->ring->name; -+} -+ -+static inline bool amdgpu_test_signaled(struct amdgpu_fence *fence) -+{ -+ return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); -+} -+ -+struct amdgpu_wait_cb { -+ struct fence_cb base; -+ struct task_struct *task; -+}; -+ -+static void amdgpu_fence_wait_cb(struct fence *fence, struct fence_cb *cb) -+{ -+ struct amdgpu_wait_cb *wait = -+ container_of(cb, struct amdgpu_wait_cb, base); -+ wake_up_process(wait->task); -+} -+ -+static signed long amdgpu_fence_default_wait(struct fence *f, bool intr, -+ signed long t) -+{ -+ struct amdgpu_fence *fence = to_amdgpu_fence(f); -+ struct amdgpu_device *adev = fence->ring->adev; -+ struct amdgpu_wait_cb cb; -+ -+ cb.task = current; -+ -+ if (fence_add_callback(f, &cb.base, amdgpu_fence_wait_cb)) -+ return t; -+ -+ while (t > 0) { -+ if (intr) -+ set_current_state(TASK_INTERRUPTIBLE); -+ else -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ -+ /* -+ * amdgpu_test_signaled must be called after -+ * set_current_state to prevent a race with wake_up_process -+ */ -+ if (amdgpu_test_signaled(fence)) -+ break; -+ -+ if (adev->needs_reset) { -+ t = -EDEADLK; -+ break; -+ } -+ -+ t = schedule_timeout(t); -+ -+ if (t > 0 && intr && signal_pending(current)) -+ t = -ERESTARTSYS; -+ } -+ -+ __set_current_state(TASK_RUNNING); -+ fence_remove_callback(f, &cb.base); -+ -+ return t; -+} -+ -+const struct fence_ops amdgpu_fence_ops = { -+ .get_driver_name = amdgpu_fence_get_driver_name, -+ .get_timeline_name = amdgpu_fence_get_timeline_name, -+ .enable_signaling = amdgpu_fence_enable_signaling, -+ .signaled = amdgpu_fence_is_signaled, -+ .wait = amdgpu_fence_default_wait, -+ .release = NULL, -+}; -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c -new file mode 100644 -index 0000000..e02db0b ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c -@@ -0,0 +1,371 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+/* -+ * GART -+ * The GART (Graphics Aperture Remapping Table) is an aperture -+ * in the GPU's address space. System pages can be mapped into -+ * the aperture and look like contiguous pages from the GPU's -+ * perspective. A page table maps the pages in the aperture -+ * to the actual backing pages in system memory. -+ * -+ * Radeon GPUs support both an internal GART, as described above, -+ * and AGP. AGP works similarly, but the GART table is configured -+ * and maintained by the northbridge rather than the driver. -+ * Radeon hw has a separate AGP aperture that is programmed to -+ * point to the AGP aperture provided by the northbridge and the -+ * requests are passed through to the northbridge aperture. -+ * Both AGP and internal GART can be used at the same time, however -+ * that is not currently supported by the driver. -+ * -+ * This file handles the common internal GART management. -+ */ -+ -+/* -+ * Common GART table functions. -+ */ -+/** -+ * amdgpu_gart_table_ram_alloc - allocate system ram for gart page table -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Allocate system memory for GART page table -+ * (r1xx-r3xx, non-pcie r4xx, rs400). These asics require the -+ * gart table to be in system memory. -+ * Returns 0 for success, -ENOMEM for failure. -+ */ -+int amdgpu_gart_table_ram_alloc(struct amdgpu_device *adev) -+{ -+ void *ptr; -+ -+ ptr = pci_alloc_consistent(adev->pdev, adev->gart.table_size, -+ &adev->gart.table_addr); -+ if (ptr == NULL) { -+ return -ENOMEM; -+ } -+#ifdef CONFIG_X86 -+ if (0) { -+ set_memory_uc((unsigned long)ptr, -+ adev->gart.table_size >> PAGE_SHIFT); -+ } -+#endif -+ adev->gart.ptr = ptr; -+ memset((void *)adev->gart.ptr, 0, adev->gart.table_size); -+ return 0; -+} -+ -+/** -+ * amdgpu_gart_table_ram_free - free system ram for gart page table -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Free system memory for GART page table -+ * (r1xx-r3xx, non-pcie r4xx, rs400). These asics require the -+ * gart table to be in system memory. -+ */ -+void amdgpu_gart_table_ram_free(struct amdgpu_device *adev) -+{ -+ if (adev->gart.ptr == NULL) { -+ return; -+ } -+#ifdef CONFIG_X86 -+ if (0) { -+ set_memory_wb((unsigned long)adev->gart.ptr, -+ adev->gart.table_size >> PAGE_SHIFT); -+ } -+#endif -+ pci_free_consistent(adev->pdev, adev->gart.table_size, -+ (void *)adev->gart.ptr, -+ adev->gart.table_addr); -+ adev->gart.ptr = NULL; -+ adev->gart.table_addr = 0; -+} -+ -+/** -+ * amdgpu_gart_table_vram_alloc - allocate vram for gart page table -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Allocate video memory for GART page table -+ * (pcie r4xx, r5xx+). These asics require the -+ * gart table to be in video memory. -+ * Returns 0 for success, error for failure. -+ */ -+int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->gart.robj == NULL) { -+ r = amdgpu_bo_create(adev, adev->gart.table_size, -+ PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, 0, -+ NULL, &adev->gart.robj); -+ if (r) { -+ return r; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_gart_table_vram_pin - pin gart page table in vram -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Pin the GART page table in vram so it will not be moved -+ * by the memory manager (pcie r4xx, r5xx+). These asics require the -+ * gart table to be in video memory. -+ * Returns 0 for success, error for failure. -+ */ -+int amdgpu_gart_table_vram_pin(struct amdgpu_device *adev) -+{ -+ uint64_t gpu_addr; -+ int r; -+ -+ r = amdgpu_bo_reserve(adev->gart.robj, false); -+ if (unlikely(r != 0)) -+ return r; -+ r = amdgpu_bo_pin(adev->gart.robj, -+ AMDGPU_GEM_DOMAIN_VRAM, &gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(adev->gart.robj); -+ return r; -+ } -+ r = amdgpu_bo_kmap(adev->gart.robj, &adev->gart.ptr); -+ if (r) -+ amdgpu_bo_unpin(adev->gart.robj); -+ amdgpu_bo_unreserve(adev->gart.robj); -+ adev->gart.table_addr = gpu_addr; -+ return r; -+} -+ -+/** -+ * amdgpu_gart_table_vram_unpin - unpin gart page table in vram -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Unpin the GART page table in vram (pcie r4xx, r5xx+). -+ * These asics require the gart table to be in video memory. -+ */ -+void amdgpu_gart_table_vram_unpin(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->gart.robj == NULL) { -+ return; -+ } -+ r = amdgpu_bo_reserve(adev->gart.robj, false); -+ if (likely(r == 0)) { -+ amdgpu_bo_kunmap(adev->gart.robj); -+ amdgpu_bo_unpin(adev->gart.robj); -+ amdgpu_bo_unreserve(adev->gart.robj); -+ adev->gart.ptr = NULL; -+ } -+} -+ -+/** -+ * amdgpu_gart_table_vram_free - free gart page table vram -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Free the video memory used for the GART page table -+ * (pcie r4xx, r5xx+). These asics require the gart table to -+ * be in video memory. -+ */ -+void amdgpu_gart_table_vram_free(struct amdgpu_device *adev) -+{ -+ if (adev->gart.robj == NULL) { -+ return; -+ } -+ amdgpu_bo_unref(&adev->gart.robj); -+} -+ -+/* -+ * Common gart functions. -+ */ -+/** -+ * amdgpu_gart_unbind - unbind pages from the gart page table -+ * -+ * @adev: amdgpu_device pointer -+ * @offset: offset into the GPU's gart aperture -+ * @pages: number of pages to unbind -+ * -+ * Unbinds the requested pages from the gart page table and -+ * replaces them with the dummy page (all asics). -+ */ -+void amdgpu_gart_unbind(struct amdgpu_device *adev, unsigned offset, -+ int pages) -+{ -+ unsigned t; -+ unsigned p; -+ int i, j; -+ u64 page_base; -+ uint32_t flags = AMDGPU_PTE_SYSTEM; -+ -+ if (!adev->gart.ready) { -+ WARN(1, "trying to unbind memory from uninitialized GART !\n"); -+ return; -+ } -+ -+ t = offset / AMDGPU_GPU_PAGE_SIZE; -+ p = t / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); -+ for (i = 0; i < pages; i++, p++) { -+ if (adev->gart.pages[p]) { -+ adev->gart.pages[p] = NULL; -+ adev->gart.pages_addr[p] = adev->dummy_page.addr; -+ page_base = adev->gart.pages_addr[p]; -+ if (!adev->gart.ptr) -+ continue; -+ -+ for (j = 0; j < (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); j++, t++) { -+ amdgpu_gart_set_pte_pde(adev, adev->gart.ptr, -+ t, page_base, flags); -+ page_base += AMDGPU_GPU_PAGE_SIZE; -+ } -+ } -+ } -+ mb(); -+ amdgpu_gart_flush_gpu_tlb(adev, 0); -+} -+ -+/** -+ * amdgpu_gart_bind - bind pages into the gart page table -+ * -+ * @adev: amdgpu_device pointer -+ * @offset: offset into the GPU's gart aperture -+ * @pages: number of pages to bind -+ * @pagelist: pages to bind -+ * @dma_addr: DMA addresses of pages -+ * -+ * Binds the requested pages to the gart page table -+ * (all asics). -+ * Returns 0 for success, -EINVAL for failure. -+ */ -+int amdgpu_gart_bind(struct amdgpu_device *adev, unsigned offset, -+ int pages, struct page **pagelist, dma_addr_t *dma_addr, -+ uint32_t flags) -+{ -+ unsigned t; -+ unsigned p; -+ uint64_t page_base; -+ int i, j; -+ -+ if (!adev->gart.ready) { -+ WARN(1, "trying to bind memory to uninitialized GART !\n"); -+ return -EINVAL; -+ } -+ -+ t = offset / AMDGPU_GPU_PAGE_SIZE; -+ p = t / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); -+ -+ for (i = 0; i < pages; i++, p++) { -+ adev->gart.pages_addr[p] = dma_addr[i]; -+ adev->gart.pages[p] = pagelist[i]; -+ if (adev->gart.ptr) { -+ page_base = adev->gart.pages_addr[p]; -+ for (j = 0; j < (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); j++, t++) { -+ amdgpu_gart_set_pte_pde(adev, adev->gart.ptr, t, page_base, flags); -+ page_base += AMDGPU_GPU_PAGE_SIZE; -+ } -+ } -+ } -+ mb(); -+ amdgpu_gart_flush_gpu_tlb(adev, 0); -+ return 0; -+} -+ -+/** -+ * amdgpu_gart_init - init the driver info for managing the gart -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Allocate the dummy page and init the gart driver info (all asics). -+ * Returns 0 for success, error for failure. -+ */ -+int amdgpu_gart_init(struct amdgpu_device *adev) -+{ -+ int r, i; -+ -+ if (adev->gart.pages) { -+ return 0; -+ } -+ /* We need PAGE_SIZE >= AMDGPU_GPU_PAGE_SIZE */ -+ if (PAGE_SIZE < AMDGPU_GPU_PAGE_SIZE) { -+ DRM_ERROR("Page size is smaller than GPU page size!\n"); -+ return -EINVAL; -+ } -+ r = amdgpu_dummy_page_init(adev); -+ if (r) -+ return r; -+ /* Compute table size */ -+ adev->gart.num_cpu_pages = adev->mc.gtt_size / PAGE_SIZE; -+ adev->gart.num_gpu_pages = adev->mc.gtt_size / AMDGPU_GPU_PAGE_SIZE; -+ DRM_INFO("GART: num cpu pages %u, num gpu pages %u\n", -+ adev->gart.num_cpu_pages, adev->gart.num_gpu_pages); -+ /* Allocate pages table */ -+ adev->gart.pages = vzalloc(sizeof(void *) * adev->gart.num_cpu_pages); -+ if (adev->gart.pages == NULL) { -+ amdgpu_gart_fini(adev); -+ return -ENOMEM; -+ } -+ adev->gart.pages_addr = vzalloc(sizeof(dma_addr_t) * -+ adev->gart.num_cpu_pages); -+ if (adev->gart.pages_addr == NULL) { -+ amdgpu_gart_fini(adev); -+ return -ENOMEM; -+ } -+ /* set GART entry to point to the dummy page by default */ -+ for (i = 0; i < adev->gart.num_cpu_pages; i++) { -+ adev->gart.pages_addr[i] = adev->dummy_page.addr; -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_gart_fini - tear down the driver info for managing the gart -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Tear down the gart driver info and free the dummy page (all asics). -+ */ -+void amdgpu_gart_fini(struct amdgpu_device *adev) -+{ -+ if (adev->gart.pages && adev->gart.pages_addr && adev->gart.ready) { -+ /* unbind pages */ -+ amdgpu_gart_unbind(adev, 0, adev->gart.num_cpu_pages); -+ } -+ adev->gart.ready = false; -+ vfree(adev->gart.pages); -+ vfree(adev->gart.pages_addr); -+ adev->gart.pages = NULL; -+ adev->gart.pages_addr = NULL; -+ -+ amdgpu_dummy_page_fini(adev); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h -new file mode 100644 -index 0000000..c3f4e85 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h -@@ -0,0 +1,72 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_GDS_H__ -+#define __AMDGPU_GDS_H__ -+ -+/* Because TTM request that alloacted buffer should be PAGE_SIZE aligned, -+ * we should report GDS/GWS/OA size as PAGE_SIZE aligned -+ * */ -+#define AMDGPU_GDS_SHIFT 2 -+#define AMDGPU_GWS_SHIFT PAGE_SHIFT -+#define AMDGPU_OA_SHIFT PAGE_SHIFT -+ -+#define AMDGPU_PL_GDS TTM_PL_PRIV0 -+#define AMDGPU_PL_GWS TTM_PL_PRIV1 -+#define AMDGPU_PL_OA TTM_PL_PRIV2 -+ -+#define AMDGPU_PL_FLAG_GDS TTM_PL_FLAG_PRIV0 -+#define AMDGPU_PL_FLAG_GWS TTM_PL_FLAG_PRIV1 -+#define AMDGPU_PL_FLAG_OA TTM_PL_FLAG_PRIV2 -+ -+struct amdgpu_ring; -+struct amdgpu_bo; -+ -+struct amdgpu_gds_asic_info { -+ uint32_t total_size; -+ uint32_t gfx_partition_size; -+ uint32_t cs_partition_size; -+}; -+ -+struct amdgpu_gds { -+ struct amdgpu_gds_asic_info mem; -+ struct amdgpu_gds_asic_info gws; -+ struct amdgpu_gds_asic_info oa; -+ /* At present, GDS, GWS and OA resources for gfx (graphics) -+ * is always pre-allocated and available for graphics operation. -+ * Such resource is shared between all gfx clients. -+ * TODO: move this operation to user space -+ * */ -+ struct amdgpu_bo* gds_gfx_bo; -+ struct amdgpu_bo* gws_gfx_bo; -+ struct amdgpu_bo* oa_gfx_bo; -+}; -+ -+struct amdgpu_gds_reg_offset { -+ uint32_t mem_base; -+ uint32_t mem_size; -+ uint32_t gws; -+ uint32_t oa; -+}; -+ -+#endif /* __AMDGPU_GDS_H__ */ -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c -new file mode 100644 -index 0000000..5fd0bc7 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c -@@ -0,0 +1,735 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <linux/ktime.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+void amdgpu_gem_object_free(struct drm_gem_object *gobj) -+{ -+ struct amdgpu_bo *robj = gem_to_amdgpu_bo(gobj); -+ -+ if (robj) { -+ if (robj->gem_base.import_attach) -+ drm_prime_gem_destroy(&robj->gem_base, robj->tbo.sg); -+ amdgpu_bo_unref(&robj); -+ } -+} -+ -+int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, -+ int alignment, u32 initial_domain, -+ u64 flags, bool kernel, -+ struct drm_gem_object **obj) -+{ -+ struct amdgpu_bo *robj; -+ unsigned long max_size; -+ int r; -+ -+ *obj = NULL; -+ /* At least align on page size */ -+ if (alignment < PAGE_SIZE) { -+ alignment = PAGE_SIZE; -+ } -+ -+ if (!(initial_domain & (AMDGPU_GEM_DOMAIN_GDS | AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA))) { -+ /* Maximum bo size is the unpinned gtt size since we use the gtt to -+ * handle vram to system pool migrations. -+ */ -+ max_size = adev->mc.gtt_size - adev->gart_pin_size; -+ if (size > max_size) { -+ DRM_DEBUG("Allocation size %ldMb bigger than %ldMb limit\n", -+ size >> 20, max_size >> 20); -+ return -ENOMEM; -+ } -+ } -+retry: -+ r = amdgpu_bo_create(adev, size, alignment, kernel, initial_domain, flags, NULL, &robj); -+ if (r) { -+ if (r != -ERESTARTSYS) { -+ if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) { -+ initial_domain |= AMDGPU_GEM_DOMAIN_GTT; -+ goto retry; -+ } -+ DRM_ERROR("Failed to allocate GEM object (%ld, %d, %u, %d)\n", -+ size, initial_domain, alignment, r); -+ } -+ return r; -+ } -+ *obj = &robj->gem_base; -+ robj->pid = task_pid_nr(current); -+ -+ mutex_lock(&adev->gem.mutex); -+ list_add_tail(&robj->list, &adev->gem.objects); -+ mutex_unlock(&adev->gem.mutex); -+ -+ return 0; -+} -+ -+int amdgpu_gem_init(struct amdgpu_device *adev) -+{ -+ INIT_LIST_HEAD(&adev->gem.objects); -+ return 0; -+} -+ -+void amdgpu_gem_fini(struct amdgpu_device *adev) -+{ -+ amdgpu_bo_force_delete(adev); -+} -+ -+/* -+ * Call from drm_gem_handle_create which appear in both new and open ioctl -+ * case. -+ */ -+int amdgpu_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv) -+{ -+ struct amdgpu_bo *rbo = gem_to_amdgpu_bo(obj); -+ struct amdgpu_device *adev = rbo->adev; -+ struct amdgpu_fpriv *fpriv = file_priv->driver_priv; -+ struct amdgpu_vm *vm = &fpriv->vm; -+ struct amdgpu_bo_va *bo_va; -+ int r; -+ -+ r = amdgpu_bo_reserve(rbo, false); -+ if (r) { -+ return r; -+ } -+ -+ bo_va = amdgpu_vm_bo_find(vm, rbo); -+ if (!bo_va) { -+ bo_va = amdgpu_vm_bo_add(adev, vm, rbo); -+ } else { -+ ++bo_va->ref_count; -+ } -+ amdgpu_bo_unreserve(rbo); -+ -+ return 0; -+} -+ -+void amdgpu_gem_object_close(struct drm_gem_object *obj, -+ struct drm_file *file_priv) -+{ -+ struct amdgpu_bo *rbo = gem_to_amdgpu_bo(obj); -+ struct amdgpu_device *adev = rbo->adev; -+ struct amdgpu_fpriv *fpriv = file_priv->driver_priv; -+ struct amdgpu_vm *vm = &fpriv->vm; -+ struct amdgpu_bo_va *bo_va; -+ int r; -+ -+ r = amdgpu_bo_reserve(rbo, true); -+ if (r) { -+ dev_err(adev->dev, "leaking bo va because " -+ "we fail to reserve bo (%d)\n", r); -+ return; -+ } -+ bo_va = amdgpu_vm_bo_find(vm, rbo); -+ if (bo_va) { -+ if (--bo_va->ref_count == 0) { -+ amdgpu_vm_bo_rmv(adev, bo_va); -+ } -+ } -+ amdgpu_bo_unreserve(rbo); -+} -+ -+static int amdgpu_gem_handle_lockup(struct amdgpu_device *adev, int r) -+{ -+ if (r == -EDEADLK) { -+ r = amdgpu_gpu_reset(adev); -+ if (!r) -+ r = -EAGAIN; -+ } -+ return r; -+} -+ -+/* -+ * GEM ioctls. -+ */ -+int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ union drm_amdgpu_gem_create *args = data; -+ uint64_t size = args->in.bo_size; -+ struct drm_gem_object *gobj; -+ uint32_t handle; -+ bool kernel = false; -+ int r; -+ -+ down_read(&adev->exclusive_lock); -+ /* create a gem object to contain this object in */ -+ if (args->in.domains & (AMDGPU_GEM_DOMAIN_GDS | -+ AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA)) { -+ kernel = true; -+ if (args->in.domains == AMDGPU_GEM_DOMAIN_GDS) -+ size = size << AMDGPU_GDS_SHIFT; -+ else if (args->in.domains == AMDGPU_GEM_DOMAIN_GWS) -+ size = size << AMDGPU_GWS_SHIFT; -+ else if (args->in.domains == AMDGPU_GEM_DOMAIN_OA) -+ size = size << AMDGPU_OA_SHIFT; -+ else { -+ r = -EINVAL; -+ goto error_unlock; -+ } -+ } -+ size = roundup(size, PAGE_SIZE); -+ -+ r = amdgpu_gem_object_create(adev, size, args->in.alignment, -+ (u32)(0xffffffff & args->in.domains), -+ args->in.domain_flags, -+ kernel, &gobj); -+ if (r) -+ goto error_unlock; -+ -+ r = drm_gem_handle_create(filp, gobj, &handle); -+ /* drop reference from allocate - handle holds it now */ -+ drm_gem_object_unreference_unlocked(gobj); -+ if (r) -+ goto error_unlock; -+ -+ memset(args, 0, sizeof(*args)); -+ args->out.handle = handle; -+ up_read(&adev->exclusive_lock); -+ return 0; -+ -+error_unlock: -+ up_read(&adev->exclusive_lock); -+ r = amdgpu_gem_handle_lockup(adev, r); -+ return r; -+} -+ -+int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_amdgpu_gem_userptr *args = data; -+ struct drm_gem_object *gobj; -+ struct amdgpu_bo *bo; -+ uint32_t handle; -+ int r; -+ -+ if (offset_in_page(args->addr | args->size)) -+ return -EINVAL; -+ -+ /* reject unknown flag values */ -+ if (args->flags & ~(AMDGPU_GEM_USERPTR_READONLY | -+ AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_VALIDATE | -+ AMDGPU_GEM_USERPTR_REGISTER)) -+ return -EINVAL; -+ -+ if (!(args->flags & AMDGPU_GEM_USERPTR_ANONONLY) || -+ !(args->flags & AMDGPU_GEM_USERPTR_REGISTER)) { -+ -+ /* if we want to write to it we must require anonymous -+ memory and install a MMU notifier */ -+ return -EACCES; -+ } -+ -+ down_read(&adev->exclusive_lock); -+ -+ /* create a gem object to contain this object in */ -+ r = amdgpu_gem_object_create(adev, args->size, 0, -+ AMDGPU_GEM_DOMAIN_CPU, 0, -+ 0, &gobj); -+ if (r) -+ goto handle_lockup; -+ -+ bo = gem_to_amdgpu_bo(gobj); -+ r = amdgpu_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags); -+ if (r) -+ goto release_object; -+ -+ if (args->flags & AMDGPU_GEM_USERPTR_REGISTER) { -+ r = amdgpu_mn_register(bo, args->addr); -+ if (r) -+ goto release_object; -+ } -+ -+ if (args->flags & AMDGPU_GEM_USERPTR_VALIDATE) { -+ down_read(¤t->mm->mmap_sem); -+ r = amdgpu_bo_reserve(bo, true); -+ if (r) { -+ up_read(¤t->mm->mmap_sem); -+ goto release_object; -+ } -+ -+ amdgpu_ttm_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_GTT); -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); -+ amdgpu_bo_unreserve(bo); -+ up_read(¤t->mm->mmap_sem); -+ if (r) -+ goto release_object; -+ } -+ -+ r = drm_gem_handle_create(filp, gobj, &handle); -+ /* drop reference from allocate - handle holds it now */ -+ drm_gem_object_unreference_unlocked(gobj); -+ if (r) -+ goto handle_lockup; -+ -+ args->handle = handle; -+ up_read(&adev->exclusive_lock); -+ return 0; -+ -+release_object: -+ drm_gem_object_unreference_unlocked(gobj); -+ -+handle_lockup: -+ up_read(&adev->exclusive_lock); -+ r = amdgpu_gem_handle_lockup(adev, r); -+ -+ return r; -+} -+ -+int amdgpu_mode_dumb_mmap(struct drm_file *filp, -+ struct drm_device *dev, -+ uint32_t handle, uint64_t *offset_p) -+{ -+ struct drm_gem_object *gobj; -+ struct amdgpu_bo *robj; -+ -+ gobj = drm_gem_object_lookup(dev, filp, handle); -+ if (gobj == NULL) { -+ return -ENOENT; -+ } -+ robj = gem_to_amdgpu_bo(gobj); -+ if (amdgpu_ttm_tt_has_userptr(robj->tbo.ttm)) { -+ drm_gem_object_unreference_unlocked(gobj); -+ return -EPERM; -+ } -+ *offset_p = amdgpu_bo_mmap_offset(robj); -+ drm_gem_object_unreference_unlocked(gobj); -+ return 0; -+} -+ -+int amdgpu_gem_mmap_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ union drm_amdgpu_gem_mmap *args = data; -+ uint32_t handle = args->in.handle; -+ memset(args, 0, sizeof(*args)); -+ return amdgpu_mode_dumb_mmap(filp, dev, handle, &args->out.addr_ptr); -+} -+ -+/** -+ * amdgpu_gem_timeout - calculate jiffies timeout from absolute value -+ * -+ * @timeout_ns: timeout in ns -+ * -+ * Calculate the timeout in jiffies from an absolute timeout in ns. -+ */ -+unsigned long amdgpu_gem_timeout(uint64_t timeout_ns) -+{ -+ unsigned long timeout_jiffies; -+ ktime_t timeout; -+ -+ /* clamp timeout if it's to large */ -+ if (((int64_t)timeout_ns) < 0) -+ return MAX_SCHEDULE_TIMEOUT; -+ -+ timeout = ktime_sub_ns(ktime_get(), timeout_ns); -+ if (ktime_to_ns(timeout) < 0) -+ return 0; -+ -+ timeout_jiffies = nsecs_to_jiffies(ktime_to_ns(timeout)); -+ /* clamp timeout to avoid unsigned-> signed overflow */ -+ if (timeout_jiffies > MAX_SCHEDULE_TIMEOUT ) -+ return MAX_SCHEDULE_TIMEOUT - 1; -+ -+ return timeout_jiffies; -+} -+ -+int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ union drm_amdgpu_gem_wait_idle *args = data; -+ struct drm_gem_object *gobj; -+ struct amdgpu_bo *robj; -+ uint32_t handle = args->in.handle; -+ unsigned long timeout = amdgpu_gem_timeout(args->in.timeout); -+ int r = 0; -+ long ret; -+ -+ gobj = drm_gem_object_lookup(dev, filp, handle); -+ if (gobj == NULL) { -+ return -ENOENT; -+ } -+ robj = gem_to_amdgpu_bo(gobj); -+ if (timeout == 0) -+ ret = reservation_object_test_signaled_rcu(robj->tbo.resv, true); -+ else -+ ret = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, timeout); -+ -+ /* ret == 0 means not signaled, -+ * ret > 0 means signaled -+ * ret < 0 means interrupted before timeout -+ */ -+ if (ret >= 0) { -+ memset(args, 0, sizeof(*args)); -+ args->out.status = (ret == 0); -+ } else -+ r = ret; -+ -+ drm_gem_object_unreference_unlocked(gobj); -+ r = amdgpu_gem_handle_lockup(adev, r); -+ return r; -+} -+ -+int amdgpu_gem_metadata_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ struct drm_amdgpu_gem_metadata *args = data; -+ struct drm_gem_object *gobj; -+ struct amdgpu_bo *robj; -+ int r = -1; -+ -+ DRM_DEBUG("%d \n", args->handle); -+ gobj = drm_gem_object_lookup(dev, filp, args->handle); -+ if (gobj == NULL) -+ return -ENOENT; -+ robj = gem_to_amdgpu_bo(gobj); -+ -+ r = amdgpu_bo_reserve(robj, false); -+ if (unlikely(r != 0)) -+ goto out; -+ -+ if (args->op == AMDGPU_GEM_METADATA_OP_GET_METADATA) { -+ amdgpu_bo_get_tiling_flags(robj, &args->data.tiling_info); -+ r = amdgpu_bo_get_metadata(robj, args->data.data, -+ sizeof(args->data.data), -+ &args->data.data_size_bytes, -+ &args->data.flags); -+ } else if (args->op == AMDGPU_GEM_METADATA_OP_SET_METADATA) { -+ r = amdgpu_bo_set_tiling_flags(robj, args->data.tiling_info); -+ if (!r) -+ r = amdgpu_bo_set_metadata(robj, args->data.data, -+ args->data.data_size_bytes, -+ args->data.flags); -+ } -+ -+ amdgpu_bo_unreserve(robj); -+out: -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+} -+ -+/** -+ * amdgpu_gem_va_update_vm -update the bo_va in its VM -+ * -+ * @adev: amdgpu_device pointer -+ * @bo_va: bo_va to update -+ * -+ * Update the bo_va directly after setting it's address. Errors are not -+ * vital here, so they are not reported back to userspace. -+ */ -+static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev, -+ struct amdgpu_bo_va *bo_va) -+{ -+ struct ttm_validate_buffer tv, *entry; -+ struct amdgpu_bo_list_entry *vm_bos; -+ struct ww_acquire_ctx ticket; -+ struct list_head list; -+ unsigned domain; -+ int r; -+ -+ INIT_LIST_HEAD(&list); -+ -+ tv.bo = &bo_va->bo->tbo; -+ tv.shared = true; -+ list_add(&tv.head, &list); -+ -+ vm_bos = amdgpu_vm_get_bos(adev, bo_va->vm, &list); -+ if (!vm_bos) -+ return; -+ -+ r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); -+ if (r) -+ goto error_free; -+ -+ list_for_each_entry(entry, &list, head) { -+ domain = amdgpu_mem_type_to_domain(entry->bo->mem.mem_type); -+ /* if anything is swapped out don't swap it in here, -+ just abort and wait for the next CS */ -+ if (domain == AMDGPU_GEM_DOMAIN_CPU) -+ goto error_unreserve; -+ } -+ -+ mutex_lock(&bo_va->vm->mutex); -+ r = amdgpu_vm_clear_freed(adev, bo_va->vm); -+ if (r) -+ goto error_unlock; -+ -+ r = amdgpu_vm_bo_update(adev, bo_va, &bo_va->bo->tbo.mem); -+ -+error_unlock: -+ mutex_unlock(&bo_va->vm->mutex); -+ -+error_unreserve: -+ ttm_eu_backoff_reservation(&ticket, &list); -+ -+error_free: -+ drm_free_large(vm_bos); -+ -+ if (r) -+ DRM_ERROR("Couldn't update BO_VA (%d)\n", r); -+} -+ -+ -+ -+int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ union drm_amdgpu_gem_va *args = data; -+ struct drm_gem_object *gobj; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_fpriv *fpriv = filp->driver_priv; -+ struct amdgpu_bo *rbo; -+ struct amdgpu_bo_va *bo_va; -+ uint32_t invalid_flags, va_flags = 0; -+ int r = 0; -+ -+ if (!adev->vm_manager.enabled) { -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ return -ENOTTY; -+ } -+ -+ if (args->in.va_address < AMDGPU_VA_RESERVED_SIZE) { -+ dev_err(&dev->pdev->dev, -+ "va_address 0x%lX is in reserved area 0x%X\n", -+ (unsigned long)args->in.va_address, -+ AMDGPU_VA_RESERVED_SIZE); -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ return -EINVAL; -+ } -+ -+ invalid_flags = ~(AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE | -+ AMDGPU_VM_PAGE_EXECUTABLE); -+ if ((args->in.flags & invalid_flags)) { -+ dev_err(&dev->pdev->dev, "invalid flags 0x%08X vs 0x%08X\n", -+ args->in.flags, invalid_flags); -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ return -EINVAL; -+ } -+ -+ switch (args->in.operation) { -+ case AMDGPU_VA_OP_MAP: -+ case AMDGPU_VA_OP_UNMAP: -+ break; -+ default: -+ dev_err(&dev->pdev->dev, "unsupported operation %d\n", -+ args->in.operation); -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ return -EINVAL; -+ } -+ -+ gobj = drm_gem_object_lookup(dev, filp, args->in.handle); -+ if (gobj == NULL) { -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ return -ENOENT; -+ } -+ rbo = gem_to_amdgpu_bo(gobj); -+ r = amdgpu_bo_reserve(rbo, false); -+ if (r) { -+ if (r != -ERESTARTSYS) { -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ } -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+ } -+ bo_va = amdgpu_vm_bo_find(&fpriv->vm, rbo); -+ if (!bo_va) { -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ drm_gem_object_unreference_unlocked(gobj); -+ return -ENOENT; -+ } -+ -+ switch (args->in.operation) { -+ case AMDGPU_VA_OP_MAP: -+ if (args->in.flags & AMDGPU_VM_PAGE_READABLE) -+ va_flags |= AMDGPU_PTE_READABLE; -+ if (args->in.flags & AMDGPU_VM_PAGE_WRITEABLE) -+ va_flags |= AMDGPU_PTE_WRITEABLE; -+ if (args->in.flags & AMDGPU_VM_PAGE_EXECUTABLE) -+ va_flags |= AMDGPU_PTE_EXECUTABLE; -+ r = amdgpu_vm_bo_map(adev, bo_va, args->in.va_address, 0, -+ amdgpu_bo_size(bo_va->bo), va_flags); -+ break; -+ case AMDGPU_VA_OP_UNMAP: -+ r = amdgpu_vm_bo_unmap(adev, bo_va, args->in.va_address); -+ break; -+ default: -+ break; -+ } -+ -+ if (!r) { -+ amdgpu_gem_va_update_vm(adev, bo_va); -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_OK; -+ } else { -+ memset(args, 0, sizeof(*args)); -+ args->out.result = AMDGPU_VA_RESULT_ERROR; -+ } -+ -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+} -+ -+int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *filp) -+{ -+ struct drm_amdgpu_gem_op *args = data; -+ struct drm_gem_object *gobj; -+ struct amdgpu_bo *robj; -+ int r; -+ -+ gobj = drm_gem_object_lookup(dev, filp, args->handle); -+ if (gobj == NULL) { -+ return -ENOENT; -+ } -+ robj = gem_to_amdgpu_bo(gobj); -+ -+ r = amdgpu_bo_reserve(robj, false); -+ if (unlikely(r)) -+ goto out; -+ -+ switch (args->op) { -+ case AMDGPU_GEM_OP_GET_GEM_CREATE_INFO: { -+ struct drm_amdgpu_gem_create_in info; -+ void __user *out = (void __user *)(long)args->value; -+ -+ info.bo_size = robj->gem_base.size; -+ info.alignment = robj->tbo.mem.page_alignment << PAGE_SHIFT; -+ info.domains = robj->initial_domain; -+ info.domain_flags = robj->flags; -+ if (copy_to_user(out, &info, sizeof(info))) -+ r = -EFAULT; -+ break; -+ } -+ case AMDGPU_GEM_OP_SET_INITIAL_DOMAIN: -+ if (amdgpu_ttm_tt_has_userptr(robj->tbo.ttm)) { -+ r = -EPERM; -+ break; -+ } -+ robj->initial_domain = args->value & (AMDGPU_GEM_DOMAIN_VRAM | -+ AMDGPU_GEM_DOMAIN_GTT | -+ AMDGPU_GEM_DOMAIN_CPU); -+ break; -+ default: -+ r = -EINVAL; -+ } -+ -+ amdgpu_bo_unreserve(robj); -+out: -+ drm_gem_object_unreference_unlocked(gobj); -+ return r; -+} -+ -+int amdgpu_mode_dumb_create(struct drm_file *file_priv, -+ struct drm_device *dev, -+ struct drm_mode_create_dumb *args) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_gem_object *gobj; -+ uint32_t handle; -+ int r; -+ -+ args->pitch = amdgpu_align_pitch(adev, args->width, args->bpp, 0) * ((args->bpp + 1) / 8); -+ args->size = args->pitch * args->height; -+ args->size = ALIGN(args->size, PAGE_SIZE); -+ -+ r = amdgpu_gem_object_create(adev, args->size, 0, -+ AMDGPU_GEM_DOMAIN_VRAM, -+ 0, ttm_bo_type_device, -+ &gobj); -+ if (r) -+ return -ENOMEM; -+ -+ r = drm_gem_handle_create(file_priv, gobj, &handle); -+ /* drop reference from allocate - handle holds it now */ -+ drm_gem_object_unreference_unlocked(gobj); -+ if (r) { -+ return r; -+ } -+ args->handle = handle; -+ return 0; -+} -+ -+#if defined(CONFIG_DEBUG_FS) -+static int amdgpu_debugfs_gem_info(struct seq_file *m, void *data) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_bo *rbo; -+ unsigned i = 0; -+ -+ mutex_lock(&adev->gem.mutex); -+ list_for_each_entry(rbo, &adev->gem.objects, list) { -+ unsigned domain; -+ const char *placement; -+ -+ domain = amdgpu_mem_type_to_domain(rbo->tbo.mem.mem_type); -+ switch (domain) { -+ case AMDGPU_GEM_DOMAIN_VRAM: -+ placement = "VRAM"; -+ break; -+ case AMDGPU_GEM_DOMAIN_GTT: -+ placement = " GTT"; -+ break; -+ case AMDGPU_GEM_DOMAIN_CPU: -+ default: -+ placement = " CPU"; -+ break; -+ } -+ seq_printf(m, "bo[0x%08x] %8ldkB %8ldMB %s pid %8ld\n", -+ i, amdgpu_bo_size(rbo) >> 10, amdgpu_bo_size(rbo) >> 20, -+ placement, (unsigned long)rbo->pid); -+ i++; -+ } -+ mutex_unlock(&adev->gem.mutex); -+ return 0; -+} -+ -+static struct drm_info_list amdgpu_debugfs_gem_list[] = { -+ {"amdgpu_gem_info", &amdgpu_debugfs_gem_info, 0, NULL}, -+}; -+#endif -+ -+int amdgpu_gem_debugfs_init(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_gem_list, 1); -+#endif -+ return 0; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c -new file mode 100644 -index 0000000..9f95da4 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c -@@ -0,0 +1,72 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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 <drm/drmP.h> -+#include "amdgpu.h" -+ -+/* -+ * GPU scratch registers helpers function. -+ */ -+/** -+ * amdgpu_gfx_scratch_get - Allocate a scratch register -+ * -+ * @adev: amdgpu_device pointer -+ * @reg: scratch register mmio offset -+ * -+ * Allocate a CP scratch register for use by the driver (all asics). -+ * Returns 0 on success or -EINVAL on failure. -+ */ -+int amdgpu_gfx_scratch_get(struct amdgpu_device *adev, uint32_t *reg) -+{ -+ int i; -+ -+ for (i = 0; i < adev->gfx.scratch.num_reg; i++) { -+ if (adev->gfx.scratch.free[i]) { -+ adev->gfx.scratch.free[i] = false; -+ *reg = adev->gfx.scratch.reg[i]; -+ return 0; -+ } -+ } -+ return -EINVAL; -+} -+ -+/** -+ * amdgpu_gfx_scratch_free - Free a scratch register -+ * -+ * @adev: amdgpu_device pointer -+ * @reg: scratch register mmio offset -+ * -+ * Free a CP scratch register allocated for use by the driver (all asics) -+ */ -+void amdgpu_gfx_scratch_free(struct amdgpu_device *adev, uint32_t reg) -+{ -+ int i; -+ -+ for (i = 0; i < adev->gfx.scratch.num_reg; i++) { -+ if (adev->gfx.scratch.reg[i] == reg) { -+ adev->gfx.scratch.free[i] = true; -+ return; -+ } -+ } -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h -new file mode 100644 -index 0000000..dc06cbd ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h -@@ -0,0 +1,30 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_GFX_H__ -+#define __AMDGPU_GFX_H__ -+ -+int amdgpu_gfx_scratch_get(struct amdgpu_device *adev, uint32_t *reg); -+void amdgpu_gfx_scratch_free(struct amdgpu_device *adev, uint32_t reg); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c -new file mode 100644 -index 0000000..31a6763 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c -@@ -0,0 +1,395 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <linux/export.h> -+ -+#include <drm/drmP.h> -+#include <drm/drm_edid.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_i2c.h" -+#include "amdgpu_atombios.h" -+#include "atom.h" -+#include "atombios_dp.h" -+#include "atombios_i2c.h" -+ -+/* bit banging i2c */ -+static int amdgpu_i2c_pre_xfer(struct i2c_adapter *i2c_adap) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); -+ struct amdgpu_device *adev = i2c->dev->dev_private; -+ struct amdgpu_i2c_bus_rec *rec = &i2c->rec; -+ uint32_t temp; -+ -+ mutex_lock(&i2c->mutex); -+ -+ /* switch the pads to ddc mode */ -+ if (rec->hw_capable) { -+ temp = RREG32(rec->mask_clk_reg); -+ temp &= ~(1 << 16); -+ WREG32(rec->mask_clk_reg, temp); -+ } -+ -+ /* clear the output pin values */ -+ temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask; -+ WREG32(rec->a_clk_reg, temp); -+ -+ temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask; -+ WREG32(rec->a_data_reg, temp); -+ -+ /* set the pins to input */ -+ temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask; -+ WREG32(rec->en_clk_reg, temp); -+ -+ temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask; -+ WREG32(rec->en_data_reg, temp); -+ -+ /* mask the gpio pins for software use */ -+ temp = RREG32(rec->mask_clk_reg) | rec->mask_clk_mask; -+ WREG32(rec->mask_clk_reg, temp); -+ temp = RREG32(rec->mask_clk_reg); -+ -+ temp = RREG32(rec->mask_data_reg) | rec->mask_data_mask; -+ WREG32(rec->mask_data_reg, temp); -+ temp = RREG32(rec->mask_data_reg); -+ -+ return 0; -+} -+ -+static void amdgpu_i2c_post_xfer(struct i2c_adapter *i2c_adap) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); -+ struct amdgpu_device *adev = i2c->dev->dev_private; -+ struct amdgpu_i2c_bus_rec *rec = &i2c->rec; -+ uint32_t temp; -+ -+ /* unmask the gpio pins for software use */ -+ temp = RREG32(rec->mask_clk_reg) & ~rec->mask_clk_mask; -+ WREG32(rec->mask_clk_reg, temp); -+ temp = RREG32(rec->mask_clk_reg); -+ -+ temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask; -+ WREG32(rec->mask_data_reg, temp); -+ temp = RREG32(rec->mask_data_reg); -+ -+ mutex_unlock(&i2c->mutex); -+} -+ -+static int amdgpu_i2c_get_clock(void *i2c_priv) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_priv; -+ struct amdgpu_device *adev = i2c->dev->dev_private; -+ struct amdgpu_i2c_bus_rec *rec = &i2c->rec; -+ uint32_t val; -+ -+ /* read the value off the pin */ -+ val = RREG32(rec->y_clk_reg); -+ val &= rec->y_clk_mask; -+ -+ return (val != 0); -+} -+ -+ -+static int amdgpu_i2c_get_data(void *i2c_priv) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_priv; -+ struct amdgpu_device *adev = i2c->dev->dev_private; -+ struct amdgpu_i2c_bus_rec *rec = &i2c->rec; -+ uint32_t val; -+ -+ /* read the value off the pin */ -+ val = RREG32(rec->y_data_reg); -+ val &= rec->y_data_mask; -+ -+ return (val != 0); -+} -+ -+static void amdgpu_i2c_set_clock(void *i2c_priv, int clock) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_priv; -+ struct amdgpu_device *adev = i2c->dev->dev_private; -+ struct amdgpu_i2c_bus_rec *rec = &i2c->rec; -+ uint32_t val; -+ -+ /* set pin direction */ -+ val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask; -+ val |= clock ? 0 : rec->en_clk_mask; -+ WREG32(rec->en_clk_reg, val); -+} -+ -+static void amdgpu_i2c_set_data(void *i2c_priv, int data) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_priv; -+ struct amdgpu_device *adev = i2c->dev->dev_private; -+ struct amdgpu_i2c_bus_rec *rec = &i2c->rec; -+ uint32_t val; -+ -+ /* set pin direction */ -+ val = RREG32(rec->en_data_reg) & ~rec->en_data_mask; -+ val |= data ? 0 : rec->en_data_mask; -+ WREG32(rec->en_data_reg, val); -+} -+ -+static const struct i2c_algorithm amdgpu_atombios_i2c_algo = { -+ .master_xfer = amdgpu_atombios_i2c_xfer, -+ .functionality = amdgpu_atombios_i2c_func, -+}; -+ -+struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev, -+ struct amdgpu_i2c_bus_rec *rec, -+ const char *name) -+{ -+ struct amdgpu_i2c_chan *i2c; -+ int ret; -+ -+ /* don't add the mm_i2c bus unless hw_i2c is enabled */ -+ if (rec->mm_i2c && (amdgpu_hw_i2c == 0)) -+ return NULL; -+ -+ i2c = kzalloc(sizeof(struct amdgpu_i2c_chan), GFP_KERNEL); -+ if (i2c == NULL) -+ return NULL; -+ -+ i2c->rec = *rec; -+ i2c->adapter.owner = THIS_MODULE; -+ i2c->adapter.class = I2C_CLASS_DDC; -+ i2c->adapter.dev.parent = &dev->pdev->dev; -+ i2c->dev = dev; -+ i2c_set_adapdata(&i2c->adapter, i2c); -+ mutex_init(&i2c->mutex); -+ if (rec->hw_capable && -+ amdgpu_hw_i2c) { -+ /* hw i2c using atom */ -+ snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), -+ "AMDGPU i2c hw bus %s", name); -+ i2c->adapter.algo = &amdgpu_atombios_i2c_algo; -+ ret = i2c_add_adapter(&i2c->adapter); -+ if (ret) { -+ DRM_ERROR("Failed to register hw i2c %s\n", name); -+ goto out_free; -+ } -+ } else { -+ /* set the amdgpu bit adapter */ -+ snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), -+ "AMDGPU i2c bit bus %s", name); -+ i2c->adapter.algo_data = &i2c->bit; -+ i2c->bit.pre_xfer = amdgpu_i2c_pre_xfer; -+ i2c->bit.post_xfer = amdgpu_i2c_post_xfer; -+ i2c->bit.setsda = amdgpu_i2c_set_data; -+ i2c->bit.setscl = amdgpu_i2c_set_clock; -+ i2c->bit.getsda = amdgpu_i2c_get_data; -+ i2c->bit.getscl = amdgpu_i2c_get_clock; -+ i2c->bit.udelay = 10; -+ i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */ -+ i2c->bit.data = i2c; -+ ret = i2c_bit_add_bus(&i2c->adapter); -+ if (ret) { -+ DRM_ERROR("Failed to register bit i2c %s\n", name); -+ goto out_free; -+ } -+ } -+ -+ return i2c; -+out_free: -+ kfree(i2c); -+ return NULL; -+ -+} -+ -+void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c) -+{ -+ if (!i2c) -+ return; -+ i2c_del_adapter(&i2c->adapter); -+ kfree(i2c); -+} -+ -+/* Add the default buses */ -+void amdgpu_i2c_init(struct amdgpu_device *adev) -+{ -+ if (amdgpu_hw_i2c) -+ DRM_INFO("hw_i2c forced on, you may experience display detection problems!\n"); -+ -+ if (adev->is_atom_bios) -+ amdgpu_atombios_i2c_init(adev); -+} -+ -+/* remove all the buses */ -+void amdgpu_i2c_fini(struct amdgpu_device *adev) -+{ -+ int i; -+ -+ for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) { -+ if (adev->i2c_bus[i]) { -+ amdgpu_i2c_destroy(adev->i2c_bus[i]); -+ adev->i2c_bus[i] = NULL; -+ } -+ } -+} -+ -+/* Add additional buses */ -+void amdgpu_i2c_add(struct amdgpu_device *adev, -+ struct amdgpu_i2c_bus_rec *rec, -+ const char *name) -+{ -+ struct drm_device *dev = adev->ddev; -+ int i; -+ -+ for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) { -+ if (!adev->i2c_bus[i]) { -+ adev->i2c_bus[i] = amdgpu_i2c_create(dev, rec, name); -+ return; -+ } -+ } -+} -+ -+/* looks up bus based on id */ -+struct amdgpu_i2c_chan * -+amdgpu_i2c_lookup(struct amdgpu_device *adev, -+ struct amdgpu_i2c_bus_rec *i2c_bus) -+{ -+ int i; -+ -+ for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) { -+ if (adev->i2c_bus[i] && -+ (adev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) { -+ return adev->i2c_bus[i]; -+ } -+ } -+ return NULL; -+} -+ -+static void amdgpu_i2c_get_byte(struct amdgpu_i2c_chan *i2c_bus, -+ u8 slave_addr, -+ u8 addr, -+ u8 *val) -+{ -+ u8 out_buf[2]; -+ u8 in_buf[2]; -+ struct i2c_msg msgs[] = { -+ { -+ .addr = slave_addr, -+ .flags = 0, -+ .len = 1, -+ .buf = out_buf, -+ }, -+ { -+ .addr = slave_addr, -+ .flags = I2C_M_RD, -+ .len = 1, -+ .buf = in_buf, -+ } -+ }; -+ -+ out_buf[0] = addr; -+ out_buf[1] = 0; -+ -+ if (i2c_transfer(&i2c_bus->adapter, msgs, 2) == 2) { -+ *val = in_buf[0]; -+ DRM_DEBUG("val = 0x%02x\n", *val); -+ } else { -+ DRM_DEBUG("i2c 0x%02x 0x%02x read failed\n", -+ addr, *val); -+ } -+} -+ -+static void amdgpu_i2c_put_byte(struct amdgpu_i2c_chan *i2c_bus, -+ u8 slave_addr, -+ u8 addr, -+ u8 val) -+{ -+ uint8_t out_buf[2]; -+ struct i2c_msg msg = { -+ .addr = slave_addr, -+ .flags = 0, -+ .len = 2, -+ .buf = out_buf, -+ }; -+ -+ out_buf[0] = addr; -+ out_buf[1] = val; -+ -+ if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1) -+ DRM_DEBUG("i2c 0x%02x 0x%02x write failed\n", -+ addr, val); -+} -+ -+/* ddc router switching */ -+void -+amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector) -+{ -+ u8 val; -+ -+ if (!amdgpu_connector->router.ddc_valid) -+ return; -+ -+ if (!amdgpu_connector->router_bus) -+ return; -+ -+ amdgpu_i2c_get_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x3, &val); -+ val &= ~amdgpu_connector->router.ddc_mux_control_pin; -+ amdgpu_i2c_put_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x3, val); -+ amdgpu_i2c_get_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x1, &val); -+ val &= ~amdgpu_connector->router.ddc_mux_control_pin; -+ val |= amdgpu_connector->router.ddc_mux_state; -+ amdgpu_i2c_put_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x1, val); -+} -+ -+/* clock/data router switching */ -+void -+amdgpu_i2c_router_select_cd_port(struct amdgpu_connector *amdgpu_connector) -+{ -+ u8 val; -+ -+ if (!amdgpu_connector->router.cd_valid) -+ return; -+ -+ if (!amdgpu_connector->router_bus) -+ return; -+ -+ amdgpu_i2c_get_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x3, &val); -+ val &= ~amdgpu_connector->router.cd_mux_control_pin; -+ amdgpu_i2c_put_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x3, val); -+ amdgpu_i2c_get_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x1, &val); -+ val &= ~amdgpu_connector->router.cd_mux_control_pin; -+ val |= amdgpu_connector->router.cd_mux_state; -+ amdgpu_i2c_put_byte(amdgpu_connector->router_bus, -+ amdgpu_connector->router.i2c_addr, -+ 0x1, val); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h -new file mode 100644 -index 0000000..d81e19b ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h -@@ -0,0 +1,44 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_I2C_H__ -+#define __AMDGPU_I2C_H__ -+ -+struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev, -+ struct amdgpu_i2c_bus_rec *rec, -+ const char *name); -+void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c); -+void amdgpu_i2c_init(struct amdgpu_device *adev); -+void amdgpu_i2c_fini(struct amdgpu_device *adev); -+void amdgpu_i2c_add(struct amdgpu_device *adev, -+ struct amdgpu_i2c_bus_rec *rec, -+ const char *name); -+struct amdgpu_i2c_chan * -+amdgpu_i2c_lookup(struct amdgpu_device *adev, -+ struct amdgpu_i2c_bus_rec *i2c_bus); -+void -+amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector); -+void -+amdgpu_i2c_router_select_cd_port(struct amdgpu_connector *amdgpu_connector); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c -new file mode 100644 -index 0000000..847cab2 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c -@@ -0,0 +1,345 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ * Christian König -+ */ -+#include <linux/seq_file.h> -+#include <linux/slab.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "atom.h" -+ -+/* -+ * IB -+ * IBs (Indirect Buffers) and areas of GPU accessible memory where -+ * commands are stored. You can put a pointer to the IB in the -+ * command ring and the hw will fetch the commands from the IB -+ * and execute them. Generally userspace acceleration drivers -+ * produce command buffers which are send to the kernel and -+ * put in IBs for execution by the requested ring. -+ */ -+static int amdgpu_debugfs_sa_init(struct amdgpu_device *adev); -+ -+/** -+ * amdgpu_ib_get - request an IB (Indirect Buffer) -+ * -+ * @ring: ring index the IB is associated with -+ * @size: requested IB size -+ * @ib: IB object returned -+ * -+ * Request an IB (all asics). IBs are allocated using the -+ * suballocator. -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_ib_get(struct amdgpu_ring *ring, struct amdgpu_vm *vm, -+ unsigned size, struct amdgpu_ib *ib) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ int r; -+ -+ if (size) { -+ r = amdgpu_sa_bo_new(adev, &adev->ring_tmp_bo, -+ &ib->sa_bo, size, 256); -+ if (r) { -+ dev_err(adev->dev, "failed to get a new IB (%d)\n", r); -+ return r; -+ } -+ -+ ib->ptr = amdgpu_sa_bo_cpu_addr(ib->sa_bo); -+ -+ if (!vm) -+ ib->gpu_addr = amdgpu_sa_bo_gpu_addr(ib->sa_bo); -+ else -+ ib->gpu_addr = 0; -+ -+ } else { -+ ib->sa_bo = NULL; -+ ib->ptr = NULL; -+ ib->gpu_addr = 0; -+ } -+ -+ amdgpu_sync_create(&ib->sync); -+ -+ ib->ring = ring; -+ ib->fence = NULL; -+ ib->user = NULL; -+ ib->vm = vm; -+ ib->is_const_ib = false; -+ ib->gds_base = 0; -+ ib->gds_size = 0; -+ ib->gws_base = 0; -+ ib->gws_size = 0; -+ ib->oa_base = 0; -+ ib->oa_size = 0; -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_ib_free - free an IB (Indirect Buffer) -+ * -+ * @adev: amdgpu_device pointer -+ * @ib: IB object to free -+ * -+ * Free an IB (all asics). -+ */ -+void amdgpu_ib_free(struct amdgpu_device *adev, struct amdgpu_ib *ib) -+{ -+ amdgpu_sync_free(adev, &ib->sync, ib->fence); -+ amdgpu_sa_bo_free(adev, &ib->sa_bo, ib->fence); -+ amdgpu_fence_unref(&ib->fence); -+} -+ -+/** -+ * amdgpu_ib_schedule - schedule an IB (Indirect Buffer) on the ring -+ * -+ * @adev: amdgpu_device pointer -+ * @num_ibs: number of IBs to schedule -+ * @ibs: IB objects to schedule -+ * @owner: owner for creating the fences -+ * -+ * Schedule an IB on the associated ring (all asics). -+ * Returns 0 on success, error on failure. -+ * -+ * On SI, there are two parallel engines fed from the primary ring, -+ * the CE (Constant Engine) and the DE (Drawing Engine). Since -+ * resource descriptors have moved to memory, the CE allows you to -+ * prime the caches while the DE is updating register state so that -+ * the resource descriptors will be already in cache when the draw is -+ * processed. To accomplish this, the userspace driver submits two -+ * IBs, one for the CE and one for the DE. If there is a CE IB (called -+ * a CONST_IB), it will be put on the ring prior to the DE IB. Prior -+ * to SI there was just a DE IB. -+ */ -+int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs, -+ struct amdgpu_ib *ibs, void *owner) -+{ -+ struct amdgpu_ring *ring; -+ struct amdgpu_vm *vm = ibs->vm; -+ struct amdgpu_ib *ib = &ibs[0]; -+ unsigned i; -+ int r = 0; -+ bool flush_hdp = true; -+ -+ if (num_ibs == 0) -+ return -EINVAL; -+ -+ ring = ibs->ring; -+ if (!ring->ready) { -+ dev_err(adev->dev, "couldn't schedule ib\n"); -+ return -EINVAL; -+ } -+ -+ r = amdgpu_ring_lock(ring, (256 + AMDGPU_NUM_SYNCS * 8) * num_ibs); -+ if (r) { -+ dev_err(adev->dev, "scheduling IB failed (%d).\n", r); -+ return r; -+ } -+ -+ if (vm) { -+ /* grab a vm id if necessary */ -+ struct amdgpu_fence *vm_id_fence = NULL; -+ vm_id_fence = amdgpu_vm_grab_id(ibs->ring, ibs->vm); -+ amdgpu_sync_fence(&ibs->sync, vm_id_fence); -+ } -+ -+ r = amdgpu_sync_rings(&ibs->sync, ring); -+ if (r) { -+ amdgpu_ring_unlock_undo(ring); -+ dev_err(adev->dev, "failed to sync rings (%d)\n", r); -+ return r; -+ } -+ -+ if (vm) { -+ /* do context switch */ -+ amdgpu_vm_flush(ring, vm, ib->sync.last_vm_update); -+ } -+ -+ if (ring->funcs->emit_gds_switch && ib->vm && ib->gds_needed) -+ amdgpu_ring_emit_gds_switch(ring, ib->vm->ids[ring->idx].id, -+ ib->gds_base, ib->gds_size, -+ ib->gws_base, ib->gws_size, -+ ib->oa_base, ib->oa_size); -+ -+ for (i = 0; i < num_ibs; ++i) { -+ ib = &ibs[i]; -+ -+ if (ib->ring != ring) { -+ amdgpu_ring_unlock_undo(ring); -+ return -EINVAL; -+ } -+ ib->flush_hdp_writefifo = flush_hdp; -+ flush_hdp = false; -+ amdgpu_ring_emit_ib(ring, ib); -+ } -+ -+ r = amdgpu_fence_emit(ring, owner, &ib->fence); -+ if (r) { -+ dev_err(adev->dev, "failed to emit fence (%d)\n", r); -+ amdgpu_ring_unlock_undo(ring); -+ return r; -+ } -+ -+ /* wrap the last IB with fence */ -+ if (ib->user) { -+ uint64_t addr = amdgpu_bo_gpu_offset(ib->user->bo); -+ addr += ib->user->offset; -+ amdgpu_ring_emit_fence(ring, addr, ib->fence->seq, true); -+ } -+ -+ if (ib->vm) -+ amdgpu_vm_fence(adev, ib->vm, ib->fence); -+ -+ amdgpu_ring_unlock_commit(ring); -+ return 0; -+} -+ -+/** -+ * amdgpu_ib_pool_init - Init the IB (Indirect Buffer) pool -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Initialize the suballocator to manage a pool of memory -+ * for use as IBs (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_ib_pool_init(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->ib_pool_ready) { -+ return 0; -+ } -+ r = amdgpu_sa_bo_manager_init(adev, &adev->ring_tmp_bo, -+ AMDGPU_IB_POOL_SIZE*64*1024, -+ AMDGPU_GPU_PAGE_SIZE, -+ AMDGPU_GEM_DOMAIN_GTT); -+ if (r) { -+ return r; -+ } -+ -+ r = amdgpu_sa_bo_manager_start(adev, &adev->ring_tmp_bo); -+ if (r) { -+ return r; -+ } -+ -+ adev->ib_pool_ready = true; -+ if (amdgpu_debugfs_sa_init(adev)) { -+ dev_err(adev->dev, "failed to register debugfs file for SA\n"); -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_ib_pool_fini - Free the IB (Indirect Buffer) pool -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Tear down the suballocator managing the pool of memory -+ * for use as IBs (all asics). -+ */ -+void amdgpu_ib_pool_fini(struct amdgpu_device *adev) -+{ -+ if (adev->ib_pool_ready) { -+ amdgpu_sa_bo_manager_suspend(adev, &adev->ring_tmp_bo); -+ amdgpu_sa_bo_manager_fini(adev, &adev->ring_tmp_bo); -+ adev->ib_pool_ready = false; -+ } -+} -+ -+/** -+ * amdgpu_ib_ring_tests - test IBs on the rings -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Test an IB (Indirect Buffer) on each ring. -+ * If the test fails, disable the ring. -+ * Returns 0 on success, error if the primary GFX ring -+ * IB test fails. -+ */ -+int amdgpu_ib_ring_tests(struct amdgpu_device *adev) -+{ -+ unsigned i; -+ int r; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ -+ if (!ring || !ring->ready) -+ continue; -+ -+ r = amdgpu_ring_test_ib(ring); -+ if (r) { -+ ring->ready = false; -+ adev->needs_reset = false; -+ -+ if (ring == &adev->gfx.gfx_ring[0]) { -+ /* oh, oh, that's really bad */ -+ DRM_ERROR("amdgpu: failed testing IB on GFX ring (%d).\n", r); -+ adev->accel_working = false; -+ return r; -+ -+ } else { -+ /* still not good, but we can live with it */ -+ DRM_ERROR("amdgpu: failed testing IB on ring %d (%d).\n", i, r); -+ } -+ } -+ } -+ return 0; -+} -+ -+/* -+ * Debugfs info -+ */ -+#if defined(CONFIG_DEBUG_FS) -+ -+static int amdgpu_debugfs_sa_info(struct seq_file *m, void *data) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ amdgpu_sa_bo_dump_debug_info(&adev->ring_tmp_bo, m); -+ -+ return 0; -+ -+} -+ -+static struct drm_info_list amdgpu_debugfs_sa_list[] = { -+ {"amdgpu_sa_info", &amdgpu_debugfs_sa_info, 0, NULL}, -+}; -+ -+#endif -+ -+static int amdgpu_debugfs_sa_init(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_sa_list, 1); -+#else -+ return 0; -+#endif -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c -new file mode 100644 -index 0000000..db5422e ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c -@@ -0,0 +1,216 @@ -+/* -+ * 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 <drm/drmP.h> -+#include "amdgpu.h" -+#include "amdgpu_ih.h" -+ -+/** -+ * amdgpu_ih_ring_alloc - allocate memory for the IH ring -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Allocate a ring buffer for the interrupt controller. -+ * Returns 0 for success, errors for failure. -+ */ -+static int amdgpu_ih_ring_alloc(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ /* Allocate ring buffer */ -+ if (adev->irq.ih.ring_obj == NULL) { -+ r = amdgpu_bo_create(adev, adev->irq.ih.ring_size, -+ PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_GTT, 0, -+ NULL, &adev->irq.ih.ring_obj); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to create ih ring buffer (%d).\n", r); -+ return r; -+ } -+ r = amdgpu_bo_reserve(adev->irq.ih.ring_obj, false); -+ if (unlikely(r != 0)) -+ return r; -+ r = amdgpu_bo_pin(adev->irq.ih.ring_obj, -+ AMDGPU_GEM_DOMAIN_GTT, -+ &adev->irq.ih.gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(adev->irq.ih.ring_obj); -+ DRM_ERROR("amdgpu: failed to pin ih ring buffer (%d).\n", r); -+ return r; -+ } -+ r = amdgpu_bo_kmap(adev->irq.ih.ring_obj, -+ (void **)&adev->irq.ih.ring); -+ amdgpu_bo_unreserve(adev->irq.ih.ring_obj); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to map ih ring buffer (%d).\n", r); -+ return r; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_ih_ring_init - initialize the IH state -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Initializes the IH state and allocates a buffer -+ * for the IH ring buffer. -+ * Returns 0 for success, errors for failure. -+ */ -+int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size, -+ bool use_bus_addr) -+{ -+ u32 rb_bufsz; -+ int r; -+ -+ /* Align ring size */ -+ rb_bufsz = order_base_2(ring_size / 4); -+ ring_size = (1 << rb_bufsz) * 4; -+ adev->irq.ih.ring_size = ring_size; -+ adev->irq.ih.ptr_mask = adev->irq.ih.ring_size - 1; -+ adev->irq.ih.rptr = 0; -+ adev->irq.ih.use_bus_addr = use_bus_addr; -+ -+ if (adev->irq.ih.use_bus_addr) { -+ if (!adev->irq.ih.ring) { -+ /* add 8 bytes for the rptr/wptr shadows and -+ * add them to the end of the ring allocation. -+ */ -+ adev->irq.ih.ring = kzalloc(adev->irq.ih.ring_size + 8, GFP_KERNEL); -+ if (adev->irq.ih.ring == NULL) -+ return -ENOMEM; -+ adev->irq.ih.rb_dma_addr = pci_map_single(adev->pdev, -+ (void *)adev->irq.ih.ring, -+ adev->irq.ih.ring_size, -+ PCI_DMA_BIDIRECTIONAL); -+ if (pci_dma_mapping_error(adev->pdev, adev->irq.ih.rb_dma_addr)) { -+ dev_err(&adev->pdev->dev, "Failed to DMA MAP the IH RB page\n"); -+ kfree((void *)adev->irq.ih.ring); -+ return -ENOMEM; -+ } -+ adev->irq.ih.wptr_offs = (adev->irq.ih.ring_size / 4) + 0; -+ adev->irq.ih.rptr_offs = (adev->irq.ih.ring_size / 4) + 1; -+ } -+ return 0; -+ } else { -+ r = amdgpu_wb_get(adev, &adev->irq.ih.wptr_offs); -+ if (r) { -+ dev_err(adev->dev, "(%d) ih wptr_offs wb alloc failed\n", r); -+ return r; -+ } -+ -+ r = amdgpu_wb_get(adev, &adev->irq.ih.rptr_offs); -+ if (r) { -+ amdgpu_wb_free(adev, adev->irq.ih.wptr_offs); -+ dev_err(adev->dev, "(%d) ih rptr_offs wb alloc failed\n", r); -+ return r; -+ } -+ -+ return amdgpu_ih_ring_alloc(adev); -+ } -+} -+ -+/** -+ * amdgpu_ih_ring_fini - tear down the IH state -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Tears down the IH state and frees buffer -+ * used for the IH ring buffer. -+ */ -+void amdgpu_ih_ring_fini(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->irq.ih.use_bus_addr) { -+ if (adev->irq.ih.ring) { -+ /* add 8 bytes for the rptr/wptr shadows and -+ * add them to the end of the ring allocation. -+ */ -+ pci_unmap_single(adev->pdev, adev->irq.ih.rb_dma_addr, -+ adev->irq.ih.ring_size + 8, PCI_DMA_BIDIRECTIONAL); -+ kfree((void *)adev->irq.ih.ring); -+ adev->irq.ih.ring = NULL; -+ } -+ } else { -+ if (adev->irq.ih.ring_obj) { -+ r = amdgpu_bo_reserve(adev->irq.ih.ring_obj, false); -+ if (likely(r == 0)) { -+ amdgpu_bo_kunmap(adev->irq.ih.ring_obj); -+ amdgpu_bo_unpin(adev->irq.ih.ring_obj); -+ amdgpu_bo_unreserve(adev->irq.ih.ring_obj); -+ } -+ amdgpu_bo_unref(&adev->irq.ih.ring_obj); -+ adev->irq.ih.ring = NULL; -+ adev->irq.ih.ring_obj = NULL; -+ } -+ amdgpu_wb_free(adev, adev->irq.ih.wptr_offs); -+ amdgpu_wb_free(adev, adev->irq.ih.rptr_offs); -+ } -+} -+ -+/** -+ * amdgpu_ih_process - interrupt handler -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Interrupt hander (VI), walk the IH ring. -+ * Returns irq process return code. -+ */ -+int amdgpu_ih_process(struct amdgpu_device *adev) -+{ -+ struct amdgpu_iv_entry entry; -+ u32 wptr; -+ -+ if (!adev->irq.ih.enabled || adev->shutdown) -+ return IRQ_NONE; -+ -+ wptr = amdgpu_ih_get_wptr(adev); -+ -+restart_ih: -+ /* is somebody else already processing irqs? */ -+ if (atomic_xchg(&adev->irq.ih.lock, 1)) -+ return IRQ_NONE; -+ -+ DRM_DEBUG("%s: rptr %d, wptr %d\n", __func__, adev->irq.ih.rptr, wptr); -+ -+ /* Order reading of wptr vs. reading of IH ring data */ -+ rmb(); -+ -+ while (adev->irq.ih.rptr != wptr) { -+ amdgpu_ih_decode_iv(adev, &entry); -+ adev->irq.ih.rptr &= adev->irq.ih.ptr_mask; -+ -+ amdgpu_irq_dispatch(adev, &entry); -+ } -+ amdgpu_ih_set_rptr(adev); -+ atomic_set(&adev->irq.ih.lock, 0); -+ -+ /* make sure wptr hasn't changed while processing */ -+ wptr = amdgpu_ih_get_wptr(adev); -+ if (wptr != adev->irq.ih.rptr) -+ goto restart_ih; -+ -+ return IRQ_HANDLED; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h -new file mode 100644 -index 0000000..c62b09e ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.h -@@ -0,0 +1,62 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_IH_H__ -+#define __AMDGPU_IH_H__ -+ -+struct amdgpu_device; -+ -+/* -+ * R6xx+ IH ring -+ */ -+struct amdgpu_ih_ring { -+ struct amdgpu_bo *ring_obj; -+ volatile uint32_t *ring; -+ unsigned rptr; -+ unsigned ring_size; -+ uint64_t gpu_addr; -+ uint32_t ptr_mask; -+ atomic_t lock; -+ bool enabled; -+ unsigned wptr_offs; -+ unsigned rptr_offs; -+ u32 doorbell_index; -+ bool use_doorbell; -+ bool use_bus_addr; -+ dma_addr_t rb_dma_addr; /* only used when use_bus_addr = true */ -+}; -+ -+struct amdgpu_iv_entry { -+ unsigned src_id; -+ unsigned src_data; -+ unsigned ring_id; -+ unsigned vm_id; -+ unsigned pas_id; -+}; -+ -+int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size, -+ bool use_bus_addr); -+void amdgpu_ih_ring_fini(struct amdgpu_device *adev); -+int amdgpu_ih_process(struct amdgpu_device *adev); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ioc32.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ioc32.c -new file mode 100644 -index 0000000..2648291 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ioc32.c -@@ -0,0 +1,47 @@ -+/** -+ * \file amdgpu_ioc32.c -+ * -+ * 32-bit ioctl compatibility routines for the AMDGPU DRM. -+ * -+ * \author Paul Mackerras <paulus@samba.org> -+ * -+ * Copyright (C) Paul Mackerras 2005 -+ * 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 -+ * THE AUTHOR 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 <linux/compat.h> -+ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu_drv.h" -+ -+long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -+{ -+ unsigned int nr = DRM_IOCTL_NR(cmd); -+ int ret; -+ -+ if (nr < DRM_COMMAND_BASE) -+ return drm_compat_ioctl(filp, cmd, arg); -+ -+ ret = amdgpu_drm_ioctl(filp, cmd, arg); -+ -+ return ret; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c -new file mode 100644 -index 0000000..2187960 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c -@@ -0,0 +1,456 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_ih.h" -+#include "atom.h" -+#include "amdgpu_connectors.h" -+ -+#include <linux/pm_runtime.h> -+ -+#define AMDGPU_WAIT_IDLE_TIMEOUT 200 -+ -+/* -+ * Handle hotplug events outside the interrupt handler proper. -+ */ -+/** -+ * amdgpu_hotplug_work_func - display hotplug work handler -+ * -+ * @work: work struct -+ * -+ * This is the hot plug event work handler (all asics). -+ * The work gets scheduled from the irq handler if there -+ * was a hot plug interrupt. It walks the connector table -+ * and calls the hotplug handler for each one, then sends -+ * a drm hotplug event to alert userspace. -+ */ -+static void amdgpu_hotplug_work_func(struct work_struct *work) -+{ -+ struct amdgpu_device *adev = container_of(work, struct amdgpu_device, -+ hotplug_work); -+ struct drm_device *dev = adev->ddev; -+ struct drm_mode_config *mode_config = &dev->mode_config; -+ struct drm_connector *connector; -+ -+ if (mode_config->num_connector) { -+ list_for_each_entry(connector, &mode_config->connector_list, head) -+ amdgpu_connector_hotplug(connector); -+ } -+ /* Just fire off a uevent and let userspace tell us what to do */ -+ drm_helper_hpd_irq_event(dev); -+} -+ -+/** -+ * amdgpu_irq_reset_work_func - execute gpu reset -+ * -+ * @work: work struct -+ * -+ * Execute scheduled gpu reset (cayman+). -+ * This function is called when the irq handler -+ * thinks we need a gpu reset. -+ */ -+static void amdgpu_irq_reset_work_func(struct work_struct *work) -+{ -+ struct amdgpu_device *adev = container_of(work, struct amdgpu_device, -+ reset_work); -+ -+ amdgpu_gpu_reset(adev); -+} -+ -+/* Disable *all* interrupts */ -+static void amdgpu_irq_disable_all(struct amdgpu_device *adev) -+{ -+ unsigned long irqflags; -+ unsigned i, j; -+ int r; -+ -+ spin_lock_irqsave(&adev->irq.lock, irqflags); -+ for (i = 0; i < AMDGPU_MAX_IRQ_SRC_ID; ++i) { -+ struct amdgpu_irq_src *src = adev->irq.sources[i]; -+ -+ if (!src || !src->funcs->set || !src->num_types) -+ continue; -+ -+ for (j = 0; j < src->num_types; ++j) { -+ atomic_set(&src->enabled_types[j], 0); -+ r = src->funcs->set(adev, src, j, -+ AMDGPU_IRQ_STATE_DISABLE); -+ if (r) -+ DRM_ERROR("error disabling interrupt (%d)\n", -+ r); -+ } -+ } -+ spin_unlock_irqrestore(&adev->irq.lock, irqflags); -+} -+ -+/** -+ * amdgpu_irq_preinstall - drm irq preinstall callback -+ * -+ * @dev: drm dev pointer -+ * -+ * Gets the hw ready to enable irqs (all asics). -+ * This function disables all interrupt sources on the GPU. -+ */ -+void amdgpu_irq_preinstall(struct drm_device *dev) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ /* Disable *all* interrupts */ -+ amdgpu_irq_disable_all(adev); -+ /* Clear bits */ -+ amdgpu_ih_process(adev); -+} -+ -+/** -+ * amdgpu_irq_postinstall - drm irq preinstall callback -+ * -+ * @dev: drm dev pointer -+ * -+ * Handles stuff to be done after enabling irqs (all asics). -+ * Returns 0 on success. -+ */ -+int amdgpu_irq_postinstall(struct drm_device *dev) -+{ -+ dev->max_vblank_count = 0x001fffff; -+ return 0; -+} -+ -+/** -+ * amdgpu_irq_uninstall - drm irq uninstall callback -+ * -+ * @dev: drm dev pointer -+ * -+ * This function disables all interrupt sources on the GPU (all asics). -+ */ -+void amdgpu_irq_uninstall(struct drm_device *dev) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (adev == NULL) { -+ return; -+ } -+ amdgpu_irq_disable_all(adev); -+} -+ -+/** -+ * amdgpu_irq_handler - irq handler -+ * -+ * @int irq, void *arg: args -+ * -+ * This is the irq handler for the amdgpu driver (all asics). -+ */ -+irqreturn_t amdgpu_irq_handler(int irq, void *arg) -+{ -+ struct drm_device *dev = (struct drm_device *) arg; -+ struct amdgpu_device *adev = dev->dev_private; -+ irqreturn_t ret; -+ -+ ret = amdgpu_ih_process(adev); -+ if (ret == IRQ_HANDLED) -+ pm_runtime_mark_last_busy(dev->dev); -+ return ret; -+} -+ -+/** -+ * amdgpu_msi_ok - asic specific msi checks -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Handles asic specific MSI checks to determine if -+ * MSIs should be enabled on a particular chip (all asics). -+ * Returns true if MSIs should be enabled, false if MSIs -+ * should not be enabled. -+ */ -+static bool amdgpu_msi_ok(struct amdgpu_device *adev) -+{ -+ /* force MSI on */ -+ if (amdgpu_msi == 1) -+ return true; -+ else if (amdgpu_msi == 0) -+ return false; -+ -+ return true; -+} -+ -+/** -+ * amdgpu_irq_init - init driver interrupt info -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Sets up the work irq handlers, vblank init, MSIs, etc. (all asics). -+ * Returns 0 for success, error for failure. -+ */ -+int amdgpu_irq_init(struct amdgpu_device *adev) -+{ -+ int r = 0; -+ -+ spin_lock_init(&adev->irq.lock); -+ r = drm_vblank_init(adev->ddev, adev->mode_info.num_crtc); -+ if (r) { -+ return r; -+ } -+ /* enable msi */ -+ adev->irq.msi_enabled = false; -+ -+ if (amdgpu_msi_ok(adev)) { -+ int ret = pci_enable_msi(adev->pdev); -+ if (!ret) { -+ adev->irq.msi_enabled = true; -+ dev_info(adev->dev, "amdgpu: using MSI.\n"); -+ } -+ } -+ -+ INIT_WORK(&adev->hotplug_work, amdgpu_hotplug_work_func); -+ INIT_WORK(&adev->reset_work, amdgpu_irq_reset_work_func); -+ -+ adev->irq.installed = true; -+ r = drm_irq_install(adev->ddev, adev->ddev->pdev->irq); -+ if (r) { -+ adev->irq.installed = false; -+ flush_work(&adev->hotplug_work); -+ return r; -+ } -+ -+ DRM_INFO("amdgpu: irq initialized.\n"); -+ return 0; -+} -+ -+/** -+ * amdgpu_irq_fini - tear down driver interrupt info -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Tears down the work irq handlers, vblank handlers, MSIs, etc. (all asics). -+ */ -+void amdgpu_irq_fini(struct amdgpu_device *adev) -+{ -+ unsigned i; -+ -+ drm_vblank_cleanup(adev->ddev); -+ if (adev->irq.installed) { -+ drm_irq_uninstall(adev->ddev); -+ adev->irq.installed = false; -+ if (adev->irq.msi_enabled) -+ pci_disable_msi(adev->pdev); -+ flush_work(&adev->hotplug_work); -+ } -+ -+ for (i = 0; i < AMDGPU_MAX_IRQ_SRC_ID; ++i) { -+ struct amdgpu_irq_src *src = adev->irq.sources[i]; -+ -+ if (!src) -+ continue; -+ -+ kfree(src->enabled_types); -+ src->enabled_types = NULL; -+ } -+} -+ -+/** -+ * amdgpu_irq_add_id - register irq source -+ * -+ * @adev: amdgpu device pointer -+ * @src_id: source id for this source -+ * @source: irq source -+ * -+ */ -+int amdgpu_irq_add_id(struct amdgpu_device *adev, unsigned src_id, -+ struct amdgpu_irq_src *source) -+{ -+ if (src_id >= AMDGPU_MAX_IRQ_SRC_ID) -+ return -EINVAL; -+ -+ if (adev->irq.sources[src_id] != NULL) -+ return -EINVAL; -+ -+ if (!source->funcs) -+ return -EINVAL; -+ -+ if (source->num_types && !source->enabled_types) { -+ atomic_t *types; -+ -+ types = kcalloc(source->num_types, sizeof(atomic_t), -+ GFP_KERNEL); -+ if (!types) -+ return -ENOMEM; -+ -+ source->enabled_types = types; -+ } -+ -+ adev->irq.sources[src_id] = source; -+ return 0; -+} -+ -+/** -+ * amdgpu_irq_dispatch - dispatch irq to IP blocks -+ * -+ * @adev: amdgpu device pointer -+ * @entry: interrupt vector -+ * -+ * Dispatches the irq to the different IP blocks -+ */ -+void amdgpu_irq_dispatch(struct amdgpu_device *adev, -+ struct amdgpu_iv_entry *entry) -+{ -+ unsigned src_id = entry->src_id; -+ struct amdgpu_irq_src *src; -+ int r; -+ -+ if (src_id >= AMDGPU_MAX_IRQ_SRC_ID) { -+ DRM_DEBUG("Invalid src_id in IV: %d\n", src_id); -+ return; -+ } -+ -+ src = adev->irq.sources[src_id]; -+ if (!src) { -+ DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id); -+ return; -+ } -+ -+ r = src->funcs->process(adev, src, entry); -+ if (r) -+ DRM_ERROR("error processing interrupt (%d)\n", r); -+} -+ -+/** -+ * amdgpu_irq_update - update hw interrupt state -+ * -+ * @adev: amdgpu device pointer -+ * @src: interrupt src you want to enable -+ * @type: type of interrupt you want to update -+ * -+ * Updates the interrupt state for a specific src (all asics). -+ */ -+int amdgpu_irq_update(struct amdgpu_device *adev, -+ struct amdgpu_irq_src *src, unsigned type) -+{ -+ unsigned long irqflags; -+ enum amdgpu_interrupt_state state; -+ int r; -+ -+ spin_lock_irqsave(&adev->irq.lock, irqflags); -+ -+ /* we need to determine after taking the lock, otherwise -+ we might disable just enabled interrupts again */ -+ if (amdgpu_irq_enabled(adev, src, type)) -+ state = AMDGPU_IRQ_STATE_ENABLE; -+ else -+ state = AMDGPU_IRQ_STATE_DISABLE; -+ -+ r = src->funcs->set(adev, src, type, state); -+ spin_unlock_irqrestore(&adev->irq.lock, irqflags); -+ return r; -+} -+ -+/** -+ * amdgpu_irq_get - enable interrupt -+ * -+ * @adev: amdgpu device pointer -+ * @src: interrupt src you want to enable -+ * @type: type of interrupt you want to enable -+ * -+ * Enables the interrupt type for a specific src (all asics). -+ */ -+int amdgpu_irq_get(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type) -+{ -+ if (!adev->ddev->irq_enabled) -+ return -ENOENT; -+ -+ if (type >= src->num_types) -+ return -EINVAL; -+ -+ if (!src->enabled_types || !src->funcs->set) -+ return -EINVAL; -+ -+ if (atomic_inc_return(&src->enabled_types[type]) == 1) -+ return amdgpu_irq_update(adev, src, type); -+ -+ return 0; -+} -+ -+bool amdgpu_irq_get_delayed(struct amdgpu_device *adev, -+ struct amdgpu_irq_src *src, -+ unsigned type) -+{ -+ if ((type >= src->num_types) || !src->enabled_types) -+ return false; -+ return atomic_inc_return(&src->enabled_types[type]) == 1; -+} -+ -+/** -+ * amdgpu_irq_put - disable interrupt -+ * -+ * @adev: amdgpu device pointer -+ * @src: interrupt src you want to disable -+ * @type: type of interrupt you want to disable -+ * -+ * Disables the interrupt type for a specific src (all asics). -+ */ -+int amdgpu_irq_put(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type) -+{ -+ if (!adev->ddev->irq_enabled) -+ return -ENOENT; -+ -+ if (type >= src->num_types) -+ return -EINVAL; -+ -+ if (!src->enabled_types || !src->funcs->set) -+ return -EINVAL; -+ -+ if (atomic_dec_and_test(&src->enabled_types[type])) -+ return amdgpu_irq_update(adev, src, type); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_irq_enabled - test if irq is enabled or not -+ * -+ * @adev: amdgpu device pointer -+ * @idx: interrupt src you want to test -+ * -+ * Tests if the given interrupt source is enabled or not -+ */ -+bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type) -+{ -+ if (!adev->ddev->irq_enabled) -+ return false; -+ -+ if (type >= src->num_types) -+ return false; -+ -+ if (!src->enabled_types || !src->funcs->set) -+ return false; -+ -+ return !!atomic_read(&src->enabled_types[type]); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h -new file mode 100644 -index 0000000..8299795 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h -@@ -0,0 +1,92 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_IRQ_H__ -+#define __AMDGPU_IRQ_H__ -+ -+#include "amdgpu_ih.h" -+ -+#define AMDGPU_MAX_IRQ_SRC_ID 0x100 -+ -+struct amdgpu_device; -+struct amdgpu_iv_entry; -+ -+enum amdgpu_interrupt_state { -+ AMDGPU_IRQ_STATE_DISABLE, -+ AMDGPU_IRQ_STATE_ENABLE, -+}; -+ -+struct amdgpu_irq_src { -+ unsigned num_types; -+ atomic_t *enabled_types; -+ const struct amdgpu_irq_src_funcs *funcs; -+}; -+ -+/* provided by interrupt generating IP blocks */ -+struct amdgpu_irq_src_funcs { -+ int (*set)(struct amdgpu_device *adev, struct amdgpu_irq_src *source, -+ unsigned type, enum amdgpu_interrupt_state state); -+ -+ int (*process)(struct amdgpu_device *adev, -+ struct amdgpu_irq_src *source, -+ struct amdgpu_iv_entry *entry); -+}; -+ -+struct amdgpu_irq { -+ bool installed; -+ spinlock_t lock; -+ /* interrupt sources */ -+ struct amdgpu_irq_src *sources[AMDGPU_MAX_IRQ_SRC_ID]; -+ -+ /* status, etc. */ -+ bool msi_enabled; /* msi enabled */ -+ -+ /* interrupt ring */ -+ struct amdgpu_ih_ring ih; -+ const struct amdgpu_ih_funcs *ih_funcs; -+}; -+ -+void amdgpu_irq_preinstall(struct drm_device *dev); -+int amdgpu_irq_postinstall(struct drm_device *dev); -+void amdgpu_irq_uninstall(struct drm_device *dev); -+irqreturn_t amdgpu_irq_handler(int irq, void *arg); -+ -+int amdgpu_irq_init(struct amdgpu_device *adev); -+void amdgpu_irq_fini(struct amdgpu_device *adev); -+int amdgpu_irq_add_id(struct amdgpu_device *adev, unsigned src_id, -+ struct amdgpu_irq_src *source); -+void amdgpu_irq_dispatch(struct amdgpu_device *adev, -+ struct amdgpu_iv_entry *entry); -+int amdgpu_irq_update(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type); -+int amdgpu_irq_get(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type); -+bool amdgpu_irq_get_delayed(struct amdgpu_device *adev, -+ struct amdgpu_irq_src *src, -+ unsigned type); -+int amdgpu_irq_put(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type); -+bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src, -+ unsigned type); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c -new file mode 100644 -index 0000000..c271da3 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c -@@ -0,0 +1,674 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include <drm/amdgpu_drm.h> -+#include "amdgpu_uvd.h" -+#include "amdgpu_vce.h" -+ -+#include <linux/vga_switcheroo.h> -+#include <linux/slab.h> -+#include <linux/pm_runtime.h> -+ -+#if defined(CONFIG_VGA_SWITCHEROO) -+bool amdgpu_has_atpx(void); -+#else -+static inline bool amdgpu_has_atpx(void) { return false; } -+#endif -+ -+/** -+ * amdgpu_driver_unload_kms - Main unload function for KMS. -+ * -+ * @dev: drm dev pointer -+ * -+ * This is the main unload function for KMS (all asics). -+ * Returns 0 on success. -+ */ -+int amdgpu_driver_unload_kms(struct drm_device *dev) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (adev == NULL) -+ return 0; -+ -+ if (adev->rmmio == NULL) -+ goto done_free; -+ -+ pm_runtime_get_sync(dev->dev); -+ -+ amdgpu_acpi_fini(adev); -+ -+ amdgpu_device_fini(adev); -+ -+done_free: -+ kfree(adev); -+ dev->dev_private = NULL; -+ return 0; -+} -+ -+/** -+ * amdgpu_driver_load_kms - Main load function for KMS. -+ * -+ * @dev: drm dev pointer -+ * @flags: device flags -+ * -+ * This is the main load function for KMS (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags) -+{ -+ struct amdgpu_device *adev; -+ int r, acpi_status; -+ -+ adev = kzalloc(sizeof(struct amdgpu_device), GFP_KERNEL); -+ if (adev == NULL) { -+ return -ENOMEM; -+ } -+ dev->dev_private = (void *)adev; -+ -+ if ((amdgpu_runtime_pm != 0) && -+ amdgpu_has_atpx() && -+ ((flags & AMDGPU_IS_APU) == 0)) -+ flags |= AMDGPU_IS_PX; -+ -+ /* amdgpu_device_init should report only fatal error -+ * like memory allocation failure or iomapping failure, -+ * or memory manager initialization failure, it must -+ * properly initialize the GPU MC controller and permit -+ * VRAM allocation -+ */ -+ r = amdgpu_device_init(adev, dev, dev->pdev, flags); -+ if (r) { -+ dev_err(&dev->pdev->dev, "Fatal error during GPU init\n"); -+ goto out; -+ } -+ -+ /* Call ACPI methods: require modeset init -+ * but failure is not fatal -+ */ -+ if (!r) { -+ acpi_status = amdgpu_acpi_init(adev); -+ if (acpi_status) -+ dev_dbg(&dev->pdev->dev, -+ "Error during ACPI methods call\n"); -+ } -+ -+ if (amdgpu_device_is_px(dev)) { -+ pm_runtime_use_autosuspend(dev->dev); -+ pm_runtime_set_autosuspend_delay(dev->dev, 5000); -+ pm_runtime_set_active(dev->dev); -+ pm_runtime_allow(dev->dev); -+ pm_runtime_mark_last_busy(dev->dev); -+ pm_runtime_put_autosuspend(dev->dev); -+ } -+ -+out: -+ if (r) -+ amdgpu_driver_unload_kms(dev); -+ -+ -+ return r; -+} -+ -+/* -+ * Userspace get information ioctl -+ */ -+/** -+ * amdgpu_info_ioctl - answer a device specific request. -+ * -+ * @adev: amdgpu device pointer -+ * @data: request object -+ * @filp: drm filp -+ * -+ * This function is used to pass device specific parameters to the userspace -+ * drivers. Examples include: pci device id, pipeline parms, tiling params, -+ * etc. (all asics). -+ * Returns 0 on success, -EINVAL on failure. -+ */ -+static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_amdgpu_info *info = data; -+ struct amdgpu_mode_info *minfo = &adev->mode_info; -+ void __user *out = (void __user *)(long)info->return_pointer; -+ uint32_t size = info->return_size; -+ struct drm_crtc *crtc; -+ uint32_t ui32 = 0; -+ uint64_t ui64 = 0; -+ int i, found; -+ -+ if (!info->return_size || !info->return_pointer) -+ return -EINVAL; -+ -+ switch (info->query) { -+ case AMDGPU_INFO_ACCEL_WORKING: -+ ui32 = adev->accel_working; -+ return copy_to_user(out, &ui32, min(size, 4u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_CRTC_FROM_ID: -+ for (i = 0, found = 0; i < adev->mode_info.num_crtc; i++) { -+ crtc = (struct drm_crtc *)minfo->crtcs[i]; -+ if (crtc && crtc->base.id == info->mode_crtc.id) { -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ ui32 = amdgpu_crtc->crtc_id; -+ found = 1; -+ break; -+ } -+ } -+ if (!found) { -+ DRM_DEBUG_KMS("unknown crtc id %d\n", info->mode_crtc.id); -+ return -EINVAL; -+ } -+ return copy_to_user(out, &ui32, min(size, 4u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_HW_IP_INFO: { -+ struct drm_amdgpu_info_hw_ip ip = {}; -+ enum amdgpu_ip_block_type type; -+ uint32_t ring_mask = 0; -+ -+ if (info->query_hw_ip.ip_instance >= AMDGPU_HW_IP_INSTANCE_MAX_COUNT) -+ return -EINVAL; -+ -+ switch (info->query_hw_ip.type) { -+ case AMDGPU_HW_IP_GFX: -+ type = AMDGPU_IP_BLOCK_TYPE_GFX; -+ for (i = 0; i < adev->gfx.num_gfx_rings; i++) -+ ring_mask |= ((adev->gfx.gfx_ring[i].ready ? 1 : 0) << i); -+ break; -+ case AMDGPU_HW_IP_COMPUTE: -+ type = AMDGPU_IP_BLOCK_TYPE_GFX; -+ for (i = 0; i < adev->gfx.num_compute_rings; i++) -+ ring_mask |= ((adev->gfx.compute_ring[i].ready ? 1 : 0) << i); -+ break; -+ case AMDGPU_HW_IP_DMA: -+ type = AMDGPU_IP_BLOCK_TYPE_SDMA; -+ ring_mask = adev->sdma[0].ring.ready ? 1 : 0; -+ ring_mask |= ((adev->sdma[1].ring.ready ? 1 : 0) << 1); -+ break; -+ case AMDGPU_HW_IP_UVD: -+ type = AMDGPU_IP_BLOCK_TYPE_UVD; -+ ring_mask = adev->uvd.ring.ready ? 1 : 0; -+ break; -+ case AMDGPU_HW_IP_VCE: -+ type = AMDGPU_IP_BLOCK_TYPE_VCE; -+ for (i = 0; i < AMDGPU_MAX_VCE_RINGS; i++) -+ ring_mask |= ((adev->vce.ring[i].ready ? 1 : 0) << i); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) { -+ if (adev->ip_blocks[i].type == type && -+ adev->ip_block_enabled[i]) { -+ ip.hw_ip_version_major = adev->ip_blocks[i].major; -+ ip.hw_ip_version_minor = adev->ip_blocks[i].minor; -+ ip.capabilities_flags = 0; -+ ip.available_rings = ring_mask; -+ break; -+ } -+ } -+ return copy_to_user(out, &ip, -+ min((size_t)size, sizeof(ip))) ? -EFAULT : 0; -+ } -+ case AMDGPU_INFO_HW_IP_COUNT: { -+ enum amdgpu_ip_block_type type; -+ uint32_t count = 0; -+ -+ switch (info->query_hw_ip.type) { -+ case AMDGPU_HW_IP_GFX: -+ type = AMDGPU_IP_BLOCK_TYPE_GFX; -+ break; -+ case AMDGPU_HW_IP_COMPUTE: -+ type = AMDGPU_IP_BLOCK_TYPE_GFX; -+ break; -+ case AMDGPU_HW_IP_DMA: -+ type = AMDGPU_IP_BLOCK_TYPE_SDMA; -+ break; -+ case AMDGPU_HW_IP_UVD: -+ type = AMDGPU_IP_BLOCK_TYPE_UVD; -+ break; -+ case AMDGPU_HW_IP_VCE: -+ type = AMDGPU_IP_BLOCK_TYPE_VCE; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < adev->num_ip_blocks; i++) -+ if (adev->ip_blocks[i].type == type && -+ adev->ip_block_enabled[i] && -+ count < AMDGPU_HW_IP_INSTANCE_MAX_COUNT) -+ count++; -+ -+ return copy_to_user(out, &count, min(size, 4u)) ? -EFAULT : 0; -+ } -+ case AMDGPU_INFO_TIMESTAMP: -+ ui64 = amdgpu_asic_get_gpu_clock_counter(adev); -+ return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_FW_VERSION: { -+ struct drm_amdgpu_info_firmware fw_info; -+ -+ /* We only support one instance of each IP block right now. */ -+ if (info->query_fw.ip_instance != 0) -+ return -EINVAL; -+ -+ switch (info->query_fw.fw_type) { -+ case AMDGPU_INFO_FW_VCE: -+ fw_info.ver = adev->vce.fw_version; -+ fw_info.feature = adev->vce.fb_version; -+ break; -+ case AMDGPU_INFO_FW_UVD: -+ fw_info.ver = 0; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_GMC: -+ fw_info.ver = adev->mc.fw_version; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_GFX_ME: -+ fw_info.ver = adev->gfx.me_fw_version; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_GFX_PFP: -+ fw_info.ver = adev->gfx.pfp_fw_version; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_GFX_CE: -+ fw_info.ver = adev->gfx.ce_fw_version; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_GFX_RLC: -+ fw_info.ver = adev->gfx.rlc_fw_version; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_GFX_MEC: -+ if (info->query_fw.index == 0) -+ fw_info.ver = adev->gfx.mec_fw_version; -+ else if (info->query_fw.index == 1) -+ fw_info.ver = adev->gfx.mec2_fw_version; -+ else -+ return -EINVAL; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_SMC: -+ fw_info.ver = adev->pm.fw_version; -+ fw_info.feature = 0; -+ break; -+ case AMDGPU_INFO_FW_SDMA: -+ if (info->query_fw.index >= 2) -+ return -EINVAL; -+ fw_info.ver = adev->sdma[info->query_fw.index].fw_version; -+ fw_info.feature = 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return copy_to_user(out, &fw_info, -+ min((size_t)size, sizeof(fw_info))) ? -EFAULT : 0; -+ } -+ case AMDGPU_INFO_NUM_BYTES_MOVED: -+ ui64 = atomic64_read(&adev->num_bytes_moved); -+ return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_VRAM_USAGE: -+ ui64 = atomic64_read(&adev->vram_usage); -+ return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_VIS_VRAM_USAGE: -+ ui64 = atomic64_read(&adev->vram_vis_usage); -+ return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_GTT_USAGE: -+ ui64 = atomic64_read(&adev->gtt_usage); -+ return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; -+ case AMDGPU_INFO_GDS_CONFIG: { -+ struct drm_amdgpu_info_gds gds_info; -+ -+ gds_info.gds_gfx_partition_size = adev->gds.mem.gfx_partition_size >> AMDGPU_GDS_SHIFT; -+ gds_info.compute_partition_size = adev->gds.mem.cs_partition_size >> AMDGPU_GDS_SHIFT; -+ gds_info.gds_total_size = adev->gds.mem.total_size >> AMDGPU_GDS_SHIFT; -+ gds_info.gws_per_gfx_partition = adev->gds.gws.gfx_partition_size >> AMDGPU_GWS_SHIFT; -+ gds_info.gws_per_compute_partition = adev->gds.gws.cs_partition_size >> AMDGPU_GWS_SHIFT; -+ gds_info.oa_per_gfx_partition = adev->gds.oa.gfx_partition_size >> AMDGPU_OA_SHIFT; -+ gds_info.oa_per_compute_partition = adev->gds.oa.cs_partition_size >> AMDGPU_OA_SHIFT; -+ return copy_to_user(out, &gds_info, -+ min((size_t)size, sizeof(gds_info))) ? -EFAULT : 0; -+ } -+ case AMDGPU_INFO_VRAM_GTT: { -+ struct drm_amdgpu_info_vram_gtt vram_gtt; -+ -+ vram_gtt.vram_size = adev->mc.real_vram_size; -+ vram_gtt.vram_cpu_accessible_size = adev->mc.visible_vram_size; -+ vram_gtt.vram_cpu_accessible_size -= adev->vram_pin_size; -+ vram_gtt.gtt_size = adev->mc.gtt_size; -+ vram_gtt.gtt_size -= adev->gart_pin_size; -+ return copy_to_user(out, &vram_gtt, -+ min((size_t)size, sizeof(vram_gtt))) ? -EFAULT : 0; -+ } -+ case AMDGPU_INFO_READ_MMR_REG: { -+ unsigned n, alloc_size = info->read_mmr_reg.count * 4; -+ uint32_t *regs; -+ unsigned se_num = (info->read_mmr_reg.instance >> -+ AMDGPU_INFO_MMR_SE_INDEX_SHIFT) & -+ AMDGPU_INFO_MMR_SE_INDEX_MASK; -+ unsigned sh_num = (info->read_mmr_reg.instance >> -+ AMDGPU_INFO_MMR_SH_INDEX_SHIFT) & -+ AMDGPU_INFO_MMR_SH_INDEX_MASK; -+ -+ /* set full masks if the userspace set all bits -+ * in the bitfields */ -+ if (se_num == AMDGPU_INFO_MMR_SE_INDEX_MASK) -+ se_num = 0xffffffff; -+ if (sh_num == AMDGPU_INFO_MMR_SH_INDEX_MASK) -+ sh_num = 0xffffffff; -+ -+ regs = kmalloc(alloc_size, GFP_KERNEL); -+ if (!regs) -+ return -ENOMEM; -+ -+ for (i = 0; i < info->read_mmr_reg.count; i++) -+ if (amdgpu_asic_read_register(adev, se_num, sh_num, -+ info->read_mmr_reg.dword_offset + i, -+ ®s[i])) { -+ DRM_DEBUG_KMS("unallowed offset %#x\n", -+ info->read_mmr_reg.dword_offset + i); -+ kfree(regs); -+ return -EFAULT; -+ } -+ n = copy_to_user(out, regs, min(size, alloc_size)); -+ kfree(regs); -+ return n ? -EFAULT : 0; -+ } -+ case AMDGPU_INFO_DEV_INFO: { -+ struct drm_amdgpu_info_device dev_info; -+ struct amdgpu_cu_info cu_info; -+ -+ dev_info.device_id = dev->pdev->device; -+ dev_info.chip_rev = adev->rev_id; -+ dev_info.external_rev = adev->external_rev_id; -+ dev_info.pci_rev = dev->pdev->revision; -+ dev_info.family = adev->family; -+ dev_info.num_shader_engines = adev->gfx.config.max_shader_engines; -+ dev_info.num_shader_arrays_per_engine = adev->gfx.config.max_sh_per_se; -+ /* return all clocks in KHz */ -+ dev_info.gpu_counter_freq = amdgpu_asic_get_xclk(adev) * 10; -+ if (adev->pm.dpm_enabled) -+ dev_info.max_engine_clock = -+ adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk * 10; -+ else -+ dev_info.max_engine_clock = adev->pm.default_sclk * 10; -+ dev_info.enabled_rb_pipes_mask = adev->gfx.config.backend_enable_mask; -+ dev_info.num_rb_pipes = adev->gfx.config.max_backends_per_se * -+ adev->gfx.config.max_shader_engines; -+ dev_info.num_hw_gfx_contexts = adev->gfx.config.max_hw_contexts; -+ dev_info._pad = 0; -+ dev_info.ids_flags = 0; -+ if (adev->flags & AMDGPU_IS_APU) -+ dev_info.ids_flags |= AMDGPU_IDS_FLAGS_FUSION; -+ dev_info.virtual_address_offset = AMDGPU_VA_RESERVED_SIZE; -+ dev_info.virtual_address_alignment = max(PAGE_SIZE, 0x10000UL); -+ dev_info.pte_fragment_size = (1 << AMDGPU_LOG2_PAGES_PER_FRAG) * -+ AMDGPU_GPU_PAGE_SIZE; -+ dev_info.gart_page_size = AMDGPU_GPU_PAGE_SIZE; -+ -+ amdgpu_asic_get_cu_info(adev, &cu_info); -+ dev_info.cu_active_number = cu_info.number; -+ dev_info.cu_ao_mask = cu_info.ao_cu_mask; -+ memcpy(&dev_info.cu_bitmap[0], &cu_info.bitmap[0], sizeof(cu_info.bitmap)); -+ -+ return copy_to_user(out, &dev_info, -+ min((size_t)size, sizeof(dev_info))) ? -EFAULT : 0; -+ } -+ default: -+ DRM_DEBUG_KMS("Invalid request %d\n", info->query); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+ -+/* -+ * Outdated mess for old drm with Xorg being in charge (void function now). -+ */ -+/** -+ * amdgpu_driver_firstopen_kms - drm callback for last close -+ * -+ * @dev: drm dev pointer -+ * -+ * Switch vga switcheroo state after last close (all asics). -+ */ -+void amdgpu_driver_lastclose_kms(struct drm_device *dev) -+{ -+ vga_switcheroo_process_delayed_switch(); -+} -+ -+/** -+ * amdgpu_driver_open_kms - drm callback for open -+ * -+ * @dev: drm dev pointer -+ * @file_priv: drm file -+ * -+ * On device open, init vm on cayman+ (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_fpriv *fpriv; -+ int r; -+ -+ file_priv->driver_priv = NULL; -+ -+ r = pm_runtime_get_sync(dev->dev); -+ if (r < 0) -+ return r; -+ -+ fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); -+ if (unlikely(!fpriv)) -+ return -ENOMEM; -+ -+ r = amdgpu_vm_init(adev, &fpriv->vm); -+ if (r) -+ goto error_free; -+ -+ mutex_init(&fpriv->bo_list_lock); -+ idr_init(&fpriv->bo_list_handles); -+ -+ /* init context manager */ -+ mutex_init(&fpriv->ctx_mgr.hlock); -+ idr_init(&fpriv->ctx_mgr.ctx_handles); -+ fpriv->ctx_mgr.adev = adev; -+ -+ file_priv->driver_priv = fpriv; -+ -+ pm_runtime_mark_last_busy(dev->dev); -+ pm_runtime_put_autosuspend(dev->dev); -+ return 0; -+ -+error_free: -+ kfree(fpriv); -+ -+ return r; -+} -+ -+/** -+ * amdgpu_driver_postclose_kms - drm callback for post close -+ * -+ * @dev: drm dev pointer -+ * @file_priv: drm file -+ * -+ * On device post close, tear down vm on cayman+ (all asics). -+ */ -+void amdgpu_driver_postclose_kms(struct drm_device *dev, -+ struct drm_file *file_priv) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_fpriv *fpriv = file_priv->driver_priv; -+ struct amdgpu_bo_list *list; -+ int handle; -+ -+ if (!fpriv) -+ return; -+ -+ amdgpu_vm_fini(adev, &fpriv->vm); -+ -+ idr_for_each_entry(&fpriv->bo_list_handles, list, handle) -+ amdgpu_bo_list_free(list); -+ -+ idr_destroy(&fpriv->bo_list_handles); -+ mutex_destroy(&fpriv->bo_list_lock); -+ -+ /* release context */ -+ amdgpu_ctx_fini(fpriv); -+ -+ kfree(fpriv); -+ file_priv->driver_priv = NULL; -+} -+ -+/** -+ * amdgpu_driver_preclose_kms - drm callback for pre close -+ * -+ * @dev: drm dev pointer -+ * @file_priv: drm file -+ * -+ * On device pre close, tear down hyperz and cmask filps on r1xx-r5xx -+ * (all asics). -+ */ -+void amdgpu_driver_preclose_kms(struct drm_device *dev, -+ struct drm_file *file_priv) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ amdgpu_uvd_free_handles(adev, file_priv); -+ amdgpu_vce_free_handles(adev, file_priv); -+} -+ -+/* -+ * VBlank related functions. -+ */ -+/** -+ * amdgpu_get_vblank_counter_kms - get frame count -+ * -+ * @dev: drm dev pointer -+ * @crtc: crtc to get the frame count from -+ * -+ * Gets the frame count on the requested crtc (all asics). -+ * Returns frame count on success, -EINVAL on failure. -+ */ -+u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, int crtc) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (crtc < 0 || crtc >= adev->mode_info.num_crtc) { -+ DRM_ERROR("Invalid crtc %d\n", crtc); -+ return -EINVAL; -+ } -+ -+ return amdgpu_display_vblank_get_counter(adev, crtc); -+} -+ -+/** -+ * amdgpu_enable_vblank_kms - enable vblank interrupt -+ * -+ * @dev: drm dev pointer -+ * @crtc: crtc to enable vblank interrupt for -+ * -+ * Enable the interrupt on the requested crtc (all asics). -+ * Returns 0 on success, -EINVAL on failure. -+ */ -+int amdgpu_enable_vblank_kms(struct drm_device *dev, int crtc) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ int idx = amdgpu_crtc_idx_to_irq_type(adev, crtc); -+ -+ return amdgpu_irq_get(adev, &adev->crtc_irq, idx); -+} -+ -+/** -+ * amdgpu_disable_vblank_kms - disable vblank interrupt -+ * -+ * @dev: drm dev pointer -+ * @crtc: crtc to disable vblank interrupt for -+ * -+ * Disable the interrupt on the requested crtc (all asics). -+ */ -+void amdgpu_disable_vblank_kms(struct drm_device *dev, int crtc) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ int idx = amdgpu_crtc_idx_to_irq_type(adev, crtc); -+ -+ amdgpu_irq_put(adev, &adev->crtc_irq, idx); -+} -+ -+/** -+ * amdgpu_get_vblank_timestamp_kms - get vblank timestamp -+ * -+ * @dev: drm dev pointer -+ * @crtc: crtc to get the timestamp for -+ * @max_error: max error -+ * @vblank_time: time value -+ * @flags: flags passed to the driver -+ * -+ * Gets the timestamp on the requested crtc based on the -+ * scanout position. (all asics). -+ * Returns postive status flags on success, negative error on failure. -+ */ -+int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, -+ int *max_error, -+ struct timeval *vblank_time, -+ unsigned flags) -+{ -+ struct drm_crtc *drmcrtc; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (crtc < 0 || crtc >= dev->num_crtcs) { -+ DRM_ERROR("Invalid crtc %d\n", crtc); -+ return -EINVAL; -+ } -+ -+ /* Get associated drm_crtc: */ -+ drmcrtc = &adev->mode_info.crtcs[crtc]->base; -+ -+ /* Helper routine in DRM core does all the work: */ -+ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, -+ vblank_time, flags, -+ drmcrtc, &drmcrtc->hwmode); -+} -+ -+const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_BO_LIST, amdgpu_bo_list_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ /* KMS */ -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_MMAP, amdgpu_gem_mmap_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_WAIT_IDLE, amdgpu_gem_wait_idle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_CS, amdgpu_cs_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_INFO, amdgpu_info_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_WAIT_CS, amdgpu_cs_wait_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_METADATA, amdgpu_gem_metadata_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_VA, amdgpu_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_OP, amdgpu_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_USERPTR, amdgpu_gem_userptr_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), -+}; -+int amdgpu_max_kms_ioctl = ARRAY_SIZE(amdgpu_ioctls_kms); -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c -new file mode 100644 -index 0000000..e944291 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c -@@ -0,0 +1,319 @@ -+/* -+ * 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Christian König <christian.koenig@amd.com> -+ */ -+ -+#include <linux/firmware.h> -+#include <linux/module.h> -+#include <linux/mmu_notifier.h> -+#include <drm/drmP.h> -+#include <drm/drm.h> -+ -+#include "amdgpu.h" -+ -+struct amdgpu_mn { -+ /* constant after initialisation */ -+ struct amdgpu_device *adev; -+ struct mm_struct *mm; -+ struct mmu_notifier mn; -+ -+ /* only used on destruction */ -+ struct work_struct work; -+ -+ /* protected by adev->mn_lock */ -+ struct hlist_node node; -+ -+ /* objects protected by lock */ -+ struct mutex lock; -+ struct rb_root objects; -+}; -+ -+struct amdgpu_mn_node { -+ struct interval_tree_node it; -+ struct list_head bos; -+}; -+ -+/** -+ * amdgpu_mn_destroy - destroy the rmn -+ * -+ * @work: previously sheduled work item -+ * -+ * Lazy destroys the notifier from a work item -+ */ -+static void amdgpu_mn_destroy(struct work_struct *work) -+{ -+ struct amdgpu_mn *rmn = container_of(work, struct amdgpu_mn, work); -+ struct amdgpu_device *adev = rmn->adev; -+ struct amdgpu_mn_node *node, *next_node; -+ struct amdgpu_bo *bo, *next_bo; -+ -+ mutex_lock(&adev->mn_lock); -+ mutex_lock(&rmn->lock); -+ hash_del(&rmn->node); -+ rbtree_postorder_for_each_entry_safe(node, next_node, &rmn->objects, -+ it.rb) { -+ -+ interval_tree_remove(&node->it, &rmn->objects); -+ list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) { -+ bo->mn = NULL; -+ list_del_init(&bo->mn_list); -+ } -+ kfree(node); -+ } -+ mutex_unlock(&rmn->lock); -+ mutex_unlock(&adev->mn_lock); -+ mmu_notifier_unregister(&rmn->mn, rmn->mm); -+ kfree(rmn); -+} -+ -+/** -+ * amdgpu_mn_release - callback to notify about mm destruction -+ * -+ * @mn: our notifier -+ * @mn: the mm this callback is about -+ * -+ * Shedule a work item to lazy destroy our notifier. -+ */ -+static void amdgpu_mn_release(struct mmu_notifier *mn, -+ struct mm_struct *mm) -+{ -+ struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); -+ INIT_WORK(&rmn->work, amdgpu_mn_destroy); -+ schedule_work(&rmn->work); -+} -+ -+/** -+ * amdgpu_mn_invalidate_range_start - callback to notify about mm change -+ * -+ * @mn: our notifier -+ * @mn: the mm this callback is about -+ * @start: start of updated range -+ * @end: end of updated range -+ * -+ * We block for all BOs between start and end to be idle and -+ * unmap them by move them into system domain again. -+ */ -+static void amdgpu_mn_invalidate_range_start(struct mmu_notifier *mn, -+ struct mm_struct *mm, -+ unsigned long start, -+ unsigned long end) -+{ -+ struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); -+ struct interval_tree_node *it; -+ -+ /* notification is exclusive, but interval is inclusive */ -+ end -= 1; -+ -+ mutex_lock(&rmn->lock); -+ -+ it = interval_tree_iter_first(&rmn->objects, start, end); -+ while (it) { -+ struct amdgpu_mn_node *node; -+ struct amdgpu_bo *bo; -+ int r; -+ -+ node = container_of(it, struct amdgpu_mn_node, it); -+ it = interval_tree_iter_next(it, start, end); -+ -+ list_for_each_entry(bo, &node->bos, mn_list) { -+ -+ r = amdgpu_bo_reserve(bo, true); -+ if (r) { -+ DRM_ERROR("(%d) failed to reserve user bo\n", r); -+ continue; -+ } -+ -+ r = reservation_object_wait_timeout_rcu(bo->tbo.resv, -+ true, false, MAX_SCHEDULE_TIMEOUT); -+ if (r) -+ DRM_ERROR("(%d) failed to wait for user bo\n", r); -+ -+ amdgpu_ttm_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU); -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); -+ if (r) -+ DRM_ERROR("(%d) failed to validate user bo\n", r); -+ -+ amdgpu_bo_unreserve(bo); -+ } -+ } -+ -+ mutex_unlock(&rmn->lock); -+} -+ -+static const struct mmu_notifier_ops amdgpu_mn_ops = { -+ .release = amdgpu_mn_release, -+ .invalidate_range_start = amdgpu_mn_invalidate_range_start, -+}; -+ -+/** -+ * amdgpu_mn_get - create notifier context -+ * -+ * @adev: amdgpu device pointer -+ * -+ * Creates a notifier context for current->mm. -+ */ -+static struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev) -+{ -+ struct mm_struct *mm = current->mm; -+ struct amdgpu_mn *rmn; -+ int r; -+ -+ down_write(&mm->mmap_sem); -+ mutex_lock(&adev->mn_lock); -+ -+ hash_for_each_possible(adev->mn_hash, rmn, node, (unsigned long)mm) -+ if (rmn->mm == mm) -+ goto release_locks; -+ -+ rmn = kzalloc(sizeof(*rmn), GFP_KERNEL); -+ if (!rmn) { -+ rmn = ERR_PTR(-ENOMEM); -+ goto release_locks; -+ } -+ -+ rmn->adev = adev; -+ rmn->mm = mm; -+ rmn->mn.ops = &amdgpu_mn_ops; -+ mutex_init(&rmn->lock); -+ rmn->objects = RB_ROOT; -+ -+ r = __mmu_notifier_register(&rmn->mn, mm); -+ if (r) -+ goto free_rmn; -+ -+ hash_add(adev->mn_hash, &rmn->node, (unsigned long)mm); -+ -+release_locks: -+ mutex_unlock(&adev->mn_lock); -+ up_write(&mm->mmap_sem); -+ -+ return rmn; -+ -+free_rmn: -+ mutex_unlock(&adev->mn_lock); -+ up_write(&mm->mmap_sem); -+ kfree(rmn); -+ -+ return ERR_PTR(r); -+} -+ -+/** -+ * amdgpu_mn_register - register a BO for notifier updates -+ * -+ * @bo: amdgpu buffer object -+ * @addr: userptr addr we should monitor -+ * -+ * Registers an MMU notifier for the given BO at the specified address. -+ * Returns 0 on success, -ERRNO if anything goes wrong. -+ */ -+int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) -+{ -+ unsigned long end = addr + amdgpu_bo_size(bo) - 1; -+ struct amdgpu_device *adev = bo->adev; -+ struct amdgpu_mn *rmn; -+ struct amdgpu_mn_node *node = NULL; -+ struct list_head bos; -+ struct interval_tree_node *it; -+ -+ rmn = amdgpu_mn_get(adev); -+ if (IS_ERR(rmn)) -+ return PTR_ERR(rmn); -+ -+ INIT_LIST_HEAD(&bos); -+ -+ mutex_lock(&rmn->lock); -+ -+ while ((it = interval_tree_iter_first(&rmn->objects, addr, end))) { -+ kfree(node); -+ node = container_of(it, struct amdgpu_mn_node, it); -+ interval_tree_remove(&node->it, &rmn->objects); -+ addr = min(it->start, addr); -+ end = max(it->last, end); -+ list_splice(&node->bos, &bos); -+ } -+ -+ if (!node) { -+ node = kmalloc(sizeof(struct amdgpu_mn_node), GFP_KERNEL); -+ if (!node) { -+ mutex_unlock(&rmn->lock); -+ return -ENOMEM; -+ } -+ } -+ -+ bo->mn = rmn; -+ -+ node->it.start = addr; -+ node->it.last = end; -+ INIT_LIST_HEAD(&node->bos); -+ list_splice(&bos, &node->bos); -+ list_add(&bo->mn_list, &node->bos); -+ -+ interval_tree_insert(&node->it, &rmn->objects); -+ -+ mutex_unlock(&rmn->lock); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_mn_unregister - unregister a BO for notifier updates -+ * -+ * @bo: amdgpu buffer object -+ * -+ * Remove any registration of MMU notifier updates from the buffer object. -+ */ -+void amdgpu_mn_unregister(struct amdgpu_bo *bo) -+{ -+ struct amdgpu_device *adev = bo->adev; -+ struct amdgpu_mn *rmn; -+ struct list_head *head; -+ -+ mutex_lock(&adev->mn_lock); -+ rmn = bo->mn; -+ if (rmn == NULL) { -+ mutex_unlock(&adev->mn_lock); -+ return; -+ } -+ -+ mutex_lock(&rmn->lock); -+ /* save the next list entry for later */ -+ head = bo->mn_list.next; -+ -+ bo->mn = NULL; -+ list_del(&bo->mn_list); -+ -+ if (list_empty(head)) { -+ struct amdgpu_mn_node *node; -+ node = container_of(head, struct amdgpu_mn_node, bos); -+ interval_tree_remove(&node->it, &rmn->objects); -+ kfree(node); -+ } -+ -+ mutex_unlock(&rmn->lock); -+ mutex_unlock(&adev->mn_lock); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h -new file mode 100644 -index 0000000..64efe5b ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h -@@ -0,0 +1,586 @@ -+/* -+ * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and -+ * VA Linux Systems Inc., Fremont, California. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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. -+ * -+ * Original Authors: -+ * Kevin E. Martin, Rickard E. Faith, Alan Hourihane -+ * -+ * Kernel port Author: Dave Airlie -+ */ -+ -+#ifndef AMDGPU_MODE_H -+#define AMDGPU_MODE_H -+ -+#include <drm/drm_crtc.h> -+#include <drm/drm_edid.h> -+#include <drm/drm_dp_helper.h> -+#include <drm/drm_fixed.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/drm_plane_helper.h> -+#include <linux/i2c.h> -+#include <linux/i2c-algo-bit.h> -+ -+struct amdgpu_bo; -+struct amdgpu_device; -+struct amdgpu_encoder; -+struct amdgpu_router; -+struct amdgpu_hpd; -+ -+#define to_amdgpu_crtc(x) container_of(x, struct amdgpu_crtc, base) -+#define to_amdgpu_connector(x) container_of(x, struct amdgpu_connector, base) -+#define to_amdgpu_encoder(x) container_of(x, struct amdgpu_encoder, base) -+#define to_amdgpu_framebuffer(x) container_of(x, struct amdgpu_framebuffer, base) -+ -+#define AMDGPU_MAX_HPD_PINS 6 -+#define AMDGPU_MAX_CRTCS 6 -+#define AMDGPU_MAX_AFMT_BLOCKS 7 -+ -+enum amdgpu_rmx_type { -+ RMX_OFF, -+ RMX_FULL, -+ RMX_CENTER, -+ RMX_ASPECT -+}; -+ -+enum amdgpu_underscan_type { -+ UNDERSCAN_OFF, -+ UNDERSCAN_ON, -+ UNDERSCAN_AUTO, -+}; -+ -+#define AMDGPU_HPD_CONNECT_INT_DELAY_IN_MS 50 -+#define AMDGPU_HPD_DISCONNECT_INT_DELAY_IN_MS 10 -+ -+enum amdgpu_hpd_id { -+ AMDGPU_HPD_1 = 0, -+ AMDGPU_HPD_2, -+ AMDGPU_HPD_3, -+ AMDGPU_HPD_4, -+ AMDGPU_HPD_5, -+ AMDGPU_HPD_6, -+ AMDGPU_HPD_LAST, -+ AMDGPU_HPD_NONE = 0xff, -+}; -+ -+enum amdgpu_crtc_irq { -+ AMDGPU_CRTC_IRQ_VBLANK1 = 0, -+ AMDGPU_CRTC_IRQ_VBLANK2, -+ AMDGPU_CRTC_IRQ_VBLANK3, -+ AMDGPU_CRTC_IRQ_VBLANK4, -+ AMDGPU_CRTC_IRQ_VBLANK5, -+ AMDGPU_CRTC_IRQ_VBLANK6, -+ AMDGPU_CRTC_IRQ_VLINE1, -+ AMDGPU_CRTC_IRQ_VLINE2, -+ AMDGPU_CRTC_IRQ_VLINE3, -+ AMDGPU_CRTC_IRQ_VLINE4, -+ AMDGPU_CRTC_IRQ_VLINE5, -+ AMDGPU_CRTC_IRQ_VLINE6, -+ AMDGPU_CRTC_IRQ_LAST, -+ AMDGPU_CRTC_IRQ_NONE = 0xff -+}; -+ -+enum amdgpu_pageflip_irq { -+ AMDGPU_PAGEFLIP_IRQ_D1 = 0, -+ AMDGPU_PAGEFLIP_IRQ_D2, -+ AMDGPU_PAGEFLIP_IRQ_D3, -+ AMDGPU_PAGEFLIP_IRQ_D4, -+ AMDGPU_PAGEFLIP_IRQ_D5, -+ AMDGPU_PAGEFLIP_IRQ_D6, -+ AMDGPU_PAGEFLIP_IRQ_LAST, -+ AMDGPU_PAGEFLIP_IRQ_NONE = 0xff -+}; -+ -+enum amdgpu_flip_status { -+ AMDGPU_FLIP_NONE, -+ AMDGPU_FLIP_PENDING, -+ AMDGPU_FLIP_SUBMITTED -+}; -+ -+#define AMDGPU_MAX_I2C_BUS 16 -+ -+/* amdgpu gpio-based i2c -+ * 1. "mask" reg and bits -+ * grabs the gpio pins for software use -+ * 0=not held 1=held -+ * 2. "a" reg and bits -+ * output pin value -+ * 0=low 1=high -+ * 3. "en" reg and bits -+ * sets the pin direction -+ * 0=input 1=output -+ * 4. "y" reg and bits -+ * input pin value -+ * 0=low 1=high -+ */ -+struct amdgpu_i2c_bus_rec { -+ bool valid; -+ /* id used by atom */ -+ uint8_t i2c_id; -+ /* id used by atom */ -+ enum amdgpu_hpd_id hpd; -+ /* can be used with hw i2c engine */ -+ bool hw_capable; -+ /* uses multi-media i2c engine */ -+ bool mm_i2c; -+ /* regs and bits */ -+ uint32_t mask_clk_reg; -+ uint32_t mask_data_reg; -+ uint32_t a_clk_reg; -+ uint32_t a_data_reg; -+ uint32_t en_clk_reg; -+ uint32_t en_data_reg; -+ uint32_t y_clk_reg; -+ uint32_t y_data_reg; -+ uint32_t mask_clk_mask; -+ uint32_t mask_data_mask; -+ uint32_t a_clk_mask; -+ uint32_t a_data_mask; -+ uint32_t en_clk_mask; -+ uint32_t en_data_mask; -+ uint32_t y_clk_mask; -+ uint32_t y_data_mask; -+}; -+ -+#define AMDGPU_MAX_BIOS_CONNECTOR 16 -+ -+/* pll flags */ -+#define AMDGPU_PLL_USE_BIOS_DIVS (1 << 0) -+#define AMDGPU_PLL_NO_ODD_POST_DIV (1 << 1) -+#define AMDGPU_PLL_USE_REF_DIV (1 << 2) -+#define AMDGPU_PLL_LEGACY (1 << 3) -+#define AMDGPU_PLL_PREFER_LOW_REF_DIV (1 << 4) -+#define AMDGPU_PLL_PREFER_HIGH_REF_DIV (1 << 5) -+#define AMDGPU_PLL_PREFER_LOW_FB_DIV (1 << 6) -+#define AMDGPU_PLL_PREFER_HIGH_FB_DIV (1 << 7) -+#define AMDGPU_PLL_PREFER_LOW_POST_DIV (1 << 8) -+#define AMDGPU_PLL_PREFER_HIGH_POST_DIV (1 << 9) -+#define AMDGPU_PLL_USE_FRAC_FB_DIV (1 << 10) -+#define AMDGPU_PLL_PREFER_CLOSEST_LOWER (1 << 11) -+#define AMDGPU_PLL_USE_POST_DIV (1 << 12) -+#define AMDGPU_PLL_IS_LCD (1 << 13) -+#define AMDGPU_PLL_PREFER_MINM_OVER_MAXP (1 << 14) -+ -+struct amdgpu_pll { -+ /* reference frequency */ -+ uint32_t reference_freq; -+ -+ /* fixed dividers */ -+ uint32_t reference_div; -+ uint32_t post_div; -+ -+ /* pll in/out limits */ -+ uint32_t pll_in_min; -+ uint32_t pll_in_max; -+ uint32_t pll_out_min; -+ uint32_t pll_out_max; -+ uint32_t lcd_pll_out_min; -+ uint32_t lcd_pll_out_max; -+ uint32_t best_vco; -+ -+ /* divider limits */ -+ uint32_t min_ref_div; -+ uint32_t max_ref_div; -+ uint32_t min_post_div; -+ uint32_t max_post_div; -+ uint32_t min_feedback_div; -+ uint32_t max_feedback_div; -+ uint32_t min_frac_feedback_div; -+ uint32_t max_frac_feedback_div; -+ -+ /* flags for the current clock */ -+ uint32_t flags; -+ -+ /* pll id */ -+ uint32_t id; -+}; -+ -+struct amdgpu_i2c_chan { -+ struct i2c_adapter adapter; -+ struct drm_device *dev; -+ struct i2c_algo_bit_data bit; -+ struct amdgpu_i2c_bus_rec rec; -+ struct drm_dp_aux aux; -+ bool has_aux; -+ struct mutex mutex; -+}; -+ -+struct amdgpu_fbdev; -+ -+struct amdgpu_afmt { -+ bool enabled; -+ int offset; -+ bool last_buffer_filled_status; -+ int id; -+ struct amdgpu_audio_pin *pin; -+}; -+ -+/* -+ * Audio -+ */ -+struct amdgpu_audio_pin { -+ int channels; -+ int rate; -+ int bits_per_sample; -+ u8 status_bits; -+ u8 category_code; -+ u32 offset; -+ bool connected; -+ u32 id; -+}; -+ -+struct amdgpu_audio { -+ bool enabled; -+ struct amdgpu_audio_pin pin[AMDGPU_MAX_AFMT_BLOCKS]; -+ int num_pins; -+}; -+ -+struct amdgpu_mode_mc_save { -+ u32 vga_render_control; -+ u32 vga_hdp_control; -+ bool crtc_enabled[AMDGPU_MAX_CRTCS]; -+}; -+ -+struct amdgpu_display_funcs { -+ /* vga render */ -+ void (*set_vga_render_state)(struct amdgpu_device *adev, bool render); -+ /* display watermarks */ -+ void (*bandwidth_update)(struct amdgpu_device *adev); -+ /* get frame count */ -+ u32 (*vblank_get_counter)(struct amdgpu_device *adev, int crtc); -+ /* wait for vblank */ -+ void (*vblank_wait)(struct amdgpu_device *adev, int crtc); -+ /* is dce hung */ -+ bool (*is_display_hung)(struct amdgpu_device *adev); -+ /* set backlight level */ -+ void (*backlight_set_level)(struct amdgpu_encoder *amdgpu_encoder, -+ u8 level); -+ /* get backlight level */ -+ u8 (*backlight_get_level)(struct amdgpu_encoder *amdgpu_encoder); -+ /* hotplug detect */ -+ bool (*hpd_sense)(struct amdgpu_device *adev, enum amdgpu_hpd_id hpd); -+ void (*hpd_set_polarity)(struct amdgpu_device *adev, -+ enum amdgpu_hpd_id hpd); -+ u32 (*hpd_get_gpio_reg)(struct amdgpu_device *adev); -+ /* pageflipping */ -+ void (*page_flip)(struct amdgpu_device *adev, -+ int crtc_id, u64 crtc_base); -+ int (*page_flip_get_scanoutpos)(struct amdgpu_device *adev, int crtc, -+ u32 *vbl, u32 *position); -+ /* display topology setup */ -+ void (*add_encoder)(struct amdgpu_device *adev, -+ uint32_t encoder_enum, -+ uint32_t supported_device, -+ u16 caps); -+ void (*add_connector)(struct amdgpu_device *adev, -+ uint32_t connector_id, -+ uint32_t supported_device, -+ int connector_type, -+ struct amdgpu_i2c_bus_rec *i2c_bus, -+ uint16_t connector_object_id, -+ struct amdgpu_hpd *hpd, -+ struct amdgpu_router *router); -+ void (*stop_mc_access)(struct amdgpu_device *adev, -+ struct amdgpu_mode_mc_save *save); -+ void (*resume_mc_access)(struct amdgpu_device *adev, -+ struct amdgpu_mode_mc_save *save); -+}; -+ -+struct amdgpu_mode_info { -+ struct atom_context *atom_context; -+ struct card_info *atom_card_info; -+ bool mode_config_initialized; -+ struct amdgpu_crtc *crtcs[6]; -+ struct amdgpu_afmt *afmt[7]; -+ /* DVI-I properties */ -+ struct drm_property *coherent_mode_property; -+ /* DAC enable load detect */ -+ struct drm_property *load_detect_property; -+ /* underscan */ -+ struct drm_property *underscan_property; -+ struct drm_property *underscan_hborder_property; -+ struct drm_property *underscan_vborder_property; -+ /* audio */ -+ struct drm_property *audio_property; -+ /* FMT dithering */ -+ struct drm_property *dither_property; -+ /* hardcoded DFP edid from BIOS */ -+ struct edid *bios_hardcoded_edid; -+ int bios_hardcoded_edid_size; -+ -+ /* pointer to fbdev info structure */ -+ struct amdgpu_fbdev *rfbdev; -+ /* firmware flags */ -+ u16 firmware_flags; -+ /* pointer to backlight encoder */ -+ struct amdgpu_encoder *bl_encoder; -+ struct amdgpu_audio audio; /* audio stuff */ -+ int num_crtc; /* number of crtcs */ -+ int num_hpd; /* number of hpd pins */ -+ int num_dig; /* number of dig blocks */ -+ int disp_priority; -+ const struct amdgpu_display_funcs *funcs; -+}; -+ -+#define AMDGPU_MAX_BL_LEVEL 0xFF -+ -+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) -+ -+struct amdgpu_backlight_privdata { -+ struct amdgpu_encoder *encoder; -+ uint8_t negative; -+}; -+ -+#endif -+ -+struct amdgpu_atom_ss { -+ uint16_t percentage; -+ uint16_t percentage_divider; -+ uint8_t type; -+ uint16_t step; -+ uint8_t delay; -+ uint8_t range; -+ uint8_t refdiv; -+ /* asic_ss */ -+ uint16_t rate; -+ uint16_t amount; -+}; -+ -+struct amdgpu_crtc { -+ struct drm_crtc base; -+ int crtc_id; -+ u16 lut_r[256], lut_g[256], lut_b[256]; -+ bool enabled; -+ bool can_tile; -+ uint32_t crtc_offset; -+ struct drm_gem_object *cursor_bo; -+ uint64_t cursor_addr; -+ int cursor_width; -+ int cursor_height; -+ int max_cursor_width; -+ int max_cursor_height; -+ enum amdgpu_rmx_type rmx_type; -+ u8 h_border; -+ u8 v_border; -+ fixed20_12 vsc; -+ fixed20_12 hsc; -+ struct drm_display_mode native_mode; -+ u32 pll_id; -+ /* page flipping */ -+ struct workqueue_struct *pflip_queue; -+ struct amdgpu_flip_work *pflip_works; -+ enum amdgpu_flip_status pflip_status; -+ int deferred_flip_completion; -+ /* pll sharing */ -+ struct amdgpu_atom_ss ss; -+ bool ss_enabled; -+ u32 adjusted_clock; -+ int bpc; -+ u32 pll_reference_div; -+ u32 pll_post_div; -+ u32 pll_flags; -+ struct drm_encoder *encoder; -+ struct drm_connector *connector; -+ /* for dpm */ -+ u32 line_time; -+ u32 wm_low; -+ u32 wm_high; -+ struct drm_display_mode hw_mode; -+}; -+ -+struct amdgpu_encoder_atom_dig { -+ bool linkb; -+ /* atom dig */ -+ bool coherent_mode; -+ int dig_encoder; /* -1 disabled, 0 DIGA, 1 DIGB, etc. */ -+ /* atom lvds/edp */ -+ uint32_t lcd_misc; -+ uint16_t panel_pwr_delay; -+ uint32_t lcd_ss_id; -+ /* panel mode */ -+ struct drm_display_mode native_mode; -+ struct backlight_device *bl_dev; -+ int dpms_mode; -+ uint8_t backlight_level; -+ int panel_mode; -+ struct amdgpu_afmt *afmt; -+}; -+ -+struct amdgpu_encoder { -+ struct drm_encoder base; -+ uint32_t encoder_enum; -+ uint32_t encoder_id; -+ uint32_t devices; -+ uint32_t active_device; -+ uint32_t flags; -+ uint32_t pixel_clock; -+ enum amdgpu_rmx_type rmx_type; -+ enum amdgpu_underscan_type underscan_type; -+ uint32_t underscan_hborder; -+ uint32_t underscan_vborder; -+ struct drm_display_mode native_mode; -+ void *enc_priv; -+ int audio_polling_active; -+ bool is_ext_encoder; -+ u16 caps; -+}; -+ -+struct amdgpu_connector_atom_dig { -+ /* displayport */ -+ u8 dpcd[DP_RECEIVER_CAP_SIZE]; -+ u8 dp_sink_type; -+ int dp_clock; -+ int dp_lane_count; -+ bool edp_on; -+}; -+ -+struct amdgpu_gpio_rec { -+ bool valid; -+ u8 id; -+ u32 reg; -+ u32 mask; -+ u32 shift; -+}; -+ -+struct amdgpu_hpd { -+ enum amdgpu_hpd_id hpd; -+ u8 plugged_state; -+ struct amdgpu_gpio_rec gpio; -+}; -+ -+struct amdgpu_router { -+ u32 router_id; -+ struct amdgpu_i2c_bus_rec i2c_info; -+ u8 i2c_addr; -+ /* i2c mux */ -+ bool ddc_valid; -+ u8 ddc_mux_type; -+ u8 ddc_mux_control_pin; -+ u8 ddc_mux_state; -+ /* clock/data mux */ -+ bool cd_valid; -+ u8 cd_mux_type; -+ u8 cd_mux_control_pin; -+ u8 cd_mux_state; -+}; -+ -+enum amdgpu_connector_audio { -+ AMDGPU_AUDIO_DISABLE = 0, -+ AMDGPU_AUDIO_ENABLE = 1, -+ AMDGPU_AUDIO_AUTO = 2 -+}; -+ -+enum amdgpu_connector_dither { -+ AMDGPU_FMT_DITHER_DISABLE = 0, -+ AMDGPU_FMT_DITHER_ENABLE = 1, -+}; -+ -+struct amdgpu_connector { -+ struct drm_connector base; -+ uint32_t connector_id; -+ uint32_t devices; -+ struct amdgpu_i2c_chan *ddc_bus; -+ /* some systems have an hdmi and vga port with a shared ddc line */ -+ bool shared_ddc; -+ bool use_digital; -+ /* we need to mind the EDID between detect -+ and get modes due to analog/digital/tvencoder */ -+ struct edid *edid; -+ void *con_priv; -+ bool dac_load_detect; -+ bool detected_by_load; /* if the connection status was determined by load */ -+ uint16_t connector_object_id; -+ struct amdgpu_hpd hpd; -+ struct amdgpu_router router; -+ struct amdgpu_i2c_chan *router_bus; -+ enum amdgpu_connector_audio audio; -+ enum amdgpu_connector_dither dither; -+ unsigned pixelclock_for_modeset; -+}; -+ -+struct amdgpu_framebuffer { -+ struct drm_framebuffer base; -+ struct drm_gem_object *obj; -+}; -+ -+#define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \ -+ ((em) == ATOM_ENCODER_MODE_DP_MST)) -+ -+void amdgpu_link_encoder_connector(struct drm_device *dev); -+ -+struct drm_connector * -+amdgpu_get_connector_for_encoder(struct drm_encoder *encoder); -+struct drm_connector * -+amdgpu_get_connector_for_encoder_init(struct drm_encoder *encoder); -+bool amdgpu_dig_monitor_is_duallink(struct drm_encoder *encoder, -+ u32 pixel_clock); -+ -+u16 amdgpu_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder); -+struct drm_encoder *amdgpu_get_external_encoder(struct drm_encoder *encoder); -+ -+bool amdgpu_ddc_probe(struct amdgpu_connector *amdgpu_connector, bool use_aux); -+ -+void amdgpu_encoder_set_active_device(struct drm_encoder *encoder); -+ -+int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, -+ unsigned int flags, -+ int *vpos, int *hpos, ktime_t *stime, -+ ktime_t *etime); -+ -+int amdgpu_framebuffer_init(struct drm_device *dev, -+ struct amdgpu_framebuffer *rfb, -+ struct drm_mode_fb_cmd2 *mode_cmd, -+ struct drm_gem_object *obj); -+ -+int amdgpufb_remove(struct drm_device *dev, struct drm_framebuffer *fb); -+ -+void amdgpu_enc_destroy(struct drm_encoder *encoder); -+void amdgpu_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj); -+bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc, -+ const struct drm_display_mode *mode, -+ struct drm_display_mode *adjusted_mode); -+void amdgpu_panel_mode_fixup(struct drm_encoder *encoder, -+ struct drm_display_mode *adjusted_mode); -+int amdgpu_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc); -+ -+/* fbdev layer */ -+int amdgpu_fbdev_init(struct amdgpu_device *adev); -+void amdgpu_fbdev_fini(struct amdgpu_device *adev); -+void amdgpu_fbdev_set_suspend(struct amdgpu_device *adev, int state); -+int amdgpu_fbdev_total_size(struct amdgpu_device *adev); -+bool amdgpu_fbdev_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj); -+ -+void amdgpu_fb_output_poll_changed(struct amdgpu_device *adev); -+ -+ -+int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tiled); -+ -+/* amdgpu_display.c */ -+void amdgpu_print_display_setup(struct drm_device *dev); -+int amdgpu_modeset_create_props(struct amdgpu_device *adev); -+int amdgpu_crtc_set_config(struct drm_mode_set *set); -+int amdgpu_crtc_page_flip(struct drm_crtc *crtc, -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t page_flip_flags); -+extern const struct drm_mode_config_funcs amdgpu_mode_funcs; -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c -new file mode 100644 -index 0000000..b515827 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c -@@ -0,0 +1,646 @@ -+/* -+ * Copyright 2009 Jerome Glisse. -+ * All Rights Reserved. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the -+ * "Software"), to deal in the Software without restriction, including -+ * without limitation the rights to use, copy, modify, merge, publish, -+ * distribute, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Jerome Glisse <glisse@freedesktop.org> -+ * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> -+ * Dave Airlie -+ */ -+#include <linux/list.h> -+#include <linux/slab.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_trace.h" -+ -+ -+int amdgpu_ttm_init(struct amdgpu_device *adev); -+void amdgpu_ttm_fini(struct amdgpu_device *adev); -+ -+static u64 amdgpu_get_vis_part_size(struct amdgpu_device *adev, -+ struct ttm_mem_reg * mem) -+{ -+ u64 ret = 0; -+ if (mem->start << PAGE_SHIFT < adev->mc.visible_vram_size) { -+ ret = (u64)((mem->start << PAGE_SHIFT) + mem->size) > -+ adev->mc.visible_vram_size ? -+ adev->mc.visible_vram_size - (mem->start << PAGE_SHIFT): -+ mem->size; -+ } -+ return ret; -+} -+ -+static void amdgpu_update_memory_usage(struct amdgpu_device *adev, -+ struct ttm_mem_reg *old_mem, -+ struct ttm_mem_reg *new_mem) -+{ -+ u64 vis_size; -+ if (!adev) -+ return; -+ -+ if (new_mem) { -+ switch (new_mem->mem_type) { -+ case TTM_PL_TT: -+ atomic64_add(new_mem->size, &adev->gtt_usage); -+ break; -+ case TTM_PL_VRAM: -+ atomic64_add(new_mem->size, &adev->vram_usage); -+ vis_size = amdgpu_get_vis_part_size(adev, new_mem); -+ atomic64_add(vis_size, &adev->vram_vis_usage); -+ break; -+ } -+ } -+ -+ if (old_mem) { -+ switch (old_mem->mem_type) { -+ case TTM_PL_TT: -+ atomic64_sub(old_mem->size, &adev->gtt_usage); -+ break; -+ case TTM_PL_VRAM: -+ atomic64_sub(old_mem->size, &adev->vram_usage); -+ vis_size = amdgpu_get_vis_part_size(adev, old_mem); -+ atomic64_sub(vis_size, &adev->vram_vis_usage); -+ break; -+ } -+ } -+} -+ -+static void amdgpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) -+{ -+ struct amdgpu_bo *bo; -+ -+ bo = container_of(tbo, struct amdgpu_bo, tbo); -+ -+ amdgpu_update_memory_usage(bo->adev, &bo->tbo.mem, NULL); -+ amdgpu_mn_unregister(bo); -+ -+ mutex_lock(&bo->adev->gem.mutex); -+ list_del_init(&bo->list); -+ mutex_unlock(&bo->adev->gem.mutex); -+ drm_gem_object_release(&bo->gem_base); -+ kfree(bo->metadata); -+ kfree(bo); -+} -+ -+bool amdgpu_ttm_bo_is_amdgpu_bo(struct ttm_buffer_object *bo) -+{ -+ if (bo->destroy == &amdgpu_ttm_bo_destroy) -+ return true; -+ return false; -+} -+ -+void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *rbo, u32 domain) -+{ -+ u32 c = 0, i; -+ rbo->placement.placement = rbo->placements; -+ rbo->placement.busy_placement = rbo->placements; -+ -+ if (domain & AMDGPU_GEM_DOMAIN_VRAM) { -+ if (rbo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS && -+ rbo->adev->mc.visible_vram_size < rbo->adev->mc.real_vram_size) { -+ rbo->placements[c].fpfn = -+ rbo->adev->mc.visible_vram_size >> PAGE_SHIFT; -+ rbo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | -+ TTM_PL_FLAG_VRAM; -+ } -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | -+ TTM_PL_FLAG_VRAM; -+ } -+ -+ if (domain & AMDGPU_GEM_DOMAIN_GTT) { -+ if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_UC) { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_TT; -+ } else if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_WC) { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT | -+ TTM_PL_FLAG_UNCACHED; -+ } else { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT; -+ } -+ } -+ -+ if (domain & AMDGPU_GEM_DOMAIN_CPU) { -+ if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_UC) { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_SYSTEM; -+ } else if (rbo->flags & AMDGPU_GEM_CREATE_CPU_GTT_WC) { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_SYSTEM | -+ TTM_PL_FLAG_UNCACHED; -+ } else { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; -+ } -+ } -+ -+ if (domain & AMDGPU_GEM_DOMAIN_GDS) { -+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | -+ AMDGPU_PL_FLAG_GDS; -+ } -+ if (domain & AMDGPU_GEM_DOMAIN_GWS) { -+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | -+ AMDGPU_PL_FLAG_GWS; -+ } -+ if (domain & AMDGPU_GEM_DOMAIN_OA) { -+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | -+ AMDGPU_PL_FLAG_OA; -+ } -+ -+ if (!c) { -+ rbo->placements[c].fpfn = 0; -+ rbo->placements[c++].flags = TTM_PL_MASK_CACHING | -+ TTM_PL_FLAG_SYSTEM; -+ } -+ rbo->placement.num_placement = c; -+ rbo->placement.num_busy_placement = c; -+ -+ for (i = 0; i < c; i++) { -+ if ((rbo->flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) && -+ (rbo->placements[i].flags & TTM_PL_FLAG_VRAM) && -+ !rbo->placements[i].fpfn) -+ rbo->placements[i].lpfn = -+ rbo->adev->mc.visible_vram_size >> PAGE_SHIFT; -+ else -+ rbo->placements[i].lpfn = 0; -+ } -+ -+ if (rbo->tbo.mem.size > 512 * 1024) { -+ for (i = 0; i < c; i++) { -+ rbo->placements[i].flags |= TTM_PL_FLAG_TOPDOWN; -+ } -+ } -+} -+ -+int amdgpu_bo_create(struct amdgpu_device *adev, -+ unsigned long size, int byte_align, bool kernel, u32 domain, u64 flags, -+ struct sg_table *sg, struct amdgpu_bo **bo_ptr) -+{ -+ struct amdgpu_bo *bo; -+ enum ttm_bo_type type; -+ unsigned long page_align; -+ size_t acc_size; -+ int r; -+ -+ /* VI has a hw bug where VM PTEs have to be allocated in groups of 8. -+ * do this as a temporary workaround -+ */ -+ if (!(domain & (AMDGPU_GEM_DOMAIN_GDS | AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA))) { -+ if (adev->asic_type >= CHIP_TOPAZ) { -+ if (byte_align & 0x7fff) -+ byte_align = ALIGN(byte_align, 0x8000); -+ if (size & 0x7fff) -+ size = ALIGN(size, 0x8000); -+ } -+ } -+ -+ page_align = roundup(byte_align, PAGE_SIZE) >> PAGE_SHIFT; -+ size = ALIGN(size, PAGE_SIZE); -+ -+ if (kernel) { -+ type = ttm_bo_type_kernel; -+ } else if (sg) { -+ type = ttm_bo_type_sg; -+ } else { -+ type = ttm_bo_type_device; -+ } -+ *bo_ptr = NULL; -+ -+ acc_size = ttm_bo_dma_acc_size(&adev->mman.bdev, size, -+ sizeof(struct amdgpu_bo)); -+ -+ bo = kzalloc(sizeof(struct amdgpu_bo), GFP_KERNEL); -+ if (bo == NULL) -+ return -ENOMEM; -+ r = drm_gem_object_init(adev->ddev, &bo->gem_base, size); -+ if (unlikely(r)) { -+ kfree(bo); -+ return r; -+ } -+ bo->adev = adev; -+ INIT_LIST_HEAD(&bo->list); -+ INIT_LIST_HEAD(&bo->va); -+ bo->initial_domain = domain & (AMDGPU_GEM_DOMAIN_VRAM | -+ AMDGPU_GEM_DOMAIN_GTT | -+ AMDGPU_GEM_DOMAIN_CPU | -+ AMDGPU_GEM_DOMAIN_GDS | -+ AMDGPU_GEM_DOMAIN_GWS | -+ AMDGPU_GEM_DOMAIN_OA); -+ -+ bo->flags = flags; -+ amdgpu_ttm_placement_from_domain(bo, domain); -+ /* Kernel allocation are uninterruptible */ -+ down_read(&adev->pm.mclk_lock); -+ r = ttm_bo_init(&adev->mman.bdev, &bo->tbo, size, type, -+ &bo->placement, page_align, !kernel, NULL, -+ acc_size, sg, NULL, &amdgpu_ttm_bo_destroy); -+ up_read(&adev->pm.mclk_lock); -+ if (unlikely(r != 0)) { -+ return r; -+ } -+ *bo_ptr = bo; -+ -+ trace_amdgpu_bo_create(bo); -+ -+ return 0; -+} -+ -+int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr) -+{ -+ bool is_iomem; -+ int r; -+ -+ if (bo->kptr) { -+ if (ptr) { -+ *ptr = bo->kptr; -+ } -+ return 0; -+ } -+ r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); -+ if (r) { -+ return r; -+ } -+ bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); -+ if (ptr) { -+ *ptr = bo->kptr; -+ } -+ return 0; -+} -+ -+void amdgpu_bo_kunmap(struct amdgpu_bo *bo) -+{ -+ if (bo->kptr == NULL) -+ return; -+ bo->kptr = NULL; -+ ttm_bo_kunmap(&bo->kmap); -+} -+ -+struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo) -+{ -+ if (bo == NULL) -+ return NULL; -+ -+ ttm_bo_reference(&bo->tbo); -+ return bo; -+} -+ -+void amdgpu_bo_unref(struct amdgpu_bo **bo) -+{ -+ struct ttm_buffer_object *tbo; -+ -+ if ((*bo) == NULL) -+ return; -+ -+ tbo = &((*bo)->tbo); -+ ttm_bo_unref(&tbo); -+ if (tbo == NULL) -+ *bo = NULL; -+} -+ -+int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain, u64 max_offset, -+ u64 *gpu_addr) -+{ -+ int r, i; -+ -+ if (amdgpu_ttm_tt_has_userptr(bo->tbo.ttm)) -+ return -EPERM; -+ -+ if (bo->pin_count) { -+ bo->pin_count++; -+ if (gpu_addr) -+ *gpu_addr = amdgpu_bo_gpu_offset(bo); -+ -+ if (max_offset != 0) { -+ u64 domain_start; -+ -+ if (domain == AMDGPU_GEM_DOMAIN_VRAM) -+ domain_start = bo->adev->mc.vram_start; -+ else -+ domain_start = bo->adev->mc.gtt_start; -+ WARN_ON_ONCE(max_offset < -+ (amdgpu_bo_gpu_offset(bo) - domain_start)); -+ } -+ -+ return 0; -+ } -+ amdgpu_ttm_placement_from_domain(bo, domain); -+ for (i = 0; i < bo->placement.num_placement; i++) { -+ /* force to pin into visible video ram */ -+ if ((bo->placements[i].flags & TTM_PL_FLAG_VRAM) && -+ !(bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) && -+ (!max_offset || max_offset > bo->adev->mc.visible_vram_size)) -+ bo->placements[i].lpfn = -+ bo->adev->mc.visible_vram_size >> PAGE_SHIFT; -+ else -+ bo->placements[i].lpfn = max_offset >> PAGE_SHIFT; -+ -+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; -+ } -+ -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); -+ if (likely(r == 0)) { -+ bo->pin_count = 1; -+ if (gpu_addr != NULL) -+ *gpu_addr = amdgpu_bo_gpu_offset(bo); -+ if (domain == AMDGPU_GEM_DOMAIN_VRAM) -+ bo->adev->vram_pin_size += amdgpu_bo_size(bo); -+ else -+ bo->adev->gart_pin_size += amdgpu_bo_size(bo); -+ } else { -+ dev_err(bo->adev->dev, "%p pin failed\n", bo); -+ } -+ return r; -+} -+ -+int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain, u64 *gpu_addr) -+{ -+ return amdgpu_bo_pin_restricted(bo, domain, 0, gpu_addr); -+} -+ -+int amdgpu_bo_unpin(struct amdgpu_bo *bo) -+{ -+ int r, i; -+ -+ if (!bo->pin_count) { -+ dev_warn(bo->adev->dev, "%p unpin not necessary\n", bo); -+ return 0; -+ } -+ bo->pin_count--; -+ if (bo->pin_count) -+ return 0; -+ for (i = 0; i < bo->placement.num_placement; i++) { -+ bo->placements[i].lpfn = 0; -+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; -+ } -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); -+ if (likely(r == 0)) { -+ if (bo->tbo.mem.mem_type == TTM_PL_VRAM) -+ bo->adev->vram_pin_size -= amdgpu_bo_size(bo); -+ else -+ bo->adev->gart_pin_size -= amdgpu_bo_size(bo); -+ } else { -+ dev_err(bo->adev->dev, "%p validate failed for unpin\n", bo); -+ } -+ return r; -+} -+ -+int amdgpu_bo_evict_vram(struct amdgpu_device *adev) -+{ -+ /* late 2.6.33 fix IGP hibernate - we need pm ops to do this correct */ -+ if (0 && (adev->flags & AMDGPU_IS_APU)) { -+ /* Useless to evict on IGP chips */ -+ return 0; -+ } -+ return ttm_bo_evict_mm(&adev->mman.bdev, TTM_PL_VRAM); -+} -+ -+void amdgpu_bo_force_delete(struct amdgpu_device *adev) -+{ -+ struct amdgpu_bo *bo, *n; -+ -+ if (list_empty(&adev->gem.objects)) { -+ return; -+ } -+ dev_err(adev->dev, "Userspace still has active objects !\n"); -+ list_for_each_entry_safe(bo, n, &adev->gem.objects, list) { -+ mutex_lock(&adev->ddev->struct_mutex); -+ dev_err(adev->dev, "%p %p %lu %lu force free\n", -+ &bo->gem_base, bo, (unsigned long)bo->gem_base.size, -+ *((unsigned long *)&bo->gem_base.refcount)); -+ mutex_lock(&bo->adev->gem.mutex); -+ list_del_init(&bo->list); -+ mutex_unlock(&bo->adev->gem.mutex); -+ /* this should unref the ttm bo */ -+ drm_gem_object_unreference(&bo->gem_base); -+ mutex_unlock(&adev->ddev->struct_mutex); -+ } -+} -+ -+int amdgpu_bo_init(struct amdgpu_device *adev) -+{ -+ /* Add an MTRR for the VRAM */ -+ adev->mc.vram_mtrr = arch_phys_wc_add(adev->mc.aper_base, -+ adev->mc.aper_size); -+ DRM_INFO("Detected VRAM RAM=%lluM, BAR=%lluM\n", -+ adev->mc.mc_vram_size >> 20, -+ (unsigned long long)adev->mc.aper_size >> 20); -+ DRM_INFO("RAM width %dbits DDR\n", -+ adev->mc.vram_width); -+ return amdgpu_ttm_init(adev); -+} -+ -+void amdgpu_bo_fini(struct amdgpu_device *adev) -+{ -+ amdgpu_ttm_fini(adev); -+ arch_phys_wc_del(adev->mc.vram_mtrr); -+} -+ -+int amdgpu_bo_fbdev_mmap(struct amdgpu_bo *bo, -+ struct vm_area_struct *vma) -+{ -+ return ttm_fbdev_mmap(vma, &bo->tbo); -+} -+ -+int amdgpu_bo_set_tiling_flags(struct amdgpu_bo *bo, u64 tiling_flags) -+{ -+ unsigned bankw, bankh, mtaspect, tilesplit, stilesplit; -+ -+ bankw = (tiling_flags >> AMDGPU_TILING_EG_BANKW_SHIFT) & AMDGPU_TILING_EG_BANKW_MASK; -+ bankh = (tiling_flags >> AMDGPU_TILING_EG_BANKH_SHIFT) & AMDGPU_TILING_EG_BANKH_MASK; -+ mtaspect = (tiling_flags >> AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK; -+ tilesplit = (tiling_flags >> AMDGPU_TILING_EG_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_TILE_SPLIT_MASK; -+ stilesplit = (tiling_flags >> AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_SHIFT) & AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_MASK; -+ switch (bankw) { -+ case 0: -+ case 1: -+ case 2: -+ case 4: -+ case 8: -+ break; -+ default: -+ return -EINVAL; -+ } -+ switch (bankh) { -+ case 0: -+ case 1: -+ case 2: -+ case 4: -+ case 8: -+ break; -+ default: -+ return -EINVAL; -+ } -+ switch (mtaspect) { -+ case 0: -+ case 1: -+ case 2: -+ case 4: -+ case 8: -+ break; -+ default: -+ return -EINVAL; -+ } -+ if (tilesplit > 6) { -+ return -EINVAL; -+ } -+ if (stilesplit > 6) { -+ return -EINVAL; -+ } -+ -+ bo->tiling_flags = tiling_flags; -+ return 0; -+} -+ -+void amdgpu_bo_get_tiling_flags(struct amdgpu_bo *bo, u64 *tiling_flags) -+{ -+ lockdep_assert_held(&bo->tbo.resv->lock.base); -+ -+ if (tiling_flags) -+ *tiling_flags = bo->tiling_flags; -+} -+ -+int amdgpu_bo_set_metadata (struct amdgpu_bo *bo, void *metadata, -+ uint32_t metadata_size, uint64_t flags) -+{ -+ void *buffer; -+ -+ if (!metadata_size) { -+ if (bo->metadata_size) { -+ kfree(bo->metadata); -+ bo->metadata_size = 0; -+ } -+ return 0; -+ } -+ -+ if (metadata == NULL) -+ return -EINVAL; -+ -+ buffer = kzalloc(metadata_size, GFP_KERNEL); -+ if (buffer == NULL) -+ return -ENOMEM; -+ -+ memcpy(buffer, metadata, metadata_size); -+ -+ kfree(bo->metadata); -+ bo->metadata_flags = flags; -+ bo->metadata = buffer; -+ bo->metadata_size = metadata_size; -+ -+ return 0; -+} -+ -+int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer, -+ size_t buffer_size, uint32_t *metadata_size, -+ uint64_t *flags) -+{ -+ if (!buffer && !metadata_size) -+ return -EINVAL; -+ -+ if (buffer) { -+ if (buffer_size < bo->metadata_size) -+ return -EINVAL; -+ -+ if (bo->metadata_size) -+ memcpy(buffer, bo->metadata, bo->metadata_size); -+ } -+ -+ if (metadata_size) -+ *metadata_size = bo->metadata_size; -+ if (flags) -+ *flags = bo->metadata_flags; -+ -+ return 0; -+} -+ -+void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, -+ struct ttm_mem_reg *new_mem) -+{ -+ struct amdgpu_bo *rbo; -+ -+ if (!amdgpu_ttm_bo_is_amdgpu_bo(bo)) -+ return; -+ -+ rbo = container_of(bo, struct amdgpu_bo, tbo); -+ amdgpu_vm_bo_invalidate(rbo->adev, rbo); -+ -+ /* update statistics */ -+ if (!new_mem) -+ return; -+ -+ /* move_notify is called before move happens */ -+ amdgpu_update_memory_usage(rbo->adev, &bo->mem, new_mem); -+} -+ -+int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo) -+{ -+ struct amdgpu_device *adev; -+ struct amdgpu_bo *rbo; -+ unsigned long offset, size; -+ int r; -+ -+ if (!amdgpu_ttm_bo_is_amdgpu_bo(bo)) -+ return 0; -+ rbo = container_of(bo, struct amdgpu_bo, tbo); -+ adev = rbo->adev; -+ if (bo->mem.mem_type == TTM_PL_VRAM) { -+ size = bo->mem.num_pages << PAGE_SHIFT; -+ offset = bo->mem.start << PAGE_SHIFT; -+ if ((offset + size) > adev->mc.visible_vram_size) { -+ /* hurrah the memory is not visible ! */ -+ amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_VRAM); -+ rbo->placements[0].lpfn = adev->mc.visible_vram_size >> PAGE_SHIFT; -+ r = ttm_bo_validate(bo, &rbo->placement, false, false); -+ if (unlikely(r != 0)) -+ return r; -+ offset = bo->mem.start << PAGE_SHIFT; -+ /* this should not happen */ -+ if ((offset + size) > adev->mc.visible_vram_size) -+ return -EINVAL; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_bo_fence - add fence to buffer object -+ * -+ * @bo: buffer object in question -+ * @fence: fence to add -+ * @shared: true if fence should be added shared -+ * -+ */ -+void amdgpu_bo_fence(struct amdgpu_bo *bo, struct amdgpu_fence *fence, -+ bool shared) -+{ -+ struct reservation_object *resv = bo->tbo.resv; -+ -+ if (shared) -+ reservation_object_add_shared_fence(resv, &fence->base); -+ else -+ reservation_object_add_excl_fence(resv, &fence->base); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h -new file mode 100644 -index 0000000..b1e0a03 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h -@@ -0,0 +1,196 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#ifndef __AMDGPU_OBJECT_H__ -+#define __AMDGPU_OBJECT_H__ -+ -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+/** -+ * amdgpu_mem_type_to_domain - return domain corresponding to mem_type -+ * @mem_type: ttm memory type -+ * -+ * Returns corresponding domain of the ttm mem_type -+ */ -+static inline unsigned amdgpu_mem_type_to_domain(u32 mem_type) -+{ -+ switch (mem_type) { -+ case TTM_PL_VRAM: -+ return AMDGPU_GEM_DOMAIN_VRAM; -+ case TTM_PL_TT: -+ return AMDGPU_GEM_DOMAIN_GTT; -+ case TTM_PL_SYSTEM: -+ return AMDGPU_GEM_DOMAIN_CPU; -+ case AMDGPU_PL_GDS: -+ return AMDGPU_GEM_DOMAIN_GDS; -+ case AMDGPU_PL_GWS: -+ return AMDGPU_GEM_DOMAIN_GWS; -+ case AMDGPU_PL_OA: -+ return AMDGPU_GEM_DOMAIN_OA; -+ default: -+ break; -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_bo_reserve - reserve bo -+ * @bo: bo structure -+ * @no_intr: don't return -ERESTARTSYS on pending signal -+ * -+ * Returns: -+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by -+ * a signal. Release all buffer reservations and return to user-space. -+ */ -+static inline int amdgpu_bo_reserve(struct amdgpu_bo *bo, bool no_intr) -+{ -+ int r; -+ -+ r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, 0); -+ if (unlikely(r != 0)) { -+ if (r != -ERESTARTSYS) -+ dev_err(bo->adev->dev, "%p reserve failed\n", bo); -+ return r; -+ } -+ return 0; -+} -+ -+static inline void amdgpu_bo_unreserve(struct amdgpu_bo *bo) -+{ -+ ttm_bo_unreserve(&bo->tbo); -+} -+ -+/** -+ * amdgpu_bo_gpu_offset - return GPU offset of bo -+ * @bo: amdgpu object for which we query the offset -+ * -+ * Returns current GPU offset of the object. -+ * -+ * Note: object should either be pinned or reserved when calling this -+ * function, it might be useful to add check for this for debugging. -+ */ -+static inline u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo) -+{ -+ return bo->tbo.offset; -+} -+ -+static inline unsigned long amdgpu_bo_size(struct amdgpu_bo *bo) -+{ -+ return bo->tbo.num_pages << PAGE_SHIFT; -+} -+ -+static inline unsigned amdgpu_bo_ngpu_pages(struct amdgpu_bo *bo) -+{ -+ return (bo->tbo.num_pages << PAGE_SHIFT) / AMDGPU_GPU_PAGE_SIZE; -+} -+ -+static inline unsigned amdgpu_bo_gpu_page_alignment(struct amdgpu_bo *bo) -+{ -+ return (bo->tbo.mem.page_alignment << PAGE_SHIFT) / AMDGPU_GPU_PAGE_SIZE; -+} -+ -+/** -+ * amdgpu_bo_mmap_offset - return mmap offset of bo -+ * @bo: amdgpu object for which we query the offset -+ * -+ * Returns mmap offset of the object. -+ */ -+static inline u64 amdgpu_bo_mmap_offset(struct amdgpu_bo *bo) -+{ -+ return drm_vma_node_offset_addr(&bo->tbo.vma_node); -+} -+ -+int amdgpu_bo_create(struct amdgpu_device *adev, -+ unsigned long size, int byte_align, -+ bool kernel, u32 domain, u64 flags, -+ struct sg_table *sg, -+ struct amdgpu_bo **bo_ptr); -+int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr); -+void amdgpu_bo_kunmap(struct amdgpu_bo *bo); -+struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo); -+void amdgpu_bo_unref(struct amdgpu_bo **bo); -+int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain, u64 *gpu_addr); -+int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain, -+ u64 max_offset, u64 *gpu_addr); -+int amdgpu_bo_unpin(struct amdgpu_bo *bo); -+int amdgpu_bo_evict_vram(struct amdgpu_device *adev); -+void amdgpu_bo_force_delete(struct amdgpu_device *adev); -+int amdgpu_bo_init(struct amdgpu_device *adev); -+void amdgpu_bo_fini(struct amdgpu_device *adev); -+int amdgpu_bo_fbdev_mmap(struct amdgpu_bo *bo, -+ struct vm_area_struct *vma); -+int amdgpu_bo_set_tiling_flags(struct amdgpu_bo *bo, u64 tiling_flags); -+void amdgpu_bo_get_tiling_flags(struct amdgpu_bo *bo, u64 *tiling_flags); -+int amdgpu_bo_set_metadata (struct amdgpu_bo *bo, void *metadata, -+ uint32_t metadata_size, uint64_t flags); -+int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer, -+ size_t buffer_size, uint32_t *metadata_size, -+ uint64_t *flags); -+void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, -+ struct ttm_mem_reg *new_mem); -+int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo); -+void amdgpu_bo_fence(struct amdgpu_bo *bo, struct amdgpu_fence *fence, -+ bool shared); -+ -+/* -+ * sub allocation -+ */ -+ -+static inline uint64_t amdgpu_sa_bo_gpu_addr(struct amdgpu_sa_bo *sa_bo) -+{ -+ return sa_bo->manager->gpu_addr + sa_bo->soffset; -+} -+ -+static inline void * amdgpu_sa_bo_cpu_addr(struct amdgpu_sa_bo *sa_bo) -+{ -+ return sa_bo->manager->cpu_ptr + sa_bo->soffset; -+} -+ -+int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager, -+ unsigned size, u32 align, u32 domain); -+void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager); -+int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager); -+int amdgpu_sa_bo_manager_suspend(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager); -+int amdgpu_sa_bo_new(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager, -+ struct amdgpu_sa_bo **sa_bo, -+ unsigned size, unsigned align); -+void amdgpu_sa_bo_free(struct amdgpu_device *adev, -+ struct amdgpu_sa_bo **sa_bo, -+ struct amdgpu_fence *fence); -+#if defined(CONFIG_DEBUG_FS) -+void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, -+ struct seq_file *m); -+#endif -+ -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c -new file mode 100644 -index 0000000..d153149 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c -@@ -0,0 +1,350 @@ -+/* -+ * 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 <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "atom.h" -+#include "atombios_encoders.h" -+#include <asm/div64.h> -+#include <linux/gcd.h> -+ -+/** -+ * amdgpu_pll_reduce_ratio - fractional number reduction -+ * -+ * @nom: nominator -+ * @den: denominator -+ * @nom_min: minimum value for nominator -+ * @den_min: minimum value for denominator -+ * -+ * Find the greatest common divisor and apply it on both nominator and -+ * denominator, but make nominator and denominator are at least as large -+ * as their minimum values. -+ */ -+static void amdgpu_pll_reduce_ratio(unsigned *nom, unsigned *den, -+ unsigned nom_min, unsigned den_min) -+{ -+ unsigned tmp; -+ -+ /* reduce the numbers to a simpler ratio */ -+ tmp = gcd(*nom, *den); -+ *nom /= tmp; -+ *den /= tmp; -+ -+ /* make sure nominator is large enough */ -+ if (*nom < nom_min) { -+ tmp = DIV_ROUND_UP(nom_min, *nom); -+ *nom *= tmp; -+ *den *= tmp; -+ } -+ -+ /* make sure the denominator is large enough */ -+ if (*den < den_min) { -+ tmp = DIV_ROUND_UP(den_min, *den); -+ *nom *= tmp; -+ *den *= tmp; -+ } -+} -+ -+/** -+ * amdgpu_pll_get_fb_ref_div - feedback and ref divider calculation -+ * -+ * @nom: nominator -+ * @den: denominator -+ * @post_div: post divider -+ * @fb_div_max: feedback divider maximum -+ * @ref_div_max: reference divider maximum -+ * @fb_div: resulting feedback divider -+ * @ref_div: resulting reference divider -+ * -+ * Calculate feedback and reference divider for a given post divider. Makes -+ * sure we stay within the limits. -+ */ -+static void amdgpu_pll_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, -+ unsigned fb_div_max, unsigned ref_div_max, -+ unsigned *fb_div, unsigned *ref_div) -+{ -+ /* limit reference * post divider to a maximum */ -+ ref_div_max = min(128 / post_div, ref_div_max); -+ -+ /* get matching reference and feedback divider */ -+ *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max); -+ *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den); -+ -+ /* limit fb divider to its maximum */ -+ if (*fb_div > fb_div_max) { -+ *ref_div = DIV_ROUND_CLOSEST(*ref_div * fb_div_max, *fb_div); -+ *fb_div = fb_div_max; -+ } -+} -+ -+/** -+ * amdgpu_pll_compute - compute PLL paramaters -+ * -+ * @pll: information about the PLL -+ * @dot_clock_p: resulting pixel clock -+ * fb_div_p: resulting feedback divider -+ * frac_fb_div_p: fractional part of the feedback divider -+ * ref_div_p: resulting reference divider -+ * post_div_p: resulting reference divider -+ * -+ * Try to calculate the PLL parameters to generate the given frequency: -+ * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div) -+ */ -+void amdgpu_pll_compute(struct amdgpu_pll *pll, -+ u32 freq, -+ u32 *dot_clock_p, -+ u32 *fb_div_p, -+ u32 *frac_fb_div_p, -+ u32 *ref_div_p, -+ u32 *post_div_p) -+{ -+ unsigned target_clock = pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV ? -+ freq : freq / 10; -+ -+ unsigned fb_div_min, fb_div_max, fb_div; -+ unsigned post_div_min, post_div_max, post_div; -+ unsigned ref_div_min, ref_div_max, ref_div; -+ unsigned post_div_best, diff_best; -+ unsigned nom, den; -+ -+ /* determine allowed feedback divider range */ -+ fb_div_min = pll->min_feedback_div; -+ fb_div_max = pll->max_feedback_div; -+ -+ if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) { -+ fb_div_min *= 10; -+ fb_div_max *= 10; -+ } -+ -+ /* determine allowed ref divider range */ -+ if (pll->flags & AMDGPU_PLL_USE_REF_DIV) -+ ref_div_min = pll->reference_div; -+ else -+ ref_div_min = pll->min_ref_div; -+ -+ if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV && -+ pll->flags & AMDGPU_PLL_USE_REF_DIV) -+ ref_div_max = pll->reference_div; -+ else -+ ref_div_max = pll->max_ref_div; -+ -+ /* determine allowed post divider range */ -+ if (pll->flags & AMDGPU_PLL_USE_POST_DIV) { -+ post_div_min = pll->post_div; -+ post_div_max = pll->post_div; -+ } else { -+ unsigned vco_min, vco_max; -+ -+ if (pll->flags & AMDGPU_PLL_IS_LCD) { -+ vco_min = pll->lcd_pll_out_min; -+ vco_max = pll->lcd_pll_out_max; -+ } else { -+ vco_min = pll->pll_out_min; -+ vco_max = pll->pll_out_max; -+ } -+ -+ if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) { -+ vco_min *= 10; -+ vco_max *= 10; -+ } -+ -+ post_div_min = vco_min / target_clock; -+ if ((target_clock * post_div_min) < vco_min) -+ ++post_div_min; -+ if (post_div_min < pll->min_post_div) -+ post_div_min = pll->min_post_div; -+ -+ post_div_max = vco_max / target_clock; -+ if ((target_clock * post_div_max) > vco_max) -+ --post_div_max; -+ if (post_div_max > pll->max_post_div) -+ post_div_max = pll->max_post_div; -+ } -+ -+ /* represent the searched ratio as fractional number */ -+ nom = target_clock; -+ den = pll->reference_freq; -+ -+ /* reduce the numbers to a simpler ratio */ -+ amdgpu_pll_reduce_ratio(&nom, &den, fb_div_min, post_div_min); -+ -+ /* now search for a post divider */ -+ if (pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP) -+ post_div_best = post_div_min; -+ else -+ post_div_best = post_div_max; -+ diff_best = ~0; -+ -+ for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { -+ unsigned diff; -+ amdgpu_pll_get_fb_ref_div(nom, den, post_div, fb_div_max, -+ ref_div_max, &fb_div, &ref_div); -+ diff = abs(target_clock - (pll->reference_freq * fb_div) / -+ (ref_div * post_div)); -+ -+ if (diff < diff_best || (diff == diff_best && -+ !(pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP))) { -+ -+ post_div_best = post_div; -+ diff_best = diff; -+ } -+ } -+ post_div = post_div_best; -+ -+ /* get the feedback and reference divider for the optimal value */ -+ amdgpu_pll_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max, -+ &fb_div, &ref_div); -+ -+ /* reduce the numbers to a simpler ratio once more */ -+ /* this also makes sure that the reference divider is large enough */ -+ amdgpu_pll_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); -+ -+ /* avoid high jitter with small fractional dividers */ -+ if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) { -+ fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 60); -+ if (fb_div < fb_div_min) { -+ unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div); -+ fb_div *= tmp; -+ ref_div *= tmp; -+ } -+ } -+ -+ /* and finally save the result */ -+ if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) { -+ *fb_div_p = fb_div / 10; -+ *frac_fb_div_p = fb_div % 10; -+ } else { -+ *fb_div_p = fb_div; -+ *frac_fb_div_p = 0; -+ } -+ -+ *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) + -+ (pll->reference_freq * *frac_fb_div_p)) / -+ (ref_div * post_div * 10); -+ *ref_div_p = ref_div; -+ *post_div_p = post_div; -+ -+ DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", -+ freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, -+ ref_div, post_div); -+} -+ -+/** -+ * amdgpu_pll_get_use_mask - look up a mask of which pplls are in use -+ * -+ * @crtc: drm crtc -+ * -+ * Returns the mask of which PPLLs (Pixel PLLs) are in use. -+ */ -+u32 amdgpu_pll_get_use_mask(struct drm_crtc *crtc) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct drm_crtc *test_crtc; -+ struct amdgpu_crtc *test_amdgpu_crtc; -+ u32 pll_in_use = 0; -+ -+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { -+ if (crtc == test_crtc) -+ continue; -+ -+ test_amdgpu_crtc = to_amdgpu_crtc(test_crtc); -+ if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID) -+ pll_in_use |= (1 << test_amdgpu_crtc->pll_id); -+ } -+ return pll_in_use; -+} -+ -+/** -+ * amdgpu_pll_get_shared_dp_ppll - return the PPLL used by another crtc for DP -+ * -+ * @crtc: drm crtc -+ * -+ * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is -+ * also in DP mode. For DP, a single PPLL can be used for all DP -+ * crtcs/encoders. -+ */ -+int amdgpu_pll_get_shared_dp_ppll(struct drm_crtc *crtc) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct drm_crtc *test_crtc; -+ struct amdgpu_crtc *test_amdgpu_crtc; -+ -+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { -+ if (crtc == test_crtc) -+ continue; -+ test_amdgpu_crtc = to_amdgpu_crtc(test_crtc); -+ if (test_amdgpu_crtc->encoder && -+ ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) { -+ /* for DP use the same PLL for all */ -+ if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID) -+ return test_amdgpu_crtc->pll_id; -+ } -+ } -+ return ATOM_PPLL_INVALID; -+} -+ -+/** -+ * amdgpu_pll_get_shared_nondp_ppll - return the PPLL used by another non-DP crtc -+ * -+ * @crtc: drm crtc -+ * @encoder: drm encoder -+ * -+ * Returns the PPLL (Pixel PLL) used by another non-DP crtc/encoder which can -+ * be shared (i.e., same clock). -+ */ -+int amdgpu_pll_get_shared_nondp_ppll(struct drm_crtc *crtc) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct drm_crtc *test_crtc; -+ struct amdgpu_crtc *test_amdgpu_crtc; -+ u32 adjusted_clock, test_adjusted_clock; -+ -+ adjusted_clock = amdgpu_crtc->adjusted_clock; -+ -+ if (adjusted_clock == 0) -+ return ATOM_PPLL_INVALID; -+ -+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) { -+ if (crtc == test_crtc) -+ continue; -+ test_amdgpu_crtc = to_amdgpu_crtc(test_crtc); -+ if (test_amdgpu_crtc->encoder && -+ !ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) { -+ /* check if we are already driving this connector with another crtc */ -+ if (test_amdgpu_crtc->connector == amdgpu_crtc->connector) { -+ /* if we are, return that pll */ -+ if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID) -+ return test_amdgpu_crtc->pll_id; -+ } -+ /* for non-DP check the clock */ -+ test_adjusted_clock = test_amdgpu_crtc->adjusted_clock; -+ if ((crtc->mode.clock == test_crtc->mode.clock) && -+ (adjusted_clock == test_adjusted_clock) && -+ (amdgpu_crtc->ss_enabled == test_amdgpu_crtc->ss_enabled) && -+ (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)) -+ return test_amdgpu_crtc->pll_id; -+ } -+ } -+ return ATOM_PPLL_INVALID; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pll.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_pll.h -new file mode 100644 -index 0000000..db6136f ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pll.h -@@ -0,0 +1,38 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_PLL_H__ -+#define __AMDGPU_PLL_H__ -+ -+void amdgpu_pll_compute(struct amdgpu_pll *pll, -+ u32 freq, -+ u32 *dot_clock_p, -+ u32 *fb_div_p, -+ u32 *frac_fb_div_p, -+ u32 *ref_div_p, -+ u32 *post_div_p); -+u32 amdgpu_pll_get_use_mask(struct drm_crtc *crtc); -+int amdgpu_pll_get_shared_dp_ppll(struct drm_crtc *crtc); -+int amdgpu_pll_get_shared_nondp_ppll(struct drm_crtc *crtc); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c -new file mode 100644 -index 0000000..8978254 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c -@@ -0,0 +1,801 @@ -+/* -+ * 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: Rafał Miłecki <zajec5@gmail.com> -+ * Alex Deucher <alexdeucher@gmail.com> -+ */ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include "amdgpu_drv.h" -+#include "amdgpu_pm.h" -+#include "amdgpu_dpm.h" -+#include "atom.h" -+#include <linux/power_supply.h> -+#include <linux/hwmon.h> -+#include <linux/hwmon-sysfs.h> -+ -+static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev); -+ -+void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev) -+{ -+ if (adev->pm.dpm_enabled) { -+ mutex_lock(&adev->pm.mutex); -+ if (power_supply_is_system_supplied() > 0) -+ adev->pm.dpm.ac_power = true; -+ else -+ adev->pm.dpm.ac_power = false; -+ if (adev->pm.funcs->enable_bapm) -+ amdgpu_dpm_enable_bapm(adev, adev->pm.dpm.ac_power); -+ mutex_unlock(&adev->pm.mutex); -+ } -+} -+ -+static ssize_t amdgpu_get_dpm_state(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct drm_device *ddev = dev_get_drvdata(dev); -+ struct amdgpu_device *adev = ddev->dev_private; -+ enum amdgpu_pm_state_type pm = adev->pm.dpm.user_state; -+ -+ return snprintf(buf, PAGE_SIZE, "%s\n", -+ (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : -+ (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); -+} -+ -+static ssize_t amdgpu_set_dpm_state(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ struct drm_device *ddev = dev_get_drvdata(dev); -+ struct amdgpu_device *adev = ddev->dev_private; -+ -+ mutex_lock(&adev->pm.mutex); -+ if (strncmp("battery", buf, strlen("battery")) == 0) -+ adev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; -+ else if (strncmp("balanced", buf, strlen("balanced")) == 0) -+ adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; -+ else if (strncmp("performance", buf, strlen("performance")) == 0) -+ adev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; -+ else { -+ mutex_unlock(&adev->pm.mutex); -+ count = -EINVAL; -+ goto fail; -+ } -+ mutex_unlock(&adev->pm.mutex); -+ -+ /* Can't set dpm state when the card is off */ -+ if (!(adev->flags & AMDGPU_IS_PX) || -+ (ddev->switch_power_state == DRM_SWITCH_POWER_ON)) -+ amdgpu_pm_compute_clocks(adev); -+fail: -+ return count; -+} -+ -+static ssize_t amdgpu_get_dpm_forced_performance_level(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct drm_device *ddev = dev_get_drvdata(dev); -+ struct amdgpu_device *adev = ddev->dev_private; -+ enum amdgpu_dpm_forced_level level = adev->pm.dpm.forced_level; -+ -+ return snprintf(buf, PAGE_SIZE, "%s\n", -+ (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) ? "auto" : -+ (level == AMDGPU_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); -+} -+ -+static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ struct drm_device *ddev = dev_get_drvdata(dev); -+ struct amdgpu_device *adev = ddev->dev_private; -+ enum amdgpu_dpm_forced_level level; -+ int ret = 0; -+ -+ mutex_lock(&adev->pm.mutex); -+ if (strncmp("low", buf, strlen("low")) == 0) { -+ level = AMDGPU_DPM_FORCED_LEVEL_LOW; -+ } else if (strncmp("high", buf, strlen("high")) == 0) { -+ level = AMDGPU_DPM_FORCED_LEVEL_HIGH; -+ } else if (strncmp("auto", buf, strlen("auto")) == 0) { -+ level = AMDGPU_DPM_FORCED_LEVEL_AUTO; -+ } else { -+ count = -EINVAL; -+ goto fail; -+ } -+ if (adev->pm.funcs->force_performance_level) { -+ if (adev->pm.dpm.thermal_active) { -+ count = -EINVAL; -+ goto fail; -+ } -+ ret = amdgpu_dpm_force_performance_level(adev, level); -+ if (ret) -+ count = -EINVAL; -+ } -+fail: -+ mutex_unlock(&adev->pm.mutex); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, amdgpu_get_dpm_state, amdgpu_set_dpm_state); -+static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR, -+ amdgpu_get_dpm_forced_performance_level, -+ amdgpu_set_dpm_forced_performance_level); -+ -+static ssize_t amdgpu_hwmon_show_temp(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ int temp; -+ -+ if (adev->pm.funcs->get_temperature) -+ temp = amdgpu_dpm_get_temperature(adev); -+ else -+ temp = 0; -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", temp); -+} -+ -+static ssize_t amdgpu_hwmon_show_temp_thresh(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ int hyst = to_sensor_dev_attr(attr)->index; -+ int temp; -+ -+ if (hyst) -+ temp = adev->pm.dpm.thermal.min_temp; -+ else -+ temp = adev->pm.dpm.thermal.max_temp; -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", temp); -+} -+ -+static ssize_t amdgpu_hwmon_get_pwm1_enable(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ u32 pwm_mode = 0; -+ -+ if (adev->pm.funcs->get_fan_control_mode) -+ pwm_mode = amdgpu_dpm_get_fan_control_mode(adev); -+ -+ /* never 0 (full-speed), fuse or smc-controlled always */ -+ return sprintf(buf, "%i\n", pwm_mode == FDO_PWM_MODE_STATIC ? 1 : 2); -+} -+ -+static ssize_t amdgpu_hwmon_set_pwm1_enable(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, -+ size_t count) -+{ -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ int err; -+ int value; -+ -+ if(!adev->pm.funcs->set_fan_control_mode) -+ return -EINVAL; -+ -+ err = kstrtoint(buf, 10, &value); -+ if (err) -+ return err; -+ -+ switch (value) { -+ case 1: /* manual, percent-based */ -+ amdgpu_dpm_set_fan_control_mode(adev, FDO_PWM_MODE_STATIC); -+ break; -+ default: /* disable */ -+ amdgpu_dpm_set_fan_control_mode(adev, 0); -+ break; -+ } -+ -+ return count; -+} -+ -+static ssize_t amdgpu_hwmon_get_pwm1_min(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ return sprintf(buf, "%i\n", 0); -+} -+ -+static ssize_t amdgpu_hwmon_get_pwm1_max(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ return sprintf(buf, "%i\n", 255); -+} -+ -+static ssize_t amdgpu_hwmon_set_pwm1(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ int err; -+ u32 value; -+ -+ err = kstrtou32(buf, 10, &value); -+ if (err) -+ return err; -+ -+ value = (value * 100) / 255; -+ -+ err = amdgpu_dpm_set_fan_speed_percent(adev, value); -+ if (err) -+ return err; -+ -+ return count; -+} -+ -+static ssize_t amdgpu_hwmon_get_pwm1(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) -+{ -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ int err; -+ u32 speed; -+ -+ err = amdgpu_dpm_get_fan_speed_percent(adev, &speed); -+ if (err) -+ return err; -+ -+ speed = (speed * 255) / 100; -+ -+ return sprintf(buf, "%i\n", speed); -+} -+ -+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, amdgpu_hwmon_show_temp, NULL, 0); -+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, amdgpu_hwmon_show_temp_thresh, NULL, 0); -+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, amdgpu_hwmon_show_temp_thresh, NULL, 1); -+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, amdgpu_hwmon_get_pwm1, amdgpu_hwmon_set_pwm1, 0); -+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, amdgpu_hwmon_get_pwm1_enable, amdgpu_hwmon_set_pwm1_enable, 0); -+static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, amdgpu_hwmon_get_pwm1_min, NULL, 0); -+static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, amdgpu_hwmon_get_pwm1_max, NULL, 0); -+ -+static struct attribute *hwmon_attributes[] = { -+ &sensor_dev_attr_temp1_input.dev_attr.attr, -+ &sensor_dev_attr_temp1_crit.dev_attr.attr, -+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, -+ &sensor_dev_attr_pwm1.dev_attr.attr, -+ &sensor_dev_attr_pwm1_enable.dev_attr.attr, -+ &sensor_dev_attr_pwm1_min.dev_attr.attr, -+ &sensor_dev_attr_pwm1_max.dev_attr.attr, -+ NULL -+}; -+ -+static umode_t hwmon_attributes_visible(struct kobject *kobj, -+ struct attribute *attr, int index) -+{ -+ struct device *dev = container_of(kobj, struct device, kobj); -+ struct amdgpu_device *adev = dev_get_drvdata(dev); -+ umode_t effective_mode = attr->mode; -+ -+ /* Skip limit attributes if DPM is not enabled */ -+ if (!adev->pm.dpm_enabled && -+ (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || -+ attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) -+ return 0; -+ -+ /* Skip fan attributes if fan is not present */ -+ if (adev->pm.no_fan && -+ (attr == &sensor_dev_attr_pwm1.dev_attr.attr || -+ attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || -+ attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || -+ attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) -+ return 0; -+ -+ /* mask fan attributes if we have no bindings for this asic to expose */ -+ if ((!adev->pm.funcs->get_fan_speed_percent && -+ attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't query fan */ -+ (!adev->pm.funcs->get_fan_control_mode && -+ attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't query state */ -+ effective_mode &= ~S_IRUGO; -+ -+ if ((!adev->pm.funcs->set_fan_speed_percent && -+ attr == &sensor_dev_attr_pwm1.dev_attr.attr) || /* can't manage fan */ -+ (!adev->pm.funcs->set_fan_control_mode && -+ attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr)) /* can't manage state */ -+ effective_mode &= ~S_IWUSR; -+ -+ /* hide max/min values if we can't both query and manage the fan */ -+ if ((!adev->pm.funcs->set_fan_speed_percent && -+ !adev->pm.funcs->get_fan_speed_percent) && -+ (attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || -+ attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) -+ return 0; -+ -+ return effective_mode; -+} -+ -+static const struct attribute_group hwmon_attrgroup = { -+ .attrs = hwmon_attributes, -+ .is_visible = hwmon_attributes_visible, -+}; -+ -+static const struct attribute_group *hwmon_groups[] = { -+ &hwmon_attrgroup, -+ NULL -+}; -+ -+void amdgpu_dpm_thermal_work_handler(struct work_struct *work) -+{ -+ struct amdgpu_device *adev = -+ container_of(work, struct amdgpu_device, -+ pm.dpm.thermal.work); -+ /* switch to the thermal state */ -+ enum amdgpu_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; -+ -+ if (!adev->pm.dpm_enabled) -+ return; -+ -+ if (adev->pm.funcs->get_temperature) { -+ int temp = amdgpu_dpm_get_temperature(adev); -+ -+ if (temp < adev->pm.dpm.thermal.min_temp) -+ /* switch back the user state */ -+ dpm_state = adev->pm.dpm.user_state; -+ } else { -+ if (adev->pm.dpm.thermal.high_to_low) -+ /* switch back the user state */ -+ dpm_state = adev->pm.dpm.user_state; -+ } -+ mutex_lock(&adev->pm.mutex); -+ if (dpm_state == POWER_STATE_TYPE_INTERNAL_THERMAL) -+ adev->pm.dpm.thermal_active = true; -+ else -+ adev->pm.dpm.thermal_active = false; -+ adev->pm.dpm.state = dpm_state; -+ mutex_unlock(&adev->pm.mutex); -+ -+ amdgpu_pm_compute_clocks(adev); -+} -+ -+static struct amdgpu_ps *amdgpu_dpm_pick_power_state(struct amdgpu_device *adev, -+ enum amdgpu_pm_state_type dpm_state) -+{ -+ int i; -+ struct amdgpu_ps *ps; -+ u32 ui_class; -+ bool single_display = (adev->pm.dpm.new_active_crtc_count < 2) ? -+ true : false; -+ -+ /* check if the vblank period is too short to adjust the mclk */ -+ if (single_display && adev->pm.funcs->vblank_too_short) { -+ if (amdgpu_dpm_vblank_too_short(adev)) -+ single_display = false; -+ } -+ -+ /* certain older asics have a separare 3D performance state, -+ * so try that first if the user selected performance -+ */ -+ if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) -+ dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; -+ /* balanced states don't exist at the moment */ -+ if (dpm_state == POWER_STATE_TYPE_BALANCED) -+ dpm_state = POWER_STATE_TYPE_PERFORMANCE; -+ -+restart_search: -+ /* Pick the best power state based on current conditions */ -+ for (i = 0; i < adev->pm.dpm.num_ps; i++) { -+ ps = &adev->pm.dpm.ps[i]; -+ ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; -+ switch (dpm_state) { -+ /* user states */ -+ case POWER_STATE_TYPE_BATTERY: -+ if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { -+ if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { -+ if (single_display) -+ return ps; -+ } else -+ return ps; -+ } -+ break; -+ case POWER_STATE_TYPE_BALANCED: -+ if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { -+ if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { -+ if (single_display) -+ return ps; -+ } else -+ return ps; -+ } -+ break; -+ case POWER_STATE_TYPE_PERFORMANCE: -+ if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { -+ if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { -+ if (single_display) -+ return ps; -+ } else -+ return ps; -+ } -+ break; -+ /* internal states */ -+ case POWER_STATE_TYPE_INTERNAL_UVD: -+ if (adev->pm.dpm.uvd_ps) -+ return adev->pm.dpm.uvd_ps; -+ else -+ break; -+ case POWER_STATE_TYPE_INTERNAL_UVD_SD: -+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_UVD_HD: -+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_UVD_HD2: -+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_UVD_MVC: -+ if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_BOOT: -+ return adev->pm.dpm.boot_ps; -+ case POWER_STATE_TYPE_INTERNAL_THERMAL: -+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_ACPI: -+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_ULV: -+ if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) -+ return ps; -+ break; -+ case POWER_STATE_TYPE_INTERNAL_3DPERF: -+ if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) -+ return ps; -+ break; -+ default: -+ break; -+ } -+ } -+ /* use a fallback state if we didn't match */ -+ switch (dpm_state) { -+ case POWER_STATE_TYPE_INTERNAL_UVD_SD: -+ dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD; -+ goto restart_search; -+ case POWER_STATE_TYPE_INTERNAL_UVD_HD: -+ case POWER_STATE_TYPE_INTERNAL_UVD_HD2: -+ case POWER_STATE_TYPE_INTERNAL_UVD_MVC: -+ if (adev->pm.dpm.uvd_ps) { -+ return adev->pm.dpm.uvd_ps; -+ } else { -+ dpm_state = POWER_STATE_TYPE_PERFORMANCE; -+ goto restart_search; -+ } -+ case POWER_STATE_TYPE_INTERNAL_THERMAL: -+ dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; -+ goto restart_search; -+ case POWER_STATE_TYPE_INTERNAL_ACPI: -+ dpm_state = POWER_STATE_TYPE_BATTERY; -+ goto restart_search; -+ case POWER_STATE_TYPE_BATTERY: -+ case POWER_STATE_TYPE_BALANCED: -+ case POWER_STATE_TYPE_INTERNAL_3DPERF: -+ dpm_state = POWER_STATE_TYPE_PERFORMANCE; -+ goto restart_search; -+ default: -+ break; -+ } -+ -+ return NULL; -+} -+ -+static void amdgpu_dpm_change_power_state_locked(struct amdgpu_device *adev) -+{ -+ int i; -+ struct amdgpu_ps *ps; -+ enum amdgpu_pm_state_type dpm_state; -+ int ret; -+ -+ /* if dpm init failed */ -+ if (!adev->pm.dpm_enabled) -+ return; -+ -+ if (adev->pm.dpm.user_state != adev->pm.dpm.state) { -+ /* add other state override checks here */ -+ if ((!adev->pm.dpm.thermal_active) && -+ (!adev->pm.dpm.uvd_active)) -+ adev->pm.dpm.state = adev->pm.dpm.user_state; -+ } -+ dpm_state = adev->pm.dpm.state; -+ -+ ps = amdgpu_dpm_pick_power_state(adev, dpm_state); -+ if (ps) -+ adev->pm.dpm.requested_ps = ps; -+ else -+ return; -+ -+ /* no need to reprogram if nothing changed unless we are on BTC+ */ -+ if (adev->pm.dpm.current_ps == adev->pm.dpm.requested_ps) { -+ /* vce just modifies an existing state so force a change */ -+ if (ps->vce_active != adev->pm.dpm.vce_active) -+ goto force; -+ if (adev->flags & AMDGPU_IS_APU) { -+ /* for APUs if the num crtcs changed but state is the same, -+ * all we need to do is update the display configuration. -+ */ -+ if (adev->pm.dpm.new_active_crtcs != adev->pm.dpm.current_active_crtcs) { -+ /* update display watermarks based on new power state */ -+ amdgpu_display_bandwidth_update(adev); -+ /* update displays */ -+ amdgpu_dpm_display_configuration_changed(adev); -+ adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; -+ adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; -+ } -+ return; -+ } else { -+ /* for BTC+ if the num crtcs hasn't changed and state is the same, -+ * nothing to do, if the num crtcs is > 1 and state is the same, -+ * update display configuration. -+ */ -+ if (adev->pm.dpm.new_active_crtcs == -+ adev->pm.dpm.current_active_crtcs) { -+ return; -+ } else if ((adev->pm.dpm.current_active_crtc_count > 1) && -+ (adev->pm.dpm.new_active_crtc_count > 1)) { -+ /* update display watermarks based on new power state */ -+ amdgpu_display_bandwidth_update(adev); -+ /* update displays */ -+ amdgpu_dpm_display_configuration_changed(adev); -+ adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; -+ adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; -+ return; -+ } -+ } -+ } -+ -+force: -+ if (amdgpu_dpm == 1) { -+ printk("switching from power state:\n"); -+ amdgpu_dpm_print_power_state(adev, adev->pm.dpm.current_ps); -+ printk("switching to power state:\n"); -+ amdgpu_dpm_print_power_state(adev, adev->pm.dpm.requested_ps); -+ } -+ -+ mutex_lock(&adev->ddev->struct_mutex); -+ down_write(&adev->pm.mclk_lock); -+ mutex_lock(&adev->ring_lock); -+ -+ /* update whether vce is active */ -+ ps->vce_active = adev->pm.dpm.vce_active; -+ -+ ret = amdgpu_dpm_pre_set_power_state(adev); -+ if (ret) -+ goto done; -+ -+ /* update display watermarks based on new power state */ -+ amdgpu_display_bandwidth_update(adev); -+ /* update displays */ -+ amdgpu_dpm_display_configuration_changed(adev); -+ -+ adev->pm.dpm.current_active_crtcs = adev->pm.dpm.new_active_crtcs; -+ adev->pm.dpm.current_active_crtc_count = adev->pm.dpm.new_active_crtc_count; -+ -+ /* wait for the rings to drain */ -+ for (i = 0; i < AMDGPU_MAX_RINGS; i++) { -+ struct amdgpu_ring *ring = adev->rings[i]; -+ if (ring && ring->ready) -+ amdgpu_fence_wait_empty(ring); -+ } -+ -+ /* program the new power state */ -+ amdgpu_dpm_set_power_state(adev); -+ -+ /* update current power state */ -+ adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps; -+ -+ amdgpu_dpm_post_set_power_state(adev); -+ -+ if (adev->pm.funcs->force_performance_level) { -+ if (adev->pm.dpm.thermal_active) { -+ enum amdgpu_dpm_forced_level level = adev->pm.dpm.forced_level; -+ /* force low perf level for thermal */ -+ amdgpu_dpm_force_performance_level(adev, AMDGPU_DPM_FORCED_LEVEL_LOW); -+ /* save the user's level */ -+ adev->pm.dpm.forced_level = level; -+ } else { -+ /* otherwise, user selected level */ -+ amdgpu_dpm_force_performance_level(adev, adev->pm.dpm.forced_level); -+ } -+ } -+ -+done: -+ mutex_unlock(&adev->ring_lock); -+ up_write(&adev->pm.mclk_lock); -+ mutex_unlock(&adev->ddev->struct_mutex); -+} -+ -+void amdgpu_dpm_enable_uvd(struct amdgpu_device *adev, bool enable) -+{ -+ if (adev->pm.funcs->powergate_uvd) { -+ mutex_lock(&adev->pm.mutex); -+ /* enable/disable UVD */ -+ amdgpu_dpm_powergate_uvd(adev, !enable); -+ mutex_unlock(&adev->pm.mutex); -+ } else { -+ if (enable) { -+ mutex_lock(&adev->pm.mutex); -+ adev->pm.dpm.uvd_active = true; -+ adev->pm.dpm.state = POWER_STATE_TYPE_INTERNAL_UVD; -+ mutex_unlock(&adev->pm.mutex); -+ } else { -+ mutex_lock(&adev->pm.mutex); -+ adev->pm.dpm.uvd_active = false; -+ mutex_unlock(&adev->pm.mutex); -+ } -+ -+ amdgpu_pm_compute_clocks(adev); -+ } -+} -+ -+void amdgpu_dpm_enable_vce(struct amdgpu_device *adev, bool enable) -+{ -+ if (enable) { -+ mutex_lock(&adev->pm.mutex); -+ adev->pm.dpm.vce_active = true; -+ /* XXX select vce level based on ring/task */ -+ adev->pm.dpm.vce_level = AMDGPU_VCE_LEVEL_AC_ALL; -+ mutex_unlock(&adev->pm.mutex); -+ } else { -+ mutex_lock(&adev->pm.mutex); -+ adev->pm.dpm.vce_active = false; -+ mutex_unlock(&adev->pm.mutex); -+ } -+ -+ amdgpu_pm_compute_clocks(adev); -+} -+ -+void amdgpu_pm_print_power_states(struct amdgpu_device *adev) -+{ -+ int i; -+ -+ for (i = 0; i < adev->pm.dpm.num_ps; i++) { -+ printk("== power state %d ==\n", i); -+ amdgpu_dpm_print_power_state(adev, &adev->pm.dpm.ps[i]); -+ } -+} -+ -+int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) -+{ -+ int ret; -+ -+ if (adev->pm.funcs->get_temperature == NULL) -+ return 0; -+ adev->pm.int_hwmon_dev = hwmon_device_register_with_groups(adev->dev, -+ DRIVER_NAME, adev, -+ hwmon_groups); -+ if (IS_ERR(adev->pm.int_hwmon_dev)) { -+ ret = PTR_ERR(adev->pm.int_hwmon_dev); -+ dev_err(adev->dev, -+ "Unable to register hwmon device: %d\n", ret); -+ return ret; -+ } -+ -+ ret = device_create_file(adev->dev, &dev_attr_power_dpm_state); -+ if (ret) { -+ DRM_ERROR("failed to create device file for dpm state\n"); -+ return ret; -+ } -+ ret = device_create_file(adev->dev, &dev_attr_power_dpm_force_performance_level); -+ if (ret) { -+ DRM_ERROR("failed to create device file for dpm state\n"); -+ return ret; -+ } -+ ret = amdgpu_debugfs_pm_init(adev); -+ if (ret) { -+ DRM_ERROR("Failed to register debugfs file for dpm!\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev) -+{ -+ if (adev->pm.int_hwmon_dev) -+ hwmon_device_unregister(adev->pm.int_hwmon_dev); -+ device_remove_file(adev->dev, &dev_attr_power_dpm_state); -+ device_remove_file(adev->dev, &dev_attr_power_dpm_force_performance_level); -+} -+ -+void amdgpu_pm_compute_clocks(struct amdgpu_device *adev) -+{ -+ struct drm_device *ddev = adev->ddev; -+ struct drm_crtc *crtc; -+ struct amdgpu_crtc *amdgpu_crtc; -+ -+ if (!adev->pm.dpm_enabled) -+ return; -+ -+ mutex_lock(&adev->pm.mutex); -+ -+ /* update active crtc counts */ -+ adev->pm.dpm.new_active_crtcs = 0; -+ adev->pm.dpm.new_active_crtc_count = 0; -+ if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { -+ list_for_each_entry(crtc, -+ &ddev->mode_config.crtc_list, head) { -+ amdgpu_crtc = to_amdgpu_crtc(crtc); -+ if (crtc->enabled) { -+ adev->pm.dpm.new_active_crtcs |= (1 << amdgpu_crtc->crtc_id); -+ adev->pm.dpm.new_active_crtc_count++; -+ } -+ } -+ } -+ -+ /* update battery/ac status */ -+ if (power_supply_is_system_supplied() > 0) -+ adev->pm.dpm.ac_power = true; -+ else -+ adev->pm.dpm.ac_power = false; -+ -+ amdgpu_dpm_change_power_state_locked(adev); -+ -+ mutex_unlock(&adev->pm.mutex); -+ -+} -+ -+/* -+ * Debugfs info -+ */ -+#if defined(CONFIG_DEBUG_FS) -+ -+static int amdgpu_debugfs_pm_info(struct seq_file *m, void *data) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (adev->pm.dpm_enabled) { -+ mutex_lock(&adev->pm.mutex); -+ if (adev->pm.funcs->debugfs_print_current_performance_level) -+ amdgpu_dpm_debugfs_print_current_performance_level(adev, m); -+ else -+ seq_printf(m, "Debugfs support not implemented for this asic\n"); -+ mutex_unlock(&adev->pm.mutex); -+ } -+ -+ return 0; -+} -+ -+static struct drm_info_list amdgpu_pm_info_list[] = { -+ {"amdgpu_pm_info", amdgpu_debugfs_pm_info, 0, NULL}, -+}; -+#endif -+ -+static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ return amdgpu_debugfs_add_files(adev, amdgpu_pm_info_list, ARRAY_SIZE(amdgpu_pm_info_list)); -+#else -+ return 0; -+#endif -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h -new file mode 100644 -index 0000000..5fd7734 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h -@@ -0,0 +1,35 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_PM_H__ -+#define __AMDGPU_PM_H__ -+ -+int amdgpu_pm_sysfs_init(struct amdgpu_device *adev); -+void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev); -+void amdgpu_pm_print_power_states(struct amdgpu_device *adev); -+void amdgpu_pm_compute_clocks(struct amdgpu_device *adev); -+void amdgpu_dpm_thermal_work_handler(struct work_struct *work); -+void amdgpu_dpm_enable_uvd(struct amdgpu_device *adev, bool enable); -+void amdgpu_dpm_enable_vce(struct amdgpu_device *adev, bool enable); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c -new file mode 100644 -index 0000000..d9652fe ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c -@@ -0,0 +1,125 @@ -+/* -+ * Copyright 2012 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ * based on nouveau_prime.c -+ * -+ * Authors: Alex Deucher -+ */ -+#include <drm/drmP.h> -+ -+#include "amdgpu.h" -+#include <drm/amdgpu_drm.h> -+#include <linux/dma-buf.h> -+ -+struct sg_table *amdgpu_gem_prime_get_sg_table(struct drm_gem_object *obj) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -+ int npages = bo->tbo.num_pages; -+ -+ return drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); -+} -+ -+void *amdgpu_gem_prime_vmap(struct drm_gem_object *obj) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -+ int ret; -+ -+ ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, -+ &bo->dma_buf_vmap); -+ if (ret) -+ return ERR_PTR(ret); -+ -+ return bo->dma_buf_vmap.virtual; -+} -+ -+void amdgpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -+ -+ ttm_bo_kunmap(&bo->dma_buf_vmap); -+} -+ -+struct drm_gem_object *amdgpu_gem_prime_import_sg_table(struct drm_device *dev, -+ struct dma_buf_attachment *attach, -+ struct sg_table *sg) -+{ -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_bo *bo; -+ int ret; -+ -+ ret = amdgpu_bo_create(adev, attach->dmabuf->size, PAGE_SIZE, false, -+ AMDGPU_GEM_DOMAIN_GTT, 0, sg, &bo); -+ if (ret) -+ return ERR_PTR(ret); -+ -+ mutex_lock(&adev->gem.mutex); -+ list_add_tail(&bo->list, &adev->gem.objects); -+ mutex_unlock(&adev->gem.mutex); -+ -+ return &bo->gem_base; -+} -+ -+int amdgpu_gem_prime_pin(struct drm_gem_object *obj) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -+ int ret = 0; -+ -+ ret = amdgpu_bo_reserve(bo, false); -+ if (unlikely(ret != 0)) -+ return ret; -+ -+ /* pin buffer into GTT */ -+ ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT, NULL); -+ amdgpu_bo_unreserve(bo); -+ return ret; -+} -+ -+void amdgpu_gem_prime_unpin(struct drm_gem_object *obj) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -+ int ret = 0; -+ -+ ret = amdgpu_bo_reserve(bo, false); -+ if (unlikely(ret != 0)) -+ return; -+ -+ amdgpu_bo_unpin(bo); -+ amdgpu_bo_unreserve(bo); -+} -+ -+struct reservation_object *amdgpu_gem_prime_res_obj(struct drm_gem_object *obj) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); -+ -+ return bo->tbo.resv; -+} -+ -+struct dma_buf *amdgpu_gem_prime_export(struct drm_device *dev, -+ struct drm_gem_object *gobj, -+ int flags) -+{ -+ struct amdgpu_bo *bo = gem_to_amdgpu_bo(gobj); -+ -+ if (amdgpu_ttm_tt_has_userptr(bo->tbo.ttm)) -+ return ERR_PTR(-EPERM); -+ -+ return drm_gem_prime_export(dev, gobj, flags); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c -new file mode 100644 -index 0000000..855e219 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c -@@ -0,0 +1,561 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ * Christian König -+ */ -+#include <linux/seq_file.h> -+#include <linux/slab.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "atom.h" -+ -+/* -+ * Rings -+ * Most engines on the GPU are fed via ring buffers. Ring -+ * buffers are areas of GPU accessible memory that the host -+ * writes commands into and the GPU reads commands out of. -+ * There is a rptr (read pointer) that determines where the -+ * GPU is currently reading, and a wptr (write pointer) -+ * which determines where the host has written. When the -+ * pointers are equal, the ring is idle. When the host -+ * writes commands to the ring buffer, it increments the -+ * wptr. The GPU then starts fetching commands and executes -+ * them until the pointers are equal again. -+ */ -+static int amdgpu_debugfs_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring); -+ -+/** -+ * amdgpu_ring_free_size - update the free size -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Update the free dw slots in the ring buffer (all asics). -+ */ -+void amdgpu_ring_free_size(struct amdgpu_ring *ring) -+{ -+ uint32_t rptr = amdgpu_ring_get_rptr(ring); -+ -+ /* This works because ring_size is a power of 2 */ -+ ring->ring_free_dw = rptr + (ring->ring_size / 4); -+ ring->ring_free_dw -= ring->wptr; -+ ring->ring_free_dw &= ring->ptr_mask; -+ if (!ring->ring_free_dw) { -+ /* this is an empty ring */ -+ ring->ring_free_dw = ring->ring_size / 4; -+ /* update lockup info to avoid false positive */ -+ amdgpu_ring_lockup_update(ring); -+ } -+} -+ -+/** -+ * amdgpu_ring_alloc - allocate space on the ring buffer -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: amdgpu_ring structure holding ring information -+ * @ndw: number of dwords to allocate in the ring buffer -+ * -+ * Allocate @ndw dwords in the ring buffer (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_ring_alloc(struct amdgpu_ring *ring, unsigned ndw) -+{ -+ int r; -+ -+ /* make sure we aren't trying to allocate more space than there is on the ring */ -+ if (ndw > (ring->ring_size / 4)) -+ return -ENOMEM; -+ /* Align requested size with padding so unlock_commit can -+ * pad safely */ -+ amdgpu_ring_free_size(ring); -+ ndw = (ndw + ring->align_mask) & ~ring->align_mask; -+ while (ndw > (ring->ring_free_dw - 1)) { -+ amdgpu_ring_free_size(ring); -+ if (ndw < ring->ring_free_dw) { -+ break; -+ } -+ r = amdgpu_fence_wait_next(ring); -+ if (r) -+ return r; -+ } -+ ring->count_dw = ndw; -+ ring->wptr_old = ring->wptr; -+ return 0; -+} -+ -+/** -+ * amdgpu_ring_lock - lock the ring and allocate space on it -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: amdgpu_ring structure holding ring information -+ * @ndw: number of dwords to allocate in the ring buffer -+ * -+ * Lock the ring and allocate @ndw dwords in the ring buffer -+ * (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_ring_lock(struct amdgpu_ring *ring, unsigned ndw) -+{ -+ int r; -+ -+ mutex_lock(ring->ring_lock); -+ r = amdgpu_ring_alloc(ring, ndw); -+ if (r) { -+ mutex_unlock(ring->ring_lock); -+ return r; -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_ring_commit - tell the GPU to execute the new -+ * commands on the ring buffer -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Update the wptr (write pointer) to tell the GPU to -+ * execute new commands on the ring buffer (all asics). -+ */ -+void amdgpu_ring_commit(struct amdgpu_ring *ring) -+{ -+ /* We pad to match fetch size */ -+ while (ring->wptr & ring->align_mask) { -+ amdgpu_ring_write(ring, ring->nop); -+ } -+ mb(); -+ amdgpu_ring_set_wptr(ring); -+} -+ -+/** -+ * amdgpu_ring_unlock_commit - tell the GPU to execute the new -+ * commands on the ring buffer and unlock it -+ * -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Call amdgpu_ring_commit() then unlock the ring (all asics). -+ */ -+void amdgpu_ring_unlock_commit(struct amdgpu_ring *ring) -+{ -+ amdgpu_ring_commit(ring); -+ mutex_unlock(ring->ring_lock); -+} -+ -+/** -+ * amdgpu_ring_undo - reset the wptr -+ * -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Reset the driver's copy of the wptr (all asics). -+ */ -+void amdgpu_ring_undo(struct amdgpu_ring *ring) -+{ -+ ring->wptr = ring->wptr_old; -+} -+ -+/** -+ * amdgpu_ring_unlock_undo - reset the wptr and unlock the ring -+ * -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Call amdgpu_ring_undo() then unlock the ring (all asics). -+ */ -+void amdgpu_ring_unlock_undo(struct amdgpu_ring *ring) -+{ -+ amdgpu_ring_undo(ring); -+ mutex_unlock(ring->ring_lock); -+} -+ -+/** -+ * amdgpu_ring_lockup_update - update lockup variables -+ * -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Update the last rptr value and timestamp (all asics). -+ */ -+void amdgpu_ring_lockup_update(struct amdgpu_ring *ring) -+{ -+ atomic_set(&ring->last_rptr, amdgpu_ring_get_rptr(ring)); -+ atomic64_set(&ring->last_activity, jiffies_64); -+} -+ -+/** -+ * amdgpu_ring_test_lockup() - check if ring is lockedup by recording information -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ */ -+bool amdgpu_ring_test_lockup(struct amdgpu_ring *ring) -+{ -+ uint32_t rptr = amdgpu_ring_get_rptr(ring); -+ uint64_t last = atomic64_read(&ring->last_activity); -+ uint64_t elapsed; -+ -+ if (rptr != atomic_read(&ring->last_rptr)) { -+ /* ring is still working, no lockup */ -+ amdgpu_ring_lockup_update(ring); -+ return false; -+ } -+ -+ elapsed = jiffies_to_msecs(jiffies_64 - last); -+ if (amdgpu_lockup_timeout && elapsed >= amdgpu_lockup_timeout) { -+ dev_err(ring->adev->dev, "ring %d stalled for more than %llumsec\n", -+ ring->idx, elapsed); -+ return true; -+ } -+ /* give a chance to the GPU ... */ -+ return false; -+} -+ -+/** -+ * amdgpu_ring_backup - Back up the content of a ring -+ * -+ * @ring: the ring we want to back up -+ * -+ * Saves all unprocessed commits from a ring, returns the number of dwords saved. -+ */ -+unsigned amdgpu_ring_backup(struct amdgpu_ring *ring, -+ uint32_t **data) -+{ -+ unsigned size, ptr, i; -+ -+ /* just in case lock the ring */ -+ mutex_lock(ring->ring_lock); -+ *data = NULL; -+ -+ if (ring->ring_obj == NULL) { -+ mutex_unlock(ring->ring_lock); -+ return 0; -+ } -+ -+ /* it doesn't make sense to save anything if all fences are signaled */ -+ if (!amdgpu_fence_count_emitted(ring)) { -+ mutex_unlock(ring->ring_lock); -+ return 0; -+ } -+ -+ ptr = le32_to_cpu(*ring->next_rptr_cpu_addr); -+ -+ size = ring->wptr + (ring->ring_size / 4); -+ size -= ptr; -+ size &= ring->ptr_mask; -+ if (size == 0) { -+ mutex_unlock(ring->ring_lock); -+ return 0; -+ } -+ -+ /* and then save the content of the ring */ -+ *data = kmalloc_array(size, sizeof(uint32_t), GFP_KERNEL); -+ if (!*data) { -+ mutex_unlock(ring->ring_lock); -+ return 0; -+ } -+ for (i = 0; i < size; ++i) { -+ (*data)[i] = ring->ring[ptr++]; -+ ptr &= ring->ptr_mask; -+ } -+ -+ mutex_unlock(ring->ring_lock); -+ return size; -+} -+ -+/** -+ * amdgpu_ring_restore - append saved commands to the ring again -+ * -+ * @ring: ring to append commands to -+ * @size: number of dwords we want to write -+ * @data: saved commands -+ * -+ * Allocates space on the ring and restore the previously saved commands. -+ */ -+int amdgpu_ring_restore(struct amdgpu_ring *ring, -+ unsigned size, uint32_t *data) -+{ -+ int i, r; -+ -+ if (!size || !data) -+ return 0; -+ -+ /* restore the saved ring content */ -+ r = amdgpu_ring_lock(ring, size); -+ if (r) -+ return r; -+ -+ for (i = 0; i < size; ++i) { -+ amdgpu_ring_write(ring, data[i]); -+ } -+ -+ amdgpu_ring_unlock_commit(ring); -+ kfree(data); -+ return 0; -+} -+ -+/** -+ * amdgpu_ring_init - init driver ring struct. -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: amdgpu_ring structure holding ring information -+ * @ring_size: size of the ring -+ * @nop: nop packet for this ring -+ * -+ * Initialize the driver information for the selected ring (all asics). -+ * Returns 0 on success, error on failure. -+ */ -+int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, -+ unsigned ring_size, u32 nop, u32 align_mask, -+ struct amdgpu_irq_src *irq_src, unsigned irq_type, -+ enum amdgpu_ring_type ring_type) -+{ -+ u32 rb_bufsz; -+ int r; -+ -+ if (ring->adev == NULL) { -+ if (adev->num_rings >= AMDGPU_MAX_RINGS) -+ return -EINVAL; -+ -+ ring->adev = adev; -+ ring->idx = adev->num_rings++; -+ adev->rings[ring->idx] = ring; -+ amdgpu_fence_driver_init_ring(ring); -+ } -+ -+ r = amdgpu_wb_get(adev, &ring->rptr_offs); -+ if (r) { -+ dev_err(adev->dev, "(%d) ring rptr_offs wb alloc failed\n", r); -+ return r; -+ } -+ -+ r = amdgpu_wb_get(adev, &ring->wptr_offs); -+ if (r) { -+ dev_err(adev->dev, "(%d) ring wptr_offs wb alloc failed\n", r); -+ return r; -+ } -+ -+ r = amdgpu_wb_get(adev, &ring->fence_offs); -+ if (r) { -+ dev_err(adev->dev, "(%d) ring fence_offs wb alloc failed\n", r); -+ return r; -+ } -+ -+ r = amdgpu_wb_get(adev, &ring->next_rptr_offs); -+ if (r) { -+ dev_err(adev->dev, "(%d) ring next_rptr wb alloc failed\n", r); -+ return r; -+ } -+ ring->next_rptr_gpu_addr = adev->wb.gpu_addr + (ring->next_rptr_offs * 4); -+ ring->next_rptr_cpu_addr = &adev->wb.wb[ring->next_rptr_offs]; -+ -+ r = amdgpu_fence_driver_start_ring(ring, irq_src, irq_type); -+ if (r) { -+ dev_err(adev->dev, "failed initializing fences (%d).\n", r); -+ return r; -+ } -+ -+ ring->ring_lock = &adev->ring_lock; -+ /* Align ring size */ -+ rb_bufsz = order_base_2(ring_size / 8); -+ ring_size = (1 << (rb_bufsz + 1)) * 4; -+ ring->ring_size = ring_size; -+ ring->align_mask = align_mask; -+ ring->nop = nop; -+ ring->type = ring_type; -+ -+ /* Allocate ring buffer */ -+ if (ring->ring_obj == NULL) { -+ r = amdgpu_bo_create(adev, ring->ring_size, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_GTT, 0, -+ NULL, &ring->ring_obj); -+ if (r) { -+ dev_err(adev->dev, "(%d) ring create failed\n", r); -+ return r; -+ } -+ r = amdgpu_bo_reserve(ring->ring_obj, false); -+ if (unlikely(r != 0)) -+ return r; -+ r = amdgpu_bo_pin(ring->ring_obj, AMDGPU_GEM_DOMAIN_GTT, -+ &ring->gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(ring->ring_obj); -+ dev_err(adev->dev, "(%d) ring pin failed\n", r); -+ return r; -+ } -+ r = amdgpu_bo_kmap(ring->ring_obj, -+ (void **)&ring->ring); -+ amdgpu_bo_unreserve(ring->ring_obj); -+ if (r) { -+ dev_err(adev->dev, "(%d) ring map failed\n", r); -+ return r; -+ } -+ } -+ ring->ptr_mask = (ring->ring_size / 4) - 1; -+ ring->ring_free_dw = ring->ring_size / 4; -+ -+ if (amdgpu_debugfs_ring_init(adev, ring)) { -+ DRM_ERROR("Failed to register debugfs file for rings !\n"); -+ } -+ amdgpu_ring_lockup_update(ring); -+ return 0; -+} -+ -+/** -+ * amdgpu_ring_fini - tear down the driver ring struct. -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: amdgpu_ring structure holding ring information -+ * -+ * Tear down the driver information for the selected ring (all asics). -+ */ -+void amdgpu_ring_fini(struct amdgpu_ring *ring) -+{ -+ int r; -+ struct amdgpu_bo *ring_obj; -+ -+ if (ring->ring_lock == NULL) -+ return; -+ -+ mutex_lock(ring->ring_lock); -+ ring_obj = ring->ring_obj; -+ ring->ready = false; -+ ring->ring = NULL; -+ ring->ring_obj = NULL; -+ mutex_unlock(ring->ring_lock); -+ -+ amdgpu_wb_free(ring->adev, ring->fence_offs); -+ amdgpu_wb_free(ring->adev, ring->rptr_offs); -+ amdgpu_wb_free(ring->adev, ring->wptr_offs); -+ amdgpu_wb_free(ring->adev, ring->next_rptr_offs); -+ -+ if (ring_obj) { -+ r = amdgpu_bo_reserve(ring_obj, false); -+ if (likely(r == 0)) { -+ amdgpu_bo_kunmap(ring_obj); -+ amdgpu_bo_unpin(ring_obj); -+ amdgpu_bo_unreserve(ring_obj); -+ } -+ amdgpu_bo_unref(&ring_obj); -+ } -+} -+ -+/* -+ * Debugfs info -+ */ -+#if defined(CONFIG_DEBUG_FS) -+ -+static int amdgpu_debugfs_ring_info(struct seq_file *m, void *data) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ int roffset = *(int*)node->info_ent->data; -+ struct amdgpu_ring *ring = (void *)(((uint8_t*)adev) + roffset); -+ -+ uint32_t rptr, wptr, rptr_next; -+ unsigned count, i, j; -+ -+ amdgpu_ring_free_size(ring); -+ count = (ring->ring_size / 4) - ring->ring_free_dw; -+ -+ wptr = amdgpu_ring_get_wptr(ring); -+ seq_printf(m, "wptr: 0x%08x [%5d]\n", -+ wptr, wptr); -+ -+ rptr = amdgpu_ring_get_rptr(ring); -+ seq_printf(m, "rptr: 0x%08x [%5d]\n", -+ rptr, rptr); -+ -+ rptr_next = ~0; -+ -+ seq_printf(m, "driver's copy of the wptr: 0x%08x [%5d]\n", -+ ring->wptr, ring->wptr); -+ seq_printf(m, "last semaphore signal addr : 0x%016llx\n", -+ ring->last_semaphore_signal_addr); -+ seq_printf(m, "last semaphore wait addr : 0x%016llx\n", -+ ring->last_semaphore_wait_addr); -+ seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); -+ seq_printf(m, "%u dwords in ring\n", count); -+ -+ if (!ring->ready) -+ return 0; -+ -+ /* print 8 dw before current rptr as often it's the last executed -+ * packet that is the root issue -+ */ -+ i = (rptr + ring->ptr_mask + 1 - 32) & ring->ptr_mask; -+ for (j = 0; j <= (count + 32); j++) { -+ seq_printf(m, "r[%5d]=0x%08x", i, ring->ring[i]); -+ if (rptr == i) -+ seq_puts(m, " *"); -+ if (rptr_next == i) -+ seq_puts(m, " #"); -+ seq_puts(m, "\n"); -+ i = (i + 1) & ring->ptr_mask; -+ } -+ return 0; -+} -+ -+/* TODO: clean this up !*/ -+static int amdgpu_gfx_index = offsetof(struct amdgpu_device, gfx.gfx_ring[0]); -+static int cayman_cp1_index = offsetof(struct amdgpu_device, gfx.compute_ring[0]); -+static int cayman_cp2_index = offsetof(struct amdgpu_device, gfx.compute_ring[1]); -+static int amdgpu_dma1_index = offsetof(struct amdgpu_device, sdma[0].ring); -+static int amdgpu_dma2_index = offsetof(struct amdgpu_device, sdma[1].ring); -+static int r600_uvd_index = offsetof(struct amdgpu_device, uvd.ring); -+static int si_vce1_index = offsetof(struct amdgpu_device, vce.ring[0]); -+static int si_vce2_index = offsetof(struct amdgpu_device, vce.ring[1]); -+ -+static struct drm_info_list amdgpu_debugfs_ring_info_list[] = { -+ {"amdgpu_ring_gfx", amdgpu_debugfs_ring_info, 0, &amdgpu_gfx_index}, -+ {"amdgpu_ring_cp1", amdgpu_debugfs_ring_info, 0, &cayman_cp1_index}, -+ {"amdgpu_ring_cp2", amdgpu_debugfs_ring_info, 0, &cayman_cp2_index}, -+ {"amdgpu_ring_dma1", amdgpu_debugfs_ring_info, 0, &amdgpu_dma1_index}, -+ {"amdgpu_ring_dma2", amdgpu_debugfs_ring_info, 0, &amdgpu_dma2_index}, -+ {"amdgpu_ring_uvd", amdgpu_debugfs_ring_info, 0, &r600_uvd_index}, -+ {"amdgpu_ring_vce1", amdgpu_debugfs_ring_info, 0, &si_vce1_index}, -+ {"amdgpu_ring_vce2", amdgpu_debugfs_ring_info, 0, &si_vce2_index}, -+}; -+ -+#endif -+ -+static int amdgpu_debugfs_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ unsigned i; -+ for (i = 0; i < ARRAY_SIZE(amdgpu_debugfs_ring_info_list); ++i) { -+ struct drm_info_list *info = &amdgpu_debugfs_ring_info_list[i]; -+ int roffset = *(int*)amdgpu_debugfs_ring_info_list[i].data; -+ struct amdgpu_ring *other = (void *)(((uint8_t*)adev) + roffset); -+ unsigned r; -+ -+ if (other != ring) -+ continue; -+ -+ r = amdgpu_debugfs_add_files(adev, info, 1); -+ if (r) -+ return r; -+ } -+#endif -+ return 0; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c -new file mode 100644 -index 0000000..eb20987 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c -@@ -0,0 +1,419 @@ -+/* -+ * Copyright 2011 Red Hat 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Jerome Glisse <glisse@freedesktop.org> -+ */ -+/* Algorithm: -+ * -+ * We store the last allocated bo in "hole", we always try to allocate -+ * after the last allocated bo. Principle is that in a linear GPU ring -+ * progression was is after last is the oldest bo we allocated and thus -+ * the first one that should no longer be in use by the GPU. -+ * -+ * If it's not the case we skip over the bo after last to the closest -+ * done bo if such one exist. If none exist and we are not asked to -+ * block we report failure to allocate. -+ * -+ * If we are asked to block we wait on all the oldest fence of all -+ * rings. We just wait for any of those fence to complete. -+ */ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+ -+static void amdgpu_sa_bo_remove_locked(struct amdgpu_sa_bo *sa_bo); -+static void amdgpu_sa_bo_try_free(struct amdgpu_sa_manager *sa_manager); -+ -+int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager, -+ unsigned size, u32 align, u32 domain) -+{ -+ int i, r; -+ -+ init_waitqueue_head(&sa_manager->wq); -+ sa_manager->bo = NULL; -+ sa_manager->size = size; -+ sa_manager->domain = domain; -+ sa_manager->align = align; -+ sa_manager->hole = &sa_manager->olist; -+ INIT_LIST_HEAD(&sa_manager->olist); -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ INIT_LIST_HEAD(&sa_manager->flist[i]); -+ } -+ -+ r = amdgpu_bo_create(adev, size, align, true, -+ domain, 0, NULL, &sa_manager->bo); -+ if (r) { -+ dev_err(adev->dev, "(%d) failed to allocate bo for manager\n", r); -+ return r; -+ } -+ -+ return r; -+} -+ -+void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager) -+{ -+ struct amdgpu_sa_bo *sa_bo, *tmp; -+ -+ if (!list_empty(&sa_manager->olist)) { -+ sa_manager->hole = &sa_manager->olist, -+ amdgpu_sa_bo_try_free(sa_manager); -+ if (!list_empty(&sa_manager->olist)) { -+ dev_err(adev->dev, "sa_manager is not empty, clearing anyway\n"); -+ } -+ } -+ list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) { -+ amdgpu_sa_bo_remove_locked(sa_bo); -+ } -+ amdgpu_bo_unref(&sa_manager->bo); -+ sa_manager->size = 0; -+} -+ -+int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager) -+{ -+ int r; -+ -+ if (sa_manager->bo == NULL) { -+ dev_err(adev->dev, "no bo for sa manager\n"); -+ return -EINVAL; -+ } -+ -+ /* map the buffer */ -+ r = amdgpu_bo_reserve(sa_manager->bo, false); -+ if (r) { -+ dev_err(adev->dev, "(%d) failed to reserve manager bo\n", r); -+ return r; -+ } -+ r = amdgpu_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(sa_manager->bo); -+ dev_err(adev->dev, "(%d) failed to pin manager bo\n", r); -+ return r; -+ } -+ r = amdgpu_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr); -+ amdgpu_bo_unreserve(sa_manager->bo); -+ return r; -+} -+ -+int amdgpu_sa_bo_manager_suspend(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager) -+{ -+ int r; -+ -+ if (sa_manager->bo == NULL) { -+ dev_err(adev->dev, "no bo for sa manager\n"); -+ return -EINVAL; -+ } -+ -+ r = amdgpu_bo_reserve(sa_manager->bo, false); -+ if (!r) { -+ amdgpu_bo_kunmap(sa_manager->bo); -+ amdgpu_bo_unpin(sa_manager->bo); -+ amdgpu_bo_unreserve(sa_manager->bo); -+ } -+ return r; -+} -+ -+static void amdgpu_sa_bo_remove_locked(struct amdgpu_sa_bo *sa_bo) -+{ -+ struct amdgpu_sa_manager *sa_manager = sa_bo->manager; -+ if (sa_manager->hole == &sa_bo->olist) { -+ sa_manager->hole = sa_bo->olist.prev; -+ } -+ list_del_init(&sa_bo->olist); -+ list_del_init(&sa_bo->flist); -+ amdgpu_fence_unref(&sa_bo->fence); -+ kfree(sa_bo); -+} -+ -+static void amdgpu_sa_bo_try_free(struct amdgpu_sa_manager *sa_manager) -+{ -+ struct amdgpu_sa_bo *sa_bo, *tmp; -+ -+ if (sa_manager->hole->next == &sa_manager->olist) -+ return; -+ -+ sa_bo = list_entry(sa_manager->hole->next, struct amdgpu_sa_bo, olist); -+ list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) { -+ if (sa_bo->fence == NULL || !amdgpu_fence_signaled(sa_bo->fence)) { -+ return; -+ } -+ amdgpu_sa_bo_remove_locked(sa_bo); -+ } -+} -+ -+static inline unsigned amdgpu_sa_bo_hole_soffset(struct amdgpu_sa_manager *sa_manager) -+{ -+ struct list_head *hole = sa_manager->hole; -+ -+ if (hole != &sa_manager->olist) { -+ return list_entry(hole, struct amdgpu_sa_bo, olist)->eoffset; -+ } -+ return 0; -+} -+ -+static inline unsigned amdgpu_sa_bo_hole_eoffset(struct amdgpu_sa_manager *sa_manager) -+{ -+ struct list_head *hole = sa_manager->hole; -+ -+ if (hole->next != &sa_manager->olist) { -+ return list_entry(hole->next, struct amdgpu_sa_bo, olist)->soffset; -+ } -+ return sa_manager->size; -+} -+ -+static bool amdgpu_sa_bo_try_alloc(struct amdgpu_sa_manager *sa_manager, -+ struct amdgpu_sa_bo *sa_bo, -+ unsigned size, unsigned align) -+{ -+ unsigned soffset, eoffset, wasted; -+ -+ soffset = amdgpu_sa_bo_hole_soffset(sa_manager); -+ eoffset = amdgpu_sa_bo_hole_eoffset(sa_manager); -+ wasted = (align - (soffset % align)) % align; -+ -+ if ((eoffset - soffset) >= (size + wasted)) { -+ soffset += wasted; -+ -+ sa_bo->manager = sa_manager; -+ sa_bo->soffset = soffset; -+ sa_bo->eoffset = soffset + size; -+ list_add(&sa_bo->olist, sa_manager->hole); -+ INIT_LIST_HEAD(&sa_bo->flist); -+ sa_manager->hole = &sa_bo->olist; -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * amdgpu_sa_event - Check if we can stop waiting -+ * -+ * @sa_manager: pointer to the sa_manager -+ * @size: number of bytes we want to allocate -+ * @align: alignment we need to match -+ * -+ * Check if either there is a fence we can wait for or -+ * enough free memory to satisfy the allocation directly -+ */ -+static bool amdgpu_sa_event(struct amdgpu_sa_manager *sa_manager, -+ unsigned size, unsigned align) -+{ -+ unsigned soffset, eoffset, wasted; -+ int i; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ if (!list_empty(&sa_manager->flist[i])) { -+ return true; -+ } -+ } -+ -+ soffset = amdgpu_sa_bo_hole_soffset(sa_manager); -+ eoffset = amdgpu_sa_bo_hole_eoffset(sa_manager); -+ wasted = (align - (soffset % align)) % align; -+ -+ if ((eoffset - soffset) >= (size + wasted)) { -+ return true; -+ } -+ -+ return false; -+} -+ -+static bool amdgpu_sa_bo_next_hole(struct amdgpu_sa_manager *sa_manager, -+ struct amdgpu_fence **fences, -+ unsigned *tries) -+{ -+ struct amdgpu_sa_bo *best_bo = NULL; -+ unsigned i, soffset, best, tmp; -+ -+ /* if hole points to the end of the buffer */ -+ if (sa_manager->hole->next == &sa_manager->olist) { -+ /* try again with its beginning */ -+ sa_manager->hole = &sa_manager->olist; -+ return true; -+ } -+ -+ soffset = amdgpu_sa_bo_hole_soffset(sa_manager); -+ /* to handle wrap around we add sa_manager->size */ -+ best = sa_manager->size * 2; -+ /* go over all fence list and try to find the closest sa_bo -+ * of the current last -+ */ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_sa_bo *sa_bo; -+ -+ if (list_empty(&sa_manager->flist[i])) { -+ continue; -+ } -+ -+ sa_bo = list_first_entry(&sa_manager->flist[i], -+ struct amdgpu_sa_bo, flist); -+ -+ if (!amdgpu_fence_signaled(sa_bo->fence)) { -+ fences[i] = sa_bo->fence; -+ continue; -+ } -+ -+ /* limit the number of tries each ring gets */ -+ if (tries[i] > 2) { -+ continue; -+ } -+ -+ tmp = sa_bo->soffset; -+ if (tmp < soffset) { -+ /* wrap around, pretend it's after */ -+ tmp += sa_manager->size; -+ } -+ tmp -= soffset; -+ if (tmp < best) { -+ /* this sa bo is the closest one */ -+ best = tmp; -+ best_bo = sa_bo; -+ } -+ } -+ -+ if (best_bo) { -+ ++tries[best_bo->fence->ring->idx]; -+ sa_manager->hole = best_bo->olist.prev; -+ -+ /* we knew that this one is signaled, -+ so it's save to remote it */ -+ amdgpu_sa_bo_remove_locked(best_bo); -+ return true; -+ } -+ return false; -+} -+ -+int amdgpu_sa_bo_new(struct amdgpu_device *adev, -+ struct amdgpu_sa_manager *sa_manager, -+ struct amdgpu_sa_bo **sa_bo, -+ unsigned size, unsigned align) -+{ -+ struct amdgpu_fence *fences[AMDGPU_MAX_RINGS]; -+ unsigned tries[AMDGPU_MAX_RINGS]; -+ int i, r; -+ -+ BUG_ON(align > sa_manager->align); -+ BUG_ON(size > sa_manager->size); -+ -+ *sa_bo = kmalloc(sizeof(struct amdgpu_sa_bo), GFP_KERNEL); -+ if ((*sa_bo) == NULL) { -+ return -ENOMEM; -+ } -+ (*sa_bo)->manager = sa_manager; -+ (*sa_bo)->fence = NULL; -+ INIT_LIST_HEAD(&(*sa_bo)->olist); -+ INIT_LIST_HEAD(&(*sa_bo)->flist); -+ -+ spin_lock(&sa_manager->wq.lock); -+ do { -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ fences[i] = NULL; -+ tries[i] = 0; -+ } -+ -+ do { -+ amdgpu_sa_bo_try_free(sa_manager); -+ -+ if (amdgpu_sa_bo_try_alloc(sa_manager, *sa_bo, -+ size, align)) { -+ spin_unlock(&sa_manager->wq.lock); -+ return 0; -+ } -+ -+ /* see if we can skip over some allocations */ -+ } while (amdgpu_sa_bo_next_hole(sa_manager, fences, tries)); -+ -+ spin_unlock(&sa_manager->wq.lock); -+ r = amdgpu_fence_wait_any(adev, fences, false); -+ spin_lock(&sa_manager->wq.lock); -+ /* if we have nothing to wait for block */ -+ if (r == -ENOENT) { -+ r = wait_event_interruptible_locked( -+ sa_manager->wq, -+ amdgpu_sa_event(sa_manager, size, align) -+ ); -+ } -+ -+ } while (!r); -+ -+ spin_unlock(&sa_manager->wq.lock); -+ kfree(*sa_bo); -+ *sa_bo = NULL; -+ return r; -+} -+ -+void amdgpu_sa_bo_free(struct amdgpu_device *adev, struct amdgpu_sa_bo **sa_bo, -+ struct amdgpu_fence *fence) -+{ -+ struct amdgpu_sa_manager *sa_manager; -+ -+ if (sa_bo == NULL || *sa_bo == NULL) { -+ return; -+ } -+ -+ sa_manager = (*sa_bo)->manager; -+ spin_lock(&sa_manager->wq.lock); -+ if (fence && !amdgpu_fence_signaled(fence)) { -+ (*sa_bo)->fence = amdgpu_fence_ref(fence); -+ list_add_tail(&(*sa_bo)->flist, -+ &sa_manager->flist[fence->ring->idx]); -+ } else { -+ amdgpu_sa_bo_remove_locked(*sa_bo); -+ } -+ wake_up_all_locked(&sa_manager->wq); -+ spin_unlock(&sa_manager->wq.lock); -+ *sa_bo = NULL; -+} -+ -+#if defined(CONFIG_DEBUG_FS) -+void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, -+ struct seq_file *m) -+{ -+ struct amdgpu_sa_bo *i; -+ -+ spin_lock(&sa_manager->wq.lock); -+ list_for_each_entry(i, &sa_manager->olist, olist) { -+ uint64_t soffset = i->soffset + sa_manager->gpu_addr; -+ uint64_t eoffset = i->eoffset + sa_manager->gpu_addr; -+ if (&i->olist == sa_manager->hole) { -+ seq_printf(m, ">"); -+ } else { -+ seq_printf(m, " "); -+ } -+ seq_printf(m, "[0x%010llx 0x%010llx] size %8lld", -+ soffset, eoffset, eoffset - soffset); -+ if (i->fence) { -+ seq_printf(m, " protected by 0x%016llx on ring %d", -+ i->fence->seq, i->fence->ring->idx); -+ } -+ seq_printf(m, "\n"); -+ } -+ spin_unlock(&sa_manager->wq.lock); -+} -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_semaphore.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_semaphore.c -new file mode 100644 -index 0000000..d6d41a4 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_semaphore.c -@@ -0,0 +1,102 @@ -+/* -+ * Copyright 2011 Christian König. -+ * 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Christian König <deathsimple@vodafone.de> -+ */ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include "amdgpu_trace.h" -+ -+int amdgpu_semaphore_create(struct amdgpu_device *adev, -+ struct amdgpu_semaphore **semaphore) -+{ -+ int r; -+ -+ *semaphore = kmalloc(sizeof(struct amdgpu_semaphore), GFP_KERNEL); -+ if (*semaphore == NULL) { -+ return -ENOMEM; -+ } -+ r = amdgpu_sa_bo_new(adev, &adev->ring_tmp_bo, -+ &(*semaphore)->sa_bo, 8, 8); -+ if (r) { -+ kfree(*semaphore); -+ *semaphore = NULL; -+ return r; -+ } -+ (*semaphore)->waiters = 0; -+ (*semaphore)->gpu_addr = amdgpu_sa_bo_gpu_addr((*semaphore)->sa_bo); -+ -+ *((uint64_t *)amdgpu_sa_bo_cpu_addr((*semaphore)->sa_bo)) = 0; -+ -+ return 0; -+} -+ -+bool amdgpu_semaphore_emit_signal(struct amdgpu_ring *ring, -+ struct amdgpu_semaphore *semaphore) -+{ -+ trace_amdgpu_semaphore_signale(ring->idx, semaphore); -+ -+ if (amdgpu_ring_emit_semaphore(ring, semaphore, false)) { -+ --semaphore->waiters; -+ -+ /* for debugging lockup only, used by sysfs debug files */ -+ ring->last_semaphore_signal_addr = semaphore->gpu_addr; -+ return true; -+ } -+ return false; -+} -+ -+bool amdgpu_semaphore_emit_wait(struct amdgpu_ring *ring, -+ struct amdgpu_semaphore *semaphore) -+{ -+ trace_amdgpu_semaphore_wait(ring->idx, semaphore); -+ -+ if (amdgpu_ring_emit_semaphore(ring, semaphore, true)) { -+ ++semaphore->waiters; -+ -+ /* for debugging lockup only, used by sysfs debug files */ -+ ring->last_semaphore_wait_addr = semaphore->gpu_addr; -+ return true; -+ } -+ return false; -+} -+ -+void amdgpu_semaphore_free(struct amdgpu_device *adev, -+ struct amdgpu_semaphore **semaphore, -+ struct amdgpu_fence *fence) -+{ -+ if (semaphore == NULL || *semaphore == NULL) { -+ return; -+ } -+ if ((*semaphore)->waiters > 0) { -+ dev_err(adev->dev, "semaphore %p has more waiters than signalers," -+ " hardware lockup imminent!\n", *semaphore); -+ } -+ amdgpu_sa_bo_free(adev, &(*semaphore)->sa_bo, fence); -+ kfree(*semaphore); -+ *semaphore = NULL; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c -new file mode 100644 -index 0000000..855d56a ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c -@@ -0,0 +1,231 @@ -+/* -+ * 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Christian König <christian.koenig@amd.com> -+ */ -+ -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include "amdgpu_trace.h" -+ -+/** -+ * amdgpu_sync_create - zero init sync object -+ * -+ * @sync: sync object to initialize -+ * -+ * Just clear the sync object for now. -+ */ -+void amdgpu_sync_create(struct amdgpu_sync *sync) -+{ -+ unsigned i; -+ -+ for (i = 0; i < AMDGPU_NUM_SYNCS; ++i) -+ sync->semaphores[i] = NULL; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) -+ sync->sync_to[i] = NULL; -+ -+ sync->last_vm_update = NULL; -+} -+ -+/** -+ * amdgpu_sync_fence - use the semaphore to sync to a fence -+ * -+ * @sync: sync object to add fence to -+ * @fence: fence to sync to -+ * -+ * Sync to the fence using the semaphore objects -+ */ -+void amdgpu_sync_fence(struct amdgpu_sync *sync, -+ struct amdgpu_fence *fence) -+{ -+ struct amdgpu_fence *other; -+ -+ if (!fence) -+ return; -+ -+ other = sync->sync_to[fence->ring->idx]; -+ sync->sync_to[fence->ring->idx] = amdgpu_fence_ref( -+ amdgpu_fence_later(fence, other)); -+ amdgpu_fence_unref(&other); -+ -+ if (fence->owner == AMDGPU_FENCE_OWNER_VM) { -+ other = sync->last_vm_update; -+ sync->last_vm_update = amdgpu_fence_ref( -+ amdgpu_fence_later(fence, other)); -+ amdgpu_fence_unref(&other); -+ } -+} -+ -+/** -+ * amdgpu_sync_resv - use the semaphores to sync to a reservation object -+ * -+ * @sync: sync object to add fences from reservation object to -+ * @resv: reservation object with embedded fence -+ * @shared: true if we should only sync to the exclusive fence -+ * -+ * Sync to the fence using the semaphore objects -+ */ -+int amdgpu_sync_resv(struct amdgpu_device *adev, -+ struct amdgpu_sync *sync, -+ struct reservation_object *resv, -+ void *owner) -+{ -+ struct reservation_object_list *flist; -+ struct fence *f; -+ struct amdgpu_fence *fence; -+ unsigned i; -+ int r = 0; -+ -+ /* always sync to the exclusive fence */ -+ f = reservation_object_get_excl(resv); -+ fence = f ? to_amdgpu_fence(f) : NULL; -+ if (fence && fence->ring->adev == adev) -+ amdgpu_sync_fence(sync, fence); -+ else if (f) -+ r = fence_wait(f, true); -+ -+ flist = reservation_object_get_list(resv); -+ if (!flist || r) -+ return r; -+ -+ for (i = 0; i < flist->shared_count; ++i) { -+ f = rcu_dereference_protected(flist->shared[i], -+ reservation_object_held(resv)); -+ fence = to_amdgpu_fence(f); -+ if (fence && fence->ring->adev == adev) { -+ if (fence->owner != owner || -+ fence->owner == AMDGPU_FENCE_OWNER_UNDEFINED) -+ amdgpu_sync_fence(sync, fence); -+ } else { -+ r = fence_wait(f, true); -+ if (r) -+ break; -+ } -+ } -+ return r; -+} -+ -+/** -+ * amdgpu_sync_rings - sync ring to all registered fences -+ * -+ * @sync: sync object to use -+ * @ring: ring that needs sync -+ * -+ * Ensure that all registered fences are signaled before letting -+ * the ring continue. The caller must hold the ring lock. -+ */ -+int amdgpu_sync_rings(struct amdgpu_sync *sync, -+ struct amdgpu_ring *ring) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ unsigned count = 0; -+ int i, r; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_fence *fence = sync->sync_to[i]; -+ struct amdgpu_semaphore *semaphore; -+ struct amdgpu_ring *other = adev->rings[i]; -+ -+ /* check if we really need to sync */ -+ if (!amdgpu_fence_need_sync(fence, ring)) -+ continue; -+ -+ /* prevent GPU deadlocks */ -+ if (!other->ready) { -+ dev_err(adev->dev, "Syncing to a disabled ring!"); -+ return -EINVAL; -+ } -+ -+ if (count >= AMDGPU_NUM_SYNCS) { -+ /* not enough room, wait manually */ -+ r = amdgpu_fence_wait(fence, false); -+ if (r) -+ return r; -+ continue; -+ } -+ r = amdgpu_semaphore_create(adev, &semaphore); -+ if (r) -+ return r; -+ -+ sync->semaphores[count++] = semaphore; -+ -+ /* allocate enough space for sync command */ -+ r = amdgpu_ring_alloc(other, 16); -+ if (r) -+ return r; -+ -+ /* emit the signal semaphore */ -+ if (!amdgpu_semaphore_emit_signal(other, semaphore)) { -+ /* signaling wasn't successful wait manually */ -+ amdgpu_ring_undo(other); -+ r = amdgpu_fence_wait(fence, false); -+ if (r) -+ return r; -+ continue; -+ } -+ -+ /* we assume caller has already allocated space on waiters ring */ -+ if (!amdgpu_semaphore_emit_wait(ring, semaphore)) { -+ /* waiting wasn't successful wait manually */ -+ amdgpu_ring_undo(other); -+ r = amdgpu_fence_wait(fence, false); -+ if (r) -+ return r; -+ continue; -+ } -+ -+ amdgpu_ring_commit(other); -+ amdgpu_fence_note_sync(fence, ring); -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_sync_free - free the sync object -+ * -+ * @adev: amdgpu_device pointer -+ * @sync: sync object to use -+ * @fence: fence to use for the free -+ * -+ * Free the sync object by freeing all semaphores in it. -+ */ -+void amdgpu_sync_free(struct amdgpu_device *adev, -+ struct amdgpu_sync *sync, -+ struct amdgpu_fence *fence) -+{ -+ unsigned i; -+ -+ for (i = 0; i < AMDGPU_NUM_SYNCS; ++i) -+ amdgpu_semaphore_free(adev, &sync->semaphores[i], fence); -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) -+ amdgpu_fence_unref(&sync->sync_to[i]); -+ -+ amdgpu_fence_unref(&sync->last_vm_update); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c -new file mode 100644 -index 0000000..df20299 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c -@@ -0,0 +1,552 @@ -+/* -+ * Copyright 2009 VMware, 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: Michel Dänzer -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_uvd.h" -+#include "amdgpu_vce.h" -+ -+/* Test BO GTT->VRAM and VRAM->GTT GPU copies across the whole GTT aperture */ -+static void amdgpu_do_test_moves(struct amdgpu_device *adev) -+{ -+ struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring; -+ struct amdgpu_bo *vram_obj = NULL; -+ struct amdgpu_bo **gtt_obj = NULL; -+ uint64_t gtt_addr, vram_addr; -+ unsigned n, size; -+ int i, r; -+ -+ size = 1024 * 1024; -+ -+ /* Number of tests = -+ * (Total GTT - IB pool - writeback page - ring buffers) / test size -+ */ -+ n = adev->mc.gtt_size - AMDGPU_IB_POOL_SIZE*64*1024; -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) -+ if (adev->rings[i]) -+ n -= adev->rings[i]->ring_size; -+ if (adev->wb.wb_obj) -+ n -= AMDGPU_GPU_PAGE_SIZE; -+ if (adev->irq.ih.ring_obj) -+ n -= adev->irq.ih.ring_size; -+ n /= size; -+ -+ gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL); -+ if (!gtt_obj) { -+ DRM_ERROR("Failed to allocate %d pointers\n", n); -+ r = 1; -+ goto out_cleanup; -+ } -+ -+ r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, 0, -+ NULL, &vram_obj); -+ if (r) { -+ DRM_ERROR("Failed to create VRAM object\n"); -+ goto out_cleanup; -+ } -+ r = amdgpu_bo_reserve(vram_obj, false); -+ if (unlikely(r != 0)) -+ goto out_unref; -+ r = amdgpu_bo_pin(vram_obj, AMDGPU_GEM_DOMAIN_VRAM, &vram_addr); -+ if (r) { -+ DRM_ERROR("Failed to pin VRAM object\n"); -+ goto out_unres; -+ } -+ for (i = 0; i < n; i++) { -+ void *gtt_map, *vram_map; -+ void **gtt_start, **gtt_end; -+ void **vram_start, **vram_end; -+ struct amdgpu_fence *fence = NULL; -+ -+ r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_GTT, 0, NULL, gtt_obj + i); -+ if (r) { -+ DRM_ERROR("Failed to create GTT object %d\n", i); -+ goto out_lclean; -+ } -+ -+ r = amdgpu_bo_reserve(gtt_obj[i], false); -+ if (unlikely(r != 0)) -+ goto out_lclean_unref; -+ r = amdgpu_bo_pin(gtt_obj[i], AMDGPU_GEM_DOMAIN_GTT, >t_addr); -+ if (r) { -+ DRM_ERROR("Failed to pin GTT object %d\n", i); -+ goto out_lclean_unres; -+ } -+ -+ r = amdgpu_bo_kmap(gtt_obj[i], >t_map); -+ if (r) { -+ DRM_ERROR("Failed to map GTT object %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ for (gtt_start = gtt_map, gtt_end = gtt_map + size; -+ gtt_start < gtt_end; -+ gtt_start++) -+ *gtt_start = gtt_start; -+ -+ amdgpu_bo_kunmap(gtt_obj[i]); -+ -+ r = amdgpu_copy_buffer(ring, gtt_addr, vram_addr, -+ size, NULL, &fence); -+ -+ if (r) { -+ DRM_ERROR("Failed GTT->VRAM copy %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ r = amdgpu_fence_wait(fence, false); -+ if (r) { -+ DRM_ERROR("Failed to wait for GTT->VRAM fence %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ amdgpu_fence_unref(&fence); -+ -+ r = amdgpu_bo_kmap(vram_obj, &vram_map); -+ if (r) { -+ DRM_ERROR("Failed to map VRAM object after copy %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ for (gtt_start = gtt_map, gtt_end = gtt_map + size, -+ vram_start = vram_map, vram_end = vram_map + size; -+ vram_start < vram_end; -+ gtt_start++, vram_start++) { -+ if (*vram_start != gtt_start) { -+ DRM_ERROR("Incorrect GTT->VRAM copy %d: Got 0x%p, " -+ "expected 0x%p (GTT/VRAM offset " -+ "0x%16llx/0x%16llx)\n", -+ i, *vram_start, gtt_start, -+ (unsigned long long) -+ (gtt_addr - adev->mc.gtt_start + -+ (void*)gtt_start - gtt_map), -+ (unsigned long long) -+ (vram_addr - adev->mc.vram_start + -+ (void*)gtt_start - gtt_map)); -+ amdgpu_bo_kunmap(vram_obj); -+ goto out_lclean_unpin; -+ } -+ *vram_start = vram_start; -+ } -+ -+ amdgpu_bo_kunmap(vram_obj); -+ -+ r = amdgpu_copy_buffer(ring, vram_addr, gtt_addr, -+ size, NULL, &fence); -+ -+ if (r) { -+ DRM_ERROR("Failed VRAM->GTT copy %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ r = amdgpu_fence_wait(fence, false); -+ if (r) { -+ DRM_ERROR("Failed to wait for VRAM->GTT fence %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ amdgpu_fence_unref(&fence); -+ -+ r = amdgpu_bo_kmap(gtt_obj[i], >t_map); -+ if (r) { -+ DRM_ERROR("Failed to map GTT object after copy %d\n", i); -+ goto out_lclean_unpin; -+ } -+ -+ for (gtt_start = gtt_map, gtt_end = gtt_map + size, -+ vram_start = vram_map, vram_end = vram_map + size; -+ gtt_start < gtt_end; -+ gtt_start++, vram_start++) { -+ if (*gtt_start != vram_start) { -+ DRM_ERROR("Incorrect VRAM->GTT copy %d: Got 0x%p, " -+ "expected 0x%p (VRAM/GTT offset " -+ "0x%16llx/0x%16llx)\n", -+ i, *gtt_start, vram_start, -+ (unsigned long long) -+ (vram_addr - adev->mc.vram_start + -+ (void*)vram_start - vram_map), -+ (unsigned long long) -+ (gtt_addr - adev->mc.gtt_start + -+ (void*)vram_start - vram_map)); -+ amdgpu_bo_kunmap(gtt_obj[i]); -+ goto out_lclean_unpin; -+ } -+ } -+ -+ amdgpu_bo_kunmap(gtt_obj[i]); -+ -+ DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n", -+ gtt_addr - adev->mc.gtt_start); -+ continue; -+ -+out_lclean_unpin: -+ amdgpu_bo_unpin(gtt_obj[i]); -+out_lclean_unres: -+ amdgpu_bo_unreserve(gtt_obj[i]); -+out_lclean_unref: -+ amdgpu_bo_unref(>t_obj[i]); -+out_lclean: -+ for (--i; i >= 0; --i) { -+ amdgpu_bo_unpin(gtt_obj[i]); -+ amdgpu_bo_unreserve(gtt_obj[i]); -+ amdgpu_bo_unref(>t_obj[i]); -+ } -+ if (fence) -+ amdgpu_fence_unref(&fence); -+ break; -+ } -+ -+ amdgpu_bo_unpin(vram_obj); -+out_unres: -+ amdgpu_bo_unreserve(vram_obj); -+out_unref: -+ amdgpu_bo_unref(&vram_obj); -+out_cleanup: -+ kfree(gtt_obj); -+ if (r) { -+ printk(KERN_WARNING "Error while testing BO move.\n"); -+ } -+} -+ -+void amdgpu_test_moves(struct amdgpu_device *adev) -+{ -+ if (adev->mman.buffer_funcs) -+ amdgpu_do_test_moves(adev); -+} -+ -+static int amdgpu_test_create_and_emit_fence(struct amdgpu_device *adev, -+ struct amdgpu_ring *ring, -+ struct amdgpu_fence **fence) -+{ -+ uint32_t handle = ring->idx ^ 0xdeafbeef; -+ int r; -+ -+ if (ring == &adev->uvd.ring) { -+ r = amdgpu_uvd_get_create_msg(ring, handle, NULL); -+ if (r) { -+ DRM_ERROR("Failed to get dummy create msg\n"); -+ return r; -+ } -+ -+ r = amdgpu_uvd_get_destroy_msg(ring, handle, fence); -+ if (r) { -+ DRM_ERROR("Failed to get dummy destroy msg\n"); -+ return r; -+ } -+ -+ } else if (ring == &adev->vce.ring[0] || -+ ring == &adev->vce.ring[1]) { -+ r = amdgpu_vce_get_create_msg(ring, handle, NULL); -+ if (r) { -+ DRM_ERROR("Failed to get dummy create msg\n"); -+ return r; -+ } -+ -+ r = amdgpu_vce_get_destroy_msg(ring, handle, fence); -+ if (r) { -+ DRM_ERROR("Failed to get dummy destroy msg\n"); -+ return r; -+ } -+ -+ } else { -+ r = amdgpu_ring_lock(ring, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring A %d\n", ring->idx); -+ return r; -+ } -+ amdgpu_fence_emit(ring, AMDGPU_FENCE_OWNER_UNDEFINED, fence); -+ amdgpu_ring_unlock_commit(ring); -+ } -+ return 0; -+} -+ -+void amdgpu_test_ring_sync(struct amdgpu_device *adev, -+ struct amdgpu_ring *ringA, -+ struct amdgpu_ring *ringB) -+{ -+ struct amdgpu_fence *fence1 = NULL, *fence2 = NULL; -+ struct amdgpu_semaphore *semaphore = NULL; -+ int r; -+ -+ r = amdgpu_semaphore_create(adev, &semaphore); -+ if (r) { -+ DRM_ERROR("Failed to create semaphore\n"); -+ goto out_cleanup; -+ } -+ -+ r = amdgpu_ring_lock(ringA, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring A %d\n", ringA->idx); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_wait(ringA, semaphore); -+ amdgpu_ring_unlock_commit(ringA); -+ -+ r = amdgpu_test_create_and_emit_fence(adev, ringA, &fence1); -+ if (r) -+ goto out_cleanup; -+ -+ r = amdgpu_ring_lock(ringA, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring A %d\n", ringA->idx); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_wait(ringA, semaphore); -+ amdgpu_ring_unlock_commit(ringA); -+ -+ r = amdgpu_test_create_and_emit_fence(adev, ringA, &fence2); -+ if (r) -+ goto out_cleanup; -+ -+ mdelay(1000); -+ -+ if (amdgpu_fence_signaled(fence1)) { -+ DRM_ERROR("Fence 1 signaled without waiting for semaphore.\n"); -+ goto out_cleanup; -+ } -+ -+ r = amdgpu_ring_lock(ringB, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring B %p\n", ringB); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_signal(ringB, semaphore); -+ amdgpu_ring_unlock_commit(ringB); -+ -+ r = amdgpu_fence_wait(fence1, false); -+ if (r) { -+ DRM_ERROR("Failed to wait for sync fence 1\n"); -+ goto out_cleanup; -+ } -+ -+ mdelay(1000); -+ -+ if (amdgpu_fence_signaled(fence2)) { -+ DRM_ERROR("Fence 2 signaled without waiting for semaphore.\n"); -+ goto out_cleanup; -+ } -+ -+ r = amdgpu_ring_lock(ringB, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring B %p\n", ringB); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_signal(ringB, semaphore); -+ amdgpu_ring_unlock_commit(ringB); -+ -+ r = amdgpu_fence_wait(fence2, false); -+ if (r) { -+ DRM_ERROR("Failed to wait for sync fence 1\n"); -+ goto out_cleanup; -+ } -+ -+out_cleanup: -+ amdgpu_semaphore_free(adev, &semaphore, NULL); -+ -+ if (fence1) -+ amdgpu_fence_unref(&fence1); -+ -+ if (fence2) -+ amdgpu_fence_unref(&fence2); -+ -+ if (r) -+ printk(KERN_WARNING "Error while testing ring sync (%d).\n", r); -+} -+ -+static void amdgpu_test_ring_sync2(struct amdgpu_device *adev, -+ struct amdgpu_ring *ringA, -+ struct amdgpu_ring *ringB, -+ struct amdgpu_ring *ringC) -+{ -+ struct amdgpu_fence *fenceA = NULL, *fenceB = NULL; -+ struct amdgpu_semaphore *semaphore = NULL; -+ bool sigA, sigB; -+ int i, r; -+ -+ r = amdgpu_semaphore_create(adev, &semaphore); -+ if (r) { -+ DRM_ERROR("Failed to create semaphore\n"); -+ goto out_cleanup; -+ } -+ -+ r = amdgpu_ring_lock(ringA, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring A %d\n", ringA->idx); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_wait(ringA, semaphore); -+ amdgpu_ring_unlock_commit(ringA); -+ -+ r = amdgpu_test_create_and_emit_fence(adev, ringA, &fenceA); -+ if (r) -+ goto out_cleanup; -+ -+ r = amdgpu_ring_lock(ringB, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring B %d\n", ringB->idx); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_wait(ringB, semaphore); -+ amdgpu_ring_unlock_commit(ringB); -+ r = amdgpu_test_create_and_emit_fence(adev, ringB, &fenceB); -+ if (r) -+ goto out_cleanup; -+ -+ mdelay(1000); -+ -+ if (amdgpu_fence_signaled(fenceA)) { -+ DRM_ERROR("Fence A signaled without waiting for semaphore.\n"); -+ goto out_cleanup; -+ } -+ if (amdgpu_fence_signaled(fenceB)) { -+ DRM_ERROR("Fence B signaled without waiting for semaphore.\n"); -+ goto out_cleanup; -+ } -+ -+ r = amdgpu_ring_lock(ringC, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring B %p\n", ringC); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_signal(ringC, semaphore); -+ amdgpu_ring_unlock_commit(ringC); -+ -+ for (i = 0; i < 30; ++i) { -+ mdelay(100); -+ sigA = amdgpu_fence_signaled(fenceA); -+ sigB = amdgpu_fence_signaled(fenceB); -+ if (sigA || sigB) -+ break; -+ } -+ -+ if (!sigA && !sigB) { -+ DRM_ERROR("Neither fence A nor B has been signaled\n"); -+ goto out_cleanup; -+ } else if (sigA && sigB) { -+ DRM_ERROR("Both fence A and B has been signaled\n"); -+ goto out_cleanup; -+ } -+ -+ DRM_INFO("Fence %c was first signaled\n", sigA ? 'A' : 'B'); -+ -+ r = amdgpu_ring_lock(ringC, 64); -+ if (r) { -+ DRM_ERROR("Failed to lock ring B %p\n", ringC); -+ goto out_cleanup; -+ } -+ amdgpu_semaphore_emit_signal(ringC, semaphore); -+ amdgpu_ring_unlock_commit(ringC); -+ -+ mdelay(1000); -+ -+ r = amdgpu_fence_wait(fenceA, false); -+ if (r) { -+ DRM_ERROR("Failed to wait for sync fence A\n"); -+ goto out_cleanup; -+ } -+ r = amdgpu_fence_wait(fenceB, false); -+ if (r) { -+ DRM_ERROR("Failed to wait for sync fence B\n"); -+ goto out_cleanup; -+ } -+ -+out_cleanup: -+ amdgpu_semaphore_free(adev, &semaphore, NULL); -+ -+ if (fenceA) -+ amdgpu_fence_unref(&fenceA); -+ -+ if (fenceB) -+ amdgpu_fence_unref(&fenceB); -+ -+ if (r) -+ printk(KERN_WARNING "Error while testing ring sync (%d).\n", r); -+} -+ -+static bool amdgpu_test_sync_possible(struct amdgpu_ring *ringA, -+ struct amdgpu_ring *ringB) -+{ -+ if (ringA == &ringA->adev->vce.ring[0] && -+ ringB == &ringB->adev->vce.ring[1]) -+ return false; -+ -+ return true; -+} -+ -+void amdgpu_test_syncing(struct amdgpu_device *adev) -+{ -+ int i, j, k; -+ -+ for (i = 1; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_ring *ringA = adev->rings[i]; -+ if (!ringA || !ringA->ready) -+ continue; -+ -+ for (j = 0; j < i; ++j) { -+ struct amdgpu_ring *ringB = adev->rings[j]; -+ if (!ringB || !ringB->ready) -+ continue; -+ -+ if (!amdgpu_test_sync_possible(ringA, ringB)) -+ continue; -+ -+ DRM_INFO("Testing syncing between rings %d and %d...\n", i, j); -+ amdgpu_test_ring_sync(adev, ringA, ringB); -+ -+ DRM_INFO("Testing syncing between rings %d and %d...\n", j, i); -+ amdgpu_test_ring_sync(adev, ringB, ringA); -+ -+ for (k = 0; k < j; ++k) { -+ struct amdgpu_ring *ringC = adev->rings[k]; -+ if (!ringC || !ringC->ready) -+ continue; -+ -+ if (!amdgpu_test_sync_possible(ringA, ringC)) -+ continue; -+ -+ if (!amdgpu_test_sync_possible(ringB, ringC)) -+ continue; -+ -+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", i, j, k); -+ amdgpu_test_ring_sync2(adev, ringA, ringB, ringC); -+ -+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", i, k, j); -+ amdgpu_test_ring_sync2(adev, ringA, ringC, ringB); -+ -+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", j, i, k); -+ amdgpu_test_ring_sync2(adev, ringB, ringA, ringC); -+ -+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", j, k, i); -+ amdgpu_test_ring_sync2(adev, ringB, ringC, ringA); -+ -+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", k, i, j); -+ amdgpu_test_ring_sync2(adev, ringC, ringA, ringB); -+ -+ DRM_INFO("Testing syncing between rings %d, %d and %d...\n", k, j, i); -+ amdgpu_test_ring_sync2(adev, ringC, ringB, ringA); -+ } -+ } -+ } -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h -new file mode 100644 -index 0000000..b57647e ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h -@@ -0,0 +1,209 @@ -+#if !defined(_AMDGPU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -+#define _AMDGPU_TRACE_H_ -+ -+#include <linux/stringify.h> -+#include <linux/types.h> -+#include <linux/tracepoint.h> -+ -+#include <drm/drmP.h> -+ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM amdgpu -+#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) -+#define TRACE_INCLUDE_FILE amdgpu_trace -+ -+TRACE_EVENT(amdgpu_bo_create, -+ TP_PROTO(struct amdgpu_bo *bo), -+ TP_ARGS(bo), -+ TP_STRUCT__entry( -+ __field(struct amdgpu_bo *, bo) -+ __field(u32, pages) -+ ), -+ -+ TP_fast_assign( -+ __entry->bo = bo; -+ __entry->pages = bo->tbo.num_pages; -+ ), -+ TP_printk("bo=%p, pages=%u", __entry->bo, __entry->pages) -+); -+ -+TRACE_EVENT(amdgpu_cs, -+ TP_PROTO(struct amdgpu_cs_parser *p, int i), -+ TP_ARGS(p, i), -+ TP_STRUCT__entry( -+ __field(u32, ring) -+ __field(u32, dw) -+ __field(u32, fences) -+ ), -+ -+ TP_fast_assign( -+ __entry->ring = p->ibs[i].ring->idx; -+ __entry->dw = p->ibs[i].length_dw; -+ __entry->fences = amdgpu_fence_count_emitted( -+ p->ibs[i].ring); -+ ), -+ TP_printk("ring=%u, dw=%u, fences=%u", -+ __entry->ring, __entry->dw, -+ __entry->fences) -+); -+ -+TRACE_EVENT(amdgpu_vm_grab_id, -+ TP_PROTO(unsigned vmid, int ring), -+ TP_ARGS(vmid, ring), -+ TP_STRUCT__entry( -+ __field(u32, vmid) -+ __field(u32, ring) -+ ), -+ -+ TP_fast_assign( -+ __entry->vmid = vmid; -+ __entry->ring = ring; -+ ), -+ TP_printk("vmid=%u, ring=%u", __entry->vmid, __entry->ring) -+); -+ -+TRACE_EVENT(amdgpu_vm_bo_update, -+ TP_PROTO(struct amdgpu_bo_va_mapping *mapping), -+ TP_ARGS(mapping), -+ TP_STRUCT__entry( -+ __field(u64, soffset) -+ __field(u64, eoffset) -+ __field(u32, flags) -+ ), -+ -+ TP_fast_assign( -+ __entry->soffset = mapping->it.start; -+ __entry->eoffset = mapping->it.last + 1; -+ __entry->flags = mapping->flags; -+ ), -+ TP_printk("soffs=%010llx, eoffs=%010llx, flags=%08x", -+ __entry->soffset, __entry->eoffset, __entry->flags) -+); -+ -+TRACE_EVENT(amdgpu_vm_set_page, -+ TP_PROTO(uint64_t pe, uint64_t addr, unsigned count, -+ uint32_t incr, uint32_t flags), -+ TP_ARGS(pe, addr, count, incr, flags), -+ TP_STRUCT__entry( -+ __field(u64, pe) -+ __field(u64, addr) -+ __field(u32, count) -+ __field(u32, incr) -+ __field(u32, flags) -+ ), -+ -+ TP_fast_assign( -+ __entry->pe = pe; -+ __entry->addr = addr; -+ __entry->count = count; -+ __entry->incr = incr; -+ __entry->flags = flags; -+ ), -+ TP_printk("pe=%010Lx, addr=%010Lx, incr=%u, flags=%08x, count=%u", -+ __entry->pe, __entry->addr, __entry->incr, -+ __entry->flags, __entry->count) -+); -+ -+TRACE_EVENT(amdgpu_vm_flush, -+ TP_PROTO(uint64_t pd_addr, unsigned ring, unsigned id), -+ TP_ARGS(pd_addr, ring, id), -+ TP_STRUCT__entry( -+ __field(u64, pd_addr) -+ __field(u32, ring) -+ __field(u32, id) -+ ), -+ -+ TP_fast_assign( -+ __entry->pd_addr = pd_addr; -+ __entry->ring = ring; -+ __entry->id = id; -+ ), -+ TP_printk("pd_addr=%010Lx, ring=%u, id=%u", -+ __entry->pd_addr, __entry->ring, __entry->id) -+); -+ -+DECLARE_EVENT_CLASS(amdgpu_fence_request, -+ -+ TP_PROTO(struct drm_device *dev, int ring, u32 seqno), -+ -+ TP_ARGS(dev, ring, seqno), -+ -+ TP_STRUCT__entry( -+ __field(u32, dev) -+ __field(int, ring) -+ __field(u32, seqno) -+ ), -+ -+ TP_fast_assign( -+ __entry->dev = dev->primary->index; -+ __entry->ring = ring; -+ __entry->seqno = seqno; -+ ), -+ -+ TP_printk("dev=%u, ring=%d, seqno=%u", -+ __entry->dev, __entry->ring, __entry->seqno) -+); -+ -+DEFINE_EVENT(amdgpu_fence_request, amdgpu_fence_emit, -+ -+ TP_PROTO(struct drm_device *dev, int ring, u32 seqno), -+ -+ TP_ARGS(dev, ring, seqno) -+); -+ -+DEFINE_EVENT(amdgpu_fence_request, amdgpu_fence_wait_begin, -+ -+ TP_PROTO(struct drm_device *dev, int ring, u32 seqno), -+ -+ TP_ARGS(dev, ring, seqno) -+); -+ -+DEFINE_EVENT(amdgpu_fence_request, amdgpu_fence_wait_end, -+ -+ TP_PROTO(struct drm_device *dev, int ring, u32 seqno), -+ -+ TP_ARGS(dev, ring, seqno) -+); -+ -+DECLARE_EVENT_CLASS(amdgpu_semaphore_request, -+ -+ TP_PROTO(int ring, struct amdgpu_semaphore *sem), -+ -+ TP_ARGS(ring, sem), -+ -+ TP_STRUCT__entry( -+ __field(int, ring) -+ __field(signed, waiters) -+ __field(uint64_t, gpu_addr) -+ ), -+ -+ TP_fast_assign( -+ __entry->ring = ring; -+ __entry->waiters = sem->waiters; -+ __entry->gpu_addr = sem->gpu_addr; -+ ), -+ -+ TP_printk("ring=%u, waiters=%d, addr=%010Lx", __entry->ring, -+ __entry->waiters, __entry->gpu_addr) -+); -+ -+DEFINE_EVENT(amdgpu_semaphore_request, amdgpu_semaphore_signale, -+ -+ TP_PROTO(int ring, struct amdgpu_semaphore *sem), -+ -+ TP_ARGS(ring, sem) -+); -+ -+DEFINE_EVENT(amdgpu_semaphore_request, amdgpu_semaphore_wait, -+ -+ TP_PROTO(int ring, struct amdgpu_semaphore *sem), -+ -+ TP_ARGS(ring, sem) -+); -+ -+#endif -+ -+/* This part must be outside protection */ -+#undef TRACE_INCLUDE_PATH -+#define TRACE_INCLUDE_PATH . -+#include <trace/define_trace.h> -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace_points.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace_points.c -new file mode 100644 -index 0000000..385b7e1 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace_points.c -@@ -0,0 +1,9 @@ -+/* Copyright Red Hat Inc 2010. -+ * Author : Dave Airlie <airlied@redhat.com> -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+#define CREATE_TRACE_POINTS -+#include "amdgpu_trace.h" -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c -new file mode 100644 -index 0000000..120e6e7 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c -@@ -0,0 +1,1249 @@ -+/* -+ * Copyright 2009 Jerome Glisse. -+ * All Rights Reserved. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the -+ * "Software"), to deal in the Software without restriction, including -+ * without limitation the rights to use, copy, modify, merge, publish, -+ * distribute, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Jerome Glisse <glisse@freedesktop.org> -+ * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> -+ * Dave Airlie -+ */ -+#include <ttm/ttm_bo_api.h> -+#include <ttm/ttm_bo_driver.h> -+#include <ttm/ttm_placement.h> -+#include <ttm/ttm_module.h> -+#include <ttm/ttm_page_alloc.h> -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include <linux/seq_file.h> -+#include <linux/slab.h> -+#include <linux/swiotlb.h> -+#include <linux/swap.h> -+#include <linux/pagemap.h> -+#include <linux/debugfs.h> -+#include "amdgpu.h" -+#include "bif/bif_4_1_d.h" -+ -+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) -+ -+static int amdgpu_ttm_debugfs_init(struct amdgpu_device *adev); -+static void amdgpu_ttm_debugfs_fini(struct amdgpu_device *adev); -+ -+static struct amdgpu_device *amdgpu_get_adev(struct ttm_bo_device *bdev) -+{ -+ struct amdgpu_mman *mman; -+ struct amdgpu_device *adev; -+ -+ mman = container_of(bdev, struct amdgpu_mman, bdev); -+ adev = container_of(mman, struct amdgpu_device, mman); -+ return adev; -+} -+ -+ -+/* -+ * Global memory. -+ */ -+static int amdgpu_ttm_mem_global_init(struct drm_global_reference *ref) -+{ -+ return ttm_mem_global_init(ref->object); -+} -+ -+static void amdgpu_ttm_mem_global_release(struct drm_global_reference *ref) -+{ -+ ttm_mem_global_release(ref->object); -+} -+ -+static int amdgpu_ttm_global_init(struct amdgpu_device *adev) -+{ -+ struct drm_global_reference *global_ref; -+ int r; -+ -+ adev->mman.mem_global_referenced = false; -+ global_ref = &adev->mman.mem_global_ref; -+ global_ref->global_type = DRM_GLOBAL_TTM_MEM; -+ global_ref->size = sizeof(struct ttm_mem_global); -+ global_ref->init = &amdgpu_ttm_mem_global_init; -+ global_ref->release = &amdgpu_ttm_mem_global_release; -+ r = drm_global_item_ref(global_ref); -+ if (r != 0) { -+ DRM_ERROR("Failed setting up TTM memory accounting " -+ "subsystem.\n"); -+ return r; -+ } -+ -+ adev->mman.bo_global_ref.mem_glob = -+ adev->mman.mem_global_ref.object; -+ global_ref = &adev->mman.bo_global_ref.ref; -+ global_ref->global_type = DRM_GLOBAL_TTM_BO; -+ global_ref->size = sizeof(struct ttm_bo_global); -+ global_ref->init = &ttm_bo_global_init; -+ global_ref->release = &ttm_bo_global_release; -+ r = drm_global_item_ref(global_ref); -+ if (r != 0) { -+ DRM_ERROR("Failed setting up TTM BO subsystem.\n"); -+ drm_global_item_unref(&adev->mman.mem_global_ref); -+ return r; -+ } -+ -+ adev->mman.mem_global_referenced = true; -+ return 0; -+} -+ -+static void amdgpu_ttm_global_fini(struct amdgpu_device *adev) -+{ -+ if (adev->mman.mem_global_referenced) { -+ drm_global_item_unref(&adev->mman.bo_global_ref.ref); -+ drm_global_item_unref(&adev->mman.mem_global_ref); -+ adev->mman.mem_global_referenced = false; -+ } -+} -+ -+static int amdgpu_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) -+{ -+ return 0; -+} -+ -+static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, -+ struct ttm_mem_type_manager *man) -+{ -+ struct amdgpu_device *adev; -+ -+ adev = amdgpu_get_adev(bdev); -+ -+ switch (type) { -+ case TTM_PL_SYSTEM: -+ /* System memory */ -+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; -+ man->available_caching = TTM_PL_MASK_CACHING; -+ man->default_caching = TTM_PL_FLAG_CACHED; -+ break; -+ case TTM_PL_TT: -+ man->func = &ttm_bo_manager_func; -+ man->gpu_offset = adev->mc.gtt_start; -+ man->available_caching = TTM_PL_MASK_CACHING; -+ man->default_caching = TTM_PL_FLAG_CACHED; -+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA; -+ break; -+ case TTM_PL_VRAM: -+ /* "On-card" video ram */ -+ man->func = &ttm_bo_manager_func; -+ man->gpu_offset = adev->mc.vram_start; -+ man->flags = TTM_MEMTYPE_FLAG_FIXED | -+ TTM_MEMTYPE_FLAG_MAPPABLE; -+ man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; -+ man->default_caching = TTM_PL_FLAG_WC; -+ break; -+ case AMDGPU_PL_GDS: -+ case AMDGPU_PL_GWS: -+ case AMDGPU_PL_OA: -+ /* On-chip GDS memory*/ -+ man->func = &ttm_bo_manager_func; -+ man->gpu_offset = 0; -+ man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_CMA; -+ man->available_caching = TTM_PL_FLAG_UNCACHED; -+ man->default_caching = TTM_PL_FLAG_UNCACHED; -+ break; -+ default: -+ DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void amdgpu_evict_flags(struct ttm_buffer_object *bo, -+ struct ttm_placement *placement) -+{ -+ struct amdgpu_bo *rbo; -+ static struct ttm_place placements = { -+ .fpfn = 0, -+ .lpfn = 0, -+ .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM -+ }; -+ -+ if (!amdgpu_ttm_bo_is_amdgpu_bo(bo)) { -+ placement->placement = &placements; -+ placement->busy_placement = &placements; -+ placement->num_placement = 1; -+ placement->num_busy_placement = 1; -+ return; -+ } -+ rbo = container_of(bo, struct amdgpu_bo, tbo); -+ switch (bo->mem.mem_type) { -+ case TTM_PL_VRAM: -+ if (rbo->adev->mman.buffer_funcs_ring->ready == false) -+ amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_CPU); -+ else -+ amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_GTT); -+ break; -+ case TTM_PL_TT: -+ default: -+ amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_CPU); -+ } -+ *placement = rbo->placement; -+} -+ -+static int amdgpu_verify_access(struct ttm_buffer_object *bo, struct file *filp) -+{ -+ struct amdgpu_bo *rbo = container_of(bo, struct amdgpu_bo, tbo); -+ -+ return drm_vma_node_verify_access(&rbo->gem_base.vma_node, filp); -+} -+ -+static void amdgpu_move_null(struct ttm_buffer_object *bo, -+ struct ttm_mem_reg *new_mem) -+{ -+ struct ttm_mem_reg *old_mem = &bo->mem; -+ -+ BUG_ON(old_mem->mm_node != NULL); -+ *old_mem = *new_mem; -+ new_mem->mm_node = NULL; -+} -+ -+static int amdgpu_move_blit(struct ttm_buffer_object *bo, -+ bool evict, bool no_wait_gpu, -+ struct ttm_mem_reg *new_mem, -+ struct ttm_mem_reg *old_mem) -+{ -+ struct amdgpu_device *adev; -+ struct amdgpu_ring *ring; -+ uint64_t old_start, new_start; -+ struct amdgpu_fence *fence; -+ int r; -+ -+ adev = amdgpu_get_adev(bo->bdev); -+ ring = adev->mman.buffer_funcs_ring; -+ old_start = old_mem->start << PAGE_SHIFT; -+ new_start = new_mem->start << PAGE_SHIFT; -+ -+ switch (old_mem->mem_type) { -+ case TTM_PL_VRAM: -+ old_start += adev->mc.vram_start; -+ break; -+ case TTM_PL_TT: -+ old_start += adev->mc.gtt_start; -+ break; -+ default: -+ DRM_ERROR("Unknown placement %d\n", old_mem->mem_type); -+ return -EINVAL; -+ } -+ switch (new_mem->mem_type) { -+ case TTM_PL_VRAM: -+ new_start += adev->mc.vram_start; -+ break; -+ case TTM_PL_TT: -+ new_start += adev->mc.gtt_start; -+ break; -+ default: -+ DRM_ERROR("Unknown placement %d\n", old_mem->mem_type); -+ return -EINVAL; -+ } -+ if (!ring->ready) { -+ DRM_ERROR("Trying to move memory with ring turned off.\n"); -+ return -EINVAL; -+ } -+ -+ BUILD_BUG_ON((PAGE_SIZE % AMDGPU_GPU_PAGE_SIZE) != 0); -+ -+ r = amdgpu_copy_buffer(ring, old_start, new_start, -+ new_mem->num_pages * PAGE_SIZE, /* bytes */ -+ bo->resv, &fence); -+ /* FIXME: handle copy error */ -+ r = ttm_bo_move_accel_cleanup(bo, &fence->base, -+ evict, no_wait_gpu, new_mem); -+ amdgpu_fence_unref(&fence); -+ return r; -+} -+ -+static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, -+ bool evict, bool interruptible, -+ bool no_wait_gpu, -+ struct ttm_mem_reg *new_mem) -+{ -+ struct amdgpu_device *adev; -+ struct ttm_mem_reg *old_mem = &bo->mem; -+ struct ttm_mem_reg tmp_mem; -+ struct ttm_place placements; -+ struct ttm_placement placement; -+ int r; -+ -+ adev = amdgpu_get_adev(bo->bdev); -+ tmp_mem = *new_mem; -+ tmp_mem.mm_node = NULL; -+ placement.num_placement = 1; -+ placement.placement = &placements; -+ placement.num_busy_placement = 1; -+ placement.busy_placement = &placements; -+ placements.fpfn = 0; -+ placements.lpfn = 0; -+ placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; -+ r = ttm_bo_mem_space(bo, &placement, &tmp_mem, -+ interruptible, no_wait_gpu); -+ if (unlikely(r)) { -+ return r; -+ } -+ -+ r = ttm_tt_set_placement_caching(bo->ttm, tmp_mem.placement); -+ if (unlikely(r)) { -+ goto out_cleanup; -+ } -+ -+ r = ttm_tt_bind(bo->ttm, &tmp_mem); -+ if (unlikely(r)) { -+ goto out_cleanup; -+ } -+ r = amdgpu_move_blit(bo, true, no_wait_gpu, &tmp_mem, old_mem); -+ if (unlikely(r)) { -+ goto out_cleanup; -+ } -+ r = ttm_bo_move_ttm(bo, true, no_wait_gpu, new_mem); -+out_cleanup: -+ ttm_bo_mem_put(bo, &tmp_mem); -+ return r; -+} -+ -+static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, -+ bool evict, bool interruptible, -+ bool no_wait_gpu, -+ struct ttm_mem_reg *new_mem) -+{ -+ struct amdgpu_device *adev; -+ struct ttm_mem_reg *old_mem = &bo->mem; -+ struct ttm_mem_reg tmp_mem; -+ struct ttm_placement placement; -+ struct ttm_place placements; -+ int r; -+ -+ adev = amdgpu_get_adev(bo->bdev); -+ tmp_mem = *new_mem; -+ tmp_mem.mm_node = NULL; -+ placement.num_placement = 1; -+ placement.placement = &placements; -+ placement.num_busy_placement = 1; -+ placement.busy_placement = &placements; -+ placements.fpfn = 0; -+ placements.lpfn = 0; -+ placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; -+ r = ttm_bo_mem_space(bo, &placement, &tmp_mem, -+ interruptible, no_wait_gpu); -+ if (unlikely(r)) { -+ return r; -+ } -+ r = ttm_bo_move_ttm(bo, true, no_wait_gpu, &tmp_mem); -+ if (unlikely(r)) { -+ goto out_cleanup; -+ } -+ r = amdgpu_move_blit(bo, true, no_wait_gpu, new_mem, old_mem); -+ if (unlikely(r)) { -+ goto out_cleanup; -+ } -+out_cleanup: -+ ttm_bo_mem_put(bo, &tmp_mem); -+ return r; -+} -+ -+static int amdgpu_bo_move(struct ttm_buffer_object *bo, -+ bool evict, bool interruptible, -+ bool no_wait_gpu, -+ struct ttm_mem_reg *new_mem) -+{ -+ struct amdgpu_device *adev; -+ struct ttm_mem_reg *old_mem = &bo->mem; -+ int r; -+ -+ adev = amdgpu_get_adev(bo->bdev); -+ if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { -+ amdgpu_move_null(bo, new_mem); -+ return 0; -+ } -+ if ((old_mem->mem_type == TTM_PL_TT && -+ new_mem->mem_type == TTM_PL_SYSTEM) || -+ (old_mem->mem_type == TTM_PL_SYSTEM && -+ new_mem->mem_type == TTM_PL_TT)) { -+ /* bind is enough */ -+ amdgpu_move_null(bo, new_mem); -+ return 0; -+ } -+ if (adev->mman.buffer_funcs == NULL || -+ adev->mman.buffer_funcs_ring == NULL || -+ !adev->mman.buffer_funcs_ring->ready) { -+ /* use memcpy */ -+ goto memcpy; -+ } -+ -+ if (old_mem->mem_type == TTM_PL_VRAM && -+ new_mem->mem_type == TTM_PL_SYSTEM) { -+ r = amdgpu_move_vram_ram(bo, evict, interruptible, -+ no_wait_gpu, new_mem); -+ } else if (old_mem->mem_type == TTM_PL_SYSTEM && -+ new_mem->mem_type == TTM_PL_VRAM) { -+ r = amdgpu_move_ram_vram(bo, evict, interruptible, -+ no_wait_gpu, new_mem); -+ } else { -+ r = amdgpu_move_blit(bo, evict, no_wait_gpu, new_mem, old_mem); -+ } -+ -+ if (r) { -+memcpy: -+ r = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); -+ if (r) { -+ return r; -+ } -+ } -+ -+ /* update statistics */ -+ atomic64_add((u64)bo->num_pages << PAGE_SHIFT, &adev->num_bytes_moved); -+ return 0; -+} -+ -+static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -+{ -+ struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; -+ struct amdgpu_device *adev = amdgpu_get_adev(bdev); -+ -+ mem->bus.addr = NULL; -+ mem->bus.offset = 0; -+ mem->bus.size = mem->num_pages << PAGE_SHIFT; -+ mem->bus.base = 0; -+ mem->bus.is_iomem = false; -+ if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) -+ return -EINVAL; -+ switch (mem->mem_type) { -+ case TTM_PL_SYSTEM: -+ /* system memory */ -+ return 0; -+ case TTM_PL_TT: -+ break; -+ case TTM_PL_VRAM: -+ mem->bus.offset = mem->start << PAGE_SHIFT; -+ /* check if it's visible */ -+ if ((mem->bus.offset + mem->bus.size) > adev->mc.visible_vram_size) -+ return -EINVAL; -+ mem->bus.base = adev->mc.aper_base; -+ mem->bus.is_iomem = true; -+#ifdef __alpha__ -+ /* -+ * Alpha: use bus.addr to hold the ioremap() return, -+ * so we can modify bus.base below. -+ */ -+ if (mem->placement & TTM_PL_FLAG_WC) -+ mem->bus.addr = -+ ioremap_wc(mem->bus.base + mem->bus.offset, -+ mem->bus.size); -+ else -+ mem->bus.addr = -+ ioremap_nocache(mem->bus.base + mem->bus.offset, -+ mem->bus.size); -+ -+ /* -+ * Alpha: Use just the bus offset plus -+ * the hose/domain memory base for bus.base. -+ * It then can be used to build PTEs for VRAM -+ * access, as done in ttm_bo_vm_fault(). -+ */ -+ mem->bus.base = (mem->bus.base & 0x0ffffffffUL) + -+ adev->ddev->hose->dense_mem_base; -+#endif -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void amdgpu_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -+{ -+} -+ -+/* -+ * TTM backend functions. -+ */ -+struct amdgpu_ttm_tt { -+ struct ttm_dma_tt ttm; -+ struct amdgpu_device *adev; -+ u64 offset; -+ uint64_t userptr; -+ struct mm_struct *usermm; -+ uint32_t userflags; -+}; -+ -+/* prepare the sg table with the user pages */ -+static int amdgpu_ttm_tt_pin_userptr(struct ttm_tt *ttm) -+{ -+ struct amdgpu_device *adev = amdgpu_get_adev(ttm->bdev); -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ unsigned pinned = 0, nents; -+ int r; -+ -+ int write = !(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY); -+ enum dma_data_direction direction = write ? -+ DMA_BIDIRECTIONAL : DMA_TO_DEVICE; -+ -+ if (current->mm != gtt->usermm) -+ return -EPERM; -+ -+ if (gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) { -+ /* check that we only pin down anonymous memory -+ to prevent problems with writeback */ -+ unsigned long end = gtt->userptr + ttm->num_pages * PAGE_SIZE; -+ struct vm_area_struct *vma; -+ -+ vma = find_vma(gtt->usermm, gtt->userptr); -+ if (!vma || vma->vm_file || vma->vm_end < end) -+ return -EPERM; -+ } -+ -+ do { -+ unsigned num_pages = ttm->num_pages - pinned; -+ uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE; -+ struct page **pages = ttm->pages + pinned; -+ -+ r = get_user_pages(current, current->mm, userptr, num_pages, -+ write, 0, pages, NULL); -+ if (r < 0) -+ goto release_pages; -+ -+ pinned += r; -+ -+ } while (pinned < ttm->num_pages); -+ -+ r = sg_alloc_table_from_pages(ttm->sg, ttm->pages, ttm->num_pages, 0, -+ ttm->num_pages << PAGE_SHIFT, -+ GFP_KERNEL); -+ if (r) -+ goto release_sg; -+ -+ r = -ENOMEM; -+ nents = dma_map_sg(adev->dev, ttm->sg->sgl, ttm->sg->nents, direction); -+ if (nents != ttm->sg->nents) -+ goto release_sg; -+ -+ drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, -+ gtt->ttm.dma_address, ttm->num_pages); -+ -+ return 0; -+ -+release_sg: -+ kfree(ttm->sg); -+ -+release_pages: -+ release_pages(ttm->pages, pinned, 0); -+ return r; -+} -+ -+static void amdgpu_ttm_tt_unpin_userptr(struct ttm_tt *ttm) -+{ -+ struct amdgpu_device *adev = amdgpu_get_adev(ttm->bdev); -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ struct scatterlist *sg; -+ int i; -+ -+ int write = !(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY); -+ enum dma_data_direction direction = write ? -+ DMA_BIDIRECTIONAL : DMA_TO_DEVICE; -+ -+ /* double check that we don't free the table twice */ -+ if (!ttm->sg->sgl) -+ return; -+ -+ /* free the sg table and pages again */ -+ dma_unmap_sg(adev->dev, ttm->sg->sgl, ttm->sg->nents, direction); -+ -+ for_each_sg(ttm->sg->sgl, sg, ttm->sg->nents, i) { -+ struct page *page = sg_page(sg); -+ -+ if (!(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY)) -+ set_page_dirty(page); -+ -+ mark_page_accessed(page); -+ page_cache_release(page); -+ } -+ -+ sg_free_table(ttm->sg); -+} -+ -+static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm, -+ struct ttm_mem_reg *bo_mem) -+{ -+ struct amdgpu_ttm_tt *gtt = (void*)ttm; -+ uint32_t flags = amdgpu_ttm_tt_pte_flags(gtt->adev, ttm, bo_mem); -+ int r; -+ -+ if (gtt->userptr) -+ amdgpu_ttm_tt_pin_userptr(ttm); -+ -+ gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); -+ if (!ttm->num_pages) { -+ WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", -+ ttm->num_pages, bo_mem, ttm); -+ } -+ -+ if (bo_mem->mem_type == AMDGPU_PL_GDS || -+ bo_mem->mem_type == AMDGPU_PL_GWS || -+ bo_mem->mem_type == AMDGPU_PL_OA) -+ return -EINVAL; -+ -+ r = amdgpu_gart_bind(gtt->adev, gtt->offset, ttm->num_pages, -+ ttm->pages, gtt->ttm.dma_address, flags); -+ -+ if (r) { -+ DRM_ERROR("failed to bind %lu pages at 0x%08X\n", -+ ttm->num_pages, (unsigned)gtt->offset); -+ return r; -+ } -+ return 0; -+} -+ -+static int amdgpu_ttm_backend_unbind(struct ttm_tt *ttm) -+{ -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ -+ /* unbind shouldn't be done for GDS/GWS/OA in ttm_bo_clean_mm */ -+ if (gtt->adev->gart.ready) -+ amdgpu_gart_unbind(gtt->adev, gtt->offset, ttm->num_pages); -+ -+ if (gtt->userptr) -+ amdgpu_ttm_tt_unpin_userptr(ttm); -+ -+ return 0; -+} -+ -+static void amdgpu_ttm_backend_destroy(struct ttm_tt *ttm) -+{ -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ -+ ttm_dma_tt_fini(>t->ttm); -+ kfree(gtt); -+} -+ -+static struct ttm_backend_func amdgpu_backend_func = { -+ .bind = &amdgpu_ttm_backend_bind, -+ .unbind = &amdgpu_ttm_backend_unbind, -+ .destroy = &amdgpu_ttm_backend_destroy, -+}; -+ -+static struct ttm_tt *amdgpu_ttm_tt_create(struct ttm_bo_device *bdev, -+ unsigned long size, uint32_t page_flags, -+ struct page *dummy_read_page) -+{ -+ struct amdgpu_device *adev; -+ struct amdgpu_ttm_tt *gtt; -+ -+ adev = amdgpu_get_adev(bdev); -+ -+ gtt = kzalloc(sizeof(struct amdgpu_ttm_tt), GFP_KERNEL); -+ if (gtt == NULL) { -+ return NULL; -+ } -+ gtt->ttm.ttm.func = &amdgpu_backend_func; -+ gtt->adev = adev; -+ if (ttm_dma_tt_init(>t->ttm, bdev, size, page_flags, dummy_read_page)) { -+ kfree(gtt); -+ return NULL; -+ } -+ return >t->ttm.ttm; -+} -+ -+static int amdgpu_ttm_tt_populate(struct ttm_tt *ttm) -+{ -+ struct amdgpu_device *adev; -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ unsigned i; -+ int r; -+ bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); -+ -+ if (ttm->state != tt_unpopulated) -+ return 0; -+ -+ if (gtt && gtt->userptr) { -+ ttm->sg = kcalloc(1, sizeof(struct sg_table), GFP_KERNEL); -+ if (!ttm->sg) -+ return -ENOMEM; -+ -+ ttm->page_flags |= TTM_PAGE_FLAG_SG; -+ ttm->state = tt_unbound; -+ return 0; -+ } -+ -+ if (slave && ttm->sg) { -+ drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, -+ gtt->ttm.dma_address, ttm->num_pages); -+ ttm->state = tt_unbound; -+ return 0; -+ } -+ -+ adev = amdgpu_get_adev(ttm->bdev); -+ -+#ifdef CONFIG_SWIOTLB -+ if (swiotlb_nr_tbl()) { -+ return ttm_dma_populate(>t->ttm, adev->dev); -+ } -+#endif -+ -+ r = ttm_pool_populate(ttm); -+ if (r) { -+ return r; -+ } -+ -+ for (i = 0; i < ttm->num_pages; i++) { -+ gtt->ttm.dma_address[i] = pci_map_page(adev->pdev, ttm->pages[i], -+ 0, PAGE_SIZE, -+ PCI_DMA_BIDIRECTIONAL); -+ if (pci_dma_mapping_error(adev->pdev, gtt->ttm.dma_address[i])) { -+ while (--i) { -+ pci_unmap_page(adev->pdev, gtt->ttm.dma_address[i], -+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -+ gtt->ttm.dma_address[i] = 0; -+ } -+ ttm_pool_unpopulate(ttm); -+ return -EFAULT; -+ } -+ } -+ return 0; -+} -+ -+static void amdgpu_ttm_tt_unpopulate(struct ttm_tt *ttm) -+{ -+ struct amdgpu_device *adev; -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ unsigned i; -+ bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); -+ -+ if (gtt && gtt->userptr) { -+ kfree(ttm->sg); -+ ttm->page_flags &= ~TTM_PAGE_FLAG_SG; -+ return; -+ } -+ -+ if (slave) -+ return; -+ -+ adev = amdgpu_get_adev(ttm->bdev); -+ -+#ifdef CONFIG_SWIOTLB -+ if (swiotlb_nr_tbl()) { -+ ttm_dma_unpopulate(>t->ttm, adev->dev); -+ return; -+ } -+#endif -+ -+ for (i = 0; i < ttm->num_pages; i++) { -+ if (gtt->ttm.dma_address[i]) { -+ pci_unmap_page(adev->pdev, gtt->ttm.dma_address[i], -+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -+ } -+ } -+ -+ ttm_pool_unpopulate(ttm); -+} -+ -+int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr, -+ uint32_t flags) -+{ -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ -+ if (gtt == NULL) -+ return -EINVAL; -+ -+ gtt->userptr = addr; -+ gtt->usermm = current->mm; -+ gtt->userflags = flags; -+ return 0; -+} -+ -+bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm) -+{ -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ -+ if (gtt == NULL) -+ return false; -+ -+ return !!gtt->userptr; -+} -+ -+bool amdgpu_ttm_tt_is_readonly(struct ttm_tt *ttm) -+{ -+ struct amdgpu_ttm_tt *gtt = (void *)ttm; -+ -+ if (gtt == NULL) -+ return false; -+ -+ return !!(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY); -+} -+ -+uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm, -+ struct ttm_mem_reg *mem) -+{ -+ uint32_t flags = 0; -+ -+ if (mem && mem->mem_type != TTM_PL_SYSTEM) -+ flags |= AMDGPU_PTE_VALID; -+ -+ if (mem && mem->mem_type == TTM_PL_TT) -+ flags |= AMDGPU_PTE_SYSTEM; -+ -+ if (!ttm || ttm->caching_state == tt_cached) -+ flags |= AMDGPU_PTE_SNOOPED; -+ -+ if (adev->asic_type >= CHIP_TOPAZ) -+ flags |= AMDGPU_PTE_EXECUTABLE; -+ -+ flags |= AMDGPU_PTE_READABLE; -+ -+ if (!amdgpu_ttm_tt_is_readonly(ttm)) -+ flags |= AMDGPU_PTE_WRITEABLE; -+ -+ return flags; -+} -+ -+static struct ttm_bo_driver amdgpu_bo_driver = { -+ .ttm_tt_create = &amdgpu_ttm_tt_create, -+ .ttm_tt_populate = &amdgpu_ttm_tt_populate, -+ .ttm_tt_unpopulate = &amdgpu_ttm_tt_unpopulate, -+ .invalidate_caches = &amdgpu_invalidate_caches, -+ .init_mem_type = &amdgpu_init_mem_type, -+ .evict_flags = &amdgpu_evict_flags, -+ .move = &amdgpu_bo_move, -+ .verify_access = &amdgpu_verify_access, -+ .move_notify = &amdgpu_bo_move_notify, -+ .fault_reserve_notify = &amdgpu_bo_fault_reserve_notify, -+ .io_mem_reserve = &amdgpu_ttm_io_mem_reserve, -+ .io_mem_free = &amdgpu_ttm_io_mem_free, -+}; -+ -+int amdgpu_ttm_init(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ r = amdgpu_ttm_global_init(adev); -+ if (r) { -+ return r; -+ } -+ /* No others user of address space so set it to 0 */ -+ r = ttm_bo_device_init(&adev->mman.bdev, -+ adev->mman.bo_global_ref.ref.object, -+ &amdgpu_bo_driver, -+ adev->ddev->anon_inode->i_mapping, -+ DRM_FILE_PAGE_OFFSET, -+ adev->need_dma32); -+ if (r) { -+ DRM_ERROR("failed initializing buffer object driver(%d).\n", r); -+ return r; -+ } -+ adev->mman.initialized = true; -+ r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_VRAM, -+ adev->mc.real_vram_size >> PAGE_SHIFT); -+ if (r) { -+ DRM_ERROR("Failed initializing VRAM heap.\n"); -+ return r; -+ } -+ /* Change the size here instead of the init above so only lpfn is affected */ -+ amdgpu_ttm_set_active_vram_size(adev, adev->mc.visible_vram_size); -+ -+ r = amdgpu_bo_create(adev, 256 * 1024, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, -+ NULL, &adev->stollen_vga_memory); -+ if (r) { -+ return r; -+ } -+ r = amdgpu_bo_reserve(adev->stollen_vga_memory, false); -+ if (r) -+ return r; -+ r = amdgpu_bo_pin(adev->stollen_vga_memory, AMDGPU_GEM_DOMAIN_VRAM, NULL); -+ amdgpu_bo_unreserve(adev->stollen_vga_memory); -+ if (r) { -+ amdgpu_bo_unref(&adev->stollen_vga_memory); -+ return r; -+ } -+ DRM_INFO("amdgpu: %uM of VRAM memory ready\n", -+ (unsigned) (adev->mc.real_vram_size / (1024 * 1024))); -+ r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_TT, -+ adev->mc.gtt_size >> PAGE_SHIFT); -+ if (r) { -+ DRM_ERROR("Failed initializing GTT heap.\n"); -+ return r; -+ } -+ DRM_INFO("amdgpu: %uM of GTT memory ready.\n", -+ (unsigned)(adev->mc.gtt_size / (1024 * 1024))); -+ -+ adev->gds.mem.total_size = adev->gds.mem.total_size << AMDGPU_GDS_SHIFT; -+ adev->gds.mem.gfx_partition_size = adev->gds.mem.gfx_partition_size << AMDGPU_GDS_SHIFT; -+ adev->gds.mem.cs_partition_size = adev->gds.mem.cs_partition_size << AMDGPU_GDS_SHIFT; -+ adev->gds.gws.total_size = adev->gds.gws.total_size << AMDGPU_GWS_SHIFT; -+ adev->gds.gws.gfx_partition_size = adev->gds.gws.gfx_partition_size << AMDGPU_GWS_SHIFT; -+ adev->gds.gws.cs_partition_size = adev->gds.gws.cs_partition_size << AMDGPU_GWS_SHIFT; -+ adev->gds.oa.total_size = adev->gds.oa.total_size << AMDGPU_OA_SHIFT; -+ adev->gds.oa.gfx_partition_size = adev->gds.oa.gfx_partition_size << AMDGPU_OA_SHIFT; -+ adev->gds.oa.cs_partition_size = adev->gds.oa.cs_partition_size << AMDGPU_OA_SHIFT; -+ /* GDS Memory */ -+ r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_GDS, -+ adev->gds.mem.total_size >> PAGE_SHIFT); -+ if (r) { -+ DRM_ERROR("Failed initializing GDS heap.\n"); -+ return r; -+ } -+ -+ /* GWS */ -+ r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_GWS, -+ adev->gds.gws.total_size >> PAGE_SHIFT); -+ if (r) { -+ DRM_ERROR("Failed initializing gws heap.\n"); -+ return r; -+ } -+ -+ /* OA */ -+ r = ttm_bo_init_mm(&adev->mman.bdev, AMDGPU_PL_OA, -+ adev->gds.oa.total_size >> PAGE_SHIFT); -+ if (r) { -+ DRM_ERROR("Failed initializing oa heap.\n"); -+ return r; -+ } -+ -+ r = amdgpu_ttm_debugfs_init(adev); -+ if (r) { -+ DRM_ERROR("Failed to init debugfs\n"); -+ return r; -+ } -+ return 0; -+} -+ -+void amdgpu_ttm_fini(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (!adev->mman.initialized) -+ return; -+ amdgpu_ttm_debugfs_fini(adev); -+ if (adev->stollen_vga_memory) { -+ r = amdgpu_bo_reserve(adev->stollen_vga_memory, false); -+ if (r == 0) { -+ amdgpu_bo_unpin(adev->stollen_vga_memory); -+ amdgpu_bo_unreserve(adev->stollen_vga_memory); -+ } -+ amdgpu_bo_unref(&adev->stollen_vga_memory); -+ } -+ ttm_bo_clean_mm(&adev->mman.bdev, TTM_PL_VRAM); -+ ttm_bo_clean_mm(&adev->mman.bdev, TTM_PL_TT); -+ ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_GDS); -+ ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_GWS); -+ ttm_bo_clean_mm(&adev->mman.bdev, AMDGPU_PL_OA); -+ ttm_bo_device_release(&adev->mman.bdev); -+ amdgpu_gart_fini(adev); -+ amdgpu_ttm_global_fini(adev); -+ adev->mman.initialized = false; -+ DRM_INFO("amdgpu: ttm finalized\n"); -+} -+ -+/* this should only be called at bootup or when userspace -+ * isn't running */ -+void amdgpu_ttm_set_active_vram_size(struct amdgpu_device *adev, u64 size) -+{ -+ struct ttm_mem_type_manager *man; -+ -+ if (!adev->mman.initialized) -+ return; -+ -+ man = &adev->mman.bdev.man[TTM_PL_VRAM]; -+ /* this just adjusts TTM size idea, which sets lpfn to the correct value */ -+ man->size = size >> PAGE_SHIFT; -+} -+ -+static struct vm_operations_struct amdgpu_ttm_vm_ops; -+static const struct vm_operations_struct *ttm_vm_ops = NULL; -+ -+static int amdgpu_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -+{ -+ struct ttm_buffer_object *bo; -+ struct amdgpu_device *adev; -+ int r; -+ -+ bo = (struct ttm_buffer_object *)vma->vm_private_data; -+ if (bo == NULL) { -+ return VM_FAULT_NOPAGE; -+ } -+ adev = amdgpu_get_adev(bo->bdev); -+ down_read(&adev->pm.mclk_lock); -+ r = ttm_vm_ops->fault(vma, vmf); -+ up_read(&adev->pm.mclk_lock); -+ return r; -+} -+ -+int amdgpu_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ struct drm_file *file_priv; -+ struct amdgpu_device *adev; -+ int r; -+ -+ if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { -+ return -EINVAL; -+ } -+ -+ file_priv = filp->private_data; -+ adev = file_priv->minor->dev->dev_private; -+ if (adev == NULL) { -+ return -EINVAL; -+ } -+ r = ttm_bo_mmap(filp, vma, &adev->mman.bdev); -+ if (unlikely(r != 0)) { -+ return r; -+ } -+ if (unlikely(ttm_vm_ops == NULL)) { -+ ttm_vm_ops = vma->vm_ops; -+ amdgpu_ttm_vm_ops = *ttm_vm_ops; -+ amdgpu_ttm_vm_ops.fault = &amdgpu_ttm_fault; -+ } -+ vma->vm_ops = &amdgpu_ttm_vm_ops; -+ return 0; -+} -+ -+int amdgpu_copy_buffer(struct amdgpu_ring *ring, -+ uint64_t src_offset, -+ uint64_t dst_offset, -+ uint32_t byte_count, -+ struct reservation_object *resv, -+ struct amdgpu_fence **fence) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ struct amdgpu_sync sync; -+ uint32_t max_bytes; -+ unsigned num_loops, num_dw; -+ unsigned i; -+ int r; -+ -+ /* sync other rings */ -+ amdgpu_sync_create(&sync); -+ if (resv) { -+ r = amdgpu_sync_resv(adev, &sync, resv, false); -+ if (r) { -+ DRM_ERROR("sync failed (%d).\n", r); -+ amdgpu_sync_free(adev, &sync, NULL); -+ return r; -+ } -+ } -+ -+ max_bytes = adev->mman.buffer_funcs->copy_max_bytes; -+ num_loops = DIV_ROUND_UP(byte_count, max_bytes); -+ num_dw = num_loops * adev->mman.buffer_funcs->copy_num_dw; -+ -+ /* for fence and sync */ -+ num_dw += 64 + AMDGPU_NUM_SYNCS * 8; -+ -+ r = amdgpu_ring_lock(ring, num_dw); -+ if (r) { -+ DRM_ERROR("ring lock failed (%d).\n", r); -+ amdgpu_sync_free(adev, &sync, NULL); -+ return r; -+ } -+ -+ amdgpu_sync_rings(&sync, ring); -+ -+ for (i = 0; i < num_loops; i++) { -+ uint32_t cur_size_in_bytes = min(byte_count, max_bytes); -+ -+ amdgpu_emit_copy_buffer(adev, ring, src_offset, dst_offset, -+ cur_size_in_bytes); -+ -+ src_offset += cur_size_in_bytes; -+ dst_offset += cur_size_in_bytes; -+ byte_count -= cur_size_in_bytes; -+ } -+ -+ r = amdgpu_fence_emit(ring, AMDGPU_FENCE_OWNER_MOVE, fence); -+ if (r) { -+ amdgpu_ring_unlock_undo(ring); -+ amdgpu_sync_free(adev, &sync, NULL); -+ return r; -+ } -+ -+ amdgpu_ring_unlock_commit(ring); -+ amdgpu_sync_free(adev, &sync, *fence); -+ -+ return 0; -+} -+ -+#if defined(CONFIG_DEBUG_FS) -+ -+static int amdgpu_mm_dump_table(struct seq_file *m, void *data) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ unsigned ttm_pl = *(int *)node->info_ent->data; -+ struct drm_device *dev = node->minor->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_mm *mm = (struct drm_mm *)adev->mman.bdev.man[ttm_pl].priv; -+ int ret; -+ struct ttm_bo_global *glob = adev->mman.bdev.glob; -+ -+ spin_lock(&glob->lru_lock); -+ ret = drm_mm_dump_table(m, mm); -+ spin_unlock(&glob->lru_lock); -+ return ret; -+} -+ -+static int ttm_pl_vram = TTM_PL_VRAM; -+static int ttm_pl_tt = TTM_PL_TT; -+ -+static struct drm_info_list amdgpu_ttm_debugfs_list[] = { -+ {"amdgpu_vram_mm", amdgpu_mm_dump_table, 0, &ttm_pl_vram}, -+ {"amdgpu_gtt_mm", amdgpu_mm_dump_table, 0, &ttm_pl_tt}, -+ {"ttm_page_pool", ttm_page_alloc_debugfs, 0, NULL}, -+#ifdef CONFIG_SWIOTLB -+ {"ttm_dma_page_pool", ttm_dma_page_alloc_debugfs, 0, NULL} -+#endif -+}; -+ -+static ssize_t amdgpu_ttm_vram_read(struct file *f, char __user *buf, -+ size_t size, loff_t *pos) -+{ -+ struct amdgpu_device *adev = f->f_inode->i_private; -+ ssize_t result = 0; -+ int r; -+ -+ if (size & 0x3 || *pos & 0x3) -+ return -EINVAL; -+ -+ while (size) { -+ unsigned long flags; -+ uint32_t value; -+ -+ if (*pos >= adev->mc.mc_vram_size) -+ return result; -+ -+ spin_lock_irqsave(&adev->mmio_idx_lock, flags); -+ WREG32(mmMM_INDEX, ((uint32_t)*pos) | 0x80000000); -+ WREG32(mmMM_INDEX_HI, *pos >> 31); -+ value = RREG32(mmMM_DATA); -+ spin_unlock_irqrestore(&adev->mmio_idx_lock, flags); -+ -+ r = put_user(value, (uint32_t *)buf); -+ if (r) -+ return r; -+ -+ result += 4; -+ buf += 4; -+ *pos += 4; -+ size -= 4; -+ } -+ -+ return result; -+} -+ -+static const struct file_operations amdgpu_ttm_vram_fops = { -+ .owner = THIS_MODULE, -+ .read = amdgpu_ttm_vram_read, -+ .llseek = default_llseek -+}; -+ -+static ssize_t amdgpu_ttm_gtt_read(struct file *f, char __user *buf, -+ size_t size, loff_t *pos) -+{ -+ struct amdgpu_device *adev = f->f_inode->i_private; -+ ssize_t result = 0; -+ int r; -+ -+ while (size) { -+ loff_t p = *pos / PAGE_SIZE; -+ unsigned off = *pos & ~PAGE_MASK; -+ size_t cur_size = min_t(size_t, size, PAGE_SIZE - off); -+ struct page *page; -+ void *ptr; -+ -+ if (p >= adev->gart.num_cpu_pages) -+ return result; -+ -+ page = adev->gart.pages[p]; -+ if (page) { -+ ptr = kmap(page); -+ ptr += off; -+ -+ r = copy_to_user(buf, ptr, cur_size); -+ kunmap(adev->gart.pages[p]); -+ } else -+ r = clear_user(buf, cur_size); -+ -+ if (r) -+ return -EFAULT; -+ -+ result += cur_size; -+ buf += cur_size; -+ *pos += cur_size; -+ size -= cur_size; -+ } -+ -+ return result; -+} -+ -+static const struct file_operations amdgpu_ttm_gtt_fops = { -+ .owner = THIS_MODULE, -+ .read = amdgpu_ttm_gtt_read, -+ .llseek = default_llseek -+}; -+ -+#endif -+ -+static int amdgpu_ttm_debugfs_init(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ unsigned count; -+ -+ struct drm_minor *minor = adev->ddev->primary; -+ struct dentry *ent, *root = minor->debugfs_root; -+ -+ ent = debugfs_create_file("amdgpu_vram", S_IFREG | S_IRUGO, root, -+ adev, &amdgpu_ttm_vram_fops); -+ if (IS_ERR(ent)) -+ return PTR_ERR(ent); -+ i_size_write(ent->d_inode, adev->mc.mc_vram_size); -+ adev->mman.vram = ent; -+ -+ ent = debugfs_create_file("amdgpu_gtt", S_IFREG | S_IRUGO, root, -+ adev, &amdgpu_ttm_gtt_fops); -+ if (IS_ERR(ent)) -+ return PTR_ERR(ent); -+ i_size_write(ent->d_inode, adev->mc.gtt_size); -+ adev->mman.gtt = ent; -+ -+ count = ARRAY_SIZE(amdgpu_ttm_debugfs_list); -+ -+#ifdef CONFIG_SWIOTLB -+ if (!swiotlb_nr_tbl()) -+ --count; -+#endif -+ -+ return amdgpu_debugfs_add_files(adev, amdgpu_ttm_debugfs_list, count); -+#else -+ -+ return 0; -+#endif -+} -+ -+static void amdgpu_ttm_debugfs_fini(struct amdgpu_device *adev) -+{ -+#if defined(CONFIG_DEBUG_FS) -+ -+ debugfs_remove(adev->mman.vram); -+ adev->mman.vram = NULL; -+ -+ debugfs_remove(adev->mman.gtt); -+ adev->mman.gtt = NULL; -+#endif -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c -new file mode 100644 -index 0000000..482e667 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c -@@ -0,0 +1,317 @@ -+/* -+ * 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 <linux/firmware.h> -+#include <linux/slab.h> -+#include <linux/module.h> -+#include <drm/drmP.h> -+#include "amdgpu.h" -+#include "amdgpu_ucode.h" -+ -+static void amdgpu_ucode_print_common_hdr(const struct common_firmware_header *hdr) -+{ -+ DRM_DEBUG("size_bytes: %u\n", le32_to_cpu(hdr->size_bytes)); -+ DRM_DEBUG("header_size_bytes: %u\n", le32_to_cpu(hdr->header_size_bytes)); -+ DRM_DEBUG("header_version_major: %u\n", le16_to_cpu(hdr->header_version_major)); -+ DRM_DEBUG("header_version_minor: %u\n", le16_to_cpu(hdr->header_version_minor)); -+ DRM_DEBUG("ip_version_major: %u\n", le16_to_cpu(hdr->ip_version_major)); -+ DRM_DEBUG("ip_version_minor: %u\n", le16_to_cpu(hdr->ip_version_minor)); -+ DRM_DEBUG("ucode_version: 0x%08x\n", le32_to_cpu(hdr->ucode_version)); -+ DRM_DEBUG("ucode_size_bytes: %u\n", le32_to_cpu(hdr->ucode_size_bytes)); -+ DRM_DEBUG("ucode_array_offset_bytes: %u\n", -+ le32_to_cpu(hdr->ucode_array_offset_bytes)); -+ DRM_DEBUG("crc32: 0x%08x\n", le32_to_cpu(hdr->crc32)); -+} -+ -+void amdgpu_ucode_print_mc_hdr(const struct common_firmware_header *hdr) -+{ -+ uint16_t version_major = le16_to_cpu(hdr->header_version_major); -+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor); -+ -+ DRM_DEBUG("MC\n"); -+ amdgpu_ucode_print_common_hdr(hdr); -+ -+ if (version_major == 1) { -+ const struct mc_firmware_header_v1_0 *mc_hdr = -+ container_of(hdr, struct mc_firmware_header_v1_0, header); -+ -+ DRM_DEBUG("io_debug_size_bytes: %u\n", -+ le32_to_cpu(mc_hdr->io_debug_size_bytes)); -+ DRM_DEBUG("io_debug_array_offset_bytes: %u\n", -+ le32_to_cpu(mc_hdr->io_debug_array_offset_bytes)); -+ } else { -+ DRM_ERROR("Unknown MC ucode version: %u.%u\n", version_major, version_minor); -+ } -+} -+ -+void amdgpu_ucode_print_smc_hdr(const struct common_firmware_header *hdr) -+{ -+ uint16_t version_major = le16_to_cpu(hdr->header_version_major); -+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor); -+ -+ DRM_DEBUG("SMC\n"); -+ amdgpu_ucode_print_common_hdr(hdr); -+ -+ if (version_major == 1) { -+ const struct smc_firmware_header_v1_0 *smc_hdr = -+ container_of(hdr, struct smc_firmware_header_v1_0, header); -+ -+ DRM_DEBUG("ucode_start_addr: %u\n", le32_to_cpu(smc_hdr->ucode_start_addr)); -+ } else { -+ DRM_ERROR("Unknown SMC ucode version: %u.%u\n", version_major, version_minor); -+ } -+} -+ -+void amdgpu_ucode_print_gfx_hdr(const struct common_firmware_header *hdr) -+{ -+ uint16_t version_major = le16_to_cpu(hdr->header_version_major); -+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor); -+ -+ DRM_DEBUG("GFX\n"); -+ amdgpu_ucode_print_common_hdr(hdr); -+ -+ if (version_major == 1) { -+ const struct gfx_firmware_header_v1_0 *gfx_hdr = -+ container_of(hdr, struct gfx_firmware_header_v1_0, header); -+ -+ DRM_DEBUG("ucode_feature_version: %u\n", -+ le32_to_cpu(gfx_hdr->ucode_feature_version)); -+ DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(gfx_hdr->jt_offset)); -+ DRM_DEBUG("jt_size: %u\n", le32_to_cpu(gfx_hdr->jt_size)); -+ } else { -+ DRM_ERROR("Unknown GFX ucode version: %u.%u\n", version_major, version_minor); -+ } -+} -+ -+void amdgpu_ucode_print_rlc_hdr(const struct common_firmware_header *hdr) -+{ -+ uint16_t version_major = le16_to_cpu(hdr->header_version_major); -+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor); -+ -+ DRM_DEBUG("RLC\n"); -+ amdgpu_ucode_print_common_hdr(hdr); -+ -+ if (version_major == 1) { -+ const struct rlc_firmware_header_v1_0 *rlc_hdr = -+ container_of(hdr, struct rlc_firmware_header_v1_0, header); -+ -+ DRM_DEBUG("ucode_feature_version: %u\n", -+ le32_to_cpu(rlc_hdr->ucode_feature_version)); -+ DRM_DEBUG("save_and_restore_offset: %u\n", -+ le32_to_cpu(rlc_hdr->save_and_restore_offset)); -+ DRM_DEBUG("clear_state_descriptor_offset: %u\n", -+ le32_to_cpu(rlc_hdr->clear_state_descriptor_offset)); -+ DRM_DEBUG("avail_scratch_ram_locations: %u\n", -+ le32_to_cpu(rlc_hdr->avail_scratch_ram_locations)); -+ DRM_DEBUG("master_pkt_description_offset: %u\n", -+ le32_to_cpu(rlc_hdr->master_pkt_description_offset)); -+ } else if (version_major == 2) { -+ const struct rlc_firmware_header_v2_0 *rlc_hdr = -+ container_of(hdr, struct rlc_firmware_header_v2_0, header); -+ -+ DRM_DEBUG("ucode_feature_version: %u\n", -+ le32_to_cpu(rlc_hdr->ucode_feature_version)); -+ DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(rlc_hdr->jt_offset)); -+ DRM_DEBUG("jt_size: %u\n", le32_to_cpu(rlc_hdr->jt_size)); -+ DRM_DEBUG("save_and_restore_offset: %u\n", -+ le32_to_cpu(rlc_hdr->save_and_restore_offset)); -+ DRM_DEBUG("clear_state_descriptor_offset: %u\n", -+ le32_to_cpu(rlc_hdr->clear_state_descriptor_offset)); -+ DRM_DEBUG("avail_scratch_ram_locations: %u\n", -+ le32_to_cpu(rlc_hdr->avail_scratch_ram_locations)); -+ DRM_DEBUG("reg_restore_list_size: %u\n", -+ le32_to_cpu(rlc_hdr->reg_restore_list_size)); -+ DRM_DEBUG("reg_list_format_start: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_format_start)); -+ DRM_DEBUG("reg_list_format_separate_start: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_format_separate_start)); -+ DRM_DEBUG("starting_offsets_start: %u\n", -+ le32_to_cpu(rlc_hdr->starting_offsets_start)); -+ DRM_DEBUG("reg_list_format_size_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_format_size_bytes)); -+ DRM_DEBUG("reg_list_format_array_offset_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_format_array_offset_bytes)); -+ DRM_DEBUG("reg_list_size_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_size_bytes)); -+ DRM_DEBUG("reg_list_array_offset_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_array_offset_bytes)); -+ DRM_DEBUG("reg_list_format_separate_size_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_format_separate_size_bytes)); -+ DRM_DEBUG("reg_list_format_separate_array_offset_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_format_separate_array_offset_bytes)); -+ DRM_DEBUG("reg_list_separate_size_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_separate_size_bytes)); -+ DRM_DEBUG("reg_list_separate_size_bytes: %u\n", -+ le32_to_cpu(rlc_hdr->reg_list_separate_size_bytes)); -+ } else { -+ DRM_ERROR("Unknown RLC ucode version: %u.%u\n", version_major, version_minor); -+ } -+} -+ -+void amdgpu_ucode_print_sdma_hdr(const struct common_firmware_header *hdr) -+{ -+ uint16_t version_major = le16_to_cpu(hdr->header_version_major); -+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor); -+ -+ DRM_DEBUG("SDMA\n"); -+ amdgpu_ucode_print_common_hdr(hdr); -+ -+ if (version_major == 1) { -+ const struct sdma_firmware_header_v1_0 *sdma_hdr = -+ container_of(hdr, struct sdma_firmware_header_v1_0, header); -+ -+ DRM_DEBUG("ucode_feature_version: %u\n", -+ le32_to_cpu(sdma_hdr->ucode_feature_version)); -+ DRM_DEBUG("ucode_change_version: %u\n", -+ le32_to_cpu(sdma_hdr->ucode_change_version)); -+ DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(sdma_hdr->jt_offset)); -+ DRM_DEBUG("jt_size: %u\n", le32_to_cpu(sdma_hdr->jt_size)); -+ if (version_minor >= 1) { -+ const struct sdma_firmware_header_v1_1 *sdma_v1_1_hdr = -+ container_of(sdma_hdr, struct sdma_firmware_header_v1_1, v1_0); -+ DRM_DEBUG("digest_size: %u\n", le32_to_cpu(sdma_v1_1_hdr->digest_size)); -+ } -+ } else { -+ DRM_ERROR("Unknown SDMA ucode version: %u.%u\n", -+ version_major, version_minor); -+ } -+} -+ -+int amdgpu_ucode_validate(const struct firmware *fw) -+{ -+ const struct common_firmware_header *hdr = -+ (const struct common_firmware_header *)fw->data; -+ -+ if (fw->size == le32_to_cpu(hdr->size_bytes)) -+ return 0; -+ -+ return -EINVAL; -+} -+ -+bool amdgpu_ucode_hdr_version(union amdgpu_firmware_header *hdr, -+ uint16_t hdr_major, uint16_t hdr_minor) -+{ -+ if ((hdr->common.header_version_major == hdr_major) && -+ (hdr->common.header_version_minor == hdr_minor)) -+ return false; -+ return true; -+} -+ -+static int amdgpu_ucode_init_single_fw(struct amdgpu_firmware_info *ucode, -+ uint64_t mc_addr, void *kptr) -+{ -+ const struct common_firmware_header *header = NULL; -+ -+ if (NULL == ucode->fw) -+ return 0; -+ -+ ucode->mc_addr = mc_addr; -+ ucode->kaddr = kptr; -+ -+ header = (const struct common_firmware_header *)ucode->fw->data; -+ memcpy(ucode->kaddr, (void *)((uint8_t *)ucode->fw->data + -+ le32_to_cpu(header->ucode_array_offset_bytes)), -+ le32_to_cpu(header->ucode_size_bytes)); -+ -+ return 0; -+} -+ -+int amdgpu_ucode_init_bo(struct amdgpu_device *adev) -+{ -+ struct amdgpu_bo **bo = &adev->firmware.fw_buf; -+ uint64_t fw_mc_addr; -+ void *fw_buf_ptr = NULL; -+ uint64_t fw_offset = 0; -+ int i, err; -+ struct amdgpu_firmware_info *ucode = NULL; -+ const struct common_firmware_header *header = NULL; -+ -+ err = amdgpu_bo_create(adev, adev->firmware.fw_size, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_GTT, 0, NULL, bo); -+ if (err) { -+ dev_err(adev->dev, "(%d) Firmware buffer allocate failed\n", err); -+ err = -ENOMEM; -+ goto failed; -+ } -+ -+ err = amdgpu_bo_reserve(*bo, false); -+ if (err) { -+ amdgpu_bo_unref(bo); -+ dev_err(adev->dev, "(%d) Firmware buffer reserve failed\n", err); -+ goto failed; -+ } -+ -+ err = amdgpu_bo_pin(*bo, AMDGPU_GEM_DOMAIN_GTT, &fw_mc_addr); -+ if (err) { -+ amdgpu_bo_unreserve(*bo); -+ amdgpu_bo_unref(bo); -+ dev_err(adev->dev, "(%d) Firmware buffer pin failed\n", err); -+ goto failed; -+ } -+ -+ err = amdgpu_bo_kmap(*bo, &fw_buf_ptr); -+ if (err) { -+ dev_err(adev->dev, "(%d) Firmware buffer kmap failed\n", err); -+ amdgpu_bo_unpin(*bo); -+ amdgpu_bo_unreserve(*bo); -+ amdgpu_bo_unref(bo); -+ goto failed; -+ } -+ -+ amdgpu_bo_unreserve(*bo); -+ -+ fw_offset = 0; -+ for (i = 0; i < AMDGPU_UCODE_ID_MAXIMUM; i++) { -+ ucode = &adev->firmware.ucode[i]; -+ if (ucode->fw) { -+ header = (const struct common_firmware_header *)ucode->fw->data; -+ amdgpu_ucode_init_single_fw(ucode, fw_mc_addr + fw_offset, -+ fw_buf_ptr + fw_offset); -+ fw_offset += ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE); -+ } -+ } -+ -+failed: -+ if (err) -+ adev->firmware.smu_load = false; -+ -+ return err; -+} -+ -+int amdgpu_ucode_fini_bo(struct amdgpu_device *adev) -+{ -+ int i; -+ struct amdgpu_firmware_info *ucode = NULL; -+ -+ for (i = 0; i < AMDGPU_UCODE_ID_MAXIMUM; i++) { -+ ucode = &adev->firmware.ucode[i]; -+ if (ucode->fw) { -+ ucode->mc_addr = 0; -+ ucode->kaddr = NULL; -+ } -+ } -+ amdgpu_bo_unref(&adev->firmware.fw_buf); -+ adev->firmware.fw_buf = NULL; -+ -+ return 0; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h -new file mode 100644 -index 0000000..e468be4 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.h -@@ -0,0 +1,176 @@ -+/* -+ * Copyright 2012 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+#ifndef __AMDGPU_UCODE_H__ -+#define __AMDGPU_UCODE_H__ -+ -+struct common_firmware_header { -+ uint32_t size_bytes; /* size of the entire header+image(s) in bytes */ -+ uint32_t header_size_bytes; /* size of just the header in bytes */ -+ uint16_t header_version_major; /* header version */ -+ uint16_t header_version_minor; /* header version */ -+ uint16_t ip_version_major; /* IP version */ -+ uint16_t ip_version_minor; /* IP version */ -+ uint32_t ucode_version; -+ uint32_t ucode_size_bytes; /* size of ucode in bytes */ -+ uint32_t ucode_array_offset_bytes; /* payload offset from the start of the header */ -+ uint32_t crc32; /* crc32 checksum of the payload */ -+}; -+ -+/* version_major=1, version_minor=0 */ -+struct mc_firmware_header_v1_0 { -+ struct common_firmware_header header; -+ uint32_t io_debug_size_bytes; /* size of debug array in dwords */ -+ uint32_t io_debug_array_offset_bytes; /* payload offset from the start of the header */ -+}; -+ -+/* version_major=1, version_minor=0 */ -+struct smc_firmware_header_v1_0 { -+ struct common_firmware_header header; -+ uint32_t ucode_start_addr; -+}; -+ -+/* version_major=1, version_minor=0 */ -+struct gfx_firmware_header_v1_0 { -+ struct common_firmware_header header; -+ uint32_t ucode_feature_version; -+ uint32_t jt_offset; /* jt location */ -+ uint32_t jt_size; /* size of jt */ -+}; -+ -+/* version_major=1, version_minor=0 */ -+struct rlc_firmware_header_v1_0 { -+ struct common_firmware_header header; -+ uint32_t ucode_feature_version; -+ uint32_t save_and_restore_offset; -+ uint32_t clear_state_descriptor_offset; -+ uint32_t avail_scratch_ram_locations; -+ uint32_t master_pkt_description_offset; -+}; -+ -+/* version_major=2, version_minor=0 */ -+struct rlc_firmware_header_v2_0 { -+ struct common_firmware_header header; -+ uint32_t ucode_feature_version; -+ uint32_t jt_offset; /* jt location */ -+ uint32_t jt_size; /* size of jt */ -+ uint32_t save_and_restore_offset; -+ uint32_t clear_state_descriptor_offset; -+ uint32_t avail_scratch_ram_locations; -+ uint32_t reg_restore_list_size; -+ uint32_t reg_list_format_start; -+ uint32_t reg_list_format_separate_start; -+ uint32_t starting_offsets_start; -+ uint32_t reg_list_format_size_bytes; /* size of reg list format array in bytes */ -+ uint32_t reg_list_format_array_offset_bytes; /* payload offset from the start of the header */ -+ uint32_t reg_list_size_bytes; /* size of reg list array in bytes */ -+ uint32_t reg_list_array_offset_bytes; /* payload offset from the start of the header */ -+ uint32_t reg_list_format_separate_size_bytes; /* size of reg list format array in bytes */ -+ uint32_t reg_list_format_separate_array_offset_bytes; /* payload offset from the start of the header */ -+ uint32_t reg_list_separate_size_bytes; /* size of reg list array in bytes */ -+ uint32_t reg_list_separate_array_offset_bytes; /* payload offset from the start of the header */ -+}; -+ -+/* version_major=1, version_minor=0 */ -+struct sdma_firmware_header_v1_0 { -+ struct common_firmware_header header; -+ uint32_t ucode_feature_version; -+ uint32_t ucode_change_version; -+ uint32_t jt_offset; /* jt location */ -+ uint32_t jt_size; /* size of jt */ -+}; -+ -+/* version_major=1, version_minor=1 */ -+struct sdma_firmware_header_v1_1 { -+ struct sdma_firmware_header_v1_0 v1_0; -+ uint32_t digest_size; -+}; -+ -+/* header is fixed size */ -+union amdgpu_firmware_header { -+ struct common_firmware_header common; -+ struct mc_firmware_header_v1_0 mc; -+ struct smc_firmware_header_v1_0 smc; -+ struct gfx_firmware_header_v1_0 gfx; -+ struct rlc_firmware_header_v1_0 rlc; -+ struct rlc_firmware_header_v2_0 rlc_v2_0; -+ struct sdma_firmware_header_v1_0 sdma; -+ struct sdma_firmware_header_v1_1 sdma_v1_1; -+ uint8_t raw[0x100]; -+}; -+ -+/* -+ * fw loading support -+ */ -+enum AMDGPU_UCODE_ID { -+ AMDGPU_UCODE_ID_SDMA0 = 0, -+ AMDGPU_UCODE_ID_SDMA1, -+ AMDGPU_UCODE_ID_CP_CE, -+ AMDGPU_UCODE_ID_CP_PFP, -+ AMDGPU_UCODE_ID_CP_ME, -+ AMDGPU_UCODE_ID_CP_MEC1, -+ AMDGPU_UCODE_ID_CP_MEC2, -+ AMDGPU_UCODE_ID_RLC_G, -+ AMDGPU_UCODE_ID_MAXIMUM, -+}; -+ -+/* engine firmware status */ -+enum AMDGPU_UCODE_STATUS { -+ AMDGPU_UCODE_STATUS_INVALID, -+ AMDGPU_UCODE_STATUS_NOT_LOADED, -+ AMDGPU_UCODE_STATUS_LOADED, -+}; -+ -+/* conform to smu_ucode_xfer_cz.h */ -+#define AMDGPU_SDMA0_UCODE_LOADED 0x00000001 -+#define AMDGPU_SDMA1_UCODE_LOADED 0x00000002 -+#define AMDGPU_CPCE_UCODE_LOADED 0x00000004 -+#define AMDGPU_CPPFP_UCODE_LOADED 0x00000008 -+#define AMDGPU_CPME_UCODE_LOADED 0x00000010 -+#define AMDGPU_CPMEC1_UCODE_LOADED 0x00000020 -+#define AMDGPU_CPMEC2_UCODE_LOADED 0x00000040 -+#define AMDGPU_CPRLC_UCODE_LOADED 0x00000100 -+ -+/* amdgpu firmware info */ -+struct amdgpu_firmware_info { -+ /* ucode ID */ -+ enum AMDGPU_UCODE_ID ucode_id; -+ /* request_firmware */ -+ const struct firmware *fw; -+ /* starting mc address */ -+ uint64_t mc_addr; -+ /* kernel linear address */ -+ void *kaddr; -+}; -+ -+void amdgpu_ucode_print_mc_hdr(const struct common_firmware_header *hdr); -+void amdgpu_ucode_print_smc_hdr(const struct common_firmware_header *hdr); -+void amdgpu_ucode_print_gfx_hdr(const struct common_firmware_header *hdr); -+void amdgpu_ucode_print_rlc_hdr(const struct common_firmware_header *hdr); -+void amdgpu_ucode_print_sdma_hdr(const struct common_firmware_header *hdr); -+int amdgpu_ucode_validate(const struct firmware *fw); -+bool amdgpu_ucode_hdr_version(union amdgpu_firmware_header *hdr, -+ uint16_t hdr_major, uint16_t hdr_minor); -+int amdgpu_ucode_init_bo(struct amdgpu_device *adev); -+int amdgpu_ucode_fini_bo(struct amdgpu_device *adev); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c -new file mode 100644 -index 0000000..c03bce6 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c -@@ -0,0 +1,976 @@ -+/* -+ * Copyright 2011 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ */ -+/* -+ * Authors: -+ * Christian König <deathsimple@vodafone.de> -+ */ -+ -+#include <linux/firmware.h> -+#include <linux/module.h> -+#include <drm/drmP.h> -+#include <drm/drm.h> -+ -+#include "amdgpu.h" -+#include "amdgpu_pm.h" -+#include "amdgpu_uvd.h" -+#include "cikd.h" -+#include "uvd/uvd_4_2_d.h" -+ -+/* 1 second timeout */ -+#define UVD_IDLE_TIMEOUT_MS 1000 -+ -+/* Firmware Names */ -+#ifdef CONFIG_DRM_AMDGPU_CIK -+#define FIRMWARE_BONAIRE "radeon/bonaire_uvd.bin" -+#define FIRMWARE_KABINI "radeon/kabini_uvd.bin" -+#define FIRMWARE_KAVERI "radeon/kaveri_uvd.bin" -+#define FIRMWARE_HAWAII "radeon/hawaii_uvd.bin" -+#define FIRMWARE_MULLINS "radeon/mullins_uvd.bin" -+#endif -+#define FIRMWARE_TONGA "radeon/tonga_uvd.bin" -+#define FIRMWARE_CARRIZO "radeon/carrizo_uvd.bin" -+ -+/** -+ * amdgpu_uvd_cs_ctx - Command submission parser context -+ * -+ * Used for emulating virtual memory support on UVD 4.2. -+ */ -+struct amdgpu_uvd_cs_ctx { -+ struct amdgpu_cs_parser *parser; -+ unsigned reg, count; -+ unsigned data0, data1; -+ unsigned idx; -+ unsigned ib_idx; -+ -+ /* does the IB has a msg command */ -+ bool has_msg_cmd; -+ -+ /* minimum buffer sizes */ -+ unsigned *buf_sizes; -+}; -+ -+#ifdef CONFIG_DRM_AMDGPU_CIK -+MODULE_FIRMWARE(FIRMWARE_BONAIRE); -+MODULE_FIRMWARE(FIRMWARE_KABINI); -+MODULE_FIRMWARE(FIRMWARE_KAVERI); -+MODULE_FIRMWARE(FIRMWARE_HAWAII); -+MODULE_FIRMWARE(FIRMWARE_MULLINS); -+#endif -+MODULE_FIRMWARE(FIRMWARE_TONGA); -+MODULE_FIRMWARE(FIRMWARE_CARRIZO); -+ -+static void amdgpu_uvd_note_usage(struct amdgpu_device *adev); -+static void amdgpu_uvd_idle_work_handler(struct work_struct *work); -+ -+int amdgpu_uvd_sw_init(struct amdgpu_device *adev) -+{ -+ unsigned long bo_size; -+ const char *fw_name; -+ const struct common_firmware_header *hdr; -+ unsigned version_major, version_minor, family_id; -+ int i, r; -+ -+ INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler); -+ -+ switch (adev->asic_type) { -+#ifdef CONFIG_DRM_AMDGPU_CIK -+ case CHIP_BONAIRE: -+ fw_name = FIRMWARE_BONAIRE; -+ break; -+ case CHIP_KABINI: -+ fw_name = FIRMWARE_KABINI; -+ break; -+ case CHIP_KAVERI: -+ fw_name = FIRMWARE_KAVERI; -+ break; -+ case CHIP_HAWAII: -+ fw_name = FIRMWARE_HAWAII; -+ break; -+ case CHIP_MULLINS: -+ fw_name = FIRMWARE_MULLINS; -+ break; -+#endif -+ case CHIP_TONGA: -+ fw_name = FIRMWARE_TONGA; -+ break; -+ case CHIP_CARRIZO: -+ fw_name = FIRMWARE_CARRIZO; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ r = request_firmware(&adev->uvd.fw, fw_name, adev->dev); -+ if (r) { -+ dev_err(adev->dev, "amdgpu_uvd: Can't load firmware \"%s\"\n", -+ fw_name); -+ return r; -+ } -+ -+ r = amdgpu_ucode_validate(adev->uvd.fw); -+ if (r) { -+ dev_err(adev->dev, "amdgpu_uvd: Can't validate firmware \"%s\"\n", -+ fw_name); -+ release_firmware(adev->uvd.fw); -+ adev->uvd.fw = NULL; -+ return r; -+ } -+ -+ hdr = (const struct common_firmware_header *)adev->uvd.fw->data; -+ family_id = le32_to_cpu(hdr->ucode_version) & 0xff; -+ version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; -+ version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; -+ DRM_INFO("Found UVD firmware Version: %hu.%hu Family ID: %hu\n", -+ version_major, version_minor, family_id); -+ -+ bo_size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8) -+ + AMDGPU_UVD_STACK_SIZE + AMDGPU_UVD_HEAP_SIZE; -+ r = amdgpu_bo_create(adev, bo_size, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, &adev->uvd.vcpu_bo); -+ if (r) { -+ dev_err(adev->dev, "(%d) failed to allocate UVD bo\n", r); -+ return r; -+ } -+ -+ r = amdgpu_bo_reserve(adev->uvd.vcpu_bo, false); -+ if (r) { -+ amdgpu_bo_unref(&adev->uvd.vcpu_bo); -+ dev_err(adev->dev, "(%d) failed to reserve UVD bo\n", r); -+ return r; -+ } -+ -+ r = amdgpu_bo_pin(adev->uvd.vcpu_bo, AMDGPU_GEM_DOMAIN_VRAM, -+ &adev->uvd.gpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(adev->uvd.vcpu_bo); -+ amdgpu_bo_unref(&adev->uvd.vcpu_bo); -+ dev_err(adev->dev, "(%d) UVD bo pin failed\n", r); -+ return r; -+ } -+ -+ r = amdgpu_bo_kmap(adev->uvd.vcpu_bo, &adev->uvd.cpu_addr); -+ if (r) { -+ dev_err(adev->dev, "(%d) UVD map failed\n", r); -+ return r; -+ } -+ -+ amdgpu_bo_unreserve(adev->uvd.vcpu_bo); -+ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { -+ atomic_set(&adev->uvd.handles[i], 0); -+ adev->uvd.filp[i] = NULL; -+ } -+ -+ /* from uvd v5.0 HW addressing capacity increased to 64 bits */ -+ if (!amdgpu_ip_block_version_cmp(adev, AMDGPU_IP_BLOCK_TYPE_UVD, 5, 0)) -+ adev->uvd.address_64_bit = true; -+ -+ return 0; -+} -+ -+int amdgpu_uvd_sw_fini(struct amdgpu_device *adev) -+{ -+ int r; -+ -+ if (adev->uvd.vcpu_bo == NULL) -+ return 0; -+ -+ r = amdgpu_bo_reserve(adev->uvd.vcpu_bo, false); -+ if (!r) { -+ amdgpu_bo_kunmap(adev->uvd.vcpu_bo); -+ amdgpu_bo_unpin(adev->uvd.vcpu_bo); -+ amdgpu_bo_unreserve(adev->uvd.vcpu_bo); -+ } -+ -+ amdgpu_bo_unref(&adev->uvd.vcpu_bo); -+ -+ amdgpu_ring_fini(&adev->uvd.ring); -+ -+ release_firmware(adev->uvd.fw); -+ -+ return 0; -+} -+ -+int amdgpu_uvd_suspend(struct amdgpu_device *adev) -+{ -+ unsigned size; -+ void *ptr; -+ const struct common_firmware_header *hdr; -+ int i; -+ -+ if (adev->uvd.vcpu_bo == NULL) -+ return 0; -+ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) -+ if (atomic_read(&adev->uvd.handles[i])) -+ break; -+ -+ if (i == AMDGPU_MAX_UVD_HANDLES) -+ return 0; -+ -+ hdr = (const struct common_firmware_header *)adev->uvd.fw->data; -+ -+ size = amdgpu_bo_size(adev->uvd.vcpu_bo); -+ size -= le32_to_cpu(hdr->ucode_size_bytes); -+ -+ ptr = adev->uvd.cpu_addr; -+ ptr += le32_to_cpu(hdr->ucode_size_bytes); -+ -+ adev->uvd.saved_bo = kmalloc(size, GFP_KERNEL); -+ memcpy(adev->uvd.saved_bo, ptr, size); -+ -+ return 0; -+} -+ -+int amdgpu_uvd_resume(struct amdgpu_device *adev) -+{ -+ unsigned size; -+ void *ptr; -+ const struct common_firmware_header *hdr; -+ unsigned offset; -+ -+ if (adev->uvd.vcpu_bo == NULL) -+ return -EINVAL; -+ -+ hdr = (const struct common_firmware_header *)adev->uvd.fw->data; -+ offset = le32_to_cpu(hdr->ucode_array_offset_bytes); -+ memcpy(adev->uvd.cpu_addr, (adev->uvd.fw->data) + offset, -+ (adev->uvd.fw->size) - offset); -+ -+ size = amdgpu_bo_size(adev->uvd.vcpu_bo); -+ size -= le32_to_cpu(hdr->ucode_size_bytes); -+ ptr = adev->uvd.cpu_addr; -+ ptr += le32_to_cpu(hdr->ucode_size_bytes); -+ -+ if (adev->uvd.saved_bo != NULL) { -+ memcpy(ptr, adev->uvd.saved_bo, size); -+ kfree(adev->uvd.saved_bo); -+ adev->uvd.saved_bo = NULL; -+ } else -+ memset(ptr, 0, size); -+ -+ return 0; -+} -+ -+void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp) -+{ -+ struct amdgpu_ring *ring = &adev->uvd.ring; -+ int i, r; -+ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { -+ uint32_t handle = atomic_read(&adev->uvd.handles[i]); -+ if (handle != 0 && adev->uvd.filp[i] == filp) { -+ struct amdgpu_fence *fence; -+ -+ amdgpu_uvd_note_usage(adev); -+ -+ r = amdgpu_uvd_get_destroy_msg(ring, handle, &fence); -+ if (r) { -+ DRM_ERROR("Error destroying UVD (%d)!\n", r); -+ continue; -+ } -+ -+ amdgpu_fence_wait(fence, false); -+ amdgpu_fence_unref(&fence); -+ -+ adev->uvd.filp[i] = NULL; -+ atomic_set(&adev->uvd.handles[i], 0); -+ } -+ } -+} -+ -+static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *rbo) -+{ -+ int i; -+ for (i = 0; i < rbo->placement.num_placement; ++i) { -+ rbo->placements[i].fpfn = 0 >> PAGE_SHIFT; -+ rbo->placements[i].lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT; -+ } -+} -+ -+/** -+ * amdgpu_uvd_cs_pass1 - first parsing round -+ * -+ * @ctx: UVD parser context -+ * -+ * Make sure UVD message and feedback buffers are in VRAM and -+ * nobody is violating an 256MB boundary. -+ */ -+static int amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx) -+{ -+ struct amdgpu_bo_va_mapping *mapping; -+ struct amdgpu_bo *bo; -+ uint32_t cmd, lo, hi; -+ uint64_t addr; -+ int r = 0; -+ -+ lo = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->data0); -+ hi = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->data1); -+ addr = ((uint64_t)lo) | (((uint64_t)hi) << 32); -+ -+ mapping = amdgpu_cs_find_mapping(ctx->parser, addr, &bo); -+ if (mapping == NULL) { -+ DRM_ERROR("Can't find BO for addr 0x%08Lx\n", addr); -+ return -EINVAL; -+ } -+ -+ if (!ctx->parser->adev->uvd.address_64_bit) { -+ /* check if it's a message or feedback command */ -+ cmd = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->idx) >> 1; -+ if (cmd == 0x0 || cmd == 0x3) { -+ /* yes, force it into VRAM */ -+ uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM; -+ amdgpu_ttm_placement_from_domain(bo, domain); -+ } -+ amdgpu_uvd_force_into_uvd_segment(bo); -+ -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); -+ } -+ -+ return r; -+} -+ -+/** -+ * amdgpu_uvd_cs_msg_decode - handle UVD decode message -+ * -+ * @msg: pointer to message structure -+ * @buf_sizes: returned buffer sizes -+ * -+ * Peek into the decode message and calculate the necessary buffer sizes. -+ */ -+static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) -+{ -+ unsigned stream_type = msg[4]; -+ unsigned width = msg[6]; -+ unsigned height = msg[7]; -+ unsigned dpb_size = msg[9]; -+ unsigned pitch = msg[28]; -+ unsigned level = msg[57]; -+ -+ unsigned width_in_mb = width / 16; -+ unsigned height_in_mb = ALIGN(height / 16, 2); -+ unsigned fs_in_mb = width_in_mb * height_in_mb; -+ -+ unsigned image_size, tmp, min_dpb_size, num_dpb_buffer; -+ -+ image_size = width * height; -+ image_size += image_size / 2; -+ image_size = ALIGN(image_size, 1024); -+ -+ switch (stream_type) { -+ case 0: /* H264 */ -+ case 7: /* H264 Perf */ -+ switch(level) { -+ case 30: -+ num_dpb_buffer = 8100 / fs_in_mb; -+ break; -+ case 31: -+ num_dpb_buffer = 18000 / fs_in_mb; -+ break; -+ case 32: -+ num_dpb_buffer = 20480 / fs_in_mb; -+ break; -+ case 41: -+ num_dpb_buffer = 32768 / fs_in_mb; -+ break; -+ case 42: -+ num_dpb_buffer = 34816 / fs_in_mb; -+ break; -+ case 50: -+ num_dpb_buffer = 110400 / fs_in_mb; -+ break; -+ case 51: -+ num_dpb_buffer = 184320 / fs_in_mb; -+ break; -+ default: -+ num_dpb_buffer = 184320 / fs_in_mb; -+ break; -+ } -+ num_dpb_buffer++; -+ if (num_dpb_buffer > 17) -+ num_dpb_buffer = 17; -+ -+ /* reference picture buffer */ -+ min_dpb_size = image_size * num_dpb_buffer; -+ -+ /* macroblock context buffer */ -+ min_dpb_size += width_in_mb * height_in_mb * num_dpb_buffer * 192; -+ -+ /* IT surface buffer */ -+ min_dpb_size += width_in_mb * height_in_mb * 32; -+ break; -+ -+ case 1: /* VC1 */ -+ -+ /* reference picture buffer */ -+ min_dpb_size = image_size * 3; -+ -+ /* CONTEXT_BUFFER */ -+ min_dpb_size += width_in_mb * height_in_mb * 128; -+ -+ /* IT surface buffer */ -+ min_dpb_size += width_in_mb * 64; -+ -+ /* DB surface buffer */ -+ min_dpb_size += width_in_mb * 128; -+ -+ /* BP */ -+ tmp = max(width_in_mb, height_in_mb); -+ min_dpb_size += ALIGN(tmp * 7 * 16, 64); -+ break; -+ -+ case 3: /* MPEG2 */ -+ -+ /* reference picture buffer */ -+ min_dpb_size = image_size * 3; -+ break; -+ -+ case 4: /* MPEG4 */ -+ -+ /* reference picture buffer */ -+ min_dpb_size = image_size * 3; -+ -+ /* CM */ -+ min_dpb_size += width_in_mb * height_in_mb * 64; -+ -+ /* IT surface buffer */ -+ min_dpb_size += ALIGN(width_in_mb * height_in_mb * 32, 64); -+ break; -+ -+ default: -+ DRM_ERROR("UVD codec not handled %d!\n", stream_type); -+ return -EINVAL; -+ } -+ -+ if (width > pitch) { -+ DRM_ERROR("Invalid UVD decoding target pitch!\n"); -+ return -EINVAL; -+ } -+ -+ if (dpb_size < min_dpb_size) { -+ DRM_ERROR("Invalid dpb_size in UVD message (%d / %d)!\n", -+ dpb_size, min_dpb_size); -+ return -EINVAL; -+ } -+ -+ buf_sizes[0x1] = dpb_size; -+ buf_sizes[0x2] = image_size; -+ return 0; -+} -+ -+/** -+ * amdgpu_uvd_cs_msg - handle UVD message -+ * -+ * @ctx: UVD parser context -+ * @bo: buffer object containing the message -+ * @offset: offset into the buffer object -+ * -+ * Peek into the UVD message and extract the session id. -+ * Make sure that we don't open up to many sessions. -+ */ -+static int amdgpu_uvd_cs_msg(struct amdgpu_uvd_cs_ctx *ctx, -+ struct amdgpu_bo *bo, unsigned offset) -+{ -+ struct amdgpu_device *adev = ctx->parser->adev; -+ int32_t *msg, msg_type, handle; -+ struct fence *f; -+ void *ptr; -+ -+ int i, r; -+ -+ if (offset & 0x3F) { -+ DRM_ERROR("UVD messages must be 64 byte aligned!\n"); -+ return -EINVAL; -+ } -+ -+ f = reservation_object_get_excl(bo->tbo.resv); -+ if (f) { -+ r = amdgpu_fence_wait((struct amdgpu_fence *)f, false); -+ if (r) { -+ DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); -+ return r; -+ } -+ } -+ -+ r = amdgpu_bo_kmap(bo, &ptr); -+ if (r) { -+ DRM_ERROR("Failed mapping the UVD message (%d)!\n", r); -+ return r; -+ } -+ -+ msg = ptr + offset; -+ -+ msg_type = msg[1]; -+ handle = msg[2]; -+ -+ if (handle == 0) { -+ DRM_ERROR("Invalid UVD handle!\n"); -+ return -EINVAL; -+ } -+ -+ if (msg_type == 1) { -+ /* it's a decode msg, calc buffer sizes */ -+ r = amdgpu_uvd_cs_msg_decode(msg, ctx->buf_sizes); -+ amdgpu_bo_kunmap(bo); -+ if (r) -+ return r; -+ -+ } else if (msg_type == 2) { -+ /* it's a destroy msg, free the handle */ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) -+ atomic_cmpxchg(&adev->uvd.handles[i], handle, 0); -+ amdgpu_bo_kunmap(bo); -+ return 0; -+ } else { -+ /* it's a create msg */ -+ amdgpu_bo_kunmap(bo); -+ -+ if (msg_type != 0) { -+ DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); -+ return -EINVAL; -+ } -+ -+ /* it's a create msg, no special handling needed */ -+ } -+ -+ /* create or decode, validate the handle */ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { -+ if (atomic_read(&adev->uvd.handles[i]) == handle) -+ return 0; -+ } -+ -+ /* handle not found try to alloc a new one */ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { -+ if (!atomic_cmpxchg(&adev->uvd.handles[i], 0, handle)) { -+ adev->uvd.filp[i] = ctx->parser->filp; -+ return 0; -+ } -+ } -+ -+ DRM_ERROR("No more free UVD handles!\n"); -+ return -EINVAL; -+} -+ -+/** -+ * amdgpu_uvd_cs_pass2 - second parsing round -+ * -+ * @ctx: UVD parser context -+ * -+ * Patch buffer addresses, make sure buffer sizes are correct. -+ */ -+static int amdgpu_uvd_cs_pass2(struct amdgpu_uvd_cs_ctx *ctx) -+{ -+ struct amdgpu_bo_va_mapping *mapping; -+ struct amdgpu_bo *bo; -+ struct amdgpu_ib *ib; -+ uint32_t cmd, lo, hi; -+ uint64_t start, end; -+ uint64_t addr; -+ int r; -+ -+ lo = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->data0); -+ hi = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->data1); -+ addr = ((uint64_t)lo) | (((uint64_t)hi) << 32); -+ -+ mapping = amdgpu_cs_find_mapping(ctx->parser, addr, &bo); -+ if (mapping == NULL) -+ return -EINVAL; -+ -+ start = amdgpu_bo_gpu_offset(bo); -+ -+ end = (mapping->it.last + 1 - mapping->it.start); -+ end = end * AMDGPU_GPU_PAGE_SIZE + start; -+ -+ addr -= ((uint64_t)mapping->it.start) * AMDGPU_GPU_PAGE_SIZE; -+ start += addr; -+ -+ ib = &ctx->parser->ibs[ctx->ib_idx]; -+ ib->ptr[ctx->data0] = start & 0xFFFFFFFF; -+ ib->ptr[ctx->data1] = start >> 32; -+ -+ cmd = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->idx) >> 1; -+ if (cmd < 0x4) { -+ if ((end - start) < ctx->buf_sizes[cmd]) { -+ DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, -+ (unsigned)(end - start), -+ ctx->buf_sizes[cmd]); -+ return -EINVAL; -+ } -+ -+ } else if ((cmd != 0x100) && (cmd != 0x204)) { -+ DRM_ERROR("invalid UVD command %X!\n", cmd); -+ return -EINVAL; -+ } -+ -+ if (!ctx->parser->adev->uvd.address_64_bit) { -+ if ((start >> 28) != ((end - 1) >> 28)) { -+ DRM_ERROR("reloc %LX-%LX crossing 256MB boundary!\n", -+ start, end); -+ return -EINVAL; -+ } -+ -+ if ((cmd == 0 || cmd == 0x3) && -+ (start >> 28) != (ctx->parser->adev->uvd.gpu_addr >> 28)) { -+ DRM_ERROR("msg/fb buffer %LX-%LX out of 256MB segment!\n", -+ start, end); -+ return -EINVAL; -+ } -+ } -+ -+ if (cmd == 0) { -+ ctx->has_msg_cmd = true; -+ r = amdgpu_uvd_cs_msg(ctx, bo, addr); -+ if (r) -+ return r; -+ } else if (!ctx->has_msg_cmd) { -+ DRM_ERROR("Message needed before other commands are send!\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_uvd_cs_reg - parse register writes -+ * -+ * @ctx: UVD parser context -+ * @cb: callback function -+ * -+ * Parse the register writes, call cb on each complete command. -+ */ -+static int amdgpu_uvd_cs_reg(struct amdgpu_uvd_cs_ctx *ctx, -+ int (*cb)(struct amdgpu_uvd_cs_ctx *ctx)) -+{ -+ struct amdgpu_ib *ib = &ctx->parser->ibs[ctx->ib_idx]; -+ int i, r; -+ -+ ctx->idx++; -+ for (i = 0; i <= ctx->count; ++i) { -+ unsigned reg = ctx->reg + i; -+ -+ if (ctx->idx >= ib->length_dw) { -+ DRM_ERROR("Register command after end of CS!\n"); -+ return -EINVAL; -+ } -+ -+ switch (reg) { -+ case mmUVD_GPCOM_VCPU_DATA0: -+ ctx->data0 = ctx->idx; -+ break; -+ case mmUVD_GPCOM_VCPU_DATA1: -+ ctx->data1 = ctx->idx; -+ break; -+ case mmUVD_GPCOM_VCPU_CMD: -+ r = cb(ctx); -+ if (r) -+ return r; -+ break; -+ case mmUVD_ENGINE_CNTL: -+ break; -+ default: -+ DRM_ERROR("Invalid reg 0x%X!\n", reg); -+ return -EINVAL; -+ } -+ ctx->idx++; -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_uvd_cs_packets - parse UVD packets -+ * -+ * @ctx: UVD parser context -+ * @cb: callback function -+ * -+ * Parse the command stream packets. -+ */ -+static int amdgpu_uvd_cs_packets(struct amdgpu_uvd_cs_ctx *ctx, -+ int (*cb)(struct amdgpu_uvd_cs_ctx *ctx)) -+{ -+ struct amdgpu_ib *ib = &ctx->parser->ibs[ctx->ib_idx]; -+ int r; -+ -+ for (ctx->idx = 0 ; ctx->idx < ib->length_dw; ) { -+ uint32_t cmd = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->idx); -+ unsigned type = CP_PACKET_GET_TYPE(cmd); -+ switch (type) { -+ case PACKET_TYPE0: -+ ctx->reg = CP_PACKET0_GET_REG(cmd); -+ ctx->count = CP_PACKET_GET_COUNT(cmd); -+ r = amdgpu_uvd_cs_reg(ctx, cb); -+ if (r) -+ return r; -+ break; -+ case PACKET_TYPE2: -+ ++ctx->idx; -+ break; -+ default: -+ DRM_ERROR("Unknown packet type %d !\n", type); -+ return -EINVAL; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * amdgpu_uvd_ring_parse_cs - UVD command submission parser -+ * -+ * @parser: Command submission parser context -+ * -+ * Parse the command stream, patch in addresses as necessary. -+ */ -+int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx) -+{ -+ struct amdgpu_uvd_cs_ctx ctx = {}; -+ unsigned buf_sizes[] = { -+ [0x00000000] = 2048, -+ [0x00000001] = 32 * 1024 * 1024, -+ [0x00000002] = 2048 * 1152 * 3, -+ [0x00000003] = 2048, -+ }; -+ struct amdgpu_ib *ib = &parser->ibs[ib_idx]; -+ int r; -+ -+ if (ib->length_dw % 16) { -+ DRM_ERROR("UVD IB length (%d) not 16 dwords aligned!\n", -+ ib->length_dw); -+ return -EINVAL; -+ } -+ -+ ctx.parser = parser; -+ ctx.buf_sizes = buf_sizes; -+ ctx.ib_idx = ib_idx; -+ -+ /* first round, make sure the buffers are actually in the UVD segment */ -+ r = amdgpu_uvd_cs_packets(&ctx, amdgpu_uvd_cs_pass1); -+ if (r) -+ return r; -+ -+ /* second round, patch buffer addresses into the command stream */ -+ r = amdgpu_uvd_cs_packets(&ctx, amdgpu_uvd_cs_pass2); -+ if (r) -+ return r; -+ -+ if (!ctx.has_msg_cmd) { -+ DRM_ERROR("UVD-IBs need a msg command!\n"); -+ return -EINVAL; -+ } -+ -+ amdgpu_uvd_note_usage(ctx.parser->adev); -+ -+ return 0; -+} -+ -+static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, -+ struct amdgpu_bo *bo, -+ struct amdgpu_fence **fence) -+{ -+ struct ttm_validate_buffer tv; -+ struct ww_acquire_ctx ticket; -+ struct list_head head; -+ struct amdgpu_ib ib; -+ uint64_t addr; -+ int i, r; -+ -+ memset(&tv, 0, sizeof(tv)); -+ tv.bo = &bo->tbo; -+ -+ INIT_LIST_HEAD(&head); -+ list_add(&tv.head, &head); -+ -+ r = ttm_eu_reserve_buffers(&ticket, &head, true, NULL); -+ if (r) -+ return r; -+ -+ if (!bo->adev->uvd.address_64_bit) { -+ amdgpu_ttm_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM); -+ amdgpu_uvd_force_into_uvd_segment(bo); -+ } -+ -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); -+ if (r) -+ goto err; -+ -+ r = amdgpu_ib_get(ring, NULL, 64, &ib); -+ if (r) -+ goto err; -+ -+ addr = amdgpu_bo_gpu_offset(bo); -+ ib.ptr[0] = PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0); -+ ib.ptr[1] = addr; -+ ib.ptr[2] = PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0); -+ ib.ptr[3] = addr >> 32; -+ ib.ptr[4] = PACKET0(mmUVD_GPCOM_VCPU_CMD, 0); -+ ib.ptr[5] = 0; -+ for (i = 6; i < 16; ++i) -+ ib.ptr[i] = PACKET2(0); -+ ib.length_dw = 16; -+ -+ r = amdgpu_ib_schedule(ring->adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED); -+ if (r) -+ goto err; -+ ttm_eu_fence_buffer_objects(&ticket, &head, &ib.fence->base); -+ -+ if (fence) -+ *fence = amdgpu_fence_ref(ib.fence); -+ -+ amdgpu_ib_free(ring->adev, &ib); -+ amdgpu_bo_unref(&bo); -+ return 0; -+ -+err: -+ ttm_eu_backoff_reservation(&ticket, &head); -+ return r; -+} -+ -+/* multiple fence commands without any stream commands in between can -+ crash the vcpu so just try to emmit a dummy create/destroy msg to -+ avoid this */ -+int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ struct amdgpu_bo *bo; -+ uint32_t *msg; -+ int r, i; -+ -+ r = amdgpu_bo_create(adev, 1024, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, &bo); -+ if (r) -+ return r; -+ -+ r = amdgpu_bo_reserve(bo, false); -+ if (r) { -+ amdgpu_bo_unref(&bo); -+ return r; -+ } -+ -+ r = amdgpu_bo_kmap(bo, (void **)&msg); -+ if (r) { -+ amdgpu_bo_unreserve(bo); -+ amdgpu_bo_unref(&bo); -+ return r; -+ } -+ -+ /* stitch together an UVD create msg */ -+ msg[0] = cpu_to_le32(0x00000de4); -+ msg[1] = cpu_to_le32(0x00000000); -+ msg[2] = cpu_to_le32(handle); -+ msg[3] = cpu_to_le32(0x00000000); -+ msg[4] = cpu_to_le32(0x00000000); -+ msg[5] = cpu_to_le32(0x00000000); -+ msg[6] = cpu_to_le32(0x00000000); -+ msg[7] = cpu_to_le32(0x00000780); -+ msg[8] = cpu_to_le32(0x00000440); -+ msg[9] = cpu_to_le32(0x00000000); -+ msg[10] = cpu_to_le32(0x01b37000); -+ for (i = 11; i < 1024; ++i) -+ msg[i] = cpu_to_le32(0x0); -+ -+ amdgpu_bo_kunmap(bo); -+ amdgpu_bo_unreserve(bo); -+ -+ return amdgpu_uvd_send_msg(ring, bo, fence); -+} -+ -+int amdgpu_uvd_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ struct amdgpu_bo *bo; -+ uint32_t *msg; -+ int r, i; -+ -+ r = amdgpu_bo_create(adev, 1024, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, &bo); -+ if (r) -+ return r; -+ -+ r = amdgpu_bo_reserve(bo, false); -+ if (r) { -+ amdgpu_bo_unref(&bo); -+ return r; -+ } -+ -+ r = amdgpu_bo_kmap(bo, (void **)&msg); -+ if (r) { -+ amdgpu_bo_unreserve(bo); -+ amdgpu_bo_unref(&bo); -+ return r; -+ } -+ -+ /* stitch together an UVD destroy msg */ -+ msg[0] = cpu_to_le32(0x00000de4); -+ msg[1] = cpu_to_le32(0x00000002); -+ msg[2] = cpu_to_le32(handle); -+ msg[3] = cpu_to_le32(0x00000000); -+ for (i = 4; i < 1024; ++i) -+ msg[i] = cpu_to_le32(0x0); -+ -+ amdgpu_bo_kunmap(bo); -+ amdgpu_bo_unreserve(bo); -+ -+ return amdgpu_uvd_send_msg(ring, bo, fence); -+} -+ -+static void amdgpu_uvd_idle_work_handler(struct work_struct *work) -+{ -+ struct amdgpu_device *adev = -+ container_of(work, struct amdgpu_device, uvd.idle_work.work); -+ unsigned i, fences, handles = 0; -+ -+ fences = amdgpu_fence_count_emitted(&adev->uvd.ring); -+ -+ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) -+ if (atomic_read(&adev->uvd.handles[i])) -+ ++handles; -+ -+ if (fences == 0 && handles == 0) { -+ if (adev->pm.dpm_enabled) { -+ amdgpu_dpm_enable_uvd(adev, false); -+ } else { -+ amdgpu_asic_set_uvd_clocks(adev, 0, 0); -+ } -+ } else { -+ schedule_delayed_work(&adev->uvd.idle_work, -+ msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); -+ } -+} -+ -+static void amdgpu_uvd_note_usage(struct amdgpu_device *adev) -+{ -+ bool set_clocks = !cancel_delayed_work_sync(&adev->uvd.idle_work); -+ set_clocks &= schedule_delayed_work(&adev->uvd.idle_work, -+ msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); -+ -+ if (set_clocks) { -+ if (adev->pm.dpm_enabled) { -+ amdgpu_dpm_enable_uvd(adev, true); -+ } else { -+ amdgpu_asic_set_uvd_clocks(adev, 53300, 40000); -+ } -+ } -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h -new file mode 100644 -index 0000000..2255aa7 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h -@@ -0,0 +1,39 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_UVD_H__ -+#define __AMDGPU_UVD_H__ -+ -+int amdgpu_uvd_sw_init(struct amdgpu_device *adev); -+int amdgpu_uvd_sw_fini(struct amdgpu_device *adev); -+int amdgpu_uvd_suspend(struct amdgpu_device *adev); -+int amdgpu_uvd_resume(struct amdgpu_device *adev); -+int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence); -+int amdgpu_uvd_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence); -+void amdgpu_uvd_free_handles(struct amdgpu_device *adev, -+ struct drm_file *filp); -+int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c -new file mode 100644 -index 0000000..c65d93c ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c -@@ -0,0 +1,727 @@ -+/* -+ * Copyright 2013 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, sub license, and/or sell copies of the Software, and to -+ * permit persons to whom the Software is furnished to do so, subject to -+ * the following conditions: -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDERS, AUTHORS 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. -+ * -+ * The above copyright notice and this permission notice (including the -+ * next paragraph) shall be included in all copies or substantial portions -+ * of the Software. -+ * -+ * Authors: Christian König <christian.koenig@amd.com> -+ */ -+ -+#include <linux/firmware.h> -+#include <linux/module.h> -+#include <drm/drmP.h> -+#include <drm/drm.h> -+ -+#include "amdgpu.h" -+#include "amdgpu_pm.h" -+#include "amdgpu_vce.h" -+#include "cikd.h" -+ -+/* 1 second timeout */ -+#define VCE_IDLE_TIMEOUT_MS 1000 -+ -+/* Firmware Names */ -+#ifdef CONFIG_DRM_AMDGPU_CIK -+#define FIRMWARE_BONAIRE "radeon/bonaire_vce.bin" -+#define FIRMWARE_KABINI "radeon/kabini_vce.bin" -+#define FIRMWARE_KAVERI "radeon/kaveri_vce.bin" -+#define FIRMWARE_HAWAII "radeon/hawaii_vce.bin" -+#define FIRMWARE_MULLINS "radeon/mullins_vce.bin" -+#endif -+#define FIRMWARE_TONGA "radeon/tonga_vce.bin" -+#define FIRMWARE_CARRIZO "radeon/carrizo_vce.bin" -+ -+#ifdef CONFIG_DRM_AMDGPU_CIK -+MODULE_FIRMWARE(FIRMWARE_BONAIRE); -+MODULE_FIRMWARE(FIRMWARE_KABINI); -+MODULE_FIRMWARE(FIRMWARE_KAVERI); -+MODULE_FIRMWARE(FIRMWARE_HAWAII); -+MODULE_FIRMWARE(FIRMWARE_MULLINS); -+#endif -+MODULE_FIRMWARE(FIRMWARE_TONGA); -+MODULE_FIRMWARE(FIRMWARE_CARRIZO); -+ -+static void amdgpu_vce_idle_work_handler(struct work_struct *work); -+ -+/** -+ * amdgpu_vce_init - allocate memory, load vce firmware -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * First step to get VCE online, allocate memory and load the firmware -+ */ -+int amdgpu_vce_sw_init(struct amdgpu_device *adev) -+{ -+ unsigned long size; -+ const char *fw_name; -+ const struct common_firmware_header *hdr; -+ unsigned ucode_version, version_major, version_minor, binary_id; -+ int i, r; -+ -+ INIT_DELAYED_WORK(&adev->vce.idle_work, amdgpu_vce_idle_work_handler); -+ -+ switch (adev->asic_type) { -+#ifdef CONFIG_DRM_AMDGPU_CIK -+ case CHIP_BONAIRE: -+ fw_name = FIRMWARE_BONAIRE; -+ break; -+ case CHIP_KAVERI: -+ fw_name = FIRMWARE_KAVERI; -+ break; -+ case CHIP_KABINI: -+ fw_name = FIRMWARE_KABINI; -+ break; -+ case CHIP_HAWAII: -+ fw_name = FIRMWARE_HAWAII; -+ break; -+ case CHIP_MULLINS: -+ fw_name = FIRMWARE_MULLINS; -+ break; -+#endif -+ case CHIP_TONGA: -+ fw_name = FIRMWARE_TONGA; -+ break; -+ case CHIP_CARRIZO: -+ fw_name = FIRMWARE_CARRIZO; -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ r = request_firmware(&adev->vce.fw, fw_name, adev->dev); -+ if (r) { -+ dev_err(adev->dev, "amdgpu_vce: Can't load firmware \"%s\"\n", -+ fw_name); -+ return r; -+ } -+ -+ r = amdgpu_ucode_validate(adev->vce.fw); -+ if (r) { -+ dev_err(adev->dev, "amdgpu_vce: Can't validate firmware \"%s\"\n", -+ fw_name); -+ release_firmware(adev->vce.fw); -+ adev->vce.fw = NULL; -+ return r; -+ } -+ -+ hdr = (const struct common_firmware_header *)adev->vce.fw->data; -+ -+ ucode_version = le32_to_cpu(hdr->ucode_version); -+ version_major = (ucode_version >> 20) & 0xfff; -+ version_minor = (ucode_version >> 8) & 0xfff; -+ binary_id = ucode_version & 0xff; -+ DRM_INFO("Found VCE firmware Version: %hhd.%hhd Binary ID: %hhd\n", -+ version_major, version_minor, binary_id); -+ adev->vce.fw_version = ((version_major << 24) | (version_minor << 16) | -+ (binary_id << 8)); -+ -+ /* allocate firmware, stack and heap BO */ -+ -+ size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes)) + -+ AMDGPU_VCE_STACK_SIZE + AMDGPU_VCE_HEAP_SIZE; -+ r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, &adev->vce.vcpu_bo); -+ if (r) { -+ dev_err(adev->dev, "(%d) failed to allocate VCE bo\n", r); -+ return r; -+ } -+ -+ r = amdgpu_bo_reserve(adev->vce.vcpu_bo, false); -+ if (r) { -+ amdgpu_bo_unref(&adev->vce.vcpu_bo); -+ dev_err(adev->dev, "(%d) failed to reserve VCE bo\n", r); -+ return r; -+ } -+ -+ r = amdgpu_bo_pin(adev->vce.vcpu_bo, AMDGPU_GEM_DOMAIN_VRAM, -+ &adev->vce.gpu_addr); -+ amdgpu_bo_unreserve(adev->vce.vcpu_bo); -+ if (r) { -+ amdgpu_bo_unref(&adev->vce.vcpu_bo); -+ dev_err(adev->dev, "(%d) VCE bo pin failed\n", r); -+ return r; -+ } -+ -+ for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) { -+ atomic_set(&adev->vce.handles[i], 0); -+ adev->vce.filp[i] = NULL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vce_fini - free memory -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Last step on VCE teardown, free firmware memory -+ */ -+int amdgpu_vce_sw_fini(struct amdgpu_device *adev) -+{ -+ if (adev->vce.vcpu_bo == NULL) -+ return 0; -+ -+ amdgpu_bo_unref(&adev->vce.vcpu_bo); -+ -+ amdgpu_ring_fini(&adev->vce.ring[0]); -+ amdgpu_ring_fini(&adev->vce.ring[1]); -+ -+ release_firmware(adev->vce.fw); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vce_suspend - unpin VCE fw memory -+ * -+ * @adev: amdgpu_device pointer -+ * -+ */ -+int amdgpu_vce_suspend(struct amdgpu_device *adev) -+{ -+ int i; -+ -+ if (adev->vce.vcpu_bo == NULL) -+ return 0; -+ -+ for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) -+ if (atomic_read(&adev->vce.handles[i])) -+ break; -+ -+ if (i == AMDGPU_MAX_VCE_HANDLES) -+ return 0; -+ -+ /* TODO: suspending running encoding sessions isn't supported */ -+ return -EINVAL; -+} -+ -+/** -+ * amdgpu_vce_resume - pin VCE fw memory -+ * -+ * @adev: amdgpu_device pointer -+ * -+ */ -+int amdgpu_vce_resume(struct amdgpu_device *adev) -+{ -+ void *cpu_addr; -+ const struct common_firmware_header *hdr; -+ unsigned offset; -+ int r; -+ -+ if (adev->vce.vcpu_bo == NULL) -+ return -EINVAL; -+ -+ r = amdgpu_bo_reserve(adev->vce.vcpu_bo, false); -+ if (r) { -+ dev_err(adev->dev, "(%d) failed to reserve VCE bo\n", r); -+ return r; -+ } -+ -+ r = amdgpu_bo_kmap(adev->vce.vcpu_bo, &cpu_addr); -+ if (r) { -+ amdgpu_bo_unreserve(adev->vce.vcpu_bo); -+ dev_err(adev->dev, "(%d) VCE map failed\n", r); -+ return r; -+ } -+ -+ hdr = (const struct common_firmware_header *)adev->vce.fw->data; -+ offset = le32_to_cpu(hdr->ucode_array_offset_bytes); -+ memcpy(cpu_addr, (adev->vce.fw->data) + offset, -+ (adev->vce.fw->size) - offset); -+ -+ amdgpu_bo_kunmap(adev->vce.vcpu_bo); -+ -+ amdgpu_bo_unreserve(adev->vce.vcpu_bo); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vce_idle_work_handler - power off VCE -+ * -+ * @work: pointer to work structure -+ * -+ * power of VCE when it's not used any more -+ */ -+static void amdgpu_vce_idle_work_handler(struct work_struct *work) -+{ -+ struct amdgpu_device *adev = -+ container_of(work, struct amdgpu_device, vce.idle_work.work); -+ -+ if ((amdgpu_fence_count_emitted(&adev->vce.ring[0]) == 0) && -+ (amdgpu_fence_count_emitted(&adev->vce.ring[1]) == 0)) { -+ if (adev->pm.dpm_enabled) { -+ amdgpu_dpm_enable_vce(adev, false); -+ } else { -+ amdgpu_asic_set_vce_clocks(adev, 0, 0); -+ } -+ } else { -+ schedule_delayed_work(&adev->vce.idle_work, -+ msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); -+ } -+} -+ -+/** -+ * amdgpu_vce_note_usage - power up VCE -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Make sure VCE is powerd up when we want to use it -+ */ -+static void amdgpu_vce_note_usage(struct amdgpu_device *adev) -+{ -+ bool streams_changed = false; -+ bool set_clocks = !cancel_delayed_work_sync(&adev->vce.idle_work); -+ set_clocks &= schedule_delayed_work(&adev->vce.idle_work, -+ msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); -+ -+ if (adev->pm.dpm_enabled) { -+ /* XXX figure out if the streams changed */ -+ streams_changed = false; -+ } -+ -+ if (set_clocks || streams_changed) { -+ if (adev->pm.dpm_enabled) { -+ amdgpu_dpm_enable_vce(adev, true); -+ } else { -+ amdgpu_asic_set_vce_clocks(adev, 53300, 40000); -+ } -+ } -+} -+ -+/** -+ * amdgpu_vce_free_handles - free still open VCE handles -+ * -+ * @adev: amdgpu_device pointer -+ * @filp: drm file pointer -+ * -+ * Close all VCE handles still open by this file pointer -+ */ -+void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp) -+{ -+ struct amdgpu_ring *ring = &adev->vce.ring[0]; -+ int i, r; -+ for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) { -+ uint32_t handle = atomic_read(&adev->vce.handles[i]); -+ if (!handle || adev->vce.filp[i] != filp) -+ continue; -+ -+ amdgpu_vce_note_usage(adev); -+ -+ r = amdgpu_vce_get_destroy_msg(ring, handle, NULL); -+ if (r) -+ DRM_ERROR("Error destroying VCE handle (%d)!\n", r); -+ -+ adev->vce.filp[i] = NULL; -+ atomic_set(&adev->vce.handles[i], 0); -+ } -+} -+ -+/** -+ * amdgpu_vce_get_create_msg - generate a VCE create msg -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: ring we should submit the msg to -+ * @handle: VCE session handle to use -+ * @fence: optional fence to return -+ * -+ * Open up a stream for HW test -+ */ -+int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence) -+{ -+ const unsigned ib_size_dw = 1024; -+ struct amdgpu_ib ib; -+ uint64_t dummy; -+ int i, r; -+ -+ r = amdgpu_ib_get(ring, NULL, ib_size_dw * 4, &ib); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to get ib (%d).\n", r); -+ return r; -+ } -+ -+ dummy = ib.gpu_addr + 1024; -+ -+ /* stitch together an VCE create msg */ -+ ib.length_dw = 0; -+ ib.ptr[ib.length_dw++] = 0x0000000c; /* len */ -+ ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */ -+ ib.ptr[ib.length_dw++] = handle; -+ -+ ib.ptr[ib.length_dw++] = 0x00000030; /* len */ -+ ib.ptr[ib.length_dw++] = 0x01000001; /* create cmd */ -+ ib.ptr[ib.length_dw++] = 0x00000000; -+ ib.ptr[ib.length_dw++] = 0x00000042; -+ ib.ptr[ib.length_dw++] = 0x0000000a; -+ ib.ptr[ib.length_dw++] = 0x00000001; -+ ib.ptr[ib.length_dw++] = 0x00000080; -+ ib.ptr[ib.length_dw++] = 0x00000060; -+ ib.ptr[ib.length_dw++] = 0x00000100; -+ ib.ptr[ib.length_dw++] = 0x00000100; -+ ib.ptr[ib.length_dw++] = 0x0000000c; -+ ib.ptr[ib.length_dw++] = 0x00000000; -+ -+ ib.ptr[ib.length_dw++] = 0x00000014; /* len */ -+ ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */ -+ ib.ptr[ib.length_dw++] = upper_32_bits(dummy); -+ ib.ptr[ib.length_dw++] = dummy; -+ ib.ptr[ib.length_dw++] = 0x00000001; -+ -+ for (i = ib.length_dw; i < ib_size_dw; ++i) -+ ib.ptr[i] = 0x0; -+ -+ r = amdgpu_ib_schedule(ring->adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r); -+ } -+ -+ if (fence) -+ *fence = amdgpu_fence_ref(ib.fence); -+ -+ amdgpu_ib_free(ring->adev, &ib); -+ -+ return r; -+} -+ -+/** -+ * amdgpu_vce_get_destroy_msg - generate a VCE destroy msg -+ * -+ * @adev: amdgpu_device pointer -+ * @ring: ring we should submit the msg to -+ * @handle: VCE session handle to use -+ * @fence: optional fence to return -+ * -+ * Close up a stream for HW test or if userspace failed to do so -+ */ -+int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence) -+{ -+ const unsigned ib_size_dw = 1024; -+ struct amdgpu_ib ib; -+ uint64_t dummy; -+ int i, r; -+ -+ r = amdgpu_ib_get(ring, NULL, ib_size_dw * 4, &ib); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to get ib (%d).\n", r); -+ return r; -+ } -+ -+ dummy = ib.gpu_addr + 1024; -+ -+ /* stitch together an VCE destroy msg */ -+ ib.length_dw = 0; -+ ib.ptr[ib.length_dw++] = 0x0000000c; /* len */ -+ ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */ -+ ib.ptr[ib.length_dw++] = handle; -+ -+ ib.ptr[ib.length_dw++] = 0x00000014; /* len */ -+ ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */ -+ ib.ptr[ib.length_dw++] = upper_32_bits(dummy); -+ ib.ptr[ib.length_dw++] = dummy; -+ ib.ptr[ib.length_dw++] = 0x00000001; -+ -+ ib.ptr[ib.length_dw++] = 0x00000008; /* len */ -+ ib.ptr[ib.length_dw++] = 0x02000001; /* destroy cmd */ -+ -+ for (i = ib.length_dw; i < ib_size_dw; ++i) -+ ib.ptr[i] = 0x0; -+ -+ r = amdgpu_ib_schedule(ring->adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r); -+ } -+ -+ if (fence) -+ *fence = amdgpu_fence_ref(ib.fence); -+ -+ amdgpu_ib_free(ring->adev, &ib); -+ -+ return r; -+} -+ -+/** -+ * amdgpu_vce_cs_reloc - command submission relocation -+ * -+ * @p: parser context -+ * @lo: address of lower dword -+ * @hi: address of higher dword -+ * -+ * Patch relocation inside command stream with real buffer address -+ */ -+int amdgpu_vce_cs_reloc(struct amdgpu_cs_parser *p, uint32_t ib_idx, int lo, int hi) -+{ -+ struct amdgpu_bo_va_mapping *mapping; -+ struct amdgpu_ib *ib = &p->ibs[ib_idx]; -+ struct amdgpu_bo *bo; -+ uint64_t addr; -+ -+ addr = ((uint64_t)amdgpu_get_ib_value(p, ib_idx, lo)) | -+ ((uint64_t)amdgpu_get_ib_value(p, ib_idx, hi)) << 32; -+ -+ mapping = amdgpu_cs_find_mapping(p, addr, &bo); -+ if (mapping == NULL) { -+ DRM_ERROR("Can't find BO for addr 0x%010Lx %d %d\n", -+ addr, lo, hi); -+ return -EINVAL; -+ } -+ -+ addr -= ((uint64_t)mapping->it.start) * AMDGPU_GPU_PAGE_SIZE; -+ addr += amdgpu_bo_gpu_offset(bo); -+ -+ ib->ptr[lo] = addr & 0xFFFFFFFF; -+ ib->ptr[hi] = addr >> 32; -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vce_cs_parse - parse and validate the command stream -+ * -+ * @p: parser context -+ * -+ */ -+int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx) -+{ -+ uint32_t handle = 0; -+ bool destroy = false; -+ int i, r, idx = 0; -+ struct amdgpu_ib *ib = &p->ibs[ib_idx]; -+ -+ amdgpu_vce_note_usage(p->adev); -+ -+ while (idx < ib->length_dw) { -+ uint32_t len = amdgpu_get_ib_value(p, ib_idx, idx); -+ uint32_t cmd = amdgpu_get_ib_value(p, ib_idx, idx + 1); -+ -+ if ((len < 8) || (len & 3)) { -+ DRM_ERROR("invalid VCE command length (%d)!\n", len); -+ return -EINVAL; -+ } -+ -+ switch (cmd) { -+ case 0x00000001: // session -+ handle = amdgpu_get_ib_value(p, ib_idx, idx + 2); -+ break; -+ -+ case 0x00000002: // task info -+ case 0x01000001: // create -+ case 0x04000001: // config extension -+ case 0x04000002: // pic control -+ case 0x04000005: // rate control -+ case 0x04000007: // motion estimation -+ case 0x04000008: // rdo -+ case 0x04000009: // vui -+ case 0x05000002: // auxiliary buffer -+ break; -+ -+ case 0x03000001: // encode -+ r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 10, idx + 9); -+ if (r) -+ return r; -+ -+ r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 12, idx + 11); -+ if (r) -+ return r; -+ break; -+ -+ case 0x02000001: // destroy -+ destroy = true; -+ break; -+ -+ case 0x05000001: // context buffer -+ case 0x05000004: // video bitstream buffer -+ case 0x05000005: // feedback buffer -+ r = amdgpu_vce_cs_reloc(p, ib_idx, idx + 3, idx + 2); -+ if (r) -+ return r; -+ break; -+ -+ default: -+ DRM_ERROR("invalid VCE command (0x%x)!\n", cmd); -+ return -EINVAL; -+ } -+ -+ idx += len / 4; -+ } -+ -+ if (destroy) { -+ /* IB contains a destroy msg, free the handle */ -+ for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) -+ atomic_cmpxchg(&p->adev->vce.handles[i], handle, 0); -+ -+ return 0; -+ } -+ -+ /* create or encode, validate the handle */ -+ for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) { -+ if (atomic_read(&p->adev->vce.handles[i]) == handle) -+ return 0; -+ } -+ -+ /* handle not found try to alloc a new one */ -+ for (i = 0; i < AMDGPU_MAX_VCE_HANDLES; ++i) { -+ if (!atomic_cmpxchg(&p->adev->vce.handles[i], 0, handle)) { -+ p->adev->vce.filp[i] = p->filp; -+ return 0; -+ } -+ } -+ -+ DRM_ERROR("No more free VCE handles!\n"); -+ -+ return -EINVAL; -+} -+ -+/** -+ * amdgpu_vce_ring_emit_semaphore - emit a semaphore command -+ * -+ * @ring: engine to use -+ * @semaphore: address of semaphore -+ * @emit_wait: true=emit wait, false=emit signal -+ * -+ */ -+bool amdgpu_vce_ring_emit_semaphore(struct amdgpu_ring *ring, -+ struct amdgpu_semaphore *semaphore, -+ bool emit_wait) -+{ -+ uint64_t addr = semaphore->gpu_addr; -+ -+ amdgpu_ring_write(ring, VCE_CMD_SEMAPHORE); -+ amdgpu_ring_write(ring, (addr >> 3) & 0x000FFFFF); -+ amdgpu_ring_write(ring, (addr >> 23) & 0x000FFFFF); -+ amdgpu_ring_write(ring, 0x01003000 | (emit_wait ? 1 : 0)); -+ if (!emit_wait) -+ amdgpu_ring_write(ring, VCE_CMD_END); -+ -+ return true; -+} -+ -+/** -+ * amdgpu_vce_ring_emit_ib - execute indirect buffer -+ * -+ * @ring: engine to use -+ * @ib: the IB to execute -+ * -+ */ -+void amdgpu_vce_ring_emit_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib) -+{ -+ amdgpu_ring_write(ring, VCE_CMD_IB); -+ amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr)); -+ amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr)); -+ amdgpu_ring_write(ring, ib->length_dw); -+} -+ -+/** -+ * amdgpu_vce_ring_emit_fence - add a fence command to the ring -+ * -+ * @ring: engine to use -+ * @fence: the fence -+ * -+ */ -+void amdgpu_vce_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq, -+ bool write64bits) -+{ -+ WARN_ON(write64bits); -+ -+ amdgpu_ring_write(ring, VCE_CMD_FENCE); -+ amdgpu_ring_write(ring, addr); -+ amdgpu_ring_write(ring, upper_32_bits(addr)); -+ amdgpu_ring_write(ring, seq); -+ amdgpu_ring_write(ring, VCE_CMD_TRAP); -+ amdgpu_ring_write(ring, VCE_CMD_END); -+} -+ -+/** -+ * amdgpu_vce_ring_test_ring - test if VCE ring is working -+ * -+ * @ring: the engine to test on -+ * -+ */ -+int amdgpu_vce_ring_test_ring(struct amdgpu_ring *ring) -+{ -+ struct amdgpu_device *adev = ring->adev; -+ uint32_t rptr = amdgpu_ring_get_rptr(ring); -+ unsigned i; -+ int r; -+ -+ r = amdgpu_ring_lock(ring, 16); -+ if (r) { -+ DRM_ERROR("amdgpu: vce failed to lock ring %d (%d).\n", -+ ring->idx, r); -+ return r; -+ } -+ amdgpu_ring_write(ring, VCE_CMD_END); -+ amdgpu_ring_unlock_commit(ring); -+ -+ for (i = 0; i < adev->usec_timeout; i++) { -+ if (amdgpu_ring_get_rptr(ring) != rptr) -+ break; -+ DRM_UDELAY(1); -+ } -+ -+ if (i < adev->usec_timeout) { -+ DRM_INFO("ring test on %d succeeded in %d usecs\n", -+ ring->idx, i); -+ } else { -+ DRM_ERROR("amdgpu: ring %d test failed\n", -+ ring->idx); -+ r = -ETIMEDOUT; -+ } -+ -+ return r; -+} -+ -+/** -+ * amdgpu_vce_ring_test_ib - test if VCE IBs are working -+ * -+ * @ring: the engine to test on -+ * -+ */ -+int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring) -+{ -+ struct amdgpu_fence *fence = NULL; -+ int r; -+ -+ r = amdgpu_vce_get_create_msg(ring, 1, NULL); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to get create msg (%d).\n", r); -+ goto error; -+ } -+ -+ r = amdgpu_vce_get_destroy_msg(ring, 1, &fence); -+ if (r) { -+ DRM_ERROR("amdgpu: failed to get destroy ib (%d).\n", r); -+ goto error; -+ } -+ -+ r = amdgpu_fence_wait(fence, false); -+ if (r) { -+ DRM_ERROR("amdgpu: fence wait failed (%d).\n", r); -+ } else { -+ DRM_INFO("ib test on ring %d succeeded\n", ring->idx); -+ } -+error: -+ amdgpu_fence_unref(&fence); -+ return r; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h -new file mode 100644 -index 0000000..b9411e4 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h -@@ -0,0 +1,47 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __AMDGPU_VCE_H__ -+#define __AMDGPU_VCE_H__ -+ -+int amdgpu_vce_sw_init(struct amdgpu_device *adev); -+int amdgpu_vce_sw_fini(struct amdgpu_device *adev); -+int amdgpu_vce_suspend(struct amdgpu_device *adev); -+int amdgpu_vce_resume(struct amdgpu_device *adev); -+int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence); -+int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, -+ struct amdgpu_fence **fence); -+void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp); -+int amdgpu_vce_cs_reloc(struct amdgpu_cs_parser *p, uint32_t ib_idx, int lo, int hi); -+int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx); -+bool amdgpu_vce_ring_emit_semaphore(struct amdgpu_ring *ring, -+ struct amdgpu_semaphore *semaphore, -+ bool emit_wait); -+void amdgpu_vce_ring_emit_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib); -+void amdgpu_vce_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq, -+ bool write64bit); -+int amdgpu_vce_ring_test_ring(struct amdgpu_ring *ring); -+int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c -new file mode 100644 -index 0000000..1cc01fb ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c -@@ -0,0 +1,1248 @@ -+/* -+ * Copyright 2008 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * Copyright 2009 Jerome Glisse. -+ * -+ * 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_trace.h" -+ -+/* -+ * GPUVM -+ * GPUVM is similar to the legacy gart on older asics, however -+ * rather than there being a single global gart table -+ * for the entire GPU, there are multiple VM page tables active -+ * at any given time. The VM page tables can contain a mix -+ * vram pages and system memory pages and system memory pages -+ * can be mapped as snooped (cached system pages) or unsnooped -+ * (uncached system pages). -+ * Each VM has an ID associated with it and there is a page table -+ * associated with each VMID. When execting a command buffer, -+ * the kernel tells the the ring what VMID to use for that command -+ * buffer. VMIDs are allocated dynamically as commands are submitted. -+ * The userspace drivers maintain their own address space and the kernel -+ * sets up their pages tables accordingly when they submit their -+ * command buffers and a VMID is assigned. -+ * Cayman/Trinity support up to 8 active VMs at any given time; -+ * SI supports 16. -+ */ -+ -+/** -+ * amdgpu_vm_num_pde - return the number of page directory entries -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Calculate the number of page directory entries (cayman+). -+ */ -+static unsigned amdgpu_vm_num_pdes(struct amdgpu_device *adev) -+{ -+ return adev->vm_manager.max_pfn >> amdgpu_vm_block_size; -+} -+ -+/** -+ * amdgpu_vm_directory_size - returns the size of the page directory in bytes -+ * -+ * @adev: amdgpu_device pointer -+ * -+ * Calculate the size of the page directory in bytes (cayman+). -+ */ -+static unsigned amdgpu_vm_directory_size(struct amdgpu_device *adev) -+{ -+ return AMDGPU_GPU_PAGE_ALIGN(amdgpu_vm_num_pdes(adev) * 8); -+} -+ -+/** -+ * amdgpu_vm_get_bos - add the vm BOs to a validation list -+ * -+ * @vm: vm providing the BOs -+ * @head: head of validation list -+ * -+ * Add the page directory to the list of BOs to -+ * validate for command submission (cayman+). -+ */ -+struct amdgpu_bo_list_entry *amdgpu_vm_get_bos(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm, -+ struct list_head *head) -+{ -+ struct amdgpu_bo_list_entry *list; -+ unsigned i, idx; -+ -+ list = drm_malloc_ab(vm->max_pde_used + 2, -+ sizeof(struct amdgpu_bo_list_entry)); -+ if (!list) -+ return NULL; -+ -+ /* add the vm page table to the list */ -+ list[0].robj = vm->page_directory; -+ list[0].prefered_domains = AMDGPU_GEM_DOMAIN_VRAM; -+ list[0].allowed_domains = AMDGPU_GEM_DOMAIN_VRAM; -+ list[0].priority = 0; -+ list[0].tv.bo = &vm->page_directory->tbo; -+ list[0].tv.shared = true; -+ list_add(&list[0].tv.head, head); -+ -+ for (i = 0, idx = 1; i <= vm->max_pde_used; i++) { -+ if (!vm->page_tables[i].bo) -+ continue; -+ -+ list[idx].robj = vm->page_tables[i].bo; -+ list[idx].prefered_domains = AMDGPU_GEM_DOMAIN_VRAM; -+ list[idx].allowed_domains = AMDGPU_GEM_DOMAIN_VRAM; -+ list[idx].priority = 0; -+ list[idx].tv.bo = &list[idx].robj->tbo; -+ list[idx].tv.shared = true; -+ list_add(&list[idx++].tv.head, head); -+ } -+ -+ return list; -+} -+ -+/** -+ * amdgpu_vm_grab_id - allocate the next free VMID -+ * -+ * @ring: ring we want to submit job to -+ * @vm: vm to allocate id for -+ * -+ * Allocate an id for the vm (cayman+). -+ * Returns the fence we need to sync to (if any). -+ * -+ * Global and local mutex must be locked! -+ */ -+struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring, -+ struct amdgpu_vm *vm) -+{ -+ struct amdgpu_fence *best[AMDGPU_MAX_RINGS] = {}; -+ struct amdgpu_vm_id *vm_id = &vm->ids[ring->idx]; -+ struct amdgpu_device *adev = ring->adev; -+ -+ unsigned choices[2] = {}; -+ unsigned i; -+ -+ /* check if the id is still valid */ -+ if (vm_id->id && vm_id->last_id_use && -+ vm_id->last_id_use == adev->vm_manager.active[vm_id->id]) -+ return NULL; -+ -+ /* we definately need to flush */ -+ vm_id->pd_gpu_addr = ~0ll; -+ -+ /* skip over VMID 0, since it is the system VM */ -+ for (i = 1; i < adev->vm_manager.nvm; ++i) { -+ struct amdgpu_fence *fence = adev->vm_manager.active[i]; -+ -+ if (fence == NULL) { -+ /* found a free one */ -+ vm_id->id = i; -+ trace_amdgpu_vm_grab_id(i, ring->idx); -+ return NULL; -+ } -+ -+ if (amdgpu_fence_is_earlier(fence, best[fence->ring->idx])) { -+ best[fence->ring->idx] = fence; -+ choices[fence->ring == ring ? 0 : 1] = i; -+ } -+ } -+ -+ for (i = 0; i < 2; ++i) { -+ if (choices[i]) { -+ vm_id->id = choices[i]; -+ trace_amdgpu_vm_grab_id(choices[i], ring->idx); -+ return adev->vm_manager.active[choices[i]]; -+ } -+ } -+ -+ /* should never happen */ -+ BUG(); -+ return NULL; -+} -+ -+/** -+ * amdgpu_vm_flush - hardware flush the vm -+ * -+ * @ring: ring to use for flush -+ * @vm: vm we want to flush -+ * @updates: last vm update that we waited for -+ * -+ * Flush the vm (cayman+). -+ * -+ * Global and local mutex must be locked! -+ */ -+void amdgpu_vm_flush(struct amdgpu_ring *ring, -+ struct amdgpu_vm *vm, -+ struct amdgpu_fence *updates) -+{ -+ uint64_t pd_addr = amdgpu_bo_gpu_offset(vm->page_directory); -+ struct amdgpu_vm_id *vm_id = &vm->ids[ring->idx]; -+ -+ if (pd_addr != vm_id->pd_gpu_addr || !vm_id->flushed_updates || -+ amdgpu_fence_is_earlier(vm_id->flushed_updates, updates)) { -+ -+ trace_amdgpu_vm_flush(pd_addr, ring->idx, vm_id->id); -+ amdgpu_fence_unref(&vm_id->flushed_updates); -+ vm_id->flushed_updates = amdgpu_fence_ref(updates); -+ vm_id->pd_gpu_addr = pd_addr; -+ amdgpu_ring_emit_vm_flush(ring, vm_id->id, vm_id->pd_gpu_addr); -+ } -+} -+ -+/** -+ * amdgpu_vm_fence - remember fence for vm -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: vm we want to fence -+ * @fence: fence to remember -+ * -+ * Fence the vm (cayman+). -+ * Set the fence used to protect page table and id. -+ * -+ * Global and local mutex must be locked! -+ */ -+void amdgpu_vm_fence(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm, -+ struct amdgpu_fence *fence) -+{ -+ unsigned ridx = fence->ring->idx; -+ unsigned vm_id = vm->ids[ridx].id; -+ -+ amdgpu_fence_unref(&adev->vm_manager.active[vm_id]); -+ adev->vm_manager.active[vm_id] = amdgpu_fence_ref(fence); -+ -+ amdgpu_fence_unref(&vm->ids[ridx].last_id_use); -+ vm->ids[ridx].last_id_use = amdgpu_fence_ref(fence); -+} -+ -+/** -+ * amdgpu_vm_bo_find - find the bo_va for a specific vm & bo -+ * -+ * @vm: requested vm -+ * @bo: requested buffer object -+ * -+ * Find @bo inside the requested vm (cayman+). -+ * Search inside the @bos vm list for the requested vm -+ * Returns the found bo_va or NULL if none is found -+ * -+ * Object has to be reserved! -+ */ -+struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm, -+ struct amdgpu_bo *bo) -+{ -+ struct amdgpu_bo_va *bo_va; -+ -+ list_for_each_entry(bo_va, &bo->va, bo_list) { -+ if (bo_va->vm == vm) { -+ return bo_va; -+ } -+ } -+ return NULL; -+} -+ -+/** -+ * amdgpu_vm_update_pages - helper to call the right asic function -+ * -+ * @adev: amdgpu_device pointer -+ * @ib: indirect buffer to fill with commands -+ * @pe: addr of the page entry -+ * @addr: dst addr to write into pe -+ * @count: number of page entries to update -+ * @incr: increase next addr by incr bytes -+ * @flags: hw access flags -+ * @gtt_flags: GTT hw access flags -+ * -+ * Traces the parameters and calls the right asic functions -+ * to setup the page table using the DMA. -+ */ -+static void amdgpu_vm_update_pages(struct amdgpu_device *adev, -+ struct amdgpu_ib *ib, -+ uint64_t pe, uint64_t addr, -+ unsigned count, uint32_t incr, -+ uint32_t flags, uint32_t gtt_flags) -+{ -+ trace_amdgpu_vm_set_page(pe, addr, count, incr, flags); -+ -+ if ((flags & AMDGPU_PTE_SYSTEM) && (flags == gtt_flags)) { -+ uint64_t src = adev->gart.table_addr + (addr >> 12) * 8; -+ amdgpu_vm_copy_pte(adev, ib, pe, src, count); -+ -+ } else if ((flags & AMDGPU_PTE_SYSTEM) || (count < 3)) { -+ amdgpu_vm_write_pte(adev, ib, pe, addr, -+ count, incr, flags); -+ -+ } else { -+ amdgpu_vm_set_pte_pde(adev, ib, pe, addr, -+ count, incr, flags); -+ } -+} -+ -+/** -+ * amdgpu_vm_clear_bo - initially clear the page dir/table -+ * -+ * @adev: amdgpu_device pointer -+ * @bo: bo to clear -+ */ -+static int amdgpu_vm_clear_bo(struct amdgpu_device *adev, -+ struct amdgpu_bo *bo) -+{ -+ struct amdgpu_ring *ring = adev->vm_manager.vm_pte_funcs_ring; -+ struct amdgpu_ib ib; -+ unsigned entries; -+ uint64_t addr; -+ int r; -+ -+ r = amdgpu_bo_reserve(bo, false); -+ if (r) -+ return r; -+ -+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); -+ if (r) -+ goto error_unreserve; -+ -+ addr = amdgpu_bo_gpu_offset(bo); -+ entries = amdgpu_bo_size(bo) / 8; -+ -+ r = amdgpu_ib_get(ring, NULL, entries * 2 + 64, &ib); -+ if (r) -+ goto error_unreserve; -+ -+ ib.length_dw = 0; -+ -+ amdgpu_vm_update_pages(adev, &ib, addr, 0, entries, 0, 0, 0); -+ amdgpu_vm_pad_ib(adev, &ib); -+ WARN_ON(ib.length_dw > 64); -+ -+ r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_VM); -+ if (r) -+ goto error_free; -+ -+ amdgpu_bo_fence(bo, ib.fence, false); -+ -+error_free: -+ amdgpu_ib_free(adev, &ib); -+ -+error_unreserve: -+ amdgpu_bo_unreserve(bo); -+ return r; -+} -+ -+/** -+ * amdgpu_vm_map_gart - get the physical address of a gart page -+ * -+ * @adev: amdgpu_device pointer -+ * @addr: the unmapped addr -+ * -+ * Look up the physical address of the page that the pte resolves -+ * to (cayman+). -+ * Returns the physical address of the page. -+ */ -+uint64_t amdgpu_vm_map_gart(struct amdgpu_device *adev, uint64_t addr) -+{ -+ uint64_t result; -+ -+ /* page table offset */ -+ result = adev->gart.pages_addr[addr >> PAGE_SHIFT]; -+ -+ /* in case cpu page size != gpu page size*/ -+ result |= addr & (~PAGE_MASK); -+ -+ return result; -+} -+ -+/** -+ * amdgpu_vm_update_pdes - make sure that page directory is valid -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * @start: start of GPU address range -+ * @end: end of GPU address range -+ * -+ * Allocates new page tables if necessary -+ * and updates the page directory (cayman+). -+ * Returns 0 for success, error for failure. -+ * -+ * Global and local mutex must be locked! -+ */ -+int amdgpu_vm_update_page_directory(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm) -+{ -+ struct amdgpu_ring *ring = adev->vm_manager.vm_pte_funcs_ring; -+ struct amdgpu_bo *pd = vm->page_directory; -+ uint64_t pd_addr = amdgpu_bo_gpu_offset(pd); -+ uint32_t incr = AMDGPU_VM_PTE_COUNT * 8; -+ uint64_t last_pde = ~0, last_pt = ~0; -+ unsigned count = 0, pt_idx, ndw; -+ struct amdgpu_ib ib; -+ int r; -+ -+ /* padding, etc. */ -+ ndw = 64; -+ -+ /* assume the worst case */ -+ ndw += vm->max_pde_used * 6; -+ -+ /* update too big for an IB */ -+ if (ndw > 0xfffff) -+ return -ENOMEM; -+ -+ r = amdgpu_ib_get(ring, NULL, ndw * 4, &ib); -+ if (r) -+ return r; -+ ib.length_dw = 0; -+ -+ /* walk over the address space and update the page directory */ -+ for (pt_idx = 0; pt_idx <= vm->max_pde_used; ++pt_idx) { -+ struct amdgpu_bo *bo = vm->page_tables[pt_idx].bo; -+ uint64_t pde, pt; -+ -+ if (bo == NULL) -+ continue; -+ -+ pt = amdgpu_bo_gpu_offset(bo); -+ if (vm->page_tables[pt_idx].addr == pt) -+ continue; -+ vm->page_tables[pt_idx].addr = pt; -+ -+ pde = pd_addr + pt_idx * 8; -+ if (((last_pde + 8 * count) != pde) || -+ ((last_pt + incr * count) != pt)) { -+ -+ if (count) { -+ amdgpu_vm_update_pages(adev, &ib, last_pde, -+ last_pt, count, incr, -+ AMDGPU_PTE_VALID, 0); -+ } -+ -+ count = 1; -+ last_pde = pde; -+ last_pt = pt; -+ } else { -+ ++count; -+ } -+ } -+ -+ if (count) -+ amdgpu_vm_update_pages(adev, &ib, last_pde, last_pt, count, -+ incr, AMDGPU_PTE_VALID, 0); -+ -+ if (ib.length_dw != 0) { -+ amdgpu_vm_pad_ib(adev, &ib); -+ amdgpu_sync_resv(adev, &ib.sync, pd->tbo.resv, AMDGPU_FENCE_OWNER_VM); -+ WARN_ON(ib.length_dw > ndw); -+ r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_VM); -+ if (r) { -+ amdgpu_ib_free(adev, &ib); -+ return r; -+ } -+ amdgpu_bo_fence(pd, ib.fence, false); -+ } -+ amdgpu_ib_free(adev, &ib); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_frag_ptes - add fragment information to PTEs -+ * -+ * @adev: amdgpu_device pointer -+ * @ib: IB for the update -+ * @pe_start: first PTE to handle -+ * @pe_end: last PTE to handle -+ * @addr: addr those PTEs should point to -+ * @flags: hw mapping flags -+ * @gtt_flags: GTT hw mapping flags -+ * -+ * Global and local mutex must be locked! -+ */ -+static void amdgpu_vm_frag_ptes(struct amdgpu_device *adev, -+ struct amdgpu_ib *ib, -+ uint64_t pe_start, uint64_t pe_end, -+ uint64_t addr, uint32_t flags, -+ uint32_t gtt_flags) -+{ -+ /** -+ * The MC L1 TLB supports variable sized pages, based on a fragment -+ * field in the PTE. When this field is set to a non-zero value, page -+ * granularity is increased from 4KB to (1 << (12 + frag)). The PTE -+ * flags are considered valid for all PTEs within the fragment range -+ * and corresponding mappings are assumed to be physically contiguous. -+ * -+ * The L1 TLB can store a single PTE for the whole fragment, -+ * significantly increasing the space available for translation -+ * caching. This leads to large improvements in throughput when the -+ * TLB is under pressure. -+ * -+ * The L2 TLB distributes small and large fragments into two -+ * asymmetric partitions. The large fragment cache is significantly -+ * larger. Thus, we try to use large fragments wherever possible. -+ * Userspace can support this by aligning virtual base address and -+ * allocation size to the fragment size. -+ */ -+ -+ /* SI and newer are optimized for 64KB */ -+ uint64_t frag_flags = AMDGPU_PTE_FRAG_64KB; -+ uint64_t frag_align = 0x80; -+ -+ uint64_t frag_start = ALIGN(pe_start, frag_align); -+ uint64_t frag_end = pe_end & ~(frag_align - 1); -+ -+ unsigned count; -+ -+ /* system pages are non continuously */ -+ if ((flags & AMDGPU_PTE_SYSTEM) || !(flags & AMDGPU_PTE_VALID) || -+ (frag_start >= frag_end)) { -+ -+ count = (pe_end - pe_start) / 8; -+ amdgpu_vm_update_pages(adev, ib, pe_start, addr, count, -+ AMDGPU_GPU_PAGE_SIZE, flags, gtt_flags); -+ return; -+ } -+ -+ /* handle the 4K area at the beginning */ -+ if (pe_start != frag_start) { -+ count = (frag_start - pe_start) / 8; -+ amdgpu_vm_update_pages(adev, ib, pe_start, addr, count, -+ AMDGPU_GPU_PAGE_SIZE, flags, gtt_flags); -+ addr += AMDGPU_GPU_PAGE_SIZE * count; -+ } -+ -+ /* handle the area in the middle */ -+ count = (frag_end - frag_start) / 8; -+ amdgpu_vm_update_pages(adev, ib, frag_start, addr, count, -+ AMDGPU_GPU_PAGE_SIZE, flags | frag_flags, -+ gtt_flags); -+ -+ /* handle the 4K area at the end */ -+ if (frag_end != pe_end) { -+ addr += AMDGPU_GPU_PAGE_SIZE * count; -+ count = (pe_end - frag_end) / 8; -+ amdgpu_vm_update_pages(adev, ib, frag_end, addr, count, -+ AMDGPU_GPU_PAGE_SIZE, flags, gtt_flags); -+ } -+} -+ -+/** -+ * amdgpu_vm_update_ptes - make sure that page tables are valid -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * @start: start of GPU address range -+ * @end: end of GPU address range -+ * @dst: destination address to map to -+ * @flags: mapping flags -+ * -+ * Update the page tables in the range @start - @end (cayman+). -+ * -+ * Global and local mutex must be locked! -+ */ -+static int amdgpu_vm_update_ptes(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm, -+ struct amdgpu_ib *ib, -+ uint64_t start, uint64_t end, -+ uint64_t dst, uint32_t flags, -+ uint32_t gtt_flags) -+{ -+ uint64_t mask = AMDGPU_VM_PTE_COUNT - 1; -+ uint64_t last_pte = ~0, last_dst = ~0; -+ unsigned count = 0; -+ uint64_t addr; -+ -+ /* walk over the address space and update the page tables */ -+ for (addr = start; addr < end; ) { -+ uint64_t pt_idx = addr >> amdgpu_vm_block_size; -+ struct amdgpu_bo *pt = vm->page_tables[pt_idx].bo; -+ unsigned nptes; -+ uint64_t pte; -+ int r; -+ -+ amdgpu_sync_resv(adev, &ib->sync, pt->tbo.resv, -+ AMDGPU_FENCE_OWNER_VM); -+ r = reservation_object_reserve_shared(pt->tbo.resv); -+ if (r) -+ return r; -+ -+ if ((addr & ~mask) == (end & ~mask)) -+ nptes = end - addr; -+ else -+ nptes = AMDGPU_VM_PTE_COUNT - (addr & mask); -+ -+ pte = amdgpu_bo_gpu_offset(pt); -+ pte += (addr & mask) * 8; -+ -+ if ((last_pte + 8 * count) != pte) { -+ -+ if (count) { -+ amdgpu_vm_frag_ptes(adev, ib, last_pte, -+ last_pte + 8 * count, -+ last_dst, flags, -+ gtt_flags); -+ } -+ -+ count = nptes; -+ last_pte = pte; -+ last_dst = dst; -+ } else { -+ count += nptes; -+ } -+ -+ addr += nptes; -+ dst += nptes * AMDGPU_GPU_PAGE_SIZE; -+ } -+ -+ if (count) { -+ amdgpu_vm_frag_ptes(adev, ib, last_pte, -+ last_pte + 8 * count, -+ last_dst, flags, gtt_flags); -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_fence_pts - fence page tables after an update -+ * -+ * @vm: requested vm -+ * @start: start of GPU address range -+ * @end: end of GPU address range -+ * @fence: fence to use -+ * -+ * Fence the page tables in the range @start - @end (cayman+). -+ * -+ * Global and local mutex must be locked! -+ */ -+static void amdgpu_vm_fence_pts(struct amdgpu_vm *vm, -+ uint64_t start, uint64_t end, -+ struct amdgpu_fence *fence) -+{ -+ unsigned i; -+ -+ start >>= amdgpu_vm_block_size; -+ end >>= amdgpu_vm_block_size; -+ -+ for (i = start; i <= end; ++i) -+ amdgpu_bo_fence(vm->page_tables[i].bo, fence, true); -+} -+ -+/** -+ * amdgpu_vm_bo_update_mapping - update a mapping in the vm page table -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * @mapping: mapped range and flags to use for the update -+ * @addr: addr to set the area to -+ * @gtt_flags: flags as they are used for GTT -+ * @fence: optional resulting fence -+ * -+ * Fill in the page table entries for @mapping. -+ * Returns 0 for success, -EINVAL for failure. -+ * -+ * Object have to be reserved and mutex must be locked! -+ */ -+static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm, -+ struct amdgpu_bo_va_mapping *mapping, -+ uint64_t addr, uint32_t gtt_flags, -+ struct amdgpu_fence **fence) -+{ -+ struct amdgpu_ring *ring = adev->vm_manager.vm_pte_funcs_ring; -+ unsigned nptes, ncmds, ndw; -+ uint32_t flags = gtt_flags; -+ struct amdgpu_ib ib; -+ int r; -+ -+ /* normally,bo_va->flags only contians READABLE and WIRTEABLE bit go here -+ * but in case of something, we filter the flags in first place -+ */ -+ if (!(mapping->flags & AMDGPU_PTE_READABLE)) -+ flags &= ~AMDGPU_PTE_READABLE; -+ if (!(mapping->flags & AMDGPU_PTE_WRITEABLE)) -+ flags &= ~AMDGPU_PTE_WRITEABLE; -+ -+ trace_amdgpu_vm_bo_update(mapping); -+ -+ nptes = mapping->it.last - mapping->it.start + 1; -+ -+ /* -+ * reserve space for one command every (1 << BLOCK_SIZE) -+ * entries or 2k dwords (whatever is smaller) -+ */ -+ ncmds = (nptes >> min(amdgpu_vm_block_size, 11)) + 1; -+ -+ /* padding, etc. */ -+ ndw = 64; -+ -+ if ((flags & AMDGPU_PTE_SYSTEM) && (flags == gtt_flags)) { -+ /* only copy commands needed */ -+ ndw += ncmds * 7; -+ -+ } else if (flags & AMDGPU_PTE_SYSTEM) { -+ /* header for write data commands */ -+ ndw += ncmds * 4; -+ -+ /* body of write data command */ -+ ndw += nptes * 2; -+ -+ } else { -+ /* set page commands needed */ -+ ndw += ncmds * 10; -+ -+ /* two extra commands for begin/end of fragment */ -+ ndw += 2 * 10; -+ } -+ -+ /* update too big for an IB */ -+ if (ndw > 0xfffff) -+ return -ENOMEM; -+ -+ r = amdgpu_ib_get(ring, NULL, ndw * 4, &ib); -+ if (r) -+ return r; -+ ib.length_dw = 0; -+ -+ if (!(flags & AMDGPU_PTE_VALID)) { -+ unsigned i; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ struct amdgpu_fence *f = vm->ids[i].last_id_use; -+ amdgpu_sync_fence(&ib.sync, f); -+ } -+ } -+ -+ r = amdgpu_vm_update_ptes(adev, vm, &ib, mapping->it.start, -+ mapping->it.last + 1, addr + mapping->offset, -+ flags, gtt_flags); -+ -+ if (r) { -+ amdgpu_ib_free(adev, &ib); -+ return r; -+ } -+ -+ amdgpu_vm_pad_ib(adev, &ib); -+ WARN_ON(ib.length_dw > ndw); -+ -+ r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_VM); -+ if (r) { -+ amdgpu_ib_free(adev, &ib); -+ return r; -+ } -+ amdgpu_vm_fence_pts(vm, mapping->it.start, -+ mapping->it.last + 1, ib.fence); -+ if (fence) { -+ amdgpu_fence_unref(fence); -+ *fence = amdgpu_fence_ref(ib.fence); -+ } -+ amdgpu_ib_free(adev, &ib); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_bo_update - update all BO mappings in the vm page table -+ * -+ * @adev: amdgpu_device pointer -+ * @bo_va: requested BO and VM object -+ * @mem: ttm mem -+ * -+ * Fill in the page table entries for @bo_va. -+ * Returns 0 for success, -EINVAL for failure. -+ * -+ * Object have to be reserved and mutex must be locked! -+ */ -+int amdgpu_vm_bo_update(struct amdgpu_device *adev, -+ struct amdgpu_bo_va *bo_va, -+ struct ttm_mem_reg *mem) -+{ -+ struct amdgpu_vm *vm = bo_va->vm; -+ struct amdgpu_bo_va_mapping *mapping; -+ uint32_t flags; -+ uint64_t addr; -+ int r; -+ -+ if (mem) { -+ addr = mem->start << PAGE_SHIFT; -+ if (mem->mem_type != TTM_PL_TT) -+ addr += adev->vm_manager.vram_base_offset; -+ } else { -+ addr = 0; -+ } -+ -+ if (addr == bo_va->addr) -+ return 0; -+ -+ flags = amdgpu_ttm_tt_pte_flags(adev, bo_va->bo->tbo.ttm, mem); -+ -+ list_for_each_entry(mapping, &bo_va->mappings, list) { -+ r = amdgpu_vm_bo_update_mapping(adev, vm, mapping, addr, -+ flags, &bo_va->last_pt_update); -+ if (r) -+ return r; -+ } -+ -+ bo_va->addr = addr; -+ spin_lock(&vm->status_lock); -+ list_del_init(&bo_va->vm_status); -+ spin_unlock(&vm->status_lock); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_clear_freed - clear freed BOs in the PT -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * -+ * Make sure all freed BOs are cleared in the PT. -+ * Returns 0 for success. -+ * -+ * PTs have to be reserved and mutex must be locked! -+ */ -+int amdgpu_vm_clear_freed(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm) -+{ -+ struct amdgpu_bo_va_mapping *mapping; -+ int r; -+ -+ while (!list_empty(&vm->freed)) { -+ mapping = list_first_entry(&vm->freed, -+ struct amdgpu_bo_va_mapping, list); -+ list_del(&mapping->list); -+ -+ r = amdgpu_vm_bo_update_mapping(adev, vm, mapping, 0, 0, NULL); -+ kfree(mapping); -+ if (r) -+ return r; -+ -+ } -+ return 0; -+ -+} -+ -+/** -+ * amdgpu_vm_clear_invalids - clear invalidated BOs in the PT -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * -+ * Make sure all invalidated BOs are cleared in the PT. -+ * Returns 0 for success. -+ * -+ * PTs have to be reserved and mutex must be locked! -+ */ -+int amdgpu_vm_clear_invalids(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm) -+{ -+ struct amdgpu_bo_va *bo_va; -+ int r; -+ -+ spin_lock(&vm->status_lock); -+ while (!list_empty(&vm->invalidated)) { -+ bo_va = list_first_entry(&vm->invalidated, -+ struct amdgpu_bo_va, vm_status); -+ spin_unlock(&vm->status_lock); -+ -+ r = amdgpu_vm_bo_update(adev, bo_va, NULL); -+ if (r) -+ return r; -+ -+ spin_lock(&vm->status_lock); -+ } -+ spin_unlock(&vm->status_lock); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_bo_add - add a bo to a specific vm -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * @bo: amdgpu buffer object -+ * -+ * Add @bo into the requested vm (cayman+). -+ * Add @bo to the list of bos associated with the vm -+ * Returns newly added bo_va or NULL for failure -+ * -+ * Object has to be reserved! -+ */ -+struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev, -+ struct amdgpu_vm *vm, -+ struct amdgpu_bo *bo) -+{ -+ struct amdgpu_bo_va *bo_va; -+ -+ bo_va = kzalloc(sizeof(struct amdgpu_bo_va), GFP_KERNEL); -+ if (bo_va == NULL) { -+ return NULL; -+ } -+ bo_va->vm = vm; -+ bo_va->bo = bo; -+ bo_va->addr = 0; -+ bo_va->ref_count = 1; -+ INIT_LIST_HEAD(&bo_va->bo_list); -+ INIT_LIST_HEAD(&bo_va->mappings); -+ INIT_LIST_HEAD(&bo_va->vm_status); -+ -+ mutex_lock(&vm->mutex); -+ list_add_tail(&bo_va->bo_list, &bo->va); -+ mutex_unlock(&vm->mutex); -+ -+ return bo_va; -+} -+ -+/** -+ * amdgpu_vm_bo_map - map bo inside a vm -+ * -+ * @adev: amdgpu_device pointer -+ * @bo_va: bo_va to store the address -+ * @saddr: where to map the BO -+ * @offset: requested offset in the BO -+ * @flags: attributes of pages (read/write/valid/etc.) -+ * -+ * Add a mapping of the BO at the specefied addr into the VM. -+ * Returns 0 for success, error for failure. -+ * -+ * Object has to be reserved and gets unreserved by this function! -+ */ -+int amdgpu_vm_bo_map(struct amdgpu_device *adev, -+ struct amdgpu_bo_va *bo_va, -+ uint64_t saddr, uint64_t offset, -+ uint64_t size, uint32_t flags) -+{ -+ struct amdgpu_bo_va_mapping *mapping; -+ struct amdgpu_vm *vm = bo_va->vm; -+ struct interval_tree_node *it; -+ unsigned last_pfn, pt_idx; -+ uint64_t eaddr; -+ int r; -+ -+ /* make sure object fit at this offset */ -+ eaddr = saddr + size; -+ if ((saddr >= eaddr) || (offset + size > amdgpu_bo_size(bo_va->bo))) { -+ amdgpu_bo_unreserve(bo_va->bo); -+ return -EINVAL; -+ } -+ -+ last_pfn = eaddr / AMDGPU_GPU_PAGE_SIZE; -+ if (last_pfn > adev->vm_manager.max_pfn) { -+ dev_err(adev->dev, "va above limit (0x%08X > 0x%08X)\n", -+ last_pfn, adev->vm_manager.max_pfn); -+ amdgpu_bo_unreserve(bo_va->bo); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&vm->mutex); -+ -+ saddr /= AMDGPU_GPU_PAGE_SIZE; -+ eaddr /= AMDGPU_GPU_PAGE_SIZE; -+ -+ it = interval_tree_iter_first(&vm->va, saddr, eaddr - 1); -+ if (it) { -+ struct amdgpu_bo_va_mapping *tmp; -+ tmp = container_of(it, struct amdgpu_bo_va_mapping, it); -+ /* bo and tmp overlap, invalid addr */ -+ dev_err(adev->dev, "bo %p va 0x%010Lx-0x%010Lx conflict with " -+ "0x%010lx-0x%010lx\n", bo_va->bo, saddr, eaddr, -+ tmp->it.start, tmp->it.last + 1); -+ amdgpu_bo_unreserve(bo_va->bo); -+ r = -EINVAL; -+ goto error_unlock; -+ } -+ -+ mapping = kmalloc(sizeof(*mapping), GFP_KERNEL); -+ if (!mapping) { -+ amdgpu_bo_unreserve(bo_va->bo); -+ r = -ENOMEM; -+ goto error_unlock; -+ } -+ -+ INIT_LIST_HEAD(&mapping->list); -+ mapping->it.start = saddr; -+ mapping->it.last = eaddr - 1; -+ mapping->offset = offset; -+ mapping->flags = flags; -+ -+ list_add(&mapping->list, &bo_va->mappings); -+ interval_tree_insert(&mapping->it, &vm->va); -+ -+ /* Make sure the page tables are allocated */ -+ saddr >>= amdgpu_vm_block_size; -+ eaddr >>= amdgpu_vm_block_size; -+ -+ BUG_ON(eaddr >= amdgpu_vm_num_pdes(adev)); -+ -+ if (eaddr > vm->max_pde_used) -+ vm->max_pde_used = eaddr; -+ -+ amdgpu_bo_unreserve(bo_va->bo); -+ -+ /* walk over the address space and allocate the page tables */ -+ for (pt_idx = saddr; pt_idx <= eaddr; ++pt_idx) { -+ struct amdgpu_bo *pt; -+ -+ if (vm->page_tables[pt_idx].bo) -+ continue; -+ -+ /* drop mutex to allocate and clear page table */ -+ mutex_unlock(&vm->mutex); -+ -+ r = amdgpu_bo_create(adev, AMDGPU_VM_PTE_COUNT * 8, -+ AMDGPU_GPU_PAGE_SIZE, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, &pt); -+ if (r) -+ goto error_free; -+ -+ r = amdgpu_vm_clear_bo(adev, pt); -+ if (r) { -+ amdgpu_bo_unref(&pt); -+ goto error_free; -+ } -+ -+ /* aquire mutex again */ -+ mutex_lock(&vm->mutex); -+ if (vm->page_tables[pt_idx].bo) { -+ /* someone else allocated the pt in the meantime */ -+ mutex_unlock(&vm->mutex); -+ amdgpu_bo_unref(&pt); -+ mutex_lock(&vm->mutex); -+ continue; -+ } -+ -+ vm->page_tables[pt_idx].addr = 0; -+ vm->page_tables[pt_idx].bo = pt; -+ } -+ -+ mutex_unlock(&vm->mutex); -+ return 0; -+ -+error_free: -+ mutex_lock(&vm->mutex); -+ list_del(&mapping->list); -+ interval_tree_remove(&mapping->it, &vm->va); -+ kfree(mapping); -+ -+error_unlock: -+ mutex_unlock(&vm->mutex); -+ return r; -+} -+ -+/** -+ * amdgpu_vm_bo_unmap - remove bo mapping from vm -+ * -+ * @adev: amdgpu_device pointer -+ * @bo_va: bo_va to remove the address from -+ * @saddr: where to the BO is mapped -+ * -+ * Remove a mapping of the BO at the specefied addr from the VM. -+ * Returns 0 for success, error for failure. -+ * -+ * Object has to be reserved and gets unreserved by this function! -+ */ -+int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, -+ struct amdgpu_bo_va *bo_va, -+ uint64_t saddr) -+{ -+ struct amdgpu_bo_va_mapping *mapping; -+ struct amdgpu_vm *vm = bo_va->vm; -+ -+ list_for_each_entry(mapping, &bo_va->mappings, list) { -+ if (mapping->it.start == saddr) -+ break; -+ } -+ -+ if (&mapping->list == &bo_va->mappings) { -+ amdgpu_bo_unreserve(bo_va->bo); -+ return -ENOENT; -+ } -+ -+ mutex_lock(&vm->mutex); -+ list_del(&mapping->list); -+ interval_tree_remove(&mapping->it, &vm->va); -+ -+ if (bo_va->addr) { -+ /* clear the old address */ -+ list_add(&mapping->list, &vm->freed); -+ } else { -+ kfree(mapping); -+ } -+ mutex_unlock(&vm->mutex); -+ amdgpu_bo_unreserve(bo_va->bo); -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_bo_rmv - remove a bo to a specific vm -+ * -+ * @adev: amdgpu_device pointer -+ * @bo_va: requested bo_va -+ * -+ * Remove @bo_va->bo from the requested vm (cayman+). -+ * -+ * Object have to be reserved! -+ */ -+void amdgpu_vm_bo_rmv(struct amdgpu_device *adev, -+ struct amdgpu_bo_va *bo_va) -+{ -+ struct amdgpu_bo_va_mapping *mapping, *next; -+ struct amdgpu_vm *vm = bo_va->vm; -+ -+ list_del(&bo_va->bo_list); -+ -+ mutex_lock(&vm->mutex); -+ -+ spin_lock(&vm->status_lock); -+ list_del(&bo_va->vm_status); -+ spin_unlock(&vm->status_lock); -+ -+ list_for_each_entry_safe(mapping, next, &bo_va->mappings, list) { -+ list_del(&mapping->list); -+ interval_tree_remove(&mapping->it, &vm->va); -+ if (bo_va->addr) -+ list_add(&mapping->list, &vm->freed); -+ else -+ kfree(mapping); -+ } -+ amdgpu_fence_unref(&bo_va->last_pt_update); -+ kfree(bo_va); -+ -+ mutex_unlock(&vm->mutex); -+} -+ -+/** -+ * amdgpu_vm_bo_invalidate - mark the bo as invalid -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * @bo: amdgpu buffer object -+ * -+ * Mark @bo as invalid (cayman+). -+ */ -+void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev, -+ struct amdgpu_bo *bo) -+{ -+ struct amdgpu_bo_va *bo_va; -+ -+ list_for_each_entry(bo_va, &bo->va, bo_list) { -+ if (bo_va->addr) { -+ spin_lock(&bo_va->vm->status_lock); -+ list_del(&bo_va->vm_status); -+ list_add(&bo_va->vm_status, &bo_va->vm->invalidated); -+ spin_unlock(&bo_va->vm->status_lock); -+ } -+ } -+} -+ -+/** -+ * amdgpu_vm_init - initialize a vm instance -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * -+ * Init @vm fields (cayman+). -+ */ -+int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) -+{ -+ const unsigned align = min(AMDGPU_VM_PTB_ALIGN_SIZE, -+ AMDGPU_VM_PTE_COUNT * 8); -+ unsigned pd_size, pd_entries, pts_size; -+ int i, r; -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ vm->ids[i].id = 0; -+ vm->ids[i].flushed_updates = NULL; -+ vm->ids[i].last_id_use = NULL; -+ } -+ mutex_init(&vm->mutex); -+ vm->va = RB_ROOT; -+ spin_lock_init(&vm->status_lock); -+ INIT_LIST_HEAD(&vm->invalidated); -+ INIT_LIST_HEAD(&vm->freed); -+ -+ pd_size = amdgpu_vm_directory_size(adev); -+ pd_entries = amdgpu_vm_num_pdes(adev); -+ -+ /* allocate page table array */ -+ pts_size = pd_entries * sizeof(struct amdgpu_vm_pt); -+ vm->page_tables = kzalloc(pts_size, GFP_KERNEL); -+ if (vm->page_tables == NULL) { -+ DRM_ERROR("Cannot allocate memory for page table array\n"); -+ return -ENOMEM; -+ } -+ -+ r = amdgpu_bo_create(adev, pd_size, align, true, -+ AMDGPU_GEM_DOMAIN_VRAM, 0, -+ NULL, &vm->page_directory); -+ if (r) -+ return r; -+ -+ r = amdgpu_vm_clear_bo(adev, vm->page_directory); -+ if (r) { -+ amdgpu_bo_unref(&vm->page_directory); -+ vm->page_directory = NULL; -+ return r; -+ } -+ -+ return 0; -+} -+ -+/** -+ * amdgpu_vm_fini - tear down a vm instance -+ * -+ * @adev: amdgpu_device pointer -+ * @vm: requested vm -+ * -+ * Tear down @vm (cayman+). -+ * Unbind the VM and remove all bos from the vm bo list -+ */ -+void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) -+{ -+ struct amdgpu_bo_va_mapping *mapping, *tmp; -+ int i; -+ -+ if (!RB_EMPTY_ROOT(&vm->va)) { -+ dev_err(adev->dev, "still active bo inside vm\n"); -+ } -+ rbtree_postorder_for_each_entry_safe(mapping, tmp, &vm->va, it.rb) { -+ list_del(&mapping->list); -+ interval_tree_remove(&mapping->it, &vm->va); -+ kfree(mapping); -+ } -+ list_for_each_entry_safe(mapping, tmp, &vm->freed, list) { -+ list_del(&mapping->list); -+ kfree(mapping); -+ } -+ -+ for (i = 0; i < amdgpu_vm_num_pdes(adev); i++) -+ amdgpu_bo_unref(&vm->page_tables[i].bo); -+ kfree(vm->page_tables); -+ -+ amdgpu_bo_unref(&vm->page_directory); -+ -+ for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { -+ amdgpu_fence_unref(&vm->ids[i].flushed_updates); -+ amdgpu_fence_unref(&vm->ids[i].last_id_use); -+ } -+ -+ mutex_destroy(&vm->mutex); -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/atom-bits.h b/drivers/gpu/drm/amd/amdgpu/atom-bits.h -new file mode 100644 -index 0000000..e8fae5c ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atom-bits.h -@@ -0,0 +1,48 @@ -+/* -+ * Copyright 2008 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. -+ * -+ * Author: Stanislaw Skowronek -+ */ -+ -+#ifndef ATOM_BITS_H -+#define ATOM_BITS_H -+ -+static inline uint8_t get_u8(void *bios, int ptr) -+{ -+ return ((unsigned char *)bios)[ptr]; -+} -+#define U8(ptr) get_u8(ctx->ctx->bios, (ptr)) -+#define CU8(ptr) get_u8(ctx->bios, (ptr)) -+static inline uint16_t get_u16(void *bios, int ptr) -+{ -+ return get_u8(bios ,ptr)|(((uint16_t)get_u8(bios, ptr+1))<<8); -+} -+#define U16(ptr) get_u16(ctx->ctx->bios, (ptr)) -+#define CU16(ptr) get_u16(ctx->bios, (ptr)) -+static inline uint32_t get_u32(void *bios, int ptr) -+{ -+ return get_u16(bios, ptr)|(((uint32_t)get_u16(bios, ptr+2))<<16); -+} -+#define U32(ptr) get_u32(ctx->ctx->bios, (ptr)) -+#define CU32(ptr) get_u32(ctx->bios, (ptr)) -+#define CSTR(ptr) (((char *)(ctx->bios))+(ptr)) -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atom-names.h b/drivers/gpu/drm/amd/amdgpu/atom-names.h -new file mode 100644 -index 0000000..6f907a5 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atom-names.h -@@ -0,0 +1,100 @@ -+/* -+ * Copyright 2008 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. -+ * -+ * Author: Stanislaw Skowronek -+ */ -+ -+#ifndef ATOM_NAMES_H -+#define ATOM_NAMES_H -+ -+#include "atom.h" -+ -+#ifdef ATOM_DEBUG -+ -+#define ATOM_OP_NAMES_CNT 123 -+static char *atom_op_names[ATOM_OP_NAMES_CNT] = { -+"RESERVED", "MOVE_REG", "MOVE_PS", "MOVE_WS", "MOVE_FB", "MOVE_PLL", -+"MOVE_MC", "AND_REG", "AND_PS", "AND_WS", "AND_FB", "AND_PLL", "AND_MC", -+"OR_REG", "OR_PS", "OR_WS", "OR_FB", "OR_PLL", "OR_MC", "SHIFT_LEFT_REG", -+"SHIFT_LEFT_PS", "SHIFT_LEFT_WS", "SHIFT_LEFT_FB", "SHIFT_LEFT_PLL", -+"SHIFT_LEFT_MC", "SHIFT_RIGHT_REG", "SHIFT_RIGHT_PS", "SHIFT_RIGHT_WS", -+"SHIFT_RIGHT_FB", "SHIFT_RIGHT_PLL", "SHIFT_RIGHT_MC", "MUL_REG", -+"MUL_PS", "MUL_WS", "MUL_FB", "MUL_PLL", "MUL_MC", "DIV_REG", "DIV_PS", -+"DIV_WS", "DIV_FB", "DIV_PLL", "DIV_MC", "ADD_REG", "ADD_PS", "ADD_WS", -+"ADD_FB", "ADD_PLL", "ADD_MC", "SUB_REG", "SUB_PS", "SUB_WS", "SUB_FB", -+"SUB_PLL", "SUB_MC", "SET_ATI_PORT", "SET_PCI_PORT", "SET_SYS_IO_PORT", -+"SET_REG_BLOCK", "SET_FB_BASE", "COMPARE_REG", "COMPARE_PS", -+"COMPARE_WS", "COMPARE_FB", "COMPARE_PLL", "COMPARE_MC", "SWITCH", -+"JUMP", "JUMP_EQUAL", "JUMP_BELOW", "JUMP_ABOVE", "JUMP_BELOW_OR_EQUAL", -+"JUMP_ABOVE_OR_EQUAL", "JUMP_NOT_EQUAL", "TEST_REG", "TEST_PS", "TEST_WS", -+"TEST_FB", "TEST_PLL", "TEST_MC", "DELAY_MILLISEC", "DELAY_MICROSEC", -+"CALL_TABLE", "REPEAT", "CLEAR_REG", "CLEAR_PS", "CLEAR_WS", "CLEAR_FB", -+"CLEAR_PLL", "CLEAR_MC", "NOP", "EOT", "MASK_REG", "MASK_PS", "MASK_WS", -+"MASK_FB", "MASK_PLL", "MASK_MC", "POST_CARD", "BEEP", "SAVE_REG", -+"RESTORE_REG", "SET_DATA_BLOCK", "XOR_REG", "XOR_PS", "XOR_WS", "XOR_FB", -+"XOR_PLL", "XOR_MC", "SHL_REG", "SHL_PS", "SHL_WS", "SHL_FB", "SHL_PLL", -+"SHL_MC", "SHR_REG", "SHR_PS", "SHR_WS", "SHR_FB", "SHR_PLL", "SHR_MC", -+"DEBUG", "CTB_DS", -+}; -+ -+#define ATOM_TABLE_NAMES_CNT 74 -+static char *atom_table_names[ATOM_TABLE_NAMES_CNT] = { -+"ASIC_Init", "GetDisplaySurfaceSize", "ASIC_RegistersInit", -+"VRAM_BlockVenderDetection", "SetClocksRatio", "MemoryControllerInit", -+"GPIO_PinInit", "MemoryParamAdjust", "DVOEncoderControl", -+"GPIOPinControl", "SetEngineClock", "SetMemoryClock", "SetPixelClock", -+"DynamicClockGating", "ResetMemoryDLL", "ResetMemoryDevice", -+"MemoryPLLInit", "EnableMemorySelfRefresh", "AdjustMemoryController", -+"EnableASIC_StaticPwrMgt", "ASIC_StaticPwrMgtStatusChange", -+"DAC_LoadDetection", "TMDS2EncoderControl", "LCD1OutputControl", -+"DAC1EncoderControl", "DAC2EncoderControl", "DVOOutputControl", -+"CV1OutputControl", "SetCRTC_DPM_State", "TVEncoderControl", -+"TMDS1EncoderControl", "LVDSEncoderControl", "TV1OutputControl", -+"EnableScaler", "BlankCRTC", "EnableCRTC", "GetPixelClock", -+"EnableVGA_Render", "EnableVGA_Access", "SetCRTC_Timing", -+"SetCRTC_OverScan", "SetCRTC_Replication", "SelectCRTC_Source", -+"EnableGraphSurfaces", "UpdateCRTC_DoubleBufferRegisters", -+"LUT_AutoFill", "EnableHW_IconCursor", "GetMemoryClock", -+"GetEngineClock", "SetCRTC_UsingDTDTiming", "TVBootUpStdPinDetection", -+"DFP2OutputControl", "VRAM_BlockDetectionByStrap", "MemoryCleanUp", -+"ReadEDIDFromHWAssistedI2C", "WriteOneByteToHWAssistedI2C", -+"ReadHWAssistedI2CStatus", "SpeedFanControl", "PowerConnectorDetection", -+"MC_Synchronization", "ComputeMemoryEnginePLL", "MemoryRefreshConversion", -+"VRAM_GetCurrentInfoBlock", "DynamicMemorySettings", "MemoryTraining", -+"EnableLVDS_SS", "DFP1OutputControl", "SetVoltage", "CRT1OutputControl", -+"CRT2OutputControl", "SetupHWAssistedI2CStatus", "ClockSource", -+"MemoryDeviceInit", "EnableYUV", -+}; -+ -+#define ATOM_IO_NAMES_CNT 5 -+static char *atom_io_names[ATOM_IO_NAMES_CNT] = { -+"MM", "PLL", "MC", "PCIE", "PCIE PORT", -+}; -+ -+#else -+ -+#define ATOM_OP_NAMES_CNT 0 -+#define ATOM_TABLE_NAMES_CNT 0 -+#define ATOM_IO_NAMES_CNT 0 -+ -+#endif -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atom-types.h b/drivers/gpu/drm/amd/amdgpu/atom-types.h -new file mode 100644 -index 0000000..1125b86 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atom-types.h -@@ -0,0 +1,42 @@ -+/* -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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. -+ * -+ * Author: Dave Airlie -+ */ -+ -+#ifndef ATOM_TYPES_H -+#define ATOM_TYPES_H -+ -+/* sync atom types to kernel types */ -+ -+typedef uint16_t USHORT; -+typedef uint32_t ULONG; -+typedef uint8_t UCHAR; -+ -+ -+#ifndef ATOM_BIG_ENDIAN -+#if defined(__BIG_ENDIAN) -+#define ATOM_BIG_ENDIAN 1 -+#else -+#define ATOM_BIG_ENDIAN 0 -+#endif -+#endif -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atom.c b/drivers/gpu/drm/amd/amdgpu/atom.c -new file mode 100644 -index 0000000..a0346a9 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atom.c -@@ -0,0 +1,1408 @@ -+/* -+ * Copyright 2008 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. -+ * -+ * Author: Stanislaw Skowronek -+ */ -+ -+#include <linux/module.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <asm/unaligned.h> -+ -+#define ATOM_DEBUG -+ -+#include "atom.h" -+#include "atom-names.h" -+#include "atom-bits.h" -+#include "amdgpu.h" -+ -+#define ATOM_COND_ABOVE 0 -+#define ATOM_COND_ABOVEOREQUAL 1 -+#define ATOM_COND_ALWAYS 2 -+#define ATOM_COND_BELOW 3 -+#define ATOM_COND_BELOWOREQUAL 4 -+#define ATOM_COND_EQUAL 5 -+#define ATOM_COND_NOTEQUAL 6 -+ -+#define ATOM_PORT_ATI 0 -+#define ATOM_PORT_PCI 1 -+#define ATOM_PORT_SYSIO 2 -+ -+#define ATOM_UNIT_MICROSEC 0 -+#define ATOM_UNIT_MILLISEC 1 -+ -+#define PLL_INDEX 2 -+#define PLL_DATA 3 -+ -+typedef struct { -+ struct atom_context *ctx; -+ uint32_t *ps, *ws; -+ int ps_shift; -+ uint16_t start; -+ unsigned last_jump; -+ unsigned long last_jump_jiffies; -+ bool abort; -+} atom_exec_context; -+ -+int amdgpu_atom_debug = 0; -+static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); -+int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); -+ -+static uint32_t atom_arg_mask[8] = -+ { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, -+0xFF000000 }; -+static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 }; -+ -+static int atom_dst_to_src[8][4] = { -+ /* translate destination alignment field to the source alignment encoding */ -+ {0, 0, 0, 0}, -+ {1, 2, 3, 0}, -+ {1, 2, 3, 0}, -+ {1, 2, 3, 0}, -+ {4, 5, 6, 7}, -+ {4, 5, 6, 7}, -+ {4, 5, 6, 7}, -+ {4, 5, 6, 7}, -+}; -+static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 }; -+ -+static int debug_depth = 0; -+#ifdef ATOM_DEBUG -+static void debug_print_spaces(int n) -+{ -+ while (n--) -+ printk(" "); -+} -+ -+#define DEBUG(...) do if (amdgpu_atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while (0) -+#define SDEBUG(...) do if (amdgpu_atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while (0) -+#else -+#define DEBUG(...) do { } while (0) -+#define SDEBUG(...) do { } while (0) -+#endif -+ -+static uint32_t atom_iio_execute(struct atom_context *ctx, int base, -+ uint32_t index, uint32_t data) -+{ -+ uint32_t temp = 0xCDCDCDCD; -+ -+ while (1) -+ switch (CU8(base)) { -+ case ATOM_IIO_NOP: -+ base++; -+ break; -+ case ATOM_IIO_READ: -+ temp = ctx->card->ioreg_read(ctx->card, CU16(base + 1)); -+ base += 3; -+ break; -+ case ATOM_IIO_WRITE: -+ ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp); -+ base += 3; -+ break; -+ case ATOM_IIO_CLEAR: -+ temp &= -+ ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << -+ CU8(base + 2)); -+ base += 3; -+ break; -+ case ATOM_IIO_SET: -+ temp |= -+ (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + -+ 2); -+ base += 3; -+ break; -+ case ATOM_IIO_MOVE_INDEX: -+ temp &= -+ ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << -+ CU8(base + 3)); -+ temp |= -+ ((index >> CU8(base + 2)) & -+ (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + -+ 3); -+ base += 4; -+ break; -+ case ATOM_IIO_MOVE_DATA: -+ temp &= -+ ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << -+ CU8(base + 3)); -+ temp |= -+ ((data >> CU8(base + 2)) & -+ (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + -+ 3); -+ base += 4; -+ break; -+ case ATOM_IIO_MOVE_ATTR: -+ temp &= -+ ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << -+ CU8(base + 3)); -+ temp |= -+ ((ctx-> -+ io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - -+ CU8 -+ (base -+ + -+ 1)))) -+ << CU8(base + 3); -+ base += 4; -+ break; -+ case ATOM_IIO_END: -+ return temp; -+ default: -+ printk(KERN_INFO "Unknown IIO opcode.\n"); -+ return 0; -+ } -+} -+ -+static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, -+ int *ptr, uint32_t *saved, int print) -+{ -+ uint32_t idx, val = 0xCDCDCDCD, align, arg; -+ struct atom_context *gctx = ctx->ctx; -+ arg = attr & 7; -+ align = (attr >> 3) & 7; -+ switch (arg) { -+ case ATOM_ARG_REG: -+ idx = U16(*ptr); -+ (*ptr) += 2; -+ if (print) -+ DEBUG("REG[0x%04X]", idx); -+ idx += gctx->reg_block; -+ switch (gctx->io_mode) { -+ case ATOM_IO_MM: -+ val = gctx->card->reg_read(gctx->card, idx); -+ break; -+ case ATOM_IO_PCI: -+ printk(KERN_INFO -+ "PCI registers are not implemented.\n"); -+ return 0; -+ case ATOM_IO_SYSIO: -+ printk(KERN_INFO -+ "SYSIO registers are not implemented.\n"); -+ return 0; -+ default: -+ if (!(gctx->io_mode & 0x80)) { -+ printk(KERN_INFO "Bad IO mode.\n"); -+ return 0; -+ } -+ if (!gctx->iio[gctx->io_mode & 0x7F]) { -+ printk(KERN_INFO -+ "Undefined indirect IO read method %d.\n", -+ gctx->io_mode & 0x7F); -+ return 0; -+ } -+ val = -+ atom_iio_execute(gctx, -+ gctx->iio[gctx->io_mode & 0x7F], -+ idx, 0); -+ } -+ break; -+ case ATOM_ARG_PS: -+ idx = U8(*ptr); -+ (*ptr)++; -+ /* get_unaligned_le32 avoids unaligned accesses from atombios -+ * tables, noticed on a DEC Alpha. */ -+ val = get_unaligned_le32((u32 *)&ctx->ps[idx]); -+ if (print) -+ DEBUG("PS[0x%02X,0x%04X]", idx, val); -+ break; -+ case ATOM_ARG_WS: -+ idx = U8(*ptr); -+ (*ptr)++; -+ if (print) -+ DEBUG("WS[0x%02X]", idx); -+ switch (idx) { -+ case ATOM_WS_QUOTIENT: -+ val = gctx->divmul[0]; -+ break; -+ case ATOM_WS_REMAINDER: -+ val = gctx->divmul[1]; -+ break; -+ case ATOM_WS_DATAPTR: -+ val = gctx->data_block; -+ break; -+ case ATOM_WS_SHIFT: -+ val = gctx->shift; -+ break; -+ case ATOM_WS_OR_MASK: -+ val = 1 << gctx->shift; -+ break; -+ case ATOM_WS_AND_MASK: -+ val = ~(1 << gctx->shift); -+ break; -+ case ATOM_WS_FB_WINDOW: -+ val = gctx->fb_base; -+ break; -+ case ATOM_WS_ATTRIBUTES: -+ val = gctx->io_attr; -+ break; -+ case ATOM_WS_REGPTR: -+ val = gctx->reg_block; -+ break; -+ default: -+ val = ctx->ws[idx]; -+ } -+ break; -+ case ATOM_ARG_ID: -+ idx = U16(*ptr); -+ (*ptr) += 2; -+ if (print) { -+ if (gctx->data_block) -+ DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block); -+ else -+ DEBUG("ID[0x%04X]", idx); -+ } -+ val = U32(idx + gctx->data_block); -+ break; -+ case ATOM_ARG_FB: -+ idx = U8(*ptr); -+ (*ptr)++; -+ if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { -+ DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n", -+ gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); -+ val = 0; -+ } else -+ val = gctx->scratch[(gctx->fb_base / 4) + idx]; -+ if (print) -+ DEBUG("FB[0x%02X]", idx); -+ break; -+ case ATOM_ARG_IMM: -+ switch (align) { -+ case ATOM_SRC_DWORD: -+ val = U32(*ptr); -+ (*ptr) += 4; -+ if (print) -+ DEBUG("IMM 0x%08X\n", val); -+ return val; -+ case ATOM_SRC_WORD0: -+ case ATOM_SRC_WORD8: -+ case ATOM_SRC_WORD16: -+ val = U16(*ptr); -+ (*ptr) += 2; -+ if (print) -+ DEBUG("IMM 0x%04X\n", val); -+ return val; -+ case ATOM_SRC_BYTE0: -+ case ATOM_SRC_BYTE8: -+ case ATOM_SRC_BYTE16: -+ case ATOM_SRC_BYTE24: -+ val = U8(*ptr); -+ (*ptr)++; -+ if (print) -+ DEBUG("IMM 0x%02X\n", val); -+ return val; -+ } -+ return 0; -+ case ATOM_ARG_PLL: -+ idx = U8(*ptr); -+ (*ptr)++; -+ if (print) -+ DEBUG("PLL[0x%02X]", idx); -+ val = gctx->card->pll_read(gctx->card, idx); -+ break; -+ case ATOM_ARG_MC: -+ idx = U8(*ptr); -+ (*ptr)++; -+ if (print) -+ DEBUG("MC[0x%02X]", idx); -+ val = gctx->card->mc_read(gctx->card, idx); -+ break; -+ } -+ if (saved) -+ *saved = val; -+ val &= atom_arg_mask[align]; -+ val >>= atom_arg_shift[align]; -+ if (print) -+ switch (align) { -+ case ATOM_SRC_DWORD: -+ DEBUG(".[31:0] -> 0x%08X\n", val); -+ break; -+ case ATOM_SRC_WORD0: -+ DEBUG(".[15:0] -> 0x%04X\n", val); -+ break; -+ case ATOM_SRC_WORD8: -+ DEBUG(".[23:8] -> 0x%04X\n", val); -+ break; -+ case ATOM_SRC_WORD16: -+ DEBUG(".[31:16] -> 0x%04X\n", val); -+ break; -+ case ATOM_SRC_BYTE0: -+ DEBUG(".[7:0] -> 0x%02X\n", val); -+ break; -+ case ATOM_SRC_BYTE8: -+ DEBUG(".[15:8] -> 0x%02X\n", val); -+ break; -+ case ATOM_SRC_BYTE16: -+ DEBUG(".[23:16] -> 0x%02X\n", val); -+ break; -+ case ATOM_SRC_BYTE24: -+ DEBUG(".[31:24] -> 0x%02X\n", val); -+ break; -+ } -+ return val; -+} -+ -+static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr) -+{ -+ uint32_t align = (attr >> 3) & 7, arg = attr & 7; -+ switch (arg) { -+ case ATOM_ARG_REG: -+ case ATOM_ARG_ID: -+ (*ptr) += 2; -+ break; -+ case ATOM_ARG_PLL: -+ case ATOM_ARG_MC: -+ case ATOM_ARG_PS: -+ case ATOM_ARG_WS: -+ case ATOM_ARG_FB: -+ (*ptr)++; -+ break; -+ case ATOM_ARG_IMM: -+ switch (align) { -+ case ATOM_SRC_DWORD: -+ (*ptr) += 4; -+ return; -+ case ATOM_SRC_WORD0: -+ case ATOM_SRC_WORD8: -+ case ATOM_SRC_WORD16: -+ (*ptr) += 2; -+ return; -+ case ATOM_SRC_BYTE0: -+ case ATOM_SRC_BYTE8: -+ case ATOM_SRC_BYTE16: -+ case ATOM_SRC_BYTE24: -+ (*ptr)++; -+ return; -+ } -+ return; -+ } -+} -+ -+static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr) -+{ -+ return atom_get_src_int(ctx, attr, ptr, NULL, 1); -+} -+ -+static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr) -+{ -+ uint32_t val = 0xCDCDCDCD; -+ -+ switch (align) { -+ case ATOM_SRC_DWORD: -+ val = U32(*ptr); -+ (*ptr) += 4; -+ break; -+ case ATOM_SRC_WORD0: -+ case ATOM_SRC_WORD8: -+ case ATOM_SRC_WORD16: -+ val = U16(*ptr); -+ (*ptr) += 2; -+ break; -+ case ATOM_SRC_BYTE0: -+ case ATOM_SRC_BYTE8: -+ case ATOM_SRC_BYTE16: -+ case ATOM_SRC_BYTE24: -+ val = U8(*ptr); -+ (*ptr)++; -+ break; -+ } -+ return val; -+} -+ -+static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr, -+ int *ptr, uint32_t *saved, int print) -+{ -+ return atom_get_src_int(ctx, -+ arg | atom_dst_to_src[(attr >> 3) & -+ 7][(attr >> 6) & 3] << 3, -+ ptr, saved, print); -+} -+ -+static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr) -+{ -+ atom_skip_src_int(ctx, -+ arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & -+ 3] << 3, ptr); -+} -+ -+static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr, -+ int *ptr, uint32_t val, uint32_t saved) -+{ -+ uint32_t align = -+ atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val = -+ val, idx; -+ struct atom_context *gctx = ctx->ctx; -+ old_val &= atom_arg_mask[align] >> atom_arg_shift[align]; -+ val <<= atom_arg_shift[align]; -+ val &= atom_arg_mask[align]; -+ saved &= ~atom_arg_mask[align]; -+ val |= saved; -+ switch (arg) { -+ case ATOM_ARG_REG: -+ idx = U16(*ptr); -+ (*ptr) += 2; -+ DEBUG("REG[0x%04X]", idx); -+ idx += gctx->reg_block; -+ switch (gctx->io_mode) { -+ case ATOM_IO_MM: -+ if (idx == 0) -+ gctx->card->reg_write(gctx->card, idx, -+ val << 2); -+ else -+ gctx->card->reg_write(gctx->card, idx, val); -+ break; -+ case ATOM_IO_PCI: -+ printk(KERN_INFO -+ "PCI registers are not implemented.\n"); -+ return; -+ case ATOM_IO_SYSIO: -+ printk(KERN_INFO -+ "SYSIO registers are not implemented.\n"); -+ return; -+ default: -+ if (!(gctx->io_mode & 0x80)) { -+ printk(KERN_INFO "Bad IO mode.\n"); -+ return; -+ } -+ if (!gctx->iio[gctx->io_mode & 0xFF]) { -+ printk(KERN_INFO -+ "Undefined indirect IO write method %d.\n", -+ gctx->io_mode & 0x7F); -+ return; -+ } -+ atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF], -+ idx, val); -+ } -+ break; -+ case ATOM_ARG_PS: -+ idx = U8(*ptr); -+ (*ptr)++; -+ DEBUG("PS[0x%02X]", idx); -+ ctx->ps[idx] = cpu_to_le32(val); -+ break; -+ case ATOM_ARG_WS: -+ idx = U8(*ptr); -+ (*ptr)++; -+ DEBUG("WS[0x%02X]", idx); -+ switch (idx) { -+ case ATOM_WS_QUOTIENT: -+ gctx->divmul[0] = val; -+ break; -+ case ATOM_WS_REMAINDER: -+ gctx->divmul[1] = val; -+ break; -+ case ATOM_WS_DATAPTR: -+ gctx->data_block = val; -+ break; -+ case ATOM_WS_SHIFT: -+ gctx->shift = val; -+ break; -+ case ATOM_WS_OR_MASK: -+ case ATOM_WS_AND_MASK: -+ break; -+ case ATOM_WS_FB_WINDOW: -+ gctx->fb_base = val; -+ break; -+ case ATOM_WS_ATTRIBUTES: -+ gctx->io_attr = val; -+ break; -+ case ATOM_WS_REGPTR: -+ gctx->reg_block = val; -+ break; -+ default: -+ ctx->ws[idx] = val; -+ } -+ break; -+ case ATOM_ARG_FB: -+ idx = U8(*ptr); -+ (*ptr)++; -+ if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { -+ DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n", -+ gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); -+ } else -+ gctx->scratch[(gctx->fb_base / 4) + idx] = val; -+ DEBUG("FB[0x%02X]", idx); -+ break; -+ case ATOM_ARG_PLL: -+ idx = U8(*ptr); -+ (*ptr)++; -+ DEBUG("PLL[0x%02X]", idx); -+ gctx->card->pll_write(gctx->card, idx, val); -+ break; -+ case ATOM_ARG_MC: -+ idx = U8(*ptr); -+ (*ptr)++; -+ DEBUG("MC[0x%02X]", idx); -+ gctx->card->mc_write(gctx->card, idx, val); -+ return; -+ } -+ switch (align) { -+ case ATOM_SRC_DWORD: -+ DEBUG(".[31:0] <- 0x%08X\n", old_val); -+ break; -+ case ATOM_SRC_WORD0: -+ DEBUG(".[15:0] <- 0x%04X\n", old_val); -+ break; -+ case ATOM_SRC_WORD8: -+ DEBUG(".[23:8] <- 0x%04X\n", old_val); -+ break; -+ case ATOM_SRC_WORD16: -+ DEBUG(".[31:16] <- 0x%04X\n", old_val); -+ break; -+ case ATOM_SRC_BYTE0: -+ DEBUG(".[7:0] <- 0x%02X\n", old_val); -+ break; -+ case ATOM_SRC_BYTE8: -+ DEBUG(".[15:8] <- 0x%02X\n", old_val); -+ break; -+ case ATOM_SRC_BYTE16: -+ DEBUG(".[23:16] <- 0x%02X\n", old_val); -+ break; -+ case ATOM_SRC_BYTE24: -+ DEBUG(".[31:24] <- 0x%02X\n", old_val); -+ break; -+ } -+} -+ -+static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src, saved; -+ int dptr = *ptr; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ dst += src; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src, saved; -+ int dptr = *ptr; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ dst &= src; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ printk("ATOM BIOS beeped!\n"); -+} -+ -+static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ int idx = U8((*ptr)++); -+ int r = 0; -+ -+ if (idx < ATOM_TABLE_NAMES_CNT) -+ SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); -+ else -+ SDEBUG(" table: %d\n", idx); -+ if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) -+ r = amdgpu_atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); -+ if (r) { -+ ctx->abort = true; -+ } -+} -+ -+static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t saved; -+ int dptr = *ptr; -+ attr &= 0x38; -+ attr |= atom_def_dst[attr >> 3] << 6; -+ atom_get_dst(ctx, arg, attr, ptr, &saved, 0); -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, 0, saved); -+} -+ -+static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src; -+ SDEBUG(" src1: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); -+ SDEBUG(" src2: "); -+ src = atom_get_src(ctx, attr, ptr); -+ ctx->ctx->cs_equal = (dst == src); -+ ctx->ctx->cs_above = (dst > src); -+ SDEBUG(" result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE", -+ ctx->ctx->cs_above ? "GT" : "LE"); -+} -+ -+static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ unsigned count = U8((*ptr)++); -+ SDEBUG(" count: %d\n", count); -+ if (arg == ATOM_UNIT_MICROSEC) -+ udelay(count); -+ else if (!drm_can_sleep()) -+ mdelay(count); -+ else -+ msleep(count); -+} -+ -+static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src; -+ SDEBUG(" src1: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); -+ SDEBUG(" src2: "); -+ src = atom_get_src(ctx, attr, ptr); -+ if (src != 0) { -+ ctx->ctx->divmul[0] = dst / src; -+ ctx->ctx->divmul[1] = dst % src; -+ } else { -+ ctx->ctx->divmul[0] = 0; -+ ctx->ctx->divmul[1] = 0; -+ } -+} -+ -+static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ /* functionally, a nop */ -+} -+ -+static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ int execute = 0, target = U16(*ptr); -+ unsigned long cjiffies; -+ -+ (*ptr) += 2; -+ switch (arg) { -+ case ATOM_COND_ABOVE: -+ execute = ctx->ctx->cs_above; -+ break; -+ case ATOM_COND_ABOVEOREQUAL: -+ execute = ctx->ctx->cs_above || ctx->ctx->cs_equal; -+ break; -+ case ATOM_COND_ALWAYS: -+ execute = 1; -+ break; -+ case ATOM_COND_BELOW: -+ execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal); -+ break; -+ case ATOM_COND_BELOWOREQUAL: -+ execute = !ctx->ctx->cs_above; -+ break; -+ case ATOM_COND_EQUAL: -+ execute = ctx->ctx->cs_equal; -+ break; -+ case ATOM_COND_NOTEQUAL: -+ execute = !ctx->ctx->cs_equal; -+ break; -+ } -+ if (arg != ATOM_COND_ALWAYS) -+ SDEBUG(" taken: %s\n", execute ? "yes" : "no"); -+ SDEBUG(" target: 0x%04X\n", target); -+ if (execute) { -+ if (ctx->last_jump == (ctx->start + target)) { -+ cjiffies = jiffies; -+ if (time_after(cjiffies, ctx->last_jump_jiffies)) { -+ cjiffies -= ctx->last_jump_jiffies; -+ if ((jiffies_to_msecs(cjiffies) > 5000)) { -+ DRM_ERROR("atombios stuck in loop for more than 5secs aborting\n"); -+ ctx->abort = true; -+ } -+ } else { -+ /* jiffies wrap around we will just wait a little longer */ -+ ctx->last_jump_jiffies = jiffies; -+ } -+ } else { -+ ctx->last_jump = ctx->start + target; -+ ctx->last_jump_jiffies = jiffies; -+ } -+ *ptr = ctx->start + target; -+ } -+} -+ -+static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, mask, src, saved; -+ int dptr = *ptr; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr); -+ SDEBUG(" mask: 0x%08x", mask); -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ dst &= mask; -+ dst |= src; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t src, saved; -+ int dptr = *ptr; -+ if (((attr >> 3) & 7) != ATOM_SRC_DWORD) -+ atom_get_dst(ctx, arg, attr, ptr, &saved, 0); -+ else { -+ atom_skip_dst(ctx, arg, attr, ptr); -+ saved = 0xCDCDCDCD; -+ } -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, src, saved); -+} -+ -+static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src; -+ SDEBUG(" src1: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); -+ SDEBUG(" src2: "); -+ src = atom_get_src(ctx, attr, ptr); -+ ctx->ctx->divmul[0] = dst * src; -+} -+ -+static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ /* nothing */ -+} -+ -+static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src, saved; -+ int dptr = *ptr; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ dst |= src; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t val = U8((*ptr)++); -+ SDEBUG("POST card output: 0x%02X\n", val); -+} -+ -+static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ printk(KERN_INFO "unimplemented!\n"); -+} -+ -+static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ printk(KERN_INFO "unimplemented!\n"); -+} -+ -+static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ printk(KERN_INFO "unimplemented!\n"); -+} -+ -+static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ int idx = U8(*ptr); -+ (*ptr)++; -+ SDEBUG(" block: %d\n", idx); -+ if (!idx) -+ ctx->ctx->data_block = 0; -+ else if (idx == 255) -+ ctx->ctx->data_block = ctx->start; -+ else -+ ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx); -+ SDEBUG(" base: 0x%04X\n", ctx->ctx->data_block); -+} -+ -+static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ SDEBUG(" fb_base: "); -+ ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr); -+} -+ -+static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ int port; -+ switch (arg) { -+ case ATOM_PORT_ATI: -+ port = U16(*ptr); -+ if (port < ATOM_IO_NAMES_CNT) -+ SDEBUG(" port: %d (%s)\n", port, atom_io_names[port]); -+ else -+ SDEBUG(" port: %d\n", port); -+ if (!port) -+ ctx->ctx->io_mode = ATOM_IO_MM; -+ else -+ ctx->ctx->io_mode = ATOM_IO_IIO | port; -+ (*ptr) += 2; -+ break; -+ case ATOM_PORT_PCI: -+ ctx->ctx->io_mode = ATOM_IO_PCI; -+ (*ptr)++; -+ break; -+ case ATOM_PORT_SYSIO: -+ ctx->ctx->io_mode = ATOM_IO_SYSIO; -+ (*ptr)++; -+ break; -+ } -+} -+ -+static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ ctx->ctx->reg_block = U16(*ptr); -+ (*ptr) += 2; -+ SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block); -+} -+ -+static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++), shift; -+ uint32_t saved, dst; -+ int dptr = *ptr; -+ attr &= 0x38; -+ attr |= atom_def_dst[attr >> 3] << 6; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); -+ SDEBUG(" shift: %d\n", shift); -+ dst <<= shift; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++), shift; -+ uint32_t saved, dst; -+ int dptr = *ptr; -+ attr &= 0x38; -+ attr |= atom_def_dst[attr >> 3] << 6; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); -+ SDEBUG(" shift: %d\n", shift); -+ dst >>= shift; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++), shift; -+ uint32_t saved, dst; -+ int dptr = *ptr; -+ uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ /* op needs to full dst value */ -+ dst = saved; -+ shift = atom_get_src(ctx, attr, ptr); -+ SDEBUG(" shift: %d\n", shift); -+ dst <<= shift; -+ dst &= atom_arg_mask[dst_align]; -+ dst >>= atom_arg_shift[dst_align]; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++), shift; -+ uint32_t saved, dst; -+ int dptr = *ptr; -+ uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ /* op needs to full dst value */ -+ dst = saved; -+ shift = atom_get_src(ctx, attr, ptr); -+ SDEBUG(" shift: %d\n", shift); -+ dst >>= shift; -+ dst &= atom_arg_mask[dst_align]; -+ dst >>= atom_arg_shift[dst_align]; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src, saved; -+ int dptr = *ptr; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ dst -= src; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t src, val, target; -+ SDEBUG(" switch: "); -+ src = atom_get_src(ctx, attr, ptr); -+ while (U16(*ptr) != ATOM_CASE_END) -+ if (U8(*ptr) == ATOM_CASE_MAGIC) { -+ (*ptr)++; -+ SDEBUG(" case: "); -+ val = -+ atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM, -+ ptr); -+ target = U16(*ptr); -+ if (val == src) { -+ SDEBUG(" target: %04X\n", target); -+ *ptr = ctx->start + target; -+ return; -+ } -+ (*ptr) += 2; -+ } else { -+ printk(KERN_INFO "Bad case.\n"); -+ return; -+ } -+ (*ptr) += 2; -+} -+ -+static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src; -+ SDEBUG(" src1: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); -+ SDEBUG(" src2: "); -+ src = atom_get_src(ctx, attr, ptr); -+ ctx->ctx->cs_equal = ((dst & src) == 0); -+ SDEBUG(" result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE"); -+} -+ -+static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ uint8_t attr = U8((*ptr)++); -+ uint32_t dst, src, saved; -+ int dptr = *ptr; -+ SDEBUG(" dst: "); -+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); -+ SDEBUG(" src: "); -+ src = atom_get_src(ctx, attr, ptr); -+ dst ^= src; -+ SDEBUG(" dst: "); -+ atom_put_dst(ctx, arg, attr, &dptr, dst, saved); -+} -+ -+static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg) -+{ -+ printk(KERN_INFO "unimplemented!\n"); -+} -+ -+static struct { -+ void (*func) (atom_exec_context *, int *, int); -+ int arg; -+} opcode_table[ATOM_OP_CNT] = { -+ { -+ NULL, 0}, { -+ atom_op_move, ATOM_ARG_REG}, { -+ atom_op_move, ATOM_ARG_PS}, { -+ atom_op_move, ATOM_ARG_WS}, { -+ atom_op_move, ATOM_ARG_FB}, { -+ atom_op_move, ATOM_ARG_PLL}, { -+ atom_op_move, ATOM_ARG_MC}, { -+ atom_op_and, ATOM_ARG_REG}, { -+ atom_op_and, ATOM_ARG_PS}, { -+ atom_op_and, ATOM_ARG_WS}, { -+ atom_op_and, ATOM_ARG_FB}, { -+ atom_op_and, ATOM_ARG_PLL}, { -+ atom_op_and, ATOM_ARG_MC}, { -+ atom_op_or, ATOM_ARG_REG}, { -+ atom_op_or, ATOM_ARG_PS}, { -+ atom_op_or, ATOM_ARG_WS}, { -+ atom_op_or, ATOM_ARG_FB}, { -+ atom_op_or, ATOM_ARG_PLL}, { -+ atom_op_or, ATOM_ARG_MC}, { -+ atom_op_shift_left, ATOM_ARG_REG}, { -+ atom_op_shift_left, ATOM_ARG_PS}, { -+ atom_op_shift_left, ATOM_ARG_WS}, { -+ atom_op_shift_left, ATOM_ARG_FB}, { -+ atom_op_shift_left, ATOM_ARG_PLL}, { -+ atom_op_shift_left, ATOM_ARG_MC}, { -+ atom_op_shift_right, ATOM_ARG_REG}, { -+ atom_op_shift_right, ATOM_ARG_PS}, { -+ atom_op_shift_right, ATOM_ARG_WS}, { -+ atom_op_shift_right, ATOM_ARG_FB}, { -+ atom_op_shift_right, ATOM_ARG_PLL}, { -+ atom_op_shift_right, ATOM_ARG_MC}, { -+ atom_op_mul, ATOM_ARG_REG}, { -+ atom_op_mul, ATOM_ARG_PS}, { -+ atom_op_mul, ATOM_ARG_WS}, { -+ atom_op_mul, ATOM_ARG_FB}, { -+ atom_op_mul, ATOM_ARG_PLL}, { -+ atom_op_mul, ATOM_ARG_MC}, { -+ atom_op_div, ATOM_ARG_REG}, { -+ atom_op_div, ATOM_ARG_PS}, { -+ atom_op_div, ATOM_ARG_WS}, { -+ atom_op_div, ATOM_ARG_FB}, { -+ atom_op_div, ATOM_ARG_PLL}, { -+ atom_op_div, ATOM_ARG_MC}, { -+ atom_op_add, ATOM_ARG_REG}, { -+ atom_op_add, ATOM_ARG_PS}, { -+ atom_op_add, ATOM_ARG_WS}, { -+ atom_op_add, ATOM_ARG_FB}, { -+ atom_op_add, ATOM_ARG_PLL}, { -+ atom_op_add, ATOM_ARG_MC}, { -+ atom_op_sub, ATOM_ARG_REG}, { -+ atom_op_sub, ATOM_ARG_PS}, { -+ atom_op_sub, ATOM_ARG_WS}, { -+ atom_op_sub, ATOM_ARG_FB}, { -+ atom_op_sub, ATOM_ARG_PLL}, { -+ atom_op_sub, ATOM_ARG_MC}, { -+ atom_op_setport, ATOM_PORT_ATI}, { -+ atom_op_setport, ATOM_PORT_PCI}, { -+ atom_op_setport, ATOM_PORT_SYSIO}, { -+ atom_op_setregblock, 0}, { -+ atom_op_setfbbase, 0}, { -+ atom_op_compare, ATOM_ARG_REG}, { -+ atom_op_compare, ATOM_ARG_PS}, { -+ atom_op_compare, ATOM_ARG_WS}, { -+ atom_op_compare, ATOM_ARG_FB}, { -+ atom_op_compare, ATOM_ARG_PLL}, { -+ atom_op_compare, ATOM_ARG_MC}, { -+ atom_op_switch, 0}, { -+ atom_op_jump, ATOM_COND_ALWAYS}, { -+ atom_op_jump, ATOM_COND_EQUAL}, { -+ atom_op_jump, ATOM_COND_BELOW}, { -+ atom_op_jump, ATOM_COND_ABOVE}, { -+ atom_op_jump, ATOM_COND_BELOWOREQUAL}, { -+ atom_op_jump, ATOM_COND_ABOVEOREQUAL}, { -+ atom_op_jump, ATOM_COND_NOTEQUAL}, { -+ atom_op_test, ATOM_ARG_REG}, { -+ atom_op_test, ATOM_ARG_PS}, { -+ atom_op_test, ATOM_ARG_WS}, { -+ atom_op_test, ATOM_ARG_FB}, { -+ atom_op_test, ATOM_ARG_PLL}, { -+ atom_op_test, ATOM_ARG_MC}, { -+ atom_op_delay, ATOM_UNIT_MILLISEC}, { -+ atom_op_delay, ATOM_UNIT_MICROSEC}, { -+ atom_op_calltable, 0}, { -+ atom_op_repeat, 0}, { -+ atom_op_clear, ATOM_ARG_REG}, { -+ atom_op_clear, ATOM_ARG_PS}, { -+ atom_op_clear, ATOM_ARG_WS}, { -+ atom_op_clear, ATOM_ARG_FB}, { -+ atom_op_clear, ATOM_ARG_PLL}, { -+ atom_op_clear, ATOM_ARG_MC}, { -+ atom_op_nop, 0}, { -+ atom_op_eot, 0}, { -+ atom_op_mask, ATOM_ARG_REG}, { -+ atom_op_mask, ATOM_ARG_PS}, { -+ atom_op_mask, ATOM_ARG_WS}, { -+ atom_op_mask, ATOM_ARG_FB}, { -+ atom_op_mask, ATOM_ARG_PLL}, { -+ atom_op_mask, ATOM_ARG_MC}, { -+ atom_op_postcard, 0}, { -+ atom_op_beep, 0}, { -+ atom_op_savereg, 0}, { -+ atom_op_restorereg, 0}, { -+ atom_op_setdatablock, 0}, { -+ atom_op_xor, ATOM_ARG_REG}, { -+ atom_op_xor, ATOM_ARG_PS}, { -+ atom_op_xor, ATOM_ARG_WS}, { -+ atom_op_xor, ATOM_ARG_FB}, { -+ atom_op_xor, ATOM_ARG_PLL}, { -+ atom_op_xor, ATOM_ARG_MC}, { -+ atom_op_shl, ATOM_ARG_REG}, { -+ atom_op_shl, ATOM_ARG_PS}, { -+ atom_op_shl, ATOM_ARG_WS}, { -+ atom_op_shl, ATOM_ARG_FB}, { -+ atom_op_shl, ATOM_ARG_PLL}, { -+ atom_op_shl, ATOM_ARG_MC}, { -+ atom_op_shr, ATOM_ARG_REG}, { -+ atom_op_shr, ATOM_ARG_PS}, { -+ atom_op_shr, ATOM_ARG_WS}, { -+ atom_op_shr, ATOM_ARG_FB}, { -+ atom_op_shr, ATOM_ARG_PLL}, { -+ atom_op_shr, ATOM_ARG_MC}, { -+atom_op_debug, 0},}; -+ -+static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) -+{ -+ int base = CU16(ctx->cmd_table + 4 + 2 * index); -+ int len, ws, ps, ptr; -+ unsigned char op; -+ atom_exec_context ectx; -+ int ret = 0; -+ -+ if (!base) -+ return -EINVAL; -+ -+ len = CU16(base + ATOM_CT_SIZE_PTR); -+ ws = CU8(base + ATOM_CT_WS_PTR); -+ ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK; -+ ptr = base + ATOM_CT_CODE_PTR; -+ -+ SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps); -+ -+ ectx.ctx = ctx; -+ ectx.ps_shift = ps / 4; -+ ectx.start = base; -+ ectx.ps = params; -+ ectx.abort = false; -+ ectx.last_jump = 0; -+ if (ws) -+ ectx.ws = kzalloc(4 * ws, GFP_KERNEL); -+ else -+ ectx.ws = NULL; -+ -+ debug_depth++; -+ while (1) { -+ op = CU8(ptr++); -+ if (op < ATOM_OP_NAMES_CNT) -+ SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); -+ else -+ SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); -+ if (ectx.abort) { -+ DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n", -+ base, len, ws, ps, ptr - 1); -+ ret = -EINVAL; -+ goto free; -+ } -+ -+ if (op < ATOM_OP_CNT && op > 0) -+ opcode_table[op].func(&ectx, &ptr, -+ opcode_table[op].arg); -+ else -+ break; -+ -+ if (op == ATOM_OP_EOT) -+ break; -+ } -+ debug_depth--; -+ SDEBUG("<<\n"); -+ -+free: -+ if (ws) -+ kfree(ectx.ws); -+ return ret; -+} -+ -+int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) -+{ -+ int r; -+ -+ mutex_lock(&ctx->mutex); -+ /* reset data block */ -+ ctx->data_block = 0; -+ /* reset reg block */ -+ ctx->reg_block = 0; -+ /* reset fb window */ -+ ctx->fb_base = 0; -+ /* reset io mode */ -+ ctx->io_mode = ATOM_IO_MM; -+ /* reset divmul */ -+ ctx->divmul[0] = 0; -+ ctx->divmul[1] = 0; -+ r = amdgpu_atom_execute_table_locked(ctx, index, params); -+ mutex_unlock(&ctx->mutex); -+ return r; -+} -+ -+static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; -+ -+static void atom_index_iio(struct atom_context *ctx, int base) -+{ -+ ctx->iio = kzalloc(2 * 256, GFP_KERNEL); -+ if (!ctx->iio) -+ return; -+ while (CU8(base) == ATOM_IIO_START) { -+ ctx->iio[CU8(base + 1)] = base + 2; -+ base += 2; -+ while (CU8(base) != ATOM_IIO_END) -+ base += atom_iio_len[CU8(base)]; -+ base += 3; -+ } -+} -+ -+struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios) -+{ -+ int base; -+ struct atom_context *ctx = -+ kzalloc(sizeof(struct atom_context), GFP_KERNEL); -+ char *str; -+ char name[512]; -+ int i; -+ -+ if (!ctx) -+ return NULL; -+ -+ ctx->card = card; -+ ctx->bios = bios; -+ -+ if (CU16(0) != ATOM_BIOS_MAGIC) { -+ printk(KERN_INFO "Invalid BIOS magic.\n"); -+ kfree(ctx); -+ return NULL; -+ } -+ if (strncmp -+ (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC, -+ strlen(ATOM_ATI_MAGIC))) { -+ printk(KERN_INFO "Invalid ATI magic.\n"); -+ kfree(ctx); -+ return NULL; -+ } -+ -+ base = CU16(ATOM_ROM_TABLE_PTR); -+ if (strncmp -+ (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC, -+ strlen(ATOM_ROM_MAGIC))) { -+ printk(KERN_INFO "Invalid ATOM magic.\n"); -+ kfree(ctx); -+ return NULL; -+ } -+ -+ ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR); -+ ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR); -+ atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4); -+ if (!ctx->iio) { -+ amdgpu_atom_destroy(ctx); -+ return NULL; -+ } -+ -+ str = CSTR(CU16(base + ATOM_ROM_MSG_PTR)); -+ while (*str && ((*str == '\n') || (*str == '\r'))) -+ str++; -+ /* name string isn't always 0 terminated */ -+ for (i = 0; i < 511; i++) { -+ name[i] = str[i]; -+ if (name[i] < '.' || name[i] > 'z') { -+ name[i] = 0; -+ break; -+ } -+ } -+ printk(KERN_INFO "ATOM BIOS: %s\n", name); -+ -+ return ctx; -+} -+ -+int amdgpu_atom_asic_init(struct atom_context *ctx) -+{ -+ int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); -+ uint32_t ps[16]; -+ int ret; -+ -+ memset(ps, 0, 64); -+ -+ ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR)); -+ ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR)); -+ if (!ps[0] || !ps[1]) -+ return 1; -+ -+ if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) -+ return 1; -+ ret = amdgpu_atom_execute_table(ctx, ATOM_CMD_INIT, ps); -+ if (ret) -+ return ret; -+ -+ memset(ps, 0, 64); -+ -+ return ret; -+} -+ -+void amdgpu_atom_destroy(struct atom_context *ctx) -+{ -+ kfree(ctx->iio); -+ kfree(ctx); -+} -+ -+bool amdgpu_atom_parse_data_header(struct atom_context *ctx, int index, -+ uint16_t * size, uint8_t * frev, uint8_t * crev, -+ uint16_t * data_start) -+{ -+ int offset = index * 2 + 4; -+ int idx = CU16(ctx->data_table + offset); -+ u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4); -+ -+ if (!mdt[index]) -+ return false; -+ -+ if (size) -+ *size = CU16(idx); -+ if (frev) -+ *frev = CU8(idx + 2); -+ if (crev) -+ *crev = CU8(idx + 3); -+ *data_start = idx; -+ return true; -+} -+ -+bool amdgpu_atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, -+ uint8_t * crev) -+{ -+ int offset = index * 2 + 4; -+ int idx = CU16(ctx->cmd_table + offset); -+ u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4); -+ -+ if (!mct[index]) -+ return false; -+ -+ if (frev) -+ *frev = CU8(idx + 2); -+ if (crev) -+ *crev = CU8(idx + 3); -+ return true; -+} -+ -+int amdgpu_atom_allocate_fb_scratch(struct atom_context *ctx) -+{ -+ int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware); -+ uint16_t data_offset; -+ int usage_bytes = 0; -+ struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage; -+ -+ if (amdgpu_atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) { -+ firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); -+ -+ DRM_DEBUG("atom firmware requested %08x %dkb\n", -+ le32_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware), -+ le16_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb)); -+ -+ usage_bytes = le16_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb) * 1024; -+ } -+ ctx->scratch_size_bytes = 0; -+ if (usage_bytes == 0) -+ usage_bytes = 20 * 1024; -+ /* allocate some scratch memory */ -+ ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL); -+ if (!ctx->scratch) -+ return -ENOMEM; -+ ctx->scratch_size_bytes = usage_bytes; -+ return 0; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/atom.h b/drivers/gpu/drm/amd/amdgpu/atom.h -new file mode 100644 -index 0000000..09d0f82 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atom.h -@@ -0,0 +1,159 @@ -+/* -+ * Copyright 2008 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. -+ * -+ * Author: Stanislaw Skowronek -+ */ -+ -+#ifndef ATOM_H -+#define ATOM_H -+ -+#include <linux/types.h> -+#include <drm/drmP.h> -+ -+#define ATOM_BIOS_MAGIC 0xAA55 -+#define ATOM_ATI_MAGIC_PTR 0x30 -+#define ATOM_ATI_MAGIC " 761295520" -+#define ATOM_ROM_TABLE_PTR 0x48 -+ -+#define ATOM_ROM_MAGIC "ATOM" -+#define ATOM_ROM_MAGIC_PTR 4 -+ -+#define ATOM_ROM_MSG_PTR 0x10 -+#define ATOM_ROM_CMD_PTR 0x1E -+#define ATOM_ROM_DATA_PTR 0x20 -+ -+#define ATOM_CMD_INIT 0 -+#define ATOM_CMD_SETSCLK 0x0A -+#define ATOM_CMD_SETMCLK 0x0B -+#define ATOM_CMD_SETPCLK 0x0C -+#define ATOM_CMD_SPDFANCNTL 0x39 -+ -+#define ATOM_DATA_FWI_PTR 0xC -+#define ATOM_DATA_IIO_PTR 0x32 -+ -+#define ATOM_FWI_DEFSCLK_PTR 8 -+#define ATOM_FWI_DEFMCLK_PTR 0xC -+#define ATOM_FWI_MAXSCLK_PTR 0x24 -+#define ATOM_FWI_MAXMCLK_PTR 0x28 -+ -+#define ATOM_CT_SIZE_PTR 0 -+#define ATOM_CT_WS_PTR 4 -+#define ATOM_CT_PS_PTR 5 -+#define ATOM_CT_PS_MASK 0x7F -+#define ATOM_CT_CODE_PTR 6 -+ -+#define ATOM_OP_CNT 123 -+#define ATOM_OP_EOT 91 -+ -+#define ATOM_CASE_MAGIC 0x63 -+#define ATOM_CASE_END 0x5A5A -+ -+#define ATOM_ARG_REG 0 -+#define ATOM_ARG_PS 1 -+#define ATOM_ARG_WS 2 -+#define ATOM_ARG_FB 3 -+#define ATOM_ARG_ID 4 -+#define ATOM_ARG_IMM 5 -+#define ATOM_ARG_PLL 6 -+#define ATOM_ARG_MC 7 -+ -+#define ATOM_SRC_DWORD 0 -+#define ATOM_SRC_WORD0 1 -+#define ATOM_SRC_WORD8 2 -+#define ATOM_SRC_WORD16 3 -+#define ATOM_SRC_BYTE0 4 -+#define ATOM_SRC_BYTE8 5 -+#define ATOM_SRC_BYTE16 6 -+#define ATOM_SRC_BYTE24 7 -+ -+#define ATOM_WS_QUOTIENT 0x40 -+#define ATOM_WS_REMAINDER 0x41 -+#define ATOM_WS_DATAPTR 0x42 -+#define ATOM_WS_SHIFT 0x43 -+#define ATOM_WS_OR_MASK 0x44 -+#define ATOM_WS_AND_MASK 0x45 -+#define ATOM_WS_FB_WINDOW 0x46 -+#define ATOM_WS_ATTRIBUTES 0x47 -+#define ATOM_WS_REGPTR 0x48 -+ -+#define ATOM_IIO_NOP 0 -+#define ATOM_IIO_START 1 -+#define ATOM_IIO_READ 2 -+#define ATOM_IIO_WRITE 3 -+#define ATOM_IIO_CLEAR 4 -+#define ATOM_IIO_SET 5 -+#define ATOM_IIO_MOVE_INDEX 6 -+#define ATOM_IIO_MOVE_ATTR 7 -+#define ATOM_IIO_MOVE_DATA 8 -+#define ATOM_IIO_END 9 -+ -+#define ATOM_IO_MM 0 -+#define ATOM_IO_PCI 1 -+#define ATOM_IO_SYSIO 2 -+#define ATOM_IO_IIO 0x80 -+ -+struct card_info { -+ struct drm_device *dev; -+ void (* reg_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ -+ uint32_t (* reg_read)(struct card_info *, uint32_t); /* filled by driver */ -+ void (* ioreg_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ -+ uint32_t (* ioreg_read)(struct card_info *, uint32_t); /* filled by driver */ -+ void (* mc_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ -+ uint32_t (* mc_read)(struct card_info *, uint32_t); /* filled by driver */ -+ void (* pll_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ -+ uint32_t (* pll_read)(struct card_info *, uint32_t); /* filled by driver */ -+}; -+ -+struct atom_context { -+ struct card_info *card; -+ struct mutex mutex; -+ void *bios; -+ uint32_t cmd_table, data_table; -+ uint16_t *iio; -+ -+ uint16_t data_block; -+ uint32_t fb_base; -+ uint32_t divmul[2]; -+ uint16_t io_attr; -+ uint16_t reg_block; -+ uint8_t shift; -+ int cs_equal, cs_above; -+ int io_mode; -+ uint32_t *scratch; -+ int scratch_size_bytes; -+}; -+ -+extern int amdgpu_atom_debug; -+ -+struct atom_context *amdgpu_atom_parse(struct card_info *, void *); -+int amdgpu_atom_execute_table(struct atom_context *, int, uint32_t *); -+int amdgpu_atom_asic_init(struct atom_context *); -+void amdgpu_atom_destroy(struct atom_context *); -+bool amdgpu_atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, -+ uint8_t *frev, uint8_t *crev, uint16_t *data_start); -+bool amdgpu_atom_parse_cmd_header(struct atom_context *ctx, int index, -+ uint8_t *frev, uint8_t *crev); -+int amdgpu_atom_allocate_fb_scratch(struct atom_context *ctx); -+#include "atom-types.h" -+#include "atombios.h" -+#include "ObjectID.h" -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c b/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c -new file mode 100644 -index 0000000..49aa350 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_crtc.c -@@ -0,0 +1,807 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <drm/drmP.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/amdgpu_drm.h> -+#include <drm/drm_fixed.h> -+#include "amdgpu.h" -+#include "atom.h" -+#include "atom-bits.h" -+#include "atombios_encoders.h" -+#include "amdgpu_atombios.h" -+#include "amdgpu_pll.h" -+#include "amdgpu_connectors.h" -+ -+void amdgpu_atombios_crtc_overscan_setup(struct drm_crtc *crtc, -+ struct drm_display_mode *mode, -+ struct drm_display_mode *adjusted_mode) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ SET_CRTC_OVERSCAN_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_OverScan); -+ int a1, a2; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucCRTC = amdgpu_crtc->crtc_id; -+ -+ switch (amdgpu_crtc->rmx_type) { -+ case RMX_CENTER: -+ args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2); -+ args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2); -+ args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2); -+ args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2); -+ break; -+ case RMX_ASPECT: -+ a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay; -+ a2 = adjusted_mode->crtc_vdisplay * mode->crtc_hdisplay; -+ -+ if (a1 > a2) { -+ args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); -+ args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); -+ } else if (a2 > a1) { -+ args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); -+ args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); -+ } -+ break; -+ case RMX_FULL: -+ default: -+ args.usOverscanRight = cpu_to_le16(amdgpu_crtc->h_border); -+ args.usOverscanLeft = cpu_to_le16(amdgpu_crtc->h_border); -+ args.usOverscanBottom = cpu_to_le16(amdgpu_crtc->v_border); -+ args.usOverscanTop = cpu_to_le16(amdgpu_crtc->v_border); -+ break; -+ } -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_scaler_setup(struct drm_crtc *crtc) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ ENABLE_SCALER_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucScaler = amdgpu_crtc->crtc_id; -+ -+ switch (amdgpu_crtc->rmx_type) { -+ case RMX_FULL: -+ args.ucEnable = ATOM_SCALER_EXPANSION; -+ break; -+ case RMX_CENTER: -+ args.ucEnable = ATOM_SCALER_CENTER; -+ break; -+ case RMX_ASPECT: -+ args.ucEnable = ATOM_SCALER_EXPANSION; -+ break; -+ default: -+ args.ucEnable = ATOM_SCALER_DISABLE; -+ break; -+ } -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_lock(struct drm_crtc *crtc, int lock) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ int index = -+ GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters); -+ ENABLE_CRTC_PS_ALLOCATION args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucCRTC = amdgpu_crtc->crtc_id; -+ args.ucEnable = lock; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_enable(struct drm_crtc *crtc, int state) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC); -+ ENABLE_CRTC_PS_ALLOCATION args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucCRTC = amdgpu_crtc->crtc_id; -+ args.ucEnable = state; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_blank(struct drm_crtc *crtc, int state) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC); -+ BLANK_CRTC_PS_ALLOCATION args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucCRTC = amdgpu_crtc->crtc_id; -+ args.ucBlanking = state; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_powergate(struct drm_crtc *crtc, int state) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating); -+ ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucDispPipeId = amdgpu_crtc->crtc_id; -+ args.ucEnable = state; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_powergate_init(struct amdgpu_device *adev) -+{ -+ int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating); -+ ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.ucEnable = ATOM_INIT; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+void amdgpu_atombios_crtc_set_dtd_timing(struct drm_crtc *crtc, -+ struct drm_display_mode *mode) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ SET_CRTC_USING_DTD_TIMING_PARAMETERS args; -+ int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming); -+ u16 misc = 0; -+ -+ memset(&args, 0, sizeof(args)); -+ args.usH_Size = cpu_to_le16(mode->crtc_hdisplay - (amdgpu_crtc->h_border * 2)); -+ args.usH_Blanking_Time = -+ cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay + (amdgpu_crtc->h_border * 2)); -+ args.usV_Size = cpu_to_le16(mode->crtc_vdisplay - (amdgpu_crtc->v_border * 2)); -+ args.usV_Blanking_Time = -+ cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay + (amdgpu_crtc->v_border * 2)); -+ args.usH_SyncOffset = -+ cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay + amdgpu_crtc->h_border); -+ args.usH_SyncWidth = -+ cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); -+ args.usV_SyncOffset = -+ cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay + amdgpu_crtc->v_border); -+ args.usV_SyncWidth = -+ cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); -+ args.ucH_Border = amdgpu_crtc->h_border; -+ args.ucV_Border = amdgpu_crtc->v_border; -+ -+ if (mode->flags & DRM_MODE_FLAG_NVSYNC) -+ misc |= ATOM_VSYNC_POLARITY; -+ if (mode->flags & DRM_MODE_FLAG_NHSYNC) -+ misc |= ATOM_HSYNC_POLARITY; -+ if (mode->flags & DRM_MODE_FLAG_CSYNC) -+ misc |= ATOM_COMPOSITESYNC; -+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) -+ misc |= ATOM_INTERLACE; -+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN) -+ misc |= ATOM_DOUBLE_CLOCK_MODE; -+ -+ args.susModeMiscInfo.usAccess = cpu_to_le16(misc); -+ args.ucCRTC = amdgpu_crtc->crtc_id; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+union atom_enable_ss { -+ ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1; -+ ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2; -+ ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3; -+}; -+ -+static void amdgpu_atombios_crtc_program_ss(struct amdgpu_device *adev, -+ int enable, -+ int pll_id, -+ int crtc_id, -+ struct amdgpu_atom_ss *ss) -+{ -+ unsigned i; -+ int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); -+ union atom_enable_ss args; -+ -+ if (enable) { -+ /* Don't mess with SS if percentage is 0 or external ss. -+ * SS is already disabled previously, and disabling it -+ * again can cause display problems if the pll is already -+ * programmed. -+ */ -+ if (ss->percentage == 0) -+ return; -+ if (ss->type & ATOM_EXTERNAL_SS_MASK) -+ return; -+ } else { -+ for (i = 0; i < adev->mode_info.num_crtc; i++) { -+ if (adev->mode_info.crtcs[i] && -+ adev->mode_info.crtcs[i]->enabled && -+ i != crtc_id && -+ pll_id == adev->mode_info.crtcs[i]->pll_id) { -+ /* one other crtc is using this pll don't turn -+ * off spread spectrum as it might turn off -+ * display on active crtc -+ */ -+ return; -+ } -+ } -+ } -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.v3.usSpreadSpectrumAmountFrac = cpu_to_le16(0); -+ args.v3.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK; -+ switch (pll_id) { -+ case ATOM_PPLL1: -+ args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL; -+ break; -+ case ATOM_PPLL2: -+ args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL; -+ break; -+ case ATOM_DCPLL: -+ args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL; -+ break; -+ case ATOM_PPLL_INVALID: -+ return; -+ } -+ args.v3.usSpreadSpectrumAmount = cpu_to_le16(ss->amount); -+ args.v3.usSpreadSpectrumStep = cpu_to_le16(ss->step); -+ args.v3.ucEnable = enable; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+union adjust_pixel_clock { -+ ADJUST_DISPLAY_PLL_PS_ALLOCATION v1; -+ ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3; -+}; -+ -+static u32 amdgpu_atombios_crtc_adjust_pll(struct drm_crtc *crtc, -+ struct drm_display_mode *mode) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct drm_encoder *encoder = amdgpu_crtc->encoder; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder); -+ u32 adjusted_clock = mode->clock; -+ int encoder_mode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ u32 dp_clock = mode->clock; -+ u32 clock = mode->clock; -+ int bpc = amdgpu_crtc->bpc; -+ bool is_duallink = amdgpu_dig_monitor_is_duallink(encoder, mode->clock); -+ union adjust_pixel_clock args; -+ u8 frev, crev; -+ int index; -+ -+ amdgpu_crtc->pll_flags = AMDGPU_PLL_USE_FRAC_FB_DIV; -+ -+ if ((amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || -+ (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { -+ if (connector) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector = -+ amdgpu_connector->con_priv; -+ -+ dp_clock = dig_connector->dp_clock; -+ } -+ } -+ -+ /* use recommended ref_div for ss */ -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { -+ if (amdgpu_crtc->ss_enabled) { -+ if (amdgpu_crtc->ss.refdiv) { -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_REF_DIV; -+ amdgpu_crtc->pll_reference_div = amdgpu_crtc->ss.refdiv; -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_FRAC_FB_DIV; -+ } -+ } -+ } -+ -+ /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */ -+ if (amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1) -+ adjusted_clock = mode->clock * 2; -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_PREFER_CLOSEST_LOWER; -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_IS_LCD; -+ -+ -+ /* adjust pll for deep color modes */ -+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI) { -+ switch (bpc) { -+ case 8: -+ default: -+ break; -+ case 10: -+ clock = (clock * 5) / 4; -+ break; -+ case 12: -+ clock = (clock * 3) / 2; -+ break; -+ case 16: -+ clock = clock * 2; -+ break; -+ } -+ } -+ -+ /* DCE3+ has an AdjustDisplayPll that will adjust the pixel clock -+ * accordingly based on the encoder/transmitter to work around -+ * special hw requirements. -+ */ -+ index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, -+ &crev)) -+ return adjusted_clock; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 1: -+ case 2: -+ args.v1.usPixelClock = cpu_to_le16(clock / 10); -+ args.v1.ucTransmitterID = amdgpu_encoder->encoder_id; -+ args.v1.ucEncodeMode = encoder_mode; -+ if (amdgpu_crtc->ss_enabled && amdgpu_crtc->ss.percentage) -+ args.v1.ucConfig |= -+ ADJUST_DISPLAY_CONFIG_SS_ENABLE; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, -+ index, (uint32_t *)&args); -+ adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10; -+ break; -+ case 3: -+ args.v3.sInput.usPixelClock = cpu_to_le16(clock / 10); -+ args.v3.sInput.ucTransmitterID = amdgpu_encoder->encoder_id; -+ args.v3.sInput.ucEncodeMode = encoder_mode; -+ args.v3.sInput.ucDispPllConfig = 0; -+ if (amdgpu_crtc->ss_enabled && amdgpu_crtc->ss.percentage) -+ args.v3.sInput.ucDispPllConfig |= -+ DISPPLL_CONFIG_SS_ENABLE; -+ if (ENCODER_MODE_IS_DP(encoder_mode)) { -+ args.v3.sInput.ucDispPllConfig |= -+ DISPPLL_CONFIG_COHERENT_MODE; -+ /* 16200 or 27000 */ -+ args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10); -+ } else if (amdgpu_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ if (dig->coherent_mode) -+ args.v3.sInput.ucDispPllConfig |= -+ DISPPLL_CONFIG_COHERENT_MODE; -+ if (is_duallink) -+ args.v3.sInput.ucDispPllConfig |= -+ DISPPLL_CONFIG_DUAL_LINK; -+ } -+ if (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) != -+ ENCODER_OBJECT_ID_NONE) -+ args.v3.sInput.ucExtTransmitterID = -+ amdgpu_encoder_get_dp_bridge_encoder_id(encoder); -+ else -+ args.v3.sInput.ucExtTransmitterID = 0; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, -+ index, (uint32_t *)&args); -+ adjusted_clock = le32_to_cpu(args.v3.sOutput.ulDispPllFreq) * 10; -+ if (args.v3.sOutput.ucRefDiv) { -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_FRAC_FB_DIV; -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_REF_DIV; -+ amdgpu_crtc->pll_reference_div = args.v3.sOutput.ucRefDiv; -+ } -+ if (args.v3.sOutput.ucPostDiv) { -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_FRAC_FB_DIV; -+ amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_POST_DIV; -+ amdgpu_crtc->pll_post_div = args.v3.sOutput.ucPostDiv; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d %d\n", frev, crev); -+ return adjusted_clock; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d %d\n", frev, crev); -+ return adjusted_clock; -+ } -+ -+ return adjusted_clock; -+} -+ -+union set_pixel_clock { -+ SET_PIXEL_CLOCK_PS_ALLOCATION base; -+ PIXEL_CLOCK_PARAMETERS v1; -+ PIXEL_CLOCK_PARAMETERS_V2 v2; -+ PIXEL_CLOCK_PARAMETERS_V3 v3; -+ PIXEL_CLOCK_PARAMETERS_V5 v5; -+ PIXEL_CLOCK_PARAMETERS_V6 v6; -+}; -+ -+/* on DCE5, make sure the voltage is high enough to support the -+ * required disp clk. -+ */ -+void amdgpu_atombios_crtc_set_disp_eng_pll(struct amdgpu_device *adev, -+ u32 dispclk) -+{ -+ u8 frev, crev; -+ int index; -+ union set_pixel_clock args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, -+ &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 5: -+ /* if the default dcpll clock is specified, -+ * SetPixelClock provides the dividers -+ */ -+ args.v5.ucCRTC = ATOM_CRTC_INVALID; -+ args.v5.usPixelClock = cpu_to_le16(dispclk); -+ args.v5.ucPpll = ATOM_DCPLL; -+ break; -+ case 6: -+ /* if the default dcpll clock is specified, -+ * SetPixelClock provides the dividers -+ */ -+ args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk); -+ args.v6.ucPpll = ATOM_EXT_PLL1; -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d %d\n", frev, crev); -+ return; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d %d\n", frev, crev); -+ return; -+ } -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+static bool is_pixel_clock_source_from_pll(u32 encoder_mode, int pll_id) -+{ -+ if (ENCODER_MODE_IS_DP(encoder_mode)) { -+ if (pll_id < ATOM_EXT_PLL1) -+ return true; -+ else -+ return false; -+ } else { -+ return true; -+ } -+} -+ -+void amdgpu_atombios_crtc_program_pll(struct drm_crtc *crtc, -+ u32 crtc_id, -+ int pll_id, -+ u32 encoder_mode, -+ u32 encoder_id, -+ u32 clock, -+ u32 ref_div, -+ u32 fb_div, -+ u32 frac_fb_div, -+ u32 post_div, -+ int bpc, -+ bool ss_enabled, -+ struct amdgpu_atom_ss *ss) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ u8 frev, crev; -+ int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); -+ union set_pixel_clock args; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, -+ &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 1: -+ if (clock == ATOM_DISABLE) -+ return; -+ args.v1.usPixelClock = cpu_to_le16(clock / 10); -+ args.v1.usRefDiv = cpu_to_le16(ref_div); -+ args.v1.usFbDiv = cpu_to_le16(fb_div); -+ args.v1.ucFracFbDiv = frac_fb_div; -+ args.v1.ucPostDiv = post_div; -+ args.v1.ucPpll = pll_id; -+ args.v1.ucCRTC = crtc_id; -+ args.v1.ucRefDivSrc = 1; -+ break; -+ case 2: -+ args.v2.usPixelClock = cpu_to_le16(clock / 10); -+ args.v2.usRefDiv = cpu_to_le16(ref_div); -+ args.v2.usFbDiv = cpu_to_le16(fb_div); -+ args.v2.ucFracFbDiv = frac_fb_div; -+ args.v2.ucPostDiv = post_div; -+ args.v2.ucPpll = pll_id; -+ args.v2.ucCRTC = crtc_id; -+ args.v2.ucRefDivSrc = 1; -+ break; -+ case 3: -+ args.v3.usPixelClock = cpu_to_le16(clock / 10); -+ args.v3.usRefDiv = cpu_to_le16(ref_div); -+ args.v3.usFbDiv = cpu_to_le16(fb_div); -+ args.v3.ucFracFbDiv = frac_fb_div; -+ args.v3.ucPostDiv = post_div; -+ args.v3.ucPpll = pll_id; -+ if (crtc_id == ATOM_CRTC2) -+ args.v3.ucMiscInfo = PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2; -+ else -+ args.v3.ucMiscInfo = PIXEL_CLOCK_MISC_CRTC_SEL_CRTC1; -+ if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) -+ args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; -+ args.v3.ucTransmitterId = encoder_id; -+ args.v3.ucEncoderMode = encoder_mode; -+ break; -+ case 5: -+ args.v5.ucCRTC = crtc_id; -+ args.v5.usPixelClock = cpu_to_le16(clock / 10); -+ args.v5.ucRefDiv = ref_div; -+ args.v5.usFbDiv = cpu_to_le16(fb_div); -+ args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000); -+ args.v5.ucPostDiv = post_div; -+ args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ -+ if ((ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) && -+ (pll_id < ATOM_EXT_PLL1)) -+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC; -+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI) { -+ switch (bpc) { -+ case 8: -+ default: -+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; -+ break; -+ case 10: -+ /* yes this is correct, the atom define is wrong */ -+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_32BPP; -+ break; -+ case 12: -+ /* yes this is correct, the atom define is wrong */ -+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; -+ break; -+ } -+ } -+ args.v5.ucTransmitterID = encoder_id; -+ args.v5.ucEncoderMode = encoder_mode; -+ args.v5.ucPpll = pll_id; -+ break; -+ case 6: -+ args.v6.ulDispEngClkFreq = cpu_to_le32(crtc_id << 24 | clock / 10); -+ args.v6.ucRefDiv = ref_div; -+ args.v6.usFbDiv = cpu_to_le16(fb_div); -+ args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000); -+ args.v6.ucPostDiv = post_div; -+ args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */ -+ if ((ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK)) && -+ (pll_id < ATOM_EXT_PLL1) && -+ !is_pixel_clock_source_from_pll(encoder_mode, pll_id)) -+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; -+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI) { -+ switch (bpc) { -+ case 8: -+ default: -+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; -+ break; -+ case 10: -+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP_V6; -+ break; -+ case 12: -+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP_V6; -+ break; -+ case 16: -+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; -+ break; -+ } -+ } -+ args.v6.ucTransmitterID = encoder_id; -+ args.v6.ucEncoderMode = encoder_mode; -+ args.v6.ucPpll = pll_id; -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d %d\n", frev, crev); -+ return; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d %d\n", frev, crev); -+ return; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+int amdgpu_atombios_crtc_prepare_pll(struct drm_crtc *crtc, -+ struct drm_display_mode *mode) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = -+ to_amdgpu_encoder(amdgpu_crtc->encoder); -+ int encoder_mode = amdgpu_atombios_encoder_get_encoder_mode(amdgpu_crtc->encoder); -+ -+ amdgpu_crtc->bpc = 8; -+ amdgpu_crtc->ss_enabled = false; -+ -+ if ((amdgpu_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || -+ (amdgpu_encoder_get_dp_bridge_encoder_id(amdgpu_crtc->encoder) != ENCODER_OBJECT_ID_NONE)) { -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ struct drm_connector *connector = -+ amdgpu_get_connector_for_encoder(amdgpu_crtc->encoder); -+ struct amdgpu_connector *amdgpu_connector = -+ to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector = -+ amdgpu_connector->con_priv; -+ int dp_clock; -+ -+ /* Assign mode clock for hdmi deep color max clock limit check */ -+ amdgpu_connector->pixelclock_for_modeset = mode->clock; -+ amdgpu_crtc->bpc = amdgpu_connector_get_monitor_bpc(connector); -+ -+ switch (encoder_mode) { -+ case ATOM_ENCODER_MODE_DP_MST: -+ case ATOM_ENCODER_MODE_DP: -+ /* DP/eDP */ -+ dp_clock = dig_connector->dp_clock / 10; -+ amdgpu_crtc->ss_enabled = -+ amdgpu_atombios_get_asic_ss_info(adev, &amdgpu_crtc->ss, -+ ASIC_INTERNAL_SS_ON_DP, -+ dp_clock); -+ break; -+ case ATOM_ENCODER_MODE_LVDS: -+ amdgpu_crtc->ss_enabled = -+ amdgpu_atombios_get_asic_ss_info(adev, -+ &amdgpu_crtc->ss, -+ dig->lcd_ss_id, -+ mode->clock / 10); -+ break; -+ case ATOM_ENCODER_MODE_DVI: -+ amdgpu_crtc->ss_enabled = -+ amdgpu_atombios_get_asic_ss_info(adev, -+ &amdgpu_crtc->ss, -+ ASIC_INTERNAL_SS_ON_TMDS, -+ mode->clock / 10); -+ break; -+ case ATOM_ENCODER_MODE_HDMI: -+ amdgpu_crtc->ss_enabled = -+ amdgpu_atombios_get_asic_ss_info(adev, -+ &amdgpu_crtc->ss, -+ ASIC_INTERNAL_SS_ON_HDMI, -+ mode->clock / 10); -+ break; -+ default: -+ break; -+ } -+ } -+ -+ /* adjust pixel clock as needed */ -+ amdgpu_crtc->adjusted_clock = amdgpu_atombios_crtc_adjust_pll(crtc, mode); -+ -+ return 0; -+} -+ -+void amdgpu_atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) -+{ -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); -+ struct drm_device *dev = crtc->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = -+ to_amdgpu_encoder(amdgpu_crtc->encoder); -+ u32 pll_clock = mode->clock; -+ u32 clock = mode->clock; -+ u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0; -+ struct amdgpu_pll *pll; -+ int encoder_mode = amdgpu_atombios_encoder_get_encoder_mode(amdgpu_crtc->encoder); -+ -+ /* pass the actual clock to amdgpu_atombios_crtc_program_pll for HDMI */ -+ if ((encoder_mode == ATOM_ENCODER_MODE_HDMI) && -+ (amdgpu_crtc->bpc > 8)) -+ clock = amdgpu_crtc->adjusted_clock; -+ -+ switch (amdgpu_crtc->pll_id) { -+ case ATOM_PPLL1: -+ pll = &adev->clock.ppll[0]; -+ break; -+ case ATOM_PPLL2: -+ pll = &adev->clock.ppll[1]; -+ break; -+ case ATOM_PPLL0: -+ case ATOM_PPLL_INVALID: -+ default: -+ pll = &adev->clock.ppll[2]; -+ break; -+ } -+ -+ /* update pll params */ -+ pll->flags = amdgpu_crtc->pll_flags; -+ pll->reference_div = amdgpu_crtc->pll_reference_div; -+ pll->post_div = amdgpu_crtc->pll_post_div; -+ -+ amdgpu_pll_compute(pll, amdgpu_crtc->adjusted_clock, &pll_clock, -+ &fb_div, &frac_fb_div, &ref_div, &post_div); -+ -+ amdgpu_atombios_crtc_program_ss(adev, ATOM_DISABLE, amdgpu_crtc->pll_id, -+ amdgpu_crtc->crtc_id, &amdgpu_crtc->ss); -+ -+ amdgpu_atombios_crtc_program_pll(crtc, amdgpu_crtc->crtc_id, amdgpu_crtc->pll_id, -+ encoder_mode, amdgpu_encoder->encoder_id, clock, -+ ref_div, fb_div, frac_fb_div, post_div, -+ amdgpu_crtc->bpc, amdgpu_crtc->ss_enabled, &amdgpu_crtc->ss); -+ -+ if (amdgpu_crtc->ss_enabled) { -+ /* calculate ss amount and step size */ -+ u32 step_size; -+ u32 amount = (((fb_div * 10) + frac_fb_div) * -+ (u32)amdgpu_crtc->ss.percentage) / -+ (100 * (u32)amdgpu_crtc->ss.percentage_divider); -+ amdgpu_crtc->ss.amount = (amount / 10) & ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK; -+ amdgpu_crtc->ss.amount |= ((amount - (amount / 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) & -+ ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK; -+ if (amdgpu_crtc->ss.type & ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD) -+ step_size = (4 * amount * ref_div * ((u32)amdgpu_crtc->ss.rate * 2048)) / -+ (125 * 25 * pll->reference_freq / 100); -+ else -+ step_size = (2 * amount * ref_div * ((u32)amdgpu_crtc->ss.rate * 2048)) / -+ (125 * 25 * pll->reference_freq / 100); -+ amdgpu_crtc->ss.step = step_size; -+ -+ amdgpu_atombios_crtc_program_ss(adev, ATOM_ENABLE, amdgpu_crtc->pll_id, -+ amdgpu_crtc->crtc_id, &amdgpu_crtc->ss); -+ } -+} -+ -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_crtc.h b/drivers/gpu/drm/amd/amdgpu/atombios_crtc.h -new file mode 100644 -index 0000000..c670833 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_crtc.h -@@ -0,0 +1,58 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __ATOMBIOS_CRTC_H__ -+#define __ATOMBIOS_CRTC_H__ -+ -+void amdgpu_atombios_crtc_overscan_setup(struct drm_crtc *crtc, -+ struct drm_display_mode *mode, -+ struct drm_display_mode *adjusted_mode); -+void amdgpu_atombios_crtc_scaler_setup(struct drm_crtc *crtc); -+void amdgpu_atombios_crtc_lock(struct drm_crtc *crtc, int lock); -+void amdgpu_atombios_crtc_enable(struct drm_crtc *crtc, int state); -+void amdgpu_atombios_crtc_blank(struct drm_crtc *crtc, int state); -+void amdgpu_atombios_crtc_powergate(struct drm_crtc *crtc, int state); -+void amdgpu_atombios_crtc_powergate_init(struct amdgpu_device *adev); -+void amdgpu_atombios_crtc_set_dtd_timing(struct drm_crtc *crtc, -+ struct drm_display_mode *mode); -+void amdgpu_atombios_crtc_set_disp_eng_pll(struct amdgpu_device *adev, -+ u32 dispclk); -+void amdgpu_atombios_crtc_program_pll(struct drm_crtc *crtc, -+ u32 crtc_id, -+ int pll_id, -+ u32 encoder_mode, -+ u32 encoder_id, -+ u32 clock, -+ u32 ref_div, -+ u32 fb_div, -+ u32 frac_fb_div, -+ u32 post_div, -+ int bpc, -+ bool ss_enabled, -+ struct amdgpu_atom_ss *ss); -+int amdgpu_atombios_crtc_prepare_pll(struct drm_crtc *crtc, -+ struct drm_display_mode *mode); -+void amdgpu_atombios_crtc_set_pll(struct drm_crtc *crtc, -+ struct drm_display_mode *mode); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c -new file mode 100644 -index 0000000..e00b8ad ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c -@@ -0,0 +1,774 @@ -+/* -+ * Copyright 2007-8 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ * Jerome Glisse -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+ -+#include "atom.h" -+#include "atom-bits.h" -+#include "atombios_encoders.h" -+#include "atombios_dp.h" -+#include "amdgpu_connectors.h" -+#include "amdgpu_atombios.h" -+#include <drm/drm_dp_helper.h> -+ -+/* move these to drm_dp_helper.c/h */ -+#define DP_LINK_CONFIGURATION_SIZE 9 -+#define DP_DPCD_SIZE DP_RECEIVER_CAP_SIZE -+ -+static char *voltage_names[] = { -+ "0.4V", "0.6V", "0.8V", "1.2V" -+}; -+static char *pre_emph_names[] = { -+ "0dB", "3.5dB", "6dB", "9.5dB" -+}; -+ -+/***** amdgpu AUX functions *****/ -+ -+union aux_channel_transaction { -+ PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; -+ PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; -+}; -+ -+static int amdgpu_atombios_dp_process_aux_ch(struct amdgpu_i2c_chan *chan, -+ u8 *send, int send_bytes, -+ u8 *recv, int recv_size, -+ u8 delay, u8 *ack) -+{ -+ struct drm_device *dev = chan->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ union aux_channel_transaction args; -+ int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); -+ unsigned char *base; -+ int recv_bytes; -+ int r = 0; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ mutex_lock(&chan->mutex); -+ -+ base = (unsigned char *)(adev->mode_info.atom_context->scratch + 1); -+ -+ amdgpu_atombios_copy_swap(base, send, send_bytes, true); -+ -+ args.v2.lpAuxRequest = cpu_to_le16((u16)(0 + 4)); -+ args.v2.lpDataOut = cpu_to_le16((u16)(16 + 4)); -+ args.v2.ucDataOutLen = 0; -+ args.v2.ucChannelID = chan->rec.i2c_id; -+ args.v2.ucDelay = delay / 10; -+ args.v2.ucHPD_ID = chan->rec.hpd; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ *ack = args.v2.ucReplyStatus; -+ -+ /* timeout */ -+ if (args.v2.ucReplyStatus == 1) { -+ DRM_DEBUG_KMS("dp_aux_ch timeout\n"); -+ r = -ETIMEDOUT; -+ goto done; -+ } -+ -+ /* flags not zero */ -+ if (args.v2.ucReplyStatus == 2) { -+ DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); -+ r = -EIO; -+ goto done; -+ } -+ -+ /* error */ -+ if (args.v2.ucReplyStatus == 3) { -+ DRM_DEBUG_KMS("dp_aux_ch error\n"); -+ r = -EIO; -+ goto done; -+ } -+ -+ recv_bytes = args.v1.ucDataOutLen; -+ if (recv_bytes > recv_size) -+ recv_bytes = recv_size; -+ -+ if (recv && recv_size) -+ amdgpu_atombios_copy_swap(recv, base + 16, recv_bytes, false); -+ -+ r = recv_bytes; -+done: -+ mutex_unlock(&chan->mutex); -+ -+ return r; -+} -+ -+#define BARE_ADDRESS_SIZE 3 -+#define HEADER_SIZE (BARE_ADDRESS_SIZE + 1) -+ -+static ssize_t -+amdgpu_atombios_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) -+{ -+ struct amdgpu_i2c_chan *chan = -+ container_of(aux, struct amdgpu_i2c_chan, aux); -+ int ret; -+ u8 tx_buf[20]; -+ size_t tx_size; -+ u8 ack, delay = 0; -+ -+ if (WARN_ON(msg->size > 16)) -+ return -E2BIG; -+ -+ tx_buf[0] = msg->address & 0xff; -+ tx_buf[1] = msg->address >> 8; -+ tx_buf[2] = msg->request << 4; -+ tx_buf[3] = msg->size ? (msg->size - 1) : 0; -+ -+ switch (msg->request & ~DP_AUX_I2C_MOT) { -+ case DP_AUX_NATIVE_WRITE: -+ case DP_AUX_I2C_WRITE: -+ /* tx_size needs to be 4 even for bare address packets since the atom -+ * table needs the info in tx_buf[3]. -+ */ -+ tx_size = HEADER_SIZE + msg->size; -+ if (msg->size == 0) -+ tx_buf[3] |= BARE_ADDRESS_SIZE << 4; -+ else -+ tx_buf[3] |= tx_size << 4; -+ memcpy(tx_buf + HEADER_SIZE, msg->buffer, msg->size); -+ ret = amdgpu_atombios_dp_process_aux_ch(chan, -+ tx_buf, tx_size, NULL, 0, delay, &ack); -+ if (ret >= 0) -+ /* Return payload size. */ -+ ret = msg->size; -+ break; -+ case DP_AUX_NATIVE_READ: -+ case DP_AUX_I2C_READ: -+ /* tx_size needs to be 4 even for bare address packets since the atom -+ * table needs the info in tx_buf[3]. -+ */ -+ tx_size = HEADER_SIZE; -+ if (msg->size == 0) -+ tx_buf[3] |= BARE_ADDRESS_SIZE << 4; -+ else -+ tx_buf[3] |= tx_size << 4; -+ ret = amdgpu_atombios_dp_process_aux_ch(chan, -+ tx_buf, tx_size, msg->buffer, msg->size, delay, &ack); -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (ret >= 0) -+ msg->reply = ack >> 4; -+ -+ return ret; -+} -+ -+void amdgpu_atombios_dp_aux_init(struct amdgpu_connector *amdgpu_connector) -+{ -+ int ret; -+ -+ amdgpu_connector->ddc_bus->rec.hpd = amdgpu_connector->hpd.hpd; -+ amdgpu_connector->ddc_bus->aux.dev = amdgpu_connector->base.kdev; -+ amdgpu_connector->ddc_bus->aux.transfer = amdgpu_atombios_dp_aux_transfer; -+ ret = drm_dp_aux_register(&amdgpu_connector->ddc_bus->aux); -+ if (!ret) -+ amdgpu_connector->ddc_bus->has_aux = true; -+ -+ WARN(ret, "drm_dp_aux_register_i2c_bus() failed with error %d\n", ret); -+} -+ -+/***** general DP utility functions *****/ -+ -+#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3 -+#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPH_LEVEL_3 -+ -+static void amdgpu_atombios_dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], -+ int lane_count, -+ u8 train_set[4]) -+{ -+ u8 v = 0; -+ u8 p = 0; -+ int lane; -+ -+ for (lane = 0; lane < lane_count; lane++) { -+ u8 this_v = drm_dp_get_adjust_request_voltage(link_status, lane); -+ u8 this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); -+ -+ DRM_DEBUG_KMS("requested signal parameters: lane %d voltage %s pre_emph %s\n", -+ lane, -+ voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT], -+ pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); -+ -+ if (this_v > v) -+ v = this_v; -+ if (this_p > p) -+ p = this_p; -+ } -+ -+ if (v >= DP_VOLTAGE_MAX) -+ v |= DP_TRAIN_MAX_SWING_REACHED; -+ -+ if (p >= DP_PRE_EMPHASIS_MAX) -+ p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; -+ -+ DRM_DEBUG_KMS("using signal parameters: voltage %s pre_emph %s\n", -+ voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT], -+ pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); -+ -+ for (lane = 0; lane < 4; lane++) -+ train_set[lane] = v | p; -+} -+ -+/* convert bits per color to bits per pixel */ -+/* get bpc from the EDID */ -+static int amdgpu_atombios_dp_convert_bpc_to_bpp(int bpc) -+{ -+ if (bpc == 0) -+ return 24; -+ else -+ return bpc * 3; -+} -+ -+/* get the max pix clock supported by the link rate and lane num */ -+static int amdgpu_atombios_dp_get_max_dp_pix_clock(int link_rate, -+ int lane_num, -+ int bpp) -+{ -+ return (link_rate * lane_num * 8) / bpp; -+} -+ -+/***** amdgpu specific DP functions *****/ -+ -+/* First get the min lane# when low rate is used according to pixel clock -+ * (prefer low rate), second check max lane# supported by DP panel, -+ * if the max lane# < low rate lane# then use max lane# instead. -+ */ -+static int amdgpu_atombios_dp_get_dp_lane_number(struct drm_connector *connector, -+ u8 dpcd[DP_DPCD_SIZE], -+ int pix_clock) -+{ -+ int bpp = amdgpu_atombios_dp_convert_bpc_to_bpp(amdgpu_connector_get_monitor_bpc(connector)); -+ int max_link_rate = drm_dp_max_link_rate(dpcd); -+ int max_lane_num = drm_dp_max_lane_count(dpcd); -+ int lane_num; -+ int max_dp_pix_clock; -+ -+ for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) { -+ max_dp_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp); -+ if (pix_clock <= max_dp_pix_clock) -+ break; -+ } -+ -+ return lane_num; -+} -+ -+static int amdgpu_atombios_dp_get_dp_link_clock(struct drm_connector *connector, -+ u8 dpcd[DP_DPCD_SIZE], -+ int pix_clock) -+{ -+ int bpp = amdgpu_atombios_dp_convert_bpc_to_bpp(amdgpu_connector_get_monitor_bpc(connector)); -+ int lane_num, max_pix_clock; -+ -+ if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) == -+ ENCODER_OBJECT_ID_NUTMEG) -+ return 270000; -+ -+ lane_num = amdgpu_atombios_dp_get_dp_lane_number(connector, dpcd, pix_clock); -+ max_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(162000, lane_num, bpp); -+ if (pix_clock <= max_pix_clock) -+ return 162000; -+ max_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(270000, lane_num, bpp); -+ if (pix_clock <= max_pix_clock) -+ return 270000; -+ if (amdgpu_connector_is_dp12_capable(connector)) { -+ max_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(540000, lane_num, bpp); -+ if (pix_clock <= max_pix_clock) -+ return 540000; -+ } -+ -+ return drm_dp_max_link_rate(dpcd); -+} -+ -+static u8 amdgpu_atombios_dp_encoder_service(struct amdgpu_device *adev, -+ int action, int dp_clock, -+ u8 ucconfig, u8 lane_num) -+{ -+ DP_ENCODER_SERVICE_PARAMETERS args; -+ int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); -+ -+ memset(&args, 0, sizeof(args)); -+ args.ucLinkClock = dp_clock / 10; -+ args.ucConfig = ucconfig; -+ args.ucAction = action; -+ args.ucLaneNum = lane_num; -+ args.ucStatus = 0; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ return args.ucStatus; -+} -+ -+u8 amdgpu_atombios_dp_get_sinktype(struct amdgpu_connector *amdgpu_connector) -+{ -+ struct drm_device *dev = amdgpu_connector->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ return amdgpu_atombios_dp_encoder_service(adev, ATOM_DP_ACTION_GET_SINK_TYPE, 0, -+ amdgpu_connector->ddc_bus->rec.i2c_id, 0); -+} -+ -+static void amdgpu_atombios_dp_probe_oui(struct amdgpu_connector *amdgpu_connector) -+{ -+ struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv; -+ u8 buf[3]; -+ -+ if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) -+ return; -+ -+ if (drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3) == 3) -+ DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", -+ buf[0], buf[1], buf[2]); -+ -+ if (drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3) == 3) -+ DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", -+ buf[0], buf[1], buf[2]); -+} -+ -+int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector) -+{ -+ struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv; -+ u8 msg[DP_DPCD_SIZE]; -+ int ret, i; -+ -+ ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_DPCD_REV, msg, -+ DP_DPCD_SIZE); -+ if (ret > 0) { -+ memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE); -+ DRM_DEBUG_KMS("DPCD: "); -+ for (i = 0; i < DP_DPCD_SIZE; i++) -+ DRM_DEBUG_KMS("%02x ", msg[i]); -+ DRM_DEBUG_KMS("\n"); -+ -+ amdgpu_atombios_dp_probe_oui(amdgpu_connector); -+ -+ return 0; -+ } -+ dig_connector->dpcd[0] = 0; -+ return -EINVAL; -+} -+ -+int amdgpu_atombios_dp_get_panel_mode(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector; -+ int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; -+ u16 dp_bridge = amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector); -+ u8 tmp; -+ -+ if (!amdgpu_connector->con_priv) -+ return panel_mode; -+ -+ dig_connector = amdgpu_connector->con_priv; -+ -+ if (dp_bridge != ENCODER_OBJECT_ID_NONE) { -+ /* DP bridge chips */ -+ if (drm_dp_dpcd_readb(&amdgpu_connector->ddc_bus->aux, -+ DP_EDP_CONFIGURATION_CAP, &tmp) == 1) { -+ if (tmp & 1) -+ panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; -+ else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) || -+ (dp_bridge == ENCODER_OBJECT_ID_TRAVIS)) -+ panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; -+ else -+ panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; -+ } -+ } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { -+ /* eDP */ -+ if (drm_dp_dpcd_readb(&amdgpu_connector->ddc_bus->aux, -+ DP_EDP_CONFIGURATION_CAP, &tmp) == 1) { -+ if (tmp & 1) -+ panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; -+ } -+ } -+ -+ return panel_mode; -+} -+ -+void amdgpu_atombios_dp_set_link_config(struct drm_connector *connector, -+ const struct drm_display_mode *mode) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector; -+ -+ if (!amdgpu_connector->con_priv) -+ return; -+ dig_connector = amdgpu_connector->con_priv; -+ -+ if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || -+ (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { -+ dig_connector->dp_clock = -+ amdgpu_atombios_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); -+ dig_connector->dp_lane_count = -+ amdgpu_atombios_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock); -+ } -+} -+ -+int amdgpu_atombios_dp_mode_valid_helper(struct drm_connector *connector, -+ struct drm_display_mode *mode) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector; -+ int dp_clock; -+ -+ if (!amdgpu_connector->con_priv) -+ return MODE_CLOCK_HIGH; -+ dig_connector = amdgpu_connector->con_priv; -+ -+ dp_clock = -+ amdgpu_atombios_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); -+ -+ if ((dp_clock == 540000) && -+ (!amdgpu_connector_is_dp12_capable(connector))) -+ return MODE_CLOCK_HIGH; -+ -+ return MODE_OK; -+} -+ -+bool amdgpu_atombios_dp_needs_link_train(struct amdgpu_connector *amdgpu_connector) -+{ -+ u8 link_status[DP_LINK_STATUS_SIZE]; -+ struct amdgpu_connector_atom_dig *dig = amdgpu_connector->con_priv; -+ -+ if (drm_dp_dpcd_read_link_status(&amdgpu_connector->ddc_bus->aux, link_status) -+ <= 0) -+ return false; -+ if (drm_dp_channel_eq_ok(link_status, dig->dp_lane_count)) -+ return false; -+ return true; -+} -+ -+void amdgpu_atombios_dp_set_rx_power_state(struct drm_connector *connector, -+ u8 power_state) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector; -+ -+ if (!amdgpu_connector->con_priv) -+ return; -+ -+ dig_connector = amdgpu_connector->con_priv; -+ -+ /* power up/down the sink */ -+ if (dig_connector->dpcd[0] >= 0x11) { -+ drm_dp_dpcd_writeb(&amdgpu_connector->ddc_bus->aux, -+ DP_SET_POWER, power_state); -+ usleep_range(1000, 2000); -+ } -+} -+ -+struct amdgpu_atombios_dp_link_train_info { -+ struct amdgpu_device *adev; -+ struct drm_encoder *encoder; -+ struct drm_connector *connector; -+ int dp_clock; -+ int dp_lane_count; -+ bool tp3_supported; -+ u8 dpcd[DP_RECEIVER_CAP_SIZE]; -+ u8 train_set[4]; -+ u8 link_status[DP_LINK_STATUS_SIZE]; -+ u8 tries; -+ struct drm_dp_aux *aux; -+}; -+ -+static void -+amdgpu_atombios_dp_update_vs_emph(struct amdgpu_atombios_dp_link_train_info *dp_info) -+{ -+ /* set the initial vs/emph on the source */ -+ amdgpu_atombios_encoder_setup_dig_transmitter(dp_info->encoder, -+ ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, -+ 0, dp_info->train_set[0]); /* sets all lanes at once */ -+ -+ /* set the vs/emph on the sink */ -+ drm_dp_dpcd_write(dp_info->aux, DP_TRAINING_LANE0_SET, -+ dp_info->train_set, dp_info->dp_lane_count); -+} -+ -+static void -+amdgpu_atombios_dp_set_tp(struct amdgpu_atombios_dp_link_train_info *dp_info, int tp) -+{ -+ int rtp = 0; -+ -+ /* set training pattern on the source */ -+ switch (tp) { -+ case DP_TRAINING_PATTERN_1: -+ rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1; -+ break; -+ case DP_TRAINING_PATTERN_2: -+ rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2; -+ break; -+ case DP_TRAINING_PATTERN_3: -+ rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3; -+ break; -+ } -+ amdgpu_atombios_encoder_setup_dig_encoder(dp_info->encoder, rtp, 0); -+ -+ /* enable training pattern on the sink */ -+ drm_dp_dpcd_writeb(dp_info->aux, DP_TRAINING_PATTERN_SET, tp); -+} -+ -+static int -+amdgpu_atombios_dp_link_train_init(struct amdgpu_atombios_dp_link_train_info *dp_info) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(dp_info->encoder); -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ u8 tmp; -+ -+ /* power up the sink */ -+ amdgpu_atombios_dp_set_rx_power_state(dp_info->connector, DP_SET_POWER_D0); -+ -+ /* possibly enable downspread on the sink */ -+ if (dp_info->dpcd[3] & 0x1) -+ drm_dp_dpcd_writeb(dp_info->aux, -+ DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); -+ else -+ drm_dp_dpcd_writeb(dp_info->aux, -+ DP_DOWNSPREAD_CTRL, 0); -+ -+ if (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE) -+ drm_dp_dpcd_writeb(dp_info->aux, DP_EDP_CONFIGURATION_SET, 1); -+ -+ /* set the lane count on the sink */ -+ tmp = dp_info->dp_lane_count; -+ if (drm_dp_enhanced_frame_cap(dp_info->dpcd)) -+ tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN; -+ drm_dp_dpcd_writeb(dp_info->aux, DP_LANE_COUNT_SET, tmp); -+ -+ /* set the link rate on the sink */ -+ tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock); -+ drm_dp_dpcd_writeb(dp_info->aux, DP_LINK_BW_SET, tmp); -+ -+ /* start training on the source */ -+ amdgpu_atombios_encoder_setup_dig_encoder(dp_info->encoder, -+ ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0); -+ -+ /* disable the training pattern on the sink */ -+ drm_dp_dpcd_writeb(dp_info->aux, -+ DP_TRAINING_PATTERN_SET, -+ DP_TRAINING_PATTERN_DISABLE); -+ -+ return 0; -+} -+ -+static int -+amdgpu_atombios_dp_link_train_finish(struct amdgpu_atombios_dp_link_train_info *dp_info) -+{ -+ udelay(400); -+ -+ /* disable the training pattern on the sink */ -+ drm_dp_dpcd_writeb(dp_info->aux, -+ DP_TRAINING_PATTERN_SET, -+ DP_TRAINING_PATTERN_DISABLE); -+ -+ /* disable the training pattern on the source */ -+ amdgpu_atombios_encoder_setup_dig_encoder(dp_info->encoder, -+ ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0); -+ -+ return 0; -+} -+ -+static int -+amdgpu_atombios_dp_link_train_cr(struct amdgpu_atombios_dp_link_train_info *dp_info) -+{ -+ bool clock_recovery; -+ u8 voltage; -+ int i; -+ -+ amdgpu_atombios_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1); -+ memset(dp_info->train_set, 0, 4); -+ amdgpu_atombios_dp_update_vs_emph(dp_info); -+ -+ udelay(400); -+ -+ /* clock recovery loop */ -+ clock_recovery = false; -+ dp_info->tries = 0; -+ voltage = 0xff; -+ while (1) { -+ drm_dp_link_train_clock_recovery_delay(dp_info->dpcd); -+ -+ if (drm_dp_dpcd_read_link_status(dp_info->aux, -+ dp_info->link_status) <= 0) { -+ DRM_ERROR("displayport link status failed\n"); -+ break; -+ } -+ -+ if (drm_dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) { -+ clock_recovery = true; -+ break; -+ } -+ -+ for (i = 0; i < dp_info->dp_lane_count; i++) { -+ if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) -+ break; -+ } -+ if (i == dp_info->dp_lane_count) { -+ DRM_ERROR("clock recovery reached max voltage\n"); -+ break; -+ } -+ -+ if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { -+ ++dp_info->tries; -+ if (dp_info->tries == 5) { -+ DRM_ERROR("clock recovery tried 5 times\n"); -+ break; -+ } -+ } else -+ dp_info->tries = 0; -+ -+ voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; -+ -+ /* Compute new train_set as requested by sink */ -+ amdgpu_atombios_dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, -+ dp_info->train_set); -+ -+ amdgpu_atombios_dp_update_vs_emph(dp_info); -+ } -+ if (!clock_recovery) { -+ DRM_ERROR("clock recovery failed\n"); -+ return -1; -+ } else { -+ DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n", -+ dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, -+ (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> -+ DP_TRAIN_PRE_EMPHASIS_SHIFT); -+ return 0; -+ } -+} -+ -+static int -+amdgpu_atombios_dp_link_train_ce(struct amdgpu_atombios_dp_link_train_info *dp_info) -+{ -+ bool channel_eq; -+ -+ if (dp_info->tp3_supported) -+ amdgpu_atombios_dp_set_tp(dp_info, DP_TRAINING_PATTERN_3); -+ else -+ amdgpu_atombios_dp_set_tp(dp_info, DP_TRAINING_PATTERN_2); -+ -+ /* channel equalization loop */ -+ dp_info->tries = 0; -+ channel_eq = false; -+ while (1) { -+ drm_dp_link_train_channel_eq_delay(dp_info->dpcd); -+ -+ if (drm_dp_dpcd_read_link_status(dp_info->aux, -+ dp_info->link_status) <= 0) { -+ DRM_ERROR("displayport link status failed\n"); -+ break; -+ } -+ -+ if (drm_dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) { -+ channel_eq = true; -+ break; -+ } -+ -+ /* Try 5 times */ -+ if (dp_info->tries > 5) { -+ DRM_ERROR("channel eq failed: 5 tries\n"); -+ break; -+ } -+ -+ /* Compute new train_set as requested by sink */ -+ amdgpu_atombios_dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, -+ dp_info->train_set); -+ -+ amdgpu_atombios_dp_update_vs_emph(dp_info); -+ dp_info->tries++; -+ } -+ -+ if (!channel_eq) { -+ DRM_ERROR("channel eq failed\n"); -+ return -1; -+ } else { -+ DRM_DEBUG_KMS("channel eq at voltage %d pre-emphasis %d\n", -+ dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, -+ (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) -+ >> DP_TRAIN_PRE_EMPHASIS_SHIFT); -+ return 0; -+ } -+} -+ -+void amdgpu_atombios_dp_link_train(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_encoder_atom_dig *dig; -+ struct amdgpu_connector *amdgpu_connector; -+ struct amdgpu_connector_atom_dig *dig_connector; -+ struct amdgpu_atombios_dp_link_train_info dp_info; -+ u8 tmp; -+ -+ if (!amdgpu_encoder->enc_priv) -+ return; -+ dig = amdgpu_encoder->enc_priv; -+ -+ amdgpu_connector = to_amdgpu_connector(connector); -+ if (!amdgpu_connector->con_priv) -+ return; -+ dig_connector = amdgpu_connector->con_priv; -+ -+ if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) && -+ (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP)) -+ return; -+ -+ if (drm_dp_dpcd_readb(&amdgpu_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp) -+ == 1) { -+ if (tmp & DP_TPS3_SUPPORTED) -+ dp_info.tp3_supported = true; -+ else -+ dp_info.tp3_supported = false; -+ } else { -+ dp_info.tp3_supported = false; -+ } -+ -+ memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE); -+ dp_info.adev = adev; -+ dp_info.encoder = encoder; -+ dp_info.connector = connector; -+ dp_info.dp_lane_count = dig_connector->dp_lane_count; -+ dp_info.dp_clock = dig_connector->dp_clock; -+ dp_info.aux = &amdgpu_connector->ddc_bus->aux; -+ -+ if (amdgpu_atombios_dp_link_train_init(&dp_info)) -+ goto done; -+ if (amdgpu_atombios_dp_link_train_cr(&dp_info)) -+ goto done; -+ if (amdgpu_atombios_dp_link_train_ce(&dp_info)) -+ goto done; -+done: -+ if (amdgpu_atombios_dp_link_train_finish(&dp_info)) -+ return; -+} -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.h b/drivers/gpu/drm/amd/amdgpu/atombios_dp.h -new file mode 100644 -index 0000000..f59d85e ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.h -@@ -0,0 +1,42 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __ATOMBIOS_DP_H__ -+#define __ATOMBIOS_DP_H__ -+ -+void amdgpu_atombios_dp_aux_init(struct amdgpu_connector *amdgpu_connector); -+u8 amdgpu_atombios_dp_get_sinktype(struct amdgpu_connector *amdgpu_connector); -+int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector); -+int amdgpu_atombios_dp_get_panel_mode(struct drm_encoder *encoder, -+ struct drm_connector *connector); -+void amdgpu_atombios_dp_set_link_config(struct drm_connector *connector, -+ const struct drm_display_mode *mode); -+int amdgpu_atombios_dp_mode_valid_helper(struct drm_connector *connector, -+ struct drm_display_mode *mode); -+bool amdgpu_atombios_dp_needs_link_train(struct amdgpu_connector *amdgpu_connector); -+void amdgpu_atombios_dp_set_rx_power_state(struct drm_connector *connector, -+ u8 power_state); -+void amdgpu_atombios_dp_link_train(struct drm_encoder *encoder, -+ struct drm_connector *connector); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c -new file mode 100644 -index 0000000..ae8caca ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c -@@ -0,0 +1,2066 @@ -+/* -+ * Copyright 2007-11 Advanced Micro Devices, Inc. -+ * Copyright 2008 Red Hat Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE 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: Dave Airlie -+ * Alex Deucher -+ */ -+#include <drm/drmP.h> -+#include <drm/drm_crtc_helper.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "amdgpu_connectors.h" -+#include "atom.h" -+#include "atombios_encoders.h" -+#include "atombios_dp.h" -+#include <linux/backlight.h> -+#include "bif/bif_4_1_d.h" -+ -+static u8 -+amdgpu_atombios_encoder_get_backlight_level_from_reg(struct amdgpu_device *adev) -+{ -+ u8 backlight_level; -+ u32 bios_2_scratch; -+ -+ bios_2_scratch = RREG32(mmBIOS_SCRATCH_2); -+ -+ backlight_level = ((bios_2_scratch & ATOM_S2_CURRENT_BL_LEVEL_MASK) >> -+ ATOM_S2_CURRENT_BL_LEVEL_SHIFT); -+ -+ return backlight_level; -+} -+ -+static void -+amdgpu_atombios_encoder_set_backlight_level_to_reg(struct amdgpu_device *adev, -+ u8 backlight_level) -+{ -+ u32 bios_2_scratch; -+ -+ bios_2_scratch = RREG32(mmBIOS_SCRATCH_2); -+ -+ bios_2_scratch &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK; -+ bios_2_scratch |= ((backlight_level << ATOM_S2_CURRENT_BL_LEVEL_SHIFT) & -+ ATOM_S2_CURRENT_BL_LEVEL_MASK); -+ -+ WREG32(mmBIOS_SCRATCH_2, bios_2_scratch); -+} -+ -+u8 -+amdgpu_atombios_encoder_get_backlight_level(struct amdgpu_encoder *amdgpu_encoder) -+{ -+ struct drm_device *dev = amdgpu_encoder->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) -+ return 0; -+ -+ return amdgpu_atombios_encoder_get_backlight_level_from_reg(adev); -+} -+ -+void -+amdgpu_atombios_encoder_set_backlight_level(struct amdgpu_encoder *amdgpu_encoder, -+ u8 level) -+{ -+ struct drm_encoder *encoder = &amdgpu_encoder->base; -+ struct drm_device *dev = amdgpu_encoder->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder_atom_dig *dig; -+ -+ if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) -+ return; -+ -+ if ((amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) && -+ amdgpu_encoder->enc_priv) { -+ dig = amdgpu_encoder->enc_priv; -+ dig->backlight_level = level; -+ amdgpu_atombios_encoder_set_backlight_level_to_reg(adev, dig->backlight_level); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ if (dig->backlight_level == 0) -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0); -+ else { -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL, 0, 0); -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); -+ } -+ break; -+ default: -+ break; -+ } -+ } -+} -+ -+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) -+ -+static u8 amdgpu_atombios_encoder_backlight_level(struct backlight_device *bd) -+{ -+ u8 level; -+ -+ /* Convert brightness to hardware level */ -+ if (bd->props.brightness < 0) -+ level = 0; -+ else if (bd->props.brightness > AMDGPU_MAX_BL_LEVEL) -+ level = AMDGPU_MAX_BL_LEVEL; -+ else -+ level = bd->props.brightness; -+ -+ return level; -+} -+ -+static int amdgpu_atombios_encoder_update_backlight_status(struct backlight_device *bd) -+{ -+ struct amdgpu_backlight_privdata *pdata = bl_get_data(bd); -+ struct amdgpu_encoder *amdgpu_encoder = pdata->encoder; -+ -+ amdgpu_atombios_encoder_set_backlight_level(amdgpu_encoder, -+ amdgpu_atombios_encoder_backlight_level(bd)); -+ -+ return 0; -+} -+ -+static int -+amdgpu_atombios_encoder_get_backlight_brightness(struct backlight_device *bd) -+{ -+ struct amdgpu_backlight_privdata *pdata = bl_get_data(bd); -+ struct amdgpu_encoder *amdgpu_encoder = pdata->encoder; -+ struct drm_device *dev = amdgpu_encoder->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ -+ return amdgpu_atombios_encoder_get_backlight_level_from_reg(adev); -+} -+ -+static const struct backlight_ops amdgpu_atombios_encoder_backlight_ops = { -+ .get_brightness = amdgpu_atombios_encoder_get_backlight_brightness, -+ .update_status = amdgpu_atombios_encoder_update_backlight_status, -+}; -+ -+void amdgpu_atombios_encoder_init_backlight(struct amdgpu_encoder *amdgpu_encoder, -+ struct drm_connector *drm_connector) -+{ -+ struct drm_device *dev = amdgpu_encoder->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct backlight_device *bd; -+ struct backlight_properties props; -+ struct amdgpu_backlight_privdata *pdata; -+ struct amdgpu_encoder_atom_dig *dig; -+ u8 backlight_level; -+ char bl_name[16]; -+ -+ /* Mac laptops with multiple GPUs use the gmux driver for backlight -+ * so don't register a backlight device -+ */ -+ if ((adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) && -+ (adev->pdev->device == 0x6741)) -+ return; -+ -+ if (!amdgpu_encoder->enc_priv) -+ return; -+ -+ if (!adev->is_atom_bios) -+ return; -+ -+ if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) -+ return; -+ -+ pdata = kmalloc(sizeof(struct amdgpu_backlight_privdata), GFP_KERNEL); -+ if (!pdata) { -+ DRM_ERROR("Memory allocation failed\n"); -+ goto error; -+ } -+ -+ memset(&props, 0, sizeof(props)); -+ props.max_brightness = AMDGPU_MAX_BL_LEVEL; -+ props.type = BACKLIGHT_RAW; -+ snprintf(bl_name, sizeof(bl_name), -+ "amdgpu_bl%d", dev->primary->index); -+ bd = backlight_device_register(bl_name, drm_connector->kdev, -+ pdata, &amdgpu_atombios_encoder_backlight_ops, &props); -+ if (IS_ERR(bd)) { -+ DRM_ERROR("Backlight registration failed\n"); -+ goto error; -+ } -+ -+ pdata->encoder = amdgpu_encoder; -+ -+ backlight_level = amdgpu_atombios_encoder_get_backlight_level_from_reg(adev); -+ -+ dig = amdgpu_encoder->enc_priv; -+ dig->bl_dev = bd; -+ -+ bd->props.brightness = amdgpu_atombios_encoder_get_backlight_brightness(bd); -+ bd->props.power = FB_BLANK_UNBLANK; -+ backlight_update_status(bd); -+ -+ DRM_INFO("amdgpu atom DIG backlight initialized\n"); -+ -+ return; -+ -+error: -+ kfree(pdata); -+ return; -+} -+ -+void -+amdgpu_atombios_encoder_fini_backlight(struct amdgpu_encoder *amdgpu_encoder) -+{ -+ struct drm_device *dev = amdgpu_encoder->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct backlight_device *bd = NULL; -+ struct amdgpu_encoder_atom_dig *dig; -+ -+ if (!amdgpu_encoder->enc_priv) -+ return; -+ -+ if (!adev->is_atom_bios) -+ return; -+ -+ if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) -+ return; -+ -+ dig = amdgpu_encoder->enc_priv; -+ bd = dig->bl_dev; -+ dig->bl_dev = NULL; -+ -+ if (bd) { -+ struct amdgpu_legacy_backlight_privdata *pdata; -+ -+ pdata = bl_get_data(bd); -+ backlight_device_unregister(bd); -+ kfree(pdata); -+ -+ DRM_INFO("amdgpu atom LVDS backlight unloaded\n"); -+ } -+} -+ -+#else /* !CONFIG_BACKLIGHT_CLASS_DEVICE */ -+ -+void amdgpu_atombios_encoder_init_backlight(struct amdgpu_encoder *encoder) -+{ -+} -+ -+void amdgpu_atombios_encoder_fini_backlight(struct amdgpu_encoder *encoder) -+{ -+} -+ -+#endif -+ -+bool amdgpu_atombios_encoder_is_digital(struct drm_encoder *encoder) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+bool amdgpu_atombios_encoder_mode_fixup(struct drm_encoder *encoder, -+ const struct drm_display_mode *mode, -+ struct drm_display_mode *adjusted_mode) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ /* set the active encoder to connector routing */ -+ amdgpu_encoder_set_active_device(encoder); -+ drm_mode_set_crtcinfo(adjusted_mode, 0); -+ -+ /* hw bug */ -+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) -+ && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) -+ adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; -+ -+ /* get the native mode for scaling */ -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) -+ amdgpu_panel_mode_fixup(encoder, adjusted_mode); -+ else if (amdgpu_encoder->rmx_type != RMX_OFF) -+ amdgpu_panel_mode_fixup(encoder, adjusted_mode); -+ -+ if ((amdgpu_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) || -+ (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { -+ struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder); -+ amdgpu_atombios_dp_set_link_config(connector, adjusted_mode); -+ } -+ -+ return true; -+} -+ -+static void -+amdgpu_atombios_encoder_setup_dac(struct drm_encoder *encoder, int action) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ DAC_ENCODER_CONTROL_PS_ALLOCATION args; -+ int index = 0; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_DAC1: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: -+ index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl); -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_DAC2: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: -+ index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl); -+ break; -+ } -+ -+ args.ucAction = action; -+ args.ucDacStandard = ATOM_DAC1_PS2; -+ args.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+} -+ -+static u8 amdgpu_atombios_encoder_get_bpc(struct drm_encoder *encoder) -+{ -+ int bpc = 8; -+ -+ if (encoder->crtc) { -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(encoder->crtc); -+ bpc = amdgpu_crtc->bpc; -+ } -+ -+ switch (bpc) { -+ case 0: -+ return PANEL_BPC_UNDEFINE; -+ case 6: -+ return PANEL_6BIT_PER_COLOR; -+ case 8: -+ default: -+ return PANEL_8BIT_PER_COLOR; -+ case 10: -+ return PANEL_10BIT_PER_COLOR; -+ case 12: -+ return PANEL_12BIT_PER_COLOR; -+ case 16: -+ return PANEL_16BIT_PER_COLOR; -+ } -+} -+ -+union dvo_encoder_control { -+ ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds; -+ DVO_ENCODER_CONTROL_PS_ALLOCATION dvo; -+ DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3; -+ DVO_ENCODER_CONTROL_PS_ALLOCATION_V1_4 dvo_v4; -+}; -+ -+static void -+amdgpu_atombios_encoder_setup_dvo(struct drm_encoder *encoder, int action) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ union dvo_encoder_control args; -+ int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); -+ uint8_t frev, crev; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 1: -+ /* R4xx, R5xx */ -+ args.ext_tmds.sXTmdsEncoder.ucEnable = action; -+ -+ if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL; -+ -+ args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB; -+ break; -+ case 2: -+ /* RS600/690/740 */ -+ args.dvo.sDVOEncoder.ucAction = action; -+ args.dvo.sDVOEncoder.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ /* DFP1, CRT1, TV1 depending on the type of port */ -+ args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX; -+ -+ if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL; -+ break; -+ case 3: -+ /* R6xx */ -+ args.dvo_v3.ucAction = action; -+ args.dvo_v3.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ args.dvo_v3.ucDVOConfig = 0; /* XXX */ -+ break; -+ case 4: -+ /* DCE8 */ -+ args.dvo_v4.ucAction = action; -+ args.dvo_v4.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ args.dvo_v4.ucDVOConfig = 0; /* XXX */ -+ args.dvo_v4.ucBitPerColor = amdgpu_atombios_encoder_get_bpc(encoder); -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ break; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ break; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+int amdgpu_atombios_encoder_get_encoder_mode(struct drm_encoder *encoder) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_connector *connector; -+ struct amdgpu_connector *amdgpu_connector; -+ struct amdgpu_connector_atom_dig *dig_connector; -+ -+ /* dp bridges are always DP */ -+ if (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) -+ return ATOM_ENCODER_MODE_DP; -+ -+ /* DVO is always DVO */ -+ if ((amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DVO1) || -+ (amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)) -+ return ATOM_ENCODER_MODE_DVO; -+ -+ connector = amdgpu_get_connector_for_encoder(encoder); -+ /* if we don't have an active device yet, just use one of -+ * the connectors tied to the encoder. -+ */ -+ if (!connector) -+ connector = amdgpu_get_connector_for_encoder_init(encoder); -+ amdgpu_connector = to_amdgpu_connector(connector); -+ -+ switch (connector->connector_type) { -+ case DRM_MODE_CONNECTOR_DVII: -+ case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ -+ if (amdgpu_audio != 0) { -+ if (amdgpu_connector->use_digital && -+ (amdgpu_connector->audio == AMDGPU_AUDIO_ENABLE)) -+ return ATOM_ENCODER_MODE_HDMI; -+ else if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector)) && -+ (amdgpu_connector->audio == AMDGPU_AUDIO_AUTO)) -+ return ATOM_ENCODER_MODE_HDMI; -+ else if (amdgpu_connector->use_digital) -+ return ATOM_ENCODER_MODE_DVI; -+ else -+ return ATOM_ENCODER_MODE_CRT; -+ } else if (amdgpu_connector->use_digital) { -+ return ATOM_ENCODER_MODE_DVI; -+ } else { -+ return ATOM_ENCODER_MODE_CRT; -+ } -+ break; -+ case DRM_MODE_CONNECTOR_DVID: -+ case DRM_MODE_CONNECTOR_HDMIA: -+ default: -+ if (amdgpu_audio != 0) { -+ if (amdgpu_connector->audio == AMDGPU_AUDIO_ENABLE) -+ return ATOM_ENCODER_MODE_HDMI; -+ else if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector)) && -+ (amdgpu_connector->audio == AMDGPU_AUDIO_AUTO)) -+ return ATOM_ENCODER_MODE_HDMI; -+ else -+ return ATOM_ENCODER_MODE_DVI; -+ } else { -+ return ATOM_ENCODER_MODE_DVI; -+ } -+ break; -+ case DRM_MODE_CONNECTOR_LVDS: -+ return ATOM_ENCODER_MODE_LVDS; -+ break; -+ case DRM_MODE_CONNECTOR_DisplayPort: -+ dig_connector = amdgpu_connector->con_priv; -+ if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || -+ (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { -+ return ATOM_ENCODER_MODE_DP; -+ } else if (amdgpu_audio != 0) { -+ if (amdgpu_connector->audio == AMDGPU_AUDIO_ENABLE) -+ return ATOM_ENCODER_MODE_HDMI; -+ else if (drm_detect_hdmi_monitor(amdgpu_connector_edid(connector)) && -+ (amdgpu_connector->audio == AMDGPU_AUDIO_AUTO)) -+ return ATOM_ENCODER_MODE_HDMI; -+ else -+ return ATOM_ENCODER_MODE_DVI; -+ } else { -+ return ATOM_ENCODER_MODE_DVI; -+ } -+ break; -+ case DRM_MODE_CONNECTOR_eDP: -+ return ATOM_ENCODER_MODE_DP; -+ case DRM_MODE_CONNECTOR_DVIA: -+ case DRM_MODE_CONNECTOR_VGA: -+ return ATOM_ENCODER_MODE_CRT; -+ break; -+ case DRM_MODE_CONNECTOR_Composite: -+ case DRM_MODE_CONNECTOR_SVIDEO: -+ case DRM_MODE_CONNECTOR_9PinDIN: -+ /* fix me */ -+ return ATOM_ENCODER_MODE_TV; -+ /*return ATOM_ENCODER_MODE_CV;*/ -+ break; -+ } -+} -+ -+/* -+ * DIG Encoder/Transmitter Setup -+ * -+ * DCE 6.0 -+ * - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B). -+ * Supports up to 6 digital outputs -+ * - 6 DIG encoder blocks. -+ * - DIG to PHY mapping is hardcoded -+ * DIG1 drives UNIPHY0 link A, A+B -+ * DIG2 drives UNIPHY0 link B -+ * DIG3 drives UNIPHY1 link A, A+B -+ * DIG4 drives UNIPHY1 link B -+ * DIG5 drives UNIPHY2 link A, A+B -+ * DIG6 drives UNIPHY2 link B -+ * -+ * Routing -+ * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links) -+ * Examples: -+ * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI -+ * crtc1 -> dig1 -> UNIPHY0 link B -> DP -+ * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS -+ * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI -+ */ -+ -+union dig_encoder_control { -+ DIG_ENCODER_CONTROL_PS_ALLOCATION v1; -+ DIG_ENCODER_CONTROL_PARAMETERS_V2 v2; -+ DIG_ENCODER_CONTROL_PARAMETERS_V3 v3; -+ DIG_ENCODER_CONTROL_PARAMETERS_V4 v4; -+}; -+ -+void -+amdgpu_atombios_encoder_setup_dig_encoder(struct drm_encoder *encoder, -+ int action, int panel_mode) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder); -+ union dig_encoder_control args; -+ int index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl); -+ uint8_t frev, crev; -+ int dp_clock = 0; -+ int dp_lane_count = 0; -+ int hpd_id = AMDGPU_HPD_NONE; -+ -+ if (connector) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector = -+ amdgpu_connector->con_priv; -+ -+ dp_clock = dig_connector->dp_clock; -+ dp_lane_count = dig_connector->dp_lane_count; -+ hpd_id = amdgpu_connector->hpd.hpd; -+ } -+ -+ /* no dig encoder assigned */ -+ if (dig->dig_encoder == -1) -+ return; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 1: -+ args.v1.ucAction = action; -+ args.v1.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) -+ args.v3.ucPanelMode = panel_mode; -+ else -+ args.v1.ucEncoderMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ -+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) -+ args.v1.ucLaneNum = dp_lane_count; -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v1.ucLaneNum = 8; -+ else -+ args.v1.ucLaneNum = 4; -+ -+ if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000)) -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: -+ args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3; -+ break; -+ } -+ if (dig->linkb) -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; -+ else -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; -+ break; -+ case 2: -+ case 3: -+ args.v3.ucAction = action; -+ args.v3.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) -+ args.v3.ucPanelMode = panel_mode; -+ else -+ args.v3.ucEncoderMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ -+ if (ENCODER_MODE_IS_DP(args.v3.ucEncoderMode)) -+ args.v3.ucLaneNum = dp_lane_count; -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v3.ucLaneNum = 8; -+ else -+ args.v3.ucLaneNum = 4; -+ -+ if (ENCODER_MODE_IS_DP(args.v3.ucEncoderMode) && (dp_clock == 270000)) -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ; -+ args.v3.acConfig.ucDigSel = dig->dig_encoder; -+ args.v3.ucBitPerColor = amdgpu_atombios_encoder_get_bpc(encoder); -+ break; -+ case 4: -+ args.v4.ucAction = action; -+ args.v4.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE) -+ args.v4.ucPanelMode = panel_mode; -+ else -+ args.v4.ucEncoderMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ -+ if (ENCODER_MODE_IS_DP(args.v4.ucEncoderMode)) -+ args.v4.ucLaneNum = dp_lane_count; -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v4.ucLaneNum = 8; -+ else -+ args.v4.ucLaneNum = 4; -+ -+ if (ENCODER_MODE_IS_DP(args.v4.ucEncoderMode)) { -+ if (dp_clock == 540000) -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ; -+ else if (dp_clock == 324000) -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_3_24GHZ; -+ else if (dp_clock == 270000) -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ; -+ else -+ args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_1_62GHZ; -+ } -+ args.v4.acConfig.ucDigSel = dig->dig_encoder; -+ args.v4.ucBitPerColor = amdgpu_atombios_encoder_get_bpc(encoder); -+ if (hpd_id == AMDGPU_HPD_NONE) -+ args.v4.ucHPD_ID = 0; -+ else -+ args.v4.ucHPD_ID = hpd_id + 1; -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ break; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ break; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+} -+ -+union dig_transmitter_control { -+ DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1; -+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2; -+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3; -+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 v4; -+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 v5; -+}; -+ -+void -+amdgpu_atombios_encoder_setup_dig_transmitter(struct drm_encoder *encoder, int action, -+ uint8_t lane_num, uint8_t lane_set) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ struct drm_connector *connector; -+ union dig_transmitter_control args; -+ int index = 0; -+ uint8_t frev, crev; -+ bool is_dp = false; -+ int pll_id = 0; -+ int dp_clock = 0; -+ int dp_lane_count = 0; -+ int connector_object_id = 0; -+ int igp_lane_info = 0; -+ int dig_encoder = dig->dig_encoder; -+ int hpd_id = AMDGPU_HPD_NONE; -+ -+ if (action == ATOM_TRANSMITTER_ACTION_INIT) { -+ connector = amdgpu_get_connector_for_encoder_init(encoder); -+ /* just needed to avoid bailing in the encoder check. the encoder -+ * isn't used for init -+ */ -+ dig_encoder = 0; -+ } else -+ connector = amdgpu_get_connector_for_encoder(encoder); -+ -+ if (connector) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector = -+ amdgpu_connector->con_priv; -+ -+ hpd_id = amdgpu_connector->hpd.hpd; -+ dp_clock = dig_connector->dp_clock; -+ dp_lane_count = dig_connector->dp_lane_count; -+ connector_object_id = -+ (amdgpu_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; -+ } -+ -+ if (encoder->crtc) { -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(encoder->crtc); -+ pll_id = amdgpu_crtc->pll_id; -+ } -+ -+ /* no dig encoder assigned */ -+ if (dig_encoder == -1) -+ return; -+ -+ if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(encoder))) -+ is_dp = true; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: -+ index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: -+ index = GetIndexIntoMasterTable(COMMAND, LVTMATransmitterControl); -+ break; -+ } -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 1: -+ args.v1.ucAction = action; -+ if (action == ATOM_TRANSMITTER_ACTION_INIT) { -+ args.v1.usInitInfo = cpu_to_le16(connector_object_id); -+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { -+ args.v1.asMode.ucLaneSel = lane_num; -+ args.v1.asMode.ucLaneSet = lane_set; -+ } else { -+ if (is_dp) -+ args.v1.usPixelClock = cpu_to_le16(dp_clock / 10); -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v1.usPixelClock = cpu_to_le16((amdgpu_encoder->pixel_clock / 2) / 10); -+ else -+ args.v1.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ } -+ -+ args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL; -+ -+ if (dig_encoder) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER; -+ else -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER; -+ -+ if ((adev->flags & AMDGPU_IS_APU) && -+ (amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) { -+ if (is_dp || -+ !amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) { -+ if (igp_lane_info & 0x1) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3; -+ else if (igp_lane_info & 0x2) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_4_7; -+ else if (igp_lane_info & 0x4) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_11; -+ else if (igp_lane_info & 0x8) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15; -+ } else { -+ if (igp_lane_info & 0x3) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7; -+ else if (igp_lane_info & 0xc) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15; -+ } -+ } -+ -+ if (dig->linkb) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB; -+ else -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA; -+ -+ if (is_dp) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; -+ else if (amdgpu_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { -+ if (dig->coherent_mode) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; -+ if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK; -+ } -+ break; -+ case 2: -+ args.v2.ucAction = action; -+ if (action == ATOM_TRANSMITTER_ACTION_INIT) { -+ args.v2.usInitInfo = cpu_to_le16(connector_object_id); -+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { -+ args.v2.asMode.ucLaneSel = lane_num; -+ args.v2.asMode.ucLaneSet = lane_set; -+ } else { -+ if (is_dp) -+ args.v2.usPixelClock = cpu_to_le16(dp_clock / 10); -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v2.usPixelClock = cpu_to_le16((amdgpu_encoder->pixel_clock / 2) / 10); -+ else -+ args.v2.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ } -+ -+ args.v2.acConfig.ucEncoderSel = dig_encoder; -+ if (dig->linkb) -+ args.v2.acConfig.ucLinkSel = 1; -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ args.v2.acConfig.ucTransmitterSel = 0; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ args.v2.acConfig.ucTransmitterSel = 1; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ args.v2.acConfig.ucTransmitterSel = 2; -+ break; -+ } -+ -+ if (is_dp) { -+ args.v2.acConfig.fCoherentMode = 1; -+ args.v2.acConfig.fDPConnector = 1; -+ } else if (amdgpu_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { -+ if (dig->coherent_mode) -+ args.v2.acConfig.fCoherentMode = 1; -+ if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v2.acConfig.fDualLinkConnector = 1; -+ } -+ break; -+ case 3: -+ args.v3.ucAction = action; -+ if (action == ATOM_TRANSMITTER_ACTION_INIT) { -+ args.v3.usInitInfo = cpu_to_le16(connector_object_id); -+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { -+ args.v3.asMode.ucLaneSel = lane_num; -+ args.v3.asMode.ucLaneSet = lane_set; -+ } else { -+ if (is_dp) -+ args.v3.usPixelClock = cpu_to_le16(dp_clock / 10); -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v3.usPixelClock = cpu_to_le16((amdgpu_encoder->pixel_clock / 2) / 10); -+ else -+ args.v3.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ } -+ -+ if (is_dp) -+ args.v3.ucLaneNum = dp_lane_count; -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v3.ucLaneNum = 8; -+ else -+ args.v3.ucLaneNum = 4; -+ -+ if (dig->linkb) -+ args.v3.acConfig.ucLinkSel = 1; -+ if (dig_encoder & 1) -+ args.v3.acConfig.ucEncoderSel = 1; -+ -+ /* Select the PLL for the PHY -+ * DP PHY should be clocked from external src if there is -+ * one. -+ */ -+ /* On DCE4, if there is an external clock, it generates the DP ref clock */ -+ if (is_dp && adev->clock.dp_extclk) -+ args.v3.acConfig.ucRefClkSource = 2; /* external src */ -+ else -+ args.v3.acConfig.ucRefClkSource = pll_id; -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ args.v3.acConfig.ucTransmitterSel = 0; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ args.v3.acConfig.ucTransmitterSel = 1; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ args.v3.acConfig.ucTransmitterSel = 2; -+ break; -+ } -+ -+ if (is_dp) -+ args.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */ -+ else if (amdgpu_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { -+ if (dig->coherent_mode) -+ args.v3.acConfig.fCoherentMode = 1; -+ if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v3.acConfig.fDualLinkConnector = 1; -+ } -+ break; -+ case 4: -+ args.v4.ucAction = action; -+ if (action == ATOM_TRANSMITTER_ACTION_INIT) { -+ args.v4.usInitInfo = cpu_to_le16(connector_object_id); -+ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) { -+ args.v4.asMode.ucLaneSel = lane_num; -+ args.v4.asMode.ucLaneSet = lane_set; -+ } else { -+ if (is_dp) -+ args.v4.usPixelClock = cpu_to_le16(dp_clock / 10); -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v4.usPixelClock = cpu_to_le16((amdgpu_encoder->pixel_clock / 2) / 10); -+ else -+ args.v4.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ } -+ -+ if (is_dp) -+ args.v4.ucLaneNum = dp_lane_count; -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v4.ucLaneNum = 8; -+ else -+ args.v4.ucLaneNum = 4; -+ -+ if (dig->linkb) -+ args.v4.acConfig.ucLinkSel = 1; -+ if (dig_encoder & 1) -+ args.v4.acConfig.ucEncoderSel = 1; -+ -+ /* Select the PLL for the PHY -+ * DP PHY should be clocked from external src if there is -+ * one. -+ */ -+ /* On DCE5 DCPLL usually generates the DP ref clock */ -+ if (is_dp) { -+ if (adev->clock.dp_extclk) -+ args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK; -+ else -+ args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL; -+ } else -+ args.v4.acConfig.ucRefClkSource = pll_id; -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ args.v4.acConfig.ucTransmitterSel = 0; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ args.v4.acConfig.ucTransmitterSel = 1; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ args.v4.acConfig.ucTransmitterSel = 2; -+ break; -+ } -+ -+ if (is_dp) -+ args.v4.acConfig.fCoherentMode = 1; /* DP requires coherent */ -+ else if (amdgpu_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { -+ if (dig->coherent_mode) -+ args.v4.acConfig.fCoherentMode = 1; -+ if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v4.acConfig.fDualLinkConnector = 1; -+ } -+ break; -+ case 5: -+ args.v5.ucAction = action; -+ if (is_dp) -+ args.v5.usSymClock = cpu_to_le16(dp_clock / 10); -+ else -+ args.v5.usSymClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ if (dig->linkb) -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYB; -+ else -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYA; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ if (dig->linkb) -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYD; -+ else -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYC; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ if (dig->linkb) -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYF; -+ else -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYE; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYG; -+ break; -+ } -+ if (is_dp) -+ args.v5.ucLaneNum = dp_lane_count; -+ else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v5.ucLaneNum = 8; -+ else -+ args.v5.ucLaneNum = 4; -+ args.v5.ucConnObjId = connector_object_id; -+ args.v5.ucDigMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ -+ if (is_dp && adev->clock.dp_extclk) -+ args.v5.asConfig.ucPhyClkSrcId = ENCODER_REFCLK_SRC_EXTCLK; -+ else -+ args.v5.asConfig.ucPhyClkSrcId = pll_id; -+ -+ if (is_dp) -+ args.v5.asConfig.ucCoherentMode = 1; /* DP requires coherent */ -+ else if (amdgpu_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { -+ if (dig->coherent_mode) -+ args.v5.asConfig.ucCoherentMode = 1; -+ } -+ if (hpd_id == AMDGPU_HPD_NONE) -+ args.v5.asConfig.ucHPDSel = 0; -+ else -+ args.v5.asConfig.ucHPDSel = hpd_id + 1; -+ args.v5.ucDigEncoderSel = 1 << dig_encoder; -+ args.v5.ucDPLaneSet = lane_set; -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ break; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version %d, %d\n", frev, crev); -+ break; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+bool -+amdgpu_atombios_encoder_set_edp_panel_power(struct drm_connector *connector, -+ int action) -+{ -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_device *dev = amdgpu_connector->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ union dig_transmitter_control args; -+ int index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); -+ uint8_t frev, crev; -+ -+ if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) -+ goto done; -+ -+ if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) && -+ (action != ATOM_TRANSMITTER_ACTION_POWER_OFF)) -+ goto done; -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ goto done; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ args.v1.ucAction = action; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ /* wait for the panel to power up */ -+ if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) { -+ int i; -+ -+ for (i = 0; i < 300; i++) { -+ if (amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) -+ return true; -+ mdelay(1); -+ } -+ return false; -+ } -+done: -+ return true; -+} -+ -+union external_encoder_control { -+ EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION v1; -+ EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 v3; -+}; -+ -+static void -+amdgpu_atombios_encoder_setup_external_encoder(struct drm_encoder *encoder, -+ struct drm_encoder *ext_encoder, -+ int action) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_encoder *ext_amdgpu_encoder = to_amdgpu_encoder(ext_encoder); -+ union external_encoder_control args; -+ struct drm_connector *connector; -+ int index = GetIndexIntoMasterTable(COMMAND, ExternalEncoderControl); -+ u8 frev, crev; -+ int dp_clock = 0; -+ int dp_lane_count = 0; -+ int connector_object_id = 0; -+ u32 ext_enum = (ext_amdgpu_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; -+ -+ if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT) -+ connector = amdgpu_get_connector_for_encoder_init(encoder); -+ else -+ connector = amdgpu_get_connector_for_encoder(encoder); -+ -+ if (connector) { -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct amdgpu_connector_atom_dig *dig_connector = -+ amdgpu_connector->con_priv; -+ -+ dp_clock = dig_connector->dp_clock; -+ dp_lane_count = dig_connector->dp_lane_count; -+ connector_object_id = -+ (amdgpu_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; -+ } -+ -+ memset(&args, 0, sizeof(args)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ /* no params on frev 1 */ -+ break; -+ case 2: -+ switch (crev) { -+ case 1: -+ case 2: -+ args.v1.sDigEncoder.ucAction = action; -+ args.v1.sDigEncoder.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ args.v1.sDigEncoder.ucEncoderMode = -+ amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ -+ if (ENCODER_MODE_IS_DP(args.v1.sDigEncoder.ucEncoderMode)) { -+ if (dp_clock == 270000) -+ args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; -+ args.v1.sDigEncoder.ucLaneNum = dp_lane_count; -+ } else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v1.sDigEncoder.ucLaneNum = 8; -+ else -+ args.v1.sDigEncoder.ucLaneNum = 4; -+ break; -+ case 3: -+ args.v3.sExtEncoder.ucAction = action; -+ if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT) -+ args.v3.sExtEncoder.usConnectorId = cpu_to_le16(connector_object_id); -+ else -+ args.v3.sExtEncoder.usPixelClock = cpu_to_le16(amdgpu_encoder->pixel_clock / 10); -+ args.v3.sExtEncoder.ucEncoderMode = -+ amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ -+ if (ENCODER_MODE_IS_DP(args.v3.sExtEncoder.ucEncoderMode)) { -+ if (dp_clock == 270000) -+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ; -+ else if (dp_clock == 540000) -+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ; -+ args.v3.sExtEncoder.ucLaneNum = dp_lane_count; -+ } else if (amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) -+ args.v3.sExtEncoder.ucLaneNum = 8; -+ else -+ args.v3.sExtEncoder.ucLaneNum = 4; -+ switch (ext_enum) { -+ case GRAPH_OBJECT_ENUM_ID1: -+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER1; -+ break; -+ case GRAPH_OBJECT_ENUM_ID2: -+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER2; -+ break; -+ case GRAPH_OBJECT_ENUM_ID3: -+ args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER3; -+ break; -+ } -+ args.v3.sExtEncoder.ucBitPerColor = amdgpu_atombios_encoder_get_bpc(encoder); -+ break; -+ default: -+ DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); -+ return; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); -+ return; -+ } -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+static void -+amdgpu_atombios_encoder_setup_dig(struct drm_encoder *encoder, int action) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_encoder *ext_encoder = amdgpu_get_external_encoder(encoder); -+ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; -+ struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder); -+ struct amdgpu_connector *amdgpu_connector = NULL; -+ struct amdgpu_connector_atom_dig *amdgpu_dig_connector = NULL; -+ -+ if (connector) { -+ amdgpu_connector = to_amdgpu_connector(connector); -+ amdgpu_dig_connector = amdgpu_connector->con_priv; -+ } -+ -+ if (action == ATOM_ENABLE) { -+ if (!connector) -+ dig->panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; -+ else -+ dig->panel_mode = amdgpu_atombios_dp_get_panel_mode(encoder, connector); -+ -+ /* setup and enable the encoder */ -+ amdgpu_atombios_encoder_setup_dig_encoder(encoder, ATOM_ENCODER_CMD_SETUP, 0); -+ amdgpu_atombios_encoder_setup_dig_encoder(encoder, -+ ATOM_ENCODER_CMD_SETUP_PANEL_MODE, -+ dig->panel_mode); -+ if (ext_encoder) -+ amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, -+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP); -+ if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(encoder)) && -+ connector) { -+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { -+ amdgpu_atombios_encoder_set_edp_panel_power(connector, -+ ATOM_TRANSMITTER_ACTION_POWER_ON); -+ amdgpu_dig_connector->edp_on = true; -+ } -+ } -+ /* enable the transmitter */ -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_ENABLE, -+ 0, 0); -+ if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(encoder)) && -+ connector) { -+ /* DP_SET_POWER_D0 is set in amdgpu_atombios_dp_link_train */ -+ amdgpu_atombios_dp_link_train(encoder, connector); -+ amdgpu_atombios_encoder_setup_dig_encoder(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); -+ } -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); -+ if (ext_encoder) -+ amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, ATOM_ENABLE); -+ } else { -+ if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(encoder)) && -+ connector) -+ amdgpu_atombios_encoder_setup_dig_encoder(encoder, -+ ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); -+ if (ext_encoder) -+ amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, ATOM_DISABLE); -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0); -+ -+ if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(encoder)) && -+ connector) -+ amdgpu_atombios_dp_set_rx_power_state(connector, DP_SET_POWER_D3); -+ /* disable the transmitter */ -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, -+ ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); -+ if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(encoder)) && -+ connector) { -+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { -+ amdgpu_atombios_encoder_set_edp_panel_power(connector, -+ ATOM_TRANSMITTER_ACTION_POWER_OFF); -+ amdgpu_dig_connector->edp_on = false; -+ } -+ } -+ } -+} -+ -+void -+amdgpu_atombios_encoder_dpms(struct drm_encoder *encoder, int mode) -+{ -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ -+ DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n", -+ amdgpu_encoder->encoder_id, mode, amdgpu_encoder->devices, -+ amdgpu_encoder->active_device); -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ switch (mode) { -+ case DRM_MODE_DPMS_ON: -+ amdgpu_atombios_encoder_setup_dig(encoder, ATOM_ENABLE); -+ break; -+ case DRM_MODE_DPMS_STANDBY: -+ case DRM_MODE_DPMS_SUSPEND: -+ case DRM_MODE_DPMS_OFF: -+ amdgpu_atombios_encoder_setup_dig(encoder, ATOM_DISABLE); -+ break; -+ } -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: -+ switch (mode) { -+ case DRM_MODE_DPMS_ON: -+ amdgpu_atombios_encoder_setup_dvo(encoder, ATOM_ENABLE); -+ break; -+ case DRM_MODE_DPMS_STANDBY: -+ case DRM_MODE_DPMS_SUSPEND: -+ case DRM_MODE_DPMS_OFF: -+ amdgpu_atombios_encoder_setup_dvo(encoder, ATOM_DISABLE); -+ break; -+ } -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: -+ switch (mode) { -+ case DRM_MODE_DPMS_ON: -+ amdgpu_atombios_encoder_setup_dac(encoder, ATOM_ENABLE); -+ break; -+ case DRM_MODE_DPMS_STANDBY: -+ case DRM_MODE_DPMS_SUSPEND: -+ case DRM_MODE_DPMS_OFF: -+ amdgpu_atombios_encoder_setup_dac(encoder, ATOM_DISABLE); -+ break; -+ } -+ break; -+ default: -+ return; -+ } -+} -+ -+union crtc_source_param { -+ SELECT_CRTC_SOURCE_PS_ALLOCATION v1; -+ SELECT_CRTC_SOURCE_PARAMETERS_V2 v2; -+ SELECT_CRTC_SOURCE_PARAMETERS_V3 v3; -+}; -+ -+void -+amdgpu_atombios_encoder_set_crtc_source(struct drm_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(encoder->crtc); -+ union crtc_source_param args; -+ int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source); -+ uint8_t frev, crev; -+ struct amdgpu_encoder_atom_dig *dig; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return; -+ -+ switch (frev) { -+ case 1: -+ switch (crev) { -+ case 1: -+ default: -+ args.v1.ucCRTC = amdgpu_crtc->crtc_id; -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: -+ args.v1.ucDevice = ATOM_DEVICE_DFP1_INDEX; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_LVDS: -+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1: -+ if (amdgpu_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) -+ args.v1.ucDevice = ATOM_DEVICE_LCD1_INDEX; -+ else -+ args.v1.ucDevice = ATOM_DEVICE_DFP3_INDEX; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_DVO1: -+ case ENCODER_OBJECT_ID_INTERNAL_DDI: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: -+ args.v1.ucDevice = ATOM_DEVICE_DFP2_INDEX; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_DAC1: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; -+ else if (amdgpu_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) -+ args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; -+ else -+ args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_DAC2: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; -+ else if (amdgpu_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) -+ args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; -+ else -+ args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX; -+ break; -+ } -+ break; -+ case 2: -+ args.v2.ucCRTC = amdgpu_crtc->crtc_id; -+ if (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) { -+ struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder); -+ -+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) -+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; -+ else if (connector->connector_type == DRM_MODE_CONNECTOR_VGA) -+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_CRT; -+ else -+ args.v2.ucEncodeMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ } else if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { -+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; -+ } else { -+ args.v2.ucEncodeMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ } -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: -+ dig = amdgpu_encoder->enc_priv; -+ switch (dig->dig_encoder) { -+ case 0: -+ args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; -+ break; -+ case 1: -+ args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; -+ break; -+ case 2: -+ args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID; -+ break; -+ case 3: -+ args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID; -+ break; -+ case 4: -+ args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID; -+ break; -+ case 5: -+ args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID; -+ break; -+ case 6: -+ args.v2.ucEncoderID = ASIC_INT_DIG7_ENCODER_ID; -+ break; -+ } -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: -+ args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else if (amdgpu_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) -+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else -+ args.v2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else if (amdgpu_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) -+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else -+ args.v2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID; -+ break; -+ } -+ break; -+ case 3: -+ args.v3.ucCRTC = amdgpu_crtc->crtc_id; -+ if (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) { -+ struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder); -+ -+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) -+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; -+ else if (connector->connector_type == DRM_MODE_CONNECTOR_VGA) -+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_CRT; -+ else -+ args.v2.ucEncodeMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ } else if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { -+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; -+ } else { -+ args.v2.ucEncodeMode = amdgpu_atombios_encoder_get_encoder_mode(encoder); -+ } -+ args.v3.ucDstBpc = amdgpu_atombios_encoder_get_bpc(encoder); -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: -+ dig = amdgpu_encoder->enc_priv; -+ switch (dig->dig_encoder) { -+ case 0: -+ args.v3.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID; -+ break; -+ case 1: -+ args.v3.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; -+ break; -+ case 2: -+ args.v3.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID; -+ break; -+ case 3: -+ args.v3.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID; -+ break; -+ case 4: -+ args.v3.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID; -+ break; -+ case 5: -+ args.v3.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID; -+ break; -+ case 6: -+ args.v3.ucEncoderID = ASIC_INT_DIG7_ENCODER_ID; -+ break; -+ } -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: -+ args.v3.ucEncoderID = ASIC_INT_DVO_ENCODER_ID; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ args.v3.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else if (amdgpu_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) -+ args.v3.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else -+ args.v3.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID; -+ break; -+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: -+ if (amdgpu_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) -+ args.v3.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else if (amdgpu_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) -+ args.v3.ucEncoderID = ASIC_INT_TV_ENCODER_ID; -+ else -+ args.v3.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID; -+ break; -+ } -+ break; -+ } -+ break; -+ default: -+ DRM_ERROR("Unknown table version: %d, %d\n", frev, crev); -+ return; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+} -+ -+/* This only needs to be called once at startup */ -+void -+amdgpu_atombios_encoder_init_dig(struct amdgpu_device *adev) -+{ -+ struct drm_device *dev = adev->ddev; -+ struct drm_encoder *encoder; -+ -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct drm_encoder *ext_encoder = amdgpu_get_external_encoder(encoder); -+ -+ switch (amdgpu_encoder->encoder_id) { -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: -+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: -+ amdgpu_atombios_encoder_setup_dig_transmitter(encoder, ATOM_TRANSMITTER_ACTION_INIT, -+ 0, 0); -+ break; -+ } -+ -+ if (ext_encoder) -+ amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, -+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT); -+ } -+} -+ -+static bool -+amdgpu_atombios_encoder_dac_load_detect(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ -+ if (amdgpu_encoder->devices & (ATOM_DEVICE_TV_SUPPORT | -+ ATOM_DEVICE_CV_SUPPORT | -+ ATOM_DEVICE_CRT_SUPPORT)) { -+ DAC_LOAD_DETECTION_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection); -+ uint8_t frev, crev; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev)) -+ return false; -+ -+ args.sDacload.ucMisc = 0; -+ -+ if ((amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) || -+ (amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)) -+ args.sDacload.ucDacType = ATOM_DAC_A; -+ else -+ args.sDacload.ucDacType = ATOM_DAC_B; -+ -+ if (amdgpu_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) -+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT); -+ else if (amdgpu_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) -+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT); -+ else if (amdgpu_connector->devices & ATOM_DEVICE_CV_SUPPORT) { -+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT); -+ if (crev >= 3) -+ args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb; -+ } else if (amdgpu_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { -+ args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT); -+ if (crev >= 3) -+ args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb; -+ } -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ return true; -+ } else -+ return false; -+} -+ -+enum drm_connector_status -+amdgpu_atombios_encoder_dac_detect(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ uint32_t bios_0_scratch; -+ -+ if (!amdgpu_atombios_encoder_dac_load_detect(encoder, connector)) { -+ DRM_DEBUG_KMS("detect returned false \n"); -+ return connector_status_unknown; -+ } -+ -+ bios_0_scratch = RREG32(mmBIOS_SCRATCH_0); -+ -+ DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, amdgpu_encoder->devices); -+ if (amdgpu_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) { -+ if (bios_0_scratch & ATOM_S0_CRT1_MASK) -+ return connector_status_connected; -+ } -+ if (amdgpu_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) { -+ if (bios_0_scratch & ATOM_S0_CRT2_MASK) -+ return connector_status_connected; -+ } -+ if (amdgpu_connector->devices & ATOM_DEVICE_CV_SUPPORT) { -+ if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) -+ return connector_status_connected; -+ } -+ if (amdgpu_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { -+ if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) -+ return connector_status_connected; /* CTV */ -+ else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) -+ return connector_status_connected; /* STV */ -+ } -+ return connector_status_disconnected; -+} -+ -+enum drm_connector_status -+amdgpu_atombios_encoder_dig_detect(struct drm_encoder *encoder, -+ struct drm_connector *connector) -+{ -+ struct drm_device *dev = encoder->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); -+ struct drm_encoder *ext_encoder = amdgpu_get_external_encoder(encoder); -+ u32 bios_0_scratch; -+ -+ if (!ext_encoder) -+ return connector_status_unknown; -+ -+ if ((amdgpu_connector->devices & ATOM_DEVICE_CRT_SUPPORT) == 0) -+ return connector_status_unknown; -+ -+ /* load detect on the dp bridge */ -+ amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, -+ EXTERNAL_ENCODER_ACTION_V3_DACLOAD_DETECTION); -+ -+ bios_0_scratch = RREG32(mmBIOS_SCRATCH_0); -+ -+ DRM_DEBUG_KMS("Bios 0 scratch %x %08x\n", bios_0_scratch, amdgpu_encoder->devices); -+ if (amdgpu_connector->devices & ATOM_DEVICE_CRT1_SUPPORT) { -+ if (bios_0_scratch & ATOM_S0_CRT1_MASK) -+ return connector_status_connected; -+ } -+ if (amdgpu_connector->devices & ATOM_DEVICE_CRT2_SUPPORT) { -+ if (bios_0_scratch & ATOM_S0_CRT2_MASK) -+ return connector_status_connected; -+ } -+ if (amdgpu_connector->devices & ATOM_DEVICE_CV_SUPPORT) { -+ if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) -+ return connector_status_connected; -+ } -+ if (amdgpu_connector->devices & ATOM_DEVICE_TV1_SUPPORT) { -+ if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) -+ return connector_status_connected; /* CTV */ -+ else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) -+ return connector_status_connected; /* STV */ -+ } -+ return connector_status_disconnected; -+} -+ -+void -+amdgpu_atombios_encoder_setup_ext_encoder_ddc(struct drm_encoder *encoder) -+{ -+ struct drm_encoder *ext_encoder = amdgpu_get_external_encoder(encoder); -+ -+ if (ext_encoder) -+ /* ddc_setup on the dp bridge */ -+ amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, -+ EXTERNAL_ENCODER_ACTION_V3_DDC_SETUP); -+ -+} -+ -+void -+amdgpu_atombios_encoder_set_bios_scratch_regs(struct drm_connector *connector, -+ struct drm_encoder *encoder, -+ bool connected) -+{ -+ struct drm_device *dev = connector->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_connector *amdgpu_connector = -+ to_amdgpu_connector(connector); -+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); -+ uint32_t bios_0_scratch, bios_3_scratch, bios_6_scratch; -+ -+ bios_0_scratch = RREG32(mmBIOS_SCRATCH_0); -+ bios_3_scratch = RREG32(mmBIOS_SCRATCH_3); -+ bios_6_scratch = RREG32(mmBIOS_SCRATCH_6); -+ -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_LCD1_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("LCD1 connected\n"); -+ bios_0_scratch |= ATOM_S0_LCD1; -+ bios_3_scratch |= ATOM_S3_LCD1_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_LCD1; -+ } else { -+ DRM_DEBUG_KMS("LCD1 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_LCD1; -+ bios_3_scratch &= ~ATOM_S3_LCD1_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_LCD1; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("CRT1 connected\n"); -+ bios_0_scratch |= ATOM_S0_CRT1_COLOR; -+ bios_3_scratch |= ATOM_S3_CRT1_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_CRT1; -+ } else { -+ DRM_DEBUG_KMS("CRT1 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_CRT1_MASK; -+ bios_3_scratch &= ~ATOM_S3_CRT1_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_CRT1; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("CRT2 connected\n"); -+ bios_0_scratch |= ATOM_S0_CRT2_COLOR; -+ bios_3_scratch |= ATOM_S3_CRT2_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_CRT2; -+ } else { -+ DRM_DEBUG_KMS("CRT2 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_CRT2_MASK; -+ bios_3_scratch &= ~ATOM_S3_CRT2_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_CRT2; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_DFP1_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("DFP1 connected\n"); -+ bios_0_scratch |= ATOM_S0_DFP1; -+ bios_3_scratch |= ATOM_S3_DFP1_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP1; -+ } else { -+ DRM_DEBUG_KMS("DFP1 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_DFP1; -+ bios_3_scratch &= ~ATOM_S3_DFP1_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP1; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_DFP2_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("DFP2 connected\n"); -+ bios_0_scratch |= ATOM_S0_DFP2; -+ bios_3_scratch |= ATOM_S3_DFP2_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP2; -+ } else { -+ DRM_DEBUG_KMS("DFP2 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_DFP2; -+ bios_3_scratch &= ~ATOM_S3_DFP2_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP2; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_DFP3_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_DFP3_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("DFP3 connected\n"); -+ bios_0_scratch |= ATOM_S0_DFP3; -+ bios_3_scratch |= ATOM_S3_DFP3_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP3; -+ } else { -+ DRM_DEBUG_KMS("DFP3 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_DFP3; -+ bios_3_scratch &= ~ATOM_S3_DFP3_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP3; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_DFP4_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_DFP4_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("DFP4 connected\n"); -+ bios_0_scratch |= ATOM_S0_DFP4; -+ bios_3_scratch |= ATOM_S3_DFP4_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP4; -+ } else { -+ DRM_DEBUG_KMS("DFP4 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_DFP4; -+ bios_3_scratch &= ~ATOM_S3_DFP4_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP4; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_DFP5_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_DFP5_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("DFP5 connected\n"); -+ bios_0_scratch |= ATOM_S0_DFP5; -+ bios_3_scratch |= ATOM_S3_DFP5_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP5; -+ } else { -+ DRM_DEBUG_KMS("DFP5 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_DFP5; -+ bios_3_scratch &= ~ATOM_S3_DFP5_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP5; -+ } -+ } -+ if ((amdgpu_encoder->devices & ATOM_DEVICE_DFP6_SUPPORT) && -+ (amdgpu_connector->devices & ATOM_DEVICE_DFP6_SUPPORT)) { -+ if (connected) { -+ DRM_DEBUG_KMS("DFP6 connected\n"); -+ bios_0_scratch |= ATOM_S0_DFP6; -+ bios_3_scratch |= ATOM_S3_DFP6_ACTIVE; -+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP6; -+ } else { -+ DRM_DEBUG_KMS("DFP6 disconnected\n"); -+ bios_0_scratch &= ~ATOM_S0_DFP6; -+ bios_3_scratch &= ~ATOM_S3_DFP6_ACTIVE; -+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP6; -+ } -+ } -+ -+ WREG32(mmBIOS_SCRATCH_0, bios_0_scratch); -+ WREG32(mmBIOS_SCRATCH_3, bios_3_scratch); -+ WREG32(mmBIOS_SCRATCH_6, bios_6_scratch); -+} -+ -+union lvds_info { -+ struct _ATOM_LVDS_INFO info; -+ struct _ATOM_LVDS_INFO_V12 info_12; -+}; -+ -+struct amdgpu_encoder_atom_dig * -+amdgpu_atombios_encoder_get_lcd_info(struct amdgpu_encoder *encoder) -+{ -+ struct drm_device *dev = encoder->base.dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ struct amdgpu_mode_info *mode_info = &adev->mode_info; -+ int index = GetIndexIntoMasterTable(DATA, LVDS_Info); -+ uint16_t data_offset, misc; -+ union lvds_info *lvds_info; -+ uint8_t frev, crev; -+ struct amdgpu_encoder_atom_dig *lvds = NULL; -+ int encoder_enum = (encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; -+ -+ if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, -+ &frev, &crev, &data_offset)) { -+ lvds_info = -+ (union lvds_info *)(mode_info->atom_context->bios + data_offset); -+ lvds = -+ kzalloc(sizeof(struct amdgpu_encoder_atom_dig), GFP_KERNEL); -+ -+ if (!lvds) -+ return NULL; -+ -+ lvds->native_mode.clock = -+ le16_to_cpu(lvds_info->info.sLCDTiming.usPixClk) * 10; -+ lvds->native_mode.hdisplay = -+ le16_to_cpu(lvds_info->info.sLCDTiming.usHActive); -+ lvds->native_mode.vdisplay = -+ le16_to_cpu(lvds_info->info.sLCDTiming.usVActive); -+ lvds->native_mode.htotal = lvds->native_mode.hdisplay + -+ le16_to_cpu(lvds_info->info.sLCDTiming.usHBlanking_Time); -+ lvds->native_mode.hsync_start = lvds->native_mode.hdisplay + -+ le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncOffset); -+ lvds->native_mode.hsync_end = lvds->native_mode.hsync_start + -+ le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncWidth); -+ lvds->native_mode.vtotal = lvds->native_mode.vdisplay + -+ le16_to_cpu(lvds_info->info.sLCDTiming.usVBlanking_Time); -+ lvds->native_mode.vsync_start = lvds->native_mode.vdisplay + -+ le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncOffset); -+ lvds->native_mode.vsync_end = lvds->native_mode.vsync_start + -+ le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); -+ lvds->panel_pwr_delay = -+ le16_to_cpu(lvds_info->info.usOffDelayInMs); -+ lvds->lcd_misc = lvds_info->info.ucLVDS_Misc; -+ -+ misc = le16_to_cpu(lvds_info->info.sLCDTiming.susModeMiscInfo.usAccess); -+ if (misc & ATOM_VSYNC_POLARITY) -+ lvds->native_mode.flags |= DRM_MODE_FLAG_NVSYNC; -+ if (misc & ATOM_HSYNC_POLARITY) -+ lvds->native_mode.flags |= DRM_MODE_FLAG_NHSYNC; -+ if (misc & ATOM_COMPOSITESYNC) -+ lvds->native_mode.flags |= DRM_MODE_FLAG_CSYNC; -+ if (misc & ATOM_INTERLACE) -+ lvds->native_mode.flags |= DRM_MODE_FLAG_INTERLACE; -+ if (misc & ATOM_DOUBLE_CLOCK_MODE) -+ lvds->native_mode.flags |= DRM_MODE_FLAG_DBLSCAN; -+ -+ lvds->native_mode.width_mm = le16_to_cpu(lvds_info->info.sLCDTiming.usImageHSize); -+ lvds->native_mode.height_mm = le16_to_cpu(lvds_info->info.sLCDTiming.usImageVSize); -+ -+ /* set crtc values */ -+ drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V); -+ -+ lvds->lcd_ss_id = lvds_info->info.ucSS_Id; -+ -+ encoder->native_mode = lvds->native_mode; -+ -+ if (encoder_enum == 2) -+ lvds->linkb = true; -+ else -+ lvds->linkb = false; -+ -+ /* parse the lcd record table */ -+ if (le16_to_cpu(lvds_info->info.usModePatchTableOffset)) { -+ ATOM_FAKE_EDID_PATCH_RECORD *fake_edid_record; -+ ATOM_PANEL_RESOLUTION_PATCH_RECORD *panel_res_record; -+ bool bad_record = false; -+ u8 *record; -+ -+ if ((frev == 1) && (crev < 2)) -+ /* absolute */ -+ record = (u8 *)(mode_info->atom_context->bios + -+ le16_to_cpu(lvds_info->info.usModePatchTableOffset)); -+ else -+ /* relative */ -+ record = (u8 *)(mode_info->atom_context->bios + -+ data_offset + -+ le16_to_cpu(lvds_info->info.usModePatchTableOffset)); -+ while (*record != ATOM_RECORD_END_TYPE) { -+ switch (*record) { -+ case LCD_MODE_PATCH_RECORD_MODE_TYPE: -+ record += sizeof(ATOM_PATCH_RECORD_MODE); -+ break; -+ case LCD_RTS_RECORD_TYPE: -+ record += sizeof(ATOM_LCD_RTS_RECORD); -+ break; -+ case LCD_CAP_RECORD_TYPE: -+ record += sizeof(ATOM_LCD_MODE_CONTROL_CAP); -+ break; -+ case LCD_FAKE_EDID_PATCH_RECORD_TYPE: -+ fake_edid_record = (ATOM_FAKE_EDID_PATCH_RECORD *)record; -+ if (fake_edid_record->ucFakeEDIDLength) { -+ struct edid *edid; -+ int edid_size = -+ max((int)EDID_LENGTH, (int)fake_edid_record->ucFakeEDIDLength); -+ edid = kmalloc(edid_size, GFP_KERNEL); -+ if (edid) { -+ memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0], -+ fake_edid_record->ucFakeEDIDLength); -+ -+ if (drm_edid_is_valid(edid)) { -+ adev->mode_info.bios_hardcoded_edid = edid; -+ adev->mode_info.bios_hardcoded_edid_size = edid_size; -+ } else -+ kfree(edid); -+ } -+ } -+ record += fake_edid_record->ucFakeEDIDLength ? -+ fake_edid_record->ucFakeEDIDLength + 2 : -+ sizeof(ATOM_FAKE_EDID_PATCH_RECORD); -+ break; -+ case LCD_PANEL_RESOLUTION_RECORD_TYPE: -+ panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record; -+ lvds->native_mode.width_mm = panel_res_record->usHSize; -+ lvds->native_mode.height_mm = panel_res_record->usVSize; -+ record += sizeof(ATOM_PANEL_RESOLUTION_PATCH_RECORD); -+ break; -+ default: -+ DRM_ERROR("Bad LCD record %d\n", *record); -+ bad_record = true; -+ break; -+ } -+ if (bad_record) -+ break; -+ } -+ } -+ } -+ return lvds; -+} -+ -+struct amdgpu_encoder_atom_dig * -+amdgpu_atombios_encoder_get_dig_info(struct amdgpu_encoder *amdgpu_encoder) -+{ -+ int encoder_enum = (amdgpu_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT; -+ struct amdgpu_encoder_atom_dig *dig = kzalloc(sizeof(struct amdgpu_encoder_atom_dig), GFP_KERNEL); -+ -+ if (!dig) -+ return NULL; -+ -+ /* coherent mode by default */ -+ dig->coherent_mode = true; -+ dig->dig_encoder = -1; -+ -+ if (encoder_enum == 2) -+ dig->linkb = true; -+ else -+ dig->linkb = false; -+ -+ return dig; -+} -+ -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.h b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.h -new file mode 100644 -index 0000000..2bdec40 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.h -@@ -0,0 +1,73 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __ATOMBIOS_ENCODER_H__ -+#define __ATOMBIOS_ENCODER_H__ -+ -+u8 -+amdgpu_atombios_encoder_get_backlight_level(struct amdgpu_encoder *amdgpu_encoder); -+void -+amdgpu_atombios_encoder_set_backlight_level(struct amdgpu_encoder *amdgpu_encoder, -+ u8 level); -+void amdgpu_atombios_encoder_init_backlight(struct amdgpu_encoder *amdgpu_encoder, -+ struct drm_connector *drm_connector); -+void -+amdgpu_atombios_encoder_fini_backlight(struct amdgpu_encoder *amdgpu_encoder); -+bool amdgpu_atombios_encoder_is_digital(struct drm_encoder *encoder); -+bool amdgpu_atombios_encoder_mode_fixup(struct drm_encoder *encoder, -+ const struct drm_display_mode *mode, -+ struct drm_display_mode *adjusted_mode); -+int amdgpu_atombios_encoder_get_encoder_mode(struct drm_encoder *encoder); -+void -+amdgpu_atombios_encoder_setup_dig_encoder(struct drm_encoder *encoder, -+ int action, int panel_mode); -+void -+amdgpu_atombios_encoder_setup_dig_transmitter(struct drm_encoder *encoder, int action, -+ uint8_t lane_num, uint8_t lane_set); -+bool -+amdgpu_atombios_encoder_set_edp_panel_power(struct drm_connector *connector, -+ int action); -+void -+amdgpu_atombios_encoder_dpms(struct drm_encoder *encoder, int mode); -+void -+amdgpu_atombios_encoder_set_crtc_source(struct drm_encoder *encoder); -+void -+amdgpu_atombios_encoder_init_dig(struct amdgpu_device *adev); -+enum drm_connector_status -+amdgpu_atombios_encoder_dac_detect(struct drm_encoder *encoder, -+ struct drm_connector *connector); -+enum drm_connector_status -+amdgpu_atombios_encoder_dig_detect(struct drm_encoder *encoder, -+ struct drm_connector *connector); -+void -+amdgpu_atombios_encoder_setup_ext_encoder_ddc(struct drm_encoder *encoder); -+void -+amdgpu_atombios_encoder_set_bios_scratch_regs(struct drm_connector *connector, -+ struct drm_encoder *encoder, -+ bool connected); -+struct amdgpu_encoder_atom_dig * -+amdgpu_atombios_encoder_get_lcd_info(struct amdgpu_encoder *encoder); -+struct amdgpu_encoder_atom_dig * -+amdgpu_atombios_encoder_get_dig_info(struct amdgpu_encoder *amdgpu_encoder); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c -new file mode 100644 -index 0000000..13cdb01 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c -@@ -0,0 +1,158 @@ -+/* -+ * Copyright 2011 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: Alex Deucher -+ * -+ */ -+#include <drm/drmP.h> -+#include <drm/amdgpu_drm.h> -+#include "amdgpu.h" -+#include "atom.h" -+#include "amdgpu_atombios.h" -+ -+#define TARGET_HW_I2C_CLOCK 50 -+ -+/* these are a limitation of ProcessI2cChannelTransaction not the hw */ -+#define ATOM_MAX_HW_I2C_WRITE 3 -+#define ATOM_MAX_HW_I2C_READ 255 -+ -+static int amdgpu_atombios_i2c_process_i2c_ch(struct amdgpu_i2c_chan *chan, -+ u8 slave_addr, u8 flags, -+ u8 *buf, u8 num) -+{ -+ struct drm_device *dev = chan->dev; -+ struct amdgpu_device *adev = dev->dev_private; -+ PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args; -+ int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); -+ unsigned char *base; -+ u16 out = cpu_to_le16(0); -+ int r = 0; -+ -+ memset(&args, 0, sizeof(args)); -+ -+ mutex_lock(&chan->mutex); -+ -+ base = (unsigned char *)adev->mode_info.atom_context->scratch; -+ -+ if (flags & HW_I2C_WRITE) { -+ if (num > ATOM_MAX_HW_I2C_WRITE) { -+ DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num); -+ r = -EINVAL; -+ goto done; -+ } -+ if (buf == NULL) -+ args.ucRegIndex = 0; -+ else -+ args.ucRegIndex = buf[0]; -+ if (num) -+ num--; -+ if (num) -+ memcpy(&out, &buf[1], num); -+ args.lpI2CDataOut = cpu_to_le16(out); -+ } else { -+ if (num > ATOM_MAX_HW_I2C_READ) { -+ DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num); -+ r = -EINVAL; -+ goto done; -+ } -+ args.ucRegIndex = 0; -+ args.lpI2CDataOut = 0; -+ } -+ -+ args.ucFlag = flags; -+ args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; -+ args.ucTransBytes = num; -+ args.ucSlaveAddr = slave_addr << 1; -+ args.ucLineNumber = chan->rec.i2c_id; -+ -+ amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); -+ -+ /* error */ -+ if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) { -+ DRM_DEBUG_KMS("hw_i2c error\n"); -+ r = -EIO; -+ goto done; -+ } -+ -+ if (!(flags & HW_I2C_WRITE)) -+ amdgpu_atombios_copy_swap(buf, base, num, false); -+ -+done: -+ mutex_unlock(&chan->mutex); -+ -+ return r; -+} -+ -+int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap, -+ struct i2c_msg *msgs, int num) -+{ -+ struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap); -+ struct i2c_msg *p; -+ int i, remaining, current_count, buffer_offset, max_bytes, ret; -+ u8 flags; -+ -+ /* check for bus probe */ -+ p = &msgs[0]; -+ if ((num == 1) && (p->len == 0)) { -+ ret = amdgpu_atombios_i2c_process_i2c_ch(i2c, -+ p->addr, HW_I2C_WRITE, -+ NULL, 0); -+ if (ret) -+ return ret; -+ else -+ return num; -+ } -+ -+ for (i = 0; i < num; i++) { -+ p = &msgs[i]; -+ remaining = p->len; -+ buffer_offset = 0; -+ /* max_bytes are a limitation of ProcessI2cChannelTransaction not the hw */ -+ if (p->flags & I2C_M_RD) { -+ max_bytes = ATOM_MAX_HW_I2C_READ; -+ flags = HW_I2C_READ; -+ } else { -+ max_bytes = ATOM_MAX_HW_I2C_WRITE; -+ flags = HW_I2C_WRITE; -+ } -+ while (remaining) { -+ if (remaining > max_bytes) -+ current_count = max_bytes; -+ else -+ current_count = remaining; -+ ret = amdgpu_atombios_i2c_process_i2c_ch(i2c, -+ p->addr, flags, -+ &p->buf[buffer_offset], current_count); -+ if (ret) -+ return ret; -+ remaining -= current_count; -+ buffer_offset += current_count; -+ } -+ } -+ -+ return num; -+} -+ -+u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap) -+{ -+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -+} -+ -diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h -new file mode 100644 -index 0000000..d6128d9d ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h -@@ -0,0 +1,31 @@ -+/* -+ * Copyright 2014 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ */ -+ -+#ifndef __ATOMBIOS_I2C_H__ -+#define __ATOMBIOS_I2C_H__ -+ -+int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap, -+ struct i2c_msg *msgs, int num); -+u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap); -+ -+#endif -diff --git a/drivers/gpu/drm/amd/amdgpu/cikd.h b/drivers/gpu/drm/amd/amdgpu/cikd.h -new file mode 100644 -index 0000000..11828e2 ---- /dev/null -+++ b/drivers/gpu/drm/amd/amdgpu/cikd.h -@@ -0,0 +1,550 @@ -+/* -+ * Copyright 2012 Advanced Micro Devices, Inc. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR -+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -+ * OTHER DEALINGS IN THE SOFTWARE. -+ * -+ * Authors: Alex Deucher -+ */ -+#ifndef CIK_H -+#define CIK_H -+ -+#define MC_SEQ_MISC0__GDDR5__SHIFT 0x1c -+#define MC_SEQ_MISC0__GDDR5_MASK 0xf0000000 -+#define MC_SEQ_MISC0__GDDR5_VALUE 5 -+ -+#define CP_ME_TABLE_SIZE 96 -+ -+/* display controller offsets used for crtc/cur/lut/grph/viewport/etc. */ -+#define CRTC0_REGISTER_OFFSET (0x1b7c - 0x1b7c) -+#define CRTC1_REGISTER_OFFSET (0x1e7c - 0x1b7c) -+#define CRTC2_REGISTER_OFFSET (0x417c - 0x1b7c) -+#define CRTC3_REGISTER_OFFSET (0x447c - 0x1b7c) -+#define CRTC4_REGISTER_OFFSET (0x477c - 0x1b7c) -+#define CRTC5_REGISTER_OFFSET (0x4a7c - 0x1b7c) -+ -+#define BONAIRE_GB_ADDR_CONFIG_GOLDEN 0x12010001 -+#define HAWAII_GB_ADDR_CONFIG_GOLDEN 0x12011003 -+ -+#define CIK_RB_BITMAP_WIDTH_PER_SH 2 -+#define HAWAII_RB_BITMAP_WIDTH_PER_SH 4 -+ -+#define AMDGPU_NUM_OF_VMIDS 8 -+ -+#define PIPEID(x) ((x) << 0) -+#define MEID(x) ((x) << 2) -+#define VMID(x) ((x) << 4) -+#define QUEUEID(x) ((x) << 8) -+ -+#define mmCC_DRM_ID_STRAPS 0x1559 -+#define CC_DRM_ID_STRAPS__ATI_REV_ID_MASK 0xf0000000 -+ -+#define mmCHUB_CONTROL 0x619 -+#define BYPASS_VM (1 << 0) -+ -+#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) -+ -+#define mmGRPH_LUT_10BIT_BYPASS_CONTROL 0x1a02 -+#define LUT_10BIT_BYPASS_EN (1 << 8) -+ -+# define CURSOR_MONO 0 -+# define CURSOR_24_1 1 -+# define CURSOR_24_8_PRE_MULT 2 -+# define CURSOR_24_8_UNPRE_MULT 3 -+# define CURSOR_URGENT_ALWAYS 0 -+# define CURSOR_URGENT_1_8 1 -+# define CURSOR_URGENT_1_4 2 -+# define CURSOR_URGENT_3_8 3 -+# define CURSOR_URGENT_1_2 4 -+ -+# define GRPH_DEPTH_8BPP 0 -+# define GRPH_DEPTH_16BPP 1 -+# define GRPH_DEPTH_32BPP 2 -+/* 8 BPP */ -+# define GRPH_FORMAT_INDEXED 0 -+/* 16 BPP */ -+# define GRPH_FORMAT_ARGB1555 0 -+# define GRPH_FORMAT_ARGB565 1 -+# define GRPH_FORMAT_ARGB4444 2 -+# define GRPH_FORMAT_AI88 3 -+# define GRPH_FORMAT_MONO16 4 -+# define GRPH_FORMAT_BGRA5551 5 -+/* 32 BPP */ -+# define GRPH_FORMAT_ARGB8888 0 -+# define GRPH_FORMAT_ARGB2101010 1 -+# define GRPH_FORMAT_32BPP_DIG 2 -+# define GRPH_FORMAT_8B_ARGB2101010 3 -+# define GRPH_FORMAT_BGRA1010102 4 -+# define GRPH_FORMAT_8B_BGRA1010102 5 -+# define GRPH_FORMAT_RGB111110 6 -+# define GRPH_FORMAT_BGR101111 7 -+# define ADDR_SURF_MACRO_TILE_ASPECT_1 0 -+# define ADDR_SURF_MACRO_TILE_ASPECT_2 1 -+# define ADDR_SURF_MACRO_TILE_ASPECT_4 2 -+# define ADDR_SURF_MACRO_TILE_ASPECT_8 3 -+# define GRPH_ARRAY_LINEAR_GENERAL 0 -+# define GRPH_ARRAY_LINEAR_ALIGNED 1 -+# define GRPH_ARRAY_1D_TILED_THIN1 2 -+# define GRPH_ARRAY_2D_TILED_THIN1 4 -+# define DISPLAY_MICRO_TILING 0 -+# define THIN_MICRO_TILING 1 -+# define DEPTH_MICRO_TILING 2 -+# define ROTATED_MICRO_TILING 4 -+# define GRPH_ENDIAN_NONE 0 -+# define GRPH_ENDIAN_8IN16 1 -+# define GRPH_ENDIAN_8IN32 2 -+# define GRPH_ENDIAN_8IN64 3 -+# define GRPH_RED_SEL_R 0 -+# define GRPH_RED_SEL_G 1 -+# define GRPH_RED_SEL_B 2 -+# define GRPH_RED_SEL_A 3 -+# define GRPH_GREEN_SEL_G 0 -+# define GRPH_GREEN_SEL_B 1 -+# define GRPH_GREEN_SEL_A 2 -+# define GRPH_GREEN_SEL_R 3 -+# define GRPH_BLUE_SEL_B 0 -+# define GRPH_BLUE_SEL_A 1 -+# define GRPH_BLUE_SEL_R 2 -+# define GRPH_BLUE_SEL_G 3 -+# define GRPH_ALPHA_SEL_A 0 -+# define GRPH_ALPHA_SEL_R 1 -+# define GRPH_ALPHA_SEL_G 2 -+# define GRPH_ALPHA_SEL_B 3 -+# define INPUT_GAMMA_USE_LUT 0 -+# define INPUT_GAMMA_BYPASS 1 -+# define INPUT_GAMMA_SRGB_24 2 -+# define INPUT_GAMMA_XVYCC_222 3 -+ -+# define INPUT_CSC_BYPASS 0 -+# define INPUT_CSC_PROG_COEFF 1 -+# define INPUT_CSC_PROG_SHARED_MATRIXA 2 -+ -+# define OUTPUT_CSC_BYPASS 0 -+# define OUTPUT_CSC_TV_RGB 1 -+# define OUTPUT_CSC_YCBCR_601 2 -+# define OUTPUT_CSC_YCBCR_709 3 -+# define OUTPUT_CSC_PROG_COEFF 4 -+# define OUTPUT_CSC_PROG_SHARED_MATRIXB 5 -+ -+# define DEGAMMA_BYPASS 0 -+# define DEGAMMA_SRGB_24 1 -+# define DEGAMMA_XVYCC_222 2 -+# define GAMUT_REMAP_BYPASS 0 -+# define GAMUT_REMAP_PROG_COEFF 1 -+# define GAMUT_REMAP_PROG_SHARED_MATRIXA 2 -+# define GAMUT_REMAP_PROG_SHARED_MATRIXB 3 -+ -+# define REGAMMA_BYPASS 0 -+# define REGAMMA_SRGB_24 1 -+# define REGAMMA_XVYCC_222 2 -+# define REGAMMA_PROG_A 3 -+# define REGAMMA_PROG_B 4 -+ -+# define FMT_CLAMP_6BPC 0 -+# define FMT_CLAMP_8BPC 1 -+# define FMT_CLAMP_10BPC 2 -+ -+# define HDMI_24BIT_DEEP_COLOR 0 -+# define HDMI_30BIT_DEEP_COLOR 1 -+# define HDMI_36BIT_DEEP_COLOR 2 -+# define HDMI_ACR_HW 0 -+# define HDMI_ACR_32 1 -+# define HDMI_ACR_44 2 -+# define HDMI_ACR_48 3 -+# define HDMI_ACR_X1 1 -+# define HDMI_ACR_X2 2 -+# define HDMI_ACR_X4 4 -+# define AFMT_AVI_INFO_Y_RGB 0 -+# define AFMT_AVI_INFO_Y_YCBCR422 1 -+# define AFMT_AVI_INFO_Y_YCBCR444 2 -+ -+#define NO_AUTO 0 -+#define ES_AUTO 1 -+#define GS_AUTO 2 -+#define ES_AND_GS_AUTO 3 -+ -+# define ARRAY_MODE(x) ((x) << 2) -+# define PIPE_CONFIG(x) ((x) << 6) -+# define TILE_SPLIT(x) ((x) << 11) -+# define MICRO_TILE_MODE_NEW(x) ((x) << 22) -+# define SAMPLE_SPLIT(x) ((x) << 25) -+# define BANK_WIDTH(x) ((x) << 0) -+# define BANK_HEIGHT(x) ((x) << 2) -+# define MACRO_TILE_ASPECT(x) ((x) << 4) -+# define NUM_BANKS(x) ((x) << 6) -+ -+#define MSG_ENTER_RLC_SAFE_MODE 1 -+#define MSG_EXIT_RLC_SAFE_MODE 0 -+ -+/* -+ * PM4 -+ */ -+#define PACKET_TYPE0 0 -+#define PACKET_TYPE1 1 -+#define PACKET_TYPE2 2 -+#define PACKET_TYPE3 3 -+ -+#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) -+#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) -+#define CP_PACKET0_GET_REG(h) ((h) & 0xFFFF) -+#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) -+#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ -+ ((reg) & 0xFFFF) | \ -+ ((n) & 0x3FFF) << 16) -+#define CP_PACKET2 0x80000000 -+#define PACKET2_PAD_SHIFT 0 -+#define PACKET2_PAD_MASK (0x3fffffff << 0) -+ -+#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) -+ -+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ -+ (((op) & 0xFF) << 8) | \ -+ ((n) & 0x3FFF) << 16) -+ -+#define PACKET3_COMPUTE(op, n) (PACKET3(op, n) | 1 << 1) -+ -+/* Packet 3 types */ -+#define PACKET3_NOP 0x10 -+#define PACKET3_SET_BASE 0x11 -+#define PACKET3_BASE_INDEX(x) ((x) << 0) -+#define CE_PARTITION_BASE 3 -+#define PACKET3_CLEAR_STATE 0x12 -+#define PACKET3_INDEX_BUFFER_SIZE 0x13 -+#define PACKET3_DISPATCH_DIRECT 0x15 -+#define PACKET3_DISPATCH_INDIRECT 0x16 -+#define PACKET3_ATOMIC_GDS 0x1D -+#define PACKET3_ATOMIC_MEM 0x1E -+#define PACKET3_OCCLUSION_QUERY 0x1F -+#define PACKET3_SET_PREDICATION 0x20 -+#define PACKET3_REG_RMW 0x21 -+#define PACKET3_COND_EXEC 0x22 -+#define PACKET3_PRED_EXEC 0x23 -+#define PACKET3_DRAW_INDIRECT 0x24 -+#define PACKET3_DRAW_INDEX_INDIRECT 0x25 -+#define PACKET3_INDEX_BASE 0x26 -+#define PACKET3_DRAW_INDEX_2 0x27 -+#define PACKET3_CONTEXT_CONTROL 0x28 -+#define PACKET3_INDEX_TYPE 0x2A -+#define PACKET3_DRAW_INDIRECT_MULTI 0x2C -+#define PACKET3_DRAW_INDEX_AUTO 0x2D -+#define PACKET3_NUM_INSTANCES 0x2F -+#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30 -+#define PACKET3_INDIRECT_BUFFER_CONST 0x33 -+#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34 -+#define PACKET3_DRAW_INDEX_OFFSET_2 0x35 -+#define PACKET3_DRAW_PREAMBLE 0x36 -+#define PACKET3_WRITE_DATA 0x37 -+#define WRITE_DATA_DST_SEL(x) ((x) << 8) -+ /* 0 - register -+ * 1 - memory (sync - via GRBM) -+ * 2 - gl2 -+ * 3 - gds -+ * 4 - reserved -+ * 5 - memory (async - direct) -+ */ -+#define WR_ONE_ADDR (1 << 16) -+#define WR_CONFIRM (1 << 20) -+#define WRITE_DATA_CACHE_POLICY(x) ((x) << 25) -+ /* 0 - LRU -+ * 1 - Stream -+ */ -+#define WRITE_DATA_ENGINE_SEL(x) ((x) << 30) -+ /* 0 - me -+ * 1 - pfp -+ * 2 - ce -+ */ -+#define PACKET3_DRAW_INDEX_INDIRECT_MULTI 0x38 -+#define PACKET3_MEM_SEMAPHORE 0x39 -+# define PACKET3_SEM_USE_MAILBOX (0x1 << 16) -+# define PACKET3_SEM_SEL_SIGNAL_TYPE (0x1 << 20) /* 0 = increment, 1 = write 1 */ -+# define PACKET3_SEM_CLIENT_CODE ((x) << 24) /* 0 = CP, 1 = CB, 2 = DB */ -+# define PACKET3_SEM_SEL_SIGNAL (0x6 << 29) -+# define PACKET3_SEM_SEL_WAIT (0x7 << 29) -+#define PACKET3_COPY_DW 0x3B -+#define PACKET3_WAIT_REG_MEM 0x3C -+#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0) -+ /* 0 - always -+ * 1 - < -+ * 2 - <= -+ * 3 - == -+ * 4 - != -+ * 5 - >= -+ * 6 - > -+ */ -+#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) -+ /* 0 - reg -+ * 1 - mem -+ */ -+#define WAIT_REG_MEM_OPERATION(x) ((x) << 6) -+ /* 0 - wait_reg_mem -+ * 1 - wr_wait_wr_reg -+ */ -+#define WAIT_REG_MEM_ENGINE(x) ((x) << 8) -+ /* 0 - me -+ * 1 - pfp -+ */ -+#define PACKET3_INDIRECT_BUFFER 0x3F -+#define INDIRECT_BUFFER_TCL2_VOLATILE (1 << 22) -+#define INDIRECT_BUFFER_VALID (1 << 23) -+#define INDIRECT_BUFFER_CACHE_POLICY(x) ((x) << 28) -+ /* 0 - LRU -+ * 1 - Stream -+ * 2 - Bypass -+ */ -+#define PACKET3_COPY_DATA 0x40 -+#define PACKET3_PFP_SYNC_ME 0x42 -+#define PACKET3_SURFACE_SYNC 0x43 -+# define PACKET3_DEST_BASE_0_ENA (1 << 0) -+# define PACKET3_DEST_BASE_1_ENA (1 << 1) -+# define PACKET3_CB0_DEST_BASE_ENA (1 << 6) -+# define PACKET3_CB1_DEST_BASE_ENA (1 << 7) -+# define PACKET3_CB2_DEST_BASE_ENA (1 << 8) -+# define PACKET3_CB3_DEST_BASE_ENA (1 << 9) -+# define PACKET3_CB4_DEST_BASE_ENA (1 << 10) -+# define PACKET3_CB5_DEST_BASE_ENA (1 << 11) -+# define PACKET3_CB6_DEST_BASE_ENA (1 << 12) -+# define PACKET3_CB7_DEST_BASE_ENA (1 << 13) -+# define PACKET3_DB_DEST_BASE_ENA (1 << 14) -+# define PACKET3_TCL1_VOL_ACTION_ENA (1 << 15) -+# define PACKET3_TC_VOL_ACTION_ENA (1 << 16) /* L2 */ -+# define PACKET3_TC_WB_ACTION_ENA (1 << 18) /* L2 */ -+# define PACKET3_DEST_BASE_2_ENA (1 << 19) -+# define PACKET3_DEST_BASE_3_ENA (1 << 21) -+# define PACKET3_TCL1_ACTION_ENA (1 << 22) -+# define PACKET3_TC_ACTION_ENA (1 << 23) /* L2 */ -+# define PACKET3_CB_ACTION_ENA (1 << 25) -+# define PACKET3_DB_ACTION_ENA (1 << 26) -+# define PACKET3_SH_KCACHE_ACTION_ENA (1 << 27) -+# define PACKET3_SH_KCACHE_VOL_ACTION_ENA (1 << 28) -+# define PACKET3_SH_ICACHE_ACTION_ENA (1 << 29) -+#define PACKET3_COND_WRITE 0x45 -+#define PACKET3_EVENT_WRITE 0x46 -+#define EVENT_TYPE(x) ((x) << 0) -+#define EVENT_INDEX(x) ((x) << 8) -+ /* 0 - any non-TS event -+ * 1 - ZPASS_DONE, PIXEL_PIPE_STAT_* -+ * 2 - SAMPLE_PIPELINESTAT -+ * 3 - SAMPLE_STREAMOUTSTAT* -+ * 4 - *S_PARTIAL_FLUSH -+ * 5 - EOP events -+ * 6 - EOS events -+ */ -+#define PACKET3_EVENT_WRITE_EOP 0x47 -+#define EOP_TCL1_VOL_ACTION_EN (1 << 12) -+#define EOP_TC_VOL_ACTION_EN (1 << 13) /* L2 */ -+#define EOP_TC_WB_ACTION_EN (1 << 15) /* L2 */ -+#define EOP_TCL1_ACTION_EN (1 << 16) -+#define EOP_TC_ACTION_EN (1 << 17) /* L2 */ -+#define EOP_TCL2_VOLATILE (1 << 24) -+#define EOP_CACHE_POLICY(x) ((x) << 25) -+ /* 0 - LRU -+ * 1 - Stream -+ * 2 - Bypass -+ */ -+#define DATA_SEL(x) ((x) << 29) -+ /* 0 - discard -+ * 1 - send low 32bit data -+ * 2 - send 64bit data -+ * 3 - send 64bit GPU counter value -+ * 4 - send 64bit sys counter value -+ */ -+#define INT_SEL(x) ((x) << 24) -+ /* 0 - none -+ * 1 - interrupt only (DATA_SEL = 0) -+ * 2 - interrupt when data write is confirmed -+ */ -+#define DST_SEL(x) ((x) << 16) -+ /* 0 - MC -+ * 1 - TC/L2 -+ */ -+#define PACKET3_EVENT_WRITE_EOS 0x48 -+#define PACKET3_RELEASE_MEM 0x49 -+#define PACKET3_PREAMBLE_CNTL 0x4A -+# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28) -+# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28) -+#define PACKET3_DMA_DATA 0x50 -+/* 1. header -+ * 2. CONTROL -+ * 3. SRC_ADDR_LO or DATA [31:0] -+ * 4. SRC_ADDR_HI [31:0] -+ * 5. DST_ADDR_LO [31:0] -+ * 6. DST_ADDR_HI [7:0] -+ * 7. COMMAND [30:21] | BYTE_COUNT [20:0] -+ */ -+/* CONTROL */ -+# define PACKET3_DMA_DATA_ENGINE(x) ((x) << 0) -+ /* 0 - ME -+ * 1 - PFP -+ */ -+# define PACKET3_DMA_DATA_SRC_CACHE_POLICY(x) ((x) << 13) -+ /* 0 - LRU -+ * 1 - Stream -+ * 2 - Bypass -+ */ -+# define PACKET3_DMA_DATA_SRC_VOLATILE (1 << 15) -+# define PACKET3_DMA_DATA_DST_SEL(x) ((x) << 20) -+ /* 0 - DST_ADDR using DAS -+ * 1 - GDS -+ * 3 - DST_ADDR using L2 -+ */ -+# define PACKET3_DMA_DATA_DST_CACHE_POLICY(x) ((x) << 25) -+ /* 0 - LRU -+ * 1 - Stream -+ * 2 - Bypass -+ */ -+# define PACKET3_DMA_DATA_DST_VOLATILE (1 << 27) -+# define PACKET3_DMA_DATA_SRC_SEL(x) ((x) << 29) -+ /* 0 - SRC_ADDR using SAS -+ * 1 - GDS -+ * 2 - DATA -+ * 3 - SRC_ADDR using L2 -+ */ -+# define PACKET3_DMA_DATA_CP_SYNC (1 << 31) -+/* COMMAND */ -+# define PACKET3_DMA_DATA_DIS_WC (1 << 21) -+# define PACKET3_DMA_DATA_CMD_SRC_SWAP(x) ((x) << 22) -+ /* 0 - none -+ * 1 - 8 in 16 -+ * 2 - 8 in 32 -+ * 3 - 8 in 64 -+ */ -+# define PACKET3_DMA_DATA_CMD_DST_SWAP(x) ((x) << 24) -+ /* 0 - none -+ * 1 - 8 in 16 -+ * 2 - 8 in 32 -+ * 3 - 8 in 64 -+ */ -+# define PACKET3_DMA_DATA_CMD_SAS (1 << 26) -+ /* 0 - memory -+ * 1 - register -+ */ -+# define PACKET3_DMA_DATA_CMD_DAS (1 << 27) -+ /* 0 - memory -+ * 1 - register -+ */ -+# define PACKET3_DMA_DATA_CMD_SAIC (1 << 28) -+# define PACKET3_DMA_DATA_CMD_DAIC (1 << 29) -+# define PACKET3_DMA_DATA_CMD_RAW_WAIT (1 << 30) -+#define PACKET3_AQUIRE_MEM 0x58 -+#define PACKET3_REWIND 0x59 -+#define PACKET3_LOAD_UCONFIG_REG 0x5E -+#define PACKET3_LOAD_SH_REG 0x5F -+#define PACKET3_LOAD_CONFIG_REG 0x60 -+#define PACKET3_LOAD_CONTEXT_REG 0x61 -+#define PACKET3_SET_CONFIG_REG 0x68 -+#define PACKET3_SET_CONFIG_REG_START 0x00002000 -+#define PACKET3_SET_CONFIG_REG_END 0x00002c00 -+#define PACKET3_SET_CONTEXT_REG 0x69 -+#define PACKET3_SET_CONTEXT_REG_START 0x0000a000 -+#define PACKET3_SET_CONTEXT_REG_END 0x0000a400 -+#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73 -+#define PACKET3_SET_SH_REG 0x76 -+#define PACKET3_SET_SH_REG_START 0x00002c00 -+#define PACKET3_SET_SH_REG_END 0x00003000 -+#define PACKET3_SET_SH_REG_OFFSET 0x77 -+#define PACKET3_SET_QUEUE_REG 0x78 -+#define PACKET3_SET_UCONFIG_REG 0x79 -+#define PACKET3_SET_UCONFIG_REG_START 0x0000c000 -+#define PACKET3_SET_UCONFIG_REG_END 0x0000c400 -+#define PACKET3_SCRATCH_RAM_WRITE 0x7D -+#define PACKET3_SCRATCH_RAM_READ 0x7E -+#define PACKET3_LOAD_CONST_RAM 0x80 -+#define PACKET3_WRITE_CONST_RAM 0x81 -+#define PACKET3_DUMP_CONST_RAM 0x83 -+#define PACKET3_INCREMENT_CE_COUNTER 0x84 -+#define PACKET3_INCREMENT_DE_COUNTER 0x85 -+#define PACKET3_WAIT_ON_CE_COUNTER 0x86 -+#define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88 -+#define PACKET3_SWITCH_BUFFER 0x8B -+ -+/* SDMA - first instance at 0xd000, second at 0xd800 */ -+#define SDMA0_REGISTER_OFFSET 0x0 /* not a register */ -+#define SDMA1_REGISTER_OFFSET 0x200 /* not a register */ -+#define SDMA_MAX_INSTANCE 2 -+ -+#define SDMA_PACKET(op, sub_op, e) ((((e) & 0xFFFF) << 16) | \ -+ (((sub_op) & 0xFF) << 8) | \ -+ (((op) & 0xFF) << 0)) -+/* sDMA opcodes */ -+#define SDMA_OPCODE_NOP 0 -+#define SDMA_OPCODE_COPY 1 -+# define SDMA_COPY_SUB_OPCODE_LINEAR 0 -+# define SDMA_COPY_SUB_OPCODE_TILED 1 -+# define SDMA_COPY_SUB_OPCODE_SOA 3 -+# define SDMA_COPY_SUB_OPCODE_LINEAR_SUB_WINDOW 4 -+# define SDMA_COPY_SUB_OPCODE_TILED_SUB_WINDOW 5 -+# define SDMA_COPY_SUB_OPCODE_T2T_SUB_WINDOW 6 -+#define SDMA_OPCODE_WRITE 2 -+# define SDMA_WRITE_SUB_OPCODE_LINEAR 0 -+# define SDMA_WRTIE_SUB_OPCODE_TILED 1 -+#define SDMA_OPCODE_INDIRECT_BUFFER 4 -+#define SDMA_OPCODE_FENCE 5 -+#define SDMA_OPCODE_TRAP 6 -+#define SDMA_OPCODE_SEMAPHORE 7 -+# define SDMA_SEMAPHORE_EXTRA_O (1 << 13) -+ /* 0 - increment -+ * 1 - write 1 -+ */ -+# define SDMA_SEMAPHORE_EXTRA_S (1 << 14) -+ /* 0 - wait -+ * 1 - signal -+ */ -+# define SDMA_SEMAPHORE_EXTRA_M (1 << 15) -+ /* mailbox */ -+#define SDMA_OPCODE_POLL_REG_MEM 8 -+# define SDMA_POLL_REG_MEM_EXTRA_OP(x) ((x) << 10) -+ /* 0 - wait_reg_mem -+ * 1 - wr_wait_wr_reg -+ */ -+# define SDMA_POLL_REG_MEM_EXTRA_FUNC(x) ((x) << 12) -+ /* 0 - always -+ * 1 - < -+ * 2 - <= -+ * 3 - == -+ * 4 - != -+ * 5 - >= -+ * 6 - > -+ */ -+# define SDMA_POLL_REG_MEM_EXTRA_M (1 << 15) -+ /* 0 = register -+ * 1 = memory -+ */ -+#define SDMA_OPCODE_COND_EXEC 9 -+#define SDMA_OPCODE_CONSTANT_FILL 11 -+# define SDMA_CONSTANT_FILL_EXTRA_SIZE(x) ((x) << 14) -+ /* 0 = byte fill -+ * 2 = DW fill -+ */ -+#define SDMA_OPCODE_GENERATE_PTE_PDE 12 -+#define SDMA_OPCODE_TIMESTAMP 13 -+# define SDMA_TIMESTAMP_SUB_OPCODE_SET_LOCAL 0 -+# define SDMA_TIMESTAMP_SUB_OPCODE_GET_LOCAL 1 -+# define SDMA_TIMESTAMP_SUB_OPCODE_GET_GLOBAL 2 -+#define SDMA_OPCODE_SRBM_WRITE 14 -+# define SDMA_SRBM_WRITE_EXTRA_BYTE_ENABLE(x) ((x) << 12) -+ /* byte mask */ -+ -+#define VCE_CMD_NO_OP 0x00000000 -+#define VCE_CMD_END 0x00000001 -+#define VCE_CMD_IB 0x00000002 -+#define VCE_CMD_FENCE 0x00000003 -+#define VCE_CMD_TRAP 0x00000004 -+#define VCE_CMD_IB_AUTO 0x00000005 -+#define VCE_CMD_SEMAPHORE 0x00000006 -+ -+#endif --- -1.9.1 - |