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