Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / char / sonypi.c
index fd04206..a90f5d9 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/kfifo.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -98,12 +99,13 @@ MODULE_PARM_DESC(useinput,
 
 #define SONYPI_DEVICE_MODEL_TYPE1      1
 #define SONYPI_DEVICE_MODEL_TYPE2      2
+#define SONYPI_DEVICE_MODEL_TYPE3      3
 
 /* type1 models use those */
 #define SONYPI_IRQ_PORT                        0x8034
 #define SONYPI_IRQ_SHIFT               22
-#define SONYPI_BASE                    0x50
-#define SONYPI_G10A                    (SONYPI_BASE+0x14)
+#define SONYPI_TYPE1_BASE              0x50
+#define SONYPI_G10A                    (SONYPI_TYPE1_BASE+0x14)
 #define SONYPI_TYPE1_REGION_SIZE       0x08
 #define SONYPI_TYPE1_EVTYPE_OFFSET     0x04
 
@@ -114,6 +116,13 @@ MODULE_PARM_DESC(useinput,
 #define SONYPI_TYPE2_REGION_SIZE       0x20
 #define SONYPI_TYPE2_EVTYPE_OFFSET     0x12
 
+/* type3 series specifics */
+#define SONYPI_TYPE3_BASE              0x40
+#define SONYPI_TYPE3_GID2              (SONYPI_TYPE3_BASE+0x48) /* 16 bits */
+#define SONYPI_TYPE3_MISC              (SONYPI_TYPE3_BASE+0x6d) /* 8 bits  */
+#define SONYPI_TYPE3_REGION_SIZE       0x20
+#define SONYPI_TYPE3_EVTYPE_OFFSET     0x12
+
 /* battery / brightness addresses */
 #define SONYPI_BAT_FLAGS       0x81
 #define SONYPI_LCD_LIGHT       0x96
@@ -159,6 +168,10 @@ static struct sonypi_ioport_list sonypi_type2_ioport_list[] = {
        { 0x0, 0x0 }
 };
 
+/* same as in type 2 models */
+static struct sonypi_ioport_list *sonypi_type3_ioport_list =
+       sonypi_type2_ioport_list;
+
 /* The set of possible interrupts */
 struct sonypi_irq_list {
        u16     irq;
@@ -180,6 +193,9 @@ static struct sonypi_irq_list sonypi_type2_irq_list[] = {
        {  0, 0x00 }    /* no IRQ, 0x00 in SIRQ in AML */
 };
 
+/* same as in type2 models */
+static struct sonypi_irq_list *sonypi_type3_irq_list = sonypi_type2_irq_list;
+
 #define SONYPI_CAMERA_BRIGHTNESS               0
 #define SONYPI_CAMERA_CONTRAST                 1
 #define SONYPI_CAMERA_HUE                      2
@@ -223,6 +239,7 @@ static struct sonypi_irq_list sonypi_type2_irq_list[] = {
 #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;
@@ -305,6 +322,13 @@ static struct sonypi_event sonypi_blueev[] = {
        { 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 },
@@ -383,7 +407,6 @@ static struct sonypi_eventtypes {
        { SONYPI_DEVICE_MODEL_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev },
-       { SONYPI_DEVICE_MODEL_TYPE2, 0x08, SONYPI_HELP_MASK, sonypi_helpev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
@@ -391,15 +414,17 @@ static struct sonypi_eventtypes {
        { SONYPI_DEVICE_MODEL_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
        { SONYPI_DEVICE_MODEL_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
 
+       { SONYPI_DEVICE_MODEL_TYPE3, 0, 0xffffffff, sonypi_releaseev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { SONYPI_DEVICE_MODEL_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
        { 0 }
 };
 
 #define SONYPI_BUF_SIZE        128
 
-/* The name of the devices for the input device drivers */
-#define SONYPI_JOG_INPUTNAME   "Sony Vaio Jogdial"
-#define SONYPI_KEY_INPUTNAME   "Sony Vaio Keys"
-
 /* Correspondance table between sonypi events and input layer events */
 static struct {
        int sonypiev;
@@ -439,9 +464,13 @@ static struct {
        { 0, 0 },
 };
 
+struct sonypi_keypress {
+       struct input_dev *dev;
+       int key;
+};
+
 static struct sonypi_device {
        struct pci_dev *dev;
-       struct platform_device *pdev;
        u16 irq;
        u16 bits;
        u16 ioport1;
@@ -457,8 +486,8 @@ static struct sonypi_device {
        struct fasync_struct *fifo_async;
        int open_count;
        int model;
-       struct input_dev input_jog_dev;
-       struct input_dev input_key_dev;
+       struct input_dev *input_jog_dev;
+       struct input_dev *input_key_dev;
        struct work_struct input_work;
        struct kfifo *input_fifo;
        spinlock_t input_fifo_lock;
@@ -481,6 +510,11 @@ static struct sonypi_device {
 #define SONYPI_ACPI_ACTIVE 0
 #endif                         /* CONFIG_ACPI */
 
+#ifdef CONFIG_ACPI
+static struct acpi_device *sonypi_acpi_device;
+static int acpi_enabled;
+#endif
+
 static int sonypi_ec_write(u8 addr, u8 value)
 {
 #ifdef CONFIG_ACPI_EC
@@ -558,6 +592,23 @@ static void sonypi_type2_srs(void)
        udelay(10);
 }
 
+static void sonypi_type3_srs(void)
+{
+       u16 v16;
+       u8  v8;
+
+       /* This model type uses the same initialiazation of
+        * the embedded controller as the type2 models. */
+       sonypi_type2_srs();
+
+       /* Initialization of PCI config space of the LPC interface bridge. */
+       v16 = (sonypi_device.ioport1 & 0xFFF0) | 0x01;
+       pci_write_config_word(sonypi_device.dev, SONYPI_TYPE3_GID2, v16);
+       pci_read_config_byte(sonypi_device.dev, SONYPI_TYPE3_MISC, &v8);
+       v8 = (v8 & 0xCF) | 0x10;
+       pci_write_config_byte(sonypi_device.dev, SONYPI_TYPE3_MISC, v8);
+}
+
 /* Disables the device - this comes from the AML code in the ACPI bios */
 static void sonypi_type1_dis(void)
 {
@@ -582,6 +633,13 @@ static void sonypi_type2_dis(void)
                printk(KERN_WARNING "ec_write failed\n");
 }
 
+static void sonypi_type3_dis(void)
+{
+       sonypi_type2_dis();
+       udelay(10);
+       pci_write_config_word(sonypi_device.dev, SONYPI_TYPE3_GID2, 0);
+}
+
 static u8 sonypi_call1(u8 dev)
 {
        u8 v1, v2;
@@ -710,22 +768,61 @@ static void sonypi_setbluetoothpower(u8 state)
 
 static void input_keyrelease(void *data)
 {
-       struct input_dev *input_dev;
-       int key;
-
-       while (1) {
-               if (kfifo_get(sonypi_device.input_fifo,
-                             (unsigned char *)&input_dev,
-                             sizeof(input_dev)) != sizeof(input_dev))
-                       return;
-               if (kfifo_get(sonypi_device.input_fifo,
-                             (unsigned char *)&key,
-                             sizeof(key)) != sizeof(key))
-                       return;
+       struct sonypi_keypress kp;
 
+       while (kfifo_get(sonypi_device.input_fifo, (unsigned char *)&kp,
+                        sizeof(kp)) == sizeof(kp)) {
                msleep(10);
-               input_report_key(input_dev, key, 0);
-               input_sync(input_dev);
+               input_report_key(kp.dev, kp.key, 0);
+               input_sync(kp.dev);
+       }
+}
+
+static void sonypi_report_input_event(u8 event)
+{
+       struct input_dev *jog_dev = sonypi_device.input_jog_dev;
+       struct input_dev *key_dev = sonypi_device.input_key_dev;
+       struct sonypi_keypress kp = { NULL };
+       int i;
+
+       switch (event) {
+       case SONYPI_EVENT_JOGDIAL_UP:
+       case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, 1);
+               input_sync(jog_dev);
+               break;
+
+       case SONYPI_EVENT_JOGDIAL_DOWN:
+       case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, -1);
+               input_sync(jog_dev);
+               break;
+
+       case SONYPI_EVENT_JOGDIAL_PRESSED:
+               kp.key = BTN_MIDDLE;
+               kp.dev = jog_dev;
+               break;
+
+       case SONYPI_EVENT_FNKEY_RELEASED:
+               /* Nothing, not all VAIOs generate this event */
+               break;
+
+       default:
+               for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
+                       if (event == sonypi_inputkeys[i].sonypiev) {
+                               kp.dev = key_dev;
+                               kp.key = sonypi_inputkeys[i].inputev;
+                               break;
+                       }
+               break;
+       }
+
+       if (kp.dev) {
+               input_report_key(kp.dev, kp.key, 1);
+               input_sync(kp.dev);
+               kfifo_put(sonypi_device.input_fifo,
+                         (unsigned char *)&kp, sizeof(kp));
+               schedule_work(&sonypi_device.input_work);
        }
 }
 
@@ -768,51 +865,13 @@ found:
                printk(KERN_INFO
                       "sonypi: event port1=0x%02x,port2=0x%02x\n", v1, v2);
 
-       if (useinput) {
-               struct input_dev *input_jog_dev = &sonypi_device.input_jog_dev;
-               struct input_dev *input_key_dev = &sonypi_device.input_key_dev;
-               switch (event) {
-               case SONYPI_EVENT_JOGDIAL_UP:
-               case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
-                       input_report_rel(input_jog_dev, REL_WHEEL, 1);
-                       break;
-               case SONYPI_EVENT_JOGDIAL_DOWN:
-               case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
-                       input_report_rel(input_jog_dev, REL_WHEEL, -1);
-                       break;
-               case SONYPI_EVENT_JOGDIAL_PRESSED: {
-                       int key = BTN_MIDDLE;
-                       input_report_key(input_jog_dev, key, 1);
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&input_jog_dev,
-                                 sizeof(input_jog_dev));
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&key, sizeof(key));
-                       break;
-               }
-               case SONYPI_EVENT_FNKEY_RELEASED:
-                       /* Nothing, not all VAIOs generate this event */
-                       break;
-               }
-               input_sync(input_jog_dev);
+       if (useinput)
+               sonypi_report_input_event(event);
 
-               for (i = 0; sonypi_inputkeys[i].sonypiev; i++) {
-                       int key;
-
-                       if (event != sonypi_inputkeys[i].sonypiev)
-                               continue;
-
-                       key = sonypi_inputkeys[i].inputev;
-                       input_report_key(input_key_dev, key, 1);
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&input_key_dev,
-                                 sizeof(input_key_dev));
-                       kfifo_put(sonypi_device.input_fifo,
-                                 (unsigned char *)&key, sizeof(key));
-               }
-               input_sync(input_key_dev);
-               schedule_work(&sonypi_device.input_work);
-       }
+#ifdef CONFIG_ACPI
+       if (acpi_enabled)
+               acpi_bus_generate_event(sonypi_acpi_device, 1, event);
+#endif
 
        kfifo_put(sonypi_device.fifo, (unsigned char *)&event, sizeof(event));
        kill_fasync(&sonypi_device.fifo_async, SIGIO, POLL_IN);
@@ -1066,10 +1125,17 @@ static struct miscdevice sonypi_misc_device = {
 
 static void sonypi_enable(unsigned int camera_on)
 {
-       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2)
-               sonypi_type2_srs();
-       else
+       switch (sonypi_device.model) {
+       case SONYPI_DEVICE_MODEL_TYPE1:
                sonypi_type1_srs();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE2:
+               sonypi_type2_srs();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE3:
+               sonypi_type3_srs();
+               break;
+       }
 
        sonypi_call1(0x82);
        sonypi_call2(0x81, 0xff);
@@ -1093,175 +1159,249 @@ static int sonypi_disable(void)
        if (!SONYPI_ACPI_ACTIVE && fnkeyinit)
                outb(0xf1, 0xb2);
 
-       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2)
-               sonypi_type2_dis();
-       else
+       switch (sonypi_device.model) {
+       case SONYPI_DEVICE_MODEL_TYPE1:
                sonypi_type1_dis();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE2:
+               sonypi_type2_dis();
+               break;
+       case SONYPI_DEVICE_MODEL_TYPE3:
+               sonypi_type3_dis();
+               break;
+       }
+
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int old_camera_power;
-
-static int sonypi_suspend(struct device *dev, pm_message_t state, u32 level)
+#ifdef CONFIG_ACPI
+static int sonypi_acpi_add(struct acpi_device *device)
 {
-       if (level == SUSPEND_DISABLE) {
-               old_camera_power = sonypi_device.camera_power;
-               sonypi_disable();
-       }
+       sonypi_acpi_device = device;
+       strcpy(acpi_device_name(device), "Sony laptop hotkeys");
+       strcpy(acpi_device_class(device), "sony/hotkey");
        return 0;
 }
 
-static int sonypi_resume(struct device *dev, u32 level)
+static int sonypi_acpi_remove(struct acpi_device *device, int type)
 {
-       if (level == RESUME_ENABLE)
-               sonypi_enable(old_camera_power);
+       sonypi_acpi_device = NULL;
        return 0;
 }
+
+static struct acpi_driver sonypi_acpi_driver = {
+       .name           = "sonypi",
+       .class          = "hkey",
+       .ids            = "SNY6001",
+       .ops            = {
+                          .add = sonypi_acpi_add,
+                          .remove = sonypi_acpi_remove,
+       },
+};
 #endif
 
-static void sonypi_shutdown(struct device *dev)
+static int __devinit sonypi_create_input_devices(void)
 {
-       sonypi_disable();
+       struct input_dev *jog_dev;
+       struct input_dev *key_dev;
+       int i;
+       int error;
+
+       sonypi_device.input_jog_dev = jog_dev = input_allocate_device();
+       if (!jog_dev)
+               return -ENOMEM;
+
+       jog_dev->name = "Sony Vaio Jogdial";
+       jog_dev->id.bustype = BUS_ISA;
+       jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
+
+       jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+       jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE);
+       jog_dev->relbit[0] = BIT(REL_WHEEL);
+
+       sonypi_device.input_key_dev = key_dev = input_allocate_device();
+       if (!key_dev) {
+               error = -ENOMEM;
+               goto err_free_jogdev;
+       }
+
+       key_dev->name = "Sony Vaio Keys";
+       key_dev->id.bustype = BUS_ISA;
+       key_dev->id.vendor = PCI_VENDOR_ID_SONY;
+
+       /* Initialize the Input Drivers: special keys */
+       key_dev->evbit[0] = BIT(EV_KEY);
+       for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
+               if (sonypi_inputkeys[i].inputev)
+                       set_bit(sonypi_inputkeys[i].inputev, key_dev->keybit);
+
+       error = input_register_device(jog_dev);
+       if (error)
+               goto err_free_keydev;
+
+       error = input_register_device(key_dev);
+       if (error)
+               goto err_unregister_jogdev;
+
+       return 0;
+
+ err_unregister_jogdev:
+       input_unregister_device(jog_dev);
+       /* Set to NULL so we don't free it again below */
+       jog_dev = NULL;
+ err_free_keydev:
+       input_free_device(key_dev);
+       sonypi_device.input_key_dev = NULL;
+ err_free_jogdev:
+       input_free_device(jog_dev);
+       sonypi_device.input_jog_dev = NULL;
+
+       return error;
 }
 
-static struct device_driver sonypi_driver = {
-       .name           = "sonypi",
-       .bus            = &platform_bus_type,
-#ifdef CONFIG_PM
-       .suspend        = sonypi_suspend,
-       .resume         = sonypi_resume,
-#endif
-       .shutdown       = sonypi_shutdown,
-};
+static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
+                               const struct sonypi_ioport_list *ioport_list)
+{
+       while (ioport_list->port1) {
 
-static int __devinit sonypi_probe(void)
+               if (request_region(ioport_list->port1,
+                                  sonypi_device.region_size,
+                                  "Sony Programable I/O Device")) {
+                       dev->ioport1 = ioport_list->port1;
+                       dev->ioport2 = ioport_list->port2;
+                       return 0;
+               }
+               ioport_list++;
+       }
+
+       return -EBUSY;
+}
+
+static int __devinit sonypi_setup_irq(struct sonypi_device *dev,
+                                     const struct sonypi_irq_list *irq_list)
 {
-       int i, ret;
-       struct sonypi_ioport_list *ioport_list;
-       struct sonypi_irq_list *irq_list;
-       struct pci_dev *pcidev;
+       while (irq_list->irq) {
 
-       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                               PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+               if (!request_irq(irq_list->irq, sonypi_irq,
+                                SA_SHIRQ, "sonypi", sonypi_irq)) {
+                       dev->irq = irq_list->irq;
+                       dev->bits = irq_list->bits;
+                       return 0;
+               }
+               irq_list++;
+       }
 
-       sonypi_device.dev = pcidev;
-       sonypi_device.model = pcidev ?
-               SONYPI_DEVICE_MODEL_TYPE1 : SONYPI_DEVICE_MODEL_TYPE2;
+       return -EBUSY;
+}
+
+static void __devinit sonypi_display_info(void)
+{
+       printk(KERN_INFO "sonypi: detected type%d model, "
+              "verbose = %d, fnkeyinit = %s, camera = %s, "
+              "compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n",
+              sonypi_device.model,
+              verbose,
+              fnkeyinit ? "on" : "off",
+              camera ? "on" : "off",
+              compat ? "on" : "off",
+              mask,
+              useinput ? "on" : "off",
+              SONYPI_ACPI_ACTIVE ? "on" : "off");
+       printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
+              sonypi_device.irq,
+              sonypi_device.ioport1, sonypi_device.ioport2);
+
+       if (minor == -1)
+               printk(KERN_INFO "sonypi: device allocated minor is %d\n",
+                      sonypi_misc_device.minor);
+}
+
+static int __devinit sonypi_probe(struct platform_device *dev)
+{
+       const struct sonypi_ioport_list *ioport_list;
+       const struct sonypi_irq_list *irq_list;
+       struct pci_dev *pcidev;
+       int error;
 
        spin_lock_init(&sonypi_device.fifo_lock);
        sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL,
                                         &sonypi_device.fifo_lock);
        if (IS_ERR(sonypi_device.fifo)) {
                printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
-               ret = PTR_ERR(sonypi_device.fifo);
-               goto out_fifo;
+               return PTR_ERR(sonypi_device.fifo);
        }
 
        init_waitqueue_head(&sonypi_device.fifo_proc_list);
        init_MUTEX(&sonypi_device.lock);
        sonypi_device.bluetooth_power = -1;
 
+       if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                    PCI_DEVICE_ID_INTEL_82371AB_3, NULL)))
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE1;
+       else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                         PCI_DEVICE_ID_INTEL_ICH6_1, NULL)))
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE3;
+       else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                         PCI_DEVICE_ID_INTEL_ICH7_1, NULL)))
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE3;
+       else
+               sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE2;
+
        if (pcidev && pci_enable_device(pcidev)) {
                printk(KERN_ERR "sonypi: pci_enable_device failed\n");
-               ret = -EIO;
-               goto out_pcienable;
+               error = -EIO;
+               goto err_put_pcidev;
        }
 
