aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authormalattia@linux.it <malattia@linux.it>2007-04-09 13:26:03 -0400
committerLen Brown <len.brown@intel.com>2007-04-10 16:01:19 -0400
commit33a04454527edd33d4a6332a2944d2b4f46fbb18 (patch)
tree44a4f265613cc8d46e7e9796f5a4db1c999dd32b /drivers
parentb9a218b738c5c2387f666731b81a4376021d681e (diff)
sony-laptop: Add SNY6001 device handling (sonypi reimplementation)
Reimplement sonypi using ACPI only functions. Signed-off-by: Mattia Dongili <malattia@linux.it> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/sony-laptop.c1085
1 files changed, 1072 insertions, 13 deletions
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
index f4755370dd52..b797c8cb47df 100644
--- a/drivers/misc/sony-laptop.c
+++ b/drivers/misc/sony-laptop.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * ACPI Sony Notebook Control Driver (SNC) 2 * ACPI Sony Notebook Control Driver (SNC and SPIC)
3 * 3 *
4 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net> 4 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
5 * Copyright (C) 2007 Mattia Dongili <malattia@linux.it> 5 * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
@@ -7,6 +7,25 @@
7 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c 7 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
8 * which are copyrighted by their respective authors. 8 * which are copyrighted by their respective authors.
9 * 9 *
10 * The SNY6001 driver part is based on the sonypi driver which includes
11 * material from:
12 *
13 * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
14 *
15 * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
16 *
17 * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
18 *
19 * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
20 *
21 * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
22 *
23 * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
24 *
25 * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
26 *
27 * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
28 *
10 * This program is free software; you can redistribute it and/or modify 29 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by 30 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or 31 * the Free Software Foundation; either version 2 of the License, or
@@ -31,32 +50,65 @@
31#include <linux/backlight.h> 50#include <linux/backlight.h>
32#include <linux/platform_device.h> 51#include <linux/platform_device.h>
33#include <linux/err.h> 52#include <linux/err.h>
53#include <linux/dmi.h>
54#include <linux/pci.h>
55#include <linux/interrupt.h>
56#include <linux/delay.h>
57#include <linux/input.h>
58#include <linux/kfifo.h>
59#include <linux/workqueue.h>
60#include <linux/acpi.h>
34#include <acpi/acpi_drivers.h> 61#include <acpi/acpi_drivers.h>
35#include <acpi/acpi_bus.h> 62#include <acpi/acpi_bus.h>
36#include <asm/uaccess.h> 63#include <asm/uaccess.h>
64#include <linux/sonypi.h>
37 65
38#define LOG_PFX KERN_WARNING "sony-laptop: " 66#define DRV_PFX "sony-laptop: "
67#define LOG_PFX KERN_WARNING DRV_PFX
39#define dprintk(msg...) do { \ 68#define dprintk(msg...) do { \
40 if (debug) printk(KERN_WARNING LOG_PFX msg); \ 69 if (debug) printk(LOG_PFX msg); \
41} while (0) 70} while (0)
42 71
43#define SONY_NC_CLASS "sony" 72#define SONY_LAPTOP_DRIVER_VERSION "0.5"
73
74#define SONY_NC_CLASS "sony-nc"
44#define SONY_NC_HID "SNY5001" 75#define SONY_NC_HID "SNY5001"
45#define SONY_NC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.4" 76#define SONY_NC_DRIVER_NAME "Sony Notebook Control"
46 77
47/* the device uses 1-based values, while the backlight subsystem uses 78#define SONY_PIC_CLASS "sony-pic"
48 0-based values */ 79#define SONY_PIC_HID "SNY6001"
49#define SONY_MAX_BRIGHTNESS 8 80#define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control"
50 81
51MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); 82MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
52MODULE_DESCRIPTION(SONY_NC_DRIVER_NAME); 83MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
53MODULE_LICENSE("GPL"); 84MODULE_LICENSE("GPL");
85MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
54 86
55static int debug; 87static int debug;
56module_param(debug, int, 0); 88module_param(debug, int, 0);
57MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help " 89MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
58 "the development of this driver"); 90 "the development of this driver");
59 91
92static int no_spic; /* = 0 */
93module_param(no_spic, int, 0444);
94MODULE_PARM_DESC(no_spic,
95 "set this if you don't want to enable the SPIC device");
96
97static int compat; /* = 0 */
98module_param(compat, int, 0444);
99MODULE_PARM_DESC(compat,
100 "set this if you want to enable backward compatibility mode");
101
102static int force_jog; /* = 0 */
103module_param(force_jog, int, 0444);
104MODULE_PARM_DESC(force_jog,
105 "set this if the driver doesn't detect your jogdial");
106
107static unsigned long mask = 0xffffffff;
108module_param(mask, ulong, 0644);
109MODULE_PARM_DESC(mask,
110 "set this to the mask of event you want to enable (see doc)");
111
60/*********** Platform Device ***********/ 112/*********** Platform Device ***********/
61 113
62static atomic_t sony_pf_users = ATOMIC_INIT(0); 114static atomic_t sony_pf_users = ATOMIC_INIT(0);
@@ -115,6 +167,13 @@ static void sony_pf_remove(void)
115 167
116/*********** SNC (SNY5001) Device ***********/ 168/*********** SNC (SNY5001) Device ***********/
117 169
170/* the device uses 1-based values, while the backlight subsystem uses
171 0-based values */
172#define SONY_MAX_BRIGHTNESS 8
173
174#define SNC_VALIDATE_IN 0
175#define SNC_VALIDATE_OUT 1
176
118static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *, 177static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
119 char *); 178 char *);
120static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *, 179static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
@@ -122,9 +181,6 @@ static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
122static int boolean_validate(const int, const int); 181static int boolean_validate(const int, const int);
123static int brightness_default_validate(const int, const int); 182static int brightness_default_validate(const int, const int);
124 183
125#define SNC_VALIDATE_IN 0
126#define SNC_VALIDATE_OUT 1
127
128struct sony_nc_value { 184struct sony_nc_value {
129 char *name; /* name of the entry */ 185 char *name; /* name of the entry */
130 char **acpiget; /* names of the ACPI get function */ 186 char **acpiget; /* names of the ACPI get function */
@@ -420,6 +476,7 @@ static int sony_nc_add(struct acpi_device *device)
420 struct sony_nc_value *item; 476 struct sony_nc_value *item;
421 477
422 sony_nc_acpi_device = device; 478 sony_nc_acpi_device = device;
479 strcpy(acpi_device_class(device), "sony/hotkey");
423 480
424 sony_nc_acpi_handle = device->handle; 481 sony_nc_acpi_handle = device->handle;
425 482
@@ -555,6 +612,7 @@ static struct acpi_driver sony_nc_driver = {
555 .name = SONY_NC_DRIVER_NAME, 612 .name = SONY_NC_DRIVER_NAME,
556 .class = SONY_NC_CLASS, 613 .class = SONY_NC_CLASS,
557 .ids = SONY_NC_HID, 614 .ids = SONY_NC_HID,
615 .owner = THIS_MODULE,
558 .ops = { 616 .ops = {
559 .add = sony_nc_add, 617 .add = sony_nc_add,
560 .remove = sony_nc_remove, 618 .remove = sony_nc_remove,
@@ -562,14 +620,1015 @@ static struct acpi_driver sony_nc_driver = {
562 }, 620 },
563}; 621};
564 622
623/*********** SPIC (SNY6001) Device ***********/
624
625#define SONYPI_DEVICE_TYPE1 0x00000001
626#define SONYPI_DEVICE_TYPE2 0x00000002
627#define SONYPI_DEVICE_TYPE3 0x00000004
628
629#define SONY_EC_JOGB 0x82
630#define SONY_EC_JOGB_MASK 0x02
631
632#define SONY_PIC_EV_MASK 0xff
633
634#define SONYPI_BUF_SIZE 128
635
636struct sony_pic_ioport {
637 struct acpi_resource_io io;
638 struct list_head list;
639};
640
641struct sony_pic_irq {
642 struct acpi_resource_irq irq;
643 struct list_head list;
644};
645
646struct sony_pic_dev {
647 int model;
648 struct acpi_device *acpi_dev;
649 struct sony_pic_irq *cur_irq;
650 struct sony_pic_ioport *cur_ioport;
651 struct list_head interrupts;
652 struct list_head ioports;
653
654 struct input_dev *input_jog_dev;
655 struct input_dev *input_key_dev;
656 struct kfifo *input_fifo;
657 spinlock_t input_fifo_lock;
658 struct workqueue_struct *sony_pic_wq;
659};
660
661static struct sony_pic_dev spic_dev = {
662 .interrupts = LIST_HEAD_INIT(spic_dev.interrupts),
663 .ioports = LIST_HEAD_INIT(spic_dev.ioports),
664};
665
666/* Event masks */
667#define SONYPI_JOGGER_MASK 0x00000001
668#define SONYPI_CAPTURE_MASK 0x00000002
669#define SONYPI_FNKEY_MASK 0x00000004
670#define SONYPI_BLUETOOTH_MASK 0x00000008
671#define SONYPI_PKEY_MASK 0x00000010
672#define SONYPI_BACK_MASK 0x00000020
673#define SONYPI_HELP_MASK 0x00000040
674#define SONYPI_LID_MASK 0x00000080
675#define SONYPI_ZOOM_MASK 0x00000100
676#define SONYPI_THUMBPHRASE_MASK 0x00000200
677#define SONYPI_MEYE_MASK 0x00000400
678#define SONYPI_MEMORYSTICK_MASK 0x00000800
679#define SONYPI_BATTERY_MASK 0x00001000
680#define SONYPI_WIRELESS_MASK 0x00002000
681
682struct sonypi_event {
683 u8 data;
684 u8 event;
685};
686
687/* The set of possible button release events */
688static struct sonypi_event sonypi_releaseev[] = {
689 { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
690 { 0, 0 }
691};
692
693/* The set of possible jogger events */
694static struct sonypi_event sonypi_joggerev[] = {
695 { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
696 { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
697 { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
698 { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
699 { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
700 { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
701 { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
702 { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
703 { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
704 { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
705 { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
706 { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
707 { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
708 { 0, 0 }
709};
710
711/* The set of possible capture button events */
712static struct sonypi_event sonypi_captureev[] = {
713 { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
714 { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
715 { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
716 { 0, 0 }
717};
718
719/* The set of possible fnkeys events */
720static struct sonypi_event sonypi_fnkeyev[] = {
721 { 0x10, SONYPI_EVENT_FNKEY_ESC },
722 { 0x11, SONYPI_EVENT_FNKEY_F1 },
723 { 0x12, SONYPI_EVENT_FNKEY_F2 },
724 { 0x13, SONYPI_EVENT_FNKEY_F3 },
725 { 0x14, SONYPI_EVENT_FNKEY_F4 },
726 { 0x15, SONYPI_EVENT_FNKEY_F5 },
727 { 0x16, SONYPI_EVENT_FNKEY_F6 },
728 { 0x17, SONYPI_EVENT_FNKEY_F7 },
729 { 0x18, SONYPI_EVENT_FNKEY_F8 },
730 { 0x19, SONYPI_EVENT_FNKEY_F9 },
731 { 0x1a, SONYPI_EVENT_FNKEY_F10 },
732 { 0x1b, SONYPI_EVENT_FNKEY_F11 },
733 { 0x1c, SONYPI_EVENT_FNKEY_F12 },
734 { 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
735 { 0x21, SONYPI_EVENT_FNKEY_1 },
736 { 0x22, SONYPI_EVENT_FNKEY_2 },
737 { 0x31, SONYPI_EVENT_FNKEY_D },
738 { 0x32, SONYPI_EVENT_FNKEY_E },
739 { 0x33, SONYPI_EVENT_FNKEY_F },
740 { 0x34, SONYPI_EVENT_FNKEY_S },
741 { 0x35, SONYPI_EVENT_FNKEY_B },
742 { 0x36, SONYPI_EVENT_FNKEY_ONLY },
743 { 0, 0 }
744};
745
746/* The set of possible program key events */
747static struct sonypi_event sonypi_pkeyev[] = {
748 { 0x01, SONYPI_EVENT_PKEY_P1 },
749 { 0x02, SONYPI_EVENT_PKEY_P2 },
750 { 0x04, SONYPI_EVENT_PKEY_P3 },
751 { 0x5c, SONYPI_EVENT_PKEY_P1 },
752 { 0, 0 }
753};
754
755/* The set of possible bluetooth events */
756static struct sonypi_event sonypi_blueev[] = {
757 { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
758 { 0x59, SONYPI_EVENT_BLUETOOTH_ON },
759 { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
760 { 0, 0 }
761};
762
763/* The set of possible wireless events */
764static struct sonypi_event sonypi_wlessev[] = {
765 { 0x59, SONYPI_EVENT_WIRELESS_ON },
766 { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
767 { 0, 0 }
768};
769
770/* The set of possible back button events */
771static struct sonypi_event sonypi_backev[] = {
772 { 0x20, SONYPI_EVENT_BACK_PRESSED },
773 { 0, 0 }
774};
775
776/* The set of possible help button events */
777static struct sonypi_event sonypi_helpev[] = {
778 { 0x3b, SONYPI_EVENT_HELP_PRESSED },
779 { 0, 0 }
780};
781
782
783/* The set of possible lid events */
784static struct sonypi_event sonypi_lidev[] = {
785 { 0x51, SONYPI_EVENT_LID_CLOSED },
786 { 0x50, SONYPI_EVENT_LID_OPENED },
787 { 0, 0 }
788};
789
790/* The set of possible zoom events */
791static struct sonypi_event sonypi_zoomev[] = {
792 { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
793 { 0, 0 }
794};
795
796/* The set of possible thumbphrase events */
797static struct sonypi_event sonypi_thumbphraseev[] = {
798 { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
799 { 0, 0 }
800};
801
802/* The set of possible motioneye camera events */
803static struct sonypi_event sonypi_meyeev[] = {
804 { 0x00, SONYPI_EVENT_MEYE_FACE },
805 { 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
806 { 0, 0 }
807};
808
809/* The set of possible memorystick events */
810static struct sonypi_event sonypi_memorystickev[] = {
811 { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
812 { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
813 { 0, 0 }
814};
815
816/* The set of possible battery events */
817static struct sonypi_event sonypi_batteryev[] = {
818 { 0x20, SONYPI_EVENT_BATTERY_INSERT },
819 { 0x30, SONYPI_EVENT_BATTERY_REMOVE },
820 { 0, 0 }
821};
822
823static struct sonypi_eventtypes {
824 int model;
825 u8 data;
826 unsigned long mask;
827 struct sonypi_event * events;
828} sony_pic_eventtypes[] = {
829 { SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev },
830 { SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
831 { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev },
832 { SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
833 { SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
834 { SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
835 { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
836 { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
837 { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
838 { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
839
840 { SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev },
841 { SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev },
842 { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
843 { SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
844 { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
845 { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
846 { SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
847 { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev },
848 { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev },
849 { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
850 { SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
851 { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
852 { SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
853 { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
854
855 { SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev },
856 { SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
857 { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
858 { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
859 { SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
860 { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
861 { 0 }
862};
863
864static int sony_pic_detect_device_type(void)
865{
866 struct pci_dev *pcidev;
867 int model = 0;
868
869 if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
870 PCI_DEVICE_ID_INTEL_82371AB_3, NULL)))
871 model = SONYPI_DEVICE_TYPE1;
872
873 else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
874 PCI_DEVICE_ID_INTEL_ICH6_1, NULL)))
875 model = SONYPI_DEVICE_TYPE3;
876
877 else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
878 PCI_DEVICE_ID_INTEL_ICH7_1, NULL)))
879 model = SONYPI_DEVICE_TYPE3;
880
881 else
882 model = SONYPI_DEVICE_TYPE2;
883
884 if (pcidev)
885 pci_dev_put(pcidev);
886
887 printk(KERN_INFO DRV_PFX "detected Type%d model\n",
888 model == SONYPI_DEVICE_TYPE1 ? 1 :
889 model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
890 return model;
891}
892
893#define ITERATIONS_LONG 10000
894#define ITERATIONS_SHORT 10
895#define wait_on_command(command, iterations) { \
896 unsigned int n = iterations; \
897 while (--n && (command)) \
898 udelay(1); \
899 if (!n) \
900 dprintk("command failed at %s : %s (line %d)\n", \
901 __FILE__, __FUNCTION__, __LINE__); \
902}
903
904static u8 sony_pic_call1(u8 dev)
905{
906 u8 v1, v2;
907
908 wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
909 ITERATIONS_LONG);
910 outb(dev, spic_dev.cur_ioport->io.minimum + 4);
911 v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4);
912 v2 = inb_p(spic_dev.cur_ioport->io.minimum);
913 dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1);
914 return v2;
915}
916
917static u8 sony_pic_call2(u8 dev, u8 fn)
918{
919 u8 v1;
920
921 wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
922 ITERATIONS_LONG);
923 outb(dev, spic_dev.cur_ioport->io.minimum + 4);
924 wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
925 ITERATIONS_LONG);
926 outb(fn, spic_dev.cur_ioport->io.minimum);
927 v1 = inb_p(spic_dev.cur_ioport->io.minimum);
928 dprintk("sony_pic_call2: 0x%.4x\n", v1);
929 return v1;
930}
931
932/*****************
933 *
934 * INPUT Device
935 *
936 *****************/
937struct sony_pic_keypress {
938 struct input_dev *dev;
939 int key;
940};
941
942/* Correspondance table between sonypi events and input layer events */
943static struct {
944 int sonypiev;
945 int inputev;
946} sony_pic_inputkeys[] = {
947 { SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA },
948 { SONYPI_EVENT_FNKEY_ONLY, KEY_FN },
949 { SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC },
950 { SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 },
951 { SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 },
952 { SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 },
953 { SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 },
954 { SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 },
955 { SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 },
956 { SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 },
957 { SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 },
958 { SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 },
959 { SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 },
960 { SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 },
961 { SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 },
962 { SONYPI_EVENT_FNKEY_1, KEY_FN_1 },
963 { SONYPI_EVENT_FNKEY_2, KEY_FN_2 },
964 { SONYPI_EVENT_FNKEY_D, KEY_FN_D },
965 { SONYPI_EVENT_FNKEY_E, KEY_FN_E },
966 { SONYPI_EVENT_FNKEY_F, KEY_FN_F },
967 { SONYPI_EVENT_FNKEY_S, KEY_FN_S },
968 { SONYPI_EVENT_FNKEY_B, KEY_FN_B },
969 { SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE },
970 { SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE },
971 { SONYPI_EVENT_PKEY_P1, KEY_PROG1 },
972 { SONYPI_EVENT_PKEY_P2, KEY_PROG2 },
973 { SONYPI_EVENT_PKEY_P3, KEY_PROG3 },
974 { SONYPI_EVENT_BACK_PRESSED, KEY_BACK },
975 { SONYPI_EVENT_HELP_PRESSED, KEY_HELP },
976 { SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM },
977 { SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB },
978 { 0, 0 },
979};
980
981/* release buttons after a short delay if pressed */
982static void do_sony_pic_release_key(struct work_struct *work)
983{
984 struct sony_pic_keypress kp;
985
986 while (kfifo_get(spic_dev.input_fifo, (unsigned char *)&kp,
987 sizeof(kp)) == sizeof(kp)) {
988 msleep(10);
989 input_report_key(kp.dev, kp.key, 0);
990 input_sync(kp.dev);
991 }
992}
993static DECLARE_WORK(sony_pic_release_key_work,
994 do_sony_pic_release_key);
995
996/* forward event to the input subsytem */
997static void sony_pic_report_input_event(u8 event)
998{
999 struct input_dev *jog_dev = spic_dev.input_jog_dev;
1000 struct input_dev *key_dev = spic_dev.input_key_dev;
1001 struct sony_pic_keypress kp = { NULL };
1002 int i;
1003
1004 if (event == SONYPI_EVENT_FNKEY_RELEASED) {
1005 /* Nothing, not all VAIOs generate this event */
1006 return;
1007 }
1008
1009 /* report jog_dev events */
1010 if (jog_dev) {
1011 switch (event) {
1012 case SONYPI_EVENT_JOGDIAL_UP:
1013 case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
1014 input_report_rel(jog_dev, REL_WHEEL, 1);
1015 input_sync(jog_dev);
1016 return;
1017
1018 case SONYPI_EVENT_JOGDIAL_DOWN:
1019 case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
1020 input_report_rel(jog_dev, REL_WHEEL, -1);
1021 input_sync(jog_dev);
1022 return;
1023
1024 default:
1025 break;
1026 }
1027 }
1028
1029 switch (event) {
1030 case SONYPI_EVENT_JOGDIAL_PRESSED:
1031 kp.key = BTN_MIDDLE;
1032 kp.dev = jog_dev;
1033 break;
1034
1035 default:
1036 for (i = 0; sony_pic_inputkeys[i].sonypiev; i++)
1037 if (event == sony_pic_inputkeys[i].sonypiev) {
1038 kp.dev = key_dev;
1039 kp.key = sony_pic_inputkeys[i].inputev;
1040 break;
1041 }
1042 break;
1043 }
1044
1045 if (kp.dev) {
1046 input_report_key(kp.dev, kp.key, 1);
1047 input_sync(kp.dev);
1048 kfifo_put(spic_dev.input_fifo,
1049 (unsigned char *)&kp, sizeof(kp));
1050
1051 if (!work_pending(&sony_pic_release_key_work))
1052 queue_work(spic_dev.sony_pic_wq,
1053 &sony_pic_release_key_work);
1054 }
1055}
1056
1057static int sony_pic_setup_input(void)
1058{
1059 struct input_dev *jog_dev;
1060 struct input_dev *key_dev;
1061 int i;
1062 int error;
1063 u8 jog_present = 0;
1064
1065 /* kfifo */
1066 spin_lock_init(&spic_dev.input_fifo_lock);
1067 spic_dev.input_fifo =
1068 kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
1069 &spic_dev.input_fifo_lock);
1070 if (IS_ERR(spic_dev.input_fifo)) {
1071 printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
1072 return PTR_ERR(spic_dev.input_fifo);
1073 }
1074
1075 /* init workqueue */
1076 spic_dev.sony_pic_wq = create_singlethread_workqueue("sony-pic");
1077 if (!spic_dev.sony_pic_wq) {
1078 printk(KERN_ERR DRV_PFX
1079 "Unabe to create workqueue.\n");
1080 error = -ENXIO;
1081 goto err_free_kfifo;
1082 }
1083
1084 /* input keys */
1085 key_dev = input_allocate_device();
1086 if (!key_dev) {
1087 error = -ENOMEM;
1088 goto err_destroy_wq;
1089 }
1090
1091 key_dev->name = "Sony Vaio Keys";
1092 key_dev->id.bustype = BUS_ISA;
1093 key_dev->id.vendor = PCI_VENDOR_ID_SONY;
1094
1095 /* Initialize the Input Drivers: special keys */
1096 key_dev->evbit[0] = BIT(EV_KEY);
1097 for (i = 0; sony_pic_inputkeys[i].sonypiev; i++)
1098 if (sony_pic_inputkeys[i].inputev)
1099 set_bit(sony_pic_inputkeys[i].inputev, key_dev->keybit);
1100
1101 error = input_register_device(key_dev);
1102 if (error)
1103 goto err_free_keydev;
1104
1105 spic_dev.input_key_dev = key_dev;
1106
1107 /* jogdial - really reliable ? */
1108 ec_read(SONY_EC_JOGB, &jog_present);
1109 if (jog_present & SONY_EC_JOGB_MASK || force_jog) {
1110 jog_dev = input_allocate_device();
1111 if (!jog_dev) {
1112 error = -ENOMEM;
1113 goto err_unregister_keydev;
1114 }
1115
1116 jog_dev->name = "Sony Vaio Jogdial";
1117 jog_dev->id.bustype = BUS_ISA;
1118 jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
1119
1120 jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
1121 jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE);
1122 jog_dev->relbit[0] = BIT(REL_WHEEL);
1123
1124 error = input_register_device(jog_dev);
1125 if (error)
1126 goto err_free_jogdev;
1127
1128 spic_dev.input_jog_dev = jog_dev;
1129 }
1130
1131 return 0;
1132
1133err_free_jogdev:
1134 input_free_device(jog_dev);
1135
1136err_unregister_keydev:
1137 input_unregister_device(key_dev);
1138 /* to avoid kref underflow below at input_free_device */
1139 key_dev = NULL;
1140
1141err_free_keydev:
1142 input_free_device(key_dev);
1143
1144err_destroy_wq:
1145 destroy_workqueue(spic_dev.sony_pic_wq);
1146
1147err_free_kfifo:
1148 kfifo_free(spic_dev.input_fifo);
1149
1150 return error;
1151}
1152
1153static void sony_pic_remove_input(void)
1154{
1155 /* flush workqueue first */
1156 flush_workqueue(spic_dev.sony_pic_wq);
1157
1158 /* destroy input devs */
1159 input_unregister_device(spic_dev.input_key_dev);
1160 spic_dev.input_key_dev = NULL;
1161
1162 if (spic_dev.input_jog_dev) {
1163 input_unregister_device(spic_dev.input_jog_dev);
1164 spic_dev.input_jog_dev = NULL;
1165 }
1166
1167 destroy_workqueue(spic_dev.sony_pic_wq);
1168 kfifo_free(spic_dev.input_fifo);
1169}
1170
1171/********************
1172 *
1173 * ACPI callbacks
1174 *
1175 ********************/
1176static acpi_status
1177sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
1178{
1179 u32 i;
1180 struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
1181
1182 switch (resource->type) {
1183 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
1184 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
1185 return AE_OK;
1186
1187 case ACPI_RESOURCE_TYPE_IRQ:
1188 {
1189 struct acpi_resource_irq *p = &resource->data.irq;
1190 struct sony_pic_irq *interrupt = NULL;
1191 if (!p || !p->interrupt_count) {
1192 /*
1193 * IRQ descriptors may have no IRQ# bits set,
1194 * particularly those those w/ _STA disabled
1195 */
1196 dprintk("Blank IRQ resource\n");
1197 return AE_OK;
1198 }
1199 for (i = 0; i < p->interrupt_count; i++) {
1200 if (!p->interrupts[i]) {
1201 printk(KERN_WARNING DRV_PFX
1202 "Invalid IRQ %d\n",
1203 p->interrupts[i]);
1204 continue;
1205 }
1206 interrupt = kzalloc(sizeof(*interrupt),
1207 GFP_KERNEL);
1208 if (!interrupt)
1209 return AE_ERROR;
1210
1211 list_add(&interrupt->list, &dev->interrupts);
1212 interrupt->irq.triggering = p->triggering;
1213 interrupt->irq.polarity = p->polarity;
1214 interrupt->irq.sharable = p->sharable;
1215 interrupt->irq.interrupt_count = 1;
1216 interrupt->irq.interrupts[0] = p->interrupts[i];
1217 }
1218 return AE_OK;
1219 }
1220 case ACPI_RESOURCE_TYPE_IO:
1221 {
1222 struct acpi_resource_io *io = &resource->data.io;
1223 struct sony_pic_ioport *ioport = NULL;
1224 if (!io) {
1225 dprintk("Blank IO resource\n");
1226 return AE_OK;
1227 }
1228
1229 ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
1230 if (!ioport)
1231 return AE_ERROR;
1232
1233 list_add(&ioport->list, &dev->ioports);
1234 memcpy(&ioport->io, io, sizeof(*io));
1235 return AE_OK;
1236 }
1237 default:
1238 dprintk("Resource %d isn't an IRQ nor an IO port\n",
1239 resource->type);
1240
1241 case ACPI_RESOURCE_TYPE_END_TAG:
1242 return AE_OK;
1243 }
1244 return AE_CTRL_TERMINATE;
1245}
1246
1247static int sony_pic_possible_resources(struct acpi_device *device)
1248{
1249 int result = 0;
1250 acpi_status status = AE_OK;
1251
1252 if (!device)
1253 return -EINVAL;
1254
1255 /* get device status */
1256 /* see acpi_pci_link_get_current acpi_pci_link_get_possible */
1257 dprintk("Evaluating _STA\n");
1258 result = acpi_bus_get_status(device);
1259 if (result) {
1260 printk(KERN_WARNING DRV_PFX "Unable to read status\n");
1261 goto end;
1262 }
1263
1264 if (!device->status.enabled)
1265 dprintk("Device disabled\n");
1266 else
1267 dprintk("Device enabled\n");
1268
1269 /*
1270 * Query and parse 'method'
1271 */
1272 dprintk("Evaluating %s\n", METHOD_NAME__PRS);
1273 status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
1274 sony_pic_read_possible_resource, &spic_dev);
1275 if (ACPI_FAILURE(status)) {
1276 printk(KERN_WARNING DRV_PFX
1277 "Failure evaluating %s\n",
1278 METHOD_NAME__PRS);
1279 result = -ENODEV;
1280 }
1281end:
1282 return result;
1283}
1284
1285/*
1286 * Disable the spic device by calling its _DIS method
1287 */
1288static int sony_pic_disable(struct acpi_device *device)
1289{
1290 if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL)))
1291 return -ENXIO;
1292
1293 dprintk("Device disabled\n");
1294 return 0;
1295}
1296
1297
1298/*
1299 * Based on drivers/acpi/pci_link.c:acpi_pci_link_set
1300 *
1301 * Call _SRS to set current resources
1302 */
1303static int sony_pic_enable(struct acpi_device *device,
1304 struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
1305{
1306 acpi_status status;
1307 int result = 0;
1308 struct {
1309 struct acpi_resource io_res;
1310 struct acpi_resource irq_res;
1311 struct acpi_resource end;
1312 } *resource;
1313 struct acpi_buffer buffer = { 0, NULL };
1314
1315 if (!ioport || !irq)
1316 return -EINVAL;
1317
1318 /* init acpi_buffer */
1319 resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
1320 if (!resource)
1321 return -ENOMEM;
1322
1323 buffer.length = sizeof(*resource) + 1;
1324 buffer.pointer = resource;
1325
1326 /* setup io resource */
1327 resource->io_res.type = ACPI_RESOURCE_TYPE_IO;
1328 resource->io_res.length = sizeof(struct acpi_resource);
1329 memcpy(&resource->io_res.data.io, &ioport->io,
1330 sizeof(struct acpi_resource_io));
1331
1332 /* setup irq resource */
1333 resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ;
1334 resource->irq_res.length = sizeof(struct acpi_resource);
1335 memcpy(&resource->irq_res.data.irq, &irq->irq,
1336 sizeof(struct acpi_resource_irq));
1337 /* we requested a shared irq */
1338 resource->irq_res.data.irq.sharable = ACPI_SHARED;
1339
1340 resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
1341
1342 /* Attempt to set the resource */
1343 dprintk("Evaluating _SRS\n");
1344 status = acpi_set_current_resources(device->handle, &buffer);
1345
1346 /* check for total failure */
1347 if (ACPI_FAILURE(status)) {
1348 printk(KERN_ERR DRV_PFX "Error evaluating _SRS");
1349 result = -ENODEV;
1350 goto end;
1351 }
1352
1353 /* Necessary device initializations calls (from sonypi) */
1354 sony_pic_call1(0x82);
1355 sony_pic_call2(0x81, 0xff);
1356 sony_pic_call1(compat ? 0x92 : 0x82);
1357
1358end:
1359 kfree(resource);
1360 return result;
1361}
1362
1363/*****************
1364 *
1365 * ISR: some event is available
1366 *
1367 *****************/
1368static irqreturn_t sony_pic_irq(int irq, void *dev_id)
1369{
1370 int i, j;
1371 u32 port_val = 0;
1372 u8 ev = 0;
1373 u8 data_mask = 0;
1374 u8 device_event = 0;
1375
1376 struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
1377
1378 acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val,
1379 dev->cur_ioport->io.address_length);
1380 ev = port_val & SONY_PIC_EV_MASK;
1381 data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8));
1382
1383 dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n",
1384 port_val, ev, data_mask, dev->cur_ioport->io.minimum);
1385
1386 if (ev == 0x00 || ev == 0xff)
1387 return IRQ_HANDLED;
1388
1389 for (i = 0; sony_pic_eventtypes[i].model; i++) {
1390
1391 if (spic_dev.model != sony_pic_eventtypes[i].model)
1392 continue;
1393
1394 if ((data_mask & sony_pic_eventtypes[i].data) !=
1395 sony_pic_eventtypes[i].data)
1396 continue;
1397
1398 if (!(mask & sony_pic_eventtypes[i].mask))
1399 continue;
1400
1401 for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) {
1402 if (ev == sony_pic_eventtypes[i].events[j].data) {
1403 device_event =
1404 sony_pic_eventtypes[i].events[j].event;
1405 goto found;
1406 }
1407 }
1408 }
1409 return IRQ_HANDLED;
1410
1411found:
1412 sony_pic_report_input_event(device_event);
1413 acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event);
1414
1415 return IRQ_HANDLED;
1416}
1417
1418/*****************
1419 *
1420 * ACPI driver
1421 *
1422 *****************/
1423static int sony_pic_remove(struct acpi_device *device, int type)
1424{
1425 struct sony_pic_ioport *io, *tmp_io;
1426 struct sony_pic_irq *irq, *tmp_irq;
1427
1428 if (sony_pic_disable(device)) {
1429 printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
1430 return -ENXIO;
1431 }
1432
1433 free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
1434 release_region(spic_dev.cur_ioport->io.minimum,
1435 spic_dev.cur_ioport->io.address_length);
1436
1437 sony_pic_remove_input();
1438
1439 list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
1440 list_del(&io->list);
1441 kfree(io);
1442 }
1443 list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
1444 list_del(&irq->list);
1445 kfree(irq);
1446 }
1447 spic_dev.cur_ioport = NULL;
1448 spic_dev.cur_irq = NULL;
1449
1450 dprintk("removed.\n");
1451 return 0;
1452}
1453
1454static int sony_pic_add(struct acpi_device *device)
1455{
1456 int result;
1457 struct sony_pic_ioport *io, *tmp_io;
1458 struct sony_pic_irq *irq, *tmp_irq;
1459
1460 printk(KERN_INFO DRV_PFX
1461 "Sony Programmable I/O Controller Driver v%s.\n",
1462 SONY_LAPTOP_DRIVER_VERSION);
1463
1464 spic_dev.acpi_dev = device;
1465 strcpy(acpi_device_class(device), "sony/hotkey");
1466 spic_dev.model = sony_pic_detect_device_type();
1467
1468 /* read _PRS resources */
1469 result = sony_pic_possible_resources(device);
1470 if (result) {
1471 printk(KERN_ERR DRV_PFX
1472 "Unabe to read possible resources.\n");
1473 goto err_free_resources;
1474 }
1475
1476 /* setup input devices and helper fifo */
1477 result = sony_pic_setup_input();
1478 if (result) {
1479 printk(KERN_ERR DRV_PFX
1480 "Unabe to create input devices.\n");
1481 goto err_free_resources;
1482 }
1483
1484 /* request io port */
1485 list_for_each_entry(io, &spic_dev.ioports, list) {
1486 if (request_region(io->io.minimum, io->io.address_length,
1487 "Sony Programable I/O Device")) {
1488 dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n",
1489 io->io.minimum, io->io.maximum,
1490 io->io.address_length);
1491 spic_dev.cur_ioport = io;
1492 break;
1493 }
1494 }
1495 if (!spic_dev.cur_ioport) {
1496 printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
1497 result = -ENODEV;
1498 goto err_remove_input;
1499 }
1500
1501 /* request IRQ */
1502 list_for_each_entry(irq, &spic_dev.interrupts, list) {
1503 if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
1504 IRQF_SHARED, "sony-laptop", &spic_dev)) {
1505 dprintk("IRQ: %d - triggering: %d - "
1506 "polarity: %d - shr: %d\n",
1507 irq->irq.interrupts[0],
1508 irq->irq.triggering,
1509 irq->irq.polarity,
1510 irq->irq.sharable);
1511 spic_dev.cur_irq = irq;
1512 break;
1513 }
1514 }
1515 if (!spic_dev.cur_irq) {
1516 printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
1517 result = -ENODEV;
1518 goto err_release_region;
1519 }
1520
1521 /* set resource status _SRS */
1522 result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
1523 if (result) {
1524 printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
1525 goto err_free_irq;
1526 }
1527
1528 return 0;
1529
1530err_free_irq:
1531 free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
1532
1533err_release_region:
1534 release_region(spic_dev.cur_ioport->io.minimum,
1535 spic_dev.cur_ioport->io.address_length);
1536
1537err_remove_input:
1538 sony_pic_remove_input();
1539
1540err_free_resources:
1541 list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
1542 list_del(&io->list);
1543 kfree(io);
1544 }
1545 list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
1546 list_del(&irq->list);
1547 kfree(irq);
1548 }
1549 spic_dev.cur_ioport = NULL;
1550 spic_dev.cur_irq = NULL;
1551
1552 return result;
1553}
1554
1555static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
1556{
1557 if (sony_pic_disable(device))
1558 return -ENXIO;
1559 return 0;
1560}
1561
1562static int sony_pic_resume(struct acpi_device *device)
1563{
1564 sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
1565 return 0;
1566}
1567
1568static struct acpi_driver sony_pic_driver = {
1569 .name = SONY_PIC_DRIVER_NAME,
1570 .class = SONY_PIC_CLASS,
1571 .ids = SONY_PIC_HID,
1572 .owner = THIS_MODULE,
1573 .ops = {
1574 .add = sony_pic_add,
1575 .remove = sony_pic_remove,
1576 .suspend = sony_pic_suspend,
1577 .resume = sony_pic_resume,
1578 },
1579};
1580
1581static struct dmi_system_id __initdata sonypi_dmi_table[] = {
1582 {
1583 .ident = "Sony Vaio",
1584 .matches = {
1585 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
1586 DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
1587 },
1588 },
1589 {
1590 .ident = "Sony Vaio",
1591 .matches = {
1592 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
1593 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
1594 },
1595 },
1596 { }
1597};
1598
565static int __init sony_laptop_init(void) 1599static int __init sony_laptop_init(void)
566{ 1600{
567 return acpi_bus_register_driver(&sony_nc_driver); 1601 int result;
1602
1603 if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
1604 result = acpi_bus_register_driver(&sony_pic_driver);
1605 if (result) {
1606 printk(KERN_ERR DRV_PFX
1607 "Unable to register SPIC driver.");
1608 goto out;
1609 }
1610 }
1611
1612 result = acpi_bus_register_driver(&sony_nc_driver);
1613 if (result) {
1614 printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
1615 goto out_unregister_pic;
1616 }
1617
1618 return 0;
1619
1620out_unregister_pic:
1621 if (!no_spic)
1622 acpi_bus_unregister_driver(&sony_pic_driver);
1623out:
1624 return result;
568} 1625}
569 1626
570static void __exit sony_laptop_exit(void) 1627static void __exit sony_laptop_exit(void)
571{ 1628{
572 acpi_bus_unregister_driver(&sony_nc_driver); 1629 acpi_bus_unregister_driver(&sony_nc_driver);
1630 if (!no_spic)
1631 acpi_bus_unregister_driver(&sony_pic_driver);
573} 1632}
574 1633
575module_init(sony_laptop_init); 1634module_init(sony_laptop_init);