2007-08-29 10:28:19 +04:00
/*-*-linux-c-*-*/
/*
2008-06-11 04:44:56 +04:00
Copyright ( C ) 2007 , 2008 Jonathan Woithe < jwoithe @ physics . adelaide . edu . au >
Copyright ( C ) 2008 Peter Gruber < nokos @ gmx . net >
2008-12-31 21:19:59 +03:00
Copyright ( C ) 2008 Tony Vroon < tony @ linx . net >
2007-08-29 10:28:19 +04:00
Based on earlier work :
Copyright ( C ) 2003 Shane Spencer < shane @ bogomip . com >
Adrian Yee < brewt - fujitsu @ brewt . org >
2008-06-11 04:44:56 +04:00
Templated from msi - laptop . c and thinkpad_acpi . c which is copyright
by its respective authors .
2007-08-29 10:28:19 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
02110 - 1301 , USA .
*/
/*
* fujitsu - laptop . c - Fujitsu laptop support , providing access to additional
* features made available on a range of Fujitsu laptops including the
* P2xxx / P5xxx / S6xxx / S7xxx series .
*
* This driver exports a few files in / sys / devices / platform / fujitsu - laptop / ;
* others may be added at a later date .
*
* lcd_level - Screen brightness : contains a single integer in the
* range 0. .7 . ( rw )
*
* In addition to these platform device attributes the driver
* registers itself in the Linux backlight control subsystem and is
* available to userspace under / sys / class / backlight / fujitsu - laptop / .
*
2008-06-11 04:44:56 +04:00
* Hotkeys present on certain Fujitsu laptops ( eg : the S6xxx series ) are
* also supported by this driver .
*
2008-10-09 08:14:40 +04:00
* This driver has been tested on a Fujitsu Lifebook S6410 , S7020 and
* P8010 . It should work on most P - series and S - series Lifebooks , but
* YMMV .
2008-06-11 04:44:56 +04:00
*
* The module parameter use_alt_lcd_levels switches between different ACPI
* brightness controls which are used by different Fujitsu laptops . In most
* cases the correct method is automatically detected . " use_alt_lcd_levels=1 "
* is applicable for a Fujitsu Lifebook S6410 if autodetection fails .
*
2007-08-29 10:28:19 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/acpi.h>
# include <linux/dmi.h>
# include <linux/backlight.h>
2008-06-11 04:44:56 +04:00
# include <linux/input.h>
# include <linux/kfifo.h>
# include <linux/video_output.h>
2007-08-29 10:28:19 +04:00
# include <linux/platform_device.h>
2009-08-25 09:11:52 +04:00
# if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
2008-12-31 21:19:59 +03:00
# include <linux/leds.h>
# endif
2007-08-29 10:28:19 +04:00
2009-07-31 12:46:59 +04:00
# define FUJITSU_DRIVER_VERSION "0.6.0"
2007-08-29 10:28:19 +04:00
# define FUJITSU_LCD_N_LEVELS 8
# define ACPI_FUJITSU_CLASS "fujitsu"
# define ACPI_FUJITSU_HID "FUJ02B1"
2008-06-11 04:44:56 +04:00
# define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
2007-08-29 10:28:19 +04:00
# define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1"
2008-06-11 04:44:56 +04:00
# define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3"
# define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
# define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
# define ACPI_FUJITSU_NOTIFY_CODE1 0x80
# define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
# define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
2008-12-31 21:19:59 +03:00
/* FUNC interface - command values */
# define FUNC_RFKILL 0x1000
# define FUNC_LEDS 0x1001
# define FUNC_BUTTONS 0x1002
# define FUNC_BACKLIGHT 0x1004
/* FUNC interface - responses */
# define UNSUPPORTED_CMD 0x80000000
2009-08-25 09:11:52 +04:00
# if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
2008-12-31 21:19:59 +03:00
/* FUNC interface - LED control */
# define FUNC_LED_OFF 0x1
# define FUNC_LED_ON 0x30001
# define KEYBOARD_LAMPS 0x100
# define LOGOLAMP_POWERON 0x2000
# define LOGOLAMP_ALWAYS 0x4000
# endif
2008-06-11 04:44:56 +04:00
/* Hotkey details */
2008-10-09 08:14:40 +04:00
# define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
# define KEY2_CODE 0x411
# define KEY3_CODE 0x412
# define KEY4_CODE 0x413
2008-06-11 04:44:56 +04:00
# define MAX_HOTKEY_RINGBUFFER_SIZE 100
# define RINGBUFFERSIZE 40
/* Debugging */
# define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": "
# define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG
# define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG
# define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG
# define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG
# define FUJLAPTOP_DBG_ALL 0xffff
# define FUJLAPTOP_DBG_ERROR 0x0001
# define FUJLAPTOP_DBG_WARN 0x0002
# define FUJLAPTOP_DBG_INFO 0x0004
# define FUJLAPTOP_DBG_TRACE 0x0008
# define dbg_printk(a_dbg_level, format, arg...) \
do { if ( dbg_level & a_dbg_level ) \
printk ( FUJLAPTOP_DEBUG " %s: " format , __func__ , # # arg ) ; \
} while ( 0 )
# ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
# define vdbg_printk(a_dbg_level, format, arg...) \
dbg_printk ( a_dbg_level , format , # # arg )
# else
# define vdbg_printk(a_dbg_level, format, arg...)
# endif
/* Device controlling the backlight and associated keys */
2007-08-29 10:28:19 +04:00
struct fujitsu_t {
acpi_handle acpi_handle ;
2008-06-11 04:44:56 +04:00
struct acpi_device * dev ;
struct input_dev * input ;
char phys [ 32 ] ;
2007-08-29 10:28:19 +04:00
struct backlight_device * bl_device ;
struct platform_device * pf_device ;
2008-10-09 08:14:40 +04:00
int keycode1 , keycode2 , keycode3 , keycode4 ;
2007-08-29 10:28:19 +04:00
2008-06-11 04:44:56 +04:00
unsigned int max_brightness ;
2007-08-29 10:28:19 +04:00
unsigned int brightness_changed ;
unsigned int brightness_level ;
} ;
static struct fujitsu_t * fujitsu ;
2008-06-11 04:44:56 +04:00
static int use_alt_lcd_levels = - 1 ;
static int disable_brightness_adjust = - 1 ;
/* Device used to access other hotkeys on the laptop */
struct fujitsu_hotkey_t {
acpi_handle acpi_handle ;
struct acpi_device * dev ;
struct input_dev * input ;
char phys [ 32 ] ;
struct platform_device * pf_device ;
struct kfifo * fifo ;
spinlock_t fifo_lock ;
2009-02-02 14:11:10 +03:00
int rfkill_supported ;
2008-12-31 21:19:59 +03:00
int rfkill_state ;
int logolamp_registered ;
int kblamps_registered ;
2008-06-11 04:44:56 +04:00
} ;
2007-08-29 10:28:19 +04:00
2008-06-11 04:44:56 +04:00
static struct fujitsu_hotkey_t * fujitsu_hotkey ;
2009-04-07 19:37:22 +04:00
static void acpi_fujitsu_hotkey_notify ( struct acpi_device * device , u32 event ) ;
2008-06-11 04:44:56 +04:00
2009-08-25 09:11:52 +04:00
# if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
2008-12-31 21:19:59 +03:00
static enum led_brightness logolamp_get ( struct led_classdev * cdev ) ;
static void logolamp_set ( struct led_classdev * cdev ,
enum led_brightness brightness ) ;
struct led_classdev logolamp_led = {
. name = " fujitsu::logolamp " ,
. brightness_get = logolamp_get ,
. brightness_set = logolamp_set
} ;
static enum led_brightness kblamps_get ( struct led_classdev * cdev ) ;
static void kblamps_set ( struct led_classdev * cdev ,
enum led_brightness brightness ) ;
struct led_classdev kblamps_led = {
. name = " fujitsu::kblamps " ,
. brightness_get = kblamps_get ,
. brightness_set = kblamps_set
} ;
# endif
2008-06-11 04:44:56 +04:00
# ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
static u32 dbg_level = 0x03 ;
# endif
2009-04-07 19:37:16 +04:00
static void acpi_fujitsu_notify ( struct acpi_device * device , u32 event ) ;
2008-06-11 04:44:56 +04:00
2008-12-31 21:19:59 +03:00
/* Fujitsu ACPI interface function */
static int call_fext_func ( int cmd , int arg0 , int arg1 , int arg2 )
{
acpi_status status = AE_OK ;
union acpi_object params [ 4 ] = {
{ . type = ACPI_TYPE_INTEGER } ,
{ . type = ACPI_TYPE_INTEGER } ,
{ . type = ACPI_TYPE_INTEGER } ,
{ . type = ACPI_TYPE_INTEGER }
} ;
struct acpi_object_list arg_list = { 4 , & params [ 0 ] } ;
struct acpi_buffer output ;
union acpi_object out_obj ;
acpi_handle handle = NULL ;
status = acpi_get_handle ( fujitsu_hotkey - > acpi_handle , " FUNC " , & handle ) ;
if ( ACPI_FAILURE ( status ) ) {
vdbg_printk ( FUJLAPTOP_DBG_ERROR ,
" FUNC interface is not present \n " ) ;
return - ENODEV ;
}
params [ 0 ] . integer . value = cmd ;
params [ 1 ] . integer . value = arg0 ;
params [ 2 ] . integer . value = arg1 ;
params [ 3 ] . integer . value = arg2 ;
output . length = sizeof ( out_obj ) ;
output . pointer = & out_obj ;
status = acpi_evaluate_object ( handle , NULL , & arg_list , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
vdbg_printk ( FUJLAPTOP_DBG_WARN ,
" FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed \n " ,
cmd , arg0 , arg1 , arg2 ) ;
return - ENODEV ;
}
if ( out_obj . type ! = ACPI_TYPE_INTEGER ) {
vdbg_printk ( FUJLAPTOP_DBG_WARN ,
" FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not "
" return an integer \n " ,
cmd , arg0 , arg1 , arg2 ) ;
return - ENODEV ;
}
vdbg_printk ( FUJLAPTOP_DBG_TRACE ,
" FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x \n " ,
cmd , arg0 , arg1 , arg2 , ( int ) out_obj . integer . value ) ;
return out_obj . integer . value ;
}
2009-08-25 09:11:52 +04:00
# if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
2008-12-31 21:19:59 +03:00
/* LED class callbacks */
static void logolamp_set ( struct led_classdev * cdev ,
enum led_brightness brightness )
{
if ( brightness > = LED_FULL ) {
call_fext_func ( FUNC_LEDS , 0x1 , LOGOLAMP_POWERON , FUNC_LED_ON ) ;
call_fext_func ( FUNC_LEDS , 0x1 , LOGOLAMP_ALWAYS , FUNC_LED_ON ) ;
} else if ( brightness > = LED_HALF ) {
call_fext_func ( FUNC_LEDS , 0x1 , LOGOLAMP_POWERON , FUNC_LED_ON ) ;
call_fext_func ( FUNC_LEDS , 0x1 , LOGOLAMP_ALWAYS , FUNC_LED_OFF ) ;
} else {
call_fext_func ( FUNC_LEDS , 0x1 , LOGOLAMP_POWERON , FUNC_LED_OFF ) ;
}
}
static void kblamps_set ( struct led_classdev * cdev ,
enum led_brightness brightness )
{
if ( brightness > = LED_FULL )
call_fext_func ( FUNC_LEDS , 0x1 , KEYBOARD_LAMPS , FUNC_LED_ON ) ;
else
call_fext_func ( FUNC_LEDS , 0x1 , KEYBOARD_LAMPS , FUNC_LED_OFF ) ;
}
static enum led_brightness logolamp_get ( struct led_classdev * cdev )
{
enum led_brightness brightness = LED_OFF ;
int poweron , always ;
poweron = call_fext_func ( FUNC_LEDS , 0x2 , LOGOLAMP_POWERON , 0x0 ) ;
if ( poweron = = FUNC_LED_ON ) {
brightness = LED_HALF ;
always = call_fext_func ( FUNC_LEDS , 0x2 , LOGOLAMP_ALWAYS , 0x0 ) ;
if ( always = = FUNC_LED_ON )
brightness = LED_FULL ;
}
return brightness ;
}
static enum led_brightness kblamps_get ( struct led_classdev * cdev )
{
enum led_brightness brightness = LED_OFF ;
if ( call_fext_func ( FUNC_LEDS , 0x2 , KEYBOARD_LAMPS , 0x0 ) = = FUNC_LED_ON )
brightness = LED_FULL ;
return brightness ;
}
# endif
2008-06-11 04:44:56 +04:00
/* Hardware access for LCD brightness control */
2007-08-29 10:28:19 +04:00
static int set_lcd_level ( int level )
{
acpi_status status = AE_OK ;
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list arg_list = { 1 , & arg0 } ;
acpi_handle handle = NULL ;
2008-06-11 04:44:56 +04:00
vdbg_printk ( FUJLAPTOP_DBG_TRACE , " set lcd level via SBLL [%d] \n " ,
level ) ;
if ( level < 0 | | level > = fujitsu - > max_brightness )
2007-08-29 10:28:19 +04:00
return - EINVAL ;
status = acpi_get_handle ( fujitsu - > acpi_handle , " SBLL " , & handle ) ;
if ( ACPI_FAILURE ( status ) ) {
2008-06-11 04:44:56 +04:00
vdbg_printk ( FUJLAPTOP_DBG_ERROR , " SBLL not present \n " ) ;
return - ENODEV ;
}
arg0 . integer . value = level ;
status = acpi_evaluate_object ( handle , NULL , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
return 0 ;
}
static int set_lcd_level_alt ( int level )
{
acpi_status status = AE_OK ;
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list arg_list = { 1 , & arg0 } ;
acpi_handle handle = NULL ;
vdbg_printk ( FUJLAPTOP_DBG_TRACE , " set lcd level via SBL2 [%d] \n " ,
level ) ;
if ( level < 0 | | level > = fujitsu - > max_brightness )
return - EINVAL ;
status = acpi_get_handle ( fujitsu - > acpi_handle , " SBL2 " , & handle ) ;
if ( ACPI_FAILURE ( status ) ) {
vdbg_printk ( FUJLAPTOP_DBG_ERROR , " SBL2 not present \n " ) ;
2007-08-29 10:28:19 +04:00
return - ENODEV ;
}
arg0 . integer . value = level ;
status = acpi_evaluate_object ( handle , NULL , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
return 0 ;
}
static int get_lcd_level ( void )
{
2008-10-10 10:22:59 +04:00
unsigned long long state = 0 ;
2007-08-29 10:28:19 +04:00
acpi_status status = AE_OK ;
2008-06-11 04:44:56 +04:00
vdbg_printk ( FUJLAPTOP_DBG_TRACE , " get lcd level via GBLL \n " ) ;
2007-08-29 10:28:19 +04:00
status =
acpi_evaluate_integer ( fujitsu - > acpi_handle , " GBLL " , NULL , & state ) ;
if ( status < 0 )
return status ;
2008-06-11 04:44:56 +04:00
fujitsu - > brightness_level = state & 0x0fffffff ;
if ( state & 0x80000000 )
fujitsu - > brightness_changed = 1 ;
else
fujitsu - > brightness_changed = 0 ;
return fujitsu - > brightness_level ;
}
static int get_max_brightness ( void )
{
2008-10-10 10:22:59 +04:00
unsigned long long state = 0 ;
2008-06-11 04:44:56 +04:00
acpi_status status = AE_OK ;
vdbg_printk ( FUJLAPTOP_DBG_TRACE , " get max lcd level via RBLL \n " ) ;
status =
acpi_evaluate_integer ( fujitsu - > acpi_handle , " RBLL " , NULL , & state ) ;
if ( status < 0 )
return status ;
fujitsu - > max_brightness = state ;
return fujitsu - > max_brightness ;
}
2007-08-29 10:28:19 +04:00
/* Backlight device stuff */
static int bl_get_brightness ( struct backlight_device * b )
{
2009-01-07 13:11:24 +03:00
return get_lcd_level ( ) ;
2007-08-29 10:28:19 +04:00
}
static int bl_update_status ( struct backlight_device * b )
{
2008-12-31 21:19:59 +03:00
int ret ;
if ( b - > props . power = = 4 )
ret = call_fext_func ( FUNC_BACKLIGHT , 0x1 , 0x4 , 0x3 ) ;
else
ret = call_fext_func ( FUNC_BACKLIGHT , 0x1 , 0x4 , 0x0 ) ;
if ( ret ! = 0 )
vdbg_printk ( FUJLAPTOP_DBG_ERROR ,
" Unable to adjust backlight power, error code %i \n " ,
ret ) ;
2008-06-11 04:44:56 +04:00
if ( use_alt_lcd_levels )
2008-12-31 21:19:59 +03:00
ret = set_lcd_level_alt ( b - > props . brightness ) ;
2008-06-11 04:44:56 +04:00
else
2008-12-31 21:19:59 +03:00
ret = set_lcd_level ( b - > props . brightness ) ;
if ( ret ! = 0 )
vdbg_printk ( FUJLAPTOP_DBG_ERROR ,
" Unable to adjust LCD brightness, error code %i \n " ,
ret ) ;
return ret ;
2007-08-29 10:28:19 +04:00
}
static struct backlight_ops fujitsubl_ops = {
. get_brightness = bl_get_brightness ,
. update_status = bl_update_status ,
} ;
2008-06-11 04:44:56 +04:00
/* Platform LCD brightness device */
static ssize_t
show_max_brightness ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int ret ;
ret = get_max_brightness ( ) ;
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %i \n " , ret ) ;
}
static ssize_t
show_brightness_changed ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int ret ;
ret = fujitsu - > brightness_changed ;
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %i \n " , ret ) ;
}
2007-08-29 10:28:19 +04:00
static ssize_t show_lcd_level ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int ret ;
2009-01-07 13:11:24 +03:00
ret = get_lcd_level ( ) ;
2007-08-29 10:28:19 +04:00
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %i \n " , ret ) ;
}
static ssize_t store_lcd_level ( struct device * dev ,
struct device_attribute * attr , const char * buf ,
size_t count )
{
int level , ret ;
if ( sscanf ( buf , " %i " , & level ) ! = 1
2008-06-11 04:44:56 +04:00
| | ( level < 0 | | level > = fujitsu - > max_brightness ) )
2007-08-29 10:28:19 +04:00
return - EINVAL ;
2008-06-11 04:44:56 +04:00
if ( use_alt_lcd_levels )
ret = set_lcd_level_alt ( level ) ;
else
ret = set_lcd_level ( level ) ;
if ( ret < 0 )
return ret ;
2009-01-07 13:11:24 +03:00
ret = get_lcd_level ( ) ;
2007-08-29 10:28:19 +04:00
if ( ret < 0 )
return ret ;
return count ;
}
2008-12-31 21:19:59 +03:00
static ssize_t
ignore_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2008-06-11 04:44:56 +04:00
{
2008-12-31 21:19:59 +03:00
return count ;
}
2008-06-11 04:44:56 +04:00
2008-12-31 21:19:59 +03:00
static ssize_t
show_lid_state ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-02-02 14:11:10 +03:00
if ( ! ( fujitsu_hotkey - > rfkill_supported & 0x100 ) )
2008-12-31 21:19:59 +03:00
return sprintf ( buf , " unknown \n " ) ;
if ( fujitsu_hotkey - > rfkill_state & 0x100 )
return sprintf ( buf , " open \n " ) ;
else
return sprintf ( buf , " closed \n " ) ;
}
2008-06-11 04:44:56 +04:00
2008-12-31 21:19:59 +03:00
static ssize_t
show_dock_state ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-02-02 14:11:10 +03:00
if ( ! ( fujitsu_hotkey - > rfkill_supported & 0x200 ) )
2008-12-31 21:19:59 +03:00
return sprintf ( buf , " unknown \n " ) ;
if ( fujitsu_hotkey - > rfkill_state & 0x200 )
return sprintf ( buf , " docked \n " ) ;
else
return sprintf ( buf , " undocked \n " ) ;
2008-06-11 04:44:56 +04:00
}
static ssize_t
2008-12-31 21:19:59 +03:00
show_radios_state ( struct device * dev ,
struct device_attribute * attr , char * buf )
2008-06-11 04:44:56 +04:00
{
2009-02-02 14:11:10 +03:00
if ( ! ( fujitsu_hotkey - > rfkill_supported & 0x20 ) )
2008-12-31 21:19:59 +03:00
return sprintf ( buf , " unknown \n " ) ;
if ( fujitsu_hotkey - > rfkill_state & 0x20 )
return sprintf ( buf , " on \n " ) ;
else
return sprintf ( buf , " killed \n " ) ;
2008-06-11 04:44:56 +04:00
}
static DEVICE_ATTR ( max_brightness , 0444 , show_max_brightness , ignore_store ) ;
static DEVICE_ATTR ( brightness_changed , 0444 , show_brightness_changed ,
ignore_store ) ;
2007-08-29 10:28:19 +04:00
static DEVICE_ATTR ( lcd_level , 0644 , show_lcd_level , store_lcd_level ) ;
2008-12-31 21:19:59 +03:00
static DEVICE_ATTR ( lid , 0444 , show_lid_state , ignore_store ) ;
static DEVICE_ATTR ( dock , 0444 , show_dock_state , ignore_store ) ;
static DEVICE_ATTR ( radios , 0444 , show_radios_state , ignore_store ) ;
2007-08-29 10:28:19 +04:00
static struct attribute * fujitsupf_attributes [ ] = {
2008-06-11 04:44:56 +04:00
& dev_attr_brightness_changed . attr ,
& dev_attr_max_brightness . attr ,
2007-08-29 10:28:19 +04:00
& dev_attr_lcd_level . attr ,
2008-12-31 21:19:59 +03:00
& dev_attr_lid . attr ,
& dev_attr_dock . attr ,
& dev_attr_radios . attr ,
2007-08-29 10:28:19 +04:00
NULL
} ;
static struct attribute_group fujitsupf_attribute_group = {
. attrs = fujitsupf_attributes
} ;
static struct platform_driver fujitsupf_driver = {
. driver = {
. name = " fujitsu-laptop " ,
. owner = THIS_MODULE ,
}
} ;
2008-10-09 08:14:40 +04:00
static void dmi_check_cb_common ( const struct dmi_system_id * id )
2008-06-11 04:44:56 +04:00
{
acpi_handle handle ;
printk ( KERN_INFO " fujitsu-laptop: Identified laptop model '%s'. \n " ,
id - > ident ) ;
if ( use_alt_lcd_levels = = - 1 ) {
2009-01-07 13:11:24 +03:00
if ( ACPI_SUCCESS ( acpi_get_handle ( NULL ,
" \\ _SB.PCI0.LPCB.FJEX.SBL2 " , & handle ) ) )
use_alt_lcd_levels = 1 ;
else
use_alt_lcd_levels = 0 ;
vdbg_printk ( FUJLAPTOP_DBG_TRACE , " auto-detected usealt as "
" %i \n " , use_alt_lcd_levels ) ;
2008-06-11 04:44:56 +04:00
}
2008-10-09 08:14:40 +04:00
}
static int dmi_check_cb_s6410 ( const struct dmi_system_id * id )
{
dmi_check_cb_common ( id ) ;
fujitsu - > keycode1 = KEY_SCREENLOCK ; /* "Lock" */
fujitsu - > keycode2 = KEY_HELP ; /* "Mobility Center" */
return 0 ;
}
2008-11-09 07:20:05 +03:00
static int dmi_check_cb_s6420 ( const struct dmi_system_id * id )
{
dmi_check_cb_common ( id ) ;
fujitsu - > keycode1 = KEY_SCREENLOCK ; /* "Lock" */
fujitsu - > keycode2 = KEY_HELP ; /* "Mobility Center" */
return 0 ;
}
2008-10-09 08:14:40 +04:00
static int dmi_check_cb_p8010 ( const struct dmi_system_id * id )
{
dmi_check_cb_common ( id ) ;
fujitsu - > keycode1 = KEY_HELP ; /* "Support" */
fujitsu - > keycode3 = KEY_SWITCHVIDEOMODE ; /* "Presentation" */
fujitsu - > keycode4 = KEY_WWW ; /* "Internet" */
2008-06-11 04:44:56 +04:00
return 0 ;
}
2008-10-30 00:13:20 +03:00
static struct dmi_system_id fujitsu_dmi_table [ ] = {
2008-06-11 04:44:56 +04:00
{
2008-10-09 08:14:40 +04:00
. ident = " Fujitsu Siemens S6410 " ,
2008-06-11 04:44:56 +04:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU SIEMENS " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " LIFEBOOK S6410 " ) ,
} ,
. callback = dmi_check_cb_s6410 } ,
2008-11-09 07:20:05 +03:00
{
. ident = " Fujitsu Siemens S6420 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU SIEMENS " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " LIFEBOOK S6420 " ) ,
} ,
. callback = dmi_check_cb_s6420 } ,
2008-08-29 05:36:21 +04:00
{
2008-10-09 08:14:40 +04:00
. ident = " Fujitsu LifeBook P8010 " ,
2008-08-29 05:36:21 +04:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " LifeBook P8010 " ) ,
2008-10-09 08:14:40 +04:00
} ,
. callback = dmi_check_cb_p8010 } ,
2008-06-11 04:44:56 +04:00
{ }
} ;
/* ACPI device for LCD brightness control */
2007-08-29 10:28:19 +04:00
2007-10-24 20:23:16 +04:00
static int acpi_fujitsu_add ( struct acpi_device * device )
2007-08-29 10:28:19 +04:00
{
2008-06-11 04:44:56 +04:00
acpi_handle handle ;
2007-08-29 10:28:19 +04:00
int result = 0 ;
int state = 0 ;
2008-06-11 04:44:56 +04:00
struct input_dev * input ;
int error ;
2007-08-29 10:28:19 +04:00
if ( ! device )
return - EINVAL ;
fujitsu - > acpi_handle = device - > handle ;
sprintf ( acpi_device_name ( device ) , " %s " , ACPI_FUJITSU_DEVICE_NAME ) ;
sprintf ( acpi_device_class ( device ) , " %s " , ACPI_FUJITSU_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = fujitsu ;
2007-08-29 10:28:19 +04:00
2008-06-11 04:44:56 +04:00
fujitsu - > input = input = input_allocate_device ( ) ;
if ( ! input ) {
error = - ENOMEM ;
2009-04-07 19:37:16 +04:00
goto err_stop ;
2008-06-11 04:44:56 +04:00
}
snprintf ( fujitsu - > phys , sizeof ( fujitsu - > phys ) ,
" %s/video/input0 " , acpi_device_hid ( device ) ) ;
input - > name = acpi_device_name ( device ) ;
input - > phys = fujitsu - > phys ;
input - > id . bustype = BUS_HOST ;
input - > id . product = 0x06 ;
input - > dev . parent = & device - > dev ;
input - > evbit [ 0 ] = BIT ( EV_KEY ) ;
set_bit ( KEY_BRIGHTNESSUP , input - > keybit ) ;
set_bit ( KEY_BRIGHTNESSDOWN , input - > keybit ) ;
set_bit ( KEY_UNKNOWN , input - > keybit ) ;
error = input_register_device ( input ) ;
if ( error )
goto err_free_input_dev ;
2007-08-29 10:28:19 +04:00
result = acpi_bus_get_power ( fujitsu - > acpi_handle , & state ) ;
if ( result ) {
2008-06-11 04:44:56 +04:00
printk ( KERN_ERR " Error reading power state \n " ) ;
2009-07-31 12:46:02 +04:00
goto err_unregister_input_dev ;
2007-08-29 10:28:19 +04:00
}
2009-07-29 00:45:54 +04:00
printk ( KERN_INFO " ACPI: %s [%s] (%s) \n " ,
2007-08-29 10:28:19 +04:00
acpi_device_name ( device ) , acpi_device_bid ( device ) ,
! device - > power . state ? " on " : " off " ) ;
2008-06-11 04:44:56 +04:00
fujitsu - > dev = device ;
if ( ACPI_SUCCESS
( acpi_get_handle ( device - > handle , METHOD_NAME__INI , & handle ) ) ) {
vdbg_printk ( FUJLAPTOP_DBG_INFO , " Invoking _INI \n " ) ;
if ( ACPI_FAILURE
( acpi_evaluate_object
( device - > handle , METHOD_NAME__INI , NULL , NULL ) ) )
printk ( KERN_ERR " _INI Method failed \n " ) ;
}
/* do config (detect defaults) */
use_alt_lcd_levels = use_alt_lcd_levels = = 1 ? 1 : 0 ;
disable_brightness_adjust = disable_brightness_adjust = = 1 ? 1 : 0 ;
vdbg_printk ( FUJLAPTOP_DBG_INFO ,
2009-01-07 13:11:24 +03:00
" config: [alt interface: %d], [adjust disable: %d] \n " ,
use_alt_lcd_levels , disable_brightness_adjust ) ;
2008-06-11 04:44:56 +04:00
if ( get_max_brightness ( ) < = 0 )
fujitsu - > max_brightness = FUJITSU_LCD_N_LEVELS ;
2009-01-07 13:11:24 +03:00
get_lcd_level ( ) ;
2008-06-11 04:44:56 +04:00
return result ;
2009-07-31 12:46:02 +04:00
err_unregister_input_dev :
input_unregister_device ( input ) ;
2008-06-11 04:44:56 +04:00
err_free_input_dev :
input_free_device ( input ) ;
err_stop :
2007-08-29 10:28:19 +04:00
return result ;
}
2007-10-24 20:23:16 +04:00
static int acpi_fujitsu_remove ( struct acpi_device * device , int type )
2007-08-29 10:28:19 +04:00
{
2009-07-31 12:46:02 +04:00
struct fujitsu_t * fujitsu = acpi_driver_data ( device ) ;
struct input_dev * input = fujitsu - > input ;
2007-08-29 10:28:19 +04:00
2009-07-31 12:46:02 +04:00
input_unregister_device ( input ) ;
2008-06-11 04:44:56 +04:00
2009-07-31 12:46:02 +04:00
input_free_device ( input ) ;
2008-06-11 04:44:56 +04:00
2008-03-29 06:07:48 +03:00
fujitsu - > acpi_handle = NULL ;
2007-08-29 10:28:19 +04:00
return 0 ;
}
2008-06-11 04:44:56 +04:00
/* Brightness notify */
2009-04-07 19:37:16 +04:00
static void acpi_fujitsu_notify ( struct acpi_device * device , u32 event )
2008-06-11 04:44:56 +04:00
{
struct input_dev * input ;
int keycode ;
int oldb , newb ;
input = fujitsu - > input ;
switch ( event ) {
case ACPI_FUJITSU_NOTIFY_CODE1 :
keycode = 0 ;
oldb = fujitsu - > brightness_level ;
2009-01-07 13:11:24 +03:00
get_lcd_level ( ) ;
2008-06-11 04:44:56 +04:00
newb = fujitsu - > brightness_level ;
vdbg_printk ( FUJLAPTOP_DBG_TRACE ,
" brightness button event [%i -> %i (%i)] \n " ,
oldb , newb , fujitsu - > brightness_changed ) ;
2009-01-07 13:11:24 +03:00
if ( oldb < newb ) {
2008-06-11 04:44:56 +04:00
if ( disable_brightness_adjust ! = 1 ) {
if ( use_alt_lcd_levels )
set_lcd_level_alt ( newb ) ;
else
set_lcd_level ( newb ) ;
}
2009-01-07 13:11:24 +03:00
acpi_bus_generate_proc_event ( fujitsu - > dev ,
ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS , 0 ) ;
keycode = KEY_BRIGHTNESSUP ;
2008-06-11 04:44:56 +04:00
} else if ( oldb > newb ) {
if ( disable_brightness_adjust ! = 1 ) {
if ( use_alt_lcd_levels )
set_lcd_level_alt ( newb ) ;
else
set_lcd_level ( newb ) ;
}
2009-01-07 13:11:24 +03:00
acpi_bus_generate_proc_event ( fujitsu - > dev ,
ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS , 0 ) ;
keycode = KEY_BRIGHTNESSDOWN ;
2008-06-11 04:44:56 +04:00
}
break ;
default :
keycode = KEY_UNKNOWN ;
vdbg_printk ( FUJLAPTOP_DBG_WARN ,
" unsupported event [0x%x] \n " , event ) ;
break ;
}
if ( keycode ! = 0 ) {
input_report_key ( input , keycode , 1 ) ;
input_sync ( input ) ;
input_report_key ( input , keycode , 0 ) ;
input_sync ( input ) ;
}
}
/* ACPI device for hotkey handling */
static int acpi_fujitsu_hotkey_add ( struct acpi_device * device )
{
acpi_handle handle ;
int result = 0 ;
int state = 0 ;
struct input_dev * input ;
int error ;
int i ;
if ( ! device )
return - EINVAL ;
fujitsu_hotkey - > acpi_handle = device - > handle ;
sprintf ( acpi_device_name ( device ) , " %s " ,
ACPI_FUJITSU_HOTKEY_DEVICE_NAME ) ;
sprintf ( acpi_device_class ( device ) , " %s " , ACPI_FUJITSU_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = fujitsu_hotkey ;
2008-06-11 04:44:56 +04:00
/* kfifo */
spin_lock_init ( & fujitsu_hotkey - > fifo_lock ) ;
fujitsu_hotkey - > fifo =
kfifo_alloc ( RINGBUFFERSIZE * sizeof ( int ) , GFP_KERNEL ,
& fujitsu_hotkey - > fifo_lock ) ;
if ( IS_ERR ( fujitsu_hotkey - > fifo ) ) {
printk ( KERN_ERR " kfifo_alloc failed \n " ) ;
error = PTR_ERR ( fujitsu_hotkey - > fifo ) ;
goto err_stop ;
}
fujitsu_hotkey - > input = input = input_allocate_device ( ) ;
if ( ! input ) {
error = - ENOMEM ;
2009-04-07 19:37:22 +04:00
goto err_free_fifo ;
2008-06-11 04:44:56 +04:00
}
snprintf ( fujitsu_hotkey - > phys , sizeof ( fujitsu_hotkey - > phys ) ,
" %s/video/input0 " , acpi_device_hid ( device ) ) ;
input - > name = acpi_device_name ( device ) ;
input - > phys = fujitsu_hotkey - > phys ;
input - > id . bustype = BUS_HOST ;
input - > id . product = 0x06 ;
input - > dev . parent = & device - > dev ;
2008-12-31 21:19:59 +03:00
set_bit ( EV_KEY , input - > evbit ) ;
2008-10-09 08:14:40 +04:00
set_bit ( fujitsu - > keycode1 , input - > keybit ) ;
set_bit ( fujitsu - > keycode2 , input - > keybit ) ;
set_bit ( fujitsu - > keycode3 , input - > keybit ) ;
set_bit ( fujitsu - > keycode4 , input - > keybit ) ;
2008-06-11 04:44:56 +04:00
set_bit ( KEY_UNKNOWN , input - > keybit ) ;
error = input_register_device ( input ) ;
if ( error )
goto err_free_input_dev ;
result = acpi_bus_get_power ( fujitsu_hotkey - > acpi_handle , & state ) ;
if ( result ) {
printk ( KERN_ERR " Error reading power state \n " ) ;
2009-07-31 12:46:02 +04:00
goto err_unregister_input_dev ;
2008-06-11 04:44:56 +04:00
}
2009-07-29 00:45:54 +04:00
printk ( KERN_INFO " ACPI: %s [%s] (%s) \n " ,
2008-06-11 04:44:56 +04:00
acpi_device_name ( device ) , acpi_device_bid ( device ) ,
! device - > power . state ? " on " : " off " ) ;
fujitsu_hotkey - > dev = device ;
if ( ACPI_SUCCESS
( acpi_get_handle ( device - > handle , METHOD_NAME__INI , & handle ) ) ) {
vdbg_printk ( FUJLAPTOP_DBG_INFO , " Invoking _INI \n " ) ;
if ( ACPI_FAILURE
( acpi_evaluate_object
( device - > handle , METHOD_NAME__INI , NULL , NULL ) ) )
printk ( KERN_ERR " _INI Method failed \n " ) ;
}
2008-12-31 21:19:59 +03:00
i = 0 ;
while ( call_fext_func ( FUNC_BUTTONS , 0x1 , 0x0 , 0x0 ) ! = 0
& & ( i + + ) < MAX_HOTKEY_RINGBUFFER_SIZE )
; /* No action, result is discarded */
2008-06-11 04:44:56 +04:00
vdbg_printk ( FUJLAPTOP_DBG_INFO , " Discarded %i ringbuffer entries \n " , i ) ;
2009-02-02 14:11:10 +03:00
fujitsu_hotkey - > rfkill_supported =
call_fext_func ( FUNC_RFKILL , 0x0 , 0x0 , 0x0 ) ;
/* Make sure our bitmask of supported functions is cleared if the
RFKILL function block is not implemented , like on the S7020 . */
if ( fujitsu_hotkey - > rfkill_supported = = UNSUPPORTED_CMD )
fujitsu_hotkey - > rfkill_supported = 0 ;
if ( fujitsu_hotkey - > rfkill_supported )
fujitsu_hotkey - > rfkill_state =
call_fext_func ( FUNC_RFKILL , 0x4 , 0x0 , 0x0 ) ;
2008-12-31 21:19:59 +03:00
/* Suspect this is a keymap of the application panel, print it */
printk ( KERN_INFO " fujitsu-laptop: BTNI: [0x%x] \n " ,
call_fext_func ( FUNC_BUTTONS , 0x0 , 0x0 , 0x0 ) ) ;
2009-07-31 12:46:02 +04:00
# if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
2008-12-31 21:19:59 +03:00
if ( call_fext_func ( FUNC_LEDS , 0x0 , 0x0 , 0x0 ) & LOGOLAMP_POWERON ) {
result = led_classdev_register ( & fujitsu - > pf_device - > dev ,
& logolamp_led ) ;
if ( result = = 0 ) {
fujitsu_hotkey - > logolamp_registered = 1 ;
} else {
printk ( KERN_ERR " fujitsu-laptop: Could not register "
" LED handler for logo lamp, error %i \n " , result ) ;
}
}
if ( ( call_fext_func ( FUNC_LEDS , 0x0 , 0x0 , 0x0 ) & KEYBOARD_LAMPS ) & &
( call_fext_func ( FUNC_BUTTONS , 0x0 , 0x0 , 0x0 ) = = 0x0 ) ) {
result = led_classdev_register ( & fujitsu - > pf_device - > dev ,
& kblamps_led ) ;
if ( result = = 0 ) {
fujitsu_hotkey - > kblamps_registered = 1 ;
} else {
printk ( KERN_ERR " fujitsu-laptop: Could not register "
" LED handler for keyboard lamps, error %i \n " , result ) ;
}
}
2009-07-31 12:46:02 +04:00
# endif
2008-12-31 21:19:59 +03:00
2008-06-11 04:44:56 +04:00
return result ;
2009-07-31 12:46:02 +04:00
err_unregister_input_dev :
input_unregister_device ( input ) ;
2008-06-11 04:44:56 +04:00
err_free_input_dev :
input_free_device ( input ) ;
2009-04-07 19:37:22 +04:00
err_free_fifo :
2008-06-11 04:44:56 +04:00
kfifo_free ( fujitsu_hotkey - > fifo ) ;
err_stop :
return result ;
}
static int acpi_fujitsu_hotkey_remove ( struct acpi_device * device , int type )
{
2009-07-31 12:46:02 +04:00
struct fujitsu_hotkey_t * fujitsu_hotkey = acpi_driver_data ( device ) ;
struct input_dev * input = fujitsu_hotkey - > input ;
2008-06-11 04:44:56 +04:00
2009-10-08 07:49:12 +04:00
# if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
2009-07-31 12:46:02 +04:00
if ( fujitsu_hotkey - > logolamp_registered )
led_classdev_unregister ( & logolamp_led ) ;
2008-06-11 04:44:56 +04:00
2009-07-31 12:46:02 +04:00
if ( fujitsu_hotkey - > kblamps_registered )
led_classdev_unregister ( & kblamps_led ) ;
# endif
2008-06-11 04:44:56 +04:00
2009-07-31 12:46:02 +04:00
input_unregister_device ( input ) ;
2008-06-11 04:44:56 +04:00
2009-07-31 12:46:02 +04:00
input_free_device ( input ) ;
2008-06-11 04:44:56 +04:00
kfifo_free ( fujitsu_hotkey - > fifo ) ;
2009-07-31 12:46:02 +04:00
fujitsu_hotkey - > acpi_handle = NULL ;
2008-06-11 04:44:56 +04:00
return 0 ;
}
2009-04-07 19:37:22 +04:00
static void acpi_fujitsu_hotkey_notify ( struct acpi_device * device , u32 event )
2008-06-11 04:44:56 +04:00
{
struct input_dev * input ;
int keycode , keycode_r ;
unsigned int irb = 1 ;
int i , status ;
input = fujitsu_hotkey - > input ;
2009-02-02 14:11:10 +03:00
if ( fujitsu_hotkey - > rfkill_supported )
fujitsu_hotkey - > rfkill_state =
call_fext_func ( FUNC_RFKILL , 0x4 , 0x0 , 0x0 ) ;
2008-06-11 04:44:56 +04:00
switch ( event ) {
case ACPI_FUJITSU_NOTIFY_CODE1 :
i = 0 ;
2008-12-31 21:19:59 +03:00
while ( ( irb =
call_fext_func ( FUNC_BUTTONS , 0x1 , 0x0 , 0x0 ) ) ! = 0
& & ( i + + ) < MAX_HOTKEY_RINGBUFFER_SIZE ) {
2008-06-11 04:44:56 +04:00
switch ( irb & 0x4ff ) {
2008-10-09 08:14:40 +04:00
case KEY1_CODE :
keycode = fujitsu - > keycode1 ;
2008-06-11 04:44:56 +04:00
break ;
2008-10-09 08:14:40 +04:00
case KEY2_CODE :
keycode = fujitsu - > keycode2 ;
2008-06-11 04:44:56 +04:00
break ;
2008-10-09 08:14:40 +04:00
case KEY3_CODE :
keycode = fujitsu - > keycode3 ;
2008-06-11 04:44:56 +04:00
break ;
2008-10-09 08:14:40 +04:00
case KEY4_CODE :
keycode = fujitsu - > keycode4 ;
2008-06-11 04:44:56 +04:00
break ;
case 0 :
keycode = 0 ;
break ;
default :
vdbg_printk ( FUJLAPTOP_DBG_WARN ,
2008-10-09 08:14:40 +04:00
" Unknown GIRB result [%x] \n " , irb ) ;
2008-06-11 04:44:56 +04:00
keycode = - 1 ;
break ;
}
if ( keycode > 0 ) {
vdbg_printk ( FUJLAPTOP_DBG_TRACE ,
" Push keycode into ringbuffer [%d] \n " ,
keycode ) ;
status = kfifo_put ( fujitsu_hotkey - > fifo ,
2008-10-09 08:14:40 +04:00
( unsigned char * ) & keycode ,
sizeof ( keycode ) ) ;
2008-06-11 04:44:56 +04:00
if ( status ! = sizeof ( keycode ) ) {
vdbg_printk ( FUJLAPTOP_DBG_WARN ,
2008-10-09 08:14:40 +04:00
" Could not push keycode [0x%x] \n " ,
keycode ) ;
2008-06-11 04:44:56 +04:00
} else {
input_report_key ( input , keycode , 1 ) ;
input_sync ( input ) ;
}
} else if ( keycode = = 0 ) {
while ( ( status =
kfifo_get
( fujitsu_hotkey - > fifo , ( unsigned char * )
& keycode_r ,
sizeof
( keycode_r ) ) ) = = sizeof ( keycode_r ) ) {
input_report_key ( input , keycode_r , 0 ) ;
input_sync ( input ) ;
vdbg_printk ( FUJLAPTOP_DBG_TRACE ,
2008-10-09 08:14:40 +04:00
" Pop keycode from ringbuffer [%d] \n " ,
keycode_r ) ;
2008-06-11 04:44:56 +04:00
}
}
}
break ;
default :
keycode = KEY_UNKNOWN ;
vdbg_printk ( FUJLAPTOP_DBG_WARN ,
" Unsupported event [0x%x] \n " , event ) ;
input_report_key ( input , keycode , 1 ) ;
input_sync ( input ) ;
input_report_key ( input , keycode , 0 ) ;
input_sync ( input ) ;
break ;
}
}
/* Initialization */
2007-08-29 10:28:19 +04:00
static const struct acpi_device_id fujitsu_device_ids [ ] = {
{ ACPI_FUJITSU_HID , 0 } ,
{ " " , 0 } ,
} ;
static struct acpi_driver acpi_fujitsu_driver = {
. name = ACPI_FUJITSU_DRIVER_NAME ,
. class = ACPI_FUJITSU_CLASS ,
. ids = fujitsu_device_ids ,
. ops = {
. add = acpi_fujitsu_add ,
. remove = acpi_fujitsu_remove ,
2009-04-07 19:37:16 +04:00
. notify = acpi_fujitsu_notify ,
2007-08-29 10:28:19 +04:00
} ,
} ;
2008-06-11 04:44:56 +04:00
static const struct acpi_device_id fujitsu_hotkey_device_ids [ ] = {
{ ACPI_FUJITSU_HOTKEY_HID , 0 } ,
{ " " , 0 } ,
} ;
static struct acpi_driver acpi_fujitsu_hotkey_driver = {
. name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME ,
. class = ACPI_FUJITSU_CLASS ,
. ids = fujitsu_hotkey_device_ids ,
. ops = {
. add = acpi_fujitsu_hotkey_add ,
. remove = acpi_fujitsu_hotkey_remove ,
2009-04-07 19:37:22 +04:00
. notify = acpi_fujitsu_hotkey_notify ,
2008-06-11 04:44:56 +04:00
} ,
} ;
2007-08-29 10:28:19 +04:00
static int __init fujitsu_init ( void )
{
2008-06-11 04:44:56 +04:00
int ret , result , max_brightness ;
2007-08-29 10:28:19 +04:00
if ( acpi_disabled )
return - ENODEV ;
fujitsu = kmalloc ( sizeof ( struct fujitsu_t ) , GFP_KERNEL ) ;
if ( ! fujitsu )
return - ENOMEM ;
memset ( fujitsu , 0 , sizeof ( struct fujitsu_t ) ) ;
2008-10-09 08:14:40 +04:00
fujitsu - > keycode1 = KEY_PROG1 ;
fujitsu - > keycode2 = KEY_PROG2 ;
fujitsu - > keycode3 = KEY_PROG3 ;
fujitsu - > keycode4 = KEY_PROG4 ;
dmi_check_system ( fujitsu_dmi_table ) ;
2007-08-29 10:28:19 +04:00
result = acpi_bus_register_driver ( & acpi_fujitsu_driver ) ;
if ( result < 0 ) {
ret = - ENODEV ;
goto fail_acpi ;
}
/* Register platform stuff */
fujitsu - > pf_device = platform_device_alloc ( " fujitsu-laptop " , - 1 ) ;
if ( ! fujitsu - > pf_device ) {
ret = - ENOMEM ;
goto fail_platform_driver ;
}
ret = platform_device_add ( fujitsu - > pf_device ) ;
if ( ret )
goto fail_platform_device1 ;
ret =
sysfs_create_group ( & fujitsu - > pf_device - > dev . kobj ,
& fujitsupf_attribute_group ) ;
if ( ret )
goto fail_platform_device2 ;
2008-06-11 04:44:56 +04:00
/* Register backlight stuff */
2008-08-01 19:38:00 +04:00
if ( ! acpi_video_backlight_support ( ) ) {
fujitsu - > bl_device =
backlight_device_register ( " fujitsu-laptop " , NULL , NULL ,
& fujitsubl_ops ) ;
2009-07-31 12:46:02 +04:00
if ( IS_ERR ( fujitsu - > bl_device ) ) {
ret = PTR_ERR ( fujitsu - > bl_device ) ;
fujitsu - > bl_device = NULL ;
goto fail_sysfs_group ;
}
2008-08-01 19:38:00 +04:00
max_brightness = fujitsu - > max_brightness ;
fujitsu - > bl_device - > props . max_brightness = max_brightness - 1 ;
fujitsu - > bl_device - > props . brightness = fujitsu - > brightness_level ;
}
2008-06-11 04:44:56 +04:00
ret = platform_driver_register ( & fujitsupf_driver ) ;
if ( ret )
goto fail_backlight ;
/* Register hotkey driver */
fujitsu_hotkey = kmalloc ( sizeof ( struct fujitsu_hotkey_t ) , GFP_KERNEL ) ;
if ( ! fujitsu_hotkey ) {
ret = - ENOMEM ;
goto fail_hotkey ;
}
memset ( fujitsu_hotkey , 0 , sizeof ( struct fujitsu_hotkey_t ) ) ;
result = acpi_bus_register_driver ( & acpi_fujitsu_hotkey_driver ) ;
if ( result < 0 ) {
ret = - ENODEV ;
goto fail_hotkey1 ;
}
2008-12-31 21:19:59 +03:00
/* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
if ( ! acpi_video_backlight_support ( ) ) {
if ( call_fext_func ( FUNC_BACKLIGHT , 0x2 , 0x4 , 0x0 ) = = 3 )
fujitsu - > bl_device - > props . power = 4 ;
else
fujitsu - > bl_device - > props . power = 0 ;
}
2007-08-29 10:28:19 +04:00
printk ( KERN_INFO " fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
" successfully loaded. \n " ) ;
return 0 ;
2008-06-11 04:44:56 +04:00
fail_hotkey1 :
kfree ( fujitsu_hotkey ) ;
fail_hotkey :
2007-08-29 10:28:19 +04:00
platform_driver_unregister ( & fujitsupf_driver ) ;
2008-06-11 04:44:56 +04:00
fail_backlight :
2008-08-01 19:38:00 +04:00
if ( fujitsu - > bl_device )
backlight_device_unregister ( fujitsu - > bl_device ) ;
2009-07-31 12:46:02 +04:00
fail_sysfs_group :
sysfs_remove_group ( & fujitsu - > pf_device - > dev . kobj ,
& fujitsupf_attribute_group ) ;
2008-06-11 04:44:56 +04:00
fail_platform_device2 :
platform_device_del ( fujitsu - > pf_device ) ;
fail_platform_device1 :
platform_device_put ( fujitsu - > pf_device ) ;
fail_platform_driver :
acpi_bus_unregister_driver ( & acpi_fujitsu_driver ) ;
fail_acpi :
2007-08-29 10:28:19 +04:00
kfree ( fujitsu ) ;
return ret ;
}
static void __exit fujitsu_cleanup ( void )
{
2009-07-31 12:46:02 +04:00
acpi_bus_unregister_driver ( & acpi_fujitsu_hotkey_driver ) ;
2008-12-31 21:19:59 +03:00
2009-07-31 12:46:02 +04:00
kfree ( fujitsu_hotkey ) ;
2008-12-31 21:19:59 +03:00
2007-08-29 10:28:19 +04:00
platform_driver_unregister ( & fujitsupf_driver ) ;
2009-07-31 12:46:02 +04:00
2008-08-01 19:38:00 +04:00
if ( fujitsu - > bl_device )
backlight_device_unregister ( fujitsu - > bl_device ) ;
2007-08-29 10:28:19 +04:00
2009-07-31 12:46:02 +04:00
sysfs_remove_group ( & fujitsu - > pf_device - > dev . kobj ,
& fujitsupf_attribute_group ) ;
2007-08-29 10:28:19 +04:00
2009-07-31 12:46:02 +04:00
platform_device_unregister ( fujitsu - > pf_device ) ;
2007-08-29 10:28:19 +04:00
2009-07-31 12:46:02 +04:00
acpi_bus_unregister_driver ( & acpi_fujitsu_driver ) ;
2008-06-11 04:44:56 +04:00
2009-07-31 12:46:02 +04:00
kfree ( fujitsu ) ;
2008-06-11 04:44:56 +04:00
2007-08-29 10:28:19 +04:00
printk ( KERN_INFO " fujitsu-laptop: driver unloaded. \n " ) ;
}
module_init ( fujitsu_init ) ;
module_exit ( fujitsu_cleanup ) ;
2008-06-11 04:44:56 +04:00
module_param ( use_alt_lcd_levels , uint , 0644 ) ;
MODULE_PARM_DESC ( use_alt_lcd_levels ,
" Use alternative interface for lcd_levels (needed for Lifebook s6410). " ) ;
module_param ( disable_brightness_adjust , uint , 0644 ) ;
MODULE_PARM_DESC ( disable_brightness_adjust , " Disable brightness adjustment . " ) ;
# ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
module_param_named ( debug , dbg_level , uint , 0644 ) ;
MODULE_PARM_DESC ( debug , " Sets debug level bit-mask " ) ;
# endif
2008-12-31 21:19:59 +03:00
MODULE_AUTHOR ( " Jonathan Woithe, Peter Gruber, Tony Vroon " ) ;
2007-08-29 10:28:19 +04:00
MODULE_DESCRIPTION ( " Fujitsu laptop extras support " ) ;
MODULE_VERSION ( FUJITSU_DRIVER_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-06-06 09:46:08 +04:00
2008-10-09 08:14:40 +04:00
MODULE_ALIAS ( " dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:* " ) ;
2008-12-31 21:19:59 +03:00
MODULE_ALIAS ( " dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:* " ) ;
2008-10-09 08:14:40 +04:00
MODULE_ALIAS ( " dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:* " ) ;
2008-06-11 04:44:56 +04:00
2008-06-06 09:46:08 +04:00
static struct pnp_device_id pnp_ids [ ] = {
2008-10-09 08:14:40 +04:00
{ . id = " FUJ02bf " } ,
{ . id = " FUJ02B1 " } ,
{ . id = " FUJ02E3 " } ,
{ . id = " " }
2008-06-06 09:46:08 +04:00
} ;
2008-10-09 08:14:40 +04:00
2008-06-06 09:46:08 +04:00
MODULE_DEVICE_TABLE ( pnp , pnp_ids ) ;