2015-04-20 16:55:21 -04:00
/*
* 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>
2016-06-01 12:54:33 -04:00
# include <linux/delay.h>
2015-04-20 16:55:21 -04:00
2015-09-18 16:35:17 +08:00
# include "amd_acpi.h"
2015-04-20 16:55:21 -04:00
2017-12-20 13:29:58 -05:00
# define AMDGPU_PX_QUIRK_FORCE_ATPX (1 << 0)
struct amdgpu_px_quirk {
u32 chip_vendor ;
u32 chip_device ;
u32 subsys_vendor ;
u32 subsys_device ;
u32 px_quirk_flags ;
} ;
2015-04-20 16:55:21 -04:00
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 ;
2018-06-25 21:09:05 -04:00
bool disp_detection_ports ;
2015-04-20 16:55:21 -04:00
} ;
struct amdgpu_atpx {
acpi_handle handle ;
struct amdgpu_atpx_functions functions ;
2016-06-02 09:04:01 -04:00
bool is_hybrid ;
2016-09-14 14:01:41 -04:00
bool dgpu_req_power_for_displays ;
2015-04-20 16:55:21 -04:00
} ;
static struct amdgpu_atpx_priv {
bool atpx_detected ;
2016-11-23 02:22:24 +01:00
bool bridge_pm_usable ;
2017-12-20 13:29:58 -05:00
unsigned int quirks ;
2015-04-20 16:55:21 -04:00
/* 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 ;
}
2016-06-01 13:08:21 -04:00
bool amdgpu_has_atpx_dgpu_power_cntl ( void ) {
return amdgpu_atpx_priv . atpx . functions . power_cntl ;
}
2016-06-02 09:04:01 -04:00
bool amdgpu_is_atpx_hybrid ( void ) {
return amdgpu_atpx_priv . atpx . is_hybrid ;
}
2016-09-14 14:01:41 -04:00
bool amdgpu_atpx_dgpu_req_power_for_displays ( void ) {
return amdgpu_atpx_priv . atpx . dgpu_req_power_for_displays ;
}
2015-04-20 16:55:21 -04:00
/**
* 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 ;
2018-06-25 21:09:05 -04:00
f - > disp_detection_ports = mask & ATPX_GET_DISPLAY_DETECTION_PORTS_SUPPORTED ;
2015-04-20 16:55:21 -04:00
}
/**
* 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 )
{
2016-06-01 12:42:35 -04:00
u32 valid_bits = 0 ;
2016-04-25 13:12:18 -04:00
2015-04-20 16:55:21 -04:00
if ( atpx - > functions . px_params ) {
union acpi_object * info ;
struct atpx_px_params output ;
size_t size ;
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 ;
2016-06-01 12:28:13 -04:00
2015-04-20 16:55:21 -04:00
kfree ( info ) ;
}
2016-06-01 12:42:35 -04:00
/* 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 ;
/* some bioses set these bits rather than flagging power_cntl as supported */
if ( valid_bits & ( ATPX_DYNAMIC_PX_SUPPORTED |
ATPX_DYNAMIC_DGPU_POWER_OFF_SUPPORTED ) )
atpx - > functions . power_cntl = true ;
2016-06-02 09:04:01 -04:00
atpx - > is_hybrid = false ;
2016-06-01 12:42:35 -04:00
if ( valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED ) {
2017-12-20 13:29:58 -05:00
if ( amdgpu_atpx_priv . quirks & AMDGPU_PX_QUIRK_FORCE_ATPX ) {
printk ( " ATPX Hybrid Graphics, forcing to ATPX \n " ) ;
atpx - > functions . power_cntl = true ;
atpx - > is_hybrid = false ;
} else {
printk ( " ATPX Hybrid Graphics \n " ) ;
/*
* Disable legacy PM methods only when pcie port PM is usable ,
* otherwise the device might fail to power off or power on .
*/
atpx - > functions . power_cntl = ! amdgpu_atpx_priv . bridge_pm_usable ;
atpx - > is_hybrid = true ;
}
2016-06-01 12:42:35 -04:00
}
2016-09-14 14:01:41 -04:00
atpx - > dgpu_req_power_for_displays = false ;
if ( valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS )
atpx - > dgpu_req_power_for_displays = true ;
2015-04-20 16:55:21 -04:00
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 ,
& params ) ;
if ( ! info )
return - EIO ;
kfree ( info ) ;
2016-06-01 12:54:33 -04:00
/* 200ms delay is required after off */
if ( state = = 0 )
msleep ( 200 ) ;
2015-04-20 16:55:21 -04:00
}
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 ,
& params ) ;
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 ,
& params ) ;
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 ,
& params ) ;
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 ,
& params ) ;
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 .
*/
2018-04-24 15:15:34 +02:00
static enum vga_switcheroo_client_id amdgpu_atpx_get_client_id ( struct pci_dev * pdev )
2015-04-20 16:55:21 -04:00
{
if ( amdgpu_atpx_priv . dhandle = = ACPI_HANDLE ( & pdev - > dev ) )
return VGA_SWITCHEROO_IGD ;
else
return VGA_SWITCHEROO_DIS ;
}
2015-10-18 13:05:40 +02:00
static const struct vga_switcheroo_handler amdgpu_atpx_handler = {
2015-04-20 16:55:21 -04:00
. switchto = amdgpu_atpx_switchto ,
. power_state = amdgpu_atpx_power_state ,
. get_client_id = amdgpu_atpx_get_client_id ,
} ;
2017-12-20 13:29:58 -05:00
static const struct amdgpu_px_quirk amdgpu_px_quirk_list [ ] = {
/* HG _PR3 doesn't seem to work on this A+A weston board */
{ 0x1002 , 0x6900 , 0x1002 , 0x0124 , AMDGPU_PX_QUIRK_FORCE_ATPX } ,
{ 0x1002 , 0x6900 , 0x1028 , 0x0812 , AMDGPU_PX_QUIRK_FORCE_ATPX } ,
2018-02-08 17:46:01 +08:00
{ 0x1002 , 0x6900 , 0x1028 , 0x0813 , AMDGPU_PX_QUIRK_FORCE_ATPX } ,
2017-12-20 13:29:58 -05:00
{ 0 , 0 , 0 , 0 , 0 } ,
} ;
static void amdgpu_atpx_get_quirks ( struct pci_dev * pdev )
{
const struct amdgpu_px_quirk * p = amdgpu_px_quirk_list ;
/* Apply PX quirks */
while ( p & & p - > chip_device ! = 0 ) {
if ( pdev - > vendor = = p - > chip_vendor & &
pdev - > device = = p - > chip_device & &
pdev - > subsystem_vendor = = p - > subsys_vendor & &
pdev - > subsystem_device = = p - > subsys_device ) {
amdgpu_atpx_priv . quirks | = p - > px_quirk_flags ;
break ;
}
+ + p ;
}
}
2015-04-20 16:55:21 -04:00
/**
* 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 ;
2016-11-26 15:05:01 +01:00
bool d3_supported = false ;
struct pci_dev * parent_pdev ;
2015-04-20 16:55:21 -04:00
while ( ( pdev = pci_get_class ( PCI_CLASS_DISPLAY_VGA < < 8 , pdev ) ) ! = NULL ) {
vga_count + + ;
has_atpx | = ( amdgpu_atpx_pci_probe_handle ( pdev ) = = true ) ;
2016-11-26 15:05:01 +01:00
parent_pdev = pci_upstream_bridge ( pdev ) ;
d3_supported | = parent_pdev & & parent_pdev - > bridge_d3 ;
2017-12-20 13:29:58 -05:00
amdgpu_atpx_get_quirks ( pdev ) ;
2015-04-20 16:55:21 -04:00
}
while ( ( pdev = pci_get_class ( PCI_CLASS_DISPLAY_OTHER < < 8 , pdev ) ) ! = NULL ) {
vga_count + + ;
has_atpx | = ( amdgpu_atpx_pci_probe_handle ( pdev ) = = true ) ;
2016-11-26 15:05:01 +01:00
parent_pdev = pci_upstream_bridge ( pdev ) ;
d3_supported | = parent_pdev & & parent_pdev - > bridge_d3 ;
2017-12-20 13:29:58 -05:00
amdgpu_atpx_get_quirks ( pdev ) ;
2015-04-20 16:55:21 -04:00
}
if ( has_atpx & & vga_count = = 2 ) {
acpi_get_name ( amdgpu_atpx_priv . atpx . handle , ACPI_FULL_PATHNAME , & buffer ) ;
2017-02-28 04:55:52 -08:00
pr_info ( " vga_switcheroo: detected switching method %s handle \n " ,
acpi_method_name ) ;
2015-04-20 16:55:21 -04:00
amdgpu_atpx_priv . atpx_detected = true ;
2016-11-26 15:05:01 +01:00
amdgpu_atpx_priv . bridge_pm_usable = d3_supported ;
2016-07-27 14:52:35 -04:00
amdgpu_atpx_init ( ) ;
2015-04-20 16:55:21 -04:00
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 ;
2016-01-11 20:09:20 +01:00
enum vga_switcheroo_handler_flags_t handler_flags = 0 ;
2015-04-20 16:55:21 -04:00
/* detect if we have any ATPX + 2 VGA in the system */
r = amdgpu_atpx_detect ( ) ;
if ( ! r )
return ;
2016-01-11 20:09:20 +01:00
vga_switcheroo_register_handler ( & amdgpu_atpx_handler , handler_flags ) ;
2015-04-20 16:55:21 -04:00
}
/**
* 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 ( ) ;
}