@ -41,6 +41,8 @@ static const char acpi_optimus_dsm_muid[16] = {
0xA7 , 0x2B , 0x60 , 0x42 , 0xA6 , 0xB5 , 0xBE , 0xE0 ,
} ;
# define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
# define MXM_WMMX_FUNC_DSM 0x4D53445F /* NVIDIA DSM */
static const char acpi_nvidia_dsm_muid [ 16 ] = {
0xA0 , 0xA0 , 0x95 , 0x9D , 0x60 , 0x00 , 0x48 , 0x4D ,
0xB3 , 0x4D , 0x7E , 0x5F , 0xEA , 0x12 , 0x9F , 0xD4
@ -56,10 +58,22 @@ It looks like something for Intel GPU:
http : //lxr.linux.no/#linux+v3.1.5/drivers/gpu/drm/i915/intel_acpi.c
*/
# define DSM_TYPE_UNSUPPORTED 0
# define DSM_TYPE_OPTIMUS 1
# define DSM_TYPE_NVIDIA 2
static int dsm_type = DSM_TYPE_UNSUPPORTED ;
struct mxdsm_args {
u32 func ;
char muid [ 16 ] ;
u32 revid ;
u32 sfnc ;
char args [ 4 ] ;
} ;
enum dsm_type {
DSM_TYPE_UNSUPPORTED ,
DSM_TYPE_OPTIMUS ,
DSM_TYPE_NVIDIA ,
/* _DSM call through a WMI MX method */
DSM_TYPE_NVIDIA_WMI ,
} ;
static enum dsm_type dsm_type = DSM_TYPE_UNSUPPORTED ;
static struct pci_dev * dis_dev ;
static acpi_handle dis_handle ;
@ -78,6 +92,8 @@ static char *buffer_to_string(const char buffer[], char *target) {
return target ;
}
static int parse_dsm_result ( struct acpi_buffer * output , uint32_t * result ) ;
// Returns 0 if the call succeeded and non-zero otherwise. If the call
// succeeded, the result is stored in "result" providing that the result is an
// integer or a buffer containing 4 values
@ -86,7 +102,6 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid,
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
struct acpi_object_list input ;
union acpi_object params [ 4 ] ;
union acpi_object * obj ;
int err ;
input . count = 4 ;
@ -118,8 +133,12 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid,
buffer_to_string ( args , tmp ) , acpi_format_exception ( err ) ) ;
return err ;
}
return parse_dsm_result ( & output , result ) ;
}
obj = ( union acpi_object * ) output . pointer ;
// Processes the result of an ACPI _DSM call and deallocates the result memory
static int parse_dsm_result ( struct acpi_buffer * output , uint32_t * result ) {
union acpi_object * obj = output - > pointer ;
if ( obj - > type = = ACPI_TYPE_INTEGER & & result ) {
* result = obj - > integer . value ;
@ -136,7 +155,7 @@ static int acpi_call_dsm(acpi_handle handle, const char muid[16], int revid,
" type: %X \n " , obj - > type ) ;
}
kfree ( output . pointer ) ;
kfree ( output - > pointer ) ;
return 0 ;
}
@ -153,6 +172,51 @@ static int has_dsm_func(const char muid[16], int revid, int sfnc) {
return result & 1 & & result & ( 1 < < sfnc ) ;
}
// Returns 0 if the call succeeded and non-zero otherwise. If the call
// succeeded, the result is stored in "result" providing that the result is an
// integer or a buffer containing 4 values
static int wmmx_call ( const char muid [ 16 ] , int revid , int func , char args [ 4 ] ,
uint32_t * result ) {
struct mxdsm_args mx_args = {
. func = MXM_WMMX_FUNC_DSM ,
. revid = revid ,
. sfnc = func ,
} ;
struct acpi_buffer input = { ( acpi_size ) sizeof ( mx_args ) , & mx_args } ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
acpi_status status ;
memcpy ( mx_args . muid , muid , sizeof ( mx_args . muid ) ) ;
if ( args ) {
memcpy ( mx_args . args , args , sizeof ( mx_args . args ) ) ;
} else {
// some implementations do not like empty values
memset ( mx_args . args , 0 , sizeof ( mx_args . args ) ) ;
}
// 1, > 1 <-- required by at least Acer Travelmate 8472TG, seen on others
status = wmi_evaluate_method ( MXM_WMMX_GUID , 1 , 1 , & input , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_WARNING " bbswitch: failed to evaluate WMMX: %s \n " ,
acpi_format_exception ( status ) ) ;
return status ;
}
return parse_dsm_result ( & output , result ) ;
}
// Returns 1 if a _DSM function and its function index exists and 0 otherwise
static int has_wmi_func ( const char muid [ 16 ] , int revid , int sfnc ) {
u32 result = 0 ;
// fail if the _DSM call failed
if ( wmmx_call ( muid , revid , 0 , 0 , & result ) )
return 0 ;
// ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. If
// the n-th bit is enabled, function n is supported
return result & 1 & & result & ( 1 < < sfnc ) ;
}
static int bbswitch_optimus_dsm ( void ) {
if ( dsm_type = = DSM_TYPE_OPTIMUS ) {
char args [ ] = { 1 , 0 , 0 , 3 } ;
@ -170,10 +234,9 @@ static int bbswitch_optimus_dsm(void) {
}
static int bbswitch_acpi_off ( void ) {
if ( dsm_type = = DSM_TYPE_NVIDIA ) {
char args [ ] = { 2 , 0 , 0 , 0 } ;
u32 result = 0 ;
if ( dsm_type = = DSM_TYPE_NVIDIA ) {
if ( acpi_call_dsm ( dis_handle , acpi_nvidia_dsm_muid , 0x102 , 0x3 , args ,
& result ) ) {
// failure
@ -181,15 +244,21 @@ static int bbswitch_acpi_off(void) {
}
printk ( KERN_DEBUG " bbswitch: Result of _DSM call for OFF: %08X \n " ,
result ) ;
} else if ( dsm_type = = DSM_TYPE_NVIDIA_WMI ) {
if ( wmmx_call ( acpi_nvidia_dsm_muid , 0x102 , 0x3 , args , & result ) ) {
// failure
return 1 ;
}
printk ( KERN_DEBUG " bbswitch: Result of WMMX call for OFF: %08X \n " ,
result ) ;
}
return 0 ;
}
static int bbswitch_acpi_on ( void ) {
if ( dsm_type = = DSM_TYPE_NVIDIA ) {
char args [ ] = { 1 , 0 , 0 , 0 } ;
u32 result = 0 ;
if ( dsm_type = = DSM_TYPE_NVIDIA ) {
if ( acpi_call_dsm ( dis_handle , acpi_nvidia_dsm_muid , 0x102 , 0x3 , args ,
& result ) ) {
// failure
@ -197,6 +266,13 @@ static int bbswitch_acpi_on(void) {
}
printk ( KERN_DEBUG " bbswitch: Result of _DSM call for ON: %08X \n " ,
result ) ;
} else if ( dsm_type = = DSM_TYPE_NVIDIA_WMI ) {
if ( wmmx_call ( acpi_nvidia_dsm_muid , 0x102 , 0x3 , args , & result ) ) {
// failure
return 1 ;
}
printk ( KERN_DEBUG " bbswitch: Result of WMMX call for ON: %08X \n " ,
result ) ;
}
return 0 ;
}
@ -369,6 +445,9 @@ static int __init bbswitch_init(void) {
if ( has_dsm_func ( acpi_optimus_dsm_muid , 0x100 , 0x1A ) ) {
dsm_type = DSM_TYPE_OPTIMUS ;
printk ( KERN_INFO " bbswitch: detected an Optimus _DSM function \n " ) ;
} else if ( has_wmi_func ( acpi_nvidia_dsm_muid , 0x102 , 0x3 ) ) {
dsm_type = DSM_TYPE_NVIDIA_WMI ;
printk ( KERN_INFO " bbswitch: detected a nVidia WMMX function \n " ) ;
} else if ( has_dsm_func ( acpi_nvidia_dsm_muid , 0x102 , 0x3 ) ) {
dsm_type = DSM_TYPE_NVIDIA ;
printk ( KERN_INFO " bbswitch: detected a nVidia _DSM function \n " ) ;