-       if (minor != -1)
-               sonypi_misc_device.minor = minor;
-       if ((ret = misc_register(&sonypi_misc_device))) {
-               printk(KERN_ERR "sonypi: misc_register failed\n");
-               goto out_miscreg;
-       }
+       sonypi_device.dev = pcidev;
 
-       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) {
+       if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) {
+               ioport_list = sonypi_type1_ioport_list;
+               sonypi_device.region_size = SONYPI_TYPE1_REGION_SIZE;
+               sonypi_device.evtype_offset = SONYPI_TYPE1_EVTYPE_OFFSET;
+               irq_list = sonypi_type1_irq_list;
+       } else if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) {
                ioport_list = sonypi_type2_ioport_list;
                sonypi_device.region_size = SONYPI_TYPE2_REGION_SIZE;
                sonypi_device.evtype_offset = SONYPI_TYPE2_EVTYPE_OFFSET;
                irq_list = sonypi_type2_irq_list;
        } else {
-               ioport_list = sonypi_type1_ioport_list;
-               sonypi_device.region_size = SONYPI_TYPE1_REGION_SIZE;
-               sonypi_device.evtype_offset = SONYPI_TYPE1_EVTYPE_OFFSET;
-               irq_list = sonypi_type1_irq_list;
+               ioport_list = sonypi_type3_ioport_list;
+               sonypi_device.region_size = SONYPI_TYPE3_REGION_SIZE;
+               sonypi_device.evtype_offset = SONYPI_TYPE3_EVTYPE_OFFSET;
+               irq_list = sonypi_type3_irq_list;
        }
 
