linux/drivers/platform/x86/fujitsu-laptop.c
<<
>>
Prefs
   1/*-*-linux-c-*-*/
   2
   3/*
   4  Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
   5  Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
   6  Copyright (C) 2008 Tony Vroon <tony@linx.net>
   7  Based on earlier work:
   8    Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
   9    Adrian Yee <brewt-fujitsu@brewt.org>
  10
  11  Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
  12  by its respective authors.
  13
  14  This program is free software; you can redistribute it and/or modify
  15  it under the terms of the GNU General Public License as published by
  16  the Free Software Foundation; either version 2 of the License, or
  17  (at your option) any later version.
  18
  19  This program is distributed in the hope that it will be useful, but
  20  WITHOUT ANY WARRANTY; without even the implied warranty of
  21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  22  General Public License for more details.
  23
  24  You should have received a copy of the GNU General Public License
  25  along with this program; if not, write to the Free Software
  26  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  27  02110-1301, USA.
  28 */
  29
  30/*
  31 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
  32 * features made available on a range of Fujitsu laptops including the
  33 * P2xxx/P5xxx/S6xxx/S7xxx series.
  34 *
  35 * This driver implements a vendor-specific backlight control interface for
  36 * Fujitsu laptops and provides support for hotkeys present on certain Fujitsu
  37 * laptops.
  38 *
  39 * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
  40 * P8010.  It should work on most P-series and S-series Lifebooks, but
  41 * YMMV.
  42 *
  43 * The module parameter use_alt_lcd_levels switches between different ACPI
  44 * brightness controls which are used by different Fujitsu laptops.  In most
  45 * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
  46 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
  47 *
  48 */
  49
  50#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  51
  52#include <linux/module.h>
  53#include <linux/kernel.h>
  54#include <linux/init.h>
  55#include <linux/acpi.h>
  56#include <linux/dmi.h>
  57#include <linux/backlight.h>
  58#include <linux/fb.h>
  59#include <linux/input.h>
  60#include <linux/input/sparse-keymap.h>
  61#include <linux/kfifo.h>
  62#include <linux/leds.h>
  63#include <linux/platform_device.h>
  64#include <linux/slab.h>
  65#include <acpi/video.h>
  66
  67#define FUJITSU_DRIVER_VERSION "0.6.0"
  68
  69#define FUJITSU_LCD_N_LEVELS 8
  70
  71#define ACPI_FUJITSU_CLASS              "fujitsu"
  72#define ACPI_FUJITSU_BL_HID             "FUJ02B1"
  73#define ACPI_FUJITSU_BL_DRIVER_NAME     "Fujitsu laptop FUJ02B1 ACPI brightness driver"
  74#define ACPI_FUJITSU_BL_DEVICE_NAME     "Fujitsu FUJ02B1"
  75#define ACPI_FUJITSU_LAPTOP_HID         "FUJ02E3"
  76#define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
  77#define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3"
  78
  79#define ACPI_FUJITSU_NOTIFY_CODE1     0x80
  80
  81/* FUNC interface - command values */
  82#define FUNC_FLAGS      0x1000
  83#define FUNC_LEDS       0x1001
  84#define FUNC_BUTTONS    0x1002
  85#define FUNC_BACKLIGHT  0x1004
  86
  87/* FUNC interface - responses */
  88#define UNSUPPORTED_CMD 0x80000000
  89
  90/* FUNC interface - status flags */
  91#define FLAG_RFKILL     0x020
  92#define FLAG_LID        0x100
  93#define FLAG_DOCK       0x200
  94
  95/* FUNC interface - LED control */
  96#define FUNC_LED_OFF    0x1
  97#define FUNC_LED_ON     0x30001
  98#define KEYBOARD_LAMPS  0x100
  99#define LOGOLAMP_POWERON 0x2000
 100#define LOGOLAMP_ALWAYS  0x4000
 101#define RADIO_LED_ON    0x20
 102#define ECO_LED 0x10000
 103#define ECO_LED_ON      0x80000
 104
 105/* Hotkey details */
 106#define KEY1_CODE       0x410   /* codes for the keys in the GIRB register */
 107#define KEY2_CODE       0x411
 108#define KEY3_CODE       0x412
 109#define KEY4_CODE       0x413
 110#define KEY5_CODE       0x420
 111
 112#define MAX_HOTKEY_RINGBUFFER_SIZE 100
 113#define RINGBUFFERSIZE 40
 114
 115/* Debugging */
 116#define FUJLAPTOP_DBG_ERROR       0x0001
 117#define FUJLAPTOP_DBG_WARN        0x0002
 118#define FUJLAPTOP_DBG_INFO        0x0004
 119#define FUJLAPTOP_DBG_TRACE       0x0008
 120
 121#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
 122#define vdbg_printk(a_dbg_level, format, arg...) \
 123        do { if (dbg_level & a_dbg_level) \
 124                printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
 125        } while (0)
 126#else
 127#define vdbg_printk(a_dbg_level, format, arg...) \
 128        do { } while (0)
 129#endif
 130
 131/* Device controlling the backlight and associated keys */
 132struct fujitsu_bl {
 133        acpi_handle acpi_handle;
 134        struct input_dev *input;
 135        char phys[32];
 136        struct backlight_device *bl_device;
 137        unsigned int max_brightness;
 138        unsigned int brightness_level;
 139};
 140
 141static struct fujitsu_bl *fujitsu_bl;
 142static int use_alt_lcd_levels = -1;
 143static bool disable_brightness_adjust;
 144
 145/* Device used to access hotkeys and other features on the laptop */
 146struct fujitsu_laptop {
 147        acpi_handle acpi_handle;
 148        struct acpi_device *dev;
 149        struct input_dev *input;
 150        char phys[32];
 151        struct platform_device *pf_device;
 152        struct kfifo fifo;
 153        spinlock_t fifo_lock;
 154        int flags_supported;
 155        int flags_state;
 156};
 157
 158static struct fujitsu_laptop *fujitsu_laptop;
 159
 160#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
 161static u32 dbg_level = 0x03;
 162#endif
 163
 164/* Fujitsu ACPI interface function */
 165
 166static int call_fext_func(int func, int op, int feature, int state)
 167{
 168        union acpi_object params[4] = {
 169                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
 170                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
 171                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
 172                { .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
 173        };
 174        struct acpi_object_list arg_list = { 4, params };
 175        unsigned long long value;
 176        acpi_status status;
 177
 178        status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC",
 179                                       &arg_list, &value);
 180        if (ACPI_FAILURE(status)) {
 181                vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate FUNC\n");
 182                return -ENODEV;
 183        }
 184
 185        vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
 186                    func, op, feature, state, (int)value);
 187        return value;
 188}
 189
 190/* Hardware access for LCD brightness control */
 191
 192static int set_lcd_level(int level)
 193{
 194        acpi_status status;
 195        char *method;
 196
 197        switch (use_alt_lcd_levels) {
 198        case -1:
 199                if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2"))
 200                        method = "SBL2";
 201                else
 202                        method = "SBLL";
 203                break;
 204        case 1:
 205                method = "SBL2";
 206                break;
 207        default:
 208                method = "SBLL";
 209                break;
 210        }
 211
 212        vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n",
 213                    method, level);
 214
 215        if (level < 0 || level >= fujitsu_bl->max_brightness)
 216                return -EINVAL;
 217
 218        status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method,
 219                                            level);
 220        if (ACPI_FAILURE(status)) {
 221                vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n",
 222                            method);
 223                return -ENODEV;
 224        }
 225
 226        fujitsu_bl->brightness_level = level;
 227
 228        return 0;
 229}
 230
 231static int get_lcd_level(void)
 232{
 233        unsigned long long state = 0;
 234        acpi_status status = AE_OK;
 235
 236        vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
 237
 238        status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL,
 239                                       &state);
 240        if (ACPI_FAILURE(status))
 241                return 0;
 242
 243        fujitsu_bl->brightness_level = state & 0x0fffffff;
 244
 245        return fujitsu_bl->brightness_level;
 246}
 247
 248static int get_max_brightness(void)
 249{
 250        unsigned long long state = 0;
 251        acpi_status status = AE_OK;
 252
 253        vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
 254
 255        status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL,
 256                                       &state);
 257        if (ACPI_FAILURE(status))
 258                return -1;
 259
 260        fujitsu_bl->max_brightness = state;
 261
 262        return fujitsu_bl->max_brightness;
 263}
 264
 265/* Backlight device stuff */
 266
 267static int bl_get_brightness(struct backlight_device *b)
 268{
 269        return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level();
 270}
 271
 272static int bl_update_status(struct backlight_device *b)
 273{
 274        if (b->props.power == FB_BLANK_POWERDOWN)
 275                call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
 276        else
 277                call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
 278
 279        return set_lcd_level(b->props.brightness);
 280}
 281
 282static const struct backlight_ops fujitsu_bl_ops = {
 283        .get_brightness = bl_get_brightness,
 284        .update_status = bl_update_status,
 285};
 286
 287static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
 288                        char *buf)
 289{
 290        if (!(fujitsu_laptop->flags_supported & FLAG_LID))
 291                return sprintf(buf, "unknown\n");
 292        if (fujitsu_laptop->flags_state & FLAG_LID)
 293                return sprintf(buf, "open\n");
 294        else
 295                return sprintf(buf, "closed\n");
 296}
 297
 298static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
 299                         char *buf)
 300{
 301        if (!(fujitsu_laptop->flags_supported & FLAG_DOCK))
 302                return sprintf(buf, "unknown\n");
 303        if (fujitsu_laptop->flags_state & FLAG_DOCK)
 304                return sprintf(buf, "docked\n");
 305        else
 306                return sprintf(buf, "undocked\n");
 307}
 308
 309static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
 310                           char *buf)
 311{
 312        if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL))
 313                return sprintf(buf, "unknown\n");
 314        if (fujitsu_laptop->flags_state & FLAG_RFKILL)
 315                return sprintf(buf, "on\n");
 316        else
 317                return sprintf(buf, "killed\n");
 318}
 319
 320static DEVICE_ATTR_RO(lid);
 321static DEVICE_ATTR_RO(dock);
 322static DEVICE_ATTR_RO(radios);
 323
 324static struct attribute *fujitsu_pf_attributes[] = {
 325        &dev_attr_lid.attr,
 326        &dev_attr_dock.attr,
 327        &dev_attr_radios.attr,
 328        NULL
 329};
 330
 331static struct attribute_group fujitsu_pf_attribute_group = {
 332        .attrs = fujitsu_pf_attributes
 333};
 334
 335static struct platform_driver fujitsu_pf_driver = {
 336        .driver = {
 337                   .name = "fujitsu-laptop",
 338                   }
 339};
 340
 341/* ACPI device for LCD brightness control */
 342
 343static const struct key_entry keymap_backlight[] = {
 344        { KE_KEY, true, { KEY_BRIGHTNESSUP } },
 345        { KE_KEY, false, { KEY_BRIGHTNESSDOWN } },
 346        { KE_END, 0 }
 347};
 348
 349static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
 350{
 351        struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device);
 352        int ret;
 353
 354        fujitsu_bl->input = devm_input_allocate_device(&device->dev);
 355        if (!fujitsu_bl->input)
 356                return -ENOMEM;
 357
 358        snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys),
 359                 "%s/video/input0", acpi_device_hid(device));
 360
 361        fujitsu_bl->input->name = acpi_device_name(device);
 362        fujitsu_bl->input->phys = fujitsu_bl->phys;
 363        fujitsu_bl->input->id.bustype = BUS_HOST;
 364        fujitsu_bl->input->id.product = 0x06;
 365
 366        ret = sparse_keymap_setup(fujitsu_bl->input, keymap_backlight, NULL);
 367        if (ret)
 368                return ret;
 369
 370        return input_register_device(fujitsu_bl->input);
 371}
 372
 373static int fujitsu_backlight_register(struct acpi_device *device)
 374{
 375        const struct backlight_properties props = {
 376                .brightness = fujitsu_bl->brightness_level,
 377                .max_brightness = fujitsu_bl->max_brightness - 1,
 378                .type = BACKLIGHT_PLATFORM
 379        };
 380        struct backlight_device *bd;
 381
 382        bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
 383                                            &device->dev, NULL,
 384                                            &fujitsu_bl_ops, &props);
 385        if (IS_ERR(bd))
 386                return PTR_ERR(bd);
 387
 388        fujitsu_bl->bl_device = bd;
 389
 390        return 0;
 391}
 392
 393static int acpi_fujitsu_bl_add(struct acpi_device *device)
 394{
 395        int state = 0;
 396        int error;
 397
 398        if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
 399                return -ENODEV;
 400
 401        if (!device)
 402                return -EINVAL;
 403
 404        fujitsu_bl->acpi_handle = device->handle;
 405        sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME);
 406        sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
 407        device->driver_data = fujitsu_bl;
 408
 409        error = acpi_fujitsu_bl_input_setup(device);
 410        if (error)
 411                return error;
 412
 413        error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state);
 414        if (error) {
 415                pr_err("Error reading power state\n");
 416                return error;
 417        }
 418
 419        pr_info("ACPI: %s [%s] (%s)\n",
 420               acpi_device_name(device), acpi_device_bid(device),
 421               !device->power.state ? "on" : "off");
 422
 423        if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
 424                vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
 425                if (ACPI_FAILURE
 426                    (acpi_evaluate_object
 427                     (device->handle, METHOD_NAME__INI, NULL, NULL)))
 428                        pr_err("_INI Method failed\n");
 429        }
 430
 431        if (get_max_brightness() <= 0)
 432                fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS;
 433        get_lcd_level();
 434
 435        error = fujitsu_backlight_register(device);
 436        if (error)
 437                return error;
 438
 439        return 0;
 440}
 441
 442/* Brightness notify */
 443
 444static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
 445{
 446        struct input_dev *input;
 447        int oldb, newb;
 448
 449        input = fujitsu_bl->input;
 450
 451        if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
 452                vdbg_printk(FUJLAPTOP_DBG_WARN,
 453                            "unsupported event [0x%x]\n", event);
 454                sparse_keymap_report_event(input, -1, 1, true);
 455                return;
 456        }
 457
 458        oldb = fujitsu_bl->brightness_level;
 459        get_lcd_level();
 460        newb = fujitsu_bl->brightness_level;
 461
 462        vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n",
 463                    oldb, newb);
 464
 465        if (oldb == newb)
 466                return;
 467
 468        if (!disable_brightness_adjust)
 469                set_lcd_level(newb);
 470
 471        sparse_keymap_report_event(input, oldb < newb, 1, true);
 472}
 473
 474/* ACPI device for hotkey handling */
 475
 476static const struct key_entry keymap_default[] = {
 477        { KE_KEY, KEY1_CODE, { KEY_PROG1 } },
 478        { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
 479        { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
 480        { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
 481        { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
 482        { KE_KEY, BIT(26),   { KEY_TOUCHPAD_TOGGLE } },
 483        { KE_END, 0 }
 484};
 485
 486static const struct key_entry keymap_s64x0[] = {
 487        { KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } },      /* "Lock" */
 488        { KE_KEY, KEY2_CODE, { KEY_HELP } },            /* "Mobility Center */
 489        { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
 490        { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
 491        { KE_END, 0 }
 492};
 493
 494static const struct key_entry keymap_p8010[] = {
 495        { KE_KEY, KEY1_CODE, { KEY_HELP } },            /* "Support" */
 496        { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
 497        { KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } }, /* "Presentation" */
 498        { KE_KEY, KEY4_CODE, { KEY_WWW } },             /* "WWW" */
 499        { KE_END, 0 }
 500};
 501
 502static const struct key_entry *keymap = keymap_default;
 503
 504static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
 505{
 506        pr_info("Identified laptop model '%s'\n", id->ident);
 507        keymap = id->driver_data;
 508        return 1;
 509}
 510
 511static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
 512        {
 513                .callback = fujitsu_laptop_dmi_keymap_override,
 514                .ident = "Fujitsu Siemens S6410",
 515                .matches = {
 516                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 517                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
 518                },
 519                .driver_data = (void *)keymap_s64x0
 520        },
 521        {
 522                .callback = fujitsu_laptop_dmi_keymap_override,
 523                .ident = "Fujitsu Siemens S6420",
 524                .matches = {
 525                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 526                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
 527                },
 528                .driver_data = (void *)keymap_s64x0
 529        },
 530        {
 531                .callback = fujitsu_laptop_dmi_keymap_override,
 532                .ident = "Fujitsu LifeBook P8010",
 533                .matches = {
 534                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
 535                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
 536                },
 537                .driver_data = (void *)keymap_p8010
 538        },
 539        {}
 540};
 541
 542static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
 543{
 544        struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
 545        int ret;
 546
 547        fujitsu_laptop->input = devm_input_allocate_device(&device->dev);
 548        if (!fujitsu_laptop->input)
 549                return -ENOMEM;
 550
 551        snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys),
 552                 "%s/video/input0", acpi_device_hid(device));
 553
 554        fujitsu_laptop->input->name = acpi_device_name(device);
 555        fujitsu_laptop->input->phys = fujitsu_laptop->phys;
 556        fujitsu_laptop->input->id.bustype = BUS_HOST;
 557        fujitsu_laptop->input->id.product = 0x06;
 558
 559        dmi_check_system(fujitsu_laptop_dmi_table);
 560        ret = sparse_keymap_setup(fujitsu_laptop->input, keymap, NULL);
 561        if (ret)
 562                return ret;
 563
 564        return input_register_device(fujitsu_laptop->input);
 565}
 566
 567static int fujitsu_laptop_platform_add(void)
 568{
 569        int ret;
 570
 571        fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1);
 572        if (!fujitsu_laptop->pf_device)
 573                return -ENOMEM;
 574
 575        ret = platform_device_add(fujitsu_laptop->pf_device);
 576        if (ret)
 577                goto err_put_platform_device;
 578
 579        ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj,
 580                                 &fujitsu_pf_attribute_group);
 581        if (ret)
 582                goto err_del_platform_device;
 583
 584        return 0;
 585
 586err_del_platform_device:
 587        platform_device_del(fujitsu_laptop->pf_device);
 588err_put_platform_device:
 589        platform_device_put(fujitsu_laptop->pf_device);
 590
 591        return ret;
 592}
 593
 594static void fujitsu_laptop_platform_remove(void)
 595{
 596        sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj,
 597                           &fujitsu_pf_attribute_group);
 598        platform_device_unregister(fujitsu_laptop->pf_device);
 599}
 600
 601static int logolamp_set(struct led_classdev *cdev,
 602                        enum led_brightness brightness)
 603{
 604        int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
 605        int ret;
 606
 607        if (brightness < LED_HALF)
 608                poweron = FUNC_LED_OFF;
 609
 610        if (brightness < LED_FULL)
 611                always = FUNC_LED_OFF;
 612
 613        ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
 614        if (ret < 0)
 615                return ret;
 616
 617        return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
 618}
 619
 620static enum led_brightness logolamp_get(struct led_classdev *cdev)
 621{
 622        int ret;
 623
 624        ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
 625        if (ret == FUNC_LED_ON)
 626                return LED_FULL;
 627
 628        ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
 629        if (ret == FUNC_LED_ON)
 630                return LED_HALF;
 631
 632        return LED_OFF;
 633}
 634
 635static struct led_classdev logolamp_led = {
 636        .name = "fujitsu::logolamp",
 637        .brightness_set_blocking = logolamp_set,
 638        .brightness_get = logolamp_get
 639};
 640
 641static int kblamps_set(struct led_classdev *cdev,
 642                       enum led_brightness brightness)
 643{
 644        if (brightness >= LED_FULL)
 645                return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
 646                                      FUNC_LED_ON);
 647        else
 648                return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
 649                                      FUNC_LED_OFF);
 650}
 651
 652static enum led_brightness kblamps_get(struct led_classdev *cdev)
 653{
 654        enum led_brightness brightness = LED_OFF;
 655
 656        if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
 657                brightness = LED_FULL;
 658
 659        return brightness;
 660}
 661
 662static struct led_classdev kblamps_led = {
 663        .name = "fujitsu::kblamps",
 664        .brightness_set_blocking = kblamps_set,
 665        .brightness_get = kblamps_get
 666};
 667
 668static int radio_led_set(struct led_classdev *cdev,
 669                         enum led_brightness brightness)
 670{
 671        if (brightness >= LED_FULL)
 672                return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON,
 673                                      RADIO_LED_ON);
 674        else
 675                return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0);
 676}
 677
 678static enum led_brightness radio_led_get(struct led_classdev *cdev)
 679{
 680        enum led_brightness brightness = LED_OFF;
 681
 682        if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
 683                brightness = LED_FULL;
 684
 685        return brightness;
 686}
 687
 688static struct led_classdev radio_led = {
 689        .name = "fujitsu::radio_led",
 690        .brightness_set_blocking = radio_led_set,
 691        .brightness_get = radio_led_get,
 692        .default_trigger = "rfkill-any"
 693};
 694
 695static int eco_led_set(struct led_classdev *cdev,
 696                       enum led_brightness brightness)
 697{
 698        int curr;
 699
 700        curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
 701        if (brightness >= LED_FULL)
 702                return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
 703                                      curr | ECO_LED_ON);
 704        else
 705                return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
 706                                      curr & ~ECO_LED_ON);
 707}
 708
 709static enum led_brightness eco_led_get(struct led_classdev *cdev)
 710{
 711        enum led_brightness brightness = LED_OFF;
 712
 713        if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
 714                brightness = LED_FULL;
 715
 716        return brightness;
 717}
 718
 719static struct led_classdev eco_led = {
 720        .name = "fujitsu::eco_led",
 721        .brightness_set_blocking = eco_led_set,
 722        .brightness_get = eco_led_get
 723};
 724
 725static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
 726{
 727        int result;
 728
 729        if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
 730                result = devm_led_classdev_register(&device->dev,
 731                                                    &logolamp_led);
 732                if (result)
 733                        return result;
 734        }
 735
 736        if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
 737            (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
 738                result = devm_led_classdev_register(&device->dev, &kblamps_led);
 739                if (result)
 740                        return result;
 741        }
 742
 743        /*
 744         * BTNI bit 24 seems to indicate the presence of a radio toggle
 745         * button in place of a slide switch, and all such machines appear
 746         * to also have an RF LED.  Therefore use bit 24 as an indicator
 747         * that an RF LED is present.
 748         */
 749        if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
 750                result = devm_led_classdev_register(&device->dev, &radio_led);
 751                if (result)
 752                        return result;
 753        }
 754
 755        /* Support for eco led is not always signaled in bit corresponding
 756         * to the bit used to control the led. According to the DSDT table,
 757         * bit 14 seems to indicate presence of said led as well.
 758         * Confirm by testing the status.
 759         */
 760        if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
 761            (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
 762                result = devm_led_classdev_register(&device->dev, &eco_led);
 763                if (result)
 764                        return result;
 765        }
 766
 767        return 0;
 768}
 769
 770static int acpi_fujitsu_laptop_add(struct acpi_device *device)
 771{
 772        int state = 0;
 773        int error;
 774        int i;
 775
 776        if (!device)
 777                return -EINVAL;
 778
 779        fujitsu_laptop->acpi_handle = device->handle;
 780        sprintf(acpi_device_name(device), "%s",
 781                ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
 782        sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
 783        device->driver_data = fujitsu_laptop;
 784
 785        /* kfifo */
 786        spin_lock_init(&fujitsu_laptop->fifo_lock);
 787        error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int),
 788                        GFP_KERNEL);
 789        if (error) {
 790                pr_err("kfifo_alloc failed\n");
 791                goto err_stop;
 792        }
 793
 794        error = acpi_fujitsu_laptop_input_setup(device);
 795        if (error)
 796                goto err_free_fifo;
 797
 798        error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state);
 799        if (error) {
 800                pr_err("Error reading power state\n");
 801                goto err_free_fifo;
 802        }
 803
 804        pr_info("ACPI: %s [%s] (%s)\n",
 805                acpi_device_name(device), acpi_device_bid(device),
 806                !device->power.state ? "on" : "off");
 807
 808        fujitsu_laptop->dev = device;
 809
 810        if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
 811                vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
 812                if (ACPI_FAILURE
 813                    (acpi_evaluate_object
 814                     (device->handle, METHOD_NAME__INI, NULL, NULL)))
 815                        pr_err("_INI Method failed\n");
 816        }
 817
 818        i = 0;
 819        while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
 820                && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
 821                ; /* No action, result is discarded */
 822        vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
 823
 824        fujitsu_laptop->flags_supported =
 825                call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0);
 826
 827        /* Make sure our bitmask of supported functions is cleared if the
 828           RFKILL function block is not implemented, like on the S7020. */
 829        if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD)
 830                fujitsu_laptop->flags_supported = 0;
 831
 832        if (fujitsu_laptop->flags_supported)
 833                fujitsu_laptop->flags_state =
 834                        call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
 835
 836        /* Suspect this is a keymap of the application panel, print it */
 837        pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
 838
 839        /* Sync backlight power status */
 840        if (fujitsu_bl->bl_device &&
 841            acpi_video_get_backlight_type() == acpi_backlight_vendor) {
 842                if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
 843                        fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
 844                else
 845                        fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
 846        }
 847
 848        error = acpi_fujitsu_laptop_leds_register(device);
 849        if (error)
 850                goto err_free_fifo;
 851
 852        error = fujitsu_laptop_platform_add();
 853        if (error)
 854                goto err_free_fifo;
 855
 856        return 0;
 857
 858err_free_fifo:
 859        kfifo_free(&fujitsu_laptop->fifo);
 860err_stop:
 861        return error;
 862}
 863
 864static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
 865{
 866        struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
 867
 868        fujitsu_laptop_platform_remove();
 869
 870        kfifo_free(&fujitsu_laptop->fifo);
 871
 872        return 0;
 873}
 874
 875static void acpi_fujitsu_laptop_press(int scancode)
 876{
 877        struct input_dev *input = fujitsu_laptop->input;
 878        int status;
 879
 880        status = kfifo_in_locked(&fujitsu_laptop->fifo,
 881                                 (unsigned char *)&scancode, sizeof(scancode),
 882                                 &fujitsu_laptop->fifo_lock);
 883        if (status != sizeof(scancode)) {
 884                vdbg_printk(FUJLAPTOP_DBG_WARN,
 885                            "Could not push scancode [0x%x]\n", scancode);
 886                return;
 887        }
 888        sparse_keymap_report_event(input, scancode, 1, false);
 889        vdbg_printk(FUJLAPTOP_DBG_TRACE,
 890                    "Push scancode into ringbuffer [0x%x]\n", scancode);
 891}
 892
 893static void acpi_fujitsu_laptop_release(void)
 894{
 895        struct input_dev *input = fujitsu_laptop->input;
 896        int scancode, status;
 897
 898        while (true) {
 899                status = kfifo_out_locked(&fujitsu_laptop->fifo,
 900                                          (unsigned char *)&scancode,
 901                                          sizeof(scancode),
 902                                          &fujitsu_laptop->fifo_lock);
 903                if (status != sizeof(scancode))
 904                        return;
 905                sparse_keymap_report_event(input, scancode, 0, false);
 906                vdbg_printk(FUJLAPTOP_DBG_TRACE,
 907                            "Pop scancode from ringbuffer [0x%x]\n", scancode);
 908        }
 909}
 910
 911static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
 912{
 913        struct input_dev *input;
 914        int scancode, i = 0;
 915        unsigned int irb;
 916
 917        input = fujitsu_laptop->input;
 918
 919        if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
 920                vdbg_printk(FUJLAPTOP_DBG_WARN,
 921                            "Unsupported event [0x%x]\n", event);
 922                sparse_keymap_report_event(input, -1, 1, true);
 923                return;
 924        }
 925
 926        if (fujitsu_laptop->flags_supported)
 927                fujitsu_laptop->flags_state =
 928                        call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
 929
 930        while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
 931               i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
 932                scancode = irb & 0x4ff;
 933                if (sparse_keymap_entry_from_scancode(input, scancode))
 934                        acpi_fujitsu_laptop_press(scancode);
 935                else if (scancode == 0)
 936                        acpi_fujitsu_laptop_release();
 937                else
 938                        vdbg_printk(FUJLAPTOP_DBG_WARN,
 939                                    "Unknown GIRB result [%x]\n", irb);
 940        }
 941
 942        /* On some models (first seen on the Skylake-based Lifebook
 943         * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
 944         * handled in software; its state is queried using FUNC_FLAGS
 945         */
 946        if ((fujitsu_laptop->flags_supported & BIT(26)) &&
 947            (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
 948                sparse_keymap_report_event(input, BIT(26), 1, true);
 949}
 950
 951/* Initialization */
 952
 953static const struct acpi_device_id fujitsu_bl_device_ids[] = {
 954        {ACPI_FUJITSU_BL_HID, 0},
 955        {"", 0},
 956};
 957
 958static struct acpi_driver acpi_fujitsu_bl_driver = {
 959        .name = ACPI_FUJITSU_BL_DRIVER_NAME,
 960        .class = ACPI_FUJITSU_CLASS,
 961        .ids = fujitsu_bl_device_ids,
 962        .ops = {
 963                .add = acpi_fujitsu_bl_add,
 964                .notify = acpi_fujitsu_bl_notify,
 965                },
 966};
 967
 968static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
 969        {ACPI_FUJITSU_LAPTOP_HID, 0},
 970        {"", 0},
 971};
 972
 973static struct acpi_driver acpi_fujitsu_laptop_driver = {
 974        .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
 975        .class = ACPI_FUJITSU_CLASS,
 976        .ids = fujitsu_laptop_device_ids,
 977        .ops = {
 978                .add = acpi_fujitsu_laptop_add,
 979                .remove = acpi_fujitsu_laptop_remove,
 980                .notify = acpi_fujitsu_laptop_notify,
 981                },
 982};
 983
 984static const struct acpi_device_id fujitsu_ids[] __used = {
 985        {ACPI_FUJITSU_BL_HID, 0},
 986        {ACPI_FUJITSU_LAPTOP_HID, 0},
 987        {"", 0}
 988};
 989MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
 990
 991static int __init fujitsu_init(void)
 992{
 993        int ret;
 994
 995        if (acpi_disabled)
 996                return -ENODEV;
 997
 998        fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL);
 999        if (!fujitsu_bl)
