2008-06-10 00:56:16 +04:00
/*
2011-03-23 02:30:28 +03:00
* Backlight Driver for Intel - based Apples
2008-06-10 00:56:16 +04:00
*
* Copyright ( c ) Red Hat < mjg @ redhat . com >
* Based on code from Pommed :
* Copyright ( C ) 2006 Nicolas Boichat < nicolas @ boichat . ch >
* Copyright ( C ) 2006 Felipe Alfaro Solana < felipe_alfaro @ linuxmail . org >
* Copyright ( C ) 2007 Julien BLACHE < jb @ jblache . org >
*
* 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 .
*
* This driver triggers SMIs which cause the firmware to change the
* backlight brightness . This is icky in many ways , but it ' s impractical to
* get at the firmware code in order to figure out what it ' s actually doing .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/backlight.h>
# include <linux/err.h>
# include <linux/io.h>
2011-03-23 02:30:26 +03:00
# include <linux/pci.h>
# include <linux/acpi.h>
2008-06-10 00:56:16 +04:00
2011-03-23 02:30:28 +03:00
static struct backlight_device * apple_backlight_device ;
2008-06-10 00:56:16 +04:00
2011-03-23 02:30:26 +03:00
struct hw_data {
2009-01-11 03:11:34 +03:00
/* I/O resource to allocate. */
unsigned long iostart ;
unsigned long iolen ;
/* Backlight operations structure. */
2009-12-14 02:58:57 +03:00
const struct backlight_ops backlight_ops ;
2011-03-23 02:30:26 +03:00
void ( * set_brightness ) ( int ) ;
2008-06-10 00:56:16 +04:00
} ;
2011-03-23 02:30:26 +03:00
static const struct hw_data * hw_data ;
2011-03-23 02:30:28 +03:00
# define DRIVER "apple_backlight: "
2011-03-23 02:30:26 +03:00
2009-01-11 03:19:31 +03:00
/* Module parameters. */
static int debug ;
module_param_named ( debug , debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Set to one to enable debugging messages. " ) ;
2009-01-11 03:11:34 +03:00
/*
2011-03-23 02:30:28 +03:00
* Implementation for machines with Intel chipset .
2009-01-11 03:11:34 +03:00
*/
2011-03-23 02:30:26 +03:00
static void intel_chipset_set_brightness ( int intensity )
{
outb ( 0x04 | ( intensity < < 4 ) , 0xb3 ) ;
outb ( 0xbf , 0xb2 ) ;
}
2009-01-11 03:11:34 +03:00
static int intel_chipset_send_intensity ( struct backlight_device * bd )
2008-06-10 00:56:16 +04:00
{
int intensity = bd - > props . brightness ;
2009-01-11 03:19:31 +03:00
if ( debug )
2011-03-23 02:30:26 +03:00
printk ( KERN_DEBUG DRIVER " setting brightness to %d \n " ,
2009-01-11 03:19:31 +03:00
intensity ) ;
2011-03-23 02:30:26 +03:00
intel_chipset_set_brightness ( intensity ) ;
2008-06-10 00:56:16 +04:00
return 0 ;
}
2009-01-11 03:11:34 +03:00
static int intel_chipset_get_intensity ( struct backlight_device * bd )
2008-06-10 00:56:16 +04:00
{
2009-01-11 03:19:31 +03:00
int intensity ;
2008-06-10 00:56:16 +04:00
outb ( 0x03 , 0xb3 ) ;
outb ( 0xbf , 0xb2 ) ;
2009-01-11 03:19:31 +03:00
intensity = inb ( 0xb3 ) > > 4 ;
if ( debug )
2011-03-23 02:30:26 +03:00
printk ( KERN_DEBUG DRIVER " read brightness of %d \n " ,
2009-01-11 03:19:31 +03:00
intensity ) ;
return intensity ;
2008-06-10 00:56:16 +04:00
}
2011-03-23 02:30:26 +03:00
static const struct hw_data intel_chipset_data = {
2009-01-11 03:11:34 +03:00
. iostart = 0xb2 ,
. iolen = 2 ,
. backlight_ops = {
. options = BL_CORE_SUSPENDRESUME ,
. get_brightness = intel_chipset_get_intensity ,
. update_status = intel_chipset_send_intensity ,
2011-03-23 02:30:26 +03:00
} ,
. set_brightness = intel_chipset_set_brightness ,
2009-01-11 03:11:34 +03:00
} ;
/*
2011-03-23 02:30:28 +03:00
* Implementation for machines with Nvidia chipset .
2009-01-11 03:11:34 +03:00
*/
2011-03-23 02:30:26 +03:00
static void nvidia_chipset_set_brightness ( int intensity )
{
outb ( 0x04 | ( intensity < < 4 ) , 0x52f ) ;
outb ( 0xbf , 0x52e ) ;
}
2009-01-11 03:11:34 +03:00
static int nvidia_chipset_send_intensity ( struct backlight_device * bd )
{
int intensity = bd - > props . brightness ;
2009-01-11 03:19:31 +03:00
if ( debug )
2011-03-23 02:30:26 +03:00
printk ( KERN_DEBUG DRIVER " setting brightness to %d \n " ,
2009-01-11 03:19:31 +03:00
intensity ) ;
2011-03-23 02:30:26 +03:00
nvidia_chipset_set_brightness ( intensity ) ;
2009-01-11 03:11:34 +03:00
return 0 ;
}
static int nvidia_chipset_get_intensity ( struct backlight_device * bd )
{
2009-01-11 03:19:31 +03:00
int intensity ;
2009-01-11 03:11:34 +03:00
outb ( 0x03 , 0x52f ) ;
outb ( 0xbf , 0x52e ) ;
2009-01-11 03:19:31 +03:00
intensity = inb ( 0x52f ) > > 4 ;
if ( debug )
2011-03-23 02:30:26 +03:00
printk ( KERN_DEBUG DRIVER " read brightness of %d \n " ,
2009-01-11 03:19:31 +03:00
intensity ) ;
return intensity ;
2009-01-11 03:11:34 +03:00
}
2011-03-23 02:30:26 +03:00
static const struct hw_data nvidia_chipset_data = {
2009-01-11 03:11:34 +03:00
. iostart = 0x52e ,
. iolen = 2 ,
. backlight_ops = {
. options = BL_CORE_SUSPENDRESUME ,
. get_brightness = nvidia_chipset_get_intensity ,
. update_status = nvidia_chipset_send_intensity
2011-03-23 02:30:26 +03:00
} ,
. set_brightness = nvidia_chipset_set_brightness ,
2009-01-11 03:11:34 +03:00
} ;
2011-03-23 02:30:28 +03:00
static int __devinit apple_bl_add ( struct acpi_device * dev )
2009-01-11 03:11:34 +03:00
{
2011-03-23 02:30:26 +03:00
struct backlight_properties props ;
struct pci_dev * host ;
2011-03-23 02:30:27 +03:00
int intensity ;
2009-01-11 03:11:34 +03:00
2011-03-23 02:30:26 +03:00
host = pci_get_bus_and_slot ( 0 , 0 ) ;
2009-01-11 03:11:34 +03:00
2011-03-23 02:30:26 +03:00
if ( ! host ) {
printk ( KERN_ERR DRIVER " unable to find PCI host \n " ) ;
return - ENODEV ;
}
2008-06-10 00:56:16 +04:00
2011-03-23 02:30:26 +03:00
if ( host - > vendor = = PCI_VENDOR_ID_INTEL )
hw_data = & intel_chipset_data ;
else if ( host - > vendor = = PCI_VENDOR_ID_NVIDIA )
hw_data = & nvidia_chipset_data ;
pci_dev_put ( host ) ;
if ( ! hw_data ) {
printk ( KERN_ERR DRIVER " unknown hardware \n " ) ;
2008-06-10 00:56:16 +04:00
return - ENODEV ;
2011-03-23 02:30:26 +03:00
}
2008-06-10 00:56:16 +04:00
2011-03-23 02:30:27 +03:00
/* Check that the hardware responds - this may not work under EFI */
intensity = hw_data - > backlight_ops . get_brightness ( NULL ) ;
if ( ! intensity ) {
hw_data - > set_brightness ( 1 ) ;
if ( ! hw_data - > backlight_ops . get_brightness ( NULL ) )
return - ENODEV ;
hw_data - > set_brightness ( 0 ) ;
}
2011-03-23 02:30:26 +03:00
if ( ! request_region ( hw_data - > iostart , hw_data - > iolen ,
2011-03-23 02:30:28 +03:00
" Apple backlight " ) )
2008-06-10 00:56:16 +04:00
return - ENXIO ;
2010-02-18 00:39:44 +03:00
memset ( & props , 0 , sizeof ( struct backlight_properties ) ) ;
2011-03-23 02:30:21 +03:00
props . type = BACKLIGHT_PLATFORM ;
2010-02-18 00:39:44 +03:00
props . max_brightness = 15 ;
2011-03-23 02:30:28 +03:00
apple_backlight_device = backlight_device_register ( " apple_backlight " ,
NULL , NULL , & hw_data - > backlight_ops , & props ) ;
2011-03-23 02:30:26 +03:00
2011-03-23 02:30:28 +03:00
if ( IS_ERR ( apple_backlight_device ) ) {
2011-03-23 02:30:26 +03:00
release_region ( hw_data - > iostart , hw_data - > iolen ) ;
2011-03-23 02:30:28 +03:00
return PTR_ERR ( apple_backlight_device ) ;
2008-06-10 00:56:16 +04:00
}
2011-03-23 02:30:28 +03:00
apple_backlight_device - > props . brightness =
hw_data - > backlight_ops . get_brightness ( apple_backlight_device ) ;
backlight_update_status ( apple_backlight_device ) ;
2008-06-10 00:56:16 +04:00
return 0 ;
}
2011-03-23 02:30:28 +03:00
static int __devexit apple_bl_remove ( struct acpi_device * dev , int type )
2008-06-10 00:56:16 +04:00
{
2011-03-23 02:30:28 +03:00
backlight_device_unregister ( apple_backlight_device ) ;
2008-06-10 00:56:16 +04:00
2011-03-23 02:30:26 +03:00
release_region ( hw_data - > iostart , hw_data - > iolen ) ;
hw_data = NULL ;
return 0 ;
}
2011-03-23 02:30:28 +03:00
static const struct acpi_device_id apple_bl_ids [ ] = {
2011-03-23 02:30:26 +03:00
{ " APP0002 " , 0 } ,
{ " " , 0 } ,
} ;
2011-03-23 02:30:28 +03:00
static struct acpi_driver apple_bl_driver = {
. name = " Apple backlight " ,
. ids = apple_bl_ids ,
2011-03-23 02:30:26 +03:00
. ops = {
2011-03-23 02:30:28 +03:00
. add = apple_bl_add ,
. remove = apple_bl_remove ,
2011-03-23 02:30:26 +03:00
} ,
} ;
2011-03-23 02:30:28 +03:00
static int __init apple_bl_init ( void )
2011-03-23 02:30:26 +03:00
{
2011-03-23 02:30:28 +03:00
return acpi_bus_register_driver ( & apple_bl_driver ) ;
2011-03-23 02:30:26 +03:00
}
2011-03-23 02:30:28 +03:00
static void __exit apple_bl_exit ( void )
2011-03-23 02:30:26 +03:00
{
2011-03-23 02:30:28 +03:00
acpi_bus_unregister_driver ( & apple_bl_driver ) ;
2008-06-10 00:56:16 +04:00
}
2011-03-23 02:30:28 +03:00
module_init ( apple_bl_init ) ;
module_exit ( apple_bl_exit ) ;
2008-06-10 00:56:16 +04:00
MODULE_AUTHOR ( " Matthew Garrett <mjg@redhat.com> " ) ;
2011-03-23 02:30:28 +03:00
MODULE_DESCRIPTION ( " Apple Backlight Driver " ) ;
2008-06-10 00:56:16 +04:00
MODULE_LICENSE ( " GPL " ) ;
2011-03-23 02:30:28 +03:00
MODULE_DEVICE_TABLE ( acpi , apple_bl_ids ) ;
MODULE_ALIAS ( " mbp_nvidia_bl " ) ;