2012-03-16 23:41:22 +04:00
/*
* Gmux driver for Apple laptops
*
* Copyright ( C ) Canonical Ltd . < seth . forshee @ canonical . com >
2012-08-17 20:17:04 +04:00
* Copyright ( C ) 2010 - 2012 Andreas Heider < andreas @ meetr . de >
2012-03-16 23:41:22 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/backlight.h>
# include <linux/acpi.h>
# include <linux/pnp.h>
# include <linux/apple_bl.h>
# include <linux/slab.h>
2012-08-09 21:45:01 +04:00
# include <linux/delay.h>
2012-08-17 20:17:04 +04:00
# include <linux/pci.h>
# include <linux/vga_switcheroo.h>
2012-03-16 23:41:22 +04:00
# include <acpi/video.h>
# include <asm/io.h>
struct apple_gmux_data {
unsigned long iostart ;
unsigned long iolen ;
2012-08-09 21:45:01 +04:00
bool indexed ;
struct mutex index_lock ;
2012-03-16 23:41:22 +04:00
struct backlight_device * bdev ;
2012-08-17 20:17:04 +04:00
/* switcheroo data */
acpi_handle dhandle ;
int gpe ;
enum vga_switcheroo_client_id resume_client_id ;
enum vga_switcheroo_state power_state ;
struct completion powerchange_done ;
2012-03-16 23:41:22 +04:00
} ;
2012-08-17 20:17:04 +04:00
static struct apple_gmux_data * apple_gmux_data ;
2012-03-16 23:41:22 +04:00
/*
* gmux port offsets . Many of these are not yet used , but may be in the
* future , and it ' s useful to have them documented here anyhow .
*/
# define GMUX_PORT_VERSION_MAJOR 0x04
# define GMUX_PORT_VERSION_MINOR 0x05
# define GMUX_PORT_VERSION_RELEASE 0x06
# define GMUX_PORT_SWITCH_DISPLAY 0x10
# define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
# define GMUX_PORT_INTERRUPT_ENABLE 0x14
# define GMUX_PORT_INTERRUPT_STATUS 0x16
# define GMUX_PORT_SWITCH_DDC 0x28
# define GMUX_PORT_SWITCH_EXTERNAL 0x40
# define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
# define GMUX_PORT_DISCRETE_POWER 0x50
# define GMUX_PORT_MAX_BRIGHTNESS 0x70
# define GMUX_PORT_BRIGHTNESS 0x74
2012-08-09 21:45:01 +04:00
# define GMUX_PORT_VALUE 0xc2
# define GMUX_PORT_READ 0xd0
# define GMUX_PORT_WRITE 0xd4
2012-03-16 23:41:22 +04:00
# define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
# define GMUX_INTERRUPT_ENABLE 0xff
# define GMUX_INTERRUPT_DISABLE 0x00
# define GMUX_INTERRUPT_STATUS_ACTIVE 0
# define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
# define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
# define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
# define GMUX_BRIGHTNESS_MASK 0x00ffffff
# define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
2012-08-09 21:45:01 +04:00
static u8 gmux_pio_read8 ( struct apple_gmux_data * gmux_data , int port )
2012-03-16 23:41:22 +04:00
{
return inb ( gmux_data - > iostart + port ) ;
}
2012-08-09 21:45:01 +04:00
static void gmux_pio_write8 ( struct apple_gmux_data * gmux_data , int port ,
2012-03-16 23:41:22 +04:00
u8 val )
{
outb ( val , gmux_data - > iostart + port ) ;
}
2012-08-09 21:45:01 +04:00
static u32 gmux_pio_read32 ( struct apple_gmux_data * gmux_data , int port )
2012-03-16 23:41:22 +04:00
{
return inl ( gmux_data - > iostart + port ) ;
}
2012-08-09 21:45:01 +04:00
static void gmux_pio_write32 ( struct apple_gmux_data * gmux_data , int port ,
u32 val )
2012-08-09 20:47:00 +04:00
{
int i ;
u8 tmpval ;
for ( i = 0 ; i < 4 ; i + + ) {
tmpval = ( val > > ( i * 8 ) ) & 0xff ;
2012-08-22 06:56:49 +04:00
outb ( tmpval , gmux_data - > iostart + port + i ) ;
2012-08-09 20:47:00 +04:00
}
}
2012-08-09 21:45:01 +04:00
static int gmux_index_wait_ready ( struct apple_gmux_data * gmux_data )
{
int i = 200 ;
u8 gwr = inb ( gmux_data - > iostart + GMUX_PORT_WRITE ) ;
while ( i & & ( gwr & 0x01 ) ) {
inb ( gmux_data - > iostart + GMUX_PORT_READ ) ;
gwr = inb ( gmux_data - > iostart + GMUX_PORT_WRITE ) ;
udelay ( 100 ) ;
i - - ;
}
return ! ! i ;
}
static int gmux_index_wait_complete ( struct apple_gmux_data * gmux_data )
{
int i = 200 ;
u8 gwr = inb ( gmux_data - > iostart + GMUX_PORT_WRITE ) ;
while ( i & & ! ( gwr & 0x01 ) ) {
gwr = inb ( gmux_data - > iostart + GMUX_PORT_WRITE ) ;
udelay ( 100 ) ;
i - - ;
}
if ( gwr & 0x01 )
inb ( gmux_data - > iostart + GMUX_PORT_READ ) ;
return ! ! i ;
}
static u8 gmux_index_read8 ( struct apple_gmux_data * gmux_data , int port )
{
u8 val ;
mutex_lock ( & gmux_data - > index_lock ) ;
gmux_index_wait_ready ( gmux_data ) ;
2012-08-25 12:30:48 +04:00
outb ( ( port & 0xff ) , gmux_data - > iostart + GMUX_PORT_READ ) ;
gmux_index_wait_complete ( gmux_data ) ;
2012-08-09 21:45:01 +04:00
val = inb ( gmux_data - > iostart + GMUX_PORT_VALUE ) ;
mutex_unlock ( & gmux_data - > index_lock ) ;
return val ;
}
static void gmux_index_write8 ( struct apple_gmux_data * gmux_data , int port ,
u8 val )
{
mutex_lock ( & gmux_data - > index_lock ) ;
outb ( val , gmux_data - > iostart + GMUX_PORT_VALUE ) ;
gmux_index_wait_ready ( gmux_data ) ;
outb ( port & 0xff , gmux_data - > iostart + GMUX_PORT_WRITE ) ;
gmux_index_wait_complete ( gmux_data ) ;
mutex_unlock ( & gmux_data - > index_lock ) ;
}
static u32 gmux_index_read32 ( struct apple_gmux_data * gmux_data , int port )
{
u32 val ;
mutex_lock ( & gmux_data - > index_lock ) ;
gmux_index_wait_ready ( gmux_data ) ;
2012-08-25 12:30:48 +04:00
outb ( ( port & 0xff ) , gmux_data - > iostart + GMUX_PORT_READ ) ;
gmux_index_wait_complete ( gmux_data ) ;
2012-08-09 21:45:01 +04:00
val = inl ( gmux_data - > iostart + GMUX_PORT_VALUE ) ;
mutex_unlock ( & gmux_data - > index_lock ) ;
return val ;
}
static void gmux_index_write32 ( struct apple_gmux_data * gmux_data , int port ,
u32 val )
{
int i ;
u8 tmpval ;
mutex_lock ( & gmux_data - > index_lock ) ;
for ( i = 0 ; i < 4 ; i + + ) {
tmpval = ( val > > ( i * 8 ) ) & 0xff ;
outb ( tmpval , gmux_data - > iostart + GMUX_PORT_VALUE + i ) ;
}
gmux_index_wait_ready ( gmux_data ) ;
outb ( port & 0xff , gmux_data - > iostart + GMUX_PORT_WRITE ) ;
gmux_index_wait_complete ( gmux_data ) ;
mutex_unlock ( & gmux_data - > index_lock ) ;
}
static u8 gmux_read8 ( struct apple_gmux_data * gmux_data , int port )
{
if ( gmux_data - > indexed )
return gmux_index_read8 ( gmux_data , port ) ;
else
return gmux_pio_read8 ( gmux_data , port ) ;
}
static void gmux_write8 ( struct apple_gmux_data * gmux_data , int port , u8 val )
{
if ( gmux_data - > indexed )
gmux_index_write8 ( gmux_data , port , val ) ;
else
gmux_pio_write8 ( gmux_data , port , val ) ;
}
static u32 gmux_read32 ( struct apple_gmux_data * gmux_data , int port )
{
if ( gmux_data - > indexed )
return gmux_index_read32 ( gmux_data , port ) ;
else
return gmux_pio_read32 ( gmux_data , port ) ;
}
static void gmux_write32 ( struct apple_gmux_data * gmux_data , int port ,
u32 val )
{
if ( gmux_data - > indexed )
gmux_index_write32 ( gmux_data , port , val ) ;
else
gmux_pio_write32 ( gmux_data , port , val ) ;
}
static bool gmux_is_indexed ( struct apple_gmux_data * gmux_data )
{
u16 val ;
outb ( 0xaa , gmux_data - > iostart + 0xcc ) ;
outb ( 0x55 , gmux_data - > iostart + 0xcd ) ;
outb ( 0x00 , gmux_data - > iostart + 0xce ) ;
val = inb ( gmux_data - > iostart + 0xcc ) |
( inb ( gmux_data - > iostart + 0xcd ) < < 8 ) ;
if ( val = = 0x55aa )
return true ;
return false ;
}
2012-03-16 23:41:22 +04:00
static int gmux_get_brightness ( struct backlight_device * bd )
{
struct apple_gmux_data * gmux_data = bl_get_data ( bd ) ;
return gmux_read32 ( gmux_data , GMUX_PORT_BRIGHTNESS ) &
GMUX_BRIGHTNESS_MASK ;
}
static int gmux_update_status ( struct backlight_device * bd )
{
struct apple_gmux_data * gmux_data = bl_get_data ( bd ) ;
u32 brightness = bd - > props . brightness ;
2012-04-19 19:55:35 +04:00
if ( bd - > props . state & BL_CORE_SUSPENDED )
2012-06-01 23:18:52 +04:00
return 0 ;
2012-04-19 19:55:35 +04:00
2012-08-09 20:47:00 +04:00
gmux_write32 ( gmux_data , GMUX_PORT_BRIGHTNESS , brightness ) ;
2012-03-16 23:41:22 +04:00
return 0 ;
}
static const struct backlight_ops gmux_bl_ops = {
2012-04-19 19:55:35 +04:00
. options = BL_CORE_SUSPENDRESUME ,
2012-03-16 23:41:22 +04:00
. get_brightness = gmux_get_brightness ,
. update_status = gmux_update_status ,
} ;
2012-08-17 20:17:04 +04:00
static int gmux_switchto ( enum vga_switcheroo_client_id id )
{
if ( id = = VGA_SWITCHEROO_IGD ) {
gmux_write8 ( apple_gmux_data , GMUX_PORT_SWITCH_DDC , 1 ) ;
gmux_write8 ( apple_gmux_data , GMUX_PORT_SWITCH_DISPLAY , 2 ) ;
gmux_write8 ( apple_gmux_data , GMUX_PORT_SWITCH_EXTERNAL , 2 ) ;
} else {
gmux_write8 ( apple_gmux_data , GMUX_PORT_SWITCH_DDC , 2 ) ;
gmux_write8 ( apple_gmux_data , GMUX_PORT_SWITCH_DISPLAY , 3 ) ;
gmux_write8 ( apple_gmux_data , GMUX_PORT_SWITCH_EXTERNAL , 3 ) ;
}
return 0 ;
}
static int gmux_set_discrete_state ( struct apple_gmux_data * gmux_data ,
enum vga_switcheroo_state state )
{
INIT_COMPLETION ( gmux_data - > powerchange_done ) ;
if ( state = = VGA_SWITCHEROO_ON ) {
gmux_write8 ( gmux_data , GMUX_PORT_DISCRETE_POWER , 1 ) ;
gmux_write8 ( gmux_data , GMUX_PORT_DISCRETE_POWER , 3 ) ;
pr_debug ( " Discrete card powered up \n " ) ;
} else {
gmux_write8 ( gmux_data , GMUX_PORT_DISCRETE_POWER , 1 ) ;
gmux_write8 ( gmux_data , GMUX_PORT_DISCRETE_POWER , 0 ) ;
pr_debug ( " Discrete card powered down \n " ) ;
}
gmux_data - > power_state = state ;
if ( gmux_data - > gpe > = 0 & &
! wait_for_completion_interruptible_timeout ( & gmux_data - > powerchange_done ,
msecs_to_jiffies ( 200 ) ) )
pr_warn ( " Timeout waiting for gmux switch to complete \n " ) ;
return 0 ;
}
static int gmux_set_power_state ( enum vga_switcheroo_client_id id ,
enum vga_switcheroo_state state )
{
if ( id = = VGA_SWITCHEROO_IGD )
return 0 ;
return gmux_set_discrete_state ( apple_gmux_data , state ) ;
}
static int gmux_get_client_id ( struct pci_dev * pdev )
{
/*
* Early Macbook Pros with switchable graphics use nvidia
* integrated graphics . Hardcode that the 9400 M is integrated .
*/
if ( pdev - > vendor = = PCI_VENDOR_ID_INTEL )
return VGA_SWITCHEROO_IGD ;
else if ( pdev - > vendor = = PCI_VENDOR_ID_NVIDIA & &
pdev - > device = = 0x0863 )
return VGA_SWITCHEROO_IGD ;
else
return VGA_SWITCHEROO_DIS ;
}
static enum vga_switcheroo_client_id
gmux_active_client ( struct apple_gmux_data * gmux_data )
{
if ( gmux_read8 ( gmux_data , GMUX_PORT_SWITCH_DISPLAY ) = = 2 )
return VGA_SWITCHEROO_IGD ;
return VGA_SWITCHEROO_DIS ;
}
static struct vga_switcheroo_handler gmux_handler = {
. switchto = gmux_switchto ,
. power_state = gmux_set_power_state ,
. get_client_id = gmux_get_client_id ,
} ;
static inline void gmux_disable_interrupts ( struct apple_gmux_data * gmux_data )
{
gmux_write8 ( gmux_data , GMUX_PORT_INTERRUPT_ENABLE ,
GMUX_INTERRUPT_DISABLE ) ;
}
static inline void gmux_enable_interrupts ( struct apple_gmux_data * gmux_data )
{
gmux_write8 ( gmux_data , GMUX_PORT_INTERRUPT_ENABLE ,
GMUX_INTERRUPT_ENABLE ) ;
}
static inline u8 gmux_interrupt_get_status ( struct apple_gmux_data * gmux_data )
{
return gmux_read8 ( gmux_data , GMUX_PORT_INTERRUPT_STATUS ) ;
}
static void gmux_clear_interrupts ( struct apple_gmux_data * gmux_data )
{
u8 status ;
/* to clear interrupts write back current status */
status = gmux_interrupt_get_status ( gmux_data ) ;
gmux_write8 ( gmux_data , GMUX_PORT_INTERRUPT_STATUS , status ) ;
}
static void gmux_notify_handler ( acpi_handle device , u32 value , void * context )
{
u8 status ;
struct pnp_dev * pnp = ( struct pnp_dev * ) context ;
struct apple_gmux_data * gmux_data = pnp_get_drvdata ( pnp ) ;
status = gmux_interrupt_get_status ( gmux_data ) ;
gmux_disable_interrupts ( gmux_data ) ;
pr_debug ( " Notify handler called: status %d \n " , status ) ;
gmux_clear_interrupts ( gmux_data ) ;
gmux_enable_interrupts ( gmux_data ) ;
if ( status & GMUX_INTERRUPT_STATUS_POWER )
complete ( & gmux_data - > powerchange_done ) ;
}
static int gmux_suspend ( struct pnp_dev * pnp , pm_message_t state )
{
struct apple_gmux_data * gmux_data = pnp_get_drvdata ( pnp ) ;
gmux_data - > resume_client_id = gmux_active_client ( gmux_data ) ;
gmux_disable_interrupts ( gmux_data ) ;
return 0 ;
}
static int gmux_resume ( struct pnp_dev * pnp )
{
struct apple_gmux_data * gmux_data = pnp_get_drvdata ( pnp ) ;
gmux_enable_interrupts ( gmux_data ) ;
gmux_switchto ( gmux_data - > resume_client_id ) ;
if ( gmux_data - > power_state = = VGA_SWITCHEROO_OFF )
gmux_set_discrete_state ( gmux_data , gmux_data - > power_state ) ;
return 0 ;
}
2012-12-22 01:18:33 +04:00
static int gmux_probe ( struct pnp_dev * pnp , const struct pnp_device_id * id )
2012-03-16 23:41:22 +04:00
{
struct apple_gmux_data * gmux_data ;
struct resource * res ;
struct backlight_properties props ;
struct backlight_device * bdev ;
u8 ver_major , ver_minor , ver_release ;
int ret = - ENXIO ;
2012-08-17 20:17:04 +04:00
acpi_status status ;
unsigned long long gpe ;
if ( apple_gmux_data )
return - EBUSY ;
2012-03-16 23:41:22 +04:00
gmux_data = kzalloc ( sizeof ( * gmux_data ) , GFP_KERNEL ) ;
if ( ! gmux_data )
return - ENOMEM ;
pnp_set_drvdata ( pnp , gmux_data ) ;
res = pnp_get_resource ( pnp , IORESOURCE_IO , 0 ) ;
if ( ! res ) {
pr_err ( " Failed to find gmux I/O resource \n " ) ;
goto err_free ;
}
gmux_data - > iostart = res - > start ;
gmux_data - > iolen = res - > end - res - > start ;
if ( gmux_data - > iolen < GMUX_MIN_IO_LEN ) {
pr_err ( " gmux I/O region too small (%lu < %u) \n " ,
gmux_data - > iolen , GMUX_MIN_IO_LEN ) ;
goto err_free ;
}
if ( ! request_region ( gmux_data - > iostart , gmux_data - > iolen ,
" Apple gmux " ) ) {
pr_err ( " gmux I/O already in use \n " ) ;
goto err_free ;
}
/*
2012-08-09 21:45:01 +04:00
* Invalid version information may indicate either that the gmux
* device isn ' t present or that it ' s a new one that uses indexed
* io
2012-03-16 23:41:22 +04:00
*/
2012-08-09 21:45:01 +04:00
2012-03-16 23:41:22 +04:00
ver_major = gmux_read8 ( gmux_data , GMUX_PORT_VERSION_MAJOR ) ;
ver_minor = gmux_read8 ( gmux_data , GMUX_PORT_VERSION_MINOR ) ;
ver_release = gmux_read8 ( gmux_data , GMUX_PORT_VERSION_RELEASE ) ;
if ( ver_major = = 0xff & & ver_minor = = 0xff & & ver_release = = 0xff ) {
2012-08-09 21:45:01 +04:00
if ( gmux_is_indexed ( gmux_data ) ) {
2012-08-25 12:30:49 +04:00
u32 version ;
2012-08-09 21:45:01 +04:00
mutex_init ( & gmux_data - > index_lock ) ;
gmux_data - > indexed = true ;
2012-08-25 12:30:49 +04:00
version = gmux_read32 ( gmux_data ,
GMUX_PORT_VERSION_MAJOR ) ;
ver_major = ( version > > 24 ) & 0xff ;
ver_minor = ( version > > 16 ) & 0xff ;
ver_release = ( version > > 8 ) & 0xff ;
2012-08-09 21:45:01 +04:00
} else {
pr_info ( " gmux device not present \n " ) ;
ret = - ENODEV ;
goto err_release ;
}
2012-03-16 23:41:22 +04:00
}
2012-08-25 12:30:49 +04:00
pr_info ( " Found gmux version %d.%d.%d [%s] \n " , ver_major , ver_minor ,
ver_release , ( gmux_data - > indexed ? " indexed " : " classic " ) ) ;
2012-03-16 23:41:22 +04:00
memset ( & props , 0 , sizeof ( props ) ) ;
props . type = BACKLIGHT_PLATFORM ;
props . max_brightness = gmux_read32 ( gmux_data , GMUX_PORT_MAX_BRIGHTNESS ) ;
/*
* Currently it ' s assumed that the maximum brightness is less than
* 2 ^ 24 for compatibility with old gmux versions . Cap the max
* brightness at this value , but print a warning if the hardware
* reports something higher so that it can be fixed .
*/
if ( WARN_ON ( props . max_brightness > GMUX_MAX_BRIGHTNESS ) )
props . max_brightness = GMUX_MAX_BRIGHTNESS ;
bdev = backlight_device_register ( " gmux_backlight " , & pnp - > dev ,
gmux_data , & gmux_bl_ops , & props ) ;
if ( IS_ERR ( bdev ) ) {
ret = PTR_ERR ( bdev ) ;
goto err_release ;
}
gmux_data - > bdev = bdev ;
bdev - > props . brightness = gmux_get_brightness ( bdev ) ;
backlight_update_status ( bdev ) ;
/*
* The backlight situation on Macs is complicated . If the gmux is
* present it ' s the best choice , because it always works for
* backlight control and supports more levels than other options .
* Disable the other backlight choices .
*/
2012-06-13 11:32:02 +04:00
acpi_video_dmi_promote_vendor ( ) ;
2012-03-16 23:41:22 +04:00
acpi_video_unregister ( ) ;
apple_bl_unregister ( ) ;
2012-08-17 20:17:04 +04:00
gmux_data - > power_state = VGA_SWITCHEROO_ON ;
gmux_data - > dhandle = DEVICE_ACPI_HANDLE ( & pnp - > dev ) ;
if ( ! gmux_data - > dhandle ) {
pr_err ( " Cannot find acpi handle for pnp device %s \n " ,
dev_name ( & pnp - > dev ) ) ;
ret = - ENODEV ;
goto err_notify ;
}
status = acpi_evaluate_integer ( gmux_data - > dhandle , " GMGP " , NULL , & gpe ) ;
if ( ACPI_SUCCESS ( status ) ) {
gmux_data - > gpe = ( int ) gpe ;
status = acpi_install_notify_handler ( gmux_data - > dhandle ,
ACPI_DEVICE_NOTIFY ,
& gmux_notify_handler , pnp ) ;
if ( ACPI_FAILURE ( status ) ) {
pr_err ( " Install notify handler failed: %s \n " ,
acpi_format_exception ( status ) ) ;
ret = - ENODEV ;
goto err_notify ;
}
status = acpi_enable_gpe ( NULL , gmux_data - > gpe ) ;
if ( ACPI_FAILURE ( status ) ) {
pr_err ( " Cannot enable gpe: %s \n " ,
acpi_format_exception ( status ) ) ;
goto err_enable_gpe ;
}
} else {
pr_warn ( " No GPE found for gmux \n " ) ;
gmux_data - > gpe = - 1 ;
}
if ( vga_switcheroo_register_handler ( & gmux_handler ) ) {
ret = - ENODEV ;
goto err_register_handler ;
}
init_completion ( & gmux_data - > powerchange_done ) ;
apple_gmux_data = gmux_data ;
gmux_enable_interrupts ( gmux_data ) ;
2012-03-16 23:41:22 +04:00
return 0 ;
2012-08-17 20:17:04 +04:00
err_register_handler :
if ( gmux_data - > gpe > = 0 )
acpi_disable_gpe ( NULL , gmux_data - > gpe ) ;
err_enable_gpe :
if ( gmux_data - > gpe > = 0 )
acpi_remove_notify_handler ( gmux_data - > dhandle ,
ACPI_DEVICE_NOTIFY ,
& gmux_notify_handler ) ;
err_notify :
backlight_device_unregister ( bdev ) ;
2012-03-16 23:41:22 +04:00
err_release :
release_region ( gmux_data - > iostart , gmux_data - > iolen ) ;
err_free :
kfree ( gmux_data ) ;
return ret ;
}
2012-12-22 01:18:33 +04:00
static void gmux_remove ( struct pnp_dev * pnp )
2012-03-16 23:41:22 +04:00
{
struct apple_gmux_data * gmux_data = pnp_get_drvdata ( pnp ) ;
2012-08-17 20:17:04 +04:00
vga_switcheroo_unregister_handler ( ) ;
gmux_disable_interrupts ( gmux_data ) ;
if ( gmux_data - > gpe > = 0 ) {
acpi_disable_gpe ( NULL , gmux_data - > gpe ) ;
acpi_remove_notify_handler ( gmux_data - > dhandle ,
ACPI_DEVICE_NOTIFY ,
& gmux_notify_handler ) ;
}
2012-03-16 23:41:22 +04:00
backlight_device_unregister ( gmux_data - > bdev ) ;
2012-08-17 20:17:04 +04:00
2012-03-16 23:41:22 +04:00
release_region ( gmux_data - > iostart , gmux_data - > iolen ) ;
2012-08-17 20:17:04 +04:00
apple_gmux_data = NULL ;
2012-03-16 23:41:22 +04:00
kfree ( gmux_data ) ;
2012-06-13 11:32:02 +04:00
acpi_video_dmi_demote_vendor ( ) ;
2012-03-16 23:41:22 +04:00
acpi_video_register ( ) ;
apple_bl_register ( ) ;
}
static const struct pnp_device_id gmux_device_ids [ ] = {
{ " APP000B " , 0 } ,
{ " " , 0 }
} ;
static struct pnp_driver gmux_pnp_driver = {
. name = " apple-gmux " ,
. probe = gmux_probe ,
2012-12-22 01:18:33 +04:00
. remove = gmux_remove ,
2012-03-16 23:41:22 +04:00
. id_table = gmux_device_ids ,
2012-08-17 20:17:04 +04:00
. suspend = gmux_suspend ,
. resume = gmux_resume
2012-03-16 23:41:22 +04:00
} ;
static int __init apple_gmux_init ( void )
{
return pnp_register_driver ( & gmux_pnp_driver ) ;
}
static void __exit apple_gmux_exit ( void )
{
pnp_unregister_driver ( & gmux_pnp_driver ) ;
}
module_init ( apple_gmux_init ) ;
module_exit ( apple_gmux_exit ) ;
MODULE_AUTHOR ( " Seth Forshee <seth.forshee@canonical.com> " ) ;
MODULE_DESCRIPTION ( " Apple Gmux Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pnp , gmux_device_ids ) ;