-       for (i = 0; ioport_list[i].port1; i++) {
-               if (request_region(ioport_list[i].port1,
-                                  sonypi_device.region_size,
-                                  "Sony Programable I/O Device")) {
-                       /* get the ioport */
-                       sonypi_device.ioport1 = ioport_list[i].port1;
-                       sonypi_device.ioport2 = ioport_list[i].port2;
-                       break;
-               }
-       }
-       if (!sonypi_device.ioport1) {
-               printk(KERN_ERR "sonypi: request_region failed\n");
-               ret = -ENODEV;
-               goto out_reqreg;
+       error = sonypi_setup_ioports(&sonypi_device, ioport_list);
+       if (error) {
+               printk(KERN_ERR "sonypi: failed to request ioports\n");
+               goto err_disable_pcidev;
        }
 
-       for (i = 0; irq_list[i].irq; i++) {
-
-               sonypi_device.irq = irq_list[i].irq;
-               sonypi_device.bits = irq_list[i].bits;
-
-               if (!request_irq(sonypi_device.irq, sonypi_irq,
-                                SA_SHIRQ, "sonypi", sonypi_irq))
-                       break;
+       error = sonypi_setup_irq(&sonypi_device, irq_list);
+       if (error) {
+               printk(KERN_ERR "sonypi: request_irq failed\n");
+               goto err_free_ioports;
        }
 
-       if (!irq_list[i].irq) {
-               printk(KERN_ERR "sonypi: request_irq failed\n");
-               ret = -ENODEV;
-               goto out_reqirq;
+       if (minor != -1)
+               sonypi_misc_device.minor = minor;
+       error = misc_register(&sonypi_misc_device);
+       if (error) {
+               printk(KERN_ERR "sonypi: misc_register failed\n");
+               goto err_free_irq;
        }
 
-       if (useinput) {
-               /* Initialize the Input Drivers: jogdial */
-               int i;
-               sonypi_device.input_jog_dev.evbit[0] =
-                       BIT(EV_KEY) | BIT(EV_REL);
-               sonypi_device.input_jog_dev.keybit[LONG(BTN_MOUSE)] =
-                       BIT(BTN_MIDDLE);
-               sonypi_device.input_jog_dev.relbit[0] = BIT(REL_WHEEL);
-               sonypi_device.input_jog_dev.name =
-                       kmalloc(sizeof(SONYPI_JOG_INPUTNAME), GFP_KERNEL);
-               if (!sonypi_device.input_jog_dev.name) {
-                       printk(KERN_ERR "sonypi: kmalloc failed\n");
-                       ret = -ENOMEM;
-                       goto out_inkmallocinput1;
-               }
-               sprintf(sonypi_device.input_jog_dev.name, SONYPI_JOG_INPUTNAME);
-               sonypi_device.input_jog_dev.id.bustype = BUS_ISA;
-               sonypi_device.input_jog_dev.id.vendor = PCI_VENDOR_ID_SONY;
+       sonypi_display_info();
 
-               input_register_device(&sonypi_device.input_jog_dev);
-               printk(KERN_INFO "%s input method installed.\n",
-                      sonypi_device.input_jog_dev.name);
+       if (useinput) {
 
-               /* Initialize the Input Drivers: special keys */
-               sonypi_device.input_key_dev.evbit[0] = BIT(EV_KEY);
-               for (i = 0; sonypi_inputkeys[i].sonypiev; i++)
-                       if (sonypi_inputkeys[i].inputev)
-                               set_bit(sonypi_inputkeys[i].inputev,
-                                       sonypi_device.input_key_dev.keybit);
-               sonypi_device.input_key_dev.name =
-                       kmalloc(sizeof(SONYPI_KEY_INPUTNAME), GFP_KERNEL);
-               if (!sonypi_device.input_key_dev.name) {
-                       printk(KERN_ERR "sonypi: kmalloc failed\n");
-                       ret = -ENOMEM;
-                       goto out_inkmallocinput2;
+               error = sonypi_create_input_devices();
+               if (error) {
+                       printk(KERN_ERR
+                               "sonypi: failed to create input devices\n");
+                       goto err_miscdev_unregister;
                }
-               sprintf(sonypi_device.input_key_dev.name, SONYPI_KEY_INPUTNAME);
-               sonypi_device.input_key_dev.id.bustype = BUS_ISA;
-               sonypi_device.input_key_dev.id.vendor = PCI_VENDOR_ID_SONY;
-
-               input_register_device(&sonypi_device.input_key_dev);
-               printk(KERN_INFO "%s input method installed.\n",
-                      sonypi_device.input_key_dev.name);
 
                spin_lock_init(&sonypi_device.input_fifo_lock);
                sonypi_device.input_fifo =
@@ -1269,94 +1409,104 @@ static int __devinit sonypi_probe(void)
                                    &sonypi_device.input_fifo_lock);
                if (IS_ERR(sonypi_device.input_fifo)) {
                        printk(KERN_ERR "sonypi: kfifo_alloc failed\n");
-                       ret = PTR_ERR(sonypi_device.input_fifo);
-                       goto out_infifo;
+                       error = PTR_ERR(sonypi_device.input_fifo);
+                       goto err_inpdev_unregister;
                }
 
                INIT_WORK(&sonypi_device.input_work, input_keyrelease, NULL);
        }
 
-       sonypi_device.pdev = platform_device_register_simple("sonypi", -1,
-                                                            NULL, 0);
-       if (IS_ERR(sonypi_device.pdev)) {
-               ret = PTR_ERR(sonypi_device.pdev);
-               goto out_platformdev;
-       }
-
        sonypi_enable(0);
 
-       printk(KERN_INFO "sonypi: Sony Programmable I/O Controller Driver"
-              "v%s.\n", SONYPI_DRIVER_VERSION);
-       printk(KERN_INFO "sonypi: detected %s model, "
-              "verbose = %d, fnkeyinit = %s, camera = %s, "
-              "compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n",
-              (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) ?
-                       "type1" : "type2",
-              verbose,
-              fnkeyinit ? "on" : "off",
-              camera ? "on" : "off",
-              compat ? "on" : "off",
-              mask,
-              useinput ? "on" : "off",
-              SONYPI_ACPI_ACTIVE ? "on" : "off");
-       printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
-              sonypi_device.irq,
-              sonypi_device.ioport1, sonypi_device.ioport2);
-
-       if (minor == -1)
-               printk(KERN_INFO "sonypi: device allocated minor is %d\n",
-                      sonypi_misc_device.minor);
-
        return 0;
 
-out_platformdev:
-       kfifo_free(sonypi_device.input_fifo);
-out_infifo:
-       input_unregister_device(&sonypi_device.input_key_dev);
-       kfree(sonypi_device.input_key_dev.name);
-out_inkmallocinput2:
-       input_unregister_device(&sonypi_device.input_jog_dev);
-       kfree(sonypi_device.input_jog_dev.name);
-out_inkmallocinput1:
+ err_inpdev_unregister:
+       input_unregister_device(sonypi_device.input_key_dev);
+       input_unregister_device(sonypi_device.input_jog_dev);
+ err_miscdev_unregister:
+       misc_deregister(&sonypi_misc_device);
+ err_free_irq:
        free_irq(sonypi_device.irq, sonypi_irq);
-out_reqirq:
+ err_free_ioports:
        release_region(sonypi_device.ioport1, sonypi_device.region_size);
-out_reqreg:
-       misc_deregister(&sonypi_misc_device);
-out_miscreg:
+ err_disable_pcidev:
        if (pcidev)
                pci_disable_device(pcidev);
-out_pcienable:
+ err_put_pcidev:
+       pci_dev_put(pcidev);
        kfifo_free(sonypi_device.fifo);
-out_fifo:
-       pci_dev_put(sonypi_device.dev);
-       return ret;
+
+       return error;
 }
 
-static void __devexit sonypi_remove(void)
+static int __devexit sonypi_remove(struct platform_device *dev)
 {
        sonypi_disable();
 
-       platform_device_unregister(sonypi_device.pdev);
+       synchronize_sched();  /* Allow sonypi interrupt to complete. */
+       flush_scheduled_work();
 
        if (useinput) {
-               input_unregister_device(&sonypi_device.input_key_dev);
-               kfree(sonypi_device.input_key_dev.name);
-               input_unregister_device(&sonypi_device.input_jog_dev);
-               kfree(sonypi_device.input_jog_dev.name);
+               input_unregister_device(sonypi_device.input_key_dev);
+               input_unregister_device(sonypi_device.input_jog_dev);
                kfifo_free(sonypi_device.input_fifo);
        }
 
+       misc_deregister(&sonypi_misc_device);
+
        free_irq(sonypi_device.irq, sonypi_irq);
        release_region(sonypi_device.ioport1, sonypi_device.region_size);
-       misc_deregister(&sonypi_misc_device);
-       if (sonypi_device.dev)
+
+       if (sonypi_device.dev) {
                pci_disable_device(sonypi_device.dev);
+               pci_dev_put(sonypi_device.dev);
+       }
+
        kfifo_free(sonypi_device.fifo);
-       pci_dev_put(sonypi_device.dev);
-       printk(KERN_INFO "sonypi: removed.\n");
+
+       return 0;
 }
 
+#ifdef CONFIG_PM
+static int old_camera_power;
+
+static int sonypi_suspend(struct platform_device *dev, pm_message_t state)
+{
+       old_camera_power = sonypi_device.camera_power;
+       sonypi_disable();
+
+       return 0;
+}
+
+static int sonypi_resume(struct platform_device *dev)
+{
+       sonypi_enable(old_camera_power);
+       return 0;
+}
+#else
+#define sonypi_suspend NULL
+#define sonypi_resume  NULL
+#endif
+
+static void sonypi_shutdown(struct platform_device *dev)
+{
+       sonypi_disable();
+}
+
+static struct platform_driver sonypi_driver = {
+       .driver         = {
+               .name   = "sonypi",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sonypi_probe,
+       .remove         = __devexit_p(sonypi_remove),
+       .shutdown       = sonypi_shutdown,
+       .suspend        = sonypi_suspend,
+       .resume         = sonypi_resume,
+};
+
+static struct platform_device *sonypi_platform_device;
+
 static struct dmi_system_id __initdata sonypi_dmi_table[] = {
        {
                .ident = "Sony Vaio",
@@ -1377,26 +1527,52 @@ static struct dmi_system_id __initdata sonypi_dmi_table[] = {
 
 static int __init sonypi_init(void)
 {
-       int ret;
+       int error;
+
+       printk(KERN_INFO
+               "sonypi: Sony Programmable I/O Controller Driver v%s.\n",
+               SONYPI_DRIVER_VERSION);
 
        if (!dmi_check_system(sonypi_dmi_table))
                return -ENODEV;
 
-       ret = driver_register(&sonypi_driver);
-       if (ret)
-               return ret;
+       error = platform_driver_register(&sonypi_driver);
+       if (error)
+               return error;
 
-       ret = sonypi_probe();
-       if (ret)
-               driver_unregister(&sonypi_driver);
+       sonypi_platform_device = platform_device_alloc("sonypi", -1);
+       if (!sonypi_platform_device) {
+               error = -ENOMEM;
+               goto err_driver_unregister;
+       }
 
-       return ret;
+       error = platform_device_add(sonypi_platform_device);
+       if (error)
+               goto err_free_device;
+
+#ifdef CONFIG_ACPI
+       if (acpi_bus_register_driver(&sonypi_acpi_driver) > 0)
+               acpi_enabled = 1;
+#endif
+
+       return 0;
+
+ err_free_device:
+       platform_device_put(sonypi_platform_device);
+ err_driver_unregister:
+       platform_driver_unregister(&sonypi_driver);
+       return error;
 }
 
 static void __exit sonypi_exit(void)
 {
-       driver_unregister(&sonypi_driver);
-       sonypi_remove();
+#ifdef CONFIG_ACPI
+       if (acpi_enabled)
+               acpi_bus_unregister_driver(&sonypi_acpi_driver);
+#endif
+       platform_device_unregister(sonypi_platform_device);
+       platform_driver_unregister(&sonypi_driver);
+       printk(KERN_INFO "sonypi: removed.\n");
 }
 
 module_init(sonypi_init);