2015-04-20 16:55:21 -04:00
/*
* 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>
2016-09-14 14:04:29 -04:00
# include <linux/pm_runtime.h>
2015-04-20 16:55:21 -04:00
# include <acpi/video.h>
2019-06-10 00:07:56 +02:00
2015-04-20 16:55:21 -04:00
# include <drm/drm_crtc_helper.h>
# include "amdgpu.h"
2017-07-30 13:42:55 +02:00
# include "amdgpu_pm.h"
2018-08-09 09:50:12 -05:00
# include "amdgpu_display.h"
2015-09-18 16:35:17 +08:00
# include "amd_acpi.h"
2015-04-20 16:55:21 -04:00
# include "atom.h"
2018-06-25 21:09:04 -04:00
struct amdgpu_atif_notification_cfg {
bool enabled ;
int command_code ;
} ;
struct amdgpu_atif_notifications {
bool thermal_state ;
bool forced_power_state ;
bool system_power_state ;
bool brightness_change ;
bool dgpu_display_event ;
2018-11-26 13:14:14 -05:00
bool gpu_package_power_limit ;
2018-06-25 21:09:04 -04:00
} ;
struct amdgpu_atif_functions {
bool system_params ;
bool sbios_requests ;
bool temperature_change ;
2018-11-26 13:14:14 -05:00
bool query_backlight_transfer_characteristics ;
bool ready_to_undock ;
bool external_gpu_information ;
2018-06-25 21:09:04 -04:00
} ;
struct amdgpu_atif {
2018-06-25 21:09:07 -04:00
acpi_handle handle ;
2018-06-25 21:09:04 -04:00
struct amdgpu_atif_notifications notifications ;
struct amdgpu_atif_functions functions ;
struct amdgpu_atif_notification_cfg notification_cfg ;
2020-05-05 15:44:57 -04:00
# if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
struct backlight_device * bd ;
# endif
2018-11-26 11:44:06 -05:00
struct amdgpu_dm_backlight_caps backlight_caps ;
2018-06-25 21:09:04 -04:00
} ;
2015-04-20 16:55:21 -04:00
/* 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 .
*/
2018-06-25 21:09:07 -04:00
static union acpi_object * amdgpu_atif_call ( struct amdgpu_atif * atif ,
int function ,
struct acpi_buffer * params )
2015-04-20 16:55:21 -04:00
{
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 ;
}
2018-06-25 21:09:07 -04:00
status = acpi_evaluate_object ( atif - > handle , NULL , & atif_arg ,
& buffer ) ;
2015-04-20 16:55:21 -04:00
/* 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 - > 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 - > brightness_change = mask & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST_SUPPORTED ;
n - > dgpu_display_event = mask & ATIF_DGPU_DISPLAY_EVENT_SUPPORTED ;
2018-11-26 13:14:14 -05:00
n - > gpu_package_power_limit = mask & ATIF_GPU_PACKAGE_POWER_LIMIT_REQUEST_SUPPORTED ;
2015-04-20 16:55:21 -04:00
}
/**
* 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 - > temperature_change = mask & ATIF_TEMPERATURE_CHANGE_NOTIFICATION_SUPPORTED ;
2018-11-26 13:14:14 -05:00
f - > query_backlight_transfer_characteristics =
mask & ATIF_QUERY_BACKLIGHT_TRANSFER_CHARACTERISTICS_SUPPORTED ;
f - > ready_to_undock = mask & ATIF_READY_TO_UNDOCK_NOTIFICATION_SUPPORTED ;
f - > external_gpu_information = mask & ATIF_GET_EXTERNAL_GPU_INFORMATION_SUPPORTED ;
2015-04-20 16:55:21 -04:00
}
/**
* 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 .
*/
2018-06-25 21:09:07 -04:00
static int amdgpu_atif_verify_interface ( struct amdgpu_atif * atif )
2015-04-20 16:55:21 -04:00
{
union acpi_object * info ;
struct atif_verify_interface output ;
size_t size ;
int err = 0 ;
2018-06-25 21:09:07 -04:00
info = amdgpu_atif_call ( atif , ATIF_FUNCTION_VERIFY_INTERFACE , NULL ) ;
2015-04-20 16:55:21 -04:00
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 ;
}
2018-06-25 21:09:07 -04:00
static acpi_handle amdgpu_atif_probe_handle ( acpi_handle dhandle )
{
acpi_handle handle = NULL ;
char acpi_method_name [ 255 ] = { 0 } ;
struct acpi_buffer buffer = { sizeof ( acpi_method_name ) , acpi_method_name } ;
acpi_status status ;
/* For PX/HG systems, ATIF and ATPX are in the iGPU's namespace, on dGPU only
* systems , ATIF is in the dGPU ' s namespace .
*/
status = acpi_get_handle ( dhandle , " ATIF " , & handle ) ;
if ( ACPI_SUCCESS ( status ) )
goto out ;
if ( amdgpu_has_atpx ( ) ) {
status = acpi_get_handle ( amdgpu_atpx_get_dhandle ( ) , " ATIF " ,
& handle ) ;
if ( ACPI_SUCCESS ( status ) )
goto out ;
}
DRM_DEBUG_DRIVER ( " No ATIF handle found \n " ) ;
return NULL ;
out :
acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
DRM_DEBUG_DRIVER ( " Found ATIF handle %s \n " , acpi_method_name ) ;
return handle ;
}
2015-04-20 16:55:21 -04:00
/**
* 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 .
*/
2018-06-25 21:09:07 -04:00
static int amdgpu_atif_get_notification_params ( struct amdgpu_atif * atif )
2015-04-20 16:55:21 -04:00
{
union acpi_object * info ;
2018-06-25 21:09:07 -04:00
struct amdgpu_atif_notification_cfg * n = & atif - > notification_cfg ;
2015-04-20 16:55:21 -04:00
struct atif_system_params params ;
size_t size ;
int err = 0 ;
2018-06-25 21:09:07 -04:00
info = amdgpu_atif_call ( atif , ATIF_FUNCTION_GET_SYSTEM_PARAMETERS ,
NULL ) ;
2015-04-20 16:55:21 -04:00
if ( ! info ) {
err = - EIO ;
goto out ;
}
size = * ( u16 * ) info - > buffer . pointer ;
if ( size < 10 ) {
err = - EINVAL ;
goto out ;
}
memset ( & params , 0 , sizeof ( params ) ) ;
size = min ( sizeof ( params ) , size ) ;
memcpy ( & params , 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 ;
}
2018-11-26 11:44:06 -05:00
/**
* amdgpu_atif_query_backlight_caps - get min and max backlight input signal
*
* @ handle : acpi handle
*
* Execute the QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS ATIF function
* to determine the acceptable range of backlight values
*
* Backlight_caps . caps_valid will be set to true if the query is successful
*
* The input signals are in range 0 - 255
*
* This function assumes the display with backlight is the first LCD
*
* Returns 0 on success , error on failure .
*/
static int amdgpu_atif_query_backlight_caps ( struct amdgpu_atif * atif )
{
union acpi_object * info ;
struct atif_qbtc_output characteristics ;
struct atif_qbtc_arguments arguments ;
struct acpi_buffer params ;
size_t size ;
int err = 0 ;
arguments . size = sizeof ( arguments ) ;
arguments . requested_display = ATIF_QBTC_REQUEST_LCD1 ;
params . length = sizeof ( arguments ) ;
params . pointer = ( void * ) & arguments ;
info = amdgpu_atif_call ( atif ,
ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS ,
& params ) ;
if ( ! info ) {
err = - EIO ;
goto out ;
}
size = * ( u16 * ) info - > buffer . pointer ;
if ( size < 10 ) {
err = - EINVAL ;
goto out ;
}
memset ( & characteristics , 0 , sizeof ( characteristics ) ) ;
size = min ( sizeof ( characteristics ) , size ) ;
memcpy ( & characteristics , info - > buffer . pointer , size ) ;
atif - > backlight_caps . caps_valid = true ;
atif - > backlight_caps . min_input_signal =
characteristics . min_input_signal ;
atif - > backlight_caps . max_input_signal =
characteristics . max_input_signal ;
out :
kfree ( info ) ;
return err ;
}
2015-04-20 16:55:21 -04:00
/**
* 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 .
*/
2018-06-25 21:09:07 -04:00
static int amdgpu_atif_get_sbios_requests ( struct amdgpu_atif * atif ,
struct atif_sbios_requests * req )
2015-04-20 16:55:21 -04:00
{
union acpi_object * info ;
size_t size ;
int count = 0 ;
2018-06-25 21:09:07 -04:00
info = amdgpu_atif_call ( atif , ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS ,
NULL ) ;
2015-04-20 16:55:21 -04:00
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 .
2018-09-21 20:43:44 -04:00
*
* Returns :
* NOTIFY_BAD or NOTIFY_DONE , depending on the event .
2015-04-20 16:55:21 -04:00
*/
2017-07-30 13:11:02 +02:00
static int amdgpu_atif_handler ( struct amdgpu_device * adev ,
2018-06-25 21:09:07 -04:00
struct acpi_bus_event * event )
2015-04-20 16:55:21 -04:00
{
2018-06-25 21:09:04 -04:00
struct amdgpu_atif * atif = adev - > atif ;
2015-04-20 16:55:21 -04:00
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 ;
2018-09-21 20:43:44 -04:00
/* Is this actually our event? */
2018-06-25 21:09:04 -04:00
if ( ! atif | |
! atif - > notification_cfg . enabled | |
2018-09-21 20:43:44 -04:00
event - > type ! = atif - > notification_cfg . command_code ) {
/* These events will generate keypresses otherwise */
if ( event - > type = = ACPI_VIDEO_NOTIFY_PROBE )
return NOTIFY_BAD ;
else
return NOTIFY_DONE ;
}
2015-04-20 16:55:21 -04:00
2018-07-19 09:17:02 -05:00
if ( atif - > functions . sbios_requests ) {
struct atif_sbios_requests req ;
2015-04-20 16:55:21 -04:00
2018-07-19 09:17:02 -05:00
/* Check pending SBIOS requests */
count = amdgpu_atif_get_sbios_requests ( atif , & req ) ;
if ( count < = 0 )
2018-09-21 20:43:44 -04:00
return NOTIFY_BAD ;
2015-04-20 16:55:21 -04:00
2018-07-19 09:17:02 -05:00
DRM_DEBUG_DRIVER ( " ATIF: %d pending SBIOS requests \n " , count ) ;
2015-04-20 16:55:21 -04:00
2020-05-05 15:44:57 -04:00
if ( req . pending & ATIF_PANEL_BRIGHTNESS_CHANGE_REQUEST ) {
2020-05-05 16:27:29 +03:00
# if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
2020-05-05 15:44:57 -04:00
if ( atif - > bd ) {
2020-05-05 16:27:29 +03:00
DRM_DEBUG_DRIVER ( " Changing brightness to %d \n " ,
req . backlight_level ) ;
/*
* XXX backlight_device_set_brightness ( ) is
* hardwired to post BACKLIGHT_UPDATE_SYSFS .
* It probably should accept ' reason ' parameter .
*/
2020-05-05 15:44:57 -04:00
backlight_device_set_brightness ( atif - > bd , req . backlight_level ) ;
2020-05-05 16:27:29 +03:00
}
# endif
2020-05-05 15:44:57 -04:00
}
2018-07-19 09:17:02 -05:00
if ( req . pending & ATIF_DGPU_DISPLAY_EVENT ) {
2019-04-30 09:47:25 +08:00
if ( adev - > flags & AMD_IS_PX ) {
2020-08-24 12:29:45 -04:00
pm_runtime_get_sync ( adev_to_drm ( adev ) - > dev ) ;
2018-07-19 09:17:02 -05:00
/* Just fire off a uevent and let userspace tell us what to do */
2020-08-24 12:29:45 -04:00
drm_helper_hpd_irq_event ( adev_to_drm ( adev ) ) ;
pm_runtime_mark_last_busy ( adev_to_drm ( adev ) - > dev ) ;
pm_runtime_put_autosuspend ( adev_to_drm ( adev ) - > dev ) ;
2018-07-19 09:17:02 -05:00
}
2016-09-14 14:04:29 -04:00
}
2018-07-19 09:17:02 -05:00
/* TODO: check other events */
2016-09-14 14:04:29 -04:00
}
2015-04-20 16:55:21 -04:00
/* 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 ;
2018-02-27 18:20:53 +08:00
if ( amdgpu_acpi_pcie_notify_device_ready ( adev ) )
return - EINVAL ;
2015-04-20 16:55:21 -04:00
/* 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 , & params ) ;
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 )
{
2018-06-25 21:09:07 -04:00
acpi_handle handle , atif_handle ;
2018-06-25 21:09:04 -04:00
struct amdgpu_atif * atif ;
2015-04-20 16:55:21 -04:00
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 ) ;
}
2018-06-25 21:09:07 -04:00
/* Probe for ATIF, and initialize it if found */
atif_handle = amdgpu_atif_probe_handle ( handle ) ;
if ( ! atif_handle )
goto out ;
2018-06-25 21:09:04 -04:00
atif = kzalloc ( sizeof ( * atif ) , GFP_KERNEL ) ;
if ( ! atif ) {
DRM_WARN ( " Not enough memory to initialize ATIF \n " ) ;
goto out ;
}
2018-06-25 21:09:07 -04:00
atif - > handle = atif_handle ;
2018-06-25 21:09:04 -04:00
2018-06-25 21:09:07 -04:00
/* Call the ATIF method */
ret = amdgpu_atif_verify_interface ( atif ) ;
2015-04-20 16:55:21 -04:00
if ( ret ) {
DRM_DEBUG_DRIVER ( " Call to ATIF verify_interface failed: %d \n " , ret ) ;
2018-06-25 21:09:04 -04:00
kfree ( atif ) ;
2015-04-20 16:55:21 -04:00
goto out ;
}
2018-06-25 21:09:04 -04:00
adev - > atif = atif ;
2015-04-20 16:55:21 -04:00
if ( atif - > notifications . brightness_change ) {
2020-05-05 15:44:57 -04:00
# if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
if ( amdgpu_device_has_dc_support ( adev ) ) {
# if defined(CONFIG_DRM_AMD_DC)
struct amdgpu_display_manager * dm = & adev - > dm ;
atif - > bd = dm - > backlight_dev ;
# endif
} else {
struct drm_encoder * tmp ;
/* Find the encoder controlling the brightness */
2020-08-24 12:29:45 -04:00
list_for_each_entry ( tmp , & adev_to_drm ( adev ) - > mode_config . encoder_list ,
2020-05-05 15:44:57 -04:00
head ) {
struct amdgpu_encoder * enc = to_amdgpu_encoder ( tmp ) ;
if ( ( enc - > devices & ( ATOM_DEVICE_LCD_SUPPORT ) ) & &
enc - > enc_priv ) {
struct amdgpu_encoder_atom_dig * dig = enc - > enc_priv ;
if ( dig - > bl_dev ) {
atif - > bd = dig - > bl_dev ;
break ;
}
2015-04-20 16:55:21 -04:00
}
}
}
}
2020-05-05 15:44:57 -04:00
# endif
2015-04-20 16:55:21 -04:00
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 ) {
2018-06-25 21:09:07 -04:00
ret = amdgpu_atif_get_notification_params ( atif ) ;
2015-04-20 16:55:21 -04:00
if ( ret ) {
DRM_DEBUG_DRIVER ( " Call to GET_SYSTEM_PARAMS failed: %d \n " ,
ret ) ;
/* Disable notification */
atif - > notification_cfg . enabled = false ;
}
}
2018-11-26 11:44:06 -05:00
if ( atif - > functions . query_backlight_transfer_characteristics ) {
ret = amdgpu_atif_query_backlight_caps ( atif ) ;
if ( ret ) {
DRM_DEBUG_DRIVER ( " Call to QUERY_BACKLIGHT_TRANSFER_CHARACTERISTICS failed: %d \n " ,
ret ) ;
atif - > backlight_caps . caps_valid = false ;
}
} else {
atif - > backlight_caps . caps_valid = false ;
}
2015-04-20 16:55:21 -04:00
out :
adev - > acpi_nb . notifier_call = amdgpu_acpi_event ;
register_acpi_notifier ( & adev - > acpi_nb ) ;
return ret ;
}
2018-11-26 11:44:06 -05:00
void amdgpu_acpi_get_backlight_caps ( struct amdgpu_device * adev ,
struct amdgpu_dm_backlight_caps * caps )
{
if ( ! adev - > atif ) {
caps - > caps_valid = false ;
return ;
}
caps - > caps_valid = adev - > atif - > backlight_caps . caps_valid ;
caps - > min_input_signal = adev - > atif - > backlight_caps . min_input_signal ;
caps - > max_input_signal = adev - > atif - > backlight_caps . max_input_signal ;
}
2015-04-20 16:55:21 -04:00
/**
* 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 ) ;
2018-12-05 15:43:19 +08:00
kfree ( adev - > atif ) ;
2015-04-20 16:55:21 -04:00
}