sony-laptop: rfkill support for newer models
[sfrench/cifs-2.6.git] / drivers / platform / x86 / sony-laptop.c
index f9f68e0e73443b4c7348b31744ae1fe5cb860a7c..1144686a33f5dcda31a6d027a1f1cd56d8cb0376 100644 (file)
@@ -131,6 +131,7 @@ enum sony_nc_rfkill {
        N_SONY_RFKILL,
 };
 
+static int sony_rfkill_handle;
 static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
 static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
 static void sony_nc_rfkill_update(void);
@@ -232,6 +233,7 @@ static int sony_laptop_input_index[] = {
        56,     /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */
        57,     /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */
        -1,     /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */
+       58,     /* 72 SONYPI_EVENT_MEDIA_PRESSED */
 };
 
 static int sony_laptop_input_keycode_map[] = {
@@ -293,6 +295,7 @@ static int sony_laptop_input_keycode_map[] = {
        KEY_F15,        /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */
        KEY_VOLUMEUP,   /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */
        KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */
+       KEY_MEDIA,      /* 58 SONYPI_EVENT_MEDIA_PRESSED */
 };
 
 /* release buttons after a short delay if pressed */
@@ -890,6 +893,8 @@ static struct sony_nc_event sony_100_events[] = {
        { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
        { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED },
        { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0xa1, SONYPI_EVENT_MEDIA_PRESSED },
+       { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0, 0 },
 };
 
@@ -961,7 +966,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
                                else
                                        sony_laptop_report_input_event(ev);
                        }
-               } else if (sony_find_snc_handle(0x124) == ev) {
+               } else if (sony_find_snc_handle(sony_rfkill_handle) == ev) {
                        sony_nc_rfkill_update();
                        return;
                }
@@ -1041,6 +1046,9 @@ static int sony_nc_resume(struct acpi_device *device)
                        sony_backlight_update_status(sony_backlight_device) < 0)
                printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
 
+       /* re-read rfkill state */
+       sony_nc_rfkill_update();
+
        return 0;
 }
 
@@ -1064,7 +1072,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)
        if (!blocked)
                argument |= 0xff0000;
 
-       return sony_call_snc_handle(0x124, argument, &result);
+       return sony_call_snc_handle(sony_rfkill_handle, argument, &result);
 }
 
 static const struct rfkill_ops sony_rfkill_ops = {
@@ -1078,6 +1086,8 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
        struct rfkill *rfk;
        enum rfkill_type type;
        const char *name;
+       int result;
+       bool hwblock;
 
        switch (nc_type) {
        case SONY_WIFI:
@@ -1105,6 +1115,10 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
        if (!rfk)
                return -ENOMEM;
 
+       sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);
+       hwblock = !(result & 0x1);
+       rfkill_set_hw_state(rfk, hwblock);
+
        err = rfkill_register(rfk);
        if (err) {
                rfkill_destroy(rfk);
@@ -1120,7 +1134,7 @@ static void sony_nc_rfkill_update()
        int result;
        bool hwblock;
 
-       sony_call_snc_handle(0x124, 0x200, &result);
+       sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);
        hwblock = !(result & 0x1);
 
        for (i = 0; i < N_SONY_RFKILL; i++) {
@@ -1136,7 +1150,7 @@ static void sony_nc_rfkill_update()
                        continue;
                }
 
-               sony_call_snc_handle(0x124, argument, &result);
+               sony_call_snc_handle(sony_rfkill_handle, argument, &result);
                rfkill_set_states(sony_rfkill_devices[i],
                                  !(result & 0xf), false);
        }
