2007-01-14 01:04:31 +03:00
/*
2007-04-09 21:26:03 +04:00
* ACPI Sony Notebook Control Driver ( SNC and SPIC )
2007-01-14 01:04:31 +03:00
*
* Copyright ( C ) 2004 - 2005 Stelian Pop < stelian @ popies . net >
2007-02-07 22:01:54 +03:00
* Copyright ( C ) 2007 Mattia Dongili < malattia @ linux . it >
2007-01-14 01:04:31 +03:00
*
* Parts of this driver inspired from asus_acpi . c and ibm_acpi . c
* which are copyrighted by their respective authors .
*
2007-04-09 21:26:03 +04:00
* The SNY6001 driver part is based on the sonypi driver which includes
* material from :
*
* Copyright ( C ) 2001 - 2005 Stelian Pop < stelian @ popies . net >
*
* Copyright ( C ) 2005 Narayanan R S < nars @ kadamba . org >
*
2007-10-20 01:22:11 +04:00
* Copyright ( C ) 2001 - 2002 Alcôve < www . alcove . com >
2007-04-09 21:26:03 +04:00
*
* Copyright ( C ) 2001 Michael Ashley < m . ashley @ unsw . edu . au >
*
* Copyright ( C ) 2001 Junichi Morita < jun1m @ mars . dti . ne . jp >
*
* Copyright ( C ) 2000 Takaya Kinjo < t - kinjo @ tc4 . so - net . ne . jp >
*
* Copyright ( C ) 2000 Andrew Tridgell < tridge @ valinux . com >
*
* Earlier work by Werner Almesberger , Paul ` Rusty ' Russell and Paul Mackerras .
*
2007-01-14 01:04:31 +03: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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
2008-05-20 21:16:45 +04:00
# include <linux/smp_lock.h>
2007-01-14 01:04:31 +03:00
# include <linux/types.h>
2007-01-14 01:04:34 +03:00
# include <linux/backlight.h>
2007-02-07 22:01:54 +03:00
# include <linux/platform_device.h>
2007-01-14 01:04:34 +03:00
# include <linux/err.h>
2007-04-09 21:26:03 +04:00
# include <linux/dmi.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/input.h>
# include <linux/kfifo.h>
# include <linux/workqueue.h>
# include <linux/acpi.h>
2007-01-14 01:04:31 +03:00
# include <acpi/acpi_drivers.h>
# include <acpi/acpi_bus.h>
# include <asm/uaccess.h>
2007-04-09 21:26:03 +04:00
# include <linux/sonypi.h>
2007-04-28 18:34:36 +04:00
# include <linux/sony-laptop.h>
2007-05-01 06:19:53 +04:00
# ifdef CONFIG_SONYPI_COMPAT
2007-04-09 21:31:25 +04:00
# include <linux/poll.h>
# include <linux/miscdevice.h>
# endif
2007-01-14 01:04:31 +03:00
2007-04-09 21:26:03 +04:00
# define DRV_PFX "sony-laptop: "
2007-04-09 12:19:06 +04:00
# define dprintk(msg...) do { \
2007-04-09 21:31:06 +04:00
if ( debug ) printk ( KERN_WARNING DRV_PFX msg ) ; \
2007-04-09 12:19:06 +04:00
} while ( 0 )
2008-01-14 12:05:44 +03:00
# define SONY_LAPTOP_DRIVER_VERSION "0.6"
2007-04-09 21:26:03 +04:00
# define SONY_NC_CLASS "sony-nc"
2007-04-09 12:19:04 +04:00
# define SONY_NC_HID "SNY5001"
2007-04-09 21:31:06 +04:00
# define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
2007-01-14 01:04:34 +03:00
2007-04-09 21:26:03 +04:00
# define SONY_PIC_CLASS "sony-pic"
# define SONY_PIC_HID "SNY6001"
2007-04-09 21:31:06 +04:00
# define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver"
2007-01-14 01:04:31 +03:00
2007-02-07 22:01:54 +03:00
MODULE_AUTHOR ( " Stelian Pop, Mattia Dongili " ) ;
2007-04-09 21:26:03 +04:00
MODULE_DESCRIPTION ( " Sony laptop extras driver (SPIC and SNC ACPI device) " ) ;
2007-01-14 01:04:31 +03:00
MODULE_LICENSE ( " GPL " ) ;
2007-04-09 21:26:03 +04:00
MODULE_VERSION ( SONY_LAPTOP_DRIVER_VERSION ) ;
2007-01-14 01:04:31 +03:00
static int debug ;
module_param ( debug , int , 0 ) ;
MODULE_PARM_DESC ( debug , " set this to 1 (and RTFM) if you want to help "
2007-02-07 23:34:02 +03:00
" the development of this driver " ) ;
2007-01-14 01:04:31 +03:00
2007-04-09 21:26:03 +04:00
static int no_spic ; /* = 0 */
module_param ( no_spic , int , 0444 ) ;
MODULE_PARM_DESC ( no_spic ,
" set this if you don't want to enable the SPIC device " ) ;
static int compat ; /* = 0 */
module_param ( compat , int , 0444 ) ;
MODULE_PARM_DESC ( compat ,
2007-04-09 21:31:25 +04:00
" set this if you want to enable backward compatibility mode " ) ;
2007-04-09 21:26:03 +04:00
static unsigned long mask = 0xffffffff ;
module_param ( mask , ulong , 0644 ) ;
MODULE_PARM_DESC ( mask ,
" set this to the mask of event you want to enable (see doc) " ) ;
2007-04-28 18:18:45 +04:00
static int camera ; /* = 0 */
module_param ( camera , int , 0444 ) ;
MODULE_PARM_DESC ( camera ,
" set this to 1 to enable Motion Eye camera controls "
" (only use it if you have a C1VE or C1VN model) " ) ;
2007-05-01 06:19:53 +04:00
# ifdef CONFIG_SONYPI_COMPAT
2007-04-09 21:31:25 +04:00
static int minor = - 1 ;
module_param ( minor , int , 0 ) ;
MODULE_PARM_DESC ( minor ,
" minor number of the misc device for the SPIC compatibility code, "
" default is -1 (automatic) " ) ;
# endif
2007-04-09 12:19:08 +04:00
/*********** Input Devices ***********/
# define SONY_LAPTOP_BUF_SIZE 128
struct sony_laptop_input_s {
atomic_t users ;
struct input_dev * jog_dev ;
struct input_dev * key_dev ;
struct kfifo * fifo ;
spinlock_t fifo_lock ;
struct workqueue_struct * wq ;
} ;
static struct sony_laptop_input_s sony_laptop_input = {
. users = ATOMIC_INIT ( 0 ) ,
} ;
struct sony_laptop_keypress {
struct input_dev * dev ;
int key ;
} ;
2007-07-19 21:01:57 +04:00
/* Correspondance table between sonypi events
* and input layer indexes in the keymap
*/
static int sony_laptop_input_index [ ] = {
2008-01-14 12:05:45 +03:00
- 1 , /* 0 no event */
- 1 , /* 1 SONYPI_EVENT_JOGDIAL_DOWN */
- 1 , /* 2 SONYPI_EVENT_JOGDIAL_UP */
- 1 , /* 3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
- 1 , /* 4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */
- 1 , /* 5 SONYPI_EVENT_JOGDIAL_PRESSED */
- 1 , /* 6 SONYPI_EVENT_JOGDIAL_RELEASED */
0 , /* 7 SONYPI_EVENT_CAPTURE_PRESSED */
1 , /* 8 SONYPI_EVENT_CAPTURE_RELEASED */
2 , /* 9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
3 , /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
4 , /* 11 SONYPI_EVENT_FNKEY_ESC */
5 , /* 12 SONYPI_EVENT_FNKEY_F1 */
6 , /* 13 SONYPI_EVENT_FNKEY_F2 */
7 , /* 14 SONYPI_EVENT_FNKEY_F3 */
8 , /* 15 SONYPI_EVENT_FNKEY_F4 */
9 , /* 16 SONYPI_EVENT_FNKEY_F5 */
10 , /* 17 SONYPI_EVENT_FNKEY_F6 */
11 , /* 18 SONYPI_EVENT_FNKEY_F7 */
12 , /* 19 SONYPI_EVENT_FNKEY_F8 */
13 , /* 20 SONYPI_EVENT_FNKEY_F9 */
14 , /* 21 SONYPI_EVENT_FNKEY_F10 */
15 , /* 22 SONYPI_EVENT_FNKEY_F11 */
16 , /* 23 SONYPI_EVENT_FNKEY_F12 */
17 , /* 24 SONYPI_EVENT_FNKEY_1 */
18 , /* 25 SONYPI_EVENT_FNKEY_2 */
19 , /* 26 SONYPI_EVENT_FNKEY_D */
20 , /* 27 SONYPI_EVENT_FNKEY_E */
21 , /* 28 SONYPI_EVENT_FNKEY_F */
22 , /* 29 SONYPI_EVENT_FNKEY_S */
23 , /* 30 SONYPI_EVENT_FNKEY_B */
24 , /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */
25 , /* 32 SONYPI_EVENT_PKEY_P1 */
26 , /* 33 SONYPI_EVENT_PKEY_P2 */
27 , /* 34 SONYPI_EVENT_PKEY_P3 */
28 , /* 35 SONYPI_EVENT_BACK_PRESSED */
- 1 , /* 36 SONYPI_EVENT_LID_CLOSED */
- 1 , /* 37 SONYPI_EVENT_LID_OPENED */
29 , /* 38 SONYPI_EVENT_BLUETOOTH_ON */
30 , /* 39 SONYPI_EVENT_BLUETOOTH_OFF */
31 , /* 40 SONYPI_EVENT_HELP_PRESSED */
32 , /* 41 SONYPI_EVENT_FNKEY_ONLY */
33 , /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
34 , /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */
35 , /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
36 , /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
37 , /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
38 , /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */
39 , /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
40 , /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
41 , /* 50 SONYPI_EVENT_ZOOM_PRESSED */
42 , /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */
43 , /* 52 SONYPI_EVENT_MEYE_FACE */
44 , /* 53 SONYPI_EVENT_MEYE_OPPOSITE */
45 , /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */
46 , /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */
- 1 , /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */
- 1 , /* 57 SONYPI_EVENT_BATTERY_INSERT */
- 1 , /* 58 SONYPI_EVENT_BATTERY_REMOVE */
- 1 , /* 59 SONYPI_EVENT_FNKEY_RELEASED */
47 , /* 60 SONYPI_EVENT_WIRELESS_ON */
48 , /* 61 SONYPI_EVENT_WIRELESS_OFF */
49 , /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
50 , /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
2007-07-19 21:01:57 +04:00
} ;
static int sony_laptop_input_keycode_map [ ] = {
KEY_CAMERA , /* 0 SONYPI_EVENT_CAPTURE_PRESSED */
KEY_RESERVED , /* 1 SONYPI_EVENT_CAPTURE_RELEASED */
KEY_RESERVED , /* 2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
KEY_RESERVED , /* 3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
KEY_FN_ESC , /* 4 SONYPI_EVENT_FNKEY_ESC */
KEY_FN_F1 , /* 5 SONYPI_EVENT_FNKEY_F1 */
KEY_FN_F2 , /* 6 SONYPI_EVENT_FNKEY_F2 */
KEY_FN_F3 , /* 7 SONYPI_EVENT_FNKEY_F3 */
KEY_FN_F4 , /* 8 SONYPI_EVENT_FNKEY_F4 */
KEY_FN_F5 , /* 9 SONYPI_EVENT_FNKEY_F5 */
KEY_FN_F6 , /* 10 SONYPI_EVENT_FNKEY_F6 */
KEY_FN_F7 , /* 11 SONYPI_EVENT_FNKEY_F7 */
KEY_FN_F8 , /* 12 SONYPI_EVENT_FNKEY_F8 */
KEY_FN_F9 , /* 13 SONYPI_EVENT_FNKEY_F9 */
KEY_FN_F10 , /* 14 SONYPI_EVENT_FNKEY_F10 */
KEY_FN_F11 , /* 15 SONYPI_EVENT_FNKEY_F11 */
KEY_FN_F12 , /* 16 SONYPI_EVENT_FNKEY_F12 */
KEY_FN_F1 , /* 17 SONYPI_EVENT_FNKEY_1 */
KEY_FN_F2 , /* 18 SONYPI_EVENT_FNKEY_2 */
KEY_FN_D , /* 19 SONYPI_EVENT_FNKEY_D */
KEY_FN_E , /* 20 SONYPI_EVENT_FNKEY_E */
KEY_FN_F , /* 21 SONYPI_EVENT_FNKEY_F */
KEY_FN_S , /* 22 SONYPI_EVENT_FNKEY_S */
KEY_FN_B , /* 23 SONYPI_EVENT_FNKEY_B */
KEY_BLUETOOTH , /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */
KEY_PROG1 , /* 25 SONYPI_EVENT_PKEY_P1 */
KEY_PROG2 , /* 26 SONYPI_EVENT_PKEY_P2 */
KEY_PROG3 , /* 27 SONYPI_EVENT_PKEY_P3 */
KEY_BACK , /* 28 SONYPI_EVENT_BACK_PRESSED */
KEY_BLUETOOTH , /* 29 SONYPI_EVENT_BLUETOOTH_ON */
KEY_BLUETOOTH , /* 30 SONYPI_EVENT_BLUETOOTH_OFF */
KEY_HELP , /* 31 SONYPI_EVENT_HELP_PRESSED */
KEY_FN , /* 32 SONYPI_EVENT_FNKEY_ONLY */
KEY_RESERVED , /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
KEY_RESERVED , /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */
KEY_RESERVED , /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
KEY_RESERVED , /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
KEY_RESERVED , /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
KEY_RESERVED , /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */
KEY_RESERVED , /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
KEY_RESERVED , /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
KEY_ZOOM , /* 41 SONYPI_EVENT_ZOOM_PRESSED */
BTN_THUMB , /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */
KEY_RESERVED , /* 43 SONYPI_EVENT_MEYE_FACE */
KEY_RESERVED , /* 44 SONYPI_EVENT_MEYE_OPPOSITE */
KEY_RESERVED , /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */
KEY_RESERVED , /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
KEY_WLAN , /* 47 SONYPI_EVENT_WIRELESS_ON */
KEY_WLAN , /* 48 SONYPI_EVENT_WIRELESS_OFF */
2008-01-14 12:05:45 +03:00
KEY_ZOOMIN , /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
2007-04-09 12:19:08 +04:00
} ;
/* release buttons after a short delay if pressed */
static void do_sony_laptop_release_key ( struct work_struct * work )
{
struct sony_laptop_keypress kp ;
while ( kfifo_get ( sony_laptop_input . fifo , ( unsigned char * ) & kp ,
sizeof ( kp ) ) = = sizeof ( kp ) ) {
msleep ( 10 ) ;
input_report_key ( kp . dev , kp . key , 0 ) ;
input_sync ( kp . dev ) ;
}
}
static DECLARE_WORK ( sony_laptop_release_key_work ,
do_sony_laptop_release_key ) ;
2007-10-20 01:10:43 +04:00
/* forward event to the input subsystem */
2007-04-09 12:19:08 +04:00
static void sony_laptop_report_input_event ( u8 event )
{
struct input_dev * jog_dev = sony_laptop_input . jog_dev ;
struct input_dev * key_dev = sony_laptop_input . key_dev ;
struct sony_laptop_keypress kp = { NULL } ;
if ( event = = SONYPI_EVENT_FNKEY_RELEASED ) {
/* Nothing, not all VAIOs generate this event */
return ;
}
/* report events */
switch ( event ) {
/* jog_dev events */
case SONYPI_EVENT_JOGDIAL_UP :
case SONYPI_EVENT_JOGDIAL_UP_PRESSED :
input_report_rel ( jog_dev , REL_WHEEL , 1 ) ;
input_sync ( jog_dev ) ;
return ;
case SONYPI_EVENT_JOGDIAL_DOWN :
case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED :
input_report_rel ( jog_dev , REL_WHEEL , - 1 ) ;
input_sync ( jog_dev ) ;
return ;
/* key_dev events */
case SONYPI_EVENT_JOGDIAL_PRESSED :
kp . key = BTN_MIDDLE ;
kp . dev = jog_dev ;
break ;
default :
2008-02-20 01:59:03 +03:00
if ( event > = ARRAY_SIZE ( sony_laptop_input_index ) ) {
2007-07-19 21:01:57 +04:00
dprintk ( " sony_laptop_report_input_event, event not known: %d \n " , event ) ;
break ;
}
if ( sony_laptop_input_index [ event ] ! = - 1 ) {
kp . key = sony_laptop_input_keycode_map [ sony_laptop_input_index [ event ] ] ;
if ( kp . key ! = KEY_UNKNOWN )
2007-04-09 12:19:08 +04:00
kp . dev = key_dev ;
2007-07-19 21:01:57 +04:00
}
2007-04-09 12:19:08 +04:00
break ;
}
if ( kp . dev ) {
input_report_key ( kp . dev , kp . key , 1 ) ;
2007-07-19 21:01:57 +04:00
/* we emit the scancode so we can always remap the key */
input_event ( kp . dev , EV_MSC , MSC_SCAN , event ) ;
2007-04-09 12:19:08 +04:00
input_sync ( kp . dev ) ;
kfifo_put ( sony_laptop_input . fifo ,
( unsigned char * ) & kp , sizeof ( kp ) ) ;
if ( ! work_pending ( & sony_laptop_release_key_work ) )
queue_work ( sony_laptop_input . wq ,
& sony_laptop_release_key_work ) ;
} else
dprintk ( " unknown input event %.2x \n " , event ) ;
}
2007-11-21 22:15:53 +03:00
static int sony_laptop_setup_input ( struct acpi_device * acpi_device )
2007-04-09 12:19:08 +04:00
{
struct input_dev * jog_dev ;
struct input_dev * key_dev ;
int i ;
int error ;
/* don't run again if already initialized */
if ( atomic_add_return ( 1 , & sony_laptop_input . users ) > 1 )
return 0 ;
/* kfifo */
spin_lock_init ( & sony_laptop_input . fifo_lock ) ;
sony_laptop_input . fifo =
kfifo_alloc ( SONY_LAPTOP_BUF_SIZE , GFP_KERNEL ,
& sony_laptop_input . fifo_lock ) ;
if ( IS_ERR ( sony_laptop_input . fifo ) ) {
printk ( KERN_ERR DRV_PFX " kfifo_alloc failed \n " ) ;
error = PTR_ERR ( sony_laptop_input . fifo ) ;
goto err_dec_users ;
}
/* init workqueue */
sony_laptop_input . wq = create_singlethread_workqueue ( " sony-laptop " ) ;
if ( ! sony_laptop_input . wq ) {
printk ( KERN_ERR DRV_PFX
" Unabe to create workqueue. \n " ) ;
error = - ENXIO ;
goto err_free_kfifo ;
}
/* input keys */
key_dev = input_allocate_device ( ) ;
if ( ! key_dev ) {
error = - ENOMEM ;
goto err_destroy_wq ;
}
key_dev - > name = " Sony Vaio Keys " ;
key_dev - > id . bustype = BUS_ISA ;
key_dev - > id . vendor = PCI_VENDOR_ID_SONY ;
2007-11-21 22:15:53 +03:00
key_dev - > dev . parent = & acpi_device - > dev ;
2007-04-09 12:19:08 +04:00
/* Initialize the Input Drivers: special keys */
2007-07-19 21:01:57 +04:00
set_bit ( EV_KEY , key_dev - > evbit ) ;
set_bit ( EV_MSC , key_dev - > evbit ) ;
set_bit ( MSC_SCAN , key_dev - > mscbit ) ;
key_dev - > keycodesize = sizeof ( sony_laptop_input_keycode_map [ 0 ] ) ;
key_dev - > keycodemax = ARRAY_SIZE ( sony_laptop_input_keycode_map ) ;
key_dev - > keycode = & sony_laptop_input_keycode_map ;
for ( i = 0 ; i < ARRAY_SIZE ( sony_laptop_input_keycode_map ) ; i + + ) {
if ( sony_laptop_input_keycode_map [ i ] ! = KEY_RESERVED ) {
set_bit ( sony_laptop_input_keycode_map [ i ] ,
key_dev - > keybit ) ;
}
}
2007-04-09 12:19:08 +04:00
error = input_register_device ( key_dev ) ;
if ( error )
goto err_free_keydev ;
sony_laptop_input . key_dev = key_dev ;
/* jogdial */
jog_dev = input_allocate_device ( ) ;
if ( ! jog_dev ) {
error = - ENOMEM ;
goto err_unregister_keydev ;
}
jog_dev - > name = " Sony Vaio Jogdial " ;
jog_dev - > id . bustype = BUS_ISA ;
jog_dev - > id . vendor = PCI_VENDOR_ID_SONY ;
2007-11-21 22:15:53 +03:00
key_dev - > dev . parent = & acpi_device - > dev ;
2007-04-09 12:19:08 +04:00
2007-10-19 10:40:32 +04:00
jog_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REL ) ;
jog_dev - > keybit [ BIT_WORD ( BTN_MOUSE ) ] = BIT_MASK ( BTN_MIDDLE ) ;
jog_dev - > relbit [ 0 ] = BIT_MASK ( REL_WHEEL ) ;
2007-04-09 12:19:08 +04:00
error = input_register_device ( jog_dev ) ;
if ( error )
goto err_free_jogdev ;
sony_laptop_input . jog_dev = jog_dev ;
return 0 ;
err_free_jogdev :
input_free_device ( jog_dev ) ;
err_unregister_keydev :
input_unregister_device ( key_dev ) ;
/* to avoid kref underflow below at input_free_device */
key_dev = NULL ;
err_free_keydev :
input_free_device ( key_dev ) ;
err_destroy_wq :
destroy_workqueue ( sony_laptop_input . wq ) ;
err_free_kfifo :
kfifo_free ( sony_laptop_input . fifo ) ;
err_dec_users :
atomic_dec ( & sony_laptop_input . users ) ;
return error ;
}
static void sony_laptop_remove_input ( void )
{
/* cleanup only after the last user has gone */
if ( ! atomic_dec_and_test ( & sony_laptop_input . users ) )
return ;
/* flush workqueue first */
flush_workqueue ( sony_laptop_input . wq ) ;
/* destroy input devs */
input_unregister_device ( sony_laptop_input . key_dev ) ;
sony_laptop_input . key_dev = NULL ;
if ( sony_laptop_input . jog_dev ) {
input_unregister_device ( sony_laptop_input . jog_dev ) ;
sony_laptop_input . jog_dev = NULL ;
}
destroy_workqueue ( sony_laptop_input . wq ) ;
kfifo_free ( sony_laptop_input . fifo ) ;
}
2007-04-09 12:19:05 +04:00
/*********** Platform Device ***********/
static atomic_t sony_pf_users = ATOMIC_INIT ( 0 ) ;
static struct platform_driver sony_pf_driver = {
. driver = {
. name = " sony-laptop " ,
. owner = THIS_MODULE ,
}
} ;
static struct platform_device * sony_pf_device ;
static int sony_pf_add ( void )
{
int ret = 0 ;
/* don't run again if already initialized */
if ( atomic_add_return ( 1 , & sony_pf_users ) > 1 )
return 0 ;
ret = platform_driver_register ( & sony_pf_driver ) ;
if ( ret )
goto out ;
sony_pf_device = platform_device_alloc ( " sony-laptop " , - 1 ) ;
if ( ! sony_pf_device ) {
ret = - ENOMEM ;
goto out_platform_registered ;
}
ret = platform_device_add ( sony_pf_device ) ;
if ( ret )
goto out_platform_alloced ;
return 0 ;
out_platform_alloced :
platform_device_put ( sony_pf_device ) ;
sony_pf_device = NULL ;
out_platform_registered :
platform_driver_unregister ( & sony_pf_driver ) ;
out :
atomic_dec ( & sony_pf_users ) ;
return ret ;
}
static void sony_pf_remove ( void )
{
/* deregister only after the last user has gone */
if ( ! atomic_dec_and_test ( & sony_pf_users ) )
return ;
platform_device_del ( sony_pf_device ) ;
platform_device_put ( sony_pf_device ) ;
platform_driver_unregister ( & sony_pf_driver ) ;
}
/*********** SNC (SNY5001) Device ***********/
2007-04-09 21:26:03 +04:00
/* the device uses 1-based values, while the backlight subsystem uses
0 - based values */
# define SONY_MAX_BRIGHTNESS 8
# define SNC_VALIDATE_IN 0
# define SNC_VALIDATE_OUT 1
2007-04-09 12:19:04 +04:00
static ssize_t sony_nc_sysfs_show ( struct device * , struct device_attribute * ,
2007-02-07 23:34:02 +03:00
char * ) ;
2007-04-09 12:19:04 +04:00
static ssize_t sony_nc_sysfs_store ( struct device * , struct device_attribute * ,
2007-02-07 23:34:02 +03:00
const char * , size_t ) ;
2007-02-13 00:01:07 +03:00
static int boolean_validate ( const int , const int ) ;
static int brightness_default_validate ( const int , const int ) ;
2007-04-09 12:19:04 +04:00
struct sony_nc_value {
2007-02-07 23:34:02 +03:00
char * name ; /* name of the entry */
char * * acpiget ; /* names of the ACPI get function */
char * * acpiset ; /* names of the ACPI set function */
2007-02-13 00:01:07 +03:00
int ( * validate ) ( const int , const int ) ; /* input/output validation */
2007-02-07 23:34:02 +03:00
int value ; /* current setting */
int valid ; /* Has ever been set */
int debug ; /* active only in debug mode ? */
struct device_attribute devattr ; /* sysfs atribute */
2007-02-07 22:01:54 +03:00
} ;
2007-04-09 12:19:04 +04:00
# define SNC_HANDLE_NAMES(_name, _values...) \
2007-02-07 22:01:54 +03:00
static char * snc_ # # _name [ ] = { _values , NULL }
2007-04-09 12:19:04 +04:00
# define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
2007-02-07 22:01:54 +03:00
{ \
. name = __stringify ( _name ) , \
. acpiget = _getters , \
. acpiset = _setters , \
2007-02-13 00:01:07 +03:00
. validate = _validate , \
2007-02-07 22:01:54 +03:00
. debug = _debug , \
2007-04-09 12:19:04 +04:00
. devattr = __ATTR ( _name , 0 , sony_nc_sysfs_show , sony_nc_sysfs_store ) , \
2007-01-14 01:04:31 +03:00
}
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
# define SNC_HANDLE_NULL { .name = NULL }
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( fnkey_get , " GHKE " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( brightness_def_get , " GPBR " ) ;
SNC_HANDLE_NAMES ( brightness_def_set , " SPBR " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( cdpower_get , " GCDP " ) ;
SNC_HANDLE_NAMES ( cdpower_set , " SCDP " , " CDPW " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( audiopower_get , " GAZP " ) ;
SNC_HANDLE_NAMES ( audiopower_set , " AZPW " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( lanpower_get , " GLNP " ) ;
SNC_HANDLE_NAMES ( lanpower_set , " LNPW " ) ;
2007-02-07 22:01:54 +03:00
2007-07-15 21:34:33 +04:00
SNC_HANDLE_NAMES ( lidstate_get , " GLID " ) ;
SNC_HANDLE_NAMES ( indicatorlamp_get , " GILS " ) ;
SNC_HANDLE_NAMES ( indicatorlamp_set , " SILS " ) ;
SNC_HANDLE_NAMES ( gainbass_get , " GMGB " ) ;
SNC_HANDLE_NAMES ( gainbass_set , " CMGB " ) ;
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( PID_get , " GPID " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( CTR_get , " GCTR " ) ;
SNC_HANDLE_NAMES ( CTR_set , " SCTR " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( PCR_get , " GPCR " ) ;
SNC_HANDLE_NAMES ( PCR_set , " SPCR " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
SNC_HANDLE_NAMES ( CMI_get , " GCMI " ) ;
SNC_HANDLE_NAMES ( CMI_set , " SCMI " ) ;
2007-02-07 22:01:54 +03:00
2007-04-09 12:19:04 +04:00
static struct sony_nc_value sony_nc_values [ ] = {
SNC_HANDLE ( brightness_default , snc_brightness_def_get ,
2007-02-13 00:01:07 +03:00
snc_brightness_def_set , brightness_default_validate , 0 ) ,
2007-04-09 12:19:04 +04:00
SNC_HANDLE ( fnkey , snc_fnkey_get , NULL , NULL , 0 ) ,
SNC_HANDLE ( cdpower , snc_cdpower_get , snc_cdpower_set , boolean_validate , 0 ) ,
SNC_HANDLE ( audiopower , snc_audiopower_get , snc_audiopower_set ,
2007-02-13 00:01:07 +03:00
boolean_validate , 0 ) ,
2007-04-09 12:19:04 +04:00
SNC_HANDLE ( lanpower , snc_lanpower_get , snc_lanpower_set ,
2007-02-13 00:01:07 +03:00
boolean_validate , 1 ) ,
2007-07-15 21:34:33 +04:00
SNC_HANDLE ( lidstate , snc_lidstate_get , NULL ,
boolean_validate , 0 ) ,
SNC_HANDLE ( indicatorlamp , snc_indicatorlamp_get , snc_indicatorlamp_set ,
boolean_validate , 0 ) ,
SNC_HANDLE ( gainbass , snc_gainbass_get , snc_gainbass_set ,
boolean_validate , 0 ) ,
2007-02-07 22:01:54 +03:00
/* unknown methods */
2007-04-09 12:19:04 +04:00
SNC_HANDLE ( PID , snc_PID_get , NULL , NULL , 1 ) ,
SNC_HANDLE ( CTR , snc_CTR_get , snc_CTR_set , NULL , 1 ) ,
SNC_HANDLE ( PCR , snc_PCR_get , snc_PCR_set , NULL , 1 ) ,
SNC_HANDLE ( CMI , snc_CMI_get , snc_CMI_set , NULL , 1 ) ,
SNC_HANDLE_NULL
2007-01-14 01:04:31 +03:00
} ;
2007-04-09 12:19:04 +04:00
static acpi_handle sony_nc_acpi_handle ;
static struct acpi_device * sony_nc_acpi_device = NULL ;
2007-02-07 22:01:54 +03:00
2007-02-07 22:01:56 +03:00
/*
* acpi_evaluate_object wrappers
*/
2007-01-14 01:04:31 +03:00
static int acpi_callgetfunc ( acpi_handle handle , char * name , int * result )
{
struct acpi_buffer output ;
union acpi_object out_obj ;
acpi_status status ;
output . length = sizeof ( out_obj ) ;
output . pointer = & out_obj ;
status = acpi_evaluate_object ( handle , name , NULL , & output ) ;
if ( ( status = = AE_OK ) & & ( out_obj . type = = ACPI_TYPE_INTEGER ) ) {
* result = out_obj . integer . value ;
return 0 ;
}
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " acpi_callreadfunc failed \n " ) ;
2007-01-14 01:04:31 +03:00
return - 1 ;
}
static int acpi_callsetfunc ( acpi_handle handle , char * name , int value ,
int * result )
{
struct acpi_object_list params ;
union acpi_object in_obj ;
struct acpi_buffer output ;
union acpi_object out_obj ;
acpi_status status ;
params . count = 1 ;
params . pointer = & in_obj ;
in_obj . type = ACPI_TYPE_INTEGER ;
in_obj . integer . value = value ;
output . length = sizeof ( out_obj ) ;
output . pointer = & out_obj ;
status = acpi_evaluate_object ( handle , name , & params , & output ) ;
if ( status = = AE_OK ) {
if ( result ! = NULL ) {
if ( out_obj . type ! = ACPI_TYPE_INTEGER ) {
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " acpi_evaluate_object bad "
2007-01-14 01:04:31 +03:00
" return type \n " ) ;
return - 1 ;
}
* result = out_obj . integer . value ;
}
return 0 ;
}
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " acpi_evaluate_object failed \n " ) ;
2007-01-14 01:04:31 +03:00
return - 1 ;
}
2007-02-13 00:01:07 +03:00
/*
2007-04-09 12:19:04 +04:00
* sony_nc_values input / output validate functions
2007-02-13 00:01:07 +03:00
*/
/* brightness_default_validate:
*
* manipulate input output values to keep consistency with the
* backlight framework for which brightness values are 0 - based .
*/
static int brightness_default_validate ( const int direction , const int value )
{
switch ( direction ) {
case SNC_VALIDATE_OUT :
return value - 1 ;
case SNC_VALIDATE_IN :
if ( value > = 0 & & value < SONY_MAX_BRIGHTNESS )
return value + 1 ;
}
return - EINVAL ;
}
/* boolean_validate:
*
* on input validate boolean values 0 / 1 , on output just pass the
* received value .
*/
static int boolean_validate ( const int direction , const int value )
{
if ( direction = = SNC_VALIDATE_IN ) {
if ( value ! = 0 & & value ! = 1 )
return - EINVAL ;
}
return value ;
}
2007-02-07 22:01:54 +03:00
/*
2007-04-09 12:19:04 +04:00
* Sysfs show / store common to all sony_nc_values
2007-02-07 22:01:54 +03:00
*/
2007-04-09 12:19:04 +04:00
static ssize_t sony_nc_sysfs_show ( struct device * dev , struct device_attribute * attr ,
2007-02-07 23:34:02 +03:00
char * buffer )
2007-01-14 01:04:31 +03:00
{
int value ;
2007-04-09 12:19:04 +04:00
struct sony_nc_value * item =
container_of ( attr , struct sony_nc_value , devattr ) ;
2007-01-14 01:04:31 +03:00
2007-02-07 22:01:54 +03:00
if ( ! * item - > acpiget )
2007-01-14 01:04:31 +03:00
return - EIO ;
2007-04-09 12:19:04 +04:00
if ( acpi_callgetfunc ( sony_nc_acpi_handle , * item - > acpiget , & value ) < 0 )
2007-01-14 01:04:31 +03:00
return - EIO ;
2007-02-13 00:01:07 +03:00
if ( item - > validate )
value = item - > validate ( SNC_VALIDATE_OUT , value ) ;
2007-02-07 22:01:54 +03:00
return snprintf ( buffer , PAGE_SIZE , " %d \n " , value ) ;
2007-01-14 01:04:31 +03:00
}
2007-04-09 12:19:04 +04:00
static ssize_t sony_nc_sysfs_store ( struct device * dev ,
2007-02-07 23:34:02 +03:00
struct device_attribute * attr ,
const char * buffer , size_t count )
2007-01-14 01:04:31 +03:00
{
int value ;
2007-04-09 12:19:04 +04:00
struct sony_nc_value * item =
container_of ( attr , struct sony_nc_value , devattr ) ;
2007-01-14 01:04:31 +03:00
if ( ! item - > acpiset )
return - EIO ;
2007-02-07 22:01:54 +03:00
if ( count > 31 )
return - EINVAL ;
value = simple_strtoul ( buffer , NULL , 10 ) ;
2007-01-14 01:04:31 +03:00
2007-02-13 00:01:07 +03:00
if ( item - > validate )
value = item - > validate ( SNC_VALIDATE_IN , value ) ;
if ( value < 0 )
return value ;
2007-01-14 01:04:31 +03:00
2007-04-09 12:19:04 +04:00
if ( acpi_callsetfunc ( sony_nc_acpi_handle , * item - > acpiset , value , NULL ) < 0 )
2007-01-14 01:04:31 +03:00
return - EIO ;
2007-01-14 01:04:32 +03:00
item - > value = value ;
item - > valid = 1 ;
2007-01-14 01:04:31 +03:00
return count ;
}
2007-02-07 22:01:54 +03:00
2007-02-07 22:01:56 +03:00
/*
* Backlight device
*/
static int sony_backlight_update_status ( struct backlight_device * bd )
2007-01-14 01:04:32 +03:00
{
2007-04-09 12:19:04 +04:00
return acpi_callsetfunc ( sony_nc_acpi_handle , " SBRT " ,
2007-02-10 18:04:08 +03:00
bd - > props . brightness + 1 , NULL ) ;
2007-02-07 22:01:56 +03:00
}
2007-01-14 01:04:32 +03:00
2007-02-07 22:01:56 +03:00
static int sony_backlight_get_brightness ( struct backlight_device * bd )
{
int value ;
2007-01-14 01:04:32 +03:00
2007-04-09 12:19:04 +04:00
if ( acpi_callgetfunc ( sony_nc_acpi_handle , " GBRT " , & value ) )
2007-02-07 22:01:56 +03:00
return 0 ;
/* brightness levels are 1-based, while backlight ones are 0-based */
return value - 1 ;
2007-01-14 01:04:32 +03:00
}
2007-02-07 22:01:56 +03:00
static struct backlight_device * sony_backlight_device ;
2007-02-10 18:04:08 +03:00
static struct backlight_ops sony_backlight_ops = {
2007-02-07 23:34:02 +03:00
. update_status = sony_backlight_update_status ,
. get_brightness = sony_backlight_get_brightness ,
2007-02-07 22:01:56 +03:00
} ;
2007-07-15 21:34:35 +04:00
/*
* New SNC - only Vaios event mapping to driver known keys
*/
struct sony_nc_event {
u8 data ;
u8 event ;
} ;
static struct sony_nc_event * sony_nc_events ;
/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
* for Fn keys
*/
2007-10-03 23:15:40 +04:00
static int sony_nc_C_enable ( const struct dmi_system_id * id )
2007-07-15 21:34:35 +04:00
{
int result = 0 ;
printk ( KERN_NOTICE DRV_PFX " detected %s \n " , id - > ident ) ;
sony_nc_events = id - > driver_data ;
if ( acpi_callsetfunc ( sony_nc_acpi_handle , " SN02 " , 0x4 , & result ) < 0
| | acpi_callsetfunc ( sony_nc_acpi_handle , " SN07 " , 0x2 , & result ) < 0
| | acpi_callsetfunc ( sony_nc_acpi_handle , " SN02 " , 0x10 , & result ) < 0
| | acpi_callsetfunc ( sony_nc_acpi_handle , " SN07 " , 0x0 , & result ) < 0
| | acpi_callsetfunc ( sony_nc_acpi_handle , " SN03 " , 0x2 , & result ) < 0
| | acpi_callsetfunc ( sony_nc_acpi_handle , " SN07 " , 0x101 , & result ) < 0 ) {
printk ( KERN_WARNING DRV_PFX " failed to initialize SNC, some "
" functionalities may be missing \n " ) ;
return 1 ;
}
return 0 ;
}
static struct sony_nc_event sony_C_events [ ] = {
{ 0x81 , SONYPI_EVENT_FNKEY_F1 } ,
{ 0x01 , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0x85 , SONYPI_EVENT_FNKEY_F5 } ,
{ 0x05 , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0x86 , SONYPI_EVENT_FNKEY_F6 } ,
{ 0x06 , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0x87 , SONYPI_EVENT_FNKEY_F7 } ,
{ 0x07 , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0x8A , SONYPI_EVENT_FNKEY_F10 } ,
{ 0x0A , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0x8C , SONYPI_EVENT_FNKEY_F12 } ,
{ 0x0C , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0 , 0 } ,
} ;
/* SNC-only model map */
2007-10-03 23:15:40 +04:00
static const struct dmi_system_id sony_nc_ids [ ] = {
2007-07-15 21:34:38 +04:00
{
. ident = " Sony Vaio FE Series " ,
. callback = sony_nc_C_enable ,
. driver_data = sony_C_events ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " VGN-FE " ) ,
} ,
} ,
2007-08-12 11:20:26 +04:00
{
. ident = " Sony Vaio FZ Series " ,
. callback = sony_nc_C_enable ,
. driver_data = sony_C_events ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " VGN-FZ " ) ,
} ,
} ,
2007-07-15 21:34:35 +04:00
{
. ident = " Sony Vaio C Series " ,
. callback = sony_nc_C_enable ,
. driver_data = sony_C_events ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " VGN-C " ) ,
} ,
} ,
2008-01-14 12:05:42 +03:00
{
. ident = " Sony Vaio N Series " ,
. callback = sony_nc_C_enable ,
. driver_data = sony_C_events ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " VGN-N " ) ,
} ,
} ,
2007-07-15 21:34:35 +04:00
{ }
} ;
2007-02-07 22:01:56 +03:00
/*
* ACPI callbacks
*/
2007-01-14 01:04:31 +03:00
static void sony_acpi_notify ( acpi_handle handle , u32 event , void * data )
{
2007-07-15 21:34:35 +04:00
struct sony_nc_event * evmap ;
u32 ev = event ;
int result ;
if ( ev = = 0x92 ) {
/* read the key pressed from EC.GECR
* A call to SN07 with 0x0202 will do it as well respecting
* the current protocol on different OSes
*
* Note : the path for GECR may be
* \ _SB . PCI0 . LPCB . EC ( C , FE , AR , N and friends )
* \ _SB . PCI0 . PIB . EC0 ( VGN - FR notifications are sent directly , no GECR )
*
* TODO : we may want to do the same for the older GHKE - need
* dmi list - so this snippet may become one more callback .
*/
if ( acpi_callsetfunc ( handle , " SN07 " , 0x0202 , & result ) < 0 )
dprintk ( " sony_acpi_notify, unable to decode event 0x%.2x \n " , ev ) ;
else
ev = result & 0xFF ;
}
if ( sony_nc_events )
for ( evmap = sony_nc_events ; evmap - > event ; evmap + + ) {
if ( evmap - > data = = ev ) {
ev = evmap - > event ;
break ;
}
}
dprintk ( " sony_acpi_notify, event: 0x%.2x \n " , ev ) ;
sony_laptop_report_input_event ( ev ) ;
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( sony_nc_acpi_device , 1 , ev ) ;
2007-01-14 01:04:31 +03:00
}
static acpi_status sony_walk_callback ( acpi_handle handle , u32 level ,
void * context , void * * return_value )
{
2008-12-16 11:59:35 +03:00
struct acpi_device_info * info ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
2007-01-14 01:04:31 +03:00
2008-12-16 11:59:35 +03:00
if ( ACPI_SUCCESS ( acpi_get_object_info ( handle , & buffer ) ) ) {
info = buffer . pointer ;
2007-01-14 01:04:31 +03:00
2008-12-16 11:59:35 +03:00
printk ( KERN_WARNING DRV_PFX " method: name: %4.4s, args %X \n " ,
( char * ) & info - > name , info - > param_count ) ;
kfree ( buffer . pointer ) ;
}
2007-01-14 01:04:31 +03:00
return AE_OK ;
}
2007-02-07 22:01:56 +03:00
/*
* ACPI device
*/
2007-04-09 12:19:04 +04:00
static int sony_nc_resume ( struct acpi_device * device )
2007-02-07 22:01:56 +03:00
{
2007-04-09 12:19:04 +04:00
struct sony_nc_value * item ;
2007-02-07 22:01:56 +03:00
2007-04-09 12:19:04 +04:00
for ( item = sony_nc_values ; item - > name ; item + + ) {
2007-02-07 22:01:56 +03:00
int ret ;
if ( ! item - > valid )
continue ;
2007-04-09 12:19:04 +04:00
ret = acpi_callsetfunc ( sony_nc_acpi_handle , * item - > acpiset ,
2007-02-07 23:34:02 +03:00
item - > value , NULL ) ;
2007-02-07 22:01:56 +03:00
if ( ret < 0 ) {
2008-04-29 11:59:20 +04:00
printk ( " %s: %d \n " , __func__ , ret ) ;
2007-02-07 22:01:56 +03:00
break ;
}
}
2007-07-15 21:34:35 +04:00
2007-08-03 19:22:30 +04:00
/* set the last requested brightness level */
if ( sony_backlight_device & &
! sony_backlight_update_status ( sony_backlight_device ) )
2008-10-16 23:15:35 +04:00
printk ( KERN_WARNING DRV_PFX " unable to restore brightness level \n " ) ;
2007-08-03 19:22:30 +04:00
2007-07-15 21:34:35 +04:00
/* re-initialize models with specific requirements */
dmi_check_system ( sony_nc_ids ) ;
2007-02-07 22:01:56 +03:00
return 0 ;
}
2007-04-09 12:19:04 +04:00
static int sony_nc_add ( struct acpi_device * device )
2007-01-14 01:04:31 +03:00
{
acpi_status status ;
2007-03-06 13:29:42 +03:00
int result = 0 ;
2007-01-14 01:04:34 +03:00
acpi_handle handle ;
2007-04-09 12:19:05 +04:00
struct sony_nc_value * item ;
2007-01-14 01:04:31 +03:00
2007-04-09 21:31:06 +04:00
printk ( KERN_INFO DRV_PFX " %s v%s. \n " ,
SONY_NC_DRIVER_NAME , SONY_LAPTOP_DRIVER_VERSION ) ;
2007-04-09 12:19:04 +04:00
sony_nc_acpi_device = device ;
2007-04-09 21:26:03 +04:00
strcpy ( acpi_device_class ( device ) , " sony/hotkey " ) ;
2007-01-14 01:04:37 +03:00
2007-04-09 12:19:04 +04:00
sony_nc_acpi_handle = device - > handle ;
2007-01-14 01:04:31 +03:00
2007-07-15 21:34:36 +04:00
/* read device status */
result = acpi_bus_get_status ( device ) ;
/* bail IFF the above call was successful and the device is not present */
if ( ! result & & ! device - > status . present ) {
dprintk ( " Device not present \n " ) ;
result = - ENODEV ;
goto outwalk ;
}
2007-01-14 01:04:31 +03:00
if ( debug ) {
2007-04-09 12:19:04 +04:00
status = acpi_walk_namespace ( ACPI_TYPE_METHOD , sony_nc_acpi_handle ,
2007-01-14 01:04:31 +03:00
1 , sony_walk_callback , NULL , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " unable to walk acpi resources \n " ) ;
2007-01-14 01:04:31 +03:00
result = - ENODEV ;
goto outwalk ;
}
2007-01-14 01:04:37 +03:00
}
2007-01-14 01:04:31 +03:00
2007-07-15 21:34:36 +04:00
/* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
* should be respected as we already checked for the device presence above */
if ( ACPI_SUCCESS ( acpi_get_handle ( sony_nc_acpi_handle , METHOD_NAME__INI , & handle ) ) ) {
dprintk ( " Invoking _INI \n " ) ;
if ( ACPI_FAILURE ( acpi_evaluate_object ( sony_nc_acpi_handle , METHOD_NAME__INI ,
NULL , NULL ) ) )
dprintk ( " _INI Method failed \n " ) ;
}
2007-04-09 12:19:08 +04:00
/* setup input devices and helper fifo */
2007-11-21 22:15:53 +03:00
result = sony_laptop_setup_input ( device ) ;
2007-04-09 12:19:08 +04:00
if ( result ) {
printk ( KERN_ERR DRV_PFX
" Unabe to create input devices. \n " ) ;
goto outwalk ;
}
2007-04-09 12:19:04 +04:00
status = acpi_install_notify_handler ( sony_nc_acpi_handle ,
2007-01-14 01:04:37 +03:00
ACPI_DEVICE_NOTIFY ,
2007-02-07 23:34:02 +03:00
sony_acpi_notify , NULL ) ;
2007-01-14 01:04:37 +03:00
if ( ACPI_FAILURE ( status ) ) {
2007-07-15 21:34:36 +04:00
printk ( KERN_WARNING DRV_PFX " unable to install notify handler (%u) \n " , status ) ;
2007-01-14 01:04:37 +03:00
result = - ENODEV ;
2007-04-09 12:19:08 +04:00
goto outinput ;
2007-01-14 01:04:31 +03:00
}
2008-11-13 01:03:28 +03:00
if ( acpi_video_backlight_support ( ) ) {
2008-11-13 01:13:35 +03:00
printk ( KERN_INFO DRV_PFX " brightness ignored, must be "
2008-08-01 19:38:02 +04:00
" controlled by ACPI video driver \n " ) ;
} else if ( ACPI_SUCCESS ( acpi_get_handle ( sony_nc_acpi_handle , " GBRT " ,
& handle ) ) ) {
2007-01-14 01:04:34 +03:00
sony_backlight_device = backlight_device_register ( " sony " , NULL ,
2007-02-07 23:34:02 +03:00
NULL ,
2007-02-10 18:04:08 +03:00
& sony_backlight_ops ) ;
2007-01-14 01:04:41 +03:00
2007-02-07 23:34:02 +03:00
if ( IS_ERR ( sony_backlight_device ) ) {
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " unable to register backlight device \n " ) ;
2007-01-14 01:04:41 +03:00
sony_backlight_device = NULL ;
2007-02-10 18:04:08 +03:00
} else {
sony_backlight_device - > props . brightness =
2007-02-07 23:34:02 +03:00
sony_backlight_get_brightness
( sony_backlight_device ) ;
2007-11-21 22:15:53 +03:00
sony_backlight_device - > props . max_brightness =
2007-02-10 18:04:08 +03:00
SONY_MAX_BRIGHTNESS - 1 ;
}
2007-01-14 01:04:34 +03:00
}
2007-01-14 01:04:31 +03:00
2007-07-15 21:34:35 +04:00
/* initialize models with specific requirements */
dmi_check_system ( sony_nc_ids ) ;
2007-04-09 21:28:56 +04:00
result = sony_pf_add ( ) ;
if ( result )
2007-02-07 22:01:54 +03:00
goto outbacklight ;
2007-01-14 01:04:31 +03:00
2007-04-09 12:19:05 +04:00
/* create sony_pf sysfs attributes related to the SNC device */
for ( item = sony_nc_values ; item - > name ; + + item ) {
if ( ! debug & & item - > debug )
continue ;
/* find the available acpiget as described in the DSDT */
for ( ; item - > acpiget & & * item - > acpiget ; + + item - > acpiget ) {
if ( ACPI_SUCCESS ( acpi_get_handle ( sony_nc_acpi_handle ,
* item - > acpiget ,
& handle ) ) ) {
2007-04-09 12:19:06 +04:00
dprintk ( " Found %s getter: %s \n " ,
item - > name , * item - > acpiget ) ;
2007-04-09 12:19:05 +04:00
item - > devattr . attr . mode | = S_IRUGO ;
break ;
}
}
/* find the available acpiset as described in the DSDT */
for ( ; item - > acpiset & & * item - > acpiset ; + + item - > acpiset ) {
if ( ACPI_SUCCESS ( acpi_get_handle ( sony_nc_acpi_handle ,
* item - > acpiset ,
& handle ) ) ) {
2007-04-09 12:19:06 +04:00
dprintk ( " Found %s setter: %s \n " ,
item - > name , * item - > acpiset ) ;
2007-04-09 12:19:05 +04:00
item - > devattr . attr . mode | = S_IWUSR ;
break ;
}
}
if ( item - > devattr . attr . mode ! = 0 ) {
result =
device_create_file ( & sony_pf_device - > dev ,
& item - > devattr ) ;
if ( result )
goto out_sysfs ;
}
}
2007-01-14 01:04:31 +03:00
return 0 ;
2007-04-09 12:19:05 +04:00
out_sysfs :
for ( item = sony_nc_values ; item - > name ; + + item ) {
device_remove_file ( & sony_pf_device - > dev , & item - > devattr ) ;
}
sony_pf_remove ( ) ;
2007-04-09 12:19:08 +04:00
2007-02-07 23:34:02 +03:00
outbacklight :
2007-01-14 01:04:41 +03:00
if ( sony_backlight_device )
backlight_device_unregister ( sony_backlight_device ) ;
2007-04-09 12:19:04 +04:00
status = acpi_remove_notify_handler ( sony_nc_acpi_handle ,
2007-01-14 01:04:37 +03:00
ACPI_DEVICE_NOTIFY ,
sony_acpi_notify ) ;
if ( ACPI_FAILURE ( status ) )
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " unable to remove notify handler \n " ) ;
2007-04-09 12:19:08 +04:00
outinput :
sony_laptop_remove_input ( ) ;
2007-02-07 23:34:02 +03:00
outwalk :
2007-01-14 01:04:31 +03:00
return result ;
}
2007-04-09 12:19:04 +04:00
static int sony_nc_remove ( struct acpi_device * device , int type )
2007-01-14 01:04:31 +03:00
{
acpi_status status ;
2007-04-09 12:19:05 +04:00
struct sony_nc_value * item ;
2007-01-14 01:04:31 +03:00
2007-01-14 01:04:34 +03:00
if ( sony_backlight_device )
backlight_device_unregister ( sony_backlight_device ) ;
2007-04-09 12:19:04 +04:00
sony_nc_acpi_device = NULL ;
2007-01-14 01:04:37 +03:00
2007-04-09 12:19:04 +04:00
status = acpi_remove_notify_handler ( sony_nc_acpi_handle ,
2007-01-14 01:04:37 +03:00
ACPI_DEVICE_NOTIFY ,
sony_acpi_notify ) ;
if ( ACPI_FAILURE ( status ) )
2007-04-09 21:31:06 +04:00
printk ( KERN_WARNING DRV_PFX " unable to remove notify handler \n " ) ;
2007-01-14 01:04:31 +03:00
2007-04-09 12:19:05 +04:00
for ( item = sony_nc_values ; item - > name ; + + item ) {
device_remove_file ( & sony_pf_device - > dev , & item - > devattr ) ;
}
sony_pf_remove ( ) ;
2007-04-09 12:19:08 +04:00
sony_laptop_remove_input ( ) ;
2007-04-09 21:31:06 +04:00
dprintk ( SONY_NC_DRIVER_NAME " removed. \n " ) ;
2007-01-14 01:04:31 +03:00
return 0 ;
}
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id sony_device_ids [ ] = {
{ SONY_NC_HID , 0 } ,
{ SONY_PIC_HID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , sony_device_ids ) ;
static const struct acpi_device_id sony_nc_device_ids [ ] = {
{ SONY_NC_HID , 0 } ,
{ " " , 0 } ,
} ;
2007-04-09 12:19:04 +04:00
static struct acpi_driver sony_nc_driver = {
. name = SONY_NC_DRIVER_NAME ,
. class = SONY_NC_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = sony_nc_device_ids ,
2007-04-09 21:26:03 +04:00
. owner = THIS_MODULE ,
2007-02-07 23:34:02 +03:00
. ops = {
2007-04-09 12:19:04 +04:00
. add = sony_nc_add ,
. remove = sony_nc_remove ,
. resume = sony_nc_resume ,
2007-02-07 23:34:02 +03:00
} ,
2007-01-14 01:04:32 +03:00
} ;
2007-04-09 21:26:03 +04:00
/*********** SPIC (SNY6001) Device ***********/
# define SONYPI_DEVICE_TYPE1 0x00000001
# define SONYPI_DEVICE_TYPE2 0x00000002
# define SONYPI_DEVICE_TYPE3 0x00000004
2008-01-14 12:05:45 +03:00
# define SONYPI_DEVICE_TYPE4 0x00000008
2007-04-09 21:26:03 +04:00
2007-07-15 21:34:39 +04:00
# define SONYPI_TYPE1_OFFSET 0x04
# define SONYPI_TYPE2_OFFSET 0x12
# define SONYPI_TYPE3_OFFSET 0x12
2008-01-14 12:05:45 +03:00
# define SONYPI_TYPE4_OFFSET 0x12
2007-04-09 21:26:03 +04:00
struct sony_pic_ioport {
2007-08-12 11:20:28 +04:00
struct acpi_resource_io io1 ;
struct acpi_resource_io io2 ;
2007-04-09 21:26:03 +04:00
struct list_head list ;
} ;
struct sony_pic_irq {
struct acpi_resource_irq irq ;
struct list_head list ;
} ;
2008-01-14 12:05:43 +03:00
struct sonypi_eventtypes {
u8 data ;
unsigned long mask ;
struct sonypi_event * events ;
} ;
struct device_ctrl {
int model ;
2008-01-14 12:05:45 +03:00
int ( * handle_irq ) ( const u8 , const u8 ) ;
2008-01-14 12:05:43 +03:00
u16 evport_offset ;
u8 has_camera ;
u8 has_bluetooth ;
u8 has_wwan ;
struct sonypi_eventtypes * event_types ;
} ;
2007-04-09 21:26:03 +04:00
struct sony_pic_dev {
2008-01-14 12:05:43 +03:00
struct device_ctrl * control ;
2007-04-09 21:26:03 +04:00
struct acpi_device * acpi_dev ;
struct sony_pic_irq * cur_irq ;
struct sony_pic_ioport * cur_ioport ;
struct list_head interrupts ;
struct list_head ioports ;
2007-04-28 18:19:36 +04:00
struct mutex lock ;
2008-01-14 12:05:43 +03:00
u8 camera_power ;
u8 bluetooth_power ;
u8 wwan_power ;
2007-04-09 21:26:03 +04:00
} ;
static struct sony_pic_dev spic_dev = {
. interrupts = LIST_HEAD_INIT ( spic_dev . interrupts ) ,
. ioports = LIST_HEAD_INIT ( spic_dev . ioports ) ,
} ;
/* Event masks */
# define SONYPI_JOGGER_MASK 0x00000001
# define SONYPI_CAPTURE_MASK 0x00000002
# define SONYPI_FNKEY_MASK 0x00000004
# define SONYPI_BLUETOOTH_MASK 0x00000008
# define SONYPI_PKEY_MASK 0x00000010
# define SONYPI_BACK_MASK 0x00000020
# define SONYPI_HELP_MASK 0x00000040
# define SONYPI_LID_MASK 0x00000080
# define SONYPI_ZOOM_MASK 0x00000100
# define SONYPI_THUMBPHRASE_MASK 0x00000200
# define SONYPI_MEYE_MASK 0x00000400
# define SONYPI_MEMORYSTICK_MASK 0x00000800
# define SONYPI_BATTERY_MASK 0x00001000
# define SONYPI_WIRELESS_MASK 0x00002000
struct sonypi_event {
u8 data ;
u8 event ;
} ;
/* The set of possible button release events */
static struct sonypi_event sonypi_releaseev [ ] = {
{ 0x00 , SONYPI_EVENT_ANYBUTTON_RELEASED } ,
{ 0 , 0 }
} ;
/* The set of possible jogger events */
static struct sonypi_event sonypi_joggerev [ ] = {
{ 0x1f , SONYPI_EVENT_JOGDIAL_UP } ,
{ 0x01 , SONYPI_EVENT_JOGDIAL_DOWN } ,
{ 0x5f , SONYPI_EVENT_JOGDIAL_UP_PRESSED } ,
{ 0x41 , SONYPI_EVENT_JOGDIAL_DOWN_PRESSED } ,
{ 0x1e , SONYPI_EVENT_JOGDIAL_FAST_UP } ,
{ 0x02 , SONYPI_EVENT_JOGDIAL_FAST_DOWN } ,
{ 0x5e , SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED } ,
{ 0x42 , SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED } ,
{ 0x1d , SONYPI_EVENT_JOGDIAL_VFAST_UP } ,
{ 0x03 , SONYPI_EVENT_JOGDIAL_VFAST_DOWN } ,
{ 0x5d , SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED } ,
{ 0x43 , SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED } ,
{ 0x40 , SONYPI_EVENT_JOGDIAL_PRESSED } ,
{ 0 , 0 }
} ;
/* The set of possible capture button events */
static struct sonypi_event sonypi_captureev [ ] = {
{ 0x05 , SONYPI_EVENT_CAPTURE_PARTIALPRESSED } ,
{ 0x07 , SONYPI_EVENT_CAPTURE_PRESSED } ,
2008-01-14 12:05:45 +03:00
{ 0x40 , SONYPI_EVENT_CAPTURE_PRESSED } ,
2007-04-09 21:26:03 +04:00
{ 0x01 , SONYPI_EVENT_CAPTURE_PARTIALRELEASED } ,
{ 0 , 0 }
} ;
/* The set of possible fnkeys events */
static struct sonypi_event sonypi_fnkeyev [ ] = {
{ 0x10 , SONYPI_EVENT_FNKEY_ESC } ,
{ 0x11 , SONYPI_EVENT_FNKEY_F1 } ,
{ 0x12 , SONYPI_EVENT_FNKEY_F2 } ,
{ 0x13 , SONYPI_EVENT_FNKEY_F3 } ,
{ 0x14 , SONYPI_EVENT_FNKEY_F4 } ,
{ 0x15 , SONYPI_EVENT_FNKEY_F5 } ,
{ 0x16 , SONYPI_EVENT_FNKEY_F6 } ,
{ 0x17 , SONYPI_EVENT_FNKEY_F7 } ,
{ 0x18 , SONYPI_EVENT_FNKEY_F8 } ,
{ 0x19 , SONYPI_EVENT_FNKEY_F9 } ,
{ 0x1a , SONYPI_EVENT_FNKEY_F10 } ,
{ 0x1b , SONYPI_EVENT_FNKEY_F11 } ,
{ 0x1c , SONYPI_EVENT_FNKEY_F12 } ,
{ 0x1f , SONYPI_EVENT_FNKEY_RELEASED } ,
{ 0x21 , SONYPI_EVENT_FNKEY_1 } ,
{ 0x22 , SONYPI_EVENT_FNKEY_2 } ,
{ 0x31 , SONYPI_EVENT_FNKEY_D } ,
{ 0x32 , SONYPI_EVENT_FNKEY_E } ,
{ 0x33 , SONYPI_EVENT_FNKEY_F } ,
{ 0x34 , SONYPI_EVENT_FNKEY_S } ,
{ 0x35 , SONYPI_EVENT_FNKEY_B } ,
{ 0x36 , SONYPI_EVENT_FNKEY_ONLY } ,
{ 0 , 0 }
} ;
/* The set of possible program key events */
static struct sonypi_event sonypi_pkeyev [ ] = {
{ 0x01 , SONYPI_EVENT_PKEY_P1 } ,
{ 0x02 , SONYPI_EVENT_PKEY_P2 } ,
{ 0x04 , SONYPI_EVENT_PKEY_P3 } ,
{ 0 , 0 }
} ;
/* The set of possible bluetooth events */
static struct sonypi_event sonypi_blueev [ ] = {
{ 0x55 , SONYPI_EVENT_BLUETOOTH_PRESSED } ,
{ 0x59 , SONYPI_EVENT_BLUETOOTH_ON } ,
{ 0x5a , SONYPI_EVENT_BLUETOOTH_OFF } ,
{ 0 , 0 }
} ;
/* The set of possible wireless events */
static struct sonypi_event sonypi_wlessev [ ] = {
{ 0x59 , SONYPI_EVENT_WIRELESS_ON } ,
{ 0x5a , SONYPI_EVENT_WIRELESS_OFF } ,
{ 0 , 0 }
} ;
/* The set of possible back button events */
static struct sonypi_event sonypi_backev [ ] = {
{ 0x20 , SONYPI_EVENT_BACK_PRESSED } ,
{ 0 , 0 }
} ;
/* The set of possible help button events */
static struct sonypi_event sonypi_helpev [ ] = {
{ 0x3b , SONYPI_EVENT_HELP_PRESSED } ,
{ 0 , 0 }
} ;
/* The set of possible lid events */
static struct sonypi_event sonypi_lidev [ ] = {
{ 0x51 , SONYPI_EVENT_LID_CLOSED } ,
{ 0x50 , SONYPI_EVENT_LID_OPENED } ,
{ 0 , 0 }
} ;
/* The set of possible zoom events */
static struct sonypi_event sonypi_zoomev [ ] = {
{ 0x39 , SONYPI_EVENT_ZOOM_PRESSED } ,
2008-01-14 12:05:45 +03:00
{ 0x10 , SONYPI_EVENT_ZOOM_IN_PRESSED } ,
{ 0x20 , SONYPI_EVENT_ZOOM_OUT_PRESSED } ,
2007-04-09 21:26:03 +04:00
{ 0 , 0 }
} ;
/* The set of possible thumbphrase events */
static struct sonypi_event sonypi_thumbphraseev [ ] = {
{ 0x3a , SONYPI_EVENT_THUMBPHRASE_PRESSED } ,
{ 0 , 0 }
} ;
/* The set of possible motioneye camera events */
static struct sonypi_event sonypi_meyeev [ ] = {
{ 0x00 , SONYPI_EVENT_MEYE_FACE } ,
{ 0x01 , SONYPI_EVENT_MEYE_OPPOSITE } ,
{ 0 , 0 }
} ;
/* The set of possible memorystick events */
static struct sonypi_event sonypi_memorystickev [ ] = {
{ 0x53 , SONYPI_EVENT_MEMORYSTICK_INSERT } ,
{ 0x54 , SONYPI_EVENT_MEMORYSTICK_EJECT } ,
{ 0 , 0 }
} ;
/* The set of possible battery events */
static struct sonypi_event sonypi_batteryev [ ] = {
{ 0x20 , SONYPI_EVENT_BATTERY_INSERT } ,
{ 0x30 , SONYPI_EVENT_BATTERY_REMOVE } ,
{ 0 , 0 }
} ;
2008-01-14 12:05:43 +03:00
static struct sonypi_eventtypes type1_events [ ] = {
{ 0 , 0xffffffff , sonypi_releaseev } ,
{ 0x70 , SONYPI_MEYE_MASK , sonypi_meyeev } ,
{ 0x30 , SONYPI_LID_MASK , sonypi_lidev } ,
{ 0x60 , SONYPI_CAPTURE_MASK , sonypi_captureev } ,
{ 0x10 , SONYPI_JOGGER_MASK , sonypi_joggerev } ,
{ 0x20 , SONYPI_FNKEY_MASK , sonypi_fnkeyev } ,
{ 0x30 , SONYPI_BLUETOOTH_MASK , sonypi_blueev } ,
{ 0x40 , SONYPI_PKEY_MASK , sonypi_pkeyev } ,
{ 0x30 , SONYPI_MEMORYSTICK_MASK , sonypi_memorystickev } ,
{ 0x40 , SONYPI_BATTERY_MASK , sonypi_batteryev } ,
{ 0 } ,
} ;
static struct sonypi_eventtypes type2_events [ ] = {
{ 0 , 0xffffffff , sonypi_releaseev } ,
{ 0x38 , SONYPI_LID_MASK , sonypi_lidev } ,
{ 0x11 , SONYPI_JOGGER_MASK , sonypi_joggerev } ,
{ 0x61 , SONYPI_CAPTURE_MASK , sonypi_captureev } ,
{ 0x21 , SONYPI_FNKEY_MASK , sonypi_fnkeyev } ,
{ 0x31 , SONYPI_BLUETOOTH_MASK , sonypi_blueev } ,
{ 0x08 , SONYPI_PKEY_MASK , sonypi_pkeyev } ,
{ 0x11 , SONYPI_BACK_MASK , sonypi_backev } ,
{ 0x21 , SONYPI_HELP_MASK , sonypi_helpev } ,
{ 0x21 , SONYPI_ZOOM_MASK , sonypi_zoomev } ,
{ 0x20 , SONYPI_THUMBPHRASE_MASK , sonypi_thumbphraseev } ,
{ 0x31 , SONYPI_MEMORYSTICK_MASK , sonypi_memorystickev } ,
{ 0x41 , SONYPI_BATTERY_MASK , sonypi_batteryev } ,
{ 0x31 , SONYPI_PKEY_MASK , sonypi_pkeyev } ,
{ 0 } ,
} ;
static struct sonypi_eventtypes type3_events [ ] = {
{ 0 , 0xffffffff , sonypi_releaseev } ,
{ 0x21 , SONYPI_FNKEY_MASK , sonypi_fnkeyev } ,
{ 0x31 , SONYPI_WIRELESS_MASK , sonypi_wlessev } ,
{ 0x31 , SONYPI_MEMORYSTICK_MASK , sonypi_memorystickev } ,
{ 0x41 , SONYPI_BATTERY_MASK , sonypi_batteryev } ,
{ 0x31 , SONYPI_PKEY_MASK , sonypi_pkeyev } ,
{ 0 } ,
2007-04-09 21:26:03 +04:00
} ;
2008-01-14 12:05:45 +03:00
static struct sonypi_eventtypes type4_events [ ] = {
{ 0 , 0xffffffff , sonypi_releaseev } ,
{ 0x21 , SONYPI_FNKEY_MASK , sonypi_fnkeyev } ,
{ 0x31 , SONYPI_WIRELESS_MASK , sonypi_wlessev } ,
{ 0x31 , SONYPI_MEMORYSTICK_MASK , sonypi_memorystickev } ,
{ 0x41 , SONYPI_BATTERY_MASK , sonypi_batteryev } ,
{ 0x05 , SONYPI_PKEY_MASK , sonypi_pkeyev } ,
{ 0x05 , SONYPI_ZOOM_MASK , sonypi_zoomev } ,
{ 0x05 , SONYPI_CAPTURE_MASK , sonypi_captureev } ,
{ 0 } ,
2008-01-14 12:05:43 +03:00
} ;
2008-01-14 12:05:45 +03:00
/* low level spic calls */
2007-04-09 21:26:03 +04:00
# define ITERATIONS_LONG 10000
# define ITERATIONS_SHORT 10
# define wait_on_command(command, iterations) { \
unsigned int n = iterations ; \
while ( - - n & & ( command ) ) \
udelay ( 1 ) ; \
if ( ! n ) \
dprintk ( " command failed at %s : %s (line %d) \n " , \
2008-04-29 11:59:20 +04:00
__FILE__ , __func__ , __LINE__ ) ; \
2007-04-09 21:26:03 +04:00
}
static u8 sony_pic_call1 ( u8 dev )
{
u8 v1 , v2 ;
2007-08-12 11:20:28 +04:00
wait_on_command ( inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) & 2 ,
2007-04-09 21:26:03 +04:00
ITERATIONS_LONG ) ;
2007-08-12 11:20:28 +04:00
outb ( dev , spic_dev . cur_ioport - > io1 . minimum + 4 ) ;
v1 = inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) ;
v2 = inb_p ( spic_dev . cur_ioport - > io1 . minimum ) ;
2008-01-14 12:05:41 +03:00
dprintk ( " sony_pic_call1(0x%.2x): 0x%.4x \n " , dev , ( v2 < < 8 ) | v1 ) ;
2007-04-09 21:26:03 +04:00
return v2 ;
}
static u8 sony_pic_call2 ( u8 dev , u8 fn )
{
u8 v1 ;
2007-08-12 11:20:28 +04:00
wait_on_command ( inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) & 2 ,
2007-04-09 21:26:03 +04:00
ITERATIONS_LONG ) ;
2007-08-12 11:20:28 +04:00
outb ( dev , spic_dev . cur_ioport - > io1 . minimum + 4 ) ;
wait_on_command ( inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) & 2 ,
2007-04-09 21:26:03 +04:00
ITERATIONS_LONG ) ;
2007-08-12 11:20:28 +04:00
outb ( fn , spic_dev . cur_ioport - > io1 . minimum ) ;
v1 = inb_p ( spic_dev . cur_ioport - > io1 . minimum ) ;
2008-01-14 12:05:41 +03:00
dprintk ( " sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x \n " , dev , fn , v1 ) ;
2007-04-09 21:26:03 +04:00
return v1 ;
}
2007-04-09 21:28:56 +04:00
static u8 sony_pic_call3 ( u8 dev , u8 fn , u8 v )
{
u8 v1 ;
2007-08-12 11:20:28 +04:00
wait_on_command ( inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) & 2 , ITERATIONS_LONG ) ;
outb ( dev , spic_dev . cur_ioport - > io1 . minimum + 4 ) ;
wait_on_command ( inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) & 2 , ITERATIONS_LONG ) ;
outb ( fn , spic_dev . cur_ioport - > io1 . minimum ) ;
wait_on_command ( inb_p ( spic_dev . cur_ioport - > io1 . minimum + 4 ) & 2 , ITERATIONS_LONG ) ;
outb ( v , spic_dev . cur_ioport - > io1 . minimum ) ;
v1 = inb_p ( spic_dev . cur_ioport - > io1 . minimum ) ;
2008-01-14 12:05:41 +03:00
dprintk ( " sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x \n " ,
dev , fn , v , v1 ) ;
2007-04-09 21:28:56 +04:00
return v1 ;
}
2008-01-14 12:05:45 +03:00
/*
* minidrivers for SPIC models
*/
static int type4_handle_irq ( const u8 data_mask , const u8 ev )
{
/*
* 0x31 could mean we have to take some extra action and wait for
* the next irq for some Type4 models , it will generate a new
* irq and we can read new data from the device :
* - 0x5c and 0x5f requires 0xA0
* - 0x61 requires 0xB3
*/
if ( data_mask = = 0x31 ) {
if ( ev = = 0x5c | | ev = = 0x5f )
sony_pic_call1 ( 0xA0 ) ;
else if ( ev = = 0x61 )
sony_pic_call1 ( 0xB3 ) ;
return 0 ;
}
return 1 ;
}
static struct device_ctrl spic_types [ ] = {
{
. model = SONYPI_DEVICE_TYPE1 ,
. handle_irq = NULL ,
. evport_offset = SONYPI_TYPE1_OFFSET ,
. event_types = type1_events ,
} ,
{
. model = SONYPI_DEVICE_TYPE2 ,
. handle_irq = NULL ,
. evport_offset = SONYPI_TYPE2_OFFSET ,
. event_types = type2_events ,
} ,
{
. model = SONYPI_DEVICE_TYPE3 ,
. handle_irq = NULL ,
. evport_offset = SONYPI_TYPE3_OFFSET ,
. event_types = type3_events ,
} ,
{
. model = SONYPI_DEVICE_TYPE4 ,
. handle_irq = type4_handle_irq ,
. evport_offset = SONYPI_TYPE4_OFFSET ,
. event_types = type4_events ,
} ,
} ;
static void sony_pic_detect_device_type ( struct sony_pic_dev * dev )
{
struct pci_dev * pcidev ;
pcidev = pci_get_device ( PCI_VENDOR_ID_INTEL ,
PCI_DEVICE_ID_INTEL_82371AB_3 , NULL ) ;
if ( pcidev ) {
dev - > control = & spic_types [ 0 ] ;
goto out ;
}
pcidev = pci_get_device ( PCI_VENDOR_ID_INTEL ,
PCI_DEVICE_ID_INTEL_ICH6_1 , NULL ) ;
if ( pcidev ) {
dev - > control = & spic_types [ 2 ] ;
goto out ;
}
pcidev = pci_get_device ( PCI_VENDOR_ID_INTEL ,
PCI_DEVICE_ID_INTEL_ICH7_1 , NULL ) ;
if ( pcidev ) {
dev - > control = & spic_types [ 3 ] ;
goto out ;
}
pcidev = pci_get_device ( PCI_VENDOR_ID_INTEL ,
PCI_DEVICE_ID_INTEL_ICH8_4 , NULL ) ;
if ( pcidev ) {
dev - > control = & spic_types [ 3 ] ;
goto out ;
}
/* default */
dev - > control = & spic_types [ 1 ] ;
out :
if ( pcidev )
pci_dev_put ( pcidev ) ;
printk ( KERN_INFO DRV_PFX " detected Type%d model \n " ,
dev - > control - > model = = SONYPI_DEVICE_TYPE1 ? 1 :
dev - > control - > model = = SONYPI_DEVICE_TYPE2 ? 2 :
dev - > control - > model = = SONYPI_DEVICE_TYPE3 ? 3 : 4 ) ;
}
2007-04-09 21:28:56 +04:00
/* camera tests and poweron/poweroff */
# define SONYPI_CAMERA_PICTURE 5
# define SONYPI_CAMERA_CONTROL 0x10
2007-04-28 18:34:22 +04:00
# define SONYPI_CAMERA_BRIGHTNESS 0
# define SONYPI_CAMERA_CONTRAST 1
# define SONYPI_CAMERA_HUE 2
# define SONYPI_CAMERA_COLOR 3
# define SONYPI_CAMERA_SHARPNESS 4
# define SONYPI_CAMERA_EXPOSURE_MASK 0xC
# define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3
# define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30
# define SONYPI_CAMERA_MUTE_MASK 0x40
/* the rest don't need a loop until not 0xff */
# define SONYPI_CAMERA_AGC 6
# define SONYPI_CAMERA_AGC_MASK 0x30
# define SONYPI_CAMERA_SHUTTER_MASK 0x7
# define SONYPI_CAMERA_SHUTDOWN_REQUEST 7
# define SONYPI_CAMERA_CONTROL 0x10
# define SONYPI_CAMERA_STATUS 7
# define SONYPI_CAMERA_STATUS_READY 0x2
# define SONYPI_CAMERA_STATUS_POSITION 0x4
# define SONYPI_DIRECTION_BACKWARDS 0x4
# define SONYPI_CAMERA_REVISION 8
# define SONYPI_CAMERA_ROMVERSION 9
2007-04-09 21:28:56 +04:00
2007-04-28 18:19:36 +04:00
static int __sony_pic_camera_ready ( void )
2007-04-09 21:28:56 +04:00
{
u8 v ;
v = sony_pic_call2 ( 0x8f , SONYPI_CAMERA_STATUS ) ;
return ( v ! = 0xff & & ( v & SONYPI_CAMERA_STATUS_READY ) ) ;
}
2007-04-28 18:34:22 +04:00
static int __sony_pic_camera_off ( void )
2007-04-09 21:28:56 +04:00
{
2007-04-28 18:18:45 +04:00
if ( ! camera ) {
printk ( KERN_WARNING DRV_PFX " camera control not enabled \n " ) ;
return - ENODEV ;
}
2007-04-09 21:28:56 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_PICTURE ,
SONYPI_CAMERA_MUTE_MASK ) ,
ITERATIONS_SHORT ) ;
2007-04-28 18:18:45 +04:00
if ( spic_dev . camera_power ) {
sony_pic_call2 ( 0x91 , 0 ) ;
spic_dev . camera_power = 0 ;
}
return 0 ;
2007-04-09 21:28:56 +04:00
}
2007-04-28 18:34:22 +04:00
static int __sony_pic_camera_on ( void )
2007-04-09 21:28:56 +04:00
{
2007-04-28 18:18:45 +04:00
int i , j , x ;
if ( ! camera ) {
printk ( KERN_WARNING DRV_PFX " camera control not enabled \n " ) ;
return - ENODEV ;
}
2007-04-09 21:28:56 +04:00
if ( spic_dev . camera_power )
2007-04-28 18:34:22 +04:00
return 0 ;
2007-04-09 21:28:56 +04:00
for ( j = 5 ; j > 0 ; j - - ) {
2007-04-28 18:18:45 +04:00
for ( x = 0 ; x < 100 & & sony_pic_call2 ( 0x91 , 0x1 ) ; x + + )
2007-04-09 21:28:56 +04:00
msleep ( 10 ) ;
sony_pic_call1 ( 0x93 ) ;
for ( i = 400 ; i > 0 ; i - - ) {
2007-04-28 18:19:36 +04:00
if ( __sony_pic_camera_ready ( ) )
2007-04-09 21:28:56 +04:00
break ;
msleep ( 10 ) ;
}
if ( i )
break ;
}
if ( j = = 0 ) {
2007-04-28 18:18:45 +04:00
printk ( KERN_WARNING DRV_PFX " failed to power on camera \n " ) ;
2007-04-28 18:34:22 +04:00
return - ENODEV ;
2007-04-09 21:28:56 +04:00
}
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_CONTROL ,
0x5a ) ,
ITERATIONS_SHORT ) ;
spic_dev . camera_power = 1 ;
2007-04-28 18:18:45 +04:00
return 0 ;
2007-04-09 21:28:56 +04:00
}
2007-04-28 18:34:22 +04:00
/* External camera command (exported to the motion eye v4l driver) */
int sony_pic_camera_command ( int command , u8 value )
{
if ( ! camera )
return - EIO ;
mutex_lock ( & spic_dev . lock ) ;
switch ( command ) {
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERA :
2007-04-28 18:34:22 +04:00
if ( value )
__sony_pic_camera_on ( ) ;
else
__sony_pic_camera_off ( ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_BRIGHTNESS , value ) ,
ITERATIONS_SHORT ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERACONTRAST :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_CONTRAST , value ) ,
ITERATIONS_SHORT ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERAHUE :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_HUE , value ) ,
ITERATIONS_SHORT ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERACOLOR :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_COLOR , value ) ,
ITERATIONS_SHORT ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERASHARPNESS :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_SHARPNESS , value ) ,
ITERATIONS_SHORT ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERAPICTURE :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_PICTURE , value ) ,
ITERATIONS_SHORT ) ;
break ;
2007-04-28 18:34:36 +04:00
case SONY_PIC_COMMAND_SETCAMERAAGC :
2007-04-28 18:34:22 +04:00
wait_on_command ( sony_pic_call3 ( 0x90 , SONYPI_CAMERA_AGC , value ) ,
ITERATIONS_SHORT ) ;
break ;
default :
printk ( KERN_ERR DRV_PFX " sony_pic_camera_command invalid: %d \n " ,
command ) ;
break ;
}
mutex_unlock ( & spic_dev . lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( sony_pic_camera_command ) ;
2007-04-28 18:21:42 +04:00
/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
static void sony_pic_set_wwanpower ( u8 state )
{
state = ! ! state ;
mutex_lock ( & spic_dev . lock ) ;
if ( spic_dev . wwan_power = = state ) {
mutex_unlock ( & spic_dev . lock ) ;
return ;
}
sony_pic_call2 ( 0xB0 , state ) ;
spic_dev . wwan_power = state ;
mutex_unlock ( & spic_dev . lock ) ;
}
static ssize_t sony_pic_wwanpower_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buffer , size_t count )
{
unsigned long value ;
if ( count > 31 )
return - EINVAL ;
value = simple_strtoul ( buffer , NULL , 10 ) ;
sony_pic_set_wwanpower ( value ) ;
return count ;
}
static ssize_t sony_pic_wwanpower_show ( struct device * dev ,
struct device_attribute * attr , char * buffer )
{
ssize_t count ;
mutex_lock ( & spic_dev . lock ) ;
count = snprintf ( buffer , PAGE_SIZE , " %d \n " , spic_dev . wwan_power ) ;
mutex_unlock ( & spic_dev . lock ) ;
return count ;
}
2007-04-09 21:28:56 +04:00
/* bluetooth subsystem power state */
2007-04-28 18:19:36 +04:00
static void __sony_pic_set_bluetoothpower ( u8 state )
2007-04-09 21:28:56 +04:00
{
state = ! ! state ;
if ( spic_dev . bluetooth_power = = state )
return ;
sony_pic_call2 ( 0x96 , state ) ;
sony_pic_call1 ( 0x82 ) ;
spic_dev . bluetooth_power = state ;
}
static ssize_t sony_pic_bluetoothpower_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buffer , size_t count )
{
unsigned long value ;
if ( count > 31 )
return - EINVAL ;
value = simple_strtoul ( buffer , NULL , 10 ) ;
2007-04-28 18:19:36 +04:00
mutex_lock ( & spic_dev . lock ) ;
__sony_pic_set_bluetoothpower ( value ) ;
mutex_unlock ( & spic_dev . lock ) ;
2007-04-09 21:28:56 +04:00
return count ;
}
static ssize_t sony_pic_bluetoothpower_show ( struct device * dev ,
struct device_attribute * attr , char * buffer )
{
2007-04-28 18:19:36 +04:00
ssize_t count = 0 ;
mutex_lock ( & spic_dev . lock ) ;
count = snprintf ( buffer , PAGE_SIZE , " %d \n " , spic_dev . bluetooth_power ) ;
mutex_unlock ( & spic_dev . lock ) ;
return count ;
2007-04-09 21:28:56 +04:00
}
/* fan speed */
/* FAN0 information (reverse engineered from ACPI tables) */
# define SONY_PIC_FAN0_STATUS 0x93
2007-04-09 21:31:25 +04:00
static int sony_pic_set_fanspeed ( unsigned long value )
{
return ec_write ( SONY_PIC_FAN0_STATUS , value ) ;
}
static int sony_pic_get_fanspeed ( u8 * value )
{
return ec_read ( SONY_PIC_FAN0_STATUS , value ) ;
}
2007-04-09 21:28:56 +04:00
static ssize_t sony_pic_fanspeed_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buffer , size_t count )
{
unsigned long value ;
if ( count > 31 )
return - EINVAL ;
value = simple_strtoul ( buffer , NULL , 10 ) ;
2007-04-09 21:31:25 +04:00
if ( sony_pic_set_fanspeed ( value ) )
2007-04-09 21:28:56 +04:00
return - EIO ;
return count ;
}
static ssize_t sony_pic_fanspeed_show ( struct device * dev ,
struct device_attribute * attr , char * buffer )
{
u8 value = 0 ;
2007-04-09 21:31:25 +04:00
if ( sony_pic_get_fanspeed ( & value ) )
2007-04-09 21:28:56 +04:00
return - EIO ;
return snprintf ( buffer , PAGE_SIZE , " %d \n " , value ) ;
}
# define SPIC_ATTR(_name, _mode) \
struct device_attribute spic_attr_ # # _name = __ATTR ( _name , \
_mode , sony_pic_ # # _name # # _show , \
sony_pic_ # # _name # # _store )
static SPIC_ATTR ( bluetoothpower , 0644 ) ;
2007-04-28 18:21:42 +04:00
static SPIC_ATTR ( wwanpower , 0644 ) ;
2007-04-09 21:28:56 +04:00
static SPIC_ATTR ( fanspeed , 0644 ) ;
static struct attribute * spic_attributes [ ] = {
& spic_attr_bluetoothpower . attr ,
2007-04-28 18:21:42 +04:00
& spic_attr_wwanpower . attr ,
2007-04-09 21:28:56 +04:00
& spic_attr_fanspeed . attr ,
NULL
} ;
static struct attribute_group spic_attribute_group = {
. attrs = spic_attributes
} ;
2007-04-09 21:31:25 +04:00
/******** SONYPI compatibility **********/
2007-05-01 06:19:53 +04:00
# ifdef CONFIG_SONYPI_COMPAT
2007-04-09 21:31:25 +04:00
/* battery / brightness / temperature addresses */
# define SONYPI_BAT_FLAGS 0x81
# define SONYPI_LCD_LIGHT 0x96
# define SONYPI_BAT1_PCTRM 0xa0
# define SONYPI_BAT1_LEFT 0xa2
# define SONYPI_BAT1_MAXRT 0xa4
# define SONYPI_BAT2_PCTRM 0xa8
# define SONYPI_BAT2_LEFT 0xaa
# define SONYPI_BAT2_MAXRT 0xac
# define SONYPI_BAT1_MAXTK 0xb0
# define SONYPI_BAT1_FULL 0xb2
# define SONYPI_BAT2_MAXTK 0xb8
# define SONYPI_BAT2_FULL 0xba
# define SONYPI_TEMP_STATUS 0xC1
struct sonypi_compat_s {
struct fasync_struct * fifo_async ;
struct kfifo * fifo ;
spinlock_t fifo_lock ;
wait_queue_head_t fifo_proc_list ;
atomic_t open_count ;
} ;
static struct sonypi_compat_s sonypi_compat = {
. open_count = ATOMIC_INIT ( 0 ) ,
} ;
static int sonypi_misc_fasync ( int fd , struct file * filp , int on )
{
int retval ;
retval = fasync_helper ( fd , filp , on , & sonypi_compat . fifo_async ) ;
if ( retval < 0 )
return retval ;
return 0 ;
}
static int sonypi_misc_release ( struct inode * inode , struct file * file )
{
atomic_dec ( & sonypi_compat . open_count ) ;
return 0 ;
}
static int sonypi_misc_open ( struct inode * inode , struct file * file )
{
/* Flush input queue on first open */
2008-05-20 21:16:45 +04:00
lock_kernel ( ) ;
2007-04-09 21:31:25 +04:00
if ( atomic_inc_return ( & sonypi_compat . open_count ) = = 1 )
kfifo_reset ( sonypi_compat . fifo ) ;
2008-05-20 21:16:45 +04:00
unlock_kernel ( ) ;
2007-04-09 21:31:25 +04:00
return 0 ;
}
static ssize_t sonypi_misc_read ( struct file * file , char __user * buf ,
size_t count , loff_t * pos )
{
ssize_t ret ;
unsigned char c ;
if ( ( kfifo_len ( sonypi_compat . fifo ) = = 0 ) & &
( file - > f_flags & O_NONBLOCK ) )
return - EAGAIN ;
ret = wait_event_interruptible ( sonypi_compat . fifo_proc_list ,
kfifo_len ( sonypi_compat . fifo ) ! = 0 ) ;
if ( ret )
return ret ;
while ( ret < count & &
( kfifo_get ( sonypi_compat . fifo , & c , sizeof ( c ) ) = = sizeof ( c ) ) ) {
if ( put_user ( c , buf + + ) )
return - EFAULT ;
ret + + ;
}
if ( ret > 0 ) {
struct inode * inode = file - > f_path . dentry - > d_inode ;
inode - > i_atime = current_fs_time ( inode - > i_sb ) ;
}
return ret ;
}
static unsigned int sonypi_misc_poll ( struct file * file , poll_table * wait )
{
poll_wait ( file , & sonypi_compat . fifo_proc_list , wait ) ;
if ( kfifo_len ( sonypi_compat . fifo ) )
return POLLIN | POLLRDNORM ;
return 0 ;
}
static int ec_read16 ( u8 addr , u16 * value )
{
u8 val_lb , val_hb ;
if ( ec_read ( addr , & val_lb ) )
return - 1 ;
if ( ec_read ( addr + 1 , & val_hb ) )
return - 1 ;
* value = val_lb | ( val_hb < < 8 ) ;
return 0 ;
}
static int sonypi_misc_ioctl ( struct inode * ip , struct file * fp ,
unsigned int cmd , unsigned long arg )
{
int ret = 0 ;
void __user * argp = ( void __user * ) arg ;
u8 val8 ;
u16 val16 ;
int value ;
2007-04-28 18:19:36 +04:00
mutex_lock ( & spic_dev . lock ) ;
2007-04-09 21:31:25 +04:00
switch ( cmd ) {
case SONYPI_IOCGBRT :
if ( sony_backlight_device = = NULL ) {
ret = - EIO ;
break ;
}
if ( acpi_callgetfunc ( sony_nc_acpi_handle , " GBRT " , & value ) ) {
ret = - EIO ;
break ;
}
val8 = ( ( value & 0xff ) - 1 ) < < 5 ;
if ( copy_to_user ( argp , & val8 , sizeof ( val8 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCSBRT :
if ( sony_backlight_device = = NULL ) {
ret = - EIO ;
break ;
}
if ( copy_from_user ( & val8 , argp , sizeof ( val8 ) ) ) {
ret = - EFAULT ;
break ;
}
if ( acpi_callsetfunc ( sony_nc_acpi_handle , " SBRT " ,
( val8 > > 5 ) + 1 , NULL ) ) {
ret = - EIO ;
break ;
}
/* sync the backlight device status */
sony_backlight_device - > props . brightness =
sony_backlight_get_brightness ( sony_backlight_device ) ;
break ;
case SONYPI_IOCGBAT1CAP :
if ( ec_read16 ( SONYPI_BAT1_FULL , & val16 ) ) {
ret = - EIO ;
break ;
}
if ( copy_to_user ( argp , & val16 , sizeof ( val16 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCGBAT1REM :
if ( ec_read16 ( SONYPI_BAT1_LEFT , & val16 ) ) {
ret = - EIO ;
break ;
}
if ( copy_to_user ( argp , & val16 , sizeof ( val16 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCGBAT2CAP :
if ( ec_read16 ( SONYPI_BAT2_FULL , & val16 ) ) {
ret = - EIO ;
break ;
}
if ( copy_to_user ( argp , & val16 , sizeof ( val16 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCGBAT2REM :
if ( ec_read16 ( SONYPI_BAT2_LEFT , & val16 ) ) {
ret = - EIO ;
break ;
}
if ( copy_to_user ( argp , & val16 , sizeof ( val16 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCGBATFLAGS :
if ( ec_read ( SONYPI_BAT_FLAGS , & val8 ) ) {
ret = - EIO ;
break ;
}
val8 & = 0x07 ;
if ( copy_to_user ( argp , & val8 , sizeof ( val8 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCGBLUE :
val8 = spic_dev . bluetooth_power ;
if ( copy_to_user ( argp , & val8 , sizeof ( val8 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCSBLUE :
if ( copy_from_user ( & val8 , argp , sizeof ( val8 ) ) ) {
ret = - EFAULT ;
break ;
}
2007-04-28 18:19:36 +04:00
__sony_pic_set_bluetoothpower ( val8 ) ;
2007-04-09 21:31:25 +04:00
break ;
/* FAN Controls */
case SONYPI_IOCGFAN :
if ( sony_pic_get_fanspeed ( & val8 ) ) {
ret = - EIO ;
break ;
}
if ( copy_to_user ( argp , & val8 , sizeof ( val8 ) ) )
ret = - EFAULT ;
break ;
case SONYPI_IOCSFAN :
if ( copy_from_user ( & val8 , argp , sizeof ( val8 ) ) ) {
ret = - EFAULT ;
break ;
}
if ( sony_pic_set_fanspeed ( val8 ) )
ret = - EIO ;
break ;
/* GET Temperature (useful under APM) */
case SONYPI_IOCGTEMP :
if ( ec_read ( SONYPI_TEMP_STATUS , & val8 ) ) {
ret = - EIO ;
break ;
}
if ( copy_to_user ( argp , & val8 , sizeof ( val8 ) ) )
ret = - EFAULT ;
break ;
default :
ret = - EINVAL ;
}
2007-04-28 18:19:36 +04:00
mutex_unlock ( & spic_dev . lock ) ;
2007-04-09 21:31:25 +04:00
return ret ;
}
static const struct file_operations sonypi_misc_fops = {
. owner = THIS_MODULE ,
. read = sonypi_misc_read ,
. poll = sonypi_misc_poll ,
. open = sonypi_misc_open ,
. release = sonypi_misc_release ,
. fasync = sonypi_misc_fasync ,
. ioctl = sonypi_misc_ioctl ,
} ;
static struct miscdevice sonypi_misc_device = {
. minor = MISC_DYNAMIC_MINOR ,
. name = " sonypi " ,
. fops = & sonypi_misc_fops ,
} ;
static void sonypi_compat_report_event ( u8 event )
{
kfifo_put ( sonypi_compat . fifo , ( unsigned char * ) & event , sizeof ( event ) ) ;
kill_fasync ( & sonypi_compat . fifo_async , SIGIO , POLL_IN ) ;
wake_up_interruptible ( & sonypi_compat . fifo_proc_list ) ;
}
static int sonypi_compat_init ( void )
{
int error ;
spin_lock_init ( & sonypi_compat . fifo_lock ) ;
sonypi_compat . fifo = kfifo_alloc ( SONY_LAPTOP_BUF_SIZE , GFP_KERNEL ,
& sonypi_compat . fifo_lock ) ;
if ( IS_ERR ( sonypi_compat . fifo ) ) {
printk ( KERN_ERR DRV_PFX " kfifo_alloc failed \n " ) ;
return PTR_ERR ( sonypi_compat . fifo ) ;
}
init_waitqueue_head ( & sonypi_compat . fifo_proc_list ) ;
if ( minor ! = - 1 )
sonypi_misc_device . minor = minor ;
error = misc_register ( & sonypi_misc_device ) ;
if ( error ) {
printk ( KERN_ERR DRV_PFX " misc_register failed \n " ) ;
goto err_free_kfifo ;
}
if ( minor = = - 1 )
2007-04-28 18:18:45 +04:00
printk ( KERN_INFO DRV_PFX " device allocated minor is %d \n " ,
2007-04-09 21:31:25 +04:00
sonypi_misc_device . minor ) ;
return 0 ;
err_free_kfifo :
kfifo_free ( sonypi_compat . fifo ) ;
return error ;
}
static void sonypi_compat_exit ( void )
{
misc_deregister ( & sonypi_misc_device ) ;
kfifo_free ( sonypi_compat . fifo ) ;
}
# else
static int sonypi_compat_init ( void ) { return 0 ; }
static void sonypi_compat_exit ( void ) { }
static void sonypi_compat_report_event ( u8 event ) { }
2007-05-01 06:19:53 +04:00
# endif /* CONFIG_SONYPI_COMPAT */
2007-04-09 21:31:25 +04:00
2007-04-09 12:19:08 +04:00
/*
2007-04-09 21:26:03 +04:00
* ACPI callbacks
2007-04-09 12:19:08 +04:00
*/
2007-04-09 21:26:03 +04:00
static acpi_status
sony_pic_read_possible_resource ( struct acpi_resource * resource , void * context )
{
u32 i ;
struct sony_pic_dev * dev = ( struct sony_pic_dev * ) context ;
switch ( resource - > type ) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT :
2007-08-12 11:20:28 +04:00
{
/* start IO enumeration */
struct sony_pic_ioport * ioport = kzalloc ( sizeof ( * ioport ) , GFP_KERNEL ) ;
if ( ! ioport )
return AE_ERROR ;
list_add ( & ioport - > list , & dev - > ioports ) ;
return AE_OK ;
}
2007-04-09 21:26:03 +04:00
case ACPI_RESOURCE_TYPE_END_DEPENDENT :
2007-08-12 11:20:28 +04:00
/* end IO enumeration */
2007-04-09 21:26:03 +04:00
return AE_OK ;
case ACPI_RESOURCE_TYPE_IRQ :
{
struct acpi_resource_irq * p = & resource - > data . irq ;
struct sony_pic_irq * interrupt = NULL ;
if ( ! p | | ! p - > interrupt_count ) {
/*
* IRQ descriptors may have no IRQ # bits set ,
* particularly those those w / _STA disabled
*/
dprintk ( " Blank IRQ resource \n " ) ;
return AE_OK ;
}
for ( i = 0 ; i < p - > interrupt_count ; i + + ) {
if ( ! p - > interrupts [ i ] ) {
printk ( KERN_WARNING DRV_PFX
" Invalid IRQ %d \n " ,
p - > interrupts [ i ] ) ;
continue ;
}
interrupt = kzalloc ( sizeof ( * interrupt ) ,
GFP_KERNEL ) ;
if ( ! interrupt )
return AE_ERROR ;
2007-08-12 11:20:28 +04:00
list_add ( & interrupt - > list , & dev - > interrupts ) ;
2007-04-09 21:26:03 +04:00
interrupt - > irq . triggering = p - > triggering ;
interrupt - > irq . polarity = p - > polarity ;
interrupt - > irq . sharable = p - > sharable ;
interrupt - > irq . interrupt_count = 1 ;
interrupt - > irq . interrupts [ 0 ] = p - > interrupts [ i ] ;
}
return AE_OK ;
}
case ACPI_RESOURCE_TYPE_IO :
{
struct acpi_resource_io * io = & resource - > data . io ;
2007-08-12 11:20:28 +04:00
struct sony_pic_ioport * ioport =
list_first_entry ( & dev - > ioports , struct sony_pic_ioport , list ) ;
2007-04-09 21:26:03 +04:00
if ( ! io ) {
dprintk ( " Blank IO resource \n " ) ;
return AE_OK ;
}
2007-08-12 11:20:28 +04:00
if ( ! ioport - > io1 . minimum ) {
memcpy ( & ioport - > io1 , io , sizeof ( * io ) ) ;
dprintk ( " IO1 at 0x%.4x (0x%.2x) \n " , ioport - > io1 . minimum ,
ioport - > io1 . address_length ) ;
}
else if ( ! ioport - > io2 . minimum ) {
memcpy ( & ioport - > io2 , io , sizeof ( * io ) ) ;
dprintk ( " IO2 at 0x%.4x (0x%.2x) \n " , ioport - > io2 . minimum ,
ioport - > io2 . address_length ) ;
}
else {
printk ( KERN_ERR DRV_PFX " Unknown SPIC Type, more than 2 IO Ports \n " ) ;
2007-04-09 21:26:03 +04:00
return AE_ERROR ;
2007-08-12 11:20:28 +04:00
}
2007-04-09 21:26:03 +04:00
return AE_OK ;
}
default :
dprintk ( " Resource %d isn't an IRQ nor an IO port \n " ,
resource - > type ) ;
case ACPI_RESOURCE_TYPE_END_TAG :
return AE_OK ;
}
return AE_CTRL_TERMINATE ;
}
static int sony_pic_possible_resources ( struct acpi_device * device )
{
int result = 0 ;
acpi_status status = AE_OK ;
if ( ! device )
return - EINVAL ;
/* get device status */
/* see acpi_pci_link_get_current acpi_pci_link_get_possible */
dprintk ( " Evaluating _STA \n " ) ;
result = acpi_bus_get_status ( device ) ;
if ( result ) {
printk ( KERN_WARNING DRV_PFX " Unable to read status \n " ) ;
goto end ;
}
if ( ! device - > status . enabled )
dprintk ( " Device disabled \n " ) ;
else
dprintk ( " Device enabled \n " ) ;
/*
* Query and parse ' method '
*/
dprintk ( " Evaluating %s \n " , METHOD_NAME__PRS ) ;
status = acpi_walk_resources ( device - > handle , METHOD_NAME__PRS ,
sony_pic_read_possible_resource , & spic_dev ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_WARNING DRV_PFX
" Failure evaluating %s \n " ,
METHOD_NAME__PRS ) ;
result = - ENODEV ;
}
end :
return result ;
}
/*
* Disable the spic device by calling its _DIS method
*/
static int sony_pic_disable ( struct acpi_device * device )
{
2008-10-30 00:01:03 +03:00
acpi_status ret = acpi_evaluate_object ( device - > handle , " _DIS " , NULL ,
NULL ) ;
if ( ACPI_FAILURE ( ret ) & & ret ! = AE_NOT_FOUND )
2007-04-09 21:26:03 +04:00
return - ENXIO ;
dprintk ( " Device disabled \n " ) ;
return 0 ;
}
/*
* Based on drivers / acpi / pci_link . c : acpi_pci_link_set
*
* Call _SRS to set current resources
*/
static int sony_pic_enable ( struct acpi_device * device ,
struct sony_pic_ioport * ioport , struct sony_pic_irq * irq )
{
acpi_status status ;
int result = 0 ;
2007-08-12 11:20:28 +04:00
/* Type 1 resource layout is:
* IO
* IO
* IRQNoFlags
* End
*
* Type 2 and 3 resource layout is :
* IO
* IRQNoFlags
* End
*/
2007-04-09 21:26:03 +04:00
struct {
2007-08-12 11:20:28 +04:00
struct acpi_resource res1 ;
struct acpi_resource res2 ;
struct acpi_resource res3 ;
struct acpi_resource res4 ;
2007-04-09 21:26:03 +04:00
} * resource ;
struct acpi_buffer buffer = { 0 , NULL } ;
if ( ! ioport | | ! irq )
return - EINVAL ;
/* init acpi_buffer */
resource = kzalloc ( sizeof ( * resource ) + 1 , GFP_KERNEL ) ;
if ( ! resource )
return - ENOMEM ;
buffer . length = sizeof ( * resource ) + 1 ;
buffer . pointer = resource ;
2007-08-12 11:20:28 +04:00
/* setup Type 1 resources */
2008-01-14 12:05:43 +03:00
if ( spic_dev . control - > model = = SONYPI_DEVICE_TYPE1 ) {
2007-04-09 21:26:03 +04:00
2007-08-12 11:20:28 +04:00
/* setup io resources */
resource - > res1 . type = ACPI_RESOURCE_TYPE_IO ;
resource - > res1 . length = sizeof ( struct acpi_resource ) ;
memcpy ( & resource - > res1 . data . io , & ioport - > io1 ,
sizeof ( struct acpi_resource_io ) ) ;
2007-04-09 21:26:03 +04:00
2007-08-12 11:20:28 +04:00
resource - > res2 . type = ACPI_RESOURCE_TYPE_IO ;
resource - > res2 . length = sizeof ( struct acpi_resource ) ;
memcpy ( & resource - > res2 . data . io , & ioport - > io2 ,
sizeof ( struct acpi_resource_io ) ) ;
/* setup irq resource */
resource - > res3 . type = ACPI_RESOURCE_TYPE_IRQ ;
resource - > res3 . length = sizeof ( struct acpi_resource ) ;
memcpy ( & resource - > res3 . data . irq , & irq - > irq ,
sizeof ( struct acpi_resource_irq ) ) ;
/* we requested a shared irq */
resource - > res3 . data . irq . sharable = ACPI_SHARED ;
resource - > res4 . type = ACPI_RESOURCE_TYPE_END_TAG ;
}
/* setup Type 2/3 resources */
else {
/* setup io resource */
resource - > res1 . type = ACPI_RESOURCE_TYPE_IO ;
resource - > res1 . length = sizeof ( struct acpi_resource ) ;
memcpy ( & resource - > res1 . data . io , & ioport - > io1 ,
sizeof ( struct acpi_resource_io ) ) ;
/* setup irq resource */
resource - > res2 . type = ACPI_RESOURCE_TYPE_IRQ ;
resource - > res2 . length = sizeof ( struct acpi_resource ) ;
memcpy ( & resource - > res2 . data . irq , & irq - > irq ,
sizeof ( struct acpi_resource_irq ) ) ;
/* we requested a shared irq */
resource - > res2 . data . irq . sharable = ACPI_SHARED ;
resource - > res3 . type = ACPI_RESOURCE_TYPE_END_TAG ;
}
2007-04-09 21:26:03 +04:00
/* Attempt to set the resource */
dprintk ( " Evaluating _SRS \n " ) ;
status = acpi_set_current_resources ( device - > handle , & buffer ) ;
/* check for total failure */
if ( ACPI_FAILURE ( status ) ) {
2007-08-12 11:20:28 +04:00
printk ( KERN_ERR DRV_PFX " Error evaluating _SRS \n " ) ;
2007-04-09 21:26:03 +04:00
result = - ENODEV ;
goto end ;
}
/* Necessary device initializations calls (from sonypi) */
sony_pic_call1 ( 0x82 ) ;
sony_pic_call2 ( 0x81 , 0xff ) ;
sony_pic_call1 ( compat ? 0x92 : 0x82 ) ;
end :
kfree ( resource ) ;
return result ;
}
/*****************
*
* ISR : some event is available
*
* * * * * * * * * * * * * * * * */
static irqreturn_t sony_pic_irq ( int irq , void * dev_id )
{
int i , j ;
u8 ev = 0 ;
u8 data_mask = 0 ;
u8 device_event = 0 ;
struct sony_pic_dev * dev = ( struct sony_pic_dev * ) dev_id ;
2007-08-12 11:20:28 +04:00
ev = inb_p ( dev - > cur_ioport - > io1 . minimum ) ;
if ( dev - > cur_ioport - > io2 . minimum )
data_mask = inb_p ( dev - > cur_ioport - > io2 . minimum ) ;
else
2008-01-14 12:05:43 +03:00
data_mask = inb_p ( dev - > cur_ioport - > io1 . minimum +
dev - > control - > evport_offset ) ;
2007-04-09 21:26:03 +04:00
2007-07-15 21:34:39 +04:00
dprintk ( " event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x) \n " ,
2008-01-14 12:05:43 +03:00
ev , data_mask , dev - > cur_ioport - > io1 . minimum ,
dev - > control - > evport_offset ) ;
2007-04-09 21:26:03 +04:00
if ( ev = = 0x00 | | ev = = 0xff )
return IRQ_HANDLED ;
2008-01-14 12:05:43 +03:00
for ( i = 0 ; dev - > control - > event_types [ i ] . mask ; i + + ) {
2007-04-09 21:26:03 +04:00
2008-01-14 12:05:43 +03:00
if ( ( data_mask & dev - > control - > event_types [ i ] . data ) ! =
dev - > control - > event_types [ i ] . data )
2007-04-09 21:26:03 +04:00
continue ;
2008-01-14 12:05:43 +03:00
if ( ! ( mask & dev - > control - > event_types [ i ] . mask ) )
2007-04-09 21:26:03 +04:00
continue ;
2008-01-14 12:05:43 +03:00
for ( j = 0 ; dev - > control - > event_types [ i ] . events [ j ] . event ; j + + ) {
if ( ev = = dev - > control - > event_types [ i ] . events [ j ] . data ) {
2007-04-09 21:26:03 +04:00
device_event =
2008-01-14 12:05:43 +03:00
dev - > control - >
event_types [ i ] . events [ j ] . event ;
2007-04-09 21:26:03 +04:00
goto found ;
}
}
}
2008-01-14 12:05:45 +03:00
/* Still not able to decode the event try to pass
* it over to the minidriver
*/
if ( dev - > control - > handle_irq & &
dev - > control - > handle_irq ( data_mask , ev ) = = 0 )
return IRQ_HANDLED ;
2008-01-14 12:05:43 +03:00
dprintk ( " unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x) \n " ,
ev , data_mask , dev - > cur_ioport - > io1 . minimum ,
dev - > control - > evport_offset ) ;
2007-04-09 21:26:03 +04:00
return IRQ_HANDLED ;
found :
2007-04-09 12:19:08 +04:00
sony_laptop_report_input_event ( device_event ) ;
2008-01-14 12:05:43 +03:00
acpi_bus_generate_proc_event ( dev - > acpi_dev , 1 , device_event ) ;
2007-04-09 21:31:25 +04:00
sonypi_compat_report_event ( device_event ) ;
2007-04-09 21:26:03 +04:00
return IRQ_HANDLED ;
}
/*****************
*
* ACPI driver
*
* * * * * * * * * * * * * * * * */
static int sony_pic_remove ( struct acpi_device * device , int type )
{
struct sony_pic_ioport * io , * tmp_io ;
struct sony_pic_irq * irq , * tmp_irq ;
if ( sony_pic_disable ( device ) ) {
printk ( KERN_ERR DRV_PFX " Couldn't disable device. \n " ) ;
return - ENXIO ;
}
free_irq ( spic_dev . cur_irq - > irq . interrupts [ 0 ] , & spic_dev ) ;
2007-08-12 11:20:28 +04:00
release_region ( spic_dev . cur_ioport - > io1 . minimum ,
spic_dev . cur_ioport - > io1 . address_length ) ;
if ( spic_dev . cur_ioport - > io2 . minimum )
release_region ( spic_dev . cur_ioport - > io2 . minimum ,
spic_dev . cur_ioport - > io2 . address_length ) ;
2007-04-09 21:26:03 +04:00
2007-08-12 11:20:27 +04:00
sonypi_compat_exit ( ) ;
2007-04-09 12:19:08 +04:00
sony_laptop_remove_input ( ) ;
2007-04-09 21:26:03 +04:00
2007-04-09 21:28:56 +04:00
/* pf attrs */
sysfs_remove_group ( & sony_pf_device - > dev . kobj , & spic_attribute_group ) ;
sony_pf_remove ( ) ;
2007-04-09 21:26:03 +04:00
list_for_each_entry_safe ( io , tmp_io , & spic_dev . ioports , list ) {
list_del ( & io - > list ) ;
kfree ( io ) ;
}
list_for_each_entry_safe ( irq , tmp_irq , & spic_dev . interrupts , list ) {
list_del ( & irq - > list ) ;
kfree ( irq ) ;
}
spic_dev . cur_ioport = NULL ;
spic_dev . cur_irq = NULL ;
2007-04-09 21:31:06 +04:00
dprintk ( SONY_PIC_DRIVER_NAME " removed. \n " ) ;
2007-04-09 21:26:03 +04:00
return 0 ;
}
static int sony_pic_add ( struct acpi_device * device )
{
int result ;
struct sony_pic_ioport * io , * tmp_io ;
struct sony_pic_irq * irq , * tmp_irq ;
2007-04-09 21:31:06 +04:00
printk ( KERN_INFO DRV_PFX " %s v%s. \n " ,
SONY_PIC_DRIVER_NAME , SONY_LAPTOP_DRIVER_VERSION ) ;
2007-04-09 21:26:03 +04:00
spic_dev . acpi_dev = device ;
strcpy ( acpi_device_class ( device ) , " sony/hotkey " ) ;
2008-01-14 12:05:43 +03:00
sony_pic_detect_device_type ( & spic_dev ) ;
2007-04-28 18:19:36 +04:00
mutex_init ( & spic_dev . lock ) ;
2007-04-09 21:26:03 +04:00
/* read _PRS resources */
result = sony_pic_possible_resources ( device ) ;
if ( result ) {
printk ( KERN_ERR DRV_PFX
" Unabe to read possible resources. \n " ) ;
goto err_free_resources ;
}
/* setup input devices and helper fifo */
2007-11-21 22:15:53 +03:00
result = sony_laptop_setup_input ( device ) ;
2007-04-09 21:26:03 +04:00
if ( result ) {
printk ( KERN_ERR DRV_PFX
" Unabe to create input devices. \n " ) ;
goto err_free_resources ;
}
2007-08-12 11:20:27 +04:00
if ( sonypi_compat_init ( ) )
goto err_remove_input ;
2007-04-09 21:26:03 +04:00
/* request io port */
2007-08-12 11:20:28 +04:00
list_for_each_entry_reverse ( io , & spic_dev . ioports , list ) {
if ( request_region ( io - > io1 . minimum , io - > io1 . address_length ,
2007-04-09 21:26:03 +04:00
" Sony Programable I/O Device " ) ) {
2007-08-12 11:20:28 +04:00
dprintk ( " I/O port1: 0x%.4x (0x%.4x) + 0x%.2x \n " ,
io - > io1 . minimum , io - > io1 . maximum ,
io - > io1 . address_length ) ;
/* Type 1 have 2 ioports */
if ( io - > io2 . minimum ) {
if ( request_region ( io - > io2 . minimum ,
io - > io2 . address_length ,
" Sony Programable I/O Device " ) ) {
dprintk ( " I/O port2: 0x%.4x (0x%.4x) + 0x%.2x \n " ,
io - > io2 . minimum , io - > io2 . maximum ,
io - > io2 . address_length ) ;
spic_dev . cur_ioport = io ;
break ;
}
else {
dprintk ( " Unable to get I/O port2: "
" 0x%.4x (0x%.4x) + 0x%.2x \n " ,
io - > io2 . minimum , io - > io2 . maximum ,
io - > io2 . address_length ) ;
release_region ( io - > io1 . minimum ,
io - > io1 . address_length ) ;
}
}
else {
spic_dev . cur_ioport = io ;
break ;
}
2007-04-09 21:26:03 +04:00
}
}
if ( ! spic_dev . cur_ioport ) {
printk ( KERN_ERR DRV_PFX " Failed to request_region. \n " ) ;
result = - ENODEV ;
2007-08-12 11:20:27 +04:00
goto err_remove_compat ;
2007-04-09 21:26:03 +04:00
}
/* request IRQ */
2007-08-12 11:20:28 +04:00
list_for_each_entry_reverse ( irq , & spic_dev . interrupts , list ) {
2007-04-09 21:26:03 +04:00
if ( ! request_irq ( irq - > irq . interrupts [ 0 ] , sony_pic_irq ,
IRQF_SHARED , " sony-laptop " , & spic_dev ) ) {
dprintk ( " IRQ: %d - triggering: %d - "
" polarity: %d - shr: %d \n " ,
irq - > irq . interrupts [ 0 ] ,
irq - > irq . triggering ,
irq - > irq . polarity ,
irq - > irq . sharable ) ;
spic_dev . cur_irq = irq ;
break ;
}
}
if ( ! spic_dev . cur_irq ) {
printk ( KERN_ERR DRV_PFX " Failed to request_irq. \n " ) ;
result = - ENODEV ;
goto err_release_region ;
}
/* set resource status _SRS */
result = sony_pic_enable ( device , spic_dev . cur_ioport , spic_dev . cur_irq ) ;
if ( result ) {
printk ( KERN_ERR DRV_PFX " Couldn't enable device. \n " ) ;
goto err_free_irq ;
}
2007-04-09 21:28:56 +04:00
spic_dev . bluetooth_power = - 1 ;
/* create device attributes */
result = sony_pf_add ( ) ;
if ( result )
goto err_disable_device ;
result = sysfs_create_group ( & sony_pf_device - > dev . kobj , & spic_attribute_group ) ;
if ( result )
goto err_remove_pf ;
2007-04-09 21:26:03 +04:00
return 0 ;
2007-04-09 21:28:56 +04:00
err_remove_pf :
sony_pf_remove ( ) ;
err_disable_device :
sony_pic_disable ( device ) ;
2007-04-09 21:26:03 +04:00
err_free_irq :
free_irq ( spic_dev . cur_irq - > irq . interrupts [ 0 ] , & spic_dev ) ;
err_release_region :
2007-08-12 11:20:28 +04:00
release_region ( spic_dev . cur_ioport - > io1 . minimum ,
spic_dev . cur_ioport - > io1 . address_length ) ;
if ( spic_dev . cur_ioport - > io2 . minimum )
release_region ( spic_dev . cur_ioport - > io2 . minimum ,
spic_dev . cur_ioport - > io2 . address_length ) ;
2007-04-09 21:26:03 +04:00
2007-08-12 11:20:27 +04:00
err_remove_compat :
sonypi_compat_exit ( ) ;
2007-04-09 21:26:03 +04:00
err_remove_input :
2007-04-09 12:19:08 +04:00
sony_laptop_remove_input ( ) ;
2007-04-09 21:26:03 +04:00
err_free_resources :
list_for_each_entry_safe ( io , tmp_io , & spic_dev . ioports , list ) {
list_del ( & io - > list ) ;
kfree ( io ) ;
}
list_for_each_entry_safe ( irq , tmp_irq , & spic_dev . interrupts , list ) {
list_del ( & irq - > list ) ;
kfree ( irq ) ;
}
spic_dev . cur_ioport = NULL ;
spic_dev . cur_irq = NULL ;
return result ;
}
static int sony_pic_suspend ( struct acpi_device * device , pm_message_t state )
{
if ( sony_pic_disable ( device ) )
return - ENXIO ;
return 0 ;
}
static int sony_pic_resume ( struct acpi_device * device )
{
sony_pic_enable ( device , spic_dev . cur_ioport , spic_dev . cur_irq ) ;
return 0 ;
}
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id sony_pic_device_ids [ ] = {
{ SONY_PIC_HID , 0 } ,
{ " " , 0 } ,
} ;
2007-04-09 21:26:03 +04:00
static struct acpi_driver sony_pic_driver = {
. name = SONY_PIC_DRIVER_NAME ,
. class = SONY_PIC_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = sony_pic_device_ids ,
2007-04-09 21:26:03 +04:00
. owner = THIS_MODULE ,
. ops = {
. add = sony_pic_add ,
. remove = sony_pic_remove ,
. suspend = sony_pic_suspend ,
. resume = sony_pic_resume ,
} ,
} ;
static struct dmi_system_id __initdata sonypi_dmi_table [ ] = {
{
. ident = " Sony Vaio " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " PCG- " ) ,
} ,
} ,
{
. ident = " Sony Vaio " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " VGN- " ) ,
} ,
} ,
{ }
} ;
2007-04-09 12:19:04 +04:00
static int __init sony_laptop_init ( void )
2007-01-14 01:04:31 +03:00
{
2007-04-09 21:26:03 +04:00
int result ;
if ( ! no_spic & & dmi_check_system ( sonypi_dmi_table ) ) {
result = acpi_bus_register_driver ( & sony_pic_driver ) ;
if ( result ) {
printk ( KERN_ERR DRV_PFX
" Unable to register SPIC driver. " ) ;
goto out ;
}
}
result = acpi_bus_register_driver ( & sony_nc_driver ) ;
if ( result ) {
printk ( KERN_ERR DRV_PFX " Unable to register SNC driver. " ) ;
goto out_unregister_pic ;
}
return 0 ;
out_unregister_pic :
if ( ! no_spic )
acpi_bus_unregister_driver ( & sony_pic_driver ) ;
out :
return result ;
2007-01-14 01:04:31 +03:00
}
2007-04-09 12:19:04 +04:00
static void __exit sony_laptop_exit ( void )
2007-01-14 01:04:31 +03:00
{
2007-04-09 12:19:04 +04:00
acpi_bus_unregister_driver ( & sony_nc_driver ) ;
2007-04-09 21:26:03 +04:00
if ( ! no_spic )
acpi_bus_unregister_driver ( & sony_pic_driver ) ;
2007-01-14 01:04:31 +03:00
}
2007-04-09 12:19:04 +04:00
module_init ( sony_laptop_init ) ;
module_exit ( sony_laptop_exit ) ;