1000                return -ENOMEM;
1001
1002        ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
1003        if (ret)
1004                goto err_free_fujitsu_bl;
1005
1006        /* Register platform stuff */
1007
1008        ret = platform_driver_register(&fujitsu_pf_driver);
1009        if (ret)
1010                goto err_unregister_acpi;
1011
1012        /* Register laptop driver */
1013
1014        fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL);
1015        if (!fujitsu_laptop) {
1016                ret = -ENOMEM;
1017                goto err_unregister_platform_driver;
1018        }
1019
1020        ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
1021        if (ret)
1022                goto err_free_fujitsu_laptop;
1023
1024        pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
1025
1026        return 0;
1027
1028err_free_fujitsu_laptop:
1029        kfree(fujitsu_laptop);
1030err_unregister_platform_driver:
1031        platform_driver_unregister(&fujitsu_pf_driver);
1032err_unregister_acpi:
1033        acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1034err_free_fujitsu_bl:
1035        kfree(fujitsu_bl);
1036
1037        return ret;
1038}
1039
1040static void __exit fujitsu_cleanup(void)
1041{
1042        acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
1043
1044        kfree(fujitsu_laptop);
1045
1046        platform_driver_unregister(&fujitsu_pf_driver);
1047
1048        acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1049
1050        kfree(fujitsu_bl);
1051
1052        pr_info("driver unloaded\n");
1053}
1054
1055module_init(fujitsu_init);
1056module_exit(fujitsu_cleanup);
1057
1058module_param(use_alt_lcd_levels, int, 0644);
1059MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
1060module_param(disable_brightness_adjust, bool, 0644);
1061MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
1062#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1063module_param_named(debug, dbg_level, uint, 0644);
1064MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1065#endif
1066
1067MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1068MODULE_DESCRIPTION("Fujitsu laptop extras support");
1069MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1070MODULE_LICENSE("GPL");
1071