@@ -1146,10 +1160,15 @@ static int sony_nc_rfkill_setup(struct acpi_device *device)
 {
        int result, ret;
 
-       if (sony_find_snc_handle(0x124) == -1)
-               return -1;
+       if (sony_find_snc_handle(0x124) == -1) {
+               if (sony_find_snc_handle(0x135) == -1)
+                       return -1;
+               else
+                       sony_rfkill_handle = 0x135;
+       } else
+               sony_rfkill_handle = 0x124;
 
-       ret = sony_call_snc_handle(0x124, 0xb00, &result);
+       ret = sony_call_snc_handle(sony_rfkill_handle, 0xb00, &result);
        if (ret) {
                printk(KERN_INFO DRV_PFX
                       "Unable to enumerate rfkill devices: %x\n", ret);
@@ -1202,15 +1221,6 @@ static int sony_nc_add(struct acpi_device *device)
                }
        }
 
-       /* 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");
-       }
-
        if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
                                         &handle))) {
                if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL))
@@ -1390,27 +1400,20 @@ struct sonypi_eventtypes {
        struct sonypi_event     *events;
 };
 
-struct device_ctrl {
+struct sony_pic_dev {
+       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;
+       struct mutex                    lock;
+       struct sonypi_eventtypes        *event_types;
+       int                             (*handle_irq)(const u8, const u8);
        int                             model;
-       int                             (*handle_irq)(const u8, const u8);
        u16                             evport_offset;
-       u8                              has_camera;
-       u8                              has_bluetooth;
-       u8                              has_wwan;
-       struct sonypi_eventtypes        *event_types;
-};
-
-struct sony_pic_dev {
-       struct device_ctrl      *control;
-       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;
-       struct mutex            lock;
-       u8                      camera_power;
-       u8                      bluetooth_power;
-       u8                      wwan_power;
+       u8                              camera_power;
+       u8                              bluetooth_power;
+       u8                              wwan_power;
 };
 
 static struct sony_pic_dev spic_dev = {
@@ -1418,6 +1421,8 @@ static struct sony_pic_dev spic_dev = {
        .ioports        = LIST_HEAD_INIT(spic_dev.ioports),
 };
 
+static int spic_drv_registered;
+
 /* Event masks */
 #define SONYPI_JOGGER_MASK                     0x00000001
 #define SONYPI_CAPTURE_MASK                    0x00000002
@@ -1715,27 +1720,6 @@ static int type3_handle_irq(const u8 data_mask, const u8 ev)
        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 = type3_handle_irq,
-               .evport_offset = SONYPI_TYPE3_OFFSET,
-               .event_types = type3_events,
-       },
-};
-
 static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
 {
        struct pci_dev *pcidev;
@@ -1743,48 +1727,63 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
        if (pcidev) {
-               dev->control = &spic_types[0];
+               dev->model = SONYPI_DEVICE_TYPE1;
+               dev->evport_offset = SONYPI_TYPE1_OFFSET;
+               dev->event_types = type1_events;
                goto out;
        }
 
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_ICH6_1, NULL);
        if (pcidev) {
-               dev->control = &spic_types[2];
+               dev->model = SONYPI_DEVICE_TYPE2;
+               dev->evport_offset = SONYPI_TYPE2_OFFSET;
+               dev->event_types = type2_events;
                goto out;
        }
 
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_ICH7_1, NULL);
        if (pcidev) {
-               dev->control = &spic_types[2];
+               dev->model = SONYPI_DEVICE_TYPE3;
+               dev->handle_irq = type3_handle_irq;
+               dev->evport_offset = SONYPI_TYPE3_OFFSET;
+               dev->event_types = type3_events;
                goto out;
        }
 
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_ICH8_4, NULL);
        if (pcidev) {
-               dev->control = &spic_types[2];
+               dev->model = SONYPI_DEVICE_TYPE3;
+               dev->handle_irq = type3_handle_irq;
+               dev->evport_offset = SONYPI_TYPE3_OFFSET;
+               dev->event_types = type3_events;
                goto out;
        }
 
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
                        PCI_DEVICE_ID_INTEL_ICH9_1, NULL);
        if (pcidev) {
-               dev->control = &spic_types[2];
+               dev->model = SONYPI_DEVICE_TYPE3;
+               dev->handle_irq = type3_handle_irq;
+               dev->evport_offset = SONYPI_TYPE3_OFFSET;
+               dev->event_types = type3_events;
                goto out;
        }
 
        /* default */
-       dev->control = &spic_types[1];
+       dev->model = SONYPI_DEVICE_TYPE2;
+       dev->evport_offset = SONYPI_TYPE2_OFFSET;
+       dev->event_types = type2_events;
 
 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 : 3);
+                       dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
+                       dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
 }
 
 /* camera tests and poweron/poweroff */
@@ -2557,7 +2556,7 @@ static int sony_pic_enable(struct acpi_device *device,
        buffer.pointer = resource;
 
        /* setup Type 1 resources */
-       if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) {
+       if (spic_dev.model == SONYPI_DEVICE_TYPE1) {
 
                /* setup io resources */
                resource->res1.type = ACPI_RESOURCE_TYPE_IO;
@@ -2640,29 +2639,28 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
                data_mask = inb_p(dev->cur_ioport->io2.minimum);
        else
                data_mask = inb_p(dev->cur_ioport->io1.minimum +
-                               dev->control->evport_offset);
+                               dev->evport_offset);
 
        dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
                        ev, data_mask, dev->cur_ioport->io1.minimum,
-                       dev->control->evport_offset);
+                       dev->evport_offset);
 
        if (ev == 0x00 || ev == 0xff)
                return IRQ_HANDLED;
 
-       for (i = 0; dev->control->event_types[i].mask; i++) {
+       for (i = 0; dev->event_types[i].mask; i++) {
 
-               if ((data_mask & dev->control->event_types[i].data) !=
-                   dev->control->event_types[i].data)
+               if ((data_mask & dev->event_types[i].data) !=
+                   dev->event_types[i].data)
                        continue;
 
-               if (!(mask & dev->control->event_types[i].mask))
+               if (!(mask & dev->event_types[i].mask))
                        continue;
 
-               for (j = 0; dev->control->event_types[i].events[j].event; j++) {
-                       if (ev == dev->control->event_types[i].events[j].data) {
+               for (j = 0; dev->event_types[i].events[j].event; j++) {
+                       if (ev == dev->event_types[i].events[j].data) {
                                device_event =
-                                       dev->control->
-                                               event_types[i].events[j].event;
+                                       dev->event_types[i].events[j].event;
                                goto found;
                        }
                }
@@ -2670,13 +2668,12 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
        /* 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)
+       if (dev->handle_irq && dev->handle_irq(data_mask, ev) == 0)
                return IRQ_HANDLED;
 
        dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
                        ev, data_mask, dev->cur_ioport->io1.minimum,
-                       dev->control->evport_offset);
+                       dev->evport_offset);
        return IRQ_HANDLED;
 
 found:
@@ -2807,7 +2804,7 @@ static int sony_pic_add(struct acpi_device *device)
        /* request IRQ */
        list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
                if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
-                                       IRQF_SHARED, "sony-laptop", &spic_dev)) {
+                                       IRQF_DISABLED, "sony-laptop", &spic_dev)) {
                        dprintk("IRQ: %d - triggering: %d - "
                                        "polarity: %d - shr: %d\n",
                                        irq->irq.interrupts[0],
@@ -2940,6 +2937,7 @@ static int __init sony_laptop_init(void)
                                        "Unable to register SPIC driver.");
                        goto out;
                }
+               spic_drv_registered = 1;
        }
 
        result = acpi_bus_register_driver(&sony_nc_driver);
@@ -2951,7 +2949,7 @@ static int __init sony_laptop_init(void)
        return 0;
 
 out_unregister_pic:
-       if (!no_spic)
+       if (spic_drv_registered)
                acpi_bus_unregister_driver(&sony_pic_driver);
 out:
        return result;
@@ -2960,7 +2958,7 @@ out:
 static void __exit sony_laptop_exit(void)
 {
        acpi_bus_unregister_driver(&sony_nc_driver);
-       if (!no_spic)
+       if (spic_drv_registered)
                acpi_bus_unregister_driver(&sony_pic_driver);
 }