linux/drivers/platform/x86/ideapad-laptop.c
<<
>>
Prefs
   1/*
   2 *  ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
   3 *
   4 *  Copyright © 2010 Intel Corporation
   5 *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License as published by
   9 *  the Free Software Foundation; either version 2 of the License, or
  10 *  (at your option) any later version.
  11 *
  12 *  This program is distributed in the hope that it will be useful,
  13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *  GNU General Public License for more details.
  16 *
  17 *  You should have received a copy of the GNU General Public License
  18 *  along with this program; if not, write to the Free Software
  19 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20 *  02110-1301, USA.
  21 */
  22
  23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  24
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/init.h>
  28#include <linux/types.h>
  29#include <linux/acpi.h>
  30#include <linux/rfkill.h>
  31#include <linux/platform_device.h>
  32#include <linux/input.h>
  33#include <linux/input/sparse-keymap.h>
  34#include <linux/backlight.h>
  35#include <linux/fb.h>
  36#include <linux/debugfs.h>
  37#include <linux/seq_file.h>
  38#include <linux/i8042.h>
  39#include <linux/dmi.h>
  40#include <linux/device.h>
  41#include <acpi/video.h>
  42
  43#define IDEAPAD_RFKILL_DEV_NUM  (3)
  44
  45#define BM_CONSERVATION_BIT (5)
  46#define HA_FNLOCK_BIT       (10)
  47
  48#define CFG_BT_BIT      (16)
  49#define CFG_3G_BIT      (17)
  50#define CFG_WIFI_BIT    (18)
  51#define CFG_CAMERA_BIT  (19)
  52
  53#if IS_ENABLED(CONFIG_ACPI_WMI)
  54static const char *const ideapad_wmi_fnesc_events[] = {
  55        "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
  56        "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
  57};
  58#endif
  59
  60enum {
  61        BMCMD_CONSERVATION_ON = 3,
  62        BMCMD_CONSERVATION_OFF = 5,
  63        HACMD_FNLOCK_ON = 0xe,
  64        HACMD_FNLOCK_OFF = 0xf,
  65};
  66
  67enum {
  68        VPCCMD_R_VPC1 = 0x10,
  69        VPCCMD_R_BL_MAX,
  70        VPCCMD_R_BL,
  71        VPCCMD_W_BL,
  72        VPCCMD_R_WIFI,
  73        VPCCMD_W_WIFI,
  74        VPCCMD_R_BT,
  75        VPCCMD_W_BT,
  76        VPCCMD_R_BL_POWER,
  77        VPCCMD_R_NOVO,
  78        VPCCMD_R_VPC2,
  79        VPCCMD_R_TOUCHPAD,
  80        VPCCMD_W_TOUCHPAD,
  81        VPCCMD_R_CAMERA,
  82        VPCCMD_W_CAMERA,
  83        VPCCMD_R_3G,
  84        VPCCMD_W_3G,
  85        VPCCMD_R_ODD, /* 0x21 */
  86        VPCCMD_W_FAN,
  87        VPCCMD_R_RF,
  88        VPCCMD_W_RF,
  89        VPCCMD_R_FAN = 0x2B,
  90        VPCCMD_R_SPECIAL_BUTTONS = 0x31,
  91        VPCCMD_W_BL_POWER = 0x33,
  92};
  93
  94struct ideapad_rfk_priv {
  95        int dev;
  96        struct ideapad_private *priv;
  97};
  98
  99struct ideapad_private {
 100        struct acpi_device *adev;
 101        struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
 102        struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
 103        struct platform_device *platform_device;
 104        struct input_dev *inputdev;
 105        struct backlight_device *blightdev;
 106        struct dentry *debug;
 107        unsigned long cfg;
 108        bool has_hw_rfkill_switch;
 109        const char *fnesc_guid;
 110};
 111
 112static bool no_bt_rfkill;
 113module_param(no_bt_rfkill, bool, 0444);
 114MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
 115
 116/*
 117 * ACPI Helpers
 118 */
 119#define IDEAPAD_EC_TIMEOUT (200) /* in ms */
 120
 121static int read_method_int(acpi_handle handle, const char *method, int *val)
 122{
 123        acpi_status status;
 124        unsigned long long result;
 125
 126        status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
 127        if (ACPI_FAILURE(status)) {
 128                *val = -1;
 129                return -1;
 130        }
 131        *val = result;
 132        return 0;
 133
 134}
 135
 136static int method_gbmd(acpi_handle handle, unsigned long *ret)
 137{
 138        int result, val;
 139
 140        result = read_method_int(handle, "GBMD", &val);
 141        *ret = val;
 142        return result;
 143}
 144
 145static int method_int1(acpi_handle handle, char *method, int cmd)
 146{
 147        acpi_status status;
 148
 149        status = acpi_execute_simple_method(handle, method, cmd);
 150        return ACPI_FAILURE(status) ? -1 : 0;
 151}
 152
 153static int method_vpcr(acpi_handle handle, int cmd, int *ret)
 154{
 155        acpi_status status;
 156        unsigned long long result;
 157        struct acpi_object_list params;
 158        union acpi_object in_obj;
 159
 160        params.count = 1;
 161        params.pointer = &in_obj;
 162        in_obj.type = ACPI_TYPE_INTEGER;
 163        in_obj.integer.value = cmd;
 164
 165        status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
 166
 167        if (ACPI_FAILURE(status)) {
 168                *ret = -1;
 169                return -1;
 170        }
 171        *ret = result;
 172        return 0;
 173
 174}
 175
 176static int method_vpcw(acpi_handle handle, int cmd, int data)
 177{
 178        struct acpi_object_list params;
 179        union acpi_object in_obj[2];
 180        acpi_status status;
 181
 182        params.count = 2;
 183        params.pointer = in_obj;
 184        in_obj[0].type = ACPI_TYPE_INTEGER;
 185        in_obj[0].integer.value = cmd;
 186        in_obj[1].type = ACPI_TYPE_INTEGER;
 187        in_obj[1].integer.value = data;
 188
 189        status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
 190        if (status != AE_OK)
 191                return -1;
 192        return 0;
 193}
 194
 195static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
 196{
 197        int val;
 198        unsigned long int end_jiffies;
 199
 200        if (method_vpcw(handle, 1, cmd))
 201                return -1;
 202
 203        for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
 204             time_before(jiffies, end_jiffies);) {
 205                schedule();
 206                if (method_vpcr(handle, 1, &val))
 207                        return -1;
 208                if (val == 0) {
 209                        if (method_vpcr(handle, 0, &val))
 210                                return -1;
 211                        *data = val;
 212                        return 0;
 213                }
 214        }
 215        pr_err("timeout in read_ec_cmd\n");
 216        return -1;
 217}
 218
 219static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
 220{
 221        int val;
 222        unsigned long int end_jiffies;
 223
 224        if (method_vpcw(handle, 0, data))
 225                return -1;
 226        if (method_vpcw(handle, 1, cmd))
 227                return -1;
 228
 229        for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
 230             time_before(jiffies, end_jiffies);) {
 231                schedule();
 232                if (method_vpcr(handle, 1, &val))
 233                        return -1;
 234                if (val == 0)
 235                        return 0;
 236        }
 237        pr_err("timeout in %s\n", __func__);
 238        return -1;
 239}
 240
 241/*
 242 * debugfs
 243 */
 244static int debugfs_status_show(struct seq_file *s, void *data)
 245{
 246        struct ideapad_private *priv = s->private;
 247        unsigned long value;
 248
 249        if (!priv)
 250                return -EINVAL;
 251
 252        if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
 253                seq_printf(s, "Backlight max:\t%lu\n", value);
 254        if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
 255                seq_printf(s, "Backlight now:\t%lu\n", value);
 256        if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
 257                seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
 258        seq_printf(s, "=====================\n");
 259
 260        if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
 261                seq_printf(s, "Radio status:\t%s(%lu)\n",
 262                           value ? "On" : "Off", value);
 263        if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
 264                seq_printf(s, "Wifi status:\t%s(%lu)\n",
 265                           value ? "On" : "Off", value);
 266        if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
 267                seq_printf(s, "BT status:\t%s(%lu)\n",
 268                           value ? "On" : "Off", value);
 269        if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
 270                seq_printf(s, "3G status:\t%s(%lu)\n",
 271                           value ? "On" : "Off", value);
 272        seq_printf(s, "=====================\n");
 273
 274        if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
 275                seq_printf(s, "Touchpad status:%s(%lu)\n",
 276                           value ? "On" : "Off", value);
 277        if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
 278                seq_printf(s, "Camera status:\t%s(%lu)\n",
 279                           value ? "On" : "Off", value);
 280        seq_puts(s, "=====================\n");
 281
 282        if (!method_gbmd(priv->adev->handle, &value)) {
 283                seq_printf(s, "Conservation mode:\t%s(%lu)\n",
 284                           test_bit(BM_CONSERVATION_BIT, &value) ? "On" : "Off",
 285                           value);
 286        }
 287
 288        return 0;
 289}
 290DEFINE_SHOW_ATTRIBUTE(debugfs_status);
 291
 292static int debugfs_cfg_show(struct seq_file *s, void *data)
 293{
 294        struct ideapad_private *priv = s->private;
 295
 296        if (!priv) {
 297                seq_printf(s, "cfg: N/A\n");
 298        } else {
 299                seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
 300                           priv->cfg);
 301                if (test_bit(CFG_BT_BIT, &priv->cfg))
 302                        seq_printf(s, "Bluetooth ");
 303                if (test_bit(CFG_3G_BIT, &priv->cfg))
 304                        seq_printf(s, "3G ");
 305                if (test_bit(CFG_WIFI_BIT, &priv->cfg))
 306                        seq_printf(s, "Wireless ");
 307                if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
 308                        seq_printf(s, "Camera ");
 309                seq_printf(s, "\nGraphic: ");
 310                switch ((priv->cfg)&0x700) {
 311                case 0x100:
 312                        seq_printf(s, "Intel");
 313                        break;
 314                case 0x200:
 315                        seq_printf(s, "ATI");
 316                        break;
 317                case 0x300:
 318                        seq_printf(s, "Nvidia");
 319                        break;
 320                case 0x400:
 321                        seq_printf(s, "Intel and ATI");
 322                        break;
 323                case 0x500:
 324                        seq_printf(s, "Intel and Nvidia");
 325                        break;
 326                }
 327                seq_printf(s, "\n");
 328        }
 329        return 0;
 330}
 331DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
 332
 333static int ideapad_debugfs_init(struct ideapad_private *priv)
 334{
 335        struct dentry *node;
 336
 337        priv->debug = debugfs_create_dir("ideapad", NULL);
 338        if (priv->debug == NULL) {
 339                pr_err("failed to create debugfs directory");
 340                goto errout;
 341        }
 342
 343        node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
 344                                   &debugfs_cfg_fops);
 345        if (!node) {
 346                pr_err("failed to create cfg in debugfs");
 347                goto errout;
 348        }
 349
 350        node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
 351                                   &debugfs_status_fops);
 352        if (!node) {
 353                pr_err("failed to create status in debugfs");
 354                goto errout;
 355        }
 356
 357        return 0;
 358
 359errout:
 360        return -ENOMEM;
 361}
 362
 363static void ideapad_debugfs_exit(struct ideapad_private *priv)
 364{
 365        debugfs_remove_recursive(priv->debug);
 366        priv->debug = NULL;
 367}
 368
 369/*
 370 * sysfs
 371 */
 372static ssize_t show_ideapad_cam(struct device *dev,
 373                                struct device_attribute *attr,
 374                                char *buf)
 375{
 376        unsigned long result;
 377        struct ideapad_private *priv = dev_get_drvdata(dev);
 378
 379        if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
 380                return sprintf(buf, "-1\n");
 381        return sprintf(buf, "%lu\n", result);
 382}
 383
 384static ssize_t store_ideapad_cam(struct device *dev,
 385                                 struct device_attribute *attr,
 386                                 const char *buf, size_t count)
 387{
 388        int ret, state;
 389        struct ideapad_private *priv = dev_get_drvdata(dev);
 390
 391        if (!count)
 392                return 0;
 393        if (sscanf(buf, "%i", &state) != 1)
 394                return -EINVAL;
 395        ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
 396        if (ret < 0)
 397                return -EIO;
 398        return count;
 399}
 400
 401static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
 402
 403static ssize_t show_ideapad_fan(struct device *dev,
 404                                struct device_attribute *attr,
 405                                char *buf)
 406{
 407        unsigned long result;
 408        struct ideapad_private *priv = dev_get_drvdata(dev);
 409
 410        if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
 411                return sprintf(buf, "-1\n");
 412        return sprintf(buf, "%lu\n", result);
 413}
 414
 415static ssize_t store_ideapad_fan(struct device *dev,
 416                                 struct device_attribute *attr,
 417                                 const char *buf, size_t count)
 418{
 419        int ret, state;
 420        struct ideapad_private *priv = dev_get_drvdata(dev);
 421
 422        if (!count)
 423                return 0;
 424        if (sscanf(buf, "%i", &state) != 1)
 425                return -EINVAL;
 426        if (state < 0 || state > 4 || state == 3)
 427                return -EINVAL;
 428        ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
 429        if (ret < 0)
 430                return -EIO;
 431        return count;
 432}
 433
 434static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
 435
 436static ssize_t touchpad_show(struct device *dev,
 437                             struct device_attribute *attr,
 438                             char *buf)
 439{
 440        struct ideapad_private *priv = dev_get_drvdata(dev);
 441        unsigned long result;
 442
 443        if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
 444                return sprintf(buf, "-1\n");
 445        return sprintf(buf, "%lu\n", result);
 446}
 447
 448/* Switch to RO for now: It might be revisited in the future */
 449static ssize_t __maybe_unused touchpad_store(struct device *dev,
 450                                             struct device_attribute *attr,
 451                                             const char *buf, size_t count)
 452{
 453        struct ideapad_private *priv = dev_get_drvdata(dev);
 454        bool state;
 455        int ret;
 456
 457        ret = kstrtobool(buf, &state);
 458        if (ret)
 459                return ret;
 460
 461        ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
 462        if (ret < 0)
 463                return -EIO;
 464        return count;
 465}
 466
 467static DEVICE_ATTR_RO(touchpad);
 468
 469static ssize_t conservation_mode_show(struct device *dev,
 470                                struct device_attribute *attr,
 471                                char *buf)
 472{
 473        struct ideapad_private *priv = dev_get_drvdata(dev);
 474        unsigned long result;
 475
 476        if (method_gbmd(priv->adev->handle, &result))
 477                return sprintf(buf, "-1\n");
 478        return sprintf(buf, "%u\n", test_bit(BM_CONSERVATION_BIT, &result));
 479}
 480
 481static ssize_t conservation_mode_store(struct device *dev,
 482                                 struct device_attribute *attr,
 483                                 const char *buf, size_t count)
 484{
 485        struct ideapad_private *priv = dev_get_drvdata(dev);
 486        bool state;
 487        int ret;
 488
 489        ret = kstrtobool(buf, &state);
 490        if (ret)
 491                return ret;
 492
 493        ret = method_int1(priv->adev->handle, "SBMC", state ?
 494                                              BMCMD_CONSERVATION_ON :
 495                                              BMCMD_CONSERVATION_OFF);
 496        if (ret < 0)
 497                return -EIO;
 498        return count;
 499}
 500
 501static DEVICE_ATTR_RW(conservation_mode);
 502
 503static ssize_t fn_lock_show(struct device *dev,
 504                            struct device_attribute *attr,
 505                            char *buf)
 506{
 507        struct ideapad_private *priv = dev_get_drvdata(dev);
 508        unsigned long result;
 509        int hals;
 510        int fail = read_method_int(priv->adev->handle, "HALS", &hals);
 511
 512        if (fail)
 513                return sprintf(buf, "-1\n");
 514
 515        result = hals;
 516        return sprintf(buf, "%u\n", test_bit(HA_FNLOCK_BIT, &result));
 517}
 518
 519static ssize_t fn_lock_store(struct device *dev,
 520                             struct device_attribute *attr,
 521                             const char *buf, size_t count)
 522{
 523        struct ideapad_private *priv = dev_get_drvdata(dev);
 524        bool state;
 525        int ret;
 526
 527        ret = kstrtobool(buf, &state);
 528        if (ret)
 529                return ret;
 530
 531        ret = method_int1(priv->adev->handle, "SALS", state ?
 532                          HACMD_FNLOCK_ON :
 533                          HACMD_FNLOCK_OFF);
 534        if (ret < 0)
 535                return -EIO;
 536        return count;
 537}
 538
 539static DEVICE_ATTR_RW(fn_lock);
 540
 541
 542static struct attribute *ideapad_attributes[] = {
 543        &dev_attr_camera_power.attr,
 544        &dev_attr_fan_mode.attr,
 545        &dev_attr_touchpad.attr,
 546        &dev_attr_conservation_mode.attr,
 547        &dev_attr_fn_lock.attr,
 548        NULL
 549};
 550
 551static umode_t ideapad_is_visible(struct kobject *kobj,
 552                                 struct attribute *attr,
 553                                 int idx)
 554{
 555        struct device *dev = container_of(kobj, struct device, kobj);
 556        struct ideapad_private *priv = dev_get_drvdata(dev);
 557        bool supported;
 558
 559        if (attr == &dev_attr_camera_power.attr)
 560                supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
 561        else if (attr == &dev_attr_fan_mode.attr) {
 562                unsigned long value;
 563                supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
 564                                          &value);
 565        } else if (attr == &dev_attr_conservation_mode.attr) {
 566                supported = acpi_has_method(priv->adev->handle, "GBMD") &&
 567                            acpi_has_method(priv->adev->handle, "SBMC");
 568        } else if (attr == &dev_attr_fn_lock.attr) {
 569                supported = acpi_has_method(priv->adev->handle, "HALS") &&
 570                        acpi_has_method(priv->adev->handle, "SALS");
 571        } else
 572                supported = true;
 573
 574        return supported ? attr->mode : 0;
 575}
 576
 577static const struct attribute_group ideapad_attribute_group = {
 578        .is_visible = ideapad_is_visible,
 579        .attrs = ideapad_attributes
 580};
 581
 582/*
 583 * Rfkill
 584 */
 585struct ideapad_rfk_data {
 586        char *name;
 587        int cfgbit;
 588        int opcode;
 589        int type;
 590};
 591
 592static const struct ideapad_rfk_data ideapad_rfk_data[] = {
 593        { "ideapad_wlan",    CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
 594        { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
 595        { "ideapad_3g",        CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
 596};
 597
 598static int ideapad_rfk_set(void *data, bool blocked)
 599{
 600        struct ideapad_rfk_priv *priv = data;
 601        int opcode = ideapad_rfk_data[priv->dev].opcode;
 602
 603        return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
 604}
 605
 606static const struct rfkill_ops ideapad_rfk_ops = {
 607        .set_block = ideapad_rfk_set,
 608};
 609
 610static void ideapad_sync_rfk_state(struct ideapad_private *priv)
 611{
 612        unsigned long hw_blocked = 0;
 613        int i;
 614
 615        if (priv->has_hw_rfkill_switch) {
 616                if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
 617                        return;
 618                hw_blocked = !hw_blocked;
 619        }
 620
 621        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
 622                if (priv->rfk[i])
 623                        rfkill_set_hw_state(priv->rfk[i], hw_blocked);
 624}
 625
 626static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
 627{
 628        int ret;
 629        unsigned long sw_blocked;
 630
 631        if (no_bt_rfkill &&
 632            (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
 633                /* Force to enable bluetooth when no_bt_rfkill=1 */
 634                write_ec_cmd(priv->adev->handle,
 635                             ideapad_rfk_data[dev].opcode, 1);
 636                return 0;
 637        }
 638        priv->rfk_priv[dev].dev = dev;
 639        priv->rfk_priv[dev].priv = priv;
 640
 641        priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
 642                                      &priv->platform_device->dev,
 643                                      ideapad_rfk_data[dev].type,
 644                                      &ideapad_rfk_ops,
 645                                      &priv->rfk_priv[dev]);
 646        if (!priv->rfk[dev])
 647                return -ENOMEM;
 648
 649        if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
 650                         &sw_blocked)) {
 651                rfkill_init_sw_state(priv->rfk[dev], 0);
 652        } else {
 653                sw_blocked = !sw_blocked;
 654                rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
 655        }
 656
 657        ret = rfkill_register(priv->rfk[dev]);
 658        if (ret) {
 659                rfkill_destroy(priv->rfk[dev]);
 660                return ret;
 661        }
 662        return 0;
 663}
 664
 665static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
 666{
 667        if (!priv->rfk[dev])
 668                return;
 669
 670        rfkill_unregister(priv->rfk[dev]);
 671        rfkill_destroy(priv->rfk[dev]);
 672}
 673
 674/*
 675 * Platform device
 676 */
 677static int ideapad_sysfs_init(struct ideapad_private *priv)
 678{
 679        return sysfs_create_group(&priv->platform_device->dev.kobj,
 680                                    &ideapad_attribute_group);
 681}
 682
 683static void ideapad_sysfs_exit(struct ideapad_private *priv)
 684{
 685        sysfs_remove_group(&priv->platform_device->dev.kobj,
 686                           &ideapad_attribute_group);
 687}
 688
 689/*
 690 * input device
 691 */
 692static const struct key_entry ideapad_keymap[] = {
 693        { KE_KEY, 6,  { KEY_SWITCHVIDEOMODE } },
 694        { KE_KEY, 7,  { KEY_CAMERA } },
 695        { KE_KEY, 8,  { KEY_MICMUTE } },
 696        { KE_KEY, 11, { KEY_F16 } },
 697        { KE_KEY, 13, { KEY_WLAN } },
 698        { KE_KEY, 16, { KEY_PROG1 } },
 699        { KE_KEY, 17, { KEY_PROG2 } },
 700        { KE_KEY, 64, { KEY_PROG3 } },
 701        { KE_KEY, 65, { KEY_PROG4 } },
 702        { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
 703        { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
 704        { KE_KEY, 128, { KEY_ESC } },
 705
 706        { KE_END, 0 },
 707};
 708
 709static int ideapad_input_init(struct ideapad_private *priv)
 710{
 711        struct input_dev *inputdev;
 712        int error;
 713
 714        inputdev = input_allocate_device();
 715        if (!inputdev)
 716                return -ENOMEM;
 717
 718        inputdev->name = "Ideapad extra buttons";
 719        inputdev->phys = "ideapad/input0";
 720        inputdev->id.bustype = BUS_HOST;
 721        inputdev->dev.parent = &priv->platform_device->dev;
 722
 723        error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
 724        if (error) {
 725                pr_err("Unable to setup input device keymap\n");
 726                goto err_free_dev;
 727        }
 728
 729        error = input_register_device(inputdev);
 730        if (error) {
 731                pr_err("Unable to register input device\n");
 732                goto err_free_dev;
 733        }
 734
 735        priv->inputdev = inputdev;
 736        return 0;
 737
 738err_free_dev:
 739        input_free_device(inputdev);
 740        return error;
 741}
 742
 743static void ideapad_input_exit(struct ideapad_private *priv)
 744{
 745        input_unregister_device(priv->inputdev);
 746        priv->inputdev = NULL;
 747}
 748
 749static void ideapad_input_report(struct ideapad_private *priv,
 750                                 unsigned long scancode)
 751{
 752        sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
 753}
 754
 755static void ideapad_input_novokey(struct ideapad_private *priv)
 756{
 757        unsigned long long_pressed;
 758
 759        if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
 760                return;
 761        if (long_pressed)
 762                ideapad_input_report(priv, 17);
 763        else
 764                ideapad_input_report(priv, 16);
 765}
 766
 767static void ideapad_check_special_buttons(struct ideapad_private *priv)
 768{
 769        unsigned long bit, value;
 770
 771        read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
 772
 773        for (bit = 0; bit < 16; bit++) {
 774                if (test_bit(bit, &value)) {
 775                        switch (bit) {
 776                        case 0: /* Z580 */
 777                        case 6: /* Z570 */
 778                                /* Thermal Management button */
 779                                ideapad_input_report(priv, 65);
 780                                break;
 781                        case 1:
 782                                /* OneKey Theater button */
 783                                ideapad_input_report(priv, 64);
 784                                break;
 785                        default:
 786                                pr_info("Unknown special button: %lu\n", bit);
 787                                break;
 788                        }
 789                }
 790        }
 791}
 792
 793/*
 794 * backlight
 795 */
 796static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
 797{
 798        struct ideapad_private *priv = bl_get_data(blightdev);
 799        unsigned long now;
 800
 801        if (!priv)
 802                return -EINVAL;
 803
 804        if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
 805                return -EIO;
 806        return now;
 807}
 808
 809static int ideapad_backlight_update_status(struct backlight_device *blightdev)
 810{
 811        struct ideapad_private *priv = bl_get_data(blightdev);
 812
 813        if (!priv)
 814                return -EINVAL;
 815
 816        if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
 817                         blightdev->props.brightness))
 818                return -EIO;
 819        if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
 820                         blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
 821                return -EIO;
 822
 823        return 0;
 824}
 825
 826static const struct backlight_ops ideapad_backlight_ops = {
 827        .get_brightness = ideapad_backlight_get_brightness,
 828        .update_status = ideapad_backlight_update_status,
 829};
 830
 831static int ideapad_backlight_init(struct ideapad_private *priv)
 832{
 833        struct backlight_device *blightdev;
 834        struct backlight_properties props;
 835        unsigned long max, now, power;
 836
 837        if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
 838                return -EIO;
 839        if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
 840                return -EIO;
 841        if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
 842                return -EIO;
 843
 844        memset(&props, 0, sizeof(struct backlight_properties));
 845        props.max_brightness = max;
 846        props.type = BACKLIGHT_PLATFORM;
 847        blightdev = backlight_device_register("ideapad",
 848                                              &priv->platform_device->dev,
 849                                              priv,
 850                                              &ideapad_backlight_ops,
 851                                              &props);
 852        if (IS_ERR(blightdev)) {
 853                pr_err("Could not register backlight device\n");
 854                return PTR_ERR(blightdev);
 855        }
 856
 857        priv->blightdev = blightdev;
 858        blightdev->props.brightness = now;
 859        blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
 860        backlight_update_status(blightdev);
 861
 862        return 0;
 863}
 864
 865static void ideapad_backlight_exit(struct ideapad_private *priv)
 866{
 867        backlight_device_unregister(priv->blightdev);
 868        priv->blightdev = NULL;
 869}
 870
 871static void ideapad_backlight_notify_power(struct ideapad_private *priv)
 872{
 873        unsigned long power;
 874        struct backlight_device *blightdev = priv->blightdev;
 875
 876        if (!blightdev)
 877                return;
 878        if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
 879                return;
 880        blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
 881}
 882
 883static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
 884{
 885        unsigned long now;
 886
 887        /* if we control brightness via acpi video driver */
 888        if (priv->blightdev == NULL) {
 889                read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
 890                return;
 891        }
 892
 893        backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
 894}
 895
 896/*
 897 * module init/exit
 898 */
 899static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
 900{
 901        unsigned long value;
 902
 903        /* Without reading from EC touchpad LED doesn't switch state */
 904        if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
 905                /* Some IdeaPads don't really turn off touchpad - they only
 906                 * switch the LED state. We (de)activate KBC AUX port to turn
 907                 * touchpad off and on. We send KEY_TOUCHPAD_OFF and
 908                 * KEY_TOUCHPAD_ON to not to get out of sync with LED */
 909                unsigned char param;
 910                i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
 911                              I8042_CMD_AUX_DISABLE);
 912                ideapad_input_report(priv, value ? 67 : 66);
 913        }
 914}
 915
 916static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
 917{
 918        struct ideapad_private *priv = data;
 919        unsigned long vpc1, vpc2, vpc_bit;
 920
 921        if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
 922                return;
 923        if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
 924                return;
 925
 926        vpc1 = (vpc2 << 8) | vpc1;
 927        for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
 928                if (test_bit(vpc_bit, &vpc1)) {
 929                        switch (vpc_bit) {
 930                        case 9:
 931                                ideapad_sync_rfk_state(priv);
 932                                break;
 933                        case 13:
 934                        case 11:
 935                        case 8:
 936                        case 7:
 937                        case 6:
 938                                ideapad_input_report(priv, vpc_bit);
 939                                break;
 940                        case 5:
 941                                ideapad_sync_touchpad_state(priv);
 942                                break;
 943                        case 4:
 944                                ideapad_backlight_notify_brightness(priv);
 945                                break;
 946                        case 3:
 947                                ideapad_input_novokey(priv);
 948                                break;
 949                        case 2:
 950                                ideapad_backlight_notify_power(priv);
 951                                break;
 952                        case 0:
 953                                ideapad_check_special_buttons(priv);
 954                                break;
 955                        case 1:
 956                                /* Some IdeaPads report event 1 every ~20
 957                                 * seconds while on battery power; some
 958                                 * report this when changing to/from tablet
 959                                 * mode. Squelch this event.
 960                                 */
 961                                break;
 962                        default:
 963                                pr_info("Unknown event: %lu\n", vpc_bit);
 964                        }
 965                }
 966        }
 967}
 968
 969#if IS_ENABLED(CONFIG_ACPI_WMI)
 970static void ideapad_wmi_notify(u32 value, void *context)
 971{
 972        switch (value) {
 973        case 128:
 974                ideapad_input_report(context, value);
 975                break;
 976        default:
 977                pr_info("Unknown WMI event %u\n", value);
 978        }
 979}
 980#endif
 981
 982/*
 983 * Some ideapads don't have a hardware rfkill switch, reading VPCCMD_R_RF
 984 * always results in 0 on these models, causing ideapad_laptop to wrongly
 985 * report all radios as hardware-blocked.
 986 */
 987static const struct dmi_system_id no_hw_rfkill_list[] = {
 988        {
 989                .ident = "Lenovo RESCUER R720-15IKBN",
 990                .matches = {
 991                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 992                        DMI_MATCH(DMI_BOARD_NAME, "80WW"),
 993                },
 994        },
 995        {
 996                .ident = "Lenovo G40-30",
 997                .matches = {
 998                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 999                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G40-30"),
1000                },
1001        },
1002        {
1003                .ident = "Lenovo G50-30",
1004                .matches = {
1005                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1006                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"),
1007                },
1008        },
1009        {
1010                .ident = "Lenovo V310-14IKB",
1011                .matches = {
1012                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1013                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14IKB"),
1014                },
1015        },
1016        {
1017                .ident = "Lenovo V310-14ISK",
1018                .matches = {
1019                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1020                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14ISK"),
1021                },
1022        },
1023        {
1024                .ident = "Lenovo V310-15IKB",
1025                .matches = {
1026                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1027                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15IKB"),
1028                },
1029        },
1030        {
1031                .ident = "Lenovo V310-15ISK",
1032                .matches = {
1033                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1034                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
1035                },
1036        },
1037        {
1038                .ident = "Lenovo V510-15IKB",
1039                .matches = {
1040                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1041                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V510-15IKB"),
1042                },
1043        },
1044        {
1045                .ident = "Lenovo ideapad 300-15IBR",
1046                .matches = {
1047                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1048                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IBR"),
1049                },
1050        },
1051        {
1052                .ident = "Lenovo ideapad 300-15IKB",
1053                .matches = {
1054                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1055                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IKB"),
1056                },
1057        },
1058        {
1059                .ident = "Lenovo ideapad 300S-11IBR",
1060                .matches = {
1061                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1062                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300S-11BR"),
1063                },
1064        },
1065        {
1066                .ident = "Lenovo ideapad 310-15ABR",
1067                .matches = {
1068                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1069                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ABR"),
1070                },
1071        },
1072        {
1073                .ident = "Lenovo ideapad 310-15IAP",
1074                .matches = {
1075                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1076                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IAP"),
1077                },
1078        },
1079        {
1080                .ident = "Lenovo ideapad 310-15IKB",
1081                .matches = {
1082                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1083                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
1084                },
1085        },
1086        {
1087                .ident = "Lenovo ideapad 310-15ISK",
1088                .matches = {
1089                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1090                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ISK"),
1091                },
1092        },
1093        {
1094                .ident = "Lenovo ideapad Y700-14ISK",
1095                .matches = {
1096                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1097                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-14ISK"),
1098                },
1099        },
1100        {
1101                .ident = "Lenovo ideapad Y700-15ACZ",
1102                .matches = {
1103                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1104                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ACZ"),
1105                },
1106        },
1107        {
1108                .ident = "Lenovo ideapad Y700-15ISK",
1109                .matches = {
1110                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1111                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"),
1112                },
1113        },
1114        {
1115                .ident = "Lenovo ideapad Y700 Touch-15ISK",
1116                .matches = {
1117                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1118                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"),
1119                },
1120        },
1121        {
1122                .ident = "Lenovo ideapad Y700-17ISK",
1123                .matches = {
1124                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1125                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"),
1126                },
1127        },
1128        {
1129                .ident = "Lenovo ideapad MIIX 720-12IKB",
1130                .matches = {
1131                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1132                        DMI_MATCH(DMI_PRODUCT_VERSION, "MIIX 720-12IKB"),
1133                },
1134        },
1135        {
1136                .ident = "Lenovo Legion Y520-15IKBN",
1137                .matches = {
1138                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1139                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"),
1140                },
1141        },
1142        {
1143                .ident = "Lenovo Legion Y720-15IKB",
1144                .matches = {
1145                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1146                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKB"),
1147                },
1148        },
1149        {
1150                .ident = "Lenovo Legion Y720-15IKBN",
1151                .matches = {
1152                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1153                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBN"),
1154                },
1155        },
1156        {
1157                .ident = "Lenovo Yoga 2 11 / 13 / Pro",
1158                .matches = {
1159                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1160                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
1161                },
1162        },
1163        {
1164                .ident = "Lenovo Yoga 2 11 / 13 / Pro",
1165                .matches = {
1166                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1167                        DMI_MATCH(DMI_BOARD_NAME, "Yoga2"),
1168                },
1169        },
1170        {
1171                .ident = "Lenovo Yoga 3 1170 / 1470",
1172                .matches = {
1173                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1174                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3"),
1175                },
1176        },
1177        {
1178                .ident = "Lenovo Yoga 3 Pro 1370",
1179                .matches = {
1180                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1181                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"),
1182                },
1183        },
1184        {
1185                .ident = "Lenovo Yoga 700",
1186                .matches = {
1187                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1188                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 700"),
1189                },
1190        },
1191        {
1192                .ident = "Lenovo Yoga 900",
1193                .matches = {
1194                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1195                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"),
1196                },
1197        },
1198        {
1199                .ident = "Lenovo Yoga 900",
1200                .matches = {
1201                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1202                        DMI_MATCH(DMI_BOARD_NAME, "VIUU4"),
1203                },
1204        },
1205        {
1206                .ident = "Lenovo YOGA 910-13IKB",
1207                .matches = {
1208                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1209                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"),
1210                },
1211        },
1212        {
1213                .ident = "Lenovo YOGA 920-13IKB",
1214                .matches = {
1215                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1216                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"),
1217                },
1218        },
1219        {
1220                .ident = "Lenovo Zhaoyang E42-80",
1221                .matches = {
1222                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1223                        DMI_MATCH(DMI_PRODUCT_VERSION, "ZHAOYANG E42-80"),
1224                },
1225        },
1226        {}
1227};
1228
1229static int ideapad_acpi_add(struct platform_device *pdev)
1230{
1231        int ret, i;
1232        int cfg;
1233        struct ideapad_private *priv;
1234        struct acpi_device *adev;
1235
1236        ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
1237        if (ret)
1238                return -ENODEV;
1239
1240        if (read_method_int(adev->handle, "_CFG", &cfg))
1241                return -ENODEV;
1242
1243        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1244        if (!priv)
1245                return -ENOMEM;
1246
1247        dev_set_drvdata(&pdev->dev, priv);
1248        priv->cfg = cfg;
1249        priv->adev = adev;
1250        priv->platform_device = pdev;
1251        priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list);
1252
1253        ret = ideapad_sysfs_init(priv);
1254        if (ret)
1255                return ret;
1256
1257        ret = ideapad_debugfs_init(priv);
1258        if (ret)
1259                goto debugfs_failed;
1260
1261        ret = ideapad_input_init(priv);
1262        if (ret)
1263                goto input_failed;
1264
1265        /*
1266         * On some models without a hw-switch (the yoga 2 13 at least)
1267         * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
1268         */
1269        if (!priv->has_hw_rfkill_switch)
1270                write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
1271
1272        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1273                if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1274                        ideapad_register_rfkill(priv, i);
1275
1276        ideapad_sync_rfk_state(priv);
1277        ideapad_sync_touchpad_state(priv);
1278
1279        if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1280                ret = ideapad_backlight_init(priv);
1281                if (ret && ret != -ENODEV)
1282                        goto backlight_failed;
1283        }
1284        ret = acpi_install_notify_handler(adev->handle,
1285                ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
1286        if (ret)
1287                goto notification_failed;
1288
1289#if IS_ENABLED(CONFIG_ACPI_WMI)
1290        for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
1291                ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
1292                                                 ideapad_wmi_notify, priv);
1293                if (ret == AE_OK) {
1294                        priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
1295                        break;
1296                }
1297        }
1298        if (ret != AE_OK && ret != AE_NOT_EXIST)
1299                goto notification_failed_wmi;
1300#endif
1301
1302        return 0;
1303#if IS_ENABLED(CONFIG_ACPI_WMI)
1304notification_failed_wmi:
1305        acpi_remove_notify_handler(priv->adev->handle,
1306                ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1307#endif
1308notification_failed:
1309        ideapad_backlight_exit(priv);
1310backlight_failed:
1311        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1312                ideapad_unregister_rfkill(priv, i);
1313        ideapad_input_exit(priv);
1314input_failed:
1315        ideapad_debugfs_exit(priv);
1316debugfs_failed:
1317        ideapad_sysfs_exit(priv);
1318        return ret;
1319}
1320
1321static int ideapad_acpi_remove(struct platform_device *pdev)
1322{
1323        struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
1324        int i;
1325
1326#if IS_ENABLED(CONFIG_ACPI_WMI)
1327        if (priv->fnesc_guid)
1328                wmi_remove_notify_handler(priv->fnesc_guid);
1329#endif
1330        acpi_remove_notify_handler(priv->adev->handle,
1331                ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1332        ideapad_backlight_exit(priv);
1333        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1334                ideapad_unregister_rfkill(priv, i);
1335        ideapad_input_exit(priv);
1336        ideapad_debugfs_exit(priv);
1337        ideapad_sysfs_exit(priv);
1338        dev_set_drvdata(&pdev->dev, NULL);
1339
1340        return 0;
1341}
1342
1343#ifdef CONFIG_PM_SLEEP
1344static int ideapad_acpi_resume(struct device *device)
1345{
1346        struct ideapad_private *priv;
1347
1348        if (!device)
1349                return -EINVAL;
1350        priv = dev_get_drvdata(device);
1351
1352        ideapad_sync_rfk_state(priv);
1353        ideapad_sync_touchpad_state(priv);
1354        return 0;
1355}
1356#endif
1357static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
1358
1359static const struct acpi_device_id ideapad_device_ids[] = {
1360        { "VPC2004", 0},
1361        { "", 0},
1362};
1363MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
1364
1365static struct platform_driver ideapad_acpi_driver = {
1366        .probe = ideapad_acpi_add,
1367        .remove = ideapad_acpi_remove,
1368        .driver = {
1369                .name   = "ideapad_acpi",
1370                .pm     = &ideapad_pm,
1371                .acpi_match_table = ACPI_PTR(ideapad_device_ids),
1372        },
1373};
1374
1375module_platform_driver(ideapad_acpi_driver);
1376
1377MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1378MODULE_DESCRIPTION("IdeaPad ACPI Extras");
1379MODULE_LICENSE("GPL");
1380