drm/amdgpu: add core driver (v4)
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>
This commit is contained in:
parent
97b2e202fb
commit
d38ceaf99e
78 changed files with 33596 additions and 0 deletions
|
|
@ -120,6 +120,27 @@ config DRM_RADEON
|
|||
|
||||
source "drivers/gpu/drm/radeon/Kconfig"
|
||||
|
||||
config DRM_AMDGPU
|
||||
tristate "AMD GPU"
|
||||
depends on DRM && PCI
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select FW_LOADER
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_FB_HELPER
|
||||
select DRM_TTM
|
||||
select POWER_SUPPLY
|
||||
select HWMON
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
select INTERVAL_TREE
|
||||
help
|
||||
Choose this option if you have a recent AMD Radeon graphics card.
|
||||
|
||||
If M is selected, the module will be called amdgpu.
|
||||
|
||||
source "drivers/gpu/drm/amd/amdgpu/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/nouveau/Kconfig"
|
||||
|
||||
config DRM_I810
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ obj-$(CONFIG_DRM_TDFX) += tdfx/
|
|||
obj-$(CONFIG_DRM_R128) += r128/
|
||||
obj-$(CONFIG_HSA_AMD) += amd/amdkfd/
|
||||
obj-$(CONFIG_DRM_RADEON)+= radeon/
|
||||
obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/
|
||||
obj-$(CONFIG_DRM_MGA) += mga/
|
||||
obj-$(CONFIG_DRM_I810) += i810/
|
||||
obj-$(CONFIG_DRM_I915) += i915/
|
||||
|
|
|
|||
17
drivers/gpu/drm/amd/amdgpu/Kconfig
Normal file
17
drivers/gpu/drm/amd/amdgpu/Kconfig
Normal file
|
|
@ -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.
|
||||
49
drivers/gpu/drm/amd/amdgpu/Makefile
Normal file
49
drivers/gpu/drm/amd/amdgpu/Makefile
Normal file
|
|
@ -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)
|
||||
768
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
Normal file
768
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
445
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h
Normal file
445
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.h
Normal file
|
|
@ -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
|
||||
105
drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c
Normal file
105
drivers/gpu/drm/amd/amdgpu/amdgpu_afmt.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
1598
drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c
Normal file
1598
drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c
Normal file
File diff suppressed because it is too large
Load diff
206
drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h
Normal file
206
drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h
Normal file
|
|
@ -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
|
||||
572
drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
Normal file
572
drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
Normal file
|
|
@ -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();
|
||||
}
|
||||
221
drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c
Normal file
221
drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c
Normal file
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
359
drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
Normal file
359
drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
268
drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
Normal file
268
drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
1907
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
Normal file
1907
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
Normal file
File diff suppressed because it is too large
Load diff
42
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h
Normal file
42
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.h
Normal file
|
|
@ -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
|
||||
825
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
Normal file
825
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
161
drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
Normal file
161
drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
1971
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
Normal file
1971
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
Normal file
File diff suppressed because it is too large
Load diff
832
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
Normal file
832
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
955
drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
Normal file
955
drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
Normal file
|
|
@ -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];
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue