sony-laptop: enumerate rfkill devices using SN06
[sfrench/cifs-2.6.git] / drivers / platform / x86 / sony-laptop.c
index f9f68e0e73443b4c7348b31744ae1fe5cb860a7c..49f3fe0ac59ca92b4164d267f8a443bdf9d21412 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,36 +1150,79 @@ 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);
        }
 }
 
-static int sony_nc_rfkill_setup(struct acpi_device *device)
+static void sony_nc_rfkill_setup(struct acpi_device *device)
 {
-       int result, ret;
+       int offset;
+       u8 dev_code, i;
+       acpi_status status;
+       struct acpi_object_list params;
+       union acpi_object in_obj;
+       union acpi_object *device_enum;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 
-       if (sony_find_snc_handle(0x124) == -1)
-               return -1;
+       offset = sony_find_snc_handle(0x124);
+       if (offset == -1) {
+               offset = sony_find_snc_handle(0x135);
+               if (offset == -1)
+                       return;
+               else
+                       sony_rfkill_handle = 0x135;
+       } else
+               sony_rfkill_handle = 0x124;
+       dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle);
 
-       ret = sony_call_snc_handle(0x124, 0xb00, &result);
-       if (ret) {
-               printk(KERN_INFO DRV_PFX
-                      "Unable to enumerate rfkill devices: %x\n", ret);
-               return ret;
+       /* need to read the whole buffer returned by the acpi call to SN06
+        * here otherwise we may miss some features
+        */
+       params.count = 1;
+       params.pointer = &in_obj;
+       in_obj.type = ACPI_TYPE_INTEGER;
+       in_obj.integer.value = offset;
+       status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", &params,
+                       &buffer);
+       if (ACPI_FAILURE(status)) {
+               dprintk("Radio device enumeration failed\n");
+               return;
        }
 
-       if (result & 0x1)
-               sony_nc_setup_rfkill(device, SONY_WIFI);
-       if (result & 0x2)
-               sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
-       if (result & 0x1c)
-               sony_nc_setup_rfkill(device, SONY_WWAN);
-       if (result & 0x20)
-               sony_nc_setup_rfkill(device, SONY_WIMAX);
+       device_enum = (union acpi_object *) buffer.pointer;
+       if (!device_enum || device_enum->type != ACPI_TYPE_BUFFER) {
+               printk(KERN_ERR "Invalid SN06 return object 0x%.2x\n",
+                               device_enum->type);
+               goto out_no_enum;
+       }
 
-       return 0;
+       /* the buffer is filled with magic numbers describing the devices
+        * available, 0xff terminates the enumeration
+        */
+       while ((dev_code = *(device_enum->buffer.pointer + i)) != 0xff &&
+                       i < device_enum->buffer.length) {
+               i++;
+               dprintk("Radio devices, looking at 0x%.2x\n", dev_code);
+
+               if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI])
+                       sony_nc_setup_rfkill(device, SONY_WIFI);
+
+               if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH])
+                       sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
+
+               if ((0xf0 & dev_code) == 0x20 &&
+                               !sony_rfkill_devices[SONY_WWAN])
+                       sony_nc_setup_rfkill(device, SONY_WWAN);
+
+               if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX])
+                       sony_nc_setup_rfkill(device, SONY_WIMAX);
+       }
+
+out_no_enum:
+       kfree(buffer.pointer);
+       return;
 }
 
 static int sony_nc_add(struct acpi_device *device)
@@ -1202,15 +1259,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 +1438,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 +1459,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 +1758,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 +1765,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 +2594,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 +2677,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 +2706,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 +2842,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 +2975,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 +2987,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 +2996,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);
 }