mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-24 03:24:55 +08:00
8b48463f89
Replace direct inclusions of <acpi/acpi.h>, <acpi/acpi_bus.h> and <acpi/acpi_drivers.h>, which are incorrect, with <linux/acpi.h> inclusions and remove some inclusions of those files that aren't necessary. First of all, <acpi/acpi.h>, <acpi/acpi_bus.h> and <acpi/acpi_drivers.h> should not be included directly from any files that are built for CONFIG_ACPI unset, because that generally leads to build warnings about undefined symbols in !CONFIG_ACPI builds. For CONFIG_ACPI set, <linux/acpi.h> includes those files and for CONFIG_ACPI unset it provides stub ACPI symbols to be used in that case. Second, there are ordering dependencies between those files that always have to be met. Namely, it is required that <acpi/acpi_bus.h> be included prior to <acpi/acpi_drivers.h> so that the acpi_pci_root declarations the latter depends on are always there. And <acpi/acpi.h> which provides basic ACPICA type declarations should always be included prior to any other ACPI headers in CONFIG_ACPI builds. That also is taken care of including <linux/acpi.h> as appropriate. Signed-off-by: Lv Zheng <lv.zheng@intel.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Matthew Garrett <mjg59@srcf.ucam.org> Cc: Tony Luck <tony.luck@intel.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com> (drivers/pci stuff) Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> (Xen stuff) Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
791 lines
22 KiB
C
791 lines
22 KiB
C
/*
|
|
* 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 "radeon.h"
|
|
#include "radeon_acpi.h"
|
|
#include "atom.h"
|
|
|
|
#define ACPI_AC_CLASS "ac_adapter"
|
|
|
|
extern void radeon_pm_acpi_event_handler(struct radeon_device *rdev);
|
|
|
|
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
|
|
*/
|
|
/**
|
|
* radeon_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 *radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_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 radeon_atif_parse_notification(struct radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_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 radeon_atif_parse_functions(struct radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_atif_verify_interface - verify ATIF
|
|
*
|
|
* @handle: acpi handle
|
|
* @atif: radeon 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 radeon_atif_verify_interface(acpi_handle handle,
|
|
struct radeon_atif *atif)
|
|
{
|
|
union acpi_object *info;
|
|
struct atif_verify_interface output;
|
|
size_t size;
|
|
int err = 0;
|
|
|
|
info = radeon_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);
|
|
|
|
radeon_atif_parse_notification(&atif->notifications, output.notification_mask);
|
|
radeon_atif_parse_functions(&atif->functions, output.function_bits);
|
|
|
|
out:
|
|
kfree(info);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* radeon_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 radeon_atif_get_notification_params(acpi_handle handle,
|
|
struct radeon_atif_notification_cfg *n)
|
|
{
|
|
union acpi_object *info;
|
|
struct atif_system_params params;
|
|
size_t size;
|
|
int err = 0;
|
|
|
|
info = radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_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 radeon_atif_get_sbios_requests(acpi_handle handle,
|
|
struct atif_sbios_requests *req)
|
|
{
|
|
union acpi_object *info;
|
|
size_t size;
|
|
int count = 0;
|
|
|
|
info = radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_atif_handler - handle ATIF notify requests
|
|
*
|
|
* @rdev: radeon_device pointer
|
|
* @event: atif sbios request struct
|
|
*
|
|
* Checks the acpi event and if it matches an atif event,
|
|
* handles it.
|
|
* Returns NOTIFY code
|
|
*/
|
|
int radeon_atif_handler(struct radeon_device *rdev,
|
|
struct acpi_bus_event *event)
|
|
{
|
|
struct radeon_atif *atif = &rdev->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(&rdev->pdev->dev);
|
|
count = radeon_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 radeon_encoder *enc = atif->encoder_for_bl;
|
|
|
|
if (enc) {
|
|
DRM_DEBUG_DRIVER("Changing brightness to %d\n",
|
|
req.backlight_level);
|
|
|
|
radeon_set_backlight_level(rdev, enc, req.backlight_level);
|
|
|
|
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
|
|
if (rdev->is_atom_bios) {
|
|
struct radeon_encoder_atom_dig *dig = enc->enc_priv;
|
|
backlight_force_update(dig->bl_dev,
|
|
BACKLIGHT_UPDATE_HOTKEY);
|
|
} else {
|
|
struct radeon_encoder_lvds *dig = enc->enc_priv;
|
|
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
|
|
*/
|
|
/**
|
|
* radeon_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 *radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_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 radeon_atcs_parse_functions(struct radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_atcs_verify_interface - verify ATCS
|
|
*
|
|
* @handle: acpi handle
|
|
* @atcs: radeon 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 radeon_atcs_verify_interface(acpi_handle handle,
|
|
struct radeon_atcs *atcs)
|
|
{
|
|
union acpi_object *info;
|
|
struct atcs_verify_interface output;
|
|
size_t size;
|
|
int err = 0;
|
|
|
|
info = radeon_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);
|
|
|
|
radeon_atcs_parse_functions(&atcs->functions, output.function_bits);
|
|
|
|
out:
|
|
kfree(info);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* radeon_acpi_is_pcie_performance_request_supported
|
|
*
|
|
* @rdev: radeon_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 radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev)
|
|
{
|
|
struct radeon_atcs *atcs = &rdev->atcs;
|
|
|
|
if (atcs->functions.pcie_perf_req && atcs->functions.pcie_dev_rdy)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* radeon_acpi_pcie_notify_device_ready
|
|
*
|
|
* @rdev: radeon_device pointer
|
|
*
|
|
* Executes the PCIE_DEVICE_READY_NOTIFICATION method
|
|
* (all asics).
|
|
* returns 0 on success, error on failure.
|
|
*/
|
|
int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev)
|
|
{
|
|
acpi_handle handle;
|
|
union acpi_object *info;
|
|
struct radeon_atcs *atcs = &rdev->atcs;
|
|
|
|
/* Get the device handle */
|
|
handle = ACPI_HANDLE(&rdev->pdev->dev);
|
|
if (!handle)
|
|
return -EINVAL;
|
|
|
|
if (!atcs->functions.pcie_dev_rdy)
|
|
return -EINVAL;
|
|
|
|
info = radeon_atcs_call(handle, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION, NULL);
|
|
if (!info)
|
|
return -EIO;
|
|
|
|
kfree(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* radeon_acpi_pcie_performance_request
|
|
*
|
|
* @rdev: radeon_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 radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
|
|
u8 perf_req, bool advertise)
|
|
{
|
|
acpi_handle handle;
|
|
union acpi_object *info;
|
|
struct radeon_atcs *atcs = &rdev->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(&rdev->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 = rdev->pdev->devfn | (rdev->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 = radeon_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;
|
|
}
|
|
|
|
/**
|
|
* radeon_acpi_event - handle notify events
|
|
*
|
|
* @nb: notifier block
|
|
* @val: val
|
|
* @data: acpi event
|
|
*
|
|
* Calls relevant radeon functions in response to various
|
|
* acpi events.
|
|
* Returns NOTIFY code
|
|
*/
|
|
static int radeon_acpi_event(struct notifier_block *nb,
|
|
unsigned long val,
|
|
void *data)
|
|
{
|
|
struct radeon_device *rdev = container_of(nb, struct radeon_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");
|
|
|
|
radeon_pm_acpi_event_handler(rdev);
|
|
}
|
|
|
|
/* Check for pending SBIOS requests */
|
|
return radeon_atif_handler(rdev, entry);
|
|
}
|
|
|
|
/* Call all ACPI methods here */
|
|
/**
|
|
* radeon_acpi_init - init driver acpi support
|
|
*
|
|
* @rdev: radeon_device pointer
|
|
*
|
|
* Verifies the AMD ACPI interfaces and registers with the acpi
|
|
* notifier chain (all asics).
|
|
* Returns 0 on success, error on failure.
|
|
*/
|
|
int radeon_acpi_init(struct radeon_device *rdev)
|
|
{
|
|
acpi_handle handle;
|
|
struct radeon_atif *atif = &rdev->atif;
|
|
struct radeon_atcs *atcs = &rdev->atcs;
|
|
int ret;
|
|
|
|
/* Get the device handle */
|
|
handle = ACPI_HANDLE(&rdev->pdev->dev);
|
|
|
|
/* No need to proceed if we're sure that ATIF is not supported */
|
|
if (!ASIC_IS_AVIVO(rdev) || !rdev->bios || !handle)
|
|
return 0;
|
|
|
|
/* Call the ATCS method */
|
|
ret = radeon_atcs_verify_interface(handle, atcs);
|
|
if (ret) {
|
|
DRM_DEBUG_DRIVER("Call to ATCS verify_interface failed: %d\n", ret);
|
|
}
|
|
|
|
/* Call the ATIF method */
|
|
ret = radeon_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;
|
|
struct radeon_encoder *target = NULL;
|
|
|
|
/* Find the encoder controlling the brightness */
|
|
list_for_each_entry(tmp, &rdev->ddev->mode_config.encoder_list,
|
|
head) {
|
|
struct radeon_encoder *enc = to_radeon_encoder(tmp);
|
|
|
|
if ((enc->devices & (ATOM_DEVICE_LCD_SUPPORT)) &&
|
|
enc->enc_priv) {
|
|
if (rdev->is_atom_bios) {
|
|
struct radeon_encoder_atom_dig *dig = enc->enc_priv;
|
|
if (dig->bl_dev) {
|
|
target = enc;
|
|
break;
|
|
}
|
|
} else {
|
|
struct radeon_encoder_lvds *dig = enc->enc_priv;
|
|
if (dig->bl_dev) {
|
|
target = enc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
atif->encoder_for_bl = target;
|
|
if (!target) {
|
|
/* Brightness change notification is enabled, but we
|
|
* didn't find a backlight controller, this should
|
|
* never happen.
|
|
*/
|
|
DRM_ERROR("Cannot find a backlight controller\n");
|
|
}
|
|
}
|
|
|
|
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 = radeon_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:
|
|
rdev->acpi_nb.notifier_call = radeon_acpi_event;
|
|
register_acpi_notifier(&rdev->acpi_nb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* radeon_acpi_fini - tear down driver acpi support
|
|
*
|
|
* @rdev: radeon_device pointer
|
|
*
|
|
* Unregisters with the acpi notifier chain (all asics).
|
|
*/
|
|
void radeon_acpi_fini(struct radeon_device *rdev)
|
|
{
|
|
unregister_acpi_notifier(&rdev->acpi_nb);
|
|
}
|